summaryrefslogtreecommitdiffstats
path: root/keystore
diff options
context:
space:
mode:
authorAlex Klyubin <klyubin@google.com>2015-07-15 17:15:08 -0700
committerAlex Klyubin <klyubin@google.com>2015-07-16 14:18:49 -0700
commit53d544a4b9964166f90f34b46f3866cafefb84e7 (patch)
tree31072f3652496e693cc4791e49c4cdb713f8ba97 /keystore
parentb09b84cafde5ac31f5ef5c79f7a78db540824471 (diff)
downloadframeworks_base-53d544a4b9964166f90f34b46f3866cafefb84e7.zip
frameworks_base-53d544a4b9964166f90f34b46f3866cafefb84e7.tar.gz
frameworks_base-53d544a4b9964166f90f34b46f3866cafefb84e7.tar.bz2
KM module may consume less input than provided by finish time.
Keymaster1 HAL permits the implementation of "update" operation to leave some input unconsumed by the time "finish" operation neeeds to be invoked. This needs to be treated as "invalid input" error rather than a "can't happen" exception. This CL was confirmed to fix the issue by the vendor who encountered the issue. Bug: 22512100 Change-Id: Ibb1a37d58f650d03605612559a154ce2416d147c
Diffstat (limited to 'keystore')
-rw-r--r--keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java94
1 files changed, 71 insertions, 23 deletions
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
index ea0f4b9..dbb79bc 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
@@ -19,12 +19,14 @@ package android.security.keystore;
import android.os.IBinder;
import android.security.KeyStore;
import android.security.KeyStoreException;
+import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.OperationResult;
import libcore.util.EmptyArray;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.security.ProviderException;
/**
* Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
@@ -135,14 +137,15 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
mBuffered = EmptyArray.BYTE;
mBufferedOffset = 0;
mBufferedLength = 0;
- } else if (opResult.inputConsumed == 0) {
+ } else if (opResult.inputConsumed <= 0) {
// Nothing was consumed. More input needed.
if (inputLength > 0) {
// More input is available, but it wasn't included into the previous chunk
// because the chunk reached its maximum permitted size.
// Shouldn't have happened.
- throw new IllegalStateException("Nothing consumed from max-sized chunk: "
- + chunk.length + " bytes");
+ throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
+ "Keystore consumed nothing from max-sized chunk: " + chunk.length
+ + " bytes");
}
mBuffered = chunk;
mBufferedOffset = 0;
@@ -153,8 +156,9 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
mBufferedOffset = opResult.inputConsumed;
mBufferedLength = chunk.length - opResult.inputConsumed;
} else {
- throw new IllegalStateException("Consumed more than provided: "
- + opResult.inputConsumed + ", provided: " + chunk.length);
+ throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
+ "Keystore consumed more input than provided. Provided: " + chunk.length
+ + ", consumed: " + opResult.inputConsumed);
}
if ((opResult.output != null) && (opResult.output.length > 0)) {
@@ -165,7 +169,7 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
try {
bufferedOutput.write(opResult.output);
} catch (IOException e) {
- throw new IllegalStateException("Failed to buffer output", e);
+ throw new ProviderException("Failed to buffer output", e);
}
}
} else {
@@ -179,7 +183,7 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
try {
bufferedOutput.write(opResult.output);
} catch (IOException e) {
- throw new IllegalStateException("Failed to buffer output", e);
+ throw new ProviderException("Failed to buffer output", e);
}
result = bufferedOutput.toByteArray();
}
@@ -229,27 +233,71 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS
return EmptyArray.BYTE;
}
- byte[] chunk = ArrayUtils.subarray(mBuffered, mBufferedOffset, mBufferedLength);
- mBuffered = EmptyArray.BYTE;
- mBufferedLength = 0;
- mBufferedOffset = 0;
+ // Keep invoking the update operation with remaining buffered data until either all of the
+ // buffered data is consumed or until update fails to consume anything.
+ ByteArrayOutputStream bufferedOutput = null;
+ while (mBufferedLength > 0) {
+ byte[] chunk = ArrayUtils.subarray(mBuffered, mBufferedOffset, mBufferedLength);
+ OperationResult opResult = mKeyStoreStream.update(chunk);
+ if (opResult == null) {
+ throw new KeyStoreConnectException();
+ } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+ throw KeyStore.getKeyStoreException(opResult.resultCode);
+ }
- OperationResult opResult = mKeyStoreStream.update(chunk);
- if (opResult == null) {
- throw new KeyStoreConnectException();
- } else if (opResult.resultCode != KeyStore.NO_ERROR) {
- throw KeyStore.getKeyStoreException(opResult.resultCode);
+ if (opResult.inputConsumed <= 0) {
+ // Nothing was consumed. Break out of the loop to avoid an infinite loop.
+ break;
+ }
+
+ if (opResult.inputConsumed >= chunk.length) {
+ // All of the input was consumed
+ mBuffered = EmptyArray.BYTE;
+ mBufferedOffset = 0;
+ mBufferedLength = 0;
+ } else {
+ // Some of the input was not consumed
+ mBuffered = chunk;
+ mBufferedOffset = opResult.inputConsumed;
+ mBufferedLength = chunk.length - opResult.inputConsumed;
+ }
+
+ if (opResult.inputConsumed > chunk.length) {
+ throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
+ "Keystore consumed more input than provided. Provided: "
+ + chunk.length + ", consumed: " + opResult.inputConsumed);
+ }
+
+ if ((opResult.output != null) && (opResult.output.length > 0)) {
+ // Some output was produced by this update operation
+ if (bufferedOutput == null) {
+ // No output buffered yet.
+ if (mBufferedLength == 0) {
+ // No more output will be produced by this flush operation
+ mProducedOutputSizeBytes += opResult.output.length;
+ return opResult.output;
+ } else {
+ // More output might be produced by this flush operation -- buffer output.
+ bufferedOutput = new ByteArrayOutputStream();
+ }
+ }
+ // Buffer the output from this update operation
+ try {
+ bufferedOutput.write(opResult.output);
+ } catch (IOException e) {
+ throw new ProviderException("Failed to buffer output", e);
+ }
+ }
}
- if (opResult.inputConsumed < chunk.length) {
- throw new IllegalStateException("Keystore failed to consume all input. Provided: "
- + chunk.length + ", consumed: " + opResult.inputConsumed);
- } else if (opResult.inputConsumed > chunk.length) {
- throw new IllegalStateException("Keystore consumed more input than provided"
- + " . Provided: " + chunk.length + ", consumed: " + opResult.inputConsumed);
+ if (mBufferedLength > 0) {
+ throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
+ "Keystore failed to consume last "
+ + ((mBufferedLength != 1) ? (mBufferedLength + " bytes") : "byte")
+ + " of input");
}
- byte[] result = (opResult.output != null) ? opResult.output : EmptyArray.BYTE;
+ byte[] result = (bufferedOutput != null) ? bufferedOutput.toByteArray() : EmptyArray.BYTE;
mProducedOutputSizeBytes += result.length;
return result;
}