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
This commit is contained in:
iadgovuser62 2024-01-22 16:18:01 -05:00 committed by GitHub
parent 2efb1a98f3
commit e9f5d7245c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 3494 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,44 @@
<Project>
<Target Name="RenameBeforePublishLinux" BeforeTargets="CreatePackageProperties" Condition="$(RuntimeIdentifier.Contains('linux'))">
<PropertyGroup>
<SymlinkAppHostInBin Condition="'$(SymlinkAppHostInBin)' == ''">false</SymlinkAppHostInBin> <!-- If true, this will place a symlink in /usr/local/bin/, which is not on $PATH for root by default. We need the symlink in /bin or /sbin -->
</PropertyGroup>
<Move SourceFiles="$(OUTDIR)publish\$(AssemblyName)" DestinationFiles="$(OUTDIR)publish\tpm_aca_provision" />
<Message Text="Renamed linux binary file." Importance="high" />
<PropertyGroup>
<Prefix>/usr/share/hirs</Prefix>
<PostInstallScript>/usr/bin/chmod 644 /usr/share/hirs/appsettings.json; /usr/bin/ln -s /usr/share/hirs/tpm_aca_provision /usr/bin/tpm_aca_provision</PostInstallScript>
<PostRemoveScript>rm -f /usr/bin/tpm_aca_provision; rm -rf /usr/share/hirs</PostRemoveScript>
</PropertyGroup>
<Message Text="Set installed directory prefix to $(PREFIX)." Importance="high" />
</Target>
<Target Name="RenameBeforePublishWindows" BeforeTargets="SetWixPath" Condition="$(RuntimeIdentifier.Contains('win'))">
<Move SourceFiles="$(OUTDIR)publish\$(AssemblyName).exe" DestinationFiles="$(OUTDIR)publish\tpm_aca_provision.exe" />
<Message Text="Renamed exe binary file." Importance="high" />
</Target>
<Target Name="SetWixPath" BeforeTargets="Msi">
<PropertyGroup>
<ProductSourceFilePath>$(MSBuildThisFileDirectory)\Resources\Product.wxs</ProductSourceFilePath>
<WixInstallPath>$(NuGetPackageRoot)wix\3.11.2\tools\</WixInstallPath>
<Heat>$(WixInstallPath)heat.exe</Heat>
<Candle>$(WixInstallPath)candle.exe</Candle>
<Light>$(WixInstallPath)light.exe</Light>
</PropertyGroup>
</Target>
<Target Name="DeletePDB" AfterTargets="RenameBeforePublishLinux;RenameBeforePublishWindows">
<ItemGroup>
<PDBToDelete Include="$(PublishDir)*.pdb"/>
</ItemGroup>
<Delete Files="@(PDBToDelete)" />
<Message Text="Deleted PDB files." Importance="high" />
</Target>
<Target Name="CopyFiles" AfterTargets="DeletePDB">
<ItemGroup>
<PaccorScriptsDll Include="$(OUTDIR)paccor_scripts.dll"/>
</ItemGroup>
<Copy
SourceFiles="@(PaccorScriptsDll)"
DestinationFolder="$(PublishDir)plugins"
/>
</Target>
</Project>

View File

