Merge changes I22ca138e,I1d93a855,I72017b39

* changes:
  ConfirmationUI reference implementation
  Add confirmation UI support libaray
  ConfirmationUI HAL definition
This commit is contained in:
TreeHugger Robot
2018-01-25 14:20:42 +00:00
committed by Android (Google) Code Review
23 changed files with 2367 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
// This file is autogenerated by hidl-gen -Landroidbp.
hidl_interface {
name: "android.hardware.confirmationui@1.0",
root: "android.hardware",
vndk: {
enabled: true,
},
srcs: [
"types.hal",
"IConfirmationResultCallback.hal",
"IConfirmationUI.hal",
],
interfaces: [
"android.hardware.keymaster@4.0",
"android.hidl.base@1.0",
],
types: [
"MessageSize",
"ResponseCode",
"TestKeyBits",
"TestModeCommands",
"UIOption",
],
gen_java: false,
}

View File

@@ -0,0 +1,61 @@
/*
* 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.
*/
package android.hardware.confirmationui@1.0;
/**
* Callback interface passed to IConfirmationUI::promptUserConfirmation().
* Informs the caller about the result of the prompt operation.
*/
interface IConfirmationResultCallback {
/**
* This callback is called by the confirmation provider when it stops prompting the user.
* Iff the user has confirmed the prompted text, error is ErrorCode::OK and the
* parameters formattedMessage and confirmationToken hold the values needed to request
* a signature from keymaster.
* In all other cases formattedMessage and confirmationToken must be of length 0.
*
* @param error - OK: IFF the user has confirmed the prompt.
* - Canceled: If the user has pressed the cancel button.
* - Aborted: If IConfirmationUI::abort() was called.
* - SystemError: If an unexpected System error occurred that prevented the TUI
* from being shut down gracefully.
* @param formattedMessage holds the prompt text and extra data.
* The message is CBOR (RFC 7049) encoded and has the following format:
* CBOR_MAP{ "prompt", <promptText>, "extra", <extraData> }
* The message is a CBOR encoded map (type 5) with the keys
* "prompt" and "extra". The keys are encoded as CBOR text string
* (type 3). The value <promptText> is encoded as CBOR text string
* (type 3), and the value <extraData> is encoded as CBOR byte string
* (type 2). The map must have exactly one key value pair for each of
* the keys "prompt" and "extra". Other keys are not allowed.
* The value of "prompt" is given by the proptText argument to
* IConfirmationUI::promptUserConfirmation and must not be modified
* by the implementation.
* The value of "extra" is given by the extraData argument to
* IConfirmationUI::promptUserConfirmation and must not be modified
* or interpreted by the implementation.
*
* @param confirmationToken a 32-byte HMAC-SHA256 value, computed over
* "confirmation token" || <formattedMessage>
* i.e. the literal UTF-8 encoded string "confirmation token", without
* the "", concatenated with the formatted message as returned in the
* formattedMessage argument. The HMAC is keyed with a 256-bit secret
* which is shared with Keymaster. In test mode the test key MUST be
* used (see types.hal TestModeCommands and TestKeyBits).
*/
result(ResponseCode error, vec<uint8_t> formattedMessage, vec<uint8_t> confirmationToken);
};

View File

@@ -0,0 +1,81 @@
/*
* 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.
*/
package android.hardware.confirmationui@1.0;
import android.hardware.keymaster@4.0::HardwareAuthToken;
import IConfirmationResultCallback;
interface IConfirmationUI {
/**
* Asynchronously initiates a confirmation UI dialog prompting the user to confirm a given text.
* The TUI prompt must be implemented in such a way that a positive response indicates with
* high confidence that a user has seen the given prompt text even if the Android framework
* including the kernel was compromised.
*
* @param resultCB Implementation of IResultCallback. Used by the implementation to report
* the result of the current pending user prompt.
*
* @param promptText UTF-8 encoded string which is to be presented to the user.
*
* @param extraData A binary blob that must be included in the formatted output message as is.
* It is opaque to the implementation. Implementations must neither interpret
* nor modify the content.
*
* @param locale String specifying the locale that must be used by the TUI dialog. The string
* is an IETF BCP 47 tag.
*
* @param uiOptions A set of uiOptions manipulating how the confirmation prompt is displayed.
* Refer to UIOption in types.hal for possible options.
*
* @return error - OK: IFF the dialog was successfully started. In this case, and only in this
* case, the implementation must, eventually, call the callback to
* indicate completion.
* - OperationPending: Is returned when the confirmation provider is currently
* in use.
* - SystemError: An error occurred trying to communicate with the confirmation
* provider (e.g. trusted app).
* - UIError: The confirmation provider encountered an issue with displaying
* the prompt text to the user.
*/
promptUserConfirmation(IConfirmationResultCallback resultCB, string promptText,
vec<uint8_t> extraData, string locale, vec<UIOption> uiOptions)
generates(ResponseCode error);
/**
* DeliverSecureInput is used by the framework to deliver a secure input event to the
* confirmation provider.
*
* VTS test mode:
* This function can be used to test certain code paths non-interactively. See TestModeCommands
* in types.hal for details.
*
* @param secureInputToken An authentication token as generated by Android authentication
* providers.
*
* @return error - Ignored: Unless used for testing (See TestModeCommands).
*/
deliverSecureInputEvent(HardwareAuthToken secureInputToken)
generates(ResponseCode error);
/**
* Aborts a pending user prompt. This allows the framework to gracefully end a TUI dialog.
* If a TUI operation was pending the corresponding call back is informed with
* ErrorCode::Aborted.
*/
abort();
};

View File

@@ -0,0 +1,43 @@
//
// 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_binary {
name: "android.hardware.confirmationui@1.0-service",
init_rc: ["android.hardware.confirmationui@1.0-service.rc"],
vendor: true,
relative_install_path: "hw",
cflags: [
"-Wall",
"-Wextra",
"-Werror",
],
srcs: [
"service.cpp",
"ConfirmationUI.cpp",
"PlatformSpecifics.cpp",
],
shared_libs: [
"android.hardware.confirmationui@1.0",
"android.hardware.confirmationui-support-lib",
"android.hardware.keymaster@4.0",
"libcrypto",
"libbase",
"libhidlbase",
"libhidltransport",
"liblog",
"libutils",
],
}

View File

