diff --git a/HAL/keymaster/4.1/CommonUtils.cpp b/HAL/keymaster/4.1/CommonUtils.cpp index 090c8d0d..476fe68e 100644 --- a/HAL/keymaster/4.1/CommonUtils.cpp +++ b/HAL/keymaster/4.1/CommonUtils.cpp @@ -212,7 +212,7 @@ pubModulus) { return legacy_enum_conversion(TranslateLastOpenSslError()); } - UniquePtr rsa_key(EVP_PKEY_get1_RSA(pkey)); + UniquePtr rsa_key(EVP_PKEY_get1_RSA(pkey)); if(!rsa_key.get()) { return legacy_enum_conversion(TranslateLastOpenSslError()); } diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp index 67f8f527..87daddbc 100644 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -40,6 +40,10 @@ #define JAVACARD_KEYMASTER_NAME "JavacardKeymaster4.1Device v1.0" #define JAVACARD_KEYMASTER_AUTHOR "Android Open Source Project" +#define PROP_BUILD_QEMU "ro.kernel.qemu" +#define PROP_BUILD_FINGERPRINT "ro.build.fingerprint" +// Cuttlefish build fingerprint substring. +#define CUTTLEFISH_FINGERPRINT_SS "aosp_cf_" #define APDU_CLS 0x80 #define APDU_P1 0x40 @@ -125,9 +129,20 @@ enum ExtendedErrors { }; static inline std::unique_ptr& getTransportFactoryInstance() { + bool isEmulator = false; if(pTransportFactory == nullptr) { + // Check if the current build is for emulator or device. + isEmulator = android::base::GetBoolProperty(PROP_BUILD_QEMU, false); + if (!isEmulator) { + std::string fingerprint = android::base::GetProperty(PROP_BUILD_FINGERPRINT, ""); + if (!fingerprint.empty()) { + if (fingerprint.find(CUTTLEFISH_FINGERPRINT_SS, 0)) { + isEmulator = true; + } + } + } pTransportFactory = std::unique_ptr(new se_transport::TransportFactory( - android::base::GetBoolProperty("ro.kernel.qemu", false))); + isEmulator)); pTransportFactory->openConnection(); } return pTransportFactory; @@ -480,11 +495,14 @@ JavacardKeymaster4Device::JavacardKeymaster4Device(): softKm_(new ::keymaster::A context->SetSystemVersion(GetOsVersion(), GetOsPatchlevel()); return context; }(), - kOperationTableSize)), oprCtx_(new OperationContext()), isEachSystemPropertySet(false) { + kOperationTableSize, keymaster::MessageVersion(keymaster::KmVersion::KEYMASTER_4_1, + 0 /* km_date */) )), oprCtx_(new OperationContext()), isEachSystemPropertySet(false) { // Send Android system properties like os_version, os_patchlevel and vendor_patchlevel // to the Applet. Incase if setting system properties fails here, again try setting // it from computeSharedHmac. + if (ErrorCode::OK == setAndroidSystemProperties(cborConverter_, oprCtx_)) { + LOG(ERROR) << "javacard strongbox : setAndroidSystemProperties from constructor - successful"; isEachSystemPropertySet = true; } @@ -522,7 +540,7 @@ Return JavacardKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_ } else { // It should not come here, but incase if for any reason SB keymaster fails to getHardwareInfo // return proper values from HAL. - LOG(ERROR) << "Failed to fetch getHardwareInfo from javacard"; + LOG(ERROR) << "Failed to fetch getHardwareInfo from javacard returning fixed values from HAL itself"; _hidl_cb(SecurityLevel::STRONGBOX, JAVACARD_KEYMASTER_NAME, JAVACARD_KEYMASTER_AUTHOR); return Void(); } @@ -541,28 +559,12 @@ Return JavacardKeymaster4Device::getHmacSharingParameters(getHmacSharingPa true, oprCtx_); if (item != nullptr) { if(!cborConverter_.getHmacSharingParameters(item, 1, hmacSharingParameters)) { - LOG(ERROR) << "Failed to convert cbor data of INS_GET_HMAC_SHARING_PARAM_CMD"; + LOG(ERROR) << "javacard strongbox : Failed to convert cbor data of INS_GET_HMAC_SHARING_PARAM_CMD"; errorCode = ErrorCode::UNKNOWN_ERROR; } } + LOG(ERROR) << "javacard strongbox : received getHmacSharingParameter from Javacard - successful"; } -#ifdef VTS_EMULATOR - /* TODO temporary fix: vold daemon calls performHmacKeyAgreement. At that time when vold calls this API there is no - * network connectivity and socket cannot be connected. So as a hack we are calling softkeymaster to getHmacSharing - * parameters. - */ - else { - auto response = softKm_->GetHmacSharingParameters(); - LOG(DEBUG) << "INS_GET_HMAC_SHARING_PARAM_CMD not succeded with javacard"; - LOG(DEBUG) << "Setting software keymaster hmac sharing parameters"; - hmacSharingParameters.seed.setToExternal(const_cast(response.params.seed.data), - response.params.seed.data_length); - static_assert(sizeof(response.params.nonce) == hmacSharingParameters.nonce.size(), "Nonce sizes don't match"); - memcpy(hmacSharingParameters.nonce.data(), response.params.nonce, hmacSharingParameters.nonce.size()); - errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "INS_GET_HMAC_SHARING_PARAM_CMD softkm status: " << (int32_t) errorCode; - } -#endif _hidl_cb(errorCode, hmacSharingParameters); return Void(); } @@ -575,21 +577,22 @@ Return JavacardKeymaster4Device::computeSharedHmac(const hidl_vec tempVec; cppbor::Array outerArray; -#ifndef VTS_EMULATOR // The Android system properties like OS_VERSION, OS_PATCHLEVEL and VENDOR_PATCHLEVEL are to // be delivered to the Applet when the HAL is first loaded. Incase if settting system properties // failed at construction time then this is one of the ideal places to send this information // to the Applet as computeSharedHmac is called everytime when Android device boots. if (!isEachSystemPropertySet) { - errorCode = setAndroidSystemProperties(cborConverter_); + errorCode = setAndroidSystemProperties(cborConverter_, oprCtx_); if (ErrorCode::OK != errorCode) { LOG(ERROR) << " Failed to set os_version, os_patchlevel and vendor_patchlevel err: " << (int32_t)errorCode; _hidl_cb(errorCode, sharingCheck); return Void(); } + + LOG(ERROR) << "javacard strongbox : setAndroidSystemProperties from ComputeSharedHmac - successful "; + isEachSystemPropertySet = true; } -#endif for(size_t i = 0; i < params.size(); ++i) { cppbor::Array innerArray; @@ -606,6 +609,7 @@ Return JavacardKeymaster4Device::computeSharedHmac(const hidl_vec(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); @@ -619,34 +623,12 @@ Return JavacardKeymaster4Device::computeSharedHmac(const hidl_vecComputeSharedHmac(request); - if (response.error == KM_ERROR_OK) sharingCheck = kmBlob2hidlVec(response.sharing_check); - errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "INS_COMPUTE_SHARED_HMAC_CMD softkm status: " << (int32_t) errorCode; - } -#endif + LOG(ERROR) << "javacard strongbox : computeSharedHmac - sending sharingCheckToKeystore"; + _hidl_cb(errorCode, sharingCheck); return Void(); - } +} Return JavacardKeymaster4Device::verifyAuthorization(uint64_t , const hidl_vec& , const HardwareAuthToken& , verifyAuthorization_cb _hidl_cb) { VerificationToken verificationToken; @@ -869,11 +851,11 @@ Return JavacardKeymaster4Device::exportKey(KeyFormat exportFormat, const h return Void(); } - ExportKeyRequest request; + ExportKeyRequest request(softKm_->message_version()); request.key_format = legacy_enum_conversion(exportFormat); request.SetKeyMaterial(keyBlob.data(), keyBlob.size()); - ExportKeyResponse response; + ExportKeyResponse response(softKm_->message_version()); softKm_->ExportKey(request, &response); if(response.error == KM_ERROR_INCOMPATIBLE_ALGORITHM) { @@ -1043,12 +1025,12 @@ Return JavacardKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec< */ LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD purpose: " << (int32_t)purpose; if (KeyPurpose::ENCRYPT == purpose || KeyPurpose::VERIFY == purpose) { - BeginOperationRequest request; + BeginOperationRequest request(softKm_->message_version()); request.purpose = legacy_enum_conversion(purpose); request.SetKeyMaterial(keyBlob.data(), keyBlob.size()); request.additional_params.Reinitialize(KmParamSet(inParams)); - BeginOperationResponse response; + BeginOperationResponse response(softKm_->message_version()); /* For Symmetric key operation, the BeginOperation returns KM_ERROR_INCOMPATIBLE_ALGORITHM error. */ softKm_->BeginOperation(request, &response); errorCode = legacy_enum_conversion(response.error); @@ -1152,7 +1134,7 @@ Return JavacardKeymaster4Device::update(uint64_t halGeneratedOprHandle, co hidl_vec outParams; hidl_vec output; uint64_t operationHandle; - UpdateOperationResponse response; + UpdateOperationResponse response(softKm_->message_version()); if (ErrorCode::OK != (errorCode = getOrigOperationHandle(halGeneratedOprHandle, operationHandle))) { LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle is passed or if" << " secure element reset occurred."; @@ -1163,7 +1145,7 @@ Return JavacardKeymaster4Device::update(uint64_t halGeneratedOprHandle, co if (!isStrongboxOperation(halGeneratedOprHandle)) { /* SW keymaster (Public key operation) */ LOG(DEBUG) << "INS_UPDATE_OPERATION_CMD - swkm operation "; - UpdateOperationRequest request; + UpdateOperationRequest request(softKm_->message_version()); request.op_handle = operationHandle; request.input.Reinitialize(input.data(), input.size()); request.additional_params.Reinitialize(KmParamSet(inParams)); @@ -1267,7 +1249,7 @@ Return JavacardKeymaster4Device::finish(uint64_t halGeneratedOprHandle, co uint64_t operationHandle; hidl_vec outParams; hidl_vec output; - FinishOperationResponse response; + FinishOperationResponse response(softKm_->message_version()); if (ErrorCode::OK != (errorCode = getOrigOperationHandle(halGeneratedOprHandle, operationHandle))) { LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle is passed or if" @@ -1279,7 +1261,7 @@ Return JavacardKeymaster4Device::finish(uint64_t halGeneratedOprHandle, co if (!isStrongboxOperation(halGeneratedOprHandle)) { /* SW keymaster (Public key operation) */ LOG(DEBUG) << "FINISH - swkm operation "; - FinishOperationRequest request; + FinishOperationRequest request(softKm_->message_version()); request.op_handle = operationHandle; request.input.Reinitialize(input.data(), input.size()); request.signature.Reinitialize(signature.data(), signature.size()); @@ -1405,10 +1387,10 @@ Return JavacardKeymaster4Device::abort(uint64_t halGeneratedOprHandle << " secure element reset occurred."; return errorCode; } - AbortOperationRequest request; + AbortOperationRequest request(softKm_->message_version()); request.op_handle = operationHandle; - AbortOperationResponse response; + AbortOperationResponse response(softKm_->message_version()); softKm_->AbortOperation(request, &response); errorCode = legacy_enum_conversion(response.error); diff --git a/HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp b/HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp index 8f9905af..bbdfba28 100644 --- a/HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp +++ b/HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp @@ -35,7 +35,7 @@ using ::keymaster::V4_1::javacard::KmParamSet; namespace keymaster { JavaCardSoftKeymasterContext::JavaCardSoftKeymasterContext(keymaster_security_level_t security_level) - : PureSoftKeymasterContext(security_level) {} + : PureSoftKeymasterContext(KmVersion::KEYMASTER_4_1, security_level) {} JavaCardSoftKeymasterContext::~JavaCardSoftKeymasterContext() {} diff --git a/HAL/keymaster/4.1/SocketTransport.cpp b/HAL/keymaster/4.1/SocketTransport.cpp index 331a00b1..e060262f 100644 --- a/HAL/keymaster/4.1/SocketTransport.cpp +++ b/HAL/keymaster/4.1/SocketTransport.cpp @@ -22,7 +22,7 @@ #include #define PORT 8080 -#define IPADDR "10.9.40.24" +#define IPADDR "192.168.0.29" //#define IPADDR "192.168.0.5" #define MAX_RECV_BUFFER_SIZE 2500 @@ -30,6 +30,11 @@ namespace se_transport { bool SocketTransport::openConnection() { struct sockaddr_in serv_addr; + + if(mSocketStatus){ + closeConnection(); + } + if ((mSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { LOG(ERROR) << "Socket creation failed" << " Error: "<& output) { uint8_t buffer[MAX_RECV_BUFFER_SIZE]; int count = 1; - while(!socketStatus && count++ < 5 ) { + bool sendStatus = false; + while(!mSocketStatus && count++ < 5 ) { sleep(1); LOG(ERROR) << "Trying to open socket connection... count: " << count; openConnection(); @@ -67,22 +74,24 @@ bool SocketTransport::sendData(const uint8_t* inData, const size_t inLen, std::v if(count >= 5) { LOG(ERROR) << "Failed to open socket connection"; + closeConnection(); return false; } - if (0 > send(mSocket, inData, inLen , 0 )) { + if (send(mSocket, inData, inLen , 0)< 0) { static int connectionResetCnt = 0; /* To avoid loop */ if (ECONNRESET == errno && connectionResetCnt == 0) { //Connection reset. Try open socket and then sendData. - socketStatus = false; + closeConnection(); connectionResetCnt++; - return sendData(inData, inLen, output); + sendStatus = sendData(inData, inLen, output); + return sendStatus; } LOG(ERROR) << "Failed to send data over socket err: " << errno; connectionResetCnt = 0; return false; } - ssize_t valRead = read( mSocket , buffer, MAX_RECV_BUFFER_SIZE); + ssize_t valRead = read( mSocket , buffer, MAX_RECV_BUFFER_SIZE); if(0 > valRead) { LOG(ERROR) << "Failed to read data from socket."; } @@ -93,13 +102,14 @@ bool SocketTransport::sendData(const uint8_t* inData, const size_t inLen, std::v } bool SocketTransport::closeConnection() { - close(mSocket); - socketStatus = false; + if(mSocketStatus) + close(mSocket); + mSocketStatus = false; return true; } bool SocketTransport::isConnected() { - return socketStatus; + return mSocketStatus; } } diff --git a/HAL/keymaster/4.1/android.hardware.keymaster@4.1-javacard.service.rc b/HAL/keymaster/4.1/android.hardware.keymaster@4.1-javacard.service.rc deleted file mode 100644 index 556ffd1d..00000000 --- a/HAL/keymaster/4.1/android.hardware.keymaster@4.1-javacard.service.rc +++ /dev/null @@ -1,6 +0,0 @@ -service android.hardware.keymaster@4.1-javacard.service /vendor/bin/hw/android.hardware.keymaster@4.1-javacard.service - interface android.hardware.keymaster@4.0::IKeymasterDevice default - interface android.hardware.keymaster@4.1::IKeymasterDevice default - class early_hal - user system - group system drmrpc diff --git a/HAL/keymaster/4.1/android.hardware.keymaster@4.1-strongbox.service.rc b/HAL/keymaster/4.1/android.hardware.keymaster@4.1-strongbox.service.rc new file mode 100644 index 00000000..8ccde380 --- /dev/null +++ b/HAL/keymaster/4.1/android.hardware.keymaster@4.1-strongbox.service.rc @@ -0,0 +1,6 @@ +service android.hardware.keymaster@4.1-strongbox.service /vendor/bin/hw/android.hardware.keymaster@4.1-strongbox.service + interface android.hardware.keymaster@4.0::IKeymasterDevice strongbox + interface android.hardware.keymaster@4.1::IKeymasterDevice strongbox + class early_hal + user system + group system drmrpc diff --git a/HAL/keymaster/4.1/android.hardware.keymaster@4.1-javacard.service.xml b/HAL/keymaster/4.1/android.hardware.keymaster@4.1-strongbox.service.xml similarity index 75% rename from HAL/keymaster/4.1/android.hardware.keymaster@4.1-javacard.service.xml rename to HAL/keymaster/4.1/android.hardware.keymaster@4.1-strongbox.service.xml index 83fccabe..b82da1e6 100644 --- a/HAL/keymaster/4.1/android.hardware.keymaster@4.1-javacard.service.xml +++ b/HAL/keymaster/4.1/android.hardware.keymaster@4.1-strongbox.service.xml @@ -2,6 +2,6 @@ android.hardware.keymaster hwbinder - @4.1::IKeymasterDevice/javacard + @4.1::IKeymasterDevice/strongbox diff --git a/HAL/keymaster/4.1/android.hardware.strongbox_keystore.xml b/HAL/keymaster/4.1/android.hardware.strongbox_keystore.xml new file mode 100644 index 00000000..ee3ed8fa --- /dev/null +++ b/HAL/keymaster/4.1/android.hardware.strongbox_keystore.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/HAL/keymaster/4.1/service.cpp b/HAL/keymaster/4.1/service.cpp index 75ed2510..cd7653d0 100644 --- a/HAL/keymaster/4.1/service.cpp +++ b/HAL/keymaster/4.1/service.cpp @@ -23,10 +23,9 @@ int main() { ::android::hardware::configureRpcThreadpool(1, true); auto keymaster = new ::keymaster::V4_1::javacard::JavacardKeymaster4Device(); - - auto status = keymaster->registerAsService("javacard"); + auto status = keymaster->registerAsService("strongbox"); if (status != android::OK) { - LOG(FATAL) << "Could not register service for Keymaster 4.1 (" << status << ")"; + LOG(FATAL) << "Could not register service for Javacard Keymaster 4.1 (" << status << ")"; return -1; } diff --git a/HAL/keymaster/Android.bp b/HAL/keymaster/Android.bp index 92e3be8f..cb88780c 100644 --- a/HAL/keymaster/Android.bp +++ b/HAL/keymaster/Android.bp @@ -15,11 +15,11 @@ cc_binary { - name: "android.hardware.keymaster@4.1-javacard.service", + name: "android.hardware.keymaster@4.1-strongbox.service", relative_install_path: "hw", vendor: true, - init_rc: ["4.1/android.hardware.keymaster@4.1-javacard.service.rc"], - vintf_fragments: ["4.1/android.hardware.keymaster@4.1-javacard.service.xml"], + init_rc: ["4.1/android.hardware.keymaster@4.1-strongbox.service.rc"], + vintf_fragments: ["4.1/android.hardware.keymaster@4.1-strongbox.service.xml"], srcs: [ "4.1/service.cpp", "4.1/JavacardKeymaster4Device.cpp", @@ -38,23 +38,19 @@ cc_binary { "libhardware", "libhidlbase", "libsoftkeymasterdevice", + "libsoft_attestation_cert", "libkeymaster_messages", "libkeymaster_portable", - "libcppbor", + "libcppbor_external", "android.hardware.keymaster@4.1", "android.hardware.keymaster@4.0", "libjc_transport", "libjc_common", "libcrypto", ], - arch: { - x86_64: { - cflags: ["-DVTS_EMULATOR"], - }, - x86: { - cflags: ["-DVTS_EMULATOR"], - }, - }, + required: [ + "android.hardware.strongbox_keystore.xml", + ], } cc_library { @@ -77,14 +73,15 @@ cc_library { "libutils", "libhardware", "libhidlbase", - "libsoftkeymasterdevice", + "libsoftkeymasterdevice", + "libsoft_attestation_cert", "libkeymaster_messages", - "libkeymaster_portable", - "libcppbor", + "libkeymaster_portable", + "libcppbor_external", "android.hardware.keymaster@4.1", "android.hardware.keymaster@4.0", "libjc_transport", - "libcrypto", + "libcrypto", ], } @@ -131,12 +128,20 @@ cc_library { "libutils", "libhardware", "libhidlbase", - "libsoftkeymasterdevice", + "libsoftkeymasterdevice", + "libsoft_attestation_cert", "libkeymaster_messages", - "libkeymaster_portable", - "libcppbor", + "libkeymaster_portable", + "libcppbor_external", "android.hardware.keymaster@4.1", "android.hardware.keymaster@4.0", - "libcrypto", + "libcrypto", ], } + +prebuilt_etc { + name: "android.hardware.strongbox_keystore.xml", + sub_dir: "permissions", + vendor: true, + src: "4.1/android.hardware.strongbox_keystore.xml", +} diff --git a/HAL/keymaster/include/CommonUtils.h b/HAL/keymaster/include/CommonUtils.h index b6612741..8fd247f7 100644 --- a/HAL/keymaster/include/CommonUtils.h +++ b/HAL/keymaster/include/CommonUtils.h @@ -59,9 +59,7 @@ inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) { } inline hidl_vec kmBuffer2hidlVec(const ::keymaster::Buffer& buf) { - hidl_vec result; - result.setToExternal(const_cast(buf.peek_read()), buf.available_read()); - return result; + return hidl_vec(buf.begin(), buf.end()); } inline void blob2Vec(const uint8_t *from, size_t size, std::vector& to) { @@ -71,9 +69,7 @@ inline void blob2Vec(const uint8_t *from, size_t size, std::vector& to) } inline hidl_vec kmBlob2hidlVec(const keymaster_blob_t& blob) { - hidl_vec result; - result.setToExternal(const_cast(blob.data), blob.data_length); - return result; + return hidl_vec(blob.data, blob.data+blob.data_length); } keymaster_key_param_set_t hidlKeyParams2Km(const hidl_vec& keyParams); diff --git a/HAL/keymaster/include/JavacardSoftKeymasterContext.h b/HAL/keymaster/include/JavacardSoftKeymasterContext.h index 0fa2d711..8cdeab92 100644 --- a/HAL/keymaster/include/JavacardSoftKeymasterContext.h +++ b/HAL/keymaster/include/JavacardSoftKeymasterContext.h @@ -18,7 +18,7 @@ #define SYSTEM_KEYMASTER_JAVA_CARD_SOFT_KEYMASTER_CONTEXT_H_ #include - +#include namespace keymaster { class SoftKeymasterKeyRegistrations; diff --git a/HAL/keymaster/include/Transport.h b/HAL/keymaster/include/Transport.h index 25294102..c6674dca 100644 --- a/HAL/keymaster/include/Transport.h +++ b/HAL/keymaster/include/Transport.h @@ -64,6 +64,7 @@ class OmapiTransport : public ITransport { * Transmists the data over the opened basic channel and receives the data back. */ bool sendData(const uint8_t* inData, const size_t inLen, std::vector& output) override; + /** * Closes the connection. */ @@ -79,7 +80,7 @@ class OmapiTransport : public ITransport { class SocketTransport : public ITransport { public: - SocketTransport() : mSocket(-1), socketStatus(false) { + SocketTransport() : mSocket(-1), mSocketStatus(false){ } /** * Creates a socket instance and connects to the provided server IP and port. @@ -89,6 +90,7 @@ class SocketTransport : public ITransport { * Sends data over socket and receives data back. */ bool sendData(const uint8_t* inData, const size_t inLen, std::vector& output) override; + /** * Closes the connection. */ @@ -103,8 +105,7 @@ class SocketTransport : public ITransport { * Socket instance. */ int mSocket; - bool socketStatus; - + bool mSocketStatus; }; } diff --git a/ProvisioningTool/Android.bp b/ProvisioningTool/Android.bp deleted file mode 100644 index ad1ee2d1..00000000 --- a/ProvisioningTool/Android.bp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - - -cc_binary { - name: "provision_tool", - vendor: true, - relative_install_path: "hw", - srcs: [ - "ProvisionTool.cpp", - ], - shared_libs: [ - "libdl", - "libcutils", - "libutils", - "libbase", - "libhardware", - "libhidlbase", - "libkeymaster_messages", - "libkeymaster_portable", - "android.hardware.keymaster@4.1", - "android.hardware.keymaster@4.0", - "libcppbor", - "libjc_transport", - "libcrypto", - "libjsoncpp", - "libjc_common", - "libjc_provision", - ], -} - -cc_library { - name: "libjc_provision", - vendor_available: true, - srcs: [ - "Provision.cpp", - ], - shared_libs: [ - "liblog", - "libcutils", - "libdl", - "libbase", - "libutils", - "libhardware", - "libhidlbase", - "libsoftkeymasterdevice", - "libkeymaster_messages", - "libkeymaster_portable", - "libcppbor", - "android.hardware.keymaster@4.1", - "android.hardware.keymaster@4.0", - "libjc_transport", - "libcrypto", - "libjc_common", - ], -} diff --git a/ProvisioningTool/Makefile b/ProvisioningTool/Makefile new file mode 100644 index 00000000..ca462cb1 --- /dev/null +++ b/ProvisioningTool/Makefile @@ -0,0 +1,58 @@ +CC = g++ +SRC_DIR = src +# source files for construct_apdus +CONSTRUCT_APDUS_SRC = $(SRC_DIR)/construct_apdus.cpp \ + $(SRC_DIR)/cppbor.cpp \ + $(SRC_DIR)/cppbor_parse.cpp \ + $(SRC_DIR)/utils.cpp \ + +CONSTRUCT_APDUS_OBJFILES = $(CONSTRUCT_APDUS_SRC:.cpp=.o) +CONSTRUCT_APDUS_BIN = construct_apdus + +# source files for provision +PROVISION_SRC = $(SRC_DIR)/provision.cpp \ + $(SRC_DIR)/socket.cpp \ + $(SRC_DIR)/cppbor.cpp \ + $(SRC_DIR)/cppbor_parse.cpp \ + $(SRC_DIR)/utils.cpp \ + +PROVISION_OBJFILES = $(PROVISION_SRC:.cpp=.o) +PROVISION_BIN = provision + + +ifeq ($(OS),Windows_NT) + uname_S := Windows +else + uname_S := $(shell uname -s) +endif + +ifeq ($(uname_S), Windows) + PLATFORM = -D__WIN32__ +endif +ifeq ($(uname_S), Linux) + PLATFORM = -D__LINUX__ +endif + +DEBUG = -g +CXXFLAGS = $(DEBUG) $(PLATFORM) -Wall -std=c++2a +CFLAGS = $(CXXFLAGS) -Iinclude +LDFLAGS = -Llib/ +LIB_JSON = -ljsoncpp +LIB_CRYPTO = -lcrypto +LDLIBS = $(LIB_JSON) $(LIB_CRYPTO) + +all: $(CONSTRUCT_APDUS_BIN) $(PROVISION_BIN) + +$(CONSTRUCT_APDUS_BIN): $(CONSTRUCT_APDUS_OBJFILES) + $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +$(PROVISION_BIN): $(PROVISION_OBJFILES) + $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +%.o: %.cpp + $(CC) $(CFLAGS) -c -o $@ $^ + + +.PHONY: clean +clean: + rm -f $(CONSTRUCT_APDUS_OBJFILES) $(CONSTRUCT_APDUS_BIN) $(PROVISION_OBJFILES) $(PROVISION_BIN) diff --git a/ProvisioningTool/Provision.cpp b/ProvisioningTool/Provision.cpp deleted file mode 100644 index 4251cd80..00000000 --- a/ProvisioningTool/Provision.cpp +++ /dev/null @@ -1,509 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include - -#define INS_BEGIN_KM_CMD 0x00 -#define APDU_CLS 0x80 -#define APDU_P1 0x40 -#define APDU_P2 0x00 -#define APDU_RESP_STATUS_OK 0x9000 -#define SE_POWER_RESET_STATUS_FLAG ( 1 << 30) - -namespace keymaster { -namespace V4_1 { -namespace javacard { - -enum class Instruction { - // Provisioning commands - INS_PROVISION_ATTESTATION_KEY_CMD = INS_BEGIN_KM_CMD+1, - INS_PROVISION_CERT_CHAIN_CMD = INS_BEGIN_KM_CMD+2, - INS_PROVISION_CERT_PARAMS_CMD = INS_BEGIN_KM_CMD+3, - INS_PROVISION_ATTEST_IDS_CMD = INS_BEGIN_KM_CMD+4, - INS_PROVISION_PRESHARED_SECRET_CMD = INS_BEGIN_KM_CMD+5, - INS_SET_BOOT_PARAMS_CMD = INS_BEGIN_KM_CMD+6, - INS_LOCK_PROVISIONING_CMD = INS_BEGIN_KM_CMD+7, - INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD+8, - INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD+9, -}; - -//Extended error codes -enum ExtendedErrors { - SW_CONDITIONS_NOT_SATISFIED = -10001, - UNSUPPORTED_CLA = -10002, - INVALID_P1P2 = -10003, - UNSUPPORTED_INSTRUCTION = -10004, - CMD_NOT_ALLOWED = -10005, - SW_WRONG_LENGTH = -10006, - INVALID_DATA = -10007, - CRYPTO_ILLEGAL_USE = -10008, - CRYPTO_ILLEGAL_VALUE = -10009, - CRYPTO_INVALID_INIT = -10010, - CRYPTO_NO_SUCH_ALGORITHM = -10011, - CRYPTO_UNINITIALIZED_KEY = -10012, - GENERIC_UNKNOWN_ERROR = -10013 -}; - -enum ProvisionStatus { - NOT_PROVISIONED = 0x00, - PROVISION_STATUS_ATTESTATION_KEY = 0x01, - PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x02, - PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04, - PROVISION_STATUS_ATTEST_IDS = 0x08, - PROVISION_STATUS_PRESHARED_SECRET = 0x10, - PROVISION_STATUS_BOOT_PARAM = 0x20, - PROVISION_STATUS_PROVISIONING_LOCKED = 0x40, -}; - -// Static function declarations. -static ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData, std::vector& apduOut); -static ErrorCode sendProvisionData(std::unique_ptr& transport, Instruction ins, std::vector& inData, std::vector& response); -static uint16_t getStatus(std::vector& inputData); -template -static std::tuple, T> decodeData(CborConverter& cb, const std::vector& response); -template -static T translateExtendedErrorsToHalErrors(T& errorCode); - -template -static T translateExtendedErrorsToHalErrors(T& errorCode) { - T err; - switch(static_cast(errorCode)) { - case SW_CONDITIONS_NOT_SATISFIED: - case UNSUPPORTED_CLA: - case INVALID_P1P2: - case INVALID_DATA: - case CRYPTO_ILLEGAL_USE: - case CRYPTO_ILLEGAL_VALUE: - case CRYPTO_INVALID_INIT: - case CRYPTO_UNINITIALIZED_KEY: - case GENERIC_UNKNOWN_ERROR: - err = T::UNKNOWN_ERROR; - break; - case CRYPTO_NO_SUCH_ALGORITHM: - err = T::UNSUPPORTED_ALGORITHM; - break; - case UNSUPPORTED_INSTRUCTION: - case CMD_NOT_ALLOWED: - case SW_WRONG_LENGTH: - err = T::UNIMPLEMENTED; - break; - default: - err = static_cast(errorCode); - break; - } - return err; -} - -/** - * Returns the negative value of the same number. - */ -static inline int32_t get2sCompliment(uint32_t value) { - return static_cast(~value+1); -} - -/** - * This function separates the original error code from the - * power reset flag and returns the original error code. - */ -static uint32_t extractErrorCode(uint32_t errorCode) { - //Check if secure element is reset - bool isSeResetOccurred = (0 != (errorCode & SE_POWER_RESET_STATUS_FLAG)); - - if (isSeResetOccurred) { - LOG(ERROR) << "Secure element reset happened"; - errorCode &= ~SE_POWER_RESET_STATUS_FLAG; - } - return errorCode; -} - -template -static std::tuple, T> decodeData(CborConverter& cb, const std::vector& response) { - std::unique_ptr item(nullptr); - T errorCode = T::OK; - std::tie(item, errorCode) = cb.decodeData(response, true); - - uint32_t tempErrCode = extractErrorCode(static_cast(errorCode)); - - // SE sends errocode as unsigned value so convert the unsigned value - // into a signed value of same magnitude and copy back to errorCode. - errorCode = static_cast(get2sCompliment(tempErrCode)); - - if (T::OK != errorCode) - errorCode = translateExtendedErrorsToHalErrors(errorCode); - return {std::move(item), errorCode}; -} - -static inline X509* parseDerCertificate(std::vector& certData) { - X509 *x509 = NULL; - - /* Create BIO instance from certificate data */ - BIO *bio = BIO_new_mem_buf(certData.data(), certData.size()); - if(bio == NULL) { - LOG(ERROR) << " Failed to create BIO from buffer."; - return NULL; - } - /* Create X509 instance from BIO */ - x509 = d2i_X509_bio(bio, NULL); - if(x509 == NULL) { - LOG(ERROR) << " Failed to get X509 instance from BIO."; - return NULL; - } - BIO_free(bio); - return x509; -} - -static inline void getDerSubjectName(X509* x509, std::vector& subject) { - uint8_t *subjectDer = NULL; - X509_NAME* asn1Subject = X509_get_subject_name(x509); - if(asn1Subject == NULL) { - LOG(ERROR) << " Failed to read the subject."; - return; - } - /* Convert X509_NAME to der encoded subject */ - int len = i2d_X509_NAME(asn1Subject, &subjectDer); - if (len < 0) { - LOG(ERROR) << " Failed to get readable name from X509_NAME."; - return; - } - subject.insert(subject.begin(), subjectDer, subjectDer+len); -} - -static inline void getNotAfter(X509* x509, std::vector& notAfterDate) { - const ASN1_TIME* notAfter = X509_get0_notAfter(x509); - if(notAfter == NULL) { - LOG(ERROR) << " Failed to read expiry time."; - return; - } - int strNotAfterLen = ASN1_STRING_length(notAfter); - const uint8_t *strNotAfter = ASN1_STRING_get0_data(notAfter); - if(strNotAfter == NULL) { - LOG(ERROR) << " Failed to read expiry time from ASN1 string."; - return; - } - notAfterDate.insert(notAfterDate.begin(), strNotAfter, strNotAfter + strNotAfterLen); -} - -static uint16_t getStatus(std::vector& inputData) { - //Last two bytes are the status SW0SW1 - return (inputData.at(inputData.size()-2) << 8) | (inputData.at(inputData.size()-1)); -} - -static ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData, std::vector& apduOut) { - - apduOut.push_back(static_cast(APDU_CLS)); //CLS - apduOut.push_back(static_cast(ins)); //INS - apduOut.push_back(static_cast(APDU_P1)); //P1 - apduOut.push_back(static_cast(APDU_P2)); //P2 - - if(USHRT_MAX >= inputData.size()) { - // Send extended length APDU always as response size is not known to HAL. - // Case 1: Lc > 0 CLS | INS | P1 | P2 | 00 | 2 bytes of Lc | CommandData | 2 bytes of Le all set to 00. - // Case 2: Lc = 0 CLS | INS | P1 | P2 | 3 bytes of Le all set to 00. - //Extended length 3 bytes, starts with 0x00 - apduOut.push_back(static_cast(0x00)); - if (inputData.size() > 0) { - apduOut.push_back(static_cast(inputData.size() >> 8)); - apduOut.push_back(static_cast(inputData.size() & 0xFF)); - //Data - apduOut.insert(apduOut.end(), inputData.begin(), inputData.end()); - } - //Expected length of output. - //Accepting complete length of output every time. - apduOut.push_back(static_cast(0x00)); - apduOut.push_back(static_cast(0x00)); - } else { - return (ErrorCode::INSUFFICIENT_BUFFER_SPACE); - } - - return (ErrorCode::OK);//success -} - - - -static ErrorCode sendProvisionData(std::unique_ptr& transport, Instruction ins, std::vector& inData, std::vector& response) { - ErrorCode ret = ErrorCode::OK; - std::vector apdu; - CborConverter cborConverter; - std::unique_ptr item; - ret = constructApduMessage(ins, inData, apdu); - if(ret != ErrorCode::OK) return ret; - - if(!transport->sendData(apdu.data(), apdu.size(), response)) { - return (ErrorCode::SECURE_HW_COMMUNICATION_FAILED); - } - - if((response.size() < 2) || (getStatus(response) != APDU_RESP_STATUS_OK)) { - return (ErrorCode::UNKNOWN_ERROR); - } - - if((response.size() > 2)) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, ret) = decodeData(cborConverter, std::vector(response.begin(), response.end()-2)); - } else { - ret = ErrorCode::UNKNOWN_ERROR; - } - - return ret; -} - -ErrorCode Provision::init() { - if(pTransportFactory == nullptr) { - pTransportFactory = std::unique_ptr(new se_transport::TransportFactory( - android::base::GetBoolProperty("ro.kernel.qemu", false))); - if(!pTransportFactory->openConnection()) - return ErrorCode::UNKNOWN_ERROR; - } - return ErrorCode::OK; -} - -ErrorCode Provision::provisionAttestationKey(std::vector& batchKey) { - ErrorCode errorCode = ErrorCode::OK; - std::vector privKey; - std::vector pubKey; - EcCurve curve; - CborConverter cborConverter; - cppbor::Array array; - cppbor::Array subArray; - std::vector response; - Instruction ins = Instruction::INS_PROVISION_ATTESTATION_KEY_CMD; - - AuthorizationSet authSetKeyParams(AuthorizationSetBuilder() - .Authorization(TAG_ALGORITHM, KM_ALGORITHM_EC) - .Authorization(TAG_DIGEST, KM_DIGEST_SHA_2_256) - .Authorization(TAG_EC_CURVE, KM_EC_CURVE_P_256) - .Authorization(TAG_PURPOSE, static_cast(0x7F))); /* The value 0x7F is not present in types.hal */ - hidl_vec keyParams = kmParamSet2Hidl(authSetKeyParams); - if(ErrorCode::OK != (errorCode = ecRawKeyFromPKCS8(batchKey, privKey, pubKey, curve))) { - return errorCode; - } - subArray.add(privKey); - subArray.add(pubKey); - std::vector encodedArray = subArray.encode(); - cppbor::Bstr bstr(encodedArray.begin(), encodedArray.end()); - //Encode data. - cborConverter.addKeyparameters(array, keyParams); - array.add(static_cast(KeyFormat::RAW)); - array.add(bstr); - - std::vector cborData = array.encode(); - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::provisionAtestationCertificateChain(std::vector>& certChain) { - ErrorCode errorCode = ErrorCode::OK; - cppbor::Array array; - Instruction ins = Instruction::INS_PROVISION_CERT_CHAIN_CMD; - std::vector response; - - std::vector certData; - for (auto data : certChain) { - certData.insert(certData.end(), data.begin(), data.end()); - } - cppbor::Bstr bstrCertChain(certData.begin(), certData.end()); - std::vector cborData = bstrCertChain.encode(); - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::provisionAttestationCertificateParams(std::vector& batchCertificate) { - ErrorCode errorCode = ErrorCode::OK; - cppbor::Array array; - Instruction ins = Instruction::INS_PROVISION_CERT_PARAMS_CMD; - std::vector response; - X509 *x509 = NULL; - std::vector subject; - std::vector notAfter; - - /* Subject, AuthorityKeyIdentifier and Expirty time of the root certificate are required by javacard. */ - /* Get X509 certificate instance for the root certificate.*/ - if(NULL == (x509 = parseDerCertificate(batchCertificate))) { - return errorCode; - } - - /* Get subject in DER */ - getDerSubjectName(x509, subject); - /* Get Expirty Time */ - getNotAfter(x509, notAfter); - /*Free X509 */ - X509_free(x509); - - array = cppbor::Array(); - array.add(subject); - array.add(notAfter); - std::vector cborData = array.encode(); - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::provisionAttestationID(AttestIDParams& attestParams) { - ErrorCode errorCode = ErrorCode::OK; - CborConverter cborConverter; - cppbor::Array array; - Instruction ins = Instruction::INS_PROVISION_ATTEST_IDS_CMD; - std::vector response; - - AuthorizationSet authSetAttestParams(AuthorizationSetBuilder() - .Authorization(TAG_ATTESTATION_ID_BRAND, attestParams.brand.data(), attestParams.brand.size()) - .Authorization(TAG_ATTESTATION_ID_DEVICE, attestParams.device.data(), attestParams.device.size()) - .Authorization(TAG_ATTESTATION_ID_PRODUCT, attestParams.product.data(), attestParams.product.size()) - .Authorization(TAG_ATTESTATION_ID_SERIAL, attestParams.serial.data(), attestParams.serial.size()) - .Authorization(TAG_ATTESTATION_ID_IMEI, attestParams.imei.data(), attestParams.imei.size()) - .Authorization(TAG_ATTESTATION_ID_MEID, attestParams.meid.data(), attestParams.meid.size()) - .Authorization(TAG_ATTESTATION_ID_MANUFACTURER, attestParams.manufacturer.data(), attestParams.manufacturer.size()) - .Authorization(TAG_ATTESTATION_ID_MODEL, attestParams.model.data(), attestParams.model.size())); - - hidl_vec attestKeyParams = kmParamSet2Hidl(authSetAttestParams); - - array = cppbor::Array(); - cborConverter.addKeyparameters(array, attestKeyParams); - std::vector cborData = array.encode(); - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::provisionPreSharedSecret(std::vector& preSharedSecret) { - ErrorCode errorCode = ErrorCode::OK; - cppbor::Array array; - Instruction ins = Instruction::INS_PROVISION_PRESHARED_SECRET_CMD; - std::vector response; - - array = cppbor::Array(); - array.add(preSharedSecret); - std::vector cborData = array.encode(); - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::setAndroidSystemProperties() { - ErrorCode errorCode = ErrorCode::OK; - cppbor::Array array; - std::vector apdu; - std::vector response; - Instruction ins = Instruction::INS_SET_VERSION_PATCHLEVEL_CMD; - - array.add(GetOsVersion()). - add(GetOsPatchlevel()). - add(GetVendorPatchlevel()); - std::vector cborData = array.encode(); - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::provisionBootParameters(BootParams& bootParams) { - ErrorCode errorCode = ErrorCode::OK; - cppbor::Array array; - std::vector apdu; - std::vector response; - Instruction ins = Instruction::INS_SET_BOOT_PARAMS_CMD; - - array.add(bootParams.bootPatchLevel). - /* Verified Boot Key */ - add(bootParams.verifiedBootKey). - /* Verified Boot Hash */ - add(bootParams.verifiedBootKeyHash). - /* boot state */ - add(bootParams.verifiedBootState). - /* device locked */ - add(bootParams.deviceLocked); - - std::vector cborData = array.encode(); - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::getProvisionStatus(uint64_t& status) { - ErrorCode errorCode = ErrorCode::OK; - Instruction ins = Instruction::INS_GET_PROVISION_STATUS_CMD; - std::vector cborData; - std::vector response; - std::unique_ptr item; - CborConverter cborConverter; - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - LOG(ERROR) << "Failed to get provision status err: " << static_cast(errorCode); - return errorCode; - } - //Check if SE is provisioned. - std::tie(item, errorCode) = decodeData(cborConverter, std::vector(response.begin(), response.end()-2)); - if(item != NULL) { - - if(!cborConverter.getUint64(item, 1, status)) { - LOG(ERROR) << "Failed to parse the status from cbor data"; - return ErrorCode::UNKNOWN_ERROR; - } - } - return errorCode; -} - -ErrorCode Provision::lockProvision() { - ErrorCode errorCode = ErrorCode::OK; - Instruction ins = Instruction::INS_LOCK_PROVISIONING_CMD; - std::vector cborData; - std::vector response; - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::uninit() { - if(pTransportFactory != nullptr) { - if(!pTransportFactory->closeConnection()) - return ErrorCode::UNKNOWN_ERROR; - } - return ErrorCode::OK; -} -// Provision End - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster diff --git a/ProvisioningTool/Provision.h b/ProvisioningTool/Provision.h deleted file mode 100644 index 68a7f1d1..00000000 --- a/ProvisioningTool/Provision.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - - -#ifndef KEYMASTER_V4_1_JAVACARD_PROVISION_H_ -#define KEYMASTER_V4_1_JAVACARD_PROVISION_H_ - -#include "TransportFactory.h" - -namespace keymaster { -namespace V4_1 { -namespace javacard { - -typedef struct SystemProperties__ { - uint32_t osVersion; - uint32_t osPatchLevel; - uint32_t vendorPatchLevel; -} SystemProperties; - -typedef struct BootParams_ { - uint32_t bootPatchLevel; - std::vector verifiedBootKey; - std::vector verifiedBootKeyHash; - uint32_t verifiedBootState; - uint32_t deviceLocked; -} BootParams; - -typedef struct AttestIDParams_ { - std::string brand; - std::string device; - std::string product; - std::string serial; - std::string imei; - std::string meid; - std::string manufacturer; - std::string model; -} AttestIDParams; - -class Provision { -public: - /** - * Initalizes the transport layer. - */ - ErrorCode init(); - /** - * Provision the Attestation key. - */ - ErrorCode provisionAttestationKey(std::vector& batchKey); - /** - * Provision the Attestation certificate chain. - */ - ErrorCode provisionAtestationCertificateChain(std::vector>& CertChain); - /** - * Provision the Attestation certificate paramters. - */ - ErrorCode provisionAttestationCertificateParams(std::vector& batchCertificate); - /** - * Provision the Attestation ID. - */ - ErrorCode provisionAttestationID(AttestIDParams& attestParams); - /** - * Provision the pre-shared secret. - */ - ErrorCode provisionPreSharedSecret(std::vector& preSharedSecret); - /** - * Provision the boot parameters. - */ - ErrorCode provisionBootParameters(BootParams& bootParams ); - /** - * Set system properties. - */ - ErrorCode setAndroidSystemProperties(); - - /** - * Locks the provision. After this no more provision commanands are allowed. - */ - ErrorCode lockProvision(); - /** - * Get the provision status. - */ - ErrorCode getProvisionStatus(uint64_t&); - /** - * Uninitialize the transport layer. - */ - ErrorCode uninit(); - -private: - std::unique_ptr pTransportFactory; -}; - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster -#endif //KEYMASTER_V4_1_JAVACARD_PROVISION_H_ diff --git a/ProvisioningTool/ProvisionTool.cpp b/ProvisioningTool/ProvisionTool.cpp deleted file mode 100644 index 3ff242f5..00000000 --- a/ProvisioningTool/ProvisionTool.cpp +++ /dev/null @@ -1,616 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BUFFER_MAX_LENGTH 256 -#define SB_KEYMASTER_SERVICE "javacard" -#define INS_BEGIN_KM_CMD 0x00 -#define APDU_CLS 0x80 -#define APDU_P1 0x40 -#define APDU_P2 0x00 -#define APDU_RESP_STATUS_OK 0x9000 -#define MAX_ATTEST_IDS_SIZE 8 -#define SHARED_SECRET_SIZE 32 - -enum class Instruction { - // Provisioning commands - INS_PROVISION_ATTESTATION_KEY_CMD = INS_BEGIN_KM_CMD+1, - INS_PROVISION_CERT_CHAIN_CMD = INS_BEGIN_KM_CMD+2, - INS_PROVISION_CERT_PARAMS_CMD = INS_BEGIN_KM_CMD+3, - INS_PROVISION_ATTEST_IDS_CMD = INS_BEGIN_KM_CMD+4, - INS_PROVISION_SHARED_SECRET_CMD = INS_BEGIN_KM_CMD+5, - INS_SET_BOOT_PARAMS_CMD = INS_BEGIN_KM_CMD+6, - INS_LOCK_PROVISIONING_CMD = INS_BEGIN_KM_CMD+7, - INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD+8, -}; - -enum ProvisionStatus { - NOT_PROVISIONED = 0x00, - PROVISION_STATUS_ATTESTATION_KEY = 0x01, - PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x02, - PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04, - PROVISION_STATUS_ATTEST_IDS = 0x08, - PROVISION_STATUS_PRESHARED_SECRET = 0x10, - PROVISION_STATUS_BOOT_PARAM = 0x20, - PROVISION_STATUS_PROVISIONING_LOCKED = 0x40, -}; - -using ::android::hardware::keymaster::V4_0::ErrorCode; - -static keymaster::V4_1::javacard::Provision mProvision; -Json::Value root; - -constexpr char hex_value[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // '0'..'9' - 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'A'..'F' - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'a'..'f' - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -std::string hex2str(std::string a) { - std::string b; - size_t num = a.size() / 2; - b.resize(num); - for (size_t i = 0; i < num; i++) { - b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]); - } - return b; -} - -bool parseJsonFile(const char* filename); - -static bool readDataFromFile(const char *filename, std::vector& data) { - FILE *fp; - bool ret = true; - fp = fopen(filename, "rb"); - if(fp == NULL) { - printf("\nFailed to open file: \n"); - return false; - } - fseek(fp, 0L, SEEK_END); - long int filesize = ftell(fp); - rewind(fp); - std::unique_ptr buf(new uint8_t[filesize]); - if( 0 == fread(buf.get(), filesize, 1, fp)) { - printf("\n No content in the file \n"); - ret = false; - } - if(true == ret) { - data.insert(data.end(), buf.get(), buf.get() + filesize); - } - fclose(fp); - return ret; -} - -void usage() { - printf("Usage: provision_tool [options]\n"); - printf("Valid options are:\n"); - printf("-h, --help show this help message and exit.\n"); - printf("-a, --all jsonFile \t Executes all the provision commands \n"); - printf("-k, --attest_key jsonFile \t Provision attestation key \n"); - printf("-c, --cert_chain jsonFile \t Provision attestation certificate chain \n"); - printf("-p, --cert_params jsonFile \t Provision attestation certificate parameters \n"); - printf("-i, --attest_ids jsonFile \t Provision attestation IDs \n"); - printf("-r, --shared_secret jsonFile \t Provision shared secret \n"); - printf("-b, --set_boot_params jsonFile \t Set boot parameters \n"); - printf("-e, --set_system_properties \t Set system properties \n"); - printf("-s, --provision_status \t Prints the provision status.\n"); - printf("-l, --lock_provision \t Locks the provision commands.\n"); -} - -bool getBootParameterIntValue(Json::Value& bootParamsObj, const char* key, uint32_t *value) { - bool ret = false; - Json::Value val = bootParamsObj[key]; - if(val.empty()) - return ret; - - if(!val.isInt()) - return ret; - - *value = (uint32_t)val.asInt(); - - return true; -} - -bool getBootParameterBlobValue(Json::Value& bootParamsObj, const char* key, std::vector& blob) { - bool ret = false; - Json::Value val = bootParamsObj[key]; - if(val.empty()) - return ret; - - if(!val.isString()) - return ret; - - std::string blobStr = hex2str(val.asString()); - - for(char ch : blobStr) { - blob.push_back((uint8_t)ch); - } - - return true; -} - -bool setAndroidSystemProperties() { - ErrorCode err = ErrorCode::OK; - bool ret = false; - if (ErrorCode::OK != (err = mProvision.setAndroidSystemProperties())) { - printf("\n set boot parameters failed with err:%d \n", (int32_t)err); - return ret; - } - printf("\n SE successfully accepted system properties.\n"); - return true; - -} - -bool setBootParameters(const char* filename) { - Json::Value bootParamsObj; - bool ret = false; - ErrorCode err = ErrorCode::OK; - keymaster::V4_1::javacard::BootParams bootParams; - - if(!parseJsonFile(filename)) - return ret; - - bootParamsObj = root.get("set_boot_params", bootParamsObj); - if (!bootParamsObj.isNull()) { - - if(!getBootParameterIntValue(bootParamsObj, "boot_patch_level", &bootParams.bootPatchLevel)) { - printf("\n Invalid value for boot_patch_level or boot_patch_level tag missing\n"); - return ret; - } - if(!getBootParameterBlobValue(bootParamsObj, "verified_boot_key", bootParams.verifiedBootKey)) { - printf("\n Invalid value for verified_boot_key or verified_boot_key tag missing\n"); - return ret; - } - if(!getBootParameterBlobValue(bootParamsObj, "verified_boot_key_hash", bootParams.verifiedBootKeyHash)) { - printf("\n Invalid value for verified_boot_key_hash or verified_boot_key_hash tag missing\n"); - return ret; - } - if(!getBootParameterIntValue(bootParamsObj, "boot_state", &bootParams.verifiedBootState)) { - printf("\n Invalid value for boot_state or boot_state tag missing\n"); - return ret; - } - if(!getBootParameterIntValue(bootParamsObj, "device_locked", &bootParams.deviceLocked)) { - printf("\n Invalid value for device_locked or device_locked tag missing\n"); - return ret; - } - - } else { - printf("\n Fail: Improper value found for set_boot_params key inside the json file\n"); - return ret; - } - - if (ErrorCode::OK != (err = mProvision.provisionBootParameters(bootParams))) { - printf("\n set boot parameters failed with err:%d \n", (int32_t)err); - return ret; - } - - printf("\n SE successfully accepted boot paramters \n"); - return true; -} - -bool provisionAttestationIds(const char *filename) { - Json::Value attestIds; - bool ret = false; - ErrorCode err = ErrorCode::OK; - keymaster::V4_1::javacard::AttestIDParams params; - - if(!parseJsonFile(filename)) - return ret; - - attestIds = root.get("attest_ids", attestIds); - if (!attestIds.isNull()) { - Json::Value value; - Json::Value::Members keys = attestIds.getMemberNames(); - for(std::string key : keys) { - value = attestIds[key]; - if(value.empty()) { - continue; - } - if (!value.isString()) { - printf("\n Fail: Value for each attest ids key should be a string in the json file \n"); - return ret; - } - - if (0 == key.compare("brand")) { - params.brand = value.asString(); - } else if(0 == key.compare("device")) { - params.device = value.asString(); - } else if(0 == key.compare("product")) { - params.product = value.asString(); - } else if(0 == key.compare("serial")) { - params.serial = value.asString(); - } else if(0 == key.compare("imei")) { - params.imei = value.asString(); - } else if(0 == key.compare("meid")) { - params.meid = value.asString(); - } else if(0 == key.compare("manufacturer")) { - params.manufacturer = value.asString(); - } else if(0 == key.compare("model")) { - params.model = value.asString(); - } else { - printf("\n unknown attestation id key:%s \n", key.c_str()); - return ret; - } - } - - if (ErrorCode::OK != (err = mProvision.provisionAttestationID(params))) { - printf("\n Provision attestationID parameters failed with err:%d \n", (int32_t)err); - return ret; - } - } else { - printf("\n Fail: Improper value found for attest_ids key inside the json file \n"); - return ret; - } - printf("\n provisioned attestation ids successfully \n"); - return true; -} - -bool lockProvision() { - ErrorCode errorCode; - bool ret = false; - - if(ErrorCode::OK != (errorCode = mProvision.lockProvision())) { - printf("\n Failed to lock provisioning error: %d\n", uint32_t(errorCode)); - return ret; - } - printf("\n Successfully locked provisioning process. Now SE doesn't accept any further provision commands. \n"); - return true; -} - -bool getProvisionStatus() { - bool ret = false; - uint64_t status; - if (ErrorCode::OK != mProvision.getProvisionStatus(status)) { - return ret; - } - if ( (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_KEY)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_BOOT_PARAM))) { - printf("\n SE is provisioned \n"); - } else { - if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_KEY)) { - printf("\n Attestation key is not provisioned \n"); - } - if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) { - printf("\n Attestation certificate chain is not provisioned \n"); - } - if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) { - printf("\n Attestation certificate params are not provisioned \n"); - } - if (0 == (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) { - printf("\n Shared secret is not provisioned \n"); - } - if (0 == (status & ProvisionStatus::PROVISION_STATUS_BOOT_PARAM)) { - printf("\n Boot params are not provisioned \n"); - } - } - return true; -} - -bool provisionSharedSecret(const char* filename) { - Json::Value sharedSecret; - bool ret = false; - ErrorCode err = ErrorCode::OK; - - if(!parseJsonFile(filename)) - return ret; - - sharedSecret = root.get("shared_secret", sharedSecret); - if (!sharedSecret.isNull()) { - - if (!sharedSecret.isString()) { - printf("\n Fail: Value for shared secret key should be string inside the json file\n"); - return ret; - } - std::string secret = hex2str(sharedSecret.asString()); - std::vector data(secret.begin(), secret.end()); - if(ErrorCode::OK != (err = mProvision.provisionPreSharedSecret(data))) { - printf("\n Provision pre-shared secret failed with err:%d \n", (int32_t)err); - return ret; - } - } else { - printf("\n Fail: Improper value for shared_secret key inside the json file\n"); - return ret; - } - printf("\n Provisioned shared secret successfully \n"); - return true; -} - -static bool provisionAttestationKey(const char* filename) { - Json::Value keyFile; - bool ret = false; - ErrorCode err = ErrorCode::OK; - - if(!parseJsonFile(filename)) - return ret; - - keyFile = root.get("attest_key", keyFile); - if (!keyFile.isNull()) { - std::vector data; - - std::string keyFileName = keyFile.asString(); - if(!readDataFromFile(keyFileName.data(), data)) { - printf("\n Failed to read the Root ec key\n"); - return ret; - } - if(ErrorCode::OK != (err = mProvision.provisionAttestationKey(data))) { - printf("\n Provision attestation key failed with error: %d\n", (int32_t)err); - return ret; - } - } else { - printf("\n Improper value for attest_key in json file \n"); - return ret; - } - printf("\n Provisioned attestation key successfully\n"); - return true; -} - -bool provisionAttestationCertificateChain(const char* filename) { - Json::Value certChainFile; - bool ret = false; - ErrorCode err = ErrorCode::OK; - - if(!parseJsonFile(filename)) - return ret; - - certChainFile = root.get("attest_cert_chain", certChainFile); - if (!certChainFile.isNull()) { - std::vector> certData; - - if(certChainFile.isArray()) { - for (int i = 0; i < certChainFile.size(); i++) { - std::vector tmp; - if(certChainFile[i].isString()) { - /* Read the certificates. */ - if(!readDataFromFile(certChainFile[i].asString().data(), tmp)) { - printf("\n Failed to read the Root certificate\n"); - return ret; - } - certData.push_back(std::move(tmp)); - } else { - printf("\n Fail: Only proper certificate paths as a string is allowed inside the json file. \n"); - return ret; - } - } - } else { - printf("\n Fail: cert chain value should be an array inside the json file. \n"); - return ret; - } - if (ErrorCode::OK != (err = mProvision.provisionAtestationCertificateChain(certData))) { - printf("\n Provision certificate chain failed with error: %d\n", (int32_t)err); - return ret; - } - } else { - printf("\n Fail: Improper value found for attest_cert_chain key inside json file \n"); - return ret; - } - printf("\n Provisioned attestation certificate chain successfully\n"); - return true; -} - -bool provisionAttestationCertificateParams(const char* filename) { - Json::Value certChainFile; - bool ret = false; - ErrorCode err = ErrorCode::OK; - - if(!parseJsonFile(filename)) - return ret; - - certChainFile = root.get("attest_cert_chain", certChainFile); - if (!certChainFile.isNull()) { - std::vector> certData; - - if(certChainFile.isArray()) { - if (certChainFile.size() == 0) { - return ret; - } - std::vector tmp; - if(!readDataFromFile(certChainFile[0].asString().data(), tmp)) { - printf("\n Failed to read the Root certificate\n"); - return ret; - } - if (ErrorCode::OK != (err = mProvision.provisionAttestationCertificateParams(tmp))) { - printf("\n Provision certificate params failed with error: %d\n", (int32_t)err); - return ret; - } - } else { - printf("\n Fail: cert chain value should be an array inside the json file. \n"); - return ret; - } - } else { - printf("\n Fail: Improper value found for attest_cert_chain key inside json file \n"); - return ret; - } - printf("\n Provisioned attestation certificate parameters successfully\n"); - return true; -} - -bool provision(const char* filename) { - - if(!provisionAttestationKey(filename)) { - return false; - } - if(!provisionAttestationCertificateChain(filename)) { - return false; - } - if(!provisionAttestationCertificateParams(filename)) { - return false; - } - if(!provisionSharedSecret(filename)) { - return false; - } - if(!provisionAttestationIds(filename)) { - return false; - } - if(!setBootParameters(filename)) { - return false; - } - if(!setAndroidSystemProperties()) { - return false; - } - return true; -} - -bool parseJsonFile(const char* filename) { - std::stringstream buffer; - Json::Reader jsonReader; - - if(!root.empty()) { - printf("\n Already parsed \n"); - return true; - } - std::ifstream stream(filename); - buffer << stream.rdbuf(); - if(jsonReader.parse(buffer.str(), root)) { - printf("\n Parsed json file successfully\n"); - return true; - } else { - printf("\n Failed to parse json file\n"); - return false; - } -} - -int main(int argc, char* argv[]) -{ - int c; - struct option longOpts[] = { - {"all", required_argument, NULL, 'a'}, - {"attest_key", required_argument, NULL, 'k'}, - {"cert_chain", required_argument, NULL, 'c'}, - {"cert_params", required_argument, NULL,'p'}, - {"attest_ids", required_argument, NULL, 'i'}, - {"shared_secret", required_argument, NULL, 'r'}, - {"set_boot_params", required_argument, NULL, 'b'}, - {"set_system_properties", no_argument, NULL, 'e'}, - {"provision_status", no_argument, NULL, 's'}, - {"lock_provision", no_argument, NULL, 'l'}, - {"help", no_argument, NULL, 'h'}, - {0,0,0,0} - }; - - if (argc <= 1) { - printf("\n Invalid command \n"); - usage(); - } - /* Initialize provision */ - mProvision.init(); - - /* getopt_long stores the option index here. */ - while ((c = getopt_long(argc, argv, ":slhea:k:c:p:i:r:b:", longOpts, NULL)) != -1) { - switch(c) { - case 'a': - //all - if(!provision(optarg)) - printf("\n Failed to provision the device \n"); - break; - case 'k': - //attest key - if(!provisionAttestationKey(optarg)) - printf("\n Failed to provision attestaion key\n"); - break; - case 'c': - //attest certchain - if(!provisionAttestationCertificateChain(optarg)) - printf("\n Failed to provision attestaion certificate chain\n"); - break; - case 'p': - //attest cert params - if(!provisionAttestationCertificateParams(optarg)) - printf("\n Failed to provision attestaion certificate paramaters\n"); - break; - case 'i': - //attestation ids. - if(!provisionAttestationIds(optarg)) - printf("\n Failed to provision attestaion ids\n"); - break; - //shared secret - case 'r': - if(!provisionSharedSecret(optarg)) - printf("\n Failed to provision shared secret\n"); - break; - case 'b': - //set boot params - if(!setBootParameters(optarg)) - printf("\n Failed to set boot parameters.\n"); - break; - case 'e': - //set Android system properties - if(!setAndroidSystemProperties()) - printf("\n Failed to set android system properties.\n"); - break; - case 's': - if(!getProvisionStatus()) - printf("\n Failed to get provision status \n"); - break; - case 'l': - lockProvision(); - break; - case 'h': - usage(); - break; - case ':': - printf("\n missing argument\n"); - usage(); - break; - case '?': - default: - printf("\n Invalid option\n"); - usage(); - break; - } - } - if(optind < argc) { - usage(); - } - /*Uninitalize */ - mProvision.uninit(); - return 0; -} diff --git a/ProvisioningTool/README.md b/ProvisioningTool/README.md index 9d65e5d1..e7eaef20 100644 --- a/ProvisioningTool/README.md +++ b/ProvisioningTool/README.md @@ -1,12 +1,18 @@ # Provisioning tool -This directory contains provisioning tool which helps in provisioning -the secure element by using the APIs exposed by Provision library. -This tool takes the input parameters from json file. +This directory contains two tools. One which constructs the apdus and dumps them to a json file, Other which gets the apuds from the json file and provision them into a secure element simulator. Both the tools can be compiled and executed from a Linux machine. -#### Build -This tool can be built along with aosp build. It has dependency on -[libjc_common](../HAL/keymaster/Android.bp) and -[libjc_provision](Android.bp). +#### Build instruction +The default target generates both the executables. One construct_apdus and the other provision. +$ make +Individual targets can also be selected as shown below +$ make construct_apdus +$ make provision +Make clean will remove all the object files and binaries +$ make clean + +#### Environment setup +Before executing the binaries make sure LD_LIBRARY_PATH is set +export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH #### Sample resources for quick testing Two sample json files are located in this directory with names @@ -17,18 +23,23 @@ keys can be found in [test_resources](test_resources) directory. Copy the certificates and the key into the emulator/device filesystem in their respective paths mentioned in the sample json file. -#### Usage +#### Usage for construct_apdus
-Usage: provision_tool options
+Usage: construct_apdus options
 Valid options are:
 -h, --help                        show the help message and exit.
