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

Commit 5eedf5a3 authored by Eran Messeri's avatar Eran Messeri
Browse files

Keystore: Surface service error message

Surface the service-specific error message. To avoid API changes, the
error message is surfaced in the toString / getMessage methods.

Test: atest android.security.keystore.KeyStoreExceptionTest
Bug: 217593122
Change-Id: Id4090564b46db9b3b10ea390390f6683f7314463
parent 9b73fc72
Loading
Loading
Loading
Loading
+21 −13
Original line number Diff line number Diff line
@@ -108,7 +108,7 @@ public class KeyStore2 {
            try {
                return request.execute(service);
            } catch (ServiceSpecificException e) {
                throw getKeyStoreException(e.errorCode);
                throw getKeyStoreException(e.errorCode, e.getMessage());
            } catch (RemoteException e) {
                if (firstTry) {
                    Log.w(TAG, "Looks like we may have lost connection to the Keystore "
@@ -120,7 +120,7 @@ public class KeyStore2 {
                    firstTry = false;
                } else {
                    Log.e(TAG, "Cannot connect to Keystore daemon.", e);
                    throw new KeyStoreException(ResponseCode.SYSTEM_ERROR, "");
                    throw new KeyStoreException(ResponseCode.SYSTEM_ERROR, "", e.getMessage());
                }
            }
        }
@@ -322,26 +322,32 @@ public class KeyStore2 {
        }
    }

    static KeyStoreException getKeyStoreException(int errorCode) {
    static KeyStoreException getKeyStoreException(int errorCode, String serviceErrorMessage) {
        if (errorCode > 0) {
            // KeyStore layer error
            switch (errorCode) {
                case ResponseCode.LOCKED:
                    return new KeyStoreException(errorCode, "User authentication required");
                    return new KeyStoreException(errorCode, "User authentication required",
                            serviceErrorMessage);
                case ResponseCode.UNINITIALIZED:
                    return new KeyStoreException(errorCode, "Keystore not initialized");
                    return new KeyStoreException(errorCode, "Keystore not initialized",
                            serviceErrorMessage);
                case ResponseCode.SYSTEM_ERROR:
                    return new KeyStoreException(errorCode, "System error");
                    return new KeyStoreException(errorCode, "System error", serviceErrorMessage);
                case ResponseCode.PERMISSION_DENIED:
                    return new KeyStoreException(errorCode, "Permission denied");
                    return new KeyStoreException(errorCode, "Permission denied",
                            serviceErrorMessage);
                case ResponseCode.KEY_NOT_FOUND:
                    return new KeyStoreException(errorCode, "Key not found");
                    return new KeyStoreException(errorCode, "Key not found", serviceErrorMessage);
                case ResponseCode.VALUE_CORRUPTED:
                    return new KeyStoreException(errorCode, "Key blob corrupted");
                    return new KeyStoreException(errorCode, "Key blob corrupted",
                            serviceErrorMessage);
                case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
                    return new KeyStoreException(errorCode, "Key permanently invalidated");
                    return new KeyStoreException(errorCode, "Key permanently invalidated",
                            serviceErrorMessage);
                default:
                    return new KeyStoreException(errorCode, String.valueOf(errorCode));
                    return new KeyStoreException(errorCode, String.valueOf(errorCode),
                            serviceErrorMessage);
            }
        } else {
            // Keymaster layer error
@@ -350,10 +356,12 @@ public class KeyStore2 {
                    // The name of this parameter significantly differs between Keymaster and
                    // framework APIs. Use the framework wording to make life easier for developers.
                    return new KeyStoreException(errorCode,
                            "Invalid user authentication validity duration");
                            "Invalid user authentication validity duration",
                            serviceErrorMessage);
                default:
                    return new KeyStoreException(errorCode,
                            KeymasterDefs.getErrorMessage(errorCode));
                            KeymasterDefs.getErrorMessage(errorCode),
                            serviceErrorMessage);
            }
        }
    }
+10 −0
Original line number Diff line number Diff line
@@ -157,6 +157,16 @@ public class KeyStoreException extends Exception {
        mErrorCode = errorCode;
    }

    /**
     * @hide
     */
    public KeyStoreException(int errorCode, @Nullable String message,
            @Nullable String keystoreErrorMessage) {
        super(message + " (internal Keystore code: " + errorCode + " message: "
                + keystoreErrorMessage + ")");
        mErrorCode = errorCode;
    }

    /**
     * Returns the internal error code. Only for use by the platform.
     *
+3 −2
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ public class KeyStoreOperation {
                    );
                }
                default:
                    throw KeyStore2.getKeyStoreException(e.errorCode);
                    throw KeyStore2.getKeyStoreException(e.errorCode, e.getMessage());
            }
        } catch (RemoteException e) {
            // Log exception and report invalid operation handle.
@@ -85,7 +85,8 @@ public class KeyStoreOperation {
                    "Remote exception while advancing a KeyStoreOperation.",
                    e
            );
            throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_OPERATION_HANDLE, "");
            throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_OPERATION_HANDLE, "",
                    e.getMessage());
        }
    }

+3 −3
Original line number Diff line number Diff line
@@ -54,12 +54,12 @@ public class KeyStoreSecurityLevel {
        try {
            return request.execute();
        } catch (ServiceSpecificException e) {
            throw KeyStore2.getKeyStoreException(e.errorCode);
            throw KeyStore2.getKeyStoreException(e.errorCode, e.getMessage());
        } catch (RemoteException e) {
            // Log exception and report invalid operation handle.
            // This should prompt the caller drop the reference to this operation and retry.
            Log.e(TAG, "Could not connect to Keystore.", e);
            throw new KeyStoreException(ResponseCode.SYSTEM_ERROR, "");
            throw new KeyStoreException(ResponseCode.SYSTEM_ERROR, "", e.getMessage());
        }
    }

@@ -117,7 +117,7 @@ public class KeyStoreSecurityLevel {
                        break;
                    }
                    default:
                        throw KeyStore2.getKeyStoreException(e.errorCode);
                        throw KeyStore2.getKeyStoreException(e.errorCode, e.getMessage());
                }
            } catch (RemoteException e) {
                Log.w(TAG, "Cannot connect to keystore", e);
+44 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.keystore;

import static org.junit.Assert.assertTrue;

import android.security.KeyStoreException;

import androidx.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class KeyStoreExceptionTest {
    @Test
    public void testKeystoreMessageIsIncluded() {
        final String primaryMessage = "some_message";
        final String keystoreMessage = "ks_message";
        KeyStoreException exception = new KeyStoreException(-1, primaryMessage, keystoreMessage);

        String exceptionMessage = exception.getMessage();
        assertTrue(exceptionMessage.contains(primaryMessage));
        assertTrue(exceptionMessage.contains(keystoreMessage));

        String exceptionString = exception.toString();
        assertTrue(exceptionString.contains(primaryMessage));
        assertTrue(exceptionString.contains(keystoreMessage));
    }
}