Compare commits

..

21 Commits

Author SHA1 Message Date
Chris Eager
49af72520c CI: update Java matrix to LTS releases
Some checks failed
Build/test / JDK ${{ matrix.java }} (11, ubuntu-22.04) (push) Has been cancelled
Build/test / JDK ${{ matrix.java }} (17, ubuntu-22.04) (push) Has been cancelled
Build/test / JDK ${{ matrix.java }} (21, ubuntu-22.04) (push) Has been cancelled
Build/test / JDK ${{ matrix.java }} (8, ubuntu-22.04) (push) Has been cancelled
2024-02-29 16:43:45 -06:00
Chris Eager
36c1079322 CI: update matrix.os to ubuntu-22.04 2024-02-29 16:43:45 -06:00
Chris Eager
3f0fba316d CI: update actions/checkout and actions/setup-java to the latest versions 2024-02-29 16:43:45 -06:00
Jon Chambers
a529bd0143 [maven-release-plugin] prepare for next development iteration 2024-02-26 17:20:03 -05:00
Jon Chambers
9ba25253c9 [maven-release-plugin] prepare release noise-java-0.1.1
Some checks failed
Build/test / JDK ${{ matrix.java }} (11, ubuntu-20.04) (push) Has been cancelled
Build/test / JDK ${{ matrix.java }} (13, ubuntu-20.04) (push) Has been cancelled
Build/test / JDK ${{ matrix.java }} (15, ubuntu-20.04) (push) Has been cancelled
Build/test / JDK ${{ matrix.java }} (8, ubuntu-20.04) (push) Has been cancelled
2024-02-26 17:20:01 -05:00
Jon Chambers
ed3f6064a2 Parameterize vector-based tests 2024-02-26 13:38:23 -05:00
Jon Chambers
87b27851aa Update to JUnit5 2024-02-26 13:38:23 -05:00
Jon Chambers
c111aa7e9d Don't increment nonces within a CipherState unless the encryption/decryption operation succeeds 2024-02-26 13:22:24 -05:00
Jon Chambers
f43de4a734 [maven-release-plugin] prepare for next development iteration 2022-08-03 12:44:10 -04:00
Jon Chambers
10ac3e0b3f [maven-release-plugin] prepare release noise-java-0.1.0
Some checks failed
Build/test / JDK ${{ matrix.java }} (11, ubuntu-20.04) (push) Has been cancelled
Build/test / JDK ${{ matrix.java }} (13, ubuntu-20.04) (push) Has been cancelled
Build/test / JDK ${{ matrix.java }} (15, ubuntu-20.04) (push) Has been cancelled
Build/test / JDK ${{ matrix.java }} (8, ubuntu-20.04) (push) Has been cancelled
2022-08-03 12:44:08 -04:00
Jon Chambers
4d7b49a422 Prepare the pom for publication to Maven Central 2022-08-03 12:43:23 -04:00
Jon Chambers
7a570218d3 Change group ID to org.signal.forks 2022-08-03 12:18:09 -04:00
Jon Chambers
3fffbedb42 Update artifact coordinates and README 2022-08-02 16:13:52 -04:00
Jon Chambers
76caeb592b Build/test with GitHub Actions 2022-08-02 16:13:51 -04:00
Jon Chambers
c2af0ac12b Move Javadoc generation to Maven 2022-08-02 16:13:49 -04:00
Jon Chambers
ed523d3db3 Include test vectors as a local resource
This eliminates an implicit test dependency on network connectivity
2022-08-02 16:13:44 -04:00
Jon Chambers
84a405197c Add IntelliJ project files/directories to .gitignore 2022-08-02 16:13:42 -04:00
rweather
49377b6dfc
Merge pull request #16 from jon-signal/accidental_junit5_dependency
Fix test-running issues
2022-08-03 04:10:19 +10:00
Jon Chambers
dce0d18e24 Move jaxb-api to the test scope. 2021-07-21 10:07:47 -04:00
Jon Chambers
76cbc067e1 Update to the latest version of maven-surefire-plugin. 2021-07-21 10:07:35 -04:00
Jon Chambers
226aff4d3e Fix an undeclared dependency on JUnit 5. 2021-07-21 10:02:26 -04:00
16 changed files with 262 additions and 340 deletions

View File

@ -7,23 +7,18 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-20.04] os: [ubuntu-22.04]
java: [8, 11, 13, 15] java: [8, 11, 17, 21]
fail-fast: false fail-fast: false
name: JDK ${{ matrix.java }} name: JDK ${{ matrix.java }}
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up JDK - name: Set up JDK
uses: actions/setup-java@v1 uses: actions/setup-java@9704b39bf258b59bc04b50fa2dd55e9ed76b47a8 # v4.1.0
with: with:
java-version: ${{ matrix.java }} java-version: ${{ matrix.java }}
- name: Cache local Maven repository distribution: 'temurin'
uses: actions/cache@v2 cache: maven
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Test with Maven - name: Test with Maven
run: mvn verify -B --file pom.xml run: mvn verify -B --file pom.xml

View File

@ -4,8 +4,8 @@ this fork is to make Noise-Java available via common artifact repositories like
as pull requests upstream wherever possible. as pull requests upstream wherever possible.
To avoid namespace collisions, the group identifier in this project's POM has To avoid namespace collisions, the group identifier in this project's POM has
been changed to `org.signal`, though package names within the artifact have not been changed to `org.signal.forks`, though package names within the artifact
changed. have not changed.
The original README continues below. The original README continues below.

