250 lines
9.3 KiB
Java
250 lines
9.3 KiB
Java
|
/*
|
||
|
NoekeonVects.java - Generate Noekeon test vectors using BouncyCastle.
|
||
|
|
||
|
Written in 2011 by Patrick Pelletier <code@funwithsoftware.org>
|
||
|
|
||
|
To the extent possible under law, the author(s) have dedicated all
|
||
|
copyright and related and neighboring rights to this software to
|
||
|
the public domain worldwide. This software is distributed without
|
||
|
any warranty.
|
||
|
|
||
|
This file is dedicated to the public domain with the CC0 Public Domain
|
||
|
Dedication: http://creativecommons.org/publicdomain/zero/1.0/legalcode.txt
|
||
|
|
||
|
You may also consider this file to be covered by the WTFPL, as contained
|
||
|
in the LibTomCrypt LICENSE file, if that makes you happier for some reason.
|
||
|
|
||
|
----------------------------------------------------------------------
|
||
|
|
||
|
This program was inspired by the comment in Botan 1.10.1's
|
||
|
doc/examples/eax_test.cpp:
|
||
|
|
||
|
// Noekeon: unknown cause, though LTC's lone test vector does not
|
||
|
// match Botan
|
||
|
|
||
|
So, I investigated the discrepancy by comparing them with a third
|
||
|
implementation, BouncyCastle: http://www.bouncycastle.org/java.html
|
||
|
|
||
|
I determined that there are two reasons why LibTomCrypt's Noekeon does
|
||
|
not match Botan:
|
||
|
|
||
|
1) Botan uses "indirect Noekeon" (with a key schedule), while
|
||
|
LibTomCrypt and BouncyCastle both use "direct Noekeon" (without
|
||
|
a key schedule). See slide 14 of
|
||
|
http://gro.noekeon.org/Noekeon-slides.pdf
|
||
|
|
||
|
2) However, LibTomCrypt's direct Noekeon still does not match
|
||
|
BouncyCastle's direct Noekeon. This is because of a bug in
|
||
|
LibTomCrypt's PI1 and PI2 functions:
|
||
|
https://github.com/libtom/libtomcrypt/issues/5
|
||
|
|
||
|
This program uses BouncyCastle to produce test vectors which are
|
||
|
suitable for Botan (by explicitly scheduling the key, thus
|
||
|
building indirect Noekeon out of BouncyCastle's direct Noekeon),
|
||
|
and also produces test vectors which would be suitable for
|
||
|
LibTomCrypt (direct Noekeon) once its PI1 and PI2 functions are
|
||
|
fixed to match the Noekeon specification.
|
||
|
|
||
|
Although this program uses a PRNG from BouncyCastle to generate
|
||
|
data for the test vectors, it uses a fixed seed and thus will
|
||
|
produce the same output every time it is run.
|
||
|
*/
|
||
|
|
||
|
import java.io.ByteArrayOutputStream;
|
||
|
import java.io.IOException;
|
||
|
import java.util.Locale;
|
||
|
import org.bouncycastle.crypto.digests.RIPEMD128Digest;
|
||
|
import org.bouncycastle.crypto.engines.NoekeonEngine;
|
||
|
import org.bouncycastle.crypto.modes.EAXBlockCipher;
|
||
|
import org.bouncycastle.crypto.params.AEADParameters;
|
||
|
import org.bouncycastle.crypto.params.KeyParameter;
|
||
|
import org.bouncycastle.crypto.prng.DigestRandomGenerator;
|
||
|
import org.bouncycastle.util.encoders.HexEncoder;
|
||
|
|
||
|
public class NoekeonVects
|
||
|
{
|
||
|
private final DigestRandomGenerator r =
|
||
|
new DigestRandomGenerator(new RIPEMD128Digest());
|
||
|
|
||
|
private final HexEncoder h = new HexEncoder();
|
||
|
|
||
|
private final NoekeonEngine noekeon = new NoekeonEngine();
|
||
|
|
||
|
private final KeyParameter null_key = new KeyParameter(new byte[16]);
|
||
|
|
||
|
private final boolean schedule_key;
|
||
|
|
||
|
private final boolean botan_format;
|
||
|
|
||
|
private byte[] randomBytes(int n)
|
||
|
{
|
||
|
byte[] b = new byte[n];
|
||
|
r.nextBytes(b);
|
||
|
return b;
|
||
|
}
|
||
|
|
||
|
private void hexOut(byte[] b) throws IOException
|
||
|
{
|
||
|
// HexEncoder uses lowercase, and Botan's test vectors must
|
||
|
// be in uppercase, so...
|
||
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||
|
h.encode(b, 0, b.length, os);
|
||
|
String s = os.toString("US-ASCII");
|
||
|
System.out.print(s.toUpperCase(Locale.US));
|
||
|
}
|
||
|
|
||
|
private void printCArray(byte[] a) throws IOException
|
||
|
{
|
||
|
byte[] b = new byte[1];
|
||
|
for (int i = 0; i < a.length; i++)
|
||
|
{
|
||
|
if (i > 0)
|
||
|
System.out.print(", ");
|
||
|
System.out.print("0x");
|
||
|
b[0] = a[i];
|
||
|
hexOut(b);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void printVector(byte[] key, byte[] plaintext, byte[] ciphertext)
|
||
|
throws IOException
|
||
|
{
|
||
|
if (botan_format)
|
||
|
{
|
||
|
hexOut(plaintext);
|
||
|
System.out.print(":");
|
||
|
hexOut(ciphertext);
|
||
|
System.out.println(":\\");
|
||
|
hexOut(key);
|
||
|
System.out.println();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
System.out.println(" {");
|
||
|
System.out.println(" 16,");
|
||
|
System.out.print(" { ");
|
||
|
printCArray (key);
|
||
|
System.out.println(" },");
|
||
|
System.out.print(" { ");
|
||
|
printCArray (plaintext);
|
||
|
System.out.println(" },");
|
||
|
System.out.print(" { ");
|
||
|
printCArray (ciphertext);
|
||
|
System.out.println(" }");
|
||
|
System.out.println(" },");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private KeyParameter maybe_schedule_key(byte[] key)
|
||
|
{
|
||
|
if (schedule_key)
|
||
|
{
|
||
|
noekeon.init(true, null_key);
|
||
|
byte[] scheduled = new byte[16];
|
||
|
noekeon.processBlock(key, 0, scheduled, 0);
|
||
|
return new KeyParameter(scheduled);
|
||
|
}
|
||
|
else
|
||
|
return new KeyParameter(key);
|
||
|
}
|
||
|
|
||
|
private byte[] encrypt(byte[] plaintext, byte[] key)
|
||
|
{
|
||
|
KeyParameter kp = maybe_schedule_key(key);
|
||
|
noekeon.init(true, kp);
|
||
|
byte[] ciphertext = new byte[16];
|
||
|
noekeon.processBlock(plaintext, 0, ciphertext, 0);
|
||
|
return ciphertext;
|
||
|
}
|
||
|
|
||
|
public NoekeonVects(long seed, boolean schedule_key, boolean botan_format)
|
||
|
{
|
||
|
this.schedule_key = schedule_key;
|
||
|
this.botan_format = botan_format;
|
||
|
r.addSeedMaterial(seed);
|
||
|
}
|
||
|
|
||
|
public void ecb_vectors() throws IOException
|
||
|
{
|
||
|
for (int i = 0; i < 8; i++)
|
||
|
{
|
||
|
byte[] key = randomBytes(16);
|
||
|
byte[] plaintext = randomBytes(16);
|
||
|
byte[] ciphertext = encrypt(plaintext, key);
|
||
|
printVector(key, plaintext, ciphertext);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void eax_vectors() throws Exception
|
||
|
{
|
||
|
System.out.println("EAX-noekeon (16 byte key)");
|
||
|
EAXBlockCipher eax = new EAXBlockCipher(new NoekeonEngine());
|
||
|
byte[] output = new byte[48];
|
||
|
byte[] tag = new byte[16];
|
||
|
|
||
|
for (int j = 0; j < 16; j++)
|
||
|
tag[j] = (byte) j;
|
||
|
|
||
|
for (int i = 0; i <= 32; i++)
|
||
|
{
|
||
|
byte[] header_nonce_plaintext = new byte[i];
|
||
|
for (int j = 0; j < i; j++)
|
||
|
header_nonce_plaintext[j] = (byte) j;
|
||
|
AEADParameters params =
|
||
|
new AEADParameters(maybe_schedule_key(tag),
|
||
|
128,
|
||
|
header_nonce_plaintext,
|
||
|
header_nonce_plaintext);
|
||
|
eax.init(true, params);
|
||
|
int off = eax.processBytes(header_nonce_plaintext, 0, i,
|
||
|
output, 0);
|
||
|
off += eax.doFinal(output, off);
|
||
|
if (off != i + 16)
|
||
|
throw new RuntimeException("didn't expect that");
|
||
|
byte[] ciphertext = new byte[i];
|
||
|
for (int j = 0; j < i; j++)
|
||
|
ciphertext[j] = output[j];
|
||
|
for (int j = 0; j < 16; j++)
|
||
|
tag[j] = output[i + j];
|
||
|
System.out.print(i < 10 ? " " : " ");
|
||
|
System.out.print(i);
|
||
|
System.out.print(": ");
|
||
|
hexOut(ciphertext);
|
||
|
System.out.print(", ");
|
||
|
hexOut(tag);
|
||
|
System.out.println();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static void main(String[] argv) throws Exception
|
||
|
{
|
||
|
NoekeonVects bot = new NoekeonVects(0xdefacedbadfacadeL, true, true);
|
||
|
NoekeonVects tom = new NoekeonVects(0xdefacedbadfacadeL, false, false);
|
||
|
System.out.println("# ECB vectors for indirect Noekeon, in Botan's");
|
||
|
System.out.println("# test vector format, suitable for insertion");
|
||
|
System.out.println("# into Botan's file checks/validate.dat");
|
||
|
System.out.println("# Block cipher format is plaintext:ciphertext:key");
|
||
|
bot.ecb_vectors();
|
||
|
System.out.println();
|
||
|
System.out.println("/* ECB vectors for direct Noekeon, as C arrays");
|
||
|
System.out.println(" * suitable for insertion into LibTomCrypt's");
|
||
|
System.out.println(" * noekeon_test() in src/ciphers/noekeon.c,");
|
||
|
System.out.println(" * once LTC's PI1/PI2 bug is fixed. */");
|
||
|
tom.ecb_vectors();
|
||
|
System.out.println();
|
||
|
System.out.println("# EAX vectors for indirect Noekeon, in the format");
|
||
|
System.out.println("# generated by LTC's demos/tv_gen.c and consumed");
|
||
|
System.out.println("# by Botan's doc/examples/eax_test.cpp, suitable");
|
||
|
System.out.println("# for insertion in Botan's doc/examples/eax.vec");
|
||
|
bot.eax_vectors();
|
||
|
System.out.println();
|
||
|
System.out.println("# EAX vectors for direct Noekeon, in the format");
|
||
|
System.out.println("# generated by LTC's demos/tv_gen.c and consumed");
|
||
|
System.out.println("# by Botan's doc/examples/eax_test.cpp, which");
|
||
|
System.out.println("# should match LTC's notes/eax_tv.txt, once");
|
||
|
System.out.println("# LTC's PI1/PI2 bug is fixed.");
|
||
|
tom.eax_vectors();
|
||
|
System.out.flush();
|
||
|
}
|
||
|
}
|