Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e2b9bb45 authored by Alex Klyubin's avatar Alex Klyubin Committed by Gerrit Code Review
Browse files

Merge "No runtime exceptions during normal use of AndroidKeyStore crypto."

parents 71223ebe ad9ba10e
Loading
Loading
Loading
Loading
+0 −61
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.security;

/**
 * Base class for exceptions during cryptographic operations which cannot throw a suitable checked
 * exception.
 *
 * <p>The contract of the majority of crypto primitives/operations (e.g. {@code Cipher} or
 * {@code Signature}) is that they can throw a checked exception during initialization, but are not
 * permitted to throw a checked exception during operation. Because crypto operations can fail
 * for a variety of reasons after initialization, this base class provides type-safety for unchecked
 * exceptions that may be thrown in those cases.
 *
 * @hide
 */
public class CryptoOperationException extends RuntimeException {

    /**
     * Constructs a new {@code CryptoOperationException} without detail message and cause.
     */
    public CryptoOperationException() {
        super();
    }

    /**
     * Constructs a new {@code CryptoOperationException} with the provided detail message and no
     * cause.
     */
    public CryptoOperationException(String message) {
        super(message);
    }

    /**
     * Constructs a new {@code CryptoOperationException} with the provided detail message and cause.
     */
    public CryptoOperationException(String message, Throwable cause) {
        super(message, cause);
    }

