Merge pull request #866 from nsacyber/v3_issue_825-remove-hirs_provisionertpm2

[#825] Delete HIRS Provisioner TPM2
This commit is contained in:
iadgovuser26 2024-11-15 10:33:49 -05:00 committed by GitHub
commit e1f59072be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
53 changed files with 15 additions and 7933 deletions

View File

@ -89,7 +89,7 @@ protobuf {
sourceSets { sourceSets {
main { main {
proto { proto {
srcDir '../HIRS_ProvisionerTPM2/src' srcDir '../HIRS_Provisioner.NET/hirs/Resources'
} }
} }
} }

View File

@ -7,10 +7,10 @@
dir=$(pwd) dir=$(pwd)
# Relative paths are different when building locally versus on CI # Relative paths are different when building locally versus on CI
if [[ "$dir" == *"package"* ]]; then if [[ "$dir" == *"package"* ]]; then
SRC_DIR=$dir/../../../../../../HIRS_ProvisionerTPM2/src SRC_DIR=$dir/../../../../../../HIRS_Provisioner.NET/hirs/Resources
DEST_DIR=$dir/../src/main/java DEST_DIR=$dir/../src/main/java
else else
SRC_DIR=../../HIRS_ProvisionerTPM2/src SRC_DIR=../../HIRS_Provisioner.NET/hirs/Resources
DEST_DIR=../src/main/java DEST_DIR=../src/main/java
fi fi
protoc -I=$SRC_DIR --java_out=$DEST_DIR $SRC_DIR/ProvisionerTpm2.proto protoc -I=$SRC_DIR --java_out=$DEST_DIR $SRC_DIR/ProvisionerTpm2.proto

View File

@ -7,10 +7,10 @@
dir=$(pwd) dir=$(pwd)
# Relative paths are different when building locally versus on CI # Relative paths are different when building locally versus on CI
if [[ "$dir" == *"package"* ]]; then if [[ "$dir" == *"package"* ]]; then
SRC_DIR=$dir/../../../../../../HIRS_ProvisionerTPM2/src SRC_DIR=$dir/../../../../../../HIRS_Provisioner.NET/hirs/Resources
DEST_DIR=$dir/../src/main/java DEST_DIR=$dir/../src/main/java
else else
SRC_DIR=../../HIRS_ProvisionerTPM2/src SRC_DIR=../../HIRS_Provisioner.NET/hirs/Resources
DEST_DIR=../src/main/java DEST_DIR=../src/main/java
fi fi
protoc -I=$SRC_DIR --java_out=$DEST_DIR $SRC_DIR/ProvisionerTpm2.proto protoc -I=$SRC_DIR --java_out=$DEST_DIR $SRC_DIR/ProvisionerTpm2.proto

View File

@ -1,312 +0,0 @@
# Root CMake file in charge of managing build/testing of TPM 2.0 Provisioner Library and Executable
# General CMake Configuration
cmake_minimum_required(VERSION 2.8.12)
# Initialize Project
project(HIRS_ProvisionerTPM2)
# Set Project Information Variables
set(PROJECT_NAME hirs-provisioner-tpm2)
# Retrieve Complete Version
file(STRINGS ../VERSION COMPLETE_VERSION LIMIT_COUNT 1)
# Break Version into Components
string(REGEX MATCHALL "[0-9]+" VERSION_COMPONENTS ${COMPLETE_VERSION})
# Set MAJOR_VERSION
list(GET VERSION_COMPONENTS 0 MAJOR_VERSION)
# Set MINOR_VERSION
list(GET VERSION_COMPONENTS 1 MINOR_VERSION)
# Set PATCH_VERSION
list(GET VERSION_COMPONENTS 2 PATCH_VERSION)
# Sets PACKAGE_RELEASE_NUMBER & PACKAGE_RELEASE_RETURN_CODE
execute_process(COMMAND sh "package/package_release.sh"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE PACKAGE_RELEASE_RETURN_ERROR
OUTPUT_VARIABLE PACKAGE_RELEASE_NUMBER
ERROR_STRIP_TRAILING_WHITESPACE
OUTPUT_STRIP_TRAILING_WHITESPACE)
list(LENGTH VERSION_COMPONENTS VERSION_COMPONENTS_LENGTH)
# Check if version information pulled successfully, error otherwise
if(NOT ${VERSION_COMPONENTS_LENGTH} EQUAL 3)
message(FATAL_ERROR "Failed to pull version information from VERSION file, aborting.")
elseif(${PACKAGE_RELEASE_RETURN_ERROR})
message(FATAL_ERROR "Failed to pull package release information from git, aborting.")
endif()
# Embed version and package release into header file
configure_file ("${CMAKE_SOURCE_DIR}/include/Version.h.in"
"${CMAKE_SOURCE_DIR}/include/Version.h")
# Attempt to Determine Build Environment
if (UNIX AND NOT APPLE)
file(READ /etc/os-release OS_INFO)
string(REGEX MATCH "NAME=\"[A-Za-z ]+\"" DISTRIBUTION_NAME ${OS_INFO})
string(REGEX MATCH "VERSION_ID=\"[0-9. ]+\"" DISTRIBUTION_VERSION ${OS_INFO})
string(REPLACE "NAME=" "" DISTRIBUTION ${DISTRIBUTION_NAME})
string(REPLACE "VERSION_ID=" "" DISTRIBUTION_VERSION ${DISTRIBUTION_VERSION})
string(REPLACE "\"" "" DISTRIBUTION ${DISTRIBUTION})
string(REPLACE "\"" "" DISTRIBUTION_VERSION ${DISTRIBUTION_VERSION})
endif()
# Set C++ Standard 11 based on version information
if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
else ()
set(CMAKE_CXX_FLAGS "-std=gnu++11")
endif ()
# Set User configurable options
option(BUILD_TESTS "Set to OFF to turn off testing" ON)
option(GENERATE_DOCS "Set to OFF to turn off documentation generation" ON)
option(STATIC_ANALYSIS "Set to OFF to turn off Static Analysis" ON)
option(STYLE_CHECK "Set to OFF to turn off code style checking" ON)
# Set Project Path Variables
set(EXECUTABLE_OUTPUT_PATH bin)
set(PROJECT_CONFIG_DIR ${CMAKE_SOURCE_DIR}/config)
set(PROJECT_CONFIG_FILES ${PROJECT_CONFIG_DIR}/log4cplus_config.ini)
# Set directories to look for header files
include_directories(${CMAKE_SOURCE_DIR}/include)
# Protobuf generated files are placed in the binary directory. The structure of
# the binary directory matches that of the source directory. Specifically,
# protobuf places the generated files in the same subfolder of the binary
# directory where the spec file was located in the source directory. In this
# case, that is the src folder. We get this file location automatically in the
# src/CMakeLists.txt file, but the variable holding its location is not defined
# in the scope of this file, so we need to add that directory to the include
# path manually.
include_directories(${CMAKE_BINARY_DIR}/src)
# Attempt to find local 3rd party libraries and set their absolute paths
# Sets LOG_LIB
find_library(LOG_LIB NAMES log4cplus)
list(APPEND REQUIRED_LIBS ${LOG_LIB})
# Sets RE_LIB
find_library(RE_LIB NAMES re2)
list(APPEND REQUIRED_LIBS ${RE_LIB})
# Setup for TPM2_TSS_LIBRARIES
find_library(TPM2_SAPI_LIB NAMES sapi tss2)
find_library(TPM2_TCTI_DEVICE_LIB NAMES tcti-device tss2)
find_library(TPM2_TCTI_SOCKET_LIB NAMES tcti-socket tss2)
find_library(TPM2_TCTI_TABRMD_LIB NAMES tcti-tabrmd tss2)
set(TPM2_TSS_LIBRARIES ${TPM2_SAPI_LIB} ${TPM2_TCTI_DEVICE_LIB}
${TPM2_TCTI_SOCKET_LIB} ${TPM2_TCTI_TABRMD_LIB})
list(APPEND REQUIRED_LIBS ${TPM2_TSS_LIBRARIES})
# Set variable to determine TSS SAPI import
set(TSS_LIBRARY "<sapi/tpm20.h>")
string(COMPARE EQUAL ${TPM2_SAPI_LIB} ${TPM2_TCTI_DEVICE_LIB} LEGACY_TSS2_LIB_PRESENT)
if(LEGACY_TSS2_LIB_PRESENT)
set(TSS_LIBRARY "<tss2/tpm20.h>")
endif()
# Embed correct TSS import into header file
configure_file ("${CMAKE_SOURCE_DIR}/include/Tss.h.in"
"${CMAKE_SOURCE_DIR}/include/Tss.h")
# Download necessary 3rd party libraries
# Setup for CPR
configure_file(lib/CPR.CMakeLists.txt.in ${CMAKE_BINARY_DIR}/lib/cpr-download/CMakeLists.txt)
set(USE_SYSTEM_CURL ON CACHE BOOL "Do not allow CPR to use its own version of curl." FORCE)
set(BUILD_CPR_TESTS OFF CACHE BOOL "Do not waste time running CPR unit tests" FORCE)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lib/cpr-download)
if(result)
message(FATAL_ERROR "CMake step for CPR failed: ${result}")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lib/cpr-download )
if(result)
message(FATAL_ERROR "Build step for CPR failed: ${result}")
endif()
# Add cpr directly to our build and define the cpr target.
add_subdirectory(${CMAKE_BINARY_DIR}/lib/cpr-src
${CMAKE_BINARY_DIR}/lib/cpr-build)
list(APPEND REQUIRED_LIBS ${CPR_LIBRARIES})
# Imports the FindProtobuf module, used to locate protobuf package and
# do source code generation
include(FindProtobuf)
# Finds protobuf binaries
find_package(Protobuf REQUIRED)
list(APPEND REQUIRED_LIBS ${PROTOBUF_LIBRARY})
# Define the TPM 2.0 Provisioner Library
add_subdirectory(src)
# Create project executable
add_executable(${PROJECT_NAME} src/TPM2_Provisioner.cpp ${PROJECT_CONFIG_FILES})
# In TPM 2.0 land, there is currently not a way to fetch the TPM version info
add_executable(tpm_version src/tpm_version.cpp)
target_link_libraries(tpm_version ${TPM2_SAPI_LIB} ${TPM2_TCTI_TABRMD_LIB})
# Link necessary libraries
target_link_libraries(${PROJECT_NAME} TPM2_PROVISIONER_LIBRARY)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
if(THREADS_HAVE_PTHREAD_ARG)
target_compile_options(${PROJECT_NAME} PUBLIC "-pthread")
endif()
if(CMAKE_THREAD_LIBS_INIT)
target_link_libraries(${PROJECT_NAME} "${CMAKE_THREAD_LIBS_INIT}")
endif()
# Set commands for installation of project on target system (i.e. "make install")
install(TARGETS ${PROJECT_NAME} tpm_version
DESTINATION "bin")
install(FILES config/log4cplus_config.ini DESTINATION /etc/hirs/TPM2_Provisioner)
install(FILES scripts/tpm_aca_provision DESTINATION /usr/local/bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY DESTINATION ${/var/log/hirs/provisioner}
DIRECTORY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ
GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY DESTINATION ${/etc/hirs/provisioner}
DIRECTORY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ
GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(FILES ../HIRS_Provisioner/src/main/resources/defaults.properties DESTINATION /etc/hirs/provisioner RENAME provisioner.properties)
install(FILES ../HIRS_Provisioner/hirs-provisioner-config.sh DESTINATION /etc/hirs/provisioner
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ
GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(FILES ../HIRS_Provisioner/scripts/install/hirs-provisioner.sh DESTINATION /etc/hirs/provisioner
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ
GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
execute_process(COMMAND cp ../HIRS_Utils/src/main/resources/logging.properties ./config/
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
if(result)
message(FATAL_ERROR "cp logging.properties from HIRS_Utils failed.")
endif()
execute_process(COMMAND cp ../HIRS_Provisioner/scripts/install/tpm_aca_provision ./scripts/
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
if(result)
message(FATAL_ERROR "cp tpm_aca_provision from HIRS_Provisioner failed.")
endif()
install(FILES config/logging.properties DESTINATION /etc/hirs/)
# check if Doxygen is installed
if(GENERATE_DOCS)
find_package(Doxygen)
if (DOXYGEN_FOUND)
# set input config file
set(DOXYGEN_CONFIG ${PROJECT_CONFIG_DIR}/doxygen.config)
add_custom_target( doc_doxygen ALL
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_CONFIG}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Generating API documentation with Doxygen"
VERBATIM )
else (DOXYGEN_FOUND)
message("Doxygen needs to be installed to generate the doxygen documentation")
endif (DOXYGEN_FOUND)
endif(GENERATE_DOCS)
# Based on user-defined flag, optionally code style check the TPM 2.0 Library
if(STYLE_CHECK)
# Download and integrate CppLint for Style Checking
configure_file(lib/CppLint.CMakeLists.txt.in ${CMAKE_BINARY_DIR}/lib/cpplint/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lib/cpplint)
if(result)
message(FATAL_ERROR "CMake step for CppLint failed: ${result}")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lib/cpplint)
if(result)
message(FATAL_ERROR "Build step for CppLint failed: ${result}")
endif()
configure_file(${CMAKE_BINARY_DIR}/lib/cpplint-download/cpplint/cpplint.py ${CMAKE_SOURCE_DIR}/lint/cpplint.py)
add_custom_command(
TARGET ${PROJECT_NAME}
COMMENT "Run Style Check"
PRE_BUILD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/lint
COMMAND python cpplint.py --root=${CMAKE_SOURCE_DIR}/../ --filter=-build/c++11,-legal/copyright ${CMAKE_SOURCE_DIR}/src/*.cpp ${CMAKE_SOURCE_DIR}/include/*.hpp ${CMAKE_SOURCE_DIR}/src/*.c ${CMAKE_SOURCE_DIR}/include/*.h ${CMAKE_SOURCE_DIR}/test/*.cpp
)
endif(STYLE_CHECK)
if(STATIC_ANALYSIS)
add_custom_command(
TARGET ${PROJECT_NAME}
COMMENT "Run Cppcheck Static Analysis"
PRE_BUILD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND cppcheck
--enable=warning,performance,portability,style
--std=c++11
--library=posix.cfg
--error-exitcode=1
--verbose
--suppress=readdirCalled
--suppress=passedByValue
-I include/
src/
)
endif(STATIC_ANALYSIS)
# Set variables for CPack Package generation tool
set(CPACK_PACKAGE_NAME HIRS_Provisioner_TPM_2_0)
set(CPACK_PACKAGE_VENDOR "U.S. Government")
set(CPACK_PACKAGE_CONTACT "U.S. Government")
set(CPACK_PACKAGE_VERSION_MAJOR ${MAJOR_VERSION})
set(CPACK_PACKAGE_VERSION_MINOR ${MINOR_VERSION})
set(CPACK_PACKAGE_VERSION_PATCH ${PATCH_VERSION})
set(CPACK_PACKAGE_RELEASE ${PACKAGE_RELEASE_NUMBER})
set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
# Setup Development Distribution CPack
if (${DISTRIBUTION} STREQUAL "Ubuntu")
# Set variables specific to CPack DEB package generator
set(CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_NAME "HIRSProvisionerTPM2.0")
set(CPACK_DEBIAN_PACKAGE_SECTION "admin")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "liblog4cplus-1.1-9(>=1.1.2), libcurl4-openssl-dev(>=7.0.0), paccor, procps(>=3.3.0)")
# Set variables specific to Ubuntu release version
if (${DISTRIBUTION_VERSION} STREQUAL "16.04")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}, libre2-1v5(>=20160201), libprotobuf9v5(>=2.4.1)")
else()
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}, libprotobuf10(>=2.4.1)")
if (${DISTRIBUTION_VERSION} STREQUAL "17.10")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}, libre2-3(>=20160201)")
elseif(${DISTRIBUTION_VERSION} STREQUAL "18.04" OR ${DISTRIBUTION_VERSION} STREQUAL "18.10")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}, libre2-4(>=20160201)")
endif()
endif()
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE amd64)
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA ${CMAKE_SOURCE_DIR}/package/postinst)
set(CPACK_PACKAGE_FILE_NAME "${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
elseif (${DISTRIBUTION} STREQUAL "CentOS Linux")
# Set variables specific to CPack RPM package generator
set(CPACK_GENERATOR "RPM")
set(CPACK_RPM_PACKAGE_NAME "HIRS_Provisioner_TPM_2_0")
set(CPACK_RPM_PACKAGE_RELEASE_DIST "el7")
set(CPACK_RPM_PACKAGE_LICENSE "Apache License, Version 2.0")
set(CPACK_RPM_PACKAGE_GROUP "System Environment/Base")
set(CPACK_RPM_PACKAGE_REQUIRES "log4cplus >= 1.1.2, tpm2-tss >= 1.0, tpm2-tools >= 1.1.0, protobuf >= 2.4.1, re2 >= 20160401, libcurl >= 7.0.0, paccor, procps-ng >= 3.3.0")
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/package/rpm-post-install.sh)
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/local /usr/local/bin /usr/local/include /usr/local/lib)
set(CPACK_PACKAGE_FILE_NAME "${CPACK_RPM_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CPACK_RPM_PACKAGE_RELEASE_DIST}.${CMAKE_SYSTEM_PROCESSOR}")
endif()
# Set command to allow for running of CPack tool in build directory
include(CPack)
# Based on user-defined flag, optionally build tests for TPM 2.0 Library
if (BUILD_TESTS)
enable_testing()
add_subdirectory(test)
endif(BUILD_TESTS)

View File

@ -1,71 +0,0 @@
# HIRS TPM 2.0 Provisioner
Notice: The HIRS TPM 2.0 Provisioner is being deprecated.
Please refer to the [HIRS_Provisioner.Net](https://github.com/nsacyber/HIRS/tree/main/HIRS_Provisioner.NET) for currently supported HIRS provisioner.
### Overview
This document describes the HIRS TPM 2.0 Provisioner, a program that can leverage a machine and its TPM to:
- verify system attributes (as chosen in the ACA policy)
- request and store an Attestation Identity Credential
See the top-level project documentation for more details.
### Requirements
**Development and runtime of this project is currently only supported on CentOS 7.**
This project is built with the CMake cross-platform build suite. Consult the developer dependencies in [docs/](docs/) for a list of all third-party software that should be installed before attempting to the compile the project. Additional dependencies will be downloaded and built by CMake, so an active Internet connection is required to properly build the project.
Python 2 is required for style checking. If you do not have Python 2 installed, either install it or set the `STYLE_CHECK` option to `OFF` as part of your CMake command or in the root `CMakeLists.txt` file.
This project uses cppcheck to provide static code analysis. If you do not wish to run this analysis, set the `STATIC_ANALYSIS` option to `OFF` as part of your CMake Command or in the root `CMakeLists.txt` file.
### Building
Before you begin, please ensure you have the prerequisite dependencies installed on your system (listed in docs/developer-dependencies-centos.md).
Two procedures for building the HIRS TPM 2.0 Provisioner with CMake are described below.
#### Building with the CLion IDE
1. Import the root directory as a project into the [CLion](https://www.jetbrains.com/clion/) IDE.
2. Click `Tools` > `CMake` > `Reset Cache and Reload Project`
3. Click `Run` > `Build`
#### Building on the CLI
1. Navigate to the root of the project directory.
2. Make a build folder.
3. Navigate into the build folder.
4. Run the following command to generate the appropriate make files:
```
cmake ../
```
5. Run the following command to build the executable in the `bin` directory of the build folder:
```
make
```
By default, the build will gather additional third-party dependencies, run the unit test suite, run static analsysis with cppcheck, and will generate code documentation (which is placed in the `./docs` directory.)
#### Troubleshooting build issues
- CMake will fetch additional third-party dependencies during the build. The build will fail if these cannot be retrieved, so please ensure you have an active Internet connection before building.
- If it is found that CMake is building in an unusual/undesired directory, it's likely that CMake is using a cached target directory in lieu of an implicit target. At this point, look around the local project for a CMakeCache.txt file and delete it to force a cache refresh.
### RPM Packaging
The only currently supported target runtime environment is CentOS7.
The CMakeLists is configured to package the project into an RPM using CPack. To build the RPM, navigate to the target build directory and run the following command:
```
cpack
```
This will create the CentOS 7 RPM.
**NOTE:** Packaging of the software for a given distribution should be done in the same environment as the target environment. Due to host-system specific, compilation-time targeting of certain system libraries and APIs, cross-platform compilation is not advised and could lead to package installation errors.
### Installing
Ensure that the third-party runtime dependencies are present on the target machine(s). These can be found in [/docs/developer-depedencies-centos.md](/docs/developer-depedencies-centos.md). If installing these via yum or another package manager that performs dependency resolution, the EPEL repository will need to be configured, as several of the Provisioner's dependencies are not in the base CentOS 7 repository. The Provisioner RPM can be transferred and installed on client machines via the usual mechanisms (rpm/yum/etc.)

View File

@ -1,19 +0,0 @@
task cleanup(type: Delete) {
delete fileTree(dir: 'build', exclude: 'lib/')
}
task cmake(type: Exec) {
workingDir './build'
commandLine 'cmake', '../'
}
task make(type: Exec) {
workingDir './build'
commandLine 'make'
}
task fullBuild(type: GradleBuild) {
tasks = ['cleanup', 'cmake', 'make']
}
build.dependsOn tasks.fullBuild

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +0,0 @@
###################################
########Define log Levels##########
###################################
# All classes - except as otherwise specified.
# The logging threshold will be overridden if a value for root.level
# is set in /etc/hirs/logging.properties.
log4cplus.rootLogger=WARN, FILE
# Configure top level main logger
# The logging threshold will be overridden if a value for tpm2_provisioner.level
# is set in /etc/hirs/logging.properties.
log4cplus.logger.tpm2_provisioner=WARN, FILE
log4cplus.additivity.tpm2_provisioner=false
###################################
########Define the Appenders#######
###################################
# File Appender
log4cplus.appender.FILE.File=/var/log/hirs/provisioner/HIRS_ProvisionerTPM2.log
log4cplus.appender.FILE=log4cplus::RollingFileAppender
log4cplus.appender.FILE.MaxFileSize=10MB
log4cplus.appender.FILE.Append=true
log4cplus.appender.FILE.MaxBackupIndex=1
log4cplus.appender.FILE.layout=log4cplus::PatternLayout
log4cplus.appender.FILE.layout.ConversionPattern=[%d{%Y/%m/%d %H:%M:%S:%q}][%-5p][%-l] %m%n

View File

@ -1,19 +0,0 @@
Dependencies
============
These are the dependencies currently used by the TPM2 Provisioner that must be supplied by the runtime environment (in this case CentOS 7) and which are not statically linked.
Please look up their respective names in the CentOS repositories.
**NOTE**: Please consult [the developer dependency list](./developer-dependencies-centos.md) and make sure both the regular lib and the devel libs are installed.
| Dependency | Version used | Minimum required | Repository required | Project repository |
| -----------| ------------ | ----------------- | --------------------- | ------------------------------------------- |
| libcurl | 7.29.0 | 7.0.0 (estimated) | CentOS 7 base | https://github.com/curl/curl |
| log4cplus | 1.1.2 | 1.1.2 | CentOS 7 epel-release | https://github.com/log4cplus/log4cplus |
| protobuf | 2.5.0 | 2.4.1 (estimated) | CentOS 7 base | https://github.com/google/protobuf |
| re2 | 20160401 | 20160201 | CentOS 7 epel-release | https://github.com/google/re2 |
| tpm2-tss | 1.2.0 | 1.0.0 | CentOS 7 base | https://github.com/intel/tpm2-tss |
| tpm2-tools | 1.1.0 | 1.1.0 | CentOS 7 base | https://github.com/tpm2-software/tpm2-tools |
| paccor | 1.0.6 | none | N/A | https://github.com/nsacyber/paccor |
| procps-ng | 3.3.10 | 3.3.0 | CentOS 7 base | https://gitlab.com/procps-ng/procps |

View File

@ -1,27 +0,0 @@
Developer Dependencies
======================
These are the dependencies currently used by the TPM2 Provisioner project that must be supplied by the development environment (in this case CentOS 7) in order to properly build and package the project.
Please look up their respective names in the CentOS repositories.
If no available repository for the development environment contains the dependencies at an acceptable version level, it is expected that the packages be retrieved and built from their respective source repositories.
| Dependency | Version used | Minimum required | Repository | Project repository |
| ----------------- | ------------ | ------------------ | --------------------- | -------------------------------------- |
| cppcheck | 1.80 | 1.72 | CentOS 7 epel-release | http://cppcheck.sourceforge.net/ |
| doxygen | 1.8.13 | 1.8.0 (estimated) | CentOS 7 base | https://github.com/doxygen/doxygen |
| graphviz | 2.30.1 | 2.28.0 (estimated) | CentOS 7 base | https://gitlab.com/graphviz/graphviz |
| gcc-c++ | 4.8.5 | 4.8.5 | CentOS 7 base | https://gcc.gnu.org/ |
| libcurl-devel | 7.29.0 | 7.0.0 (estimated) | CentOS 7 base | https://github.com/curl/curl |
| libssh2-devel | 1.4.3 | 1.4.3 (estimated) | CentOS 7 base | https://github.com/libssh2/libssh2 |
| log4cplus-devel | 1.1.3 | 1.1.2 | CentOS 7 epel-release | https://github.com/log4cplus/log4cplus |
| openssl-devel | 1.0.2k | 1.0.2g (estimated) | CentOS 7 base | https://github.com/openssl/openssl |
| protobuf-compiler | 2.5.0 | 2.4.1 (estimated) | CentOS 7 base | https://github.com/google/protobuf |
| protobuf-devel | 2.5.0 | 2.4.1 (estimated) | CentOS 7 base | https://github.com/google/protobuf |
| re2-devel | 20160401 | 20160201 | CentOS 7 epel-release | https://github.com/google/re2 |
| tpm2-tss-devel | 1.2.0 | 1.0.0 | CentOS 7 base | https://github.com/intel/tpm2-tss |
| tpm2-abrmd-devel | 1.1.0 | 1.1.0 | CentOS 7 base | https://github.com/tpm2-software/tpm2-abrmd |
| cmake | 2.8.12.2 | 2.6.0 (estimated) | CentOS 7 base | https://cmake.org/ |
| cpack | 2.8.12.2 | 2.6.0 (estimated) | CentOS 7 base | https://cmake.org/ |
| git | 1.8.3.1 | 1.6.0 (estimated) | CentOS 7 base | https://github.com/git/git |

View File

@ -1,25 +0,0 @@
Developer Dependencies
======================
These are the dependencies currently used by the TPM2 Provisioner project that must be supplied by the development environment (in this case Debian-based) in order to properly build and package the project.
Please look up their respective names in the appropriate repositories.
If no available repository for the development environment contains the dependencies at an acceptable version level, it is expected that the packages be retrieved and built from their respective source repositories.
| Dependency | Version used | Minimum required | Repository | Project repository |
| -------------------- | ------------ | ------------------ | --------------------- | --------------------------------------------- |
| cppcheck | 1.82 | 1.72 | Ubuntu 18.04 base | http://cppcheck.sourceforge.net/ |
| doxygen | 1.8.13 | 1.8.0 (estimated) | Ubuntu 18.04 base | https://github.com/doxygen/doxygen |
| graphviz | 2.40.1 | 2.28.0 (estimated) | Ubuntu 18.04 base | https://gitlab.com/graphviz/graphviz |
| libcurl4-openssl-dev | 7.47.0 | 7.0.0 (estimated) | Ubuntu 18.04 base | https://github.com/curl/curl |
| liblog4cplus-dev | 1.1.2 | 1.1.2 | Ubuntu 18.04 base | https://github.com/log4cplus/log4cplus |
| libssl-dev | 1.1.0g | 1.0.2g (estimated) | Ubuntu 18.04 base | https://github.com/openssl/openssl |
| protobuf-compiler | 3.0.0 | 2.4.1 (estimated) | Ubuntu 18.04 base | https://github.com/google/protobuf |
| libprotobuf-dev | 3.0.0 | 2.4.1 (estimated) | Ubuntu 18.04 base | https://github.com/google/protobuf |
| libre2-dev | 20180201 | 20160201 | Ubuntu 18.04 base | https://github.com/google/re2 |
| libsapi-dev | 1.0.0 | 1.0.0 | Ubuntu 18.04 base | https://github.com/intel/tpm2-tss |
| tpm2-tss | 1.3.0 | 1.3.0 | Source Code | https://github.com/tpm2-software/tpm2-tss |
| tpm2-abrmd | 1.3.1 | 1.3.1 | Source Code | https://github.com/tpm2-software/tpm2-abrmd |
| cmake | 3.10.2 | 2.6.0 (estimated) | Ubuntu 18.04 base | https://cmake.org/ |
| git | 2.17.1 | 1.6.0 (estimated) | Ubuntu 18.04 base | https://github.com/git/git |

View File

@ -1,145 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#ifndef HIRS_PROVISIONERTPM2_INCLUDE_COMMANDTPM2_H_
#define HIRS_PROVISIONERTPM2_INCLUDE_COMMANDTPM2_H_
#include <Logger.h>
#include <ProvisionerTpm2.pb.h>
#include <Tpm2ToolsUtils.h>
#include <Tss.h>
#include <string>
#include <vector>
namespace hirs {
namespace tpm2 {
enum class AsymmetricKeyType {
RSA,
ECC
};
/**
* Manages the issuing of commands to tpm2-tools executables.
*/
class CommandTpm2 {
private:
static const hirs::log::Logger LOGGER;
static const int kMaxRetryCommandAttempts;
static const char* const kAKCertificateHandle;
static const char* const kTpm2ToolsTakeOwnershipCommand;
static const char* const kTpm2ToolsNvDefineCommand;
static const char* const kTpm2ToolsNvListCommand;
static const char* const kTpm2ToolsNvReadCommand;
static const char* const kTpm2ToolsNvReleaseCommand;
static const char* const kTpm2ToolsNvWriteCommand;
static const char* const kTpm2ToolsGetPubAkCommand;
static const char* const kTpm2ToolsGetPubEkCommand;
static const char* const kTpm2ToolsListPersistentCommand;
static const char* const kTpm2ToolsReadPublicCommand;
static const char* const kTpm2ToolsActivateCredential;
static const char* const kTpm2ToolsEvictControlCommand;
static const char* const kTpm2RetryCommandCode;
static const char* const kWellKnownSecret;
static const char* const kRsaAlgorithmId;
static const char* const kEccAlgorithmId;
static const char* const kDefaultAttributeValue;
static const char* const kDefaultOwnerAuthHandle;
static const char* const kDefaultRsaEkCredentialHandle;
static const char* const kDefaultEccEkCredentialHandle;
static const char* const kDefaultPlatformCredentialHandle;
static const char* const kDefaultEkHandle;
static const char* const kDefaultAkHandle;
static const char* const kDefaultAkCertFilename;
static const char* const kDefaultAkNameFilename;
static const char* const kDefaultAkPubFilename;
static const char* const kDefaultEkPubFilename;
static const char* const kTpm2ToolsGetQuoteCommand;
static const char* const kTpm2DefaultQuoteFilename;
static const char* const kTpm2DefaultSigFilename;
static const char* const kTpm2Sha256SigAlgorithm;
static const char* const kTpm2ToolsPcrListCommand;
const hirs::tpm2_tools_utils::Tpm2ToolsVersion version;
uint16_t getNvIndexDataSize(const std::string& nvIndex);
std::string readNvIndex(const std::string& beginNvIndex,
const uint16_t& dataSize);
bool hasNvIndexDefined(const std::string& nvIndex);
void releaseNvIndex(const std::string& nvIndex);
std::string createNvReadCommandArgs(const std::string& nvIndexValue,
const uint16_t& offset,
const uint16_t& readSize);
std::string createNvWriteCommandArgs(const std::string& nvIndexValue,
const std::string& writeFile);
bool hasPersistentObject(const std::string& handle);
void flushPersistentObject(const std::string& handle);
void createPublicAreaFile(const std::string& keyHandle,
const std::string& filename);
std::string getPublicArea(const std::string& filename);
std::string runTpm2CommandWithRetry(const std::string& command,
const std::string& args,
int sourceCodeLineNumber);
public:
static const char* const kDefaultIdentityClaimResponseFilename;
static const char* const kDefaultActivatedIdentityFilename;
explicit CommandTpm2(
const hirs::tpm2_tools_utils::Tpm2ToolsVersion& version
= hirs::tpm2_tools_utils::Tpm2ToolsVersionChecker
::findTpm2ToolsVersion());
void setAuthData();
std::string getEndorsementCredentialDefault(
const AsymmetricKeyType& keyType);
std::string getPlatformCredentialDefault();
std::string getStoredCredential(
const std::string& credentialHandle);
void createEndorsementKey(const AsymmetricKeyType& keyType =
AsymmetricKeyType::RSA);
std::string getEndorsementKeyPublicArea();
void createAttestationKey();
std::string getAttestationKeyPublicArea();
hirs::pb::IdentityClaim createIdentityClaim(
const hirs::pb::DeviceInfo& deviceInfo,
const std::string& akPublicArea,
const std::string& ekPublicArea,
const std::string& endorsementCredential = {},
const std::vector<std::string>& platformCredentials = {});
std::string activateIdentity();
void storeAKCertificate(const std::string& akCertificateByteString);
std::string getQuote(const std::string& pcr_selection,
const std::string& nonce);
std::string getPcrList();
};
} // namespace tpm2
} // namespace hirs
#endif // HIRS_PROVISIONERTPM2_INCLUDE_COMMANDTPM2_H_

View File

@ -1,43 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#ifndef HIRS_PROVISIONERTPM2_INCLUDE_DEVICEINFOCOLLECTOR_H_
#define HIRS_PROVISIONERTPM2_INCLUDE_DEVICEINFOCOLLECTOR_H_
#include <Logger.h>
#include <ProvisionerTpm2.pb.h>
#include <utility>
#include <string>
#include <vector>
/**
* Manages collection of device information for the client. Retrieves the OS,
* network, hardware, firmware, and TPM info.
*/
class DeviceInfoCollector {
private:
DeviceInfoCollector() {}
static const hirs::log::Logger LOGGER;
static std::vector<std::pair<std::string, std::string>> getNetworks();
static hirs::pb::FirmwareInfo collectFirmwareInfo();
static hirs::pb::HardwareInfo collectHardwareInfo();
static std::string collectHostname();
static hirs::pb::NetworkInfo collectNetworkInfo();
static hirs::pb::OsInfo collectOsInfo();
public:
/**
* Collect all device info from the system and return it in a filled out
* DeviceInfo object.
*/
static hirs::pb::DeviceInfo collectDeviceInfo();
};
#endif // HIRS_PROVISIONERTPM2_INCLUDE_DEVICEINFOCOLLECTOR_H_

View File

@ -1,31 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#ifndef HIRS_PROVISIONERTPM2_INCLUDE_HIRSRUNTIMEEXCEPTION_H_
#define HIRS_PROVISIONERTPM2_INCLUDE_HIRSRUNTIMEEXCEPTION_H_
#include <stdexcept>
#include <string>
namespace hirs {
namespace exception {
/**
* Represents a runtime exception thrown by HIRS code.
*/
class HirsRuntimeException : public std::runtime_error {
private:
static std::string buildMessage(const std::string& msg,
const std::string& origin = "");
public:
HirsRuntimeException(const std::string& msg,
const std::string& origin);
virtual ~HirsRuntimeException();
};
} // namespace exception
} // namespace hirs
#endif // HIRS_PROVISIONERTPM2_INCLUDE_HIRSRUNTIMEEXCEPTION_H_

View File

@ -1,67 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#ifndef HIRS_PROVISIONERTPM2_INCLUDE_LOGGER_H_
#define HIRS_PROVISIONERTPM2_INCLUDE_LOGGER_H_
#include <log4cplus/logger.h>
#include <string>
#include <exception>
namespace hirs {
namespace log {
/**
* The Logger class provides a wrapper for log4cplus that allows for
* getting standardized Loggers in the TPM 2 Provisioner Library.
*/
class Logger {
private:
static const char* const kDefaultProvisionerLoggerName;
static const char* const kPropFileLocation;
const log4cplus::Logger kLogger;
explicit Logger(std::string loggerName);
enum class LogLevel {
DEBUG,
ERROR,
FATAL,
INFO,
TRACE,
WARN
};
void log(const LogLevel& logLevel, const std::string& msg,
const std::exception* ex) const;
static void setThresholdFromLoggingProperties(log4cplus::Logger logger);
public:
static Logger getDefaultLogger();
static Logger getLogger(const std::string& loggerName);
void debug(const std::string& msg) const;
void debug(const std::string& msg, const std::exception* ex) const;
void error(const std::string& msg) const;
void error(const std::string& msg, const std::exception* ex) const;
void fatal(const std::string& msg) const;
void fatal(const std::string& msg, const std::exception* ex) const;
void info(const std::string& msg) const;
void info(const std::string& msg, const std::exception* ex) const;
void trace(const std::string& msg) const;
void trace(const std::string& msg, const std::exception* ex) const;
void warn(const std::string& msg) const;
void warn(const std::string& msg, const std::exception* ex) const;
};
} // namespace log
} // namespace hirs
#endif // HIRS_PROVISIONERTPM2_INCLUDE_LOGGER_H_

View File

@ -1,58 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#ifndef HIRS_PROVISIONERTPM2_INCLUDE_PROCESS_H_
#define HIRS_PROVISIONERTPM2_INCLUDE_PROCESS_H_
#include "Logger.h"
#include <string>
#include <iostream>
namespace hirs {
namespace utils {
/**
* The Process class represents a Linux process, its return value,
* and the standard output stream.
*/
class Process {
private:
static const hirs::log::Logger LOGGER;
static const char* const kPgrepCommand;
static const int kMaxStatFileProcessNameLength;
std::string executable;
std::string arguments;
std::string output;
public:
explicit Process(const std::string& executable,
const std::string& arguments = "");
int run();
int run(std::ostream& osForErrorLogging);
std::string getOutputString() const;
static std::string run(const std::string& executable,
const std::string& arguments,
const std::string& sourceFileName,
int sourceLineNumber);
static std::string runData(const std::string& executable,
const std::string& arguments,
const std::string& sourceFileName,
int sourceLineNumber);
static bool isRunning(const std::string& executable);
};
} // namespace utils
} // namespace hirs
#endif // HIRS_PROVISIONERTPM2_INCLUDE_PROCESS_H_

View File

@ -1,42 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#ifndef HIRS_PROVISIONERTPM2_INCLUDE_PROPERTIES_H_
#define HIRS_PROVISIONERTPM2_INCLUDE_PROPERTIES_H_
#include <map>
#include <string>
#include "Logger.h"
namespace hirs {
namespace properties {
/**
* Manages the loading and retrieval of key-value configuration.
*/
class Properties {
private:
static const hirs::log::Logger LOGGER;
std::map<std::string, std::string> properties;
public:
Properties();
explicit Properties(const std::string& filepath);
void load(const std::string& filepath);
std::string get(const std::string& key);
std::string get(const std::string& key,
const std::string& defaultValue);
bool isSet(const std::string& key);
void set(const std::string& key, const std::string& value);
};
} // namespace properties
} // namespace hirs
#endif // HIRS_PROVISIONERTPM2_INCLUDE_PROPERTIES_H_

View File

@ -1,69 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#ifndef HIRS_PROVISIONERTPM2_INCLUDE_RESTFULCLIENTPROVISIONER_H_
#define HIRS_PROVISIONERTPM2_INCLUDE_RESTFULCLIENTPROVISIONER_H_
#include <Logger.h>
#include <ProvisionerTpm2.pb.h>
#include <string>
/**
* Manages the sending of messages to the ACA and their relevant replies.
*/
class RestfulClientProvisioner {
private:
// Logger
static const hirs::log::Logger LOGGER;
static const char * const PROP_FILE_LOC;
static const char * const PROP_ACA_FQDN;
static const char * const PROP_ACA_PORT;
static const char * const ACA_ERROR_FIELDNAME;
/**
* IP address of ACA
*/
std::string acaAddress;
/**
* Port used by the ACA to service requests.
*/
int port;
public:
RestfulClientProvisioner();
RestfulClientProvisioner(const std::string& acaAddress, int acaPort);
/**
* Return the IP address of the ACA
* @return the IP address of the ACA
*/
std::string getAcaAddress();
/**
* Sends the identity claim to the ACA to initiate the identity claim
* procedure. Return the wrapped challenge nonce reply from the ACA for
* decoding with TPM2_ActivateCredential.
*
* @param identityClaim request containing deviceInfo, EK public area
* AK public area and optionally an Endorsement and/or Platform Credential
* @returns the byte-encoded encrypted nonce blob
*/
std::string sendIdentityClaim(hirs::pb::IdentityClaim identityClaim);
/**
* Sends the request to get the public Attestation Certificate from the
* ACA. Contains the decrypted nonce returned from
* RestfulClientProvisioner::sendIdentityClaim.
*
* @param certificateRequest request containing nonce
* @return the byte-encoded public attestation certificate
*/
std::string sendAttestationCertificateRequest(
hirs::pb::CertificateRequest certificateRequest);
};
#endif // HIRS_PROVISIONERTPM2_INCLUDE_RESTFULCLIENTPROVISIONER_H_

View File

@ -1,112 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#ifndef HIRS_PROVISIONERTPM2_INCLUDE_TPM2TOOLSUTILS_H_
#define HIRS_PROVISIONERTPM2_INCLUDE_TPM2TOOLSUTILS_H_
#include <string>
#include <unordered_map>
namespace hirs {
namespace tpm2_tools_utils {
/**
* Enum class that provides list of supported tpm2_tools versions
*/
enum class Tpm2ToolsVersion {
VERSION_1_1_0,
VERSION_2_1_0,
VERSION_3
};
/**
* Utility class that determines the version of tpm2_tools present on the
* system.
*/
class Tpm2ToolsVersionChecker {
private:
static const std::unordered_map<std::string, Tpm2ToolsVersion> kVersionMap;
static const std::unordered_map<std::string,
Tpm2ToolsVersion> kMaxSupportedVersionMap;
static std::string getDistribution();
public:
/**
* Makes a simple tpm2-tools call to determine the version available on the
* current system.
*
* @return enum specifying the version of tpm2-tools available locally
*/
static Tpm2ToolsVersion findTpm2ToolsVersion();
};
/**
* Utility class that provides functions to parse information from tpm2_tools
* output.
*/
class Tpm2ToolsOutputParser {
public:
/**
* Parses the provided Non-volatile (NV) Index List for the data size of
* the index at the prescribed NV handle. Expects output from tpm2_nvlist.
*
* @param nvHandle memory address to search the nvListOutput for
* @param nvListOutput the NV Index List to search for the data size
* @return the size of the desired NV index or 0 if the index isn't found
*/
static uint16_t parseNvDataSize(const std::string& nvHandle,
const std::string& nvListOutput);
/**
* Pulls the data out of the output from a tpm2_nvread command.
*
* @param nvReadOutput the output from a call to tpm2_nvread
* @return the data serialized as bytes, or an empty string if
* nvReadOutput improperly formatted
*/
static std::string parseNvReadOutput(const std::string& nvReadOutput);
/**
* Parses the provided Persistent Object List for the provided handle.
*
* @param handle memory address to search the nvListPersistentOutput for
* @param listPersistentOutput the Persistent Object list to search
* @return true, if handle is found / false, otherwise
*/
static bool parsePersistentObjectExists(const std::string& handle,
const std::string& listPersistentOutput);
/**
* Parses the provided tpm2-tool output for a TPM Error Code.
*
* @param toolOutput the output from a call to any of the tpm2-tools
* @return a TPM error code if found, or an empty string, otherwise
*/
static std::string parseTpmErrorCode(const std::string& toolOutput);
/**
* Parses the provided tpm2-tool output for a tpm2_tools version.
*
* @param toolOutput the output from a call to any of the tpm2-tools
* @return a tpm2_tools version if found, or an empty string, otherwise
*/
static std::string parseTpm2ToolsVersion(const std::string& toolOutput);
/**
* Parses the provided tpm2-tool version for the major version.
*
* @param toolVersion the output from a call to parseTpm2ToolsVersion
* @return tpm2_tools major version if found, or an empty string, otherwise
*/
static std::string parseTpm2ToolsMajorVersion(
const std::string& toolVersion);
};
} // namespace tpm2_tools_utils
} // namespace hirs
#endif // HIRS_PROVISIONERTPM2_INCLUDE_TPM2TOOLSUTILS_H_

View File

@ -1,6 +0,0 @@
#ifndef HIRS_PROVISIONERTPM2_INCLUDE_TSS_H_
#define HIRS_PROVISIONERTPM2_INCLUDE_TSS_H_
#include @TSS_LIBRARY@
#endif // HIRS_PROVISIONERTPM2_INCLUDE_TSS_H_

View File

@ -1,160 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#ifndef HIRS_PROVISIONERTPM2_INCLUDE_UTILS_H_
#define HIRS_PROVISIONERTPM2_INCLUDE_UTILS_H_
#include <string>
#include <vector>
namespace hirs {
/**
* Namespace for describing file utility functions.
*/
namespace file_utils {
/**
* Returns whether or not the argument directory exists.
* @return True if the directory exists. False, otherwise.
*/
bool dirExists(const std::string& filename);
bool fileExists(const std::string& filename);
/**
* Reads the contents of an entire file into a string.
* @param filename the name and path of the file to read.
* @return the contents of the specified file as a string.
*/
std::string fileToString(const std::string& filename);
std::string fileToString(const std::string& filename,
const std::string& defaultVal);
std::string getFileAsOneLineOrEmptyString(const std::string& filename);
std::vector<std::string> search_directory(const std::string& directory);
void writeBinaryFile(const std::string& bytes,
const std::string& filename);
int getFileSize(const std::string& filename);
void splitFile(const std::string& inFilename,
const std::string& outFilename,
int startPos,
int readSize);
std::string trimFilenameFromPath(const std::string& path);
} // namespace file_utils
namespace json_utils {
/**
* Utility class that provides functions to parse information from ACA
* output.
*/
class JSONFieldParser {
public:
/**
* Parses the target field of the provided JSON object as a string.
*
* @param jsonObject the JSON-formatted object
* @param jsonFieldName the name of the field to parse from the JSON object
* @return the value of the target field in the JSON object
*/
static std::string parseJsonStringField(const std::string& jsonObject,
const std::string& jsonFieldName);
};
} // namespace json_utils
namespace string_utils {
/**
* Converts a binary string to a hex string.
*
* @param bin the binary character array to convert
* @return hex string representation of bin
*/
std::string binaryToHex(const std::string& bin);
/**
* Checks if a string contains another string.
*
* @param str containing string
* @param substring string to search for
* @return true, if the string is found / false, otherwise
*/
bool contains(const std::string& str, const std::string& substring);
/**
* Converts an unsigned long (uint32) value to a hex string.
*
* @param value the unsigned long to convert
* @return hex string representation of the value
*/
std::string longToHex(const uint32_t& value);
/**
* Checks provided string contains only hexadecimal characters.
*
* @param str the string to check if hexadecimal only
* @return true, if hex chars only / false, otherwise
*/
bool isHexString(const std::string& str);
/**
* Converts a hex string to a string of bytes.
* Requires the hex string to have an even number of chars.
*
* @param hexString string of hex chars to be converted to bytes
* @return a byte string or, if error detected, empty string
*/
std::string hexToBytes(const std::string& hexString);
/**
* Takes a given hex string and converts it to a long value.
*
* @param hexString the hex string to be converted
* @return the value of the hex string when converted to a long value
* or 0 if an error occurred
*/
uint32_t hexToLong(const std::string& hexString);
/**
* Removes any new line characters in the input string and returns the
* pruned, input string.
* @param str string to remove new line characters from.
* @return str with new line characters removed.
*/
std::string trimNewLines(std::string str);
/**
* Removes any double-quote characters in the input string and returns the
* pruned, input string.
* @param str string to remove double-quotes characters from.
* @return str with double-quote characters removed.
*/
std::string trimQuotes(std::string str);
/**
* Removes any occurrences of the target character in the input string and
* returns the pruned, input string.
* @param str string to characters from.
* @param targetChar char to prune from the string
* @return str with the characters removed.
*/
std::string trimChar(std::string str, char targetChar);
std::string trimWhitespaceFromLeft(std::string str);
std::string trimWhitespaceFromRight(std::string str);
std::string trimWhitespaceFromBothEnds(std::string str);
std::vector<std::string> split(const std::string& str, char delim);
} // namespace string_utils
} // namespace hirs
#endif // HIRS_PROVISIONERTPM2_INCLUDE_UTILS_H_

View File

@ -1,13 +0,0 @@
#ifndef HIRS_PROVISIONERTPM2_INCLUDE_VERSION_H_
#define HIRS_PROVISIONERTPM2_INCLUDE_VERSION_H_
#define MAJOR_VERSION "@MAJOR_VERSION@"
#define MINOR_VERSION "@MINOR_VERSION@"
#define PATCH_VERSION "@PATCH_VERSION@"
#define PACKAGE_RELEASE_NUMBER "@PACKAGE_RELEASE_NUMBER@"
#define CLIENT_VERSION MAJOR_VERSION "."\
MINOR_VERSION "."\
PATCH_VERSION "."\
PACKAGE_RELEASE_NUMBER
#endif // HIRS_PROVISIONERTPM2_INCLUDE_VERSION_H_

View File

@ -1,15 +0,0 @@
cmake_minimum_required(VERSION 2.8.12)
project(cpr-download NONE)
include(ExternalProject)
ExternalProject_Add(cpr
URL https://github.com/whoshuu/cpr/archive/1.3.0.zip
URL_HASH SHA1=d669d94b41ffaa2de478923c35a83074e34fdc12
SOURCE_DIR "${CMAKE_BINARY_DIR}/lib/cpr-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/lib/cpr-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

View File

@ -1,14 +0,0 @@
cmake_minimum_required(VERSION 2.8.12)
project(cpplint-download NONE)
include(ExternalProject)
ExternalProject_Add(cpplint
GIT_REPOSITORY https://github.com/google/styleguide
GIT_TAG gh-pages
SOURCE_DIR "${CMAKE_BINARY_DIR}/lib/cpplint-download"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

View File

@ -1,15 +0,0 @@
cmake_minimum_required(VERSION 2.8.12)
project(googletest-download NONE)
include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.8.0
SOURCE_DIR "${CMAKE_BINARY_DIR}/lib/googletest-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/lib/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

View File

@ -1,47 +0,0 @@
#!/bin/bash
set -e
# Check packager OS is Centos
OS_NAME=$(awk -F= '/^NAME/{print $2}' /etc/os-release)
if [ "$OS_NAME" != "\"CentOS Linux\"" ] && [ "$OS_NAME" != "\"Amazon Linux\"" ] ; then
echo "Error: RPMs must be built with CentOS or Amazon Linux"
exit 1
fi
# Find package directory
cd $( dirname "${BASH_SOURCE[0]}" )
# Ensure clean build environment
shopt -s extglob
# Delete everything but downloaded dependencies
rm -rf BUILD/!(lib)
shopt -u extglob
# Make BUILD directory if it doesn't already exist
if [ ! -d "BUILD" ]; then
mkdir BUILD
fi
# Navigate to build directory
cd BUILD
# Build HIRS_ProvisionerTPM2 RPM
cmake ../..
# If the current directory is empty, there may be an existing CmakeCache.txt
# file that prevents cmake from building in the current directory
if ! [ "$(ls -A)" ]; then
echo "CMake failed to generate files in the target directory. Is there "
echo "an existing CMakeCache.txt file in the CMake source directory? "
echo "If so, delete it."
exit 1
fi
make
cpack
# Move HIRS_ProvisionerTPM2 RPM
rm -f ../../../package/rpm/RPMS/x86_64/HIRS_Provisioner_TPM_2_0*.rpm
if [ ! -d "../../../package/rpm/RPMS/x86_64" ]; then
mkdir -p ../../../package/rpm/RPMS/x86_64
fi
mv *.rpm ../../../package/rpm/RPMS/x86_64

View File

@ -1,47 +0,0 @@
#!/bin/bash
set -e
# Check packager OS is Ubuntu
OS_NAME=$(awk -F= '/^NAME/{print $2}' /etc/os-release)
if [ "$OS_NAME" != "\"Ubuntu\"" ]; then
echo "Error: DEBs must be built with Ubuntu"
exit 1
fi
# Find package directory
cd $( dirname "${BASH_SOURCE[0]}" )
# Ensure clean build environment
shopt -s extglob
# Delete everything but downloaded dependencies
rm -rf BUILD/!(lib)
shopt -u extglob
# Make BUILD directory if it doesn't already exist
if [ ! -d "BUILD" ]; then
mkdir BUILD
fi
# Navigate to build directory
cd BUILD
# Build HIRS_ProvisionerTPM2 DEB
cmake ../..
# If the current directory is empty, there may be an existing CmakeCache.txt
# file that prevents cmake from building in the current directory
if ! [ "$(ls -A)" ]; then
echo "CMake failed to generate files in the target directory. Is there "
echo "an existing CMakeCache.txt file in the CMake source directory? "
echo "If so, delete it."
exit 1
fi
make
cpack
# Move HIRS_ProvisionerTPM2 DEB
rm -f ../../../package/deb/DEBS/HIRSProvisionerTPM2.0*.deb
if [ ! -d "../../../package/deb/DEBS" ]; then
mkdir -p ../../../package/deb/DEBS
fi
mv *.deb ../../../package/deb/DEBS

View File

@ -1,15 +0,0 @@
#!/bin/bash
# NOTE: This script uses many of the same commands as the build_version_helper.sh found under the top level
# 'package' directory
# script that pulls version information from git for populating the TPM2 Provisioner release information
# with regards to packaging
# script is to be invoked in CMake for loading into build process
GIT_HASH=`git rev-parse HEAD | head -c6`
GIT_COMMIT_UNIX_TIMESTAMP=`git show -s --format=%ct | xargs echo -n`
RELEASE="$((GIT_COMMIT_UNIX_TIMESTAMP)).$GIT_HASH"
echo "$RELEASE"

View File

@ -1,38 +0,0 @@
HIRS_SITE_CONFIG="/etc/hirs/hirs-site.config"
mkdir -p /var/log/hirs/provisioner
mkdir -p /etc/hirs/provisioner/certs
ln -s -f /usr/local/bin/hirs-provisioner-tpm2 /usr/sbin/hirs-provisioner-tpm2
ln -s -f /usr/local/bin/tpm_aca_provision /usr/sbin/tpm_aca_provision
ln -s -f /usr/local/bin/tpm_version /usr/sbin/tpm_version
if [ ! -f $HIRS_SITE_CONFIG ]; then
# Create template site config if it does not exist
cat <<DEFAULT_SITE_CONFIG_FILE > $HIRS_SITE_CONFIG
#*******************************************
#* HIRS site configuration properties file
#*******************************************
# Client configuration
CLIENT_HOSTNAME=$(hostname -f)
TPM_ENABLED=
IMA_ENABLED=
# Site-specific configuration
ATTESTATION_CA_FQDN=
ATTESTATION_CA_PORT=8443
BROKER_FQDN=
BROKER_PORT=61616
PORTAL_FQDN=
PORTAL_PORT=8443
DEFAULT_SITE_CONFIG_FILE
echo "$HIRS_SITE_CONFIG not found - a template has been created"
echo "Set your site configuration manually in $HIRS_SITE_CONFIG, then run 'hirs-provisioner-tpm2 provision' to provision this system"
fi
ln -s -f /etc/hirs/provisioner/hirs-provisioner.sh /usr/sbin/hirs-provisioner
if ! [ -f "set_tcg_properties.sh" ]; then
sh ./set_tcg_properties.sh
fi

View File

@ -1,59 +0,0 @@
set -e
if ! [ $(id -u) = 0 ]; then
echo "Please run this script as root."
exit 1
fi
HIRS_SITE_CONFIG="/etc/hirs/hirs-site.config"
mkdir -p /var/log/hirs/provisioner
mkdir -p /etc/hirs/provisioner/certs
ln -s -f /usr/local/bin/hirs-provisioner-tpm2 /usr/sbin/hirs-provisioner-tpm2
ln -s -f /usr/local/bin/tpm_aca_provision /usr/sbin/tpm_aca_provision
ln -s -f /usr/local/bin/tpm_version /usr/sbin/tpm_version
if [ ! -f $HIRS_SITE_CONFIG ]; then
# Create template site config if it does not exist
cat <<DEFAULT_SITE_CONFIG_FILE > $HIRS_SITE_CONFIG
#*******************************************
#* HIRS site configuration properties file
#*******************************************
# Client configuration
CLIENT_HOSTNAME=$(hostname -f)
TPM_ENABLED=
IMA_ENABLED=
# Site-specific configuration
ATTESTATION_CA_FQDN=
ATTESTATION_CA_PORT=8443
BROKER_FQDN=
BROKER_PORT=61616
PORTAL_FQDN=
PORTAL_PORT=8443
DEFAULT_SITE_CONFIG_FILE
echo "$HIRS_SITE_CONFIG not found - a template has been created"
echo "Set your site configuration manually in $HIRS_SITE_CONFIG, then run 'hirs-provisioner-tpm2 provision' to provision this system"
fi
ln -s -f /etc/hirs/provisioner/hirs-provisioner.sh /usr/sbin/hirs-provisioner
TCG_TEMP_FILE="/tmp/boot_properties"
TCG_BOOT_FILE="/etc/hirs/tcg_boot.properties"
TCG_DIRECTORY="/boot/tcg"
RIM_FILE_LOCATION="$TCG_DIRECTORY/manifest/rim/"
SWIDTAG_FILE_LOCATION="$TCG_DIRECTORY/manifest/swidtag/"
CREDENTIALS_LOCATION="$TCG_DIRECTORY/cert/platform/"
BINARY_BIOS_MEASUREMENTS="/sys/kernel/security/tpm0/binary_bios_measurements"
if [ ! -f "$TCG_BOOT_FILE" ]; then
touch "$TCG_TEMP_FILE"
echo "tcg.rim.dir=$RIM_FILE_LOCATION" > "$TCG_TEMP_FILE"
echo "tcg.swidtag.dir=$SWIDTAG_FILE_LOCATION" >> "$TCG_TEMP_FILE"
echo "tcg.cert.dir=$CREDENTIALS_LOCATION" >> "$TCG_TEMP_FILE"
echo "tcg.event.file=$BINARY_BIOS_MEASUREMENTS" >> "$TCG_TEMP_FILE"
install -m 644 $TCG_TEMP_FILE $TCG_BOOT_FILE
fi

View File

@ -1,16 +0,0 @@
#!/bin/bash
TPM_VER_1_2=$(dmesg | grep -i "1\.2 TPM")
TPM_VERSION_2=$(dmesg | grep -i "2\.0 TPM")
TSS_LIB_FOUND=$(gcc -lsapi 2>&1 | grep 'main')
if [ -z "$TPM_VERSION_2" ]; then
printf "The system does not show a TPM v2.0 installed on the system.\n";
if [ ! -z "$TPM_VER_1_2" ]; then
printf "The system shows a TPM v1.2 installed.\n";
fi
fi
if [ -z "$TSS_LIB_FOUND" ]; then
printf "Add the directory containing the TSS libraries to the load path.\n";
fi

View File

@ -1,45 +0,0 @@
# Generate Protobuf C++ source files from proto spec using protobuf compiler
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ProvisionerTpm2.proto)
# CMake file to control creation of TPM 2.0 Provisioner Library
add_library(TPM2_PROVISIONER_LIBRARY
# Source Files
CommandTpm2.cpp
DeviceInfoCollector.cpp
HirsRuntimeException.cpp
Logger.cpp
Process.cpp
Properties.cpp
RestfulClientProvisioner.cpp
Tpm2ToolsUtils.cpp
Utils.cpp
# Header Files
${CMAKE_SOURCE_DIR}/include/CommandTpm2.h
${CMAKE_SOURCE_DIR}/include/DeviceInfoCollector.h
${CMAKE_SOURCE_DIR}/include/HirsRuntimeException.h
${CMAKE_SOURCE_DIR}/include/Logger.h
${CMAKE_SOURCE_DIR}/include/Process.h
${CMAKE_SOURCE_DIR}/include/Properties.h
${CMAKE_SOURCE_DIR}/include/RestfulClientProvisioner.h
${CMAKE_SOURCE_DIR}/include/Tpm2ToolsUtils.h
${CMAKE_SOURCE_DIR}/include/Tss.h
${CMAKE_SOURCE_DIR}/include/Utils.h
${CMAKE_SOURCE_DIR}/include/Version.h
# Autogenerated Files
${PROTO_SRCS}
${PROTO_HDRS})
# Link necessary libraries from project's root CMake file
target_link_libraries(TPM2_PROVISIONER_LIBRARY
${REQUIRED_LIBS})
# Specify 3rd party include directories for IDEs
include_directories(${CPR_INCLUDE_DIRS})
# Includes protobuf libraries
include_directories(${PROTOBUF_INCLUDE_DIR})
# Need to add directory of generated protobuf files to include path
get_filename_component(PROTO_DIR ${PROTO_HDRS} DIRECTORY)
include_directories(${PROTO_DIR})

View File

@ -1,836 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include <CommandTpm2.h>
#include <DeviceInfoCollector.h>
#include <HirsRuntimeException.h>
#include <Process.h>
#include <Utils.h>
#include <chrono>
#include <fstream>
#include <string>
#include <sstream>
#include <thread>
#include <utility>
#include <vector>
#include <iostream>
#include <iomanip>
using hirs::exception::HirsRuntimeException;
using hirs::file_utils::fileToString;
using hirs::file_utils::writeBinaryFile;
using hirs::file_utils::fileExists;
using hirs::log::Logger;
using hirs::pb::IdentityClaim;
using hirs::tpm2::CommandTpm2;
using hirs::string_utils::binaryToHex;
using hirs::string_utils::contains;
using hirs::string_utils::longToHex;
using hirs::string_utils::hexToLong;
using hirs::tpm2_tools_utils::Tpm2ToolsVersion;
using hirs::tpm2_tools_utils::Tpm2ToolsOutputParser;
using hirs::utils::Process;
using std::chrono::milliseconds;
using std::cout;
using std::endl;
using std::string;
using std::stringstream;
using std::ifstream;
using std::this_thread::sleep_for;
using std::to_string;
using std::vector;
const Logger CommandTpm2::LOGGER = Logger::getDefaultLogger();
const int CommandTpm2::kMaxRetryCommandAttempts = 5;
const char* const CommandTpm2::kTpm2ToolsTakeOwnershipCommand
= "tpm2_takeownership";
const char* const CommandTpm2::kTpm2ToolsNvDefineCommand = "tpm2_nvdefine";
const char* const CommandTpm2::kTpm2ToolsNvListCommand = "tpm2_nvlist";
const char* const CommandTpm2::kTpm2ToolsNvReadCommand = "tpm2_nvread";
const char* const CommandTpm2::kTpm2ToolsNvReleaseCommand = "tpm2_nvrelease";
const char* const CommandTpm2::kTpm2ToolsNvWriteCommand = "tpm2_nvwrite";
const char* const CommandTpm2::kTpm2ToolsGetPubAkCommand = "tpm2_getpubak";
const char* const CommandTpm2::kTpm2ToolsGetPubEkCommand = "tpm2_getpubek";
const char* const CommandTpm2::kTpm2ToolsListPersistentCommand
= "tpm2_listpersistent";
const char* const CommandTpm2::kTpm2ToolsReadPublicCommand = "tpm2_readpublic";
const char* const CommandTpm2::kTpm2ToolsActivateCredential
= "tpm2_activatecredential";
const char* const CommandTpm2::kTpm2ToolsEvictControlCommand
= "tpm2_evictcontrol";
const char* const CommandTpm2::kTpm2ToolsGetQuoteCommand = "tpm2_quote";
const char* const CommandTpm2::kTpm2ToolsPcrListCommand = "tpm2_pcrlist";
/**
* The value for the TPM_RC_RETRY was obtained from Table 16 (pgs. 37-41) of
* the "Trusted Platform Module Library Part 2: Structures" Revision 1.38
* document.
*/
const char* const CommandTpm2::kTpm2RetryCommandCode = "0x922";
const char* const CommandTpm2::kWellKnownSecret = "00";
/**
* The value for kDefaultAttributeValue can be understood by checking Part 2 of
* the TPM 2.0 Specification, Table 204: Definition of (UINT32) TPMA_NV Bits.
* The value of 0x2000A means the following 3 bits are set:
* Bit 1: The index data can be written if Owner Authorization is provided.
* Bit 3: Authorizations to change the Index contents that require USER role
* may not be provided with a policy session.
* Bit 17: The index data can be read if Owner Authorization is provided.
*/
const char* const CommandTpm2::kDefaultAttributeValue = "0x2000A";
/**
* The following algorithm IDs were obtained from Table 9 (pgs. 26-28) of
* the "Trusted Platform Module Library Part 2: Structures" Revision 1.38
* document.
*/
const char* const CommandTpm2::kRsaAlgorithmId = "0x01";
const char* const CommandTpm2::kEccAlgorithmId = "0x23";
/**
* The following default memory address was obtained from Table 28 (pg. 57) of
* the "Trusted Platform Module Library Part 2: Structures" Revision 1.38
* document.
*/
const char* const CommandTpm2::kDefaultOwnerAuthHandle = "0x40000001";
/**
* The following default memory addresses were obtained from Table 2 (pg. 29) of
* the "TCG TPM v2.0 Provisioning Guidance", Revision 1.0 document.
*/
const char* const CommandTpm2::kDefaultRsaEkCredentialHandle = "0x1c00002";
const char* const CommandTpm2::kDefaultEccEkCredentialHandle = "0x1c0000a";
const char* const CommandTpm2::kDefaultPlatformCredentialHandle = "0x1c90000";
const char* const CommandTpm2::kDefaultEkHandle = "0x81010001";
const char* const CommandTpm2::kDefaultAkHandle = "0x81010002";
const char* const CommandTpm2::kAKCertificateHandle = "0x1c0000c";
const char* const CommandTpm2::kDefaultAkCertFilename =
"/etc/hirs/ak.cer";
const char* const CommandTpm2::kDefaultAkNameFilename = "ak.name";
const char* const CommandTpm2::kDefaultAkPubFilename = "ak.pub";
const char* const CommandTpm2::kDefaultEkPubFilename = "ek.pub";
const char* const CommandTpm2::kDefaultIdentityClaimResponseFilename
= "identityClaimResponse";
const char* const CommandTpm2::kDefaultActivatedIdentityFilename
= "activatedIdentity.secret";
const char* const CommandTpm2::kTpm2DefaultQuoteFilename = "/tmp/quote.bin";
const char* const CommandTpm2::kTpm2DefaultSigFilename = "/tmp/sig.bin";
const char* const CommandTpm2::kTpm2Sha256SigAlgorithm = "sha256";
/**
* Constructor to create an interface to TPM 2.0 devices.
* @param version version of tpm2-tools to be used when making commands
*/
CommandTpm2::CommandTpm2(const Tpm2ToolsVersion& version)
: version(version) {
}
/**
* Method to set the auth data (passwords) of the TPM 2.0 device.
*
* @throws HirsRuntimeException on failure
*/
void CommandTpm2::setAuthData() {
stringstream argsStream;
switch (version) {
case Tpm2ToolsVersion::VERSION_1_1_0:
case Tpm2ToolsVersion::VERSION_2_1_0:
argsStream << " -X -o " << kWellKnownSecret
<< " -e " << kWellKnownSecret
<< " -l " << kWellKnownSecret
<< endl;
break;
case Tpm2ToolsVersion::VERSION_3:
argsStream << " -o hex:" << kWellKnownSecret
<< " -e hex:" << kWellKnownSecret
<< " -l hex:" << kWellKnownSecret
<< endl;
break;
}
LOGGER.info("Attempting to set auth data.");
runTpm2CommandWithRetry(kTpm2ToolsTakeOwnershipCommand, argsStream.str(),
__LINE__);
LOGGER.info("Auth data set successfully.");
}
/**
* Method to get the Endorsement Credential from a default address
* as a byte-based, DER-encoded X509 credential.
* @param keyType must be RSA or ECC
* @return string of hex-encoded bytes representing DER-encoded X509 credential
*/
string CommandTpm2::getEndorsementCredentialDefault(
const AsymmetricKeyType& keyType) {
LOGGER.info("Attempting to retrieve endorsement credential");
string endorsementCredential;
switch (keyType) {
case AsymmetricKeyType::RSA:
endorsementCredential = getStoredCredential(
kDefaultRsaEkCredentialHandle);
break;
case AsymmetricKeyType::ECC:
endorsementCredential = getStoredCredential(
kDefaultEccEkCredentialHandle);
break;
}
if (endorsementCredential == "") {
LOGGER.info("Unable to retrieve endorsement credential");
cout << "------> Unable to retrieve endorsement credential" << endl;
} else {
LOGGER.info("Successfully retrieved endorsement credential");
}
return endorsementCredential;
}
/**
* Method to get the Platform Credential from a default address
* as a byte-based, DER-encoded X509 credential. If platform credential
* does not exist, an empty string will be returned.
*
* @return string of hex-encoded bytes representing DER-encoded X509
* credential or an empty string if no credential exists
*/
string CommandTpm2::getPlatformCredentialDefault() {
LOGGER.info("Attempting to retrieve platform credential");
string platformCredential = getStoredCredential(
kDefaultPlatformCredentialHandle);
if (platformCredential == "") {
LOGGER.info("Unable to retrieve platform credential");
cout << "------> Unable to retrieve platform credential" << endl;
} else {
LOGGER.info("Successfully retrieved platform credential");
}
return platformCredential;
}
/**
* Method to get a stored credential such as the Endorsement Credential and
* Platform Credential as a byte-based, DER-encoded X509 credential.
*
* @param credentialHandle NVRAM address for Endorsement key pair
* @throws HirsRuntimeException on failure
* @return string of hex-encoded bytes representing DER-encoded X509 credential
*/
string CommandTpm2::getStoredCredential(
const string& credentialHandle) {
LOGGER.info("Attempting to determine key size.");
uint16_t dataSize = getNvIndexDataSize(credentialHandle);
if (dataSize == 0) {
stringstream errStream;
errStream << "Could not parse NV List command. It did not contain the "
<< "handle: " << credentialHandle;
LOGGER.warn(errStream.str());
return "";
}
stringstream logstream;
logstream << "Key size acquired. Attempting credential retrieval at "
<< "address " << credentialHandle;
LOGGER.info(logstream.str());
string credential = readNvIndex(credentialHandle, dataSize);
if (credential.empty()) {
stringstream errStream;
errStream << "Could not parse NV Read command. Verify the size and "
<< "location were correct: " << to_string(dataSize)
<< " bytes at " << credentialHandle;
LOGGER.warn(errStream.str());
return "";
}
LOGGER.info("Credential retrieval successful.");
return credential;
}
/**
* Method to generate an Endorsement Key (EK) pair at the default address of
* 0x81010001, if one does not already exist at that location.
* Currently, this uses tpm2_tools and, consequently, creates a file containing
* the public area of the newly created EK pair.
*
* @param keyType determines the algorithm used to generate the EK pair
*/
void CommandTpm2::createEndorsementKey(const AsymmetricKeyType& keyType) {
LOGGER.info("Creating Endorsement Key.");
if (hasPersistentObject(kDefaultEkHandle)) {
LOGGER.info("Endorsement key already exists at default address.");
createPublicAreaFile(kDefaultEkHandle, kDefaultEkPubFilename);
return;
}
LOGGER.info("Attempting to create EK at: " + string(kDefaultEkHandle));
stringstream argsStream;
switch (keyType) {
case AsymmetricKeyType::RSA:
argsStream << " -g " << kRsaAlgorithmId;
break;
case AsymmetricKeyType::ECC:
argsStream << " -g " << kEccAlgorithmId;
break;
}
argsStream << " -H " << kDefaultEkHandle
<< " -f " << kDefaultEkPubFilename
<< endl;
runTpm2CommandWithRetry(kTpm2ToolsGetPubEkCommand, argsStream.str(),
__LINE__);
LOGGER.info("Endorsement Key successfully created.");
}
/**
* Method to retrieve the public area of a TPM's endorsement key as a
* byte-encoded string.
*
* @return string of bytes representing an endorsement key public area
*/
string CommandTpm2::getEndorsementKeyPublicArea() {
LOGGER.info("Attempting to read EK public area from file: "
+ string(kDefaultEkPubFilename));
string binaryEncodedPublicArea = getPublicArea(kDefaultEkPubFilename);
LOGGER.info("Public area successfully read.");
return binaryEncodedPublicArea;
}
/**
* Method to generate an Attestation Key (AK) pair
* (AKA a restricted-use signing key pair.) Calls down to
* tpm2-tools to generate the ak and persist it in TPM
* storage as a child key under the default Endorsement Key.
* Generates two files: one containing the ak public data and
* one containing the ak name. Ak is persisted at the default location.
* The current algorithm for key generation defaults to RSA.
*/
void CommandTpm2::createAttestationKey() {
if (hasPersistentObject(kDefaultAkHandle)) {
LOGGER.info(string("Attestation key already exists at default address")
+ "\nFlushing key...");
flushPersistentObject(kDefaultAkHandle);
}
stringstream argsStream;
argsStream << " -E " << kDefaultEkHandle
<< " -k " << kDefaultAkHandle
<< " -f " << kDefaultAkPubFilename
<< " -n " << kDefaultAkNameFilename
<< endl;
LOGGER.info("Running getpubak with arguments: "
+ argsStream.str());
runTpm2CommandWithRetry(kTpm2ToolsGetPubAkCommand, argsStream.str(),
__LINE__);
LOGGER.info("AK created successfully");
}
/**
* Method to get the byte-encoded public key portion of the AK pair.
* Assumes createAk has been called and default filenames were used.
* Takes generated public data and name file and packages them into
* a protobuf data structure for transmission.
*
* @return protobuf encoded Attestation Public Key Data
*/
string CommandTpm2::getAttestationKeyPublicArea() {
LOGGER.info("Attempting to read AK public area from file: "
+ string(kDefaultAkPubFilename));
string binaryEncodedPublicArea = getPublicArea(kDefaultAkPubFilename);
LOGGER.info("Public area successfully read.");
return binaryEncodedPublicArea;
}
/**
* Method to create identity claim to send to the Attestation Certificate
* Authority (ACA).
*
* @param deviceInfo device specific info that can be verified
* @param akPublicArea the public key area blob for the AK
* @param ekPublicArea the public key area blob for the endorsement key
* @param endorsementCredential endorsement credential for verification
* @param platformCredentials platform credentials for verification
*/
IdentityClaim CommandTpm2::createIdentityClaim(
const hirs::pb::DeviceInfo& deviceInfo,
const string& akPublicArea,
const string& ekPublicArea,
const string& endorsementCredential,
const vector<string>& platformCredentials) {
IdentityClaim identityClaim;
identityClaim.set_allocated_dv(new hirs::pb::DeviceInfo(deviceInfo));
identityClaim.set_ak_public_area(akPublicArea);
identityClaim.set_ek_public_area(ekPublicArea);
if (endorsementCredential != "") {
identityClaim.set_endorsement_credential(endorsementCredential);
}
for (const auto& platformCredential : platformCredentials) {
if (platformCredential != "") {
identityClaim.add_platform_credential(platformCredential);
}
}
return identityClaim;
}
/**
* Method to activate a given attested identity with the TPM.
* Decodes a nonce blob provided by the ACA when the Identity Claim Request
* was made.
*
* @returns the decrypted, binary encoded nonce
*/
string CommandTpm2::activateIdentity() {
string binaryEncodedNonce;
// response blob has already been written to a file
// verify file exists
if (!fileExists(kDefaultIdentityClaimResponseFilename)) {
throw HirsRuntimeException(
"Identity claim response file does not exist",
"CommandTpm2::activateIdentity");
}
// TPM2 Tools major version 3.X.X prepends 4 bytes of a MAGIC NUMBER and
// 4 bytes of a version number to the file containing the cert and secret,
// but the ACA does not, nor does the ACA know which version of TPM2 Tools
// is running on the client machine. So we add the bytes here.
if (version == Tpm2ToolsVersion::VERSION_3) {
string s = fileToString(kDefaultIdentityClaimResponseFilename);
union {
UINT16 value;
BYTE bytes[2];
} uint16byteConverter;
uint16byteConverter.bytes[0] = s[0];
uint16byteConverter.bytes[1] = s[1];
UINT16 numBytesInCred = uint16byteConverter.value;
// Shift from Little Endian to Big Endian encoding for size of
// credential structure and secret structure, respectively
std::swap(s[0], s[1]);
std::swap(s[134], s[135]);
// Erase unnecessary zero padding due
s.erase(2 + numBytesInCred, 134 - 2 - numBytesInCred);
// Prepend header: MAGIC_NUMBER (0xBADCC0DE) + Version (0x00000001)
s.insert(s.begin(), {static_cast<char>(0xBA), static_cast<char>(0xDC),
static_cast<char>(0xC0), static_cast<char>(0xDE),
0x00, 0x00, 0x00, 0x01});
writeBinaryFile(s, kDefaultIdentityClaimResponseFilename);
}
stringstream argsStream;
argsStream << " -H " << kDefaultAkHandle
<< " -k " << kDefaultEkHandle
<< " -f " << kDefaultIdentityClaimResponseFilename
<< " -o " << kDefaultActivatedIdentityFilename
<< endl;
runTpm2CommandWithRetry(kTpm2ToolsActivateCredential, argsStream.str(),
__LINE__);
try {
binaryEncodedNonce = fileToString(kDefaultActivatedIdentityFilename);
} catch (HirsRuntimeException& ex) {
throw HirsRuntimeException("Unable to open Activate Identity file",
"CommandTpm2::activateIdentity");
}
LOGGER.debug("Identity activated successfully");
return binaryEncodedNonce;
}
/**
* Stores the AK Certificate to the TPM.
*
* @param akCertificateByteString string containing the raw bytes of the certificate
*/
void CommandTpm2::storeAKCertificate(
const string& akCertificateByteString) {
if (hasNvIndexDefined(kAKCertificateHandle)) {
LOGGER.info("AK Cert found at "
+ string(kAKCertificateHandle)
+ ". Releasing from NV Space...");
releaseNvIndex(kAKCertificateHandle);
}
std::ofstream akCertificateFile(kDefaultAkCertFilename);
akCertificateFile << akCertificateByteString;
akCertificateFile.close();
stringstream argsStream;
size_t akCertificateByteStringSize = akCertificateByteString.size();
argsStream << " -x " << kAKCertificateHandle
<< " -a " << kDefaultOwnerAuthHandle
<< " -t " << kDefaultAttributeValue
<< " -s " << akCertificateByteStringSize
<< endl;
runTpm2CommandWithRetry(kTpm2ToolsNvDefineCommand, argsStream.str(),
__LINE__);
try {
LOGGER.debug(string("Beginning to write to NV Index: ")
+ kAKCertificateHandle);
LOGGER.debug("Provided data size: "
+ to_string(akCertificateByteStringSize));
string nvWriteArguments
= createNvWriteCommandArgs(kAKCertificateHandle,
kDefaultAkCertFilename);
runTpm2CommandWithRetry(kTpm2ToolsNvWriteCommand, nvWriteArguments,
__LINE__);
} catch (HirsRuntimeException& ex) {
LOGGER.warn(string("Attempt to write AK Certificate to TPM failed.")
+ string(" The following output was given:\n")
+ string(ex.what()));
}
}
/**
* Private helper method that builds the command arguments for tpm2_tools
* NV Write tool.
*
* @param nvIndex the index at which to peform the write command
* @param writeFile the filename of the file to write to the NV index
* @return the argument string to be affixed to the NV Write command
*/
string CommandTpm2::createNvWriteCommandArgs(const string& nvIndex,
const string& writeFile) {
stringstream argumentsStringStream;
argumentsStringStream << " -x " << nvIndex
<< " -a " << kDefaultOwnerAuthHandle
<< " ";
switch (version) {
case Tpm2ToolsVersion::VERSION_1_1_0:
case Tpm2ToolsVersion::VERSION_2_1_0:
argumentsStringStream << "-f ";
break;
}
argumentsStringStream << writeFile
<< endl;
return argumentsStringStream.str();
}
/**
* Method to get a quote (signed pcr selection) from the TPM 2.0 device.
*
* @param pcr_selection selection of pcrs to sign
* @param nonce blob provided by the ACA when the Identity Claim Request
* @return the argument string to be affixed to tpm quote
*/
string CommandTpm2::getQuote(const string& pcr_selection,
const string& nonce) {
string quote;
stringstream argsStream;
int result = 0;
for (size_t count = 0; count < nonce.length(); ++count) {
result *=2;
result += nonce[count] == '1'? 1 : 0;
}
stringstream ss;
ss << std::hex << std::setw(8) << std::setfill('0') << result;
string hexNonce(ss.str());
argsStream << " -k " << kDefaultAkHandle
<< " -g " << kTpm2Sha256SigAlgorithm
<< " -l " << pcr_selection
<< " -q " << hexNonce // this needs to be a hex string
<< endl;
LOGGER.info("Running tpm2_quote with arguments: " + argsStream.str());
quote = runTpm2CommandWithRetry(kTpm2ToolsGetQuoteCommand,
argsStream.str(),
__LINE__);
LOGGER.info("TPM Quote successful");
return quote;
}
/**
* Method to get the full list of pcrs from the TPM.
*
*/
string CommandTpm2::getPcrList() {
string pcrslist;
stringstream argsStream;
argsStream << endl;
LOGGER.info("Running tpm2_pcrlist with arguments: " + argsStream.str());
pcrslist = runTpm2CommandWithRetry(kTpm2ToolsPcrListCommand,
argsStream.str(),
__LINE__);
LOGGER.info("TPM PCR List successful");
return pcrslist;
}
/**
* Private helper method to offload the process of running tpm2_nvlist
* and parsing the output for the data size at a particular nvIndex.
*
* @param nvIndex the memory address whose data size is of interest
* @throws HirsRuntimeException if tpm2_nvlist is not run successfully
* @return the size of the data at nvIndex, or 0 if it's not found
*/
uint16_t CommandTpm2::getNvIndexDataSize(const string& nvIndex) {
string listOutput;
try {
listOutput = runTpm2CommandWithRetry(kTpm2ToolsNvListCommand, "",
__LINE__);
} catch (HirsRuntimeException& ex) {
// Due to bug in tpm2-tools 2.1.0, check to see if error was success
if (contains(ex.what(), "NV indexes defined.")) {
listOutput = ex.what();
} else {
throw;
}
}
return Tpm2ToolsOutputParser::parseNvDataSize(nvIndex, listOutput);
}
/**
* Private helper method to offload the process of running tpm2_nvread
* and converting the data output to a hex-encoded byte string.
*
* @param nvIndex the starting memory address to read data from
* @param dataSize the amount of data to read at the nvIndex
* @throws HirsRuntimeException if tpm2_nvread is not run successfully
* @return the output in a hex-encoded byte string, or an empty string if
* nothing is found at nvIndex
*/
string CommandTpm2::readNvIndex(const string& nvIndex,
const uint16_t& dataSize) {
LOGGER.info("Beginning to read at NV Index: " + nvIndex);
LOGGER.info("Provided dataSize: " + to_string(dataSize));
uint16_t maxNvBufferSize = 128;
uint16_t nvReadIterations = dataSize / maxNvBufferSize;
uint16_t nvBufferRemainder = dataSize % maxNvBufferSize;
uint16_t offset = 0;
stringstream nvReadOutput;
for (int i = 0; i <= nvReadIterations; i++) {
string nvReadArguments;
if (i == nvReadIterations) {
nvReadArguments = createNvReadCommandArgs(nvIndex, offset,
nvBufferRemainder);
} else {
nvReadArguments = createNvReadCommandArgs(nvIndex, offset,
maxNvBufferSize);
}
LOGGER.info("Command args: " + nvReadArguments);
string rawNvReadOutput = runTpm2CommandWithRetry(
kTpm2ToolsNvReadCommand, nvReadArguments, __LINE__);
switch (version) {
case Tpm2ToolsVersion::VERSION_1_1_0:
case Tpm2ToolsVersion::VERSION_2_1_0:
nvReadOutput << Tpm2ToolsOutputParser::parseNvReadOutput(
rawNvReadOutput);
break;
case Tpm2ToolsVersion::VERSION_3:
nvReadOutput << rawNvReadOutput;
break;
}
if (i != nvReadIterations) {
offset += maxNvBufferSize;
}
}
return nvReadOutput.str();
}
/**
* Private helper method to determine if an NV Index has been previously
* defined.
*
* @param nvIndex memory address at which to check for defined NV Index
* @throws HirsRuntimeException if failed to retrieve dataSize successfully
* @return true, if a definition exists at the given index / false, otherwise
*/
bool CommandTpm2::hasNvIndexDefined(const string& nvIndex) {
uint16_t dataSize = getNvIndexDataSize(nvIndex);
return dataSize != 0;
}
/**
* Private helper method to offload the process of running tpm2_nvrelease
* on an NV Index. This should release the NV Index from the
* TPM and allow the space to be redefined and used again.
*
* @param nvIndex NV memory address to release
* @throws HirsRuntimeException if tpm2_nvrelease is not run successfully
*/
void CommandTpm2::releaseNvIndex(const string& nvIndex) {
LOGGER.info("Releasing NV Index at: " + nvIndex);
stringstream argsStream;
switch (version) {
case Tpm2ToolsVersion::VERSION_1_1_0:
case Tpm2ToolsVersion::VERSION_2_1_0:
argsStream << " -X -P " << kWellKnownSecret;
break;
case Tpm2ToolsVersion::VERSION_3:
argsStream << " -P hex:" << kWellKnownSecret;
break;
}
argsStream << " -a " << kDefaultOwnerAuthHandle
<< " -x " << nvIndex;
runTpm2CommandWithRetry(kTpm2ToolsNvReleaseCommand, argsStream.str(),
__LINE__);
LOGGER.info("NV Index released successfully");
}
/**
* Private helper method that builds the command arguments for tpm2_tools
* NV Read tool.
*
* @param nvIndex the index at which to peform the read command
* @param readSize the size (in bytes) to pull with the read command
* @return the argument string to be affixed to the NV Read command
*/
string CommandTpm2::createNvReadCommandArgs(const string& nvIndex,
const uint16_t& offset,
const uint16_t& readSize) {
stringstream argumentsStringStream;
argumentsStringStream << " -x " << nvIndex
<< " -a " << kDefaultOwnerAuthHandle
<< " -o " << to_string(offset)
<< " -s " << to_string(readSize)
<< endl;
return argumentsStringStream.str();
}
/**
* Private helper method to offload the process of running tpm2_listpersistent
* and determining if a persistent object exists on the TPM at the given
* memory address.
*
* @param handle memory address at which to check for a persistent object
* @throws HirsRuntimeException if tpm2_listpersistent is not run successfully
* @return true, if an object exists at the given handle / false, otherwise
*/
bool CommandTpm2::hasPersistentObject(const string& handle) {
string listOutput
= runTpm2CommandWithRetry(kTpm2ToolsListPersistentCommand, "",
__LINE__);
return Tpm2ToolsOutputParser::parsePersistentObjectExists(handle,
listOutput);
}
/**
* Private helper method to offload the process of running tpm2_evictcontrol
* on a persistent object. This should flush the persistent object from the
* TPM should there be an object at the specified handle.
*
* @param handle memory address at which to flush a persistent object
* @throws HirsRuntimeException if tpm2_evictcontrol is not run successfully
*/
void CommandTpm2::flushPersistentObject(const string& handle) {
stringstream argsStream;
argsStream << " -A " << "o" // Owner Auth Flag
<< " -H " << handle
<< " -S " << handle
<< endl;
LOGGER.info("Running evictcontrol with arguments: "
+ argsStream.str());
runTpm2CommandWithRetry(kTpm2ToolsEvictControlCommand, argsStream.str(),
__LINE__);
LOGGER.info("Object flushed successfully");
}
/**
* Private helper method that reads and then writes the public area
* for a key in the TPM to a file in the local directory.
*
* @param keyHandle memory address at which the public key is stored
* @param filename name to be given to the file that stores the key's public
* area
*/
void CommandTpm2::createPublicAreaFile(const string& keyHandle,
const string& filename) {
// Note: we always need to write the file in the event tpm2-tools has been
// updated between provisioner runs. Thus, no short circuit logic to check
// for the file can be performed and avoid rewriting it can be performed.
if (fileExists(filename)) {
LOGGER.info("Public area file exists. Deleting for rewrite.");
remove(filename.c_str());
}
LOGGER.info("Creating public area file.");
stringstream argumentsStringStream;
argumentsStringStream << " -H " << keyHandle
<< " -o " << filename
<< endl;
runTpm2CommandWithRetry(kTpm2ToolsReadPublicCommand,
argumentsStringStream.str(),
__LINE__);
LOGGER.info("Public area file successfully created.");
}
string CommandTpm2::getPublicArea(const std::string& filename) {
string binaryEncodedPublicArea;
// need to read data from files
try {
binaryEncodedPublicArea = fileToString(filename);
} catch (HirsRuntimeException& ex) {
throw HirsRuntimeException("Unable to open public area file",
"CommandTpm2::getPublicArea");
}
// TPM2 Tools versions 1.1.0 and 2.1.0 affix 2 bytes of zeroes to files
// containing a public area, but the ACA does not know which version of
// TPM2 Tools is running on the client machine. So we remove the extra
// bytes here.
switch (version) {
case Tpm2ToolsVersion::VERSION_1_1_0:
case Tpm2ToolsVersion::VERSION_2_1_0:
binaryEncodedPublicArea.erase(binaryEncodedPublicArea.end() - 2,
binaryEncodedPublicArea.end());
break;
}
LOGGER.debug("Successfully read public data");
return binaryEncodedPublicArea;
}
string CommandTpm2::runTpm2CommandWithRetry(const string& command,
const string& args,
int sourceCodeLineNumber) {
string tpmErrorCode;
for (int i = 0;; ++i) {
try {
if (command.compare(kTpm2ToolsNvReadCommand) == 0) {
return hirs::utils::Process::runData(command, args,
"CommandTpm2.cpp", sourceCodeLineNumber);
} else {
return hirs::utils::Process::run(command, args,
"CommandTpm2.cpp", sourceCodeLineNumber);
}
} catch (HirsRuntimeException& ex) {
tpmErrorCode = Tpm2ToolsOutputParser::parseTpmErrorCode(ex.what());
if (tpmErrorCode == kTpm2RetryCommandCode &&
i < kMaxRetryCommandAttempts) {
LOGGER.warn("Waiting 100 ms and Retrying Command: " + command);
sleep_for(milliseconds(100));
continue;
}
throw;
}
}
}

View File

@ -1,248 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include <DeviceInfoCollector.h>
#include <Utils.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <climits>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include <HirsRuntimeException.h>
# define NOT_SPECIFIED "Not Specified"
using hirs::exception::HirsRuntimeException;
using hirs::file_utils::fileToString;
using hirs::file_utils::getFileAsOneLineOrEmptyString;
using hirs::log::Logger;
using hirs::string_utils::trimNewLines;
using std::ifstream;
using std::pair;
using std::string;
using std::stringstream;
using std::vector;
const Logger DeviceInfoCollector::LOGGER = Logger::getDefaultLogger();
hirs::pb::DeviceInfo DeviceInfoCollector::collectDeviceInfo() {
hirs::pb::DeviceInfo dv;
dv.mutable_fw()->CopyFrom(collectFirmwareInfo());
dv.mutable_hw()->CopyFrom(collectHardwareInfo());
dv.mutable_nw()->CopyFrom(collectNetworkInfo());
dv.mutable_os()->CopyFrom(collectOsInfo());
return dv;
}
hirs::pb::FirmwareInfo DeviceInfoCollector::collectFirmwareInfo() {
hirs::pb::FirmwareInfo fw;
fw.set_biosvendor(trimNewLines(
fileToString("/sys/class/dmi/id/bios_vendor", NOT_SPECIFIED)));
fw.set_biosversion(
getFileAsOneLineOrEmptyString("/sys/class/dmi/id/bios_version"));
fw.set_biosreleasedate(
getFileAsOneLineOrEmptyString("/sys/class/dmi/id/bios_date"));
LOGGER.info("Bios Vendor: " + fw.biosvendor());
LOGGER.info("Bios Version: " + fw.biosversion());
LOGGER.info("Bios Date: " + fw.biosreleasedate());
return fw;
}
hirs::pb::HardwareInfo DeviceInfoCollector::collectHardwareInfo() {
hirs::pb::HardwareInfo hw;
// Need root to read some of these, will be "" otherwise
hw.set_manufacturer(trimNewLines(
fileToString("/sys/class/dmi/id/sys_vendor", NOT_SPECIFIED)));
hw.set_productname(trimNewLines(
fileToString("/sys/class/dmi/id/product_name", NOT_SPECIFIED)));
hw.set_productversion(
getFileAsOneLineOrEmptyString("/sys/class/dmi/id/product_version"));
hw.set_systemserialnumber(
getFileAsOneLineOrEmptyString("/sys/class/dmi/id/product_serial"));
LOGGER.info("System Manufacturer: " + hw.manufacturer());
LOGGER.info("Product Name: " + hw.productname());
LOGGER.info("Product Version: " + hw.productversion());
LOGGER.info("System Serial Number: " + hw.systemserialnumber());
return hw;
}
hirs::pb::NetworkInfo DeviceInfoCollector::collectNetworkInfo() {
hirs::pb::NetworkInfo nw;
nw.set_hostname(collectHostname());
// Get IP and MAC address, will be empty if no non-loopback interface
vector<pair<string, string>> x = getNetworks();
// Choose first non-loopback address/MAC pair
for (auto const & addressPair : x) {
string ip = addressPair.first;
string mac = addressPair.second;
if (ip != "127.0.0.1" && ip != "::1") {
nw.set_ipaddress(ip);
nw.set_macaddress(mac);
break;
}
}
LOGGER.info("Network Info IP: " + nw.ipaddress());
LOGGER.info("Network Info MAC: " + nw.macaddress());
LOGGER.info("Network Info Hostname: " + nw.hostname());
return nw;
}
vector<pair<string, string>> DeviceInfoCollector::getNetworks() {
struct ifaddrs* ifaddr, * ifa;
int ret;
char host[NI_MAXHOST];
string hostStr;
vector<pair<string, string>> interfaces;
int family;
// if command fails, return empty list
if (getifaddrs(&ifaddr) == -1) {
LOGGER.error("getifaddrs failed");
return interfaces;
}
/*
* Walk through linked list, maintaining head pointer so we can free
* list later
*/
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
/*
* Check for ifa_addr == NULL here. This is true on at least
* some systems when a tun0/P-t-P interface is up (aka, a VPN).
* My reading of the man page indicates that should not be the
* case, but ubuntu 12.04 returns NULL for all ifa_* entries.
* And the example checks for NULL here anyway, so do so and
* skip the interface if necessary.
*/
if (!ifa->ifa_addr)
continue;
family = ifa->ifa_addr->sa_family;
if (family != AF_INET && family != AF_INET6)
continue;
if (family == AF_INET || family == AF_INET6) {
memset(host, 0, NI_MAXHOST);
ret = getnameinfo(ifa->ifa_addr,
(ifa->ifa_addr->sa_family == AF_INET) ?
sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6),
host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
hostStr = string(host);
if (ret) {
LOGGER.error("getnameinfo failed");
continue;
}
// get MAC address.. doesn't seem to be a portable/posix way.
char scratch[256];
string macaddr;
snprintf(scratch, sizeof(scratch), "/sys/class/net/%s/address",
ifa->ifa_name);
macaddr = fileToString(scratch);
if (macaddr.empty()) {
LOGGER.error("Error reading MAC address");
continue;
}
macaddr.erase(remove(macaddr.begin(), macaddr.end(), '\n'),
macaddr.end());
LOGGER.debug("Adding address pair IP: " + string(host) +
" MAC: " + macaddr);
interfaces.push_back(make_pair(hostStr, macaddr));
}
}
freeifaddrs(ifaddr);
return interfaces;
}
string DeviceInfoCollector::collectHostname() {
char host[HOST_NAME_MAX];
int ret = gethostname(host, HOST_NAME_MAX);
if (ret) {
LOGGER.error("gethostname failed");
return "";
}
string hostStr(host);
LOGGER.debug("Found hostname: " + hostStr);
return hostStr;
}
hirs::pb::OsInfo DeviceInfoCollector::collectOsInfo() {
hirs::pb::OsInfo info;
ifstream releaseFile;
string line;
releaseFile.open("/etc/os-release");
if (releaseFile.is_open()) {
while (getline(releaseFile, line)) {
stringstream ss(line);
string item;
std::vector<string> tokens;
char* delim = const_cast<char*>("=");
while (getline(ss, item, *delim)) {
tokens.push_back(item);
}
for (int i=0; i < tokens.size(); i++) {
if (tokens[i] == "ID") {
info.set_distribution(tokens[i+1]);
}
if (tokens[i] == "VERSION_ID") {
info.set_distributionrelease(tokens[i+1]);
}
}
}
releaseFile.close();
} else {
LOGGER.error("/etc/os-release read failed");
}
struct utsname uts;
int ret = uname(&uts);
if (ret) {
LOGGER.error("Uname read failed");
} else {
info.set_osname(uts.sysname);
info.set_osversion(uts.version);
info.set_distributionrelease(uts.release);
info.set_osarch(uts.machine);
}
LOGGER.info("OS Name: " + info.osname());
LOGGER.info("OS Version: " + info.osversion());
LOGGER.info("Architecture: " + info.osarch());
LOGGER.info("Distribution: " + info.distribution());
LOGGER.info("Distribution Release: " + info.distributionrelease());
return info;
}

View File

@ -1,31 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include <HirsRuntimeException.h>
#include <sstream>
#include <string>
using hirs::exception::HirsRuntimeException;
using std::endl;
using std::string;
using std::stringstream;
HirsRuntimeException::HirsRuntimeException(const string& msg,
const string& origin)
: runtime_error(buildMessage(msg, origin)) {}
HirsRuntimeException::~HirsRuntimeException() = default;
string HirsRuntimeException::buildMessage(const string& msg,
const string& origin) {
stringstream headerStream;
if (!origin.empty()) {
headerStream << "<" << origin << ">: ";
}
stringstream msgStream;
msgStream << headerStream.str() << msg << endl;
return msgStream.str();
}

View File

@ -1,149 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include <Logger.h>
#include <log4cplus/loggingmacros.h>
#include <string>
#include <mutex>
#include <Properties.h>
#include <Utils.h>
using std::exception;
using std::string;
using std::stringstream;
using hirs::file_utils::fileExists;
using hirs::log::Logger;
using hirs::properties::Properties;
const char* const Logger::kDefaultProvisionerLoggerName = "tpm2_provisioner";
const char* const Logger::kPropFileLocation = "/etc/hirs/logging.properties";
static std::once_flag configureRootLoggerOnce;
Logger::Logger(const string loggerName)
: kLogger(log4cplus::Logger::getInstance(LOG4CPLUS_TEXT(loggerName))) {
// set root logger threshold once (on first logger instantiation)
std::call_once(configureRootLoggerOnce, [] {
setThresholdFromLoggingProperties(log4cplus::Logger::getRoot());
});
// set logger threshold for actual logger
setThresholdFromLoggingProperties(kLogger);
}
void Logger::setThresholdFromLoggingProperties(log4cplus::Logger logger) {
// if logging.properties exists, attempt to set the
// appropriate level for the given logger
if (fileExists(kPropFileLocation)) {
Properties props(kPropFileLocation);
string loggerName = logger.getName();
string logLevelKey = loggerName + ".level";
if (props.isSet(logLevelKey)) {
string level = props.get(logLevelKey);
int l4cpLevel = log4cplus::getLogLevelManager().fromString(level);
if (l4cpLevel != log4cplus::NOT_SET_LOG_LEVEL) {
logger.setLogLevel(l4cpLevel);
}
}
}
}
void Logger::log(const LogLevel& logLevel, const string& msg,
const exception* ex = nullptr) const {
stringstream logMsg;
if (ex != nullptr) {
logMsg << ex->what() << ": ";
}
switch (logLevel) {
case LogLevel::DEBUG: {
LOG4CPLUS_DEBUG(kLogger, LOG4CPLUS_TEXT(logMsg.str())
<< LOG4CPLUS_TEXT(msg));
break;
}
case LogLevel::ERROR: {
LOG4CPLUS_ERROR(kLogger, LOG4CPLUS_TEXT(logMsg.str())
<< LOG4CPLUS_TEXT(msg));
break;
}
case LogLevel::FATAL: {
LOG4CPLUS_FATAL(kLogger, LOG4CPLUS_TEXT(logMsg.str())
<< LOG4CPLUS_TEXT(msg));
break;
}
case LogLevel::INFO: {
LOG4CPLUS_INFO(kLogger, LOG4CPLUS_TEXT(logMsg.str())
<< LOG4CPLUS_TEXT(msg));
break;
}
case LogLevel::TRACE: {
LOG4CPLUS_TRACE(kLogger, LOG4CPLUS_TEXT(logMsg.str())
<< LOG4CPLUS_TEXT(msg));
break;
}
case LogLevel::WARN: {
LOG4CPLUS_WARN(kLogger, LOG4CPLUS_TEXT(logMsg.str())
<< LOG4CPLUS_TEXT(msg));
break;
}
}
}
Logger Logger::getDefaultLogger() {
return getLogger(kDefaultProvisionerLoggerName);
}
Logger Logger::getLogger(const string& loggerName) {
return Logger(loggerName);
}
void Logger::debug(const string& msg) const {
log(LogLevel::DEBUG, msg);
}
void Logger::debug(const string& msg, const exception* ex) const {
log(LogLevel::DEBUG, msg, ex);
}
void Logger::error(const string& msg) const {
log(LogLevel::ERROR, msg);
}
void Logger::error(const string& msg, const exception* ex) const {
log(LogLevel::ERROR, msg, ex);
}
void Logger::fatal(const string& msg) const {
log(LogLevel::FATAL, msg);
}
void Logger::fatal(const string& msg, const exception* ex) const {
log(LogLevel::FATAL, msg, ex);
}
void Logger::info(const string& msg) const {
log(LogLevel::INFO, msg);
}
void Logger::info(const string& msg, const exception* ex) const {
log(LogLevel::INFO, msg, ex);
}
void Logger::trace(const string& msg) const {
log(LogLevel::TRACE, msg);
}
void Logger::trace(const string& msg, const exception* ex) const {
log(LogLevel::TRACE, msg, ex);
}
void Logger::warn(const string& msg) const {
log(LogLevel::WARN, msg);
}
void Logger::warn(const string& msg, const exception* ex) const {
log(LogLevel::WARN, msg, ex);
}

View File

@ -1,210 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include <Process.h>
#include <Logger.h>
#include <HirsRuntimeException.h>
#include <Utils.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <cstdio>
#include <cerrno>
#include <cstring>
#include <sstream>
#include <iostream>
#include <string>
#include <utility>
using hirs::exception::HirsRuntimeException;
using hirs::log::Logger;
using hirs::utils::Process;
using hirs::file_utils::dirExists;
using hirs::file_utils::fileExists;
using hirs::file_utils::fileToString;
using hirs::file_utils::trimFilenameFromPath;
using hirs::string_utils::trimChar;
using std::cerr;
using std::endl;
using std::ostream;
using std::stringstream;
using std::string;
using std::to_string;
const Logger Process::LOGGER = Logger::getDefaultLogger();
const char* const Process::kPgrepCommand = "pgrep";
const int Process::kMaxStatFileProcessNameLength = 15;
/**
* Constructor.
* @param executable the executable to be run
* @param arguments the arguments including options to be passed to the
* executable (defaults to empty string)
*/
Process::Process(const string& executable, const string& arguments)
: executable(executable), arguments(arguments) {
}
/**
* Run the command and return the return value of the process.
* @return the return value of the Linux process (between 0-255)
*/
int Process::run() {
fflush(nullptr); // Flush output streams of preexisting buffered output.
stringstream commandStringStream;
commandStringStream << executable;
if (!arguments.empty()) {
commandStringStream << " " << arguments;
}
string command = commandStringStream.str();
LOGGER.info("Executing command: " + command);
FILE* f = popen(command.c_str(), "re");
if (f == nullptr) {
stringstream errStream;
errStream << "Unable to open output stream from command \""
<< command << "\":" << strerror(errno) << endl;
LOGGER.error(errStream.str());
return -1;
}
int c;
stringstream ss;
while ((c = getc(f)) != EOF) {
ss << static_cast<char>(c);
}
output = ss.str();
// Linux return values are 0-255 even though pclose() returns a 32-bit int
uint16_t retValAsBigEndian = static_cast<uint16_t>(pclose(f));
return ntohs(retValAsBigEndian);
}
/**
* Run the command and return the return value of the process.
* @param osForErrorLogging ostream to collect error message on failure
* @return the return value of the Linux process (between 0-255)
*/
int Process::run(ostream& osForErrorLogging) {
int processReturnValue = run();
if (processReturnValue != 0) {
osForErrorLogging << "Call to " << executable
<< " returned " << processReturnValue << endl;
}
if (processReturnValue == 127) {
osForErrorLogging << "Is " << executable << " in your path?" << endl;
}
return processReturnValue;
}
/**
* Return a string containing the standard output stream of the Linux process.
* @return a string containing the standard output stream of the Linux process
*/
string Process::getOutputString() const {
return output;
}
/**
* Static function for calling a process that must succeed or throw
* a HirsRuntimeException. This function is meant to be used with the
* RUN_PROCESS_OR_THROW macro in order to capture the source file name
* and source file line number for use in the exception message.
*
* @param executable the executable to be run
* @param arguments the arguments including options to be passed to the
* @param sourceFileName source file from which this method was called
* @param sourceLineNumber line number of source file from which this method
* was called
* executable (defaults to empty string)
*/
string Process::run(const string& executable,
const string& arguments,
const string& sourceFileName,
int sourceLineNumber) {
stringstream errorStream;
Process p(executable, arguments);
if (p.run(errorStream) != 0) {
errorStream << "\n\n"
<< "Process Output: "
<< p.getOutputString();
throw HirsRuntimeException(errorStream.str(),
sourceFileName + ": " + to_string(sourceLineNumber));
}
// Remove trailing newline if one exists
string str = p.getOutputString();
if (!str.empty() && str[str.length() - 1] == '\n') {
str.erase(str.length() - 1);
}
return str;
}
/**
* * Static function for calling a process that must succeed or throw
* * a HirsRuntimeException. This function is meant to be used with the
* * RUN_PROCESS_OR_THROW macro in order to capture the source file name
* * and source file line number for use in the exception message. Does not
* * drop any characters.
* * @param executable the executable to be run
* * @param arguments the arguments including options to be passed to the
* * @param sourceFileName source file from which this method was called
* * @param sourceLineNumber line number of source file from which this method
* * was called
* * executable (defaults to empty string)
* */
string Process::runData(const string& executable,
const string& arguments,
const string& sourceFileName,
int sourceLineNumber) {
stringstream errorStream;
Process p(executable, arguments);
if (p.run(errorStream) != 0) {
errorStream << "\n\n"
<< "Process Output: "
<< p.getOutputString();
throw HirsRuntimeException(errorStream.str(),
sourceFileName + ": " + to_string(sourceLineNumber));
}
string str = p.getOutputString();
return str;
}
/**
* Static function to check if a specified process is currently running in the
* local environment.
*
* @param executable the executable to check is running
* @return true, if executable is found to be running / false, otherwise
*/
bool Process::isRunning(const string& executable) {
// Check if executable includes path and trim to just process name if so
string processName = trimFilenameFromPath(executable);
// Log warning about including path to executable
if (processName.length() < executable.length()) {
stringstream warnStream;
warnStream << "Including the path to an executable isn't recommended "
<< "as this has no bearing on whether it's determined to "
<< "be running in the local environment.";
LOGGER.warn(warnStream.str());
}
// Sanitize any attempts to hijack the process
processName = trimChar(processName, ';');
// If the process name is longer than 15 characters log a warning
if (processName.length() > kMaxStatFileProcessNameLength) {
stringstream warnStream;
warnStream << "A process name with length greater than "
<< to_string(kMaxStatFileProcessNameLength)
<< " may result in a false negative depending on the"
<< " current runtime environment.";
LOGGER.warn(warnStream.str());
}
return Process(kPgrepCommand, "-c " + processName).run() == 0;
}

View File

@ -1,175 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include "Properties.h"
#include "Utils.h"
#include <HirsRuntimeException.h>
#include <sstream>
#include <utility>
#include <iostream>
#include <string>
#include <fstream>
#include <Logger.h>
using hirs::exception::HirsRuntimeException;
using hirs::log::Logger;
using std::string;
using hirs::file_utils::fileExists;
using hirs::string_utils::trimWhitespaceFromLeft;
using hirs::string_utils::trimWhitespaceFromRight;
using hirs::string_utils::trimWhitespaceFromBothEnds;
const Logger hirs::properties::Properties::LOGGER = Logger::getDefaultLogger();
/**
* This is a class that holds key-value configuration properties using a map.
* Properties can be dynamically set and retrieved using the available methods,
* or the class can load properties from a file with the constructor or 'load'
* method. Properties files used with this class should be of the following form:
*
* # comment lines begin with a '#'
* some.key=some.value
* some.other.key=some.other.value # inline comments are also allowed
*
* For each line, a key is the content to the left of the line's first equals sign,
* and a key's associated value is the content to the right of the first equals sign.
* Subsequent equals signs, if any, will be part of the value.
* Left-bounding and right-bounding whitespace will be trimmed from both keys and values, but any interior whitespace will be preserved.
* Keys and values are both case-sensitive.
* There may be only one pair of key and value per line.
* Keys that have a blank/empty string for a value will not be set.
* The current implementation does not provide for wrapping keys or values onto proceeding lines.
*/
namespace hirs {
namespace properties {
/**
* Construct an empty Properties mapping with no keys or values.
*/
Properties::Properties() {}
/**
* Construct a Properties mapping from a file path, pointing to a readable properties file
* file whose format conforms to the description given above.
*
* @param filepath path to a properties file
*/
Properties::Properties(const string& filepath) {
load(filepath);
}
/**
* Load keys and values from a readable properties file whose format conforms to the description given above.
* If a key contained in the file is already set in this instance, its value will be updated
* to reflect its value in the file. Keys contained in this instance but not present in the file
* will remain set.
*
* @param filepath path to a properties file
*/
void Properties::load(const string& filepath) {
if (!fileExists(filepath)) {
throw HirsRuntimeException(
"Can't load properties from file: " + filepath,
"Properties.cpp::properties::load");
}
std::ifstream f(filepath);
string line;
while (std::getline(f, line)) {
// trim whitespace from left
line = trimWhitespaceFromLeft(line);
// remove leading and inline comments
std::size_t hash_index = line.find('#');
if (hash_index != string::npos) {
line.erase(hash_index, string::npos);
}
// add new key, val pair, separated by first occurrence of '='
std::size_t eq_index = line.find('=');
if (eq_index != string::npos) {
string key = trimWhitespaceFromBothEnds(
line.substr(0, eq_index));
string value = trimWhitespaceFromBothEnds(
line.substr(eq_index+1));
// ensure that both key and value are not blank
if (key.length() > 0 && value.length() > 0) {
set(key, value);
}
}
}
f.close();
}
/**
* Retrieve the value associated with the given key. If no such key exists in these properties,
* this method will throw a HirsRuntimeException.
*
* @param key
* @return the key's associated value
*/
string Properties::get(const string& key) {
if (!isSet(key)) {
throw HirsRuntimeException(
"No such key: " + key,
"Properties.cpp::properties::get");
} else {
return properties.at(key);
}
}
/**
* Retrieve the value associated with the given key. If no such key exists in these properties,
* this method will return the given default value.
*
* @param key the name of the property being searched for
* @param defaultValue the default value to return if none is found
* @return the key's associated value, or the given default if no value is set
*/
string Properties::get(const string& key, const string& defaultValue) {
if (!isSet(key)) {
LOGGER.warn("No such key " + key +
" found in properties; returning default: " +
defaultValue);
return defaultValue;
} else {
return properties.at(key);
}
}
/**
* Returns true if there is a value set for the given key.
*
* @param key the key to check
* @return true if the key has an associated value, false if no value has been set for the given key
*/
bool Properties::isSet(const string& key) {
return properties.count(key) > 0;
}
/**
* Set the given key to the given value. Leading and trailing whitespace, if any, are trimmed from both the key and value.
* Attempting to set an empty key or value will result in this method throwing a
* HirsRuntimeException.
*
* @param key the property key to set
* @param value the property value to associate with the given key
*/
void Properties::set(const string& key, const string& value) {
string trimmedKey = trimWhitespaceFromBothEnds(key);
string trimmedValue = trimWhitespaceFromBothEnds(value);
if (trimmedKey.length() == 0 || trimmedValue.length() == 0) {
throw HirsRuntimeException(
"Cannot insert blank key (" + trimmedKey
+ ") or value (" + value + ")",
"Properties.cpp::properties::set");
}
properties[trimmedKey] = trimmedValue;
}
} // namespace properties
} // namespace hirs

View File

@ -1,102 +0,0 @@
syntax = "proto2";
package hirs.pb;
option java_package="hirs.attestationca.configuration.provisionerTpm2";
message FirmwareInfo {
required string biosVendor = 1;
required string biosVersion = 2;
required string biosReleaseDate = 3;
}
message HardwareInfo {
required string manufacturer = 1;
required string productName = 2;
required string productVersion = 3;
required string systemSerialNumber = 4;
repeated ComponentInfo chassisInfo = 5;
repeated ComponentInfo baseboardInfo = 6;
repeated ComponentInfo processorInfo = 7;
repeated ComponentInfo biosOrUefiInfo = 8;
repeated ComponentInfo nicInfo = 9;
repeated ComponentInfo hardDriveInfo = 10;
repeated ComponentInfo memoryInfo = 11;
}
message ComponentInfo {
required string manufacturer = 1;
required string model = 2;
optional string serialNumber = 3;
optional string revision = 4;
}
message NetworkInfo {
required string hostname = 1;
required string ipAddress = 2;
required string macAddress = 3;
}
message OsInfo {
required string osName = 1;
required string osVersion = 2;
required string osArch = 3;
required string distribution = 4;
required string distributionRelease = 5;
}
message TpmInfo {
required string tpmMake = 1;
required string tpmVersionMajor = 2;
required string tpmVersionMinor = 3;
required string tpmRevMajor = 4;
required string tpmRevMinor = 5;
}
message DeviceInfo {
required FirmwareInfo fw = 1;
required HardwareInfo hw = 2;
required NetworkInfo nw = 3;
required OsInfo os = 4;
optional bytes pcrslist = 5;
repeated bytes logfile = 6;
repeated bytes swidfile = 7;
optional bytes livelog = 8;
}
message IdentityClaim {
required DeviceInfo dv = 1;
required bytes ak_public_area = 2;
required bytes ek_public_area = 3;
optional bytes endorsement_credential = 4;
repeated bytes platform_credential = 5;
optional string client_version = 6;
optional string paccorOutput = 7;
optional bytes ldevid_public_area = 8;
}
message TpmQuote {
required string success = 1;
}
enum ResponseStatus {
PASS = 0;
FAIL = 1;
}
message IdentityClaimResponse {
optional bytes credential_blob = 1;
optional string pcr_mask = 2;
optional ResponseStatus status = 3 [default = FAIL];
}
message CertificateRequest {
required bytes nonce = 1;
optional bytes quote = 2;
}
message CertificateResponse {
optional string certificate = 1;
optional ResponseStatus status = 2 [default = FAIL];
optional string ldevidCertificate = 3;
}

View File

@ -1,161 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include <HirsRuntimeException.h>
#include <RestfulClientProvisioner.h>
#include <Utils.h>
#include <Properties.h>
#include <cpr/cpr.h>
#include <sstream>
#include <string>
#include <utility>
using hirs::exception::HirsRuntimeException;
using hirs::log::Logger;
using hirs::pb::IdentityClaim;
using hirs::pb::IdentityClaimResponse;
using hirs::pb::CertificateRequest;
using hirs::pb::CertificateResponse;
using hirs::properties::Properties;
using hirs::json_utils::JSONFieldParser;
using hirs::string_utils::binaryToHex;
using std::string;
using std::stringstream;
using std::to_string;
const Logger RestfulClientProvisioner::LOGGER = Logger::getDefaultLogger();
const char * const RestfulClientProvisioner::PROP_FILE_LOC =
"/etc/hirs/hirs-site.config";
const char * const RestfulClientProvisioner::PROP_ACA_FQDN
= "ATTESTATION_CA_FQDN";
const char * const RestfulClientProvisioner::PROP_ACA_PORT
= "ATTESTATION_CA_PORT";
const char * const RestfulClientProvisioner::ACA_ERROR_FIELDNAME
= "error";
RestfulClientProvisioner::RestfulClientProvisioner() {
Properties props(PROP_FILE_LOC);
acaAddress = props.get(PROP_ACA_FQDN, "localhost");
port = std::stoi(props.get(PROP_ACA_PORT, "8443"));
}
RestfulClientProvisioner::RestfulClientProvisioner(
const std::string& acaAddress, int acaPort)
: acaAddress(acaAddress), port(acaPort) {
}
string RestfulClientProvisioner::getAcaAddress() {
return acaAddress;
}
string RestfulClientProvisioner::sendIdentityClaim(
IdentityClaim identityClaim) {
{
stringstream logStream;
logStream << "Sending the identity claim to " << acaAddress
<< " on port " << port;
LOGGER.info(logStream.str());
}
string identityClaimByteString;
identityClaim.SerializeToString(&identityClaimByteString);
// Send serialized Identity Claim to ACA
LOGGER.info("Sending Serialized Identity Claim Binary");
auto r = cpr::Post(cpr::Url{"https://" + acaAddress + ":" + to_string(port)
+ "/HIRS_AttestationCA/portal/"
+ "client/identity-claim-tpm2/"
+ "process"},
cpr::Body{identityClaimByteString},
cpr::Header{{"Content-Type",
"application/octet-stream"},
{"Accept",
"application/octet-stream, application/json"}},
cpr::VerifySsl{false});
// Check ACA response, should be 200 if successful
if (r.status_code == 200) {
if (r.text.size() == 0) {
return "";
}
IdentityClaimResponse response;
try {
response.ParseFromString(r.text);
} catch (const google::protobuf::FatalException& e) {
LOGGER.error(e.what());
stringstream errormsg;
errormsg << "Provisioning failed. IdentityClaimResponse "
<< "did not contain credential_blob.";
throw HirsRuntimeException(errormsg.str(),
"RestfulClientProvisioner::sendIdentityClaim");
}
// Convert the nonce blob to hex for logging
string blobHex = binaryToHex(response.credential_blob());
stringstream logStream;
logStream << "Received nonce blob: " << blobHex;
LOGGER.info(logStream.str());
// Return the response
return response.SerializeAsString();
} else {
stringstream errormsg;
errormsg << "Error communicating with ACA server. "
<< "Received response code: " << to_string(r.status_code)
<< "\n\nError message from ACA was: "
<< JSONFieldParser::parseJsonStringField(r.text,
ACA_ERROR_FIELDNAME);
throw HirsRuntimeException(errormsg.str(),
"RestfulClientProvisioner::sendIdentityClaim");
}
}
string RestfulClientProvisioner::sendAttestationCertificateRequest(
CertificateRequest certificateRequest) {
string certificateRequestByteString;
certificateRequest.SerializeToString(&certificateRequestByteString);
// Send serialized certificate request to ACA
LOGGER.info("Sending Serialized DeviceInfo Binary");
auto r = cpr::Post(cpr::Url{"https://" + acaAddress + ":" + to_string(port)
+ "/HIRS_AttestationCA/portal/client"
+ "/request-certificate-tpm2"},
cpr::Body{certificateRequestByteString},
cpr::Header{{"Content-Type",
"application/octet-stream"},
{"Accept",
"application/octet-stream, application/json"}},
cpr::VerifySsl{false});
// Check ACA response, should be 200 if successful
if (r.status_code == 200) {
CertificateResponse response;
response.ParseFromString(r.text);
{
// Convert the certificate to hex for logging
string certificateHex = binaryToHex(response.certificate());
stringstream logStream;
logStream << "Received public certificate: " << certificateHex;
LOGGER.info(logStream.str());
}
// Return the public attestation certificate
// return response.certificate();
return response.SerializeAsString();
} else {
stringstream errormsg;
errormsg << "Error communicating with ACA server. "
<< "Received response code: " << to_string(r.status_code)
<< "\n\nError message from ACA was: "
<< JSONFieldParser::parseJsonStringField(r.text,
ACA_ERROR_FIELDNAME);
throw HirsRuntimeException(errormsg.str(),
"RestfulClientProvisioner::sendAttestationCertificateRequest");
}
}

View File

@ -1,287 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
/**
* Main entry point for the TPM2_Provisioner. Handles the input from the
* command line application and provisions the client for use with an
* attestation credential authority.
*/
#include <unistd.h>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <Process.h>
#include <Properties.h>
#include <regex>
#include "log4cplus/configurator.h"
#include "CommandTpm2.h"
#include "DeviceInfoCollector.h"
#include "HirsRuntimeException.h"
#include "RestfulClientProvisioner.h"
#include "Utils.h"
#include "Version.h"
using hirs::exception::HirsRuntimeException;
using hirs::file_utils::dirExists;
using hirs::log::Logger;
using hirs::tpm2::AsymmetricKeyType;
using hirs::tpm2::CommandTpm2;
using hirs::tpm2_tools_utils::Tpm2ToolsVersion;
using hirs::utils::Process;
using hirs::properties::Properties;
using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::stringstream;
using std::vector;
int provision() {
Logger logger = Logger::getDefaultLogger();
CommandTpm2 tpm2;
Properties props("/etc/hirs/tcg_boot.properties");
tpm2.setAuthData();
// get endorsement credential and endorsement key
cout << "----> Collecting endorsement credential from TPM" << endl;
string endorsementCredential = tpm2.getEndorsementCredentialDefault(
AsymmetricKeyType::RSA);
tpm2.createEndorsementKey();
string ekPublicArea = tpm2.getEndorsementKeyPublicArea();
// get attestation key
cout << "----> Creating attestation key" << endl;
tpm2.createAttestationKey();
string akPublicArea = tpm2.getAttestationKeyPublicArea();
// get platform credential
cout << "----> Collecting platform credential from TPM" << endl;
// string platformCredential = tpm2.getPlatformCredentialDefault();
std::vector<string> platformCredentials;
// if platformCredential is empty, not in TPM
// pull from properties file
// if (platformCredential.empty()) {
const std::string& cert_dir =
props.get(
"tcg.cert.dir",
"/boot/tcg/cert/platform/");
try {
platformCredentials =
hirs::file_utils::search_directory(cert_dir);
} catch (HirsRuntimeException& hirsRuntimeException) {
logger.error(hirsRuntimeException.what());
}
// } else {
// platformCredentials.push_back(platformCredential);
// }
// collect device info
cout << "----> Collecting device information" << endl;
hirs::pb::DeviceInfo dv = DeviceInfoCollector::collectDeviceInfo();
dv.set_pcrslist(tpm2.getPcrList());
// collect TCG Boot files
std::vector<string> rim_files;
std::vector<string> swidtag_files;
const std::string& rim_dir =
props.get(
"tcg.rim.dir",
"/boot/tcg/manifest/rim/");
const std::string& swid_dir =
props.get(
"tcg.swidtag.dir",
"/boot/tcg/manifest/swidtag/");
const std::string& live_log_file =
props.get(
"tcg.event.file",
"/sys/kernel/security/tpm0/binary_bios_measurements");
try {
rim_files = hirs::file_utils::search_directory(rim_dir);
for (const auto& rims : rim_files) {
if (rims != "") {
dv.add_logfile(rims);
}
}
} catch (HirsRuntimeException& hirsRuntimeException) {
logger.error(hirsRuntimeException.what());
}
try {
swidtag_files = hirs::file_utils::search_directory(swid_dir);
for (const auto& swidtag : swidtag_files) {
if (swidtag != "") {
dv.add_swidfile(swidtag);
}
}
} catch (HirsRuntimeException& hirsRuntimeException) {
logger.error(hirsRuntimeException.what());
}
try {
dv.set_livelog(hirs::file_utils::fileToString(live_log_file));
} catch (HirsRuntimeException& hirsRuntimeException) {
logger.error(hirsRuntimeException.what());
}
// send identity claim
cout << "----> Sending identity claim to Attestation CA" << endl;
hirs::pb::IdentityClaim identityClaim
= tpm2.createIdentityClaim(dv, akPublicArea, ekPublicArea,
endorsementCredential,
platformCredentials);
identityClaim.set_client_version(CLIENT_VERSION);
string paccorOutputString =
hirs::utils::Process::run(
"/opt/paccor/scripts/allcomponents.sh", "",
"TPM2_Provisioner.cpp", __LINE__);
identityClaim.set_paccoroutput(paccorOutputString);
RestfulClientProvisioner provisioner;
string response = provisioner.sendIdentityClaim(identityClaim);
hirs::pb::IdentityClaimResponse icr;
if (!icr.ParseFromString(response) || !icr.has_status()) {
logger.error("The ACA did not send a provisioning status.");
cout << "----> Provisioning failed." << endl;
cout << "Please refer to the Attestation CA for details." << endl;
return 0;
}
if (icr.status() == hirs::pb::ResponseStatus::FAIL) {
logger.error("The ACA responded with a FAIL status.");
cout << "----> Provisioning failed." << endl;
cout << "Please refer to the Attestation CA for details." << endl;
return 0;
}
if (!icr.has_credential_blob()) {
logger.error("The ACA did not send make credential blob.");
cout << "----> Provisioning failed." << endl;
cout << "The ACA did not send make credential information." << endl;
return 0;
}
string nonceBlob = icr.credential_blob();
if (nonceBlob == "") {
cout << "----> Provisioning failed." << endl;
cout << "The ACA sent empty credential information." << endl;
return 0;
}
// activateIdentity requires we read makeCredential output from a file
cout << "----> Received response. Attempting to decrypt nonce" << endl;
try {
hirs::file_utils::writeBinaryFile(
nonceBlob, CommandTpm2::kDefaultIdentityClaimResponseFilename);
} catch (const std::invalid_argument& e) {
logger.error(e.what());
throw HirsRuntimeException("Provisioning failed.",
"TPM2_Provisioner::provision");
}
string decryptedNonce = tpm2.activateIdentity();
cout << "----> Nonce successfully decrypted. Sending attestation "
<< "certificate request" << endl;
hirs::pb::CertificateRequest certificateRequest;
certificateRequest.set_nonce(decryptedNonce);
certificateRequest.set_quote(tpm2.getQuote(
icr.has_pcr_mask()
? icr.pcr_mask()
: "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23",
decryptedNonce));
const string& akCertificateByteString
= provisioner.sendAttestationCertificateRequest(certificateRequest);
hirs::pb::CertificateResponse cr;
cr.ParseFromString(akCertificateByteString);
if (cr.has_status()) {
if (cr.status() == hirs::pb::ResponseStatus::FAIL) {
cout << "----> Provisioning the quote failed. ";
cout << "Please refer to the Attestation CA for details." << endl;
return 0;
}
}
if (akCertificateByteString == "") {
cout << "----> Provisioning the quote failed. "
<< "Certificate returned is empty. ";
cout << "Please refer to the Attestation CA for details." << endl;
return 0;
}
cout << "----> Storing attestation key certificate" << endl;
tpm2.storeAKCertificate(akCertificateByteString);
return 1;
}
void printHelp() {
stringstream helpMessage;
helpMessage << "TPM 2.0 Provisioner\n"
<< "Version " << CLIENT_VERSION << "\n\n"
<< "To run the provisioning process, "
<< "enter hirs-provisioner-tpm2 provision\n";
cout << helpMessage.str() << endl;
}
int main(int argc, const char** argv) {
string log_directory = "/var/log/hirs/provisioner";
// directory should be created by rpm install
if (!dirExists(log_directory)) {
cerr << "Log directory /var/log/hirs/provisioner does not "
<< "exist. Exiting";
return 1;
}
log4cplus::initialize();
log4cplus::PropertyConfigurator::doConfigure(
"/etc/hirs/TPM2_Provisioner/log4cplus_config.ini");
Logger mainLogger = Logger::getDefaultLogger();
if (argc < 2) {
printHelp();
return 0;
}
string argument = argv[1];
if (argument == "provision") {
// Ensure we're running as root
if (getuid() != 0) {
string error = "Program must be run as root. Exiting";
cerr << (error);
mainLogger.error(error);
return 1;
}
// Ensure either tpm2-abrmd or the old resourcemgr is running
if (!Process::isRunning("tpm2-abrmd")
&& !Process::isRunning("resourcemgr")) {
stringstream errorStream;
errorStream << R"(Neither "tpm2-abmrd" nor the older )"
<< R"("resourcemgr" daemon is currently running. )"
<< "\nPlease ensure either is running before "
<< "attempting provisioning.\n";
cerr << (errorStream.str());
mainLogger.error(errorStream.str());
return 1;
}
cout << "--> Provisioning" << endl;
try {
if (provision()) {
cout << "----> Provisioning successful" << endl;
}
} catch (HirsRuntimeException& hirsRuntimeException) {
mainLogger.error(hirsRuntimeException.what());
cout << "----> Fatal error during provisioning. See "
<< "/var/log/hirs/provisioner/HIRS_ProvisionerTPM2.log for "
"details" << endl;
return 1;
}
} else {
printHelp();
}
log4cplus::Logger::shutdown();
}

View File

@ -1,199 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include <Tpm2ToolsUtils.h>
#include <HirsRuntimeException.h>
#include <Process.h>
#include <Utils.h>
#include <re2/re2.h>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>
using std::ifstream;
using std::out_of_range;
using std::string;
using std::stringstream;
using std::unordered_map;
using std::vector;
using hirs::exception::HirsRuntimeException;
namespace hirs {
namespace tpm2_tools_utils {
const unordered_map<string, Tpm2ToolsVersion>
Tpm2ToolsVersionChecker::kVersionMap = {
{"1.1.0", Tpm2ToolsVersion::VERSION_1_1_0 },
{"2.1.0", Tpm2ToolsVersion::VERSION_2_1_0 },
{"3", Tpm2ToolsVersion::VERSION_3 }
};
const unordered_map<string, Tpm2ToolsVersion>
Tpm2ToolsVersionChecker::kMaxSupportedVersionMap = {
{"Ubuntu 17.10", Tpm2ToolsVersion::VERSION_1_1_0 },
{"Ubuntu 18.04", Tpm2ToolsVersion::VERSION_2_1_0 },
{"Ubuntu 18.10", Tpm2ToolsVersion::VERSION_2_1_0 },
{"CentOS Linux 7", Tpm2ToolsVersion::VERSION_3 }
};
Tpm2ToolsVersion Tpm2ToolsVersionChecker::findTpm2ToolsVersion() {
string versionOutput = hirs::utils::Process::run("tpm2_nvlist", "-v",
"Tpm2ToolsUtils.cpp",
__LINE__);
string version = Tpm2ToolsOutputParser::parseTpm2ToolsVersion(
versionOutput);
string majorVersion = Tpm2ToolsOutputParser::parseTpm2ToolsMajorVersion(
version);
if (!version.empty()) {
try {
// Look to see if tpm2-tools major version is supported
return kVersionMap.at(majorVersion);
}
catch (const out_of_range& oor) {
// If major version not supported, then look for specific version
try {
return kVersionMap.at(version);
}
catch (const out_of_range& outOfRange) {
// If no version found, version is unsupported, throw exception
stringstream ss;
ss << "Unsupported Tpm2 Tools Version Detected: " << version;
throw HirsRuntimeException(ss.str(),
"Tpm2ToolsVersionChecker::findTpm2ToolsVersion");
}
}
} else {
// If version check returns empty, instead of throwing exception,
// then tpm2-tools is installed but version lookup is faulty.
// Get current runtime environment distribution.
string currentDistribution = getDistribution();
try {
// Look to see if current distribution has a supported version
// and use that as best guess at version number
return kMaxSupportedVersionMap.at(currentDistribution);
} catch (const out_of_range& oor) {
stringstream ss;
ss << "Unsupported Distribution Detected: " << currentDistribution;
throw HirsRuntimeException(ss.str(),
"Tpm2ToolsVersionChecker::findTpm2ToolsVersion");
}
}
}
string Tpm2ToolsVersionChecker::getDistribution() {
stringstream completeDistro;
string distribution;
string distributionRelease;
ifstream releaseFile;
string line;
releaseFile.open("/etc/os-release");
if (releaseFile.is_open()) {
while (getline(releaseFile, line)) {
stringstream ss(line);
string item;
vector<string> tokens;
while (getline(ss, item, '=')) {
tokens.push_back(item);
}
if (!tokens.empty() && tokens.at(0) == "NAME") {
distribution = string_utils::trimQuotes(tokens.at(1));
} else if (!tokens.empty() && tokens.at(0) == "VERSION_ID") {
distributionRelease = string_utils::trimQuotes(tokens.at(1));
}
}
completeDistro << distribution << " " << distributionRelease;
releaseFile.close();
}
return completeDistro.str();
}
uint16_t Tpm2ToolsOutputParser::parseNvDataSize(const string &nvHandle,
const string &nvListOutput) {
stringstream regexPatternStream;
regexPatternStream << nvHandle
<< "(?:.*\\n)+?"
<< "(?i).*size\\S*:\\s*([0-9]+)";
uint16_t dataSize;
if (RE2::PartialMatch(nvListOutput, regexPatternStream.str(), &dataSize)) {
return dataSize;
} else {
return 0;
}
}
string Tpm2ToolsOutputParser::parseNvReadOutput(const string &nvReadOutput) {
stringstream regexPatternStream;
regexPatternStream << ".*\\n*The size of data:[0-9]+";
string byteString = nvReadOutput;
// Remove tpm2_nvlist header
int numReplacements = RE2::GlobalReplace(&byteString,
regexPatternStream.str(), "");
if (numReplacements != 0) {
// Remove any non-hexadecimal characters
RE2::GlobalReplace(&byteString, "[^0-9A-Fa-f]+", "");
return string_utils::hexToBytes(byteString);
} else {
return "";
}
}
bool Tpm2ToolsOutputParser::parsePersistentObjectExists(
const std::string &handle,
const std::string &listPersistentOutput) {
stringstream regexPatternStream;
regexPatternStream << "(?i)Persistent.*handle.*:\\s*"
<< handle;
return RE2::PartialMatch(listPersistentOutput, regexPatternStream.str());
}
string Tpm2ToolsOutputParser::parseTpmErrorCode(const string& toolOutput) {
stringstream regexPatternStream;
regexPatternStream << "(?i)Error.*:\\s*(0x[0-9a-fA-F]{3})";
string errorCode;
if (RE2::PartialMatch(toolOutput, regexPatternStream.str(), &errorCode)) {
return errorCode;
} else {
return "";
}
}
string Tpm2ToolsOutputParser::parseTpm2ToolsVersion(const string& toolOutput) {
stringstream regexPatternStream;
regexPatternStream << "(?i)version[^0-9]*([0-9]+\\.[0-9]+\\.[0-9]+).*";
string version;
if (RE2::PartialMatch(toolOutput, regexPatternStream.str(), &version)) {
return version;
} else {
return "";
}
}
string Tpm2ToolsOutputParser::parseTpm2ToolsMajorVersion(
const string& toolVersion) {
stringstream regexPatternStream;
regexPatternStream << "^([0-9]+)\\.[0-9]+\\.[0-9]+$";
string majorVersion;
if (RE2::PartialMatch(toolVersion, regexPatternStream.str(),
&majorVersion)) {
return majorVersion;
} else {
return "";
}
}
} // namespace tpm2_tools_utils
} // namespace hirs

View File

@ -1,310 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include <Utils.h>
#include <HirsRuntimeException.h>
#include <re2/re2.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cctype>
#include <string>
#include <vector>
using std::hex;
using std::ifstream;
using std::ios;
using std::ofstream;
using std::remove;
using std::setfill;
using std::setw;
using std::string;
using std::stringstream;
using std::vector;
using hirs::exception::HirsRuntimeException;
namespace hirs {
namespace json_utils {
string JSONFieldParser::parseJsonStringField(const std::string &jsonObject,
const std::string &jsonFieldName) {
stringstream regexPatternStream;
regexPatternStream << "(?i)\\\""
<< jsonFieldName
<< "\\\"\\s*:\\s*\\\"(.*)\\\"";
string value;
if (RE2::PartialMatch(jsonObject, regexPatternStream.str(), &value)) {
return value;
} else {
return "";
}
}
} // namespace json_utils
namespace file_utils {
/**
* Returns whether or not the argument directory exists.
* @return True if the directory exists. False, otherwise.
*/
bool dirExists(const string& path) {
struct stat pathinfo;
if (stat(path.c_str(), &pathinfo) != 0) {
return false;
} else {
return (pathinfo.st_mode & S_IFDIR) != 0;
}
}
/**
* Returns whether or not the argument file name exists and is a
* regular file.
* @param filename file to check for existence
* @return True if the file exists. False, otherwise.
*/
bool fileExists(const string& filename) {
struct stat buffer;
if (stat(filename.c_str(), &buffer) != 0) {
return false;
} else {
return S_ISREG(buffer.st_mode);
}
}
string fileToString(const string& filename) {
stringstream ss;
ifstream t(filename);
if (!t.good()) {
ss << "Unable to open file: " << filename;
throw HirsRuntimeException(ss.str(),
"Utils.cpp::file_utils::fileToString");
}
ss << t.rdbuf();
t.close();
return ss.str();
}
/**
* Reads the contents of an entire file into a string. If
* the file can't be opened, the default value is returned.
* @param filename the name and path of the file to read.
* @param defaultVal the value to return if the file can't be
* opened.
* @return the contents of the specified file as a string.
*/
string fileToString(const string& filename, const string& defaultVal) {
stringstream ss;
ifstream t(filename);
if (!t.good()) {
return defaultVal;
}
ss << t.rdbuf();
t.close();
return ss.str();
}
string getFileAsOneLineOrEmptyString(const string& filename) {
return string_utils::trimNewLines(fileToString(filename, ""));
}
vector<string> search_directory(const string& directory) {
DIR *dr;
std::vector<string> files;
dr = opendir(directory.c_str());
if (dr) {
struct dirent *en;
while ((en = readdir(dr)) != NULL) {
stringstream ss;
ss << directory.c_str();
ss << en->d_name;
try {
files.push_back(fileToString(ss.str()));
} catch (HirsRuntimeException& hirsRuntimeException) {
std::cout << hirsRuntimeException.what();
}
}
// close directory
closedir(dr);
}
return files;
}
/**
* Takes a byte string and writes the contents to a file of the given name.
* @param bytes string bytes to write
* @param filename file name to be written to
*/
void writeBinaryFile(const string& bytes, const string& filename) {
ofstream file(filename, ios::out | ios::binary);
if (file.is_open()) {
file.write(bytes.c_str(), bytes.size());
file.close();
} else {
throw std::invalid_argument("Cannot write to specified file");
}
}
int getFileSize(const string& filename) {
std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
return in.tellg();
}
// copy portion of file to temp file
void splitFile(const string& inFilename, const string& outFilename,
int startPos, int readSize) {
ifstream inStr(inFilename, ifstream::binary);
ofstream outStr(outFilename, ofstream::binary);
inStr.seekg(startPos);
char* fileBlock = new char[readSize];
inStr.read(fileBlock, readSize);
outStr.write(fileBlock, readSize);
inStr.close();
outStr.close();
delete[] fileBlock;
}
/**
* Returns the filename from the end of a filesystem path
* @param path the filesystem path
* @return the filename from the path
*/
string trimFilenameFromPath(const string& path) {
auto lastPathSeparatorIndex = path.find_last_of('/');
// Return path if it wasn't actually a path
if (lastPathSeparatorIndex == string::npos) {
return path;
}
return path.substr(lastPathSeparatorIndex + 1);
}
} // namespace file_utils
namespace string_utils {
string binaryToHex(const string& bin) {
stringstream output;
for (int i = 0; i < 20; i++) {
stringstream str;
str << hex << setfill('0') << setw(2) << static_cast<int>(bin[i]);
string dig = str.str();
if (dig.length() > 2) {
dig = dig.substr(dig.length() - 2, 2);
}
output << dig;
}
return output.str();
}
bool contains(const string& str, const string& substring) {
return str.find(substring) != string::npos;
}
string longToHex(const uint32_t& value) {
stringstream output;
output << "0x" << hex << value;
return output.str();
}
bool isHexString(const string& str) {
bool isHexStringFlag = !str.empty();
if (isHexStringFlag) {
auto startIndex = str.begin();
if (str.substr(0, 2) == "0x") {
startIndex += 2;
}
isHexStringFlag = std::all_of(startIndex, str.end(), isxdigit);
}
return isHexStringFlag;
}
string hexToBytes(const string& hexString) {
// if the string has an odd number of chars, return an empty string
if (!isHexString(hexString) || hexString.size() % 2 != 0) {
return {};
}
vector<uint8_t> bytes;
for (uint32_t i = 0; i < hexString.length(); i += 2) {
string byteString = hexString.substr(i, 2);
uint8_t byte = static_cast<uint8_t>(strtol(byteString.c_str(),
nullptr, 16));
bytes.push_back(byte);
}
return string(bytes.begin(), bytes.end());
}
uint32_t hexToLong(const string& hexString) {
uint32_t value;
stringstream conversionStream;
conversionStream << hexString;
conversionStream >> hex >> value;
return value;
}
string trimNewLines(string str) {
return trimChar(str, '\n');
}
string trimQuotes(string str) {
return trimChar(str, '\"');
}
string trimChar(string str, char targetChar) {
str.erase(remove(str.begin(), str.end(), targetChar), str.end());
return str;
}
string trimWhitespaceFromLeft(string str) {
RE2 pattern("^\\s+");
while (RE2::PartialMatch(str, pattern)) {
str = str.erase(0, 1);
}
return str;
}
string trimWhitespaceFromRight(string str) {
RE2 pattern("\\s+$");
while (RE2::PartialMatch(str, pattern)) {
str = str.erase(str.length()-1, 1);
}
return str;
}
string trimWhitespaceFromBothEnds(string str) {
return trimWhitespaceFromRight(trimWhitespaceFromLeft(str));
}
vector<string> split(const string &str, char delim) {
vector<string> result;
stringstream ss(str);
string item;
while (getline(ss, item, delim)) {
result.push_back(item);
}
return result;
}
} // namespace string_utils
} // namespace hirs

View File

@ -1,191 +0,0 @@
#include <sapi/tpm20.h>
#include <tcti/tcti-tabrmd.h>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <memory>
#include <string>
using std::cout;
using std::endl;
using std::shared_ptr;
using std::string;
/**
* SapiContext is a class to encapsulate the TSS2_SYS_CONTEXT and its
* creation.
*/
class SapiContext {
/**
* The TCTI Context.
*/
TSS2_TCTI_CONTEXT *tctiContext = nullptr;
/**
* The SAPI Context.
*/
TSS2_SYS_CONTEXT *sapiContext = nullptr;
/**
* Private Constructor
*
* The constructor is private because the goal of the class is to manage
* the memory of the TSS2_TCTI_CONTEXT and TSS2_SYS_CONTEXT that are
* allocated, which are forced to be done through the static create()
* function. Since create() returns a shared_ptr<SapiContext>, there is no
* way to leak memory by forgetting to free anything created by this class.
* @param tcti_ctx the TCTI Context needed for creating the SAPI Context
* @param sapi_ctx the SAPI Context needed to interact with the TSS
*/
SapiContext(TSS2_TCTI_CONTEXT* tcti_ctx, TSS2_SYS_CONTEXT* sapi_ctx)
: tctiContext(tcti_ctx), sapiContext(sapi_ctx) {}
public:
/**
* Destructor.
*/
~SapiContext() {free(sapiContext); free(tctiContext);}
/**
* Factory function for creating SapiContext objects and guaranteeing
* that their memory will be freed by placing them into shared_ptr objects.
*
* @return a shared_ptr to a new SapiContext object
*/
static shared_ptr<SapiContext> create() {
size_t size;
TSS2_RC rc = tss2_tcti_tabrmd_init(nullptr, &size);
if (rc != TSS2_RC_SUCCESS) {
return nullptr;
}
TSS2_TCTI_CONTEXT *tContext
= reinterpret_cast<TSS2_TCTI_CONTEXT*>(calloc(1, size));
if (tContext == nullptr) {
return nullptr;
}
rc = tss2_tcti_tabrmd_init(tContext, &size);
if (rc != TSS2_RC_SUCCESS) {
free(tContext);
return nullptr;
}
size = Tss2_Sys_GetContextSize(0);
TSS2_SYS_CONTEXT *sContext
= reinterpret_cast<TSS2_SYS_CONTEXT*>(calloc(1, size));
if (sContext == nullptr) {
free(tContext);
return nullptr;
}
TSS2_ABI_VERSION abi_version = {
.tssCreator = TSSWG_INTEROP,
.tssFamily = TSS_SAPI_FIRST_FAMILY,
.tssLevel = TSS_SAPI_FIRST_LEVEL,
.tssVersion = TSS_SAPI_FIRST_VERSION,
};
rc = Tss2_Sys_Initialize(sContext, size, tContext, &abi_version);
if (rc != TSS2_RC_SUCCESS) {
free(sContext);
free(tContext);
return nullptr;
}
// To make sure the memory is not leaked, store the SapiContext in a
// shared_ptr to make sure the destructor gets called and no copies
// are made.
return shared_ptr<SapiContext>(
new SapiContext(tContext, sContext));
}
TSS2_SYS_CONTEXT * getPointer() {return sapiContext;}
};
/**
* Returns the 4-byte string represented by the bytes of value.
*
* Assumes the bytes of value are reversed.
* @param value the 4 bytes to be reversed and placed in the returned string
* @return the string represented by value
*/
string reversedStringValue(UINT32 value) {
string stringValue(sizeof value, 0);
std::memcpy(&stringValue[0], &value, stringValue.size()); // copy bytes in
// get rid of all contained null values
stringValue.erase(std::remove(stringValue.begin(), stringValue.end(), '\0'),
stringValue.end());
std::reverse(stringValue.begin(), stringValue.end()); // reverse the bytes
return stringValue;
}
/**
* The tpm_version application prints two lines to the terminal:
* 1.) The TPM Chip Version, and
* 2.) The TPM Manufacturer (4 character abbreviation)
*
* @return 0 if successful; 1 if not
*/
int main(void) {
TPMS_CAPABILITY_DATA capability_data;
TPMI_YES_NO more_data;
shared_ptr<SapiContext> sapiContext = SapiContext::create();
TSS2_RC rc;
do {
// The return code will be 4 bytes. The most significant byte
// tells what software layer is reposible for a non-successful
// attempt at executing the call. The other bytes provide the error
// type. If we get a retry warning, we don't care which layer is
// responsible, so we mask the lower 3 bytes and compare it to
// TSS2_RC_RETRY to see if we need to try again.
rc = Tss2_Sys_GetCapability(sapiContext->getPointer(), nullptr,
TPM_CAP_TPM_PROPERTIES,
PT_FIXED,
MAX_TPM_PROPERTIES,
&more_data,
&capability_data,
nullptr);
} while ((rc & 0xfff) == TPM_RC_RETRY);
// There is nothing we can do if an error occurred. The HIRS Client will
// know how to respond if this happens. No need to log anything.
if (rc != TSS2_RC_SUCCESS) {
return 1;
}
// We only need to collect the following data from the TPM.
string manufacturer;
string majorVersion;
float minorVersion = 0.0f;
for (size_t i = 0; i < capability_data.data.tpmProperties.count; ++i) {
TPMS_TAGGED_PROPERTY & p
= capability_data.data.tpmProperties.tpmProperty[i];
TPM_PT property = p.property;
// All data is in the form of a UINT32, even if it represents a string.
// For strings, the bytes are in the wrong endianness and are not
// null-terminated.
UINT32 value = p.value;
switch (property) {
case TPM_PT_FAMILY_INDICATOR:
majorVersion = reversedStringValue(value);
break;
case TPM_PT_REVISION:
// The minor version has two decimal places, but since it is
// stored as an integer, it is stored as 100 times its value.
minorVersion = value / 100.0f;
break;
case TPM_PT_MANUFACTURER:
manufacturer = reversedStringValue(value);
break;
}
}
cout << "Chip Version: " << majorVersion << "." << minorVersion << endl;
cout << "TPM Vendor ID: " << manufacturer << endl;
return 0;
}

View File

@ -1,51 +0,0 @@
# CMake file for managing TPM 2.0 Library Test Suite
include(CTest)
# Setup for GoogleTest
configure_file(../lib/GoogleTest.CMakeLists.txt.in ${CMAKE_BINARY_DIR}/lib/googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lib/googletest-download)
if(result)
message(FATAL "CMake step for GoogleTest failed: ${result}")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/lib/googletest-download )
if(result)
message(FATAL "Build step for GoogleTest failed: ${result}")
endif()
# Prevent overriding the parent project's compiler/linker
# settings on Windows
if(MSVC)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
endif()
# Add googletest directly to our build. This defines
# the gtest and gtest_main targets.
# Note the EXCLUDE_FROM_ALL here will prevent the test code from
# being linked into the executable.
add_subdirectory(${CMAKE_BINARY_DIR}/lib/googletest-src
${CMAKE_BINARY_DIR}/lib/googletest-build
EXCLUDE_FROM_ALL)
macro(register_test TEST_TARGET_NAME)
add_executable(${TEST_TARGET_NAME} ${TEST_TARGET_NAME}_Test.cpp)
target_link_libraries(${TEST_TARGET_NAME}
TPM2_PROVISIONER_LIBRARY
gtest gmock)
add_test(NAME ${TEST_TARGET_NAME}_Tests COMMAND $<TARGET_FILE:${TEST_TARGET_NAME}>)
add_custom_command(
TARGET ${TEST_TARGET_NAME}
COMMENT "Run ${TEST_TARGET_NAME} Tests"
POST_BUILD
COMMAND ${TEST_TARGET_NAME}
)
endmacro()
register_test(Process)
register_test(Properties)
register_test(RestfulClientProvisioner)
register_test(Tpm2ToolsUtils)
register_test(Utils)

View File

@ -1,117 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include "Process.h"
#include <iostream>
#include <sstream>
#include <string>
#include "log4cplus/configurator.h"
#include "gtest/gtest.h"
using hirs::utils::Process;
using std::string;
using std::stringstream;
namespace {
class ProcessTest : public :: testing::Test {
protected:
ProcessTest() {
// You can do set-up work for each test here.
log4cplus::initialize();
log4cplus::BasicConfigurator::doConfigure();
}
virtual ~ProcessTest() {
// You can do clean-up work that doesn't throw exceptions here.
}
virtual void SetUp() {
// Code here will be called immediately after the constructor (right
// before each test).
}
virtual void TearDown() {
// Code here will be called immediately after each test (right
// before the destructor).
}
};
TEST_F(ProcessTest, ProcessWorks) {
Process p("echo \"Hello World\"");
int retVal = p.run();
ASSERT_EQ(retVal, 0);
ASSERT_EQ("Hello World\n", p.getOutputString());
}
TEST_F(ProcessTest, ProcessTwoArgConstructorWorks) {
Process p("echo", "\"Hello World\"");
int retVal = p.run();
ASSERT_EQ(retVal, 0);
ASSERT_EQ("Hello World\n", p.getOutputString());
}
TEST_F(ProcessTest, ProcessFailsWithNonZeroReturnValue) {
Process p("ls", "isjlfidjsaij");
int retVal = p.run();
ASSERT_EQ(retVal, 2);
}
TEST_F(ProcessTest, NonExistentProcessFailsWithNonZeroReturnValue) {
Process p("isjlfidjsaij");
int retVal = p.run();
ASSERT_EQ(retVal, 127);
}
TEST_F(ProcessTest, NonExistentProcessFailsAndGivesErrorMessage) {
Process p("isjlfidjsaij", "ijijdfi");
stringstream expectedError;
expectedError << "Call to isjlfidjsaij returned 127" << std::endl
<< "Is isjlfidjsaij in your path?" << std::endl;
string expectedErrorString(expectedError.str());
stringstream errorStream;
int retVal = p.run(errorStream);
ASSERT_EQ(retVal, 127);
string receivedErrorString(errorStream.str());
ASSERT_EQ(receivedErrorString, expectedErrorString);
}
TEST_F(ProcessTest, SuccessfulProcessDoesNotProduceErrorMessage) {
Process p("echo", "\"Hello World\"");
stringstream errorStream;
int retVal = p.run(errorStream);
ASSERT_EQ(retVal, 0);
string receivedErrorString(errorStream.str());
ASSERT_TRUE(receivedErrorString.empty());
}
TEST_F(ProcessTest, ProcessIsRunningSuccessful) {
ASSERT_TRUE(Process::isRunning("Process"));
}
TEST_F(ProcessTest, ProcessIsRunningSuccessfulPathBased) {
ASSERT_TRUE(Process::isRunning("/opt/Process"));
}
TEST_F(ProcessTest, ProcessIsRunningFalse) {
ASSERT_FALSE(Process::isRunning("foobar"));
}
TEST_F(ProcessTest, ProcessIsRunningEmptyStringReturnsFalse) {
ASSERT_FALSE(Process::isRunning(""));
}
TEST_F(ProcessTest, ProcessIsRunningPreventCommandHijack) {
ASSERT_FALSE(Process::isRunning("foobar; echo blarg"));
}
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -1,120 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include "Properties.h"
#include <fstream>
#include <string>
#include "gtest/gtest.h"
#include "log4cplus/configurator.h"
#include "HirsRuntimeException.h"
#include "Utils.h"
using hirs::properties::Properties;
namespace {
class PropertiesTest : public ::testing::Test {
protected:
PropertiesTest() {
// You can do set-up work for each test here.
log4cplus::initialize();
log4cplus::BasicConfigurator::doConfigure();
}
virtual ~PropertiesTest() {
// You can do clean-up work that doesn't throw exceptions here.
}
virtual void SetUp() {
// Code here will be called immediately after the constructor (right
// before each test).
}
virtual void TearDown() {
// Code here will be called immediately after each test (right
// before the destructor).
}
};
const char kFileName[] = "/tmp/test_provisioner.properties";
const char kFileContents[] =
"# a comment here\nk1=v1\nk2=v2 #this is a value\nk3= v 3 \nk4=\n";
TEST_F(PropertiesTest, EmptyConstructor) {
Properties props;
EXPECT_THROW(props.get("k1"), hirs::exception::HirsRuntimeException);
}
TEST_F(PropertiesTest, SetAndGet) {
Properties props;
props.set("k1", "v1");
ASSERT_EQ(props.get("k1"), "v1");
}
TEST_F(PropertiesTest, SetTwiceAndGet) {
Properties props;
props.set("k1", "v1");
ASSERT_EQ(props.get("k1"), "v1");
props.set("k1", "v2");
ASSERT_EQ(props.get("k1"), "v2");
}
TEST_F(PropertiesTest, GetUsingDefault) {
Properties props;
ASSERT_EQ(props.get("k1", "v1"), "v1");
ASSERT_EQ(props.get("k1", ""), "");
EXPECT_THROW(props.get("k1"), hirs::exception::HirsRuntimeException);
}
TEST_F(PropertiesTest, LoadAndGet) {
if (hirs::file_utils::fileExists(kFileName)) {
remove(kFileName);
}
std::ofstream propFile(kFileName);
propFile << kFileContents;
propFile.close();
Properties props;
props.load(kFileName);
ASSERT_EQ(props.get("k1"), "v1");
ASSERT_EQ(props.get("k2"), "v2");
ASSERT_EQ(props.get("k3"), "v 3");
ASSERT_FALSE(props.isSet("k4"));
EXPECT_THROW(props.get("k4"), hirs::exception::HirsRuntimeException);
remove(kFileName);
}
TEST_F(PropertiesTest, IsSet) {
Properties props;
props.set("k1", "v1");
ASSERT_TRUE(props.isSet("k1"));
ASSERT_FALSE(props.isSet("k2"));
ASSERT_FALSE(props.isSet(""));
}
TEST_F(PropertiesTest, SetBlankKey) {
Properties props;
EXPECT_THROW(props.set(" ", "v1"), hirs::exception::HirsRuntimeException);
}
TEST_F(PropertiesTest, SetBlankValue) {
Properties props;
EXPECT_THROW(props.set("k1", " "), hirs::exception::HirsRuntimeException);
}
TEST_F(PropertiesTest, SetBlankKeyAndValue) {
Properties props;
EXPECT_THROW(props.set(" ", " "), hirs::exception::HirsRuntimeException);
}
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -1,54 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include "gtest/gtest.h"
#include "RestfulClientProvisioner.h"
namespace {
class RestfulClientProvisionerTest : public :: testing::Test {
protected:
RestfulClientProvisionerTest() {
// You can do set-up work for each test here.
kRestfulClientProvisioner =
new RestfulClientProvisioner("localhost", 8443);
}
virtual ~RestfulClientProvisionerTest() {
// You can do clean-up work that doesn't throw exceptions here.
delete kRestfulClientProvisioner;
kRestfulClientProvisioner = NULL;
}
virtual void SetUp() {
// Code here will be called immediately after the constructor (right
// before each test).
}
virtual void TearDown() {
// Code here will be called immediately after each test (right
// before the destructor).
}
// Objects declared here can be used by all tests in the test case for
// RestfulClientProvisioner.
static RestfulClientProvisioner* kRestfulClientProvisioner;
static const char kAcaTestAddress[];
};
RestfulClientProvisioner* RestfulClientProvisionerTest::
kRestfulClientProvisioner = NULL;
const char RestfulClientProvisionerTest::kAcaTestAddress[] = "localhost";
TEST_F(RestfulClientProvisionerTest, GetAcaAddress) {
ASSERT_STREQ(kAcaTestAddress,
kRestfulClientProvisioner->getAcaAddress().c_str());
}
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -1,348 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include "gtest/gtest.h"
#include "Tpm2ToolsUtils.h"
using std::string;
using std::stringstream;
using hirs::tpm2_tools_utils::Tpm2ToolsOutputParser;
namespace {
class Tpm2ToolsUtilsTest : public :: testing::Test {
protected:
Tpm2ToolsUtilsTest() {
// You can do set-up work for each test here.
}
virtual ~Tpm2ToolsUtilsTest() {
// You can do clean-up work that doesn't throw exceptions here.
}
virtual void SetUp() {
// Code here will be called immediately after the constructor (right
// before each test).
}
virtual void TearDown() {
// Code here will be called immediately after each test (right
// before the destructor).
}
};
TEST_F(Tpm2ToolsUtilsTest, ParseNvDataSizeSuccess) {
stringstream nvListOutput;
nvListOutput << "2 NV indexes defined.\n"
<< "\n"
<< " 0. NV Index: 0x1800001\n"
<< " {\n"
<< "\tHash algorithm(nameAlg):11\n"
<< "\tThe Index attributes(attributes):0x62042c04\n"
<< "\tThe size of the data area(dataSize):70\n"
<< " }\n"
<< "\n"
<< " 1. NV Index: 0x1c00002\n"
<< " {\n"
<< "\tHash algorithm(nameAlg):11\n"
<< "\tThe Index attributes(attributes):0x620f1001\n"
<< "\tThe size of the data area(dataSize):991\n"
<< " }\n";
uint16_t addressSize = Tpm2ToolsOutputParser::parseNvDataSize(
"0x1c00002", nvListOutput.str());
ASSERT_EQ(991, addressSize);
}
TEST_F(Tpm2ToolsUtilsTest, ParseNvDataSizeSuccessTpm2ToolsV3) {
stringstream nvListOutput;
nvListOutput << "0x1c00002\n"
<< "\thash algorithm:\n"
<< "\t\tfriendly: sha256\n"
<< "\t\tvalue: 0xB\n"
<< "\tattributes:\n"
<< "\t\tfriendly: ownerwrite|policywrite\n"
<< "\t\tvalue: 0xA000220\n"
<< "\tsize: 991\n\n"
<< "0x1c00003\n"
<< "\thash algorithm:\n"
<< "\t\tfriendly: sha256\n"
<< "\t\tvalue: 0xB\n"
<< "\tattributes:\n"
<< "\t\tfriendly: ownerwrite|policywrite\n"
<< "\t\tvalue: 0xA000220\n"
<< "\tsize: 1722\n\n";
uint16_t addressSize = Tpm2ToolsOutputParser::parseNvDataSize(
"0x1c00002", nvListOutput.str());
ASSERT_EQ(991, addressSize);
}
TEST_F(Tpm2ToolsUtilsTest, ParseNvDataSizeFailure) {
stringstream nvListOutput;
nvListOutput << "0 NV indexes defined.\n";
uint16_t addressSize = Tpm2ToolsOutputParser::parseNvDataSize(
"0x1c00002", nvListOutput.str());
ASSERT_EQ(0, addressSize);
}
TEST_F(Tpm2ToolsUtilsTest, ParseNvReadSuccess) {
stringstream nvReadOutput;
nvReadOutput << "The size of data:10\n"
<< " 30 7f 03 6d 30 7f 03 7e 3c 03";
string nvReadData = Tpm2ToolsOutputParser::parseNvReadOutput(
nvReadOutput.str());
string expectedOutput = {48, 127, 3, 109, 48, 127, 3, 126, 60, 3};
ASSERT_EQ(expectedOutput, nvReadData);
}
TEST_F(Tpm2ToolsUtilsTest, ParseNvReadFailure) {
stringstream nvReadOutput;
nvReadOutput << "Failed to read NVRAM area at index 0x1c00001 "
<< "(29360129).Error:0x28b\n";
string nvReadData = Tpm2ToolsOutputParser::parseNvReadOutput(
nvReadOutput.str());
ASSERT_EQ("", nvReadData);
}
TEST_F(Tpm2ToolsUtilsTest, ParsePersistentObjectExistsSuccess) {
stringstream listPersistentOutput;
listPersistentOutput << "1 persistent objects defined.\n"
<< "\n"
<< " 0. Persistent handle: 0x81010001\n"
<< " {\n"
<< "\tType: 0x1\n"
<< "\tHash algorithm(nameAlg): 0xb\n"
<< "\tAttributes: 0x300b2\n"
<< " }\n";
ASSERT_TRUE(Tpm2ToolsOutputParser::parsePersistentObjectExists(
"0x81010001", listPersistentOutput.str()));
}
TEST_F(Tpm2ToolsUtilsTest, ParsePersistentObjectExistsSuccessTpm2ToolsV3) {
stringstream listPersistentOutput;
listPersistentOutput << "persistent-handle[0]:0x81010001 "
<< "key-alg:rsa hash-alg:sha256 "
<< "object-attr:fixedtpm|fixedparent";
ASSERT_TRUE(Tpm2ToolsOutputParser::parsePersistentObjectExists(
"0x81010001", listPersistentOutput.str()));
}
TEST_F(Tpm2ToolsUtilsTest, ParsePersistentObjectExistsFailure) {
stringstream listPersistentOutput;
listPersistentOutput << "0 persistent objects defined.\n";
ASSERT_FALSE(Tpm2ToolsOutputParser::parsePersistentObjectExists(
"0x81010001", listPersistentOutput.str()));
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpmErrorCodeSuccessAnyCharBetweenErrorAndCode) {
stringstream errorOutput;
errorOutput << "Create Object Failed ! ErrorCode: 0x922";
string expectedOutput = "0x922";
string errorCode = Tpm2ToolsOutputParser::parseTpmErrorCode(
errorOutput.str());
ASSERT_EQ(expectedOutput, errorCode);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpmErrorCodeSuccessHexChars) {
stringstream errorOutput;
errorOutput << "Failed to read NVRAM area at index 0x1c00003"
<< " (29360131).Error:0x28b";
string expectedOutput = "0x28b";
string errorCode = Tpm2ToolsOutputParser::parseTpmErrorCode(
errorOutput.str());
ASSERT_EQ(expectedOutput, errorCode);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpmErrorCodeSuccessFirstThreeHex) {
stringstream errorOutput;
errorOutput << "Failed to read NVRAM area at index 0x1c00003"
<< " (29360131).Error:0x28b90210";
string expectedOutput = "0x28b";
string errorCode = Tpm2ToolsOutputParser::parseTpmErrorCode(
errorOutput.str());
ASSERT_EQ(expectedOutput, errorCode);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpmErrorCodeSuccessMultiline) {
stringstream errorOutput;
errorOutput << "Failed to read NVRAM area at index 0x1c00003"
<< " (29360131).Error:\n\n0x28b";
string expectedOutput = "0x28b";
string errorCode = Tpm2ToolsOutputParser::parseTpmErrorCode(
errorOutput.str());
ASSERT_EQ(expectedOutput, errorCode);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpmErrorCodeSuccessCapitalHex) {
stringstream errorOutput;
errorOutput << "Failed to read NVRAM area at index 0x1c00003"
<< " (29360131).Error:\n\n0x28B";
string expectedOutput = "0x28B";
string errorCode = Tpm2ToolsOutputParser::parseTpmErrorCode(
errorOutput.str());
ASSERT_EQ(expectedOutput, errorCode);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpmErrorCodeFailNonHex) {
stringstream errorOutput;
errorOutput << "Failed to read NVRAM area at index 0x1c00003"
<< " (29360131).Error:0x28g";
string expectedOutput = "";
string errorCode = Tpm2ToolsOutputParser::parseTpmErrorCode(
errorOutput.str());
ASSERT_EQ(expectedOutput, errorCode);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpmErrorCodeFailNonHexFormatted) {
stringstream errorOutput;
errorOutput << "Failed to read NVRAM area at index 0x1c00003"
<< " (29360131).Error:28b";
string expectedOutput = "";
string errorCode = Tpm2ToolsOutputParser::parseTpmErrorCode(
errorOutput.str());
ASSERT_EQ(expectedOutput, errorCode);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpmErrorCodeFailNotErrorCode) {
stringstream errorOutput;
errorOutput << "Easter Egg to be found at memory address: 0x042";
string expectedOutput = "";
string errorCode = Tpm2ToolsOutputParser::parseTpmErrorCode(
errorOutput.str());
ASSERT_EQ(expectedOutput, errorCode);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpm2ToolsVersionSuccess) {
stringstream versionOutput;
versionOutput << R"(tool="tpm2_rc_decode" version="3.0.1")"
<< R"(tctis="tabrmd,socket,device,")";
string expectedOutput = "3.0.1";
string version = Tpm2ToolsOutputParser::parseTpm2ToolsVersion(
versionOutput.str());
ASSERT_EQ(expectedOutput, version);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpm2ToolsVersionSuccessCaseInsensitive) {
stringstream versionOutput;
versionOutput << R"(tool="tpm2_rc_decode" VeRSion="3.0.1")"
<< R"(tctis="tabrmd,socket,device,")";
string expectedOutput = "3.0.1";
string version = Tpm2ToolsOutputParser::parseTpm2ToolsVersion(
versionOutput.str());
ASSERT_EQ(expectedOutput, version);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpm2ToolsVersionSuccessWhitespace) {
stringstream versionOutput;
versionOutput << "tpm2_rc_decode, version 1.1.0";
string expectedOutput = "1.1.0";
string version = Tpm2ToolsOutputParser::parseTpm2ToolsVersion(
versionOutput.str());
ASSERT_EQ(expectedOutput, version);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpm2ToolsVersionSuccessMultiNumeralVersion) {
stringstream versionOutput;
versionOutput << "tpm2_rc_decode, version 10.29.970";
string expectedOutput = "10.29.970";
string version = Tpm2ToolsOutputParser::parseTpm2ToolsVersion(
versionOutput.str());
ASSERT_EQ(expectedOutput, version);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpm2ToolsVersionSuccessAnyCharsBeforeVersion) {
stringstream versionOutput;
versionOutput << "tpm2_rc_decode, version!@#$%^&*()+=-_|1.2.9";
string expectedOutput = "1.2.9";
string version = Tpm2ToolsOutputParser::parseTpm2ToolsVersion(
versionOutput.str());
ASSERT_EQ(expectedOutput, version);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpm2ToolsVersionFailNonSemanticVersion) {
stringstream versionOutput;
versionOutput << "tpm2_rc_decode, version 1.2";
string expectedOutput = "";
string version = Tpm2ToolsOutputParser::parseTpm2ToolsVersion(
versionOutput.str());
ASSERT_EQ(expectedOutput, version);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpm2ToolsMajorVersionSuccess) {
stringstream versionOutput;
versionOutput << "3.0.1";
string expectedOutput = "3";
string majorVersion = Tpm2ToolsOutputParser::parseTpm2ToolsMajorVersion(
versionOutput.str());
ASSERT_EQ(expectedOutput, majorVersion);
}
TEST_F(Tpm2ToolsUtilsTest,
ParseTpm2ToolsMajorVersionSuccessMultiNumeralVersion) {
stringstream versionOutput;
versionOutput << "10.29.970";
string expectedOutput = "10";
string majorVersion = Tpm2ToolsOutputParser::parseTpm2ToolsMajorVersion(
versionOutput.str());
ASSERT_EQ(expectedOutput, majorVersion);
}
TEST_F(Tpm2ToolsUtilsTest, ParseTpm2ToolsMajorVersionFailNonSemanticVersion) {
stringstream versionOutput;
versionOutput << "3.0";
string expectedOutput = "";
string majorVersion = Tpm2ToolsOutputParser::parseTpm2ToolsMajorVersion(
versionOutput.str());
ASSERT_EQ(expectedOutput, majorVersion);
}
TEST_F(Tpm2ToolsUtilsTest,
ParseTpm2ToolsMajorVersionFailLongNonSemanticVersion) {
stringstream versionOutput;
versionOutput << "3.0.1.27";
string expectedOutput = "";
string majorVersion = Tpm2ToolsOutputParser::parseTpm2ToolsMajorVersion(
versionOutput.str());
ASSERT_EQ(expectedOutput, majorVersion);
}
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -1,356 +0,0 @@
/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include "gtest/gtest.h"
#include "Utils.h"
using hirs::file_utils::dirExists;
using hirs::file_utils::fileExists;
using hirs::file_utils::trimFilenameFromPath;
using hirs::json_utils::JSONFieldParser;
using hirs::string_utils::binaryToHex;
using hirs::string_utils::contains;
using hirs::string_utils::longToHex;
using hirs::string_utils::isHexString;
using hirs::string_utils::hexToBytes;
using hirs::string_utils::hexToLong;
using hirs::string_utils::trimNewLines;
using hirs::string_utils::trimQuotes;
using hirs::string_utils::trimChar;
using hirs::string_utils::trimWhitespaceFromLeft;
using hirs::string_utils::trimWhitespaceFromRight;
using hirs::string_utils::trimWhitespaceFromBothEnds;
using std::ofstream;
using std::string;
using std::stringstream;
namespace {
class UtilsTest : public :: testing::Test {
protected:
UtilsTest() {
// You can do set-up work for each test here.
}
virtual ~UtilsTest() {
// You can do clean-up work that doesn't throw exceptions here.
}
virtual void SetUp() {
// Code here will be called immediately after the constructor (right
// before each test).
}
virtual void TearDown() {
// Code here will be called immediately after each test (right
// before the destructor).
}
// Objects declared here can be used by all tests in the test case for
// Utils.
static const char kFileName[];
};
const char UtilsTest::kFileName[] = "bitsAndBytes";
TEST_F(UtilsTest, ParseJsonFieldSuccess) {
stringstream jsonObject;
jsonObject << R"({"error":"identityClaim cannot be null or empty"})";
string errorMessage = JSONFieldParser::parseJsonStringField(
jsonObject.str(), "error");
string expectedOutput = "identityClaim cannot be null or empty";
ASSERT_EQ(expectedOutput, errorMessage);
}
TEST_F(UtilsTest, ParseJsonFieldSuccessCaseInsensitive) {
stringstream jsonObject;
jsonObject << R"({"ERROR":"identityClaim cannot be null or empty"})";
string errorMessage = JSONFieldParser::parseJsonStringField(
jsonObject.str(), "error");
string expectedOutput = "identityClaim cannot be null or empty";
ASSERT_EQ(expectedOutput, errorMessage);
}
TEST_F(UtilsTest, ParseJsonFieldSuccessWhiteSpaces) {
stringstream jsonObject;
jsonObject << R"({"error" : "identityClaim cannot be null or empty"})";
string errorMessage = JSONFieldParser::parseJsonStringField(
jsonObject.str(), "error");
string expectedOutput = "identityClaim cannot be null or empty";
ASSERT_EQ(expectedOutput, errorMessage);
}
TEST_F(UtilsTest, ParseJsonFieldSuccessMultiJsonFields) {
stringstream jsonObject;
jsonObject << R"({"error" : "identityClaim cannot be null or empty",)"
<< "\n" << R"("endpoint":"url.com"})";
string errorMessage = JSONFieldParser::parseJsonStringField(
jsonObject.str(), "error");
string expectedOutput = "identityClaim cannot be null or empty";
ASSERT_EQ(expectedOutput, errorMessage);
}
TEST_F(UtilsTest, ParseJsonFieldInvalidJson) {
stringstream jsonObject;
jsonObject << R"({error:"identityClaim cannot be null or empty"})";
string errorMessage = JSONFieldParser::parseJsonStringField(
jsonObject.str(), "error");
string expectedOutput = "";
ASSERT_EQ(expectedOutput, errorMessage);
}
TEST_F(UtilsTest, DirectoryExists) {
mkdir(kFileName, 0755);
ASSERT_TRUE(dirExists(kFileName));
rmdir(kFileName);
}
TEST_F(UtilsTest, DirectoryDoesNotExist) {
ASSERT_FALSE(dirExists(kFileName));
}
TEST_F(UtilsTest, FileExists) {
ofstream outputFile;
outputFile.open(kFileName);
outputFile.close();
ASSERT_TRUE(fileExists(kFileName));
remove(kFileName);
}
TEST_F(UtilsTest, FileDoesNotExist) {
ASSERT_FALSE(fileExists(kFileName));
}
TEST_F(UtilsTest, FileSize) {
string test = "Hello World";
hirs::file_utils::writeBinaryFile(test, "testfile");
int size = hirs::file_utils::getFileSize("testfile");
ASSERT_EQ(size, 11);
}
TEST_F(UtilsTest, SplitFile) {
string test = "Hello World";
hirs::file_utils::writeBinaryFile(test, "testfile");
hirs::file_utils::splitFile("testfile", "testfilep1", 0, 5);
string s = hirs::file_utils::fileToString("testfilep1");
ASSERT_EQ(s, "Hello");
hirs::file_utils::splitFile("testfile", "testfilep2", 5, 5);
s = hirs::file_utils::fileToString("testfilep2");
ASSERT_EQ(s, " Worl");
hirs::file_utils::splitFile("testfile", "testfilep3", 10, 1);
s = hirs::file_utils::fileToString("testfilep3");
ASSERT_EQ(s, "d");
}
TEST_F(UtilsTest, TrimFilenameFromAbsolutePathSuccess) {
string test = "/usr/bin/echo";
ASSERT_EQ("echo", trimFilenameFromPath(test));
}
TEST_F(UtilsTest, TrimFilenameFromRelativePathSuccess) {
string test = "usr/bin/echo";
ASSERT_EQ("echo", trimFilenameFromPath(test));
}
TEST_F(UtilsTest, TrimFilenameFromPathSuccessWhenJustFilename) {
string test = "echo";
ASSERT_EQ("echo", trimFilenameFromPath(test));
}
TEST_F(UtilsTest, TrimEmptyFilenameFromPathSuccess) {
string test = "/usr/bin/";
ASSERT_EQ("", trimFilenameFromPath(test));
}
TEST_F(UtilsTest, BinToHex) {
const char* testBin = "j\223\255x\216=\330c\aaj\262@\343i\246?\204T5";
ASSERT_EQ(binaryToHex(testBin),
"6a93ad788e3dd86307616ab240e369a63f845435");
}
TEST_F(UtilsTest, Contains) {
string teststr = "The more you know";
string substr = "more you";
ASSERT_TRUE(contains(teststr, substr));
}
TEST_F(UtilsTest, ContainsSelf) {
string teststr = "The more you know";
string substr = "The more you know";
ASSERT_TRUE(contains(teststr, substr));
}
TEST_F(UtilsTest, DoesNotContain) {
string teststr = "The more you know";
string substr = "moor";
ASSERT_FALSE(contains(teststr, substr));
}
TEST_F(UtilsTest, DoesNotContainMoreThanSelf) {
string teststr = "The more you know";
string substr = "The more you know.";
ASSERT_FALSE(contains(teststr, substr));
}
TEST_F(UtilsTest, LongToHex) {
const uint32_t testValue = 464367618;
ASSERT_EQ(longToHex(testValue), "0x1badb002");
}
TEST_F(UtilsTest, LongToHexZero) {
const uint32_t testValue = 0;
ASSERT_EQ(longToHex(testValue), "0x0");
}
TEST_F(UtilsTest, LongToHexUnderflow) {
const uint32_t testValue = -1;
ASSERT_EQ(longToHex(testValue), "0xffffffff");
}
TEST_F(UtilsTest, LongToHexOverflow) {
uint32_t testValue = 0xffffffff + 1;
ASSERT_EQ(longToHex(testValue), "0x0");
}
TEST_F(UtilsTest, IsHexStringEmpty) {
string testStr = "";
ASSERT_FALSE(isHexString(testStr));
}
TEST_F(UtilsTest, IsHexStringTrue) {
string testStr = "8BADF00D";
ASSERT_TRUE(isHexString(testStr));
}
TEST_F(UtilsTest, IsHexStringPrefixTrue) {
string testStr = "0x8BADF00D";
ASSERT_TRUE(isHexString(testStr));
}
TEST_F(UtilsTest, IsHexStringFalse) {
string testStr = "G00DF00D";
ASSERT_FALSE(isHexString(testStr));
}
TEST_F(UtilsTest, IsHexStringFalseWithSpaces) {
string testStr = "8BAD F00D";
ASSERT_FALSE(isHexString(testStr));
}
TEST_F(UtilsTest, HexToBytesEmptyString) {
string testStr = "";
ASSERT_TRUE(hexToBytes(testStr).empty());
}
TEST_F(UtilsTest, HexToBytesNotHex) {
string testStr = "A study in mopishness";
ASSERT_TRUE(hexToBytes(testStr).empty());
}
TEST_F(UtilsTest, HexToBytesNotEven) {
string testStr = "8BADF00";
ASSERT_TRUE(hexToBytes(testStr).empty());
}
TEST_F(UtilsTest, HexToBytesSuccess) {
// ASCII bytes for "TWO$"
string testBytes = {84, 87, 79, 36};
// Hex encoding of "TWO$"
string testStr = "54574F24";
ASSERT_EQ(testBytes, hexToBytes(testStr));
}
TEST_F(UtilsTest, HexToLong) {
string testStr = "BADF00D";
ASSERT_EQ(hexToLong(testStr), 195948557);
}
TEST_F(UtilsTest, HexWithPrefixToLong) {
string testStr = "0xBADF00D";
ASSERT_EQ(hexToLong(testStr), 195948557);
}
TEST_F(UtilsTest, HexToLongNotHex) {
string testStr = "G00DF00D";
ASSERT_EQ(hexToLong(testStr), 0);
}
TEST_F(UtilsTest, TrimNewLines) {
string test = "abc\ndef\nghi\n";
ASSERT_EQ(trimNewLines(test),
"abcdefghi");
}
TEST_F(UtilsTest, TrimQuotes) {
string test = "abc\"def\"ghi\"";
ASSERT_EQ(trimQuotes(test),
"abcdefghi");
}
TEST_F(UtilsTest, TrimChar) {
string test = "abc@def@ghi@";
ASSERT_EQ(trimChar(test, '@'),
"abcdefghi");
}
TEST_F(UtilsTest, trimWhitespaceFromLeft) {
ASSERT_EQ(trimWhitespaceFromLeft(" asdf"), "asdf");
ASSERT_EQ(trimWhitespaceFromLeft(" as df"), "as df");
ASSERT_EQ(trimWhitespaceFromLeft("\tas df"), "as df");
ASSERT_EQ(trimWhitespaceFromLeft("\t\ras\rdf"), "as\rdf");
ASSERT_EQ(trimWhitespaceFromLeft("asdf "), "asdf ");
ASSERT_EQ(trimWhitespaceFromLeft("asdf"), "asdf");
ASSERT_EQ(trimWhitespaceFromLeft(" "), "");
ASSERT_EQ(trimWhitespaceFromLeft(""), "");
}
TEST_F(UtilsTest, trimWhitespaceFromRight) {
ASSERT_EQ(trimWhitespaceFromRight("asdf "), "asdf");
ASSERT_EQ(trimWhitespaceFromRight("as df "), "as df");
ASSERT_EQ(trimWhitespaceFromRight("as df\t"), "as df");
ASSERT_EQ(trimWhitespaceFromRight("as\rdf\t\r"), "as\rdf");
ASSERT_EQ(trimWhitespaceFromRight(" asdf"), " asdf");
ASSERT_EQ(trimWhitespaceFromRight("asdf"), "asdf");
ASSERT_EQ(trimWhitespaceFromRight(" "), "");
ASSERT_EQ(trimWhitespaceFromRight(""), "");
}
TEST_F(UtilsTest, trimWhitespaceFromBoth) {
ASSERT_EQ(trimWhitespaceFromBothEnds(" asdf "), "asdf");
ASSERT_EQ(trimWhitespaceFromBothEnds(" as df "), "as df");
ASSERT_EQ(trimWhitespaceFromBothEnds("\tas df\t"), "as df");
ASSERT_EQ(trimWhitespaceFromBothEnds("\t\ras\rdf\t\r"), "as\rdf");
ASSERT_EQ(trimWhitespaceFromBothEnds("asdf"), "asdf");
ASSERT_EQ(trimWhitespaceFromBothEnds(" "), "");
ASSERT_EQ(trimWhitespaceFromBothEnds(""), "");
ASSERT_EQ(trimWhitespaceFromBothEnds("asdf "), "asdf");
ASSERT_EQ(trimWhitespaceFromBothEnds("as df "), "as df");
ASSERT_EQ(trimWhitespaceFromBothEnds("as df\t"), "as df");
ASSERT_EQ(trimWhitespaceFromBothEnds("as\rdf\t\r"), "as\rdf");
ASSERT_EQ(trimWhitespaceFromBothEnds(" asdf"), "asdf");
ASSERT_EQ(trimWhitespaceFromBothEnds(" as df"), "as df");
ASSERT_EQ(trimWhitespaceFromBothEnds("\tas df"), "as df");
ASSERT_EQ(trimWhitespaceFromBothEnds("\t\ras\rdf"), "as\rdf");
}
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -1,4 +0,0 @@
Vagrant.configure("2") do |config|
config.vm.box = "centos-7.4-tpm2tools-v1.1.0"
config.vm.box_url = "https://rpm:reason-joined-rock@forge.outer.jhuapl.edu/rpm/vagrant-boxes/centos-7.4-x86_64-tpm2-emulator-tpm2-tools_1_1_0_20180308.box"
end

View File

@ -1,4 +0,0 @@
Vagrant.configure("2") do |config|
config.vm.box = "centos-7.4-tpm2tools-v2.1.0"
config.vm.box_url = "https://rpm:reason-joined-rock@forge.outer.jhuapl.edu/rpm/vagrant-boxes/centos-7.4-x86_64-tpm2-emulator-tpm2-tools_2_1_0_20180308.box"
end

View File

@ -7,10 +7,10 @@
dir=$(pwd) dir=$(pwd)
# Relative paths are different when building locally versus on CI # Relative paths are different when building locally versus on CI
if [[ "$dir" == *"package"* ]]; then if [[ "$dir" == *"package"* ]]; then
SRC_DIR=$dir/../../../../../../HIRS_ProvisionerTPM2/src SRC_DIR=$dir/../../../../../../HIRS_Provisioner.NET/hirs/Resources
DEST_DIR=$dir/../src/main/java DEST_DIR=$dir/../src/main/java
else else
SRC_DIR=../../HIRS_ProvisionerTPM2/src SRC_DIR=../../HIRS_Provisioner.NET/hirs/Resources
DEST_DIR=../src/main/java DEST_DIR=../src/main/java
fi fi
protoc -I=$SRC_DIR --java_out=$DEST_DIR $SRC_DIR/ProvisionerTpm2.proto protoc -I=$SRC_DIR --java_out=$DEST_DIR $SRC_DIR/ProvisionerTpm2.proto