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 VerifyHandle(byte[] password, long sid)36 public VerifyHandle(byte[] password, long sid) { 37 this.password = password; 38 this.sid = sid; 39 } 40 VerifyHandle(byte[] handle)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 toBytes()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 AuthToken(long challenge, long sid)64 public AuthToken(long challenge, long sid) { 65 this.challenge = challenge; 66 this.sid = sid; 67 } 68 AuthToken(byte[] handle)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 toBytes()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 enroll(int uid, byte[] currentPasswordHandle, byte[] currentPassword, byte[] desiredPassword)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 verify(int uid, byte[] enrolledPasswordHandle, byte[] providedPassword)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 verifyChallenge(int uid, long challenge, byte[] enrolledPasswordHandle, byte[] providedPassword)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 refreshAuthToken(int uid, AuthToken token)144 private void refreshAuthToken(int uid, AuthToken token) { 145 authTokenMap.put(uid, token); 146 } 147 getAuthToken(int uid)148 public AuthToken getAuthToken(int uid) { 149 return authTokenMap.get(uid); 150 } 151 getAuthTokenForSid(long sid)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 clearAuthToken(int uid)161 public void clearAuthToken(int uid) { 162 authTokenMap.remove(uid); 163 } 164 165 @Override asBinder()166 public IBinder asBinder() { 167 throw new UnsupportedOperationException(); 168 } 169 170 @Override clearSecureUserId(int userId)171 public void clearSecureUserId(int userId) throws RemoteException { 172 sidMap.remove(userId); 173 } 174 175 @Override reportDeviceSetupComplete()176 public void reportDeviceSetupComplete() throws RemoteException { 177 } 178 179 @Override getSecureUserId(int userId)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 refreshSid(int uid, long sid, boolean force)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