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

Commit ba685389 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Keystore APIs for Import Wrapped Key, Strongbox, 3DES"

parents 70553693 21d9c1d4
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -37856,6 +37856,7 @@ package android.security.keystore {
    method public boolean isDigestsSpecified();
    method public boolean isInvalidatedByBiometricEnrollment();
    method public boolean isRandomizedEncryptionRequired();
    method public boolean isStrongBoxBacked();
    method public boolean isUserAuthenticationRequired();
    method public boolean isUserAuthenticationValidWhileOnBody();
  }
@@ -37873,6 +37874,7 @@ package android.security.keystore {
    method public android.security.keystore.KeyGenParameterSpec.Builder setDigests(java.lang.String...);
    method public android.security.keystore.KeyGenParameterSpec.Builder setEncryptionPaddings(java.lang.String...);
    method public android.security.keystore.KeyGenParameterSpec.Builder setInvalidatedByBiometricEnrollment(boolean);
    method public android.security.keystore.KeyGenParameterSpec.Builder setIsStrongBoxBacked(boolean);
    method public android.security.keystore.KeyGenParameterSpec.Builder setKeySize(int);
    method public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityEnd(java.util.Date);
    method public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForConsumptionEnd(java.util.Date);
@@ -37933,6 +37935,7 @@ package android.security.keystore {
    field public static final java.lang.String ENCRYPTION_PADDING_PKCS7 = "PKCS7Padding";
    field public static final java.lang.String ENCRYPTION_PADDING_RSA_OAEP = "OAEPPadding";
    field public static final java.lang.String ENCRYPTION_PADDING_RSA_PKCS1 = "PKCS1Padding";
    field public static final deprecated java.lang.String KEY_ALGORITHM_3DES = "DESede";
    field public static final java.lang.String KEY_ALGORITHM_AES = "AES";
    field public static final java.lang.String KEY_ALGORITHM_EC = "EC";
    field public static final java.lang.String KEY_ALGORITHM_HMAC_SHA1 = "HmacSHA1";
@@ -37943,11 +37946,13 @@ package android.security.keystore {
    field public static final java.lang.String KEY_ALGORITHM_RSA = "RSA";
    field public static final int ORIGIN_GENERATED = 1; // 0x1
    field public static final int ORIGIN_IMPORTED = 2; // 0x2
    field public static final int ORIGIN_SECURELY_IMPORTED = 8; // 0x8
    field public static final int ORIGIN_UNKNOWN = 4; // 0x4
    field public static final int PURPOSE_DECRYPT = 2; // 0x2
    field public static final int PURPOSE_ENCRYPT = 1; // 0x1
    field public static final int PURPOSE_SIGN = 4; // 0x4
    field public static final int PURPOSE_VERIFY = 8; // 0x8
    field public static final int PURPOSE_WRAP_KEY = 32; // 0x20
    field public static final java.lang.String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
    field public static final java.lang.String SIGNATURE_PADDING_RSA_PSS = "PSS";
  }
@@ -37987,12 +37992,24 @@ package android.security.keystore {
    method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int);
  }
  public class StrongBoxUnavailableException extends java.security.ProviderException {
    ctor public StrongBoxUnavailableException();
  }
  public class UserNotAuthenticatedException extends java.security.InvalidKeyException {
    ctor public UserNotAuthenticatedException();
    ctor public UserNotAuthenticatedException(java.lang.String);
    ctor public UserNotAuthenticatedException(java.lang.String, java.lang.Throwable);
  }
  public class WrappedKeyEntry implements java.security.KeyStore.Entry {
    ctor public WrappedKeyEntry(byte[], java.lang.String, java.lang.String, java.security.spec.AlgorithmParameterSpec);
    method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
    method public java.lang.String getTransformation();
    method public byte[] getWrappedKeyBytes();
    method public java.lang.String getWrappingKeyAlias();
  }
}
package android.service.autofill {
+3 −0
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ public final class KeymasterDefs {
    public static final int KM_ALGORITHM_RSA = 1;
    public static final int KM_ALGORITHM_EC = 3;
    public static final int KM_ALGORITHM_AES = 32;
    public static final int KM_ALGORITHM_3DES = 33;
    public static final int KM_ALGORITHM_HMAC = 128;

    // Block modes.
@@ -131,6 +132,7 @@ public final class KeymasterDefs {
    public static final int KM_ORIGIN_GENERATED = 0;
    public static final int KM_ORIGIN_IMPORTED = 2;
    public static final int KM_ORIGIN_UNKNOWN = 3;
    public static final int KM_ORIGIN_SECURELY_IMPORTED = 4;

    // Key usability requirements.
    public static final int KM_BLOB_STANDALONE = 0;
@@ -141,6 +143,7 @@ public final class KeymasterDefs {
    public static final int KM_PURPOSE_DECRYPT = 1;
    public static final int KM_PURPOSE_SIGN = 2;
    public static final int KM_PURPOSE_VERIFY = 3;
    public static final int KM_PURPOSE_WRAP = 5;

    // Key formats.
    public static final int KM_KEY_FORMAT_X509 = 0;
+13 −0
Original line number Diff line number Diff line
@@ -510,6 +510,19 @@ public class KeyStore {
        return importKey(alias, args, format, keyData, UID_SELF, flags, outCharacteristics);
    }

    public int importWrappedKey(String wrappedKeyAlias, byte[] wrappedKey,
            String wrappingKeyAlias,
            byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid, int uid,
            KeyCharacteristics outCharacteristics) {
        try {
            return mBinder.importWrappedKey(wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
                    maskingKey, args, rootSid, fingerprintSid, outCharacteristics);
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return SYSTEM_ERROR;
        }
    }

    public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
            KeymasterBlob appId, int uid) {
        try {
+298 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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 android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;

import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;

import javax.crypto.CipherSpi;
import javax.crypto.spec.IvParameterSpec;

/**
 * Base class for Android Keystore 3DES {@link CipherSpi} implementations.
 *
 * @hide
 */
public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {

    private static final int BLOCK_SIZE_BYTES = 8;

    private final int mKeymasterBlockMode;
    private final int mKeymasterPadding;
    /** Whether this transformation requires an IV. */
    private final boolean mIvRequired;

    private byte[] mIv;

    /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */
    private boolean mIvHasBeenUsed;

    AndroidKeyStore3DESCipherSpi(
            int keymasterBlockMode,
            int keymasterPadding,
            boolean ivRequired) {
        mKeymasterBlockMode = keymasterBlockMode;
        mKeymasterPadding = keymasterPadding;
        mIvRequired = ivRequired;
    }

    abstract static class ECB extends AndroidKeyStore3DESCipherSpi {
        protected ECB(int keymasterPadding) {
            super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false);
        }

        public static class NoPadding extends ECB {
            public NoPadding() {
                super(KeymasterDefs.KM_PAD_NONE);
            }
        }

        public static class PKCS7Padding extends ECB {
            public PKCS7Padding() {
                super(KeymasterDefs.KM_PAD_PKCS7);
            }
        }
    }

    abstract static class CBC extends AndroidKeyStore3DESCipherSpi {
        protected CBC(int keymasterPadding) {
            super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true);
        }

        public static class NoPadding extends CBC {
            public NoPadding() {
                super(KeymasterDefs.KM_PAD_NONE);
            }
        }

        public static class PKCS7Padding extends CBC {
            public PKCS7Padding() {
                super(KeymasterDefs.KM_PAD_PKCS7);
            }
        }
    }

    @Override
    protected void initKey(int i, Key key) throws InvalidKeyException {
        if (!(key instanceof AndroidKeyStoreSecretKey)) {
            throw new InvalidKeyException(
                    "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
        }
        if (!KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(key.getAlgorithm())) {
            throw new InvalidKeyException(
                    "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " +
                            KeyProperties.KEY_ALGORITHM_3DES + " supported");
        }
        setKey((AndroidKeyStoreSecretKey) key);
    }

    @Override
    protected int engineGetBlockSize() {
        return BLOCK_SIZE_BYTES;
    }

    @Override
    protected int engineGetOutputSize(int inputLen) {
        return inputLen + 3 * BLOCK_SIZE_BYTES;
    }

    @Override
    protected final byte[] engineGetIV() {
        return ArrayUtils.cloneIfNotEmpty(mIv);
    }

    @Override
    protected AlgorithmParameters engineGetParameters() {
        if (!mIvRequired) {
            return null;
        }
        if ((mIv != null) && (mIv.length > 0)) {
            try {
                AlgorithmParameters params = AlgorithmParameters.getInstance("DESede");
                params.init(new IvParameterSpec(mIv));
                return params;
            } catch (NoSuchAlgorithmException e) {
                throw new ProviderException(
                        "Failed to obtain 3DES AlgorithmParameters", e);
            } catch (InvalidParameterSpecException e) {
                throw new ProviderException(
                        "Failed to initialize 3DES AlgorithmParameters with an IV",
                        e);
            }
        }
        return null;
    }

    @Override
    protected void initAlgorithmSpecificParameters() throws InvalidKeyException {
        if (!mIvRequired) {
            return;
        }

        // IV is used
        if (!isEncrypting()) {
            throw new InvalidKeyException("IV required when decrypting"
                    + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
        }
    }

    @Override
    protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
            throws InvalidAlgorithmParameterException {
        if (!mIvRequired) {
            if (params != null) {
                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
            }
            return;
        }

        // IV is used
        if (params == null) {
            if (!isEncrypting()) {
                // IV must be provided by the caller
                throw new InvalidAlgorithmParameterException(
                        "IvParameterSpec must be provided when decrypting");
            }
            return;
        }
        if (!(params instanceof IvParameterSpec)) {
            throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported");
        }
        mIv = ((IvParameterSpec) params).getIV();
        if (mIv == null) {
            throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec");
        }
    }

    @Override
    protected void initAlgorithmSpecificParameters(AlgorithmParameters params)
            throws InvalidAlgorithmParameterException {
        if (!mIvRequired) {
            if (params != null) {
                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
            }
            return;
        }

        // IV is used
        if (params == null) {
            if (!isEncrypting()) {
                // IV must be provided by the caller
                throw new InvalidAlgorithmParameterException("IV required when decrypting"
                        + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
            }
            return;
        }

        if (!"DESede".equalsIgnoreCase(params.getAlgorithm())) {
            throw new InvalidAlgorithmParameterException(
                    "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm()
                            + ". Supported: DESede");
        }

        IvParameterSpec ivSpec;
        try {
            ivSpec = params.getParameterSpec(IvParameterSpec.class);
        } catch (InvalidParameterSpecException e) {
            if (!isEncrypting()) {
                // IV must be provided by the caller
                throw new InvalidAlgorithmParameterException("IV required when decrypting"
                        + ", but not found in parameters: " + params, e);
            }
            mIv = null;
            return;
        }
        mIv = ivSpec.getIV();
        if (mIv == null) {
            throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters");
        }
    }

    @Override
    protected final int getAdditionalEntropyAmountForBegin() {
        if ((mIvRequired) && (mIv == null) && (isEncrypting())) {
            // IV will need to be generated
            return BLOCK_SIZE_BYTES;
        }

        return 0;
    }

    @Override
    protected int getAdditionalEntropyAmountForFinish() {
        return 0;
    }

    @Override
    protected void addAlgorithmSpecificParametersToBegin(KeymasterArguments keymasterArgs) {
        if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) {
            // IV is being reused for encryption: this violates security best practices.
            throw new IllegalStateException(
                    "IV has already been used. Reusing IV in encryption mode violates security best"
                            + " practices.");
        }

        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_3DES);
        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
        if ((mIvRequired) && (mIv != null)) {
            keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv);
        }
    }

    @Override
    protected void loadAlgorithmSpecificParametersFromBeginResult(
            KeymasterArguments keymasterArgs) {
        mIvHasBeenUsed = true;

        // NOTE: Keymaster doesn't always return an IV, even if it's used.
        byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null);
        if ((returnedIv != null) && (returnedIv.length == 0)) {
            returnedIv = null;
        }

        if (mIvRequired) {
            if (mIv == null) {
                mIv = returnedIv;
            } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
                throw new ProviderException("IV in use differs from provided IV");
            }
        } else {
            if (returnedIv != null) {
                throw new ProviderException(
                        "IV in use despite IV not being used by this transformation");
            }
        }
    }

    @Override
    protected final void resetAll() {
        mIv = null;
        mIvHasBeenUsed = false;
        super.resetAll();
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -93,6 +93,16 @@ class AndroidKeyStoreBCWorkaroundProvider extends Provider {
        putSymmetricCipherImpl("AES/CTR/NoPadding",
                PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CTR$NoPadding");

        putSymmetricCipherImpl("DESede/CBC/NoPadding",
                PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$NoPadding");
        putSymmetricCipherImpl("DESede/CBC/PKCS7Padding",
                PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$PKCS7Padding");

        putSymmetricCipherImpl("DESede/ECB/NoPadding",
                PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$NoPadding");
        putSymmetricCipherImpl("DESede/ECB/PKCS7Padding",
                PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$PKCS7Padding");

        putSymmetricCipherImpl("AES/GCM/NoPadding",
                PACKAGE_NAME + ".AndroidKeyStoreAuthenticatedAESCipherSpi$GCM$NoPadding");

Loading