Search in sources :

Example 31 with OperationResult

use of android.security.keymaster.OperationResult in project android_frameworks_base by DirtyUnicorns.

the class KeyStoreTest method testOperationPruning.

// This is a very implementation specific test and should be thrown out eventually, however it
// is nice for now to test that keystore is properly pruning operations.
public void testOperationPruning() throws Exception {
    String name = "test";
    KeymasterArguments args = new KeymasterArguments();
    args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
    args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
    args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
    args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
    args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
    args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR);
    args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
    KeyCharacteristics outCharacteristics = new KeyCharacteristics();
    int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
    assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
    args = new KeymasterArguments();
    args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
    args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR);
    args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
    OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, null);
    assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
    IBinder first = result.token;
    // Implementation detail: softkeymaster supports 16 concurrent operations
    for (int i = 0; i < 16; i++) {
        result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, null);
        assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
    }
    // At this point the first operation should be pruned.
    assertEquals("Operation should be pruned", KeymasterDefs.KM_ERROR_INVALID_OPERATION_HANDLE, mKeyStore.update(first, null, new byte[] { 0x01 }).resultCode);
}
Also used : IBinder(android.os.IBinder) KeymasterArguments(android.security.keymaster.KeymasterArguments) KeyCharacteristics(android.security.keymaster.KeyCharacteristics) OperationResult(android.security.keymaster.OperationResult)

Example 32 with OperationResult

use of android.security.keymaster.OperationResult in project platform_frameworks_base by android.

the class KeyStoreCryptoOperationChunkedStreamer method update.

@Override
public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException {
    if (inputLength == 0) {
        // No input provided
        return EmptyArray.BYTE;
    }
    ByteArrayOutputStream bufferedOutput = null;
    while (inputLength > 0) {
        byte[] chunk;
        int inputBytesInChunk;
        if ((mBufferedLength + inputLength) > mMaxChunkSize) {
            // Too much input for one chunk -- extract one max-sized chunk and feed it into the
            // update operation.
            inputBytesInChunk = mMaxChunkSize - mBufferedLength;
            chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength, input, inputOffset, inputBytesInChunk);
        } else {
            // All of available input fits into one chunk.
            if ((mBufferedLength == 0) && (inputOffset == 0) && (inputLength == input.length)) {
                // Nothing buffered and all of input array needs to be fed into the update
                // operation.
                chunk = input;
                inputBytesInChunk = input.length;
            } else {
                // Need to combine buffered data with input data into one array.
                inputBytesInChunk = inputLength;
                chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength, input, inputOffset, inputBytesInChunk);
            }
        }
        // Update input array references to reflect that some of its bytes are now in mBuffered.
        inputOffset += inputBytesInChunk;
        inputLength -= inputBytesInChunk;
        mConsumedInputSizeBytes += inputBytesInChunk;
        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 == chunk.length) {
            // The whole chunk was consumed
            mBuffered = EmptyArray.BYTE;
            mBufferedOffset = 0;
            mBufferedLength = 0;
        } else if (opResult.inputConsumed <= 0) {
            // Nothing was consumed. More input needed.
            if (inputLength > 0) {
                // Shouldn't have happened.
                throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR, "Keystore consumed nothing from max-sized chunk: " + chunk.length + " bytes");
            }
            mBuffered = chunk;
            mBufferedOffset = 0;
            mBufferedLength = chunk.length;
        } else if (opResult.inputConsumed < chunk.length) {
            // The chunk was consumed only partially -- buffer the rest of the chunk
            mBuffered = chunk;
            mBufferedOffset = opResult.inputConsumed;
            mBufferedLength = chunk.length - opResult.inputConsumed;
        } else {
            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)) {
            if (inputLength > 0) {
                // More output might be produced in this loop -- buffer the current output
                if (bufferedOutput == null) {
                    bufferedOutput = new ByteArrayOutputStream();
                    try {
                        bufferedOutput.write(opResult.output);
                    } catch (IOException e) {
                        throw new ProviderException("Failed to buffer output", e);
                    }
                }
            } else {
                // No more output will be produced in this loop
                byte[] result;
                if (bufferedOutput == null) {
                    // No previously buffered output
                    result = opResult.output;
                } else {
                    // There was some previously buffered output
                    try {
                        bufferedOutput.write(opResult.output);
                    } catch (IOException e) {
                        throw new ProviderException("Failed to buffer output", e);
                    }
                    result = bufferedOutput.toByteArray();
                }
                mProducedOutputSizeBytes += result.length;
                return result;
            }
        }
    }
    byte[] result;
    if (bufferedOutput == null) {
        // No output produced
        result = EmptyArray.BYTE;
    } else {
        result = bufferedOutput.toByteArray();
    }
    mProducedOutputSizeBytes += result.length;
    return result;
}
Also used : ProviderException(java.security.ProviderException) OperationResult(android.security.keymaster.OperationResult) ByteArrayOutputStream(java.io.ByteArrayOutputStream) KeyStoreException(android.security.KeyStoreException) IOException(java.io.IOException)

