/* * GidsApplet: A Java Card implementation of the GIDS (Generic Identity * Device Specification) specification * https://msdn.microsoft.com/en-us/library/windows/hardware/dn642100%28v=vs.85%29.aspx * Copyright (C) 2016 Vincent Le Toux(vincent.letoux@mysmartlogon.com) * * It has been based on the IsoApplet * Copyright (C) 2014 Philip Wendland (wendlandphilip@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package com.mysmartlogon.gidsApplet; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.CryptoException; import javacard.security.KeyBuilder; import javacard.security.KeyPair; import javacard.security.RSAPrivateCrtKey; import javacard.security.RSAPublicKey; /** * \brief class used to store key file */ public class CRTKeyFile extends ElementaryFile { private final short posCRT; private final short lenCRT; private KeyPair keyPair = null; private byte[] symmetricKey = null; public CRTKeyFile(short fileID, byte[] fileControlInformation, short pos, short len) { super(fileID, fileControlInformation); posCRT = pos; lenCRT = len; } public static void CheckCRT(byte[] fcp, short pos, short len) throws InvalidArgumentsException { if (len < 11 || len > 127) { throw InvalidArgumentsException.getInstance(); } } void clearContents() { if (symmetricKey != null) { symmetricKey = null; } if (keyPair != null) { keyPair.getPrivate().clearKey(); keyPair = null; } if(JCSystem.isObjectDeletionSupported()) { JCSystem.requestObjectDeletion(); } } public void SaveKey(KeyPair kp) { clearContents(); keyPair = kp; } public KeyPair GetKey() { return keyPair; } public void CheckUsage(byte operation, byte algRef) throws NotFoundException { short innerPos = (short) (posCRT+2), pos = 0; short innerLen = 0, len = 0; boolean found = false; while( !found) { // Search the operation tag. If not found, then raise an error try { innerPos = UtilTLV.findTag(fcp, innerPos, lenCRT, operation); innerLen = UtilTLV.decodeLengthField(fcp, (short)(innerPos+1)); } catch (NotFoundException e) { throw NotFoundException.getInstance(); } catch (InvalidArgumentsException e) { throw NotFoundException.getInstance(); } try { pos = UtilTLV.findTag(fcp, (short) (innerPos+2), innerLen, (byte) 0x80); len = UtilTLV.decodeLengthField(fcp, (short)(pos+1)); if (len != 1) { throw InvalidArgumentsException.getInstance(); } byte ref = fcp[(short) (pos+2)]; if (algRef == ref) { found = true; break; } } catch (NotFoundException e) { // search next tag continue; } catch (InvalidArgumentsException e) { throw NotFoundException.getInstance(); } innerPos += 2; innerPos += innerLen; } if (!found) { throw NotFoundException.getInstance(); } } public void importKey(byte[] buffer, short offset, short length) throws InvalidArgumentsException { // focused on A5 tag short innerPos = 0, innerLen = 0; short pos = 0, len = 0; // keytype is missing for symetric key byte keytype = 1; byte keyref = 0; if (buffer[offset] != (byte) 0xA5) { throw InvalidArgumentsException.getInstance(); } innerPos = (short) (offset + 1 + UtilTLV.getLengthFieldLength(buffer, (short) (offset+1))); innerLen = UtilTLV.decodeLengthField(buffer, (short) (offset+1)); try { pos = UtilTLV.findTag(buffer, innerPos, innerLen, (byte) 0x83); len = UtilTLV.decodeLengthField(buffer, (short)(pos+1)); if (len != 1) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } keytype = buffer[(short) (pos+2)]; } catch (NotFoundException e) { // optional tag: default = symetric key } catch (InvalidArgumentsException e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } try { pos = UtilTLV.findTag(buffer, innerPos, innerLen, (byte) 0x84); len = UtilTLV.decodeLengthField(buffer, (short)(pos+1)); if (len != 1) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } // key ref used to encrypt the imported key keyref = buffer[(short) (pos+2)]; if (keyref != 0) { ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); } } catch (NotFoundException e) { // optional tag: default = none } catch (InvalidArgumentsException e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } try { pos = UtilTLV.findTag(buffer, innerPos, innerLen, (byte) 0x87); len = UtilTLV.decodeLengthField(buffer, (short)(pos+1)); pos += 1 + UtilTLV.getLengthFieldLength(buffer, (short)(pos+1)); if (keytype == 1) { importSymetricKey(buffer, pos, len); } else if (keytype == 2) { importRsaKey(buffer, pos, len); } else { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } catch (Exception e) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } private void importRsaKey(byte[] buffer, short offset, short length) throws InvalidArgumentsException { short pos = offset; short len = 0; RSAPrivateCrtKey rsaPrKey = null; RSAPublicKey rsaPuKey = null; if (buffer[pos] != 0x30) { throw InvalidArgumentsException.getInstance(); } len = UtilTLV.decodeLengthField(buffer, (short)(pos+1)); pos += 1 + UtilTLV.getLengthFieldLength(buffer, (short)(pos+1)); if (len > (short) (length +2)) { throw InvalidArgumentsException.getInstance(); } // version; len=1 ; value = 0 if (buffer[pos++] != 0x02) { throw InvalidArgumentsException.getInstance(); } if (buffer[pos++] != 0x01) { throw InvalidArgumentsException.getInstance(); } if (buffer[pos++] != 0x00) { throw InvalidArgumentsException.getInstance(); } // modulus if (buffer[pos] != 0x02) { throw InvalidArgumentsException.getInstance(); } len = UtilTLV.decodeLengthField(buffer, (short)(pos+1)); pos += 1 + UtilTLV.getLengthFieldLength(buffer, (short)(pos+1)); if ((len & 0x0F) == (byte) 1 && buffer[pos] == 0) { len -= 1; pos++; } short keysize = (short) (len * 8); try { rsaPrKey = (RSAPrivateCrtKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_CRT_PRIVATE, keysize, false); rsaPuKey = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, keysize, false); } catch(CryptoException e) { if(e.getReason() == CryptoException.NO_SUCH_ALGORITHM) { ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED); } ISOException.throwIt(ISO7816.SW_UNKNOWN); } rsaPuKey.setModulus(buffer, pos, len); pos += len; // public exponent if (buffer[pos] != 0x02) { throw InvalidArgumentsException.getInstance(); } len = UtilTLV.decodeLengthField(buffer, (short)(pos+1)); pos += 1 + UtilTLV.getLengthFieldLength(buffer, (short)(pos+1)); rsaPuKey.setExponent(buffer, pos, len); pos += len; // private exponent if (buffer[pos] != 0x02) { throw InvalidArgumentsException.getInstance(); } len = UtilTLV.decodeLengthField(buffer, (short)(pos+1)); pos += 1 + UtilTLV.getLengthFieldLength(buffer, (short)(pos+1)); pos += len; // P if (buffer[pos] != 0x02) { throw InvalidArgumentsException.getInstance(); } len = UtilTLV.decodeLengthField(buffer, (short)(pos+1)); pos += 1 + UtilTLV.getLengthFieldLength(buffer, (short)(pos+1)); // the minidriver may prepend a 00 before (len = len+1) and the javacard don't like it => remove the 00 if ((len & 0x0F) == (byte) 1 && buffer[pos] == 0) { len -= 1; pos++; } try { rsaPrKey.setP(buffer, pos, len); } catch(CryptoException e) { if(e.getReason() == CryptoException.ILLEGAL_VALUE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } ISOException.throwIt(ISO7816.SW_UNKNOWN); } pos += len; // Q if (buffer[pos] != 0x02) { throw InvalidArgumentsException.getInstance(); } len = UtilTLV.decodeLengthField(buffer, (short)(pos+1)); pos += 1 + UtilTLV.getLengthFieldLength(buffer, (short)(pos+1)); if ((len & 0x0F) == (byte) 1 && buffer[pos] == 0) { len -= 1; pos++; } rsaPrKey.setQ(buffer, pos, len); pos += len; // d mod p-1 if (buffer[pos] != 0x02) { throw InvalidArgumentsException.getInstance(); } len = UtilTLV.decodeLengthField(buffer, (short)(pos+1)); pos += 1 + UtilTLV.getLengthFieldLength(buffer, (short)(pos+1)); if ((len & 0x0F) == (byte) 1 && buffer[pos] == 0) { len -= 1; pos++; } rsaPrKey.setDP1(buffer, pos, len); pos += len; // d mod q-1 if (buffer[pos] != 0x02) { throw InvalidArgumentsException.getInstance(); } len = UtilTLV.decodeLengthField(buffer, (short)(pos+1)); pos += 1 + UtilTLV.getLengthFieldLength(buffer, (short)(pos+1)); if ((len & 0x0F) == (byte) 1 && buffer[pos] == 0) { len -= 1; pos++; } rsaPrKey.setDQ1(buffer, pos, len); pos += len; // q-1 mod p if (buffer[pos] != 0x02) { throw InvalidArgumentsException.getInstance(); } len = UtilTLV.decodeLengthField(buffer, (short)(pos+1)); pos += 1 + UtilTLV.getLengthFieldLength(buffer, (short)(pos+1)); if ((len & 0x0F) == (byte) 1 && buffer[pos] == 0) { len -= 1; pos++; } rsaPrKey.setPQ(buffer, pos, len); pos += len; if(rsaPrKey.isInitialized()) { // If the key is usable, it MUST NOT remain in buf. Util.arrayFillNonAtomic(buffer, offset, length, (byte)0x00); clearContents(); this.keyPair = new KeyPair(rsaPuKey, rsaPrKey); if(JCSystem.isObjectDeletionSupported()) { JCSystem.requestObjectDeletion(); } } else { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } private void importSymetricKey(byte[] buffer, short offset, short length) { clearContents(); byte[] key = new byte[length]; Util.arrayCopyNonAtomic(buffer, offset, key, (short) 0, length); this.symmetricKey = key; } public byte[] GetSymmectricKey() { return symmetricKey; } }