Пример #1
0
  /**
   * 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;
  }
Пример #2
0
  /* 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;
  }
Пример #3
0
 /** 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);
 }
Пример #4
0
  // ------------------------------------------------
  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);
  }
Пример #5
0
  /* 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;
  }
Пример #6
0
 /**
  * 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);
    }
  }
Пример #8
0
  /** 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);
  }
Пример #9
0
  /**
   * 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();
  }
Пример #10
0
  /**
   * 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]);
  }
Пример #11
0
  // ------------------------------------------------
  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;
  }
Пример #12
0
  // ------------------------------------------------
  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);
  }
Пример #13
0
 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);
   }
 }
Пример #14
0
  // ------------------------------------------------
  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);
  }
Пример #15
0
 /**
  * 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);
   }
 }
Пример #16
0
 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;
   }
 }
Пример #17
0
  // ------------------------------------------------
  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;
  }
Пример #18
0
 /**
  * \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();
   }
 }
Пример #19
0
 /**
  * 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();
   }
 }
Пример #20
0
  /** 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);
  }
Пример #21
0
    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;
    }
Пример #22
0
  /**
   * 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);
  }
Пример #23
0
 /** 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()));
   }
 }
Пример #24
0
 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);
 }
Пример #25
0
 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);
 }
Пример #26
0
  /** 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;
 }
Пример #28
0
  /**
   * 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;
    }
  }
Пример #29
0
  /** 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);
    }
  }
Пример #30
0
 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);
   }
 }