Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 275aa876 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

2/n: Add HardwareAuthTokenUtils

This should move to android.security in the future. However, with
changing interfaces and build issues, this should suffice for now.

Fixes: 170163175
Bug: 168843828
Test: atest HardwareAuthTokenUtilsTest

Change-Id: Ib566e3f3739dbd72e0e2b4dea67a8921465ed963
parent 06e8478e
Loading
Loading
Loading
Loading
+138 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.biometrics;

import static java.nio.ByteOrder.LITTLE_ENDIAN;

import android.hardware.keymaster.HardwareAuthToken;
import android.hardware.keymaster.Timestamp;

import java.nio.ByteOrder;

/**
 * Utilities for converting between old and new HardwareAuthToken types. See
 * {@link HardwareAuthToken}.
 */
public class HardwareAuthTokenUtils {
    public static byte[] toByteArray(HardwareAuthToken hat) {
        final byte[] array = new byte[69];

        // Version, first byte. Used in hw_auth_token.h but not HardwareAuthToken
        array[0] = 0;

        // Challenge, 1:8.
        writeLong(hat.challenge, array, 1 /* offset */);

        // UserId, 9:16.
        writeLong(hat.userId, array, 9 /* offset */);

        // AuthenticatorId, 17:24.
        writeLong(hat.authenticatorId, array, 17 /* offset */);

        // AuthenticatorType, 25:28.
        writeInt(flipIfNativelyLittle(hat.authenticatorType), array, 25 /* offset */);

        // Timestamp, 29:36.
        writeLong(flipIfNativelyLittle(hat.timestamp.milliSeconds), array, 29 /* offset */);

        // MAC, 37:69. Byte array.
        System.arraycopy(hat.mac, 0 /* srcPos */, array, 37 /* destPos */, hat.mac.length);

        return array;
    }

    public static HardwareAuthToken toHardwareAuthToken(byte[] array) {
        final HardwareAuthToken hardwareAuthToken = new HardwareAuthToken();

        // First byte is version, which doesn't not exist in HardwareAuthToken anymore
        // Next 8 bytes is the challenge.
        hardwareAuthToken.challenge = getLong(array, 1 /* offset */);

        // Next 8 bytes is the userId
        hardwareAuthToken.userId = getLong(array, 9 /* offset */);

        // Next 8 bytes is the authenticatorId.
        hardwareAuthToken.authenticatorId = getLong(array, 17 /* offset */);

        // Next 4 bytes is the authenticatorType.
        hardwareAuthToken.authenticatorType = flipIfNativelyLittle(getInt(array, 25 /* offset */));

        // Next 8 bytes is the timestamp.
        final Timestamp timestamp = new Timestamp();
        timestamp.milliSeconds = flipIfNativelyLittle(getLong(array, 29 /* offset */));
        hardwareAuthToken.timestamp = timestamp;

        // Last 32 bytes is the mac, 37:69
        hardwareAuthToken.mac = new byte[32];
        System.arraycopy(array, 37 /* srcPos */,
                hardwareAuthToken.mac,
                0 /* destPos */,
                32 /* length */);

        return hardwareAuthToken;
    }

    private static long flipIfNativelyLittle(long l) {
        if (LITTLE_ENDIAN == ByteOrder.nativeOrder()) {
            return Long.reverseBytes(l);
        }
        return l;
    }

    private static int flipIfNativelyLittle(int i) {
        if (LITTLE_ENDIAN == ByteOrder.nativeOrder()) {
            return Integer.reverseBytes(i);
        }
        return i;
    }

    private static void writeLong(long l, byte[] dest, int offset) {
        dest[offset + 0] = (byte) l;
        dest[offset + 1] = (byte) (l >> 8);
        dest[offset + 2] = (byte) (l >> 16);
        dest[offset + 3] = (byte) (l >> 24);
        dest[offset + 4] = (byte) (l >> 32);
        dest[offset + 5] = (byte) (l >> 40);
        dest[offset + 6] = (byte) (l >> 48);
        dest[offset + 7] = (byte) (l >> 56);
    }

