Author: j16sdiz
Date: 2008-07-04 13:53:29 +0000 (Fri, 04 Jul 2008)
New Revision: 20989
Added:
branches/saltedhashstore/freenet/src/freenet/store/saltedhash/CipherManager.java
Modified:
branches/saltedhashstore/freenet/src/freenet/store/saltedhash/SaltedHashFreenetStore.java
Log:
factor out CipherManager
Added:
branches/saltedhashstore/freenet/src/freenet/store/saltedhash/CipherManager.java
===================================================================
---
branches/saltedhashstore/freenet/src/freenet/store/saltedhash/CipherManager.java
(rev 0)
+++
branches/saltedhashstore/freenet/src/freenet/store/saltedhash/CipherManager.java
2008-07-04 13:53:29 UTC (rev 20989)
@@ -0,0 +1,163 @@
+/* This code is part of Freenet. It is distributed under the GNU General
+ * Public License, version 2 (or at your option any later version). See
+ * http://www.gnu.org/ for further details of the GPL. */
+package freenet.store.saltedhash;
+
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Random;
+
+import freenet.crypt.BlockCipher;
+import freenet.crypt.PCFBMode;
+import freenet.crypt.SHA256;
+import freenet.crypt.UnsupportedCipherException;
+import freenet.crypt.ciphers.Rijndael;
+import freenet.support.ByteArrayWrapper;
+import freenet.support.Logger;
+
+/**
+ * Cipher Manager
+ *
+ * Manage all kind of digestion and encryption in store
+ *
+ * @author sdiz
+ */
+public class CipherManager {
+ /**
+ * <tt>0x10</tt> bytes of salt for better digestion, not too salty.
+ */
+ private byte[] salt;
+
+ CipherManager(byte[] salt) {
+ assert salt.length == 0x10;
+ this.salt = salt;
+ }
+
+ /**
+ * Get salt
+ *
+ * @return salt
+ */
+ byte[] getSalt() {
+ return salt;
+ }
+
+ /**
+ * Cache for digested keys
+ */
+ private Map<ByteArrayWrapper, byte[]> digestRoutingKeyCache = new
LinkedHashMap<ByteArrayWrapper, byte[]>() {
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<ByteArrayWrapper,
byte[]> eldest) {
+ return size() > 128;
+ }
+ };
+
+ /**
+ * Get digested routing key
+ *
+ * @param plainKey
+ * @return
+ */
+ byte[] getDigestedKey(byte[] plainKey) {
+ ByteArrayWrapper key = new ByteArrayWrapper(plainKey);
+ synchronized (digestRoutingKeyCache) {
+ byte[] dk = digestRoutingKeyCache.get(key);
+ if (dk != null)
+ return dk;
+ }
+
+ MessageDigest digest = SHA256.getMessageDigest();
+ try {
+ digest.update(plainKey);
+ digest.update(salt);
+
+ byte[] hashedRoutingKey = digest.digest();
+ assert hashedRoutingKey.length == 0x20;
+
+ synchronized (digestRoutingKeyCache) {
+ digestRoutingKeyCache.put(key,
hashedRoutingKey);
+ }
+
+ return hashedRoutingKey;
+ } finally {
+ SHA256.returnMessageDigest(digest);
+ }
+ }
+
+ /**
+ * Encrypt this entry
+ */
+ void encrypt(SaltedHashFreenetStore.Entry entry, Random random) {
+ if (entry.isEncrypted)
+ return;
+
+ entry.dataEncryptIV = new byte[16];
+ random.nextBytes(entry.dataEncryptIV);
+
+ PCFBMode cipher = makeCipher(entry.dataEncryptIV,
entry.plainRoutingKey);
+ entry.header = cipher.blockEncipher(entry.header, 0,
entry.header.length);
+ entry.data = cipher.blockEncipher(entry.data, 0,
entry.data.length);
+
+ entry.getDigestedRoutingKey();
+ entry.isEncrypted = true;
+ }
+
+ /**
+ * Verify and decrypt this entry
+ *
+ * @param routingKey
+ * @return <code>true</code> if the <code>routeKey</code> match and the
entry is decrypted.
+ */
+ boolean decrypt(SaltedHashFreenetStore.Entry entry, byte[] routingKey) {
+ if (!entry.isEncrypted) {
+ // Already decrypted
+ if (Arrays.equals(entry.plainRoutingKey, routingKey))
+ return true;
+ else
+ return false;
+ }
+
+ if (entry.plainRoutingKey != null) {
+ // we knew the key
+ if (!Arrays.equals(entry.plainRoutingKey, routingKey)) {
+ return false;
+ }
+ } else {
+ // we do not know the plain key, let's check the digest
+ if (!Arrays.equals(entry.digestedRoutingKey,
getDigestedKey(routingKey)))
+ return false;
+ }
+
+ entry.plainRoutingKey = routingKey;
+
+ PCFBMode cipher = makeCipher(entry.dataEncryptIV,
entry.plainRoutingKey);
+ entry.header = cipher.blockDecipher(entry.header, 0,
entry.header.length);
+ entry.data = cipher.blockDecipher(entry.data, 0,
entry.data.length);
+
+ entry.isEncrypted = false;
+
+ return true;
+ }
+
+ /**
+ * Create PCFBMode object for this key
+ */
+ PCFBMode makeCipher(byte[] iv, byte[] key) {
+ byte[] iv2 = new byte[0x20]; // 256 bits
+
+ System.arraycopy(salt, 0, iv2, 0, 0x10);
+ System.arraycopy(iv, 0, iv2, 0x10, 0x10);
+
+ try {
+ BlockCipher aes = new Rijndael(256, 256);
+ aes.initialize(key);
+
+ return PCFBMode.create(aes, iv2);
+ } catch (UnsupportedCipherException e) {
+ Logger.error(this, "Rijndael not supported!", e);
+ throw new Error("Rijndael not supported!", e);
+ }
+ }
+}
Modified:
branches/saltedhashstore/freenet/src/freenet/store/saltedhash/SaltedHashFreenetStore.java
===================================================================
---
branches/saltedhashstore/freenet/src/freenet/store/saltedhash/SaltedHashFreenetStore.java
2008-07-04 13:53:05 UTC (rev 20988)
+++
branches/saltedhashstore/freenet/src/freenet/store/saltedhash/SaltedHashFreenetStore.java
2008-07-04 13:53:29 UTC (rev 20989)
@@ -9,14 +9,11 @@
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
-import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.util.Arrays;
-import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
-import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedSet;
@@ -31,11 +28,6 @@
import org.tanukisoftware.wrapper.WrapperManager;
-import freenet.crypt.BlockCipher;
-import freenet.crypt.PCFBMode;
-import freenet.crypt.SHA256;
-import freenet.crypt.UnsupportedCipherException;
-import freenet.crypt.ciphers.Rijndael;
import freenet.keys.KeyVerifyException;
import freenet.node.SemiOrderedShutdownHook;
import freenet.store.FreenetStore;
@@ -43,7 +35,6 @@
import freenet.store.StorableBlock;
import freenet.store.StoreCallback;
import freenet.support.BloomFilter;
-import freenet.support.ByteArrayWrapper;
import freenet.support.Fields;
import freenet.support.HexUtil;
import freenet.support.Logger;
@@ -195,7 +186,7 @@
*/
private Entry probeEntry(byte[] routingKey) throws IOException {
if (checkBloom)
- if
(!bloomFilter.checkFilter(getDigestedRoutingKey(routingKey)))
+ if
(!bloomFilter.checkFilter(cipherManager.getDigestedKey(routingKey)))
return null;
Entry entry = probeEntry0(routingKey, storeSize);
@@ -279,7 +270,7 @@
if (logDEBUG)
Logger.debug(this,
"probing, write to i=" + i + ", offset=" + offset[i]);
if (updateBloom)
-
bloomFilter.updateFilter(getDigestedRoutingKey(routingKey));
+
bloomFilter.updateFilter(cipherManager.getDigestedKey(routingKey));
writeEntry(entry, offset[i]);
writes.incrementAndGet();
keyCount.incrementAndGet();
@@ -292,7 +283,7 @@
if (logDEBUG)
Logger.debug(this, "collision, write to
i=0, offset=" + offset[0]);
if (updateBloom)
-
bloomFilter.updateFilter(getDigestedRoutingKey(routingKey));
+
bloomFilter.updateFilter(cipherManager.getDigestedKey(routingKey));
oldEntry = readEntry(offset[0], null);
writeEntry(entry, offset[0]);
writes.incrementAndGet();
@@ -355,17 +346,17 @@
* Total length is padded to multiple of 512bytes. All reserved bytes
should be zero when
* written, ignored on read.
*/
- private class Entry {
- private byte[] plainRoutingKey;
- private byte[] digestedRoutingKey;
- private byte[] dataEncryptIV;
+ class Entry {
+ byte[] plainRoutingKey;
+ byte[] digestedRoutingKey;
+ byte[] dataEncryptIV;
private long flag;
private long storeSize;
private byte generation;
- private byte[] header;
- private byte[] data;
+ byte[] header;
+ byte[] data;
- private boolean isEncrypted;
+ boolean isEncrypted;
public long curOffset = -1;
@@ -451,7 +442,7 @@
public ByteBuffer toByteBuffer() {
ByteBuffer out = ByteBuffer.allocate((int)
entryTotalLength);
- encrypt();
+ cipherManager.encrypt(this, random);
out.put(getDigestedRoutingKey());
out.put(dataEncryptIV);
@@ -480,7 +471,7 @@
public StorableBlock getStorableBlock(byte[] routingKey, byte[]
fullKey) throws KeyVerifyException {
if ((flag & ENTRY_FLAG_OCCUPIED) == 0)
return null; // this is a free block
- if (!decrypt(routingKey))
+ if (!cipherManager.decrypt(this, routingKey))
return null;
StorableBlock block = callback.construct(data, header,
routingKey, fullKey);
@@ -502,89 +493,13 @@
return getOffsetFromPlainKey(plainRoutingKey,
storeSize);
}
- /**
- * Verify and decrypt this entry
- *
- * @param routingKey
- * @return <code>true</code> if the <code>routeKey</code> match
and the entry is decrypted.
- */
- private boolean decrypt(byte[] routingKey) {
- if (!isEncrypted) {
- // Already decrypted
- if (Arrays.equals(this.plainRoutingKey,
routingKey))
- return true;
- else
- return false;
- }
-
- if (plainRoutingKey != null) {
- // we knew the key
- if (!Arrays.equals(this.plainRoutingKey,
routingKey)) {
- return false;
- }
- } else {
- // we do not know the plain key, let's check
the digest
- if (!Arrays.equals(this.digestedRoutingKey,
SaltedHashFreenetStore.this
- .getDigestedRoutingKey(routingKey)))
- return false;
- }
-
- this.plainRoutingKey = routingKey;
-
- PCFBMode cipher = makeCipher(plainRoutingKey);
- header = cipher.blockDecipher(header, 0, header.length);
- data = cipher.blockDecipher(data, 0, data.length);
-
- isEncrypted = false;
-
- return true;
- }
-
- /**
- * Encrypt this entry
- */
- private void encrypt() {
- if (isEncrypted)
- return;
-
- dataEncryptIV = new byte[16];
- random.nextBytes(dataEncryptIV);
-
- PCFBMode cipher = makeCipher(plainRoutingKey);
- header = cipher.blockEncipher(header, 0, header.length);
- data = cipher.blockEncipher(data, 0, data.length);
-
- getDigestedRoutingKey();
- isEncrypted = true;
- }
-
- /**
- * Create Cipher
- */
- private PCFBMode makeCipher(byte[] routingKey) {
- byte[] iv = new byte[0x20]; // 256 bits
-
- System.arraycopy(salt, 0, iv, 0, 0x10);
- System.arraycopy(dataEncryptIV, 0, iv, 0x10, 0x10);
-
- try {
- BlockCipher aes = new Rijndael(256, 256);
- aes.initialize(routingKey);
-
- return PCFBMode.create(aes, iv);
- } catch (UnsupportedCipherException e) {
- Logger.error(this, "Rijndael not supported!",
e);
- throw new RuntimeException(e);
- }
- }
-
public boolean isFree() {
return (flag & ENTRY_FLAG_OCCUPIED) == 0;
}
public byte[] getDigestedRoutingKey() {
if (digestedRoutingKey == null)
- digestedRoutingKey =
SaltedHashFreenetStore.this.getDigestedRoutingKey(this.plainRoutingKey);
+ digestedRoutingKey =
cipherManager.getDigestedKey(plainRoutingKey);
return digestedRoutingKey;
}
@@ -656,7 +571,7 @@
Entry entry = new Entry(bf);
if (routingKey != null) {
- boolean decrypted = entry.decrypt(routingKey);
+ boolean decrypted = cipherManager.decrypt(entry,
routingKey);
if (!decrypted)
return null;
}
@@ -675,7 +590,7 @@
* </ul>
*/
private void writeEntry(Entry entry, long offset) throws IOException {
- entry.encrypt();
+ cipherManager.encrypt(entry, random);
int split = (int) (offset % FILE_SPLIT);
long rawOffset = (offset / FILE_SPLIT) * entryTotalLength;
@@ -816,19 +731,21 @@
* Load config file
*/
private void loadConfigFile() throws IOException {
- assert salt == null; // never load the configuration twice
+ assert cipherManager == null; // never load the configuration
twice
if (!configFile.exists()) {
// create new
- salt = new byte[0x10];
- random.nextBytes(salt);
+ byte[] newsalt = new byte[0x10];
+ random.nextBytes(newsalt);
+ cipherManager = new CipherManager(newsalt);
writeConfigFile();
} else {
// try to load
RandomAccessFile raf = new RandomAccessFile(configFile,
"r");
- salt = new byte[0x10];
+ byte[] salt = new byte[0x10];
raf.readFully(salt);
+ cipherManager = new CipherManager(salt);
storeSize = raf.readLong();
prevStoreSize = raf.readLong();
@@ -853,7 +770,7 @@
File tempConfig = new File(configFile.getPath() +
".tmp");
RandomAccessFile raf = new RandomAccessFile(tempConfig,
"rw");
raf.seek(0);
- raf.write(salt);
+ raf.write(cipherManager.getSalt());
raf.writeLong(storeSize);
raf.writeLong(prevStoreSize);
@@ -1355,11 +1272,11 @@
* @return <code>true</code> if all the offsets are locked.
*/
private boolean lockPlainKey(byte[] plainKey, boolean usePrevStoreSize)
{
- return lockDigestedKey(getDigestedRoutingKey(plainKey),
usePrevStoreSize);
+ return lockDigestedKey(cipherManager.getDigestedKey(plainKey),
usePrevStoreSize);
}
private void unlockPlainKey(byte[] plainKey, boolean usePrevStoreSize) {
- unlockDigestedKey(getDigestedRoutingKey(plainKey),
usePrevStoreSize);
+ unlockDigestedKey(cipherManager.getDigestedKey(plainKey),
usePrevStoreSize);
}
/**
@@ -1439,50 +1356,9 @@
}
// ------------- Hashing
- /**
- * <tt>0x10</tt> bytes of salt for better digestion, not too salty.
- */
- private byte[] salt;
-
- private Map<ByteArrayWrapper, byte[]> digestRoutingKeyCache = new
LinkedHashMap<ByteArrayWrapper, byte[]>() {
- @Override
- protected boolean removeEldestEntry(Map.Entry<ByteArrayWrapper,
byte[]> eldest) {
- return size() > 128;
- }
- };
+ private CipherManager cipherManager;
- /**
- * Get hashed routing key
- *
- * @param routingKey
- * @return
- */
- private byte[] getDigestedRoutingKey(byte[] routingKey) {
- ByteArrayWrapper key = new ByteArrayWrapper(routingKey);
- synchronized (digestRoutingKeyCache) {
- byte[] dk = digestRoutingKeyCache.get(key);
- if (dk != null)
- return dk;
- }
-
- MessageDigest digest = SHA256.getMessageDigest();
- try {
- digest.update(routingKey);
- digest.update(salt);
- byte[] hashedRoutingKey = digest.digest();
- assert hashedRoutingKey.length == 0x20;
-
- synchronized (digestRoutingKeyCache) {
- digestRoutingKeyCache.put(key,
hashedRoutingKey);
- }
-
- return hashedRoutingKey;
- } finally {
- SHA256.returnMessageDigest(digest);
- }
- }
-
/**
* Get offset in the hash table, given a plain routing key.
*
@@ -1491,7 +1367,7 @@
* @return
*/
private long[] getOffsetFromPlainKey(byte[] plainKey, long storeSize) {
- return
getOffsetFromDigestedKey(getDigestedRoutingKey(plainKey), storeSize);
+ return
getOffsetFromDigestedKey(cipherManager.getDigestedKey(plainKey), storeSize);
}
/**