/** * This method encodes a PrivateRsaKeyAttribute. The value of the member must have been previously * set, otherwise it will return null. * * @return Byte array containing the encoding */ public byte[] encode() { encoding = null; if ((this.value == null) || (this.modulusLength == null)) return null; byte[] ctxValueEnc = encodeContextSpecificExplicit(value.encode(), (byte) 0x00); byte[] modLenEnc = modulusLength.encode(); if ((ctxValueEnc == null) || (modLenEnc == null)) return null; short length = (short) (ctxValueEnc.length + modLenEnc.length); lengthEncoded = encodeLength((short) length); encoding = new byte[(short) (1 + lengthEncoded.length + length)]; encoding[0] = this.TAG; short offset = 1; Util.arrayCopy(lengthEncoded, (short) 0, encoding, offset, (short) lengthEncoded.length); offset += lengthEncoded.length; Util.arrayCopy(ctxValueEnc, (short) 0, encoding, offset, (short) ctxValueEnc.length); offset += ctxValueEnc.length; Util.arrayCopy(modLenEnc, (short) 0, encoding, offset, (short) modLenEnc.length); return encoding; }
/* input data is supposed to have been validaded before */ public static short GetBERTLVDataLen(byte[] buf, short offset, short len) { short size = 0; short sizeforsize = 0; short i = 1; if ((buf[offset] & 0x1F) != 0x1F) { // simplified tag } else { // tag start with all 5 last bits to 1 // skip the tag while (((buf[(short) (offset + i)] & 0x80) != 0) && ((short) (i + offset)) < len) { i++; } // pass the last byte of the tag i += 1; } // check the size if ((buf[(short) (offset + i)] & 0x80) != 0) { // size encoded in many bytes sizeforsize = (short) (buf[(short) (offset + i)] & 0x7F); if (sizeforsize > 2) { // more than two bytes for encoding => not something than we can handle return 0; } else if (sizeforsize == 1) { size = Util.makeShort((byte) 0, buf[(short) (offset + i + 1)]); } else if (sizeforsize == 2) { size = Util.getShort(buf, (short) (offset + i + 1)); } } else { // size encode in one byte size = Util.makeShort((byte) 0, buf[(short) (offset + i)]); } return size; }
/** Process the WRITE BINARY Instruction (0xD0). ISO7816-4 Section 7.2.4 */ private void processWriteBinary(APDU apdu) throws ISOException { if (state != STATE_INITIAL) { ISOException.throwIt(SW_INS_NOT_SUPPORTED); } byte[] buf = apdu.getBuffer(); byte p1 = buf[OFFSET_P1]; byte p2 = buf[OFFSET_P2]; short offset = 0; short ef = -1; if ((byte) (p1 & MASK_SFI) == MASK_SFI) { byte sfi = (byte) (p1 | ~MASK_SFI); if (sfi >= 0x1F) { ISOException.throwIt(SW_INCORRECT_P1P2); } ef = fileSystem.findCurrentSFI(sfi); if (ef == -1) { ISOException.throwIt(SW_FILE_NOT_FOUND); } ef = fileSystem.fileStructure[ef]; offset = unsigned(p2); } else { ef = fileSystem.getCurrentIndex(); if (fileSystem.getFile(ef) == null) { ISOException.throwIt(SW_COMMAND_NOT_ALLOWED); } offset = Util.makeShort(p1, p2); } byte[] file = fileSystem.getFile(ef); short lc = unsigned(buf[OFFSET_LC]); if ((short) (offset + lc) > file.length) { ISOException.throwIt(SW_WRONG_LENGTH); } apdu.setIncomingAndReceive(); Util.arrayCopyNonAtomic(buf, OFFSET_CDATA, file, offset, lc); }
// ------------------------------------------------ public final void diversify(byte[] MxK, byte[] factor, byte[] DxK) { Util.arrayCopyNonAtomic(factor, (short) 0, tbuf2, (short) 0, (short) 8); tripledes(MxK, tbuf2, (short) 0, (short) 8, tbuf1, (short) 0, Cipher.MODE_ENCRYPT); Util.arrayCopy(tbuf1, (short) 0, DxK, (short) 0, (short) 8); notblock8(tbuf2); tripledes(MxK, tbuf2, (short) 0, (short) 8, tbuf1, (short) 0, Cipher.MODE_ENCRYPT); Util.arrayCopyNonAtomic(tbuf1, (short) 0, DxK, (short) 8, (short) 8); }
/* returns 0 or the size of the BERTLV structure */ public static short CheckBERTLV(byte[] buf, short offset, short len) { short size = 0; short sizeforsize = 0; short i = 1; short totalsize = 0; if ((buf[offset] & 0x1F) != 0x1F) { // simplified tag } else { // tag start with all 5 last bits to 1 // skip the tag while (((buf[(short) (offset + i)] & 0x80) != 0) && i < len) { i++; } // pass the last byte of the tag i += 1; } if ((short) (i + 1) > len) { return 0; } // check the size if ((buf[(short) (offset + i)] & 0x80) != 0) { // size encoded in many bytes sizeforsize = (short) (buf[(short) (offset + i)] & 0x7F); if ((short) (i + 1 + sizeforsize) > len) { return 0; } if (sizeforsize > (short) 2) { // more than two bytes for encoding => not something than we can handle return 0; } else if (sizeforsize == (short) 1) { if ((short) (offset + i + 1 + sizeforsize) > len) { return 0; } size = Util.makeShort((byte) 0, buf[(short) (offset + i + 1)]); } else if (sizeforsize == 2) { totalsize = (short) (i + 1 + sizeforsize + size); if ((short) (offset + i + 1 + sizeforsize) > len) { return (short) 0; } size = Util.getShort(buf, (short) (offset + i + 1)); } } else { // size encode in one byte size = Util.makeShort((byte) 0, buf[(short) (offset + i)]); } totalsize = (short) (i + 1 + sizeforsize + size); if (totalsize < (short) 240 && (short) (offset + totalsize) > len) { return (short) 0; } return totalsize; }
/** * Searches for an index to the file specified by the id in the file structure starting from * position start. * * @param fileStructureArray the array with the file structure * @param shift the shift in the input array (e.g. when the array is the APDU with the header * bytes) * @param start starting position to search * @param lastOffset the last valid offset in the input array * @param id the id of the file that is searched * @return the index of the file, if found * @throws ArrayIndexOutOfBoundsException when start and lastOffset point outside of the input * array * @throws FileNotFoundException when file not found */ short searchId(byte[] fileStructureArray, short shift, short start, short lastOffset, short id) throws ArrayIndexOutOfBoundsException, FileNotFoundException { if (start < 0 || start > (short) (lastOffset - 5)) { // This sould produce ArrayIndexOutOfBoundsException fileStructureArray[fileStructureArray.length] = (byte) 0xFF; } short fid = Util.getShort(fileStructureArray, (short) (start + 1)); if (fid == id) { return start; } if (fileStructureArray[start] != DIR) { throw fnfe; } else { short childNum = fileStructureArray[(short) (start + 4)]; if (start > (short) ((short) (lastOffset - 5) - childNum)) { fileStructureArray[fileStructureArray.length] = (byte) 0xFF; } for (short i = 0; i < childNum; i++) { try { return searchId( fileStructureArray, shift, (short) (fileStructureArray[(short) (start + (short) (5 + i))] + shift), lastOffset, id); } catch (FileNotFoundException e) { } } } throw fnfe; }
void writeData(short fid, short file_offset, byte[] data, short data_offset, short length) { byte[] file = getFile(fid); short fileSize = getFileSize(fid); if (file == null) { ISOException.throwIt(ISO7816.SW_FILE_NOT_FOUND); } if (fileSize < (short) (file_offset + length)) ISOException.throwIt(ISO7816.SW_FILE_FULL); Util.arrayCopyNonAtomic(data, data_offset, file, file_offset, length); // Extract the pointers to where the Root and Alternate root CVCA certificate // identifiers are stored. Properly this should be done with a BERTLVScanner or // similar. if (fid == EF_COM_FID) { short cvcaRootIndex = -1; short cvcaAltIndex = -1; for (short i = 0; i < length; i++) { if (data[i] == 0x04 && data[(short) (i + 1)] == 0x11) { if (cvcaRootIndex == -1) { cvcaRootIndex = (short) ((short) (file_offset + i) + 2); } else { cvcaAltIndex = (short) ((short) (file_offset + i) + 2); } } } SmartIDApplet.certificate.setCOMFileData(file, cvcaRootIndex, cvcaAltIndex); } }
/** Process the INTERNAL AUTHENTICATE instruction (0x88) ISO 7816-4 Section 7.5.2 */ private void processInternalAuthenticate(APDU apdu) { if (state != STATE_PERSONALISED) { ISOException.throwIt(SW_INS_NOT_SUPPORTED); } if (!pin.isValidated()) { ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED); } byte[] buf = apdu.getBuffer(); short lc = unsigned(buf[OFFSET_LC]); if (lc == 0) { ISOException.throwIt(SW_WRONG_LENGTH); } apdu.setIncomingAndReceive(); RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) currentPrivateKey[0]; byte alg = tmp[TMP_SIGNALG_OFFSET]; if (privateKey != authKeyPrivate || alg != ALG_AUTH_DEC_RSA) { ISOException.throwIt(SW_WRONG_DATA); } short offset = OFFSET_CDATA; short maxLength = (short) ((short) (privateKey.getSize() / 8) - 11); if (lc > maxLength) { ISOException.throwIt(SW_WRONG_LENGTH); } pkcs1Cipher.init(privateKey, Cipher.MODE_ENCRYPT); short len = pkcs1Cipher.doFinal(buf, offset, lc, tmp, TMP_OFFSET); Util.arrayCopyNonAtomic(tmp, TMP_OFFSET, buf, (short) 0, len); apdu.setOutgoingAndSend((short) 0, len); }
/** * This method decodes a Private RSA key Attribute with encoding given as parameter * * @param enc Byte array containing the encoding of the Private RSA Key Attribute * @param offset offset in the byte array from where the encoding starts * @param len length of the encoding */ public void decode(byte[] enc, short offset, short len) { encoding = new byte[len]; Util.arrayCopy(enc, offset, encoding, (short) 0, (short) len); decode(); }
/** * Pads the input according to the RSASSA-PSS algorithm, the result is placed in output. The input * should be 20-byte SHA1 hash of the message to be signed. This method *does not* do signing * (encrypting) itself. Due to the randomness of this algorithm the subsequent signing may fail * (when the result of this method is larger than the key modulus) in which case the padding * should be attempted again. */ private void pssPad( byte[] input, short inOffset, short hashLen, byte[] output, short outputOffset, short emLen, byte firstKeyByte) throws CryptoException { do { short hLen = hashLen; short outOffset = outputOffset; if (hLen != SHA1_LEN || (short) (inOffset + hLen) > input.length || (short) (outOffset + emLen) > output.length) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } short sLen = SHA1_LEN; short psLen = (short) (emLen - sLen - hLen - 2); Util.arrayFillNonAtomic(output, outOffset, emLen, (byte) 0x00); md.update(output, outOffset, (short) 8); md.update(input, inOffset, hLen); rd.generateData(output, (short) (outOffset + psLen + 1), sLen); md.doFinal(output, (short) (outOffset + psLen + 1), sLen, tmp, TMP_HASH_OFFSET); output[(short) (outOffset + psLen)] = (byte) 0x01; Util.arrayFillNonAtomic(output, outOffset, psLen, (byte) 0x00); short hOffset = (short) (outOffset + emLen - hLen - 1); Util.arrayCopyNonAtomic(tmp, TMP_HASH_OFFSET, output, hOffset, hLen); output[(short) (outOffset + emLen - 1)] = (byte) 0xbc; tmp[(short) (TMP_C_OFFSET + C_LEN - 1)] = 0; while (outOffset < hOffset) { md.update(output, hOffset, hLen); md.doFinal(tmp, TMP_C_OFFSET, C_LEN, tmp, TMP_HASH_OFFSET); if ((short) (outOffset + hLen) > hOffset) { hLen = (short) (hOffset - outOffset); } for (short i = 0; i < hLen; i++) { output[outOffset++] ^= tmp[(short) (TMP_HASH_OFFSET + i)]; } tmp[(short) (TMP_C_OFFSET + C_LEN - 1)]++; } } while (firstKeyByte <= tmp[TMP_OFFSET]); }
// ------------------------------------------------ public final short PBDecrypt( byte alg, byte[] key, byte[] data, short doff, short len, byte[] res) { if (alg == ALG_3DES) tripledes(key, data, doff, len, res, (short) 0, Cipher.MODE_DECRYPT); else cdes(key, (short) 0, data, doff, len, res, (short) 0, Cipher.MODE_DECRYPT); len = res[0]; Util.arrayCopyNonAtomic(res, (short) 1, res, (short) 0, len); return len; }
// ------------------------------------------------ public final void gmac4(byte alg, byte[] key, byte[] data, short dl, byte[] mac) { dl = pbocpadding(data, dl); Util.arrayFillNonAtomic(tbuf1, (short) 0, (short) 8, (byte) 0); for (short i = (short) 0; i < dl; i += (short) 8) { xorblock8(tbuf1, data, i); cdes(key, (short) 0, tbuf1, (short) 0, (short) 8, tbuf2, (short) 0, Cipher.MODE_ENCRYPT); Util.arrayCopyNonAtomic(tbuf2, (short) 0, tbuf1, (short) 0, (short) 8); } if (alg == ALG_3DES) { cdes(key, (short) 8, tbuf1, (short) 0, (short) 8, tbuf2, (short) 0, Cipher.MODE_DECRYPT); cdes(key, (short) 0, tbuf2, (short) 0, (short) 8, tbuf1, (short) 0, Cipher.MODE_ENCRYPT); } Util.arrayCopyNonAtomic(tbuf1, (short) 0, mac, (short) 0, (short) 4); }
private void processCreateFile(APDU apdu) { if (state != STATE_INITIAL) { ISOException.throwIt(SW_INS_NOT_SUPPORTED); } byte[] buf = apdu.getBuffer(); short lc = unsigned(buf[OFFSET_LC]); apdu.setIncomingAndReceive(); if (lc != 5) { ISOException.throwIt(SW_WRONG_LENGTH); } short offset = OFFSET_CDATA; short id = Util.getShort(buf, offset); offset += 2; short len = Util.getShort(buf, offset); offset += 2; byte perm = buf[offset]; if (!fileSystem.createFile(id, len, perm)) { ISOException.throwIt(SW_WRONG_DATA); } }
// ------------------------------------------------ public final short pbocpadding(byte[] data, short len) { short f; data[len] = (byte) 0x80; len++; f = (short) (len % 8); f = (short) (8 - f); if (f != (short) 8) Util.arrayFillNonAtomic(data, len, f, (byte) 0); else f = (short) 0; return (short) (len + f); }
/** * Process the READ BINARY instruction (0xB0) ISO7816-4 Section 7.2.3 * * <p>We handle only the INS == 0xB0 case. */ private void processReadBinary(APDU apdu) { if (state != STATE_PERSONALISED) { ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); } byte[] buf = apdu.getBuffer(); byte p1 = buf[OFFSET_P1]; byte p2 = buf[OFFSET_P2]; short offset = 0; short ef = -1; if ((byte) (p1 & MASK_SFI) == MASK_SFI) { byte sfi = (byte) (p1 & ~MASK_SFI); if (sfi >= 0x1F) { ISOException.throwIt(SW_INCORRECT_P1P2); } ef = fileSystem.findCurrentSFI(sfi); if (ef == -1) { ISOException.throwIt(SW_FILE_NOT_FOUND); } ef = fileSystem.fileStructure[ef]; offset = unsigned(p2); } else { ef = fileSystem.getCurrentIndex(); if (fileSystem.getFile(ef) == null) { ISOException.throwIt(SW_COMMAND_NOT_ALLOWED); } offset = Util.makeShort(p1, p2); } byte[] file = fileSystem.getFile(ef); if (offset > file.length) { ISOException.throwIt(SW_INCORRECT_P1P2); } if (fileSystem.getPerm(ef) == FileSystem.PERM_PIN && !pin.isValidated()) { ISOException.throwIt(SW_SECURITY_STATUS_NOT_SATISFIED); } short le = apdu.setOutgoing(); if (le == 0 || le == 256) { le = (short) (file.length - offset); if (le > 256) le = 256; } boolean eof = false; if ((short) (file.length - offset) < le) { le = (short) (file.length - offset); eof = true; } apdu.setOutgoingLength(le); apdu.sendBytesLong(file, offset, le); if (eof) { ISOException.throwIt(SW_END_OF_FILE); } }
private short findEntryPath(short start, byte[] path, short offset, short length) throws FileNotFoundException { try { if (length == 0) { return start; } short id = Util.makeShort(path[offset], path[(short) (offset + 1)]); start = findEntryRelative(start, id); offset += 2; length = (short) (length - 2); return findEntryPath(start, path, offset, length); } catch (ArrayIndexOutOfBoundsException aioobe) { throw fnfe; } }
// ------------------------------------------------ public final short PBEncrypt(byte alg, byte[] key, byte[] data, short len, byte[] res) { Util.arrayCopyNonAtomic(data, (short) 0, res, (short) 1, len); res[0] = (byte) len; if (((short) (len + 1) % (short) 8) > (short) 0) len = pbocpadding(res, (short) (len + 1)); else len = (short) (len + 1); if (alg == ALG_3DES) { tripledes(key, data, (short) 0, len, res, (short) 0, Cipher.MODE_ENCRYPT); } else { cdes(key, (short) 0, data, (short) 0, len, res, (short) 0, Cipher.MODE_ENCRYPT); } return len; }
/** * \brief Decode the length field of a TLV-entry. * * <p>The length field itself can be 1, 2 or 3 bytes long: - If the length is between 0 and 127, * it is 1 byte long. - If the length is between 128 and 255, it is 2 bytes long. The first byte * is 0x81 to indicate this. - If the length is between 256 and 65535, it is 3 bytes long. The * first byte is 0x82, the following 2 contain the actual length. Note: Only lengths up to 0x7FFF * (32767) are supported here, because a short in Java is signed. * * <p>\param buf The buffer containing the length field. * * <p>\param offset The offset at where the length field starts. * * <p>\param length The length of the buffer (buf). This is to prevent that the index gets out of * bounds. * * <p>\return The (positive) length encoded by the length field, or in case of an error, -1. * * <p>\throw InvalidArgumentsException If offset is too big for a signed Java short If the first * byte of the length field is invalid */ public static short decodeLengthField(byte[] buf, short offset) throws InvalidArgumentsException { if (buf[offset] == (byte) 0x82) { // 256..65535 // Check for short overflow // (In Java, a short is signed: positive values are 0000..7FFF) if (buf[(short) (offset + 1)] < 0) { // 80..FF throw InvalidArgumentsException.getInstance(); } return Util.getShort(buf, (short) (offset + 1)); } else if (buf[offset] == (byte) 0x81) { return (short) (0x00FF & buf[(short) (offset + 1)]); } else if (buf[offset] > 0) { // 00..7F return (short) (0x007F & buf[offset]); } else { throw InvalidArgumentsException.getInstance(); } }
/** * Process the CHANGE REFERENCE DATA instruction (0x24) ISO7816-4 Section 7.5.7 * * <p>We have two options here: (a) in a procudction state we can set the PUC with this, (b) in * the distribution state and operational state we change the PIN */ private void processChangeReferenceData(APDU apdu) { byte[] buf = apdu.getBuffer(); short lc = unsigned(buf[OFFSET_LC]); byte p1 = buf[OFFSET_P1]; byte p2 = buf[OFFSET_P2]; if (state > STATE_INITIAL) { // We are changing the PIN, PUC has to be provided // check that P1 is 0x00: verification data (puc) followed by new reference data (pin) if (p1 != 0x00 || p2 != (byte) 0x00) { ISOException.throwIt(SW_INCORRECT_P1P2); } short pinSize = (short) (lc - PUC_SIZE); if (pinSize < MIN_PIN_SIZE || pinSize > MAX_PIN_SIZE) { ISOException.throwIt(SW_WRONG_LENGTH); } apdu.setIncomingAndReceive(); short offset = (short) (OFFSET_CDATA + PUC_SIZE); for (short i = 0; i < pinSize; i++) { byte b = buf[(short) (offset + i)]; if (b < (byte) 0x30 || b > (byte) 0x39) { ISOException.throwIt(SW_WRONG_DATA); } } // Pad the pin with 0x00 to overwrite any garbage, e.g. le Util.arrayFillNonAtomic( buf, (short) (offset + pinSize), (short) (MAX_PIN_SIZE - pinSize), (byte) 0x00); if (!puc.check(buf, OFFSET_CDATA, PUC_SIZE)) { ISOException.throwIt((short) (SW_PIN_INCORRECT_TRIES_LEFT | puc.getTriesRemaining())); } pin.update(buf, offset, MAX_PIN_SIZE); pin.resetAndUnblock(); if (state == STATE_PREPERSONALISED) { state = STATE_PERSONALISED; } } else { // State is production, we set the puc if (p1 != 0x01 || p2 != 0x00) { ISOException.throwIt(SW_INCORRECT_P1P2); } if (lc != PUC_SIZE) { ISOException.throwIt(SW_WRONG_LENGTH); } apdu.setIncomingAndReceive(); puc.update(buf, OFFSET_CDATA, (byte) lc); puc.resetAndUnblock(); } }
/** Process the PSO COMPUTE DIGITAL SIGNATURE instruction (0x2A) ISO 7816-8 Section 5.4 */ private void processComputeDigitalSignature(APDU apdu) { pin.reset(); byte[] buf = apdu.getBuffer(); short lc = unsigned(buf[OFFSET_LC]); if (lc == 0) { ISOException.throwIt(SW_WRONG_LENGTH); } apdu.setIncomingAndReceive(); RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) currentPrivateKey[0]; byte alg = tmp[TMP_SIGNALG_OFFSET]; if (privateKey != signKeyPrivate || (alg != ALG_SIGN_RSA_PKCS1_SHA1 && alg != ALG_SIGN_RSA_PKCS1_SHA256 && alg != ALG_SIGN_RSA_PSS && alg != ALG_SIGN_RSA_PKCS1_SHA1MD5)) { ISOException.throwIt(SW_WRONG_DATA); } short offset = OFFSET_CDATA; short expectedLength = 0; if (alg == ALG_SIGN_RSA_PKCS1_SHA256) { expectedLength = (short) (SHA256_LEN + 17); } else if (alg == ALG_SIGN_RSA_PKCS1_SHA1) { expectedLength = (short) (SHA1_LEN + 13); } else if (alg == ALG_SIGN_RSA_PSS) { expectedLength = SHA1_LEN; } else if (alg == ALG_SIGN_RSA_PKCS1_SHA1MD5) { expectedLength = SHA1MD5_LEN; } if (lc != expectedLength) { ISOException.throwIt(SW_WRONG_LENGTH); } short sigLen = 0; if (alg == ALG_SIGN_RSA_PKCS1_SHA1 || alg == ALG_SIGN_RSA_PKCS1_SHA256 || alg == ALG_SIGN_RSA_PKCS1_SHA1MD5) { pkcs1Cipher.init(privateKey, Cipher.MODE_ENCRYPT); sigLen = pkcs1Cipher.doFinal(buf, offset, lc, tmp, TMP_OFFSET); Util.arrayCopyNonAtomic(tmp, TMP_OFFSET, buf, (short) 0, sigLen); } else { short emLen = (short) (privateKey.getSize() / 8); pssPad(buf, offset, lc, tmp, TMP_OFFSET, emLen, signKeyFirstModulusByte); nopadCipher.init(privateKey, Cipher.MODE_ENCRYPT); sigLen = nopadCipher.doFinal(tmp, (short) 0, emLen, buf, (short) 0); } apdu.setOutgoingAndSend((short) 0, sigLen); }
private short findEntryRelative(short start, short id) throws FileNotFoundException { try { if (fileStructure[start] != DIR) { throw fnfe; } short childNum = fileStructure[(short) (start + 4)]; for (short i = 0; i < childNum; i++) { short index = fileStructure[(short) (start + (short) (5 + i))]; short fid = Util.getShort(fileStructure, (short) (index + 1)); if (fid == id) { return index; } } } catch (ArrayIndexOutOfBoundsException aioobe) { } throw fnfe; }
/** * Processes an incoming APDU. * * @see APDU * @param apdu the incoming APDU * @exception ISOException with the response bytes per ISO 7816-4 */ public void process(APDU apdu) { byte buffer[] = apdu.getBuffer(); short bytesRead = apdu.setIncomingAndReceive(); short echoOffset = (short) 0; while (bytesRead > 0) { Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, echoBytes, echoOffset, bytesRead); echoOffset += bytesRead; bytesRead = apdu.receiveBytes(ISO7816.OFFSET_CDATA); } apdu.setOutgoing(); apdu.setOutgoingLength((short) (echoOffset + 5)); // echo header apdu.sendBytes((short) 0, (short) 5); // echo data apdu.sendBytesLong(echoBytes, (short) 0, echoOffset); }
/** Process the VERIFY instruction (0x20) ISO7816-4 Section 7.5.6 */ private void processVerify(APDU apdu) { if (state != STATE_PERSONALISED) { ISOException.throwIt(SW_INS_NOT_SUPPORTED); } byte[] buf = apdu.getBuffer(); if (buf[OFFSET_P1] != 0x00 || buf[OFFSET_P2] != 0x00) { ISOException.throwIt(SW_INCORRECT_P1P2); } short lc = unsigned(buf[OFFSET_LC]); if (lc < MIN_PIN_SIZE || lc > MAX_PIN_SIZE) { ISOException.throwIt(SW_WRONG_LENGTH); } apdu.setIncomingAndReceive(); // Pad the PIN to overwrite any possible garbage in the APDU (e.g. Le) Util.arrayFillNonAtomic( buf, (short) (OFFSET_CDATA + lc), (short) (MAX_PIN_SIZE - lc), (byte) 0x00); if (!pin.check(buf, OFFSET_CDATA, MAX_PIN_SIZE)) { ISOException.throwIt((short) (SW_PIN_INCORRECT_TRIES_LEFT | pin.getTriesRemaining())); } }
private void processCreateFileSystemStructure(APDU apdu) { if (state != STATE_INITIAL) { ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); } byte[] buf = apdu.getBuffer(); short lc = unsigned(buf[OFFSET_LC]); apdu.setIncomingAndReceive(); // Hack: // Search for a non-existing file, // if the structure is correct, then only the FileNotFoundException would be // thrown. try { fileSystem.searchId( buf, OFFSET_CDATA, OFFSET_CDATA, (short) (OFFSET_CDATA + lc), (short) 0x0000); ISOException.throwIt(SW_WRONG_DATA); } catch (FileNotFoundException e) { } catch (ArrayIndexOutOfBoundsException aioobe) { ISOException.throwIt(SW_WRONG_DATA); } fileSystem.fileStructure = new byte[lc]; Util.arrayCopy(buf, OFFSET_CDATA, fileSystem.fileStructure, (short) 0, lc); }
public void setSeed(byte[] buffer, short offset, short length) { byte[] seed = new byte[length]; Util.arrayCopyNonAtomic(buffer, offset, seed, (short) 0, length); engine.addSeedMaterial(seed); }
/** Process the PSO DECIPHER instruction. ISO 7816-8 Section 5.10 */ private void processDecipher(APDU apdu) { byte[] buf = apdu.getBuffer(); byte cla = buf[OFFSET_CLA]; boolean chain = ((byte) (cla & CLA_CHAIN) == CLA_CHAIN); short lc = unsigned(buf[OFFSET_LC]); // We need at least 1 byte of data to feed into the cipher, // so that a progression is made if (lc == 0) { ISOException.throwIt(SW_WRONG_LENGTH); } apdu.setIncomingAndReceive(); short offset = OFFSET_CDATA; // The first in chain, intialized and check: if (tmp[TMP_DECSEQ_OFFSET] == (byte) 0x00) { RSAPrivateCrtKey key = (RSAPrivateCrtKey) currentPrivateKey[0]; if (key == null) { ISOException.throwIt(SW_KEY_NOT_FOUND); } byte alg = tmp[TMP_SIGNALG_OFFSET]; if (key != decKeyPrivate || alg != ALG_AUTH_DEC_RSA) { ISOException.throwIt(SW_WRONG_DATA); } pkcs1Cipher.init(key, Cipher.MODE_DECRYPT); tmp[TMP_DECSEQ_OFFSET]++; expectedDecipherDataLength[0] = (short) (key.getSize() / 8); } short decipheredLen = 0; try { decipheredLen = pkcs1Cipher.update(buf, offset, lc, tmp, (short) (TMP_OFFSET + decipheredLen)); } catch (CryptoException ce) { ISOException.throwIt(SW_WRONG_DATA); } expectedDecipherDataLength[0] -= lc; offset += lc; // Data still to come: if (expectedDecipherDataLength[0] != 0 && !chain) { ISOException.throwIt(SW_WRONG_DATA); } // No more data: if (expectedDecipherDataLength[0] == 0 && chain) { ISOException.throwIt(SW_LAST_COMMAND_EXPECTED); } if (chain) { // It should also be the case the deciphereLen == 0, check? return; } pin.reset(); tmp[TMP_DECSEQ_OFFSET] = 0x00; try { decipheredLen = pkcs1Cipher.doFinal(buf, offset, (short) 0, tmp, (short) (TMP_OFFSET + decipheredLen)); } catch (CryptoException ce) { ISOException.throwIt(SW_WRONG_DATA); } Util.arrayCopyNonAtomic(tmp, TMP_OFFSET, buf, (short) 0, decipheredLen); apdu.setOutgoingAndSend((short) 0, decipheredLen); }
private short getFileIndex(short fid) throws ISOException { short result = -1; switch (fid) { case EF_DG1_FID: result = EF_DG1_INDEX; break; case EF_DG2_FID: result = EF_DG2_INDEX; break; case EF_DG3_FID: result = EF_DG3_INDEX; break; case EF_DG4_FID: result = EF_DG4_INDEX; break; case EF_DG5_FID: result = EF_DG5_INDEX; break; case EF_DG6_FID: result = EF_DG6_INDEX; break; case EF_DG7_FID: result = EF_DG7_INDEX; break; case EF_DG8_FID: result = EF_DG8_INDEX; break; case EF_DG9_FID: result = EF_DG9_INDEX; break; case EF_DG10_FID: result = EF_DG10_INDEX; break; case EF_DG11_FID: result = EF_DG11_INDEX; break; case EF_DG12_FID: result = EF_DG12_INDEX; break; case EF_DG13_FID: result = EF_DG13_INDEX; break; case EF_DG14_FID: result = EF_DG14_INDEX; break; case EF_DG15_FID: result = EF_DG15_INDEX; break; case EF_DG16_FID: result = EF_DG16_INDEX; break; case EF_SOD_FID: result = EF_SOD_INDEX; break; case EF_COM_FID: result = EF_COM_INDEX; break; default: result = -1; break; } if (result != -1 && SmartIDApplet.isLocked() && SmartIDApplet.hasMutualAuthenticationKeys()) { // We are in the personalized state and BAC is active, // we need to control the access // a. check that the current authorization level is sufficient to // access // the given file // b. if we are passed the EAC protocol we also need to check // whether the current certificate authorization allows us to read the file. byte perm = filePerms[result]; if ((byte) (perm & SmartIDApplet.volatileState[0]) != perm) { ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED); } if (result <= EF_DG16_INDEX && SmartIDApplet.hasTerminalAuthenticated() && perm == SmartIDApplet.TERMINAL_AUTHENTICATED) { short m = (short) (0x1 << result); if ((Util.getShort(currentAuthorization, (short) 1) & m) != m) { ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED); } } } return result; }
/** * Process the MANAGE SECURITY ENVIRONMENT instruction (0x22). ISO7816-4, Section 7.5.11 * * <p>This command can be also used to prepare key generation. In this case the algorithm * indication is not required, in fact, should not be present. Note that the key identifiers * should be already set up with put data before that. */ private void processManageSecurityEnvironment(APDU apdu) { boolean forKeyGeneration = false; if (state == STATE_INITIAL) { forKeyGeneration = true; } else if (state == STATE_PREPERSONALISED) { ISOException.throwIt(SW_INS_NOT_SUPPORTED); } pin.reset(); byte[] buf = apdu.getBuffer(); byte p1 = buf[OFFSET_P1]; byte p2 = buf[OFFSET_P2]; // P1 should be: // (a) 0x40: computation, decipherment, internal authentication, ... // (b) 0x01: set if (p1 != (byte) 0x41) { ISOException.throwIt(SW_INCORRECT_P1P2); } byte[] expectedKeyId = null; // P2 should be one of the following, see ISO7816-4 Table 79 if (p2 == (byte) 0xa4) { expectedKeyId = authKeyId; } else if (p2 == (byte) 0xb6) { expectedKeyId = signKeyId; } else if (p2 == (byte) 0xB8) { expectedKeyId = decKeyId; } else { ISOException.throwIt(SW_INCORRECT_P1P2); } short lc = unsigned(buf[OFFSET_LC]); if (lc == 0) { ISOException.throwIt(SW_WRONG_LENGTH); } apdu.setIncomingAndReceive(); short offset = OFFSET_CDATA; lc += OFFSET_CDATA; // Tag for the private key: short len = checkDataObject(buf, offset, lc, (byte) 0x84); offset += 2; if (len != expectedKeyId[0]) { ISOException.throwIt(SW_WRONG_LENGTH); } if (Util.arrayCompare(buf, offset, expectedKeyId, (short) 1, len) != 0) { ISOException.throwIt(SW_KEY_NOT_FOUND); } offset += len; // Algorithm identfier tag if (!forKeyGeneration) { if (offset >= lc) { ISOException.throwIt(SW_WRONG_DATA); } len = checkDataObject(buf, offset, lc, (byte) 0x80); offset += 2; if (len != 1) { ISOException.throwIt(SW_WRONG_LENGTH); } byte sAlg = buf[offset++]; if (offset != lc) { ISOException.throwIt(SW_WRONG_LENGTH); } if (sAlg < ALG_AUTH_DEC_RSA || sAlg > ALG_SIGN_RSA_PKCS1_SHA1MD5) { ISOException.throwIt(SW_WRONG_DATA); } tmp[TMP_SIGNALG_OFFSET] = sAlg; } else { if (offset != lc) { ISOException.throwIt(SW_WRONG_LENGTH); } } if (expectedKeyId == authKeyId) { currentPrivateKey[0] = authKeyPrivate; } else if (expectedKeyId == signKeyId) { currentPrivateKey[0] = signKeyPrivate; } else if (expectedKeyId == decKeyId) { currentPrivateKey[0] = decKeyPrivate; } }
/** Process the SELECT (file) instruction (0xA4) ISO7816-4 Section 7.1.1 */ private void processSelectFile(APDU apdu) { byte[] buf = apdu.getBuffer(); byte p1 = buf[OFFSET_P1]; // byte p2 = buf[OFFSET_P2]; short lc = unsigned(buf[OFFSET_LC]); if (p1 == 0x04) { // Select the AID of the applet // do heavy verification, just for the fun of it ;) if (lc != (short) 0x0C) { ISOException.throwIt(SW_WRONG_LENGTH); } apdu.setIncomingAndReceive(); if (Util.arrayCompare(buf, OFFSET_CDATA, myAID, (short) 0, lc) != 0) { ISOException.throwIt(SW_WRONG_DATA); } return; } short id = 0; switch (p1) { case (byte) 0x00: // Direct selection of MF, DF, or EF: if (lc != 0 && lc != 2) { ISOException.throwIt(SW_WRONG_LENGTH); } if (lc > 0) { apdu.setIncomingAndReceive(); id = Util.makeShort(buf[OFFSET_CDATA], buf[(short) (OFFSET_CDATA + 1)]); } else { id = FileSystem.MASTER_FILE_ID; } if (!fileSystem.selectEntryAbsolute(id)) { ISOException.throwIt(SW_FILE_NOT_FOUND); } break; case (byte) 0x01: case (byte) 0x02: // Select the child under the current DF, // p1 0x01 DF identifier in data field // p1 0x02 EF identifier in data field if (lc != 2) { ISOException.throwIt(SW_WRONG_LENGTH); } apdu.setIncomingAndReceive(); id = Util.makeShort(buf[OFFSET_CDATA], buf[(short) (OFFSET_CDATA + 1)]); if (!fileSystem.selectEntryUnderCurrent(id, p1 == (byte) 0x02)) { ISOException.throwIt(SW_FILE_NOT_FOUND); } break; case (byte) 0x03: // Select the parent of the current DF // no command data if (lc != 0) { ISOException.throwIt(SW_WRONG_LENGTH); } if (!fileSystem.selectEntryParent()) { ISOException.throwIt(SW_FILE_NOT_FOUND); } break; case (byte) 0x08: case (byte) 0x09: // Select by path // p1 0x08 from MF // p1 0x09 from current DF // data field: the path without the head if (lc == 0 || (short) (lc % 2) != 0) { ISOException.throwIt(SW_WRONG_LENGTH); } apdu.setIncomingAndReceive(); if (!fileSystem.selectEntryByPath(buf, OFFSET_CDATA, lc, p1 == (byte) 0x08)) { ISOException.throwIt(SW_FILE_NOT_FOUND); } break; default: ISOException.throwIt(SW_INCORRECT_P1P2); } }
private void processSetupKey(APDU apdu) throws ISOException { if (state != STATE_INITIAL) { ISOException.throwIt(SW_CONDITIONS_NOT_SATISFIED); } byte[] buf = apdu.getBuffer(); byte p1 = buf[OFFSET_P1]; byte p2 = buf[OFFSET_P2]; short lc = unsigned(buf[OFFSET_LC]); apdu.setIncomingAndReceive(); if (p1 == (byte) 0x61 || p1 == (byte) 0x62 || p1 == (byte) 0x63) { if (lc > 16) { ISOException.throwIt(SW_WRONG_LENGTH); } byte[] keyId = null; if (p1 == (byte) 0x61) { keyId = authKeyId; } else if (p1 == (byte) 0x62) { keyId = signKeyId; } else if (p1 == (byte) 0x63) { keyId = decKeyId; } Util.arrayCopy(buf, OFFSET_CDATA, keyId, (short) 1, lc); keyId[0] = (byte) lc; return; } RSAPrivateCrtKey privKey = null; if (p1 == (byte) 0x64) { privKey = authKeyPrivate; } else if (p1 == (byte) 0x65) { privKey = signKeyPrivate; } else if (p1 == (byte) 0x66) { privKey = decKeyPrivate; } else { ISOException.throwIt(SW_INCORRECT_P1P2); } try { switch (p2) { case (byte) 0x81: // Modulus, ignore, but record the first byte if key is sign key if (privKey == signKeyPrivate) { signKeyFirstModulusByte = buf[OFFSET_CDATA]; } break; case (byte) 0x82: // Exponent, ignore break; case (byte) 0x83: privKey.setP(buf, OFFSET_CDATA, lc); break; case (byte) 0x84: privKey.setQ(buf, OFFSET_CDATA, lc); break; case (byte) 0x85: privKey.setDP1(buf, OFFSET_CDATA, lc); break; case (byte) 0x86: privKey.setDQ1(buf, OFFSET_CDATA, lc); break; case (byte) 0x87: privKey.setPQ(buf, OFFSET_CDATA, lc); break; default: } } catch (Exception e) { ISOException.throwIt(SW_WRONG_DATA); } }