    /**
     * Constructs a new {@code CryptoOperationException} with the provided cause.
     */
    public CryptoOperationException(Throwable cause) {
        super(cause);
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -16,13 +16,15 @@

package android.security;

import java.security.InvalidKeyException;

/**
 * Indicates that a cryptographic operation failed because the employed key's validity end date
 * is in the past.
 *
 * @hide
 */
public class KeyExpiredException extends CryptoOperationException {
public class KeyExpiredException extends InvalidKeyException {

    /**
     * Constructs a new {@code KeyExpiredException} without detail message and cause.
+3 −1
Original line number Diff line number Diff line
@@ -16,13 +16,15 @@

package android.security;

import java.security.InvalidKeyException;

/**
 * Indicates that a cryptographic operation failed because the employed key's validity start date
 * is in the future.
 *
 * @hide
 */
public class KeyNotYetValidException extends CryptoOperationException {
public class KeyNotYetValidException extends InvalidKeyException {

    /**
     * Constructs a new {@code KeyNotYetValidException} without detail message and cause.
+18 −5
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.OperationResult;
import android.util.Log;

import java.security.InvalidKeyException;
import java.util.Locale;

/**
@@ -508,7 +509,11 @@ public class KeyStore {
        }
    }

    public static KeyStoreException getKeyStoreException(int errorCode) {
    /**
     * Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error
     * code.
     */
    static KeyStoreException getKeyStoreException(int errorCode) {
        if (errorCode > 0) {
            // KeyStore layer error
            switch (errorCode) {
@@ -544,7 +549,11 @@ public class KeyStore {
        }
    }

    public static CryptoOperationException getCryptoOperationException(KeyStoreException e) {
    /**
     * Returns an {@link InvalidKeyException} corresponding to the provided
     * {@link KeyStoreException}.
     */
    static InvalidKeyException getInvalidKeyException(KeyStoreException e) {
        switch (e.getErrorCode()) {
            case KeymasterDefs.KM_ERROR_KEY_EXPIRED:
                return new KeyExpiredException();
@@ -553,11 +562,15 @@ public class KeyStore {
            case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED:
                return new UserNotAuthenticatedException();
            default:
                return new CryptoOperationException("Crypto operation failed", e);
                return new InvalidKeyException("Keystore operation failed", e);
        }
    }

    public static CryptoOperationException getCryptoOperationException(int errorCode) {
        return getCryptoOperationException(getKeyStoreException(errorCode));
    /**
     * Returns an {@link InvalidKeyException} corresponding to the provided keystore/keymaster error
     * code.
     */
    static InvalidKeyException getInvalidKeyException(int errorCode) {
        return getInvalidKeyException(getKeyStoreException(errorCode));
    }
}
+50 −10
Original line number Diff line number Diff line
@@ -136,6 +136,14 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry
    private Long mOperationHandle;
    private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer;

    /**
     * Encountered exception which could not be immediately thrown because it was encountered inside
     * a method that does not throw checked exception. This exception will be thrown from
     * {@code engineDoFinal}. Once such an exception is encountered, {@code engineUpdate} and
     * {@code engineDoFinal} start ignoring input data.
     */
    private Exception mCachedException;

    protected KeyStoreCipherSpi(
            int keymasterAlgorithm,
            int keymasterBlockMode,
@@ -158,7 +166,11 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry
        try {
            init(opmode, key, random);
            initAlgorithmSpecificParameters();
            try {
                ensureKeystoreOperationInitialized();
            } catch (InvalidAlgorithmParameterException e) {
                throw new InvalidKeyException(e);
            }
            success = true;
        } finally {
            if (!success) {
@@ -236,6 +248,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry
        mOperationToken = null;
        mOperationHandle = null;
        mMainDataStreamer = null;
        mCachedException = null;
    }

    private void resetWhilePreservingInitState() {
@@ -247,12 +260,17 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry
        mOperationHandle = null;
        mMainDataStreamer = null;
        mAdditionalEntropyForBegin = null;
        mCachedException = null;
    }

    private void ensureKeystoreOperationInitialized() {
    private void ensureKeystoreOperationInitialized() throws InvalidKeyException,
            InvalidAlgorithmParameterException {
        if (mMainDataStreamer != null) {
            return;
        }
        if (mCachedException != null) {
            return;
        }
        if (mKey == null) {
            throw new IllegalStateException("Not initialized");
        }
@@ -281,11 +299,15 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry
        if (opResult == null) {
            throw new KeyStoreConnectException();
        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
            throw KeyStore.getCryptoOperationException(opResult.resultCode);
            switch (opResult.resultCode) {
                case KeymasterDefs.KM_ERROR_INVALID_NONCE:
                    throw new InvalidAlgorithmParameterException("Invalid IV");
            }
            throw KeyStore.getInvalidKeyException(opResult.resultCode);
        }

        if (opResult.token == null) {
            throw new CryptoOperationException("Keystore returned null operation token");
            throw new IllegalStateException("Keystore returned null operation token");
        }
        mOperationToken = opResult.token;
        mOperationHandle = opResult.operationHandle;
@@ -299,7 +321,15 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry

    @Override
    protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
        if (mCachedException != null) {
            return null;
        }
        try {
            ensureKeystoreOperationInitialized();
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            mCachedException = e;
            return null;
        }

        if (inputLen == 0) {
            return null;
@@ -309,7 +339,8 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry
        try {
            output = mMainDataStreamer.update(input, inputOffset, inputLen);
        } catch (KeyStoreException e) {
            throw KeyStore.getCryptoOperationException(e);
            mCachedException = e;
            return null;
        }

        if (output.length == 0) {
@@ -338,7 +369,16 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry
    @Override
    protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
            throws IllegalBlockSizeException, BadPaddingException {
        if (mCachedException != null) {
            throw (IllegalBlockSizeException)
                    new IllegalBlockSizeException().initCause(mCachedException);
        }

        try {
            ensureKeystoreOperationInitialized();
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
        }

        byte[] output;
        try {
@@ -352,7 +392,7 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry
                case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
                    throw new AEADBadTagException();
                default:
                    throw KeyStore.getCryptoOperationException(e);
                    throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
            }
        }

@@ -613,11 +653,11 @@ public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCry
            if (mIv == null) {
                mIv = returnedIv;
            } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
                throw new CryptoOperationException("IV in use differs from provided IV");
                throw new IllegalStateException("IV in use differs from provided IV");
            }
        } else {
            if (returnedIv != null) {
                throw new CryptoOperationException(
                throw new IllegalStateException(
                        "IV in use despite IV not being used by this transformation");
            }
        }
Loading