From e9f5d7245c67921675710bd6f5166a04e009d58d Mon Sep 17 00:00:00 2001
From: iadgovuser62 <145499407+iadgovuser62@users.noreply.github.com>
Date: Mon, 22 Jan 2024 16:18:01 -0500
Subject: [PATCH] Xfer HIRS_Provisioner.NET to main (#663)
* Moving HIRS_Provisioner.NET and dotnet_provisioner_unit_tests.yml into main branch
* Adding fixed dotnet provisioner workflow
* Updated files to match updated master branch, including hirs.sln, Directory.Build.targets, HIRS_Provisioner.NET.csproj, and hirsTest.csproj
---
.../dotnet_provisioner_unit_tests.yml | 136 +++++
HIRS_Provisioner.NET/.editorconfig | 201 +++++++
HIRS_Provisioner.NET/hirs.sln | 158 +++++
.../hirs/Directory.Build.targets | 44 ++
.../hirs/HIRS_Provisioner.NET.csproj | 102 ++++
.../hirs/Resources/Product.wxs | 35 ++
.../hirs/Resources/ProvisionerTpm2.proto | 100 ++++
HIRS_Provisioner.NET/hirs/appsettings.json | 38 ++
HIRS_Provisioner.NET/hirs/src/Program.cs | 69 +++
.../hirs/src/client/Client.cs | 130 +++++
.../hirs/src/client/IHirsAcaClient.cs | 53 ++
.../hirs/src/client/TbsWrapper.cs | 179 ++++++
HIRS_Provisioner.NET/hirs/src/config/CLI.cs | 39 ++
.../hirs/src/config/ClientExitCodes.cs | 18 +
.../hirs/src/config/Settings.cs | 547 ++++++++++++++++++
.../deviceInfo/ClassicDeviceInfoCollector.cs | 250 ++++++++
.../deviceInfo/IHirsDeviceInfoCollector.cs | 10 +
.../hirs/src/provisioner/IHirsProvisioner.cs | 15 +
.../hirs/src/provisioner/Provisioner.cs | 296 ++++++++++
.../hirs/src/tpm/CommandTpm.cs | 497 ++++++++++++++++
.../hirs/src/tpm/CommandTpmQuoteResponse.cs | 30 +
.../hirs/src/tpm/IHirsAcaTpm.cs | 18 +
.../test/settings_test/appsettings.json | 37 ++
.../test/settings_test/component_list | 1 +
.../efi/boot/tcg/cert/Mfg.Serial.BASE.cer | 1 +
.../cert/platform/Mfg.Serial.ver2.base.cer | 1 +
.../boot/tcg/manifest/rim/Mfg.Model.1.rimel | 1 +
.../boot/tcg/manifest/rim/Mfg.Model.1.rimpcr | 1 +
.../tcg/manifest/swidtag/Mfg.Model.1.swidtag | 1 +
.../efi/boot/tcg/other/notread.base.pem | 0
.../efi/boot/tcg/other/notread.delta.cer | 0
.../efi/boot/tcg/other/notread.rimel | 0
.../efi/boot/tcg/other/notread.rimpcr | 0
.../efi/boot/tcg/other/notread.swidtag | 0
.../boot/tcg/pccert/Mfg.Model.ver1.delta.pem | 1 +
.../Resources/test/settings_test/event_log | 1 +
HIRS_Provisioner.NET/hirsTest/hirsTest.csproj | 38 ++
.../hirsTest/src/client/ClientTests.cs | 53 ++
.../hirsTest/src/config/SettingsTests.cs | 59 ++
.../src/provisioner/ProvisionerTests.cs | 123 ++++
.../tools/pcrextend/pcrextend.csproj | 16 +
.../tools/pcrextend/src/Program.cs | 42 ++
.../tools/pcrextend/src/config/CLI.cs | 29 +
.../tools/pcrextend/src/tool/PcrExtendTool.cs | 55 ++
.../pcrextend/src/tpm/CommandTpmSimulator.cs | 69 +++
45 files changed, 3494 insertions(+)
create mode 100644 .github/workflows/dotnet_provisioner_unit_tests.yml
create mode 100644 HIRS_Provisioner.NET/.editorconfig
create mode 100644 HIRS_Provisioner.NET/hirs.sln
create mode 100644 HIRS_Provisioner.NET/hirs/Directory.Build.targets
create mode 100644 HIRS_Provisioner.NET/hirs/HIRS_Provisioner.NET.csproj
create mode 100644 HIRS_Provisioner.NET/hirs/Resources/Product.wxs
create mode 100644 HIRS_Provisioner.NET/hirs/Resources/ProvisionerTpm2.proto
create mode 100644 HIRS_Provisioner.NET/hirs/appsettings.json
create mode 100644 HIRS_Provisioner.NET/hirs/src/Program.cs
create mode 100644 HIRS_Provisioner.NET/hirs/src/client/Client.cs
create mode 100644 HIRS_Provisioner.NET/hirs/src/client/IHirsAcaClient.cs
create mode 100644 HIRS_Provisioner.NET/hirs/src/client/TbsWrapper.cs
create mode 100644 HIRS_Provisioner.NET/hirs/src/config/CLI.cs
create mode 100644 HIRS_Provisioner.NET/hirs/src/config/ClientExitCodes.cs
create mode 100644 HIRS_Provisioner.NET/hirs/src/config/Settings.cs
create mode 100644 HIRS_Provisioner.NET/hirs/src/deviceInfo/ClassicDeviceInfoCollector.cs
create mode 100644 HIRS_Provisioner.NET/hirs/src/deviceInfo/IHirsDeviceInfoCollector.cs
create mode 100644 HIRS_Provisioner.NET/hirs/src/provisioner/IHirsProvisioner.cs
create mode 100644 HIRS_Provisioner.NET/hirs/src/provisioner/Provisioner.cs
create mode 100644 HIRS_Provisioner.NET/hirs/src/tpm/CommandTpm.cs
create mode 100644 HIRS_Provisioner.NET/hirs/src/tpm/CommandTpmQuoteResponse.cs
create mode 100644 HIRS_Provisioner.NET/hirs/src/tpm/IHirsAcaTpm.cs
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/appsettings.json
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/component_list
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/cert/Mfg.Serial.BASE.cer
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/cert/platform/Mfg.Serial.ver2.base.cer
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/manifest/rim/Mfg.Model.1.rimel
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/manifest/rim/Mfg.Model.1.rimpcr
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/manifest/swidtag/Mfg.Model.1.swidtag
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.base.pem
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.delta.cer
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.rimel
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.rimpcr
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.swidtag
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/pccert/Mfg.Model.ver1.delta.pem
create mode 100644 HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/event_log
create mode 100644 HIRS_Provisioner.NET/hirsTest/hirsTest.csproj
create mode 100644 HIRS_Provisioner.NET/hirsTest/src/client/ClientTests.cs
create mode 100644 HIRS_Provisioner.NET/hirsTest/src/config/SettingsTests.cs
create mode 100644 HIRS_Provisioner.NET/hirsTest/src/provisioner/ProvisionerTests.cs
create mode 100644 HIRS_Provisioner.NET/tools/pcrextend/pcrextend.csproj
create mode 100644 HIRS_Provisioner.NET/tools/pcrextend/src/Program.cs
create mode 100644 HIRS_Provisioner.NET/tools/pcrextend/src/config/CLI.cs
create mode 100644 HIRS_Provisioner.NET/tools/pcrextend/src/tool/PcrExtendTool.cs
create mode 100644 HIRS_Provisioner.NET/tools/pcrextend/src/tpm/CommandTpmSimulator.cs
diff --git a/.github/workflows/dotnet_provisioner_unit_tests.yml b/.github/workflows/dotnet_provisioner_unit_tests.yml
new file mode 100644
index 00000000..0149f7a3
--- /dev/null
+++ b/.github/workflows/dotnet_provisioner_unit_tests.yml
@@ -0,0 +1,136 @@
+name: Dotnet Provisioner Unit Tests
+
+on: push
+env:
+ DOTNET_VERSION: '6.0'
+jobs:
+ dotnet_provisioner_unit_tests:
+ name: Restore and Run Unit Tests
+ continue-on-error: true
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ include:
+ - os: windows-2022
+ - os: ubuntu-20.04
+ # - os: windows-2019 Cannot Target windows-2019 because the .NET 6 SDK won't receive security patches for this image
+ steps:
+ - name: Set git to use LF
+ run: |
+ git config --global core.autocrlf false
+ git config --global core.eol lf
+
+ - name: Checkout repo
+ uses: actions/checkout@v3
+ with:
+ submodules: recursive
+
+ - name: Restore Project
+ working-directory: HIRS_Provisioner.NET
+ run: |
+ dotnet restore
+
+ - name: Build on Windows
+ working-directory: HIRS_Provisioner.NET
+ if: contains(matrix.os, 'windows')
+ run: |
+ cd hirs
+ dotnet build -r win-x64 --configuration Release --no-restore
+ cd ..
+
+ - name: Build on Ubuntu
+ working-directory: HIRS_Provisioner.NET
+ if: contains(matrix.os, 'ubuntu')
+ run: |
+ dotnet build --configuration Release --no-restore
+
+ - name: Run Unit Tests and Save Logs - Windows
+ id: window_result
+ if: contains(matrix.os, 'windows') && always()
+ working-directory: HIRS_Provisioner.NET
+ run: |
+ $logs = dotnet test /p:PublishSingleFile=false --no-restore -v m
+ $results = [string]$logs
+ $results = $results.Contains("Passed!")
+ if($results) { $results = "Pass" } else { $results = "Fail"}
+ echo "::set-output name=result::$results"
+ $logName = "${{matrix.os}}-unit-tests-" + $results + ".log"
+ New-Item $logName
+ Set-Content $logName $logs
+ Get-Content $logName
+
+ - name: Run Unit Tests Ubuntu
+ if: contains(matrix.os, 'ubuntu')
+ working-directory: HIRS_Provisioner.NET
+ run: |
+ logName="${{matrix.os}}-unit-tests.log"
+ dotnet test --no-restore -v m > $logName
+
+ - name: Extract Ubuntu Unit Test Results
+ id: ubuntu_result
+ if: contains(matrix.os, 'ubuntu') && always()
+ working-directory: HIRS_Provisioner.NET
+ run: |
+ logName="${{matrix.os}}-unit-tests.log"
+ if grep -rnw $logName -e "Passed\!" ;
+ then
+ result="Pass"
+ else
+ result="Fail"
+ fi
+ echo "::set-output name=result::$result"
+ more $logName
+
+ - name: Upload Logs Ubuntu
+ uses: actions/upload-artifact@v2
+ if: contains(matrix.os, 'ubuntu') && always()
+ with:
+ name: "${{matrix.os}}-unit-tests-${{steps.ubuntu_result.outputs.result}}.log"
+ path: HIRS_Provisioner.NET/*.log
+
+ - name: Upload Logs Windows
+ uses: actions/upload-artifact@v2
+ if: contains(matrix.os, 'windows') && always()
+ with:
+ name: "${{matrix.os}}-unit-tests-${{steps.window_result.outputs.result}}.log"
+ path: HIRS_Provisioner.NET/*.log
+
+ Evaluator:
+ name: Evaluate Tests
+ needs: [dotnet_provisioner_unit_tests]
+ runs-on: ubuntu-latest
+ continue-on-error: false
+ steps:
+
+ - uses: actions/checkout@v2
+ with:
+ submodules: recursive
+
+ - name: Make artifact directory
+ run: |
+ mkdir artifacts
+
+ - uses: actions/download-artifact@v3
+ with:
+ path: artifacts
+
+ - name: Determine if a test failed
+ working-directory: artifacts
+ run: |
+ result=""
+ suffix="-unit-tests-Fail.log"
+ msg=" OS did not pass all the unit tests."
+
+ # Generate Annotations and Console Output
+ for file in *.log; do
+ if [[ "$file" == *"Fail"* ]]; then
+ title=${file%"$suffix"}
+ echo "::error title=$title Unit Tests Failed::The $title $msg"
+ result="Failed"
+ fi
+ done
+
+ if [ -n "$result" ]
+ then
+ exit 1
+ fi
\ No newline at end of file
diff --git a/HIRS_Provisioner.NET/.editorconfig b/HIRS_Provisioner.NET/.editorconfig
new file mode 100644
index 00000000..13fe4924
--- /dev/null
+++ b/HIRS_Provisioner.NET/.editorconfig
@@ -0,0 +1,201 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = false
+
+#### .NET Coding Conventions ####
+
+# Organize usings
+dotnet_separate_import_directive_groups = false
+dotnet_sort_system_directives_first = false
+file_header_template = unset
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false:silent
+dotnet_style_qualification_for_field = false:silent
+dotnet_style_qualification_for_method = false:silent
+dotnet_style_qualification_for_property = false:silent
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true:silent
+dotnet_style_predefined_type_for_member_access = true:silent
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
+
+# Expression-level preferences
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true:silent
+dotnet_style_prefer_compound_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_assignment = true:silent
+dotnet_style_prefer_conditional_expression_over_return = true:silent
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
+dotnet_style_prefer_simplified_interpolation = true:suggestion
+
+# Field preferences
+dotnet_style_readonly_field = true:suggestion
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all:suggestion
+
+#### C# Coding Conventions ####
+
+# var preferences
+csharp_style_var_elsewhere = false:silent
+csharp_style_var_for_built_in_types = false:silent
+csharp_style_var_when_type_is_apparent = false:silent
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_prefer_switch_expression = true:suggestion
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true:suggestion
+
+# Modifier preferences
+csharp_prefer_static_local_function = true:suggestion
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
+
+# Code-block preferences
+csharp_prefer_braces = true:silent
+csharp_prefer_simple_using_statement = true:suggestion
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true:suggestion
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_pattern_local_over_anonymous_function = true:suggestion
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_throw_expression = true:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable:suggestion
+csharp_style_unused_value_expression_statement_preference = discard_variable:silent
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace:silent
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = false
+csharp_new_line_before_else = false
+csharp_new_line_before_finally = false
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = none
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = false
+csharp_preserve_single_line_statements = false
+
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
diff --git a/HIRS_Provisioner.NET/hirs.sln b/HIRS_Provisioner.NET/hirs.sln
new file mode 100644
index 00000000..4a2c8b2a
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs.sln
@@ -0,0 +1,158 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.1.32421.90
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "hirs", "hirs\HIRS_Provisioner.NET.csproj", "{300FF15E-1E10-4586-843D-D652BA40DEE5}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E61D6E28-B993-436D-AA88-165857AAEEC0}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "hirsTest", "hirsTest\hirsTest.csproj", "{C6458436-D548-428C-B250-23E3084F74FC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "pcrextend", "tools\pcrextend\pcrextend.csproj", "{2D518622-5C95-4180-87CE-9F730B2714AD}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|ARM = Debug|ARM
+ Debug|ARM64 = Debug|ARM64
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|ARM = Release|ARM
+ Release|ARM64 = Release|ARM64
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Debug|ARM.Build.0 = Debug|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Debug|x64.Build.0 = Debug|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Debug|x86.Build.0 = Debug|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Release|ARM.ActiveCfg = Release|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Release|ARM.Build.0 = Release|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Release|ARM64.Build.0 = Release|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Release|x64.ActiveCfg = Release|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Release|x64.Build.0 = Release|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Release|x86.ActiveCfg = Release|Any CPU
+ {300FF15E-1E10-4586-843D-D652BA40DEE5}.Release|x86.Build.0 = Release|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Debug|ARM.Build.0 = Debug|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Debug|x64.Build.0 = Debug|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Debug|x86.Build.0 = Debug|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Release|ARM.ActiveCfg = Release|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Release|ARM.Build.0 = Release|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Release|ARM64.Build.0 = Release|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Release|x64.ActiveCfg = Release|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Release|x64.Build.0 = Release|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Release|x86.ActiveCfg = Release|Any CPU
+ {C6458436-D548-428C-B250-23E3084F74FC}.Release|x86.Build.0 = Release|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Debug|ARM.Build.0 = Debug|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Debug|x64.Build.0 = Debug|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Debug|x86.Build.0 = Debug|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Release|ARM.ActiveCfg = Release|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Release|ARM.Build.0 = Release|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Release|ARM64.Build.0 = Release|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Release|x64.ActiveCfg = Release|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Release|x64.Build.0 = Release|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Release|x86.ActiveCfg = Release|Any CPU
+ {2D518622-5C95-4180-87CE-9F730B2714AD}.Release|x86.Build.0 = Release|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Debug|ARM.Build.0 = Debug|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Debug|x64.Build.0 = Debug|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Debug|x86.Build.0 = Debug|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Release|ARM.ActiveCfg = Release|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Release|ARM.Build.0 = Release|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Release|ARM64.Build.0 = Release|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Release|x64.ActiveCfg = Release|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Release|x64.Build.0 = Release|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Release|x86.ActiveCfg = Release|Any CPU
+ {08A014E3-3E70-4E8B-9870-5000F3429E9F}.Release|x86.Build.0 = Release|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Debug|ARM.Build.0 = Debug|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Debug|x64.Build.0 = Debug|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Debug|x86.Build.0 = Debug|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Release|ARM.ActiveCfg = Release|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Release|ARM.Build.0 = Release|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Release|ARM64.Build.0 = Release|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Release|x64.ActiveCfg = Release|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Release|x64.Build.0 = Release|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Release|x86.ActiveCfg = Release|Any CPU
+ {34463663-1DEF-46FB-99AC-D8FABB71E7F0}.Release|x86.Build.0 = Release|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Debug|ARM.Build.0 = Debug|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Debug|x64.Build.0 = Debug|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Debug|x86.Build.0 = Debug|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Release|Any CPU.Build.0 = Release|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Release|ARM.ActiveCfg = Release|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Release|ARM.Build.0 = Release|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Release|ARM64.Build.0 = Release|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Release|x64.ActiveCfg = Release|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Release|x64.Build.0 = Release|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Release|x86.ActiveCfg = Release|Any CPU
+ {540546BE-36E3-4C3A-B84B-70A4F0393546}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {6F78678F-F528-4DE8-BC2A-71FA403D68B8}
+ EndGlobalSection
+EndGlobal
diff --git a/HIRS_Provisioner.NET/hirs/Directory.Build.targets b/HIRS_Provisioner.NET/hirs/Directory.Build.targets
new file mode 100644
index 00000000..6d1615f5
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/Directory.Build.targets
@@ -0,0 +1,44 @@
+
+
+
+ false
+
+
+
+
+ /usr/share/hirs
+ /usr/bin/chmod 644 /usr/share/hirs/appsettings.json; /usr/bin/ln -s /usr/share/hirs/tpm_aca_provision /usr/bin/tpm_aca_provision
+ rm -f /usr/bin/tpm_aca_provision; rm -rf /usr/share/hirs
+
+
+
+
+
+
+
+
+
+ $(MSBuildThisFileDirectory)\Resources\Product.wxs
+ $(NuGetPackageRoot)wix\3.11.2\tools\
+ $(WixInstallPath)heat.exe
+ $(WixInstallPath)candle.exe
+ $(WixInstallPath)light.exe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/HIRS_Provisioner.NET/hirs/HIRS_Provisioner.NET.csproj b/HIRS_Provisioner.NET/hirs/HIRS_Provisioner.NET.csproj
new file mode 100644
index 00000000..71bbaed8
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/HIRS_Provisioner.NET.csproj
@@ -0,0 +1,102 @@
+
+
+
+ Exe
+ net6.0
+ linux-x64;win-x64
+ hirs.Program
+ true
+ enable
+ enable
+ 2.2.0
+
+
+
+
+ DEBUG;TRACE
+ 0
+
+
+
+ TRACE
+ 0
+
+
+
+
+
+
+ all
+
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+ all
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+ true
+
+
+
+
+
+ $(ProjectDir)Resources
+ $(ProjectDir)generated
+
+
+ $(protoc_linux64)
+ $(protoc_linux86)
+ $(protoc_macosx64)
+ $(protoc_macosx86)
+ $(protoc_windows64)
+ $(protoc_windows86)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+ Always
+ true
+
+
+
+
diff --git a/HIRS_Provisioner.NET/hirs/Resources/Product.wxs b/HIRS_Provisioner.NET/hirs/Resources/Product.wxs
new file mode 100644
index 00000000..49320713
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/Resources/Product.wxs
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HIRS_Provisioner.NET/hirs/Resources/ProvisionerTpm2.proto b/HIRS_Provisioner.NET/hirs/Resources/ProvisionerTpm2.proto
new file mode 100644
index 00000000..71996560
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/Resources/ProvisionerTpm2.proto
@@ -0,0 +1,100 @@
+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;
+}
+
+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 bytes certificate = 1;
+ optional ResponseStatus status = 2 [default = FAIL];
+}
+
diff --git a/HIRS_Provisioner.NET/hirs/appsettings.json b/HIRS_Provisioner.NET/hirs/appsettings.json
new file mode 100644
index 00000000..854d682e
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/appsettings.json
@@ -0,0 +1,38 @@
+{
+ "auto_detect_tpm": "TRUE",
+ "aca_address_port": "https://127.0.0.1:8443",
+ "efi_prefix": "",
+ "paccor_output_file": "",
+ "event_log_file": "",
+ "hardware_manifest_collectors": "paccor_scripts",
+
+ "Serilog": {
+ "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
+ "Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId" ],
+ "MinimumLevel": {
+ "Default": "Debug",
+ "Override": {
+ "Microsoft": "Warning",
+ "System": "Warning"
+ }
+ },
+ "WriteTo": [
+ {
+ "Name": "Console",
+ "Args": {
+ "outputTemplate": "{Message}{NewLine}",
+ "theme": "Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Grayscale, Serilog.Sinks.Console",
+ "restrictedToMinimumLevel": "Information"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "hirs.log",
+ "rollingInterval": "Day",
+ "retainedFileCountLimit": 5
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/HIRS_Provisioner.NET/hirs/src/Program.cs b/HIRS_Provisioner.NET/hirs/src/Program.cs
new file mode 100644
index 00000000..c5a67e56
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/Program.cs
@@ -0,0 +1,69 @@
+using CommandLine;
+using Serilog;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Security.Principal;
+using System.Threading.Tasks;
+
+namespace hirs {
+ class Program {
+ public static readonly string VERSION = "17";
+
+ static async Task Main(string[] args) {
+ ClientExitCodes result = 0;
+ try {
+ Settings settings = Settings.LoadSettingsFromDefaultFile();
+ settings.SetUpLog();
+ Log.Information("Starting hirs version " + VERSION);
+ if (!IsRunningAsAdmin()) {
+ result = ClientExitCodes.NOT_PRIVILEGED;
+ Log.Warning("The HIRS provisioner is not running as administrator.");
+ }
+ settings.CompleteSetUp();
+ CLI cli = new();
+ Log.Debug("Parsing CLI args.");
+ ParserResult cliParseResult =
+ CommandLine.Parser.Default.ParseArguments(args)
+ .WithParsed(parsed => cli = parsed)
+ .WithNotParsed(HandleParseError);
+
+ if (cliParseResult.Tag == ParserResultType.NotParsed) {
+ // Help text requested, or parsing failed. Exit.
+ Log.Warning("Could not parse command line arguments. Set --tcp --sim, --tcp :, --nix, or --win. See documentation for further assistance.");
+ } else {
+ Provisioner p = new(settings, cli);
+ IHirsAcaTpm tpm = p.ConnectTpm();
+ p.UseClassicDeviceInfoCollector();
+ result = (ClientExitCodes)await p.Provision(tpm);
+ Log.Information("----> Provisioning " + (result == 0 ? "successful" : "failed") + ".");
+ }
+ } catch (Exception e) {
+ result = ClientExitCodes.FAIL;
+ Log.Fatal(e, "Application stopped.");
+ }
+ Log.CloseAndFlush();
+
+ return (int)result;
+ }
+
+ private static void HandleParseError(IEnumerable errs) {
+ //handle errors
+ Log.Error("There was a CLI error: " + errs.ToString());
+ }
+
+ private static bool IsRunningAsAdmin() {
+ bool isAdmin = false;
+ try {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
+ WindowsIdentity user = WindowsIdentity.GetCurrent();
+ WindowsPrincipal principal = new(user);
+ isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
+ } else {
+ isAdmin = Mono.Unix.Native.Syscall.geteuid() == 0;
+ }
+ } catch { }
+ return isAdmin;
+ }
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirs/src/client/Client.cs b/HIRS_Provisioner.NET/hirs/src/client/Client.cs
new file mode 100644
index 00000000..f075ae2c
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/client/Client.cs
@@ -0,0 +1,130 @@
+using Google.Protobuf;
+using Hirs.Pb; // Imports ProvisionerTpm2.proto (compiled and generated by protobuf)
+using Serilog;
+using System;
+using System.Buffers.Text;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace hirs {
+ public class Client : IHirsAcaClient {
+ public static readonly string POST_IDENTITY_CLAIM_PATH = "HIRS_AttestationCA/identity-claim-tpm2/process";
+ public static readonly string POST_REQUEST_CERT_TPM2_PATH = "HIRS_AttestationCA/request-certificate-tpm2";
+
+ private readonly Uri uri;
+ private static readonly HttpClientHandler handler = new() {
+ ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
+ }; // TODO: Overhaul ACA security
+ private readonly HttpClient client;
+
+ /**
+ * This method will create an HttpClient that will accept any server certificate.
+ */
+ public Client(string address) {
+ uri = new(address);
+ client = new HttpClient(handler);
+ }
+
+ public Client(string address, HttpClient httpClient) {
+ uri = new(address);
+ client = httpClient;
+ }
+
+ public async Task PostIdentityClaim(IdentityClaim identityClaim) {
+ MemoryStream stream = new(identityClaim.ToByteArray());
+ // serialize to stream
+
+ stream.Seek(0, SeekOrigin.Begin);
+ Uri full_address = new(uri.AbsoluteUri + POST_IDENTITY_CLAIM_PATH);
+ // send data via HTTP
+ StreamContent streamContent = new(stream);
+ streamContent.Headers.TryAddWithoutValidation("Content-Type", "application/octet-stream");
+ streamContent.Headers.TryAddWithoutValidation("Accept", "application/octet-stream, application/json");
+
+ IdentityClaimResponse icr = null;
+ try {
+ Log.Debug("Attempting to send IdentityClaim to " + full_address);
+ HttpResponseMessage response = await client.PostAsync(full_address, streamContent).ConfigureAwait(continueOnCapturedContext: false);
+ Log.Debug(response.ToString());
+ if (response.StatusCode == HttpStatusCode.OK) {
+ byte[] contentBytes = await response.Content.ReadAsByteArrayAsync();
+ icr = IdentityClaimResponse.Parser.ParseFrom(contentBytes);
+ Log.Debug("IdentityClaim delivery succeeded.");
+ } else {
+ Log.Debug("IdentityClaim delivery failed.");
+ Log.Debug("Request reason phrase: " + response.ReasonPhrase);
+ Log.Debug("Request content: " + response.Content);
+ }
+ } catch (Exception e) {
+ Log.Debug(e, "Error during post of the identity claim.");
+ }
+ return icr;
+ }
+
+ public IdentityClaim CreateIdentityClaim(DeviceInfo dv, byte[] akPublicArea, byte[] ekPublicArea,
+ byte[] endorsementCredential, List platformCredentials,
+ string paccoroutput) {
+ IdentityClaim identityClaim = new();
+ identityClaim.Dv = dv;
+ identityClaim.AkPublicArea = ByteString.CopyFrom(akPublicArea);
+ identityClaim.EkPublicArea = ByteString.CopyFrom(ekPublicArea);
+ identityClaim.EndorsementCredential = ByteString.CopyFrom(endorsementCredential);
+ if (platformCredentials != null) {
+ foreach (byte[] platformCertificate in platformCredentials) {
+ identityClaim.PlatformCredential.Add(ByteString.CopyFrom(platformCertificate));
+ }
+ }
+ identityClaim.PaccorOutput = paccoroutput;
+
+ return identityClaim;
+ }
+
+ public async Task PostCertificateRequest(CertificateRequest certReq) {
+ MemoryStream stream = new(certReq.ToByteArray());
+ // serialize to stream
+
+ stream.Seek(0, SeekOrigin.Begin);
+ Uri full_address = new(uri.AbsoluteUri + POST_REQUEST_CERT_TPM2_PATH);
+ // send data via HTTP
+ StreamContent streamContent = new(stream);
+ streamContent.Headers.TryAddWithoutValidation("Content-Type", "application/octet-stream");
+ streamContent.Headers.TryAddWithoutValidation("Accept", "application/octet-stream, application/json");
+
+ CertificateResponse cr = null;
+ try {
+ Log.Debug("Attempting to send the Certificate Request to " + full_address);
+ HttpResponseMessage response = await client.PostAsync(full_address, streamContent).ConfigureAwait(continueOnCapturedContext: false);
+ Log.Debug(response.ToString());
+ if (response.StatusCode == HttpStatusCode.OK) {
+ byte[] contentBytes = await response.Content.ReadAsByteArrayAsync();
+ cr = CertificateResponse.Parser.ParseFrom(contentBytes);
+ Log.Debug("Certificate Response recevied.");
+ } else {
+ Log.Debug("Certificate Response failed.");
+ Log.Debug("Request reason phrase: " + response.ReasonPhrase);
+ Log.Debug("Request content: " + response.Content);
+ }
+ } catch (Exception e) {
+ Log.Debug(e, "Error during post of the certificate request.");
+ }
+ return cr;
+ }
+
+ public CertificateRequest CreateAkCertificateRequest(byte[] secret, CommandTpmQuoteResponse ctqr) {
+ CertificateRequest akCertReq = new();
+ akCertReq.Nonce = ByteString.CopyFrom(secret);
+ CommandTpmQuoteResponse.formatQuoteInfoSigForAca(ctqr.quoted, ctqr.signature, out string quoteInfoSigStr);
+ akCertReq.Quote = ByteString.CopyFromUtf8(quoteInfoSigStr);
+ //formatPcrValuesForAca(pcrValues, out string pcrValuesStr);
+ //akCertReq.Pcrslist = ByteString.CopyFromUtf8(pcrValuesStr);
+
+ return akCertReq;
+ }
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirs/src/client/IHirsAcaClient.cs b/HIRS_Provisioner.NET/hirs/src/client/IHirsAcaClient.cs
new file mode 100644
index 00000000..837beafe
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/client/IHirsAcaClient.cs
@@ -0,0 +1,53 @@
+using Hirs.Pb;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace hirs {
+ public interface IHirsAcaClient {
+ ///
+ /// Send the to the ACA. The claim is delivered
+ /// asynchronously to the ACA. However, the client will wait for the response.
+ ///
+ /// Evidence about the client.
+ /// The <> from the
+ /// ACA. The response is wrapped in a Task.
+ Task PostIdentityClaim(IdentityClaim identityClaim);
+ ///
+ /// Send the to the ACA. The request is delivered
+ /// asynchronously to the ACA. However, the client will wait for the response.
+ ///
+ /// The request for a certificate. Should contain evidence from
+ /// client to enable nonce verification.
+ /// The <> from the ACA.
+ /// The response is wrapped in a Task. It will contain a certificate or the reason why
+ /// the certificate request was rejected.
+ Task PostCertificateRequest(CertificateRequest certReq);
+ ///
+ /// Collect client evidence regarding a Device into an object that can be interpreted by
+ /// the ACA.
+ ///
+ /// Facts about the Device.
+ /// The public AK retrieved as a TPM2B_PUBLIC.
+ /// The public EK retrieved as a TPM2B_PUBLIC.
+ /// The public EK certificate, encoded in DER or
+ /// PEM.
+ /// Any platform certificates relevant to the Device,
+ /// encoded in DER or PEM.
+ /// Platform Manifest in a JSON format.
+ /// An object that can be sent to the ACA.
+ IdentityClaim CreateIdentityClaim(DeviceInfo dv, byte[] akPublicArea, byte[] ekPublicArea,
+ byte[] endorsementCredential,
+ List platformCredentials, string paccoroutput);
+ ///
+ /// Collect answers to verification requirements regarding a Device into an object that
+ /// can be interpreted by the ACA.
+ ///
+ /// Verification data.
+ /// TPM Quote data from the client Device.
+ /// A object that can be sent to the
+ /// ACA.
+ CertificateRequest CreateAkCertificateRequest(byte[] secret, CommandTpmQuoteResponse ctqr);
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirs/src/client/TbsWrapper.cs b/HIRS_Provisioner.NET/hirs/src/client/TbsWrapper.cs
new file mode 100644
index 00000000..5ca41d56
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/client/TbsWrapper.cs
@@ -0,0 +1,179 @@
+using Serilog;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Principal;
+using System.Text;
+
+namespace hirs {
+ class TbsWrapper {
+ public class NativeMethods {
+ [DllImport("tbs.dll", CharSet = CharSet.Unicode)]
+ internal static extern TBS_RESULT
+ Tbsi_Context_Create(
+ ref TBS_CONTEXT_PARAMS ContextParams,
+ ref UIntPtr Context);
+
+ [DllImport("tbs.dll", CharSet = CharSet.Unicode)]
+ internal static extern TBS_RESULT
+ Tbsip_Context_Close(
+ UIntPtr Context);
+
+ [DllImport("tbs.dll", CharSet = CharSet.Unicode)]
+ internal static extern TBS_RESULT
+ Tbsi_Get_OwnerAuth(
+ UIntPtr Context,
+ [System.Runtime.InteropServices.MarshalAs(UnmanagedType.U4), In]
+ TBS_OWNERAUTH_TYPE OwnerAuthType,
+ [System.Runtime.InteropServices.MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3), In, Out]
+ byte[] OutBuffer,
+ ref uint OutBufferSize);
+
+ [DllImport("tbs.dll", CharSet = CharSet.Unicode)]
+ internal static extern TBS_RESULT
+ Tbsi_Get_TCG_Log(
+ UIntPtr Context,
+ [System.Runtime.InteropServices.MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), In, Out]
+ byte[] pOutputBuf,
+ ref uint pOutputBufLen);
+ }
+
+ public enum TBS_RESULT : uint {
+ TBS_SUCCESS = 0,
+ TBS_E_BLOCKED = 0x80280400,
+ TBS_E_INTERNAL_ERROR = 0x80284001,
+ TBS_E_BAD_PARAMETER = 0x80284002,
+ TBS_E_INSUFFICIENT_BUFFER = 0x80284005,
+ TBS_E_COMMAND_CANCELED = 0x8028400D,
+ TBS_E_OWNERAUTH_NOT_FOUND = 0x80284015
+ }
+
+ public enum TBS_OWNERAUTH_TYPE : uint {
+ TBS_OWNERAUTH_TYPE_FULL = 1,
+ TBS_OWNERAUTH_TYPE_ADMIN = 2,
+ TBS_OWNERAUTH_TYPE_USER = 3,
+ TBS_OWNERAUTH_TYPE_ENDORSEMENT = 4,
+ TBS_OWNERAUTH_TYPE_ENDORSEMENT_20 = 12,
+ TBS_OWNERAUTH_TYPE_STORAGE_20 = 13
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct TBS_CONTEXT_PARAMS {
+ public TBS_CONTEXT_VERSION Version;
+ public TBS_CONTEXT_CREATE_FLAGS Flags;
+ }
+
+ public enum TBS_CONTEXT_VERSION : uint {
+ ONE = 1,
+ TWO = 2
+ }
+
+ public enum TBS_CONTEXT_CREATE_FLAGS : uint {
+ RequestRaw = 0x00000001,
+ IncludeTpm12 = 0x00000002,
+ IncludeTpm20 = 0x00000004,
+ }
+
+ // This method is only intended to be called from Windows.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "")]
+ public static bool GetOwnerAuthFromOS(out byte[] ownerAuth) {
+ ownerAuth = Array.Empty();
+ WindowsIdentity identity = WindowsIdentity.GetCurrent();
+ WindowsPrincipal principal = new(identity);
+ if (!principal.IsInRole(WindowsBuiltInRole.Administrator)) {
+ Log.Error("GetOwnerAuthFromOS: run the client with Administrator privileges");
+ return false;
+ }
+
+ // open context
+ TbsWrapper.TBS_CONTEXT_PARAMS contextParams;
+ UIntPtr tbsContext = UIntPtr.Zero;
+ contextParams.Version = TbsWrapper.TBS_CONTEXT_VERSION.TWO;
+ contextParams.Flags = TbsWrapper.TBS_CONTEXT_CREATE_FLAGS.IncludeTpm20;
+ TbsWrapper.TBS_RESULT result = TbsWrapper.NativeMethods.Tbsi_Context_Create(ref contextParams, ref tbsContext);
+
+ if (result != TbsWrapper.TBS_RESULT.TBS_SUCCESS) {
+ return false;
+ }
+ if (tbsContext == UIntPtr.Zero) {
+ return false;
+ }
+
+ // get owner auth size
+ uint ownerAuthSize = 0;
+ TbsWrapper.TBS_OWNERAUTH_TYPE ownerType = TbsWrapper.TBS_OWNERAUTH_TYPE.TBS_OWNERAUTH_TYPE_STORAGE_20;
+ result = TbsWrapper.NativeMethods.Tbsi_Get_OwnerAuth(tbsContext, ownerType, ownerAuth, ref ownerAuthSize);
+ if (result != TbsWrapper.TBS_RESULT.TBS_SUCCESS &&
+ result != TbsWrapper.TBS_RESULT.TBS_E_INSUFFICIENT_BUFFER) {
+ ownerType = TbsWrapper.TBS_OWNERAUTH_TYPE.TBS_OWNERAUTH_TYPE_FULL;
+ result = TbsWrapper.NativeMethods.Tbsi_Get_OwnerAuth(tbsContext, ownerType, ownerAuth, ref ownerAuthSize);
+ if (result != TbsWrapper.TBS_RESULT.TBS_SUCCESS &&
+ result != TbsWrapper.TBS_RESULT.TBS_E_INSUFFICIENT_BUFFER) {
+ Log.Debug("Failed to get ownerAuthSize.");
+ return false;
+ }
+ }
+ // get owner auth itself
+ ownerAuth = new byte[ownerAuthSize];
+ result = TbsWrapper.NativeMethods.Tbsi_Get_OwnerAuth(tbsContext, ownerType, ownerAuth, ref ownerAuthSize);
+ if (result != TbsWrapper.TBS_RESULT.TBS_SUCCESS) {
+ Log.Debug("Failed to get ownerAuth.");
+ return false;
+ }
+
+ TbsWrapper.NativeMethods.Tbsip_Context_Close(tbsContext);
+
+ return true;
+ }
+
+ // This method is only intended to be called from Windows.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "")]
+ public static bool GetEventLog(out byte[] eventLog) {
+ eventLog = Array.Empty();
+ WindowsIdentity identity = WindowsIdentity.GetCurrent();
+ WindowsPrincipal principal = new(identity);
+ if (!principal.IsInRole(WindowsBuiltInRole.Administrator)) {
+ Log.Debug("GetEventLog: run the client with Administrator privileges");
+ return false;
+ }
+
+ // open context
+ TbsWrapper.TBS_CONTEXT_PARAMS contextParams;
+ UIntPtr tbsContext = UIntPtr.Zero;
+ contextParams.Version = TbsWrapper.TBS_CONTEXT_VERSION.TWO;
+ contextParams.Flags = TbsWrapper.TBS_CONTEXT_CREATE_FLAGS.IncludeTpm12 | TbsWrapper.TBS_CONTEXT_CREATE_FLAGS.IncludeTpm20;
+ TbsWrapper.TBS_RESULT result = TbsWrapper.NativeMethods.Tbsi_Context_Create(ref contextParams, ref tbsContext);
+
+ if (result != TbsWrapper.TBS_RESULT.TBS_SUCCESS) {
+ return false;
+ }
+ if (tbsContext == UIntPtr.Zero) {
+ return false;
+ }
+
+ // Two calls needed
+ // First gets the log size
+ uint eventLogSize = 0;
+ Log.Debug("Attempting to get the event log size from Tbsi.");
+ result = TbsWrapper.NativeMethods.Tbsi_Get_TCG_Log(tbsContext, eventLog, ref eventLogSize);
+ if (result != TbsWrapper.TBS_RESULT.TBS_SUCCESS &&
+ result != TbsWrapper.TBS_RESULT.TBS_E_INSUFFICIENT_BUFFER) {
+ Log.Debug("Failed to get eventLogSize.");
+ return false;
+ }
+ // Second gets the log
+ Log.Debug("Attempting to get the event log from Tbsi.");
+ eventLog = new byte[eventLogSize];
+ result = TbsWrapper.NativeMethods.Tbsi_Get_TCG_Log(tbsContext, eventLog, ref eventLogSize);
+ if (result != TbsWrapper.TBS_RESULT.TBS_SUCCESS) {
+ Log.Debug("Failed to get eventLog.");
+ return false;
+ }
+
+ TbsWrapper.NativeMethods.Tbsip_Context_Close(tbsContext);
+
+ return true;
+ }
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirs/src/config/CLI.cs b/HIRS_Provisioner.NET/hirs/src/config/CLI.cs
new file mode 100644
index 00000000..33168c88
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/config/CLI.cs
@@ -0,0 +1,39 @@
+using CommandLine;
+
+namespace hirs {
+ public class CLI {
+ [Option("tcp", SetName="type", Default = false, HelpText = "Connect to the TPM by IP. Use the format ip:port. By default will connect to " + CommandTpm.DefaultSimulatorNamePort + ".")]
+ public bool Tcp {
+ get; set;
+ }
+
+ [Option("win", SetName = "type", Default = false, HelpText = "Connect to a Windows TPM device.")]
+ public bool Win {
+ get; set;
+ }
+
+ [Option("nix", SetName = "type", Default = false, HelpText = "Connect to a Linux TPM device.")]
+ public bool Nix {
+ get; set;
+ }
+
+ [Option("sim", Default = false, HelpText = "Notify the program of intent to connect to a TPM simulator.")]
+ public bool Sim {
+ get; set;
+ }
+
+ [Option("ip", Default = CommandTpm.DefaultSimulatorNamePort, HelpText = "IP of the TPM Device. Use the format ip:port.")]
+ public string Ip {
+ get; set;
+ }
+
+ [Option("replaceAK", Default = false, HelpText = "Clear any existing hirs AK and create a new one.")]
+ public bool ReplaceAK {
+ get; set;
+ }
+
+ public static string[] SplitArgs(string argString) {
+ return argString.SplitArgs(true);
+ }
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirs/src/config/ClientExitCodes.cs b/HIRS_Provisioner.NET/hirs/src/config/ClientExitCodes.cs
new file mode 100644
index 00000000..1e5912e5
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/config/ClientExitCodes.cs
@@ -0,0 +1,18 @@
+
+namespace hirs {
+ public enum ClientExitCodes {
+ SUCCESS = 0, // Full successful program completion
+ FAIL = 1, // Unknown/Generic failure resulting in exit
+ USER_ERROR = 20, // Generic user error
+ MISSING_CONFIG = 21, // Config file missing
+ ACA_UNREACHABLE = 22, // Nothing found at the address specified
+ NOT_PRIVILEGED = 23, // Client not run as root
+ EXTERNAL_APP_ERROR = 40, // Generic external application error
+ TPM_ERROR = 41, // Encountered error with the TPM, log the TPM Return Code
+ HW_COLLECTION_ERROR = 42, // Encountered error when gathering hardware details
+ PROVISIONING_ERROR = 60, // Generic provisioning error |
+ PASS_1_STATUS_FAIL = 61,
+ PASS_2_STATUS_FAIL = 62,
+ MAKE_CREDENTIAL_BLOB_MALFORMED = 63 // The TPM2_MakeCredential blob was not correct
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirs/src/config/Settings.cs b/HIRS_Provisioner.NET/hirs/src/config/Settings.cs
new file mode 100644
index 00000000..d9bd28dc
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/config/Settings.cs
@@ -0,0 +1,547 @@
+using HardwareManifestPlugin;
+using HardwareManifestPluginManager;
+using Microsoft.Extensions.Configuration;
+using Serilog;
+using System;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+
+namespace hirs {
+ public class Settings {
+ public enum Options {
+ paccor_output_file,
+ aca_address_port,
+ efi_prefix,
+ auto_detect_tpm,
+ event_log_file,
+ hardware_manifest_collectors,
+ hardware_manifest_collection_swid_enforced,
+ linux_bios_vendor_file,
+ linux_bios_version_file,
+ linux_bios_date_file,
+ linux_sys_vendor_file,
+ linux_product_name_file,
+ linux_product_version_file,
+ linux_product_serial_file
+ }
+
+ private static readonly string DEFAULT_SETTINGS_FILE = "appsettings.json";
+ private static readonly string EFI_ARTIFACT_PATH_COMPAT = "/boot/tcg/";
+ private static readonly string EFI_ARTIFACT_PATH = "/EFI/tcg/";
+ private static readonly string EFI_ARTIFACT_LINUX_PREFIX = "/boot/efi";
+
+ private readonly string settingsFile;
+ private readonly IConfiguration configFromSettingsFile;
+
+ // Storage of options collected from the settingsFile, with some default values
+ public virtual string paccor_output {
+ get; private set;
+ }
+ public virtual Uri aca_address_port {
+ get; private set;
+ }
+ public string efi_prefix {
+ get; private set;
+ }
+ public bool auto_detect_tpm {
+ get; private set;
+ }
+ public virtual byte[] event_log {
+ get; private set;
+ }
+ public virtual string linux_bios_vendor {
+ get; private set;
+ }
+ public virtual string linux_bios_version {
+ get; private set;
+ }
+ public virtual string linux_bios_date {
+ get; private set;
+ }
+ public virtual string linux_sys_vendor {
+ get; private set;
+ }
+ public virtual string linux_product_name {
+ get; private set;
+ }
+ public virtual string linux_product_version {
+ get; private set;
+ }
+ public virtual string linux_product_serial {
+ get; private set;
+ }
+ private List hardwareManifests = new();
+ private Dictionary hardware_manifest_collectors_with_args = new();
+ private bool hardware_manifest_collection_swid_enforced = false;
+
+ private Settings() : this(Settings.DEFAULT_SETTINGS_FILE) { }
+ ///
+ ///
+ /// The path to the appsettings.json file on the file system.
+ private Settings(string file) {
+ settingsFile = file;
+ configFromSettingsFile = ReadSettingsFile();
+ }
+
+ public static Settings LoadSettingsFromDefaultFile() {
+ return new(DEFAULT_SETTINGS_FILE);
+ }
+ ///
+ ///
+ /// The path to the settings JSON file on the file system.
+ public static Settings LoadSettingsFromFile(string path) {
+ Settings settings = new(path);
+ return settings;
+ }
+
+ private static string GetBasePath() {
+ return AppContext.BaseDirectory;
+ }
+
+ private IConfiguration ReadSettingsFile() {
+ string basePath = GetBasePath();
+ IConfiguration configuration = new ConfigurationBuilder()
+ .SetBasePath(basePath)
+ .AddJsonFile(settingsFile, false, true)
+ .Build();
+ return configuration;
+ }
+
+ public void SetUpLog() {
+ Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(configFromSettingsFile).CreateLogger();
+ Log.Debug("Reading settings file: " + Path.GetFullPath(Path.Combine(GetBasePath(), settingsFile)));
+ }
+
+ public void CompleteSetUp() {
+ try {
+ ConfigureHardwareManifestManagement();
+
+ IngestPaccorDataFromFile();
+
+ ParseAcaAddress();
+
+ CheckAutoDetectTpm();
+
+ CheckEfiPrefix();
+
+ IngestEventLogFromFile();
+
+ StoreCustomDeviceInfoCollectorOptions();
+
+ } catch (Exception e) {
+ if (Log.Logger == null) {
+ Console.WriteLine("Could not set up logging.");
+ }
+ Log.Error(e, "Error reading the settings file.");
+ throw;
+ }
+ }
+
+ #region Hardware Manifest
+ private void ConfigureHardwareManifestManagement() {
+ if (!string.IsNullOrWhiteSpace(configFromSettingsFile[Options.hardware_manifest_collectors.ToString()])) {
+ Log.Debug("Configuring Hardware Manifest Plugin Manager");
+ string hardware_manifest_collectors = $"{ configFromSettingsFile[Options.hardware_manifest_collectors.ToString()] }";
+ hardware_manifest_collectors_with_args = ParseHardwareManifestCollectorsString(hardware_manifest_collectors);
+ // Collectors are identified by Name.
+ // Multiple collectors can be identified with a comma delimiter between collector names.
+ // There is a field in the HardwareManifestPlugin Interface that must match this Name.
+ // Each Name can be optionally followed by a space and command-line style arguments.
+ // Those arguments must not break the JSON encoding of the settings file.
+ // ex: collector1_name -a --b=c,collector2_name,collector3_name
+ // If SWID enforcement is enabled, Collectors must also pass validation prior to loading.
+ // Once loaded, the command-line arguments are passed directly to the collector by the Configure method of the Interface.
+ List names = hardware_manifest_collectors_with_args.Keys.ToList();
+ if (!string.IsNullOrWhiteSpace(configFromSettingsFile[Options.hardware_manifest_collection_swid_enforced.ToString()])) {
+ string hardware_manifest_collection_swid_enforced_str = $"{ configFromSettingsFile[Options.hardware_manifest_collection_swid_enforced.ToString()] }";
+ hardware_manifest_collection_swid_enforced = Boolean.Parse(hardware_manifest_collection_swid_enforced_str);
+ Log.Debug("SWID enforcement of Hardware Manifest Plugins are " + (hardware_manifest_collection_swid_enforced ? "en" : "dis") + "abled in settings.");
+ }
+ hardwareManifests = HardwareManifestPluginManagerUtils.LoadPlugins(names, hardware_manifest_collection_swid_enforced);
+ CleanHardwareManifestCollectors();
+ Log.Debug("Finished configuring the Hardware Manifest Plugin Manager.");
+ } else {
+ Log.Debug("Hardware Manifest Plugin Manager will not be used. No collectors were identified in settings.");
+ }
+ }
+
+ private static Dictionary ParseHardwareManifestCollectorsString(string hardware_manifest_collectors) {
+ Dictionary dict = new();
+ List names = hardware_manifest_collectors.Split(',').Select(s => s.Trim()).ToList();
+ foreach (string name in names) {
+ string[] parts = name.Split(' ', 2); // split on first space
+ dict.Add(parts[0], parts.Length == 2 ? parts[1] : "");
+ }
+ return dict;
+ }
+
+ private void CleanHardwareManifestCollectors() {
+ List names = hardwareManifests.Select(x => x.Name).ToList();
+ Dictionary dict = new();
+ foreach (string name in names) {
+ dict.Add(name, hardware_manifest_collectors_with_args[name]);
+ }
+ hardware_manifest_collectors_with_args.Clear();
+ hardware_manifest_collectors_with_args = dict;
+ }
+
+ public virtual string RunHardwareManifestCollectors() {
+ Log.Debug("Gathering data from loaded hardware manifest collectors.");
+ string manifestJson = "";
+ foreach (IHardwareManifest manifest in hardwareManifests) {
+ try {
+ Log.Debug(" Configuring " + manifest.Name);
+ if (hardware_manifest_collectors_with_args.ContainsKey(manifest.Name)) {
+ manifest.Configure(CLI.SplitArgs(hardware_manifest_collectors_with_args[manifest.Name]));
+ }
+ // TODO: Combine JSON Better
+ // OR Return proto objects
+ Log.Debug(" Gathering from " + manifest.Name);
+ manifestJson = string.Join(manifestJson, manifest.GatherHardwareManifestAsJsonString());
+ } catch (Exception e) {
+ Log.Debug($"Problem retrieving hardware manifest from {manifest.Name}.", e.InnerException);
+ }
+ }
+ //TODO: Verify JSON?
+ return manifestJson;
+ }
+ #endregion
+
+ #region Ingest paccor data from file
+ private void IngestPaccorDataFromFile() {
+ if (!string.IsNullOrWhiteSpace(configFromSettingsFile[Options.paccor_output_file.ToString()])) {
+ Log.Debug("Checking location of the paccor output file.");
+ string paccor_output_path = $"{ configFromSettingsFile[Options.paccor_output_file.ToString()] }";
+ if (DoesFileExist(paccor_output_path, out paccor_output_path)) {
+ if (HasHardwareManifestPlugins()) {
+ Log.Warning("The settings file specified hardware manifest collectors and a paccor output file. Fresh data is preferred over data from a file. If you want to use the file data, clear the collectors field from the settings file.");
+ } else {
+ Log.Debug("Retrieving components from " + Options.paccor_output_file.ToString() + ".");
+ paccor_output = File.ReadAllText(paccor_output_path);
+ if (string.IsNullOrWhiteSpace(paccor_output)) {
+ Log.Warning(Options.paccor_output_file.ToString() + " Paccor output was empty. Cannot perform Platform Attribute validation.");
+ } else {
+ Log.Debug("Output file contains:\n" + paccor_output);
+ }
+ }
+ }
+ } else {
+ Log.Debug(Options.paccor_output_file.ToString() + " not set in the settings file.");
+ }
+ }
+ #endregion
+
+ #region ACA Address
+ private void ParseAcaAddress() {
+ if (!string.IsNullOrWhiteSpace(configFromSettingsFile[Options.aca_address_port.ToString()])) {
+ Log.Debug("Parsing the ACA Address.");
+ string aca_address_port_str = $"{ configFromSettingsFile[Options.aca_address_port.ToString()] }";
+ if (!string.IsNullOrWhiteSpace(aca_address_port_str)) {
+ aca_address_port = new Uri(aca_address_port_str);
+ Log.Debug(" Found " + aca_address_port);
+ }
+ }
+ if (!HasAcaAddress()) {
+ Log.Error(Options.aca_address_port.ToString() + " not set in the settings file. No HIRS ACA server to talk to. Looking for the format: \"https://:\"");
+ }
+ }
+ #endregion
+
+ #region Auto Detect TPM
+ private void CheckAutoDetectTpm() {
+ if (!string.IsNullOrWhiteSpace(configFromSettingsFile[Options.auto_detect_tpm.ToString()])) {
+ Log.Debug("Checking Auto Detect TPM setting.");
+ string auto_detect_tpm_str = $"{ configFromSettingsFile[Options.auto_detect_tpm.ToString()] }";
+ try {
+ auto_detect_tpm = Boolean.Parse(auto_detect_tpm_str);
+ Log.Debug(" Auto Detect TPM is " + (auto_detect_tpm ? "en" : "dis") + "abled.");
+ } catch (FormatException) {
+ auto_detect_tpm = false;
+ Log.Warning(Options.auto_detect_tpm.ToString() + " did not contain a readable true/false setting. Setting to default of false.");
+ }
+ } else {
+ auto_detect_tpm = false;
+ Log.Debug(Options.auto_detect_tpm.ToString() + " not set in the settings file. Setting to default of false.");
+ }
+ }
+ #endregion
+
+ #region EFI
+ private void CheckEfiPrefix() {
+ if (!string.IsNullOrWhiteSpace(configFromSettingsFile[Options.efi_prefix.ToString()])) {
+ Log.Debug("Checking EFI Prefix setting.");
+ efi_prefix = $"{ configFromSettingsFile[Options.efi_prefix.ToString()] }";
+ if (string.IsNullOrWhiteSpace(efi_prefix)) { // If not explicitly set in appsettings, try to use default EFI location on Linux
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
+ efi_prefix = EFI_ARTIFACT_LINUX_PREFIX + EFI_ARTIFACT_PATH;
+ }
+ } else {
+ if (!Directory.Exists(efi_prefix)) {
+ Log.Debug(Options.efi_prefix.ToString() + ": " + efi_prefix + " did not exist.");
+ efi_prefix = null;
+ }
+ }
+ }
+ if (efi_prefix == null) {
+ Log.Warning(Options.efi_prefix.ToString() + " not set in the settings file. Will not attempt to scan for artifacts in EFI.");
+ } else {
+ Log.Debug(" Will scan for artifacts in " + efi_prefix);
+ }
+ }
+
+ public virtual List gatherPlatformCertificatesFromEFI() {
+ // According to FIM: EFIPREFIX/boot/tcg/{cert,pccert,platform}
+ List platformCerts = null;
+ if (!string.IsNullOrWhiteSpace(efi_prefix)) {
+ EnumerationOptions enumOpts = new EnumerationOptions();
+ enumOpts.MatchCasing = MatchCasing.CaseInsensitive;
+ enumOpts.RecurseSubdirectories = true;
+ List files = new List();
+ string[] paths = { "cert", "pccert", "platform" };
+ foreach (string subdir in paths) {
+ string path = efi_prefix + EFI_ARTIFACT_PATH + subdir;
+ if (Directory.Exists(path)) {
+ files.AddRange(new DirectoryInfo(path).GetFiles("*base*", enumOpts));
+ files.AddRange(new DirectoryInfo(path).GetFiles("*delta*", enumOpts));
+ } else {
+ path = efi_prefix + EFI_ARTIFACT_PATH_COMPAT + subdir;
+ if (Directory.Exists(path)) {
+ files.AddRange(new DirectoryInfo(path).GetFiles("*base*", enumOpts));
+ files.AddRange(new DirectoryInfo(path).GetFiles("*delta*", enumOpts));
+ }
+ }
+ }
+ if (files.Count > 0) { // if none found, don't initialize platformCerts
+ // At least one base platform cert found
+ platformCerts = new List();
+ foreach (FileInfo file in files) {
+ platformCerts.Add(File.ReadAllBytes(file.FullName));
+ Log.Debug("gatherPlatformCertificatesFromEFI: Gathering " + file.FullName);
+ }
+ }
+ } else {
+ Log.Warning("gatherPlatformCertificatesFromEFI was called without verifying HasEfiPrefix.");
+ }
+ Log.Debug("Found " + (platformCerts == null ? 0 : platformCerts.Count) + " platform certs.");
+ return platformCerts;
+ }
+
+ public virtual List gatherRIMBasesFromEFI() {
+ // According to PC Client RIM
+ List baseRims = null;
+
+ // /boot/tcg/manifest/swidtag Base RIM Files
+ // + + .swidtag
+ if (!string.IsNullOrWhiteSpace(efi_prefix)) {
+ EnumerationOptions enumOpts = new EnumerationOptions();
+ enumOpts.MatchCasing = MatchCasing.CaseInsensitive;
+ enumOpts.RecurseSubdirectories = true;
+ List files = new List();
+
+ string[] paths = { "manifest", "swidtag" };
+ string ext = "*swidtag";
+ foreach (string subdir in paths) {
+ string path = efi_prefix + EFI_ARTIFACT_PATH + subdir;
+ if (Directory.Exists(path)) {
+ files.AddRange(new DirectoryInfo(path).GetFiles(ext, enumOpts));
+ } else {
+ path = efi_prefix + EFI_ARTIFACT_PATH_COMPAT + subdir;
+ if (Directory.Exists(path)) {
+ files.AddRange(new DirectoryInfo(path).GetFiles(ext, enumOpts));
+ }
+ }
+ }
+ if (files.Count() > 0) { // if none found, don't initialize baseRims
+ // At least one base platform cert found
+ baseRims = new List();
+ foreach (FileInfo file in files) {
+ baseRims.Add(File.ReadAllBytes(file.FullName));
+ Log.Debug("gatherRIMBasesFromEFI: Gathering " + file.FullName);
+ }
+ }
+ } else {
+ Log.Warning("gatherRIMBasesFromEFI was called without verifying HasEfiPrefix.");
+ }
+ Log.Debug("Found " + (baseRims == null ? 0 : baseRims.Count) + " base RIMs.");
+ return baseRims;
+ }
+
+ public virtual List gatherSupportRIMELsFromEFI() {
+ // According to PC Client RIM
+ List supportRimELs = null;
+ // /boot/tcg/manifest/rim Support RIM Files
+ // + + .rimel
+ if (!string.IsNullOrWhiteSpace(efi_prefix)) {
+ EnumerationOptions enumOpts = new EnumerationOptions();
+ enumOpts.MatchCasing = MatchCasing.CaseInsensitive;
+ enumOpts.RecurseSubdirectories = true;
+ List files = new List();
+
+ string[] paths = { "manifest", "rim" };
+ string ext = "*rimel";
+ foreach (string subdir in paths) {
+ string path = efi_prefix + EFI_ARTIFACT_PATH + subdir;
+ if (Directory.Exists(path)) {
+ files.AddRange(new DirectoryInfo(path).GetFiles(ext, enumOpts));
+ } else {
+ path = efi_prefix + EFI_ARTIFACT_PATH_COMPAT + subdir;
+ if (Directory.Exists(path)) {
+ files.AddRange(new DirectoryInfo(path).GetFiles(ext, enumOpts));
+ }
+ }
+ }
+ if (files.Count() > 0) { // if none found, don't initialize baseRims
+ // At least one base platform cert found
+ supportRimELs = new List();
+ foreach (FileInfo file in files) {
+ supportRimELs.Add(File.ReadAllBytes(file.FullName));
+ Log.Debug("gatherSupportRIMELsFromEFI: Gathering " + file.FullName);
+ }
+ }
+ } else {
+ Log.Warning("gatherSupportRIMELsFromEFI was called without verifying HasEfiPrefix.");
+ }
+ Log.Debug("Found " + (supportRimELs == null ? 0 : supportRimELs.Count) + " support rimel files.");
+ return supportRimELs;
+ }
+
+ public virtual List gatherSupportRIMPCRsFromEFI() {
+ // According to PC Client RIM
+ List supportRimPCRs = null;
+ // /boot/tcg/manifest/rim Support RIM Files
+ // + + .rimpcr
+ if (!string.IsNullOrWhiteSpace(efi_prefix)) {
+ EnumerationOptions enumOpts = new EnumerationOptions();
+ enumOpts.MatchCasing = MatchCasing.CaseInsensitive;
+ enumOpts.RecurseSubdirectories = true;
+ List files = new List();
+
+ string[] paths = { "manifest", "rim" };
+ string ext = "*rimpcr";
+ foreach (string subdir in paths) {
+ string path = efi_prefix + EFI_ARTIFACT_PATH + subdir;
+ if (Directory.Exists(path)) {
+ files.AddRange(new DirectoryInfo(path).GetFiles(ext, enumOpts));
+ } else {
+ path = efi_prefix + EFI_ARTIFACT_PATH_COMPAT + subdir;
+ if (Directory.Exists(path)) {
+ files.AddRange(new DirectoryInfo(path).GetFiles(ext, enumOpts));
+ }
+ }
+ }
+ if (files.Count() > 0) { // if none found, don't initialize baseRims
+ // At least one base platform cert found
+ supportRimPCRs = new List();
+ foreach (FileInfo file in files) {
+ supportRimPCRs.Add(File.ReadAllBytes(file.FullName));
+ Log.Debug("gatherSupportRIMPCRsFromEFI: Gathering " + file.FullName);
+ }
+ }
+ } else {
+ Log.Warning("gatherSupportRIMPCRsFromEFI was called without verifying HasEfiPrefix.");
+ }
+ Log.Debug("Found " + (supportRimPCRs == null ? 0 : supportRimPCRs.Count) + " support rimpcr files.");
+ return supportRimPCRs;
+ }
+ #endregion
+
+ #region Ingest Event Log from File
+ private void IngestEventLogFromFile() {
+ if (!string.IsNullOrWhiteSpace(configFromSettingsFile[Options.event_log_file.ToString()])) {
+ Log.Debug("Checking location of the event log.");
+ string event_log_path = $"{ configFromSettingsFile[Options.event_log_file.ToString()] }";
+ if (DoesFileExist(event_log_path, out event_log_path)) {
+ Log.Debug("Retrieving the Event Log. ");
+ event_log = File.ReadAllBytes(event_log_path);
+ if (event_log == null || event_log.Length == 0) {
+ Log.Warning(Options.event_log_file.ToString() + " The event log was empty.");
+ }
+ }
+ } else {
+ Log.Debug(Options.event_log_file.ToString() + " not set in the settings file.");
+ }
+
+ }
+ #endregion
+
+ #region Store Custom Device Info Collector Options
+ private void StoreCustomDeviceInfoCollectorOptions() {
+ if (!string.IsNullOrWhiteSpace(configFromSettingsFile[Options.linux_bios_vendor_file.ToString()])) {
+ Log.Debug("Custom bios vendor file specified for the Device Info Collector on Linux.");
+ string path = $"{ configFromSettingsFile[Options.linux_bios_vendor_file.ToString()] }";
+ linux_bios_vendor = ReadFileText(path);
+ }
+ if (!string.IsNullOrWhiteSpace(configFromSettingsFile[Options.linux_bios_version_file.ToString()])) {
+ Log.Debug("Custom bios version file specified for the Device Info Collector on Linux.");
+ string path = $"{ configFromSettingsFile[Options.linux_bios_version_file.ToString()] }";
+ linux_bios_version = ReadFileText(path);
+ }
+ if (!string.IsNullOrWhiteSpace(configFromSettingsFile[Options.linux_sys_vendor_file.ToString()])) {
+ Log.Debug("Custom hardware manufacturer file specified for the Device Info Collector on Linux.");
+ string path = $"{ configFromSettingsFile[Options.linux_sys_vendor_file.ToString()] }";
+ linux_sys_vendor = ReadFileText(path);
+ }
+ if (!string.IsNullOrWhiteSpace(configFromSettingsFile[Options.linux_product_name_file.ToString()])) {
+ Log.Debug("Custom hardware product name file specified for the Device Info Collector on Linux.");
+ string path = $"{ configFromSettingsFile[Options.linux_product_name_file.ToString()] }";
+ linux_product_name = ReadFileText(path);
+ }
+ if (!string.IsNullOrWhiteSpace(configFromSettingsFile[Options.linux_product_version_file.ToString()])) {
+ Log.Debug("Custom hardware product version file specified for the Device Info Collector on Linux.");
+ string path = $"{ configFromSettingsFile[Options.linux_product_version_file.ToString()] }";
+ linux_product_version = ReadFileText(path);
+ }
+ if (!string.IsNullOrWhiteSpace(configFromSettingsFile[Options.linux_product_serial_file.ToString()])) {
+ Log.Debug("Custom hardware product serial file specified for the Device Info Collector on Linux.");
+ string path = $"{ configFromSettingsFile[Options.linux_product_serial_file.ToString()] }";
+ linux_product_serial = ReadFileText(path);
+ }
+ }
+ #endregion
+ public static bool DoesFileExist(string path, out string out_full_path) {
+ bool found = false;
+ out_full_path = "";
+ if (!string.IsNullOrWhiteSpace(path)) {
+ out_full_path = Path.GetFullPath(path);
+ found = File.Exists(out_full_path);
+ if (!found) {
+ Log.Debug(" File identified in settings did not exist: " + out_full_path);
+ } else {
+ Log.Debug(" File exists: " + out_full_path);
+ }
+ }
+ return found;
+ }
+
+ public static string ReadFileText(string path) {
+ string text = "";
+ if (DoesFileExist(path, out string full_path)) {
+ Log.Debug(" Reading file: " + full_path + ".");
+ text = File.ReadAllText(full_path);
+ Log.Debug(" " + (string.IsNullOrWhiteSpace(text) ? "File was empty." : text));
+ }
+ return text;
+ }
+
+ public bool HasHardwareManifestPlugins() {
+ return hardwareManifests.Count > 0;
+ }
+ public bool HasPaccorOutputFromFile() {
+ return !string.IsNullOrEmpty(paccor_output);
+ }
+ public bool HasAcaAddress() {
+ return aca_address_port != null;
+ }
+ public bool HasEfiPrefix() {
+ return !string.IsNullOrEmpty(efi_prefix);
+ }
+ public bool IsAutoDetectTpmEnabled() {
+ return auto_detect_tpm;
+ }
+ public bool HasEventLogFromFile() {
+ return event_log != null && event_log.Length > 0;
+ }
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirs/src/deviceInfo/ClassicDeviceInfoCollector.cs b/HIRS_Provisioner.NET/hirs/src/deviceInfo/ClassicDeviceInfoCollector.cs
new file mode 100644
index 00000000..27bf6af8
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/deviceInfo/ClassicDeviceInfoCollector.cs
@@ -0,0 +1,250 @@
+using Hirs.Pb;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Management;
+using System.Net;
+using System.Net.NetworkInformation;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.IO;
+using Serilog;
+
+namespace hirs {
+ public class ClassicDeviceInfoCollector : IHirsDeviceInfoCollector {
+ public static readonly string NOT_SPECIFIED = "Not Specified";
+ public static readonly string LINUX_DEFAULT_BIOS_VENDOR_PATH = "/sys/class/dmi/id/bios_vendor";
+ public static readonly string LINUX_DEFAULT_BIOS_VERSION_PATH = "/sys/class/dmi/id/bios_version";
+ public static readonly string LINUX_DEFAULT_BIOS_DATE_PATH = "/sys/class/dmi/id/bios_date";
+ public static readonly string LINUX_DEFAULT_SYS_VENDOR_PATH = "/sys/class/dmi/id/sys_vendor";
+ public static readonly string LINUX_DEFAULT_PRODUCT_NAME_PATH = "/sys/class/dmi/id/product_name";
+ public static readonly string LINUX_DEFAULT_PRODUCT_VERSION_PATH = "/sys/class/dmi/id/product_version";
+ public static readonly string LINUX_DEFAULT_PRODUCT_SERIAL_PATH = "/sys/class/dmi/id/product_serial";
+ private readonly Settings? settings;
+
+ public ClassicDeviceInfoCollector() {
+ settings = null;
+ }
+ public ClassicDeviceInfoCollector(Settings settings) {
+ this.settings = settings;
+ }
+
+ public static string FileToString(string path, string def) {
+ string result;
+ try {
+ result = File.ReadAllText(path).Trim();
+ } catch {
+ result = def;
+ }
+ if (string.IsNullOrWhiteSpace(result)) {
+ result = def;
+ }
+ return result;
+ }
+
+ public DeviceInfo CollectDeviceInfo(string acaAddress) {
+ DeviceInfo dv = new();
+ dv.Fw = CollectFirmwareInfo();
+ dv.Hw = CollectHardwareInfo();
+ dv.Nw = CollectNetworkInfo(acaAddress);
+ dv.Os = CollectOsInfo();
+ return dv;
+ }
+
+ public FirmwareInfo CollectFirmwareInfo() {
+ FirmwareInfo fw = new();
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
+ ManagementScope myScope = new("root\\CIMV2");
+ ManagementObjectSearcher s = new("SELECT * FROM Win32_BIOS");
+ fw.BiosVendor = NOT_SPECIFIED;
+ fw.BiosVersion = NOT_SPECIFIED;
+ fw.BiosReleaseDate = NOT_SPECIFIED;
+ foreach (ManagementObject o in s.Get()) {
+ string manufacturer = (string)o.GetPropertyValue("Manufacturer");
+ string version = (string)o.GetPropertyValue("Version");
+ string releasedate = (string)o.GetPropertyValue("ReleaseDate");
+ fw.BiosVendor = string.IsNullOrEmpty(manufacturer) ? NOT_SPECIFIED : manufacturer;
+ fw.BiosVersion = string.IsNullOrEmpty(version) ? NOT_SPECIFIED : version;
+ fw.BiosReleaseDate = string.IsNullOrEmpty(releasedate) ? NOT_SPECIFIED : releasedate;
+ break;
+ }
+ } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
+ if (settings != null) {
+ if (!string.IsNullOrEmpty(settings.linux_bios_vendor)) {
+ fw.BiosVendor = settings.linux_bios_vendor.Trim();
+ }
+ if (!string.IsNullOrEmpty(settings.linux_bios_version)) {
+ fw.BiosVersion = settings.linux_bios_version.Trim();
+ }
+ if (!string.IsNullOrEmpty(settings.linux_bios_date)) {
+ fw.BiosReleaseDate = settings.linux_bios_date.Trim();
+ }
+ }
+ if (string.IsNullOrEmpty(fw.BiosVendor)) {
+ fw.BiosVendor = FileToString(LINUX_DEFAULT_BIOS_VENDOR_PATH, NOT_SPECIFIED);
+ }
+ if (string.IsNullOrEmpty(fw.BiosVersion)) {
+ fw.BiosVersion = FileToString(LINUX_DEFAULT_BIOS_VERSION_PATH, NOT_SPECIFIED);
+ }
+ if (string.IsNullOrEmpty(fw.BiosReleaseDate)) {
+ fw.BiosReleaseDate = FileToString(LINUX_DEFAULT_BIOS_DATE_PATH, NOT_SPECIFIED);
+ }
+ } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
+ // tbd
+ } else {
+ // tbd
+ }
+
+ Log.Debug("Bios Vendor: " + fw.BiosVendor);
+ Log.Debug("Bios Version: " + fw.BiosVersion);
+ Log.Debug("Bios Date: " + fw.BiosReleaseDate);
+
+ return fw;
+ }
+
+ public HardwareInfo CollectHardwareInfo() {
+ HardwareInfo hw = new();
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
+ ManagementScope myScope = new("root\\CIMV2");
+ ManagementObjectSearcher s = new("SELECT * FROM Win32_ComputerSystemProduct");
+ hw.Manufacturer = NOT_SPECIFIED;
+ hw.ProductName = NOT_SPECIFIED;
+ hw.ProductVersion = NOT_SPECIFIED;
+ hw.SystemSerialNumber = NOT_SPECIFIED;
+ foreach (ManagementObject o in s.Get()) {
+ string vendor = (string)o.GetPropertyValue("Vendor");
+ string name = (string)o.GetPropertyValue("Name");
+ string version = (string)o.GetPropertyValue("Version");
+ string identifyingnumber = (string)o.GetPropertyValue("IdentifyingNumber");
+ hw.Manufacturer = string.IsNullOrWhiteSpace(vendor) ? NOT_SPECIFIED : vendor;
+ hw.ProductName = string.IsNullOrWhiteSpace(name) ? NOT_SPECIFIED : name;
+ hw.ProductVersion = string.IsNullOrWhiteSpace(version) ? NOT_SPECIFIED : version;
+ hw.SystemSerialNumber = string.IsNullOrWhiteSpace(identifyingnumber) ? NOT_SPECIFIED : identifyingnumber;
+ break;
+ }
+ } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
+ if (settings != null) {
+ if (!string.IsNullOrEmpty(settings.linux_sys_vendor)) {
+ hw.Manufacturer = settings.linux_sys_vendor.Trim();
+ }
+ if (!string.IsNullOrEmpty(settings.linux_product_name)) {
+ hw.ProductName = settings.linux_product_name.Trim();
+ }
+ if (!string.IsNullOrEmpty(settings.linux_product_version)) {
+ hw.ProductVersion = settings.linux_product_version.Trim();
+ }
+ if (!string.IsNullOrEmpty(settings.linux_product_serial)) {
+ hw.SystemSerialNumber = settings.linux_product_serial.Trim();
+ }
+ }
+ if (string.IsNullOrEmpty(hw.Manufacturer)) {
+ hw.Manufacturer = FileToString(LINUX_DEFAULT_SYS_VENDOR_PATH, NOT_SPECIFIED);
+ }
+ if (string.IsNullOrEmpty(hw.ProductName)) {
+ hw.ProductName = FileToString(LINUX_DEFAULT_PRODUCT_NAME_PATH, NOT_SPECIFIED);
+ }
+ if (string.IsNullOrEmpty(hw.ProductVersion)) {
+ hw.ProductVersion = FileToString(LINUX_DEFAULT_PRODUCT_VERSION_PATH, NOT_SPECIFIED);
+ }
+ if (string.IsNullOrEmpty(hw.SystemSerialNumber)) {
+ hw.SystemSerialNumber = FileToString(LINUX_DEFAULT_PRODUCT_SERIAL_PATH, NOT_SPECIFIED);
+ }
+ } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
+ // tbd
+ } else {
+ // tbd
+ }
+
+ Log.Debug("System Manufacturer: " + hw.Manufacturer);
+ Log.Debug("Product Name: " + hw.ProductName);
+ Log.Debug("Product Version: " + hw.ProductVersion);
+ Log.Debug("System Serial Number: " + hw.SystemSerialNumber);
+
+ return hw;
+ }
+
+ public NetworkInfo CollectNetworkInfo(string acaAddress) {
+ NetworkInfo nw = new();
+
+ NetworkInterface iface = NetworkInterface
+ .GetAllNetworkInterfaces()
+ .Where(nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback)
+ .FirstOrDefault();
+
+ nw.MacAddress = iface.GetPhysicalAddress().ToString();
+
+ nw.ClearIpAddress();
+ // First attempt to find local ip by connecting ACA
+ if (string.IsNullOrWhiteSpace(acaAddress)) {
+ Uri uri = new(acaAddress);
+ try {
+ Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, 0);
+ socket.Connect(uri.Host, uri.Port);
+ IPEndPoint endPoint = socket.LocalEndPoint as IPEndPoint;
+ nw.IpAddress = endPoint.Address.ToString();
+ } catch {
+ Log.Debug("Not connected to the internet. Trying another search.");
+ }
+ }
+ // Second attempt to find local ip by scanning first interface that is up and not a loopback address
+ if (!nw.HasIpAddress) {
+ foreach (UnicastIPAddressInformation ip in iface.GetIPProperties().UnicastAddresses) {
+ if (ip.Address.AddressFamily == AddressFamily.InterNetwork) {
+ nw.IpAddress = ip.Address.ToString();
+ break;
+ }
+ }
+ }
+
+ // Search for hostname
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
+ nw.Hostname = Dns.GetHostName().ToLower();
+ string domainName = IPGlobalProperties.GetIPGlobalProperties().DomainName;
+ if (!string.IsNullOrWhiteSpace(domainName)) {
+ nw.Hostname = nw.Hostname + "." + domainName;
+ }
+ } else if (nw.HasIpAddress) {
+ nw.Hostname = Dns.GetHostEntry(nw.IpAddress).HostName;
+ } else {
+ nw.Hostname = Dns.GetHostName();
+ }
+
+
+ Log.Debug("Network Info IP: " + nw.IpAddress);
+ Log.Debug("Network Info MAC: " + nw.MacAddress);
+ Log.Debug("Network Info Hostname: " + nw.Hostname);
+
+ return nw;
+ }
+
+ public OsInfo CollectOsInfo() {
+ OsInfo info = new();
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
+ info.OsName = OSPlatform.Windows.ToString();
+ } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
+ info.OsName = OSPlatform.Linux.ToString();
+ } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
+ info.OsName = OSPlatform.OSX.ToString();
+ } else {
+ info.OsName = RuntimeInformation.OSDescription;
+ }
+ info.OsVersion = RuntimeInformation.OSDescription;
+ info.OsArch = RuntimeInformation.OSArchitecture.ToString();
+ info.Distribution = RuntimeInformation.FrameworkDescription;
+ info.DistributionRelease = RuntimeInformation.FrameworkDescription;
+
+ Log.Debug("OS Name: " + info.OsName);
+ Log.Debug("OS Version: " + info.OsVersion);
+ Log.Debug("Architecture: " + info.OsArch);
+ Log.Debug("Distribution: " + info.Distribution);
+ Log.Debug("Distribution Release: " + info.DistributionRelease);
+
+ return info;
+ }
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirs/src/deviceInfo/IHirsDeviceInfoCollector.cs b/HIRS_Provisioner.NET/hirs/src/deviceInfo/IHirsDeviceInfoCollector.cs
new file mode 100644
index 00000000..ad968878
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/deviceInfo/IHirsDeviceInfoCollector.cs
@@ -0,0 +1,10 @@
+using Hirs.Pb;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace hirs {
+ public interface IHirsDeviceInfoCollector {
+ DeviceInfo CollectDeviceInfo(string address);
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirs/src/provisioner/IHirsProvisioner.cs b/HIRS_Provisioner.NET/hirs/src/provisioner/IHirsProvisioner.cs
new file mode 100644
index 00000000..ae703062
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/provisioner/IHirsProvisioner.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace hirs {
+ public interface IHirsProvisioner {
+ void SetSettings(Settings settings);
+ void SetCLI(CLI cli);
+ IHirsAcaTpm ConnectTpm();
+ void SetClient(IHirsAcaClient clientWithAddress);
+ void SetDeviceInfoCollector(IHirsDeviceInfoCollector collector);
+ Task Provision(IHirsAcaTpm tpm);
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirs/src/provisioner/Provisioner.cs b/HIRS_Provisioner.NET/hirs/src/provisioner/Provisioner.cs
new file mode 100644
index 00000000..0a8f3e40
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/provisioner/Provisioner.cs
@@ -0,0 +1,296 @@
+using Google.Protobuf;
+using Hirs.Pb;
+using Serilog;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+namespace hirs {
+
+ public class Provisioner : IHirsProvisioner {
+ private CLI cli = null;
+ private Settings settings = null;
+ private IHirsDeviceInfoCollector deviceInfoCollector = null;
+ private IHirsAcaClient acaClient = null;
+
+ public Provisioner() {
+ }
+
+ public Provisioner(Settings settings, CLI cli) {
+ SetSettings(settings);
+ SetCLI(cli);
+ }
+
+ public void SetSettings(Settings settings) {
+ if (settings == null) {
+ Log.Error("Unknown error. Settings were supposed to have been parsed.");
+ }
+ this.settings = settings!;
+ }
+
+ public void SetCLI(CLI cli) {
+ if (cli == null) {
+ Log.Error("Unknown error. CLI arguments were supposed to have been parsed.");
+ }
+ this.cli = cli;
+ }
+
+ public IHirsAcaTpm ConnectTpm() {
+ IHirsAcaTpm tpm = null;
+ // If tpm device type is set on the command line
+ if (cli.Nix) {
+ tpm = new CommandTpm(CommandTpm.Devices.NIX);
+ } else if (cli.Tcp && cli.Ip != null) {
+ string[] split = cli.Ip.Split(":");
+ if (split.Length == 2) {
+ tpm = new CommandTpm(cli.Sim, split[0], Int32.Parse(split[1]));
+ Log.Debug("Connected to TPM via TCP at " + cli.Ip);
+ } else {
+ Log.Error("ip input should have the format servername:port. The given input was '" + cli.Ip + "'.");
+ }
+ } else if (cli.Win) {
+ tpm = new CommandTpm(CommandTpm.Devices.WIN);
+ }
+
+ // If command line not set, check if auto detect is enabled
+ if ((tpm == null) && settings.IsAutoDetectTpmEnabled()) {
+ Log.Debug("Auto Detect TPM is Enabled. Starting search for the TPM.");
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
+ try {
+ tpm = new CommandTpm(CommandTpm.Devices.WIN);
+ Log.Debug("Auto Detect found a WIN TPM Device.");
+ } catch (Exception) {
+ Log.Debug("No WIN TPM Device found by auto detect.");
+ }
+ } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
+ try {
+ tpm = new CommandTpm(CommandTpm.Devices.NIX);
+ Log.Debug("Auto Detect found a Linux TPM Device.");
+ } catch (Exception) {
+ Log.Debug("No Linux TPM Device found by auto detect.");
+ }
+ }
+
+ // if tpm still null, try set up TcpTpmDevice on sim, catch exception
+ if (tpm == null) {
+ try {
+ string[] split = CommandTpm.DefaultSimulatorNamePort.Split(":");
+ tpm = new CommandTpm(true, split[0], Int32.Parse(split[1]));
+ Log.Debug("Auto Detect found a TPM simulator at " + CommandTpm.DefaultSimulatorNamePort + ".");
+ } catch (Exception) {
+ Log.Debug("No TPM simulator found by auto detect.");
+ }
+ }
+ } else if ((tpm != null) && settings.IsAutoDetectTpmEnabled()) {
+ Log.Debug("Auto detect TPM was enabled in settings, but command line options were also given. Using command line options.");
+ }
+
+ // If TPM is still not set up, offer help message
+ if (tpm == null) {
+ Log.Fatal(
+ "To connect to a TPM device on Windows, add the command line argument --win\n" +
+ "To connect to a TPM device on LINUX, add the command line argument --nix\n" +
+ "To connect to a TPM via TCP, add the command line arguments --tcp :\n" +
+ "To connect to a TPM simulator at the default TCP socket of " + CommandTpm.DefaultSimulatorNamePort + ", add the command line arguments --tcp --sim\n" +
+ "To connect to a TPM simulator at any other socket, add the command line arguments --tcp --sim :\n");
+ }
+ return tpm;
+ }
+
+ public void UseBuiltInClient(string addr) {
+ acaClient = new Client(addr);
+ }
+
+ public void SetClient(IHirsAcaClient client) {
+ acaClient = client;
+ }
+
+ public void UseClassicDeviceInfoCollector() {
+ deviceInfoCollector = new ClassicDeviceInfoCollector(settings);
+ }
+
+ public void SetDeviceInfoCollector(IHirsDeviceInfoCollector collector) {
+ if (collector == null) {
+ UseClassicDeviceInfoCollector();
+ } else {
+ deviceInfoCollector = collector;
+ }
+ }
+
+ public async Task Provision(IHirsAcaTpm tpm) {
+ ClientExitCodes result = ClientExitCodes.SUCCESS;
+ if (tpm != null) {
+ Log.Information("--> Provisioning");
+ Log.Information("----> Gathering Endorsement Key Certificate.");
+ byte[] ekc = tpm.GetCertificateFromNvIndex(CommandTpm.DefaultEkcNvIndex);
+ if (ekc.Length == 0) {
+ Log.Information("------> No Endorsement Key Certificate found at the expected index. The ACA may have one uploaded for this TPM.");
+ }
+ Log.Debug("Checking EK PUBLIC");
+ tpm.CreateEndorsementKey(CommandTpm.DefaultEkHandle); // Will not create key if obj already exists at handle
+ byte[] ekPublicArea = tpm.ReadPublicArea(CommandTpm.DefaultEkHandle, out byte[] name, out byte[] qualifiedName);
+
+ Log.Information("----> " + (cli.ReplaceAK ? "Creating new" : "Verifying existence of") + " Attestation Key.");
+ tpm.CreateAttestationKey(CommandTpm.DefaultEkHandle, CommandTpm.DefaultAkHandle, cli.ReplaceAK);
+
+ Log.Debug("Gathering AK PUBLIC.");
+ byte[] akPublicArea = tpm.ReadPublicArea(CommandTpm.DefaultAkHandle, out name, out qualifiedName);
+
+ List pcs = null, baseRims = null, supportRimELs = null, supportRimPCRs = null;
+ if (settings.HasEfiPrefix()) {
+ Log.Information("----> Gathering artifacts from EFI.");
+ pcs = settings.gatherPlatformCertificatesFromEFI();
+ baseRims = settings.gatherRIMBasesFromEFI();
+ supportRimELs = settings.gatherSupportRIMELsFromEFI();
+ supportRimPCRs = settings.gatherSupportRIMPCRsFromEFI();
+ }
+
+ Log.Debug("Setting up the Client.");
+ Uri acaAddress = settings.aca_address_port;
+ if (acaClient == null) {
+ UseBuiltInClient(acaAddress.AbsoluteUri);
+ }
+
+ Log.Information("----> Collecting device information.");
+ DeviceInfo dv = deviceInfoCollector.CollectDeviceInfo(acaAddress.AbsoluteUri);
+ if (baseRims != null) {
+ foreach (byte[] baseRim in baseRims) {
+ dv.Swidfile.Add(ByteString.CopyFrom(baseRim));
+ }
+ }
+ if (supportRimELs != null) {
+ foreach (byte[] supportRimEL in supportRimELs) {
+ dv.Logfile.Add(ByteString.CopyFrom(supportRimEL));
+ }
+ }
+ if (supportRimPCRs != null) {
+ foreach (byte[] supportRimPCR in supportRimPCRs) {
+ dv.Logfile.Add(ByteString.CopyFrom(supportRimPCR));
+ }
+ }
+
+ Log.Debug("Gathering hardware component information:");
+ string manifest = "";
+ if (settings.HasHardwareManifestPlugins()) {
+ manifest = settings.RunHardwareManifestCollectors();
+ } else if (settings.HasPaccorOutputFromFile()) {
+ manifest = settings.paccor_output;
+ } else {
+ Log.Warning("No hardware collectors nor paccor output file were identified.");
+ }
+ Log.Debug("Hardware component information that will be sent to the ACA: " + manifest);
+
+ Log.Debug("Gathering the event log.");
+ byte[] eventLog;
+ if (settings.HasEventLogFromFile()) {
+ Log.Debug(" Using the event log identified in settings.");
+ eventLog = settings.event_log;
+ } else {
+ Log.Debug(" Attempting to collect the event log from the system.");
+ eventLog = tpm.GetEventLog();
+ }
+
+ if (eventLog != null) {
+ Log.Debug("Event log gathered is " + eventLog.Length + " bytes.");
+ dv.Livelog = ByteString.CopyFrom(eventLog);
+ }
+
+ Log.Debug("Gathering PCR data from the TPM.");
+ string pcrsList, pcrsSha1, pcrsSha256;
+ CommandTpm.FormatPcrValuesForAca(tpm.GetPcrList(Tpm2Lib.TpmAlgId.Sha1), "sha1", out pcrsSha1);
+ CommandTpm.FormatPcrValuesForAca(tpm.GetPcrList(Tpm2Lib.TpmAlgId.Sha256), "sha256", out pcrsSha256);
+ pcrsList = pcrsSha1 + pcrsSha256;
+ Log.Debug("Result of formatting pcr values for the ACA:");
+ Log.Debug("\n" + pcrsList);
+ dv.Pcrslist = ByteString.CopyFromUtf8(pcrsList);
+
+ Log.Debug("Create identity claim");
+ IdentityClaim idClaim = acaClient.CreateIdentityClaim(dv, akPublicArea, ekPublicArea, ekc, pcs, manifest);
+
+ Log.Information("----> Sending identity claim to Attestation CA");
+ IdentityClaimResponse icr = await acaClient.PostIdentityClaim(idClaim);
+ Log.Information("----> Received response. Attempting to decrypt nonce");
+ if (icr.HasStatus) {
+ if (icr.Status == ResponseStatus.Pass) {
+ Log.Debug("The ACA accepted the identity claim.");
+ } else {
+ Log.Debug("The ACA did not accept the identity claim. See details on the ACA.");
+ result = ClientExitCodes.PASS_1_STATUS_FAIL;
+ return (int)result;
+ }
+ }
+
+ byte[] integrityHMAC = null, encIdentity = null, encryptedSecret = null;
+ if (icr.HasCredentialBlob) {
+ byte[] credentialBlob = icr.CredentialBlob.ToByteArray(); // look for the nonce
+ Log.Debug("ACA delivered IdentityClaimResponse credentialBlob " + BitConverter.ToString(credentialBlob));
+ int credentialBlobLen = credentialBlob[0] | (credentialBlob[1] << 8);
+ int integrityHmacLen = (credentialBlob[2] << 8) | credentialBlob[3];
+ integrityHMAC = new byte[integrityHmacLen];
+ Array.Copy(credentialBlob, 4, integrityHMAC, 0, integrityHmacLen);
+ int encIdentityLen = credentialBlobLen - integrityHmacLen - 2;
+ encIdentity = new byte[encIdentityLen];
+ Array.Copy(credentialBlob, 4 + integrityHmacLen, encIdentity, 0, encIdentityLen);
+ // The following offsets are bound tightly to the way makecredential is implemented on the ACA.
+ int encryptedSecretLen = credentialBlob[134] | (credentialBlob[135] << 8);
+ encryptedSecret = new byte[encryptedSecretLen];
+ Array.Copy(credentialBlob, 136, encryptedSecret, 0, encryptedSecretLen);
+ Log.Debug("Prepared values to give to activateCredential.");
+ Log.Debug(" integrityHMAC: " + BitConverter.ToString(integrityHMAC));
+ Log.Debug(" encIdentity: " + BitConverter.ToString(encIdentity));
+ Log.Debug(" encryptedSecret: " + BitConverter.ToString(encryptedSecret));
+ } else {
+ result = ClientExitCodes.MAKE_CREDENTIAL_BLOB_MALFORMED;
+ Log.Error("The response from the ACA did not contain a CredentialBlob.");
+ }
+
+ if (integrityHMAC != null && encIdentity != null && encryptedSecret != null) {
+ Log.Debug("Executing activateCredential.");
+ byte[] recoveredSecret = tpm.ActivateCredential(CommandTpm.DefaultAkHandle, CommandTpm.DefaultEkHandle, integrityHMAC, encIdentity, encryptedSecret);
+ Log.Debug("Gathering quote.");
+ uint[] selectPcrs = null;
+ if (icr.HasPcrMask) {
+ // For now, the ACA will send a comma separated selection of PCRs as a string
+ try {
+ selectPcrs = icr.PcrMask.Split(',').Select(uint.Parse).ToList().ToArray();
+ } catch (Exception) {
+ Log.Warning("PcrMask was included in the IdentityClaimResponse, but could not be parsed." +
+ "Collecting quote over default PCR selection.");
+ Log.Debug("This PcrMask could not be parsed: " + icr.PcrMask);
+ }
+ }
+ tpm.GetQuote(CommandTpm.DefaultAkHandle, Tpm2Lib.TpmAlgId.Sha256, recoveredSecret, out CommandTpmQuoteResponse ctqr, selectPcrs);
+ Log.Information("----> Nonce successfully decrypted. Sending attestation certificate request");
+ CertificateRequest akCertReq = acaClient.CreateAkCertificateRequest(recoveredSecret, ctqr);
+ byte[] certificate;
+ Log.Debug("Communicate certificate request to the ACA.");
+ CertificateResponse cr = await acaClient.PostCertificateRequest(akCertReq);
+ Log.Debug("Response received from the ACA regarding the certificate request.");
+ if (cr.HasStatus) {
+ if (cr.Status == ResponseStatus.Pass) {
+ Log.Debug("ACA returned a positive response to the Certificate Request.");
+ } else {
+ Log.Debug("The ACA did not return any certificates. See details on the ACA.");
+ result = ClientExitCodes.PASS_2_STATUS_FAIL;
+ return (int)result;
+ }
+ }
+ if (cr.HasCertificate) {
+ certificate = cr.Certificate.ToByteArray(); // contains certificate
+ Log.Debug("Printing attestation key certificate: " + BitConverter.ToString(certificate));
+ }
+ } else {
+ result = ClientExitCodes.MAKE_CREDENTIAL_BLOB_MALFORMED;
+ Log.Error("Credential elements could not be extracted from the ACA's response.");
+ }
+ } else {
+ result = ClientExitCodes.TPM_ERROR;
+ Log.Error("Could not provision because the TPM object was null.");
+ }
+ return (int)result;
+ }
+
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirs/src/tpm/CommandTpm.cs b/HIRS_Provisioner.NET/hirs/src/tpm/CommandTpm.cs
new file mode 100644
index 00000000..82af26a3
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/tpm/CommandTpm.cs
@@ -0,0 +1,497 @@
+using Serilog;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Security.Principal;
+using Tpm2Lib;
+
+namespace hirs {
+ public class CommandTpm : IHirsAcaTpm {
+ public enum Devices {
+ NIX,
+ TCP,
+ WIN
+ }
+
+ ///
+ /// If using a TCP connection, the default DNS name/IP address for the
+ /// simulator.
+ ///
+ public const string DefaultSimulatorNamePort = "127.0.0.1:2321";
+
+ public const uint DefaultEkcNvIndex = 0x1c00002;
+ public const uint DefaultEkHandle = 0x81010001;
+ public const uint DefaultAkHandle = 0x81010002;
+
+ private readonly Tpm2 tpm;
+
+ private readonly Boolean simulator;
+
+ private List sessionTracking = new List();
+
+ /**
+ * For TCP TpmDevices
+ */
+ public CommandTpm(Boolean sim, string ip, int port) {
+ simulator = sim;
+ Tpm2Device tpmDevice = new TcpTpmDevice(ip, port);
+ tpm = TpmSetupByType(tpmDevice);
+ }
+
+ /**
+ * For a TPM device on Linux and Windows
+ */
+ public CommandTpm(Devices dev) {
+ Tpm2Device tpmDevice = null;
+ switch (dev) {
+ case Devices.NIX:
+ // LinuxTpmDevice will first try to connect tpm2-abrmd and second try to connect directly to device
+ StringWriter writer = new();
+ Console.SetOut(writer);
+ // The LinuxTpmDevice will print to Console/Stdout an error when it cannot find a resource manager
+ // This will redirect the Console messages
+ tpmDevice = new LinuxTpmDevice();
+ // Reset the Console messages
+ Console.SetOut(new StreamWriter(Console.OpenStandardOutput()));
+ break;
+ case Devices.WIN:
+ tpmDevice = new TbsDevice();
+ break;
+ default:
+ Log.Error("Unknown option selected in CommandTpm(Devices) constructor.");
+ break;
+ }
+ tpm = TpmSetupByType(tpmDevice);
+ }
+
+ public CommandTpm(Tpm2 tpm) {
+ this.tpm = tpm;
+ }
+
+ ~CommandTpm() {
+ if (tpm != null) {
+ tpm.Dispose();
+ }
+ }
+
+ public byte[] GetCertificateFromNvIndex(uint index) {
+ Log.Debug("GetCertificateFromNvIndex 0x" + index.ToString("X"));
+ byte[] certificate = Array.Empty();
+
+ TpmHandle nvHandle = new(index);
+ try {
+ byte[] nvName; // not used for this function. have to collect from NvReadPublic.
+ NvPublic obj = tpm.NvReadPublic(nvHandle, out nvName);
+ if (obj != null) {
+ byte[] indexData = NvBufferedRead(TpmHandle.RhOwner, nvHandle, obj.dataSize, 0);
+ if (indexData != null) {
+ certificate = ExtractFirstCertificate(indexData); // the nvIndex could contain random fill around the certificate
+ if (certificate != null) {
+ Log.Debug("GetCertificateFromNvIndex: Read: " + BitConverter.ToString(certificate));
+ } else {
+ Log.Debug("GetCertificateFromNvIndex: No certificate found within data at index.");
+ }
+ } else {
+ Log.Debug("GetCertificateFromNvIndex: Could not read any data.");
+ }
+ } else {
+ Log.Debug("GetCertificateFromNvIndex: Nothing found at index: " + DefaultEkcNvIndex);
+ }
+ } catch (TpmException e) {
+ Log.Debug(e, "GetCertificateFromNvIndex TPM error");
+ }
+ return certificate;
+ }
+
+ private byte[] NvBufferedRead(TpmHandle authHandle, TpmHandle nvIndex, ushort size, ushort offset) {
+ ushort maxReadSize = 256;
+ byte[] buffer = new byte[size];
+
+ ushort ptr = 0;
+ while (offset < size) {
+ int q = Math.DivRem(size - offset, maxReadSize, out int r);
+ ushort sizeToRead = q > 0 ? maxReadSize : (ushort)r;
+ byte[] block = tpm.NvRead(authHandle, nvIndex, sizeToRead, offset);
+ Array.Copy(block, 0, buffer, ptr, sizeToRead);
+ offset += sizeToRead;
+ ptr += sizeToRead;
+ }
+ return buffer;
+ }
+
+ private static byte[] ExtractFirstCertificate(byte[] data) {
+ byte[] extracted = null;
+
+ if (data != null) {
+ // search for first instance of 30 82
+ int pos = 0;
+ bool found = false;
+ while (pos < (data.Length - 1)) {
+ if (data[pos] == 0x30) {
+ if (data[pos + 1] == 0x82) {
+ found = true;
+ break;
+ }
+ }
+ pos++;
+ }
+
+ // find the size of the structure.
+ // 30 82 means the size will be described in next 2 bytes
+ // Data from NV should be BIG ENDIAN since 3082 was found in step 1.
+ int size = 0;
+ if (found && data.Length > (pos + 3)) {
+ byte[] sizeBuffer = new byte[2];
+ sizeBuffer[0] = data[pos + 3];
+ sizeBuffer[1] = data[pos + 2];
+ size = 4 + BitConverter.ToInt16(sizeBuffer); // 4 bytes added to final count for pos+3
+ }
+
+ // copy the structure to the output buffer
+ if (size > 0) {
+ extracted = new byte[size];
+ Array.Copy(data, pos, extracted, 0, size);
+ }
+ }
+
+ return extracted;
+ }
+
+ // allows client to access the readpublic function
+ public TpmPublic ReadPublicArea(uint handleInt, out byte[] name, out byte[] qualifiedName) {
+ TpmHandle handle = new(handleInt);
+ TpmPublic obj = null;
+ name = null;
+ qualifiedName = null;
+ try {
+ obj = tpm.ReadPublic(handle, out byte[] localName, out byte[] localQualifiedName);
+ name = localName;
+ qualifiedName = localQualifiedName;
+ } catch {
+ // Don't think I need an exception here. Let the calling method throw if needed.
+ }
+ return obj;
+ }
+
+ public static TpmPublic GenerateEKTemplateL1() {
+ TpmAlgId nameAlg = TpmAlgId.Sha256;
+ ObjectAttr attributes = ObjectAttr.FixedTPM | ObjectAttr.FixedParent | ObjectAttr.SensitiveDataOrigin | ObjectAttr.AdminWithPolicy | ObjectAttr.Restricted | ObjectAttr.Decrypt;
+ byte[] auth_policy = { // Template L-1
+ 0x83, 0x71, 0x97, 0x67, 0x44, 0x84,
+ 0xB3, 0xF8, 0x1A, 0x90, 0xCC, 0x8D,
+ 0x46, 0xA5, 0xD7, 0x24, 0xFD, 0x52,
+ 0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64,
+ 0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14,
+ 0x69, 0xAA
+ };
+ // ASYM: RSA 2048 with NULL scheme, SYM: AES-128 with CFB mode
+ RsaParms rsa = new(new SymDefObject(TpmAlgId.Aes, 128, TpmAlgId.Cfb), new NullAsymScheme(), 2048, 0);
+ // unique buffer must be filled with 0 for the EK Template L-1.
+ byte[] zero256 = new byte[256];
+ Array.Fill(zero256, 0x00);
+ Tpm2bPublicKeyRsa unique = new(zero256);
+ TpmPublic inPublic = new(nameAlg, attributes, auth_policy, rsa, unique);
+ return inPublic;
+ }
+
+ public void CreateEndorsementKey(uint ekHandleInt) {
+ TpmHandle ekHandle = new(ekHandleInt);
+
+ TpmPublic existingObject;
+ try {
+ existingObject = tpm.ReadPublic(ekHandle, out byte[] name, out byte[] qualifiedName);
+ Log.Debug("EK already exists.");
+ return;
+ } catch (TpmException) {
+ Log.Debug("Verified EK does not exist at expected handle. Creating EK.");
+ }
+
+ SensitiveCreate inSens = new(); // key password (no params = no key password)
+ TpmPublic inPublic = CommandTpm.GenerateEKTemplateL1();
+
+ TpmHandle newTransientEkHandle = tpm.CreatePrimary(TpmRh.Endorsement, inSens, inPublic,
+ new byte[] { }, new PcrSelection[] { }, out TpmPublic outPublic,
+ out CreationData creationData, out byte[] creationHash, out TkCreation ticket);
+
+ Log.Debug("New EK Handle: " + BitConverter.ToString(newTransientEkHandle));
+ Log.Debug("New EK PUB Name: " + BitConverter.ToString(outPublic.GetName()));
+ Log.Debug("New EK PUB 2BREP: " + BitConverter.ToString(outPublic.GetTpm2BRepresentation()));
+
+ // Make the object persistent
+ tpm.EvictControl(TpmRh.Owner, newTransientEkHandle, ekHandle);
+ Log.Debug("Successfully made the new EK persistent at handle " + BitConverter.ToString(ekHandle) + ".");
+
+ tpm.FlushContext(newTransientEkHandle);
+ Log.Debug("Flushed the context for the transient EK.");
+ }
+
+ private static RsaParms AkRsaParms() {
+ TpmAlgId digestAlg = TpmAlgId.Sha256;
+ RsaParms parms = new(new SymDefObject(TpmAlgId.Null, 0, TpmAlgId.Null), new SchemeRsassa(digestAlg), 2048, 0);
+ return parms;
+ }
+
+ private static ObjectAttr AkAttributes() {
+ ObjectAttr attrib = ObjectAttr.Restricted | ObjectAttr.Sign | ObjectAttr.FixedParent | ObjectAttr.FixedTPM
+ | ObjectAttr.SensitiveDataOrigin | ObjectAttr.UserWithAuth;
+ return attrib;
+ }
+
+ private static TpmPublic GenerateAKTemplate(TpmAlgId nameAlg) {
+ RsaParms rsa = AkRsaParms();
+ ObjectAttr attributes = AkAttributes();
+ TpmPublic inPublic = new(nameAlg, attributes, null, rsa, new Tpm2bPublicKeyRsa());
+ return inPublic;
+ }
+
+ public void CreateAttestationKey(uint ekHandleInt, uint akHandleInt, bool replace) {
+ TpmHandle ekHandle = new(ekHandleInt);
+ TpmHandle akHandle = new(akHandleInt);
+
+ TpmPublic existingObject = null;
+ try {
+ existingObject = tpm.ReadPublic(akHandle, out byte[] name, out byte[] qualifiedName);
+ } catch { }
+
+ if (!replace && existingObject != null) {
+ // Do Nothing
+ Log.Debug("AK exists at expected handle. Flag to not replace the AK is set in the settings file.");
+ return;
+ } else if (replace && existingObject != null) {
+ // Clear the object and continue
+ tpm.EvictControl(TpmRh.Owner, akHandle, akHandle);
+ Log.Debug("Removed previous AK.");
+ }
+
+ // Create a new key and make it persistent at akHandle
+ TpmAlgId nameAlg = TpmAlgId.Sha256;
+
+ SensitiveCreate inSens = new();
+ TpmPublic inPublic = GenerateAKTemplate(nameAlg);
+
+ var policyEK = new PolicyTree(nameAlg);
+ policyEK.SetPolicyRoot(new TpmPolicySecret(TpmRh.Endorsement, false, 0, null, null));
+
+ AuthSession sessEK = tpm.StartAuthSessionEx(TpmSe.Policy, nameAlg);
+ sessEK.RunPolicy(tpm, policyEK);
+
+ TpmPrivate kAK = tpm[sessEK].Create(ekHandle, inSens, inPublic, null, null, out TpmPublic outPublic,
+ out CreationData creationData, out byte[] creationHash, out TkCreation ticket);
+
+ Log.Debug("New AK PUB Name: " + BitConverter.ToString(outPublic.GetName()));
+ Log.Debug("New AK PUB 2BREP: " + BitConverter.ToString(outPublic.GetTpm2BRepresentation()));
+ Log.Debug("New AK PUB unique: " + BitConverter.ToString((Tpm2bPublicKeyRsa)(outPublic.unique)));
+
+ tpm.FlushContext(sessEK);
+
+ sessEK = tpm.StartAuthSessionEx(TpmSe.Policy, nameAlg);
+ sessEK.RunPolicy(tpm, policyEK);
+
+ TpmHandle hAK = tpm[sessEK].Load(ekHandle, kAK, outPublic);
+
+ tpm.EvictControl(TpmRh.Owner, hAK, akHandle);
+ Log.Debug("Created and persisted new AK at handle 0x" + akHandle.handle.ToString("X") + ".");
+
+ tpm.FlushContext(sessEK);
+ }
+
+ public Tpm2bDigest[] GetPcrList(TpmAlgId pcrBankDigestAlg, uint[] pcrs = null) {
+ if (pcrs == null) {
+ pcrs = new uint[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };
+ }
+ Log.Debug("Retrieving PCR LIST for pcrs: " + string.Join(",", pcrs));
+
+ PcrSelection[] pcrSelection = new PcrSelection[] {
+ new PcrSelection(pcrBankDigestAlg, pcrs, (uint)pcrs.Length)
+ };
+ Tpm2bDigest[] pcrValues = MultiplePcrRead(pcrSelection[0]);
+ return pcrValues;
+ }
+
+ // qualifying data hashed with SHA256, quote set to use RSASSA scheme with SHA256-- TODO: enable usage of ECC and other digest alg
+ // if no pcrs are requested (parameter pcrs == null), all pcrs wil be returned. The function does not check if any pcr is available before asking for the quote
+ public void GetQuote(uint akHandleInt, TpmAlgId pcrBankDigestAlg, byte[] nonce, out CommandTpmQuoteResponse ctqr, uint[] pcrs = null) {
+ TpmHandle akHandle = new(akHandleInt);
+
+ if (pcrs == null) {
+ pcrs = new uint[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };
+ }
+ Log.Debug("Retrieving TPM quote for pcrs: " + string.Join(",", pcrs));
+
+
+ PcrSelection[] pcrSelection = new PcrSelection[] {
+ new PcrSelection(pcrBankDigestAlg, pcrs, (uint)pcrs.Length)
+ };
+
+ // for test only
+ TpmHash qualifyingData = TpmHash.FromData(TpmAlgId.Sha256, nonce);
+
+ Attest localQuotedInfo = tpm.Quote(akHandle, qualifyingData, new SchemeRsassa(TpmAlgId.Sha256), pcrSelection, out ISignatureUnion localQuoteSig);
+ Tpm2bDigest[] localPcrValues = MultiplePcrRead(pcrSelection[0]);
+
+ TpmPublic pub = tpm.ReadPublic(akHandle, out byte[] name, out byte[] qualifiedName);
+
+ bool verified = pub.VerifyQuote(TpmAlgId.Sha256, pcrSelection, localPcrValues, qualifyingData, localQuotedInfo, localQuoteSig, qualifiedName);
+ Log.Debug("Quote " + (verified ? "was" : "was not") + " verified.");
+ ctqr = null;
+ if (verified) {
+ ctqr = new CommandTpmQuoteResponse(localQuotedInfo, localQuoteSig, localPcrValues);
+ }
+ }
+
+ public Tpm2bDigest[] MultiplePcrRead(PcrSelection pcrs) {
+ if (pcrs == null) {
+ return Array.Empty();
+ }
+
+ List pcrValues = new();
+
+ const int MAX_NUM_PCRS_PER_READ = 8; // anticipate TPM has a limit on the number of PCRs read at a time
+ Queue selectedPcrs = new(pcrs.GetSelectedPcrs());
+
+ while (selectedPcrs.Count() > 0) {
+ int numPcrsToRead = (selectedPcrs.Count > MAX_NUM_PCRS_PER_READ) ? MAX_NUM_PCRS_PER_READ : selectedPcrs.Count;
+
+ List subset = new();
+ for (int i = 0; i < numPcrsToRead; i++) {
+ subset.Add(selectedPcrs.Dequeue());
+ }
+ PcrSelection selection = new(pcrs.hash, subset.ToArray());
+ PcrSelection[] pcrsIn = { // Need to wrap into an array
+ selection
+ };
+ uint count = tpm.PcrRead(pcrsIn, out PcrSelection[] pcrsOut, out Tpm2bDigest[] pcrValuesRetrieved); // TODO incorporate check on count to handle race condition
+ if (pcrValuesRetrieved != null && pcrValuesRetrieved.Length > 0) {
+ pcrValues.AddRange(pcrValuesRetrieved);
+ }
+ }
+ return pcrValues.ToArray();
+ }
+
+ public byte[] ActivateCredential(uint akHandleInt, uint ekHandleInt, byte[] integrityHMAC, byte[] encIdentity, byte[] encryptedSecret) {
+ byte[] recoveredSecret;
+
+ TpmHandle ekHandle = new(ekHandleInt);
+ TpmHandle akHandle = new(akHandleInt);
+
+ IdObject credentialBlob = new(integrityHMAC, encIdentity);
+
+ TpmAlgId nameAlg = TpmAlgId.Sha256;
+ var policyEK = new PolicyTree(nameAlg);
+ policyEK.SetPolicyRoot(new TpmPolicySecret(TpmRh.Endorsement, false, 0, null, null));
+
+ AuthSession sessEK = tpm.StartAuthSessionEx(TpmSe.Policy, nameAlg);
+ sessEK.RunPolicy(tpm, policyEK);
+
+ AuthSession sessAK = tpm.StartAuthSessionEx(TpmSe.None, nameAlg);
+ recoveredSecret = tpm[sessAK, sessEK].ActivateCredential(akHandle, ekHandle, credentialBlob, encryptedSecret);
+ Log.Debug("encryptedSecret: " + BitConverter.ToString(encryptedSecret));
+
+ tpm.FlushContext(sessEK);
+ tpm.FlushContext(sessAK);
+
+ return recoveredSecret;
+ }
+
+ public byte[] GetEventLog() {
+ byte[] eventLog = null;
+ if (tpm._GetUnderlyingDevice().GetType() == typeof(TbsDevice)) { // if windows TPM
+ if (!TbsWrapper.GetEventLog(out eventLog)) {
+ eventLog = null;
+ Log.Debug("Could not retrieve the event log from Tbsi");
+ }
+ }
+
+ if (eventLog == null) {
+ if (tpm._GetUnderlyingDevice().GetType() == typeof(TcpTpmDevice) && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // if TCP TPM on Windows
+ // attempt to read from the measuredboot log folder
+ string windir = System.Environment.GetEnvironmentVariable("windir");
+ string path = windir + "\\Logs\\MeasuredBoot\\";
+ DirectoryInfo directory = new(path);
+ FileInfo mostRecent = directory.GetFiles()
+ .OrderByDescending(f => f.LastWriteTime)
+ .FirstOrDefault();
+ if (mostRecent != null && File.Exists(mostRecent.FullName)) {
+ eventLog = File.ReadAllBytes(mostRecent.FullName);
+ } else {
+ Log.Debug("Windows boot configuration log does not exist at expected location");
+ }
+ } else if (tpm._GetUnderlyingDevice().GetType() == typeof(LinuxTpmDevice) || (tpm._GetUnderlyingDevice().GetType() == typeof(TcpTpmDevice) && RuntimeInformation.IsOSPlatform(OSPlatform.Linux))) { // if Linux TPM or TCP TPM on Linux
+ // attempt to read from the binary_bios_measurements file created by the kernel
+ string path = "/sys/kernel/security/tpm0/binary_bios_measurements";
+ if (File.Exists(path)) {
+ eventLog = File.ReadAllBytes(path);
+ } else {
+ Log.Debug("Linux bios measurements log does not exist at expected location");
+ }
+ }
+ }
+ return eventLog;
+ }
+
+ private Tpm2 TpmSetupByType(Tpm2Device tpmDevice) {
+ try {
+ tpmDevice.Connect();
+ } catch (AggregateException e) {
+ Log.Error(e, "tpmSetupByType: Error connecting to tpmDevice");
+ throw e;
+ }
+
+ Tpm2 tpm = new(tpmDevice);
+ if (tpmDevice is TcpTpmDevice) {
+ //
+ // If we are using the simulator, we have to do a few things the
+ // firmware would usually do. These actions have to occur after
+ // the connection has been established.
+ //
+ if (simulator) {
+ uint rc = 0;
+ try {
+ rc = tpm.PcrRead(new PcrSelection[] { new PcrSelection(TpmAlgId.Sha1, new uint[] { 0 }) }, out _, out _);
+ } catch (TpmException e) {
+ if (e.RawResponse == TpmRc.Initialize) {
+ Log.Debug("TPM simulator not initialized. Running startup with clear.");
+ tpmDevice.PowerCycle();
+ tpm.Startup(Su.Clear);
+ } else {
+ Log.Debug("TPM simulator already initialized. Skipping TPM2_Startup.");
+ }
+ }
+ }
+ } else if (tpmDevice is TbsDevice) {
+ /**
+ * For device TPMs on Windows, we have to use Windows Identity
+ */
+ // ask windows for owner auth
+ byte[] ownerAuth;
+ if (TbsWrapper.GetOwnerAuthFromOS(out ownerAuth)) {
+ // if found, ownerauth will be delivered with the tpm object
+ tpm.OwnerAuth = ownerAuth;
+ } else {
+ Log.Warning("Could not retrieve owner auth from registry. Trying empty auth.");
+ }
+ } else if (tpmDevice is LinuxTpmDevice) {
+
+ }
+ return tpm;
+ }
+
+ //TODO Fix ACA so that I don't have to re-format data in this way
+ public static void FormatPcrValuesForAca(Tpm2bDigest[] pcrValues, string algName, out string pcrValuesStr) {
+ pcrValuesStr = "";
+ if (pcrValues != null && pcrValues.Length > 0) {
+ pcrValuesStr = algName + " :\n";
+ }
+ for (int i = 0; i < pcrValues.Length; i++) {
+ Tpm2bDigest pcrValue = pcrValues[i];
+ pcrValuesStr += " " + i;
+ pcrValuesStr += ((i > 9) ? " " : " ") + ": ";
+ pcrValuesStr += BitConverter.ToString(pcrValue.buffer).Replace("-", "").ToLower().Trim() + "\n";
+ }
+ }
+ }
+}
+
diff --git a/HIRS_Provisioner.NET/hirs/src/tpm/CommandTpmQuoteResponse.cs b/HIRS_Provisioner.NET/hirs/src/tpm/CommandTpmQuoteResponse.cs
new file mode 100644
index 00000000..a0556dff
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/tpm/CommandTpmQuoteResponse.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Tpm2Lib;
+
+namespace hirs {
+ public class CommandTpmQuoteResponse : Tpm2QuoteResponse {
+
+ private readonly Attest quoted1;
+ private readonly ISignatureUnion signature1;
+ private readonly Tpm2bDigest[] pcrValues1;
+
+ public CommandTpmQuoteResponse(Attest quoted, ISignatureUnion signature, Tpm2bDigest[] pcrValues) {
+ quoted1 = quoted;
+ signature1 = signature;
+ pcrValues1 = pcrValues != null ? (Tpm2bDigest[])pcrValues.Clone() : null;
+ }
+ public new Attest quoted => quoted1;
+ public new ISignatureUnion signature => signature1;
+ public Tpm2bDigest[] pcrValues => pcrValues1;
+
+ //TODO Fix ACA so that I don't have to re-format data in this way
+ public static void formatQuoteInfoSigForAca(Attest quoteInfo, ISignatureUnion quoteSig, out string quoteInfoSigStr) {
+ quoteInfoSigStr = "quoted:";
+ quoteInfoSigStr += BitConverter.ToString(quoteInfo.GetTpm2BRepresentation()).Replace("-", "").ToLower().Trim();
+ quoteInfoSigStr += "signature:";
+ quoteInfoSigStr += BitConverter.ToString(((SignatureRsassa)quoteSig).GetTpm2BRepresentation()).Replace("-", "").ToLower().Trim();
+ }
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirs/src/tpm/IHirsAcaTpm.cs b/HIRS_Provisioner.NET/hirs/src/tpm/IHirsAcaTpm.cs
new file mode 100644
index 00000000..9749ee9f
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirs/src/tpm/IHirsAcaTpm.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Tpm2Lib;
+
+namespace hirs {
+ public interface IHirsAcaTpm {
+ byte[] GetCertificateFromNvIndex(uint index);
+ TpmPublic ReadPublicArea(uint handleInt, out byte[] name, out byte[] qualifiedName);
+ void CreateEndorsementKey(uint ekHandleInt);
+ void CreateAttestationKey(uint ekHandleInt, uint akHandleInt, bool replace);
+ Tpm2bDigest[] GetPcrList(TpmAlgId pcrBankDigestAlg, uint[] pcrs = null);
+ void GetQuote(uint akHandleInt, TpmAlgId pcrBankDigestAlg, byte[] nonce, out CommandTpmQuoteResponse ctqr, uint[] pcrs = null);
+ byte[] ActivateCredential(uint akHandleInt, uint ekHandleInt, byte[] integrityHMAC, byte[] encIdentity, byte[] encryptedSecret);
+ byte[] GetEventLog();
+
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/appsettings.json b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/appsettings.json
new file mode 100644
index 00000000..ab1f646f
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/appsettings.json
@@ -0,0 +1,37 @@
+{
+ "auto_detect_tpm": "FALSE",
+ "aca_address_port": "https://127.0.0.1:8443",
+ "efi_prefix": "./Resources/test/settings_test/efi/",
+ "paccor_output_file": "./Resources/test/settings_test/component_list",
+ "event_log_file": "./Resources/test/settings_test/event_log",
+ "hardware_manifest_collectors": "",
+
+ "Serilog": {
+ "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
+ "Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId" ],
+ "MinimumLevel": {
+ "Default": "Debug",
+ "Override": {
+ "Microsoft": "Warning",
+ "System": "Warning"
+ }
+ },
+ "WriteTo": [
+ {
+ "Name": "Console",
+ "Args": {
+ "outputTemplate": "[{Timestamp:HH:mm:ss} {SourceContext} [{Level}] {Message}{NewLine}{Exception}",
+ "theme": "Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Grayscale, Serilog.Sinks.Console"
+ }
+ },
+ {
+ "Name": "File",
+ "Args": {
+ "path": "hirs.log",
+ "rollingInterval": "Day",
+ "retainedFileCountLimit": 5
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/component_list b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/component_list
new file mode 100644
index 00000000..3ff542ef
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/component_list
@@ -0,0 +1 @@
+component list
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/cert/Mfg.Serial.BASE.cer b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/cert/Mfg.Serial.BASE.cer
new file mode 100644
index 00000000..06c9e62d
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/cert/Mfg.Serial.BASE.cer
@@ -0,0 +1 @@
+Base Platform Cert Serial
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/cert/platform/Mfg.Serial.ver2.base.cer b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/cert/platform/Mfg.Serial.ver2.base.cer
new file mode 100644
index 00000000..4a56b5de
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/cert/platform/Mfg.Serial.ver2.base.cer
@@ -0,0 +1 @@
+Delta Platform Certificate Serial
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/manifest/rim/Mfg.Model.1.rimel b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/manifest/rim/Mfg.Model.1.rimel
new file mode 100644
index 00000000..93b1ee59
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/manifest/rim/Mfg.Model.1.rimel
@@ -0,0 +1 @@
+RIM EL Model 1
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/manifest/rim/Mfg.Model.1.rimpcr b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/manifest/rim/Mfg.Model.1.rimpcr
new file mode 100644
index 00000000..47f2ae1b
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/manifest/rim/Mfg.Model.1.rimpcr
@@ -0,0 +1 @@
+RIM PCR Model 1
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/manifest/swidtag/Mfg.Model.1.swidtag b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/manifest/swidtag/Mfg.Model.1.swidtag
new file mode 100644
index 00000000..6c59c1fc
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/manifest/swidtag/Mfg.Model.1.swidtag
@@ -0,0 +1 @@
+RIM Swidtag Model 1
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.base.pem b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.base.pem
new file mode 100644
index 00000000..e69de29b
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.delta.cer b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.delta.cer
new file mode 100644
index 00000000..e69de29b
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.rimel b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.rimel
new file mode 100644
index 00000000..e69de29b
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.rimpcr b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.rimpcr
new file mode 100644
index 00000000..e69de29b
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.swidtag b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/other/notread.swidtag
new file mode 100644
index 00000000..e69de29b
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/pccert/Mfg.Model.ver1.delta.pem b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/pccert/Mfg.Model.ver1.delta.pem
new file mode 100644
index 00000000..d20243da
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/efi/boot/tcg/pccert/Mfg.Model.ver1.delta.pem
@@ -0,0 +1 @@
+Delta Platform Certificiate Model
diff --git a/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/event_log b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/event_log
new file mode 100644
index 00000000..2c74f866
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirsTest/Resources/test/settings_test/event_log
@@ -0,0 +1 @@
+event log
diff --git a/HIRS_Provisioner.NET/hirsTest/hirsTest.csproj b/HIRS_Provisioner.NET/hirsTest/hirsTest.csproj
new file mode 100644
index 00000000..120dfd60
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirsTest/hirsTest.csproj
@@ -0,0 +1,38 @@
+
+
+
+ net6.0
+ false
+ enable
+ enable
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
diff --git a/HIRS_Provisioner.NET/hirsTest/src/client/ClientTests.cs b/HIRS_Provisioner.NET/hirsTest/src/client/ClientTests.cs
new file mode 100644
index 00000000..9529efde
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirsTest/src/client/ClientTests.cs
@@ -0,0 +1,53 @@
+using hirs;
+using Hirs.Pb;
+using NUnit.Framework;
+using System.Collections.Generic;
+using System.Text;
+using Tpm2Lib;
+
+namespace hirsTest {
+ public class ClientTests {
+ public static readonly string localhost = "https://127.0.0.1:8443/";
+
+ [Test]
+ public void TestCreateIdentityClaim() {
+ IHirsAcaClient client = new Client(ClientTests.localhost);
+
+ DeviceInfo dv = new DeviceInfo();
+ byte[] akPub = new byte[] { };
+ byte[] ekPub = new byte[] { };
+ byte[] ekc = new byte[] { };
+ List pcs = new List();
+ pcs.Add(new byte[] { });
+ string componentList= "";
+
+ IdentityClaim obj = client.CreateIdentityClaim(dv, akPub, ekPub, ekc, pcs, componentList);
+ Assert.Multiple(() => {
+ Assert.That(obj.Dv, Is.Not.Null);
+ Assert.That(obj.HasAkPublicArea, Is.True);
+ Assert.That(obj.HasEkPublicArea, Is.True);
+ Assert.That(obj.HasEndorsementCredential, Is.True);
+ Assert.That(obj.PlatformCredential, Has.Count.EqualTo(1));
+ Assert.That(obj.HasPaccorOutput, Is.True);
+ });
+ }
+
+ [Test]
+ public void TestCreateAkCertificateRequest() {
+ IHirsAcaClient client = new Client(ClientTests.localhost);
+
+ byte[] secret = Encoding.UTF8.GetBytes("secret");
+ Attest quoted = new Attest(Generated.None, Encoding.UTF8.GetBytes("QUALIFIED SIGNER"), Encoding.UTF8.GetBytes("EXTRA DATA"), new ClockInfo(0, 0, 0, 0), 0, new QuoteInfo());
+ ISignatureUnion signature = new SignatureRsassa();
+ Tpm2bDigest[] pcrValues = new Tpm2bDigest[] { new Tpm2bDigest(Encoding.UTF8.GetBytes("SHA1 DIGEST1")) };
+
+ CommandTpmQuoteResponse ctqr = new CommandTpmQuoteResponse(quoted, signature, pcrValues);
+
+ CertificateRequest obj = client.CreateAkCertificateRequest(secret, ctqr);
+ Assert.Multiple(() => {
+ Assert.That(obj.HasNonce, Is.True);
+ Assert.That(obj.HasQuote, Is.True);
+ });
+ }
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirsTest/src/config/SettingsTests.cs b/HIRS_Provisioner.NET/hirsTest/src/config/SettingsTests.cs
new file mode 100644
index 00000000..b0e3411a
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirsTest/src/config/SettingsTests.cs
@@ -0,0 +1,59 @@
+using hirs;
+using NUnit.Framework;
+using System.Collections.Generic;
+using System.Text;
+
+namespace hirsTest {
+ public class SettingsTests {
+ [Test]
+ public void TestConstructorWithAppsettings() {
+ Settings settings = Settings.LoadSettingsFromFile("./Resources/test/settings_test/appsettings.json");
+ settings.SetUpLog();
+ settings.CompleteSetUp();
+
+ Assert.Multiple(() => {
+ Assert.That(settings.IsAutoDetectTpmEnabled(), Is.False);
+ Assert.That(settings.HasAcaAddress(), Is.True);
+ Assert.That(settings.aca_address_port.ToString(), Is.EqualTo("https://127.0.0.1:8443/"));
+ Assert.That(settings.HasEfiPrefix(), Is.True);
+ Assert.That(settings.HasPaccorOutputFromFile(), Is.True);
+ Assert.That(settings.HasEventLogFromFile(), Is.True);
+ });
+
+ const string baseSerial = "Base Platform Cert Serial\n";
+ const string deltaSerial = "Delta Platform Certificate Serial\n";
+ const string deltaModel = "Delta Platform Certificiate Model\n";
+ const string swidtagModel = "RIM Swidtag Model 1\n";
+ const string rimelModel = "RIM EL Model 1\n";
+ const string rimpcrModel = "RIM PCR Model 1\n";
+ const string eventLogModel = "event log\n";
+ const string componentList = "component list\n";
+
+ string paccorOutput = settings.paccor_output;
+ List platformCerts = settings.gatherPlatformCertificatesFromEFI();
+ List rims = settings.gatherRIMBasesFromEFI();
+ List rimELs = settings.gatherSupportRIMELsFromEFI();
+ List rimPCRs = settings.gatherSupportRIMPCRsFromEFI();
+ byte[] eventLog = settings.event_log;
+ //Assert.Multiple(() => {
+ Assert.That(paccorOutput, Is.EqualTo(componentList));
+ Assert.That(platformCerts, Has.Count.EqualTo(3));
+
+ Assert.That(platformCerts[0], Is.EqualTo(Encoding.ASCII.GetBytes(baseSerial)));
+ Assert.That(platformCerts[1], Is.EqualTo(Encoding.ASCII.GetBytes(deltaSerial)));
+ Assert.That(platformCerts[2], Is.EqualTo(Encoding.ASCII.GetBytes(deltaModel)));
+
+ Assert.That(rims, Has.Count.EqualTo(1));
+ Assert.That(rims[0], Is.EqualTo(Encoding.ASCII.GetBytes(swidtagModel)));
+
+ Assert.That(rimELs, Has.Count.EqualTo(1));
+ Assert.That(rimELs[0], Is.EqualTo(Encoding.ASCII.GetBytes(rimelModel)));
+
+ Assert.That(rimPCRs, Has.Count.EqualTo(1));
+ Assert.That(rimPCRs[0], Is.EqualTo(Encoding.ASCII.GetBytes(rimpcrModel)));
+
+ Assert.That(Encoding.ASCII.GetString(eventLog), Is.EqualTo(eventLogModel));
+ //});
+ }
+ }
+}
diff --git a/HIRS_Provisioner.NET/hirsTest/src/provisioner/ProvisionerTests.cs b/HIRS_Provisioner.NET/hirsTest/src/provisioner/ProvisionerTests.cs
new file mode 100644
index 00000000..4236c62f
--- /dev/null
+++ b/HIRS_Provisioner.NET/hirsTest/src/provisioner/ProvisionerTests.cs
@@ -0,0 +1,123 @@
+using Hirs.Pb;
+using hirs;
+using FakeItEasy;
+using NUnit.Framework;
+using System;
+using System.Text;
+using System.Threading.Tasks;
+using Tpm2Lib;
+
+namespace hirsTest {
+ public class ProvisionerTests {
+ [Test]
+ public async Task TestGoodAsync() {
+ const string address = "https://127.0.0.1:8443/";
+ byte[] ekCert = Encoding.UTF8.GetBytes("EK CERTIFICATE");
+ byte[] secret = Encoding.UTF8.GetBytes("AuthCredential Secret");
+ byte[] acaIssuedCert = Encoding.UTF8.GetBytes("ACA ISSUED CERTIFICATE");
+ byte[] integrityHMAC = Convert.FromBase64String("VAtedc1RlNA1w0XfrtwmhE0ILBlILP6163Tur5HRIo0=");
+ byte[] encIdentity = Convert.FromBase64String("6e2oGBsK3H9Vzbj667ZsjnVOtvpSpQ==");
+ byte[] encryptedSecret = Convert.FromBase64String("NekvnOX8RPRdyd0/cxBI4FTCuNkiu0KAnS28yT7yYJUL5Lwfcv5ctEK6zQA0fq0IsX5TlAYSidGKxrAilOSwALJmJ+m7sMiXwMKrZn1cd4gzXObZEQimQoWgSEQbPO7rfpUn1UfI8K5SzmUFUTxc5X3D8zFonaEBp6QCjtdLegKGgioCDcQFdz20Y0PFAa1Itug7YbZdCFpfit570eQQinmqdVryiNyn6CLQdMgIejuBxoEpoTSWszB5eFKEdn5g/+8wcvhp6RpNBQ0hikF+6688TOVK/j8n3JDwKVltJ/WNHjVO+lxa2aLIMJRgs5ZRuzuz6OSMf10KqJjSWZE04w==");
+ byte[] credentialBlob = Convert.FromBase64String("OAAAIFQLXnXNUZTQNcNF367cJoRNCCwZSCz+tet07q+R0SKN6e2oGBsK3H9Vzbj667ZsjnVOtvpSpQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATXpL5zl/ET0XcndP3MQSOBUwrjZIrtCgJ0tvMk+8mCVC+S8H3L+XLRCus0ANH6tCLF+U5QGEonRisawIpTksACyZifpu7DIl8DCq2Z9XHeIM1zm2REIpkKFoEhEGzzu636VJ9VHyPCuUs5lBVE8XOV9w/MxaJ2hAaekAo7XS3oChoIqAg3EBXc9tGNDxQGtSLboO2G2XQhaX4ree9HkEIp5qnVa8ojcp+gi0HTICHo7gcaBKaE0lrMweXhShHZ+YP/vMHL4aekaTQUNIYpBfuuvPEzlSv4/J9yQ8ClZbSf1jR41TvpcWtmiyDCUYLOWUbs7s+jkjH9dCqiY0lmRNOM=");
+ TpmPublic ekPublic = CommandTpm.GenerateEKTemplateL1();
+ TpmPublic akPublic = new(TpmAlgId.Sha256, ObjectAttr.None, System.Text.Encoding.UTF8.GetBytes("AK PUBLIC AUTH POLICY"), new RsaParms(new SymDefObject(TpmAlgId.Null, 0, TpmAlgId.Null), new SchemeRsassa(TpmAlgId.Sha256), 2048, 0), new Tpm2bPublicKeyRsa());
+ Tpm2bDigest[] sha1Values = new Tpm2bDigest[] { new Tpm2bDigest(System.Text.Encoding.UTF8.GetBytes("SHA1 DIGEST1")) };
+ Tpm2bDigest[] sha256Values = new Tpm2bDigest[] { new Tpm2bDigest(System.Text.Encoding.UTF8.GetBytes("SHA256 DIGEST1")) };
+ DeviceInfo dv = new();
+ string paccorOutput = "paccor output";
+
+ CommandTpmQuoteResponse ctqr = null;
+ IdentityClaimResponse idClaimResp = new();
+ idClaimResp.Status = ResponseStatus.Pass;
+ idClaimResp.CredentialBlob = Google.Protobuf.ByteString.CopyFrom(credentialBlob);
+ CertificateResponse certResp = new();
+ certResp.Status = ResponseStatus.Pass;
+ certResp.Certificate = Google.Protobuf.ByteString.CopyFrom(acaIssuedCert);
+
+ IHirsAcaTpm tpm = A.Fake();
+ byte[] name = null, qualifiedName = null;
+ A.CallTo(() => tpm.GetCertificateFromNvIndex(CommandTpm.DefaultEkcNvIndex)).Returns(ekCert);
+ A.CallTo(() => tpm.CreateEndorsementKey(CommandTpm.DefaultEkHandle)).DoesNothing();
+ A.CallTo(() => tpm.ReadPublicArea(CommandTpm.DefaultEkHandle, out name, out qualifiedName)).Returns(ekPublic);
+ A.CallTo(() => tpm.CreateAttestationKey(CommandTpm.DefaultEkHandle, CommandTpm.DefaultAkHandle, false)).DoesNothing();
+ A.CallTo(() => tpm.ReadPublicArea(CommandTpm.DefaultAkHandle, out name, out qualifiedName)).Returns(akPublic);
+ //A.CallTo(() => tpm.getPcrList(TpmAlgId.Sha1, A.Ignored)).Returns(sha1Values);
+ //A.CallTo(() => tpm.getPcrList(TpmAlgId.Sha256, A.Ignored)).Returns(sha256Values);
+ A.CallTo(() => tpm.GetQuote(CommandTpm.DefaultAkHandle, TpmAlgId.Sha256, secret, out ctqr, A.Ignored)).DoesNothing();
+
+ IHirsDeviceInfoCollector collector = A.Fake();
+ A.CallTo(() => collector.CollectDeviceInfo(address)).Returns(dv);
+
+ IHirsAcaClient client = A.Fake();
+ IdentityClaim idClaim = client.CreateIdentityClaim(dv, akPublic, ekPublic, ekCert, null, paccorOutput);
+ CertificateRequest certReq = client.CreateAkCertificateRequest(secret, ctqr);
+ A.CallTo(() => client.PostIdentityClaim(idClaim)).Returns(Task.FromResult(idClaimResp));
+ A.CallTo(() => client.PostCertificateRequest(certReq)).Returns(Task.FromResult(certResp));
+
+ Settings settings = Settings.LoadSettingsFromFile("./Resources/test/settings_test/appsettings.json");
+ settings.SetUpLog();
+ settings.CompleteSetUp();
+
+ CLI cli = A.Fake();
+
+ IHirsProvisioner p = A.Fake();
+ p.SetSettings(settings);
+ p.SetCLI(cli);
+ p.SetClient(client);
+
+ p.SetDeviceInfoCollector(collector); // Give the provisioner the mocked collector
+ int result = await p.Provision(tpm);
+
+ A.CallTo(() => tpm.ActivateCredential(CommandTpm.DefaultAkHandle, CommandTpm.DefaultEkHandle, A.That.IsSameSequenceAs(integrityHMAC), A.That.IsSameSequenceAs(encIdentity), A.That.IsSameSequenceAs(encryptedSecret))).MustHaveHappenedOnceExactly();
+ Assert.That(result, Is.EqualTo(0));
+ }
+
+ [Test]
+ public async Task TestIssueWithIdentityClaimResponse() {
+ const string address = "https://127.0.0.1:8443/";
+ byte[] ekCert = Encoding.UTF8.GetBytes("EK CERTIFICATE");
+ byte[] acaIssuedCert = Encoding.UTF8.GetBytes("ACA ISSUED CERTIFICATE");
+ TpmPublic ekPublic = CommandTpm.GenerateEKTemplateL1();
+ TpmPublic akPublic = new(TpmAlgId.Sha256, ObjectAttr.None, System.Text.Encoding.UTF8.GetBytes("AK PUBLIC AUTH POLICY"), new RsaParms(new SymDefObject(TpmAlgId.Null, 0, TpmAlgId.Null), new SchemeRsassa(TpmAlgId.Sha256), 2048, 0), new Tpm2bPublicKeyRsa());
+ Tpm2bDigest[] sha1Values = new Tpm2bDigest[] { new Tpm2bDigest(System.Text.Encoding.UTF8.GetBytes("SHA1 DIGEST1")) };
+ Tpm2bDigest[] sha256Values = new Tpm2bDigest[] { new Tpm2bDigest(System.Text.Encoding.UTF8.GetBytes("SHA256 DIGEST1")) };
+ DeviceInfo dv = new();
+ string paccorOutput = "paccor output";
+ IdentityClaimResponse idClaimResp = new();
+ idClaimResp.ClearCredentialBlob();
+
+ IHirsAcaTpm tpm = A.Fake();
+ byte[] name = null, qualifiedName = null;
+ A.CallTo(() => tpm.GetCertificateFromNvIndex(CommandTpm.DefaultEkcNvIndex)).Returns(ekCert);
+ A.CallTo(() => tpm.CreateEndorsementKey(CommandTpm.DefaultEkHandle)).DoesNothing();
+ A.CallTo(() => tpm.ReadPublicArea(CommandTpm.DefaultEkHandle, out name, out qualifiedName)).Returns(ekPublic);
+ A.CallTo(() => tpm.CreateAttestationKey(CommandTpm.DefaultEkHandle, CommandTpm.DefaultAkHandle, false)).DoesNothing();
+ A.CallTo(() => tpm.ReadPublicArea(CommandTpm.DefaultAkHandle, out name, out qualifiedName)).Returns(ekPublic);
+ A.CallTo(() => tpm.GetPcrList(TpmAlgId.Sha1, A.Ignored)).Returns(sha1Values);
+ A.CallTo(() => tpm.GetPcrList(TpmAlgId.Sha256, A.Ignored)).Returns(sha256Values);
+
+ IHirsDeviceInfoCollector collector = A.Fake();
+ A.CallTo(() => collector.CollectDeviceInfo(address)).Returns(dv);
+
+ IHirsAcaClient client = A.Fake();
+ IdentityClaim idClaim = client.CreateIdentityClaim(dv, akPublic, ekPublic, ekCert, null, paccorOutput);
+ A.CallTo(() => client.PostIdentityClaim(idClaim)).WithAnyArguments().Returns(Task.FromResult(idClaimResp));
+
+ Settings settings = Settings.LoadSettingsFromFile("./Resources/test/settings_test/appsettings.json");
+ settings.SetUpLog();
+ settings.CompleteSetUp();
+
+ CLI cli = A.Fake();
+
+ IHirsProvisioner p = A.Fake();
+ p.SetSettings(settings);
+ p.SetCLI(cli);
+ p.SetClient(client);
+
+ p.SetDeviceInfoCollector(collector); // Give the provisioner the mocked collector
+ int result = await p.Provision(tpm);
+
+ Assert.That(result, Is.EqualTo((int)ClientExitCodes.MAKE_CREDENTIAL_BLOB_MALFORMED));
+ }
+ }
+}
diff --git a/HIRS_Provisioner.NET/tools/pcrextend/pcrextend.csproj b/HIRS_Provisioner.NET/tools/pcrextend/pcrextend.csproj
new file mode 100644
index 00000000..1ca57b5a
--- /dev/null
+++ b/HIRS_Provisioner.NET/tools/pcrextend/pcrextend.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+ 0.1.0
+
+
+
+
+
+
+
+
diff --git a/HIRS_Provisioner.NET/tools/pcrextend/src/Program.cs b/HIRS_Provisioner.NET/tools/pcrextend/src/Program.cs
new file mode 100644
index 00000000..2d16f0cd
--- /dev/null
+++ b/HIRS_Provisioner.NET/tools/pcrextend/src/Program.cs
@@ -0,0 +1,42 @@
+using CommandLine;
+
+namespace pcrextend {
+ class Program {
+ static void Main(string[] args) {
+ bool result = false;
+ try {
+ CLI cli = new();
+ ParserResult cliParseResult =
+ CommandLine.Parser.Default.ParseArguments(args)
+ .WithParsed(parsed => cli = parsed)
+ .WithNotParsed(HandleParseError);
+
+ if (cliParseResult.Tag == ParserResultType.Parsed) {
+ // filter pci index
+ int pcr = cli.Pcr;
+ if (pcr < 0 || pcr > 23) {
+ Console.WriteLine("Unknown PCR index: " + pcr + ". Should be from 0 to 23.");
+ return;
+ }
+ PcrExtendTool p = new(cli);
+ CommandTpmSimulator? tpm = p.connectTpm();
+ if (tpm != null) {
+ result = p.Extend(tpm);
+ }
+ Console.WriteLine("PCR Extend " + (result ? "" : "un") + "successful.");
+ }
+ } catch (Exception e) {
+ Console.WriteLine("Application stopped.");
+ Console.WriteLine(e.ToString());
+ Console.WriteLine(e.StackTrace);
+ }
+ }
+
+ private static void HandleParseError(IEnumerable errs) {
+ if (!errs.IsHelp() && !errs.IsVersion()) {
+ //handle errors
+ Console.WriteLine("There was a CLI error: " + errs.ToString());
+ }
+ }
+ }
+}
diff --git a/HIRS_Provisioner.NET/tools/pcrextend/src/config/CLI.cs b/HIRS_Provisioner.NET/tools/pcrextend/src/config/CLI.cs
new file mode 100644
index 00000000..092cee59
--- /dev/null
+++ b/HIRS_Provisioner.NET/tools/pcrextend/src/config/CLI.cs
@@ -0,0 +1,29 @@
+using CommandLine;
+
+namespace pcrextend {
+ public class CLI {
+ // These fields are controlled by the CommandLineParser library.
+ // CS8618 is not relevant at this time.
+ // Non-nullable field must contain a non-null value when exiting constructor.
+#pragma warning disable CS8618
+ [Option('i', "ip", Default = CommandTpmSimulator.DefaultSimulatorNamePort, HelpText = "IP of the TPM Simulator. Use the format ip:port.")]
+ public string Ip {
+
+ get; set;
+ }
+ [Option('p', "pcr", HelpText = "PCR Index 0 thru 23")]
+ public short Pcr {
+ get; set;
+ }
+ [Option('a', "hashalg", HelpText = "sha1, sha256, sha384, or sha512. OR their ID Values in decimal (4, 11, 12, 13) or in hex (0x4, 0xB, 0xC, 0xD).")]
+ public string HashAlg {
+ get; set;
+ }
+
+ [Option('d', "digests", HelpText = "Comma separated digest values in hex. e.g. AB,34F53,9785")]
+ public string Digests {
+ get; set;
+ }
+#pragma warning restore CS8618
+ }
+}
diff --git a/HIRS_Provisioner.NET/tools/pcrextend/src/tool/PcrExtendTool.cs b/HIRS_Provisioner.NET/tools/pcrextend/src/tool/PcrExtendTool.cs
new file mode 100644
index 00000000..49cb2988
--- /dev/null
+++ b/HIRS_Provisioner.NET/tools/pcrextend/src/tool/PcrExtendTool.cs
@@ -0,0 +1,55 @@
+namespace pcrextend {
+ public class PcrExtendTool {
+ private CLI? cli = null;
+
+ public PcrExtendTool(CLI cli) {
+ SetCLI(cli);
+ }
+
+ public void SetCLI(CLI cli) {
+ this.cli = cli;
+ }
+
+ public CommandTpmSimulator? connectTpm() {
+ CommandTpmSimulator? tpm = null;
+ if (cli != null && cli.Ip != null) {
+ string[] split = cli.Ip.Split(":");
+ if (split.Length == 2) {
+ try {
+ tpm = new CommandTpmSimulator(split[0], Int32.Parse(split[1]));
+ } catch (Exception) {
+ Console.WriteLine("No tpm found at " + cli.Ip);
+ }
+ } else {
+ Console.WriteLine("ip input should have the format servername:port. The given input was '" + cli.Ip + "'.");
+ }
+ }
+
+ return tpm;
+ }
+
+ public bool Extend(CommandTpmSimulator tpm) {
+ bool result = false;
+ if (tpm != null && cli != null) {
+ // parse hashAlg
+ string hashAlg = cli.HashAlg;
+ if (hashAlg.StartsWith("0x", StringComparison.OrdinalIgnoreCase) || hashAlg.EndsWith("h")) {
+ hashAlg = hashAlg.Replace("0x", "");
+ hashAlg = hashAlg.TrimEnd('h');
+ hashAlg = "" + Convert.ToInt64(hashAlg, 16);
+ }
+
+ string digestCSV = cli.Digests;
+ digestCSV = digestCSV.ToUpper().Replace("0X", "").Replace("H", "");
+ string[] digestArray = digestCSV.Split(",");
+ List digestList = new();
+ foreach (string digest in digestArray) {
+ digestList.Add(Convert.FromHexString(digest));
+ }
+ tpm.pcrextend(cli.Pcr, hashAlg, digestList);
+ result = true;
+ }
+ return result;
+ }
+ }
+}
diff --git a/HIRS_Provisioner.NET/tools/pcrextend/src/tpm/CommandTpmSimulator.cs b/HIRS_Provisioner.NET/tools/pcrextend/src/tpm/CommandTpmSimulator.cs
new file mode 100644
index 00000000..d0a1a09f
--- /dev/null
+++ b/HIRS_Provisioner.NET/tools/pcrextend/src/tpm/CommandTpmSimulator.cs
@@ -0,0 +1,69 @@
+using Tpm2Lib;
+
+namespace pcrextend {
+ public class CommandTpmSimulator {
+ ///
+ /// If using a TCP connection, the default DNS name/IP address for the
+ /// simulator.
+ ///
+ public const string DefaultSimulatorNamePort = "127.0.0.1:2321";
+
+ private readonly Tpm2 tpm;
+
+ private readonly Boolean simulator;
+
+ /**
+ * For TCP TpmDevices
+ */
+ public CommandTpmSimulator(string ip, int port) {
+ simulator = true;
+ Tpm2Device tpmDevice = new TcpTpmDevice(ip, port);
+ Console.WriteLine("Connecting to TPM at " + ip + ":" + port);
+ tpm = tpmSetupByType(tpmDevice);
+ }
+
+ ~CommandTpmSimulator() {
+ if (tpm != null) {
+ tpm.Dispose();
+ }
+ }
+
+ public void pcrextend(int pcr, string hashAlgStr, List digestList) {
+ TpmHandle pcrHandle = TpmHandle.Pcr(pcr);
+ TpmAlgId hashAlg = Enum.Parse(hashAlgStr, true);
+ List digests = new();
+ foreach (byte[] digest in digestList) {
+ digests.Add(new TpmHash(hashAlg, digest));
+ }
+ tpm.PcrExtend(pcrHandle, digests.ToArray());
+ }
+
+ private Tpm2 tpmSetupByType(Tpm2Device tpmDevice) {
+ tpmDevice.Connect();
+
+ Tpm2 tpm = new Tpm2(tpmDevice);
+ if (tpmDevice is TcpTpmDevice) {
+ //
+ // If we are using the simulator, we have to do a few things the
+ // firmware would usually do. These actions have to occur after
+ // the connection has been established.
+ //
+ if (simulator) {
+ uint rc = 0;
+ try {
+ rc = tpm.PcrRead(new PcrSelection[] { new PcrSelection(TpmAlgId.Sha1, new uint[] { 0 }) }, out _, out _);
+ } catch (TpmException e) {
+ if (e.RawResponse == TpmRc.Initialize) {
+ Console.WriteLine("TPM simulator not initialized. Running startup with clear.");
+ tpmDevice.PowerCycle();
+ tpm.Startup(Su.Clear);
+ } else {
+ Console.WriteLine("TPM simulator already initialized. Skipping TPM2_Startup.");
+ }
+ }
+ }
+ }
+ return tpm;
+ }
+ }
+}