Block encryption implement the Tiny Encryption Algorithm TEA
Block encryption:
implement the Tiny Encryption Algorithm (TEA) in Java.
- Part 1: Use your TEA algorithm to encrypt the 64-bit plaintext block:
0x0123456789ABCDEF
Using 128 bit key:
0xA56BABCDEF00F000FFFFFFFFABCDEF01
Enter your cipher text here:
- Part 2: Implement decryption and verify that you obtain the original plain text
Solution
 public class TinyE {
   
     public static enum Mode { ECB, CBC, CTR };
     private final int DELTA_INT = 0x9e3779b9;
    /**
      * Encrypts an array of Integers using the given key, mode, and array.
      * @param plaintext length must be even, so that all 64 bit blocks are
      * present. This is verified in the conversions in Tools.java
      * @param key 64 bits (Integer[] of length 2)
      * @param mode ECB, CBC, or CTR - used for encrypting multiple blocks
      * @param iv used by CBC and CTR modes
      * @return Encrypted message Integer[] of same length as plaintext
      */
     public Integer[] encrypt(Integer[] plaintext, Integer[] key, Mode mode,
             Integer[] iv) {
         // Make sure the parameters are properly provided
         checkParams(plaintext, key, mode, iv);
       
         switch (mode) {
             case CBC:
                 return encryptCBC(plaintext, key, iv);
             case CTR:
                 return cryptCTR(plaintext, key, iv);
             case ECB:
             default:
                 return encryptECB(plaintext, key);          
          }
     }
    /**
      * Decrypts an array of Integers using the given key, mode, and array.
      * @param ciphertext length must be even, so that all 64 bit blocks are
      * present. This is verified in the conversions in Tools.java
      * @param key 64 bits (Integer[] of length 2)
      * @param mode ECB, CBC, or CTR - used for decrypting multiple blocks
      * @param iv used by CBC and CTR modes
      * @return Decrypted message Integer[] of same length as ciphertext
      */
     public Integer[] decrypt(Integer[] ciphertext, Integer[] key, Mode mode,
             Integer[] iv) {
         // Make sure the parameters are properly provided
         checkParams(ciphertext, key, mode, iv);      
              
          switch (mode) {
             case CBC:
                 return decryptCBC(ciphertext, key, iv);
             case CTR:
                 return cryptCTR(ciphertext, key, iv);
             case ECB:
             default:
                 return decryptECB(ciphertext, key);
         }
     }
   
     /**
      * Encrypt a message with ECB mode
      * @param plaintext the plaintext Integer[]
      * @param key Integer[]
      * @return Integer[] of encrypted message
      */
     private Integer[] encryptECB(Integer[] plaintext, Integer[] key) {
         // Init variables
         Integer[] cipherBlock;
         Integer[] plainBlock = new Integer[2];
         Integer[] ciphertext = new Integer[plaintext.length];
        // Cycle through each 64-bit block (every 2 Integers)
         for (int i = 0; i < plaintext.length; i+=2) {
             plainBlock[0] = plaintext[i];
             plainBlock[1] = plaintext[i+1];
             // Encrypt block
             cipherBlock = this.encryptBlock(plainBlock, key);
             // Append to full encrypted message
             ciphertext[i] = cipherBlock[0];
             ciphertext[i+1] = cipherBlock[1];
         }
         return ciphertext;
     }
   
     /**
      * Encrypt a message with CBC mode
      * @param plaintext the plaintext Integer[]
      * @param key Integer[] 128-bit key
      * @param iv Integer[] 64-bit iv
      * @return Integer[] of encrypted message
      */
     private Integer[] encryptCBC(Integer[] plaintext, Integer[] key,
             Integer[] iv) {
         // Init variables
         Integer[] cipherBlock;
         Integer[] plainBlock = new Integer[2];
         Integer[] ciphertext = new Integer[plaintext.length];
         Integer[] xor = iv;
        // Cycle through each 64-bit block (2 integers = 64 bits)
         for (int i = 0; i < plaintext.length; i+=2) {
             plainBlock[0] = xor[0] ^ plaintext[i];
             plainBlock[1] = xor[1] ^ plaintext[i+1];
             // Encrypt block
             cipherBlock = this.encryptBlock(plainBlock, key);
           
             // reset xor variable
             xor[0] = cipherBlock[0];
             xor[1] = cipherBlock[1];
             // copy value into final array
             ciphertext[i] = cipherBlock[0];
             ciphertext[i+1] = cipherBlock[1];
         }
         return ciphertext;
     }
   
     /**
      * Encrypt or decrypt a message encrypted with CTR mode
      * @param oldtext the encrypted or decrypted Integer[]
      * @param key Integer[] 128-bit key
      * @param iv Integer[] 64-bit iv
      * @return Integer[] of decrypted or encrypted message
      */
     private Integer[] cryptCTR(Integer[] oldtext, Integer[] key,
             Integer[] iv) {
         // Init variables
         Integer[] newBlock;
         Integer[] newtext = new Integer[oldtext.length];
        // holds 64 bit interpretation of iv for incrementing
         long tempIV = ((((long)iv[0]) << 32) & 0xffffffff00000000l) |
                 ((long)(iv[1] & 0x00000000ffffffffl));
        for (int i = 0; i < oldtext.length; i+=2) {
             // CTR works like a stream cipher
             newBlock = this.encryptBlock(iv, key);
             // XOR 64 bit blocks together
             newtext[i] = oldtext[i] ^ newBlock[0];
             newtext[i+1] = oldtext[i+1] ^ newBlock[1];
             // increment iv
             tempIV++;
             iv[0] = (int) (tempIV >> 32);
             iv[1] = (int) tempIV;
         }
         return newtext;
     }
   
     /**
      * Decrypt a message encrypted with ECB mode
      * @param ciphertext the encrypted Integer[]
      * @param key Integer[]
      * @return Integer[] of decrypted message
      */
     private Integer[] decryptECB(Integer[] ciphertext, Integer[] key) {
         // Init variables
         Integer[] cipherBlock = new Integer[2];
         Integer[] plainBlock;
         Integer[] plaintext = new Integer[ciphertext.length];
        // Cycle through each 64-bit block;
         for (int i = 0; i < ciphertext.length; i+=2) {
             cipherBlock[0] = ciphertext[i];
             cipherBlock[1] = ciphertext[i+1];
             // Decrypt this block
             plainBlock = this.decryptBlock(cipherBlock, key);
           
             // append to full plaintext message
             plaintext[i] = plainBlock[0];
             plaintext[i+1] = plainBlock[1];
         }
         return plaintext;
     }
   
     /**
      * Decrypt a message encrypted with CBC mode
      * @param ciphertext the encrypted Integer[]
      * @param key Integer[] 128-bit key
      * @param iv Integer[] 64-bit iv
      * @return Integer[] of decrypted message
      */
     private Integer[] decryptCBC(Integer[] ciphertext, Integer[] key,
             Integer[] iv) {
         // Init variables
         Integer[] cipherBlock = new Integer[2];
         Integer[] plainBlock;
         Integer[] plaintext = new Integer[ciphertext.length];
         Integer[] xor = iv;
        // Iterate through each 64-bit block
         for (int i = 0; i < ciphertext.length; i+=2) {
             // get next 64 bit block
             cipherBlock[0] = ciphertext[i];
             cipherBlock[1] = ciphertext[i+1];
             // decrypt block
             plainBlock = this.decryptBlock(cipherBlock, key);
             // Perform xor operation
             plaintext[i] = xor[0] ^ plainBlock[0];
             plaintext[i+1] = xor[1] ^ plainBlock[1];
             // Setup xor variable for next cylcle
             xor[0] = ciphertext[i];
             xor[1] = ciphertext[i+1];
         }
         return plaintext;
     }
    /**
      * The basic TEA encryption algorithm on one 64-bit block of data
      * @param plaintext an Integer[] - The 64-bit block to encrypt
      * @param key an Integer[] - The 128-bit key
      * @return the encrypted ciphertext
      */
     private Integer[] encryptBlock(Integer[] plaintext, Integer[] key) {
         // Setup
         int l = plaintext[0];   // l is the left side of the text block
         int r = plaintext[1];   // r is the right side of the text block
         int sum = 0;
               
          // basic tea encrypt cycle
         for (int i=0; i < 32; i++) {
             sum += DELTA_INT;
             l += ((r<<4) + key[0]) ^ (r + sum) ^ ((r>>5) + key[1]);
             r += ((l<<4) + key[2]) ^ (l + sum) ^ ((l>>5) + key[3]);
         }
        Integer[] ciphertext = {l, r};
         return ciphertext;
     }
   
     /**
      * The basic TEA decryption algorithm on one 64-bit block of data
      * @param ciphertext an Integer[] - The 64-bit block to decrypt
      * @param key an Integer[] - The 128-bit key
      * @return the decrypted plaintext
      */
     private Integer[] decryptBlock(Integer[] ciphertext, Integer[] key) {
         // Setup
         int l = ciphertext[0];   // l is the left side of the text block
         int r = ciphertext[1];   // r is the right side of the text block
         int sum = DELTA_INT << 5;
               
          // basic tea encrypt cycle
         for (int i=0; i < 32; i++) {
             r -= ((l<<4) + key[2]) ^ (l + sum) ^ ((l>>5) + key[3]);
             l -= ((r<<4) + key[0]) ^ (r + sum) ^ ((r>>5) + key[1]);
             sum -= DELTA_INT;
         }
        Integer[] plaintext = {l, r};
         return plaintext;
     }
   
     /**
      * Verify that all parameters were correctly filled.
      * @param text Integer[] containing the long message to encode or decode
      * @param key 128 bit key
      * @param mode ECB, CBC, or CTR
      * @param iv 64 bit iv if present
      */
     private void checkParams(Integer[] text, Integer[] key, Mode mode,
             Integer[] iv) {
         // Size must be a multiple of 64-bits (2 integers)
         if (text.length % 2 != 0) {
             System.err.println(\"Text is not a multple of 64 bits.\");
             System.exit(1);
         }
         else if (!arrayFilled(text, text.length)) {
             System.err.println(\"Text is not correctly filled.\");
             System.exit(1);
         }
         else if (!arrayFilled(key, 4)) {
             System.err.println(\"Key is not correctly filled.\");
             System.exit(1);          
          }
         else if (mode == null) {
             System.err.println(\"Mode is null. Please assign it a value.\");
             System.exit(1);
         }
         else if (mode != Mode.ECB) {
             if (!arrayFilled(iv, 2)) {
                 System.err.println(\"iv is not correctly filled.\");
                 System.exit(1);
             }
         }
     }
    /**
      * Checks if an array has all of the elements needed for encrypting or
      * decrypting
      * @param arr Integer[] with key or text
      * @param size the number of elements that should be in the array
      * @return true if all elements are present, false otherwise
      */
     private boolean arrayFilled(Integer[] arr, int size) {
         // Check if arr is null or the wrong size
         if (arr == null || arr.length != size)
             return false;
       
         // Check if any elements of arr are null
         for (Integer i: arr) {
             if (i == null)
                 return false;
         }
       
         return true;
     }
 }






