package org.bouncycastle.crypto.test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.StreamCipher; import org.bouncycastle.crypto.engines.HC128Engine; import org.bouncycastle.crypto.engines.HC256Engine; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; /** * HC-128 and HC-256 Tests. Based on the test vectors in the official reference * papers, respectively: * * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf */ public class HCFamilyVecTest extends SimpleTest { private static class PeekableLineReader extends BufferedReader { public PeekableLineReader(Reader r) throws IOException { super(r); peek = super.readLine(); } public String peekLine() { return peek; } public String readLine() throws IOException { String tmp = peek; peek = super.readLine(); return tmp; } private String peek; } public String getName() { return "HC-128 and HC-256 (ecrypt)"; } public void performTest() throws Exception { runTests(new HC128Engine(), "ecrypt_HC-128.txt"); runTests(new HC256Engine(), "ecrypt_HC-256_128K_128IV.txt"); runTests(new HC256Engine(), "ecrypt_HC-256_256K_128IV.txt"); runTests(new HC256Engine(), "ecrypt_HC-256_128K_256IV.txt"); runTests(new HC256Engine(), "ecrypt_HC-256_256K_256IV.txt"); } private void runTests(StreamCipher hc, String fileName) throws IOException { Reader resource = new InputStreamReader(getClass().getResourceAsStream(fileName)); PeekableLineReader r = new PeekableLineReader(resource); runAllVectors(hc, fileName, r); } private void runAllVectors(StreamCipher hc, String fileName, PeekableLineReader r) throws IOException { for (;;) { String line = r.readLine(); if (line == null) { break; } line = line.trim(); if (line.startsWith("Set ")) { runVector(hc, fileName, r, dellChar(line, ':')); } } } private String dellChar(String s, char c) { StringBuffer b = new StringBuffer(); for (int i = 0; i != s.length(); i++) { if (s.charAt(i) != c) { b.append(s.charAt(i)); } } return b.toString(); } private void runVector(StreamCipher hc, String fileName, PeekableLineReader r, String vectorName) throws IOException { // System.out.println(fileName + " => " + vectorName); String hexKey = readBlock(r); String hexIV = readBlock(r); CipherParameters cp = new KeyParameter(Hex.decode(hexKey)); cp = new ParametersWithIV(cp, Hex.decode(hexIV)); hc.init(true, cp); byte[] input = new byte[64]; byte[] output = new byte[64]; byte[] digest = new byte[64]; int pos = 0; for (;;) { String line1 = r.peekLine().trim(); int equalsPos = line1.indexOf('='); String lead = line1.substring(0, equalsPos - 1); String hexData = readBlock(r); byte[] data = Hex.decode(hexData); if (lead.equals("xor-digest")) { if (!Arrays.areEqual(data, digest)) { fail("Failed in " + fileName + " for test vector: " + vectorName + " at " + lead); // System.out.println(fileName + " => " + vectorName + " failed at " + lead); return; } break; } int posA = lead.indexOf('['); int posB = lead.indexOf(".."); int posC = lead.indexOf(']'); int start = Integer.parseInt(lead.substring(posA + 1, posB)); int end = Integer.parseInt(lead.substring(posB + 2, posC)); if (start % 64 != 0 || (end - start != 63)) { throw new IllegalStateException(vectorName + ": " + lead + " not on 64 byte boundaries"); } while (pos < end) { hc.processBytes(input, 0, input.length, output, 0); xor(digest, output); pos += 64; } if (!Arrays.areEqual(data, output)) { fail("Failed in " + fileName + " for test vector: " + vectorName + " at " + lead); // System.out.println(fileName + " => " + vectorName + " failed at " + lead); return; } } } private static String readBlock(PeekableLineReader r) throws IOException { String first = r.readLine().trim(); String result = first.substring(first.lastIndexOf(' ') + 1); for (;;) { String peek = r.peekLine().trim(); if (peek.length() < 1 || peek.indexOf('=') >= 0) { break; } result += r.readLine().trim(); } return result; } private static void xor(byte[] digest, byte[] block) { for (int i = 0; i < digest.length; ++i) { digest[i] ^= block[i]; } } public static void main(String[] args) { runTest(new HCFamilyVecTest()); } }