Merge "media.bufferpool2: ensure message-ids are safe from overflow/underflow" into main
diff --git a/audio/aidl/TEST_MAPPING b/audio/aidl/TEST_MAPPING
index e325001..d848af9 100644
--- a/audio/aidl/TEST_MAPPING
+++ b/audio/aidl/TEST_MAPPING
@@ -78,6 +78,12 @@
"include-filter": "android.virtualdevice.cts.VirtualAudioTest"
}
]
+ },
+ {
+ "name": "VtsHalBassBoostTargetTest"
+ },
+ {
+ "name": "VtsHalDynamicsProcessingTest"
}
]
}
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 8c6c537..fc82a65 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -245,7 +245,7 @@
"EffectFactory.cpp",
"EffectMain.cpp",
],
- installable: false, //installed in apex com.android.hardware.audio.effect
+ installable: false, //installed in apex com.android.hardware.audio
}
cc_library_headers {
@@ -267,9 +267,3 @@
sub_dir: "vintf",
installable: false,
}
-
-prebuilt_etc {
- name: "audio_effects_config.xml",
- src: "audio_effects_config.xml",
- installable: false,
-}
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 807348f..697ff0d 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -21,6 +21,7 @@
#include <Utils.h>
#include <android-base/logging.h>
#include <android/binder_ibinder_platform.h>
+#include <cutils/properties.h>
#include <utils/SystemClock.h>
#include <utils/Trace.h>
@@ -652,16 +653,34 @@
isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::input>(),
AudioInputFlags::FAST)) ||
(flags.getTag() == AudioIoFlags::Tag::output &&
- isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::output>(),
- AudioOutputFlags::FAST))) {
+ (isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::output>(),
+ AudioOutputFlags::FAST) ||
+ isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::output>(),
+ AudioOutputFlags::SPATIALIZER)))) {
// FAST workers should be run with a SCHED_FIFO scheduler, however the host process
// might be lacking the capability to request it, thus a failure to set is not an error.
pid_t workerTid = mWorker->getTid();
if (workerTid > 0) {
- struct sched_param param;
- param.sched_priority = 3; // Must match SchedulingPolicyService.PRIORITY_MAX (Java).
+ constexpr int32_t kRTPriorityMin = 1; // SchedulingPolicyService.PRIORITY_MIN (Java).
+ constexpr int32_t kRTPriorityMax = 3; // SchedulingPolicyService.PRIORITY_MAX (Java).
+ int priorityBoost = kRTPriorityMax;
+ if (flags.getTag() == AudioIoFlags::Tag::output &&
+ isBitPositionFlagSet(flags.template get<AudioIoFlags::Tag::output>(),
+ AudioOutputFlags::SPATIALIZER)) {
+ const int32_t sptPrio =
+ property_get_int32("audio.spatializer.priority", kRTPriorityMin);
+ if (sptPrio >= kRTPriorityMin && sptPrio <= kRTPriorityMax) {
+ priorityBoost = sptPrio;
+ } else {
+ LOG(WARNING) << __func__ << ": invalid spatializer priority: " << sptPrio;
+ return ndk::ScopedAStatus::ok();
+ }
+ }
+ struct sched_param param = {
+ .sched_priority = priorityBoost,
+ };
if (sched_setscheduler(workerTid, SCHED_FIFO | SCHED_RESET_ON_FORK, ¶m) != 0) {
- PLOG(WARNING) << __func__ << ": failed to set FIFO scheduler for a fast thread";
+ PLOG(WARNING) << __func__ << ": failed to set FIFO scheduler and priority";
}
} else {
LOG(WARNING) << __func__ << ": invalid worker tid: " << workerTid;
diff --git a/audio/aidl/default/apex/com.android.hardware.audio/Android.bp b/audio/aidl/default/apex/com.android.hardware.audio/Android.bp
index 9c91e27..ee92512 100644
--- a/audio/aidl/default/apex/com.android.hardware.audio/Android.bp
+++ b/audio/aidl/default/apex/com.android.hardware.audio/Android.bp
@@ -38,6 +38,7 @@
"libpreprocessingaidl",
"libpresetreverbsw",
"libreverbaidl",
+ "libspatializersw",
"libvirtualizersw",
"libvisualizeraidl",
"libvolumesw",
@@ -45,6 +46,6 @@
prebuilts: [
"android.hardware.audio.service-aidl.example.rc",
"android.hardware.audio.service-aidl.xml",
- "audio_effects_config.xml",
+ "android.hardware.bluetooth.audio.xml",
],
}
diff --git a/audio/aidl/default/audio_effects_config.xml b/audio/aidl/default/audio_effects_config.xml
index 827ff80..e859a0e 100644
--- a/audio/aidl/default/audio_effects_config.xml
+++ b/audio/aidl/default/audio_effects_config.xml
@@ -72,10 +72,7 @@
<effects>
<effect name="automatic_gain_control_v2" library="pre_processing" uuid="89f38e65-d4d2-4d64-ad0e-2b3e799ea886"/>
- <effectProxy name="bassboost" uuid="14804144-a5ee-4d24-aa88-0002a5d5c51b">
- <libsw library="bassboostsw" uuid="fa8181f2-588b-11ed-9b6a-0242ac120002"/>
- <libsw library="bundle" uuid="8631f300-72e2-11df-b57e-0002a5d5c51b"/>
- </effectProxy>
+ <effect name="bassboost" library="bundle" uuid="8631f300-72e2-11df-b57e-0002a5d5c51b"/>
<effect name="downmix" library="downmix" uuid="93f04452-e4fe-41cc-91f9-e475b6d1d69f"/>
<effect name="dynamics_processing" library="dynamics_processing" uuid="e0e6539b-1781-7261-676f-6d7573696340"/>
<effect name="haptic_generator" library="haptic_generator" uuid="97c4acd1-8b82-4f2f-832e-c2fe5d7a9931"/>
@@ -86,16 +83,10 @@
<effect name="reverb_env_ins" library="reverb" uuid="c7a511a0-a3bb-11df-860e-0002a5d5c51b"/>
<effect name="reverb_pre_aux" library="reverb" uuid="f29a1400-a3bb-11df-8ddc-0002a5d5c51b"/>
<effect name="reverb_pre_ins" library="reverb" uuid="172cdf00-a3bc-11df-a72f-0002a5d5c51b"/>
- <effectProxy name="virtualizer" uuid="d3467faa-acc7-4d34-acaf-0002a5d5c51b">
- <libsw library="virtualizersw" uuid="fa819d86-588b-11ed-9b6a-0242ac120002"/>
- <libsw library="bundle" uuid="1d4033c0-8557-11df-9f2d-0002a5d5c51b"/>
- </effectProxy>
+ <effect name="virtualizer" library="bundle" uuid="1d4033c0-8557-11df-9f2d-0002a5d5c51b"/>
<effect name="visualizer" library="visualizer" uuid="d069d9e0-8329-11df-9168-0002a5d5c51b"/>
<effect name="volume" library="bundle" uuid="119341a0-8469-11df-81f9-0002a5d5c51b"/>
- <effectProxy name="equalizer" uuid="c8e70ecd-48ca-456e-8a4f-0002a5d5c51b">
- <libsw library="equalizersw" uuid="0bed4300-847d-11df-bb17-0002a5d5c51b"/>
- <libsw library="bundle" uuid="ce772f20-847d-11df-bb17-0002a5d5c51b"/>
- </effectProxy>
+ <effect name="equalizer" library="bundle" uuid="ce772f20-847d-11df-bb17-0002a5d5c51b"/>
<effect name="extension_effect" library="extensioneffect" uuid="fa81dd00-588b-11ed-9b6a-0242ac120002" type="fa81de0e-588b-11ed-9b6a-0242ac120002"/>
<effect name="acoustic_echo_canceler" library="pre_processing" uuid="bb392ec0-8d4d-11e0-a896-0002a5d5c51b"/>
<effect name="noise_suppression" library="pre_processing" uuid="c06c8400-8e06-11e0-9cb6-0002a5d5c51b"/>
diff --git a/audio/aidl/default/bluetooth/StreamBluetooth.cpp b/audio/aidl/default/bluetooth/StreamBluetooth.cpp
index 77e48df..f22b7a9 100644
--- a/audio/aidl/default/bluetooth/StreamBluetooth.cpp
+++ b/audio/aidl/default/bluetooth/StreamBluetooth.cpp
@@ -16,16 +16,13 @@
#include <algorithm>
-#define ATRACE_TAG ATRACE_TAG_AUDIO
#define LOG_TAG "AHAL_StreamBluetooth"
#include <Utils.h>
#include <android-base/logging.h>
#include <audio_utils/clock.h>
-#include <utils/Trace.h>
#include "core-impl/StreamBluetooth.h"
-using aidl::android::hardware::audio::common::frameCountFromDurationUs;
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::hardware::audio::core::VendorParameter;
@@ -67,8 +64,6 @@
: (mIsInput ? kBluetoothDefaultInputBufferMs
: kBluetoothDefaultOutputBufferMs) *
1000),
- mPreferredFrameCount(
- frameCountFromDurationUs(mPreferredDataIntervalUs, pcmConfig.sampleRateHz)),
mBtDeviceProxy(btDeviceProxy) {}
::android::status_t StreamBluetooth::init() {
@@ -77,6 +72,7 @@
// This is a normal situation in VTS tests.
LOG(INFO) << __func__ << ": no BT HAL proxy, stream is non-functional";
}
+ LOG(INFO) << __func__ << ": preferred data interval (us): " << mPreferredDataIntervalUs;
return ::android::OK;
}
@@ -108,12 +104,10 @@
LOG(ERROR) << __func__ << ": state= " << mBtDeviceProxy->getState() << " failed to start";
return -EIO;
}
- const size_t fc = std::min(frameCount, mPreferredFrameCount);
- const size_t bytesToTransfer = fc * mFrameSizeBytes;
+ const size_t bytesToTransfer = frameCount * mFrameSizeBytes;
const size_t bytesTransferred = mIsInput ? mBtDeviceProxy->readData(buffer, bytesToTransfer)
: mBtDeviceProxy->writeData(buffer, bytesToTransfer);
*actualFrameCount = bytesTransferred / mFrameSizeBytes;
- ATRACE_INT("BTdropped", bytesToTransfer - bytesTransferred);
PresentationPosition presentation_position;
if (!mBtDeviceProxy->getPresentationPosition(presentation_position)) {
presentation_position.remoteDeviceAudioDelayNanos =
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index ce71d70..d6e8905 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -211,9 +211,12 @@
const int32_t rawSizeFrames =
aidl::android::hardware::audio::common::frameCountFromDurationMs(latencyMs,
sampleRateHz);
- if (latencyMs >= 5) return rawSizeFrames;
+ // Round up to nearest 16 frames since in the framework this is the size of a mixer burst.
+ const int32_t multipleOf16 = (rawSizeFrames + 15) & ~15;
+ if (multipleOf16 <= 512) return multipleOf16;
+ // Larger buffers should use powers of 2.
int32_t powerOf2 = 1;
- while (powerOf2 < rawSizeFrames) powerOf2 <<= 1;
+ while (powerOf2 < multipleOf16) powerOf2 <<= 1;
return powerOf2;
}
diff --git a/audio/aidl/default/include/core-impl/StreamBluetooth.h b/audio/aidl/default/include/core-impl/StreamBluetooth.h
index 35c3183..7f4239c 100644
--- a/audio/aidl/default/include/core-impl/StreamBluetooth.h
+++ b/audio/aidl/default/include/core-impl/StreamBluetooth.h
@@ -63,7 +63,6 @@
const std::weak_ptr<IBluetoothA2dp> mBluetoothA2dp;
const std::weak_ptr<IBluetoothLe> mBluetoothLe;
const size_t mPreferredDataIntervalUs;
- const size_t mPreferredFrameCount;
mutable std::mutex mLock;
// The lock is also used to serialize calls to the proxy.
std::shared_ptr<::android::bluetooth::audio::aidl::BluetoothAudioPortAidl> mBtDeviceProxy
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index fa4135d..2376ed9 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -136,10 +136,12 @@
mCurrentRoute->exitStandby(mIsInput);
RETURN_STATUS_IF_ERROR(mIsInput ? inRead(buffer, frameCount, actualFrameCount)
: outWrite(buffer, frameCount, actualFrameCount));
+ mFramesSinceStart += *actualFrameCount;
+ if (!mIsInput) return ::android::OK;
+ // Only input streams need to block, for output this is implemented by MonoPipe.
const long bufferDurationUs =
(*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate();
const auto totalDurationUs = (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND;
- mFramesSinceStart += *actualFrameCount;
const long totalOffsetUs =
mFramesSinceStart * MICROS_PER_SECOND / mContext.getSampleRate() - totalDurationUs;
LOG(VERBOSE) << __func__ << ": totalOffsetUs " << totalOffsetUs;
@@ -275,8 +277,9 @@
char* buff = (char*)buffer;
size_t actuallyRead = 0;
long remainingFrames = frameCount;
- const int64_t deadlineTimeNs = ::android::uptimeNanos() +
- getDelayInUsForFrameCount(frameCount) * NANOS_PER_MICROSECOND;
+ const int64_t deadlineTimeNs =
+ ::android::uptimeNanos() +
+ getDelayInUsForFrameCount(frameCount) * NANOS_PER_MICROSECOND / 2;
while (remainingFrames > 0) {
ssize_t framesRead = source->read(buff, remainingFrames);
LOG(VERBOSE) << __func__ << ": frames read " << framesRead;
diff --git a/audio/aidl/default/spatializer/Android.bp b/audio/aidl/default/spatializer/Android.bp
index 400629e..2c229fe 100644
--- a/audio/aidl/default/spatializer/Android.bp
+++ b/audio/aidl/default/spatializer/Android.bp
@@ -35,6 +35,6 @@
],
relative_install_path: "soundfx",
visibility: [
- "//hardware/interfaces/audio/aidl/default",
+ "//hardware/interfaces/audio/aidl/default:__subpackages__",
],
}
diff --git a/audio/aidl/default/spatializer/SpatializerSw.cpp b/audio/aidl/default/spatializer/SpatializerSw.cpp
index 6d3c4bd..ab4a53e 100644
--- a/audio/aidl/default/spatializer/SpatializerSw.cpp
+++ b/audio/aidl/default/spatializer/SpatializerSw.cpp
@@ -65,8 +65,6 @@
const std::string SpatializerSw::kEffectName = "SpatializerSw";
const std::vector<Range::SpatializerRange> SpatializerSw::kRanges = {
- MAKE_RANGE(Spatializer, supportedChannelLayout, std::vector<AudioChannelLayout>{},
- std::vector<AudioChannelLayout>{}),
MAKE_RANGE(Spatializer, spatializationLevel, Spatialization::Level::NONE,
Spatialization::Level::BED_PLUS_OBJECTS),
MAKE_RANGE(Spatializer, spatializationMode, Spatialization::Mode::BINAURAL,
@@ -108,6 +106,8 @@
ndk::ScopedAStatus SpatializerSw::getParameterSpecific(const Parameter::Id& id,
Parameter::Specific* specific) {
+ RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext");
+
auto tag = id.getTag();
RETURN_IF(Parameter::Id::spatializerTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag");
auto spatializerId = id.get<Parameter::Id::spatializerTag>();
@@ -173,11 +173,19 @@
if (mParamsMap.find(tag) != mParamsMap.end()) {
return mParamsMap.at(tag);
}
+ if (tag == Spatializer::supportedChannelLayout) {
+ return Spatializer::make<Spatializer::supportedChannelLayout>(
+ {AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
+ AudioChannelLayout::LAYOUT_5POINT1)});
+ }
return std::nullopt;
}
template <typename TAG>
ndk::ScopedAStatus SpatializerSwContext::setParam(TAG tag, Spatializer spatializer) {
+ RETURN_IF(tag == Spatializer::supportedChannelLayout, EX_ILLEGAL_ARGUMENT,
+ "supportedChannelLayoutGetOnly");
+
mParamsMap[tag] = spatializer;
return ndk::ScopedAStatus::ok();
}
diff --git a/audio/aidl/default/visualizer/VisualizerSw.h b/audio/aidl/default/visualizer/VisualizerSw.h
index 4b87b04..819351a 100644
--- a/audio/aidl/default/visualizer/VisualizerSw.h
+++ b/audio/aidl/default/visualizer/VisualizerSw.h
@@ -19,20 +19,22 @@
#include <vector>
#include <aidl/android/hardware/audio/effect/BnEffect.h>
+#include <system/audio_effects/effect_visualizer.h>
#include "effect-impl/EffectImpl.h"
namespace aidl::android::hardware::audio::effect {
class VisualizerSwContext final : public EffectContext {
public:
- static const int kMinCaptureSize = 0x80;
- static const int kMaxCaptureSize = 0x400;
- static const int kMaxLatencyMs = 3000;
- static const int kMaxCaptureBufSize = 0xffff;
+ // need align the min/max capture size to VISUALIZER_CAPTURE_SIZE_MIN and
+ // VISUALIZER_CAPTURE_SIZE_MAX because of limitation in audio_utils fixedfft.
+ static constexpr int32_t kMinCaptureSize = VISUALIZER_CAPTURE_SIZE_MIN;
+ static constexpr int32_t kMaxCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX;
+ static constexpr int32_t kMaxLatencyMs = 3000;
VisualizerSwContext(int statusDepth, const Parameter::Common& common)
: EffectContext(statusDepth, common) {
LOG(DEBUG) << __func__;
- mCaptureSampleBuffer.resize(kMaxCaptureBufSize);
+ mCaptureSampleBuffer.resize(kMaxCaptureSize);
fill(mCaptureSampleBuffer.begin(), mCaptureSampleBuffer.end(), 0x80);
}
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index 5218fdd..d219fa4 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -26,6 +26,7 @@
"android.hardware.common.fmq-V1-ndk",
"libaudioaidlcommon",
"libaidlcommonsupport",
+ "libpffft",
],
header_libs: [
"libaudioaidl_headers",
@@ -36,6 +37,7 @@
"-Wextra",
"-Werror",
"-Wthread-safety",
+ "-Wno-error=unused-parameter",
],
test_config_template: "VtsHalAudioTargetTestTemplate.xml",
test_suites: [
diff --git a/audio/aidl/vts/EffectFactoryHelper.h b/audio/aidl/vts/EffectFactoryHelper.h
index 7100431..54b82d3 100644
--- a/audio/aidl/vts/EffectFactoryHelper.h
+++ b/audio/aidl/vts/EffectFactoryHelper.h
@@ -23,6 +23,7 @@
#include <aidl/Vintf.h>
#include <android/binder_auto_utils.h>
+#include <system/audio_effects/aidl_effects_utils.h>
#include "AudioHalBinderServiceUtil.h"
#include "TestUtils.h"
@@ -35,21 +36,6 @@
class EffectFactoryHelper {
public:
- explicit EffectFactoryHelper(const std::string& name) : mServiceName(name) {}
-
- void ConnectToFactoryService() {
- mEffectFactory = IFactory::fromBinder(binderUtil.connectToService(mServiceName));
- ASSERT_NE(mEffectFactory, nullptr);
- }
-
- void RestartFactoryService() {
- ASSERT_NE(mEffectFactory, nullptr);
- mEffectFactory = IFactory::fromBinder(binderUtil.restartService());
- ASSERT_NE(mEffectFactory, nullptr);
- }
-
- std::shared_ptr<IFactory> GetFactory() const { return mEffectFactory; }
-
static std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> getAllEffectDescriptors(
std::string serviceName, std::optional<AudioUuid> type = std::nullopt) {
AudioHalBinderServiceUtil util;
@@ -74,8 +60,13 @@
return result;
}
- private:
- std::shared_ptr<IFactory> mEffectFactory;
- std::string mServiceName;
- AudioHalBinderServiceUtil binderUtil;
+ static int getHalVersion(const std::shared_ptr<IFactory>& factory) {
+ int version = 0;
+ return (factory && factory->getInterfaceVersion(&version).isOk()) ? version : 0;
+ }
+
+ static bool isReopenSupported(const std::shared_ptr<IFactory>& factory) {
+ return EffectFactoryHelper::getHalVersion(factory) >=
+ aidl::android::hardware::audio::effect::kReopenSupportedVersion;
+ }
};
diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h
index 0be4e50..82a07fd 100644
--- a/audio/aidl/vts/EffectHelper.h
+++ b/audio/aidl/vts/EffectHelper.h
@@ -37,6 +37,7 @@
#include "EffectFactoryHelper.h"
#include "TestUtils.h"
+#include "pffft.hpp"
using namespace android;
using aidl::android::hardware::audio::effect::CommandId;
@@ -329,4 +330,45 @@
ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::RESET));
ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE));
}
+
+ // Find FFT bin indices for testFrequencies and get bin center frequencies
+ void roundToFreqCenteredToFftBin(std::vector<int>& testFrequencies,
+ std::vector<int>& binOffsets, const float kBinWidth) {
+ for (size_t i = 0; i < testFrequencies.size(); i++) {
+ binOffsets[i] = std::round(testFrequencies[i] / kBinWidth);
+ testFrequencies[i] = std::round(binOffsets[i] * kBinWidth);
+ }
+ }
+
+ // Generate multitone input between -1 to +1 using testFrequencies
+ void generateMultiTone(const std::vector<int>& testFrequencies, std::vector<float>& input,
+ const int samplingFrequency) {
+ for (size_t i = 0; i < input.size(); i++) {
+ input[i] = 0;
+
+ for (size_t j = 0; j < testFrequencies.size(); j++) {
+ input[i] += sin(2 * M_PI * testFrequencies[j] * i / samplingFrequency);
+ }
+ input[i] /= testFrequencies.size();
+ }
+ }
+
+ // Use FFT transform to convert the buffer to frequency domain
+ // Compute its magnitude at binOffsets
+ std::vector<float> calculateMagnitude(const std::vector<float>& buffer,
+ const std::vector<int>& binOffsets, const int nPointFFT) {
+ std::vector<float> fftInput(nPointFFT);
+ PFFFT_Setup* inputHandle = pffft_new_setup(nPointFFT, PFFFT_REAL);
+ pffft_transform_ordered(inputHandle, buffer.data(), fftInput.data(), nullptr,
+ PFFFT_FORWARD);
+ pffft_destroy_setup(inputHandle);
+ std::vector<float> bufferMag(binOffsets.size());
+ for (size_t i = 0; i < binOffsets.size(); i++) {
+ size_t k = binOffsets[i];
+ bufferMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) +
+ (fftInput[k * 2 + 1] * fftInput[k * 2 + 1]));
+ }
+
+ return bufferMag;
+ }
};
diff --git a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
index 4e86ec3..21df163 100644
--- a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp
@@ -56,10 +56,7 @@
/// Effect factory testing.
class EffectFactoryTest : public testing::TestWithParam<std::string> {
public:
- void SetUp() override {
- mFactoryHelper = std::make_unique<EffectFactoryHelper>(GetParam());
- connectAndGetFactory();
- }
+ void SetUp() override { connectAndGetFactory(); }
void TearDown() override {
for (auto& effect : mEffects) {
@@ -68,13 +65,14 @@
}
}
- std::unique_ptr<EffectFactoryHelper> mFactoryHelper;
+ std::string kServiceName = GetParam();
std::shared_ptr<IFactory> mEffectFactory;
std::vector<std::shared_ptr<IEffect>> mEffects;
const Descriptor::Identity kNullId = {.uuid = getEffectUuidNull()};
const Descriptor::Identity kZeroId = {.uuid = getEffectUuidZero()};
const Descriptor kNullDesc = {.common.id = kNullId};
const Descriptor kZeroDesc = {.common.id = kZeroId};
+ AudioHalBinderServiceUtil mBinderUtil;
template <typename Functor>
void ForEachId(const std::vector<Descriptor::Identity> ids, Functor functor) {
@@ -117,8 +115,7 @@
}
}
void connectAndGetFactory() {
- ASSERT_NO_FATAL_FAILURE(mFactoryHelper->ConnectToFactoryService());
- mEffectFactory = mFactoryHelper->GetFactory();
+ mEffectFactory = IFactory::fromBinder(mBinderUtil.connectToService(kServiceName));
ASSERT_NE(mEffectFactory, nullptr);
}
};
@@ -128,7 +125,9 @@
}
TEST_P(EffectFactoryTest, CanBeRestarted) {
- ASSERT_NO_FATAL_FAILURE(mFactoryHelper->RestartFactoryService());
+ ASSERT_NE(mEffectFactory, nullptr);
+ mEffectFactory = IFactory::fromBinder(mBinderUtil.restartService());
+ ASSERT_NE(mEffectFactory, nullptr);
}
/**
@@ -250,7 +249,8 @@
EXPECT_NE(descs.size(), 0UL);
creatAndDestroyDescs(descs);
- mFactoryHelper->RestartFactoryService();
+ mEffectFactory = IFactory::fromBinder(mBinderUtil.restartService());
+ ASSERT_NE(mEffectFactory, nullptr);
connectAndGetFactory();
creatAndDestroyDescs(descs);
@@ -263,7 +263,8 @@
EXPECT_NE(descs.size(), 0UL);
std::vector<std::shared_ptr<IEffect>> effects = createWithDescs(descs);
- ASSERT_NO_FATAL_FAILURE(mFactoryHelper->RestartFactoryService());
+ mEffectFactory = IFactory::fromBinder(mBinderUtil.restartService());
+ ASSERT_NE(mEffectFactory, nullptr);
connectAndGetFactory();
destroyEffects(effects, EX_ILLEGAL_ARGUMENT);
diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
index 5479825..4693f10 100644
--- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp
@@ -42,6 +42,7 @@
using aidl::android::hardware::audio::effect::Flags;
using aidl::android::hardware::audio::effect::IEffect;
using aidl::android::hardware::audio::effect::IFactory;
+using aidl::android::hardware::audio::effect::kReopenSupportedVersion;
using aidl::android::hardware::audio::effect::Parameter;
using aidl::android::hardware::audio::effect::State;
using aidl::android::media::audio::common::AudioDeviceDescription;
@@ -613,6 +614,10 @@
* verify reopen sequence.
*/
TEST_P(AudioEffectDataPathTest, SetCommonParameterAndReopen) {
+ if (!EffectFactoryHelper::isReopenSupported(mFactory)) {
+ GTEST_SKIP() << "Skipping test as effect does not support reopen";
+ }
+
ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
Parameter::Common common = EffectHelper::createParamCommon(
@@ -732,6 +737,10 @@
// Send data to effects and expect it to be consumed after effect reopen (IO AudioConfig change).
// Effects exposing bypass flags or operating in offload mode will be skipped.
TEST_P(AudioEffectDataPathTest, ConsumeDataAfterReopen) {
+ if (!EffectFactoryHelper::isReopenSupported(mFactory)) {
+ GTEST_SKIP() << "Skipping test as effect does not support reopen";
+ }
+
ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
Parameter::Common common = EffectHelper::createParamCommon(
diff --git a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
index aa2c05f..059d6ab 100644
--- a/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalVolumeTargetTest.cpp
@@ -21,6 +21,7 @@
using namespace android;
+using aidl::android::hardware::audio::common::getChannelCount;
using aidl::android::hardware::audio::effect::Descriptor;
using aidl::android::hardware::audio::effect::getEffectTypeUuidVolume;
using aidl::android::hardware::audio::effect::IEffect;
@@ -29,6 +30,80 @@
using aidl::android::hardware::audio::effect::Volume;
using android::hardware::audio::common::testing::detail::TestExecutionTracer;
+class VolumeControlHelper : public EffectHelper {
+ public:
+ void SetUpVolumeControl() {
+ ASSERT_NE(nullptr, mFactory);
+ ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
+ initFrameCount();
+ Parameter::Specific specific = getDefaultParamSpecific();
+ Parameter::Common common = EffectHelper::createParamCommon(
+ 0 /* session */, 1 /* ioHandle */, kSamplingFrequency /* iSampleRate */,
+ kSamplingFrequency /* oSampleRate */, mInputFrameCount /* iFrameCount */,
+ mInputFrameCount /* oFrameCount */);
+ ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &mOpenEffectReturn, EX_NONE));
+ ASSERT_NE(nullptr, mEffect);
+ }
+
+ void TearDownVolumeControl() {
+ ASSERT_NO_FATAL_FAILURE(close(mEffect));
+ ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
+ mOpenEffectReturn = IEffect::OpenEffectReturn{};
+ }
+
+ Parameter::Specific getDefaultParamSpecific() {
+ Volume vol = Volume::make<Volume::levelDb>(kMinLevel);
+ Parameter::Specific specific = Parameter::Specific::make<Parameter::Specific::volume>(vol);
+ return specific;
+ }
+
+ Parameter createVolumeParam(int param, Volume::Tag volTag) {
+ return Parameter::make<Parameter::specific>(
+ Parameter::Specific::make<Parameter::Specific::volume>(
+ (volTag == Volume::mute) ? Volume::make<Volume::mute>(param)
+ : Volume::make<Volume::levelDb>(param)));
+ }
+
+ void initFrameCount() {
+ int channelCount = getChannelCount(
+ AudioChannelLayout::make<AudioChannelLayout::layoutMask>(kDefaultChannelLayout));
+ mInputFrameCount = kBufferSize / channelCount;
+ mOutputFrameCount = kBufferSize / channelCount;
+ }
+
+ bool isLevelValid(int level) {
+ auto vol = Volume::make<Volume::levelDb>(level);
+ return isParameterValid<Volume, Range::volume>(vol, mDescriptor);
+ }
+
+ void setAndVerifyParameters(Volume::Tag volTag, int param, binder_exception_t expected) {
+ auto expectedParam = createVolumeParam(param, volTag);
+ EXPECT_STATUS(expected, mEffect->setParameter(expectedParam)) << expectedParam.toString();
+
+ if (expected == EX_NONE) {
+ Volume::Id volId = Volume::Id::make<Volume::Id::commonTag>(volTag);
+
+ auto id = Parameter::Id::make<Parameter::Id::volumeTag>(volId);
+ // get parameter
+ Parameter getParam;
+ // if set success, then get should match
+ EXPECT_STATUS(expected, mEffect->getParameter(id, &getParam));
+ EXPECT_EQ(expectedParam, getParam) << "\nexpectedParam:" << expectedParam.toString()
+ << "\ngetParam:" << getParam.toString();
+ }
+ }
+
+ static constexpr int kSamplingFrequency = 44100;
+ static constexpr int kDurationMilliSec = 2000;
+ static constexpr int kBufferSize = kSamplingFrequency * kDurationMilliSec / 1000;
+ static constexpr int kMinLevel = -96;
+ static constexpr int kDefaultChannelLayout = AudioChannelLayout::LAYOUT_STEREO;
+ long mInputFrameCount, mOutputFrameCount;
+ std::shared_ptr<IFactory> mFactory;
+ std::shared_ptr<IEffect> mEffect;
+ IEffect::OpenEffectReturn mOpenEffectReturn;
+ Descriptor mDescriptor;
+};
/**
* Here we focus on specific parameter checking, general IEffect interfaces testing performed in
* VtsAudioEffectTargetTest.
@@ -37,7 +112,8 @@
using VolumeParamTestParam =
std::tuple<std::pair<std::shared_ptr<IFactory>, Descriptor>, int, bool>;
-class VolumeParamTest : public ::testing::TestWithParam<VolumeParamTestParam>, public EffectHelper {
+class VolumeParamTest : public ::testing::TestWithParam<VolumeParamTestParam>,
+ public VolumeControlHelper {
public:
VolumeParamTest()
: mParamLevel(std::get<PARAM_LEVEL>(GetParam())),
@@ -45,94 +121,167 @@
std::tie(mFactory, mDescriptor) = std::get<PARAM_INSTANCE_NAME>(GetParam());
}
- void SetUp() override {
- ASSERT_NE(nullptr, mFactory);
- ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor));
+ void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpVolumeControl()); }
+ void TearDown() override { TearDownVolumeControl(); }
- Parameter::Specific specific = getDefaultParamSpecific();
- Parameter::Common common = EffectHelper::createParamCommon(
- 0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */,
- kInputFrameCount /* iFrameCount */, kOutputFrameCount /* oFrameCount */);
- IEffect::OpenEffectReturn ret;
- ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &ret, EX_NONE));
- ASSERT_NE(nullptr, mEffect);
- }
- void TearDown() override {
- ASSERT_NO_FATAL_FAILURE(close(mEffect));
- ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect));
- }
-
- Parameter::Specific getDefaultParamSpecific() {
- Volume vol = Volume::make<Volume::levelDb>(-9600);
- Parameter::Specific specific = Parameter::Specific::make<Parameter::Specific::volume>(vol);
- return specific;
- }
-
- static const long kInputFrameCount = 0x100, kOutputFrameCount = 0x100;
- std::shared_ptr<IFactory> mFactory;
- std::shared_ptr<IEffect> mEffect;
- Descriptor mDescriptor;
int mParamLevel = 0;
bool mParamMute = false;
-
- void SetAndGetParameters() {
- for (auto& it : mTags) {
- auto& tag = it.first;
- auto& vol = it.second;
-
- // validate parameter
- Descriptor desc;
- ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc));
- const bool valid = isParameterValid<Volume, Range::volume>(it.second, desc);
- const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT;
-
- // set parameter
- Parameter expectParam;
- Parameter::Specific specific;
- specific.set<Parameter::Specific::volume>(vol);
- expectParam.set<Parameter::specific>(specific);
- EXPECT_STATUS(expected, mEffect->setParameter(expectParam)) << expectParam.toString();
-
- // only get if parameter is in range and set success
- if (expected == EX_NONE) {
- Parameter getParam;
- Parameter::Id id;
- Volume::Id volId;
- volId.set<Volume::Id::commonTag>(tag);
- id.set<Parameter::Id::volumeTag>(volId);
- EXPECT_STATUS(EX_NONE, mEffect->getParameter(id, &getParam));
-
- EXPECT_EQ(expectParam, getParam) << "\nexpect:" << expectParam.toString()
- << "\ngetParam:" << getParam.toString();
- }
- }
- }
-
- void addLevelParam(int level) {
- Volume vol;
- vol.set<Volume::levelDb>(level);
- mTags.push_back({Volume::levelDb, vol});
- }
-
- void addMuteParam(bool mute) {
- Volume vol;
- vol.set<Volume::mute>(mute);
- mTags.push_back({Volume::mute, vol});
- }
-
- private:
- std::vector<std::pair<Volume::Tag, Volume>> mTags;
- void CleanUp() { mTags.clear(); }
};
-TEST_P(VolumeParamTest, SetAndGetLevel) {
- EXPECT_NO_FATAL_FAILURE(addLevelParam(mParamLevel));
- SetAndGetParameters();
+TEST_P(VolumeParamTest, SetAndGetParams) {
+ ASSERT_NO_FATAL_FAILURE(
+ setAndVerifyParameters(Volume::levelDb, mParamLevel,
+ isLevelValid(mParamLevel) ? EX_NONE : EX_ILLEGAL_ARGUMENT));
+ ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, mParamMute, EX_NONE));
}
-TEST_P(VolumeParamTest, SetAndGetMute) {
- EXPECT_NO_FATAL_FAILURE(addMuteParam(mParamMute));
- SetAndGetParameters();
+using VolumeDataTestParam = std::pair<std::shared_ptr<IFactory>, Descriptor>;
+
+class VolumeDataTest : public ::testing::TestWithParam<VolumeDataTestParam>,
+ public VolumeControlHelper {
+ public:
+ VolumeDataTest() {
+ std::tie(mFactory, mDescriptor) = GetParam();
+ mInput.resize(kBufferSize);
+ mInputMag.resize(mTestFrequencies.size());
+ mBinOffsets.resize(mTestFrequencies.size());
+ roundToFreqCenteredToFftBin(mTestFrequencies, mBinOffsets, kBinWidth);
+ generateMultiTone(mTestFrequencies, mInput, kSamplingFrequency);
+ mInputMag = calculateMagnitude(mInput, mBinOffsets, kNPointFFT);
+ }
+
+ std::vector<int> calculatePercentageDiff(const std::vector<float>& outputMag) {
+ std::vector<int> percentages(mTestFrequencies.size());
+
+ for (size_t i = 0; i < mInputMag.size(); i++) {
+ float diff = mInputMag[i] - outputMag[i];
+ percentages[i] = std::round(diff / mInputMag[i] * 100);
+ }
+ return percentages;
+ }
+
+ // Convert Decibel value to Percentage
+ int percentageDb(float level) { return std::round((1 - (pow(10, level / 20))) * 100); }
+
+ void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpVolumeControl()); }
+ void TearDown() override { TearDownVolumeControl(); }
+
+ static constexpr int kMaxAudioSample = 1;
+ static constexpr int kTransitionDuration = 300;
+ static constexpr int kNPointFFT = 32768;
+ static constexpr float kBinWidth = (float)kSamplingFrequency / kNPointFFT;
+ static constexpr size_t offset = kSamplingFrequency * kTransitionDuration / 1000;
+ static constexpr float kBaseLevel = 0;
+ std::vector<int> mTestFrequencies = {100, 1000};
+ std::vector<float> mInput;
+ std::vector<float> mInputMag;
+ std::vector<int> mBinOffsets;
+};
+
+TEST_P(VolumeDataTest, ApplyLevelMuteUnmute) {
+ std::vector<float> output(kBufferSize);
+ std::vector<int> diffs(mTestFrequencies.size());
+ std::vector<float> outputMag(mTestFrequencies.size());
+
+ if (!isLevelValid(kBaseLevel)) {
+ GTEST_SKIP() << "Volume Level not supported, skipping the test\n";
+ }
+
+ // Apply Volume Level
+
+ ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, kBaseLevel, EX_NONE));
+ ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
+
+ outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT);
+ diffs = calculatePercentageDiff(outputMag);
+
+ for (size_t i = 0; i < diffs.size(); i++) {
+ ASSERT_EQ(diffs[i], percentageDb(kBaseLevel));
+ }
+
+ // Apply Mute
+
+ ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, true /*mute*/, EX_NONE));
+ ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
+
+ std::vector<float> subOutputMute(output.begin() + offset, output.end());
+ outputMag = calculateMagnitude(subOutputMute, mBinOffsets, kNPointFFT);
+ diffs = calculatePercentageDiff(outputMag);
+
+ for (size_t i = 0; i < diffs.size(); i++) {
+ ASSERT_EQ(diffs[i], percentageDb(kMinLevel /*Mute*/));
+ }
+
+ // Verifying Fade out
+ outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT);
+ diffs = calculatePercentageDiff(outputMag);
+
+ for (size_t i = 0; i < diffs.size(); i++) {
+ ASSERT_LT(diffs[i], percentageDb(kMinLevel /*Mute*/));
+ }
+
+ // Apply Unmute
+
+ ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::mute, false /*unmute*/, EX_NONE));
+ ASSERT_NO_FATAL_FAILURE(processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
+
+ std::vector<float> subOutputUnmute(output.begin() + offset, output.end());
+
+ outputMag = calculateMagnitude(subOutputUnmute, mBinOffsets, kNPointFFT);
+ diffs = calculatePercentageDiff(outputMag);
+
+ for (size_t i = 0; i < diffs.size(); i++) {
+ ASSERT_EQ(diffs[i], percentageDb(kBaseLevel));
+ }
+
+ // Verifying Fade in
+ outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT);
+ diffs = calculatePercentageDiff(outputMag);
+
+ for (size_t i = 0; i < diffs.size(); i++) {
+ ASSERT_GT(diffs[i], percentageDb(kBaseLevel));
+ }
+}
+
+TEST_P(VolumeDataTest, DecreasingLevels) {
+ std::vector<int> decreasingLevels = {-24, -48, -96};
+ std::vector<float> baseOutput(kBufferSize);
+ std::vector<int> baseDiffs(mTestFrequencies.size());
+ std::vector<float> outputMag(mTestFrequencies.size());
+
+ if (!isLevelValid(kBaseLevel)) {
+ GTEST_SKIP() << "Volume Level not supported, skipping the test\n";
+ }
+
+ ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, kBaseLevel, EX_NONE));
+ ASSERT_NO_FATAL_FAILURE(
+ processAndWriteToOutput(mInput, baseOutput, mEffect, &mOpenEffectReturn));
+
+ outputMag = calculateMagnitude(baseOutput, mBinOffsets, kNPointFFT);
+ baseDiffs = calculatePercentageDiff(outputMag);
+
+ for (int level : decreasingLevels) {
+ std::vector<float> output(kBufferSize);
+ std::vector<int> diffs(mTestFrequencies.size());
+
+ // Skipping the further steps for unnsupported level values
+ if (!isLevelValid(level)) {
+ continue;
+ }
+ ASSERT_NO_FATAL_FAILURE(setAndVerifyParameters(Volume::levelDb, level, EX_NONE));
+ ASSERT_NO_FATAL_FAILURE(
+ processAndWriteToOutput(mInput, output, mEffect, &mOpenEffectReturn));
+
+ outputMag = calculateMagnitude(output, mBinOffsets, kNPointFFT);
+ diffs = calculatePercentageDiff(outputMag);
+
+ // Decrease in volume level results in greater magnitude difference
+ for (size_t i = 0; i < diffs.size(); i++) {
+ ASSERT_GT(diffs[i], baseDiffs[i]);
+ }
+
+ baseDiffs = diffs;
+ }
}
std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> kDescPair;
@@ -157,6 +306,20 @@
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VolumeParamTest);
+INSTANTIATE_TEST_SUITE_P(VolumeTest, VolumeDataTest,
+ testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors(
+ IFactory::descriptor, getEffectTypeUuidVolume())),
+ [](const testing::TestParamInfo<VolumeDataTest::ParamType>& info) {
+ auto descriptor = info.param;
+ std::string name = getPrefix(descriptor.second);
+ std::replace_if(
+ name.begin(), name.end(),
+ [](const char c) { return !std::isalnum(c); }, '_');
+ return name;
+ });
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VolumeDataTest);
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
diff --git a/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp
index c7761c5..1eb6a6d 100644
--- a/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp
+++ b/bluetooth/audio/aidl/default/A2dpOffloadAudioProvider.cpp
@@ -120,6 +120,12 @@
ndk::ScopedAStatus A2dpOffloadAudioProvider::parseA2dpConfiguration(
const CodecId& codec_id, const std::vector<uint8_t>& configuration,
CodecParameters* codec_parameters, A2dpStatus* _aidl_return) {
+ if (!kEnableA2dpCodecExtensibility) {
+ // parseA2dpConfiguration must not be implemented if A2dp codec
+ // extensibility is not supported.
+ return ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION);
+ }
+
auto codec = codec_factory_.GetCodec(codec_id);
if (!codec) {
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
@@ -136,6 +142,12 @@
const std::vector<A2dpRemoteCapabilities>& remote_a2dp_capabilities,
const A2dpConfigurationHint& hint,
std::optional<audio::A2dpConfiguration>* _aidl_return) {
+ if (!kEnableA2dpCodecExtensibility) {
+ // getA2dpConfiguration must not be implemented if A2dp codec
+ // extensibility is not supported.
+ return ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION);
+ }
+
*_aidl_return = std::nullopt;
A2dpConfiguration avdtp_configuration;
diff --git a/bluetooth/audio/aidl/default/Android.bp b/bluetooth/audio/aidl/default/Android.bp
index 69db1b3..af6bf86 100644
--- a/bluetooth/audio/aidl/default/Android.bp
+++ b/bluetooth/audio/aidl/default/Android.bp
@@ -40,3 +40,10 @@
"libbluetooth_audio_session_aidl",
],
}
+
+prebuilt_etc {
+ name: "android.hardware.bluetooth.audio.xml",
+ src: "bluetooth_audio.xml",
+ sub_dir: "vintf",
+ installable: false,
+}
diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProvider.h b/bluetooth/audio/aidl/default/BluetoothAudioProvider.h
index 2c21440..866eaeb 100644
--- a/bluetooth/audio/aidl/default/BluetoothAudioProvider.h
+++ b/bluetooth/audio/aidl/default/BluetoothAudioProvider.h
@@ -35,6 +35,23 @@
namespace bluetooth {
namespace audio {
+/// Enable flag for the reference implementation for A2dp Codec
+/// Extensibility.
+///
+/// A2dp Codec extensibility cannot be enabled until the following
+/// requirements are fulfilled.
+///
+/// 1. The Bluetooth controller must support the HCI Requirements
+/// v1.04 or later, and must support the vendor HCI command
+/// A2DP Offload Start (v2), A2DP Offload Stop (v2) as indicated
+/// by the field a2dp_offload_v2 of the vendor capabilities.
+///
+/// 2. The implementation of the provider must be completed with
+/// DSP configuration for streaming.
+enum : bool {
+ kEnableA2dpCodecExtensibility = false,
+};
+
class BluetoothAudioProvider : public BnBluetoothAudioProvider {
public:
BluetoothAudioProvider();
diff --git a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp
index c7c6e6d..584640b 100644
--- a/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp
+++ b/bluetooth/audio/aidl/default/BluetoothAudioProviderFactory.cpp
@@ -159,6 +159,12 @@
if (session_type == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
+ if (!kEnableA2dpCodecExtensibility) {
+ // Implementing getProviderInfo equates supporting
+ // A2dp codec extensibility.
+ return ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION);
+ }
+
auto& provider_info = _aidl_return->emplace();
provider_info.name = a2dp_offload_codec_factory_.name;
diff --git a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
index 7b98634..789e8a1 100644
--- a/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
+++ b/bluetooth/audio/aidl/vts/VtsHalBluetoothAudioTargetTest.cpp
@@ -2583,6 +2583,10 @@
TEST_P(BluetoothAudioProviderLeAudioOutputHardwareAidl,
GetDataPathConfiguration) {
+ if (GetProviderFactoryInterfaceVersion() <
+ BluetoothAudioHalVersion::VERSION_AIDL_V4) {
+ GTEST_SKIP();
+ }
IBluetoothAudioProvider::StreamConfig sink_requirement;
IBluetoothAudioProvider::StreamConfig source_requirement;
std::vector<IBluetoothAudioProvider::LeAudioDataPathConfiguration>
diff --git a/gnss/1.1/vts/functional/gnss_hal_test.cpp b/gnss/1.1/vts/functional/gnss_hal_test.cpp
index 6663a19..5ec9806 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test.cpp
+++ b/gnss/1.1/vts/functional/gnss_hal_test.cpp
@@ -168,8 +168,7 @@
manager->listManifestByInterface(
"android.hardware.gnss@1.1::IGnss",
[&hasGnssHalVersion_1_1](const hidl_vec<hidl_string>& registered) {
- ASSERT_EQ(1, registered.size());
- hasGnssHalVersion_1_1 = true;
+ hasGnssHalVersion_1_1 = registered.size() != 0;
});
bool hasGnssHalVersion_2_0 = false;
diff --git a/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp b/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp
index 4778020..0430ea7 100644
--- a/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp
+++ b/graphics/allocator/aidl/vts/VtsHalGraphicsAllocatorAidl_TargetTest.cpp
@@ -144,7 +144,8 @@
auto status = mAllocator->getIMapperLibrarySuffix(&mapperSuffix);
ASSERT_TRUE(status.isOk());
std::string lib_name = "mapper." + mapperSuffix + ".so";
- void* so = android_load_sphal_library(lib_name.c_str(), RTLD_LOCAL | RTLD_NOW);
+ void* so = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.c_str(),
+ RTLD_LOCAL | RTLD_NOW);
ASSERT_NE(nullptr, so) << "Failed to load " << lib_name;
auto loadIMapper = (AIMapper_loadIMapperFn)dlsym(so, "AIMapper_loadIMapper");
ASSERT_NE(nullptr, loadIMapper) << "AIMapper_locaIMapper missing from " << lib_name;
diff --git a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
index b329de2..1e0c427 100644
--- a/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
+++ b/graphics/mapper/stable-c/vts/VtsHalGraphicsMapperStableC_TargetTest.cpp
@@ -166,7 +166,8 @@
auto status = mAllocator->getIMapperLibrarySuffix(&mapperSuffix);
ASSERT_TRUE(status.isOk()) << "Failed to get IMapper library suffix";
std::string lib_name = "mapper." + mapperSuffix + ".so";
- void* so = android_load_sphal_library(lib_name.c_str(), RTLD_LOCAL | RTLD_NOW);
+ void* so = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.c_str(),
+ RTLD_LOCAL | RTLD_NOW);
ASSERT_NE(nullptr, so) << "Failed to load " << lib_name;
mIMapperLoader = (AIMapper_loadIMapperFn)dlsym(so, "AIMapper_loadIMapper");
ASSERT_NE(nullptr, mIMapperLoader) << "AIMapper_locaIMapper missing from " << lib_name;
diff --git a/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp b/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
index 1bff076..a076438 100644
--- a/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
+++ b/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
@@ -51,7 +51,7 @@
};
using KeymasterVec = std::vector<sp<IKeymasterDevice>>;
- using ByteString = std::basic_string<uint8_t>;
+ using ByteString = std::vector<uint8_t>;
// using NonceVec = std::vector<HidlBuf>;
GetParamsResult getHmacSharingParameters(IKeymasterDevice& keymaster) {
@@ -98,7 +98,7 @@
std::vector<ByteString> copyNonces(const hidl_vec<HmacSharingParameters>& paramsVec) {
std::vector<ByteString> nonces;
for (auto& param : paramsVec) {
- nonces.emplace_back(param.nonce.data(), param.nonce.size());
+ nonces.emplace_back(param.nonce.data(), param.nonce.data() + param.nonce.size());
}
return nonces;
}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index aeb0163..4ebafee 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -794,33 +794,40 @@
in @nullable HardwareAuthToken authToken);
/**
- * Called by client to notify the IKeyMintDevice that the device is now locked, and keys with
- * the UNLOCKED_DEVICE_REQUIRED tag should no longer be usable. When this function is called,
- * the IKeyMintDevice should note the current timestamp, and attempts to use
- * UNLOCKED_DEVICE_REQUIRED keys must be rejected with Error::DEVICE_LOCKED until an
- * authentication token with a later timestamp is presented. If the `passwordOnly' argument is
- * set to true the sufficiently-recent authentication token must indicate that the user
- * authenticated with a password, not a biometric.
+ * This method is deprecated and has never been used. Implementations should return
+ * ErrorCode::UNIMPLEMENTED.
*
- * Note that the IKeyMintDevice UNLOCKED_DEVICE_REQUIRED semantics are slightly different from
- * the UNLOCKED_DEVICE_REQUIRED semantics enforced by keystore. Keystore handles device locking
- * on a per-user basis. Because auth tokens do not contain an Android user ID, it's not
- * possible to replicate the keystore enforcement logic in IKeyMintDevice. So from the
- * IKeyMintDevice perspective, any user unlock unlocks all UNLOCKED_DEVICE_REQUIRED keys.
- * Keystore will continue enforcing the per-user device locking.
+ * This method was originally intended to be used to notify KeyMint that the device is now
+ * locked, and keys with the UNLOCKED_DEVICE_REQUIRED tag should no longer be usable until a
+ * later valid HardwareAuthToken is presented. However, Android has never called this method
+ * and it cannot start doing so, because KeyMint's enforcement of UNLOCKED_DEVICE_REQUIRED did
+ * not provide the correct semantics and therefore could never be enabled. Specifically, the
+ * following issues existed with the design of KeyMint's enforcement of
+ * UNLOCKED_DEVICE_REQUIRED:
*
- * @param passwordOnly specifies whether the device must be unlocked with a password, rather
- * than a biometric, before UNLOCKED_DEVICE_REQUIRED keys can be used.
+ * o It assumed a global device lock state only. Android actually has a separate lock state for
+ * each user. See the javadoc for KeyguardManager#isDeviceLocked().
+ * o It assumed that unlocking the device involves a successful user authentication that
+ * generates a HardwareAuthToken. This is not necessarily the case, since Android supports
+ * weaker unlock methods including class 1 and 2 biometrics and trust agents. These unlock
+ * methods do not generate a HardwareAuthToken or interact with KeyMint in any way. Also,
+ * UNLOCKED_DEVICE_REQUIRED must work even for users who do not have a secure lock screen.
+ * o It would have made UNLOCKED_DEVICE_REQUIRED incompatible with requiring user
+ * authentication in some cases. These two key protections can each require a different
+ * HardwareAuthToken, but KeyMint only supports one HardwareAuthToken per operation.
+ * o It would have provided no security benefit over Keystore's enforcement of
+ * UNLOCKED_DEVICE_REQUIRED. This is because since Android 12, Keystore enforces
+ * UNLOCKED_DEVICE_REQUIRED not just logically, but it also cryptographically by
+ * superencrypting all such keys and wiping or re-encrypting the superencryption key when the
+ * device is locked (whenever possible). KeyMint is still used to support biometric unlocks,
+ * but this mechanism does not use KeyMint's direct enforcement of UNLOCKED_DEVICE_REQUIRED.
*
- * @param timestampToken is used by StrongBox implementations of IKeyMintDevice. It
- * provides the StrongBox IKeyMintDevice with a fresh, MACed timestamp which it can use as the
- * device-lock time, for future comparison against auth tokens when operations using
- * UNLOCKED_DEVICE_REQUIRED keys are attempted. Unless the auth token timestamp is newer than
- * the timestamp in the timestampToken, the device is still considered to be locked.
- * Crucially, if a StrongBox IKeyMintDevice receives a deviceLocked() call with a timestampToken
- * timestamp that is less than the timestamp in the last deviceLocked() call, it must ignore the
- * new timestamp. TEE IKeyMintDevice implementations will receive an empty timestampToken (zero
- * values and empty vectors) and should use their own clock as the device-lock time.
+ * Therefore, this method is not useful, and there is no reason for it be called.
+ * Implementations should return ErrorCode::UNIMPLEMENTED and should not include
+ * UNLOCKED_DEVICE_REQUIRED in the list of hardware-enforced key parameters.
+ *
+ * @param passwordOnly N/A due to the deprecation
+ * @param timestampToken N/A due to the deprecation
*/
void deviceLocked(in boolean passwordOnly, in @nullable TimeStampToken timestampToken);
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
index be29f59..996e4e3 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -482,11 +482,12 @@
/**
* Tag::UNLOCKED_DEVICE_REQUIRED specifies that the key may only be used when the device is
- * unlocked, as reported to KeyMint via authToken operation parameter and the
- * IKeyMintDevice::deviceLocked() method
+ * unlocked.
*
- * Must be hardware-enforced (but is also keystore-enforced on a per-user basis: see the
- * deviceLocked() documentation).
+ * This tag was originally intended to be hardware-enforced. However, the support for hardware
+ * enforcement of this tag is now considered deprecated because it cannot work correctly, and
+ * even if implemented it does nothing because it was never enabled by Keystore. Refer to the
+ * documentation for the deprecated method IKeyMintDevice::deviceLocked().
*/
UNLOCKED_DEVICE_REQUIRED = TagType.BOOL | 509,
diff --git a/security/keymint/aidl/default/Android.bp b/security/keymint/aidl/default/Android.bp
index 953630b..d815ff7 100644
--- a/security/keymint/aidl/default/Android.bp
+++ b/security/keymint/aidl/default/Android.bp
@@ -7,39 +7,29 @@
default_applicable_licenses: ["hardware_interfaces_license"],
}
-cc_binary {
+rust_binary {
name: "android.hardware.security.keymint-service",
relative_install_path: "hw",
+ vendor: true,
init_rc: ["android.hardware.security.keymint-service.rc"],
vintf_fragments: [
"android.hardware.security.keymint-service.xml",
"android.hardware.security.sharedsecret-service.xml",
"android.hardware.security.secureclock-service.xml",
],
- vendor: true,
- cflags: [
- "-Wall",
- "-Wextra",
- ],
defaults: [
- "keymint_use_latest_hal_aidl_ndk_shared",
- ],
- shared_libs: [
- "android.hardware.security.rkp-V3-ndk",
- "android.hardware.security.sharedsecret-V1-ndk",
- "android.hardware.security.secureclock-V1-ndk",
- "libbase",
- "libbinder_ndk",
- "libcppbor_external",
- "libcrypto",
- "libkeymaster_portable",
- "libkeymint",
- "liblog",
- "libpuresoftkeymasterdevice",
- "libutils",
+ "keymint_use_latest_hal_aidl_rust",
],
srcs: [
- "service.cpp",
+ "main.rs",
+ ],
+ rustlibs: [
+ "libandroid_logger",
+ "libbinder_rs",
+ "liblog_rust",
+ "libkmr_hal",
+ "libkmr_hal_nonsecure",
+ "libkmr_ta_nonsecure",
],
required: [
"android.hardware.hardware_keystore.xml",
@@ -52,3 +42,39 @@
vendor: true,
src: "android.hardware.hardware_keystore.xml",
}
+
+rust_library {
+ name: "libkmr_hal_nonsecure",
+ crate_name: "kmr_hal_nonsecure",
+ vendor_available: true,
+ lints: "android",
+ rustlibs: [
+ "libbinder_rs",
+ "libhex",
+ "liblibc",
+ "liblog_rust",
+ "libkmr_hal",
+ "libkmr_wire",
+ ],
+ srcs: ["hal/lib.rs"],
+
+}
+
+rust_library {
+ name: "libkmr_ta_nonsecure",
+ crate_name: "kmr_ta_nonsecure",
+ vendor_available: true,
+ host_supported: true,
+ lints: "android",
+ rustlibs: [
+ "libhex",
+ "liblibc",
+ "liblog_rust",
+ "libkmr_common",
+ "libkmr_crypto_boring",
+ "libkmr_ta",
+ "libkmr_wire",
+ ],
+ srcs: ["ta/lib.rs"],
+
+}
diff --git a/security/keymint/aidl/default/hal/lib.rs b/security/keymint/aidl/default/hal/lib.rs
new file mode 100644
index 0000000..621f077
--- /dev/null
+++ b/security/keymint/aidl/default/hal/lib.rs
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+//! KeyMint helper functions that are only suitable for non-secure environments
+//! such as Cuttlefish.
+
+use kmr_hal::env::get_property;
+use log::error;
+
+/// Populate attestation ID information based on properties (where available).
+/// Retrieving the serial number requires SELinux permission.
+pub fn attestation_id_info() -> kmr_wire::AttestationIdInfo {
+ let prop = |name| {
+ get_property(name)
+ .unwrap_or_else(|_| format!("{} unavailable", name))
+ .as_bytes()
+ .to_vec()
+ };
+ kmr_wire::AttestationIdInfo {
+ brand: prop("ro.product.brand"),
+ device: prop("ro.product.device"),
+ product: prop("ro.product.name"),
+ serial: prop("ro.serialno"),
+ manufacturer: prop("ro.product.manufacturer"),
+ model: prop("ro.product.model"),
+ // Currently modem_simulator always returns one fixed value. See `handleGetIMEI` in
+ // device/google/cuttlefish/host/commands/modem_simulator/misc_service.cpp for more details.
+ // TODO(b/263188546): Use device-specific IMEI values when available.
+ imei: b"867400022047199".to_vec(),
+ imei2: b"867400022047199".to_vec(),
+ meid: vec![],
+ }
+}
+
+/// Get boot information based on system properties.
+pub fn get_boot_info() -> kmr_wire::SetBootInfoRequest {
+ // No access to a verified boot key.
+ let verified_boot_key = vec![0; 32];
+ let vbmeta_digest = get_property("ro.boot.vbmeta.digest").unwrap_or_else(|_| "00".repeat(32));
+ let verified_boot_hash = hex::decode(&vbmeta_digest).unwrap_or_else(|_e| {
+ error!("failed to parse hex data in '{}'", vbmeta_digest);
+ vec![0; 32]
+ });
+ let device_boot_locked = match get_property("ro.boot.vbmeta.device_state")
+ .unwrap_or_else(|_| "no-prop".to_string())
+ .as_str()
+ {
+ "locked" => true,
+ "unlocked" => false,
+ v => {
+ error!("Unknown device_state '{}', treating as unlocked", v);
+ false
+ }
+ };
+ let verified_boot_state = match get_property("ro.boot.verifiedbootstate")
+ .unwrap_or_else(|_| "no-prop".to_string())
+ .as_str()
+ {
+ "green" => 0, // Verified
+ "yellow" => 1, // SelfSigned
+ "orange" => 2, // Unverified,
+ "red" => 3, // Failed,
+ v => {
+ error!("Unknown boot state '{}', treating as Unverified", v);
+ 2
+ }
+ };
+
+ // Attempt to get the boot patchlevel from a system property. This requires an SELinux
+ // permission, so fall back to re-using the OS patchlevel if this can't be done.
+ let boot_patchlevel_prop = get_property("ro.vendor.boot_security_patch").unwrap_or_else(|e| {
+ error!("Failed to retrieve boot patchlevel: {:?}", e);
+ get_property(kmr_hal::env::OS_PATCHLEVEL_PROPERTY)
+ .unwrap_or_else(|_| "1970-09-19".to_string())
+ });
+ let boot_patchlevel =
+ kmr_hal::env::extract_patchlevel(&boot_patchlevel_prop).unwrap_or(19700919);
+
+ kmr_wire::SetBootInfoRequest {
+ verified_boot_key,
+ device_boot_locked,
+ verified_boot_state,
+ verified_boot_hash,
+ boot_patchlevel,
+ }
+}
diff --git a/security/keymint/aidl/default/main.rs b/security/keymint/aidl/default/main.rs
new file mode 100644
index 0000000..ea432d1
--- /dev/null
+++ b/security/keymint/aidl/default/main.rs
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+//! Default implementation of the KeyMint HAL and related HALs.
+//!
+//! This implementation of the HAL is only intended to allow testing and policy compliance. A real
+//! implementation **must be implemented in a secure environment**.
+
+use kmr_hal::SerializedChannel;
+use kmr_hal_nonsecure::{attestation_id_info, get_boot_info};
+use log::{debug, error, info};
+use std::ops::DerefMut;
+use std::sync::{mpsc, Arc, Mutex};
+
+/// Name of KeyMint binder device instance.
+static SERVICE_INSTANCE: &str = "default";
+
+static KM_SERVICE_NAME: &str = "android.hardware.security.keymint.IKeyMintDevice";
+static RPC_SERVICE_NAME: &str = "android.hardware.security.keymint.IRemotelyProvisionedComponent";
+static CLOCK_SERVICE_NAME: &str = "android.hardware.security.secureclock.ISecureClock";
+static SECRET_SERVICE_NAME: &str = "android.hardware.security.sharedsecret.ISharedSecret";
+
+/// Local error type for failures in the HAL service.
+#[derive(Debug, Clone)]
+struct HalServiceError(String);
+
+impl From<String> for HalServiceError {
+ fn from(s: String) -> Self {
+ Self(s)
+ }
+}
+
+fn main() {
+ if let Err(e) = inner_main() {
+ panic!("HAL service failed: {:?}", e);
+ }
+}
+
+fn inner_main() -> Result<(), HalServiceError> {
+ // Initialize Android logging.
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("keymint-hal-nonsecure")
+ .with_max_level(log::LevelFilter::Info)
+ .with_log_buffer(android_logger::LogId::System),
+ );
+ // Redirect panic messages to logcat.
+ std::panic::set_hook(Box::new(|panic_info| {
+ error!("{}", panic_info);
+ }));
+
+ info!("Insecure KeyMint HAL service is starting.");
+
+ info!("Starting thread pool now.");
+ binder::ProcessState::start_thread_pool();
+
+ // Create a TA in-process, which acts as a local channel for communication.
+ let channel = Arc::new(Mutex::new(LocalTa::new()));
+
+ let km_service = kmr_hal::keymint::Device::new_as_binder(channel.clone());
+ let service_name = format!("{}/{}", KM_SERVICE_NAME, SERVICE_INSTANCE);
+ binder::add_service(&service_name, km_service.as_binder()).map_err(|e| {
+ HalServiceError(format!(
+ "Failed to register service {} because of {:?}.",
+ service_name, e
+ ))
+ })?;
+
+ let rpc_service = kmr_hal::rpc::Device::new_as_binder(channel.clone());
+ let service_name = format!("{}/{}", RPC_SERVICE_NAME, SERVICE_INSTANCE);
+ binder::add_service(&service_name, rpc_service.as_binder()).map_err(|e| {
+ HalServiceError(format!(
+ "Failed to register service {} because of {:?}.",
+ service_name, e
+ ))
+ })?;
+
+ let clock_service = kmr_hal::secureclock::Device::new_as_binder(channel.clone());
+ let service_name = format!("{}/{}", CLOCK_SERVICE_NAME, SERVICE_INSTANCE);
+ binder::add_service(&service_name, clock_service.as_binder()).map_err(|e| {
+ HalServiceError(format!(
+ "Failed to register service {} because of {:?}.",
+ service_name, e
+ ))
+ })?;
+
+ let secret_service = kmr_hal::sharedsecret::Device::new_as_binder(channel.clone());
+ let service_name = format!("{}/{}", SECRET_SERVICE_NAME, SERVICE_INSTANCE);
+ binder::add_service(&service_name, secret_service.as_binder()).map_err(|e| {
+ HalServiceError(format!(
+ "Failed to register service {} because of {:?}.",
+ service_name, e
+ ))
+ })?;
+
+ info!("Successfully registered KeyMint HAL services.");
+
+ // Let the TA know information about the boot environment. In a real device this
+ // is communicated directly from the bootloader to the TA, but here we retrieve
+ // the information from system properties and send from the HAL service.
+ let boot_req = get_boot_info();
+ debug!("boot/HAL->TA: boot info is {:?}", boot_req);
+ kmr_hal::send_boot_info(channel.lock().unwrap().deref_mut(), boot_req)
+ .map_err(|e| HalServiceError(format!("Failed to send boot info: {:?}", e)))?;
+
+ // Let the TA know information about the userspace environment.
+ if let Err(e) = kmr_hal::send_hal_info(channel.lock().unwrap().deref_mut()) {
+ error!("Failed to send HAL info: {:?}", e);
+ }
+
+ // Let the TA know about attestation IDs. (In a real device these would be pre-provisioned into
+ // the TA.)
+ let attest_ids = attestation_id_info();
+ if let Err(e) = kmr_hal::send_attest_ids(channel.lock().unwrap().deref_mut(), attest_ids) {
+ error!("Failed to send attestation ID info: {:?}", e);
+ }
+
+ info!("Successfully registered KeyMint HAL services.");
+ binder::ProcessState::join_thread_pool();
+ info!("KeyMint HAL service is terminating."); // should not reach here
+ Ok(())
+}
+
+/// Implementation of the KeyMint TA that runs locally in-process (and which is therefore
+/// insecure).
+#[derive(Debug)]
+pub struct LocalTa {
+ in_tx: mpsc::Sender<Vec<u8>>,
+ out_rx: mpsc::Receiver<Vec<u8>>,
+}
+
+impl LocalTa {
+ /// Create a new instance.
+ pub fn new() -> Self {
+ // Create a pair of channels to communicate with the TA thread.
+ let (in_tx, in_rx) = mpsc::channel();
+ let (out_tx, out_rx) = mpsc::channel();
+
+ // The TA code expects to run single threaded, so spawn a thread to run it in.
+ std::thread::spawn(move || {
+ let mut ta = kmr_ta_nonsecure::build_ta();
+ loop {
+ let req_data: Vec<u8> = in_rx.recv().expect("failed to receive next req");
+ let rsp_data = ta.process(&req_data);
+ out_tx.send(rsp_data).expect("failed to send out rsp");
+ }
+ });
+ Self { in_tx, out_rx }
+ }
+}
+
+impl SerializedChannel for LocalTa {
+ const MAX_SIZE: usize = usize::MAX;
+
+ fn execute(&mut self, req_data: &[u8]) -> binder::Result<Vec<u8>> {
+ self.in_tx
+ .send(req_data.to_vec())
+ .expect("failed to send in request");
+ Ok(self.out_rx.recv().expect("failed to receive response"))
+ }
+}
diff --git a/security/keymint/aidl/default/service.cpp b/security/keymint/aidl/default/service.cpp
deleted file mode 100644
index 10cbf07..0000000
--- a/security/keymint/aidl/default/service.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#define LOG_TAG "android.hardware.security.keymint-service"
-
-#include <android-base/logging.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-
-#include <AndroidKeyMintDevice.h>
-#include <AndroidRemotelyProvisionedComponentDevice.h>
-#include <AndroidSecureClock.h>
-#include <AndroidSharedSecret.h>
-#include <keymaster/soft_keymaster_logger.h>
-
-using aidl::android::hardware::security::keymint::AndroidKeyMintDevice;
-using aidl::android::hardware::security::keymint::AndroidRemotelyProvisionedComponentDevice;
-using aidl::android::hardware::security::keymint::SecurityLevel;
-using aidl::android::hardware::security::secureclock::AndroidSecureClock;
-using aidl::android::hardware::security::sharedsecret::AndroidSharedSecret;
-
-template <typename T, class... Args>
-std::shared_ptr<T> addService(Args&&... args) {
- std::shared_ptr<T> ser = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
- auto instanceName = std::string(T::descriptor) + "/default";
- LOG(INFO) << "adding keymint service instance: " << instanceName;
- binder_status_t status =
- AServiceManager_addService(ser->asBinder().get(), instanceName.c_str());
- CHECK_EQ(status, STATUS_OK);
- return ser;
-}
-
-int main() {
- // The global logger object required by keymaster's logging macros in keymaster/logger.h.
- keymaster::SoftKeymasterLogger km_logger;
- // Zero threads seems like a useless pool, but below we'll join this thread to it, increasing
- // the pool size to 1.
- ABinderProcess_setThreadPoolMaxThreadCount(0);
- // Add Keymint Service
- std::shared_ptr<AndroidKeyMintDevice> keyMint =
- addService<AndroidKeyMintDevice>(SecurityLevel::SOFTWARE);
- // Add Secure Clock Service
- addService<AndroidSecureClock>(keyMint);
- // Add Shared Secret Service
- addService<AndroidSharedSecret>(keyMint);
- // Add Remotely Provisioned Component Service
- addService<AndroidRemotelyProvisionedComponentDevice>(keyMint);
- ABinderProcess_joinThreadPool();
- return EXIT_FAILURE; // should not reach
-}
diff --git a/security/keymint/aidl/default/ta/attest.rs b/security/keymint/aidl/default/ta/attest.rs
new file mode 100644
index 0000000..1ce2066
--- /dev/null
+++ b/security/keymint/aidl/default/ta/attest.rs
@@ -0,0 +1,425 @@
+//
+// Copyright (C) 2022 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.
+
+//! Attestation keys and certificates.
+//!
+//! Hard-coded keys and certs copied from system/keymaster/context/soft_attestation_cert.cpp
+
+use kmr_common::{
+ crypto::ec, crypto::rsa, crypto::CurveType, crypto::KeyMaterial, wire::keymint,
+ wire::keymint::EcCurve, Error,
+};
+use kmr_ta::device::{RetrieveCertSigningInfo, SigningAlgorithm, SigningKeyType};
+
+/// RSA attestation private key in PKCS#1 format.
+///
+/// Decoded contents (using [der2ascii](https://github.com/google/der-ascii)):
+///
+/// ```
+/// SEQUENCE {
+/// INTEGER { 0 }
+/// INTEGER { `00c08323dc56881bb8302069f5b08561c6eebe7f05e2f5a842048abe8b47be76feaef25cf29b2afa3200141601429989a15fcfc6815eb363583c2fd2f20be4983283dd814b16d7e185417ae54abc296a3a6db5c004083b68c556c1f02339916419864d50b74d40aeca484c77356c895a0c275abfac499d5d7d2362f29c5e02e871` }
+/// INTEGER { 65537 }
+/// INTEGER { `00be860b0b99a802a6fb1a59438a7bb715065b09a36dc6e9cacc6bf3c02c34d7d79e94c6606428d88c7b7f6577c1cdea64074abe8e7286df1f0811dc9728260868de95d32efc96b6d084ff271a5f60defcc703e7a38e6e29ba9a3c5fc2c28076b6a896af1d34d78828ce9bddb1f34f9c9404430781298e201316725bbdbc993a41` }
+/// INTEGER { `00e1c6d927646c0916ec36826d594983740c21f1b074c4a1a59867c669795c85d3dc464c5b929e94bfb34e0dcc5014b10f13341ab7fdd5f60414d2a326cad41cc5` }
+/// INTEGER { `00da485997785cd5630fb0fd8c5254f98e538e18983aae9e6b7e6a5a7b5d343755b9218ebd40320d28387d789f76fa218bcc2d8b68a5f6418fbbeca5179ab3afbd` }
+/// INTEGER { `50fefc32649559616ed6534e154509329d93a3d810dbe5bdb982292cf78bd8badb8020ae8d57f4b71d05386ffe9e9db271ca3477a34999db76f8e5ece9c0d49d` }
+/// INTEGER { `15b74cf27cceff8bb36bf04d9d8346b09a2f70d2f4439b0f26ac7e03f7e9d1f77d4b915fd29b2823f03acb5d5200e0857ff2a803e93eee96d6235ce95442bc21` }
+/// INTEGER { `0090a745da8970b2cd649660324228c5f82856ffd665ba9a85c8d60f1b8bee717ecd2c72eae01dad86ba7654d4cf45adb5f1f2b31d9f8122cfa5f1a5570f9b2d25` }
+/// }
+/// ```
+const RSA_ATTEST_KEY: &str = concat!(
+ "3082025d02010002818100c08323dc56881bb8302069f5b08561c6eebe7f05e2",
+ "f5a842048abe8b47be76feaef25cf29b2afa3200141601429989a15fcfc6815e",
+ "b363583c2fd2f20be4983283dd814b16d7e185417ae54abc296a3a6db5c00408",
+ "3b68c556c1f02339916419864d50b74d40aeca484c77356c895a0c275abfac49",
+ "9d5d7d2362f29c5e02e871020301000102818100be860b0b99a802a6fb1a5943",
+ "8a7bb715065b09a36dc6e9cacc6bf3c02c34d7d79e94c6606428d88c7b7f6577",
+ "c1cdea64074abe8e7286df1f0811dc9728260868de95d32efc96b6d084ff271a",
+ "5f60defcc703e7a38e6e29ba9a3c5fc2c28076b6a896af1d34d78828ce9bddb1",
+ "f34f9c9404430781298e201316725bbdbc993a41024100e1c6d927646c0916ec",
+ "36826d594983740c21f1b074c4a1a59867c669795c85d3dc464c5b929e94bfb3",
+ "4e0dcc5014b10f13341ab7fdd5f60414d2a326cad41cc5024100da485997785c",
+ "d5630fb0fd8c5254f98e538e18983aae9e6b7e6a5a7b5d343755b9218ebd4032",
+ "0d28387d789f76fa218bcc2d8b68a5f6418fbbeca5179ab3afbd024050fefc32",
+ "649559616ed6534e154509329d93a3d810dbe5bdb982292cf78bd8badb8020ae",
+ "8d57f4b71d05386ffe9e9db271ca3477a34999db76f8e5ece9c0d49d024015b7",
+ "4cf27cceff8bb36bf04d9d8346b09a2f70d2f4439b0f26ac7e03f7e9d1f77d4b",
+ "915fd29b2823f03acb5d5200e0857ff2a803e93eee96d6235ce95442bc210241",
+ "0090a745da8970b2cd649660324228c5f82856ffd665ba9a85c8d60f1b8bee71",
+ "7ecd2c72eae01dad86ba7654d4cf45adb5f1f2b31d9f8122cfa5f1a5570f9b2d",
+ "25",
+);
+
+/// Attestation certificate corresponding to [`RSA_ATTEST_KEY`], signed by the key in
+/// [`RSA_ATTEST_ROOT_CERT`].
+///
+/// Decoded contents:
+///
+/// ```
+/// Certificate:
+/// Data:
+/// Version: 3 (0x2)
+/// Serial Number: 4096 (0x1000)
+/// Signature Algorithm: SHA256-RSA
+/// Issuer: C=US, O=Google, Inc., OU=Android, L=Mountain View, ST=California
+/// Validity:
+/// Not Before: 2016-01-04 12:40:53 +0000 UTC
+/// Not After : 2035-12-30 12:40:53 +0000 UTC
+/// Subject: C=US, O=Google, Inc., OU=Android, ST=California, CN=Android Software Attestation Key
+/// Subject Public Key Info:
+/// Public Key Algorithm: rsaEncryption
+/// Public Key: (1024 bit)
+/// Modulus:
+/// c0:83:23:dc:56:88:1b:b8:30:20:69:f5:b0:85:61:
+/// c6:ee:be:7f:05:e2:f5:a8:42:04:8a:be:8b:47:be:
+/// 76:fe:ae:f2:5c:f2:9b:2a:fa:32:00:14:16:01:42:
+/// 99:89:a1:5f:cf:c6:81:5e:b3:63:58:3c:2f:d2:f2:
+/// 0b:e4:98:32:83:dd:81:4b:16:d7:e1:85:41:7a:e5:
+/// 4a:bc:29:6a:3a:6d:b5:c0:04:08:3b:68:c5:56:c1:
+/// f0:23:39:91:64:19:86:4d:50:b7:4d:40:ae:ca:48:
+/// 4c:77:35:6c:89:5a:0c:27:5a:bf:ac:49:9d:5d:7d:
+/// 23:62:f2:9c:5e:02:e8:71:
+/// Exponent: 65537 (0x10001)
+/// X509v3 extensions:
+/// X509v3 Authority Key Identifier:
+/// keyid:29faf1accc4dd24c96402775b6b0e932e507fe2e
+/// X509v3 Subject Key Identifier:
+/// keyid:d40c101bf8cd63b9f73952b50e135ca6d7999386
+/// X509v3 Key Usage: critical
+/// Digital Signature, Certificate Signing
+/// X509v3 Basic Constraints: critical
+/// CA:true, pathlen:0
+/// Signature Algorithm: SHA256-RSA
+/// 9e:2d:48:5f:8c:67:33:dc:1a:85:ad:99:d7:50:23:ea:14:ec:
+/// 43:b0:e1:9d:ea:c2:23:46:1e:72:b5:19:dc:60:22:e4:a5:68:
+/// 31:6c:0b:55:c4:e6:9c:a2:2d:9f:3a:4f:93:6b:31:8b:16:78:
+/// 16:0d:88:cb:d9:8b:cc:80:9d:84:f0:c2:27:e3:6b:38:f1:fd:
+/// d1:e7:17:72:31:59:35:7d:96:f3:c5:7f:ab:9d:8f:96:61:26:
+/// 4f:b2:be:81:bb:0d:49:04:22:8a:ce:9f:f7:f5:42:2e:25:44:
+/// fa:21:07:12:5a:83:b5:55:ad:18:82:f8:40:14:9b:9c:20:63:
+/// 04:7f:
+/// ```
+const RSA_ATTEST_CERT: &str = concat!(
+ "308202b63082021fa00302010202021000300d06092a864886f70d01010b0500",
+ "3063310b30090603550406130255533113301106035504080c0a43616c69666f",
+ "726e69613116301406035504070c0d4d6f756e7461696e205669657731153013",
+ "060355040a0c0c476f6f676c652c20496e632e3110300e060355040b0c07416e",
+ "64726f6964301e170d3136303130343132343035335a170d3335313233303132",
+ "343035335a3076310b30090603550406130255533113301106035504080c0a43",
+ "616c69666f726e696131153013060355040a0c0c476f6f676c652c20496e632e",
+ "3110300e060355040b0c07416e64726f69643129302706035504030c20416e64",
+ "726f696420536f667477617265204174746573746174696f6e204b657930819f",
+ "300d06092a864886f70d010101050003818d0030818902818100c08323dc5688",
+ "1bb8302069f5b08561c6eebe7f05e2f5a842048abe8b47be76feaef25cf29b2a",
+ "fa3200141601429989a15fcfc6815eb363583c2fd2f20be4983283dd814b16d7",
+ "e185417ae54abc296a3a6db5c004083b68c556c1f02339916419864d50b74d40",
+ "aeca484c77356c895a0c275abfac499d5d7d2362f29c5e02e8710203010001a3",
+ "663064301d0603551d0e04160414d40c101bf8cd63b9f73952b50e135ca6d799",
+ "9386301f0603551d2304183016801429faf1accc4dd24c96402775b6b0e932e5",
+ "07fe2e30120603551d130101ff040830060101ff020100300e0603551d0f0101",
+ "ff040403020284300d06092a864886f70d01010b0500038181009e2d485f8c67",
+ "33dc1a85ad99d75023ea14ec43b0e19deac223461e72b519dc6022e4a568316c",
+ "0b55c4e69ca22d9f3a4f936b318b1678160d88cbd98bcc809d84f0c227e36b38",
+ "f1fdd1e717723159357d96f3c57fab9d8f9661264fb2be81bb0d4904228ace9f",
+ "f7f5422e2544fa2107125a83b555ad1882f840149b9c2063047f",
+);
+
+/// Attestation self-signed root certificate holding the key that signed [`RSA_ATTEST_CERT`].
+///
+/// Decoded contents:
+///
+/// ```
+/// Certificate:
+/// Data:
+/// Version: 3 (0x2)
+/// Serial Number: 18416584322103887884 (0xff94d9dd9f07c80c)
+/// Signature Algorithm: SHA256-RSA
+/// Issuer: C=US, O=Google, Inc., OU=Android, L=Mountain View, ST=California
+/// Validity:
+/// Not Before: 2016-01-04 12:31:08 +0000 UTC
+/// Not After : 2035-12-30 12:31:08 +0000 UTC
+/// Subject: C=US, O=Google, Inc., OU=Android, L=Mountain View, ST=California
+/// Subject Public Key Info:
+/// Public Key Algorithm: rsaEncryption
+/// Public Key: (1024 bit)
+/// Modulus:
+/// a2:6b:ad:eb:6e:2e:44:61:ef:d5:0e:82:e6:b7:94:
+/// d1:75:23:1f:77:9b:63:91:63:ff:f7:aa:ff:0b:72:
+/// 47:4e:c0:2c:43:ec:33:7c:d7:ac:ed:40:3e:8c:28:
+/// a0:66:d5:f7:87:0b:33:97:de:0e:b8:4e:13:40:ab:
+/// af:a5:27:bf:95:69:a0:31:db:06:52:65:f8:44:59:
+/// 57:61:f0:bb:f2:17:4b:b7:41:80:64:c0:28:0e:8f:
+/// 52:77:8e:db:d2:47:b6:45:e9:19:c8:e9:8b:c3:db:
+/// c2:91:3f:d7:d7:50:c4:1d:35:66:f9:57:e4:97:96:
+/// 0b:09:ac:ce:92:35:85:9b:
+/// Exponent: 65537 (0x10001)
+/// X509v3 extensions:
+/// X509v3 Authority Key Identifier:
+/// keyid:29faf1accc4dd24c96402775b6b0e932e507fe2e
+/// X509v3 Subject Key Identifier:
+/// keyid:29faf1accc4dd24c96402775b6b0e932e507fe2e
+/// X509v3 Key Usage: critical
+/// Digital Signature, Certificate Signing
+/// X509v3 Basic Constraints: critical
+/// CA:true
+/// Signature Algorithm: SHA256-RSA
+/// 4f:72:f3:36:59:8d:0e:c1:b9:74:5b:31:59:f6:f0:8d:25:49:
+/// 30:9e:a3:1c:1c:29:d2:45:2d:20:b9:4d:5f:64:b4:e8:80:c7:
+/// 78:7a:9c:39:de:a8:b3:f5:bf:2f:70:5f:47:10:5c:c5:e6:eb:
+/// 4d:06:99:61:d2:ae:9a:07:ff:f7:7c:b8:ab:eb:9c:0f:24:07:
+/// 5e:b1:7f:ba:79:71:fd:4d:5b:9e:df:14:a9:fe:df:ed:7c:c0:
+/// 88:5d:f8:dd:9b:64:32:56:d5:35:9a:e2:13:f9:8f:ce:c1:7c:
+/// dc:ef:a4:aa:b2:55:c3:83:a9:2e:fb:5c:f6:62:f5:27:52:17:
+/// be:63:
+/// ```
+const RSA_ATTEST_ROOT_CERT: &str = concat!(
+ "308202a730820210a003020102020900ff94d9dd9f07c80c300d06092a864886",
+ "f70d01010b05003063310b30090603550406130255533113301106035504080c",
+ "0a43616c69666f726e69613116301406035504070c0d4d6f756e7461696e2056",
+ "69657731153013060355040a0c0c476f6f676c652c20496e632e3110300e0603",
+ "55040b0c07416e64726f6964301e170d3136303130343132333130385a170d33",
+ "35313233303132333130385a3063310b30090603550406130255533113301106",
+ "035504080c0a43616c69666f726e69613116301406035504070c0d4d6f756e74",
+ "61696e205669657731153013060355040a0c0c476f6f676c652c20496e632e31",
+ "10300e060355040b0c07416e64726f696430819f300d06092a864886f70d0101",
+ "01050003818d0030818902818100a26badeb6e2e4461efd50e82e6b794d17523",
+ "1f779b639163fff7aaff0b72474ec02c43ec337cd7aced403e8c28a066d5f787",
+ "0b3397de0eb84e1340abafa527bf9569a031db065265f844595761f0bbf2174b",
+ "b7418064c0280e8f52778edbd247b645e919c8e98bc3dbc2913fd7d750c41d35",
+ "66f957e497960b09acce9235859b0203010001a3633061301d0603551d0e0416",
+ "041429faf1accc4dd24c96402775b6b0e932e507fe2e301f0603551d23041830",
+ "16801429faf1accc4dd24c96402775b6b0e932e507fe2e300f0603551d130101",
+ "ff040530030101ff300e0603551d0f0101ff040403020284300d06092a864886",
+ "f70d01010b0500038181004f72f336598d0ec1b9745b3159f6f08d2549309ea3",
+ "1c1c29d2452d20b94d5f64b4e880c7787a9c39dea8b3f5bf2f705f47105cc5e6",
+ "eb4d069961d2ae9a07fff77cb8abeb9c0f24075eb17fba7971fd4d5b9edf14a9",
+ "fedfed7cc0885df8dd9b643256d5359ae213f98fcec17cdcefa4aab255c383a9",
+ "2efb5cf662f5275217be63",
+);
+
+/// EC attestation private key in `ECPrivateKey` format.
+///
+/// Decoded contents (using [der2ascii](https://github.com/google/der-ascii)):
+///
+/// ```
+/// SEQUENCE {
+/// INTEGER { 1 }
+/// OCTET_STRING { `21e086432a15198459cf363a50fc14c9daadf935f527c2dfd71e4d6dbc42e544` }
+/// [0] {
+/// # secp256r1
+/// OBJECT_IDENTIFIER { 1.2.840.10045.3.1.7 }
+/// }
+/// [1] {
+/// BIT_STRING { `00` `04eb9e79f8426359accb2a914c8986cc70ad90669382a9732613feaccbf821274c2174974a2afea5b94d7f66d4e065106635bc53b7a0a3a671583edb3e11ae1014` }
+/// }
+/// }
+/// ```
+const EC_ATTEST_KEY: &str = concat!(
+ "3077020101042021e086432a15198459cf363a50fc14c9daadf935f527c2dfd7",
+ "1e4d6dbc42e544a00a06082a8648ce3d030107a14403420004eb9e79f8426359",
+ "accb2a914c8986cc70ad90669382a9732613feaccbf821274c2174974a2afea5",
+ "b94d7f66d4e065106635bc53b7a0a3a671583edb3e11ae1014",
+);
+
+/// Attestation certificate corresponding to [`EC_ATTEST_KEY`], signed by the key in
+/// [`EC_ATTEST_ROOT_CERT`].
+///
+/// Decoded contents:
+///
+/// ```
+/// Certificate:
+/// Data:
+/// Version: 3 (0x2)
+/// Serial Number: 4097 (0x1001)
+/// Signature Algorithm: ECDSA-SHA256
+/// Issuer: C=US, O=Google, Inc., OU=Android, L=Mountain View, ST=California, CN=Android Keystore Software Attestation Root
+/// Validity:
+/// Not Before: 2016-01-11 00:46:09 +0000 UTC
+/// Not After : 2026-01-08 00:46:09 +0000 UTC
+/// Subject: C=US, O=Google, Inc., OU=Android, ST=California, CN=Android Keystore Software Attestation Intermediate
+/// Subject Public Key Info:
+/// Public Key Algorithm: id-ecPublicKey
+/// Public Key: (256 bit)
+/// pub:
+/// 04:eb:9e:79:f8:42:63:59:ac:cb:2a:91:4c:89:86:
+/// cc:70:ad:90:66:93:82:a9:73:26:13:fe:ac:cb:f8:
+/// 21:27:4c:21:74:97:4a:2a:fe:a5:b9:4d:7f:66:d4:
+/// e0:65:10:66:35:bc:53:b7:a0:a3:a6:71:58:3e:db:
+/// 3e:11:ae:10:14:
+/// ASN1 OID: prime256v1
+/// X509v3 extensions:
+/// X509v3 Authority Key Identifier:
+/// keyid:c8ade9774c45c3a3cf0d1610e479433a215a30cf
+/// X509v3 Subject Key Identifier:
+/// keyid:3ffcacd61ab13a9e8120b8d5251cc565bb1e91a9
+/// X509v3 Key Usage: critical
+/// Digital Signature, Certificate Signing
+/// X509v3 Basic Constraints: critical
+/// CA:true, pathlen:0
+/// Signature Algorithm: ECDSA-SHA256
+/// 30:45:02:20:4b:8a:9b:7b:ee:82:bc:c0:33:87:ae:2f:c0:89:
+/// 98:b4:dd:c3:8d:ab:27:2a:45:9f:69:0c:c7:c3:92:d4:0f:8e:
+/// 02:21:00:ee:da:01:5d:b6:f4:32:e9:d4:84:3b:62:4c:94:04:
+/// ef:3a:7c:cc:bd:5e:fb:22:bb:e7:fe:b9:77:3f:59:3f:fb:
+/// ```
+const EC_ATTEST_CERT: &str = concat!(
+ "308202783082021ea00302010202021001300a06082a8648ce3d040302308198",
+ "310b30090603550406130255533113301106035504080c0a43616c69666f726e",
+ "69613116301406035504070c0d4d6f756e7461696e2056696577311530130603",
+ "55040a0c0c476f6f676c652c20496e632e3110300e060355040b0c07416e6472",
+ "6f69643133303106035504030c2a416e64726f6964204b657973746f72652053",
+ "6f667477617265204174746573746174696f6e20526f6f74301e170d31363031",
+ "31313030343630395a170d3236303130383030343630395a308188310b300906",
+ "03550406130255533113301106035504080c0a43616c69666f726e6961311530",
+ "13060355040a0c0c476f6f676c652c20496e632e3110300e060355040b0c0741",
+ "6e64726f6964313b303906035504030c32416e64726f6964204b657973746f72",
+ "6520536f667477617265204174746573746174696f6e20496e7465726d656469",
+ "6174653059301306072a8648ce3d020106082a8648ce3d03010703420004eb9e",
+ "79f8426359accb2a914c8986cc70ad90669382a9732613feaccbf821274c2174",
+ "974a2afea5b94d7f66d4e065106635bc53b7a0a3a671583edb3e11ae1014a366",
+ "3064301d0603551d0e041604143ffcacd61ab13a9e8120b8d5251cc565bb1e91",
+ "a9301f0603551d23041830168014c8ade9774c45c3a3cf0d1610e479433a215a",
+ "30cf30120603551d130101ff040830060101ff020100300e0603551d0f0101ff",
+ "040403020284300a06082a8648ce3d040302034800304502204b8a9b7bee82bc",
+ "c03387ae2fc08998b4ddc38dab272a459f690cc7c392d40f8e022100eeda015d",
+ "b6f432e9d4843b624c9404ef3a7cccbd5efb22bbe7feb9773f593ffb",
+);
+
+/// Attestation self-signed root certificate holding the key that signed [`EC_ATTEST_CERT`].
+///
+/// Decoded contents:
+///
+/// ```
+/// Certificate:
+/// Data:
+/// Version: 3 (0x2)
+/// Serial Number: 11674912229752527703 (0xa2059ed10e435b57)
+/// Signature Algorithm: ECDSA-SHA256
+/// Issuer: C=US, O=Google, Inc., OU=Android, L=Mountain View, ST=California, CN=Android Keystore Software Attestation Root
+/// Validity:
+/// Not Before: 2016-01-11 00:43:50 +0000 UTC
+/// Not After : 2036-01-06 00:43:50 +0000 UTC
+/// Subject: C=US, O=Google, Inc., OU=Android, L=Mountain View, ST=California, CN=Android Keystore Software Attestation Root
+/// Subject Public Key Info:
+/// Public Key Algorithm: id-ecPublicKey
+/// Public Key: (256 bit)
+/// pub:
+/// 04:ee:5d:5e:c7:e1:c0:db:6d:03:a6:7e:e6:b6:1b:
+/// ec:4d:6a:5d:6a:68:2e:0f:ff:7f:49:0e:7d:77:1f:
+/// 44:22:6d:bd:b1:af:fa:16:cb:c7:ad:c5:77:d2:56:
+/// 9c:aa:b7:b0:2d:54:01:5d:3e:43:2b:2a:8e:d7:4e:
+/// ec:48:75:41:a4:
+/// ASN1 OID: prime256v1
+/// X509v3 extensions:
+/// X509v3 Authority Key Identifier:
+/// keyid:c8ade9774c45c3a3cf0d1610e479433a215a30cf
+/// X509v3 Subject Key Identifier:
+/// keyid:c8ade9774c45c3a3cf0d1610e479433a215a30cf
+/// X509v3 Key Usage: critical
+/// Digital Signature, Certificate Signing
+/// X509v3 Basic Constraints: critical
+/// CA:true
+/// Signature Algorithm: ECDSA-SHA256
+/// 30:44:02:20:35:21:a3:ef:8b:34:46:1e:9c:d5:60:f3:1d:58:
+/// 89:20:6a:dc:a3:65:41:f6:0d:9e:ce:8a:19:8c:66:48:60:7b:
+/// 02:20:4d:0b:f3:51:d9:30:7c:7d:5b:da:35:34:1d:a8:47:1b:
+/// 63:a5:85:65:3c:ad:4f:24:a7:e7:4d:af:41:7d:f1:bf:
+/// ```
+const EC_ATTEST_ROOT_CERT: &str = concat!(
+ "3082028b30820232a003020102020900a2059ed10e435b57300a06082a8648ce",
+ "3d040302308198310b30090603550406130255533113301106035504080c0a43",
+ "616c69666f726e69613116301406035504070c0d4d6f756e7461696e20566965",
+ "7731153013060355040a0c0c476f6f676c652c20496e632e3110300e06035504",
+ "0b0c07416e64726f69643133303106035504030c2a416e64726f6964204b6579",
+ "73746f726520536f667477617265204174746573746174696f6e20526f6f7430",
+ "1e170d3136303131313030343335305a170d3336303130363030343335305a30",
+ "8198310b30090603550406130255533113301106035504080c0a43616c69666f",
+ "726e69613116301406035504070c0d4d6f756e7461696e205669657731153013",
+ "060355040a0c0c476f6f676c652c20496e632e3110300e060355040b0c07416e",
+ "64726f69643133303106035504030c2a416e64726f6964204b657973746f7265",
+ "20536f667477617265204174746573746174696f6e20526f6f74305930130607",
+ "2a8648ce3d020106082a8648ce3d03010703420004ee5d5ec7e1c0db6d03a67e",
+ "e6b61bec4d6a5d6a682e0fff7f490e7d771f44226dbdb1affa16cbc7adc577d2",
+ "569caab7b02d54015d3e432b2a8ed74eec487541a4a3633061301d0603551d0e",
+ "04160414c8ade9774c45c3a3cf0d1610e479433a215a30cf301f0603551d2304",
+ "1830168014c8ade9774c45c3a3cf0d1610e479433a215a30cf300f0603551d13",
+ "0101ff040530030101ff300e0603551d0f0101ff040403020284300a06082a86",
+ "48ce3d040302034700304402203521a3ef8b34461e9cd560f31d5889206adca3",
+ "6541f60d9ece8a198c6648607b02204d0bf351d9307c7d5bda35341da8471b63",
+ "a585653cad4f24a7e74daf417df1bf",
+);
+
+/// Per-algorithm attestation certificate signing information.
+pub struct CertSignAlgoInfo {
+ key: KeyMaterial,
+ chain: Vec<keymint::Certificate>,
+}
+
+/// Certificate signing information for all asymmetric key types.
+pub struct CertSignInfo {
+ rsa_info: CertSignAlgoInfo,
+ ec_info: CertSignAlgoInfo,
+}
+
+impl CertSignInfo {
+ /// Create a new cert signing impl.
+ pub fn new() -> Self {
+ CertSignInfo {
+ rsa_info: CertSignAlgoInfo {
+ key: KeyMaterial::Rsa(rsa::Key(hex::decode(RSA_ATTEST_KEY).unwrap()).into()),
+ chain: vec![
+ keymint::Certificate {
+ encoded_certificate: hex::decode(RSA_ATTEST_CERT).unwrap(),
+ },
+ keymint::Certificate {
+ encoded_certificate: hex::decode(RSA_ATTEST_ROOT_CERT).unwrap(),
+ },
+ ],
+ },
+ ec_info: CertSignAlgoInfo {
+ key: KeyMaterial::Ec(
+ EcCurve::P256,
+ CurveType::Nist,
+ ec::Key::P256(ec::NistKey(hex::decode(EC_ATTEST_KEY).unwrap())).into(),
+ ),
+ chain: vec![
+ keymint::Certificate {
+ encoded_certificate: hex::decode(EC_ATTEST_CERT).unwrap(),
+ },
+ keymint::Certificate {
+ encoded_certificate: hex::decode(EC_ATTEST_ROOT_CERT).unwrap(),
+ },
+ ],
+ },
+ }
+ }
+}
+
+impl RetrieveCertSigningInfo for CertSignInfo {
+ fn signing_key(&self, key_type: SigningKeyType) -> Result<KeyMaterial, Error> {
+ Ok(match key_type.algo_hint {
+ SigningAlgorithm::Rsa => self.rsa_info.key.clone(),
+ SigningAlgorithm::Ec => self.ec_info.key.clone(),
+ })
+ }
+
+ fn cert_chain(&self, key_type: SigningKeyType) -> Result<Vec<keymint::Certificate>, Error> {
+ Ok(match key_type.algo_hint {
+ SigningAlgorithm::Rsa => self.rsa_info.chain.clone(),
+ SigningAlgorithm::Ec => self.ec_info.chain.clone(),
+ })
+ }
+}
diff --git a/security/keymint/aidl/default/ta/clock.rs b/security/keymint/aidl/default/ta/clock.rs
new file mode 100644
index 0000000..ad8509a
--- /dev/null
+++ b/security/keymint/aidl/default/ta/clock.rs
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2022 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.
+
+//! Monotonic clock implementation.
+
+use kmr_common::crypto;
+use std::time::Instant;
+
+/// Monotonic clock.
+pub struct StdClock {
+ start: Instant,
+}
+
+impl StdClock {
+ /// Create new clock instance, holding time since construction.
+ pub fn new() -> Self {
+ Self {
+ start: Instant::now(),
+ }
+ }
+}
+
+impl crypto::MonotonicClock for StdClock {
+ fn now(&self) -> crypto::MillisecondsSinceEpoch {
+ let duration = self.start.elapsed();
+ crypto::MillisecondsSinceEpoch(duration.as_millis().try_into().unwrap())
+ }
+}
diff --git a/security/keymint/aidl/default/ta/lib.rs b/security/keymint/aidl/default/ta/lib.rs
new file mode 100644
index 0000000..fe8ad95
--- /dev/null
+++ b/security/keymint/aidl/default/ta/lib.rs
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+//! Local in-process implementation of the KeyMint TA. This is insecure and should
+//! only be used for testing purposes.
+
+// This crate is `std` using, but some of the code uses macros from a `no_std` world.
+extern crate alloc;
+
+use kmr_common::crypto;
+use kmr_crypto_boring::{
+ aes::BoringAes, aes_cmac::BoringAesCmac, des::BoringDes, ec::BoringEc, eq::BoringEq,
+ hmac::BoringHmac, rng::BoringRng, rsa::BoringRsa, sha256::BoringSha256,
+};
+use kmr_ta::device::{
+ BootloaderDone, CsrSigningAlgorithm, Implementation, TrustedPresenceUnsupported,
+};
+use kmr_ta::{HardwareInfo, KeyMintTa, RpcInfo, RpcInfoV3};
+use kmr_wire::keymint::SecurityLevel;
+use kmr_wire::rpc::MINIMUM_SUPPORTED_KEYS_IN_CSR;
+use log::info;
+
+pub mod attest;
+pub mod clock;
+pub mod rpc;
+pub mod soft;
+
+/// Build a set of crypto trait implementations based around BoringSSL and the standard library
+/// clock.
+pub fn boringssl_crypto_impls() -> crypto::Implementation {
+ let rng = BoringRng;
+ let clock = clock::StdClock::new();
+ let rsa = BoringRsa::default();
+ let ec = BoringEc::default();
+ crypto::Implementation {
+ rng: Box::new(rng),
+ clock: Some(Box::new(clock)),
+ compare: Box::new(BoringEq),
+ aes: Box::new(BoringAes),
+ des: Box::new(BoringDes),
+ hmac: Box::new(BoringHmac),
+ rsa: Box::new(rsa),
+ ec: Box::new(ec),
+ ckdf: Box::new(BoringAesCmac),
+ hkdf: Box::new(BoringHmac),
+ sha256: Box::new(BoringSha256),
+ }
+}
+
+/// Build a [`kmr_ta::KeyMintTa`] instance for nonsecure use.
+pub fn build_ta() -> kmr_ta::KeyMintTa {
+ info!("Building NON-SECURE KeyMint Rust TA");
+ let hw_info = HardwareInfo {
+ version_number: 1,
+ security_level: SecurityLevel::TrustedEnvironment,
+ impl_name: "Rust reference implementation",
+ author_name: "Google",
+ unique_id: "NON-SECURE KeyMint TA",
+ };
+ let rpc_sign_algo = CsrSigningAlgorithm::EdDSA;
+ let rpc_info_v3 = RpcInfoV3 {
+ author_name: "Google",
+ unique_id: "NON-SECURE KeyMint TA",
+ fused: false,
+ supported_num_of_keys_in_csr: MINIMUM_SUPPORTED_KEYS_IN_CSR,
+ };
+
+ let sign_info = attest::CertSignInfo::new();
+ let keys: Box<dyn kmr_ta::device::RetrieveKeyMaterial> = Box::new(soft::Keys);
+ let rpc: Box<dyn kmr_ta::device::RetrieveRpcArtifacts> = Box::new(soft::RpcArtifacts::new(
+ soft::Derive::default(),
+ rpc_sign_algo,
+ ));
+ let dev = Implementation {
+ keys,
+ sign_info: Box::new(sign_info),
+ // HAL populates attestation IDs from properties.
+ attest_ids: None,
+ sdd_mgr: None,
+ // `BOOTLOADER_ONLY` keys not supported.
+ bootloader: Box::new(BootloaderDone),
+ // `STORAGE_KEY` keys not supported.
+ sk_wrapper: None,
+ // `TRUSTED_USER_PRESENCE_REQUIRED` keys not supported
+ tup: Box::new(TrustedPresenceUnsupported),
+ // No support for converting previous implementation's keyblobs.
+ legacy_key: None,
+ rpc,
+ };
+ KeyMintTa::new(
+ hw_info,
+ RpcInfo::V3(rpc_info_v3),
+ boringssl_crypto_impls(),
+ dev,
+ )
+}
diff --git a/security/keymint/aidl/default/ta/rpc.rs b/security/keymint/aidl/default/ta/rpc.rs
new file mode 100644
index 0000000..39da50e
--- /dev/null
+++ b/security/keymint/aidl/default/ta/rpc.rs
@@ -0,0 +1,234 @@
+//
+// Copyright (C) 2022 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.
+
+//! Emulated implementation of device traits for `IRemotelyProvisionedComponent`.
+
+use core::cell::RefCell;
+use kmr_common::crypto::{ec, ec::CoseKeyPurpose, Ec, KeyMaterial};
+use kmr_common::{crypto, explicit, rpc_err, vec_try, Error};
+use kmr_crypto_boring::{ec::BoringEc, hmac::BoringHmac, rng::BoringRng};
+use kmr_ta::device::{
+ CsrSigningAlgorithm, DiceInfo, PubDiceArtifacts, RetrieveRpcArtifacts, RpcV2Req,
+};
+use kmr_wire::coset::{iana, CoseSign1Builder, HeaderBuilder};
+use kmr_wire::keymint::{Digest, EcCurve};
+use kmr_wire::{cbor::value::Value, coset::AsCborValue, rpc, CborError};
+
+/// Trait to encapsulate deterministic derivation of secret data.
+pub trait DeriveBytes {
+ /// Derive `output_len` bytes of data from `context`, deterministically.
+ fn derive_bytes(&self, context: &[u8], output_len: usize) -> Result<Vec<u8>, Error>;
+}
+
+/// Common emulated implementation of RPC artifact retrieval.
+pub struct Artifacts<T: DeriveBytes> {
+ derive: T,
+ sign_algo: CsrSigningAlgorithm,
+ // Invariant once populated: `self.dice_info.signing_algorithm` == `self.sign_algo`
+ dice_info: RefCell<Option<DiceInfo>>,
+ // Invariant once populated: `self.bcc_signing_key` is a variant that matches `self.sign_algo`
+ bcc_signing_key: RefCell<Option<ec::Key>>,
+}
+
+impl<T: DeriveBytes> RetrieveRpcArtifacts for Artifacts<T> {
+ fn derive_bytes_from_hbk(
+ &self,
+ _hkdf: &dyn crypto::Hkdf,
+ context: &[u8],
+ output_len: usize,
+ ) -> Result<Vec<u8>, Error> {
+ self.derive.derive_bytes(context, output_len)
+ }
+
+ fn get_dice_info(&self, _test_mode: rpc::TestMode) -> Result<DiceInfo, Error> {
+ if self.dice_info.borrow().is_none() {
+ let (dice_info, priv_key) = self.generate_dice_artifacts(rpc::TestMode(false))?;
+ *self.dice_info.borrow_mut() = Some(dice_info);
+ *self.bcc_signing_key.borrow_mut() = Some(priv_key);
+ }
+
+ Ok(self
+ .dice_info
+ .borrow()
+ .as_ref()
+ .ok_or_else(|| rpc_err!(Failed, "DICE artifacts are not initialized."))?
+ .clone())
+ }
+
+ fn sign_data(
+ &self,
+ ec: &dyn crypto::Ec,
+ data: &[u8],
+ _rpc_v2: Option<RpcV2Req>,
+ ) -> Result<Vec<u8>, Error> {
+ // DICE artifacts should have been initialized via `get_dice_info()` by the time this
+ // method is called.
+ let private_key = self
+ .bcc_signing_key
+ .borrow()
+ .as_ref()
+ .ok_or_else(|| rpc_err!(Failed, "DICE artifacts are not initialized."))?
+ .clone();
+
+ let mut op = ec.begin_sign(private_key.into(), self.signing_digest())?;
+ op.update(data)?;
+ let sig = op.finish()?;
+ crypto::ec::to_cose_signature(self.signing_curve(), sig)
+ }
+}
+
+impl<T: DeriveBytes> Artifacts<T> {
+ /// Constructor.
+ pub fn new(derive: T, sign_algo: CsrSigningAlgorithm) -> Self {
+ Self {
+ derive,
+ sign_algo,
+ dice_info: RefCell::new(None),
+ bcc_signing_key: RefCell::new(None),
+ }
+ }
+
+ /// Indicate the curve used in signing.
+ fn signing_curve(&self) -> EcCurve {
+ match self.sign_algo {
+ CsrSigningAlgorithm::ES256 => EcCurve::P256,
+ CsrSigningAlgorithm::ES384 => EcCurve::P384,
+ CsrSigningAlgorithm::EdDSA => EcCurve::Curve25519,
+ }
+ }
+
+ /// Indicate the digest used in signing.
+ fn signing_digest(&self) -> Digest {
+ match self.sign_algo {
+ CsrSigningAlgorithm::ES256 => Digest::Sha256,
+ CsrSigningAlgorithm::ES384 => Digest::Sha384,
+ CsrSigningAlgorithm::EdDSA => Digest::None,
+ }
+ }
+
+ /// Indicate the COSE algorithm value associated with signing.
+ fn signing_cose_algo(&self) -> iana::Algorithm {
+ match self.sign_algo {
+ CsrSigningAlgorithm::ES256 => iana::Algorithm::ES256,
+ CsrSigningAlgorithm::ES384 => iana::Algorithm::ES384,
+ CsrSigningAlgorithm::EdDSA => iana::Algorithm::EdDSA,
+ }
+ }
+
+ fn generate_dice_artifacts(
+ &self,
+ _test_mode: rpc::TestMode,
+ ) -> Result<(DiceInfo, ec::Key), Error> {
+ let ec = BoringEc::default();
+
+ let key_material = match self.sign_algo {
+ CsrSigningAlgorithm::EdDSA => {
+ let secret = self.derive_bytes_from_hbk(&BoringHmac, b"Device Key Seed", 32)?;
+ ec::import_raw_ed25519_key(&secret)
+ }
+ // TODO: generate the *same* key after reboot, by use of the TPM.
+ CsrSigningAlgorithm::ES256 => {
+ ec.generate_nist_key(&mut BoringRng, ec::NistCurve::P256, &[])
+ }
+ CsrSigningAlgorithm::ES384 => {
+ ec.generate_nist_key(&mut BoringRng, ec::NistCurve::P384, &[])
+ }
+ }?;
+ let (pub_cose_key, private_key) = match key_material {
+ KeyMaterial::Ec(curve, curve_type, key) => (
+ key.public_cose_key(
+ &ec,
+ curve,
+ curve_type,
+ CoseKeyPurpose::Sign,
+ None, /* no key ID */
+ rpc::TestMode(false),
+ )?,
+ key,
+ ),
+ _ => {
+ return Err(rpc_err!(
+ Failed,
+ "expected the Ec variant of KeyMaterial for the cdi leaf key."
+ ))
+ }
+ };
+
+ let cose_key_cbor = pub_cose_key.to_cbor_value().map_err(CborError::from)?;
+ let cose_key_cbor_data = kmr_ta::rkp::serialize_cbor(&cose_key_cbor)?;
+
+ // Construct `DiceChainEntryPayload`
+ let dice_chain_entry_payload = Value::Map(vec_try![
+ // Issuer
+ (
+ Value::Integer(1.into()),
+ Value::Text(String::from("Issuer"))
+ ),
+ // Subject
+ (
+ Value::Integer(2.into()),
+ Value::Text(String::from("Subject"))
+ ),
+ // Subject public key
+ (
+ Value::Integer((-4670552).into()),
+ Value::Bytes(cose_key_cbor_data)
+ ),
+ // Key Usage field contains a CBOR byte string of the bits which correspond
+ // to `keyCertSign` as per RFC 5280 Section 4.2.1.3 (in little-endian byte order)
+ (
+ Value::Integer((-4670553).into()),
+ Value::Bytes(vec_try![0x20]?)
+ ),
+ ]?);
+ let dice_chain_entry_payload_data = kmr_ta::rkp::serialize_cbor(&dice_chain_entry_payload)?;
+
+ // Construct `DiceChainEntry`
+ let protected = HeaderBuilder::new()
+ .algorithm(self.signing_cose_algo())
+ .build();
+ let dice_chain_entry = CoseSign1Builder::new()
+ .protected(protected)
+ .payload(dice_chain_entry_payload_data)
+ .try_create_signature(&[], |input| {
+ let mut op = ec.begin_sign(private_key.clone(), self.signing_digest())?;
+ op.update(input)?;
+ let sig = op.finish()?;
+ crypto::ec::to_cose_signature(self.signing_curve(), sig)
+ })?
+ .build();
+ let dice_chain_entry_cbor = dice_chain_entry.to_cbor_value().map_err(CborError::from)?;
+
+ // Construct `DiceCertChain`
+ let dice_cert_chain = Value::Array(vec_try![cose_key_cbor, dice_chain_entry_cbor]?);
+ let dice_cert_chain_data = kmr_ta::rkp::serialize_cbor(&dice_cert_chain)?;
+
+ // Construct `UdsCerts` as an empty CBOR map
+ let uds_certs_data = kmr_ta::rkp::serialize_cbor(&Value::Map(Vec::new()))?;
+
+ let pub_dice_artifacts = PubDiceArtifacts {
+ dice_cert_chain: dice_cert_chain_data,
+ uds_certs: uds_certs_data,
+ };
+
+ let dice_info = DiceInfo {
+ pub_dice_artifacts,
+ signing_algorithm: self.sign_algo,
+ rpc_v2_test_cdi_priv: None,
+ };
+
+ Ok((dice_info, explicit!(private_key)?))
+ }
+}
diff --git a/security/keymint/aidl/default/ta/soft.rs b/security/keymint/aidl/default/ta/soft.rs
new file mode 100644
index 0000000..5bbe060
--- /dev/null
+++ b/security/keymint/aidl/default/ta/soft.rs
@@ -0,0 +1,67 @@
+//
+// Copyright (C) 2022 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.
+
+//! Software-only trait implementations using fake keys.
+
+use kmr_common::{
+ crypto,
+ crypto::{Hkdf, Rng},
+ Error,
+};
+use kmr_crypto_boring::{hmac::BoringHmac, rng::BoringRng};
+use kmr_ta::device::RetrieveKeyMaterial;
+
+/// Root key retrieval using hard-coded fake keys.
+pub struct Keys;
+
+impl RetrieveKeyMaterial for Keys {
+ fn root_kek(&self, _context: &[u8]) -> Result<crypto::OpaqueOr<crypto::hmac::Key>, Error> {
+ // Matches `MASTER_KEY` in system/keymaster/key_blob_utils/software_keyblobs.cpp
+ Ok(crypto::hmac::Key::new([0; 16].to_vec()).into())
+ }
+ fn kak(&self) -> Result<crypto::OpaqueOr<crypto::aes::Key>, Error> {
+ // Matches `kFakeKeyAgreementKey` in
+ // system/keymaster/km_openssl/soft_keymaster_enforcement.cpp.
+ Ok(crypto::aes::Key::Aes256([0; 32]).into())
+ }
+ fn unique_id_hbk(&self, _ckdf: &dyn crypto::Ckdf) -> Result<crypto::hmac::Key, Error> {
+ // Matches value used in system/keymaster/contexts/pure_soft_keymaster_context.cpp.
+ crypto::hmac::Key::new_from(b"MustBeRandomBits")
+ }
+}
+
+/// Implementation of key derivation using a random fake key.
+pub struct Derive {
+ hbk: Vec<u8>,
+}
+
+impl Default for Derive {
+ fn default() -> Self {
+ // Use random data as an emulation of a hardware-backed key.
+ let mut hbk = vec![0; 32];
+ let mut rng = BoringRng;
+ rng.fill_bytes(&mut hbk);
+ Self { hbk }
+ }
+}
+
+impl crate::rpc::DeriveBytes for Derive {
+ fn derive_bytes(&self, context: &[u8], output_len: usize) -> Result<Vec<u8>, Error> {
+ BoringHmac.hkdf(&[], &self.hbk, context, output_len)
+ }
+}
+
+/// RPC artifact retrieval using software fake key.
+pub type RpcArtifacts = crate::rpc::Artifacts<Derive>;
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 087f763..c121d31 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -2024,7 +2024,7 @@
}
}
- if (KeyMintAidlTestBase::dump_Attestations) std::cout << cert_data.str();
+ if (KeyMintAidlTestBase::dump_Attestations) std::cout << "cert chain:\n" << cert_data.str();
return AssertionSuccess();
}
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index e098aca..9575183 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -1064,32 +1064,54 @@
TEST_P(NewKeyGenerationTest, RsaWithSpecifiedValidity) {
vector<uint8_t> key_blob;
vector<KeyCharacteristics> key_characteristics;
- ASSERT_EQ(ErrorCode::OK,
- GenerateKey(AuthorizationSetBuilder()
- .RsaSigningKey(2048, 65537)
- .Digest(Digest::NONE)
- .Padding(PaddingMode::NONE)
- .Authorization(TAG_CERTIFICATE_NOT_BEFORE,
- 1183806000000 /* 2007-07-07T11:00:00Z */)
- .Authorization(TAG_CERTIFICATE_NOT_AFTER,
- 1916049600000 /* 2030-09-19T12:00:00Z */),
- &key_blob, &key_characteristics));
- ASSERT_GT(cert_chain_.size(), 0);
+ vector<uint64_t> test_vector_not_before_millis = {
+ 458046000000, /* 1984-07-07T11:00:00Z */
+ 1183806000000, /* 2007-07-07T11:00:00Z */
+ 1924991999000, /* 2030-12-31T23:59:59Z */
+ 3723753599000, /* 2087-12-31T23:59:59Z */
+ 26223868799000, /* 2800-12-31T23:59:59Z */
+ 45157996799000, /* 3400-12-31T23:59:59Z */
+ 60719587199000, /* 3894-02-15T23:59:59Z */
+ 95302051199000, /* 4989-12-31T23:59:59Z */
+ 86182012799000, /* 4700-12-31T23:59:59Z */
+ 111427574399000, /* 5500-12-31T23:59:59Z */
+ 136988668799000, /* 6310-12-31T23:59:59Z */
+ 139828895999000, /* 6400-12-31T23:59:59Z */
+ 169839503999000, /* 7351-12-31T23:59:59Z */
+ 171385804799000, /* 7400-12-31T23:59:59Z */
+ 190320019199000, /* 8000-12-31T23:59:59Z */
+ 193475692799000, /* 8100-12-31T23:59:59Z */
+ 242515209599000, /* 9654-12-31T23:59:59Z */
+ 250219065599000, /* 9899-02-15T23:59:59Z */
+ };
+ for (auto notBefore : test_vector_not_before_millis) {
+ uint64_t notAfter = notBefore + 378691200000 /* 12 years milliseconds*/;
+ SCOPED_TRACE(testing::Message() << "notBefore: " << notBefore << " notAfter: " << notAfter);
+ ASSERT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048, 65537)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_CERTIFICATE_NOT_BEFORE, notBefore)
+ .Authorization(TAG_CERTIFICATE_NOT_AFTER, notAfter),
+ &key_blob, &key_characteristics));
+ ASSERT_GT(cert_chain_.size(), 0);
- X509_Ptr cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
- ASSERT_TRUE(!!cert.get());
+ X509_Ptr cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+ ASSERT_TRUE(!!cert.get());
- const ASN1_TIME* not_before = X509_get0_notBefore(cert.get());
- ASSERT_NE(not_before, nullptr);
- time_t not_before_time;
- ASSERT_EQ(ASN1_TIME_to_time_t(not_before, ¬_before_time), 1);
- EXPECT_EQ(not_before_time, 1183806000);
+ const ASN1_TIME* not_before = X509_get0_notBefore(cert.get());
+ ASSERT_NE(not_before, nullptr);
+ int64_t not_before_time;
+ ASSERT_EQ(ASN1_TIME_to_posix(not_before, ¬_before_time), 1);
+ EXPECT_EQ(not_before_time, (notBefore / 1000));
- const ASN1_TIME* not_after = X509_get0_notAfter(cert.get());
- ASSERT_NE(not_after, nullptr);
- time_t not_after_time;
- ASSERT_EQ(ASN1_TIME_to_time_t(not_after, ¬_after_time), 1);
- EXPECT_EQ(not_after_time, 1916049600);
+ const ASN1_TIME* not_after = X509_get0_notAfter(cert.get());
+ ASSERT_NE(not_after, nullptr);
+ int64_t not_after_time;
+ ASSERT_EQ(ASN1_TIME_to_posix(not_after, ¬_after_time), 1);
+ EXPECT_EQ(not_after_time, (notAfter / 1000));
+ }
}
/*
@@ -8739,40 +8761,6 @@
INSTANTIATE_KEYMINT_AIDL_TEST(EarlyBootKeyTest);
-using UnlockedDeviceRequiredTest = KeyMintAidlTestBase;
-
-// This may be a problematic test. It can't be run repeatedly without unlocking the device in
-// between runs... and on most test devices there are no enrolled credentials so it can't be
-// unlocked at all, meaning the only way to get the test to pass again on a properly-functioning
-// device is to reboot it. For that reason, this is disabled by default. It can be used as part of
-// a manual test process, which includes unlocking between runs, which is why it's included here.
-// Well, that and the fact that it's the only test we can do without also making calls into the
-// Gatekeeper HAL. We haven't written any cross-HAL tests, and don't know what all of the
-// implications might be, so that may or may not be a solution.
-TEST_P(UnlockedDeviceRequiredTest, DISABLED_KeysBecomeUnusable) {
- auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
- CreateTestKeys(TAG_UNLOCKED_DEVICE_REQUIRED, ErrorCode::OK);
- KeyBlobDeleter aes_deleter(keymint_, aesKeyData.blob);
- KeyBlobDeleter hmac_deleter(keymint_, hmacKeyData.blob);
- KeyBlobDeleter rsa_deleter(keymint_, rsaKeyData.blob);
- KeyBlobDeleter ecdsa_deleter(keymint_, ecdsaKeyData.blob);
-
- EXPECT_EQ(ErrorCode::OK, UseAesKey(aesKeyData.blob));
- EXPECT_EQ(ErrorCode::OK, UseHmacKey(hmacKeyData.blob));
- EXPECT_EQ(ErrorCode::OK, UseRsaKey(rsaKeyData.blob));
- EXPECT_EQ(ErrorCode::OK, UseEcdsaKey(ecdsaKeyData.blob));
-
- ErrorCode rc = GetReturnErrorCode(
- keyMint().deviceLocked(false /* passwordOnly */, {} /* timestampToken */));
- ASSERT_EQ(ErrorCode::OK, rc);
- EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseAesKey(aesKeyData.blob));
- EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseHmacKey(hmacKeyData.blob));
- EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseRsaKey(rsaKeyData.blob));
- EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseEcdsaKey(ecdsaKeyData.blob));
-}
-
-INSTANTIATE_KEYMINT_AIDL_TEST(UnlockedDeviceRequiredTest);
-
using VsrRequirementTest = KeyMintAidlTestBase;
// @VsrTest = VSR-3.10-008
diff --git a/security/keymint/support/remote_prov_utils_test.cpp b/security/keymint/support/remote_prov_utils_test.cpp
index 630f7bb..89469f1 100644
--- a/security/keymint/support/remote_prov_utils_test.cpp
+++ b/security/keymint/support/remote_prov_utils_test.cpp
@@ -14,20 +14,23 @@
* limitations under the License.
*/
-#include "cppbor.h"
-#include "keymaster/cppcose/cppcose.h"
#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
#include <android-base/properties.h>
+#include <cppbor.h>
#include <cppbor_parse.h>
-#include <cstdint>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/cppcose/cppcose.h>
#include <keymaster/logger.h>
#include <keymaster/remote_provisioning_utils.h>
#include <openssl/curve25519.h>
#include <remote_prov/remote_prov_utils.h>
+#include <algorithm>
+#include <cstdint>
+#include <span>
+
namespace aidl::android::hardware::security::keymint::remote_prov {
namespace {
@@ -36,7 +39,11 @@
using ::keymaster::kStatusInvalidEek;
using ::keymaster::StatusOr;
using ::testing::ElementsAreArray;
-using byte_view = std::basic_string_view<uint8_t>;
+using byte_view = std::span<const uint8_t>;
+
+inline bool equal_byte_views(const byte_view& view1, const byte_view& view2) {
+ return std::equal(view1.begin(), view1.end(), view2.begin(), view2.end());
+}
struct KeyInfoEcdsa {
CoseKeyCurve curve;
@@ -44,7 +51,8 @@
byte_view pubKeyY;
bool operator==(const KeyInfoEcdsa& other) const {
- return curve == other.curve && pubKeyX == other.pubKeyX && pubKeyY == other.pubKeyY;
+ return curve == other.curve && equal_byte_views(pubKeyX, other.pubKeyX) &&
+ equal_byte_views(pubKeyY, other.pubKeyY);
}
};
diff --git a/security/secretkeeper/aidl/vts/Android.bp b/security/secretkeeper/aidl/vts/Android.bp
index e6521ae..b9ce5ce 100644
--- a/security/secretkeeper/aidl/vts/Android.bp
+++ b/security/secretkeeper/aidl/vts/Android.bp
@@ -47,6 +47,7 @@
"android.hardware.security.secretkeeper-V1-rust",
"libauthgraph_boringssl",
"libauthgraph_core",
+ "libauthgraph_wire",
"libauthgraph_vts_test",
"libbinder_rs",
"libciborium",
diff --git a/security/secretkeeper/aidl/vts/secretkeeper_cli.rs b/security/secretkeeper/aidl/vts/secretkeeper_cli.rs
index d02bfe6..377ed37 100644
--- a/security/secretkeeper/aidl/vts/secretkeeper_cli.rs
+++ b/security/secretkeeper/aidl/vts/secretkeeper_cli.rs
@@ -120,7 +120,7 @@
fn new(instance: &str, dice_artifacts: OwnedDiceArtifactsWithExplicitKey) -> Self {
let sk: binder::Strong<dyn ISecretkeeper> =
binder::get_interface(&format!("{SECRETKEEPER_SERVICE}/{instance}")).unwrap();
- let session = SkSession::new(sk.clone(), &dice_artifacts).unwrap();
+ let session = SkSession::new(sk.clone(), &dice_artifacts, None).unwrap();
Self { sk, session, dice_artifacts }
}
diff --git a/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs b/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs
index 72d3e57..7c00aa9 100644
--- a/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs
+++ b/security/secretkeeper/aidl/vts/secretkeeper_test_client.rs
@@ -19,11 +19,11 @@
use authgraph_vts_test as ag_vts;
use authgraph_boringssl as boring;
use authgraph_core::key;
-use coset::{CborSerializable, CoseEncrypt0};
+use coset::{CborOrdering, CborSerializable, CoseEncrypt0, CoseKey};
use dice_policy_builder::{CertIndex, ConstraintSpec, ConstraintType, MissingAction, WILDCARD_FULL_ARRAY, policy_for_dice_chain};
use rdroidtest::{ignore_if, rdroidtest};
use secretkeeper_client::dice::OwnedDiceArtifactsWithExplicitKey;
-use secretkeeper_client::SkSession;
+use secretkeeper_client::{SkSession, Error as SkClientError};
use secretkeeper_core::cipher;
use secretkeeper_comm::data_types::error::SecretkeeperError;
use secretkeeper_comm::data_types::request::Request;
@@ -38,9 +38,14 @@
SUBCOMPONENT_DESCRIPTORS, SUBCOMPONENT_SECURITY_VERSION,
dice_sample::make_explicit_owned_dice
};
+use std::fs;
+use std::path::Path;
const SECRETKEEPER_SERVICE: &str = "android.hardware.security.secretkeeper.ISecretkeeper";
const CURRENT_VERSION: u64 = 1;
+const SECRETKEEPER_KEY_HOST_DT: &str =
+ "/proc/device-tree/avf/reference/avf/secretkeeper_public_key";
+
// Random bytes (of ID_SIZE/SECRET_SIZE) generated for tests.
const ID_EXAMPLE: Id = Id([
0xF1, 0xB2, 0xED, 0x3B, 0xD1, 0xBD, 0xF0, 0x7D, 0xE1, 0xF0, 0x01, 0xFC, 0x61, 0x71, 0xD3, 0x42,
@@ -65,6 +70,22 @@
0x06, 0xAC, 0x36, 0x8B, 0x3C, 0x95, 0x50, 0x16, 0x67, 0x71, 0x65, 0x26, 0xEB, 0xD0, 0xC3, 0x98,
]);
+// Android expects the public key of Secretkeeper instance to be present in the Linux device tree.
+// This allows clients to (cryptographically) verify that they are indeed talking to the real
+// secretkeeper.
+// Note that this is the identity of the `default` instance (and not `nonsecure`)!
+fn get_secretkeeper_identity() -> Option<CoseKey> {
+ let path = Path::new(SECRETKEEPER_KEY_HOST_DT);
+ if path.exists() {
+ let key = fs::read(path).unwrap();
+ let mut key = CoseKey::from_slice(&key).unwrap();
+ key.canonicalize(CborOrdering::Lexicographic);
+ Some(key)
+ } else {
+ None
+ }
+}
+
fn get_instances() -> Vec<(String, String)> {
// Determine which instances are available.
binder::get_declared_instances(SECRETKEEPER_SERVICE)
@@ -80,6 +101,7 @@
}
/// Secretkeeper client information.
+#[derive(Debug)]
struct SkClient {
sk: binder::Strong<dyn ISecretkeeper>,
session: SkSession,
@@ -95,19 +117,40 @@
impl SkClient {
/// Create an `SkClient` using the default `OwnedDiceArtifactsWithExplicitKey` for identity.
- fn new(instance: &str) -> Self {
+ fn new(instance: &str) -> Result<Self, SkClientError> {
let default_dice = make_explicit_owned_dice(/*Security version in a node */ 5);
- Self::with_identity(instance, default_dice)
+ Self::create(instance, default_dice, None)
}
- /// Create an `SkClient` using the given `OwnedDiceArtifactsWithExplicitKey` for identity.
- fn with_identity(instance: &str, dice_artifacts: OwnedDiceArtifactsWithExplicitKey) -> Self {
+ /// Create an `SkClient` using given `OwnedDiceArtifactsWithExplicitKey` as client identity.
+ fn with_identity(
+ instance: &str,
+ dice_artifacts: OwnedDiceArtifactsWithExplicitKey,
+ ) -> Result<Self, SkClientError> {
+ Self::create(instance, dice_artifacts, None)
+ }
+
+ /// Create an `SkClient` with default client identity, requiring Secretkeeper's identity to be
+ /// matched against given `expected_sk_key`.
+ fn with_expected_sk_identity(
+ instance: &str,
+ expected_sk_key: coset::CoseKey,
+ ) -> Result<Self, SkClientError> {
+ let default_dice = make_explicit_owned_dice(/*Security version in a node */ 5);
+ Self::create(instance, default_dice, Some(expected_sk_key))
+ }
+
+ fn create(
+ instance: &str,
+ dice_artifacts: OwnedDiceArtifactsWithExplicitKey,
+ expected_sk_key: Option<coset::CoseKey>,
+ ) -> Result<Self, SkClientError> {
let sk = get_connection(instance);
- Self {
+ Ok(Self {
sk: sk.clone(),
- session: SkSession::new(sk, &dice_artifacts).unwrap(),
+ session: SkSession::new(sk, &dice_artifacts, expected_sk_key)?,
dice_artifacts,
- }
+ })
}
/// This method is wrapper that use `SkSession::secret_management_request` which handles
@@ -347,7 +390,7 @@
#[rdroidtest(get_instances())]
fn secret_management_get_version(instance: String) {
- let mut sk_client = SkClient::new(&instance);
+ let mut sk_client = SkClient::new(&instance).unwrap();
let request = GetVersionRequest {};
let request_packet = request.serialize_to_packet();
@@ -364,7 +407,7 @@
#[rdroidtest(get_instances())]
fn secret_management_malformed_request(instance: String) {
- let mut sk_client = SkClient::new(&instance);
+ let mut sk_client = SkClient::new(&instance).unwrap();
let request = GetVersionRequest {};
let request_packet = request.serialize_to_packet();
@@ -383,7 +426,7 @@
#[rdroidtest(get_instances())]
fn secret_management_store_get_secret_found(instance: String) {
- let mut sk_client = SkClient::new(&instance);
+ let mut sk_client = SkClient::new(&instance).unwrap();
sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
@@ -393,7 +436,7 @@
#[rdroidtest(get_instances())]
fn secret_management_store_get_secret_not_found(instance: String) {
- let mut sk_client = SkClient::new(&instance);
+ let mut sk_client = SkClient::new(&instance).unwrap();
// Store a secret (corresponding to an id).
sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
@@ -404,7 +447,7 @@
#[rdroidtest(get_instances())]
fn secretkeeper_store_delete_ids(instance: String) {
- let mut sk_client = SkClient::new(&instance);
+ let mut sk_client = SkClient::new(&instance).unwrap();
sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE).unwrap();
@@ -420,7 +463,7 @@
#[rdroidtest(get_instances())]
fn secretkeeper_store_delete_multiple_ids(instance: String) {
- let mut sk_client = SkClient::new(&instance);
+ let mut sk_client = SkClient::new(&instance).unwrap();
sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE).unwrap();
@@ -431,7 +474,7 @@
}
#[rdroidtest(get_instances())]
fn secretkeeper_store_delete_duplicate_ids(instance: String) {
- let mut sk_client = SkClient::new(&instance);
+ let mut sk_client = SkClient::new(&instance).unwrap();
sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE).unwrap();
@@ -444,7 +487,7 @@
#[rdroidtest(get_instances())]
fn secretkeeper_store_delete_nonexistent(instance: String) {
- let mut sk_client = SkClient::new(&instance);
+ let mut sk_client = SkClient::new(&instance).unwrap();
sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE).unwrap();
@@ -459,7 +502,7 @@
#[rdroidtest(get_instances())]
#[ignore_if(|p| p != "nonsecure")]
fn secretkeeper_store_delete_all(instance: String) {
- let mut sk_client = SkClient::new(&instance);
+ let mut sk_client = SkClient::new(&instance).unwrap();
sk_client.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
sk_client.store(&ID_EXAMPLE_2, &SECRET_EXAMPLE).unwrap();
@@ -486,7 +529,7 @@
fn secret_management_replay_protection_seq_num(instance: String) {
let dice_chain = make_explicit_owned_dice(/*Security version in a node */ 5);
let sealing_policy = sealing_policy(dice_chain.explicit_key_dice_chain().unwrap());
- let sk_client = SkClient::with_identity(&instance, dice_chain);
+ let sk_client = SkClient::with_identity(&instance, dice_chain).unwrap();
// Construct encoded request packets for the test
let (req_1, req_2, req_3) = construct_secret_management_requests(sealing_policy);
@@ -522,7 +565,7 @@
fn secret_management_replay_protection_seq_num_per_session(instance: String) {
let dice_chain = make_explicit_owned_dice(/*Security version in a node */ 5);
let sealing_policy = sealing_policy(dice_chain.explicit_key_dice_chain().unwrap());
- let sk_client = SkClient::with_identity(&instance, dice_chain);
+ let sk_client = SkClient::with_identity(&instance, dice_chain).unwrap();
// Construct encoded request packets for the test
let (req_1, _, _) = construct_secret_management_requests(sealing_policy);
@@ -538,7 +581,7 @@
assert_eq!(res.response_type().unwrap(), ResponseType::Success);
// Start another session
- let sk_client_diff = SkClient::new(&instance);
+ let sk_client_diff = SkClient::new(&instance).unwrap();
// Check first request/response is with seq_0 is successful
let res = ResponsePacket::from_slice(
&sk_client_diff.secret_management_request_custom_aad(&req_1, &seq_0, &seq_0).unwrap(),
@@ -553,7 +596,7 @@
fn secret_management_replay_protection_out_of_seq_req_not_accepted(instance: String) {
let dice_chain = make_explicit_owned_dice(/*Security version in a node */ 5);
let sealing_policy = sealing_policy(dice_chain.explicit_key_dice_chain().unwrap());
- let sk_client = SkClient::with_identity(&instance, dice_chain);
+ let sk_client = SkClient::with_identity(&instance, dice_chain).unwrap();
// Construct encoded request packets for the test
let (req_1, req_2, _) = construct_secret_management_requests(sealing_policy);
@@ -578,18 +621,19 @@
#[rdroidtest(get_instances())]
fn secret_management_policy_gate(instance: String) {
let dice_chain = make_explicit_owned_dice(/*Security version in a node */ 100);
- let mut sk_client_original = SkClient::with_identity(&instance, dice_chain);
+ let mut sk_client_original = SkClient::with_identity(&instance, dice_chain).unwrap();
sk_client_original.store(&ID_EXAMPLE, &SECRET_EXAMPLE).unwrap();
assert_eq!(sk_client_original.get(&ID_EXAMPLE).unwrap(), SECRET_EXAMPLE);
// Start a session with higher security_version & get the stored secret.
let dice_chain_upgraded = make_explicit_owned_dice(/*Security version in a node */ 101);
- let mut sk_client_upgraded = SkClient::with_identity(&instance, dice_chain_upgraded);
+ let mut sk_client_upgraded = SkClient::with_identity(&instance, dice_chain_upgraded).unwrap();
assert_eq!(sk_client_upgraded.get(&ID_EXAMPLE).unwrap(), SECRET_EXAMPLE);
// Start a session with lower security_version (This should be denied access to the secret).
let dice_chain_downgraded = make_explicit_owned_dice(/*Security version in a node */ 99);
- let mut sk_client_downgraded = SkClient::with_identity(&instance, dice_chain_downgraded);
+ let mut sk_client_downgraded =
+ SkClient::with_identity(&instance, dice_chain_downgraded).unwrap();
assert!(matches!(
sk_client_downgraded.get(&ID_EXAMPLE).unwrap_err(),
Error::SecretkeeperError(SecretkeeperError::DicePolicyError)
@@ -610,6 +654,30 @@
));
}
+// This test checks that the identity of Secretkeeper (in context of AuthGraph key exchange) is
+// same as the one advertized in Linux device tree. This is only expected from `default` instance.
+#[rdroidtest(get_instances())]
+#[ignore_if(|p| p != "default")]
+fn secretkeeper_check_identity(instance: String) {
+ let sk_key = get_secretkeeper_identity()
+ .expect("Failed to extract identity of default instance from device tree");
+ // Create a session with this expected identity. This succeeds only if the identity used by
+ // Secretkeeper is sk_key.
+ let _ = SkClient::with_expected_sk_identity(&instance, sk_key).unwrap();
+ // Create a session using any other expected sk identity, this should fail. Note that the
+ // failure arises from validation which happens at the local participant.
+ let mut any_other_key = CoseKey::default();
+ any_other_key.canonicalize(CborOrdering::Lexicographic);
+ let err = SkClient::with_expected_sk_identity(&instance, any_other_key).unwrap_err();
+ assert!(matches!(
+ err,
+ SkClientError::AuthgraphError(authgraph_core::error::Error(
+ authgraph_wire::ErrorCode::InvalidPeerKeKey,
+ _
+ ))
+ ));
+}
+
// Helper method that constructs 3 SecretManagement requests. Callers would usually not care about
// what each of the request concretely is.
fn construct_secret_management_requests(sealing_policy: Vec<u8>) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
diff --git a/staging/security/see/storage/aidl/Android.bp b/staging/security/see/storage/aidl/Android.bp
new file mode 100644
index 0000000..f669be8
--- /dev/null
+++ b/staging/security/see/storage/aidl/Android.bp
@@ -0,0 +1,26 @@
+package {
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+ name: "android.hardware.security.see.storage",
+ unstable: true,
+ host_supported: true,
+ srcs: [
+ "android/hardware/security/see/storage/*.aidl",
+ ],
+ backend: {
+ java: {
+ enabled: false,
+ },
+ cpp: {
+ enabled: true,
+ },
+ ndk: {
+ enabled: true,
+ },
+ rust: {
+ enabled: true,
+ },
+ },
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/CreationMode.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/CreationMode.aidl
new file mode 100644
index 0000000..1c65038
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/CreationMode.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+enum CreationMode {
+ /** Returns an error if the file does not already exist. */
+ NO_CREATE,
+
+ /** Creates the file or returns an error if it already exists. */
+ CREATE_EXCLUSIVE,
+
+ /** Creates the file if it does not already exist. */
+ CREATE,
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/DeleteOptions.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/DeleteOptions.aidl
new file mode 100644
index 0000000..1a94eb2
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/DeleteOptions.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.ReadIntegrity;
+
+parcelable DeleteOptions {
+ /**
+ * Set to acknowledge possible files tampering.
+ *
+ * If unacknowledged tampering is detected, the operation will fail with an ERR_FS_*
+ * service-specific code.
+ */
+ ReadIntegrity readIntegrity = ReadIntegrity.NO_TAMPER;
+
+ /**
+ * Allow writes to succeed while the filesystem is in the middle of an A/B update.
+ *
+ * If the A/B update fails, the operation will be rolled back. This rollback will not
+ * cause subsequent operations fail with any ERR_FS_* code nor will need to be
+ * acknowledged by setting the `readIntegrity`.
+ */
+ boolean allowWritesDuringAbUpdate = false;
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileAvailability.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileAvailability.aidl
new file mode 100644
index 0000000..d339170
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileAvailability.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+/** Determines how early during the boot process file is able to be accessed. */
+enum FileAvailability {
+ /** Available before userdata is mounted, but after android has booted. */
+ BEFORE_USERDATA,
+
+ /** Available after userdata is mounted. */
+ AFTER_USERDATA,
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileIntegrity.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileIntegrity.aidl
new file mode 100644
index 0000000..1879b16
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileIntegrity.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+enum FileIntegrity {
+ /** REE may prevent operations, but cannot alter data once written. */
+ TAMPER_PROOF_AT_REST,
+
+ /**
+ * REE may alter written data, but changes will be detected and reported as
+ * an error on read.
+ */
+ TAMPER_DETECT,
+
+ /**
+ * REE may alter written data. Changes other than full filesystem resets will be detected and
+ * reported.
+ */
+ TAMPER_DETECT_IGNORE_RESET,
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileMode.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileMode.aidl
new file mode 100644
index 0000000..18a2eae
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileMode.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+enum FileMode {
+ /** The file may only be read from. */
+ READ_ONLY,
+
+ /** The file may only be written to. */
+ WRITE_ONLY,
+
+ /** The file may be both read from and written to. */
+ READ_WRITE,
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileProperties.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileProperties.aidl
new file mode 100644
index 0000000..733b5b0
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/FileProperties.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.FileAvailability;
+import android.hardware.security.see.storage.FileIntegrity;
+
+parcelable FileProperties {
+ FileIntegrity integrity = FileIntegrity.TAMPER_PROOF_AT_REST;
+ FileAvailability availability = FileAvailability.BEFORE_USERDATA;
+
+ /** Whether the file is reset when user data is wiped. */
+ boolean persistent;
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/IDir.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IDir.aidl
new file mode 100644
index 0000000..a0a9f3d
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IDir.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+/** The interface for an open directory */
+interface IDir {
+ /**
+ * Gets the next batch of filenames in this directory.
+ *
+ * Calling multiple times will return different results as the IDir iterates through all the
+ * files it contains. When all filenames have been returned, all successive calls will return an
+ * empty list.
+ *
+ * @maxCount:
+ * the maximum number of filenames to return. A @maxCount of 0 signifies no limit on the
+ * number of filenames returned.
+ *
+ * Returns:
+ * An ordered list of filenames. If @maxCount > 0, the length of the returned list will be
+ * less than or equal to @maxCount.
+ *
+ * May return service-specific errors:
+ * - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+ * dir was opened with does not acknowledge
+ */
+ @utf8InCpp String[] readNextFilenames(int maxCount);
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/IFile.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IFile.aidl
new file mode 100644
index 0000000..ff26aa4
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IFile.aidl
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.CreationMode;
+
+/** The interface for an open file */
+interface IFile {
+ /**
+ * Read bytes from this file.
+ *
+ * @size:
+ * the size (in bytes) of the segment to read. If @size is larger than the service's maximum
+ * read size, the call will return an error (EX_ILLEGAL_ARGUMENT).
+ * @offset:
+ * the offset (in bytes) at which to start reading
+ *
+ * Return:
+ * the sequence of bytes at [offset, offset + size) in the file
+ *
+ * May return service-specific errors:
+ * - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+ * file was opened with does not acknowledge
+ */
+ byte[] read(long size, long offset);
+
+ /**
+ * Write the bytes in `buffer` to this file.
+ *
+ * @offset:
+ * the offset (in bytes) at which to start writing
+ *
+ * Return:
+ * the number of bytes written successfully
+ *
+ * May return service-specific errors:
+ * - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+ * file was opened with does not acknowledge
+ */
+ long write(long offset, in byte[] buffer);
+
+ /**
+ * Reads this file's size.
+ *
+ * May return service-specific errors:
+ * - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+ * file was opened with does not acknowledge
+ */
+ long getSize();
+
+ /**
+ * Sets this file's size.
+ *
+ * Truncates the file if `new_size` is less than the current size. If `new_size` is greater than
+ * the current size, the file will be extended with zeroed data.
+ *
+ * @newSize:
+ * the file's new size
+ *
+ * May return service-specific errors:
+ * - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+ * file was opened with does not acknowledge
+ */
+ void setSize(long newSize);
+
+ /**
+ * Renames this file.
+ *
+ * @destPath:
+ * the file's new path, relative to filesystem root
+ * @destCreateMode:
+ * controls creation behavior of the dest file
+ *
+ * May return service-specific errors:
+ * - ERR_NOT_FOUND if no file exists at @destPath and @destCreateMode is `NO_CREATE`
+ * - ERR_ALREADY_EXISTS if a file already exists at @destPath and @destCreateMode is
+ * `CREATE_EXCLUSIVE`
+ * - ERR_FS_* if the filesystem has been tampered with in a way that the `readIntegrity` the
+ * file was opened with does not acknowledge
+ */
+ void rename(in @utf8InCpp String destPath, in CreationMode destCreateMode);
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/ISecureStorage.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/ISecureStorage.aidl
new file mode 100644
index 0000000..be3c045
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/ISecureStorage.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.FileProperties;
+import android.hardware.security.see.storage.IStorageSession;
+
+/**
+ * Interface for the Secure Storage HAL
+ *
+ * Creates sessions which can be used to access storage.
+ */
+interface ISecureStorage {
+ const int ERR_UNSUPPORTED_PROPERTIES = 1;
+ const int ERR_NOT_FOUND = 2;
+ const int ERR_ALREADY_EXISTS = 3;
+ const int ERR_BAD_TRANSACTION = 4;
+
+ const int ERR_FS_RESET = 5;
+ const int ERR_FS_ROLLED_BACK = 6;
+ const int ERR_FS_TAMPERED = 7;
+
+ /**
+ * Starts a storage session for a filesystem.
+ *
+ * @properties:
+ * the minimum filesystem properties requested for the session.
+ *
+ * May return service-specific errors:
+ * - ERR_UNSUPPORTED_PROPERTIES if no filesystems exist which meet the minimum requested
+ * requirements
+ */
+ IStorageSession startSession(in FileProperties properties);
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/IStorageSession.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IStorageSession.aidl
new file mode 100644
index 0000000..cd126b8
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/IStorageSession.aidl
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.DeleteOptions;
+import android.hardware.security.see.storage.IDir;
+import android.hardware.security.see.storage.IFile;
+import android.hardware.security.see.storage.OpenOptions;
+import android.hardware.security.see.storage.ReadIntegrity;
+import android.hardware.security.see.storage.RenameOptions;
+
+/**
+ * Interface for a Secure Storage session
+ *
+ * When the connection is opened, it will start a transaction and any changes made through this
+ * session or the interfaces this session returns will be added to this transaction's pending
+ * changes. Calling `CommitChanges`/`AbandonChanges` will commit/abandon these pending changes, and
+ * start a new, empty transaction. The interfaces this session returns _remain_ valid across
+ * transactions; it is not necessary, for example, to reopen a file after a commit.
+ *
+ * Any changes still pending when the session is dropped will be abandoned.
+ */
+interface IStorageSession {
+ /**
+ * Commits any pending changes made through this session to storage.
+ *
+ * The session will no longer have pending changes after this call returns. Files may then still
+ * be modified through this session to create another commit.
+ *
+ * May return service-specific errors:
+ * - ERR_BAD_TRANSACTION
+ */
+ void commitChanges();
+
+ /**
+ * Abandons any pending changes made through this session.
+ *
+ * The session can then be reused to make new changes.
+ */
+ void abandonChanges();
+
+ /**
+ * Opens a secure file for writing and/or reading.
+ *
+ * Changes made to the file are part of the current transaction. Dropping this session
+ * invalidates the returned `IFile` interface
+ *
+ * @filePath:
+ * path to the file, relative to filesystem root
+ * @options:
+ * options controlling opening behavior
+ *
+ * May return service-specific errors:
+ * - ERR_NOT_FOUND
+ * - ERR_ALREADY_EXISTS
+ * - ERR_FS_* if the filesystem has been tampered with in a way that @options.readIntegrity
+ * does not acknowledge
+ */
+ IFile openFile(in @utf8InCpp String filePath, in OpenOptions options);
+
+ /**
+ * Delete a file.
+ *
+ * @filePath:
+ * path to the file, relative to filesystem root
+ * @options:
+ * options controlling deletion behavior
+ *
+ * May return service-specific errors:
+ * - ERR_NOT_FOUND
+ * - ERR_FS_* if the filesystem has been tampered with in a way that @options.readIntegrity
+ * does not acknowledge
+ */
+ void deleteFile(in @utf8InCpp String filePath, in DeleteOptions options);
+
+ /**
+ * Renames an existing file.
+ *
+ * The file must not already be opened. (If it is, use `IFile::rename`.)
+ *
+ * @currentPath:
+ * path to the file, relative to filesystem root
+ * @destPath:
+ * the file's new path, relative to filesystem root
+ * @options:
+ * options controlling rename behavior
+ *
+ * May return service-specific errors:
+ * - ERR_NOT_FOUND if no file exists at @currentPath, or if @options.destCreateMode is
+ * `NO_CREATE` and no file exists at @destPath
+ * - ERR_ALREADY_EXISTS if @options.destCreateMode is `CREATE_EXCLUSIVE` and a file exists at
+ * @destPath
+ * - ERR_FS_* if the filesystem has been tampered with in a way that @options.readIntegrity
+ * does not acknowledge
+ */
+ void renameFile(in @utf8InCpp String currentPath, in @utf8InCpp String destPath,
+ in RenameOptions options);
+
+ /**
+ * Opens a directory from a filesystem with the given properties.
+ *
+ * Dropping this session invalidates the returned `IDir` interface.
+ *
+ * @path:
+ * path to the directory, relative to filesystem root
+ * @readIntegrity:
+ * allow opening (and subsequent read/write operations) despite possible tampering for the
+ * directory
+ *
+ * May return service-specific errors:
+ * - ERR_NOT_FOUND
+ * - ERR_FS_* if the filesystem has been tampered with in a way that @readIntegrity does not
+ * acknowledge
+ */
+ IDir openDir(in @utf8InCpp String path, in ReadIntegrity readIntegrity);
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/OpenOptions.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/OpenOptions.aidl
new file mode 100644
index 0000000..997ca62
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/OpenOptions.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.CreationMode;
+import android.hardware.security.see.storage.FileMode;
+import android.hardware.security.see.storage.ReadIntegrity;
+
+parcelable OpenOptions {
+ /** Controls creation behavior of the to-be-opened file. See `CreationMode` docs for details. */
+ CreationMode createMode = CreationMode.NO_CREATE;
+
+ /** Controls access behavior of the to-be-opened file. See `FileMode` docs for details. */
+ FileMode accessMode = FileMode.READ_WRITE;
+
+ /**
+ * Set to acknowledge possible files tampering.
+ *
+ * If unacknowledged tampering is detected, the operation will fail with an ERR_FS_*
+ * service-specific code.
+ */
+ ReadIntegrity readIntegrity = ReadIntegrity.NO_TAMPER;
+
+ /**
+ * If this file already exists, discard existing content and open
+ * it as a new file. No semantic change if the file does not exist.
+ */
+ boolean truncateOnOpen;
+
+ /**
+ * Allow writes to succeed while the filesystem is in the middle of an A/B update.
+ *
+ * If the A/B update fails, the operation will be rolled back. This rollback will not
+ * cause subsequent operations fail with any ERR_FS_* code nor will need to be
+ * acknowledged by setting the `readIntegrity`.
+ */
+ boolean allowWritesDuringAbUpdate = false;
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/ReadIntegrity.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/ReadIntegrity.aidl
new file mode 100644
index 0000000..cc0e4f9
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/ReadIntegrity.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+enum ReadIntegrity {
+ /**
+ * Return an error on reads if any REE alteration of the written data
+ * has been detected.
+ */
+ NO_TAMPER,
+
+ /**
+ * Return an error on reads if any REE alteration other than a reset
+ * has been detected.
+ */
+ IGNORE_RESET,
+
+ /**
+ * Return an error if any REE alteration other than a rollback to a
+ * valid checkpoint has been detected. (What makes a checkpoint valid is
+ * implementation defined; an implementation might take a checkpoint on its
+ * first post-factory boot. A reset is a rollback to the initial state.)
+ */
+ IGNORE_ROLLBACK,
+
+ // There's no `IGNORE_ALL` because if REE has done any alteration other
+ // than a rollback, the file contents will be known-bad data.
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/RenameOptions.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/RenameOptions.aidl
new file mode 100644
index 0000000..f55ea7f
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/RenameOptions.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+import android.hardware.security.see.storage.CreationMode;
+import android.hardware.security.see.storage.ReadIntegrity;
+
+parcelable RenameOptions {
+ /** Controls creation behavior of the dest file. See `CreationMode` docs for details. */
+ CreationMode destCreateMode = CreationMode.CREATE_EXCLUSIVE;
+
+ /**
+ * Set to acknowledge possible files tampering.
+ *
+ * If unacknowledged tampering is detected, the operation will fail with an ERR_FS_*
+ * service-specific code.
+ */
+ ReadIntegrity readIntegrity = ReadIntegrity.NO_TAMPER;
+
+ /**
+ * Allow writes to succeed while the filesystem is in the middle of an A/B update.
+ *
+ * If the A/B update fails, the operation will be rolled back. This rollback will not
+ * cause subsequent operations fail with any ERR_FS_* code nor will need to be
+ * acknowledged by setting the `readIntegrity`.
+ */
+ boolean allowWritesDuringAbUpdate = false;
+}
diff --git a/staging/security/see/storage/aidl/android/hardware/security/see/storage/Tamper.aidl b/staging/security/see/storage/aidl/android/hardware/security/see/storage/Tamper.aidl
new file mode 100644
index 0000000..0a39fdd
--- /dev/null
+++ b/staging/security/see/storage/aidl/android/hardware/security/see/storage/Tamper.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 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.hardware.security.see.storage;
+
+/** Specifies types of REE tampering the filesystem may detect */
+enum Tamper {
+ /** REE has reset this file or the containing file system. */
+ RESET,
+
+ /** REE has rolled back this file or the containing file system to a previous state. */
+ ROLLBACK,
+
+ /** REE has made some other modification to the file. */
+ OTHER,
+}
diff --git a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
index fa7149f..e456e49 100644
--- a/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
+++ b/wifi/aidl/vts/functional/wifi_sta_iface_aidl_test.cpp
@@ -74,7 +74,17 @@
return testing::deviceSupportsFeature("com.google.android.tv.mdns_offload");
}
- // Detected panel TV device by using ro.oem.key1 property.
+ bool doesDeviceSupportFullNetworkingUnder2w() {
+ return testing::deviceSupportsFeature("com.google.android.tv.full_networking_under_2w");
+ }
+
+ // Detect TV devices.
+ bool isTvDevice() {
+ return testing::deviceSupportsFeature("android.software.leanback") ||
+ testing::deviceSupportsFeature("android.hardware.type.television");
+ }
+
+ // Detect Panel TV devices by using ro.oem.key1 property.
// https://docs.partner.android.com/tv/build/platform/props-vars/ro-oem-key1
bool isPanelTvDevice() {
const std::string oem_key1 = getPropertyString("ro.oem.key1");
@@ -135,10 +145,23 @@
*/
// @VsrTest = 5.3.12
TEST_P(WifiStaIfaceAidlTest, CheckApfIsSupported) {
- // Flat panel TV devices that support MDNS offload do not have to implement APF if the WiFi
- // chipset does not have sufficient RAM to do so.
- if (isPanelTvDevice() && isMdnsOffloadPresentInNIC()) {
- GTEST_SKIP() << "Panel TV supports mDNS offload. It is not required to support APF";
+ const std::string oem_key1 = getPropertyString("ro.oem.key1");
+ if (isTvDevice()) {
+ // Flat panel TV devices that support MDNS offload do not have to implement APF if the WiFi
+ // chipset does not have sufficient RAM to do so.
+ if (isPanelTvDevice() && isMdnsOffloadPresentInNIC()) {
+ GTEST_SKIP() << "Panel TV supports mDNS offload. It is not required to support APF";
+ }
+ // For TV devices declaring the
+ // com.google.android.tv.full_networking_under_2w feature, this indicates
+ // the device can meet the <= 2W standby power requirement while
+ // continuously processing network packets on the CPU, even in standby mode.
+ // In these cases, APF support is strongly recommended rather than being
+ // mandatory.
+ if (doesDeviceSupportFullNetworkingUnder2w()) {
+ GTEST_SKIP() << "TV Device meets the <= 2W standby power demand requirement. It is not "
+ "required to support APF.";
+ }
}
int vendor_api_level = property_get_int32("ro.vendor.api_level", 0);
// Before VSR 14, APF support is optional.