/* * Copyright (C) 2019 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 com.android.server.backup.encryption.keys; import android.annotation.Nullable; import android.content.Context; import android.util.Slog; import com.android.server.backup.encryption.protos.nano.WrappedKeyProto; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Optional; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; /** * Gets the correct tertiary key to use during a backup, rotating it if required. * *

Calling any method on this class will count a incremental backup against the app, and the key * will be rotated if required. */ public class TertiaryKeyManager { private static final String TAG = "TertiaryKeyMgr"; private final TertiaryKeyStore mKeyStore; private final TertiaryKeyGenerator mKeyGenerator; private final TertiaryKeyRotationScheduler mTertiaryKeyRotationScheduler; private final RecoverableKeyStoreSecondaryKey mSecondaryKey; private final String mPackageName; private boolean mKeyRotated; @Nullable private SecretKey mTertiaryKey; public TertiaryKeyManager( Context context, SecureRandom secureRandom, TertiaryKeyRotationScheduler tertiaryKeyRotationScheduler, RecoverableKeyStoreSecondaryKey secondaryKey, String packageName) { mSecondaryKey = secondaryKey; mPackageName = packageName; mKeyGenerator = new TertiaryKeyGenerator(secureRandom); mKeyStore = TertiaryKeyStore.newInstance(context, secondaryKey); mTertiaryKeyRotationScheduler = tertiaryKeyRotationScheduler; } /** * Returns either the previously used tertiary key, or a new tertiary key if there was no * previous key or it needed to be rotated. */ public SecretKey getKey() throws InvalidKeyException, IOException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { init(); return mTertiaryKey; } /** Returns the key given by {@link #getKey()} wrapped by the secondary key. */ public WrappedKeyProto.WrappedKey getWrappedKey() throws InvalidKeyException, IOException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { init(); return KeyWrapUtils.wrap(mSecondaryKey.getSecretKey(), mTertiaryKey); } /** * Returns {@code true} if a new tertiary key was generated at the start of this session, * otherwise {@code false}. */ public boolean wasKeyRotated() throws InvalidKeyException, IllegalBlockSizeException, IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { init(); return mKeyRotated; } private void init() throws IllegalBlockSizeException, InvalidKeyException, IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException { if (mTertiaryKey != null) { return; } Optional key = getExistingKeyIfNotRotated(); if (!key.isPresent()) { Slog.d(TAG, "Generating new tertiary key for " + mPackageName); key = Optional.of(mKeyGenerator.generate()); mKeyRotated = true; mTertiaryKeyRotationScheduler.recordKeyRotation(mPackageName); mKeyStore.save(mPackageName, key.get()); } mTertiaryKey = key.get(); mTertiaryKeyRotationScheduler.recordBackup(mPackageName); } private Optional getExistingKeyIfNotRotated() throws InvalidKeyException, IOException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException { if (mTertiaryKeyRotationScheduler.isKeyRotationDue(mPackageName)) { Slog.i(TAG, "Tertiary key rotation was required for " + mPackageName); return Optional.empty(); } else { Slog.i(TAG, "Tertiary key rotation was not required"); return mKeyStore.load(mPackageName); } } }