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