Home | History | Annotate | Download | only in storage
      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 // Shared interface for accessing hardware state.
     17 //
     18 
     19 package com.android.verifiedboot.storage;
     20 
     21 import javacard.framework.AID;
     22 import javacard.framework.Applet;
     23 import javacard.framework.ISO7816;
     24 import javacard.framework.ISOException;
     25 import javacard.framework.JCSystem;
     26 import javacard.framework.Shareable;
     27 
     28 import com.nxp.id.jcopx.util.SystemInfo;
     29 
     30 import com.android.verifiedboot.globalstate.owner.OwnerInterface;
     31 import com.android.verifiedboot.globalstate.callback.CallbackInterface;
     32 
     33 class GlobalStateImpl implements OwnerInterface {
     34     // Used to track if a client needs to be renotified in case of a
     35     // power down, etc.
     36     final static byte CLIENT_STATE_CLEAR_PENDING = (byte)0x1;
     37     // Scenario values
     38     final static byte BOOTLOADER_UNDEFINED = (byte) 0xff;
     39     final static byte BOOTLOADER_HIGH = (byte) 0x5a;
     40     final static byte BOOTLOADER_LOW = (byte) 0xa5;
     41 
     42     private Object[] clientApplets;
     43     private byte[] clientAppletState;
     44     boolean inProduction;
     45 
     46 
     47     public GlobalStateImpl() {
     48         // Support up to 10 clients.
     49         clientApplets = new Object[10];
     50         // Used to store mask for each client.
     51         clientAppletState = new byte[clientApplets.length];
     52         // Used to signal the end of factory provisioning.
     53         inProduction = false;
     54     }
     55 
     56     /**
     57      * Returns the index of the given AID in clientApplets
     58      * if existent. Returns -1 if not found.
     59      *
     60      * @param aid The client applet AID to find.
     61      */
     62     private short findClientApplet(AID aid) {
     63         for (short i = 0; i < clientApplets.length; ++i) {
     64             if (clientApplets[i] == null) {
     65                 continue;
     66             }
     67             if (((AID)clientApplets[i]).equals(aid)) {
     68                 return i;
     69             }
     70         }
     71         return (short) -1;
     72     }
     73 
     74     /**
     75      * {@inheritDoc}
     76      *
     77      * It is expected that an applet can call notifyOnDataClear() on every
     78      * select.
     79      *
     80      * @param unregister If true, will remove the caller's AID from the registered store.
     81      *                   If false, will add the caller's AID to the register store.
     82      */
     83     @Override
     84     public boolean notifyOnDataClear(boolean unregister) {
     85         final AID aid = JCSystem.getPreviousContextAID();
     86         short firstFreeSlot = -1;
     87         for (short i = 0; i < clientApplets.length; ++i) {
     88             if (clientApplets[i] == null) {
     89                 if (firstFreeSlot == -1) {
     90                     firstFreeSlot = i;
     91                 }
     92                 continue;
     93             }
     94             if (((AID)clientApplets[i]).equals(aid)) {
     95                 if (unregister == true) {
     96                     clientApplets[i] = null;
     97                     // Clean up memory if we can.
     98                     if (JCSystem.isObjectDeletionSupported()) {
     99                         JCSystem.requestObjectDeletion();
    100                     }
    101                     return true;
    102                 } else {
    103                     // Already registered.
    104                     return true;
    105                 }
    106             }
    107         }
    108         // Not registered anyway.
    109         if (unregister == true) {
    110             return true;
    111         }
    112         // No spaces left.
    113         if (firstFreeSlot == -1) {
    114             return false;
    115         }
    116         clientApplets[firstFreeSlot] = aid;
    117         return true;
    118     }
    119 
    120 
    121     /**
    122      * {@inheritDoc}
    123      *
    124      * Processes an acknowledgement request from the given AID.
    125      */
    126     @Override
    127     public void reportDataCleared() {
    128         final AID aid = JCSystem.getPreviousContextAID();
    129         short id = findClientApplet(aid);
    130         if (id >= clientAppletState.length) {
    131             // This would be surprising.
    132             return;
    133         }
    134         if (id == (short) -1) {
    135             // Not found.
    136             return;
    137         }
    138         if ((clientAppletState[id] & CLIENT_STATE_CLEAR_PENDING)
    139                 == CLIENT_STATE_CLEAR_PENDING) {
    140             clientAppletState[id] ^= CLIENT_STATE_CLEAR_PENDING;
    141         }
    142     }
    143 
    144     /**
    145      * {@inheritDoc}
    146      *
    147      * Returns true if data still needs to be cleared.
    148      */
    149     @Override
    150     public boolean dataClearNeeded() {
    151         final AID aid = JCSystem.getPreviousContextAID();
    152         short id = findClientApplet(aid);
    153         if (id >= clientAppletState.length) {
    154             // This would be surprising.
    155             return false;
    156         }
    157        if (id == (short) -1) {
    158            return false;
    159        }
    160         return ((clientAppletState[id] & CLIENT_STATE_CLEAR_PENDING)
    161                 == CLIENT_STATE_CLEAR_PENDING);
    162     }
    163 
    164     /**
    165      * {@inheritDoc}
    166      *
    167      * Walks all clientApplets, and if they exist, checks if the have not
    168      * cleared the pending action bit. If any applets need to clear data,
    169      * false will be returned.
    170      */
    171     @Override
    172     public boolean globalDataClearComplete() {
    173         for (short i = 0; i < clientApplets.length; ++i) {
    174             if (clientApplets[i] == null) {
    175                 continue;
    176             }
    177             if ((clientAppletState[i] & CLIENT_STATE_CLEAR_PENDING)
    178                 == CLIENT_STATE_CLEAR_PENDING) {
    179                 return false;
    180             }
    181         }
    182         return true;
    183     }
    184 
    185     /**
    186      * {@inheritDoc}
    187      *
    188      * Notifies all clients that a data clear is needed.
    189      *
    190      * If a power down or other interrupting event occurs, it is expected
    191      * that the clients will always call dataClearNeeded() prior to doing work
    192      * in process().
    193      *
    194      * However, the secure boot applet will check globalDataClearComplete()
    195      * on its first process() invocation after power on and call this method
    196      * with resume=true if a data clear was intended but incomplete.
    197      */
    198     @Override
    199     public void triggerDataClear(boolean resume) {
    200         // Two passes for a full
    201         // - First just sets the byte atomically.
    202         // - Second attempts notification.
    203         if (resume == false) {
    204             for (short i = 0; i < clientApplets.length; ++i) {
    205                 if (clientApplets[i] == null) {
    206                     continue;
    207                 }
    208                 clientAppletState[i] |= CLIENT_STATE_CLEAR_PENDING;
    209             }
    210         }
    211         for (short i = 0; i < clientApplets.length; ++i) {
    212             if (clientApplets[i] == null) {
    213                 continue;
    214             }
    215             if ((clientAppletState[i] & CLIENT_STATE_CLEAR_PENDING)
    216                 == CLIENT_STATE_CLEAR_PENDING) {
    217                 ((CallbackInterface) JCSystem.getAppletShareableInterfaceObject(
    218                         (AID)clientApplets[i], (byte)0)).clearData();
    219             }
    220         }
    221     }
    222 
    223     /**
    224      *  {@inheritDoc}
    225      *
    226      *  Returns true if the AP reset GPIO latch has not been cleared.
    227      */
    228     @Override
    229     public boolean inBootloader() {
    230         try {
    231             switch (SystemInfo.getExternalState(
    232                     SystemInfo.SYSTEMINFO_SCENARIO_0)) {
    233             case BOOTLOADER_HIGH:
    234                 return true;
    235             case BOOTLOADER_LOW:
    236             case BOOTLOADER_UNDEFINED:
    237             default:
    238                 return false;
    239             }
    240         } catch (ISOException e) {
    241             // If we can't read it, we fail closed unless we're in debug mode.
    242             return false;
    243         }
    244     }
    245 
    246     /**
    247      * Returns the external signal that feeds inBootloader().
    248      */
    249     public byte inBootloaderRaw() {
    250         try {
    251             return SystemInfo.getExternalState(SystemInfo.SYSTEMINFO_SCENARIO_0);
    252         } catch (ISOException e) {
    253             return (byte) 0x0;
    254         }
    255     }
    256 
    257     /**
    258      * {@inheritDoc}
    259      *
    260      * @param val value assigned to the inProduction value.
    261      * @return true if changed and false otherwise.
    262      */
    263     @Override
    264     public boolean setProduction(boolean val) {
    265         // Move to production is one way except for RMA - which
    266         // is handled in bootloader mode.
    267         if (val == false) {
    268             if (inBootloader() == false) {
    269                 return false;
    270             }
    271         }
    272         inProduction = val;
    273         return true;
    274     }
    275 
    276     /**
    277      * {@inheritDoc}
    278      *
    279      * accessor for inProduction.
    280      */
    281     @Override
    282     public boolean production() {
    283         return inProduction;
    284     }
    285 }
    286