118
pom.xml
View File

@ -1,21 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<artifactId>noise-java</artifactId>
<groupId>org.signal</groupId>
<version>0.1-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.signal.forks</groupId>
<artifactId>noise-java</artifactId>
<version>0.1.2-SNAPSHOT</version>
<name>Noise-Java</name>
<description>Plain Java implementation of the Noise protocol</description>
<inceptionYear>2016</inceptionYear>
<url>https://github.com/signalapp/noise-java</url>
<scm>
<connection>scm:git:https://github.com/signalapp/noise-java.git</connection>
<developerConnection>scm:git:git@github.com:signalapp/noise-java.git</developerConnection>
<url>https://github.com/signalapp/noise-java</url>
<tag>HEAD</tag>
</scm>
<developers>
<developer>
<id>rweather</id>
<name>Rhys Weatherley</name>
<email>rhys.weatherley@gmail.com</email>
<url>https://github.com/rweather/</url>
<roles>
<role>developer</role>
</roles>
</developer>
</developers>
<licenses>
<license>
<name>The MIT License (MIT)</name>
<url>http://opensource.org/licenses/MIT</url>
<distribution>repo</distribution>
</license>
</licenses>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit</artifactId> <artifactId>junit-jupiter</artifactId>
<version>4.13.1</version> <version>5.10.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -25,6 +57,7 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<pluginManagement> <pluginManagement>
<plugins> <plugins>
@ -60,7 +93,74 @@
<excludePackageNames>com.southernstorm.noise.crypto</excludePackageNames> <excludePackageNames>com.southernstorm.noise.crypto</excludePackageNames>
<windowtitle>Noise-Java</windowtitle> <windowtitle>Noise-Java</windowtitle>
</configuration> </configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.13</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>false</autoReleaseAfterClose>
</configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<profiles>
<profile>
<id>release-sign-artifacts</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project> </project>

View File

@ -130,7 +130,6 @@ class AESGCMFallbackCipherState implements CipherState {
iv[13] = 0; iv[13] = 0;
iv[14] = 0; iv[14] = 0;
iv[15] = 1; iv[15] = 1;
++n;
// Encrypt a block of zeroes to generate the hash key to XOR // Encrypt a block of zeroes to generate the hash key to XOR
// the GHASH tag with at the end of the encrypt/decrypt operation. // the GHASH tag with at the end of the encrypt/decrypt operation.
@ -207,6 +206,7 @@ class AESGCMFallbackCipherState implements CipherState {
ghash.finish(ciphertext, ciphertextOffset + length, 16); ghash.finish(ciphertext, ciphertextOffset + length, 16);
for (int index = 0; index < 16; ++index) for (int index = 0; index < 16; ++index)
ciphertext[ciphertextOffset + length + index] ^= hashKey[index]; ciphertext[ciphertextOffset + length + index] ^= hashKey[index];
n += 1;
return length + 16; return length + 16;
} }
@ -247,6 +247,7 @@ class AESGCMFallbackCipherState implements CipherState {
if ((temp & 0xFF) != 0) if ((temp & 0xFF) != 0)
Noise.throwBadTagException(); Noise.throwBadTagException();
encryptCTR(ciphertext, ciphertextOffset, plaintext, plaintextOffset, dataLen); encryptCTR(ciphertext, ciphertextOffset, plaintext, plaintextOffset, dataLen);
n += 1;
return dataLen; return dataLen;
} }

View File

@ -190,7 +190,6 @@ class AESGCMOnCtrCipherState implements CipherState {
iv[13] = 0; iv[13] = 0;
iv[14] = 0; iv[14] = 0;
iv[15] = 1; iv[15] = 1;
++n;
// Initialize the CTR mode cipher with the key and IV. // Initialize the CTR mode cipher with the key and IV.
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv)); cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv));
@ -255,6 +254,7 @@ class AESGCMOnCtrCipherState implements CipherState {
ghash.finish(ciphertext, ciphertextOffset + length, 16); ghash.finish(ciphertext, ciphertextOffset + length, 16);
for (int index = 0; index < 16; ++index) for (int index = 0; index < 16; ++index)
ciphertext[ciphertextOffset + length + index] ^= hashKey[index]; ciphertext[ciphertextOffset + length + index] ^= hashKey[index];
n += 1;
return length + 16; return length + 16;
} }
@ -312,6 +312,7 @@ class AESGCMOnCtrCipherState implements CipherState {
// Shouldn't happen. // Shouldn't happen.
throw new IllegalStateException(e); throw new IllegalStateException(e);
} }
n += 1;
return dataLen; return dataLen;
} }

View File