@@ -0,0 +1,66 @@
/*
**
** 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.
*/
#include "ConfirmationUI.h"
#include "PlatformSpecifics.h"
#include <android/hardware/confirmationui/support/cbor.h>
#include <android/hardware/confirmationui/support/confirmationui_utils.h>
#include <android/hardware/confirmationui/1.0/generic/GenericOperation.h>
#include <time.h>
namespace android {
namespace hardware {
namespace confirmationui {
namespace V1_0 {
namespace implementation {
using ::android::hardware::confirmationui::V1_0::generic::Operation;
using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
uint8_t hmacKey[32];
// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI follow.
Return<ResponseCode> ConfirmationUI::promptUserConfirmation(
const sp<IConfirmationResultCallback>& resultCB, const hidl_string& promptText,
const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
const hidl_vec<UIOption>& uiOptions) {
auto& operation = MyOperation::get();
return operation.init(resultCB, promptText, extraData, locale, uiOptions);
}
Return<ResponseCode> ConfirmationUI::deliverSecureInputEvent(
const HardwareAuthToken& secureInputToken) {
auto& operation = MyOperation::get();
return operation.deliverSecureInputEvent(secureInputToken);
}
Return<void> ConfirmationUI::abort() {
auto& operation = MyOperation::get();
operation.abort();
operation.finalize(hmacKey);
return Void();
}
} // namespace implementation
} // namespace V1_0
} // namespace confirmationui
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,57 @@
/*
**
** 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 ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_CONFIRMATIONUI_H
#define ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_CONFIRMATIONUI_H
#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
namespace android {
namespace hardware {
namespace confirmationui {
namespace V1_0 {
namespace implementation {
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::hardware::Void;
using ::android::sp;
struct ConfirmationUI : public IConfirmationUI {
// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI follow.
Return<ResponseCode> promptUserConfirmation(const sp<IConfirmationResultCallback>& resultCB,
const hidl_string& promptText,
const hidl_vec<uint8_t>& extraData,
const hidl_string& locale,
const hidl_vec<UIOption>& uiOptions) override;
Return<ResponseCode> deliverSecureInputEvent(
const ::android::hardware::keymaster::V4_0::HardwareAuthToken& secureInputToken) override;
Return<void> abort() override;
};
} // namespace implementation
} // namespace V1_0
} // namespace confirmationui
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_CONFIRMATIONUI_H

View File

@@ -0,0 +1,2 @@
jdanis@google.com
swillden@google.com

View File

@@ -0,0 +1,62 @@
/*
**
** 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.
*/
#include "PlatformSpecifics.h"
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <time.h>
namespace android {
namespace hardware {
namespace confirmationui {
namespace V1_0 {
namespace implementation {
MonotonicClockTimeStamper::TimeStamp MonotonicClockTimeStamper::now() {
timespec ts;
if (!clock_gettime(CLOCK_BOOTTIME, &ts)) {
return TimeStamp(ts.tv_sec * UINT64_C(1000) + ts.tv_nsec / UINT64_C(1000000));
} else {
return {};
}
}
support::NullOr<support::array<uint8_t, 32>> HMacImplementation::hmac256(
const uint8_t key[32], std::initializer_list<support::ByteBufferProxy> buffers) {
HMAC_CTX hmacCtx;
HMAC_CTX_init(&hmacCtx);
if (!HMAC_Init_ex(&hmacCtx, key, 32, EVP_sha256(), nullptr)) {
return {};
}
for (auto& buffer : buffers) {
if (!HMAC_Update(&hmacCtx, buffer.data(), buffer.size())) {
return {};
}
}
support::array<uint8_t, 32> result;
if (!HMAC_Final(&hmacCtx, result.data(), nullptr)) {
return {};
}
return result;
}
} // namespace implementation
} // namespace V1_0
} // namespace confirmationui
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,64 @@
/*
**
** 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 CONFIRMATIONUI_1_0_DEFAULT_PLATFORMSPECIFICS_H_
#define CONFIRMATIONUI_1_0_DEFAULT_PLATFORMSPECIFICS_H_
#include <stdint.h>
#include <time.h>
#include <android/hardware/confirmationui/1.0/IConfirmationResultCallback.h>
#include <android/hardware/confirmationui/1.0/generic/GenericOperation.h>
#include <android/hardware/confirmationui/support/confirmationui_utils.h>
namespace android {
namespace hardware {
namespace confirmationui {
namespace V1_0 {
namespace implementation {
struct MonotonicClockTimeStamper {
class TimeStamp {
public:
explicit TimeStamp(uint64_t ts) : timestamp_(ts), ok_(true) {}
TimeStamp() : timestamp_(0), ok_(false) {}
bool isOk() const { return ok_; }
operator const uint64_t() const { return timestamp_; }
private:
uint64_t timestamp_;
bool ok_;
};
static TimeStamp now();
};
class HMacImplementation {
public:
static support::NullOr<support::array<uint8_t, 32>> hmac256(
const uint8_t key[32], std::initializer_list<support::ByteBufferProxy> buffers);
};
using MyOperation = generic::Operation<sp<IConfirmationResultCallback>, MonotonicClockTimeStamper,
HMacImplementation>;
} // namespace implementation
} // namespace V1_0
} // namespace confirmationui
} // namespace hardware
} // namespace android
#endif // CONFIRMATIONUI_1_0_DEFAULT_PLATFORMSPECIFICS_H_

View File

@@ -0,0 +1,4 @@
service vendor.confirmationui-1-0 /vendor/bin/hw/android.hardware.confirmationui@1.0-service
class hal
user system
group system drmrpc

View File

@@ -0,0 +1,38 @@
/*
**
** 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.
*/
#define LOG_TAG "android.hardware.confirmationui@1.0-service"
#include <android-base/logging.h>
#include <hidl/HidlTransportSupport.h>
#include "ConfirmationUI.h"
using android::hardware::joinRpcThreadpool;
using android::hardware::confirmationui::V1_0::implementation::ConfirmationUI;
int main() {
auto confirmationui = new ConfirmationUI();
auto status = confirmationui->registerAsService();
if (status != android::OK) {
LOG(FATAL) << "Could not register service for ConfirmationIU 1.0 (" << status << ")";
}
joinRpcThreadpool();
return -1; // Should never get here.
}

View File

@@ -0,0 +1,104 @@
/*
* 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.
*/
package android.hardware.confirmationui@1.0;
/**
* UI modification options.
*/
enum UIOption : uint32_t {
/** Accessibility: Requests color inverted style. */
AccessibilityInverted = 0,
/** Accessibility: Requests magnified style. */
AccessibilityMagnified = 1,
};
/**
* Codes returned by ConfirmationUI API calls.
*/
enum ResponseCode : uint32_t {
/** API call succeeded or the user gave approval (result callback). */
OK = 0,
/** The user canceled the TUI (result callback). */
Canceled = 1,
/** IConfirmationUI::abort() was called. (result callback). */
Aborted = 2,
/** Cannot start another prompt. */
OperationPending = 3,
/** IConfirmationUI::deliverSecureInputEvent call was ingored. */
Ignored = 4,
/** An unexpected system error occured. */
SystemError = 5,
/** Returned by an unimplemented API call. */
Unimplemented = 6,
/**
* This is returned when an error is diagnosed that should have been
* caught by earlier input sanitization. Should never be seen in production.
*/
Unexpected = 7,
/** General UI error. */
UIError = 0x10000,
UIErrorMissingGlyph,
/**
* The implementation must return this error code on promptUserConfirmation if the
* resulting formatted message does not fit into MessageSize::MAX bytes. It is
* advised that the implementation formats the message upon receiving this API call to
* be able to diagnose this syndrome.
*/
UIErrorMessageTooLong,
UIErrorMalformedUTF8Encoding,
};
/**
* This defines the maximum message size. This indirectly limits the size of the prompt text
* and the extra data that can be passed to the confirmation UI. The prompt text and extra data
* must fit in to this size including CBOR header information.
*/
enum MessageSize : uint32_t { MAX = 0x1800 };
/**
* The test key is 32byte word with all bytes set to TestKeyBits::BYTE.
*/
enum TestKeyBits: uint8_t { BYTE = 0xA5 };
/**
* Test mode commands.
*
* IConfirmationUI::deliverSecureInputEvent can be used to test certain code paths.
* To that end, the caller passes an auth token that has an HMAC keyed with the test key
* (see TestKeyBits in types.hal). Implementations first check the HMAC against test key.
* If the test key produces a matching HMAC, the implementation evaluates the challenge field
* of the auth token against the values defined in TestModeCommand.
* If the command indicates that a confirmation token is to be generated the test key MUST be used
* to generate this confirmation token.
*
* See command code for individual test command descriptions.
*/
enum TestModeCommands: uint64_t {
/**
* Simulates the user pressing the OK button on the UI. If no operation is pending
* ResponseCode::Ignored must be returned. A pending operation is finalized successfully
* see IConfirmationResultCallback::result, however, the test key (see TestKeyBits) MUST be
* used to generate the confirmation token.
*/
OK_EVENT = 0,
/**
* Simulates the user pressing the CANCEL button on the UI. If no operation is pending
* Result::Ignored must be returned. A pending operation is finalized as specified in
* IConfirmationResultCallback.hal.
*/
CANCEL_EVENT = 1,
};

View File

@@ -0,0 +1,51 @@
//
// 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_library {
name: "android.hardware.confirmationui-support-lib",
vendor_available: true,
host_supported: true,
vndk: {
enabled: true,
},
srcs: [
"src/cbor.cpp",
"src/confirmationui_utils.cpp",
],
export_include_dirs: [
"include",
]
}
cc_test {
name: "android.hardware.confirmationui-support-lib-tests",
srcs: [
"test/gtest_main.cpp",
"test/android_cbor_test.cpp",
"test/msg_formatting_test.cpp",
],
static_libs: [
"libgtest",
"android.hardware.confirmationui-support-lib",
],
shared_libs: [
"android.hardware.confirmationui@1.0",
"android.hardware.keymaster@4.0",
"libhidlbase",
],
clang: true,
cflags: [ "-O0" ],
}

View File

@@ -0,0 +1,2 @@
jdanis@google.com
swillden@google.com

View File

@@ -0,0 +1,188 @@
/*
**
** 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 CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_
#define CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_
#include <android/hardware/confirmationui/1.0/types.h>
#include <android/hardware/confirmationui/support/cbor.h>
#include <android/hardware/confirmationui/support/confirmationui_utils.h>
#include <android/hardware/keymaster/4.0/types.h>
namespace android {
namespace hardware {
namespace confirmationui {
namespace V1_0 {
namespace generic {
namespace {
using namespace ::android::hardware::confirmationui::support;
using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType;
inline bool hasOption(UIOption option, const hidl_vec<UIOption>& uiOptions) {
for (auto& o : uiOptions) {
if (o == option) return true;
}
return false;
}
template <typename Callback, typename TimeStamper, typename HmacImplementation>
class Operation {
using HMacer = support::HMac<HmacImplementation>;
public:
Operation() : error_(ResponseCode::Ignored), formattedMessageLength_(0) {}
ResponseCode init(const Callback& resultCB, const hidl_string& promptText,
const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
const hidl_vec<UIOption>& uiOptions) {
(void)locale;
(void)uiOptions;
resultCB_ = resultCB;
if (error_ != ResponseCode::Ignored) return ResponseCode::OperationPending;
// TODO make copy of promptText before using it may reside in shared buffer
auto state = write(
WriteState(formattedMessageBuffer_),
map(pair(text("prompt"), text(promptText)), pair(text("extra"), bytes(extraData))));
switch (state.error_) {
case Error::OK:
break;
case Error::OUT_OF_DATA:
return ResponseCode::UIErrorMessageTooLong;
case Error::MALFORMED_UTF8:
return ResponseCode::UIErrorMalformedUTF8Encoding;
case Error::MALFORMED:
default:
return ResponseCode::Unexpected;
}
formattedMessageLength_ = state.data_ - formattedMessageBuffer_;
// setup TUI and diagnose more UI errors here.
// on success record the start time
startTime_ = TimeStamper::now();
if (!startTime_.isOk()) {
return ResponseCode::SystemError;
}
error_ = ResponseCode::OK;
return ResponseCode::OK;
}
void setHmacKey(const uint8_t (&key)[32]) { hmacKey_ = {key}; }
void abort() {
// tear down TUI here
if (isPending()) {
resultCB_->result(ResponseCode::Aborted, {}, {});
error_ = ResponseCode::Ignored;
}
}
void userCancel() {
// tear down TUI here
if (isPending()) error_ = ResponseCode::Canceled;
}
void finalize(const uint8_t key[32]) {
if (error_ == ResponseCode::Ignored) return;
resultCB_->result(error_, getMessage(), userConfirm(key));
error_ = ResponseCode::Ignored;
resultCB_ = {};
}
bool isPending() const { return error_ != ResponseCode::Ignored; }
static Operation& get() {
static Operation operation;
return operation;
}
ResponseCode deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) {
constexpr uint8_t testKeyByte = static_cast<uint8_t>(TestKeyBits::BYTE);
constexpr uint8_t testKey[32] = {testKeyByte, testKeyByte, testKeyByte, testKeyByte,
testKeyByte, testKeyByte, testKeyByte, testKeyByte,
testKeyByte, testKeyByte, testKeyByte, testKeyByte,
testKeyByte, testKeyByte, testKeyByte, testKeyByte};
auto hmac = HMacer::hmac256(testKey, "\0", bytes_cast(secureInputToken.challenge),
bytes_cast(secureInputToken.userId),
bytes_cast(secureInputToken.authenticatorId),
bytes_cast(hton(secureInputToken.authenticatorType)),
bytes_cast(hton(secureInputToken.timestamp)));
if (!hmac.isOk()) return ResponseCode::Unexpected;
if (hmac.value() == secureInputToken.mac) {
// okay so this is a test token
switch (static_cast<TestModeCommands>(secureInputToken.challenge)) {
case TestModeCommands::OK_EVENT: {
if (isPending()) {
finalize(testKey);
return ResponseCode::OK;
} else {
return ResponseCode::Ignored;
}
}
case TestModeCommands::CANCEL_EVENT: {
bool ignored = !isPending();
userCancel();
finalize(testKey);
return ignored ? ResponseCode::Ignored : ResponseCode::OK;
}
default:
return ResponseCode::Ignored;
}
}
return ResponseCode::Ignored;
}
private:
bool acceptAuthToken(const HardwareAuthToken&) { return false; }
hidl_vec<uint8_t> getMessage() {
hidl_vec<uint8_t> result;
if (error_ != ResponseCode::OK) return {};
result.setToExternal(formattedMessageBuffer_, formattedMessageLength_);
return result;
}
hidl_vec<uint8_t> userConfirm(const uint8_t key[32]) {
// tear down TUI here
if (error_ != ResponseCode::OK) return {};
confirmationTokenScratchpad_ = HMacer::hmac256(key, "confirmation token", getMessage());
if (!confirmationTokenScratchpad_.isOk()) {
error_ = ResponseCode::Unexpected;
return {};
}
hidl_vec<uint8_t> result;
result.setToExternal(confirmationTokenScratchpad_->data(),
confirmationTokenScratchpad_->size());
return result;
}
ResponseCode error_;
uint8_t formattedMessageBuffer_[uint32_t(MessageSize::MAX)];
size_t formattedMessageLength_;
NullOr<array<uint8_t, 32>> confirmationTokenScratchpad_;
Callback resultCB_;
typename TimeStamper::TimeStamp startTime_;
NullOr<array<uint8_t, 32>> hmacKey_;
};
} // namespace
} // namespace generic
} // namespace V1_0
} // namespace confirmationui
} // namespace hardware
} // namespace android
#endif // CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_

View File

@@ -0,0 +1,335 @@
/*
**
** 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 CONFIRMATIONUI_1_0_DEFAULT_CBOR_H_
#define CONFIRMATIONUI_1_0_DEFAULT_CBOR_H_
#include <stddef.h>
#include <stdint.h>
#include <type_traits>
namespace android {
namespace hardware {
namespace confirmationui {
namespace support {
template <typename In, typename Out>
Out copy(In begin, In end, Out out) {
while (begin != end) {
*out++ = *begin++;
}
return out;
}
enum class Type : uint8_t {
NUMBER = 0,
NEGATIVE = 1,
BYTE_STRING = 2,
TEXT_STRING = 3,
ARRAY = 4,
MAP = 5,
TAG = 6,
FLOAT = 7,
};
enum class Error : uint32_t {
OK = 0,
OUT_OF_DATA = 1,
MALFORMED = 2,
MALFORMED_UTF8 = 3,
};
template <typename Key, typename Value>
struct MapElement {
const Key& key_;
const Value& value_;
MapElement(const Key& key, const Value& value) : key_(key), value_(value) {}
};
template <typename... Elems>
struct Array;
template <typename Head, typename... Tail>
struct Array<Head, Tail...> {
const Head& head_;
Array<Tail...> tail_;
Array(const Head& head, const Tail&... tail) : head_(head), tail_(tail...) {}
constexpr size_t size() const { return sizeof...(Tail) + 1; };
};
template <>
struct Array<> {};
struct TextStr {};
struct ByteStr {};
template <typename T, typename Variant>
struct StringBuffer {
const T* data_;
size_t size_;
StringBuffer(const T* data, size_t size) : data_(data), size_(size) {
static_assert(sizeof(T) == 1, "elements too large");
}
const T* data() const { return data_; }
size_t size() const { return size_; }
};
/**
* Takes a char array turns it into a StringBuffer of TextStr type. The length of the resulting
* StringBuffer is size - 1, effectively stripping the 0 character from the region being considered.
* If the terminating 0 shall not be stripped use text_keep_last.
*/
template <size_t size>
StringBuffer<char, TextStr> text(const char (&str)[size]) {
if (size > 0) return StringBuffer<char, TextStr>(str, size - 1);
return StringBuffer<char, TextStr>(str, size);
}
/**
* As opposed to text(const char (&str)[size] this function does not strips the last character.
*/
template <size_t size>
StringBuffer<char, TextStr> text_keep_last(const char (&str)[size]) {
return StringBuffer<char, TextStr>(str, size);
}
template <typename T>
auto getData(const T& v) -> decltype(v.data()) {
return v.data();
}
template <typename T>
auto getData(const T& v) -> decltype(v.c_str()) {
return v.c_str();
}
template <typename T>
auto text(const T& str) -> StringBuffer<std::decay_t<decltype(*getData(str))>, TextStr> {
return StringBuffer<std::decay_t<decltype(*getData(str))>, TextStr>(getData(str), str.size());
}
inline StringBuffer<char, TextStr> text(const char* str, size_t size) {
return StringBuffer<char, TextStr>(str, size);
}
template <typename T, size_t size>
StringBuffer<T, ByteStr> bytes(const T (&str)[size]) {
return StringBuffer<T, ByteStr>(str, size);
}
template <typename T>
StringBuffer<T, ByteStr> bytes(const T* str, size_t size) {
return StringBuffer<T, ByteStr>(str, size);
}
template <typename T>
auto bytes(const T& str) -> StringBuffer<std::decay_t<decltype(*getData(str))>, ByteStr> {
return StringBuffer<std::decay_t<decltype(*getData(str))>, ByteStr>(getData(str), str.size());
}
template <typename... Elems>
struct Map;
template <typename HeadKey, typename HeadValue, typename... Tail>
struct Map<MapElement<HeadKey, HeadValue>, Tail...> {
const MapElement<HeadKey, HeadValue>& head_;
Map<Tail...> tail_;
Map(const MapElement<HeadKey, HeadValue>& head, const Tail&... tail)
: head_(head), tail_(tail...) {}
constexpr size_t size() const { return sizeof...(Tail) + 1; };
};
template <>
struct Map<> {};
template <typename... Keys, typename... Values>
Map<MapElement<Keys, Values>...> map(const MapElement<Keys, Values>&... elements) {
return Map<MapElement<Keys, Values>...>(elements...);
}
template <typename... Elements>
Array<Elements...> arr(const Elements&... elements) {
return Array<Elements...>(elements...);
}
template <typename Key, typename Value>
MapElement<Key, Value> pair(const Key& k, const Value& v) {
return MapElement<Key, Value>(k, v);
}
template <size_t size>
struct getUnsignedType;
template <>
struct getUnsignedType<sizeof(uint8_t)> {
typedef uint8_t type;
};
template <>
struct getUnsignedType<sizeof(uint16_t)> {
typedef uint16_t type;
};
template <>
struct getUnsignedType<sizeof(uint32_t)> {
typedef uint32_t type;
};
template <>
struct getUnsignedType<sizeof(uint64_t)> {
typedef uint64_t type;
};
template <size_t size>
using Unsigned = typename getUnsignedType<size>::type;
class WriteState {
public:
WriteState() : data_(nullptr), size_(0), error_(Error::OK) {}
WriteState(uint8_t* buffer, size_t size) : data_(buffer), size_(size), error_(Error::OK) {}
WriteState(uint8_t* buffer, size_t size, Error error)
: data_(buffer), size_(size), error_(error) {}
template <size_t size>
WriteState(uint8_t (&buffer)[size]) : data_(buffer), size_(size), error_(Error::OK) {}
WriteState& operator++() {
if (size_) {
++data_;
--size_;
} else {
error_ = Error::OUT_OF_DATA;
}
return *this;
}
WriteState& operator+=(size_t offset) {
if (offset > size_) {
error_ = Error::OUT_OF_DATA;
} else {
data_ += offset;
size_ -= offset;
}
return *this;
}
operator bool() const { return error_ == Error::OK; }
uint8_t* data_;
size_t size_;
Error error_;
};
WriteState writeHeader(WriteState wState, Type type, const uint64_t value);
bool checkUTF8Copy(const char* begin, const char* const end, uint8_t* out);
template <typename T>
WriteState writeNumber(WriteState wState, const T& v) {
if (!wState) return wState;
if (v >= 0) {
return writeHeader(wState, Type::NUMBER, v);
} else {
return writeHeader(wState, Type::NEGATIVE, UINT64_C(-1) - v);
}
}
inline WriteState write(const WriteState& wState, const uint8_t& v) {
return writeNumber(wState, v);
}
inline WriteState write(const WriteState& wState, const int8_t& v) {
return writeNumber(wState, v);
}
inline WriteState write(const WriteState& wState, const uint16_t& v) {
return writeNumber(wState, v);
}
inline WriteState write(const WriteState& wState, const int16_t& v) {
return writeNumber(wState, v);
}
inline WriteState write(const WriteState& wState, const uint32_t& v) {
return writeNumber(wState, v);
}
inline WriteState write(const WriteState& wState, const int32_t& v) {
return writeNumber(wState, v);
}
inline WriteState write(const WriteState& wState, const uint64_t& v) {
return writeNumber(wState, v);
}
inline WriteState write(const WriteState& wState, const int64_t& v) {
return writeNumber(wState, v);
}
template <typename T>
WriteState write(WriteState wState, const StringBuffer<T, TextStr>& v) {
wState = writeHeader(wState, Type::TEXT_STRING, v.size());
uint8_t* buffer = wState.data_;
wState += v.size();
if (!wState) return wState;
if (!checkUTF8Copy(v.data(), v.data() + v.size(), buffer)) {
wState.error_ = Error::MALFORMED_UTF8;
}
return wState;
}
template <typename T>
WriteState write(WriteState wState, const StringBuffer<T, ByteStr>& v) {
wState = writeHeader(wState, Type::BYTE_STRING, v.size());
uint8_t* buffer = wState.data_;
wState += v.size();
if (!wState) return wState;
static_assert(sizeof(*v.data()) == 1, "elements too large");
copy(v.data(), v.data() + v.size(), buffer);
return wState;
}
template <template <typename...> class Arr>
WriteState writeArrayHelper(WriteState wState, const Arr<>&) {
return wState;
}
template <template <typename...> class Arr, typename Head, typename... Tail>
WriteState writeArrayHelper(WriteState wState, const Arr<Head, Tail...>& arr) {
wState = write(wState, arr.head_);
return writeArrayHelper(wState, arr.tail_);
}
template <typename... Elems>
WriteState write(WriteState wState, const Map<Elems...>& map) {
if (!wState) return wState;
wState = writeHeader(wState, Type::MAP, map.size());
return writeArrayHelper(wState, map);
}
template <typename... Elems>
WriteState write(WriteState wState, const Array<Elems...>& arr) {
if (!wState) return wState;
wState = writeHeader(wState, Type::ARRAY, arr.size());
return writeArrayHelper(wState, arr);
}
template <typename Key, typename Value>
WriteState write(WriteState wState, const MapElement<Key, Value>& element) {
if (!wState) return wState;
wState = write(wState, element.key_);
return write(wState, element.value_);
}
template <typename Head, typename... Tail>
WriteState write(WriteState wState, const Head& head, const Tail&... tail) {
wState = write(wState, head);
return write(wState, tail...);
}
} // namespace support
} // namespace confirmationui
} // namespace hardware
} // namespace android
#endif // CONFIRMATIONUI_1_0_DEFAULT_CBOR_H_

