diff --git a/ZonenTuereAppletHash/src/ms/warpzone/tuer/applet/TuereApplet.java b/ZonenTuereAppletHash/src/ms/warpzone/tuer/applet/TuereApplet.java index 78b633f3b7166ac0f89b0ab9eef6f468a949bcae..075db32e3e52762be9f3ec4b8e4c4b10f7b36018 100644 --- a/ZonenTuereAppletHash/src/ms/warpzone/tuer/applet/TuereApplet.java +++ b/ZonenTuereAppletHash/src/ms/warpzone/tuer/applet/TuereApplet.java @@ -1,226 +1,216 @@ -/** - * - */ -package ms.warpzone.tuer.applet; - -import javacard.framework.APDU; -import javacard.framework.ISO7816; -import javacard.framework.Applet; -import javacard.framework.ISOException; -import javacard.framework.JCSystem; -import javacard.framework.Util; -import javacard.security.MessageDigest; -import javacard.security.RandomData; - -/** - * @author warpzone - * - */ -public class TuereApplet extends Applet { - static final byte CURRENT_VERSION = 1; - static final byte CHAL_LEN = 32; - - // Warnung: Alignment beachten (Vielfaches von 64 Bytes) oder SHA-Code anpassen. - static final byte SECRET_LEN = 64; - MessageDigest md_sha = MessageDigest.getInstance(MessageDigest.ALG_SHA, false); - RandomData devurandom = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM); - - // Backup-Secret wird für türseitigen Fehler vorgehalten. secretBackup wird verwendet gdw. secretBackupActive==true - byte[] secret = new byte[SECRET_LEN]; - byte[] secretBackup = new byte[SECRET_LEN]; // Backupsecret für türseitigen Schlüssel-Aktualisierungsfehler. - boolean[] secretBackupActive = JCSystem.makeTransientBooleanArray((short)1, JCSystem.CLEAR_ON_DESELECT); - - // Hält das Challenge, das wir in 0x02 an die Tür gestellt haben. - byte[] challenge = JCSystem.makeTransientByteArray(CHAL_LEN, JCSystem.CLEAR_ON_DESELECT); - - public TuereApplet() { - for(byte i=0; i<SECRET_LEN; i++){ - secret[i] = 0x00; - secretBackup[i] = 0x00; - } - } - - public static void install(byte[] bArray, short bOffset, byte bLength) { - new TuereApplet().register(bArray, (short) (bOffset + 1), - bArray[bOffset]); - } - - public boolean select() { - secretBackupActive[0] = true; - return super.select(); - } - - public void process(APDU apdu) { - // Good practice: Return 9000 on SELECT - if (selectingApplet()) { - return; - } - - byte[] buf = apdu.getBuffer(); - short recv; - short responseApduStartOffset; - short hashSize; - - // Schlüssel alternieren bei Misserfolg. - byte[] currentSecret = secretBackupActive[0] ? secretBackup : secret; - - switch (buf[ISO7816.OFFSET_INS]) { - - /** - * Out = Versionsnummer dieses Applets - * #Out = 1 - */ - case (byte) 0x00: - buf[0] = CURRENT_VERSION; - apdu.setOutgoingAndSend((short)0, (short)1); - break; - - /** - * In = Neues Secret - * #In = 64 - * - * Die Karte wird mit dem neuen übergebenen Secret initialisiert. - * Dieser Vorgang wird genau einmal zugelassen. - */ - case (byte) 0x01: - for(byte i=0; i<SECRET_LEN; i++){ - if(secret[i] != 0x00){ - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - } - if(buf[ISO7816.OFFSET_LC] != SECRET_LEN){ - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - - recv = apdu.setIncomingAndReceive(); - - if(recv != SECRET_LEN){ - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - - // Voraussetzungen erfüllt. - JCSystem.beginTransaction(); - Util.arrayCopy(buf, ISO7816.OFFSET_CDATA, secret, (short)0, SECRET_LEN); - JCSystem.commitTransaction(); - break; - - /** - * In = Challenge für Auth der Karte - * #In = 32 - * Out = sha(secret+salt) + Challenge für Auth der Tür - * #Out = 20 32 = 52 - */ - case (byte) 0x02: - if(buf[ISO7816.OFFSET_LC] != CHAL_LEN){ - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - - recv = apdu.setIncomingAndReceive(); - - if(recv != CHAL_LEN){ - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - - short le = (short)((short)buf[ISO7816.OFFSET_CDATA + CHAL_LEN] & 0x00FF); - if(le < md_sha.getLength()){ - ISOException.throwIt(ISO7816.SW_WRONG_DATA); - } - - // Voraussetzungen erfüllt. - - // Schlüssel alternieren bei Misserfolg. - secretBackupActive[0] = !secretBackupActive[0]; - currentSecret = secretBackupActive[0] ? secretBackup : secret; - - md_sha.reset(); - md_sha.update(currentSecret, (short)0, SECRET_LEN); - responseApduStartOffset = ISO7816.OFFSET_CDATA + CHAL_LEN + 1; - - // Wir werden hier gleich Platz im APDU-Puffer brauchen. Ist der da? - if(buf.length - responseApduStartOffset < md_sha.getLength() + CHAL_LEN){ - ISOException.throwIt(ISO7816.SW_BYTES_REMAINING_00); - } - - hashSize = md_sha.doFinal(buf, ISO7816.OFFSET_CDATA, CHAL_LEN, buf, responseApduStartOffset); - - devurandom.generateData(challenge, (short)0, CHAL_LEN); - - Util.arrayCopyNonAtomic(challenge, (short)0, buf, (short)(responseApduStartOffset + hashSize), CHAL_LEN); - - apdu.setOutgoingAndSend(responseApduStartOffset, (short)(hashSize + CHAL_LEN)); - - break; - - /** - * In = Neues Secret gexored mit altem + Antwort auf Challenge aus 0x02 - * #In = 64 20 = 84 - * - * Bei Erfolg: secret := CDATA xor secret - */ - case (byte) 0x03: - if(buf[ISO7816.OFFSET_LC] != SECRET_LEN + md_sha.getLength()){ - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - - recv = apdu.setIncomingAndReceive(); - - if(recv != SECRET_LEN + md_sha.getLength()){ - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - - responseApduStartOffset = (short)(ISO7816.OFFSET_CDATA + SECRET_LEN + md_sha.getLength()); - - md_sha.reset(); - md_sha.update(currentSecret, (short)0, SECRET_LEN); - md_sha.doFinal(challenge, (short)0, CHAL_LEN, buf, responseApduStartOffset); - - if(Util.arrayCompare(buf, (short)(ISO7816.OFFSET_CDATA + SECRET_LEN), buf, responseApduStartOffset, md_sha.getLength()) != 0) { - ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED); - } - - //Response passt; Tür authentifiziert. - - for(byte i=0; i<SECRET_LEN; i++){ - buf[responseApduStartOffset+i] = (byte)(currentSecret[i] ^ buf[ISO7816.OFFSET_CDATA + i]); - } - - JCSystem.beginTransaction(); - Util.arrayCopy(currentSecret, (short)0, secretBackup, (short)0, SECRET_LEN); - Util.arrayCopy(buf, responseApduStartOffset, secret, (short)0, SECRET_LEN); - secretBackupActive[0] = true; - JCSystem.commitTransaction(); - - break; - - /** - * Keyausgabe für Debugzwecke. - * - * Out = Secret + Backup-Secret - */ - case (byte) 0xFE: - responseApduStartOffset = ISO7816.OFFSET_CDATA; - Util.arrayCopyNonAtomic(secret, (short)0, buf, responseApduStartOffset, SECRET_LEN); - Util.arrayCopyNonAtomic(secretBackup, (short)0, buf, (short)(responseApduStartOffset+SECRET_LEN), SECRET_LEN); - apdu.setOutgoingAndSend(responseApduStartOffset, (short)(2*SECRET_LEN)); - break; - - /** - * Secrets nullen für Debugzwecke. - */ - case (byte) 0xFF: - JCSystem.beginTransaction(); - for(byte i=0; i<SECRET_LEN; i++){ - secret[i] = 0x00; - secretBackup[i] = 0x00; - } - JCSystem.commitTransaction(); - break; - - /** - * Unbekannte Instruktion (INS). - */ - default: - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); - } - } +/** + * + */ +package ms.warpzone.tuer.applet; + +import javacard.framework.APDU; +import javacard.framework.ISO7816; +import javacard.framework.Applet; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.MessageDigest; +import javacard.security.RandomData; + +public class TuereApplet extends Applet { + static final byte CURRENT_VERSION = 1; + static final byte CHAL_LEN = 32; + + // Warnung: Alignment beachten (Vielfaches von 64 Bytes) oder SHA-Code anpassen. + static final byte SECRET_LEN = 64; + MessageDigest md_sha = MessageDigest.getInstance(MessageDigest.ALG_SHA, false); + RandomData devurandom = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM); + + static byte[] store = new byte[2*SECRET_LEN]; + + // Backup-Secret wird für türseitigen Fehler vorgehalten. secretBackup wird verwendet gdw. secretBackupActive==true + boolean[] secretBackupActive = JCSystem.makeTransientBooleanArray((short)1, JCSystem.CLEAR_ON_DESELECT); + + // Hält das Challenge, das wir in 0x02 an die Tür gestellt haben. + byte[] challenge = JCSystem.makeTransientByteArray(CHAL_LEN, JCSystem.CLEAR_ON_DESELECT); + + public TuereApplet() { + //Initialisierung erfolgt oben und im KeyStore. + } + + public static void install(byte[] bArray, short bOffset, byte bLength) { + new TuereApplet().register(bArray, (short) (bOffset + 1), + bArray[bOffset]); + } + + public boolean select() { + secretBackupActive[0] = true; + return super.select(); + } + + public void process(APDU apdu) { + // Good practice: Return 9000 on SELECT + if (selectingApplet()) { + return; + } + + byte[] buf = apdu.getBuffer(); + short recv; + short responseApduStartOffset; + short hashSize; + + // Schlüssel alternieren bei Misserfolg. +// byte[] currentSecret = secretBackupActive[0] ? secretBackup : secret; + short secretOffset = secretBackupActive[0] ? SECRET_LEN : 0; + + switch (buf[ISO7816.OFFSET_INS]) { + + /** + * Out = Versionsnummer dieses Applets + * #Out = 1 + */ + case (byte) 0x00: + buf[0] = CURRENT_VERSION; + apdu.setOutgoingAndSend((short)0, (short)1); + break; + + /** + * In = Neues Secret + * #In = 64 + * + * Die Karte wird mit dem neuen übergebenen Secret initialisiert. + * Dieser Vorgang wird genau einmal zugelassen. + */ + case (byte) 0x01: + for(short i=0; i<store.length; i++){ + if(store[i] != 0x00){ + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + } + if(buf[ISO7816.OFFSET_LC] != SECRET_LEN){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + + recv = apdu.setIncomingAndReceive(); + + if(recv != SECRET_LEN){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + + // Voraussetzungen erfüllt. + JCSystem.beginTransaction(); + Util.arrayCopy(buf, ISO7816.OFFSET_CDATA, store, (short)0, SECRET_LEN); + JCSystem.commitTransaction(); + break; + + /** + * In = Challenge für Auth der Karte + * #In = 32 + * Out = sha(secret+salt) + Challenge für Auth der Tür + * #Out = 20 32 = 52 + */ + case (byte) 0x02: + if(buf[ISO7816.OFFSET_LC] != CHAL_LEN){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + + recv = apdu.setIncomingAndReceive(); + + if(recv != CHAL_LEN){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + + short le = (short)((short)buf[ISO7816.OFFSET_CDATA + CHAL_LEN] & 0x00FF); + if(le < md_sha.getLength()){ + ISOException.throwIt(ISO7816.SW_WRONG_DATA); + } + + // Voraussetzungen erfüllt. + + // Schlüssel alternieren bei Misserfolg. + secretBackupActive[0] = !secretBackupActive[0]; + secretOffset = secretBackupActive[0] ? SECRET_LEN : 0; + + md_sha.reset(); + md_sha.update(store, (short)secretOffset, SECRET_LEN); + responseApduStartOffset = ISO7816.OFFSET_CDATA + CHAL_LEN + 1; + + // Wir werden hier gleich Platz im APDU-Puffer brauchen. Ist der da? + if(buf.length - responseApduStartOffset < md_sha.getLength() + CHAL_LEN){ + ISOException.throwIt(ISO7816.SW_BYTES_REMAINING_00); + } + + hashSize = md_sha.doFinal(buf, ISO7816.OFFSET_CDATA, CHAL_LEN, buf, responseApduStartOffset); + + devurandom.generateData(challenge, (short)0, CHAL_LEN); + + Util.arrayCopyNonAtomic(challenge, (short)0, buf, (short)(responseApduStartOffset + hashSize), CHAL_LEN); + + apdu.setOutgoingAndSend(responseApduStartOffset, (short)(hashSize + CHAL_LEN)); + + break; + + /** + * In = Neues Secret gexored mit altem + Antwort auf Challenge aus 0x02 + * #In = 64 20 = 84 + * + * Bei Erfolg: secret := CDATA xor secret + */ + case (byte) 0x03: + if(buf[ISO7816.OFFSET_LC] != SECRET_LEN + md_sha.getLength()){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + + recv = apdu.setIncomingAndReceive(); + + if(recv != SECRET_LEN + md_sha.getLength()){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + + responseApduStartOffset = (short)(ISO7816.OFFSET_CDATA + SECRET_LEN + md_sha.getLength()); + + md_sha.reset(); + md_sha.update(store, (short)secretOffset, SECRET_LEN); + md_sha.doFinal(challenge, (short)0, CHAL_LEN, buf, responseApduStartOffset); + + if(Util.arrayCompare(buf, (short)(ISO7816.OFFSET_CDATA + SECRET_LEN), buf, responseApduStartOffset, md_sha.getLength()) != 0) { + ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED); + } + + //Response passt; Tür authentifiziert. + + for(byte i=0; i<SECRET_LEN; i++){ + buf[responseApduStartOffset+i] = (byte)(store[i+secretOffset] ^ buf[ISO7816.OFFSET_CDATA + i]); + } + + JCSystem.beginTransaction(); + Util.arrayCopy(store, (short)secretOffset, store, secretOffset==SECRET_LEN ? 0 : SECRET_LEN, SECRET_LEN); + Util.arrayCopy(buf, responseApduStartOffset, store, (short)0, SECRET_LEN); //hier wird auf jeden Fall ins erste Secret kopiert. + secretBackupActive[0] = true; + JCSystem.commitTransaction(); + + break; + + /** + * Keyausgabe für Debugzwecke. + * + * Out = Secret + Backup-Secret + */ + case (byte) 0xFE: + responseApduStartOffset = ISO7816.OFFSET_CDATA; + Util.arrayCopyNonAtomic(store, (short)0, buf, responseApduStartOffset, (short)(2*SECRET_LEN)); + apdu.setOutgoingAndSend(responseApduStartOffset, (short)(2*SECRET_LEN)); + break; + + /** + * Secrets nullen für Debugzwecke. + */ + case (byte) 0xFF: + JCSystem.beginTransaction(); + Util.arrayFillNonAtomic(store, (short)0, (short)store.length, (byte)0); + JCSystem.commitTransaction(); + break; + + /** + * Unbekannte Instruktion (INS). + */ + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } + } } \ No newline at end of file