Merge "Remove the suffix LEGACY from CLUSTER_NAVIGATION_STATE_LEGACY." into sc-dev
diff --git a/audio/effect/all-versions/default/util/EffectUtils.cpp b/audio/effect/all-versions/default/util/EffectUtils.cpp
index b4382dc..1156d21 100644
--- a/audio/effect/all-versions/default/util/EffectUtils.cpp
+++ b/audio/effect/all-versions/default/util/EffectUtils.cpp
@@ -25,8 +25,6 @@
#include "util/EffectUtils.h"
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
-
using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
using ::android::hardware::audio::common::CPP_VERSION::implementation::UuidUtils;
using ::android::hardware::audio::common::utils::EnumBitfield;
@@ -154,6 +152,29 @@
return result;
}
+template <std::size_t N>
+inline hidl_string charBufferFromHal(const char (&halBuf)[N]) {
+ // Even if the original field contains a non-terminated string, hidl_string
+ // adds a NUL terminator.
+ return hidl_string(halBuf, strnlen(halBuf, N));
+}
+
+template <std::size_t N>
+inline status_t charBufferToHal(const hidl_string& str, char (&halBuf)[N], const char* fieldName) {
+ static_assert(N > 0);
+ const size_t halBufChars = N - 1; // Reserve one character for terminating NUL.
+ status_t result = NO_ERROR;
+ size_t strSize = str.size();
+ if (strSize > halBufChars) {
+ ALOGE("%s is too long: %zu (%zu max)", fieldName, strSize, halBufChars);
+ strSize = halBufChars;
+ result = BAD_VALUE;
+ }
+ strncpy(halBuf, str.c_str(), strSize);
+ halBuf[strSize] = '\0';
+ return result;
+}
+
status_t EffectUtils::effectDescriptorFromHal(const effect_descriptor_t& halDescriptor,
EffectDescriptor* descriptor) {
UuidUtils::uuidFromHal(halDescriptor.type, &descriptor->type);
@@ -166,9 +187,8 @@
memcpy(descriptor->implementor.data(), halDescriptor.implementor,
descriptor->implementor.size());
#else
- descriptor->name = hidl_string(halDescriptor.name, ARRAY_SIZE(halDescriptor.name));
- descriptor->implementor =
- hidl_string(halDescriptor.implementor, ARRAY_SIZE(halDescriptor.implementor));
+ descriptor->name = charBufferFromHal(halDescriptor.name);
+ descriptor->implementor = charBufferFromHal(halDescriptor.implementor);
#endif
return NO_ERROR;
}
@@ -186,25 +206,11 @@
memcpy(halDescriptor->implementor, descriptor.implementor.data(),
descriptor.implementor.size());
#else
- // According to 'dumpEffectDescriptor' 'name' and 'implementor' must be NUL-terminated.
- size_t nameSize = descriptor.name.size();
- if (nameSize >= ARRAY_SIZE(halDescriptor->name)) {
- ALOGE("effect name is too long: %zu (%zu max)", nameSize,
- ARRAY_SIZE(halDescriptor->name) - 1);
- nameSize = ARRAY_SIZE(halDescriptor->name) - 1;
- result = BAD_VALUE;
- }
- strncpy(halDescriptor->name, descriptor.name.c_str(), nameSize);
- halDescriptor->name[nameSize] = '\0';
- size_t implementorSize = descriptor.implementor.size();
- if (implementorSize >= ARRAY_SIZE(halDescriptor->implementor)) {
- ALOGE("effect implementor is too long: %zu (%zu max)", implementorSize,
- ARRAY_SIZE(halDescriptor->implementor) - 1);
- implementorSize = ARRAY_SIZE(halDescriptor->implementor) - 1;
- result = BAD_VALUE;
- }
- strncpy(halDescriptor->implementor, descriptor.implementor.c_str(), implementorSize);
- halDescriptor->implementor[implementorSize] = '\0';
+ // According to 'dumpEffectDescriptor', 'name' and 'implementor' must be NUL-terminated.
+ CONVERT_CHECKED(charBufferToHal(descriptor.name, halDescriptor->name, "effect name"), result);
+ CONVERT_CHECKED(charBufferToHal(descriptor.implementor, halDescriptor->implementor,
+ "effect implementor"),
+ result);
#endif
return result;
}
diff --git a/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp b/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
index f3651de..d021fa0 100644
--- a/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
+++ b/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
@@ -154,3 +154,20 @@
EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorFromHal(halDesc, &descBack));
EXPECT_EQ(desc, descBack);
}
+
+TEST(EffectUtils, ConvertNameAndImplementor) {
+ for (size_t i = 0; i < EFFECT_STRING_LEN_MAX; ++i) {
+ effect_descriptor_t halDesc{};
+ for (size_t c = 0; c < i; ++c) { // '<' to accommodate NUL terminator.
+ halDesc.name[c] = halDesc.implementor[c] = 'A' + static_cast<char>(c);
+ }
+ EffectDescriptor desc;
+ EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorFromHal(halDesc, &desc));
+ effect_descriptor_t halDescBack;
+ EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorToHal(desc, &halDescBack));
+ EXPECT_EQ(i, strlen(halDescBack.name));
+ EXPECT_EQ(i, strlen(halDescBack.implementor));
+ EXPECT_EQ(0, strcmp(halDesc.name, halDescBack.name));
+ EXPECT_EQ(0, strcmp(halDesc.implementor, halDescBack.implementor));
+ }
+}
diff --git a/automotive/audiocontrol/aidl/default/Android.bp b/automotive/audiocontrol/aidl/default/Android.bp
index 878bee1..7694bdf 100644
--- a/automotive/audiocontrol/aidl/default/Android.bp
+++ b/automotive/audiocontrol/aidl/default/Android.bp
@@ -27,10 +27,8 @@
init_rc: ["audiocontrol-default.rc"],
vintf_fragments: ["audiocontrol-default.xml"],
vendor: true,
- generated_headers: ["audio_policy_configuration_V7_0"],
- generated_sources: ["audio_policy_configuration_V7_0"],
- header_libs: ["libxsdc-utils"],
shared_libs: [
+ "android.hardware.audio.common@7.0-enums",
"android.frameworks.automotive.powerpolicy-V1-ndk_platform",
"android.hardware.automotive.audiocontrol-V1-ndk_platform",
"libbase",
@@ -38,7 +36,6 @@
"libcutils",
"liblog",
"libpowerpolicyclient",
- "libxml2",
],
srcs: [
"AudioControl.cpp",
diff --git a/automotive/audiocontrol/aidl/default/AudioControl.cpp b/automotive/audiocontrol/aidl/default/AudioControl.cpp
index b076d01..c0bc796 100644
--- a/automotive/audiocontrol/aidl/default/AudioControl.cpp
+++ b/automotive/audiocontrol/aidl/default/AudioControl.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#define LOG_TAG "AudioControl"
+// #define LOG_NDEBUG 0
+
#include "AudioControl.h"
#include <aidl/android/hardware/automotive/audiocontrol/AudioFocusChange.h>
@@ -24,7 +27,7 @@
#include <android-base/parseint.h>
#include <android-base/strings.h>
-#include <android_audio_policy_configuration_V7_0.h>
+#include <android_audio_policy_configuration_V7_0-enums.h>
#include <private/android_filesystem_config.h>
#include <stdio.h>
@@ -33,6 +36,7 @@
using ::android::base::EqualsIgnoreCase;
using ::android::base::ParseInt;
+using ::std::shared_ptr;
using ::std::string;
namespace xsd {
@@ -68,7 +72,7 @@
const shared_ptr<IFocusListener>& in_listener) {
LOG(DEBUG) << "registering focus listener";
- if (in_listener.get()) {
+ if (in_listener) {
std::atomic_store(&mFocusListener, in_listener);
} else {
LOG(ERROR) << "Unexpected nullptr for listener resulting in no-op.";
@@ -80,9 +84,10 @@
if (isValidValue(value)) {
// Just log in this default mock implementation
LOG(INFO) << "Balance set to " << value;
- } else {
- LOG(ERROR) << "Balance value out of range -1 to 1 at " << value;
+ return ndk::ScopedAStatus::ok();
}
+
+ LOG(ERROR) << "Balance value out of range -1 to 1 at " << value;
return ndk::ScopedAStatus::ok();
}
@@ -90,9 +95,10 @@
if (isValidValue(value)) {
// Just log in this default mock implementation
LOG(INFO) << "Fader set to " << value;
- } else {
- LOG(ERROR) << "Fader value out of range -1 to 1 at " << value;
+ return ndk::ScopedAStatus::ok();
}
+
+ LOG(ERROR) << "Fader value out of range -1 to 1 at " << value;
return ndk::ScopedAStatus::ok();
}
@@ -194,7 +200,7 @@
}
string usage = string(args[1]);
- if (xsd::stringToAudioUsage(usage) == xsd::AudioUsage::UNKNOWN) {
+ if (xsd::isUnknownAudioUsage(usage)) {
dprintf(fd,
"Unknown usage provided: %s. Please see audio_policy_configuration.xsd V7_0 "
"for supported values\n",
@@ -236,7 +242,7 @@
}
string usage = string(args[1]);
- if (xsd::stringToAudioUsage(usage) == xsd::AudioUsage::UNKNOWN) {
+ if (xsd::isUnknownAudioUsage(usage)) {
dprintf(fd,
"Unknown usage provided: %s. Please see audio_policy_configuration.xsd V7_0 "
"for supported values\n",
diff --git a/automotive/audiocontrol/aidl/default/AudioControl.h b/automotive/audiocontrol/aidl/default/AudioControl.h
index ab0b1b3..cca9c44 100644
--- a/automotive/audiocontrol/aidl/default/AudioControl.h
+++ b/automotive/audiocontrol/aidl/default/AudioControl.h
@@ -23,8 +23,6 @@
namespace aidl::android::hardware::automotive::audiocontrol {
-using ::std::shared_ptr;
-
class AudioControl : public BnAudioControl {
public:
ndk::ScopedAStatus onAudioFocusChange(const std::string& in_usage, int32_t in_zoneId,
@@ -34,7 +32,7 @@
ndk::ScopedAStatus onDevicesToMuteChange(
const std::vector<MutingInfo>& in_mutingInfos) override;
ndk::ScopedAStatus registerFocusListener(
- const shared_ptr<IFocusListener>& in_listener) override;
+ const std::shared_ptr<IFocusListener>& in_listener) override;
ndk::ScopedAStatus setBalanceTowardRight(float in_value) override;
ndk::ScopedAStatus setFadeTowardFront(float in_value) override;
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
@@ -44,7 +42,7 @@
// a single instance of CarAudioService. As such, it doesn't have explicit serialization.
// If a different AudioControl implementation were to have multiple threads leveraging this
// listener, then it should also include mutexes or make the listener atomic.
- shared_ptr<IFocusListener> mFocusListener;
+ std::shared_ptr<IFocusListener> mFocusListener;
binder_status_t cmdHelp(int fd) const;
binder_status_t cmdRequestFocus(int fd, const char** args, uint32_t numArgs);
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index b2fd402..ed3b1fa 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -125,6 +125,7 @@
using ::android::hardware::camera::metadata::V3_4::
CameraMetadataEnumAndroidSensorInfoColorFilterArrangement;
using ::android::hardware::camera::metadata::V3_4::CameraMetadataTag;
+using ::android::hardware::camera::metadata::V3_6::CameraMetadataEnumAndroidSensorPixelMode;
using ::android::hardware::camera::provider::V2_4::ICameraProvider;
using ::android::hardware::camera::provider::V2_4::ICameraProviderCallback;
using ::android::hardware::camera::provider::V2_6::CameraIdAndStreamCombination;
@@ -767,6 +768,8 @@
sp<device::V3_7::ICameraDeviceSession> *session3_7 /*out*/);
void castDevice(const sp<device::V3_2::ICameraDevice> &device, int32_t deviceVersion,
sp<device::V3_5::ICameraDevice> *device3_5/*out*/);
+ void castDevice3_7(const sp<device::V3_2::ICameraDevice>& device, int32_t deviceVersion,
+ sp<device::V3_7::ICameraDevice>* device3_7 /*out*/);
void createStreamConfiguration(const ::android::hardware::hidl_vec<V3_2::Stream>& streams3_2,
StreamConfigurationMode configMode,
::android::hardware::camera::device::V3_2::StreamConfiguration *config3_2,
@@ -785,6 +788,16 @@
sp<DeviceCb> *outCb /*out*/,
uint32_t *jpegBufferSize /*out*/,
bool *useHalBufManager /*out*/);
+ void configureStreams3_7(const std::string& name, int32_t deviceVersion,
+ sp<ICameraProvider> provider, PixelFormat format,
+ sp<device::V3_7::ICameraDeviceSession>* session3_7 /*out*/,
+ V3_2::Stream* previewStream /*out*/,
+ device::V3_6::HalStreamConfiguration* halStreamConfig /*out*/,
+ bool* supportsPartialResults /*out*/,
+ uint32_t* partialResultCount /*out*/, bool* useHalBufManager /*out*/,
+ sp<DeviceCb>* outCb /*out*/, uint32_t streamConfigCounter,
+ bool maxResolution);
+
void configurePreviewStreams3_4(const std::string &name, int32_t deviceVersion,
sp<ICameraProvider> provider,
const AvailableStream *previewThreshold,
@@ -846,6 +859,10 @@
hidl_vec<int32_t> streamIds, sp<DeviceCb> cb,
uint32_t streamConfigCounter = 0);
+ void verifyBuffersReturned(sp<device::V3_7::ICameraDeviceSession> session,
+ hidl_vec<int32_t> streamIds, sp<DeviceCb> cb,
+ uint32_t streamConfigCounter = 0);
+
void verifySessionReconfigurationQuery(sp<device::V3_5::ICameraDeviceSession> session3_5,
camera_metadata* oldSessionParams, camera_metadata* newSessionParams);
@@ -853,12 +870,15 @@
static bool isDepthOnly(const camera_metadata_t* staticMeta);
- static Status getAvailableOutputStreams(const camera_metadata_t *staticMeta,
- std::vector<AvailableStream> &outputStreams,
- const AvailableStream *threshold = nullptr);
+ static bool isUltraHighResolution(const camera_metadata_t* staticMeta);
+
+ static Status getAvailableOutputStreams(const camera_metadata_t* staticMeta,
+ std::vector<AvailableStream>& outputStreams,
+ const AvailableStream* threshold = nullptr,
+ bool maxResolution = false);
static Status getMaxOutputSizeForFormat(const camera_metadata_t* staticMeta, PixelFormat format,
- Size* size);
+ Size* size, bool maxResolution = false);
static Status getMandatoryConcurrentStreams(const camera_metadata_t* staticMeta,
std::vector<AvailableStream>* outputStreams);
@@ -4841,6 +4861,184 @@
}
}
+// Generate and verify an ultra high resolution capture request
+TEST_P(CameraHidlTest, processUltraHighResolutionRequest) {
+ hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
+ uint64_t bufferId = 1;
+ uint32_t frameNumber = 1;
+ ::android::hardware::hidl_vec<uint8_t> settings;
+
+ for (const auto& name : cameraDeviceNames) {
+ int deviceVersion = getCameraDeviceVersion(name, mProviderType);
+ if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_7) {
+ continue;
+ }
+ std::string version, deviceId;
+ ASSERT_TRUE(::matchDeviceName(name, mProviderType, &version, &deviceId));
+ camera_metadata_t* staticMeta;
+ Return<void> ret;
+ sp<ICameraDeviceSession> session;
+ openEmptyDeviceSession(name, mProvider, &session, &staticMeta);
+ if (!isUltraHighResolution(staticMeta)) {
+ free_camera_metadata(staticMeta);
+ ret = session->close();
+ ASSERT_TRUE(ret.isOk());
+ continue;
+ }
+ android::hardware::camera::common::V1_0::helper::CameraMetadata defaultSettings;
+ ret = session->constructDefaultRequestSettings(
+ RequestTemplate::STILL_CAPTURE,
+ [&defaultSettings](auto status, const auto& req) mutable {
+ ASSERT_EQ(Status::OK, status);
+
+ const camera_metadata_t* metadata =
+ reinterpret_cast<const camera_metadata_t*>(req.data());
+ size_t expectedSize = req.size();
+ int result = validate_camera_metadata_structure(metadata, &expectedSize);
+ ASSERT_TRUE((result == 0) || (result == CAMERA_METADATA_VALIDATION_SHIFTED));
+
+ size_t entryCount = get_camera_metadata_entry_count(metadata);
+ ASSERT_GT(entryCount, 0u);
+ defaultSettings = metadata;
+ });
+ ASSERT_TRUE(ret.isOk());
+ uint8_t sensorPixelMode =
+ static_cast<uint8_t>(ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION);
+ ASSERT_EQ(::android::OK,
+ defaultSettings.update(ANDROID_SENSOR_PIXEL_MODE, &sensorPixelMode, 1));
+
+ const camera_metadata_t* settingsBuffer = defaultSettings.getAndLock();
+ settings.setToExternal(
+ reinterpret_cast<uint8_t*>(const_cast<camera_metadata_t*>(settingsBuffer)),
+ get_camera_metadata_size(settingsBuffer));
+
+ free_camera_metadata(staticMeta);
+ ret = session->close();
+ ASSERT_TRUE(ret.isOk());
+ V3_6::HalStreamConfiguration halStreamConfig;
+ bool supportsPartialResults = false;
+ bool useHalBufManager = false;
+ uint32_t partialResultCount = 0;
+ V3_2::Stream previewStream;
+ sp<device::V3_7::ICameraDeviceSession> session3_7;
+ sp<DeviceCb> cb;
+ std::list<PixelFormat> pixelFormats = {PixelFormat::YCBCR_420_888, PixelFormat::RAW16};
+ for (PixelFormat format : pixelFormats) {
+ configureStreams3_7(name, deviceVersion, mProvider, format, &session3_7, &previewStream,
+ &halStreamConfig, &supportsPartialResults, &partialResultCount,
+ &useHalBufManager, &cb, 0, /*maxResolution*/ true);
+ ASSERT_NE(session3_7, nullptr);
+
+ std::shared_ptr<ResultMetadataQueue> resultQueue;
+ auto resultQueueRet = session3_7->getCaptureResultMetadataQueue(
+ [&resultQueue](const auto& descriptor) {
+ resultQueue = std::make_shared<ResultMetadataQueue>(descriptor);
+ if (!resultQueue->isValid() || resultQueue->availableToWrite() <= 0) {
+ ALOGE("%s: HAL returns empty result metadata fmq,"
+ " not use it",
+ __func__);
+ resultQueue = nullptr;
+ // Don't use the queue onwards.
+ }
+ });
+ ASSERT_TRUE(resultQueueRet.isOk());
+
+ std::vector<hidl_handle> graphicBuffers;
+ graphicBuffers.reserve(halStreamConfig.streams.size());
+ ::android::hardware::hidl_vec<StreamBuffer> outputBuffers;
+ outputBuffers.resize(halStreamConfig.streams.size());
+ InFlightRequest inflightReq = {static_cast<ssize_t>(halStreamConfig.streams.size()),
+ false,
+ supportsPartialResults,
+ partialResultCount,
+ std::unordered_set<std::string>(),
+ resultQueue};
+
+ size_t k = 0;
+ for (const auto& halStream : halStreamConfig.streams) {
+ hidl_handle buffer_handle;
+ if (useHalBufManager) {
+ outputBuffers[k] = {halStream.v3_4.v3_3.v3_2.id,
+ 0,
+ buffer_handle,
+ BufferStatus::OK,
+ nullptr,
+ nullptr};
+ } else {
+ allocateGraphicBuffer(
+ previewStream.width, previewStream.height,
+ android_convertGralloc1To0Usage(halStream.v3_4.v3_3.v3_2.producerUsage,
+ halStream.v3_4.v3_3.v3_2.consumerUsage),
+ halStream.v3_4.v3_3.v3_2.overrideFormat, &buffer_handle);
+ graphicBuffers.push_back(buffer_handle);
+ outputBuffers[k] = {halStream.v3_4.v3_3.v3_2.id,
+ bufferId,
+ buffer_handle,
+ BufferStatus::OK,
+ nullptr,
+ nullptr};
+ bufferId++;
+ }
+ k++;
+ }
+
+ StreamBuffer emptyInputBuffer = {-1, 0, nullptr, BufferStatus::ERROR, nullptr, nullptr};
+ V3_4::CaptureRequest request3_4;
+ request3_4.v3_2.frameNumber = frameNumber;
+ request3_4.v3_2.fmqSettingsSize = 0;
+ request3_4.v3_2.settings = settings;
+ request3_4.v3_2.inputBuffer = emptyInputBuffer;
+ request3_4.v3_2.outputBuffers = outputBuffers;
+ V3_7::CaptureRequest request3_7;
+ request3_7.v3_4 = request3_4;
+ request3_7.inputWidth = 0;
+ request3_7.inputHeight = 0;
+
+ {
+ std::unique_lock<std::mutex> l(mLock);
+ mInflightMap.clear();
+ mInflightMap.add(frameNumber, &inflightReq);
+ }
+
+ Status stat = Status::INTERNAL_ERROR;
+ uint32_t numRequestProcessed = 0;
+ hidl_vec<BufferCache> cachesToRemove;
+ Return<void> returnStatus = session3_7->processCaptureRequest_3_7(
+ {request3_7}, cachesToRemove,
+ [&stat, &numRequestProcessed](auto s, uint32_t n) {
+ stat = s;
+ numRequestProcessed = n;
+ });
+ ASSERT_TRUE(returnStatus.isOk());
+ ASSERT_EQ(Status::OK, stat);
+ ASSERT_EQ(numRequestProcessed, 1u);
+
+ {
+ std::unique_lock<std::mutex> l(mLock);
+ while (!inflightReq.errorCodeValid &&
+ ((0 < inflightReq.numBuffersLeft) || (!inflightReq.haveResultMetadata))) {
+ auto timeout = std::chrono::system_clock::now() +
+ std::chrono::seconds(kStreamBufferTimeoutSec);
+ ASSERT_NE(std::cv_status::timeout, mResultCondition.wait_until(l, timeout));
+ }
+
+ ASSERT_FALSE(inflightReq.errorCodeValid);
+ ASSERT_NE(inflightReq.resultOutputBuffers.size(), 0u);
+ }
+ if (useHalBufManager) {
+ hidl_vec<int32_t> streamIds(halStreamConfig.streams.size());
+ for (size_t i = 0; i < streamIds.size(); i++) {
+ streamIds[i] = halStreamConfig.streams[i].v3_4.v3_3.v3_2.id;
+ }
+ verifyBuffersReturned(session3_7, streamIds, cb);
+ }
+
+ ret = session3_7->close();
+ ASSERT_TRUE(ret.isOk());
+ }
+ }
+}
+
// Generate and verify a burst containing alternating sensor sensitivity values
TEST_P(CameraHidlTest, processCaptureRequestBurstISO) {
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
@@ -5537,21 +5735,26 @@
// Retrieve all valid output stream resolutions from the camera
// static characteristics.
-Status CameraHidlTest::getAvailableOutputStreams(const camera_metadata_t *staticMeta,
- std::vector<AvailableStream> &outputStreams,
- const AvailableStream *threshold) {
+Status CameraHidlTest::getAvailableOutputStreams(const camera_metadata_t* staticMeta,
+ std::vector<AvailableStream>& outputStreams,
+ const AvailableStream* threshold,
+ bool maxResolution) {
AvailableStream depthPreviewThreshold = {kMaxPreviewWidth, kMaxPreviewHeight,
static_cast<int32_t>(PixelFormat::Y16)};
if (nullptr == staticMeta) {
return Status::ILLEGAL_ARGUMENT;
}
+ int scalerTag = maxResolution
+ ? ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+ : ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
+ int depthTag = maxResolution
+ ? ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION
+ : ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS;
camera_metadata_ro_entry scalarEntry;
camera_metadata_ro_entry depthEntry;
- int foundScalar = find_camera_metadata_ro_entry(staticMeta,
- ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &scalarEntry);
- int foundDepth = find_camera_metadata_ro_entry(staticMeta,
- ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, &depthEntry);
+ int foundScalar = find_camera_metadata_ro_entry(staticMeta, scalerTag, &scalarEntry);
+ int foundDepth = find_camera_metadata_ro_entry(staticMeta, depthTag, &depthEntry);
if ((0 != foundScalar || (0 != (scalarEntry.count % 4))) &&
(0 != foundDepth || (0 != (depthEntry.count % 4)))) {
return Status::ILLEGAL_ARGUMENT;
@@ -5619,9 +5822,12 @@
}
Status CameraHidlTest::getMaxOutputSizeForFormat(const camera_metadata_t* staticMeta,
- PixelFormat format, Size* size) {
+ PixelFormat format, Size* size,
+ bool maxResolution) {
std::vector<AvailableStream> outputStreams;
- if (size == nullptr || getAvailableOutputStreams(staticMeta, outputStreams) != Status::OK) {
+ if (size == nullptr ||
+ getAvailableOutputStreams(staticMeta, outputStreams,
+ /*threshold*/ nullptr, maxResolution) != Status::OK) {
return Status::ILLEGAL_ARGUMENT;
}
Size maxSize;
@@ -6065,6 +6271,148 @@
*config3_2 = {streams3_2, configMode};
}
+// Configure streams
+void CameraHidlTest::configureStreams3_7(
+ const std::string& name, int32_t deviceVersion, sp<ICameraProvider> provider,
+ PixelFormat format, sp<device::V3_7::ICameraDeviceSession>* session3_7 /*out*/,
+ V3_2::Stream* previewStream /*out*/,
+ device::V3_6::HalStreamConfiguration* halStreamConfig /*out*/,
+ bool* supportsPartialResults /*out*/, uint32_t* partialResultCount /*out*/,
+ bool* useHalBufManager /*out*/, sp<DeviceCb>* outCb /*out*/, uint32_t streamConfigCounter,
+ bool maxResolution) {
+ ASSERT_NE(nullptr, session3_7);
+ ASSERT_NE(nullptr, halStreamConfig);
+ ASSERT_NE(nullptr, previewStream);
+ ASSERT_NE(nullptr, supportsPartialResults);
+ ASSERT_NE(nullptr, partialResultCount);
+ ASSERT_NE(nullptr, useHalBufManager);
+ ASSERT_NE(nullptr, outCb);
+
+ std::vector<AvailableStream> outputStreams;
+ ::android::sp<ICameraDevice> device3_x;
+ ALOGI("configureStreams: Testing camera device %s", name.c_str());
+ Return<void> ret;
+ ret = provider->getCameraDeviceInterface_V3_x(name, [&](auto status, const auto& device) {
+ ALOGI("getCameraDeviceInterface_V3_x returns status:%d", (int)status);
+ ASSERT_EQ(Status::OK, status);
+ ASSERT_NE(device, nullptr);
+ device3_x = device;
+ });
+ ASSERT_TRUE(ret.isOk());
+
+ camera_metadata_t* staticMeta;
+ ret = device3_x->getCameraCharacteristics([&](Status s, CameraMetadata metadata) {
+ ASSERT_EQ(Status::OK, s);
+ staticMeta =
+ clone_camera_metadata(reinterpret_cast<const camera_metadata_t*>(metadata.data()));
+ ASSERT_NE(nullptr, staticMeta);
+ });
+ ASSERT_TRUE(ret.isOk());
+
+ camera_metadata_ro_entry entry;
+ auto status =
+ find_camera_metadata_ro_entry(staticMeta, ANDROID_REQUEST_PARTIAL_RESULT_COUNT, &entry);
+ if ((0 == status) && (entry.count > 0)) {
+ *partialResultCount = entry.data.i32[0];
+ *supportsPartialResults = (*partialResultCount > 1);
+ }
+
+ sp<DeviceCb> cb = new DeviceCb(this, deviceVersion, staticMeta);
+ sp<ICameraDeviceSession> session;
+ ret = device3_x->open(cb, [&session](auto status, const auto& newSession) {
+ ALOGI("device::open returns status:%d", (int)status);
+ ASSERT_EQ(Status::OK, status);
+ ASSERT_NE(newSession, nullptr);
+ session = newSession;
+ });
+ ASSERT_TRUE(ret.isOk());
+ *outCb = cb;
+
+ sp<device::V3_3::ICameraDeviceSession> session3_3;
+ sp<device::V3_4::ICameraDeviceSession> session3_4;
+ sp<device::V3_5::ICameraDeviceSession> session3_5;
+ sp<device::V3_6::ICameraDeviceSession> session3_6;
+ castSession(session, deviceVersion, &session3_3, &session3_4, &session3_5, &session3_6,
+ session3_7);
+ ASSERT_NE(nullptr, (*session3_7).get());
+
+ *useHalBufManager = false;
+ status = find_camera_metadata_ro_entry(
+ staticMeta, ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, &entry);
+ if ((0 == status) && (entry.count == 1)) {
+ *useHalBufManager = (entry.data.u8[0] ==
+ ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5);
+ }
+
+ outputStreams.clear();
+ Size maxSize;
+ auto rc = getMaxOutputSizeForFormat(staticMeta, format, &maxSize, maxResolution);
+ ASSERT_EQ(Status::OK, rc);
+ free_camera_metadata(staticMeta);
+
+ ::android::hardware::hidl_vec<V3_7::Stream> streams3_7(1);
+ streams3_7[0].groupId = -1;
+ streams3_7[0].sensorPixelModesUsed = {
+ CameraMetadataEnumAndroidSensorPixelMode::ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION};
+ streams3_7[0].v3_4.bufferSize = 0;
+ streams3_7[0].v3_4.v3_2.id = 0;
+ streams3_7[0].v3_4.v3_2.streamType = StreamType::OUTPUT;
+ streams3_7[0].v3_4.v3_2.width = static_cast<uint32_t>(maxSize.width);
+ streams3_7[0].v3_4.v3_2.height = static_cast<uint32_t>(maxSize.height);
+ streams3_7[0].v3_4.v3_2.format = static_cast<PixelFormat>(format);
+ streams3_7[0].v3_4.v3_2.usage = GRALLOC1_CONSUMER_USAGE_CPU_READ;
+ streams3_7[0].v3_4.v3_2.dataSpace = 0;
+ streams3_7[0].v3_4.v3_2.rotation = StreamRotation::ROTATION_0;
+
+ ::android::hardware::camera::device::V3_7::StreamConfiguration config3_7;
+ config3_7.streams = streams3_7;
+ config3_7.operationMode = StreamConfigurationMode::NORMAL_MODE;
+ config3_7.streamConfigCounter = streamConfigCounter;
+ config3_7.multiResolutionInputImage = false;
+ RequestTemplate reqTemplate = RequestTemplate::STILL_CAPTURE;
+ ret = (*session3_7)
+ ->constructDefaultRequestSettings(reqTemplate,
+ [&config3_7](auto status, const auto& req) {
+ ASSERT_EQ(Status::OK, status);
+ config3_7.sessionParams = req;
+ });
+ ASSERT_TRUE(ret.isOk());
+
+ ASSERT_TRUE(deviceVersion >= CAMERA_DEVICE_API_VERSION_3_7);
+ sp<device::V3_7::ICameraDevice> cameraDevice3_7 = nullptr;
+ castDevice3_7(device3_x, deviceVersion, &cameraDevice3_7);
+ ASSERT_NE(cameraDevice3_7, nullptr);
+ bool supported = false;
+ ret = cameraDevice3_7->isStreamCombinationSupported_3_7(
+ config3_7, [&supported](Status s, bool combStatus) {
+ ASSERT_TRUE((Status::OK == s) || (Status::METHOD_NOT_SUPPORTED == s));
+ if (Status::OK == s) {
+ supported = combStatus;
+ }
+ });
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_EQ(supported, true);
+
+ if (*session3_7 != nullptr) {
+ ret = (*session3_7)
+ ->configureStreams_3_7(
+ config3_7,
+ [&](Status s, device::V3_6::HalStreamConfiguration halConfig) {
+ ASSERT_EQ(Status::OK, s);
+ *halStreamConfig = halConfig;
+ if (*useHalBufManager) {
+ hidl_vec<V3_4::Stream> streams(1);
+ hidl_vec<V3_2::HalStream> halStreams(1);
+ streams[0] = streams3_7[0].v3_4;
+ halStreams[0] = halConfig.streams[0].v3_4.v3_3.v3_2;
+ cb->setCurrentStreamConfig(streams, halStreams);
+ }
+ });
+ }
+ *previewStream = streams3_7[0].v3_4.v3_2;
+ ASSERT_TRUE(ret.isOk());
+}
+
// Configure multiple preview streams using different physical ids.
void CameraHidlTest::configurePreviewStreams3_4(const std::string &name, int32_t deviceVersion,
sp<ICameraProvider> provider,
@@ -6362,6 +6710,21 @@
ASSERT_TRUE(ret.isOk());
}
+bool CameraHidlTest::isUltraHighResolution(const camera_metadata_t* staticMeta) {
+ camera_metadata_ro_entry scalarEntry;
+ int rc = find_camera_metadata_ro_entry(staticMeta, ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
+ &scalarEntry);
+ if (rc == 0) {
+ for (uint32_t i = 0; i < scalarEntry.count; i++) {
+ if (scalarEntry.data.u8[i] ==
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool CameraHidlTest::isDepthOnly(const camera_metadata_t* staticMeta) {
camera_metadata_ro_entry scalarEntry;
camera_metadata_ro_entry depthEntry;
@@ -6590,6 +6953,17 @@
ASSERT_TRUE(ret.isOk());
}
+void CameraHidlTest::castDevice3_7(const sp<device::V3_2::ICameraDevice>& device,
+ int32_t deviceVersion,
+ sp<device::V3_7::ICameraDevice>* device3_7 /*out*/) {
+ ASSERT_NE(nullptr, device3_7);
+ if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_7) {
+ auto castResult = device::V3_7::ICameraDevice::castFrom(device);
+ ASSERT_TRUE(castResult.isOk());
+ *device3_7 = castResult;
+ }
+}
+
void CameraHidlTest::castDevice(const sp<device::V3_2::ICameraDevice> &device,
int32_t deviceVersion, sp<device::V3_5::ICameraDevice> *device3_5/*out*/) {
ASSERT_NE(nullptr, device3_5);
@@ -7303,6 +7677,13 @@
cb->waitForBuffersReturned();
}
+void CameraHidlTest::verifyBuffersReturned(sp<device::V3_7::ICameraDeviceSession> session3_7,
+ hidl_vec<int32_t> streamIds, sp<DeviceCb> cb,
+ uint32_t streamConfigCounter) {
+ session3_7->signalStreamFlush(streamIds, /*streamConfigCounter*/ streamConfigCounter);
+ cb->waitForBuffersReturned();
+}
+
void CameraHidlTest::verifyLogicalCameraResult(const camera_metadata_t* staticMetadata,
const ::android::hardware::camera::common::V1_0::helper::CameraMetadata& resultMetadata) {
std::unordered_set<std::string> physicalIds;
diff --git a/identity/TEST_MAPPING b/identity/TEST_MAPPING
new file mode 100644
index 0000000..f35f4b7
--- /dev/null
+++ b/identity/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsIdentityTestCases"
+ },
+ {
+ "name": "VtsHalIdentityTargetTest"
+ },
+ {
+ "name": "android.hardware.identity-support-lib-test"
+ }
+ ]
+}
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
index ca3a52c..1bdde1e 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
@@ -520,6 +520,8 @@
}
return packet;
}
+
+ std::this_thread::yield();
}
// If we get to this point, we either stopped polling because it was taking too long or polling
@@ -665,6 +667,8 @@
}
return packet;
}
+
+ std::this_thread::yield();
}
// If we get to this point, we either stopped polling because it was taking too long or polling
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
index c21b2fc..6cf4567 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -158,6 +158,9 @@
{::android::hardware::radio::V1_6::RadioError::NONE,
::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
::android::hardware::radio::V1_6::RadioError::OP_NOT_ALLOWED_BEFORE_REG_TO_NW}));
+ if (radioRsp_v1_6->setupDataCallResult.trafficDescriptors.size() <= 0) {
+ return;
+ }
EXPECT_EQ(optionalTrafficDescriptor.value().osAppId.value().osAppId,
radioRsp_v1_6->setupDataCallResult.trafficDescriptors[0].osAppId.value().osAppId);
}
diff --git a/security/keymint/aidl/OWNERS b/security/keymint/aidl/OWNERS
index 5c79db8..a93b171 100644
--- a/security/keymint/aidl/OWNERS
+++ b/security/keymint/aidl/OWNERS
@@ -1,3 +1,4 @@
+jbires@google.com
jdanis@google.com
seleneh@google.com
swillden@google.com
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
index 1cb50ba..1ae6762 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -202,7 +202,7 @@
* 2 : bstr // KID : EEK ID
* 3 : -25, // Algorithm : ECDH-ES + HKDF-256
* -1 : 4, // Curve : X25519
- * -2 : bstr // Ed25519 public key
+ * -2 : bstr // X25519 public key
* }
*
* EekSignatureInput = [
@@ -221,7 +221,7 @@
* in the chain, which implies that it must not attempt to validate the signature.
*
* If testMode is false, the method must validate the chain signatures, and must verify
- * that the public key in the root certifictate is in its pre-configured set of
+ * that the public key in the root certificate is in its pre-configured set of
* authorized EEK root keys. If the public key is not in the database, or if signature
* verification fails, the method must return STATUS_INVALID_EEK.
*
diff --git a/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl b/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
index a26094c..62a48e9 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
@@ -26,7 +26,7 @@
/**
* key is a COSE_Mac0 structure containing the new public key. It's MACed by a key available
* only to the secure environment, as proof that the public key was generated by that
- * environment. In CDDL, assuming the contained key is an Ed25519 public key:
+ * environment. In CDDL, assuming the contained key is a P-256 public key:
*
* MacedPublicKey = [ // COSE_Mac0
* protected: bstr .cbor { 1 : 5}, // Algorithm : HMAC-256
@@ -36,10 +36,11 @@
* ]
*
* PublicKey = { // COSE_Key
- * 1 : 1, // Key type : octet key pair
- * 3 : -8 // Algorithm : EdDSA
- * -1 : 6, // Curve : Ed25519
+ * 1 : 2, // Key type : EC2
+ * 3 : -8 // Algorithm : ES256
+ * -1 : 6, // Curve : P256
* -2 : bstr // X coordinate, little-endian
+ * -3 : bstr // Y coordinate, little-endian
* ? -70000 : nil // Presence indicates this is a test key. If set, K_mac is
* // all zeros.
* },
@@ -51,7 +52,7 @@
* payload : bstr .cbor PublicKey
* ]
*
- * if a non-Ed25519 public key were contained, the contents of the PublicKey map would change a
+ * if a non-P256 public key were contained, the contents of the PublicKey map would change a
* little; see RFC 8152 for details.
*/
byte[] macedKey;
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
index 44f316f..5199062 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
@@ -33,7 +33,7 @@
* unprotected: {
* 5 : bstr .size 12 // IV
* },
- * ciphertext: bstr, // AES-GCM-128(K, .cbor ProtectedDataPayload)
+ * ciphertext: bstr, // AES-GCM-256(K, .cbor ProtectedDataPayload)
* recipients : [
* [ // COSE_Recipient
* protected : bstr .cbor {
diff --git a/security/keymint/aidl/default/RemotelyProvisionedComponent.cpp b/security/keymint/aidl/default/RemotelyProvisionedComponent.cpp
index 4dbaa05..ca06abc 100644
--- a/security/keymint/aidl/default/RemotelyProvisionedComponent.cpp
+++ b/security/keymint/aidl/default/RemotelyProvisionedComponent.cpp
@@ -46,6 +46,14 @@
namespace {
+// Hard-coded set of acceptable public keys that can act as roots of EEK chains.
+inline const vector<bytevec> kAuthorizedEekRoots = {
+ // TODO(drysdale): replace this random value with real root pubkey(s).
+ {0x5c, 0xea, 0x4b, 0xd2, 0x31, 0x27, 0x15, 0x5e, 0x62, 0x94, 0x70,
+ 0x53, 0x94, 0x43, 0x0f, 0x9a, 0x89, 0xd5, 0xc5, 0x0f, 0x82, 0x9b,
+ 0xcd, 0x10, 0xe0, 0x79, 0xef, 0xf3, 0xfa, 0x40, 0xeb, 0x0a},
+};
+
constexpr auto STATUS_FAILED = RemotelyProvisionedComponent::STATUS_FAILED;
constexpr auto STATUS_INVALID_EEK = RemotelyProvisionedComponent::STATUS_INVALID_EEK;
constexpr auto STATUS_INVALID_MAC = RemotelyProvisionedComponent::STATUS_INVALID_MAC;
@@ -135,6 +143,13 @@
"Failed to validate EEK chain: " + cosePubKey.moveMessage());
}
lastPubKey = *std::move(cosePubKey);
+
+ // In prod mode the first pubkey should match a well-known Google public key.
+ if (!testMode && i == 0 &&
+ std::find(kAuthorizedEekRoots.begin(), kAuthorizedEekRoots.end(), lastPubKey) ==
+ kAuthorizedEekRoots.end()) {
+ return Status(STATUS_INVALID_EEK, "Unrecognized root of EEK chain");
+ }
}
auto eek = CoseKey::parseX25519(lastPubKey, true /* requireKid */);
@@ -417,8 +432,8 @@
.add(1 /* Issuer */, "Issuer")
.add(2 /* Subject */, "Subject")
.add(-4670552 /* Subject Pub Key */, coseKey)
- .add(-4670553 /* Key Usage */,
- std::vector<uint8_t>(0x05) /* Big endian order */)
+ .add(-4670553 /* Key Usage (little-endian order) */,
+ std::vector<uint8_t>{0x20} /* keyCertSign = 1<<5 */)
.canonicalize()
.encode();
auto coseSign1 = constructCoseSign1(privKey, /* signing key */
diff --git a/security/keymint/aidl/vts/functional/Android.bp b/security/keymint/aidl/vts/functional/Android.bp
index 991d77a..c1affa6 100644
--- a/security/keymint/aidl/vts/functional/Android.bp
+++ b/security/keymint/aidl/vts/functional/Android.bp
@@ -94,11 +94,14 @@
],
static_libs: [
"android.hardware.security.keymint-V1-ndk_platform",
+ "android.hardware.security.secureclock-V1-ndk_platform",
"libcppcose",
"libgmock_ndk",
- "libremote_provisioner",
"libkeymint",
+ "libkeymint_support",
"libkeymint_remote_prov_support",
+ "libkeymint_vts_test_utils",
+ "libremote_provisioner",
],
test_suites: [
"general-tests",
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
index 7e7a466..1e907db 100644
--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -26,29 +26,6 @@
namespace {
-vector<uint8_t> make_name_from_str(const string& name) {
- X509_NAME_Ptr x509_name(X509_NAME_new());
- EXPECT_TRUE(x509_name.get() != nullptr);
- if (!x509_name) return {};
-
- EXPECT_EQ(1, X509_NAME_add_entry_by_txt(x509_name.get(), //
- "CN", //
- MBSTRING_ASC,
- reinterpret_cast<const uint8_t*>(name.c_str()),
- -1, // len
- -1, // loc
- 0 /* set */));
-
- int len = i2d_X509_NAME(x509_name.get(), nullptr /* only return length */);
- EXPECT_GT(len, 0);
-
- vector<uint8_t> retval(len);
- uint8_t* p = retval.data();
- i2d_X509_NAME(x509_name.get(), &p);
-
- return retval;
-}
-
bool IsSelfSigned(const vector<Certificate>& chain) {
if (chain.size() != 1) return false;
return ChainSignaturesAreValid(chain);
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 3e87b6b..ce6f67a 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -811,30 +811,6 @@
return (found == key_characteristics.end()) ? kEmptyAuthList : found->authorizations;
}
-AuthorizationSet KeyMintAidlTestBase::HwEnforcedAuthorizations(
- const vector<KeyCharacteristics>& key_characteristics) {
- AuthorizationSet authList;
- for (auto& entry : key_characteristics) {
- if (entry.securityLevel == SecurityLevel::STRONGBOX ||
- entry.securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT) {
- authList.push_back(AuthorizationSet(entry.authorizations));
- }
- }
- return authList;
-}
-
-AuthorizationSet KeyMintAidlTestBase::SwEnforcedAuthorizations(
- const vector<KeyCharacteristics>& key_characteristics) {
- AuthorizationSet authList;
- for (auto& entry : key_characteristics) {
- if (entry.securityLevel == SecurityLevel::SOFTWARE ||
- entry.securityLevel == SecurityLevel::KEYSTORE) {
- authList.push_back(AuthorizationSet(entry.authorizations));
- }
- }
- return authList;
-}
-
ErrorCode KeyMintAidlTestBase::UseAesKey(const vector<uint8_t>& aesKeyBlob) {
auto [result, ciphertext] = ProcessMessage(
aesKeyBlob, KeyPurpose::ENCRYPT, "1234567890123456",
@@ -1046,6 +1022,28 @@
return retval;
}
+AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics) {
+ AuthorizationSet authList;
+ for (auto& entry : key_characteristics) {
+ if (entry.securityLevel == SecurityLevel::STRONGBOX ||
+ entry.securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT) {
+ authList.push_back(AuthorizationSet(entry.authorizations));
+ }
+ }
+ return authList;
+}
+
+AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics) {
+ AuthorizationSet authList;
+ for (auto& entry : key_characteristics) {
+ if (entry.securityLevel == SecurityLevel::SOFTWARE ||
+ entry.securityLevel == SecurityLevel::KEYSTORE) {
+ authList.push_back(AuthorizationSet(entry.authorizations));
+ }
+ }
+ return authList;
+}
+
AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain) {
std::stringstream cert_data;
@@ -1097,6 +1095,29 @@
return X509_Ptr(d2i_X509(nullptr /* allocate new */, &p, blob.size()));
}
+vector<uint8_t> make_name_from_str(const string& name) {
+ X509_NAME_Ptr x509_name(X509_NAME_new());
+ EXPECT_TRUE(x509_name.get() != nullptr);
+ if (!x509_name) return {};
+
+ EXPECT_EQ(1, X509_NAME_add_entry_by_txt(x509_name.get(), //
+ "CN", //
+ MBSTRING_ASC,
+ reinterpret_cast<const uint8_t*>(name.c_str()),
+ -1, // len
+ -1, // loc
+ 0 /* set */));
+
+ int len = i2d_X509_NAME(x509_name.get(), nullptr /* only return length */);
+ EXPECT_GT(len, 0);
+
+ vector<uint8_t> retval(len);
+ uint8_t* p = retval.data();
+ i2d_X509_NAME(x509_name.get(), &p);
+
+ return retval;
+}
+
} // namespace test
} // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 0aef81b..86bc9c4 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -252,10 +252,6 @@
const vector<KeyParameter>& SecLevelAuthorizations(
const vector<KeyCharacteristics>& key_characteristics, SecurityLevel securityLevel);
- AuthorizationSet HwEnforcedAuthorizations(
- const vector<KeyCharacteristics>& key_characteristics);
- AuthorizationSet SwEnforcedAuthorizations(
- const vector<KeyCharacteristics>& key_characteristics);
ErrorCode UseAesKey(const vector<uint8_t>& aesKeyBlob);
ErrorCode UseHmacKey(const vector<uint8_t>& hmacKeyBlob);
ErrorCode UseRsaKey(const vector<uint8_t>& rsaKeyBlob);
@@ -280,6 +276,9 @@
const vector<uint8_t>& attestation_cert);
string bin2hex(const vector<uint8_t>& data);
X509_Ptr parse_cert_blob(const vector<uint8_t>& blob);
+vector<uint8_t> make_name_from_str(const string& name);
+AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
+AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
::testing::AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain);
#define INSTANTIATE_KEYMINT_AIDL_TEST(name) \
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index 9b797de..e4c4a22 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -17,18 +17,21 @@
#define LOG_TAG "VtsRemotelyProvisionableComponentTests"
#include <RemotelyProvisionedComponent.h>
-#include <aidl/Gtest.h>
-#include <aidl/Vintf.h>
#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
#include <android/binder_manager.h>
#include <cppbor_parse.h>
#include <cppcose/cppcose.h>
#include <gmock/gmock.h>
-#include <gtest/gtest.h>
#include <keymaster/keymaster_configuration.h>
+#include <keymint_support/authorization_set.h>
+#include <openssl/ec.h>
+#include <openssl/ec_key.h>
+#include <openssl/x509.h>
#include <remote_prov/remote_prov_utils.h>
+#include "KeyMintAidlTestBase.h"
+
namespace aidl::android::hardware::security::keymint::test {
using ::std::string;
@@ -52,6 +55,190 @@
return bytevec(p, p + strlen(s));
}
+void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey) {
+ // Extract x and y affine coordinates from the encoded Cose_Key.
+ auto [parsedPayload, __, payloadParseErr] = cppbor::parse(coseKeyData);
+ ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
+ auto coseKey = parsedPayload->asMap();
+ const std::unique_ptr<cppbor::Item>& xItem = coseKey->get(cppcose::CoseKey::PUBKEY_X);
+ ASSERT_NE(xItem->asBstr(), nullptr);
+ vector<uint8_t> x = xItem->asBstr()->value();
+ const std::unique_ptr<cppbor::Item>& yItem = coseKey->get(cppcose::CoseKey::PUBKEY_Y);
+ ASSERT_NE(yItem->asBstr(), nullptr);
+ vector<uint8_t> y = yItem->asBstr()->value();
+
+ // Concatenate: 0x04 (uncompressed form marker) | x | y
+ vector<uint8_t> pubKeyData{0x04};
+ pubKeyData.insert(pubKeyData.end(), x.begin(), x.end());
+ pubKeyData.insert(pubKeyData.end(), y.begin(), y.end());
+
+ EC_KEY_Ptr ecKey = EC_KEY_Ptr(EC_KEY_new());
+ ASSERT_NE(ecKey, nullptr);
+ EC_GROUP_Ptr group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ ASSERT_NE(group, nullptr);
+ ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
+ EC_POINT_Ptr point = EC_POINT_Ptr(EC_POINT_new(group.get()));
+ ASSERT_NE(point, nullptr);
+ ASSERT_EQ(EC_POINT_oct2point(group.get(), point.get(), pubKeyData.data(), pubKeyData.size(),
+ nullptr),
+ 1);
+ ASSERT_EQ(EC_KEY_set_public_key(ecKey.get(), point.get()), 1);
+
+ EVP_PKEY_Ptr pubKey = EVP_PKEY_Ptr(EVP_PKEY_new());
+ ASSERT_NE(pubKey, nullptr);
+ EVP_PKEY_assign_EC_KEY(pubKey.get(), ecKey.release());
+ *signingKey = std::move(pubKey);
+}
+
+void check_cose_key(const vector<uint8_t>& data, bool testMode) {
+ auto [parsedPayload, __, payloadParseErr] = cppbor::parse(data);
+ ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
+
+ // The following check assumes that canonical CBOR encoding is used for the COSE_Key.
+ if (testMode) {
+ EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
+ MatchesRegex("{\n"
+ " 1 : 2,\n" // kty: EC2
+ " 3 : -7,\n" // alg: ES256
+ " -1 : 1,\n" // EC id: P256
+ // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
+ // sequence of 32 hexadecimal bytes, enclosed in braces and
+ // separated by commas. In this case, some Ed25519 public key.
+ " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_x: data
+ " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_y: data
+ " -70000 : null,\n" // test marker
+ "}"));
+ } else {
+ EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
+ MatchesRegex("{\n"
+ " 1 : 2,\n" // kty: EC2
+ " 3 : -7,\n" // alg: ES256
+ " -1 : 1,\n" // EC id: P256
+ // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
+ // sequence of 32 hexadecimal bytes, enclosed in braces and
+ // separated by commas. In this case, some Ed25519 public key.
+ " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_x: data
+ " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n" // pub_y: data
+ "}"));
+ }
+}
+
+void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
+ vector<uint8_t>* payload_value) {
+ auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
+ ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;
+
+ ASSERT_NE(coseMac0->asArray(), nullptr);
+ ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);
+
+ auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
+ ASSERT_NE(protParms, nullptr);
+
+ // Header label:value of 'alg': HMAC-256
+ ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n 1 : 5,\n}");
+
+ auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
+ ASSERT_NE(unprotParms, nullptr);
+ ASSERT_EQ(unprotParms->size(), 0);
+
+ // The payload is a bstr holding an encoded COSE_Key
+ auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
+ ASSERT_NE(payload, nullptr);
+ check_cose_key(payload->value(), testMode);
+
+ auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
+ ASSERT_TRUE(coseMac0Tag);
+ auto extractedTag = coseMac0Tag->value();
+ EXPECT_EQ(extractedTag.size(), 32U);
+
+ // Compare with tag generated with kTestMacKey. Should only match in test mode
+ auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
+ payload->value());
+ ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();
+
+ if (testMode) {
+ EXPECT_EQ(*testTag, extractedTag);
+ } else {
+ EXPECT_NE(*testTag, extractedTag);
+ }
+ if (payload_value != nullptr) {
+ *payload_value = payload->value();
+ }
+}
+
+ErrMsgOr<MacedPublicKey> corrupt_maced_key(const MacedPublicKey& macedPubKey) {
+ auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
+ if (!coseMac0 || coseMac0->asArray()->size() != kCoseMac0EntryCount) {
+ return "COSE Mac0 parse failed";
+ }
+ auto protParams = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
+ auto unprotParams = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
+ auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
+ auto tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
+ if (!protParams || !unprotParams || !payload || !tag) {
+ return "Invalid COSE_Sign1: missing content";
+ }
+ auto corruptMac0 = cppbor::Array();
+ corruptMac0.add(protParams->clone());
+ corruptMac0.add(unprotParams->clone());
+ corruptMac0.add(payload->clone());
+ vector<uint8_t> tagData = tag->value();
+ tagData[0] ^= 0x08;
+ tagData[tagData.size() - 1] ^= 0x80;
+ corruptMac0.add(cppbor::Bstr(tagData));
+
+ return MacedPublicKey{corruptMac0.encode()};
+}
+
+ErrMsgOr<cppbor::Array> corrupt_sig(const cppbor::Array* coseSign1) {
+ if (coseSign1->size() != kCoseSign1EntryCount) {
+ return "Invalid COSE_Sign1, wrong entry count";
+ }
+ const cppbor::Bstr* protectedParams = coseSign1->get(kCoseSign1ProtectedParams)->asBstr();
+ const cppbor::Map* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asMap();
+ const cppbor::Bstr* payload = coseSign1->get(kCoseSign1Payload)->asBstr();
+ const cppbor::Bstr* signature = coseSign1->get(kCoseSign1Signature)->asBstr();
+ if (!protectedParams || !unprotectedParams || !payload || !signature) {
+ return "Invalid COSE_Sign1: missing content";
+ }
+
+ auto corruptSig = cppbor::Array();
+ corruptSig.add(protectedParams->clone());
+ corruptSig.add(unprotectedParams->clone());
+ corruptSig.add(payload->clone());
+ vector<uint8_t> sigData = signature->value();
+ sigData[0] ^= 0x08;
+ corruptSig.add(cppbor::Bstr(sigData));
+
+ return std::move(corruptSig);
+}
+
+ErrMsgOr<EekChain> corrupt_sig_chain(const EekChain& eek, int which) {
+ auto [chain, _, parseErr] = cppbor::parse(eek.chain);
+ if (!chain || !chain->asArray()) {
+ return "EekChain parse failed";
+ }
+
+ cppbor::Array* eekChain = chain->asArray();
+ if (which >= eekChain->size()) {
+ return "selected sig out of range";
+ }
+ auto corruptChain = cppbor::Array();
+
+ for (int ii = 0; ii < eekChain->size(); ++ii) {
+ if (ii == which) {
+ auto sig = corrupt_sig(eekChain->get(which)->asArray());
+ if (!sig) {
+ return "Failed to build corrupted signature" + sig.moveMessage();
+ }
+ corruptChain.add(sig.moveValue());
+ } else {
+ corruptChain.add(eekChain->get(ii)->clone());
+ }
+ }
+ return EekChain{corruptChain.encode(), eek.last_pubkey, eek.last_privkey};
+}
+
} // namespace
class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std::string> {
@@ -78,7 +265,8 @@
INSTANTIATE_REM_PROV_AIDL_TEST(GenerateKeyTests);
/**
- * Generate and validate a production-mode key. MAC tag can't be verified.
+ * Generate and validate a production-mode key. MAC tag can't be verified, but
+ * the private key blob should be usable in KeyMint operations.
*/
TEST_P(GenerateKeyTests, generateEcdsaP256Key_prodMode) {
MacedPublicKey macedPubKey;
@@ -86,48 +274,59 @@
bool testMode = false;
auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
ASSERT_TRUE(status.isOk());
+ vector<uint8_t> coseKeyData;
+ check_maced_pubkey(macedPubKey, testMode, &coseKeyData);
+ AttestationKey attestKey;
+ attestKey.keyBlob = std::move(privateKeyBlob);
+ attestKey.issuerSubjectName = make_name_from_str("Android Keystore Key");
- auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
- ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;
+ // Also talk to an IKeyMintDevice.
+ // TODO: if there were multiple instances of IRemotelyProvisionedComponent and IKeyMintDevice,
+ // what should the correlation between them be?
+ vector<string> params = ::android::getAidlHalInstanceNames(IKeyMintDevice::descriptor);
+ ASSERT_GT(params.size(), 0U);
+ ASSERT_TRUE(AServiceManager_isDeclared(params[0].c_str()));
+ ::ndk::SpAIBinder binder(AServiceManager_waitForService(params[0].c_str()));
+ std::shared_ptr<IKeyMintDevice> keyMint = IKeyMintDevice::fromBinder(binder);
+ KeyMintHardwareInfo info;
+ ASSERT_TRUE(keyMint->getHardwareInfo(&info).isOk());
- ASSERT_NE(coseMac0->asArray(), nullptr);
- ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);
+ // Generate an ECDSA key that is attested by the generated P256 keypair.
+ AuthorizationSet keyDesc = AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(256)
+ .AttestationChallenge("foo")
+ .AttestationApplicationId("bar")
+ .Digest(Digest::NONE)
+ .SetDefaultValidity();
+ KeyCreationResult creationResult;
+ auto result = keyMint->generateKey(keyDesc.vector_data(), attestKey, &creationResult);
+ ASSERT_TRUE(result.isOk());
+ vector<uint8_t> attested_key_blob = std::move(creationResult.keyBlob);
+ vector<KeyCharacteristics> attested_key_characteristics =
+ std::move(creationResult.keyCharacteristics);
+ vector<Certificate> attested_key_cert_chain = std::move(creationResult.certificateChain);
+ EXPECT_EQ(attested_key_cert_chain.size(), 1);
- auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
- ASSERT_NE(protParms, nullptr);
- ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n 1 : 5,\n}");
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+ EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced,
+ info.securityLevel,
+ attested_key_cert_chain[0].encodedCertificate));
- auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
- ASSERT_NE(unprotParms, nullptr);
- ASSERT_EQ(unprotParms->size(), 0);
+ // Attestation by itself is not valid (last entry is not self-signed).
+ EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
- auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
- ASSERT_NE(payload, nullptr);
- auto [parsedPayload, __, payloadParseErr] = cppbor::parse(payload->value());
- ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
- EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
- MatchesRegex("{\n"
- " 1 : 2,\n"
- " 3 : -7,\n"
- " -1 : 1,\n"
- // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a sequence of
- // 32 hexadecimal bytes, enclosed in braces and separated by commas.
- // In this case, some Ed25519 public key.
- " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
- " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
- "}"));
+ // The signature over the attested key should correspond to the P256 public key.
+ X509_Ptr key_cert(parse_cert_blob(attested_key_cert_chain[0].encodedCertificate));
+ ASSERT_TRUE(key_cert.get());
+ EVP_PKEY_Ptr signing_pubkey;
+ p256_pub_key(coseKeyData, &signing_pubkey);
+ ASSERT_TRUE(signing_pubkey.get());
- auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
- ASSERT_TRUE(coseMac0Tag);
- auto extractedTag = coseMac0Tag->value();
- EXPECT_EQ(extractedTag.size(), 32U);
-
- // Compare with tag generated with kTestMacKey. Shouldn't match.
- auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
- payload->value());
- ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();
-
- EXPECT_NE(*testTag, extractedTag);
+ ASSERT_TRUE(X509_verify(key_cert.get(), signing_pubkey.get()))
+ << "Verification of attested certificate failed "
+ << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL);
}
/**
@@ -140,56 +339,20 @@
auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
ASSERT_TRUE(status.isOk());
- auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
- ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;
-
- ASSERT_NE(coseMac0->asArray(), nullptr);
- ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);
-
- auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
- ASSERT_NE(protParms, nullptr);
- ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n 1 : 5,\n}");
-
- auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
- ASSERT_NE(unprotParms, nullptr);
- ASSERT_EQ(unprotParms->size(), 0);
-
- auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
- ASSERT_NE(payload, nullptr);
- auto [parsedPayload, __, payloadParseErr] = cppbor::parse(payload->value());
- ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
- EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
- MatchesRegex("{\n"
- " 1 : 2,\n"
- " 3 : -7,\n"
- " -1 : 1,\n"
- // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a sequence of
- // 32 hexadecimal bytes, enclosed in braces and separated by commas.
- // In this case, some Ed25519 public key.
- " -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
- " -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"
- " -70000 : null,\n"
- "}"));
-
- auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
- ASSERT_TRUE(coseMac0);
- auto extractedTag = coseMac0Tag->value();
- EXPECT_EQ(extractedTag.size(), 32U);
-
- // Compare with tag generated with kTestMacKey. Should match.
- auto testTag = cppcose::generateCoseMac0Mac(remote_prov::kTestMacKey, {} /* external_aad */,
- payload->value());
- ASSERT_TRUE(testTag) << testTag.message();
-
- EXPECT_EQ(*testTag, extractedTag);
+ check_maced_pubkey(macedPubKey, testMode, nullptr);
}
class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
protected:
- CertificateRequestTest() : eekId_(string_to_bytevec("eekid")) {
- auto chain = generateEekChain(3, eekId_);
+ CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(32)) {
+ generateEek(3);
+ }
+
+ void generateEek(size_t eekLength) {
+ auto chain = generateEekChain(eekLength, eekId_);
EXPECT_TRUE(chain) << chain.message();
if (chain) eekChain_ = chain.moveValue();
+ eekLength_ = eekLength;
}
void generateKeys(bool testMode, size_t numKeys) {
@@ -201,21 +364,71 @@
auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &key, &privateKeyBlob);
ASSERT_TRUE(status.isOk()) << status.getMessage();
- auto [parsedMacedKey, _, parseErr] = cppbor::parse(key.macedKey);
- ASSERT_TRUE(parsedMacedKey) << "Failed parsing MACed key: " << parseErr;
- ASSERT_TRUE(parsedMacedKey->asArray()) << "COSE_Mac0 not an array?";
- ASSERT_EQ(parsedMacedKey->asArray()->size(), kCoseMac0EntryCount);
-
- auto& payload = parsedMacedKey->asArray()->get(kCoseMac0Payload);
- ASSERT_TRUE(payload);
- ASSERT_TRUE(payload->asBstr());
-
- cborKeysToSign_.add(cppbor::EncodedItem(payload->asBstr()->value()));
+ vector<uint8_t> payload_value;
+ check_maced_pubkey(key, testMode, &payload_value);
+ cborKeysToSign_.add(cppbor::EncodedItem(payload_value));
}
}
+ void checkProtectedData(bool testMode, const cppbor::Array& keysToSign,
+ const bytevec& keysToSignMac, const ProtectedData& protectedData) {
+ auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
+ ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
+ ASSERT_TRUE(parsedProtectedData->asArray());
+ ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
+
+ auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
+ ASSERT_TRUE(senderPubkey) << senderPubkey.message();
+ EXPECT_EQ(senderPubkey->second, eekId_);
+
+ auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
+ senderPubkey->first, false /* senderIsA */);
+ ASSERT_TRUE(sessionKey) << sessionKey.message();
+
+ auto protectedDataPayload =
+ decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
+ ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
+
+ auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
+ ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
+ ASSERT_TRUE(parsedPayload->asArray());
+ EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
+
+ auto& signedMac = parsedPayload->asArray()->get(0);
+ auto& bcc = parsedPayload->asArray()->get(1);
+ ASSERT_TRUE(signedMac && signedMac->asArray());
+ ASSERT_TRUE(bcc && bcc->asArray());
+
+ // BCC is [ pubkey, + BccEntry]
+ auto bccContents = validateBcc(bcc->asArray());
+ ASSERT_TRUE(bccContents) << "\n" << bccContents.message() << "\n" << prettyPrint(bcc.get());
+ ASSERT_GT(bccContents->size(), 0U);
+
+ auto& signingKey = bccContents->back().pubKey;
+ auto macKey = verifyAndParseCoseSign1(testMode, signedMac->asArray(), signingKey,
+ cppbor::Array() // DeviceInfo
+ .add(challenge_)
+ .add(cppbor::Map())
+ .encode());
+ ASSERT_TRUE(macKey) << macKey.message();
+
+ auto coseMac0 = cppbor::Array()
+ .add(cppbor::Map() // protected
+ .add(ALGORITHM, HMAC_256)
+ .canonicalize()
+ .encode())
+ .add(cppbor::Map()) // unprotected
+ .add(keysToSign.encode()) // payload (keysToSign)
+ .add(keysToSignMac); // tag
+
+ auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
+ ASSERT_TRUE(macPayload) << macPayload.message();
+ }
+
bytevec eekId_;
+ size_t eekLength_;
EekChain eekChain_;
+ bytevec challenge_;
std::vector<MacedPublicKey> keysToSign_;
cppbor::Array cborKeysToSign_;
};
@@ -226,66 +439,20 @@
*/
TEST_P(CertificateRequestTest, EmptyRequest_testMode) {
bool testMode = true;
- bytevec keysToSignMac;
- DeviceInfo deviceInfo;
- ProtectedData protectedData;
- auto challenge = randomBytes(32);
- auto status = provisionable_->generateCertificateRequest(
- testMode, {} /* keysToSign */, eekChain_.chain, challenge, &deviceInfo, &protectedData,
- &keysToSignMac);
- ASSERT_TRUE(status.isOk()) << status.getMessage();
+ for (size_t eekLength : {2, 3, 7}) {
+ SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
+ generateEek(eekLength);
- auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
- ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
- ASSERT_TRUE(parsedProtectedData->asArray());
- ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, {} /* keysToSign */, eekChain_.chain, challenge_, &deviceInfo,
+ &protectedData, &keysToSignMac);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
- auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
- ASSERT_TRUE(senderPubkey) << senderPubkey.message();
- EXPECT_EQ(senderPubkey->second, eekId_);
-
- auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
- senderPubkey->first, false /* senderIsA */);
- ASSERT_TRUE(sessionKey) << sessionKey.message();
-
- auto protectedDataPayload =
- decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
- ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
-
- auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
- ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
- ASSERT_TRUE(parsedPayload->asArray());
- EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
-
- auto& signedMac = parsedPayload->asArray()->get(0);
- auto& bcc = parsedPayload->asArray()->get(1);
- ASSERT_TRUE(signedMac && signedMac->asArray());
- ASSERT_TRUE(bcc && bcc->asArray());
-
- // BCC is [ pubkey, + BccEntry]
- auto bccContents = validateBcc(bcc->asArray());
- ASSERT_TRUE(bccContents) << "\n" << bccContents.message() << "\n" << prettyPrint(bcc.get());
- ASSERT_GT(bccContents->size(), 0U);
-
- auto& signingKey = bccContents->back().pubKey;
- auto macKey = verifyAndParseCoseSign1(testMode, signedMac->asArray(), signingKey,
- cppbor::Array() // DeviceInfo
- .add(challenge) //
- .add(cppbor::Map())
- .encode());
- ASSERT_TRUE(macKey) << macKey.message();
-
- auto coseMac0 = cppbor::Array()
- .add(cppbor::Map() // protected
- .add(ALGORITHM, HMAC_256)
- .canonicalize()
- .encode())
- .add(cppbor::Map()) // unprotected
- .add(cppbor::Array().encode()) // payload (keysToSign)
- .add(std::move(keysToSignMac)); // tag
-
- auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
- ASSERT_TRUE(macPayload) << macPayload.message();
+ checkProtectedData(testMode, cppbor::Array(), keysToSignMac, protectedData);
+ }
}
/**
@@ -297,15 +464,20 @@
*/
TEST_P(CertificateRequestTest, EmptyRequest_prodMode) {
bool testMode = false;
- bytevec keysToSignMac;
- DeviceInfo deviceInfo;
- ProtectedData protectedData;
- auto challenge = randomBytes(32);
- auto status = provisionable_->generateCertificateRequest(
- testMode, {} /* keysToSign */, eekChain_.chain, challenge, &deviceInfo, &protectedData,
- &keysToSignMac);
- ASSERT_FALSE(status.isOk());
- ASSERT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+ for (size_t eekLength : {2, 3, 7}) {
+ SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
+ generateEek(eekLength);
+
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, {} /* keysToSign */, eekChain_.chain, challenge_, &deviceInfo,
+ &protectedData, &keysToSignMac);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(),
+ BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+ }
}
/**
@@ -315,65 +487,20 @@
bool testMode = true;
generateKeys(testMode, 4 /* numKeys */);
- bytevec keysToSignMac;
- DeviceInfo deviceInfo;
- ProtectedData protectedData;
- auto challenge = randomBytes(32);
- auto status = provisionable_->generateCertificateRequest(testMode, keysToSign_, eekChain_.chain,
- challenge, &deviceInfo, &protectedData,
- &keysToSignMac);
- ASSERT_TRUE(status.isOk()) << status.getMessage();
+ for (size_t eekLength : {2, 3, 7}) {
+ SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
+ generateEek(eekLength);
- auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
- ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
- ASSERT_TRUE(parsedProtectedData->asArray());
- ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, keysToSign_, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
+ &keysToSignMac);
+ ASSERT_TRUE(status.isOk()) << status.getMessage();
- auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
- ASSERT_TRUE(senderPubkey) << senderPubkey.message();
- EXPECT_EQ(senderPubkey->second, eekId_);
-
- auto sessionKey = x25519_HKDF_DeriveKey(eekChain_.last_pubkey, eekChain_.last_privkey,
- senderPubkey->first, false /* senderIsA */);
- ASSERT_TRUE(sessionKey) << sessionKey.message();
-
- auto protectedDataPayload =
- decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
- ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
-
- auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
- ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
- ASSERT_TRUE(parsedPayload->asArray());
- EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
-
- auto& signedMac = parsedPayload->asArray()->get(0);
- auto& bcc = parsedPayload->asArray()->get(1);
- ASSERT_TRUE(signedMac && signedMac->asArray());
- ASSERT_TRUE(bcc);
-
- auto bccContents = validateBcc(bcc->asArray());
- ASSERT_TRUE(bccContents) << "\n" << prettyPrint(bcc.get());
- ASSERT_GT(bccContents->size(), 0U);
-
- auto& signingKey = bccContents->back().pubKey;
- auto macKey = verifyAndParseCoseSign1(testMode, signedMac->asArray(), signingKey,
- cppbor::Array() // DeviceInfo
- .add(challenge) //
- .add(cppbor::Array())
- .encode());
- ASSERT_TRUE(macKey) << macKey.message();
-
- auto coseMac0 = cppbor::Array()
- .add(cppbor::Map() // protected
- .add(ALGORITHM, HMAC_256)
- .canonicalize()
- .encode())
- .add(cppbor::Map()) // unprotected
- .add(cborKeysToSign_.encode()) // payload
- .add(std::move(keysToSignMac)); // tag
-
- auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
- ASSERT_TRUE(macPayload) << macPayload.message();
+ checkProtectedData(testMode, cborKeysToSign_, keysToSignMac, protectedData);
+ }
}
/**
@@ -387,13 +514,117 @@
bool testMode = false;
generateKeys(testMode, 4 /* numKeys */);
+ for (size_t eekLength : {2, 3, 7}) {
+ SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
+ generateEek(eekLength);
+
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, keysToSign_, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
+ &keysToSignMac);
+ EXPECT_FALSE(status.isOk());
+ EXPECT_EQ(status.getServiceSpecificError(),
+ BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+ }
+}
+
+/**
+ * Generate a non-empty certificate request in test mode, but with the MAC corrupted on the keypair.
+ */
+TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_testMode) {
+ bool testMode = true;
+ generateKeys(testMode, 1 /* numKeys */);
+ MacedPublicKey keyWithCorruptMac = corrupt_maced_key(keysToSign_[0]).moveValue();
+
bytevec keysToSignMac;
DeviceInfo deviceInfo;
ProtectedData protectedData;
- auto challenge = randomBytes(32);
- auto status = provisionable_->generateCertificateRequest(testMode, keysToSign_, eekChain_.chain,
- challenge, &deviceInfo, &protectedData,
- &keysToSignMac);
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, {keyWithCorruptMac}, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
+ &keysToSignMac);
+ ASSERT_FALSE(status.isOk()) << status.getMessage();
+ EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode, but with the MAC corrupted on the keypair.
+ */
+TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_prodMode) {
+ bool testMode = true;
+ generateKeys(testMode, 1 /* numKeys */);
+ MacedPublicKey keyWithCorruptMac = corrupt_maced_key(keysToSign_[0]).moveValue();
+
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, {keyWithCorruptMac}, eekChain_.chain, challenge_, &deviceInfo, &protectedData,
+ &keysToSignMac);
+ ASSERT_FALSE(status.isOk()) << status.getMessage();
+ auto rc = status.getServiceSpecificError();
+
+ // TODO(drysdale): drop the INVALID_EEK potential error code when a real GEEK is available.
+ EXPECT_TRUE(rc == BnRemotelyProvisionedComponent::STATUS_INVALID_EEK ||
+ rc == BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode that has a corrupt EEK chain.
+ * Confirm that the request is rejected.
+ *
+ * TODO(drysdale): Update to use a valid GEEK, so that the test actually confirms that the
+ * implementation is checking signatures.
+ */
+TEST_P(CertificateRequestTest, NonEmptyCorruptEekRequest_prodMode) {
+ bool testMode = false;
+ generateKeys(testMode, 4 /* numKeys */);
+
+ for (size_t ii = 0; ii < eekLength_; ii++) {
+ auto chain = corrupt_sig_chain(eekChain_, ii);
+ ASSERT_TRUE(chain) << chain.message();
+ EekChain corruptEek = chain.moveValue();
+
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, keysToSign_, corruptEek.chain, challenge_, &deviceInfo, &protectedData,
+ &keysToSignMac);
+ ASSERT_FALSE(status.isOk());
+ ASSERT_EQ(status.getServiceSpecificError(),
+ BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+ }
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode that has an incomplete EEK chain.
+ * Confirm that the request is rejected.
+ *
+ * TODO(drysdale): Update to use a valid GEEK, so that the test actually confirms that the
+ * implementation is checking signatures.
+ */
+TEST_P(CertificateRequestTest, NonEmptyIncompleteEekRequest_prodMode) {
+ bool testMode = false;
+ generateKeys(testMode, 4 /* numKeys */);
+
+ // Build an EEK chain that omits the first self-signed cert.
+ auto truncatedChain = cppbor::Array();
+ auto [chain, _, parseErr] = cppbor::parse(eekChain_.chain);
+ ASSERT_TRUE(chain);
+ auto eekChain = chain->asArray();
+ ASSERT_NE(eekChain, nullptr);
+ for (size_t ii = 1; ii < eekChain->size(); ii++) {
+ truncatedChain.add(eekChain->get(ii)->clone());
+ }
+
+ bytevec keysToSignMac;
+ DeviceInfo deviceInfo;
+ ProtectedData protectedData;
+ auto status = provisionable_->generateCertificateRequest(
+ testMode, keysToSign_, truncatedChain.encode(), challenge_, &deviceInfo, &protectedData,
+ &keysToSignMac);
ASSERT_FALSE(status.isOk());
ASSERT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
}
@@ -408,9 +639,8 @@
bytevec keysToSignMac;
DeviceInfo deviceInfo;
ProtectedData protectedData;
- auto challenge = randomBytes(32);
auto status = provisionable_->generateCertificateRequest(
- true /* testMode */, keysToSign_, eekChain_.chain, challenge, &deviceInfo,
+ true /* testMode */, keysToSign_, eekChain_.chain, challenge_, &deviceInfo,
&protectedData, &keysToSignMac);
ASSERT_FALSE(status.isOk());
ASSERT_EQ(status.getServiceSpecificError(),
@@ -428,8 +658,8 @@
DeviceInfo deviceInfo;
ProtectedData protectedData;
auto status = provisionable_->generateCertificateRequest(
- false /* testMode */, keysToSign_, eekChain_.chain, randomBytes(32) /* challenge */,
- &deviceInfo, &protectedData, &keysToSignMac);
+ false /* testMode */, keysToSign_, eekChain_.chain, challenge_, &deviceInfo,
+ &protectedData, &keysToSignMac);
ASSERT_FALSE(status.isOk());
ASSERT_EQ(status.getServiceSpecificError(),
BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
diff --git a/security/keymint/support/include/keymint_support/openssl_utils.h b/security/keymint/support/include/keymint_support/openssl_utils.h
index a0212aa..dee28ba 100644
--- a/security/keymint/support/include/keymint_support/openssl_utils.h
+++ b/security/keymint/support/include/keymint_support/openssl_utils.h
@@ -37,6 +37,7 @@
MAKE_OPENSSL_PTR_TYPE(BN_CTX)
MAKE_OPENSSL_PTR_TYPE(EC_GROUP)
MAKE_OPENSSL_PTR_TYPE(EC_KEY)
+MAKE_OPENSSL_PTR_TYPE(EC_POINT)
MAKE_OPENSSL_PTR_TYPE(EVP_PKEY)
MAKE_OPENSSL_PTR_TYPE(EVP_PKEY_CTX)
MAKE_OPENSSL_PTR_TYPE(RSA)
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
index 3e4f3f7..da10eb2 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -54,6 +54,8 @@
{} /* AAD */);
if (!coseSign1) return coseSign1.moveMessage();
eekChain.add(coseSign1.moveValue());
+
+ prev_priv_key = priv_key;
}
bytevec pub_key(X25519_PUBLIC_VALUE_LEN);
diff --git a/tv/cec/1.0/vts/functional/Android.bp b/tv/cec/1.0/vts/functional/Android.bp
index 4d82da3..9a2c714 100644
--- a/tv/cec/1.0/vts/functional/Android.bp
+++ b/tv/cec/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
cc_test {
name: "VtsHalTvCecV1_0TargetTest",
defaults: ["VtsHalTargetTestDefaults"],