Home | History | Annotate | Download | only in locksettings
      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;
     18 
     19 import android.os.IBinder;
     20 import android.os.RemoteException;
     21 import android.service.gatekeeper.GateKeeperResponse;
     22 import android.service.gatekeeper.IGateKeeperService;
     23 import android.util.ArrayMap;
     24 
     25 import junit.framework.AssertionFailedError;
     26 
     27 import java.nio.ByteBuffer;
     28 import java.util.Arrays;
     29 import java.util.Random;
     30 
     31 public class FakeGateKeeperService implements IGateKeeperService {
     32     static class VerifyHandle {
     33         public byte[] password;
     34         public long sid;
     35 
     36         public VerifyHandle(byte[] password, long sid) {
     37             this.password = password;
     38             this.sid = sid;
     39         }
     40 
     41         public VerifyHandle(byte[] handle) {
     42             ByteBuffer buffer = ByteBuffer.allocate(handle.length);
     43             buffer.put(handle, 0, handle.length);
     44             buffer.flip();
     45             int version = buffer.get();
     46             sid = buffer.getLong();
     47             password = new byte[buffer.remaining()];
     48             buffer.get(password);
     49         }
     50 
     51         public byte[] toBytes() {
     52             ByteBuffer buffer = ByteBuffer.allocate(1 + Long.BYTES + password.length);
     53             buffer.put((byte)0);
     54             buffer.putLong(sid);
     55             buffer.put(password);
     56             return buffer.array();
     57         }
     58     }
     59 
     60     static class AuthToken {
     61         public long challenge;
     62         public long sid;
     63 
     64         public AuthToken(long challenge, long sid) {
     65             this.challenge = challenge;
     66             this.sid = sid;
     67         }
     68 
     69         public AuthToken(byte[] handle) {
     70             ByteBuffer buffer = ByteBuffer.allocate(handle.length);
     71             buffer.put(handle, 0, handle.length);
     72             buffer.flip();
     73             int version = buffer.get();
     74             challenge = buffer.getLong();
     75             sid = buffer.getLong();
     76         }
     77 
     78         public byte[] toBytes() {
     79             ByteBuffer buffer = ByteBuffer.allocate(1 + Long.BYTES + Long.BYTES);
     80             buffer.put((byte)0);
     81             buffer.putLong(challenge);
     82             buffer.putLong(sid);
     83             return buffer.array();
     84         }
     85     }
     86 
     87     private ArrayMap<Integer, Long> sidMap = new ArrayMap<>();
     88     private ArrayMap<Integer, AuthToken> authTokenMap = new ArrayMap<>();
     89 
     90     private ArrayMap<Integer, byte[]> handleMap = new ArrayMap<>();
     91 
     92     @Override
     93     public GateKeeperResponse enroll(int uid, byte[] currentPasswordHandle, byte[] currentPassword,
     94             byte[] desiredPassword) throws android.os.RemoteException {
     95         if (currentPasswordHandle != null) {
     96             VerifyHandle handle = new VerifyHandle(currentPasswordHandle);
     97             if (Arrays.equals(currentPassword, handle.password)) {
     98                 // Trusted enroll
     99                 VerifyHandle newHandle = new VerifyHandle(desiredPassword, handle.sid);
    100                 refreshSid(uid, handle.sid, false);
    101                 handleMap.put(uid, newHandle.toBytes());
    102                 return GateKeeperResponse.createOkResponse(newHandle.toBytes(), false);
    103             } else if (currentPassword != null) {
    104                 // current password is provided but does not match handle, this is an error case.
    105                 return null;
    106             }
    107             // Fall through: password handle is provided, but no password
    108         }
    109         // Untrusted/new enrollment: generate a new SID
    110         long newSid = new Random().nextLong();
    111         VerifyHandle newHandle = new VerifyHandle(desiredPassword, newSid);
    112         refreshSid(uid, newSid, true);
    113         handleMap.put(uid, newHandle.toBytes());
    114         return GateKeeperResponse.createOkResponse(newHandle.toBytes(), false);
    115     }
    116 
    117     @Override
    118     public GateKeeperResponse verify(int uid, byte[] enrolledPasswordHandle,
    119             byte[] providedPassword) throws android.os.RemoteException {
    120         return verifyChallenge(uid, 0, enrolledPasswordHandle, providedPassword);
    121     }
    122 
    123     @Override
    124     public GateKeeperResponse verifyChallenge(int uid, long challenge,
    125             byte[] enrolledPasswordHandle, byte[] providedPassword) throws RemoteException {
    126 
    127         VerifyHandle handle = new VerifyHandle(enrolledPasswordHandle);
    128         if (Arrays.equals(handle.password, providedPassword)) {
    129             byte[] knownHandle = handleMap.get(uid);
    130             if (knownHandle != null) {
    131                 if (!Arrays.equals(knownHandle, enrolledPasswordHandle)) {
    132                     throw new AssertionFailedError("Got correct but obsolete handle");
    133                 }
    134             }
    135             refreshSid(uid, handle.sid, false);
    136             AuthToken token = new AuthToken(challenge, handle.sid);
    137             refreshAuthToken(uid, token);
    138             return GateKeeperResponse.createOkResponse(token.toBytes(), false);
    139         } else {
    140             return GateKeeperResponse.createGenericResponse(GateKeeperResponse.RESPONSE_ERROR);
    141         }
    142     }
    143 
    144     private void refreshAuthToken(int uid, AuthToken token) {
    145         authTokenMap.put(uid, token);
    146     }
    147 
    148     public AuthToken getAuthToken(int uid) {
    149         return authTokenMap.get(uid);
    150     }
    151 
    152     public AuthToken getAuthTokenForSid(long sid) {
    153         for(AuthToken token : authTokenMap.values()) {
    154             if (token.sid == sid) {
    155                 return token;
    156             }
    157         }
    158         return null;
    159     }
    160 
    161     public void clearAuthToken(int uid) {
    162         authTokenMap.remove(uid);
    163     }
    164 
    165     @Override
    166     public IBinder asBinder() {
    167         throw new UnsupportedOperationException();
    168     }
    169 
    170     @Override
    171     public void clearSecureUserId(int userId) throws RemoteException {
    172         sidMap.remove(userId);
    173     }
    174 
    175     @Override
    176     public void reportDeviceSetupComplete() throws RemoteException {
    177     }
    178 
    179     @Override
    180     public long getSecureUserId(int userId) throws RemoteException {
    181         if (sidMap.containsKey(userId)) {
    182             return sidMap.get(userId);
    183         } else {
    184             return 0L;
    185         }
    186     }
    187 
    188     private void refreshSid(int uid, long sid, boolean force) {
    189         if (!sidMap.containsKey(uid) || force) {
    190             sidMap.put(uid, sid);
    191         } else{
    192             if (sidMap.get(uid) != sid) {
    193                 throw new AssertionFailedError("Inconsistent SID");
    194             }
    195         }
    196     }
    197 
    198 }
    199