--a, --all jsonFile                Executes all the provision commands.
--k, --attest_key jsonFile         Provision attestation key.
--c, --cert_chain jsonFile         Provision attestation certificate chain.
--p, --cert_params jsonFile        Provision attestation certificate parameters.
--i, --attest_ids jsonFile         Provision attestation IDs.
--r, --shared_secret jsonFile      Provision pre-shared secret.
--b, --set_boot_params jsonFile    Provision boot parameters.
--s, --provision_stautus           Prints the current provision status.
--l, --lock_provision              Locks the provision commands.
+-v, --km_version version Version of the keymaster (4.1 for keymaster; 5.0 for keymint)
+-i, --input  jsonFile	 Input json file
+-o, --output jsonFile 	 Output json file
+
+ +#### Usage for provision +
+Usage: provision options
+Valid options are:
+-h, --help                      show the help message and exit.
+-v, --km_version version  Version of the keymaster (4.1 for keymaster; 5.0 for keymint)
+-i, --input  jsonFile 	  Input json file 
+-s, --provision_stautus   Prints the current provision status.
+-l, --lock_provision      Locks the provision state.
 
diff --git a/ProvisioningTool/include/UniquePtr.h b/ProvisioningTool/include/UniquePtr.h new file mode 100644 index 00000000..da74780b --- /dev/null +++ b/ProvisioningTool/include/UniquePtr.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010 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. + */ + +#pragma once + +#include // for size_t + +#include + +// Default deleter for pointer types. +template struct DefaultDelete { + enum { type_must_be_complete = sizeof(T) }; + DefaultDelete() {} + void operator()(T* p) const { delete p; } +}; + +// Default deleter for array types. +template struct DefaultDelete { + enum { type_must_be_complete = sizeof(T) }; + void operator()(T* p) const { delete[] p; } +}; + +template > +using UniquePtr = std::unique_ptr; + + diff --git a/ProvisioningTool/include/constants.h b/ProvisioningTool/include/constants.h new file mode 100644 index 00000000..36f30bd8 --- /dev/null +++ b/ProvisioningTool/include/constants.h @@ -0,0 +1,101 @@ +/* + ** + ** Copyright 2021, 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. + */ +#pragma once + +#include +#include +#include +#include +#include +#include "UniquePtr.h" + +#define SUCCESS 0 +#define FAILURE 1 +#define KEYMASTER_VERSION 4.1 +#define KEYMINT_VERSION 5 +#define P1_40 0x40 +#define P1_50 0x50 +#define APDU_CLS 0x80 +#define APDU_P1(version) \ + (version == KEYMASTER_VERSION) ? \ + P1_40 : \ + P1_50 +#define APDU_P2 0x00 +#define INS_BEGIN_KM_CMD 0x00 +#define APDU_RESP_STATUS_OK 0x9000 +#define SE_POWER_RESET_STATUS_FLAG ( 1 << 30) + + + +template +struct OpenSslObjectDeleter { + void operator()(T* p) { FreeFunc(p); } +}; + +#define DEFINE_OPENSSL_OBJECT_POINTER(name) \ + typedef OpenSslObjectDeleter name##_Delete; \ + typedef UniquePtr name##_Ptr; + +DEFINE_OPENSSL_OBJECT_POINTER(EC_KEY) +DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY) +DEFINE_OPENSSL_OBJECT_POINTER(X509) + + +// Tags +constexpr uint64_t kTagAlgorithm = 268435458u; +constexpr uint64_t kTagDigest = 536870917u; +constexpr uint64_t kTagCurve = 268435466u; +constexpr uint64_t kTagPurpose = 536870913u; +constexpr uint64_t kTagAttestationIdBrand = 2415919814u; +constexpr uint64_t kTagAttestationIdDevice = 2415919815u; +constexpr uint64_t kTagAttestationIdProduct = 2415919816u; +constexpr uint64_t kTagAttestationIdSerial = 2415919817u; +constexpr uint64_t kTagAttestationIdImei = 2415919818u; +constexpr uint64_t kTagAttestationIdMeid = 2415919819u; +constexpr uint64_t kTagAttestationIdManufacturer = 2415919820u; +constexpr uint64_t kTagAttestationIdModel = 2415919821u; + +// Values +constexpr uint64_t kCurveP256 = 1; +constexpr uint64_t kAlgorithmEc = 3; +constexpr uint64_t kDigestSha256 = 4; +constexpr uint64_t kPurposeAttest = 0x7F; +constexpr uint64_t kKeyFormatRaw = 3; + +// json keys +constexpr char kAttestKey[] = "attest_key"; +constexpr char kAttestCertChain[] = "attest_cert_chain"; +constexpr char kAttestCertParams[] = "attest_cert_params"; +constexpr char kSharedSecret[] = "shared_secret"; +constexpr char kBootParams[] = "boot_params"; +constexpr char kAttestationIds[] = "attestation_ids"; +constexpr char kDeviceUniqueKey[] = "device_unique_key"; +constexpr char kAdditionalCertChain[] = "additional_cert_chain"; +constexpr char kProvisionStatus[] = "provision_status"; +constexpr char kLockProvision[] = "lock_provision"; + +// Instruction constatnts +constexpr int kAttestationKeyCmd = INS_BEGIN_KM_CMD + 1; +constexpr int kAttestCertChainCmd = INS_BEGIN_KM_CMD + 2; +constexpr int kAttestCertParamsCmd = INS_BEGIN_KM_CMD + 3; +constexpr int kAttestationIdsCmd = INS_BEGIN_KM_CMD + 4; +constexpr int kPresharedSecretCmd = INS_BEGIN_KM_CMD + 5; +constexpr int kBootParamsCmd = INS_BEGIN_KM_CMD + 6; +constexpr int kLockProvisionCmd = INS_BEGIN_KM_CMD + 7; +constexpr int kGetProvisionStatusCmd = INS_BEGIN_KM_CMD + 8; +constexpr int kDeviceUniqueKeyCmd = INS_BEGIN_KM_CMD + 10; +constexpr int kAdditionalCertChainCmd = INS_BEGIN_KM_CMD + 11; \ No newline at end of file diff --git a/ProvisioningTool/include/cppbor/cppbor.h b/ProvisioningTool/include/cppbor/cppbor.h new file mode 100644 index 00000000..06976118 --- /dev/null +++ b/ProvisioningTool/include/cppbor/cppbor.h @@ -0,0 +1,1113 @@ +/* + * Copyright 2019 Google LLC + * + * 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 + * + * https://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. + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace cppbor { + +enum MajorType : uint8_t { + UINT = 0 << 5, + NINT = 1 << 5, + BSTR = 2 << 5, + TSTR = 3 << 5, + ARRAY = 4 << 5, + MAP = 5 << 5, + SEMANTIC = 6 << 5, + SIMPLE = 7 << 5, +}; + +enum SimpleType { + BOOLEAN, + NULL_T, // Only two supported, as yet. +}; + +enum SpecialAddlInfoValues : uint8_t { + FALSE = 20, + TRUE = 21, + NULL_V = 22, + ONE_BYTE_LENGTH = 24, + TWO_BYTE_LENGTH = 25, + FOUR_BYTE_LENGTH = 26, + EIGHT_BYTE_LENGTH = 27, +}; + +class Item; +class Uint; +class Nint; +class Int; +class Tstr; +class Bstr; +class Simple; +class Bool; +class Array; +class Map; +class Null; +class SemanticTag; +class EncodedItem; +class ViewTstr; +class ViewBstr; + +/** + * Returns the size of a CBOR header that contains the additional info value addlInfo. + */ +size_t headerSize(uint64_t addlInfo); + +/** + * Encodes a CBOR header with the specified type and additional info into the range [pos, end). + * Returns a pointer to one past the last byte written, or nullptr if there isn't sufficient space + * to write the header. + */ +uint8_t* encodeHeader(MajorType type, uint64_t addlInfo, uint8_t* pos, const uint8_t* end); + +using EncodeCallback = std::function; + +/** + * Encodes a CBOR header with the specified type and additional info, passing each byte in turn to + * encodeCallback. + */ +void encodeHeader(MajorType type, uint64_t addlInfo, EncodeCallback encodeCallback); + +/** + * Encodes a CBOR header witht he specified type and additional info, writing each byte to the + * provided OutputIterator. + */ +template ::iterator_category>>> +void encodeHeader(MajorType type, uint64_t addlInfo, OutputIterator iter) { + return encodeHeader(type, addlInfo, [&](uint8_t v) { *iter++ = v; }); +} + +/** + * Item represents a CBOR-encodeable data item. Item is an abstract interface with a set of virtual + * methods that allow encoding of the item or conversion to the appropriate derived type. + */ +class Item { + public: + virtual ~Item() {} + + /** + * Returns the CBOR type of the item. + */ + virtual MajorType type() const = 0; + + // These methods safely downcast an Item to the appropriate subclass. + virtual Int* asInt() { return nullptr; } + const Int* asInt() const { return const_cast(this)->asInt(); } + virtual Uint* asUint() { return nullptr; } + const Uint* asUint() const { return const_cast(this)->asUint(); } + virtual Nint* asNint() { return nullptr; } + const Nint* asNint() const { return const_cast(this)->asNint(); } + virtual Tstr* asTstr() { return nullptr; } + const Tstr* asTstr() const { return const_cast(this)->asTstr(); } + virtual Bstr* asBstr() { return nullptr; } + const Bstr* asBstr() const { return const_cast(this)->asBstr(); } + virtual Simple* asSimple() { return nullptr; } + const Simple* asSimple() const { return const_cast(this)->asSimple(); } + virtual Map* asMap() { return nullptr; } + const Map* asMap() const { return const_cast(this)->asMap(); } + virtual Array* asArray() { return nullptr; } + const Array* asArray() const { return const_cast(this)->asArray(); } + + virtual ViewTstr* asViewTstr() { return nullptr; } + const ViewTstr* asViewTstr() const { return const_cast(this)->asViewTstr(); } + virtual ViewBstr* asViewBstr() { return nullptr; } + const ViewBstr* asViewBstr() const { return const_cast(this)->asViewBstr(); } + + // Like those above, these methods safely downcast an Item when it's actually a SemanticTag. + // However, if you think you want to use these methods, you probably don't. Typically, the way + // you should handle tagged Items is by calling the appropriate method above (e.g. asInt()) + // which will return a pointer to the tagged Item, rather than the tag itself. If you want to + // find out if the Item* you're holding is to something with one or more tags applied, see + // semanticTagCount() and semanticTag() below. + virtual SemanticTag* asSemanticTag() { return nullptr; } + const SemanticTag* asSemanticTag() const { return const_cast(this)->asSemanticTag(); } + + /** + * Returns the number of semantic tags prefixed to this Item. + */ + virtual size_t semanticTagCount() const { return 0; } + + /** + * Returns the semantic tag at the specified nesting level `nesting`, iff `nesting` is less than + * the value returned by semanticTagCount(). + * + * CBOR tags are "nested" by applying them in sequence. The "rightmost" tag is the "inner" tag. + * That is, given: + * + * 4(5(6("AES"))) which encodes as C1 C2 C3 63 414553 + * + * The tstr "AES" is tagged with 6. The combined entity ("AES" tagged with 6) is tagged with 5, + * etc. So in this example, semanticTagCount() would return 3, and semanticTag(0) would return + * 5 semanticTag(1) would return 5 and semanticTag(2) would return 4. For values of n > 2, + * semanticTag(n) will return 0, but this is a meaningless value. + * + * If this layering is confusing, you probably don't have to worry about it. Nested tagging does + * not appear to be common, so semanticTag(0) is the only one you'll use. + */ + virtual uint64_t semanticTag(size_t /* nesting */ = 0) const { return 0; } + + /** + * Returns true if this is a "compound" item, i.e. one that contains one or more other items. + */ + virtual bool isCompound() const { return false; } + + bool operator==(const Item& other) const&; + bool operator!=(const Item& other) const& { return !(*this == other); } + + /** + * Returns the number of bytes required to encode this Item into CBOR. Note that if this is a + * complex Item, calling this method will require walking the whole tree. + */ + virtual size_t encodedSize() const = 0; + + /** + * Encodes the Item into buffer referenced by range [*pos, end). Returns a pointer to one past + * the last position written. Returns nullptr if there isn't enough space to encode. + */ + virtual uint8_t* encode(uint8_t* pos, const uint8_t* end) const = 0; + + /** + * Encodes the Item by passing each encoded byte to encodeCallback. + */ + virtual void encode(EncodeCallback encodeCallback) const = 0; + + /** + * Clones the Item + */ + virtual std::unique_ptr clone() const = 0; + + /** + * Encodes the Item into the provided OutputIterator. + */ + template ::iterator_category> + void encode(OutputIterator i) const { + return encode([&](uint8_t v) { *i++ = v; }); + } + + /** + * Encodes the Item into a new std::vector. + */ + std::vector encode() const { + std::vector retval; + retval.reserve(encodedSize()); + encode(std::back_inserter(retval)); + return retval; + } + + /** + * Encodes the Item into a new std::string. + */ + std::string toString() const { + std::string retval; + retval.reserve(encodedSize()); + encode([&](uint8_t v) { retval.push_back(v); }); + return retval; + } + + /** + * Encodes only the header of the Item. + */ + inline uint8_t* encodeHeader(uint64_t addlInfo, uint8_t* pos, const uint8_t* end) const { + return ::cppbor::encodeHeader(type(), addlInfo, pos, end); + } + + /** + * Encodes only the header of the Item. + */ + inline void encodeHeader(uint64_t addlInfo, EncodeCallback encodeCallback) const { + ::cppbor::encodeHeader(type(), addlInfo, encodeCallback); + } +}; + +/** + * EncodedItem represents a bit of already-encoded CBOR. Caveat emptor: It does no checking to + * ensure that the provided data is a valid encoding, cannot be meaninfully-compared with other + * kinds of items and you cannot use the as*() methods to find out what's inside it. + */ +class EncodedItem : public Item { + public: + explicit EncodedItem(std::vector value) : mValue(std::move(value)) {} + + bool operator==(const EncodedItem& other) const& { return mValue == other.mValue; } + + // Type can't be meaningfully-obtained. We could extract the type from the first byte and return + // it, but you can't do any of the normal things with an EncodedItem so there's no point. + MajorType type() const override { + assert(false); + return static_cast(-1); + } + size_t encodedSize() const override { return mValue.size(); } + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + if (end - pos < static_cast(mValue.size())) return nullptr; + return std::copy(mValue.begin(), mValue.end(), pos); + } + void encode(EncodeCallback encodeCallback) const override { + std::for_each(mValue.begin(), mValue.end(), encodeCallback); + } + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + std::vector mValue; +}; + +/** + * Int is an abstraction that allows Uint and Nint objects to be manipulated without caring about + * the sign. + */ +class Int : public Item { + public: + bool operator==(const Int& other) const& { return value() == other.value(); } + + virtual int64_t value() const = 0; + using Item::asInt; + Int* asInt() override { return this; } +}; + +/** + * Uint is a concrete Item that implements CBOR major type 0. + */ +class Uint : public Int { + public: + static constexpr MajorType kMajorType = UINT; + + explicit Uint(uint64_t v) : mValue(v) {} + + bool operator==(const Uint& other) const& { return mValue == other.mValue; } + + MajorType type() const override { return kMajorType; } + using Item::asUint; + Uint* asUint() override { return this; } + + size_t encodedSize() const override { return headerSize(mValue); } + + int64_t value() const override { return mValue; } + uint64_t unsignedValue() const { return mValue; } + + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + return encodeHeader(mValue, pos, end); + } + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mValue, encodeCallback); + } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + uint64_t mValue; +}; + +/** + * Nint is a concrete Item that implements CBOR major type 1. + + * Note that it is incapable of expressing the full range of major type 1 values, becaue it can only + * express values that fall into the range [std::numeric_limits::min(), -1]. It cannot + * express values in the range [std::numeric_limits::min() - 1, + * -std::numeric_limits::max()]. + */ +class Nint : public Int { + public: + static constexpr MajorType kMajorType = NINT; + + explicit Nint(int64_t v); + + bool operator==(const Nint& other) const& { return mValue == other.mValue; } + + MajorType type() const override { return kMajorType; } + using Item::asNint; + Nint* asNint() override { return this; } + size_t encodedSize() const override { return headerSize(addlInfo()); } + + int64_t value() const override { return mValue; } + + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + return encodeHeader(addlInfo(), pos, end); + } + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(addlInfo(), encodeCallback); + } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + uint64_t addlInfo() const { return -1ll - mValue; } + + int64_t mValue; +}; + +/** + * Bstr is a concrete Item that implements major type 2. + */ +class Bstr : public Item { + public: + static constexpr MajorType kMajorType = BSTR; + + // Construct an empty Bstr + explicit Bstr() {} + + // Construct from a vector + explicit Bstr(std::vector v) : mValue(std::move(v)) {} + + // Construct from a string + explicit Bstr(const std::string& v) + : mValue(reinterpret_cast(v.data()), + reinterpret_cast(v.data()) + v.size()) {} + + // Construct from a pointer/size pair + explicit Bstr(const std::pair& buf) + : mValue(buf.first, buf.first + buf.second) {} + + // Construct from a pair of iterators + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + explicit Bstr(const std::pair& pair) : mValue(pair.first, pair.second) {} + + // Construct from an iterator range. + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + Bstr(I1 begin, I2 end) : mValue(begin, end) {} + + bool operator==(const Bstr& other) const& { return mValue == other.mValue; } + + MajorType type() const override { return kMajorType; } + using Item::asBstr; + Bstr* asBstr() override { return this; } + size_t encodedSize() const override { return headerSize(mValue.size()) + mValue.size(); } + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mValue.size(), encodeCallback); + encodeValue(encodeCallback); + } + + const std::vector& value() const { return mValue; } + std::vector&& moveValue() { return std::move(mValue); } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + void encodeValue(EncodeCallback encodeCallback) const; + + std::vector mValue; +}; + +/** + * ViewBstr is a read-only version of Bstr backed by std::string_view + */ +class ViewBstr : public Item { + public: + static constexpr MajorType kMajorType = BSTR; + + // Construct an empty ViewBstr + explicit ViewBstr() {} + + // Construct from a string_view of uint8_t values + explicit ViewBstr(std::basic_string_view v) : mView(std::move(v)) {} + + // Construct from a string_view + explicit ViewBstr(std::string_view v) + : mView(reinterpret_cast(v.data()), v.size()) {} + + // Construct from an iterator range + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + ViewBstr(I1 begin, I2 end) : mView(begin, end) {} + + // Construct from a uint8_t pointer pair + ViewBstr(const uint8_t* begin, const uint8_t* end) + : mView(begin, std::distance(begin, end)) {} + + bool operator==(const ViewBstr& other) const& { return mView == other.mView; } + + MajorType type() const override { return kMajorType; } + using Item::asViewBstr; + ViewBstr* asViewBstr() override { return this; } + size_t encodedSize() const override { return headerSize(mView.size()) + mView.size(); } + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mView.size(), encodeCallback); + encodeValue(encodeCallback); + } + + const std::basic_string_view& view() const { return mView; } + + std::unique_ptr clone() const override { return std::make_unique(mView); } + + private: + void encodeValue(EncodeCallback encodeCallback) const; + + std::basic_string_view mView; +}; + +/** + * Tstr is a concrete Item that implements major type 3. + */ +class Tstr : public Item { + public: + static constexpr MajorType kMajorType = TSTR; + + // Construct from a string + explicit Tstr(std::string v) : mValue(std::move(v)) {} + + // Construct from a string_view + explicit Tstr(const std::string_view& v) : mValue(v) {} + + // Construct from a C string + explicit Tstr(const char* v) : mValue(std::string(v)) {} + + // Construct from a pair of iterators + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + explicit Tstr(const std::pair& pair) : mValue(pair.first, pair.second) {} + + // Construct from an iterator range + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + Tstr(I1 begin, I2 end) : mValue(begin, end) {} + + bool operator==(const Tstr& other) const& { return mValue == other.mValue; } + + MajorType type() const override { return kMajorType; } + using Item::asTstr; + Tstr* asTstr() override { return this; } + size_t encodedSize() const override { return headerSize(mValue.size()) + mValue.size(); } + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mValue.size(), encodeCallback); + encodeValue(encodeCallback); + } + + const std::string& value() const { return mValue; } + std::string&& moveValue() { return std::move(mValue); } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + void encodeValue(EncodeCallback encodeCallback) const; + + std::string mValue; +}; + +/** + * ViewTstr is a read-only version of Tstr backed by std::string_view + */ +class ViewTstr : public Item { + public: + static constexpr MajorType kMajorType = TSTR; + + // Construct an empty ViewTstr + explicit ViewTstr() {} + + // Construct from a string_view + explicit ViewTstr(std::string_view v) : mView(std::move(v)) {} + + // Construct from an iterator range + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + ViewTstr(I1 begin, I2 end) : mView(begin, end) {} + + // Construct from a uint8_t pointer pair + ViewTstr(const uint8_t* begin, const uint8_t* end) + : mView(reinterpret_cast(begin), + std::distance(begin, end)) {} + + bool operator==(const ViewTstr& other) const& { return mView == other.mView; } + + MajorType type() const override { return kMajorType; } + using Item::asViewTstr; + ViewTstr* asViewTstr() override { return this; } + size_t encodedSize() const override { return headerSize(mView.size()) + mView.size(); } + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mView.size(), encodeCallback); + encodeValue(encodeCallback); + } + + const std::string_view& view() const { return mView; } + + std::unique_ptr clone() const override { return std::make_unique(mView); } + + private: + void encodeValue(EncodeCallback encodeCallback) const; + + std::string_view mView; +}; + +/* + * Array is a concrete Item that implements CBOR major type 4. + * + * Note that Arrays are not copyable. This is because copying them is expensive and making them + * move-only ensures that they're never copied accidentally. If you actually want to copy an Array, + * use the clone() method. + */ +class Array : public Item { + public: + static constexpr MajorType kMajorType = ARRAY; + + Array() = default; + Array(const Array& other) = delete; + Array(Array&&) = default; + Array& operator=(const Array&) = delete; + Array& operator=(Array&&) = default; + + bool operator==(const Array& other) const&; + + /** + * Construct an Array from a variable number of arguments of different types. See + * details::makeItem below for details on what types may be provided. In general, this accepts + * all of the types you'd expect and doest the things you'd expect (integral values are addes as + * Uint or Nint, std::string and char* are added as Tstr, bools are added as Bool, etc.). + */ + template + Array(Args&&... args); + + /** + * Append a single element to the Array, of any compatible type. + */ + template + Array& add(T&& v) &; + template + Array&& add(T&& v) &&; + + bool isCompound() const override { return true; } + + virtual size_t size() const { return mEntries.size(); } + + size_t encodedSize() const override { + return std::accumulate(mEntries.begin(), mEntries.end(), headerSize(size()), + [](size_t sum, auto& entry) { return sum + entry->encodedSize(); }); + } + + using Item::encode; // Make base versions visible. + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override; + + const std::unique_ptr& operator[](size_t index) const { return get(index); } + std::unique_ptr& operator[](size_t index) { return get(index); } + + const std::unique_ptr& get(size_t index) const { return mEntries[index]; } + std::unique_ptr& get(size_t index) { return mEntries[index]; } + + MajorType type() const override { return kMajorType; } + using Item::asArray; + Array* asArray() override { return this; } + + std::unique_ptr clone() const override; + + auto begin() { return mEntries.begin(); } + auto begin() const { return mEntries.begin(); } + auto end() { return mEntries.end(); } + auto end() const { return mEntries.end(); } + + protected: + std::vector> mEntries; +}; + +/* + * Map is a concrete Item that implements CBOR major type 5. + * + * Note that Maps are not copyable. This is because copying them is expensive and making them + * move-only ensures that they're never copied accidentally. If you actually want to copy a + * Map, use the clone() method. + */ +class Map : public Item { + public: + static constexpr MajorType kMajorType = MAP; + + using entry_type = std::pair, std::unique_ptr>; + + Map() = default; + Map(const Map& other) = delete; + Map(Map&&) = default; + Map& operator=(const Map& other) = delete; + Map& operator=(Map&&) = default; + + bool operator==(const Map& other) const&; + + /** + * Construct a Map from a variable number of arguments of different types. An even number of + * arguments must be provided (this is verified statically). See details::makeItem below for + * details on what types may be provided. In general, this accepts all of the types you'd + * expect and doest the things you'd expect (integral values are addes as Uint or Nint, + * std::string and char* are added as Tstr, bools are added as Bool, etc.). + */ + template + Map(Args&&... args); + + /** + * Append a key/value pair to the Map, of any compatible types. + */ + template + Map& add(Key&& key, Value&& value) &; + template + Map&& add(Key&& key, Value&& value) &&; + + bool isCompound() const override { return true; } + + virtual size_t size() const { return mEntries.size(); } + + size_t encodedSize() const override { + return std::accumulate( + mEntries.begin(), mEntries.end(), headerSize(size()), [](size_t sum, auto& entry) { + return sum + entry.first->encodedSize() + entry.second->encodedSize(); + }); + } + + using Item::encode; // Make base versions visible. + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override; + + /** + * Find and return the value associated with `key`, if any. + * + * If the searched-for `key` is not present, returns `nullptr`. + * + * Note that if the map is canonicalized (sorted), Map::get() peforms a binary search. If your + * map is large and you're searching in it many times, it may be worthwhile to canonicalize it + * to make Map::get() faster. Any use of a method that might modify the map disables the + * speedup. + */ + template + const std::unique_ptr& get(Key key) const; + + // Note that use of non-const operator[] marks the map as not canonicalized. + auto& operator[](size_t index) { + mCanonicalized = false; + return mEntries[index]; + } + const auto& operator[](size_t index) const { return mEntries[index]; } + + MajorType type() const override { return kMajorType; } + using Item::asMap; + Map* asMap() override { return this; } + + /** + * Sorts the map in canonical order, as defined in RFC 7049. Use this before encoding if you + * want canonicalization; cppbor does not canonicalize by default, though the integer encodings + * are always canonical and cppbor does not support indefinite-length encodings, so map order + * canonicalization is the only thing that needs to be done. + * + * @param recurse If set to true, canonicalize() will also walk the contents of the map and + * canonicalize any contained maps as well. + */ + Map& canonicalize(bool recurse = false) &; + Map&& canonicalize(bool recurse = false) && { + canonicalize(recurse); + return std::move(*this); + } + + bool isCanonical() { return mCanonicalized; } + + std::unique_ptr clone() const override; + + auto begin() { + mCanonicalized = false; + return mEntries.begin(); + } + auto begin() const { return mEntries.begin(); } + auto end() { + mCanonicalized = false; + return mEntries.end(); + } + auto end() const { return mEntries.end(); } + + // Returns true if a < b, per CBOR map key canonicalization rules. + static bool keyLess(const Item* a, const Item* b); + + protected: + std::vector mEntries; + + private: + bool mCanonicalized = false; +}; + +class SemanticTag : public Item { + public: + static constexpr MajorType kMajorType = SEMANTIC; + + template + SemanticTag(uint64_t tagValue, T&& taggedItem); + SemanticTag(const SemanticTag& other) = delete; + SemanticTag(SemanticTag&&) = default; + SemanticTag& operator=(const SemanticTag& other) = delete; + SemanticTag& operator=(SemanticTag&&) = default; + + bool operator==(const SemanticTag& other) const& { + return mValue == other.mValue && *mTaggedItem == *other.mTaggedItem; + } + + bool isCompound() const override { return true; } + + virtual size_t size() const { return 1; } + + // Encoding returns the tag + enclosed Item. + size_t encodedSize() const override { return headerSize(mValue) + mTaggedItem->encodedSize(); } + + using Item::encode; // Make base versions visible. + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override; + + // type() is a bit special. In normal usage it should return the wrapped type, but during + // parsing when we haven't yet parsed the tagged item, it needs to return SEMANTIC. + MajorType type() const override { return mTaggedItem ? mTaggedItem->type() : SEMANTIC; } + using Item::asSemanticTag; + SemanticTag* asSemanticTag() override { return this; } + + // Type information reflects the enclosed Item. Note that if the immediately-enclosed Item is + // another tag, these methods will recurse down to the non-tag Item. + using Item::asInt; + Int* asInt() override { return mTaggedItem->asInt(); } + using Item::asUint; + Uint* asUint() override { return mTaggedItem->asUint(); } + using Item::asNint; + Nint* asNint() override { return mTaggedItem->asNint(); } + using Item::asTstr; + Tstr* asTstr() override { return mTaggedItem->asTstr(); } + using Item::asBstr; + Bstr* asBstr() override { return mTaggedItem->asBstr(); } + using Item::asSimple; + Simple* asSimple() override { return mTaggedItem->asSimple(); } + using Item::asMap; + Map* asMap() override { return mTaggedItem->asMap(); } + using Item::asArray; + Array* asArray() override { return mTaggedItem->asArray(); } + using Item::asViewTstr; + ViewTstr* asViewTstr() override { return mTaggedItem->asViewTstr(); } + using Item::asViewBstr; + ViewBstr* asViewBstr() override { return mTaggedItem->asViewBstr(); } + + std::unique_ptr clone() const override; + + size_t semanticTagCount() const override; + uint64_t semanticTag(size_t nesting = 0) const override; + + protected: + SemanticTag() = default; + SemanticTag(uint64_t value) : mValue(value) {} + uint64_t mValue; + std::unique_ptr mTaggedItem; +}; + +/** + * Simple is abstract Item that implements CBOR major type 7. It is intended to be subclassed to + * create concrete Simple types. At present only Bool is provided. + */ +class Simple : public Item { + public: + static constexpr MajorType kMajorType = SIMPLE; + + bool operator==(const Simple& other) const&; + + virtual SimpleType simpleType() const = 0; + MajorType type() const override { return kMajorType; } + + Simple* asSimple() override { return this; } + + virtual const Bool* asBool() const { return nullptr; }; + virtual const Null* asNull() const { return nullptr; }; +}; + +/** + * Bool is a concrete type that implements CBOR major type 7, with additional item values for TRUE + * and FALSE. + */ +class Bool : public Simple { + public: + static constexpr SimpleType kSimpleType = BOOLEAN; + + explicit Bool(bool v) : mValue(v) {} + + bool operator==(const Bool& other) const& { return mValue == other.mValue; } + + SimpleType simpleType() const override { return kSimpleType; } + const Bool* asBool() const override { return this; } + + size_t encodedSize() const override { return 1; } + + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + return encodeHeader(mValue ? TRUE : FALSE, pos, end); + } + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mValue ? TRUE : FALSE, encodeCallback); + } + + bool value() const { return mValue; } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + bool mValue; +}; + +/** + * Null is a concrete type that implements CBOR major type 7, with additional item value for NULL + */ +class Null : public Simple { + public: + static constexpr SimpleType kSimpleType = NULL_T; + + explicit Null() {} + + SimpleType simpleType() const override { return kSimpleType; } + const Null* asNull() const override { return this; } + + size_t encodedSize() const override { return 1; } + + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + return encodeHeader(NULL_V, pos, end); + } + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(NULL_V, encodeCallback); + } + + std::unique_ptr clone() const override { return std::make_unique(); } +}; + +/** + * Returns pretty-printed CBOR for |item| + * + * If a byte-string is larger than |maxBStrSize| its contents will not be printed, instead the value + * of the form "" will be + * printed. Pass zero for |maxBStrSize| to disable this. + * + * The |mapKeysToNotPrint| parameter specifies the name of map values to not print. This is useful + * for unit tests. + */ +std::string prettyPrint(const Item* item, size_t maxBStrSize = 32, + const std::vector& mapKeysNotToPrint = {}); + +/** + * Returns pretty-printed CBOR for |value|. + * + * Only valid CBOR should be passed to this function. + * + * If a byte-string is larger than |maxBStrSize| its contents will not be printed, instead the value + * of the form "" will be + * printed. Pass zero for |maxBStrSize| to disable this. + * + * The |mapKeysToNotPrint| parameter specifies the name of map values to not print. This is useful + * for unit tests. + */ +std::string prettyPrint(const std::vector& encodedCbor, size_t maxBStrSize = 32, + const std::vector& mapKeysNotToPrint = {}); + +/** + * Details. Mostly you shouldn't have to look below, except perhaps at the docstring for makeItem. + */ +namespace details { + +template +struct is_iterator_pair_over : public std::false_type {}; + +template +struct is_iterator_pair_over< + std::pair, V, + typename std::enable_if_t::value_type>>> + : public std::true_type {}; + +template +struct is_unique_ptr_of_subclass_of_v : public std::false_type {}; + +template +struct is_unique_ptr_of_subclass_of_v, + typename std::enable_if_t>> + : public std::true_type {}; + +/* check if type is one of std::string (1), std::string_view (2), null-terminated char* (3) or pair + * of iterators (4)*/ +template +struct is_text_type_v : public std::false_type {}; + +template +struct is_text_type_v< + T, typename std::enable_if_t< + /* case 1 */ // + std::is_same_v>, std::string> + /* case 2 */ // + || std::is_same_v>, std::string_view> + /* case 3 */ // + || std::is_same_v>, char*> // + || std::is_same_v>, const char*> + /* case 4 */ + || details::is_iterator_pair_over::value>> : public std::true_type {}; + +/** + * Construct a unique_ptr from many argument types. Accepts: + * + * (a) booleans; + * (b) integers, all sizes and signs; + * (c) text strings, as defined by is_text_type_v above; + * (d) byte strings, as std::vector(d1), pair of iterators (d2) or pair + * (d3); and + * (e) Item subclass instances, including Array and Map. Items may be provided by naked pointer + * (e1), unique_ptr (e2), reference (e3) or value (e3). If provided by reference or value, will + * be moved if possible. If provided by pointer, ownership is taken. + * (f) null pointer; + * (g) enums, using the underlying integer value. + */ +template +std::unique_ptr makeItem(T v) { + Item* p = nullptr; + if constexpr (/* case a */ std::is_same_v) { + p = new Bool(v); + } else if constexpr (/* case b */ std::is_integral_v) { // b + if (v < 0) { + p = new Nint(v); + } else { + p = new Uint(static_cast(v)); + } + } else if constexpr (/* case c */ // + details::is_text_type_v::value) { + p = new Tstr(v); + } else if constexpr (/* case d1 */ // + std::is_same_v>, + std::vector> + /* case d2 */ // + || details::is_iterator_pair_over::value + /* case d3 */ // + || std::is_same_v>, + std::pair>) { + p = new Bstr(v); + } else if constexpr (/* case e1 */ // + std::is_pointer_v && + std::is_base_of_v>) { + p = v; + } else if constexpr (/* case e2 */ // + details::is_unique_ptr_of_subclass_of_v::value) { + p = v.release(); + } else if constexpr (/* case e3 */ // + std::is_base_of_v) { + p = new T(std::move(v)); + } else if constexpr (/* case f */ std::is_null_pointer_v) { + p = new Null(); + } else if constexpr (/* case g */ std::is_enum_v) { + return makeItem(static_cast>(v)); + } else { + // It's odd that this can't be static_assert(false), since it shouldn't be evaluated if one + // of the above ifs matches. But static_assert(false) always triggers. + static_assert(std::is_same_v, "makeItem called with unsupported type"); + } + return std::unique_ptr(p); +} + +inline void map_helper(Map& /* map */) {} + +template +inline void map_helper(Map& map, Key&& key, Value&& value, Rest&&... rest) { + map.add(std::forward(key), std::forward(value)); + map_helper(map, std::forward(rest)...); +} + +} // namespace details + +template >> || ...)>> +Array::Array(Args&&... args) { + mEntries.reserve(sizeof...(args)); + (mEntries.push_back(details::makeItem(std::forward(args))), ...); +} + +template +Array& Array::add(T&& v) & { + mEntries.push_back(details::makeItem(std::forward(v))); + return *this; +} + +template +Array&& Array::add(T&& v) && { + mEntries.push_back(details::makeItem(std::forward(v))); + return std::move(*this); +} + +template > +Map::Map(Args&&... args) { + static_assert((sizeof...(Args)) % 2 == 0, "Map must have an even number of entries"); + mEntries.reserve(sizeof...(args) / 2); + details::map_helper(*this, std::forward(args)...); +} + +template +Map& Map::add(Key&& key, Value&& value) & { + mEntries.push_back({details::makeItem(std::forward(key)), + details::makeItem(std::forward(value))}); + mCanonicalized = false; + return *this; +} + +template +Map&& Map::add(Key&& key, Value&& value) && { + this->add(std::forward(key), std::forward(value)); + return std::move(*this); +} + +static const std::unique_ptr kEmptyItemPtr; + +template || std::is_enum_v || + details::is_text_type_v::value>> +const std::unique_ptr& Map::get(Key key) const { + auto keyItem = details::makeItem(key); + + if (mCanonicalized) { + // It's sorted, so binary-search it. + auto found = std::lower_bound(begin(), end(), keyItem.get(), + [](const entry_type& entry, const Item* key) { + return keyLess(entry.first.get(), key); + }); + return (found == end() || *found->first != *keyItem) ? kEmptyItemPtr : found->second; + } else { + // Unsorted, do a linear search. + auto found = std::find_if( + begin(), end(), [&](const entry_type& entry) { return *entry.first == *keyItem; }); + return found == end() ? kEmptyItemPtr : found->second; + } +} + +template +SemanticTag::SemanticTag(uint64_t value, T&& taggedItem) + : mValue(value), mTaggedItem(details::makeItem(std::forward(taggedItem))) {} + +} // namespace cppbor diff --git a/ProvisioningTool/include/cppbor/cppbor_parse.h b/ProvisioningTool/include/cppbor/cppbor_parse.h new file mode 100644 index 00000000..22cd18d0 --- /dev/null +++ b/ProvisioningTool/include/cppbor/cppbor_parse.h @@ -0,0 +1,195 @@ +/* + * Copyright 2019 Google LLC + * + * 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 + * + * https://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. + */ + +#pragma once + +#include "cppbor.h" + +namespace cppbor { + +using ParseResult = std::tuple /* result */, const uint8_t* /* newPos */, + std::string /* errMsg */>; + +/** + * Parse the first CBOR data item (possibly compound) from the range [begin, end). + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + */ +ParseResult parse(const uint8_t* begin, const uint8_t* end); + +/** + * Parse the first CBOR data item (possibly compound) from the range [begin, end). + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + * + * The returned CBOR data item will contain View* items backed by + * std::string_view types over the input range. + * WARNING! If the input range changes underneath, the corresponding views will + * carry the same change. + */ +ParseResult parseWithViews(const uint8_t* begin, const uint8_t* end); + +/** + * Parse the first CBOR data item (possibly compound) from the byte vector. + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + */ +inline ParseResult parse(const std::vector& encoding) { + return parse(encoding.data(), encoding.data() + encoding.size()); +} + +/** + * Parse the first CBOR data item (possibly compound) from the range [begin, begin + size). + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + */ +inline ParseResult parse(const uint8_t* begin, size_t size) { + return parse(begin, begin + size); +} + +/** + * Parse the first CBOR data item (possibly compound) from the range [begin, begin + size). + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + * + * The returned CBOR data item will contain View* items backed by + * std::string_view types over the input range. + * WARNING! If the input range changes underneath, the corresponding views will + * carry the same change. + */ +inline ParseResult parseWithViews(const uint8_t* begin, size_t size) { + return parseWithViews(begin, begin + size); +} + +/** + * Parse the first CBOR data item (possibly compound) from the value contained in a Bstr. + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + */ +inline ParseResult parse(const Bstr* bstr) { + if (!bstr) + return ParseResult(nullptr, nullptr, "Null Bstr pointer"); + return parse(bstr->value()); +} + +class ParseClient; + +/** + * Parse the CBOR data in the range [begin, end) in streaming fashion, calling methods on the + * provided ParseClient when elements are found. + */ +void parse(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient); + +/** + * Parse the CBOR data in the range [begin, end) in streaming fashion, calling methods on the + * provided ParseClient when elements are found. Uses the View* item types + * instead of the copying ones. + */ +void parseWithViews(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient); + +/** + * Parse the CBOR data in the vector in streaming fashion, calling methods on the + * provided ParseClient when elements are found. + */ +inline void parse(const std::vector& encoding, ParseClient* parseClient) { + return parse(encoding.data(), encoding.data() + encoding.size(), parseClient); +} + +/** + * A pure interface that callers of the streaming parse functions must implement. + */ +class ParseClient { + public: + virtual ~ParseClient() {} + + /** + * Called when an item is found. The Item pointer points to the found item; use type() and + * the appropriate as*() method to examine the value. hdrBegin points to the first byte of the + * header, valueBegin points to the first byte of the value and end points one past the end of + * the item. In the case of header-only items, such as integers, and compound items (ARRAY, + * MAP or SEMANTIC) whose end has not yet been found, valueBegin and end are equal and point to + * the byte past the header. + * + * Note that for compound types (ARRAY, MAP, and SEMANTIC), the Item will have no content. For + * Map and Array items, the size() method will return a correct value, but the index operators + * are unsafe, and the object cannot be safely compared with another Array/Map. + * + * The method returns a ParseClient*. In most cases "return this;" will be the right answer, + * but a different ParseClient may be returned, which the parser will begin using. If the method + * returns nullptr, parsing will be aborted immediately. + */ + virtual ParseClient* item(std::unique_ptr& item, const uint8_t* hdrBegin, + const uint8_t* valueBegin, const uint8_t* end) = 0; + + /** + * Called when the end of a compound item (MAP or ARRAY) is found. The item argument will be + * the same one passed to the item() call -- and may be empty if item() moved its value out. + * hdrBegin, valueBegin and end point to the beginning of the item header, the beginning of the + * first contained value, and one past the end of the last contained value, respectively. + * + * Note that the Item will have no content. + * + * As with item(), itemEnd() can change the ParseClient by returning a different one, or end the + * parsing by returning nullptr; + */ + virtual ParseClient* itemEnd(std::unique_ptr& item, const uint8_t* hdrBegin, + const uint8_t* valueBegin, const uint8_t* end) = 0; + + /** + * Called when parsing encounters an error. position is set to the first unparsed byte (one + * past the last successfully-parsed byte) and errorMessage contains an message explaining what + * sort of error occurred. + */ + virtual void error(const uint8_t* position, const std::string& errorMessage) = 0; +}; + +} // namespace cppbor diff --git a/ProvisioningTool/include/json/assertions.h b/ProvisioningTool/include/json/assertions.h new file mode 100644 index 00000000..fbec7ae0 --- /dev/null +++ b/ProvisioningTool/include/json/assertions.h @@ -0,0 +1,54 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED +#define CPPTL_JSON_ASSERTIONS_H_INCLUDED + +#include +#include + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +/** It should not be possible for a maliciously designed file to + * cause an abort() or seg-fault, so these macros are used only + * for pre-condition violations and internal logic errors. + */ +#if JSON_USE_EXCEPTION + +// @todo <= add detail about condition in exception +# define JSON_ASSERT(condition) \ + {if (!(condition)) {Json::throwLogicError( "assert json failed" );}} + +# define JSON_FAIL_MESSAGE(message) \ + { \ + std::ostringstream oss; oss << message; \ + Json::throwLogicError(oss.str()); \ + abort(); \ + } + +#else // JSON_USE_EXCEPTION + +# define JSON_ASSERT(condition) assert(condition) + +// The call to assert() will show the failure message in debug builds. In +// release builds we abort, for a core-dump or debugger. +# define JSON_FAIL_MESSAGE(message) \ + { \ + std::ostringstream oss; oss << message; \ + assert(false && oss.str().c_str()); \ + abort(); \ + } + + +#endif + +#define JSON_ASSERT_MESSAGE(condition, message) \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message); \ + } + +#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED diff --git a/ProvisioningTool/include/json/autolink.h b/ProvisioningTool/include/json/autolink.h new file mode 100644 index 00000000..6fcc8afa --- /dev/null +++ b/ProvisioningTool/include/json/autolink.h @@ -0,0 +1,25 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_AUTOLINK_H_INCLUDED +#define JSON_AUTOLINK_H_INCLUDED + +#include "config.h" + +#ifdef JSON_IN_CPPTL +#include +#endif + +#if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && \ + !defined(JSON_IN_CPPTL) +#define CPPTL_AUTOLINK_NAME "json" +#undef CPPTL_AUTOLINK_DLL +#ifdef JSON_DLL +#define CPPTL_AUTOLINK_DLL +#endif +#include "autolink.h" +#endif + +#endif // JSON_AUTOLINK_H_INCLUDED diff --git a/ProvisioningTool/include/json/config.h b/ProvisioningTool/include/json/config.h new file mode 100644 index 00000000..5ca32281 --- /dev/null +++ b/ProvisioningTool/include/json/config.h @@ -0,0 +1,119 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +#if !defined(JSON_HAS_UNIQUE_PTR) +#if __cplusplus >= 201103L +#define JSON_HAS_UNIQUE_PTR (1) +#elif _MSC_VER >= 1600 +#define JSON_HAS_UNIQUE_PTR (1) +#else +#define JSON_HAS_UNIQUE_PTR (0) +#endif +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' +// characters in the debug information) +// All projects I've ever seen with VS6 were using this globally (not bothering +// with pragma push/pop). +#pragma warning(disable : 4786) +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__clang__) && defined(__has_feature) +#if __has_feature(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#endif +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +} // end namespace Json + +#endif // JSON_CONFIG_H_INCLUDED diff --git a/ProvisioningTool/include/json/features.h b/ProvisioningTool/include/json/features.h new file mode 100644 index 00000000..78135478 --- /dev/null +++ b/ProvisioningTool/include/json/features.h @@ -0,0 +1,51 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +#define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features { +public: + /** \brief A configuration that allows all features and assumes all strings + * are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON + * specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c + /// false. + bool strictRoot_; +}; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED diff --git a/ProvisioningTool/include/json/forwards.h b/ProvisioningTool/include/json/forwards.h new file mode 100644 index 00000000..ccfe09ab --- /dev/null +++ b/ProvisioningTool/include/json/forwards.h @@ -0,0 +1,37 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED diff --git a/ProvisioningTool/include/json/json.h b/ProvisioningTool/include/json/json.h new file mode 100644 index 00000000..8f10ac2b --- /dev/null +++ b/ProvisioningTool/include/json/json.h @@ -0,0 +1,15 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_JSON_H_INCLUDED +#define JSON_JSON_H_INCLUDED + +#include "autolink.h" +#include "value.h" +#include "reader.h" +#include "writer.h" +#include "features.h" + +#endif // JSON_JSON_H_INCLUDED diff --git a/ProvisioningTool/include/json/reader.h b/ProvisioningTool/include/json/reader.h new file mode 100644 index 00000000..9c9923a5 --- /dev/null +++ b/ProvisioningTool/include/json/reader.h @@ -0,0 +1,360 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_READER_H_INCLUDED +#define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "features.h" +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +/** \brief Unserialize a JSON document into a + *Value. + * + * \deprecated Use CharReader and CharReaderBuilder. + */ +class JSON_API Reader { +public: + typedef char Char; + typedef const Char* Location; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader(const Features& features); + + /** \brief Read a Value from a JSON + * document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + * back during + * serialization, \c false to discard comments. + * This parameter is ignored if + * Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + bool + parse(const std::string& document, Value& root, bool collectComments = true); + + /** \brief Read a Value from a JSON + document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + back during + * serialization, \c false to discard comments. + * This parameter is ignored if + Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse(std::istream& is, Value& root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") + std::string getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + */ + std::string getFormattedErrorMessages() const; + +private: + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, std::string& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const std::string& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + std::string getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + std::string commentsBefore_; + Features features_; + bool collectComments_; +}; // Reader + +/** Interface for reading JSON from a char array. + */ +class JSON_API CharReader { +public: + virtual ~CharReader() {} + /** \brief Read a Value from a JSON + document. + * The document must be a UTF-8 encoded string containing the document to read. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param errs [out] Formatted error messages (if not NULL) + * a user friendly string that lists errors in the parsed + * document. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + virtual bool parse( + char const* beginDoc, char const* endDoc, + Value* root, std::string* errs) = 0; + + class Factory { + public: + virtual ~Factory() {} + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual CharReader* newCharReader() const = 0; + }; // Factory +}; // CharReader + +/** \brief Build a CharReader implementation. + +Usage: +\code + using namespace Json; + CharReaderBuilder builder; + builder["collectComments"] = false; + Value value; + std::string errs; + bool ok = parseFromStream(builder, std::cin, &value, &errs); +\endcode +*/ +class JSON_API CharReaderBuilder : public CharReader::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + These are case-sensitive. + Available settings (case-sensitive): + - `"collectComments": false or true` + - true to collect comment and allow writing them + back during serialization, false to discard comments. + This parameter is ignored if allowComments is false. + - `"allowComments": false or true` + - true if comments are allowed. + - `"strictRoot": false or true` + - true if root must be either an array or an object value + - `"allowDroppedNullPlaceholders": false or true` + - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) + - `"allowNumericKeys": false or true` + - true if numeric object keys are allowed. + - `"allowSingleQuotes": false or true` + - true if '' are allowed for strings (both keys and values) + - `"stackLimit": integer` + - Exceeding stackLimit (recursive depth of `readValue()`) will + cause an exception. + - This is a security issue (seg-faults caused by deeply nested JSON), + so the default is low. + - `"failIfExtra": false or true` + - If true, `parse()` returns false when extra non-whitespace trails + the JSON value in the input string. + - `"rejectDupKeys": false or true` + - If true, `parse()` returns false when a key is duplicated within an object. + - `"allowSpecialFloats": false or true` + - If true, special float values (NaNs and infinities) are allowed + and their values are lossfree restorable. + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + CharReaderBuilder(); + virtual ~CharReaderBuilder(); + + virtual CharReader* newCharReader() const; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + + /** A simple way to update a specific setting. + */ + Value& operator[](std::string key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults + */ + static void setDefaults(Json::Value* settings); + /** Same as old Features::strictMode(). + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode + */ + static void strictMode(Json::Value* settings); +}; + +/** Consume entire stream and use its begin/end. + * Someday we might have a real StreamReader, but for now this + * is convenient. + */ +bool JSON_API parseFromStream( + CharReader::Factory const&, + std::istream&, + Value* root, std::string* errs); + +/** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() +*/ +JSON_API std::istream& operator>>(std::istream&, Value&); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_READER_H_INCLUDED diff --git a/ProvisioningTool/include/json/value.h b/ProvisioningTool/include/json/value.h new file mode 100644 index 00000000..66433f88 --- /dev/null +++ b/ProvisioningTool/include/json/value.h @@ -0,0 +1,850 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_H_INCLUDED +#define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +#ifndef JSON_USE_CPPTL_SMALLMAP +#include +#else +#include +#endif +#ifdef JSON_USE_CPPTL +#include +#endif + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +//Conditional NORETURN attribute on the throw functions would: +// a) suppress false positives from static code analysis +// b) possibly improve optimization opportunities. +#if !defined(JSONCPP_NORETURN) +# if defined(_MSC_VER) +# define JSONCPP_NORETURN __declspec(noreturn) +# elif defined(__GNUC__) +# define JSONCPP_NORETURN __attribute__ ((__noreturn__)) +# else +# define JSONCPP_NORETURN +# endif +#endif + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + +/** Base class for all exceptions we throw. + * + * We use nothing but these internally. Of course, STL can throw others. + */ +class JSON_API Exception : public std::exception { +public: + Exception(std::string const& msg); + virtual ~Exception() throw(); + virtual char const* what() const throw(); +protected: + std::string const msg_; +}; + +/** Exceptions which the user cannot easily avoid. + * + * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input + * + * \remark derived from Json::Exception + */ +class JSON_API RuntimeError : public Exception { +public: + RuntimeError(std::string const& msg); +}; + +/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. + * + * These are precondition-violations (user bugs) and internal errors (our bugs). + * + * \remark derived from Json::Exception + */ +class JSON_API LogicError : public Exception { +public: + LogicError(std::string const& msg); +}; + +/// used internally +JSONCPP_NORETURN void throwRuntimeError(std::string const& msg); +/// used internally +JSONCPP_NORETURN void throwLogicError(std::string const& msg); + +/** \brief Type of the value held by a Value object. + */ +enum ValueType { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; + +enum CommentPlacement { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for + /// root value) + numberOfCommentPlacement +}; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString { +public: + explicit StaticString(const char* czstring) : c_str_(czstring) {} + + operator const char*() const { return c_str_; } + + const char* c_str() const { return c_str_; } + +private: + const char* c_str_; +}; + +/** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * Values of an #objectValue or #arrayValue can be accessed using operator[]() + * methods. + * Non-const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resized and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtain default value in the case the + * required element does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + * + * \note #Value string-length fit in size_t, but keys must be < 2^30. + * (The reason is an implementation detail.) A #CharReader will raise an + * exception if a bound is exceeded to avoid security holes in your app, + * but the Value API does *not* check bounds. That is the responsibility + * of the caller. + */ +class JSON_API Value { + friend class ValueIteratorBase; +public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +#if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + static const Value& nullRef; +#if !defined(__ARMEL__) + /// \deprecated This exists for binary compatibility only. Use nullRef. + static const Value null; +#endif + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + +#if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; +#endif // defined(JSON_HAS_INT64) + +//MW: workaround for bug in NVIDIAs CUDA 7.5 nvcc compiler +#ifdef __NVCC__ +public: +#else +private: +#endif //__NVCC__ +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + class CZString { + public: + enum DuplicationPolicy { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString(ArrayIndex index); + CZString(char const* str, unsigned length, DuplicationPolicy allocate); + CZString(CZString const& other); + ~CZString(); + CZString& operator=(CZString other); + bool operator<(CZString const& other) const; + bool operator==(CZString const& other) const; + ArrayIndex index() const; + //const char* c_str() const; ///< \deprecated + char const* data() const; + unsigned length() const; + bool isStaticString() const; + + private: + void swap(CZString& other); + + struct StringStorage { + unsigned policy_: 2; + unsigned length_: 30; // 1GB max + }; + + char const* cstr_; // actually, a prefixed string, unless policy is noDup + union { + ArrayIndex index_; + StringStorage storage_; + }; + }; + +public: +#ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +#else + typedef CppTL::SmallMap ObjectValues; +#endif // ifndef JSON_USE_CPPTL_SMALLMAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. +This is useful since clear() and resize() will not alter types. + + Examples: +\code +Json::Value null_value; // null +Json::Value arr_value(Json::arrayValue); // [] +Json::Value obj_value(Json::objectValue); // {} +\endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); +#if defined(JSON_HAS_INT64) + Value(Int64 value); + Value(UInt64 value); +#endif // if defined(JSON_HAS_INT64) + Value(double value); + Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) + Value(const char* begin, const char* end); ///< Copy all, incl zeroes. + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * \note This works only for null-terminated strings. (We cannot change the + * size of this class, so we have nowhere to store the length, + * which might be computed later for various operations.) + * + * Example of usage: + * \code + * static StaticString foo("some text"); + * Json::Value aValue(foo); + * \endcode + */ + Value(const StaticString& value); + Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too. +#ifdef JSON_USE_CPPTL + Value(const CppTL::ConstString& value); +#endif + Value(bool value); + /// Deep copy. + Value(const Value& other); + ~Value(); + + /// Deep copy, then swap(other). + /// \note Over-write existing comments. To preserve comments, use #swapPayload(). + Value &operator=(const Value &other); + /// Swap everything. + void swap(Value& other); + /// Swap values but leave comments and source offsets in place. + void swapPayload(Value& other); + + ValueType type() const; + + /// Compare payload only, not comments etc. + bool operator<(const Value& other) const; + bool operator<=(const Value& other) const; + bool operator>=(const Value& other) const; + bool operator>(const Value& other) const; + bool operator==(const Value& other) const; + bool operator!=(const Value& other) const; + int compare(const Value& other) const; + + const char* asCString() const; ///< Embedded zeroes could cause you trouble! + std::string asString() const; ///< Embedded zeroes are possible. + /** Get raw char* of string-value. + * \return false if !string. (Seg-fault if str or end are NULL.) + */ + bool getString( + char const** begin, char const** end) const; +#ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +#endif + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(ArrayIndex size); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](ArrayIndex index); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](int index); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](ArrayIndex index) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](int index) const; + + /// If the array contains at least index+1 elements, returns the element + /// value, + /// otherwise returns defaultValue. + Value get(ArrayIndex index, const Value& defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(ArrayIndex index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value& append(const Value& value); + + /// Access an object value by name, create a null member if it does not exist. + /// \note Because of our implementation, keys are limited to 2^30 -1 chars. + /// Exceeding that will cause an exception. + Value& operator[](const char* key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const char* key) const; + /// Access an object value by name, create a null member if it does not exist. + /// \param key may contain embedded nulls. + Value& operator[](const std::string& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + /// \param key may contain embedded nulls. + const Value& operator[](const std::string& key) const; + /** \brief Access an object value by name, create a null member if it does not + exist. + + * If the object has no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value& operator[](const StaticString& key); +#ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value& operator[](const CppTL::ConstString& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const CppTL::ConstString& key) const; +#endif + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const char* key, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \note key may contain embedded nulls. + Value get(const char* begin, const char* end, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \param key may contain embedded nulls. + Value get(const std::string& key, const Value& defaultValue) const; +#ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const CppTL::ConstString& key, const Value& defaultValue) const; +#endif + /// Most general and efficient version of isMember()const, get()const, + /// and operator[]const + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + Value const* find(char const* begin, char const* end) const; + /// Most general and efficient version of object-mutators. + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. + Value const* demand(char const* begin, char const* end); + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + /// \deprecated + Value removeMember(const char* key); + /// Same as removeMember(const char*) + /// \param key may contain embedded nulls. + /// \deprecated + Value removeMember(const std::string& key); + /// Same as removeMember(const char* begin, const char* end, Value* removed), + /// but 'key' is null-terminated. + bool removeMember(const char* key, Value* removed); + /** \brief Remove the named map member. + + Update 'removed' iff removed. + \param key may contain embedded nulls. + \return true iff removed (no exceptions) + */ + bool removeMember(std::string const& key, Value* removed); + /// Same as removeMember(std::string const& key, Value* removed) + bool removeMember(const char* begin, const char* end, Value* removed); + /** \brief Remove the indexed array element. + + O(n) expensive operations. + Update 'removed' iff removed. + \return true iff removed (no exceptions) + */ + bool removeIndex(ArrayIndex i, Value* removed); + + /// Return true if the object has a member named key. + /// \note 'key' must be null-terminated. + bool isMember(const char* key) const; + /// Return true if the object has a member named key. + /// \param key may contain embedded nulls. + bool isMember(const std::string& key) const; + /// Same as isMember(std::string const& key)const + bool isMember(const char* begin, const char* end) const; +#ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember(const CppTL::ConstString& key) const; +#endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + + //# ifdef JSON_USE_CPPTL + // EnumMemberNames enumMemberNames() const; + // EnumValues enumValues() const; + //# endif + + /// \deprecated Always pass len. + JSONCPP_DEPRECATED("Use setComment(std::string const&) instead.") + void setComment(const char* comment, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const char* comment, size_t len, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const std::string& comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + std::string getComment(CommentPlacement placement) const; + + std::string toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + +private: + void initBasic(ValueType type, bool allocated = false); + + Value& resolveReference(const char* key); + Value& resolveReference(const char* key, const char* end); + + struct CommentInfo { + CommentInfo(); + ~CommentInfo(); + + void setComment(const char* text, size_t len); + + char* comment_; + }; + + // struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ + ObjectValues* map_; + } value_; + ValueType type_ : 8; + unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. + // If not allocated_, string_ must be null-terminated. + CommentInfo* comments_; +}; + +/** \brief Experimental and untested: represents an element of the "path" to + * access a node. + */ +class JSON_API PathArgument { +public: + friend class Path; + + PathArgument(); + PathArgument(ArrayIndex index); + PathArgument(const char* key); + PathArgument(const std::string& key); + +private: + enum Kind { + kindNone = 0, + kindIndex, + kindKey + }; + std::string key_; + ArrayIndex index_; + Kind kind_; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ +class JSON_API Path { +public: + Path(const std::string& path, + const PathArgument& a1 = PathArgument(), + const PathArgument& a2 = PathArgument(), + const PathArgument& a3 = PathArgument(), + const PathArgument& a4 = PathArgument(), + const PathArgument& a5 = PathArgument()); + + const Value& resolve(const Value& root) const; + Value resolve(const Value& root, const Value& defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on + /// the node. + Value& make(Value& root) const; + +private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath(const std::string& path, const InArgs& in); + void addPathInArg(const std::string& path, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind); + void invalidPath(const std::string& path, int location); + + Args args_; +}; + +/** \brief base class for Value iterators. + * + */ +class JSON_API ValueIteratorBase { +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + bool operator==(const SelfType& other) const { return isEqual(other); } + + bool operator!=(const SelfType& other) const { return !isEqual(other); } + + difference_type operator-(const SelfType& other) const { + return other.computeDistance(*this); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value key() const; + + /// Return the index of the referenced Value, or -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value, or "" if it is not an + /// objectValue. + /// \note Avoid `c_str()` on result, as embedded zeroes are possible. + std::string name() const; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. + JSONCPP_DEPRECATED("Use `key = name();` instead.") + char const* memberName() const; + /// Return the member name of the referenced Value, or NULL if it is not an + /// objectValue. + /// \note Better version than memberName(). Allows embedded nulls. + char const* memberName(char const** end) const; + +protected: + Value& deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType& other) const; + + bool isEqual(const SelfType& other) const; + + void copy(const SelfType& other); + +private: + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; + +public: + // For some reason, BORLAND needs these at the end, rather + // than earlier. No idea why. + ValueIteratorBase(); + explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); +}; + +/** \brief const iterator for object and array value. + * + */ +class JSON_API ValueConstIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef const Value value_type; + //typedef unsigned int size_t; + //typedef int difference_type; + typedef const Value& reference; + typedef const Value* pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueConstIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const ValueIteratorBase& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +/** \brief Iterator for object and array value. + */ +class JSON_API ValueIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef Value value_type; + typedef unsigned int size_t; + typedef int difference_type; + typedef Value& reference; + typedef Value* pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + ValueIterator(const ValueConstIterator& other); + ValueIterator(const ValueIterator& other); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const SelfType& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +} // namespace Json + + +namespace std { +/// Specialize std::swap() for Json::Value. +template<> +inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } +} + + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_H_INCLUDED diff --git a/ProvisioningTool/include/json/version.h b/ProvisioningTool/include/json/version.h new file mode 100644 index 00000000..d0f3dcb0 --- /dev/null +++ b/ProvisioningTool/include/json/version.h @@ -0,0 +1,13 @@ +// DO NOT EDIT. This file (and "version") is generated by CMake. +// Run CMake configure step to update it. +#ifndef JSON_VERSION_H_INCLUDED +# define JSON_VERSION_H_INCLUDED + +# define JSONCPP_VERSION_STRING "0.10.7" +# define JSONCPP_VERSION_MAJOR 0 +# define JSONCPP_VERSION_MINOR 10 +# define JSONCPP_VERSION_PATCH 7 +# define JSONCPP_VERSION_QUALIFIER +# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) + +#endif // JSON_VERSION_H_INCLUDED diff --git a/ProvisioningTool/include/json/writer.h b/ProvisioningTool/include/json/writer.h new file mode 100644 index 00000000..a7fd11d2 --- /dev/null +++ b/ProvisioningTool/include/json/writer.h @@ -0,0 +1,320 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +#define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +class Value; + +/** + +Usage: +\code + using namespace Json; + void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { + std::unique_ptr const writer( + factory.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush + } +\endcode +*/ +class JSON_API StreamWriter { +protected: + std::ostream* sout_; // not owned; will not delete +public: + StreamWriter(); + virtual ~StreamWriter(); + /** Write Value into document as configured in sub-class. + Do not take ownership of sout, but maintain a reference during function. + \pre sout != NULL + \return zero on success (For now, we always return zero, so check the stream instead.) + \throw std::exception possibly, depending on configuration + */ + virtual int write(Value const& root, std::ostream* sout) = 0; + + /** \brief A simple abstract factory. + */ + class JSON_API Factory { + public: + virtual ~Factory(); + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const = 0; + }; // Factory +}; // StreamWriter + +/** \brief Write into stringstream, then return string, for convenience. + * A StreamWriter will be created from the factory, used, and then deleted. + */ +std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); + + +/** \brief Build a StreamWriter implementation. + +Usage: +\code + using namespace Json; + Value value = ...; + StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = " "; // or whatever you like + std::unique_ptr writer( + builder.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush +\endcode +*/ +class JSON_API StreamWriterBuilder : public StreamWriter::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + Available settings (case-sensitive): + - "commentStyle": "None" or "All" + - "indentation": "" + - "enableYAMLCompatibility": false or true + - slightly change the whitespace around colons + - "dropNullPlaceholders": false or true + - Drop the "null" string from the writer's output for nullValues. + Strictly speaking, this is not valid JSON. But when the output is being + fed to a browser's Javascript, it makes for smaller output and the + browser can handle the output just fine. + - "useSpecialFloats": false or true + - If true, outputs non-finite floating point values in the following way: + NaN values as "NaN", positive infinity as "Infinity", and negative infinity + as "-Infinity". + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + StreamWriterBuilder(); + virtual ~StreamWriterBuilder(); + + /** + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + /** A simple way to update a specific setting. + */ + Value& operator[](std::string key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults + */ + static void setDefaults(Json::Value* settings); +}; + +/** \brief Abstract class for writers. + * \deprecated Use StreamWriter. (And really, this is an implementation detail.) + */ +class JSON_API Writer { +public: + virtual ~Writer(); + + virtual std::string write(const Value& root) = 0; +}; + +/** \brief Outputs a Value in JSON format + *without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' + *consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API FastWriter : public Writer { + +public: + FastWriter(); + virtual ~FastWriter() {} + + void enableYAMLCompatibility(); + +public: // overridden from Writer + virtual std::string write(const Value& root); + +private: + void writeValue(const Value& value); + + std::string document_; + bool yamlCompatiblityEnabled_; +}; + +/** \brief Writes a Value in JSON format in a + *human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + *line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + *types, + * and all the values fit on one lines, then print the array on a single + *line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + *#CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API StyledWriter : public Writer { +public: + StyledWriter(); + virtual ~StyledWriter() {} + +public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + virtual std::string write(const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const std::string& value); + void writeIndent(); + void writeWithIndent(const std::string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static std::string normalizeEOL(const std::string& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string document_; + std::string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; +}; + +/** \brief Writes a Value in JSON format in a + human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + types, + * and all the values fit on one lines, then print the array on a single + line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API StyledStreamWriter { +public: + StyledStreamWriter(std::string indentation = "\t"); + ~StyledStreamWriter() {} + +public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not + * return a value. + */ + void write(std::ostream& out, const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const std::string& value); + void writeIndent(); + void writeWithIndent(const std::string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static std::string normalizeEOL(const std::string& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; + +#if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(Int value); +std::string JSON_API valueToString(UInt value); +#endif // if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(LargestInt value); +std::string JSON_API valueToString(LargestUInt value); +std::string JSON_API valueToString(double value); +std::string JSON_API valueToString(bool value); +std::string JSON_API valueToQuotedString(const char* value); + +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() +JSON_API std::ostream& operator<<(std::ostream&, const Value& root); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_WRITER_H_INCLUDED diff --git a/ProvisioningTool/include/socket.h b/ProvisioningTool/include/socket.h new file mode 100644 index 00000000..8f47325a --- /dev/null +++ b/ProvisioningTool/include/socket.h @@ -0,0 +1,53 @@ +/* + ** + ** Copyright 2021, 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. + */ +#pragma once + +class SocketTransport +{ +public: + static inline std::shared_ptr getInstance() { + static std::shared_ptr socket = std::shared_ptr(new SocketTransport()); + return socket; + } + + ~SocketTransport(); + /** + * Creates a socket instance and connects to the provided server IP and port. + */ + bool openConnection(); + /** + * Sends data over socket and receives data back. + */ + bool sendData(const std::vector &inData, std::vector &output); + /** + * Closes the connection. + */ + bool closeConnection(); + /** + * Returns the state of the connection status. Returns true if the connection is active, + * false if connection is broken. + */ + bool isConnected(); + +private: + SocketTransport() : mSocket(-1), socketStatus(false) {} + /** + * Socket instance. + */ + int mSocket; + bool socketStatus; +}; \ No newline at end of file diff --git a/ProvisioningTool/include/utils.h b/ProvisioningTool/include/utils.h new file mode 100644 index 00000000..9eb991bd --- /dev/null +++ b/ProvisioningTool/include/utils.h @@ -0,0 +1,30 @@ +/* + ** + ** Copyright 2021, 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. + */ +#pragma once +#include +#include +#include +#include +#include + +std::string getHexString(std::vector& input); + +std::string hex2str(std::string a); + +int readJsonFile(Json::Value& root, std::string& inputFileName); + +int writeJsonFile(Json::Value& writerRoot, std::string& outputFileName); \ No newline at end of file diff --git a/ProvisioningTool/lib/README.md b/ProvisioningTool/lib/README.md new file mode 100644 index 00000000..d9ac780e --- /dev/null +++ b/ProvisioningTool/lib/README.md @@ -0,0 +1,25 @@ +# Instructions to build jsoncpp +Download the code from below opensource link: +https://github.com/open-source-parsers/jsoncpp/tree/0.y.z + +#### Unzip it +
+unzip jsoncpp-0.y.z.zip
+cd jsoncpp-0.y.z
+
+ +#### Build +
+$ mkdir -p build/debug
+$ cd build/debug
+$ cmake -DCMAKE_BUILD_TYPE=debug -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=ON -DARCHIVE_INSTALL_DIR=. -G "Unix Makefiles" ../..
+$ make
+
+ +#### Check the generated static and dynamic link library +
+$ find . -name *.a
+./src/lib_json/libjsoncpp.a
+$ find . -name *.so
+./src/lib_json/libjsoncpp.so
+
diff --git a/ProvisioningTool/lib/libjsoncpp.a b/ProvisioningTool/lib/libjsoncpp.a new file mode 100644 index 00000000..601854f4 Binary files /dev/null and b/ProvisioningTool/lib/libjsoncpp.a differ diff --git a/ProvisioningTool/lib/libjsoncpp.so b/ProvisioningTool/lib/libjsoncpp.so new file mode 100755 index 00000000..a23ae279 Binary files /dev/null and b/ProvisioningTool/lib/libjsoncpp.so differ diff --git a/ProvisioningTool/lib/libjsoncpp.so.0 b/ProvisioningTool/lib/libjsoncpp.so.0 new file mode 100755 index 00000000..a23ae279 Binary files /dev/null and b/ProvisioningTool/lib/libjsoncpp.so.0 differ diff --git a/ProvisioningTool/lib/libjsoncpp.so.0.10.7 b/ProvisioningTool/lib/libjsoncpp.so.0.10.7 new file mode 100755 index 00000000..a23ae279 Binary files /dev/null and b/ProvisioningTool/lib/libjsoncpp.so.0.10.7 differ diff --git a/ProvisioningTool/sample_json_cf.txt b/ProvisioningTool/sample_json_cf.txt index fd79e227..f9667dba 100644 --- a/ProvisioningTool/sample_json_cf.txt +++ b/ProvisioningTool/sample_json_cf.txt @@ -15,12 +15,12 @@ "verified_boot_key": "0000000000000000000000000000000000000000000000000000000000000000", "verified_boot_key_hash": "0000000000000000000000000000000000000000000000000000000000000000", "boot_state": 2, - "device_locked": 1 + "device_locked": 0 }, - "attest_key": "/data/vendor/batch_key.der", + "attest_key": "test_resources/batch_key.der", "attest_cert_chain": [ - "/data/vendor/batch_cert.der", - "/data/vendor/intermediate_cert.der", - "/data/vendor/ca_cert.der" + "test_resources/batch_cert.der", + "test_resources/intermediate_cert.der", + "test_resources/ca_cert.der" ] } diff --git a/ProvisioningTool/sample_json_gf.txt b/ProvisioningTool/sample_json_gf.txt index 7825e693..0974ec0a 100644 --- a/ProvisioningTool/sample_json_gf.txt +++ b/ProvisioningTool/sample_json_gf.txt @@ -15,12 +15,12 @@ "verified_boot_key": "0000000000000000000000000000000000000000000000000000000000000000", "verified_boot_key_hash": "0000000000000000000000000000000000000000000000000000000000000000", "boot_state": 2, - "device_locked": 1 + "device_locked": 0 }, - "attest_key": "/data/vendor/batch_key.der", + "attest_key": "test_resources/batch_key.der", "attest_cert_chain": [ - "/data/vendor/batch_cert.der", - "/data/vendor/intermediate_cert.der", - "/data/vendor/ca_cert.der" + "test_resources/batch_cert.der", + "test_resources/intermediate_cert.der", + "test_resources/ca_cert.der" ] } diff --git a/ProvisioningTool/src/construct_apdus.cpp b/ProvisioningTool/src/construct_apdus.cpp new file mode 100644 index 00000000..d3498a96 --- /dev/null +++ b/ProvisioningTool/src/construct_apdus.cpp @@ -0,0 +1,634 @@ +/* + ** + ** Copyright 2021, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cppbor/cppbor.h" + +// static globals. +static double keymasterVersion = -1; +static std::string inputFileName; +static std::string outputFileName; +Json::Value root; +Json::Value writerRoot; + +using namespace std; +using cppbor::Array; +using cppbor::Map; +using cppbor::Bstr; + +// static function declarations +static int processInputFile(); +static int processAttestationKey(); +static int processAttestationCertificateChain(); +static int processAttestationCertificateParams(); +static int processAttestationIds(); +static int processSharedSecret(); +static int processDeviceUniqueKey(); +static int processAdditionalCertificateChain(); +static int processSetBootParameters(); +static int readDataFromFile(const char *fileName, std::vector& data); +static int addApduHeader(const int ins, std::vector& inputData); +static int ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, std::vector&publicKey); +static X509* parseDerCertificate(std::vector& certData); +static int getNotAfter(X509* x509, std::vector& notAfterDate); +static int getDerSubjectName(X509* x509, std::vector& subject); +static int getBootParameterIntValue(Json::Value& bootParamsObj, const char* key, uint32_t *value); +static int getBootParameterBlobValue(Json::Value& bootParamsObj, const char* key, std::vector& blob); + + +// Print usage. +void usage() { + printf("Usage: Please give jason files with values as input to generate the apdus command. Please refer to sample_json files available in the folder for reference. Sample json files are written using hardcode parameters to be used for testing setup on cuttlefilsh emulator and goldfish emulators\n"); + printf("construct_apdus [options]\n"); + printf("Valid options are:\n"); + printf("-h, --help show this help message and exit.\n"); + printf("-v, --km_version version \t Version of the keymaster (4.1 for keymaster; 5 for keymint) \n"); + printf("-i, --input jsonFile \t Input json file \n"); + printf("-o, --output jsonFile \t Output json file \n"); +} + + +X509* parseDerCertificate(std::vector& certData) { + X509 *x509 = nullptr; + + /* Create BIO instance from certificate data */ + BIO *bio = BIO_new_mem_buf(certData.data(), certData.size()); + if(bio == nullptr) { + printf("\n Failed to create BIO from buffer.\n"); + return nullptr; + } + /* Create X509 instance from BIO */ + x509 = d2i_X509_bio(bio, NULL); + if(x509 == nullptr) { + printf("\n Failed to get X509 instance from BIO.\n"); + return nullptr; + } + BIO_free(bio); + return x509; +} + +int getDerSubjectName(X509* x509, std::vector& subject) { + uint8_t *subjectDer = NULL; + X509_NAME* asn1Subject = X509_get_subject_name(x509); + if(asn1Subject == NULL) { + printf("\n Failed to read the subject.\n"); + return FAILURE; + } + /* Convert X509_NAME to der encoded subject */ + int len = i2d_X509_NAME(asn1Subject, &subjectDer); + if (len < 0) { + printf("\n Failed to get readable name from X509_NAME.\n"); + return FAILURE; + } + subject.insert(subject.begin(), subjectDer, subjectDer+len); + return SUCCESS; +} + +int getNotAfter(X509* x509, std::vector& notAfterDate) { + const ASN1_TIME* notAfter = X509_get0_notAfter(x509); + if(notAfter == NULL) { + printf("\n Failed to read expiry time.\n"); + return FAILURE; + } + int strNotAfterLen = ASN1_STRING_length(notAfter); + const uint8_t *strNotAfter = ASN1_STRING_get0_data(notAfter); + if(strNotAfter == NULL) { + printf("\n Failed to read expiry time from ASN1 string.\n"); + return FAILURE; + } + notAfterDate.insert(notAfterDate.begin(), strNotAfter, strNotAfter + strNotAfterLen); + return SUCCESS; +} + + +int getBootParameterIntValue(Json::Value& bootParamsObj, const char* key, uint32_t *value) { + Json::Value val = bootParamsObj[key]; + if(val.empty()) + return FAILURE; + + if(!val.isInt()) + return FAILURE; + + *value = (uint32_t)val.asInt(); + + return SUCCESS; +} + +int getBootParameterBlobValue(Json::Value& bootParamsObj, const char* key, std::vector& blob) { + Json::Value val = bootParamsObj[key]; + if(val.empty()) + return FAILURE; + + if(!val.isString()) + return FAILURE; + + std::string blobStr = hex2str(val.asString()); + + for(char ch : blobStr) { + blob.push_back((uint8_t)ch); + } + + return SUCCESS; +} + + +// Parses the input json file. Prepares the apdu for each entry in the json +// file and dump all the apdus into the output json file. +int processInputFile() { + + // Parse Json file + if (0 != readJsonFile(root, inputFileName)) { + return FAILURE; + } + + if (keymasterVersion == KEYMASTER_VERSION) { + printf("\n Selected Keymaster version(%f) for provisioning \n", keymasterVersion); + if (0 != processAttestationKey() || + 0 != processAttestationCertificateChain() || + 0 != processAttestationCertificateParams()) { + return FAILURE; + } + } else { + printf("\n Selected keymint version(%f) for provisioning \n", keymasterVersion); + if ( 0 != processDeviceUniqueKey() || + 0 != processAttestationCertificateChain()) { + return FAILURE; + } + } + if (0 != processAttestationIds() || + 0 != processSharedSecret() || + 0 != processSetBootParameters()) { + return FAILURE; + } + if (SUCCESS != writeJsonFile(writerRoot, outputFileName)) { + return FAILURE; + } + printf("\n Successfully written json to outfile: %s\n ", outputFileName.c_str()); + return SUCCESS; +} + +int processAttestationKey() { + Json::Value keyFile = root.get(kAttestKey, Json::Value::nullRef); + if (!keyFile.isNull()) { + std::vector data; + std::vector privateKey; + std::vector publicKey; + + std::string keyFileName = keyFile.asString(); + if(SUCCESS != readDataFromFile(keyFileName.data(), data)) { + printf("\n Failed to read the attestation key from the file.\n"); + return FAILURE; + } + if (SUCCESS != ecRawKeyFromPKCS8(data, privateKey, publicKey)) { + return FAILURE; + } + + // Prepare cbor input. + Array input; + Array keys; + Map map; + keys.add(privateKey); + keys.add(publicKey); + map.add(kTagAlgorithm, kAlgorithmEc); + map.add(kTagDigest, std::vector({kDigestSha256})); + map.add(kTagCurve, kCurveP256); + map.add(kTagPurpose, std::vector({kPurposeAttest})); + // Add elements inside cbor array. + input.add(std::move(map)); + input.add(kKeyFormatRaw); + input.add(keys.encode()); + std::vector cborData = input.encode(); + + if(SUCCESS != addApduHeader(kAttestationKeyCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kAttestKey] = getHexString(cborData); + } else { + printf("\n Improper value for attest_key in json file \n"); + return FAILURE; + } + printf("\n Constructed attestation key APDU successfully. \n"); + return SUCCESS; +} + +static int processAttestationCertificateChain() { + Json::Value certChainFiles = root.get(kAttestCertChain, Json::Value::nullRef); + if (!certChainFiles.isNull()) { + std::vector certData; + + if(certChainFiles.isArray()) { + for (uint32_t i = 0; i < certChainFiles.size(); i++) { + if(certChainFiles[i].isString()) { + /* Read the certificates. */ + if(SUCCESS != readDataFromFile(certChainFiles[i].asString().data(), certData)) { + printf("\n Failed to read the Root certificate\n"); + return FAILURE; + } + } else { + printf("\n Fail: Only proper certificate paths as a string is allowed inside the json file. \n"); + return FAILURE; + } + } + } else { + printf("\n Fail: cert chain value should be an array inside the json file. \n"); + return FAILURE; + } + // Prepare cbor input + std::vector cborData = Bstr(certData).encode(); + if (SUCCESS != addApduHeader(kAttestCertChainCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kAttestCertChain] = getHexString(cborData); + } else { + printf("\n Fail: Improper value found for attest_cert_chain key inside json file \n"); + return FAILURE; + } + printf("\n Constructed attestation certificate chain APDU successfully. \n"); + return SUCCESS; +} + +int processAttestationCertificateParams() { + Json::Value certChainFile = root.get(kAttestCertChain, Json::Value::nullRef); + if (!certChainFile.isNull()) { + std::vector> certData; + if (certChainFile.isArray()) { + if (certChainFile.size() == 0) { + printf("\n empty certificate.\n"); + return FAILURE; + } + std::vector leafCertificate; + if (SUCCESS != readDataFromFile(certChainFile[0].asString().data(), leafCertificate)) { + printf("\n Failed to read the Root certificate\n"); + return FAILURE; + } + // ---------------- + // Prepare cbor data. + Array array; + std::vector subject; + std::vector notAfter; + + /* Subject, AuthorityKeyIdentifier and Expirty time of the root certificate are required by javacard. */ + /* Get X509 certificate instance for the root certificate.*/ + X509_Ptr x509(parseDerCertificate(leafCertificate)); + if (!x509) { + return FAILURE; + } + + /* Get subject in DER */ + getDerSubjectName(x509.get(), subject); + /* Get Expirty Time */ + getNotAfter(x509.get(), notAfter); + + array.add(subject); + array.add(notAfter); + std::vector cborData = array.encode(); + if (SUCCESS != addApduHeader(kAttestCertParamsCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kAttestCertParams] = getHexString(cborData); + //----------------- + } else { + printf("\n Fail: cert chain value should be an array inside the json file. \n"); + return FAILURE; + } + } else { + printf("\n Fail: Improper value found for attest_cert_chain key inside json file \n"); + return FAILURE; + } + printf("\n Constructed attestation certificate params APDU successfully. \n"); + return SUCCESS; +} + +int processAttestationIds() { + //AttestIDParams params; + Json::Value attestIds = root.get("attest_ids", Json::Value::nullRef); + if (!attestIds.isNull()) { + Json::Value value; + Map map; + Json::Value::Members keys = attestIds.getMemberNames(); + for(std::string key : keys) { + value = attestIds[key]; + if(value.empty()) { + continue; + } + if (!value.isString()) { + printf("\n Fail: Value for each attest ids key should be a string in the json file \n"); + return FAILURE; + } + std::string idVal = value.asString(); + if (0 == key.compare("brand")) { + map.add(kTagAttestationIdBrand, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("device")) { + map.add(kTagAttestationIdDevice, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("product")) { + map.add(kTagAttestationIdProduct, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("serial")) { + map.add(kTagAttestationIdSerial, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("imei")) { + map.add(kTagAttestationIdImei, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("meid")) { + map.add(kTagAttestationIdMeid, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("manufacturer")) { + map.add(kTagAttestationIdManufacturer, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("model")) { + map.add(kTagAttestationIdModel, std::vector(idVal.begin(), idVal.end())); + } else { + printf("\n unknown attestation id key:%s \n", key.c_str()); + return FAILURE; + } + } + + //------------------------- + // construct cbor input. + Array array; + array.add(std::move(map)); + std::vector cborData = array.encode(); + if (SUCCESS != addApduHeader(kAttestationIdsCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kAttestationIds] = getHexString(cborData); + //------------------------- + } else { + printf("\n Fail: Improper value found for attest_ids key inside the json file \n"); + return FAILURE; + } + printf("\n Constructed attestation ids APDU successfully \n"); + return SUCCESS; +} + +int processSharedSecret() { + Json::Value sharedSecret = root.get("shared_secret", Json::Value::nullRef); + if (!sharedSecret.isNull()) { + + if (!sharedSecret.isString()) { + printf("\n Fail: Value for shared secret key should be string inside the json file\n"); + return FAILURE; + } + std::string secret = hex2str(sharedSecret.asString()); + std::vector data(secret.begin(), secret.end()); + // -------------------------- + // Construct apdu. + Array array; + array.add(data); + std::vector cborData = array.encode(); + if (SUCCESS != addApduHeader(kPresharedSecretCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kSharedSecret] = getHexString(cborData); + // -------------------------- + } else { + printf("\n Fail: Improper value for shared_secret key inside the json file\n"); + return FAILURE; + } + printf("\n Constructed shared secret APDU successfully \n"); + return SUCCESS; +} + +int processSetBootParameters() { + uint32_t bootPatchLevel; + std::vector verifiedBootKey; + std::vector verifiedBootKeyHash; + uint32_t verifiedBootState; + uint32_t deviceLocked; + Json::Value bootParamsObj = root.get("set_boot_params", Json::Value::nullRef); + if (!bootParamsObj.isNull()) { + + if(SUCCESS != getBootParameterIntValue(bootParamsObj, "boot_patch_level", &bootPatchLevel)) { + printf("\n Invalid value for boot_patch_level or boot_patch_level tag missing\n"); + return FAILURE; + } + if(SUCCESS != getBootParameterBlobValue(bootParamsObj, "verified_boot_key", verifiedBootKey)) { + printf("\n Invalid value for verified_boot_key or verified_boot_key tag missing\n"); + return FAILURE; + } + if(SUCCESS != getBootParameterBlobValue(bootParamsObj, "verified_boot_key_hash", verifiedBootKeyHash)) { + printf("\n Invalid value for verified_boot_key_hash or verified_boot_key_hash tag missing\n"); + return FAILURE; + } + if(SUCCESS != getBootParameterIntValue(bootParamsObj, "boot_state", &verifiedBootState)) { + printf("\n Invalid value for boot_state or boot_state tag missing\n"); + return FAILURE; + } + if(SUCCESS != getBootParameterIntValue(bootParamsObj, "device_locked", &deviceLocked)) { + printf("\n Invalid value for device_locked or device_locked tag missing\n"); + return FAILURE; + } + + } else { + printf("\n Fail: Improper value found for set_boot_params key inside the json file\n"); + return FAILURE; + } + //--------------------------------- + // prepare cbor data. + Array array; + array.add(bootPatchLevel). + add(verifiedBootKey). /* Verified Boot Key */ + add(verifiedBootKeyHash). /* Verified Boot Hash */ + add(verifiedBootState). /* boot state */ + add(deviceLocked); /* device locked */ + + std::vector cborData = array.encode(); + if (SUCCESS != addApduHeader(kBootParamsCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kBootParams] = getHexString(cborData); + + //--------------------------------- + printf("\n Constructed boot paramters APDU successfully \n"); + return SUCCESS; +} + +int ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, + std::vector&publicKey) { + const uint8_t *data = pkcs8Blob.data(); + EVP_PKEY *evpkey = d2i_PrivateKey(EVP_PKEY_EC, nullptr, &data, pkcs8Blob.size()); + if(!evpkey) { + printf("\n Failed to decode private key from PKCS8, Error: %ld", ERR_peek_last_error()); + return FAILURE; + } + EVP_PKEY_Ptr pkey(evpkey); + + EC_KEY_Ptr ec_key(EVP_PKEY_get1_EC_KEY(pkey.get())); + if(!ec_key.get()) { + printf("\n Failed to create EC_KEY, Error: %ld", ERR_peek_last_error()); + return FAILURE; + } + + //Get EC Group + const EC_GROUP *group = EC_KEY_get0_group(ec_key.get()); + if(group == NULL) { + printf("\n Failed to get the EC_GROUP from ec_key."); + return FAILURE; + } + + //Extract private key. + const BIGNUM *privBn = EC_KEY_get0_private_key(ec_key.get()); + int privKeyLen = BN_num_bytes(privBn); + std::unique_ptr privKey(new uint8_t[privKeyLen]); + BN_bn2bin(privBn, privKey.get()); + secret.insert(secret.begin(), privKey.get(), privKey.get()+privKeyLen); + + //Extract public key. + const EC_POINT *point = EC_KEY_get0_public_key(ec_key.get()); + int pubKeyLen=0; + pubKeyLen = EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); + std::unique_ptr pubKey(new uint8_t[pubKeyLen]); + EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, pubKey.get(), pubKeyLen, NULL); + publicKey.insert(publicKey.begin(), pubKey.get(), pubKey.get()+pubKeyLen); + + return SUCCESS; +} + + +int addApduHeader(const int ins, std::vector& inputData) { + if(USHRT_MAX >= inputData.size()) { + // Send extended length APDU always as response size is not known to HAL. + // Case 1: Lc > 0 CLS | INS | P1 | P2 | 00 | 2 bytes of Lc | CommandData | 2 bytes of Le all set to 00. + // Case 2: Lc = 0 CLS | INS | P1 | P2 | 3 bytes of Le all set to 00. + //Extended length 3 bytes, starts with 0x00 + if (inputData.size() > 0) { + inputData.insert(inputData.begin(), static_cast(inputData.size() & 0xFF)); // LSB + inputData.insert(inputData.begin(), static_cast(inputData.size() >> 8)); // MSB + } + inputData.insert(inputData.begin(), static_cast(0x00)); + //Expected length of output. + //Accepting complete length of output every time. + inputData.push_back(static_cast(0x00)); + inputData.push_back(static_cast(0x00)); + } else { + printf("\n Failed to construct apdu. input data larger than USHORT_MAX.\n"); + return FAILURE; + } + + inputData.insert(inputData.begin(), static_cast(APDU_P2));//P2 + inputData.insert(inputData.begin(), static_cast(APDU_P1(keymasterVersion)));//P1 + inputData.insert(inputData.begin(), static_cast(ins));//INS + inputData.insert(inputData.begin(), static_cast(APDU_CLS));//CLS + return SUCCESS; +} + +int readDataFromFile(const char *filename, std::vector& data) { + FILE *fp; + int ret = SUCCESS; + fp = fopen(filename, "rb"); + if(fp == NULL) { + printf("\nFailed to open file: \n"); + return FAILURE; + } + fseek(fp, 0L, SEEK_END); + long int filesize = ftell(fp); + rewind(fp); + std::unique_ptr buf(new uint8_t[filesize]); + if( 0 == fread(buf.get(), filesize, 1, fp)) { + printf("\n No content in the file \n"); + ret = FAILURE; + goto exit; + } + data.insert(data.end(), buf.get(), buf.get() + filesize); +exit: + fclose(fp); + return ret; +} + +// TODO +static int processDeviceUniqueKey() { return 0; } +static int processAdditionalCertificateChain() { return 0; } + + +int main(int argc, char* argv[]) { + int c; + struct option longOpts[] = { + {"km_version", required_argument, NULL, 'v'}, + {"input", required_argument, NULL, 'i'}, + {"output", required_argument, NULL, 'o'}, + {"help", no_argument, NULL, 'h'}, + {0,0,0,0} + }; + + if (argc <= 1) { + printf("\n Invalid command \n"); + usage(); + return FAILURE; + } + + /* getopt_long stores the option index here. */ + while ((c = getopt_long(argc, argv, ":hv:i:o:", longOpts, NULL)) != -1) { + switch(c) { + case 'v': + // keymaster version + keymasterVersion = atof(optarg); + std::cout << "Version: " << keymasterVersion << std::endl; + break; + case 'i': + // input file + inputFileName = std::string(optarg); + std::cout << "input file: " << inputFileName << std::endl; + break; + case 'o': + // output file + outputFileName = std::string(optarg); + std::cout << "output file: " << outputFileName << std::endl; + break; + case 'h': + // help + usage(); + return SUCCESS; + case ':': + printf("\n missing argument\n"); + usage(); + return FAILURE; + case '?': + default: + printf("\n Invalid option\n"); + usage(); + return FAILURE; + } + } + if (keymasterVersion == -1 || inputFileName.empty() || + outputFileName.empty() || optind < argc) { + printf("\n Missing mandatory arguments \n"); + usage(); + return FAILURE; + } + if (keymasterVersion != KEYMASTER_VERSION && keymasterVersion != KEYMINT_VERSION) { + printf("\n Error unknown version."); + return FAILURE; + } + // Process input file; construct apuds and store in output json file. + processInputFile(); + return SUCCESS; +} diff --git a/ProvisioningTool/src/cppbor.cpp b/ProvisioningTool/src/cppbor.cpp new file mode 100644 index 00000000..3414b8e5 --- /dev/null +++ b/ProvisioningTool/src/cppbor.cpp @@ -0,0 +1,626 @@ +/* + * Copyright 2019 Google LLC + * + * 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 + * + * https://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 + +#include +#include + +#include + +using std::string; +using std::vector; + + +#if !defined(__TRUSTY__) && !defined(__LINUX__) +#include +#define LOG_TAG "CppBor" +#else +#define CHECK(x) (void)(x) +#endif + +#ifdef __LINUX__ +#define ERROR "ERROR: " +#define LOG(x) std::cout << x +#endif + +namespace cppbor { + +namespace { + +template ::value>> +Iterator writeBigEndian(T value, Iterator pos) { + for (unsigned i = 0; i < sizeof(value); ++i) { + *pos++ = static_cast(value >> (8 * (sizeof(value) - 1))); + value = static_cast(value << 8); + } + return pos; +} + +template ::value>> +void writeBigEndian(T value, std::function& cb) { + for (unsigned i = 0; i < sizeof(value); ++i) { + cb(static_cast(value >> (8 * (sizeof(value) - 1)))); + value = static_cast(value << 8); + } +} + +bool cborAreAllElementsNonCompound(const Item* compoundItem) { + if (compoundItem->type() == ARRAY) { + const Array* array = compoundItem->asArray(); + for (size_t n = 0; n < array->size(); n++) { + const Item* entry = (*array)[n].get(); + switch (entry->type()) { + case ARRAY: + case MAP: + return false; + default: + break; + } + } + } else { + const Map* map = compoundItem->asMap(); + for (auto& [keyEntry, valueEntry] : *map) { + switch (keyEntry->type()) { + case ARRAY: + case MAP: + return false; + default: + break; + } + switch (valueEntry->type()) { + case ARRAY: + case MAP: + return false; + default: + break; + } + } + } + return true; +} + +bool prettyPrintInternal(const Item* item, string& out, size_t indent, size_t maxBStrSize, + const vector& mapKeysToNotPrint) { + if (!item) { + out.append(""); + return false; + } + + char buf[80]; + + string indentString(indent, ' '); + + size_t tagCount = item->semanticTagCount(); + while (tagCount > 0) { + --tagCount; + snprintf(buf, sizeof(buf), "tag %" PRIu64 " ", item->semanticTag(tagCount)); + out.append(buf); + } + + switch (item->type()) { + case SEMANTIC: + // Handled above. + break; + + case UINT: + snprintf(buf, sizeof(buf), "%" PRIu64, item->asUint()->unsignedValue()); + out.append(buf); + break; + + case NINT: + snprintf(buf, sizeof(buf), "%" PRId64, item->asNint()->value()); + out.append(buf); + break; + + case BSTR: { + const uint8_t* valueData; + size_t valueSize; + const Bstr* bstr = item->asBstr(); + if (bstr != nullptr) { + const vector& value = bstr->value(); + valueData = value.data(); + valueSize = value.size(); + } else { + const ViewBstr* viewBstr = item->asViewBstr(); + assert(viewBstr != nullptr); + + std::basic_string_view view = viewBstr->view(); + valueData = view.data(); + valueSize = view.size(); + } + + if (valueSize > maxBStrSize) { + unsigned char digest[SHA_DIGEST_LENGTH]; + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, valueData, valueSize); + SHA1_Final(digest, &ctx); + char buf2[SHA_DIGEST_LENGTH * 2 + 1]; + for (size_t n = 0; n < SHA_DIGEST_LENGTH; n++) { + snprintf(buf2 + n * 2, 3, "%02x", digest[n]); + } + snprintf(buf, sizeof(buf), "", valueSize, buf2); + out.append(buf); + } else { + out.append("{"); + for (size_t n = 0; n < valueSize; n++) { + if (n > 0) { + out.append(", "); + } + snprintf(buf, sizeof(buf), "0x%02x", valueData[n]); + out.append(buf); + } + out.append("}"); + } + } break; + + case TSTR: + out.append("'"); + { + // TODO: escape "'" characters + if (item->asTstr() != nullptr) { + out.append(item->asTstr()->value().c_str()); + } else { + const ViewTstr* viewTstr = item->asViewTstr(); + assert(viewTstr != nullptr); + out.append(viewTstr->view()); + } + } + out.append("'"); + break; + + case ARRAY: { + const Array* array = item->asArray(); + if (array->size() == 0) { + out.append("[]"); + } else if (cborAreAllElementsNonCompound(array)) { + out.append("["); + for (size_t n = 0; n < array->size(); n++) { + if (!prettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize, + mapKeysToNotPrint)) { + return false; + } + out.append(", "); + } + out.append("]"); + } else { + out.append("[\n" + indentString); + for (size_t n = 0; n < array->size(); n++) { + out.append(" "); + if (!prettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize, + mapKeysToNotPrint)) { + return false; + } + out.append(",\n" + indentString); + } + out.append("]"); + } + } break; + + case MAP: { + const Map* map = item->asMap(); + + if (map->size() == 0) { + out.append("{}"); + } else { + out.append("{\n" + indentString); + for (auto& [map_key, map_value] : *map) { + out.append(" "); + + if (!prettyPrintInternal(map_key.get(), out, indent + 2, maxBStrSize, + mapKeysToNotPrint)) { + return false; + } + out.append(" : "); + if (map_key->type() == TSTR && + std::find(mapKeysToNotPrint.begin(), mapKeysToNotPrint.end(), + map_key->asTstr()->value()) != mapKeysToNotPrint.end()) { + out.append(""); + } else { + if (!prettyPrintInternal(map_value.get(), out, indent + 2, maxBStrSize, + mapKeysToNotPrint)) { + return false; + } + } + out.append(",\n" + indentString); + } + out.append("}"); + } + } break; + + case SIMPLE: + const Bool* asBool = item->asSimple()->asBool(); + const Null* asNull = item->asSimple()->asNull(); + if (asBool != nullptr) { + out.append(asBool->value() ? "true" : "false"); + } else if (asNull != nullptr) { + out.append("null"); + } else { +#ifndef __TRUSTY__ + LOG(ERROR) << "Only boolean/null is implemented for SIMPLE"; +#endif // __TRUSTY__ + return false; + } + break; + } + + return true; +} + +} // namespace + +size_t headerSize(uint64_t addlInfo) { + if (addlInfo < ONE_BYTE_LENGTH) return 1; + if (addlInfo <= std::numeric_limits::max()) return 2; + if (addlInfo <= std::numeric_limits::max()) return 3; + if (addlInfo <= std::numeric_limits::max()) return 5; + return 9; +} + +uint8_t* encodeHeader(MajorType type, uint64_t addlInfo, uint8_t* pos, const uint8_t* end) { + size_t sz = headerSize(addlInfo); + if (end - pos < static_cast(sz)) return nullptr; + switch (sz) { + case 1: + *pos++ = type | static_cast(addlInfo); + return pos; + case 2: + *pos++ = type | ONE_BYTE_LENGTH; + *pos++ = static_cast(addlInfo); + return pos; + case 3: + *pos++ = type | TWO_BYTE_LENGTH; + return writeBigEndian(static_cast(addlInfo), pos); + case 5: + *pos++ = type | FOUR_BYTE_LENGTH; + return writeBigEndian(static_cast(addlInfo), pos); + case 9: + *pos++ = type | EIGHT_BYTE_LENGTH; + return writeBigEndian(addlInfo, pos); + default: + CHECK(false); // Impossible to get here. + return nullptr; + } +} + +void encodeHeader(MajorType type, uint64_t addlInfo, EncodeCallback encodeCallback) { + size_t sz = headerSize(addlInfo); + switch (sz) { + case 1: + encodeCallback(type | static_cast(addlInfo)); + break; + case 2: + encodeCallback(type | ONE_BYTE_LENGTH); + encodeCallback(static_cast(addlInfo)); + break; + case 3: + encodeCallback(type | TWO_BYTE_LENGTH); + writeBigEndian(static_cast(addlInfo), encodeCallback); + break; + case 5: + encodeCallback(type | FOUR_BYTE_LENGTH); + writeBigEndian(static_cast(addlInfo), encodeCallback); + break; + case 9: + encodeCallback(type | EIGHT_BYTE_LENGTH); + writeBigEndian(addlInfo, encodeCallback); + break; + default: + CHECK(false); // Impossible to get here. + } +} + +bool Item::operator==(const Item& other) const& { + if (type() != other.type()) return false; + switch (type()) { + case UINT: + return *asUint() == *(other.asUint()); + case NINT: + return *asNint() == *(other.asNint()); + case BSTR: + if (asBstr() != nullptr && other.asBstr() != nullptr) { + return *asBstr() == *(other.asBstr()); + } + if (asViewBstr() != nullptr && other.asViewBstr() != nullptr) { + return *asViewBstr() == *(other.asViewBstr()); + } + // Interesting corner case: comparing a Bstr and ViewBstr with + // identical contents. The function currently returns false for + // this case. + // TODO: if it should return true, this needs a deep comparison + return false; + case TSTR: + if (asTstr() != nullptr && other.asTstr() != nullptr) { + return *asTstr() == *(other.asTstr()); + } + if (asViewTstr() != nullptr && other.asViewTstr() != nullptr) { + return *asViewTstr() == *(other.asViewTstr()); + } + // Same corner case as Bstr + return false; + case ARRAY: + return *asArray() == *(other.asArray()); + case MAP: + return *asMap() == *(other.asMap()); + case SIMPLE: + return *asSimple() == *(other.asSimple()); + case SEMANTIC: + return *asSemanticTag() == *(other.asSemanticTag()); + default: + CHECK(false); // Impossible to get here. + return false; + } +} + +Nint::Nint(int64_t v) : mValue(v) { + CHECK(v < 0); +} + +bool Simple::operator==(const Simple& other) const& { + if (simpleType() != other.simpleType()) return false; + + switch (simpleType()) { + case BOOLEAN: + return *asBool() == *(other.asBool()); + case NULL_T: + return true; + default: + CHECK(false); // Impossible to get here. + return false; + } +} + +uint8_t* Bstr::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(mValue.size(), pos, end); + if (!pos || end - pos < static_cast(mValue.size())) return nullptr; + return std::copy(mValue.begin(), mValue.end(), pos); +} + +void Bstr::encodeValue(EncodeCallback encodeCallback) const { + for (auto c : mValue) { + encodeCallback(c); + } +} + +uint8_t* ViewBstr::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(mView.size(), pos, end); + if (!pos || end - pos < static_cast(mView.size())) return nullptr; + return std::copy(mView.begin(), mView.end(), pos); +} + +void ViewBstr::encodeValue(EncodeCallback encodeCallback) const { + for (auto c : mView) { + encodeCallback(static_cast(c)); + } +} + +uint8_t* Tstr::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(mValue.size(), pos, end); + if (!pos || end - pos < static_cast(mValue.size())) return nullptr; + return std::copy(mValue.begin(), mValue.end(), pos); +} + +void Tstr::encodeValue(EncodeCallback encodeCallback) const { + for (auto c : mValue) { + encodeCallback(static_cast(c)); + } +} + +uint8_t* ViewTstr::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(mView.size(), pos, end); + if (!pos || end - pos < static_cast(mView.size())) return nullptr; + return std::copy(mView.begin(), mView.end(), pos); +} + +void ViewTstr::encodeValue(EncodeCallback encodeCallback) const { + for (auto c : mView) { + encodeCallback(static_cast(c)); + } +} + +bool Array::operator==(const Array& other) const& { + return size() == other.size() + // Can't use vector::operator== because the contents are pointers. std::equal lets us + // provide a predicate that does the dereferencing. + && std::equal(mEntries.begin(), mEntries.end(), other.mEntries.begin(), + [](auto& a, auto& b) -> bool { return *a == *b; }); +} + +uint8_t* Array::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(size(), pos, end); + if (!pos) return nullptr; + for (auto& entry : mEntries) { + pos = entry->encode(pos, end); + if (!pos) return nullptr; + } + return pos; +} + +void Array::encode(EncodeCallback encodeCallback) const { + encodeHeader(size(), encodeCallback); + for (auto& entry : mEntries) { + entry->encode(encodeCallback); + } +} + +std::unique_ptr Array::clone() const { + auto res = std::make_unique(); + for (size_t i = 0; i < mEntries.size(); i++) { + res->add(mEntries[i]->clone()); + } + return res; +} + +bool Map::operator==(const Map& other) const& { + return size() == other.size() + // Can't use vector::operator== because the contents are pairs of pointers. std::equal + // lets us provide a predicate that does the dereferencing. + && std::equal(begin(), end(), other.begin(), [](auto& a, auto& b) { + return *a.first == *b.first && *a.second == *b.second; + }); +} + +uint8_t* Map::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(size(), pos, end); + if (!pos) return nullptr; + for (auto& entry : mEntries) { + pos = entry.first->encode(pos, end); + if (!pos) return nullptr; + pos = entry.second->encode(pos, end); + if (!pos) return nullptr; + } + return pos; +} + +void Map::encode(EncodeCallback encodeCallback) const { + encodeHeader(size(), encodeCallback); + for (auto& entry : mEntries) { + entry.first->encode(encodeCallback); + entry.second->encode(encodeCallback); + } +} + +bool Map::keyLess(const Item* a, const Item* b) { + // CBOR map canonicalization rules are: + + // 1. If two keys have different lengths, the shorter one sorts earlier. + if (a->encodedSize() < b->encodedSize()) return true; + if (a->encodedSize() > b->encodedSize()) return false; + + // 2. If two keys have the same length, the one with the lower value in (byte-wise) lexical + // order sorts earlier. This requires encoding both items. + auto encodedA = a->encode(); + auto encodedB = b->encode(); + + return std::lexicographical_compare(encodedA.begin(), encodedA.end(), // + encodedB.begin(), encodedB.end()); +} + +void recursivelyCanonicalize(std::unique_ptr& item) { + switch (item->type()) { + case UINT: + case NINT: + case BSTR: + case TSTR: + case SIMPLE: + return; + + case ARRAY: + std::for_each(item->asArray()->begin(), item->asArray()->end(), + recursivelyCanonicalize); + return; + + case MAP: + item->asMap()->canonicalize(true /* recurse */); + return; + + case SEMANTIC: + // This can't happen. SemanticTags delegate their type() method to the contained Item's + // type. + assert(false); + return; + } +} + +Map& Map::canonicalize(bool recurse) & { + if (recurse) { + for (auto& entry : mEntries) { + recursivelyCanonicalize(entry.first); + recursivelyCanonicalize(entry.second); + } + } + + if (size() < 2 || mCanonicalized) { + // Trivially or already canonical; do nothing. + return *this; + } + + std::sort(begin(), end(), + [](auto& a, auto& b) { return keyLess(a.first.get(), b.first.get()); }); + mCanonicalized = true; + return *this; +} + +std::unique_ptr Map::clone() const { + auto res = std::make_unique(); + for (auto& [key, value] : *this) { + res->add(key->clone(), value->clone()); + } + res->mCanonicalized = mCanonicalized; + return res; +} + +std::unique_ptr SemanticTag::clone() const { + return std::make_unique(mValue, mTaggedItem->clone()); +} + +uint8_t* SemanticTag::encode(uint8_t* pos, const uint8_t* end) const { + // Can't use the encodeHeader() method that calls type() to get the major type, since that will + // return the tagged Item's type. + pos = ::cppbor::encodeHeader(kMajorType, mValue, pos, end); + if (!pos) return nullptr; + return mTaggedItem->encode(pos, end); +} + +void SemanticTag::encode(EncodeCallback encodeCallback) const { + // Can't use the encodeHeader() method that calls type() to get the major type, since that will + // return the tagged Item's type. + ::cppbor::encodeHeader(kMajorType, mValue, encodeCallback); + mTaggedItem->encode(encodeCallback); +} + +size_t SemanticTag::semanticTagCount() const { + size_t levelCount = 1; // Count this level. + const SemanticTag* cur = this; + while (cur->mTaggedItem && (cur = cur->mTaggedItem->asSemanticTag()) != nullptr) ++levelCount; + return levelCount; +} + +uint64_t SemanticTag::semanticTag(size_t nesting) const { + // Getting the value of a specific nested tag is a bit tricky, because we start with the outer + // tag and don't know how many are inside. We count the number of nesting levels to find out + // how many there are in total, then to get the one we want we have to walk down levelCount - + // nesting steps. + size_t levelCount = semanticTagCount(); + if (nesting >= levelCount) return 0; + + levelCount -= nesting; + const SemanticTag* cur = this; + while (--levelCount > 0) cur = cur->mTaggedItem->asSemanticTag(); + + return cur->mValue; +} + +string prettyPrint(const Item* item, size_t maxBStrSize, const vector& mapKeysToNotPrint) { + string out; + prettyPrintInternal(item, out, 0, maxBStrSize, mapKeysToNotPrint); + return out; +} +string prettyPrint(const vector& encodedCbor, size_t maxBStrSize, + const vector& mapKeysToNotPrint) { + auto [item, _, message] = parse(encodedCbor); + if (item == nullptr) { +#ifndef __TRUSTY__ + LOG(ERROR) << "Data to pretty print is not valid CBOR: " << message; +#endif // __TRUSTY__ + return ""; + } + + return prettyPrint(item.get(), maxBStrSize, mapKeysToNotPrint); +} + +} // namespace cppbor diff --git a/ProvisioningTool/src/cppbor_parse.cpp b/ProvisioningTool/src/cppbor_parse.cpp new file mode 100644 index 00000000..b1803310 --- /dev/null +++ b/ProvisioningTool/src/cppbor_parse.cpp @@ -0,0 +1,389 @@ +/* + * Copyright 2019 Google LLC + * + * 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 + * + * https://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 "cppbor/cppbor_parse.h" + +#include + +#if !defined( __TRUSTY__) && !defined(__LINUX__) +#include +#define LOG_TAG "CppBor" +#else +#define CHECK(x) (void)(x) +#endif + +namespace cppbor { + +namespace { + +std::string insufficientLengthString(size_t bytesNeeded, size_t bytesAvail, + const std::string& type) { + char buf[1024]; + snprintf(buf, sizeof(buf), "Need %zu byte(s) for %s, have %zu.", bytesNeeded, type.c_str(), + bytesAvail); + return std::string(buf); +} + +template >> +std::tuple parseLength(const uint8_t* pos, const uint8_t* end, + ParseClient* parseClient) { + if (pos + sizeof(T) > end) { + parseClient->error(pos - 1, insufficientLengthString(sizeof(T), end - pos, "length field")); + return {false, 0, pos}; + } + + const uint8_t* intEnd = pos + sizeof(T); + T result = 0; + do { + result = static_cast((result << 8) | *pos++); + } while (pos < intEnd); + return {true, result, pos}; +} + +std::tuple parseRecursively(const uint8_t* begin, const uint8_t* end, + bool emitViews, ParseClient* parseClient); + +std::tuple handleUint(uint64_t value, const uint8_t* hdrBegin, + const uint8_t* hdrEnd, + ParseClient* parseClient) { + std::unique_ptr item = std::make_unique(value); + return {hdrEnd, + parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; +} + +std::tuple handleNint(uint64_t value, const uint8_t* hdrBegin, + const uint8_t* hdrEnd, + ParseClient* parseClient) { + if (value > std::numeric_limits::max()) { + parseClient->error(hdrBegin, "NINT values that don't fit in int64_t are not supported."); + return {hdrBegin, nullptr /* end parsing */}; + } + std::unique_ptr item = std::make_unique(-1 - static_cast(value)); + return {hdrEnd, + parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; +} + +std::tuple handleBool(uint64_t value, const uint8_t* hdrBegin, + const uint8_t* hdrEnd, + ParseClient* parseClient) { + std::unique_ptr item = std::make_unique(value == TRUE); + return {hdrEnd, + parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; +} + +std::tuple handleNull(const uint8_t* hdrBegin, const uint8_t* hdrEnd, + ParseClient* parseClient) { + std::unique_ptr item = std::make_unique(); + return {hdrEnd, + parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; +} + +template +std::tuple handleString(uint64_t length, const uint8_t* hdrBegin, + const uint8_t* valueBegin, const uint8_t* end, + const std::string& errLabel, + ParseClient* parseClient) { + if (end - valueBegin < static_cast(length)) { + parseClient->error(hdrBegin, insufficientLengthString(length, end - valueBegin, errLabel)); + return {hdrBegin, nullptr /* end parsing */}; + } + + std::unique_ptr item = std::make_unique(valueBegin, valueBegin + length); + return {valueBegin + length, + parseClient->item(item, hdrBegin, valueBegin, valueBegin + length)}; +} + +class IncompleteItem { + public: + virtual ~IncompleteItem() {} + virtual void add(std::unique_ptr item) = 0; +}; + +class IncompleteArray : public Array, public IncompleteItem { + public: + explicit IncompleteArray(size_t size) : mSize(size) {} + + // We return the "complete" size, rather than the actual size. + size_t size() const override { return mSize; } + + void add(std::unique_ptr item) override { + mEntries.reserve(mSize); + mEntries.push_back(std::move(item)); + } + + private: + size_t mSize; +}; + +class IncompleteMap : public Map, public IncompleteItem { + public: + explicit IncompleteMap(size_t size) : mSize(size) {} + + // We return the "complete" size, rather than the actual size. + size_t size() const override { return mSize; } + + void add(std::unique_ptr item) override { + if (mKeyHeldForAdding) { + mEntries.reserve(mSize); + mEntries.push_back({std::move(mKeyHeldForAdding), std::move(item)}); + } else { + mKeyHeldForAdding = std::move(item); + } + } + + private: + std::unique_ptr mKeyHeldForAdding; + size_t mSize; +}; + +class IncompleteSemanticTag : public SemanticTag, public IncompleteItem { + public: + explicit IncompleteSemanticTag(uint64_t value) : SemanticTag(value) {} + + // We return the "complete" size, rather than the actual size. + size_t size() const override { return 1; } + + void add(std::unique_ptr item) override { mTaggedItem = std::move(item); } +}; + +std::tuple handleEntries(size_t entryCount, const uint8_t* hdrBegin, + const uint8_t* pos, const uint8_t* end, + const std::string& typeName, + bool emitViews, + ParseClient* parseClient) { + while (entryCount > 0) { + --entryCount; + if (pos == end) { + parseClient->error(hdrBegin, "Not enough entries for " + typeName + "."); + return {hdrBegin, nullptr /* end parsing */}; + } + std::tie(pos, parseClient) = parseRecursively(pos, end, emitViews, parseClient); + if (!parseClient) return {hdrBegin, nullptr}; + } + return {pos, parseClient}; +} + +std::tuple handleCompound( + std::unique_ptr item, uint64_t entryCount, const uint8_t* hdrBegin, + const uint8_t* valueBegin, const uint8_t* end, const std::string& typeName, + bool emitViews, ParseClient* parseClient) { + parseClient = + parseClient->item(item, hdrBegin, valueBegin, valueBegin /* don't know the end yet */); + if (!parseClient) return {hdrBegin, nullptr}; + + const uint8_t* pos; + std::tie(pos, parseClient) = + handleEntries(entryCount, hdrBegin, valueBegin, end, typeName, emitViews, parseClient); + if (!parseClient) return {hdrBegin, nullptr}; + + return {pos, parseClient->itemEnd(item, hdrBegin, valueBegin, pos)}; +} + +std::tuple parseRecursively(const uint8_t* begin, const uint8_t* end, + bool emitViews, ParseClient* parseClient) { + const uint8_t* pos = begin; + + MajorType type = static_cast(*pos & 0xE0); + uint8_t tagInt = *pos & 0x1F; + ++pos; + + bool success = true; + uint64_t addlData; + if (tagInt < ONE_BYTE_LENGTH) { + addlData = tagInt; + } else if (tagInt > EIGHT_BYTE_LENGTH) { + parseClient->error( + begin, + "Reserved additional information value or unsupported indefinite length item."); + return {begin, nullptr}; + } else { + switch (tagInt) { + case ONE_BYTE_LENGTH: + std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); + break; + + case TWO_BYTE_LENGTH: + std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); + break; + + case FOUR_BYTE_LENGTH: + std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); + break; + + case EIGHT_BYTE_LENGTH: + std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); + break; + + default: + CHECK(false); // It's impossible to get here + break; + } + } + + if (!success) return {begin, nullptr}; + + switch (type) { + case UINT: + return handleUint(addlData, begin, pos, parseClient); + + case NINT: + return handleNint(addlData, begin, pos, parseClient); + + case BSTR: + if (emitViews) { + return handleString(addlData, begin, pos, end, "byte string", parseClient); + } else { + return handleString(addlData, begin, pos, end, "byte string", parseClient); + } + + case TSTR: + if (emitViews) { + return handleString(addlData, begin, pos, end, "text string", parseClient); + } else { + return handleString(addlData, begin, pos, end, "text string", parseClient); + } + + case ARRAY: + return handleCompound(std::make_unique(addlData), addlData, begin, pos, + end, "array", emitViews, parseClient); + + case MAP: + return handleCompound(std::make_unique(addlData), addlData * 2, begin, + pos, end, "map", emitViews, parseClient); + + case SEMANTIC: + return handleCompound(std::make_unique(addlData), 1, begin, pos, + end, "semantic", emitViews, parseClient); + + case SIMPLE: + switch (addlData) { + case TRUE: + case FALSE: + return handleBool(addlData, begin, pos, parseClient); + case NULL_V: + return handleNull(begin, pos, parseClient); + default: + parseClient->error(begin, "Unsupported floating-point or simple value."); + return {begin, nullptr}; + } + } + CHECK(false); // Impossible to get here. + return {}; +} + +class FullParseClient : public ParseClient { + public: + virtual ParseClient* item(std::unique_ptr& item, const uint8_t*, const uint8_t*, + const uint8_t* end) override { + if (mParentStack.empty() && !item->isCompound()) { + // This is the first and only item. + mTheItem = std::move(item); + mPosition = end; + return nullptr; // We're done. + } + + if (item->isCompound()) { + // Starting a new compound data item, i.e. a new parent. Save it on the parent stack. + // It's safe to save a raw pointer because the unique_ptr is guaranteed to stay in + // existence until the corresponding itemEnd() call. + mParentStack.push(item.get()); + return this; + } else { + appendToLastParent(std::move(item)); + return this; + } + } + + virtual ParseClient* itemEnd(std::unique_ptr& item, const uint8_t*, const uint8_t*, + const uint8_t* end) override { + CHECK(item->isCompound() && item.get() == mParentStack.top()); + mParentStack.pop(); + + if (mParentStack.empty()) { + mTheItem = std::move(item); + mPosition = end; + return nullptr; // We're done + } else { + appendToLastParent(std::move(item)); + return this; + } + } + + virtual void error(const uint8_t* position, const std::string& errorMessage) override { + mPosition = position; + mErrorMessage = errorMessage; + } + + std::tuple /* result */, const uint8_t* /* newPos */, + std::string /* errMsg */> + parseResult() { + std::unique_ptr p = std::move(mTheItem); + return {std::move(p), mPosition, std::move(mErrorMessage)}; + } + + private: + void appendToLastParent(std::unique_ptr item) { + auto parent = mParentStack.top(); +//#if __has_feature(cxx_rtti) + assert(dynamic_cast(parent)); +//#endif + + IncompleteItem* parentItem{}; + if (parent->type() == ARRAY) { + parentItem = static_cast(parent); + } else if (parent->type() == MAP) { + parentItem = static_cast(parent); + } else if (parent->asSemanticTag()) { + parentItem = static_cast(parent); + } else { + CHECK(false); // Impossible to get here. + } + parentItem->add(std::move(item)); + } + + std::unique_ptr mTheItem; + std::stack mParentStack; + const uint8_t* mPosition = nullptr; + std::string mErrorMessage; +}; + +} // anonymous namespace + +void parse(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient) { + parseRecursively(begin, end, false, parseClient); +} + +std::tuple /* result */, const uint8_t* /* newPos */, + std::string /* errMsg */> +parse(const uint8_t* begin, const uint8_t* end) { + FullParseClient parseClient; + parse(begin, end, &parseClient); + return parseClient.parseResult(); +} + +void parseWithViews(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient) { + parseRecursively(begin, end, true, parseClient); +} + +std::tuple /* result */, const uint8_t* /* newPos */, + std::string /* errMsg */> +parseWithViews(const uint8_t* begin, const uint8_t* end) { + FullParseClient parseClient; + parseWithViews(begin, end, &parseClient); + return parseClient.parseResult(); +} + +} // namespace cppbor diff --git a/ProvisioningTool/src/provision.cpp b/ProvisioningTool/src/provision.cpp new file mode 100644 index 00000000..2007e08e --- /dev/null +++ b/ProvisioningTool/src/provision.cpp @@ -0,0 +1,345 @@ +/* + ** + ** Copyright 2021, 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 +#include +#include +#include +#include "socket.h" +#include +#include +#include +#include +#include +#include + +enum ProvisionStatus { + NOT_PROVISIONED = 0x00, + PROVISION_STATUS_ATTESTATION_KEY = 0x01, + PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x02, + PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04, + PROVISION_STATUS_ATTEST_IDS = 0x08, + PROVISION_STATUS_PRESHARED_SECRET = 0x10, + PROVISION_STATUS_BOOT_PARAM = 0x20, + PROVISION_STATUS_PROVISIONING_LOCKED = 0x40, +}; + +std::string provisionStatusApdu = hex2str("80084000000000"); +std::string lockProvisionApdu = hex2str("80074000000000"); + +Json::Value root; +static double keymasterVersion = -1; +static std::string inputFileName; +using cppbor::Item; +using cppbor::Array; +using cppbor::Uint; +using cppbor::MajorType; + +// static function declarations +static uint16_t getApduStatus(std::vector& inputData); +static int sendData(std::shared_ptr& pSocket, std::string input, std::vector& response); +static int provisionData(std::shared_ptr& pSocket, const char* jsonKey, std::vector& response); +static int getUint64(const std::unique_ptr &item, const uint32_t pos, uint64_t &value); + + +// Print usage. +void usage() { + printf("Usage: Please consturcture the apdu(s) with help of construct apdu tool and pass the output file to this utility.\n"); + printf("provision [options]\n"); + printf("Valid options are:\n"); + printf("-h, --help show this help message and exit.\n"); + printf("-v, --km_version version \t Version of the keymaster(4.1 for keymaster; 5 for keymint \n"); + printf("-i, --input jsonFile \t Input json file \n"); + printf("-s, --provision_status jsonFile \t Gets the provision status of applet. \n"); + printf("-l, --lock_provision jsonFile \t Gets the provision status of applet. \n"); + +} + +static uint16_t getApduStatus(std::vector& inputData) { + // Last two bytes are the status SW0SW1 + uint8_t SW0 = inputData.at(inputData.size() - 2); + uint8_t SW1 = inputData.at(inputData.size() - 1); + return (SW0 << 8 | SW1); +} + +static int sendData(std::shared_ptr& pSocket, std::string input, std::vector& response) { + + std::vector apdu(input.begin(), input.end()); + + if(!pSocket->sendData(apdu, response)) { + std::cout << "Failed to provision attestation key" << std::endl; + return FAILURE; + } + + // Response size should be greater than 2. Cbor output data followed by two bytes of APDU + // status. + if ((response.size() <= 2) || (getApduStatus(response) != APDU_RESP_STATUS_OK)) { + printf("\n Received error response with error: %d\n", getApduStatus(response)); + return FAILURE; + } + // remove the status bytes + response.pop_back(); + response.pop_back(); + return SUCCESS; +} + +int getUint64(const std::unique_ptr &item, const uint32_t pos, uint64_t &value) { + Array *arr = nullptr; + + if (MajorType::ARRAY != item.get()->type()) { + return FAILURE; + } + arr = const_cast(item.get()->asArray()); + if (arr->size() < (pos + 1)) { + return FAILURE; + } + std::unique_ptr subItem = std::move((*arr)[pos]); + const Uint* uintVal = subItem.get()->asUint(); + value = uintVal->value(); + return SUCCESS; +} + +int provisionData(std::shared_ptr& pSocket, std::string apdu, std::vector& response) { + if (SUCCESS != sendData(pSocket, apdu, response)) { + return FAILURE; + } + auto [item, pos, message] = cppbor::parse(response); + if(item != nullptr) { + uint64_t err; + if(MajorType::ARRAY == item.get()->type()) { + if(SUCCESS != getUint64(item, 0, err)) { + printf("\n Failed to parse the error code \n"); + return FAILURE; + } + } else if (MajorType::UINT == item.get()->type()) { + const Uint* uintVal = item.get()->asUint(); + err = uintVal->value(); + } + if (err != 0) { + printf("\n Failed with error:%ld", err); + return FAILURE; + } + } else { + printf("\n Failed to parse the response\n"); + return FAILURE; + } + return SUCCESS; +} + +int provisionData(std::shared_ptr& pSocket, const char* jsonKey, std::vector& response) { + Json::Value val = root.get(jsonKey, Json::Value::nullRef); + if (!val.isNull()) { + if (val.isString()) { + if (SUCCESS != provisionData(pSocket, hex2str(val.asString()), response)) { + printf("\n Error while provisioning %s \n", jsonKey); + return FAILURE; + } + } else { + printf("\n Fail: Expected (%s) tag value is string. \n", jsonKey); + return FAILURE; + } + } + printf("\n Successfully provisioned %s \n", jsonKey); + return SUCCESS; +} + +int openConnection(std::shared_ptr& pSocket) { + if (!pSocket->isConnected()) { + if (!pSocket->openConnection()) + return FAILURE; + } else { + printf("\n Socket already opened.\n"); + } + return SUCCESS; +} + +// Parses the input json file. Sends the apdus to JCServer. +int processInputFile() { + + if (keymasterVersion != KEYMASTER_VERSION && keymasterVersion != KEYMINT_VERSION) { + printf("\n Error unknown version.\n"); + usage(); + return FAILURE; + } + // Parse Json file + if (0 != readJsonFile(root, inputFileName)) { + return FAILURE; + } + std::shared_ptr pSocket = SocketTransport::getInstance(); + if (SUCCESS != openConnection(pSocket)) { + printf("\n Failed to open connection \n"); + return FAILURE; + } + std::vector response; + + if (keymasterVersion == KEYMASTER_VERSION) { + printf("\n Selected Keymaster version(%f) for provisioning \n", keymasterVersion); + if (0 != provisionData(pSocket, kAttestKey, response) || + 0 != provisionData(pSocket, kAttestCertChain, response) || + 0 != provisionData(pSocket, kAttestCertParams, response)) { + return FAILURE; + } + } else { + printf("\n Selected keymint version(%f) for provisioning \n", keymasterVersion); + if ( 0 != provisionData(pSocket, kDeviceUniqueKey, response) || + 0 != provisionData(pSocket, kAdditionalCertChain, response)) { + return FAILURE; + } + } + if (0 != provisionData(pSocket, kAttestationIds, response) || + 0 != provisionData(pSocket, kSharedSecret, response) || + 0 != provisionData(pSocket, kBootParams, response)) { + return FAILURE; + } + return SUCCESS; +} + +int lockProvision() { + std::vector response; + std::shared_ptr pSocket = SocketTransport::getInstance(); + if (SUCCESS != openConnection(pSocket)) { + printf("\n Failed to open connection \n"); + return FAILURE; + } + if (SUCCESS != provisionData(pSocket, lockProvisionApdu, response)) { + printf("\n Failed to lock provision.\n"); + return FAILURE; + } + printf("\n Provision lock is successfull.\n"); + return SUCCESS; +} + +int getProvisionStatus() { + std::vector response; + std::shared_ptr pSocket = SocketTransport::getInstance(); + if (SUCCESS != openConnection(pSocket)) { + printf("\n Failed to open connection \n"); + return FAILURE; + } + + if (SUCCESS != provisionData(pSocket, provisionStatusApdu, response)) { + printf("\n Failed to get provision status \n"); + return FAILURE; + } + auto [item, pos, message] = cppbor::parse(response); + if(item != nullptr) { + uint64_t status; + if(SUCCESS != getUint64(item, 1, status)) { + printf("\n Failed to get the provision status.\n"); + return FAILURE; + } + if ( (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_KEY)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_BOOT_PARAM))) { + printf("\n SE is provisioned \n"); + } else { + if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_KEY)) { + printf("\n Attestation key is not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) { + printf("\n Attestation certificate chain is not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) { + printf("\n Attestation certificate params are not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) { + printf("\n Shared secret is not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_BOOT_PARAM)) { + printf("\n Boot params are not provisioned \n"); + } + } + } else { + printf("\n Fail to parse the response \n"); + return FAILURE; + } + return SUCCESS; +} + +int main(int argc, char* argv[]) { + int c; + bool provisionStatusSet = false; + bool lockProvisionSet = false; + + struct option longOpts[] = { + {"km_version", required_argument, NULL, 'v'}, + {"input", required_argument, NULL, 'i'}, + {"provision_status", no_argument, NULL, 's'}, + {"lock_provision", no_argument, NULL, 'l'}, + {"help", no_argument, NULL, 'h'}, + {0,0,0,0} + }; + + if (argc <= 1) { + printf("\n Invalid command \n"); + usage(); + return FAILURE; + } + + /* getopt_long stores the option index here. */ + while ((c = getopt_long(argc, argv, ":hlsv:i:", longOpts, NULL)) != -1) { + switch(c) { + case 'v': + // keymaster version + keymasterVersion = atof(optarg); + std::cout << "Version: " << keymasterVersion << std::endl; + break; + case 'i': + // input file + inputFileName = std::string(optarg); + std::cout << "input file: " << inputFileName << std::endl; + break; + case 's': + provisionStatusSet = true; + break; + case 'l': + lockProvisionSet = true; + break; + case 'h': + // help + usage(); + return SUCCESS; + case ':': + printf("\n Required arguments missing.\n"); + usage(); + return FAILURE; + case '?': + default: + printf("\n Invalid option\n"); + usage(); + return FAILURE; + } + } + // Process input file; send apuds to JCServer over socket. + if (argc >= 5) { + if (SUCCESS != processInputFile()) { + return FAILURE; + } + } else if (keymasterVersion != -1 || !inputFileName.empty()) { + printf("\n For provisioning km_version and input json file arguments are mandatory.\n"); + usage(); + return FAILURE; + } + if (provisionStatusSet) + getProvisionStatus(); + if (lockProvisionSet) + lockProvision(); + return SUCCESS; +} + + diff --git a/ProvisioningTool/src/socket.cpp b/ProvisioningTool/src/socket.cpp new file mode 100644 index 00000000..b85e09c5 --- /dev/null +++ b/ProvisioningTool/src/socket.cpp @@ -0,0 +1,110 @@ +/* + ** + ** Copyright 2021, 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 +#include +#include +#include +#include +#include +#include +#include +#include "socket.h" + +#define PORT 8080 +#define IPADDR "127.0.0.1" +//#define IPADDR "192.168.0.5" +#define MAX_RECV_BUFFER_SIZE 2500 + +using namespace std; + +SocketTransport::~SocketTransport() { + if (closeConnection()) + std::cout << "Socket is closed"; +} + +bool SocketTransport::openConnection() { + struct sockaddr_in serv_addr; + if ((mSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("Socket "); + return false; + } + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(PORT); + + // Convert IPv4 and IPv6 addresses from text to binary form + if (inet_pton(AF_INET, IPADDR, &serv_addr.sin_addr) <= 0) { + std::cout << "Invalid address/ Address not supported."; + return false; + } + + if (connect(mSocket, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { + close(mSocket); + perror("Socket "); + return false; + } + socketStatus = true; + return true; +} + +bool SocketTransport::sendData(const std::vector& inData, std::vector& output) { + uint8_t buffer[MAX_RECV_BUFFER_SIZE]; + int count = 1; + while (!socketStatus && count++ < 5) { + sleep(1); + std::cout << "Trying to open socket connection... count: " << count; + openConnection(); + } + + if (count >= 5) { + std::cout << "Failed to open socket connection"; + return false; + } + + if (0 > send(mSocket, inData.data(), inData.size(), 0)) { + static int connectionResetCnt = 0; /* To avoid loop */ + if (ECONNRESET == errno && connectionResetCnt == 0) { + // Connection reset. Try open socket and then sendData. + socketStatus = false; + connectionResetCnt++; + return sendData(inData, output); + } + std::cout << "Failed to send data over socket err: " << errno; + connectionResetCnt = 0; + return false; + } + + ssize_t valRead = read(mSocket, buffer, MAX_RECV_BUFFER_SIZE); + if (0 > valRead) { + std::cout << "Failed to read data from socket."; + } + for (ssize_t i = 0; i < valRead; i++) { + output.push_back(buffer[i]); + } + return true; +} + +bool SocketTransport::closeConnection() { + close(mSocket); + socketStatus = false; + return true; +} + +bool SocketTransport::isConnected() { + return socketStatus; +} + diff --git a/ProvisioningTool/src/utils.cpp b/ProvisioningTool/src/utils.cpp new file mode 100644 index 00000000..41ad8a6c --- /dev/null +++ b/ProvisioningTool/src/utils.cpp @@ -0,0 +1,96 @@ +/* + ** + ** Copyright 2021, 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 +#include +#include +#include + + +constexpr char hex_value[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // '0'..'9' + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'A'..'F' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'a'..'f' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +std::string getHexString(std::vector& input) { + std::stringstream ss; + for (auto b : input) { + ss << std::setw(2) << std::setfill('0') << std::hex << (int) (b & 0xFF); + } + return ss.str(); +} + + +std::string hex2str(std::string a) { + std::string b; + size_t num = a.size() / 2; + b.resize(num); + for (size_t i = 0; i < num; i++) { + b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]); + } + return b; +} + + +// Parses the json file and returns 0 if success; otherwise 1. +int readJsonFile(Json::Value& root, std::string& inputFileName) { + Json::CharReaderBuilder builder; + std::string errorMessage; + + if(!root.empty()) { + printf("\n Already parsed \n"); + return 1; + } + std::ifstream stream(inputFileName); + if (Json::parseFromStream(builder, stream, &root, &errorMessage)) { + printf("\n Parsed json file successfully.\n"); + return 0; + } else { + printf("\n Failed to parse json file error:%s\n", errorMessage.c_str()); + return 1; + } +} + +// Write the json data to the output file. +int writeJsonFile(Json::Value& writerRoot, std::string& outputFileName) { + + std::ofstream ofs; + // Delete file if already exists. + std::remove(outputFileName.data()); + ofs.open(outputFileName, std::ofstream::out | std::ios_base::app); + if (ofs.fail()) { + printf("\n Fail to open the output file:%s", outputFileName.c_str()); + return FAILURE; + } + + Json::StyledWriter styledWriter; + ofs << styledWriter.write(writerRoot); + + ofs.close(); + return SUCCESS; +} \ No newline at end of file diff --git a/aosp_integration_patches/build-make.patch b/aosp_integration_patches/build-make.patch new file mode 100644 index 00000000..6cdb7423 --- /dev/null +++ b/aosp_integration_patches/build-make.patch @@ -0,0 +1,20 @@ +commit ef71f76d787eab8e5332a467a2e08e186acfdaa5 +Author: Manish Dwivedi +Date: Tue Jun 29 01:42:20 2021 +0000 + + aosp_master + + Change-Id: I82cb82e483f55608b9441f08c0a550bc6401822a + +diff --git a/target/product/gsi/current.txt b/target/product/gsi/current.txt +index c753e6c3be..765ca558c9 100644 +--- a/target/product/gsi/current.txt ++++ b/target/product/gsi/current.txt +@@ -113,6 +113,7 @@ VNDK-core: libgatekeeper.so + VNDK-core: libgui.so + VNDK-core: libhardware_legacy.so + VNDK-core: libhidlallocatorutils.so ++VNDK-core: libjc_transport.so + VNDK-core: libjpeg.so + VNDK-core: libldacBT_abr.so + VNDK-core: libldacBT_enc.so diff --git a/aosp_integration_patches/cuttlefish.patch b/aosp_integration_patches/cuttlefish.patch new file mode 100644 index 00000000..4007d0ad --- /dev/null +++ b/aosp_integration_patches/cuttlefish.patch @@ -0,0 +1,45 @@ +commit a205fd77b33093e31a0e1b8d8e9fed453e6dea20 +Author: Manish Dwivedi +Date: Tue Jun 29 01:39:31 2021 +0000 + + aosp upmerge + + Change-Id: I6e5ef5ffcfa0d34c5a5bf65cbc73604270a3222c + +diff --git a/shared/BoardConfig.mk b/shared/BoardConfig.mk +index aec8869f2..a8185aacd 100644 +--- a/shared/BoardConfig.mk ++++ b/shared/BoardConfig.mk +@@ -207,6 +207,8 @@ BOARD_RAMDISK_USE_LZ4 := true + # To see full logs from init, disable ratelimiting. + # The default is 5 messages per second amortized, with a burst of up to 10. + BOARD_KERNEL_CMDLINE += printk.devkmsg=on ++BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive ++ + BOARD_KERNEL_CMDLINE += firmware_class.path=/vendor/etc/ + + BOARD_KERNEL_CMDLINE += init=/init +diff --git a/shared/device.mk b/shared/device.mk +index 290b61f97..6ccae3bae 100644 +--- a/shared/device.mk ++++ b/shared/device.mk +@@ -502,6 +502,11 @@ endif + PRODUCT_PACKAGES += \ + $(LOCAL_KEYMINT_PRODUCT_PACKAGE) + ++PRODUCT_PACKAGES += \ ++ android.hardware.keymaster@4.1-strongbox.service \ ++ ++ ++ + # Keymint configuration + PRODUCT_COPY_FILES += \ + frameworks/native/data/etc/android.software.device_id_attestation.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.device_id_attestation.xml +@@ -611,6 +616,7 @@ PRODUCT_PACKAGES += setup_wifi + PRODUCT_VENDOR_PROPERTIES += ro.vendor.wifi_impl=virt_wifi + endif + ++ + # Host packages to install + PRODUCT_HOST_PACKAGES += socket_vsock_proxy + diff --git a/aosp_integration_patches/sepolicy.patch b/aosp_integration_patches/sepolicy.patch new file mode 100644 index 00000000..f0f1a676 --- /dev/null +++ b/aosp_integration_patches/sepolicy.patch @@ -0,0 +1,61 @@ +commit ad5c9bf6cc9c806717ced142cc44b19b0b46d334 +Author: Manish Dwivedi +Date: Tue Jun 29 01:36:44 2021 +0000 + + aosp upmerge + + Change-Id: I99cde570325f6d56103fbbf318a5afdf0cbd1831 + +diff --git a/public/hal_neverallows.te b/public/hal_neverallows.te +index faec07420..020ee16bd 100644 +--- a/public/hal_neverallows.te ++++ b/public/hal_neverallows.te +@@ -2,6 +2,7 @@ + # network capabilities + neverallow { + halserverdomain ++ -hal_keymaster_server + -hal_bluetooth_server + -hal_can_controller_server + -hal_wifi_server +@@ -19,6 +20,7 @@ neverallow { + # will result in CTS failure. + neverallow { + halserverdomain ++ -hal_keymaster_server + -hal_automotive_socket_exemption + -hal_can_controller_server + -hal_tetheroffload_server +@@ -31,6 +33,7 @@ neverallow { + + neverallow { + halserverdomain ++ -hal_keymaster_server + -hal_automotive_socket_exemption + -hal_can_controller_server + -hal_tetheroffload_server +diff --git a/vendor/file_contexts b/vendor/file_contexts +index 12e5d9f97..a6d7bd72f 100644 +--- a/vendor/file_contexts ++++ b/vendor/file_contexts +@@ -71,6 +71,7 @@ + /(vendor|system/vendor)/bin/hw/android\.hardware\.sensors@[0-9]\.[0-9]-service(\.multihal)? u:object_r:hal_sensors_default_exec:s0 + /(vendor|system/vendor)/bin/hw/android\.hardware\.secure_element@1\.0-service u:object_r:hal_secure_element_default_exec:s0 + /(vendor|system/vendor)/bin/hw/android\.hardware\.security\.keymint-service u:object_r:hal_keymint_default_exec:s0 ++/(vendor|system/vendor)/bin/hw/android\.hardware\.keymaster@4\.1-strongbox\.service u:object_r:hal_keymaster_default_exec:s0 + /(vendor|system/vendor)/bin/hw/rild u:object_r:rild_exec:s0 + /(vendor|system/vendor)/bin/hw/android\.hardware\.thermal@1\.[01]-service u:object_r:hal_thermal_default_exec:s0 + /(vendor|system/vendor)/bin/hw/android\.hardware\.tv\.cec@1\.[01]-service u:object_r:hal_tv_cec_default_exec:s0 +diff --git a/vendor/hal_keymaster_default.te b/vendor/hal_keymaster_default.te +index 6f0d82aa7..2bbcff6a7 100644 +--- a/vendor/hal_keymaster_default.te ++++ b/vendor/hal_keymaster_default.te +@@ -5,3 +5,8 @@ type hal_keymaster_default_exec, exec_type, vendor_file_type, file_type; + init_daemon_domain(hal_keymaster_default) + + get_prop(hal_keymaster_default, vendor_security_patch_level_prop); ++allow hal_keymaster_default self:tcp_socket { connect create write read getattr getopt setopt }; ++allow hal_keymaster_default port_type:tcp_socket name_connect; ++allow hal_keymaster_default port:tcp_socket { name_connect }; ++allow hal_keymaster_default vendor_data_file:file { open read getattr }; ++