mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 16:50:18 +00:00
Merge changes I22ca138e,I1d93a855,I72017b39
* changes: ConfirmationUI reference implementation Add confirmation UI support libaray ConfirmationUI HAL definition
This commit is contained in:
committed by
Android (Google) Code Review
commit
075d928e0b
27
confirmationui/1.0/Android.bp
Normal file
27
confirmationui/1.0/Android.bp
Normal 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,
|
||||
}
|
||||
|
||||
61
confirmationui/1.0/IConfirmationResultCallback.hal
Normal file
61
confirmationui/1.0/IConfirmationResultCallback.hal
Normal 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);
|
||||
};
|
||||
81
confirmationui/1.0/IConfirmationUI.hal
Normal file
81
confirmationui/1.0/IConfirmationUI.hal
Normal 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();
|
||||
};
|
||||
|
||||
43
confirmationui/1.0/default/Android.bp
Normal file
43
confirmationui/1.0/default/Android.bp
Normal 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",
|
||||
],
|
||||
}
|
||||
66
confirmationui/1.0/default/ConfirmationUI.cpp
Normal file
66
confirmationui/1.0/default/ConfirmationUI.cpp
Normal 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
|
||||
57
confirmationui/1.0/default/ConfirmationUI.h
Normal file
57
confirmationui/1.0/default/ConfirmationUI.h
Normal 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
|
||||
2
confirmationui/1.0/default/OWNERS
Normal file
2
confirmationui/1.0/default/OWNERS
Normal file
@@ -0,0 +1,2 @@
|
||||
jdanis@google.com
|
||||
swillden@google.com
|
||||
62
confirmationui/1.0/default/PlatformSpecifics.cpp
Normal file
62
confirmationui/1.0/default/PlatformSpecifics.cpp
Normal 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
|
||||
64
confirmationui/1.0/default/PlatformSpecifics.h
Normal file
64
confirmationui/1.0/default/PlatformSpecifics.h
Normal 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_
|
||||
@@ -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
|
||||
38
confirmationui/1.0/default/service.cpp
Normal file
38
confirmationui/1.0/default/service.cpp
Normal 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.
|
||||
}
|
||||
104
confirmationui/1.0/types.hal
Normal file
104
confirmationui/1.0/types.hal
Normal 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,
|
||||
};
|
||||
51
confirmationui/support/Android.bp
Normal file
51
confirmationui/support/Android.bp
Normal 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" ],
|
||||
}
|
||||
2
confirmationui/support/OWNERS
Normal file
2
confirmationui/support/OWNERS
Normal file
@@ -0,0 +1,2 @@
|
||||
jdanis@google.com
|
||||
swillden@google.com
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
111
confirmationui/support/src/cbor.cpp
Normal file
111
confirmationui/support/src/cbor.cpp
Normal 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
|
||||
39
confirmationui/support/src/confirmationui_utils.cpp
Normal file
39
confirmationui/support/src/confirmationui_utils.cpp
Normal 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
|
||||
197
confirmationui/support/test/android_cbor_test.cpp
Normal file
197
confirmationui/support/test/android_cbor_test.cpp
Normal 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);
|
||||
}
|
||||
23
confirmationui/support/test/gtest_main.cpp
Normal file
23
confirmationui/support/test/gtest_main.cpp
Normal 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();
|
||||
}
|
||||
128
confirmationui/support/test/msg_formatting_test.cpp
Normal file
128
confirmationui/support/test/msg_formatting_test.cpp
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user