Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5d586dd5 authored by Alex Klyubin's avatar Alex Klyubin Committed by Android (Google) Code Review
Browse files

Merge "KM module may consume less input than provided by finish time." into mnc-dev

parents 23e19066 53d544a4
Loading
Loading
Loading
Loading
+71 −23
Original line number Diff line number Diff line
@@ -19,12 +19,14 @@ package android.security.keystore;
import android.os.IBinder;
import android.security.KeyStore;
import android.security.KeyStoreException;
import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.OperationResult;

import libcore.util.EmptyArray;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.ProviderException;

/**
 * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
@@ -135,14 +137,15 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
                mBuffered = EmptyArray.BYTE;
                mBufferedOffset = 0;
                mBufferedLength = 0;
            } else if (opResult.inputConsumed == 0) {
            } else if (opResult.inputConsumed <= 0) {
                // Nothing was consumed. More input needed.
                if (inputLength > 0) {
                    // More input is available, but it wasn't included into the previous chunk
                    // because the chunk reached its maximum permitted size.
                    // Shouldn't have happened.
                    throw new IllegalStateException("Nothing consumed from max-sized chunk: "
                            + chunk.length + " bytes");
                    throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
                            "Keystore consumed nothing from max-sized chunk: " + chunk.length
                                    + " bytes");
                }
                mBuffered = chunk;
                mBufferedOffset = 0;
@@ -153,8 +156,9 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
                mBufferedOffset = opResult.inputConsumed;
                mBufferedLength = chunk.length - opResult.inputConsumed;
            } else {
                throw new IllegalStateException("Consumed more than provided: "
                        + opResult.inputConsumed + ", provided: " + chunk.length);
                throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
                        "Keystore consumed more input than provided. Provided: " + chunk.length
                                + ", consumed: " + opResult.inputConsumed);
            }

            if ((opResult.output != null) && (opResult.output.length > 0)) {
@@ -165,7 +169,7 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
                        try {
                            bufferedOutput.write(opResult.output);
                        } catch (IOException e) {
                            throw new IllegalStateException("Failed to buffer output", e);
                            throw new ProviderException("Failed to buffer output", e);
                        }
                    }
                } else {
@@ -179,7 +183,7 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
                        try {
                            bufferedOutput.write(opResult.output);
                        } catch (IOException e) {
                            throw new IllegalStateException("Failed to buffer output", e);
                            throw new ProviderException("Failed to buffer output", e);
                        }
                        result = bufferedOutput.toByteArray();
                    }
@@ -229,11 +233,11 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
            return EmptyArray.BYTE;
        }

        // Keep invoking the update operation with remaining buffered data until either all of the
        // buffered data is consumed or until update fails to consume anything.
        ByteArrayOutputStream bufferedOutput = null;
        while (mBufferedLength > 0) {
            byte[] chunk = ArrayUtils.subarray(mBuffered, mBufferedOffset, mBufferedLength);
        mBuffered = EmptyArray.BYTE;
        mBufferedLength = 0;
        mBufferedOffset = 0;

            OperationResult opResult = mKeyStoreStream.update(chunk);
            if (opResult == null) {
                throw new KeyStoreConnectException();
@@ -241,15 +245,59 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
                throw KeyStore.getKeyStoreException(opResult.resultCode);
            }

        if (opResult.inputConsumed < chunk.length) {
            throw new IllegalStateException("Keystore failed to consume all input. Provided: "
            if (opResult.inputConsumed <= 0) {
                // Nothing was consumed. Break out of the loop to avoid an infinite loop.
                break;
            }

            if (opResult.inputConsumed >= chunk.length) {
                // All of the input was consumed
                mBuffered = EmptyArray.BYTE;
                mBufferedOffset = 0;
                mBufferedLength = 0;
            } else {
                // Some of the input was not consumed
                mBuffered = chunk;
                mBufferedOffset = opResult.inputConsumed;
                mBufferedLength = chunk.length - opResult.inputConsumed;
            }

            if (opResult.inputConsumed > chunk.length) {
                throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
                        "Keystore consumed more input than provided. Provided: "
                                + chunk.length + ", consumed: " + opResult.inputConsumed);
        } else if (opResult.inputConsumed > chunk.length) {
            throw new IllegalStateException("Keystore consumed more input than provided"
                    + " . Provided: " + chunk.length + ", consumed: " + opResult.inputConsumed);
            }

        byte[] result = (opResult.output != null) ? opResult.output : EmptyArray.BYTE;
            if ((opResult.output != null) && (opResult.output.length > 0)) {
                // Some output was produced by this update operation
                if (bufferedOutput == null) {
                    // No output buffered yet.
                    if (mBufferedLength == 0) {
                        // No more output will be produced by this flush operation
                        mProducedOutputSizeBytes += opResult.output.length;
                        return opResult.output;
                    } else {
                        // More output might be produced by this flush operation -- buffer output.
                        bufferedOutput = new ByteArrayOutputStream();
                    }
                }
                // Buffer the output from this update operation
                try {
                    bufferedOutput.write(opResult.output);
                } catch (IOException e) {
                    throw new ProviderException("Failed to buffer output", e);
                }
            }
        }

        if (mBufferedLength > 0) {
            throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
                    "Keystore failed to consume last "
                            + ((mBufferedLength != 1) ? (mBufferedLength + " bytes") : "byte")
                            + " of input");
        }

        byte[] result = (bufferedOutput != null) ? bufferedOutput.toByteArray() : EmptyArray.BYTE;
        mProducedOutputSizeBytes += result.length;
        return result;
    }