/* 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.crypt; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Control mechanism for the Periodic Cipher Feed Back mode. This is * a CFB variant used apparently by a number of programs, including PGP. * Thanks to Hal for suggesting it. * * http://www.streamsec.com/pcfb1.pdf * * NOTE: This is identical to CFB if block size = key size. As of Freenet 0.7, * we use it with block size = key size. Which is not recommended, but is as * safe as CFB. We will get rid of this eventually, and move to 128-bit block * size (i.e. standard AES) with a more standard mode (e.g. CTR or CBC). * * @author Scott */ public class PCFBMode { /** The underlying block cipher. */ protected final BlockCipher c; /** The register, with which data is XOR'ed */ protected final byte[] feedback_register; /** When this reaches the end of the register, we refillBuffer() i.e. re-encrypt the * register. */ protected int registerPointer; /** Create the PCFB with no IV. The caller must either: * a) Call reset() with a proper IV, or * b) Accept the initial IV of all zero's. (We will still encrypt this before using it). * If the key is random and never reused, for instance if it is a one time key or * derived from a hash, b) may be acceptable. It is used in some parts of Freenet. * However, it is very bad practice cryptographically, and we should get rid of it. * NOTE THAT IV:KEY PAIRS *MUST* BE UNIQUE! If two instances use the same key with the * same empty IV, the bad guys will be able to XOR the two ciphertexts to get the XOR * of the plaintext. If they can deduce the register's value they may even be able to * decrypt the next 32 bytes, however after that they should hopefully be stumped - * but it will certainly make more sophisticated attacks easier. * FIXME CRYPTO !!! * @param c The underlying block cipher * @deprecated */ @Deprecated public static PCFBMode create(BlockCipher c) { return new PCFBMode(c); } public static PCFBMode create(BlockCipher c, byte[] iv) { return create(c, iv, 0); } /** Create the PCFB with an IV. The register pointer will be set to the end of the IV, * so refillBuffer() will be called prior to any encryption. IV's *must* be unique for * a given key. IT IS STRONGLY RECOMMENDED TO USE THIS CONSTRUCTOR, THE OTHER ONE WILL * BE REMOVED EVENTUALLY. * @param offset */ public static PCFBMode create(BlockCipher c, byte[] iv, int offset) { return new PCFBMode(c, iv, offset); } protected PCFBMode(BlockCipher c) { this.c = c; feedback_register = new byte[c.getBlockSize() >> 3]; registerPointer = feedback_register.length; } protected PCFBMode(BlockCipher c, byte[] iv, int offset) { this(c); System.arraycopy(iv, offset, feedback_register, 0, feedback_register.length); // registerPointer is already set to the end by this(c), so we will refillBuffer() immediately. } /** * Resets the PCFBMode to an initial IV */ public final void reset(byte[] iv) { System.arraycopy(iv, 0, feedback_register, 0, feedback_register.length); registerPointer = feedback_register.length; } /** * Resets the PCFBMode to an initial IV * @param iv The buffer containing the IV. * @param offset The offset to start reading the IV at. */ public final void reset(byte[] iv, int offset) { System.arraycopy(iv, offset, feedback_register, 0, feedback_register.length); registerPointer = feedback_register.length; } /** * Writes the initialization vector to the stream. Though the IV * is transmitted in the clear, this gives the attacker no additional * information because the registerPointer is set so that the encrypted * buffer is empty. This causes an immediate encryption of the IV, * thus invalidating any information that the attacker had. */ public void writeIV(RandomSource rs, OutputStream out) throws IOException { rs.nextBytes(feedback_register); out.write(feedback_register); } /** * Reads the initialization vector from the given stream. */ public void readIV(InputStream in) throws IOException { //for (int i=0; i<feedback_register.length; i++) { // feedback_register[i]=(byte)in.read(); //} Util.readFully(in, feedback_register); } /** * returns the length of the IV */ public int lengthIV() { return feedback_register.length; } /** * returns the length of the IV for a PCFB created with a specific cipher. */ public static int lengthIV(BlockCipher c) { return c.getBlockSize() >> 3; } /** * Deciphers one byte of data, by XOR'ing the ciphertext byte with * one byte from the encrypted buffer. Then places the received * byte in the feedback register. If no bytes are available in * the encrypted buffer, the feedback register is encrypted, providing * block_size/8 new bytes for decryption */ //public synchronized int decipher(int b) { public int decipher(int b) { if (registerPointer == feedback_register.length) refillBuffer(); int rv = (feedback_register[registerPointer] ^ (byte) b) & 0xff; feedback_register[registerPointer++] = (byte) b; return rv; } public void blockDecipher(byte[] buf, int off, int len) { final int feedback_length = feedback_register.length; if (registerPointer != 0) { /* handle first incomplete feedback run */ int l = Math.min(feedback_length - registerPointer, len); len -= l; while(l-- > 0) { byte b = buf[off]; buf[off++] ^= feedback_register[registerPointer]; feedback_register[registerPointer++] = b; } if (len == 0) return; refillBuffer(); } // assert(registerPointer == 0); while (len > feedback_length) { /* consume full blocks */ // note: we skip *last* full block to avoid extra refillBuffer() len -= feedback_length; while (registerPointer < feedback_length) { byte b = buf[off]; buf[off++] ^= feedback_register[registerPointer]; feedback_register[registerPointer++] = b; } refillBuffer(); } // assert(registerPointer == 0 && len <= feedback_length); while (len-- > 0) { /* handle final block */ byte b = buf[off]; buf[off++] ^= feedback_register[registerPointer]; feedback_register[registerPointer++] = b; } return; } /** * Enciphers one byte of data, by XOR'ing the plaintext byte with * one byte from the encrypted buffer. Then places the enciphered * byte in the feedback register. If no bytes are available in * the encrypted buffer, the feedback register is encrypted, providing * block_size/8 new bytes for encryption */ //public synchronized int encipher(int b) { public int encipher(int b) { if (registerPointer == feedback_register.length) refillBuffer(); feedback_register[registerPointer] ^= (byte) b; return feedback_register[registerPointer++] & 0xff; } public void blockEncipher(byte[] buf, int off, int len) { final int feedback_length = feedback_register.length; if (registerPointer != 0) { /* handle first incomplete feedback run */ int l = Math.min(feedback_length - registerPointer, len); for(len -= l; l-- > 0; off++) buf[off] = (feedback_register[registerPointer++] ^= buf[off]); if (len == 0) return; refillBuffer(); } // assert(registerPointer == 0); while (len > feedback_length) { /* consume full blocks */ // note: we skip *last* full block to avoid extra refillBuffer() len -= feedback_length; for (; registerPointer < feedback_length; off++) buf[off] = (feedback_register[registerPointer++] ^= buf[off]); refillBuffer(); } // assert(registerPointer == 0 && len <= feedback_length); for (; len-- > 0; off++) { /* handle final partial block */ buf[off] = (feedback_register[registerPointer++] ^= buf[off]); } return; } // Refills the encrypted buffer with data. //private synchronized void refillBuffer() { protected void refillBuffer() { // Encrypt feedback into result c.encipher(feedback_register, feedback_register); registerPointer=0; } }