+ * This is used to interleave the generated private key and the application parameter into two AES-CBC blocks, + * as not doing so would result in the application parameter being encrypted as a block with an all zero IV which + * would always result in the same first block for all generated private keys with the same application parameter + * wrapped under the same wrapping key, which would break privacy of U2F. + * + * @param array1 + * @param array1Offset + * @param array2 + * @param array2Offset + * @param target + * @param targetOffset + * @param length + */ + private static void interleave32(byte[] array1, short array1Offset, byte[] array2, short array2Offset, byte[] target, short targetOffset, short[] permutation) { + for (short c = 0; c < 32; c++) { + short i = permutation[c]; + short a = (short) (array1[(short) (array1Offset + i)] & 0xff); + short b = (short) (array2[(short) (array2Offset + i)] & 0xff); + target[(short) (targetOffset + 2 * i)] = (byte) ((short) (a & 0xf0) | (short) (b >> 4)); + target[(short) (targetOffset + 2 * i + 1)] = (byte) ((short) ((a & 0x0f) << 4) | (short) (b & 0x0f)); + } + } + + /** + * Deinterleave a byte array back into two arrays of half size. + * Example: + * src = [0x1a, 0x2b, 0x3c, 0x4d] + * -> [0x12, 0x34] and [0xab, 0xcd] + * + * @param src + * @param srcOffset + * @param array1 + * @param array1Offset + * @param array2 + * @param array2Offset + * @param length + */ + private static void deinterleave32(byte[] src, short srcOffset, byte[] array1, short array1Offset, byte[] array2, short array2Offset, short[] permutation) { + for (short c = 0; c < 32; c++) { + short i = permutation[c]; + short a = (short) (src[(short) (srcOffset + 2 * i)] & 0xff); + short b = (short) (src[(short) (srcOffset + 2 * i + 1)] & 0xff); + array1[(short) (array1Offset + i)] = (byte) ((short) (a & 0xf0) | (short) (b >> 4)); + array2[(short) (array2Offset + i)] = (byte) (((short) (a & 0x0f) << 4) | (short) (b & 0x0f)); + } + } + + /* @override */ + public short generateKeyAndWrap(byte[] applicationParameter, short applicationParameterOffset, ECPrivateKey generatedPrivateKey, byte[] publicKey, short publicKeyOffset, byte[] keyHandle, short keyHandleOffset) { + // Generate a new pair + keyPair.genKeyPair(); + // Copy public key + ((ECPublicKey) keyPair.getPublic()).getW(publicKey, publicKeyOffset); + // Wrap keypair and application parameters + ((ECPrivateKey) keyPair.getPrivate()).getS(scratch, (short) 0); + + // generate random access table + for(short i = 0;i < 32;i++) { + shuffleAccess[i] = i; + } + randomShuffle32(shuffleAccess); + interleave32(applicationParameter, applicationParameterOffset, scratch, (short) 0, keyHandle, keyHandleOffset, shuffleAccess); + + // add dummy rounds for AES encryption + short doEncrypt = (short) (shuffleAccess[0] % 4); + for(short i = 0;i<4;i++) { + random.generateData(scratchRandom, (short) 0, (short) 32); + obfuscationKey.setKey(scratchRandom, (short) 0); + obfuscationCipherEncrypt.init(obfuscationKey, Cipher.MODE_ENCRYPT, IV_ZERO_AES, (short) 0, (short) IV_ZERO_AES.length); + + if(i == doEncrypt) { + cipherEncrypt.doFinal(keyHandle, keyHandleOffset, (short) 64, keyHandle, keyHandleOffset); + } else { + obfuscationCipherEncrypt.doFinal(scratchRandom, (short) 0, (short) 64, scratchRandom, (short) 0); + } + + } + Util.arrayFillNonAtomic(scratch, (short) 0, (short) 32, (byte) 0x00); + return (short) 64; + } + + /* @override */ + public boolean unwrap(byte[] keyHandle, short keyHandleOffset, short keyHandleLength, byte[] applicationParameter, short applicationParameterOffset, ECPrivateKey unwrappedPrivateKey) { + + // our random shuffled array will be used both for dummy decryption + // as well as randomly deinterleave the keyHandle + for(short i = 0;i < 32;i++) { + shuffleAccess[i] = i; + } + randomShuffle32(shuffleAccess); + + // add dummy rounds for AES decryption + short doDecrypt = (short) (shuffleAccess[0] % 4); + for(short i = 0;i<4;i++) { + random.generateData(scratchRandom, (short) 0, (short) 32); + obfuscationKey.setKey(scratchRandom, (short) 0); + obfuscationCipherDecrypt.init(obfuscationKey, Cipher.MODE_DECRYPT, IV_ZERO_AES, (short) 0, (short) IV_ZERO_AES.length); + if(i == doDecrypt) { + cipherDecrypt.doFinal(keyHandle, keyHandleOffset, (short) 64, keyHandle, keyHandleOffset); + } else { + obfuscationCipherDecrypt.doFinal(scratchRandom, (short) 0, (short) 64, scratchRandom, (short) 0); + } + + } + + // Verify + deinterleave32(keyHandle, keyHandleOffset, scratch, (short) 0, scratch, (short) 32, shuffleAccess); + if (!FIDOUtils.compareConstantTime(applicationParameter, applicationParameterOffset, scratch, (short) 0, (short) 32)) { + Util.arrayFillNonAtomic(scratch, (short) 32, (short) 32, (byte) 0x00); + Util.arrayFillNonAtomic(keyHandle, keyHandleOffset, (short) 64, (byte) 0x00); + return false; + } + Util.arrayFillNonAtomic(keyHandle, keyHandleOffset, (short) 64, (byte) 0x00); + if (unwrappedPrivateKey != null) { + unwrappedPrivateKey.setS(scratch, (short) 32, (short) 32); + } + Util.arrayFillNonAtomic(scratch, (short) 32, (short) 32, (byte) 0x00); + return true; + } + +} diff --git a/src/main/java/com/ledger/u2f/FIDOUtils.java b/src/de/asdfjkl/u2f/javacard/FIDOUtils.java similarity index 96% rename from src/main/java/com/ledger/u2f/FIDOUtils.java rename to src/de/asdfjkl/u2f/javacard/FIDOUtils.java index 7d58555..6806846 100644 --- a/src/main/java/com/ledger/u2f/FIDOUtils.java +++ b/src/de/asdfjkl/u2f/javacard/FIDOUtils.java @@ -2,6 +2,7 @@ ******************************************************************************* * FIDO U2F Authenticator * (c) 2015 Ledger + * (c) 2022 Dominik Klein * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +18,7 @@ ******************************************************************************* */ -package com.ledger.u2f; +package de.asdfjkl.u2f.javacard; /** * Utlity functions. diff --git a/src/main/java/com/ledger/u2f/Secp256r1.java b/src/de/asdfjkl/u2f/javacard/Secp256r1.java similarity index 96% rename from src/main/java/com/ledger/u2f/Secp256r1.java rename to src/de/asdfjkl/u2f/javacard/Secp256r1.java index 857b34a..b2876d8 100644 --- a/src/main/java/com/ledger/u2f/Secp256r1.java +++ b/src/de/asdfjkl/u2f/javacard/Secp256r1.java @@ -2,6 +2,7 @@ ******************************************************************************* * FIDO U2F Authenticator * (c) 2015 Ledger + * (c) 2022 Dominik Klein * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +18,7 @@ ******************************************************************************* */ -package com.ledger.u2f; +package de.asdfjkl.u2f.javacard; import javacard.security.ECKey; @@ -88,4 +89,8 @@ protected static boolean setCommonCurveParameters(ECKey key) { } } + + protected static void setCurveParamA(ECKey key) { + key.setA(SECP256R1_A, (short) 0, (short) SECP256R1_A.length); + } } diff --git a/src/main/java/com/ledger/u2f/U2FApplet.java b/src/de/asdfjkl/u2f/javacard/U2FApplet.java similarity index 95% rename from src/main/java/com/ledger/u2f/U2FApplet.java rename to src/de/asdfjkl/u2f/javacard/U2FApplet.java index acc1821..fecc741 100644 --- a/src/main/java/com/ledger/u2f/U2FApplet.java +++ b/src/de/asdfjkl/u2f/javacard/U2FApplet.java @@ -2,6 +2,7 @@ ******************************************************************************* * FIDO U2F Authenticator * (c) 2015 Ledger + * (c) 2022 Dominik Klein * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +18,7 @@ ******************************************************************************* */ -package com.ledger.u2f; +package de.asdfjkl.u2f.javacard; import javacard.framework.*; import javacard.security.CryptoException; @@ -40,6 +41,7 @@ public class U2FApplet extends Applet implements ExtendedLength { private ECPrivateKey attestationPrivateKey; private ECPrivateKey localPrivateKey; private boolean localPrivateTransient; + private boolean localPrivateTransient1; private boolean counterOverflowed; private Signature attestationSignature; private Signature localSignature; @@ -53,8 +55,8 @@ public class U2FApplet extends Applet implements ExtendedLength { private static final byte FIDO_INS_VERSION = (byte) 0x03; private static final byte ISO_INS_GET_DATA = (byte) 0xC0; - private static final byte PROPRIETARY_CLA = (byte) 0xF0; - private static final byte FIDO_ADM_SET_ATTESTATION_CERT = (byte) 0x01; + private static final byte PROPRIETARY_CLA = (byte) 0x80; + private static final byte FIDO_ADM_SET_ATTESTATION_CERT = (byte) 0x09; private static final byte SCRATCH_TRANSPORT_STATE = (byte) 0; private static final byte SCRATCH_CURRENT_OFFSET = (byte) 1; @@ -107,12 +109,23 @@ public class U2FApplet extends Applet implements ExtendedLength { * @param parametersLength always 35 */ public U2FApplet(byte[] parameters, short parametersOffset, byte parametersLength) { - if (parametersLength != 35) { + if (parametersLength != (35+32)) { ISOException.throwIt(ISO7816.SW_WRONG_DATA); } counter = new byte[4]; scratchPersistent = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_RESET); scratch = JCSystem.makeTransientByteArray((short) (SCRATCH_PAD + SCRATCH_PAD_SIZE), JCSystem.CLEAR_ON_DESELECT); + + localPrivateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false); + Secp256r1.setCommonCurveParameters(localPrivateKey); + + //localPrivateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE_TRANSIENT_DESELECT, KeyBuilder.LENGTH_EC_FP_256, false); + //localPrivateTransient = true; + + //localPrivateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE_TRANSIENT_RESET, KeyBuilder.LENGTH_EC_FP_256, false); + //localPrivateTransient = true; + + /* try { // ok, let's save RAM localPrivateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE_TRANSIENT_DESELECT, KeyBuilder.LENGTH_EC_FP_256, false); @@ -127,7 +140,7 @@ public U2FApplet(byte[] parameters, short parametersOffset, byte parametersLengt localPrivateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false); Secp256r1.setCommonCurveParameters(localPrivateKey); } - } + }*/ attestationSignature = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false); localSignature = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false); flags = parameters[parametersOffset]; @@ -136,7 +149,7 @@ public U2FApplet(byte[] parameters, short parametersOffset, byte parametersLengt Secp256r1.setCommonCurveParameters(attestationPrivateKey); attestationPrivateKey.setS(parameters, (short) (parametersOffset + 3), (short) 32); attestationSignature.init(attestationPrivateKey, Signature.MODE_SIGN); - fidoImpl = new FIDOStandalone(); + fidoImpl = new FIDOStandalone(parameters, (short) (parametersOffset + 35)); } /** @@ -454,4 +467,3 @@ public static void install(byte bArray[], short bOffset, byte bLength) throws IS new U2FApplet(bArray, (short) (offset + 1), bArray[offset]).register(bArray, (short) (bOffset + 1), bArray[bOffset]); } } - diff --git a/src/main/java/com/ledger/u2f/FIDOStandalone.java b/src/main/java/com/ledger/u2f/FIDOStandalone.java deleted file mode 100644 index 61d0806..0000000 --- a/src/main/java/com/ledger/u2f/FIDOStandalone.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - ******************************************************************************* - * FIDO U2F Authenticator - * (c) 2015 Ledger - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************* - */ - -package com.ledger.u2f; - -import javacard.framework.JCSystem; -import javacard.security.RandomData; -import javacard.framework.Util; -import javacard.security.*; -import javacardx.crypto.Cipher; - -public class FIDOStandalone implements FIDOAPI { - - private KeyPair keyPair; - private AESKey chipKey; - private Cipher cipherEncrypt; - private Cipher cipherDecrypt; - private RandomData random; - private byte[] scratch; - - private static final byte[] IV_ZERO_AES = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - - /** - * Init cipher engines and allocate memory. - */ - public FIDOStandalone() { - scratch = JCSystem.makeTransientByteArray((short) 64, JCSystem.CLEAR_ON_DESELECT); - keyPair = new KeyPair( - (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, KeyBuilder.LENGTH_EC_FP_256, false), - (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false)); - Secp256r1.setCommonCurveParameters((ECKey) keyPair.getPrivate()); - Secp256r1.setCommonCurveParameters((ECKey) keyPair.getPublic()); - random = RandomData.getInstance(RandomData.ALG_KEYGENERATION); - // Initialize the unique wrapping key - chipKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); - random.nextBytes(scratch, (short) 0, (short) 32); - chipKey.setKey(scratch, (short) 0); - cipherEncrypt = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); - cipherEncrypt.init(chipKey, Cipher.MODE_ENCRYPT, IV_ZERO_AES, (short) 0, (short) IV_ZERO_AES.length); - cipherDecrypt = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); - cipherDecrypt.init(chipKey, Cipher.MODE_DECRYPT, IV_ZERO_AES, (short) 0, (short) IV_ZERO_AES.length); - } - - /** - * Interleave two byte arrays into the target one, nibble by nibble. - * Example: - * array1 = [0x12, 0x34] - * array2 = [0xab, 0xcd] - * -> [0x1a, 0x2b, 0x3c, 0x4d] - *
- * This is used to interleave the generated private key and the application parameter into two AES-CBC blocks, - * as not doing so would result in the application parameter being encrypted as a block with an all zero IV which - * would always result in the same first block for all generated private keys with the same application parameter - * wrapped under the same wrapping key, which would break privacy of U2F. - * - * @param array1 - * @param array1Offset - * @param array2 - * @param array2Offset - * @param target - * @param targetOffset - * @param length - */ - private static void interleave(byte[] array1, short array1Offset, byte[] array2, short array2Offset, byte[] target, short targetOffset, short length) { - for (short i = 0; i < length; i++) { - short a = (short) (array1[(short) (array1Offset + i)] & 0xff); - short b = (short) (array2[(short) (array2Offset + i)] & 0xff); - target[(short) (targetOffset + 2 * i)] = (byte) ((short) (a & 0xf0) | (short) (b >> 4)); - target[(short) (targetOffset + 2 * i + 1)] = (byte) ((short) ((a & 0x0f) << 4) | (short) (b & 0x0f)); - } - } - - /** - * Deinterleave a byte array back into two arrays of half size. - * Example: - * src = [0x1a, 0x2b, 0x3c, 0x4d] - * -> [0x12, 0x34] and [0xab, 0xcd] - * - * @param src - * @param srcOffset - * @param array1 - * @param array1Offset - * @param array2 - * @param array2Offset - * @param length - */ - private static void deinterleave(byte[] src, short srcOffset, byte[] array1, short array1Offset, byte[] array2, short array2Offset, short length) { - for (short i = 0; i < length; i++) { - short a = (short) (src[(short) (srcOffset + 2 * i)] & 0xff); - short b = (short) (src[(short) (srcOffset + 2 * i + 1)] & 0xff); - array1[(short) (array1Offset + i)] = (byte) ((short) (a & 0xf0) | (short) (b >> 4)); - array2[(short) (array2Offset + i)] = (byte) (((short) (a & 0x0f) << 4) | (short) (b & 0x0f)); - } - } - - /* @override */ - public short generateKeyAndWrap(byte[] applicationParameter, short applicationParameterOffset, ECPrivateKey generatedPrivateKey, byte[] publicKey, short publicKeyOffset, byte[] keyHandle, short keyHandleOffset) { - // Generate a new pair - keyPair.genKeyPair(); - // Copy public key - ((ECPublicKey) keyPair.getPublic()).getW(publicKey, publicKeyOffset); - // Wrap keypair and application parameters - ((ECPrivateKey) keyPair.getPrivate()).getS(scratch, (short) 0); - interleave(applicationParameter, applicationParameterOffset, scratch, (short) 0, keyHandle, keyHandleOffset, (short) 32); - cipherEncrypt.doFinal(keyHandle, keyHandleOffset, (short) 64, keyHandle, keyHandleOffset); - Util.arrayFillNonAtomic(scratch, (short) 0, (short) 32, (byte) 0x00); - return (short) 64; - } - - /* @override */ - public boolean unwrap(byte[] keyHandle, short keyHandleOffset, short keyHandleLength, byte[] applicationParameter, short applicationParameterOffset, ECPrivateKey unwrappedPrivateKey) { - // Verify - cipherDecrypt.doFinal(keyHandle, keyHandleOffset, (short) 64, keyHandle, keyHandleOffset); - deinterleave(keyHandle, keyHandleOffset, scratch, (short) 0, scratch, (short) 32, (short) 32); - if (!FIDOUtils.compareConstantTime(applicationParameter, applicationParameterOffset, scratch, (short) 0, (short) 32)) { - Util.arrayFillNonAtomic(scratch, (short) 32, (short) 32, (byte) 0x00); - Util.arrayFillNonAtomic(keyHandle, keyHandleOffset, (short) 64, (byte) 0x00); - return false; - } - Util.arrayFillNonAtomic(keyHandle, keyHandleOffset, (short) 64, (byte) 0x00); - if (unwrappedPrivateKey != null) { - unwrappedPrivateKey.setS(scratch, (short) 32, (short) 32); - } - Util.arrayFillNonAtomic(scratch, (short) 32, (short) 32, (byte) 0x00); - return true; - } - -} diff --git a/state-model.png b/state-model.png deleted file mode 100644 index 6db5911..0000000 Binary files a/state-model.png and /dev/null differ diff --git a/tools/ant-javacard.jar b/tools/ant-javacard.jar new file mode 100644 index 0000000..739fd23 Binary files /dev/null and b/tools/ant-javacard.jar differ diff --git a/tools/gp.jar b/tools/gp.jar new file mode 100644 index 0000000..49267d7 Binary files /dev/null and b/tools/gp.jar differ diff --git a/tools/install_applet.bat b/tools/install_applet.bat new file mode 100644 index 0000000..bfca081 --- /dev/null +++ b/tools/install_applet.bat @@ -0,0 +1 @@ +java -jar gp.jar --reinstall ..\cap\u2f.cap -params 000140f3fccc0d00d8031954f90864d43c247f4bf5f0665c6b50cc17749a27d1cf7664 \ No newline at end of file diff --git a/tools/install_attestation.py b/tools/install_attestation.py new file mode 100644 index 0000000..27481c0 --- /dev/null +++ b/tools/install_attestation.py @@ -0,0 +1,51 @@ +from smartcard.Exceptions import NoCardException +from smartcard.System import readers +from smartcard.util import toHexString +import sys + +SEL_APPLET = [ 0x00,0xA4,0x04,0x00,0x08,0xA0,0x00,0x00,0x06,0x47,0x2F,0x00,0x01 ] +UPLOAD_1 = [ 0x80,0x09,0x00,0x00,0x80,0x30,0x82,0x01,0x3c,0x30,0x81,0xe4,0xa0,0x03,0x02,0x01,0x02,0x02,0x0a,0x47,0x90,0x12,0x80,0x00,0x11,0x55,0x95,0x73,0x52,0x30,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x30,0x17,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x03,0x13,0x0c,0x47,0x6e,0x75,0x62,0x62,0x79,0x20,0x50,0x69,0x6c,0x6f,0x74,0x30,0x1e,0x17,0x0d,0x31,0x32,0x30,0x38,0x31,0x34,0x31,0x38,0x32,0x39,0x33,0x32,0x5a,0x17,0x0d,0x31,0x33,0x30,0x38,0x31,0x34,0x31,0x38,0x32,0x39,0x33,0x32,0x5a,0x30,0x31,0x31,0x2f,0x30,0x2d,0x06,0x03,0x55,0x04,0x03,0x13,0x26,0x50,0x69,0x6c,0x6f,0x74,0x47,0x6e,0x75,0x62,0x62,0x79,0x2d,0x30,0x2e,0x34,0x2e,0x31,0x2d,0x34,0x37,0x39,0x30 ] +UPLOAD_2 = [ 0x80,0x09,0x00,0x80,0x80,0x31,0x32,0x38,0x30,0x30,0x30,0x31,0x31,0x35,0x35,0x39,0x35,0x37,0x33,0x35,0x32,0x30,0x59,0x30,0x13,0x06,0x07,0x2a,0x86,0x48,0xce,0x3d,0x02,0x01,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x03,0x01,0x07,0x03,0x42,0x00,0x04,0x8d,0x61,0x7e,0x65,0xc9,0x50,0x8e,0x64,0xbc,0xc5,0x67,0x3a,0xc8,0x2a,0x67,0x99,0xda,0x3c,0x14,0x46,0x68,0x2c,0x25,0x8c,0x46,0x3f,0xff,0xdf,0x58,0xdf,0xd2,0xfa,0x3e,0x6c,0x37,0x8b,0x53,0xd7,0x95,0xc4,0xa4,0xdf,0xfb,0x41,0x99,0xed,0xd7,0x86,0x2f,0x23,0xab,0xaf,0x02,0x03,0xb4,0xb8,0x91,0x1b,0xa0,0x56,0x99,0x94,0xe1,0x01,0x30,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x03,0x47,0x00,0x30,0x44,0x02,0x20,0x60,0xcd ] +UPLOAD_3 = [ 0x80,0x09,0x01,0x00,0x40,0xb6,0x06,0x1e,0x9c,0x22,0x26,0x2d,0x1a,0xac,0x1d,0x96,0xd8,0xc7,0x08,0x29,0xb2,0x36,0x65,0x31,0xdd,0xa2,0x68,0x83,0x2c,0xb8,0x36,0xbc,0xd3,0x0d,0xfa,0x02,0x20,0x63,0x1b,0x14,0x59,0xf0,0x9e,0x63,0x30,0x05,0x57,0x22,0xc8,0xd8,0x9b,0x7f,0x48,0x88,0x3b,0x90,0x89,0xb8,0x8d,0x60,0xd1,0xd9,0x79,0x59,0x02,0xb3,0x04,0x10,0xdf ] + +def print_response(response, sw1, sw2): + response_string = toHexString(response) + if(len(response) > 0): + print(response_string + ", sw1, sw2: "+ hex(sw1) + "," + hex(sw2)) + else: + print("sw1, sw2: " + hex(sw1) + "," + hex(sw2)) + + +for reader in readers(): + try: + connection = reader.createConnection() + connection.connect() + print(str(reader) + toHexString(connection.getATR())) + + apdu = SEL_APPLET + print("selecting applet...") + response, sw1, sw2 = connection.transmit(apdu) + print_response(response, sw1, sw2) + + apdu = UPLOAD_1 + print("upload 1...") + response, sw1, sw2 = connection.transmit(apdu) + print_response(response, sw1, sw2) + + apdu = UPLOAD_2 + print("upload 2...") + response, sw1, sw2 = connection.transmit(apdu) + print_response(response, sw1, sw2) + + apdu = UPLOAD_3 + print("upload 3...") + response, sw1, sw2 = connection.transmit(apdu) + print_response(response, sw1, sw2) + + + except NoCardException: + print(reader, 'no card inserted') + +if 'win32' == sys.platform: + print('press Enter to continue') + sys.stdin.read(1) \ No newline at end of file