Example 33 with OperationResult

use of android.security.keymaster.OperationResult in project platform_frameworks_base by android.

the class KeyStoreCryptoOperationChunkedStreamer method flush.

public byte[] flush() throws KeyStoreException {
    if (mBufferedLength <= 0) {
        return EmptyArray.BYTE;
    }
    // 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);
        }
        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 (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 = (bufferedOutput != null) ? bufferedOutput.toByteArray() : EmptyArray.BYTE;
    mProducedOutputSizeBytes += result.length;
    return result;
}
Also used : ProviderException(java.security.ProviderException) OperationResult(android.security.keymaster.OperationResult) ByteArrayOutputStream(java.io.ByteArrayOutputStream) KeyStoreException(android.security.KeyStoreException) IOException(java.io.IOException)

Example 34 with OperationResult

use of android.security.keymaster.OperationResult in project platform_frameworks_base by android.

the class KeyStoreTest method testAuthNeeded.

public void testAuthNeeded() throws Exception {
    String name = "test";
    KeymasterArguments args = new KeymasterArguments();
    args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
    args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
    args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
    args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_PKCS7);
    args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
    args.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
    args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 1);
    KeyCharacteristics outCharacteristics = new KeyCharacteristics();
    int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
    assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
    OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, null);
    assertEquals("Begin should expect authorization", KeyStore.OP_AUTH_NEEDED, result.resultCode);
    IBinder token = result.token;
    result = mKeyStore.update(token, null, new byte[] { 0x01, 0x02, 0x03, 0x04 });
    assertEquals("Update should require authorization", KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED, result.resultCode);
}
Also used : IBinder(android.os.IBinder) KeymasterArguments(android.security.keymaster.KeymasterArguments) KeyCharacteristics(android.security.keymaster.KeyCharacteristics) OperationResult(android.security.keymaster.OperationResult)

Example 35 with OperationResult

use of android.security.keymaster.OperationResult in project platform_frameworks_base by android.

the class AndroidKeyStoreHmacSpi method ensureKeystoreOperationInitialized.

private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
    if (mChunkedStreamer != null) {
        return;
    }
    if (mKey == null) {
        throw new IllegalStateException("Not initialized");
    }
    KeymasterArguments keymasterArgs = new KeymasterArguments();
    keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC);
    keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
    keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mMacSizeBits);
    OperationResult opResult = mKeyStore.begin(mKey.getAlias(), KeymasterDefs.KM_PURPOSE_SIGN, true, keymasterArgs, // no additional entropy needed for HMAC because it's deterministic
    null, mKey.getUid());
    if (opResult == null) {
        throw new KeyStoreConnectException();
    }
    // Store operation token and handle regardless of the error code returned by KeyStore to
    // ensure that the operation gets aborted immediately if the code below throws an exception.
    mOperationToken = opResult.token;
    mOperationHandle = opResult.operationHandle;
    // If necessary, throw an exception due to KeyStore operation having failed.
    InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(mKeyStore, mKey, opResult.resultCode);
    if (e != null) {
        throw e;
    }
    if (mOperationToken == null) {
        throw new ProviderException("Keystore returned null operation token");
    }
    if (mOperationHandle == 0) {
        throw new ProviderException("Keystore returned invalid operation handle");
    }
    mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(mKeyStore, mOperationToken));
}
Also used : KeymasterArguments(android.security.keymaster.KeymasterArguments) ProviderException(java.security.ProviderException) OperationResult(android.security.keymaster.OperationResult) InvalidKeyException(java.security.InvalidKeyException)

Aggregations

OperationResult (android.security.keymaster.OperationResult)55 KeymasterArguments (android.security.keymaster.KeymasterArguments)30 IBinder (android.os.IBinder)25 ProviderException (java.security.ProviderException)25 KeyCharacteristics (android.security.keymaster.KeyCharacteristics)15 InvalidKeyException (java.security.InvalidKeyException)15 KeyStoreException (android.security.KeyStoreException)10 ByteArrayOutputStream (java.io.ByteArrayOutputStream)10 IOException (java.io.IOException)10 Binder (android.os.Binder)5 GeneralSecurityException (java.security.GeneralSecurityException)5 InvalidAlgorithmParameterException (java.security.InvalidAlgorithmParameterException)5