@ -0,0 +1,102 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifiers>linux-x64;win-x64</RuntimeIdentifiers>
<StartupObject>hirs.Program</StartupObject>
<PublishSingleFile>true</PublishSingleFile>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageVersion>2.2.0</PackageVersion>
<Release></Release>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
<WarningLevel>0</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>0</WarningLevel>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Google.Protobuf" Version="3.20.1" />
<PackageReference Include="Google.Protobuf.Tools" Version="3.20.1">
<PrivateAssets>all</PrivateAssets> <!-- These assets will be consumed but won't flow to the parent project -->
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
<PackageReference Include="Microsoft.TSS" Version="2.1.1" />
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="paccor.HardwareManifestPlugin" Version="1.0.0" />
<PackageReference Include="paccor.HardwareManifestPluginManager" Version="1.0.0" />
<PackageReference Include="paccor.paccor_scripts" Version="1.0.1" />
<PackageReference Include="Packaging.Targets" Version="0.1.220">
<PrivateAssets>all</PrivateAssets> <!-- These assets will be consumed but won't flow to the parent project -->
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Serilog" Version="2.11.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.3.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="System.Management" Version="6.0.0" />
<PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" />
<PackageReference Include="WiX" Version="3.11.2">
<PrivateAssets>all</PrivateAssets> <!-- These assets will be consumed but won't flow to the parent project -->
</PackageReference>
</ItemGroup>
<ItemGroup>
<Protobuf Include="**/*.proto" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</None>
</ItemGroup>
<Target Name="BuildProto" BeforeTargets="PreBuildEvent">
<PropertyGroup>
<FOLDER_PROTO>$(ProjectDir)Resources</FOLDER_PROTO>
<FOLDER_OUT>$(ProjectDir)generated</FOLDER_OUT>
</PropertyGroup>
<PropertyGroup>
<protoc Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' And '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)'=='X64'">$(protoc_linux64)</protoc>
<protoc Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' And '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)'=='X86'">$(protoc_linux86)</protoc>
<protoc Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' And '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)'=='X64'">$(protoc_macosx64)</protoc>
<protoc Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' And '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)'=='X86'">$(protoc_macosx86)</protoc>
<protoc Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' And '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)'=='X64'">$(protoc_windows64)</protoc>
<protoc Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' And '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)'=='X86'">$(protoc_windows86)</protoc>
</PropertyGroup>
<ItemGroup>
<Compile Condition="!Exists('$(FOLDER_OUT)')" Include="generated/ProvisionerTpm2.cs" /> <!-- Necessary to include the proto file(s) compiled during this prebuild phase, if they didn't exist before. CANNOT use wildcards! -->
</ItemGroup>
<Exec Condition="!Exists('$(FOLDER_OUT)')" Command="mkdir $(FOLDER_OUT)" />
<Exec Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))'" Command="for /f %%i in ('dir /s /b $(FOLDER_PROTO)\*.proto') do ( $(protoc) -I=$(FOLDER_PROTO) --csharp_out=$(FOLDER_OUT) %%i )" />
<Exec Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'" Command="for file in `ls -1R $(FOLDER_PROTO)/*.proto` ; do $(protoc) -I=$(FOLDER_PROTO) --csharp_out=$(FOLDER_OUT) $file; done " />
</Target>
<Import Project="$(NuGetPackageRoot)paccor.paccor_scripts/1.0.1/contentFiles/any/net6.0/resources/paccor.paccor_scripts.targets" Condition="Exists('$(NuGetPackageRoot)paccor.paccor_scripts/1.0.1/contentFiles/any/net6.0/resources/paccor.paccor_scripts.targets')" />
<Target Name="ImportPaccorScripts" BeforeTargets="PreBuildEvent">
<ItemGroup>
<PaccorScriptsLinux Include="$(dotnet_paccor_scripts_directory)/*" />
<PaccorScriptsWindows Include="$(dotnet_paccor_scripts_windows_directory)/*" />
</ItemGroup>
<Message Text="Adding files to plugins/scripts: @(PaccorScriptsLinux)" Importance="high" />
<Message Text="Adding files to plugins/scripts/windows: @(PaccorScriptsWindows)" Importance="high" />
<Copy SourceFiles="@(PaccorScriptsLinux)" DestinationFolder="$(ProjectDir)/plugins/scripts/" />
<Copy SourceFiles="@(PaccorScriptsWindows)" DestinationFolder="$(ProjectDir)/plugins/scripts/windows/" />
<ItemGroup>
<Content Include="plugins\**\*.*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
</ItemGroup>
</Target>
</Project>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="$(var.SetupProductName)" Manufacturer="$(var.SetupProductManufacturer)" Language="$(var.SetupProductLanguage)" Version="$(var.SetupProductVersion)">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<!--
Because we don't have a way (yet) to create unique product codes, we can't include the MajorUpgrade element, either
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
-->
<MediaTemplate EmbedCab="yes" />
<Feature Id="$(var.SetupFeatureId)" Title="$(var.SetupFeatureName)" Level="1">
<ComponentGroupRef Id="ProductComponents" />
<Component Directory='INSTALLFOLDER'>
<RegistryValue Root='HKLM' Key='Software\HIRS' Name='InstallFolder' Value='[INSTALLFOLDER]' Type='string' />
<Environment Id='UpdatePath' Name='PATH' Action='set' System='yes' Part='last' Value='[INSTALLFOLDER]' />
</Component>
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="$(var.SetupInstallFolderName)" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<ComponentGroupRef Id="SourceComponentGroup" />
</ComponentGroup>
</Fragment>
</Wix>