@ -138,7 +138,7 @@ class ChaChaPolyCipherState implements CipherState {
{ {
if (n == -1L) if (n == -1L)
throw new IllegalStateException("Nonce has wrapped around"); throw new IllegalStateException("Nonce has wrapped around");
ChaChaCore.initIV(input, n++); ChaChaCore.initIV(input, n);
ChaChaCore.hash(output, input); ChaChaCore.hash(output, input);
Arrays.fill(polyKey, (byte)0); Arrays.fill(polyKey, (byte)0);
xorBlock(polyKey, 0, polyKey, 0, 32, output); xorBlock(polyKey, 0, polyKey, 0, 32, output);
@ -234,6 +234,7 @@ class ChaChaPolyCipherState implements CipherState {
poly.update(ciphertext, ciphertextOffset, length); poly.update(ciphertext, ciphertextOffset, length);
finish(ad, length); finish(ad, length);
System.arraycopy(polyKey, 0, ciphertext, ciphertextOffset + length, 16); System.arraycopy(polyKey, 0, ciphertext, ciphertextOffset + length, 16);
n += 1;
return length + 16; return length + 16;
} }
@ -273,6 +274,7 @@ class ChaChaPolyCipherState implements CipherState {
if ((temp & 0xFF) != 0) if ((temp & 0xFF) != 0)
Noise.throwBadTagException(); Noise.throwBadTagException();
encrypt(ciphertext, ciphertextOffset, plaintext, plaintextOffset, dataLen); encrypt(ciphertext, ciphertextOffset, plaintext, plaintextOffset, dataLen);
n += 1;
return dataLen; return dataLen;
} }

View File

@ -30,7 +30,7 @@ import java.io.Reader;
* *
* Intentionally compatible with android.util.JsonReader. * Intentionally compatible with android.util.JsonReader.
*/ */
public class JsonReader { public class JsonReader implements AutoCloseable {
private Reader in; private Reader in;
private JsonToken token; private JsonToken token;
@ -54,6 +54,7 @@ public class JsonReader {
expectNext(JsonToken.BEGIN_OBJECT, "JSON begin object expected"); expectNext(JsonToken.BEGIN_OBJECT, "JSON begin object expected");
} }
@Override
public void close() throws IOException { public void close() throws IOException {
in.close(); in.close();
} }

View File

@ -22,18 +22,17 @@
package com.southernstorm.noise.tests; package com.southernstorm.noise.tests;
import static org.junit.Assert.*;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Arrays; import java.util.Arrays;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.ShortBufferException; import javax.crypto.ShortBufferException;
import org.junit.Test;
import com.southernstorm.noise.protocol.CipherState; import com.southernstorm.noise.protocol.CipherState;
import com.southernstorm.noise.protocol.Noise; import com.southernstorm.noise.protocol.Noise;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* Perform tests on the cipher algorithms used by Noise. * Perform tests on the cipher algorithms used by Noise.
@ -43,51 +42,35 @@ public class CipherStateTests {
private void testCipher(String name, int keyLen, int macLen, private void testCipher(String name, int keyLen, int macLen,
String key, long nonce, String ad, String key, long nonce, String ad,
String plaintext, String ciphertext, String plaintext, String ciphertext,
String mac, boolean forceFallbacks) String mac, boolean forceFallbacks) throws NoSuchAlgorithmException, ShortBufferException, BadPaddingException {
{
byte[] keyBytes = TestUtils.stringToData(key); byte[] keyBytes = TestUtils.stringToData(key);
byte[] adBytes = TestUtils.stringToData(ad); byte[] adBytes = TestUtils.stringToData(ad);
byte[] plaintextBytes = TestUtils.stringToData(plaintext); byte[] plaintextBytes = TestUtils.stringToData(plaintext);
byte[] ciphertextBytes; byte[] ciphertextBytes;
byte[] buffer;
if (ciphertext.length() > 0) if (ciphertext.length() > 0)
ciphertextBytes = TestUtils.stringToData(ciphertext + mac.substring(2)); ciphertextBytes = TestUtils.stringToData(ciphertext + mac.substring(2));
else else
ciphertextBytes = TestUtils.stringToData(mac); ciphertextBytes = TestUtils.stringToData(mac);
final byte[] plaintextBuffer = new byte[plaintextBytes.length];
final byte[] ciphertextBuffer = new byte[ciphertextBytes.length];
// Create the cipher object and check its properties. // Create the cipher object and check its properties.
CipherState cipher = null;
Noise.setForceFallbacks(forceFallbacks); Noise.setForceFallbacks(forceFallbacks);
try { final CipherState cipher = Noise.createCipher(name);
cipher = Noise.createCipher(name);
} catch (NoSuchAlgorithmException e) {
fail(name + " cipher is not supported");
}
assertEquals(name, cipher.getCipherName()); assertEquals(name, cipher.getCipherName());
assertEquals(keyLen, cipher.getKeyLength()); assertEquals(keyLen, cipher.getKeyLength());
assertEquals(0, cipher.getMACLength()); // Key has not been set yet. assertEquals(0, cipher.getMACLength()); // Key has not been set yet.
// Try to encrypt. Because the key is not set yet, this will // Try to encrypt. Because the key is not set yet, this will
// return the plaintext as-is. // return the plaintext as-is.
try { Arrays.fill(plaintextBuffer, (byte)0xAA);
buffer = new byte [plaintextBytes.length]; assertEquals(plaintextBytes.length, cipher.encryptWithAd(adBytes, plaintextBytes, 0, plaintextBuffer, 0, plaintextBytes.length));
Arrays.fill(buffer, (byte)0xAA); assertArrayEquals(plaintextBytes, plaintextBuffer);
assertEquals(plaintextBytes.length, cipher.encryptWithAd(adBytes, plaintextBytes, 0, buffer, 0, plaintextBytes.length));
assertArrayEquals(plaintextBytes, buffer);
} catch (ShortBufferException e) {
fail("Buffer should have been big enough");
}
// Try to decrypt. Will return the ciphertext and MAC as-is. // Try to decrypt. Will return the ciphertext and MAC as-is.
buffer = new byte [ciphertextBytes.length]; Arrays.fill(ciphertextBuffer, (byte)0xAA);
Arrays.fill(buffer, (byte)0xAA); assertEquals(ciphertextBytes.length, cipher.decryptWithAd(adBytes, ciphertextBytes, 0, ciphertextBuffer, 0, ciphertextBytes.length));
try {
assertEquals(ciphertextBytes.length, cipher.decryptWithAd(adBytes, ciphertextBytes, 0, buffer, 0, ciphertextBytes.length));
} catch (BadPaddingException e) {
fail();
} catch (ShortBufferException e) {
fail();
}
// Set the key and fast-forward the nonce. // Set the key and fast-forward the nonce.
cipher.initializeKey(keyBytes, 0); cipher.initializeKey(keyBytes, 0);
@ -95,39 +78,24 @@ public class CipherStateTests {
assertEquals(macLen, cipher.getMACLength()); assertEquals(macLen, cipher.getMACLength());
// Encrypt the data. // Encrypt the data.
try { Arrays.fill(ciphertextBuffer, (byte)0xAA);
buffer = new byte [ciphertextBytes.length]; assertEquals(ciphertextBytes.length, cipher.encryptWithAd(adBytes, plaintextBytes, 0, ciphertextBuffer, 0, plaintextBytes.length));
Arrays.fill(buffer, (byte)0xAA); assertArrayEquals(ciphertextBytes, ciphertextBuffer);
assertEquals(ciphertextBytes.length, cipher.encryptWithAd(adBytes, plaintextBytes, 0, buffer, 0, plaintextBytes.length));
assertArrayEquals(ciphertextBytes, buffer);
} catch (ShortBufferException e) {
fail("Buffer should have been big enough");
}
// Try to decrypt. The MAC check should fail because the internal // Try to decrypt. The MAC check should fail because the internal
// nonce was incremented and no longer matches the parameter. // nonce was incremented and no longer matches the parameter.
try { assertThrows(BadPaddingException.class, () ->
cipher.decryptWithAd(adBytes, ciphertextBytes, 0, buffer, 0, ciphertextBytes.length); cipher.decryptWithAd(adBytes, ciphertextBytes, 0, ciphertextBuffer, 0, ciphertextBytes.length));
fail();
} catch (BadPaddingException e) {
// Success!
} catch (ShortBufferException e) {
fail();
}
// Fast-forward the nonce to just before the rollover. We will be able // Fast-forward the nonce to just before the rollover. We will be able
// to encrypt one more block, and then the next request will be rejected. // to encrypt one more block, and then the next request will be rejected.
cipher.setNonce(-2L); cipher.setNonce(-2L);
try { try {
buffer = new byte [ciphertextBytes.length]; Arrays.fill(ciphertextBuffer, (byte)0xAA);
Arrays.fill(buffer, (byte)0xAA); cipher.encryptWithAd(adBytes, plaintextBytes, 0, ciphertextBuffer, 0, plaintextBytes.length);
cipher.encryptWithAd(adBytes, plaintextBytes, 0, buffer, 0, plaintextBytes.length);
try { assertThrows(IllegalStateException.class, () ->
cipher.encryptWithAd(adBytes, plaintextBytes, 0, buffer, 0, plaintextBytes.length); cipher.encryptWithAd(adBytes, plaintextBytes, 0, ciphertextBuffer, 0, plaintextBytes.length));
fail();
} catch (IllegalStateException e) {
// Success!
}
} catch (ShortBufferException e) { } catch (ShortBufferException e) {
fail("Buffer should have been big enough"); fail("Buffer should have been big enough");
} }
@ -138,53 +106,34 @@ public class CipherStateTests {
assertEquals(macLen, cipher.getMACLength()); assertEquals(macLen, cipher.getMACLength());
// Decrypt the test ciphertext and MAC. // Decrypt the test ciphertext and MAC.
try { Arrays.fill(plaintextBuffer, (byte)0xAA);
buffer = new byte [plaintextBytes.length]; assertEquals(plaintextBytes.length, cipher.decryptWithAd(adBytes, ciphertextBytes, 0, plaintextBuffer, 0, ciphertextBytes.length));
Arrays.fill(buffer, (byte)0xAA); assertArrayEquals(plaintextBytes, plaintextBuffer);
assertEquals(plaintextBytes.length, cipher.decryptWithAd(adBytes, ciphertextBytes, 0, buffer, 0, ciphertextBytes.length));
assertArrayEquals(plaintextBytes, buffer);
} catch (BadPaddingException e) {
fail();
} catch (ShortBufferException e) {
fail();
}
// Fast-forward the nonce to just before the rollover. We will be able // Fast-forward the nonce to just before the rollover. We will be able
// to decrypt one more block, and then the next request will be rejected. // to decrypt one more block, and then the next request will be rejected.
cipher.setNonce(-2L); cipher.setNonce(-2L);
try {
buffer = new byte [plaintextBytes.length]; Arrays.fill(plaintextBuffer, (byte)0xAA);
Arrays.fill(buffer, (byte)0xAA); assertThrows(BadPaddingException.class, () ->
try { cipher.decryptWithAd(adBytes, ciphertextBytes, 0, plaintextBuffer, 0, ciphertextBytes.length));
cipher.decryptWithAd(adBytes, ciphertextBytes, 0, buffer, 0, ciphertextBytes.length);
fail(); cipher.setNonce(-1L);
} catch (BadPaddingException e) {
// Success! assertThrows(IllegalStateException.class, () ->
} cipher.decryptWithAd(adBytes, ciphertextBytes, 0, plaintextBuffer, 0, ciphertextBytes.length));
try {
cipher.decryptWithAd(adBytes, ciphertextBytes, 0, buffer, 0, ciphertextBytes.length);
fail();
} catch (IllegalStateException e) {
// Success!
} catch (BadPaddingException e) {
fail();
}
} catch (ShortBufferException e) {
fail("Buffer should have been big enough");
}
} }
private void testCipher(String name, int keyLen, int macLen, private void testCipher(String name, int keyLen, int macLen,
String key, long nonce, String ad, String key, long nonce, String ad,
String plaintext, String ciphertext, String plaintext, String ciphertext,
String mac) String mac) throws ShortBufferException, NoSuchAlgorithmException, BadPaddingException {
{
testCipher(name, keyLen, macLen, key, nonce, ad, plaintext, ciphertext, mac, true); testCipher(name, keyLen, macLen, key, nonce, ad, plaintext, ciphertext, mac, true);
testCipher(name, keyLen, macLen, key, nonce, ad, plaintext, ciphertext, mac, false); testCipher(name, keyLen, macLen, key, nonce, ad, plaintext, ciphertext, mac, false);
} }
@Test @Test
public void AESGCM() { public void AESGCM() throws ShortBufferException, NoSuchAlgorithmException, BadPaddingException {
/* Test vectors for AES in GCM mode from Appendix B of: /* Test vectors for AES in GCM mode from Appendix B of:
http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf
We can only use a few of the vectors because most of the IV's in the We can only use a few of the vectors because most of the IV's in the
@ -212,7 +161,7 @@ public class CipherStateTests {
} }
@Test @Test
public void ChaChaPoly() { public void ChaChaPoly() throws ShortBufferException, NoSuchAlgorithmException, BadPaddingException {
// ChaChaPoly test vectors from Appendix A.5 of RFC 7539. // ChaChaPoly test vectors from Appendix A.5 of RFC 7539.
testCipher testCipher
("ChaChaPoly", 32, 16, ("ChaChaPoly", 32, 16,

View File

@ -22,13 +22,12 @@
package com.southernstorm.noise.tests; package com.southernstorm.noise.tests;
import static org.junit.Assert.*;
import java.util.Arrays; import java.util.Arrays;
import org.junit.Test;
import com.southernstorm.noise.crypto.Curve25519; import com.southernstorm.noise.crypto.Curve25519;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
public class Curve25519Tests { public class Curve25519Tests {

View File

@ -22,13 +22,12 @@
package com.southernstorm.noise.tests; package com.southernstorm.noise.tests;
import static org.junit.Assert.*;
import java.util.Arrays; import java.util.Arrays;
import org.junit.Test;
import com.southernstorm.noise.crypto.Curve448; import com.southernstorm.noise.crypto.Curve448;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
public class Curve448Tests { public class Curve448Tests {

View File

@ -22,13 +22,12 @@
package com.southernstorm.noise.tests; package com.southernstorm.noise.tests;
import static org.junit.Assert.*;
import java.util.Arrays; import java.util.Arrays;
import org.junit.Test;
import com.southernstorm.noise.crypto.GHASH; import com.southernstorm.noise.crypto.GHASH;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
public class GHASHTests { public class GHASHTests {

View File

@ -22,23 +22,21 @@
package com.southernstorm.noise.tests; package com.southernstorm.noise.tests;
import static org.junit.Assert.*;
import java.security.DigestException; import java.security.DigestException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import org.junit.Test;
import com.southernstorm.noise.protocol.Noise; import com.southernstorm.noise.protocol.Noise;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
/** /**
* Perform tests on the hash algorithms used by Noise. * Perform tests on the hash algorithms used by Noise.
*/ */
public class HashTests { public class HashTests {
private void testHash(MessageDigest digest, String input, String hash) private void testHash(MessageDigest digest, String input, String hash) throws DigestException {
{
byte[] inputBytes = TestUtils.stringToData(input); byte[] inputBytes = TestUtils.stringToData(input);
byte[] hashBytes = TestUtils.stringToData(hash); byte[] hashBytes = TestUtils.stringToData(hash);
byte[] result = new byte [digest.getDigestLength()]; byte[] result = new byte [digest.getDigestLength()];
@ -46,50 +44,36 @@ public class HashTests {
// Hash the entire input in one request. // Hash the entire input in one request.
digest.reset(); digest.reset();
digest.update(inputBytes); digest.update(inputBytes);
try { digest.digest(result, 0, result.length);
digest.digest(result, 0, result.length);
} catch (DigestException e) {
fail("digest failed");
}
assertArrayEquals(hashBytes, result); assertArrayEquals(hashBytes, result);
// Hash the input in pieces to test split requests. // Hash the input in pieces to test split requests.
digest.reset(); digest.reset();
digest.update(inputBytes, 0, inputBytes.length / 2); digest.update(inputBytes, 0, inputBytes.length / 2);
digest.update(inputBytes, inputBytes.length / 2, inputBytes.length - (inputBytes.length / 2)); digest.update(inputBytes, inputBytes.length / 2, inputBytes.length - (inputBytes.length / 2));
try { digest.digest(result, 0, result.length);
digest.digest(result, 0, result.length);
} catch (DigestException e) {
fail("digest failed");
}
assertArrayEquals(hashBytes, result); assertArrayEquals(hashBytes, result);
} }
private void testHash(String name, String input, String hash) private void testHash(String name, String input, String hash) throws NoSuchAlgorithmException, DigestException {
{
MessageDigest digest = null; MessageDigest digest = null;
Noise.setForceFallbacks(true); Noise.setForceFallbacks(true);
try { try {
digest = Noise.createHash(name); digest = Noise.createHash(name);
} catch (NoSuchAlgorithmException e) {
fail("No crypto provider for " + name);
} finally { } finally {
Noise.setForceFallbacks(false); Noise.setForceFallbacks(false);
} }
testHash(digest, input, hash); testHash(digest, input, hash);
Noise.setForceFallbacks(false); Noise.setForceFallbacks(false);
try { digest = Noise.createHash(name);
digest = Noise.createHash(name);
} catch (NoSuchAlgorithmException e) {
fail("No crypto provider for " + name);
}
testHash(digest, input, hash); testHash(digest, input, hash);
} }
@Test @Test
public void blake2b() { public void blake2b() throws DigestException, NoSuchAlgorithmException {
testHash("BLAKE2b", "", "0x786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce"); testHash("BLAKE2b", "", "0x786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce");
testHash("BLAKE2b", "abc", "0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); testHash("BLAKE2b", "abc", "0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923");
testHash("BLAKE2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "0x7285ff3e8bd768d69be62b3bf18765a325917fa9744ac2f582a20850bc2b1141ed1b3e4528595acc90772bdf2d37dc8a47130b44f33a02e8730e5ad8e166e888"); testHash("BLAKE2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "0x7285ff3e8bd768d69be62b3bf18765a325917fa9744ac2f582a20850bc2b1141ed1b3e4528595acc90772bdf2d37dc8a47130b44f33a02e8730e5ad8e166e888");
@ -97,7 +81,7 @@ public class HashTests {
} }
@Test @Test
public void blake2s() { public void blake2s() throws DigestException, NoSuchAlgorithmException {
testHash("BLAKE2s", "", "0x69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9"); testHash("BLAKE2s", "", "0x69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9");
testHash("BLAKE2s", "abc", "0x508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"); testHash("BLAKE2s", "abc", "0x508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982");
testHash("BLAKE2s", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "0x6f4df5116a6f332edab1d9e10ee87df6557beab6259d7663f3bcd5722c13f189"); testHash("BLAKE2s", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "0x6f4df5116a6f332edab1d9e10ee87df6557beab6259d7663f3bcd5722c13f189");
@ -105,14 +89,14 @@ public class HashTests {
} }
@Test @Test
public void sha256() { public void sha256() throws DigestException, NoSuchAlgorithmException {
testHash("SHA256", "", "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); testHash("SHA256", "", "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
testHash("SHA256", "abc", "0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); testHash("SHA256", "abc", "0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
testHash("SHA256", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "0x248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); testHash("SHA256", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "0x248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
} }
@Test @Test
public void sha512() { public void sha512() throws DigestException, NoSuchAlgorithmException {
testHash("SHA512", "", "0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); testHash("SHA512", "", "0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e");
testHash("SHA512", "abc", "0xddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"); testHash("SHA512", "abc", "0xddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f");
testHash("SHA512", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", "0x8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"); testHash("SHA512", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", "0x8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909");

View File

@ -22,13 +22,12 @@
package com.southernstorm.noise.tests; package com.southernstorm.noise.tests;
import static org.junit.Assert.*;
import java.util.Arrays; import java.util.Arrays;
import org.junit.Test;
import com.southernstorm.noise.crypto.Poly1305; import com.southernstorm.noise.crypto.Poly1305;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
/** /**
* Perform tests on the Poly1305 implementation in isolation from ChaChaPoly. * Perform tests on the Poly1305 implementation in isolation from ChaChaPoly.

View File

@ -22,13 +22,12 @@
package com.southernstorm.noise.tests; package com.southernstorm.noise.tests;
import static org.junit.Assert.*; import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import java.util.Arrays; import java.util.Arrays;
import org.junit.Test;
import com.southernstorm.noise.crypto.RijndaelAES; import com.southernstorm.noise.crypto.RijndaelAES;
import org.junit.jupiter.api.Test;
/** /**
* AES test cases to verify the fallback RijndaelAES implementation. * AES test cases to verify the fallback RijndaelAES implementation.

View File

@ -1,17 +0,0 @@
package com.southernstorm.noise.tests;
import java.io.InputStream;
import org.junit.Assert;
import org.junit.Test;
public class UnitVectorTests {
@Test
public void testBasicVector() throws Exception {
try (final InputStream stream = getClass().getResourceAsStream("test-vectors.json")) {
VectorTests vectorTests = new VectorTests();
vectorTests.processInputStream(stream);
Assert.assertEquals(vectorTests.getFailed(), 0);
}
}
}

View File

@ -22,16 +22,14 @@
package com.southernstorm.noise.tests; package com.southernstorm.noise.tests;
import static org.junit.Assert.*;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.ShortBufferException; import javax.crypto.ShortBufferException;
@ -41,27 +39,22 @@ import com.southernstorm.json.JsonReader;
import com.southernstorm.noise.protocol.CipherState; import com.southernstorm.noise.protocol.CipherState;
import com.southernstorm.noise.protocol.CipherStatePair; import com.southernstorm.noise.protocol.CipherStatePair;
import com.southernstorm.noise.protocol.HandshakeState; import com.southernstorm.noise.protocol.HandshakeState;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* Executes Noise vector tests in JSON format. * Executes Noise vector tests in JSON format.
*/ */
public class VectorTests { public class VectorTests {
private int total;
private int failed;
private int skipped;
public VectorTests()
{
total = 0;
failed = 0;
skipped = 0;
}
/** /**
* Information about a handshake or transport message. * Information about a handshake or transport message.
*/ */
private class TestMessage private static class TestMessage
{ {
public byte[] payload; public byte[] payload;
public byte[] ciphertext; public byte[] ciphertext;
@ -70,7 +63,7 @@ public class VectorTests {
/** /**
* Information about a Noise test vector that was parsed from a JSON stream. * Information about a Noise test vector that was parsed from a JSON stream.
*/ */
private class TestVector private static class TestVector
{ {
public String name; public String name;
public String pattern; public String pattern;
@ -116,18 +109,20 @@ public class VectorTests {
private void assertSubArrayEquals(String msg, byte[] expected, byte[] actual) private void assertSubArrayEquals(String msg, byte[] expected, byte[] actual)
{ {
for (int index = 0; index < expected.length; ++index) for (int index = 0; index < expected.length; ++index)
assertEquals(msg + "[" + Integer.toString(index) + "]", expected[index], actual[index]); assertEquals(expected[index], actual[index], msg + "[" + index + "]");
} }
/** @ParameterizedTest
* Runs a Noise test vector. @MethodSource
* void testVectors(TestVector vec) throws ShortBufferException, BadPaddingException, NoSuchAlgorithmException
* @param vec The test vector.
* @param initiator Handshake object for the initiator.
* @param responder Handshake object for the responder.
*/
private void runTest(TestVector vec, HandshakeState initiator, HandshakeState responder) throws ShortBufferException, BadPaddingException, NoSuchAlgorithmException
{ {
HandshakeState initiator = new HandshakeState(vec.name, HandshakeState.INITIATOR);
HandshakeState responder = new HandshakeState(vec.name, HandshakeState.RESPONDER);
assertEquals(HandshakeState.INITIATOR, initiator.getRole());
assertEquals(HandshakeState.RESPONDER, responder.getRole());
assertEquals(vec.name, initiator.getProtocolName());
assertEquals(vec.name, responder.getProtocolName());
// Set all keys and special values that we need. // Set all keys and special values that we need.
if (vec.init_prologue != null) if (vec.init_prologue != null)
initiator.setPrologue(vec.init_prologue, 0, vec.init_prologue.length); initiator.setPrologue(vec.init_prologue, 0, vec.init_prologue.length);
@ -169,12 +164,13 @@ public class VectorTests {
// Work through the messages one by one until both sides "split". // Work through the messages one by one until both sides "split".
int role = HandshakeState.INITIATOR; int role = HandshakeState.INITIATOR;
int index = 0; int index = 0;
HandshakeState send, recv;
boolean isOneWay = (vec.pattern.length() == 1); boolean isOneWay = (vec.pattern.length() == 1);
boolean fallback = vec.fallback_expected; boolean fallback = vec.fallback_expected;
byte[] message = new byte [8192]; byte[] message = new byte [8192];
byte[] plaintext = new byte [8192]; byte[] plaintext = new byte [8192];
for (; index < vec.messages.length; ++index) { for (; index < vec.messages.length; ++index) {
final HandshakeState send, recv;
if (initiator.getAction() == HandshakeState.SPLIT && if (initiator.getAction() == HandshakeState.SPLIT &&
responder.getAction() == HandshakeState.SPLIT) { responder.getAction() == HandshakeState.SPLIT) {
break; break;
@ -196,15 +192,11 @@ public class VectorTests {
TestMessage msg = vec.messages[index]; TestMessage msg = vec.messages[index];
int len = send.writeMessage(message, 0, msg.payload, 0, msg.payload.length); int len = send.writeMessage(message, 0, msg.payload, 0, msg.payload.length);
assertEquals(msg.ciphertext.length, len); assertEquals(msg.ciphertext.length, len);
assertSubArrayEquals(Integer.toString(index) + ": ciphertext", msg.ciphertext, message); assertSubArrayEquals(index + ": ciphertext", msg.ciphertext, message);
if (fallback) { if (fallback) {
// Perform a read on the responder, which will fail. // Perform a read on the responder, which will fail.
try { assertThrows(BadPaddingException.class, () -> recv.readMessage(message, 0, len, plaintext, 0),
recv.readMessage(message, 0, len, plaintext, 0); "read should have triggered fallback");
fail("read should have triggered fallback");
} catch (BadPaddingException e) {
// Success!
}
// Look up the pattern to fall back to. // Look up the pattern to fall back to.
String pattern = vec.fallback_pattern; String pattern = vec.fallback_pattern;
@ -224,7 +216,7 @@ public class VectorTests {
} else { } else {
int plen = recv.readMessage(message, 0, len, plaintext, 0); int plen = recv.readMessage(message, 0, len, plaintext, 0);
assertEquals(msg.payload.length, plen); assertEquals(msg.payload.length, plen);
assertSubArrayEquals(Integer.toString(index) + ": payload", msg.payload, plaintext); assertSubArrayEquals(index + ": payload", msg.payload, plaintext);
} }
} }
if (vec.fallback_expected) { if (vec.fallback_expected) {
@ -276,10 +268,10 @@ public class VectorTests {
} }
int len = csend.encryptWithAd(null, msg.payload, 0, message, 0, msg.payload.length); int len = csend.encryptWithAd(null, msg.payload, 0, message, 0, msg.payload.length);
assertEquals(msg.ciphertext.length, len); assertEquals(msg.ciphertext.length, len);
assertSubArrayEquals(Integer.toString(index) + ": ciphertext", msg.ciphertext, message); assertSubArrayEquals(index + ": ciphertext", msg.ciphertext, message);
int plen = crecv.decryptWithAd(null, message, 0, plaintext, 0, len); int plen = crecv.decryptWithAd(null, message, 0, plaintext, 0, len);
assertEquals(msg.payload.length, plen); assertEquals(msg.payload.length, plen);
assertSubArrayEquals(Integer.toString(index) + ": payload", msg.payload, plaintext); assertSubArrayEquals(index + ": payload", msg.payload, plaintext);
} }
// Clean up. // Clean up.
@ -289,16 +281,20 @@ public class VectorTests {
respPair.destroy(); respPair.destroy();
} }
/** private static Stream<Arguments> testVectors() throws IOException {
* Processes a single test vector from an input stream. try (InputStream testVectorInputStream = VectorTests.class.getResourceAsStream("test-vectors.json")) {
* if (testVectorInputStream == null) {
* @param reader The JSON reader for the input stream. throw new IOException("Could not load test vectors");
* }
* The reader is positioned on the first field of the vector object.
*/ return loadTestVectors(testVectorInputStream).stream()
private void processVector(JsonReader reader) throws IOException .map(testVector -> Arguments.of(Named.of(testVector.name, testVector)));
}
}
private static TestVector getNextVector(final JsonReader reader) throws IOException
{ {
boolean res = true; boolean res = true;
// Parse the contents of the test vector. // Parse the contents of the test vector.
TestVector vec = new TestVector(); TestVector vec = new TestVector();
while (reader.hasNext()) { while (reader.hasNext()) {
@ -385,116 +381,31 @@ public class VectorTests {
if (vec.name == null) if (vec.name == null)
vec.name = protocolName; vec.name = protocolName;
// Execute the test vector. return vec;
++total; }
System.out.print(vec.name);
System.out.print(" ... "); private static List<TestVector> loadTestVectors(InputStream jsonInputStream) throws IOException {
System.out.flush(); List<TestVector> testVectors = new ArrayList<>();
try {
HandshakeState initiator = new HandshakeState(protocolName, HandshakeState.INITIATOR); try (JsonReader jsonReader = new JsonReader(new BufferedReader(new InputStreamReader(jsonInputStream)))) {
HandshakeState responder = new HandshakeState(protocolName, HandshakeState.RESPONDER); jsonReader.beginObject();
assertEquals(HandshakeState.INITIATOR, initiator.getRole()); while (jsonReader.hasNext()) {
assertEquals(HandshakeState.RESPONDER, responder.getRole()); String name = jsonReader.nextName();
assertEquals(protocolName, initiator.getProtocolName()); if (name.equals("vectors")) {
assertEquals(protocolName, responder.getProtocolName()); jsonReader.beginArray();
runTest(vec, initiator, responder); while (jsonReader.hasNext()) {
if (!vec.failure_expected) { jsonReader.beginObject();
System.out.println("ok"); testVectors.add(getNextVector(jsonReader));
jsonReader.endObject();
}
jsonReader.endArray();
} else { } else {
System.out.println("failure expected"); jsonReader.skipValue();
++failed;
}
} catch (NoSuchAlgorithmException e) {
System.out.println("unsupported");
++skipped;
} catch (AssertionError e) {
System.out.println(e.getMessage());
e.printStackTrace(System.out);
++failed;
} catch (Exception e) {
if (!vec.failure_expected) {
System.out.println("failed");
e.printStackTrace(System.out);
++failed;
} else {
System.out.println("ok");
} }
} }
} jsonReader.endObject();
public void processFile(String filename) throws IOException {
try {
try (FileInputStream fileStream = new FileInputStream(filename)) {
System.out.print(filename + ": ");
processInputStream(fileStream);
}
} catch (FileNotFoundException e) {
System.err.println(filename + ": File not found");
}
}
public void processInputStream(InputStream jsonInputStream) throws IOException {
try(Reader streamReader = new BufferedReader(new InputStreamReader(jsonInputStream))) {
processReader(streamReader);
}
}
public void processReader(Reader jsonStream) throws IOException {
total = 0;
skipped = 0;
failed = 0;
JsonReader reader = new JsonReader(jsonStream);
try {
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("vectors")) {
reader.beginArray();
while (reader.hasNext() /*&& total < 50*/) {
reader.beginObject();
processVector(reader);
reader.endObject();
}
reader.endArray();
} else {
reader.skipValue();
}
}
reader.endObject();
} catch (IOException e) {
System.err.println("Exception while parsing JSON: " + e.toString());
e.printStackTrace();
} finally {
reader.close();
}
System.out.print(total);
System.out.print(" tests, ");
System.out.print(skipped);
System.out.print(" skipped, ");
System.out.print(failed);
System.out.println(" failed");
} }
return testVectors;
public int getTotal() {
return total;
} }
public int getFailed() {
return failed;
}
public int getSkipped() {
return skipped;
}
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.out.println("Usage: VectorTests file1 file2 ...");
return;
}
VectorTests app = new VectorTests();
for (String filename : args)
app.processFile(filename);
}
} }