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; 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 MockGateKeeperService 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 96 if (currentPasswordHandle != null) { 97 VerifyHandle handle = new VerifyHandle(currentPasswordHandle); 98 if (Arrays.equals(currentPassword, handle.password)) { 99 // Trusted enroll 100 VerifyHandle newHandle = new VerifyHandle(desiredPassword, handle.sid); 101 refreshSid(uid, handle.sid, false); 102 handleMap.put(uid, newHandle.toBytes()); 103 return GateKeeperResponse.createOkResponse(newHandle.toBytes(), false); 104 } else { 105 return null; 106 } 107 } else { 108 // Untrusted enroll 109 long newSid = new Random().nextLong(); 110 VerifyHandle newHandle = new VerifyHandle(desiredPassword, newSid); 111 refreshSid(uid, newSid, true); 112 handleMap.put(uid, newHandle.toBytes()); 113 return GateKeeperResponse.createOkResponse(newHandle.toBytes(), false); 114 } 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 getSecureUserId(int userId)176 public long getSecureUserId(int userId) throws RemoteException { 177 if (sidMap.containsKey(userId)) { 178 return sidMap.get(userId); 179 } else { 180 return 0L; 181 } 182 } 183 refreshSid(int uid, long sid, boolean force)184 private void refreshSid(int uid, long sid, boolean force) { 185 if (!sidMap.containsKey(uid) || force) { 186 sidMap.put(uid, sid); 187 } else{ 188 if (sidMap.get(uid) != sid) { 189 throw new AssertionFailedError("Inconsistent SID"); 190 } 191 } 192 } 193 194 } 195