* Javadocs

* Base64: comment out some unused methods
* Remove huge whitespace in CryptoConstants
* ElGamalAESEngine:
  - Reduce rates
  - Check number of tags earlier
This commit is contained in:
zzz
2010-08-30 17:51:49 +00:00
parent c035ef6eb7
commit 91bcf947df
14 changed files with 193 additions and 48 deletions

View File

@ -26,6 +26,7 @@ import net.i2p.util.Log;
/** /**
* Supports the following: * Supports the following:
*<pre>
* (where protocol is generally HTTP/1.1 but is ignored) * (where protocol is generally HTTP/1.1 but is ignored)
* (where host is one of: * (where host is one of:
* example.i2p * example.i2p
@ -39,16 +40,19 @@ import net.i2p.util.Log;
* CONNECT host protocol * CONNECT host protocol
* CONNECT host:port * CONNECT host:port
* CONNECT host:port protocol (this is the standard) * CONNECT host:port protocol (this is the standard)
*</pre>
* *
* Additional lines after the CONNECT line but before the blank line are ignored and stripped. * Additional lines after the CONNECT line but before the blank line are ignored and stripped.
* The CONNECT line is removed for .i2p accesses * The CONNECT line is removed for .i2p accesses
* but passed along for outproxy accesses. * but passed along for outproxy accesses.
* *
* Ref: * Ref:
*<pre>
* INTERNET-DRAFT Ari Luotonen * INTERNET-DRAFT Ari Luotonen
* Expires: September 26, 1997 Netscape Communications Corporation * Expires: September 26, 1997 Netscape Communications Corporation
* <draft-luotonen-ssl-tunneling-03.txt> March 26, 1997 * <draft-luotonen-ssl-tunneling-03.txt> March 26, 1997
* Tunneling SSL Through a WWW Proxy * Tunneling SSL Through a WWW Proxy
*</pre>
* *
* @author zzz a stripped-down I2PTunnelHTTPClient * @author zzz a stripped-down I2PTunnelHTTPClient
*/ */

View File

@ -127,6 +127,8 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
// this does not increment totalSent // this does not increment totalSent
i2pout.write(initialI2PData); i2pout.write(initialI2PData);
// do NOT flush here, it will block and then onTimeout.run() won't happen on fail. // do NOT flush here, it will block and then onTimeout.run() won't happen on fail.
// But if we don't flush, then we have to wait for the connectDelay timer to fire
// in i2p socket? To be researched and/or fixed.
//i2pout.flush(); //i2pout.flush();
} }
} }

View File