View File

@ -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];
}

View File

@ -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
}
}
]
}
}

View File

@ -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<int> 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<CLI> cliParseResult =
CommandLine.Parser.Default.ParseArguments<CLI>(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 <ip>:<port>, --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<Error> 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;
}
}
}

View File

@ -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<IdentityClaimResponse> 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<byte[]> 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<CertificateResponse> 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;
}
}
}

View File

@ -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 {
/// <summary>
/// Send the <see cref="IdentityClaim"/> to the ACA. The claim is delivered
/// asynchronously to the ACA. However, the client will wait for the response.
/// </summary>
/// <param name="identityClaim">Evidence about the client.</param>
/// <returns>The <see cref="Task"/>&lt;<see cref="IdentityClaimResponse"/>&gt; from the
/// ACA. The response is wrapped in a Task.</returns>
Task<IdentityClaimResponse> PostIdentityClaim(IdentityClaim identityClaim);
/// <summary>
/// Send the <see cref="CertificateRequest"/> to the ACA. The request is delivered
/// asynchronously to the ACA. However, the client will wait for the response.
/// </summary>
/// <param name="certReq">The request for a certificate. Should contain evidence from
/// client to enable nonce verification.</param>
/// <returns>The <see cref="Task"/>&lt;<see cref="CertificateResponse"/>&gt; from the ACA.
/// The response is wrapped in a Task. It will contain a certificate or the reason why
/// the certificate request was rejected.</returns>
Task<CertificateResponse> PostCertificateRequest(CertificateRequest certReq);
/// <summary>
/// Collect client evidence regarding a Device into an object that can be interpreted by
/// the ACA.
/// </summary>
/// <param name="dv">Facts about the Device.</param>
/// <param name="akPublicArea">The public AK retrieved as a TPM2B_PUBLIC.</param>
/// <param name="ekPublicArea">The public EK retrieved as a TPM2B_PUBLIC.</param>
/// <param name="endorsementCredential">The public EK certificate, encoded in DER or
/// PEM.</param>
/// <param name="platformCredentials">Any platform certificates relevant to the Device,
/// encoded in DER or PEM.</param>
/// <param name="paccoroutput">Platform Manifest in a JSON format.</param>
/// <returns>An <see cref="IdentityClaim"/> object that can be sent to the ACA.</returns>
IdentityClaim CreateIdentityClaim(DeviceInfo dv, byte[] akPublicArea, byte[] ekPublicArea,
byte[] endorsementCredential,
List<byte[]> platformCredentials, string paccoroutput);
/// <summary>
/// Collect answers to verification requirements regarding a Device into an object that
/// can be interpreted by the ACA.
/// </summary>
/// <param name="secret">Verification data.</param>
/// <param name="ctqr">TPM Quote data from the client Device.</param>
/// <returns>A <see cref="CertificateRequest"/> object that can be sent to the
/// ACA.</returns>
CertificateRequest CreateAkCertificateRequest(byte[] secret, CommandTpmQuoteResponse ctqr);
}
}

View File

@ -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 = "<Pending>")]
public static bool GetOwnerAuthFromOS(out byte[] ownerAuth) {
ownerAuth = Array.Empty<byte>();
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 = "<Pending>")]
public static bool GetEventLog(out byte[] eventLog) {
eventLog = Array.Empty<byte>();
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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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
}
}

View File

