Author: toad
Date: 2007-01-09 04:38:13 +0000 (Tue, 09 Jan 2007)
New Revision: 11586
Modified:
trunk/freenet/src/freenet/crypt/KeyAgreementSchemeContext.java
trunk/freenet/src/freenet/crypt/ciphers/Rijndael.java
trunk/freenet/src/freenet/crypt/ciphers/Rijndael_Algorithm.java
trunk/freenet/src/freenet/keys/CHKBlock.java
trunk/freenet/src/freenet/keys/ClientCHK.java
trunk/freenet/src/freenet/keys/ClientCHKBlock.java
trunk/freenet/src/freenet/keys/ClientKSK.java
trunk/freenet/src/freenet/keys/ClientSSK.java
trunk/freenet/src/freenet/keys/ClientSSKBlock.java
trunk/freenet/src/freenet/keys/InsertableClientSSK.java
trunk/freenet/src/freenet/keys/InsertableUSK.java
trunk/freenet/src/freenet/keys/Key.java
trunk/freenet/src/freenet/keys/NodeCHK.java
trunk/freenet/src/freenet/keys/NodeSSK.java
trunk/freenet/src/freenet/keys/USK.java
trunk/freenet/src/freenet/node/PeerNode.java
trunk/freenet/src/freenet/node/Version.java
trunk/freenet/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
Log:
1010: MANDATORY SECURITY FIX
Fix a serious crypto bug. Mandatory immediately (sorry folks).
All old KSKs are no longer retrievable, and you will need to generate a new SSK
for your inserts to be properly encrypted.
Otherwise is backwards compatible with prior builds (in terms of content), so
no content reset.
Modified: trunk/freenet/src/freenet/crypt/KeyAgreementSchemeContext.java
===================================================================
--- trunk/freenet/src/freenet/crypt/KeyAgreementSchemeContext.java
2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/crypt/KeyAgreementSchemeContext.java
2007-01-09 04:38:13 UTC (rev 11586)
@@ -27,7 +27,7 @@
if(cipher != null) return cipher;
getKey();
try {
- cipher = new Rijndael(256, 256);
+ cipher = new Rijndael(256, 256, false);
} catch (UnsupportedCipherException e1) {
throw new Error(e1);
}
Modified: trunk/freenet/src/freenet/crypt/ciphers/Rijndael.java
===================================================================
--- trunk/freenet/src/freenet/crypt/ciphers/Rijndael.java 2007-01-09
02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/crypt/ciphers/Rijndael.java 2007-01-09
04:38:13 UTC (rev 11586)
@@ -19,7 +19,10 @@
*/
public class Rijndael implements BlockCipher {
private Object sessionKey;
- private int keysize, blocksize;
+ private final int keysize, blocksize;
+ // FIXME remove. This is used to simulate an old security threatening bug
in order
+ // to have backwards compatibility with old keys.
+ private final int cryptBlockSize;
// for Util.getCipherByName.. and yes, screw you too, java
public Rijndael(Integer keysize) throws UnsupportedCipherException {
@@ -27,10 +30,19 @@
}
public Rijndael(int keysize) throws UnsupportedCipherException {
- this(keysize, 128);
+ this(keysize, 128, false);
}
- public Rijndael(int keysize, int blocksize) throws
UnsupportedCipherException {
+ /**
+ * Create a Rijndael instance.
+ * @param keysize The key size.
+ * @param blocksize The block size.
+ * @param fakeInsecure If true, only encrypt the first 128 bits of any
block. This
+ * is insecure! It is used for backwards compatibility with old data
encrypted with
+ * the old code.
+ * @throws UnsupportedCipherException
+ */
+ public Rijndael(int keysize, int blocksize, boolean fakeInsecure) throws
UnsupportedCipherException {
if (! ((keysize == 128) ||
(keysize == 192) ||
(keysize == 256)))
@@ -41,11 +53,18 @@
throw new UnsupportedCipherException("Invalid blocksize");
this.keysize=keysize;
this.blocksize=blocksize;
+ // FIXME This is deliberate insecurity! It is used for backwards
compatibility *ONLY*!
+ // FIXME IT MUST BE REMOVED SOON!
+ if(fakeInsecure)
+ this.cryptBlockSize = 128;
+ else
+ this.cryptBlockSize = blocksize;
}
public Rijndael() {
this.keysize = 128;
this.blocksize = 128;
+ this.cryptBlockSize = 128;
}
public final int getBlockSize() {
@@ -60,7 +79,7 @@
try {
byte[] nkey=new byte[keysize>>3];
System.arraycopy(key, 0, nkey, 0, nkey.length);
- sessionKey=Rijndael_Algorithm.makeKey(nkey);
+ sessionKey=Rijndael_Algorithm.makeKey(nkey, cryptBlockSize/8);
} catch (InvalidKeyException e) {
e.printStackTrace();
Logger.error(this,"Invalid key");
@@ -70,13 +89,13 @@
public synchronized final void encipher(byte[] block, byte[] result) {
if(block.length != blocksize/8)
throw new IllegalArgumentException();
- Rijndael_Algorithm.blockEncrypt(block, result, 0, sessionKey);
+ Rijndael_Algorithm.blockEncrypt(block, result, 0, sessionKey,
cryptBlockSize/8);
}
public synchronized final void decipher(byte[] block, byte[] result) {
if(block.length != blocksize/8)
throw new IllegalArgumentException();
- Rijndael_Algorithm.blockDecrypt(block, result, 0, sessionKey);
+ Rijndael_Algorithm.blockDecrypt(block, result, 0, sessionKey,
cryptBlockSize/8);
}
public static void main(String[] args) throws UnsupportedCipherException {
Modified: trunk/freenet/src/freenet/crypt/ciphers/Rijndael_Algorithm.java
===================================================================
--- trunk/freenet/src/freenet/crypt/ciphers/Rijndael_Algorithm.java
2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/crypt/ciphers/Rijndael_Algorithm.java
2007-01-09 04:38:13 UTC (rev 11586)
@@ -300,7 +300,7 @@
* @param k The 128/192/256-bit user-key to use.
* @exception InvalidKeyException If the key is invalid.
*/
- public static final Object makeKey (byte[] k) throws InvalidKeyException {
+ private static final Object makeKey (byte[] k) throws InvalidKeyException {
return makeKey(k, BLOCK_SIZE);
}
@@ -313,7 +313,7 @@
* @param inOffset Index of in from which to start considering data.
* @param sessionKey The session key to use for encryption.
*/
- public static final void
+ private static final void
blockEncrypt (byte[] in, byte[] result, int inOffset, Object sessionKey) {
if (RDEBUG) trace(IN, "blockEncrypt("+in+", "+inOffset+", "+sessionKey+ ')');
int[][] Ke = (int[][]) ((Object[]) sessionKey)[0]; // extract
encryption round keys
@@ -402,7 +402,7 @@
* @param inOffset Index of in from which to start considering data.
* @param sessionKey The session key to use for decryption.
*/
- public static final void
+ private static final void
blockDecrypt (byte[] in, byte[] result, int inOffset, Object sessionKey) {
if (RDEBUG) trace(IN, "blockDecrypt("+in+", "+inOffset+", "+sessionKey+ ')');
int[][] Kd = (int[][]) ((Object[]) sessionKey)[1]; // extract
decryption round keys
Modified: trunk/freenet/src/freenet/keys/CHKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/CHKBlock.java 2007-01-09 02:25:01 UTC
(rev 11585)
+++ trunk/freenet/src/freenet/keys/CHKBlock.java 2007-01-09 04:38:13 UTC
(rev 11586)
@@ -42,12 +42,16 @@
public byte[] getData() {
return data;
}
-
+
public CHKBlock(byte[] data2, byte[] header2, NodeCHK key) throws
CHKVerifyException {
- this(data2, header2, key, true);
+ this(data2, header2, key, key.cryptoAlgorithm);
}
+
+ public CHKBlock(byte[] data2, byte[] header2, NodeCHK key, byte
cryptoAlgorithm) throws CHKVerifyException {
+ this(data2, header2, key, true, cryptoAlgorithm);
+ }
- public CHKBlock(byte[] data2, byte[] header2, NodeCHK key, boolean verify)
throws CHKVerifyException {
+ public CHKBlock(byte[] data2, byte[] header2, NodeCHK key, boolean verify,
byte cryptoAlgorithm) throws CHKVerifyException {
data = data2;
headers = header2;
if(headers.length != TOTAL_HEADERS_LENGTH)
@@ -69,7 +73,7 @@
md.update(data);
byte[] hash = md.digest();
if(key == null) {
- chk = new NodeCHK(hash);
+ chk = new NodeCHK(hash, cryptoAlgorithm);
} else {
chk = key;
byte[] check = chk.routingKey;
Modified: trunk/freenet/src/freenet/keys/ClientCHK.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientCHK.java 2007-01-09 02:25:01 UTC
(rev 11585)
+++ trunk/freenet/src/freenet/keys/ClientCHK.java 2007-01-09 04:38:13 UTC
(rev 11586)
@@ -25,7 +25,7 @@
/** Is the data a control document? */
final boolean controlDocument;
/** Encryption algorithm */
- final short cryptoAlgorithm;
+ final byte cryptoAlgorithm;
/** Compression algorithm, negative means uncompressed */
final short compressionAlgorithm;
@@ -49,7 +49,7 @@
* values.
*/
public ClientCHK(byte[] routingKey, byte[] encKey,
- boolean isControlDocument, short algo, short compressionAlgorithm)
{
+ boolean isControlDocument, byte algo, short compressionAlgorithm) {
this.routingKey = routingKey;
this.cryptoKey = encKey;
this.controlDocument = isControlDocument;
@@ -68,8 +68,10 @@
byte[] extra = uri.getExtra();
if((extra == null) || (extra.length < 5))
throw new MalformedURLException();
- cryptoAlgorithm = (short)(((extra[0] & 0xff) << 8) + (extra[1] &
0xff));
- if(cryptoAlgorithm != Key.ALGO_AES_PCFB_256_SHA256)
+ // byte 0 is reserved, for now
+ cryptoAlgorithm = extra[1];
+ if((!(cryptoAlgorithm == Key.ALGO_AES_PCFB_256_SHA256 ||
+ cryptoAlgorithm ==
Key.ALGO_INSECURE_AES_PCFB_256_SHA256)))
throw new MalformedURLException("Invalid crypto
algorithm");
controlDocument = (extra[2] & 0x02) != 0;
compressionAlgorithm = (short)(((extra[3] & 0xff) << 8) + (extra[4] &
0xff));
@@ -83,8 +85,10 @@
private ClientCHK(DataInputStream dis) throws IOException {
byte[] extra = new byte[EXTRA_LENGTH];
dis.readFully(extra);
- cryptoAlgorithm = (short)(((extra[0] & 0xff) << 8) + (extra[1] &
0xff));
- if(cryptoAlgorithm != Key.ALGO_AES_PCFB_256_SHA256)
+ // byte 0 is reserved, for now
+ cryptoAlgorithm = extra[1];
+ if((!(cryptoAlgorithm == Key.ALGO_AES_PCFB_256_SHA256 ||
+ cryptoAlgorithm ==
Key.ALGO_INSECURE_AES_PCFB_256_SHA256)))
throw new MalformedURLException("Invalid crypto
algorithm");
compressionAlgorithm = (short)(((extra[3] & 0xff) << 8) + (extra[4] &
0xff));
controlDocument = (extra[2] & 0x02) != 0;
@@ -126,7 +130,7 @@
public NodeCHK getNodeCHK() {
if(nodeKey == null)
- nodeKey = new NodeCHK(routingKey);
+ nodeKey = new NodeCHK(routingKey, cryptoAlgorithm);
return nodeKey;
}
Modified: trunk/freenet/src/freenet/keys/ClientCHKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientCHKBlock.java 2007-01-09 02:25:01 UTC
(rev 11585)
+++ trunk/freenet/src/freenet/keys/ClientCHKBlock.java 2007-01-09 04:38:13 UTC
(rev 11586)
@@ -44,7 +44,7 @@
* @param data The data.
*/
public ClientCHKBlock(byte[] data, byte[] header, ClientCHK key2, boolean
verify) throws CHKVerifyException {
- super(data, header, key2.getNodeCHK(), verify);
+ super(data, header, key2.getNodeCHK(), verify, key2.cryptoAlgorithm);
this.key = key2;
}
@@ -75,11 +75,12 @@
*/
public Bucket decode(BucketFactory bf, int maxLength, boolean
dontCompress) throws CHKDecodeException, IOException {
// Overall hash already verified, so first job is to decrypt.
- if(key.cryptoAlgorithm != Key.ALGO_AES_PCFB_256_SHA256)
+ if((!(key.cryptoAlgorithm == Key.ALGO_AES_PCFB_256_SHA256 ||
+ key.cryptoAlgorithm ==
Key.ALGO_INSECURE_AES_PCFB_256_SHA256)))
throw new UnsupportedOperationException();
BlockCipher cipher;
try {
- cipher = new Rijndael(256, 256);
+ cipher = new Rijndael(256, 256, key.cryptoAlgorithm ==
Key.ALGO_INSECURE_AES_PCFB_256_SHA256);
} catch (UnsupportedCipherException e) {
// FIXME - log this properly
throw new Error(e);
@@ -178,7 +179,7 @@
// Now encrypt the header, then the data, using the same PCFB instance
BlockCipher cipher;
try {
- cipher = new Rijndael(256, 256);
+ cipher = new Rijndael(256, 256, false);
} catch (UnsupportedCipherException e) {
// FIXME - log this properly
throw new Error(e);
Modified: trunk/freenet/src/freenet/keys/ClientKSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientKSK.java 2007-01-09 02:25:01 UTC
(rev 11585)
+++ trunk/freenet/src/freenet/keys/ClientKSK.java 2007-01-09 04:38:13 UTC
(rev 11586)
@@ -19,7 +19,7 @@
final String keyword;
private ClientKSK(String keyword, byte[] pubKeyHash, DSAPublicKey
pubKey, DSAPrivateKey privKey, byte[] keywordHash) throws MalformedURLException
{
- super(keyword, pubKeyHash, pubKey, privKey, keywordHash);
+ super(keyword, pubKeyHash, pubKey, privKey, keywordHash,
Key.ALGO_AES_PCFB_256_SHA256);
this.keyword = keyword;
}
Modified: trunk/freenet/src/freenet/keys/ClientSSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientSSK.java 2007-01-09 02:25:01 UTC
(rev 11585)
+++ trunk/freenet/src/freenet/keys/ClientSSK.java 2007-01-09 04:38:13 UTC
(rev 11586)
@@ -16,6 +16,8 @@
public class ClientSSK extends ClientKey {
+ /** Crypto type */
+ public final byte cryptoAlgorithm;
/** Document name */
public final String docName;
/** Public key */
@@ -34,6 +36,12 @@
this.docName = docName;
this.pubKey = pubKey;
this.pubKeyHash = pubKeyHash;
+ if(extras.length < 5)
+ throw new MalformedURLException("Extra bytes too short:
"+extras.length+" bytes");
+ this.cryptoAlgorithm = extras[2];
+ if(!(cryptoAlgorithm == Key.ALGO_AES_PCFB_256_SHA256 ||
+ cryptoAlgorithm ==
Key.ALGO_INSECURE_AES_PCFB_256_SHA256))
+ throw new MalformedURLException("Unknown encryption
algorithm "+cryptoAlgorithm);
if(!Arrays.equals(extras, getExtraBytes()))
throw new MalformedURLException("Wrong extra bytes");
if(pubKeyHash.length != NodeSSK.PUBKEY_HASH_SIZE)
@@ -56,7 +64,7 @@
}
byte[] buf = md.digest();
try {
- Rijndael aes = new Rijndael(256,256);
+ Rijndael aes = new Rijndael(256,256,cryptoAlgorithm ==
Key.ALGO_INSECURE_AES_PCFB_256_SHA256);
aes.initialize(cryptoKey);
aes.encipher(buf, buf);
ehDocname = buf;
@@ -80,15 +88,17 @@
public FreenetURI getURI() {
return new FreenetURI("SSK", docName, pubKeyHash, cryptoKey,
getExtraBytes());
}
+
+ protected final byte[] getExtraBytes() {
+ return getExtraBytes(cryptoAlgorithm);
+ }
- protected static final byte[] getExtraBytes() {
+ protected static byte[] getExtraBytes(byte cryptoAlgorithm) {
// 5 bytes.
byte[] extra = new byte[5];
- short cryptoAlgorithm = Key.ALGO_AES_PCFB_256_SHA256;
-
extra[0] = NodeSSK.SSK_VERSION;
- extra[1] = (byte) (cryptoAlgorithm >> 8);
+ extra[1] = 0; // 0 = fetch (public) URI; 1 = insert (private)
URI
extra[2] = (byte) cryptoAlgorithm;
extra[3] = (byte) (KeyBlock.HASH_SHA256 >> 8);
extra[4] = (byte) KeyBlock.HASH_SHA256;
@@ -97,7 +107,7 @@
public Key getNodeKey() {
try {
- return new NodeSSK(pubKeyHash, ehDocname, pubKey);
+ return new NodeSSK(pubKeyHash, ehDocname, pubKey,
cryptoAlgorithm);
} catch (SSKVerifyException e) {
IllegalStateException x = new
IllegalStateException("Have already verified and yet it fails!: "+e);
Logger.error(this, "Have already verified and yet it
fails!: "+e);
Modified: trunk/freenet/src/freenet/keys/ClientSSKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientSSKBlock.java 2007-01-09 02:25:01 UTC
(rev 11585)
+++ trunk/freenet/src/freenet/keys/ClientSSKBlock.java 2007-01-09 04:38:13 UTC
(rev 11586)
@@ -8,6 +8,7 @@
import freenet.crypt.PCFBMode;
import freenet.crypt.UnsupportedCipherException;
import freenet.crypt.ciphers.Rijndael;
+import freenet.support.Logger;
import freenet.support.api.Bucket;
import freenet.support.api.BucketFactory;
import freenet.support.io.BucketTools;
@@ -47,7 +48,8 @@
System.arraycopy(headers, headersOffset, decryptedHeaders, 0,
ENCRYPTED_HEADERS_LENGTH);
Rijndael aes;
try {
- aes = new Rijndael(256,256);
+ Logger.minor(this,
"cryptoAlgorithm="+key.cryptoAlgorithm+" for "+getClientKey().getURI());
+ aes = new
Rijndael(256,256,key.cryptoAlgorithm==Key.ALGO_INSECURE_AES_PCFB_256_SHA256);
} catch (UnsupportedCipherException e) {
throw new Error(e);
}
Modified: trunk/freenet/src/freenet/keys/InsertableClientSSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/InsertableClientSSK.java 2007-01-09
02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/keys/InsertableClientSSK.java 2007-01-09
04:38:13 UTC (rev 11586)
@@ -29,8 +29,8 @@
public final DSAPrivateKey privKey;
- public InsertableClientSSK(String docName, byte[] pubKeyHash,
DSAPublicKey pubKey, DSAPrivateKey privKey, byte[] cryptoKey) throws
MalformedURLException {
- super(docName, pubKeyHash, getExtraBytes(), pubKey, cryptoKey);
+ public InsertableClientSSK(String docName, byte[] pubKeyHash,
DSAPublicKey pubKey, DSAPrivateKey privKey, byte[] cryptoKey, byte
cryptoAlgorithm) throws MalformedURLException {
+ super(docName, pubKeyHash, getExtraBytes(cryptoAlgorithm),
pubKey, cryptoKey);
if(pubKey == null) throw new NullPointerException();
this.privKey = privKey;
}
@@ -38,18 +38,38 @@
public static InsertableClientSSK create(FreenetURI uri) throws
MalformedURLException {
if(uri.getKeyType().equalsIgnoreCase("KSK"))
return ClientKSK.create(uri);
- if(!uri.getKeyType().equalsIgnoreCase("SSK"))
- throw new MalformedURLException();
+
+ byte keyType;
+
+ byte[] extra = uri.getExtra();
+ if(uri.getKeyType().equals("SSK")) {
+ // FIXME remove once everyone is using PSKs
+ if(extra == null) {
+ keyType = Key.ALGO_INSECURE_AES_PCFB_256_SHA256;
+ } else {
+ // Formatted exactly as ,extra on fetching
+ if(extra.length < 5)
+ throw new MalformedURLException("SSK
private key ,extra too short");
+ if(extra[1] != 1) {
+ throw new MalformedURLException("SSK
not a private key");
+ }
+ keyType = extra[2];
+ if(!(keyType == Key.ALGO_AES_PCFB_256_SHA256 ||
+ keyType ==
Key.ALGO_INSECURE_AES_PCFB_256_SHA256))
+ throw new
MalformedURLException("Unrecognized crypto type in SSK private key");
+ }
+ } else {
+ throw new MalformedURLException("Not a valid SSK insert
URI type: "+uri.getKeyType());
+ }
+
if((uri.getDocName() == null) || (uri.getDocName().length() ==
0))
throw new MalformedURLException("SSK URIs must have a
document name (to avoid ambiguity)");
- if(uri.getExtra() != null)
- throw new MalformedURLException("Insertable SSK URIs
must NOT have ,extra - inserting from a pubkey rather than the privkey
perhaps?");
DSAGroup g = Global.DSAgroupBigA;
DSAPrivateKey privKey = new DSAPrivateKey(new
NativeBigInteger(1, uri.getKeyVal()));
DSAPublicKey pubKey = new DSAPublicKey(g, privKey);
MessageDigest md = SHA256.getMessageDigest();
md.update(pubKey.asBytes());
- return new InsertableClientSSK(uri.getDocName(), md.digest(),
pubKey, privKey, uri.getCryptoKey());
+ return new InsertableClientSSK(uri.getDocName(), md.digest(),
pubKey, privKey, uri.getCryptoKey(), keyType);
}
public ClientSSKBlock encode(Bucket sourceData, boolean asMetadata,
boolean dontCompress, short alreadyCompressedCodec, long sourceLength,
RandomSource r) throws SSKEncodeException, IOException {
@@ -89,7 +109,7 @@
Rijndael aes;
try {
- aes = new Rijndael(256, 256);
+ aes = new Rijndael(256, 256, cryptoAlgorithm ==
Key.ALGO_INSECURE_AES_PCFB_256_SHA256);
} catch (UnsupportedCipherException e) {
throw new Error("256/256 Rijndael not supported!");
}
@@ -188,14 +208,24 @@
DSAPublicKey pubKey = new DSAPublicKey(g, privKey);
MessageDigest md = SHA256.getMessageDigest();
try {
- return new InsertableClientSSK(docName,
md.digest(pubKey.asBytes()), pubKey, privKey, ckey);
+ return new InsertableClientSSK(docName,
md.digest(pubKey.asBytes()), pubKey, privKey, ckey,
Key.ALGO_AES_PCFB_256_SHA256);
} catch (MalformedURLException e) {
throw new Error(e);
}
}
public FreenetURI getInsertURI() {
- return new FreenetURI("SSK", docName,
privKey.getX().toByteArray(), cryptoKey, null);
+ return new FreenetURI("SSK", docName,
privKey.getX().toByteArray(), cryptoKey, getInsertExtraBytes());
}
+
+ private byte[] getInsertExtraBytes() {
+ byte[] extra = getExtraBytes();
+ extra[1] = 1; // insert
+ return extra;
+ }
+
+ public DSAGroup getCryptoGroup() {
+ return Global.DSAgroupBigA;
+ }
}
Modified: trunk/freenet/src/freenet/keys/InsertableUSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/InsertableUSK.java 2007-01-09 02:25:01 UTC
(rev 11585)
+++ trunk/freenet/src/freenet/keys/InsertableUSK.java 2007-01-09 04:38:13 UTC
(rev 11586)
@@ -32,20 +32,14 @@
public static InsertableUSK createInsertable(FreenetURI uri) throws
MalformedURLException {
if(!uri.getKeyType().equalsIgnoreCase("USK"))
throw new MalformedURLException();
- if((uri.getDocName() == null) || (uri.getDocName().length() ==
0))
- throw new MalformedURLException("USK URIs must have a
document name (to avoid ambiguity)");
- if(uri.getExtra() != null)
- throw new MalformedURLException("Insertable SSK URIs
must NOT have ,extra - inserting from a pubkey rather than the privkey
perhaps?");
- DSAGroup g = Global.DSAgroupBigA;
- DSAPrivateKey privKey = new DSAPrivateKey(new
NativeBigInteger(1, uri.getKeyVal()));
- DSAPublicKey pubKey = new DSAPublicKey(g, privKey);
- MessageDigest md = SHA256.getMessageDigest();
- md.update(pubKey.asBytes());
- return new InsertableUSK(uri.getDocName(), md.digest(),
uri.getCryptoKey(), privKey, g, uri.getSuggestedEdition());
+ uri = uri.setKeyType("SSK");
+ InsertableClientSSK ssk =
+ InsertableClientSSK.create(uri);
+ return new InsertableUSK(ssk.docName, ssk.pubKeyHash,
ssk.cryptoKey, ssk.privKey, ssk.getCryptoGroup(), uri.getSuggestedEdition(),
ssk.cryptoAlgorithm);
}
- InsertableUSK(String docName, byte[] pubKeyHash, byte[] cryptoKey,
DSAPrivateKey key, DSAGroup group, long suggestedEdition) throws
MalformedURLException {
- super(pubKeyHash, cryptoKey, docName, suggestedEdition);
+ InsertableUSK(String docName, byte[] pubKeyHash, byte[] cryptoKey,
DSAPrivateKey key, DSAGroup group, long suggestedEdition, byte cryptoAlgorithm)
throws MalformedURLException {
+ super(pubKeyHash, cryptoKey, docName, suggestedEdition,
cryptoAlgorithm);
if(cryptoKey.length != ClientSSK.CRYPTO_KEY_LENGTH)
throw new MalformedURLException("Decryption key wrong
length: "+cryptoKey.length+" should be "+ClientSSK.CRYPTO_KEY_LENGTH);
this.privKey = key;
@@ -57,13 +51,13 @@
}
public USK getUSK() {
- return new USK(pubKeyHash, cryptoKey, siteName,
suggestedEdition);
+ return new USK(pubKeyHash, cryptoKey, siteName,
suggestedEdition, cryptoAlgorithm);
}
public InsertableClientSSK getInsertableSSK(long ver) {
try {
return new InsertableClientSSK(siteName + SEPARATOR +
ver, pubKeyHash,
- new DSAPublicKey(group, privKey),
privKey, cryptoKey);
+ new DSAPublicKey(group, privKey),
privKey, cryptoKey, cryptoAlgorithm);
} catch (MalformedURLException e) {
Logger.error(this, "Caught "+e+" should not be possible
in USK.getSSK", e);
throw new Error(e);
@@ -73,11 +67,10 @@
public InsertableUSK privCopy(long edition) {
if(edition == suggestedEdition) return this;
try {
- return new InsertableUSK(siteName, pubKeyHash,
cryptoKey, privKey, group, edition);
+ return new InsertableUSK(siteName, pubKeyHash,
cryptoKey, privKey, group, edition, cryptoAlgorithm);
} catch (MalformedURLException e) {
throw new Error(e);
}
}
-
}
Modified: trunk/freenet/src/freenet/keys/Key.java
===================================================================
--- trunk/freenet/src/freenet/keys/Key.java 2007-01-09 02:25:01 UTC (rev
11585)
+++ trunk/freenet/src/freenet/keys/Key.java 2007-01-09 04:38:13 UTC (rev
11586)
@@ -34,7 +34,10 @@
final byte[] routingKey;
/** Code for 256-bit AES with PCFB and SHA-256 */
- static final short ALGO_AES_PCFB_256_SHA256 = 1;
+ static final byte ALGO_AES_PCFB_256_SHA256 = 2;
+ /** Code for old, insecure (only encrypts first 128 bits of block) 256-bit
AES with PCFB and SHA-256.
+ * FIXME: REMOVE!! */
+ static final byte ALGO_INSECURE_AES_PCFB_256_SHA256 = 1;
protected Key(byte[] routingKey) {
this.routingKey = routingKey;
@@ -55,11 +58,12 @@
* @return a Key, or throw an exception, or return null if the key is not
parsable.
*/
public static final Key read(DataInput raf) throws IOException {
- short type = raf.readShort();
- if(type == NodeCHK.TYPE) {
- return NodeCHK.readCHK(raf);
- } else if(type == NodeSSK.TYPE)
- return NodeSSK.readSSK(raf);
+ byte type = raf.readByte();
+ byte subtype = raf.readByte();
+ if(type == NodeCHK.BASE_TYPE) {
+ return NodeCHK.readCHK(raf, subtype);
+ } else if(type == NodeSSK.BASE_TYPE)
+ return NodeSSK.readSSK(raf, subtype);
throw new IOException("Unrecognized format: "+type);
}
Modified: trunk/freenet/src/freenet/keys/NodeCHK.java
===================================================================
--- trunk/freenet/src/freenet/keys/NodeCHK.java 2007-01-09 02:25:01 UTC (rev
11585)
+++ trunk/freenet/src/freenet/keys/NodeCHK.java 2007-01-09 04:38:13 UTC (rev
11586)
@@ -21,19 +21,22 @@
/** 32 bytes for hash, 2 bytes for type */
public static final short KEY_SIZE_ON_DISK = 34;
- public NodeCHK(byte[] routingKey2) {
+ public NodeCHK(byte[] routingKey2, byte cryptoAlgorithm) {
super(routingKey2);
if(routingKey2.length != KEY_LENGTH)
throw new IllegalArgumentException("Wrong length:
"+routingKey2.length+" should be "+KEY_LENGTH);
+ this.cryptoAlgorithm = cryptoAlgorithm;
}
static final int KEY_LENGTH = 32;
- // 01 = CHK, 01 = first version of CHK
- public static final short TYPE = 0x0101;
+ /** Crypto algorithm */
+ final byte cryptoAlgorithm;
/** The size of the data */
public static final int BLOCK_SIZE = 32768;
+ public static final byte BASE_TYPE = 1;
+
public final void writeToDataOutputStream(DataOutputStream stream) throws
IOException {
write(stream);
}
@@ -43,14 +46,14 @@
}
public final void write(DataOutput _index) throws IOException {
- _index.writeShort(TYPE);
+ _index.writeShort(getType());
_index.write(routingKey);
}
- public static Key readCHK(DataInput raf) throws IOException {
+ public static Key readCHK(DataInput raf, byte algo) throws IOException {
byte[] buf = new byte[KEY_LENGTH];
raf.readFully(buf);
- return new NodeCHK(buf);
+ return new NodeCHK(buf, algo);
}
public boolean equals(Object key) {
@@ -66,7 +69,7 @@
}
public short getType() {
- return TYPE;
+ return (short) (0x100 + (cryptoAlgorithm & 0xFF));
}
public byte[] getRoutingKey(){
Modified: trunk/freenet/src/freenet/keys/NodeSSK.java
===================================================================
--- trunk/freenet/src/freenet/keys/NodeSSK.java 2007-01-09 02:25:01 UTC (rev
11585)
+++ trunk/freenet/src/freenet/keys/NodeSSK.java 2007-01-09 04:38:13 UTC (rev
11586)
@@ -27,6 +27,8 @@
*/
public class NodeSSK extends Key {
+ /** Crypto algorithm */
+ final byte cryptoAlgorithm;
/** Public key hash */
final byte[] pubKeyHash;
/** E(H(docname)) (E = encrypt using decrypt key, which only clients
know) */
@@ -39,15 +41,17 @@
static final int PUBKEY_HASH_SIZE = 32;
static final int E_H_DOCNAME_SIZE = 32;
+ static final byte BASE_TYPE = 2;
public String toString() {
return
super.toString()+":pkh="+HexUtil.bytesToHex(pubKeyHash)+":ehd="+HexUtil.bytesToHex(encryptedHashedDocname);
}
- public NodeSSK(byte[] pkHash, byte[] ehDocname, DSAPublicKey pubKey)
throws SSKVerifyException {
+ public NodeSSK(byte[] pkHash, byte[] ehDocname, DSAPublicKey pubKey,
byte cryptoAlgorithm) throws SSKVerifyException {
super(makeRoutingKey(pkHash, ehDocname));
this.encryptedHashedDocname = ehDocname;
this.pubKeyHash = pkHash;
+ this.cryptoAlgorithm = cryptoAlgorithm;
this.pubKey = pubKey;
if(pubKey != null) {
MessageDigest md256 = SHA256.getMessageDigest();
@@ -70,22 +74,19 @@
return md256.digest();
}
- // 01 = SSK, 01 = first version of SSK
- public static short TYPE = 0x0201;
-
public void write(DataOutput _index) throws IOException {
- _index.writeShort(TYPE);
+ _index.writeShort(getType());
_index.write(encryptedHashedDocname);
_index.write(pubKeyHash);
}
- public static Key readSSK(DataInput raf) throws IOException {
+ public static Key readSSK(DataInput raf, byte cryptoAlgorithm) throws
IOException {
byte[] buf = new byte[E_H_DOCNAME_SIZE];
raf.readFully(buf);
byte[] buf2 = new byte[PUBKEY_HASH_SIZE];
raf.readFully(buf2);
try {
- return new NodeSSK(buf2, buf, null);
+ return new NodeSSK(buf2, buf, null, cryptoAlgorithm);
} catch (SSKVerifyException e) {
IllegalStateException impossible =
new IllegalStateException("Impossible: "+e);
@@ -95,7 +96,7 @@
}
public short getType() {
- return TYPE;
+ return (short) (0x0200 + (cryptoAlgorithm & 0xff));
}
public void writeToDataOutputStream(DataOutputStream stream) throws
IOException {
Modified: trunk/freenet/src/freenet/keys/USK.java
===================================================================
--- trunk/freenet/src/freenet/keys/USK.java 2007-01-09 02:25:01 UTC (rev
11585)
+++ trunk/freenet/src/freenet/keys/USK.java 2007-01-09 04:38:13 UTC (rev
11586)
@@ -25,6 +25,8 @@
* SSK form, and we don't need to go vice versa.
*/
static protected final String SEPARATOR = "-";
+ /** Encryption type */
+ public final byte cryptoAlgorithm;
/** Public key hash */
public final byte[] pubKeyHash;
/** Encryption key */
@@ -41,8 +43,9 @@
this.cryptoKey = cryptoKey;
this.siteName = siteName;
this.suggestedEdition = suggestedEdition;
- if(!Arrays.equals(extra, ClientSSK.getExtraBytes()))
- throw new MalformedURLException("Invalid extra bytes");
+ // Verify extra bytes, get cryptoAlgorithm
+ ClientSSK tmp = new ClientSSK(siteName, pubKeyHash, extra,
null, cryptoKey);
+ cryptoAlgorithm = tmp.cryptoAlgorithm;
if(pubKeyHash.length != NodeSSK.PUBKEY_HASH_SIZE)
throw new MalformedURLException("Pubkey hash wrong
length: "+pubKeyHash.length+" should be "+NodeSSK.PUBKEY_HASH_SIZE);
if(cryptoKey.length != ClientSSK.CRYPTO_KEY_LENGTH)
@@ -56,11 +59,12 @@
return new USK(uri.getRoutingKey(), uri.getCryptoKey(),
uri.getExtra(), uri.getDocName(), uri.getSuggestedEdition());
}
- protected USK(byte[] pubKeyHash2, byte[] cryptoKey2, String siteName2,
long suggestedEdition2) {
+ protected USK(byte[] pubKeyHash2, byte[] cryptoKey2, String siteName2,
long suggestedEdition2, byte cryptoAlgorithm) {
this.pubKeyHash = pubKeyHash2;
this.cryptoKey = cryptoKey2;
this.siteName = siteName2;
this.suggestedEdition = suggestedEdition2;
+ this.cryptoAlgorithm = cryptoAlgorithm;
hashCode = Fields.hashCode(pubKeyHash) ^
Fields.hashCode(cryptoKey) ^
siteName.hashCode() ^ (int)suggestedEdition ^
(int)(suggestedEdition >> 32);
}
@@ -70,17 +74,18 @@
this.cryptoKey = ssk.cryptoKey;
this.siteName = ssk.docName;
this.suggestedEdition = myARKNumber;
+ this.cryptoAlgorithm = ssk.cryptoAlgorithm;
hashCode = Fields.hashCode(pubKeyHash) ^
Fields.hashCode(cryptoKey) ^
siteName.hashCode() ^ (int)suggestedEdition ^
(int)(suggestedEdition >> 32);
}
public FreenetURI getURI() {
- return new FreenetURI(pubKeyHash, cryptoKey,
ClientSSK.getExtraBytes(), siteName, suggestedEdition);
+ return new FreenetURI(pubKeyHash, cryptoKey,
ClientSSK.getExtraBytes(cryptoAlgorithm), siteName, suggestedEdition);
}
public ClientSSK getSSK(long ver) {
try {
- return new ClientSSK(siteName + SEPARATOR + ver,
pubKeyHash, ClientSSK.getExtraBytes(), null, cryptoKey);
+ return new ClientSSK(siteName + SEPARATOR + ver,
pubKeyHash, ClientSSK.getExtraBytes(cryptoAlgorithm), null, cryptoKey);
} catch (MalformedURLException e) {
Logger.error(this, "Caught "+e+" should not be possible
in USK.getSSK", e);
throw new Error(e);
@@ -93,7 +98,7 @@
public USK copy(long edition) {
if(suggestedEdition == edition) return this;
- return new USK(pubKeyHash, cryptoKey, siteName, edition);
+ return new USK(pubKeyHash, cryptoKey, siteName, edition,
cryptoAlgorithm);
}
public USK clearCopy() {
@@ -122,7 +127,7 @@
}
public FreenetURI getBaseSSK() {
- return new FreenetURI("SSK", siteName, pubKeyHash, cryptoKey,
ClientSSK.getExtraBytes());
+ return new FreenetURI("SSK", siteName, pubKeyHash, cryptoKey,
ClientSSK.getExtraBytes(cryptoAlgorithm));
}
public String toString() {
Modified: trunk/freenet/src/freenet/node/PeerNode.java
===================================================================
--- trunk/freenet/src/freenet/node/PeerNode.java 2007-01-09 02:25:01 UTC
(rev 11585)
+++ trunk/freenet/src/freenet/node/PeerNode.java 2007-01-09 04:38:13 UTC
(rev 11586)
@@ -467,9 +467,9 @@
"\nFor: "+getPeer());
try {
- incomingSetupCipher = new Rijndael(256,256);
+ incomingSetupCipher = new Rijndael(256,256,false);
incomingSetupCipher.initialize(incomingSetupKey);
- outgoingSetupCipher = new Rijndael(256,256);
+ outgoingSetupCipher = new Rijndael(256,256,false);
outgoingSetupCipher.initialize(outgoingSetupKey);
} catch (UnsupportedCipherException e1) {
Logger.error(this, "Caught: "+e1);
Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2007-01-09 02:25:01 UTC (rev
11585)
+++ trunk/freenet/src/freenet/node/Version.java 2007-01-09 04:38:13 UTC (rev
11586)
@@ -24,11 +24,11 @@
public static final String protocolVersion = "1.0";
/** The build number of the current revision */
- private static final int buildNumber = 1009;
+ private static final int buildNumber = 1010;
/** Oldest build of Fred we will talk to */
- private static final int oldLastGoodBuild = 1007;
- private static final int newLastGoodBuild = 1009;
+ private static final int oldLastGoodBuild = 1010;
+ private static final int newLastGoodBuild = 1010;
private static final long transitionTime;
static {
Modified:
trunk/freenet/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
===================================================================
--- trunk/freenet/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
2007-01-09 04:38:13 UTC (rev 11586)
@@ -31,6 +31,8 @@
private final Rijndael aes;
/** The decryption key. May be null. */
private final byte[] key;
+ /** Broken (old) encryption? */
+ private final boolean brokenEncryption;
private long dataLength;
private boolean readOnly;
private int lastOutputStream;
@@ -48,10 +50,11 @@
this.bucket = bucket;
if(bucket.size() != 0) throw new
IllegalArgumentException("Bucket must be empty");
try {
- aes = new Rijndael(256, 256);
+ aes = new Rijndael(256, 256, false);
} catch (UnsupportedCipherException e) {
throw new Error(e);
}
+ brokenEncryption = false;
byte[] tempKey = new byte[32];
origRandom.nextBytes(tempKey);
aes.initialize(tempKey);
@@ -78,14 +81,15 @@
* @param origRandom
* @throws IOException
*/
- public PaddedEphemerallyEncryptedBucket(Bucket bucket, int minSize,
long knownSize, byte[] key, RandomSource origRandom) throws IOException {
+ public PaddedEphemerallyEncryptedBucket(Bucket bucket, int minSize,
long knownSize, byte[] key, RandomSource origRandom, boolean oldCrypto) throws
IOException {
if(bucket.size() < knownSize)
throw new IOException("Bucket "+bucket+" is too small
on disk - knownSize="+knownSize+" but bucket.size="+bucket.size()+" for
"+bucket);
this.dataLength = knownSize;
this.origRandom = origRandom;
this.bucket = bucket;
+ brokenEncryption = oldCrypto;
try {
- aes = new Rijndael(256, 256);
+ aes = new Rijndael(256, 256, oldCrypto);
} catch (UnsupportedCipherException e) {
throw new Error(e);
}
@@ -113,9 +117,10 @@
tmp = fs.get("DecryptKey");
if(tmp == null)
throw new CannotCreateFromFieldSetException("No key");
+ brokenEncryption = fs.get("CryptoType") == null;
key = HexUtil.hexToBytes(tmp);
try {
- aes = new Rijndael(256, 256);
+ aes = new Rijndael(256, 256, brokenEncryption);
} catch (UnsupportedCipherException e) {
throw new Error(e);
}
@@ -362,6 +367,8 @@
return null;
}
fs.put("MinPaddedSize", minPaddedSize);
+ if(!brokenEncryption)
+ fs.put("CryptoType", "aes256");
return fs;
}
Modified: trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
===================================================================
--- trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
2007-01-09 04:38:13 UTC (rev 11586)
@@ -120,19 +120,6 @@
}
/**
- * Restore an encrypted temp bucket from last time.
- * @param filename The filename. Must exist unless len=0.
- * @param key The encryption key for the bucket.
- * @param len The data length. The file must be of at least this length.
- * @return
- * @throws IOException If the file doesn't exist or if it is too short.
- */
- public Bucket registerEncryptedBucket(String filename, byte[] key, long
len) throws IOException {
- Bucket fileBucket = register(filename, len > 0);
- return new DelayedFreeBucket(this, new
PaddedEphemerallyEncryptedBucket(fileBucket, 1024, len, key, rand));
- }
-
- /**
* Free an allocated bucket, but only after the change has been written
to disk.
*/
public void delayedFreeBucket(Bucket b) {
Modified:
trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
===================================================================
--- trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
2007-01-09 02:25:01 UTC (rev 11585)
+++ trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
2007-01-09 04:38:13 UTC (rev 11586)
@@ -47,7 +47,7 @@
FileBucket fb = new FileBucket(fnam, false,
false, false, true);
try {
PaddedEphemerallyEncryptedBucket eb =
- new
PaddedEphemerallyEncryptedBucket(fb, 1024, len, decryptKey, random);
+ new
PaddedEphemerallyEncryptedBucket(fb, 1024, len, decryptKey, random, true);
return eb;
} catch (IOException e) {
throw new
CannotCreateFromFieldSetException("Cannot create from old-format fieldset: "+e,
e);