Merge "Create the system service for AVF & hook the manager to it."

This commit is contained in:
TreeHugger Robot
2021-11-30 01:41:31 +00:00
committed by Android (Google) Code Review
11 changed files with 470 additions and 4 deletions

View File

@@ -200,6 +200,8 @@ import android.safetycenter.SafetyCenterFrameworkInitializer;
import android.scheduling.SchedulingFrameworkInitializer;
import android.security.FileIntegrityManager;
import android.security.IFileIntegrityService;
import android.security.attestationverification.AttestationVerificationManager;
import android.security.attestationverification.IAttestationVerificationManagerService;
import android.service.oemlock.IOemLockService;
import android.service.oemlock.OemLockManager;
import android.service.persistentdata.IPersistentDataBlockService;
@@ -1425,6 +1427,19 @@ public final class SystemServiceRegistry {
return new FileIntegrityManager(ctx.getOuterContext(),
IFileIntegrityService.Stub.asInterface(b));
}});
registerService(Context.ATTESTATION_VERIFICATION_SERVICE,
AttestationVerificationManager.class,
new CachedServiceFetcher<AttestationVerificationManager>() {
@Override
public AttestationVerificationManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(
Context.ATTESTATION_VERIFICATION_SERVICE);
return new AttestationVerificationManager(ctx.getOuterContext(),
IAttestationVerificationManagerService.Stub.asInterface(b));
}});
//CHECKSTYLE:ON IndentationCheck
registerService(Context.APP_INTEGRITY_SERVICE, AppIntegrityManager.class,
new CachedServiceFetcher<AppIntegrityManager>() {

View File

@@ -26,12 +26,19 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
import android.os.Bundle;
import android.os.ParcelDuration;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.infra.AndroidFuture;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.time.Duration;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
/**
@@ -43,6 +50,12 @@ import java.util.function.BiConsumer;
@SystemService(Context.ATTESTATION_VERIFICATION_SERVICE)
public class AttestationVerificationManager {
private static final String TAG = "AVF";
private static final Duration MAX_TOKEN_AGE = Duration.ofHours(1);
private final Context mContext;
private final IAttestationVerificationManagerService mService;
/**
* Verifies that {@code attestation} describes a computing environment that meets the
* requirements of {@code profile}, {@code localBindingType}, and {@code requirements}.
@@ -96,7 +109,21 @@ public class AttestationVerificationManager {
@NonNull byte[] attestation,
@NonNull @CallbackExecutor Executor executor,
@NonNull BiConsumer<@VerificationResult Integer, VerificationToken> callback) {
executor.execute(() -> callback.accept(RESULT_UNKNOWN, null));
try {
AndroidFuture<IVerificationResult> resultCallback = new AndroidFuture<>();
resultCallback.thenAccept(result -> {
Log.d(TAG, "verifyAttestation result: " + result.resultCode + " / " + result.token);
executor.execute(() -> {
callback.accept(result.resultCode, result.token);
});
});
mService.verifyAttestation(profile, localBindingType, requirements, attestation,
resultCallback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
@@ -134,12 +161,38 @@ public class AttestationVerificationManager {
@LocalBindingType int localBindingType,
@NonNull Bundle requirements,
@NonNull VerificationToken token,
@Nullable java.time.Duration maximumAge) {
return RESULT_UNKNOWN;
@Nullable Duration maximumAge) {
Duration usedMaximumAge;
if (maximumAge == null) {
usedMaximumAge = MAX_TOKEN_AGE;
} else {
if (maximumAge.compareTo(MAX_TOKEN_AGE) > 0) {
throw new IllegalArgumentException(
"maximumAge cannot be greater than " + MAX_TOKEN_AGE + "; was "
+ maximumAge);
}
usedMaximumAge = maximumAge;
}
try {
AndroidFuture<Integer> resultCallback = new AndroidFuture<>();
resultCallback.orTimeout(5, TimeUnit.SECONDS);
mService.verifyToken(token, new ParcelDuration(usedMaximumAge), resultCallback);
return resultCallback.get(); // block on result callback
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (Throwable t) {
throw new RuntimeException("Error verifying token.", t);
}
}
/** @hide */
public AttestationVerificationManager() {
public AttestationVerificationManager(
@NonNull Context context,
@NonNull IAttestationVerificationManagerService service) {
this.mContext = context;
this.mService = service;
}
/** @hide */

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2021 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 android.security.attestationverification;
import android.os.Bundle;
import android.os.ParcelDuration;
import android.security.attestationverification.AttestationProfile;
import android.security.attestationverification.VerificationToken;
import com.android.internal.infra.AndroidFuture;
/**
* Binder interface to communicate with AttestationVerificationManagerService.
* @hide
*/
oneway interface IAttestationVerificationManagerService {
void verifyAttestation(
in AttestationProfile profile,
in int localBindingType,
in Bundle requirements,
in byte[] attestation,
in AndroidFuture resultCallback);
void verifyToken(
in VerificationToken token,
in ParcelDuration maximumTokenAge,
in AndroidFuture resultCallback);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2021 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 android.security.attestationverification;
import android.os.Bundle;
import com.android.internal.infra.AndroidFuture;
/**
* Binder interface for the system server to communicate with app implementations of
* AttestationVerificationService.
* @hide
*/
oneway interface IAttestationVerificationService {
void onVerifyAttestation(
in Bundle requirements,
in byte[] attestation,
in AndroidFuture callback);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2021 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 android.security.attestationverification;
import android.security.attestationverification.VerificationToken;
/**
* The result of an attestation verification.
*
* {@hide}
*/
parcelable IVerificationResult {
/** The result code corresponding to @VerificationResult. */
int resultCode;
/** The token for the verification or null. */
VerificationToken token;
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2021 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.security;
import static android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN;
import android.content.Context;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelDuration;
import android.os.RemoteException;
import android.security.attestationverification.AttestationProfile;
import android.security.attestationverification.IAttestationVerificationManagerService;
import android.security.attestationverification.IVerificationResult;
import android.security.attestationverification.VerificationToken;
import android.util.ExceptionUtils;
import android.util.Slog;
import com.android.internal.infra.AndroidFuture;
import com.android.server.SystemService;
/**
* A {@link SystemService} which provides functionality related to verifying attestations of
* (usually) remote computing environments.
*
* @hide
*/
public class AttestationVerificationManagerService extends SystemService {
private static final String TAG = "AVF";
public AttestationVerificationManagerService(final Context context) {
super(context);
}
private final IBinder mService = new IAttestationVerificationManagerService.Stub() {
@Override
public void verifyAttestation(
AttestationProfile profile,
int localBindingType,
Bundle requirements,
byte[] attestation,
AndroidFuture resultCallback) throws RemoteException {
try {
Slog.d(TAG, "verifyAttestation");
verifyAttestationForAllVerifiers(profile, localBindingType, requirements,
attestation, resultCallback);
} catch (Throwable t) {
Slog.e(TAG, "failed to verify attestation", t);
throw ExceptionUtils.propagate(t, RemoteException.class);
}
}
@Override
public void verifyToken(VerificationToken token, ParcelDuration parcelDuration,
AndroidFuture resultCallback) throws RemoteException {
// TODO(b/201696614): Implement
resultCallback.complete(RESULT_UNKNOWN);
}
};
private void verifyAttestationForAllVerifiers(
AttestationProfile profile, int localBindingType, Bundle requirements,
byte[] attestation, AndroidFuture<IVerificationResult> resultCallback) {
// TODO(b/201696614): Implement
IVerificationResult result = new IVerificationResult();
result.resultCode = RESULT_UNKNOWN;
result.token = null;
resultCallback.complete(result);
}
@Override
public void onStart() {
Slog.d(TAG, "Started");
publishBinderService(Context.ATTESTATION_VERIFICATION_SERVICE, mService);
}
}

View File

@@ -176,6 +176,7 @@ import com.android.server.recoverysystem.RecoverySystemService;
import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.role.RoleServicePlatformHelper;
import com.android.server.rotationresolver.RotationResolverManagerService;
import com.android.server.security.AttestationVerificationManagerService;
import com.android.server.security.FileIntegrityService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService;
@@ -2339,6 +2340,10 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
}
t.traceBegin("StartAttestationVerificationService");
mSystemServiceManager.startService(AttestationVerificationManagerService.class);
t.traceEnd();
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP)) {
t.traceBegin("StartCompanionDeviceManager");
mSystemServiceManager.startService(COMPANION_DEVICE_MANAGER_SERVICE_CLASS);

View File

@@ -0,0 +1,44 @@
// Copyright (C) 2021 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 {
default_applicable_licenses: ["frameworks_base_license"],
}
android_test {
name: "AttestationVerificationTest",
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
defaults: ["cts_defaults"],
manifest: "AndroidManifest.xml",
test_config: "AndroidTest.xml",
platform_apis: true,
certificate: "platform",
optimize: {
enabled: false,
},
test_suites: ["device-tests"],
libs: [
"android.test.runner",
"android.test.base",
],
static_libs: [
"compatibility-device-util-axt",
"androidx.test.rules",
"androidx.test.ext.junit",
"platform-test-annotations",
],
}

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2021 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.security.attestationverification">
<uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
<uses-permission android:name="android.permission.USE_ATTESTATION_VERIFICATION_SERVICE" />
<application>
<uses-library android:name="android.test.runner"/>
<activity android:name=".SystemAttestationVerificationTest$TestActivity" />
</application>
<!-- self-instrumenting test package. -->
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.security.attestationverification">
</instrumentation>
</manifest>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2021 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.
-->
<configuration description="Platform tests for Attestation Verification Framework">
<option name="test-tag" value="AttestationVerificationTest" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="AttestationVerificationTest.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.security.attestationverification" />
<option name="hidden-api-checks" value="false" />
</test>
</configuration>

View File

@@ -0,0 +1,90 @@
package android.security.attestationverification
import android.os.Bundle
import android.app.Activity
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import com.google.common.truth.Truth.assertThat
import android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED
import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
import android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN
import java.lang.IllegalArgumentException
import java.time.Duration
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
/** Test for system-defined attestation verifiers. */
@SmallTest
@RunWith(AndroidJUnit4::class)
class SystemAttestationVerificationTest {
@get:Rule
val rule = ActivityScenarioRule(TestActivity::class.java)
private lateinit var activity: Activity
private lateinit var avm: AttestationVerificationManager
@Before
fun setup() {
rule.getScenario().onActivity {
avm = it.getSystemService(AttestationVerificationManager::class.java)
activity = it
}
}
@Test
fun verifyAttestation_returnsUnknown() {
val future = CompletableFuture<Int>()
val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
activity.mainExecutor) { result, _ ->
future.complete(result)
}
assertThat(future.getSoon()).isEqualTo(RESULT_UNKNOWN)
}
@Test
fun verifyToken_returnsUnknown() {
val future = CompletableFuture<Int>()
val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
activity.mainExecutor) { _, token ->
val result = avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), token, null)
future.complete(result)
}
assertThat(future.getSoon()).isEqualTo(RESULT_UNKNOWN)
}
@Test
fun verifyToken_tooBigMaxAgeThrows() {
val future = CompletableFuture<VerificationToken>()
val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
activity.mainExecutor) { _, token ->
future.complete(token)
}
assertThrows(IllegalArgumentException::class.java) {
avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), future.getSoon(),
Duration.ofSeconds(3601))
}
}
private fun <T> CompletableFuture<T>.getSoon(): T {
return this.get(1, TimeUnit.SECONDS)
}
class TestActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
}