KeyStoreCryptoOperationChunkedStreamer.java revision 00af27b7d9010eb41e45959dab7c4ff6de119897
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;
224ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubinimport android.security.keymaster.OperationResult;
234ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
245927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubinimport libcore.util.EmptyArray;
255927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin
264ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubinimport java.io.ByteArrayOutputStream;
274ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubinimport java.io.IOException;
284ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
294ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin/**
304ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
314ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * {@code update} and {@code finish} operations.
324ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin *
334ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
34b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
35b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * amount of data in one go because the operations are marshalled via Binder. Secondly, the update
36b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * operation may consume less data than provided, in which case the caller has to buffer the
37b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
38a72b55195c23fc06d1600efe8f6aac85290c7f8fAlex Klyubin * {@link #doFinal(byte[], int, int, byte[]) doFinal} operations which can be used to conveniently
39a72b55195c23fc06d1600efe8f6aac85290c7f8fAlex Klyubin * implement various JCA crypto primitives.
404ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin *
41b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * <p>Bidirectional chunked streaming of data via a KeyStore crypto operation is abstracted away as
42b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * a {@link Stream} to avoid having this class deal with operation tokens and occasional additional
43b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * parameters to {@code update} and {@code final} operations.
444ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin *
454ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * @hide
464ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin */
474f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubinclass KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationStreamer {
48b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
49b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    /**
50b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * Bidirectional chunked data stream over a KeyStore crypto operation.
51b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     */
524f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin    interface Stream {
534ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        /**
54b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin         * Returns the result of the KeyStore {@code update} operation or null if keystore couldn't
55b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin         * be reached.
564ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin         */
574ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        OperationResult update(byte[] input);
584ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
594ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        /**
60b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin         * Returns the result of the KeyStore {@code finish} operation or null if keystore couldn't
61b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin         * be reached.
624ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin         */
63a72b55195c23fc06d1600efe8f6aac85290c7f8fAlex Klyubin        OperationResult finish(byte[] additionalEntropy);
644ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
654ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
664ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    // Binder buffer is about 1MB, but it's shared between all active transactions of the process.
674ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    // Thus, it's safer to use a much smaller upper bound.
684ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
694ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
70b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    private final Stream mKeyStoreStream;
714ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private final int mMaxChunkSize;
724ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
734f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin    private byte[] mBuffered = EmptyArray.BYTE;
744ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private int mBufferedOffset;
754ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private int mBufferedLength;
7600af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    private long mConsumedInputSizeBytes;
7700af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    private long mProducedOutputSizeBytes;
784ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
79b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    public KeyStoreCryptoOperationChunkedStreamer(Stream operation) {
804ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        this(operation, DEFAULT_MAX_CHUNK_SIZE);
814ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
824ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
83b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    public KeyStoreCryptoOperationChunkedStreamer(Stream operation, int maxChunkSize) {
84b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        mKeyStoreStream = operation;
854ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        mMaxChunkSize = maxChunkSize;
864ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
874ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
884f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin    @Override
89b4834ae3fa09e8013f7ab743a12def063ae999e3Alex Klyubin    public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException {
904ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        if (inputLength == 0) {
914ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            // No input provided
924f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin            return EmptyArray.BYTE;
934ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
944ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
954ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        ByteArrayOutputStream bufferedOutput = null;
964ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
974ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        while (inputLength > 0) {
984ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            byte[] chunk;
994ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            int inputBytesInChunk;
1004ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            if ((mBufferedLength + inputLength) > mMaxChunkSize) {
1014ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // Too much input for one chunk -- extract one max-sized chunk and feed it into the
1024ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // update operation.
103b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                inputBytesInChunk = mMaxChunkSize - mBufferedLength;
1045927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin                chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength,
105b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                        input, inputOffset, inputBytesInChunk);
1064ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else {
1074ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // All of available input fits into one chunk.
1084ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                if ((mBufferedLength == 0) && (inputOffset == 0)
1094ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        && (inputLength == input.length)) {
1104ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // Nothing buffered and all of input array needs to be fed into the update
1114ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // operation.
1124ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    chunk = input;
1134ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    inputBytesInChunk = input.length;
1144ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                } else {
1154ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // Need to combine buffered data with input data into one array.
1164ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    inputBytesInChunk = inputLength;
1175927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin                    chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength,
118b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                            input, inputOffset, inputBytesInChunk);
1194ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                }
1204ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            }
1214ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            // Update input array references to reflect that some of its bytes are now in mBuffered.
1224ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            inputOffset += inputBytesInChunk;
1234ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            inputLength -= inputBytesInChunk;
12400af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin            mConsumedInputSizeBytes += inputBytesInChunk;
1254ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
126b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            OperationResult opResult = mKeyStoreStream.update(chunk);
1274ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            if (opResult == null) {
1284ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                throw new KeyStoreConnectException();
1294ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else if (opResult.resultCode != KeyStore.NO_ERROR) {
130b4834ae3fa09e8013f7ab743a12def063ae999e3Alex Klyubin                throw KeyStore.getKeyStoreException(opResult.resultCode);
1314ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            }
1324ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
1334ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            if (opResult.inputConsumed == chunk.length) {
1344ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // The whole chunk was consumed
1354f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin                mBuffered = EmptyArray.BYTE;
1364ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedOffset = 0;
1374ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedLength = 0;
1384ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else if (opResult.inputConsumed == 0) {
1394ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // Nothing was consumed. More input needed.
1404ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                if (inputLength > 0) {
1414ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // More input is available, but it wasn't included into the previous chunk
1424ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // because the chunk reached its maximum permitted size.
1434ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // Shouldn't have happened.
144ad9ba10ecda10c14e46d00f40fc3e431cc2d9bc2Alex Klyubin                    throw new IllegalStateException("Nothing consumed from max-sized chunk: "
1454ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                            + chunk.length + " bytes");
1464ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                }
1474ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBuffered = chunk;
1484ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedOffset = 0;
1494ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedLength = chunk.length;
1504ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else if (opResult.inputConsumed < chunk.length) {
1514ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // The chunk was consumed only partially -- buffer the rest of the chunk
1524ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBuffered = chunk;
1534ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedOffset = opResult.inputConsumed;
1544ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedLength = chunk.length - opResult.inputConsumed;
1554ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else {
156ad9ba10ecda10c14e46d00f40fc3e431cc2d9bc2Alex Klyubin                throw new IllegalStateException("Consumed more than provided: "
1574ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        + opResult.inputConsumed + ", provided: " + chunk.length);
1584ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            }
1594ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
1604ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            if ((opResult.output != null) && (opResult.output.length > 0)) {
1614ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                if (inputLength > 0) {
1624ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // More output might be produced in this loop -- buffer the current output
1634ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    if (bufferedOutput == null) {
1644ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        bufferedOutput = new ByteArrayOutputStream();
1654ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        try {
1664ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                            bufferedOutput.write(opResult.output);
1674ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        } catch (IOException e) {
168ad9ba10ecda10c14e46d00f40fc3e431cc2d9bc2Alex Klyubin                            throw new IllegalStateException("Failed to buffer output", e);
1694ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        }
1704ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    }
1714ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                } else {
1724ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // No more output will be produced in this loop
17300af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin                    byte[] result;
1744ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    if (bufferedOutput == null) {
1754ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        // No previously buffered output
17600af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin                        result = opResult.output;
1774ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    } else {
1784ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        // There was some previously buffered output
1794ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        try {
1804ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                            bufferedOutput.write(opResult.output);
1814ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        } catch (IOException e) {
182ad9ba10ecda10c14e46d00f40fc3e431cc2d9bc2Alex Klyubin                            throw new IllegalStateException("Failed to buffer output", e);
1834ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        }
18400af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin                        result = bufferedOutput.toByteArray();
1854ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    }
18600af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin                    mProducedOutputSizeBytes += result.length;
18700af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin                    return result;
1884ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                }
1894ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            }
1904ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
1914ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
19200af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        byte[] result;
1934ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        if (bufferedOutput == null) {
1944ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            // No output produced
19500af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin            result = EmptyArray.BYTE;
1964ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        } else {
19700af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin            result = bufferedOutput.toByteArray();
1984ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
19900af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        mProducedOutputSizeBytes += result.length;
20000af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        return result;
2014ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
2024ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
2034f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin    @Override
204a72b55195c23fc06d1600efe8f6aac85290c7f8fAlex Klyubin    public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] additionalEntropy)
205b4834ae3fa09e8013f7ab743a12def063ae999e3Alex Klyubin            throws KeyStoreException {
2064ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        if (inputLength == 0) {
2074ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            // No input provided -- simplify the rest of the code
2084f389fd200fee9e055d3f28b20bee3132329a056Alex Klyubin            input = EmptyArray.BYTE;
2094ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            inputOffset = 0;
2104ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
2114ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
212b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        // Flush all buffered input and provided input into keystore/keymaster.
213b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        byte[] output = update(input, inputOffset, inputLength);
2145927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin        output = ArrayUtils.concat(output, flush());
215b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
216a72b55195c23fc06d1600efe8f6aac85290c7f8fAlex Klyubin        OperationResult opResult = mKeyStoreStream.finish(additionalEntropy);
217b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        if (opResult == null) {
218b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            throw new KeyStoreConnectException();
219b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
220b4834ae3fa09e8013f7ab743a12def063ae999e3Alex Klyubin            throw KeyStore.getKeyStoreException(opResult.resultCode);
2214ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
22200af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        mProducedOutputSizeBytes += opResult.output.length;
2234ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
2245927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin        return ArrayUtils.concat(output, opResult.output);
225b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    }
226b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
227b4834ae3fa09e8013f7ab743a12def063ae999e3Alex Klyubin    public byte[] flush() throws KeyStoreException {
228b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        if (mBufferedLength <= 0) {
2295927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin            return EmptyArray.BYTE;
2304ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
231b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
2325927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin        byte[] chunk = ArrayUtils.subarray(mBuffered, mBufferedOffset, mBufferedLength);
2335927c9f1b12f597839a664c1c6593114175cbcd8Alex Klyubin        mBuffered = EmptyArray.BYTE;
2344ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        mBufferedLength = 0;
2354ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        mBufferedOffset = 0;
2364ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
237b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        OperationResult opResult = mKeyStoreStream.update(chunk);
2384ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        if (opResult == null) {
2394ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            throw new KeyStoreConnectException();
2404ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
241b4834ae3fa09e8013f7ab743a12def063ae999e3Alex Klyubin            throw KeyStore.getKeyStoreException(opResult.resultCode);
2424ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
2434ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
244b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        if (opResult.inputConsumed < chunk.length) {
245ad9ba10ecda10c14e46d00f40fc3e431cc2d9bc2Alex Klyubin            throw new IllegalStateException("Keystore failed to consume all input. Provided: "
246b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                    + chunk.length + ", consumed: " + opResult.inputConsumed);
247b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        } else if (opResult.inputConsumed > chunk.length) {
248ad9ba10ecda10c14e46d00f40fc3e431cc2d9bc2Alex Klyubin            throw new IllegalStateException("Keystore consumed more input than provided"
249b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                    + " . Provided: " + chunk.length + ", consumed: " + opResult.inputConsumed);
2504ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
2514ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
25200af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        byte[] result = (opResult.output != null) ? opResult.output : EmptyArray.BYTE;
25300af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        mProducedOutputSizeBytes += result.length;
25400af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        return result;
25500af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    }
25600af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin
25700af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    @Override
25800af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    public long getConsumedInputSizeBytes() {
25900af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        return mConsumedInputSizeBytes;
26000af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    }
26100af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin
26200af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    @Override
26300af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin    public long getProducedOutputSizeBytes() {
26400af27b7d9010eb41e45959dab7c4ff6de119897Alex Klyubin        return mProducedOutputSizeBytes;
265b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    }
266b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
267b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    /**
268b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * Main data stream via a KeyStore streaming operation.
269b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     *
270b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * <p>For example, for an encryption operation, this is the stream through which plaintext is
271b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * provided and ciphertext is obtained.
272b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     */
273b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    public static class MainDataStream implements Stream {
274b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
275b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        private final KeyStore mKeyStore;
276b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        private final IBinder mOperationToken;
277b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
278b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        public MainDataStream(KeyStore keyStore, IBinder operationToken) {
279b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            mKeyStore = keyStore;
280b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            mOperationToken = operationToken;
281b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        }
282b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
283b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        @Override
284b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        public OperationResult update(byte[] input) {
285b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            return mKeyStore.update(mOperationToken, null, input);
286b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        }
287b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
288b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        @Override
289a72b55195c23fc06d1600efe8f6aac85290c7f8fAlex Klyubin        public OperationResult finish(byte[] additionalEntropy) {
290a72b55195c23fc06d1600efe8f6aac85290c7f8fAlex Klyubin            return mKeyStore.finish(mOperationToken, null, null, additionalEntropy);
2914ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
2924ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
2934ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin}
294