@ -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<IHardwareManifest> hardwareManifests = new();
private Dictionary<string, string> hardware_manifest_collectors_with_args = new();
private bool hardware_manifest_collection_swid_enforced = false;
private Settings() : this(Settings.DEFAULT_SETTINGS_FILE) { }
/// <summary>
/// </summary>
/// <param name="file">The path to the appsettings.json file on the file system.</param>
private Settings(string file) {
settingsFile = file;
configFromSettingsFile = ReadSettingsFile();
}
public static Settings LoadSettingsFromDefaultFile() {
return new(DEFAULT_SETTINGS_FILE);
}
/// <summary>
/// </summary>
/// <param name="path">The path to the settings JSON file on the file system.</param>
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<string> 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<string, string> ParseHardwareManifestCollectorsString(string hardware_manifest_collectors) {
Dictionary<string, string> dict = new();
List<string> 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<string> names = hardwareManifests.Select(x => x.Name).ToList();
Dictionary<string, string> 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://<aca_server>:<port>\"");
}
}
#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<byte[]> gatherPlatformCertificatesFromEFI() {
// According to FIM: EFIPREFIX/boot/tcg/{cert,pccert,platform}
List<byte[]> platformCerts = null;
if (!string.IsNullOrWhiteSpace(efi_prefix)) {
EnumerationOptions enumOpts = new EnumerationOptions();
enumOpts.MatchCasing = MatchCasing.CaseInsensitive;
enumOpts.RecurseSubdirectories = true;
List<FileInfo> files = new List<FileInfo>();
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<byte[]>();
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<byte[]> gatherRIMBasesFromEFI() {
// According to PC Client RIM
List<byte[]> baseRims = null;
// /boot/tcg/manifest/swidtag Base RIM Files
// <name of the tag creator> + <product name> + <RIM version>.swidtag
if (!string.IsNullOrWhiteSpace(efi_prefix)) {
EnumerationOptions enumOpts = new EnumerationOptions();
enumOpts.MatchCasing = MatchCasing.CaseInsensitive;
enumOpts.RecurseSubdirectories = true;
List<FileInfo> files = new List<FileInfo>();
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<byte[]>();
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<byte[]> gatherSupportRIMELsFromEFI() {
// According to PC Client RIM
List<byte[]> supportRimELs = null;
// /boot/tcg/manifest/rim Support RIM Files
// <name of the tag creator> + <product name> + <product version>.rimel
if (!string.IsNullOrWhiteSpace(efi_prefix)) {
EnumerationOptions enumOpts = new EnumerationOptions();
enumOpts.MatchCasing = MatchCasing.CaseInsensitive;
enumOpts.RecurseSubdirectories = true;
List<FileInfo> files = new List<FileInfo>();
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<byte[]>();
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<byte[]> gatherSupportRIMPCRsFromEFI() {
// According to PC Client RIM
List<byte[]> supportRimPCRs = null;
// /boot/tcg/manifest/rim Support RIM Files
// <name of the tag creator> + <product name> + <product version>.rimpcr
if (!string.IsNullOrWhiteSpace(efi_prefix)) {
EnumerationOptions enumOpts = new EnumerationOptions();
enumOpts.MatchCasing = MatchCasing.CaseInsensitive;
enumOpts.RecurseSubdirectories = true;
List<FileInfo> files = new List<FileInfo>();
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<byte[]>();
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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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<int> Provision(IHirsAcaTpm tpm);
}
}

View File

@ -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 <address>:<port>\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 <address>:<port>\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<int> 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<byte[]> 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;
}
}
}

View File

@ -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
}
/// <summary>
/// If using a TCP connection, the default DNS name/IP address for the
/// simulator.
/// </summary>
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<AuthSession> sessionTracking = new List<AuthSession>();
/**
* 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<byte>();
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<byte>(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<Tpm2bDigest>();
}
List<Tpm2bDigest> 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<uint> 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<uint> 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";
}
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}

View File

@ -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
}
}
]
}
}

View File

@ -0,0 +1 @@
component list

View File

@ -0,0 +1 @@
Base Platform Cert Serial

View File

@ -0,0 +1 @@
Delta Platform Certificate Serial

View File

@ -0,0 +1 @@
Delta Platform Certificiate Model

View File

@ -0,0 +1 @@
event log

View File

@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FakeItEasy" Version="7.3.1" />
<PackageReference Include="FakeItEasy.Analyzer.CSharp" Version="6.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit.Analyzers" Version="3.3.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
</ItemGroup>
<ItemGroup>
<Content Include="Resources\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\hirs\HIRS_Provisioner.NET.csproj" />
</ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties resources_4settings_3test_4appsettings_1json__JsonSchema="" /></VisualStudio></ProjectExtensions>
</Project>

View File

@ -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<byte[]> pcs = new List<byte[]>();
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);
});
}
}
}

View File

@ -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<byte[]> platformCerts = settings.gatherPlatformCertificatesFromEFI();
List<byte[]> rims = settings.gatherRIMBasesFromEFI();
List<byte[]> rimELs = settings.gatherSupportRIMELsFromEFI();
List<byte[]> 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));
//});
}
}
}

View File

@ -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<IHirsAcaTpm>();
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<uint[]>.Ignored)).Returns(sha1Values);
//A.CallTo(() => tpm.getPcrList(TpmAlgId.Sha256, A<uint[]>.Ignored)).Returns(sha256Values);
A.CallTo(() => tpm.GetQuote(CommandTpm.DefaultAkHandle, TpmAlgId.Sha256, secret, out ctqr, A<uint[]>.Ignored)).DoesNothing();
IHirsDeviceInfoCollector collector = A.Fake<IHirsDeviceInfoCollector>();
A.CallTo(() => collector.CollectDeviceInfo(address)).Returns(dv);
IHirsAcaClient client = A.Fake<IHirsAcaClient>();
IdentityClaim idClaim = client.CreateIdentityClaim(dv, akPublic, ekPublic, ekCert, null, paccorOutput);
CertificateRequest certReq = client.CreateAkCertificateRequest(secret, ctqr);
A.CallTo(() => client.PostIdentityClaim(idClaim)).Returns(Task.FromResult<IdentityClaimResponse>(idClaimResp));
A.CallTo(() => client.PostCertificateRequest(certReq)).Returns(Task.FromResult<CertificateResponse>(certResp));
Settings settings = Settings.LoadSettingsFromFile("./Resources/test/settings_test/appsettings.json");
settings.SetUpLog();
settings.CompleteSetUp();
CLI cli = A.Fake<CLI>();
IHirsProvisioner p = A.Fake<Provisioner>();
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<byte[]>.That.IsSameSequenceAs(integrityHMAC), A<byte[]>.That.IsSameSequenceAs(encIdentity), A<byte[]>.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<IHirsAcaTpm>();
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<uint[]>.Ignored)).Returns(sha1Values);
A.CallTo(() => tpm.GetPcrList(TpmAlgId.Sha256, A<uint[]>.Ignored)).Returns(sha256Values);
IHirsDeviceInfoCollector collector = A.Fake<IHirsDeviceInfoCollector>();
A.CallTo(() => collector.CollectDeviceInfo(address)).Returns(dv);
IHirsAcaClient client = A.Fake<IHirsAcaClient>();
IdentityClaim idClaim = client.CreateIdentityClaim(dv, akPublic, ekPublic, ekCert, null, paccorOutput);
A.CallTo(() => client.PostIdentityClaim(idClaim)).WithAnyArguments().Returns(Task.FromResult<IdentityClaimResponse>(idClaimResp));
Settings settings = Settings.LoadSettingsFromFile("./Resources/test/settings_test/appsettings.json");
settings.SetUpLog();
settings.CompleteSetUp();
CLI cli = A.Fake<CLI>();
IHirsProvisioner p = A.Fake<Provisioner>();
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));
}
}
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>0.1.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Microsoft.TSS" Version="2.1.1" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,42 @@
using CommandLine;
namespace pcrextend {
class Program {
static void Main(string[] args) {
bool result = false;
try {
CLI cli = new();
ParserResult<CLI> cliParseResult =
CommandLine.Parser.Default.ParseArguments<CLI>(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<Error> errs) {
if (!errs.IsHelp() && !errs.IsVersion()) {
//handle errors
Console.WriteLine("There was a CLI error: " + errs.ToString());
}
}
}
}

View File

@ -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
}
}

View File

@ -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<byte[]> digestList = new();
foreach (string digest in digestArray) {
digestList.Add(Convert.FromHexString(digest));
}
tpm.pcrextend(cli.Pcr, hashAlg, digestList);
result = true;
}
return result;
}
}
}

View File

@ -0,0 +1,69 @@
using Tpm2Lib;
namespace pcrextend {
public class CommandTpmSimulator {
/// <summary>
/// If using a TCP connection, the default DNS name/IP address for the
/// simulator.
/// </summary>
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<byte[]> digestList) {
TpmHandle pcrHandle = TpmHandle.Pcr(pcr);
TpmAlgId hashAlg = Enum.Parse<TpmAlgId>(hashAlgStr, true);
List<TpmHash> 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;
}
}
}