2 * Copyright (C) 2017 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 package com.android.server;
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;
25 import junit.framework.AssertionFailedError;
27 import java.nio.ByteBuffer;
28 import java.util.Arrays;
29 import java.util.Random;
31 public class MockGateKeeperService implements IGateKeeperService {
32 static class VerifyHandle {
33 public byte[] password;
36 public VerifyHandle(byte[] password, long sid) {
37 this.password = password;
41 public VerifyHandle(byte[] handle) {
42 ByteBuffer buffer = ByteBuffer.allocate(handle.length);
43 buffer.put(handle, 0, handle.length);
45 int version = buffer.get();
46 sid = buffer.getLong();
47 password = new byte[buffer.remaining()];
51 public byte[] toBytes() {
52 ByteBuffer buffer = ByteBuffer.allocate(1 + Long.BYTES + password.length);
56 return buffer.array();
60 static class AuthToken {
61 public long challenge;
64 public AuthToken(long challenge, long sid) {
65 this.challenge = challenge;
69 public AuthToken(byte[] handle) {
70 ByteBuffer buffer = ByteBuffer.allocate(handle.length);
71 buffer.put(handle, 0, handle.length);
73 int version = buffer.get();
74 challenge = buffer.getLong();
75 sid = buffer.getLong();
78 public byte[] toBytes() {
79 ByteBuffer buffer = ByteBuffer.allocate(1 + Long.BYTES + Long.BYTES);
81 buffer.putLong(challenge);
83 return buffer.array();
87 private ArrayMap<Integer, Long> sidMap = new ArrayMap<>();
88 private ArrayMap<Integer, AuthToken> authTokenMap = new ArrayMap<>();
90 private ArrayMap<Integer, byte[]> handleMap = new ArrayMap<>();
93 public GateKeeperResponse enroll(int uid, byte[] currentPasswordHandle, byte[] currentPassword,
94 byte[] desiredPassword) throws android.os.RemoteException {
96 if (currentPasswordHandle != null) {
97 VerifyHandle handle = new VerifyHandle(currentPasswordHandle);
98 if (Arrays.equals(currentPassword, handle.password)) {
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);
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);
118 public GateKeeperResponse verify(int uid, byte[] enrolledPasswordHandle,
119 byte[] providedPassword) throws android.os.RemoteException {
120 return verifyChallenge(uid, 0, enrolledPasswordHandle, providedPassword);
124 public GateKeeperResponse verifyChallenge(int uid, long challenge,
125 byte[] enrolledPasswordHandle, byte[] providedPassword) throws RemoteException {
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");
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);
140 return GateKeeperResponse.createGenericResponse(GateKeeperResponse.RESPONSE_ERROR);
144 private void refreshAuthToken(int uid, AuthToken token) {
145 authTokenMap.put(uid, token);
148 public AuthToken getAuthToken(int uid) {
149 return authTokenMap.get(uid);
152 public AuthToken getAuthTokenForSid(long sid) {
153 for(AuthToken token : authTokenMap.values()) {
154 if (token.sid == sid) {
161 public void clearAuthToken(int uid) {
162 authTokenMap.remove(uid);
166 public IBinder asBinder() {
167 throw new UnsupportedOperationException();
171 public void clearSecureUserId(int userId) throws RemoteException {
172 sidMap.remove(userId);
176 public long getSecureUserId(int userId) throws RemoteException {
177 if (sidMap.containsKey(userId)) {
178 return sidMap.get(userId);
184 private void refreshSid(int uid, long sid, boolean force) {
185 if (!sidMap.containsKey(uid) || force) {
186 sidMap.put(uid, sid);
188 if (sidMap.get(uid) != sid) {
189 throw new AssertionFailedError("Inconsistent SID");