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