/* * A CCNx library test. * * Copyright (C) 2008-2012 Palo Alto Research Center, Inc. * * This work is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2 as published by the * Free Software Foundation. * This work is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ package org.ccnx.ccn.test.security.crypto; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.Security; import java.util.Random; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.ccnx.ccn.impl.security.crypto.CCNDigestHelper; import org.ccnx.ccn.impl.security.crypto.CCNMerkleTree; import org.ccnx.ccn.impl.support.DataUtils; import org.ccnx.ccn.impl.support.Log; import org.ccnx.ccn.profiles.SegmentationProfile; import org.ccnx.ccn.profiles.VersioningProfile; import org.ccnx.ccn.protocol.ContentName; import org.ccnx.ccn.protocol.ContentObject; import org.ccnx.ccn.protocol.KeyLocator; import org.ccnx.ccn.protocol.PublisherPublicKeyDigest; import org.ccnx.ccn.protocol.Signature; import org.ccnx.ccn.protocol.SignedInfo; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; /** * Test CCN-specific Merkle tree functionality. */ public class CCNMerkleTreeTest { protected static Random _rand = new Random(); // don't need SecureRandom protected static KeyPair _pair = null; static ContentName keyname = new ContentName("test","keys","treeKey"); static ContentName baseName = new ContentName("test","data","treeTest"); static KeyPair pair = null; static PublisherPublicKeyDigest publisher = null; static KeyLocator keyLoc = null; @BeforeClass public static void setUpBeforeClass() throws Exception { try { Security.addProvider(new BouncyCastleProvider()); // generate key pair KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); kpg.initialize(512); // go for fast pair = kpg.generateKeyPair(); publisher = new PublisherPublicKeyDigest(pair.getPublic()); keyLoc = new KeyLocator(pair.getPublic()); } catch (Exception e) { System.out.println("Exception in test setup: " + e.getMessage()); e.printStackTrace(); throw e; } } @Test public void testMerkleTree() throws Exception { Log.info(Log.FAC_TEST, "Starting testMerkleTree"); int [] sizes = new int[]{128,256,512,4096}; try { testTree(0, sizes[0], false); Assert.fail("CCNMerkleTree should throw an exception for tree sizes < 2."); } catch (IllegalArgumentException e) { // ok } try { testTree(1, sizes[0], false); Assert.fail("CCNMerkleTree should throw an exception for tree sizes < 2."); } catch (IllegalArgumentException e) { // ok } System.out.println("Testing small trees, fixed block widths."); for (int i=2; i < 515; ++i) { testTree(i,sizes[i%sizes.length],false); } System.out.println("Testing large trees, fixed block widths."); int [] nodecounts = new int[]{1000,1001,1025,1098,1536,1575,2053,5147,8900,9998}; // int [] nodecounts = new int[]{1000,1001,1025,1098,1536,1575,2053,5147,8900,9998,9999,10000}; for (int i=0; i < nodecounts.length; ++i) { testTreeWrapper(nodecounts[i],sizes[i%sizes.length],false); } Log.info(Log.FAC_TEST, "Completed testMerkleTree"); } @Test public void testMerkleTreeBuf() { Log.info(Log.FAC_TEST, "Starting testMerkleTreeBuf"); int [] sizes = new int[]{128,256,512,4096}; System.out.println("Testing small trees, random block widths."); for (int i=10; i < 515; ++i) { testTreeWrapper(i,sizes[i%sizes.length], true); } Log.info(Log.FAC_TEST, "Completed testMerkleTreeBuf"); } public static void testTreeWrapper(int testNodeCount, int blockWidth, boolean randomWidths) { try { testTree(testNodeCount, blockWidth, randomWidths); } catch (Exception e) { System.out.println("Building tree of " + testNodeCount + " nodes, random widths? " + randomWidths + ". Caught a " + e.getClass().getName() + " exception: " + e.getMessage()); e.printStackTrace(); Assert.fail("Exception " + e.getClass().getName() + " in testTreeWrapper."); } } public static ContentObject [] makeContent(ContentName rootName, int numNodes, int maxLength, boolean randLengths) { ContentObject [] cos = new ContentObject[numNodes]; byte [][] bufs = new byte[numNodes][]; SignedInfo si = new SignedInfo(publisher, keyLoc); for (int i=0; i < numNodes; ++i) { int blockLen = 0; if (randLengths) { while (blockLen == 0) { blockLen = Math.abs(_rand.nextInt(maxLength)); } } else { blockLen = maxLength; } bufs[i] = new byte[blockLen]; _rand.nextBytes(bufs[i]); cos[i] = new ContentObject( SegmentationProfile.segmentName(rootName, (i + SegmentationProfile.baseSegment())), si, bufs[i], (Signature)null); } return cos; } public static void testTree(int nodeCount, int blockWidth, boolean randomWidths) throws Exception { int version = _rand.nextInt(1000); ContentName theName = new ContentName(baseName, "testDocBuffer.txt"); theName = VersioningProfile.addVersion(theName, version); try { ContentObject [] cos = makeContent(theName, nodeCount, blockWidth, randomWidths); // TODO DKS Need to do offset versions with different ranges of fragments // Generate a merkle tree. Verify each path for the content. CCNMerkleTree tree = new CCNMerkleTree(cos, pair.getPrivate()); tree.setSignatures(); System.out.println("Constructed tree of numleaves: " + tree.numLeaves() + " max pathlength: " + tree.maxDepth()); ContentObject block; for (int i=0; i < tree.numLeaves()-1; ++i) { block = cos[i]; boolean result = block.verify(pair.getPublic()); if (!result) { System.out.println("Block name: " + tree.segmentName(i) + " num " + i + " verified? " + result + ", content: " + DataUtils.printBytes(block.digest())); byte [] digest = CCNDigestHelper.digest(block.encode()); byte [] tbsdigest = CCNDigestHelper.digest(block.prepareContent()); System.out.println("Raw content digest: " + DataUtils.printBytes(CCNDigestHelper.digest(block.content())) + " object content digest: " + DataUtils.printBytes(CCNDigestHelper.digest(block.content()))); System.out.println("Block: " + block.name() + " timestamp: " + block.signedInfo().getTimestamp() + " encoded digest: " + DataUtils.printBytes(digest) + " tbs content: " + DataUtils.printBytes(tbsdigest)); } else if (i % 100 == 0) { System.out.println("Block name: " + tree.segmentName(i) + " num " + i + " verified? " + result + ", content: " + DataUtils.printBytes(block.digest())); } Assert.assertTrue("Path " + i + " failed to verify.", result); } tree = null; } catch (Exception e) { System.out.println("Exception in testTree: " + e.getClass().getName() + ": " + e.getMessage()); e.printStackTrace(); throw(e); // must re-throw rather than assert fail; one test actually expects an exception } } }