// Rc6Cipher - the RC6 encryption method // // This was one of the finalists for the Advanced Encryption Standard. // Most of the algorithm is Copyright (C) 1998 RSA Data Security, Inc. // A few modifications to the algorithm, and the surrounding glue, are: // // Copyright (C)1996,2000 by Jef Poskanzer . // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. // // Visit the ACME Labs Java page for up-to-date versions of this and other // fine Java utilities: http://www.acme.com/java/ package Acme.Crypto; import java.io.*; /// The RC6 encryption method. //

// This was one of the finalists for the Advanced Encryption Standard. //

// Fetch the software.
// Fetch the entire Acme package. //

// @see EncryptedOutputStream // @see EncryptedInputStream public class Rc6Cipher extends BlockCipher { // Constructor, string key. public Rc6Cipher( String keyStr ) { super( 0, 16 ); setKey( keyStr ); } // Constructor, byte-array key. public Rc6Cipher( byte[] key ) { super( 0, 16 ); setKey( key ); } // Key routines. private int S0, S1, S42, S43; private int[] Sp0 = new int[5]; private int[] Sp1 = new int[5]; private int[] Sp2 = new int[5]; private int[] Sp3 = new int[5]; private int[] Sp4 = new int[5]; private int[] Sp5 = new int[5]; private int[] Sp6 = new int[5]; private int[] Sp7 = new int[5]; /// Set the key. public void setKey( byte[] key ) { int keyLength = key.length; if ( keyLength > 255 ) keyLength = 255; // A convenient table of values for f(x) = (x+1) mod 36. final byte[] nextScheduleDwordIndex = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 0 }; // Compute how many dwords of key we need. int c = ( ( keyLength + 3 ) >> 2 ) + ( ( keyLength - 1 ) >>> 31 ); int L[] = new int[c]; short nextKeyDwordIndex[] = new short[c]; // Load all the key dwords that get a full 4 bytes of key. At // the same time, set up a table of values for g(x) = (x+1) mod c. int almostEnd = keyLength & 0xfffffffc; for ( int i = almostEnd - 4; i >= 0; i -= 4 ) { int index = i >> 2; L[index] = ( ( ( (int) key[i ] ) & 0xff ) ) + ( ( ( (int) key[i + 1] ) & 0xff ) << 8 ) + ( ( ( (int) key[i + 2] ) & 0xff ) << 16 ) + ( ( ( (int) key[i + 3] ) & 0xff ) << 24 ); nextKeyDwordIndex[index] = (short) (index + 1); } // Now fill up last dword. int remainder = keyLength & 3; if ( remainder > 0 ) { int lastDword = ( (int) key[almostEnd] ) & 0xff; if ( remainder > 1 ) { lastDword |= ( ( (int) key[almostEnd + 1] ) & 0xff ) << 8; if ( remainder > 2 ) lastDword |= ( ( (int) key[almostEnd + 2] ) & 0xff ) << 16; } L[c - 1] = lastDword; } // Finish setting up table by setting last value. nextKeyDwordIndex[c - 1] = 0; // Key schedule table. This starts out having the values generated // from the magic constants P32 and Q32. int[] S = { 0xb7e15163, 0x5618cb1c, // Pseudo-round #0 0xf45044d5, 0x9287be8e, // Round #1 0x30bf3847, 0xcef6b200, // Round #2 0x6d2e2bb9, 0x0b65a572, // Round #3 0xa99d1f2b, 0x47d498e4, // Round #4 0xe60c129d, 0x84438c56, // Round #5 0x227b060f, 0xc0b27fc8, // Round #6 0x5ee9f981, 0xfd21733a, // Round #7 0x9b58ecf3, 0x399066ac, // Round #8 0xd7c7e065, 0x75ff5a1e, // Round #9 0x1436d3d7, 0xb26e4d90, // Round #10 0x50a5c749, 0xeedd4102, // Round #11 0x8d14babb, 0x2b4c3474, // Round #12 0xc983ae2d, 0x67bb27e6, // Round #13 0x05f2a19f, 0xa42a1b58, // Round #14 0x42619511, 0xe0990eca, // Round #15 0x7ed08883, 0x1d08023c, // Round #16 0xbb3f7bf5, 0x5976f5ae, // Round #17 0xf7ae6f67, 0x95e5e920, // Round #18 0x341d62d9, 0xd254dc92, // Round #19 0x708c564b, 0x0ec3d004, // Round #20 0xacfb49bd, 0x4b32c376 // Pseudo-round #21 }; int i = 0; int j = 0; int A = 0; int B = 0; int sum; // Now we actually mix the key into the key schedule array. for ( int counter = ( ( c <= 44 ) ? 44 : c ); --counter >= 0; ) { // A = S[i] = (S[i]+A+B) rotated left by 3. A += S[i] + B; S[i] = A = ( A << 3 ) | ( A >>> 29 ); // B = L[j] = (L[j]+A+B) rotated left by (A+B). sum = A + B; B = sum + L[j]; L[j] = B = ( B << sum ) | ( B >>> ( -sum ) ); // i = (i+1) mod t and j = (j+1) mod c. i = nextScheduleDwordIndex[i]; j = nextKeyDwordIndex[j]; // A = S[i] = (S[i]+A+B) rotated left by 3. A += S[i] + B; S[i] = A = ( A << 3 ) | ( A >>> 29 ); // B = L[j] = (L[j]+A+B) rotated left by (A+B). sum = A + B; B = sum + L[j]; L[j] = B = ( B << sum ) | ( B >>> ( -sum ) ); // i = (i+1) mod t and j = (j+1) mod c. i = nextScheduleDwordIndex[i]; j = nextKeyDwordIndex[j]; // A = S[i] = (S[i]+A+B) rotated left by 3. A += S[i] + B; S[i] = A = ( A << 3 ) | ( A >>> 29 ); // B = L[j] = (L[j]+A+B) rotated left by (A+B). sum = A + B; B = sum + L[j]; L[j] = B = ( B << sum ) | ( B >>> ( -sum ) ); // i = (i+1) mod t and j = (j+1) mod c. i = nextScheduleDwordIndex[i]; j = nextKeyDwordIndex[j]; } S0 = S[0]; S1 = S[1]; int count = 0; int offset = 1; do { Sp0[count] = S[++offset]; Sp1[count] = S[++offset]; Sp2[count] = S[++offset]; Sp3[count] = S[++offset]; Sp4[count] = S[++offset]; Sp5[count] = S[++offset]; Sp6[count] = S[++offset]; Sp7[count] = S[++offset]; ++count; } while ( offset <= 33 ); S42 = S[42]; S43 = S[43]; } // Encryption routines. private int[] tempInts = new int[4]; /// Encrypt a block. public void encrypt( byte[] clearText, int clearOff, byte[] cipherText, int cipherOff ) { // Get A, B, C, D. squashBytesToIntsLittle( clearText, clearOff, tempInts, 0, 4 ); int A = tempInts[0]; int B = tempInts[1]; int C = tempInts[2]; int D = tempInts[3]; // Do pseudo-round #0. B += S0; D += S1; int t,u; for ( int i = 0; i < 5; ++i ) { // Round #1, #5, #9, #13, #17. t = B * ( ( B << 1 ) + 1 ); u = D * ( ( D << 1 ) + 1 ); t = ( t << 5 ) | ( t >>> 27 ); u = ( u << 5 ) | ( u >>> 27 ); A ^= t; C ^= u; A = ( ( A << u ) | ( A >>> -u ) ) + Sp0[i]; C = ( ( C << t ) | ( C >>> -t ) ) + Sp1[i]; // Round #2, #6, #10, #14, #18. t = C * ( ( C << 1 ) + 1 ); u = A * ( ( A << 1 ) + 1 ); t = ( t << 5 ) | ( t >>> 27 ); u = ( u << 5 ) | ( u >>> 27 ); B ^= t; D ^= u; B = ( ( B << u ) | ( B >>> -u ) ) + Sp2[i]; D = ( ( D << t ) | ( D >>> -t ) ) + Sp3[i]; // Round #3, #7, #11, #15, #19. t = D * ( ( D << 1 ) + 1 ); u = B * ( ( B << 1 ) + 1 ); t = ( t << 5 ) | ( t >>> 27 ); u = ( u << 5 ) | ( u >>> 27 ); C ^= t; A ^= u; C = ( ( C << u ) | ( C >>> -u ) ) + Sp4[i]; A = ( ( A << t ) | ( A >>> -t ) ) + Sp5[i]; // Round #4, #8, #12, #16, #20. t = A * ( ( A << 1 ) + 1 ); u = C * ( ( C << 1 ) + 1 ); t = ( t << 5 ) | ( t >>> 27 ); u = ( u << 5 ) | ( u >>> 27 ); D ^= t; B ^= u; D = ( ( D << u ) | ( D >>> -u ) ) + Sp6[i]; B = ( ( B << t ) | ( B >>> -t ) ) + Sp7[i]; } // Do pseudo-round #21. A += S42; C += S43; // Return cipher text. tempInts[0] = A; tempInts[1] = B; tempInts[2] = C; tempInts[3] = D; spreadIntsToBytesLittle( tempInts, 0, cipherText, cipherOff, 4 ); } /// Decrypt a block. public void decrypt( byte[] cipherText, int cipherOff, byte[] clearText, int clearOff ) { // Get A, B, C, D. squashBytesToIntsLittle( cipherText, cipherOff, tempInts, 0, 4 ); int A = tempInts[0]; int B = tempInts[1]; int C = tempInts[2]; int D = tempInts[3]; // Undo pseudo-round #21. C -= S43; A -= S42; int t,u; for ( int i = 4; i >= 0; --i ) { // Round #20, #16, #12, #8, #4. t = A * ( ( A << 1 ) + 1 ); u = C * ( ( C << 1 ) + 1 ); t = ( t << 5 ) | ( t >>> 27 ); u = ( u << 5 ) | ( u >>> 27 ); B -= Sp7[i]; D -= Sp6[i]; B = ( ( B >>> t ) | ( B << -t ) ) ^u; D = ( ( D >>> u ) | ( D << -u ) ) ^t; // Round #19, #15, #11, #7, #3. t = D * ( ( D << 1 ) + 1 ); u = B * ( ( B << 1 ) + 1 ); t = ( t << 5 ) | ( t >>> 27 ); u = ( u << 5 ) | ( u >>> 27 ); A -= Sp5[i]; C -= Sp4[i]; A = ( ( A >>> t ) | ( A << -t ) ) ^u; C = ( ( C >>> u ) | ( C << -u ) ) ^t; // Round #18, #14, #10, #6, #2. t = C * ( ( C << 1 ) + 1 ); u = A * ( ( A << 1 ) + 1 ); t = ( t << 5 ) | ( t >>> 27 ); u = ( u << 5 ) | ( u >>> 27 ); D -= Sp3[i]; B -= Sp2[i]; D = ( ( D >>> t ) | ( D << -t ) ) ^u; B = ( ( B >>> u ) | ( B << -u ) ) ^t; // Round #17, #13, #9, #5, #1. t = B * ( ( B << 1 ) + 1 ); u = D * ( ( D << 1 ) + 1 ); t = ( t << 5 ) | ( t >>> 27 ); u = ( u << 5 ) | ( u >>> 27 ); C -= Sp1[i]; A -= Sp0[i]; C = ( ( C >>> t ) | ( C << -t ) ) ^u; A = ( ( A >>> u ) | ( A << -u ) ) ^t; } // Undo pseudo-round #0. D -= S1; B -= S0; // Return clear text. tempInts[0] = A; tempInts[1] = B; tempInts[2] = C; tempInts[3] = D; spreadIntsToBytesLittle( tempInts, 0, clearText, clearOff, 4 ); } /// Test routine. public static void main( String[] args ) { byte[] cipherText = new byte[16]; byte[] decipherText = new byte[16]; BlockCipher rc6a = new Rc6Cipher( "0123456789" ); byte[] clearText1 = { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; System.out.println( "cleartext: " + toStringBlock( clearText1 ) ); rc6a.encrypt( clearText1, cipherText ); System.out.println( "encrypted: " + toStringBlock( cipherText ) ); rc6a.decrypt( cipherText, decipherText ); System.out.println( "decrypted: " + toStringBlock( decipherText ) ); System.out.println(); BlockCipher rc6b = new Rc6Cipher( "abcdefghijklmnopqrstuvwxyz" ); byte[] clearText2 = { (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c, (byte) 0x0d, (byte) 0x0e, (byte) 0x0f }; System.out.println( "cleartext: " + toStringBlock( clearText2 ) ); rc6b.encrypt( clearText2, cipherText ); System.out.println( "encrypted: " + toStringBlock( cipherText ) ); rc6b.decrypt( cipherText, decipherText ); System.out.println( "decrypted: " + toStringBlock( decipherText ) ); } }