@ -176,7 +176,11 @@ public class I2PSocketManagerFull implements I2PSocketManager {
} }
/** /**
* Create a new connected socket (block until the socket is created) * Create a new connected socket. Blocks until the socket is created,
* unless the connectDelay option (i2p.streaming.connectDelay) is
* set and greater than zero. If so this will return immediately,
* and the client may quickly write initial data to the socket and
* this data will be bundled in the SYN packet.
* *
* @param peer Destination to connect to * @param peer Destination to connect to
* @param options I2P socket options to be used for connecting * @param options I2P socket options to be used for connecting
@ -199,6 +203,7 @@ public class I2PSocketManagerFull implements I2PSocketManager {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO))
_log.info("Connecting to " + peer.calculateHash().toBase64().substring(0,6) _log.info("Connecting to " + peer.calculateHash().toBase64().substring(0,6)
+ " with options: " + opts); + " with options: " + opts);
// the following blocks unless connect delay > 0
Connection con = _connectionManager.connect(peer, opts); Connection con = _connectionManager.connect(peer, opts);
if (con == null) if (con == null)
throw new TooManyStreamsException("Too many streams (max " + _maxStreams + ")"); throw new TooManyStreamsException("Too many streams (max " + _maxStreams + ")");
@ -212,7 +217,11 @@ public class I2PSocketManagerFull implements I2PSocketManager {
} }
/** /**
* Create a new connected socket (block until the socket is created) * Create a new connected socket. Blocks until the socket is created,
* unless the connectDelay option (i2p.streaming.connectDelay) is
* set and greater than zero in the default options. If so this will return immediately,
* and the client may quickly write initial data to the socket and
* this data will be bundled in the SYN packet.
* *
* @param peer Destination to connect to * @param peer Destination to connect to
* *

View File

@ -41,11 +41,13 @@ public class CryptixAESEngine extends AESEngine {
_cache = new CryptixAESKeyCache(); _cache = new CryptixAESKeyCache();
} }
/** @param length must be a multiple of 16 */
@Override @Override
public void encrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int length) { public void encrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int length) {
encrypt(payload, payloadIndex, out, outIndex, sessionKey, iv, 0, length); encrypt(payload, payloadIndex, out, outIndex, sessionKey, iv, 0, length);
} }
/** @param length must be a multiple of 16 */
@Override @Override
public void encrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int ivOffset, int length) { public void encrypt(byte payload[], int payloadIndex, byte out[], int outIndex, SessionKey sessionKey, byte iv[], int ivOffset, int length) {
if ( (payload == null) || (out == null) || (sessionKey == null) || (iv == null) ) if ( (payload == null) || (out == null) || (sessionKey == null) || (iv == null) )
@ -142,7 +144,7 @@ public class CryptixAESEngine extends AESEngine {
CryptixRijndael_Algorithm.blockEncrypt(payload, out, inIndex, outIndex, sessionKey.getPreparedKey(), 16); CryptixRijndael_Algorithm.blockEncrypt(payload, out, inIndex, outIndex, sessionKey.getPreparedKey(), 16);
} }
/** decrypt the data with the session key provided /** decrypt exactly 16 bytes of data with the session key provided
* @param payload encrypted data * @param payload encrypted data
* @param sessionKey private session key * @param sessionKey private session key
*/ */

View File

@ -35,21 +35,21 @@ import net.i2p.util.NativeBigInteger;
/** /**
* Prime for ElGamal from http://tools.ietf.org/html/rfc3526 * Prime for ElGamal from http://tools.ietf.org/html/rfc3526
* Primes for DSA: unknown. * Primes for DSA: Generated by TheCrypto http://article.gmane.org/gmane.comp.security.invisiblenet.iip.devel/343
*/ */
public class CryptoConstants { public class CryptoConstants {
public static final BigInteger dsap = new NativeBigInteger( public static final BigInteger dsap = new NativeBigInteger(
"9c05b2aa960d9b97b8931963c9cc9e8c3026e9b8ed92fad0a69cc886d5bf8015fcadae31" "9c05b2aa960d9b97b8931963c9cc9e8c3026e9b8ed92fad0a69cc886d5bf8015fcadae31"
+ "a0ad18fab3f01b00a358de237655c4964afaa2b337e96ad316b9fb1cc564b5aec5b69a9f" + "a0ad18fab3f01b00a358de237655c4964afaa2b337e96ad316b9fb1cc564b5aec5b69a9f"
+ "f6c3e4548707fef8503d91dd8602e867e6d35d2235c1869ce2479c3b9d5401de04e0727f" + "f6c3e4548707fef8503d91dd8602e867e6d35d2235c1869ce2479c3b9d5401de04e0727f"
+ "b33d6511285d4cf29538d9e3b6051f5b22cc1c93", + "b33d6511285d4cf29538d9e3b6051f5b22cc1c93",
16); 16);
public static final BigInteger dsaq = new NativeBigInteger("a5dfc28fef4ca1e286744cd8eed9d29d684046b7", 16); public static final BigInteger dsaq = new NativeBigInteger("a5dfc28fef4ca1e286744cd8eed9d29d684046b7", 16);
public static final BigInteger dsag = new NativeBigInteger( public static final BigInteger dsag = new NativeBigInteger(
"c1f4d27d40093b429e962d7223824e0bbc47e7c832a39236fc683af84889581075ff9082" "c1f4d27d40093b429e962d7223824e0bbc47e7c832a39236fc683af84889581075ff9082"
+ "ed32353d4374d7301cda1d23c431f4698599dda02451824ff369752593647cc3ddc197de" + "ed32353d4374d7301cda1d23c431f4698599dda02451824ff369752593647cc3ddc197de"
+ "985e43d136cdcfc6bd5409cd2f450821142a5e6f8eb1c3ab5d0484b8129fcf17bce4f7f3" + "985e43d136cdcfc6bd5409cd2f450821142a5e6f8eb1c3ab5d0484b8129fcf17bce4f7f3"
+ "3321c3cb3dbb14a905e7b2b3e93be4708cbcc82", + "3321c3cb3dbb14a905e7b2b3e93be4708cbcc82",
16); 16);
public static final BigInteger elgp = new NativeBigInteger("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" public static final BigInteger elgp = new NativeBigInteger("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"

View File

@ -43,19 +43,19 @@ public class ElGamalAESEngine {
_context.statManager().createFrequencyStat("crypto.elGamalAES.encryptNewSession", _context.statManager().createFrequencyStat("crypto.elGamalAES.encryptNewSession",
"how frequently we encrypt to a new ElGamal/AES+SessionTag session?", "how frequently we encrypt to a new ElGamal/AES+SessionTag session?",
"Encryption", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l}); "Encryption", new long[] { 60*60*1000l});
_context.statManager().createFrequencyStat("crypto.elGamalAES.encryptExistingSession", _context.statManager().createFrequencyStat("crypto.elGamalAES.encryptExistingSession",
"how frequently we encrypt to an existing ElGamal/AES+SessionTag session?", "how frequently we encrypt to an existing ElGamal/AES+SessionTag session?",
"Encryption", new long[] { 60 * 1000l, 60 * 60 * 1000l, 24 * 60 * 60 * 1000l}); "Encryption", new long[] { 60*60*1000l});
_context.statManager().createFrequencyStat("crypto.elGamalAES.decryptNewSession", _context.statManager().createFrequencyStat("crypto.elGamalAES.decryptNewSession",
"how frequently we decrypt with a new ElGamal/AES+SessionTag session?", "how frequently we decrypt with a new ElGamal/AES+SessionTag session?",
"Encryption", new long[] { 60 * 1000l, 60 * 60 * 1000l, 24 * 60 * 60 * 1000l}); "Encryption", new long[] { 60*60*1000l});
_context.statManager().createFrequencyStat("crypto.elGamalAES.decryptExistingSession", _context.statManager().createFrequencyStat("crypto.elGamalAES.decryptExistingSession",
"how frequently we decrypt with an existing ElGamal/AES+SessionTag session?", "how frequently we decrypt with an existing ElGamal/AES+SessionTag session?",
"Encryption", new long[] { 60 * 1000l, 60 * 60 * 1000l, 24 * 60 * 60 * 1000l}); "Encryption", new long[] { 60*60*1000l});
_context.statManager().createFrequencyStat("crypto.elGamalAES.decryptFailed", _context.statManager().createFrequencyStat("crypto.elGamalAES.decryptFailed",
"how frequently we fail to decrypt with ElGamal/AES+SessionTag?", "Encryption", "how frequently we fail to decrypt with ElGamal/AES+SessionTag?",
new long[] { 60 * 60 * 1000l, 24 * 60 * 60 * 1000l}); "Encryption", new long[] { 60*60*1000l});
} }
/** /**
@ -73,6 +73,7 @@ public class ElGamalAESEngine {
* This works according to the * This works according to the
* ElGamal+AES algorithm in the data structure spec. * ElGamal+AES algorithm in the data structure spec.
* *
* @return decrypted data or null on failure
*/ */
public byte[] decrypt(byte data[], PrivateKey targetPrivateKey, SessionKeyManager keyManager) throws DataFormatException { public byte[] decrypt(byte data[], PrivateKey targetPrivateKey, SessionKeyManager keyManager) throws DataFormatException {
if (data == null) { if (data == null) {
@ -148,9 +149,12 @@ public class ElGamalAESEngine {
/** /**
* scenario 1: * scenario 1:
* Begin with 222 bytes, ElG encrypted, containing: * Begin with 222 bytes, ElG encrypted, containing:
* <pre>
* - 32 byte SessionKey * - 32 byte SessionKey
* - 32 byte pre-IV for the AES * - 32 byte pre-IV for the AES
* - 158 bytes of random padding * - 158 bytes of random padding
* </pre>
* After encryption, the ElG section is 514 bytes long.
* Then encrypt with AES using that session key and the first 16 bytes of the SHA256 of the pre-IV, using * Then encrypt with AES using that session key and the first 16 bytes of the SHA256 of the pre-IV, using
* the decryptAESBlock method & structure. * the decryptAESBlock method & structure.
* *
@ -213,6 +217,7 @@ public class ElGamalAESEngine {
* scenario 2: * scenario 2:
* The data begins with 32 byte session tag, which also serves as the preIV. * The data begins with 32 byte session tag, which also serves as the preIV.
* Then decrypt with AES using that session key and the first 16 bytes of the SHA256 of the pre-IV: * Then decrypt with AES using that session key and the first 16 bytes of the SHA256 of the pre-IV:
* <pre>
* - 2 byte integer specifying the # of session tags * - 2 byte integer specifying the # of session tags
* - that many 32 byte session tags * - that many 32 byte session tags
* - 4 byte integer specifying data.length * - 4 byte integer specifying data.length
@ -220,11 +225,13 @@ public class ElGamalAESEngine {
* - 1 byte flag that, if == 1, is followed by a new SessionKey * - 1 byte flag that, if == 1, is followed by a new SessionKey
* - data * - data
* - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0 * - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
* </pre>
* *
* If anything doesn't match up in decryption, it falls back to decryptNewSession * If anything doesn't match up in decryption, it falls back to decryptNewSession
* *
* @param foundTags set which is filled with any sessionTags found during decryption * @param foundTags set which is filled with any sessionTags found during decryption
* @param foundKey session key which may be filled with a new sessionKey found during decryption * @param foundKey session key which may be filled with a new sessionKey found during decryption
* @return decrypted data or null on failure
* *
*/ */
byte[] decryptExistingSession(byte data[], SessionKey key, PrivateKey targetPrivateKey, Set foundTags, byte[] decryptExistingSession(byte data[], SessionKey key, PrivateKey targetPrivateKey, Set foundTags,
@ -264,6 +271,7 @@ public class ElGamalAESEngine {
/** /**
* Decrypt the AES data with the session key and IV. The result should be: * Decrypt the AES data with the session key and IV. The result should be:
* <pre>
* - 2 byte integer specifying the # of session tags * - 2 byte integer specifying the # of session tags
* - that many 32 byte session tags * - that many 32 byte session tags
* - 4 byte integer specifying data.length * - 4 byte integer specifying data.length
@ -271,6 +279,7 @@ public class ElGamalAESEngine {
* - 1 byte flag that, if == 1, is followed by a new SessionKey * - 1 byte flag that, if == 1, is followed by a new SessionKey
* - data * - data
* - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0 * - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
* </pre>
* *
* If anything doesn't match up in decryption, return null. Otherwise, return * If anything doesn't match up in decryption, return null. Otherwise, return
* the decrypted data and update the session as necessary. If the sentTag is not null, * the decrypted data and update the session as necessary. If the sentTag is not null,
@ -278,6 +287,7 @@ public class ElGamalAESEngine {
* *
* @param foundTags set which is filled with any sessionTags found during decryption * @param foundTags set which is filled with any sessionTags found during decryption
* @param foundKey session key which may be filled with a new sessionKey found during decryption * @param foundKey session key which may be filled with a new sessionKey found during decryption
* @return decrypted data or null on failure
*/ */
byte[] decryptAESBlock(byte encrypted[], SessionKey key, byte iv[], byte[] decryptAESBlock(byte encrypted[], SessionKey key, byte iv[],
byte sentTag[], Set foundTags, SessionKey foundKey) throws DataFormatException { byte sentTag[], Set foundTags, SessionKey foundKey) throws DataFormatException {
@ -299,10 +309,10 @@ public class ElGamalAESEngine {
//ByteArrayInputStream bais = new ByteArrayInputStream(decrypted); //ByteArrayInputStream bais = new ByteArrayInputStream(decrypted);
int cur = 0; int cur = 0;
long numTags = DataHelper.fromLong(decrypted, cur, 2); long numTags = DataHelper.fromLong(decrypted, cur, 2);
if ((numTags < 0) || (numTags > 200)) throw new Exception("Invalid number of session tags");
if (numTags > 0) tags = new ArrayList((int)numTags); if (numTags > 0) tags = new ArrayList((int)numTags);
cur += 2; cur += 2;
//_log.debug("# tags: " + numTags); //_log.debug("# tags: " + numTags);
if ((numTags < 0) || (numTags > 200)) throw new Exception("Invalid number of session tags");
if (numTags * SessionTag.BYTE_LENGTH > decrypted.length - 2) { if (numTags * SessionTag.BYTE_LENGTH > decrypted.length - 2) {
throw new Exception("# tags: " + numTags + " is too many for " + (decrypted.length - 2)); throw new Exception("# tags: " + numTags + " is too many for " + (decrypted.length - 2));
} }
@ -363,6 +373,8 @@ public class ElGamalAESEngine {
* @param newKey key to be delivered to the target, with which the tagsForDelivery should be associated, or null * @param newKey key to be delivered to the target, with which the tagsForDelivery should be associated, or null
* @param paddedSize minimum size in bytes of the body after padding it (if less than the * @param paddedSize minimum size in bytes of the body after padding it (if less than the
* body's real size, no bytes are appended but the body is not truncated) * body's real size, no bytes are appended but the body is not truncated)
*
* Unused externally, only called by below (i.e. newKey is always null)
*/ */
public byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery, public byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
SessionTag currentTag, SessionKey newKey, long paddedSize) { SessionTag currentTag, SessionKey newKey, long paddedSize) {
@ -384,6 +396,7 @@ public class ElGamalAESEngine {
/** /**
* Encrypt the data to the target using the given key and deliver the specified tags * Encrypt the data to the target using the given key and deliver the specified tags
* No new session key * No new session key
* This is the one called from GarlicMessageBuilder and is the primary entry point.
*/ */
public byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery, public byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
SessionTag currentTag, long paddedSize) { SessionTag currentTag, long paddedSize) {
@ -394,6 +407,8 @@ public class ElGamalAESEngine {
* Encrypt the data to the target using the given key and deliver the specified tags * Encrypt the data to the target using the given key and deliver the specified tags
* No new session key * No new session key
* No current tag (encrypt as new session) * No current tag (encrypt as new session)
*
* @deprecated unused
*/ */
public byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery, long paddedSize) { public byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery, long paddedSize) {
return encrypt(data, target, key, tagsForDelivery, null, null, paddedSize); return encrypt(data, target, key, tagsForDelivery, null, null, paddedSize);
@ -403,6 +418,8 @@ public class ElGamalAESEngine {
* Encrypt the data to the target using the given key delivering no tags * Encrypt the data to the target using the given key delivering no tags
* No new session key * No new session key
* No current tag (encrypt as new session) * No current tag (encrypt as new session)
*
* @deprecated unused
*/ */
public byte[] encrypt(byte data[], PublicKey target, SessionKey key, long paddedSize) { public byte[] encrypt(byte data[], PublicKey target, SessionKey key, long paddedSize) {
return encrypt(data, target, key, null, null, null, paddedSize); return encrypt(data, target, key, null, null, null, paddedSize);
@ -411,10 +428,14 @@ public class ElGamalAESEngine {
/** /**
* scenario 1: * scenario 1:
* Begin with 222 bytes, ElG encrypted, containing: * Begin with 222 bytes, ElG encrypted, containing:
* <pre>
* - 32 byte SessionKey * - 32 byte SessionKey
* - 32 byte pre-IV for the AES * - 32 byte pre-IV for the AES
* - 158 bytes of random padding * - 158 bytes of random padding
* Then encrypt with AES using that session key and the first 16 bytes of the SHA256 of the pre-IV: * </pre>
* After encryption, the ElG section is 514 bytes long.
* Then encrypt the following with AES using that session key and the first 16 bytes of the SHA256 of the pre-IV:
* <pre>
* - 2 byte integer specifying the # of session tags * - 2 byte integer specifying the # of session tags
* - that many 32 byte session tags * - that many 32 byte session tags
* - 4 byte integer specifying data.length * - 4 byte integer specifying data.length
@ -422,6 +443,7 @@ public class ElGamalAESEngine {
* - 1 byte flag that, if == 1, is followed by a new SessionKey * - 1 byte flag that, if == 1, is followed by a new SessionKey
* - data * - data
* - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0 * - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
* </pre>
* *
*/ */
byte[] encryptNewSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery, byte[] encryptNewSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
@ -440,10 +462,12 @@ public class ElGamalAESEngine {
//_log.debug("SessionKey for encryptNewSession: " + DataHelper.toString(key.getData(), 32)); //_log.debug("SessionKey for encryptNewSession: " + DataHelper.toString(key.getData(), 32));
long before = _context.clock().now(); long before = _context.clock().now();
byte elgEncr[] = _context.elGamalEngine().encrypt(elgSrcData, target); byte elgEncr[] = _context.elGamalEngine().encrypt(elgSrcData, target);
long after = _context.clock().now(); if (_log.shouldLog(Log.INFO)) {
if (_log.shouldLog(Log.INFO)) long after = _context.clock().now();
_log.info("elgEngine.encrypt of the session key took " + (after - before) + "ms"); _log.info("elgEngine.encrypt of the session key took " + (after - before) + "ms");
}
if (elgEncr.length < 514) { if (elgEncr.length < 514) {
// ??? ElGamalEngine.encrypt() always returns 514 bytes
byte elg[] = new byte[514]; byte elg[] = new byte[514];
int diff = elg.length - elgEncr.length; int diff = elg.length - elgEncr.length;
//if (_log.shouldLog(Log.DEBUG)) _log.debug("Difference in size: " + diff); //if (_log.shouldLog(Log.DEBUG)) _log.debug("Difference in size: " + diff);
@ -474,6 +498,7 @@ public class ElGamalAESEngine {
* scenario 2: * scenario 2:
* Begin with 32 byte session tag, which also serves as the preIV. * Begin with 32 byte session tag, which also serves as the preIV.
* Then encrypt with AES using that session key and the first 16 bytes of the SHA256 of the pre-IV: * Then encrypt with AES using that session key and the first 16 bytes of the SHA256 of the pre-IV:
* <pre>
* - 2 byte integer specifying the # of session tags * - 2 byte integer specifying the # of session tags
* - that many 32 byte session tags * - that many 32 byte session tags
* - 4 byte integer specifying data.length * - 4 byte integer specifying data.length
@ -481,6 +506,7 @@ public class ElGamalAESEngine {
* - 1 byte flag that, if == 1, is followed by a new SessionKey * - 1 byte flag that, if == 1, is followed by a new SessionKey
* - data * - data
* - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0 * - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
* </pre>
* *
*/ */
byte[] encryptExistingSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery, byte[] encryptExistingSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
@ -506,6 +532,7 @@ public class ElGamalAESEngine {
* For both scenarios, this method encrypts the AES area using the given key, iv * For both scenarios, this method encrypts the AES area using the given key, iv
* and making sure the resulting data is at least as long as the paddedSize and * and making sure the resulting data is at least as long as the paddedSize and
* also mod 16 bytes. The contents of the encrypted data is: * also mod 16 bytes. The contents of the encrypted data is:
* <pre>
* - 2 byte integer specifying the # of session tags * - 2 byte integer specifying the # of session tags
* - that many 32 byte session tags * - that many 32 byte session tags
* - 4 byte integer specifying data.length * - 4 byte integer specifying data.length
@ -513,6 +540,7 @@ public class ElGamalAESEngine {
* - 1 byte flag that, if == 1, is followed by a new SessionKey * - 1 byte flag that, if == 1, is followed by a new SessionKey
* - data * - data
* - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0 * - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
* </pre>
* *
*/ */
final byte[] encryptAESBlock(byte data[], SessionKey key, byte[] iv, Set tagsForDelivery, SessionKey newKey, final byte[] encryptAESBlock(byte data[], SessionKey key, byte[] iv, Set tagsForDelivery, SessionKey newKey,

View File

@ -85,9 +85,17 @@ public class ElGamalEngine {
} }
/** encrypt the data to the public key /** encrypt the data to the public key
* @return encrypted data, will be about twice as big as the cleartext * @return encrypted data, will be exactly 514 bytes long
* Contains the two-part encrypted data starting at bytes 0 and 257.
* If the encrypted parts are smaller than 257 bytes, they will be
* padded with leading zeros.
* The parts appear to always be 256 bytes or less, in other words,
* bytes 0 and 257 are always zero.
* @param publicKey public key encrypt to * @param publicKey public key encrypt to
* @param data data to encrypt, must be 222 bytes or less * @param data data to encrypt, must be 222 bytes or less
* As the encrypted data may contain a substantial number of zeros if the
* cleartext is smaller than 222 bytes, it is recommended that the caller pad
* the cleartext to 222 bytes with random data.
*/ */
public byte[] encrypt(byte data[], PublicKey publicKey) { public byte[] encrypt(byte data[], PublicKey publicKey) {
if ((data == null) || (data.length >= 223)) if ((data == null) || (data.length >= 223))
@ -97,6 +105,7 @@ public class ElGamalEngine {
long start = _context.clock().now(); long start = _context.clock().now();
byte d2[] = new byte[1+Hash.HASH_LENGTH+data.length]; byte d2[] = new byte[1+Hash.HASH_LENGTH+data.length];
// FIXME this isn't a random nonzero byte!
d2[0] = (byte)0xFF; d2[0] = (byte)0xFF;
Hash hash = _context.sha().calculateHash(data); Hash hash = _context.sha().calculateHash(data);
System.arraycopy(hash.getData(), 0, d2, 1, Hash.HASH_LENGTH); System.arraycopy(hash.getData(), 0, d2, 1, Hash.HASH_LENGTH);
@ -156,11 +165,15 @@ public class ElGamalEngine {
} }
/** Decrypt the data /** Decrypt the data
* @param encrypted encrypted data, must be 514 bytes or less * @param encrypted encrypted data, must be exactly 514 bytes
* Contains the two-part encrypted data starting at bytes 0 and 257.
* If the encrypted parts are smaller than 257 bytes, they must be
* padded with leading zeros.
* @param privateKey private key to decrypt with * @param privateKey private key to decrypt with
* @return unencrypted data * @return unencrypted data or null on failure
*/ */
public byte[] decrypt(byte encrypted[], PrivateKey privateKey) { public byte[] decrypt(byte encrypted[], PrivateKey privateKey) {
// actually it must be exactly 514 bytes or the arraycopy below will AIOOBE
if ((encrypted == null) || (encrypted.length > 514)) if ((encrypted == null) || (encrypted.length > 514))
throw new IllegalArgumentException("Data to decrypt must be <= 514 bytes at the moment"); throw new IllegalArgumentException("Data to decrypt must be <= 514 bytes at the moment");
long start = _context.clock().now(); long start = _context.clock().now();

View File

@ -23,12 +23,14 @@ import net.i2p.util.Log;
* No whitespace allowed. * No whitespace allowed.
* *
* Decode accepts upper or lower case. * Decode accepts upper or lower case.
* @author zzz
* @since 0.7
*/ */
public class Base32 { public class Base32 {
private final static Log _log = new Log(Base32.class); private final static Log _log = new Log(Base32.class);
/** The 64 valid Base32 values. */ /** The 32 valid Base32 values. */
private final static char[] ALPHABET = {'a', 'b', 'c', 'd', private final static char[] ALPHABET = {'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'k', 'l', 'm', 'n', 'o', 'p',
@ -132,10 +134,16 @@ public class Base32 {
System.out.println("or : Base32 decode"); System.out.println("or : Base32 decode");
} }
/**
* @param source if null will return ""
*/
public static String encode(String source) { public static String encode(String source) {
return (source != null ? encode(source.getBytes()) : ""); return (source != null ? encode(source.getBytes()) : "");
} }
/**
* @param source The data to convert non-null
*/
public static String encode(byte[] source) { public static String encode(byte[] source) {
StringBuilder buf = new StringBuilder((source.length + 7) * 8 / 5); StringBuilder buf = new StringBuilder((source.length + 7) * 8 / 5);
encodeBytes(source, buf); encodeBytes(source, buf);
@ -147,7 +155,7 @@ public class Base32 {
/** /**
* Encodes a byte array into Base32 notation. * Encodes a byte array into Base32 notation.
* *
* @param source The data to convert * @param source The data to convert non-null
*/ */
private static void encodeBytes(byte[] source, StringBuilder out) { private static void encodeBytes(byte[] source, StringBuilder out) {
int usedbits = 0; int usedbits = 0;
@ -174,7 +182,7 @@ public class Base32 {
* Decodes data from Base32 notation and * Decodes data from Base32 notation and
* returns it as a string. * returns it as a string.
* *
* @param s the string to decode * @param s the string to decode, if null returns null
* @return The data as a string or null on failure * @return The data as a string or null on failure
*/ */
public static String decodeToString(String s) { public static String decodeToString(String s) {
@ -184,6 +192,10 @@ public class Base32 {
return new String(b); return new String(b);
} }
/**
* @param s non-null
* @return decoded data, null on error
*/
public static byte[] decode(String s) { public static byte[] decode(String s) {
return decode(s.getBytes()); return decode(s.getBytes());
} }
@ -194,8 +206,8 @@ public class Base32 {
* Decodes Base32 content in byte array format and returns * Decodes Base32 content in byte array format and returns
* the decoded byte array. * the decoded byte array.
* *
* @param source The Base32 encoded data * @param source The Base32 encoded data non-null
* @return decoded data * @return decoded data, null on error
*/ */
private static byte[] decode(byte[] source) { private static byte[] decode(byte[] source) {
int len58; int len58;

View File

@ -42,23 +42,46 @@ public class Base64 {
private final static Log _log = new Log(Base64.class); private final static Log _log = new Log(Base64.class);
/** added by aum */ /**
* @param source if null will return ""
*/
public static String encode(String source) { public static String encode(String source) {
return (source != null ? encode(source.getBytes()) : ""); return (source != null ? encode(source.getBytes()) : "");
} }
/**
* @param source if null will return ""
*/
public static String encode(byte[] source) { public static String encode(byte[] source) {
return (source != null ? encode(source, 0, source.length) : ""); return (source != null ? encode(source, 0, source.length) : "");
} }
/**
* @param source if null will return ""
*/
public static String encode(byte[] source, int off, int len) { public static String encode(byte[] source, int off, int len) {
return (source != null ? encode(source, off, len, false) : ""); return (source != null ? encode(source, off, len, false) : "");
} }
/**
* @param source if null will return ""
* @param useStandardAlphabet Warning, must be false for I2P compatibility
*/
public static String encode(byte[] source, boolean useStandardAlphabet) { public static String encode(byte[] source, boolean useStandardAlphabet) {
return (source != null ? encode(source, 0, source.length, useStandardAlphabet) : ""); return (source != null ? encode(source, 0, source.length, useStandardAlphabet) : "");
} }
/**
* @param source if null will return ""
* @param useStandardAlphabet Warning, must be false for I2P compatibility
*/
public static String encode(byte[] source, int off, int len, boolean useStandardAlphabet) { public static String encode(byte[] source, int off, int len, boolean useStandardAlphabet) {
return (source != null ? safeEncode(source, off, len, useStandardAlphabet) : ""); return (source != null ? safeEncode(source, off, len, useStandardAlphabet) : "");
} }
/**
* @param s Base 64 encoded string using the I2P alphabet A-Z, a-z, 0-9, -, ~
*/
public static byte[] decode(String s) { public static byte[] decode(String s) {
return safeDecode(s, false); return safeDecode(s, false);
} }
@ -84,6 +107,8 @@ public class Base64 {
(byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1',
(byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
(byte) '8', (byte) '9', (byte) '+', (byte) '/'}; (byte) '8', (byte) '9', (byte) '+', (byte) '/'};
/** The 64 valid Base64 values for I2P. */
private final static byte[] ALPHABET_ALT = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', private final static byte[] ALPHABET_ALT = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
(byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
(byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R',
@ -241,7 +266,7 @@ public class Base64 {
* @return four byte array in Base64 notation. * @return four byte array in Base64 notation.
* @since 1.3 * @since 1.3
*/ */
/***** unused /***** unused (standard alphabet)
private static byte[] encode3to4(byte[] threeBytes) { private static byte[] encode3to4(byte[] threeBytes) {
return encode3to4(threeBytes, 3); return encode3to4(threeBytes, 3);
} // end encodeToBytes } // end encodeToBytes
@ -260,11 +285,13 @@ public class Base64 {
* @return four byte array in Base64 notation. * @return four byte array in Base64 notation.
* @since 1.3 * @since 1.3
*/ */
/***** unused (standard alphabet)
private static byte[] encode3to4(byte[] threeBytes, int numSigBytes) { private static byte[] encode3to4(byte[] threeBytes, int numSigBytes) {
byte[] dest = new byte[4]; byte[] dest = new byte[4];
encode3to4(threeBytes, 0, numSigBytes, dest, 0); encode3to4(threeBytes, 0, numSigBytes, dest, 0);
return dest; return dest;
} }
******/
/** /**
* Encodes up to three bytes of the array <var>source</var> * Encodes up to three bytes of the array <var>source</var>
@ -287,6 +314,7 @@ public class Base64 {
* @return the <var>destination</var> array * @return the <var>destination</var> array
* @since 1.3 * @since 1.3
*/ */
/***** unused (standard alphabet)
private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset) { private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset) {
// 1 2 3 // 1 2 3
// 01234567890123456789012345678901 Bit position // 01234567890123456789012345678901 Bit position
@ -329,7 +357,11 @@ public class Base64 {
return destination; return destination;
} // end switch } // end switch
} // end encode3to4 } // end encode3to4
******/
/**
* @param alpha alphabet
*/
private static void encode3to4(byte[] source, int srcOffset, int numSigBytes, StringBuilder buf, byte alpha[]) { private static void encode3to4(byte[] source, int srcOffset, int numSigBytes, StringBuilder buf, byte alpha[]) {
// 1 2 3 // 1 2 3
// 01234567890123456789012345678901 Bit position // 01234567890123456789012345678901 Bit position
@ -628,7 +660,7 @@ public class Base64 {
* Decodes data from Base64 notation. * Decodes data from Base64 notation.
* *
* @param s the string to decode * @param s the string to decode
* @return the decoded data * @return the decoded data, null on error
* @since 1.4 * @since 1.4
*/ */
private static byte[] standardDecode(String s) { private static byte[] standardDecode(String s) {
@ -647,6 +679,7 @@ public class Base64 {
* @param s the strind to decode * @param s the strind to decode
* @return The data as a string * @return The data as a string
* @since 1.4 * @since 1.4
* @throws NPE on error?
*/ */
public static String decodeToString(String s) { public static String decodeToString(String s) {
return new String(decode(s)); return new String(decode(s));
@ -659,7 +692,7 @@ public class Base64 {
* @param source The Base64 encoded data * @param source The Base64 encoded data
* @param off The offset of where to begin decoding * @param off The offset of where to begin decoding
* @param len The length of characters to decode * @param len The length of characters to decode
* @return decoded data * @return decoded data, null on error
* @since 1.3 * @since 1.3
*/ */
private static byte[] decode(byte[] source, int off, int len) { private static byte[] decode(byte[] source, int off, int len) {

View File

@ -23,7 +23,7 @@ import net.i2p.data.SessionKey;
* byte 184: flags * byte 184: flags
* bytes 185-188: request time (in hours since the epoch) * bytes 185-188: request time (in hours since the epoch)
* bytes 189-192: next message ID * bytes 189-192: next message ID
* bytes 193-222: uninterpreted / random padding * bytes 193-221: uninterpreted / random padding
* </pre> * </pre>
* *
*/ */
@ -226,7 +226,7 @@ public class BuildRequestRecord {
* byte 184: flags * byte 184: flags
* bytes 185-188: request time (in hours since the epoch) * bytes 185-188: request time (in hours since the epoch)
* bytes 189-192: next message ID * bytes 189-192: next message ID
* bytes 193-222: uninterpreted / random padding * bytes 193-221: uninterpreted / random padding
*/ */
DataHelper.toLong(buf, OFF_RECV_TUNNEL, 4, receiveTunnelId); DataHelper.toLong(buf, OFF_RECV_TUNNEL, 4, receiveTunnelId);
System.arraycopy(peer.getData(), 0, buf, OFF_OUR_IDENT, Hash.HASH_LENGTH); System.arraycopy(peer.getData(), 0, buf, OFF_OUR_IDENT, Hash.HASH_LENGTH);

View File

@ -35,6 +35,7 @@ public class GarlicMessageBuilder {
/** /**
* This was 100 since 0.6.1.10 (50 before that). It's important because: * This was 100 since 0.6.1.10 (50 before that). It's important because:
* <pre>
* - Tags are 32 bytes. So it previously added 3200 bytes to an initial message. * - Tags are 32 bytes. So it previously added 3200 bytes to an initial message.
* - Too many tags adds a huge overhead to short-duration connections * - Too many tags adds a huge overhead to short-duration connections
* (like http, datagrams, etc.) * (like http, datagrams, etc.)
@ -43,14 +44,17 @@ public class GarlicMessageBuilder {
* - This reduces the effective maximum datagram size because the client * - This reduces the effective maximum datagram size because the client
* doesn't know when tags will be bundled, so the tag size must be * doesn't know when tags will be bundled, so the tag size must be
* subtracted from the maximum I2NP size or transport limit. * subtracted from the maximum I2NP size or transport limit.
* </pre>
* *
* Issues with too small a value: * Issues with too small a value:
* <pre>
* - When tags are sent, a reply leaseset (~1KB) is always bundled. * - When tags are sent, a reply leaseset (~1KB) is always bundled.
* Maybe don't need to bundle more than every minute or so * Maybe don't need to bundle more than every minute or so
* rather than every time? * rather than every time?
* - Does the number of tags (and the threshold of 20) limit the effective * - Does the number of tags (and the threshold of 20) limit the effective
* streaming lib window size? Should the threshold and the number of * streaming lib window size? Should the threshold and the number of
* sent tags be variable based on the message rate? * sent tags be variable based on the message rate?
* </pre>
* *
* We have to be very careful if we implement an adaptive scheme, * We have to be very careful if we implement an adaptive scheme,
* since the key manager is per-router, not per-local-dest. * since the key manager is per-router, not per-local-dest.
@ -218,6 +222,7 @@ public class GarlicMessageBuilder {
byte cloveSet[] = buildCloveSet(ctx, config); byte cloveSet[] = buildCloveSet(ctx, config);
// TODO - 128 is the minimum padded size - should it be more? less? random?
byte encData[] = ctx.elGamalAESEngine().encrypt(cloveSet, target, encryptKey, wrappedTags, encryptTag, 128); byte encData[] = ctx.elGamalAESEngine().encrypt(cloveSet, target, encryptKey, wrappedTags, encryptTag, 128);
msg.setData(encData); msg.setData(encData);
msg.setMessageExpiration(config.getExpiration()); msg.setMessageExpiration(config.getExpiration());

View File

@ -20,7 +20,7 @@ import net.i2p.router.message.PayloadGarlicConfig;
import net.i2p.util.Log; import net.i2p.util.Log;
/** /**
* Method an class for garlic encrypting outbound netdb traffic, * Method and class for garlic encrypting outbound netdb traffic,
* including management of the ElGamal/AES tags * including management of the ElGamal/AES tags
* *
* @since 0.7.10 * @since 0.7.10

View File

@ -29,18 +29,45 @@ import net.i2p.router.Router;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
import net.i2p.util.Log; import net.i2p.util.Log;
/* /**
* Handle the 4-phase establishment, which is as follows:
*
* <pre>
*
* Alice contacts Bob * Alice contacts Bob
* ========================================================= * =========================================================
*
* Message 1 (Session Request):
* X+(H(X) xor Bob.identHash)-----------------------------> * X+(H(X) xor Bob.identHash)----------------------------->
*
* Message 2 (Session Created):
* <----------------------------------------Y+E(H(X+Y)+tsB, sk, Y[239:255]) * <----------------------------------------Y+E(H(X+Y)+tsB, sk, Y[239:255])
* E(#+Alice.identity+tsA+padding+S(X+Y+Bob.identHash+tsA+tsB+padding), sk, hX_xor_Bob.identHash[16:31])---> *
* Message 3 (Session Confirm A):
* E(sz+Alice.identity+tsA+padding+S(X+Y+Bob.identHash+tsA+tsB), sk, hX_xor_Bob.identHash[16:31])--->
*
* Message 4 (Session Confirm B):
* <----------------------E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev) * <----------------------E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev)
* *
* Key:
*
* X, Y: 256 byte DH keys
* H(): 32 byte SHA256 Hash
* E(data, session key, IV): AES256 Encrypt
* S(): 40 byte DSA Signature
* tsA, tsB: timestamps (4 bytes, seconds since epoch)
* sk: 32 byte Session key
* sz: 2 byte size of Alice identity to follow
*
* </pre>
*
*
* Alternately, when Bob receives a connection, it could be a * Alternately, when Bob receives a connection, it could be a
* check connection (perhaps prompted by Bob asking for someone * check connection (perhaps prompted by Bob asking for someone
* to verify his listener). check connections are formatted per * to verify his listener). check connections are formatted per
* {@link #isCheckInfo()} * isCheckInfo()
* NOTE: Check info is unused.
*
*/ */
public class EstablishState { public class EstablishState {
private RouterContext _context; private RouterContext _context;
@ -57,7 +84,9 @@ public class EstablishState {
// alice receives (and bob sends) // alice receives (and bob sends)
private byte _Y[]; private byte _Y[];
private transient byte _e_hXY_tsB[]; private transient byte _e_hXY_tsB[];
/** Bob's Timestamp in seconds */
private transient long _tsB; private transient long _tsB;
/** Alice's Timestamp in seconds */
private transient long _tsA; private transient long _tsA;
private transient byte _e_bobSig[]; private transient byte _e_bobSig[];
@ -98,9 +127,6 @@ public class EstablishState {
_log = ctx.logManager().getLog(getClass()); _log = ctx.logManager().getLog(getClass());
_transport = transport; _transport = transport;
_con = con; _con = con;
_verified = false;
_corrupt = false;
_confirmWritten = false;
_dh = new DHSessionKeyBuilder(); _dh = new DHSessionKeyBuilder();
if (_con.isInbound()) { if (_con.isInbound()) {
_X = new byte[256]; _X = new byte[256];
@ -116,10 +142,7 @@ public class EstablishState {
_prevEncrypted = new byte[16]; _prevEncrypted = new byte[16];
_curEncrypted = new byte[16]; _curEncrypted = new byte[16];
_curEncryptedOffset = 0;
_curDecrypted = new byte[16]; _curDecrypted = new byte[16];
_received = 0;
} }
/** /**
@ -150,7 +173,10 @@ public class EstablishState {
public boolean getFailedBySkew() { return _failedBySkew; } public boolean getFailedBySkew() { return _failedBySkew; }
/** we are Bob, so receive these bytes as part of an inbound connection */ /**
* we are Bob, so receive these bytes as part of an inbound connection
* This method receives messages 1 and 3, and sends messages 2 and 4.
*/
private void receiveInbound(ByteBuffer src) { private void receiveInbound(ByteBuffer src) {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug(prefix()+"Receiving inbound: prev received=" + _received + " src.remaining=" + src.remaining()); _log.debug(prefix()+"Receiving inbound: prev received=" + _received + " src.remaining=" + src.remaining());
@ -311,7 +337,10 @@ public class EstablishState {
} }
} }
/** we are Alice, so receive these bytes as part of an outbound connection */ /**
* We are Alice, so receive these bytes as part of an outbound connection.
* This method receives messages 2 and 4, and sends message 3.
*/
private void receiveOutbound(ByteBuffer src) { private void receiveOutbound(ByteBuffer src) {
if (_log.shouldLog(Log.DEBUG)) _log.debug(prefix()+"Receive outbound " + src + " received=" + _received); if (_log.shouldLog(Log.DEBUG)) _log.debug(prefix()+"Receive outbound " + src + " received=" + _received);
@ -498,8 +527,10 @@ public class EstablishState {
public boolean isComplete() { return _verified; } public boolean isComplete() { return _verified; }
/** /**
* we are establishing an outbound connection, so prepare ourselves by * We are Alice.
* We are establishing an outbound connection, so prepare ourselves by
* queueing up the write of the first part of the handshake * queueing up the write of the first part of the handshake
* This method sends message #1 to Bob.
*/ */
public void prepareOutbound() { public void prepareOutbound() {
if (_received <= 0) { if (_received <= 0) {
@ -516,7 +547,9 @@ public class EstablishState {
} }
/** /**
* make sure the signatures are correct, and if they are, update the * We are Bob. Verify message #3 from Alice, then send message #4 to Alice.
*
* Make sure the signatures are correct, and if they are, update the
* NIOConnection with the session key / peer ident / clock skew / iv. * NIOConnection with the session key / peer ident / clock skew / iv.
* The NIOConnection itself is responsible for registering with the * The NIOConnection itself is responsible for registering with the
* transport * transport
@ -623,6 +656,9 @@ public class EstablishState {
} }
} }
/**
* We are Bob. Send message #4 to Alice.
*/
private void sendInboundConfirm(RouterIdentity alice, long tsA) { private void sendInboundConfirm(RouterIdentity alice, long tsA) {
// send Alice E(S(X+Y+Alice.identHash+tsA+tsB), sk, prev) // send Alice E(S(X+Y+Alice.identHash+tsA+tsB), sk, prev)
byte toSign[] = new byte[256+256+32+4+4]; byte toSign[] = new byte[256+256+32+4+4];

View File

@ -607,6 +607,7 @@ public class PacketBuilder {
// pad here if we want. maybe randomized? // pad here if we want. maybe randomized?
// pad up so we're on the encryption boundary // pad up so we're on the encryption boundary
// TODO: why not random data?
if ( (off % 16) != 0) if ( (off % 16) != 0)
off += 16 - (off % 16); off += 16 - (off % 16);
packet.getPacket().setLength(off); packet.getPacket().setLength(off);