// ------------------------------------------------ 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); }
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 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); }
/** 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); }
// ------------------------------------------------ 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); }
// ------------------------------------------------ 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 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; }
/** 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); }
/** * 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]); }
/** * 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 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); }
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); }