1cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin/*
2cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin * Copyright (C) 2015 The Android Open Source Project
3cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin *
4cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin * Licensed under the Apache License, Version 2.0 (the "License");
5cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin * you may not use this file except in compliance with the License.
6cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin * You may obtain a copy of the License at
7cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin *
8cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin *      http://www.apache.org/licenses/LICENSE-2.0
9cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin *
10cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin * Unless required by applicable law or agreed to in writing, software
11cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin * distributed under the License is distributed on an "AS IS" BASIS,
12cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin * See the License for the specific language governing permissions and
14cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin * limitations under the License.
15cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin */
16cc21bb3a56915842b545a577d3481047005b1764Alex Klyubin
17dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinpackage android.security.keystore;
184ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
19b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubinimport android.os.IBinder;
20dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinimport android.security.KeyStore;
21dcdaf87ed0aa99073638bcfe645949f130f0c7adAlex Klyubinimport android.security.KeyStoreException;
2253d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubinimport android.security.keymaster.KeymasterDefs;
234ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubinimport android.security.keymaster.OperationResult;
244ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
255927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubinimport libcore.util.EmptyArray;
265927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin
274ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubinimport java.io.ByteArrayOutputStream;
284ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubinimport java.io.IOException;
2953d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubinimport java.security.ProviderException;
304ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
314ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin/**
324ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
334ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * {@code update} and {@code finish} operations.
344ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin *
354ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
36b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
37b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * amount of data in one go because the operations are marshalled via Binder. Secondly, the update
38b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * operation may consume less data than provided, in which case the caller has to buffer the
39b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
40d23dc502b0a1952887d4453cba98aa2e3d2f5009Alex Klyubin * {@link #doFinal(byte[], int, int, byte[], byte[]) doFinal} operations which can be used to
41d23dc502b0a1952887d4453cba98aa2e3d2f5009Alex Klyubin * conveniently implement various JCA crypto primitives.
424ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin *
43b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * <p>Bidirectional chunked streaming of data via a KeyStore crypto operation is abstracted away as
44b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * a {@link Stream} to avoid having this class deal with operation tokens and occasional additional
45b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * parameters to {@code update} and {@code final} operations.
464ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin *
474ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * @hide
484ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin */
494f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubinclass KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationStreamer {
50b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
51b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    /**
52b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * Bidirectional chunked data stream over a KeyStore crypto operation.
53b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     */
544f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin    interface Stream {
554ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        /**
56b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin         * Returns the result of the KeyStore {@code update} operation or null if keystore couldn't
57b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin         * be reached.
584ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin         */
594ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        OperationResult update(byte[] input);
604ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
614ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        /**
62b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin         * Returns the result of the KeyStore {@code finish} operation or null if keystore couldn't
63b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin         * be reached.
644ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin         */
65d23dc502b0a1952887d4453cba98aa2e3d2f5009Alex Klyubin        OperationResult finish(byte[] siganture, byte[] additionalEntropy);
664ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
674ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
684ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    // Binder buffer is about 1MB, but it's shared between all active transactions of the process.
694ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    // Thus, it's safer to use a much smaller upper bound.
704ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
714ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
72b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    private final Stream mKeyStoreStream;
734ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private final int mMaxChunkSize;
744ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
754f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin    private byte[] mBuffered = EmptyArray.BYTE;
764ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private int mBufferedOffset;
774ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private int mBufferedLength;
7800af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    private long mConsumedInputSizeBytes;
7900af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    private long mProducedOutputSizeBytes;
804ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
81b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    public KeyStoreCryptoOperationChunkedStreamer(Stream operation) {
824ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        this(operation, DEFAULT_MAX_CHUNK_SIZE);
834ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
844ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
85b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    public KeyStoreCryptoOperationChunkedStreamer(Stream operation, int maxChunkSize) {
86b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        mKeyStoreStream = operation;
874ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        mMaxChunkSize = maxChunkSize;
884ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
894ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
904f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin    @Override
91b4834ae3fa09e8013f7ab743a12def063ae999e3Alex Klyubin    public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException {
924ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        if (inputLength == 0) {
934ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            // No input provided
944f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin            return EmptyArray.BYTE;
954ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
964ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
974ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        ByteArrayOutputStream bufferedOutput = null;
984ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
994ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        while (inputLength > 0) {
1004ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            byte[] chunk;
1014ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            int inputBytesInChunk;
1024ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            if ((mBufferedLength + inputLength) > mMaxChunkSize) {
1034ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // Too much input for one chunk -- extract one max-sized chunk and feed it into the
1044ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // update operation.
105b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                inputBytesInChunk = mMaxChunkSize - mBufferedLength;
1065927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin                chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength,
107b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                        input, inputOffset, inputBytesInChunk);
1084ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else {
1094ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // All of available input fits into one chunk.
1104ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                if ((mBufferedLength == 0) && (inputOffset == 0)
1114ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        && (inputLength == input.length)) {
1124ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // Nothing buffered and all of input array needs to be fed into the update
1134ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // operation.
1144ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    chunk = input;
1154ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    inputBytesInChunk = input.length;
1164ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                } else {
1174ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // Need to combine buffered data with input data into one array.
1184ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    inputBytesInChunk = inputLength;
1195927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin                    chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength,
120b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                            input, inputOffset, inputBytesInChunk);
1214ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                }
1224ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            }
1234ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            // Update input array references to reflect that some of its bytes are now in mBuffered.
1244ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            inputOffset += inputBytesInChunk;
1254ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            inputLength -= inputBytesInChunk;
12600af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin            mConsumedInputSizeBytes += inputBytesInChunk;
1274ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
128b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            OperationResult opResult = mKeyStoreStream.update(chunk);
1294ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            if (opResult == null) {
1304ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                throw new KeyStoreConnectException();
1314ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else if (opResult.resultCode != KeyStore.NO_ERROR) {
132b4834ae3fa09e8013f7ab743a12def063ae999e3Alex Klyubin                throw KeyStore.getKeyStoreException(opResult.resultCode);
1334ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            }
1344ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
1354ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            if (opResult.inputConsumed == chunk.length) {
1364ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // The whole chunk was consumed
1374f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin                mBuffered = EmptyArray.BYTE;
1384ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedOffset = 0;
1394ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedLength = 0;
14053d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            } else if (opResult.inputConsumed <= 0) {
1414ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // Nothing was consumed. More input needed.
1424ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                if (inputLength > 0) {
1434ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // More input is available, but it wasn't included into the previous chunk
1444ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // because the chunk reached its maximum permitted size.
1454ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // Shouldn't have happened.
14653d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                    throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
14753d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                            "Keystore consumed nothing from max-sized chunk: " + chunk.length
14853d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                                    + " bytes");
1494ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                }
1504ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBuffered = chunk;
1514ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedOffset = 0;
1524ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedLength = chunk.length;
1534ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else if (opResult.inputConsumed < chunk.length) {
1544ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // The chunk was consumed only partially -- buffer the rest of the chunk
1554ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBuffered = chunk;
1564ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedOffset = opResult.inputConsumed;
1574ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedLength = chunk.length - opResult.inputConsumed;
1584ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else {
15953d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
16053d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                        "Keystore consumed more input than provided. Provided: " + chunk.length
16153d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                                + ", consumed: " + opResult.inputConsumed);
1624ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            }
1634ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
1644ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            if ((opResult.output != null) && (opResult.output.length > 0)) {
1654ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                if (inputLength > 0) {
1664ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // More output might be produced in this loop -- buffer the current output
1674ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    if (bufferedOutput == null) {
1684ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        bufferedOutput = new ByteArrayOutputStream();
1694ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        try {
1704ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                            bufferedOutput.write(opResult.output);
1714ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        } catch (IOException e) {
17253d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                            throw new ProviderException("Failed to buffer output", e);
1734ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        }
1744ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    }
1754ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                } else {
1764ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // No more output will be produced in this loop
17700af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin                    byte[] result;
1784ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    if (bufferedOutput == null) {
1794ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        // No previously buffered output
18000af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin                        result = opResult.output;
1814ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    } else {
1824ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        // There was some previously buffered output
1834ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        try {
1844ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                            bufferedOutput.write(opResult.output);
1854ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        } catch (IOException e) {
18653d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                            throw new ProviderException("Failed to buffer output", e);
1874ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        }
18800af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin                        result = bufferedOutput.toByteArray();
1894ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    }
19000af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin                    mProducedOutputSizeBytes += result.length;
19100af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin                    return result;
1924ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                }
1934ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            }
1944ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
1954ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
19600af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        byte[] result;
1974ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        if (bufferedOutput == null) {
1984ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            // No output produced
19900af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin            result = EmptyArray.BYTE;
2004ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        } else {
20100af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin            result = bufferedOutput.toByteArray();
2024ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
20300af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        mProducedOutputSizeBytes += result.length;
20400af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        return result;
2054ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
2064ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
2074f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin    @Override
208d23dc502b0a1952887d4453cba98aa2e3d2f5009Alex Klyubin    public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
209d23dc502b0a1952887d4453cba98aa2e3d2f5009Alex Klyubin            byte[] signature, byte[] additionalEntropy) throws KeyStoreException {
2104ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        if (inputLength == 0) {
2114ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            // No input provided -- simplify the rest of the code
2124f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin            input = EmptyArray.BYTE;
2134ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            inputOffset = 0;
2144ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
2154ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
216b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        // Flush all buffered input and provided input into keystore/keymaster.
217b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        byte[] output = update(input, inputOffset, inputLength);
2185927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin        output = ArrayUtils.concat(output, flush());
219b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
220d23dc502b0a1952887d4453cba98aa2e3d2f5009Alex Klyubin        OperationResult opResult = mKeyStoreStream.finish(signature, additionalEntropy);
221b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        if (opResult == null) {
222b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            throw new KeyStoreConnectException();
223b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
224b4834ae3fa09e8013f7ab743a12def063ae999e3Alex Klyubin            throw KeyStore.getKeyStoreException(opResult.resultCode);
2254ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
22600af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        mProducedOutputSizeBytes += opResult.output.length;
2274ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
2285927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin        return ArrayUtils.concat(output, opResult.output);
229b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    }
230b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
231b4834ae3fa09e8013f7ab743a12def063ae999e3Alex Klyubin    public byte[] flush() throws KeyStoreException {
232b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        if (mBufferedLength <= 0) {
2335927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin            return EmptyArray.BYTE;
2344ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
235b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
23653d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin        // Keep invoking the update operation with remaining buffered data until either all of the
23753d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin        // buffered data is consumed or until update fails to consume anything.
23853d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin        ByteArrayOutputStream bufferedOutput = null;
23953d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin        while (mBufferedLength > 0) {
24053d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            byte[] chunk = ArrayUtils.subarray(mBuffered, mBufferedOffset, mBufferedLength);
24153d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            OperationResult opResult = mKeyStoreStream.update(chunk);
24253d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            if (opResult == null) {
24353d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                throw new KeyStoreConnectException();
24453d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            } else if (opResult.resultCode != KeyStore.NO_ERROR) {
24553d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                throw KeyStore.getKeyStoreException(opResult.resultCode);
24653d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            }
2474ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
24853d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            if (opResult.inputConsumed <= 0) {
24953d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                // Nothing was consumed. Break out of the loop to avoid an infinite loop.
25053d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                break;
25153d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            }
25253d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin
25353d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            if (opResult.inputConsumed >= chunk.length) {
25453d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                // All of the input was consumed
25553d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                mBuffered = EmptyArray.BYTE;
25653d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                mBufferedOffset = 0;
25753d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                mBufferedLength = 0;
25853d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            } else {
25953d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                // Some of the input was not consumed
26053d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                mBuffered = chunk;
26153d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                mBufferedOffset = opResult.inputConsumed;
26253d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                mBufferedLength = chunk.length - opResult.inputConsumed;
26353d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            }
26453d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin
26553d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            if (opResult.inputConsumed > chunk.length) {
26653d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
26753d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                        "Keystore consumed more input than provided. Provided: "
26853d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                                + chunk.length + ", consumed: " + opResult.inputConsumed);
26953d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            }
27053d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin
27153d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            if ((opResult.output != null) && (opResult.output.length > 0)) {
27253d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                // Some output was produced by this update operation
27353d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                if (bufferedOutput == null) {
27453d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                    // No output buffered yet.
27553d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                    if (mBufferedLength == 0) {
27653d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                        // No more output will be produced by this flush operation
27753d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                        mProducedOutputSizeBytes += opResult.output.length;
27853d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                        return opResult.output;
27953d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                    } else {
28053d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                        // More output might be produced by this flush operation -- buffer output.
28153d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                        bufferedOutput = new ByteArrayOutputStream();
28253d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                    }
28353d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                }
28453d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                // Buffer the output from this update operation
28553d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                try {
28653d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                    bufferedOutput.write(opResult.output);
28753d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                } catch (IOException e) {
28853d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                    throw new ProviderException("Failed to buffer output", e);
28953d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                }
29053d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            }
2914ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
2924ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
29353d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin        if (mBufferedLength > 0) {
29453d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin            throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
29553d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                    "Keystore failed to consume last "
29653d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                            + ((mBufferedLength != 1) ? (mBufferedLength + " bytes") : "byte")
29753d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin                            + " of input");
2984ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
2994ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
30053d544a4b9964166f90f34b46f3866cafefb84e7Alex Klyubin        byte[] result = (bufferedOutput != null) ? bufferedOutput.toByteArray() : EmptyArray.BYTE;
30100af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        mProducedOutputSizeBytes += result.length;
30200af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        return result;
30300af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    }
30400af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin
30500af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    @Override
30600af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    public long getConsumedInputSizeBytes() {
30700af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        return mConsumedInputSizeBytes;
30800af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    }
30900af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin
31000af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    @Override
31100af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    public long getProducedOutputSizeBytes() {
31200af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        return mProducedOutputSizeBytes;
313b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    }
314b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
315b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    /**
316b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * Main data stream via a KeyStore streaming operation.
317b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     *
318b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * <p>For example, for an encryption operation, this is the stream through which plaintext is
319b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * provided and ciphertext is obtained.
320b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     */
321b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    public static class MainDataStream implements Stream {
322b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
323b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        private final KeyStore mKeyStore;
324b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        private final IBinder mOperationToken;
325b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
326b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        public MainDataStream(KeyStore keyStore, IBinder operationToken) {
327b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            mKeyStore = keyStore;
328b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            mOperationToken = operationToken;
329b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        }
330b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
331b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        @Override
332b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        public OperationResult update(byte[] input) {
333b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            return mKeyStore.update(mOperationToken, null, input);
334b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        }
335b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
336b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        @Override
337d23dc502b0a1952887d4453cba98aa2e3d2f5009Alex Klyubin        public OperationResult finish(byte[] signature, byte[] additionalEntropy) {
338d23dc502b0a1952887d4453cba98aa2e3d2f5009Alex Klyubin            return mKeyStore.finish(mOperationToken, null, signature, additionalEntropy);
3394ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
3404ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
3414ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin}
342