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();
 | |
|     }
 | |
| }
 |