package org.bouncycastle.crypto.test;

import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.engines.AESFastEngine;
import org.bouncycastle.crypto.engines.BlowfishEngine;
import org.bouncycastle.crypto.engines.DESEngine;
import org.bouncycastle.crypto.engines.DESedeEngine;
import org.bouncycastle.crypto.engines.RijndaelEngine;
import org.bouncycastle.crypto.engines.Shacal2Engine;
import org.bouncycastle.crypto.macs.CMac;
import org.bouncycastle.crypto.macs.CMacWithIV;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.test.SimpleTest;

CMAC tester - Official Test Vectors.
/** * CMAC tester - <a href="http://www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/tv/omac1-tv.txt">Official Test Vectors</a>. */
public class CMacTest extends SimpleTest { private static final byte[] keyBytes128 = Hex.decode("2b7e151628aed2a6abf7158809cf4f3c"); private static final byte[] keyBytes192 = Hex.decode( "8e73b0f7da0e6452c810f32b809079e5" + "62f8ead2522c6b7b"); private static final byte[] keyBytes256 = Hex.decode( "603deb1015ca71be2b73aef0857d7781" + "1f352c073b6108d72d9810a30914dff4"); private static final byte[] input0 = Hex.decode(""); private static final byte[] input16 = Hex.decode("6bc1bee22e409f96e93d7e117393172a"); private static final byte[] input40 = Hex.decode( "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411"); private static final byte[] input64 = Hex.decode( "6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710"); private static final byte[] output_k128_m0 = Hex.decode("bb1d6929e95937287fa37d129b756746"); private static final byte[] output_k128_m16 = Hex.decode("070a16b46b4d4144f79bdd9dd04a287c"); private static final byte[] output_k128_m40 = Hex.decode("dfa66747de9ae63030ca32611497c827"); private static final byte[] output_k128_m64 = Hex.decode("51f0bebf7e3b9d92fc49741779363cfe"); private static final byte[] output_k192_m0 = Hex.decode("d17ddf46adaacde531cac483de7a9367"); private static final byte[] output_k192_m16 = Hex.decode("9e99a7bf31e710900662f65e617c5184"); private static final byte[] output_k192_m40 = Hex.decode("8a1de5be2eb31aad089a82e6ee908b0e"); private static final byte[] output_k192_m64 = Hex.decode("a1d5df0eed790f794d77589659f39a11"); private static final byte[] output_k256_m0 = Hex.decode("028962f61b7bf89efc6b551f4667d983"); private static final byte[] output_k256_m16 = Hex.decode("28a7023f452e8f82bd4bf28d8c37c35c"); private static final byte[] output_k256_m40 = Hex.decode("aaf3d8f1de5640c232f5b169b9c911e6"); private static final byte[] output_k256_m64 = Hex.decode("e1992190549f6ed5696a2c056c315410"); private static final byte[] output_des_ede = Hex.decode("1ca670dea381d37c"); private static final byte[] general_input = Strings.toByteArray("The quick brown fox jumps over the lazy dog."); public CMacTest() { } public void performTest() { BlockCipher cipher = new AESFastEngine(); Mac mac = new CMac(cipher, 128); //128 bytes key KeyParameter key = new KeyParameter(keyBytes128); // 0 bytes message - 128 bytes key mac.init(key); mac.update(input0, 0, input0.length); byte[] out = new byte[16]; mac.doFinal(out, 0); if (!areEqual(out, output_k128_m0)) { fail("Failed - expected " + new String(Hex.encode(output_k128_m0)) + " got " + new String(Hex.encode(out))); } // 16 bytes message - 128 bytes key mac.init(key); mac.update(input16, 0, input16.length); out = new byte[16]; mac.doFinal(out, 0); if (!areEqual(out, output_k128_m16)) { fail("Failed - expected " + new String(Hex.encode(output_k128_m16)) + " got " + new String(Hex.encode(out))); } // 40 bytes message - 128 bytes key mac.init(key); mac.update(input40, 0, input40.length); out = new byte[16]; mac.doFinal(out, 0); if (!areEqual(out, output_k128_m40)) { fail("Failed - expected " + new String(Hex.encode(output_k128_m40)) + " got " + new String(Hex.encode(out))); } // 64 bytes message - 128 bytes key mac.init(key); mac.update(input64, 0, input64.length); out = new byte[16]; mac.doFinal(out, 0); if (!areEqual(out, output_k128_m64)) { fail("Failed - expected " + new String(Hex.encode(output_k128_m64)) + " got " + new String(Hex.encode(out))); } //192 bytes key key = new KeyParameter(keyBytes192); // 0 bytes message - 192 bytes key mac.init(key); mac.update(input0, 0, input0.length); out = new byte[16]; mac.doFinal(out, 0); if (!areEqual(out, output_k192_m0)) { fail("Failed - expected " + new String(Hex.encode(output_k192_m0)) + " got " + new String(Hex.encode(out))); } // 16 bytes message - 192 bytes key mac.init(key); mac.update(input16, 0, input16.length); out = new byte[16]; mac.doFinal(out, 0); if (!areEqual(out, output_k192_m16)) { fail("Failed - expected " + new String(Hex.encode(output_k192_m16)) + " got " + new String(Hex.encode(out))); } // 40 bytes message - 192 bytes key mac.init(key); mac.update(input40, 0, input40.length); out = new byte[16]; mac.doFinal(out, 0); if (!areEqual(out, output_k192_m40)) { fail("Failed - expected " + new String(Hex.encode(output_k192_m40)) + " got " + new String(Hex.encode(out))); } // 64 bytes message - 192 bytes key mac.init(key); mac.update(input64, 0, input64.length); out = new byte[16]; mac.doFinal(out, 0); if (!areEqual(out, output_k192_m64)) { fail("Failed - expected " + new String(Hex.encode(output_k192_m64)) + " got " + new String(Hex.encode(out))); } //256 bytes key key = new KeyParameter(keyBytes256); // 0 bytes message - 256 bytes key mac.init(key); mac.update(input0, 0, input0.length); out = new byte[16]; mac.doFinal(out, 0); if (!areEqual(out, output_k256_m0)) { fail("Failed - expected " + new String(Hex.encode(output_k256_m0)) + " got " + new String(Hex.encode(out))); } // 16 bytes message - 256 bytes key mac.init(key); mac.update(input16, 0, input16.length); out = new byte[16]; mac.doFinal(out, 0); if (!areEqual(out, output_k256_m16)) { fail("Failed - expected " + new String(Hex.encode(output_k256_m16)) + " got " + new String(Hex.encode(out))); } // 40 bytes message - 256 bytes key mac.init(key); mac.update(input40, 0, input40.length); out = new byte[16]; mac.doFinal(out, 0); if (!areEqual(out, output_k256_m40)) { fail("Failed - expected " + new String(Hex.encode(output_k256_m40)) + " got " + new String(Hex.encode(out))); } // 64 bytes message - 256 bytes key mac.init(key); mac.update(input64, 0, input64.length); out = new byte[16]; mac.doFinal(out, 0); if (!areEqual(out, output_k256_m64)) { fail("Failed - expected " + new String(Hex.encode(output_k256_m64)) + " got " + new String(Hex.encode(out))); } // CMAC with IV // 16 bytes message - 256 bytes key mac = new CMacWithIV(new AESFastEngine()); mac.init(key); mac.update(input16, 0, input16.length); out = new byte[16]; mac.doFinal(out, 0); if (!areEqual(out, output_k256_m16)) { fail("Failed - expected " + new String(Hex.encode(output_k256_m16)) + " got " + new String(Hex.encode(out))); } // CMAC with IV // 16 bytes message - 256 bytes key mac = new CMacWithIV(new AESFastEngine()); mac.init(new ParametersWithIV(key, Hex.decode("000102030405060708090a0b0c0d0e0f"))); mac.update(input16, 0, input16.length); out = new byte[16]; mac.doFinal(out, 0); if (areEqual(out, output_k256_m16)) { fail("Failed - got " + new String(Hex.encode(output_k256_m16))); } if (!areEqual(out, Hex.decode("9347a60c64061b9ff2a92522ca8e08fc"))) { fail("Failed - expected " + "9347a60c64061b9ff2a92522ca8e08fc" + " got " + new String(Hex.encode(out))); } testCMac(new DESedeEngine(), keyBytes128, input0, output_des_ede); testCMac(new RijndaelEngine(), "2b7e151628aed2a6abf7158809cf4f3c", "682b9b57e769cc63231cf778c5c76646"); testCMac(new RijndaelEngine(192), "2b7e151628aed2a6abf7158809cf4f3c", "2a11b6bdd1e4f8b6127c2960859ae73ede59c7200d77ff45"); testCMac(new RijndaelEngine(256), "2b7e151628aed2a6abf7158809cf4f3c", "316d1df4084ada3e10b26266ae1fdae170a9d824ab37e981f06227c80c80fddd"); testCMac(new BlowfishEngine(), "2b7e151628aed2a6abf7158809cf4f3c", "875d73b9bc3de78a"); testCMac(new DESEngine(), "2b7e151628aed2a6", "3cc3a242585e49f9"); testCMac(new Shacal2Engine(), "2b7e151628aed2a6abf7158809cf4f3c", "794b2766cd0d550877f1ded48ab74f9ddff20f32e6d69fae8a1ede4205e7d640"); testExceptions(); } private void testCMac(BlockCipher cipher, String keyBytes, String expected) { testCMac(cipher, Hex.decode(keyBytes), general_input, Hex.decode(expected)); } private void testCMac(BlockCipher cipher, byte[] keyBytes, byte[] input, byte[] expected) { Mac mac = new CMac(cipher, cipher.getBlockSize() * 8); KeyParameter key = new KeyParameter(keyBytes); mac.init(key); mac.update(input, 0, input.length); byte[] out = new byte[mac.getMacSize()]; mac.doFinal(out, 0); if (!areEqual(out, expected)) { fail("Failed - expected " + Strings.fromByteArray(Hex.encode(expected)) + " got " + new String(Hex.encode(out))); } } private void testExceptions() { try { CMac mac = new CMac(new AESEngine()); mac.init(new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16])); fail("CMac does not accept IV"); } catch(IllegalArgumentException e) { // Expected } } public String getName() { return "CMac"; } public static void main(String[] args) { runTest(new CMacTest()); } }