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