View File

@@ -0,0 +1,213 @@
/*
**
** 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 CONFIRMATIONUI_1_0_SUPPORT_INCLUDE_CONFIRMATIONUI_UTILS_H_
#define CONFIRMATIONUI_1_0_SUPPORT_INCLUDE_CONFIRMATIONUI_UTILS_H_
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <initializer_list>
#include <type_traits>
namespace android {
namespace hardware {
namespace confirmationui {
namespace support {
/**
* 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_); }
const std::remove_reference_t<ValueT>* operator->() const { return &value_; }
std::remove_reference_t<ValueT>* operator->() { return &value_; }
private:
ValueT value_;
bool null_;
};
template <typename T, size_t elements>
class array {
using array_type = T[elements];
public:
array() : data_{} {}
array(const T (&data)[elements]) { std::copy(data, data + elements, data_); }
T* data() { return data_; }
const T* data() const { return data_; }
constexpr size_t size() const { return elements; }
operator const array_type&() const { return data_; }
T* begin() { return data_; }
T* end() { return data_ + elements; }
const T* begin() const { return data_; }
const T* end() const { return data_ + elements; }
private:
array_type data_;
};
template <typename T>
auto bytes_cast(const T& v) -> const uint8_t (&)[sizeof(T)] {
return *reinterpret_cast<const uint8_t(*)[sizeof(T)]>(&v);
}
template <typename T>
auto bytes_cast(T& v) -> uint8_t (&)[sizeof(T)] {
return *reinterpret_cast<uint8_t(*)[sizeof(T)]>(&v);
}
class ByteBufferProxy {
template <typename T>
struct has_data {
template <typename U>
static int f(const U*, const void*) {
return 0;
}
template <typename U>
static int* f(const U* u, decltype(u->data())) {
return nullptr;
}
static constexpr bool value = std::is_pointer<decltype(f((T*)nullptr, ""))>::value;
};
public:
template <typename T>
ByteBufferProxy(const T& buffer, decltype(buffer.data()) = nullptr)
: data_(reinterpret_cast<const uint8_t*>(buffer.data())), size_(buffer.size()) {
static_assert(sizeof(decltype(*buffer.data())) == 1, "elements to large");
}
// this overload kicks in for types that have .c_str() but not .data(), such as hidl_string.
// std::string has both so we need to explicitly disable this overload if .data() is present.
template <typename T>
ByteBufferProxy(const T& buffer,
std::enable_if_t<!has_data<T>::value, decltype(buffer.c_str())> = nullptr)
: data_(reinterpret_cast<const uint8_t*>(buffer.c_str())), size_(buffer.size()) {
static_assert(sizeof(decltype(*buffer.c_str())) == 1, "elements to large");
}
template <size_t size>
ByteBufferProxy(const char (&buffer)[size])
: data_(reinterpret_cast<const uint8_t*>(buffer)), size_(size - 1) {
static_assert(size > 0, "even an empty string must be 0-terminated");
}
template <size_t size>
ByteBufferProxy(const uint8_t (&buffer)[size]) : data_(buffer), size_(size) {}
ByteBufferProxy() : data_(nullptr), size_(0) {}
const uint8_t* data() const { return data_; }
size_t size() const { return size_; }
const uint8_t* begin() const { return data_; }
const uint8_t* end() const { return data_ + size_; }
private:
const uint8_t* data_;
size_t size_;
};
/**
* Implementer are expected to provide an implementation with the following prototype:
* static NullOr<array<uint8_t, 32>> hmac256(const uint8_t key[32],
* std::initializer_list<ByteBufferProxy> buffers);
*/
template <typename Impl>
class HMac {
public:
template <typename... Data>
static NullOr<array<uint8_t, 32>> hmac256(const uint8_t key[32], const Data&... data) {
return Impl::hmac256(key, {data...});
}
};
bool operator==(const ByteBufferProxy& lhs, const ByteBufferProxy& rhs);
template <typename IntType, uint32_t byteOrder>
struct choose_hton;
template <typename IntType>
struct choose_hton<IntType, __ORDER_LITTLE_ENDIAN__> {
inline static IntType hton(const IntType& value) {
IntType result = {};
const unsigned char* inbytes = reinterpret_cast<const unsigned char*>(&value);
unsigned char* outbytes = reinterpret_cast<unsigned char*>(&result);
for (int i = sizeof(IntType) - 1; i >= 0; --i) {
*(outbytes++) = inbytes[i];
}
return result;
}
};
template <typename IntType>
struct choose_hton<IntType, __ORDER_BIG_ENDIAN__> {
inline static IntType hton(const IntType& value) { return value; }
};
template <typename IntType>
inline IntType hton(const IntType& value) {
return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
}
template <typename IntType>
inline IntType ntoh(const IntType& value) {
// same operation as hton
return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
}
} // namespace support
} // namespace confirmationui
} // namespace hardware
} // namespace android
#endif // CONFIRMATIONUI_1_0_SUPPORT_INCLUDE_CONFIRMATIONUI_UTILS_H_

