KeyStoreCryptoOperationChunkedStreamer.java revision cc21bb3a56915842b545a577d3481047005b1764
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
174ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubinpackage android.security;
184ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
19b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubinimport android.os.IBinder;
204ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubinimport android.security.keymaster.OperationResult;
214ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
224ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubinimport java.io.ByteArrayOutputStream;
234ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubinimport java.io.IOException;
244ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
254ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin/**
264ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
274ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * {@code update} and {@code finish} operations.
284ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin *
294ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
30b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
31b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * amount of data in one go because the operations are marshalled via Binder. Secondly, the update
32b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * operation may consume less data than provided, in which case the caller has to buffer the
33b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
344ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * {@link #doFinal(byte[], int, int) doFinal} operations which can be used to conveniently implement
354ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * various JCA crypto primitives.
364ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin *
37b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * <p>Bidirectional chunked streaming of data via a KeyStore crypto operation is abstracted away as
38b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * a {@link Stream} to avoid having this class deal with operation tokens and occasional additional
39b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin * parameters to {@code update} and {@code final} operations.
404ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin *
414ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin * @hide
424ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin */
434ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubinpublic class KeyStoreCryptoOperationChunkedStreamer {
44b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
45b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    /**
46b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * Bidirectional chunked data stream over a KeyStore crypto operation.
47b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     */
48b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    public interface Stream {
494ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        /**
50b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin         * Returns the result of the KeyStore {@code update} operation or null if keystore couldn't
51b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin         * be reached.
524ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin         */
534ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        OperationResult update(byte[] input);
544ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
554ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        /**
56b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin         * Returns the result of the KeyStore {@code finish} operation or null if keystore couldn't
57b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin         * be reached.
584ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin         */
59b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        OperationResult finish();
604ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
614ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
624ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    // Binder buffer is about 1MB, but it's shared between all active transactions of the process.
634ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    // Thus, it's safer to use a much smaller upper bound.
644ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
654ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
664ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
67b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    private final Stream mKeyStoreStream;
684ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private final int mMaxChunkSize;
694ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
704ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private byte[] mBuffered = EMPTY_BYTE_ARRAY;
714ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private int mBufferedOffset;
724ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    private int mBufferedLength;
734ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
74b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    public KeyStoreCryptoOperationChunkedStreamer(Stream operation) {
754ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        this(operation, DEFAULT_MAX_CHUNK_SIZE);
764ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
774ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
78b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    public KeyStoreCryptoOperationChunkedStreamer(Stream operation, int maxChunkSize) {
79b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        mKeyStoreStream = operation;
804ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        mMaxChunkSize = maxChunkSize;
814ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
824ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
834ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeymasterException {
844ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        if (inputLength == 0) {
854ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            // No input provided
864ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            return EMPTY_BYTE_ARRAY;
874ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
884ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
894ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        ByteArrayOutputStream bufferedOutput = null;
904ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
914ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        while (inputLength > 0) {
924ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            byte[] chunk;
934ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            int inputBytesInChunk;
944ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            if ((mBufferedLength + inputLength) > mMaxChunkSize) {
954ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // Too much input for one chunk -- extract one max-sized chunk and feed it into the
964ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // update operation.
97b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                inputBytesInChunk = mMaxChunkSize - mBufferedLength;
98b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                chunk = concat(mBuffered, mBufferedOffset, mBufferedLength,
99b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                        input, inputOffset, inputBytesInChunk);
1004ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else {
1014ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // All of available input fits into one chunk.
1024ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                if ((mBufferedLength == 0) && (inputOffset == 0)
1034ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        && (inputLength == input.length)) {
1044ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // Nothing buffered and all of input array needs to be fed into the update
1054ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // operation.
1064ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    chunk = input;
1074ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    inputBytesInChunk = input.length;
1084ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                } else {
1094ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // Need to combine buffered data with input data into one array.
1104ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    inputBytesInChunk = inputLength;
111b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                    chunk = concat(mBuffered, mBufferedOffset, mBufferedLength,
112b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                            input, inputOffset, inputBytesInChunk);
1134ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                }
1144ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            }
1154ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            // Update input array references to reflect that some of its bytes are now in mBuffered.
1164ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            inputOffset += inputBytesInChunk;
1174ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            inputLength -= inputBytesInChunk;
1184ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
119b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            OperationResult opResult = mKeyStoreStream.update(chunk);
1204ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            if (opResult == null) {
1214ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                throw new KeyStoreConnectException();
1224ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else if (opResult.resultCode != KeyStore.NO_ERROR) {
1234ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
1244ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            }
1254ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
1264ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            if (opResult.inputConsumed == chunk.length) {
1274ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // The whole chunk was consumed
1284ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBuffered = EMPTY_BYTE_ARRAY;
1294ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedOffset = 0;
1304ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedLength = 0;
1314ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else if (opResult.inputConsumed == 0) {
1324ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // Nothing was consumed. More input needed.
1334ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                if (inputLength > 0) {
1344ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // More input is available, but it wasn't included into the previous chunk
1354ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // because the chunk reached its maximum permitted size.
1364ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // Shouldn't have happened.
1374ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    throw new CryptoOperationException("Nothing consumed from max-sized chunk: "
1384ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                            + chunk.length + " bytes");
1394ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                }
1404ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBuffered = chunk;
1414ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedOffset = 0;
1424ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedLength = chunk.length;
1434ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else if (opResult.inputConsumed < chunk.length) {
1444ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                // The chunk was consumed only partially -- buffer the rest of the chunk
1454ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBuffered = chunk;
1464ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedOffset = opResult.inputConsumed;
1474ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                mBufferedLength = chunk.length - opResult.inputConsumed;
1484ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            } else {
1494ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                throw new CryptoOperationException("Consumed more than provided: "
1504ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        + opResult.inputConsumed + ", provided: " + chunk.length);
1514ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            }
1524ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
1534ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            if ((opResult.output != null) && (opResult.output.length > 0)) {
1544ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                if (inputLength > 0) {
1554ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // More output might be produced in this loop -- buffer the current output
1564ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    if (bufferedOutput == null) {
1574ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        bufferedOutput = new ByteArrayOutputStream();
1584ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        try {
1594ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                            bufferedOutput.write(opResult.output);
1604ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        } catch (IOException e) {
1614ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                            throw new CryptoOperationException("Failed to buffer output", e);
1624ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        }
1634ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    }
1644ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                } else {
1654ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    // No more output will be produced in this loop
1664ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    if (bufferedOutput == null) {
1674ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        // No previously buffered output
1684ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        return opResult.output;
1694ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    } else {
1704ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        // There was some previously buffered output
1714ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        try {
1724ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                            bufferedOutput.write(opResult.output);
1734ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        } catch (IOException e) {
1744ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                            throw new CryptoOperationException("Failed to buffer output", e);
1754ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        }
1764ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                        return bufferedOutput.toByteArray();
1774ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                    }
1784ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin                }
1794ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            }
1804ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
1814ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
1824ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        if (bufferedOutput == null) {
1834ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            // No output produced
1844ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            return EMPTY_BYTE_ARRAY;
1854ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        } else {
1864ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            return bufferedOutput.toByteArray();
1874ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
1884ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
1894ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
1904ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    public byte[] doFinal(byte[] input, int inputOffset, int inputLength)
1914ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            throws KeymasterException {
1924ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        if (inputLength == 0) {
1934ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            // No input provided -- simplify the rest of the code
1944ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            input = EMPTY_BYTE_ARRAY;
1954ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            inputOffset = 0;
1964ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
1974ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
198b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        // Flush all buffered input and provided input into keystore/keymaster.
199b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        byte[] output = update(input, inputOffset, inputLength);
200b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        output = concat(output, flush());
201b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
202b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        OperationResult opResult = mKeyStoreStream.finish();
203b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        if (opResult == null) {
204b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            throw new KeyStoreConnectException();
205b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
206b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
2074ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
2084ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
209b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        return concat(output, opResult.output);
210b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    }
211b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
212b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    /**
213b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * Passes all of buffered input into the the KeyStore operation (via the {@code update}
214b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * operation) and returns output.
215b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     */
216b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    public byte[] flush() throws KeymasterException {
217b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        if (mBufferedLength <= 0) {
218b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            return EMPTY_BYTE_ARRAY;
2194ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
220b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
221b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        byte[] chunk = subarray(mBuffered, mBufferedOffset, mBufferedLength);
2224ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        mBuffered = EMPTY_BYTE_ARRAY;
2234ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        mBufferedLength = 0;
2244ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        mBufferedOffset = 0;
2254ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
226b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        OperationResult opResult = mKeyStoreStream.update(chunk);
2274ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        if (opResult == null) {
2284ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            throw new KeyStoreConnectException();
2294ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
2304ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin            throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
2314ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
2324ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
233b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        if (opResult.inputConsumed < chunk.length) {
234b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            throw new CryptoOperationException("Keystore failed to consume all input. Provided: "
235b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                    + chunk.length + ", consumed: " + opResult.inputConsumed);
236b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        } else if (opResult.inputConsumed > chunk.length) {
237b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            throw new CryptoOperationException("Keystore consumed more input than provided"
238b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin                    + " . Provided: " + chunk.length + ", consumed: " + opResult.inputConsumed);
2394ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
2404ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin
241b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        return (opResult.output != null) ? opResult.output : EMPTY_BYTE_ARRAY;
242b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    }
243b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
244b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    private static byte[] concat(byte[] arr1, byte[] arr2) {
245b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        if ((arr1 == null) || (arr1.length == 0)) {
246b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            return arr2;
247b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        } else if ((arr2 == null) || (arr2.length == 0)) {
248b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            return arr1;
2494ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        } else {
250b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            byte[] result = new byte[arr1.length + arr2.length];
251b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            System.arraycopy(arr1, 0, result, 0, arr1.length);
252b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
253b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            return result;
254b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        }
255b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    }
256b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
257b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    private static byte[] concat(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2,
258b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            int len2) {
259b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        if (len1 == 0) {
260b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            return subarray(arr2, offset2, len2);
261b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        } else if (len2 == 0) {
262b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            return subarray(arr1, offset1, len1);
263b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        } else {
264b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            byte[] result = new byte[len1 + len2];
265b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            System.arraycopy(arr1, offset1, result, 0, len1);
266b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            System.arraycopy(arr2, offset2, result, len1, len2);
267b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            return result;
268b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        }
269b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    }
270b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
271b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    private static byte[] subarray(byte[] arr, int offset, int len) {
272b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        if (len == 0) {
273b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            return EMPTY_BYTE_ARRAY;
274b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        }
275b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        if ((offset == 0) && (len == arr.length)) {
276b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            return arr;
277b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        }
278b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        byte[] result = new byte[len];
279b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        System.arraycopy(arr, offset, result, 0, len);
280b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        return result;
281b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    }
282b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
283b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    /**
284b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * Main data stream via a KeyStore streaming operation.
285b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     *
286b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * <p>For example, for an encryption operation, this is the stream through which plaintext is
287b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     * provided and ciphertext is obtained.
288b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin     */
289b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin    public static class MainDataStream implements Stream {
290b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
291b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        private final KeyStore mKeyStore;
292b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        private final IBinder mOperationToken;
293b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
294b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        public MainDataStream(KeyStore keyStore, IBinder operationToken) {
295b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            mKeyStore = keyStore;
296b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            mOperationToken = operationToken;
297b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        }
298b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
299b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        @Override
300b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        public OperationResult update(byte[] input) {
301b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            return mKeyStore.update(mOperationToken, null, input);
302b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        }
303b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin
304b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        @Override
305b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin        public OperationResult finish() {
306b406f242911fa4d910a4cf915a61e39aeace1e1bAlex Klyubin            return mKeyStore.finish(mOperationToken, null, null);
3074ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin        }
3084ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin    }
3094ab8ea4498aa25eafdbaadd238fed6eab3f6ee59Alex Klyubin}
310