/*
* A CCNx library test.
*
* Copyright (C) 2008, 2009, 2011 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.io.content;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.ccnx.ccn.CCNHandle;
import org.ccnx.ccn.impl.CCNFlowControl.SaveType;
import org.ccnx.ccn.impl.security.crypto.jce.CCNCryptoProvider;
import org.ccnx.ccn.impl.support.Log;
import org.ccnx.ccn.io.content.WrappedKey;
import org.ccnx.ccn.io.content.WrappedKey.WrappedKeyObject;
import org.ccnx.ccn.profiles.VersioningProfile;
import org.ccnx.ccn.profiles.security.access.group.GroupAccessControlProfile;
import org.ccnx.ccn.protocol.ContentName;
import org.ccnx.ccn.protocol.PublisherID;
import org.ccnx.ccn.test.CCNTestHelper;
import org.ccnx.ccn.test.Flosser;
import org.ccnx.ccn.test.impl.encoding.XMLEncodableTester;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* Test both encoding/decoding of WrappedKey data structures and writing them
* to CCN using WrappedKeyObjects. Move tests that require either unlimited-strength
* crypto or algorithms that BouncyCastle does not support on all platforms/versions
* to the expanded tests. See apps/examples/ExpandedCryptoTests.
*/
public class WrappedKeyTest {
/**
* Handle naming for the test
*/
static CCNTestHelper testHelper = new CCNTestHelper(PublicKeyObjectTestRepo.class);
public static boolean setupDone = false;
public static KeyPair wrappingKeyPair = null;
public static KeyPair wrappedKeyPair = null;
public static KeyPair wrappedDHKeyPair = null;
public static KeyPair wrappedDSAKeyPair = null;
public static SecretKeySpec wrappingAESKey = null;
public static SecretKeySpec wrappedAESKey = null;
public static String aLabel = "FileEncryptionKeys";
public static byte [] wrappingKeyID = null;
public static ContentName wrappingKeyName = null;
public static ContentName storedKeyName = null;
public static byte [] dummyWrappedKey = new byte[64];
@BeforeClass
public static void setUpBeforeClass() throws Exception {
Security.addProvider(new CCNCryptoProvider());
}
/**
* Do this in the first test. Were doing it in setupBeforeClass, but I think
* it was failing sometimes, possibly because it was too slow.
* @throws Exception
*/
public void setupTest() throws Exception {
if (setupDone) {
return;
}
SecureRandom sr = new SecureRandom();
sr.nextBytes(dummyWrappedKey);
// generate key pair
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024); // go for fast
wrappingKeyPair = kpg.generateKeyPair();
wrappedKeyPair = kpg.generateKeyPair();
wrappingKeyID = PublisherID.generatePublicKeyDigest(wrappingKeyPair.getPublic());
wrappingKeyName = VersioningProfile.addVersion(ContentName.fromNative("/parc/Users/briggs/KEY"));
kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(1024);
wrappedDSAKeyPair = kpg.genKeyPair();
// Generate a 576-bit DH key pair
kpg = KeyPairGenerator.getInstance("DH");
kpg.initialize(576);
wrappedDHKeyPair = kpg.genKeyPair();
byte [] key = new byte[16];
sr.nextBytes(key);
wrappingAESKey = new SecretKeySpec(key, "AES");
sr.nextBytes(key);
wrappedAESKey = new SecretKeySpec(key, "AES");
ContentName nodeName = testHelper.getClassNamespace().append(
ContentName.fromNative("/test/content/File1.txt"));
storedKeyName = GroupAccessControlProfile.nodeKeyName(nodeName);
setupDone = true;
Log.info(Log.FAC_TEST, "Initialized keys for WrappedKeyTest");
}
@Test
public void testWrapUnwrapKey() throws Exception {
Log.info(Log.FAC_TEST, "Starting testWrapUnwrapKey");
// don't use setUpBeforeClass, may not be handling slow initialization well
setupTest();
// for each wrap case, wrap, unwrap, and make sure it matches.
// Wrap secret in secret
Log.info(Log.FAC_TEST, "Wrap secret key in secret key.");
WrappedKey wks = WrappedKey.wrapKey(wrappedAESKey, null, aLabel, wrappingAESKey);
Key unwrappedKey = wks.unwrapKey(wrappingAESKey);
Assert.assertArrayEquals(wrappedAESKey.getEncoded(), unwrappedKey.getEncoded());
// wrap secret in public
Log.info(Log.FAC_TEST, "Wrap secret key in public key.");
WrappedKey wksp = WrappedKey.wrapKey(wrappedAESKey, null, aLabel, wrappingKeyPair.getPublic());
unwrappedKey = wksp.unwrapKey(wrappingKeyPair.getPrivate());
Assert.assertArrayEquals(wrappedAESKey.getEncoded(), unwrappedKey.getEncoded());
// wrap private in public
Log.info(Log.FAC_TEST, "Wrap private key in public key.");
WrappedKey wkpp = WrappedKey.wrapKey(wrappingKeyPair.getPrivate(), null, aLabel, wrappingKeyPair.getPublic());
unwrappedKey = wkpp.unwrapKey(wrappingKeyPair.getPrivate());
Assert.assertArrayEquals(wrappingKeyPair.getPrivate().getEncoded(), unwrappedKey.getEncoded());
// wrap private in secret
Log.info(Log.FAC_TEST, "Wrap private key in secret key.");
Log.info(Log.FAC_TEST, "wpk length " + wrappingKeyPair.getPrivate().getEncoded().length);
WrappedKey wkp = WrappedKey.wrapKey(wrappingKeyPair.getPrivate(), null, aLabel, wrappingAESKey);
unwrappedKey = wkp.unwrapKey(wrappingAESKey);
Assert.assertArrayEquals(wrappingKeyPair.getPrivate().getEncoded(), unwrappedKey.getEncoded());
// ditto for el gamal
/*
* ElGamal encryption requires unlimited strength crypto. This used to be installed
* by default on OSX, but not anymore, and not on Ubuntu or Windows. Moved to expanded tests.
*
wksp = WrappedKey.wrapKey(wrappedAESKey, null, aLabel, wrappingEGKeyPair.getPublic());
unwrappedKey = wksp.unwrapKey(wrappingEGKeyPair.getPrivate());
Assert.assertEquals(new BigInteger(1, wrappedAESKey.getEncoded()), new BigInteger(1, unwrappedKey.getEncoded()));
wkpp = WrappedKey.wrapKey(wrappingEGKeyPair.getPrivate(), null, aLabel, wrappingKeyPair.getPublic());
unwrappedKey = wkpp.unwrapKey(wrappingKeyPair.getPrivate());
Assert.assertArrayEquals(wrappingEGKeyPair.getPrivate().getEncoded(), unwrappedKey.getEncoded());
*/
// wrap DSA private in public key
Log.info(Log.FAC_TEST, "Wrap DSA private in private.");
wkpp = WrappedKey.wrapKey(wrappedDSAKeyPair.getPrivate(), null, aLabel, wrappingKeyPair.getPublic());
unwrappedKey = wkpp.unwrapKey(wrappingKeyPair.getPrivate());
Assert.assertArrayEquals(wrappedDSAKeyPair.getPrivate().getEncoded(), unwrappedKey.getEncoded());
Log.info(Log.FAC_TEST, "Wrap DSA private in secret.");
wkp = WrappedKey.wrapKey(wrappedDSAKeyPair.getPrivate(), null, aLabel, wrappingAESKey);
unwrappedKey = wkp.unwrapKey(wrappingAESKey);
Assert.assertArrayEquals(wrappedDSAKeyPair.getPrivate().getEncoded(), unwrappedKey.getEncoded());
// wrap DH private in public key
Log.info(Log.FAC_TEST, "Wrap DH private in private.");
wkpp = WrappedKey.wrapKey(wrappedDHKeyPair.getPrivate(), null, aLabel, wrappingKeyPair.getPublic());
unwrappedKey = wkpp.unwrapKey(wrappingKeyPair.getPrivate());
Assert.assertArrayEquals(wrappedDHKeyPair.getPrivate().getEncoded(), unwrappedKey.getEncoded());
Log.info(Log.FAC_TEST, "Wrap DH private in secret.");
wkp = WrappedKey.wrapKey(wrappedDHKeyPair.getPrivate(), null, aLabel, wrappingAESKey);
unwrappedKey = wkp.unwrapKey(wrappingAESKey);
Assert.assertArrayEquals(wrappedDHKeyPair.getPrivate().getEncoded(), unwrappedKey.getEncoded());
Log.info(Log.FAC_TEST, "Completed testWrapUnwrapKey");
}
@Test
public void testWrappedKeyByteArrayStringStringStringByteArrayByteArray() throws Exception {
Log.info(Log.FAC_TEST, "Starting testWrappedKeyByteArrayStringStringStringByteArrayByteArray");
// don't use setUpBeforeClass, may not be handling slow initialization well
setupTest();
WrappedKey wka = null;
wka = WrappedKey.wrapKey(wrappedAESKey, null, aLabel,
wrappingKeyPair.getPublic());
WrappedKey wk2 = new WrappedKey(wrappingKeyID,
WrappedKey.wrapAlgorithmForKey(wrappingKeyPair.getPublic().getAlgorithm()),
wrappedAESKey.getAlgorithm(),
aLabel,
wka.encryptedNonceKey(),
wka.encryptedKey());
WrappedKey dwk = new WrappedKey();
WrappedKey bdwk = new WrappedKey();
XMLEncodableTester.encodeDecodeTest("WrappedKey(full)", wk2, dwk, bdwk);
wka.setWrappingKeyIdentifier(wrappingKeyID);
Log.info(Log.FAC_TEST, "Completed testWrappedKeyByteArrayStringStringStringByteArrayByteArray");
}
@Test
public void testDecodeInputStream() throws Exception {
Log.info(Log.FAC_TEST, "Starting testDecodeInputStream");
// don't use setUpBeforeClass, may not be handling slow initialization well
setupTest();
WrappedKey wk = new WrappedKey(wrappingKeyID, dummyWrappedKey);
WrappedKey dwk = new WrappedKey();
WrappedKey bdwk = new WrappedKey();
XMLEncodableTester.encodeDecodeTest("WrappedKey(dummy)", wk, dwk, bdwk);
WrappedKey wks = WrappedKey.wrapKey(wrappedAESKey, null, aLabel, wrappingAESKey);
WrappedKey dwks = new WrappedKey();
WrappedKey bdwks = new WrappedKey();
XMLEncodableTester.encodeDecodeTest("WrappedKey(symmetric, real)", wks, dwks, bdwks);
WrappedKey wka = WrappedKey.wrapKey(wrappedAESKey, NISTObjectIdentifiers.id_aes128_CBC.toString(),
aLabel, wrappingKeyPair.getPublic());
wka.setWrappingKeyIdentifier(wrappingKeyID);
wka.setWrappingKeyName(wrappingKeyName);
WrappedKey dwka = new WrappedKey();
WrappedKey bdwka = new WrappedKey();
XMLEncodableTester.encodeDecodeTest("WrappedKey(assymmetric wrap symmetric, with id and name)", wka, dwka, bdwka);
Assert.assertArrayEquals(dwka.wrappingKeyIdentifier(), wrappingKeyID);
Log.info(Log.FAC_TEST, "Completed testDecodeInputStream");
}
@Test
public void testWrappedKeyObject() throws Exception {
Log.info(Log.FAC_TEST, "Starting testWrappedKeyObject");
// don't use setUpBeforeClass, may not be handling slow initialization well
setupTest();
WrappedKey wks = WrappedKey.wrapKey(wrappedAESKey, null, aLabel, wrappingAESKey);
WrappedKey wka = WrappedKey.wrapKey(wrappedAESKey, NISTObjectIdentifiers.id_aes128_CBC.toString(),
aLabel, wrappingKeyPair.getPublic());
wka.setWrappingKeyIdentifier(wrappingKeyID);
wka.setWrappingKeyName(wrappingKeyName);
CCNHandle thandle = CCNHandle.open();
CCNHandle thandle2 = CCNHandle.open();
Flosser flosser = null;
try {
flosser = new Flosser();
flosser.handleNamespace(storedKeyName);
WrappedKeyObject wko =
new WrappedKeyObject(storedKeyName, wks, SaveType.RAW, thandle);
wko.save();
Assert.assertTrue(VersioningProfile.hasTerminalVersion(wko.getVersionedName()));
// should update in another thread
WrappedKeyObject wkoread = new WrappedKeyObject(storedKeyName, thandle2);
Assert.assertTrue(wkoread.available());
Assert.assertEquals(wkoread.getVersionedName(), wko.getVersionedName());
Assert.assertEquals(wkoread.wrappedKey(), wko.wrappedKey());
// DKS -- bug in interest handling, can't save wkoread and update wko
wko.save(wka);
Assert.assertTrue(VersioningProfile.isLaterVersionOf(wko.getVersionedName(), wkoread.getVersionedName()));
wkoread.update();
Assert.assertEquals(wkoread.getVersionedName(), wko.getVersionedName());
Assert.assertEquals(wkoread.wrappedKey(), wko.wrappedKey());
Assert.assertEquals(wko.wrappedKey(), wka);
} finally {
if (null != flosser) {
Log.info(Log.FAC_TEST, "WrappedKeyTest: Stopping flosser.");
flosser.stop();
Log.info(Log.FAC_TEST, "WrappedKeyTest: flosser stopped.");
}
thandle.close();
thandle2.close();
}
Log.info(Log.FAC_TEST, "Completed testWrappedKeyObject");
}
}