Merge "supplicant(vts): Compilation fixes" into oc-dev
diff --git a/audio/2.0/default/Device.cpp b/audio/2.0/default/Device.cpp
index 8a51cd7..5ced0bc 100644
--- a/audio/2.0/default/Device.cpp
+++ b/audio/2.0/default/Device.cpp
@@ -59,6 +59,14 @@
}
}
+void Device::closeInputStream(audio_stream_in_t* stream) {
+ mDevice->close_input_stream(mDevice, stream);
+}
+
+void Device::closeOutputStream(audio_stream_out_t* stream) {
+ mDevice->close_output_stream(mDevice, stream);
+}
+
char* Device::halGetParameters(const char* keys) {
return mDevice->get_parameters(mDevice, keys);
}
@@ -160,7 +168,7 @@
ALOGV("open_output_stream status %d stream %p", status, halStream);
sp<IStreamOut> streamOut;
if (status == OK) {
- streamOut = new StreamOut(mDevice, halStream);
+ streamOut = new StreamOut(this, halStream);
}
AudioConfig suggestedConfig;
HidlUtils::audioConfigFromHal(halConfig, &suggestedConfig);
@@ -196,7 +204,7 @@
ALOGV("open_input_stream status %d stream %p", status, halStream);
sp<IStreamIn> streamIn;
if (status == OK) {
- streamIn = new StreamIn(mDevice, halStream);
+ streamIn = new StreamIn(this, halStream);
}
AudioConfig suggestedConfig;
HidlUtils::audioConfigFromHal(halConfig, &suggestedConfig);
diff --git a/audio/2.0/default/Device.h b/audio/2.0/default/Device.h
index 46177fc..7738361 100644
--- a/audio/2.0/default/Device.h
+++ b/audio/2.0/default/Device.h
@@ -98,6 +98,8 @@
// Utility methods for extending interfaces.
Result analyzeStatus(const char* funcName, int status);
+ void closeInputStream(audio_stream_in_t* stream);
+ void closeOutputStream(audio_stream_out_t* stream);
audio_hw_device_t* device() const { return mDevice; }
private:
diff --git a/audio/2.0/default/StreamIn.cpp b/audio/2.0/default/StreamIn.cpp
index b641e82..2745607 100644
--- a/audio/2.0/default/StreamIn.cpp
+++ b/audio/2.0/default/StreamIn.cpp
@@ -135,7 +135,7 @@
} // namespace
-StreamIn::StreamIn(audio_hw_device_t* device, audio_stream_in_t* stream)
+StreamIn::StreamIn(const sp<Device>& device, audio_stream_in_t* stream)
: mIsClosed(false), mDevice(device), mStream(stream),
mStreamCommon(new Stream(&stream->common)),
mStreamMmap(new StreamMmap<audio_stream_in_t>(stream)),
@@ -154,9 +154,8 @@
status_t status = EventFlag::deleteEventFlag(&mEfGroup);
ALOGE_IF(status, "read MQ event flag deletion error: %s", strerror(-status));
}
- mDevice->close_input_stream(mDevice, mStream);
+ mDevice->closeInputStream(mStream);
mStream = nullptr;
- mDevice = nullptr;
}
// Methods from ::android::hardware::audio::V2_0::IStream follow.
diff --git a/audio/2.0/default/StreamIn.h b/audio/2.0/default/StreamIn.h
index b867387..950d68f 100644
--- a/audio/2.0/default/StreamIn.h
+++ b/audio/2.0/default/StreamIn.h
@@ -27,6 +27,7 @@
#include <hidl/Status.h>
#include <utils/Thread.h>
+#include "Device.h"
#include "Stream.h"
namespace android {
@@ -55,7 +56,7 @@
typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
typedef MessageQueue<ReadStatus, kSynchronizedReadWrite> StatusMQ;
- StreamIn(audio_hw_device_t* device, audio_stream_in_t* stream);
+ StreamIn(const sp<Device>& device, audio_stream_in_t* stream);
// Methods from ::android::hardware::audio::V2_0::IStream follow.
Return<uint64_t> getFrameSize() override;
@@ -101,10 +102,10 @@
private:
bool mIsClosed;
- audio_hw_device_t *mDevice;
+ const sp<Device> mDevice;
audio_stream_in_t *mStream;
- sp<Stream> mStreamCommon;
- sp<StreamMmap<audio_stream_in_t>> mStreamMmap;
+ const sp<Stream> mStreamCommon;
+ const sp<StreamMmap<audio_stream_in_t>> mStreamMmap;
std::unique_ptr<CommandMQ> mCommandMQ;
std::unique_ptr<DataMQ> mDataMQ;
std::unique_ptr<StatusMQ> mStatusMQ;
diff --git a/audio/2.0/default/StreamOut.cpp b/audio/2.0/default/StreamOut.cpp
index d820f3c..88045a0 100644
--- a/audio/2.0/default/StreamOut.cpp
+++ b/audio/2.0/default/StreamOut.cpp
@@ -135,7 +135,7 @@
} // namespace
-StreamOut::StreamOut(audio_hw_device_t* device, audio_stream_out_t* stream)
+StreamOut::StreamOut(const sp<Device>& device, audio_stream_out_t* stream)
: mIsClosed(false), mDevice(device), mStream(stream),
mStreamCommon(new Stream(&stream->common)),
mStreamMmap(new StreamMmap<audio_stream_out_t>(stream)),
@@ -155,9 +155,8 @@
ALOGE_IF(status, "write MQ event flag deletion error: %s", strerror(-status));
}
mCallback.clear();
- mDevice->close_output_stream(mDevice, mStream);
+ mDevice->closeOutputStream(mStream);
mStream = nullptr;
- mDevice = nullptr;
}
// Methods from ::android::hardware::audio::V2_0::IStream follow.
diff --git a/audio/2.0/default/StreamOut.h b/audio/2.0/default/StreamOut.h
index bbe64a1..99352bc 100644
--- a/audio/2.0/default/StreamOut.h
+++ b/audio/2.0/default/StreamOut.h
@@ -27,6 +27,7 @@
#include <fmq/MessageQueue.h>
#include <utils/Thread.h>
+#include "Device.h"
#include "Stream.h"
namespace android {
@@ -57,7 +58,7 @@
typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
typedef MessageQueue<WriteStatus, kSynchronizedReadWrite> StatusMQ;
- StreamOut(audio_hw_device_t* device, audio_stream_out_t* stream);
+ StreamOut(const sp<Device>& device, audio_stream_out_t* stream);
// Methods from ::android::hardware::audio::V2_0::IStream follow.
Return<uint64_t> getFrameSize() override;
@@ -112,10 +113,10 @@
private:
bool mIsClosed;
- audio_hw_device_t *mDevice;
+ const sp<Device> mDevice;
audio_stream_out_t *mStream;
- sp<Stream> mStreamCommon;
- sp<StreamMmap<audio_stream_out_t>> mStreamMmap;
+ const sp<Stream> mStreamCommon;
+ const sp<StreamMmap<audio_stream_out_t>> mStreamMmap;
sp<IStreamOutCallback> mCallback;
std::unique_ptr<CommandMQ> mCommandMQ;
std::unique_ptr<DataMQ> mDataMQ;
diff --git a/automotive/evs/1.0/IEvsCamera.hal b/automotive/evs/1.0/IEvsCamera.hal
index 1b55d1f..dbcaf92 100644
--- a/automotive/evs/1.0/IEvsCamera.hal
+++ b/automotive/evs/1.0/IEvsCamera.hal
@@ -28,10 +28,10 @@
/**
* Returns the ID of this camera.
*
- * Returns the string id of this camera. This must be the same value as reported in
- * the camera_id field of the CameraDesc structure by EvsEnumerator::getCamerList().
+ * Returns the description of this camera. This must be the same value as reported
+ * by EvsEnumerator::getCamerList().
*/
- getId() generates (string cameraId);
+ getCameraInfo() generates (CameraDesc info);
/**
* Specifies the depth of the buffer chain the camera is asked to support.
diff --git a/automotive/evs/1.0/IEvsDisplay.hal b/automotive/evs/1.0/IEvsDisplay.hal
index bbad428..12541f3 100644
--- a/automotive/evs/1.0/IEvsDisplay.hal
+++ b/automotive/evs/1.0/IEvsDisplay.hal
@@ -27,7 +27,7 @@
/**
* Returns basic information about the EVS display provided by the system.
*
- * See the description of the DisplayDesc structure below for details.
+ * See the description of the DisplayDesc structure for details.
*/
getDisplayInfo() generates (DisplayDesc info);
diff --git a/automotive/evs/1.0/IEvsEnumerator.hal b/automotive/evs/1.0/IEvsEnumerator.hal
index 334430b..98d117a 100644
--- a/automotive/evs/1.0/IEvsEnumerator.hal
+++ b/automotive/evs/1.0/IEvsEnumerator.hal
@@ -31,14 +31,14 @@
*/
getCameraList() generates (vec<CameraDesc> cameras);
-
/**
* Get the IEvsCamera associated with a cameraId from a CameraDesc
*
* Given a camera's unique cameraId from ca CameraDesc, returns
- * the ICamera interface assocaited with the specified camera.
- * When done using the camera, it must be returned by calling
- * closeCamera on the ICamera interface.
+ * the ICamera interface associated with the specified camera.
+ * When done using the camera, the caller may release it by calling closeCamera().
+ * TODO(b/36122635) Reliance on the sp<> going out of scope is not recommended because the
+ * resources may not be released right away due to asynchronos behavior in the hardware binder.
*/
openCamera(string cameraId) generates (IEvsCamera carCamera);
@@ -57,6 +57,9 @@
* There can be at most one EVS display object for the system and this function
* requests access to it. If the EVS display is not available or is already in use,
* a null pointer is returned.
+ * When done using the display, the caller may release it by calling closeDisplay().
+ * TODO(b/36122635) Reliance on the sp<> going out of scope is not recommended because the
+ * resources may not be released right away due to asynchronos behavior in the hardware binder.
*/
openDisplay() generates (IEvsDisplay display);
@@ -64,7 +67,7 @@
* Return the specified IEvsDisplay interface as no longer in use
*
* When the IEvsDisplay object is no longer required, it must be released.
- * NOTE: All buffer must have been returned to the display before making this call.
+ * NOTE: All buffers must have been returned to the display before making this call.
*/
closeDisplay(IEvsDisplay display);
diff --git a/automotive/evs/1.0/default/Android.bp b/automotive/evs/1.0/default/Android.bp
index 8b214e3..2574e86 100644
--- a/automotive/evs/1.0/default/Android.bp
+++ b/automotive/evs/1.0/default/Android.bp
@@ -23,4 +23,9 @@
"liblog",
"libutils",
],
+
+ cflags: [
+ "-O0",
+ "-g",
+ ],
}
diff --git a/automotive/evs/1.0/default/EvsCamera.cpp b/automotive/evs/1.0/default/EvsCamera.cpp
index c4436ee..148b796 100644
--- a/automotive/evs/1.0/default/EvsCamera.cpp
+++ b/automotive/evs/1.0/default/EvsCamera.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "android.hardware.automotive.evs@1.0-service"
#include "EvsCamera.h"
+#include "EvsEnumerator.h"
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
@@ -30,18 +31,15 @@
namespace implementation {
-// These are the special camera names for which we'll initialize custom test data
+// Special camera names for which we'll initialize alternate test data
const char EvsCamera::kCameraName_Backup[] = "backup";
-const char EvsCamera::kCameraName_RightTurn[] = "Right Turn";
+
// Arbitrary limit on number of graphics buffers allowed to be allocated
// Safeguards against unreasonable resource consumption and provides a testable limit
const unsigned MAX_BUFFERS_IN_FLIGHT = 100;
-// TODO(b/31632518): Need to get notification when our client dies so we can close the camera.
-// As it stands, if the client dies suddenly, the buffer may be stranded.
-
EvsCamera::EvsCamera(const char *id) :
mFramesAllowed(0),
mFramesInUse(0),
@@ -53,22 +51,14 @@
// Set up dummy data for testing
if (mDescription.cameraId == kCameraName_Backup) {
- mDescription.hints = static_cast<uint32_t>(UsageHint::USAGE_HINT_REVERSE);
- mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary value
- mDescription.defaultHorResolution = 320; // 1/2 NTSC/VGA
- mDescription.defaultVerResolution = 240; // 1/2 NTSC/VGA
- } else if (mDescription.cameraId == kCameraName_RightTurn) {
- // Nothing but the name and the usage hint
- mDescription.hints = static_cast<uint32_t>(UsageHint::USAGE_HINT_RIGHT_TURN);
+ mWidth = 640; // full NTSC/VGA
+ mHeight = 480; // full NTSC/VGA
+ mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary value
} else {
- // Leave empty for a minimalist camera description without even a hint
+ mWidth = 320; // 1/2 NTSC/VGA
+ mHeight = 240; // 1/2 NTSC/VGA
}
-
- // Set our buffer properties
- mWidth = (mDescription.defaultHorResolution) ? mDescription.defaultHorResolution : 640;
- mHeight = (mDescription.defaultVerResolution) ? mDescription.defaultVerResolution : 480;
-
mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
@@ -77,32 +67,49 @@
EvsCamera::~EvsCamera() {
ALOGD("EvsCamera being destroyed");
- std::lock_guard<std::mutex> lock(mAccessLock);
+ forceShutdown();
+}
+
+
+//
+// This gets called if another caller "steals" ownership of the camera
+//
+void EvsCamera::forceShutdown()
+{
+ ALOGD("EvsCamera forceShutdown");
// Make sure our output stream is cleaned up
// (It really should be already)
stopVideoStream();
+ // Claim the lock while we work on internal state
+ std::lock_guard <std::mutex> lock(mAccessLock);
+
// Drop all the graphics buffers we've been using
- GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
- for (auto&& rec : mBuffers) {
- if (rec.inUse) {
- ALOGE("Error - releasing buffer despite remote ownership");
+ if (mBuffers.size() > 0) {
+ GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
+ for (auto&& rec : mBuffers) {
+ if (rec.inUse) {
+ ALOGE("Error - releasing buffer despite remote ownership");
+ }
+ alloc.free(rec.handle);
+ rec.handle = nullptr;
}
- alloc.free(rec.handle);
- rec.handle = nullptr;
+ mBuffers.clear();
}
- ALOGD("EvsCamera destroyed");
+ // Put this object into an unrecoverable error state since somebody else
+ // is going to own the underlying camera now
+ mStreamState = DEAD;
}
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
-Return<void> EvsCamera::getId(getId_cb id_cb) {
- ALOGD("getId");
+Return<void> EvsCamera::getCameraInfo(getCameraInfo_cb _hidl_cb) {
+ ALOGD("getCameraInfo");
- id_cb(mDescription.cameraId);
-
+ // Send back our self description
+ _hidl_cb(mDescription);
return Void();
}
@@ -111,6 +118,12 @@
ALOGD("setMaxFramesInFlight");
std::lock_guard<std::mutex> lock(mAccessLock);
+ // If we've been displaced by another owner of the camera, then we can't do anything else
+ if (mStreamState == DEAD) {
+ ALOGE("ignoring setMaxFramesInFlight call when camera has been lost.");
+ return EvsResult::OWNERSHIP_LOST;
+ }
+
// We cannot function without at least one video buffer to send data
if (bufferCount < 1) {
ALOGE("Ignoring setMaxFramesInFlight with less than one buffer requested");
@@ -130,6 +143,11 @@
ALOGD("startVideoStream");
std::lock_guard<std::mutex> lock(mAccessLock);
+ // If we've been displaced by another owner of the camera, then we can't do anything else
+ if (mStreamState == DEAD) {
+ ALOGE("ignoring startVideoStream call when camera has been lost.");
+ return EvsResult::OWNERSHIP_LOST;
+ }
if (mStreamState != STOPPED) {
ALOGE("ignoring startVideoStream call when a stream is already running.");
return EvsResult::STREAM_ALREADY_RUNNING;
@@ -207,6 +225,7 @@
lock.lock();
mStreamState = STOPPED;
+ mStream = nullptr;
ALOGD("Stream marked STOPPED.");
}
@@ -232,6 +251,12 @@
ALOGD("setExtendedInfo");
std::lock_guard<std::mutex> lock(mAccessLock);
+ // If we've been displaced by another owner of the camera, then we can't do anything else
+ if (mStreamState == DEAD) {
+ ALOGE("ignoring setExtendedInfo call when camera has been lost.");
+ return EvsResult::OWNERSHIP_LOST;
+ }
+
// We don't store any device specific information in this implementation
return EvsResult::INVALID_ARG;
}
@@ -358,7 +383,9 @@
while (true) {
bool timeForFrame = false;
- // Lock scope
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ // Lock scope for updating shared state
{
std::lock_guard<std::mutex> lock(mAccessLock);
@@ -427,8 +454,15 @@
}
}
- // We arbitrarily choose to generate frames at 10 fps (1/10 * uSecPerSec)
- usleep(100000);
+ // We arbitrarily choose to generate frames at 12 fps to ensure we pass the 10fps test requirement
+ static const int kTargetFrameRate = 12;
+ static const nsecs_t kTargetFrameTimeUs = 1000*1000 / kTargetFrameRate;
+ const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ const nsecs_t workTimeUs = (now - startTime) / 1000;
+ const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs;
+ if (sleepDurationUs > 0) {
+ usleep(sleepDurationUs);
+ }
}
// If we've been asked to stop, send one last NULL frame to signal the actual end of stream
diff --git a/automotive/evs/1.0/default/EvsCamera.h b/automotive/evs/1.0/default/EvsCamera.h
index ee91ca4..ff6eb39 100644
--- a/automotive/evs/1.0/default/EvsCamera.h
+++ b/automotive/evs/1.0/default/EvsCamera.h
@@ -32,54 +32,50 @@
namespace implementation {
+// From EvsEnumerator.h
+class EvsEnumerator;
+
+
class EvsCamera : public IEvsCamera {
public:
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
- Return<void> getId(getId_cb id_cb) override;
-
+ Return<void> getCameraInfo(getCameraInfo_cb _hidl_cb) override;
Return <EvsResult> setMaxFramesInFlight(uint32_t bufferCount) override;
-
Return <EvsResult> startVideoStream(const ::android::sp<IEvsCameraStream>& stream) override;
-
Return<void> doneWithFrame(const BufferDesc& buffer) override;
-
Return<void> stopVideoStream() override;
-
Return <int32_t> getExtendedInfo(uint32_t opaqueIdentifier) override;
-
Return <EvsResult> setExtendedInfo(uint32_t opaqueIdentifier, int32_t opaqueValue) override;
// Implementation details
- EvsCamera(const char* id);
-
+ EvsCamera(const char *id);
virtual ~EvsCamera() override;
+ void forceShutdown(); // This gets called if another caller "steals" ownership of the camera
const CameraDesc& getDesc() { return mDescription; };
static const char kCameraName_Backup[];
- static const char kCameraName_RightTurn[];
private:
// These three functions are expected to be called while mAccessLock is held
bool setAvailableFrames_Locked(unsigned bufferCount);
-
unsigned increaseAvailableFrames_Locked(unsigned numToAdd);
-
unsigned decreaseAvailableFrames_Locked(unsigned numToRemove);
void generateFrames();
-
void fillTestFrame(const BufferDesc& buff);
- CameraDesc mDescription = {}; // The properties of this camera
+ sp<EvsEnumerator> mEnumerator; // The enumerator object that created this camera
+
+ CameraDesc mDescription = {}; // The properties of this camera
std::thread mCaptureThread; // The thread we'll use to synthesize frames
- uint32_t mWidth = 0; // Horizontal pixel count in the buffers
- uint32_t mHeight = 0; // Vertical pixel count in the buffers
- uint32_t mFormat = 0; // Values from android_pixel_format_t [TODO: YUV? Leave opaque?]
- uint32_t mUsage = 0; // Values from from Gralloc.h
- uint32_t mStride = 0; // Bytes per line in the buffers
+ uint32_t mWidth = 0; // Horizontal pixel count in the buffers
+ uint32_t mHeight = 0; // Vertical pixel count in the buffers
+ uint32_t mFormat = 0; // Values from android_pixel_format_t [TODO: YUV? Leave opaque?]
+ uint32_t mUsage = 0; // Values from from Gralloc.h
+ uint32_t mStride = 0; // Bytes per line in the buffers
sp <IEvsCameraStream> mStream = nullptr; // The callback used to deliver each frame
@@ -98,10 +94,11 @@
STOPPED,
RUNNING,
STOPPING,
+ DEAD,
};
StreamStateValues mStreamState;
- // Syncrhonization necessary to deconflict mCaptureThread from the main service thread
+ // Synchronization necessary to deconflict mCaptureThread from the main service thread
std::mutex mAccessLock;
};
diff --git a/automotive/evs/1.0/default/EvsDisplay.cpp b/automotive/evs/1.0/default/EvsDisplay.cpp
index a1a76d0..9ad332a 100644
--- a/automotive/evs/1.0/default/EvsDisplay.cpp
+++ b/automotive/evs/1.0/default/EvsDisplay.cpp
@@ -30,12 +30,6 @@
namespace implementation {
-// TODO(b/31632518): Need to get notification when our client dies so we can close the camera.
-// As it stands, if the client dies suddently, the buffer may be stranded.
-// As possible work around would be to give the client a HIDL object to exclusively hold
-// and use it's destructor to perform some work in the server side.
-
-
EvsDisplay::EvsDisplay() {
ALOGD("EvsDisplay instantiated");
@@ -43,34 +37,55 @@
// NOTE: These are arbitrary values chosen for testing
mInfo.displayId = "Mock Display";
mInfo.vendorFlags = 3870;
- mInfo.defaultHorResolution = 320;
- mInfo.defaultVerResolution = 240;
+
+ // Assemble the buffer description we'll use for our render target
+ mBuffer.width = 320;
+ mBuffer.height = 240;
+ mBuffer.format = HAL_PIXEL_FORMAT_RGBA_8888;
+ mBuffer.usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER;
+ mBuffer.bufferId = 0x3870; // Arbitrary magic number for self recognition
+ mBuffer.pixelSize = 4;
}
EvsDisplay::~EvsDisplay() {
ALOGD("EvsDisplay being destroyed");
+ forceShutdown();
+}
+
+
+/**
+ * This gets called if another caller "steals" ownership of the display
+ */
+void EvsDisplay::forceShutdown()
+{
+ ALOGD("EvsDisplay forceShutdown");
std::lock_guard<std::mutex> lock(mAccessLock);
- // Report if we're going away while a buffer is outstanding
- if (mFrameBusy) {
- ALOGE("EvsDisplay going down while client is holding a buffer");
- }
-
- // Make sure we release our frame buffer
+ // If the buffer isn't being held by a remote client, release it now as an
+ // optimization to release the resources more quickly than the destructor might
+ // get called.
if (mBuffer.memHandle) {
+ // Report if we're going away while a buffer is outstanding
+ if (mFrameBusy) {
+ ALOGE("EvsDisplay going down while client is holding a buffer");
+ }
+
// Drop the graphics buffer we've been using
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
alloc.free(mBuffer.memHandle);
mBuffer.memHandle = nullptr;
}
- ALOGD("EvsDisplay destroyed");
+
+ // Put this object into an unrecoverable error state since somebody else
+ // is going to own the display now.
+ mRequestedState = DisplayState::DEAD;
}
/**
* Returns basic information about the EVS display provided by the system.
- * See the description of the DisplayDesc structure below for details.
+ * See the description of the DisplayDesc structure for details.
*/
Return<void> EvsDisplay::getDisplayInfo(getDisplayInfo_cb _hidl_cb) {
ALOGD("getDisplayInfo");
@@ -94,6 +109,11 @@
ALOGD("setDisplayState");
std::lock_guard<std::mutex> lock(mAccessLock);
+ if (mRequestedState == DisplayState::DEAD) {
+ // This object no longer owns the display -- it's been superceeded!
+ return EvsResult::OWNERSHIP_LOST;
+ }
+
// Ensure we recognize the requested state so we don't go off the rails
if (state < DisplayState::NUM_STATES) {
// Record the requested state
@@ -119,10 +139,7 @@
ALOGD("getDisplayState");
std::lock_guard<std::mutex> lock(mAccessLock);
- // At the moment, we treat the requested state as immediately active
- DisplayState currentState = mRequestedState;
-
- return currentState;
+ return mRequestedState;
}
@@ -137,15 +154,16 @@
ALOGD("getTargetBuffer");
std::lock_guard<std::mutex> lock(mAccessLock);
+ if (mRequestedState == DisplayState::DEAD) {
+ ALOGE("Rejecting buffer request from object that lost ownership of the display.");
+ BufferDesc nullBuff = {};
+ _hidl_cb(nullBuff);
+ return Void();
+ }
+
// If we don't already have a buffer, allocate one now
if (!mBuffer.memHandle) {
- // Assemble the buffer description we'll use for our render target
- mBuffer.width = mInfo.defaultHorResolution;
- mBuffer.height = mInfo.defaultVerResolution;
- mBuffer.format = HAL_PIXEL_FORMAT_RGBA_8888;
- mBuffer.usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER;
- mBuffer.bufferId = 0x3870; // Arbitrary magic number for self recognition
-
+ // Allocate the buffer that will hold our displayable image
buffer_handle_t handle = nullptr;
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
status_t result = alloc.allocate(mBuffer.width, mBuffer.height,
@@ -220,6 +238,11 @@
mFrameBusy = false;
+ // If we've been displaced by another owner of the display, then we can't do anything else
+ if (mRequestedState == DisplayState::DEAD) {
+ return EvsResult::OWNERSHIP_LOST;
+ }
+
// If we were waiting for a new frame, this is it!
if (mRequestedState == DisplayState::VISIBLE_ON_NEXT_FRAME) {
mRequestedState = DisplayState::VISIBLE;
@@ -248,8 +271,8 @@
// Check the test pixels
bool frameLooksGood = true;
- for (unsigned row = 0; row < mInfo.defaultVerResolution; row++) {
- for (unsigned col = 0; col < mInfo.defaultHorResolution; col++) {
+ for (unsigned row = 0; row < mBuffer.height; row++) {
+ for (unsigned col = 0; col < mBuffer.width; col++) {
// Index into the row to check the pixel at this column.
// We expect 0xFF in the LSB channel, a vertical gradient in the
// second channel, a horitzontal gradient in the third channel, and
diff --git a/automotive/evs/1.0/default/EvsDisplay.h b/automotive/evs/1.0/default/EvsDisplay.h
index fcf4a06..ebd6446 100644
--- a/automotive/evs/1.0/default/EvsDisplay.h
+++ b/automotive/evs/1.0/default/EvsDisplay.h
@@ -27,6 +27,7 @@
namespace V1_0 {
namespace implementation {
+
class EvsDisplay : public IEvsDisplay {
public:
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsDisplay follow.
@@ -40,6 +41,8 @@
EvsDisplay();
virtual ~EvsDisplay() override;
+ void forceShutdown(); // This gets called if another caller "steals" ownership of the display
+
private:
DisplayDesc mInfo = {};
BufferDesc mBuffer = {}; // A graphics buffer into which we'll store images
diff --git a/automotive/evs/1.0/default/EvsEnumerator.cpp b/automotive/evs/1.0/default/EvsEnumerator.cpp
index e54f699..731e21b 100644
--- a/automotive/evs/1.0/default/EvsEnumerator.cpp
+++ b/automotive/evs/1.0/default/EvsEnumerator.cpp
@@ -28,33 +28,36 @@
namespace implementation {
-// TODO(b/31632518): Need to get notification when our client dies so we can close the camera.
-// As it stands, if the client dies suddenly, the camera will be stuck "open".
-// NOTE: Display should already be safe by virtue of holding only a weak pointer.
+// NOTE: All members values are static so that all clients operate on the same state
+// That is to say, this is effectively a singleton despite the fact that HIDL
+// constructs a new instance for each client.
+std::list<EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
+wp<EvsDisplay> EvsEnumerator::sActiveDisplay;
EvsEnumerator::EvsEnumerator() {
ALOGD("EvsEnumerator created");
// Add sample camera data to our list of cameras
- // NOTE: The id strings trigger special initialization inside the EvsCamera constructor
- mCameraList.emplace_back( new EvsCamera(EvsCamera::kCameraName_Backup), false );
- mCameraList.emplace_back( new EvsCamera("LaneView"), false );
- mCameraList.emplace_back( new EvsCamera(EvsCamera::kCameraName_RightTurn), false );
+ // In a real driver, this would be expected to can the available hardware
+ sCameraList.emplace_back(EvsCamera::kCameraName_Backup);
+ sCameraList.emplace_back("LaneView");
+ sCameraList.emplace_back("right turn");
}
+
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
Return<void> EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb) {
ALOGD("getCameraList");
- const unsigned numCameras = mCameraList.size();
+ const unsigned numCameras = sCameraList.size();
// Build up a packed array of CameraDesc for return
// NOTE: Only has to live until the callback returns
std::vector<CameraDesc> descriptions;
descriptions.reserve(numCameras);
- for (const auto& cam : mCameraList) {
- descriptions.push_back( cam.pCamera->getDesc() );
+ for (const auto& cam : sCameraList) {
+ descriptions.push_back( cam.desc );
}
// Encapsulate our camera descriptions in the HIDL vec type
@@ -68,97 +71,137 @@
return Void();
}
+
Return<sp<IEvsCamera>> EvsEnumerator::openCamera(const hidl_string& cameraId) {
ALOGD("openCamera");
// Find the named camera
CameraRecord *pRecord = nullptr;
- for (auto &&cam : mCameraList) {
- if (cam.pCamera->getDesc().cameraId == cameraId) {
+ for (auto &&cam : sCameraList) {
+ if (cam.desc.cameraId == cameraId) {
// Found a match!
pRecord = &cam;
break;
}
}
+ // Is this a recognized camera id?
if (!pRecord) {
ALOGE("Requested camera %s not found", cameraId.c_str());
return nullptr;
- } else if (pRecord->inUse) {
- ALOGE("Cannot open camera %s which is already in use", cameraId.c_str());
- return nullptr;
- } else {
- pRecord->inUse = true;
- return(pRecord->pCamera);
}
+
+ // Has this camera already been instantiated by another caller?
+ sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote();
+ if (pActiveCamera != nullptr) {
+ ALOGW("Killing previous camera because of new caller");
+ closeCamera(pActiveCamera);
+ }
+
+ // Construct a camera instance for the caller
+ pActiveCamera = new EvsCamera(cameraId);
+ pRecord->activeInstance = pActiveCamera;
+ if (pActiveCamera == nullptr) {
+ ALOGE("Failed to allocate new EvsCamera object for %s\n", cameraId.c_str());
+ }
+
+ return pActiveCamera;
}
-Return<void> EvsEnumerator::closeCamera(const ::android::sp<IEvsCamera>& camera) {
+
+Return<void> EvsEnumerator::closeCamera(const ::android::sp<IEvsCamera>& pCamera) {
ALOGD("closeCamera");
- if (camera == nullptr) {
- ALOGE("Ignoring call to closeCamera with null camera pointer");
- } else {
- // Find this camera in our list
- auto it = std::find_if(mCameraList.begin(),
- mCameraList.end(),
- [camera](const CameraRecord& rec) {
- return (rec.pCamera == camera);
- });
- if (it == mCameraList.end()) {
- ALOGE("Ignoring close on unrecognized camera");
- } else {
- // Make sure the camera has stopped streaming
- camera->stopVideoStream();
+ if (pCamera == nullptr) {
+ ALOGE("Ignoring call to closeCamera with null camera ptr");
+ return Void();
+ }
- it->inUse = false;
+ // Get the camera id so we can find it in our list
+ std::string cameraId;
+ pCamera->getCameraInfo([&cameraId](CameraDesc desc) {
+// TODO(b/36532780) Should we able to just use a simple assignment?
+// cameraId = desc.cameraId;
+ cameraId.assign(desc.cameraId.c_str());
+ }
+ );
+
+ // Find the named camera
+ CameraRecord *pRecord = nullptr;
+ for (auto &&cam : sCameraList) {
+ if (cam.desc.cameraId == cameraId) {
+ // Found a match!
+ pRecord = &cam;
+ break;
+ }
+ }
+
+ // Is the display being destroyed actually the one we think is active?
+ if (!pRecord) {
+ ALOGE("Asked to close a camera who's name isn't recognized");
+ } else {
+ sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote();
+
+ if (pActiveCamera == nullptr) {
+ ALOGE("Somehow a camera is being destroyed when the enumerator didn't know one existed");
+ } else if (pActiveCamera != pCamera) {
+ // This can happen if the camera was aggressively reopened, orphaning this previous instance
+ ALOGW("Ignoring close of previously orphaned camera - why did a client steal?");
+ } else {
+ // Drop the active camera
+ pActiveCamera->forceShutdown();
+ pRecord->activeInstance = nullptr;
}
}
return Void();
}
+
Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay() {
ALOGD("openDisplay");
- // If we already have a display active, then this request must be denied
- sp<IEvsDisplay> pActiveDisplay = mActiveDisplay.promote();
+ // If we already have a display active, then we need to shut it down so we can
+ // give exclusive access to the new caller.
+ sp<EvsDisplay> pActiveDisplay = sActiveDisplay.promote();
if (pActiveDisplay != nullptr) {
- ALOGW("Rejecting openDisplay request the display is already in use.");
- return nullptr;
- } else {
- // Create a new display interface and return it
- pActiveDisplay = new EvsDisplay();
- mActiveDisplay = pActiveDisplay;
- ALOGD("Returning new EvsDisplay object %p", pActiveDisplay.get());
- return pActiveDisplay;
+ ALOGW("Killing previous display because of new caller");
+ closeDisplay(pActiveDisplay);
}
+
+ // Create a new display interface and return it
+ pActiveDisplay = new EvsDisplay();
+ sActiveDisplay = pActiveDisplay;
+
+ ALOGD("Returning new EvsDisplay object %p", pActiveDisplay.get());
+ return pActiveDisplay;
}
-Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay>& display) {
+
+Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay>& pDisplay) {
ALOGD("closeDisplay");
// Do we still have a display object we think should be active?
- sp<IEvsDisplay> pActiveDisplay = mActiveDisplay.promote();
-
+ sp<EvsDisplay> pActiveDisplay = sActiveDisplay.promote();
if (pActiveDisplay == nullptr) {
- ALOGE("Ignoring closeDisplay when there is no active display.");
- } else if (display != pActiveDisplay) {
- ALOGE("Ignoring closeDisplay on a display we didn't issue");
- ALOGI("Got %p while active display is %p.", display.get(), pActiveDisplay.get());
+ ALOGE("Somehow a display is being destroyed when the enumerator didn't know one existed");
+ } else if (sActiveDisplay != pDisplay) {
+ ALOGW("Ignoring close of previously orphaned display - why did a client steal?");
} else {
// Drop the active display
- mActiveDisplay = nullptr;
+ pActiveDisplay->forceShutdown();
+ sActiveDisplay = nullptr;
}
return Void();
}
+
Return<DisplayState> EvsEnumerator::getDisplayState() {
ALOGD("getDisplayState");
// Do we still have a display object we think should be active?
- sp<IEvsDisplay> pActiveDisplay = mActiveDisplay.promote();
+ sp<IEvsDisplay> pActiveDisplay = sActiveDisplay.promote();
if (pActiveDisplay != nullptr) {
return pActiveDisplay->getDisplayState();
} else {
diff --git a/automotive/evs/1.0/default/EvsEnumerator.h b/automotive/evs/1.0/default/EvsEnumerator.h
index 3d6e264..6b70f9b 100644
--- a/automotive/evs/1.0/default/EvsEnumerator.h
+++ b/automotive/evs/1.0/default/EvsEnumerator.h
@@ -22,7 +22,6 @@
#include <list>
-#include "EvsCamera.h"
namespace android {
namespace hardware {
@@ -31,6 +30,11 @@
namespace V1_0 {
namespace implementation {
+
+class EvsCamera; // from EvsCamera.h
+class EvsDisplay; // from EvsDisplay.h
+
+
class EvsEnumerator : public IEvsEnumerator {
public:
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
@@ -45,14 +49,18 @@
EvsEnumerator();
private:
+ // NOTE: All members values are static so that all clients operate on the same state
+ // That is to say, this is effectively a singleton despite the fact that HIDL
+ // constructs a new instance for each client.
struct CameraRecord {
- sp<EvsCamera> pCamera;
- bool inUse;
- CameraRecord(EvsCamera* p, bool b) : pCamera(p), inUse(b) {}
- };
- std::list<CameraRecord> mCameraList;
+ CameraDesc desc;
+ wp<EvsCamera> activeInstance;
- wp<IEvsDisplay> mActiveDisplay; // Weak pointer -> object destructs if client dies
+ CameraRecord(const char *cameraId) : desc() { desc.cameraId = cameraId; }
+ };
+ static std::list<CameraRecord> sCameraList;
+
+ static wp<EvsDisplay> sActiveDisplay; // Weak pointer. Object destructs if client dies.
};
} // namespace implementation
diff --git a/automotive/evs/1.0/types.hal b/automotive/evs/1.0/types.hal
index 0ce39d1..7cebf6d 100644
--- a/automotive/evs/1.0/types.hal
+++ b/automotive/evs/1.0/types.hal
@@ -18,40 +18,14 @@
/**
- * Bit flags indicating suggested uses for a given EVS camera
- *
- * The values in the UsageHint bit field provide a generic expression of how a
- * given camera is intended to be used. The values for these flags support
- * existing use cases, and are used by the default EVS application to select
- * appropriate cameras for display based on observed vehicle state (such as
- * turn signal activation or selection of reverse gear). When implementing
- * their own specialized EVS Applications, OEMs are free to use these flags
- * and/or the opaque vendor_flags to drive their own vehicle specific logic.
- */
-enum UsageHint : uint32_t {
- USAGE_HINT_REVERSE = 0x00000001,
- USAGE_HINT_LEFT_TURN = 0x00000002,
- USAGE_HINT_RIGHT_TURN = 0x00000004,
-};
-
-
-/**
* Structure describing the basic properties of an EVS camera
*
* The HAL is responsible for filling out this structure for each
- * EVS camera in the system. Attention should be given to the field
- * of view, direction of view, and location parameters as these may
- * be used to (if available) to project overlay graphics into the
- * scene by an EVS application.
- * Any of these values for which the HAL does not have reasonable values
- * should be set to ZERO.
+ * EVS camera in the system.
*/
struct CameraDesc {
- string cameraId;
- bitfield<UsageHint> hints; // Mask of usage hints
- uint32_t vendorFlags; // Opaque value from driver
- uint32_t defaultHorResolution; // Units of pixels
- uint32_t defaultVerResolution; // Units of pixels
+ string cameraId;
+ uint32_t vendorFlags; // Opaque value from driver
};
@@ -65,9 +39,7 @@
*/
struct DisplayDesc {
string displayId;
- uint32_t vendorFlags; // Opaque value from driver
- uint32_t defaultHorResolution; // Units of pixels
- uint32_t defaultVerResolution; // Units of pixels
+ uint32_t vendorFlags; // Opaque value from driver
};
@@ -86,7 +58,8 @@
struct BufferDesc {
uint32_t width; // Units of pixels
uint32_t height; // Units of pixels
- uint32_t stride; // Units of bytes
+ uint32_t stride; // Units of pixels to match gralloc
+ uint32_t pixelSize; // Units of bytes
uint32_t format; // May contain values from android_pixel_format_t
uint32_t usage; // May contain values from from Gralloc.h
uint32_t bufferId; // Opaque value from driver
@@ -108,6 +81,7 @@
NOT_VISIBLE, // Display is inhibited
VISIBLE_ON_NEXT_FRAME, // Will become visible with next frame
VISIBLE, // Display is currently active
+ DEAD, // Driver is in an undefined state. Interface should be closed.
NUM_STATES // Must be last
};
@@ -118,5 +92,6 @@
INVALID_ARG,
STREAM_ALREADY_RUNNING,
BUFFER_NOT_AVAILABLE,
+ OWNERSHIP_LOST,
UNDERLYING_SERVICE_ERROR,
};
diff --git a/drm/1.0/default/DrmPlugin.cpp b/drm/1.0/default/DrmPlugin.cpp
index 6f51e0e..c7428a5 100644
--- a/drm/1.0/default/DrmPlugin.cpp
+++ b/drm/1.0/default/DrmPlugin.cpp
@@ -77,7 +77,7 @@
android::DrmPlugin::KeyRequestType legacyRequestType =
android::DrmPlugin::kKeyRequestType_Unknown;
- status_t status = mLegacyPlugin->getKeyRequest(toVector(scope),
+ status = mLegacyPlugin->getKeyRequest(toVector(scope),
toVector(initData), String8(mimeType), legacyKeyType,
legacyOptionalParameters, legacyRequest, defaultUrl,
&legacyRequestType);
@@ -93,7 +93,7 @@
requestType = KeyRequestType::RELEASE;
break;
case android::DrmPlugin::kKeyRequestType_Unknown:
- status = android::BAD_VALUE;
+ requestType = KeyRequestType::UNKNOWN;
break;
}
}
diff --git a/drm/1.0/vts/doc/Drm_Vendor_Modules_v1.pdf b/drm/1.0/vts/doc/Drm_Vendor_Modules_v1.pdf
new file mode 100644
index 0000000..1b44e4f
--- /dev/null
+++ b/drm/1.0/vts/doc/Drm_Vendor_Modules_v1.pdf
Binary files differ
diff --git a/drm/1.0/vts/functional/Android.bp b/drm/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..546aa12
--- /dev/null
+++ b/drm/1.0/vts/functional/Android.bp
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2017 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.
+//
+
+cc_test {
+ name: "drm_hidl_test",
+ srcs: [
+ "drm_hal_clearkey_test.cpp",
+ "drm_hal_vendor_test.cpp",
+ "shared_library.cpp",
+ "vendor_modules.cpp"
+ ],
+ shared_libs: [
+ "android.hardware.drm@1.0",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libbase",
+ "libcutils",
+ "libhidlbase",
+ "libhidlmemory",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libnativehelper",
+ "libutils",
+ ],
+ static_libs: [
+ "VtsHalHidlTargetTestBase"
+ ],
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+}
diff --git a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
new file mode 100644
index 0000000..2296d2d
--- /dev/null
+++ b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
@@ -0,0 +1,904 @@
+/*
+ * Copyright (C) 2017 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 "drm_hal_clearkey_test@1.0"
+
+#include <android-base/logging.h>
+#include <android/hardware/drm/1.0/ICryptoFactory.h>
+#include <android/hardware/drm/1.0/ICryptoPlugin.h>
+#include <android/hardware/drm/1.0/IDrmFactory.h>
+#include <android/hardware/drm/1.0/IDrmPlugin.h>
+#include <android/hardware/drm/1.0/types.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidlmemory/mapping.h>
+#include <memory>
+#include <random>
+
+#include "VtsHalHidlTargetTestBase.h"
+
+using ::android::hardware::drm::V1_0::BufferType;
+using ::android::hardware::drm::V1_0::DestinationBuffer;
+using ::android::hardware::drm::V1_0::ICryptoFactory;
+using ::android::hardware::drm::V1_0::ICryptoPlugin;
+using ::android::hardware::drm::V1_0::IDrmFactory;
+using ::android::hardware::drm::V1_0::IDrmPlugin;
+using ::android::hardware::drm::V1_0::KeyedVector;
+using ::android::hardware::drm::V1_0::KeyValue;
+using ::android::hardware::drm::V1_0::KeyRequestType;
+using ::android::hardware::drm::V1_0::KeyType;
+using ::android::hardware::drm::V1_0::Mode;
+using ::android::hardware::drm::V1_0::Pattern;
+using ::android::hardware::drm::V1_0::SecureStop;
+using ::android::hardware::drm::V1_0::SecureStopId;
+using ::android::hardware::drm::V1_0::SessionId;
+using ::android::hardware::drm::V1_0::SharedBuffer;
+using ::android::hardware::drm::V1_0::Status;
+using ::android::hardware::drm::V1_0::SubSample;
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::sp;
+
+using std::string;
+using std::unique_ptr;
+using std::random_device;
+using std::map;
+using std::mt19937;
+using std::vector;
+
+/**
+ * These clearkey tests use white box knowledge of the legacy clearkey
+ * plugin to verify that the HIDL HAL services and interfaces are working.
+ * It is not intended to verify any vendor's HAL implementation. If you
+ * are looking for vendor HAL tests, see drm_hal_vendor_test.cpp
+ */
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+static const uint8_t kClearKeyUUID[16] = {
+ 0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02,
+ 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B};
+
+static const uint8_t kInvalidUUID[16] = {
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
+
+class DrmHalClearkeyFactoryTest : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+ virtual void SetUp() override {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("Running test %s.%s", test_info->test_case_name(),
+ test_info->name());
+
+ drmFactory =
+ ::testing::VtsHalHidlTargetTestBase::getService<IDrmFactory>(
+ "drm");
+ ASSERT_NE(drmFactory, nullptr);
+ cryptoFactory =
+ ::testing::VtsHalHidlTargetTestBase::getService<ICryptoFactory>(
+ "crypto");
+ ASSERT_NE(cryptoFactory, nullptr);
+ }
+
+ virtual void TearDown() override {}
+
+ protected:
+ sp<IDrmFactory> drmFactory;
+ sp<ICryptoFactory> cryptoFactory;
+};
+
+/**
+ * Ensure the factory supports the clearkey scheme UUID
+ */
+TEST_F(DrmHalClearkeyFactoryTest, ClearKeyPluginSupported) {
+ EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(kClearKeyUUID));
+ EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(kClearKeyUUID));
+}
+
+/**
+ * Ensure the factory doesn't support an invalid scheme UUID
+ */
+TEST_F(DrmHalClearkeyFactoryTest, InvalidPluginNotSupported) {
+ EXPECT_FALSE(drmFactory->isCryptoSchemeSupported(kInvalidUUID));
+ EXPECT_FALSE(cryptoFactory->isCryptoSchemeSupported(kInvalidUUID));
+}
+
+/**
+ * Ensure clearkey drm plugin can be created
+ */
+TEST_F(DrmHalClearkeyFactoryTest, CreateClearKeyDrmPlugin) {
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ kClearKeyUUID, packageName,
+ [&](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Ensure clearkey crypto plugin can be created
+ */
+TEST_F(DrmHalClearkeyFactoryTest, CreateClearKeyCryptoPlugin) {
+ hidl_vec<uint8_t> initVec;
+ auto res = cryptoFactory->createPlugin(
+ kClearKeyUUID, initVec,
+ [&](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Ensure invalid drm plugin can't be created
+ */
+TEST_F(DrmHalClearkeyFactoryTest, CreateInvalidDrmPlugin) {
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ kInvalidUUID, packageName,
+ [&](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ EXPECT_EQ(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Ensure invalid crypto plugin can't be created
+ */
+TEST_F(DrmHalClearkeyFactoryTest, CreateInvalidCryptoPlugin) {
+ hidl_vec<uint8_t> initVec;
+ auto res = cryptoFactory->createPlugin(
+ kInvalidUUID, initVec,
+ [&](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ EXPECT_EQ(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+class DrmHalClearkeyPluginTest : public DrmHalClearkeyFactoryTest {
+ public:
+ virtual void SetUp() override {
+ // Create factories
+ DrmHalClearkeyFactoryTest::SetUp();
+
+ ASSERT_NE(drmFactory, nullptr);
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ kClearKeyUUID, packageName,
+ [this](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ ASSERT_NE(plugin, nullptr);
+ drmPlugin = plugin;
+ });
+ ASSERT_OK(res);
+
+ hidl_vec<uint8_t> initVec;
+ res = cryptoFactory->createPlugin(
+ kClearKeyUUID, initVec,
+ [this](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ ASSERT_NE(plugin, nullptr);
+ cryptoPlugin = plugin;
+ });
+ ASSERT_OK(res);
+ }
+
+ virtual void TearDown() override {}
+
+ SessionId openSession();
+ void closeSession(const SessionId& sessionId);
+ sp<IMemory> getDecryptMemory(size_t size, size_t index);
+
+ protected:
+ sp<IDrmPlugin> drmPlugin;
+ sp<ICryptoPlugin> cryptoPlugin;
+};
+
+/**
+ * DrmPlugin tests
+ */
+
+/**
+ * Test that the plugin can return a provision request. Since
+ * the clearkey plugin doesn't support provisioning, it is
+ * expected to return Status::ERROR_DRM_CANNOT_HANDLE.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetProvisionRequest) {
+ hidl_string certificateType;
+ hidl_string certificateAuthority;
+ auto res = drmPlugin->getProvisionRequest(
+ certificateType, certificateAuthority,
+ [&](Status status, const hidl_vec<uint8_t>&, const hidl_string&) {
+ // clearkey doesn't require provisioning
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * The DRM HAL should return BAD_VALUE if an empty provisioning
+ * response is provided.
+ */
+TEST_F(DrmHalClearkeyPluginTest, ProvideEmptyProvisionResponse) {
+ hidl_vec<uint8_t> response;
+ auto res = drmPlugin->provideProvisionResponse(
+ response, [&](Status status, const hidl_vec<uint8_t>&,
+ const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Helper method to open a session and verify that a non-empty
+ * session ID is returned
+ */
+SessionId DrmHalClearkeyPluginTest::openSession() {
+ SessionId sessionId;
+
+ auto res = drmPlugin->openSession(
+ [&sessionId](Status status, const SessionId& id) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(0u, id.size());
+ sessionId = id;
+ });
+ EXPECT_OK(res);
+ return sessionId;
+}
+
+/**
+ * Helper method to close a session
+ */
+void DrmHalClearkeyPluginTest::closeSession(const SessionId& sessionId) {
+ auto result = drmPlugin->closeSession(sessionId);
+ EXPECT_EQ(Status::OK, result);
+}
+
+/**
+ * Test that a session can be opened and closed
+ */
+TEST_F(DrmHalClearkeyPluginTest, OpenCloseSession) {
+ auto sessionId = openSession();
+ closeSession(sessionId);
+}
+
+/**
+ * Test that attempting to close an invalid (empty) sessionId
+ * is prohibited with the documented error code.
+ */
+TEST_F(DrmHalClearkeyPluginTest, CloseInvalidSession) {
+ SessionId invalidSessionId;
+ Status result = drmPlugin->closeSession(invalidSessionId);
+ EXPECT_EQ(Status::BAD_VALUE, result);
+}
+
+/**
+ * Test that attempting to close a session that is already closed
+ * is prohibited with the documented error code.
+ */
+TEST_F(DrmHalClearkeyPluginTest, CloseClosedSession) {
+ SessionId sessionId = openSession();
+ closeSession(sessionId);
+ Status result = drmPlugin->closeSession(sessionId);
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, result);
+}
+
+/**
+ * A get key request should fail if no sessionId is provided
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetKeyRequestNoSession) {
+ SessionId invalidSessionId;
+ hidl_vec<uint8_t> initData;
+ hidl_string mimeType = "video/mp4";
+ KeyedVector optionalParameters;
+ auto res = drmPlugin->getKeyRequest(
+ invalidSessionId, initData, mimeType, KeyType::STREAMING,
+ optionalParameters,
+ [&](Status status, const hidl_vec<uint8_t>&, KeyRequestType,
+ const hidl_string&) { EXPECT_EQ(Status::BAD_VALUE, status); });
+ EXPECT_OK(res);
+}
+
+/**
+ * The clearkey plugin doesn't support offline key requests.
+ * Test that the plugin returns the expected error code in
+ * this case.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetKeyRequestOfflineKeyTypeNotSupported) {
+ auto sessionId = openSession();
+ hidl_vec<uint8_t> initData;
+ hidl_string mimeType = "video/mp4";
+ KeyedVector optionalParameters;
+
+ auto res = drmPlugin->getKeyRequest(
+ sessionId, initData, mimeType, KeyType::OFFLINE, optionalParameters,
+ [&](Status status, const hidl_vec<uint8_t>&, KeyRequestType,
+ const hidl_string&) {
+ // Clearkey plugin doesn't support offline key type
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+ closeSession(sessionId);
+}
+
+/**
+ * Test that the plugin returns the documented error for the
+ * case of attempting to generate a key request using an
+ * invalid mime type
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetKeyRequestBadMime) {
+ auto sessionId = openSession();
+ hidl_vec<uint8_t> initData;
+ hidl_string mimeType = "video/unknown";
+ KeyedVector optionalParameters;
+ auto res = drmPlugin->getKeyRequest(
+ sessionId, initData, mimeType, KeyType::STREAMING,
+ optionalParameters, [&](Status status, const hidl_vec<uint8_t>&,
+ KeyRequestType, const hidl_string&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+ closeSession(sessionId);
+}
+
+/**
+ * Test that a closed sessionID returns SESSION_NOT_OPENED
+ */
+TEST_F(DrmHalClearkeyPluginTest, ProvideKeyResponseClosedSession) {
+ SessionId session = openSession();
+ closeSession(session);
+
+ hidl_vec<uint8_t> keyResponse = {0x7b, 0x22, 0x6b, 0x65,
+ 0x79, 0x73, 0x22, 0x3a};
+ auto res = drmPlugin->provideKeyResponse(
+ session, keyResponse,
+ [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+ EXPECT_EQ(0u, keySetId.size());
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that an empty sessionID returns BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, ProvideKeyResponseInvalidSessionId) {
+ SessionId session;
+
+ hidl_vec<uint8_t> keyResponse = {0x7b, 0x22, 0x6b, 0x65,
+ 0x79, 0x73, 0x22, 0x3a};
+ auto res = drmPlugin->provideKeyResponse(
+ session, keyResponse,
+ [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ EXPECT_EQ(0u, keySetId.size());
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that an empty key response returns BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, ProvideKeyResponseEmptyResponse) {
+ SessionId session = openSession();
+ hidl_vec<uint8_t> emptyResponse;
+ auto res = drmPlugin->provideKeyResponse(
+ session, emptyResponse,
+ [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ EXPECT_EQ(0u, keySetId.size());
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+/**
+ * Test that the clearkey plugin doesn't support getting
+ * secure stops.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetSecureStops) {
+ auto res = drmPlugin->getSecureStops(
+ [&](Status status, const hidl_vec<SecureStop>&) {
+ // Clearkey plugin doesn't support secure stops
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that the clearkey plugin returns BAD_VALUE if
+ * an empty ssid is provided.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetSecureStopEmptySSID) {
+ SecureStopId ssid;
+ auto res = drmPlugin->getSecureStop(
+ ssid, [&](Status status, const SecureStop&) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that releasing all secure stops isn't handled by
+ * clearkey.
+ */
+TEST_F(DrmHalClearkeyPluginTest, ReleaseAllSecureStops) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+ drmPlugin->releaseAllSecureStops());
+}
+
+/**
+ * Test that releasing a specific secure stop with an empty
+ * SSID returns BAD_VALUE.
+ */
+TEST_F(DrmHalClearkeyPluginTest, ReleaseSecureStopEmptySSID) {
+ SecureStopId ssid;
+ Status status = drmPlugin->releaseSecureStop(ssid);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * The following four tests verify that the properties
+ * defined in the MediaDrm API are supported by
+ * the plugin.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetVendorProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "vendor", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_EQ("Google", value);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GetVersionProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "version", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_EQ("1.0", value);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GetDescriptionProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "description", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_EQ("ClearKey CDM", value);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GetAlgorithmsProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "algorithms", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_EQ("", value);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that attempting to read invalid string and byte array
+ * properties returns the documented error code.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetInvalidStringProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "invalid", [&](Status status, const hidl_string&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GetByteArrayPropertyNotSupported) {
+ auto res = drmPlugin->getPropertyByteArray(
+ "deviceUniqueId", [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Clearkey doesn't support setting string or byte array properties,
+ * particularly an undefined one.
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetStringPropertyNotSupported) {
+ Status status = drmPlugin->setPropertyString("property", "value");
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, SetByteArrayPropertyNotSupported) {
+ hidl_vec<uint8_t> value;
+ Status status = drmPlugin->setPropertyByteArray("property", value);
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+}
+
+/**
+ * Clearkey doesn't support setting cipher algorithms, verify it
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetCipherAlgorithmNotSupported) {
+ SessionId session = openSession();
+ hidl_string algorithm = "AES/CBC/NoPadding";
+ Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ closeSession(session);
+}
+
+/**
+ * Setting an empty algorithm should return BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetCipherEmptyAlgorithm) {
+ SessionId session = openSession();
+ hidl_string algorithm;
+ Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ closeSession(session);
+}
+
+/**
+ * Setting a cipher algorithm with no session returns BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetCipherAlgorithmNoSession) {
+ SessionId session;
+ hidl_string algorithm = "AES/CBC/NoPadding";
+ Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * Clearkey doesn't support setting mac algorithms, verify it
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMacAlgorithmNotSupported) {
+ SessionId session = openSession();
+ hidl_string algorithm = "HmacSHA256";
+ Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ closeSession(session);
+}
+
+/**
+ * Setting an empty algorithm should return BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMacEmptyAlgorithm) {
+ SessionId session = openSession();
+ hidl_string algorithm;
+ Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ closeSession(session);
+}
+
+/**
+ * Setting a mac algorithm with no session should return BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMacAlgorithmNoSession) {
+ SessionId session;
+ hidl_string algorithm = "HmacSHA256";
+ Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * The Generic* methods provide general purpose crypto operations
+ * that may be used for applications other than DRM. They leverage
+ * the hardware root of trust and secure key distribution mechanisms
+ * of a DRM system to enable app-specific crypto functionality where
+ * the crypto keys are not exposed outside of the trusted execution
+ * environment.
+ *
+ * Clearkey doesn't support generic encrypt/decrypt/sign/verify.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GenericEncryptNotSupported) {
+ SessionId session = openSession();
+ ;
+ hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ hidl_vec<uint8_t> input = {1, 2, 3, 4, 5};
+ hidl_vec<uint8_t> iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ auto res = drmPlugin->encrypt(session, keyId, input, iv,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+ status);
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GenericDecryptNotSupported) {
+ SessionId session = openSession();
+ ;
+ hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ hidl_vec<uint8_t> input = {1, 2, 3, 4, 5};
+ hidl_vec<uint8_t> iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ auto res = drmPlugin->decrypt(session, keyId, input, iv,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+ status);
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GenericSignNotSupported) {
+ SessionId session = openSession();
+ ;
+ hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ hidl_vec<uint8_t> message = {1, 2, 3, 4, 5};
+ auto res = drmPlugin->sign(session, keyId, message,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+ status);
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GenericVerifyNotSupported) {
+ SessionId session = openSession();
+ ;
+ hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ hidl_vec<uint8_t> message = {1, 2, 3, 4, 5};
+ hidl_vec<uint8_t> signature = {0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+ auto res = drmPlugin->verify(
+ session, keyId, message, signature, [&](Status status, bool) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GenericSignRSANotSupported) {
+ SessionId session = openSession();
+ hidl_string algorithm = "RSASSA-PSS-SHA1";
+ hidl_vec<uint8_t> message = {1, 2, 3, 4, 5};
+ hidl_vec<uint8_t> wrappedKey = {0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+ auto res = drmPlugin->signRSA(session, algorithm, message, wrappedKey,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+ status);
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+/**
+ * CryptoPlugin tests
+ */
+
+/**
+ * Clearkey doesn't support secure decoder and is expected to
+ * return false.
+ */
+TEST_F(DrmHalClearkeyPluginTest, RequiresSecureDecoder) {
+ EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent("cenc"));
+}
+
+/**
+ * Verify that requiresSecureDecoderComponent handles empty mimetype
+ */
+TEST_F(DrmHalClearkeyPluginTest, RequiresSecureDecoderEmptyMimeType) {
+ EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent(""));
+}
+
+/**
+ * Exercise the NotifyResolution API. There is no observable result,
+ * just call the method for coverage.
+ */
+TEST_F(DrmHalClearkeyPluginTest, NotifyResolution) {
+ cryptoPlugin->notifyResolution(1920, 1080);
+}
+
+/**
+ * getDecryptMemory allocates memory for decryption, then sets it
+ * as a shared buffer base in the crypto hal. The allocated and
+ * mapped IMemory is returned.
+ *
+ * @param size the size of the memory segment to allocate
+ * @param the index of the memory segment which will be used
+ * to refer to it for decryption.
+ */
+sp<IMemory> DrmHalClearkeyPluginTest::getDecryptMemory(size_t size,
+ size_t index) {
+ sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
+ EXPECT_NE(ashmemAllocator, nullptr);
+
+ hidl_memory hidlMemory;
+ auto res = ashmemAllocator->allocate(
+ size, [&](bool success, const hidl_memory& memory) {
+ EXPECT_EQ(true, success);
+ EXPECT_OK(cryptoPlugin->setSharedBufferBase(memory, index));
+ hidlMemory = memory;
+ });
+ EXPECT_OK(res);
+
+ sp<IMemory> mappedMemory = mapMemory(hidlMemory);
+ EXPECT_OK(cryptoPlugin->setSharedBufferBase(hidlMemory, index));
+ return mappedMemory;
+}
+
+/**
+ * Exercise the setMediaDrmSession method. setMediaDrmSession
+ * is used to associate a drm session with a crypto session.
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMediaDrmSession) {
+ auto sessionId = openSession();
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+ closeSession(sessionId);
+}
+
+/**
+ * setMediaDrmSession with a closed session id
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMediaDrmSessionClosedSession) {
+ auto sessionId = openSession();
+ closeSession(sessionId);
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+}
+
+/**
+ * Decrypt tests
+ */
+
+class DrmHalClearkeyDecryptTest : public DrmHalClearkeyPluginTest {
+ public:
+ void loadKeys(const SessionId& sessionId);
+ void fillRandom(const sp<IMemory>& memory);
+ hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) {
+ EXPECT_EQ(vec.size(), 16u);
+ return hidl_array<uint8_t, 16>(&vec[0]);
+ }
+};
+
+/**
+ * Helper method to load keys for subsequent decrypt tests.
+ * These tests use predetermined key request/response to
+ * avoid requiring a round trip to a license server.
+ */
+void DrmHalClearkeyDecryptTest::loadKeys(const SessionId& sessionId) {
+ hidl_vec<uint8_t> initData = {
+ // BMFF box header (4 bytes size + 'pssh')
+ 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
+ // full box header (version = 1 flags = 0)
+ 0x01, 0x00, 0x00, 0x00,
+ // system id
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c,
+ 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ // number of key ids
+ 0x00, 0x00, 0x00, 0x01,
+ // key id
+ 0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87, 0x7e, 0x57, 0xd0,
+ 0x0d, 0x1e, 0xd0, 0x0d, 0x1e,
+ // size of data, must be zero
+ 0x00, 0x00, 0x00, 0x00};
+
+ hidl_vec<uint8_t> expectedKeyRequest = {
+ 0x7b, 0x22, 0x6b, 0x69, 0x64, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x59,
+ 0x41, 0x59, 0x65, 0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2b,
+ 0x56, 0x39, 0x41, 0x4e, 0x48, 0x74, 0x41, 0x4e, 0x48, 0x67, 0x22,
+ 0x5d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x22, 0x74,
+ 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x22, 0x7d};
+
+ hidl_vec<uint8_t> knownKeyResponse = {
+ 0x7b, 0x22, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22,
+ 0x6b, 0x74, 0x79, 0x22, 0x3a, 0x22, 0x6f, 0x63, 0x74, 0x22, 0x2c,
+ 0x22, 0x6b, 0x69, 0x64, 0x22, 0x3a, 0x22, 0x59, 0x41, 0x59, 0x65,
+ 0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2b, 0x56, 0x39, 0x41,
+ 0x4e, 0x48, 0x74, 0x41, 0x4e, 0x48, 0x67, 0x22, 0x2c, 0x22, 0x6b,
+ 0x22, 0x3a, 0x22, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54, 0x65,
+ 0x73, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x61, 0x73, 0x65, 0x36, 0x34,
+ 0x67, 0x67, 0x67, 0x22, 0x7d, 0x5d, 0x7d, 0x0a};
+
+ hidl_string mimeType = "video/mp4";
+ KeyedVector optionalParameters;
+ auto res = drmPlugin->getKeyRequest(
+ sessionId, initData, mimeType, KeyType::STREAMING,
+ optionalParameters,
+ [&](Status status, const hidl_vec<uint8_t>& request,
+ KeyRequestType requestType, const hidl_string&) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_EQ(KeyRequestType::INITIAL, requestType);
+ EXPECT_EQ(request, expectedKeyRequest);
+ });
+ EXPECT_OK(res);
+
+ res = drmPlugin->provideKeyResponse(
+ sessionId, knownKeyResponse,
+ [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_EQ(0u, keySetId.size());
+ });
+ EXPECT_OK(res);
+}
+
+void DrmHalClearkeyDecryptTest::fillRandom(const sp<IMemory>& memory) {
+ random_device rd;
+ mt19937 rand(rd());
+ for (size_t i = 0; i < memory->getSize() / sizeof(uint32_t); i++) {
+ auto p = static_cast<uint32_t*>(
+ static_cast<void*>(memory->getPointer()));
+ p[i] = rand();
+ }
+}
+
+/**
+ * Positive decrypt test. "Decrypt" a single clear
+ * segment. Verify data matches.
+ */
+TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) {
+ const size_t kSegmentSize = 1024;
+ const size_t kSegmentIndex = 0;
+ const vector<uint8_t> keyId = {0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47,
+ 0x7e, 0x87, 0x7e, 0x57, 0xd0, 0x0d,
+ 0x1e, 0xd0, 0x0d, 0x1e};
+ uint8_t iv[16] = {0};
+
+ sp<IMemory> sharedMemory =
+ getDecryptMemory(kSegmentSize * 2, kSegmentIndex);
+
+ SharedBuffer sourceBuffer = {
+ .bufferId = kSegmentIndex, .offset = 0, .size = kSegmentSize};
+ fillRandom(sharedMemory);
+
+ DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
+ {.bufferId = kSegmentIndex,
+ .offset = kSegmentSize,
+ .size = kSegmentSize},
+ .secureMemory = nullptr};
+
+ Pattern noPattern = {0, 0};
+ vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
+ .numBytesOfEncryptedData = 0}};
+ uint64_t offset = 0;
+
+ auto sessionId = openSession();
+ loadKeys(sessionId);
+
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+
+ const bool kNotSecure = false;
+ auto res = cryptoPlugin->decrypt(
+ kNotSecure, toHidlArray(keyId), iv, Mode::UNENCRYPTED, noPattern,
+ subSamples, sourceBuffer, offset, destBuffer,
+ [&](Status status, uint32_t bytesWritten, string detailedError) {
+ EXPECT_EQ(Status::OK, status) << "Failure in decryption:"
+ << detailedError;
+ EXPECT_EQ(bytesWritten, kSegmentSize);
+ });
+ EXPECT_OK(res);
+
+ uint8_t* base = static_cast<uint8_t*>(
+ static_cast<void*>(sharedMemory->getPointer()));
+
+ EXPECT_EQ(0, memcmp(static_cast<void*>(base),
+ static_cast<void*>(base + kSegmentSize), kSegmentSize))
+ << "decrypt data mismatch";
+ closeSession(sessionId);
+}
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_module_api.h b/drm/1.0/vts/functional/drm_hal_vendor_module_api.h
new file mode 100644
index 0000000..db19719
--- /dev/null
+++ b/drm/1.0/vts/functional/drm_hal_vendor_module_api.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef DRM_HAL_VENDOR_MODULE_API_H
+#define DRM_HAL_VENDOR_MODULE_API_H
+
+#include <stdint.h>
+#include <map>
+#include <string>
+#include <vector>
+
+/**
+ * The DRM and Crypto HALs interact with vendor-provided HAL implementations
+ * that have DRM-specific capabilities. Since the VTS tests cannot contain
+ * DRM-specific functionality, supporting modules are required to enable VTS
+ * to validate HAL implementations in a generic way. If the vendor-specific
+ * VTS module is not provided for a given drm HAL implementation, only very
+ * small subset of functionality can be verified.
+ *
+ * As an example, a DRM HAL implementation interacts with a DRM-specific
+ * license server to obtain licenses for decrypting content. The DRM HAL
+ * implementation generates a key request message, delivers it to the server
+ * and receives a key response message which is then loaded into the HAL. Once
+ * the keys are loaded, the Crypto HAL decryption functionality and performance
+ * and other associated APIs can be tested by the common VTS test suite.
+ *
+ * Vendor-specific VTS modules are shared libraries used by the DRM VTS test.
+ * They provide a set of functions to support VTS testing of the DRM HAL module.
+ *
+ * The modules are placed in a common location on the file system. The VTS test
+ * scans through all vendor-provided support libraries and runs the VTS test
+ * suite on each library that is found.
+ *
+ * The vendor-specific module exposes an extern “C” vendorModuleFactory()
+ * function that returns a DrmHalVTSVendorModule instance. DrmHalVTSVendorModule
+ * instances are versioned, where each version is represented by subclass of
+ * DrmHalVTSVendorModule that corresponds to the API version. For example, a
+ * vendor-specific module that implements version 1 of the API would return a
+ * DrmHalVTSVendorModule_V1 from the vendorModuleFactory() function.
+ */
+
+class DrmHalVTSVendorModule;
+
+extern "C" {
+/**
+ * The factory method for creating DrmHalVTSVendorModule instances. The returned
+ * instance will be a subclass of DrmHalVTSVendorModule that corresponds to the
+ * supported API version.
+ */
+DrmHalVTSVendorModule* vendorModuleFactory();
+};
+
+class DrmHalVTSVendorModule {
+ public:
+ DrmHalVTSVendorModule() {}
+ virtual ~DrmHalVTSVendorModule() {}
+
+ /**
+ * Return the vendor-specific module API version. The version is an integer
+ * value with initial version 1. The API version indicates which subclass
+ * version DrmHalVTSVendorModule this instance is.
+ */
+ virtual uint32_t getAPIVersion() = 0;
+
+ /**
+ * Return the UUID for the DRM HAL implementation. Protection System
+ * Specific
+ * UUID (see http://dashif.org/identifiers/protection/)
+ */
+ virtual std::vector<uint8_t> getUUID() = 0;
+
+ /**
+ * Return the service name for the DRM HAL implementation. If the hal is a
+ * legacy
+ * drm plugin, i.e. not running as a HIDL service, return the empty string.
+ */
+ virtual std::string getServiceName() = 0;
+
+ private:
+ DrmHalVTSVendorModule(const DrmHalVTSVendorModule&) = delete;
+ void operator=(const DrmHalVTSVendorModule&) = delete;
+};
+
+/**
+ * API Version 1. This is the baseline version that supports a minimal set
+ * of VTS tests.
+ */
+class DrmHalVTSVendorModule_V1 : public DrmHalVTSVendorModule {
+ public:
+ DrmHalVTSVendorModule_V1() {}
+ virtual ~DrmHalVTSVendorModule_V1() {}
+
+ virtual uint32_t getAPIVersion() { return 1; }
+
+ /**
+ * Handle a provisioning request. This function will be called if the HAL
+ * module's getProvisionRequest returns a provision request. The vendor
+ * module should process the provisioning request, either by sending it
+ * to a provisioning server, or generating a mock response. The resulting
+ * provisioning response is returned to the VTS test.
+ *
+ * @param provisioningRequest the provisioning request recieved from
+ * the DRM HAL
+ * @param url the default url the HAL implementation provided with the
+ * provisioning request
+ * @return the generated provisioning response
+ */
+ virtual std::vector<uint8_t> handleProvisioningRequest(
+ const std::vector<uint8_t>& provisioningRequest,
+ const std::string& url) = 0;
+
+ /**
+ * Content configuration specifies content-specific parameters associated
+ * with a key request/response transaction. It allows the VTS test to
+ * request keys and use them to perform decryption.
+ */
+ struct ContentConfiguration {
+ /**
+ * Assign a name for this configuration that will be referred to
+ * in log messages.
+ */
+ const std::string name;
+
+ /**
+ * Server to use when requesting a key response. This url will be
+ * passed as a parameter to the vendor vts module along with the
+ * key request to perform the key request transaction.
+ */
+ const std::string serverUrl;
+
+ /**
+ * Initialization data provided to getKeyRequest, e.g. PSSH for CENC
+ * content
+ */
+ const std::vector<uint8_t> initData;
+
+ /**
+ * Mime type provided to getKeyRequest, e.g. "video/mp4", or "cenc"
+ */
+ const std::string mimeType;
+
+ /**
+ * Optional parameters to be associated with the key request
+ */
+ const std::map<std::string, std::string> optionalParameters;
+
+ /**
+ * The keys that will be available once the keys are loaded
+ */
+ struct Key {
+ /**
+ * Indicate if the key content is configured to require secure
+ * buffers,
+ * where the output buffers are protected and cannot be accessed.
+ * A vendor module should provide some content configurations where
+ * isSecure is false, to allow decrypt result verification tests to
+ * be
+ * run.
+ */
+ bool isSecure;
+
+ /**
+ * A key ID identifies a key to use for decryption
+ */
+ const std::vector<uint8_t> keyId;
+
+ /**
+ * The key value is provided to generate expected values for
+ * validating
+ * decryption. If isSecure is false, no key value is required.
+ */
+ const std::vector<uint8_t> keyValue;
+ };
+ std::vector<Key> keys;
+ };
+
+ /**
+ * Return a list of content configurations that can be exercised by the
+ * VTS test.
+ */
+ virtual std::vector<ContentConfiguration> getContentConfigurations() = 0;
+
+ /**
+ * Handle a key request. This function will be called if the HAL
+ * module's getKeyRequest returns a key request. The vendor
+ * module should process the key request, either by sending it
+ * to a license server, or by generating a mock response. The resulting
+ * key response is returned to the VTS test.
+ *
+ * @param keyRequest the key request recieved from the DRM HAL
+ * @param serverUrl the url of the key server that was supplied
+ * by the ContentConfiguration
+ * @return the generated key response
+ */
+ virtual std::vector<uint8_t> handleKeyRequest(
+ const std::vector<uint8_t>& keyRequest,
+ const std::string& serverUrl) = 0;
+};
+
+#endif // DRM_HAL_VENDOR_MODULE_API_H
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
new file mode 100644
index 0000000..dcfee4e
--- /dev/null
+++ b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
@@ -0,0 +1,980 @@
+/*
+ * Copyright (C) 2017 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 "drm_hal_vendor_test@1.0"
+
+#include <android-base/logging.h>
+#include <android/hardware/drm/1.0/ICryptoFactory.h>
+#include <android/hardware/drm/1.0/ICryptoPlugin.h>
+#include <android/hardware/drm/1.0/IDrmFactory.h>
+#include <android/hardware/drm/1.0/IDrmPlugin.h>
+#include <android/hardware/drm/1.0/types.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <gtest/gtest.h>
+#include <hidlmemory/mapping.h>
+#include <memory>
+#include <random>
+
+#include "VtsHalHidlTargetTestBase.h"
+#include "drm_hal_vendor_module_api.h"
+#include "vendor_modules.h"
+
+using ::android::hardware::drm::V1_0::BufferType;
+using ::android::hardware::drm::V1_0::DestinationBuffer;
+using ::android::hardware::drm::V1_0::ICryptoFactory;
+using ::android::hardware::drm::V1_0::ICryptoPlugin;
+using ::android::hardware::drm::V1_0::IDrmFactory;
+using ::android::hardware::drm::V1_0::IDrmPlugin;
+using ::android::hardware::drm::V1_0::KeyedVector;
+using ::android::hardware::drm::V1_0::KeyValue;
+using ::android::hardware::drm::V1_0::KeyRequestType;
+using ::android::hardware::drm::V1_0::KeyType;
+using ::android::hardware::drm::V1_0::Mode;
+using ::android::hardware::drm::V1_0::Pattern;
+using ::android::hardware::drm::V1_0::SecureStop;
+using ::android::hardware::drm::V1_0::SecureStopId;
+using ::android::hardware::drm::V1_0::SessionId;
+using ::android::hardware::drm::V1_0::SharedBuffer;
+using ::android::hardware::drm::V1_0::Status;
+using ::android::hardware::drm::V1_0::SubSample;
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::sp;
+
+using std::string;
+using std::unique_ptr;
+using std::random_device;
+using std::map;
+using std::mt19937;
+using std::vector;
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+static const uint8_t kInvalidUUID[16] = {
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+};
+
+static drm_vts::VendorModules* gVendorModules = nullptr;
+
+class DrmHalVendorFactoryTest : public testing::TestWithParam<std::string> {
+ public:
+ DrmHalVendorFactoryTest()
+ : vendorModule(gVendorModules ? static_cast<DrmHalVTSVendorModule_V1*>(
+ gVendorModules->getVendorModule(
+ GetParam()))
+ : nullptr) {}
+
+ virtual ~DrmHalVendorFactoryTest() {}
+
+ virtual void SetUp() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("Running test %s.%s from vendor module %s",
+ test_info->test_case_name(), test_info->name(),
+ GetParam().c_str());
+
+ ASSERT_NE(vendorModule, nullptr);
+ string name = vendorModule->getServiceName();
+ drmFactory =
+ ::testing::VtsHalHidlTargetTestBase::getService<IDrmFactory>(
+ name != "default" ? name : "drm");
+ ASSERT_NE(drmFactory, nullptr);
+ cryptoFactory =
+ ::testing::VtsHalHidlTargetTestBase::getService<ICryptoFactory>(
+ name != "default" ? name : "crypto");
+ ASSERT_NE(cryptoFactory, nullptr);
+ }
+
+ virtual void TearDown() override {}
+
+ protected:
+ hidl_array<uint8_t, 16> getVendorUUID() {
+ vector<uint8_t> uuid = vendorModule->getUUID();
+ return hidl_array<uint8_t, 16>(&uuid[0]);
+ }
+
+ sp<IDrmFactory> drmFactory;
+ sp<ICryptoFactory> cryptoFactory;
+ unique_ptr<DrmHalVTSVendorModule_V1> vendorModule;
+};
+
+/**
+ * Ensure the factory supports its scheme UUID
+ */
+TEST_P(DrmHalVendorFactoryTest, VendorPluginSupported) {
+ EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(getVendorUUID()));
+ EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(getVendorUUID()));
+}
+
+/**
+ * Ensure the factory doesn't support an invalid scheme UUID
+ */
+TEST_P(DrmHalVendorFactoryTest, InvalidPluginNotSupported) {
+ EXPECT_FALSE(drmFactory->isCryptoSchemeSupported(kInvalidUUID));
+ EXPECT_FALSE(cryptoFactory->isCryptoSchemeSupported(kInvalidUUID));
+}
+
+/**
+ * Ensure vendor drm plugin can be created
+ */
+TEST_P(DrmHalVendorFactoryTest, CreateVendorDrmPlugin) {
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ getVendorUUID(), packageName,
+ [&](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Ensure vendor crypto plugin can be created
+ */
+TEST_P(DrmHalVendorFactoryTest, CreateVendorCryptoPlugin) {
+ hidl_vec<uint8_t> initVec;
+ auto res = cryptoFactory->createPlugin(
+ getVendorUUID(), initVec,
+ [&](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Ensure invalid drm plugin can't be created
+ */
+TEST_P(DrmHalVendorFactoryTest, CreateInvalidDrmPlugin) {
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ kInvalidUUID, packageName,
+ [&](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ EXPECT_EQ(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Ensure invalid crypto plugin can't be created
+ */
+TEST_P(DrmHalVendorFactoryTest, CreateInvalidCryptoPlugin) {
+ hidl_vec<uint8_t> initVec;
+ auto res = cryptoFactory->createPlugin(
+ kInvalidUUID, initVec,
+ [&](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ EXPECT_EQ(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+class DrmHalVendorPluginTest : public DrmHalVendorFactoryTest {
+ public:
+ virtual ~DrmHalVendorPluginTest() {}
+ virtual void SetUp() override {
+ // Create factories
+ DrmHalVendorFactoryTest::SetUp();
+
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ getVendorUUID(), packageName,
+ [this](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ ASSERT_NE(plugin, nullptr);
+ drmPlugin = plugin;
+ });
+ ASSERT_OK(res);
+
+ hidl_vec<uint8_t> initVec;
+ res = cryptoFactory->createPlugin(
+ getVendorUUID(), initVec,
+ [this](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ ASSERT_NE(plugin, nullptr);
+ cryptoPlugin = plugin;
+ });
+ ASSERT_OK(res);
+ }
+
+ virtual void TearDown() override {}
+
+ SessionId openSession();
+ void closeSession(const SessionId& sessionId);
+ sp<IMemory> getDecryptMemory(size_t size, size_t index);
+
+ protected:
+ sp<IDrmPlugin> drmPlugin;
+ sp<ICryptoPlugin> cryptoPlugin;
+};
+
+/**
+ * DrmPlugin tests
+ */
+
+/**
+ * Test that a DRM plugin can handle provisioning. While
+ * it is not required that a DRM scheme require provisioning,
+ * it should at least return appropriate status values. If
+ * a provisioning request is returned, it is passed to the
+ * vendor module which should provide a provisioning response
+ * that is delivered back to the HAL.
+ */
+
+TEST_P(DrmHalVendorPluginTest, DoProvisioning) {
+ hidl_string certificateType;
+ hidl_string certificateAuthority;
+ hidl_vec<uint8_t> provisionRequest;
+ hidl_string defaultUrl;
+ auto res = drmPlugin->getProvisionRequest(
+ certificateType, certificateAuthority,
+ [&](Status status, const hidl_vec<uint8_t>& request,
+ const hidl_string& url) {
+ if (status == Status::OK) {
+ EXPECT_NE(request.size(), 0u);
+ provisionRequest = request;
+ defaultUrl = url;
+ } else if (status == Status::ERROR_DRM_CANNOT_HANDLE) {
+ EXPECT_EQ(0u, request.size());
+ }
+ });
+ EXPECT_OK(res);
+
+ if (provisionRequest.size() > 0) {
+ vector<uint8_t> response = vendorModule->handleProvisioningRequest(
+ provisionRequest, defaultUrl);
+ ASSERT_NE(0u, response.size());
+
+ auto res = drmPlugin->provideProvisionResponse(
+ response, [&](Status status, const hidl_vec<uint8_t>&,
+ const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::OK, status);
+ });
+ EXPECT_OK(res);
+ }
+}
+
+/**
+ * The DRM HAL should return BAD_VALUE if an empty provisioning
+ * response is provided.
+ */
+TEST_P(DrmHalVendorPluginTest, ProvideEmptyProvisionResponse) {
+ hidl_vec<uint8_t> response;
+ auto res = drmPlugin->provideProvisionResponse(
+ response, [&](Status status, const hidl_vec<uint8_t>&,
+ const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Helper method to open a session and verify that a non-empty
+ * session ID is returned
+ */
+SessionId DrmHalVendorPluginTest::openSession() {
+ SessionId sessionId;
+
+ auto res = drmPlugin->openSession([&](Status status, const SessionId& id) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(id.size(), 0u);
+ sessionId = id;
+ });
+ EXPECT_OK(res);
+ return sessionId;
+}
+
+/**
+ * Helper method to close a session
+ */
+void DrmHalVendorPluginTest::closeSession(const SessionId& sessionId) {
+ Status status = drmPlugin->closeSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+}
+
+/**
+ * Test that a session can be opened and closed
+ */
+TEST_P(DrmHalVendorPluginTest, OpenCloseSession) {
+ auto sessionId = openSession();
+ closeSession(sessionId);
+}
+
+/**
+ * Test that attempting to close an invalid (empty) sessionId
+ * is prohibited with the documented error code.
+ */
+TEST_P(DrmHalVendorPluginTest, CloseInvalidSession) {
+ SessionId invalidSessionId;
+ Status status = drmPlugin->closeSession(invalidSessionId);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * Test that attempting to close a valid session twice
+ * is prohibited with the documented error code.
+ */
+TEST_P(DrmHalVendorPluginTest, CloseClosedSession) {
+ auto sessionId = openSession();
+ closeSession(sessionId);
+ Status status = drmPlugin->closeSession(sessionId);
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+}
+
+/**
+ * A get key request should fail if no sessionId is provided
+ */
+TEST_P(DrmHalVendorPluginTest, GetKeyRequestNoSession) {
+ SessionId invalidSessionId;
+ hidl_vec<uint8_t> initData;
+ hidl_string mimeType = "video/mp4";
+ KeyedVector optionalParameters;
+ auto res = drmPlugin->getKeyRequest(
+ invalidSessionId, initData, mimeType, KeyType::STREAMING,
+ optionalParameters,
+ [&](Status status, const hidl_vec<uint8_t>&, KeyRequestType,
+ const hidl_string&) { EXPECT_EQ(Status::BAD_VALUE, status); });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that an empty sessionID returns BAD_VALUE
+ */
+TEST_P(DrmHalVendorPluginTest, ProvideKeyResponseEmptySessionId) {
+ SessionId session;
+
+ hidl_vec<uint8_t> keyResponse = {0x7b, 0x22, 0x6b, 0x65,
+ 0x79, 0x73, 0x22, 0x3a};
+ auto res = drmPlugin->provideKeyResponse(
+ session, keyResponse,
+ [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ EXPECT_EQ(keySetId.size(), 0u);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that an empty key response returns BAD_VALUE
+ */
+TEST_P(DrmHalVendorPluginTest, ProvideKeyResponseEmptyResponse) {
+ SessionId session = openSession();
+ hidl_vec<uint8_t> emptyResponse;
+ auto res = drmPlugin->provideKeyResponse(
+ session, emptyResponse,
+ [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ EXPECT_EQ(keySetId.size(), 0u);
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+/**
+ * Test that the plugin either doesn't support getting
+ * secure stops, or has no secure stops available after
+ * clearing them.
+ */
+TEST_P(DrmHalVendorPluginTest, GetSecureStops) {
+ // There may be secure stops, depending on if there were keys
+ // loaded and unloaded previously. Clear them to get to a known
+ // state, then make sure there are none.
+ auto res = drmPlugin->getSecureStops(
+ [&](Status status, const hidl_vec<SecureStop>&) {
+ if (status != Status::OK) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ }
+ });
+ EXPECT_OK(res);
+
+ res = drmPlugin->getSecureStops(
+ [&](Status status, const hidl_vec<SecureStop>& secureStops) {
+ if (status == Status::OK) {
+ EXPECT_EQ(secureStops.size(), 0u);
+ } else {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ }
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that the clearkey plugin returns BAD_VALUE if
+ * an empty ssid is provided.
+ */
+TEST_P(DrmHalVendorPluginTest, GetSecureStopEmptySSID) {
+ SecureStopId ssid;
+ auto res = drmPlugin->getSecureStop(
+ ssid, [&](Status status, const SecureStop&) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that releasing all secure stops either isn't supported
+ * or is completed successfully
+ */
+TEST_P(DrmHalVendorPluginTest, ReleaseAllSecureStops) {
+ Status status = drmPlugin->releaseAllSecureStops();
+ EXPECT_TRUE(status == Status::OK ||
+ status == Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+/**
+ * Releasing a secure stop without first getting one and sending it to the
+ * server to get a valid SSID should return ERROR_DRM_INVALID_STATE.
+ * This is an optional API so it can also return CANNOT_HANDLE.
+ */
+TEST_P(DrmHalVendorPluginTest, ReleaseSecureStopSequenceError) {
+ SecureStopId ssid = {1, 2, 3, 4};
+ Status status = drmPlugin->releaseSecureStop(ssid);
+ EXPECT_TRUE(status == Status::ERROR_DRM_INVALID_STATE ||
+ status == Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+/**
+ * Test that releasing a specific secure stop with an empty ssid
+ * return BAD_VALUE. This is an optional API so it can also return
+ * CANNOT_HANDLE.
+ */
+TEST_P(DrmHalVendorPluginTest, ReleaseSecureStopEmptySSID) {
+ SecureStopId ssid;
+ Status status = drmPlugin->releaseSecureStop(ssid);
+ EXPECT_TRUE(status == Status::BAD_VALUE ||
+ status == Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+/**
+ * The following five tests verify that the properties
+ * defined in the MediaDrm API are supported by
+ * the plugin.
+ */
+TEST_P(DrmHalVendorPluginTest, GetVendorProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "vendor", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(value.size(), 0u);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetVersionProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "version", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(value.size(), 0u);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetDescriptionProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "description", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(value.size(), 0u);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetAlgorithmsProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "algorithms", [&](Status status, const hidl_string& value) {
+ if (status == Status::OK) {
+ EXPECT_NE(value.size(), 0u);
+ } else {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ }
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetPropertyUniqueDeviceID) {
+ auto res = drmPlugin->getPropertyByteArray(
+ "deviceUniqueId",
+ [&](Status status, const hidl_vec<uint8_t>& value) {
+ if (status == Status::OK) {
+ EXPECT_NE(value.size(), 0u);
+ } else {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ }
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that attempting to read invalid string and byte array
+ * properties returns the documented error code.
+ */
+TEST_P(DrmHalVendorPluginTest, GetInvalidStringProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "invalid", [&](Status status, const hidl_string&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetInvalidByteArrayProperty) {
+ auto res = drmPlugin->getPropertyByteArray(
+ "invalid", [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that setting invalid string and byte array properties returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetStringPropertyNotSupported) {
+ EXPECT_EQ(drmPlugin->setPropertyString("awefijaeflijwef", "value"),
+ Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+TEST_P(DrmHalVendorPluginTest, SetByteArrayPropertyNotSupported) {
+ hidl_vec<uint8_t> value;
+ EXPECT_EQ(drmPlugin->setPropertyByteArray("awefijaeflijwef", value),
+ Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+/**
+ * Test that setting an invalid cipher algorithm returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetCipherInvalidAlgorithm) {
+ SessionId session = openSession();
+ hidl_string algorithm;
+ Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ closeSession(session);
+}
+
+/**
+ * Test that setting a cipher algorithm with no session returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetCipherAlgorithmNoSession) {
+ SessionId session;
+ hidl_string algorithm = "AES/CBC/NoPadding";
+ Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * Test that setting a valid cipher algorithm returns
+ * the expected status value. It is not required that all
+ * vendor modules support this algorithm, but they must
+ * either accept it or return ERROR_DRM_CANNOT_HANDLE
+ */
+TEST_P(DrmHalVendorPluginTest, SetCipherAlgorithm) {
+ SessionId session = openSession();
+ ;
+ hidl_string algorithm = "AES/CBC/NoPadding";
+ Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+ EXPECT_TRUE(status == Status::OK ||
+ status == Status::ERROR_DRM_CANNOT_HANDLE);
+ closeSession(session);
+}
+
+/**
+ * Test that setting an invalid mac algorithm returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetMacInvalidAlgorithm) {
+ SessionId session = openSession();
+ hidl_string algorithm;
+ Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ closeSession(session);
+}
+
+/**
+ * Test that setting a mac algorithm with no session returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetMacNullAlgorithmNoSession) {
+ SessionId session;
+ hidl_string algorithm = "HmacSHA256";
+ Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * Test that setting a valid mac algorithm returns
+ * the expected status value. It is not required that all
+ * vendor modules support this algorithm, but they must
+ * either accept it or return ERROR_DRM_CANNOT_HANDLE
+ */
+TEST_P(DrmHalVendorPluginTest, SetMacAlgorithm) {
+ SessionId session = openSession();
+ hidl_string algorithm = "HmacSHA256";
+ Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+ EXPECT_TRUE(status == Status::OK ||
+ status == Status::ERROR_DRM_CANNOT_HANDLE);
+ closeSession(session);
+}
+
+/**
+ * The Generic* methods provide general purpose crypto operations
+ * that may be used for applications other than DRM. They leverage
+ * the hardware root of trust and secure key distribution mechanisms
+ * of a DRM system to enable app-specific crypto functionality where
+ * the crypto keys are not exposed outside of the trusted execution
+ * environment.
+ *
+ * Generic encrypt/decrypt/sign/verify should fail on invalid
+ * inputs, e.g. empty sessionId
+ */
+TEST_P(DrmHalVendorPluginTest, GenericEncryptNoSession) {
+ SessionId session;
+ hidl_vec<uint8_t> keyId, input, iv;
+ auto res = drmPlugin->encrypt(
+ session, keyId, input, iv,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GenericDecryptNoSession) {
+ SessionId session;
+ hidl_vec<uint8_t> keyId, input, iv;
+ auto res = drmPlugin->decrypt(
+ session, keyId, input, iv,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GenericSignNoSession) {
+ SessionId session;
+ hidl_vec<uint8_t> keyId, message;
+ auto res = drmPlugin->sign(
+ session, keyId, message,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GenericVerifyNoSession) {
+ SessionId session;
+ hidl_vec<uint8_t> keyId, message, signature;
+ auto res = drmPlugin->verify(
+ session, keyId, message, signature, [&](Status status, bool) {
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GenericSignRSANoSession) {
+ SessionId session;
+ hidl_string algorithm;
+ hidl_vec<uint8_t> message, wrappedKey;
+ auto res = drmPlugin->signRSA(session, algorithm, message, wrappedKey,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Exercise the requiresSecureDecoderComponent method. Additional tests
+ * will verify positive cases with specific vendor content configurations.
+ * Below we just test the negative cases.
+ */
+
+/**
+ * Verify that requiresSecureDecoderComponent handles empty mimetype.
+ */
+TEST_P(DrmHalVendorPluginTest, RequiresSecureDecoderEmptyMimeType) {
+ EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent(""));
+}
+
+/**
+ * Verify that requiresSecureDecoderComponent handles invalid mimetype.
+ */
+TEST_P(DrmHalVendorPluginTest, RequiresSecureDecoderInvalidMimeType) {
+ EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent("bad"));
+}
+
+/**
+ * CryptoPlugin tests
+ */
+
+/**
+ * Exercise the NotifyResolution API. There is no observable result,
+ * just call the method for coverage.
+ */
+TEST_P(DrmHalVendorPluginTest, NotifyResolution) {
+ cryptoPlugin->notifyResolution(1920, 1080);
+}
+
+/**
+ * getDecryptMemory allocates memory for decryption, then sets it
+ * as a shared buffer base in the crypto hal. The allocated and
+ * mapped IMemory is returned.
+ *
+ * @param size the size of the memory segment to allocate
+ * @param the index of the memory segment which will be used
+ * to refer to it for decryption.
+ */
+sp<IMemory> DrmHalVendorPluginTest::getDecryptMemory(size_t size,
+ size_t index) {
+ sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
+ EXPECT_NE(ashmemAllocator, nullptr);
+
+ hidl_memory hidlMemory;
+ auto res = ashmemAllocator->allocate(
+ size, [&](bool success, const hidl_memory& memory) {
+ EXPECT_EQ(success, true);
+ EXPECT_EQ(memory.size(), size);
+ hidlMemory = memory;
+ });
+
+ EXPECT_OK(res);
+
+ sp<IMemory> mappedMemory = mapMemory(hidlMemory);
+ EXPECT_NE(mappedMemory, nullptr);
+ res = cryptoPlugin->setSharedBufferBase(hidlMemory, index);
+ EXPECT_OK(res);
+ return mappedMemory;
+}
+
+/**
+ * Exercise the setMediaDrmSession method. setMediaDrmSession
+ * is used to associate a drm session with a crypto session.
+ */
+TEST_P(DrmHalVendorPluginTest, SetMediaDrmSession) {
+ auto sessionId = openSession();
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+ closeSession(sessionId);
+}
+
+/**
+ * setMediaDrmSession with a closed session id
+ */
+TEST_P(DrmHalVendorPluginTest, SetMediaDrmSessionClosedSession) {
+ auto sessionId = openSession();
+ closeSession(sessionId);
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+}
+
+/**
+ * Decrypt tests
+ */
+
+class DrmHalVendorDecryptTest : public DrmHalVendorPluginTest {
+ public:
+ DrmHalVendorDecryptTest() = default;
+ virtual ~DrmHalVendorDecryptTest() {}
+
+ protected:
+ void loadKeys(const SessionId& sessionId,
+ const DrmHalVTSVendorModule_V1::ContentConfiguration&
+ configuration);
+ void fillRandom(const sp<IMemory>& memory);
+ KeyedVector toHidlKeyedVector(const map<string, string>& params);
+ hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) {
+ EXPECT_EQ(vec.size(), 16u);
+ return hidl_array<uint8_t, 16>(&vec[0]);
+ }
+};
+
+KeyedVector DrmHalVendorDecryptTest::toHidlKeyedVector(
+ const map<string, string>& params) {
+ std::vector<KeyValue> stdKeyedVector;
+ for (auto it = params.begin(); it != params.end(); ++it) {
+ KeyValue keyValue;
+ keyValue.key = it->first;
+ keyValue.value = it->second;
+ stdKeyedVector.push_back(keyValue);
+ }
+ return KeyedVector(stdKeyedVector);
+}
+
+/**
+ * Helper method to load keys for subsequent decrypt tests.
+ * These tests use predetermined key request/response to
+ * avoid requiring a round trip to a license server.
+ */
+void DrmHalVendorDecryptTest::loadKeys(
+ const SessionId& sessionId,
+ const DrmHalVTSVendorModule_V1::ContentConfiguration& configuration) {
+ hidl_vec<uint8_t> keyRequest;
+ auto res = drmPlugin->getKeyRequest(
+ sessionId, configuration.initData, configuration.mimeType,
+ KeyType::STREAMING,
+ toHidlKeyedVector(configuration.optionalParameters),
+ [&](Status status, const hidl_vec<uint8_t>& request,
+ KeyRequestType type, const hidl_string&) {
+ EXPECT_EQ(Status::OK, status)
+ << "Failed to get "
+ "key request for configuration "
+ << configuration.name;
+ EXPECT_EQ(type, KeyRequestType::INITIAL);
+ EXPECT_NE(request.size(), 0u) << "Expected key request size"
+ " to have length > 0 bytes";
+ keyRequest = request;
+ });
+ EXPECT_OK(res);
+
+ /**
+ * Get key response from vendor module
+ */
+ hidl_vec<uint8_t> keyResponse =
+ vendorModule->handleKeyRequest(keyRequest, configuration.serverUrl);
+
+ EXPECT_NE(keyResponse.size(), 0u) << "Expected key response size "
+ "to have length > 0 bytes";
+
+ res = drmPlugin->provideKeyResponse(
+ sessionId, keyResponse,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::OK, status)
+ << "Failure providing "
+ "key response for configuration "
+ << configuration.name;
+ });
+ EXPECT_OK(res);
+}
+
+void DrmHalVendorDecryptTest::fillRandom(const sp<IMemory>& memory) {
+ random_device rd;
+ mt19937 rand(rd());
+ for (size_t i = 0; i < memory->getSize() / sizeof(uint32_t); i++) {
+ auto p = static_cast<uint32_t*>(
+ static_cast<void*>(memory->getPointer()));
+ p[i] = rand();
+ }
+}
+
+TEST_P(DrmHalVendorDecryptTest, ValidateConfigurations) {
+ vector<DrmHalVTSVendorModule_V1::ContentConfiguration> configurations =
+ vendorModule->getContentConfigurations();
+ const char* kVendorStr = "Vendor module ";
+ for (auto config : configurations) {
+ ASSERT_TRUE(config.name.size() > 0) << kVendorStr << "has no name";
+ ASSERT_TRUE(config.serverUrl.size() > 0) << kVendorStr
+ << "has no serverUrl";
+ ASSERT_TRUE(config.initData.size() > 0) << kVendorStr
+ << "has no init data";
+ ASSERT_TRUE(config.mimeType.size() > 0) << kVendorStr
+ << "has no mime type";
+ ASSERT_TRUE(config.keys.size() >= 1) << kVendorStr << "has no keys";
+ for (auto key : config.keys) {
+ ASSERT_TRUE(key.keyId.size() > 0) << kVendorStr
+ << " has zero length keyId";
+ ASSERT_TRUE(key.keyId.size() > 0) << kVendorStr
+ << " has zero length key value";
+ }
+ }
+}
+
+/**
+ * Positive decrypt test. "Decrypt" a single clear
+ * segment. Verify data matches.
+ */
+TEST_P(DrmHalVendorDecryptTest, ClearSegmentTest) {
+ vector<DrmHalVTSVendorModule_V1::ContentConfiguration> configurations =
+ vendorModule->getContentConfigurations();
+ for (auto config : configurations) {
+ const size_t kSegmentSize = 1024;
+ const size_t kSegmentIndex = 0;
+ uint8_t iv[16] = {0};
+
+ sp<IMemory> sharedMemory =
+ getDecryptMemory(kSegmentSize * 2, kSegmentIndex);
+
+ SharedBuffer sourceBuffer = {
+ .bufferId = kSegmentIndex, .offset = 0, .size = kSegmentSize};
+ fillRandom(sharedMemory);
+
+ DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
+ {.bufferId = kSegmentIndex,
+ .offset = kSegmentSize,
+ .size = kSegmentSize},
+ .secureMemory = nullptr};
+
+ Pattern noPattern = {0, 0};
+ vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
+ .numBytesOfEncryptedData = 0}};
+ uint64_t offset = 0;
+
+ auto sessionId = openSession();
+ loadKeys(sessionId, config);
+
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+
+ const bool kNotSecure = false;
+ auto res = cryptoPlugin->decrypt(
+ kNotSecure, toHidlArray(config.keys[0].keyId), iv,
+ Mode::UNENCRYPTED, noPattern, subSamples, sourceBuffer, offset,
+ destBuffer, [&](Status status, uint32_t bytesWritten,
+ string detailedError) {
+ EXPECT_EQ(Status::OK, status) << "Failure in decryption "
+ "for configuration "
+ << config.name << ": "
+ << detailedError;
+ EXPECT_EQ(bytesWritten, kSegmentSize);
+ });
+ EXPECT_OK(res);
+ uint8_t* base = static_cast<uint8_t*>(
+ static_cast<void*>(sharedMemory->getPointer()));
+
+ EXPECT_EQ(0,
+ memcmp(static_cast<void*>(base),
+ static_cast<void*>(base + kSegmentSize), kSegmentSize))
+ << "decrypt data mismatch";
+ closeSession(sessionId);
+ }
+}
+
+/**
+ * Instantiate the set of test cases for each vendor module
+ */
+
+INSTANTIATE_TEST_CASE_P(
+ DrmHalVendorFactoryTestCases, DrmHalVendorFactoryTest,
+ testing::ValuesIn(gVendorModules->getVendorModulePaths()));
+
+INSTANTIATE_TEST_CASE_P(
+ DrmHalVendorPluginTestCases, DrmHalVendorPluginTest,
+ testing::ValuesIn(gVendorModules->getVendorModulePaths()));
+
+INSTANTIATE_TEST_CASE_P(
+ DrmHalVendorDecryptTestCases, DrmHalVendorDecryptTest,
+ testing::ValuesIn(gVendorModules->getVendorModulePaths()));
+
+int main(int argc, char** argv) {
+ gVendorModules =
+ new drm_vts::VendorModules("/data/nativetest/drm_hidl_test/vendor");
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/drm/1.0/vts/functional/shared_library.cpp b/drm/1.0/vts/functional/shared_library.cpp
new file mode 100644
index 0000000..6658150
--- /dev/null
+++ b/drm/1.0/vts/functional/shared_library.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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 "drm-vts-shared-library"
+
+#include <dlfcn.h>
+#include <shared_library.h>
+
+using std::string;
+
+namespace drm_vts {
+
+SharedLibrary::SharedLibrary(const string& path) {
+ mLibHandle = dlopen(path.c_str(), RTLD_NOW);
+}
+
+SharedLibrary::~SharedLibrary() {
+ if (mLibHandle != NULL) {
+ dlclose(mLibHandle);
+ mLibHandle = NULL;
+ }
+}
+
+bool SharedLibrary::operator!() const {
+ return mLibHandle == NULL;
+}
+
+void* SharedLibrary::lookup(const char* symbol) const {
+ if (!mLibHandle) {
+ return NULL;
+ }
+
+ // Clear last error before we load the symbol again,
+ // in case the caller didn't retrieve it.
+ (void)dlerror();
+ return dlsym(mLibHandle, symbol);
+}
+
+const char* SharedLibrary::lastError() const {
+ const char* error = dlerror();
+ return error ? error : "No errors or unknown error";
+}
+};
diff --git a/drm/1.0/vts/functional/shared_library.h b/drm/1.0/vts/functional/shared_library.h
new file mode 100644
index 0000000..1f32243
--- /dev/null
+++ b/drm/1.0/vts/functional/shared_library.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef SHARED_LIBRARY_H_
+#define SHARED_LIBRARY_H_
+
+#include <string>
+#include <vector>
+
+namespace drm_vts {
+class SharedLibrary {
+ public:
+ explicit SharedLibrary(const std::string& path);
+ ~SharedLibrary();
+
+ bool operator!() const;
+ void* lookup(const char* symbol) const;
+ const char* lastError() const;
+
+ private:
+ void* mLibHandle;
+
+ SharedLibrary(const SharedLibrary&) = delete;
+ void operator=(const SharedLibrary&) = delete;
+};
+};
+
+#endif // SHARED_LIBRARY_H_
diff --git a/drm/1.0/vts/functional/vendor/lib/libvtswidevine.so b/drm/1.0/vts/functional/vendor/lib/libvtswidevine.so
new file mode 100755
index 0000000..d365b34
--- /dev/null
+++ b/drm/1.0/vts/functional/vendor/lib/libvtswidevine.so
Binary files differ
diff --git a/drm/1.0/vts/functional/vendor_modules.cpp b/drm/1.0/vts/functional/vendor_modules.cpp
new file mode 100644
index 0000000..34af6f8
--- /dev/null
+++ b/drm/1.0/vts/functional/vendor_modules.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 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 "drm-vts-vendor-modules"
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <utils/Log.h>
+#include <memory>
+
+#include "shared_library.h"
+#include "vendor_modules.h"
+
+using std::string;
+using std::vector;
+using std::unique_ptr;
+
+namespace drm_vts {
+vector<string> VendorModules::getVendorModulePaths() {
+ if (mModuleList.size() > 0) {
+ return mModuleList;
+ }
+
+ DIR* dir = opendir(mModulesPath.c_str());
+ if (dir == NULL) {
+ ALOGE("Unable to open drm VTS vendor directory %s",
+ mModulesPath.c_str());
+ return mModuleList;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dir))) {
+ string fullpath = mModulesPath + "/" + entry->d_name;
+ if (endsWith(fullpath, ".so")) {
+ mModuleList.push_back(fullpath);
+ }
+ }
+
+ closedir(dir);
+ return mModuleList;
+}
+
+DrmHalVTSVendorModule* VendorModules::getVendorModule(const string& path) {
+ unique_ptr<SharedLibrary>& library = mOpenLibraries[path];
+ if (!library) {
+ library = unique_ptr<SharedLibrary>(new SharedLibrary(path));
+ if (!library) {
+ ALOGE("failed to map shared library %s", path.c_str());
+ return NULL;
+ }
+ }
+ void* symbol = library->lookup("vendorModuleFactory");
+ if (symbol == NULL) {
+ ALOGE("getVendorModule failed to lookup 'vendorModuleFactory' in %s: "
+ "%s",
+ path.c_str(), library->lastError());
+ return NULL;
+ }
+ typedef DrmHalVTSVendorModule* (*ModuleFactory)();
+ ModuleFactory moduleFactory = reinterpret_cast<ModuleFactory>(symbol);
+ return (*moduleFactory)();
+}
+};
diff --git a/drm/1.0/vts/functional/vendor_modules.h b/drm/1.0/vts/functional/vendor_modules.h
new file mode 100644
index 0000000..5371a0d
--- /dev/null
+++ b/drm/1.0/vts/functional/vendor_modules.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef VENDOR_MODULES_H
+#define VENDOR_MODULES_H
+
+#include <map>
+
+#include "shared_library.h"
+
+class DrmHalVTSVendorModule;
+
+namespace drm_vts {
+class VendorModules {
+ public:
+ /**
+ * Initialize with a file system path where the shared libraries
+ * are to be found.
+ */
+ explicit VendorModules(const std::string& path) : mModulesPath(path) {}
+ ~VendorModules() {}
+
+ /**
+ * Return a list of paths to available vendor modules.
+ */
+ std::vector<std::string> getVendorModulePaths();
+
+ /**
+ * Retrieve a DrmHalVTSVendorModule given its full path. The
+ * getAPIVersion method can be used to determine the versioned
+ * subclass type.
+ */
+ DrmHalVTSVendorModule* getVendorModule(const std::string& path);
+
+ private:
+ std::string mModulesPath;
+ std::vector<std::string> mModuleList;
+ std::map<std::string, std::unique_ptr<SharedLibrary>> mOpenLibraries;
+
+ inline bool endsWith(const std::string& str, const std::string& suffix) {
+ if (suffix.size() > str.size()) return false;
+ return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
+ }
+
+ VendorModules(const VendorModules&) = delete;
+ void operator=(const VendorModules&) = delete;
+};
+};
+
+#endif // VENDOR_MODULES_H
diff --git a/drm/Android.bp b/drm/Android.bp
index bbb3e4b..33f70eb 100644
--- a/drm/Android.bp
+++ b/drm/Android.bp
@@ -1,4 +1,5 @@
// This is an autogenerated file, do not edit.
subdirs = [
"1.0",
+ "1.0/vts/functional",
]
diff --git a/dumpstate/1.0/default/service.cpp b/dumpstate/1.0/default/service.cpp
index 85bea12..4f276b7 100644
--- a/dumpstate/1.0/default/service.cpp
+++ b/dumpstate/1.0/default/service.cpp
@@ -24,11 +24,18 @@
using ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
using ::android::hardware::dumpstate::V1_0::implementation::DumpstateDevice;
using ::android::hardware::joinRpcThreadpool;
+using ::android::OK;
using ::android::sp;
-int main (int /* argc */, char * /* argv */ []) {
+int main(int /* argc */, char* /* argv */ []) {
sp<IDumpstateDevice> dumpstate = new DumpstateDevice;
- configureRpcThreadpool(1, true);
- dumpstate->registerAsService();
+ configureRpcThreadpool(1, true /* will join */);
+ if (dumpstate->registerAsService() != OK) {
+ ALOGE("Could not register service.");
+ return 1;
+ }
joinRpcThreadpool();
+
+ ALOGE("Service exited!");
+ return 1;
}
diff --git a/gnss/1.0/IGnssDebug.hal b/gnss/1.0/IGnssDebug.hal
index 9c1038f..716ba88 100644
--- a/gnss/1.0/IGnssDebug.hal
+++ b/gnss/1.0/IGnssDebug.hal
@@ -3,30 +3,51 @@
/** Extended interface for DEBUG support. */
interface IGnssDebug {
enum SatelliteEphemerisType : uint8_t {
- /** no information is known to the gnss hardware, about this satellite */
- UNKNOWN,
- /** this satellite is known to exist */
- KNOWN,
- /** this satellite is not known to exist */
- NONEXISTENT,
- /** Only Almanac (approximate) location known for this satellite */
+ /** Ephemeris is known for this satellite. */
+ EPHEMERIS,
+ /**
+ * Ephemeris is not known, but Almanac (approximate location) is known.
+ */
ALMANAC_ONLY,
- /** Ephemeris is known from demodulating the signal on device */
+ /**
+ * Both ephemeris & almanac are not known (e.g. during a cold start
+ * blind search.)
+ */
+ NOT_AVAILABLE
+ };
+
+ enum SatelliteEphemerisSource : uint8_t {
+ /**
+ * The ephemeris (or almanac only) information was demodulated from the
+ * signal received on the device
+ */
DEMODULATED,
- /** Ephemeris has been provided by SUPL */
+ /**
+ * The ephemeris (or almanac only) information was received from a SUPL
+ * server.
+ */
SUPL_PROVIDED,
- /** Ephemeris has been provided by another server */
+ /**
+ * The ephemeris (or almanac only) information was provided by another
+ * server.
+ */
OTHER_SERVER_PROVIDED,
/**
- * Predicted ephemeris has been provided by a server
- * (e.g. Xtra, Extended Ephemeris, etc...)
+ * The ephemeris (or almanac only) information was provided by another
+ * method, e.g. injected via a local debug tool, from build defaults
+ * (e.g. almanac), or is from a satellite
+ * with SatelliteEphemerisType::NOT_AVAILABLE.
*/
- SERVER_PREDICTED,
- /**
- * Predicted ephemeris in use, generated locally on the device (e.g. from prior
- * ephemeris)
- */
- LOCALLY_PREDICTED
+ OTHER
+ };
+
+ enum SatelliteEphemerisHealth : uint8_t {
+ /** The ephemeris is known good. */
+ GOOD,
+ /** The ephemeris is known bad. */
+ BAD,
+ /** The ephemeris is unknown to be good or bad. */
+ UNKNOWN
};
/**
@@ -45,22 +66,22 @@
double longitudeDegrees;
/** Altitude above ellipsoid expressed in meters */
float altitudeMeters;
- /** Represents speed in meters per second. */
+ /** Represents horizontal speed in meters per second. */
float speedMetersPerSec;
/** Represents heading in degrees. */
float bearingDegrees;
/**
- * estimated horizontal accuracy of position expressed in meters, radial,
+ * Estimated horizontal accuracy of position expressed in meters, radial,
* 68% confidence.
*/
double horizontalAccuracyMeters;
/**
- * estimated vertical accuracy of position expressed in meters, with
+ * Estimated vertical accuracy of position expressed in meters, with
* 68% confidence.
*/
double verticalAccuracyMeters;
/**
- * estimated speed accuracy in meters per second with 68% confidence.
+ * Estimated speed accuracy in meters per second with 68% confidence.
*/
double speedAccuracyMetersPerSecond;
/**
@@ -69,26 +90,31 @@
double bearingAccuracyDegrees;
/**
* Time duration before this report that this position information was
- * valid.
+ * valid. This can, for example, be a previous injected location with
+ * an age potentially thousands of seconds old, or
+ * extrapolated to the current time (with appropriately increased
+ * accuracy estimates), with a (near) zero age.
*/
float ageSeconds;
};
/**
* Provides the current best known UTC time estimate.
+ * If no fresh information is available, e.g. after a delete all,
+ * then whatever the effective defaults are on the device must be
+ * provided (e.g. Jan. 1, 2017, with an uncertainty of 5 years) expressed
+ * in the specified units.
*/
struct TimeDebug {
- /**
- * Validity of the data in the struct.
- * False if current time is unknown.
- */
- bool valid;
- /**
- * UTC time estimate.
- */
+ /** UTC time estimate. */
GnssUtcTime timeEstimate;
/** 68% error estimate in time. */
float timeUncertaintyNs;
+ /**
+ * 68% error estimate in local clock drift,
+ * in nanoseconds per second (also known as parts per billion - ppb.)
+ */
+ float frequencyUncertaintyNsPerSec;
};
/**
@@ -99,14 +125,34 @@
int16_t svid;
/** Defines the constellation type of the given SV. */
GnssConstellationType constellation;
+
/** Defines the ephemeris type of the satellite. */
SatelliteEphemerisType ephemerisType;
+ /** Defines the ephemeris source of the satellite. */
+ SatelliteEphemerisSource ephemerisSource;
/**
- * Time duration before this report, that the ephemeris source was last
- * updated, e.g. latest demodulation, or latest server download.
- * Set to 0 when ephemerisType is UNKNOWN.
+ * Defines whether the satellite is known healthy
+ * (safe for use in location calculation.)
+ */
+ SatelliteEphemerisHealth ephemerisHealth;
+ /**
+ * Time duration from this report (current time), minus the
+ * effective time of the ephemeris source (e.g. TOE, TOA.)
+ * Set to 0 when ephemerisType is NOT_AVAILABLE.
*/
float ephemerisAgeSeconds;
+
+ /**
+ * True if a server has provided a predicted orbit (& clock) for
+ * this satellite.
+ */
+ bool serverPredictionIsAvailable;
+ /**
+ * Time duration from this report (current time) minus the time of the
+ * start of the server predicted information. For example, a 1 day
+ * old prediction would be reported as 86400 seconds here.
+ */
+ float serverPredictionAgeSeconds;
};
/**
@@ -119,12 +165,11 @@
/** Current best know time estimate */
TimeDebug time;
/**
- * Provides a list of the decoded satellite ephemeris.
- * Must provide a complete list for all constellations device can track,
+ * Provides a list of the available satellite data, for all
+ * satellites and constellations the device can track,
* including GnssConstellationType UNKNOWN.
*/
vec<SatelliteData> satelliteDataArray;
-
};
/**
diff --git a/keymaster/3.0/default/KeymasterDevice.cpp b/keymaster/3.0/default/KeymasterDevice.cpp
index 720b946..6b4524b 100644
--- a/keymaster/3.0/default/KeymasterDevice.cpp
+++ b/keymaster/3.0/default/KeymasterDevice.cpp
@@ -698,14 +698,21 @@
return legacy_enum_conversion(keymaster_device_->abort(keymaster_device_, operationHandle));
}
-IKeymasterDevice* HIDL_FETCH_IKeymasterDevice(const char* /* name */) {
+IKeymasterDevice* HIDL_FETCH_IKeymasterDevice(const char* name) {
keymaster2_device_t* dev = nullptr;
- uint32_t version;
- bool supports_ec;
- bool supports_all_digests;
- auto rc = keymaster_device_initialize(&dev, &version, &supports_ec, &supports_all_digests);
- if (rc) return nullptr;
+ ALOGI("Fetching keymaster device name %s", name);
+
+ uint32_t version = -1;
+ bool supports_ec = false;
+ bool supports_all_digests = false;
+
+ if (name && strcmp(name, "softwareonly") == 0) {
+ dev = (new SoftKeymasterDevice(new SoftwareOnlyHidlKeymasterContext))->keymaster2_device();
+ } else if (name && strcmp(name, "default") == 0) {
+ auto rc = keymaster_device_initialize(&dev, &version, &supports_ec, &supports_all_digests);
+ if (rc) return nullptr;
+ }
auto kmrc = ::keymaster::ConfigureDevice(dev);
if (kmrc != KM_ERROR_OK) {
diff --git a/keymaster/3.0/vts/functional/Android.mk b/keymaster/3.0/vts/functional/Android.mk
new file mode 100644
index 0000000..4265b9f
--- /dev/null
+++ b/keymaster/3.0/vts/functional/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2017 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_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := keymaster_hidl_hal_test
+LOCAL_SRC_FILES := \
+ authorization_set.cpp \
+ attestation_record.cpp \
+ key_param_output.cpp \
+ keymaster_hidl_hal_test.cpp \
+ keystore_tags_utils.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ android.hardware.keymaster@3.0 \
+ libcrypto \
+ libhidlbase \
+ liblog \
+ libsoftkeymasterdevice \
+ libutils \
+
+LOCAL_STATIC_LIBRARIES := \
+ VtsHalHidlTargetTestBase \
+
+LOCAL_CFLAGS := -Wall -Werror
+
+include $(BUILD_NATIVE_TEST)
diff --git a/keymaster/3.0/vts/functional/attestation_record.cpp b/keymaster/3.0/vts/functional/attestation_record.cpp
new file mode 100644
index 0000000..6cdd44c
--- /dev/null
+++ b/keymaster/3.0/vts/functional/attestation_record.cpp
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#include "attestation_record.h"
+
+#include <assert.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include "openssl_utils.h"
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V3_0 {
+
+struct stack_st_ASN1_TYPE_Delete {
+ void operator()(stack_st_ASN1_TYPE* p) { sk_ASN1_TYPE_free(p); }
+};
+
+struct ASN1_STRING_Delete {
+ void operator()(ASN1_STRING* p) { ASN1_STRING_free(p); }
+};
+
+struct ASN1_TYPE_Delete {
+ void operator()(ASN1_TYPE* p) { ASN1_TYPE_free(p); }
+};
+
+#define ASN1_INTEGER_SET STACK_OF(ASN1_INTEGER)
+
+typedef struct km_root_of_trust {
+ ASN1_OCTET_STRING* verified_boot_key;
+ ASN1_BOOLEAN* device_locked;
+ ASN1_ENUMERATED* verified_boot_state;
+} KM_ROOT_OF_TRUST;
+
+ASN1_SEQUENCE(KM_ROOT_OF_TRUST) = {
+ ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_key, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(KM_ROOT_OF_TRUST, device_locked, ASN1_BOOLEAN),
+ ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_state, ASN1_ENUMERATED),
+} ASN1_SEQUENCE_END(KM_ROOT_OF_TRUST);
+IMPLEMENT_ASN1_FUNCTIONS(KM_ROOT_OF_TRUST);
+
+typedef struct km_auth_list {
+ ASN1_INTEGER_SET* purpose;
+ ASN1_INTEGER* algorithm;
+ ASN1_INTEGER* key_size;
+ ASN1_INTEGER_SET* digest;
+ ASN1_INTEGER_SET* padding;
+ ASN1_INTEGER_SET* kdf;
+ ASN1_INTEGER* ec_curve;
+ ASN1_INTEGER* rsa_public_exponent;
+ ASN1_INTEGER* active_date_time;
+ ASN1_INTEGER* origination_expire_date_time;
+ ASN1_INTEGER* usage_expire_date_time;
+ ASN1_NULL* no_auth_required;
+ ASN1_INTEGER* user_auth_type;
+ ASN1_INTEGER* auth_timeout;
+ ASN1_NULL* allow_while_on_body;
+ ASN1_NULL* all_applications;
+ ASN1_OCTET_STRING* application_id;
+ ASN1_INTEGER* creation_date_time;
+ ASN1_INTEGER* origin;
+ ASN1_NULL* rollback_resistant;
+ KM_ROOT_OF_TRUST* root_of_trust;
+ ASN1_INTEGER* os_version;
+ ASN1_INTEGER* os_patchlevel;
+ ASN1_OCTET_STRING* attestation_application_id;
+} KM_AUTH_LIST;
+
+ASN1_SEQUENCE(KM_AUTH_LIST) = {
+ ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, purpose, ASN1_INTEGER, TAG_PURPOSE.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, algorithm, ASN1_INTEGER, TAG_ALGORITHM.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, key_size, ASN1_INTEGER, TAG_KEY_SIZE.maskedTag()),
+ ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, digest, ASN1_INTEGER, TAG_DIGEST.maskedTag()),
+ ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, padding, ASN1_INTEGER, TAG_PADDING.maskedTag()),
+ ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, kdf, ASN1_INTEGER, TAG_KDF.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, ec_curve, ASN1_INTEGER, TAG_EC_CURVE.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, rsa_public_exponent, ASN1_INTEGER,
+ TAG_RSA_PUBLIC_EXPONENT.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, active_date_time, ASN1_INTEGER, TAG_ACTIVE_DATETIME.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, origination_expire_date_time, ASN1_INTEGER,
+ TAG_ORIGINATION_EXPIRE_DATETIME.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, usage_expire_date_time, ASN1_INTEGER,
+ TAG_USAGE_EXPIRE_DATETIME.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, no_auth_required, ASN1_NULL, TAG_NO_AUTH_REQUIRED.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, user_auth_type, ASN1_INTEGER, TAG_USER_AUTH_TYPE.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, auth_timeout, ASN1_INTEGER, TAG_AUTH_TIMEOUT.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, allow_while_on_body, ASN1_NULL, TAG_ALLOW_WHILE_ON_BODY.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, all_applications, ASN1_NULL, TAG_ALL_APPLICATIONS.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, application_id, ASN1_OCTET_STRING, TAG_APPLICATION_ID.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, creation_date_time, ASN1_INTEGER, TAG_CREATION_DATETIME.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, origin, ASN1_INTEGER, TAG_ORIGIN.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, rollback_resistant, ASN1_NULL, TAG_ROLLBACK_RESISTANT.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, root_of_trust, KM_ROOT_OF_TRUST, TAG_ROOT_OF_TRUST.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, os_version, ASN1_INTEGER, TAG_OS_VERSION.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, os_patchlevel, ASN1_INTEGER, TAG_OS_PATCHLEVEL.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, attestation_application_id, ASN1_OCTET_STRING,
+ TAG_ATTESTATION_APPLICATION_ID.maskedTag()),
+} ASN1_SEQUENCE_END(KM_AUTH_LIST);
+IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST);
+
+typedef struct km_key_description {
+ ASN1_INTEGER* attestation_version;
+ ASN1_ENUMERATED* attestation_security_level;
+ ASN1_INTEGER* keymaster_version;
+ ASN1_ENUMERATED* keymaster_security_level;
+ ASN1_OCTET_STRING* attestation_challenge;
+ KM_AUTH_LIST* software_enforced;
+ KM_AUTH_LIST* tee_enforced;
+ ASN1_INTEGER* unique_id;
+} KM_KEY_DESCRIPTION;
+
+ASN1_SEQUENCE(KM_KEY_DESCRIPTION) = {
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_version, ASN1_INTEGER),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_security_level, ASN1_ENUMERATED),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymaster_version, ASN1_INTEGER),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymaster_security_level, ASN1_ENUMERATED),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_challenge, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, unique_id, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, software_enforced, KM_AUTH_LIST),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, tee_enforced, KM_AUTH_LIST),
+} ASN1_SEQUENCE_END(KM_KEY_DESCRIPTION);
+IMPLEMENT_ASN1_FUNCTIONS(KM_KEY_DESCRIPTION);
+
+template <Tag tag>
+void copyAuthTag(const stack_st_ASN1_INTEGER* stack, TypedTag<TagType::ENUM_REP, tag> ttag,
+ AuthorizationSet* auth_list) {
+ typedef typename TypedTag2ValueType<decltype(ttag)>::type ValueT;
+ for (size_t i = 0; i < sk_ASN1_INTEGER_num(stack); ++i) {
+ auth_list->push_back(
+ ttag, static_cast<ValueT>(ASN1_INTEGER_get(sk_ASN1_INTEGER_value(stack, i))));
+ }
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::ENUM, tag> ttag,
+ AuthorizationSet* auth_list) {
+ typedef typename TypedTag2ValueType<decltype(ttag)>::type ValueT;
+ if (!asn1_int) return;
+ auth_list->push_back(ttag, static_cast<ValueT>(ASN1_INTEGER_get(asn1_int)));
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::UINT, tag> ttag,
+ AuthorizationSet* auth_list) {
+ if (!asn1_int) return;
+ auth_list->push_back(ttag, ASN1_INTEGER_get(asn1_int));
+}
+
+BIGNUM* construct_uint_max() {
+ BIGNUM* value = BN_new();
+ BIGNUM_Ptr one(BN_new());
+ BN_one(one.get());
+ BN_lshift(value, one.get(), 32);
+ return value;
+}
+
+uint64_t BignumToUint64(BIGNUM* num) {
+ static_assert((sizeof(BN_ULONG) == sizeof(uint32_t)) || (sizeof(BN_ULONG) == sizeof(uint64_t)),
+ "This implementation only supports 32 and 64-bit BN_ULONG");
+ if (sizeof(BN_ULONG) == sizeof(uint32_t)) {
+ BIGNUM_Ptr uint_max(construct_uint_max());
+ BIGNUM_Ptr hi(BN_new()), lo(BN_new());
+ BN_CTX_Ptr ctx(BN_CTX_new());
+ BN_div(hi.get(), lo.get(), num, uint_max.get(), ctx.get());
+ return static_cast<uint64_t>(BN_get_word(hi.get())) << 32 | BN_get_word(lo.get());
+ } else if (sizeof(BN_ULONG) == sizeof(uint64_t)) {
+ return BN_get_word(num);
+ } else {
+ return 0;
+ }
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::ULONG, tag> ttag,
+ AuthorizationSet* auth_list) {
+ if (!asn1_int) return;
+ BIGNUM_Ptr num(ASN1_INTEGER_to_BN(asn1_int, nullptr));
+ auth_list->push_back(ttag, BignumToUint64(num.get()));
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::DATE, tag> ttag,
+ AuthorizationSet* auth_list) {
+ if (!asn1_int) return;
+ BIGNUM_Ptr num(ASN1_INTEGER_to_BN(asn1_int, nullptr));
+ auth_list->push_back(ttag, BignumToUint64(num.get()));
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_NULL* asn1_null, TypedTag<TagType::BOOL, tag> ttag,
+ AuthorizationSet* auth_list) {
+ if (!asn1_null) return;
+ auth_list->push_back(ttag);
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_OCTET_STRING* asn1_string, TypedTag<TagType::BYTES, tag> ttag,
+ AuthorizationSet* auth_list) {
+ if (!asn1_string) return;
+ hidl_vec<uint8_t> buf;
+ buf.setToExternal(asn1_string->data, asn1_string->length);
+ auth_list->push_back(ttag, buf);
+}
+
+// Extract the values from the specified ASN.1 record and place them in auth_list.
+static ErrorCode extract_auth_list(const KM_AUTH_LIST* record, AuthorizationSet* auth_list) {
+ if (!record) return ErrorCode::OK;
+
+ copyAuthTag(record->active_date_time, TAG_ACTIVE_DATETIME, auth_list);
+ copyAuthTag(record->algorithm, TAG_ALGORITHM, auth_list);
+ copyAuthTag(record->all_applications, TAG_ALL_APPLICATIONS, auth_list);
+ copyAuthTag(record->application_id, TAG_APPLICATION_ID, auth_list);
+ copyAuthTag(record->auth_timeout, TAG_AUTH_TIMEOUT, auth_list);
+ copyAuthTag(record->creation_date_time, TAG_CREATION_DATETIME, auth_list);
+ copyAuthTag(record->digest, TAG_DIGEST, auth_list);
+ copyAuthTag(record->ec_curve, TAG_EC_CURVE, auth_list);
+ copyAuthTag(record->key_size, TAG_KEY_SIZE, auth_list);
+ copyAuthTag(record->no_auth_required, TAG_NO_AUTH_REQUIRED, auth_list);
+ copyAuthTag(record->origin, TAG_ORIGIN, auth_list);
+ copyAuthTag(record->origination_expire_date_time, TAG_ORIGINATION_EXPIRE_DATETIME, auth_list);
+ copyAuthTag(record->os_patchlevel, TAG_OS_PATCHLEVEL, auth_list);
+ copyAuthTag(record->os_version, TAG_OS_VERSION, auth_list);
+ copyAuthTag(record->padding, TAG_PADDING, auth_list);
+ copyAuthTag(record->purpose, TAG_PURPOSE, auth_list);
+ copyAuthTag(record->rollback_resistant, TAG_ROLLBACK_RESISTANT, auth_list);
+ copyAuthTag(record->rsa_public_exponent, TAG_RSA_PUBLIC_EXPONENT, auth_list);
+ copyAuthTag(record->usage_expire_date_time, TAG_USAGE_EXPIRE_DATETIME, auth_list);
+ copyAuthTag(record->user_auth_type, TAG_USER_AUTH_TYPE, auth_list);
+
+ return ErrorCode::OK;
+}
+
+MAKE_OPENSSL_PTR_TYPE(KM_KEY_DESCRIPTION)
+
+// Parse the DER-encoded attestation record, placing the results in keymaster_version,
+// attestation_challenge, software_enforced, tee_enforced and unique_id.
+ErrorCode parse_attestation_record(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
+ uint32_t* attestation_version, //
+ SecurityLevel* attestation_security_level,
+ uint32_t* keymaster_version,
+ SecurityLevel* keymaster_security_level,
+ hidl_vec<uint8_t>* attestation_challenge,
+ AuthorizationSet* software_enforced,
+ AuthorizationSet* tee_enforced, //
+ hidl_vec<uint8_t>* unique_id) {
+ const uint8_t* p = asn1_key_desc;
+ KM_KEY_DESCRIPTION_Ptr record(d2i_KM_KEY_DESCRIPTION(nullptr, &p, asn1_key_desc_len));
+ if (!record.get()) return ErrorCode::UNKNOWN_ERROR;
+
+ *attestation_version = ASN1_INTEGER_get(record->attestation_version);
+ *attestation_security_level =
+ static_cast<SecurityLevel>(ASN1_ENUMERATED_get(record->attestation_security_level));
+ *keymaster_version = ASN1_INTEGER_get(record->keymaster_version);
+ *keymaster_security_level =
+ static_cast<SecurityLevel>(ASN1_ENUMERATED_get(record->keymaster_security_level));
+
+ attestation_challenge->setToExternal(record->attestation_challenge->data,
+ record->attestation_challenge->length);
+
+ unique_id->setToExternal(record->unique_id->data, record->unique_id->length);
+
+ ErrorCode error = extract_auth_list(record->software_enforced, software_enforced);
+ if (error != ErrorCode::OK) return error;
+
+ return extract_auth_list(record->tee_enforced, tee_enforced);
+}
+
+} // namespace V3_0
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
diff --git a/keymaster/3.0/vts/functional/attestation_record.h b/keymaster/3.0/vts/functional/attestation_record.h
new file mode 100644
index 0000000..a042055
--- /dev/null
+++ b/keymaster/3.0/vts/functional/attestation_record.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#ifndef HARDWARE_INTERFACES_KEYMASTER_30_VTS_FUNCTIONAL_ATTESTATION_RECORD_H_
+#define HARDWARE_INTERFACES_KEYMASTER_30_VTS_FUNCTIONAL_ATTESTATION_RECORD_H_
+
+#include "authorization_set.h"
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V3_0 {
+
+/**
+ * The OID for Android attestation records. For the curious, it breaks down as follows:
+ *
+ * 1 = ISO
+ * 3 = org
+ * 6 = DoD (Huh? OIDs are weird.)
+ * 1 = IANA
+ * 4 = Private
+ * 1 = Enterprises
+ * 11129 = Google
+ * 2 = Google security
+ * 1 = certificate extension
+ * 17 = Android attestation extension.
+ */
+static const char kAttestionRecordOid[] = "1.3.6.1.4.1.11129.2.1.17";
+
+ErrorCode parse_attestation_record(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
+ uint32_t* attestation_version, //
+ SecurityLevel* attestation_security_level,
+ uint32_t* keymaster_version,
+ SecurityLevel* keymaster_security_level,
+ hidl_vec<uint8_t>* attestation_challenge,
+ AuthorizationSet* software_enforced,
+ AuthorizationSet* tee_enforced, //
+ hidl_vec<uint8_t>* unique_id);
+} // namespace V3_0
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
+
+#endif // HARDWARE_INTERFACES_KEYMASTER_30_VTS_FUNCTIONAL_ATTESTATION_RECORD_H_
diff --git a/keymaster/3.0/vts/functional/authorization_set.cpp b/keymaster/3.0/vts/functional/authorization_set.cpp
new file mode 100644
index 0000000..303f7e7
--- /dev/null
+++ b/keymaster/3.0/vts/functional/authorization_set.cpp
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "authorization_set.h"
+
+#include <assert.h>
+#include <istream>
+#include <limits>
+#include <ostream>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <new>
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V3_0 {
+
+inline bool keyParamLess(const KeyParameter& a, const KeyParameter& b) {
+ if (a.tag != b.tag) return a.tag < b.tag;
+ int retval;
+ switch (typeFromTag(a.tag)) {
+ case TagType::INVALID:
+ case TagType::BOOL:
+ return false;
+ case TagType::ENUM:
+ case TagType::ENUM_REP:
+ case TagType::UINT:
+ case TagType::UINT_REP:
+ return a.f.integer < b.f.integer;
+ case TagType::ULONG:
+ case TagType::ULONG_REP:
+ return a.f.longInteger < b.f.longInteger;
+ case TagType::DATE:
+ return a.f.dateTime < b.f.dateTime;
+ case TagType::BIGNUM:
+ case TagType::BYTES:
+ // Handle the empty cases.
+ if (a.blob.size() == 0) return b.blob.size() != 0;
+ if (b.blob.size() == 0) return false;
+
+ retval = memcmp(&a.blob[0], &b.blob[0], std::min(a.blob.size(), b.blob.size()));
+ if (retval == 0) {
+ // One is the prefix of the other, so the longer wins
+ return a.blob.size() < b.blob.size();
+ } else {
+ return retval < 0;
+ }
+ }
+ return false;
+}
+
+inline bool keyParamEqual(const KeyParameter& a, const KeyParameter& b) {
+ if (a.tag != b.tag) return false;
+
+ switch (typeFromTag(a.tag)) {
+ case TagType::INVALID:
+ case TagType::BOOL:
+ return true;
+ case TagType::ENUM:
+ case TagType::ENUM_REP:
+ case TagType::UINT:
+ case TagType::UINT_REP:
+ return a.f.integer == b.f.integer;
+ case TagType::ULONG:
+ case TagType::ULONG_REP:
+ return a.f.longInteger == b.f.longInteger;
+ case TagType::DATE:
+ return a.f.dateTime == b.f.dateTime;
+ case TagType::BIGNUM:
+ case TagType::BYTES:
+ if (a.blob.size() != b.blob.size()) return false;
+ return a.blob.size() == 0 || memcmp(&a.blob[0], &b.blob[0], a.blob.size()) == 0;
+ }
+ return false;
+}
+
+void AuthorizationSet::Sort() {
+ std::sort(data_.begin(), data_.end(), keyParamLess);
+}
+
+void AuthorizationSet::Deduplicate() {
+ if (data_.empty()) return;
+
+ Sort();
+ std::vector<KeyParameter> result;
+
+ auto curr = data_.begin();
+ auto prev = curr++;
+ for (; curr != data_.end(); ++prev, ++curr) {
+ if (prev->tag == Tag::INVALID) continue;
+
+ if (!keyParamEqual(*prev, *curr)) {
+ result.emplace_back(std::move(*prev));
+ }
+ }
+ result.emplace_back(std::move(*prev));
+
+ std::swap(data_, result);
+}
+
+void AuthorizationSet::Union(const AuthorizationSet& other) {
+ data_.insert(data_.end(), other.data_.begin(), other.data_.end());
+ Deduplicate();
+}
+
+void AuthorizationSet::Subtract(const AuthorizationSet& other) {
+ Deduplicate();
+
+ auto i = other.begin();
+ while (i != other.end()) {
+ int pos = -1;
+ do {
+ pos = find(i->tag, pos);
+ if (pos != -1 && keyParamEqual(*i, data_[pos])) {
+ data_.erase(data_.begin() + pos);
+ break;
+ }
+ } while (pos != -1);
+ ++i;
+ }
+}
+
+int AuthorizationSet::find(Tag tag, int begin) const {
+ auto iter = data_.begin() + (1 + begin);
+
+ while (iter != data_.end() && iter->tag != tag)
+ ++iter;
+
+ if (iter != data_.end()) return iter - data_.begin();
+ return -1;
+}
+
+bool AuthorizationSet::erase(int index) {
+ auto pos = data_.begin() + index;
+ if (pos != data_.end()) {
+ data_.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+KeyParameter& AuthorizationSet::operator[](int at) {
+ return data_[at];
+}
+
+const KeyParameter& AuthorizationSet::operator[](int at) const {
+ return data_[at];
+}
+
+void AuthorizationSet::Clear() {
+ data_.clear();
+}
+
+size_t AuthorizationSet::GetTagCount(Tag tag) const {
+ size_t count = 0;
+ for (int pos = -1; (pos = find(tag, pos)) != -1;)
+ ++count;
+ return count;
+}
+
+NullOr<const KeyParameter&> AuthorizationSet::GetEntry(Tag tag) const {
+ int pos = find(tag);
+ if (pos == -1) return {};
+ return data_[pos];
+}
+
+/**
+ * Persistent format is:
+ * | 32 bit indirect_size |
+ * --------------------------------
+ * | indirect_size bytes of data | this is where the blob data is stored
+ * --------------------------------
+ * | 32 bit element_count | number of entries
+ * | 32 bit elements_size | total bytes used by entries (entries have variable length)
+ * --------------------------------
+ * | elementes_size bytes of data | where the elements are stored
+ */
+
+/**
+ * Persistent format of blobs and bignums:
+ * | 32 bit tag |
+ * | 32 bit blob_length |
+ * | 32 bit indirect_offset |
+ */
+
+struct OutStreams {
+ std::ostream& indirect;
+ std::ostream& elements;
+};
+
+OutStreams& serializeParamValue(OutStreams& out, const hidl_vec<uint8_t>& blob) {
+ uint32_t buffer;
+
+ // write blob_length
+ auto blob_length = blob.size();
+ if (blob_length > std::numeric_limits<uint32_t>::max()) {
+ out.elements.setstate(std::ios_base::badbit);
+ return out;
+ }
+ buffer = blob_length;
+ out.elements.write(reinterpret_cast<const char*>(&buffer), sizeof(uint32_t));
+
+ // write indirect_offset
+ auto offset = out.indirect.tellp();
+ if (offset < 0 || offset > std::numeric_limits<uint32_t>::max() ||
+ static_cast<uint32_t>((std::numeric_limits<uint32_t>::max() - offset)) <
+ blob_length) { // overflow check
+ out.elements.setstate(std::ios_base::badbit);
+ return out;
+ }
+ buffer = offset;
+ out.elements.write(reinterpret_cast<const char*>(&buffer), sizeof(uint32_t));
+
+ // write blob to indirect stream
+ if (blob_length) out.indirect.write(reinterpret_cast<const char*>(&blob[0]), blob_length);
+
+ return out;
+}
+
+template <typename T> OutStreams& serializeParamValue(OutStreams& out, const T& value) {
+ out.elements.write(reinterpret_cast<const char*>(&value), sizeof(T));
+ return out;
+}
+
+OutStreams& serialize(TAG_INVALID_t&&, OutStreams& out, const KeyParameter&) {
+ // skip invalid entries.
+ return out;
+}
+template <typename T> OutStreams& serialize(T ttag, OutStreams& out, const KeyParameter& param) {
+ out.elements.write(reinterpret_cast<const char*>(¶m.tag), sizeof(int32_t));
+ return serializeParamValue(out, accessTagValue(ttag, param));
+}
+
+template <typename... T> struct choose_serializer;
+template <typename... Tags> struct choose_serializer<MetaList<Tags...>> {
+ static OutStreams& serialize(OutStreams& out, const KeyParameter& param) {
+ return choose_serializer<Tags...>::serialize(out, param);
+ }
+};
+template <> struct choose_serializer<> {
+ static OutStreams& serialize(OutStreams& out, const KeyParameter&) { return out; }
+};
+template <TagType tag_type, Tag tag, typename... Tail>
+struct choose_serializer<TypedTag<tag_type, tag>, Tail...> {
+ static OutStreams& serialize(OutStreams& out, const KeyParameter& param) {
+ if (param.tag == tag) {
+ return V3_0::serialize(TypedTag<tag_type, tag>(), out, param);
+ } else {
+ return choose_serializer<Tail...>::serialize(out, param);
+ }
+ }
+};
+
+OutStreams& serialize(OutStreams& out, const KeyParameter& param) {
+ return choose_serializer<all_tags_t>::serialize(out, param);
+}
+
+std::ostream& serialize(std::ostream& out, const std::vector<KeyParameter>& params) {
+ std::stringstream indirect;
+ std::stringstream elements;
+ OutStreams streams = {indirect, elements};
+ for (const auto& param : params) {
+ serialize(streams, param);
+ }
+ if (indirect.bad() || elements.bad()) {
+ out.setstate(std::ios_base::badbit);
+ return out;
+ }
+ auto pos = indirect.tellp();
+ if (pos < 0 || pos > std::numeric_limits<uint32_t>::max()) {
+ out.setstate(std::ios_base::badbit);
+ return out;
+ }
+ uint32_t indirect_size = pos;
+ pos = elements.tellp();
+ if (pos < 0 || pos > std::numeric_limits<uint32_t>::max()) {
+ out.setstate(std::ios_base::badbit);
+ return out;
+ }
+ uint32_t elements_size = pos;
+ uint32_t element_count = params.size();
+
+ out.write(reinterpret_cast<const char*>(&indirect_size), sizeof(uint32_t));
+
+ pos = out.tellp();
+ if (indirect_size) out << indirect.rdbuf();
+ assert(out.tellp() - pos == indirect_size);
+
+ out.write(reinterpret_cast<const char*>(&element_count), sizeof(uint32_t));
+ out.write(reinterpret_cast<const char*>(&elements_size), sizeof(uint32_t));
+
+ pos = out.tellp();
+ if (elements_size) out << elements.rdbuf();
+ assert(out.tellp() - pos == elements_size);
+
+ return out;
+}
+
+struct InStreams {
+ std::istream& indirect;
+ std::istream& elements;
+};
+
+InStreams& deserializeParamValue(InStreams& in, hidl_vec<uint8_t>* blob) {
+ uint32_t blob_length = 0;
+ uint32_t offset = 0;
+ in.elements.read(reinterpret_cast<char*>(&blob_length), sizeof(uint32_t));
+ blob->resize(blob_length);
+ in.elements.read(reinterpret_cast<char*>(&offset), sizeof(uint32_t));
+ in.indirect.seekg(offset);
+ in.indirect.read(reinterpret_cast<char*>(&(*blob)[0]), blob->size());
+ return in;
+}
+
+template <typename T> InStreams& deserializeParamValue(InStreams& in, T* value) {
+ in.elements.read(reinterpret_cast<char*>(value), sizeof(T));
+ return in;
+}
+
+InStreams& deserialize(TAG_INVALID_t&&, InStreams& in, KeyParameter*) {
+ // there should be no invalid KeyParamaters but if handle them as zero sized.
+ return in;
+}
+
+template <typename T> InStreams& deserialize(T&& ttag, InStreams& in, KeyParameter* param) {
+ return deserializeParamValue(in, &accessTagValue(ttag, *param));
+}
+
+template <typename... T> struct choose_deserializer;
+template <typename... Tags> struct choose_deserializer<MetaList<Tags...>> {
+ static InStreams& deserialize(InStreams& in, KeyParameter* param) {
+ return choose_deserializer<Tags...>::deserialize(in, param);
+ }
+};
+template <> struct choose_deserializer<> {
+ static InStreams& deserialize(InStreams& in, KeyParameter*) {
+ // encountered an unknown tag -> fail parsing
+ in.elements.setstate(std::ios_base::badbit);
+ return in;
+ }
+};
+template <TagType tag_type, Tag tag, typename... Tail>
+struct choose_deserializer<TypedTag<tag_type, tag>, Tail...> {
+ static InStreams& deserialize(InStreams& in, KeyParameter* param) {
+ if (param->tag == tag) {
+ return V3_0::deserialize(TypedTag<tag_type, tag>(), in, param);
+ } else {
+ return choose_deserializer<Tail...>::deserialize(in, param);
+ }
+ }
+};
+
+InStreams& deserialize(InStreams& in, KeyParameter* param) {
+ in.elements.read(reinterpret_cast<char*>(¶m->tag), sizeof(Tag));
+ return choose_deserializer<all_tags_t>::deserialize(in, param);
+}
+
+std::istream& deserialize(std::istream& in, std::vector<KeyParameter>* params) {
+ uint32_t indirect_size = 0;
+ in.read(reinterpret_cast<char*>(&indirect_size), sizeof(uint32_t));
+ std::string indirect_buffer(indirect_size, '\0');
+ if (indirect_buffer.size() != indirect_size) {
+ in.setstate(std::ios_base::badbit);
+ return in;
+ }
+ in.read(&indirect_buffer[0], indirect_buffer.size());
+
+ uint32_t element_count = 0;
+ in.read(reinterpret_cast<char*>(&element_count), sizeof(uint32_t));
+ uint32_t elements_size = 0;
+ in.read(reinterpret_cast<char*>(&elements_size), sizeof(uint32_t));
+
+ std::string elements_buffer(elements_size, '\0');
+ if (elements_buffer.size() != elements_size) {
+ in.setstate(std::ios_base::badbit);
+ return in;
+ }
+ in.read(&elements_buffer[0], elements_buffer.size());
+
+ if (in.bad()) return in;
+
+ // TODO write one-shot stream buffer to avoid copying here
+ std::stringstream indirect(indirect_buffer);
+ std::stringstream elements(elements_buffer);
+ InStreams streams = {indirect, elements};
+
+ params->resize(element_count);
+
+ for (uint32_t i = 0; i < element_count; ++i) {
+ deserialize(streams, &(*params)[i]);
+ }
+ return in;
+}
+
+void AuthorizationSet::Serialize(std::ostream* out) const {
+ serialize(*out, data_);
+}
+
+void AuthorizationSet::Deserialize(std::istream* in) {
+ deserialize(*in, &data_);
+}
+
+} // namespace V3_0
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
diff --git a/keymaster/3.0/vts/functional/authorization_set.h b/keymaster/3.0/vts/functional/authorization_set.h
new file mode 100644
index 0000000..5f92d81
--- /dev/null
+++ b/keymaster/3.0/vts/functional/authorization_set.h
@@ -0,0 +1,420 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#ifndef HARDWARE_INTERFACES_KEYMASTER_30_VTS_FUNCTIONAL_AUTHORIZATION_SET_H_
+#define HARDWARE_INTERFACES_KEYMASTER_30_VTS_FUNCTIONAL_AUTHORIZATION_SET_H_
+
+#include "keymaster_tags.h"
+
+#include <utility>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V3_0 {
+
+class AuthorizationSetBuilder;
+
+/**
+ * An ordered collection of KeyParameters. It provides memory ownership and some convenient
+ * functionality for sorting, deduplicating, joining, and subtracting sets of KeyParameters.
+ * For serialization, wrap the backing store of this structure in a hidl_vec<KeyParameter>.
+ */
+class AuthorizationSet {
+ public:
+ typedef KeyParameter value_type;
+
+ /**
+ * Construct an empty, dynamically-allocated, growable AuthorizationSet.
+ */
+ AuthorizationSet(){};
+
+ // Copy constructor.
+ AuthorizationSet(const AuthorizationSet& other) : data_(other.data_) {}
+
+ // Move constructor.
+ AuthorizationSet(AuthorizationSet&& other) : data_(std::move(other.data_)) {}
+
+ // Constructor from hidl_vec<KeyParameter>
+ AuthorizationSet(const hidl_vec<KeyParameter>& other) { *this = other; }
+
+ // Copy assignment.
+ AuthorizationSet& operator=(const AuthorizationSet& other) {
+ data_ = other.data_;
+ return *this;
+ }
+
+ // Move assignment.
+ AuthorizationSet& operator=(AuthorizationSet&& other) {
+ data_ = std::move(other.data_);
+ return *this;
+ }
+
+ AuthorizationSet& operator=(const hidl_vec<KeyParameter>& other) {
+ if (other.size() > 0) {
+ data_.resize(other.size());
+ for (size_t i = 0; i < data_.size(); ++i) {
+ /* This makes a deep copy even of embedded blobs.
+ * See assignment operator/copy constructor of hidl_vec.*/
+ data_[i] = other[i];
+ }
+ }
+ return *this;
+ }
+
+ /**
+ * Clear existing authorization set data
+ */
+ void Clear();
+
+ ~AuthorizationSet() = default;
+
+ /**
+ * Returns the size of the set.
+ */
+ size_t size() const { return data_.size(); }
+
+ /**
+ * Returns true if the set is empty.
+ */
+ bool empty() const { return size() == 0; }
+
+ /**
+ * Returns the data in the set, directly. Be careful with this.
+ */
+ const KeyParameter* data() const { return data_.data(); }
+
+ /**
+ * Sorts the set
+ */
+ void Sort();
+
+ /**
+ * Sorts the set and removes duplicates (inadvertently duplicating tags is easy to do with the
+ * AuthorizationSetBuilder).
+ */
+ void Deduplicate();
+
+ /**
+ * Adds all elements from \p set that are not already present in this AuthorizationSet. As a
+ * side-effect, if \p set is not null this AuthorizationSet will end up sorted.
+ */
+ void Union(const AuthorizationSet& set);
+
+ /**
+ * Removes all elements in \p set from this AuthorizationSet.
+ */
+ void Subtract(const AuthorizationSet& set);
+
+ /**
+ * Returns the offset of the next entry that matches \p tag, starting from the element after \p
+ * begin. If not found, returns -1.
+ */
+ int find(Tag tag, int begin = -1) const;
+
+ /**
+ * Removes the entry at the specified index. Returns true if successful, false if the index was
+ * out of bounds.
+ */
+ bool erase(int index);
+
+ /**
+ * Returns iterator (pointer) to beginning of elems array, to enable STL-style iteration
+ */
+ std::vector<KeyParameter>::const_iterator begin() const { return data_.begin(); }
+
+ /**
+ * Returns iterator (pointer) one past end of elems array, to enable STL-style iteration
+ */
+ std::vector<KeyParameter>::const_iterator end() const { return data_.end(); }
+
+ /**
+ * Returns the nth element of the set.
+ * Like for std::vector::operator[] there is no range check performed. Use of out of range
+ * indices is undefined.
+ */
+ KeyParameter& operator[](int n);
+
+ /**
+ * Returns the nth element of the set.
+ * Like for std::vector::operator[] there is no range check performed. Use of out of range
+ * indices is undefined.
+ */
+ const KeyParameter& operator[](int n) const;
+
+ /**
+ * Returns true if the set contains at least one instance of \p tag
+ */
+ bool Contains(Tag tag) const { return find(tag) != -1; }
+
+ template <typename T> bool Contains(T tag) const { return find(tag) != -1; }
+
+ template <TagType tag_type, Tag tag, typename ValueT>
+ bool Contains(TypedTag<tag_type, tag> ttag, const ValueT& value) const {
+ for (const auto& param : data_) {
+ auto entry = authorizationValue(ttag, param);
+ if (entry.isOk() && static_cast<ValueT>(entry.value()) == value) return true;
+ }
+ return false;
+ }
+ /**
+ * Returns the number of \p tag entries.
+ */
+ size_t GetTagCount(Tag tag) const;
+
+ template <typename T>
+ inline NullOr<const typename TypedTag2ValueType<T>::type&> GetTagValue(T tag) const {
+ auto entry = GetEntry(tag);
+ if (entry.isOk()) return authorizationValue(tag, entry.value());
+ return {};
+ }
+
+ void push_back(const KeyParameter& param) { data_.push_back(param); }
+ void push_back(KeyParameter&& param) { data_.push_back(std::move(param)); }
+
+ void push_back(const AuthorizationSet& set) {
+ for (auto& entry : set) {
+ push_back(entry);
+ }
+ }
+
+ void push_back(AuthorizationSet&& set) {
+ move(set.begin(), set.end());
+ set.Clear();
+ }
+
+ template <Tag tag>
+ void push_back(TypedTag<TagType::BYTES, tag> ttag, const uint8_t* data, size_t data_length) {
+ hidl_vec<uint8_t> new_blob;
+ new_blob.setToExternal(const_cast<uint8_t*>(data), data_length);
+ push_back(ttag, std::move(new_blob));
+ }
+
+ /**
+ * Append the tag and enumerated value to the set.
+ * "val" may be exactly one parameter unless a boolean parameter is added.
+ * In this case "val" is omitted. This condition is checked at compile time by Authorization()
+ */
+ template <typename TypedTagT, typename... Value> void push_back(TypedTagT tag, Value&&... val) {
+ push_back(Authorization(tag, std::forward<Value>(val)...));
+ }
+
+ template <typename Iterator> void push_back(Iterator begin, Iterator end) {
+ while (begin != end) {
+ push_back(*begin);
+ ++begin;
+ }
+ }
+
+ template <typename Iterator> void move(Iterator begin, Iterator end) {
+ std::move(begin, end, std::back_inserter(data_));
+ }
+
+ hidl_vec<KeyParameter> hidl_data() const {
+ hidl_vec<KeyParameter> result;
+ result.setToExternal(const_cast<KeyParameter*>(data()), size());
+ return result;
+ }
+
+ void Serialize(std::ostream* out) const;
+ void Deserialize(std::istream* in);
+
+ private:
+ NullOr<const KeyParameter&> GetEntry(Tag tag) const;
+
+ std::vector<KeyParameter> data_;
+};
+
+class AuthorizationSetBuilder : public AuthorizationSet {
+ public:
+ template <typename TagType, typename... ValueType>
+ AuthorizationSetBuilder& Authorization(TagType ttag, ValueType&&... value) {
+ push_back(ttag, std::forward<ValueType>(value)...);
+ return *this;
+ }
+
+ template <Tag tag>
+ AuthorizationSetBuilder& Authorization(TypedTag<TagType::BYTES, tag> ttag, const uint8_t* data,
+ size_t data_length) {
+ hidl_vec<uint8_t> new_blob;
+ new_blob.setToExternal(const_cast<uint8_t*>(data), data_length);
+ push_back(ttag, std::move(new_blob));
+ return *this;
+ }
+
+ template <Tag tag>
+ AuthorizationSetBuilder& Authorization(TypedTag<TagType::BYTES, tag> ttag, const char* data,
+ size_t data_length) {
+ return Authorization(ttag, reinterpret_cast<const uint8_t*>(data), data_length);
+ }
+
+ AuthorizationSetBuilder& Authorizations(AuthorizationSet&& set);
+ AuthorizationSetBuilder& Authorizations(const AuthorizationSet& set);
+
+ AuthorizationSetBuilder& RsaKey(uint32_t key_size, uint64_t public_exponent);
+ AuthorizationSetBuilder& EcdsaKey(uint32_t key_size);
+ AuthorizationSetBuilder& EcdsaKey(EcCurve curve);
+ AuthorizationSetBuilder& AesKey(uint32_t key_size);
+ AuthorizationSetBuilder& HmacKey(uint32_t key_size);
+
+ AuthorizationSetBuilder& RsaSigningKey(uint32_t key_size, uint64_t public_exponent);
+ AuthorizationSetBuilder& RsaEncryptionKey(uint32_t key_size, uint64_t public_exponent);
+ AuthorizationSetBuilder& EcdsaSigningKey(uint32_t key_size);
+ AuthorizationSetBuilder& EcdsaSigningKey(EcCurve curve);
+ AuthorizationSetBuilder& AesEncryptionKey(uint32_t key_size);
+
+ AuthorizationSetBuilder& SigningKey();
+ AuthorizationSetBuilder& EncryptionKey();
+ AuthorizationSetBuilder& NoDigestOrPadding();
+ AuthorizationSetBuilder& EcbMode();
+
+ AuthorizationSetBuilder& BlockMode(std::initializer_list<BlockMode> block_modes);
+ AuthorizationSetBuilder& Digest(std::initializer_list<Digest> digests);
+ AuthorizationSetBuilder& Padding(std::initializer_list<PaddingMode> padding_modes);
+
+ // The following forwarding templates enable BlockMode,Digest and Padding to be called with a
+ // variable number of arguments; no need to wrap them in braces to make them an initalizer_list.
+ template <typename... T> AuthorizationSetBuilder& BlockMode(T&&... a) {
+ return BlockMode({std::forward<T>(a)...});
+ }
+ template <typename... T> AuthorizationSetBuilder& Digest(T&&... a) {
+ return Digest({std::forward<T>(a)...});
+ }
+ template <typename... T> AuthorizationSetBuilder& Padding(T&&... a) {
+ return Padding({std::forward<T>(a)...});
+ }
+};
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::Authorizations(AuthorizationSet&& set) {
+ move(set.begin(), set.end());
+ set.Clear();
+ return *this;
+}
+
+inline AuthorizationSetBuilder&
+AuthorizationSetBuilder::Authorizations(const AuthorizationSet& set) {
+ push_back(set.begin(), set.end());
+ return *this;
+}
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::RsaKey(uint32_t key_size,
+ uint64_t public_exponent) {
+ Authorization(TAG_ALGORITHM, Algorithm::RSA);
+ Authorization(TAG_KEY_SIZE, key_size);
+ Authorization(TAG_RSA_PUBLIC_EXPONENT, public_exponent);
+ return *this;
+}
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(uint32_t key_size) {
+ Authorization(TAG_ALGORITHM, Algorithm::EC);
+ Authorization(TAG_KEY_SIZE, key_size);
+ return *this;
+}
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(EcCurve curve) {
+ Authorization(TAG_ALGORITHM, Algorithm::EC);
+ Authorization(TAG_EC_CURVE, curve);
+ return *this;
+}
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::AesKey(uint32_t key_size) {
+ Authorization(TAG_ALGORITHM, Algorithm::AES);
+ return Authorization(TAG_KEY_SIZE, key_size);
+}
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::HmacKey(uint32_t key_size) {
+ Authorization(TAG_ALGORITHM, Algorithm::HMAC);
+ Authorization(TAG_KEY_SIZE, key_size);
+ return SigningKey();
+}
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::RsaSigningKey(uint32_t key_size,
+ uint64_t public_exponent) {
+ RsaKey(key_size, public_exponent);
+ return SigningKey();
+}
+
+inline AuthorizationSetBuilder&
+AuthorizationSetBuilder::RsaEncryptionKey(uint32_t key_size, uint64_t public_exponent) {
+ RsaKey(key_size, public_exponent);
+ return EncryptionKey();
+}
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(uint32_t key_size) {
+ EcdsaKey(key_size);
+ return SigningKey();
+}
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(EcCurve curve) {
+ EcdsaKey(curve);
+ return SigningKey();
+}
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::AesEncryptionKey(uint32_t key_size) {
+ AesKey(key_size);
+ return EncryptionKey();
+}
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::SigningKey() {
+ Authorization(TAG_PURPOSE, KeyPurpose::SIGN);
+ return Authorization(TAG_PURPOSE, KeyPurpose::VERIFY);
+}
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::EncryptionKey() {
+ Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT);
+ return Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT);
+}
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::NoDigestOrPadding() {
+ Authorization(TAG_DIGEST, Digest::NONE);
+ return Authorization(TAG_PADDING, PaddingMode::NONE);
+}
+
+inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcbMode() {
+ return BlockMode(BlockMode::ECB);
+}
+
+inline AuthorizationSetBuilder&
+AuthorizationSetBuilder::BlockMode(std::initializer_list<V3_0::BlockMode> block_modes) {
+ for (auto block_mode : block_modes) {
+ Authorization(TAG_BLOCK_MODE, block_mode);
+ }
+ return *this;
+}
+
+inline AuthorizationSetBuilder&
+AuthorizationSetBuilder::Digest(std::initializer_list<V3_0::Digest> digests) {
+ for (auto digest : digests) {
+ Authorization(TAG_DIGEST, digest);
+ }
+ return *this;
+}
+
+inline AuthorizationSetBuilder&
+AuthorizationSetBuilder::Padding(std::initializer_list<V3_0::PaddingMode> padding_modes) {
+ for (auto padding : padding_modes) {
+ Authorization(TAG_PADDING, padding);
+ }
+ return *this;
+}
+
+} // namespace V3_0
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
+
+#endif // HARDWARE_INTERFACES_KEYMASTER_30_VTS_FUNCTIONAL_AUTHORIZATION_SET_H_
diff --git a/keymaster/3.0/vts/functional/key_param_output.cpp b/keymaster/3.0/vts/functional/key_param_output.cpp
new file mode 100644
index 0000000..fc9f685
--- /dev/null
+++ b/keymaster/3.0/vts/functional/key_param_output.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "key_param_output.h"
+
+#include <iomanip>
+
+namespace android {
+namespace hardware {
+
+namespace keymaster {
+namespace V3_0 {
+
+::std::ostream& operator<<(::std::ostream& os, const hidl_vec<KeyParameter>& set) {
+ if (set.size() == 0) {
+ os << "(Empty)" << ::std::endl;
+ } else {
+ os << "\n";
+ for (size_t i = 0; i < set.size(); ++i)
+ os << set[i] << ::std::endl;
+ }
+ return os;
+}
+
+::std::ostream& operator<<(::std::ostream& os, ErrorCode value) {
+ return os << (int)value;
+}
+
+::std::ostream& operator<<(::std::ostream& os, Digest value) {
+ return os << stringify(value);
+}
+
+::std::ostream& operator<<(::std::ostream& os, Algorithm value) {
+ return os << stringify(value);
+}
+
+::std::ostream& operator<<(::std::ostream& os, BlockMode value) {
+ return os << stringify(value);
+}
+
+::std::ostream& operator<<(::std::ostream& os, PaddingMode value) {
+ return os << stringify(value);
+}
+
+::std::ostream& operator<<(::std::ostream& os, KeyOrigin value) {
+ return os << stringify(value);
+}
+
+::std::ostream& operator<<(::std::ostream& os, KeyPurpose value) {
+ return os << stringify(value);
+}
+
+::std::ostream& operator<<(::std::ostream& os, EcCurve value) {
+ return os << stringify(value);
+}
+
+::std::ostream& operator<<(::std::ostream& os, const KeyParameter& param) {
+ os << stringifyTag(param.tag) << ": ";
+ switch (typeFromTag(param.tag)) {
+ case TagType::INVALID:
+ return os << " Invalid";
+ case TagType::UINT_REP:
+ case TagType::UINT:
+ return os << param.f.integer;
+ case TagType::ENUM_REP:
+ case TagType::ENUM:
+ switch (param.tag) {
+ case Tag::ALGORITHM:
+ return os << param.f.algorithm;
+ case Tag::BLOCK_MODE:
+ return os << param.f.blockMode;
+ case Tag::PADDING:
+ return os << param.f.paddingMode;
+ case Tag::DIGEST:
+ return os << param.f.digest;
+ case Tag::EC_CURVE:
+ return os << (int)param.f.ecCurve;
+ case Tag::ORIGIN:
+ return os << param.f.origin;
+ case Tag::BLOB_USAGE_REQUIREMENTS:
+ return os << (int)param.f.keyBlobUsageRequirements;
+ case Tag::PURPOSE:
+ return os << param.f.purpose;
+ default:
+ return os << " UNKNOWN ENUM " << param.f.integer;
+ }
+ case TagType::ULONG_REP:
+ case TagType::ULONG:
+ return os << param.f.longInteger;
+ case TagType::DATE:
+ return os << param.f.dateTime;
+ case TagType::BOOL:
+ return os << "true";
+ case TagType::BIGNUM:
+ os << " Bignum: ";
+ for (size_t i = 0; i < param.blob.size(); ++i) {
+ os << ::std::hex << ::std::setw(2) << static_cast<int>(param.blob[i]) << ::std::dec;
+ }
+ return os;
+ case TagType::BYTES:
+ os << " Bytes: ";
+ for (size_t i = 0; i < param.blob.size(); ++i) {
+ os << ::std::hex << ::std::setw(2) << static_cast<int>(param.blob[i]) << ::std::dec;
+ }
+ return os;
+ }
+ return os << "UNKNOWN TAG TYPE!";
+}
+
+::std::ostream& operator<<(::std::ostream& os, const KeyCharacteristics& chars) {
+ return os << "SW: " << chars.softwareEnforced << ::std::endl
+ << "TEE: " << chars.teeEnforced << ::std::endl;
+}
+
+} // namespace V3_0
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
diff --git a/keymaster/3.0/vts/functional/key_param_output.h b/keymaster/3.0/vts/functional/key_param_output.h
new file mode 100644
index 0000000..5edec2d
--- /dev/null
+++ b/keymaster/3.0/vts/functional/key_param_output.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <iostream>
+
+#include <android/hardware/keymaster/3.0/types.h>
+
+#include "keymaster_tags.h"
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V3_0 {
+
+template <typename ValueT>
+::std::ostream& operator<<(::std::ostream& os, const NullOr<ValueT>& value) {
+ if (!value.isOk()) {
+ os << "(value not present)";
+ } else {
+ os << value.value();
+ }
+ return os;
+}
+
+::std::ostream& operator<<(::std::ostream& os, const hidl_vec<KeyParameter>& set);
+::std::ostream& operator<<(::std::ostream& os, BlockMode value);
+::std::ostream& operator<<(::std::ostream& os, Digest value);
+::std::ostream& operator<<(::std::ostream& os, EcCurve value);
+::std::ostream& operator<<(::std::ostream& os, ErrorCode value);
+::std::ostream& operator<<(::std::ostream& os, PaddingMode value);
+::std::ostream& operator<<(::std::ostream& os, const KeyCharacteristics& value);
+::std::ostream& operator<<(::std::ostream& os, const KeyParameter& value);
+
+} // namespace V3_0
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
diff --git a/keymaster/3.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/3.0/vts/functional/keymaster_hidl_hal_test.cpp
new file mode 100644
index 0000000..2382f0b
--- /dev/null
+++ b/keymaster/3.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -0,0 +1,3911 @@
+/*
+ * Copyright (C) 2016 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 "keymaster_hidl_hal_test"
+#include <cutils/log.h>
+
+#include <iostream>
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
+#include <android/hardware/keymaster/3.0/types.h>
+
+#include <cutils/properties.h>
+
+#include <keymaster/keymaster_configuration.h>
+
+#include "authorization_set.h"
+#include "key_param_output.h"
+
+#include <VtsHalHidlTargetTestBase.h>
+
+#include "attestation_record.h"
+#include "openssl_utils.h"
+
+using ::android::sp;
+
+using ::std::string;
+
+// This service_name will be passed to getService when retrieving the keymaster service to test. To
+// change it from "default" specify the selected service name on the command line. The first
+// non-gtest argument will be used as the service name.
+string service_name = "default";
+
+namespace android {
+namespace hardware {
+
+template <typename T> bool operator==(const hidl_vec<T>& a, const hidl_vec<T>& b) {
+ if (a.size() != b.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < a.size(); ++i) {
+ if (a[i] != b[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+namespace keymaster {
+namespace V3_0 {
+
+bool operator==(const KeyParameter& a, const KeyParameter& b) {
+ if (a.tag != b.tag) {
+ return false;
+ }
+
+ switch (a.tag) {
+
+ /* Boolean tags */
+ case Tag::INVALID:
+ case Tag::CALLER_NONCE:
+ case Tag::INCLUDE_UNIQUE_ID:
+ case Tag::ECIES_SINGLE_HASH_MODE:
+ case Tag::BOOTLOADER_ONLY:
+ case Tag::NO_AUTH_REQUIRED:
+ case Tag::ALLOW_WHILE_ON_BODY:
+ case Tag::EXPORTABLE:
+ case Tag::ALL_APPLICATIONS:
+ case Tag::ROLLBACK_RESISTANT:
+ case Tag::RESET_SINCE_ID_ROTATION:
+ return true;
+
+ /* Integer tags */
+ case Tag::KEY_SIZE:
+ case Tag::MIN_MAC_LENGTH:
+ case Tag::MIN_SECONDS_BETWEEN_OPS:
+ case Tag::MAX_USES_PER_BOOT:
+ case Tag::ALL_USERS:
+ case Tag::USER_ID:
+ case Tag::OS_VERSION:
+ case Tag::OS_PATCHLEVEL:
+ case Tag::MAC_LENGTH:
+ case Tag::AUTH_TIMEOUT:
+ return a.f.integer == b.f.integer;
+
+ /* Long integer tags */
+ case Tag::RSA_PUBLIC_EXPONENT:
+ case Tag::USER_SECURE_ID:
+ return a.f.longInteger == b.f.longInteger;
+
+ /* Date-time tags */
+ case Tag::ACTIVE_DATETIME:
+ case Tag::ORIGINATION_EXPIRE_DATETIME:
+ case Tag::USAGE_EXPIRE_DATETIME:
+ case Tag::CREATION_DATETIME:
+ return a.f.dateTime == b.f.dateTime;
+
+ /* Bytes tags */
+ case Tag::APPLICATION_ID:
+ case Tag::APPLICATION_DATA:
+ case Tag::ROOT_OF_TRUST:
+ case Tag::UNIQUE_ID:
+ case Tag::ATTESTATION_CHALLENGE:
+ case Tag::ATTESTATION_APPLICATION_ID:
+ case Tag::ATTESTATION_ID_BRAND:
+ case Tag::ATTESTATION_ID_DEVICE:
+ case Tag::ATTESTATION_ID_PRODUCT:
+ case Tag::ATTESTATION_ID_SERIAL:
+ case Tag::ATTESTATION_ID_IMEI:
+ case Tag::ATTESTATION_ID_MEID:
+ case Tag::ATTESTATION_ID_MANUFACTURER:
+ case Tag::ATTESTATION_ID_MODEL:
+ case Tag::ASSOCIATED_DATA:
+ case Tag::NONCE:
+ case Tag::AUTH_TOKEN:
+ return a.blob == b.blob;
+
+ /* Enum tags */
+ case Tag::PURPOSE:
+ return a.f.purpose == b.f.purpose;
+ case Tag::ALGORITHM:
+ return a.f.algorithm == b.f.algorithm;
+ case Tag::BLOCK_MODE:
+ return a.f.blockMode == b.f.blockMode;
+ case Tag::DIGEST:
+ return a.f.digest == b.f.digest;
+ case Tag::PADDING:
+ return a.f.paddingMode == b.f.paddingMode;
+ case Tag::EC_CURVE:
+ return a.f.ecCurve == b.f.ecCurve;
+ case Tag::BLOB_USAGE_REQUIREMENTS:
+ return a.f.keyBlobUsageRequirements == b.f.keyBlobUsageRequirements;
+ case Tag::USER_AUTH_TYPE:
+ return a.f.integer == b.f.integer;
+ case Tag::ORIGIN:
+ return a.f.origin == b.f.origin;
+
+ /* Unsupported tags */
+ case Tag::KDF:
+ return false;
+ }
+}
+
+bool operator==(const AuthorizationSet& a, const AuthorizationSet& b) {
+ return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
+}
+
+bool operator==(const KeyCharacteristics& a, const KeyCharacteristics& b) {
+ // This isn't very efficient. Oh, well.
+ AuthorizationSet a_sw(a.softwareEnforced);
+ AuthorizationSet b_sw(b.softwareEnforced);
+ AuthorizationSet a_tee(b.teeEnforced);
+ AuthorizationSet b_tee(b.teeEnforced);
+
+ a_sw.Sort();
+ b_sw.Sort();
+ a_tee.Sort();
+ b_tee.Sort();
+
+ return a_sw == b_sw && a_tee == b_sw;
+}
+
+::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set) {
+ if (set.size() == 0)
+ os << "(Empty)" << ::std::endl;
+ else {
+ os << "\n";
+ for (size_t i = 0; i < set.size(); ++i)
+ os << set[i] << ::std::endl;
+ }
+ return os;
+}
+
+namespace test {
+namespace {
+
+template <TagType tag_type, Tag tag, typename ValueT>
+bool contains(hidl_vec<KeyParameter>& set, TypedTag<tag_type, tag> ttag, ValueT expected_value) {
+ size_t count = std::count_if(set.begin(), set.end(), [&](const KeyParameter& param) {
+ return param.tag == tag && accessTagValue(ttag, param) == expected_value;
+ });
+ return count == 1;
+}
+
+template <TagType tag_type, Tag tag>
+bool contains(hidl_vec<KeyParameter>& set, TypedTag<tag_type, tag>) {
+ size_t count = std::count_if(set.begin(), set.end(),
+ [&](const KeyParameter& param) { return param.tag == tag; });
+ return count > 0;
+}
+
+constexpr char hex_value[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // '0'..'9'
+ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'A'..'F'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'a'..'f'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+string hex2str(string a) {
+ string b;
+ size_t num = a.size() / 2;
+ b.resize(num);
+ for (size_t i = 0; i < num; i++) {
+ b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]);
+ }
+ return b;
+}
+
+string rsa_key = hex2str("30820275020100300d06092a864886f70d01010105000482025f3082025b"
+ "02010002818100c6095409047d8634812d5a218176e45c41d60a75b13901"
+ "f234226cffe776521c5a77b9e389417b71c0b6a44d13afe4e4a2805d46c9"
+ "da2935adb1ff0c1f24ea06e62b20d776430a4d435157233c6f916783c30e"
+ "310fcbd89b85c2d56771169785ac12bca244abda72bfb19fc44d27c81e1d"
+ "92de284f4061edfd99280745ea6d2502030100010281801be0f04d9cae37"
+ "18691f035338308e91564b55899ffb5084d2460e6630257e05b3ceab0297"
+ "2dfabcd6ce5f6ee2589eb67911ed0fac16e43a444b8c861e544a05933657"
+ "72f8baf6b22fc9e3c5f1024b063ac080a7b2234cf8aee8f6c47bbf4fd3ac"
+ "e7240290bef16c0b3f7f3cdd64ce3ab5912cf6e32f39ab188358afcccd80"
+ "81024100e4b49ef50f765d3b24dde01aceaaf130f2c76670a91a61ae08af"
+ "497b4a82be6dee8fcdd5e3f7ba1cfb1f0c926b88f88c92bfab137fba2285"
+ "227b83c342ff7c55024100ddabb5839c4c7f6bf3d4183231f005b31aa58a"
+ "ffdda5c79e4cce217f6bc930dbe563d480706c24e9ebfcab28a6cdefd324"
+ "b77e1bf7251b709092c24ff501fd91024023d4340eda3445d8cd26c14411"
+ "da6fdca63c1ccd4b80a98ad52b78cc8ad8beb2842c1d280405bc2f6c1bea"
+ "214a1d742ab996b35b63a82a5e470fa88dbf823cdd02401b7b57449ad30d"
+ "1518249a5f56bb98294d4b6ac12ffc86940497a5a5837a6cf946262b4945"
+ "26d328c11e1126380fde04c24f916dec250892db09a6d77cdba351024077"
+ "62cd8f4d050da56bd591adb515d24d7ccd32cca0d05f866d583514bd7324"
+ "d5f33645e8ed8b4a1cb3cc4a1d67987399f2a09f5b3fb68c88d5e5d90ac3"
+ "3492d6");
+
+string ec_key = hex2str("308187020100301306072a8648ce3d020106082a8648ce3d030107046d30"
+ "6b0201010420737c2ecd7b8d1940bf2930aa9b4ed3ff941eed09366bc032"
+ "99986481f3a4d859a14403420004bf85d7720d07c25461683bc648b4778a"
+ "9a14dd8a024e3bdd8c7ddd9ab2b528bbc7aa1b51f14ebbbb0bd0ce21bcc4"
+ "1c6eb00083cf3376d11fd44949e0b2183bfe");
+
+struct RSA_Delete {
+ void operator()(RSA* p) { RSA_free(p); }
+};
+
+X509* parse_cert_blob(const hidl_vec<uint8_t>& blob) {
+ const uint8_t* p = blob.data();
+ return d2i_X509(nullptr, &p, blob.size());
+}
+
+bool verify_chain(const hidl_vec<hidl_vec<uint8_t>>& chain) {
+ for (size_t i = 0; i < chain.size() - 1; ++i) {
+ auto& key_cert_blob = chain[i];
+ auto& signing_cert_blob = chain[i + 1];
+
+ X509_Ptr key_cert(parse_cert_blob(key_cert_blob));
+ X509_Ptr signing_cert(parse_cert_blob(signing_cert_blob));
+ EXPECT_TRUE(!!key_cert.get() && !!signing_cert.get());
+ if (!key_cert.get() || !signing_cert.get()) return false;
+
+ EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
+ EXPECT_TRUE(!!signing_pubkey.get());
+ if (!signing_pubkey.get()) return false;
+
+ EXPECT_EQ(1, X509_verify(key_cert.get(), signing_pubkey.get()))
+ << "Verification of certificate " << i << " failed";
+ }
+
+ return true;
+}
+
+// Extract attestation record from cert. Returned object is still part of cert; don't free it
+// separately.
+ASN1_OCTET_STRING* get_attestation_record(X509* certificate) {
+ ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1 /* dotted string format */));
+ EXPECT_TRUE(!!oid.get());
+ if (!oid.get()) return nullptr;
+
+ int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1 /* search from beginning */);
+ EXPECT_NE(-1, location);
+ if (location == -1) return nullptr;
+
+ X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location);
+ EXPECT_TRUE(!!attest_rec_ext);
+ if (!attest_rec_ext) return nullptr;
+
+ ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
+ EXPECT_TRUE(!!attest_rec);
+ return attest_rec;
+}
+
+bool tag_in_list(const KeyParameter& entry) {
+ // Attestations don't contain everything in key authorization lists, so we need to filter
+ // the key lists to produce the lists that we expect to match the attestations.
+ auto tag_list = {
+ Tag::USER_ID, Tag::INCLUDE_UNIQUE_ID, Tag::BLOB_USAGE_REQUIREMENTS,
+ Tag::EC_CURVE /* Tag::EC_CURVE will be included by KM2 implementations */,
+ };
+ return std::find(tag_list.begin(), tag_list.end(), entry.tag) != tag_list.end();
+}
+
+AuthorizationSet filter_tags(const AuthorizationSet& set) {
+ AuthorizationSet filtered;
+ std::remove_copy_if(set.begin(), set.end(), std::back_inserter(filtered), tag_in_list);
+ return filtered;
+}
+
+std::string make_string(const uint8_t* data, size_t length) {
+ return std::string(reinterpret_cast<const char*>(data), length);
+}
+
+template <size_t N> std::string make_string(const uint8_t (&a)[N]) {
+ return make_string(a, N);
+}
+
+class HidlBuf : public hidl_vec<uint8_t> {
+ typedef hidl_vec<uint8_t> super;
+
+ public:
+ HidlBuf() {}
+ HidlBuf(const super& other) : super(other) {}
+ HidlBuf(super&& other) : super(std::move(other)) {}
+ explicit HidlBuf(const std::string& other) : HidlBuf() { *this = other; }
+
+ HidlBuf& operator=(const super& other) {
+ super::operator=(other);
+ return *this;
+ }
+
+ HidlBuf& operator=(super&& other) {
+ super::operator=(std::move(other));
+ return *this;
+ }
+
+ HidlBuf& operator=(const string& other) {
+ resize(other.size());
+ for (size_t i = 0; i < other.size(); ++i) {
+ (*this)[i] = static_cast<uint8_t>(other[i]);
+ }
+ return *this;
+ }
+
+ string to_string() const { return string(reinterpret_cast<const char*>(data()), size()); }
+};
+
+constexpr uint64_t kOpHandleSentinel = 0xFFFFFFFFFFFFFFFF;
+
+} // namespace
+
+class KeymasterHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+ void TearDown() override {
+ if (key_blob_.size()) {
+ EXPECT_EQ(ErrorCode::OK, DeleteKey());
+ }
+ AbortIfNeeded();
+ }
+
+ // SetUpTestCase runs only once per test case, not once per test.
+ static void SetUpTestCase() {
+ keymaster_ = IKeymasterDevice::getService(service_name);
+ ASSERT_NE(keymaster_, nullptr);
+
+ ASSERT_TRUE(
+ keymaster_
+ ->getHardwareFeatures([&](bool isSecure, bool supportsEc, bool supportsSymmetric,
+ bool supportsAttestation, bool supportsAllDigests,
+ const hidl_string& name, const hidl_string& author) {
+ is_secure_ = isSecure;
+ supports_ec_ = supportsEc;
+ supports_symmetric_ = supportsSymmetric;
+ supports_attestation_ = supportsAttestation;
+ supports_all_digests_ = supportsAllDigests;
+ name_ = name;
+ author_ = author;
+ })
+ .isOk());
+
+ os_version_ = ::keymaster::GetOsVersion();
+ os_patch_level_ = ::keymaster::GetOsPatchlevel();
+ }
+
+ static void TearDownTestCase() { keymaster_.clear(); }
+
+ static IKeymasterDevice& keymaster() { return *keymaster_; }
+ static uint32_t os_version() { return os_version_; }
+ static uint32_t os_patch_level() { return os_patch_level_; }
+
+ AuthorizationSet UserAuths() { return AuthorizationSetBuilder().Authorization(TAG_USER_ID, 7); }
+
+ ErrorCode GenerateKey(const AuthorizationSet& key_desc, HidlBuf* key_blob,
+ KeyCharacteristics* key_characteristics) {
+ EXPECT_NE(key_blob, nullptr);
+ EXPECT_NE(key_characteristics, nullptr);
+ EXPECT_EQ(0U, key_blob->size());
+
+ ErrorCode error;
+ EXPECT_TRUE(keymaster_
+ ->generateKey(key_desc.hidl_data(),
+ [&](ErrorCode hidl_error, const HidlBuf& hidl_key_blob,
+ const KeyCharacteristics& hidl_key_characteristics) {
+ error = hidl_error;
+ *key_blob = hidl_key_blob;
+ *key_characteristics = hidl_key_characteristics;
+ })
+ .isOk());
+ // On error, blob & characteristics should be empty.
+ if (error != ErrorCode::OK) {
+ EXPECT_EQ(0U, key_blob->size());
+ EXPECT_EQ(0U, (key_characteristics->softwareEnforced.size() +
+ key_characteristics->teeEnforced.size()));
+ }
+ return error;
+ }
+
+ ErrorCode GenerateKey(const AuthorizationSet& key_desc) {
+ return GenerateKey(key_desc, &key_blob_, &key_characteristics_);
+ }
+
+ ErrorCode ImportKey(const AuthorizationSet& key_desc, KeyFormat format,
+ const string& key_material, HidlBuf* key_blob,
+ KeyCharacteristics* key_characteristics) {
+ ErrorCode error;
+ EXPECT_TRUE(keymaster_
+ ->importKey(key_desc.hidl_data(), format, HidlBuf(key_material),
+ [&](ErrorCode hidl_error, const HidlBuf& hidl_key_blob,
+ const KeyCharacteristics& hidl_key_characteristics) {
+ error = hidl_error;
+ *key_blob = hidl_key_blob;
+ *key_characteristics = hidl_key_characteristics;
+ })
+ .isOk());
+ // On error, blob & characteristics should be empty.
+ if (error != ErrorCode::OK) {
+ EXPECT_EQ(0U, key_blob->size());
+ EXPECT_EQ(0U, (key_characteristics->softwareEnforced.size() +
+ key_characteristics->teeEnforced.size()));
+ }
+ return error;
+ }
+
+ ErrorCode ImportKey(const AuthorizationSet& key_desc, KeyFormat format,
+ const string& key_material) {
+ return ImportKey(key_desc, format, key_material, &key_blob_, &key_characteristics_);
+ }
+
+ ErrorCode ExportKey(KeyFormat format, const HidlBuf& key_blob, const HidlBuf& client_id,
+ const HidlBuf& app_data, HidlBuf* key_material) {
+ ErrorCode error;
+ EXPECT_TRUE(
+ keymaster_
+ ->exportKey(format, key_blob, client_id, app_data,
+ [&](ErrorCode hidl_error_code, const HidlBuf& hidl_key_material) {
+ error = hidl_error_code;
+ *key_material = hidl_key_material;
+ })
+ .isOk());
+ // On error, blob should be empty.
+ if (error != ErrorCode::OK) {
+ EXPECT_EQ(0U, key_material->size());
+ }
+ return error;
+ }
+
+ ErrorCode ExportKey(KeyFormat format, HidlBuf* key_material) {
+ HidlBuf client_id, app_data;
+ return ExportKey(format, key_blob_, client_id, app_data, key_material);
+ }
+
+ ErrorCode DeleteKey(HidlBuf* key_blob) {
+ ErrorCode error = keymaster_->deleteKey(*key_blob);
+ *key_blob = HidlBuf();
+ return error;
+ }
+
+ ErrorCode DeleteKey() { return DeleteKey(&key_blob_); }
+
+ ErrorCode GetCharacteristics(const HidlBuf& key_blob, const HidlBuf& client_id,
+ const HidlBuf& app_data, KeyCharacteristics* key_characteristics) {
+ ErrorCode error;
+ keymaster_->getKeyCharacteristics(
+ key_blob, client_id, app_data,
+ [&](ErrorCode hidl_error, const KeyCharacteristics& hidl_key_characteristics) {
+ error = hidl_error, *key_characteristics = hidl_key_characteristics;
+ });
+ return error;
+ }
+
+ ErrorCode GetCharacteristics(const HidlBuf& key_blob, KeyCharacteristics* key_characteristics) {
+ HidlBuf client_id, app_data;
+ return GetCharacteristics(key_blob, client_id, app_data, key_characteristics);
+ }
+
+ ErrorCode Begin(KeyPurpose purpose, const HidlBuf& key_blob, const AuthorizationSet& in_params,
+ AuthorizationSet* out_params, OperationHandle* op_handle) {
+ SCOPED_TRACE("Begin");
+ ErrorCode error;
+ OperationHandle saved_handle = *op_handle;
+ EXPECT_TRUE(
+ keymaster_
+ ->begin(purpose, key_blob, in_params.hidl_data(),
+ [&](ErrorCode hidl_error, const hidl_vec<KeyParameter>& hidl_out_params,
+ uint64_t hidl_op_handle) {
+ error = hidl_error;
+ *out_params = hidl_out_params;
+ *op_handle = hidl_op_handle;
+ })
+ .isOk());
+ if (error != ErrorCode::OK) {
+ // Some implementations may modify *op_handle on error.
+ *op_handle = saved_handle;
+ }
+ return error;
+ }
+
+ ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params,
+ AuthorizationSet* out_params) {
+ SCOPED_TRACE("Begin");
+ EXPECT_EQ(kOpHandleSentinel, op_handle_);
+ return Begin(purpose, key_blob_, in_params, out_params, &op_handle_);
+ }
+
+ ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params) {
+ SCOPED_TRACE("Begin");
+ AuthorizationSet out_params;
+ ErrorCode error = Begin(purpose, in_params, &out_params);
+ EXPECT_TRUE(out_params.empty());
+ return error;
+ }
+
+ ErrorCode Update(OperationHandle op_handle, const AuthorizationSet& in_params,
+ const string& input, AuthorizationSet* out_params, string* output,
+ size_t* input_consumed) {
+ SCOPED_TRACE("Update");
+ ErrorCode error;
+ EXPECT_TRUE(keymaster_
+ ->update(op_handle, in_params.hidl_data(), HidlBuf(input),
+ [&](ErrorCode hidl_error, uint32_t hidl_input_consumed,
+ const hidl_vec<KeyParameter>& hidl_out_params,
+ const HidlBuf& hidl_output) {
+ error = hidl_error;
+ out_params->push_back(AuthorizationSet(hidl_out_params));
+ output->append(hidl_output.to_string());
+ *input_consumed = hidl_input_consumed;
+ })
+ .isOk());
+ return error;
+ }
+
+ ErrorCode Update(const string& input, string* out, size_t* input_consumed) {
+ SCOPED_TRACE("Update");
+ AuthorizationSet out_params;
+ ErrorCode error = Update(op_handle_, AuthorizationSet() /* in_params */, input, &out_params,
+ out, input_consumed);
+ EXPECT_TRUE(out_params.empty());
+ return error;
+ }
+
+ ErrorCode Finish(OperationHandle op_handle, const AuthorizationSet& in_params,
+ const string& input, const string& signature, AuthorizationSet* out_params,
+ string* output) {
+ SCOPED_TRACE("Finish");
+ ErrorCode error;
+ EXPECT_TRUE(
+ keymaster_
+ ->finish(op_handle, in_params.hidl_data(), HidlBuf(input), HidlBuf(signature),
+ [&](ErrorCode hidl_error, const hidl_vec<KeyParameter>& hidl_out_params,
+ const HidlBuf& hidl_output) {
+ error = hidl_error;
+ *out_params = hidl_out_params;
+ output->append(hidl_output.to_string());
+ })
+ .isOk());
+ op_handle_ = kOpHandleSentinel; // So dtor doesn't Abort().
+ return error;
+ }
+
+ ErrorCode Finish(const string& message, string* output) {
+ SCOPED_TRACE("Finish");
+ AuthorizationSet out_params;
+ string finish_output;
+ ErrorCode error = Finish(op_handle_, AuthorizationSet() /* in_params */, message,
+ "" /* signature */, &out_params, output);
+ if (error != ErrorCode::OK) {
+ return error;
+ }
+ EXPECT_EQ(0U, out_params.size());
+ return error;
+ }
+
+ ErrorCode Finish(const string& message, const string& signature, string* output) {
+ SCOPED_TRACE("Finish");
+ AuthorizationSet out_params;
+ ErrorCode error = Finish(op_handle_, AuthorizationSet() /* in_params */, message, signature,
+ &out_params, output);
+ op_handle_ = kOpHandleSentinel; // So dtor doesn't Abort().
+ if (error != ErrorCode::OK) {
+ return error;
+ }
+ EXPECT_EQ(0U, out_params.size());
+ return error;
+ }
+
+ ErrorCode Abort(OperationHandle op_handle) {
+ SCOPED_TRACE("Abort");
+ auto retval = keymaster_->abort(op_handle);
+ EXPECT_TRUE(retval.isOk());
+ return retval;
+ }
+
+ void AbortIfNeeded() {
+ SCOPED_TRACE("AbortIfNeeded");
+ if (op_handle_ != kOpHandleSentinel) {
+ EXPECT_EQ(ErrorCode::OK, Abort(op_handle_));
+ op_handle_ = kOpHandleSentinel;
+ }
+ }
+
+ ErrorCode AttestKey(const HidlBuf& key_blob, const AuthorizationSet& attest_params,
+ hidl_vec<hidl_vec<uint8_t>>* cert_chain) {
+ SCOPED_TRACE("AttestKey");
+ ErrorCode error;
+ keymaster_->attestKey(
+ key_blob, attest_params.hidl_data(),
+ [&](ErrorCode hidl_error, const hidl_vec<hidl_vec<uint8_t>>& hidl_cert_chain) {
+ error = hidl_error;
+ *cert_chain = hidl_cert_chain;
+ });
+ return error;
+ }
+
+ ErrorCode AttestKey(const AuthorizationSet& attest_params,
+ hidl_vec<hidl_vec<uint8_t>>* cert_chain) {
+ SCOPED_TRACE("AttestKey");
+ return AttestKey(key_blob_, attest_params, cert_chain);
+ }
+
+ string ProcessMessage(const HidlBuf& key_blob, KeyPurpose operation, const string& message,
+ const AuthorizationSet& in_params, AuthorizationSet* out_params) {
+ SCOPED_TRACE("ProcessMessage");
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(ErrorCode::OK,
+ Begin(operation, key_blob, in_params, &begin_out_params, &op_handle_));
+
+ string unused;
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+ string output;
+ EXPECT_EQ(ErrorCode::OK,
+ Finish(op_handle_, finish_params, message, unused, &finish_out_params, &output));
+ op_handle_ = kOpHandleSentinel;
+
+ out_params->push_back(begin_out_params);
+ out_params->push_back(finish_out_params);
+ return output;
+ }
+
+ string SignMessage(const HidlBuf& key_blob, const string& message,
+ const AuthorizationSet& params) {
+ SCOPED_TRACE("SignMessage");
+ AuthorizationSet out_params;
+ string signature = ProcessMessage(key_blob, KeyPurpose::SIGN, message, params, &out_params);
+ EXPECT_TRUE(out_params.empty());
+ return signature;
+ }
+
+ string SignMessage(const string& message, const AuthorizationSet& params) {
+ SCOPED_TRACE("SignMessage");
+ return SignMessage(key_blob_, message, params);
+ }
+
+ string MacMessage(const string& message, Digest digest, size_t mac_length) {
+ SCOPED_TRACE("MacMessage");
+ return SignMessage(
+ key_blob_, message,
+ AuthorizationSetBuilder().Digest(digest).Authorization(TAG_MAC_LENGTH, mac_length));
+ }
+
+ void CheckHmacTestVector(const string& key, const string& message, Digest digest,
+ const string& expected_mac) {
+ SCOPED_TRACE("CheckHmacTestVector");
+ ASSERT_EQ(ErrorCode::OK,
+ ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .HmacKey(key.size() * 8)
+ .Authorization(TAG_MIN_MAC_LENGTH, expected_mac.size() * 8)
+ .Digest(digest),
+ KeyFormat::RAW, key));
+ string signature = MacMessage(message, digest, expected_mac.size() * 8);
+ EXPECT_EQ(expected_mac, signature) << "Test vector didn't match for digest " << (int)digest;
+ DeleteKey();
+ }
+
+ void CheckAesCtrTestVector(const string& key, const string& nonce, const string& message,
+ const string& expected_ciphertext) {
+ SCOPED_TRACE("CheckAesCtrTestVector");
+ ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(key.size() * 8)
+ .BlockMode(BlockMode::CTR)
+ .Authorization(TAG_CALLER_NONCE)
+ .Padding(PaddingMode::NONE),
+ KeyFormat::RAW, key));
+
+ auto params = AuthorizationSetBuilder()
+ .Authorization(TAG_NONCE, nonce.data(), nonce.size())
+ .BlockMode(BlockMode::CTR)
+ .Padding(PaddingMode::NONE);
+ AuthorizationSet out_params;
+ string ciphertext = EncryptMessage(key_blob_, message, params, &out_params);
+ EXPECT_EQ(expected_ciphertext, ciphertext);
+ }
+
+ void VerifyMessage(const HidlBuf& key_blob, const string& message, const string& signature,
+ const AuthorizationSet& params) {
+ SCOPED_TRACE("VerifyMessage");
+ AuthorizationSet begin_out_params;
+ ASSERT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::VERIFY, key_blob, params, &begin_out_params, &op_handle_));
+
+ string unused;
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+ string output;
+ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, message, signature,
+ &finish_out_params, &output));
+ op_handle_ = kOpHandleSentinel;
+ EXPECT_TRUE(output.empty());
+ }
+
+ void VerifyMessage(const string& message, const string& signature,
+ const AuthorizationSet& params) {
+ SCOPED_TRACE("VerifyMessage");
+ VerifyMessage(key_blob_, message, signature, params);
+ }
+
+ string EncryptMessage(const HidlBuf& key_blob, const string& message,
+ const AuthorizationSet& in_params, AuthorizationSet* out_params) {
+ SCOPED_TRACE("EncryptMessage");
+ return ProcessMessage(key_blob, KeyPurpose::ENCRYPT, message, in_params, out_params);
+ }
+
+ string EncryptMessage(const string& message, const AuthorizationSet& params,
+ AuthorizationSet* out_params) {
+ SCOPED_TRACE("EncryptMessage");
+ return EncryptMessage(key_blob_, message, params, out_params);
+ }
+
+ string EncryptMessage(const string& message, const AuthorizationSet& params) {
+ SCOPED_TRACE("EncryptMessage");
+ AuthorizationSet out_params;
+ string ciphertext = EncryptMessage(message, params, &out_params);
+ EXPECT_TRUE(out_params.empty())
+ << "Output params should be empty. Contained: " << out_params;
+ return ciphertext;
+ }
+
+ string DecryptMessage(const HidlBuf& key_blob, const string& ciphertext,
+ const AuthorizationSet& params) {
+ SCOPED_TRACE("DecryptMessage");
+ AuthorizationSet out_params;
+ string plaintext =
+ ProcessMessage(key_blob, KeyPurpose::DECRYPT, ciphertext, params, &out_params);
+ EXPECT_TRUE(out_params.empty());
+ return plaintext;
+ }
+
+ string DecryptMessage(const string& ciphertext, const AuthorizationSet& params) {
+ SCOPED_TRACE("DecryptMessage");
+ return DecryptMessage(key_blob_, ciphertext, params);
+ }
+
+ template <TagType tag_type, Tag tag, typename ValueT>
+ void CheckKm0CryptoParam(TypedTag<tag_type, tag> ttag, ValueT expected) {
+ SCOPED_TRACE("CheckKm0CryptoParam");
+ if (is_secure_) {
+ EXPECT_TRUE(contains(key_characteristics_.teeEnforced, ttag, expected));
+ EXPECT_FALSE(contains(key_characteristics_.softwareEnforced, ttag));
+ } else {
+ EXPECT_TRUE(contains(key_characteristics_.softwareEnforced, ttag, expected));
+ EXPECT_FALSE(contains(key_characteristics_.teeEnforced, ttag));
+ }
+ }
+
+ template <TagType tag_type, Tag tag, typename ValueT>
+ void CheckKm1CryptoParam(TypedTag<tag_type, tag> ttag, ValueT expected) {
+ SCOPED_TRACE("CheckKm1CryptoParam");
+ if (is_secure_ && supports_symmetric_) {
+ EXPECT_TRUE(contains(key_characteristics_.teeEnforced, ttag, expected));
+ EXPECT_FALSE(contains(key_characteristics_.softwareEnforced, ttag));
+ } else {
+ EXPECT_TRUE(contains(key_characteristics_.softwareEnforced, ttag, expected));
+ EXPECT_FALSE(contains(key_characteristics_.teeEnforced, ttag));
+ }
+ }
+
+ template <TagType tag_type, Tag tag, typename ValueT>
+ void CheckKm2CryptoParam(TypedTag<tag_type, tag> ttag, ValueT expected) {
+ SCOPED_TRACE("CheckKm2CryptoParam");
+ if (supports_attestation_) {
+ EXPECT_TRUE(contains(key_characteristics_.teeEnforced, ttag, expected));
+ EXPECT_FALSE(contains(key_characteristics_.softwareEnforced, ttag));
+ } else if (!supports_symmetric_ /* KM version < 1 or SW */) {
+ EXPECT_TRUE(contains(key_characteristics_.softwareEnforced, ttag, expected));
+ EXPECT_FALSE(contains(key_characteristics_.teeEnforced, ttag));
+ }
+ }
+
+ void CheckOrigin() {
+ SCOPED_TRACE("CheckOrigin");
+ if (is_secure_ && supports_symmetric_) {
+ EXPECT_TRUE(
+ contains(key_characteristics_.teeEnforced, TAG_ORIGIN, KeyOrigin::IMPORTED));
+ } else if (is_secure_) {
+ EXPECT_TRUE(contains(key_characteristics_.teeEnforced, TAG_ORIGIN, KeyOrigin::UNKNOWN));
+ } else {
+ EXPECT_TRUE(
+ contains(key_characteristics_.softwareEnforced, TAG_ORIGIN, KeyOrigin::IMPORTED));
+ }
+ }
+
+ static bool IsSecure() { return is_secure_; }
+ static bool SupportsEc() { return supports_ec_; }
+ static bool SupportsSymmetric() { return supports_symmetric_; }
+ static bool SupportsAllDigests() { return supports_all_digests_; }
+ static bool SupportsAttestation() { return supports_attestation_; }
+
+ static bool Km2Profile() {
+ return SupportsAttestation() && SupportsAllDigests() && SupportsSymmetric() &&
+ SupportsEc() && IsSecure();
+ }
+
+ static bool Km1Profile() {
+ return !SupportsAttestation() && SupportsSymmetric() && SupportsEc() && IsSecure();
+ }
+
+ static bool Km0Profile() {
+ return !SupportsAttestation() && !SupportsAllDigests() && !SupportsSymmetric() &&
+ IsSecure();
+ }
+
+ static bool SwOnlyProfile() {
+ return !SupportsAttestation() && !SupportsAllDigests() && !SupportsSymmetric() &&
+ !SupportsEc() && !IsSecure();
+ }
+
+ HidlBuf key_blob_;
+ KeyCharacteristics key_characteristics_;
+ OperationHandle op_handle_ = kOpHandleSentinel;
+
+ private:
+ static sp<IKeymasterDevice> keymaster_;
+ static uint32_t os_version_;
+ static uint32_t os_patch_level_;
+
+ static bool is_secure_;
+ static bool supports_ec_;
+ static bool supports_symmetric_;
+ static bool supports_attestation_;
+ static bool supports_all_digests_;
+ static hidl_string name_;
+ static hidl_string author_;
+};
+
+uint32_t expected_keymaster_version() {
+ if (!KeymasterHidlTest::IsSecure()) return 2; // SW is KM2
+
+ uint32_t keymaster_version = 0;
+ if (KeymasterHidlTest::SupportsSymmetric()) keymaster_version = 1;
+ if (KeymasterHidlTest::SupportsAttestation()) keymaster_version = 2;
+ return keymaster_version;
+}
+
+bool verify_attestation_record(const string& challenge, AuthorizationSet expected_sw_enforced,
+ AuthorizationSet expected_tee_enforced,
+ const hidl_vec<uint8_t>& attestation_cert) {
+
+ X509_Ptr cert(parse_cert_blob(attestation_cert));
+ EXPECT_TRUE(!!cert.get());
+ if (!cert.get()) return false;
+
+ ASN1_OCTET_STRING* attest_rec = get_attestation_record(cert.get());
+ EXPECT_TRUE(!!attest_rec);
+ if (!attest_rec) return false;
+
+ AuthorizationSet att_sw_enforced;
+ AuthorizationSet att_tee_enforced;
+ uint32_t att_attestation_version;
+ uint32_t att_keymaster_version;
+ SecurityLevel att_attestation_security_level;
+ SecurityLevel att_keymaster_security_level;
+ HidlBuf att_challenge;
+ HidlBuf att_unique_id;
+ EXPECT_EQ(ErrorCode::OK,
+ parse_attestation_record(attest_rec->data, //
+ attest_rec->length, //
+ &att_attestation_version, //
+ &att_attestation_security_level, //
+ &att_keymaster_version, //
+ &att_keymaster_security_level, //
+ &att_challenge, //
+ &att_sw_enforced, //
+ &att_tee_enforced, //
+ &att_unique_id));
+
+ EXPECT_EQ(1U, att_attestation_version);
+ EXPECT_EQ(expected_keymaster_version(), att_keymaster_version);
+ EXPECT_EQ(KeymasterHidlTest::IsSecure() ? SecurityLevel::TRUSTED_ENVIRONMENT
+ : SecurityLevel::SOFTWARE,
+ att_keymaster_security_level);
+ EXPECT_EQ(KeymasterHidlTest::SupportsAttestation() ? SecurityLevel::TRUSTED_ENVIRONMENT
+ : SecurityLevel::SOFTWARE,
+ att_attestation_security_level);
+
+ EXPECT_EQ(challenge.length(), att_challenge.size());
+ EXPECT_EQ(0, memcmp(challenge.data(), att_challenge.data(), challenge.length()));
+
+ att_sw_enforced.Sort();
+ expected_sw_enforced.Sort();
+ EXPECT_EQ(filter_tags(expected_sw_enforced), filter_tags(att_sw_enforced));
+
+ att_tee_enforced.Sort();
+ expected_tee_enforced.Sort();
+ EXPECT_EQ(filter_tags(expected_tee_enforced), filter_tags(att_tee_enforced));
+
+ return true;
+}
+
+sp<IKeymasterDevice> KeymasterHidlTest::keymaster_;
+uint32_t KeymasterHidlTest::os_version_;
+uint32_t KeymasterHidlTest::os_patch_level_;
+bool KeymasterHidlTest::is_secure_;
+bool KeymasterHidlTest::supports_ec_;
+bool KeymasterHidlTest::supports_symmetric_;
+bool KeymasterHidlTest::supports_all_digests_;
+bool KeymasterHidlTest::supports_attestation_;
+hidl_string KeymasterHidlTest::name_;
+hidl_string KeymasterHidlTest::author_;
+
+typedef KeymasterHidlTest KeymasterVersionTest;
+
+/*
+ * KeymasterVersionTest.SensibleFeatures:
+ *
+ * Queries keymaster to find the set of features it supports. Fails if the combination doesn't
+ * correspond to any well-defined keymaster version.
+ */
+TEST_F(KeymasterVersionTest, SensibleFeatures) {
+ EXPECT_TRUE(Km2Profile() || Km1Profile() || Km0Profile() || SwOnlyProfile())
+ << "Keymaster feature set doesn't fit any reasonable profile. Reported features:"
+ << "SupportsAttestation [" << SupportsAttestation() << "], "
+ << "SupportsSymmetric [" << SupportsSymmetric() << "], "
+ << "SupportsAllDigests [" << SupportsAllDigests() << "], "
+ << "SupportsEc [" << SupportsEc() << "], "
+ << "IsSecure [" << IsSecure() << "]";
+}
+
+class NewKeyGenerationTest : public KeymasterHidlTest {
+ protected:
+ void CheckBaseParams(const KeyCharacteristics& keyCharacteristics) {
+ // TODO(swillden): Distinguish which params should be in which auth list.
+
+ AuthorizationSet auths(keyCharacteristics.teeEnforced);
+ auths.push_back(AuthorizationSet(keyCharacteristics.softwareEnforced));
+
+ EXPECT_TRUE(auths.Contains(TAG_ORIGIN, KeyOrigin::GENERATED));
+
+ EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
+ EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY));
+ EXPECT_TRUE(auths.Contains(TAG_USER_ID, 7))
+ << "User ID should be 7, was " << auths.GetTagValue(TAG_USER_ID);
+
+ // Verify that App ID, App data and ROT are NOT included.
+ EXPECT_FALSE(auths.Contains(TAG_ROOT_OF_TRUST));
+ EXPECT_FALSE(auths.Contains(TAG_APPLICATION_ID));
+ EXPECT_FALSE(auths.Contains(TAG_APPLICATION_DATA));
+
+ // Check that some unexpected tags/values are NOT present.
+ EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::ENCRYPT));
+ EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));
+ EXPECT_FALSE(auths.Contains(TAG_AUTH_TIMEOUT, 301));
+
+ // Now check that unspecified, defaulted tags are correct.
+ EXPECT_TRUE(auths.Contains(TAG_CREATION_DATETIME));
+
+ if (SupportsAttestation()) {
+ EXPECT_TRUE(auths.Contains(TAG_OS_VERSION, os_version()))
+ << "OS version is " << os_version() << " key reported "
+ << auths.GetTagValue(TAG_OS_VERSION);
+ EXPECT_TRUE(auths.Contains(TAG_OS_PATCHLEVEL, os_patch_level()))
+ << "OS patch level is " << os_patch_level() << " key reported "
+ << auths.GetTagValue(TAG_OS_PATCHLEVEL);
+ }
+ }
+};
+
+/*
+ * NewKeyGenerationTest.Rsa
+ *
+ * Verifies that keymaster can generate all required RSA key sizes, and that the resulting keys have
+ * correct characteristics.
+ */
+TEST_F(NewKeyGenerationTest, Rsa) {
+ for (auto key_size : {1024, 2048, 3072, 4096}) {
+ HidlBuf key_blob;
+ KeyCharacteristics key_characteristics;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(key_size, 3)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .Authorizations(UserAuths()),
+ &key_blob, &key_characteristics));
+
+ ASSERT_GT(key_blob.size(), 0U);
+ CheckBaseParams(key_characteristics);
+
+ AuthorizationSet crypto_params;
+ if (IsSecure()) {
+ crypto_params = key_characteristics.teeEnforced;
+ } else {
+ crypto_params = key_characteristics.softwareEnforced;
+ }
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, KM_ALGORITHM_RSA));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size));
+ EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 3));
+
+ EXPECT_EQ(ErrorCode::OK, DeleteKey(&key_blob));
+ }
+}
+
+/*
+ * NewKeyGenerationTest.RsaNoDefaultSize
+ *
+ * Verifies that failing to specify a key size for RSA key generation returns UNSUPPORTED_KEY_SIZE.
+ */
+TEST_F(NewKeyGenerationTest, RsaNoDefaultSize) {
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, Algorithm::RSA)
+ .Authorization(TAG_RSA_PUBLIC_EXPONENT, 3)
+ .SigningKey()));
+}
+
+/*
+ * NewKeyGenerationTest.Ecdsa
+ *
+ * Verifies that keymaster can generate all required EC key sizes, and that the resulting keys have
+ * correct characteristics.
+ */
+TEST_F(NewKeyGenerationTest, Ecdsa) {
+ for (auto key_size : {224, 256, 384, 521}) {
+ HidlBuf key_blob;
+ KeyCharacteristics key_characteristics;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(key_size)
+ .Digest(Digest::NONE)
+ .Authorizations(UserAuths()),
+ &key_blob, &key_characteristics));
+ ASSERT_GT(key_blob.size(), 0U);
+ CheckBaseParams(key_characteristics);
+
+ AuthorizationSet crypto_params;
+ if (IsSecure()) {
+ crypto_params = key_characteristics.teeEnforced;
+ } else {
+ crypto_params = key_characteristics.softwareEnforced;
+ }
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+ EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size));
+
+ EXPECT_EQ(ErrorCode::OK, DeleteKey(&key_blob));
+ }
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaDefaultSize
+ *
+ * Verifies that failing to specify a key size for EC key generation returns UNSUPPORTED_KEY_SIZE.
+ */
+TEST_F(NewKeyGenerationTest, EcdsaDefaultSize) {
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, Algorithm::EC)
+ .SigningKey()
+ .Digest(Digest::NONE)));
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaInvalidSize
+ *
+ * Verifies that failing to specify an invalid key size for EC key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_F(NewKeyGenerationTest, EcdsaInvalidSize) {
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(190).Digest(Digest::NONE)));
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaMismatchKeySize
+ *
+ * Verifies that specifying mismatched key size and curve for EC key generation returns
+ * INVALID_ARGUMENT.
+ */
+TEST_F(NewKeyGenerationTest, EcdsaMismatchKeySize) {
+ ASSERT_EQ(ErrorCode::INVALID_ARGUMENT,
+ GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(224)
+ .Authorization(TAG_EC_CURVE, EcCurve::P_256)
+ .Digest(Digest::NONE)))
+ << "(Possibly b/36233343)";
+}
+
+TEST_F(NewKeyGenerationTest, EcdsaAllValidSizes) {
+ size_t valid_sizes[] = {224, 256, 384, 521};
+ for (size_t size : valid_sizes) {
+ EXPECT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(size).Digest(Digest::NONE)))
+ << "Failed to generate size: " << size;
+ DeleteKey();
+ }
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaAllValidCurves
+ *
+ * Verifies that keymaster supports all required EC curves.
+ */
+TEST_F(NewKeyGenerationTest, EcdsaAllValidCurves) {
+ EcCurve curves[] = {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521};
+ for (auto curve : curves) {
+ EXPECT_EQ(
+ ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(curve).Digest(Digest::SHA_2_512)))
+ << "Failed to generate key on curve: " << curve;
+ DeleteKey();
+ }
+}
+
+/*
+ * NewKeyGenerationTest.Hmac
+ *
+ * Verifies that keymaster supports all required digests, and that the resulting keys have correct
+ * characteristics.
+ */
+TEST_F(NewKeyGenerationTest, Hmac) {
+ for (auto digest : {Digest::MD5, Digest::SHA1, Digest::SHA_2_224, Digest::SHA_2_256,
+ Digest::SHA_2_384, Digest::SHA_2_512}) {
+ HidlBuf key_blob;
+ KeyCharacteristics key_characteristics;
+ constexpr size_t key_size = 128;
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(key_size)
+ .Digest(digest)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)
+ .Authorizations(UserAuths()),
+ &key_blob, &key_characteristics));
+
+ ASSERT_GT(key_blob.size(), 0U);
+ CheckBaseParams(key_characteristics);
+
+ AuthorizationSet teeEnforced = key_characteristics.teeEnforced;
+ AuthorizationSet softwareEnforced = key_characteristics.softwareEnforced;
+ if (SupportsAttestation() || SupportsAllDigests()) {
+ // Either KM2, which must support all, or KM1 that claims full support
+ EXPECT_TRUE(teeEnforced.Contains(TAG_ALGORITHM, Algorithm::HMAC));
+ EXPECT_TRUE(teeEnforced.Contains(TAG_KEY_SIZE, key_size));
+ } else if (SupportsSymmetric()) {
+ if (digest == Digest::SHA1 || digest == Digest::SHA_2_256) {
+ // KM1 must support SHA1 and SHA256 in hardware
+ EXPECT_TRUE(teeEnforced.Contains(TAG_ALGORITHM, Algorithm::HMAC));
+ EXPECT_TRUE(teeEnforced.Contains(TAG_KEY_SIZE, key_size));
+ } else {
+ // Othere digests may or may not be supported
+ EXPECT_TRUE(teeEnforced.Contains(TAG_ALGORITHM, Algorithm::HMAC) ||
+ softwareEnforced.Contains(TAG_ALGORITHM, Algorithm::HMAC));
+ EXPECT_TRUE(teeEnforced.Contains(TAG_KEY_SIZE, key_size) ||
+ softwareEnforced.Contains(TAG_KEY_SIZE, key_size));
+ }
+ } else {
+ // KM0 and SW KM do all digests in SW.
+ EXPECT_TRUE(softwareEnforced.Contains(TAG_ALGORITHM, Algorithm::HMAC));
+ EXPECT_TRUE(softwareEnforced.Contains(TAG_KEY_SIZE, key_size));
+ }
+
+ EXPECT_EQ(ErrorCode::OK, DeleteKey(&key_blob));
+ }
+}
+
+/*
+ * NewKeyGenerationTest.HmacCheckKeySizes
+ *
+ * Verifies that keymaster supports all key sizes, and rejects all invalid key sizes.
+ */
+TEST_F(NewKeyGenerationTest, HmacCheckKeySizes) {
+ for (size_t key_size = 0; key_size <= 512; ++key_size) {
+ if (key_size < 64 || key_size % 8 != 0) {
+ // To keep this test from being very slow, we only test a random fraction of non-byte
+ // key sizes. We test only ~10% of such cases. Since there are 392 of them, we expect
+ // to run ~40 of them in each run.
+ if (key_size % 8 == 0 || random() % 10 == 0) {
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(key_size)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 256)))
+ << "HMAC key size " << key_size << " invalid (Possibly b/33462346)";
+ }
+ } else {
+ EXPECT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(key_size)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 256)));
+ DeleteKey();
+ }
+ }
+}
+
+/*
+ * NewKeyGenerationTest.HmacCheckMinMacLengths
+ *
+ * Verifies that keymaster supports all required MAC lengths and rejects all invalid lengths. This
+ * test is probabilistic in order to keep the runtime down, but any failure prints out the specific
+ * MAC length that failed, so reproducing a failed run will be easy.
+ */
+TEST_F(NewKeyGenerationTest, HmacCheckMinMacLengths) {
+ for (size_t min_mac_length = 0; min_mac_length <= 256; ++min_mac_length) {
+ if (min_mac_length < 64 || min_mac_length % 8 != 0) {
+ // To keep this test from being very long, we only test a random fraction of non-byte
+ // lengths. We test only ~10% of such cases. Since there are 172 of them, we expect to
+ // run ~17 of them in each run.
+ if (min_mac_length % 8 == 0 || random() % 10 == 0) {
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH,
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, min_mac_length)))
+ << "HMAC min mac length " << min_mac_length << " invalid.";
+ }
+ } else {
+ EXPECT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, min_mac_length)));
+ DeleteKey();
+ }
+ }
+}
+
+/*
+ * NewKeyGenerationTest.HmacMultipleDigests
+ *
+ * Verifies that keymaster rejects HMAC key generation with multiple specified digest algorithms.
+ */
+TEST_F(NewKeyGenerationTest, HmacMultipleDigests) {
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(Digest::SHA1)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+}
+
+/*
+ * NewKeyGenerationTest.HmacDigestNone
+ *
+ * Verifies that keymaster rejects HMAC key generation with no digest or Digest::NONE
+ */
+TEST_F(NewKeyGenerationTest, HmacDigestNone) {
+ ASSERT_EQ(
+ ErrorCode::UNSUPPORTED_DIGEST,
+ GenerateKey(AuthorizationSetBuilder().HmacKey(128).Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(Digest::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+}
+
+typedef KeymasterHidlTest GetKeyCharacteristicsTest;
+
+/*
+ * GetKeyCharacteristicsTest.HmacDigestNone
+ *
+ * Verifies that getKeyCharacteristics functions, and that generated and retrieved key
+ * characteristics match.
+ */
+TEST_F(GetKeyCharacteristicsTest, SimpleRsa) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)));
+
+ KeyCharacteristics retrieved_chars;
+ ASSERT_EQ(ErrorCode::OK, GetCharacteristics(key_blob_, &retrieved_chars));
+
+ AuthorizationSet gen_sw = key_characteristics_.softwareEnforced;
+ AuthorizationSet gen_tee = key_characteristics_.teeEnforced;
+ AuthorizationSet retrieved_sw = retrieved_chars.softwareEnforced;
+ AuthorizationSet retrieved_tee = retrieved_chars.teeEnforced;
+
+ EXPECT_EQ(gen_sw, retrieved_sw);
+ EXPECT_EQ(gen_tee, retrieved_tee);
+}
+
+typedef KeymasterHidlTest SigningOperationsTest;
+
+/*
+ * SigningOperationsTest.RsaSuccess
+ *
+ * Verifies that raw RSA signature operations succeed.
+ */
+TEST_F(SigningOperationsTest, RsaSuccess) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)));
+ string message = "12345678901234567890123456789012";
+ string signature = SignMessage(
+ message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+}
+
+/*
+ * SigningOperationsTest.RsaPssSha256Success
+ *
+ * Verifies that RSA-PSS signature operations succeed.
+ */
+TEST_F(SigningOperationsTest, RsaPssSha256Success) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PSS)
+ .Authorization(TAG_NO_AUTH_REQUIRED)));
+ // Use large message, which won't work without digesting.
+ string message(1024, 'a');
+ string signature = SignMessage(
+ message, AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS));
+}
+
+/*
+ * SigningOperationsTest.RsaPaddingNoneDoesNotAllowOther
+ *
+ * Verifies that keymaster rejects signature operations that specify a padding mode when the key
+ * supports only unpadded operations.
+ */
+TEST_F(SigningOperationsTest, RsaPaddingNoneDoesNotAllowOther) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Padding(PaddingMode::NONE)));
+ string message = "12345678901234567890123456789012";
+ string signature;
+
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE,
+ Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+}
+
+/*
+ * SigningOperationsTest.RsaPkcs1Sha256Success
+ *
+ * Verifies that digested RSA-PKCS1 signature operations succeed.
+ */
+TEST_F(SigningOperationsTest, RsaPkcs1Sha256Success) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+ string message(1024, 'a');
+ string signature = SignMessage(message, AuthorizationSetBuilder()
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN));
+}
+
+/*
+ * SigningOperationsTest.RsaPkcs1NoDigestSuccess
+ *
+ * Verifies that undigested RSA-PKCS1 signature operations succeed.
+ */
+TEST_F(SigningOperationsTest, RsaPkcs1NoDigestSuccess) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+ string message(53, 'a');
+ string signature = SignMessage(
+ message,
+ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::RSA_PKCS1_1_5_SIGN));
+}
+
+/*
+ * SigningOperationsTest.RsaPkcs1NoDigestTooLarge
+ *
+ * Verifies that undigested RSA-PKCS1 signature operations fail with the correct error code when
+ * given a too-long message.
+ */
+TEST_F(SigningOperationsTest, RsaPkcs1NoDigestTooLong) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+ string message(129, 'a');
+
+ EXPECT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+ string signature;
+ EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &signature));
+}
+
+/*
+ * SigningOperationsTest.RsaPssSha512TooSmallKey
+ *
+ * Verifies that undigested RSA-PSS signature operations fail with the correct error code when
+ * used with a key that is too small for the message.
+ *
+ * A PSS-padded message is of length salt_size + digest_size + 16 (sizes in bits), and the keymaster
+ * specification requires that salt_size == digest_size, so the message will be digest_size * 2 +
+ * 16. Such a message can only be signed by a given key if the key is at least that size. This test
+ * uses SHA512, which has a digest_size == 512, so the message size is 1040 bits, too large for a
+ * 1024-bit key.
+ */
+TEST_F(SigningOperationsTest, RsaPssSha512TooSmallKey) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::SHA_2_512)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Padding(PaddingMode::RSA_PSS)));
+ EXPECT_EQ(
+ ErrorCode::INCOMPATIBLE_DIGEST,
+ Begin(KeyPurpose::SIGN,
+ AuthorizationSetBuilder().Digest(Digest::SHA_2_512).Padding(PaddingMode::RSA_PSS)))
+ << "(Possibly b/33346750)";
+}
+
+/*
+ * SigningOperationsTest.RsaNoPaddingTooLong
+ *
+ * Verifies that raw RSA signature operations fail with the correct error code when
+ * given a too-long message.
+ */
+TEST_F(SigningOperationsTest, RsaNoPaddingTooLong) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+ // One byte too long
+ string message(1024 / 8 + 1, 'a');
+ ASSERT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+ string result;
+ EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &result));
+
+ // Very large message that should exceed the transfer buffer size of any reasonable TEE.
+ message = string(128 * 1024, 'a');
+ ASSERT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+ EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &result));
+}
+
+/*
+ * SigningOperationsTest.RsaAbort
+ *
+ * Verifies that operations can be aborted correctly. Uses an RSA signing operation for the test,
+ * but the behavior should be algorithm and purpose-independent.
+ */
+TEST_F(SigningOperationsTest, RsaAbort) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Padding(PaddingMode::NONE)));
+
+ ASSERT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::SIGN,
+ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
+ EXPECT_EQ(ErrorCode::OK, Abort(op_handle_));
+
+ // Another abort should fail
+ EXPECT_EQ(ErrorCode::INVALID_OPERATION_HANDLE, Abort(op_handle_));
+
+ // Set to sentinel, so TearDown() doesn't try to abort again.
+ op_handle_ = kOpHandleSentinel;
+}
+
+/*
+ * SigningOperationsTest.RsaUnsupportedPadding
+ *
+ * Verifies that RSA operations fail with the correct error (but key gen succeeds) when used with a
+ * padding mode inappropriate for RSA.
+ */
+TEST_F(SigningOperationsTest, RsaUnsupportedPadding) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Digest(Digest::SHA_2_256 /* supported digest */)
+ .Padding(PaddingMode::PKCS7)));
+ ASSERT_EQ(
+ ErrorCode::UNSUPPORTED_PADDING_MODE,
+ Begin(KeyPurpose::SIGN,
+ AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::PKCS7)));
+}
+
+/*
+ * SigningOperationsTest.RsaPssNoDigest
+ *
+ * Verifies that RSA PSS operations fail when no digest is used. PSS requires a digest.
+ */
+TEST_F(SigningOperationsTest, RsaNoDigest) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::RSA_PSS)));
+ ASSERT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
+ Begin(KeyPurpose::SIGN,
+ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::RSA_PSS)));
+
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+ Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Padding(PaddingMode::RSA_PSS)));
+}
+
+/*
+ * SigningOperationsTest.RsaPssNoDigest
+ *
+ * Verifies that RSA operations fail when no padding mode is specified. PaddingMode::NONE is
+ * supported in some cases (as validated in other tests), but a mode must be specified.
+ */
+TEST_F(SigningOperationsTest, RsaNoPadding) {
+ // Padding must be specified
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaKey(1024, 3)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SigningKey()
+ .Digest(Digest::NONE)));
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE,
+ Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::NONE)));
+}
+
+/*
+ * SigningOperationsTest.RsaShortMessage
+ *
+ * Verifies that raw RSA signatures succeed with a message shorter than the key size.
+ */
+TEST_F(SigningOperationsTest, RsaTooShortMessage) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)));
+
+ // Barely shorter
+ string message(1024 / 8 - 1, 'a');
+ SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+
+ // Much shorter
+ message = "a";
+ SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+}
+
+/*
+ * SigningOperationsTest.RsaSignWithEncryptionKey
+ *
+ * Verifies that RSA encryption keys cannot be used to sign.
+ */
+TEST_F(SigningOperationsTest, RsaSignWithEncryptionKey) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(1024, 3)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)));
+ ASSERT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
+ Begin(KeyPurpose::SIGN,
+ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
+}
+
+/*
+ * SigningOperationsTest.RsaSignTooLargeMessage
+ *
+ * Verifies that attempting a raw signature of a message which is the same length as the key, but
+ * numerically larger than the public modulus, fails with the correct error.
+ */
+TEST_F(SigningOperationsTest, RsaSignTooLargeMessage) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)));
+
+ // Largest possible message will always be larger than the public modulus.
+ string message(1024 / 8, static_cast<char>(0xff));
+ ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)));
+ string signature;
+ ASSERT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(message, &signature));
+}
+
+/*
+ * SigningOperationsTest.EcdsaAllSizesAndHashes
+ *
+ * Verifies that ECDSA operations succeed with all possible key sizes and hashes.
+ */
+TEST_F(SigningOperationsTest, EcdsaAllSizesAndHashes) {
+ for (auto key_size : {224, 256, 384, 521}) {
+ for (auto digest : {
+ Digest::SHA1, Digest::SHA_2_224, Digest::SHA_2_256, Digest::SHA_2_384,
+ Digest::SHA_2_512,
+ }) {
+ ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(key_size)
+ .Digest(digest));
+ EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with size " << key_size
+ << " and digest " << digest;
+ if (error != ErrorCode::OK) continue;
+
+ string message(1024, 'a');
+ if (digest == Digest::NONE) message.resize(key_size / 8);
+ SignMessage(message, AuthorizationSetBuilder().Digest(digest));
+ DeleteKey();
+ }
+ }
+}
+
+/*
+ * SigningOperationsTest.EcdsaAllCurves
+ *
+ * Verifies that ECDSA operations succeed with all possible curves.
+ */
+TEST_F(SigningOperationsTest, EcdsaAllCurves) {
+ for (auto curve : {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521}) {
+ ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(curve)
+ .Digest(Digest::SHA_2_256));
+ EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
+ if (error != ErrorCode::OK) continue;
+
+ string message(1024, 'a');
+ SignMessage(message, AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+ DeleteKey();
+ }
+}
+
+/*
+ * SigningOperationsTest.EcdsaNoDigestHugeData
+ *
+ * Verifies that ECDSA operations support very large messages, even without digesting. This should
+ * work because ECDSA actually only signs the leftmost L_n bits of the message, however large it may
+ * be. Not using digesting is a bad idea, but in some cases digesting is done by the framework.
+ */
+TEST_F(SigningOperationsTest, EcdsaNoDigestHugeData) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(224)
+ .Digest(Digest::NONE)));
+ string message(64 * 1024, 'a');
+ SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE));
+}
+
+/*
+ * SigningOperationsTest.AesEcbSign
+ *
+ * Verifies that attempts to use AES keys to sign fail in the correct way.
+ */
+TEST_F(SigningOperationsTest, AesEcbSign) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .SigningKey()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)))
+ << "(Possibly b/36252957)";
+
+ AuthorizationSet out_params;
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_PURPOSE,
+ Begin(KeyPurpose::SIGN, AuthorizationSet() /* in_params */, &out_params))
+ << "(Possibly b/36233187)";
+
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_PURPOSE,
+ Begin(KeyPurpose::VERIFY, AuthorizationSet() /* in_params */, &out_params))
+ << "(Possibly b/36233187)";
+}
+
+/*
+ * SigningOperationsTest.HmacAllDigests
+ *
+ * Verifies that HMAC works with all digests.
+ */
+TEST_F(SigningOperationsTest, HmacAllDigests) {
+ for (auto digest : {Digest::SHA1, Digest::SHA_2_224, Digest::SHA_2_256, Digest::SHA_2_384,
+ Digest::SHA_2_512}) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .HmacKey(128)
+ .Digest(digest)
+ .Authorization(TAG_MIN_MAC_LENGTH, 160)))
+ << "Failed to create HMAC key with digest " << digest;
+ string message = "12345678901234567890123456789012";
+ string signature = MacMessage(message, digest, 160);
+ EXPECT_EQ(160U / 8U, signature.size())
+ << "Failed to sign with HMAC key with digest " << digest;
+ DeleteKey();
+ }
+}
+
+/*
+ * SigningOperationsTest.HmacSha256TooLargeMacLength
+ *
+ * Verifies that HMAC fails in the correct way when asked to generate a MAC larger than the digest
+ * size.
+ */
+TEST_F(SigningOperationsTest, HmacSha256TooLargeMacLength) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .HmacKey(128)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 256)));
+ AuthorizationSet output_params;
+ EXPECT_EQ(
+ ErrorCode::UNSUPPORTED_MAC_LENGTH,
+ Begin(
+ KeyPurpose::SIGN, key_blob_,
+ AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Authorization(TAG_MAC_LENGTH, 264),
+ &output_params, &op_handle_));
+}
+
+/*
+ * SigningOperationsTest.HmacSha256TooSmallMacLength
+ *
+ * Verifies that HMAC fails in the correct way when asked to generate a MAC smaller than the
+ * specified minimum MAC length.
+ */
+TEST_F(SigningOperationsTest, HmacSha256TooSmallMacLength) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .HmacKey(128)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ AuthorizationSet output_params;
+ EXPECT_EQ(
+ ErrorCode::INVALID_MAC_LENGTH,
+ Begin(
+ KeyPurpose::SIGN, key_blob_,
+ AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Authorization(TAG_MAC_LENGTH, 120),
+ &output_params, &op_handle_));
+}
+
+/*
+ * SigningOperationsTest.HmacRfc4231TestCase3
+ *
+ * Validates against the test vectors from RFC 4231 test case 3.
+ */
+TEST_F(SigningOperationsTest, HmacRfc4231TestCase3) {
+ string key(20, 0xaa);
+ string message(50, 0xdd);
+ uint8_t sha_224_expected[] = {
+ 0x7f, 0xb3, 0xcb, 0x35, 0x88, 0xc6, 0xc1, 0xf6, 0xff, 0xa9, 0x69, 0x4d, 0x7d, 0x6a,
+ 0xd2, 0x64, 0x93, 0x65, 0xb0, 0xc1, 0xf6, 0x5d, 0x69, 0xd1, 0xec, 0x83, 0x33, 0xea,
+ };
+ uint8_t sha_256_expected[] = {
+ 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8,
+ 0xeb, 0xd0, 0x91, 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8,
+ 0xc1, 0x22, 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe,
+ };
+ uint8_t sha_384_expected[] = {
+ 0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a, 0x0a, 0xa2, 0xac, 0xe0,
+ 0x14, 0xc8, 0xa8, 0x6f, 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb,
+ 0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b, 0x2a, 0x5a, 0xb3, 0x9d,
+ 0xc1, 0x38, 0x14, 0xb9, 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27,
+ };
+ uint8_t sha_512_expected[] = {
+ 0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84, 0xef, 0xb0, 0xf0, 0x75, 0x6c,
+ 0x89, 0x0b, 0xe9, 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36, 0x55, 0xf8,
+ 0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39, 0xbf, 0x3e, 0x84, 0x82, 0x79, 0xa7, 0x22,
+ 0xc8, 0x06, 0xb4, 0x85, 0xa4, 0x7e, 0x67, 0xc8, 0x07, 0xb9, 0x46, 0xa3, 0x37,
+ 0xbe, 0xe8, 0x94, 0x26, 0x74, 0x27, 0x88, 0x59, 0xe1, 0x32, 0x92, 0xfb,
+ };
+
+ CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected));
+ CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected));
+ CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected));
+ CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected));
+}
+
+/*
+ * SigningOperationsTest.HmacRfc4231TestCase5
+ *
+ * Validates against the test vectors from RFC 4231 test case 5.
+ */
+TEST_F(SigningOperationsTest, HmacRfc4231TestCase5) {
+ string key(20, 0x0c);
+ string message = "Test With Truncation";
+
+ uint8_t sha_224_expected[] = {
+ 0x0e, 0x2a, 0xea, 0x68, 0xa9, 0x0c, 0x8d, 0x37,
+ 0xc9, 0x88, 0xbc, 0xdb, 0x9f, 0xca, 0x6f, 0xa8,
+ };
+ uint8_t sha_256_expected[] = {
+ 0xa3, 0xb6, 0x16, 0x74, 0x73, 0x10, 0x0e, 0xe0,
+ 0x6e, 0x0c, 0x79, 0x6c, 0x29, 0x55, 0x55, 0x2b,
+ };
+ uint8_t sha_384_expected[] = {
+ 0x3a, 0xbf, 0x34, 0xc3, 0x50, 0x3b, 0x2a, 0x23,
+ 0xa4, 0x6e, 0xfc, 0x61, 0x9b, 0xae, 0xf8, 0x97,
+ };
+ uint8_t sha_512_expected[] = {
+ 0x41, 0x5f, 0xad, 0x62, 0x71, 0x58, 0x0a, 0x53,
+ 0x1d, 0x41, 0x79, 0xbc, 0x89, 0x1d, 0x87, 0xa6,
+ };
+
+ CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected));
+ CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected));
+ CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected));
+ CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected));
+}
+
+/*
+ * SigningOperationsTest.HmacRfc4231TestCase6
+ *
+ * Validates against the test vectors from RFC 4231 test case 6.
+ */
+TEST_F(SigningOperationsTest, HmacRfc4231TestCase6) {
+ string key(131, 0xaa);
+ string message = "Test Using Larger Than Block-Size Key - Hash Key First";
+
+ uint8_t sha_224_expected[] = {
+ 0x95, 0xe9, 0xa0, 0xdb, 0x96, 0x20, 0x95, 0xad, 0xae, 0xbe, 0x9b, 0x2d, 0x6f, 0x0d,
+ 0xbc, 0xe2, 0xd4, 0x99, 0xf1, 0x12, 0xf2, 0xd2, 0xb7, 0x27, 0x3f, 0xa6, 0x87, 0x0e,
+ };
+ uint8_t sha_256_expected[] = {
+ 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26,
+ 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28,
+ 0xc5, 0x14, 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54,
+ };
+ uint8_t sha_384_expected[] = {
+ 0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90, 0x88, 0xd2, 0xc6, 0x3a,
+ 0x04, 0x1b, 0xc5, 0xb4, 0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f,
+ 0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6, 0x0c, 0x2e, 0xf6, 0xab,
+ 0x40, 0x30, 0xfe, 0x82, 0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52,
+ };
+ uint8_t sha_512_expected[] = {
+ 0x80, 0xb2, 0x42, 0x63, 0xc7, 0xc1, 0xa3, 0xeb, 0xb7, 0x14, 0x93, 0xc1, 0xdd,
+ 0x7b, 0xe8, 0xb4, 0x9b, 0x46, 0xd1, 0xf4, 0x1b, 0x4a, 0xee, 0xc1, 0x12, 0x1b,
+ 0x01, 0x37, 0x83, 0xf8, 0xf3, 0x52, 0x6b, 0x56, 0xd0, 0x37, 0xe0, 0x5f, 0x25,
+ 0x98, 0xbd, 0x0f, 0xd2, 0x21, 0x5d, 0x6a, 0x1e, 0x52, 0x95, 0xe6, 0x4f, 0x73,
+ 0xf6, 0x3f, 0x0a, 0xec, 0x8b, 0x91, 0x5a, 0x98, 0x5d, 0x78, 0x65, 0x98,
+ };
+
+ CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected));
+ CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected));
+ CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected));
+ CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected));
+}
+
+/*
+ * SigningOperationsTest.HmacRfc4231TestCase7
+ *
+ * Validates against the test vectors from RFC 4231 test case 7.
+ */
+TEST_F(SigningOperationsTest, HmacRfc4231TestCase7) {
+ string key(131, 0xaa);
+ string message = "This is a test using a larger than block-size key and a larger than "
+ "block-size data. The key needs to be hashed before being used by the HMAC "
+ "algorithm.";
+
+ uint8_t sha_224_expected[] = {
+ 0x3a, 0x85, 0x41, 0x66, 0xac, 0x5d, 0x9f, 0x02, 0x3f, 0x54, 0xd5, 0x17, 0xd0, 0xb3,
+ 0x9d, 0xbd, 0x94, 0x67, 0x70, 0xdb, 0x9c, 0x2b, 0x95, 0xc9, 0xf6, 0xf5, 0x65, 0xd1,
+ };
+ uint8_t sha_256_expected[] = {
+ 0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f,
+ 0xbc, 0xd5, 0xb0, 0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07,
+ 0x13, 0x93, 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2,
+ };
+ uint8_t sha_384_expected[] = {
+ 0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d, 0x35, 0x1e, 0x2f, 0x25,
+ 0x4e, 0x8f, 0xd3, 0x2c, 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a,
+ 0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5, 0xa6, 0x78, 0xcc, 0x31,
+ 0xe7, 0x99, 0x17, 0x6d, 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e,
+ };
+ uint8_t sha_512_expected[] = {
+ 0xe3, 0x7b, 0x6a, 0x77, 0x5d, 0xc8, 0x7d, 0xba, 0xa4, 0xdf, 0xa9, 0xf9, 0x6e,
+ 0x5e, 0x3f, 0xfd, 0xde, 0xbd, 0x71, 0xf8, 0x86, 0x72, 0x89, 0x86, 0x5d, 0xf5,
+ 0xa3, 0x2d, 0x20, 0xcd, 0xc9, 0x44, 0xb6, 0x02, 0x2c, 0xac, 0x3c, 0x49, 0x82,
+ 0xb1, 0x0d, 0x5e, 0xeb, 0x55, 0xc3, 0xe4, 0xde, 0x15, 0x13, 0x46, 0x76, 0xfb,
+ 0x6d, 0xe0, 0x44, 0x60, 0x65, 0xc9, 0x74, 0x40, 0xfa, 0x8c, 0x6a, 0x58,
+ };
+
+ CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected));
+ CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected));
+ CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected));
+ CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected));
+}
+
+typedef KeymasterHidlTest VerificationOperationsTest;
+
+/*
+ * VerificationOperationsTest.RsaSuccess
+ *
+ * Verifies that a simple RSA signature/verification sequence succeeds.
+ */
+TEST_F(VerificationOperationsTest, RsaSuccess) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)));
+ string message = "12345678901234567890123456789012";
+ string signature = SignMessage(
+ message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+ VerifyMessage(message, signature,
+ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+}
+
+/*
+ * VerificationOperationsTest.RsaSuccess
+ *
+ * Verifies RSA signature/verification for all padding modes and digests.
+ */
+TEST_F(VerificationOperationsTest, RsaAllPaddingsAndDigests) {
+ ASSERT_EQ(ErrorCode::OK,
+ GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(2048, 3)
+ .Digest(Digest::NONE, Digest::MD5, Digest::SHA1, Digest::SHA_2_224,
+ Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512)
+ .Padding(PaddingMode::NONE)
+ .Padding(PaddingMode::RSA_PSS)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+
+ string message(128, 'a');
+ string corrupt_message(message);
+ ++corrupt_message[corrupt_message.size() / 2];
+
+ for (auto padding :
+ {PaddingMode::NONE, PaddingMode::RSA_PSS, PaddingMode::RSA_PKCS1_1_5_SIGN}) {
+
+ for (auto digest : {Digest::NONE, Digest::MD5, Digest::SHA1, Digest::SHA_2_224,
+ Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512}) {
+ if (padding == PaddingMode::NONE && digest != Digest::NONE) {
+ // Digesting only makes sense with padding.
+ continue;
+ }
+
+ if (padding == PaddingMode::RSA_PSS && digest == Digest::NONE) {
+ // PSS requires digesting.
+ continue;
+ }
+
+ string signature =
+ SignMessage(message, AuthorizationSetBuilder().Digest(digest).Padding(padding));
+ VerifyMessage(message, signature,
+ AuthorizationSetBuilder().Digest(digest).Padding(padding));
+
+ if (digest != Digest::NONE) {
+ // Verify with OpenSSL.
+ HidlBuf pubkey;
+ ASSERT_EQ(ErrorCode::OK, ExportKey(KeyFormat::X509, &pubkey));
+
+ const uint8_t* p = pubkey.data();
+ EVP_PKEY_Ptr pkey(d2i_PUBKEY(nullptr /* alloc new */, &p, pubkey.size()));
+ ASSERT_TRUE(pkey.get());
+
+ EVP_MD_CTX digest_ctx;
+ EVP_MD_CTX_init(&digest_ctx);
+ EVP_PKEY_CTX* pkey_ctx;
+ const EVP_MD* md = openssl_digest(digest);
+ ASSERT_NE(md, nullptr);
+ EXPECT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, md, nullptr /* engine */,
+ pkey.get()));
+
+ switch (padding) {
+ case PaddingMode::RSA_PSS:
+ EXPECT_GT(EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING), 0);
+ EXPECT_GT(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, EVP_MD_size(md)), 0);
+ break;
+ case PaddingMode::RSA_PKCS1_1_5_SIGN:
+ // PKCS1 is the default; don't need to set anything.
+ break;
+ default:
+ FAIL();
+ break;
+ }
+
+ EXPECT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx, message.data(), message.size()));
+ EXPECT_EQ(1, EVP_DigestVerifyFinal(
+ &digest_ctx, reinterpret_cast<const uint8_t*>(signature.data()),
+ signature.size()));
+ EVP_MD_CTX_cleanup(&digest_ctx);
+ }
+
+ // Corrupt signature shouldn't verify.
+ string corrupt_signature(signature);
+ ++corrupt_signature[corrupt_signature.size() / 2];
+
+ EXPECT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::VERIFY,
+ AuthorizationSetBuilder().Digest(digest).Padding(padding)));
+ string result;
+ EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(message, corrupt_signature, &result));
+
+ // Corrupt message shouldn't verify
+ EXPECT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::VERIFY,
+ AuthorizationSetBuilder().Digest(digest).Padding(padding)));
+ EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(corrupt_message, signature, &result));
+ }
+ }
+}
+
+/*
+ * VerificationOperationsTest.RsaSuccess
+ *
+ * Verifies ECDSA signature/verification for all digests and curves.
+ */
+TEST_F(VerificationOperationsTest, EcdsaAllDigestsAndCurves) {
+ auto digests = {
+ Digest::NONE, Digest::SHA1, Digest::SHA_2_224,
+ Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512,
+ };
+
+ string message = "1234567890";
+ string corrupt_message = "2234567890";
+ for (auto curve : {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521}) {
+ ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(curve)
+ .Digest(digests));
+ EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate key for EC curve " << curve;
+ if (error != ErrorCode::OK) {
+ continue;
+ }
+
+ for (auto digest : digests) {
+ string signature = SignMessage(message, AuthorizationSetBuilder().Digest(digest));
+ VerifyMessage(message, signature, AuthorizationSetBuilder().Digest(digest));
+
+ // Verify with OpenSSL
+ if (digest != Digest::NONE) {
+ HidlBuf pubkey;
+ ASSERT_EQ(ErrorCode::OK, ExportKey(KeyFormat::X509, &pubkey))
+ << curve << ' ' << digest;
+
+ const uint8_t* p = pubkey.data();
+ EVP_PKEY_Ptr pkey(d2i_PUBKEY(nullptr /* alloc new */, &p, pubkey.size()));
+ ASSERT_TRUE(pkey.get());
+
+ EVP_MD_CTX digest_ctx;
+ EVP_MD_CTX_init(&digest_ctx);
+ EVP_PKEY_CTX* pkey_ctx;
+ const EVP_MD* md = openssl_digest(digest);
+
+ EXPECT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, md, nullptr /* engine */,
+ pkey.get()))
+ << curve << ' ' << digest;
+
+ EXPECT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx, message.data(), message.size()))
+ << curve << ' ' << digest;
+
+ EXPECT_EQ(1, EVP_DigestVerifyFinal(
+ &digest_ctx, reinterpret_cast<const uint8_t*>(signature.data()),
+ signature.size()))
+ << curve << ' ' << digest;
+
+ EVP_MD_CTX_cleanup(&digest_ctx);
+ }
+
+ // Corrupt signature shouldn't verify.
+ string corrupt_signature(signature);
+ ++corrupt_signature[corrupt_signature.size() / 2];
+
+ EXPECT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::VERIFY, AuthorizationSetBuilder().Digest(digest)))
+ << curve << ' ' << digest;
+
+ string result;
+ EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(message, corrupt_signature, &result))
+ << curve << ' ' << digest;
+
+ // Corrupt message shouldn't verify
+ EXPECT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::VERIFY, AuthorizationSetBuilder().Digest(digest)))
+ << curve << ' ' << digest;
+
+ EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(corrupt_message, signature, &result))
+ << curve << ' ' << digest;
+ }
+
+ ASSERT_EQ(ErrorCode::OK, DeleteKey());
+ }
+}
+
+/*
+ * VerificationOperationsTest.HmacSigningKeyCannotVerify
+ *
+ * Verifies HMAC signing and verification, but that a signing key cannot be used to verify.
+ */
+TEST_F(VerificationOperationsTest, HmacSigningKeyCannotVerify) {
+ string key_material = "HelloThisIsAKey";
+
+ HidlBuf signing_key, verification_key;
+ KeyCharacteristics signing_key_chars, verification_key_chars;
+ EXPECT_EQ(ErrorCode::OK,
+ ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Authorization(TAG_ALGORITHM, Algorithm::HMAC)
+ .Authorization(TAG_PURPOSE, KeyPurpose::SIGN)
+ .Digest(Digest::SHA1)
+ .Authorization(TAG_MIN_MAC_LENGTH, 160),
+ KeyFormat::RAW, key_material, &signing_key, &signing_key_chars));
+ EXPECT_EQ(ErrorCode::OK,
+ ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Authorization(TAG_ALGORITHM, Algorithm::HMAC)
+ .Authorization(TAG_PURPOSE, KeyPurpose::VERIFY)
+ .Digest(Digest::SHA1)
+ .Authorization(TAG_MIN_MAC_LENGTH, 160),
+ KeyFormat::RAW, key_material, &verification_key, &verification_key_chars));
+
+ string message = "This is a message.";
+ string signature = SignMessage(
+ signing_key, message,
+ AuthorizationSetBuilder().Digest(Digest::SHA1).Authorization(TAG_MAC_LENGTH, 160));
+
+ // Signing key should not work.
+ AuthorizationSet out_params;
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
+ Begin(KeyPurpose::VERIFY, signing_key, AuthorizationSetBuilder().Digest(Digest::SHA1),
+ &out_params, &op_handle_));
+
+ // Verification key should work.
+ VerifyMessage(verification_key, message, signature,
+ AuthorizationSetBuilder().Digest(Digest::SHA1));
+
+ EXPECT_EQ(ErrorCode::OK, DeleteKey(&signing_key));
+ EXPECT_EQ(ErrorCode::OK, DeleteKey(&verification_key));
+}
+
+typedef KeymasterHidlTest ExportKeyTest;
+
+/*
+ * ExportKeyTest.RsaUnsupportedKeyFormat
+ *
+ * Verifies that attempting to export RSA keys in PKCS#8 format fails with the correct error.
+ */
+TEST_F(ExportKeyTest, RsaUnsupportedKeyFormat) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)));
+ HidlBuf export_data;
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_FORMAT, ExportKey(KeyFormat::PKCS8, &export_data));
+}
+
+/*
+ * ExportKeyTest.RsaCorruptedKeyBlob
+ *
+ * Verifies that attempting to export RSA keys from corrupted key blobs fails. This is essentially
+ * a poor-man's key blob fuzzer.
+ */
+// Disabled due to b/33385206
+TEST_F(ExportKeyTest, DISABLED_RsaCorruptedKeyBlob) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(256, 3)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)));
+ for (size_t i = 0; i < key_blob_.size(); ++i) {
+ HidlBuf corrupted(key_blob_);
+ ++corrupted[i];
+
+ HidlBuf export_data;
+ EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+ ExportKey(KeyFormat::X509, corrupted, HidlBuf(), HidlBuf(), &export_data))
+ << "Blob corrupted at offset " << i << " erroneously accepted as valid";
+ }
+}
+
+/*
+ * ExportKeyTest.RsaCorruptedKeyBlob
+ *
+ * Verifies that attempting to export ECDSA keys from corrupted key blobs fails. This is
+ * essentially a poor-man's key blob fuzzer.
+ */
+// Disabled due to b/33385206
+TEST_F(ExportKeyTest, DISABLED_EcCorruptedKeyBlob) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(EcCurve::P_256)
+ .Digest(Digest::NONE)));
+ for (size_t i = 0; i < key_blob_.size(); ++i) {
+ HidlBuf corrupted(key_blob_);
+ ++corrupted[i];
+
+ HidlBuf export_data;
+ EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+ ExportKey(KeyFormat::X509, corrupted, HidlBuf(), HidlBuf(), &export_data))
+ << "Blob corrupted at offset " << i << " erroneously accepted as valid";
+ }
+}
+
+/*
+ * ExportKeyTest.AesKeyUnexportable
+ *
+ * Verifies that attempting to export AES keys fails in the expected way.
+ */
+TEST_F(ExportKeyTest, AesKeyUnexportable) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .EcbMode()
+ .Padding(PaddingMode::NONE)));
+
+ HidlBuf export_data;
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_FORMAT, ExportKey(KeyFormat::X509, &export_data));
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_FORMAT, ExportKey(KeyFormat::PKCS8, &export_data));
+ EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_FORMAT, ExportKey(KeyFormat::RAW, &export_data));
+}
+typedef KeymasterHidlTest ImportKeyTest;
+
+/*
+ * ImportKeyTest.RsaSuccess
+ *
+ * Verifies that importing and using an RSA key pair works correctly.
+ */
+TEST_F(ImportKeyTest, RsaSuccess) {
+ ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(1024, 65537)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::RSA_PSS),
+ KeyFormat::PKCS8, rsa_key));
+
+ CheckKm0CryptoParam(TAG_ALGORITHM, Algorithm::RSA);
+ CheckKm0CryptoParam(TAG_KEY_SIZE, 1024U);
+ CheckKm0CryptoParam(TAG_RSA_PUBLIC_EXPONENT, 65537U);
+ CheckKm1CryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+ CheckKm1CryptoParam(TAG_PADDING, PaddingMode::RSA_PSS);
+ CheckOrigin();
+
+ string message(1024 / 8, 'a');
+ auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS);
+ string signature = SignMessage(message, params);
+ VerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.RsaKeySizeMismatch
+ *
+ * Verifies that importing an RSA key pair with a size that doesn't match the key fails in the
+ * correct way.
+ */
+TEST_F(ImportKeyTest, RsaKeySizeMismatch) {
+ ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
+ ImportKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048 /* Doesn't match key */, 65537)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE),
+ KeyFormat::PKCS8, rsa_key));
+}
+
+/*
+ * ImportKeyTest.RsaPublicExponentMismatch
+ *
+ * Verifies that importing an RSA key pair with a public exponent that doesn't match the key fails
+ * in the correct way.
+ */
+TEST_F(ImportKeyTest, RsaPublicExponentMismatch) {
+ ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
+ ImportKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3 /* Doesn't match key */)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE),
+ KeyFormat::PKCS8, rsa_key));
+}
+
+/*
+ * ImportKeyTest.EcdsaSuccess
+ *
+ * Verifies that importing and using an ECDSA key pair works correctly.
+ */
+TEST_F(ImportKeyTest, EcdsaSuccess) {
+ ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(256)
+ .Digest(Digest::SHA_2_256),
+ KeyFormat::PKCS8, ec_key))
+ << "(Possibly b/33945114)";
+
+ CheckKm0CryptoParam(TAG_ALGORITHM, Algorithm::EC);
+ CheckKm0CryptoParam(TAG_KEY_SIZE, 256U);
+ CheckKm1CryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+ CheckKm2CryptoParam(TAG_EC_CURVE, EcCurve::P_256);
+
+ CheckOrigin();
+
+ string message(32, 'a');
+ auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
+ string signature = SignMessage(message, params);
+ VerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.EcdsaSizeMismatch
+ *
+ * Verifies that importing an ECDSA key pair with a size that doesn't match the key fails in the
+ * correct way.
+ */
+TEST_F(ImportKeyTest, EcdsaSizeMismatch) {
+ ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
+ ImportKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(224 /* Doesn't match key */)
+ .Digest(Digest::NONE),
+ KeyFormat::PKCS8, ec_key));
+}
+
+/*
+ * ImportKeyTest.EcdsaCurveMismatch
+ *
+ * Verifies that importing an ECDSA key pair with a curve that doesn't match the key fails in the
+ * correct way.
+ */
+TEST_F(ImportKeyTest, EcdsaCurveMismatch) {
+ if (SupportsSymmetric() && !SupportsAttestation()) {
+ // KM1 hardware doesn't know about curves
+ return;
+ }
+
+ ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
+ ImportKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::P_224 /* Doesn't match key */)
+ .Digest(Digest::NONE),
+ KeyFormat::PKCS8, ec_key))
+ << "(Possibly b/36233241)";
+}
+
+/*
+ * ImportKeyTest.AesSuccess
+ *
+ * Verifies that importing and using an AES key works.
+ */
+TEST_F(ImportKeyTest, AesSuccess) {
+ string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(key.size() * 8)
+ .EcbMode()
+ .Padding(PaddingMode::PKCS7),
+ KeyFormat::RAW, key));
+
+ CheckKm1CryptoParam(TAG_ALGORITHM, Algorithm::AES);
+ CheckKm1CryptoParam(TAG_KEY_SIZE, 128U);
+ CheckKm1CryptoParam(TAG_PADDING, PaddingMode::PKCS7);
+ CheckKm1CryptoParam(TAG_BLOCK_MODE, BlockMode::ECB);
+ CheckOrigin();
+
+ string message = "Hello World!";
+ auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+ string ciphertext = EncryptMessage(message, params);
+ string plaintext = DecryptMessage(ciphertext, params);
+ EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * ImportKeyTest.AesSuccess
+ *
+ * Verifies that importing and using an HMAC key works.
+ */
+TEST_F(ImportKeyTest, HmacKeySuccess) {
+ string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .HmacKey(key.size() * 8)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 256),
+ KeyFormat::RAW, key));
+
+ CheckKm1CryptoParam(TAG_ALGORITHM, Algorithm::HMAC);
+ CheckKm1CryptoParam(TAG_KEY_SIZE, 128U);
+ CheckKm1CryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+ CheckOrigin();
+
+ string message = "Hello World!";
+ string signature = MacMessage(message, Digest::SHA_2_256, 256);
+ VerifyMessage(message, signature, AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+}
+
+typedef KeymasterHidlTest EncryptionOperationsTest;
+
+/*
+ * EncryptionOperationsTest.RsaNoPaddingSuccess
+ *
+ * Verifies that raw RSA encryption works.
+ */
+TEST_F(EncryptionOperationsTest, RsaNoPaddingSuccess) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(1024, 3)
+ .Padding(PaddingMode::NONE)));
+
+ string message = string(1024 / 8, 'a');
+ auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+ string ciphertext1 = EncryptMessage(message, params);
+ EXPECT_EQ(1024U / 8, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(message, params);
+ EXPECT_EQ(1024U / 8, ciphertext2.size());
+
+ // Unpadded RSA is deterministic
+ EXPECT_EQ(ciphertext1, ciphertext2);
+}
+
+/*
+ * EncryptionOperationsTest.RsaNoPaddingShortMessage
+ *
+ * Verifies that raw RSA encryption of short messages works.
+ */
+TEST_F(EncryptionOperationsTest, RsaNoPaddingShortMessage) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(1024, 3)
+ .Padding(PaddingMode::NONE)));
+
+ string message = "1";
+ auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+
+ string ciphertext = EncryptMessage(message, params);
+ EXPECT_EQ(1024U / 8, ciphertext.size());
+
+ string expected_plaintext = string(1024 / 8 - 1, 0) + message;
+ string plaintext = DecryptMessage(ciphertext, params);
+
+ EXPECT_EQ(expected_plaintext, plaintext);
+
+ // Degenerate case, encrypting a numeric 1 yields 0x00..01 as the ciphertext.
+ message = static_cast<char>(1);
+ ciphertext = EncryptMessage(message, params);
+ EXPECT_EQ(1024U / 8, ciphertext.size());
+ EXPECT_EQ(ciphertext, string(1024 / 8 - 1, 0) + message);
+}
+
+/*
+ * EncryptionOperationsTest.RsaNoPaddingTooLong
+ *
+ * Verifies that raw RSA encryption of too-long messages fails in the expected way.
+ */
+TEST_F(EncryptionOperationsTest, RsaNoPaddingTooLong) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(1024, 3)
+ .Padding(PaddingMode::NONE)));
+
+ string message(1024 / 8 + 1, 'a');
+
+ auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
+
+ string result;
+ EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &result));
+}
+
+/*
+ * EncryptionOperationsTest.RsaNoPaddingTooLong
+ *
+ * Verifies that raw RSA encryption of too-large (numerically) messages fails in the expected way.
+ */
+TEST_F(EncryptionOperationsTest, RsaNoPaddingTooLarge) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(1024, 3)
+ .Padding(PaddingMode::NONE)));
+
+ HidlBuf exported;
+ ASSERT_EQ(ErrorCode::OK, ExportKey(KeyFormat::X509, &exported));
+
+ const uint8_t* p = exported.data();
+ EVP_PKEY_Ptr pkey(d2i_PUBKEY(nullptr /* alloc new */, &p, exported.size()));
+ RSA_Ptr rsa(EVP_PKEY_get1_RSA(pkey.get()));
+
+ size_t modulus_len = BN_num_bytes(rsa->n);
+ ASSERT_EQ(1024U / 8, modulus_len);
+ std::unique_ptr<uint8_t[]> modulus_buf(new uint8_t[modulus_len]);
+ BN_bn2bin(rsa->n, modulus_buf.get());
+
+ // The modulus is too big to encrypt.
+ string message(reinterpret_cast<const char*>(modulus_buf.get()), modulus_len);
+
+ auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
+
+ string result;
+ EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(message, &result));
+
+ // One smaller than the modulus is okay.
+ BN_sub(rsa->n, rsa->n, BN_value_one());
+ modulus_len = BN_num_bytes(rsa->n);
+ ASSERT_EQ(1024U / 8, modulus_len);
+ BN_bn2bin(rsa->n, modulus_buf.get());
+ message = string(reinterpret_cast<const char*>(modulus_buf.get()), modulus_len);
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
+ EXPECT_EQ(ErrorCode::OK, Finish(message, &result));
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepSuccess
+ *
+ * Verifies that RSA-OAEP encryption operations work, with all digests.
+ */
+TEST_F(EncryptionOperationsTest, RsaOaepSuccess) {
+ auto digests = {Digest::MD5, Digest::SHA1, Digest::SHA_2_224,
+ Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512};
+
+ size_t key_size = 2048; // Need largish key for SHA-512 test.
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(key_size, 3)
+ .Padding(PaddingMode::RSA_OAEP)
+ .Digest(digests)));
+
+ string message = "Hello";
+
+ for (auto digest : digests) {
+ auto params = AuthorizationSetBuilder().Digest(digest).Padding(PaddingMode::RSA_OAEP);
+ string ciphertext1 = EncryptMessage(message, params);
+ if (HasNonfatalFailure()) std::cout << "-->" << digest << std::endl;
+ EXPECT_EQ(key_size / 8, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(message, params);
+ EXPECT_EQ(key_size / 8, ciphertext2.size());
+
+ // OAEP randomizes padding so every result should be different (with astronomically high
+ // probability).
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ string plaintext1 = DecryptMessage(ciphertext1, params);
+ EXPECT_EQ(message, plaintext1) << "RSA-OAEP failed with digest " << digest;
+ string plaintext2 = DecryptMessage(ciphertext2, params);
+ EXPECT_EQ(message, plaintext2) << "RSA-OAEP failed with digest " << digest;
+
+ // Decrypting corrupted ciphertext should fail.
+ size_t offset_to_corrupt = random() % ciphertext1.size();
+ char corrupt_byte;
+ do {
+ corrupt_byte = static_cast<char>(random() % 256);
+ } while (corrupt_byte == ciphertext1[offset_to_corrupt]);
+ ciphertext1[offset_to_corrupt] = corrupt_byte;
+
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+ string result;
+ EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result));
+ EXPECT_EQ(0U, result.size());
+ }
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepInvalidDigest
+ *
+ * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to operate
+ * without a digest.
+ */
+TEST_F(EncryptionOperationsTest, RsaOaepInvalidDigest) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(1024, 3)
+ .Padding(PaddingMode::RSA_OAEP)
+ .Digest(Digest::NONE)));
+ string message = "Hello World!";
+
+ auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_OAEP).Digest(Digest::NONE);
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepInvalidDigest
+ *
+ * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to decrypt with a
+ * different digest than was used to encrypt.
+ */
+TEST_F(EncryptionOperationsTest, RsaOaepDecryptWithWrongDigest) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(1024, 3)
+ .Padding(PaddingMode::RSA_OAEP)
+ .Digest(Digest::SHA_2_256, Digest::SHA_2_224)));
+ string message = "Hello World!";
+ string ciphertext = EncryptMessage(
+ message,
+ AuthorizationSetBuilder().Digest(Digest::SHA_2_224).Padding(PaddingMode::RSA_OAEP));
+
+ EXPECT_EQ(
+ ErrorCode::OK,
+ Begin(KeyPurpose::DECRYPT,
+ AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_OAEP)));
+ string result;
+ EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext, &result));
+ EXPECT_EQ(0U, result.size());
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepTooLarge
+ *
+ * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to encrypt a
+ * too-large message.
+ */
+TEST_F(EncryptionOperationsTest, RsaOaepTooLarge) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(1024, 3)
+ .Padding(PaddingMode::RSA_OAEP)
+ .Digest(Digest::SHA1)));
+ constexpr size_t digest_size = 160 /* SHA1 */ / 8;
+ constexpr size_t oaep_overhead = 2 * digest_size + 2;
+ string message(1024 / 8 - oaep_overhead + 1, 'a');
+ EXPECT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::ENCRYPT,
+ AuthorizationSetBuilder().Padding(PaddingMode::RSA_OAEP).Digest(Digest::SHA1)));
+ string result;
+ EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &result));
+ EXPECT_EQ(0U, result.size());
+}
+
+/*
+ * EncryptionOperationsTest.RsaPkcs1Success
+ *
+ * Verifies that RSA PKCS encryption/decrypts works.
+ */
+TEST_F(EncryptionOperationsTest, RsaPkcs1Success) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(1024, 3)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT)));
+
+ string message = "Hello World!";
+ auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT);
+ string ciphertext1 = EncryptMessage(message, params);
+ EXPECT_EQ(1024U / 8, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(message, params);
+ EXPECT_EQ(1024U / 8, ciphertext2.size());
+
+ // PKCS1 v1.5 randomizes padding so every result should be different.
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ string plaintext = DecryptMessage(ciphertext1, params);
+ EXPECT_EQ(message, plaintext);
+
+ // Decrypting corrupted ciphertext should fail.
+ size_t offset_to_corrupt = random() % ciphertext1.size();
+ char corrupt_byte;
+ do {
+ corrupt_byte = static_cast<char>(random() % 256);
+ } while (corrupt_byte == ciphertext1[offset_to_corrupt]);
+ ciphertext1[offset_to_corrupt] = corrupt_byte;
+
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+ string result;
+ EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result));
+ EXPECT_EQ(0U, result.size());
+}
+
+/*
+ * EncryptionOperationsTest.RsaPkcs1TooLarge
+ *
+ * Verifies that RSA PKCS encryption fails in the correct way when the mssage is too large.
+ */
+TEST_F(EncryptionOperationsTest, RsaPkcs1TooLarge) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaEncryptionKey(1024, 3)
+ .Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT)));
+ string message(1024 / 8 - 10, 'a');
+
+ auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT);
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
+ string result;
+ EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &result));
+ EXPECT_EQ(0U, result.size());
+}
+
+/*
+ * EncryptionOperationsTest.EcdsaEncrypt
+ *
+ * Verifies that attempting to use ECDSA keys to encrypt fails in the correct way.
+ */
+TEST_F(EncryptionOperationsTest, EcdsaEncrypt) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(224)
+ .Digest(Digest::NONE)));
+ auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::ENCRYPT, params))
+ << "(Possibly b/33543625)";
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::DECRYPT, params))
+ << "(Possibly b/33543625)";
+}
+
+/*
+ * EncryptionOperationsTest.HmacEncrypt
+ *
+ * Verifies that attempting to use HMAC keys to encrypt fails in the correct way.
+ */
+TEST_F(EncryptionOperationsTest, HmacEncrypt) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .HmacKey(128)
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ auto params = AuthorizationSetBuilder()
+ .Digest(Digest::SHA_2_256)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 128);
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::ENCRYPT, params))
+ << "(Possibly b/33543625)";
+ ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::DECRYPT, params))
+ << "(Possibly b/33543625)";
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbRoundTripSuccess
+ *
+ * Verifies that AES ECB mode works.
+ */
+TEST_F(EncryptionOperationsTest, AesEcbRoundTripSuccess) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+ .Padding(PaddingMode::NONE)));
+
+ auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+
+ // Two-block message.
+ string message = "12345678901234567890123456789012";
+ string ciphertext1 = EncryptMessage(message, params);
+ EXPECT_EQ(message.size(), ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(string(message), params);
+ EXPECT_EQ(message.size(), ciphertext2.size());
+
+ // ECB is deterministic.
+ EXPECT_EQ(ciphertext1, ciphertext2);
+
+ string plaintext = DecryptMessage(ciphertext1, params);
+ EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbRoundTripSuccess
+ *
+ * Verifies that AES encryption fails in the correct way when an unauthorized mode is specified.
+ */
+TEST_F(EncryptionOperationsTest, AesWrongMode) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+ .Padding(PaddingMode::NONE)));
+ // Two-block message.
+ string message = "12345678901234567890123456789012";
+ EXPECT_EQ(
+ ErrorCode::INCOMPATIBLE_BLOCK_MODE,
+ Begin(KeyPurpose::ENCRYPT,
+ AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE)));
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbNoPaddingWrongInputSize
+ *
+ * Verifies that AES encryption fails in the correct way when provided an input that is not a
+ * multiple of the block size and no padding is specified.
+ */
+TEST_F(EncryptionOperationsTest, AesEcbNoPaddingWrongInputSize) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+ .Padding(PaddingMode::NONE)));
+ // Message is slightly shorter than two blocks.
+ string message(16 * 2 - 1, 'a');
+
+ auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
+ string ciphertext;
+ EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &ciphertext));
+ EXPECT_EQ(0U, ciphertext.size());
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbPkcs7Padding
+ *
+ * Verifies that AES PKCS7 padding works for any message length.
+ */
+TEST_F(EncryptionOperationsTest, AesEcbPkcs7Padding) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+ .Padding(PaddingMode::PKCS7)));
+
+ auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+
+ // Try various message lengths; all should work.
+ for (size_t i = 0; i < 32; ++i) {
+ string message(i, 'a');
+ string ciphertext = EncryptMessage(message, params);
+ EXPECT_EQ(i + 16 - (i % 16), ciphertext.size());
+ string plaintext = DecryptMessage(ciphertext, params);
+ EXPECT_EQ(message, plaintext);
+ }
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbWrongPadding
+ *
+ * Verifies that AES enryption fails in the correct way when an unauthorized padding mode is
+ * specified.
+ */
+TEST_F(EncryptionOperationsTest, AesEcbWrongPadding) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+ .Padding(PaddingMode::NONE)));
+
+ auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+
+ // Try various message lengths; all should fail
+ for (size_t i = 0; i < 32; ++i) {
+ string message(i, 'a');
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, params));
+ }
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbPkcs7PaddingCorrupted
+ *
+ * Verifies that AES decryption fails in the correct way when the padding is corrupted.
+ */
+TEST_F(EncryptionOperationsTest, AesEcbPkcs7PaddingCorrupted) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+ .Padding(PaddingMode::PKCS7)));
+
+ auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+
+ string message = "a";
+ string ciphertext = EncryptMessage(message, params);
+ EXPECT_EQ(16U, ciphertext.size());
+ EXPECT_NE(ciphertext, message);
+ ++ciphertext[ciphertext.size() / 2];
+
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+ string plaintext;
+ EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &plaintext));
+}
+
+HidlBuf CopyIv(const AuthorizationSet& set) {
+ auto iv = set.GetTagValue(TAG_NONCE);
+ EXPECT_TRUE(iv.isOk());
+ return iv.value();
+}
+
+/*
+ * EncryptionOperationsTest.AesCtrRoundTripSuccess
+ *
+ * Verifies that AES CTR mode works.
+ */
+TEST_F(EncryptionOperationsTest, AesCtrRoundTripSuccess) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::CTR)
+ .Padding(PaddingMode::NONE)));
+
+ auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CTR).Padding(PaddingMode::NONE);
+
+ string message = "123";
+ AuthorizationSet out_params;
+ string ciphertext1 = EncryptMessage(message, params, &out_params);
+ HidlBuf iv1 = CopyIv(out_params);
+ EXPECT_EQ(16U, iv1.size());
+
+ EXPECT_EQ(message.size(), ciphertext1.size());
+
+ out_params.Clear();
+ string ciphertext2 = EncryptMessage(message, params, &out_params);
+ HidlBuf iv2 = CopyIv(out_params);
+ EXPECT_EQ(16U, iv2.size());
+
+ // IVs should be random, so ciphertexts should differ.
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ auto params_iv1 =
+ AuthorizationSetBuilder().Authorizations(params).Authorization(TAG_NONCE, iv1);
+ auto params_iv2 =
+ AuthorizationSetBuilder().Authorizations(params).Authorization(TAG_NONCE, iv2);
+
+ string plaintext = DecryptMessage(ciphertext1, params_iv1);
+ EXPECT_EQ(message, plaintext);
+ plaintext = DecryptMessage(ciphertext2, params_iv2);
+ EXPECT_EQ(message, plaintext);
+
+ // Using the wrong IV will result in a "valid" decryption, but the data will be garbage.
+ plaintext = DecryptMessage(ciphertext1, params_iv2);
+ EXPECT_NE(message, plaintext);
+ plaintext = DecryptMessage(ciphertext2, params_iv1);
+ EXPECT_NE(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesIncremental
+ *
+ * Verifies that AES works, all modes, when provided data in various size increments.
+ */
+TEST_F(EncryptionOperationsTest, AesIncremental) {
+ auto block_modes = {
+ BlockMode::ECB, BlockMode::CBC, BlockMode::CTR, BlockMode::GCM,
+ };
+
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .BlockMode(block_modes)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ for (int increment = 1; increment <= 240; ++increment) {
+ for (auto block_mode : block_modes) {
+ string message(240, 'a');
+ auto params = AuthorizationSetBuilder()
+ .BlockMode(block_mode)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 128) /* for GCM */;
+
+ AuthorizationSet output_params;
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &output_params));
+
+ string ciphertext;
+ size_t input_consumed;
+ string to_send;
+ for (size_t i = 0; i < message.size(); i += increment) {
+ to_send.append(message.substr(i, increment));
+ EXPECT_EQ(ErrorCode::OK, Update(to_send, &ciphertext, &input_consumed));
+ to_send = to_send.substr(input_consumed);
+
+ switch (block_mode) {
+ case BlockMode::ECB:
+ case BlockMode::CBC:
+ // Implementations must take as many blocks as possible, leaving less than
+ // a block.
+ EXPECT_LE(to_send.length(), 16U);
+ break;
+ case BlockMode::GCM:
+ case BlockMode::CTR:
+ // Implementations must always take all the data.
+ EXPECT_EQ(0U, to_send.length());
+ break;
+ }
+ }
+ EXPECT_EQ(ErrorCode::OK, Finish(to_send, &ciphertext)) << "Error sending " << to_send;
+
+ switch (block_mode) {
+ case BlockMode::GCM:
+ EXPECT_EQ(message.size() + 16, ciphertext.size());
+ break;
+ case BlockMode::CTR:
+ EXPECT_EQ(message.size(), ciphertext.size());
+ break;
+ case BlockMode::CBC:
+ case BlockMode::ECB:
+ EXPECT_EQ(message.size() + message.size() % 16, ciphertext.size());
+ break;
+ }
+
+ auto iv = output_params.GetTagValue(TAG_NONCE);
+ switch (block_mode) {
+ case BlockMode::CBC:
+ case BlockMode::GCM:
+ case BlockMode::CTR:
+ ASSERT_TRUE(iv.isOk()) << "No IV for block mode " << block_mode;
+ EXPECT_EQ(block_mode == BlockMode::GCM ? 12U : 16U, iv.value().size());
+ params.push_back(TAG_NONCE, iv.value());
+ break;
+
+ case BlockMode::ECB:
+ EXPECT_FALSE(iv.isOk()) << "ECB mode should not generate IV";
+ break;
+ }
+
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params))
+ << "Decrypt begin() failed for block mode " << block_mode;
+
+ string plaintext;
+ for (size_t i = 0; i < ciphertext.size(); i += increment) {
+ to_send.append(ciphertext.substr(i, increment));
+ EXPECT_EQ(ErrorCode::OK, Update(to_send, &plaintext, &input_consumed));
+ to_send = to_send.substr(input_consumed);
+ }
+ ErrorCode error = Finish(to_send, &plaintext);
+ ASSERT_EQ(ErrorCode::OK, error)
+ << "Decryption failed for block mode " << block_mode << " and increment "
+ << increment << " (Possibly b/33584622)";
+ if (error == ErrorCode::OK) {
+ ASSERT_EQ(message, plaintext) << "Decryption didn't match for block mode "
+ << block_mode << " and increment " << increment;
+ }
+ }
+ }
+}
+
+struct AesCtrSp80038aTestVector {
+ const char* key;
+ const char* nonce;
+ const char* plaintext;
+ const char* ciphertext;
+};
+
+// These test vectors are taken from
+// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, section F.5.
+static const AesCtrSp80038aTestVector kAesCtrSp80038aTestVectors[] = {
+ // AES-128
+ {
+ "2b7e151628aed2a6abf7158809cf4f3c", "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+ "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
+ "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+ "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff"
+ "5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee",
+ },
+ // AES-192
+ {
+ "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+ "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
+ "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+ "1abc932417521ca24f2b0459fe7e6e0b090339ec0aa6faefd5ccc2c6f4ce8e94"
+ "1e36b26bd1ebc670d1bd1d665620abf74f78a7f6d29809585a97daec58c6b050",
+ },
+ // AES-256
+ {
+ "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
+ "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+ "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
+ "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+ "601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5"
+ "2b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6",
+ },
+};
+
+/*
+ * EncryptionOperationsTest.AesCtrSp80038aTestVector
+ *
+ * Verifies AES CTR implementation against SP800-38A test vectors.
+ */
+TEST_F(EncryptionOperationsTest, AesCtrSp80038aTestVector) {
+ for (size_t i = 0; i < 3; i++) {
+ const AesCtrSp80038aTestVector& test(kAesCtrSp80038aTestVectors[i]);
+ const string key = hex2str(test.key);
+ const string nonce = hex2str(test.nonce);
+ const string plaintext = hex2str(test.plaintext);
+ const string ciphertext = hex2str(test.ciphertext);
+ CheckAesCtrTestVector(key, nonce, plaintext, ciphertext);
+ }
+}
+
+/*
+ * EncryptionOperationsTest.AesCtrIncompatiblePaddingMode
+ *
+ * Verifies that keymaster rejects use of CTR mode with PKCS7 padding in the correct way.
+ */
+TEST_F(EncryptionOperationsTest, AesCtrIncompatiblePaddingMode) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::CTR)
+ .Padding(PaddingMode::PKCS7)));
+ auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CTR).Padding(PaddingMode::NONE);
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesCtrInvalidCallerNonce
+ *
+ * Verifies that keymaster fails correctly when the user supplies an incorrect-size nonce.
+ */
+TEST_F(EncryptionOperationsTest, AesCtrInvalidCallerNonce) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::CTR)
+ .Authorization(TAG_CALLER_NONCE)
+ .Padding(PaddingMode::NONE)));
+
+ auto params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::CTR)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NONCE, HidlBuf(string(1, 'a')));
+ EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
+
+ params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::CTR)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NONCE, HidlBuf(string(15, 'a')));
+ EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
+
+ params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::CTR)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NONCE, HidlBuf(string(17, 'a')));
+ EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesCtrInvalidCallerNonce
+ *
+ * Verifies that keymaster fails correctly when the user supplies an incorrect-size nonce.
+ */
+TEST_F(EncryptionOperationsTest, AesCbcRoundTripSuccess) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+ .Padding(PaddingMode::NONE)));
+ // Two-block message.
+ string message = "12345678901234567890123456789012";
+ auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+ AuthorizationSet out_params;
+ string ciphertext1 = EncryptMessage(message, params, &out_params);
+ HidlBuf iv1 = CopyIv(out_params);
+ EXPECT_EQ(message.size(), ciphertext1.size());
+
+ out_params.Clear();
+
+ string ciphertext2 = EncryptMessage(message, params, &out_params);
+ HidlBuf iv2 = CopyIv(out_params);
+ EXPECT_EQ(message.size(), ciphertext2.size());
+
+ // IVs should be random, so ciphertexts should differ.
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ params.push_back(TAG_NONCE, iv1);
+ string plaintext = DecryptMessage(ciphertext1, params);
+ EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesCallerNonce
+ *
+ * Verifies that AES caller-provided nonces work correctly.
+ */
+TEST_F(EncryptionOperationsTest, AesCallerNonce) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+ .Authorization(TAG_CALLER_NONCE)
+ .Padding(PaddingMode::NONE)));
+
+ string message = "12345678901234567890123456789012";
+
+ // Don't specify nonce, should get a random one.
+ AuthorizationSetBuilder params =
+ AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+ AuthorizationSet out_params;
+ string ciphertext = EncryptMessage(message, params, &out_params);
+ EXPECT_EQ(message.size(), ciphertext.size());
+ EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE).value().size());
+
+ params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE).value());
+ string plaintext = DecryptMessage(ciphertext, params);
+ EXPECT_EQ(message, plaintext);
+
+ // Now specify a nonce, should also work.
+ params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::CBC)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NONCE, HidlBuf("abcdefghijklmnop"));
+ out_params.Clear();
+ ciphertext = EncryptMessage(message, params, &out_params);
+
+ // Decrypt with correct nonce.
+ plaintext = DecryptMessage(ciphertext, params);
+ EXPECT_EQ(message, plaintext);
+
+ // Try with wrong nonce.
+ params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::CBC)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NONCE, HidlBuf("aaaaaaaaaaaaaaaa"));
+ plaintext = DecryptMessage(ciphertext, params);
+ EXPECT_NE(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesCallerNonceProhibited
+ *
+ * Verifies that caller-provided nonces are not permitted when not specified in the key
+ * authorizations.
+ */
+TEST_F(EncryptionOperationsTest, AesCallerNonceProhibited) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+ .Padding(PaddingMode::NONE)));
+
+ string message = "12345678901234567890123456789012";
+
+ // Don't specify nonce, should get a random one.
+ AuthorizationSetBuilder params =
+ AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+ AuthorizationSet out_params;
+ string ciphertext = EncryptMessage(message, params, &out_params);
+ EXPECT_EQ(message.size(), ciphertext.size());
+ EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE).value().size());
+
+ params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE).value());
+ string plaintext = DecryptMessage(ciphertext, params);
+ EXPECT_EQ(message, plaintext);
+
+ // Now specify a nonce, should fail
+ params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::CBC)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NONCE, HidlBuf("abcdefghijklmnop"));
+ out_params.Clear();
+ EXPECT_EQ(ErrorCode::CALLER_NONCE_PROHIBITED, Begin(KeyPurpose::ENCRYPT, params, &out_params));
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmRoundTripSuccess
+ *
+ * Verifies that AES GCM mode works.
+ */
+TEST_F(EncryptionOperationsTest, AesGcmRoundTripSuccess) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ string aad = "foobar";
+ string message = "123456789012345678901234567890123456";
+
+ auto begin_params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 128);
+
+ auto update_params =
+ AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params))
+ << "Begin encrypt";
+ string ciphertext;
+ AuthorizationSet update_out_params;
+ ASSERT_EQ(ErrorCode::OK,
+ Finish(op_handle_, update_params, message, "", &update_out_params, &ciphertext));
+
+ // Grab nonce
+ begin_params.push_back(begin_out_params);
+
+ // Decrypt.
+ ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)) << "Begin decrypt";
+ string plaintext;
+ size_t input_consumed;
+ ASSERT_EQ(ErrorCode::OK, Update(op_handle_, update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(ErrorCode::OK, Finish("", &plaintext));
+
+ EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmTooShortTag
+ *
+ * Verifies that AES GCM mode fails correctly when a too-short tag length is specified.
+ */
+TEST_F(EncryptionOperationsTest, AesGcmTooShortTag) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ string message = "123456789012345678901234567890123456";
+ auto params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 96);
+
+ EXPECT_EQ(ErrorCode::INVALID_MAC_LENGTH, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmTooShortTagOnDecrypt
+ *
+ * Verifies that AES GCM mode fails correctly when a too-short tag is provided to decryption.
+ */
+TEST_F(EncryptionOperationsTest, AesGcmTooShortTagOnDecrypt) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ string aad = "foobar";
+ string message = "123456789012345678901234567890123456";
+ auto params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 128);
+
+ auto finish_params =
+ AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
+ EXPECT_EQ(1U, begin_out_params.size());
+ ASSERT_TRUE(begin_out_params.GetTagValue(TAG_NONCE).isOk());
+
+ AuthorizationSet finish_out_params;
+ string ciphertext;
+ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, message, "" /* signature */,
+ &finish_out_params, &ciphertext));
+
+ params = AuthorizationSetBuilder()
+ .Authorizations(begin_out_params)
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 96);
+
+ // Decrypt.
+ EXPECT_EQ(ErrorCode::INVALID_MAC_LENGTH, Begin(KeyPurpose::DECRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmCorruptKey
+ *
+ * Verifies that AES GCM mode fails correctly when the decryption key is incorrect.
+ */
+TEST_F(EncryptionOperationsTest, AesGcmCorruptKey) {
+ const uint8_t nonce_bytes[] = {
+ 0xb7, 0x94, 0x37, 0xae, 0x08, 0xff, 0x35, 0x5d, 0x7d, 0x8a, 0x4d, 0x0f,
+ };
+ string nonce = make_string(nonce_bytes);
+ const uint8_t ciphertext_bytes[] = {
+ 0xb3, 0xf6, 0x79, 0x9e, 0x8f, 0x93, 0x26, 0xf2, 0xdf, 0x1e, 0x80, 0xfc, 0xd2, 0xcb, 0x16,
+ 0xd7, 0x8c, 0x9d, 0xc7, 0xcc, 0x14, 0xbb, 0x67, 0x78, 0x62, 0xdc, 0x6c, 0x63, 0x9b, 0x3a,
+ 0x63, 0x38, 0xd2, 0x4b, 0x31, 0x2d, 0x39, 0x89, 0xe5, 0x92, 0x0b, 0x5d, 0xbf, 0xc9, 0x76,
+ 0x76, 0x5e, 0xfb, 0xfe, 0x57, 0xbb, 0x38, 0x59, 0x40, 0xa7, 0xa4, 0x3b, 0xdf, 0x05, 0xbd,
+ 0xda, 0xe3, 0xc9, 0xd6, 0xa2, 0xfb, 0xbd, 0xfc, 0xc0, 0xcb, 0xa0,
+ };
+ string ciphertext = make_string(ciphertext_bytes);
+
+ auto params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 128)
+ .Authorization(TAG_NONCE, nonce.data(), nonce.size());
+
+ auto import_params = AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_CALLER_NONCE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128);
+
+ // Import correct key and decrypt
+ const uint8_t key_bytes[] = {
+ 0xba, 0x76, 0x35, 0x4f, 0x0a, 0xed, 0x6e, 0x8d,
+ 0x91, 0xf4, 0x5c, 0x4f, 0xf5, 0xa0, 0x62, 0xdb,
+ };
+ string key = make_string(key_bytes);
+ ASSERT_EQ(ErrorCode::OK, ImportKey(import_params, KeyFormat::RAW, key));
+ string plaintext = DecryptMessage(ciphertext, params);
+ EXPECT_EQ(ErrorCode::OK, DeleteKey());
+
+ // Corrupt key and attempt to decrypt
+ key[0] = 0;
+ ASSERT_EQ(ErrorCode::OK, ImportKey(import_params, KeyFormat::RAW, key));
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+ EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext));
+ EXPECT_EQ(ErrorCode::OK, DeleteKey());
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmAadNoData
+ *
+ * Verifies that AES GCM mode works when provided additional authenticated data, but no data to
+ * encrypt.
+ */
+TEST_F(EncryptionOperationsTest, AesGcmAadNoData) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ string aad = "1234567890123456";
+ auto params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 128);
+
+ auto finish_params =
+ AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
+ string ciphertext;
+ AuthorizationSet finish_out_params;
+ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, "" /* input */, "" /* signature */,
+ &finish_out_params, &ciphertext));
+ EXPECT_TRUE(finish_out_params.empty());
+
+ // Grab nonce
+ params.push_back(begin_out_params);
+
+ // Decrypt.
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+ string plaintext;
+ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, ciphertext, "" /* signature */,
+ &finish_out_params, &plaintext))
+ << "(Possibly b/33615032)";
+
+ EXPECT_TRUE(finish_out_params.empty());
+
+ EXPECT_EQ("", plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmMultiPartAad
+ *
+ * Verifies that AES GCM mode works when provided additional authenticated data in multiple chunks.
+ */
+TEST_F(EncryptionOperationsTest, AesGcmMultiPartAad) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ string message = "123456789012345678901234567890123456";
+ auto begin_params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 128);
+ AuthorizationSet begin_out_params;
+
+ auto update_params =
+ AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foo", (size_t)3);
+
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
+
+ // No data, AAD only.
+ string ciphertext;
+ size_t input_consumed;
+ AuthorizationSet update_out_params;
+ EXPECT_EQ(ErrorCode::OK, Update(op_handle_, update_params, "" /* input */, &update_out_params,
+ &ciphertext, &input_consumed));
+ EXPECT_EQ(0U, input_consumed);
+ EXPECT_EQ(0U, ciphertext.size());
+ EXPECT_TRUE(update_out_params.empty());
+
+ // AAD and data.
+ EXPECT_EQ(ErrorCode::OK, Update(op_handle_, update_params, message, &update_out_params,
+ &ciphertext, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(message.size(), ciphertext.size());
+ EXPECT_TRUE(update_out_params.empty());
+
+ EXPECT_EQ(ErrorCode::OK, Finish("" /* input */, &ciphertext));
+
+ // Grab nonce.
+ begin_params.push_back(begin_out_params);
+
+ // Decrypt
+ update_params =
+ AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foofoo", (size_t)6);
+
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
+ string plaintext;
+ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, update_params, ciphertext, "" /* signature */,
+ &update_out_params, &plaintext));
+ EXPECT_TRUE(update_out_params.empty());
+ EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmAadOutOfOrder
+ *
+ * Verifies that AES GCM mode fails correctly when given AAD after data to encipher.
+ */
+TEST_F(EncryptionOperationsTest, AesGcmAadOutOfOrder) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ string message = "123456789012345678901234567890123456";
+ auto begin_params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 128);
+ AuthorizationSet begin_out_params;
+
+ auto update_params =
+ AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foo", (size_t)3);
+
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
+
+ // No data, AAD only.
+ string ciphertext;
+ size_t input_consumed;
+ AuthorizationSet update_out_params;
+ EXPECT_EQ(ErrorCode::OK, Update(op_handle_, update_params, "" /* input */, &update_out_params,
+ &ciphertext, &input_consumed));
+ EXPECT_EQ(0U, input_consumed);
+ EXPECT_EQ(0U, ciphertext.size());
+ EXPECT_TRUE(update_out_params.empty());
+
+ // AAD and data.
+ EXPECT_EQ(ErrorCode::OK, Update(op_handle_, update_params, message, &update_out_params,
+ &ciphertext, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(message.size(), ciphertext.size());
+ EXPECT_TRUE(update_out_params.empty());
+
+ // More AAD
+ EXPECT_EQ(ErrorCode::INVALID_TAG, Update(op_handle_, update_params, "", &update_out_params,
+ &ciphertext, &input_consumed));
+
+ op_handle_ = kOpHandleSentinel;
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmBadAad
+ *
+ * Verifies that AES GCM decryption fails correctly when additional authenticated date is wrong.
+ */
+TEST_F(EncryptionOperationsTest, AesGcmBadAad) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ string message = "12345678901234567890123456789012";
+ auto begin_params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 128);
+
+ auto finish_params =
+ AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foobar", (size_t)6);
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
+ string ciphertext;
+ AuthorizationSet finish_out_params;
+ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, message, "" /* signature */,
+ &finish_out_params, &ciphertext));
+
+ // Grab nonce
+ begin_params.push_back(begin_out_params);
+
+ finish_params = AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA,
+ "barfoo" /* Wrong AAD */, (size_t)6);
+
+ // Decrypt.
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params, &begin_out_params));
+ string plaintext;
+ EXPECT_EQ(ErrorCode::VERIFICATION_FAILED,
+ Finish(op_handle_, finish_params, ciphertext, "" /* signature */, &finish_out_params,
+ &plaintext));
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmWrongNonce
+ *
+ * Verifies that AES GCM decryption fails correctly when the nonce is incorrect.
+ */
+TEST_F(EncryptionOperationsTest, AesGcmWrongNonce) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ string message = "12345678901234567890123456789012";
+ auto begin_params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 128);
+
+ auto finish_params =
+ AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, "foobar", (size_t)6);
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
+ string ciphertext;
+ AuthorizationSet finish_out_params;
+ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, message, "" /* signature */,
+ &finish_out_params, &ciphertext));
+
+ // Wrong nonce
+ begin_params.push_back(TAG_NONCE, HidlBuf("123456789012"));
+
+ // Decrypt.
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params, &begin_out_params));
+ string plaintext;
+ EXPECT_EQ(ErrorCode::VERIFICATION_FAILED,
+ Finish(op_handle_, finish_params, ciphertext, "" /* signature */, &finish_out_params,
+ &plaintext));
+
+ // With wrong nonce, should have gotten garbage plaintext (or none).
+ EXPECT_NE(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmCorruptTag
+ *
+ * Verifies that AES GCM decryption fails correctly when the tag is wrong.
+ */
+TEST_F(EncryptionOperationsTest, AesGcmCorruptTag) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ string aad = "1234567890123456";
+ string message = "123456789012345678901234567890123456";
+
+ auto params = AuthorizationSetBuilder()
+ .BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, 128);
+
+ auto finish_params =
+ AuthorizationSetBuilder().Authorization(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
+ string ciphertext;
+ AuthorizationSet finish_out_params;
+ EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, message, "" /* signature */,
+ &finish_out_params, &ciphertext));
+ EXPECT_TRUE(finish_out_params.empty());
+
+ // Corrupt tag
+ ++(*ciphertext.rbegin());
+
+ // Grab nonce
+ params.push_back(begin_out_params);
+
+ // Decrypt.
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+ string plaintext;
+ EXPECT_EQ(ErrorCode::VERIFICATION_FAILED,
+ Finish(op_handle_, finish_params, ciphertext, "" /* signature */, &finish_out_params,
+ &plaintext));
+ EXPECT_TRUE(finish_out_params.empty());
+}
+
+typedef KeymasterHidlTest MaxOperationsTest;
+
+/*
+ * MaxOperationsTest.TestLimitAes
+ *
+ * Verifies that the max uses per boot tag works correctly with AES keys.
+ */
+TEST_F(MaxOperationsTest, TestLimitAes) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .EcbMode()
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAX_USES_PER_BOOT, 3)));
+
+ string message = "1234567890123456";
+
+ auto params = AuthorizationSetBuilder().EcbMode().Padding(PaddingMode::NONE);
+
+ EncryptMessage(message, params);
+ EncryptMessage(message, params);
+ EncryptMessage(message, params);
+
+ // Fourth time should fail.
+ EXPECT_EQ(ErrorCode::KEY_MAX_OPS_EXCEEDED, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * MaxOperationsTest.TestLimitAes
+ *
+ * Verifies that the max uses per boot tag works correctly with RSA keys.
+ */
+TEST_F(MaxOperationsTest, TestLimitRsa) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(1024, 3)
+ .NoDigestOrPadding()
+ .Authorization(TAG_MAX_USES_PER_BOOT, 3)));
+
+ string message = "1234567890123456";
+
+ auto params = AuthorizationSetBuilder().NoDigestOrPadding();
+
+ SignMessage(message, params);
+ SignMessage(message, params);
+ SignMessage(message, params);
+
+ // Fourth time should fail.
+ EXPECT_EQ(ErrorCode::KEY_MAX_OPS_EXCEEDED, Begin(KeyPurpose::SIGN, params));
+}
+
+typedef KeymasterHidlTest AddEntropyTest;
+
+/*
+ * AddEntropyTest.AddEntropy
+ *
+ * Verifies that the addRngEntropy method doesn't blow up. There's no way to test that entropy is
+ * actually added.
+ */
+TEST_F(AddEntropyTest, AddEntropy) {
+ EXPECT_EQ(ErrorCode::OK, keymaster().addRngEntropy(HidlBuf("foo")));
+}
+
+/*
+ * AddEntropyTest.AddEmptyEntropy
+ *
+ * Verifies that the addRngEntropy method doesn't blow up when given an empty buffer.
+ */
+TEST_F(AddEntropyTest, AddEmptyEntropy) {
+ EXPECT_EQ(ErrorCode::OK, keymaster().addRngEntropy(HidlBuf()));
+}
+
+/*
+ * AddEntropyTest.AddLargeEntropy
+ *
+ * Verifies that the addRngEntropy method doesn't blow up when given a largish amount of data.
+ */
+TEST_F(AddEntropyTest, AddLargeEntropy) {
+ EXPECT_EQ(ErrorCode::OK, keymaster().addRngEntropy(HidlBuf(string(16 * 1024, 'a'))));
+}
+
+typedef KeymasterHidlTest AttestationTest;
+
+/*
+ * AttestationTest.RsaAttestation
+ *
+ * Verifies that attesting to RSA keys works and generates the expected output.
+ */
+TEST_F(AttestationTest, RsaAttestation) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_INCLUDE_UNIQUE_ID)));
+
+ hidl_vec<hidl_vec<uint8_t>> cert_chain;
+ EXPECT_EQ(ErrorCode::OK, AttestKey(AuthorizationSetBuilder().Authorization(
+ TAG_ATTESTATION_CHALLENGE, HidlBuf("challenge")),
+ &cert_chain));
+ EXPECT_GE(cert_chain.size(), 2U);
+ EXPECT_TRUE(verify_chain(cert_chain));
+ EXPECT_TRUE(verify_attestation_record("challenge", //
+ key_characteristics_.softwareEnforced, //
+ key_characteristics_.teeEnforced, //
+ cert_chain[0]));
+}
+
+/*
+ * AttestationTest.EcAttestation
+ *
+ * Verifies that attesting to EC keys works and generates the expected output.
+ */
+TEST_F(AttestationTest, EcAttestation) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(EcCurve::P_256)
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_INCLUDE_UNIQUE_ID)));
+
+ hidl_vec<hidl_vec<uint8_t>> cert_chain;
+ EXPECT_EQ(ErrorCode::OK, AttestKey(AuthorizationSetBuilder().Authorization(
+ TAG_ATTESTATION_CHALLENGE, HidlBuf("challenge")),
+ &cert_chain));
+ EXPECT_GE(cert_chain.size(), 2U);
+ EXPECT_TRUE(verify_chain(cert_chain));
+
+ EXPECT_TRUE(verify_attestation_record("challenge", //
+ key_characteristics_.softwareEnforced, //
+ key_characteristics_.teeEnforced, //
+ cert_chain[0]));
+}
+
+/*
+ * AttestationTest.AesAttestation
+ *
+ * Verifies that attesting to AES keys fails in the expected way.
+ */
+TEST_F(AttestationTest, AesAttestation) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .AesEncryptionKey(128)
+ .EcbMode()
+ .Padding(PaddingMode::PKCS7)));
+
+ hidl_vec<hidl_vec<uint8_t>> cert_chain;
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_ALGORITHM,
+ AttestKey(AuthorizationSetBuilder().Authorization(TAG_ATTESTATION_CHALLENGE,
+ HidlBuf("challenge")),
+ &cert_chain));
+}
+
+/*
+ * AttestationTest.HmacAttestation
+ *
+ * Verifies that attesting to HMAC keys fails in the expected way.
+ */
+TEST_F(AttestationTest, HmacAttestation) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .HmacKey(128)
+ .EcbMode()
+ .Digest(Digest::SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ hidl_vec<hidl_vec<uint8_t>> cert_chain;
+ EXPECT_EQ(ErrorCode::INCOMPATIBLE_ALGORITHM,
+ AttestKey(AuthorizationSetBuilder().Authorization(TAG_ATTESTATION_CHALLENGE,
+ HidlBuf("challenge")),
+ &cert_chain));
+}
+
+} // namespace test
+} // namespace V3_0
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ if (argc == 2) {
+ ALOGI("Running keymaster VTS against service \"%s\"", argv[1]);
+ service_name = argv[1];
+ }
+ int status = RUN_ALL_TESTS();
+ ALOGI("Test result = %d", status);
+ return status;
+}
diff --git a/keymaster/3.0/vts/functional/keymaster_tags.h b/keymaster/3.0/vts/functional/keymaster_tags.h
new file mode 100644
index 0000000..f241ef1
--- /dev/null
+++ b/keymaster/3.0/vts/functional/keymaster_tags.h
@@ -0,0 +1,450 @@
+/*
+ * Copyright 2014 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.
+ */
+
+#ifndef SYSTEM_SECURITY_KEYSTORE_KEYMASTER_TAGS_H_
+#define SYSTEM_SECURITY_KEYSTORE_KEYMASTER_TAGS_H_
+
+/**
+ * This header contains various definitions that make working with keymaster tags safer and easier.
+ *
+ * It makes use of a fair amount of template metaprogramming. The metaprogramming serves the purpose
+ * of making it impossible to make certain classes of mistakes when operating on keymaster
+ * authorizations. For example, it's an error to create a KeyParameter with tag == Tag::PURPOSE
+ * and then to assign Algorithm::RSA to algorithm element of its union. But because the user
+ * must choose the union field, there could be a mismatch which the compiler has now way to
+ * diagnose.
+ *
+ * The machinery in this header solves these problems by describing which union field corresponds
+ * to which Tag. Central to this mechanism is the template TypedTag. It has zero size and binds a
+ * numeric Tag to a type that the compiler understands. By means of the macro DECLARE_TYPED_TAG,
+ * we declare types for each of the tags defined in hardware/interfaces/keymaster/2.0/types.hal.
+ *
+ * The macro DECLARE_TYPED_TAG(name) generates a typename TAG_name_t and a zero sized instance
+ * TAG_name. Once these typed tags have been declared we define metafunctions mapping the each tag
+ * to its value c++ type and the correct union element of KeyParameter. This is done by means of
+ * the macros MAKE_TAG_*VALUE_ACCESSOR, which generates TypedTag2ValueType, a metafunction mapping
+ * a typed tag to the corresponding c++ type, and access function, accessTagValue returning a
+ * reference to the correct element of KeyParameter.
+ * E.g.:
+ * given "KeyParameter param;" then "accessTagValue(TAG_PURPOSE, param)"
+ * yields a reference to param.f.purpose
+ * If used in an assignment the compiler can now check the compatibility of the assigned value.
+ *
+ * For convenience we also provide the constructor like function Authorization().
+ * Authorization takes a typed tag and a value and checks at compile time whether the value given
+ * is suitable for the given tag. At runtime it creates a new KeyParameter initialized with the
+ * given tag and value and returns it by value.
+ *
+ * The second convenience function, authorizationValue, allows access to the KeyParameter value in
+ * a safe way. It takes a typed tag and a KeyParameter and returns a reference to the value wrapped
+ * by NullOr. NullOr has out-of-band information about whether it is save to access the wrapped
+ * reference.
+ * E.g.:
+ * auto param = Authorization(TAG_ALGORITM, Algorithm::RSA);
+ * auto value1 = authorizationValue(TAG_PURPOSE, param);
+ * auto value2 = authorizationValue(TAG_ALGORITM, param);
+ * value1.isOk() yields false, but value2.isOk() yields true, thus value2.value() is save to access.
+ */
+
+#include <android/hardware/keymaster/3.0/IHwKeymasterDevice.h>
+#include <hardware/hw_auth_token.h>
+#include <type_traits>
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V3_0 {
+
+// The following create the numeric values that KM_TAG_PADDING and KM_TAG_DIGEST used to have. We
+// need these old values to be able to support old keys that use them.
+static const int32_t KM_TAG_DIGEST_OLD = static_cast<int32_t>(TagType::ENUM) | 5;
+static const int32_t KM_TAG_PADDING_OLD = static_cast<int32_t>(TagType::ENUM) | 7;
+
+constexpr TagType typeFromTag(Tag tag) {
+ return static_cast<TagType>(static_cast<uint32_t>(tag) & static_cast<uint32_t>(0xf0000000));
+}
+
+/**
+ * TypedTag is a templatized version of Tag, which provides compile-time checking of keymaster tag
+ * types. Instances are convertible to Tag, so they can be used wherever Tag is expected, and
+ * because they encode the tag type it's possible to create function overloads that only operate on
+ * tags with a particular type.
+ */
+template <TagType tag_type, Tag tag> struct TypedTag {
+ inline TypedTag() {
+ // Ensure that it's impossible to create a TypedTag instance whose 'tag' doesn't have type
+ // 'tag_type'. Attempting to instantiate a tag with the wrong type will result in a compile
+ // error (no match for template specialization StaticAssert<false>), with no run-time cost.
+ static_assert(typeFromTag(tag) == tag_type, "mismatch between tag and tag_type");
+ }
+ constexpr operator Tag() { return tag; }
+ constexpr long maskedTag() {
+ return static_cast<long>(static_cast<uint32_t>(tag) & static_cast<uint32_t>(0x0fffffff));
+ }
+};
+
+template <Tag tag> struct Tag2TypedTag { typedef TypedTag<typeFromTag(tag), tag> type; };
+
+template <Tag tag> struct Tag2String;
+
+#define _TAGS_STRINGIFY(x) #x
+#define TAGS_STRINGIFY(x) _TAGS_STRINGIFY(x)
+
+#define DECLARE_TYPED_TAG(name) \
+ typedef typename Tag2TypedTag<Tag::name>::type TAG_##name##_t; \
+ extern TAG_##name##_t TAG_##name; \
+ template <> struct Tag2String<Tag::name> { \
+ static const char* value() { return "Tag::" TAGS_STRINGIFY(name); } \
+ }
+
+DECLARE_TYPED_TAG(INVALID);
+DECLARE_TYPED_TAG(KEY_SIZE);
+DECLARE_TYPED_TAG(MAC_LENGTH);
+DECLARE_TYPED_TAG(CALLER_NONCE);
+DECLARE_TYPED_TAG(MIN_MAC_LENGTH);
+DECLARE_TYPED_TAG(RSA_PUBLIC_EXPONENT);
+DECLARE_TYPED_TAG(ECIES_SINGLE_HASH_MODE);
+DECLARE_TYPED_TAG(INCLUDE_UNIQUE_ID);
+DECLARE_TYPED_TAG(ACTIVE_DATETIME);
+DECLARE_TYPED_TAG(ORIGINATION_EXPIRE_DATETIME);
+DECLARE_TYPED_TAG(USAGE_EXPIRE_DATETIME);
+DECLARE_TYPED_TAG(MIN_SECONDS_BETWEEN_OPS);
+DECLARE_TYPED_TAG(MAX_USES_PER_BOOT);
+DECLARE_TYPED_TAG(ALL_USERS);
+DECLARE_TYPED_TAG(USER_ID);
+DECLARE_TYPED_TAG(USER_SECURE_ID);
+DECLARE_TYPED_TAG(NO_AUTH_REQUIRED);
+DECLARE_TYPED_TAG(AUTH_TIMEOUT);
+DECLARE_TYPED_TAG(ALLOW_WHILE_ON_BODY);
+DECLARE_TYPED_TAG(ALL_APPLICATIONS);
+DECLARE_TYPED_TAG(APPLICATION_ID);
+DECLARE_TYPED_TAG(APPLICATION_DATA);
+DECLARE_TYPED_TAG(CREATION_DATETIME);
+DECLARE_TYPED_TAG(ROLLBACK_RESISTANT);
+DECLARE_TYPED_TAG(ROOT_OF_TRUST);
+DECLARE_TYPED_TAG(ASSOCIATED_DATA);
+DECLARE_TYPED_TAG(NONCE);
+DECLARE_TYPED_TAG(AUTH_TOKEN);
+DECLARE_TYPED_TAG(BOOTLOADER_ONLY);
+DECLARE_TYPED_TAG(OS_VERSION);
+DECLARE_TYPED_TAG(OS_PATCHLEVEL);
+DECLARE_TYPED_TAG(UNIQUE_ID);
+DECLARE_TYPED_TAG(ATTESTATION_CHALLENGE);
+DECLARE_TYPED_TAG(ATTESTATION_APPLICATION_ID);
+DECLARE_TYPED_TAG(RESET_SINCE_ID_ROTATION);
+
+DECLARE_TYPED_TAG(PURPOSE);
+DECLARE_TYPED_TAG(ALGORITHM);
+DECLARE_TYPED_TAG(BLOCK_MODE);
+DECLARE_TYPED_TAG(DIGEST);
+DECLARE_TYPED_TAG(PADDING);
+DECLARE_TYPED_TAG(BLOB_USAGE_REQUIREMENTS);
+DECLARE_TYPED_TAG(ORIGIN);
+DECLARE_TYPED_TAG(USER_AUTH_TYPE);
+DECLARE_TYPED_TAG(KDF);
+DECLARE_TYPED_TAG(EC_CURVE);
+
+template <typename... Elems> struct MetaList {};
+
+using all_tags_t = MetaList<
+ TAG_INVALID_t, TAG_KEY_SIZE_t, TAG_MAC_LENGTH_t, TAG_CALLER_NONCE_t, TAG_MIN_MAC_LENGTH_t,
+ TAG_RSA_PUBLIC_EXPONENT_t, TAG_ECIES_SINGLE_HASH_MODE_t, TAG_INCLUDE_UNIQUE_ID_t,
+ TAG_ACTIVE_DATETIME_t, TAG_ORIGINATION_EXPIRE_DATETIME_t, TAG_USAGE_EXPIRE_DATETIME_t,
+ TAG_MIN_SECONDS_BETWEEN_OPS_t, TAG_MAX_USES_PER_BOOT_t, TAG_ALL_USERS_t, TAG_USER_ID_t,
+ TAG_USER_SECURE_ID_t, TAG_NO_AUTH_REQUIRED_t, TAG_AUTH_TIMEOUT_t, TAG_ALLOW_WHILE_ON_BODY_t,
+ TAG_ALL_APPLICATIONS_t, TAG_APPLICATION_ID_t, TAG_APPLICATION_DATA_t, TAG_CREATION_DATETIME_t,
+ TAG_ROLLBACK_RESISTANT_t, TAG_ROOT_OF_TRUST_t, TAG_ASSOCIATED_DATA_t, TAG_NONCE_t,
+ TAG_AUTH_TOKEN_t, TAG_BOOTLOADER_ONLY_t, TAG_OS_VERSION_t, TAG_OS_PATCHLEVEL_t, TAG_UNIQUE_ID_t,
+ TAG_ATTESTATION_CHALLENGE_t, TAG_ATTESTATION_APPLICATION_ID_t, TAG_RESET_SINCE_ID_ROTATION_t,
+ TAG_PURPOSE_t, TAG_ALGORITHM_t, TAG_BLOCK_MODE_t, TAG_DIGEST_t, TAG_PADDING_t,
+ TAG_BLOB_USAGE_REQUIREMENTS_t, TAG_ORIGIN_t, TAG_USER_AUTH_TYPE_t, TAG_KDF_t, TAG_EC_CURVE_t>;
+
+/* implementation in keystore_utils.cpp */
+extern const char* stringifyTag(Tag tag);
+
+template <typename TypedTagType> struct TypedTag2ValueType;
+
+#define MAKE_TAG_VALUE_ACCESSOR(tag_type, field_name) \
+ template <Tag tag> struct TypedTag2ValueType<TypedTag<tag_type, tag>> { \
+ typedef decltype(static_cast<KeyParameter*>(nullptr)->field_name) type; \
+ }; \
+ template <Tag tag> \
+ inline auto accessTagValue(TypedTag<tag_type, tag>, const KeyParameter& param) \
+ ->const decltype(param.field_name)& { \
+ return param.field_name; \
+ } \
+ template <Tag tag> \
+ inline auto accessTagValue(TypedTag<tag_type, tag>, KeyParameter& param) \
+ ->decltype(param.field_name)& { \
+ return param.field_name; \
+ }
+
+MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG, f.longInteger)
+MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG_REP, f.longInteger)
+MAKE_TAG_VALUE_ACCESSOR(TagType::DATE, f.dateTime)
+MAKE_TAG_VALUE_ACCESSOR(TagType::UINT, f.integer)
+MAKE_TAG_VALUE_ACCESSOR(TagType::UINT_REP, f.integer)
+MAKE_TAG_VALUE_ACCESSOR(TagType::BOOL, f.boolValue)
+MAKE_TAG_VALUE_ACCESSOR(TagType::BYTES, blob)
+MAKE_TAG_VALUE_ACCESSOR(TagType::BIGNUM, blob)
+
+#define MAKE_TAG_ENUM_VALUE_ACCESSOR(typed_tag, field_name) \
+ template <> struct TypedTag2ValueType<decltype(typed_tag)> { \
+ typedef decltype(static_cast<KeyParameter*>(nullptr)->field_name) type; \
+ }; \
+ inline auto accessTagValue(decltype(typed_tag), const KeyParameter& param) \
+ ->const decltype(param.field_name)& { \
+ return param.field_name; \
+ } \
+ inline auto accessTagValue(decltype(typed_tag), KeyParameter& param) \
+ ->decltype(param.field_name)& { \
+ return param.field_name; \
+ }
+
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ALGORITHM, f.algorithm)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_BLOB_USAGE_REQUIREMENTS, f.keyBlobUsageRequirements)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_BLOCK_MODE, f.blockMode)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_DIGEST, f.digest)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_EC_CURVE, f.ecCurve)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_KDF, f.keyDerivationFunction)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ORIGIN, f.origin)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PADDING, f.paddingMode)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PURPOSE, f.purpose)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_USER_AUTH_TYPE, f.hardwareAuthenticatorType)
+
+template <TagType tag_type, Tag tag, typename ValueT>
+inline KeyParameter makeKeyParameter(TypedTag<tag_type, tag> ttag, ValueT&& value) {
+ KeyParameter param;
+ param.tag = tag;
+ param.f.longInteger = 0;
+ accessTagValue(ttag, param) = std::forward<ValueT>(value);
+ return param;
+}
+
+// the boolean case
+template <Tag tag> inline KeyParameter makeKeyParameter(TypedTag<TagType::BOOL, tag>) {
+ KeyParameter param;
+ param.tag = tag;
+ param.f.boolValue = true;
+ return param;
+}
+
+template <typename... Pack> struct FirstOrNoneHelper;
+template <typename First> struct FirstOrNoneHelper<First> { typedef First type; };
+template <> struct FirstOrNoneHelper<> {
+ struct type {};
+};
+
+template <typename... Pack> using FirstOrNone = typename FirstOrNoneHelper<Pack...>::type;
+
+template <TagType tag_type, Tag tag, typename... Args>
+inline KeyParameter Authorization(TypedTag<tag_type, tag> ttag, Args&&... args) {
+ static_assert(tag_type != TagType::BOOL || (sizeof...(args) == 0),
+ "TagType::BOOL Authorizations do not take parameters. Presence is truth.");
+ static_assert(tag_type == TagType::BOOL || (sizeof...(args) == 1),
+ "Authorization other then TagType::BOOL take exactly one parameter.");
+ static_assert(
+ tag_type == TagType::BOOL ||
+ std::is_convertible<std::remove_cv_t<std::remove_reference_t<FirstOrNone<Args...>>>,
+ typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type>::value,
+ "Invalid argument type for given tag.");
+
+ return makeKeyParameter(ttag, std::forward<Args>(args)...);
+}
+
+/**
+ * This class wraps a (mostly return) value and stores whether or not the wrapped value is valid out
+ * of band. Note that if the wrapped value is a reference it is unsafe to access the value if
+ * !isOk(). If the wrapped type is a pointer or value and !isOk(), it is still safe to access the
+ * wrapped value. In this case the pointer will be NULL though, and the value will be default
+ * constructed.
+ */
+template <typename ValueT> class NullOr {
+ template <typename T> struct reference_initializer {
+ static T&& init() { return *static_cast<std::remove_reference_t<T>*>(nullptr); }
+ };
+ template <typename T> struct pointer_initializer {
+ static T init() { return nullptr; }
+ };
+ template <typename T> struct value_initializer {
+ static T init() { return T(); }
+ };
+ template <typename T>
+ using initializer_t =
+ std::conditional_t<std::is_lvalue_reference<T>::value, reference_initializer<T>,
+ std::conditional_t<std::is_pointer<T>::value, pointer_initializer<T>,
+ value_initializer<T>>>;
+
+ public:
+ NullOr() : value_(initializer_t<ValueT>::init()), null_(true) {}
+ NullOr(ValueT&& value) : value_(std::forward<ValueT>(value)), null_(false) {}
+
+ bool isOk() const { return !null_; }
+
+ const ValueT& value() const & { return value_; }
+ ValueT& value() & { return value_; }
+ ValueT&& value() && { return std::move(value_); }
+
+ private:
+ ValueT value_;
+ bool null_;
+};
+
+template <typename T> std::remove_reference_t<T> NullOrOr(NullOr<T>&& v) {
+ if (v.isOk()) return v;
+ return {};
+}
+
+template <typename Head, typename... Tail>
+std::remove_reference_t<Head> NullOrOr(Head&& head, Tail&&... tail) {
+ if (head.isOk()) return head;
+ return NullOrOr(std::forward<Tail>(tail)...);
+}
+
+template <typename Default, typename Wrapped>
+std::remove_reference_t<Wrapped> defaultOr(NullOr<Wrapped>&& optional, Default&& def) {
+ static_assert(std::is_convertible<std::remove_reference_t<Default>,
+ std::remove_reference_t<Wrapped>>::value,
+ "Type of default value must match the type wrapped by NullOr");
+ if (optional.isOk()) return optional.value();
+ return def;
+}
+
+template <TagType tag_type, Tag tag>
+inline NullOr<const typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type&>
+authorizationValue(TypedTag<tag_type, tag> ttag, const KeyParameter& param) {
+ if (tag != param.tag) return {};
+ return accessTagValue(ttag, param);
+}
+
+inline const char* stringify(Digest digest) {
+ switch (digest) {
+ case Digest::NONE:
+ return "None";
+ case Digest::MD5:
+ return "Md5";
+ case Digest::SHA1:
+ return "Sha1";
+ case Digest::SHA_2_224:
+ return "Sha224";
+ case Digest::SHA_2_256:
+ return "Sha256";
+ case Digest::SHA_2_384:
+ return "Sha384";
+ case Digest::SHA_2_512:
+ return "Sha512";
+ }
+ return "UNKNOWN DIGEST!";
+}
+
+inline const char* stringify(Algorithm algorithm) {
+ switch (algorithm) {
+ case Algorithm::RSA:
+ return "Rsa";
+ case Algorithm::EC:
+ return "Ec";
+ case Algorithm::AES:
+ return "Aes";
+ case Algorithm::HMAC:
+ return "Hmac";
+ }
+ return "UNKNOWN ALGORITHM";
+}
+
+inline const char* stringify(BlockMode block_mode) {
+ switch (block_mode) {
+ case BlockMode::ECB:
+ return "Ecb";
+ case BlockMode::CBC:
+ return "Cbc";
+ case BlockMode::CTR:
+ return "Ctr";
+ case BlockMode::GCM:
+ return "Gcm";
+ }
+ return "UNKNOWN BLOCK MODE";
+}
+
+inline const char* stringify(PaddingMode padding) {
+ switch (padding) {
+ case PaddingMode::NONE:
+ return "None";
+ case PaddingMode::RSA_OAEP:
+ return "RsaOaep";
+ case PaddingMode::RSA_PSS:
+ return "RsaPss";
+ case PaddingMode::RSA_PKCS1_1_5_ENCRYPT:
+ return "RsaPkcs115Encrypt";
+ case PaddingMode::RSA_PKCS1_1_5_SIGN:
+ return "RsaPkcs115Sign";
+ case PaddingMode::PKCS7:
+ return "Pkcs7";
+ }
+ return "UNKNOWN PADDING MODE";
+}
+
+inline const char* stringify(KeyOrigin origin) {
+ switch (origin) {
+ case KeyOrigin::GENERATED:
+ return "Generated";
+ case KeyOrigin::DERIVED:
+ return "Derived";
+ case KeyOrigin::IMPORTED:
+ return "Imported";
+ case KeyOrigin::UNKNOWN:
+ return "UNKNOWN (keymaster0 didn't record it)";
+ }
+ return "UNKOWN KEY ORIGIN VALUE";
+}
+
+inline const char* stringify(KeyPurpose purpose) {
+ switch (purpose) {
+ case KeyPurpose::ENCRYPT:
+ return "Encrypt";
+ case KeyPurpose::DECRYPT:
+ return "Decrypt";
+ case KeyPurpose::SIGN:
+ return "Sign";
+ case KeyPurpose::VERIFY:
+ return "Verify";
+ case KeyPurpose::DERIVE_KEY:
+ return "DeriveKey";
+ case KeyPurpose::WRAP_KEY:
+ return "WrapKey";
+ };
+ return "UNKNOWN KEY PURPOSE";
+}
+
+inline const char* stringify(EcCurve curve) {
+ switch (curve) {
+ case EcCurve::P_224:
+ return "P_224";
+ case EcCurve::P_256:
+ return "P_256";
+ case EcCurve::P_384:
+ return "P_384";
+ case EcCurve::P_521:
+ return "P_521";
+ }
+ return "UNKNOWN EC CURVE";
+}
+
+} // namespace V3_0
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
+
+#endif // SYSTEM_SECURITY_KEYSTORE_KEYMASTER_TAGS_H_
diff --git a/keymaster/3.0/vts/functional/keystore_tags_utils.cpp b/keymaster/3.0/vts/functional/keystore_tags_utils.cpp
new file mode 100644
index 0000000..8dd99db
--- /dev/null
+++ b/keymaster/3.0/vts/functional/keystore_tags_utils.cpp
@@ -0,0 +1,50 @@
+/*
+**
+** Copyright 2016, 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.
+*/
+
+#include "keymaster_tags.h"
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V3_0 {
+
+template <typename TagList> struct TagStringifier;
+
+template <typename... Tags> struct TagStringifier<MetaList<Tags...>> {
+ template <TagType tag_type, Tag tag>
+ static TypedTag<tag_type, tag> chooseString(TypedTag<tag_type, tag> ttag, Tag runtime_tag,
+ const char** result) {
+ if (tag == runtime_tag) {
+ *result = Tag2String<tag>::value();
+ }
+ return ttag;
+ }
+ static const char* stringify(Tag tag) {
+ const char* result = "unknown tag";
+ [](Tags&&...) {}(chooseString(Tags(), tag, &result)...);
+ return result;
+ }
+};
+
+const char* stringifyTag(Tag tag) {
+ return TagStringifier<all_tags_t>::stringify(tag);
+}
+
+} // namespace V3_0
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
diff --git a/keymaster/3.0/vts/functional/openssl_utils.h b/keymaster/3.0/vts/functional/openssl_utils.h
new file mode 100644
index 0000000..2eba9ba
--- /dev/null
+++ b/keymaster/3.0/vts/functional/openssl_utils.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 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.
+ */
+
+template <typename T, void (*F)(T*)> struct UniquePtrDeleter {
+ void operator()(T* p) const { F(p); }
+};
+
+typedef UniquePtrDeleter<EVP_PKEY, EVP_PKEY_free> EVP_PKEY_Delete;
+
+#define MAKE_OPENSSL_PTR_TYPE(type) \
+ typedef std::unique_ptr<type, UniquePtrDeleter<type, type##_free>> type##_Ptr;
+
+MAKE_OPENSSL_PTR_TYPE(ASN1_OBJECT)
+MAKE_OPENSSL_PTR_TYPE(EVP_PKEY)
+MAKE_OPENSSL_PTR_TYPE(RSA)
+MAKE_OPENSSL_PTR_TYPE(X509)
+MAKE_OPENSSL_PTR_TYPE(BN_CTX)
+
+typedef std::unique_ptr<BIGNUM, UniquePtrDeleter<BIGNUM, BN_free>> BIGNUM_Ptr;
+
+inline const EVP_MD* openssl_digest(android::hardware::keymaster::V3_0::Digest digest) {
+ switch (digest) {
+ case android::hardware::keymaster::V3_0::Digest::NONE:
+ return nullptr;
+ case android::hardware::keymaster::V3_0::Digest::MD5:
+ return EVP_md5();
+ case android::hardware::keymaster::V3_0::Digest::SHA1:
+ return EVP_sha1();
+ case android::hardware::keymaster::V3_0::Digest::SHA_2_224:
+ return EVP_sha224();
+ case android::hardware::keymaster::V3_0::Digest::SHA_2_256:
+ return EVP_sha256();
+ case android::hardware::keymaster::V3_0::Digest::SHA_2_384:
+ return EVP_sha384();
+ case android::hardware::keymaster::V3_0::Digest::SHA_2_512:
+ return EVP_sha512();
+ }
+ return nullptr;
+}
diff --git a/radio/1.0/vts/functional/Android.bp b/radio/1.0/vts/functional/Android.bp
index 24e3926..7808de1 100644
--- a/radio/1.0/vts/functional/Android.bp
+++ b/radio/1.0/vts/functional/Android.bp
@@ -44,3 +44,27 @@
"-g",
],
}
+
+cc_test {
+ name: "VtsHalSapV1_0TargetTest",
+ defaults: ["hidl_defaults"],
+ srcs: ["sap_callback.cpp",
+ "sap_hidl_hal_api.cpp",
+ "sap_hidl_hal_test.cpp",
+ "VtsHalSapV1_0TargetTest.cpp"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libcutils",
+ "libhidlbase",
+ "libhidltransport",
+ "libnativehelper",
+ "libutils",
+ "android.hardware.radio@1.0",
+ ],
+ static_libs: ["VtsHalHidlTargetTestBase"],
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+}
diff --git a/radio/1.0/vts/functional/VtsHalSapV1_0TargetTest.cpp b/radio/1.0/vts/functional/VtsHalSapV1_0TargetTest.cpp
new file mode 100644
index 0000000..f902588
--- /dev/null
+++ b/radio/1.0/vts/functional/VtsHalSapV1_0TargetTest.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include<sap_hidl_hal_utils.h>
+
+int main(int argc, char** argv) {
+ // Add Sim-access Profile Hidl Environment
+ ::testing::AddGlobalTestEnvironment(new SapHidlEnvironment);
+ ::testing::InitGoogleTest(&argc, argv);
+
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+
+ return status;
+}
diff --git a/radio/1.0/vts/functional/sap_callback.cpp b/radio/1.0/vts/functional/sap_callback.cpp
new file mode 100644
index 0000000..563d066
--- /dev/null
+++ b/radio/1.0/vts/functional/sap_callback.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include<sap_hidl_hal_utils.h>
+
+SapCallback::SapCallback(SapHidlTest& parent) : parent(parent) {
+}
+
+Return<void> SapCallback::connectResponse(int32_t token, SapConnectRsp /*sapConnectRsp*/,
+ int32_t /*maxMsgSize*/) {
+ sapResponseToken = token;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::disconnectResponse(int32_t token) {
+ sapResponseToken = token;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::disconnectIndication(int32_t /*token*/,
+ SapDisconnectType /*disconnectType*/) {
+ return Void();
+}
+
+Return<void> SapCallback::apduResponse(int32_t token, SapResultCode resultCode,
+ const ::android::hardware::hidl_vec<uint8_t>& /*apduRsp*/) {
+ sapResponseToken = token;
+ sapResultCode = resultCode;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::transferAtrResponse(int32_t token, SapResultCode resultCode,
+ const ::android::hardware::hidl_vec<uint8_t>& /*atr*/) {
+ sapResponseToken = token;
+ sapResultCode = resultCode;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::powerResponse(int32_t token, SapResultCode resultCode) {
+ sapResponseToken = token;
+ sapResultCode = resultCode;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::resetSimResponse(int32_t token, SapResultCode resultCode) {
+ sapResponseToken = token;
+ sapResultCode = resultCode;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::statusIndication(int32_t /*token*/, SapStatus /*status*/) {
+ return Void();
+}
+
+Return<void> SapCallback::transferCardReaderStatusResponse(int32_t token,
+ SapResultCode resultCode, int32_t /*cardReaderStatus*/) {
+ sapResponseToken = token;
+ sapResultCode = resultCode;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::errorResponse(int32_t /*token*/) {
+ return Void();
+}
+
+Return<void> SapCallback::transferProtocolResponse(int32_t token,
+ SapResultCode resultCode) {
+ sapResponseToken = token;
+ sapResultCode = resultCode;
+ parent.notify();
+ return Void();
+}
diff --git a/radio/1.0/vts/functional/sap_hidl_hal_api.cpp b/radio/1.0/vts/functional/sap_hidl_hal_api.cpp
new file mode 100644
index 0000000..e806bd7
--- /dev/null
+++ b/radio/1.0/vts/functional/sap_hidl_hal_api.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include<sap_hidl_hal_utils.h>
+
+/*
+ * Test ISap.connectReq() for the response returned.
+ */
+TEST_F(SapHidlTest, connectReq) {
+ int32_t token = 0;
+ int32_t maxMsgSize = 100;
+
+ sap->connectReq(++token, maxMsgSize);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+}
+
+/*
+ * Test IRadio.disconnectReq() for the response returned
+ */
+TEST_F(SapHidlTest, disconnectReq) {
+ int32_t token = 0;
+
+ sap->disconnectReq(++token);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+}
+
+/*
+ * Test IRadio.apduReq() for the response returned.
+ */
+TEST_F(SapHidlTest, apduReq) {
+ int32_t token = 0;
+ SapApduType sapApduType = SapApduType::APDU;
+ android::hardware::hidl_vec<uint8_t> command = {};
+
+ sap->apduReq(++token, sapApduType, command);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+
+ ASSERT_TRUE(SapResultCode::CARD_NOT_ACCESSSIBLE == sapCb->sapResultCode ||
+ SapResultCode::CARD_ALREADY_POWERED_OFF == sapCb->sapResultCode ||
+ SapResultCode::CARD_REMOVED == sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.transferAtrReq() for the response returned.
+ */
+TEST_F(SapHidlTest, transferAtrReq) {
+ int32_t token = 0;
+
+ sap->transferAtrReq(++token);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+
+ ASSERT_TRUE(SapResultCode::DATA_NOT_AVAILABLE == sapCb->sapResultCode ||
+ SapResultCode::CARD_ALREADY_POWERED_OFF == sapCb->sapResultCode ||
+ SapResultCode::CARD_REMOVED == sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.powerReq() for the response returned.
+ */
+TEST_F(SapHidlTest, powerReq) {
+ int32_t token = 0;
+ bool state = true;
+
+ sap->powerReq(++token, state);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+
+ ASSERT_TRUE(SapResultCode::CARD_NOT_ACCESSSIBLE == sapCb->sapResultCode ||
+ SapResultCode::CARD_ALREADY_POWERED_OFF == sapCb->sapResultCode ||
+ SapResultCode::CARD_REMOVED == sapCb->sapResultCode ||
+ SapResultCode::CARD_ALREADY_POWERED_ON == sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.resetSimReq() for the response returned.
+ */
+TEST_F(SapHidlTest, resetSimReq) {
+ int32_t token = 0;
+
+ sap->resetSimReq(++token);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+
+ ASSERT_TRUE(SapResultCode::CARD_NOT_ACCESSSIBLE == sapCb->sapResultCode ||
+ SapResultCode::CARD_ALREADY_POWERED_OFF == sapCb->sapResultCode ||
+ SapResultCode::CARD_REMOVED == sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.transferCardReaderStatusReq() for the response returned.
+ */
+TEST_F(SapHidlTest, transferCardReaderStatusReq) {
+ int32_t token = 0;
+
+ sap->transferCardReaderStatusReq(++token);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+
+ EXPECT_EQ(SapResultCode::DATA_NOT_AVAILABLE, sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.setTransferProtocolReq() for the response returned.
+ */
+TEST_F(SapHidlTest, setTransferProtocolReq) {
+ int32_t token = 0;
+ SapTransferProtocol sapTransferProtocol = SapTransferProtocol::T0;
+
+ sap->setTransferProtocolReq(++token, sapTransferProtocol);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+
+ EXPECT_EQ(SapResultCode::NOT_SUPPORTED, sapCb->sapResultCode);
+}
diff --git a/radio/1.0/vts/functional/sap_hidl_hal_test.cpp b/radio/1.0/vts/functional/sap_hidl_hal_test.cpp
new file mode 100644
index 0000000..a67c5b6
--- /dev/null
+++ b/radio/1.0/vts/functional/sap_hidl_hal_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include<sap_hidl_hal_utils.h>
+
+void SapHidlTest::SetUp() {
+ sap = ::testing::VtsHalHidlTargetTestBase::getService<ISap>(hidl_string("sap_uim_socket1"));
+ ASSERT_NE(sap, nullptr);
+
+ sapCb = new SapCallback(*this);
+ ASSERT_NE(sapCb, nullptr);
+
+ count = 0;
+
+ sap->setCallback(sapCb);
+}
+
+void SapHidlTest::TearDown() {
+}
+
+void SapHidlTest::notify() {
+ std::unique_lock<std::mutex> lock(mtx);
+ count++;
+ cv.notify_one();
+}
+
+std::cv_status SapHidlTest::wait() {
+ std::unique_lock<std::mutex> lock(mtx);
+
+ std::cv_status status = std::cv_status::no_timeout;
+ auto now = std::chrono::system_clock::now();
+ while (count == 0) {
+ status = cv.wait_until(lock, now + std::chrono::seconds(TIMEOUT_PERIOD));
+ if (status == std::cv_status::timeout) {
+ return status;
+ }
+ }
+ count--;
+ return status;
+}
+
diff --git a/radio/1.0/vts/functional/sap_hidl_hal_utils.h b/radio/1.0/vts/functional/sap_hidl_hal_utils.h
new file mode 100644
index 0000000..e3efa50
--- /dev/null
+++ b/radio/1.0/vts/functional/sap_hidl_hal_utils.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <android-base/logging.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+#include <android/hardware/radio/1.0/ISap.h>
+#include <android/hardware/radio/1.0/ISapCallback.h>
+#include <android/hardware/radio/1.0/types.h>
+
+using namespace ::android::hardware::radio::V1_0;
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+#define TIMEOUT_PERIOD 40
+
+class SapHidlTest;
+
+/* Callback class for sap response */
+class SapCallback : public ISapCallback {
+private:
+ SapHidlTest& parent;
+
+public:
+ SapResultCode sapResultCode;
+ int32_t sapResponseToken;
+
+ SapCallback(SapHidlTest& parent);
+
+ virtual ~SapCallback() = default;
+
+ Return<void> connectResponse(int32_t token, SapConnectRsp sapConnectRsp, int32_t maxMsgSize);
+
+ Return<void> disconnectResponse(int32_t token);
+
+ Return<void> disconnectIndication(int32_t token, SapDisconnectType disconnectType);
+
+ Return<void> apduResponse(int32_t token, SapResultCode resultCode,
+ const ::android::hardware::hidl_vec<uint8_t>& apduRsp);
+
+ Return<void> transferAtrResponse(int32_t token, SapResultCode resultCode,
+ const ::android::hardware::hidl_vec<uint8_t>& atr);
+
+ Return<void> powerResponse(int32_t token, SapResultCode resultCode);
+
+ Return<void> resetSimResponse(int32_t token, SapResultCode resultCode);
+
+ Return<void> statusIndication(int32_t token, SapStatus status);
+
+ Return<void> transferCardReaderStatusResponse(int32_t token, SapResultCode resultCode,
+ int32_t cardReaderStatus);
+
+ Return<void> errorResponse(int32_t token);
+
+ Return<void> transferProtocolResponse(int32_t token, SapResultCode resultCode);
+};
+
+// The main test class for Sap HIDL.
+class SapHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+private:
+ std::mutex mtx;
+ std::condition_variable cv;
+ int count;
+
+public:
+ virtual void SetUp() override;
+
+ virtual void TearDown() override;
+
+ /* Used as a mechanism to inform the test about data/event callback */
+ void notify();
+
+ /* Test code calls this function to wait for response */
+ std::cv_status wait();
+
+ /* Sap service */
+ sp<ISap> sap;
+
+ /* Sap Callback object */
+ sp<SapCallback> sapCb;
+};
+
+// A class for test environment setup
+class SapHidlEnvironment : public ::testing::Environment {
+public:
+ virtual void SetUp() {}
+ virtual void TearDown() {}
+};
diff --git a/renderscript/1.0/default/Context.cpp b/renderscript/1.0/default/Context.cpp
index ef17b463..389b6e7 100644
--- a/renderscript/1.0/default/Context.cpp
+++ b/renderscript/1.0/default/Context.cpp
@@ -63,7 +63,7 @@
Return<void> Context::allocationAdapterOffset(Allocation alloc, const hidl_vec<uint32_t>& offsets) {
RsAllocation _alloc = hidl_to_rs<RsAllocation>(alloc);
const hidl_vec<uint32_t>& _offsets = offsets;
- Device::getHal().AllocationAdapterOffset(mContext, _alloc, _offsets.data(), _offsets.size());
+ Device::getHal().AllocationAdapterOffset(mContext, _alloc, _offsets.data(), _offsets.size() * sizeof(uint32_t));
return Void();
}
@@ -552,7 +552,7 @@
std::vector<RsScriptKernelID> _dstK = hidl_to_rs<RsScriptKernelID>(dstK, [](ScriptFieldID val) { return hidl_to_rs<RsScriptKernelID>(val); });
std::vector<RsScriptFieldID> _dstF = hidl_to_rs<RsScriptFieldID>(dstF, [](ScriptFieldID val) { return hidl_to_rs<RsScriptFieldID>(val); });
std::vector<RsType> _types = hidl_to_rs<RsType>(types, [](Type val) { return hidl_to_rs<RsType>(val); });
- RsScriptGroup _scriptGroup = Device::getHal().ScriptGroupCreate(mContext, _kernels.data(), _kernels.size(), _srcK.data(), _srcK.size(), _dstK.data(), _dstK.size(), _dstF.data(), _dstF.size(), _types.data(), _types.size());
+ RsScriptGroup _scriptGroup = Device::getHal().ScriptGroupCreate(mContext, _kernels.data(), _kernels.size() * sizeof(RsScriptKernelID), _srcK.data(), _srcK.size() * sizeof(RsScriptKernelID), _dstK.data(), _dstK.size() * sizeof(RsScriptKernelID), _dstF.data(), _dstF.size() * sizeof(RsScriptFieldID), _types.data(), _types.size() * sizeof(RsType));
return rs_to_hidl<ScriptGroup>(_scriptGroup);
}
@@ -725,7 +725,7 @@
size_t _len = data.size();
RsElement _ve = hidl_to_rs<RsElement>(ve);
const uint32_t* _dimsPtr = dims.data();
- size_t _dimLen = dims.size();
+ size_t _dimLen = dims.size() * sizeof(uint32_t);
Device::getHal().ScriptSetVarVE(mContext, _vs, _slot, _dataPtr, _len, _ve, _dimsPtr, _dimLen);
return Void();
}
diff --git a/renderscript/1.0/vts/functional/VtsCopyTests.cpp b/renderscript/1.0/vts/functional/VtsCopyTests.cpp
index 77217cb..168e681 100644
--- a/renderscript/1.0/vts/functional/VtsCopyTests.cpp
+++ b/renderscript/1.0/vts/functional/VtsCopyTests.cpp
@@ -43,9 +43,7 @@
context->allocation1DWrite(allocation, 0, 0, (Size)dataIn.size(), _data);
context->allocation1DRead(allocation, 0, 0, (uint32_t)dataOut.size(), (Ptr)dataOut.data(),
(Size)dataOut.size()*sizeof(float));
- bool same = std::all_of(dataOut.begin(), dataOut.end(),
- [](float x){ static int val = 0; return x == (float)val++; });
- EXPECT_EQ(true, same);
+ EXPECT_EQ(dataIn, dataOut);
}
/*
@@ -76,9 +74,7 @@
_data, 0);
context->allocation2DRead(allocation, 0, 0, 0, AllocationCubemapFace::POSITIVE_X, 128, 128,
(Ptr)dataOut.data(), (Size)dataOut.size()*sizeof(float), 0);
- bool same = std::all_of(dataOut.begin(), dataOut.end(),
- [](float x){ static int val = 0; return x == (float)val++; });
- EXPECT_EQ(true, same);
+ EXPECT_EQ(dataIn, dataOut);
}
/*
@@ -108,9 +104,7 @@
context->allocation3DWrite(allocation, 0, 0, 0, 0, 32, 32, 32, _data, 0);
context->allocation3DRead(allocation, 0, 0, 0, 0, 32, 32, 32, (Ptr)dataOut.data(),
(Size)dataOut.size()*sizeof(float), 0);
- bool same = std::all_of(dataOut.begin(), dataOut.end(),
- [](float x){ static int val = 0; return x == (float)val++; });
- EXPECT_EQ(true, same);
+ EXPECT_EQ(dataIn, dataOut);
}
/*
@@ -139,18 +133,14 @@
AllocationMipmapControl::NONE,
_data,
(int)AllocationUsageType::SCRIPT);
- EXPECT_NE(allocation, Allocation(0));
+ EXPECT_NE(Allocation(0), allocation);
context->allocationCopyToBitmap(allocation, (Ptr)dataOut1.data(),
(Size)dataOut1.size()*sizeof(float));
- bool same1 = std::all_of(dataOut1.begin(), dataOut1.end(),
- [](float x){ static int val = 0; return x == (float)val++; });
- EXPECT_EQ(true, same1);
+ EXPECT_EQ(dataIn, dataOut1);
context->allocationRead(allocation, (Ptr)dataOut2.data(), (Size)dataOut2.size()*sizeof(float));
- bool same2 = std::all_of(dataOut2.begin(), dataOut2.end(),
- [](float x){ static int val = 0; return x == (float)val++; });
- EXPECT_EQ(true, same2);
+ EXPECT_EQ(dataIn, dataOut2);
}
/*
@@ -368,24 +358,20 @@
/*
* This test creates a complex element type (uint8_t, uint32_t) out of known
* elements. It then verifies the element structure was created correctly.
- * Finally, the test creates a 128-wide, 1-dimension allocation of this type
- * and transfers memory to and from this structure.
+ * Finally, the test creates a 1-wide, 1-dimension allocation of this type
+ * and transfers memory to and from a single cell of this Allocation.
*
* Calls: elementCreate, elementComplexCreate, elementGetSubElements,
* typeCreate, allocationCreateTyped, allocationElementWrite,
* allocationElementRead
- *
- * This test currently has a bug, and should be fixed by 3/17.
- * TODO(butlermichael)
*/
-/*
TEST_F(RenderscriptHidlTest, ComplexElementTest) {
Element element1 = context->elementCreate(DataType::UNSIGNED_8, DataKind::USER, false, 1);
Element element2 = context->elementCreate(DataType::UNSIGNED_32, DataKind::USER, false, 1);
hidl_vec<Element> eins = {element1, element2};
hidl_vec<hidl_string> names = {hidl_string("first"), hidl_string("second")};
- hidl_vec<Size> arraySizesPtr = {sizeof(uint8_t), sizeof(uint32_t)};
+ hidl_vec<Size> arraySizesPtr = {1, 1};
Element element3 = context->elementComplexCreate(eins, names, arraySizesPtr);
EXPECT_NE(Element(0), element3);
@@ -400,28 +386,25 @@
namesOut.push_back(_names[1]);
arraySizesOut = _arraySizes;
});
- EXPECT_NE(Element(0), ids[0]);
- EXPECT_NE(Element(0), ids[1]);
+ EXPECT_EQ(element1, ids[0]);
+ EXPECT_EQ(element2, ids[1]);
EXPECT_EQ("first", namesOut[0]);
EXPECT_EQ("second", namesOut[1]);
- EXPECT_EQ(sizeof(uint8_t), arraySizesOut[0]);
- EXPECT_EQ(sizeof(uint32_t), arraySizesOut[1]);
+ EXPECT_EQ(Size(1), arraySizesOut[0]);
+ EXPECT_EQ(Size(1), arraySizesOut[1]);
- // 128 x (uint8_t, uint32_t)
- Type type = context->typeCreate(element3, 128, 0, 0, false, false, YuvFormat::YUV_NONE);
- // 128 x (uint8_t, uint32_t)
+ // 1 x (uint8_t, uint32_t)
+ Type type = context->typeCreate(element3, 1, 0, 0, false, false, YuvFormat::YUV_NONE);
+ // 1 x (uint8_t, uint32_t)
Allocation allocation = context->allocationCreateTyped(type, AllocationMipmapControl::NONE,
(int)AllocationUsageType::SCRIPT,
(Ptr)nullptr);
- std::vector<uint32_t> dataIn(128), dataOut(128);
+ std::vector<uint32_t> dataIn(1), dataOut(1);
std::generate(dataIn.begin(), dataIn.end(), [](){ static uint32_t val = 0; return val++; });
hidl_vec<uint8_t> _data;
_data.setToExternal((uint8_t*)dataIn.data(), dataIn.size()*sizeof(uint32_t));
context->allocationElementWrite(allocation, 0, 0, 0, 0, _data, 1);
context->allocationElementRead(allocation, 0, 0, 0, 0, (Ptr)dataOut.data(),
(Size)dataOut.size()*sizeof(uint32_t), 1);
- bool same = std::all_of(dataOut.begin(), dataOut.end(),
- [](uint32_t x){ static uint32_t val = 0; return x == val++; });
- EXPECT_EQ(true, same);
+ EXPECT_EQ(dataIn, dataOut);
}
-*/
diff --git a/renderscript/1.0/vts/functional/VtsHalRenderscriptV1_0TargetTest.h b/renderscript/1.0/vts/functional/VtsHalRenderscriptV1_0TargetTest.h
index fc1b7e4..527fef0 100644
--- a/renderscript/1.0/vts/functional/VtsHalRenderscriptV1_0TargetTest.h
+++ b/renderscript/1.0/vts/functional/VtsHalRenderscriptV1_0TargetTest.h
@@ -32,6 +32,7 @@
using ::android::hardware::renderscript::V1_0::AllocationCubemapFace;
using ::android::hardware::renderscript::V1_0::AllocationMipmapControl;
using ::android::hardware::renderscript::V1_0::AllocationUsageType;
+using ::android::hardware::renderscript::V1_0::Closure;
using ::android::hardware::renderscript::V1_0::IContext;
using ::android::hardware::renderscript::V1_0::IDevice;
using ::android::hardware::renderscript::V1_0::ContextType;
@@ -62,7 +63,26 @@
using ::android::hardware::hidl_string;
using ::android::sp;
-// bitcode variables
+// bitcode slots
+extern const int mExportVarIdx_var_int;
+extern const int mExportVarIdx_var_long;
+extern const int mExportVarIdx_var_float;
+extern const int mExportVarIdx_var_double;
+extern const int mExportVarIdx_var_allocation;
+extern const int mExportVarIdx_var_uint32_t;
+extern const int mExportVarIdx_var_point2;
+extern const int mExportVarIdx_var_int_ptr;
+// bitcode invoke slots
+//extern const int mExportForEachIdx_root;
+extern const int mExportForEachIdx_increment;
+// bitcode reduce slots
+extern const int mExportReduceIdx_summation;
+// bitcode invoke slots
+extern const int mExportFuncIdx_function;
+extern const int mExportFuncIdx_functionV;
+extern const int mExportFuncIdx_setBuffer;
+extern const int mExportFuncIdx_setAllocation;
+// bitcode
typedef signed char int8_t;
extern const int8_t bitCode[];
extern const int bitCodeLength;
diff --git a/renderscript/1.0/vts/functional/VtsMiscellaneousTests.cpp b/renderscript/1.0/vts/functional/VtsMiscellaneousTests.cpp
index c2b3354..39d63ca 100644
--- a/renderscript/1.0/vts/functional/VtsMiscellaneousTests.cpp
+++ b/renderscript/1.0/vts/functional/VtsMiscellaneousTests.cpp
@@ -15,6 +15,7 @@
*/
#include "VtsHalRenderscriptV1_0TargetTest.h"
+#include <system/window.h>
/*
* ContextCreateAndDestroy:
@@ -81,7 +82,7 @@
elementMetadata = _metadata; });
EXPECT_EQ(DataType::FLOAT_32, (DataType)elementMetadata[0]);
EXPECT_EQ(DataKind::USER, (DataKind)elementMetadata[1]);
- EXPECT_EQ(false, ((uint32_t)elementMetadata[2] == 1) ? true : false);
+ EXPECT_EQ(false, elementMetadata[2]);
EXPECT_EQ(1u, (uint32_t)elementMetadata[3]);
EXPECT_EQ(0u, (uint32_t)elementMetadata[4]);
@@ -134,21 +135,17 @@
* Calls: elementCreate, typeCreate, allocationCreateTyped, allocation2DWrite,
* allocationGetNativeWindow, allocationSetNativeWindow, allocationIoSend,
* allocationIoReceive, allocation2DRead
- *
- * This test currently has a bug, and should be fixed by 3/17.
- * TODO(butlermichael)
*/
-/*
TEST_F(RenderscriptHidlTest, NativeWindowIoTest) {
// uint8x4
Element element = context->elementCreate(DataType::UNSIGNED_8, DataKind::USER, false, 4);
// 512 x 512 x uint8x4
Type type = context->typeCreate(element, 512, 512, 0, false, false, YuvFormat::YUV_NONE);
std::vector<uint32_t> dataIn(512*512), dataOut(512*512);
- std::generate(dataIn.begin(), dataIn.end(), [](){ static int val = 0; return (uint32_t)val++; });
+ std::generate(dataIn.begin(), dataIn.end(), [](){ static uint32_t val = 0; return val++; });
hidl_vec<uint8_t> _data;
_data.setToExternal((uint8_t*)dataIn.data(), dataIn.size()*sizeof(uint32_t));
- // 512 x 512 x float1
+ // 512 x 512 x uint8x4
Allocation allocationRecv = context->allocationCreateTyped(type, AllocationMipmapControl::NONE,
(int)(AllocationUsageType::SCRIPT
| AllocationUsageType::IO_INPUT),
@@ -157,21 +154,20 @@
(int)(AllocationUsageType::SCRIPT
| AllocationUsageType::IO_OUTPUT),
(Ptr)nullptr);
- context->allocation2DWrite(allocationSend, 0, 0, 0, AllocationCubemapFace::POSITIVE_X, 512, 512,
- _data, 0);
NativeWindow nativeWindow = context->allocationGetNativeWindow(allocationRecv);
EXPECT_NE(NativeWindow(0), nativeWindow);
+ ((ANativeWindow *)nativeWindow)->incStrong(nullptr);
+
context->allocationSetNativeWindow(allocationSend, nativeWindow);
+ context->allocation2DWrite(allocationSend, 0, 0, 0, AllocationCubemapFace::POSITIVE_X, 512, 512,
+ _data, 0);
context->allocationIoSend(allocationSend);
context->allocationIoReceive(allocationRecv);
context->allocation2DRead(allocationRecv, 0, 0, 0, AllocationCubemapFace::POSITIVE_X, 512, 512,
(Ptr)dataOut.data(), (Size)dataOut.size()*sizeof(uint32_t), 0);
- bool same = std::all_of(dataOut.begin(), dataOut.end(),
- [](uint32_t x){ static int val = 0; return x == (uint32_t)val++; });
- EXPECT_EQ(true, same);
+ EXPECT_EQ(dataIn, dataOut);
}
-*/
/*
* Three allocations are created, two with IO_INPUT and one with IO_OUTPUT. The
@@ -180,21 +176,17 @@
* Calls: elementCreate, typeCreate, allocationCreateTyped,
* allocationCreateFromBitmap, allocationSetupBufferQueue,
* allocationShareBufferQueue
- *
- * This test currently has a bug, and should be fixed by 3/17.
- * TODO(butlermichael)
*/
-/*
TEST_F(RenderscriptHidlTest, BufferQueueTest) {
- // float1
- Element element = context->elementCreate(DataType::FLOAT_32, DataKind::USER, false, 1);
- // 512 x 512 x float1
+ // uint8x4
+ Element element = context->elementCreate(DataType::UNSIGNED_8, DataKind::USER, false, 4);
+ // 512 x 512 x uint8x4
Type type = context->typeCreate(element, 512, 512, 0, false, false, YuvFormat::YUV_NONE);
- std::vector<float> dataIn(512*512), dataOut1(512*512), dataOut2(512*512);
- std::generate(dataIn.begin(), dataIn.end(), [](){ static int val = 0; return (float)val++; });
+ std::vector<uint32_t> dataIn(512*512), dataOut1(512*512), dataOut2(512*512);
+ std::generate(dataIn.begin(), dataIn.end(), [](){ static uint32_t val = 0; return val++; });
hidl_vec<uint8_t> _data;
- _data.setToExternal((uint8_t*)dataIn.data(), dataIn.size()*sizeof(float));
- // 512 x 512 x float1
+ _data.setToExternal((uint8_t*)dataIn.data(), dataIn.size()*sizeof(uint32_t));
+ // 512 x 512 x uint8x4
Allocation allocationRecv1 = context->allocationCreateTyped(type, AllocationMipmapControl::NONE,
(int)(AllocationUsageType::SCRIPT
| AllocationUsageType::IO_INPUT),
@@ -203,16 +195,37 @@
(int)(AllocationUsageType::SCRIPT
| AllocationUsageType::IO_INPUT),
(Ptr)nullptr);
- Allocation allocationSend = context->allocationCreateFromBitmap(type,
- AllocationMipmapControl::NONE,
- _data,
- (int)(AllocationUsageType::SCRIPT
- | AllocationUsageType::IO_OUTPUT));
+ Allocation allocationSend = context->allocationCreateTyped(type, AllocationMipmapControl::NONE,
+ (int)(AllocationUsageType::SCRIPT
+ | AllocationUsageType::IO_INPUT),
+ (Ptr)nullptr);
context->allocationSetupBufferQueue(allocationRecv1, 2);
- context->allocationShareBufferQueue(allocationRecv1, allocationRecv2);
- // TODO: test the buffer queue
+ context->allocationShareBufferQueue(allocationRecv2, allocationRecv1);
+
+ NativeWindow nativeWindow1 = context->allocationGetNativeWindow(allocationRecv1);
+ EXPECT_NE(NativeWindow(0), nativeWindow1);
+ NativeWindow nativeWindow2 = context->allocationGetNativeWindow(allocationRecv2);
+ EXPECT_EQ(nativeWindow2, nativeWindow1);
+
+ ((ANativeWindow *)nativeWindow1)->incStrong(nullptr);
+
+ context->allocationSetNativeWindow(allocationSend, nativeWindow1);
+ context->allocation2DWrite(allocationSend, 0, 0, 0, AllocationCubemapFace::POSITIVE_X, 512, 512,
+ _data, 0);
+ context->allocationIoSend(allocationSend);
+ context->allocationIoReceive(allocationRecv1);
+ context->allocation2DRead(allocationRecv1, 0, 0, 0, AllocationCubemapFace::POSITIVE_X, 512, 512,
+ (Ptr)dataOut1.data(), (Size)dataOut1.size()*sizeof(uint32_t), 0);
+ EXPECT_EQ(dataIn, dataOut1);
+
+ context->allocation2DWrite(allocationSend, 0, 0, 0, AllocationCubemapFace::POSITIVE_X, 512, 512,
+ _data, 0);
+ context->allocationIoSend(allocationSend);
+ context->allocationIoReceive(allocationRecv2);
+ context->allocation2DRead(allocationRecv2, 0, 0, 0, AllocationCubemapFace::POSITIVE_X, 512, 512,
+ (Ptr)dataOut2.data(), (Size)dataOut2.size()*sizeof(uint32_t), 0);
+ EXPECT_EQ(dataIn, dataOut2);
}
-*/
/*
* This test sets up the message queue, sends a message, peeks at the message,
@@ -220,33 +233,29 @@
*
* Calls: contextInitToClient, contextSendMessage, contextPeekMessage,
* contextGetMessage, contextDeinitToClient, contextLog
- *
- * This test currently has a bug, and should be fixed by 3/17.
- * TODO(butlermichael)
*/
-/*
TEST_F(RenderscriptHidlTest, ContextMessageTest) {
context->contextInitToClient();
- std::string messageOut = "correct";
+ const char * message = "correct";
+ std::vector<char> messageSend(message, message + sizeof(message));
hidl_vec<uint8_t> _data;
- _data.setToExternal((uint8_t*)const_cast<char*>(messageOut.c_str()), messageOut.length());
+ _data.setToExternal((uint8_t*)messageSend.data(), messageSend.size());
context->contextSendMessage(0, _data);
MessageToClientType messageType;
size_t size;
uint32_t subID;
context->contextPeekMessage([&](MessageToClientType _type, Size _size, uint32_t _subID){
messageType = _type; size = (uint32_t)_size; subID = _subID; });
- std::vector<char> messageIn(size, '\0');
- context->contextGetMessage(messageIn.data(), messageIn.size(),
+ std::vector<char> messageRecv(size, '\0');
+ context->contextGetMessage(messageRecv.data(), messageRecv.size(),
[&](MessageToClientType _type, Size _size){
messageType = _type; size = (uint32_t)_size; });
- EXPECT_EQ(messageOut, messageIn.data());
+ EXPECT_EQ(messageSend, messageRecv);
context->contextDeinitToClient();
context->contextLog();
}
-*/
/*
* Call through a bunch of APIs and make sure they don’t crash. Assign the name
diff --git a/renderscript/1.0/vts/functional/VtsScriptTests.cpp b/renderscript/1.0/vts/functional/VtsScriptTests.cpp
index 9531e19..6bb375a 100644
--- a/renderscript/1.0/vts/functional/VtsScriptTests.cpp
+++ b/renderscript/1.0/vts/functional/VtsScriptTests.cpp
@@ -46,27 +46,30 @@
EXPECT_NE(Script(0), script);
// arg tests
- context->scriptSetVarI(script, 0, 100);
+ context->scriptSetVarI(script, mExportVarIdx_var_int, 100);
int resultI = 0;
- context->scriptGetVarV(script, 0, sizeof(int), [&](const hidl_vec<uint8_t>& _data){
- resultI = *((int*)_data.data()); });
+ context->scriptGetVarV(script, mExportVarIdx_var_int, sizeof(int),
+ [&](const hidl_vec<uint8_t>& _data){ resultI = *((int*)_data.data()); });
EXPECT_EQ(100, resultI);
- context->scriptSetVarJ(script, 1, 101l);
+ context->scriptSetVarJ(script, mExportVarIdx_var_long, 101l);
int resultJ = 0;
- context->scriptGetVarV(script, 1, sizeof(long), [&](const hidl_vec<uint8_t>& _data){
+ context->scriptGetVarV(script, mExportVarIdx_var_long, sizeof(long),
+ [&](const hidl_vec<uint8_t>& _data){
resultJ = *((long*)_data.data()); });
- EXPECT_EQ(101, resultJ);
+ EXPECT_EQ(101l, resultJ);
- context->scriptSetVarF(script, 2, 102.0f);
+ context->scriptSetVarF(script, mExportVarIdx_var_float, 102.0f);
int resultF = 0.0f;
- context->scriptGetVarV(script, 2, sizeof(float), [&](const hidl_vec<uint8_t>& _data){
+ context->scriptGetVarV(script, mExportVarIdx_var_float, sizeof(float),
+ [&](const hidl_vec<uint8_t>& _data){
resultF = *((float*)_data.data()); });
EXPECT_EQ(102.0f, resultF);
- context->scriptSetVarD(script, 3, 103.0);
+ context->scriptSetVarD(script, mExportVarIdx_var_double, 103.0);
int resultD = 0.0;
- context->scriptGetVarV(script, 3, sizeof(double), [&](const hidl_vec<uint8_t>& _data){
+ context->scriptGetVarV(script, mExportVarIdx_var_double, sizeof(double),
+ [&](const hidl_vec<uint8_t>& _data){
resultD = *((double*)_data.data()); });
EXPECT_EQ(103.0, resultD);
@@ -79,23 +82,21 @@
(int)AllocationUsageType::SCRIPT,
(Ptr)nullptr);
Allocation allocationOut = Allocation(0);
- context->scriptSetVarObj(script, 4, (ObjectBase)allocationIn);
- context->scriptGetVarV(script, 4, sizeof(ObjectBase), [&](const hidl_vec<uint8_t>& _data){
+ context->scriptSetVarObj(script, mExportVarIdx_var_allocation, (ObjectBase)allocationIn);
+ context->scriptGetVarV(script, mExportVarIdx_var_allocation, sizeof(ObjectBase),
+ [&](const hidl_vec<uint8_t>& _data){
allocationOut = (Allocation) *((ObjectBase*)_data.data()); });
EXPECT_EQ(allocationOut, allocationIn);
- std::vector<int> arrayIn = {500, 501, 502, 503};
- std::vector<int> arrayOut(4);
- hidl_vec<uint8_t> arrayData;
- arrayData.setToExternal((uint8_t*)arrayIn.data(), arrayIn.size()*sizeof(int));
- context->scriptSetVarV(script, 5, arrayData);
- context->scriptGetVarV(script, 5, 4*sizeof(int), [&](const hidl_vec<uint8_t>& _data){
- arrayOut = std::vector<int>((int*)_data.data(),
- (int*)_data.data() + 4); });
- EXPECT_EQ(500, arrayOut[0]);
- EXPECT_EQ(501, arrayOut[1]);
- EXPECT_EQ(502, arrayOut[2]);
- EXPECT_EQ(503, arrayOut[3]);
+ uint32_t valueV = 104u;
+ hidl_vec<uint8_t> _dataV;
+ _dataV.setToExternal((uint8_t*)&valueV, sizeof(uint32_t));
+ context->scriptSetVarV(script, mExportVarIdx_var_uint32_t, _dataV);
+ uint32_t resultV = 0u;
+ context->scriptGetVarV(script, mExportVarIdx_var_uint32_t, sizeof(uint32_t),
+ [&](const hidl_vec<uint8_t>& _data){
+ resultV = *((uint32_t*)_data.data()); });
+ EXPECT_EQ(104u, resultV);
std::vector<int> dataVE = {1000, 1001};
std::vector<uint32_t> dimsVE = {1};
@@ -104,12 +105,13 @@
hidl_vec<uint32_t> _dimsVE;
_dataVE.setToExternal((uint8_t*)dataVE.data(), dataVE.size()*sizeof(int));
_dimsVE.setToExternal((uint32_t*)dimsVE.data(), dimsVE.size());
- // intx2
+ // intx2 to represent point2 which is {int, int}
Element elementVE = context->elementCreate(DataType::SIGNED_32, DataKind::USER, false, 2);
- context->scriptSetVarVE(script, 6, _dataVE, elementVE, _dimsVE);
- context->scriptGetVarV(script, 6, 2*sizeof(int), [&](const hidl_vec<uint8_t>& _data){
- outVE = std::vector<int>((int*)_data.data(),
- (int*)_data.data() + 2); });
+ context->scriptSetVarVE(script, mExportVarIdx_var_point2, _dataVE, elementVE, _dimsVE);
+ context->scriptGetVarV(script, mExportVarIdx_var_point2, 2*sizeof(int),
+ [&](const hidl_vec<uint8_t>& _data){
+ outVE = std::vector<int>(
+ (int*)_data.data(), (int*)_data.data() + 2); });
EXPECT_EQ(1000, outVE[0]);
EXPECT_EQ(1001, outVE[1]);
}
@@ -127,19 +129,47 @@
EXPECT_NE(Script(0), script);
// invoke test
- int function_res = 0;
- context->scriptInvoke(script, 0);
- context->scriptGetVarV(script, 0, sizeof(int), [&](const hidl_vec<uint8_t>& _data){
- function_res = *((int*)_data.data()); });
- EXPECT_NE(100, function_res);
+ int resultI = 0;
+ long resultJ = 0l;
+ float resultF = 0.0f;
+ double resultD = 0.0;
+ uint32_t resultV = 0u;
+ std::vector<int> resultVE(2);
+ context->scriptInvoke(script, mExportFuncIdx_function);
+ context->scriptGetVarV(script, mExportVarIdx_var_int, sizeof(int),
+ [&](const hidl_vec<uint8_t>& _data){ resultI = *((int*)_data.data()); });
+ context->scriptGetVarV(script, mExportVarIdx_var_long, sizeof(long),
+ [&](const hidl_vec<uint8_t>& _data){
+ resultJ = *((long*)_data.data()); });
+ context->scriptGetVarV(script, mExportVarIdx_var_float, sizeof(float),
+ [&](const hidl_vec<uint8_t>& _data){
+ resultF = *((float*)_data.data()); });
+ context->scriptGetVarV(script, mExportVarIdx_var_double, sizeof(double),
+ [&](const hidl_vec<uint8_t>& _data){
+ resultD = *((double*)_data.data()); });
+ context->scriptGetVarV(script, mExportVarIdx_var_uint32_t, sizeof(uint32_t),
+ [&](const hidl_vec<uint8_t>& _data){
+ resultV = *((uint32_t*)_data.data()); });
+ context->scriptGetVarV(script, mExportVarIdx_var_point2, 2*sizeof(int),
+ [&](const hidl_vec<uint8_t>& _data){
+ resultVE = std::vector<int>(
+ (int*)_data.data(), (int*)_data.data() + 2); });
+ EXPECT_EQ(1, resultI);
+ EXPECT_EQ(2l, resultJ);
+ EXPECT_EQ(3.0f, resultF);
+ EXPECT_EQ(4.0, resultD);
+ EXPECT_EQ(5u, resultV);
+ EXPECT_EQ(6, resultVE[0]);
+ EXPECT_EQ(7, resultVE[1]);
// invokeV test
int functionV_arg = 5;
int functionV_res = 0;
hidl_vec<uint8_t> functionV_data;
functionV_data.setToExternal((uint8_t*)&functionV_arg, sizeof(int));
- context->scriptInvokeV(script, 1, functionV_data);
- context->scriptGetVarV(script, 0, sizeof(int), [&](const hidl_vec<uint8_t>& _data){
+ context->scriptInvokeV(script, mExportFuncIdx_functionV, functionV_data);
+ context->scriptGetVarV(script, mExportVarIdx_var_int, sizeof(int),
+ [&](const hidl_vec<uint8_t>& _data){
functionV_res = *((int*)_data.data()); });
EXPECT_EQ(5, functionV_res);
}
@@ -161,8 +191,9 @@
Element element = context->elementCreate(DataType::UNSIGNED_8, DataKind::USER, false, 1);
// 64 x uint8_t
Type type = context->typeCreate(element, 64, 0, 0, false, false, YuvFormat::YUV_NONE);
- std::vector<uint8_t> dataIn(64), dataOut(64);
+ std::vector<uint8_t> dataIn(64), dataOut(64), expected(64);
std::generate(dataIn.begin(), dataIn.end(), [](){ static uint8_t val = 0; return val++; });
+ std::generate(expected.begin(), expected.end(), [](){ static uint8_t val = 1; return val++; });
hidl_vec<uint8_t> _data;
_data.setToExternal((uint8_t*)dataIn.data(), dataIn.size());
// 64 x float1
@@ -176,11 +207,9 @@
hidl_vec<Allocation> vains;
vains.setToExternal(&allocation, 1);
hidl_vec<uint8_t> params;
- context->scriptForEach(script, 1, vains, vout, params, nullptr);
+ context->scriptForEach(script, mExportForEachIdx_increment, vains, vout, params, nullptr);
context->allocationRead(vout, (Ptr)dataOut.data(), (Size)dataOut.size()*sizeof(uint8_t));
- bool same = std::all_of(dataOut.begin(), dataOut.end(),
- [](uint8_t x){ static uint8_t val = 1; return x == val++; });
- EXPECT_EQ(true, same);
+ EXPECT_EQ(expected, dataOut);
}
/*
@@ -215,7 +244,7 @@
context->allocation1DWrite(allocation, 0, 0, (Size)dataIn.size(), _data);
hidl_vec<Allocation> vains;
vains.setToExternal(&allocation, 1);
- context->scriptReduce(script, 0, vains, vaout, nullptr);
+ context->scriptReduce(script, mExportReduceIdx_summation, vains, vaout, nullptr);
context->contextFinish();
context->allocationRead(vaout, (Ptr)dataOut.data(), (Size)dataOut.size()*sizeof(int));
// sum of 0, 1, 2, ..., 62, 63
@@ -228,38 +257,34 @@
* RenderScript script, represented in the bitcode.
*
* Calls: scriptCCreate, elementCreate, typeCreate, allocationCreateTyped,
- * allocationGetPointer, scriptBindAllocation
- *
- * This test currently has a bug, and should be fixed by 3/17.
- * TODO(butlermichael)
+ * scriptSetVarV, scriptBindAllocation, allocationRead
*/
-/*
TEST_F(RenderscriptHidlTest, ScriptBindTest) {
hidl_vec<uint8_t> bitcode;
bitcode.setToExternal((uint8_t*)bitCode, bitCodeLength);
Script script = context->scriptCCreate("struct_test", "/data/local/tmp/", bitcode);
EXPECT_NE(Script(0), script);
- // uint8_t
+ // in32
Element element = context->elementCreate(DataType::SIGNED_32, DataKind::USER, false, 1);
- // 64 x uint8_t
+ // 64 x int32
Type type = context->typeCreate(element, 64, 0, 0, false, false, YuvFormat::YUV_NONE);
- // 64 x float1
+ // 64 x int32
Allocation allocation = context->allocationCreateTyped(type, AllocationMipmapControl::NONE,
(int)AllocationUsageType::SCRIPT,
(Ptr)nullptr);
- Ptr dataPtr1, dataPtr2;
- Size stride;
- context->allocationGetPointer(allocation, 0, AllocationCubemapFace::POSITIVE_X, 0,
- [&](Ptr _dataPtr, Size _stride){ dataPtr1 = _dataPtr;
- stride = _stride; });
- context->scriptBindAllocation(script, allocation, 7);
- context->allocationGetPointer(allocation, 0, AllocationCubemapFace::POSITIVE_X, 0,
- [&](Ptr _dataPtr, Size _stride){ dataPtr2 = _dataPtr;
- stride = _stride; });
- EXPECT_NE(dataPtr1, dataPtr2);
+ std::vector<int> dataIn(64), dataOut(64), expected(64, 5);
+ hidl_vec<uint8_t> _data;
+ _data.setToExternal((uint8_t*)dataIn.data(), dataIn.size()*sizeof(int));
+ context->allocation1DWrite(allocation, 0, 0, (Size)dataIn.size(), _data);
+ context->scriptBindAllocation(script, allocation, mExportVarIdx_var_int_ptr);
+ int dim = 64;
+ hidl_vec<uint8_t> _dim;
+ _dim.setToExternal((uint8_t*)&dim, sizeof(int));
+ context->scriptInvokeV(script, mExportFuncIdx_setBuffer, _dim);
+ context->allocationRead(allocation, (Ptr)dataOut.data(), (Size)dataOut.size()*sizeof(int));
+ EXPECT_EQ(expected, dataOut);
}
-*/
/*
* This test groups together two RenderScript intrinsic kernels to run one after
@@ -269,38 +294,25 @@
* ScriptGroup.
*
* Calls: elementCreate, typeCreate, allocationCreateTyped, allocation2DWrite,
- * scriptIntrinsicCreate, scriptKernelIDCreate, scriptGroupCreate,
- * scriptGroupSetInput, scriptGroupSetOutput, scriptGroupExecute,
- * allocation2DRead
- *
- * This test currently has a bug, and should be fixed by 3/17.
- * TODO(butlermichael)
+ * scriptIntrinsicCreate, scriptKernelIDCreate, scriptFieldIDCreate,
+ * scriptGroupCreate, scriptGroupSetOutput, scriptGroupExecute, allocation2DRead
*/
-/*
TEST_F(RenderscriptHidlTest, ScriptGroupTest) {
- //std::vector<uint8_t> dataIn(256*256*1, 128), dataOut(256*256*3, 0);
- std::vector<uint8_t> dataIn(256*256*1, 128), dataOut(256*256*4, 0);
+ std::vector<uint8_t> dataIn(256*256*1, 128), dataOut(256*256*4, 0), zeros(256*256*4, 0);
hidl_vec<uint8_t> _dataIn, _dataOut;
_dataIn.setToExternal(dataIn.data(), dataIn.size());
- _dataOut.setToExternal(dataOut.data(), dataIn.size());
+ _dataOut.setToExternal(dataOut.data(), dataOut.size());
// 256 x 256 YUV pixels
Element element1 = context->elementCreate(DataType::UNSIGNED_8, DataKind::PIXEL_YUV, true, 1);
- //Type type1 = context->typeCreate(element1, 256, 256, 0, false, false, YuvFormat::YUV_420_888);
- Type type1 = context->typeCreate(element1, 256, 256, 0, false, false, YuvFormat::YUV_NV21);
+ Type type1 = context->typeCreate(element1, 256, 256, 0, false, false, YuvFormat::YUV_420_888);
Allocation allocation1 = context->allocationCreateTyped(type1, AllocationMipmapControl::NONE,
(int)AllocationUsageType::SCRIPT,
(Ptr)nullptr);
context->allocation2DWrite(allocation1, 0, 0, 0, AllocationCubemapFace::POSITIVE_X, 256, 256,
_dataIn, 0);
- Script yuv2rgb = context->scriptIntrinsicCreate(ScriptIntrinsicID::ID_YUV_TO_RGB, element1);
- EXPECT_NE(Script(0), yuv2rgb);
- ScriptKernelID yuv2rgbKID = context->scriptKernelIDCreate(yuv2rgb, 0, 2);
- EXPECT_NE(ScriptKernelID(0), yuv2rgbKID);
-
- // 256 x 256 RGB pixels
- //Element element2 = context->elementCreate(DataType::UNSIGNED_8, DataKind::PIXEL_RGB, true, 3);
+ // 256 x 256 RGBA pixels
Element element2 = context->elementCreate(DataType::UNSIGNED_8, DataKind::PIXEL_RGBA, true, 4);
Type type2 = context->typeCreate(element2, 256, 256, 0, false, false, YuvFormat::YUV_NONE);
Allocation allocation2 = context->allocationCreateTyped(type2, AllocationMipmapControl::NONE,
@@ -308,83 +320,170 @@
(Ptr)nullptr);
context->allocation2DWrite(allocation2, 0, 0, 0, AllocationCubemapFace::POSITIVE_X, 256, 256,
_dataOut, 0);
+
+ // create scripts
+ Script yuv2rgb = context->scriptIntrinsicCreate(ScriptIntrinsicID::ID_YUV_TO_RGB, element1);
+ EXPECT_NE(Script(0), yuv2rgb);
+
+ ScriptKernelID yuv2rgbKID = context->scriptKernelIDCreate(yuv2rgb, 0, 2);
+ EXPECT_NE(ScriptKernelID(0), yuv2rgbKID);
+
Script blur = context->scriptIntrinsicCreate(ScriptIntrinsicID::ID_BLUR, element2);
EXPECT_NE(Script(0), blur);
ScriptKernelID blurKID = context->scriptKernelIDCreate(blur, 0, 2);
EXPECT_NE(ScriptKernelID(0), blurKID);
+ ScriptFieldID blurFID = context->scriptFieldIDCreate(blur, 1);
+ EXPECT_NE(ScriptFieldID(0), blurFID);
// ScriptGroup
hidl_vec<ScriptKernelID> kernels = {yuv2rgbKID, blurKID};
hidl_vec<ScriptKernelID> srcK = {yuv2rgbKID};
- hidl_vec<ScriptKernelID> dstK = {blurKID};
- hidl_vec<ScriptFieldID> dstF = {};
+ hidl_vec<ScriptKernelID> dstK = {ScriptKernelID(0)};
+ hidl_vec<ScriptFieldID> dstF = {blurFID};
hidl_vec<Type> types = {type2};
ScriptGroup scriptGroup = context->scriptGroupCreate(kernels, srcK, dstK, dstF, types);
EXPECT_NE(ScriptGroup(0), scriptGroup);
- context->scriptGroupSetInput(scriptGroup, yuv2rgbKID, allocation1);
+ context->scriptSetVarObj(yuv2rgb, 0, (ObjectBase)allocation1);
context->scriptGroupSetOutput(scriptGroup, blurKID, allocation2);
context->scriptGroupExecute(scriptGroup);
+ context->contextFinish();
// verify contents were changed
context->allocation2DRead(allocation2, 0, 0, 0, AllocationCubemapFace::POSITIVE_X, 256, 256,
(Ptr)dataOut.data(), (Size)dataOut.size(), 0);
- bool same = std::all_of(dataOut.begin(), dataOut.end(), [](uint8_t x){ return x != 0; });
- EXPECT_EQ(true, same);
+ EXPECT_NE(zeros, dataOut);
}
-*/
/*
* Similar to the ScriptGroup test, this test verifies the execution flow of
* RenderScript kernels and invokables.
*
* Calls: scriptFieldIDCreate, closureCreate, scriptInvokeIDCreate,
- * invokeClosureCreate, closureSetArg, closureSetGlobal, scriptGroup2Create,
- * scriptGroupExecute
- *
- * This test currently still a work in progress, and should be finished by 3/17.
- * TODO(butlermichael)
+ * invokeClosureCreate, closureSetGlobal, scriptGroup2Create, scriptGroupExecute
*/
-/*
TEST_F(RenderscriptHidlTest, ScriptGroup2Test) {
+ hidl_vec<uint8_t> bitcode;
+ bitcode.setToExternal((uint8_t*)bitCode, bitCodeLength);
+ Script script = context->scriptCCreate("struct_test", "/data/local/tmp/", bitcode);
+ EXPECT_NE(Script(0), script);
- ScriptFieldID fieldID = context->scriptFieldIDCreate(script, slot);
+ std::vector<uint8_t> dataIn(128, 128), dataOut(128, 0), expected(128, 7+1);
+ hidl_vec<uint8_t> _dataIn, _dataOut;
+ _dataIn.setToExternal(dataIn.data(), dataIn.size());
+
+ // 256 x 256 YUV pixels
+ Element element = context->elementCreate(DataType::UNSIGNED_8, DataKind::USER, false, 1);
+ Type type = context->typeCreate(element, 128, 0, 0, false, false, YuvFormat::YUV_NONE);
+ Allocation allocation = context->allocationCreateTyped(type, AllocationMipmapControl::NONE,
+ (int)AllocationUsageType::SCRIPT,
+ (Ptr)nullptr);
+ context->allocation1DWrite(allocation, 0, 0, (Size)_dataIn.size(), _dataIn);
+
+ ScriptFieldID fieldID = context->scriptFieldIDCreate(script, mExportVarIdx_var_allocation);
EXPECT_NE(ScriptFieldID(0), fieldID);
+ ASSERT_NE(ScriptFieldID(0), fieldID);
- ScriptKernelID kernelID = context->scriptKernelIDCreate(script, slot, sig);
- EXPECT_NE(ScriptKernelID(0), kernelID);
-
- Allocation returnValue = 0;
- hidl_vec<ScriptFieldID> fieldIDS = {};
- hidl_vec<int64_t> values = {};
- hidl_vec<int32_t> sizes = {};
- hidl_veC<Closure> depClosures = {};
- hidl_vec<ScriptFieldID> depFieldIDS = {};
- Closure closure1 = context->closureCreate(kernelID, returnValue, fieldIDS, values, sizes,
- depClosures, depFieldIDS);
- EXPECT_NE(Closure(0), closure1);
-
- ScriptInvokeID invokeID = context->scriptInvokeIDCreate(script, slot);
+ // invoke
+ ScriptInvokeID invokeID = context->scriptInvokeIDCreate(script, mExportFuncIdx_setAllocation);
EXPECT_NE(ScriptInvokeID(0), invokeID);
+ ASSERT_NE(ScriptInvokeID(0), invokeID);
- hidl_vec<uint8_t> params = {};
- hidl_vec<ScriptFieldID> fieldsIDS2 = {};
- hidl_vec<int64_t> values2 = {};
- hidl_vec<int32_t> sizes2 = {};
- Closure closure2 = context->invokeClosureCreate(invokeID, params, fieldIDS2, values2, sizes2);
+ int dim = 128;
+ hidl_vec<uint8_t> params;
+ params.setToExternal((uint8_t*)&dim, sizeof(dim));
+ hidl_vec<ScriptFieldID> fieldIDS1 = {fieldID};
+ hidl_vec<int64_t> values1 = {int64_t(0)};
+ hidl_vec<int32_t> sizes1 = {int32_t(0)};
+ Closure closure1 = context->invokeClosureCreate(invokeID, params, fieldIDS1, values1, sizes1);
+ EXPECT_NE(Closure(0), closure1);
+ ASSERT_NE(Closure(0), closure1);
+
+ // kernel
+ ScriptKernelID kernelID = context->scriptKernelIDCreate(script, mExportForEachIdx_increment, 3);
+ EXPECT_NE(ScriptKernelID(0), kernelID);
+ ASSERT_NE(ScriptKernelID(0), kernelID);
+
+ hidl_vec<ScriptFieldID> fieldIDS2 = {ScriptFieldID(0)};
+ hidl_vec<int64_t> values2 = {(int64_t)(intptr_t)allocation};
+ hidl_vec<int32_t> sizes2 = {-1 /* allocation */};
+ hidl_vec<Closure> depClosures2 = {closure1};
+ hidl_vec<ScriptFieldID> depFieldIDS2 = {fieldID};
+ Closure closure2 = context->closureCreate(kernelID, allocation /* returnValue */, fieldIDS2,
+ values2, sizes2, depClosures2, depFieldIDS2);
EXPECT_NE(Closure(0), closure2);
+ ASSERT_NE(Closure(0), closure2);
- context->closureSetArg(closure, index, value, size);
- context->closureSetGlobal(closure, fieldID, value, size);
+ // set argument
+ context->closureSetGlobal(closure1, fieldID, (int64_t)(intptr_t)allocation,
+ -1 /* allocation */);
+ // execute
hidl_string name = "script_group_2_test";
- hidl_string cacheDir = "data/local/tmp/";
- hidl_vec<Closures> closures;
+ hidl_string cacheDir = "/data/local/tmp";
+ hidl_vec<Closure> closures = {closure1, closure2};
ScriptGroup2 scriptGroup2 = context->scriptGroup2Create(name, cacheDir, closures);
EXPECT_NE(ScriptGroup2(0), scriptGroup2);
+ ASSERT_NE(ScriptGroup2(0), scriptGroup2);
context->scriptGroupExecute(scriptGroup2);
- // verify script group launched...
+ context->allocationRead(allocation, (Ptr)dataOut.data(), (Size)dataOut.size()*sizeof(uint8_t));
+ EXPECT_EQ(expected, dataOut);
}
-*/
+
+/*
+ * Similar to the ScriptGroup test, this test verifies a single kernel can be
+ * called by ScriptGroup with an unbound allocation specified before launch
+ *
+ * Calls: scriptFieldIDCreate, closureCreate, scriptInvokeIDCreate,
+ * invokeClosureCreate, closureSetArg, scriptGroup2Create, scriptGroupExecute
+ */
+TEST_F(RenderscriptHidlTest, ScriptGroup2KernelTest) {
+ hidl_vec<uint8_t> bitcode;
+ bitcode.setToExternal((uint8_t*)bitCode, bitCodeLength);
+ Script script = context->scriptCCreate("struct_test", "/data/local/tmp/", bitcode);
+ EXPECT_NE(Script(0), script);
+
+ std::vector<uint8_t> dataIn(128, 128), dataOut(128, 0), expected(128, 128 + 1);
+ hidl_vec<uint8_t> _dataIn, _dataOut;
+ _dataIn.setToExternal(dataIn.data(), dataIn.size());
+
+ // 256 x 256 YUV pixels
+ Element element = context->elementCreate(DataType::UNSIGNED_8, DataKind::USER, false, 1);
+ Type type = context->typeCreate(element, 128, 0, 0, false, false, YuvFormat::YUV_NONE);
+ Allocation allocation = context->allocationCreateTyped(type, AllocationMipmapControl::NONE,
+ (int)AllocationUsageType::SCRIPT,
+ (Ptr)nullptr);
+ context->allocation1DWrite(allocation, 0, 0, (Size)_dataIn.size(), _dataIn);
+
+ // kernel
+ ScriptKernelID kernelID = context->scriptKernelIDCreate(script, mExportForEachIdx_increment, 3);
+ EXPECT_NE(ScriptKernelID(0), kernelID);
+ ASSERT_NE(ScriptKernelID(0), kernelID);
+
+ hidl_vec<ScriptFieldID> fieldIDS = {ScriptFieldID(0)};
+ hidl_vec<int64_t> values = {int64_t(0)};
+ hidl_vec<int32_t> sizes = {int32_t(0)};
+ hidl_vec<Closure> depClosures = {Closure(0)};
+ hidl_vec<ScriptFieldID> depFieldIDS = {ScriptFieldID(0)};
+ Closure closure = context->closureCreate(kernelID, allocation /* returnValue */, fieldIDS,
+ values, sizes, depClosures, depFieldIDS);
+ EXPECT_NE(Closure(0), closure);
+ ASSERT_NE(Closure(0), closure);
+
+ // set argument
+ context->closureSetArg(closure, 0 /* first argument */, (Ptr)allocation, -1);
+
+ // execute
+ hidl_string name = "script_group_2_test";
+ hidl_string cacheDir = "/data/local/tmp";
+ hidl_vec<Closure> closures = {closure};
+ ScriptGroup2 scriptGroup2 = context->scriptGroup2Create(name, cacheDir, closures);
+ EXPECT_NE(ScriptGroup2(0), scriptGroup2);
+ ASSERT_NE(ScriptGroup2(0), scriptGroup2);
+
+ context->scriptGroupExecute(scriptGroup2);
+ context->allocationRead(allocation, (Ptr)dataOut.data(), (Size)dataOut.size()*sizeof(uint8_t));
+ EXPECT_EQ(expected, dataOut);
+}
diff --git a/renderscript/1.0/vts/functional/bitcode.cpp b/renderscript/1.0/vts/functional/bitcode.cpp
index 72143c9..8a7d542 100644
--- a/renderscript/1.0/vts/functional/bitcode.cpp
+++ b/renderscript/1.0/vts/functional/bitcode.cpp
@@ -15,169 +15,215 @@
*/
/*
-#include "shared.rsh"
-
-// types
-typedef struct Point2 {
- int x;
- int y;
-} Point_2;
-
-// variables
-int var_int;
-long var_long;
-float var_float;
-double var_double;
-rs_allocation var_allocation;
-int var_array[4];
-Point_2 var_point2;
-Point_2 *var_point2_ptr;
-
-// invoke
-void function() {
- var_int = 1;
- var_long = 2;
- var_float = 3.0f;
- var_double = 4.0;
-
- var_array[0] = 5;
- var_array[1] = 6;
- var_array[2] = 7;
- var_array[3] = 8;
-
- var_point2.x = 9;
- var_point2.y = 10;
-}
-
-// invokeV
-void functionV(int arg) {
- var_int = arg;
-}
-
-// forEach
-uchar RS_KERNEL increment(uchar in) {
- return in+1;
-}
-
-// reduction
-#pragma rs reduce(summation) accumulator(sumAccumulator) combiner(sumCombiner)
-
-static void sumAccumulator(int* accum, int val) {
- *accum += val;
-}
-
-static void sumCombiner(int* accum, const int *val) {
- *accum += *val;
-}
-*/
+ * // This .rs code was used to generate the 32-bit and 64-bit versions of the
+ * // bitcode shown below. It is left in here for reference, as many of the
+ * // variables are used in the the script tests.
+ *
+ * #include "shared.rsh"
+ *
+ * // types
+ * typedef struct Point2 {
+ * int x;
+ * int y;
+ * } Point_2;
+ *
+ * // variables
+ * int var_int;
+ * long var_long;
+ * float var_float;
+ * double var_double;
+ * rs_allocation var_allocation;
+ * uint32_t var_uint32_t;
+ * Point_2 var_point2;
+ * int *var_int_ptr;
+ *
+ * // invoke
+ * void function() {
+ * var_int = 1;
+ * var_long = 2;
+ * var_float = 3.0f;
+ * var_double = 4.0;
+ * var_uint32_t = 5;
+ * var_point2.x = 6;
+ * var_point2.y = 7;
+ * }
+ *
+ * // invokeV
+ * void functionV(int arg) {
+ * var_int = arg;
+ * }
+ *
+ * // set bound buffer
+ * void setBuffer(int dim) {
+ * int i;
+ * for (i = 0; i < dim; ++i) {
+ * var_int_ptr[i] = 5;
+ * }
+ * }
+ *
+ * // set allocation
+ * void setAllocation(int dim) {
+ * int x;
+ * for (x = 0; x < dim; ++x) {
+ * rsSetElementAt_uchar(var_allocation, 7, x);
+ * }
+ * }
+ *
+ * // forEach
+ * uchar RS_KERNEL increment(uchar in) {
+ * return in+1;
+ * }
+ *
+ * // reduction
+ * #pragma rs reduce(summation) accumulator(sumAccumulator) combiner(sumCombiner)
+ *
+ * static void sumAccumulator(int* accum, int val) {
+ * *accum += val;
+ * }
+ *
+ * static void sumCombiner(int* accum, const int *val) {
+ * *accum += *val;
+ * }
+ */
#include "VtsHalRenderscriptV1_0TargetTest.h"
+// bitcode slots
+const int mExportVarIdx_var_int = 0;
+const int mExportVarIdx_var_long = 1;
+const int mExportVarIdx_var_float = 2;
+const int mExportVarIdx_var_double = 3;
+const int mExportVarIdx_var_allocation = 4;
+const int mExportVarIdx_var_uint32_t = 5;
+const int mExportVarIdx_var_point2 = 6;
+const int mExportVarIdx_var_int_ptr = 7;
+// bitcode invoke slots
+//const int mExportForEachIdx_root = 0;
+const int mExportForEachIdx_increment = 1;
+// bitcode reduce slots
+const int mExportReduceIdx_summation = 0;
+// bitcode invoke slots
+const int mExportFuncIdx_function = 0;
+const int mExportFuncIdx_functionV = 1;
+const int mExportFuncIdx_setBuffer = 2;
+const int mExportFuncIdx_setAllocation = 3;
+
+// bitcode
#ifndef __LP64__
const int8_t bitCode[] = {
- -34, -64, 23, 11, 0, 0, 0, 0, 44, 0, 0, 0, -84, 10, 0, 0,
+ -34, -64, 23, 11, 0, 0, 0, 0, 44, 0, 0, 0, -8, 12, 0, 0,
0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 1, 64, 4, 0,
96, 9, 0, 0, 2, 64, 4, 0, 3, 0, 0, 0, 66, 67, -64, -34,
- 33, 12, 0, 0, -88, 2, 0, 0, 1, 16, 0, 0, 18, 0, 0, 0,
+ 33, 12, 0, 0, 59, 3, 0, 0, 1, 16, 0, 0, 18, 0, 0, 0,
7, -127, 35, -111, 65, -56, 4, 73, 6, 16, 50, 57, -110, 1, -124, 12,
37, 5, 8, 25, 30, 4, -117, 98, -128, 24, 69, 2, 66, -110, 11, 66,
-60, 16, 50, 20, 56, 8, 24, 73, 10, 50, 68, 36, 72, 10, -112, 33,
35, -60, 82, -128, 12, 25, 33, 114, 36, 7, -56, -120, 17, 98, -88, -96,
- -88, 64, -58, -16, 1, 0, 0, 0, 73, 24, 0, 0, 25, 0, 0, 0,
- 11, -124, -1, -1, -1, -1, 31, -64, 96, -127, 1, 4, 65, -16, -1, -1,
- -1, -1, 3, 24, 45, 32, 2, 16, 4, 65, 16, 36, -2, -1, -1, -1,
- 127, 0, -125, 5, 70, 0, -126, 32, 8, -126, -124, 0, -126, 32, 8, -126,
- -60, -1, -1, -1, -1, 15, 96, -80, 64, -8, -1, -1, -1, -1, 1, 12,
- 22, 8, -1, -1, -1, -1, 63, 0, 11, -120, 0, 4, 65, 16, 4, -119,
- -1, -1, -1, -1, 31, -64, 80, 88, 64, 4, -64, -1, -1, -1, -1, 15,
- 96, 0, 0, 0, -119, 32, 0, 0, 33, 0, 0, 0, 50, 34, -120, 9,
- 32, 100, -123, 4, 19, 35, -92, -124, 4, 19, 35, -29, -124, -95, -112, 20,
- 18, 76, -116, -116, 11, -124, -60, 76, 16, -112, -63, 28, 1, 24, 16, 48,
- 71, 0, 10, 36, -52, 0, 16, 49, 4, 64, 70, 18, 0, -124, 92, 35,
- 77, 17, 37, 76, 126, -22, 32, -51, 100, 35, 1, 0, 72, -71, 75, -102,
- 34, 74, -104, -4, 72, -6, -127, 101, 113, 4, 96, 66, -100, -58, -65, 115,
- 20, 4, -60, -48, 50, 71, -128, -48, 67, -48, 8, 64, 9, 36, -102, -118,
- 32, 1, 84, 21, -31, 121, -24, 42, -125, 20, 0, -108, -107, 65, 10, 2,
- -38, -118, 32, 53, -44, -103, 0, -96, -81, 8, 18, 72, -31, 64, 0, 0,
+ -88, 64, -58, -16, 1, 0, 0, 0, 73, 24, 0, 0, 34, 0, 0, 0,
+ 11, -124, -1, -1, -1, -1, 31, -64, 96, -127, -16, -1, -1, -1, -1, 3,
+ 24, 44, 32, -124, -32, -1, -1, -1, -1, 7, 96, -127, 1, 4, 65, -16,
+ -1, -1, -1, -1, 3, 24, 45, 32, 2, 16, 4, 65, 16, 36, -2, -1,
+ -1, -1, 127, 0, -125, 5, 70, 0, -126, 32, 8, -126, -124, 0, -126, 32,
+ 8, -126, -60, -1, -1, -1, -1, 15, 96, -80, 64, -8, -1, -1, -1, -1,
+ 1, 88, 64, 4, 32, 8, -126, 32, 72, -4, -1, -1, -1, -1, 0, -122,
+ -62, 2, 34, 0, 65, 16, 4, 65, -30, -1, -1, -1, -1, 7, 48, 20,
+ 22, 16, 66, -16, -1, -1, -1, -1, 3, 24, 44, 32, 2, -32, -1, -1,
+ -1, -1, 7, 48, 0, 0, 0, 0, -119, 32, 0, 0, 36, 0, 0, 0,
+ 50, 34, -120, 9, 32, 100, -123, 4, 19, 35, -92, -124, 4, 19, 35, -29,
+ -124, -95, -112, 20, 18, 76, -116, -116, 11, -124, -60, 76, 16, -100, -63, 28,
+ 1, 24, 16, 48, 71, 0, 10, 36, -52, 0, 16, 49, 4, 64, -58, 53,
+ -46, 20, 81, -62, -28, -89, 14, -46, 76, 54, 18, 0, -128, 16, 10, -18,
+ -110, -90, -120, 18, 38, 63, -110, 126, 96, 89, 28, 1, -104, 16, -89, -15,
+ -17, 28, 5, 1, 45, 115, 4, 8, 53, -28, -116, 0, -108, 0, -94, -88,
+ 8, 16, 64, 83, 6, 0, -123, -128, -86, 6, 32, -85, 8, 77, 67, 88,
+ 25, -96, 0, 32, -83, 12, 80, 16, 16, 87, 4, -120, 33, -49, 4, 0,
+ -127, 69, -128, 66, 18, 7, 2, -26, 8, -126, 41, 0, 0, 0, 0, 0,
19, -80, 112, -112, -121, 118, -80, -121, 59, 104, 3, 119, 120, 7, 119, 40,
-121, 54, 96, -121, 116, 112, -121, 122, -64, -121, 54, 56, 7, 119, -88, -121,
114, 8, 7, 113, 72, -121, 13, 100, 80, 14, 109, 0, 15, 122, 48, 7,
114, -96, 7, 115, 32, 7, 109, -112, 14, 118, 64, 7, 122, 96, 7, 116,
-48, 6, -10, 16, 7, 114, -128, 7, 122, 96, 7, 116, -96, 7, 113, 32,
7, 120, -48, 6, -18, 48, 7, 114, -48, 6, -77, 96, 7, 116, -96, -13,
- 64, -118, 4, 50, 66, 100, 4, -40, -95, 4, -64, -124, 12, 0, 0, 4,
- -64, 14, 101, 0, 44, -124, 0, 0, 32, 0, 118, 40, 5, 112, 33, 3,
- 0, 0, 1, -80, 67, 57, 0, 12, 33, 0, 0, 8, -128, 29, 74, 2,
- 100, -56, 0, 0, 64, 0, -20, 80, 22, 64, 67, 6, 0, 0, 2, -128,
- 13, -121, -37, -95, 56, -64, -122, 12, 0, 0, 4, -64, 14, 37, 2, 58,
- 96, 0, 0, 32, 0, 118, 40, 17, -32, 1, 3, 0, 0, 1, 48, 68,
- -95, 0, 0, 8, 0, 0, 0, -126, 33, -118, 5, 0, 64, 0, 0, 0,
- 16, 12, 81, 48, 0, 0, 4, 0, 0, -128, 96, -120, -94, 1, -64, 48,
- 0, 0, 0, 0, 67, 20, 14, 0, 6, 2, 0, 0, 0, 24, -94, 80,
- 0, 0, 20, 0, 0, 0, -63, 16, -59, 3, 2, -64, 0, 0, 0, 8,
- -122, 40, 98, 0, 0, -64, 1, 0, 0, 16, 100, -127, 0, 0, 0, 0,
- 13, 0, 0, 0, 50, 30, -104, 20, 25, 17, 76, -112, -116, 9, 38, 71,
- -58, 4, 67, 2, 70, 0, 74, -96, 16, 72, 24, 1, 32, 98, 4, -128,
- -116, 17, 0, 66, 70, 0, 72, 25, 1, -96, 101, 4, -128, -104, 17, 0,
- -126, 108, -75, 6, 91, -50, 1, 0, 121, 24, 0, 0, -30, 0, 0, 0,
- 26, 3, 76, -112, 70, 2, 19, 52, 68, 0, 38, 42, 119, 99, 104, 97,
- 114, 95, 115, 105, 122, 101, 67, 4, -128, 26, 98, 0, -45, 24, 4, 0,
- -59, -90, 45, -51, -19, -85, -52, -83, -82, -19, 107, 46, 77, -81, 108, -120,
- 1, 76, 99, 64, 0, 20, -7, 32, -56, -115, 76, -18, 45, -115, 12, 100,
- -116, 45, -52, -19, 12, -60, -82, 76, 110, 46, -19, -51, 13, 100, -58, 5,
- -57, 69, -26, -90, -122, 6, 7, 6, 4, 4, 69, 44, 108, -82, -116, 12,
- -28, -51, 13, -124, -119, -55, -86, 9, 100, -58, 5, -57, 69, -26, -90, -122,
- 6, 7, 38, 101, -120, 48, 6, 6, 15, -69, 50, -71, -71, -76, 55, 55,
- 6, 49, 67, -120, 49, 64, -58, 32, 97, -92, 22, 102, 23, -10, 5, 23,
- 54, -74, 22, 118, 86, -10, -27, 22, -42, 86, -58, 105, -20, -83, -51, 37,
- -52, -115, 76, -18, 45, -115, -52, 69, 110, -50, -123, -82, 108, -114, 110, 8,
- 49, 6, -53, 24, 48, 60, -20, -62, -28, -66, -46, -36, -24, 24, -44, 12,
- 33, -58, -64, 25, -125, -121, -120, 93, -104, -36, 23, -37, -101, -37, 25, 3,
- -101, 33, -60, 24, 68, 99, 32, 49, -79, 11, -109, -5, 50, 99, 123, 11,
- -93, 27, 66, -116, 1, 53, 6, 9, 21, -69, 48, -71, 47, -78, -73, 58,
- 49, -74, 50, 6, 50, 67, -120, 49, -80, -58, -32, -94, 99, 23, 38, -9,
- 21, -58, -58, -10, 54, 22, 70, -105, -10, -26, 70, 65, 6, 102, 8, 49,
- 6, -39, 24, 104, 76, -20, -62, -28, -66, -62, -28, -28, -62, -14, -8, -16,
- 12, -67, -71, -51, -47, -123, -71, -47, 5, -55, -55, -123, -27, -7, 12, 33,
- -58, -128, 27, -125, -114, -118, 93, -104, -36, 23, -36, 91, -102, 27, -99, 12,
- 13, -88, -73, 52, 55, 58, -103, 33, -60, 24, 124, 99, 0, 6, 116, -20,
- -62, -28, -66, -32, -34, -46, -36, -24, 100, -66, -32, -24, -28, 120, -88, 64,
- -67, -91, -71, -47, -55, 12, 33, -58, 64, 12, -58, 96, 12, 24, -48, 12,
- 17, -58, -96, 12, -120, -104, -43, -71, -115, -47, -91, -67, -71, 13, 17, -58,
- -32, 12, 24, -71, -96, -107, -79, -63, -107, -55, 125, -103, -43, -71, -115, -47,
- -91, -67, -71, 89, 13, 17, -58, 32, 13, 72, -56, -67, -67, -47, 13, 17,
- -58, 96, 13, -104, -92, -71, -115, -55, -107, -75, -107, -71, -47, 13, 17, -58,
- -96, 13, 24, -64, 12, 17, -58, -32, 13, 40, -52, -44, 12, 17, -58, 32,
- 14, -104, -52, -43, -75, -75, -123, -47, -91, -67, -71, -47, -103, -85, 107, 11,
- 26, 27, -85, 107, -85, 99, 11, -93, 123, -109, 27, 66, -116, 1, 29, -116,
- 65, -62, 101, -82, -82, 109, -24, -83, 77, 44, -51, -83, 76, 110, -120, 50,
- 6, 115, 48, 6, 101, 48, 6, 117, 32, 1, 99, 96, 7, 67, -124, 49,
- 0, 3, 6, 120, 28, -46, -36, -24, -122, 16, 99, -112, 7, 99, -96, 7,
- 12, -14, -122, 16, 99, -64, 7, 99, -96, 7, 124, -34, -38, -36, -46, -32,
- -34, -24, -54, -36, -24, 64, -58, -48, -62, -28, 24, 77, -91, -75, -63, -79,
- -107, -127, 12, -67, 12, -83, -84, -128, 80, 9, 5, 5, 13, 17, -58, -32,
- 15, -122, 8, 66, 53, -60, 24, 3, 63, 24, 3, 80, 16, -86, 33, -58,
- 24, -24, -63, 24, -120, -126, 80, 13, 49, -58, 96, 20, -58, 96, 20, -124,
- -118, 4, -37, -101, -37, -39, 16, 99, 12, 74, 97, 12, 68, 65, -88, -122,
- 24, 99, 96, 10, 99, 96, 10, 66, -59, -62, -116, -19, 45, -116, 110, -120,
- 49, 6, -88, 48, 6, -94, 32, 84, 67, -116, 49, 72, -123, 49, 72, 5,
- -95, -94, 65, -10, 86, 39, -58, 86, 54, -60, 24, -125, 85, 24, 3, 81,
- 16, -86, 33, -58, 24, -80, -62, 24, -80, -126, 80, 53, 98, 99, -77, 107,
- 115, 105, 123, 35, -85, 99, 43, 115, 49, 99, 11, 59, -101, -101, 34, 12,
- 69, 21, 54, 54, -69, 54, -105, 52, -78, 50, 55, -70, 41, -63, -47, 99,
- 4, 78, 46, -20, -84, 45, 108, -118, -96, 52, 117, 70, -28, -26, -66, -54,
- -16, -32, -34, -28, -24, -66, -20, -62, -28, -90, 32, -48, 84, 97, -101, 23,
- 6, 100, 80, 104, 68, 110, -18, -21, 77, 76, -83, 108, -116, -18, 107, -114,
- -19, -115, 110, 110, 74, 96, 6, 125, 70, -28, -26, -66, -54, -16, -32, -34,
- -28, -24, -66, -52, -22, -36, -58, -90, 8, 104, -96, 6, -67, 70, -28, -26,
- -66, -54, -16, -32, -34, -28, -24, -66, -52, -34, -28, -54, -62, -58, -48, -66,
- -36, -62, -38, -54, -90, 8, 108, -32, 6, -107, 70, -28, -26, -66, -54, -16,
- -32, -34, -28, -24, -66, -52, -34, -28, -54, -62, -58, -48, -90, 8, 112, 32,
- 7, -115, 70, -28, -26, -66, -54, -16, -32, -34, -28, -24, -66, -28, -54, -56,
- -22, -58, -54, -90, 4, 119, -48, 103, 68, 110, -18, -85, 12, 15, -18, 77,
- -114, -18, -117, 46, 15, -82, 108, 74, -128, 7, 61, 74, -96, -34, -46, -36,
- -24, 100, -90, 8, 123, -48, 7, 0, 121, 24, 0, 0, 92, 0, 0, 0,
+ 64, -120, 4, 50, 66, 100, 4, -40, -95, 4, 0, -122, 12, 0, 0, 4,
+ -64, 14, 101, 0, 54, -124, 0, 0, 32, 0, 118, 40, 5, -48, 33, 3,
+ 0, 0, 1, -80, 67, 57, 0, 15, 33, 0, 0, 8, -128, 29, 74, 0,
+ 96, -56, 0, 0, 64, 0, -20, 80, 18, -32, 67, 6, 0, 0, 2, 96,
+ -121, -94, 0, 28, 50, 0, 0, 16, 0, 100, 96, -128, -37, -95, 60, 64,
+ 24, 0, 3, 0, 0, 1, -80, 67, 121, 0, 49, 0, 6, 0, 0, 2,
+ 96, -120, 34, 1, 0, 16, 0, 0, 0, 4, 67, 20, 10, 0, -128, 0,
+ 0, 0, 32, 24, -94, 80, 0, 0, 4, 0, 0, 0, -63, 16, -123, 2,
+ 0, 64, 0, 0, 0, 8, -122, 40, 23, 16, 0, 3, 0, 0, 64, 48,
+ 68, -55, 0, 0, 32, 0, 0, 0, -126, 33, -54, 6, 0, 67, 1, 0,
+ 0, 0, 12, 81, 58, 0, 24, 12, 0, 0, 0, 96, -120, 34, 1, 0,
+ 32, 0, 0, 0, 4, 67, -108, 15, 8, -128, 3, 0, 0, 32, 24, -94,
+ -116, 1, 0, 0, 8, 0, 0, 64, 48, 68, 25, 3, 0, 0, 16, 0,
+ 0, -128, 96, -120, 50, 6, 0, 0, 36, 0, 0, 0, 65, 22, 8, 0,
+ 12, 0, 0, 0, 50, 30, -104, 24, 25, 17, 76, -112, -116, 9, 38, 71,
+ -58, 4, 67, 2, 70, 0, 74, -96, 16, 72, 24, 1, -96, 96, 4, -128,
+ -120, 17, 0, 50, 70, 0, 8, 25, 1, -96, 101, 4, -128, 28, -37, 13,
+ -62, -74, -117, 0, 121, 24, 0, 0, -13, 0, 0, 0, 26, 3, 76, -112,
+ 70, 2, 19, 52, 68, 0, 48, 42, 119, 99, 104, 97, 114, 95, 115, 105,
+ 122, 101, 67, 4, 32, 27, 98, 0, 24, 25, 4, 64, -58, -90, 45, -51,
+ -19, -85, -52, -83, -82, -19, 107, 46, 77, -81, 108, -120, 1, 96, 100, 64,
+ 0, 25, -7, 32, -56, -115, 76, -18, 45, -115, 12, 100, -116, 45, -52, -19,
+ 12, -60, -82, 76, 110, 46, -19, -51, 13, 100, -58, 5, -57, 69, -26, -90,
+ -122, 6, 7, 6, 4, 4, 69, 44, 108, -82, -116, 12, -28, -51, 13, -124,
+ -119, -55, -86, 9, 100, -58, 5, -57, 69, -26, -90, -122, 6, 7, 38, 101,
+ -120, 64, 6, 6, 15, -69, 50, -71, -71, -76, 55, 55, 6, 49, 67, 8,
+ 50, 64, -56, 32, 97, -92, 22, 102, 23, -10, 5, 23, 54, -74, 22, 118,
+ 86, -10, -27, 22, -42, 86, -58, 105, -20, -83, -51, 37, -52, -115, 76, -18,
+ 45, -115, -52, 69, 110, -50, -123, -82, 108, -114, 110, 8, 65, 6, 11, 25,
+ 48, 60, -20, -62, -28, -66, -46, -36, -24, 24, -44, 12, 33, -56, -64, 33,
+ -125, -121, -120, 93, -104, -36, 23, -37, -101, -37, 25, 3, -101, 33, 4, 25,
+ 68, 100, 32, 49, -79, 11, -109, -5, 50, 99, 123, 11, -93, 27, 66, -112,
+ 1, 69, 6, 9, 21, -69, 48, -71, 47, -78, -73, 58, 49, -74, 50, 6,
+ 50, 67, 8, 50, -80, -56, -32, -94, 99, 23, 38, -9, 21, -58, -58, -10,
+ 54, 22, 70, -105, -10, -26, 70, 65, 6, 102, 8, 65, 6, 25, 25, 104,
+ 100, -20, -62, -28, -66, -22, -46, -36, -24, 102, 100, -66, -24, 24, -28, 12,
+ 33, -56, -128, 35, -125, -114, -118, 93, -104, -36, 23, -36, 91, -102, 27, -99,
+ 12, 13, -88, -73, 52, 55, 58, -103, 33, 4, 25, 124, 100, 0, 6, 92,
+ -20, -62, -28, -66, -46, -36, -24, -66, -32, -24, -28, 72, -88, -92, -71, -47,
+ 13, 33, -56, 64, 12, -56, 96, 12, 24, -48, 12, 17, -56, -96, 12, -120,
+ -104, -43, -71, -115, -47, -91, -67, -71, 13, 17, -56, -32, 12, 24, -71, -96,
+ -107, -79, -63, -107, -55, 125, -103, -43, -71, -115, -47, -91, -67, -71, 89, 13,
+ 17, -56, 32, 13, 24, -71, -96, -107, -79, -63, -107, -55, 125, -51, -107, -47,
+ 9, -43, -103, -103, -107, -55, 13, 17, -56, 96, 13, 88, -71, -96, -107, -79,
+ -63, -107, -55, 125, -51, -107, -47, 5, -79, -79, -67, -115, -123, -47, -91, -67,
+ -71, 13, 17, -56, -96, 13, 72, -56, -67, -67, -47, 13, 17, -56, -32, 13,
+ -104, -92, -71, -115, -55, -107, -75, -107, -71, -47, 13, 17, -56, 32, 14, 24,
+ -64, 12, 17, -56, 96, 14, 40, -52, -44, 12, 17, -56, -96, 14, -104, -52,
+ -43, -75, -75, -123, -47, -91, -67, -71, -47, -103, -85, 107, 11, 26, 27, -85,
+ 107, -85, 99, 11, -93, 123, -109, 27, 66, -112, 1, 30, -112, 65, -62, 101,
+ -82, -82, 109, -24, -83, 77, 44, -51, -83, 76, 110, -120, 66, 6, 119, 64,
+ 6, 101, 64, 6, 121, 0, 1, 100, -96, 7, 67, 4, 50, 0, 3, 6,
+ 120, 28, -46, -36, -24, -122, 16, 100, -48, 7, 100, -32, 7, 12, -14, -122,
+ 16, 100, 0, 10, 100, -32, 7, 124, -34, -38, -36, -46, -32, -34, -24, -54,
+ -36, -24, 64, -58, -48, -62, -28, 24, 77, -91, -75, -63, -79, -107, -127, 12,
+ -67, 12, -83, -84, -128, 80, 9, 5, 5, 13, 17, -56, 96, 20, -122, 8,
+ -126, 54, -60, 32, 3, 81, 32, 3, 82, 16, -76, 33, 6, 25, -8, 1,
+ 25, -104, -126, -96, 13, 49, -56, -32, 20, -56, -32, 20, 4, -115, 4, -37,
+ -101, -37, -39, 16, -125, 12, 82, -127, 12, 76, 65, -48, -122, 24, 100, -96,
+ 10, 100, -96, 10, -126, -58, -62, -116, -19, 45, -116, 110, -120, 65, 6, -84,
+ 64, 6, -90, 32, 104, 67, 12, 50, 104, 5, 50, 104, 5, 65, -93, 65,
+ -10, 86, 39, -58, 86, 54, -60, 32, -125, 87, 32, 3, 83, 16, -76, 33,
+ 6, 25, -64, 2, 25, -64, -126, -96, 113, 9, 115, -53, 3, -127, 123, 75,
+ 115, -93, 43, -109, 27, 98, -112, -127, 44, -112, -127, 41, 8, -38, 16, -125,
+ 12, 102, -127, 12, 102, 65, -48, 26, -79, -79, -39, -75, -71, -76, -67, -111,
+ -43, -79, -107, -71, -104, -79, -123, -99, -51, 77, 17, -122, -94, 10, 27, -101,
+ 93, -101, 75, 26, 89, -103, 27, -35, -108, -32, -24, 49, 2, 39, 23, 118,
+ -42, 22, 54, 69, 80, -102, 58, 35, 114, 115, 95, 101, 120, 112, 111, 114,
+ 116, 95, 118, 97, 114, 83, 16, 104, -86, -80, -51, 11, 3, 50, 40, 52,
+ 34, 55, -9, -11, 38, -90, 86, 54, 70, -9, 53, -57, -10, 70, 55, 55,
+ 37, 48, -125, 62, 35, 114, 115, 95, 101, 120, 112, 111, 114, 116, 95, 102,
+ 117, 110, 99, 83, 8, 52, 80, 3, 54, 112, -125, 94, 35, 114, 115, 95,
+ 101, 120, 112, 111, 114, 116, 95, 102, 111, 114, 101, 97, 99, 104, 95, 110,
+ 97, 109, 101, 83, 4, 56, -112, -125, 74, 35, 114, 115, 95, 101, 120, 112,
+ 111, 114, 116, 95, 102, 111, 114, 101, 97, 99, 104, 83, 4, 58, -80, -125,
+ 70, 35, 114, 115, 95, 101, 120, 112, 111, 114, 116, 95, 114, 101, 100, 117,
+ 99, 101, 83, -126, 61, -24, 51, 34, 55, -9, 85, -122, 7, -9, 38, 71,
+ -9, 69, -105, 7, 87, 54, 37, -32, -125, 30, 37, 80, 111, 105, 110, 116,
+ 50, 83, -124, 63, 8, 5, 0, 0, 121, 24, 0, 0, 92, 0, 0, 0,
51, 8, -128, 28, -60, -31, 28, 102, 20, 1, 61, -120, 67, 56, -124, -61,
-116, 66, -128, 7, 121, 120, 7, 115, -104, 113, 12, -26, 0, 15, -19, 16,
14, -12, -128, 14, 51, 12, 66, 30, -62, -63, 29, -50, -95, 28, 102, 48,
@@ -201,236 +247,309 @@
-127, 30, -36, -32, 28, -28, -31, 29, -22, 1, 30, 102, 24, 81, 56, -80,
67, 58, -100, -125, 59, -52, 80, 36, 118, 96, 7, 123, 104, 7, 55, 96,
-121, 119, 120, 7, 120, -104, 81, 76, -12, -112, 15, -16, 80, 14, 0, 0,
- 113, 32, 0, 0, 56, 0, 0, 0, 6, 17, 6, -1, 92, -33, -111, -60,
- 45, 4, 16, -95, 65, 66, 8, 83, 90, -33, -111, -12, 3, -53, -30, 8,
- -64, -124, 56, -115, 13, 40, 21, 16, -3, -125, 67, 5, 11, 97, 5, 74,
- 5, 68, -1, -29, 32, -51, 100, 27, 67, -126, 52, 66, 68, 48, 68, 51,
- -103, 2, 82, 80, -115, 48, 33, 78, 99, 4, 73, 5, 68, 63, 16, 69,
- 0, 102, 17, -111, 127, 16, -53, 67, 68, 127, 65, 53, -62, -124, 56, -51,
- 107, 14, -117, 68, 49, -100, -61, 4, 72, 67, 68, 102, -32, 84, 64, -12,
- 3, -53, -30, 8, -64, -124, 56, -115, 33, 112, 126, 36, -7, 17, 49, 80,
- 2, -15, 23, -115, 47, 81, -116, 38, 8, 20, 67, 45, -64, -28, 68, 6,
- 112, 84, 64, -12, 35, -51, 100, 13, -114, 68, 49, -102, 32, 80, 12, -75,
- 0, -109, 19, 89, 0, 82, 1, -47, -65, 56, -115, 97, 7, 78, 5, 68,
- -1, -29, 32, -51, 100, -1, -49, 20, -39, 3, -30, 71, -110, 63, 76, 78,
- 100, 11, 73, 65, 53, -62, -124, 56, -51, 107, 2, 73, 5, 68, 127, -79,
- 56, -64, 100, 9, -103, 31, 73, 126, 68, 12, -108, 64, -4, 69, -29, 75,
- 20, -61, 57, 76, -128, 52, 68, 4, 97, 32, 0, 0, 54, 0, 0, 0,
- 19, 4, 65, 44, 16, 0, 0, 0, 20, 0, 0, 0, 4, -108, 66, 49,
- -108, 67, 17, 20, 68, 25, -108, 68, 81, -112, 80, 4, 20, 12, 101, 36,
- 4, 32, 1, -45, 80, 70, 66, 0, 18, 16, 6, 67, 25, 9, 1, 72,
- -64, 24, 12, 101, 44, 5, 32, 1, -46, 80, -58, 82, 0, 18, 48, 13,
- 101, 36, 4, 32, 1, 18, 17, 99, 4, 32, 8, -126, 36, 24, -112, 49,
- 70, 0, -126, 32, 8, -126, 32, 8, -126, 36, 72, 0, -125, 17, -64, 52,
- 0, -125, 17, -127, 25, 16, -64, 96, -124, -48, 6, 3, 48, 24, 49, -72,
- 1, 1, 12, 70, -80, -127, 55, 0, -125, 17, 103, -16, 13, -64, 96, 4,
- 26, -128, -63, 0, 12, 70, -92, -127, 24, 12, -64, 96, -124, 26, -112, -63,
- 0, 12, 70, -84, 65, 25, 12, 0, 6, -60, 0, 0, 13, 0, 0, 0,
- 91, 6, 32, 32, -123, 45, 67, 16, -100, -62, -106, 65, 8, 84, 97, -53,
- 48, 4, -83, -80, 101, 32, 2, 82, -40, 50, 20, 1, 41, 108, 25, -116,
- -128, 20, -74, 12, 71, 64, 10, 91, 6, 36, 32, -123, 45, 67, 18, -112,
- 2, 0, 0, 0, 0, 0, 0, 0, 97, 32, 0, 0, 11, 0, 0, 0,
- 19, 4, -63, 96, 4, -32, 13, 0, -122, 3, 1, 0, 2, 0, 0, 0,
- -26, 49, 0, -111, 1, 0, 0, 0, 1, 49, 0, 0, 2, 0, 0, 0,
- 91, 6, 32, 32, 5, 0, 0, 0, 0, 0, 0, 0, 97, 32, 0, 0,
- 11, 0, 0, 0, 19, 4, -63, 121, 64, -40, 55, -63, -32, -64, -32, -127,
- 12, -125, 112, 32, 5, 0, 0, 0, -10, 65, 8, 78, 83, -103, -121, -128,
- 52, 22, 82, 8, 78, 83, -43, 6, 50, 0, -61, 0, 0, 0, 0, 0,
- 97, 32, 0, 0, 16, 0, 0, 0, 19, 4, 1, 121, -61, -64, -32, 3,
- -63, 96, -124, 23, 6, 3, -128, -31, 64, 0, 0, 0, 4, 0, 0, 0,
- -10, 49, 84, -64, 98, 30, 5, 32, 8, 20, 99, 33, 3, 48, 12, 0,
- 1, 49, 0, 0, 3, 0, 0, 0, 91, 6, 32, 32, -123, 45, -125, 16,
- -112, 2, 0, 0, 0, 0, 0, 0, 97, 32, 0, 0, 17, 0, 0, 0,
- 19, 4, 1, 125, -125, -68, 97, 97, 0, 6, 32, 24, -116, -16, -60, 96,
- 0, 48, 28, 8, 4, 0, 0, 0, -10, 49, 84, -64, 98, 30, 5, 32,
- 8, 20, 99, 34, 3, 48, 12, 0, 1, 49, 0, 0, 4, 0, 0, 0,
- 91, 6, 32, 32, -123, 45, 67, 16, -112, -62, -106, 97, 8, 72, 1, 0,
- 0, 0, 0, 0, 97, 32, 0, 0, 3, 0, 0, 0, 19, 4, -63, -120,
- 1, -127, 4, -112, -127, 0, 0, 0, 97, 32, 0, 0, 9, 0, 0, 0,
- 19, 4, -63, 120, -125, 39, 73, -12, -115, 17, 3, 2, 8, 22, 48, -64,
- 112, 32, 0, 0, 2, 0, 0, 0, 7, 80, 16, -51, 20, 97, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
+ 113, 32, 0, 0, 78, 0, 0, 0, 70, -64, 84, 64, -12, 83, 72, 51,
+ -35, -10, 63, -39, -128, 82, 1, -47, 63, 56, 84, -80, 16, -90, -128, 20,
+ 84, 35, 76, -120, -45, -104, -63, 82, 1, -47, -113, 52, -45, -1, 76, -111,
+ 53, 52, 18, 49, 105, -53, -30, 8, -64, -124, 56, -115, 5, 32, 21, 16,
+ -3, -117, -45, 24, -74, -112, 20, 84, 35, 76, -120, -45, -68, -26, -112, 6,
+ -1, 108, -45, -111, -60, 18, -109, -73, 16, 12, -47, 76, -38, -12, 83, -62,
+ 1, 68, -11, 29, 73, 63, -80, 44, -114, 0, 76, -120, -45, 28, -119, 77,
+ -124, -63, 63, -41, 119, 36, 113, 11, 1, 68, 104, -112, 16, -62, -108, -42,
+ 119, 36, -3, -64, -78, 56, 2, 48, 33, 78, 99, 22, -107, 127, 16, -53,
+ 67, 68, -65, 68, 76, -38, -78, 56, 2, 48, 33, 78, 99, 2, 73, 5,
+ 68, 127, -79, 56, -64, 100, 5, 74, 5, 68, -1, -29, 32, -51, 100, -37,
+ 67, -126, 52, 66, 68, 48, 68, 51, 25, 67, 34, 17, -45, 70, 21, 5,
+ 17, 89, -60, 34, 81, 12, -25, 48, 1, -46, 16, -111, 37, 100, 126, 36,
+ -7, 17, 49, 80, 2, -15, 23, -115, 47, 81, 12, -25, 48, 1, -46, 16,
+ -111, 81, 68, -2, 65, 44, 15, 17, -3, 5, -43, 8, 19, -30, 52, -81,
+ 29, 56, 21, 16, -3, -64, -78, 56, 2, 48, 33, 78, 99, 8, -100, 31,
+ 73, 126, 68, 12, -108, 64, -4, 69, -29, 75, 20, -93, 9, 2, -59, 80,
+ 11, 48, 57, -111, 1, 28, 21, 16, -3, 72, 51, 89, 69, -28, 31, -60,
+ -14, 16, -47, 47, 17, -45, 70, 21, 5, 17, 25, -124, 35, 81, -116, 38,
+ 8, 20, 67, 45, -64, -28, 68, 38, -127, -8, -111, -28, 15, -109, 19, 1,
+ 97, 32, 0, 0, 37, 0, 0, 0, 19, 4, 65, 44, 16, 0, 0, 0,
+ 12, 0, 0, 0, 4, -108, 66, 49, -108, 3, 9, 69, 64, -63, 80, 70,
+ 82, 0, 23, -128, 13, 101, 36, 5, 112, 1, 23, 17, 99, 4, 32, 8,
+ -126, 36, 24, -112, 49, 70, 0, -126, 32, 8, -126, 32, 8, -126, 36, 72,
+ 0, 0, 0, 0, -125, 17, 0, 54, 0, -125, 17, 65, 25, 16, -64, 96,
+ -124, -128, 6, 3, 48, 24, 49, -92, 1, 1, 12, 70, 16, 98, 48, 0,
+ -125, 17, 103, 48, 6, 3, 48, 24, 97, 6, 100, 48, 0, 24, 16, 3,
+ 9, 0, 0, 0, 91, 6, 32, 64, -123, 45, 67, 16, -84, -62, -106, 65,
+ 8, 92, 97, -53, 48, 4, -79, -80, 101, 32, 2, 84, -40, 50, 20, 1,
+ 42, 108, 25, -116, 0, 21, 0, 0, 0, 0, 0, 0, 97, 32, 0, 0,
+ 11, 0, 0, 0, 19, 4, -63, 96, 4, 32, 6, 3, -128, -31, 64, 0,
+ 2, 0, 0, 0, 38, 50, 0, -111, 1, 0, 0, 0, 1, 49, 0, 0,
+ 2, 0, 0, 0, 91, 6, 32, 64, 5, 0, 0, 0, 0, 0, 0, 0,
+ 97, 32, 0, 0, 38, 0, 0, 0, 19, 4, 68, 44, 16, 0, 0, 0,
+ 1, 0, 0, 0, 4, -108, 2, 0, -61, 13, 98, 112, -103, -63, 44, 67,
+ 48, -112, 1, 25, 99, -106, 64, 24, -88, 0, -82, 0, 13, -124, -15, -124,
+ 50, 48, -125, -63, -120, 51, 24, -125, 1, 48, 51, -64, 96, 48, -36, -128,
+ 6, 98, 0, 6, -77, 12, -125, -112, 6, 24, 14, 4, 14, 0, 0, 0,
+ 23, 96, -8, 75, -28, 63, -57, 13, 44, -2, 47, 68, -56, -12, 19, -125,
+ 65, -40, 9, 2, 68, 17, -128, 33, -61, 101, 36, -124, -64, 60, -72, -119,
+ 12, 3, -62, 24, -54, -128, 52, -126, -103, 16, -120, 79, -25, -106, -126, 16,
+ 23, 50, 9, 78, 51, 0, 0, 0, 1, 49, 0, 0, 3, 0, 0, 0,
+ 91, 6, 33, -96, -123, 45, -125, 17, -96, 2, 0, 0, 0, 0, 0, 0,
+ 97, 32, 0, 0, 36, 0, 0, 0, 19, 4, 67, 44, 16, 0, 0, 0,
+ 3, 0, 0, 0, -44, -108, 3, 5, -74, -52, 1, 85, 51, 0, 0, 0,
+ -61, 13, 98, 112, -103, -63, 44, 67, 32, -104, -63, 64, 5, -96, 6, -63,
+ 5, 16, 25, -116, -39, -122, 50, 64, 3, 96, -60, -64, 80, 2, 39, 13,
+ -58, -32, 12, -20, 12, 48, 24, 12, 55, -88, -127, 24, -128, -63, 44, -125,
+ 16, -84, 1, -122, 3, 1, 0, 0, 14, 0, 0, 0, -122, 114, -8, 84,
+ -13, 0, -126, 114, 1, -122, -65, 68, -2, 115, -100, -64, -30, -1, 66, -124,
+ 76, 63, 49, 24, -124, -103, 16, 2, -13, -32, 38, 50, 12, 8, 99, 42,
+ 3, -46, 8, 118, 66, 92, 62, -99, -37, 10, 66, 92, -56, 36, 56, -51,
+ 0, 0, 0, 0, 0, 0, 0, 0, 97, 32, 0, 0, 12, 0, 0, 0,
+ 19, 4, -63, -119, 1, 16, 54, 6, 24, 12, -114, 12, 26, -56, 50, 8,
+ 7, 2, 0, 0, 5, 0, 0, 0, 54, 66, 8, 78, 83, -103, -120, -128,
+ 52, 86, 82, 8, 78, 83, -43, 70, 50, 0, -61, 0, 0, 0, 0, 0,
+ 97, 32, 0, 0, 16, 0, 0, 0, 19, 4, 1, -119, -63, 48, 50, 24,
+ 3, 16, 12, 70, -120, 65, 25, 12, 0, -122, 3, 1, 4, 0, 0, 0,
+ 54, 50, 84, -64, 98, 34, 5, 32, 8, 20, 99, 37, 3, 48, 12, 0,
+ 1, 49, 0, 0, 3, 0, 0, 0, 91, 6, 32, 64, -123, 45, -125, 16,
+ -96, 2, 0, 0, 0, 0, 0, 0, 97, 32, 0, 0, 18, 0, 0, 0,
+ 19, 4, 1, -115, -63, 32, 49, 24, 86, 6, 100, 0, -126, -63, 8, 49,
+ 48, -125, 1, -64, 112, 32, 0, 0, 4, 0, 0, 0, 54, 50, 84, -64,
+ 98, 34, 5, 32, 8, 20, 99, 38, 3, 48, 12, 0, 1, 49, 0, 0,
+ 4, 0, 0, 0, 91, 6, 32, 64, -123, 45, 67, 16, -96, -62, -106, 97,
+ 8, 80, 1, 0, 0, 0, 0, 0, 97, 32, 0, 0, 3, 0, 0, 0,
+ 19, 4, -63, -120, 1, -79, 4, -45, -127, 0, 0, 0, 97, 32, 0, 0,
+ 9, 0, 0, 0, 19, 4, -63, 120, -125, 24, 92, 23, -115, -63, 24, 49,
+ 32, -128, 96, 33, 3, 12, 7, 2, 2, 0, 0, 0, 7, 80, 16, -51,
+ 20, 97, 0, 0, 0, 0, 0, 0, 97, 32, 0, 0, 9, 0, 0, 0,
+ 19, 4, -63, 120, -125, 24, 92, 23, -115, -63, 24, 49, 32, -128, -128, 33,
+ 3, 12, 7, 2, 2, 0, 0, 0, 7, 80, 16, -51, 20, 97, 0, 0,
+ 0, 0, 0, 0, 97, 32, 0, 0, 9, 0, 0, 0, 19, 4, -63, 120,
+ -125, 24, 92, 23, -115, -63, 24, 49, 32, -128, -96, 33, 3, 12, 7, 2,
+ 2, 0, 0, 0, 7, 80, 16, -51, 20, 97, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,
};
-const int bitCodeLength = 2776;
+const int bitCodeLength = 3364;
#else
const int8_t bitCode[] = {
- -34, -64, 23, 11, 0, 0, 0, 0, 44, 0, 0, 0, -116, 10, 0, 0,
+ -34, -64, 23, 11, 0, 0, 0, 0, 44, 0, 0, 0, 108, 13, 0, 0,
0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 1, 64, 4, 0,
96, 9, 0, 0, 2, 64, 4, 0, 3, 0, 0, 0, 66, 67, -64, -34,
- 33, 12, 0, 0, -96, 2, 0, 0, 1, 16, 0, 0, 18, 0, 0, 0,
+ 33, 12, 0, 0, 88, 3, 0, 0, 1, 16, 0, 0, 18, 0, 0, 0,
7, -127, 35, -111, 65, -56, 4, 73, 6, 16, 50, 57, -110, 1, -124, 12,
37, 5, 8, 25, 30, 4, -117, 98, -128, 24, 69, 2, 66, -110, 11, 66,
-60, 16, 50, 20, 56, 8, 24, 73, 10, 50, 68, 36, 72, 10, -112, 33,
35, -60, 82, -128, 12, 25, 33, 114, 36, 7, -56, -120, 17, 98, -88, -96,
- -88, 64, -58, -16, 1, 0, 0, 0, 73, 24, 0, 0, 24, 0, 0, 0,
+ -88, 64, -58, -16, 1, 0, 0, 0, 73, 24, 0, 0, 33, 0, 0, 0,
11, -124, -1, -1, -1, -1, 31, -64, 96, -127, -16, -1, -1, -1, -1, 3,
- 24, 45, 32, 2, 16, 4, 65, 16, 36, -2, -1, -1, -1, 127, 0, -125,
- 5, 70, 0, -126, 32, 8, -126, -124, 0, -126, 32, 8, -126, -60, -1, -1,
- -1, -1, 15, 96, -80, 64, -8, -1, -1, -1, -1, 1, 12, 22, 8, -1,
- -1, -1, -1, 63, 0, 11, -120, 0, 4, 65, 16, 4, -119, -1, -1, -1,
- -1, 31, -64, 80, 88, 64, 4, -64, -1, -1, -1, -1, 15, 96, 0, 0,
- -119, 32, 0, 0, 34, 0, 0, 0, 50, 34, -120, 9, 32, 100, -123, 4,
- 19, 35, -92, -124, 4, 19, 35, -29, -124, -95, -112, 20, 18, 76, -116, -116,
- 11, -124, -60, 76, 16, -112, -63, 28, 1, 24, 16, 48, 71, 0, 10, 36,
- -52, 0, 16, 49, 4, 64, 70, 18, 0, -124, 28, 36, 77, 17, 37, 76,
- 126, -22, 32, -51, 100, -5, 61, -114, 4, 0, 32, -27, 46, 105, -118, 40,
- 97, -14, 35, -23, 7, -106, -59, 17, -128, 9, 113, 26, -65, -49, 17, 49,
- 12, -61, 64, 12, 45, 115, 4, 8, 61, 4, -115, 0, -108, 64, -94, -87,
- 8, 18, 64, 85, 17, -98, -121, -82, 50, 72, 1, 64, 89, 25, -92, 32,
- -96, -83, 8, 82, 67, -99, 9, 0, -6, -118, 32, -127, 20, 14, 4, 0,
- 19, -76, 112, 8, 7, 121, 24, 7, 116, -80, 3, 58, 104, 3, 119, 120,
- 7, 119, 40, -121, 54, 96, -121, 116, 112, -121, 122, -64, -121, 54, 56, 7,
- 119, -88, -121, 114, 8, 7, 113, 72, -121, 13, 115, 80, 14, 109, -48, 14,
- 122, 80, 14, 109, -112, 14, 120, -96, 7, 120, -96, 7, 115, 32, 7, 109,
- -112, 14, 113, 96, 7, 122, 16, 7, 118, -96, 7, 115, 32, 7, 109, -112,
- 14, 118, 64, 7, 122, 96, 7, 116, -48, 6, -23, 16, 7, 114, -128, 7,
- 122, 16, 7, 114, -128, 7, 109, -32, 14, 115, 32, 7, 122, 96, 7, 116,
- -48, 6, -77, 16, 7, 114, -128, 7, 58, 15, -92, 72, 32, 35, 68, 70,
- -128, 29, 74, 0, 76, -56, 0, 0, 64, 0, -20, 80, 6, -128, 66, 8,
- 0, 0, 2, 96, -121, 82, 0, 21, 50, 0, 0, 16, 0, 59, -108, 3,
- -80, 16, 2, 0, -128, 0, -40, -95, 36, -64, -123, 12, 0, 0, 4, -64,
- 14, 101, 1, 48, 100, 0, 0, 32, 0, -40, 104, -56, 29, -118, 3, 100,
- 8, 1, 0, 64, 0, -20, 80, 34, 96, 3, 8, 0, 0, 2, 96, -121,
- 18, 1, 28, 64, 0, 0, 16, 0, 67, 20, 10, 0, -128, 0, 0, 0,
- 32, 24, -94, 88, 0, 0, 4, 0, 0, 0, -63, 16, 5, 3, 0, 64,
- 0, 0, 0, 8, -122, 40, 26, 0, 12, 3, 0, 0, 0, 48, 68, -31,
- 0, 96, 32, 0, 0, 0, -128, 33, 10, 5, 0, 64, 1, 0, 0, 16,
- 12, 81, 60, 32, 0, 12, 0, 0, -128, 96, -120, 34, 6, 0, 0, 28,
- 0, 0, 0, 65, 22, 8, 0, 0, 13, 0, 0, 0, 50, 30, -104, 20,
- 25, 17, 76, -112, -116, 9, 38, 71, -58, 4, 67, 2, 70, 0, 72, 24,
- 1, 32, 98, 4, -128, -116, 17, 0, 66, 70, 0, 72, 25, 1, -96, 101,
- 4, -128, -104, 17, 0, -126, 108, -75, 6, 91, -50, 1, 0, 0, 0, 0,
- 121, 24, 0, 0, -48, 0, 0, 0, 26, 3, 76, -112, 70, 2, 19, 68,
+ 24, 44, 16, -2, -1, -1, -1, 127, 0, 22, 24, 1, 8, -126, 32, 8,
+ 18, 2, 8, -126, 32, 8, 18, -1, -1, -1, -1, 63, -128, -63, 2, -31,
+ -1, -1, -1, -1, 7, 48, 90, 64, 4, 32, 8, -126, 32, 72, -4, -1,
+ -1, -1, -1, 0, 6, 11, -116, 0, 4, 65, 16, 4, 9, 1, 4, 65,
+ 16, 4, -119, -1, -1, -1, -1, 31, -64, 96, 1, 17, -128, 32, 8, -126,
+ 32, -15, -1, -1, -1, -1, 3, 24, 10, 11, -120, 0, 4, 65, 16, 4,
+ -119, -1, -1, -1, -1, 31, -64, 80, 88, 64, 4, -64, -1, -1, -1, -1,
+ 15, 96, 0, 0, -119, 32, 0, 0, 38, 0, 0, 0, 50, 34, -120, 9,
+ 32, 100, -123, 4, 19, 35, -92, -124, 4, 19, 35, -29, -124, -95, -112, 20,
+ 18, 76, -116, -116, 11, -124, -60, 76, 16, -96, -63, 28, 1, 24, 16, 48,
+ 71, 0, 10, 36, -52, 0, 16, 49, 4, 64, -58, 65, -46, 20, 81, -62,
+ -28, -89, 14, -46, 76, -74, -33, -29, 72, 0, 0, 66, 40, -72, 75, -102,
+ 34, 74, -104, -4, 72, -6, -127, 101, 113, 4, 96, 66, -100, -58, -17, 115,
+ 68, 12, -61, 48, -48, 50, 71, -128, 80, 67, -50, 8, 64, 9, 32, -118,
+ -118, 0, 1, 52, 21, 2, 98, 26, -128, -86, 57, -126, -96, 24, -112, -29,
+ 8, -64, 69, 88, 17, -102, -122, -76, 50, 64, 1, 64, 92, 25, -96, 32,
+ 32, -81, 8, 16, 67, -96, 9, 0, 18, -117, 0, -115, 68, 14, 4, 76,
+ 1, 0, 0, 0, 19, -76, 112, 8, 7, 121, 24, 7, 116, -80, 3, 58,
+ 104, 3, 119, 120, 7, 119, 40, -121, 54, 96, -121, 116, 112, -121, 122, -64,
+ -121, 54, 56, 7, 119, -88, -121, 114, 8, 7, 113, 72, -121, 13, 115, 80,
+ 14, 109, -48, 14, 122, 80, 14, 109, -112, 14, 120, -96, 7, 120, -96, 7,
+ 115, 32, 7, 109, -112, 14, 113, 96, 7, 122, 16, 7, 118, -96, 7, 115,
+ 32, 7, 109, -112, 14, 118, 64, 7, 122, 96, 7, 116, -48, 6, -23, 16,
+ 7, 114, -128, 7, 122, 16, 7, 114, -128, 7, 109, -32, 14, 115, 32, 7,
+ 122, 96, 7, 116, -48, 6, -77, 16, 7, 114, -128, 7, 58, 15, -124, 72,
+ 32, 35, 68, 70, -128, 29, 74, 0, 100, -56, 0, 0, 64, 0, -20, 80,
+ 6, 96, 67, 8, 0, 0, 2, 96, -121, 82, 0, 31, 50, 0, 0, 16,
+ 0, 59, -108, 3, 0, 3, -124, 0, 0, 32, 0, 118, 40, 1, -112, 33,
+ 3, 0, 0, 1, -80, 67, 73, -128, 48, 64, 6, 0, 0, 2, 96, -121,
+ -94, 0, 30, 66, 0, 0, 16, 0, 100, 98, -128, -36, -95, 60, -64, 24,
+ 0, 4, 0, 0, 1, -80, 67, 121, 0, 50, 0, 8, 0, 0, 2, 96,
+ -120, 34, 1, 0, 16, 0, 0, 0, 4, 67, 20, 10, 0, -128, 0, 0,
+ 0, 32, 24, -94, 80, 0, 0, 4, 0, 0, 0, -63, 16, -123, 2, 0,
+ 64, 0, 0, 0, 8, -122, 40, 22, 16, 0, 3, 0, 0, 64, 48, 68,
+ -55, -128, 0, 32, 0, 0, 0, -128, 33, -54, 6, 0, 64, 1, 0, 0,
+ 16, 12, 81, 58, 0, 24, 12, 0, 0, 0, 96, -120, -14, 1, -64, 112,
+ 0, 0, 0, 0, 67, 20, 9, 0, 0, 1, 0, 0, 32, 24, -94, -124,
+ 1, 16, 0, 3, 0, 0, 64, 48, 68, 41, 3, 0, 0, 16, 0, 0,
+ -128, 96, -120, 82, 6, 0, 0, 32, 0, 0, 0, -63, 16, -91, 12, 0,
+ 0, 72, 0, 0, 0, -126, 44, 16, 13, 0, 0, 0, 50, 30, -104, 24,
+ 25, 17, 76, -112, -116, 9, 38, 71, -58, 4, 67, 2, 70, 0, 72, 40,
+ -104, 17, -128, -126, 40, 16, 10, 70, 0, -120, 24, 1, 32, 99, 4, -128,
+ -112, 17, 0, 90, 70, 0, -56, -79, -19, 34, 108, 63, 9, 0, 0, 0,
+ 121, 24, 0, 0, -22, 0, 0, 0, 26, 3, 76, -112, 70, 2, 19, 68,
62, 8, 114, 35, -109, 123, 75, 35, 3, 25, 99, 11, 115, 59, 3, -79,
43, -109, -101, 75, 123, 115, 3, -103, 113, -63, 113, -111, -71, -87, -95, -63,
-127, 1, 1, 65, 17, 11, -101, 43, 35, 3, 121, 115, 3, 97, 98, -78,
- 106, 2, -103, 113, -63, 113, -111, -71, -87, -95, -63, -127, 73, 25, 34, -116,
- 1, -64, -61, -82, 76, 110, 46, -19, -51, -115, 65, -52, 16, 98, 12, -124,
- 49, 24, 24, -87, -123, -39, -123, 125, -63, -123, -115, -83, -123, -99, -107, 125,
+ 106, 2, -103, 113, -63, 113, -111, -71, -87, -95, -63, -127, 73, 25, 34, -104,
+ 1, -64, -61, -82, 76, 110, 46, -19, -51, -115, 65, -52, 16, -62, 12, 4,
+ 51, 24, 24, -87, -123, -39, -123, 125, -63, -123, -115, -83, -123, -99, -107, 125,
-71, -123, -75, -107, 113, 26, 123, 107, 115, 9, 115, 35, -109, 123, 75, 35,
- 115, -111, -101, 115, -95, 43, -101, -93, 27, 66, -116, 65, 49, 6, 6, 15,
- -69, 48, -71, -81, 52, 55, 58, 6, 53, 67, -120, 49, 64, -58, 32, 33,
- 98, 23, 38, -9, -59, -10, -26, 118, -58, -64, 102, 8, 49, 6, -53, 24,
- 48, 76, -20, -62, -28, -66, -52, -40, -34, -62, -24, -122, 16, 99, -32, -116,
+ 115, -111, -101, 115, -95, 43, -101, -93, 27, 66, -104, 65, 97, 6, 6, 15,
+ -69, 48, -71, -81, 52, 55, 58, 6, 53, 67, 8, 51, 64, -52, 32, 33,
+ 98, 23, 38, -9, -59, -10, -26, 118, -58, -64, 102, 8, 97, 6, -117, 25,
+ 48, 76, -20, -62, -28, -66, -52, -40, -34, -62, -24, -122, 16, 102, -32, -104,
-63, 64, -59, 46, 76, -18, -117, -20, -83, 78, -116, -83, -116, -127, -52, 16,
- 98, 12, -96, 49, -120, -24, -40, -123, -55, 125, -123, -79, -79, -67, -115, -123,
- -47, -91, -67, -71, 81, -112, -127, 25, 66, -116, -63, 52, 6, 20, 19, -69,
- 48, -71, -81, 48, 57, -71, -80, 60, 62, 60, 67, 111, 110, 115, 116, 97,
- 110, 116, 65, 114, 114, 97, 121, 62, 67, -120, 49, -80, -58, -32, -94, 98,
- 23, 38, -9, 5, -9, -106, -26, 70, 39, 67, 3, -22, 45, -51, -115, 78,
- 102, 8, 49, 6, -39, 24, 104, 116, -20, -62, -28, -66, -32, -34, -46, -36,
- -24, 100, -66, -32, -24, -28, 120, -88, 64, -67, -91, -71, -47, -55, 12, 33,
- -58, -128, 27, -125, -114, 1, -51, 16, 97, 12, 62, 34, 102, 117, 110, 99,
- 116, 105, 111, 110, 67, -124, 49, 8, 3, 70, 46, 104, 101, 108, 112, 101,
- 114, 95, 102, 117, 110, 99, 116, 105, 111, 110, 86, 67, -124, 49, 24, 3,
- 18, 114, 111, 111, 116, 67, -124, 49, 40, 3, 38, 105, 110, 99, 114, 101,
- 109, 101, 110, 116, 67, -124, 49, 56, 3, 6, 48, 67, -124, 49, 72, 3,
- 10, 51, 53, 67, -124, 49, 88, 3, 38, 115, 117, 109, 109, 97, 116, 105,
- 111, 110, 116, -26, -22, -38, -126, -58, -58, -22, -38, -22, -40, -62, -24, -34,
- -28, -122, 16, 99, -32, 6, 99, 48, 112, -103, -85, 107, 27, 122, 107, 19,
- 75, 115, 43, -109, 27, -94, -116, 65, 27, -116, -63, 55, 6, 111, 32, 1,
- 99, 0, 7, 67, -124, 49, -48, 24, -32, 113, 72, 115, -93, 27, 66, -116,
- -63, 28, -116, 1, 29, 48, -56, 27, 66, -116, -127, 29, -116, 1, 29, -16,
- 121, 107, 115, 75, -125, 123, -93, 43, 115, -93, 3, 25, 67, 11, -109, 99,
- 52, -107, -42, 6, -57, 86, 6, 50, -12, 50, -76, -78, 2, 66, 37, 20,
- 20, 52, 68, 24, -125, 60, 24, 34, 8, -45, 16, 99, 12, -16, 96, 12,
- -12, 64, -104, -122, 24, 99, 64, 7, 99, -64, 7, -62, 52, -60, 24, -125,
- 62, 24, -125, 62, 16, 38, 18, 108, 111, 110, 103, 67, -116, 49, -8, -125,
- 49, -32, 3, 97, 26, 98, -116, 1, 40, -116, 1, 40, 8, 19, 11, 51,
- -74, -73, 48, -70, 33, -58, 24, -120, -62, 24, -16, -127, 48, 13, 49, -58,
- 96, 20, -58, 96, 20, -124, -119, 6, -39, 91, -99, 24, 91, -39, 16, 99,
- 12, 74, 97, 12, -8, 64, -104, -122, 24, 99, 96, 10, 99, 96, 10, -62,
- 84, -123, -115, -51, -82, -51, 37, -115, -84, -52, -115, 110, 74, 16, -12, 24,
- -127, -109, 11, 59, 107, 11, -101, 34, 16, 71, -99, 17, -71, -71, -81, 50,
- 60, -72, 55, 57, -70, 47, -69, 48, -71, 41, -120, -46, 60, 82, -123, 109,
- 94, -95, 17, -71, -71, -81, 55, 49, -75, -78, 49, -70, -81, 57, -74, 55,
- -70, -71, 41, 1, 24, -12, 25, -111, -101, -5, 42, -61, -125, 123, -109, -93,
- -5, 50, -85, 115, 27, -101, 34, -120, 1, 25, -12, 26, -111, -101, -5, 42,
- -61, -125, 123, -109, -93, -5, 50, 123, -109, 43, 11, 27, 67, -5, 114, 11,
- 107, 43, -101, 34, -104, 1, 26, 84, 26, -111, -101, -5, 42, -61, -125, 123,
- -109, -93, -5, 50, 123, -109, 43, 11, 27, 67, -101, 34, -88, 1, 27, 52,
- 26, -111, -101, -5, 42, -61, -125, 123, -109, -93, -5, -110, 43, 35, -85, 27,
- 43, -101, 18, -60, 65, -97, 17, -71, -71, -81, 50, 60, -72, 55, 57, -70,
- 47, -70, 60, -72, -78, 41, -127, 28, -12, 40, -127, 122, 75, 115, -93, -109,
- -103, 34, -44, -63, 29, 0, 0, 0, 121, 24, 0, 0, 92, 0, 0, 0,
- 51, 8, -128, 28, -60, -31, 28, 102, 20, 1, 61, -120, 67, 56, -124, -61,
- -116, 66, -128, 7, 121, 120, 7, 115, -104, 113, 12, -26, 0, 15, -19, 16,
- 14, -12, -128, 14, 51, 12, 66, 30, -62, -63, 29, -50, -95, 28, 102, 48,
- 5, 61, -120, 67, 56, -124, -125, 27, -52, 3, 61, -56, 67, 61, -116, 3,
- 61, -52, 120, -116, 116, 112, 7, 123, 8, 7, 121, 72, -121, 112, 112, 7,
- 122, 112, 3, 118, 120, -121, 112, 32, -121, 25, -52, 17, 14, -20, -112, 14,
- -31, 48, 15, 110, 48, 15, -29, -16, 14, -16, 80, 14, 51, 16, -60, 29,
- -34, 33, 28, -40, 33, 29, -62, 97, 30, 102, 48, -119, 59, -68, -125, 59,
- -48, 67, 57, -76, 3, 60, -68, -125, 60, -124, 3, 59, -52, -16, 20, 118,
- 96, 7, 123, 104, 7, 55, 104, -121, 114, 104, 7, 55, -128, -121, 112, -112,
- -121, 112, 96, 7, 118, 40, 7, 118, -8, 5, 118, 120, -121, 119, -128, -121,
- 95, 8, -121, 113, 24, -121, 114, -104, -121, 121, -104, -127, 44, -18, -16, 14,
- -18, -32, 14, -11, -64, 14, -20, 48, 3, 98, -56, -95, 28, -28, -95, 28,
- -52, -95, 28, -28, -95, 28, -36, 97, 28, -54, 33, 28, -60, -127, 29, -54,
- 97, 6, -42, -112, 67, 57, -56, 67, 57, -104, 67, 57, -56, 67, 57, -72,
- -61, 56, -108, 67, 56, -120, 3, 59, -108, -61, 47, -68, -125, 60, -4, -126,
- 59, -44, 3, 59, -80, -61, 12, -57, 105, -121, 112, 88, -121, 114, 112, -125,
- 116, 104, 7, 120, 96, -121, 116, 24, -121, 116, -96, -121, 25, -50, 83, 15,
- -18, 0, 15, -14, 80, 14, -28, -112, 14, -29, 64, 15, -31, 32, 14, -20,
- 80, 14, 51, 32, 40, 29, -36, -63, 30, -62, 65, 30, -46, 33, 28, -36,
- -127, 30, -36, -32, 28, -28, -31, 29, -22, 1, 30, 102, 24, 81, 56, -80,
- 67, 58, -100, -125, 59, -52, 80, 36, 118, 96, 7, 123, 104, 7, 55, 96,
- -121, 119, 120, 7, 120, -104, 81, 76, -12, -112, 15, -16, 80, 14, 0, 0,
- 113, 32, 0, 0, 56, 0, 0, 0, 6, 17, 6, -1, 92, -33, -111, -60,
- 45, 4, 16, -95, 65, 66, 8, 83, 90, -33, -111, -12, 3, -53, -30, 8,
- -64, -124, 56, -115, 13, 40, 21, 16, -3, -125, 67, 5, 11, 97, 5, 74,
- 5, 68, -1, -29, 32, -51, 100, 27, 67, -126, 52, 66, 68, 48, 68, 51,
- -103, 2, 82, 80, -115, 48, 33, 78, 99, 4, 73, 5, 68, 63, 16, 69,
- 0, 102, 17, -111, 127, 16, -53, 67, 68, 127, 65, 53, -62, -124, 56, -51,
- 107, 14, -117, 68, 49, -100, -61, 4, 72, 67, 68, 102, -32, 84, 64, -12,
- 3, -53, -30, 8, -64, -124, 56, -115, 33, 112, 126, 36, -7, 17, 49, 80,
- 2, -15, 23, -115, 47, 81, -116, 38, 8, 20, 67, 45, -64, -28, 68, 6,
- 112, 84, 64, -12, 35, -51, 100, 13, -114, 68, 49, -102, 32, 80, 12, -75,
- 0, -109, 19, 89, 0, 82, 1, -47, -65, 56, -115, 97, 7, 78, 5, 68,
- -1, -29, 32, -51, 100, -1, -49, 20, -39, 3, -30, 71, -110, 63, 76, 78,
- 100, 11, 73, 65, 53, -62, -124, 56, -51, 107, 2, 73, 5, 68, 127, -79,
- 56, -64, 100, 9, -103, 31, 73, 126, 68, 12, -108, 64, -4, 69, -29, 75,
- 20, -61, 57, 76, -128, 52, 68, 4, 97, 32, 0, 0, 55, 0, 0, 0,
- 19, 4, 65, 44, 16, 0, 0, 0, 21, 0, 0, 0, 4, -108, 64, 41,
- 20, 67, 57, 20, 68, 73, 20, 5, 9, 101, 80, 2, 69, 64, -63, 80,
- 70, 66, 8, -109, 48, 6, 67, 25, 75, 33, 76, -128, 52, -108, -79, 20,
- -62, 4, 112, 67, 25, 9, 33, 76, 66, 25, 12, 101, 36, -124, 48, 9,
- 100, 48, -108, -111, 16, -62, 36, 76, 68, -116, 17, -128, 32, 8, -110, 96,
- 64, -58, 24, 1, 8, -126, 32, 8, -126, 32, 8, -110, 32, 1, 0, 0,
- -125, 17, 0, 55, 0, -125, 17, 65, 25, 16, -64, 96, -124, -64, 6, 3,
- 48, 24, 49, -76, 1, 1, 12, 70, -84, 65, 55, 0, -125, 17, 106, -32,
- 13, -64, 96, 68, 26, 124, 3, 48, 24, 97, 6, 96, 48, 0, -125, 17,
- 103, 16, 6, 3, 48, 24, -127, 6, 98, 48, 0, 24, 16, 3, 0, 0,
- 13, 0, 0, 0, 91, 6, 32, -16, -125, 45, 67, 16, -124, -62, -106, 65,
- 8, 72, 97, -53, 48, 4, -89, -80, 101, 32, 2, 63, -40, 50, 20, -127,
- 31, 108, 25, -116, -64, 15, -74, 12, 71, -32, 7, 91, 6, 36, -16, -125,
- 45, 67, 18, -8, 1, 0, 0, 0, 0, 0, 0, 0, 97, 32, 0, 0,
- 11, 0, 0, 0, 19, 4, -63, 96, 4, -64, 13, 0, -122, 3, 1, 0,
- 2, 0, 0, 0, -58, 49, 0, -111, 1, 0, 0, 0, 1, 49, 0, 0,
- 2, 0, 0, 0, 91, 6, 32, -16, 3, 0, 0, 0, 0, 0, 0, 0,
+ -62, 12, 32, 51, -120, -24, -40, -123, -55, 125, -123, -79, -79, -67, -115, -123,
+ -47, -91, -67, -71, 81, -112, -127, 25, 66, -104, -63, 100, 6, 20, 25, -69,
+ 48, -71, -81, -70, 52, 55, -70, 25, -103, 47, 58, 6, 57, 67, 8, 51,
+ -80, -52, -32, -94, 98, 23, 38, -9, 5, -9, -106, -26, 70, 39, 67, 3,
+ -22, 45, -51, -115, 78, 102, 8, 97, 6, -103, 25, 104, 92, -20, -62, -28,
+ -66, -46, -36, -24, -66, -32, -24, -28, 72, -88, -92, -71, -47, 13, 33, -52,
+ -128, 51, -125, -114, 1, -51, 16, -63, 12, 62, 34, 102, 117, 110, 99, 116,
+ 105, 111, 110, 67, 4, 51, 8, 3, 70, 46, 104, 101, 108, 112, 101, 114,
+ 95, 102, 117, 110, 99, 116, 105, 111, 110, 86, 67, 4, 51, 24, 3, 70,
+ 46, 104, 101, 108, 112, 101, 114, 95, 115, 101, 116, 66, 117, 102, 102, 101,
+ 114, 67, 4, 51, 40, 3, 86, 46, 104, 101, 108, 112, 101, 114, 95, 115,
+ 101, 116, 65, 108, 108, 111, 99, 97, 116, 105, 111, 110, 67, 4, 51, 56,
+ 3, 18, 114, 111, 111, 116, 67, 4, 51, 72, 3, 38, 105, 110, 99, 114,
+ 101, 109, 101, 110, 116, 67, 4, 51, 88, 3, 6, 48, 67, 4, 51, 104,
+ 3, 10, 51, 53, 67, 4, 51, 120, 3, 38, 115, 117, 109, 109, 97, 116,
+ 105, 111, 110, 116, -26, -22, -38, -126, -58, -58, -22, -38, -22, -40, -62, -24,
+ -34, -28, -122, 16, 102, 32, 7, 102, 48, 112, -103, -85, 107, 27, 122, 107,
+ 19, 75, 115, 43, -109, 27, -94, -104, 65, 28, -104, -63, 103, 6, 115, 0,
+ 1, 102, 64, 7, 67, 4, 51, -48, 24, -32, 113, 72, 115, -93, 27, 66,
+ -104, -63, 29, -104, 1, 30, 48, -56, 27, 66, -104, -127, 30, -104, 1, 30,
+ -16, 121, 107, 115, 75, -125, 123, -93, 43, 115, -93, 3, 25, 67, 11, -109,
+ 99, 52, -107, -42, 6, -57, 86, 6, 50, -12, 50, -76, -78, 2, 66, 37,
+ 20, 20, 52, 68, 48, -125, 62, 24, 34, 8, -38, 16, -61, 12, -8, -64,
+ 12, -4, 64, -48, -122, 24, 102, -128, 7, 102, 0, 10, -126, 54, -60, 48,
+ -125, 80, 48, -125, 80, 16, 52, 18, 108, 111, 110, 103, 67, 12, 51, 24,
+ 5, 51, 0, 5, 65, 27, 98, -104, 1, 41, -104, 1, 41, 8, 26, 11,
+ 51, -74, -73, 48, -70, 33, -122, 25, -104, -126, 25, -128, -126, -96, 13, 49,
+ -52, -32, 20, -52, -32, 20, 4, -115, 6, -39, 91, -99, 24, 91, -39, 16,
+ -61, 12, 82, -63, 12, 64, 65, -48, -122, 24, 102, -96, 10, 102, -96, 10,
+ -126, -58, 37, -52, 45, 15, 4, -18, 45, -51, -115, -82, 76, 110, -120, 97,
+ 6, -84, 96, 6, -96, 32, 104, 67, 12, 51, 104, 5, 51, 104, 5, 65,
+ 27, 34, 8, -37, 16, 65, -32, -122, 8, 66, 54, 4, 19, 52, 97, 51,
+ 3, 87, 16, 54, 97, 51, 3, 87, 16, 56, 97, 51, 3, 87, 16, 50,
+ 97, 51, 3, 87, -88, -62, -58, 102, -41, -26, -110, 70, 86, -26, 70, 55,
+ 37, 8, 122, -116, -64, -55, -123, -99, -75, -123, 77, 17, -120, -93, -50, -120,
+ -36, -36, 87, 25, 30, -36, -101, 28, -35, -105, 93, -104, -36, 20, 68, 105,
+ 30, -87, -62, 54, -81, -48, -120, -36, -36, -41, -101, -104, 90, -39, 24, -35,
+ -41, 28, -37, 27, -35, -36, -108, 0, 12, -6, -116, -56, -51, 125, -107, -31,
+ -63, -67, -55, -47, 125, -103, -43, -71, -115, 77, 33, -60, -128, 12, -52, 0,
+ 13, 122, -115, -56, -51, 125, -107, -31, -63, -67, -55, -47, 125, -103, -67, -55,
+ -107, -123, -115, -95, 125, -71, -123, -75, -107, 77, 17, -44, -128, 13, 42, -115,
+ -56, -51, 125, -107, -31, -63, -67, -55, -47, 125, -103, -67, -55, -107, -123, -115,
+ -95, 77, 17, -36, 0, 14, 26, -115, -56, -51, 125, -107, -31, -63, -67, -55,
+ -47, 125, -55, -107, -111, -43, -115, -107, 77, 9, -22, -96, -49, -120, -36, -36,
+ 87, 25, 30, -36, -101, 28, -35, 23, 93, 30, 92, -39, -108, -64, 14, 122,
+ -108, 64, -67, -91, -71, -47, -55, 76, 17, -14, 96, 15, 0, 0, 0, 0,
+ 121, 24, 0, 0, 92, 0, 0, 0, 51, 8, -128, 28, -60, -31, 28, 102,
+ 20, 1, 61, -120, 67, 56, -124, -61, -116, 66, -128, 7, 121, 120, 7, 115,
+ -104, 113, 12, -26, 0, 15, -19, 16, 14, -12, -128, 14, 51, 12, 66, 30,
+ -62, -63, 29, -50, -95, 28, 102, 48, 5, 61, -120, 67, 56, -124, -125, 27,
+ -52, 3, 61, -56, 67, 61, -116, 3, 61, -52, 120, -116, 116, 112, 7, 123,
+ 8, 7, 121, 72, -121, 112, 112, 7, 122, 112, 3, 118, 120, -121, 112, 32,
+ -121, 25, -52, 17, 14, -20, -112, 14, -31, 48, 15, 110, 48, 15, -29, -16,
+ 14, -16, 80, 14, 51, 16, -60, 29, -34, 33, 28, -40, 33, 29, -62, 97,
+ 30, 102, 48, -119, 59, -68, -125, 59, -48, 67, 57, -76, 3, 60, -68, -125,
+ 60, -124, 3, 59, -52, -16, 20, 118, 96, 7, 123, 104, 7, 55, 104, -121,
+ 114, 104, 7, 55, -128, -121, 112, -112, -121, 112, 96, 7, 118, 40, 7, 118,
+ -8, 5, 118, 120, -121, 119, -128, -121, 95, 8, -121, 113, 24, -121, 114, -104,
+ -121, 121, -104, -127, 44, -18, -16, 14, -18, -32, 14, -11, -64, 14, -20, 48,
+ 3, 98, -56, -95, 28, -28, -95, 28, -52, -95, 28, -28, -95, 28, -36, 97,
+ 28, -54, 33, 28, -60, -127, 29, -54, 97, 6, -42, -112, 67, 57, -56, 67,
+ 57, -104, 67, 57, -56, 67, 57, -72, -61, 56, -108, 67, 56, -120, 3, 59,
+ -108, -61, 47, -68, -125, 60, -4, -126, 59, -44, 3, 59, -80, -61, 12, -57,
+ 105, -121, 112, 88, -121, 114, 112, -125, 116, 104, 7, 120, 96, -121, 116, 24,
+ -121, 116, -96, -121, 25, -50, 83, 15, -18, 0, 15, -14, 80, 14, -28, -112,
+ 14, -29, 64, 15, -31, 32, 14, -20, 80, 14, 51, 32, 40, 29, -36, -63,
+ 30, -62, 65, 30, -46, 33, 28, -36, -127, 30, -36, -32, 28, -28, -31, 29,
+ -22, 1, 30, 102, 24, 81, 56, -80, 67, 58, -100, -125, 59, -52, 80, 36,
+ 118, 96, 7, 123, 104, 7, 55, 96, -121, 119, 120, 7, 120, -104, 81, 76,
+ -12, -112, 15, -16, 80, 14, 0, 0, 113, 32, 0, 0, 84, 0, 0, 0,
+ 70, -64, 84, 64, -12, 83, 72, 51, -35, -10, 63, -39, -128, 82, 1, -47,
+ 63, 56, 84, -80, 16, -90, -128, 20, 84, 35, 76, -120, -45, -104, -63, 82,
+ 1, -47, -113, 52, -45, -1, 76, -111, 53, 52, 18, 49, 105, -53, -30, 8,
+ -64, -124, 56, -115, 61, 100, -53, 82, 49, 62, 67, 48, -62, -125, -7, 15,
+ -115, -16, -2, 67, 35, -68, -113, -24, -72, 5, 32, 21, 16, -3, -117, -45,
+ 24, -74, -112, 20, 84, 35, 76, -120, -45, -68, -26, -112, 6, -1, 108, -45,
+ -111, -60, 18, -109, -73, 16, 12, -47, 76, -38, -12, 83, -62, 1, 68, -11,
+ 29, 73, 63, -80, 44, -114, 0, 76, -120, -45, 28, -119, 81, -124, -63, 63,
+ -41, 119, 36, 113, 11, 1, 68, 104, -112, 16, -62, -108, -42, 119, 36, -3,
+ -64, -78, 56, 2, 48, 33, 78, 99, 2, 73, 5, 68, 127, -79, 56, -64,
+ 100, 9, -103, 31, 73, 126, 68, 12, -108, 64, -4, 69, -29, 75, 20, -61,
+ 57, 76, -128, 52, 68, 100, 23, -107, 127, 16, -53, 67, 68, -65, 68, 76,
+ -38, -78, 56, 2, 48, 33, 78, 99, 5, 74, 5, 68, -1, -29, 32, -51,
+ 100, 27, 68, -126, 52, 66, 68, 48, 68, 51, 25, 67, 34, 17, -45, 70,
+ 21, 5, 17, -103, -60, 34, 81, 12, -25, 48, 1, -46, 16, -111, 85, 68,
+ -2, 65, 44, 15, 17, -3, 5, -43, 8, 19, -30, 52, -81, 29, 56, 21,
+ 16, -3, -64, -78, 56, 2, 48, 33, 78, 99, 8, -100, 31, 73, 126, 68,
+ 12, -108, 64, -4, 69, -29, 75, 20, -93, 9, 2, -59, 80, 11, 48, 57,
+ -111, 1, 28, 21, 16, -3, 72, 51, -103, 69, -28, 31, -60, -14, 16, -47,
+ 47, 17, -45, 70, 21, 5, 17, 89, -124, 35, 81, -116, 38, 8, 20, 67,
+ 45, -64, -28, 68, 54, -127, -8, -111, -28, 15, -109, 19, 1, 0, 0, 0,
+ 97, 32, 0, 0, 38, 0, 0, 0, 19, 4, 65, 44, 16, 0, 0, 0,
+ 12, 0, 0, 0, 4, -108, 64, 41, 20, 67, 57, -112, 80, 4, 20, 12,
+ 101, 36, -123, -96, 1, 100, 48, -108, -111, 20, -126, 6, 96, 68, -116, 17,
+ -128, 32, 8, -110, 96, 64, -58, 24, 1, 8, -126, 32, 8, -126, 32, 8,
+ -110, 32, 1, 0, -125, 17, 0, 25, 12, -64, 96, 68, -128, 6, 4, 48,
+ 24, 33, -84, -63, 0, 12, 70, 12, 108, 64, 0, -125, 17, 68, 25, 12,
+ -64, 96, -124, 26, -104, -63, 0, 12, 70, -92, -63, 25, 12, 0, 6, -60,
+ 0, 0, 0, 0, 9, 0, 0, 0, 91, 6, 32, 16, -123, 45, 67, 16,
+ -108, -62, -106, 65, 8, 80, 97, -53, 48, 4, -85, -80, 101, 32, 2, 81,
+ -40, 50, 20, -127, 40, 108, 25, -116, 64, 20, 0, 0, 0, 0, 0, 0,
+ 97, 32, 0, 0, 11, 0, 0, 0, 19, 4, -63, 96, 4, 64, 6, 3,
+ -128, -31, 64, 0, 2, 0, 0, 0, 70, 50, 0, -111, 1, 0, 0, 0,
+ 1, 49, 0, 0, 2, 0, 0, 0, 91, 6, 32, 16, 5, 0, 0, 0,
+ 0, 0, 0, 0, 97, 32, 0, 0, 46, 0, 0, 0, 19, 4, 68, 44,
+ 16, 0, 0, 0, 2, 0, 0, 0, 4, -108, 2, 9, 37, 0, 0, 0,
+ -61, 13, 100, -128, -103, -63, 44, 67, 48, -100, 1, 25, 100, -106, 64, 24,
+ -88, 16, -76, 96, 13, -124, -15, 4, 52, 72, -125, -63, 8, 53, 40, -125,
+ 1, -80, 52, 48, 3, 24, -36, 26, 0, 48, -36, -64, 6, 100, 0, 6,
+ -77, 12, -125, -48, 6, 24, 14, 4, 20, 0, 0, 0, -90, -126, 0, 81,
+ 4, 96, -56, 112, 89, -118, -126, 52, 67, 5, 68, -110, -113, 84, -58, -78,
+ 44, -59, 20, -7, 22, 50, 16, 72, 101, 43, 15, -46, 12, 21, 16, 73,
+ 62, 82, -7, 13, 113, 77, 23, 96, -8, 75, -28, 63, -57, 13, 44, -2,
+ 47, 68, -56, -12, 19, -125, 65, -40, 9, 33, 48, 15, 110, 36, -61, -128,
+ 48, -42, -126, 16, 23, 50, 9, 78, 51, 0, 0, 0, 1, 49, 0, 0,
+ 3, 0, 0, 0, 91, 6, 33, 112, -123, 45, -125, 17, -120, 2, 0, 0,
+ 0, 0, 0, 0, 97, 32, 0, 0, 45, 0, 0, 0, 19, 4, 68, 44,
+ 16, 0, 0, 0, 5, 0, 0, 0, 4, -108, 64, 65, -112, 80, 64, -44,
+ -108, 3, 93, 35, 0, -28, -40, 50, 7, 0, 0, 0, 51, 17, 12, 80,
+ 6, -60, 112, 3, 25, 96, 102, 48, -53, 16, 12, 108, 112, 107, -32, -20,
+ 44, -127, 48, 80, 1, 96, -63, 27, 8, 35, 6, 8, 0, 60, 109, -96,
+ 6, 103, 96, 6, 105, 48, 98, 96, 40, -128, -77, 6, 104, -32, 6, -26,
+ 6, 101, 0, -125, -31, -122, 55, 32, 3, 48, -104, 101, 24, 4, 56, -64,
+ 112, 32, 0, 0, 13, 0, 0, 0, -74, 50, 76, -52, 115, 1, -122, -65,
+ 68, -2, 115, -36, -64, -30, -1, 66, -124, 76, 63, 49, 24, -124, -79, 16,
+ 2, -13, -32, 70, 50, 12, 8, 99, 47, 3, -46, 8, -26, 66, 92, 62,
+ -99, 27, 12, 66, 92, -56, 36, 56, -51, 0, 0, 0, 1, 49, 0, 0,
+ 2, 0, 0, 0, 91, 6, -93, -112, 5, 0, 0, 0, 0, 0, 0, 0,
97, 32, 0, 0, 15, 0, 0, 0, 19, 4, 65, 44, 16, 0, 0, 0,
- 1, 0, 0, 0, 4, -108, 0, 0, -57, 1, 97, 94, 7, -125, -5, 30,
- 72, 48, 8, 7, 2, 0, 0, 0, 5, 0, 0, 0, -26, 65, 8, 78,
- 83, 25, -121, -128, 52, 6, 82, 8, 78, 83, -43, -10, 49, 0, -61, 0,
- 0, 0, 0, 0, 97, 32, 0, 0, 15, 0, 0, 0, 19, 4, 1, 113,
- -61, -68, 14, 4, -125, 17, -36, 55, 0, 24, 14, 4, 4, 0, 0, 0,
- -42, 49, 84, -64, 98, 28, 5, 32, 8, 20, 99, 31, 3, 48, 12, 0,
- 1, 49, 0, 0, 3, 0, 0, 0, 91, 6, 32, -16, -125, 45, -125, 16,
- -8, 1, 0, 0, 0, 0, 0, 0, 97, 32, 0, 0, 17, 0, 0, 0,
- 19, 4, 1, 117, -125, -72, 97, -97, 7, -126, -63, 8, 14, 12, 6, 0,
- -61, -127, 0, 0, 4, 0, 0, 0, -42, 49, 84, -64, 98, 28, 5, 32,
- 8, 20, 99, 32, 3, 48, 12, 0, 1, 49, 0, 0, 4, 0, 0, 0,
- 91, 6, 32, -16, -125, 45, 67, 16, -8, -63, -106, 97, 8, -4, 0, 0,
- 0, 0, 0, 0, 97, 32, 0, 0, 3, 0, 0, 0, 19, 4, -63, -120,
- 1, -127, 4, -112, -127, 0, 0, 0, 97, 32, 0, 0, 9, 0, 0, 0,
- 19, 4, -63, 120, 3, 55, 73, -44, -115, 17, 3, 2, 8, 22, 15, -61,
- -127, 0, 0, 0, 2, 0, 0, 0, 7, 80, 16, -51, 20, 97, 0, 0,
+ 1, 0, 0, 0, 4, -108, 0, 0, 71, 6, 64, -104, 25, -108, 1, 12,
+ -18, 12, 26, 72, 52, 8, 7, 2, 5, 0, 0, 0, 102, 66, 8, 78,
+ 83, 25, -119, -128, 52, -122, 82, 8, 78, 83, -43, 118, 50, 0, -61, 0,
+ 0, 0, 0, 0, 97, 32, 0, 0, 16, 0, 0, 0, 19, 4, 1, -111,
+ -63, 48, 51, 40, 3, 16, 12, 70, -112, -63, 25, 12, 0, -122, 3, 1,
+ 4, 0, 0, 0, 86, 50, 84, -64, 98, 36, 5, 32, 8, 20, 99, 39,
+ 3, 48, 12, 0, 1, 49, 0, 0, 3, 0, 0, 0, 91, 6, 32, 16,
+ -123, 45, -125, 16, -120, 2, 0, 0, 0, 0, 0, 0, 97, 32, 0, 0,
+ 18, 0, 0, 0, 19, 4, 1, -107, -63, 32, 50, 24, 118, 6, 102, 0,
+ -126, -63, 8, 50, 64, -125, 1, -64, 112, 32, 0, 0, 4, 0, 0, 0,
+ 86, 50, 84, -64, 98, 36, 5, 32, 8, 20, 99, 40, 3, 48, 12, 0,
+ 1, 49, 0, 0, 4, 0, 0, 0, 91, 6, 32, 16, -123, 45, 67, 16,
+ -120, -62, -106, 97, 8, 68, 1, 0, 0, 0, 0, 0, 97, 32, 0, 0,
+ 3, 0, 0, 0, 19, 4, -63, -120, 1, -95, 4, -44, -127, 0, 0, 0,
+ 97, 32, 0, 0, 9, 0, 0, 0, 19, 4, -63, 120, 3, 25, 104, 24,
+ -107, -63, 24, 49, 32, -128, 96, 49, 3, 12, 7, 2, 2, 0, 0, 0,
+ 7, 80, 16, -51, 20, 97, 0, 0, 0, 0, 0, 0, 97, 32, 0, 0,
+ 9, 0, 0, 0, 19, 4, -63, 120, 3, 25, 104, 24, -107, -63, 24, 49,
+ 32, -128, -128, 49, 3, 12, 7, 2, 2, 0, 0, 0, 7, 80, 16, -51,
+ 20, 97, 0, 0, 0, 0, 0, 0, 97, 32, 0, 0, 9, 0, 0, 0,
+ 19, 4, -63, 120, 3, 25, 104, 24, -107, -63, 24, 49, 32, -128, -96, 49,
+ 3, 12, 7, 2, 2, 0, 0, 0, 7, 80, 16, -51, 20, 97, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
-const int bitCodeLength = 2744;
+const int bitCodeLength = 3480;
#endif