    private static void writeInt(int i, byte[] dest, int offset) {
        dest[offset + 0] = (byte) i;
        dest[offset + 1] = (byte) (i >> 8);
        dest[offset + 2] = (byte) (i >> 16);
        dest[offset + 3] = (byte) (i >> 24);
    }

    private static long getLong(byte[] array, int offset) {
        long result = 0;
        // Lowest bit is LSB
        for (int i = 0; i < 8; i++) {
            result += (long) ((array[i + offset] & 0xffL) << (8 * i));
        }
        return result;
    }

    private static int getInt(byte[] array, int offset) {
        int result = 0;
        // Lowest bit is LSB
        for (int i = 0; i < 4; i++) {
            result += (int) (((int) array[i + offset] & 0xff) << (8 * i));
        }
        return result;
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;

import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
@@ -82,8 +83,8 @@ public class FingerprintEnrollClient extends EnrollClient<ISession> implements U
    protected void startHalOperation() {
        UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
        try {
            // TODO(b/170163175): Need a way to convert byte arrays to HardwareAuthToken
            getFreshDaemon().enroll(mSequentialId, null /* hat */);
            getFreshDaemon().enroll(mSequentialId,
                    HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken));
        } catch (RemoteException e) {
            Slog.e(TAG, "Remote exception when requesting enroll", e);
            onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
+83 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.biometrics;

import static junit.framework.Assert.assertEquals;

import android.hardware.keymaster.HardwareAuthToken;
import android.hardware.keymaster.Timestamp;
import android.platform.test.annotations.Presubmit;

import androidx.test.filters.SmallTest;

import org.junit.Test;

@Presubmit
@SmallTest
public class HardwareAuthTokenUtilsTest {

    @Test
    public void testByteArrayLoopBack() {
        final byte[] hat = new byte[69];
        for (int i = 0; i < 69; i++) {
            hat[i] = (byte) i;
        }

        final HardwareAuthToken hardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hat);
        final byte[] hat2 = HardwareAuthTokenUtils.toByteArray(hardwareAuthToken);

        for (int i = 0; i < hat.length; i++) {
            assertEquals(hat[i], hat2[i]);
        }
    }

    @Test
    public void testHardwareAuthTokenLoopBack() {
        final long testChallenge = 1000L;
        final long testUserId = 2000L;
        final long testAuthenticatorId = 3000L;
        final int testAuthenticatorType = 4000;
        final long testTimestamp = 5000L;

        final HardwareAuthToken hardwareAuthToken = new HardwareAuthToken();
        hardwareAuthToken.challenge = testChallenge;
        hardwareAuthToken.userId = testUserId;
        hardwareAuthToken.authenticatorId = testAuthenticatorId;
        hardwareAuthToken.authenticatorType = testAuthenticatorType;
        hardwareAuthToken.timestamp = new Timestamp();
        hardwareAuthToken.timestamp.milliSeconds = testTimestamp;
        hardwareAuthToken.mac = new byte[32];

        for (int i = 0; i < hardwareAuthToken.mac.length; i++) {
            hardwareAuthToken.mac[i] = (byte) i;
        }

        final byte[] hat = HardwareAuthTokenUtils.toByteArray(hardwareAuthToken);
        final HardwareAuthToken hardwareAuthToken2 =
                HardwareAuthTokenUtils.toHardwareAuthToken(hat);

        assertEquals(testChallenge, hardwareAuthToken2.challenge);
        assertEquals(testUserId, hardwareAuthToken2.userId);
        assertEquals(testAuthenticatorId, hardwareAuthToken2.authenticatorId);
        assertEquals(testAuthenticatorType, hardwareAuthToken2.authenticatorType);
        assertEquals(testTimestamp, hardwareAuthToken2.timestamp.milliSeconds);

        for (int i = 0; i < hardwareAuthToken.mac.length; i++) {
            assertEquals(hardwareAuthToken.mac[i], hardwareAuthToken2.mac[i]);
        }
    }
}