Home | History | Annotate | Download | only in recoverablekeystore
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.locksettings.recoverablekeystore;
     18 
     19 import static org.junit.Assert.assertArrayEquals;
     20 import static org.junit.Assert.assertEquals;
     21 import static org.junit.Assert.assertTrue;
     22 import static org.junit.Assert.fail;
     23 
     24 import android.security.keystore.AndroidKeyStoreSecretKey;
     25 import android.security.keystore.KeyGenParameterSpec;
     26 import android.security.keystore.KeyProperties;
     27 import android.support.test.filters.SmallTest;
     28 import android.support.test.runner.AndroidJUnit4;
     29 
     30 import org.junit.After;
     31 import org.junit.Test;
     32 import org.junit.runner.RunWith;
     33 
     34 import java.security.KeyStore;
     35 import java.util.HashMap;
     36 import java.util.Map;
     37 
     38 import javax.crypto.Cipher;
     39 import javax.crypto.KeyGenerator;
     40 import javax.crypto.SecretKey;
     41 import javax.crypto.spec.GCMParameterSpec;
     42 
     43 @SmallTest
     44 @RunWith(AndroidJUnit4.class)
     45 public class WrappedKeyTest {
     46     private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
     47     private static final String KEY_ALGORITHM = "AES";
     48     private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
     49     private static final String WRAPPING_KEY_ALIAS = "WrappedKeyTestWrappingKeyAlias";
     50     private static final int GENERATION_ID = 1;
     51     private static final int GCM_TAG_LENGTH_BYTES = 16;
     52     private static final int BITS_PER_BYTE = 8;
     53     private static final int GCM_TAG_LENGTH_BITS = GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE;
     54 
     55     @After
     56     public void tearDown() throws Exception {
     57         KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
     58         keyStore.load(/*param=*/ null);
     59         keyStore.deleteEntry(WRAPPING_KEY_ALIAS);
     60     }
     61 
     62     @Test
     63     public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped() throws Exception {
     64         PlatformEncryptionKey wrappingKey = new PlatformEncryptionKey(
     65                 GENERATION_ID, generateAndroidKeyStoreKey());
     66         SecretKey rawKey = generateKey();
     67 
     68         WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey);
     69 
     70         Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
     71         cipher.init(
     72                 Cipher.UNWRAP_MODE,
     73                 wrappingKey.getKey(),
     74                 new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
     75         SecretKey unwrappedKey = (SecretKey) cipher.unwrap(
     76                 wrappedKey.getKeyMaterial(), KEY_ALGORITHM, Cipher.SECRET_KEY);
     77         assertEquals(rawKey, unwrappedKey);
     78     }
     79 
     80     @Test
     81     public void fromSecretKey_returnsAKeyWithTheGenerationIdOfTheWrappingKey() throws Exception {
     82         PlatformEncryptionKey wrappingKey = new PlatformEncryptionKey(
     83                 GENERATION_ID, generateAndroidKeyStoreKey());
     84         SecretKey rawKey = generateKey();
     85 
     86         WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey);
     87 
     88         assertEquals(GENERATION_ID, wrappedKey.getPlatformKeyGenerationId());
     89     }
     90 
     91     @Test
     92     public void decryptWrappedKeys_decryptsWrappedKeys() throws Exception {
     93         String alias = "karlin";
     94         AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
     95         SecretKey appKey = generateKey();
     96         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
     97                 new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey);
     98         HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
     99         keysByAlias.put(alias, wrappedKey);
    100 
    101         Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys(
    102                 new PlatformDecryptionKey(GENERATION_ID, platformKey), keysByAlias);
    103 
    104         assertEquals(1, unwrappedKeys.size());
    105         assertTrue(unwrappedKeys.containsKey(alias));
    106         assertArrayEquals(appKey.getEncoded(), unwrappedKeys.get(alias).getEncoded());
    107     }
    108 
    109     @Test
    110     public void decryptWrappedKeys_doesNotDieIfSomeKeysAreUnwrappable() throws Exception {
    111         String alias = "karlin";
    112         AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
    113         SecretKey appKey = generateKey();
    114         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
    115                 new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey);
    116         HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
    117         keysByAlias.put(alias, wrappedKey);
    118 
    119         Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys(
    120                 new PlatformDecryptionKey(GENERATION_ID, generateAndroidKeyStoreKey()),
    121                 keysByAlias);
    122 
    123         assertEquals(0, unwrappedKeys.size());
    124     }
    125 
    126     @Test
    127     public void decryptWrappedKeys_throwsIfPlatformKeyGenerationIdDoesNotMatch() throws Exception {
    128         AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
    129         WrappedKey wrappedKey = WrappedKey.fromSecretKey(
    130                 new PlatformEncryptionKey(GENERATION_ID, platformKey), generateKey());
    131         HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
    132         keysByAlias.put("benji", wrappedKey);
    133 
    134         try {
    135             WrappedKey.unwrapKeys(
    136                     new PlatformDecryptionKey(/*generationId=*/ 2, platformKey),
    137                     keysByAlias);
    138             fail("Should have thrown.");
    139         } catch (BadPlatformKeyException e) {
    140             assertEquals(
    141                     "WrappedKey with alias 'benji' was wrapped with platform key 1,"
    142                             + " not platform key 2",
    143                     e.getMessage());
    144         }
    145     }
    146 
    147     private SecretKey generateKey() throws Exception {
    148         KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
    149         keyGenerator.init(/*keySize=*/ 256);
    150         return keyGenerator.generateKey();
    151     }
    152 
    153     private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
    154         KeyGenerator keyGenerator = KeyGenerator.getInstance(
    155                 KEY_ALGORITHM,
    156                 ANDROID_KEY_STORE_PROVIDER);
    157         keyGenerator.init(new KeyGenParameterSpec.Builder(
    158                 WRAPPING_KEY_ALIAS,
    159                 KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
    160                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
    161                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
    162                 .build());
    163         return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
    164     }
    165 
    166     private PlatformDecryptionKey generatePlatformDecryptionKey() throws Exception {
    167         return generatePlatformDecryptionKey(GENERATION_ID);
    168     }
    169 
    170     private PlatformDecryptionKey generatePlatformDecryptionKey(int generationId) throws Exception {
    171         return new PlatformDecryptionKey(generationId, generateAndroidKeyStoreKey());
    172     }
    173 }
    174