Example usage for org.bouncycastle.openpgp PGPLiteralData getFormat

List of usage examples for org.bouncycastle.openpgp PGPLiteralData getFormat

Introduction

In this page you can find the example usage for org.bouncycastle.openpgp PGPLiteralData getFormat.

Prototype

public int getFormat() 

Source Link

Document

Return the format of the data packet.

Usage

From source file:org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation.java

License:Open Source License

/** Decrypt and/or verify binary or ascii armored pgp data. */
@NonNull//from   www  .jav  a  2 s .  c om
private DecryptVerifyResult decryptVerify(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
        InputData inputData, InputStream in, OutputStream out, int indent) throws IOException, PGPException {

    OperationLog log = new OperationLog();

    log.add(LogType.MSG_DC, indent);
    indent += 1;

    updateProgress(R.string.progress_reading_data, 0, 100);

    // parse ASCII Armor headers
    ArmorHeaders armorHeaders = parseArmorHeaders(in, log, indent);
    String charset = armorHeaders.charset;
    boolean useBackupCode = false;
    if (armorHeaders.backupVersion != null && armorHeaders.backupVersion == 2) {
        useBackupCode = true;
    }

    OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder();

    JcaSkipMarkerPGPObjectFactory plainFact;
    Object dataChunk;
    EncryptStreamResult esResult = null;
    { // resolve encrypted (symmetric and asymmetric) packets
        JcaSkipMarkerPGPObjectFactory pgpF = new JcaSkipMarkerPGPObjectFactory(in);
        Object obj = pgpF.nextObject();

        if (obj instanceof PGPEncryptedDataList) {
            esResult = handleEncryptedPacket(input, cryptoInput, (PGPEncryptedDataList) obj, log, indent,
                    useBackupCode);

            // if there is an error, nothing left to do here
            if (esResult.errorResult != null) {
                return esResult.errorResult;
            }

            // if this worked out so far, the data is encrypted
            decryptionResultBuilder.setEncrypted(true);
            if (esResult.sessionKey != null && esResult.decryptedSessionKey != null) {
                decryptionResultBuilder.setSessionKey(esResult.sessionKey, esResult.decryptedSessionKey);
            }

            if (esResult.insecureEncryptionKey) {
                log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
                decryptionResultBuilder.setInsecure(true);
            }

            // Check for insecure encryption algorithms!
            if (!PgpSecurityConstants.isSecureSymmetricAlgorithm(esResult.symmetricEncryptionAlgo)) {
                log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
                decryptionResultBuilder.setInsecure(true);
            }

            plainFact = new JcaSkipMarkerPGPObjectFactory(esResult.cleartextStream);
            dataChunk = plainFact.nextObject();

        } else {
            decryptionResultBuilder.setEncrypted(false);

            plainFact = pgpF;
            dataChunk = obj;
        }

    }

    log.add(LogType.MSG_DC_PREP_STREAMS, indent);

    log.add(LogType.MSG_DC_CLEAR, indent);
    indent += 1;

    // resolve compressed data
    if (dataChunk instanceof PGPCompressedData) {
        log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1);

        PGPCompressedData compressedData = (PGPCompressedData) dataChunk;

        JcaSkipMarkerPGPObjectFactory fact = new JcaSkipMarkerPGPObjectFactory(compressedData.getDataStream());
        dataChunk = fact.nextObject();
        plainFact = fact;
    }

    PgpSignatureChecker signatureChecker = new PgpSignatureChecker(mProviderHelper, input.getSenderAddress());
    if (signatureChecker.initializeOnePassSignature(dataChunk, log, indent + 1)) {
        dataChunk = plainFact.nextObject();
    }

    if (dataChunk instanceof PGPSignatureList) {
        // skip
        dataChunk = plainFact.nextObject();
    }

    OpenPgpMetadata metadata;

    if (!(dataChunk instanceof PGPLiteralData)) {

        log.add(LogType.MSG_DC_ERROR_INVALID_DATA, indent);
        return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);

    }

    log.add(LogType.MSG_DC_CLEAR_DATA, indent + 1);
    indent += 2;

    PGPLiteralData literalData = (PGPLiteralData) dataChunk;

    String originalFilename = literalData.getFileName();
    // reject filenames with slashes completely (path traversal issue)
    if (originalFilename.contains("/")) {
        originalFilename = "";
    }
    String mimeType = null;
    if (literalData.getFormat() == PGPLiteralData.TEXT || literalData.getFormat() == PGPLiteralData.UTF8) {
        mimeType = "text/plain";
    } else {
        // try to guess from file ending
        String extension = MimeTypeMap.getFileExtensionFromUrl(originalFilename);
        if (extension != null) {
            MimeTypeMap mime = MimeTypeMap.getSingleton();
            mimeType = mime.getMimeTypeFromExtension(extension);
        }
    }
    if (mimeType == null) {
        mimeType = "application/octet-stream";
    }

    if (!"".equals(originalFilename)) {
        log.add(LogType.MSG_DC_CLEAR_META_FILE, indent + 1, originalFilename);
    }
    log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1,
            new Date(literalData.getModificationTime().getTime()).toString());

    // return here if we want to decrypt the metadata only
    if (input.isDecryptMetadataOnly()) {

        log.add(LogType.MSG_DC_CLEAR_META_MIME, indent + 1, mimeType);

        // this operation skips the entire stream to find the data length!
        Long originalSize = literalData.findDataLength();

        if (originalSize != null) {
            log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1, Long.toString(originalSize));
        } else {
            log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1);
        }

        metadata = new OpenPgpMetadata(originalFilename, mimeType, literalData.getModificationTime().getTime(),
                originalSize == null ? 0 : originalSize, charset);

        log.add(LogType.MSG_DC_OK_META_ONLY, indent);
        DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
        result.setDecryptionMetadata(metadata);
        return result;
    }

    InputStream dataIn = literalData.getInputStream();

    long opTime, startTime = System.currentTimeMillis();

    long alreadyWritten = 0;
    long wholeSize = inputData.getSize() - inputData.getStreamPosition();
    boolean sizeIsKnown = inputData.getSize() != InputData.UNKNOWN_FILESIZE && wholeSize > 0;
    int length;
    byte[] buffer = new byte[8192];
    byte[] firstBytes = new byte[48];
    CharsetVerifier charsetVerifier = new CharsetVerifier(buffer, mimeType, charset);

    updateProgress(R.string.progress_decrypting, 1, 100);

    long nextProgressTime = 0L;
    int lastReportedProgress = 1;
    while ((length = dataIn.read(buffer)) > 0) {
        // Log.d(Constants.TAG, "read bytes: " + length);
        if (out != null) {
            out.write(buffer, 0, length);
        }

        // update signature buffer if signature is also present
        signatureChecker.updateSignatureData(buffer, 0, length);

        charsetVerifier.readBytesFromBuffer(0, length);

        // note down first couple of bytes for "magic bytes" file type detection
        if (alreadyWritten == 0) {
            System.arraycopy(buffer, 0, firstBytes, 0, length > firstBytes.length ? firstBytes.length : length);
        }

        alreadyWritten += length;
        if (sizeIsKnown && nextProgressTime < System.currentTimeMillis()) {
            long progress = 100 * inputData.getStreamPosition() / wholeSize;
            // stop at 100% for wrong file sizes...
            if (progress > 100) {
                progress = 100;
            }
            if (progress > lastReportedProgress) {
                updateProgress((int) progress, 100);
                lastReportedProgress = (int) progress;
                nextProgressTime = System.currentTimeMillis() + PROGRESS_STRIDE_MILLISECONDS;
            }
        }
    }

    if (signatureChecker.isInitialized()) {

        Object o = plainFact.nextObject();
        boolean signatureCheckOk = signatureChecker.verifySignatureOnePass(o, log, indent + 1);

        if (!signatureCheckOk) {
            return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
        }

    }

    opTime = System.currentTimeMillis() - startTime;
    Log.d(Constants.TAG, "decrypt time taken: " + String.format("%.2f", opTime / 1000.0) + "s, for "
            + alreadyWritten + " bytes");

    // special treatment to detect pgp mime types
    // TODO move into CharsetVerifier? seems like that would be a plausible place for this logic
    if (matchesPrefix(firstBytes, "-----BEGIN PGP PUBLIC KEY BLOCK-----")
            || matchesPrefix(firstBytes, "-----BEGIN PGP PRIVATE KEY BLOCK-----")) {
        mimeType = Constants.MIME_TYPE_KEYS;
    } else if (matchesPrefix(firstBytes, "-----BEGIN PGP MESSAGE-----")) {
        // this is NOT application/pgp-encrypted, see RFC 3156!
        mimeType = Constants.MIME_TYPE_ENCRYPTED_ALTERNATE;
    } else {
        mimeType = charsetVerifier.getGuessedMimeType();
    }
    metadata = new OpenPgpMetadata(originalFilename, mimeType, literalData.getModificationTime().getTime(),
            alreadyWritten, charsetVerifier.getCharset());

    log.add(LogType.MSG_DC_CLEAR_META_MIME, indent + 1, mimeType);
    Log.d(Constants.TAG, metadata.toString());

    indent -= 1;

    if (esResult != null) {
        if (esResult.encryptedData.isIntegrityProtected()) {
            if (esResult.encryptedData.verify()) {
                log.add(LogType.MSG_DC_INTEGRITY_CHECK_OK, indent);
            } else {
                log.add(LogType.MSG_DC_ERROR_INTEGRITY_CHECK, indent);
                return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
            }
        } else if (!signatureChecker.isInitialized()) {
            // If no signature is present, we *require* an MDC!
            // Handle missing integrity protection like failed integrity protection!
            // The MDC packet can be stripped by an attacker!
            log.add(LogType.MSG_DC_INSECURE_MDC_MISSING, indent);
            decryptionResultBuilder.setInsecure(true);
        }
    }

    updateProgress(R.string.progress_done, 100, 100);

    log.add(LogType.MSG_DC_OK, indent);

    // Return a positive result, with metadata and verification info
    DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);

    result.setCachedCryptoInputParcel(cryptoInput);
    result.setSignatureResult(signatureChecker.getSignatureResult());
    result.setDecryptionResult(decryptionResultBuilder.build());
    result.setDecryptionMetadata(metadata);
    result.mOperationTime = opTime;

    return result;

}