Monday, 22 August 2011

ROT13 Encryption

I thought that today I’d tackle the thorny issue of encryption starting with one of the most simple and weakest algorithms available: ROT13. ROT13 is a simple character shift or Caesar (or Caesar’s) cipher. In ROT13 each plain text character is replaced with one that’s 13 characters further along the alphabet. For example: ABC becomes NOP, whilst the plain text characters after M loop around back to the start of the alphabet; hence: MNO becomes ZAB whilst XYZ becomes KLM. Note that only upper and lower case letters in the English alphabet are encoded.

public class Rot {

 
private final int rot;

 
public Rot() {

   
this.rot = 13;
 
}

 
public Rot(int rot) {

   
this.rot = rot;
 
}

 
/**
   * Method that encodes the input string to the specified ROT value.
   */
 
public String encrypt(String stringToEncrypt) {

   
StringBuilder result = new StringBuilder();
   
if (stringToEncrypt != null) {
     
byte[] bytes = stringToEncrypt.getBytes(); // This is the input
     
encrypt(bytes, result);
   
}
   
return result.toString();
 
}

 
private void encrypt(byte[] bytes, StringBuilder result) {

   
for (byte b : bytes) {
     
String out = encrypt(b);
      result.append
(out);
   
}
  }

 
private String encrypt(byte b) {

   
String out;

   
// This only deals with A-Z or a-z characters
   
if (isUpperCaseCharacter(b)) {
     
out = encryptByte(b, 'A');
   
} else if (isLowerCaseCharacter(b)) {
     
out = encryptByte(b, 'a');
   
} else {
     
out = String.valueOf((char) b);
   
}
   
return out;
 
}

 
private boolean isUpperCaseCharacter(byte b) {

   
if ((b >= 'A') && (b <= 'Z')) {
     
return true;
   
}
   
return false;
 
}

 
private boolean isLowerCaseCharacter(byte b) {

   
if ((b >= 'a') && (b <= 'z')) {
     
return true;
   
}
   
return false;
 
}

 
/**
   * This is the bit that does the actual encoding
   */
 
private String encryptByte(int byteToEncode, int startLetter) {

   
int anotherByte = ((byteToEncode - startLetter + rot) % 26) + startLetter;

    String str = String.valueOf
((char) anotherByte);
   
return str;
 
}
}

The code above is an example of ROT encryption and although the default constructor encrypts using ROT13, the eagle eyed will have noticed a second constructor that allows you to choose other ROT or rotation value to produce other, more secure, encryption algorithms such as double ROT13 or ROT26. This code is easily tested with the following JUnit test:

public class RotTest {

 
private static final String[] testData = { "aha", "BALK", "barf", "Bin", "envy", "errs",
     
"fur", "gnat", "clerk", "ant", "BAR", "Be", "ebbs", "er", "flap", "gel", "irk",
     
"Purely", " []{} + !", "" };

 
private static final String[] expected = { "nun", "ONYX", "ones", "Ova", "rail", "reef",
     
"she", "tang", "pyrex", "nag", "ONE", "Or", "roof", "re", "sync", "try", "vex",
     
"Cheryl", " []{} + !", "" };

 
@Test
 
public void testEncryptAsRot13() {

   
Rot rot = new Rot();

   
for (int i = 0; i < testData.length; i++) {
     
System.out.println("ROT testing: " + testData[i] + "  Results: ");
      String out = rot.encrypt
(testData[i]);
      System.out.println
(out);
      assertEquals
(out, expected[i]);
   
}
  }

 
@Test
 
public void testEncryptAsRot26() {

   
Rot rot = new Rot(26); // Create the rotation jobby

   
for (int i = 0; i < testData.length; i++) {
     
String out = rot.encrypt(testData[i]);
      assertEquals
(out, testData[i]);
   
}
  }
}

2 comments:

Tony Weston said...

Hi Roger, I might be being a little picky and could well say this is considered out of scope.... however, try this test case! (copy and past from this comment)

input="abcđ" expected="nopđ"


Cheers,
Tony.

Roger Hughes said...

input="abcđ" expected="nopđ" - this seems fine to me as 'đ' (funny d with a little tail) is not in the ranges 'a-z' and 'A-Z'