/* * Enigma.java - ENIGMA implementation * * Copyright (c) 1998, Charles McManis, All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for NON-COMMERCIAL purposes and without * fee is hereby granted provided that this copyright notice * appears in all copies. * * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT * OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. */ import javacard.framework.*; /** * This class implements a 3 rotor Enigma encryption engine. When * installed in an iButton it gives the button a rudimentary encryption * capability. */ public class Enigma extends Applet { // The enigma "class" byte final static byte EN_CLA = (byte) 0x85; // Set the key. final static byte EN_INS_KEY = (byte) 0x01; // Reset the rotors to the key. final static byte EN_INS_RESET = (byte) 0x02; // Run a series of bytes through the rotors. final static byte EN_INS_ENCRYPT = (byte) 0x03; // APDU header stuff used during applet selection final static byte SELECT_CLA = 0x00; final static byte SELECT_INS = (byte) 0xA4; /* * These are the three rotors used in our simulated ENIGMA. * They are fixed values, generated by the GenRotors utility. */ static byte rotor1[] = { '8','E','7','d','V','6','c','x','q','h','A','5','r','F','u','Y', 'U','K','m','T','l','W','w','Z','n','Q','M','b','4','t','i','2', 'o','e','N','s','O','g','3','L','I','/','p','G','y','+','R','9', 'P','z','a','1','D','J','X','k','B','v','j','0','H','f','C','S' }; static byte rotor2[] = { 'w','9','p','i','K','N','4','/','j','7','3','r','M','D','u','2', 's','q','h','T','g','A','8','G','x','f','O','1','E','P','+','V', 'y','H','n','Z','Y','X','a','Q','m','0','S','k','e','5','b','B', '6','W','C','t','L','c','U','o','F','R','d','v','J','z','I','l' }; static byte rotor3[] = { 'p','b','5','Y','N','S','T','F','w','m','2','g','D','1','O','i', 'K','7','4','B','M','j','9','P','z','A','Z','X','h','t','I','+', 'U','v','L','/','W','s','e','y','n','0','l','k','q','6','G','c', 'E','C','r','H','8','f','o','u','d','Q','R','J','3','V','x','a' }; static byte char_map[] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' }; static byte reflector[] = { '2','3','f','a','5','6','s','H','7','y','b','w','+','9','h','W', 'l','Z','c','i','U','o','P','d','/','R','D','K','S','X','n','C', 'z','O','T','x','1','Q','m','e','V','4','0','r','G','8','v','u', 'L','j','J','g','q','k','A','B','p','E','F','I','t','N','M','Y' }; private byte key[] = new byte[3]; /* the rotor stack used by the Enigma */ private Rotor rotors; public Enigma() { // workaround for a JiB bug. Rotor.setMaps(char_map, reflector); rotors = new Rotor((short) 13, rotor1); rotors = new Rotor(rotors, (short) 7, rotor2); rotors = new Rotor(rotors, (short) 3, rotor3); register(); } /* Install the Enigma machine in the ring */ public static void install(APDU apdu) { new Enigma(); } /* Generic select, always accept */ public boolean select(APDU apdu) { return true; } byte test[]; private void error(APDU apdu, short err) { if (test == null) test = new byte[5]; test[0] = (byte) 'E'; test[1] = (byte) (((err >> 12) & 0xf) + '0'); if (test[1] > '9') test[1] += 7; test[2] = (byte) (((err >> 8) & 0xf) + '0'); if (test[2] > '9') test[2] += 7; test[3] = (byte) (((err >> 4) & 0xf) + '0'); if (test[3] > '9') test[3] += 7; test[4] = (byte) ((err & 0xf) + '0'); if (test[4] > '9') test[4] += 7; apdu.setOutgoing(); apdu.sendBytesLong(test, (short) 0, (short) test.length); } /* Dispatch a request from the host applet. */ public void process(APDU apdu) throws ISOException { byte buffer[] = apdu.getBuffer(); // process is called on applet selection but we don't do anything in this applet if ((buffer[ISO.OFFSET_CLA] == SELECT_CLA) && (buffer[ISO.OFFSET_INS] == SELECT_INS)) return; if (buffer[ISO.OFFSET_CLA] != EN_CLA) { error(apdu, (short)0x1000); return; } switch (buffer[ISO.OFFSET_INS]) { default: error(apdu, (short)0x1001); return; case EN_INS_KEY: setKey(apdu); break; case EN_INS_RESET: rotorReset(apdu); break; case EN_INS_ENCRYPT: encrypt(apdu); break; } } private void rotorReset(APDU apdu) { rotors.set(key); } private void setKey(APDU apdu) throws ISOException { byte b[] = apdu.getBuffer(); short bytesRead = apdu.setIncomingAndReceive(); if (b[ISO.OFFSET_LC] != 3) { error(apdu, (short) 0x2000); return; } for (short i = 0; i < 3; i++) { key[i] = b[ISO.OFFSET_CDATA+i]; } rotors.set(key); } private byte buf[]; void encrypt(APDU apdu) throws ISOException { byte b[] = apdu.getBuffer(); byte c; short bytesRead = apdu.setIncomingAndReceive(); short len; if ((buf == null) || (buf.length < bytesRead)) { buf = new byte[bytesRead]; } len = bytesRead; for (int i = 0; i < len; i++) { rotors.rotate(); buf[i] = rotors.encrypt(b[ISO.OFFSET_CDATA+i]); } apdu.setOutgoing(); apdu.sendBytesLong(buf, (short) 0, len); } }