View File

@@ -0,0 +1,471 @@
/*
**
** 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 CONFIRMATIONUI_SUPPORT_INCLUDE_ANDROID_HARDWARE_CONFIRMATIONUI_SUPPORT_MSG_FORMATTING_H_
#define CONFIRMATIONUI_SUPPORT_INCLUDE_ANDROID_HARDWARE_CONFIRMATIONUI_SUPPORT_MSG_FORMATTING_H_
#include <android/hardware/confirmationui/1.0/types.h>
#include <android/hardware/keymaster/4.0/types.h>
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <tuple>
#include <type_traits>
#include <android/hardware/confirmationui/support/confirmationui_utils.h>
namespace android {
namespace hardware {
namespace confirmationui {
namespace support {
template <size_t... I>
class IntegerSequence {};
namespace integer_sequence {
template <typename Lhs, typename Rhs>
struct conc {};
template <size_t... ILhs, size_t... IRhs>
struct conc<IntegerSequence<ILhs...>, IntegerSequence<IRhs...>> {
using type = IntegerSequence<ILhs..., IRhs...>;
};
template <typename Lhs, typename Rhs>
using conc_t = typename conc<Lhs, Rhs>::type;
template <size_t... n>
struct make {};
template <size_t n>
struct make<n> {
using type = conc_t<typename make<n - 1>::type, IntegerSequence<n - 1>>;
};
template <size_t start, size_t n>
struct make<start, n> {
using type = conc_t<typename make<start, n - 1>::type, IntegerSequence<start + n - 1>>;
};
template <size_t start>
struct make<start, start> {
using type = IntegerSequence<start>;
};
template <>
struct make<0> {
using type = IntegerSequence<>;
};
template <size_t... n>
using make_t = typename make<n...>::type;
} // namespace integer_sequence
template <size_t... idx, typename... T>
std::tuple<std::remove_reference_t<T>&&...> tuple_move_helper(IntegerSequence<idx...>,
std::tuple<T...>&& t) {
return {std::move(std::get<idx>(t))...};
}
template <typename... T>
std::tuple<std::remove_reference_t<T>&&...> tuple_move(std::tuple<T...>&& t) {
return tuple_move_helper(integer_sequence::make_t<sizeof...(T)>(), std::move(t));
}
template <typename... T>
std::tuple<std::remove_reference_t<T>&&...> tuple_move(std::tuple<T...>& t) {
return tuple_move_helper(integer_sequence::make_t<sizeof...(T)>(), std::move(t));
}
using ::android::hardware::confirmationui::V1_0::ResponseCode;
using ::android::hardware::confirmationui::V1_0::UIOption;
using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
template <typename... fields>
class Message {};
enum class Command : uint32_t {
PromptUserConfirmation,
DeliverSecureInputEvent,
Abort,
};
template <Command cmd>
struct Cmd {};
#define DECLARE_COMMAND(cmd) using cmd##_t = Cmd<Command::cmd>
DECLARE_COMMAND(PromptUserConfirmation);
DECLARE_COMMAND(DeliverSecureInputEvent);
DECLARE_COMMAND(Abort);
using PromptUserConfirmationMsg = Message<PromptUserConfirmation_t, hidl_string, hidl_vec<uint8_t>,
hidl_string, hidl_vec<UIOption>>;
using PromptUserConfirmationResponse = Message<ResponseCode>;
using DeliverSecureInputEventMsg = Message<DeliverSecureInputEvent_t, HardwareAuthToken>;
using DeliverSecureInputEventRespose = Message<ResponseCode>;
using AbortMsg = Message<Abort_t>;
using ResultMsg = Message<ResponseCode, hidl_vec<uint8_t>, hidl_vec<uint8_t>>;
template <typename T>
struct StreamState {
using ptr_t = volatile T*;
volatile T* pos_;
size_t bytes_left_;
bool good_;
template <size_t size>
StreamState(T (&buffer)[size]) : pos_(buffer), bytes_left_(size), good_(size > 0) {}
StreamState(T* buffer, size_t size) : pos_(buffer), bytes_left_(size), good_(size > 0) {}
StreamState() : pos_(nullptr), bytes_left_(0), good_(false) {}
StreamState& operator++() {
if (good_ && bytes_left_) {
++pos_;
--bytes_left_;
} else {
good_ = false;
}
return *this;
}
StreamState& operator+=(size_t offset) {
if (!good_ || offset > bytes_left_) {
good_ = false;
} else {
pos_ += offset;
bytes_left_ -= offset;
}
return *this;
}
operator bool() const { return good_; }
volatile T* pos() const { return pos_; };
};
using WriteStream = StreamState<uint8_t>;
using ReadStream = StreamState<const uint8_t>;
inline void zero(volatile uint8_t* begin, const volatile uint8_t* end) {
while (begin != end) {
*begin++ = 0xaa;
}
}
inline void zero(const volatile uint8_t*, const volatile uint8_t*) {}
// This odd alignment function aligns the stream position to a 4byte and never 8byte boundary
// It is to accommodate the 4 byte size field which is then followed by 8byte alligned data.
template <typename T>
StreamState<T> unalign(StreamState<T> s) {
uint8_t unalignment = uintptr_t(s.pos_) & 0x3;
auto pos = s.pos_;
if (unalignment) {
s += 4 - unalignment;
}
// now s.pos_ is aligned on a 4byte boundary
if ((uintptr_t(s.pos_) & 0x4) == 0) {
// if we are 8byte aligned add 4
s += 4;
}
// zero out the gaps when writing
zero(pos, s.pos_);
return s;
}
inline WriteStream write(WriteStream out, const uint8_t* buffer, size_t size) {
auto pos = out.pos();
uint32_t v = size;
out += 4 + size;
if (out) {
if (size != v) {
out.good_ = false;
return out;
}
auto& s = bytes_cast(v);
pos = std::copy(s, s + 4, pos);
std::copy(buffer, buffer + size, pos);
}
return out;
}
template <size_t size>
WriteStream write(WriteStream out, const uint8_t (&v)[size]) {
return write(out, v, size);
}
inline std::tuple<ReadStream, ReadStream::ptr_t, size_t> read(ReadStream in) {
auto pos = in.pos();
in += 4;
if (!in) return {in, nullptr, 0};
uint32_t size;
std::copy(pos, pos + 4, bytes_cast(size));
pos = in.pos();
in += size;
if (!in) return {in, nullptr, 0};
return {in, pos, size};
}
template <typename T>
std::tuple<ReadStream, T> readSimpleType(ReadStream in) {
T result;
ReadStream::ptr_t pos = nullptr;
size_t read_size = 0;
std::tie(in, pos, read_size) = read(in);
if (!in || read_size != sizeof(T)) {
in.good_ = false;
return {in, {}};
}
std::copy(pos, pos + sizeof(T), bytes_cast(result));
return {in, std::move(result)};
}
template <typename T>
std::tuple<ReadStream, hidl_vec<T>> readSimpleHidlVecInPlace(ReadStream in) {
std::tuple<ReadStream, hidl_vec<T>> result;
ReadStream::ptr_t pos = nullptr;
size_t read_size = 0;
std::tie(std::get<0>(result), pos, read_size) = read(in);
if (!std::get<0>(result) || read_size % sizeof(T)) {
std::get<0>(result).good_ = false;
return result;
}
std::get<1>(result).setToExternal(reinterpret_cast<T*>(const_cast<uint8_t*>(pos)),
read_size / sizeof(T));
return result;
}
template <typename T>
WriteStream writeSimpleHidlVec(WriteStream out, const hidl_vec<T>& vec) {
return write(out, reinterpret_cast<const uint8_t*>(vec.data()), vec.size() * sizeof(T));
}
// HardwareAuthToken
constexpr size_t hatSizeNoMac() {
HardwareAuthToken* hat = nullptr;
return sizeof hat->challenge + sizeof hat->userId + sizeof hat->authenticatorId +
sizeof hat->authenticatorType + sizeof hat->timestamp;
}
template <typename T>
inline volatile const uint8_t* copyField(T& field, volatile const uint8_t*(&pos)) {
auto& s = bytes_cast(field);
std::copy(pos, pos + sizeof(T), s);
return pos + sizeof(T);
}
inline std::tuple<ReadStream, HardwareAuthToken> read(Message<HardwareAuthToken>, ReadStream in_) {
std::tuple<ReadStream, HardwareAuthToken> result;
ReadStream& in = std::get<0>(result) = in_;
auto& hat = std::get<1>(result);
constexpr size_t hatSize = hatSizeNoMac();
ReadStream::ptr_t pos = nullptr;
size_t read_size = 0;
std::tie(in, pos, read_size) = read(in);
if (!in || read_size != hatSize) {
in.good_ = false;
return result;
}
pos = copyField(hat.challenge, pos);
pos = copyField(hat.userId, pos);
pos = copyField(hat.authenticatorId, pos);
pos = copyField(hat.authenticatorType, pos);
pos = copyField(hat.timestamp, pos);
std::tie(in, hat.mac) = readSimpleHidlVecInPlace<uint8_t>(in);
return result;
}
template <typename T>
inline volatile uint8_t* copyField(const T& field, volatile uint8_t*(&pos)) {
auto& s = bytes_cast(field);
return std::copy(s, &s[sizeof(T)], pos);
}
inline WriteStream write(WriteStream out, const HardwareAuthToken& v) {
auto pos = out.pos();
uint32_t size_field = hatSizeNoMac();
out += 4 + size_field;
if (!out) return out;
pos = copyField(size_field, pos);
pos = copyField(v.challenge, pos);
pos = copyField(v.userId, pos);
pos = copyField(v.authenticatorId, pos);
pos = copyField(v.authenticatorType, pos);
pos = copyField(v.timestamp, pos);
return writeSimpleHidlVec(out, v.mac);
}
// ResponseCode
inline std::tuple<ReadStream, ResponseCode> read(Message<ResponseCode>, ReadStream in) {
return readSimpleType<ResponseCode>(in);
}
inline WriteStream write(WriteStream out, const ResponseCode& v) {
return write(out, bytes_cast(v));
}
// hidl_vec<uint8_t>
inline std::tuple<ReadStream, hidl_vec<uint8_t>> read(Message<hidl_vec<uint8_t>>, ReadStream in) {
return readSimpleHidlVecInPlace<uint8_t>(in);
}
inline WriteStream write(WriteStream out, const hidl_vec<uint8_t>& v) {
return writeSimpleHidlVec(out, v);
}
// hidl_vec<UIOption>
inline std::tuple<ReadStream, hidl_vec<UIOption>> read(Message<hidl_vec<UIOption>>, ReadStream in) {
in = unalign(in);
return readSimpleHidlVecInPlace<UIOption>(in);
}
inline WriteStream write(WriteStream out, const hidl_vec<UIOption>& v) {
out = unalign(out);
return writeSimpleHidlVec(out, v);
}
// hidl_string
inline std::tuple<ReadStream, hidl_string> read(Message<hidl_string>, ReadStream in) {
std::tuple<ReadStream, hidl_string> result;
ReadStream& in_ = std::get<0>(result);
hidl_string& result_ = std::get<1>(result);
ReadStream::ptr_t pos = nullptr;
size_t read_size = 0;
std::tie(in_, pos, read_size) = read(in);
auto terminating_zero = in_.pos();
++in_; // skip the terminating zero. Does nothing if the stream was already bad
if (!in_) return result;
if (*terminating_zero) {
in_.good_ = false;
return result;
}
result_.setToExternal(reinterpret_cast<const char*>(const_cast<const uint8_t*>(pos)),
read_size);
return result;
}
inline WriteStream write(WriteStream out, const hidl_string& v) {
out = write(out, reinterpret_cast<const uint8_t*>(v.c_str()), v.size());
auto terminating_zero = out.pos();
++out;
if (out) {
*terminating_zero = 0;
}
return out;
}
inline WriteStream write(WriteStream out, Command cmd) {
volatile Command* pos = reinterpret_cast<volatile Command*>(out.pos_);
out += sizeof(Command);
if (out) {
*pos = cmd;
}
return out;
}
template <Command cmd>
WriteStream write(WriteStream out, Cmd<cmd>) {
return write(out, cmd);
}
inline std::tuple<ReadStream, bool> read(ReadStream in, Command cmd) {
volatile const Command* pos = reinterpret_cast<volatile const Command*>(in.pos_);
in += sizeof(Command);
if (!in) return {in, false};
return {in, *pos == cmd};
}
template <Command cmd>
std::tuple<ReadStream, bool> read(Message<Cmd<cmd>>, ReadStream in) {
return read(in, cmd);
}
inline WriteStream write(Message<>, WriteStream out) {
return out;
}
template <typename Head, typename... Tail>
WriteStream write(Message<Head, Tail...>, WriteStream out, const Head& head, const Tail&... tail) {
out = write(out, head);
return write(Message<Tail...>(), out, tail...);
}
template <Command cmd, typename... Tail>
WriteStream write(Message<Cmd<cmd>, Tail...>, WriteStream out, const Tail&... tail) {
out = write(out, cmd);
return write(Message<Tail...>(), out, tail...);
}
template <Command cmd, typename HEAD, typename... Tail>
std::tuple<ReadStream, bool, HEAD, Tail...> read(Message<Cmd<cmd>, HEAD, Tail...>, ReadStream in) {
bool command_matches;
std::tie(in, command_matches) = read(in, cmd);
if (!command_matches) return {in, false, HEAD(), Tail()...};
return {in, true,
[&]() -> HEAD {
HEAD result;
std::tie(in, result) = read(Message<HEAD>(), in);
return result;
}(),
[&]() -> Tail {
Tail result;
std::tie(in, result) = read(Message<Tail>(), in);
return result;
}()...};
}
template <typename... Msg>
std::tuple<ReadStream, Msg...> read(Message<Msg...>, ReadStream in) {
return {in, [&in]() -> Msg {
Msg result;
std::tie(in, result) = read(Message<Msg>(), in);
return result;
}()...};
}
template <typename T>
struct msg2tuple {};
template <typename... T>
struct msg2tuple<Message<T...>> {
using type = std::tuple<T...>;
};
template <Command cmd, typename... T>
struct msg2tuple<Message<Cmd<cmd>, T...>> {
using type = std::tuple<T...>;
};
template <typename T>
using msg2tuple_t = typename msg2tuple<T>::type;
template <size_t... idx, typename HEAD, typename... T>
std::tuple<T&&...> tuple_tail(IntegerSequence<idx...>, std::tuple<HEAD, T...>&& t) {
return {std::move(std::get<idx>(t))...};
}
template <size_t... idx, typename HEAD, typename... T>
std::tuple<const T&...> tuple_tail(IntegerSequence<idx...>, const std::tuple<HEAD, T...>& t) {
return {std::get<idx>(t)...};
}
template <typename HEAD, typename... Tail>
std::tuple<Tail&&...> tuple_tail(std::tuple<HEAD, Tail...>&& t) {
return tuple_tail(integer_sequence::make_t<1, sizeof...(Tail)>(), std::move(t));
}
template <typename HEAD, typename... Tail>
std::tuple<const Tail&...> tuple_tail(const std::tuple<HEAD, Tail...>& t) {
return tuple_tail(integer_sequence::make_t<1, sizeof...(Tail)>(), t);
}
} // namespace support
} // namespace confirmationui
} // namespace hardware
} // namespace android
#endif // CONFIRMATIONUI_SUPPORT_INCLUDE_ANDROID_HARDWARE_CONFIRMATIONUI_SUPPORT_MSG_FORMATTING_H_

View File

@@ -0,0 +1,111 @@
/*
**
** 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.
*/
#include <android/hardware/confirmationui/support/cbor.h>
namespace android {
namespace hardware {
namespace confirmationui {
namespace support {
namespace {
inline uint8_t getByte(const uint64_t& v, const uint8_t index) {
return v >> (index * 8);
}
WriteState writeBytes(WriteState state, uint64_t value, uint8_t size) {
auto pos = state.data_;
if (!(state += size)) return state;
switch (size) {
case 8:
*pos++ = getByte(value, 7);
*pos++ = getByte(value, 6);
*pos++ = getByte(value, 5);
*pos++ = getByte(value, 4);
case 4:
*pos++ = getByte(value, 3);
*pos++ = getByte(value, 2);
case 2:
*pos++ = getByte(value, 1);
case 1:
*pos++ = value;
break;
default:
state.error_ = Error::MALFORMED;
}
return state;
}
} // anonymous namespace
WriteState writeHeader(WriteState wState, Type type, const uint64_t value) {
if (!wState) return wState;
uint8_t& header = *wState.data_;
if (!++wState) return wState;
header = static_cast<uint8_t>(type) << 5;
if (value < 24) {
header |= static_cast<uint8_t>(value);
} else if (value < 0x100) {
header |= 24;
wState = writeBytes(wState, value, 1);
} else if (value < 0x10000) {
header |= 25;
wState = writeBytes(wState, value, 2);
} else if (value < 0x100000000) {
header |= 26;
wState = writeBytes(wState, value, 4);
} else {
header |= 27;
wState = writeBytes(wState, value, 8);
}
return wState;
}
bool checkUTF8Copy(const char* begin, const char* const end, uint8_t* out) {
uint32_t multi_byte_length = 0;
while (begin != end) {
if (multi_byte_length) {
// parsing multi byte character - must start with 10xxxxxx
--multi_byte_length;
if ((*begin & 0xc0) != 0x80) return false;
} else if (!((*begin) & 0x80)) {
// 7bit character -> nothing to be done
} else {
// msb is set and we were not parsing a multi byte character
// so this must be a header byte
char c = *begin << 1;
while (c & 0x80) {
++multi_byte_length;
c <<= 1;
}
// headers of the form 10xxxxxx are not allowed
if (multi_byte_length < 1) return false;
// chars longer than 4 bytes are not allowed (multi_byte_length does not count the
// header thus > 3
if (multi_byte_length > 3) return false;
}
if (out) *out++ = *reinterpret_cast<const uint8_t*>(begin++);
}
// if the string ends in the middle of a multi byte char it is invalid
if (multi_byte_length) return false;
return true;
}
} // namespace support
} // namespace confirmationui
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,39 @@
/*
**
** 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.
*/
#include <android/hardware/confirmationui/support/confirmationui_utils.h>
namespace android {
namespace hardware {
namespace confirmationui {
namespace support {
bool operator==(const ByteBufferProxy& lhs, const ByteBufferProxy& rhs) {
if (lhs.size() == rhs.size()) {
auto lhsi = lhs.begin();
auto rhsi = rhs.begin();
while (lhsi != lhs.end()) {
if (*lhsi++ != *rhsi++) return false;
}
}
return true;
}
} // namespace support
} // namespace confirmationui
} // namespace hardware
} // namespace android

View File

@@ -0,0 +1,197 @@
/*
**
** 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.
*/
#include <android/hardware/confirmationui/support/cbor.h>
#include <cstddef>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <gtest/gtest.h>
using namespace android::hardware::confirmationui::support;
uint8_t testVector[] = {
0xA4, 0x63, 0x6B, 0x65, 0x79, 0x65, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x63, 0x6B, 0x65, 0x79, 0x4D,
0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x31, 0x30, 0x00, 0x04, 0x07, 0x1B,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x07, 0x1A, 0xFD, 0x49, 0x8C, 0xFF,
0xFF, 0x82, 0x69, 0xE2, 0x99, 0xA8, 0xE2, 0x9A, 0x96, 0xE2, 0xB6, 0x96, 0x59, 0x01, 0x91, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x00,
};
// 400 'a's and a '\0'
constexpr char fourHundredAs[] =
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaa";
WriteState writeTest(WriteState state) {
return write(state, //
map( //
pair(text("key"), text("value")), //
pair(text("key"), bytes("100101010010")), //
pair(4, 7), //
pair((UINT64_C(1) << 62), INT64_C(-2000000000000000)) //
), //
arr(text("♨⚖ⶖ"), bytes(fourHundredAs)));
}
TEST(Cbor, FeatureTest) {
uint8_t buffer[0x1000];
WriteState state(buffer);
state = writeTest(state);
ASSERT_EQ(sizeof(testVector), size_t(state.data_ - buffer));
ASSERT_EQ(Error::OK, state.error_);
ASSERT_EQ(0, memcmp(buffer, testVector, sizeof(testVector)));
}
// Test if in all write cases an out of data error is correctly propagated and we don't
// write beyond the end of the buffer.
TEST(Cbor, BufferTooShort) {
uint8_t buffer[0x1000];
for (size_t s = 1; s < sizeof(testVector); ++s) {
memset(buffer, 0x22, 0x1000); // 0x22 is not in the testVector
WriteState state(buffer, s);
state = writeTest(state);
for (size_t t = s; t < 0x1000; ++t) {
ASSERT_EQ(0x22, buffer[t]); // check if a canary has been killed
}
ASSERT_EQ(Error::OUT_OF_DATA, state.error_);
}
}
TEST(Cbor, MalformedUTF8Test_Stray) {
uint8_t buffer[20];
WriteState state(buffer);
char malformed[] = {char(0x80), 0};
state = write(state, text(malformed));
ASSERT_EQ(Error::MALFORMED_UTF8, state.error_);
}
TEST(Cbor, MalformendUTF8Test_StringEndsMidMultiByte) {
uint8_t buffer[20];
WriteState state(buffer);
char malformed[] = {char(0xc0), 0};
state = write(state, text(malformed));
ASSERT_EQ(Error::MALFORMED_UTF8, state.error_);
}
TEST(Cbor, UTF8Test_TwoBytes) {
uint8_t buffer[20];
WriteState state(buffer);
char neat[] = {char(0xc3), char(0x82), 0};
state = write(state, text(neat));
ASSERT_EQ(Error::OK, state.error_);
}
TEST(Cbor, UTF8Test_ThreeBytes) {
uint8_t buffer[20];
WriteState state(buffer);
char neat[] = {char(0xe3), char(0x82), char(0x82), 0};
state = write(state, text(neat));
ASSERT_EQ(Error::OK, state.error_);
}
TEST(Cbor, UTF8Test_FourBytes) {
uint8_t buffer[20];
WriteState state(buffer);
char neat[] = {char(0xf3), char(0x82), char(0x82), char(0x82), 0};
state = write(state, text(neat));
ASSERT_EQ(Error::OK, state.error_);
}
TEST(Cbor, MalformendUTF8Test_CharacterTooLong) {
uint8_t buffer[20];
WriteState state(buffer);
char malformed[] = {char(0xf8), char(0x82), char(0x82), char(0x82), char(0x82), 0};
state = write(state, text(malformed));
ASSERT_EQ(Error::MALFORMED_UTF8, state.error_);
}
TEST(Cbor, MalformendUTF8Test_StringEndsMidMultiByte2) {
uint8_t buffer[20];
WriteState state(buffer);
char malformed[] = {char(0xc0), char(0x82), char(0x83), 0};
state = write(state, text(malformed));
ASSERT_EQ(Error::MALFORMED_UTF8, state.error_);
}
TEST(Cbor, MinimalViableHeaderSizeTest) {
uint8_t buffer[20];
WriteState state(buffer);
state = writeHeader(state, Type::NUMBER, 23);
ASSERT_EQ(state.data_ - buffer, 1);
state = WriteState(buffer);
state = writeHeader(state, Type::NUMBER, 24);
ASSERT_EQ(state.data_ - buffer, 2);
state = WriteState(buffer);
state = writeHeader(state, Type::NUMBER, 0xff);
ASSERT_EQ(state.data_ - buffer, 2);
state = WriteState(buffer);
state = writeHeader(state, Type::NUMBER, 0x100);
ASSERT_EQ(state.data_ - buffer, 3);
state = WriteState(buffer);
state = writeHeader(state, Type::NUMBER, 0xffff);
ASSERT_EQ(state.data_ - buffer, 3);
state = WriteState(buffer);
state = writeHeader(state, Type::NUMBER, 0x10000);
ASSERT_EQ(state.data_ - buffer, 5);
state = WriteState(buffer);
state = writeHeader(state, Type::NUMBER, 0xffffffff);
ASSERT_EQ(state.data_ - buffer, 5);
state = WriteState(buffer);
state = writeHeader(state, Type::NUMBER, 0x100000000);
ASSERT_EQ(state.data_ - buffer, 9);
state = WriteState(buffer);
state = writeHeader(state, Type::NUMBER, 0xffffffffffffffff);
ASSERT_EQ(state.data_ - buffer, 9);
}

View File

@@ -0,0 +1,23 @@
/*
**
** 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.
*/
#include <gtest/gtest.h>
int main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,128 @@
/*
**
** 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.
*/
#include <stddef.h>
#include <stdint.h>
#include <iomanip>
#include <iostream>
#include <string>
#include <android/hardware/confirmationui/support/msg_formatting.h>
#include <gtest/gtest.h>
using android::hardware::confirmationui::support::Message;
using android::hardware::confirmationui::support::WriteStream;
using android::hardware::confirmationui::support::ReadStream;
using android::hardware::confirmationui::support::PromptUserConfirmationMsg;
using android::hardware::confirmationui::support::write;
using ::android::hardware::confirmationui::V1_0::UIOption;
using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
#ifdef DEBUG_MSG_FORMATTING
namespace {
char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
std::ostream& hexdump(std::ostream& out, const uint8_t* data, size_t size) {
for (size_t i = 0; i < size; ++i) {
uint8_t byte = data[i];
out << (nibble2hex[0x0F & (byte >> 4)]);
out << (nibble2hex[0x0F & byte]);
switch (i & 0xf) {
case 0xf:
out << "\n";
break;
case 7:
out << " ";
break;
default:
out << " ";
break;
}
}
return out;
}
} // namespace
#endif
TEST(MsgFormattingTest, FeatureTest) {
uint8_t buffer[0x1000];
WriteStream out(buffer);
out = unalign(out);
out += 4;
auto begin = out.pos();
out = write(
PromptUserConfirmationMsg(), out, hidl_string("Do you?"),
hidl_vec<uint8_t>{0x01, 0x02, 0x03}, hidl_string("en"),
hidl_vec<UIOption>{UIOption::AccessibilityInverted, UIOption::AccessibilityMagnified});
ReadStream in(buffer);
in = unalign(in);
in += 4;
hidl_string prompt;
hidl_vec<uint8_t> extra;
hidl_string locale;
hidl_vec<UIOption> uiOpts;
bool command_matches;
std::tie(in, command_matches, prompt, extra, locale, uiOpts) =
read(PromptUserConfirmationMsg(), in);
ASSERT_TRUE(in);
ASSERT_TRUE(command_matches);
ASSERT_EQ(hidl_string("Do you?"), prompt);
ASSERT_EQ((hidl_vec<uint8_t>{0x01, 0x02, 0x03}), extra);
ASSERT_EQ(hidl_string("en"), locale);
ASSERT_EQ(
(hidl_vec<UIOption>{UIOption::AccessibilityInverted, UIOption::AccessibilityMagnified}),
uiOpts);
#ifdef DEBUG_MSG_FORMATTING
hexdump(std::cout, buffer, 100) << std::endl;
#endif
// The following assertions check that the hidl_[vec|string] types are in fact read in place,
// and no copying occurs. Copying results in heap allocation which we intend to avoid.
ASSERT_EQ(8, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(prompt.c_str())) - begin);
ASSERT_EQ(20, extra.data() - begin);
ASSERT_EQ(27, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(locale.c_str())) - begin);
ASSERT_EQ(40, reinterpret_cast<uint8_t*>(uiOpts.data()) - begin);
}
TEST(MsgFormattingTest, HardwareAuthTokenTest) {
uint8_t buffer[0x1000];
HardwareAuthToken expected, actual;
expected.authenticatorId = 0xa1a3a4a5a6a7a8;
expected.authenticatorType = HardwareAuthenticatorType::NONE;
expected.challenge = 0xb1b2b3b4b5b6b7b8;
expected.userId = 0x1122334455667788;
expected.timestamp = 0xf1f2f3f4f5f6f7f8;
expected.mac =
hidl_vec<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32};
WriteStream out(buffer);
out = write(Message<HardwareAuthToken>(), out, expected);
ReadStream in(buffer);
std::tie(in, actual) = read(Message<HardwareAuthToken>(), in);
ASSERT_EQ(expected, actual);
}