diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..dc1baf0 --- /dev/null +++ b/.clang-format @@ -0,0 +1,58 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: None +AlignEscapedNewlines: DontAlign +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: true # forces brace next to typdef enum { +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 8 +DerivePointerAlignment: false +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +PointerAlignment: Left +ReflowComments: false +SortIncludes: true +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..a0aac7c --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,351 @@ +Checks: 'clang-diagnostic-*,clang-analyzer-*,*,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-llvm-header-guard,-misc-unused-parameters,-google-readability-braces-around-statements,-hicpp-braces-around-statements,-readability-braces-around-statements,-cppcoreguidelines-init-variables,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,-hicpp-signed-bitwise,-google-readability-todo,-cppcoreguidelines-special-member-functions,-hicpp-special-member-functions,-cert-err58-cpp,-cppcoreguidelines-owning-memory,-cppcoreguidelines-macro-usage,-modernize-use-trailing-return-type,-modernize-use-using,-hicpp-avoid-c-arrays,-modernize-avoid-c-arrays,-cppcoreguidelines-avoid-c-arrays,-fuchsia-statically-constructed-objects,-cppcoreguidelines-pro-type-vararg,-hicpp-vararg,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-google-runtime-references,-cppcoreguidelines-pro-type-union-access,-fuchsia-default-arguments-calls,-hicpp-no-array-decay,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-google-build-using-namespace,-hicpp-no-malloc,-cppcoreguidelines-no-malloc,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-fuchsia-default-arguments-declarations,-cppcoreguidelines-pro-bounds-constant-array-index,-llvmlibc-restrict-system-libc-headers,-llvmlibc-implementation-in-namespace,-llvmlibc-callee-namespace,-altera-unroll-loops,-altera-id-dependent-backward-branch,-altera-struct-pack-align,-cppcoreguidelines-avoid-non-const-global-variables,-readability-function-cognitive-complexity,-misc-no-recursion' +WarningsAsErrors: '' +HeaderFilterRegex: ((?!openssl/).)* +AnalyzeTemporaryDtors: false +FormatStyle: none +User: user +CheckOptions: + - key: abseil-string-find-startswith.AbseilStringsMatchHeader + value: 'absl/strings/match.h' + - key: abseil-string-find-startswith.IncludeStyle + value: llvm + - key: abseil-string-find-startswith.StringLikeClasses + value: '::std::basic_string' + - key: bugprone-argument-comment.CommentBoolLiterals + value: '0' + - key: bugprone-argument-comment.CommentCharacterLiterals + value: '0' + - key: bugprone-argument-comment.CommentFloatLiterals + value: '0' + - key: bugprone-argument-comment.CommentIntegerLiterals + value: '0' + - key: bugprone-argument-comment.CommentNullPtrs + value: '0' + - key: bugprone-argument-comment.CommentStringLiterals + value: '0' + - key: bugprone-argument-comment.CommentUserDefinedLiterals + value: '0' + - key: bugprone-argument-comment.IgnoreSingleArgument + value: '0' + - key: bugprone-argument-comment.StrictMode + value: '0' + - key: bugprone-assert-side-effect.AssertMacros + value: assert + - key: bugprone-assert-side-effect.CheckFunctionCalls + value: '0' + - key: bugprone-dangling-handle.HandleClasses + value: 'std::basic_string_view;std::experimental::basic_string_view' + - key: bugprone-dynamic-static-initializers.HeaderFileExtensions + value: ',h,hh,hpp,hxx' + - key: bugprone-exception-escape.FunctionsThatShouldNotThrow + value: '' + - key: bugprone-exception-escape.IgnoredExceptions + value: '' + - key: bugprone-misplaced-widening-cast.CheckImplicitCasts + value: '0' + - key: bugprone-not-null-terminated-result.WantToUseSafeFunctions + value: '1' + - key: bugprone-signed-char-misuse.CharTypdefsToIgnore + value: '' + - key: bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant + value: '1' + - key: bugprone-sizeof-expression.WarnOnSizeOfConstant + value: '1' + - key: bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression + value: '0' + - key: bugprone-sizeof-expression.WarnOnSizeOfThis + value: '1' + - key: bugprone-string-constructor.LargeLengthThreshold + value: '8388608' + - key: bugprone-string-constructor.WarnOnLargeLength + value: '1' + - key: bugprone-suspicious-enum-usage.StrictMode + value: '0' + - key: bugprone-suspicious-missing-comma.MaxConcatenatedTokens + value: '5' + - key: bugprone-suspicious-missing-comma.RatioThreshold + value: '0.200000' + - key: bugprone-suspicious-missing-comma.SizeThreshold + value: '5' + - key: bugprone-suspicious-string-compare.StringCompareLikeFunctions + value: '' + - key: bugprone-suspicious-string-compare.WarnOnImplicitComparison + value: '1' + - key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison + value: '0' + - key: bugprone-too-small-loop-variable.MagnitudeBitsUpperLimit + value: '16' + - key: bugprone-unhandled-self-assignment.WarnOnlyIfThisHasSuspiciousField + value: '1' + - key: bugprone-unused-return-value.CheckedFunctions + value: '::std::async;::std::launder;::std::remove;::std::remove_if;::std::unique;::std::unique_ptr::release;::std::basic_string::empty;::std::vector::empty' + - key: cert-dcl16-c.IgnoreMacros + value: '1' + - key: cert-dcl16-c.NewSuffixes + value: 'L;LL;LU;LLU' + - key: cert-dcl59-cpp.HeaderFileExtensions + value: ',h,hh,hpp,hxx' + - key: cert-err09-cpp.CheckThrowTemporaries + value: '1' + - key: cert-err61-cpp.CheckThrowTemporaries + value: '1' + - key: cert-msc32-c.DisallowedSeedTypes + value: 'time_t,std::time_t' + - key: cert-msc51-cpp.DisallowedSeedTypes + value: 'time_t,std::time_t' + - key: cert-oop11-cpp.IncludeStyle + value: llvm + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: '0' + - key: cppcoreguidelines-explicit-virtual-functions.AllowOverrideAndFinal + value: '0' + - key: cppcoreguidelines-explicit-virtual-functions.FinalSpelling + value: final + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: '1' + - key: cppcoreguidelines-explicit-virtual-functions.OverrideSpelling + value: override + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: '1' + - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays + value: '0' + - key: cppcoreguidelines-pro-type-member-init.UseAssignment + value: '0' + - key: fuchsia-header-anon-namespaces.HeaderFileExtensions + value: ',h,hh,hpp,hxx' + - key: fuchsia-restrict-system-includes.Includes + value: '*' + - key: google-build-namespaces.HeaderFileExtensions + value: ',h,hh,hpp,hxx' + - key: google-global-names-in-headers.HeaderFileExtensions + value: ',h,hh,hpp,hxx' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.BranchThreshold + value: '4294967295' + - key: google-readability-function-size.LineThreshold + value: '4294967295' + - key: google-readability-function-size.NestingThreshold + value: '4294967295' + - key: google-readability-function-size.ParameterThreshold + value: '4294967295' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-function-size.VariableThreshold + value: '4294967295' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: google-runtime-int.SignedTypePrefix + value: int + - key: google-runtime-int.TypeSuffix + value: '' + - key: google-runtime-int.UnsignedTypePrefix + value: uint + - key: hicpp-function-size.BranchThreshold + value: '4294967295' + - key: hicpp-function-size.LineThreshold + value: '4294967295' + - key: hicpp-function-size.NestingThreshold + value: '4294967295' + - key: hicpp-function-size.ParameterThreshold + value: '4294967295' + - key: hicpp-function-size.StatementThreshold + value: '800' + - key: hicpp-function-size.VariableThreshold + value: '4294967295' + - key: hicpp-member-init.IgnoreArrays + value: '0' + - key: hicpp-member-init.UseAssignment + value: '0' + - key: hicpp-move-const-arg.CheckTriviallyCopyableMove + value: '1' + - key: hicpp-multiway-paths-covered.WarnOnMissingElse + value: '0' + - key: hicpp-named-parameter.IgnoreFailedSplit + value: '0' + - key: hicpp-uppercase-literal-suffix.IgnoreMacros + value: '1' + - key: hicpp-uppercase-literal-suffix.NewSuffixes + value: '' + - key: hicpp-use-auto.MinTypeNameLength + value: '5' + - key: hicpp-use-auto.RemoveStars + value: '0' + - key: hicpp-use-emplace.ContainersWithPushBack + value: '::std::vector;::std::list;::std::deque' + - key: hicpp-use-emplace.SmartPointers + value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' + - key: hicpp-use-emplace.TupleMakeFunctions + value: '::std::make_pair;::std::make_tuple' + - key: hicpp-use-emplace.TupleTypes + value: '::std::pair;::std::tuple' + - key: hicpp-use-equals-default.IgnoreMacros + value: '1' + - key: hicpp-use-equals-delete.IgnoreMacros + value: '1' + - key: hicpp-use-noexcept.ReplacementString + value: '' + - key: hicpp-use-noexcept.UseNoexceptFalse + value: '1' + - key: hicpp-use-nullptr.NullMacros + value: '' + - key: hicpp-use-override.AllowOverrideAndFinal + value: '0' + - key: hicpp-use-override.FinalSpelling + value: final + - key: hicpp-use-override.IgnoreDestructors + value: '0' + - key: hicpp-use-override.OverrideSpelling + value: override + - key: misc-definitions-in-headers.HeaderFileExtensions + value: ',h,hh,hpp,hxx' + - key: misc-definitions-in-headers.UseHeaderFileExtension + value: '1' + - key: misc-throw-by-value-catch-by-reference.CheckThrowTemporaries + value: '1' + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-make-shared.IgnoreMacros + value: '1' + - key: modernize-make-shared.IncludeStyle + value: llvm + - key: modernize-make-shared.MakeSmartPtrFunction + value: 'std::make_shared' + - key: modernize-make-shared.MakeSmartPtrFunctionHeader + value: memory + - key: modernize-make-unique.IgnoreMacros + value: '1' + - key: modernize-make-unique.IncludeStyle + value: llvm + - key: modernize-make-unique.MakeSmartPtrFunction + value: 'std::make_unique' + - key: modernize-make-unique.MakeSmartPtrFunctionHeader + value: memory + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-pass-by-value.ValuesOnly + value: '0' + - key: modernize-raw-string-literal.ReplaceShorterLiterals + value: '0' + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-replace-random-shuffle.IncludeStyle + value: llvm + - key: modernize-use-auto.MinTypeNameLength + value: '5' + - key: modernize-use-auto.RemoveStars + value: '0' + - key: modernize-use-default-member-init.IgnoreMacros + value: '1' + - key: modernize-use-default-member-init.UseAssignment + value: '0' + - key: modernize-use-emplace.ContainersWithPushBack + value: '::std::vector;::std::list;::std::deque' + - key: modernize-use-emplace.SmartPointers + value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr' + - key: modernize-use-emplace.TupleMakeFunctions + value: '::std::make_pair;::std::make_tuple' + - key: modernize-use-emplace.TupleTypes + value: '::std::pair;::std::tuple' + - key: modernize-use-equals-default.IgnoreMacros + value: '1' + - key: modernize-use-equals-delete.IgnoreMacros + value: '1' + - key: modernize-use-nodiscard.ReplacementString + value: '[[nodiscard]]' + - key: modernize-use-noexcept.ReplacementString + value: '' + - key: modernize-use-noexcept.UseNoexceptFalse + value: '1' + - key: modernize-use-nullptr.NullMacros + value: 'NULL' + - key: modernize-use-override.AllowOverrideAndFinal + value: '0' + - key: modernize-use-override.FinalSpelling + value: final + - key: modernize-use-override.IgnoreDestructors + value: '0' + - key: modernize-use-override.OverrideSpelling + value: override + - key: modernize-use-transparent-functors.SafeMode + value: '0' + - key: objc-forbidden-subclassing.ForbiddenSuperClassNames + value: 'ABNewPersonViewController;ABPeoplePickerNavigationController;ABPersonViewController;ABUnknownPersonViewController;NSHashTable;NSMapTable;NSPointerArray;NSPointerFunctions;NSTimer;UIActionSheet;UIAlertView;UIImagePickerController;UITextInputMode;UIWebView' + - key: openmp-exception-escape.IgnoredExceptions + value: '' + - key: performance-faster-string-find.StringLikeClasses + value: 'std::basic_string' + - key: performance-for-range-copy.AllowedTypes + value: '' + - key: performance-for-range-copy.WarnOnAllAutoCopies + value: '0' + - key: performance-inefficient-string-concatenation.StrictMode + value: '0' + - key: performance-inefficient-vector-operation.EnableProto + value: '0' + - key: performance-inefficient-vector-operation.VectorLikeClasses + value: '::std::vector' + - key: performance-move-const-arg.CheckTriviallyCopyableMove + value: '1' + - key: performance-move-constructor-init.IncludeStyle + value: llvm + - key: performance-no-automatic-move.AllowedTypes + value: '' + - key: performance-type-promotion-in-math-fn.IncludeStyle + value: llvm + - key: performance-unnecessary-copy-initialization.AllowedTypes + value: '' + - key: performance-unnecessary-value-param.AllowedTypes + value: '' + - key: performance-unnecessary-value-param.IncludeStyle + value: llvm + - key: portability-simd-intrinsics.Std + value: '' + - key: portability-simd-intrinsics.Suggest + value: '0' + - key: readability-else-after-return.WarnOnUnfixable + value: '1' + - key: readability-function-size.BranchThreshold + value: '4294967295' + - key: readability-function-size.LineThreshold + value: '4294967295' + - key: readability-function-size.NestingThreshold + value: '4294967295' + - key: readability-function-size.ParameterThreshold + value: '4294967295' + - key: readability-function-size.StatementThreshold + value: '800' + - key: readability-function-size.VariableThreshold + value: '4294967295' + - key: readability-identifier-naming.IgnoreFailedSplit + value: '0' + - key: readability-inconsistent-declaration-parameter-name.IgnoreMacros + value: '1' + - key: readability-inconsistent-declaration-parameter-name.Strict + value: '0' + - key: readability-redundant-member-init.IgnoreBaseInCopyConstructors + value: '0' + - key: readability-redundant-smartptr-get.IgnoreMacros + value: '1' + - key: readability-redundant-string-init.StringNames + value: '::std::basic_string' + - key: readability-simplify-boolean-expr.ChainedConditionalAssignment + value: '0' + - key: readability-simplify-boolean-expr.ChainedConditionalReturn + value: '0' + - key: readability-simplify-subscript-expr.Types + value: '::std::basic_string;::std::basic_string_view;::std::vector;::std::array' + - key: readability-static-accessed-through-instance.NameSpecifierNestingThreshold + value: '3' + - key: readability-uppercase-literal-suffix.IgnoreMacros + value: '1' + - key: readability-uppercase-literal-suffix.NewSuffixes + value: '' + - key: zircon-temporary-objects.Names + value: '' diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5f751c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +cmake-build-* +.idea diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a9ae85a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,168 @@ +# Copyright 2020-2022 Comcast Cable Communications Management, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.16) +project(sec_api_2_adapter C CXX) + +set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE -pthread") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE") + +set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../cmake" ${CMAKE_MODULE_PATH}) +message("CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") +include(ClangFormat) + +if (DEFINED ENABLE_CLANG_TIDY) + find_program(CLANG_TIDY_COMMAND NAMES clang-tidy) + if (CLANG_TIDY_COMMAND) + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_COMMAND}; ) + set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_COMMAND}; ) + message("clang-tidy found--enabling") + else () + message("clang-tidy not found") + endif () +else() + message("clang-tidy disabled") +endif () + +if (DEFINED ENABLE_SOC_KEY_TESTS) + set(CMAKE_CXX_FLAGS "-DENABLE_SOC_KEY_TESTS ${CMAKE_CXX_FLAGS}") + set(CMAKE_C_FLAGS "-DENABLE_SOC_KEY_TESTS ${CMAKE_C_FLAGS}") +endif () + +enable_testing() + +# Set OPENSSL_ROOT_DIR if not found +find_package(OpenSSL REQUIRED) +# Set YAJL_ROOT if not found +find_package(YAJL REQUIRED) +# Set SACLIENT_ROOT if not found +find_package(SACLIENT REQUIRED) + +include_directories( + ${OPENSSL_INCLUDE_DIR} + ${YAJL_INCLUDE_DIR} + ${SACLIENT_INCLUDE_DIR} + include + src +) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + add_compile_options(-DSEC_TARGET_IOS=1) +endif () + +add_compile_options(-DYAJL_V2=true) + +add_library(sec_api SHARED + include/sec_security.h + include/sec_security_asn1kc.h + include/sec_security_comcastids.h + include/sec_security_common.h + include/sec_security_datatype.h + include/sec_version.h + src/sec_adapter_bundle.h src/sec_adapter_bundle.c + src/sec_adapter_cipher.h src/sec_adapter_cipher.c + src/sec_adapter_key.h src/sec_adapter_key.c + src/sec_adapter_processor.h src/sec_adapter_processor.c + src/sec_adapter_pubops.h src/sec_adapter_pubops.c + src/sec_adapter_store.h src/sec_adapter_store.c + src/sec_adapter_svp.h src/sec_adapter_svp.c + src/sec_adapter_asn1kc.c + src/sec_adapter_buffer.c + src/sec_adapter_certificate.c + src/sec_adapter_common.c + src/sec_adapter_digest.c + src/sec_adapter_engine.c + src/sec_adapter_keyexchange.c + src/sec_adapter_logger.c + src/sec_adapter_mac.c + src/sec_adapter_random.c + src/sec_adapter_signature.c + src/sec_adapter_utils.c + src/sec_adapter_utils_time.c + src/sec_adapter_key_legacy.h) + +set_target_properties(sec_api PROPERTIES + LINKER_LANGUAGE C + SO_VERSION 2.3 + VERSION 2.3.2.4 + ) + +target_link_libraries(sec_api + PRIVATE + ${SACLIENT_LIBRARY} + ${OPENSSL_CRYPTO_LIBRARY} + ) + +target_clangformat_setup(sec_api) + +add_executable(sec_api_2_adapter_test + test/main/headers/test_creds.h + test/openssl/headers/test_ctx.h + test/main/cpp/bundle.cpp + test/main/cpp/cert.cpp + test/main/cpp/cipher.cpp + test/main/cpp/concurrent.cpp + test/main/cpp/digest.cpp + test/main/cpp/exchange.cpp + test/main/cpp/jtype.cpp + test/main/cpp/key.cpp + test/main/cpp/keyctrl.cpp + test/main/cpp/mac.cpp + test/main/cpp/processor.cpp + test/main/cpp/random.cpp + test/main/cpp/sec_api_utest_main.cpp + test/main/cpp/sign.cpp + test/main/cpp/svp.cpp + test/main/cpp/test_creds_clear.cpp + test/main/cpp/test_ctx.cpp + test/main/cpp/wrapped.cpp + test/openssl/headers/test_ctx.h + test/openssl/headers/test_creds.h + test/openssl/src/test_creds_soc.cpp + test/openssl/src/sa_soc_key_container.cpp + ) + +target_include_directories(sec_api_2_adapter_test + PRIVATE + src + test/main/cpp + test/openssl/headers + ) + +target_link_libraries(sec_api_2_adapter_test + PRIVATE + sec_api + ${SACLIENT_LIBRARY} + ${OPENSSL_CRYPTO_LIBRARY} + ${YAJL_LIBRARY} + ) + +add_test(sec_api2_adapter_test sec_api_utest_main) + +target_clangformat_setup(sec_api_2_adapter_test) + +# 'make install' to the correct locations (provided by GNUInstallDirs). +install(TARGETS sec_api sec_api_2_adapter_test + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + ) + +install(DIRECTORY include/ DESTINATION include) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..dba4929 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,12 @@ +Contributing +============ + +If you wish to make code contributions to this project, the master source is hosted at +[code.rdkcentral.com](https://code.rdkcentral.com/r/#/admin/projects/components/generic/secapi2-adapter). +You can submit your changes for review via that site. + +Please follow the [workflow](https://wiki.rdkcentral.com/display/CMF/Gerrit+Development+Workflow) when making a +contribution. + +In order to contribute code, first-time users are requested to agree to the +[license](https://wiki.rdkcentral.com/signup.action). diff --git a/COPYING b/COPYING new file mode 120000 index 0000000..7a694c9 --- /dev/null +++ b/COPYING @@ -0,0 +1 @@ +LICENSE \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5e41586 --- /dev/null +++ b/LICENSE @@ -0,0 +1,252 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 Comcast Cable Communications Management, LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +Extracts from RFC 3526 are: +Copyright (C) The Internet Society (2003). All Rights Reserved. + +This document and translations of it may be copied and furnished to +others, and derivative works that comment on or otherwise explain it +or assist in its implementation may be prepared, copied, published +and distributed, in whole or in part, without restriction of any +kind, provided that the above copyright notice and this paragraph are +included on all such copies and derivative works. However, this +document itself may not be modified in any way, such as by removing +the copyright notice or references to the Internet Society or other +Internet organizations, except as needed for the purpose of +developing Internet standards in which case the procedures for +copyrights defined in the Internet Standards process must be +followed, or as required to translate it into languages other than +English. + +The limited permissions granted above are perpetual and will not be +revoked by the Internet Society or its successors or assigns. +This document and the information contained herein is provided on an +"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING +TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION +HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF +MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..b419829 --- /dev/null +++ b/NOTICE @@ -0,0 +1,29 @@ +secapi2_adapter + +Copyright 2020 Comcast Cable Communications Management, LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 + +This product includes software developed at Comcast (http://www.comcast.com/). + +The component may include material which is licensed under other licenses / copyrights as +listed below. Your use of this material within the component is also subject to the terms and +conditions of these licenses. The LICENSE file contains the text of all the licenses which apply within this component. + +Extracts from RFC 3526 are: +Copyright (C) The Internet Society (2003). All Rights Reserved. + +Copyright Tomas Zeman 2019-2020 +Distributed under the Boost Software License, Version 1.0. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9f95a7d --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# sec_api_2_adapter + +## Summary + +sec_api_2_adapter is a SOC neutral implementation of SecApi 2.3 that uses SecApi 3 as it's cryptographic implementation. +sec_api_2_adapter uses SecApi 2.3 unit tests to test the library. + +There are a few features in SecApi 2.3 that are not supported due to incompatibility with SecApi 3: + +- SecCipher_ProcessCtrWithOpaqueDataShift (Data shift not supported) +- SecCipher_ProcessCtrWithDataShift (Data shift not supported) +- SecProcessor_GetInstance (Deprecated function) +- SecOpaqueBuffer_Release (Not supported by SecApi 3) +- SecCodeIntegrity_SecureBootEnabled (Not supported by SecApi 3) +- SecSVP_SetTime (Not supported by SecApi 3) + +Calling any of these functions will return a SEC_RESULT_UNIMPLEMENTED_FEATURE error. + +## Directories + +- The include directory contains the SecApi 2.3 API include files. +- The src directory contains the sources for the sec_api_2_adapter. +- The test directory contains the SecApi 2.3 tests that have been modified to test the sec_api_2_adapter. +- The cmake directory contains additional cmake build helper files. +- The root directory contains the master cmake build files. + +## Building + +### Generate Build Files + +To build sec_api_2_adapter, first run cmake to generate build files. + +The build assumes that the following packages have already been installed: +YAJL - include -DYAJL_ROOT= if not found +OPENSSL - include -DOPENSSL_ROOT_DIR= if not found +SecApi3 - include -DSACLIENT_ROOT= if not found +Add -DCMAKE_INSTALL_PREFIX= to install to a non-standard install directory. + + +``` +cmake -S . -B cmake-build +``` + + +### Build + +To build sec_api_2_adapter, run a cmake build + +``` +cmake --build cmake-build +``` + +This creates a library, libsec_api.(so/dll/dylib) containing the adapter code (the extension .so/.dll/.dylib +created depends on which platform you are building on). It also creates a test application, sec_api_2_adapter_test, to +test the library. + +SOC and root key tests are also disabled by default. To enable these tests, add -DENABLE_SOC_KEY_TESTS=1. The test_root +key defined in test_creds_clear.cpp must match the root key defined on the test device for these tests to pass. + +Run unit test suite + +``` +cmake-build/sec_api_2_adapter_test +``` + +The tests for the reference implementation expect a SA_TEST_CLEAR_SOC_KEY_FORMAT to +exist so that SOC based key containers can be tested. + + +### Install + +To install sec_api_2_adapter, run a cmake install + +``` +cmake --install cmake-build +``` + +This copies the include files, the library, libsec_api.(so/dll/dylib) containing the adapter code (the +extension .so/.dll/.dylib created depends on which platform you are building on), and the test application, +sec_api_2_adapter_test, to their appropriate locations on the system. diff --git a/cmake/ClangFormat.cmake b/cmake/ClangFormat.cmake new file mode 100644 index 0000000..d041d14 --- /dev/null +++ b/cmake/ClangFormat.cmake @@ -0,0 +1,53 @@ +# Copyright Tomas Zeman 2019-2020. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +function(prefix_clangformat_setup prefix) + if(NOT CLANGFORMAT_EXECUTABLE) + set(CLANGFORMAT_EXECUTABLE clang-format) + endif() + + if(NOT EXISTS ${CLANGFORMAT_EXECUTABLE}) + find_program(clangformat_executable_tmp ${CLANGFORMAT_EXECUTABLE}) + if(clangformat_executable_tmp) + message("clang-format found--enabling") + set(CLANGFORMAT_EXECUTABLE ${clangformat_executable_tmp}) + unset(clangformat_executable_tmp) + else() + message("clang-format not found") + endif() + endif() + + foreach(clangformat_source ${ARGN}) + get_filename_component(clangformat_source ${clangformat_source} ABSOLUTE) + list(APPEND clangformat_sources ${clangformat_source}) + endforeach() + + add_custom_target(${prefix}_clangformat + COMMAND + ${CLANGFORMAT_EXECUTABLE} + -style=file + -i + ${clangformat_sources} + WORKING_DIRECTORY + ${CMAKE_SOURCE_DIR} + COMMENT + "Formatting ${prefix} with ${CLANGFORMAT_EXECUTABLE} ..." + ) + + if(TARGET clangformat) + add_dependencies(clangformat ${prefix}_clangformat) + else() + add_custom_target(clangformat DEPENDS ${prefix}_clangformat) + endif() +endfunction() + +function(clangformat_setup) + prefix_clangformat_setup(${PROJECT_NAME} ${ARGN}) +endfunction() + +function(target_clangformat_setup target) + get_target_property(target_sources ${target} SOURCES) + prefix_clangformat_setup(${target} ${target_sources}) +endfunction() diff --git a/cmake/FindSACLIENT.cmake b/cmake/FindSACLIENT.cmake new file mode 100755 index 0000000..d954a52 --- /dev/null +++ b/cmake/FindSACLIENT.cmake @@ -0,0 +1,36 @@ +# +# Copyright 2020-2022 Comcast Cable Communications Management, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# Find libyajl + +FIND_PATH(SACLIENT_INCLUDE_DIR sa.h) + +SET(SACLIENT_NAMES ${SACLIENT_NAMES} saclient libsaclient) +FIND_LIBRARY(SACLIENT_LIBRARY NAMES ${SACLIENT_NAMES} PATH) + +IF(SACLIENT_INCLUDE_DIR AND SACLIENT_LIBRARY) + SET(SACLIENT_FOUND TRUE) +ENDIF(SACLIENT_INCLUDE_DIR AND SACLIENT_LIBRARY) + +IF(SACLIENT_FOUND) + IF(NOT SACLIENT_FIND_QUIETLY) + MESSAGE(STATUS "Found SACLIENT: ${SACLIENT_LIBRARY}") + ENDIF (NOT SACLIENT_FIND_QUIETLY) +ELSE(SACLIENT_FOUND) + IF(SACLIENT_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find saclient") + ENDIF(SACLIENT_FIND_REQUIRED) +ENDIF(SACLIENT_FOUND) diff --git a/cmake/FindYAJL.cmake b/cmake/FindYAJL.cmake new file mode 100755 index 0000000..0ce6d45 --- /dev/null +++ b/cmake/FindYAJL.cmake @@ -0,0 +1,36 @@ +# +# Copyright 2020-2021 Comcast Cable Communications Management, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# Find libyajl + +FIND_PATH(YAJL_INCLUDE_DIR yajl/yajl_common.h) + +SET(YAJL_NAMES ${YAJL_NAMES} yajl libyajl) +FIND_LIBRARY(YAJL_LIBRARY NAMES ${YAJL_NAMES} PATH) + +IF(YAJL_INCLUDE_DIR AND YAJL_LIBRARY) + SET(YAJL_FOUND TRUE) +ENDIF(YAJL_INCLUDE_DIR AND YAJL_LIBRARY) + +IF(YAJL_FOUND) + IF(NOT Yajl_FIND_QUIETLY) + MESSAGE(STATUS "Found Yajl: ${YAJL_LIBRARY}") + ENDIF (NOT Yajl_FIND_QUIETLY) +ELSE(YAJL_FOUND) + IF(Yajl_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find yajl") + ENDIF(Yajl_FIND_REQUIRED) +ENDIF(YAJL_FOUND) diff --git a/include/sec_security.h b/include/sec_security.h new file mode 100644 index 0000000..d0c13e5 --- /dev/null +++ b/include/sec_security.h @@ -0,0 +1,989 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_SECURITY_H +#define SEC_SECURITY_H + +#include "sec_security_common.h" +#include "sec_security_datatype.h" +#if !defined(SEC_TARGET_IOS) && !defined(SEC_TARGET_TVOS) +#include +#endif +#include + +#ifdef __cplusplus +#include +extern "C" { +#else +#include +#endif + +#define OPENSSL_SUCCESS 1 + +#define SEC_KEYSTORAGE_FILE_DEFAULT_DIR "/opt/drm" +#define SEC_CERTIFICATESTORAGE_FILE_DEFAULT_DIR "/opt/drm" +#define SEC_BUNDLESTORAGE_FILE_DEFAULT_DIR "/opt/drm" + +/* macro to string */ +#define SEC_MTOS_(x) #x +#define SEC_MTOS(x) SEC_MTOS_(x) + +/* min */ +#ifndef SEC_MIN +#define SEC_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +/* max */ +#ifndef SEC_MAX +#define SEC_MAX(a, b) (((a) < (b)) ? (b) : (a)) +#endif + +#define SEC_FREE(x) \ + do { \ + if ((x) != NULL) { \ + free(x); \ + (x) = NULL; \ + } \ + } while (0) +#define SEC_RSA_FREE(x) \ + do { \ + if ((x) != NULL) { \ + RSA_free(x); \ + (x) = NULL; \ + } \ + } while (0) +#define SEC_ECC_FREE(x) \ + do { \ + if ((x) != NULL) { \ + EC_KEY_free(x); \ + (x) = NULL; \ + } \ + } while (0) +#define SEC_EVPPKEY_FREE(x) \ + do { \ + if ((x) != NULL) { \ + EVP_PKEY_free(x); \ + (x) = NULL; \ + } \ + } while (0) +#define SEC_BIO_FREE(x) \ + do { \ + if ((x) != NULL) { \ + BIO_free(x); \ + (x) = NULL; \ + } \ + } while (0) +#define SEC_X509_FREE(x) \ + do { \ + if ((x) != NULL) { \ + X509_free(x); \ + (x) = NULL; \ + } \ + } while (0) + +/* debug prints */ +#define SEC_PRINT(fmt, ...) \ + do { \ + if (Sec_GetLogger() != NULL) { \ + Sec_GetLogger()(fmt, ##__VA_ARGS__); \ + } \ + } while (0) + +#if defined(SEC_TARGET_IOS) || defined(SEC_TARGET_TVOS) +#define SEC_LOG(txt, ...) \ + do { \ + SEC_PRINT("" txt " (%s, %s, line %d)\n", ##__VA_ARGS__, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ + } while (0) +#else +#define SEC_LOG(txt, ...) \ + do { \ + SEC_PRINT("[%ld] " txt " (%s, %s, line %d)\n", (long int) syscall(SYS_gettid), ##__VA_ARGS__, \ + __PRETTY_FUNCTION__, __FILE__, __LINE__); \ + } while (0) +#endif + +#define SEC_LOG_ERROR(txt, ...) \ + do { \ + SEC_LOG("ERROR: " txt, ##__VA_ARGS__); \ + } while (0) + +#define SEC_TRACE(enabled, txt, ...) \ + do { \ + if (enabled) { \ + SEC_LOG(#enabled ": " txt, ##__VA_ARGS__); \ + } \ + } while (0) + +/** + * @brief Initialize secure processor + * + * Initializes the secure processor, generates key derivation base key, + * sets up all required resources. Only one secure processor can be + * active at a time. + * + * @param processorHandle pointer to a processor handle that will be set to + * a constructed handle. + * @param socInitParams pointer to initialization information for the secure + * processor. This structure is implementation specific. + * + * @return The status of the operation + */ +Sec_Result SecProcessor_GetInstance(Sec_ProcessorHandle** processorHandle, + Sec_ProcessorInitParams* socInitParams); + +/** + * @brief Initialize secure processor + * + * Initializes the secure processor, generates key derivation base key, + * sets up all required resources. Only one secure processor can be + * active at a time. + * + * @param processorHandle pointer to a processor handle that will be set to + * a constructed handle. + * @param globalDir path to the read only object directory. Can be set to NULL. + * @param appDir path to the read/write object directory. Can be set to NULL. + * + * @return The status of the operation + */ +Sec_Result SecProcessor_GetInstance_Directories(Sec_ProcessorHandle** processorHandle, const char* globalDir, + const char* appDir); + +/** + * @brief Get the minimum depth of the hardware key ladder + * + * @param handle pointer to a handle + * @param root root key type + * + * @return The key ladder depth + */ +SEC_SIZE SecProcessor_GetKeyLadderMinDepth(Sec_ProcessorHandle* processorHandle, Sec_KeyLadderRoot root); + +/** + * @brief Get the maximum depth of the hardware key ladder + * + * @param handle pointer to a handle + * @param root root key type + * + * @return The key ladder depth + */ +SEC_SIZE SecProcessor_GetKeyLadderMaxDepth(Sec_ProcessorHandle* processorHandle, Sec_KeyLadderRoot root); + +/** + * @brief Prints SOC specific version info + * + * @param processorHandle secure processor handle + */ +Sec_Result SecProcessor_PrintInfo(Sec_ProcessorHandle* processorHandle); + +/** + * @brief Get the Security Processor information (SecAPI version and build + * information). + * + * @param processorHandle secure processor handle + * @param pointer to secure processor information + */ +Sec_Result SecProcessor_GetInfo(Sec_ProcessorHandle* processorHandle, Sec_ProcessorInfo* secProcInfo); + +/** + * @brief Obtain the device id + * + * @param processorHandle secure processor handle + * @param deviceId pointer to a buffer that is SEC_DEVICEID_LEN long. The + * buffer will be filled with a device id. + * + * @return The status of the operation + */ +Sec_Result SecProcessor_GetDeviceId(Sec_ProcessorHandle* processorHandle, SEC_BYTE* deviceId); + +/** + * @brief Release the security processor + * + * @param processorHandle secure processor handle + * + * @return The status of the operation + */ +Sec_Result SecProcessor_Release(Sec_ProcessorHandle* processorHandle); + +/** + * @brief Initialize cipher object + * + * @param processorHandle secure processor handle + * @param algorithm cipher algorithm to use + * @param mode cipher mode to use + * @param keyHandle handle to use + * @param iv initialization vector value. Can be set to NULL is the cipher + * algorithm chosen does not require it. + * @param cipherHandle pointer to a cipher handle that will be set once + * the cipher object is constructed + * + * @return The status of the operation + */ +Sec_Result SecCipher_GetInstance(Sec_ProcessorHandle* processorHandle, Sec_CipherAlgorithm algorithm, + Sec_CipherMode mode, Sec_KeyHandle* keyHandle, SEC_BYTE* iv, Sec_CipherHandle** cipherHandle); + +/** + * @brief Update the IV on the cipher handle + */ +Sec_Result SecCipher_UpdateIV(Sec_CipherHandle* cipherHandle, SEC_BYTE* iv); + +/** + * @brief En/De-cipher specified input data into and output buffer + * + * @param cipherHandle cipher handle + * @param input pointer to input data + * @param inputSize the length of input data in bytes + * @param lastInput boolean value specifying whether this is the last chunk + * of input that will be processed. + * @param output pointer to output data buffer + * @param outputSize the size of the output buffer + * @param bytesWritten pointer to a value that will be set to number + * of bytes written to the output buffer + * + * @return The status of the operation + */ +Sec_Result SecCipher_Process(Sec_CipherHandle* cipherHandle, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BOOL lastInput, + SEC_BYTE* output, SEC_SIZE outputSize, SEC_SIZE* bytesWritten); + +/** + * @brief En/De-cipher specified fragmented input data into and output buffer + * + * @param cipherHandle cipher handle + * @param input pointer to input data + * @param inputSize the length of input data in bytes + * @param lastInput boolean value specifying whether this is the last chunk + * of input that will be processed. + * @param output pointer to output data buffer + * @param outputSize the size of the output buffer + * @param bytesWritten pointer to a value that will be set to number + * of bytes written to the output buffer + * @param framentOffset offset in bytes of the fragment data within larger packet + * @param fragmentSize length in bytes of the data fragment + * @param fragmentPeriod the length in bytes of the packet containing the fragment + * + * @return The status of the operation + */ +Sec_Result SecCipher_ProcessFragmented(Sec_CipherHandle* cipherHandle, SEC_BYTE* input, SEC_SIZE inputSize, + SEC_BOOL lastInput, SEC_BYTE* output, SEC_SIZE outputSize, SEC_SIZE* bytesWritten, SEC_SIZE fragmentOffset, + SEC_SIZE fragmentSize, SEC_SIZE fragmentPeriod); + +/** + * @brief Process the opaque buffers that were obtained with Sec_OpaqueBufferMalloc + * + * @param cipherHandle cipher handle + * @param inputHandle opaque buffer containing input + * @param outputHandle opaque buffer for writing output + * @param inputSize the length of input to process + * @param lastInput boolean value specifying whether this is the last chunk + * of input that will be processed. + * @param bytesWritten pointer to a value that will be set to number + * of bytes written to the output buffer + */ +Sec_Result SecCipher_ProcessOpaque(Sec_CipherHandle* cipherHandle, Sec_OpaqueBufferHandle* inOpaqueBufferHandle, + Sec_OpaqueBufferHandle* outOpaqueBufferHandle, SEC_SIZE inputSize, SEC_BOOL lastInput, SEC_SIZE* bytesWritten); + +Sec_Result SecCipher_ProcessCtrWithOpaqueDataShift(Sec_CipherHandle* cipherHandle, + Sec_OpaqueBufferHandle* inOpaqueBufferHandle, Sec_OpaqueBufferHandle* outOpaqueBufferHandle, SEC_SIZE inputSize, + SEC_SIZE* bytesWritten, SEC_SIZE dataShift); + +/** + * @brief Perform cipher operation on the opaque input handle and check the output against the expected value. + * + * @param cipherHandle pointer to Sec_CipherHandle + * @param opaqueBufferHandle pointer to opaque buffer containing input + * @param SEC_SIZE checkLength number of bytes used for comparison + * @param SEC_BYTE expected expected value used for comparison + */ +Sec_Result SecCipher_KeyCheckOpaque(Sec_CipherHandle* cipherHandle, Sec_OpaqueBufferHandle* opaqueBufferHandle, + SEC_SIZE checkLength, SEC_BYTE* expected); +/** + * @brief Release the cipher object + * + * @param cipherHandle cipher handle + * + * @return The status of the operation + */ +Sec_Result SecCipher_Release(Sec_CipherHandle* cipherHandle); + +/** + * @brief Obtain a digest object handle + * + * @param processorHandle secure processor handle + * @param algorithm digest algorithm to use + * @param digestHandle output digest object handle + * + * @return The status of the operation + */ +Sec_Result SecDigest_GetInstance(Sec_ProcessorHandle* processorHandle, Sec_DigestAlgorithm algorithm, + Sec_DigestHandle** digestHandle); + +/** + * @brief Update the digest value with the specified input + * + * @param digestHandle handle of the digest object + * @param input pointer to the input buffer + * @param inputSize size of the input buffer + * + * @return The status of the operation + */ +Sec_Result SecDigest_Update(Sec_DigestHandle* digestHandle, SEC_BYTE* input, SEC_SIZE inputSize); +/** + * @brief Update the digest value with the key data + * + * @param digestHandle handle of the digest object + * @param keyHandle key to use + * + * @return The status of the operation + */ +Sec_Result SecDigest_UpdateWithKey(Sec_DigestHandle* digestHandle, Sec_KeyHandle* keyHandle); + +/** + * @brief Calculate the resulting digest value and release the digest object + * + * @param digestHandle digest handle + * @param digestOutput pointer to an output buffer that will be filled with the resulting + * digest value. Buffer should be SEC_DIGEST_MAX_LEN bytes long. + * @param digestSize pointer to a value that will be set to actual size of the digest value + * + * @return The status of the operation + */ +Sec_Result SecDigest_Release(Sec_DigestHandle* digestHandle, SEC_BYTE* digestOutput, SEC_SIZE* digestSize); + +/** + * @brief Obtian a handle to the signature calculator + * + * @param processorHandle secure processor handle + * @param algorithm signing algorithm + * @param mode signing mode + * @param keyHandle key used for signing operations + * @param signatureHandle output signature handle + * + * @return The status of the operation + */ +Sec_Result SecSignature_GetInstance(Sec_ProcessorHandle* processorHandle, Sec_SignatureAlgorithm algorithm, + Sec_SignatureMode mode, Sec_KeyHandle* keyHandle, Sec_SignatureHandle** signatureHandle); + +/** + * @brief Sign/Verify Signature of the input data + * + * @param signatureHandle signature handle + * @param input pointer to the input buffer whose signature we are generating/verifying + * @param inputSize the length of the input + * @param signature buffer where signature is/will be stored + * @param signatureSize output variable that will be set to the signature size + * + * @return The status of the operation + */ +Sec_Result SecSignature_Process(Sec_SignatureHandle* signatureHandle, SEC_BYTE* input, SEC_SIZE inputSize, + SEC_BYTE* signature, SEC_SIZE* signatureSize); + +/** + * @brief Release the signature object + * + * @param signatureHandle cipher handle + * + * @return The status of the operation + */ +Sec_Result SecSignature_Release(Sec_SignatureHandle* signatureHandle); + +/** + * @brief Obtain a handle for the MAC calculator + * + * @param processorHandle secure processor handle + * @param algorithm MAC algorithm to use for MAC calculation + * @param keyHandle key to use for the MAC calculation + * @param macHandle output MAC calculator handle + * + * @return The status of the operation + */ +Sec_Result SecMac_GetInstance(Sec_ProcessorHandle* processorHandle, Sec_MacAlgorithm algorithm, + Sec_KeyHandle* keyHandle, Sec_MacHandle** macHandle); + +/** + * @brief Updates the digest value with the input data + * + * @param macHandle mac handle + * @param input pointer to the input data + * @param size of the input buffer + * + * @return The status of the operation + */ +Sec_Result SecMac_Update(Sec_MacHandle* macHandle, SEC_BYTE* input, SEC_SIZE inputSize); + +/** + * @brief Updates the digest value with the contents of a key + * + * @param macHandle mac handle + * @param keyHandle key to use + * + * @return The status of the operation + */ +Sec_Result SecMac_UpdateWithKey(Sec_MacHandle* macHandle, Sec_KeyHandle* keyHandle); + +/** + * @brief Calculate the resulting MAC value and release the MAC object + * + * @param macHandle mac handle + * @param macBuffer pointer to an output buffer that will be filled with the resulting + * MAC value. Buffer should be SEC_MAC_MAX_LEN bytes long. + * @param macSize pointer to a value that will be set to actual size of the MAC value + * + * @return The status of the operation + */ +Sec_Result SecMac_Release(Sec_MacHandle* macHandle, SEC_BYTE* macBuffer, SEC_SIZE* macSize); + +/** + * @brief Obtain a handle to the random number generator + * + * @param processorHandle secure processor handle + * @param algorithm random number algorithm to use + * @param randomHandle output handle for the random number generator + * + * @return The status of the operation + */ +Sec_Result SecRandom_GetInstance(Sec_ProcessorHandle* processorHandle, Sec_RandomAlgorithm algorithm, + Sec_RandomHandle** randomHandle); + +/** + * @brief Generate random data + * + * @param randomHandle random number generator handle + * @param output pointer to the output buffer where the random data will be stored + * @param outpuSize the size of the output buffer + * + * @return The status of the operation + */ +Sec_Result SecRandom_Process(Sec_RandomHandle* randomHandle, SEC_BYTE* output, SEC_SIZE outputSize); + +/** + * @brief Release the random object + * + * @param randomHandle random handle + * + * @return The status of the operation + */ +Sec_Result SecRandom_Release(Sec_RandomHandle* randomHandle); + +/** + * @brief Obtain a handle to the provisioned certificate + * + * @param processorHandle secure processor handle + * @param object_id id of the certificate + * @param certHandle output certificate handle + * + * @return The status of the operation + */ +Sec_Result SecCertificate_GetInstance(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_CertificateHandle** certHandle); + +/** + * @brief Provision a certificate onto the system + * + * @param processorHandle secure processor handle + * @param object_id id of the certificate to provision + * @param location location where the certificate should be provisioned to + * @param data_type container type for the input certificate data + * @param data pointer to certificate container data + * @param data_len certificate container data length + * + * @return The status of the operation + */ +Sec_Result SecCertificate_Provision(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_StorageLoc location, Sec_CertificateContainer data_type, SEC_BYTE* data, SEC_SIZE data_len); + +/** + * @brief Delete the specified certificate from the system + * + * @param processorHandle secure processor handle + * @param object_id id of the certificate to delete + * + * @return The status of the operation + */ +Sec_Result SecCertificate_Delete(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id); + +/** + * @brief Extract the RSA public key information from the certificate + * + * @param certificateHandle certificate handle + * @param public_key pointer to the output structure that will be filled with + * public key data + * + * @return The status of the operation + */ +Sec_Result SecCertificate_ExtractRSAPublicKey(Sec_CertificateHandle* certificateHandle, + Sec_RSARawPublicKey* public_key); + +/** + * @brief Extract the ECC public key information from the certificate + * + * @param certificateHandle certificate handle + * @param public_key pointer to the output structure that will be filled with + * public key data + * + * @return The status of the operation + */ +Sec_Result SecCertificate_ExtractECCPublicKey(Sec_CertificateHandle* certificateHandle, + Sec_ECCRawPublicKey* public_key); + +/** + * @brief Verify certificate signature + * + * @param certificateHandle certificate handle + * @param key_handle handle of the private key used for signing or it's corresponding + * public key + * + * @return The status of the operation + */ +Sec_Result SecCertificate_Verify(Sec_CertificateHandle* certificateHandle, Sec_KeyHandle* keyHandle); + +/** + * @brief Verify certificate signature + * + * @param certificateHandle certificate handle + * @param public_key structure holding the public key information + * + * @return The status of the operation + */ +Sec_Result SecCertificate_VerifyWithRawRSAPublicKey(Sec_CertificateHandle* certificateHandle, + Sec_RSARawPublicKey* public_key); + +/** + * @brief Verify certificate signature - ECC + * + * @param certificateHandle certificate handle + * @param public_key structure holding the public key information + * + * @return The status of the operation + */ +Sec_Result SecCertificate_VerifyWithRawECCPublicKey(Sec_CertificateHandle* certificateHandle, + Sec_ECCRawPublicKey* public_key); + +/** + * @brief Obtain the certificate data in clear text DER format + * + * @param certificateHandle certificate handle + * @param buffer pointer to the output buffer that will be filled with certificate data + * @param buffer_len the length of the output buffer + * @param written pointer to the output value specifying the number of bytes written to the + * output buffer + * + * @return The status of the operation + */ +Sec_Result SecCertificate_Export(Sec_CertificateHandle* certificateHandle, SEC_BYTE* buffer, SEC_SIZE buffer_len, + SEC_SIZE* written); + +/** + * @brief Release the certificate object + * + * @param certificateHandle certificate handle + * + * @return The status of the operation + */ +Sec_Result SecCertificate_Release(Sec_CertificateHandle* certificateHandle); + +/** + * @brief Obtain a list of all provisioned items. At most maxNumItems will be written to the output buffer. + * + * @param proc Secure processor handle + * @param items buffer that the found item ids will be stored in + * @param maxNumItems maximum number of items that can be written to the output buffer + * + * @return number of items written + */ +SEC_SIZE SecCertificate_List(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID* items, SEC_SIZE maxNumItems); + +/** + * @brief Get the properties for the key handle. + * + * @param keyHandle pointer to Sec_KeyHandle + * @param keyProps pointer to Sec_KeyProperties where information is stored. + */ +Sec_Result SecKey_GetKeyProperties(Sec_KeyHandle* keyHandle, Sec_KeyProperties* keyProps); + +/** + * @brief Get the length of the specified key in bytes + * + * In case of symetric keys, the length returned is the actual size of the key data. + * In case of asymetric keys, the length returned is the size of the modulus in bytes. + * + * @param keyHandle key handle + * + * @return The status of the operation + */ +SEC_SIZE SecKey_GetKeyLen(Sec_KeyHandle* keyHandle); + +/** + * @brief Get the key type of the specified key handle + * + * @param keyHandle key handle + * + * @return The key type or SEC_KEYTYPE_NUM if the key handle is invalid + */ +Sec_KeyType SecKey_GetKeyType(Sec_KeyHandle* keyHandle); + +/** + * @brief Obtain a handle to a provisioned key + * + * @param processorHandle secure processor handle + * @param object_id id of the provisioned key that we are attempting to abtain + * @param keyHandle pointer to the output key handle + * + * @return The status of the operation + */ +Sec_Result SecKey_GetInstance(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, Sec_KeyHandle** keyHandle); + +/** + * @brief Extract an RSA public key from a specified private key handle + * + * @param key_handle handle of the private key + * @param public_key pointer to the output structure containing the public rsa key + * + * @return The status of the operation + */ +Sec_Result SecKey_ExtractRSAPublicKey(Sec_KeyHandle* keyHandle, Sec_RSARawPublicKey* public_key); + +/** + * @brief Extract an ECC public key from a specified private key handle + * + * @param key_handle handle of the private key + * @param public_key pointer to the output structure containing the public ecc key + * + * @return The status of the operation + */ +Sec_Result SecKey_ExtractECCPublicKey(Sec_KeyHandle* keyHandle, Sec_ECCRawPublicKey* public_key); + +/** + * @brief Generate and provision a new key. + * + * @param processorHandle secure processor handle + * @param object_id id of the key to generate + * @param keyType type of the key to generate + * @param location location where the key should be stored + * + * @return The status of the operation + */ +Sec_Result SecKey_Generate(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, Sec_KeyType keyType, + Sec_StorageLoc location); + +/** + * @brief Provision a key + * + * @param processorHandle secure processor handle + * @param object_id id of the key to provision + * @param location storage location where the key should be provisioned + * @param data_type type of input key container that is being used + * @param data pointer to the input key container + * @param data_len the size of the input key container + * + * @return The status of the operation + */ +Sec_Result SecKey_Provision(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, Sec_StorageLoc location, + Sec_KeyContainer data_type, SEC_BYTE* data, SEC_SIZE data_len); + +/** + * @brief Derive and provision a key using the HKDF algorithm + * + * @param processorHandle secure processor handle + * @param object_id_derived id of the key to provision + * @param type_derived derived key type + * @param loc_derived storage location where the derived key should be provisioned + * @param macAlgorithm mac algorithm to use in the key derivation process + * @param salt pointer to the salt value to use in key derivation process + * @param saltSize the length of the salt buffer in bytes + * @param info pointer to the info value to use in key derivation process + * @param infoSize the length of the info buffer in bytes + * + * @return The status of the operation + */ +Sec_Result SecKey_Derive_HKDF(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id_derived, + Sec_KeyType type_derived, Sec_StorageLoc loc_derived, Sec_MacAlgorithm macAlgorithm, SEC_BYTE* nonce, + SEC_BYTE* salt, SEC_SIZE saltSize, SEC_BYTE* info, SEC_SIZE infoSize); + +/** + * @brief Derive and provision a key using the Concat KDF algorithm + * + * @param processorHandle secure processor handle + * @param object_id_derived id of the key to provision + * @param type_derived derived key type + * @param loc_derived storage location where the derived key should be provisioned + * @param digestAlgorithm digest algorithm to use in the key derivation process + * @param otherInfo pointer to the info value to use in key derivation process + * @param otherInfoSize the length of the other info buffer in bytes + * + * @return The status of the operation + */ +Sec_Result SecKey_Derive_ConcatKDF(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id_derived, + Sec_KeyType type_derived, Sec_StorageLoc loc_derived, Sec_DigestAlgorithm digestAlgorithm, SEC_BYTE* nonce, + SEC_BYTE* otherInfo, SEC_SIZE otherInfoSize); + +/** + * @brief Derive and provision an AES 128-bit key a vendor specific key ladder algorithm. + * + * This function will generate a key derived from one of the OTP keys. The + * result of this function may not be usable in Digest and Mac _UpdateWithKey + * functions. In general, this function will keep the derived key more secure + * then the other SecKey_Derive functions because the key will not be accessable + * by the host even during the generation time. + * + * @param processorHandle secure processor handle + * @param object_id_derived id of the key to provision + * @param loc_derived storage location where the derived key should be provisioned + * @param input input buffer for the key derivation + * @param input_len the length of the input buffer + * + * @return The status of the operation + */ +Sec_Result SecKey_Derive_VendorAes128(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id_derived, + Sec_StorageLoc loc_derived, SEC_BYTE* input, SEC_SIZE input_len); + +/** + * @brief Derive and provision an AES 128-bit key. + * + * This function will generate a key derived from one of the OTP keys. The + * result of this function may not be usable in Digest and Mac _UpdateWithKey + * functions. In general, this function will keep the derived key more secure + * then the other SecKey_Derive functions because the key will not be accessable + * by the host even during the generation time. + * + * @param processorHandle secure processor handle + * @param object_id_derived id of the key to provision + * @param type_derived derived key type + * @param loc_derived storage location where the derived key should be provisioned + * @param input1 16 byte input for stage 1 of the key ladder + * @param input2 16 byte input for stage 2 of the key ladder + * @param input3 16 byte input for stage 3 of the key ladder + * @param input4 16 byte input for stage 4 of the key ladder + * + * @return The status of the operation + */ +Sec_Result SecKey_Derive_KeyLadderAes128(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id_derived, + Sec_StorageLoc loc_derived, Sec_KeyLadderRoot root, SEC_BYTE* input1, SEC_BYTE* input2, SEC_BYTE* input3, + SEC_BYTE* input4); + +Sec_Result SecKey_Derive_CMAC_AES128(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID idDerived, + Sec_KeyType typeDerived, Sec_StorageLoc locDerived, SEC_OBJECTID derivationKey, SEC_BYTE* otherData, + SEC_SIZE otherDataSize, SEC_BYTE* counter, SEC_SIZE counterSize); + +/** + * @brief Delete a provisioned key + * + * @param processorHandle secure processor handle + * @param object_id id of the key to delete + * + * @return The status of the operation + */ +Sec_Result SecKey_Delete(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id); + +/** + * @brief Release the key object + * + * @param keyHandle key handle to release + * + * @return The status of the operation + */ +Sec_Result SecKey_Release(Sec_KeyHandle* keyHandle); + +/** + * @brief Obtain a digest value computed over the base key contents + * + * @param processorHandle secure processor handle + * @param nonce client nonce + * @param alg digest algorithm + * @param digest output digest value + * @param digest_len the length of output digest value + * + * @return status of the operation + */ +Sec_Result SecKey_ComputeBaseKeyDigest(Sec_ProcessorHandle* processorHandle, SEC_BYTE* nonce, Sec_DigestAlgorithm alg, + SEC_BYTE* digest, SEC_SIZE* digest_len); + +/** + * @brief Obtain a processor handle + * + * @param keyHandle key handle + * + * @return Processor handle + */ +Sec_ProcessorHandle* SecKey_GetProcessor(Sec_KeyHandle* keyHandle); + +/** + * @brief Generates a shared symmetric key and stores it in a specified location. + * + * A shared secret is calculated using the ECDH algorithm. The shared + * secret is converted to a key using the Concat KDF (SP800-56A Section + * 5.8.1). If the key with the same id already exists, the call will + * overwrite the existing key with the new key. SHA-256 is the digest + * algorithm. + * + * @param keyHandle Handle of my private ECC key + * @param otherPublicKey Public key for other party in key agreement + * @param type_derived Type of key to generate. Only symmetric keys can be derived + * @param id_derived 64-bit object id identifying the key to be generated + * @param loc_id Location where the resulting key will be stored + * @param digestAlgorithm Digest algorithm to use in KDF (typically SEC_DIGESTALGORITHM_SHA256) + * @param otherInfo Input keying material + * AlgorithmID || PartyUInfo || PartyVInfo {|| SuppPubInfo }{|| SuppPrivInfo} + * @param otherInfoSize Size of otherInfo (in bytes) + */ +Sec_Result SecKey_ECDHKeyAgreementWithKDF(Sec_KeyHandle* keyHandle, Sec_ECCRawPublicKey* otherPublicKey, + Sec_KeyType type_derived, SEC_OBJECTID id_derived, Sec_StorageLoc loc_derived, Sec_Kdf kdf, + Sec_DigestAlgorithm digestAlgorithm, const SEC_BYTE* otherInfo, SEC_SIZE otherInfoSize); + +/** + * @brief Obtain a handle to a provisioned bundle + * + * @param processorHandle secure processor handle + * @param object_id id of the provisioned bundle that we are attempting to abtain + * @param bundleHandle pointer to the output key handle + * + * @return The status of the operation + */ +Sec_Result SecBundle_GetInstance(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_BundleHandle** bundleHandle); + +/** + * @brief Provision a bundle + * + * @param processorHandle secure processor handle + * @param object_id id of the bundle to provision + * @param location storage location where the bundle should be provisioned + * @param data pointer to the input key container + * @param data_len the size of the input key container + * + * @return The status of the operation + */ +Sec_Result SecBundle_Provision(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, Sec_StorageLoc location, + SEC_BYTE* data, SEC_SIZE data_len); + +/** + * @brief Delete a provisioned bundle + * + * @param processorHandle secure processor handle + * @param object_id id of the key to delete + * + * @return The status of the operation + */ +Sec_Result SecBundle_Delete(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id); + +/** + * @brief Release the bundle object + * + * @param bundleHandle bundle handle to release + * + * @return The status of the operation + */ +Sec_Result SecBundle_Release(Sec_BundleHandle* bundleHandle); + +/** + * @brief Obtain the bundle data + * + * @param bundleHandle bundle handle + * @param buffer pointer to the output buffer that will be filled with bundle data + * @param buffer_len the length of the output buffer + * @param written pointer to the output value specifying the number of bytes written to the + * output buffer + * + * @return The status of the operation + */ +Sec_Result SecBundle_Export(Sec_BundleHandle* bundleHandle, SEC_BYTE* buffer, SEC_SIZE buffer_len, SEC_SIZE* written); + +/** + * @brief Allocate platform specific memory optimized for encryption/decryption. Used + * With SEC_CIPHERMODE_ENCRYPT_NATIVEMEM and SEC_CIPHERMODE_DECRYPT_NATIVEMEM + */ +SEC_BYTE* Sec_NativeMalloc(Sec_ProcessorHandle* processorHandle, SEC_SIZE length); + +/** + * @brief Free memory allocated with Sec_NativeMalloc + */ +void Sec_NativeFree(Sec_ProcessorHandle* processorHandle, void* ptr); + +Sec_Result SecCipher_ProcessCtrWithDataShift(Sec_CipherHandle* cipherHandle, SEC_BYTE* input, SEC_SIZE inputSize, + SEC_BYTE* output, SEC_SIZE outputSize, SEC_SIZE* bytesWritten, SEC_SIZE dataShift); + +Sec_Result SecKey_ExportKey(Sec_KeyHandle* keyHandle, SEC_BYTE* derivationInput, SEC_BYTE* exportedKey, + SEC_SIZE keyBufferLen, SEC_SIZE* keyBytesWritten); + +Sec_Result SecKey_GetProperties(Sec_KeyHandle* keyHandle, Sec_KeyProperties* keyProperties); + +/** + * @brief Checks secure boot configuration to verify that Secure Boot is enabled. + */ +Sec_Result SecCodeIntegrity_SecureBootEnabled(void); + +Sec_Result SecSVP_SetTime(time_t time); + +/* 2.2 */ +Sec_Result Sec_OpaqueBufferMalloc(SEC_SIZE bufLength, void** handle, void* params) + __attribute__((deprecated)); + +Sec_Result Sec_OpaqueBufferWrite(Sec_OpaqueBufferHandle* opaqueBufferHandle, SEC_SIZE offset, void* data, + SEC_SIZE length) __attribute__((deprecated)); + +Sec_Result Sec_OpaqueBufferFree(Sec_OpaqueBufferHandle* opaqueBufferHandle, void* params) __attribute__((deprecated)); + +Sec_Result SecOpaqueBuffer_Malloc(SEC_SIZE bufLength, Sec_OpaqueBufferHandle** handle); + +Sec_Result SecOpaqueBuffer_Write(Sec_OpaqueBufferHandle* opaqueBufferHandle, SEC_SIZE offset, SEC_BYTE* data, + SEC_SIZE length); + +Sec_Result SecOpaqueBuffer_Free(Sec_OpaqueBufferHandle* opaqueBufferHandle); + +Sec_Result SecOpaqueBuffer_Release(Sec_OpaqueBufferHandle* opaqueBufferHandle, Sec_ProtectedMemHandle** svpHandle); + +Sec_Result SecOpaqueBuffer_Copy(Sec_OpaqueBufferHandle* outOpaqueBufferHandle, SEC_SIZE out_offset, + Sec_OpaqueBufferHandle* inOpaqueBufferHandle, SEC_SIZE in_offset, SEC_SIZE num_to_copy); + +Sec_Result SecOpaqueBuffer_Check(Sec_DigestAlgorithm digestAlgorithm, Sec_OpaqueBufferHandle* opaqueBufferHandle, + SEC_SIZE length, SEC_BYTE* expected, SEC_SIZE expectedLength); + +Sec_Result SecKeyExchange_GetInstance(Sec_ProcessorHandle* processorHandle, Sec_KeyExchangeAlgorithm exchangeType, + void* exchangeParameters, Sec_KeyExchangeHandle** keyExchangeHandle); + +Sec_Result SecKeyExchange_GenerateKeys(Sec_KeyExchangeHandle* keyExchangeHandle, SEC_BYTE* publicKey, + SEC_SIZE pubKeySize); + +Sec_Result SecKeyExchange_ComputeSecret(Sec_KeyExchangeHandle* keyExchangeHandle, SEC_BYTE* otherPublicKey, + SEC_SIZE otherPublicKeySize, Sec_KeyType typeComputed, SEC_OBJECTID idComputed, Sec_StorageLoc locComputed); + +Sec_Result SecKeyExchange_Release(Sec_KeyExchangeHandle* keyExchangeHandle); + +Sec_Result SecKey_Derive_BaseKey(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID idDerived, Sec_KeyType key_type, + Sec_StorageLoc loc, SEC_BYTE* nonce); + +Sec_Result SecKey_Derive_HKDF_BaseKey(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID idDerived, + Sec_KeyType typeDerived, Sec_StorageLoc locDerived, Sec_MacAlgorithm macAlgorithm, SEC_BYTE* salt, + SEC_SIZE saltSize, SEC_BYTE* info, SEC_SIZE infoSize, SEC_OBJECTID baseKeyId); + +Sec_Result SecKey_Derive_ConcatKDF_BaseKey(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID idDerived, + Sec_KeyType typeDerived, Sec_StorageLoc locDerived, Sec_DigestAlgorithm digestAlgorithm, SEC_BYTE* otherInfo, + SEC_SIZE otherInfoSize, SEC_OBJECTID baseKeyId); + +typedef struct { + SEC_SIZE clear; + SEC_SIZE encrypted; +} SEC_MAP; + +Sec_Result SecCipher_ProcessOpaqueWithMap(Sec_CipherHandle* cipherHandle, SEC_BYTE* iv, SEC_BYTE* input, + SEC_SIZE inputSize, SEC_BOOL lastInput, SEC_MAP* map, SEC_SIZE mapLength, Sec_OpaqueBufferHandle** outputHandle, + SEC_SIZE* bytesWritten); + +#ifdef __cplusplus +} +#endif + +#endif /* SEC_SECURITY_H */ diff --git a/include/sec_security_asn1kc.h b/include/sec_security_asn1kc.h new file mode 100644 index 0000000..9419157 --- /dev/null +++ b/include/sec_security_asn1kc.h @@ -0,0 +1,113 @@ +/** + * Copyright 2020-2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_COMMON_17 + +#ifndef SEC_SECURITY_ASN1KC_H +#define SEC_SECURITY_ASN1KC_H + +#include "sec_security_datatype.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Opaque certificate handle + * + */ +#define ASN1KCATTRIBUTE_T_CHOICE_INTEGER 0 +#define ASN1KCATTRIBUTE_T_CHOICE_BITSTRING 1 +#define ASN1KCATTRIBUTE_T_CHOICE_OCTETSTRING 2 +#define ASN1KCATTRIBUTE_T_CHOICE_NULL 3 +#define ASN1KCATTRIBUTE_T_CHOICE_IA5STRING 4 +#define ASN1KCATTRIBUTE_T_CHOICE_UTCTIME 5 + +typedef struct { + int type; + union { + ASN1_INTEGER* integer; + ASN1_BIT_STRING* bitstring; + ASN1_OCTET_STRING* octetstring; + ASN1_NULL* null; + ASN1_IA5STRING* ia5string; + ASN1_UTCTIME* utctime; + } c; +} Asn1KCAttribute_t_c; + +typedef struct { + ASN1_IA5STRING* name; + Asn1KCAttribute_t_c* value; +} Asn1KCAttribute_t; + +typedef STACK_OF(Asn1KCAttribute_t) Sec_Asn1KC; +#if OPENSSL_VERSION_NUMBER < 0x10100000L +DECLARE_STACK_OF(Asn1KCAttribute_t); +#define sk_Asn1KCAttribute_t_push(st, val) SKM_sk_push(Asn1KCAttribute_t, st, val) +#define sk_Asn1KCAttribute_t_value(st, i) SKM_sk_value(Asn1KCAttribute_t, st, i) +#define sk_Asn1KCAttribute_t_num(st) SKM_sk_num(Asn1KCAttribute_t, st) +#else +DEFINE_STACK_OF(Asn1KCAttribute_t); // NOLINT +#endif + +Sec_Asn1KC* SecAsn1KC_Alloc(); + +void SecAsn1KC_Free(Sec_Asn1KC* kc); + +Sec_Result SecAsn1KC_Encode(Sec_Asn1KC* kc, SEC_BYTE* buf, SEC_SIZE buf_len, SEC_SIZE* written); + +Sec_Asn1KC* SecAsn1KC_Decode(const SEC_BYTE* buf, SEC_SIZE buf_len); + +SEC_BOOL SecAsn1KC_HasAttr(Sec_Asn1KC* kc, const char* key); + +Sec_Result SecAsn1KC_AddAttrUlong(Sec_Asn1KC* kc, const char* key, uint64_t val); + +Sec_Result SecAsn1KC_AddAttrUint64(Sec_Asn1KC* kc, const char* key, uint64_t val); + +Sec_Result SecAsn1KC_AddAttrLong(Sec_Asn1KC* kc, const char* key, int64_t val); + +Sec_Result SecAsn1KC_AddAttrInt64(Sec_Asn1KC* kc, const char* key, int64_t val); + +Sec_Result SecAsn1KC_AddAttrString(Sec_Asn1KC* kc, const char* key, const char* val); + +Sec_Result SecAsn1KC_AddAttrBuffer(Sec_Asn1KC* kc, const char* key, void* buf, SEC_SIZE buf_len); + +Sec_Result SecAsn1KC_GetAttrUlong(Sec_Asn1KC* kc, const char* key, uint64_t* val); + +Sec_Result SecAsn1KC_GetAttrUint64(Sec_Asn1KC* kc, const char* key, uint64_t* val); + +Sec_Result SecAsn1KC_GetAttrLong(Sec_Asn1KC* kc, const char* key, int64_t* val); + +Sec_Result SecAsn1KC_GetAttrInt64(Sec_Asn1KC* kc, const char* key, int64_t* val); + +Sec_Result SecAsn1KC_GetAttrBuffer(Sec_Asn1KC* kc, const char* key, SEC_BYTE* buffer, SEC_SIZE buffer_len, + SEC_SIZE* written); + +Sec_Result SecAsn1KC_GetAttrString(Sec_Asn1KC* kc, const char* key, char* buffer, SEC_SIZE buffer_len, + SEC_SIZE* written); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/include/sec_security_comcastids.h b/include/sec_security_comcastids.h new file mode 100644 index 0000000..0ca5113 --- /dev/null +++ b/include/sec_security_comcastids.h @@ -0,0 +1,123 @@ +/** + * Copyright 2020-2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_SECURITY_COMCASTIDS_H +#define SEC_SECURITY_COMCASTIDS_H + +#include "sec_security_datatype.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* adobe reserved space */ +#define SEC_OBJECTID_ADOBE_TOP 0xffffffffffff0400ULL +#define SEC_OBJECTID_ADOBE_BASE 0xffffffffffff0000ULL + +/* comcast object ids */ +#define SEC_OBJECTID_COMCAST_XCALSESSIONMACKEY 0xffffffff00000001ULL +#define SEC_OBJECTID_COMCAST_XCALSESSIONMACKEYTOKEN 0xffffffff00000002ULL +#define SEC_OBJECTID_COMCAST_SOCIDPHMACKEY 0xffffffff00000003ULL +#define SEC_OBJECTID_COMCAST_XCALSESSIONCONTEXTTOKEN 0xffffffff00000004ULL +#define SEC_OBJECTID_COMCAST_CIMAACCTTOKEN 0xffffffff00000005ULL +#define SEC_OBJECTID_COMCAST_XCALSESSIONMACKEY_HASH 0xffffffff00000006ULL +#define SEC_OBJECTID_COMCAST_XCALSESSIONMACKEYTOKEN_HASH 0xffffffff00000007ULL +#define SEC_OBJECTID_COMCAST_FKPSRT_MANIFEST 0xffffffff00000008ULL +#define SEC_OBJECTID_COMCAST_XCALSESSIONENCKEY 0xffffffff00000009ULL +#define SEC_OBJECTID_COMCAST_XCALSESSIONENCKEY_HASH 0xffffffff0000000aULL + +#define SEC_OBJECTID_COMCAST_SGNCERT 0x0111000001110001ULL +#define SEC_OBJECTID_COMCAST_SGNSUBCACERT 0x0111000001110002ULL +#define SEC_OBJECTID_COMCAST_SGNROOTCACERT 0x0111000001110003ULL +#define SEC_OBJECTID_COMCAST_ENCCERT 0x0111000001110004ULL +#define SEC_OBJECTID_COMCAST_ENCSUBCACERT 0x0111000001110005ULL +#define SEC_OBJECTID_COMCAST_ENCROOTCACERT 0x0111000001110006ULL +#define SEC_OBJECTID_COMCAST_TLSCERT 0x0111000001110007ULL +#define SEC_OBJECTID_COMCAST_TLSSUBCACERT 0x0111000001110008ULL +#define SEC_OBJECTID_COMCAST_TLSROOTCACERT 0x0111000001110009ULL +#define SEC_OBJECTID_COMCAST_KEKCERT 0x011100000111000AULL +#define SEC_OBJECTID_COMCAST_CERTCA01CERT 0x0211000002110001ULL +#define SEC_OBJECTID_COMCAST_STATUSCA01CERT 0x0211000002110002ULL +#define SEC_OBJECTID_COMCAST_SGNKEY 0x0311000003110001ULL +#define SEC_OBJECTID_COMCAST_ENCKEY 0x0311000003110002ULL +#define SEC_OBJECTID_COMCAST_TLSKEY 0x0311000003110003ULL +#define SEC_OBJECTID_COMCAST_TRANSFORMKEY 0x0311000003110004ULL +#define SEC_OBJECTID_COMCAST_KEKKEY 0x0311000003110005ULL +#define SEC_OBJECTID_COMCAST_PKIBUNDLE 0x0511000005110001ULL +#define SEC_OBJECTID_COMCAST_BRCM_OTP 0x0611000006110003ULL +#define SEC_OBJECTID_COMCAST_HASHLOCKED 0x0711000007110001ULL +#define SEC_OBJECTID_COMCAST_SECUREDRM 0x0711000007110004ULL + +/* adobe object ids */ +#define SEC_OBJECTID_ADOBE_DRMMODELSGNKEY 0x0321000003254030ULL /* temp id - will change */ +#define SEC_OBJECTID_ADOBE_DRMMODELSGNCERT 0x0121000001210012ULL /* temp id - will change */ +#define SEC_OBJECTID_ADOBE_DRMMODELENCKEY 0x0321000003264030ULL /* temp id - will change */ +#define SEC_OBJECTID_ADOBE_DRMMODELENCCERT 0x0121000001210013ULL /* temp id - will change */ +#define SEC_OBJECTID_ADOBE_DRMMODELKEY 0x0321000003214030ULL +#define SEC_OBJECTID_ADOBE_DRMMODELCERT 0x0121000001210001ULL +#define SEC_OBJECTID_ADOBE_DRMMODELINTERMEDIATERUNTIMEDRMCACERT 0x0121000001210002ULL +#define SEC_OBJECTID_ADOBE_DRMMODELINTERMEDIATECACERT 0x0121000001210003ULL +#define SEC_OBJECTID_ADOBE_DRMMODELROOTCACERT 0x0121000001210004ULL +#define SEC_OBJECTID_ADOBE_SD01CERT 0x0121000001210005ULL +#define SEC_OBJECTID_ADOBE_SD01INTERMEDIATERUNTIMEDRMCACERT 0x0121000001210006ULL +#define SEC_OBJECTID_ADOBE_SD01INTERMEDIATECACERT 0x0121000001210007ULL +#define SEC_OBJECTID_ADOBE_SD01ROOTCACERT 0x0121000001210008ULL +#define SEC_OBJECTID_ADOBE_SD02CERT 0x0121000001210009ULL +#define SEC_OBJECTID_ADOBE_SD02INTERMEDIATERUNTIMEDRMCACERT 0x012100000121000AULL +#define SEC_OBJECTID_ADOBE_SD02INTERMEDIATECACERT 0x012100000121000BULL +#define SEC_OBJECTID_ADOBE_SD02ROOTCACERT 0x012100000121000CULL +#define SEC_OBJECTID_ADOBE_SD03CERT 0x012100000121000DULL +#define SEC_OBJECTID_ADOBE_SD03INTERMEDIATERUNTIMEDRMCACERT 0x012100000121000EULL +#define SEC_OBJECTID_ADOBE_SD03INTERMEDIATECACERT 0x012100000121000FULL +#define SEC_OBJECTID_ADOBE_SD03ROOTCACERT 0x0121000001210010ULL +#define SEC_OBJECTID_ADOBE_INDIVTRANSPORTCERT 0x0121000001210011ULL +#define SEC_OBJECTID_ADOBE_SD01KEY 0x0321000003224030ULL +#define SEC_OBJECTID_ADOBE_SD02KEY 0x0321000003234030ULL +#define SEC_OBJECTID_ADOBE_SD03KEY 0x0321000003244030ULL +#define SEC_OBJECTID_ADOBE_TRANSFORMKEY 0x0321000003254030ULL +#define SEC_OBJECTID_ADOBE_PRODADOBEROOTDIGEST 0x0421000004210001ULL +#define SEC_OBJECTID_ADOBE_DRMPKI 0x0621000006210001ULL +#define SEC_OBJECTID_ADOBE_DRMPKI_ENHANCED 0x0621000006210002ULL + +#define SEC_OBJECTID_PLAYREADY_MODELCERT 0x0631000006310001ULL +#define SEC_OBJECTID_PLAYREADY_MODELKEY 0x0331000003310001ULL + +#define SEC_OBJECTID_WV_KEY 0x0351000003510001ULL +#define SEC_OBJECTID_WV_CERTBUNDLE 0x0651000006510001ULL + +#define SEC_OBJECTID_COMCAST_BRCM_DRMBIN 0x0611000006110004ULL + +//service manager keys +#define SEC_OBJECTID_CW_NETFLIX_STORAGE_KEY 0x0361000003610001ULL +#define SEC_OBJECTID_CW_YOUTUBE_STORAGE_KEY 0x0361000003610002ULL +#define SEC_OBJECTID_CW_HLU_STORAGE_KEY 0x0361000003610003ULL + +#define SEC_FKPSTYPE_CERTIFICATE 0x01 +#define SEC_FKPSTYPE_CACERTIFICATE 0x02 +#define SEC_FKPSTYPE_KEYCONTAINER 0x03 +#define SEC_FKPSTYPE_MESSAGEDIGEST 0x04 +#define SEC_FKPSTYPE_PKIBUNDLE 0x05 +#define SEC_FKPSTYPE_DRMPKIBUNDLE 0x06 +#define SEC_FKPSTYPE_HASHLOCKED 0x07 +#define SEC_FKPSTYPE_RESERVED 0xFF + +#ifdef __cplusplus +} +#endif + +#endif /* SEC_SECURITY_COMCASTIDS_H */ diff --git a/include/sec_security_common.h b/include/sec_security_common.h new file mode 100644 index 0000000..4bd6bf9 --- /dev/null +++ b/include/sec_security_common.h @@ -0,0 +1,767 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file sec_security_common.h + * + * @brief Common functions used by all platform implementations + * + */ + +#ifndef SEC_SECURITY_COMMON_H +#define SEC_SECURITY_COMMON_H + +#include "sec_security_datatype.h" + +#ifndef SEC_COMMON_17 + +#include "sec_security_asn1kc.h" + +#endif + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Buffer information structure + */ +typedef struct { + SEC_BYTE* base; + SEC_SIZE size; + SEC_SIZE written; +} Sec_Buffer; + +/** + * @brief initialize the Sec_Buffer structure + * + * @param buffer Sec_Buffer structure to initialize + * @param mem memory buffer to use + * @param len size of the memory buffer + */ +void SecBuffer_Init(Sec_Buffer* buffer, void* mem, SEC_SIZE len); + +/** + * @brief reset the buffer + * + * @param buffer Sec_Buffer structure to initialize + */ +void SecBuffer_Reset(Sec_Buffer* buffer); + +/** + * @brief Write data to a buffer + * + * @param buffer pointer to a Sec_Buffer structure to use + * @param data input data to write + * @param len length of input data + * + * @return Status of the operation. Error status will be returned if there + * is not enough space left in the output buffer. + */ +Sec_Result SecBuffer_Write(Sec_Buffer* buffer, void* data, SEC_SIZE len); + +typedef enum { + SEC_ENDIANESS_BIG, + SEC_ENDIANESS_LITTLE, + SEC_ENDIANESS_UNKNOWN +} Sec_Endianess; + +/** + * @brief Obtain chip endianess at runtime + */ +Sec_Endianess Sec_GetEndianess(void); + +/** + * @brief Convert big endian bytes to native uint32 + */ +uint32_t Sec_BEBytesToUint32(SEC_BYTE* bytes); + +/** + * @brief Convert big endian bytes to native uint64 + */ +uint64_t Sec_BEBytesToUint64(SEC_BYTE* bytes); + +/** + * @brief Convert native uint32 to big endian bytes + */ +void Sec_Uint32ToBEBytes(uint32_t val, SEC_BYTE* bytes); + +/** + * @brief Convert native uint64 to big endian bytes + */ +void Sec_Uint64ToBEBytes(uint64_t val, SEC_BYTE* bytes); + +/** + * @brief Endian swap + */ +uint16_t Sec_EndianSwap_uint16(uint16_t val); + +/** + * @brief Endian swap + */ +int16_t Sec_EndianSwap_int16(int16_t val); + +/** + * @brief Endian swap + */ +uint32_t Sec_EndianSwap_uint32(uint32_t val); + +/** + * @brief Endian swap + */ +int32_t Sec_EndianSwap_int32(int32_t val); + +/** + * @brief Endian swap + */ +int64_t Sec_EndianSwap_int64(int64_t val); + +/** + * @brief Endian swap + */ +uint64_t Sec_EndianSwap_uint64(uint64_t val); + +/** + * @brief memcmp replacement with constant time runtime + */ +int Sec_Memcmp(const void* ptr1, const void* ptr2, size_t num); + +/** + * @brief memset replacement that cannot be optimized out + */ +void* Sec_Memset(void* ptr, int value, size_t num); + +/** + * @brief Check whether the mode is any form of encrypt + * + * @param mode cipher mode + * + * @return 1 if encrypt, else 0 + */ +int SecCipher_IsModeEncrypt(Sec_CipherMode mode); + +/** + * @brief Check whether the mode is any form of decrypt + * + * @param mode cipher mode + * + * @return 1 if decrypt, else 0 + */ +int SecCipher_IsModeDecrypt(Sec_CipherMode mode); + +/** + * @brief Check whether the supplied key and iv are valid for the chosen cipher algorithm + * + * @param key_type key type + * @param algorithm cipher algorithm + * @param mode cipher mode + * @param iv initialization vector + * + * @return status of the call + */ +Sec_Result SecCipher_IsValidKey(Sec_KeyType key_type, Sec_CipherAlgorithm algorithm, Sec_CipherMode mode, + const SEC_BYTE* iv); + +SEC_BOOL SecCipher_IsCBC(Sec_CipherAlgorithm alg); + +/** + * @brief get the required output buffer size for the specified combination of input parameters + * + * Write required output buffer size for cipher configuration. + * Returns SEC_RESULT_SUCCESS if the cipher configuration parameters are valid. + * Returns SEC_RESULT_FAILURE otherwise (e.g. input size is not valid). + * + * @param algorithm cipher algorithm + * @param mode cipher mode + * @param keyType key type + * @param inputSize size of the input buffer + * @param outputSize size of the output buffer + * @param lastInput is this the last input to the cipher + * + * @return status of the call + */ +Sec_Result SecCipher_GetRequiredOutputSize(Sec_CipherAlgorithm algorithm, Sec_CipherMode mode, Sec_KeyType keyType, + SEC_SIZE inputSize, SEC_SIZE* outputSize, SEC_BOOL lastInput); + +/** + * @brief get the required output buffer length for fragemnted encryption/decryption + * + * @param algorithm cipher algorithm + * @param mode cipher mode + * @param keyType key type + * @param inputSize size of the input buffer + * @param outputSize size of the output buffer + * @param lastInput is this the last input to the cipher + * @param framentOffset offset in bytes of the fragment data within larger packet + * @param fragmentSize length in bytes of the data fragment + * @param fragmentPeriod the length in bytes of the packet containing the fragment + * + * @return status of the call + */ +Sec_Result SecCipher_GetRequiredOutputSizeFragmented(Sec_CipherAlgorithm algorithm, Sec_CipherMode mode, + Sec_KeyType keyType, SEC_SIZE inputSize, SEC_SIZE* outputSize, SEC_BOOL lastInput, SEC_SIZE fragmentOffset, + SEC_SIZE fragmentSize, SEC_SIZE fragmentPeriod); + +/** + * @brief Apply PKCS7 padding to the AES input block + * + * @param inputBlock input data to pad + * @param inputSize size of input data + * @param outputBlock Output block. Has to be the size of SEC_AES_BLOCKSIZE + */ +void SecCipher_PadAESPKCS7Block(SEC_BYTE* inputBlock, SEC_SIZE inputSize, SEC_BYTE* outputBlock); + +/** + * @brief Checks whether the specified cipher algorithm is AES + */ +SEC_BOOL SecCipher_IsAES(Sec_CipherAlgorithm alg); + +/** + * @brief Checks whether the specified cipher algorithm is RSA + */ +SEC_BOOL SecCipher_IsRsa(Sec_CipherAlgorithm alg); + +/** + * @brief Checks whether the specified cipher algorithm is ECC + */ +SEC_BOOL SecCipher_IsEcc(Sec_CipherAlgorithm alg); + +Sec_Result SecCipher_SingleInput(Sec_ProcessorHandle* processorHandle, Sec_CipherAlgorithm alg, Sec_CipherMode mode, + Sec_KeyHandle* keyHandle, SEC_BYTE* iv, SEC_BYTE* input, SEC_SIZE input_len, SEC_BYTE* output, + SEC_SIZE output_len, SEC_SIZE* written); + +Sec_Result SecCipher_SingleInputId(Sec_ProcessorHandle* processorHandle, Sec_CipherAlgorithm alg, Sec_CipherMode mode, + SEC_OBJECTID key, SEC_BYTE* iv, SEC_BYTE* input, SEC_SIZE input_len, SEC_BYTE* output, SEC_SIZE output_len, + SEC_SIZE* written); + +SEC_BOOL SecCipher_IsPKCS7Padded(Sec_CipherAlgorithm algorithm); + +SEC_BOOL SecCipher_IsDecrypt(Sec_CipherMode mode); + +/** + * @brief Checks whether the passed in key is valid for a chosen signing algorithm and mode + * + * @param key_type key type + * @param algorithm signing algorithm + * @param mode signing mode + * + * @return status of the operation + */ +Sec_Result SecSignature_IsValidKey(Sec_KeyType key_type, Sec_SignatureAlgorithm algorithm, Sec_SignatureMode mode); + +/** + * @brief Returns TRUE if the signature algorithm is an RSA variant + * + * @param alg signing algorithm + * + * @return true if RSA + */ +SEC_BOOL SecSignature_IsRsa(Sec_SignatureAlgorithm alg); + +Sec_KeyType SecKey_GetRSAKeyTypeForBitLength(int numBits); + +Sec_KeyType SecKey_GetRSAKeyTypeForByteLength(int numBytes); + +/** + * @brief Returns TRUE if the signature algorithm is an ECC variant + * + * @param alg signing algorithm + * + * @return true if ECC + */ +SEC_BOOL SecSignature_IsEcc(Sec_SignatureAlgorithm alg); + +/** + * @brief Returns the size of the algorithm's ECC signature + * + * @param alg signing algorithm + * + * @return size in bytes or 0 if unsupported algorithm + */ +SEC_SIZE SecSignature_GetEccSignatureSize(Sec_SignatureAlgorithm alg); + +/** + * @brief Obtain a digest algorithm used by a specific signing algorithm + * + * @param alg signing algorithm + * + * @return digest algorithm used + */ +Sec_DigestAlgorithm SecSignature_GetDigestAlgorithm(Sec_SignatureAlgorithm alg); + +/** + * @brief Signature util that handles Sec_SignatureHandle generation and release + * + * @param processorHandle processor handle + * @param algorithm signing algorithm + * @param mode signing mode + * @param keyHandle key used for signing operations + * @param input pointer to the input buffer whose signature we are generating/verifying + * @param inputSize the length of the input + * @param signature buffer where signature is/will be stored + * @param signatureSize output variable that will be set to the signature size + * + * @return The status of the operation + */ +Sec_Result SecSignature_SingleInput(Sec_ProcessorHandle* processorHandle, Sec_SignatureAlgorithm algorithm, + Sec_SignatureMode mode, Sec_KeyHandle* keyHandle, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BYTE* signature, + SEC_SIZE* signatureSize); + +Sec_Result SecSignature_SingleInputCert(Sec_ProcessorHandle* processorHandle, Sec_SignatureAlgorithm algorithm, + Sec_SignatureMode mode, Sec_CertificateHandle* certificateHandle, SEC_BYTE* input, SEC_SIZE inputSize, + SEC_BYTE* signature, SEC_SIZE* signatureSize); + +Sec_Result SecSignature_SingleInputId(Sec_ProcessorHandle* processorHandle, Sec_SignatureAlgorithm algorithm, + Sec_SignatureMode mode, SEC_OBJECTID id, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BYTE* signature, + SEC_SIZE* signatureSize); + +Sec_Result SecSignature_SingleInputCertId(Sec_ProcessorHandle* processorHandle, Sec_SignatureAlgorithm algorithm, + Sec_SignatureMode mode, SEC_OBJECTID cert_id, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BYTE* signature, + SEC_SIZE* signatureSize); + +/** + * @brief Check whether the passed in key type is valid for a chosen MAC algorithm + * + * @param key_type key type + * @param algorithm MAC algorithm + * + * @return status of the operation + */ +Sec_Result SecMac_IsValidKey(Sec_KeyType key_type, Sec_MacAlgorithm algorithm); + +/** + * @brief Obtain a digest algorithm used by a specified MAC algorithm + * + * @param alg MAC algorithm + * + * @return digest algorithm used + */ +Sec_DigestAlgorithm SecMac_GetDigestAlgorithm(Sec_MacAlgorithm alg); + +Sec_Result SecMac_SingleInput(Sec_ProcessorHandle* processorHandle, Sec_MacAlgorithm alg, Sec_KeyHandle* keyHandle, + SEC_BYTE* input, SEC_SIZE input_len, SEC_BYTE* mac, SEC_SIZE* mac_len); + +Sec_Result SecMac_SingleInputId(Sec_ProcessorHandle* processorHandle, Sec_MacAlgorithm alg, SEC_OBJECTID key, + SEC_BYTE* input, SEC_SIZE input_len, SEC_BYTE* mac, SEC_SIZE* mac_len); + +/** + * @brief Checks if a passed in key type is symetric. + * + * @param type key type + * + * @return 1 if key type is symetric, 0 if asymetric + */ +SEC_BOOL SecKey_IsSymetric(Sec_KeyType type); + +/** + * @brief Checks if a passed in key type is an AES key. + * + * @param type key type + * + * @return 1 if key type is AES, 0 if not + */ +SEC_BOOL SecKey_IsAES(Sec_KeyType type); + +SEC_BOOL SecKey_IsHMAC(Sec_KeyType type); + +/** + * @brief Checks if a passed in key type is RSA + * + * @param type key type + * + * @return 1 if key type is rsa, 0 otherwise + */ +SEC_BOOL SecKey_IsRsa(Sec_KeyType type); + +/** + * @brief Checks if a passed in key type is pub RSA + * + * @param type key type + * + * @return 1 if key type is pub rsa, 0 otherwise + */ +SEC_BOOL SecKey_IsPubRsa(Sec_KeyType type); + +/** + * @brief Checks if a passed in key type is priv RSA + * + * @param type key type + * + * @return 1 if key type is priv rsa, 0 otherwise + */ +SEC_BOOL SecKey_IsPrivRsa(Sec_KeyType type); + +/** + * @brief Checks if a passed in key type is ECC + * + * @param type key type + * + * @return 1 if key type is priv ECC, 0 otherwise + */ +SEC_BOOL SecKey_IsEcc(Sec_KeyType type); + +/** + * @brief Checks if a passed in key type is priv ECC + * + * @param type key type + * + * @return 1 if key type is priv ECC, 0 otherwise + */ +SEC_BOOL SecKey_IsPrivEcc(Sec_KeyType type); + +/** + * @brief Checks if a passed in key type is pub ECC + * + * @param type key type + * + * @return 1 if key type is pub ECC, 0 otherwise + */ +SEC_BOOL SecKey_IsPubEcc(Sec_KeyType type); + +/** + * @brief Obtain a key length in bytes for a specified key type. + * + * For symetric keys, the return value will be the actual key size. For asymetric keys + * the return value will be the modulus size. + * + * @param keyType key type + * + * @return key size + */ +SEC_SIZE SecKey_GetKeyLenForKeyType(Sec_KeyType keyType); + +/** + * @brief Is the specified container a raw (clear) container + */ +SEC_BOOL SecKey_IsClearKeyContainer(Sec_KeyContainer kct); + +/** + * @brief Obtain a key container type for a specified key type + * + * @param key_type key type + * @return key container type + */ +Sec_KeyContainer SecKey_GetClearContainer(Sec_KeyType key_type); + +Sec_KeyType SecKey_GetKeyTypeForClearKeyContainer(Sec_KeyContainer kc); + +/** + * @brief Find if the key with a specific id has been provisioned + * + * @param processorHandle secure processor handle + * @param object_id id of the certificate + * + * @return 1 if an object has been provisioned, 0 if it has not been + */ +SEC_BOOL SecKey_IsProvisioned(Sec_ProcessorHandle* processorHandle, + SEC_OBJECTID object_id); + +/** + * @brief finds the first available key id in the range passed in + * + * @param proc secure processor + * @param base bottom of the range to search + * @param top top of the range to search + * @return + */ +SEC_OBJECTID SecKey_ObtainFreeObjectId(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID base, SEC_OBJECTID top); + +/** + * @brief Get the type (msd byte) of the object id + */ +uint8_t SecKey_GetObjectType(SEC_OBJECTID object_id); + +/** + * @brief Obtain a digest value computed over a specified key + * + * @param proc secure processor handle + * @param key_id key id + * @param alg digest algorithm to use + * @param digest output digest value + * @param digest_len size of the written digest value + * @return + */ +Sec_Result SecKey_ComputeKeyDigest(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID key_id, Sec_DigestAlgorithm alg, + SEC_BYTE* digest, SEC_SIZE* digest_len); + +/** + * @brief Obtain the size of the digest for a specified digest algorithm + * + * @param alg digest algorithm + * + * @return digest size in bytes + */ +SEC_SIZE SecDigest_GetDigestLenForAlgorithm(Sec_DigestAlgorithm alg); + +/** + * @brief compute inputs for the base key ladder + */ +Sec_Result SecKey_ComputeBaseKeyLadderInputs(Sec_ProcessorHandle* processorHandle, const char* inputDerivationStr, + const char* cipherAlgorithmStr, SEC_BYTE* nonce, Sec_DigestAlgorithm digestAlgorithm, SEC_SIZE inputSize, + SEC_BYTE* c1, SEC_BYTE* c2, SEC_BYTE* c3, SEC_BYTE* c4); + +/** + * @brief Check if provided algorithm takes digest as an input + */ +SEC_BOOL SecSignature_IsDigest(Sec_SignatureAlgorithm alg); + +SEC_BOOL SecSignature_IsRsaPss(Sec_SignatureAlgorithm alg); + +/** + * log callback function + */ +typedef void (*SecApiLogCallback)(const char* fmt, ...); + +/** + * @brief set log callback function + * + * @param cb pointer to the logger function + */ +void Sec_SetLogger(SecApiLogCallback cb); + +/** + * @brief get the log callback function + * + * @return pointer to the logger function + */ +SecApiLogCallback Sec_GetLogger(void); + +/** + * @brief default logger implementation (stdout) + */ +void Sec_DefaultLoggerCb(const char* fmt, ...); + +/** + * @brief NOP logger implementation + */ +void Sec_NOPLoggerCb(const char* fmt, ...); + +/** + * @brief Print a hexadecimal value + */ +void Sec_PrintHex(void* data, SEC_SIZE numBytes); + +/** + * Print OpenSSL version information + */ +void Sec_PrintOpenSSLVersion(); + +/** + * Initialize all OpenSSL algorithms used by the Security API. Register securityapi engine. + */ +void Sec_InitOpenSSL(void); + +/** + * @brief Obtain an OpenSSL RSA key from the Security API key handle. This RSA + * key will support performing RSA encrypt/decrypt/sign/verify operations in hardware + * when used by OpenSSL functions such as PKCS7_sign, PKCS7_verify, etc. + */ +RSA* SecKey_ToEngineRSA(Sec_KeyHandle* keyHandle); + +RSA* SecKey_ToEngineRSAWithCert(Sec_KeyHandle* keyHandle, Sec_CertificateHandle* certificateHandle); + +/** + * @brief Obtain an OpenSSL EC key from the Security API key handle. This EC + * key will support performing EC encrypt/decrypt/sign/verify operations in hardware + * when used by OpenSSL functions. + */ +EC_KEY* SecKey_ToEngineEcc(Sec_KeyHandle* keyHandle); + +/** + * @brief Load an OpenSSL X509 object from a DER format + */ +X509* SecCertificate_DerToX509(void* mem, SEC_SIZE len); + +/** + * @brief Obtain an OpenSSL X509 certificate from the Security API cert handle. + */ +X509* SecCertificate_ToX509(Sec_CertificateHandle* certificateHandle); + +/** + * @brief Find if the certificate with a specific id has been provisioned + * + * @param processorHandle secure processor handle + * @param object_id id of the certificate + * + * @return 1 if an object has been provisioned, 0 if it has not been + */ +SEC_BOOL SecCertificate_IsProvisioned(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id); + +/** + * @brief Obtain the size of the certificate in DER format + * + * @param certHandle certificate whose size we want to obtain + */ +SEC_SIZE SecCertificate_GetSize(Sec_CertificateHandle* certificateHandle); + +/** + * @brief Returns the key type of the public key contained in a certificate. + * + * @param certHandle Handle of a certificate whose type will be returned + */ +Sec_KeyType SecCertificate_GetKeyType(Sec_CertificateHandle* certificateHandle); + +/** + * @brief finds the first available certificate id in the range passed in + * + * @param proc secure processor + * @param base bottom of the range to search + * @param top top of the range to search + * @return + */ +SEC_OBJECTID SecCertificate_ObtainFreeObjectId(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID base, + SEC_OBJECTID top); + +/** + * @brief Utility function for calculating a digest value of a single input buffer + * + * @param proc secure processor handle + * @param alg digest algorithm to use + * @param input input data to calculate digest over + * @param input_len size of input data in bytes + * @param digest output buffer where the calculated digest value will be written + * @param digest_len number of bytes written to the output digest buffer + * + * @return status of the operation + */ +Sec_Result +SecDigest_SingleInput(Sec_ProcessorHandle* processorHandle, Sec_DigestAlgorithm alg, SEC_BYTE* input, + SEC_SIZE input_len, SEC_BYTE* digest, SEC_SIZE* digest_len); + +/** + * @brief Utility function for calculating a digest value of a single input buffer + * + * @param proc secure processor handle + * @param alg digest algorithm to use + * @param key_id id of the key over which the digest is being calculated + * @param digest output buffer where the calculated digest value will be written + * @param digest_len number of bytes written to the output digest buffer + * + * @return status of the operation + */ +Sec_Result SecDigest_SingleInputWithKeyId(Sec_ProcessorHandle* processorHandle, Sec_DigestAlgorithm alg, + SEC_OBJECTID key_id, SEC_BYTE* digest, SEC_SIZE* digest_len); + +/** + * @brief Utility function for filling out a random value + * + * @param proc secure processor handle + * @param alg random algorithm to use + * @param output output buffer where the random value will be written + * @param output_len number of bytes written to the output buffer + * + * @return status of the operation + */ +Sec_Result SecRandom_SingleInput(Sec_ProcessorHandle* processorHandle, Sec_RandomAlgorithm alg, SEC_BYTE* output, + SEC_SIZE output_len); + +/** + * @brief Find if the bundle with a specific id has been provisioned + * + * @param processorHandle secure processor handle + * @param object_id id of the certificate + * + * @return 1 if an object has been provisioned, 0 if it has not been + */ +SEC_BOOL SecBundle_IsProvisioned(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id); + +/** + * @brief finds the first available bundle id in the range passed in + * + * @param proc secure processor + * @param base bottom of the range to search + * @param top top of the range to search + * @return + */ +SEC_OBJECTID SecBundle_ObtainFreeObjectId(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID base, SEC_OBJECTID top); + +#ifndef SEC_COMMON_17 + +/** + * @brief Generate an Asn1 key container for wrapped keys + */ +Sec_Result SecKey_GenerateWrappedKeyAsn1(SEC_BYTE* wrappedKey, SEC_SIZE wrappedKeyLen, Sec_KeyType wrappedKeyType, + SEC_OBJECTID wrappingKeyId, SEC_BYTE* wrappingIv, Sec_CipherAlgorithm wrappingAlgorithm, SEC_BYTE* output, + SEC_SIZE output_len, SEC_SIZE* written); + +/** + * @brief Extract wrapped key params from ASN1KC + */ +Sec_Result SecKey_ExtractWrappedKeyParamsAsn1(Sec_Asn1KC* kc, SEC_BYTE* wrappedKey, SEC_SIZE wrappedKeyLen, + SEC_SIZE* written, Sec_KeyType* wrappedKeyType, SEC_OBJECTID* wrappingId, SEC_BYTE* wrappingIv, + Sec_CipherAlgorithm* wrappingAlg); + +/** + * @brief Extract wrapped key params from ASN1KC buffer + */ +Sec_Result SecKey_ExtractWrappedKeyParamsAsn1Buffer(SEC_BYTE* asn1, SEC_SIZE asn1_len, SEC_BYTE* wrappedKey, + SEC_SIZE wrappedKeyLen, SEC_SIZE* written, Sec_KeyType* wrappedKeyType, SEC_OBJECTID* wrappingId, + SEC_BYTE* wrappingIv, Sec_CipherAlgorithm* wrappingAlg); + +/** + * @brief Generate an Asn1 key container for wrapped keys + */ +Sec_Result SecKey_GenerateWrappedKeyAsn1Off(SEC_BYTE* payload, SEC_SIZE payloadLen, Sec_KeyType wrappedKeyType, + SEC_OBJECTID wrappingKeyId, SEC_BYTE* wrappingIv, Sec_CipherAlgorithm wrappingAlgorithm, SEC_BYTE* output, + SEC_SIZE output_len, SEC_SIZE* written, SEC_SIZE key_offset); + +Sec_Result SecKey_GenerateWrappedKeyAsn1V3(SEC_BYTE* payload, SEC_SIZE payloadLen, Sec_KeyType wrappedKeyType, + SEC_BYTE* wrappingKey, SEC_SIZE wrappingKeyLen, SEC_BYTE* wrappingIv, Sec_CipherAlgorithm wrappingAlgorithm, + SEC_BYTE* output, SEC_SIZE output_len, SEC_SIZE* written, SEC_SIZE key_offset); +/** + * @brief Extract wrapped key params from ASN1KC + */ +Sec_Result SecKey_ExtractWrappedKeyParamsAsn1Off(Sec_Asn1KC* kc, SEC_BYTE* payload, SEC_SIZE payloadLen, + SEC_SIZE* written, Sec_KeyType* wrappedKeyType, SEC_OBJECTID* wrappingId, SEC_BYTE* wrappingIv, + Sec_CipherAlgorithm* wrappingAlg, SEC_SIZE* key_offset); + +/** + * @brief Extract wrapped key params from ASN1KC buffer + */ +Sec_Result SecKey_ExtractWrappedKeyParamsAsn1BufferOff(SEC_BYTE* asn1, SEC_SIZE asn1_len, SEC_BYTE* payload, + SEC_SIZE payloadLen, SEC_SIZE* written, Sec_KeyType* wrappedKeyType, SEC_OBJECTID* wrappingId, + SEC_BYTE* wrappingIv, Sec_CipherAlgorithm* wrappingAlg, SEC_SIZE* key_offset); + +/** + * @brief Extract wrapped key params from ASN1KC + */ +Sec_Result SecKey_ExtractWrappedKeyParamsAsn1V3(Sec_Asn1KC* kc, SEC_BYTE* payload, SEC_SIZE payloadLen, + SEC_SIZE* written, Sec_KeyType* wrappedKeyType, SEC_OBJECTID* wrappingId, SEC_BYTE* wrappingIv, + Sec_CipherAlgorithm* wrappingAlg, SEC_SIZE* key_offset, SEC_BYTE* wrappingKey, SEC_SIZE wrappingKeyLen, + SEC_SIZE* writtenWrappingKey); + +/** + * @brief Extract wrapped key params from ASN1KC buffer + */ +Sec_Result SecKey_ExtractWrappedKeyParamsAsn1BufferV3(SEC_BYTE* asn1, SEC_SIZE asn1_len, SEC_BYTE* payload, + SEC_SIZE payloadLen, SEC_SIZE* written, Sec_KeyType* wrappedKeyType, SEC_OBJECTID* wrappingId, + SEC_BYTE* wrappingIv, Sec_CipherAlgorithm* wrappingAlg, SEC_SIZE* key_offset, SEC_BYTE* wrappingKey, + SEC_SIZE wrappingKeyLen, SEC_SIZE* writtenWrappingKey); + +void SecKeyProperties_SetDefault(Sec_KeyProperties* props, Sec_KeyType type); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* SEC_SECURITY_COMMON_H */ diff --git a/include/sec_security_datatype.h b/include/sec_security_datatype.h new file mode 100755 index 0000000..5aaaa79 --- /dev/null +++ b/include/sec_security_datatype.h @@ -0,0 +1,646 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_SECURITY_DATATYPE_H +#define SEC_SECURITY_DATATYPE_H + +#include + +#ifdef __cplusplus +#include +extern "C" { +#else +#include +#endif + +#if !defined(BIT_32) && !defined(BIT_64) +#if defined(_WIN32) || defined(_WIN64) +#if defined(_WIN64) +#define BIT_64 +#else +#define BIT_32 +#endif +#elif __GNUC__ || defined(__APPLE__) +#if __x86_64__ || __ppc64__ +#define BIT_64 +#else +#define BIT_32 +#endif +#else +#error "Could not determine whether compiling for 32 bit or 64 bit system" +#endif +#endif + +/* enables debug prints */ +#define SEC_DEBUG + +/* macro to string */ +#define SEC_MTOS_(x) #x +#define SEC_MTOS(x) SEC_MTOS_(x) + +/* min */ +#ifndef SEC_MIN +#define SEC_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +/* max */ +#ifndef SEC_MAX +#define SEC_MAX(a, b) (((a) < (b)) ? (b) : (a)) +#endif + +#define SEC_MAX_FILE_PATH_LEN MAXPATHLEN + +/* maximum length of a digest value (in bytes) */ +#define SEC_DIGEST_MAX_LEN 32 + +#define SEC_DH_3072_LEN 384 + +/* maximum length of a MAC value (in bytes) */ +#define SEC_MAC_MAX_LEN 32 + +/* maximum length of a MAC key (in bytes) */ +#define SEC_MAC_KEY_MAX_LEN 32 + +/* maximum length of an RSA key modulus (in bytes) */ +#define SEC_RSA_KEY_MAX_LEN 256 + +/* length of an NIST_P256 ECC key */ +#define SEC_ECC_NISTP256_KEY_LEN 32UL + +/* maximum length of an ECC point coordinate (in bytes) */ +#define SEC_EC_KEY_MAX_LEN 80 + +/* maximum length of a signature value (in bytes) */ +#define SEC_SIGNATURE_MAX_LEN SEC_RSA_KEY_MAX_LEN + +/* maximum length of an AES key (in bytes) */ +#define SEC_AES_KEY_MAX_LEN 32 + +/* aes block size (in bytes) */ +#define SEC_AES_BLOCK_SIZE 16 + +/* maximum length of a symetric key (AES or MAC) (in bytes) */ +#define SEC_SYMETRIC_KEY_MAX_LEN SEC_MAX(SEC_AES_KEY_MAX_LEN, SEC_MAC_KEY_MAX_LEN) + +/* maximum length of the IV value (in bytes) */ +#define SEC_CIPHER_IV_MAX_LEN SEC_AES_KEY_MAX_LEN + +/* the length of the device id (in bytes) */ +#define SEC_DEVICEID_LEN 8 + +/* the length of client nonce (in bytes) */ +#define SEC_NONCE_LEN 20 + +/* fixed reserved ids */ +#define SEC_OBJECTID_INVALID 0xffffffffffffffffULL +#define SEC_OBJECTID_BASE_KEY_AES 0xfffffffffffffffeULL +#define SEC_OBJECTID_BASE_KEY_MAC 0xfffffffffffffffdULL +#define SEC_OBJECTID_STORE_MACKEYGEN_KEY 0xfffffffffffffffcULL +#define SEC_OBJECTID_CERTSTORE_KEY 0xfffffffffffffffbULL +#define SEC_OBJECTID_STORE_AES_KEY 0xfffffffffffffffaULL +#define SEC_OBJECTID_SIG_FROM_CERT 0xfffffffffffffff9ULL +#define SEC_OBJECTID_RESERVEDPLATFORM_8 0xfffffffffffffff8ULL +#define SEC_OBJECTID_RESERVEDPLATFORM_7 0xfffffffffffffff7ULL +#define SEC_OBJECTID_RESERVEDPLATFORM_6 0xfffffffffffffff6ULL +#define SEC_OBJECTID_RESERVEDPLATFORM_5 0xfffffffffffffff5ULL +#define SEC_OBJECTID_RESERVEDPLATFORM_4 0xfffffffffffffff4ULL +#define SEC_OBJECTID_RESERVEDPLATFORM_3 0xfffffffffffffff3ULL +#define SEC_OBJECTID_RESERVEDPLATFORM_2 0xfffffffffffffff2ULL +#define SEC_OBJECTID_RESERVEDPLATFORM_1 0xfffffffffffffff1ULL +#define SEC_OBJECTID_RESERVEDPLATFORM_0 0xfffffffffffffff0ULL + +/* reserved key space */ +#define SEC_OBJECTID_RESERVED_TOP 0xfffffffffffffff0ULL +#define SEC_OBJECTID_RESERVED_BASE 0xffffffffffffff00ULL + +/* user key space */ +#define SEC_OBJECTID_USER_TOP 0xffffffffffffff00ULL +#define SEC_OBJECTID_USER_BASE 0xfffffffffffff000ULL + +#if defined(BIT_64) && !defined(__APPLE__) +#define SEC_OBJECTID_PATTERN "%016lx" +#define SEC_KEY_FILENAME_PATTERN "%s%016lx.key" +#define SEC_KEYINFO_FILENAME_PATTERN "%s%016lx.keyinfo" +#define SEC_CERT_FILENAME_PATTERN "%s%016lx.cert" +#define SEC_CERTINFO_FILENAME_PATTERN "%s%016lx.certinfo" +#define SEC_BUNDLE_FILENAME_PATTERN "%s%016lx.bin" +#define SEC_VERIFICATION_FILENAME_PATTERN "%s%016lx.sha" +#else +#define SEC_OBJECTID_PATTERN "%016llx" +#define SEC_KEY_FILENAME_PATTERN "%s%016llx.key" +#define SEC_KEYINFO_FILENAME_PATTERN "%s%016llx.keyinfo" +#define SEC_CERT_FILENAME_PATTERN "%s%016llx.cert" +#define SEC_CERTINFO_FILENAME_PATTERN "%s%016llx.certinfo" +#define SEC_BUNDLE_FILENAME_PATTERN "%s%016llx.bin" +#define SEC_VERIFICATION_FILENAME_PATTERN "%s%016llx.sha" +#endif + +#define SEC_KEY_FILENAME_EXT ".key" +#define SEC_CERT_FILENAME_EXT ".cert" +#define SEC_BUNDLE_FILENAME_EXT ".bin" + +#define SEC_KEYCONTAINER_MAX_LEN (1024UL * 6UL) +#define SEC_BUNDLE_MAX_LEN (1024 * 128) +#define SEC_CERT_MAX_DATA_LEN (1024 * 64) + +#define SEC_TRUE 1 +#define SEC_FALSE 0 + +/* ASN1KC format defines */ +#define SEC_ASN1KC_WRAPPEDKEY "WrappedKey" +#define SEC_ASN1KC_WRAPPEDKEYTYPEID "WrappedKeyTypeId" +#define SEC_ASN1KC_WRAPPINGKEYID "WrappingKeyId" +#define SEC_ASN1KC_WRAPPINGIV "WrappingIV" +#define SEC_ASN1KC_WRAPPINGALGORITHMID "WrappingAlgorithmId" +#define SEC_ASN1KC_WRAPPEDKEYOFFSET "WrappedKeyOffset" +#define SEC_ASN1KC_WRAPPINGKEY "WrappingKey" + +/***************************************************************************** + * EXPORTED TYPES + *****************************************************************************/ + +typedef uint8_t SEC_BYTE; +typedef uint8_t SEC_BOOL; +typedef uint32_t SEC_SIZE; +typedef uint64_t SEC_OBJECTID; + +// A general note for all the enums below: +// the ..._NUM value must be the last defined. It is returned to indicate +// an invalid value + +typedef enum { + SEC_KEYLADDERROOT_UNIQUE, + SEC_KEYLADDERROOT_SHARED, + SEC_KEYLADDERROOT_NUM +} Sec_KeyLadderRoot; + +/** + * @brief Cipher algorithms + * + */ +typedef enum { + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING = 0, + SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING, + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, + SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING, + SEC_CIPHERALGORITHM_AES_CTR, + SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING, + SEC_CIPHERALGORITHM_RSA_OAEP_PADDING, + SEC_CIPHERALGORITHM_ECC_ELGAMAL, + SEC_CIPHERALGORITHM_NUM +} Sec_CipherAlgorithm; + +/** + * @brief Key types + * + */ +typedef enum { + SEC_KEYTYPE_AES_128 = 0, + SEC_KEYTYPE_AES_256, + SEC_KEYTYPE_RSA_1024, + SEC_KEYTYPE_RSA_2048, + SEC_KEYTYPE_RSA_1024_PUBLIC, + SEC_KEYTYPE_RSA_2048_PUBLIC, + SEC_KEYTYPE_HMAC_128, + SEC_KEYTYPE_HMAC_160, + SEC_KEYTYPE_HMAC_256, + SEC_KEYTYPE_ECC_NISTP256, + SEC_KEYTYPE_ECC_NISTP256_PUBLIC, + SEC_KEYTYPE_RSA_3072, + SEC_KEYTYPE_RSA_3072_PUBLIC, + SEC_KEYTYPE_NUM +} Sec_KeyType; + +/** + * @brief Key container types + * + */ +typedef enum { + SEC_KEYCONTAINER_RAW_AES_128 = 0, + SEC_KEYCONTAINER_RAW_AES_256, + SEC_KEYCONTAINER_RAW_HMAC_128, + SEC_KEYCONTAINER_RAW_HMAC_160, + SEC_KEYCONTAINER_RAW_HMAC_256, + SEC_KEYCONTAINER_RAW_RSA_1024, + SEC_KEYCONTAINER_RAW_RSA_2048, + SEC_KEYCONTAINER_RAW_RSA_1024_PUBLIC, + SEC_KEYCONTAINER_RAW_RSA_2048_PUBLIC, + SEC_KEYCONTAINER_PEM_RSA_1024, + SEC_KEYCONTAINER_PEM_RSA_2048, + SEC_KEYCONTAINER_PEM_RSA_1024_PUBLIC, + SEC_KEYCONTAINER_PEM_RSA_2048_PUBLIC, + SEC_KEYCONTAINER_SOC, + SEC_KEYCONTAINER_SOC_INTERNAL_0, + SEC_KEYCONTAINER_SOC_INTERNAL_1, + SEC_KEYCONTAINER_SOC_INTERNAL_2, + SEC_KEYCONTAINER_SOC_INTERNAL_3, + SEC_KEYCONTAINER_SOC_INTERNAL_4, + SEC_KEYCONTAINER_SOC_INTERNAL_5, + SEC_KEYCONTAINER_SOC_INTERNAL_6, + SEC_KEYCONTAINER_SOC_INTERNAL_7, + SEC_KEYCONTAINER_STORE, + SEC_KEYCONTAINER_DER_RSA_1024, + SEC_KEYCONTAINER_DER_RSA_2048, + SEC_KEYCONTAINER_DER_RSA_1024_PUBLIC, + SEC_KEYCONTAINER_DER_RSA_2048_PUBLIC, + SEC_KEYCONTAINER_ASN1, + SEC_KEYCONTAINER_PEM_ECC_NISTP256, + SEC_KEYCONTAINER_PEM_ECC_NISTP256_PUBLIC, + SEC_KEYCONTAINER_RAW_ECC_NISTP256, + SEC_KEYCONTAINER_RAW_ECC_NISTP256_PUBLIC, + SEC_KEYCONTAINER_DER_ECC_NISTP256, + SEC_KEYCONTAINER_DER_ECC_NISTP256_PUBLIC, + SEC_KEYCONTAINER_SOC_INTERNAL_8, + SEC_KEYCONTAINER_SOC_INTERNAL_9, + SEC_KEYCONTAINER_SOC_INTERNAL_10, + SEC_KEYCONTAINER_SOC_INTERNAL_11, + SEC_KEYCONTAINER_RAW_ECC_PRIVONLY_NISTP256, + SEC_KEYCONTAINER_RAW_RSA_3072, + SEC_KEYCONTAINER_RAW_RSA_3072_PUBLIC, + SEC_KEYCONTAINER_PEM_RSA_3072, + SEC_KEYCONTAINER_PEM_RSA_3072_PUBLIC, + SEC_KEYCONTAINER_DER_RSA_3072, + SEC_KEYCONTAINER_DER_RSA_3072_PUBLIC, + SEC_KEYCONTAINER_JTYPE, + SEC_KEYCONTAINER_COMCAST = SEC_KEYCONTAINER_JTYPE, + SEC_KEYCONTAINER_EXPORTED, + SEC_KEYCONTAINER_SOC_INTERNAL_12, + SEC_KEYCONTAINER_SOC_INTERNAL_13, + SEC_KEYCONTAINER_SOC_INTERNAL_14, + SEC_KEYCONTAINER_SOC_INTERNAL_15, + SEC_KEYCONTAINER_NUM +} Sec_KeyContainer; + +/** + * @brief Certificate container types + * + */ +typedef enum { + SEC_CERTIFICATECONTAINER_X509_DER = 0, + SEC_CERTIFICATECONTAINER_X509_PEM, + SEC_CERTIFICATECONTAINER_SOC, + SEC_CERTIFICATECONTAINER_NUM +} Sec_CertificateContainer; + +/** + * @brief Storage locations + * + */ +typedef enum { + SEC_STORAGELOC_RAM = 0, + SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_STORAGELOC_FILE, + SEC_STORAGELOC_FILE_SOFT_WRAPPED, + SEC_STORAGELOC_OEM, + SEC_STORAGELOC_SOC = SEC_STORAGELOC_OEM, + SEC_STORAGELOC_NUM +} Sec_StorageLoc; + +/** + * @brief Cipher modes + * + */ +typedef enum { + SEC_CIPHERMODE_ENCRYPT = 0, + SEC_CIPHERMODE_DECRYPT, + SEC_CIPHERMODE_ENCRYPT_NATIVEMEM, + SEC_CIPHERMODE_DECRYPT_NATIVEMEM, + SEC_CIPHERMODE_NUM +} Sec_CipherMode; + +/** + * @brief Signature algorithms + * + */ +typedef enum { + SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS = 0, + SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS, + SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS_DIGEST, + SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS_DIGEST, + SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS, + SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS, + SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST, + SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST, + SEC_SIGNATUREALGORITHM_ECDSA_NISTP256, + SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST, + SEC_SIGNATUREALGORITHM_NUM +} Sec_SignatureAlgorithm; + +/** + * @brief Signature modes + * + */ +typedef enum { + SEC_SIGNATUREMODE_SIGN = 0, + SEC_SIGNATUREMODE_VERIFY, + SEC_SIGNATUREMODE_NUM +} Sec_SignatureMode; + +/** + * @brief MAC algorithms + * + */ +typedef enum { + SEC_MACALGORITHM_HMAC_SHA1 = 0, + SEC_MACALGORITHM_HMAC_SHA256, + SEC_MACALGORITHM_CMAC_AES_128, + SEC_MACALGORITHM_NUM +} Sec_MacAlgorithm; + +/** + * @brief Digest algorithms + * + */ +typedef enum { + SEC_DIGESTALGORITHM_SHA1 = 0, + SEC_DIGESTALGORITHM_SHA256, + SEC_DIGESTALGORITHM_NUM +} Sec_DigestAlgorithm; + +/** + * @brief Random algorithms + * + */ +typedef enum { + SEC_RANDOMALGORITHM_TRUE = 0, + SEC_RANDOMALGORITHM_PRNG, + SEC_RANDOMALGORITHM_NUM +} Sec_RandomAlgorithm; + +/** + * @brief Function return codes + * + */ +typedef enum { + SEC_RESULT_SUCCESS = 0, + SEC_RESULT_FAILURE, + SEC_RESULT_INVALID_PARAMETERS, + SEC_RESULT_NO_SUCH_ITEM, + SEC_RESULT_BUFFER_TOO_SMALL, + SEC_RESULT_INVALID_INPUT_SIZE, + SEC_RESULT_INVALID_HANDLE, + SEC_RESULT_INVALID_PADDING, + SEC_RESULT_UNIMPLEMENTED_FEATURE, + SEC_RESULT_ITEM_ALREADY_PROVISIONED, + SEC_RESULT_ITEM_NON_REMOVABLE, + SEC_RESULT_VERIFICATION_FAILED, + SEC_RESULT_NO_KEYSLOTS_AVAILABLE, + SEC_RESULT_SVP_NOT_ENGAGED, + SEC_RESULT_OPL_NOT_ENGAGED, + SEC_RESULT_INVALID_SVP_DATA, + SEC_RESULT_ALLOCATION_FAILED, + SEC_RESULT_NUM +} Sec_Result; + +/** + * @brief Key output protection rights + */ +typedef enum { + SEC_KEYOUTPUTRIGHT_NOT_SET = 0x00, + SEC_KEYOUTPUTRIGHT_SVP_REQUIRED = 0x01, + SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_DTCP_ALLOWED = 0x02, + SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_1_4_ALLOWED = 0x03, + SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_2_2_ALLOWED = 0x04, + SEC_KEYOUTPUTRIGHT_ANALOG_OUTPUT_ALLOWED = 0x05, + SEC_KEYOUTPUTRIGHT_TRANSCRIPTION_COPY_ALLOWED = 0x06, + SEC_KEYOUTPUTRIGHT_UNRESTRICTED_COPY_ALLOWED = 0x07, + SEC_KEYOUTPUTRIGHT_CGMSA_REQUIRED = 0x08, + SEC_KEYOUTPUTRIGHT_RESERVED_10 = 0x09, + SEC_KEYOUTPUTRIGHT_RESERVED_11 = 0x0a, + SEC_KEYOUTPUTRIGHT_RESERVED_12 = 0x0b, + SEC_KEYOUTPUTRIGHT_RESERVED_13 = 0x0c, + SEC_KEYOUTPUTRIGHT_RESERVED_14 = 0x0d, + SEC_KEYOUTPUTRIGHT_RESERVED_15 = 0x0e, + SEC_KEYOUTPUTRIGHT_RESERVED_16 = 0x0f, + SEC_KEYOUTPUTRIGHT_NUM +} Sec_KeyOutputRight; + +/** + * @brief Key usage values + */ +typedef enum { + SEC_KEYUSAGE_DATA_KEY = 0, + SEC_KEYUSAGE_DATA, + SEC_KEYUSAGE_KEY, + SEC_KEYUSAGE_NUM +} Sec_KeyUsage; + +typedef struct { + char keyId[40]; + SEC_BYTE rights[SEC_KEYOUTPUTRIGHT_NUM]; + char notBefore[24]; + char notOnOrAfter[24]; + SEC_SIZE keyLength; + Sec_KeyType keyType; + Sec_KeyUsage usage; + SEC_BYTE cacheable; +} Sec_KeyProperties; + +/** + * @brief Raw Private RSA key data + * + */ +typedef struct +{ + SEC_BYTE n[SEC_RSA_KEY_MAX_LEN]; + SEC_BYTE d[SEC_RSA_KEY_MAX_LEN]; + SEC_BYTE e[4]; + SEC_BYTE modulus_len_be[4]; + uint32_t padding[2]; +} Sec_RSARawPrivateKey; + +/** + * @brief Full Raw Private RSA key data + * + */ +typedef struct +{ + SEC_BYTE n[SEC_RSA_KEY_MAX_LEN]; + SEC_BYTE d[SEC_RSA_KEY_MAX_LEN]; + SEC_BYTE e[4]; + SEC_BYTE modulus_len_be[4]; + uint32_t padding[2]; + SEC_BYTE p[SEC_RSA_KEY_MAX_LEN]; + SEC_BYTE q[SEC_RSA_KEY_MAX_LEN]; +} Sec_RSARawPrivateFullKey; + +/** + * @brief Raw Public RSA key data + * + */ +typedef struct +{ + SEC_BYTE n[SEC_RSA_KEY_MAX_LEN]; + SEC_BYTE e[4]; + SEC_BYTE modulus_len_be[4]; +} Sec_RSARawPublicKey; + +/** + * @brief Raw Private EC key data + * + * Note: A prv value need not be on the curve. A prv value is simply + * an integer multiplier k that tells how many times to add the + * universal public point P (on the curve) to itself get the + * corresponding private point. An ECC private key for the NIST + * 256-curve would be 256-bits. For that curve, prv is simply a + * random 256-bit number. prv can be gotten by a call to a sound + * random number generator, e.g. genrandom(256) and anything that + * comes out is valid. + * + */ +typedef struct +{ + Sec_KeyType type; // curve parameters indicated by key type + SEC_BYTE x[SEC_EC_KEY_MAX_LEN]; + SEC_BYTE y[SEC_EC_KEY_MAX_LEN]; + SEC_BYTE prv[SEC_EC_KEY_MAX_LEN]; + SEC_BYTE key_len[4]; // length in bytes of x (same as y and prv) +} Sec_ECCRawPrivateKey; + +/** + * @brief Raw Public EC key data + * + */ +typedef struct +{ + Sec_KeyType type; // curve parameters indicated by key type + SEC_BYTE x[SEC_EC_KEY_MAX_LEN]; + SEC_BYTE y[SEC_EC_KEY_MAX_LEN]; + SEC_BYTE key_len[4]; // length in bytes of x (same as y and prv) +} Sec_ECCRawPublicKey; + +/** + * @brief Raw ONLY Private EC key data + * + * This contains just a 256-bit [32 byte] ECC private key. + * $$$ If we add more ECC key types, will need to modify this. + */ +typedef struct { + SEC_BYTE prv[SEC_ECC_NISTP256_KEY_LEN]; +} Sec_ECCRawOnlyPrivateKey; + +typedef struct { + SEC_BYTE p[SEC_DH_3072_LEN]; // Prime p. Big-endian format + SEC_SIZE pLen; // Length of p in bytes. Max value is 384. + SEC_BYTE g[SEC_DH_3072_LEN]; // Base g. Big-endian format + SEC_SIZE gLen; // Length of g in bytes +} Sec_DHParameters; + +typedef enum { + NISTP256 = 0 +} EC_PARAMETERS; + +typedef struct { + EC_PARAMETERS curve; +} Sec_ECDHParameters; + +typedef enum { + SEC_KEYEXCHANGE_DH = 0, + SEC_KEYEXCHANGE_ECDH, + SEC_KEYEXCHANGE_NUM +} Sec_KeyExchangeAlgorithm; + +typedef enum { + SEC_KDF_HKDF = 0, + SEC_KDF_CONCAT, + SEC_KDF_ANSI_X_9_63, + SEC_KDF_NUM +} Sec_Kdf; + +typedef struct { + SEC_BYTE version[256]; +} Sec_ProcessorInfo; + +/** + * @brief Opaque processor initialization parameters + * + */ +typedef struct Sec_ProcessorInitParams_struct Sec_ProcessorInitParams; + +/** + * @brief Opaque processor handle + * + */ +typedef struct Sec_ProcessorHandle_struct Sec_ProcessorHandle; + +/** + * @brief Opaque key handle + * + */ +typedef struct Sec_KeyHandle_struct Sec_KeyHandle; + +/** + * @brief Opaque bundle handle + * + */ +typedef struct Sec_BundleHandle_struct Sec_BundleHandle; + +/** + * @brief Opaque cipher handle + * + */ +typedef struct Sec_CipherHandle_struct Sec_CipherHandle; + +/** + * @brief Opaque digest handle + * + */ +typedef struct Sec_DigestHandle_struct Sec_DigestHandle; + +/** + * @brief Opaque mac handle + * + */ +typedef struct Sec_MacHandle_struct Sec_MacHandle; + +/** + * @brief Opaque signature handle + * + */ +typedef struct Sec_SignatureHandle_struct Sec_SignatureHandle; + +/** + * @brief Opaque random handle + * + */ +typedef struct Sec_RandomHandle_struct Sec_RandomHandle; + +/** + * @brief Opaque certificate handle + * + */ +typedef struct Sec_CertificateHandle_struct Sec_CertificateHandle; + +/** + * @brief Opaque key exchange handle + * + */ +typedef struct Sec_KeyExchangeHandle_struct Sec_KeyExchangeHandle; + +/** + * @brief Opaque buffer handle + */ +typedef struct Sec_OpaqueBufferHandle_struct Sec_OpaqueBufferHandle; + +typedef void Sec_ProtectedMemHandle; + +#ifdef __cplusplus +} +#endif + +#endif /* SEC_SECURITY_DATATYPE_H */ diff --git a/include/sec_version.h b/include/sec_version.h new file mode 100644 index 0000000..7828f1c --- /dev/null +++ b/include/sec_version.h @@ -0,0 +1,19 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define SEC_API_VERSION "2.3.2.4" diff --git a/src/sec_adapter_asn1kc.c b/src/sec_adapter_asn1kc.c new file mode 100644 index 0000000..4c4b47b --- /dev/null +++ b/src/sec_adapter_asn1kc.c @@ -0,0 +1,753 @@ +/** + * Copyright 2020-2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_COMMON_17 + +#include "sec_security.h" +#include +#include + +// clang-format off +ASN1_CHOICE(Asn1KCAttribute_t_c) = { + ASN1_SIMPLE(Asn1KCAttribute_t_c, c.integer, ASN1_INTEGER), + ASN1_SIMPLE(Asn1KCAttribute_t_c, c.bitstring, ASN1_BIT_STRING), + ASN1_SIMPLE(Asn1KCAttribute_t_c, c.octetstring, ASN1_OCTET_STRING), + ASN1_SIMPLE(Asn1KCAttribute_t_c, c.null, ASN1_NULL), + ASN1_SIMPLE(Asn1KCAttribute_t_c, c.ia5string, ASN1_IA5STRING), + ASN1_SIMPLE(Asn1KCAttribute_t_c, c.utctime, ASN1_UTCTIME), +}ASN1_CHOICE_END(Asn1KCAttribute_t_c) + +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(Asn1KCAttribute_t_c) +//Implements Asn1KCAttribute_t_c_new and Asn1KCAttribute_t_c_free + +ASN1_SEQUENCE(Asn1KCAttribute_t) = { + ASN1_SIMPLE(Asn1KCAttribute_t, name, ASN1_IA5STRING), + ASN1_OPT(Asn1KCAttribute_t, value, Asn1KCAttribute_t_c), +}ASN1_SEQUENCE_END(Asn1KCAttribute_t) + +IMPLEMENT_ASN1_ALLOC_FUNCTIONS(Asn1KCAttribute_t) +//Implements Asn1KCAttribute_t_new and Asn1KCAttribute_t_free + +ASN1_ITEM_TEMPLATE(Sec_Asn1KC) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SET_OF, 0, Sec_Asn1KC, Asn1KCAttribute_t)ASN1_ITEM_TEMPLATE_END(Sec_Asn1KC) + +IMPLEMENT_ASN1_FUNCTIONS(Sec_Asn1KC) + +IMPLEMENT_ASN1_PRINT_FUNCTION(Sec_Asn1KC) //PRINTF +//Implements Sec_Asn1KC_new, Sec_Asn1KC_free, d2i_Sec_Asn1KC i2d_Sec_Asn1KC + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +static Sec_Result getBE_ASN1_INTEGER(SEC_BYTE * res, const ASN1_INTEGER* ai, SEC_SIZE size, SEC_BOOL signd) { + // clang-format on + BIGNUM* bn = NULL; + int bn_size; + + if (ai == NULL || res == NULL) { + SEC_LOG_ERROR("Failed invalid input"); + return SEC_RESULT_FAILURE; + } + + bn = ASN1_INTEGER_to_BN(ai, NULL); + if (bn == NULL) { + SEC_LOG_ERROR("Failed ASN1_INTEGER_to_BN"); + return SEC_RESULT_FAILURE; + } + + bn_size = BN_num_bytes(bn); + memset(res, 0, size); + if (bn_size == 0) { + //Special case size == 0 means the integer value is 0; + BN_free(bn); + return SEC_RESULT_SUCCESS; + } + + SEC_SIZE offset = size - bn_size; + if (!BN_bn2bin(bn, (res + offset))) { + BN_free(bn); + SEC_LOG_ERROR("BN_bn2bin failed offset = %d, res = %p, size = %d", offset, res, size); + return SEC_RESULT_FAILURE; + } + + // If needed extend sign bits + if (signd == SEC_TRUE) { + if ((res[offset] & 0x80) == 0x80) + memset(res, 0xFF, offset); + } + + BN_free(bn); + return SEC_RESULT_SUCCESS; +} + +#endif + +static Sec_Result setBE_ASN1_INTEGER(ASN1_INTEGER* st, SEC_BYTE* be_value, SEC_SIZE size) { + BIGNUM* bn = BN_bin2bn(be_value, (int) size, NULL); + if (bn == NULL) { + SEC_LOG_ERROR("Failed BN_bin2bn"); + return SEC_RESULT_FAILURE; + } + + if (!BN_to_ASN1_INTEGER(bn, st)) { + SEC_LOG_ERROR("Failed BN_to_ASN1_INTEGER"); + BN_free(bn); + return SEC_RESULT_FAILURE; + } + + BN_free(bn); + + return SEC_RESULT_SUCCESS; +} + +typedef enum att_choic_e { + asn1_integer, + asn1_bit_string, + asn1_octet_string, + asn1_null, + asn1_ia5string, + asn1_utctime +} att_choice; + +static Asn1KCAttribute_t* SecAsn1KC_AllocAttr(att_choice c) { + Asn1KCAttribute_t* ptr = NULL; + + ptr = Asn1KCAttribute_t_new(); + if (ptr == NULL) { + return NULL; + } + + ptr->value = Asn1KCAttribute_t_c_new(); + if (ptr->value == NULL) { + Asn1KCAttribute_t_free(ptr); + return NULL; + } + + switch (c) { + case asn1_integer: + ptr->value->c.integer = ASN1_INTEGER_new(); + if (ptr->value->c.integer == NULL) { + SEC_LOG_ERROR("Failed ASN1_INTEGER_new"); + Asn1KCAttribute_t_free(ptr); + return NULL; + } + + ptr->value->type = ASN1KCATTRIBUTE_T_CHOICE_INTEGER; + break; + + case asn1_bit_string: + ptr->value->c.bitstring = ASN1_BIT_STRING_new(); + if (ptr->value->c.bitstring == NULL) { + SEC_LOG_ERROR("Failed ASN1_BIT_STRING_new"); + Asn1KCAttribute_t_free(ptr); + return NULL; + } + + ptr->value->type = ASN1KCATTRIBUTE_T_CHOICE_BITSTRING; + break; + + case asn1_octet_string: + ptr->value->c.octetstring = ASN1_OCTET_STRING_new(); + if (ptr->value->c.octetstring == NULL) { + SEC_LOG_ERROR("Failed ASN1_OCTET_STRING_new"); + Asn1KCAttribute_t_free(ptr); + return NULL; + } + + ptr->value->type = ASN1KCATTRIBUTE_T_CHOICE_OCTETSTRING; + break; + + case asn1_null: + ptr->value->c.null = ASN1_NULL_new(); + if (ptr->value->c.null == NULL) { + SEC_LOG_ERROR("Failed ASN1_NULL_new"); + Asn1KCAttribute_t_free(ptr); + return NULL; + } + + ptr->value->type = ASN1KCATTRIBUTE_T_CHOICE_NULL; + break; + + case asn1_ia5string: + ptr->value->c.ia5string = ASN1_IA5STRING_new(); + if (ptr->value->c.ia5string == NULL) { + SEC_LOG_ERROR("Failed ASN1_IA5STRING_new"); + Asn1KCAttribute_t_free(ptr); + return NULL; + } + + ptr->value->type = ASN1KCATTRIBUTE_T_CHOICE_IA5STRING; + break; + + case asn1_utctime: + ptr->value->c.utctime = ASN1_UTCTIME_new(); + if (ptr->value->c.utctime == NULL) { + SEC_LOG_ERROR("Failed ASN1_UTCTIME_new"); + Asn1KCAttribute_t_free(ptr); + return NULL; + } + + ptr->value->type = ASN1KCATTRIBUTE_T_CHOICE_UTCTIME; + break; + + default: + Asn1KCAttribute_t_free(ptr); + return NULL; + } + + return ptr; +} + +static Sec_Result SecAsn1KC_AddAttr(Sec_Asn1KC* kc, Asn1KCAttribute_t* attribute) { + if (sk_Asn1KCAttribute_t_push(kc, attribute) == 0) { + SEC_LOG_ERROR("Sk_Asn1KCAttribute_t_push failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +static Asn1KCAttribute_t* SecAsn1KC_GetAttr(Sec_Asn1KC* kc, const char* key) { + Asn1KCAttribute_t* at = NULL; + + for (int i = 0; i < sk_Asn1KCAttribute_t_num(kc); ++i) { + at = sk_Asn1KCAttribute_t_value(kc, i); + if (strlen(key) == ASN1_STRING_length(at->name) && +#if OPENSSL_VERSION_NUMBER < 0x10100000L + Sec_Memcmp(key, ASN1_STRING_data(at->name), ASN1_STRING_length(at->name)) == 0) +#else + Sec_Memcmp(key, ASN1_STRING_get0_data(at->name), ASN1_STRING_length(at->name)) == 0) +#endif + { + return at; + } + } + + return NULL; +} + +SEC_BOOL SecAsn1KC_HasAttr(Sec_Asn1KC* kc, const char* key) { + return SecAsn1KC_GetAttr(kc, key) != NULL; +} + +Sec_Result SecAsn1KC_GetAttrLong(Sec_Asn1KC* kc, const char* key, int64_t* val) { + Asn1KCAttribute_t* attr = NULL; + + attr = SecAsn1KC_GetAttr(kc, key); + if (attr == NULL) { + SEC_LOG_ERROR("SecAsn1KC_GetAttr failed"); + return SEC_RESULT_FAILURE; + } + + if (attr->value->type != ASN1KCATTRIBUTE_T_CHOICE_INTEGER) { + SEC_LOG_ERROR("Invalid value type contained in the attribute: %d", + attr->value->c); + return SEC_RESULT_FAILURE; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + // Openssl 1.0.9 has support for signed long types + // With Openssl 1.1.x new applications should use ASN1_INTEGER_get_int64() + // instead + *val = ASN1_INTEGER_get(attr->value->c.integer); +#else + if (ASN1_INTEGER_get_int64(val, attr->value->c.integer) != 1) { + SEC_LOG_ERROR("Failed to get Long value from asn1 struct"); + return SEC_RESULT_FAILURE; + } +#endif + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecAsn1KC_GetAttrInt64(Sec_Asn1KC* kc, const char* key, int64_t* val) { + Asn1KCAttribute_t* attr = NULL; +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SEC_BYTE val_buf[sizeof(int64_t)]; +#endif + + attr = SecAsn1KC_GetAttr(kc, key); + if (attr == NULL) { + SEC_LOG_ERROR("SecAsn1KC_GetAttr failed invalid key"); + return SEC_RESULT_FAILURE; + } + + if (attr->value->type != ASN1KCATTRIBUTE_T_CHOICE_INTEGER) { + SEC_LOG_ERROR("Invalid value type contained in the attribute: %d", + attr->value->type); + return SEC_RESULT_FAILURE; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + //With openssl 1.1.x support you can use ASN1_INTEGER_get_int64() in place + //of getBE_ASN1_INTEGER() + if (getBE_ASN1_INTEGER(val_buf, attr->value->c.integer, sizeof(val), + SEC_TRUE) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("GetBE_ASN1_INTEGER failed"); + return SEC_RESULT_FAILURE; + } + + //val_buf is an 8 byte buffer that has sign bits extended if needed. + *val = (int64_t) Sec_BEBytesToUint64(val_buf); +#else + if (ASN1_INTEGER_get_int64(val, attr->value->c.integer) != 1) { + SEC_LOG_ERROR("Failed to get Long value from asn1 struct"); + return SEC_RESULT_FAILURE; + } +#endif + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecAsn1KC_GetAttrUlong(Sec_Asn1KC* kc, const char* key, uint64_t* val) { + Asn1KCAttribute_t* attr = NULL; +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SEC_BYTE val_buf[sizeof(unsigned long)]; +#endif + + attr = SecAsn1KC_GetAttr(kc, key); + if (attr == NULL) { + SEC_LOG_ERROR("SecAsn1KC_GetAttr failed invalid key"); + return SEC_RESULT_FAILURE; + } + + if (attr->value->type != ASN1KCATTRIBUTE_T_CHOICE_INTEGER) { + SEC_LOG_ERROR("Invalid value type contained in the attribute: %d", attr->value->c); + return SEC_RESULT_FAILURE; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + //With openssl 1.1.x support you can use ASN1_INTEGER_get_uint64() in place + //of getBE_ASN1_INTEGER() + if (getBE_ASN1_INTEGER(val_buf, attr->value->c.integer, sizeof(val), + SEC_FALSE) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("GetBE_ASN1_INTEGER failed"); + return SEC_RESULT_FAILURE; + } + + if (sizeof(unsigned long) < sizeof(uint64_t)) { // NOLINT + *val = Sec_BEBytesToUint32(val_buf); + } else { + *val = Sec_BEBytesToUint64(val_buf); + } + +#else + if (ASN1_INTEGER_get_uint64(val, attr->value->c.integer) != 1) { + SEC_LOG_ERROR("Failed to get Long value from asn1 struct"); + return SEC_RESULT_FAILURE; + } + +#endif + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecAsn1KC_GetAttrUint64(Sec_Asn1KC* kc, const char* key, uint64_t* val) { + Asn1KCAttribute_t* attr = NULL; +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SEC_BYTE val_buf[sizeof(uint64_t)]; +#endif + + attr = SecAsn1KC_GetAttr(kc, key); + if (attr == NULL) { + SEC_LOG_ERROR("SecAsn1KC_GetAttr failed invalid key"); + return SEC_RESULT_FAILURE; + } + + if (attr->value->type != ASN1KCATTRIBUTE_T_CHOICE_INTEGER) { + SEC_LOG_ERROR("Invalid value type contained in the attribute: %d", attr->value->c); + return SEC_RESULT_FAILURE; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + //With openssl 1.1.x support you can use ASN1_INTEGER_get_uint64() in place + //of getBE_ASN1_INTEGER() + if (getBE_ASN1_INTEGER(val_buf, attr->value->c.integer, sizeof(uint64_t), + SEC_FALSE) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("GetBE_ASN1_INTEGER failed"); + return SEC_RESULT_FAILURE; + } + + *val = Sec_BEBytesToUint64(val_buf); +#else + if (ASN1_INTEGER_get_uint64(val, attr->value->c.integer) != 1) { + SEC_LOG_ERROR("Failed to get Long value from asn1 struct"); + return SEC_RESULT_FAILURE; + } +#endif + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecAsn1KC_GetAttrBuffer(Sec_Asn1KC* kc, const char* key, SEC_BYTE* buffer, SEC_SIZE buffer_len, + SEC_SIZE* written) { + Asn1KCAttribute_t* attr = NULL; + + attr = SecAsn1KC_GetAttr(kc, key); + if (attr == NULL) { + SEC_LOG_ERROR("SecAsn1KC_GetAttr failed."); + return SEC_RESULT_FAILURE; + } + + if (attr->value->type != ASN1KCATTRIBUTE_T_CHOICE_OCTETSTRING) { + SEC_LOG_ERROR("Invalid value type contained in the attribute: %d", + attr->value->type); + return SEC_RESULT_FAILURE; + } + + *written = ASN1_STRING_length(attr->value->c.octetstring); + if (buffer != NULL) { + if (*written > buffer_len) { + SEC_LOG_ERROR("Output buffer is too small. Needed %d", *written); + return SEC_RESULT_FAILURE; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + memcpy(buffer, ASN1_STRING_data(attr->value->c.octetstring), *written); +#else + memcpy(buffer, ASN1_STRING_get0_data(attr->value->c.octetstring), *written); +#endif + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecAsn1KC_GetAttrString(Sec_Asn1KC* kc, const char* key, char* buffer, SEC_SIZE buffer_len, + SEC_SIZE* written) { + Asn1KCAttribute_t* attr = NULL; + + attr = SecAsn1KC_GetAttr(kc, key); + if (attr == NULL) { + SEC_LOG_ERROR("SecAsn1KC_GetAttr failed"); + return SEC_RESULT_FAILURE; + } + + if (attr->value->type != ASN1KCATTRIBUTE_T_CHOICE_IA5STRING) { + SEC_LOG_ERROR("Invalid value type contained in the attribute: %d", attr->value->type); + return SEC_RESULT_FAILURE; + } + + *written = ASN1_STRING_length(attr->value->c.ia5string); + if (buffer != NULL) { + if (*written >= buffer_len) { + SEC_LOG_ERROR("Output buffer is too small. Needed %d", *written); + return SEC_RESULT_FAILURE; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + memcpy(buffer, ASN1_STRING_data(attr->value->c.octetstring), *written); +#else + memcpy(buffer, ASN1_STRING_get0_data(attr->value->c.octetstring), *written); +#endif + buffer[*written] = '\0'; + } + + *written += 1; + + return SEC_RESULT_SUCCESS; +} + +Sec_Asn1KC* SecAsn1KC_Alloc() { + Sec_Asn1KC* ptr = NULL; + + ptr = Sec_Asn1KC_new(); + if (ptr == NULL) { + SEC_LOG_ERROR("Sec_Asn1KC_new failed"); + return ptr; + } + + return ptr; +} + +void SecAsn1KC_Free(Sec_Asn1KC* kc) { + if (kc != NULL) { + Sec_Asn1KC_free(kc); + } +} + +Sec_Result SecAsn1KC_AddAttrLong(Sec_Asn1KC* kc, const char* key, int64_t val) { + Sec_Result result = SEC_RESULT_FAILURE; + Asn1KCAttribute_t* ptr = SecAsn1KC_AllocAttr(asn1_integer); + + do { + if (ptr == NULL) { + SEC_LOG_ERROR("SecAsn1KC_AllocAttr failed"); + break; + } + + if (!ASN1_STRING_set(ptr->name, key, -1)) { + SEC_LOG_ERROR("ASN1_STRING_set failed"); + break; + } + + if (ASN1_INTEGER_set(ptr->value->c.integer, val) == 0) { + SEC_LOG_ERROR("ASN1_INTEGER_set failed"); + break; + } + + if (SecAsn1KC_AddAttr(kc, ptr) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttr failed"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (result != SEC_RESULT_SUCCESS) { + if (ptr != NULL) + Asn1KCAttribute_t_free(ptr); + } + return result; +} + +Sec_Result SecAsn1KC_AddAttrInt64(Sec_Asn1KC* kc, const char* key, int64_t val) { + Sec_Result result = SEC_RESULT_FAILURE; + Asn1KCAttribute_t* ptr = SecAsn1KC_AllocAttr(asn1_integer); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SEC_BYTE be_val[sizeof(val)]; +#endif + + do { + if (ptr == NULL) { + SEC_LOG_ERROR("SecAsn1KC_AllocAttr failed"); + break; + } + + if (!ASN1_STRING_set(ptr->name, key, -1)) { + SEC_LOG_ERROR("ASN1_STRING_set failed"); + break; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + Sec_Uint64ToBEBytes((uint64_t) val, be_val); + if (setBE_ASN1_INTEGER(ptr->value->c.integer, be_val, sizeof(val)) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SetBE_ASN1_INTEGER failed"); + break; + } +#else + if (ASN1_INTEGER_set_int64(ptr->value->c.integer, val) == 0) { + SEC_LOG_ERROR("ASN1_INTEGER_set_int64 failed"); + break; + } +#endif + + if (SecAsn1KC_AddAttr(kc, ptr) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttr failed"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (result != SEC_RESULT_SUCCESS) { + if (ptr != NULL) + Asn1KCAttribute_t_free(ptr); + } + return result; +} + +Sec_Result SecAsn1KC_AddAttrUlong(Sec_Asn1KC* kc, const char* key, uint64_t val) { + Sec_Result result = SEC_RESULT_FAILURE; + Asn1KCAttribute_t* ptr = SecAsn1KC_AllocAttr(asn1_integer); + SEC_BYTE be_val[sizeof(val)]; + + do { + if (ptr == NULL) { + SEC_LOG_ERROR("SecAsn1KC_AllocAttr failed"); + break; + } + + if (!ASN1_STRING_set(ptr->name, key, -1)) { + SEC_LOG_ERROR("ASN1_STRING_set failed"); + break; + } + + if (sizeof(unsigned long) < sizeof(uint64_t)) { // NOLINT + Sec_Uint32ToBEBytes((uint32_t) val, be_val); + } else { + Sec_Uint64ToBEBytes(val, be_val); + } + + if (setBE_ASN1_INTEGER(ptr->value->c.integer, be_val, sizeof(unsigned long)) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SetBE_ASN1_INTEGER failed"); + break; + } + + if (SecAsn1KC_AddAttr(kc, ptr) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttr failed"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (result != SEC_RESULT_SUCCESS) { + if (ptr != NULL) + Asn1KCAttribute_t_free(ptr); + } + return result; +} + +Sec_Result SecAsn1KC_AddAttrUint64(Sec_Asn1KC* kc, const char* key, uint64_t val) { + Sec_Result result = SEC_RESULT_FAILURE; + Asn1KCAttribute_t* ptr = SecAsn1KC_AllocAttr(asn1_integer); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SEC_BYTE be_val[sizeof(val)]; +#endif + + do { + if (ptr == NULL) { + SEC_LOG_ERROR("SecAsn1KC_AllocAttr failed"); + break; + } + + if (!ASN1_STRING_set(ptr->name, key, -1)) { + SEC_LOG_ERROR("ASN1_STRING_set failed"); + break; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + Sec_Uint64ToBEBytes(val, be_val); + if (setBE_ASN1_INTEGER(ptr->value->c.integer, be_val, sizeof(val)) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SetBE_ASN1_INTEGER failed"); + break; + } +#else + if (ASN1_INTEGER_set_uint64(ptr->value->c.integer, val) == 0) { + SEC_LOG_ERROR("ASN1_INTEGER_set_uint64 failed"); + break; + } +#endif + + if (SecAsn1KC_AddAttr(kc, ptr) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttr failed"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (result != SEC_RESULT_SUCCESS) { + if (ptr != NULL) + Asn1KCAttribute_t_free(ptr); + } + return result; +} + +Sec_Result SecAsn1KC_AddAttrString(Sec_Asn1KC* kc, const char* key, const char* val) { + Sec_Result result = SEC_RESULT_FAILURE; + Asn1KCAttribute_t* ptr = SecAsn1KC_AllocAttr(asn1_ia5string); + + do { + if (ptr == NULL) { + SEC_LOG_ERROR("SecAsn1KC_AllocAttr failed"); + break; + } + + if (!ASN1_STRING_set(ptr->name, key, -1)) { + SEC_LOG_ERROR("Failed to set attribute name"); + break; + } + + if (!ASN1_STRING_set(ptr->value->c.ia5string, val, (int) strlen(val))) { + SEC_LOG_ERROR("Failed to set ia5string"); + break; + } + + if (SecAsn1KC_AddAttr(kc, ptr) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttr failed"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (result != SEC_RESULT_SUCCESS) { + if (ptr != NULL) + Asn1KCAttribute_t_free(ptr); + } + return result; +} + +Sec_Result SecAsn1KC_AddAttrBuffer(Sec_Asn1KC* kc, const char* key, void* buf, SEC_SIZE buf_len) { + Sec_Result result = SEC_RESULT_FAILURE; + Asn1KCAttribute_t* ptr = SecAsn1KC_AllocAttr(asn1_octet_string); + + do { + if (ptr == NULL) { + SEC_LOG_ERROR("SecAsn1KC_AllocAttr failed"); + break; + } + + if (!ASN1_STRING_set(ptr->name, key, -1)) { + SEC_LOG_ERROR("Failed to set attribute name"); + break; + } + + if (!ASN1_STRING_set(ptr->value->c.octetstring, buf, (int) buf_len)) { + SEC_LOG_ERROR("Failed to set octetstring"); + break; + } + + if (SecAsn1KC_AddAttr(kc, ptr) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttr failed"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (result != SEC_RESULT_SUCCESS) { + free(ptr); + } + return result; +} + +Sec_Result SecAsn1KC_Encode(Sec_Asn1KC* kc, SEC_BYTE* buf, SEC_SIZE buf_len, SEC_SIZE* written) { + int der_len = i2d_Sec_Asn1KC(kc, NULL); + + if (buf == NULL) { + *written = der_len; + } else if (der_len > buf_len) { + SEC_LOG_ERROR("Der_encode_to_buffer invalid buffer length, der_len = %d, buf_len = %d", der_len, buf_len); + return SEC_RESULT_FAILURE; + } else { + *written = i2d_Sec_Asn1KC(kc, &buf); + } + + if (*written < 0) { + SEC_LOG_ERROR("Der_encode_to_buffer failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Asn1KC* SecAsn1KC_Decode(const SEC_BYTE* buf, SEC_SIZE buf_len) { + const unsigned char* c_buf = buf; + Sec_Asn1KC* ret = NULL; + + if (buf_len > UINT_MAX) { + SEC_LOG_ERROR("Buf length rollover"); + return NULL; + } + ret = d2i_Sec_Asn1KC(NULL, &c_buf, (long) buf_len); + return ret; +} + +#endif diff --git a/src/sec_adapter_buffer.c b/src/sec_adapter_buffer.c new file mode 100644 index 0000000..9d4b102 --- /dev/null +++ b/src/sec_adapter_buffer.c @@ -0,0 +1,65 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_security.h" +#include + +/** + * @brief initialize the Sec_Buffer structure. + * + * @param buffer Sec_Buffer structure to initialize. + * @param mem memory buffer to use. + * @param len size of the memory buffer. + */ +void SecBuffer_Init(Sec_Buffer* buffer, void* mem, SEC_SIZE len) { + buffer->base = (SEC_BYTE*) mem; + buffer->size = len; + buffer->written = 0; +} + +/** + * @brief reset the buffer. + * + * @param buffer Sec_Buffer structure to initialize. + */ +void SecBuffer_Reset(Sec_Buffer* buffer) { + buffer->written = 0; +} + +/** + * @brief Write data to a buffer. + * + * @param buffer pointer to a Sec_Buffer structure to use. + * @param data input data to write. + * @param len length of input data. + * + * @return Status of the operation. Error status will be returned if there + * is not enough space left in the output buffer. + * @return The status of the operation. + */ +Sec_Result SecBuffer_Write(Sec_Buffer* buffer, void* data, SEC_SIZE len) { + SEC_SIZE space_left = buffer->size - buffer->written; + + if (space_left < 0 || space_left < len) + return SEC_RESULT_BUFFER_TOO_SMALL; + + memcpy(buffer->base + buffer->written, data, len); + buffer->written += len; + + return SEC_RESULT_SUCCESS; +} diff --git a/src/sec_adapter_bundle.c b/src/sec_adapter_bundle.c new file mode 100644 index 0000000..047cbd5 --- /dev/null +++ b/src/sec_adapter_bundle.c @@ -0,0 +1,374 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_adapter_bundle.h" + +struct Sec_BundleHandle_struct { + Sec_ProcessorHandle* processorHandle; + SEC_OBJECTID object_id; + Sec_StorageLoc location; + Sec_BundleData bundle_data; +}; + +void Sec_FindRAMBundleData(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, Sec_RAMBundleData** data, + Sec_RAMBundleData** parent) { + *parent = NULL; + *data = processorHandle->ram_bundles; + + while ((*data) != NULL) { + if (object_id == (*data)->object_id) + return; + + *parent = (*data); + *data = (*data)->next; + } + + *parent = NULL; +} + +static Sec_Result Sec_RetrieveBundleData(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_StorageLoc* location, Sec_BundleData* bundleData) { + char file_name_bundle[SEC_MAX_FILE_PATH_LEN]; + char file_name_verification[SEC_MAX_FILE_PATH_LEN]; + Sec_RAMBundleData* ram_bundle = NULL; + Sec_RAMBundleData* ram_bundle_parent = NULL; + + CHECK_PROCHANDLE(processorHandle) + + /* check in RAM */ + Sec_FindRAMBundleData(processorHandle, object_id, &ram_bundle, &ram_bundle_parent); + if (ram_bundle != NULL) { + memcpy(bundleData, &(ram_bundle->bundle_data), sizeof(Sec_BundleData)); + *location = SEC_STORAGELOC_RAM; + return SEC_RESULT_SUCCESS; + } + + /* check in app folder */ + char* sec_dirs[] = {processorHandle->app_dir, processorHandle->global_dir}; + for (int i = 0; i < 2; i++) { + if (sec_dirs[i] != NULL) { + snprintf(file_name_bundle, sizeof(file_name_bundle), SEC_BUNDLE_FILENAME_PATTERN, sec_dirs[i], + object_id); + snprintf(file_name_verification, sizeof(file_name_verification), SEC_VERIFICATION_FILENAME_PATTERN, + sec_dirs[i], object_id); + if (SecUtils_FileExists(file_name_bundle)) { + if (SecUtils_ReadFile(file_name_bundle, bundleData->bundle, sizeof(bundleData->bundle), + &bundleData->bundle_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Could not read one of the bundle files"); + return SEC_RESULT_FAILURE; + } + + if (SecUtils_FileExists(file_name_verification)) { + if (verify_verification_file(processorHandle, file_name_verification, bundleData->bundle, + bundleData->bundle_len, NULL, 0) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Bundle verification failed"); + return SEC_RESULT_FAILURE; + } + } else { + // If sha file doesn't exist, the bundle file was created by an old SecApi. Just create the + // verification file. + if (write_verification_file(processorHandle, file_name_verification, bundleData->bundle, + bundleData->bundle_len, NULL, 0) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Could not write SHA file"); + } + } + + *location = SEC_STORAGELOC_FILE; + return SEC_RESULT_SUCCESS; + } + } + } + + return SEC_RESULT_NO_SUCH_ITEM; +} + +static Sec_Result Sec_StoreBundleData(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_StorageLoc location, Sec_BundleData* bundleData) { + Sec_RAMBundleData* ram_bundle; + + if (location == SEC_STORAGELOC_RAM) { + SecBundle_Delete(processorHandle, object_id); + + ram_bundle = calloc(1, sizeof(Sec_RAMBundleData)); + if (ram_bundle == NULL) { + SEC_LOG_ERROR("Malloc failed"); + return SEC_RESULT_FAILURE; + } + ram_bundle->object_id = object_id; + memcpy(&(ram_bundle->bundle_data), bundleData, sizeof(Sec_BundleData)); + ram_bundle->next = processorHandle->ram_bundles; + processorHandle->ram_bundles = ram_bundle; + + return SEC_RESULT_SUCCESS; + } + + if (location == SEC_STORAGELOC_FILE) { + if (processorHandle->app_dir == NULL) { + SEC_LOG_ERROR("Cannot write file because app_dir is NULL"); + return SEC_RESULT_FAILURE; + } + + SecBundle_Delete(processorHandle, object_id); + + char file_name_bundle[SEC_MAX_FILE_PATH_LEN]; + char file_name_verification[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name_bundle, sizeof(file_name_bundle), SEC_BUNDLE_FILENAME_PATTERN, processorHandle->app_dir, + object_id); + snprintf(file_name_verification, sizeof(file_name_verification), SEC_VERIFICATION_FILENAME_PATTERN, + processorHandle->app_dir, object_id); + + if (SecUtils_WriteFile(file_name_bundle, bundleData->bundle, bundleData->bundle_len) != SEC_RESULT_SUCCESS || + write_verification_file(processorHandle, file_name_verification, bundleData->bundle, + bundleData->bundle_len, NULL, 0) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Could not write one of the bundle files"); + SecUtils_RmFile(file_name_bundle); + SecUtils_RmFile(file_name_verification); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; + } + + SEC_LOG_ERROR("Unimplemented location type"); + return SEC_RESULT_UNIMPLEMENTED_FEATURE; +} + +/** + * @brief Obtain a handle to a provisioned bundle. + * + * @param processorHandle secure processor handle. + * @param object_id id of the provisioned bundle that we are attempting to obtain. + * @param bundleHandle pointer to the output key handle. + * + * @return The status of the operation. + */ +Sec_Result SecBundle_GetInstance(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_BundleHandle** bundleHandle) { + Sec_Result result; + Sec_StorageLoc location; + Sec_BundleData bundle_data; + + *bundleHandle = NULL; + + CHECK_PROCHANDLE(processorHandle) + + if (object_id == SEC_OBJECTID_INVALID) + return SEC_RESULT_INVALID_PARAMETERS; + + result = Sec_RetrieveBundleData(processorHandle, object_id, &location, &bundle_data); + if (result != SEC_RESULT_SUCCESS) + return result; + + *bundleHandle = calloc(1, sizeof(Sec_BundleHandle)); + if (*bundleHandle == NULL) { + SEC_LOG_ERROR("Malloc failed"); + return SEC_RESULT_FAILURE; + } + (*bundleHandle)->object_id = object_id; + memcpy(&((*bundleHandle)->bundle_data), &bundle_data, sizeof(Sec_BundleData)); + (*bundleHandle)->location = location; + (*bundleHandle)->processorHandle = processorHandle; + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Provision a bundle. + * + * @param processorHandle secure processor handle. + * @param object_id id of the bundle to provision. + * @param location storage location where the bundle should be provisioned. + * @param data pointer to the input key container. + * @param data_len the size of the input key container. + * + * @return The status of the operation. + */ +Sec_Result SecBundle_Provision(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, Sec_StorageLoc location, + SEC_BYTE* data, SEC_SIZE data_len) { + Sec_BundleData bundle_data; + + CHECK_PROCHANDLE(processorHandle) + + if (object_id == SEC_OBJECTID_INVALID) { + SEC_LOG_ERROR("Cannot provision object with SEC_OBJECTID_INVALID"); + return SEC_RESULT_FAILURE; + } + + if (data_len > SEC_BUNDLE_MAX_LEN) { + SEC_LOG_ERROR("Input bundle is too large"); + return SEC_RESULT_FAILURE; + } + + memcpy(bundle_data.bundle, data, data_len); + bundle_data.bundle_len = data_len; + + if (Sec_StoreBundleData(processorHandle, object_id, location, &bundle_data) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Sec_StoreBundleData failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Delete a provisioned bundle. + * + * @param processorHandle secure processor handle. + * @param object_id id of the key to delete. + * + * @return The status of the operation. + */ +Sec_Result SecBundle_Delete(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id) { + Sec_RAMBundleData* ram_bundle = NULL; + Sec_RAMBundleData* ram_bundle_parent = NULL; + SEC_SIZE bundles_found = 0; + SEC_SIZE bundles_deleted = 0; + + CHECK_PROCHANDLE(processorHandle) + + /* ram */ + Sec_FindRAMBundleData(processorHandle, object_id, &ram_bundle, &ram_bundle_parent); + if (ram_bundle != NULL) { + if (ram_bundle_parent == NULL) + processorHandle->ram_bundles = ram_bundle->next; + else + ram_bundle_parent->next = ram_bundle->next; + + Sec_Memset(ram_bundle, 0, sizeof(Sec_RAMBundleData)); + + SEC_FREE(ram_bundle); + + ++bundles_found; + ++bundles_deleted; + } + + /* file system */ + if (processorHandle->app_dir != NULL) { + char file_name_bundle[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name_bundle, sizeof(file_name_bundle), SEC_BUNDLE_FILENAME_PATTERN, processorHandle->app_dir, + object_id); + if (SecUtils_FileExists(file_name_bundle)) { + SecUtils_RmFile(file_name_bundle); + ++bundles_found; + + if (!SecUtils_FileExists(file_name_bundle)) + ++bundles_deleted; + } + + char file_name_verification[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name_verification, sizeof(file_name_verification), SEC_VERIFICATION_FILENAME_PATTERN, + processorHandle->app_dir, object_id); + if (!SecUtils_FileExists(file_name_bundle) && SecUtils_FileExists(file_name_verification)) + SecUtils_RmFile(file_name_verification); + } + + if (bundles_found == 0) + return SEC_RESULT_NO_SUCH_ITEM; + + if (bundles_found != bundles_deleted) { + SEC_LOG_ERROR("Could not delete the specified bundle. It is stored in a non-removable location."); + return SEC_RESULT_ITEM_NON_REMOVABLE; + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Release the bundle object. + * + * @param bundleHandle bundle handle to release. + * + * @return The status of the operation. + */ +Sec_Result SecBundle_Release(Sec_BundleHandle* bundleHandle) { + CHECK_HANDLE(bundleHandle) + + SEC_FREE(bundleHandle); + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Obtain the bundle data. + * + * @param bundleHandle bundle handle. + * @param buffer pointer to the output buffer that will be filled with bundle data. + * @param buffer_len the length of the output buffer. + * @param written pointer to the output value specifying the number of bytes written to the + * output buffer. + * + * @return The status of the operation. + */ +Sec_Result SecBundle_Export(Sec_BundleHandle* bundleHandle, SEC_BYTE* buffer, SEC_SIZE buffer_len, SEC_SIZE* written) { + CHECK_HANDLE(bundleHandle) + + if (buffer == NULL) { + *written = bundleHandle->bundle_data.bundle_len; + return SEC_RESULT_SUCCESS; + } + + if (buffer_len < bundleHandle->bundle_data.bundle_len) + return SEC_RESULT_BUFFER_TOO_SMALL; + + memcpy(buffer, bundleHandle->bundle_data.bundle, bundleHandle->bundle_data.bundle_len); + *written = bundleHandle->bundle_data.bundle_len; + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Find if the bundle with a specific id has been provisioned. + * + * @param processorHandle secure processor handle. + * @param object_id id of the certificate. + * + * @return 1 if an object has been provisioned, 0 if it has not been. + */ +SEC_BOOL SecBundle_IsProvisioned(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id) { + Sec_BundleHandle* bundleHandle; + + if (SecBundle_GetInstance(processorHandle, object_id, &bundleHandle) != SEC_RESULT_SUCCESS) { + return SEC_FALSE; + } + + SecBundle_Release(bundleHandle); + return SEC_TRUE; +} + +/** + * @brief finds the first available bundle id in the range passed in. + * + * @param proc secure processor. + * @param base bottom of the range to search. + * @param top top of the range to search. + * @return + */ +SEC_OBJECTID SecBundle_ObtainFreeObjectId(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID base, SEC_OBJECTID top) { + SEC_OBJECTID id; + Sec_BundleHandle* bundleHandle; + Sec_Result result; + + for (id = base; id < top; ++id) { + result = SecBundle_GetInstance(processorHandle, id, &bundleHandle); + if (result == SEC_RESULT_SUCCESS) + SecBundle_Release(bundleHandle); + else + return id; + } + + return SEC_OBJECTID_INVALID; +} diff --git a/src/sec_adapter_bundle.h b/src/sec_adapter_bundle.h new file mode 100644 index 0000000..8e7fe31 --- /dev/null +++ b/src/sec_adapter_bundle.h @@ -0,0 +1,29 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_ADAPTER_BUNDLE_H +#define SEC_ADAPTER_BUNDLE_H + +#include "sec_adapter_processor.h" +#include "sec_adapter_utils.h" +#include + +void Sec_FindRAMBundleData(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, Sec_RAMBundleData** data, + Sec_RAMBundleData** parent); + +#endif // SEC_ADAPTER_BUNDLE_H diff --git a/src/sec_adapter_certificate.c b/src/sec_adapter_certificate.c new file mode 100644 index 0000000..d0c6156 --- /dev/null +++ b/src/sec_adapter_certificate.c @@ -0,0 +1,734 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_adapter_processor.h" +#include "sec_adapter_pubops.h" +#include "sec_adapter_utils.h" +#include +#include +#include + +#define CERTIFICATE_BUFFER_SIZE (1024 * 64) + +struct Sec_CertificateHandle_struct { + Sec_ProcessorHandle* processorHandle; + SEC_OBJECTID object_id; + Sec_StorageLoc location; + Sec_CertificateData cert_data; +}; + +static void Sec_FindRAMCertificateData(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_RAMCertificateData** data, Sec_RAMCertificateData** parent) { + *parent = NULL; + *data = processorHandle->ram_certs; + + while ((*data) != NULL) { + if (object_id == (*data)->object_id) + return; + + *parent = (*data); + *data = (*data)->next; + } + + *parent = NULL; +} + +static Sec_Result Sec_RetrieveCertificateData(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_StorageLoc* location, Sec_CertificateData* certData) { + char file_name_cert[SEC_MAX_FILE_PATH_LEN]; + char file_name_info[SEC_MAX_FILE_PATH_LEN]; + char file_name_verification[SEC_MAX_FILE_PATH_LEN]; + Sec_RAMCertificateData* ram_cert = NULL; + Sec_RAMCertificateData* ram_cert_parent = NULL; + SEC_SIZE data_read; + + CHECK_PROCHANDLE(processorHandle) + + /* check in RAM */ + Sec_FindRAMCertificateData(processorHandle, object_id, &ram_cert, &ram_cert_parent); + if (ram_cert != NULL) { + memcpy(certData, &(ram_cert->cert_data), sizeof(Sec_CertificateData)); + *location = SEC_STORAGELOC_RAM; + return SEC_RESULT_SUCCESS; + } + + /* check in app dir */ + char* sec_dirs[] = {processorHandle->app_dir, processorHandle->global_dir}; + for (int i = 0; i < 2; i++) { + if (sec_dirs[i] != NULL) { + snprintf(file_name_cert, sizeof(file_name_cert), SEC_CERT_FILENAME_PATTERN, sec_dirs[i], + object_id); + snprintf(file_name_info, sizeof(file_name_info), SEC_CERTINFO_FILENAME_PATTERN, sec_dirs[i], + object_id); + snprintf(file_name_verification, sizeof(file_name_verification), SEC_VERIFICATION_FILENAME_PATTERN, + sec_dirs[i], object_id); + if (SecUtils_FileExists(file_name_cert) && SecUtils_FileExists(file_name_info)) { + if (SecUtils_ReadFile(file_name_cert, certData->cert, sizeof(certData->cert), &certData->cert_len) != + SEC_RESULT_SUCCESS || + SecUtils_ReadFile(file_name_info, certData->mac, sizeof(certData->mac), &data_read) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Could not read one of the certificate files"); + return SEC_RESULT_FAILURE; + } + + if (data_read != sizeof(certData->mac)) { + SEC_LOG_ERROR("File is not of the correct size"); + return SEC_RESULT_FAILURE; + } + + if (SecUtils_FileExists(file_name_verification)) { + if (verify_verification_file(processorHandle, file_name_verification, certData->cert, + certData->cert_len, certData->mac, sizeof(certData->mac)) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Certificate verification failed"); + return SEC_RESULT_FAILURE; + } + } else { + // If sha file doesn't exist, the bundle file was created by an old SecApi. Just create the + // verification file. + if (write_verification_file(processorHandle, file_name_verification, certData->cert, + certData->cert_len, certData->mac, sizeof(certData->mac)) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Could not write SHA file"); + } + } + + *location = SEC_STORAGELOC_FILE; + return SEC_RESULT_SUCCESS; + } + } + } + + return SEC_RESULT_NO_SUCH_ITEM; +} + +static Sec_Result Sec_SignCertificateData(Sec_ProcessorHandle* processorHandle, Sec_CertificateData* cert_store) { + SEC_SIZE mac_size; + + CHECK_PROCHANDLE(processorHandle) + + if (SecMac_SingleInputId(processorHandle, SEC_MACALGORITHM_HMAC_SHA256, SEC_OBJECTID_CERTSTORE_KEY, + cert_store->cert, cert_store->cert_len, cert_store->mac, &mac_size) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecMac_SingleInputId failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +static Sec_Result Sec_ValidateCertificateData(Sec_ProcessorHandle* processorHandle, Sec_CertificateData* cert_store) { + SEC_BYTE mac_buffer[SEC_MAC_MAX_LEN]; + SEC_SIZE mac_size = 0; + + CHECK_PROCHANDLE(processorHandle) + + if (SecMac_SingleInputId(processorHandle, SEC_MACALGORITHM_HMAC_SHA256, SEC_OBJECTID_CERTSTORE_KEY, + cert_store->cert, cert_store->cert_len, mac_buffer, &mac_size) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecMac_SingleInputId failed"); + return SEC_RESULT_FAILURE; + } + + if (Sec_Memcmp(mac_buffer, cert_store->mac, mac_size) != 0) { + SEC_LOG_ERROR("Certificate mac does not match the expected value"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +static Sec_Result Sec_ProcessCertificateContainer(Sec_ProcessorHandle* processorHandle, Sec_CertificateData* cert_data, + Sec_CertificateContainer data_type, void* data, SEC_SIZE data_len) { + BIO* bio = NULL; + X509* x509 = NULL; + + if (data_type == SEC_CERTIFICATECONTAINER_X509_DER) { + Sec_RSARawPublicKey pub_rsa; + Sec_ECCRawPublicKey pub_ecc; + if (Pubops_ExtractRSAPubFromX509Der(data, data_len, &pub_rsa) != SEC_RESULT_SUCCESS && + Pubops_ExtractECCPubFromX509Der(data, data_len, &pub_ecc) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Pubops_ExtractECCPubFromX509Der failed"); + return SEC_RESULT_FAILURE; + } + + memset(cert_data, 0, sizeof(Sec_CertificateData)); + memcpy(cert_data->cert, data, data_len); + cert_data->cert_len = data_len; + return Sec_SignCertificateData(processorHandle, cert_data); + } + + if (data_type == SEC_CERTIFICATECONTAINER_X509_PEM) { + bio = BIO_new_mem_buf(data, (int) data_len); + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + SEC_BIO_FREE(bio); + bio = NULL; + + if (x509 == NULL) { + SEC_X509_FREE(x509); + SEC_LOG_ERROR("Invalid X509 key container"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + memset(cert_data, 0, sizeof(Sec_CertificateData)); + cert_data->cert_len = SecUtils_X509ToDerLen(x509, cert_data->cert, sizeof(cert_data->cert)); + if (cert_data->cert_len == 0) { + SEC_X509_FREE(x509); + SEC_LOG_ERROR("Certificate is too large"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + SEC_X509_FREE(x509); + return Sec_SignCertificateData(processorHandle, cert_data); + } + + SEC_LOG_ERROR("Unimplemented certificate container type"); + return SEC_RESULT_UNIMPLEMENTED_FEATURE; +} + +static Sec_Result Sec_StoreCertificateData(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_StorageLoc location, Sec_CertificateData* certData) { + Sec_RAMCertificateData* ram_cert; + + if (location == SEC_STORAGELOC_RAM) { + SecCertificate_Delete(processorHandle, object_id); + + ram_cert = calloc(1, sizeof(Sec_RAMCertificateData)); + if (ram_cert == NULL) { + SEC_LOG_ERROR("Malloc failed"); + return SEC_RESULT_FAILURE; + } + ram_cert->object_id = object_id; + memcpy(&(ram_cert->cert_data), certData, sizeof(Sec_CertificateData)); + ram_cert->next = processorHandle->ram_certs; + processorHandle->ram_certs = ram_cert; + + return SEC_RESULT_SUCCESS; + } + + if (location == SEC_STORAGELOC_FILE) { + if (processorHandle->app_dir == NULL) { + SEC_LOG_ERROR("Cannot write file because app_dir is NULL"); + return SEC_RESULT_FAILURE; + } + + SecCertificate_Delete(processorHandle, object_id); + + char file_name_cert[SEC_MAX_FILE_PATH_LEN]; + char file_name_info[SEC_MAX_FILE_PATH_LEN]; + char file_name_verification[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name_cert, sizeof(file_name_cert), SEC_CERT_FILENAME_PATTERN, processorHandle->app_dir, + object_id); + snprintf(file_name_info, sizeof(file_name_info), SEC_CERTINFO_FILENAME_PATTERN, processorHandle->app_dir, + object_id); + snprintf(file_name_verification, sizeof(file_name_verification), SEC_VERIFICATION_FILENAME_PATTERN, + processorHandle->app_dir, object_id); + + if (SecUtils_WriteFile(file_name_cert, certData->cert, certData->cert_len) != SEC_RESULT_SUCCESS || + SecUtils_WriteFile(file_name_info, certData->mac, sizeof(certData->mac)) != SEC_RESULT_SUCCESS || + write_verification_file(processorHandle, file_name_verification, certData->cert, certData->cert_len, + certData->mac, sizeof(certData->mac)) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Could not write one of the cert files"); + SecUtils_RmFile(file_name_cert); + SecUtils_RmFile(file_name_info); + SecUtils_RmFile(file_name_verification); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; + } + + SEC_LOG_ERROR("Unimplemented location type"); + return SEC_RESULT_UNIMPLEMENTED_FEATURE; +} + +/** + * @brief Obtain a handle to the provisioned certificate. + * + * @param processorHandle secure processor handle. + * @param object_id id of the certificate. + * @param certHandle output certificate handle. + * + * @return The status of the operation. + */ +Sec_Result SecCertificate_GetInstance(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_CertificateHandle** certHandle) { + Sec_Result result; + Sec_CertificateData cert_data; + Sec_StorageLoc location; + + CHECK_PROCHANDLE(processorHandle) + + if (object_id == SEC_OBJECTID_INVALID) { + SEC_LOG_ERROR("Invalid object_id"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + result = Sec_RetrieveCertificateData(processorHandle, object_id, &location, &cert_data); + if (result != SEC_RESULT_SUCCESS) { + return result; + } + + result = Sec_ValidateCertificateData(processorHandle, &cert_data); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("_Sec_ValidateCertificateData failed"); + return SEC_RESULT_VERIFICATION_FAILED; + } + + *certHandle = calloc(1, sizeof(Sec_CertificateHandle)); + if (*certHandle == NULL) { + SEC_LOG_ERROR("Calloc failed"); + return SEC_RESULT_FAILURE; + } + + (*certHandle)->object_id = object_id; + memcpy(&((*certHandle)->cert_data), &cert_data, sizeof(Sec_CertificateData)); + (*certHandle)->location = location; + (*certHandle)->processorHandle = processorHandle; + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Provision a certificate onto the system. + * + * @param processorHandle secure processor handle. + * @param object_id id of the certificate to provision. + * @param location location where the certificate should be provisioned to. + * @param data_type container type for the input certificate data. + * @param data pointer to certificate container data. + * @param data_len certificate container data length. + * + * @return The status of the operation. + */ +Sec_Result SecCertificate_Provision(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_StorageLoc location, Sec_CertificateContainer data_type, SEC_BYTE* data, SEC_SIZE data_len) { + Sec_CertificateData cert_data; + Sec_Result result; + + CHECK_PROCHANDLE(processorHandle) + + if (object_id == SEC_OBJECTID_INVALID) { + SEC_LOG_ERROR("Cannot provision object with SEC_OBJECTID_INVALID"); + return SEC_RESULT_FAILURE; + } + + result = Sec_ProcessCertificateContainer(processorHandle, &cert_data, data_type, data, data_len); + if (result != SEC_RESULT_SUCCESS) + return result; + + return Sec_StoreCertificateData(processorHandle, object_id, location, &cert_data); +} + +/** + * @brief Delete the specified certificate from the system. + * + * @param processorHandle secure processor handle. + * @param object_id id of the certificate to delete. + * + * @return The status of the operation. + */ +Sec_Result SecCertificate_Delete(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id) { + Sec_RAMCertificateData* ram_cert = NULL; + Sec_RAMCertificateData* ram_cert_parent = NULL; + SEC_SIZE certs_found = 0; + SEC_SIZE certs_deleted = 0; + + CHECK_PROCHANDLE(processorHandle) + + /* ram */ + Sec_FindRAMCertificateData(processorHandle, object_id, &ram_cert, &ram_cert_parent); + if (ram_cert != NULL) { + if (ram_cert_parent == NULL) + processorHandle->ram_certs = ram_cert->next; + else + ram_cert_parent->next = ram_cert->next; + + Sec_Memset(ram_cert, 0, sizeof(Sec_RAMCertificateData)); + + SEC_FREE(ram_cert); + + ++certs_found; + ++certs_deleted; + } + + /* app_dir */ + if (processorHandle->app_dir != NULL) { + char file_name[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name, sizeof(file_name), SEC_CERT_FILENAME_PATTERN, processorHandle->app_dir, object_id); + if (SecUtils_FileExists(file_name)) { + SecUtils_RmFile(file_name); + ++certs_found; + + if (!SecUtils_FileExists(file_name)) + ++certs_deleted; + } + + char file_name_info[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name_info, sizeof(file_name_info), SEC_CERTINFO_FILENAME_PATTERN, processorHandle->app_dir, + object_id); + if (!SecUtils_FileExists(file_name) && SecUtils_FileExists(file_name_info)) { + SecUtils_RmFile(file_name_info); + } + + char file_name_verification[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name_verification, sizeof(file_name_verification), SEC_VERIFICATION_FILENAME_PATTERN, + processorHandle->app_dir, object_id); + if (!SecUtils_FileExists(file_name) && SecUtils_FileExists(file_name_verification)) + SecUtils_RmFile(file_name_verification); + } + + if (certs_found == 0) + return SEC_RESULT_NO_SUCH_ITEM; + + if (certs_found != certs_deleted) + return SEC_RESULT_ITEM_NON_REMOVABLE; + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Extract the RSA public key information from the certificate. + * + * @param certificateHandle certificate handle. + * @param public_key pointer to the output structure that will be filled with + * public key data. + * + * @return The status of the operation. + */ +Sec_Result SecCertificate_ExtractRSAPublicKey(Sec_CertificateHandle* certificateHandle, + Sec_RSARawPublicKey* public_key) { + CHECK_HANDLE(certificateHandle) + + if (Pubops_ExtractRSAPubFromX509Der(certificateHandle->cert_data.cert, certificateHandle->cert_data.cert_len, + public_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_ExtractRSAPubFromX509Der failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Extract the ECC public key information from the certificate. + * + * @param certificateHandle certificate handle. + * @param public_key pointer to the output structure that will be filled with + * public key data. + * + * @return The status of the operation. + */ +Sec_Result SecCertificate_ExtractECCPublicKey(Sec_CertificateHandle* certificateHandle, + Sec_ECCRawPublicKey* public_key) { + CHECK_HANDLE(certificateHandle) + + if (Pubops_ExtractECCPubFromX509Der(certificateHandle->cert_data.cert, certificateHandle->cert_data.cert_len, + public_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_ExtractECCPubFromX509Der failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Verify certificate signature. + * + * @param certificateHandle certificate handle. + * @param keyHandle handle of the private key used for signing or it's corresponding + * public key. + * + * @return The status of the operation. + */ +Sec_Result SecCertificate_Verify(Sec_CertificateHandle* certificateHandle, Sec_KeyHandle* keyHandle) { + Sec_RSARawPublicKey rsa_public_key; + Sec_ECCRawPublicKey ecc_public_key; + Sec_Result result = SEC_RESULT_FAILURE; + + CHECK_HANDLE(certificateHandle) + CHECK_HANDLE(keyHandle) + + switch (SecKey_GetKeyType(keyHandle)) { + case SEC_KEYTYPE_RSA_1024: + case SEC_KEYTYPE_RSA_1024_PUBLIC: + case SEC_KEYTYPE_RSA_2048: + case SEC_KEYTYPE_RSA_2048_PUBLIC: + case SEC_KEYTYPE_RSA_3072: + case SEC_KEYTYPE_RSA_3072_PUBLIC: + if (SecKey_ExtractRSAPublicKey(keyHandle, &rsa_public_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExtractRSAPublicKey failed"); + break; + } + + result = SecCertificate_VerifyWithRawRSAPublicKey(certificateHandle, &rsa_public_key); + break; + + case SEC_KEYTYPE_ECC_NISTP256: + case SEC_KEYTYPE_ECC_NISTP256_PUBLIC: + if (SecKey_ExtractECCPublicKey(keyHandle, &ecc_public_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExtractECCPublicKey failed"); + break; + } + + result = SecCertificate_VerifyWithRawECCPublicKey(certificateHandle, &ecc_public_key); + break; + + default: + break; // defaults to FAILURE + } + + return result; +} + +/** + * @brief Verify certificate signature. + * + * @param certificateHandle certificate handle. + * @param public_key structure holding the public key information. + * + * @return The status of the operation. + */ +Sec_Result SecCertificate_VerifyWithRawRSAPublicKey(Sec_CertificateHandle* certificateHandle, + Sec_RSARawPublicKey* public_key) { + CHECK_HANDLE(certificateHandle) + if (public_key == NULL) { + SEC_LOG_ERROR("NULL public_key"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + if (Pubops_VerifyX509WithPubRsa(certificateHandle->cert_data.cert, certificateHandle->cert_data.cert_len, + public_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Pubops_VerifyX509WithPubRsa failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Verify certificate signature - ECC. + * + * @param certificateHandle certificate handle. + * @param public_key structure holding the public key information. + * + * @return The status of the operation. + */ +Sec_Result SecCertificate_VerifyWithRawECCPublicKey(Sec_CertificateHandle* certificateHandle, + Sec_ECCRawPublicKey* public_key) { + CHECK_HANDLE(certificateHandle) + if (public_key == NULL) { + SEC_LOG_ERROR("NULL public_key"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + if (Pubops_VerifyX509WithPubEcc(certificateHandle->cert_data.cert, certificateHandle->cert_data.cert_len, + public_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Pubops_VerifyX509WithPubEcc failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Obtain the certificate data in clear text DER format. + * + * @param certificateHandle certificate handle. + * @param buffer pointer to the output buffer that will be filled with certificate data. + * @param buffer_len the length of the output buffer. + * @param written pointer to the output value specifying the number of bytes written to the + * output buffer. + * + * @return The status of the operation. + */ +Sec_Result SecCertificate_Export(Sec_CertificateHandle* certificateHandle, SEC_BYTE* buffer, SEC_SIZE buffer_len, + SEC_SIZE* written) { + CHECK_HANDLE(certificateHandle) + + if (buffer == NULL) { + *written = certificateHandle->cert_data.cert_len; + return SEC_RESULT_SUCCESS; + } + + if (buffer_len < certificateHandle->cert_data.cert_len) + return SEC_RESULT_BUFFER_TOO_SMALL; + + memcpy(buffer, certificateHandle->cert_data.cert, certificateHandle->cert_data.cert_len); + *written = certificateHandle->cert_data.cert_len; + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Release the certificate object. + * + * @param certificateHandle certificate handle. + * + * @return The status of the operation. + */ +Sec_Result SecCertificate_Release(Sec_CertificateHandle* certificateHandle) { + CHECK_HANDLE(certificateHandle) + SEC_FREE(certificateHandle); + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Obtain a list of all provisioned items. At most maxNumItems will be written to the output buffer. + * + * @param proc Secure processor handle. + * @param items buffer that the found item ids will be stored in. + * @param maxNumItems maximum number of items that can be written to the output buffer. + * + * @return number of items written. + */ +SEC_SIZE SecCertificate_List(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID* items, SEC_SIZE maxNumItems) { + Sec_RAMCertificateData* cert; + SEC_SIZE num_items = 0; + + CHECK_PROCHANDLE(processorHandle) + + /* look in RAM */ + cert = processorHandle->ram_certs; + while (cert != NULL) { + num_items = SecUtils_UpdateItemList(items, maxNumItems, num_items, cert->object_id); + cert = cert->next; + } + + /* look in file system */ + if (processorHandle->global_dir != NULL) { + num_items = SecUtils_UpdateItemListFromDir(items, maxNumItems, num_items, processorHandle->global_dir, + SEC_CERT_FILENAME_EXT); + } + + if (processorHandle->app_dir != NULL) { + num_items = SecUtils_UpdateItemListFromDir(items, maxNumItems, num_items, processorHandle->app_dir, + SEC_CERT_FILENAME_EXT); + } + + return num_items; +} + +X509* SecCertificate_DerToX509(void* mem, SEC_SIZE len) { + X509* x509 = NULL; + const SEC_BYTE* ptr = (const SEC_BYTE*) mem; + x509 = d2i_X509(&x509, &ptr, len); + return x509; +} + +Sec_KeyType SecCertificate_GetKeyType(Sec_CertificateHandle* certificateHandle) { + Sec_RSARawPublicKey pub_rsa; + if (Pubops_ExtractRSAPubFromX509Der(certificateHandle->cert_data.cert, certificateHandle->cert_data.cert_len, + &pub_rsa) == SEC_RESULT_SUCCESS) { + switch (Sec_BEBytesToUint32(pub_rsa.modulus_len_be)) { + case 128: + return SEC_KEYTYPE_RSA_1024_PUBLIC; + + case 256: + return SEC_KEYTYPE_RSA_2048_PUBLIC; + + case 384: + return SEC_KEYTYPE_RSA_3072_PUBLIC; + + default: + SEC_LOG_ERROR("Invalid RSA modulus size encountered: %d", Sec_BEBytesToUint32(pub_rsa.modulus_len_be)); + return SEC_KEYTYPE_NUM; + } + } + + Sec_ECCRawPublicKey pub_ecc; + if (Pubops_ExtractECCPubFromX509Der(certificateHandle->cert_data.cert, certificateHandle->cert_data.cert_len, + &pub_ecc) == SEC_RESULT_SUCCESS) { + return SEC_KEYTYPE_ECC_NISTP256_PUBLIC; + } + + SEC_LOG_ERROR("Could not find valid pub key in the certificate"); + return SEC_KEYTYPE_NUM; +} + +/** + * @brief Obtain an OpenSSL X509 certificate from the Security API cert handle. + */ +X509* SecCertificate_ToX509(Sec_CertificateHandle* certificateHandle) { + SEC_BYTE exported_cert[CERTIFICATE_BUFFER_SIZE]; + SEC_SIZE exported_cert_len; + + if (SecCertificate_Export(certificateHandle, exported_cert, sizeof(exported_cert), &exported_cert_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_Export failed"); + return NULL; + } + + return SecCertificate_DerToX509(exported_cert, exported_cert_len); +} + +/** + * @brief Find if the certificate with a specific id has been provisioned. + * + * @param processorHandle secure processor handle. + * @param object_id id of the certificate. + * + * @return 1 if an object has been provisioned, 0 if it has not been. + */ +SEC_BOOL SecCertificate_IsProvisioned(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id) { + Sec_CertificateHandle* certificateHandle; + + if (SecCertificate_GetInstance(processorHandle, object_id, &certificateHandle) != SEC_RESULT_SUCCESS) { + return SEC_FALSE; + } + + SecCertificate_Release(certificateHandle); + return SEC_TRUE; +} + +/** + * @brief Obtain the size of the certificate in DER format. + * + * @param certificateHandle certificate whose size we want to obtain. + */ +SEC_SIZE SecCertificate_GetSize(Sec_CertificateHandle* certificateHandle) { + SEC_BYTE buffer[SEC_CERT_MAX_DATA_LEN]; + SEC_SIZE written; + + if (SecCertificate_Export(certificateHandle, buffer, sizeof(buffer), &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_Export failed"); + return 0; + } + + return written; +} + +/** + * @brief finds the first available certificate id in the range passed in. + * + * @param proc secure processor. + * @param base bottom of the range to search. + * @param top top of the range to search. + * @return + */ +SEC_OBJECTID SecCertificate_ObtainFreeObjectId(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID base, + SEC_OBJECTID top) { + SEC_OBJECTID id; + Sec_CertificateHandle* certificateHandle; + Sec_Result result; + + for (id = base; id < top; ++id) { + result = SecCertificate_GetInstance(processorHandle, id, &certificateHandle); + + if (result == SEC_RESULT_SUCCESS) + SecCertificate_Release(certificateHandle); + else + return id; + } + + return SEC_OBJECTID_INVALID; +} diff --git a/src/sec_adapter_cipher.c b/src/sec_adapter_cipher.c new file mode 100644 index 0000000..7921220 --- /dev/null +++ b/src/sec_adapter_cipher.c @@ -0,0 +1,1169 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_adapter_cipher.h" +#include + +#define RSA_OAEP_PADDING_SIZE 42 + +struct Sec_CipherHandle_struct { + Sec_ProcessorHandle* processorHandle; + union { + sa_crypto_cipher_context context; + RSA* rsa; + EC_KEY* ec; + } cipher; + + Sec_CipherAlgorithm algorithm; + Sec_CipherMode mode; + SEC_BOOL last; + SEC_BOOL svp_required; + Sec_KeyHandle* keyHandle; +}; + +static sa_cipher_mode get_cipher_mode(Sec_CipherMode mode); + +static SEC_BOOL rsa_encrypt_pkcs1v15(void* out, size_t* out_length, const RSA* rsa, const void* in, size_t in_length); + +static SEC_BOOL rsa_encrypt_oaep(void* out, size_t* out_length, const RSA* rsa, const void* in, size_t in_length); + +static SEC_BOOL is_svp_required(Sec_KeyProperties* props); + +/** + * @brief Initialize cipher object. + * + * @param processorHandle secure processor handle. + * @param algorithm cipher algorithm to use. + * @param mode cipher mode to use. + * @param keyHandle handle to use. + * @param iv initialization vector value. Can be set to NULL is the cipher + * algorithm chosen does not require it. + * @param cipherHandle pointer to a cipher handle that will be set once + * the cipher object is constructed. + * + * @return The status of the operation. + */ +Sec_Result SecCipher_GetInstance(Sec_ProcessorHandle* processorHandle, Sec_CipherAlgorithm algorithm, + Sec_CipherMode mode, Sec_KeyHandle* keyHandle, SEC_BYTE* iv, Sec_CipherHandle** cipherHandle) { + CHECK_PROCHANDLE(processorHandle) + CHECK_HANDLE(keyHandle) + Sec_Result result; + Sec_KeyProperties key_properties; + + result = SecKey_GetProperties(keyHandle, &key_properties); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetProperties failed"); + return result; + } + + SEC_BOOL svp_required = is_svp_required(&key_properties); + result = SecCipher_IsValidKey(key_properties.keyType, algorithm, mode, iv); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Invalid key used for specified algorithm"); + return result; + } + + // Output Protection tests are delegated to SecApi 3.0, so removed from here. + Sec_KeyType key_type = SecKey_GetKeyType(keyHandle); + const Sec_Key* key = get_key(keyHandle); + switch (key_type) { + case SEC_KEYTYPE_AES_128: + case SEC_KEYTYPE_AES_256: + case SEC_KEYTYPE_RSA_1024: + case SEC_KEYTYPE_RSA_2048: + case SEC_KEYTYPE_HMAC_128: + case SEC_KEYTYPE_HMAC_160: + case SEC_KEYTYPE_HMAC_256: + case SEC_KEYTYPE_ECC_NISTP256: + case SEC_KEYTYPE_RSA_3072: { + sa_cipher_algorithm cipher_algorithm; + void* parameters; + result = get_cipher_algorithm(algorithm, SEC_FALSE, &cipher_algorithm, ¶meters, iv, 0, 0); + if (result != SEC_RESULT_SUCCESS) + return result; + + sa_cipher_mode cipher_mode = get_cipher_mode(mode); + sa_crypto_cipher_context context; + + sa_status status; + status = sa_crypto_cipher_init(&context, cipher_algorithm, cipher_mode, key->handle, parameters); + SEC_FREE(parameters); + CHECK_STATUS(status) + + *cipherHandle = calloc(1, sizeof(Sec_CipherHandle)); + (*cipherHandle)->cipher.context = context; + break; + } + case SEC_KEYTYPE_RSA_1024_PUBLIC: + case SEC_KEYTYPE_RSA_2048_PUBLIC: + case SEC_KEYTYPE_RSA_3072_PUBLIC: { + if (algorithm != SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING && + algorithm != SEC_CIPHERALGORITHM_RSA_OAEP_PADDING) { + return SEC_RESULT_INVALID_PARAMETERS; + } + + if (mode != SEC_CIPHERMODE_ENCRYPT && mode != SEC_CIPHERMODE_ENCRYPT_NATIVEMEM) { + return SEC_RESULT_INVALID_PARAMETERS; + } + + *cipherHandle = calloc(1, sizeof(Sec_CipherHandle)); + (*cipherHandle)->cipher.rsa = key->rsa; + break; + } + case SEC_KEYTYPE_ECC_NISTP256_PUBLIC: { + if (algorithm != SEC_CIPHERALGORITHM_ECC_ELGAMAL) { + return SEC_RESULT_INVALID_PARAMETERS; + } + + if (mode != SEC_CIPHERMODE_ENCRYPT && mode != SEC_CIPHERMODE_ENCRYPT_NATIVEMEM) { + return SEC_RESULT_INVALID_PARAMETERS; + } + + *cipherHandle = calloc(1, sizeof(Sec_CipherHandle)); + (*cipherHandle)->cipher.ec = key->ec_key; + break; + } + default: + SEC_LOG_ERROR("unsupported key type %u", key_type); + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + } + + (*cipherHandle)->keyHandle = keyHandle; + (*cipherHandle)->processorHandle = processorHandle; + (*cipherHandle)->algorithm = algorithm; + (*cipherHandle)->mode = mode; + (*cipherHandle)->svp_required = svp_required; + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Update the IV on the cipher handle. + */ +Sec_Result SecCipher_UpdateIV(Sec_CipherHandle* cipherHandle, SEC_BYTE* iv) { + CHECK_HANDLE(cipherHandle) + + // Update IV unimplemented for RSA and EC keys. + Sec_KeyType key_type = SecKey_GetKeyType(cipherHandle->keyHandle); + sa_status status; + if (key_type == SEC_KEYTYPE_AES_128 || key_type == SEC_KEYTYPE_AES_256) { + status = sa_crypto_cipher_update_iv(cipherHandle->cipher.context, iv, SEC_AES_BLOCK_SIZE); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; + } + + return SEC_RESULT_UNIMPLEMENTED_FEATURE; +} + +/** + * @brief En/De-cipher specified input data into and output buffer. + * + * @param cipherHandle cipher handle. + * @param input pointer to input data. + * @param inputSize the length of input data in bytes. + * @param lastInput SEC_BOOLean value specifying whether this is the last chunk + * of input that will be processed. + * @param output pointer to output data buffer. + * @param outputSize the size of the output buffer. + * @param bytesWritten pointer to a value that will be set to number + * of bytes written to the output buffer. + * + * @return The status of the operation. + */ +Sec_Result SecCipher_Process(Sec_CipherHandle* cipherHandle, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BOOL lastInput, + SEC_BYTE* output, SEC_SIZE outputSize, SEC_SIZE* bytesWritten) { + CHECK_HANDLE(cipherHandle) + SEC_SIZE output_size_needed = 0; + + if (cipherHandle->svp_required) { + SEC_LOG_ERROR("An opaque buffer must be used for cipher processing when SVP is required."); + return SEC_RESULT_FAILURE; + } + + if (cipherHandle->last != 0) { + SEC_LOG_ERROR("Last block has already been processed"); + return SEC_RESULT_FAILURE; + } + + cipherHandle->last = lastInput; + if (SecCipher_GetRequiredOutputSize(cipherHandle->algorithm, cipherHandle->mode, + SecKey_GetKeyType(cipherHandle->keyHandle), inputSize, &output_size_needed, lastInput) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_GetRequiredOutputSize failed"); + return SEC_RESULT_FAILURE; + } + + if (output == NULL) { + *bytesWritten = output_size_needed; + return SEC_RESULT_SUCCESS; + } + + if (output_size_needed > outputSize) { + SEC_LOG_ERROR("Output buffer is too small"); + return SEC_RESULT_FAILURE; + } + + size_t output_count = outputSize; + Sec_KeyType key_type = SecKey_GetKeyType(cipherHandle->keyHandle); + switch (key_type) { + case SEC_KEYTYPE_AES_128: + case SEC_KEYTYPE_AES_256: + case SEC_KEYTYPE_RSA_1024: + case SEC_KEYTYPE_RSA_2048: + case SEC_KEYTYPE_HMAC_128: + case SEC_KEYTYPE_HMAC_160: + case SEC_KEYTYPE_HMAC_256: + case SEC_KEYTYPE_ECC_NISTP256: + case SEC_KEYTYPE_RSA_3072: { + sa_buffer out_buffer; + out_buffer.buffer_type = SA_BUFFER_TYPE_CLEAR; + out_buffer.context.clear.buffer = output; + out_buffer.context.clear.length = outputSize; + out_buffer.context.clear.offset = 0; + + sa_buffer in_buffer; + in_buffer.buffer_type = SA_BUFFER_TYPE_CLEAR; + in_buffer.context.clear.buffer = input; + in_buffer.context.clear.length = inputSize; + in_buffer.context.clear.offset = 0; + + bool pkcs7 = cipherHandle->algorithm == SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING || + cipherHandle->algorithm == SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING; + if (lastInput && pkcs7) { + *bytesWritten = 0; + + // In encrypt mode, if the last block is complete add a padding block. In decrypt mode, + // the last block is the padding block. + size_t bytes_to_process = cipherHandle->mode == SEC_CIPHERMODE_DECRYPT ? + ((inputSize / SEC_AES_BLOCK_SIZE) - 1) * SEC_AES_BLOCK_SIZE : + (inputSize / SEC_AES_BLOCK_SIZE) * SEC_AES_BLOCK_SIZE; + size_t bytes_left = inputSize - bytes_to_process; + sa_status status = sa_crypto_cipher_process(&out_buffer, cipherHandle->cipher.context, &in_buffer, + &bytes_to_process); + CHECK_STATUS(status) + *bytesWritten += bytes_to_process; + + status = sa_crypto_cipher_process_last(&out_buffer, cipherHandle->cipher.context, &in_buffer, + &bytes_left, NULL); + CHECK_STATUS(status) + *bytesWritten += bytes_left; + } else { + size_t bytes_to_process = inputSize; + sa_status status = sa_crypto_cipher_process(&out_buffer, cipherHandle->cipher.context, &in_buffer, + &bytes_to_process); + CHECK_STATUS(status) + *bytesWritten = bytes_to_process; + } + + break; + } + case SEC_KEYTYPE_RSA_1024_PUBLIC: + case SEC_KEYTYPE_RSA_2048_PUBLIC: + case SEC_KEYTYPE_RSA_3072_PUBLIC: + if (cipherHandle->algorithm == SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING) { + if (rsa_encrypt_pkcs1v15(output, &output_count, cipherHandle->cipher.rsa, input, inputSize) == + SEC_FALSE) + return SEC_RESULT_FAILURE; + } else if (cipherHandle->algorithm == SEC_CIPHERALGORITHM_RSA_OAEP_PADDING) { + if (rsa_encrypt_oaep(output, &output_count, cipherHandle->cipher.rsa, input, inputSize) == SEC_FALSE) + return SEC_RESULT_FAILURE; + } else + return SEC_RESULT_FAILURE; + + *bytesWritten = output_count; + break; + + case SEC_KEYTYPE_ECC_NISTP256_PUBLIC: + *bytesWritten = SecUtils_ElGamal_Encrypt(cipherHandle->cipher.ec, input, inputSize, output, outputSize); + if (*bytesWritten == -1) { + *bytesWritten = 0; + return SEC_RESULT_FAILURE; + } + + break; + + default: + SEC_LOG_ERROR("unsupported key type %u", key_type); + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief En/De-cipher specified fragmented input data into and output buffer. + * + * @param cipherHandle cipher handle. + * @param input pointer to input data. + * @param inputSize the length of input data in bytes. + * @param lastInput SEC_BOOLean value specifying whether this is the last chunk + * of input that will be processed. + * @param output pointer to output data buffer. + * @param outputSize the size of the output buffer. + * @param bytesWritten pointer to a value that will be set to number + * of bytes written to the output buffer. + * @param fragmentOffset offset in bytes of the fragment data within larger packet. + * @param fragmentSize length in bytes of the data fragment. + * @param fragmentPeriod the length in bytes of the packet containing the fragment. + * + * @return The status of the operation. + */ +Sec_Result SecCipher_ProcessFragmented(Sec_CipherHandle* cipherHandle, SEC_BYTE* input, SEC_SIZE inputSize, + SEC_BOOL lastInput, SEC_BYTE* output, SEC_SIZE outputSize, SEC_SIZE* bytesWritten, SEC_SIZE fragmentOffset, + SEC_SIZE fragmentSize, SEC_SIZE fragmentPeriod) { + SEC_SIZE lbw; + SEC_SIZE output_size_required = 0; + Sec_Result result = SEC_RESULT_FAILURE; + + CHECK_HANDLE(cipherHandle) + + *bytesWritten = 0; + + if (SecCipher_GetRequiredOutputSizeFragmented(cipherHandle->algorithm, cipherHandle->mode, + SecKey_GetKeyType(cipherHandle->keyHandle), inputSize, &output_size_required, lastInput, + fragmentOffset, fragmentSize, fragmentPeriod) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_GetRequiredOutputSizeFragmented failed"); + return result; + } + + if (output == NULL) { + *bytesWritten = output_size_required; + result = SEC_RESULT_SUCCESS; + return result; + } + + if (output_size_required > outputSize) { + SEC_LOG_ERROR("Output buffer is too small"); + result = SEC_RESULT_INVALID_INPUT_SIZE; + return result; + } + + switch (cipherHandle->algorithm) { + case SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING: + case SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING: + case SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING: + case SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING: + case SEC_CIPHERALGORITHM_AES_CTR: + if (input != output) { + memcpy(output, input, inputSize); + } + + *bytesWritten = inputSize; + while (inputSize > 0) { + if (SecCipher_Process(cipherHandle, output + fragmentOffset, fragmentSize, + lastInput && (inputSize == fragmentPeriod), output + fragmentOffset, fragmentSize, + &lbw) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_Process failed"); + return result; + } + + output += fragmentPeriod; + inputSize -= fragmentPeriod; + } + + break; + + default: + SEC_LOG_ERROR("Unimplemented cipher algorithm"); + return result; + } + + result = SEC_RESULT_SUCCESS; + return result; +} + +/** + * @brief Process the opaque buffers that were obtained with Sec_OpaqueBufferMalloc. + * + * @param cipherHandle cipher handle. + * @param inputHandle opaque buffer containing input. + * @param outputHandle opaque buffer for writing output. + * @param inputSize the length of input to process. + * @param lastInput SEC_BOOLean value specifying whether this is the last chunk + * of input that will be processed. + * @param bytesWritten pointer to a value that will be set to number. + * of bytes written to the output buffer. + */ +Sec_Result SecCipher_ProcessOpaque(Sec_CipherHandle* cipherHandle, Sec_OpaqueBufferHandle* inOpaqueBufferHandle, + Sec_OpaqueBufferHandle* outOpaqueBufferHandle, SEC_SIZE inputSize, SEC_BOOL lastInput, SEC_SIZE* bytesWritten) { + CHECK_HANDLE(cipherHandle) + if (inOpaqueBufferHandle == NULL) { + SEC_LOG_ERROR("Invalid inputHandle"); + return SEC_RESULT_INVALID_HANDLE; + } + + if (outOpaqueBufferHandle == NULL) { + SEC_LOG_ERROR("Invalid outputHandle"); + return SEC_RESULT_INVALID_HANDLE; + } + + SEC_SIZE output_size_needed = 0; + + if (cipherHandle->last != 0) { + SEC_LOG_ERROR("Last block has already been processed"); + return SEC_RESULT_FAILURE; + } + + cipherHandle->last = lastInput; + if (SecCipher_GetRequiredOutputSize(cipherHandle->algorithm, cipherHandle->mode, + SecKey_GetKeyType(cipherHandle->keyHandle), inputSize, &output_size_needed, + lastInput) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_GetRequiredOutputSize failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType key_type = SecKey_GetKeyType(cipherHandle->keyHandle); + switch (key_type) { + case SEC_KEYTYPE_AES_128: + case SEC_KEYTYPE_AES_256: { + sa_buffer out_buffer; + out_buffer.buffer_type = SA_BUFFER_TYPE_SVP; + out_buffer.context.svp.buffer = outOpaqueBufferHandle->svp_buffer; + out_buffer.context.svp.offset = 0; + + sa_buffer in_buffer; + in_buffer.buffer_type = SA_BUFFER_TYPE_SVP; + in_buffer.context.svp.buffer = inOpaqueBufferHandle->svp_buffer; + in_buffer.context.svp.offset = 0; + + size_t bytes_to_process = inputSize; + bool pkcs7 = cipherHandle->algorithm == SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING || + cipherHandle->algorithm == SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING; + if (lastInput && pkcs7) { + sa_status status = sa_crypto_cipher_process_last(&out_buffer, cipherHandle->cipher.context, &in_buffer, + &bytes_to_process, NULL); + CHECK_STATUS(status) + } else { + sa_status status = sa_crypto_cipher_process(&out_buffer, cipherHandle->cipher.context, &in_buffer, + &bytes_to_process); + CHECK_STATUS(status) + } + + *bytesWritten = bytes_to_process; + break; + } + default: + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecCipher_ProcessCtrWithOpaqueDataShift(Sec_CipherHandle* cipherHandle, + Sec_OpaqueBufferHandle* inOpaqueBufferHandle, Sec_OpaqueBufferHandle* outOpaqueBufferHandle, SEC_SIZE inputSize, + SEC_SIZE* bytesWritten, SEC_SIZE dataShift) { + // Original implementation never worked right and function has not been implemented by any vendor. + return SEC_RESULT_UNIMPLEMENTED_FEATURE; +} + +/** + * @brief Perform cipher operation on the opaque input handle and check the output against the expected value. + * + * @param cipherHandle pointer to Sec_CipherHandle. + * @param void inputHandle pointer to opaque buffer containing input. + * @param SEC_SIZE checkLength number of bytes used for comparison. + * @param SEC_BYTE expected expected value used for comparison. + */ +Sec_Result SecCipher_KeyCheckOpaque(Sec_CipherHandle* cipherHandle, Sec_OpaqueBufferHandle* opaqueBufferHandle, + SEC_SIZE checkLength, SEC_BYTE* expected) { + if (opaqueBufferHandle == NULL) { + SEC_LOG_ERROR("Null inputHandle"); + return SEC_RESULT_FAILURE; + } + + if (checkLength < 8 || checkLength > SEC_AES_BLOCK_SIZE) { + SEC_LOG_ERROR("Length must be >=8 and <=16"); + return SEC_RESULT_FAILURE; + } + + if (cipherHandle == NULL) { + SEC_LOG_ERROR("Null cipherHandle"); + return SEC_RESULT_FAILURE; + } + + sa_status status; + sa_buffer in_buffer; + in_buffer.buffer_type = SA_BUFFER_TYPE_SVP; + in_buffer.context.svp.buffer = opaqueBufferHandle->svp_buffer; + in_buffer.context.svp.offset = 0; + if (cipherHandle->mode == SEC_CIPHERMODE_ENCRYPT) + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + + const Sec_Key* key = get_key(cipherHandle->keyHandle); + status = sa_svp_key_check(key->handle, &in_buffer, SEC_AES_BLOCK_SIZE, expected, SEC_AES_BLOCK_SIZE); + + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Release the cipher object. + * + * @param cipherHandle cipher handle. + * + * @return The status of the operation. + */ +Sec_Result SecCipher_Release(Sec_CipherHandle* cipherHandle) { + CHECK_HANDLE(cipherHandle) + + Sec_KeyType key_type = SecKey_GetKeyType(cipherHandle->keyHandle); + switch (key_type) { + case SEC_KEYTYPE_AES_128: + case SEC_KEYTYPE_AES_256: + case SEC_KEYTYPE_RSA_1024: + case SEC_KEYTYPE_RSA_2048: + case SEC_KEYTYPE_HMAC_128: + case SEC_KEYTYPE_HMAC_160: + case SEC_KEYTYPE_HMAC_256: + case SEC_KEYTYPE_ECC_NISTP256: + case SEC_KEYTYPE_RSA_3072: + sa_crypto_cipher_release(cipherHandle->cipher.context); + break; + + default: + break; + } + + SEC_FREE(cipherHandle); + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecCipher_ProcessCtrWithDataShift(Sec_CipherHandle* cipherHandle, SEC_BYTE* input, SEC_SIZE inputSize, + SEC_BYTE* output, SEC_SIZE outputSize, SEC_SIZE* bytesWritten, SEC_SIZE dataShift) { + // Original implementation never worked right and function has not been implemented by any vendor. + return SEC_RESULT_UNIMPLEMENTED_FEATURE; +} + +int SecCipher_IsModeEncrypt(Sec_CipherMode mode) { + return mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM; +} + +int SecCipher_IsModeDecrypt(Sec_CipherMode mode) { + return mode == SEC_CIPHERMODE_DECRYPT || mode == SEC_CIPHERMODE_DECRYPT_NATIVEMEM; +} + +/** + * @brief Check whether the supplied key and iv are valid for the chosen cipher algorithm. + * + * @param key_type key type. + * @param algorithm cipher algorithm. + * @param mode cipher mode. + * @param iv initialization vector. + * + * @return status of the call. + */ +Sec_Result SecCipher_IsValidKey(Sec_KeyType key_type, Sec_CipherAlgorithm alg, Sec_CipherMode mode, + const SEC_BYTE* iv) { + switch (alg) { + case SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING: + case SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING: + case SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING: + case SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING: + case SEC_CIPHERALGORITHM_AES_CTR: + if (SecKey_IsAES(key_type)) { + if (iv == NULL && alg != SEC_CIPHERALGORITHM_AES_CTR && alg != SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING && + alg != SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING) { + SEC_LOG_ERROR("IV cannot be null in CBC and CTR modes."); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; + } else { + SEC_LOG_ERROR("Not an AES key: %d", key_type); + return SEC_RESULT_FAILURE; + } + + case SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING: + case SEC_CIPHERALGORITHM_RSA_OAEP_PADDING: + if (SecCipher_IsModeEncrypt(mode)) { + if (!SecKey_IsRsa(key_type)) { + SEC_LOG_ERROR("Not an RSA key"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; + } else if (SecCipher_IsModeDecrypt(mode)) { + if (!SecKey_IsPrivRsa(key_type)) { + SEC_LOG_ERROR("Not an RSA key"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; + } else { + SEC_LOG_ERROR("Unknown cipher mode encountered: %d", mode); + return SEC_RESULT_FAILURE; + } + + case SEC_CIPHERALGORITHM_ECC_ELGAMAL: + if (SecCipher_IsModeEncrypt(mode)) { + if (!SecKey_IsEcc(key_type)) { + SEC_LOG_ERROR("Not an ECC key"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; + } else if (SecCipher_IsModeDecrypt(mode)) { + if (!SecKey_IsPrivEcc(key_type)) { + SEC_LOG_ERROR("Not an ECC key"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; + } else { + SEC_LOG_ERROR("Unknown cipher mode encountered: %d", mode); + return SEC_RESULT_FAILURE; + } + + default: + break; + } + + SEC_LOG_ERROR("Unimplemented algorithm: %d", alg); + return SEC_RESULT_UNIMPLEMENTED_FEATURE; +} + +SEC_BOOL SecCipher_IsCBC(Sec_CipherAlgorithm alg) { + return alg == SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING || alg == SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING; +} + +/** + * @brief get the required output buffer size for the specified combination of input parameters. + * + * Write required output buffer size for cipher configuration. + * Returns SEC_RESULT_SUCCESS if the cipher configuration parameters are valid. + * Returns SEC_RESULT_FAILURE otherwise (e.g. input size is not valid). + * + * @param algorithm cipher algorithm. + * @param mode cipher mode. + * @param keyType key type. + * @param inputSize size of the input buffer. + * @param outputSize size of the output buffer. + * @param lastInput is this the last input to the cipher. + * + * @return status of the call. + */ +Sec_Result SecCipher_GetRequiredOutputSize(Sec_CipherAlgorithm alg, Sec_CipherMode mode, Sec_KeyType keyType, + SEC_SIZE inputSize, SEC_SIZE* outputSize, SEC_BOOL lastInput) { + SEC_SIZE max_clear_size; + SEC_SIZE rsa_block_size; + SEC_SIZE bn_size; + *outputSize = 0; + + switch (alg) { + case SEC_CIPHERALGORITHM_AES_CTR: + *outputSize = inputSize; + break; + + case SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING: + case SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING: + if (inputSize % SEC_AES_BLOCK_SIZE != 0) { + SEC_LOG_ERROR("Input size is not a multiple of block size"); + return SEC_RESULT_INVALID_INPUT_SIZE; + } + + *outputSize = inputSize; + break; + + case SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING: + case SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING: + if (SecCipher_IsModeEncrypt(mode) && !lastInput && inputSize % SEC_AES_BLOCK_SIZE != 0) { + SEC_LOG_ERROR("Encryption input size is not a multiple of block size and is not last input"); + return SEC_RESULT_INVALID_INPUT_SIZE; + } + + if (SecCipher_IsModeDecrypt(mode) && inputSize % SEC_AES_BLOCK_SIZE != 0) { + SEC_LOG_ERROR("Decryption input size is not a multiple of block size"); + return SEC_RESULT_INVALID_INPUT_SIZE; + } + + *outputSize = (inputSize / 16) * 16 + ((lastInput && (SecCipher_IsModeEncrypt(mode))) ? 16 : 0); + break; + + case SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING: + case SEC_CIPHERALGORITHM_RSA_OAEP_PADDING: + rsa_block_size = *outputSize = SecKey_GetKeyLenForKeyType(keyType); + + if (alg == SEC_CIPHERALGORITHM_RSA_OAEP_PADDING) { + max_clear_size = rsa_block_size - RSA_OAEP_PADDING_SIZE; + } else { + max_clear_size = rsa_block_size - RSA_PKCS1_PADDING_SIZE; + } + + if (SecCipher_IsModeDecrypt(mode) && inputSize != rsa_block_size) { + SEC_LOG_ERROR("Decrypt input size %u is not equal to the RSA block size", inputSize); + return SEC_RESULT_INVALID_INPUT_SIZE; + } + + if ((SecCipher_IsModeEncrypt(mode)) && inputSize > max_clear_size) { + SEC_LOG_ERROR("Encrypt input size is too large"); + return SEC_RESULT_INVALID_INPUT_SIZE; + } + + break; + + case SEC_CIPHERALGORITHM_ECC_ELGAMAL: + bn_size = SecKey_GetKeyLenForKeyType(keyType); // one bignum + if (SecCipher_IsModeEncrypt(mode)) { + if (inputSize != bn_size) { // one bignum + SEC_LOG_ERROR("Input size invalid for El Gamal encryption"); + return SEC_RESULT_INVALID_INPUT_SIZE; + } + + *outputSize = 4 * bn_size; // two points, which are four bignums + } else { + if (inputSize != 4 * bn_size) { // two points, which are four bignums + SEC_LOG_ERROR("Input size invalid for El Gamal encryption"); + return SEC_RESULT_INVALID_INPUT_SIZE; + } + + *outputSize = bn_size; // one bignum + } + + break; + + default: + SEC_LOG_ERROR("Unimplemented cipher algorithm"); + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief get the required output buffer length for fragemnted encryption/decryption. + * + * @param algorithm cipher algorithm. + * @param mode cipher mode. + * @param keyType key type. + * @param inputSize size of the input buffer. + * @param outputSize size of the output buffer. + * @param lastInput is this the last input to the cipher. + * @param framentOffset offset in bytes of the fragment data within larger packet. + * @param fragmentSize length in bytes of the data fragment. + * @param fragmentPeriod the length in bytes of the packet containing the fragment. + * + * @return status of the call. + */ +Sec_Result SecCipher_GetRequiredOutputSizeFragmented(Sec_CipherAlgorithm alg, Sec_CipherMode mode, Sec_KeyType keyType, + SEC_SIZE inputSize, SEC_SIZE* outputSizeNeeded, SEC_BOOL lastInput, SEC_SIZE fragmentOffset, + SEC_SIZE fragmentSize, SEC_SIZE fragmentPeriod) { + *outputSizeNeeded = 0; + + if ((inputSize % fragmentPeriod) != 0) { + SEC_LOG_ERROR("Input size is not a multiple of a fragment period"); + return SEC_RESULT_INVALID_INPUT_SIZE; + } + + if ((fragmentSize % SEC_AES_BLOCK_SIZE) != 0) { + SEC_LOG_ERROR("Fragment size is not a multiple of block size"); + return SEC_RESULT_INVALID_INPUT_SIZE; + } + + if ((fragmentOffset + fragmentSize) > fragmentPeriod) { + SEC_LOG_ERROR("Invalid fragment parameters"); + return SEC_RESULT_INVALID_INPUT_SIZE; + } + + switch (alg) { + case SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING: + case SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING: + case SEC_CIPHERALGORITHM_AES_CTR: + case SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING: + case SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING: + *outputSizeNeeded = inputSize; + break; + + default: + SEC_LOG_ERROR("Unimplemented cipher algorithm"); + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Apply PKCS7 padding to the AES input block. + * + * @param inputBlock input data to pad. + * @param inputSize size of input data. + * @param outputBlock Output block. Has to be the size of SEC_AES_BLOCKSIZE. + */ +void SecCipher_PadAESPKCS7Block(SEC_BYTE* inputBlock, SEC_SIZE inputSize, SEC_BYTE* outputBlock) { + SEC_BYTE pad_val = (SEC_BYTE) (SEC_AES_BLOCK_SIZE - inputSize % SEC_AES_BLOCK_SIZE); + memset(outputBlock, pad_val, SEC_AES_BLOCK_SIZE); + memcpy(outputBlock, inputBlock, inputSize % SEC_AES_BLOCK_SIZE); +} + +/** + * @brief Checks whether the specified cipher algorithm is AES. + */ +SEC_BOOL SecCipher_IsAES(Sec_CipherAlgorithm alg) { + return alg == SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING || alg == SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING || + alg == SEC_CIPHERALGORITHM_AES_CTR || alg == SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING || + alg == SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING; +} + +/** + * @brief Checks whether the specified cipher algorithm is RSA. + */ +SEC_BOOL SecCipher_IsRsa(Sec_CipherAlgorithm alg) { + return alg == SEC_CIPHERALGORITHM_RSA_OAEP_PADDING || alg == SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING; +} + +/** + * @brief Checks whether the specified cipher algorithm is ECC. + */ +SEC_BOOL SecCipher_IsEcc(Sec_CipherAlgorithm alg) { + return alg == SEC_CIPHERALGORITHM_ECC_ELGAMAL; +} + +Sec_Result SecCipher_SingleInput(Sec_ProcessorHandle* processorHandle, Sec_CipherAlgorithm alg, Sec_CipherMode mode, + Sec_KeyHandle* keyHandle, SEC_BYTE* iv, SEC_BYTE* input, SEC_SIZE input_len, SEC_BYTE* output, + SEC_SIZE output_len, SEC_SIZE* written) { + Sec_Result result; + Sec_CipherHandle* cipherHandle = NULL; + + result = SecCipher_GetInstance(processorHandle, alg, mode, keyHandle, iv, &cipherHandle); + if (result != SEC_RESULT_SUCCESS) + return result; + + result = SecCipher_Process(cipherHandle, input, input_len, 1, output, output_len, written); + SecCipher_Release(cipherHandle); + return result; +} + +Sec_Result SecCipher_SingleInputId(Sec_ProcessorHandle* processorHandle, Sec_CipherAlgorithm alg, Sec_CipherMode mode, + SEC_OBJECTID key, SEC_BYTE* iv, SEC_BYTE* input, SEC_SIZE input_len, SEC_BYTE* output, SEC_SIZE output_len, + SEC_SIZE* written) { + Sec_Result result = SEC_RESULT_FAILURE; + Sec_KeyHandle* keyHandle = NULL; + + if (SecKey_GetInstance(processorHandle, key, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + if (keyHandle != NULL) + SecKey_Release(keyHandle); + + return result; + } + + if (SecCipher_SingleInput(processorHandle, alg, mode, keyHandle, iv, input, input_len, output, output_len, + written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_SingleInput failed"); + if (keyHandle != NULL) + SecKey_Release(keyHandle); + + return result; + } + + if (keyHandle != NULL) + SecKey_Release(keyHandle); + + return SEC_RESULT_SUCCESS; +} + +SEC_BOOL SecCipher_IsPKCS7Padded(Sec_CipherAlgorithm alg) { + return alg == SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING || alg == SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING; +} + +SEC_BOOL SecCipher_IsDecrypt(Sec_CipherMode mode) { + return mode == SEC_CIPHERMODE_DECRYPT || mode == SEC_CIPHERMODE_DECRYPT_NATIVEMEM; +} + +Sec_Result SecCipher_ProcessOpaqueWithMap(Sec_CipherHandle* cipherHandle, SEC_BYTE* iv, SEC_BYTE* input, + SEC_SIZE inputSize, SEC_BOOL lastInput, SEC_MAP* map, SEC_SIZE mapLength, Sec_OpaqueBufferHandle** outputHandle, + SEC_SIZE* bytesWritten) { + + if (cipherHandle == NULL) { + SEC_LOG_ERROR("NULL cipherHandle"); + return SEC_RESULT_FAILURE; + } + + if (iv == NULL) { + SEC_LOG_ERROR("NULL iv"); + return SEC_RESULT_FAILURE; + } + + if (input == NULL) { + SEC_LOG_ERROR("NULL input"); + return SEC_RESULT_FAILURE; + } + + if (map == NULL) { + SEC_LOG_ERROR("NULL map"); + return SEC_RESULT_FAILURE; + } + + if (outputHandle == NULL) { + SEC_LOG_ERROR("NULL outputHandle"); + return SEC_RESULT_FAILURE; + } + + if (bytesWritten == NULL) { + SEC_LOG_ERROR("NULL bytesWritten"); + return SEC_RESULT_FAILURE; + } + + Sec_Result result = SecOpaqueBuffer_Malloc(inputSize, outputHandle); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecOpaqueBuffer_Malloc failed"); + return SEC_RESULT_FAILURE; + } + + sa_subsample_length* subsample_lengths = malloc(mapLength * sizeof(sa_subsample_length)); + for (size_t i = 0; i < mapLength; i++) { + subsample_lengths[i].bytes_of_clear_data = map[i].clear; + subsample_lengths[i].bytes_of_protected_data = map[i].encrypted; + } + + sa_buffer out_buffer; + out_buffer.buffer_type = SA_BUFFER_TYPE_SVP; + out_buffer.context.svp.buffer = (*outputHandle)->svp_buffer; + out_buffer.context.svp.offset = 0; + + sa_buffer in_buffer; + in_buffer.buffer_type = SA_BUFFER_TYPE_CLEAR; + in_buffer.context.clear.buffer = input; + in_buffer.context.clear.length = inputSize; + in_buffer.context.clear.offset = 0; + + sa_sample sample; + sample.iv = iv; + sample.iv_length = SEC_AES_BLOCK_SIZE; + sample.crypt_byte_block = 0; + sample.skip_byte_block = 0; + sample.subsample_count = mapLength; + sample.subsample_lengths = subsample_lengths; + sample.context = cipherHandle->cipher.context; + sample.out = &out_buffer; + sample.in = &in_buffer; + + sa_status status = sa_process_common_encryption(1, &sample); + free(subsample_lengths); + if (status != SA_STATUS_OK) { + SecOpaqueBuffer_Free(*outputHandle); + *outputHandle = NULL; + *bytesWritten = 0; + CHECK_STATUS(status) + } + + *bytesWritten = out_buffer.context.svp.offset; + return SEC_RESULT_SUCCESS; +} + +Sec_Result get_cipher_algorithm(const Sec_CipherAlgorithm algorithm, SEC_BOOL is_unwrap, + sa_cipher_algorithm* cipher_algorithm, void** parameters, void* iv, SEC_SIZE key_length, SEC_SIZE key_offset) { + *parameters = NULL; + switch (algorithm) { + case SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING: + *cipher_algorithm = SA_CIPHER_ALGORITHM_AES_ECB; + return SEC_RESULT_SUCCESS; + + case SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING: + *cipher_algorithm = SA_CIPHER_ALGORITHM_AES_ECB_PKCS7; + return SEC_RESULT_SUCCESS; + + case SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING: + *cipher_algorithm = SA_CIPHER_ALGORITHM_AES_CBC; + if (is_unwrap) { + *parameters = calloc(1, sizeof(sa_unwrap_parameters_aes_cbc)); + if (parameters == NULL) + return SEC_RESULT_FAILURE; + + ((sa_unwrap_parameters_aes_cbc*) *parameters)->iv = iv; + ((sa_unwrap_parameters_aes_cbc*) *parameters)->iv_length = SEC_AES_BLOCK_SIZE; + } else { + *parameters = calloc(1, sizeof(sa_cipher_parameters_aes_cbc)); + if (parameters == NULL) + return SEC_RESULT_FAILURE; + + ((sa_cipher_parameters_aes_cbc*) *parameters)->iv = iv; + ((sa_cipher_parameters_aes_cbc*) *parameters)->iv_length = SEC_AES_BLOCK_SIZE; + } + + return SEC_RESULT_SUCCESS; + + case SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING: + *cipher_algorithm = SA_CIPHER_ALGORITHM_AES_CBC_PKCS7; + if (is_unwrap) { + *parameters = calloc(1, sizeof(sa_unwrap_parameters_aes_cbc)); + if (parameters == NULL) + return SEC_RESULT_FAILURE; + + ((sa_unwrap_parameters_aes_cbc*) *parameters)->iv = iv; + ((sa_unwrap_parameters_aes_cbc*) *parameters)->iv_length = SEC_AES_BLOCK_SIZE; + } else { + *parameters = calloc(1, sizeof(sa_cipher_parameters_aes_cbc)); + if (parameters == NULL) + return SEC_RESULT_FAILURE; + + ((sa_cipher_parameters_aes_cbc*) *parameters)->iv = iv; + ((sa_cipher_parameters_aes_cbc*) *parameters)->iv_length = SEC_AES_BLOCK_SIZE; + } + + return SEC_RESULT_SUCCESS; + + case SEC_CIPHERALGORITHM_AES_CTR: + *cipher_algorithm = SA_CIPHER_ALGORITHM_AES_CTR; + if (is_unwrap) { + *parameters = calloc(1, sizeof(sa_unwrap_parameters_aes_ctr)); + if (parameters == NULL) + return SEC_RESULT_FAILURE; + + ((sa_unwrap_parameters_aes_ctr*) *parameters)->ctr = iv; + ((sa_unwrap_parameters_aes_ctr*) *parameters)->ctr_length = SEC_AES_BLOCK_SIZE; + } else { + *parameters = calloc(1, sizeof(sa_cipher_parameters_aes_ctr)); + if (parameters == NULL) + return SEC_RESULT_FAILURE; + + ((sa_cipher_parameters_aes_ctr*) *parameters)->ctr = iv; + ((sa_cipher_parameters_aes_ctr*) *parameters)->ctr_length = SEC_AES_BLOCK_SIZE; + } + + return SEC_RESULT_SUCCESS; + + case SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING: + *cipher_algorithm = SA_CIPHER_ALGORITHM_RSA_PKCS1V15; + return SEC_RESULT_SUCCESS; + + case SEC_CIPHERALGORITHM_RSA_OAEP_PADDING: + *cipher_algorithm = SA_CIPHER_ALGORITHM_RSA_OAEP; + return SEC_RESULT_SUCCESS; + + case SEC_CIPHERALGORITHM_ECC_ELGAMAL: + *cipher_algorithm = SA_CIPHER_ALGORITHM_EC_ELGAMAL; + if (is_unwrap) { + *parameters = calloc(1, sizeof(sa_unwrap_parameters_ec_elgamal)); + if (parameters == NULL) + return SEC_RESULT_FAILURE; + + ((sa_unwrap_parameters_ec_elgamal*) *parameters)->key_length = key_length; + ((sa_unwrap_parameters_ec_elgamal*) *parameters)->offset = key_offset; + } + return SEC_RESULT_SUCCESS; + + default: + return SEC_RESULT_INVALID_PARAMETERS; + } +} + +static sa_cipher_mode get_cipher_mode(Sec_CipherMode mode) { + switch (mode) { + case SEC_CIPHERMODE_ENCRYPT: + case SEC_CIPHERMODE_ENCRYPT_NATIVEMEM: + return SA_CIPHER_MODE_ENCRYPT; + + case SEC_CIPHERMODE_DECRYPT: + case SEC_CIPHERMODE_DECRYPT_NATIVEMEM: + return SA_CIPHER_MODE_DECRYPT; + + default: + return SA_CIPHER_MODE_ENCRYPT; + } +} + +static SEC_BOOL rsa_encrypt_pkcs1v15(void* out, size_t* out_length, const RSA* rsa, const void* in, size_t in_length) { + if (!out) { + SEC_LOG_ERROR("NULL out"); + return SEC_FALSE; + } + + if (!out_length) { + SEC_LOG_ERROR("NULL out_length"); + return SEC_FALSE; + } + + if (!rsa) { + SEC_LOG_ERROR("NULL rsa"); + return SEC_FALSE; + } + + if (!in) { + SEC_LOG_ERROR("NULL in"); + return SEC_FALSE; + } + + if (*out_length < (size_t) RSA_size(rsa)) { + SEC_LOG_ERROR("Bad out_length"); + return SEC_FALSE; + } + + if (in_length >= (size_t) RSA_size(rsa) - RSA_PKCS1_PADDING_SIZE) { + SEC_LOG_ERROR("Bad in_length"); + return SEC_FALSE; + } + + int length = RSA_public_encrypt((int) in_length, in, out, (RSA*) rsa, RSA_PKCS1_PADDING); + if (length < 0) { + SEC_LOG_ERROR("RSA_public_encrypt failed"); + return SEC_FALSE; + } + *out_length = length; + + return SEC_TRUE; +} + +static SEC_BOOL rsa_encrypt_oaep(void* out, size_t* out_length, const RSA* rsa, const void* in, size_t in_length) { + if (!out) { + SEC_LOG_ERROR("NULL out"); + return SEC_FALSE; + } + + if (!out_length) { + SEC_LOG_ERROR("NULL out_length"); + return SEC_FALSE; + } + + if (!rsa) { + SEC_LOG_ERROR("NULL rsa"); + return SEC_FALSE; + } + + if (!in) { + SEC_LOG_ERROR("NULL in"); + return SEC_FALSE; + } + + if (*out_length < (size_t) RSA_size(rsa)) { + SEC_LOG_ERROR("Bad out_length"); + return SEC_FALSE; + } + + if (in_length >= (size_t) RSA_size(rsa) - RSA_OAEP_PADDING_SIZE) { + SEC_LOG_ERROR("Bad in_length"); + return SEC_FALSE; + } + + int length = RSA_public_encrypt((int) in_length, in, out, (RSA*) rsa, RSA_PKCS1_OAEP_PADDING); + if (length < 0) { + SEC_LOG_ERROR("RSA_private_decrypt failed"); + return SEC_FALSE; + } + + *out_length = length; + return SEC_TRUE; +} + +static SEC_BOOL is_svp_required(Sec_KeyProperties* props) { + for (int i = 0; i < SEC_KEYOUTPUTRIGHT_NUM; ++i) { + if (props->rights[i] == SEC_KEYOUTPUTRIGHT_SVP_REQUIRED) { + return SEC_TRUE; + } + } + + return SEC_FALSE; +} diff --git a/src/sec_adapter_cipher.h b/src/sec_adapter_cipher.h new file mode 100644 index 0000000..e041fd3 --- /dev/null +++ b/src/sec_adapter_cipher.h @@ -0,0 +1,33 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_ADAPTER_CIPHER_H +#define SEC_ADAPTER_CIPHER_H + +#include "sa_types.h" +#include "sec_adapter_processor.h" +#include "sec_adapter_svp.h" +#include "sec_adapter_utils.h" +#include "sec_security.h" +#include +#include + +Sec_Result get_cipher_algorithm(Sec_CipherAlgorithm algorithm, SEC_BOOL is_unwrap, + sa_cipher_algorithm* cipher_algorithm, void** parameters, void* iv, SEC_SIZE key_length, SEC_SIZE key_offset); + +#endif // SEC_ADAPTER_CIPHER_H diff --git a/src/sec_adapter_common.c b/src/sec_adapter_common.c new file mode 100644 index 0000000..be65977 --- /dev/null +++ b/src/sec_adapter_common.c @@ -0,0 +1,169 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_adapter_utils.h" +#include "sec_security.h" + +int Sec_Memcmp(const void* ptr1, const void* ptr2, const size_t num) { + size_t i; + SEC_BYTE result = 0; + SEC_BYTE* a = (SEC_BYTE*) ptr1; + SEC_BYTE* b = (SEC_BYTE*) ptr2; + + for (i = 0; i < num; ++i) + result |= a[i] ^ b[i]; + + return result; +} + +void* Sec_Memset(void* ptr, int value, size_t num) { + volatile SEC_BYTE* p = (SEC_BYTE*) ptr; + while (num--) + *p++ = value; + return ptr; +} + +Sec_Endianess Sec_GetEndianess(void) { + uint32_t u32Val = 0x03020100; + uint8_t* u8ptr = (uint8_t*) &u32Val; + + if (u8ptr[0] == 0x03 && u8ptr[1] == 0x02 && u8ptr[2] == 0x01 && u8ptr[3] == 0x00) + return SEC_ENDIANESS_BIG; + + if (u8ptr[0] == 0x00 && u8ptr[1] == 0x01 && u8ptr[2] == 0x02 && u8ptr[3] == 0x03) + return SEC_ENDIANESS_LITTLE; + + return SEC_ENDIANESS_UNKNOWN; +} + +uint32_t Sec_BEBytesToUint32(SEC_BYTE* bytes) { + uint32_t val; + + memcpy(&val, bytes, 4); + + switch (Sec_GetEndianess()) { + case SEC_ENDIANESS_BIG: + return val; + + case SEC_ENDIANESS_LITTLE: + return Sec_EndianSwap_uint32(val); + + default: + break; + } + + SEC_LOG_ERROR("Unknown endianess detected"); + return 0; +} + +uint64_t Sec_BEBytesToUint64(SEC_BYTE* bytes) { + uint64_t val; + + memcpy(&val, bytes, 8); + + switch (Sec_GetEndianess()) { + case SEC_ENDIANESS_BIG: + return val; + + case SEC_ENDIANESS_LITTLE: + return Sec_EndianSwap_uint64(val); + + default: + break; + } + + SEC_LOG_ERROR("Unknown endianess detected"); + return 0; +} + +void Sec_Uint32ToBEBytes(uint32_t val, SEC_BYTE* bytes) { + if (Sec_GetEndianess() == SEC_ENDIANESS_LITTLE) { + val = Sec_EndianSwap_uint32(val); + } + + memcpy(bytes, &val, 4); +} + +void Sec_Uint64ToBEBytes(uint64_t val, SEC_BYTE* bytes) { + if (Sec_GetEndianess() == SEC_ENDIANESS_LITTLE) { + val = Sec_EndianSwap_uint64(val); + } + + memcpy(bytes, &val, 8); +} + +// Macro that switches endianness of any stdint type. +// Note in and out parameters must be the same stdint type. +#define stdint_EndianSwap(in, out) \ + for (size_t i = 0; i < sizeof(in); i++) { \ + (out) = ((out) << 8) + ((in) >> (i * 8) & 0x0ff); \ + } + +uint16_t Sec_EndianSwap_uint16(uint16_t val) { + uint16_t ret = 0; + stdint_EndianSwap(val, ret) + return ret; +} + +int16_t Sec_EndianSwap_int16(int16_t val) { + int16_t ret = 0; + stdint_EndianSwap(val, ret) + return ret; +} + +uint32_t Sec_EndianSwap_uint32(uint32_t val) { + uint32_t ret = 0; + stdint_EndianSwap(val, ret) + return ret; +} + +int32_t Sec_EndianSwap_int32(int32_t val) { + int32_t ret = 0; + stdint_EndianSwap(val, ret) + return ret; +} + +int64_t Sec_EndianSwap_int64(int64_t val) { + int64_t ret = 0; + stdint_EndianSwap(val, ret) + return ret; +} + +uint64_t Sec_EndianSwap_uint64(uint64_t val) { + uint64_t ret = 0; + stdint_EndianSwap(val, ret) + return ret; +} + +void SecKeyProperties_SetDefault(Sec_KeyProperties* props, Sec_KeyType type) { + memset(props->keyId, 0, sizeof(props->keyId)); + SecUtils_Epoch2IsoTime(0, props->notBefore, 24); + SecUtils_Epoch2IsoTime(UINT32_MAX, props->notOnOrAfter, 24); + + memset(props->rights, SEC_KEYOUTPUTRIGHT_NOT_SET, sizeof(props->rights)); + int i = 0; + props->rights[i++] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_DTCP_ALLOWED; + props->rights[i++] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_1_4_ALLOWED; + props->rights[i++] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_2_2_ALLOWED; + props->rights[i++] = SEC_KEYOUTPUTRIGHT_ANALOG_OUTPUT_ALLOWED; + props->rights[i] = SEC_KEYOUTPUTRIGHT_CGMSA_REQUIRED; + props->keyLength = SecKey_GetKeyLenForKeyType(type); + props->keyType = type; + props->usage = SEC_KEYUSAGE_DATA_KEY; + props->cacheable = 1; +} diff --git a/src/sec_adapter_digest.c b/src/sec_adapter_digest.c new file mode 100644 index 0000000..0f6a35d --- /dev/null +++ b/src/sec_adapter_digest.c @@ -0,0 +1,311 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sa.h" +#include "sec_adapter_processor.h" +#include "sec_security.h" +#include + +struct Sec_DigestHandle_struct { + Sec_ProcessorHandle* processorHandle; + Sec_DigestAlgorithm algorithm; + SHA_CTX sha1_ctx; + SHA256_CTX sha256_ctx; + SEC_BYTE* key_digest; + size_t key_digest_length; + SEC_BOOL in_process; +}; + +/** + * @brief Obtain a digest object handle. + * + * @param processorHandle secure processor handle. + * @param algorithm digest algorithm to use. + * @param digestHandle output digest object handle. + * + * @return The status of the operation. + */ +Sec_Result SecDigest_GetInstance(Sec_ProcessorHandle* processorHandle, Sec_DigestAlgorithm algorithm, + Sec_DigestHandle** digestHandle) { + CHECK_PROCHANDLE(processorHandle) + + *digestHandle = calloc(1, sizeof(Sec_DigestHandle)); + if (*digestHandle == NULL) { + SEC_LOG_ERROR("Malloc failed"); + return SEC_RESULT_FAILURE; + } + + (*digestHandle)->algorithm = algorithm; + (*digestHandle)->processorHandle = processorHandle; + + switch (algorithm) { + case SEC_DIGESTALGORITHM_SHA1: + if (SHA1_Init(&((*digestHandle)->sha1_ctx)) != 1) { + SEC_FREE(*digestHandle); + return SEC_RESULT_FAILURE; + } + + break; + + case SEC_DIGESTALGORITHM_SHA256: + if (SHA256_Init(&((*digestHandle)->sha256_ctx)) != 1) { + SEC_FREE(*digestHandle); + return SEC_RESULT_FAILURE; + } + + break; + + default: + SEC_LOG_ERROR("Unimplemented digest algorithm"); + SEC_FREE(*digestHandle); + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Update the digest value with the specified input. + * + * @param digestHandle handle of the digest object. + * @param input pointer to the input buffer. + * @param inputSize size of the input buffer. + * + * @return The status of the operation. + */ +Sec_Result SecDigest_Update(Sec_DigestHandle* digestHandle, SEC_BYTE* input, SEC_SIZE inputSize) { + CHECK_HANDLE(digestHandle) + + // SecDigest_UpdateWithKey can't be mixed with SecDigest_Update. + if (digestHandle->key_digest != NULL) { + SEC_LOG_ERROR("SecDigest_UpdateWithKey can't be mixed with SecDigest_Update"); + return SEC_RESULT_FAILURE; + } + + switch (digestHandle->algorithm) { + case SEC_DIGESTALGORITHM_SHA1: + if (SHA1_Update(&(digestHandle->sha1_ctx), input, inputSize) != 1) + return SEC_RESULT_FAILURE; + + break; + + case SEC_DIGESTALGORITHM_SHA256: + if (SHA256_Update(&(digestHandle->sha256_ctx), input, inputSize) != 1) + return SEC_RESULT_FAILURE; + + break; + + default: + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + } + + digestHandle->in_process = SEC_TRUE; + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Update the digest value with the key data. + * + * @param digestHandle handle of the digest object. + * @param keyHandle key to use. + * + * @return The status of the operation. + */ +Sec_Result SecDigest_UpdateWithKey(Sec_DigestHandle* digestHandle, Sec_KeyHandle* keyHandle) { + // SecDigest_UpdateWithKey can't be mixed with SecDigest_Update. + if (digestHandle->in_process) { + SEC_LOG_ERROR("SecDigest_UpdateWithKey can't be mixed with SecDigest_Update"); + return SEC_RESULT_FAILURE; + } + + if (digestHandle->key_digest != NULL) { + SEC_LOG_ERROR("SecDigest_UpdateWithKey can't be called multiple times"); + return SEC_RESULT_FAILURE; + } + + const Sec_Key* key = get_key(keyHandle); + sa_digest_algorithm algorithm = (digestHandle->algorithm == SEC_DIGESTALGORITHM_SHA1) ? SA_DIGEST_ALGORITHM_SHA1 : + SA_DIGEST_ALGORITHM_SHA256; + sa_status status = sa_key_digest(NULL, &digestHandle->key_digest_length, key->handle, algorithm); + CHECK_STATUS(status) + + digestHandle->key_digest = malloc(digestHandle->key_digest_length); + if (digestHandle->key_digest == NULL) { + SEC_LOG_ERROR("malloc failed"); + return SEC_RESULT_FAILURE; + } + + status = sa_key_digest(digestHandle->key_digest, &digestHandle->key_digest_length, key->handle, + algorithm); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Calculate the resulting digest value and release the digest object. + * + * @param digestHandle digest handle. + * @param digestOutput pointer to an output buffer that will be filled with the resulting + * digest value. Buffer should be SEC_DIGEST_MAX_LEN bytes long. + * @param digestSize pointer to a value that will be set to actual size of the digest value. + * + * @return The status of the operation. + */ +Sec_Result SecDigest_Release(Sec_DigestHandle* digestHandle, SEC_BYTE* digestOutput, SEC_SIZE* digestSize) { + CHECK_HANDLE(digestHandle) + + if (digestOutput == NULL) { + SEC_LOG_ERROR("digestOutput is NULL"); + return SEC_RESULT_FAILURE; + } + + if (digestSize == NULL) { + SEC_LOG_ERROR("digestSize is NULL"); + return SEC_RESULT_FAILURE; + } + + if (digestHandle->key_digest == NULL) { + switch (digestHandle->algorithm) { + case SEC_DIGESTALGORITHM_SHA1: + *digestSize = SHA_DIGEST_LENGTH; + if (SHA1_Final(digestOutput, &(digestHandle->sha1_ctx)) != 1) + return SEC_RESULT_FAILURE; + + break; + + case SEC_DIGESTALGORITHM_SHA256: + *digestSize = SHA256_DIGEST_LENGTH; + if (SHA256_Final(digestOutput, &(digestHandle->sha256_ctx)) != 1) + return SEC_RESULT_FAILURE; + + break; + + default: + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + } + } else { + memcpy(digestOutput, digestHandle->key_digest, digestHandle->key_digest_length); + *digestSize = digestHandle->key_digest_length; + free(digestHandle->key_digest); + } + + SEC_FREE(digestHandle); + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Obtain the size of the digest for a specified digest algorithm. + * + * @param alg digest algorithm. + * + * @return digest size in bytes. + */ +SEC_SIZE SecDigest_GetDigestLenForAlgorithm(Sec_DigestAlgorithm alg) { + switch (alg) { + case SEC_DIGESTALGORITHM_SHA1: + return SHA_DIGEST_LENGTH; + + case SEC_DIGESTALGORITHM_SHA256: + return SHA256_DIGEST_LENGTH; + + default: + return 0; + } +} + +/** + * @brief Utility function for calculating a digest value of a single input buffer. + * + * @param proc secure processor handle. + * @param alg digest algorithm to use. + * @param input input data to calculate digest over. + * @param input_len size of input data in bytes. + * @param digest output buffer where the calculated digest value will be written. + * @param digest_len number of bytes written to the output digest buffer. + * + * @return status of the operation. + */ +Sec_Result SecDigest_SingleInput(Sec_ProcessorHandle* processorHandle, Sec_DigestAlgorithm alg, SEC_BYTE* input, + SEC_SIZE input_len, SEC_BYTE* digest, SEC_SIZE* digest_len) { + Sec_Result result; + Sec_DigestHandle* digestHandle = NULL; + + result = SecDigest_GetInstance(processorHandle, alg, &digestHandle); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_GetInstance failed"); + return result; + } + + result = SecDigest_Update(digestHandle, input, input_len); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_Update failed"); + SecDigest_Release(digestHandle, digest, digest_len); + return result; + } + + return SecDigest_Release(digestHandle, digest, digest_len); +} + +/** + * @brief Utility function for calculating a digest value of a single input buffer. + * + * @param proc secure processor handle. + * @param alg digest algorithm to use. + * @param key_id id of the key over which the digest is being calculated. + * @param digest output buffer where the calculated digest value will be written. + * @param digest_len number of bytes written to the output digest buffer. + * + * @return status of the operation. + */ +Sec_Result SecDigest_SingleInputWithKeyId(Sec_ProcessorHandle* processorHandle, Sec_DigestAlgorithm alg, + SEC_OBJECTID key_id, SEC_BYTE* digest, SEC_SIZE* digest_len) { + Sec_Result result = SEC_RESULT_FAILURE; + + Sec_DigestHandle* digestHandle = NULL; + Sec_KeyHandle* keyHandle = NULL; + do { + if (SecDigest_GetInstance(processorHandle, alg, &digestHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_GetInstance failed"); + break; + } + + if (SecKey_GetInstance(processorHandle, key_id, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + break; + } + + if (SecDigest_UpdateWithKey(digestHandle, keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_UpdateWithKey failed"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (SEC_FALSE); + + if (digestHandle != NULL) { + SecDigest_Release(digestHandle, digest, digest_len); + digestHandle = NULL; + } + + if (keyHandle != NULL) { + SecKey_Release(keyHandle); + keyHandle = NULL; + } + + return result; +} diff --git a/src/sec_adapter_engine.c b/src/sec_adapter_engine.c new file mode 100644 index 0000000..50b7aff --- /dev/null +++ b/src/sec_adapter_engine.c @@ -0,0 +1,355 @@ +/** +* Copyright 2020-2022 Comcast Cable Communications Management, LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +#include "sec_security.h" +#include +#include + +#define ENGINE_ID "securityapi" + +static SEC_BOOL g_sec_openssl_inited = SEC_FALSE; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +static RSA_METHOD* rsa_method = NULL; +#endif + +static void Sec_ShutdownOpenSSL() { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (rsa_method != NULL) { + RSA_meth_free(rsa_method); + rsa_method = NULL; + } +#endif + + ENGINE* engine = ENGINE_by_id(ENGINE_ID); + if (engine != NULL) { + ENGINE_remove(engine); + ENGINE_finish(engine); + ENGINE_free(engine); + } +} + +static int Sec_OpenSSLPrivSign(int type, const unsigned char* m, unsigned int m_len, unsigned char* sigret, + unsigned int* siglen, const RSA* rsa) { + Sec_KeyHandle* keyHandle = NULL; + Sec_SignatureAlgorithm alg; + switch (type) { + case NID_sha1: + alg = SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS_DIGEST; + break; + + case NID_sha256: + alg = SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS_DIGEST; + break; + + default: + SEC_LOG_ERROR("Unknown type %d", type); + return -1; + } + + keyHandle = (Sec_KeyHandle*) RSA_get_app_data(rsa); + if (keyHandle == NULL) { + SEC_LOG_ERROR("NULL keyHandle encountered"); + return -1; + } + + if (SecSignature_SingleInput(SecKey_GetProcessor(keyHandle), alg, SEC_SIGNATUREMODE_SIGN, keyHandle, + (SEC_BYTE*) m, m_len, (SEC_BYTE*) sigret, siglen) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_SingleInput failed"); + return -1; + } + + return 1; +} + +static int Sec_OpenSSLPubVerify(int type, const unsigned char* m, unsigned int m_len, const unsigned char* sigret, + unsigned int siglen, const RSA* rsa) { + Sec_KeyHandle* keyHandle = NULL; + Sec_SignatureAlgorithm alg; + switch (type) { + case NID_sha1: + alg = SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS; + break; + + case NID_sha256: + alg = SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS; + break; + + default: + SEC_LOG_ERROR("Unknown type %d", type); + return -1; + } + + keyHandle = (Sec_KeyHandle*) RSA_get_app_data(rsa); + if (keyHandle == NULL) { + SEC_LOG_ERROR("NULL keyHandle encountered"); + return -1; + } + + if (SecSignature_SingleInput(SecKey_GetProcessor(keyHandle), alg, SEC_SIGNATUREMODE_VERIFY, keyHandle, + (SEC_BYTE*) m, m_len, (SEC_BYTE*) sigret, &siglen) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_SingleInput failed"); + return -1; + } + + return 1; +} + +static int Sec_OpenSSLPubEncrypt(int flen, const unsigned char* from, unsigned char* to, RSA* rsa, int padding) { + Sec_KeyHandle* keyHandle = NULL; + Sec_CipherAlgorithm alg; + SEC_SIZE written; + switch (padding) { + case RSA_PKCS1_PADDING: + alg = SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING; + break; + + case RSA_PKCS1_OAEP_PADDING: + alg = SEC_CIPHERALGORITHM_RSA_OAEP_PADDING; + break; + + default: + SEC_LOG_ERROR("Unknown padding %d", padding); + return -1; + } + + keyHandle = (Sec_KeyHandle*) RSA_get_app_data(rsa); + if (keyHandle == NULL) { + SEC_LOG_ERROR("NULL keyHandle encountered"); + return -1; + } + + if (SecCipher_SingleInput(SecKey_GetProcessor(keyHandle), alg, SEC_CIPHERMODE_ENCRYPT, keyHandle, NULL, + (SEC_BYTE*) from, flen, (SEC_BYTE*) to, SecKey_GetKeyLen(keyHandle), &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_SingleInput failed"); + return -1; + } + + return (int) written; +} + +static int Sec_OpenSSLPrivDecrypt(int flen, const unsigned char* from, unsigned char* to, RSA* rsa, int padding) { + Sec_KeyHandle* keyHandle = NULL; + Sec_CipherAlgorithm alg; + SEC_SIZE written; + switch (padding) { + case RSA_PKCS1_PADDING: + alg = SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING; + break; + + case RSA_PKCS1_OAEP_PADDING: + alg = SEC_CIPHERALGORITHM_RSA_OAEP_PADDING; + break; + + default: + SEC_LOG_ERROR("Unknown padding %d", padding); + return -1; + } + + keyHandle = (Sec_KeyHandle*) RSA_get_app_data(rsa); + if (keyHandle == NULL) { + SEC_LOG_ERROR("NULL keyHandle encountered"); + return -1; + } + + if (SecCipher_SingleInput(SecKey_GetProcessor(keyHandle), alg, SEC_CIPHERMODE_DECRYPT, keyHandle, NULL, + (SEC_BYTE*) from, flen, (SEC_BYTE*) to, SecKey_GetKeyLen(keyHandle), &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_SingleInput failed"); + return -1; + } + + return (int) written; +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +static RSA_METHOD g_sec_openssl_rsamethod = { + "securityapi RSA method", + Sec_OpenSSLPubEncrypt, // rsa_pub_enc + NULL, // rsa_pub_dec + NULL, // rsa_priv_enc + Sec_OpenSSLPrivDecrypt, // rsa_priv_dec + NULL, // rsa_mod_exp + NULL, // bn_mod_exp + NULL, // init + NULL, // finish + RSA_METHOD_FLAG_NO_CHECK | RSA_FLAG_EXT_PKEY | RSA_FLAG_SIGN_VER, // flags + NULL, // app_data + Sec_OpenSSLPrivSign, // rsa_sign + Sec_OpenSSLPubVerify, // rsa_verify + NULL, // rsa_keygen +}; + +#endif + +static void ENGINE_load_securityapi(void) { + ENGINE* engine = ENGINE_new(); + if (engine == NULL) { + SEC_LOG_ERROR("ENGINE_new failed"); + return; + } + + if (!ENGINE_set_id(engine, ENGINE_ID)) { + ENGINE_free(engine); + return; + } + if (!ENGINE_set_name(engine, "SecurityApi engine")) { + ENGINE_free(engine); + return; + } + + if (!ENGINE_init(engine)) { + ENGINE_free(engine); + return; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + if (!ENGINE_set_RSA(engine, &g_sec_openssl_rsamethod)) { +#else + if (!ENGINE_set_RSA(engine, rsa_method)) { +#endif + ENGINE_remove(engine); + ENGINE_free(engine); + return; + } + + ENGINE_add(engine); + ENGINE_free(engine); + ERR_clear_error(); +} + +void Sec_InitOpenSSL() { + static pthread_mutex_t init_openssl_mutex = PTHREAD_MUTEX_INITIALIZER; + + pthread_mutex_lock(&init_openssl_mutex); + + if (g_sec_openssl_inited != SEC_TRUE) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (rsa_method == NULL) { + rsa_method = RSA_meth_new("securityapi RSA method", RSA_METHOD_FLAG_NO_CHECK | RSA_FLAG_EXT_PKEY); + RSA_meth_set_pub_enc(rsa_method, Sec_OpenSSLPubEncrypt); + RSA_meth_set_priv_dec(rsa_method, Sec_OpenSSLPrivDecrypt); + RSA_meth_set_sign(rsa_method, Sec_OpenSSLPrivSign); + RSA_meth_set_verify(rsa_method, Sec_OpenSSLPubVerify); + } + +#else + ERR_load_crypto_strings(); + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_ciphers(); + OpenSSL_add_all_digests(); +#endif + + ENGINE_load_builtin_engines(); + ENGINE_register_all_complete(); + ENGINE_load_securityapi(); + + if (atexit(Sec_ShutdownOpenSSL) != 0) { + SEC_LOG_ERROR("atexit failed"); + return; + } + + g_sec_openssl_inited = SEC_TRUE; + } + + pthread_mutex_unlock(&init_openssl_mutex); +} + +void Sec_PrintOpenSSLVersion() { + SEC_PRINT("Built against: %s\n", OPENSSL_VERSION_TEXT); + SEC_PRINT("Running against: %s\n", SSLeay_version(SSLEAY_VERSION)); +} + +RSA* SecKey_ToEngineRSA(Sec_KeyHandle* keyHandle) { + Sec_RSARawPublicKey pubKey; + RSA* rsa = NULL; + ENGINE* engine = NULL; + + engine = ENGINE_by_id(ENGINE_ID); + if (engine == NULL) { + SEC_LOG_ERROR("ENGINE_by_id failed"); + return NULL; + } + + if (SEC_RESULT_SUCCESS != SecKey_ExtractRSAPublicKey(keyHandle, &pubKey)) { + ENGINE_free(engine); + SEC_LOG_ERROR("SecKey_ExtractRSAPublicKey failed"); + return NULL; + } + + rsa = RSA_new_method(engine); + if (rsa == NULL) { + ENGINE_free(engine); + SEC_LOG_ERROR("RSA_new_method failed"); + return NULL; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + rsa->n = BN_bin2bn(pubKey.n, (int) Sec_BEBytesToUint32(pubKey.modulus_len_be), NULL); + rsa->e = BN_bin2bn(pubKey.e, 4, NULL); +#else + RSA_set0_key(rsa, BN_bin2bn(pubKey.n, (int) Sec_BEBytesToUint32(pubKey.modulus_len_be), NULL), + BN_bin2bn(pubKey.e, 4, NULL), NULL); +#endif + + RSA_set_app_data(rsa, keyHandle); + ENGINE_free(engine); + return rsa; +} + +RSA* SecKey_ToEngineRSAWithCert(Sec_KeyHandle* keyHandle, Sec_CertificateHandle* certificateHandle) { + Sec_RSARawPublicKey pubKey; + RSA* rsa = NULL; + ENGINE* engine = NULL; + + engine = ENGINE_by_id(ENGINE_ID); + if (engine == NULL) { + SEC_LOG_ERROR("ENGINE_by_id failed"); + return NULL; + } + + if (SEC_RESULT_SUCCESS != SecCertificate_ExtractRSAPublicKey(certificateHandle, &pubKey)) { + ENGINE_free(engine); + SEC_LOG_ERROR("SecKey_ExtractRSAPublicKey failed"); + return NULL; + } + + rsa = RSA_new_method(engine); + if (rsa == NULL) { + ENGINE_free(engine); + SEC_LOG_ERROR("RSA_new_method failed"); + return NULL; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + rsa->n = BN_bin2bn(pubKey.n, (int) Sec_BEBytesToUint32(pubKey.modulus_len_be), NULL); + rsa->e = BN_bin2bn(pubKey.e, 4, NULL); +#else + RSA_set0_key(rsa, BN_bin2bn(pubKey.n, (int) Sec_BEBytesToUint32(pubKey.modulus_len_be), NULL), + BN_bin2bn(pubKey.e, 4, NULL), NULL); +#endif + + RSA_set_app_data(rsa, keyHandle); + ENGINE_free(engine); + return rsa; +} + +EC_KEY* SecKey_ToEngineEcc(Sec_KeyHandle* keyHandle) { + SEC_LOG_ERROR("SecKey_ToEngineEcc is not implemented"); + return NULL; +} diff --git a/src/sec_adapter_key.c b/src/sec_adapter_key.c new file mode 100644 index 0000000..c7c78c8 --- /dev/null +++ b/src/sec_adapter_key.c @@ -0,0 +1,3428 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_adapter_key.h" // NOLINT +#include "sec_adapter_cipher.h" +#include "sec_adapter_key_legacy.h" +#include "sec_adapter_processor.h" +#include +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#include +#endif + +#ifndef SEC_TRACE_UNWRAP +#define SEC_TRACE_UNWRAP 0 +#endif + +#if SEC_TRACE_UNWRAP +#pragma message "SEC_TRACE_UNWRAP is enabled. Please disable in production builds." +#endif + +#define CHECK_MAC_RESULT(status, base_key, mac_context) \ + if ((status) != SA_STATUS_OK) { \ + if ((mac_context) != 0) \ + sa_crypto_mac_release(mac_context); \ + sa_key_release(base_key); \ + return SEC_RESULT_FAILURE; \ + } + +#define PUBLIC_KEY_BUFFER_SIZE 4 +#define ISO_TIME_SIZE 24 + +struct Sec_KeyHandle_struct { + Sec_ProcessorHandle* processorHandle; + Sec_KeyType key_type; + Sec_Key key; +}; + +static void find_ram_key_data(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, Sec_RAMKeyData** data, + Sec_RAMKeyData** parent); + +static Sec_Result retrieve_key_data(Sec_ProcessorHandle* processorHandle, + SEC_OBJECTID object_id, Sec_StorageLoc* location, Sec_KeyData* keyData); + +static Sec_Result store_key_data(Sec_ProcessorHandle* processorHandle, Sec_StorageLoc location, SEC_OBJECTID object_id, + Sec_KeyData* key_data); + +static Sec_Result process_key_container(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_KeyContainer in_key_container, void* data, SEC_SIZE data_length, Sec_Key* key, + Sec_KeyContainer* out_key_container, void* key_buffer, SEC_SIZE* key_length); + +static Sec_Result process_rsa_key_container(Sec_KeyContainer in_key_container, SEC_BYTE* data, + SEC_SIZE data_length, unsigned char* key_buffer, SEC_SIZE* key_length, Sec_KeyContainer* key_container); + +static Sec_Result process_rsa_public_key_container(Sec_KeyContainer in_key_container, SEC_BYTE* data, + SEC_SIZE data_length, RSA** rsa, unsigned char* key_buffer, SEC_SIZE* key_length, + Sec_KeyContainer* out_key_container); + +static Sec_Result process_ec_key_container(Sec_KeyContainer in_key_container, SEC_BYTE* data, + SEC_SIZE data_length, unsigned char* key_buffer, SEC_SIZE* key_length, Sec_KeyContainer* out_key_container); + +static Sec_Result process_ec_public_key_container(Sec_KeyContainer in_key_container, SEC_BYTE* data, + SEC_SIZE data_length, EC_KEY** ec_key, unsigned char* key_buffer, SEC_SIZE* key_length, + Sec_KeyContainer* out_key_container); + +static Sec_KeyContainer convert_key_container(Sec_KeyContainer key_container); + +static int disable_passphrase_prompt(char* buf, int size, int rwflag, void* u); + +static Sec_Result process_asn1_key_container(Sec_ProcessorHandle* processorHandle, const void* data, + SEC_SIZE data_length, SEC_BYTE* key_buffer, SEC_SIZE* key_length, Sec_KeyContainer* key_container); + +static Sec_Result process_store_key_container(Sec_ProcessorHandle* processorHandle, void* data, + SEC_SIZE data_length, SEC_BYTE* key_buffer, SEC_SIZE* key_length, Sec_KeyContainer* key_container); + +static Sec_KeyType get_key_type(sa_header* key_header); + +static Sec_Result derive_root_key_ladder(const SEC_BYTE* c1, const SEC_BYTE* c2, const SEC_BYTE* c3, const SEC_BYTE* c4, + SEC_SIZE key_size, sa_key* key, Sec_KeyType key_type); + +static Sec_Result derive_base_key(Sec_ProcessorHandle* processorHandle, SEC_BYTE* nonce, sa_key* key, + Sec_KeyType key_type); + +static Sec_Result derive_hkdf(Sec_MacAlgorithm macAlgorithm, Sec_KeyType typeDerived, const SEC_BYTE* salt, + SEC_SIZE saltSize, const SEC_BYTE* info, SEC_SIZE infoSize, sa_key baseKey, sa_key* derived_key); + +static Sec_Result derive_kdf_concat(Sec_DigestAlgorithm digestAlgorithm, Sec_KeyType typeDerived, + const SEC_BYTE* otherInfo, SEC_SIZE otherInfoSize, sa_key baseKey, sa_key* derived_key); + +static Sec_Result derive_kdf_cmac(Sec_KeyType typeDerived, const SEC_BYTE* otherData, SEC_SIZE otherDataSize, + const SEC_BYTE* counter, SEC_SIZE counterSize, sa_key baseKey, sa_key* derived_key); + +static Sec_Result unwrap_key(Sec_ProcessorHandle* processorHandle, Sec_CipherAlgorithm algorithm, + Sec_KeyType wrapped_key_type, SEC_SIZE wrapped_key_offset, SEC_OBJECTID id, SEC_BYTE* iv, SEC_BYTE* input, + SEC_SIZE input_len, SEC_BYTE* out_key, SEC_SIZE* out_key_len); + +static Sec_Result get_sa_key_type(Sec_KeyType keyType, sa_key_type* out_key_type, void** parameters); + +static Sec_Result export_key(Sec_Key* key, SEC_BYTE* derivationInput, SEC_BYTE* exportedKey, SEC_SIZE keyBufferLen, + SEC_SIZE* keyBytesWritten); + +static bool is_jwt_key_container(unsigned char* key_buffer, SEC_SIZE key_length); + +Sec_KeyType SecKey_GetKeyTypeForClearKeyContainer(Sec_KeyContainer kc) { + switch (kc) { + case SEC_KEYCONTAINER_RAW_AES_128: + return SEC_KEYTYPE_AES_128; + + case SEC_KEYCONTAINER_RAW_AES_256: + return SEC_KEYTYPE_AES_256; + + case SEC_KEYCONTAINER_RAW_HMAC_128: + return SEC_KEYTYPE_HMAC_128; + + case SEC_KEYCONTAINER_RAW_HMAC_160: + return SEC_KEYTYPE_HMAC_160; + + case SEC_KEYCONTAINER_RAW_HMAC_256: + return SEC_KEYTYPE_HMAC_256; + + case SEC_KEYCONTAINER_RAW_RSA_1024: + case SEC_KEYCONTAINER_PEM_RSA_1024: + case SEC_KEYCONTAINER_DER_RSA_1024: + return SEC_KEYTYPE_RSA_1024; + + case SEC_KEYCONTAINER_RAW_RSA_2048: + case SEC_KEYCONTAINER_PEM_RSA_2048: + case SEC_KEYCONTAINER_DER_RSA_2048: + return SEC_KEYTYPE_RSA_2048; + + case SEC_KEYCONTAINER_RAW_RSA_3072: + case SEC_KEYCONTAINER_PEM_RSA_3072: + case SEC_KEYCONTAINER_DER_RSA_3072: + return SEC_KEYTYPE_RSA_3072; + + case SEC_KEYCONTAINER_RAW_RSA_1024_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_1024_PUBLIC: + case SEC_KEYCONTAINER_DER_RSA_1024_PUBLIC: + return SEC_KEYTYPE_RSA_1024_PUBLIC; + + case SEC_KEYCONTAINER_RAW_RSA_2048_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_2048_PUBLIC: + case SEC_KEYCONTAINER_DER_RSA_2048_PUBLIC: + return SEC_KEYTYPE_RSA_2048_PUBLIC; + + case SEC_KEYCONTAINER_RAW_RSA_3072_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_3072_PUBLIC: + case SEC_KEYCONTAINER_DER_RSA_3072_PUBLIC: + return SEC_KEYTYPE_RSA_3072_PUBLIC; + + case SEC_KEYCONTAINER_PEM_ECC_NISTP256: + case SEC_KEYCONTAINER_RAW_ECC_PRIVONLY_NISTP256: + case SEC_KEYCONTAINER_RAW_ECC_NISTP256: + case SEC_KEYCONTAINER_DER_ECC_NISTP256: + return SEC_KEYTYPE_ECC_NISTP256; + + case SEC_KEYCONTAINER_PEM_ECC_NISTP256_PUBLIC: + case SEC_KEYCONTAINER_RAW_ECC_NISTP256_PUBLIC: + case SEC_KEYCONTAINER_DER_ECC_NISTP256_PUBLIC: + return SEC_KEYTYPE_ECC_NISTP256_PUBLIC; + + default: + return SEC_KEYTYPE_NUM; + } +} + +SEC_SIZE SecKey_GetKeyLenForKeyType(Sec_KeyType keyType) { + switch (keyType) { + case SEC_KEYTYPE_AES_128: + return 16; + + case SEC_KEYTYPE_AES_256: + return 32; + + case SEC_KEYTYPE_HMAC_128: + return 16; + + case SEC_KEYTYPE_HMAC_160: + return 20; + + case SEC_KEYTYPE_HMAC_256: + return 32; + + case SEC_KEYTYPE_RSA_1024: + case SEC_KEYTYPE_RSA_1024_PUBLIC: + return 128; + + case SEC_KEYTYPE_RSA_2048: + case SEC_KEYTYPE_RSA_2048_PUBLIC: + return 256; + + case SEC_KEYTYPE_RSA_3072: + case SEC_KEYTYPE_RSA_3072_PUBLIC: + return 384; + + case SEC_KEYTYPE_ECC_NISTP256: + case SEC_KEYTYPE_ECC_NISTP256_PUBLIC: + return SEC_ECC_NISTP256_KEY_LEN; + + default: + SEC_LOG_ERROR("Unknown key type encountered: %d", keyType); + return 0; + } +} + +/** + * @brief Get the properties for the key handle. + * + * @param keyHandle pointer to Sec_KeyHandle. + * @param keyProps pointer to Sec_KeyProperties where information is stored. + */ +Sec_Result SecKey_GetProperties(Sec_KeyHandle* keyHandle, Sec_KeyProperties* keyProperties) { + CHECK_HANDLE(keyHandle) + Sec_Memset(keyProperties, 0, sizeof(Sec_KeyProperties)); + + keyProperties->keyType = keyHandle->key_type; + sa_rights rights; + switch (keyHandle->key_type) { + case SEC_KEYTYPE_AES_128: + case SEC_KEYTYPE_AES_256: + case SEC_KEYTYPE_RSA_1024: + case SEC_KEYTYPE_RSA_2048: + case SEC_KEYTYPE_HMAC_128: + case SEC_KEYTYPE_HMAC_160: + case SEC_KEYTYPE_HMAC_256: + case SEC_KEYTYPE_ECC_NISTP256: + case SEC_KEYTYPE_RSA_3072: { + sa_header key_header; + sa_status status = sa_key_header(&key_header, keyHandle->key.handle); + CHECK_STATUS(status) + + rights = key_header.rights; + keyProperties->keyLength = key_header.size; + break; + } + case SEC_KEYTYPE_RSA_1024_PUBLIC: + case SEC_KEYTYPE_RSA_2048_PUBLIC: + case SEC_KEYTYPE_RSA_3072_PUBLIC: + case SEC_KEYTYPE_ECC_NISTP256_PUBLIC: + rights_set_allow_all(&rights, keyHandle->key_type); + keyProperties->keyLength = SecKey_GetKeyLenForKeyType(keyProperties->keyType); + break; + + default: + SEC_LOG_ERROR("Unsupported key_type %u", keyHandle->key_type); + return SEC_RESULT_INVALID_PARAMETERS; + } + + // Cacheable flag. + keyProperties->cacheable = SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_CACHEABLE) ? SEC_TRUE : SEC_FALSE; + + // Key ID in string format. SecApi 2 keyId is 40 chars, SecApi 3 keyID is 64 chars. Truncate to 39 bytes and null + // terminate. + memcpy(keyProperties->keyId, rights.id, 39); + keyProperties->keyId[39] = 0; + + // Validity Period + SecUtils_Epoch2IsoTime(rights.not_before, keyProperties->notBefore, ISO_TIME_SIZE); + SecUtils_Epoch2IsoTime(rights.not_on_or_after, keyProperties->notOnOrAfter, ISO_TIME_SIZE); + + // Rights flags + Sec_Memset(keyProperties->rights, SEC_KEYOUTPUTRIGHT_NOT_SET, sizeof(SEC_BYTE) * SEC_KEYOUTPUTRIGHT_NUM); + int i = 0; + // SEC_KEYOUTPUTRIGHT_TRANSCRIPTION_COPY_ALLOWED and SEC_KEYOUTPUTRIGHT_UNRESTRICTED_COPY_ALLOWED are not used in + // sec api 3. + if (!SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_SVP_OPTIONAL)) + keyProperties->rights[i++] = SEC_KEYOUTPUTRIGHT_SVP_REQUIRED; + if (SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_ALLOWED_DIGITAL_DTCP)) + keyProperties->rights[i++] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_DTCP_ALLOWED; + if (SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_ALLOWED_DIGITAL_HDCP14)) + keyProperties->rights[i++] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_1_4_ALLOWED; + if (SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_ALLOWED_DIGITAL_HDCP22)) + keyProperties->rights[i++] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_2_2_ALLOWED; + if (SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_ALLOWED_ANALOG_CGMSA) || + SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_ALLOWED_ANALOG_UNPROTECTED)) + keyProperties->rights[i++] = SEC_KEYOUTPUTRIGHT_ANALOG_OUTPUT_ALLOWED; + if (SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_ALLOWED_ANALOG_CGMSA) && + !SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_ALLOWED_ANALOG_CGMSA)) + keyProperties->rights[i++] = SEC_KEYOUTPUTRIGHT_CGMSA_REQUIRED; + + // Allow usage of both since it is not identified in the key_header. + // clang-format off + if (SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_UNWRAP) && + (SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_DECRYPT) || + SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_ENCRYPT) || + SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_SIGN) || + SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_DERIVE))) { + keyProperties->usage = SEC_KEYUSAGE_DATA_KEY; + } else if (SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_DECRYPT) || + SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_ENCRYPT) || + SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_SIGN) || + SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_DERIVE)) { + keyProperties->usage = SEC_KEYUSAGE_DATA; + } else if (SA_USAGE_BIT_TEST(rights.usage_flags, SA_USAGE_FLAG_UNWRAP)) { + keyProperties->usage = SEC_KEYUSAGE_KEY; + } + // clang-format on + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Get the length of the specified key in bytes. + * + * In case of symmetric keys, the length returned is the actual size of the key data. + * In case of asymmetric keys, the length returned is the size of the modulus in bytes. + * + * @param keyHandle key handle. + * + * @return The status of the operation. + */ +SEC_SIZE SecKey_GetKeyLen(Sec_KeyHandle* keyHandle) { + if (keyHandle == NULL) { + return 0; + } + + SEC_SIZE key_length; + sa_header key_header; + sa_status status; + switch (keyHandle->key_type) { + case SEC_KEYTYPE_ECC_NISTP256_PUBLIC: + return EC_GROUP_get_degree(EC_KEY_get0_group(keyHandle->key.ec_key)) / 8; + + case SEC_KEYTYPE_RSA_1024_PUBLIC: + case SEC_KEYTYPE_RSA_2048_PUBLIC: + case SEC_KEYTYPE_RSA_3072_PUBLIC: + return RSA_size(keyHandle->key.rsa); + + case SEC_KEYTYPE_AES_128: + case SEC_KEYTYPE_AES_256: + case SEC_KEYTYPE_RSA_1024: + case SEC_KEYTYPE_RSA_2048: + case SEC_KEYTYPE_HMAC_128: + case SEC_KEYTYPE_HMAC_160: + case SEC_KEYTYPE_HMAC_256: + case SEC_KEYTYPE_ECC_NISTP256: + case SEC_KEYTYPE_RSA_3072: + status = sa_key_header(&key_header, keyHandle->key.handle); + if (status != SA_STATUS_OK) { + return 0; + } + + return key_header.size; + + default: + return 0; + } +} + +/** + * @brief Get the key type of the specified key handle. + * + * @param keyHandle key handle. + * + * @return The key type or SEC_KEYTYPE_NUM if the key handle is invalid. + */ +Sec_KeyType SecKey_GetKeyType(Sec_KeyHandle* keyHandle) { + return keyHandle->key_type; +} + +/** + * @brief Obtain a handle to a provisioned key. + * + * @param processorHandle secure processor handle. + * @param object_id id of the provisioned key that we are attempting to obtain. + * @param keyHandle pointer to the output key handle. + * + * @return The status of the operation. + */ +Sec_Result SecKey_GetInstance(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, Sec_KeyHandle** keyHandle) { + CHECK_PROCHANDLE(processorHandle) + + if (object_id == SEC_OBJECTID_INVALID) + return SEC_RESULT_INVALID_PARAMETERS; + + Sec_StorageLoc location; + Sec_KeyData* key_data = calloc(1, sizeof(Sec_KeyData)); + if (key_data == NULL) { + SEC_LOG_ERROR("calloc failed"); + return SEC_RESULT_FAILURE; + } + + Sec_Result result = retrieve_key_data(processorHandle, object_id, &location, key_data); + if (result != SEC_RESULT_SUCCESS) { + SEC_FREE(key_data); + SEC_LOG_ERROR("retrieve_key_data failed"); + return result; + } + + Sec_Key key; + Sec_KeyContainer key_container; + SEC_SIZE key_length = SEC_KEYCONTAINER_MAX_LEN; + uint8_t* key_buffer = malloc(SEC_KEYCONTAINER_MAX_LEN); + if (key_buffer == NULL) { + SEC_FREE(key_data); + SEC_LOG_ERROR("malloc failed"); + return SEC_RESULT_FAILURE; + } + + result = process_key_container(processorHandle, object_id, key_data->info.kc_type, &key_data->key_container, + key_data->kc_len, &key, &key_container, key_buffer, &key_length); + SEC_FREE(key_data); + SEC_FREE(key_buffer); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("import_key failed"); + return result; + } + + *keyHandle = calloc(1, sizeof(Sec_KeyHandle)); + if (*keyHandle == NULL) { + SEC_LOG_ERROR("Calloc failed"); + return SEC_RESULT_FAILURE; + } + + (*keyHandle)->processorHandle = processorHandle; + memcpy(&(*keyHandle)->key, &key, sizeof(Sec_Key)); + if (key_container == SEC_KEYCONTAINER_DER_RSA_1024_PUBLIC || + key_container == SEC_KEYCONTAINER_DER_RSA_2048_PUBLIC || + key_container == SEC_KEYCONTAINER_DER_RSA_3072_PUBLIC || + key_container == SEC_KEYCONTAINER_DER_ECC_NISTP256_PUBLIC) { + (*keyHandle)->key_type = SecKey_GetKeyTypeForClearKeyContainer(key_container); + } else { + sa_header key_header; + sa_status status = sa_key_header(&key_header, (*keyHandle)->key.handle); + if (status == SA_STATUS_OK) { + (*keyHandle)->key_type = get_key_type(&key_header); + } + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Extract an RSA public key from a specified private key handle. + * + * @param keyHandle handle of the private key. + * @param public_key pointer to the output structure containing the public rsa key. + * + * @return The status of the operation. + */ +Sec_Result SecKey_ExtractRSAPublicKey(Sec_KeyHandle* keyHandle, Sec_RSARawPublicKey* public_key) { + CHECK_HANDLE(keyHandle) + if (public_key == NULL) + return SEC_RESULT_INVALID_PARAMETERS; + + switch (keyHandle->key_type) { + case SEC_KEYTYPE_RSA_1024: + case SEC_KEYTYPE_RSA_2048: + case SEC_KEYTYPE_RSA_3072: { + size_t out_len = SEC_KEYCONTAINER_MAX_LEN; + uint8_t* out = malloc(SEC_KEYCONTAINER_MAX_LEN); + if (out == NULL) { + SEC_LOG_ERROR("malloc failed"); + return SEC_RESULT_FAILURE; + } + + sa_status status; + status = sa_key_get_public(out, &out_len, keyHandle->key.handle); + if (status == SA_STATUS_OK) + Pubops_ExtractRSAPubFromPUBKEYDer(out, out_len, public_key); + + SEC_FREE(out); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; + } + case SEC_KEYTYPE_RSA_1024_PUBLIC: + case SEC_KEYTYPE_RSA_2048_PUBLIC: + case SEC_KEYTYPE_RSA_3072_PUBLIC: { + Sec_Uint32ToBEBytes(RSA_size(keyHandle->key.rsa), public_key->modulus_len_be); + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SecUtils_BigNumToBuffer(keyHandle->key.rsa->n, public_key->n, + Sec_BEBytesToUint32(public_key->modulus_len_be)); + SecUtils_BigNumToBuffer(keyHandle->key.rsa->e, public_key->e, PUBLIC_KEY_BUFFER_SIZE); +#else + const BIGNUM* n = NULL; + const BIGNUM* e = NULL; + RSA_get0_key(keyHandle->key.rsa, &n, &e, NULL); + SecUtils_BigNumToBuffer(n, public_key->n, Sec_BEBytesToUint32(public_key->modulus_len_be)); + SecUtils_BigNumToBuffer(e, public_key->e, PUBLIC_KEY_BUFFER_SIZE); +#endif + return SEC_RESULT_SUCCESS; + } + default: + return SEC_RESULT_INVALID_PARAMETERS; + } +} + +/** + * @brief Extract an ECC public key from a specified private key handle. + * + * @param keyHandle handle of the private key. + * @param public_key pointer to the output structure containing the public ecc key. + * + * @return The status of the operation. + */ +Sec_Result SecKey_ExtractECCPublicKey(Sec_KeyHandle* keyHandle, Sec_ECCRawPublicKey* public_key) { + CHECK_HANDLE(keyHandle) + if (public_key == NULL) + return SEC_RESULT_INVALID_PARAMETERS; + + switch (keyHandle->key_type) { + case SEC_KEYTYPE_ECC_NISTP256: { + size_t out_len = SEC_KEYCONTAINER_MAX_LEN; + uint8_t* out = malloc(SEC_KEYCONTAINER_MAX_LEN); + if (out == NULL) { + SEC_LOG_ERROR("malloc failed"); + return SEC_RESULT_FAILURE; + } + + sa_status status = sa_key_get_public(out, &out_len, keyHandle->key.handle); + if (status == SA_STATUS_OK) { + size_t key_length = out_len / 2; + Sec_Uint32ToBEBytes(key_length, public_key->key_len); + memcpy(public_key->x, out, key_length); + memcpy(public_key->y, out + key_length, key_length); + public_key->type = SEC_KEYTYPE_ECC_NISTP256_PUBLIC; + } + + SEC_FREE(out); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; + } + case SEC_KEYTYPE_ECC_NISTP256_PUBLIC: + if (SecUtils_ECCToPubBinary(keyHandle->key.ec_key, public_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_ECCToPubBinary failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; + + default: + return SEC_RESULT_INVALID_PARAMETERS; + } +} + +/** + * @brief Generate and provision a new key. + * + * @param processorHandle secure processor handle. + * @param object_id id of the key to generate. + * @param keyType type of the key to generate. + * @param location location where the key should be stored. + * + * @return The status of the operation. + */ +Sec_Result SecKey_Generate(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, Sec_KeyType keyType, + Sec_StorageLoc location) { + CHECK_PROCHANDLE(processorHandle) + + sa_key_type key_type; + void* parameters; + + Sec_Result result = get_sa_key_type(keyType, &key_type, ¶meters); + if (result != SEC_RESULT_SUCCESS) + return result; + + Sec_Key key; + sa_rights rights; + rights_set_allow_all(&rights, keyType); + sa_status status = sa_key_generate(&key.handle, &rights, key_type, parameters); + SEC_FREE(parameters); + CHECK_STATUS(status) + + return prepare_and_store_key_data(processorHandle, location, object_id, &key, SecKey_GetClearContainer(keyType), + NULL, 0); +} + +/** + * @brief Provision a key. + * + * @param processorHandle secure processor handle. + * @param object_id id of the key to provision. + * @param location storage location where the key should be provisioned. + * @param data_type type of input key container that is being used. + * @param data pointer to the input key container. + * @param data_length the size of the input key container. + * + * @return The status of the operation. + */ +Sec_Result SecKey_Provision(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, Sec_StorageLoc location, + Sec_KeyContainer data_type, SEC_BYTE* data, SEC_SIZE data_length) { + CHECK_PROCHANDLE(processorHandle) + + if (object_id == SEC_OBJECTID_INVALID) { + SEC_LOG_ERROR("Cannot provision object with SEC_OBJECTID_INVALID"); + return SEC_RESULT_FAILURE; + } + + if (data_length > SEC_KEYCONTAINER_MAX_LEN) { + SEC_LOG_ERROR("Key data is too long"); + return SEC_RESULT_FAILURE; + } + + Sec_Key key; + Sec_KeyContainer key_container; + SEC_SIZE key_length = SEC_KEYCONTAINER_MAX_LEN; + uint8_t* key_buffer = malloc(SEC_KEYCONTAINER_MAX_LEN); + if (key_buffer == NULL) { + SEC_LOG_ERROR("malloc failed"); + return SEC_RESULT_FAILURE; + } + + Sec_Result result = process_key_container(processorHandle, object_id, data_type, data, data_length, &key, + &key_container, key_buffer, &key_length); + if (result == SEC_RESULT_SUCCESS) + // Convert into an exported key container and store. + result = prepare_and_store_key_data(processorHandle, location, object_id, &key, key_container, key_buffer, + key_length); + + SEC_FREE(key_buffer); + return result; +} + +/** + * @brief Derive and provision a key using the HKDF algorithm. + * + * @param processorHandle secure processor handle. + * @param object_id_derived id of the key to provision. + * @param type_derived derived key type. + * @param loc_derived storage location where the derived key should be provisioned. + * @param macAlgorithm mac algorithm to use in the key derivation process. + * @param salt pointer to the salt value to use in key derivation process. + * @param saltSize the length of the salt buffer in bytes. + * @param info pointer to the info value to use in key derivation process. + * @param infoSize the length of the info buffer in bytes. + * + * @return The status of the operation. + */ +Sec_Result SecKey_Derive_HKDF(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id_derived, + Sec_KeyType type_derived, Sec_StorageLoc loc_derived, Sec_MacAlgorithm macAlgorithm, SEC_BYTE* nonce, + SEC_BYTE* salt, SEC_SIZE saltSize, SEC_BYTE* info, SEC_SIZE infoSize) { + sa_key base_key; + CHECK_PROCHANDLE(processorHandle) + + Sec_Result result = derive_base_key(processorHandle, nonce, &base_key, type_derived); + if (result != SEC_RESULT_SUCCESS) + return result; + + Sec_Key key; + result = derive_hkdf(macAlgorithm, type_derived, salt, saltSize, info, infoSize, base_key, &key.handle); + sa_key_release(base_key); + if (result != SEC_RESULT_SUCCESS) { + return result; + } + + return prepare_and_store_key_data(processorHandle, loc_derived, object_id_derived, &key, + SecKey_GetClearContainer(type_derived), NULL, 0); +} + +/** + * @brief Derive and provision a key using the Concat KDF algorithm. + * + * @param processorHandle secure processor handle. + * @param object_id_derived id of the key to provision. + * @param type_derived derived key type. + * @param loc_derived storage location where the derived key should be provisioned. + * @param digestAlgorithm digest algorithm to use in the key derivation process. + * @param otherInfo pointer to the info value to use in key derivation process. + * @param otherInfoSize the length of the other info buffer in bytes. + * + * @return The status of the operation. + */ +Sec_Result SecKey_Derive_ConcatKDF(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id_derived, + Sec_KeyType type_derived, Sec_StorageLoc loc_derived, Sec_DigestAlgorithm digestAlgorithm, SEC_BYTE* nonce, + SEC_BYTE* otherInfo, SEC_SIZE otherInfoSize) { + sa_key base_key; + CHECK_PROCHANDLE(processorHandle) + + Sec_Result result = derive_base_key(processorHandle, nonce, &base_key, type_derived); + if (result != SEC_RESULT_SUCCESS) + return result; + + Sec_Key key; + result = derive_kdf_concat(digestAlgorithm, type_derived, otherInfo, otherInfoSize, base_key, &key.handle); + sa_key_release(base_key); + if (result != SEC_RESULT_SUCCESS) { + return result; + } + + return prepare_and_store_key_data(processorHandle, loc_derived, object_id_derived, &key, + SecKey_GetClearContainer(type_derived), NULL, 0); +} + +Sec_Result SecKey_Derive_PBEKDF(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id_derived, + Sec_KeyType type_derived, Sec_StorageLoc loc_derived, Sec_MacAlgorithm macAlgorithm, SEC_BYTE* nonce, + SEC_BYTE* salt, SEC_SIZE saltSize, SEC_SIZE numIterations) { + CHECK_PROCHANDLE(processorHandle) + + SEC_BYTE loop[] = {0, 0, 0, 0}; + SEC_BYTE out_key[SEC_AES_KEY_MAX_LEN]; + + if (!SecKey_IsSymetric(type_derived)) { + SEC_LOG_ERROR("Only symmetric keys can be derived"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + sa_key base_key; + Sec_Result result = derive_base_key(processorHandle, nonce, &base_key, type_derived); + if (result != SEC_RESULT_SUCCESS) + return result; + + SEC_SIZE key_length = SecKey_GetKeyLenForKeyType(type_derived); + SEC_SIZE digest_length = SecDigest_GetDigestLenForAlgorithm(SecMac_GetDigestAlgorithm(macAlgorithm)); + SEC_SIZE l = key_length / digest_length + ((key_length % digest_length == 0) ? 0 : 1); + + for (size_t i = 1; i <= l; i++) { + loop[3] = i; + + SEC_SIZE cp_len = (i == l) ? key_length % digest_length : digest_length; + + sa_crypto_mac_context mac_context; + sa_mac_algorithm mac_algorithm = + (macAlgorithm == SEC_MACALGORITHM_CMAC_AES_128) ? SA_MAC_ALGORITHM_CMAC : SA_MAC_ALGORITHM_HMAC; + void* parameters = NULL; + if (mac_algorithm != SEC_MACALGORITHM_CMAC_AES_128) { + sa_mac_parameters_hmac hmac_parameters = { + (mac_algorithm == SEC_MACALGORITHM_HMAC_SHA1) ? SA_DIGEST_ALGORITHM_SHA1 : + SA_DIGEST_ALGORITHM_SHA256}; + parameters = &hmac_parameters; + } + + sa_status status = sa_crypto_mac_init(&mac_context, mac_algorithm, base_key, parameters); + CHECK_MAC_RESULT(status, base_key, 0) + + status = sa_crypto_mac_process(mac_context, salt, saltSize); + CHECK_MAC_RESULT(status, base_key, mac_context) + + status = sa_crypto_mac_process(mac_context, loop, sizeof(loop)); + CHECK_MAC_RESULT(status, base_key, mac_context) + + SEC_BYTE mac1[SEC_MAC_MAX_LEN]; + size_t mac1_len; + status = sa_crypto_mac_compute(mac1, &mac1_len, mac_context); + sa_crypto_mac_release(mac_context); + CHECK_MAC_RESULT(status, base_key, 0) + + SEC_BYTE out[SEC_MAC_MAX_LEN]; + memcpy(out, mac1, digest_length); + + for (size_t j = 1; j < numIterations; j++) { + status = sa_crypto_mac_init(&mac_context, mac_algorithm, base_key, parameters); + CHECK_MAC_RESULT(status, base_key, 0) + + status = sa_crypto_mac_process(mac_context, mac1, digest_length); + CHECK_MAC_RESULT(status, base_key, mac_context) + + SEC_BYTE mac2[SEC_MAC_MAX_LEN]; + size_t mac2_len; + status = sa_crypto_mac_compute(mac2, &mac2_len, mac_context); + sa_crypto_mac_release(mac_context); + CHECK_MAC_RESULT(status, base_key, 0) + + memcpy(mac1, mac2, digest_length); + + for (int k = 0; k < digest_length; ++k) { + out[k] ^= mac1[k]; + } + } + + memcpy(out_key + (i - 1) * digest_length, out, cp_len); + } + + sa_key_release(base_key); + + return SecKey_Provision(processorHandle, object_id_derived, loc_derived, SecKey_GetClearContainer(type_derived), + out_key, key_length); +} + +/** + * @brief Derive and provision an AES 128-bit key a vendor specific key ladder algorithm. + * + * This function will generate a key derived from one of the OTP keys. The + * result of this function may not be usable in Digest and Mac _UpdateWithKey + * functions. In general, this function will keep the derived key more secure + * then the other SecKey_Derive functions because the key will not be accessable + * by the host even during the generation time. + * + * @param processorHandle secure processor handle. + * @param object_id_derived id of the key to provision. + * @param loc_derived storage location where the derived key should be provisioned. + * @param input input buffer for the key derivation. + * @param input_len the length of the input buffer. + * + * @return The status of the operation. + */ +Sec_Result SecKey_Derive_VendorAes128(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id_derived, + Sec_StorageLoc loc_derived, SEC_BYTE* input, SEC_SIZE input_len) { + SEC_BYTE digest[SEC_DIGEST_MAX_LEN]; + SEC_SIZE digest_len; + SEC_BYTE digest2[SEC_DIGEST_MAX_LEN]; + SEC_SIZE digest2_len; + CHECK_PROCHANDLE(processorHandle) + + Sec_Result result = SecDigest_SingleInput(processorHandle, SEC_DIGESTALGORITHM_SHA256, input, input_len, digest, + &digest_len); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_SingleInput failed"); + return SEC_RESULT_FAILURE; + } + + result = SecDigest_SingleInput(processorHandle, SEC_DIGESTALGORITHM_SHA256, digest, digest_len, digest2, + &digest2_len); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_SingleInput failed"); + return SEC_RESULT_FAILURE; + } + + /* setup key ladder inputs */ + SEC_SIZE key_length = SecKey_GetKeyLenForKeyType(SEC_KEYTYPE_AES_128); + SEC_BYTE input1[key_length]; + SEC_BYTE input2[key_length]; + SEC_BYTE input3[key_length]; + SEC_BYTE input4[key_length]; + memcpy(input1, digest, key_length); + memcpy(input2, digest + key_length, key_length); + memcpy(input3, digest2, key_length); + memcpy(input4, digest2 + key_length, key_length); + + return SecKey_Derive_KeyLadderAes128(processorHandle, object_id_derived, loc_derived, SEC_KEYLADDERROOT_NUM, + input1, input2, input3, input4); +} + +/** + * @brief Derive and provision an AES 128-bit key. + * + * This function will generate a key derived from one of the OTP keys. The + * result of this function may not be usable in Digest and Mac _UpdateWithKey + * functions. In general, this function will keep the derived key more secure + * then the other SecKey_Derive functions because the key will not be accessable + * by the host even during the generation time. + * + * @param processorHandle secure processor handle. + * @param object_id_derived id of the key to provision. + * @param type_derived derived key type. + * @param loc_derived storage location where the derived key should be provisioned. + * @param input1 16 byte input for stage 1 of the key ladder. + * @param input2 16 byte input for stage 2 of the key ladder. + * @param input3 16 byte input for stage 3 of the key ladder. + * @param input4 16 byte input for stage 4 of the key ladder. + * + * @return The status of the operation. + */ +Sec_Result SecKey_Derive_KeyLadderAes128(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id_derived, + Sec_StorageLoc loc_derived, Sec_KeyLadderRoot root, SEC_BYTE* input1, SEC_BYTE* input2, SEC_BYTE* input3, + SEC_BYTE* input4) { + CHECK_PROCHANDLE(processorHandle) + + SEC_SIZE key_length = SecKey_GetKeyLenForKeyType(SEC_KEYTYPE_AES_128); + Sec_Key key; + if (derive_root_key_ladder(input1, input2, input3, input4, key_length, &key.handle, SEC_KEYTYPE_AES_128) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Derive_root_key_ladder failed"); + return SEC_RESULT_FAILURE; + } + + return prepare_and_store_key_data(processorHandle, loc_derived, object_id_derived, &key, + SEC_KEYCONTAINER_RAW_AES_128, NULL, 0); +} + +Sec_Result SecKey_Derive_CMAC_AES128(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID idDerived, + Sec_KeyType typeDerived, Sec_StorageLoc locDerived, SEC_OBJECTID derivationKey, SEC_BYTE* otherData, + SEC_SIZE otherDataSize, SEC_BYTE* counter, SEC_SIZE counterSize) { + Sec_KeyHandle* keyHandle; + + Sec_Result result = SecKey_GetInstance(processorHandle, derivationKey, &keyHandle); + if (result != SEC_RESULT_SUCCESS) + return result; + + Sec_Key key; + result = derive_kdf_cmac(typeDerived, otherData, otherDataSize, counter, counterSize, keyHandle->key.handle, + &key.handle); + SecKey_Release(keyHandle); + if (result != SEC_RESULT_SUCCESS) + return result; + + return prepare_and_store_key_data(processorHandle, locDerived, idDerived, &key, + SecKey_GetClearContainer(typeDerived), NULL, 0); +} + +/** + * @brief Obtain a digest value computed over the base key contents. + * + * @param processorHandle secure processor handle. + * @param nonce client nonce. + * @param alg digest algorithm. + * @param digest output digest value. + * @param digest_len the length of output digest value. + * + * @return status of the operation. + */ +Sec_Result SecKey_ComputeBaseKeyDigest(Sec_ProcessorHandle* processorHandle, SEC_BYTE* nonce, + Sec_DigestAlgorithm alg, SEC_BYTE* digest, SEC_SIZE* digest_len) { + + if (SecKey_Derive_BaseKey(processorHandle, SEC_OBJECTID_BASE_KEY_AES, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + nonce) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_BaseKey failed"); + return SEC_RESULT_FAILURE; + } + + if (SecDigest_SingleInputWithKeyId(processorHandle, alg, SEC_OBJECTID_BASE_KEY_AES, digest, digest_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_SingleInputWithKeyId failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Generates a shared symmetric key and stores it in a specified location. + * + * A shared secret is calculated using the ECDH algorithm. The shared + * secret is converted to a key using the Concat KDF (SP800-56A Section + * 5.8.1). If the key with the same id already exists, the call will + * overwrite the existing key with the new key. SHA-256 is the digest + * algorithm. + * + * @param keyHandle Handle of my private ECC key. + * @param otherPublicKey Public key for other party in key agreement. + * @param type_derived Type of key to generate. Only symmetric keys can be derived. + * @param id_derived 64-bit object id identifying the key to be generated. + * @param loc_id Location where the resulting key will be stored. + * @param digestAlgorithm Digest algorithm to use in KDF (typically SEC_DIGESTALGORITHM_SHA256). + * @param otherInfo Input keying material + * AlgorithmID || PartyUInfo || PartyVInfo) {|| SuppPubInfo }{|| SuppPrivInfo}. + * @param otherInfoSize Size of otherInfo (in bytes). + */ +Sec_Result SecKey_ECDHKeyAgreementWithKDF(Sec_KeyHandle* keyHandle, Sec_ECCRawPublicKey* otherPublicKey, + Sec_KeyType type_derived, SEC_OBJECTID id_derived, Sec_StorageLoc loc_derived, Sec_Kdf kdf, + Sec_DigestAlgorithm digestAlgorithm, const SEC_BYTE* otherInfo, SEC_SIZE otherInfoSize) { + if (kdf != SEC_KDF_CONCAT) { + SEC_LOG_ERROR("Invalid kdf parameter encountered: %d", kdf); + return SEC_RESULT_INVALID_PARAMETERS; + } + + if (otherPublicKey->type != SEC_KEYTYPE_ECC_NISTP256_PUBLIC && + otherPublicKey->type != SEC_KEYTYPE_ECC_NISTP256) { + SEC_LOG_ERROR("Can only exchange ECC keys"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + if (!SecKey_IsSymetric(type_derived)) { + SEC_LOG_ERROR("Can only derive symetric keys"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + // Derive the shared secret with a key exchange. + sa_key shared_secret; + sa_rights rights; + rights_set_allow_all(&rights, type_derived); + + size_t other_public_length = Sec_BEBytesToUint32(otherPublicKey->key_len); + SEC_BYTE other_public[other_public_length * 2]; + memcpy(other_public, otherPublicKey->x, other_public_length); + memcpy(other_public + other_public_length, otherPublicKey->y, other_public_length); + + sa_status status = sa_key_exchange(&shared_secret, &rights, SA_KEY_EXCHANGE_ALGORITHM_ECDH, + keyHandle->key.handle, other_public, other_public_length * 2, NULL); + CHECK_STATUS(status) + + // Derive the key from the shared secret using a key derivation algorithm. + if (digestAlgorithm != SEC_DIGESTALGORITHM_SHA1 && digestAlgorithm != SEC_DIGESTALGORITHM_SHA256) { + sa_key_release(shared_secret); + SEC_LOG_ERROR("Unsupported digest algorithm specified: %d", digestAlgorithm); + return SEC_RESULT_INVALID_PARAMETERS; + } + + sa_digest_algorithm digest_algorithm = + (digestAlgorithm == SEC_DIGESTALGORITHM_SHA1) ? SA_DIGEST_ALGORITHM_SHA1 : SA_DIGEST_ALGORITHM_SHA256; + + sa_kdf_algorithm kdf_algorithm; + void* parameters; + sa_kdf_parameters_hkdf hkdf_parameters; + sa_kdf_parameters_concat concat_parameters; + sa_kdf_parameters_ansi_x963 x963_parameters; + switch (kdf) { + case SEC_KDF_HKDF: + kdf_algorithm = SA_KDF_ALGORITHM_HKDF; + hkdf_parameters.key_length = SecKey_GetKeyLenForKeyType(type_derived); + hkdf_parameters.digest_algorithm = digest_algorithm; + hkdf_parameters.parent = shared_secret; + hkdf_parameters.salt = NULL; + hkdf_parameters.salt_length = 0; + hkdf_parameters.info = otherInfo; + hkdf_parameters.info_length = otherInfoSize; + parameters = &hkdf_parameters; + break; + + case SEC_KDF_CONCAT: + kdf_algorithm = SA_KDF_ALGORITHM_CONCAT; + concat_parameters.key_length = SecKey_GetKeyLenForKeyType(type_derived); + concat_parameters.digest_algorithm = digest_algorithm; + concat_parameters.parent = shared_secret; + concat_parameters.info = otherInfo; + concat_parameters.info_length = otherInfoSize; + parameters = &concat_parameters; + break; + + case SEC_KDF_ANSI_X_9_63: + kdf_algorithm = SA_KDF_ALGORITHM_ANSI_X963; + x963_parameters.key_length = SecKey_GetKeyLenForKeyType(type_derived); + x963_parameters.digest_algorithm = digest_algorithm; + x963_parameters.parent = shared_secret; + x963_parameters.info = otherInfo; + x963_parameters.info_length = otherInfoSize; + parameters = &x963_parameters; + break; + + default: + sa_key_release(shared_secret); + return SEC_RESULT_INVALID_PARAMETERS; + } + + Sec_Key key; + status = sa_key_derive(&key.handle, &rights, kdf_algorithm, parameters); + sa_key_release(shared_secret); + CHECK_STATUS(status) + + return prepare_and_store_key_data(keyHandle->processorHandle, loc_derived, id_derived, &key, + SecKey_GetClearContainer(type_derived), NULL, 0); +} + +Sec_Result SecKey_Derive_BaseKey(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID idDerived, Sec_KeyType key_type, + Sec_StorageLoc loc, SEC_BYTE* nonce) { + CHECK_PROCHANDLE(processorHandle) + + Sec_Key key; + Sec_Result result = derive_base_key(processorHandle, nonce, &key.handle, key_type); + if (result != SEC_RESULT_SUCCESS) + return result; + + return prepare_and_store_key_data(processorHandle, loc, idDerived, &key, SecKey_GetClearContainer(key_type), NULL, + 0); +} + +Sec_Result SecKey_Derive_HKDF_BaseKey(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID idDerived, + Sec_KeyType typeDerived, Sec_StorageLoc locDerived, Sec_MacAlgorithm macAlgorithm, SEC_BYTE* salt, + SEC_SIZE saltSize, SEC_BYTE* info, SEC_SIZE infoSize, SEC_OBJECTID baseKeyId) { + Sec_KeyHandle* keyHandle; + + Sec_Result result = SecKey_GetInstance(processorHandle, baseKeyId, &keyHandle); + if (result != SEC_RESULT_SUCCESS) { + return result; + } + + Sec_Key key; + result = derive_hkdf(macAlgorithm, typeDerived, salt, saltSize, info, infoSize, keyHandle->key.handle, + &key.handle); + SecKey_Release(keyHandle); + if (result != SEC_RESULT_SUCCESS) + return result; + + return prepare_and_store_key_data(processorHandle, locDerived, idDerived, &key, + SecKey_GetClearContainer(typeDerived), NULL, 0); +} + +Sec_Result SecKey_Derive_ConcatKDF_BaseKey(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID idDerived, + Sec_KeyType typeDerived, Sec_StorageLoc locDerived, Sec_DigestAlgorithm digestAlgorithm, SEC_BYTE* otherInfo, + SEC_SIZE otherInfoSize, SEC_OBJECTID baseKeyId) { + Sec_KeyHandle* keyHandle; + + Sec_Result result = SecKey_GetInstance(processorHandle, baseKeyId, &keyHandle); + if (result != SEC_RESULT_SUCCESS) { + return result; + } + + Sec_Key key; + result = derive_kdf_concat(digestAlgorithm, typeDerived, otherInfo, otherInfoSize, keyHandle->key.handle, + &key.handle); + SecKey_Release(keyHandle); + if (result != SEC_RESULT_SUCCESS) + return result; + + return prepare_and_store_key_data(processorHandle, locDerived, idDerived, &key, + SecKey_GetClearContainer(typeDerived), NULL, 0); +} + +/** + * @brief compute inputs for the base key ladder. + */ +Sec_Result SecKey_ComputeBaseKeyLadderInputs(Sec_ProcessorHandle* processorHandle, + const char* inputDerivationStr, const char* cipherAlgorithmStr, SEC_BYTE* nonce, + Sec_DigestAlgorithm digestAlgorithm, SEC_SIZE inputSize, SEC_BYTE* c1, SEC_BYTE* c2, SEC_BYTE* c3, + SEC_BYTE* c4) { + int i; + SEC_BYTE loop[] = {0, 0, 0, 0}; + SEC_BYTE digest[SEC_DIGEST_MAX_LEN]; + SEC_SIZE digest_len; + Sec_Result result = SEC_RESULT_FAILURE; + SEC_BYTE* c[4] = {c1, c2, c3, c4}; + CHECK_PROCHANDLE(processorHandle) + + if (inputSize > SecDigest_GetDigestLenForAlgorithm(digestAlgorithm)) { + SEC_LOG_ERROR("Invalid input size for specified digest algorithm"); + return SEC_RESULT_FAILURE; + } + + SEC_SIZE buffer_len = SEC_NONCE_LEN + strlen(inputDerivationStr) + strlen(cipherAlgorithmStr) + sizeof(loop); + SEC_BYTE input_buffer[buffer_len]; + + for (i = 1; i <= 4; i++) { + loop[3] = i; + SEC_SIZE offset = 0; + memcpy(input_buffer + offset, nonce, SEC_NONCE_LEN); + offset += SEC_NONCE_LEN; + memcpy(input_buffer + offset, inputDerivationStr, strlen(inputDerivationStr)); // NOLINT + offset += strlen(inputDerivationStr); + memcpy(input_buffer + offset, cipherAlgorithmStr, strlen(cipherAlgorithmStr)); // NOLINT + offset += strlen(cipherAlgorithmStr); + memcpy(input_buffer + offset, loop, sizeof(loop)); + offset += sizeof(loop); + + result = SecDigest_SingleInput(processorHandle, digestAlgorithm, input_buffer, offset, digest, &digest_len); + if (result != SEC_RESULT_SUCCESS) + return result; + + memcpy(c[i - 1], digest, inputSize); + } + + return result; +} + +/** + * @brief Delete a provisioned key. + * + * @param processorHandle secure processor handle. + * @param object_id id of the key to delete. + * + * @return The status of the operation. + */ +Sec_Result SecKey_Delete(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id) { + Sec_RAMKeyData* ram_key = NULL; + Sec_RAMKeyData* ram_key_parent = NULL; + SEC_SIZE keys_found = 0; + SEC_SIZE keys_deleted = 0; + CHECK_PROCHANDLE(processorHandle) + + /* ram */ + find_ram_key_data(processorHandle, object_id, &ram_key, &ram_key_parent); + if (ram_key != NULL) { + if (ram_key_parent == NULL) + processorHandle->ram_keys = ram_key->next; + else + ram_key_parent->next = ram_key->next; + + Sec_Memset(ram_key, 0, sizeof(Sec_RAMKeyData)); + + SEC_FREE(ram_key); + + ++keys_found; + ++keys_deleted; + } + + /* file system */ + if (processorHandle->app_dir != NULL) { + char file_name[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name, sizeof(file_name), SEC_KEY_FILENAME_PATTERN, processorHandle->app_dir, object_id); + if (SecUtils_FileExists(file_name)) { + SecUtils_RmFile(file_name); + ++keys_found; + + if (!SecUtils_FileExists(file_name)) + ++keys_deleted; + } + + char file_name_info[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name_info, sizeof(file_name_info), SEC_KEYINFO_FILENAME_PATTERN, processorHandle->app_dir, + object_id); + if (!SecUtils_FileExists(file_name) && SecUtils_FileExists(file_name_info)) { + SecUtils_RmFile(file_name_info); + } + + char file_name_verification[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name_verification, sizeof(file_name_verification), SEC_VERIFICATION_FILENAME_PATTERN, + processorHandle->app_dir, object_id); + if (!SecUtils_FileExists(file_name) && SecUtils_FileExists(file_name_verification)) { + SecUtils_RmFile(file_name_verification); + } + } + + if (keys_found == 0) + return SEC_RESULT_NO_SUCH_ITEM; + + if (keys_found != keys_deleted) + return SEC_RESULT_ITEM_NON_REMOVABLE; + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Release the key object. + * + * @param keyHandle key handle to release. + * + * @return The status of the operation. + */ +Sec_Result SecKey_Release(Sec_KeyHandle* keyHandle) { + CHECK_HANDLE(keyHandle) + + switch (keyHandle->key_type) { + case SEC_KEYTYPE_ECC_NISTP256_PUBLIC: + SEC_ECC_FREE(keyHandle->key.ec_key); + break; + + case SEC_KEYTYPE_RSA_1024_PUBLIC: + case SEC_KEYTYPE_RSA_2048_PUBLIC: + case SEC_KEYTYPE_RSA_3072_PUBLIC: + SEC_RSA_FREE(keyHandle->key.rsa); + break; + + default: + sa_key_release(keyHandle->key.handle); + break; + } + + SEC_FREE(keyHandle); + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Obtain a processor handle. + * + * @param keyHandle key handle. + * + * @return Processor handle. + */ +Sec_ProcessorHandle* SecKey_GetProcessor(Sec_KeyHandle* keyHandle) { + if (keyHandle != NULL) + return keyHandle->processorHandle; + + return NULL; +} + +Sec_Result SecKey_ExportKey(Sec_KeyHandle* keyHandle, SEC_BYTE* derivationInput, SEC_BYTE* exportedKey, + SEC_SIZE keyBufferLen, SEC_SIZE* keyBytesWritten) { + CHECK_HANDLE(keyHandle) + + if (keyHandle->key_type == SEC_KEYTYPE_RSA_1024_PUBLIC || + keyHandle->key_type == SEC_KEYTYPE_RSA_2048_PUBLIC || + keyHandle->key_type == SEC_KEYTYPE_RSA_3072_PUBLIC || + keyHandle->key_type == SEC_KEYTYPE_ECC_NISTP256_PUBLIC) + return SEC_RESULT_FAILURE; + + return export_key(&keyHandle->key, derivationInput, exportedKey, keyBufferLen, keyBytesWritten); +} + +Sec_KeyType SecKey_GetRSAKeyTypeForBitLength(int numBits) { + switch (numBits) { + case 1024: + return SEC_KEYTYPE_RSA_1024; + + case 2048: + return SEC_KEYTYPE_RSA_2048; + + case 3072: + return SEC_KEYTYPE_RSA_3072; + + default: + SEC_LOG_ERROR("Invalid numBits encountered: %d", numBits); + return SEC_KEYTYPE_NUM; + } +} + +Sec_KeyType SecKey_GetRSAKeyTypeForByteLength(int numBytes) { + switch (numBytes) { + case 128: + return SEC_KEYTYPE_RSA_1024; + + case 256: + return SEC_KEYTYPE_RSA_2048; + + case 384: + return SEC_KEYTYPE_RSA_3072; + + default: + SEC_LOG_ERROR("Invalid numBytes encountered: %d", numBytes); + return SEC_KEYTYPE_NUM; + } +} + +/** + * @brief Checks if a passed in key type is symmetric. + * + * @param type key type. + * + * @return 1 if key type is symmetric, 0 if asymmetric. + */ +SEC_BOOL SecKey_IsSymetric(Sec_KeyType type) { + switch (type) { + case SEC_KEYTYPE_AES_128: + case SEC_KEYTYPE_AES_256: + case SEC_KEYTYPE_HMAC_128: + case SEC_KEYTYPE_HMAC_160: + case SEC_KEYTYPE_HMAC_256: + return SEC_TRUE; + + default: + break; + } + + return SEC_FALSE; +} + +/** + * @brief Checks if a passed in key type is an AES key. + * + * @param type key type. + * + * @return 1 if key type is AES, 0 if not. + */ +SEC_BOOL SecKey_IsAES(Sec_KeyType type) { + switch (type) { + case SEC_KEYTYPE_AES_128: + case SEC_KEYTYPE_AES_256: + return SEC_TRUE; + + default: + break; + } + + return SEC_FALSE; +} + +SEC_BOOL SecKey_IsHMAC(Sec_KeyType type) { + switch (type) { + case SEC_KEYTYPE_HMAC_128: + case SEC_KEYTYPE_HMAC_160: + case SEC_KEYTYPE_HMAC_256: + return SEC_TRUE; + + default: + break; + } + + return SEC_FALSE; +} + +/** + * @brief Checks if a passed in key type is RSA. + * + * @param type key type. + * + * @return 1 if key type is rsa, 0 otherwise. + */ +SEC_BOOL SecKey_IsRsa(Sec_KeyType type) { + switch (type) { + case SEC_KEYTYPE_RSA_1024: + case SEC_KEYTYPE_RSA_1024_PUBLIC: + case SEC_KEYTYPE_RSA_2048: + case SEC_KEYTYPE_RSA_2048_PUBLIC: + case SEC_KEYTYPE_RSA_3072: + case SEC_KEYTYPE_RSA_3072_PUBLIC: + return SEC_TRUE; + + default: + break; + } + + return SEC_FALSE; +} + +/** + * @brief Checks if a passed in key type is pub RSA. + * + * @param type key type. + * + * @return 1 if key type is pub rsa, 0 otherwise. + */ +SEC_BOOL SecKey_IsPubRsa(Sec_KeyType type) { + switch (type) { + case SEC_KEYTYPE_RSA_1024_PUBLIC: + case SEC_KEYTYPE_RSA_2048_PUBLIC: + case SEC_KEYTYPE_RSA_3072_PUBLIC: + return SEC_TRUE; + + default: + break; + } + + return SEC_FALSE; +} + +/** + * @brief Checks if a passed in key type is priv RSA. + * + * @param type key type. + * + * @return 1 if key type is priv rsa, 0 otherwise. + */ +SEC_BOOL SecKey_IsPrivRsa(Sec_KeyType type) { + switch (type) { + case SEC_KEYTYPE_RSA_1024: + case SEC_KEYTYPE_RSA_2048: + case SEC_KEYTYPE_RSA_3072: + return SEC_TRUE; + + default: + break; + } + + return SEC_FALSE; +} + +/** + * @brief Checks if a passed in key type is ECC. + * + * @param type key type. + * + * @return 1 if key type is priv ECC, 0 otherwise. + */ +SEC_BOOL SecKey_IsEcc(Sec_KeyType type) { + switch (type) { + case SEC_KEYTYPE_ECC_NISTP256: + case SEC_KEYTYPE_ECC_NISTP256_PUBLIC: + return SEC_TRUE; + + default: + break; + } + + return SEC_FALSE; +} + +/** + * @brief Checks if a passed in key type is priv ECC. + * + * @param type key type. + * + * @return 1 if key type is priv ECC, 0 otherwise. + */ +SEC_BOOL SecKey_IsPrivEcc(Sec_KeyType type) { + if (type == SEC_KEYTYPE_ECC_NISTP256) + return SEC_TRUE; + + return SEC_FALSE; +} + +/** + * @brief Checks if a passed in key type is pub ECC. + * + * @param type key type. + * + * @return 1 if key type is pub ECC, 0 otherwise. + */ +SEC_BOOL SecKey_IsPubEcc(Sec_KeyType type) { + if (type == SEC_KEYTYPE_ECC_NISTP256_PUBLIC) + return SEC_TRUE; + + return SEC_FALSE; +} + +/** + * @brief Is the specified container a raw (clear) container. + */ +SEC_BOOL SecKey_IsClearKeyContainer(Sec_KeyContainer kct) { + switch (kct) { + case SEC_KEYCONTAINER_RAW_AES_128: + case SEC_KEYCONTAINER_RAW_AES_256: + case SEC_KEYCONTAINER_RAW_HMAC_128: + case SEC_KEYCONTAINER_RAW_HMAC_160: + case SEC_KEYCONTAINER_RAW_HMAC_256: + case SEC_KEYCONTAINER_RAW_RSA_1024: + case SEC_KEYCONTAINER_RAW_RSA_2048: + case SEC_KEYCONTAINER_RAW_RSA_3072: + case SEC_KEYCONTAINER_RAW_RSA_1024_PUBLIC: + case SEC_KEYCONTAINER_RAW_RSA_2048_PUBLIC: + case SEC_KEYCONTAINER_RAW_RSA_3072_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_1024: + case SEC_KEYCONTAINER_PEM_RSA_2048: + case SEC_KEYCONTAINER_PEM_RSA_3072: + case SEC_KEYCONTAINER_PEM_RSA_1024_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_2048_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_3072_PUBLIC: + case SEC_KEYCONTAINER_RAW_ECC_NISTP256: + case SEC_KEYCONTAINER_RAW_ECC_NISTP256_PUBLIC: + return SEC_TRUE; + + default: + return SEC_FALSE; + } +} + +/** + * @brief Obtain a key container type for a specified key type. + * + * @param key_type key type. + * @return key container type. + */ +Sec_KeyContainer SecKey_GetClearContainer(Sec_KeyType key_type) { + switch (key_type) { + case SEC_KEYTYPE_AES_128: + return SEC_KEYCONTAINER_RAW_AES_128; + + case SEC_KEYTYPE_AES_256: + return SEC_KEYCONTAINER_RAW_AES_256; + + case SEC_KEYTYPE_HMAC_128: + return SEC_KEYCONTAINER_RAW_HMAC_128; + + case SEC_KEYTYPE_HMAC_160: + return SEC_KEYCONTAINER_RAW_HMAC_160; + + case SEC_KEYTYPE_HMAC_256: + return SEC_KEYCONTAINER_RAW_HMAC_256; + + case SEC_KEYTYPE_RSA_1024: + return SEC_KEYCONTAINER_DER_RSA_1024; + + case SEC_KEYTYPE_RSA_2048: + return SEC_KEYCONTAINER_DER_RSA_2048; + + case SEC_KEYTYPE_RSA_3072: + return SEC_KEYCONTAINER_DER_RSA_3072; + + case SEC_KEYTYPE_RSA_1024_PUBLIC: + return SEC_KEYCONTAINER_DER_RSA_1024_PUBLIC; + + case SEC_KEYTYPE_RSA_2048_PUBLIC: + return SEC_KEYCONTAINER_DER_RSA_2048_PUBLIC; + + case SEC_KEYTYPE_RSA_3072_PUBLIC: + return SEC_KEYCONTAINER_DER_RSA_3072_PUBLIC; + + case SEC_KEYTYPE_ECC_NISTP256: + return SEC_KEYCONTAINER_RAW_ECC_PRIVONLY_NISTP256; + + case SEC_KEYTYPE_ECC_NISTP256_PUBLIC: + return SEC_KEYCONTAINER_DER_ECC_NISTP256_PUBLIC; + + default: + return SEC_KEYCONTAINER_NUM; + } +} + +/** + * @brief Find if the key with a specific id has been provisioned. + * + * @param processorHandle secure processor handle. + * @param object_id id of the certificate. + * + * @return 1 if an object has been provisioned, 0 if it has not been. + */ +SEC_BOOL SecKey_IsProvisioned(Sec_ProcessorHandle* processorHandle, + SEC_OBJECTID object_id) { + Sec_KeyHandle* keyHandle; + + if (SEC_OBJECTID_INVALID == object_id) + return SEC_FALSE; + + if (SecKey_GetInstance(processorHandle, object_id, &keyHandle) != SEC_RESULT_SUCCESS) + return SEC_FALSE; + + SecKey_Release(keyHandle); + return SEC_TRUE; +} + +/** + * @brief finds the first available key id in the range passed in. + * + * @param proc secure processor. + * @param base bottom of the range to search. + * @param top top of the range to search. + * @return + */ +SEC_OBJECTID SecKey_ObtainFreeObjectId(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID base, SEC_OBJECTID top) { + SEC_OBJECTID id; + Sec_KeyHandle* keyHandle; + Sec_Result result; + + for (id = base; id < top; ++id) { + result = SecKey_GetInstance(processorHandle, id, &keyHandle); + if (result == SEC_RESULT_SUCCESS) + SecKey_Release(keyHandle); + else + return id; + } + + return SEC_OBJECTID_INVALID; +} + +/** + * @brief Get the type (MSB byte) of the object id. + */ +uint8_t SecKey_GetObjectType(SEC_OBJECTID object_id) { + return (SEC_BYTE) ((object_id & 0xff00000000000000ULL) >> 56); +} + +/** + * @brief Obtain a digest value computed over a specified key. + * + * @param proc secure processor handle. + * @param key_id key id. + * @param alg digest algorithm to use. + * @param digest output digest value. + * @param digest_len size of the written digest value. + * @return + */ +Sec_Result SecKey_ComputeKeyDigest(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID key_id, Sec_DigestAlgorithm alg, + SEC_BYTE* digest, SEC_SIZE* digest_len) { + Sec_KeyHandle* keyHandle = NULL; + Sec_DigestHandle* digestHandle = NULL; + Sec_Result result = SEC_RESULT_FAILURE; + do { + if (SecKey_GetInstance(processorHandle, key_id, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance returned error"); + break; + } + + if (SecDigest_GetInstance(processorHandle, alg, &digestHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_GetInstance returned error"); + break; + } + + if (SecDigest_UpdateWithKey(digestHandle, keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_UpdateWithKey returned error"); + break; + } + + result = SecDigest_Release(digestHandle, digest, digest_len); + digestHandle = NULL; + } while (SEC_FALSE); + + if (keyHandle != NULL) + SecKey_Release(keyHandle); + + if (digestHandle != NULL) + SecDigest_Release(digestHandle, digest, digest_len); + + return result; +} + +Sec_Result SecKey_ExtractWrappedKeyParamsAsn1V3(Sec_Asn1KC* kc, SEC_BYTE* payload, SEC_SIZE payloadLen, + SEC_SIZE* written, Sec_KeyType* wrappedKeyType, SEC_OBJECTID* wrappingId, SEC_BYTE* wrappingIv, + Sec_CipherAlgorithm* wrappingAlg, SEC_SIZE* key_offset, SEC_BYTE* wrappingKey, SEC_SIZE wrappingKeyLen, + SEC_SIZE* writtenWrappingKey) { + uint64_t ulong_val; + SEC_SIZE written_iv; + + *writtenWrappingKey = 0; + *wrappingId = 0; + + if (kc == NULL) + return SEC_RESULT_FAILURE; + + if (SecAsn1KC_GetAttrBuffer(kc, SEC_ASN1KC_WRAPPEDKEY, payload, payloadLen, written) != SEC_RESULT_SUCCESS) { + SEC_TRACE(SEC_TRACE_UNWRAP, "SecAsn1KC_GetAttrBuffer SEC_ASN1KC_WRAPPEDKEY failed"); + return SEC_RESULT_FAILURE; + } + + if (SecAsn1KC_GetAttrUlong(kc, SEC_ASN1KC_WRAPPEDKEYTYPEID, &ulong_val) != SEC_RESULT_SUCCESS) { + SEC_TRACE(SEC_TRACE_UNWRAP, "SecAsn1KC_GetAttrBuffer SEC_ASN1KC_WRAPPEDKEYTYPEID failed"); + return SEC_RESULT_FAILURE; + } + + *wrappedKeyType = (Sec_KeyType) ulong_val; + + if (SecAsn1KC_HasAttr(kc, SEC_ASN1KC_WRAPPEDKEYOFFSET)) { + if (SecAsn1KC_GetAttrUlong(kc, SEC_ASN1KC_WRAPPEDKEYOFFSET, &ulong_val) != SEC_RESULT_SUCCESS) { + SEC_TRACE(SEC_TRACE_UNWRAP, "SecAsn1KC_GetAttrBuffer SEC_ASN1KC_WRAPPEDKEYOFFSET failed"); + return SEC_RESULT_FAILURE; + } + + *key_offset = (SEC_SIZE) ulong_val; + } else + *key_offset = (SEC_SIZE) 0; // default value + + if (SecAsn1KC_HasAttr(kc, SEC_ASN1KC_WRAPPINGIV) && + SecAsn1KC_GetAttrBuffer(kc, SEC_ASN1KC_WRAPPINGIV, wrappingIv, SEC_AES_BLOCK_SIZE, &written_iv) != + SEC_RESULT_SUCCESS) { + SEC_TRACE(SEC_TRACE_UNWRAP, "SecAsn1KC_GetAttrBuffer SEC_ASN1KC_WRAPPEDKEYOFFSET failed"); + return SEC_RESULT_FAILURE; + } + + if (SecAsn1KC_GetAttrUlong(kc, SEC_ASN1KC_WRAPPINGALGORITHMID, &ulong_val) != SEC_RESULT_SUCCESS) { + SEC_TRACE(SEC_TRACE_UNWRAP, "SecAsn1KC_GetAttrBuffer SEC_ASN1KC_WRAPPINGALGORITHMID failed"); + return SEC_RESULT_FAILURE; + } + + *wrappingAlg = (Sec_CipherAlgorithm) ulong_val; + + if (SecAsn1KC_HasAttr(kc, SEC_ASN1KC_WRAPPINGKEY)) { + if (SecAsn1KC_GetAttrBuffer(kc, SEC_ASN1KC_WRAPPINGKEY, wrappingKey, wrappingKeyLen, writtenWrappingKey) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_GetAttrBuffer SEC_ASN1KC_WRAPPINGKEY failed"); + return SEC_RESULT_FAILURE; + } + } else { + if (SecAsn1KC_GetAttrUint64(kc, SEC_ASN1KC_WRAPPINGKEYID, wrappingId) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_GetAttrBuffer SEC_ASN1KC_WRAPPINGKEYID failed"); + return SEC_RESULT_FAILURE; + } + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecKey_ExtractWrappedKeyParamsAsn1BufferOff(SEC_BYTE* asn1, SEC_SIZE asn1_len, SEC_BYTE* payload, + SEC_SIZE payloadLen, SEC_SIZE* written, Sec_KeyType* wrappedKeyType, SEC_OBJECTID* wrappingId, + SEC_BYTE* wrappingIv, Sec_CipherAlgorithm* wrappingAlg, SEC_SIZE* key_offset) { + Sec_Asn1KC* asn1kc = NULL; + Sec_Result result = SEC_RESULT_FAILURE; + + asn1kc = SecAsn1KC_Decode(asn1, asn1_len); + if (asn1kc == NULL) { + SEC_LOG_ERROR("SecAsn1KC_Decode failed"); + SecAsn1KC_Free(asn1kc); + return result; + } + + result = SecKey_ExtractWrappedKeyParamsAsn1Off(asn1kc, payload, payloadLen, written, wrappedKeyType, wrappingId, + wrappingIv, wrappingAlg, key_offset); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExtractWrappedKeyParamsAsn1Off failed"); + SecAsn1KC_Free(asn1kc); + return result; + } + + SecAsn1KC_Free(asn1kc); + return SEC_RESULT_SUCCESS; +} + +#ifndef SEC_COMMON_17 + +Sec_Result SecKey_GenerateWrappedKeyAsn1(SEC_BYTE* wrappedKey, SEC_SIZE wrappedKeyLen, Sec_KeyType wrappedKeyType, + SEC_OBJECTID wrappingKeyId, SEC_BYTE* wrappingIv, Sec_CipherAlgorithm wrappingAlgorithm, SEC_BYTE* output, + SEC_SIZE output_len, SEC_SIZE* written) { + Sec_Asn1KC* asn1kc = NULL; + Sec_Result result = SEC_RESULT_FAILURE; + + asn1kc = SecAsn1KC_Alloc(); + if (SecAsn1KC_AddAttrBuffer(asn1kc, SEC_ASN1KC_WRAPPEDKEY, wrappedKey, wrappedKeyLen) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrBuffer failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (SecAsn1KC_AddAttrUlong(asn1kc, SEC_ASN1KC_WRAPPEDKEYTYPEID, wrappedKeyType) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrUlong failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (SecAsn1KC_AddAttrUint64(asn1kc, SEC_ASN1KC_WRAPPINGKEYID, wrappingKeyId) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrUint64 failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (wrappingIv != NULL) { + if (SecAsn1KC_AddAttrBuffer(asn1kc, SEC_ASN1KC_WRAPPINGIV, wrappingIv, SEC_AES_BLOCK_SIZE) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrBuffer failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + } + + if (SecAsn1KC_AddAttrUlong(asn1kc, SEC_ASN1KC_WRAPPINGALGORITHMID, wrappingAlgorithm) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrUlong failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (SecAsn1KC_Encode(asn1kc, output, output_len, written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_Encode failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecKey_GenerateWrappedKeyAsn1Off(SEC_BYTE* payload, SEC_SIZE payloadLen, Sec_KeyType wrappedKeyType, + SEC_OBJECTID wrappingKeyId, SEC_BYTE* wrappingIv, Sec_CipherAlgorithm wrappingAlgorithm, SEC_BYTE* output, + SEC_SIZE output_len, SEC_SIZE* written, SEC_SIZE key_offset) { + Sec_Asn1KC* asn1kc = NULL; + Sec_Result result = SEC_RESULT_FAILURE; + + asn1kc = SecAsn1KC_Alloc(); + if (SecAsn1KC_AddAttrBuffer(asn1kc, SEC_ASN1KC_WRAPPEDKEY, payload, payloadLen) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrBuffer failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (SecAsn1KC_AddAttrUlong(asn1kc, SEC_ASN1KC_WRAPPEDKEYTYPEID, wrappedKeyType) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrUlong failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (SecAsn1KC_AddAttrUint64(asn1kc, SEC_ASN1KC_WRAPPINGKEYID, wrappingKeyId) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrUint64 failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (wrappingIv != NULL) { + if (SecAsn1KC_AddAttrBuffer(asn1kc, SEC_ASN1KC_WRAPPINGIV, wrappingIv, SEC_AES_BLOCK_SIZE) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrBuffer failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + } + + if ((key_offset + SecKey_GetKeyLenForKeyType(wrappedKeyType)) > payloadLen) { + SEC_LOG_ERROR("Illegal key_offset %ld", (long) key_offset); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (SecAsn1KC_AddAttrUlong(asn1kc, SEC_ASN1KC_WRAPPEDKEYOFFSET, key_offset) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrUlong failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (SecAsn1KC_AddAttrUlong(asn1kc, SEC_ASN1KC_WRAPPINGALGORITHMID, wrappingAlgorithm) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrUlong failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (SecAsn1KC_Encode(asn1kc, output, output_len, written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_Encode failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecKey_GenerateWrappedKeyAsn1V3(SEC_BYTE* payload, SEC_SIZE payloadLen, Sec_KeyType wrappedKeyType, + SEC_BYTE* wrappingKey, SEC_SIZE wrappingKeyLen, SEC_BYTE* wrappingIv, + Sec_CipherAlgorithm wrappingAlgorithm, SEC_BYTE* output, SEC_SIZE output_len, + SEC_SIZE* written, SEC_SIZE key_offset) { + Sec_Asn1KC* asn1kc = NULL; + Sec_Result result = SEC_RESULT_FAILURE; + + asn1kc = SecAsn1KC_Alloc(); + if (SecAsn1KC_AddAttrBuffer(asn1kc, SEC_ASN1KC_WRAPPEDKEY, payload, payloadLen) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrBuffer failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (SecAsn1KC_AddAttrUlong(asn1kc, SEC_ASN1KC_WRAPPEDKEYTYPEID, wrappedKeyType) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrUlong failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (SecAsn1KC_AddAttrBuffer(asn1kc, SEC_ASN1KC_WRAPPINGKEY, wrappingKey, wrappingKeyLen) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrBuffer failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (wrappingIv != NULL) { + if (SecAsn1KC_AddAttrBuffer(asn1kc, SEC_ASN1KC_WRAPPINGIV, wrappingIv, SEC_AES_BLOCK_SIZE) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrBuffer failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + } + + if ((key_offset + SecKey_GetKeyLenForKeyType(wrappedKeyType)) > payloadLen) { + SEC_LOG_ERROR("Illegal key_offset %ld", (long) key_offset); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (SecAsn1KC_AddAttrUlong(asn1kc, SEC_ASN1KC_WRAPPEDKEYOFFSET, key_offset) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrUlong failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (SecAsn1KC_AddAttrUlong(asn1kc, SEC_ASN1KC_WRAPPINGALGORITHMID, wrappingAlgorithm) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_AddAttrUlong failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (SecAsn1KC_Encode(asn1kc, output, output_len, written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecAsn1KC_Encode failed"); + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return result; + } + + if (asn1kc != NULL) { + SecAsn1KC_Free(asn1kc); + asn1kc = NULL; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecKey_ExtractWrappedKeyParamsAsn1Off(Sec_Asn1KC* kc, SEC_BYTE* payload, SEC_SIZE payloadLen, + SEC_SIZE* written, Sec_KeyType* wrappedKeyType, SEC_OBJECTID* wrappingId, SEC_BYTE* wrappingIv, + Sec_CipherAlgorithm* wrappingAlg, SEC_SIZE* key_offset) { + uint64_t ulong_val; + SEC_SIZE written_iv; + + if (kc == NULL) + return SEC_RESULT_FAILURE; + + if (SecAsn1KC_GetAttrBuffer(kc, SEC_ASN1KC_WRAPPEDKEY, payload, payloadLen, written) != SEC_RESULT_SUCCESS) { + SEC_TRACE(SEC_TRACE_UNWRAP, "SecAsn1KC_GetAttrBuffer SEC_ASN1KC_WRAPPEDKEY failed"); + return SEC_RESULT_FAILURE; + } + + if (SecAsn1KC_GetAttrUlong(kc, SEC_ASN1KC_WRAPPEDKEYTYPEID, &ulong_val) != SEC_RESULT_SUCCESS) { + SEC_TRACE(SEC_TRACE_UNWRAP, "SecAsn1KC_GetAttrUlong SEC_ASN1KC_WRAPPEDKEYTYPEID failed"); + return SEC_RESULT_FAILURE; + } + + *wrappedKeyType = (Sec_KeyType) ulong_val; + + if (SecAsn1KC_GetAttrUint64(kc, SEC_ASN1KC_WRAPPINGKEYID, wrappingId) != SEC_RESULT_SUCCESS) { + SEC_TRACE(SEC_TRACE_UNWRAP, "SecAsn1KC_GetAttrUint64 SEC_ASN1KC_WRAPPINGKEYID failed"); + return SEC_RESULT_FAILURE; + } + + if (SecAsn1KC_HasAttr(kc, SEC_ASN1KC_WRAPPEDKEYOFFSET)) { + if (SecAsn1KC_GetAttrUlong(kc, SEC_ASN1KC_WRAPPEDKEYOFFSET, &ulong_val) != SEC_RESULT_SUCCESS) { + SEC_TRACE(SEC_TRACE_UNWRAP, "SecAsn1KC_GetAttrUlong SEC_ASN1KC_WRAPPEDKEYOFFSET failed"); + return SEC_RESULT_FAILURE; + } + *key_offset = (SEC_SIZE) ulong_val; + } else { + *key_offset = (SEC_SIZE) 0; // default value + } + + if (SecAsn1KC_HasAttr(kc, SEC_ASN1KC_WRAPPINGIV) && + SecAsn1KC_GetAttrBuffer(kc, SEC_ASN1KC_WRAPPINGIV, wrappingIv, SEC_AES_BLOCK_SIZE, &written_iv) != + SEC_RESULT_SUCCESS) { + SEC_TRACE(SEC_TRACE_UNWRAP, "SecAsn1KC_GetAttrBuffer SEC_ASN1KC_WRAPPINGIV failed"); + return SEC_RESULT_FAILURE; + } + + if (SecAsn1KC_GetAttrUlong(kc, SEC_ASN1KC_WRAPPINGALGORITHMID, &ulong_val) != SEC_RESULT_SUCCESS) { + SEC_TRACE(SEC_TRACE_UNWRAP, "SecAsn1KC_GetAttrUlong SEC_ASN1KC_WRAPPINGALGORITHMID failed"); + return SEC_RESULT_FAILURE; + } + + *wrappingAlg = (Sec_CipherAlgorithm) ulong_val; + return SEC_RESULT_SUCCESS; +} + +#endif + +void rights_set_allow_all(sa_rights* rights, Sec_KeyType key_type) { + memset(rights, 0, sizeof(sa_rights)); + + rights->not_before = 0; + rights->not_on_or_after = UINT64_MAX; + + rights->usage_flags = 0; + switch (key_type) { + case SEC_KEYTYPE_AES_128: + case SEC_KEYTYPE_AES_256: + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_UNWRAP); + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_ENCRYPT); + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_DECRYPT); + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_SIGN); + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_DERIVE); + break; + + case SEC_KEYTYPE_HMAC_128: + case SEC_KEYTYPE_HMAC_160: + case SEC_KEYTYPE_HMAC_256: + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_SIGN); + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_DERIVE); + break; + + case SEC_KEYTYPE_RSA_1024: + case SEC_KEYTYPE_RSA_2048: + case SEC_KEYTYPE_RSA_3072: + case SEC_KEYTYPE_ECC_NISTP256: + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_UNWRAP); + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_DECRYPT); + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_SIGN); + break; + + case SEC_KEYTYPE_RSA_1024_PUBLIC: + case SEC_KEYTYPE_RSA_2048_PUBLIC: + case SEC_KEYTYPE_RSA_3072_PUBLIC: + case SEC_KEYTYPE_ECC_NISTP256_PUBLIC: + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_ENCRYPT); + break; + + default: + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_DERIVE); + break; + } + + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_KEY_EXCHANGE); + rights->usage_flags |= SA_USAGE_OUTPUT_PROTECTIONS_MASK; + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_CACHEABLE); + Sec_Memset(rights->allowed_tas, 0, sizeof(rights->allowed_tas)); + + const sa_uuid ALL_MATCH = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + memcpy(&rights->allowed_tas[7], &ALL_MATCH, sizeof(sa_uuid)); +} + +Sec_Result prepare_and_store_key_data(Sec_ProcessorHandle* processorHandle, Sec_StorageLoc location, + SEC_OBJECTID object_id, Sec_Key* key, Sec_KeyContainer key_container, void* key_buffer, SEC_SIZE key_length) { + Sec_KeyData* key_data = calloc(1, sizeof(Sec_KeyData)); + if (key_data == NULL) { + SEC_LOG_ERROR("calloc failed"); + return SEC_RESULT_FAILURE; + } + + Sec_Result result; + switch (key_container) { + case SEC_KEYCONTAINER_RAW_AES_128: + case SEC_KEYCONTAINER_RAW_AES_256: + case SEC_KEYCONTAINER_RAW_HMAC_128: + case SEC_KEYCONTAINER_RAW_HMAC_160: + case SEC_KEYCONTAINER_RAW_HMAC_256: + case SEC_KEYCONTAINER_DER_RSA_1024: + case SEC_KEYCONTAINER_DER_RSA_2048: + case SEC_KEYCONTAINER_DER_RSA_3072: + case SEC_KEYCONTAINER_RAW_ECC_PRIVONLY_NISTP256: + case SEC_KEYCONTAINER_SOC: + key_data->info.kc_type = SEC_KEYCONTAINER_EXPORTED; + result = export_key(key, NULL, key_data->key_container, SEC_KEYCONTAINER_MAX_LEN, &key_data->kc_len); + sa_key_release(key->handle); + break; + + case SEC_KEYCONTAINER_EXPORTED: + case SEC_KEYCONTAINER_JTYPE: + if (key_buffer != NULL && key_length != 0) { + key_data->info.kc_type = key_container; + memcpy(key_data->key_container, key_buffer, key_length); + key_data->kc_len = key_length; + sa_key_release(key->handle); + result = SEC_RESULT_SUCCESS; + } else { + result = SEC_RESULT_INVALID_PARAMETERS; + } + + break; + + case SEC_KEYCONTAINER_DER_RSA_1024_PUBLIC: + case SEC_KEYCONTAINER_DER_RSA_2048_PUBLIC: + case SEC_KEYCONTAINER_DER_RSA_3072_PUBLIC: + if (key_buffer != NULL && key_length != 0) { + key_data->info.kc_type = key_container; + memcpy(key_data->key_container, key_buffer, key_length); + key_data->kc_len = key_length; + RSA_free(key->rsa); + result = SEC_RESULT_SUCCESS; + } else { + result = SEC_RESULT_INVALID_PARAMETERS; + } + + break; + + case SEC_KEYCONTAINER_DER_ECC_NISTP256_PUBLIC: + if (key_buffer != NULL && key_length != 0) { + key_data->info.kc_type = key_container; + memcpy(key_data->key_container, key_buffer, key_length); + key_data->kc_len = key_length; + EC_KEY_free(key->ec_key); + result = SEC_RESULT_SUCCESS; + } else { + result = SEC_RESULT_INVALID_PARAMETERS; + } + + break; + + default: + result = SEC_RESULT_UNIMPLEMENTED_FEATURE; + } + + if (result == SEC_RESULT_SUCCESS) + result = store_key_data(processorHandle, location, object_id, key_data); + + SEC_FREE(key_data); + return result; +} + +const Sec_Key* get_key(Sec_KeyHandle* keyHandle) { + return &keyHandle->key; +} + +static Sec_KeyType get_key_type(sa_header* key_header) { + Sec_KeyType key_type; + switch ((*key_header).type) { + case SA_KEY_TYPE_SYMMETRIC: + if ((SA_USAGE_BIT_TEST((*key_header).rights.usage_flags, SA_USAGE_FLAG_SIGN) || + SA_USAGE_BIT_TEST((*key_header).rights.usage_flags, SA_USAGE_FLAG_DERIVE)) && + !SA_USAGE_BIT_TEST((*key_header).rights.usage_flags, SA_USAGE_FLAG_ENCRYPT) && + !SA_USAGE_BIT_TEST((*key_header).rights.usage_flags, SA_USAGE_FLAG_DECRYPT) && + !SA_USAGE_BIT_TEST((*key_header).rights.usage_flags, SA_USAGE_FLAG_UNWRAP)) { + if ((*key_header).size == 16) + key_type = SEC_KEYTYPE_HMAC_128; + else if ((*key_header).size == 20) + key_type = SEC_KEYTYPE_HMAC_160; + else if ((*key_header).size == 32) + key_type = SEC_KEYTYPE_HMAC_256; + else + key_type = SEC_KEYTYPE_NUM; + } else if (SA_USAGE_BIT_TEST((*key_header).rights.usage_flags, SA_USAGE_FLAG_DERIVE) || + SA_USAGE_BIT_TEST((*key_header).rights.usage_flags, SA_USAGE_FLAG_ENCRYPT) || + SA_USAGE_BIT_TEST((*key_header).rights.usage_flags, SA_USAGE_FLAG_DECRYPT) || + SA_USAGE_BIT_TEST((*key_header).rights.usage_flags, SA_USAGE_FLAG_SIGN) || + SA_USAGE_BIT_TEST((*key_header).rights.usage_flags, SA_USAGE_FLAG_UNWRAP)) { + if ((*key_header).size == 16) + key_type = SEC_KEYTYPE_AES_128; + else if ((*key_header).size == 20) + key_type = SEC_KEYTYPE_HMAC_160; + else if ((*key_header).size == 32) + key_type = SEC_KEYTYPE_AES_256; + else + key_type = SEC_KEYTYPE_NUM; + } else { + key_type = SEC_KEYTYPE_NUM; + } + + break; + + case SA_KEY_TYPE_EC: + key_type = SEC_KEYTYPE_ECC_NISTP256; + break; + + case SA_KEY_TYPE_RSA: + if ((*key_header).size == 128) + key_type = SEC_KEYTYPE_RSA_1024; + else if ((*key_header).size == 256) + key_type = SEC_KEYTYPE_RSA_2048; + else if ((*key_header).size == 384) + key_type = SEC_KEYTYPE_RSA_3072; + else + key_type = SEC_KEYTYPE_NUM; + break; + + case SA_KEY_TYPE_DH: + default: + key_type = SEC_KEYTYPE_NUM; + break; + } + + return key_type; +} + +static void find_ram_key_data(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, Sec_RAMKeyData** data, + Sec_RAMKeyData** parent) { + *parent = NULL; + *data = processorHandle->ram_keys; + + while ((*data) != NULL) { + if (object_id == (*data)->object_id) + return; + + *parent = (*data); + *data = (*data)->next; + } + + *parent = NULL; +} + +static Sec_Result retrieve_key_data(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_StorageLoc* location, Sec_KeyData* keyData) { + char file_name_key[SEC_MAX_FILE_PATH_LEN]; + char file_name_info[SEC_MAX_FILE_PATH_LEN]; + char file_name_verification[SEC_MAX_FILE_PATH_LEN]; + Sec_RAMKeyData* ram_key = NULL; + Sec_RAMKeyData* ram_key_parent = NULL; + SEC_SIZE data_read; + + CHECK_PROCHANDLE(processorHandle) + + /* check in RAM */ + find_ram_key_data(processorHandle, object_id, &ram_key, &ram_key_parent); + if (ram_key != NULL) { + memcpy(keyData, &(ram_key->key_data), sizeof(Sec_KeyData)); + *location = SEC_STORAGELOC_RAM; + return SEC_RESULT_SUCCESS; + } + + /* check in app_dir */ + char* sec_dirs[] = {processorHandle->app_dir, processorHandle->global_dir}; + for (int i = 0; i < 2; i++) { + if (sec_dirs[i] != NULL) { + snprintf(file_name_key, sizeof(file_name_key), SEC_KEY_FILENAME_PATTERN, sec_dirs[i], + object_id); + snprintf(file_name_info, sizeof(file_name_info), SEC_KEYINFO_FILENAME_PATTERN, sec_dirs[i], + object_id); + snprintf(file_name_verification, sizeof(file_name_verification), SEC_VERIFICATION_FILENAME_PATTERN, + sec_dirs[i], object_id); + if (SecUtils_FileExists(file_name_key) && SecUtils_FileExists(file_name_info)) { + if (SecUtils_ReadFile(file_name_key, keyData->key_container, sizeof(keyData->key_container), + &keyData->kc_len) != SEC_RESULT_SUCCESS || + SecUtils_ReadFile(file_name_info, &keyData->info, sizeof(keyData->info), &data_read) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Could not read one of the key files"); + return SEC_RESULT_FAILURE; + } + + if (data_read != sizeof(keyData->info)) { + SEC_LOG_ERROR("File is not of the correct size"); + return SEC_RESULT_FAILURE; + } + + if (SecUtils_FileExists(file_name_verification)) { + if (verify_verification_file(processorHandle, file_name_verification, keyData->key_container, + keyData->kc_len, (SEC_BYTE*) &keyData->info, sizeof(keyData->info)) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Key verification failed"); + return SEC_RESULT_FAILURE; + } + } else { + // If the verification file doesn't exist, the key file was created by an old SecApi. If the key + // container type is Exported, the old SecApi uses a different export format and the key file cannot + // be read. So fail the key retrieval. + if (keyData->info.kc_type == SEC_KEYCONTAINER_EXPORTED) { + SEC_LOG_ERROR("Old Exported key container format"); + return SEC_RESULT_FAILURE; + } + + if (write_verification_file(processorHandle, file_name_verification, keyData->key_container, + keyData->kc_len, (SEC_BYTE*) &keyData->info, sizeof(keyData->info)) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Could not write verification file"); + } + } + *location = SEC_STORAGELOC_FILE; + return SEC_RESULT_SUCCESS; + } + } + } + + return SEC_RESULT_NO_SUCH_ITEM; +} + +static Sec_Result store_key_data(Sec_ProcessorHandle* processorHandle, Sec_StorageLoc location, SEC_OBJECTID object_id, + Sec_KeyData* key_data) { + Sec_RAMKeyData* ram_key; + + if (location == SEC_STORAGELOC_RAM || location == SEC_STORAGELOC_RAM_SOFT_WRAPPED) { + SecKey_Delete(processorHandle, object_id); + + ram_key = calloc(1, sizeof(Sec_RAMKeyData)); + if (ram_key == NULL) { + SEC_LOG_ERROR("Calloc failed"); + return SEC_RESULT_FAILURE; + } + + ram_key->object_id = object_id; + memcpy(&(ram_key->key_data), key_data, sizeof(Sec_KeyData)); + ram_key->next = processorHandle->ram_keys; + processorHandle->ram_keys = ram_key; + return SEC_RESULT_SUCCESS; + } + + if (location == SEC_STORAGELOC_FILE || location == SEC_STORAGELOC_FILE_SOFT_WRAPPED) { + if (processorHandle->app_dir == NULL) { + SEC_LOG_ERROR("Cannot write file because app_dir is NULL"); + return SEC_RESULT_FAILURE; + } + + SecKey_Delete(processorHandle, object_id); + + char file_name_key[SEC_MAX_FILE_PATH_LEN]; + char file_name_info[SEC_MAX_FILE_PATH_LEN]; + char file_name_verification[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name_key, sizeof(file_name_key), SEC_KEY_FILENAME_PATTERN, processorHandle->app_dir, + object_id); + snprintf(file_name_info, sizeof(file_name_info), SEC_KEYINFO_FILENAME_PATTERN, + processorHandle->app_dir, object_id); + snprintf(file_name_verification, sizeof(file_name_verification), SEC_VERIFICATION_FILENAME_PATTERN, + processorHandle->app_dir, object_id); + + if (SecUtils_WriteFile(file_name_key, key_data->key_container, key_data->kc_len) != + SEC_RESULT_SUCCESS || + SecUtils_WriteFile(file_name_info, &key_data->info, sizeof(key_data->info)) != + SEC_RESULT_SUCCESS || + write_verification_file(processorHandle, file_name_verification, + key_data->key_container, key_data->kc_len, + (SEC_BYTE*) &key_data->info, sizeof(key_data->info)) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Could not write info file"); + SecUtils_RmFile(file_name_key); + SecUtils_RmFile(file_name_info); + SecUtils_RmFile(file_name_verification); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; + } + + SEC_LOG_ERROR("Unimplemented location type"); + return SEC_RESULT_UNIMPLEMENTED_FEATURE; +} + +static Sec_Result process_key_container(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID object_id, + Sec_KeyContainer in_key_container, void* data, SEC_SIZE data_length, Sec_Key* key, + Sec_KeyContainer* out_key_container, void* key_buffer, SEC_SIZE* key_length) { + Sec_Result result; + unsigned char* p_data; + SEC_SIZE key_size; + Sec_KeyType key_type; + sa_rights rights; + sa_key_format key_format; + void* parameters; + sa_import_parameters_symmetric symmetric_parameters; + sa_import_parameters_rsa_private_key_info rsa_parameters; + sa_import_parameters_ec_private_bytes ec_parameters; + sa_import_parameters_typej typej_parameters; + sa_import_parameters_soc_legacy parameters_soc_legacy; + Sec_KeyHandle* cipherKeyHandle = NULL; + Sec_KeyHandle* hmacKeyHandle = NULL; + sa_status status; + + if (in_key_container == SEC_KEYCONTAINER_STORE) { + result = process_store_key_container(processorHandle, data, data_length, key_buffer, key_length, + out_key_container); + if (result != SEC_RESULT_SUCCESS) + return result; + + p_data = key_buffer; + } else { + *out_key_container = in_key_container; + p_data = data; + *key_length = data_length; + } + + if (*out_key_container == SEC_KEYCONTAINER_ASN1) { + result = process_asn1_key_container(processorHandle, p_data, *key_length, key_buffer, key_length, + out_key_container); + if (result != SEC_RESULT_SUCCESS) + return result; + + p_data = key_buffer; + } + + switch (*out_key_container) { + case SEC_KEYCONTAINER_RAW_AES_128: + case SEC_KEYCONTAINER_RAW_AES_256: + case SEC_KEYCONTAINER_RAW_HMAC_128: + case SEC_KEYCONTAINER_RAW_HMAC_160: + case SEC_KEYCONTAINER_RAW_HMAC_256: + key_type = SecKey_GetKeyTypeForClearKeyContainer(*out_key_container); + key_size = SecKey_GetKeyLenForKeyType(key_type); + if (*key_length != key_size) { + SEC_LOG_ERROR("Invalid key container length"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + memmove(key_buffer, p_data, *key_length); + key_format = SA_KEY_FORMAT_SYMMETRIC_BYTES; + rights_set_allow_all(&rights, SecKey_GetKeyTypeForClearKeyContainer(*out_key_container)); + symmetric_parameters.rights = &rights; + parameters = &symmetric_parameters; + break; + + case SEC_KEYCONTAINER_DER_RSA_1024: + case SEC_KEYCONTAINER_DER_RSA_2048: + case SEC_KEYCONTAINER_DER_RSA_3072: + case SEC_KEYCONTAINER_RAW_RSA_1024: + case SEC_KEYCONTAINER_RAW_RSA_2048: + case SEC_KEYCONTAINER_RAW_RSA_3072: + case SEC_KEYCONTAINER_PEM_RSA_1024: + case SEC_KEYCONTAINER_PEM_RSA_2048: + case SEC_KEYCONTAINER_PEM_RSA_3072: + result = process_rsa_key_container(*out_key_container, p_data, *key_length, key_buffer, key_length, + out_key_container); + if (result != SEC_RESULT_SUCCESS) + return result; + + key_format = SA_KEY_FORMAT_RSA_PRIVATE_KEY_INFO; + rights_set_allow_all(&rights, SecKey_GetKeyTypeForClearKeyContainer(*out_key_container)); + rsa_parameters.rights = &rights; + parameters = &rsa_parameters; + break; + + case SEC_KEYCONTAINER_DER_RSA_1024_PUBLIC: + case SEC_KEYCONTAINER_DER_RSA_2048_PUBLIC: + case SEC_KEYCONTAINER_DER_RSA_3072_PUBLIC: + case SEC_KEYCONTAINER_RAW_RSA_1024_PUBLIC: + case SEC_KEYCONTAINER_RAW_RSA_2048_PUBLIC: + case SEC_KEYCONTAINER_RAW_RSA_3072_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_1024_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_2048_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_3072_PUBLIC: + result = process_rsa_public_key_container(*out_key_container, p_data, *key_length, &key->rsa, + key_buffer, key_length, out_key_container); + if (result != SEC_RESULT_SUCCESS) + return result; + + // Public key so skip key import. + return SEC_RESULT_SUCCESS; + + case SEC_KEYCONTAINER_DER_ECC_NISTP256: + case SEC_KEYCONTAINER_RAW_ECC_NISTP256: + case SEC_KEYCONTAINER_RAW_ECC_PRIVONLY_NISTP256: + case SEC_KEYCONTAINER_PEM_ECC_NISTP256: + result = process_ec_key_container(*out_key_container, p_data, *key_length, key_buffer, key_length, + out_key_container); + if (result != SEC_RESULT_SUCCESS) + return result; + + key_format = SA_KEY_FORMAT_EC_PRIVATE_BYTES; + rights_set_allow_all(&rights, SecKey_GetKeyTypeForClearKeyContainer(*out_key_container)); + ec_parameters.rights = &rights; + ec_parameters.curve = SA_ELLIPTIC_CURVE_NIST_P256; + parameters = &ec_parameters; + break; + + case SEC_KEYCONTAINER_DER_ECC_NISTP256_PUBLIC: + case SEC_KEYCONTAINER_RAW_ECC_NISTP256_PUBLIC: + case SEC_KEYCONTAINER_PEM_ECC_NISTP256_PUBLIC: + result = process_ec_public_key_container(*out_key_container, p_data, *key_length, &key->ec_key, + key_buffer, key_length, out_key_container); + if (result != SEC_RESULT_SUCCESS) + return result; + + // Public key so skip key import. + return SEC_RESULT_SUCCESS; + + case SEC_KEYCONTAINER_SOC: + case SEC_KEYCONTAINER_SOC_INTERNAL_0: + case SEC_KEYCONTAINER_SOC_INTERNAL_1: + case SEC_KEYCONTAINER_SOC_INTERNAL_2: + case SEC_KEYCONTAINER_SOC_INTERNAL_3: + case SEC_KEYCONTAINER_SOC_INTERNAL_4: + case SEC_KEYCONTAINER_SOC_INTERNAL_5: + case SEC_KEYCONTAINER_SOC_INTERNAL_6: + case SEC_KEYCONTAINER_SOC_INTERNAL_7: + case SEC_KEYCONTAINER_SOC_INTERNAL_8: + case SEC_KEYCONTAINER_SOC_INTERNAL_9: + case SEC_KEYCONTAINER_SOC_INTERNAL_10: + case SEC_KEYCONTAINER_SOC_INTERNAL_11: + case SEC_KEYCONTAINER_SOC_INTERNAL_12: + case SEC_KEYCONTAINER_SOC_INTERNAL_13: + case SEC_KEYCONTAINER_SOC_INTERNAL_14: + case SEC_KEYCONTAINER_SOC_INTERNAL_15: + *out_key_container = SEC_KEYCONTAINER_SOC; + memmove(key_buffer, p_data, *key_length); + key_format = SA_KEY_FORMAT_SOC; + if (is_jwt_key_container(key_buffer, *key_length)) { + parameters = NULL; + } else { + size_t length = sizeof(sa_import_parameters_soc_legacy); + rights_set_allow_all(&rights, SEC_KEYTYPE_AES_128); + parameters_soc_legacy.length[0] = length >> 8 & 0xff; + parameters_soc_legacy.length[1] = length & 0xff; + parameters_soc_legacy.version = VERSION_2_KEY_CONTAINER; + parameters_soc_legacy.default_rights = rights; + parameters_soc_legacy.object_id = object_id; + parameters = ¶meters_soc_legacy; + } + + break; + + case SEC_KEYCONTAINER_EXPORTED: + memmove(key_buffer, p_data, *key_length); + key_format = SA_KEY_FORMAT_EXPORTED; + parameters = NULL; + break; + + case SEC_KEYCONTAINER_JTYPE: + memmove(key_buffer, p_data, *key_length); + key_format = SA_KEY_FORMAT_TYPEJ; + + result = SecKey_GetInstance(processorHandle, SEC_OBJECTID_COMCAST_XCALSESSIONENCKEY, &cipherKeyHandle); + if (result != SEC_RESULT_SUCCESS) + return result; + + result = SecKey_GetInstance(processorHandle, SEC_OBJECTID_COMCAST_XCALSESSIONMACKEY, &hmacKeyHandle); + if (result != SEC_RESULT_SUCCESS) { + if (cipherKeyHandle != NULL) + SecKey_Release(cipherKeyHandle); + + return result; + } + + typej_parameters.kcipher = cipherKeyHandle->key.handle; + typej_parameters.khmac = hmacKeyHandle->key.handle; + parameters = &typej_parameters; + break; + + default: + return SEC_RESULT_FAILURE; + } + + // Validate the key and import it. + status = sa_key_import(&key->handle, key_format, key_buffer, *key_length, parameters); + + if (cipherKeyHandle != NULL) + SecKey_Release(cipherKeyHandle); + + if (hmacKeyHandle != NULL) + SecKey_Release(hmacKeyHandle); + + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +static bool is_jwt_key_container(SEC_BYTE* key_buffer, SEC_SIZE key_length) { + + SEC_BYTE* in_string = key_buffer; + SEC_BYTE* in_string_end = in_string + key_length; + + SEC_BYTE* header_b64 = in_string; + SEC_BYTE* header_b64_end = memchr(header_b64, '.', in_string_end - header_b64); + if (header_b64_end == NULL) + return false; + + SEC_SIZE header_b64_length = header_b64_end - header_b64; + SEC_BYTE* payload_b64 = header_b64_end + 1; + if (payload_b64 >= in_string_end) + return false; + + SEC_BYTE* payload_b64_end = memchr(payload_b64, '.', in_string_end - payload_b64); + if (payload_b64_end == NULL) + return false; + + SEC_SIZE payload_b64_length = payload_b64_end - payload_b64; + SEC_BYTE* mac_b64 = payload_b64_end + 1; + if (mac_b64 >= in_string_end) + return false; + + SEC_BYTE* mac_b64_end = in_string_end; + SEC_SIZE mac_b64_length = mac_b64_end - mac_b64; + SEC_SIZE header_length = 3 * header_b64_length / 4; + SEC_SIZE length; + SEC_BYTE* header = malloc(header_length); + Sec_Result result = SecUtils_Base64Decode(header_b64, header_b64_length, header, header_length, &length); + free(header); + if (result != SEC_RESULT_SUCCESS) + return false; + + SEC_SIZE payload_length = 3 * payload_b64_length / 4; + SEC_BYTE* payload = malloc(payload_length); + result = SecUtils_Base64Decode(payload_b64, payload_b64_length, payload, payload_length, &length); + free(payload); + if (result != SEC_RESULT_SUCCESS) + return false; + + SEC_SIZE mac_length = 3 * mac_b64_length / 4; + SEC_BYTE* mac = malloc(mac_length); + result = SecUtils_Base64Decode(mac_b64, mac_b64_length, mac, mac_length, &length); + free(mac); + if (result != SEC_RESULT_SUCCESS) + return false; + + return true; +} + +static Sec_Result process_rsa_key_container(Sec_KeyContainer in_key_container, SEC_BYTE* data, + SEC_SIZE data_length, unsigned char* key_buffer, SEC_SIZE* key_length, Sec_KeyContainer* out_key_container) { + RSA* rsa = NULL; + switch (in_key_container) { + case SEC_KEYCONTAINER_DER_RSA_1024: + case SEC_KEYCONTAINER_DER_RSA_2048: + case SEC_KEYCONTAINER_DER_RSA_3072: { + const unsigned char* p_data = data; + rsa = d2i_RSAPrivateKey(NULL, &p_data, data_length); + if (rsa == NULL) { + SEC_LOG_ERROR("d2i_RSAPrivateKey failed"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + break; + } + case SEC_KEYCONTAINER_RAW_RSA_1024: + case SEC_KEYCONTAINER_RAW_RSA_2048: + case SEC_KEYCONTAINER_RAW_RSA_3072: { + if (data_length != sizeof(Sec_RSARawPrivateKey)) { + SEC_LOG_ERROR("Invalid key container length"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + // Validate the key and convert to RSA. + Sec_RSARawPrivateKey* rawPrivateKey = (Sec_RSARawPrivateKey*) data; + rsa = RSA_new(); + if (rsa == NULL) { + SEC_LOG_ERROR("RSA_new failed"); + return SEC_RESULT_INVALID_PARAMETERS; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + rsa->n = BN_bin2bn(rawPrivateKey->n, (int) Sec_BEBytesToUint32(rawPrivateKey->modulus_len_be), NULL); + rsa->e = BN_bin2bn(rawPrivateKey->e, 4, NULL); + rsa->d = BN_bin2bn(rawPrivateKey->d, (int) Sec_BEBytesToUint32(rawPrivateKey->modulus_len_be), NULL); +#else + RSA_set0_key(rsa, + BN_bin2bn(rawPrivateKey->n, (int) Sec_BEBytesToUint32(rawPrivateKey->modulus_len_be), NULL), + BN_bin2bn(rawPrivateKey->e, 4, NULL), + BN_bin2bn(rawPrivateKey->d, (int) Sec_BEBytesToUint32(rawPrivateKey->modulus_len_be), NULL)); +#endif + break; + } + case SEC_KEYCONTAINER_PEM_RSA_1024: + case SEC_KEYCONTAINER_PEM_RSA_2048: + case SEC_KEYCONTAINER_PEM_RSA_3072: { + // Validate the key and convert to RSA. + BIO* bio = BIO_new_mem_buf(data, (int) data_length); + rsa = PEM_read_bio_RSAPrivateKey(bio, &rsa, disable_passphrase_prompt, NULL); + Sec_KeyType key_type = SecKey_GetKeyTypeForClearKeyContainer(in_key_container); + if ((rsa == NULL) || ((SEC_SIZE) RSA_size(rsa) != SecKey_GetKeyLenForKeyType(key_type))) { + SEC_RSA_FREE(rsa); + SEC_LOG_ERROR("Invalid RSA key container"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + break; + } + default: + return SEC_RESULT_FAILURE; + } + + Sec_KeyType key_type = SecKey_GetKeyTypeForClearKeyContainer(in_key_container); + if ((rsa == NULL) || ((SEC_SIZE) RSA_size(rsa) != SecKey_GetKeyLenForKeyType(key_type))) { + SEC_RSA_FREE(rsa); + SEC_LOG_ERROR("Invalid RSA key container"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + *out_key_container = convert_key_container(in_key_container); + unsigned char* p_data = key_buffer; + *key_length = i2d_RSAPrivateKey(rsa, &p_data); + SEC_RSA_FREE(rsa); + if (*key_length <= 0) { + SEC_LOG_ERROR("Invalid RSA key container"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + return SEC_RESULT_SUCCESS; +} + +static Sec_Result process_rsa_public_key_container(Sec_KeyContainer in_key_container, SEC_BYTE* data, + SEC_SIZE data_length, RSA** rsa, unsigned char* key_buffer, SEC_SIZE* key_length, + Sec_KeyContainer* out_key_container) { + switch (in_key_container) { + case SEC_KEYCONTAINER_DER_RSA_1024_PUBLIC: + case SEC_KEYCONTAINER_DER_RSA_2048_PUBLIC: + case SEC_KEYCONTAINER_DER_RSA_3072_PUBLIC: { + const unsigned char* p_data = data; + *rsa = d2i_RSA_PUBKEY(NULL, &p_data, data_length); + if (*rsa == NULL) + *rsa = d2i_RSAPublicKey(NULL, &p_data, data_length); + + if (*rsa == NULL) { + SEC_LOG_ERROR("d2i_RSAPublicKey failed"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + break; + } + case SEC_KEYCONTAINER_RAW_RSA_1024_PUBLIC: + case SEC_KEYCONTAINER_RAW_RSA_2048_PUBLIC: + case SEC_KEYCONTAINER_RAW_RSA_3072_PUBLIC: { + if (data_length < sizeof(Sec_RSARawPublicKey)) { + SEC_LOG_ERROR("Invalid key container length"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + // Validate the key and convert to RSA. + Sec_RSARawPublicKey* rawPublicKey = (Sec_RSARawPublicKey*) data; + *rsa = RSA_new(); + if (rsa == NULL) { + SEC_LOG_ERROR("RSA_new failed"); + return SEC_RESULT_FAILURE; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + (*rsa)->n = BN_bin2bn(rawPublicKey->n, (int) Sec_BEBytesToUint32(rawPublicKey->modulus_len_be), NULL); + (*rsa)->e = BN_bin2bn(rawPublicKey->e, 4, NULL); +#else + RSA_set0_key(*rsa, + BN_bin2bn(rawPublicKey->n, (int) Sec_BEBytesToUint32(rawPublicKey->modulus_len_be), NULL), + BN_bin2bn(rawPublicKey->e, 4, NULL), + NULL); +#endif + break; + } + case SEC_KEYCONTAINER_PEM_RSA_1024_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_2048_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_3072_PUBLIC: { + BIO* bio = BIO_new_mem_buf(data, (int) data_length); + EVP_PKEY* evp_pkey = PEM_read_bio_PUBKEY(bio, &evp_pkey, disable_passphrase_prompt, NULL); + if (evp_pkey == NULL) { + SEC_LOG_ERROR("PEM_read_bio_PUBKEY failed"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + *rsa = EVP_PKEY_get1_RSA(evp_pkey); + EVP_PKEY_free(evp_pkey); + if (*rsa == NULL) { + SEC_LOG_ERROR("EVP_PKEY_get0_RSA failed"); + return SEC_RESULT_FAILURE; + } + + break; + } + default: + return SEC_RESULT_FAILURE; + } + + Sec_KeyType key_type = SecKey_GetKeyTypeForClearKeyContainer(in_key_container); + if ((SEC_SIZE) RSA_size(*rsa) != SecKey_GetKeyLenForKeyType(key_type)) { + SEC_RSA_FREE(*rsa); + *rsa = NULL; + SEC_LOG_ERROR("Invalid RSA key container"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + *out_key_container = convert_key_container(in_key_container); + unsigned char* p_data = key_buffer; + *key_length = i2d_RSAPublicKey(*rsa, &p_data); + if (*key_length <= 0) { + SEC_LOG_ERROR("Invalid RSA key container"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + return SEC_RESULT_SUCCESS; +} + +static Sec_Result process_ec_key_container(Sec_KeyContainer in_key_container, SEC_BYTE* data, + SEC_SIZE data_length, unsigned char* key_buffer, SEC_SIZE* key_length, Sec_KeyContainer* out_key_container) { + switch (in_key_container) { + case SEC_KEYCONTAINER_DER_ECC_NISTP256: { + const unsigned char* p_der = data; + EC_KEY* ec_key = d2i_ECPrivateKey(NULL, &p_der, data_length); + if (ec_key == NULL) { + SEC_LOG_ERROR("Invalid ECC key container"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + *key_length = BN_bn2bin(EC_KEY_get0_private_key(ec_key), key_buffer); + SEC_ECC_FREE(ec_key); + break; + } + case SEC_KEYCONTAINER_RAW_ECC_NISTP256: { + if (data_length != sizeof(Sec_ECCRawPrivateKey)) { + SEC_LOG_ERROR("Invalid key container length"); + SEC_LOG_ERROR("Data_len != sizeof(Sec_ECCRawPrivateKey) data_length: %d, expected: %d", data_length, + sizeof(Sec_ECCRawPrivateKey)); + return SEC_RESULT_INVALID_PARAMETERS; + } + + Sec_ECCRawPrivateKey* rawPrivateKey = (Sec_ECCRawPrivateKey*) data; + if (rawPrivateKey->type != SEC_KEYTYPE_ECC_NISTP256) { + SEC_LOG_ERROR("Invalid ECC key container"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + memcpy(key_buffer, rawPrivateKey->prv, SEC_ECC_NISTP256_KEY_LEN); + *key_length = SEC_ECC_NISTP256_KEY_LEN; + break; + } + case SEC_KEYCONTAINER_RAW_ECC_PRIVONLY_NISTP256: { + if (data_length != sizeof(Sec_ECCRawOnlyPrivateKey)) { + SEC_LOG_ERROR("Invalid key container length"); + SEC_LOG_ERROR("Data_len != sizeof(Sec_ECCRawOnlyPrivateKey) data_length: %d, expected: %d", data_length, + sizeof(Sec_ECCRawOnlyPrivateKey)); + return SEC_RESULT_INVALID_PARAMETERS; + } + + memcpy(key_buffer, ((Sec_ECCRawOnlyPrivateKey*) data)->prv, SEC_ECC_NISTP256_KEY_LEN); + *key_length = SEC_ECC_NISTP256_KEY_LEN; + break; + } + case SEC_KEYCONTAINER_PEM_ECC_NISTP256: { + BIO* bio = BIO_new_mem_buf(data, (int) data_length); + EC_KEY* ec_key = PEM_read_bio_ECPrivateKey(bio, &ec_key, disable_passphrase_prompt, NULL); + SEC_BIO_FREE(bio); + if (ec_key == NULL) { + SEC_LOG_ERROR("Invalid ECC key container"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + *key_length = BN_bn2bin(EC_KEY_get0_private_key(ec_key), key_buffer); + SEC_ECC_FREE(ec_key); + break; + } + default: + return SEC_RESULT_FAILURE; + } + + *out_key_container = convert_key_container(in_key_container); + return SEC_RESULT_SUCCESS; +} + +static Sec_Result process_ec_public_key_container(Sec_KeyContainer in_key_container, SEC_BYTE* data, + SEC_SIZE data_length, EC_KEY** ec_key, unsigned char* key_buffer, SEC_SIZE* key_length, + Sec_KeyContainer* out_key_container) { + switch (in_key_container) { + case SEC_KEYCONTAINER_DER_ECC_NISTP256_PUBLIC: { + const unsigned char* p_data = data; + *ec_key = d2i_EC_PUBKEY(NULL, &p_data, data_length); + if (*ec_key == NULL) { + SEC_LOG_ERROR("d2i_EC_PUBKEY failed"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + break; + } + case SEC_KEYCONTAINER_RAW_ECC_NISTP256_PUBLIC: { + Sec_ECCRawPublicKey* binary = (Sec_ECCRawPublicKey*) data; + BN_CTX* bn_ctx = BN_CTX_new(); + + if (*key_length < sizeof(Sec_ECCRawPublicKey)) { + SEC_LOG_ERROR("Invalid key container length"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + if (binary->type != SEC_KEYTYPE_ECC_NISTP256_PUBLIC && binary->type != SEC_KEYTYPE_ECC_NISTP256) + return SEC_RESULT_INVALID_PARAMETERS; + + //create ec_key structure with NIST p256 curve; + *ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + const EC_GROUP* group = EC_KEY_get0_group(*ec_key); + EC_POINT* ec_point = EC_POINT_new(group); + BN_CTX_start(bn_ctx); + BIGNUM* xp; + BIGNUM* yp; + + if (((xp = BN_CTX_get(bn_ctx)) == NULL) || ((yp = BN_CTX_get(bn_ctx)) == NULL)) + return SEC_RESULT_INVALID_PARAMETERS; + + EC_POINT_set_affine_coordinates_GFp(group, ec_point, + BN_bin2bn(binary->x, (int) Sec_BEBytesToUint32(binary->key_len), xp), + BN_bin2bn(binary->y, (int) Sec_BEBytesToUint32(binary->key_len), yp), bn_ctx); + EC_KEY_set_public_key(*ec_key, ec_point); + + EC_POINT_free(ec_point); + BN_CTX_end(bn_ctx); + BN_CTX_free(bn_ctx); + return SEC_RESULT_SUCCESS; + } + case SEC_KEYCONTAINER_PEM_ECC_NISTP256_PUBLIC: { + BIO* bio = BIO_new_mem_buf(data, (int) data_length); + EVP_PKEY* evp_pkey = NULL; + evp_pkey = PEM_read_bio_PUBKEY(bio, &evp_pkey, disable_passphrase_prompt, NULL); + if (evp_pkey == NULL) { + SEC_LOG_ERROR("PEM_read_bio_PUBKEY failed"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + *ec_key = EVP_PKEY_get1_EC_KEY(evp_pkey); + EVP_PKEY_free(evp_pkey); + if (*ec_key == NULL) { + SEC_LOG_ERROR("EVP_PKEY_get0_EC_KEY failed"); + return SEC_RESULT_FAILURE; + } + + break; + } + default: + return SEC_RESULT_FAILURE; + } + + *out_key_container = convert_key_container(in_key_container); + *key_length = i2d_EC_PUBKEY(*ec_key, &key_buffer); + if (*key_length < 0) + return SEC_RESULT_INVALID_PARAMETERS; + + return SEC_RESULT_SUCCESS; +} + +static Sec_KeyContainer convert_key_container(Sec_KeyContainer key_container) { + switch (key_container) { + case SEC_KEYCONTAINER_RAW_RSA_1024: + case SEC_KEYCONTAINER_PEM_RSA_1024: + return SEC_KEYCONTAINER_DER_RSA_1024; + + case SEC_KEYCONTAINER_RAW_RSA_2048: + case SEC_KEYCONTAINER_PEM_RSA_2048: + return SEC_KEYCONTAINER_DER_RSA_2048; + + case SEC_KEYCONTAINER_RAW_RSA_3072: + case SEC_KEYCONTAINER_PEM_RSA_3072: + return SEC_KEYCONTAINER_DER_RSA_3072; + + case SEC_KEYCONTAINER_RAW_RSA_1024_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_1024_PUBLIC: + return SEC_KEYCONTAINER_DER_RSA_1024_PUBLIC; + + case SEC_KEYCONTAINER_RAW_RSA_2048_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_2048_PUBLIC: + return SEC_KEYCONTAINER_DER_RSA_2048_PUBLIC; + + case SEC_KEYCONTAINER_RAW_RSA_3072_PUBLIC: + case SEC_KEYCONTAINER_PEM_RSA_3072_PUBLIC: + return SEC_KEYCONTAINER_DER_RSA_3072_PUBLIC; + + case SEC_KEYCONTAINER_DER_ECC_NISTP256: + case SEC_KEYCONTAINER_RAW_ECC_NISTP256: + case SEC_KEYCONTAINER_PEM_ECC_NISTP256: + case SEC_KEYCONTAINER_RAW_ECC_PRIVONLY_NISTP256: + return SEC_KEYCONTAINER_RAW_ECC_PRIVONLY_NISTP256; + + case SEC_KEYCONTAINER_RAW_ECC_NISTP256_PUBLIC: + case SEC_KEYCONTAINER_PEM_ECC_NISTP256_PUBLIC: + return SEC_KEYCONTAINER_DER_ECC_NISTP256_PUBLIC; + + default: + return key_container; + } +} + +static Sec_Result process_asn1_key_container(Sec_ProcessorHandle* processorHandle, const void* data, + SEC_SIZE data_length, SEC_BYTE* key_buffer, SEC_SIZE* key_length, Sec_KeyContainer* key_container) { + SEC_SIZE tempkc_length = SEC_KEYCONTAINER_MAX_LEN; + uint8_t* tempkc = malloc(SEC_KEYCONTAINER_MAX_LEN); + if (tempkc == NULL) { + SEC_LOG_ERROR("malloc failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType wrapped_key_type; + SEC_OBJECTID wrapping_id; + SEC_BYTE wrapping_iv[SEC_AES_BLOCK_SIZE]; + Sec_CipherAlgorithm wrapping_alg; + SEC_SIZE wrapped_key_offset; + SEC_SIZE wrapping_key_length = SEC_KEYCONTAINER_MAX_LEN; + uint8_t* wrapping_key = malloc(SEC_KEYCONTAINER_MAX_LEN); + if (wrapping_key == NULL) { + SEC_FREE(tempkc); + SEC_LOG_ERROR("malloc failed"); + return SEC_RESULT_FAILURE; + } + + Sec_Asn1KC* asn1kc = SecAsn1KC_Decode(data, data_length); + if (asn1kc == NULL) { + SEC_FREE(tempkc); + SEC_FREE(wrapping_key); + SEC_LOG_ERROR("SecAsn1KC_Decode failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_ExtractWrappedKeyParamsAsn1V3(asn1kc, tempkc, SEC_KEYCONTAINER_MAX_LEN, &tempkc_length, + &wrapped_key_type, &wrapping_id, wrapping_iv, &wrapping_alg, &wrapped_key_offset, wrapping_key, + SEC_KEYCONTAINER_MAX_LEN, &wrapping_key_length) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExtractWrappedKeyParamsAsn1V3 failed"); + SEC_FREE(tempkc); + SEC_FREE(wrapping_key); + SecAsn1KC_Free(asn1kc); + return SEC_RESULT_FAILURE; + } + + SecAsn1KC_Free(asn1kc); + + //V3 + if (wrapping_key_length > 0) { + //get free id + wrapping_id = SecKey_ObtainFreeObjectId(processorHandle, SEC_OBJECTID_RESERVED_BASE, SEC_OBJECTID_RESERVED_TOP); + if (SEC_OBJECTID_INVALID == wrapping_id) { + SEC_LOG_ERROR("SecKey_ObtainFreeObjectId failed"); + SEC_FREE(tempkc); + SEC_FREE(wrapping_key); + return SEC_RESULT_FAILURE; + } + + // provision wrapping key--we don't know whether it is a SEC_KEYCONTAINER_ASN1 or a SEC_KEYCONTAINER_SOC + // so try the ASN1 container first. If it fails, then try to SOC container. + if (SecKey_Provision(processorHandle, wrapping_id, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_ASN1, wrapping_key, + wrapping_key_length) != SEC_RESULT_SUCCESS) { + if (SecKey_Provision(processorHandle, wrapping_id, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_SOC, wrapping_key, + wrapping_key_length) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + SEC_FREE(tempkc); + SEC_FREE(wrapping_key); + return SEC_RESULT_FAILURE; + } + } + } + + //unwrap + sa_key unwrapped_key; + Sec_Result result = unwrap_key(processorHandle, wrapping_alg, wrapped_key_type, wrapped_key_offset, wrapping_id, + wrapping_iv, tempkc, tempkc_length, key_buffer, key_length); + *key_container = SEC_KEYCONTAINER_EXPORTED; + if (wrapping_key_length > 0) + SecKey_Delete(processorHandle, wrapping_id); + + SEC_FREE(tempkc); + SEC_FREE(wrapping_key); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Unwrap_key failed"); + return result; + } + + return SEC_RESULT_SUCCESS; +} + +static Sec_Result process_store_key_container(Sec_ProcessorHandle* processorHandle, void* data, + SEC_SIZE data_length, SEC_BYTE* key_buffer, SEC_SIZE* key_length, Sec_KeyContainer* key_container) { + if (SecStore_GetStoreLen(data) != data_length) { + SEC_LOG_ERROR("Secure store length does not match the expected one"); + return SEC_RESULT_FAILURE; + } + + /* validate the store */ + Sec_Result result = SecUtils_ValidateKeyStore(processorHandle, SEC_FALSE, data, data_length); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_ValidateKeyStore failed"); + return SEC_RESULT_FAILURE; + } + + SecUtils_KeyStoreHeader keystore_header; + result = SecStore_RetrieveData(processorHandle, SEC_FALSE, &keystore_header, sizeof(keystore_header), + key_buffer, SEC_KEYCONTAINER_MAX_LEN, data, data_length); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_ValidateKeyStore failed"); + return SEC_RESULT_FAILURE; + } + + if (keystore_header.inner_kc_type == SEC_KEYCONTAINER_SOC_INTERNAL_0) { + if (sizeof(SecAdapter_DerivedInputs) != SecStore_GetDataLen(data)) { + SEC_LOG_ERROR("Invalid key length in the store"); + return SEC_RESULT_FAILURE; + } + + SecAdapter_DerivedInputs* derived_inputs = (SecAdapter_DerivedInputs*) key_buffer; + Sec_Key derived_key; + result = derive_root_key_ladder(derived_inputs->input1, derived_inputs->input2, derived_inputs->input3, + derived_inputs->input4, SEC_AES_BLOCK_SIZE, &derived_key.handle, SEC_KEYTYPE_AES_128); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Derive_root_key_ladder failed"); + return SEC_RESULT_FAILURE; + } + + // Export the key + result = export_key(&derived_key, NULL, key_buffer, SEC_KEYCONTAINER_MAX_LEN, key_length); + sa_key_release(derived_key.handle); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Export of store keycontainer derived key failed"); + return SEC_RESULT_FAILURE; + } + + *key_container = SEC_KEYCONTAINER_EXPORTED; + } else if (SecKey_IsClearKeyContainer(keystore_header.inner_kc_type)) { + *key_container = keystore_header.inner_kc_type; + *key_length = SecStore_GetDataLen(data); + } else { + SEC_LOG_ERROR("Unsupported key container in store"); + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + } + + return SEC_RESULT_SUCCESS; +} + +static Sec_Result derive_root_key_ladder(const SEC_BYTE* c1, const SEC_BYTE* c2, const SEC_BYTE* c3, const SEC_BYTE* c4, + SEC_SIZE key_size, sa_key* key, Sec_KeyType key_type) { + + sa_kdf_parameters_root_key_ladder kdf_parameters = { + .c1 = c1, + .c1_length = c1 != NULL ? key_size : 0, + .c2 = c2, + .c2_length = c2 != NULL ? key_size : 0, + .c3 = c3, + .c3_length = c3 != NULL ? key_size : 0, + .c4 = c4, + .c4_length = c4 != NULL ? key_size : 0}; + + sa_rights rights; + rights_set_allow_all(&rights, key_type); + sa_status status = sa_key_derive(key, &rights, SA_KDF_ALGORITHM_ROOT_KEY_LADDER, &kdf_parameters); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +static Sec_Result derive_base_key(Sec_ProcessorHandle* processorHandle, SEC_BYTE* nonce, sa_key* key, + Sec_KeyType key_type) { + SEC_SIZE keySize = SEC_AES_BLOCK_SIZE; + SEC_BYTE c1[keySize]; + SEC_BYTE c2[keySize]; + SEC_BYTE c3[keySize]; + SEC_BYTE c4[keySize]; + + // Most SOCs use aesEcbNone. Some SOCs use desEdeNone and this will be fixed with a SOC specific patch. + Sec_Result result = SecKey_ComputeBaseKeyLadderInputs(processorHandle, "sivSha1", "aesEcbNone", nonce, + SEC_DIGESTALGORITHM_SHA1, keySize, c1, c2, c3, c4); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ComputeBaseKeyLadderInputs failed"); + return SEC_RESULT_FAILURE; + } + + // the first input is fixed to 0x00..01 + memset(c1, 0, sizeof(c1)); + c1[15] = 0x01; + + result = derive_root_key_ladder(c1, c2, c3, c4, keySize, key, key_type); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ComputeBaseKeyLadderInputs failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +static Sec_Result derive_hkdf(Sec_MacAlgorithm macAlgorithm, Sec_KeyType typeDerived, const SEC_BYTE* salt, + SEC_SIZE saltSize, const SEC_BYTE* info, SEC_SIZE infoSize, sa_key baseKey, + sa_key* derived_key) { + if (macAlgorithm != SEC_MACALGORITHM_HMAC_SHA1 && macAlgorithm != SEC_MACALGORITHM_HMAC_SHA256) { + SEC_LOG_ERROR("Unsupported mac algorithm specified: %d", macAlgorithm); + return SEC_RESULT_FAILURE; + } + + if (!SecKey_IsSymetric(typeDerived)) { + SEC_LOG_ERROR("Can only derive symmetric keys"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + sa_digest_algorithm digest_algorithm = + (macAlgorithm == SEC_MACALGORITHM_HMAC_SHA1) ? SA_DIGEST_ALGORITHM_SHA1 : SA_DIGEST_ALGORITHM_SHA256; + sa_kdf_parameters_hkdf kdf_parameters = { + .key_length = SecKey_GetKeyLenForKeyType(typeDerived), + .digest_algorithm = digest_algorithm, + .parent = baseKey, + .salt = salt, + .salt_length = saltSize, + .info = info, + .info_length = infoSize}; + + sa_rights rights; + rights_set_allow_all(&rights, typeDerived); + sa_status status = sa_key_derive(derived_key, &rights, SA_KDF_ALGORITHM_HKDF, &kdf_parameters); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +static Sec_Result derive_kdf_concat(Sec_DigestAlgorithm digestAlgorithm, Sec_KeyType typeDerived, + const SEC_BYTE* otherInfo, SEC_SIZE otherInfoSize, sa_key baseKey, + sa_key* derived_key) { + if (digestAlgorithm != SEC_DIGESTALGORITHM_SHA1 && digestAlgorithm != SEC_DIGESTALGORITHM_SHA256) { + SEC_LOG_ERROR("Unsupported digest algorithm specified: %d", digestAlgorithm); + return SEC_RESULT_FAILURE; + } + + if (!SecKey_IsSymetric(typeDerived)) { + SEC_LOG_ERROR("Can only derive symmetric keys"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + sa_digest_algorithm digest_algorithm = + (digestAlgorithm == SEC_DIGESTALGORITHM_SHA1) ? SA_DIGEST_ALGORITHM_SHA1 : SA_DIGEST_ALGORITHM_SHA256; + sa_kdf_parameters_concat kdf_parameters = { + .key_length = SecKey_GetKeyLenForKeyType(typeDerived), + .digest_algorithm = digest_algorithm, + .parent = baseKey, + .info = otherInfo, + .info_length = otherInfoSize}; + + sa_rights rights; + rights_set_allow_all(&rights, typeDerived); + sa_status status = sa_key_derive(derived_key, &rights, SA_KDF_ALGORITHM_CONCAT, &kdf_parameters); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +static Sec_Result derive_kdf_cmac(Sec_KeyType typeDerived, const SEC_BYTE* otherData, SEC_SIZE otherDataSize, + const SEC_BYTE* counter, SEC_SIZE counterSize, sa_key baseKey, sa_key* derived_key) { + if (!SecKey_IsSymetric(typeDerived)) { + SEC_LOG_ERROR("Can only derive symmetric keys"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + if (counterSize != 1) { + SEC_LOG_ERROR("Only 1 byte counter is supported"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + if (*counter < 1 || *counter > 4) { + SEC_LOG_ERROR("Invalid counter passed in: %d", *counter); + return SEC_RESULT_INVALID_PARAMETERS; + } + + sa_kdf_parameters_cmac cmac_parameters = { + .key_length = SecKey_GetKeyLenForKeyType(typeDerived), + .parent = baseKey, + .other_data = otherData, + .other_data_length = otherDataSize, + .counter = *counter}; + + sa_rights rights; + rights_set_allow_all(&rights, typeDerived); + sa_status status = sa_key_derive(derived_key, &rights, SA_KDF_ALGORITHM_CMAC, &cmac_parameters); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +static Sec_Result unwrap_key(Sec_ProcessorHandle* processorHandle, Sec_CipherAlgorithm algorithm, + Sec_KeyType wrapped_key_type, SEC_SIZE wrapped_key_offset, SEC_OBJECTID id, SEC_BYTE* iv, SEC_BYTE* input, + SEC_SIZE input_len, SEC_BYTE* out_key, SEC_SIZE* out_key_len) { + Sec_KeyHandle* keyHandle; + Sec_Result result = SecKey_GetInstance(processorHandle, id, &keyHandle); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return result; + } + + sa_rights rights; + rights_set_allow_all(&rights, wrapped_key_type); + sa_key_type key_type; + void* key_parameters; + result = get_sa_key_type(wrapped_key_type, &key_type, &key_parameters); + if (result != SEC_RESULT_SUCCESS) { + SecKey_Release(keyHandle); + SEC_LOG_ERROR("Get_sa_key_type failed"); + return result; + } + + void* cipher_parameters; + sa_cipher_algorithm cipher_algorithm; + result = get_cipher_algorithm(algorithm, SEC_TRUE, &cipher_algorithm, &cipher_parameters, iv, + SecKey_GetKeyLenForKeyType(wrapped_key_type), wrapped_key_offset); + if (result != SEC_RESULT_SUCCESS) { + SEC_FREE(key_parameters); + SecKey_Release(keyHandle); + SEC_LOG_ERROR("Get_cipher_algorithm failed"); + return result; + } + + Sec_Key unwrapped_key; + sa_status status = sa_key_unwrap(&unwrapped_key.handle, &rights, key_type, key_parameters, cipher_algorithm, + cipher_parameters, keyHandle->key.handle, input, input_len); + SEC_FREE(key_parameters); + SEC_FREE(cipher_parameters); + SecKey_Release(keyHandle); + CHECK_STATUS(status) + + // Export the key + result = export_key(&unwrapped_key, NULL, out_key, SEC_KEYCONTAINER_MAX_LEN, out_key_len); + sa_key_release(unwrapped_key.handle); + return result; +} + +static Sec_Result get_sa_key_type(Sec_KeyType keyType, sa_key_type* out_key_type, void** parameters) { + + sa_generate_parameters_symmetric parameters_symmetric; + sa_generate_parameters_rsa parameters_rsa; + sa_generate_parameters_ec parameters_ec; + *parameters = NULL; + switch (keyType) { + case SEC_KEYTYPE_AES_128: + case SEC_KEYTYPE_AES_256: + case SEC_KEYTYPE_HMAC_128: + case SEC_KEYTYPE_HMAC_160: + case SEC_KEYTYPE_HMAC_256: + *out_key_type = SA_KEY_TYPE_SYMMETRIC; + *parameters = calloc(1, sizeof(parameters_symmetric)); + if (parameters == NULL) + return SEC_RESULT_FAILURE; + + ((sa_generate_parameters_symmetric*) *parameters)->key_length = SecKey_GetKeyLenForKeyType(keyType); + return SEC_RESULT_SUCCESS; + + case SEC_KEYTYPE_RSA_1024: + case SEC_KEYTYPE_RSA_2048: + case SEC_KEYTYPE_RSA_3072: + *out_key_type = SA_KEY_TYPE_RSA; + *parameters = calloc(1, sizeof(sa_generate_parameters_rsa)); + if (parameters == NULL) + return SEC_RESULT_FAILURE; + + ((sa_generate_parameters_rsa*) *parameters)->modulus_length = SecKey_GetKeyLenForKeyType(keyType); + return SEC_RESULT_SUCCESS; + + case SEC_KEYTYPE_ECC_NISTP256: + *out_key_type = SA_KEY_TYPE_EC; + *parameters = calloc(1, sizeof(sa_generate_parameters_ec)); + if (parameters == NULL) + return SEC_RESULT_FAILURE; + + ((sa_generate_parameters_ec*) *parameters)->curve = SA_ELLIPTIC_CURVE_NIST_P256; + return SEC_RESULT_SUCCESS; + + default: + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + } +} + +static Sec_Result export_key(Sec_Key* key, SEC_BYTE* derivationInput, SEC_BYTE* exportedKey, SEC_SIZE keyBufferLen, + SEC_SIZE* keyBytesWritten) { + + SEC_BYTE mixin[SEC_AES_BLOCK_SIZE]; + if (derivationInput == NULL) + sa_crypto_random(mixin, sizeof(mixin)); + else + memcpy(mixin, derivationInput, SEC_AES_BLOCK_SIZE); + + // Get key length. + if (exportedKey == NULL) { + size_t out_length = 0; + sa_status status = sa_key_export(exportedKey, &out_length, mixin, SEC_AES_BLOCK_SIZE, key->handle); + CHECK_STATUS(status) + + // Include the length of the derivationInput. + *keyBytesWritten = out_length; + return SEC_RESULT_SUCCESS; + } + + size_t out_length = keyBufferLen; + sa_status status = sa_key_export(exportedKey, &out_length, mixin, SEC_AES_BLOCK_SIZE, key->handle); + CHECK_STATUS(status) + *keyBytesWritten = out_length; + return SEC_RESULT_SUCCESS; +} + +static int disable_passphrase_prompt(char* buf, int size, int rwflag, void* u) { + return 0; +} diff --git a/src/sec_adapter_key.h b/src/sec_adapter_key.h new file mode 100644 index 0000000..6f8ed9b --- /dev/null +++ b/src/sec_adapter_key.h @@ -0,0 +1,55 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_ADAPTER_KEY_H +#define SEC_ADAPTER_KEY_H + +#include "sa.h" +#include "sec_security.h" +#include "sec_security_comcastids.h" +#include + +typedef union Sec_Key_union { + // if key_type is SEC_KEYTYPE_RSA_XXXX_PUBLIC + RSA* rsa; + // if key_type is SEC_KEYTYPE_ECC_NISTP256_PUBLIC + EC_KEY* ec_key; + // if key_type is anything else + sa_key handle; +} Sec_Key; + +typedef struct Sec_KeyInfo_struct { + // This field is effectively unused, but is maintained for backward compatibility. + Sec_KeyType key_type; + Sec_KeyContainer kc_type; +} Sec_KeyInfo; + +typedef struct Sec_KeyData_struct { + Sec_KeyInfo info; + SEC_BYTE key_container[SEC_KEYCONTAINER_MAX_LEN]; + SEC_SIZE kc_len; +} Sec_KeyData; + +void rights_set_allow_all(sa_rights* rights, Sec_KeyType key_type); + +Sec_Result prepare_and_store_key_data(Sec_ProcessorHandle* processorHandle, Sec_StorageLoc location, + SEC_OBJECTID object_id, Sec_Key* key, Sec_KeyContainer key_container, void* key_buffer, SEC_SIZE key_length); + +const Sec_Key* get_key(Sec_KeyHandle* keyHandle); + +#endif // SEC_ADAPTER_KEY_H diff --git a/src/sec_adapter_key_legacy.h b/src/sec_adapter_key_legacy.h new file mode 100644 index 0000000..22cf868 --- /dev/null +++ b/src/sec_adapter_key_legacy.h @@ -0,0 +1,44 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_ADAPTER_KEY_LEGACY_H +#define SEC_ADAPTER_KEY_LEGACY_H + +#define VERSION_2_KEY_CONTAINER 2 + +/** + * Import parameters for a Legacy SOC. This structure is used to signal the version of the Legacy + * key container. This structure is required for SecApi 2 (non-JWT) key containers and is optional for SecApi + * 3 (or JWT) key containers. If the sa_key_import parameters field is NULL, a SecApi 3 (JWT) key container is + * assumed. + */ +typedef struct { + /** The size of this structure. The most significant size byte is in length[0] and the least + significant size byte is in length[1]. */ + uint8_t length[2]; + + /** The version of the key container. Must be either version 2 or version 3. */ + uint8_t version; + + /** The default key rights to use only if the key container does not contain included key rights. */ + sa_rights default_rights; + + uint64_t object_id; +} sa_import_parameters_soc_legacy; + +#endif // SEC_ADAPTER_KEY_LEGACY_H diff --git a/src/sec_adapter_keyexchange.c b/src/sec_adapter_keyexchange.c new file mode 100644 index 0000000..2dd16ca --- /dev/null +++ b/src/sec_adapter_keyexchange.c @@ -0,0 +1,246 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sa.h" +#include "sec_adapter_key.h" +#include "sec_adapter_processor.h" +#include "sec_security.h" +#include + +#define BUFFER_SIZE 4096 + +struct Sec_KeyExchangeHandle_struct { + Sec_ProcessorHandle* processorHandle; + Sec_KeyExchangeAlgorithm alg; + void* parameters; + sa_key* key; +}; + +Sec_Result SecKeyExchange_GetInstance(Sec_ProcessorHandle* processorHandle, Sec_KeyExchangeAlgorithm exchangeType, + void* exchangeParameters, Sec_KeyExchangeHandle** keyExchangeHandle) { + CHECK_PROCHANDLE(processorHandle) + + if (keyExchangeHandle == NULL) { + SEC_LOG_ERROR("NULL keyExchangeHandle"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + if (exchangeParameters == NULL) { + SEC_LOG_ERROR("NULL exchangeParameters"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + *keyExchangeHandle = NULL; + sa_generate_parameters_dh* dh_parameters = NULL; + sa_generate_parameters_ec* ec_parameters = NULL; + + switch (exchangeType) { + case SEC_KEYEXCHANGE_DH: + dh_parameters = calloc(1, sizeof(sa_generate_parameters_dh)); + if (dh_parameters == NULL) { + SEC_LOG_ERROR("Calloc failed"); + return SEC_RESULT_FAILURE; + } + + Sec_DHParameters* sec_dh_parameters = (Sec_DHParameters*) exchangeParameters; + dh_parameters->g = sec_dh_parameters->g; + dh_parameters->g_length = sec_dh_parameters->gLen; + dh_parameters->p = sec_dh_parameters->p; + dh_parameters->p_length = sec_dh_parameters->pLen; + break; + + case SEC_KEYEXCHANGE_ECDH: + if (((Sec_ECDHParameters*) exchangeParameters)->curve != NISTP256) { + SEC_LOG_ERROR("Unknown EC curve"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + ec_parameters = calloc(1, sizeof(sa_generate_parameters_ec)); + if (ec_parameters == NULL) { + SEC_LOG_ERROR("Calloc failed"); + return SEC_RESULT_FAILURE; + } + + ec_parameters->curve = SA_ELLIPTIC_CURVE_NIST_P256; + break; + + default: + SEC_LOG_ERROR("Unknown exchange_type encountered: %d", exchangeType); + return SEC_RESULT_INVALID_PARAMETERS; + } + + *keyExchangeHandle = calloc(1, sizeof(Sec_KeyExchangeHandle)); + if (*keyExchangeHandle == NULL) { + SEC_FREE(dh_parameters); + SEC_FREE(ec_parameters); + SEC_LOG_ERROR("Calloc failed"); + return SEC_RESULT_FAILURE; + } + + (*keyExchangeHandle)->processorHandle = processorHandle; + (*keyExchangeHandle)->alg = exchangeType; + (*keyExchangeHandle)->parameters = dh_parameters != NULL ? (void*) dh_parameters : (void*) ec_parameters; + + return *keyExchangeHandle != NULL ? SEC_RESULT_SUCCESS : SEC_RESULT_FAILURE; +} + +Sec_Result SecKeyExchange_GenerateKeys(Sec_KeyExchangeHandle* keyExchangeHandle, SEC_BYTE* publicKey, + SEC_SIZE pubKeySize) { + CHECK_HANDLE(keyExchangeHandle) + if (publicKey == NULL) { + SEC_LOG_ERROR("NULL publicKey"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + sa_key_type type; + sa_rights rights; + switch (keyExchangeHandle->alg) { + case SEC_KEYEXCHANGE_DH: + type = SA_KEY_TYPE_DH; + rights_set_allow_all(&rights, SEC_KEYTYPE_RSA_1024); + break; + + case SEC_KEYEXCHANGE_ECDH: + type = SA_KEY_TYPE_EC; + rights_set_allow_all(&rights, SEC_KEYTYPE_ECC_NISTP256); + break; + + default: + SEC_LOG_ERROR("Invalid key exchange type"); + return SEC_RESULT_FAILURE; + } + + keyExchangeHandle->key = calloc(1, sizeof(sa_key)); + if (keyExchangeHandle->key == NULL) { + SEC_LOG_ERROR("Calloc failed"); + return SEC_RESULT_FAILURE; + } + + sa_status status = sa_key_generate(keyExchangeHandle->key, &rights, type, keyExchangeHandle->parameters); + CHECK_STATUS(status) + size_t out_length = pubKeySize; + SEC_BYTE public_key_bytes[BUFFER_SIZE]; + status = sa_key_get_public(&public_key_bytes, &out_length, *keyExchangeHandle->key); + CHECK_STATUS(status) + + Sec_ECCRawPublicKey ecc_raw_public_key; + switch (keyExchangeHandle->alg) { + case SEC_KEYEXCHANGE_DH: + memcpy(publicKey, &public_key_bytes, out_length); + break; + + case SEC_KEYEXCHANGE_ECDH: + Sec_Uint32ToBEBytes(out_length / 2, ecc_raw_public_key.key_len); + memcpy(ecc_raw_public_key.x, &public_key_bytes[0], out_length / 2); + memcpy(ecc_raw_public_key.y, &public_key_bytes[out_length / 2], out_length / 2); + ecc_raw_public_key.type = SEC_KEYTYPE_ECC_NISTP256_PUBLIC; + memcpy(publicKey, &ecc_raw_public_key, sizeof(ecc_raw_public_key)); + break; + + default: + SEC_LOG_ERROR("Invalid key exchange type"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecKeyExchange_ComputeSecret(Sec_KeyExchangeHandle* keyExchangeHandle, SEC_BYTE* otherPublicKey, + SEC_SIZE otherPublicKeySize, Sec_KeyType typeComputed, SEC_OBJECTID idComputed, + Sec_StorageLoc locComputed) { + CHECK_HANDLE(keyExchangeHandle) + if (keyExchangeHandle->key == NULL) { + SEC_LOG_ERROR("NULL keyExchangeHandle->key"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + if (otherPublicKey == NULL) { + SEC_LOG_ERROR("NULL otherPublicKey"); + return SEC_RESULT_INVALID_PARAMETERS; + } + + if (!SecKey_IsSymetric(typeComputed)) { + SEC_LOG_ERROR("Invalid key type encountered: %d", typeComputed); + return SEC_RESULT_FAILURE; + } + + // Set rights to derive only. SecApi 3.0 only allows a secret to be generated that can be fed into a derivation + // algorithm. It does not truncate the key to the typeComputed key type like is specified in SecApi 2. So disable + // the ability to sign, decrypt, encrypt, or unwrap. + sa_key_exchange_algorithm algorithm; + sa_rights rights; + rights_set_allow_all(&rights, SEC_KEYTYPE_NUM); + + SEC_BYTE public_key_bytes[BUFFER_SIZE]; + uint32_t key_len; + Sec_KeyContainer key_container; + switch (keyExchangeHandle->alg) { + case SEC_KEYEXCHANGE_DH: + algorithm = SA_KEY_EXCHANGE_ALGORITHM_DH; + memcpy(&public_key_bytes, otherPublicKey, otherPublicKeySize); + key_len = otherPublicKeySize; + + // The resulting key is actually the length of the p value. We just need a key container that will trigger + // the key to be exported and the result will be an exported key container, so pick the largest RSA key. + key_container = SEC_KEYCONTAINER_DER_RSA_3072; + break; + + case SEC_KEYEXCHANGE_ECDH: + algorithm = SA_KEY_EXCHANGE_ALGORITHM_ECDH; + if (otherPublicKeySize != sizeof(Sec_ECCRawPublicKey)) { + SEC_LOG_ERROR("Invalid ECC key"); + return SEC_RESULT_FAILURE; + } + + Sec_ECCRawPublicKey* ecc_raw_public_key = (Sec_ECCRawPublicKey*) otherPublicKey; + key_len = Sec_BEBytesToUint32(ecc_raw_public_key->key_len) * 2; + memcpy(&public_key_bytes[0], ecc_raw_public_key->x, key_len / 2); + memcpy(&public_key_bytes[key_len / 2], ecc_raw_public_key->y, key_len / 2); + + // The resulting secret is actually a valid AES 256 key. We just need a key container that will trigger + // the key to be exported and the result will be an exported key container, so just use AES_256. + key_container = SEC_KEYCONTAINER_RAW_AES_256; + break; + + default: + SEC_LOG_ERROR("Unknown alg encountered: %d", keyExchangeHandle->alg); + return SEC_RESULT_FAILURE; + } + + sa_key shared_secret; + sa_status status = sa_key_exchange(&shared_secret, &rights, algorithm, *keyExchangeHandle->key, public_key_bytes, + key_len, NULL); + CHECK_STATUS(status) + + Sec_Key key = {.handle = shared_secret}; + return prepare_and_store_key_data(keyExchangeHandle->processorHandle, locComputed, idComputed, &key, key_container, + NULL, 0); +} + +Sec_Result SecKeyExchange_Release(Sec_KeyExchangeHandle* keyExchangeHandle) { + if (keyExchangeHandle != NULL) { + if (keyExchangeHandle->key != NULL) + sa_key_release(*keyExchangeHandle->key); + + SEC_FREE(keyExchangeHandle->parameters); + SEC_FREE(keyExchangeHandle->key); + SEC_FREE(keyExchangeHandle); + } + + return SEC_RESULT_SUCCESS; +} diff --git a/src/sec_adapter_logger.c b/src/sec_adapter_logger.c new file mode 100644 index 0000000..33bdd30 --- /dev/null +++ b/src/sec_adapter_logger.c @@ -0,0 +1,70 @@ +/** + * Copyright 2020-2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_security.h" +#include + +#define BUFFER_SIZE 1024 + +SecApiLogCallback g_sec_logcb = Sec_DefaultLoggerCb; + +void Sec_SetLogger(SecApiLogCallback cb) { + g_sec_logcb = cb; +} + +SecApiLogCallback Sec_GetLogger(void) { + return g_sec_logcb; +} + +void Sec_DefaultLoggerCb(const char* fmt, ...) { + va_list args; + + va_start(args, fmt); + vfprintf(stdout, fmt, args); // NOLINT + va_end(args); + + fflush(stdout); +} + +void Sec_PrintHex(void* data, SEC_SIZE numBytes) { + char buffer[BUFFER_SIZE]; + memset(buffer, 0, sizeof(buffer)); + char* ptr = buffer; + + size_t maxBytes = (sizeof(buffer) - 1) / 2; + + SEC_BYTE* data_ptr = (SEC_BYTE*) data; + SEC_SIZE i; + + for (i = 0; i < SEC_MIN(maxBytes, numBytes); ++i) { + int result = sprintf(ptr, "%02x", data_ptr[i]); + if (result < 0) + break; + + ptr += result; + } + + SEC_PRINT(buffer); +} + +/** + * @brief NOP logger implementation + */ +void Sec_NOPLoggerCb(const char* fmt, ...) { + // NOOP +} diff --git a/src/sec_adapter_mac.c b/src/sec_adapter_mac.c new file mode 100644 index 0000000..e38208a --- /dev/null +++ b/src/sec_adapter_mac.c @@ -0,0 +1,239 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sa.h" +#include "sec_adapter_processor.h" +#include "sec_security.h" + +struct Sec_MacHandle_struct { + Sec_ProcessorHandle* processorHandle; + Sec_MacAlgorithm algorithm; + Sec_KeyHandle* keyHandle; + sa_crypto_mac_context mac_context; +}; + +Sec_Result SecMac_SingleInput(Sec_ProcessorHandle* processorHandle, Sec_MacAlgorithm alg, Sec_KeyHandle* keyHandle, + SEC_BYTE* input, SEC_SIZE input_len, SEC_BYTE* mac, SEC_SIZE* mac_len) { + Sec_Result result; + Sec_MacHandle* macHandle= NULL; + + result = SecMac_GetInstance(processorHandle, alg, keyHandle, &macHandle); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecMac_GetInstance failed"); + return result; + } + + result = SecMac_Update(macHandle, input, input_len); + SecMac_Release(macHandle, mac, mac_len); + return result; +} + +Sec_Result SecMac_SingleInputId(Sec_ProcessorHandle* processorHandle, Sec_MacAlgorithm alg, SEC_OBJECTID key, + SEC_BYTE* input, SEC_SIZE input_len, SEC_BYTE* mac, SEC_SIZE* mac_len) { + Sec_KeyHandle* keyHandle = NULL; + Sec_Result result = SecKey_GetInstance(processorHandle, key, &keyHandle); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return result; + } + + result = SecMac_SingleInput(processorHandle, alg, keyHandle, input, input_len, mac, mac_len); + SecKey_Release(keyHandle); + + return result; +} + +/** + * @brief Obtain a handle for the MAC calculator. + * + * @param processorHandle secure processor handle. + * @param algorithm MAC algorithm to use for MAC calculation. + * @param keyHandle key to use for the MAC calculation. + * @param macHandle output MAC calculator handle. + * + * @return The status of the operation. + */ +Sec_Result SecMac_GetInstance(Sec_ProcessorHandle* processorHandle, Sec_MacAlgorithm algorithm, + Sec_KeyHandle* keyHandle, Sec_MacHandle** macHandle) { + CHECK_PROCHANDLE(processorHandle) + *macHandle = NULL; + + Sec_KeyType key_type = SecKey_GetKeyType(keyHandle); + Sec_Result result = SecMac_IsValidKey(key_type, algorithm); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Not a valid mac key"); + return result; + } + + *macHandle = calloc(1, sizeof(Sec_MacHandle)); + if (*macHandle == NULL) { + SEC_LOG_ERROR("Malloc failed"); + return SEC_RESULT_FAILURE; + } + + (*macHandle)->processorHandle = processorHandle; + (*macHandle)->algorithm = algorithm; + (*macHandle)->keyHandle = keyHandle; + + sa_mac_algorithm mac_algorithm; + sa_mac_parameters_hmac hmac_parameters; + void* parameters; + switch (algorithm) { + case SEC_MACALGORITHM_HMAC_SHA1: + mac_algorithm = SA_MAC_ALGORITHM_HMAC; + hmac_parameters.digest_algorithm = SA_DIGEST_ALGORITHM_SHA1; + parameters = &hmac_parameters; + break; + + case SEC_MACALGORITHM_HMAC_SHA256: + mac_algorithm = SA_MAC_ALGORITHM_HMAC; + hmac_parameters.digest_algorithm = SA_DIGEST_ALGORITHM_SHA256; + parameters = &hmac_parameters; + break; + + case SEC_MACALGORITHM_CMAC_AES_128: + mac_algorithm = SA_MAC_ALGORITHM_CMAC; + parameters = NULL; + break; + + default: + return SEC_RESULT_INVALID_PARAMETERS; + } + + const Sec_Key* key = get_key(keyHandle); + sa_status status = sa_crypto_mac_init(&(*macHandle)->mac_context, mac_algorithm, key->handle, parameters); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Updates the digest value with the input data. + * + * @param macHandle mac handle. + * @param input pointer to the input data. + * @param size of the input buffer. + * + * @return The status of the operation. + */ +Sec_Result SecMac_Update(Sec_MacHandle* macHandle, SEC_BYTE* input, SEC_SIZE inputSize) { + CHECK_HANDLE(macHandle) + sa_status status = sa_crypto_mac_process(macHandle->mac_context, input, inputSize); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Updates the digest value with the contents of a key. + * + * @param macHandle mac handle. + * @param keyHandle key to use. + * + * @return The status of the operation. + */ +Sec_Result SecMac_UpdateWithKey(Sec_MacHandle* macHandle, Sec_KeyHandle* keyHandle) { + CHECK_HANDLE(macHandle) + CHECK_HANDLE(keyHandle) + const Sec_Key* key = get_key(keyHandle); + sa_status status = sa_crypto_mac_process_key(macHandle->mac_context, key->handle); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Calculate the resulting MAC value and release the MAC object. + * + * @param macHandle mac handle. + * @param macBuffer pointer to an output buffer that will be filled with the resulting. + * MAC value. Buffer should be SEC_MAC_MAX_LEN bytes long. + * @param macSize pointer to a value that will be set to actual size of the MAC value. + * + * @return The status of the operation. + */ +Sec_Result SecMac_Release(Sec_MacHandle* macHandle, SEC_BYTE* macBuffer, SEC_SIZE* macSize) { + CHECK_HANDLE(macHandle) + + size_t out_length = SEC_MAC_MAX_LEN; + sa_status status = sa_crypto_mac_compute(macBuffer, &out_length, macHandle->mac_context); + sa_crypto_mac_release(macHandle->mac_context); + CHECK_STATUS(status) + + *macSize = out_length; + SEC_FREE(macHandle); + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Check whether the passed in key type is valid for a chosen MAC algorithm. + * + * @param key_type key type. + * @param algorithm MAC algorithm. + * + * @return status of the operation. + */ + +Sec_Result SecMac_IsValidKey(Sec_KeyType key_type, Sec_MacAlgorithm algorithm) { + switch (algorithm) { + case SEC_MACALGORITHM_HMAC_SHA1: + case SEC_MACALGORITHM_HMAC_SHA256: + // SecApi 3 does not distinguish between HMAC and AES keys. + if (key_type == SEC_KEYTYPE_HMAC_128 || + key_type == SEC_KEYTYPE_HMAC_160 || + key_type == SEC_KEYTYPE_HMAC_256 || + key_type == SEC_KEYTYPE_AES_128 || + key_type == SEC_KEYTYPE_AES_256) { + return SEC_RESULT_SUCCESS; + } else { + return SEC_RESULT_FAILURE; + } + + case SEC_MACALGORITHM_CMAC_AES_128: + // SecApi 3 does not distinguish between HMAC and AES keys. + if (key_type == SEC_KEYTYPE_HMAC_128 || + key_type == SEC_KEYTYPE_HMAC_256 || + key_type == SEC_KEYTYPE_AES_128 || + key_type == SEC_KEYTYPE_AES_256) { + return SEC_RESULT_SUCCESS; + } else { + return SEC_RESULT_FAILURE; + } + + default: + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + } +} + +/** + * @brief Obtain a digest algorithm used by a specified MAC algorithm. + * + * @param alg MAC algorithm. + * + * @return digest algorithm used. + */ +Sec_DigestAlgorithm SecMac_GetDigestAlgorithm(Sec_MacAlgorithm algorithm) { + switch (algorithm) { + case SEC_MACALGORITHM_HMAC_SHA1: + return SEC_DIGESTALGORITHM_SHA1; + + case SEC_MACALGORITHM_HMAC_SHA256: + return SEC_DIGESTALGORITHM_SHA256; + + case SEC_MACALGORITHM_CMAC_AES_128: + default: + return SEC_DIGESTALGORITHM_NUM; + } +} diff --git a/src/sec_adapter_processor.c b/src/sec_adapter_processor.c new file mode 100644 index 0000000..cd0430e --- /dev/null +++ b/src/sec_adapter_processor.c @@ -0,0 +1,505 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_adapter_processor.h" + +// Processor handles are stored in the threadlocal proc_handle. Every time SecProcessor_GetInstance_Directories is +// called, proc_handle will be searched for a Sec_ProcessorHandle with the same globalDir and appDir. If an existing +// Sec_ProcessorHandle is found, it is returned. If not, a new one is created and stored in proc_handle. All will be +// destroyed when the thread or the application shuts down. At most 25 unique Sec_ProcessorHandles can be created per +// thread. 25 was chosen because it is believed that this is more than enough to handle current applications. It +// can be increase if this is not enough. +#define MAX_PROC_HANDLES 25 + +struct Sec_ProcessorInitParams_struct { +}; + +static SEC_BOOL initialized = SEC_FALSE; +static _Thread_local Sec_ProcessorHandle* processorHandles[MAX_PROC_HANDLES]; +static pthread_key_t key; +static pthread_once_t key_once = PTHREAD_ONCE_INIT; + +static void make_key(); + +static void release_proc_handle(void* handle); + +static void proc_shutdown(); + +static Sec_Result Sec_SetStorageDir(const char* provided_dir, const char* default_dir, char* output_dir); + +static void thread_shutdown(void* unused); + +/** + * @brief Initialize secure processor. + * + * Initializes the secure processor, generates key derivation base key, + * sets up all required resources. Only one secure processor can be + * active at a time. + * + * @param processorHandle pointer to a processor handle that will be set to + * a constructed handle. + * @param socInitParams pointer to initialization information for the secure + * processor. This structure is implementation specific. + * + * @return The status of the operation. + */ +Sec_Result SecProcessor_GetInstance(Sec_ProcessorHandle** processorHandle, + Sec_ProcessorInitParams* socInitParams) { + return SecProcessor_GetInstance_Directories(processorHandle, SEC_GLOBAL_DIR_DEFAULT, SEC_GLOBAL_DIR_DEFAULT); +} + +/** + * @brief Initialize secure processor. + * + * Initializes the secure processor, generates key derivation base key, + * sets up all required resources. Only one secure processor can be + * active at a time. + * + * @param processorHandle pointer to a processor handle that will be set to + * a constructed handle. + * @param globalDir path to the read only object directory. Can be set to NULL. + * @param appDir path to the read/write object directory. Can be set to NULL. + * + * @return The status of the operation. + */ +Sec_Result SecProcessor_GetInstance_Directories(Sec_ProcessorHandle** processorHandle, const char* globalDir, + const char* appDir) { + Sec_Result result; + SecAdapter_DerivedInputs derived_inputs; + SecUtils_KeyStoreHeader keystore_header; + SEC_BYTE store[SEC_KEYCONTAINER_MAX_LEN]; + + if (pthread_once(&key_once, make_key) != 0) + return SEC_RESULT_FAILURE; + + if (processorHandle == NULL) { + SEC_LOG_ERROR("proc_handle is NULL"); + return SEC_RESULT_FAILURE; + } + + size_t free_proc_handle = MAX_PROC_HANDLES; + for (size_t i = 0; i < MAX_PROC_HANDLES; i++) { + const char* temp_app_dir = appDir == NULL ? SEC_APP_DIR_DEFAULT : appDir; + const char* temp_global_dir = globalDir == NULL ? SEC_GLOBAL_DIR_DEFAULT : globalDir; + if (processorHandles[i] != NULL) { + if (memcmp(processorHandles[i]->global_dir, temp_global_dir, strlen(temp_global_dir)) == 0 && + memcmp(processorHandles[i]->app_dir, temp_app_dir, strlen(temp_app_dir)) == 0) { + *processorHandle = processorHandles[i]; + return SEC_RESULT_SUCCESS; + } + } else { + free_proc_handle = i; + break; + } + } + + if (free_proc_handle == MAX_PROC_HANDLES) { + SEC_LOG_ERROR("No free proc handles"); + return SEC_RESULT_FAILURE; + } + + /* create handle */ + *processorHandle = calloc(1, sizeof(Sec_ProcessorHandle)); + if (*processorHandle == NULL) { + SEC_LOG_ERROR("Calloc failed"); + return SEC_RESULT_FAILURE; + } + + /* setup key and cert directories */ + if (appDir != NULL) { + (*processorHandle)->app_dir = (char*) calloc(1, SEC_MAX_FILE_PATH_LEN); + if ((*processorHandle)->app_dir == NULL) { + SEC_LOG_ERROR("Calloc failed"); + return SEC_RESULT_FAILURE; + } + + result = Sec_SetStorageDir(appDir, SEC_APP_DIR_DEFAULT, (*processorHandle)->app_dir); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Error creating app_dir"); + SEC_FREE((*processorHandle)->app_dir); + SEC_FREE(*processorHandle); + return result; + } + + result = SecUtils_MkDir((*processorHandle)->app_dir); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Error creating app_dir"); + SEC_FREE((*processorHandle)->app_dir); + SEC_FREE(*processorHandle); + return result; + } + } + + if (globalDir != NULL) { + (*processorHandle)->global_dir = (char*) calloc(1, SEC_MAX_FILE_PATH_LEN); + if ((*processorHandle)->global_dir == NULL) { + SEC_LOG_ERROR("Calloc failed"); + return SEC_RESULT_FAILURE; + } + + result = Sec_SetStorageDir(globalDir, SEC_GLOBAL_DIR_DEFAULT, (*processorHandle)->global_dir); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Error creating global_dir"); + SEC_FREE((*processorHandle)->app_dir); + SEC_FREE((*processorHandle)->global_dir); + SEC_FREE(*processorHandle); + return result; + } + } + + // Calls client_thread_shutdown when the thread exits. + if (pthread_key_create(&key, thread_shutdown) != 0) { + SEC_LOG_ERROR("tss_create failed"); + return SEC_RESULT_FAILURE; + } + + processorHandles[free_proc_handle] = *processorHandle; + if (pthread_setspecific(key, processorHandle) != 0) { + SEC_LOG_ERROR("Error storing procHandle in thread local storage"); + SEC_FREE((*processorHandle)->app_dir); + SEC_FREE((*processorHandle)->global_dir); + processorHandles[free_proc_handle] = NULL; + SEC_FREE(*processorHandle); + return SEC_RESULT_FAILURE; + } + + // Initial OpenSSL. + Sec_InitOpenSSL(); + + /* generate sec store proc ins */ + result = SecStore_GenerateLadderInputs(*processorHandle, SEC_STORE_AES_LADDER_INPUT, NULL, + (SEC_BYTE*) &derived_inputs, sizeof(derived_inputs)); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Error Generating LadderInputs"); + SEC_FREE((*processorHandle)->app_dir); + SEC_FREE((*processorHandle)->global_dir); + processorHandles[free_proc_handle] = NULL; + SEC_FREE(*processorHandle); + return result; + } + + result = SecUtils_FillKeyStoreUserHeader(*processorHandle, &keystore_header, SEC_KEYCONTAINER_SOC_INTERNAL_0); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Error Filling KeyStoreUserHeader"); + SEC_FREE((*processorHandle)->app_dir); + SEC_FREE((*processorHandle)->global_dir); + processorHandles[free_proc_handle] = NULL; + SEC_FREE(*processorHandle); + return result; + } + + result = SecStore_StoreData(*processorHandle, SEC_FALSE, SEC_FALSE, (SEC_BYTE*) SEC_UTILS_KEYSTORE_MAGIC, + &keystore_header, sizeof(keystore_header), &derived_inputs, sizeof(derived_inputs), store, sizeof(store)); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Error storing derived_inputs"); + SEC_FREE((*processorHandle)->app_dir); + SEC_FREE((*processorHandle)->global_dir); + processorHandles[free_proc_handle] = NULL; + SEC_FREE(*processorHandle); + return result; + } + + result = SecKey_Provision(*processorHandle, SEC_OBJECTID_STORE_AES_KEY, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_KEYCONTAINER_STORE, store, SecStore_GetStoreLen(store)); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Error creating SEC_OBJECTID_STORE_AES_KEY"); + SEC_FREE((*processorHandle)->app_dir); + SEC_FREE((*processorHandle)->global_dir); + processorHandles[free_proc_handle] = NULL; + SEC_FREE(*processorHandle); + return result; + } + + result = SecStore_GenerateLadderInputs(*processorHandle, SEC_STORE_MAC_LADDER_INPUT, NULL, + (SEC_BYTE*) &derived_inputs, sizeof(derived_inputs)); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Error creating SEC_STORE_MAC_LADDER_INPUT"); + SEC_FREE((*processorHandle)->app_dir); + SEC_FREE((*processorHandle)->global_dir); + processorHandles[free_proc_handle] = NULL; + SEC_FREE(*processorHandle); + return result; + } + + result = SecUtils_FillKeyStoreUserHeader(*processorHandle, &keystore_header, SEC_KEYCONTAINER_SOC_INTERNAL_0); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Error creating keystore_header"); + SEC_FREE((*processorHandle)->app_dir); + SEC_FREE((*processorHandle)->global_dir); + processorHandles[free_proc_handle] = NULL; + SEC_FREE(*processorHandle); + return result; + } + + result = SecStore_StoreData(*processorHandle, SEC_FALSE, SEC_FALSE, (SEC_BYTE*) SEC_UTILS_KEYSTORE_MAGIC, + &keystore_header, sizeof(keystore_header), &derived_inputs, sizeof(derived_inputs), store, sizeof(store)); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Error creating sec_store"); + SEC_FREE((*processorHandle)->app_dir); + SEC_FREE((*processorHandle)->global_dir); + processorHandles[free_proc_handle] = NULL; + SEC_FREE(*processorHandle); + return result; + } + + result = SecKey_Provision(*processorHandle, SEC_OBJECTID_STORE_MACKEYGEN_KEY, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_KEYCONTAINER_STORE, store, SecStore_GetStoreLen(store)); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Error creating SEC_OBJECTID_STORE_MACKEYGEN_KEY"); + SEC_FREE((*processorHandle)->app_dir); + SEC_FREE((*processorHandle)->global_dir); + processorHandles[free_proc_handle] = NULL; + SEC_FREE(*processorHandle); + return result; + } + + // generate certificate mac key + const char* otherInfo = "certMacKeyhmacSha256concatKdfSha1"; + const char* nonce = "abcdefghijklmnopqr\0"; + result = SecKey_Derive_ConcatKDF(*processorHandle, SEC_OBJECTID_CERTSTORE_KEY, SEC_KEYTYPE_HMAC_256, + SEC_STORAGELOC_RAM, SEC_DIGESTALGORITHM_SHA256, (SEC_BYTE*) nonce, (SEC_BYTE*) otherInfo, + strlen(otherInfo)); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Error creating certificate mac key"); + SEC_FREE((*processorHandle)->app_dir); + SEC_FREE((*processorHandle)->global_dir); + processorHandles[free_proc_handle] = NULL; + SEC_FREE(*processorHandle); + return result; + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Get the minimum depth of the hardware key ladder. + * + * @param handle pointer to a handle. + * @param root root key type. + * + * @return The key ladder depth. + */ +SEC_SIZE SecProcessor_GetKeyLadderMinDepth(Sec_ProcessorHandle* processorHandle, Sec_KeyLadderRoot root) { + return SECAPI3_KEY_DEPTH; +} + +/** + * @brief Get the maximum depth of the hardware key ladder. + * + * @param handle pointer to a handle. + * @param root root key type. + * + * @return The key ladder depth. + */ +SEC_SIZE SecProcessor_GetKeyLadderMaxDepth(Sec_ProcessorHandle* processorHandle, Sec_KeyLadderRoot root) { + return SECAPI3_KEY_DEPTH; +} + +/** + * @brief Prints SOC specific version info. + * + * @param processorHandle secure processor handle. + */ +Sec_Result SecProcessor_PrintInfo(Sec_ProcessorHandle* processorHandle) { + CHECK_PROCHANDLE(processorHandle) + + SEC_BYTE deviceId[SEC_DEVICEID_LEN]; + if (SecProcessor_GetDeviceId(processorHandle, deviceId) == SEC_RESULT_SUCCESS) + SEC_PRINT("device id: " SEC_OBJECTID_PATTERN "\n", Sec_BEBytesToUint64(deviceId)); + else + SEC_PRINT("device id: unknown\n"); + + SEC_PRINT("platform: SEC_API_2_ADAPTER\n"); + SEC_PRINT("version: %s\n", SEC_API_VERSION); + + SEC_PRINT("app_dir: %s\n", processorHandle->app_dir); + SEC_PRINT("global_dir: %s\n", processorHandle->global_dir); + Sec_PrintOpenSSLVersion(); + + sa_version version; + sa_status status = sa_get_version(&version); + CHECK_STATUS(status) + + SEC_PRINT("specification_major: %ld, specification_minor: %ld, " + "specification_revision: %ld, implementation_revision: %ld\n", + version.specification_major, version.specification_minor, + version.specification_revision, version.implementation_revision); + + Sec_PrintOpenSSLVersion(); + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Get the Security Processor information (SecAPI version and build + * information). + * + * @param processorHandle secure processor handle. + * @param pointer to secure processor information. + */ +Sec_Result SecProcessor_GetInfo(Sec_ProcessorHandle* processorHandle, Sec_ProcessorInfo* secProcInfo) { + CHECK_PROCHANDLE(processorHandle) + + if (secProcInfo == NULL) + return SEC_RESULT_INVALID_PARAMETERS; + + sa_version version; + sa_status status = sa_get_version(&version); + CHECK_STATUS(status) + + memcpy(secProcInfo, &version, sizeof(sa_version)); + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Obtain the device id. + * + * @param processorHandle secure processor handle. + * @param deviceId pointer to a buffer that is SEC_DEVICEID_LEN long. The + * buffer will be filled with a device id. + * + * @return The status of the operation. + */ +Sec_Result SecProcessor_GetDeviceId(Sec_ProcessorHandle* processorHandle, SEC_BYTE* deviceId) { + CHECK_PROCHANDLE(processorHandle) + + if (deviceId == NULL) + return SEC_RESULT_INVALID_PARAMETERS; + + uint64_t sa3_device_id; + sa_status status = sa_get_device_id(&sa3_device_id); + CHECK_STATUS(status) + + Sec_Uint64ToBEBytes(sa3_device_id, deviceId); + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Release the security processor. + * + * @param processorHandle secure processor handle. + * + * @return The status of the operation. + */ +Sec_Result SecProcessor_Release(Sec_ProcessorHandle* processorHandle) { + // Do nothing. Released when the thread shutsdown. + return SEC_RESULT_SUCCESS; +} + +/** + * This was done for legacy BRCM HW which would get a performance boost if used with special pool of memory for AES + * cipher. For SecApi3, I would just map this to malloc. + * --Davor + */ +SEC_BYTE* Sec_NativeMalloc(Sec_ProcessorHandle* processorHandle, SEC_SIZE length) { + if (processorHandle == NULL) + return NULL; + + return malloc(length); +} + +/** + * This was done for legacy BRCM HW which would get a performance boost if used with special pool of memory for AES + * cipher. For SecApi3, I would just map this to malloc. + * --Davor + */ +void Sec_NativeFree(Sec_ProcessorHandle* processorHandle, void* ptr) { + if (processorHandle == NULL) + return; + + free(ptr); +} + +Sec_Result Sec_SetStorageDir(const char* provided_dir, const char* default_dir, char* output_dir) { + const char* dir_to_use; + size_t len; + + if (provided_dir == NULL || strlen(provided_dir) == 0) { + if (default_dir == NULL || strlen(default_dir) == 0) + return SEC_RESULT_FAILURE; + + dir_to_use = default_dir; + } else + dir_to_use = provided_dir; + + len = strlen(dir_to_use); + if (len >= (SEC_MAX_FILE_PATH_LEN - 2)) { + SEC_LOG_ERROR("Directory name length is too long"); + return SEC_RESULT_FAILURE; + } + + snprintf(output_dir, SEC_MAX_FILE_PATH_LEN, "%s", dir_to_use); + + if (output_dir[len - 1] != '/' && output_dir[len - 1] != '\\') { + output_dir[len] = '/'; + output_dir[len + 1] = '\0'; + } + + return SEC_RESULT_SUCCESS; +} + +static void make_key() { + // Calls release_proc_handle on thread exit and passes in the processorHandle stored in the key. But does not run + // on application (main thread) exit. + pthread_key_create(&key, release_proc_handle); + + for (size_t i = 0; i < MAX_PROC_HANDLES; i++) + processorHandles[i] = NULL; + + // Calls proc_shutdown when the application (main thread) exits. + if (atexit(proc_shutdown) != 0) { + SEC_LOG_ERROR("atexit failed"); + return; + } +} + +static void release_proc_handle(void* handle) { + if (handle == NULL) + return; + + Sec_ProcessorHandle* processorHandle = handle; + + /* release ram keys */ + while (processorHandle->ram_keys != NULL) + SecKey_Delete(processorHandle, processorHandle->ram_keys->object_id); + + /* release ram bundles */ + while (processorHandle->ram_bundles != NULL) + SecBundle_Delete(processorHandle, processorHandle->ram_bundles->object_id); + + /* release ram certs */ + while (processorHandle->ram_certs != NULL) + SecCertificate_Delete(processorHandle, processorHandle->ram_certs->object_id); + + SEC_FREE(processorHandle->app_dir); + SEC_FREE(processorHandle->global_dir); + + free(processorHandle); +} + +static void proc_shutdown() { + for (size_t i = 0; i < MAX_PROC_HANDLES; i++) { + if (processorHandles[i] != NULL) { + release_proc_handle(processorHandles[i]); + processorHandles[i] = NULL; + } + } +} + +static void thread_shutdown(void* unused) { + proc_shutdown(); +} diff --git a/src/sec_adapter_processor.h b/src/sec_adapter_processor.h new file mode 100644 index 0000000..d8a1706 --- /dev/null +++ b/src/sec_adapter_processor.h @@ -0,0 +1,120 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_ADAPTER_PROCESSOR_H +#define SEC_ADAPTER_PROCESSOR_H + +#include "sa.h" +#include "sec_adapter_key.h" +#include "sec_adapter_store.h" +#include "sec_adapter_utils.h" +#include "sec_security.h" +#include "sec_version.h" +#include +#include +#include + +#ifdef __cplusplus +#include +#else +#include +#endif + +typedef struct { + SEC_BYTE input1[SEC_AES_BLOCK_SIZE]; + SEC_BYTE input2[SEC_AES_BLOCK_SIZE]; + SEC_BYTE input3[SEC_AES_BLOCK_SIZE]; + SEC_BYTE input4[SEC_AES_BLOCK_SIZE]; +} SecAdapter_DerivedInputs; + +typedef struct { + SEC_BYTE mac[SEC_MAC_MAX_LEN]; + SEC_SIZE cert_len; + SEC_BYTE cert[SEC_CERT_MAX_DATA_LEN]; +} Sec_CertificateData; + +typedef struct { + SEC_BYTE bundle[SEC_BUNDLE_MAX_LEN]; + SEC_SIZE bundle_len; +} Sec_BundleData; + +typedef struct Sec_RAMKeyData_struct { + SEC_OBJECTID object_id; + Sec_KeyData key_data; + struct Sec_RAMKeyData_struct* next; +} Sec_RAMKeyData; + +typedef struct Sec_RAMCertificateData_struct { + SEC_OBJECTID object_id; + Sec_CertificateData cert_data; + struct Sec_RAMCertificateData_struct* next; +} Sec_RAMCertificateData; + +typedef struct Sec_RAMBundleData_struct { + SEC_OBJECTID object_id; + Sec_BundleData bundle_data; + struct Sec_RAMBundleData_struct* next; +} Sec_RAMBundleData; + +struct Sec_ProcessorHandle_struct { + Sec_RAMKeyData* ram_keys; + Sec_RAMBundleData* ram_bundles; + Sec_RAMCertificateData* ram_certs; + char* global_dir; + char* app_dir; + int device_settings_init_flag; +}; + +static const int SECAPI3_KEY_DEPTH = 4; + +#define SEC_APP_DIR_DEFAULT "./" +#define SEC_GLOBAL_DIR_DEFAULT "/opt/drm" + +#define CHECK_PROCHANDLE(handle) CHECK_HANDLE(handle) + +#define CHECK_HANDLE(handle) \ + if ((handle) == NULL) { \ + SEC_LOG_ERROR("Invalid handle"); \ + return SEC_RESULT_INVALID_HANDLE; \ + } + +#define CHECK_STATUS(status) \ + switch (status) { \ + case SA_STATUS_OK: \ + break; \ + case SA_STATUS_NO_AVAILABLE_RESOURCE_SLOT: \ + return SEC_RESULT_NO_KEYSLOTS_AVAILABLE; \ + case SA_STATUS_BAD_KEY_TYPE: \ + case SA_STATUS_NULL_PARAMETER: \ + case SA_STATUS_BAD_PARAMETER: \ + return SEC_RESULT_INVALID_PARAMETERS; \ + case SA_STATUS_BAD_SVP_BUFFER: \ + return SEC_RESULT_INVALID_SVP_DATA; \ + case SA_STATUS_OPERATION_NOT_SUPPORTED: \ + return SEC_RESULT_UNIMPLEMENTED_FEATURE; \ + case SA_STATUS_VERIFICATION_FAILED: \ + return SEC_RESULT_VERIFICATION_FAILED; \ + case SA_STATUS_BAD_KEY_FORMAT: \ + case SA_STATUS_OPERATION_NOT_ALLOWED: \ + case SA_STATUS_SELF_TEST: \ + case SA_STATUS_INTERNAL_ERROR: \ + default: \ + return SEC_RESULT_FAILURE; \ + } + +#endif // SEC_ADAPTER_PROCESSOR_H diff --git a/src/sec_adapter_pubops.c b/src/sec_adapter_pubops.c new file mode 100644 index 0000000..150fd61 --- /dev/null +++ b/src/sec_adapter_pubops.c @@ -0,0 +1,629 @@ +/** + * Copyright 2020-2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_adapter_pubops.h" + +static Sec_Result SecUtils_BigNumToBuffer(const BIGNUM* bignum, SEC_BYTE* buffer, SEC_SIZE buffer_len) { + SEC_SIZE num_bytes; + + memset(buffer, 0, buffer_len); + num_bytes = BN_num_bytes(bignum); + + if (num_bytes > buffer_len) { + SEC_LOG_ERROR("Buffer not large enough. needed: %d, actual: %d", num_bytes, buffer_len); + return SEC_RESULT_FAILURE; + } + + BN_bn2bin(bignum, buffer + buffer_len - num_bytes); + + return SEC_RESULT_SUCCESS; +} + +static RSA* SecUtils_RSAFromPubBinary(Sec_RSARawPublicKey* binary) { + RSA* rsa = NULL; + + rsa = RSA_new(); + if (rsa == NULL) { + SEC_LOG_ERROR("RSA_new failed"); + return NULL; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + rsa->n = BN_bin2bn(binary->n, (int) Sec_BEBytesToUint32(binary->modulus_len_be), NULL); + rsa->e = BN_bin2bn(binary->e, 4, NULL); +#else + RSA_set0_key(rsa, BN_bin2bn(binary->n, (int) Sec_BEBytesToUint32(binary->modulus_len_be), NULL), + BN_bin2bn(binary->e, 4, NULL), NULL); +#endif + + return rsa; +} + +static EC_KEY* SecUtils_ECCFromPubBinary(Sec_ECCRawPublicKey* binary) { + BN_CTX* ctx = BN_CTX_new(); + + if (binary->type != SEC_KEYTYPE_ECC_NISTP256_PUBLIC && binary->type != SEC_KEYTYPE_ECC_NISTP256) + return NULL; + + //create ec_key structure with NIST p256 curve; + EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + const EC_GROUP* group = EC_KEY_get0_group(ec_key); + EC_POINT* ec_point = EC_POINT_new(group); + BN_CTX_start(ctx); + BIGNUM* xp; + BIGNUM* yp; + + if (((xp = BN_CTX_get(ctx)) == NULL) || ((yp = BN_CTX_get(ctx)) == NULL)) { + EC_POINT_free(ec_point); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return NULL; + } + + EC_POINT_set_affine_coordinates_GFp(group, ec_point, + BN_bin2bn(binary->x, (int) Sec_BEBytesToUint32(binary->key_len), xp), + BN_bin2bn(binary->y, (int) Sec_BEBytesToUint32(binary->key_len), yp), ctx); + EC_KEY_set_public_key(ec_key, ec_point); + + EC_POINT_free(ec_point); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return ec_key; +} + +static Sec_Result SecUtils_VerifyX509WithRawECCPublicKey(X509* x509, Sec_ECCRawPublicKey* public_key) { + EC_KEY* ec_key = NULL; + EVP_PKEY* evp_key = NULL; + int verify_res; + + ec_key = SecUtils_ECCFromPubBinary(public_key); + if (ec_key == NULL) { + SEC_LOG_ERROR("_SecUtils_ECCFromPubBinary failed"); + SEC_ECC_FREE(ec_key); + SEC_EVPPKEY_FREE(evp_key); + return SEC_RESULT_FAILURE; + } + + evp_key = EVP_PKEY_new(); + if (EVP_PKEY_set1_EC_KEY(evp_key, ec_key) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_PKEY_set1_EC_KEY failed"); + SEC_ECC_FREE(ec_key); + SEC_EVPPKEY_FREE(evp_key); + return SEC_RESULT_FAILURE; + } + + verify_res = X509_verify(x509, evp_key); + + SEC_ECC_FREE(ec_key); + SEC_EVPPKEY_FREE(evp_key); + + if (verify_res != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("X509_verify failed, %s", + ERR_error_string(ERR_get_error(), NULL)); + return SEC_RESULT_VERIFICATION_FAILED; + } + + return SEC_RESULT_SUCCESS; +} + +static Sec_Result SecUtils_VerifyX509WithRawRSAPublicKey(X509* x509, Sec_RSARawPublicKey* public_key) { + RSA* rsa = NULL; + EVP_PKEY* evp_key = NULL; + int verify_res; + + rsa = SecUtils_RSAFromPubBinary(public_key); + if (rsa == NULL) { + SEC_LOG_ERROR("_Sec_ReadRSAPublic failed"); + SEC_RSA_FREE(rsa); + SEC_EVPPKEY_FREE(evp_key); + return SEC_RESULT_FAILURE; + } + + evp_key = EVP_PKEY_new(); + if (EVP_PKEY_set1_RSA(evp_key, rsa) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_PKEY_set1_RSA failed"); + SEC_RSA_FREE(rsa); + SEC_EVPPKEY_FREE(evp_key); + return SEC_RESULT_FAILURE; + } + + verify_res = X509_verify(x509, evp_key); + + SEC_RSA_FREE(rsa); + SEC_EVPPKEY_FREE(evp_key); + + if (verify_res != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("X509_verify failed, %s", + ERR_error_string(ERR_get_error(), NULL)); + return SEC_RESULT_VERIFICATION_FAILED; + } + + return SEC_RESULT_SUCCESS; +} + +static Sec_Result SecUtils_Extract_EC_KEY_X_Y(const EC_KEY* ec_key, BIGNUM** xp, BIGNUM** yp, Sec_KeyType* keyTypep) { + const EC_GROUP* group = NULL; + const EC_POINT* ec_point = NULL; + BN_CTX* ctx = NULL; + Sec_Result result = SEC_RESULT_FAILURE; + + if (xp == NULL) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: X cannot be NULL"); + if (ctx != NULL) + BN_CTX_free(ctx); + + return result; + } + + group = EC_KEY_get0_group(ec_key); + if (group == NULL) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: EC_KEY_get0_group: %s", ERR_error_string(ERR_get_error(), NULL)); + if (ctx != NULL) + BN_CTX_free(ctx); + + return result; + } + + ec_point = EC_KEY_get0_public_key(ec_key); + if (ec_point == NULL) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: EC_KEY_get0_public_key: %s", + ERR_error_string(ERR_get_error(), NULL)); + if (ctx != NULL) + BN_CTX_free(ctx); + + return result; + } + + ctx = BN_CTX_new(); + if (ctx == NULL) { + SEC_LOG_ERROR("BN_CTX_new() failed"); + if (ctx != NULL) + BN_CTX_free(ctx); + + return result; + } + + *xp = BN_new(); + if (*xp == NULL) { + SEC_LOG_ERROR("BN_new() failed"); + if (ctx != NULL) + BN_CTX_free(ctx); + + return result; + } + + if (yp != NULL) { // if caller wants y coordinate returned + *yp = BN_new(); + if (*yp == NULL) { + SEC_LOG_ERROR("BN_new() failed"); + if (ctx != NULL) + BN_CTX_free(ctx); + + return result; + } + } + + if (keyTypep != NULL) // if caller wants key type returned + { + *keyTypep = SEC_KEYTYPE_ECC_NISTP256_PUBLIC; + } + + // Get the X coordinate and optionally the Y coordinate + if (EC_POINT_get_affine_coordinates_GFp(group, ec_point, *xp, yp != NULL ? *yp : NULL, ctx) != 1) { + BN_clear_free(*xp); + if (yp != NULL) + BN_clear_free(*yp); + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: EC_POINT_get_affine_coordinates_GFp: %s", + ERR_error_string(ERR_get_error(), NULL)); + if (ctx != NULL) + BN_CTX_free(ctx); + + return result; + } + + if (ctx != NULL) + BN_CTX_free(ctx); + + return SEC_RESULT_SUCCESS; +} + +static EC_KEY* SecUtils_ECCFromDERPub(const SEC_BYTE* der, SEC_SIZE der_len) { + const unsigned char* p = (const unsigned char*) der; + EC_KEY* ec_key = NULL; + + ec_key = d2i_EC_PUBKEY(&ec_key, &p, der_len); + + if (ec_key == NULL) { + SEC_LOG_ERROR("Invalid ECC key container"); + return NULL; + } + + return ec_key; +} + +Sec_Result Pubops_VerifyX509WithPubEcc(SEC_BYTE* cert, SEC_SIZE cert_len, Sec_ECCRawPublicKey* pub) { + X509* x509 = SecCertificate_DerToX509(cert, cert_len); + Sec_Result result = SEC_RESULT_FAILURE; + + if (x509 == NULL) { + SEC_LOG_ERROR("SecCertificate_DerToX509 failed"); + SEC_X509_FREE(x509); + return result; + } + + if (SecUtils_VerifyX509WithRawECCPublicKey(x509, pub) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("_SecUtils_VerifyX509WithRawECCPublicKey failed"); + SEC_X509_FREE(x509); + return result; + } + + SEC_X509_FREE(x509); + return SEC_RESULT_SUCCESS; +} + +Sec_Result Pubops_ExtractRSAPubFromX509Der(SEC_BYTE* cert, SEC_SIZE cert_len, Sec_RSARawPublicKey* pub) { + X509* x509 = SecCertificate_DerToX509(cert, cert_len); + EVP_PKEY* evp_key = NULL; + RSA* rsa = NULL; + Sec_Result result = SEC_RESULT_FAILURE; + + if (x509 == NULL) { + SEC_LOG_ERROR("SecCertificate_DerToX509 failed"); + SEC_X509_FREE(x509); + SEC_EVPPKEY_FREE(evp_key); + SEC_RSA_FREE(rsa); + return result; + } + + evp_key = X509_get_pubkey(x509); + if (evp_key == NULL) { + SEC_LOG_ERROR("%s", ERR_error_string(ERR_get_error(), NULL)); + SEC_X509_FREE(x509); + SEC_EVPPKEY_FREE(evp_key); + SEC_RSA_FREE(rsa); + return result; + } + + rsa = EVP_PKEY_get1_RSA(evp_key); + if (rsa == NULL) { + SEC_X509_FREE(x509); + SEC_EVPPKEY_FREE(evp_key); + SEC_RSA_FREE(rsa); + return result; + } + + Sec_Uint32ToBEBytes(RSA_size(rsa), pub->modulus_len_be); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SecUtils_BigNumToBuffer(rsa->n, pub->n, Sec_BEBytesToUint32(pub->modulus_len_be)); + SecUtils_BigNumToBuffer(rsa->e, pub->e, 4); +#else + SecUtils_BigNumToBuffer(RSA_get0_n(rsa), pub->n, Sec_BEBytesToUint32(pub->modulus_len_be)); + SecUtils_BigNumToBuffer(RSA_get0_e(rsa), pub->e, 4); +#endif + + SEC_X509_FREE(x509); + SEC_EVPPKEY_FREE(evp_key); + SEC_RSA_FREE(rsa); + return SEC_RESULT_SUCCESS; +} + +Sec_Result Pubops_ExtractECCPubFromX509Der(SEC_BYTE* cert, SEC_SIZE cert_len, Sec_ECCRawPublicKey* pub) { + X509* x509 = SecCertificate_DerToX509(cert, cert_len); + Sec_Result result = SEC_RESULT_FAILURE; + + if (x509 == NULL) { + SEC_LOG_ERROR("SecCertificate_DerToX509 failed"); + SEC_X509_FREE(x509); + return result; + } + + EVP_PKEY* evp_key = X509_get_pubkey(x509); + if (evp_key == NULL) { + SEC_LOG_ERROR("%s", ERR_error_string(ERR_get_error(), NULL)); + SEC_X509_FREE(x509); + SEC_EVPPKEY_FREE(evp_key); + return result; + } + + EC_KEY* ec_key = EVP_PKEY_get1_EC_KEY(evp_key); + if (ec_key == NULL) { + SEC_X509_FREE(x509); + SEC_EVPPKEY_FREE(evp_key); + SEC_ECC_FREE(ec_key); + return result; + } + + BIGNUM* x = NULL; + BIGNUM* y = NULL; + Sec_KeyType key_type; + if (SecUtils_Extract_EC_KEY_X_Y(ec_key, &x, &y, &key_type) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y failed"); + if (x != NULL) + BN_clear_free(x); + + if (y != NULL) + BN_clear_free(y); + + SEC_X509_FREE(x509); + SEC_EVPPKEY_FREE(evp_key); + SEC_ECC_FREE(ec_key); + return result; + } + + pub->type = key_type; + + Sec_Uint32ToBEBytes(SecKey_GetKeyLenForKeyType(key_type), pub->key_len); + SecUtils_BigNumToBuffer(x, pub->x, Sec_BEBytesToUint32(pub->key_len)); + SecUtils_BigNumToBuffer(y, pub->y, Sec_BEBytesToUint32(pub->key_len)); + + if (x != NULL) + BN_clear_free(x); + + if (y != NULL) + BN_clear_free(y); + + SEC_X509_FREE(x509); + SEC_EVPPKEY_FREE(evp_key); + SEC_ECC_FREE(ec_key); + return SEC_RESULT_SUCCESS; +} + +static RSA* SecUtils_RSAFromDERPub(const SEC_BYTE* der, SEC_SIZE der_len) { + const unsigned char* p = (const unsigned char*) der; + RSA* rsa = NULL; + + rsa = d2i_RSAPublicKey(&rsa, &p, der_len); + + if (!rsa) { + p = (const unsigned char*) der; + rsa = d2i_RSA_PUBKEY(&rsa, &p, der_len); + } + + if (!rsa) { + SEC_LOG_ERROR("Invalid RSA key container"); + return rsa; + } + + return rsa; +} + +Sec_Result Pubops_VerifyX509WithPubRsa(SEC_BYTE* cert, SEC_SIZE cert_len, Sec_RSARawPublicKey* pub) { + X509* x509 = SecCertificate_DerToX509(cert, cert_len); + Sec_Result result = SEC_RESULT_FAILURE; + + if (x509 == NULL) { + SEC_LOG_ERROR("SecCertificate_DerToX509 failed"); + SEC_X509_FREE(x509); + return result; + } + + if (SecUtils_VerifyX509WithRawRSAPublicKey(x509, pub) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("_SecUtils_VerifyX509WithRawRSAPublicKey failed"); + SEC_X509_FREE(x509); + return result; + } + + SEC_X509_FREE(x509); + return SEC_RESULT_SUCCESS; +} + +Sec_Result Pubops_ExtractRSAPubFromPUBKEYDer(SEC_BYTE* cert, SEC_SIZE cert_len, Sec_RSARawPublicKey* pub) { + RSA* rsa = SecUtils_RSAFromDERPub(cert, cert_len); + Sec_Result result = SEC_RESULT_FAILURE; + if (rsa == NULL) { + SEC_LOG_ERROR("_SecUtils_RSAFromDERPub failed"); + SEC_RSA_FREE(rsa); + return result; + } + + Sec_Uint32ToBEBytes(RSA_size(rsa), pub->modulus_len_be); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SecUtils_BigNumToBuffer(rsa->n, pub->n, Sec_BEBytesToUint32(pub->modulus_len_be)); + SecUtils_BigNumToBuffer(rsa->e, pub->e, 4); +#else + SecUtils_BigNumToBuffer(RSA_get0_n(rsa), pub->n, Sec_BEBytesToUint32(pub->modulus_len_be)); + SecUtils_BigNumToBuffer(RSA_get0_e(rsa), pub->e, 4); +#endif + + SEC_RSA_FREE(rsa); + return SEC_RESULT_SUCCESS; +} + +Sec_Result Pubops_ExtractECCPubFromPUBKEYDer(SEC_BYTE* cert, SEC_SIZE cert_len, Sec_ECCRawPublicKey* pub) { + EC_KEY* ec_key = SecUtils_ECCFromDERPub(cert, cert_len); + Sec_Result result = SEC_RESULT_FAILURE; + if (ec_key == NULL) { + SEC_LOG_ERROR("_SecUtils_ECCFromDERPub failed"); + SEC_ECC_FREE(ec_key); + return result; + } + + BIGNUM* x = NULL; + BIGNUM* y = NULL; + Sec_KeyType key_type; + if (SecUtils_Extract_EC_KEY_X_Y(ec_key, &x, &y, &key_type) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y failed"); + if (x != NULL) + BN_clear_free(x); + + if (y != NULL) + BN_clear_free(y); + + SEC_ECC_FREE(ec_key); + return result; + } + + pub->type = key_type; + + Sec_Uint32ToBEBytes(SecKey_GetKeyLenForKeyType(key_type), pub->key_len); + SecUtils_BigNumToBuffer(x, pub->x, Sec_BEBytesToUint32(pub->key_len)); + SecUtils_BigNumToBuffer(y, pub->y, Sec_BEBytesToUint32(pub->key_len)); + + if (x != NULL) + BN_clear_free(x); + + if (y != NULL) + BN_clear_free(y); + + SEC_ECC_FREE(ec_key); + return SEC_RESULT_SUCCESS; +} + +Sec_Result Pubops_VerifyWithPubRsa(RSA* rsa, Sec_SignatureAlgorithm alg, SEC_BYTE* digest, SEC_SIZE digest_len, + SEC_BYTE* sig, SEC_SIZE sig_len, int salt_len) { + if (rsa == NULL) { + SEC_LOG_ERROR("_SecUtils_RSAFromPubBinary failed"); + return SEC_RESULT_FAILURE; + } + + int expected_sign_len = RSA_size(rsa); + if (sig_len != expected_sign_len) { + SEC_LOG_ERROR("Invalid signature size %d, expected %d", sig_len, expected_sign_len); + return SEC_RESULT_FAILURE; + } + + Sec_DigestAlgorithm digest_alg = SecSignature_GetDigestAlgorithm(alg); + + if (alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST) { + //pss padding + SEC_BYTE decrypted[SEC_RSA_KEY_MAX_LEN]; + if (RSA_public_decrypt(RSA_size(rsa), sig, decrypted, rsa, RSA_NO_PADDING) == -1) { + SEC_LOG_ERROR("RSA_public_decrypt failed with error %s\n", ERR_error_string(ERR_get_error(), NULL)); + return SEC_RESULT_FAILURE; + } + + if (salt_len < 0) { + salt_len = (digest_alg == SEC_DIGESTALGORITHM_SHA1) ? 20 : 32; + } + + /* verify the data */ + int openssl_res = RSA_verify_PKCS1_PSS(rsa, digest, + (digest_alg == SEC_DIGESTALGORITHM_SHA1) ? EVP_sha1() : EVP_sha256(), + decrypted, salt_len); + if (openssl_res != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("RSA_verify_PKCS1_PSS failed"); + SEC_LOG_ERROR("%s", ERR_error_string(ERR_get_error(), NULL)); + return SEC_RESULT_FAILURE; + } + } else { + int openssl_res = RSA_verify((digest_alg == SEC_DIGESTALGORITHM_SHA1) ? NID_sha1 : NID_sha256, digest, + digest_len, sig, sig_len, rsa); + if (openssl_res != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("RSA_verify failed"); + SEC_LOG_ERROR("%s", ERR_error_string(ERR_get_error(), NULL)); + return SEC_RESULT_FAILURE; + } + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result Pubops_VerifyWithPubEcc(EC_KEY* ec_key, Sec_SignatureAlgorithm alg, SEC_BYTE* digest, SEC_SIZE digest_len, + SEC_BYTE* sig, SEC_SIZE sig_len) { + if (ec_key == NULL) { + SEC_LOG_ERROR("_SecUtils_ECCFromPubBinary failed"); + return SEC_RESULT_FAILURE; + } + + int expected_sign_len = 2 * EC_GROUP_get_degree(EC_KEY_get0_group(ec_key)) / 8; + if (sig_len != expected_sign_len) { + SEC_LOG_ERROR("Invalid signature size %d, expected %d", sig_len, expected_sign_len); + return SEC_RESULT_FAILURE; + } + + ECDSA_SIG* esig = ECDSA_SIG_new(); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + BN_bin2bn(&sig[0], SEC_ECC_NISTP256_KEY_LEN, esig->r); + BN_bin2bn(&sig[SEC_ECC_NISTP256_KEY_LEN], SEC_ECC_NISTP256_KEY_LEN, esig->s); +#else + BIGNUM* r = BN_new(); + BIGNUM* s = BN_new(); + BN_bin2bn(&sig[0], SEC_ECC_NISTP256_KEY_LEN, r); + BN_bin2bn(&sig[SEC_ECC_NISTP256_KEY_LEN], SEC_ECC_NISTP256_KEY_LEN, s); + ECDSA_SIG_set0(esig, r, s); +#endif + int openssl_res = ECDSA_do_verify(digest, (int) digest_len, esig, ec_key); + + // Automatically frees r & s. + ECDSA_SIG_free(esig); + if (openssl_res != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("ECDSA_do_verify failed"); + + if (-1 == openssl_res) { // -1 is not an "error", just a verification failure, so don't log as much + SEC_LOG_ERROR("%s", ERR_error_string(ERR_get_error(), NULL)); + } + + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result Pubops_HMAC(Sec_MacAlgorithm alg, SEC_BYTE* key, SEC_SIZE key_len, SEC_BYTE* input, SEC_SIZE input_len, + SEC_BYTE* mac, SEC_SIZE mac_len) { + switch (alg) { + case SEC_MACALGORITHM_HMAC_SHA1: + case SEC_MACALGORITHM_HMAC_SHA256: { + unsigned int osl_mac_len = mac_len; + if (HMAC(alg == SEC_MACALGORITHM_HMAC_SHA1 ? EVP_sha1() : EVP_sha256(), key, (int) key_len, input, + input_len, mac, &osl_mac_len) == NULL) { + SEC_LOG_ERROR("HMAC failed"); + SEC_LOG_ERROR("%s", ERR_error_string(ERR_get_error(), NULL)); + return SEC_RESULT_FAILURE; + } + + break; + } + + case SEC_MACALGORITHM_CMAC_AES_128: { + CMAC_CTX* cmac_ctx = CMAC_CTX_new(); + + if (CMAC_Init(cmac_ctx, &key[0], key_len, + key_len == SEC_AES_BLOCK_SIZE ? EVP_aes_128_cbc() : EVP_aes_256_cbc(), + NULL) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("Comcast_CMAC_Init failed"); + return SEC_RESULT_FAILURE; + } + + if (CMAC_Update(cmac_ctx, &input[0], input_len) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("CMAC_Update failed"); + CMAC_CTX_free(cmac_ctx); + return SEC_RESULT_FAILURE; + } + + size_t outl = mac_len; + if (CMAC_Final(cmac_ctx, &mac[0], &outl) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("CMAC_Final failed"); + CMAC_CTX_free(cmac_ctx); + return SEC_RESULT_FAILURE; + } + + CMAC_CTX_free(cmac_ctx); + break; + } + + default: + SEC_LOG_ERROR("Unknown algorithm encountered: %d", alg); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} diff --git a/src/sec_adapter_pubops.h b/src/sec_adapter_pubops.h new file mode 100644 index 0000000..038fade --- /dev/null +++ b/src/sec_adapter_pubops.h @@ -0,0 +1,51 @@ +/** + * Copyright 2020-2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_ADAPTER_PUBOPS_H +#define SEC_ADAPTER_PUBOPS_H + +#include "sec_security.h" +#include +#include +#include +#include +#include +#include + +Sec_Result Pubops_VerifyX509WithPubEcc(SEC_BYTE* cert, SEC_SIZE cert_len, Sec_ECCRawPublicKey* pub); + +Sec_Result Pubops_ExtractRSAPubFromX509Der(SEC_BYTE* cert, SEC_SIZE cert_len, Sec_RSARawPublicKey* pub); + +Sec_Result Pubops_ExtractECCPubFromX509Der(SEC_BYTE* cert, SEC_SIZE cert_len, Sec_ECCRawPublicKey* pub); + +Sec_Result Pubops_VerifyX509WithPubRsa(SEC_BYTE* cert, SEC_SIZE cert_len, Sec_RSARawPublicKey* pub); + +Sec_Result Pubops_ExtractRSAPubFromPUBKEYDer(SEC_BYTE* cert, SEC_SIZE cert_len, Sec_RSARawPublicKey* pub); + +Sec_Result Pubops_ExtractECCPubFromPUBKEYDer(SEC_BYTE* cert, SEC_SIZE cert_len, Sec_ECCRawPublicKey* pub); + +Sec_Result Pubops_VerifyWithPubRsa(RSA* rsa, Sec_SignatureAlgorithm alg, SEC_BYTE* digest, SEC_SIZE digest_len, + SEC_BYTE* sig, SEC_SIZE sig_len, int salt_len); + +Sec_Result Pubops_VerifyWithPubEcc(EC_KEY* ec_key, Sec_SignatureAlgorithm alg, SEC_BYTE* digest, SEC_SIZE digest_len, + SEC_BYTE* sig, SEC_SIZE sig_len); + +Sec_Result Pubops_HMAC(Sec_MacAlgorithm alg, SEC_BYTE* key, SEC_SIZE key_len, SEC_BYTE* input, SEC_SIZE input_len, + SEC_BYTE* mac, SEC_SIZE mac_len); + +#endif // SEC_ADAPTER_PUBOPS_H diff --git a/src/sec_adapter_random.c b/src/sec_adapter_random.c new file mode 100644 index 0000000..8ebdc3b --- /dev/null +++ b/src/sec_adapter_random.c @@ -0,0 +1,104 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sa.h" +#include "sec_adapter_processor.h" +#include "sec_security.h" + +struct Sec_RandomHandle_struct { + Sec_ProcessorHandle* processorHandle; +}; + +/** + * @brief Obtain a handle to the random number generator. + * + * @param processorHandle secure processor handle. + * @param algorithm random number algorithm to use. + * @param randomHandle output handle for the random number generator. + * + * @return The status of the operation. + */ +Sec_Result SecRandom_GetInstance(Sec_ProcessorHandle* processorHandle, Sec_RandomAlgorithm algorithm, + Sec_RandomHandle** randomHandle) { + CHECK_PROCHANDLE(processorHandle) + + *randomHandle = malloc(sizeof(Sec_RandomHandle)); + if (*randomHandle == NULL) + return SEC_RESULT_FAILURE; + + (*randomHandle)->processorHandle = processorHandle; + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Generate random data. + * + * @param randomHandle random number generator handle. + * @param output pointer to the output buffer where the random data will be stored. + * @param outputSize the size of the output buffer. + * + * @return The status of the operation. + */ +Sec_Result SecRandom_Process(Sec_RandomHandle* randomHandle, SEC_BYTE* output, SEC_SIZE outputSize) { + CHECK_HANDLE(randomHandle) + int status = sa_crypto_random(output, outputSize); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Release the random object. + * + * @param randomHandle random handle. + * + * @return The status of the operation. + */ +Sec_Result SecRandom_Release(Sec_RandomHandle* randomHandle) { + SEC_FREE(randomHandle); + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Utility function for filling out a random value. + * + * @param proc secure processor handle. + * @param alg random algorithm to use. + * @param output output buffer where the random value will be written. + * @param output_len number of bytes written to the output buffer. + * + * @return status of the operation. + */ +Sec_Result SecRandom_SingleInput(Sec_ProcessorHandle* processorHandle, Sec_RandomAlgorithm alg, SEC_BYTE* output, + SEC_SIZE output_len) { + Sec_Result result; + Sec_RandomHandle* randomHandle = NULL; + + result = SecRandom_GetInstance(processorHandle, alg, &randomHandle); + if (result != SEC_RESULT_SUCCESS) + return result; + + result = SecRandom_Process(randomHandle, output, output_len); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecRandom_Process failed"); + SecRandom_Release(randomHandle); + return result; + } + + SecRandom_Release(randomHandle); + return result; +} diff --git a/src/sec_adapter_signature.c b/src/sec_adapter_signature.c new file mode 100644 index 0000000..f6372ef --- /dev/null +++ b/src/sec_adapter_signature.c @@ -0,0 +1,545 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_adapter_processor.h" +#include "sec_adapter_pubops.h" +#include "sec_adapter_utils.h" +#include "sec_security.h" +#include +#include + +struct Sec_SignatureHandle_struct { + Sec_ProcessorHandle* processorHandle; + Sec_SignatureAlgorithm algorithm; + Sec_SignatureMode mode; + Sec_KeyHandle* keyHandle; +}; + +static Sec_KeyContainer get_rsa_public_key_container_for_byte_length(SEC_BYTE* numBytes); + +/** + * @brief Obtain a handle to the signature calculator. + * + * @param processorHandle secure processor handle. + * @param algorithm signing algorithm. + * @param mode signing mode. + * @param keyHandle key used for signing operations. + * @param signatureHandle output signature handle. + * + * @return The status of the operation. + */ +Sec_Result SecSignature_GetInstance(Sec_ProcessorHandle* processorHandle, Sec_SignatureAlgorithm algorithm, + Sec_SignatureMode mode, Sec_KeyHandle* keyHandle, Sec_SignatureHandle** signatureHandle) { + CHECK_PROCHANDLE(processorHandle) + + Sec_KeyType key_type = SecKey_GetKeyType(keyHandle); + if (SecSignature_IsValidKey(key_type, algorithm, mode) != SEC_RESULT_SUCCESS) + return SEC_RESULT_INVALID_PARAMETERS; + + *signatureHandle = calloc(1, sizeof(Sec_SignatureHandle)); + if (*signatureHandle == NULL) { + SEC_LOG_ERROR("Calloc failed"); + return SEC_RESULT_FAILURE; + } + + (*signatureHandle)->processorHandle = processorHandle; + (*signatureHandle)->algorithm = algorithm; + (*signatureHandle)->mode = mode; + (*signatureHandle)->keyHandle = keyHandle; + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Sign/Verify Signature of the input data. + * + * @param signatureHandle signature handle. + * @param input pointer to the input buffer whose signature we are generating/verifying. + * @param inputSize the length of the input. + * @param signature buffer where signature is/will be stored. + * @param signatureSize output variable that will be set to the signature size. + * + * @return The status of the operation. + */ +Sec_Result SecSignature_Process(Sec_SignatureHandle* signatureHandle, SEC_BYTE* input, SEC_SIZE inputSize, + SEC_BYTE* signature, SEC_SIZE* signatureSize) { + CHECK_HANDLE(signatureHandle) + + if (signatureHandle->mode == SEC_SIGNATUREMODE_SIGN) { + sa_signature_algorithm signature_algorithm; + sa_sign_parameters_rsa_pss rsa_pss_parameters; + sa_sign_parameters_rsa_pkcs1v15 rsa_pkcs1v15_parameters; + sa_sign_parameters_ecdsa ecdsa_parameters; + void* parameters = NULL; + switch (signatureHandle->algorithm) { + case SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS: + case SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS_DIGEST: + signature_algorithm = SA_SIGNATURE_ALGORITHM_RSA_PKCS1V15; + rsa_pkcs1v15_parameters.digest_algorithm = SA_DIGEST_ALGORITHM_SHA1; + rsa_pkcs1v15_parameters.precomputed_digest = + (signatureHandle->algorithm == SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS_DIGEST); + parameters = &rsa_pkcs1v15_parameters; + break; + + case SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS: + case SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST: + signature_algorithm = SA_SIGNATURE_ALGORITHM_RSA_PSS; + rsa_pss_parameters.digest_algorithm = SA_DIGEST_ALGORITHM_SHA1; + rsa_pss_parameters.precomputed_digest = + (signatureHandle->algorithm == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST); + rsa_pss_parameters.salt_length = SHA_DIGEST_LENGTH; + parameters = &rsa_pss_parameters; + break; + + case SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS: + case SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS_DIGEST: + signature_algorithm = SA_SIGNATURE_ALGORITHM_RSA_PKCS1V15; + rsa_pkcs1v15_parameters.digest_algorithm = SA_DIGEST_ALGORITHM_SHA256; + rsa_pkcs1v15_parameters.precomputed_digest = + (signatureHandle->algorithm == SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS_DIGEST); + parameters = &rsa_pkcs1v15_parameters; + break; + + case SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS: + case SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST: + signature_algorithm = SA_SIGNATURE_ALGORITHM_RSA_PSS; + rsa_pss_parameters.digest_algorithm = SA_DIGEST_ALGORITHM_SHA256; + rsa_pss_parameters.precomputed_digest = + (signatureHandle->algorithm == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST); + rsa_pss_parameters.salt_length = SHA256_DIGEST_LENGTH; + parameters = &rsa_pss_parameters; + break; + + case SEC_SIGNATUREALGORITHM_ECDSA_NISTP256: + case SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST: + signature_algorithm = SA_SIGNATURE_ALGORITHM_ECDSA; + ecdsa_parameters.digest_algorithm = SA_DIGEST_ALGORITHM_SHA256; + ecdsa_parameters.precomputed_digest = + (signatureHandle->algorithm == SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST); + parameters = &ecdsa_parameters; + break; + + default: + return SEC_RESULT_INVALID_PARAMETERS; + } + + size_t out_length = 0; + const Sec_Key* key = get_key(signatureHandle->keyHandle); + // Get the out_length since it is not given to us. + sa_status status = sa_crypto_sign(NULL, &out_length, signature_algorithm, key->handle, input, inputSize, + parameters); + CHECK_STATUS(status) + status = sa_crypto_sign(signature, &out_length, signature_algorithm, key->handle, input, inputSize, parameters); + CHECK_STATUS(status) + *signatureSize = out_length; + } else { + Sec_Result result; + SEC_BYTE digest[SEC_DIGEST_MAX_LEN]; + SEC_SIZE digest_len; + if (SecSignature_IsDigest(signatureHandle->algorithm)) { + if (SecDigest_GetDigestLenForAlgorithm(SecSignature_GetDigestAlgorithm(signatureHandle->algorithm)) != + inputSize) { + SEC_LOG_ERROR("Invalid input length"); + return SEC_RESULT_FAILURE; + } + + memcpy(digest, input, inputSize); + digest_len = inputSize; + } else { + /* calculate digest */ + result = SecDigest_SingleInput(signatureHandle->processorHandle, + SecSignature_GetDigestAlgorithm(signatureHandle->algorithm), input, + inputSize, digest, &digest_len); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_SingleInput failed"); + return result; + } + } + + const Sec_Key* key = get_key(signatureHandle->keyHandle); + if (SecSignature_IsRsa(signatureHandle->algorithm)) { + result = Pubops_VerifyWithPubRsa(key->rsa, signatureHandle->algorithm, + digest, digest_len, signature, *signatureSize, -1); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Pubops_VerifyWithPubRsa failed"); + return SEC_RESULT_VERIFICATION_FAILED; + } + } else if (SecSignature_IsEcc(signatureHandle->algorithm)) { + if (*signatureSize != SecSignature_GetEccSignatureSize(signatureHandle->algorithm)) { + SEC_LOG_ERROR("Incorrect ECC signature size"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType key_type = SecKey_GetKeyType(signatureHandle->keyHandle); + EC_KEY* ec_key = NULL; + if (SecKey_IsPubEcc(key_type)) { + ec_key = EC_KEY_new(); + EC_KEY_copy(ec_key, key->ec_key); + } else { + Sec_ECCRawPublicKey public_key; + result = SecKey_ExtractECCPublicKey(signatureHandle->keyHandle, &public_key); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_SingleInput failed"); + SEC_ECC_FREE(ec_key); + return result; + } + + ec_key = SecUtils_ECCFromPubBinary(&public_key); + } + + result = Pubops_VerifyWithPubEcc(ec_key, signatureHandle->algorithm, + digest, digest_len, signature, *signatureSize); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Pubops_VerifyWithPubEcc failed"); + SEC_ECC_FREE(ec_key); + return SEC_RESULT_VERIFICATION_FAILED; + } + + SEC_ECC_FREE(ec_key); + } else { + SEC_LOG_ERROR("Unimplemented signature algorithm for verify"); + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + } + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Release the signature object. + * + * @param signatureHandle cipher handle. + * + * @return The status of the operation. + */ +Sec_Result SecSignature_Release(Sec_SignatureHandle* signatureHandle) { + CHECK_HANDLE(signatureHandle) + SEC_FREE(signatureHandle); + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Checks whether the passed in key is valid for a chosen signing algorithm and mode. + * + * @param key_type key type. + * @param algorithm signing algorithm. + * @param mode signing mode. + * + * @return status of the operation. + */ +Sec_Result SecSignature_IsValidKey(Sec_KeyType key_type, + Sec_SignatureAlgorithm algorithm, Sec_SignatureMode mode) { + switch (algorithm) { + case SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS: + case SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS_DIGEST: + case SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS: + case SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS_DIGEST: + case SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS: + case SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS: + case SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST: + case SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST: + if (mode == SEC_SIGNATUREMODE_SIGN) { + if (key_type == SEC_KEYTYPE_RSA_1024 || key_type == SEC_KEYTYPE_RSA_2048 || + key_type == SEC_KEYTYPE_RSA_3072) + return SEC_RESULT_SUCCESS; + + return SEC_RESULT_FAILURE; + } else { + if (key_type == SEC_KEYTYPE_RSA_1024 || key_type == SEC_KEYTYPE_RSA_2048 || + key_type == SEC_KEYTYPE_RSA_3072 || key_type == SEC_KEYTYPE_RSA_1024_PUBLIC || + key_type == SEC_KEYTYPE_RSA_2048_PUBLIC || key_type == SEC_KEYTYPE_RSA_3072_PUBLIC) + return SEC_RESULT_SUCCESS; + + return SEC_RESULT_FAILURE; + } + + case SEC_SIGNATUREALGORITHM_ECDSA_NISTP256: + case SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST: + if (mode == SEC_SIGNATUREMODE_SIGN) { + if (key_type == SEC_KEYTYPE_ECC_NISTP256) + return SEC_RESULT_SUCCESS; + + return SEC_RESULT_FAILURE; + } else { + if (key_type == SEC_KEYTYPE_ECC_NISTP256 || key_type == SEC_KEYTYPE_ECC_NISTP256_PUBLIC) + return SEC_RESULT_SUCCESS; + + return SEC_RESULT_FAILURE; + } + + default: + return SEC_RESULT_UNIMPLEMENTED_FEATURE; + } +} + +/** + * @brief Returns TRUE if the signature algorithm is an RSA variant. + * + * @param alg signing algorithm. + * + * @return true if RSA. + */ +SEC_BOOL SecSignature_IsRsa(Sec_SignatureAlgorithm alg) { + return alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS || alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS || alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS || alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS || alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST; +} + +/** + * @brief Returns TRUE if the signature algorithm is an ECC variant. + * + * @param alg signing algorithm. + * + * @return true if ECC. + */ +SEC_BOOL SecSignature_IsEcc(Sec_SignatureAlgorithm alg) { + return alg == SEC_SIGNATUREALGORITHM_ECDSA_NISTP256 || alg == SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST; +} + +/** + * @brief Returns the size of the algorithm's ECC signature. + * + * @param alg signing algorithm. + * + * @return size in bytes or 0 if unsupported algorithm. + */ +SEC_SIZE SecSignature_GetEccSignatureSize(Sec_SignatureAlgorithm alg) { + switch (alg) { + case SEC_SIGNATUREALGORITHM_ECDSA_NISTP256: + case SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST: + return SEC_ECC_NISTP256_KEY_LEN + SEC_ECC_NISTP256_KEY_LEN; + + default: + return 0; + } +} + +/** + * @brief Obtain a digest algorithm used by a specific signing algorithm. + * + * @param alg signing algorithm. + * + * @return digest algorithm used. + */ +Sec_DigestAlgorithm SecSignature_GetDigestAlgorithm(Sec_SignatureAlgorithm alg) { + switch (alg) { + case SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS: + case SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS_DIGEST: + case SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS: + case SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST: + return SEC_DIGESTALGORITHM_SHA1; + + case SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS: + case SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS_DIGEST: + case SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS: + case SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST: + case SEC_SIGNATUREALGORITHM_ECDSA_NISTP256: + case SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST: + return SEC_DIGESTALGORITHM_SHA256; + + default: + SEC_LOG_ERROR("Unexpected alg encountered: %d", alg); + return SEC_DIGESTALGORITHM_NUM; + } +} + +/** + * @brief Signature util that handles Sec_SignatureHandle generation and release. + * + * @param processorHandle processor handle. + * @param algorithm signing algorithm. + * @param mode signing mode. + * @param keyHandle key used for signing operations. + * @param input pointer to the input buffer whose signature we are generating/verifying. + * @param inputSize the length of the input. + * @param signature buffer where signature is/will be stored. + * @param signatureSize output variable that will be set to the signature size. + * + * @return The status of the operation. + */ +Sec_Result SecSignature_SingleInput(Sec_ProcessorHandle* processorHandle, Sec_SignatureAlgorithm algorithm, + Sec_SignatureMode mode, Sec_KeyHandle* keyHandle, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BYTE* signature, + SEC_SIZE* signatureSize) { + Sec_Result result = SEC_RESULT_FAILURE; + Sec_SignatureHandle* signatureHandle = NULL; + + if (SecSignature_GetInstance(processorHandle, algorithm, mode, keyHandle, &signatureHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_GetInstance failed"); + if (signatureHandle != NULL) + SecSignature_Release(signatureHandle); + + return result; + } + + if (SecSignature_Process(signatureHandle, input, inputSize, signature, signatureSize) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_Process failed"); + if (signatureHandle != NULL) + SecSignature_Release(signatureHandle); + + return result; + } + + if (signatureHandle != NULL) + SecSignature_Release(signatureHandle); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecSignature_SingleInputCert(Sec_ProcessorHandle* processorHandle, Sec_SignatureAlgorithm algorithm, + Sec_SignatureMode mode, Sec_CertificateHandle* certificateHandle, SEC_BYTE* input, SEC_SIZE inputSize, + SEC_BYTE* signature, SEC_SIZE* signatureSize) { + Sec_KeyHandle* keyHandle = NULL; + Sec_RSARawPublicKey rsa_public_key; + Sec_ECCRawPublicKey ecc_public_key; + CHECK_PROCHANDLE(processorHandle) + + if (mode == SEC_SIGNATUREMODE_SIGN) { // Sanity check: This does not handle SIGN + SEC_LOG_ERROR("SecSignature_SingleInputCert does not support SEC_SIGNATUREMODE_SIGN"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType key_type = SecCertificate_GetKeyType(certificateHandle); + switch (key_type) { + case SEC_KEYTYPE_RSA_1024_PUBLIC: + case SEC_KEYTYPE_RSA_2048_PUBLIC: + case SEC_KEYTYPE_RSA_3072_PUBLIC: + if (SecCertificate_ExtractRSAPublicKey(certificateHandle, &rsa_public_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_ExtractRSAPublicKey failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyContainer key_container = get_rsa_public_key_container_for_byte_length( + rsa_public_key.modulus_len_be); + if (SecKey_Provision(processorHandle, SEC_OBJECTID_SIG_FROM_CERT, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + key_container, (SEC_BYTE*) &rsa_public_key, sizeof(rsa_public_key)) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + break; + + case SEC_KEYTYPE_ECC_NISTP256: + if (SecCertificate_ExtractECCPublicKey(certificateHandle, &ecc_public_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_ExtractECCPublicKey failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(processorHandle, SEC_OBJECTID_SIG_FROM_CERT, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_KEYCONTAINER_RAW_ECC_NISTP256_PUBLIC, (SEC_BYTE*) &ecc_public_key, + sizeof(ecc_public_key)) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + break; + + default: + SEC_LOG_ERROR("SecSignature_SingleInputCert: Unhandled keyType %d", (int) key_type); + return SEC_RESULT_FAILURE; + } + + if (SecKey_GetInstance(processorHandle, SEC_OBJECTID_SIG_FROM_CERT, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + Sec_Result result = SecSignature_SingleInput(processorHandle, algorithm, mode, keyHandle, input, inputSize, + signature, signatureSize); + SecKey_Release(keyHandle); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_SingleInput failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecSignature_SingleInputId(Sec_ProcessorHandle* processorHandle, Sec_SignatureAlgorithm algorithm, + Sec_SignatureMode mode, SEC_OBJECTID id, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BYTE* signature, + SEC_SIZE* signatureSize) { + Sec_KeyHandle* keyHandle = NULL; + + if (SecKey_GetInstance(processorHandle, id, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + Sec_Result result = SecSignature_SingleInput(processorHandle, algorithm, mode, keyHandle, input, inputSize, + signature, signatureSize); + SecKey_Release(keyHandle); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_SingleInput failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecSignature_SingleInputCertId(Sec_ProcessorHandle* processorHandle, Sec_SignatureAlgorithm algorithm, + Sec_SignatureMode mode, SEC_OBJECTID cert_id, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BYTE* signature, + SEC_SIZE* signatureSize) { + Sec_CertificateHandle* certificateHandle = NULL; + + if (SecCertificate_GetInstance(processorHandle, cert_id, &certificateHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + Sec_Result result = SecSignature_SingleInputCert(processorHandle, algorithm, mode, certificateHandle, input, + inputSize, signature, signatureSize); + SecCertificate_Release(certificateHandle); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_SingleInput failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Check if provided algorithm takes digest as an input. + */ +SEC_BOOL SecSignature_IsDigest(Sec_SignatureAlgorithm alg) { + return alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS_DIGEST || alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST || alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST; +} + +SEC_BOOL SecSignature_IsRsaPss(Sec_SignatureAlgorithm alg) { + return alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS || alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST || alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST; +} + +static Sec_KeyContainer get_rsa_public_key_container_for_byte_length(SEC_BYTE* numBytes) { + switch (Sec_BEBytesToUint32(numBytes)) { + case 128: + return SEC_KEYCONTAINER_RAW_RSA_1024_PUBLIC; + + case 256: + return SEC_KEYCONTAINER_RAW_RSA_2048_PUBLIC; + + case 384: + return SEC_KEYCONTAINER_RAW_RSA_3072_PUBLIC; + + default: + SEC_LOG_ERROR("Invalid numBytes encountered: %d", numBytes); + return SEC_KEYCONTAINER_NUM; + } +} diff --git a/src/sec_adapter_store.c b/src/sec_adapter_store.c new file mode 100644 index 0000000..ddec3a9 --- /dev/null +++ b/src/sec_adapter_store.c @@ -0,0 +1,446 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_adapter_store.h" +#include + +#define SEC_STORE_MAC_KEY_INPUT "securestore" \ + "integrity" \ + "hmacSha256" \ + "aes128ecb" \ + "decrypt" + +Sec_Result SecStore_GenerateLadderInputs(Sec_ProcessorHandle* processorHandle, const char* input, const char* input2, + SEC_BYTE* output, SEC_SIZE len) { + Sec_Buffer sec_buf; + SEC_BYTE sec_buf_mem[256]; + SEC_BYTE digest[SEC_DIGEST_MAX_LEN]; + SEC_SIZE digest_len; + SEC_SIZE to_copy; + SEC_SIZE loop = 1; + SEC_BYTE loop_buf[4]; + CHECK_PROCHANDLE(processorHandle) + + SecBuffer_Init(&sec_buf, sec_buf_mem, sizeof(sec_buf_mem)); + + while (len > 0) { + SecBuffer_Reset(&sec_buf); + + Sec_Uint32ToBEBytes(loop, loop_buf); + + if (input != NULL && SEC_RESULT_SUCCESS != SecBuffer_Write(&sec_buf, (SEC_BYTE*) input, strlen(input))) { + SEC_LOG_ERROR("SecBuffer_Write failed"); + return SEC_RESULT_FAILURE; + } + + if (input2 != NULL && SEC_RESULT_SUCCESS != SecBuffer_Write(&sec_buf, (SEC_BYTE*) input2, strlen(input2))) { + SEC_LOG_ERROR("SecBuffer_Write failed"); + return SEC_RESULT_FAILURE; + } + + if (SecBuffer_Write(&sec_buf, loop_buf, sizeof(loop_buf)) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecBuffer_Write failed"); + return SEC_RESULT_FAILURE; + } + + if (SecDigest_SingleInput(processorHandle, SEC_DIGESTALGORITHM_SHA256, sec_buf.base, sec_buf.written, digest, + &digest_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_SingleInput failed"); + return SEC_RESULT_FAILURE; + } + + to_copy = SEC_MIN(digest_len, len); + memcpy(output, digest, to_copy); + len -= to_copy; + output += to_copy; + + ++loop; + } + + return SEC_RESULT_SUCCESS; +} + +static SEC_SIZE SecStore_CalculatePaddedDataLen(SEC_SIZE dataLen) { + return dataLen + SEC_AES_BLOCK_SIZE - (dataLen % SEC_AES_BLOCK_SIZE); +} + +SecStore_Header* SecStore_GetHeader(void* store) { + return (SecStore_Header*) store; +} + +void* SecStore_GetUserHeader(void* store) { + return (SEC_BYTE*) store + sizeof(SecStore_Header); +} + +static SEC_SIZE SecStore_GetHeaderLen(void* store) { + return Sec_BEBytesToUint32(SecStore_GetHeader(store)->header_len); +} + +SEC_SIZE SecStore_GetDataLen(void* store) { + return Sec_BEBytesToUint32(SecStore_GetHeader(store)->data_len); +} + +static SEC_SIZE SecStore_GetPaddedDataLen(void* store) { + return SecStore_CalculatePaddedDataLen(SecStore_GetDataLen(store)); +} + +static SEC_SIZE SecStore_CalculateStoreLen(SEC_SIZE header_len, SEC_SIZE data_len) { + return header_len + SecStore_CalculatePaddedDataLen(data_len) + SEC_STORE_MAC_LEN + SEC_STORE_IV_LEN; +} + +SEC_SIZE SecStore_CalculateRequiredStoreLen(SEC_SIZE user_header_len, SEC_SIZE data_len) { + return sizeof(SecStore_Header) + user_header_len + SecStore_CalculatePaddedDataLen(data_len) + SEC_STORE_MAC_LEN + + SEC_STORE_IV_LEN; +} + +SEC_SIZE SecStore_GetStoreLen(void* store) { + return SecStore_CalculateStoreLen(SecStore_GetHeaderLen(store), SecStore_GetDataLen(store)); +} + +SEC_SIZE SecStore_GetUserHeaderLen(void* store) { + return SecStore_GetHeaderLen(store) - sizeof(SecStore_Header); +} + +static SEC_BYTE* SecStore_GetMac(void* store) { + return ((SEC_BYTE*) store) + SecStore_GetStoreLen(store) - SEC_STORE_IV_LEN - SEC_STORE_MAC_LEN; +} + +static SEC_BYTE* SecStore_GetIV(void* store) { + return ((SEC_BYTE*) store) + SecStore_GetStoreLen(store) - SEC_STORE_IV_LEN; +} + +static SEC_BYTE* SecStore_GetData(void* store) { + return ((SEC_BYTE*) store) + SecStore_GetHeaderLen(store); +} + +static Sec_Result SecStore_ComputeMacKey(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID keyId, const char* input, + SEC_BYTE* key, SEC_SIZE key_len) { + SEC_SIZE digest_len; + SEC_SIZE written; + + if (SecDigest_GetDigestLenForAlgorithm(SEC_DIGESTALGORITHM_SHA256) != key_len) { + SEC_LOG_ERROR("Unexpected key_len: %d", key_len); + return SEC_RESULT_FAILURE; + } + + if (SecDigest_SingleInput(processorHandle, SEC_DIGESTALGORITHM_SHA256, (SEC_BYTE*) input, strlen(input), key, + &digest_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_SingleInput failed"); + return SEC_RESULT_FAILURE; + } + + if (SecCipher_SingleInputId(processorHandle, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, keyId, + NULL, key, key_len, key, key_len, &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_SingleInputId failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +static Sec_Result SecStore_Encrypt(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID keyId, void* store, + SEC_SIZE storeLen) { SEC_SIZE expected_enc_data_len; + SEC_SIZE written; + + if (store == NULL) { + SEC_LOG_ERROR("Null store"); + return SEC_RESULT_FAILURE; + } + + if (storeLen < sizeof(SecStore_Header) || storeLen < SecStore_GetStoreLen(store)) { + SEC_LOG_ERROR("Invalid store length: %d", storeLen); + return SEC_RESULT_FAILURE; + } + + if (!(SEC_STORE_FLAG_IS_ENCRYPTED & SecStore_GetHeader(store)->flags)) { + SEC_LOG_ERROR("Encryption flag is not set"); + return SEC_RESULT_FAILURE; + } + + if (SecRandom_SingleInput(processorHandle, SEC_RANDOMALGORITHM_TRUE, SecStore_GetIV(store), SEC_STORE_IV_LEN) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecRandom_SingleInput failed"); + return SEC_RESULT_FAILURE; + } + + expected_enc_data_len = SecStore_GetPaddedDataLen(store) + SEC_STORE_MAC_LEN; + + if (SecCipher_SingleInputId(processorHandle, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, SEC_CIPHERMODE_ENCRYPT, keyId, + SecStore_GetIV(store), SecStore_GetData(store), expected_enc_data_len, SecStore_GetData(store), + expected_enc_data_len, &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_SingleInputId failed"); + return SEC_RESULT_FAILURE; + } + + if (written != expected_enc_data_len) { + SEC_LOG_ERROR("Unexpected number of encrypted bytes written"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +static Sec_Result SecStore_Decrypt(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID keyId, void* store, + SEC_SIZE storeLen) { + SEC_SIZE expected_enc_data_len; + SEC_SIZE written; + + if (store == NULL) { + SEC_LOG_ERROR("Null store"); + return SEC_RESULT_FAILURE; + } + + if (storeLen < sizeof(SecStore_Header) || storeLen < SecStore_GetStoreLen(store)) { + SEC_LOG_ERROR("Invalid store length: %d", storeLen); + return SEC_RESULT_FAILURE; + } + + if (!(SEC_STORE_FLAG_IS_ENCRYPTED & SecStore_GetHeader(store)->flags)) { + SEC_LOG_ERROR("This store is not encrypted"); + return SEC_RESULT_FAILURE; + } + + expected_enc_data_len = SecStore_GetPaddedDataLen(store) + SEC_STORE_MAC_LEN; + + if (SecCipher_SingleInputId(processorHandle, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, SEC_CIPHERMODE_DECRYPT, keyId, + SecStore_GetIV(store), SecStore_GetData(store), expected_enc_data_len, SecStore_GetData(store), + expected_enc_data_len, &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_SingleInputId failed"); + return SEC_RESULT_FAILURE; + } + + if (written != expected_enc_data_len) { + SEC_LOG_ERROR("Unexpected number of decrypted bytes written"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecStore_RetrieveData(Sec_ProcessorHandle* processorHandle, SEC_BOOL require_mac, void* user_header, + SEC_SIZE user_header_len, void* data, SEC_SIZE data_len, void* store, SEC_SIZE storeLen) { + Sec_Result result; + + result = SecStore_RetrieveDataWithKey(processorHandle, SEC_OBJECTID_STORE_AES_KEY, SEC_OBJECTID_STORE_MACKEYGEN_KEY, + require_mac, user_header, user_header_len, data, data_len, store, storeLen); + + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecStore_RetrieveDataWithKey failed"); + } + + return result; +} + +Sec_Result SecStore_RetrieveDataWithKey(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID aesKeyId, + SEC_OBJECTID macGenId, SEC_BOOL require_mac, void* user_header, SEC_SIZE user_header_len, void* data, + SEC_SIZE data_len, void* store, SEC_SIZE storeLen) { + Sec_Result result = SEC_RESULT_FAILURE; + SEC_BYTE mac_key[SEC_STORE_MAC_LEN]; + SEC_BYTE mac[SEC_STORE_MAC_LEN]; + void* copy = NULL; + SEC_BYTE pad[SEC_AES_BLOCK_SIZE]; + CHECK_PROCHANDLE(processorHandle) + + do { + if (store == NULL) { + SEC_LOG_ERROR("Null store"); + break; + } + + if (storeLen < sizeof(SecStore_Header)) { + SEC_LOG_ERROR("Invalid store length: %d", storeLen); + break; + } + + if (memcmp(SEC_STORE_MAGIC, SecStore_GetHeader(store)->store_magic, strlen(SEC_STORE_MAGIC)) != 0) { + SEC_LOG_ERROR("Invalid store magic value"); + break; + } + + if (storeLen < SecStore_GetStoreLen(store)) { + SEC_LOG_ERROR("Invalid store length: %d", storeLen); + break; + } + + /* create a copy of the store that will be decrypted */ + copy = malloc(SecStore_GetStoreLen(store)); + if (copy == NULL) { + SEC_LOG_ERROR("Malloc failed"); + break; + } + memcpy(copy, store, SecStore_GetStoreLen(store)); + + /* decrypt container */ + if (SecStore_GetHeader(copy)->flags & SEC_STORE_FLAG_IS_ENCRYPTED) { + if (SecStore_Decrypt(processorHandle, aesKeyId, copy, SecStore_GetStoreLen(copy)) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecStore_Decrypt failed"); + break; + } + } + + /* check padding */ + memset(pad, (int) (SecStore_GetPaddedDataLen(copy) - SecStore_GetDataLen(copy)), sizeof(pad)); + if (Sec_Memcmp(pad, SecStore_GetData(copy) + SecStore_GetDataLen(copy), pad[0]) != 0) { + SEC_LOG_ERROR("Invalid pad value encountered"); + break; + } + + /* mac value */ + if (!(SecStore_GetHeader(copy)->flags & SEC_STORE_FLAG_IS_MACED)) { + if (require_mac) { + SEC_LOG_ERROR("Key container does not have a mac value"); + break; + } + } else { + if (SecStore_ComputeMacKey(processorHandle, macGenId, SEC_STORE_MAC_KEY_INPUT, mac_key, sizeof(mac_key)) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecStore_ComputeMacKey failed"); + break; + } + + result = Pubops_HMAC(SEC_MACALGORITHM_HMAC_SHA256, mac_key, sizeof(mac_key), copy, + SecStore_GetStoreLen(copy) - SEC_STORE_MAC_LEN - SEC_STORE_IV_LEN, mac, SEC_STORE_MAC_LEN); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Pubops_HMAC failed"); + Sec_Memset(mac_key, 0, sizeof(mac_key)); + return SEC_RESULT_FAILURE; + } + + Sec_Memset(mac_key, 0, sizeof(mac_key)); + if (Sec_Memcmp(mac, SecStore_GetMac(copy), SEC_STORE_MAC_LEN) != 0) { + SEC_LOG_ERROR("Mac does not match"); + break; + } + } + + /* get user_header */ + if (user_header != NULL) { + if (user_header_len < SecStore_GetUserHeaderLen(copy)) { + SEC_LOG_ERROR("Output buffer not large enough to hold user_header"); + break; + } + + memcpy(user_header, SecStore_GetUserHeader(copy), SecStore_GetUserHeaderLen(copy)); + } + + /* get data */ + if (data != NULL) { + if (data_len < SecStore_GetDataLen(copy)) { + SEC_LOG_ERROR("Output buffer not large enough to hold data"); + break; + } + + memcpy(data, SecStore_GetData(copy), SecStore_GetDataLen(copy)); + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + Sec_Memset(mac_key, 0, sizeof(mac_key)); + if (copy != NULL) { + Sec_Memset(copy, 0, SecStore_GetStoreLen(store)); + SEC_FREE(copy); + } + + return result; +} + +Sec_Result SecStore_StoreData(Sec_ProcessorHandle* processorHandle, SEC_BOOL encrypt, SEC_BOOL gen_mac, + SEC_BYTE* user_header_magic, void* user_header, SEC_SIZE user_header_len, void* data, SEC_SIZE data_len, + void* store, SEC_SIZE storeLen) { + Sec_Result result; + + result = SecStore_StoreDataWithKey(processorHandle, SEC_OBJECTID_STORE_AES_KEY, SEC_OBJECTID_STORE_MACKEYGEN_KEY, + encrypt, gen_mac, user_header_magic, user_header, user_header_len, data, data_len, store, storeLen); + + if (result != SEC_RESULT_SUCCESS) + SEC_LOG_ERROR("SecStore_StoreDataWithKey failed"); + + return result; +} + +Sec_Result SecStore_StoreDataWithKey(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID aesKeyId, SEC_OBJECTID macGenId, + SEC_BOOL encrypt, SEC_BOOL gen_mac, SEC_BYTE* user_header_magic, void* user_header, SEC_SIZE user_header_len, + void* data, SEC_SIZE data_len, void* store, SEC_SIZE storeLen) { + SecStore_Header* header = NULL; + SEC_BYTE mac_key[SEC_STORE_MAC_LEN]; + SEC_BYTE pad; + CHECK_PROCHANDLE(processorHandle) + + if (store == NULL) { + SEC_LOG_ERROR("Null store"); + return SEC_RESULT_FAILURE; + } + + if (storeLen < SecStore_CalculateStoreLen(sizeof(SecStore_Header) + user_header_len, data_len)) { + SEC_LOG_ERROR("Invalid store length: %d", storeLen); + return SEC_RESULT_FAILURE; + } + + header = (SecStore_Header*) store; + + /* fill header */ + memset(header, 0, sizeof(SecStore_Header)); + memcpy(header->store_magic, SEC_STORE_MAGIC, strlen(SEC_STORE_MAGIC)); + if (gen_mac) + header->flags |= SEC_STORE_FLAG_IS_MACED; + + if (encrypt) + header->flags |= SEC_STORE_FLAG_IS_ENCRYPTED; + + Sec_Uint32ToBEBytes(data_len, header->data_len); + Sec_Uint32ToBEBytes(sizeof(SecStore_Header) + user_header_len, header->header_len); + if (user_header_magic != NULL) + memcpy(header->user_header_magic, user_header_magic, sizeof(header->user_header_magic)); + + /* store user header */ + memcpy(SecStore_GetUserHeader(store), user_header, user_header_len); + + /* store data */ + memcpy(SecStore_GetData(store), data, data_len); + + /* pad data */ + pad = SecStore_GetPaddedDataLen(store) - SecStore_GetDataLen(store); + memset(SecStore_GetData(store) + data_len, pad, pad); + + if (gen_mac) { + /* calc mac */ + if (SecStore_ComputeMacKey(processorHandle, macGenId, SEC_STORE_MAC_KEY_INPUT, mac_key, sizeof(mac_key)) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecStore_ComputeMacKey failed"); + return SEC_RESULT_FAILURE; + } + + if (Pubops_HMAC(SEC_MACALGORITHM_HMAC_SHA256, mac_key, sizeof(mac_key), store, + SecStore_GetStoreLen(store) - SEC_STORE_MAC_LEN - SEC_STORE_IV_LEN, SecStore_GetMac(store), + SEC_STORE_MAC_LEN) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Pubops_HMAC failed"); + Sec_Memset(mac_key, 0, sizeof(mac_key)); + return SEC_RESULT_FAILURE; + } + + Sec_Memset(mac_key, 0, sizeof(mac_key)); + } + + if (encrypt && SEC_RESULT_SUCCESS != SecStore_Encrypt(processorHandle, aesKeyId, store, storeLen)) { + SEC_LOG_ERROR("SecStore_Encrypt failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} diff --git a/src/sec_adapter_store.h b/src/sec_adapter_store.h new file mode 100644 index 0000000..6813731 --- /dev/null +++ b/src/sec_adapter_store.h @@ -0,0 +1,119 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_ADAPTER_STORE_H +#define SEC_ADAPTER_STORE_H + +#include "sec_adapter_processor.h" +#include "sec_adapter_pubops.h" + +#ifdef __cplusplus +#include +#include +extern "C" { +#else +#include +#include +#endif + +/** + * Secure store structure + * + * |----------------------|--------- + * | | Mandatory | Signed + * | Header |-----------| + * | | User def | + * ----------|----------------------| + * Encrypted | Data | + * |----------------------| + * | Padding | + * |----------------------|--------- + * | Mac | + * ----------|----------------------| + * | IV | + * |----------------------| + */ + +#define SEC_STORE_FLAG_IS_ENCRYPTED 0x01 +#define SEC_STORE_FLAG_IS_MACED 0x02 +#define SEC_STORE_MAC_LEN 32 +#define SEC_STORE_IV_LEN 16 +#define SEC_STORE_MAGIC "SECSTOR1" + +#define SEC_STORE_AES_LADDER_INPUT "securestore" \ + "encryption" \ + "aes128" \ + "vendor128" +#define SEC_STORE_MAC_LADDER_INPUT "securestore" \ + "mackeygen" \ + "aes128" \ + "vendor128" + +#define SEC_STORE_USERHEADERMAGIC_LEN 4 + +typedef struct { + /* fixed value 'SECSTOR' + ver */ + uint8_t store_magic[8]; + + /* header length - including user defined */ + uint8_t header_len[4]; + + /* data length (without padding) */ + uint8_t data_len[4]; + + /* user header magic */ + uint8_t user_header_magic[SEC_STORE_USERHEADERMAGIC_LEN]; + + /* reserved */ + uint8_t reserved[3]; + + /* flags */ + uint8_t flags; +} SecStore_Header; + +Sec_Result SecStore_GenerateLadderInputs(Sec_ProcessorHandle* processorHandle, const char* input, const char* input2, + SEC_BYTE* output, SEC_SIZE len); + +Sec_Result SecStore_RetrieveDataWithKey(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID aesKeyId, + SEC_OBJECTID macGenId, SEC_BOOL require_mac, void* user_header, SEC_SIZE user_header_len, void* data, + SEC_SIZE data_len, void* store, SEC_SIZE storeLen); + +Sec_Result SecStore_StoreDataWithKey(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID aesKeyId, SEC_OBJECTID macGenId, + SEC_BOOL encrypt, SEC_BOOL gen_mac, SEC_BYTE* user_header_magic, void* user_header, SEC_SIZE user_header_len, + void* data, SEC_SIZE data_len, void* store, SEC_SIZE storeLen); + +Sec_Result SecStore_StoreData(Sec_ProcessorHandle* processorHandle, SEC_BOOL encrypt, SEC_BOOL gen_mac, + SEC_BYTE* user_header_magic, void* user_header, SEC_SIZE user_header_len, void* data, SEC_SIZE data_len, + void* store, SEC_SIZE storeLen); + +SEC_SIZE SecStore_GetStoreLen(void* store); + +Sec_Result SecStore_RetrieveData(Sec_ProcessorHandle* processorHandle, SEC_BOOL require_mac, void* user_header, + SEC_SIZE user_header_len, void* data, SEC_SIZE data_len, void* store, SEC_SIZE storeLen); + +SEC_SIZE SecStore_GetDataLen(void* store); + +void* SecStore_GetUserHeader(void* store); + +SecStore_Header* SecStore_GetHeader(void* store); + +#ifdef __cplusplus +} +#endif + +#endif // SEC_ADAPTER_STORE_H diff --git a/src/sec_adapter_svp.c b/src/sec_adapter_svp.c new file mode 100644 index 0000000..41946c4 --- /dev/null +++ b/src/sec_adapter_svp.c @@ -0,0 +1,161 @@ +/** + * Copyright 2020-2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_adapter_svp.h" + +// Deprecated +Sec_Result Sec_OpaqueBufferMalloc(SEC_SIZE bufLength, void** handle, void* params) { + return SecOpaqueBuffer_Malloc(bufLength, (Sec_OpaqueBufferHandle**) handle); +} + +Sec_Result SecOpaqueBuffer_Malloc(SEC_SIZE bufLength, Sec_OpaqueBufferHandle** handle) { + if (bufLength == 0) { + SEC_LOG_ERROR("Argument `length' has value of 0"); + return SEC_RESULT_FAILURE; + } + if (handle == NULL) { + SEC_LOG_ERROR("Argument `handle' has value of null"); + return SEC_RESULT_FAILURE; + } + + *handle = (Sec_OpaqueBufferHandle*) calloc(1, sizeof(Sec_OpaqueBufferHandle)); + if (*handle == NULL) { + SEC_LOG_ERROR("Calloc failed"); + return SEC_RESULT_FAILURE; + } + + sa_status status = sa_svp_buffer_alloc(&(*handle)->svp_buffer, bufLength); + if (status != SA_STATUS_OK) { + free(*handle); + CHECK_STATUS(status) + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result Sec_OpaqueBufferWrite(Sec_OpaqueBufferHandle* opaqueBufferHandle, SEC_SIZE offset, void* data, + SEC_SIZE length) { + return SecOpaqueBuffer_Write(opaqueBufferHandle, offset, data, length); +} + +Sec_Result SecOpaqueBuffer_Write(Sec_OpaqueBufferHandle* opaqueBufferHandle, SEC_SIZE offset, SEC_BYTE* data, + SEC_SIZE length) { + if (opaqueBufferHandle == NULL) { + SEC_LOG_ERROR("Invalid handle"); + return SEC_RESULT_INVALID_HANDLE; + } + + if (data == NULL) { + SEC_LOG_ERROR("Argument `data' has value of null"); + return SEC_RESULT_FAILURE; + } + + if (length == 0) { + SEC_LOG_ERROR("Argument `length' has value of 0"); + return SEC_RESULT_FAILURE; + } + + size_t out_offset = offset; + sa_status status = sa_svp_buffer_write((*opaqueBufferHandle).svp_buffer, &out_offset, data, length); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +Sec_Result Sec_OpaqueBufferFree(Sec_OpaqueBufferHandle* opaqueBufferHandle, void* params) { + return SecOpaqueBuffer_Free(opaqueBufferHandle); +} + +Sec_Result SecOpaqueBuffer_Free(Sec_OpaqueBufferHandle* opaqueBufferHandle) { + if (opaqueBufferHandle != NULL) { + + sa_svp_buffer_free(opaqueBufferHandle->svp_buffer); + SEC_FREE(opaqueBufferHandle); + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecOpaqueBuffer_Copy(Sec_OpaqueBufferHandle* outOpaqueBufferHandle, SEC_SIZE out_offset, + Sec_OpaqueBufferHandle* inOpaqueBufferHandle, SEC_SIZE in_offset, SEC_SIZE num_to_copy) { + if (outOpaqueBufferHandle == NULL || inOpaqueBufferHandle == NULL) { + SEC_LOG_ERROR("Null pointer arg encountered"); + return SEC_RESULT_FAILURE; + } + + size_t out_off = out_offset; + size_t in_off = in_offset; + sa_status status = sa_svp_buffer_copy(outOpaqueBufferHandle->svp_buffer, &out_off, inOpaqueBufferHandle->svp_buffer, + &in_off, num_to_copy); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecOpaqueBuffer_Release(Sec_OpaqueBufferHandle* opaqueBufferHandle, Sec_ProtectedMemHandle** svpHandle) { + if (svpHandle == NULL || opaqueBufferHandle == NULL) { + SEC_LOG_ERROR("Null pointer arg encountered"); + return SEC_RESULT_FAILURE; + } + + size_t out_length; + sa_status status = sa_svp_buffer_release(svpHandle, &out_length, opaqueBufferHandle->svp_buffer); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecOpaqueBuffer_Check(Sec_DigestAlgorithm digestAlgorithm, Sec_OpaqueBufferHandle* opaqueBufferHandle, + SEC_SIZE length, SEC_BYTE* expected, SEC_SIZE expectedLength) { + if (opaqueBufferHandle == NULL) { + SEC_LOG_ERROR("Null pointer arg encountered"); + return SEC_RESULT_FAILURE; + } + + if (expected == NULL) { + SEC_LOG_ERROR("Null pointer arg encountered"); + return SEC_RESULT_FAILURE; + } + + sa_digest_algorithm algorithm; + switch (digestAlgorithm) { + case SEC_DIGESTALGORITHM_SHA1: + algorithm = SA_DIGEST_ALGORITHM_SHA1; + break; + + case SEC_DIGESTALGORITHM_SHA256: + algorithm = SA_DIGEST_ALGORITHM_SHA256; + break; + + default: + return SEC_RESULT_INVALID_PARAMETERS; + } + + sa_status status = sa_svp_buffer_check(opaqueBufferHandle->svp_buffer, 0, length, algorithm, expected, + expectedLength); + CHECK_STATUS(status) + return SEC_RESULT_SUCCESS; +} + +/** + * @brief Checks secure boot configuration to verify that Secure Boot is enabled. + */ +Sec_Result SecCodeIntegrity_SecureBootEnabled(void) { + return SEC_RESULT_UNIMPLEMENTED_FEATURE; +} + +Sec_Result SecSVP_SetTime(time_t time) { + return SEC_RESULT_UNIMPLEMENTED_FEATURE; +} diff --git a/src/sec_adapter_svp.h b/src/sec_adapter_svp.h new file mode 100644 index 0000000..96058c6 --- /dev/null +++ b/src/sec_adapter_svp.h @@ -0,0 +1,30 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_ADAPTER_SVP_H +#define SEC_ADAPTER_SVP_H + +#include "sa.h" +#include "sec_adapter_processor.h" +#include "sec_security.h" + +struct Sec_OpaqueBufferHandle_struct { + sa_svp_buffer svp_buffer; +}; + +#endif // SEC_ADAPTER_SVP_H diff --git a/src/sec_adapter_utils.c b/src/sec_adapter_utils.c new file mode 100644 index 0000000..70a2d52 --- /dev/null +++ b/src/sec_adapter_utils.c @@ -0,0 +1,1216 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_adapter_utils.h" +#include +#include + +static const SEC_BYTE base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static int is_base64(unsigned char c); + +Sec_Result SecUtils_ReadFile(const char* path, void* data, SEC_SIZE data_len, SEC_SIZE* data_read) { + FILE* f = NULL; + SEC_BYTE last_byte; + + *data_read = 0; + + f = fopen(path, "rbe"); + if (f == NULL) { + SEC_LOG_ERROR("Could not open file: %s", path); + return SEC_RESULT_FAILURE; + } + + while (ferror(f) == 0 && feof(f) == 0 && *data_read < data_len) { + *data_read += fread(data, 1, data_len - *data_read, f); + } + + if (ferror(f) != 0) { + SEC_LOG_ERROR("Ferror encountered while reading file: %s", path); + fclose(f); + f = NULL; + return SEC_RESULT_NO_SUCH_ITEM; + } + + fread(&last_byte, 1, 1, f); + + if (feof(f) == 0) { + SEC_LOG_ERROR("Data_len is too small"); + fclose(f); + f = NULL; + return SEC_RESULT_BUFFER_TOO_SMALL; + } + + fclose(f); + f = NULL; + + return SEC_RESULT_SUCCESS; +} + +static long SecUtils_GetFileLen(const char* path) { + FILE* f = NULL; + long len = -1; + + f = fopen(path, "rbe"); + if (f == NULL) { + SEC_LOG_ERROR("Could not open file: %s", path); + return len; + } + + fseek(f, 0L, SEEK_END); + len = ftell(f); + fseek(f, 0L, SEEK_SET); + + if (fclose(f) != 0) { + SEC_LOG_ERROR("Fclose failed"); + } + + return len; +} + +static Sec_Result SecUtils_VerifyFile(const char* path, void* expected, SEC_SIZE expected_len) { + SEC_BYTE* read = NULL; + SEC_SIZE read_len; + SEC_SIZE file_len; + + //allocate memory for verification + read = (SEC_BYTE*) malloc(expected_len); + if (read == NULL) { + SEC_LOG_ERROR("Malloc failed for file: %s", path); + SEC_FREE(read); + return SEC_RESULT_FAILURE; + } + + //make sure that the written file is of proper length + file_len = SecUtils_GetFileLen(path); + if (expected_len != file_len) { + SEC_LOG_ERROR("File written out (%s) is %d bytes, but expected %d", path, file_len, expected_len); + SEC_FREE(read); + return SEC_RESULT_FAILURE; + } + + //read data back in + if (SecUtils_ReadFile(path, read, expected_len, &read_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_ReadFile failed for file: %s", path); + SEC_FREE(read); + return SEC_RESULT_FAILURE; + } + + //compare read data to input + if (memcmp(expected, read, expected_len) != 0) { + SEC_LOG_ERROR("Data read in does not match the data written out for file: %s", path); + SEC_FREE(read); + return SEC_RESULT_FAILURE; + } + + SEC_FREE(read); + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecUtils_WriteFile(const char* path, void* data, SEC_SIZE data_len) { + Sec_Result sec_res = SEC_RESULT_FAILURE; + FILE* f = NULL; + int fdesc; + int dir_fdesc = -1; + char* path_cpy = NULL; + + //make a copy of the path string since basedir will change it + do { + path_cpy = strdup(path); + if (path_cpy == NULL) { + SEC_LOG_ERROR("Strdup failed for file: %s", path); + break; + } + + //open file + f = fopen(path, "wbe"); + if (f == NULL) { + SEC_LOG_ERROR("Could not open file: %s, errno: %d", path, errno); + break; + } + + //get file descriptor + fdesc = fileno(f); + if (fdesc < 0) { + SEC_LOG_ERROR("Fileno failed for file: %s, errno: %d", path, errno); + break; + } + + //write contents + if (data_len != fwrite(data, 1, data_len, f)) { + SEC_LOG_ERROR("Could not write to file: %s, errno: %d", path, errno); + break; + } + + //flush + if (fflush(f) != 0) { + SEC_LOG_ERROR("Fflush failed for file: %s, errno: %d", path, errno); + break; + } + + //force sync on written file + if (fsync(fdesc) != 0) { + SEC_LOG_ERROR("Fsync failed for file: %s, errno: %d", path, errno); + break; + } + + //close file + if (fclose(f) != 0) { + SEC_LOG_ERROR("Fclose failed for file: %s, errno: %d", path, errno); + f = NULL; + break; + } + f = NULL; + + //sync parent directory + dir_fdesc = open(dirname(path_cpy), O_RDONLY | O_CLOEXEC); // NOLINT + if (dir_fdesc < 0) { + SEC_LOG_ERROR("Open parent failed for file: %s, errno: %d", path, errno); + break; + } + + if (fsync(dir_fdesc) != 0) { + SEC_LOG_ERROR("Fsync parent failed for file: %s, errno: %d", path, errno); + break; + } + + if (close(dir_fdesc) != 0) { + dir_fdesc = -1; + SEC_LOG_ERROR("Close parent failed for file: %s, errno: %d", path, errno); + break; + } + dir_fdesc = -1; + + //verify written file + if (SecUtils_VerifyFile(path, data, data_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_VerifyFile failed for file: %s", path); + break; + } + + sec_res = SEC_RESULT_SUCCESS; + } while (false); + + if (f != NULL) { + if (fclose(f) != 0) { + SEC_LOG_ERROR("Fclose failed for file: %s, errno: %d", path, errno); + } + f = NULL; + } + + if (dir_fdesc >= 0) { + if (close(dir_fdesc) != 0) { + SEC_LOG_ERROR("Close parent failed for file: %s, errno: %d", path, errno); + } + } + + SEC_FREE(path_cpy); + + return sec_res; +} + +Sec_Result SecUtils_RmFile(const char* path) { + void* zeros = NULL; + long len; + + len = SecUtils_GetFileLen(path); + if (len > 0) { + zeros = calloc(len, 1); + if (zeros != NULL) { + SecUtils_WriteFile(path, zeros, len); + free(zeros); + } else { + SEC_LOG_ERROR("Calloc failed"); + } + } + + if (unlink(path) != 0) { + SEC_LOG_ERROR("Unlink %s failed", path); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +SEC_BOOL SecUtils_FileExists(const char* path) { + FILE* f = NULL; + + f = fopen(path, "rbe"); + if (f == NULL) + return SEC_FALSE; + + fclose(f); + + return SEC_TRUE; +} + +SEC_SIZE SecUtils_LsDir(const char* path, Sec_LsDirEntry* entries, SEC_SIZE maxNumEntries) { + struct dirent* dent; + struct stat st; + DIR* srcdir; + SEC_SIZE found = 0; + char file_path[SEC_MAX_FILE_PATH_LEN]; + + srcdir = opendir(path); + + if (srcdir == NULL) { + SEC_LOG_ERROR("Opendir failed"); + return 0; + } + + while ((dent = readdir(srcdir)) != NULL) { // NOLINT + snprintf(file_path, sizeof(file_path), "%s%s", path, dent->d_name); + + if (stat(file_path, &st) < 0) { + SEC_LOG_ERROR("Fstatat failed on: %s", dent->d_name); + continue; + } + + /* store found file */ + if (entries != NULL && found < maxNumEntries) { + snprintf(entries[found].name, sizeof(entries[found].name), "%s", + dent->d_name); + entries[found].is_dir = S_ISDIR(st.st_mode); + } + + ++found; + } + + closedir(srcdir); + return found; +} + +Sec_Result SecUtils_MkDir(const char* path) { + char tmp[SEC_MAX_FILE_PATH_LEN]; + char* p = NULL; + size_t len; + + snprintf(tmp, sizeof(tmp), "%s", path); + len = strlen(tmp); + if (len == 0) { + SEC_LOG_ERROR("Empty path string"); + return SEC_RESULT_FAILURE; + } + + if (tmp[len - 1] == '/') { + tmp[len - 1] = 0; + } + + for (p = tmp + 1; *p != 0; p++) { + if (*p == '/') { + *p = 0; + if (mkdir(tmp, S_IRWXU) != 0 && errno != EEXIST) { + SEC_LOG_ERROR("Mkdir %s failed", tmp); + return SEC_RESULT_FAILURE; + } + + *p = '/'; + } + } + + if (mkdir(tmp, S_IRWXU) != 0 && errno != EEXIST) { + SEC_LOG_ERROR("Mkdir %s failed", tmp); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +SEC_SIZE SecUtils_UpdateItemList(SEC_OBJECTID* items, SEC_SIZE maxNumItems, SEC_SIZE numItems, SEC_OBJECTID item_id) { + /* if array is full, just return it */ + if (numItems >= maxNumItems) + return numItems; + + /* if item already in the list, skip it */ + if (SecUtils_ItemIndex(items, numItems, item_id) != -1) + return numItems; + + items[numItems] = item_id; + ++numItems; + + return numItems; +} + +SEC_SIZE SecUtils_UpdateItemListFromDir(SEC_OBJECTID* items, SEC_SIZE maxNumItems, SEC_SIZE numItems, const char* dir, + const char* ext) { + SEC_SIZE numEntries; + SEC_SIZE i; + Sec_LsDirEntry entries[256]; + char pattern[256]; + SEC_OBJECTID item_id; + + snprintf(pattern, sizeof(pattern), "%s%s", SEC_OBJECTID_PATTERN, ext); + + numEntries = SecUtils_LsDir(dir, entries, 256); + + for (i = 0; i < numEntries; ++i) { + if (!entries[i].is_dir && SecUtils_EndsWith(entries[i].name, ext)) { + /* obtain 64-bit item id */ + if (sscanf(entries[i].name, pattern, &item_id) != 1) { + SEC_LOG_ERROR("Sscanf failed on: %s", entries[i].name); + continue; + } + + numItems = SecUtils_UpdateItemList(items, maxNumItems, numItems, item_id); + } + } + + return numItems; +} + +SEC_BYTE SecUtils_EndsWith(const char* str, const char* end) { + SEC_SIZE lenstr; + SEC_SIZE lenend; + + if (!str || !end) + return 0; + + lenstr = strlen(str); + lenend = strlen(end); + if (lenend > lenstr) + return 0; + + return strncmp(str + lenstr - lenend, end, lenend) == 0; +} + +int SecUtils_ItemIndex(const SEC_OBJECTID* items, SEC_SIZE numItems, SEC_OBJECTID item) { + int i; + + for (i = 0; i < numItems; ++i) { + if (items[i] == item) + return i; + } + + return -1; +} + +SEC_SIZE SecUtils_X509ToDerLen(X509* x509, void* mem, SEC_SIZE mem_len) { + int written; + SEC_BYTE* ptr = (SEC_BYTE*) mem; + + if (i2d_X509(x509, NULL) >= mem_len) { + SEC_LOG_ERROR("Buffer is too small"); + return 0; + } + + written = i2d_X509(x509, &ptr); + + if (written < 0) { + SEC_LOG_ERROR("I2d_X509 failed"); + return 0; + } + + return written; +} + +EC_KEY* SecUtils_ECCFromDERPriv(const SEC_BYTE* der, SEC_SIZE der_len) { + const unsigned char* p = (const unsigned char*) der; + PKCS8_PRIV_KEY_INFO* p8 = NULL; + EVP_PKEY* evp_key = NULL; + EC_KEY* ecc = NULL; + + p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, &p, der_len); + if (p8 != NULL) { + evp_key = EVP_PKCS82PKEY(p8); + if (evp_key == NULL) { + SEC_LOG_ERROR("EVP_PKCS82PKEY failed"); + SEC_EVPPKEY_FREE(evp_key); + PKCS8_PRIV_KEY_INFO_free(p8); + return ecc; + } + } else { + evp_key = d2i_AutoPrivateKey(NULL, &p, der_len); + if (evp_key == NULL) { + SEC_LOG_ERROR("d2i_AutoPrivateKey failed"); + SEC_EVPPKEY_FREE(evp_key); + return ecc; + } + } + + ecc = EVP_PKEY_get1_EC_KEY(evp_key); + if (ecc == NULL) { + SEC_LOG_ERROR("EVP_PKEY_get1_EC_KEY failed"); + SEC_EVPPKEY_FREE(evp_key); + if (p8 != NULL) + PKCS8_PRIV_KEY_INFO_free(p8); + + return ecc; + } + + SEC_EVPPKEY_FREE(evp_key); + if (p8 != NULL) + PKCS8_PRIV_KEY_INFO_free(p8); + + return ecc; +} + +Sec_Result SecUtils_ECCToPubBinary(EC_KEY* ec_key, Sec_ECCRawPublicKey* binary) { + BIGNUM* x = NULL; + BIGNUM* y = NULL; + + if (SecUtils_Extract_EC_KEY_X_Y(ec_key, &x, &y, NULL) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_ECCToPubBinary: SecUtils_Extract_EC_KEY_X_Y failed"); + return SEC_RESULT_FAILURE; + } + + binary->type = SEC_KEYTYPE_ECC_NISTP256_PUBLIC; + Sec_Uint32ToBEBytes(SecKey_GetKeyLenForKeyType(binary->type), binary->key_len); + SecUtils_BigNumToBuffer(x, binary->x, Sec_BEBytesToUint32(binary->key_len)); + SecUtils_BigNumToBuffer(y, binary->y, Sec_BEBytesToUint32(binary->key_len)); + + BN_free(y); + BN_free(x); + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecUtils_Base64Encode(const SEC_BYTE* input, SEC_SIZE input_len, SEC_BYTE* output, SEC_SIZE max_output, + SEC_SIZE* out_len) { + int i = 0; + int j; + SEC_BYTE arr3[3]; + SEC_BYTE arr4[4]; + SEC_SIZE ret_len = 0; + + *out_len = 0; + memset(arr3, 0, 3); + memset(arr4, 0, 4); + while (input_len--) { + arr3[i++] = *(input++); + if (i == 3) { + arr4[0] = (arr3[0] & 0xfc) >> 2; + arr4[1] = ((arr3[0] & 0x03) << 4) + ((arr3[1] & 0xf0) >> 4); + arr4[2] = ((arr3[1] & 0x0f) << 2) + ((arr3[2] & 0xc0) >> 6); + arr4[3] = arr3[2] & 0x3f; + + for (i = 0; i < 4; i++) { + if (ret_len >= max_output) { + SEC_LOG_ERROR("Output buffer too small"); + return SEC_RESULT_FAILURE; + } + + output[ret_len++] = base64_chars[arr4[i]]; + } + + i = 0; + } + } + + if (i) { + for (j = i; j < 3; j++) { + arr3[j] = '\0'; + } + + arr4[0] = (arr3[0] & 0xfc) >> 2; + arr4[1] = ((arr3[0] & 0x03) << 4) + ((arr3[1] & 0xf0) >> 4); + arr4[2] = ((arr3[1] & 0x0f) << 2) + ((arr3[2] & 0xc0) >> 6); + arr4[3] = arr3[2] & 0x3f; + + for (j = 0; j < (i + 1); j++) { + if (ret_len >= max_output) { + SEC_LOG_ERROR("Output buffer too small"); + return SEC_RESULT_FAILURE; + } + + output[ret_len++] = base64_chars[arr4[j]]; + } + + while (i++ < 3) { + if (ret_len >= max_output) { + SEC_LOG_ERROR("Output buffer too small"); + return SEC_RESULT_FAILURE; + } + + output[ret_len++] = '='; + } + } + + *out_len = ret_len; + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecUtils_Base64Decode(const SEC_BYTE* input, SEC_SIZE in_len, SEC_BYTE* output, SEC_SIZE max_output, + SEC_SIZE* out_len) { + Sec_Result status = SEC_RESULT_FAILURE; + SEC_SIZE i = 0; + SEC_SIZE j = 0; + SEC_SIZE z = 0; + SEC_SIZE ret_len = 0; + SEC_SIZE curPos = 0; + SEC_BYTE arr3[3]; + SEC_BYTE arr4[4]; + + *out_len = 0; + + if (in_len <= 1) { + SEC_LOG_ERROR("Illegal base64 string"); + return SEC_RESULT_FAILURE; + } + + memset(arr3, 0, 3); + memset(arr4, 0, 4); + while (in_len-- && (input[curPos] != '=') && is_base64(input[curPos])) { + arr4[i++] = input[curPos]; + curPos++; + if (i == 4) { + for (i = 0; i < 4; i++) { + for (z = 0; z < 64; z++) { + if (base64_chars[z] == arr4[i]) { + arr4[i] = (SEC_BYTE) z; + break; + } + } + } + + arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4); + arr3[1] = ((arr4[1] & 0xf) << 4) + ((arr4[2] & 0x3c) >> 2); + arr3[2] = ((arr4[2] & 0x3) << 6) + arr4[3]; + + for (i = 0; i < 3; i++) { + if (ret_len >= max_output) { + SEC_LOG_ERROR("Output buffer too small"); + return SEC_RESULT_FAILURE; + } + output[ret_len++] = arr3[i]; + } + i = 0; + } + } + + if (i) { + for (j = i; j < 4; j++) { + arr4[j] = 0; + } + + for (j = 0; j < 4; j++) { + for (z = 0; z < 64; z++) { + if (base64_chars[z] == arr4[j]) { + arr4[j] = z; + break; + } + } + } + + arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4); + arr3[1] = ((arr4[1] & 0xf) << 4) + ((arr4[2] & 0x3c) >> 2); + arr3[2] = ((arr4[2] & 0x3) << 6) + arr4[3]; + + for (j = 0; (j < i - 1); j++) { + if (ret_len >= max_output) { + SEC_LOG_ERROR("Output buffer too small"); + return SEC_RESULT_FAILURE; + } + output[ret_len++] = arr3[j]; + } + } + + *out_len = ret_len; + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecUtils_ECCToPrivBinary(EC_KEY* ec_key, Sec_ECCRawPrivateKey* binary) { + BIGNUM* x = NULL; + BIGNUM* y = NULL; + Sec_KeyType keyType; + + if (SecUtils_Extract_EC_KEY_X_Y(ec_key, &x, &y, &keyType) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_ECCToPrivBinary: SecUtils_Extract_EC_KEY_X_Y failed"); + return SEC_RESULT_FAILURE; + } + + if (keyType != SEC_KEYTYPE_ECC_NISTP256_PUBLIC) { + SEC_LOG_ERROR("Unexpected key type encountered: %d", keyType); + return SEC_RESULT_FAILURE; + } + + binary->type = SEC_KEYTYPE_ECC_NISTP256; + Sec_Uint32ToBEBytes(SecKey_GetKeyLenForKeyType(keyType), binary->key_len); + SecUtils_BigNumToBuffer((BIGNUM*) EC_KEY_get0_private_key(ec_key), binary->prv, + Sec_BEBytesToUint32(binary->key_len)); + SecUtils_BigNumToBuffer(x, binary->x, Sec_BEBytesToUint32(binary->key_len)); + SecUtils_BigNumToBuffer(y, binary->y, Sec_BEBytesToUint32(binary->key_len)); + + BN_free(y); + BN_free(x); + return SEC_RESULT_SUCCESS; +} + +RSA* SecUtils_RSAFromDERPriv(const SEC_BYTE* der, SEC_SIZE der_len) { + const unsigned char* p = (const unsigned char*) der; + PKCS8_PRIV_KEY_INFO* p8 = NULL; + EVP_PKEY* evp_key = NULL; + RSA* rsa = NULL; + + p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, &p, der_len); + if (p8 != NULL) { + evp_key = EVP_PKCS82PKEY(p8); + if (evp_key == NULL) { + SEC_LOG_ERROR("EVP_PKCS82PKEY failed"); + SEC_EVPPKEY_FREE(evp_key); + PKCS8_PRIV_KEY_INFO_free(p8); + return rsa; + } + } else { + evp_key = d2i_AutoPrivateKey(NULL, &p, der_len); + if (evp_key == NULL) { + SEC_LOG_ERROR("d2i_AutoPrivateKey failed"); + SEC_EVPPKEY_FREE(evp_key); + return rsa; + } + } + + rsa = EVP_PKEY_get1_RSA(evp_key); + if (rsa == NULL) { + SEC_LOG_ERROR("EVP_PKEY_get1_RSA failed"); + SEC_EVPPKEY_FREE(evp_key); + if (p8 != NULL) + PKCS8_PRIV_KEY_INFO_free(p8); + + return rsa; + } + + SEC_EVPPKEY_FREE(evp_key); + if (p8 != NULL) + PKCS8_PRIV_KEY_INFO_free(p8); + + return rsa; +} + +Sec_Result SecUtils_RSAToDERPrivKeyInfo(RSA* rsa, SEC_BYTE* output, SEC_SIZE out_len, SEC_SIZE* written) { + BIO* bio = NULL; + EVP_PKEY* evp_key = NULL; + BUF_MEM* bptr = NULL; + + evp_key = EVP_PKEY_new(); + if (EVP_PKEY_set1_RSA(evp_key, rsa) == 0) { + SEC_LOG_ERROR("EVP_PKEY_set1_RSA failed"); + SEC_EVPPKEY_FREE(evp_key); + SEC_BIO_FREE(bio); + return SEC_RESULT_FAILURE; + } + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + SEC_LOG_ERROR("BIO_new(BIO_s_mem()) failed"); + SEC_EVPPKEY_FREE(evp_key); + SEC_BIO_FREE(bio); + return SEC_RESULT_FAILURE; + } + + if (!i2d_PKCS8PrivateKeyInfo_bio(bio, evp_key)) { + SEC_LOG_ERROR("I2d_PKCS8_PRIV_KEY_INFO_bio failed"); + SEC_EVPPKEY_FREE(evp_key); + SEC_BIO_FREE(bio); + return SEC_RESULT_FAILURE; + } + + BIO_flush(bio); + BIO_get_mem_ptr(bio, &bptr); + + *written = bptr->length; + + if (output != NULL) { + if (out_len < bptr->length) { + SEC_LOG_ERROR("Output buffer is not large enough"); + SEC_EVPPKEY_FREE(evp_key); + SEC_BIO_FREE(bio); + return SEC_RESULT_FAILURE; + } + + memcpy(output, bptr->data, bptr->length); + } + + SEC_EVPPKEY_FREE(evp_key); + SEC_BIO_FREE(bio); + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecUtils_WrapSymetric(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID wrappingKey, + Sec_CipherAlgorithm wrappingAlg, SEC_BYTE* iv, SEC_BYTE* payload, SEC_SIZE payloadLen, SEC_BYTE* out, + SEC_SIZE out_len, SEC_SIZE* written) { + if (SecCipher_SingleInputId(processorHandle, wrappingAlg, SEC_CIPHERMODE_ENCRYPT, wrappingKey, iv, payload, + payloadLen, out, out_len, written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_SingleInputId failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result SecUtils_BigNumToBuffer(const BIGNUM* bignum, SEC_BYTE* buffer, SEC_SIZE buffer_len) { + SEC_SIZE num_bytes; + + memset(buffer, 0, buffer_len); + num_bytes = BN_num_bytes(bignum); + + if (num_bytes > buffer_len) { + SEC_LOG_ERROR("Buffer not large enough. needed: %d, actual: %d", num_bytes, buffer_len); + return SEC_RESULT_FAILURE; + } + + BN_bn2bin(bignum, buffer + buffer_len - num_bytes); + return SEC_RESULT_SUCCESS; +} + +/* + * The next steps a caller might take are: + * SecUtils_BigNumToBuffer(x, public_key->x, Sec_BEBytesToUint32(public_key->key_len)); + * SecUtils_BigNumToBuffer(y, public_key->y, Sec_BEBytesToUint32(public_key->key_len)); + */ +Sec_Result SecUtils_Extract_EC_KEY_X_Y(const EC_KEY* ec_key, BIGNUM** xp, BIGNUM** yp, Sec_KeyType* keyTypep) { + const EC_GROUP* group = NULL; + const EC_POINT* ec_point = NULL; + BN_CTX* ctx = NULL; + + if (xp == NULL) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: X cannot be NULL"); + if (ctx != NULL) + BN_CTX_free(ctx); + + return SEC_RESULT_FAILURE; + } + + group = EC_KEY_get0_group(ec_key); + if (group == NULL) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: EC_KEY_get0_group: %s", ERR_error_string(ERR_get_error(), NULL)); + if (ctx != NULL) + BN_CTX_free(ctx); + + return SEC_RESULT_FAILURE; + } + + ec_point = EC_KEY_get0_public_key(ec_key); + if (ec_point == NULL) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: EC_KEY_get0_public_key: %s", + ERR_error_string(ERR_get_error(), NULL)); + if (ctx != NULL) + BN_CTX_free(ctx); + + return SEC_RESULT_FAILURE; + } + + ctx = BN_CTX_new(); + if (ctx == NULL) { + SEC_LOG_ERROR("BN_CTX_new() failed"); + if (ctx != NULL) + BN_CTX_free(ctx); + + return SEC_RESULT_FAILURE; + } + + *xp = BN_new(); + if (*xp == NULL) { + SEC_LOG_ERROR("BN_new() failed"); + if (ctx != NULL) + BN_CTX_free(ctx); + + return SEC_RESULT_FAILURE; + } + + if (yp != NULL) { // if caller wants y coordinate returned + *yp = BN_new(); + if (*yp == NULL) { + SEC_LOG_ERROR("BN_new() failed"); + if (ctx != NULL) + BN_CTX_free(ctx); + + return SEC_RESULT_FAILURE; + } + } + + if (keyTypep != NULL) // if caller wants key type returned + { + *keyTypep = SEC_KEYTYPE_ECC_NISTP256_PUBLIC; + } + + // Get the X coordinate and optionally the Y coordinate + if (EC_POINT_get_affine_coordinates_GFp(group, ec_point, *xp, yp != NULL ? *yp : NULL, ctx) != 1) { + BN_clear_free(*xp); + if (yp != NULL) + BN_clear_free(*yp); + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: EC_POINT_get_affine_coordinates_GFp: %s", + ERR_error_string(ERR_get_error(), NULL)); + if (ctx != NULL) + BN_CTX_free(ctx); + + return SEC_RESULT_FAILURE; + } + + if (ctx != NULL) + BN_CTX_free(ctx); + + return SEC_RESULT_SUCCESS; +} + +// ec_key is the other side's public ECC key +// +// Returns the number of bytes in the encrypted output or +// -1 if there was an error +int SecUtils_ElGamal_Encrypt_Rand(EC_KEY* ec_key, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BYTE* output, + SEC_SIZE outputSize, BIGNUM* sender_rand) { + int result = -1; + BIGNUM* inputAsBN = NULL; + const EC_GROUP* group = NULL; + const EC_POINT* P = NULL; + const EC_POINT* PK_recipient = NULL; + EC_POINT* shared_secret = NULL; + EC_POINT* key_2_wrap_point = NULL; + EC_POINT* sender_share = NULL; + EC_POINT* wrapped_key = NULL; + BIGNUM* x = NULL; + BIGNUM* y = NULL; + BN_CTX* ctx = NULL; + + do { + if (inputSize != SEC_ECC_NISTP256_KEY_LEN) { + SEC_LOG_ERROR("Input size needed != One BIGNUM"); + break; + } + + if (outputSize < 4 * SEC_ECC_NISTP256_KEY_LEN) { + SEC_LOG_ERROR("Output size needed < Four BIGNUMs"); + break; + } + + // Convert the input buffer to be encrypted to a BIGNUM + inputAsBN = BN_new(); + if (inputAsBN == NULL) { + SEC_LOG_ERROR("BN_new failed"); + break; + } + if (BN_bin2bn(input, (int) inputSize, inputAsBN) == NULL) { + SEC_LOG_ERROR("BN_bin2bn failed. Error: %s", + ERR_error_string(ERR_get_error(), NULL)); + break; + } + + group = EC_KEY_get0_group(ec_key); + if (group == NULL) { + SEC_LOG_ERROR("EC_KEY_get0_group failed"); + break; + } + + ctx = BN_CTX_new(); + if (ctx == NULL) { + SEC_LOG_ERROR("BN_CTX_new failed"); + break; + } + + // Convert the X coordinate to an EC Point. This takes the desired Y value in 1 bit (to choose + // which of the two possible Y values to use). This *calculates* an actual Y value for the point. + key_2_wrap_point = EC_POINT_new(group); + if (key_2_wrap_point == NULL) { + SEC_LOG_ERROR("EC_POINT_new failed"); + break; + } + + if (!EC_POINT_set_compressed_coordinates_GFp(group, key_2_wrap_point, inputAsBN, 0, ctx)) //$$$ 1=>0 on 7/8/15 + { + // Don't print an error message if the error is "point not on curve" 100A906E, but still fail + if (ERR_get_error() != 0x100A906E) // i.e. error:100A906E:lib(16):func(169):reason(110) + SEC_LOG_ERROR("Set EC_POINT_set_compressed_coordinates_GFp failed. Error: %s", + ERR_error_string(ERR_get_error(), NULL)); + break; + } + + // Calc sender's shared point 'wP' => this gets sent back to receiver + sender_share = EC_POINT_new(group); + if (sender_share == NULL) { + SEC_LOG_ERROR("EC_POINT_new failed"); + break; + } + + P = EC_GROUP_get0_generator(group); + if (P == NULL) { + SEC_LOG_ERROR("EC_GROUP_get0_generator failed"); + break; + } + + EC_POINT_mul(group, sender_share, NULL, P, sender_rand, ctx); + + // Calc sender's Shared Secret 'wRr' => this hides the key I want to send + shared_secret = EC_POINT_new(group); + if (shared_secret == NULL) { + SEC_LOG_ERROR("EC_POINT_new failed"); + break; + } + + PK_recipient = EC_KEY_get0_public_key(ec_key); + if (PK_recipient == NULL) { + SEC_LOG_ERROR("EC_KEY_get0_public_key failed"); + break; + } + + EC_POINT_mul(group, shared_secret, NULL, PK_recipient, sender_rand, ctx); + + // key_2_wrap_point is a point on the curve, we add the shared_secret + // to it and send the result, the wrapped_key, to the receiver. + wrapped_key = EC_POINT_new(group); + if (wrapped_key == NULL) { + SEC_LOG_ERROR("EC_POINT_new failed"); + break; + } + + EC_POINT_add(group, wrapped_key, key_2_wrap_point, shared_secret, ctx); + + // Dissect the wrapped point to get its coordinates + x = BN_new(); + if (x == NULL) { + SEC_LOG_ERROR("BN_new failed"); + break; + } + + y = BN_new(); + if (y == NULL) { + SEC_LOG_ERROR("BN_new failed"); + break; + } + + // Dissect shared_secret to get its coordinates and output them + EC_POINT_get_affine_coordinates_GFp(group, sender_share, x, y, ctx); + if (SecUtils_BigNumToBuffer(x, (unsigned char*) &output[0 * SEC_ECC_NISTP256_KEY_LEN], + SEC_ECC_NISTP256_KEY_LEN) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_BigNumToBuffer failed"); + break; + } + + if (SecUtils_BigNumToBuffer(y, (unsigned char*) &output[1 * SEC_ECC_NISTP256_KEY_LEN], + SEC_ECC_NISTP256_KEY_LEN) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_BigNumToBuffer failed"); + break; + } + + // Dissect wrapped_key to get its coordinates and output them + EC_POINT_get_affine_coordinates_GFp(group, wrapped_key, x, y, ctx); + + if (SecUtils_BigNumToBuffer(x, (unsigned char*) &output[2 * SEC_ECC_NISTP256_KEY_LEN], + SEC_ECC_NISTP256_KEY_LEN) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_BigNumToBuffer failed"); + break; + } + + if (SecUtils_BigNumToBuffer(y, (unsigned char*) &output[3 * SEC_ECC_NISTP256_KEY_LEN], + SEC_ECC_NISTP256_KEY_LEN) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_BigNumToBuffer failed"); + break; + } + + result = 4 * SEC_ECC_NISTP256_KEY_LEN; + } while (false); + + if (x != NULL) + BN_free(x); + if (y != NULL) + BN_free(y); + if (inputAsBN != NULL) + BN_free(inputAsBN); + if (sender_rand != NULL) + BN_free(sender_rand); + if (shared_secret != NULL) + EC_POINT_free(shared_secret); + if (sender_share != NULL) + EC_POINT_free(sender_share); + if (key_2_wrap_point != NULL) + EC_POINT_free(key_2_wrap_point); + if (wrapped_key != NULL) + EC_POINT_free(wrapped_key); + BN_CTX_free(ctx); + return result; +} + +// ec_key is the other side's public ECC key +// +// Returns the number of bytes in the encrypted output or +// -1 if there was an error +int SecUtils_ElGamal_Encrypt(EC_KEY* ec_key, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BYTE* output, + SEC_SIZE outputSize) { + // Generate random number 'w' (multiplier) for the sender + BIGNUM* sender_rand = BN_new(); + + if (sender_rand == NULL) { + SEC_LOG_ERROR("BN_new failed"); + return -1; + } + + if (BN_rand(sender_rand, 256, -1, 0) == 0) { + SEC_LOG_ERROR("BN_rand failed"); + if (sender_rand != NULL) + BN_free(sender_rand); + return -1; + } + + return SecUtils_ElGamal_Encrypt_Rand(ec_key, input, inputSize, output, outputSize, sender_rand); +} + +EC_KEY* SecUtils_ECCFromPubBinary(Sec_ECCRawPublicKey* binary) { + BN_CTX* ctx = BN_CTX_new(); + + if (binary->type != SEC_KEYTYPE_ECC_NISTP256_PUBLIC && binary->type != SEC_KEYTYPE_ECC_NISTP256) + return NULL; + + EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); //create ec_key structure with NIST p256 curve; + const EC_GROUP* group = EC_KEY_get0_group(ec_key); + EC_POINT* ec_point = EC_POINT_new(group); + BN_CTX_start(ctx); + BIGNUM* xp; + BIGNUM* yp; + + if (((xp = BN_CTX_get(ctx)) == NULL) || ((yp = BN_CTX_get(ctx)) == NULL)) { + EC_POINT_free(ec_point); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return ec_key; + } + + EC_POINT_set_affine_coordinates_GFp(group, ec_point, + BN_bin2bn(binary->x, (int) Sec_BEBytesToUint32(binary->key_len), xp), + BN_bin2bn(binary->y, (int) Sec_BEBytesToUint32(binary->key_len), yp), ctx); + EC_KEY_set_public_key(ec_key, ec_point); + + EC_POINT_free(ec_point); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return ec_key; +} + +Sec_Result SecUtils_FillKeyStoreUserHeader(Sec_ProcessorHandle* processorHandle, SecUtils_KeyStoreHeader* header, + Sec_KeyContainer container) { + Sec_Memset(header, 0, sizeof(SecUtils_KeyStoreHeader)); + + if (SecProcessor_GetDeviceId(processorHandle, header->device_id) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecProcessor_GetDeviceId failed"); + return SEC_RESULT_FAILURE; + } + + header->inner_kc_type = container; + return SEC_RESULT_SUCCESS; +} + +SecUtils_KeyStoreHeader* SecUtils_GetKeyStoreUserHeader(void* store) { + return (SecUtils_KeyStoreHeader*) SecStore_GetUserHeader(store); +} + +Sec_Result SecUtils_ValidateKeyStore(Sec_ProcessorHandle* processorHandle, SEC_BOOL require_mac, void* store, + SEC_SIZE store_len) { + SecUtils_KeyStoreHeader header; + SEC_BYTE device_id[SEC_DEVICEID_LEN]; + + Sec_Memset(&header, 0, sizeof(header)); + + if (store_len < sizeof(SecStore_Header) || store_len < SecStore_GetStoreLen(store)) { + SEC_LOG_ERROR("Invalid store"); + return SEC_RESULT_FAILURE; + } + + if (memcmp(SEC_UTILS_KEYSTORE_MAGIC, SecStore_GetHeader(store)->user_header_magic, + strlen(SEC_UTILS_KEYSTORE_MAGIC)) != 0) { + SEC_LOG_ERROR("Invalid key store magic value"); + return SEC_RESULT_FAILURE; + } + + if (SecStore_RetrieveData(processorHandle, require_mac, &header, sizeof(header), NULL, 0, store, store_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecStore_RetrieveData failed"); + return SEC_RESULT_FAILURE; + } + + if (SecProcessor_GetDeviceId(processorHandle, device_id) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecProcessor_GetDeviceId failed"); + return SEC_RESULT_FAILURE; + } + + if (memcmp(device_id, header.device_id, SEC_DEVICEID_LEN) != 0) { + SEC_LOG_ERROR("Device_id does not match the key store"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result write_verification_file(Sec_ProcessorHandle* processorHandle, char* filename, SEC_BYTE* data, + SEC_SIZE data_length, SEC_BYTE* info, size_t info_length) { + SEC_BYTE digest[SHA256_DIGEST_LENGTH * 2]; + SEC_SIZE length = 0; + + if (SecDigest_SingleInput(processorHandle, SEC_DIGESTALGORITHM_SHA256, data, data_length, digest, + &length) != SEC_RESULT_SUCCESS || + length != SHA256_DIGEST_LENGTH) { + SEC_LOG_ERROR("Unable to calculate verification digest"); + return SEC_RESULT_FAILURE; + } + + if (info != NULL) { + if (SecDigest_SingleInput(processorHandle, SEC_DIGESTALGORITHM_SHA256, info, info_length, + digest + SHA256_DIGEST_LENGTH, &length) != SEC_RESULT_SUCCESS || + length != SHA256_DIGEST_LENGTH) { + SEC_LOG_ERROR("Unable to calculate verification digest"); + return SEC_RESULT_FAILURE; + } + } else { + memset(digest + SHA256_DIGEST_LENGTH, 0, SHA256_DIGEST_LENGTH); + } + + if (SecUtils_WriteFile(filename, digest, SHA256_DIGEST_LENGTH * 2) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Could not write verification file"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result verify_verification_file(Sec_ProcessorHandle* processorHandle, char* filename, SEC_BYTE* data, + SEC_SIZE data_length, SEC_BYTE* info, size_t info_length) { + SEC_BYTE digest[SHA256_DIGEST_LENGTH * 2]; + SEC_SIZE length; + if (SecUtils_ReadFile(filename, digest, sizeof(digest), &length) != SEC_RESULT_SUCCESS || + length != sizeof(digest)) { + SEC_LOG_ERROR("Could not read verification file"); + return SEC_RESULT_VERIFICATION_FAILED; + } + + SEC_BYTE digest_result[SHA256_DIGEST_LENGTH]; + if (SecDigest_SingleInput(processorHandle, SEC_DIGESTALGORITHM_SHA256, data, data_length, digest_result, + &length) != SEC_RESULT_SUCCESS || + length != SHA256_DIGEST_LENGTH) { + SEC_LOG_ERROR("Unable to calculate verification digest"); + return SEC_RESULT_VERIFICATION_FAILED; + } + + if (memcmp(digest_result, digest, SHA256_DIGEST_LENGTH) != 0) { + SEC_LOG_ERROR("verification mismatch on data file"); + return SEC_RESULT_VERIFICATION_FAILED; + } + + if (info != NULL) { + if (SecDigest_SingleInput(processorHandle, SEC_DIGESTALGORITHM_SHA256, info, info_length, + digest_result, &length) != SEC_RESULT_SUCCESS || + length != SHA256_DIGEST_LENGTH) { + SEC_LOG_ERROR("Unable to calculate verification digest"); + return SEC_RESULT_VERIFICATION_FAILED; + } + + if (memcmp(digest_result, digest + SHA256_DIGEST_LENGTH, SHA256_DIGEST_LENGTH) != 0) { + SEC_LOG_ERROR("verification mismatch on info file"); + return SEC_RESULT_VERIFICATION_FAILED; + } + } + + return SEC_RESULT_SUCCESS; +} + +static int is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} diff --git a/src/sec_adapter_utils.h b/src/sec_adapter_utils.h new file mode 100644 index 0000000..dbf9e8a --- /dev/null +++ b/src/sec_adapter_utils.h @@ -0,0 +1,208 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_ADAPTER_UTILS_H_ +#define SEC_ADAPTER_UTILS_H_ + +#include "sec_adapter_store.h" +#include "sec_security.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +#include +#include +extern "C" { +#else +#include +#include +#endif + +#define SEC_INVALID_EPOCH ((SEC_SIZE) -1) + +typedef struct { + uint8_t inner_kc_type; + uint8_t reserved[7]; + uint8_t device_id[SEC_DEVICEID_LEN]; +} SecUtils_KeyStoreHeader; + +#define SEC_UTILS_KEYSTORE_MAGIC "KST0" + +/** + * @brief Read data from a file into a specified buffer. + * + * @param path file path + * @param data output data buffer where the file contents will be written + * @param data_len length of the output buffer + * @param data_read actual number of bytes written + * + * @return status of the operation + */ +Sec_Result SecUtils_ReadFile(const char* path, void* data, SEC_SIZE data_len, SEC_SIZE* data_read); + +/** + * @brief Write the input data into a specified file. + * + * @param path output file path + * @param data data to write + * @param data_len length of input data + * + * @return status of the operation + */ +Sec_Result SecUtils_WriteFile(const char* path, void* data, SEC_SIZE data_len); + +/** + * @brief create a specified directory. + * @param path directory path + */ +Sec_Result SecUtils_MkDir(const char* path); + +/** + * @brief Remove a specified file. + * + * @param path of the file to remove + */ +Sec_Result SecUtils_RmFile(const char* path); + +/** + * @brief Checks whether the specified file exists. + * + * @param path file path + * + * @return 1 if the file exists, 0 if it does not + */ +SEC_BOOL SecUtils_FileExists(const char* path); + +typedef struct { + char name[SEC_MAX_FILE_PATH_LEN]; + SEC_BYTE is_dir; +} Sec_LsDirEntry; + +/** + * @brief Obtain directory entries from a specified dir. + * + * @param path path of the directory to list + * @param entries pointer to the entry array. If NULL, the entries info will not be filled in, but the number + * of items will still be returned + * @param maxNumEntries The maximum number of entries to fill. + * + * @return number of directory entries in a specified dir + */ +SEC_SIZE SecUtils_LsDir(const char* path, Sec_LsDirEntry* entries, SEC_SIZE maxNumEntries); + +/** + * @brief Checks whether the specified strings ends with the other string. + */ +SEC_BYTE SecUtils_EndsWith(const char* str, const char* end); + +/** + * @brief obtain the index of the item in a list. + */ +int SecUtils_ItemIndex(const SEC_OBJECTID* items, SEC_SIZE numItems, SEC_OBJECTID item); + +/** + * @brief insert new item into the list if it does not exist. + */ +SEC_SIZE SecUtils_UpdateItemList(SEC_OBJECTID* items, SEC_SIZE maxNumItems, SEC_SIZE numItems, SEC_OBJECTID item_id); + +/** + * @brief insert new items into the list from the specified directory. + */ +SEC_SIZE SecUtils_UpdateItemListFromDir(SEC_OBJECTID* items, SEC_SIZE maxNumItems, SEC_SIZE numItems, const char* dir, + const char* ext); + +SEC_SIZE SecUtils_X509ToDerLen(X509* x509, void* mem, SEC_SIZE mem_len); + +/** + * @brief Convert the given epoch to iso formatted string. + */ +char* SecUtils_Epoch2IsoTime(SEC_SIZE epoch, char* iso_time, SEC_SIZE iso_time_size); + +/** + * @brief Convert the given iso time string to epoch value. + */ +SEC_SIZE SecUtils_IsoTime2Epoch(const char* iso_time); + +EC_KEY* SecUtils_ECCFromDERPriv(const SEC_BYTE* der, SEC_SIZE der_len); + +Sec_Result SecUtils_ECCToPubBinary(EC_KEY* ec_key, Sec_ECCRawPublicKey* binary); + +/** + * @brief Base64 encode the input string. + */ +Sec_Result SecUtils_Base64Encode(const SEC_BYTE* input, SEC_SIZE input_len, SEC_BYTE* output, SEC_SIZE max_output, + SEC_SIZE* out_len); + +/** + * @brief base64 decode the input string. + */ +Sec_Result SecUtils_Base64Decode(const SEC_BYTE* input, SEC_SIZE in_len, SEC_BYTE* output, SEC_SIZE max_output, + SEC_SIZE* out_len); + +/** + * @brief Write OpenSSL EC_KEY object into a private key binary blob + * + * The private key also contains the public key + */ +Sec_Result SecUtils_ECCToPrivBinary(EC_KEY* ec_key, Sec_ECCRawPrivateKey* binary); + +RSA* SecUtils_RSAFromDERPriv(const SEC_BYTE* der, SEC_SIZE der_len); + +Sec_Result SecUtils_RSAToDERPrivKeyInfo(RSA* rsa, SEC_BYTE* output, SEC_SIZE out_len, SEC_SIZE* written); + +Sec_Result SecUtils_WrapSymetric(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID wrappingKey, + Sec_CipherAlgorithm wrappingAlg, SEC_BYTE* iv, SEC_BYTE* payload, SEC_SIZE payloadLen, SEC_BYTE* out, + SEC_SIZE out_len, SEC_SIZE* written); + +Sec_Result SecUtils_BigNumToBuffer(const BIGNUM* bignum, SEC_BYTE* buffer, SEC_SIZE buffer_len); + +Sec_Result SecUtils_Extract_EC_KEY_X_Y(const EC_KEY* ec_key, BIGNUM** xp, BIGNUM** yp, Sec_KeyType* keyTypep); + +int SecUtils_ElGamal_Encrypt_Rand(EC_KEY* ec_key, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BYTE* output, + SEC_SIZE outputSize, BIGNUM* sender_rand); + +int SecUtils_ElGamal_Encrypt(EC_KEY* ec_key, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BYTE* output, + SEC_SIZE outputSize); + +EC_KEY* SecUtils_ECCFromPubBinary(Sec_ECCRawPublicKey* binary); + +Sec_Result SecUtils_FillKeyStoreUserHeader(Sec_ProcessorHandle* processorHandle, SecUtils_KeyStoreHeader* header, + Sec_KeyContainer container); + +SecUtils_KeyStoreHeader* SecUtils_GetKeyStoreUserHeader(void* store); + +Sec_Result SecUtils_ValidateKeyStore(Sec_ProcessorHandle* processorHandle, SEC_BOOL require_mac, void* store, + SEC_SIZE store_len); + +Sec_Result write_verification_file(Sec_ProcessorHandle* processorHandle, char* filename, SEC_BYTE* data, + SEC_SIZE data_length, SEC_BYTE* info, size_t info_length); + +Sec_Result verify_verification_file(Sec_ProcessorHandle* processorHandle, char* filename, SEC_BYTE* data, + SEC_SIZE data_length, SEC_BYTE* info, size_t info_length); + +#ifdef __cplusplus +} +#endif + +#endif // SEC_ADAPTER_UTILS_H_ diff --git a/src/sec_adapter_utils_time.c b/src/sec_adapter_utils_time.c new file mode 100644 index 0000000..d59aa5a --- /dev/null +++ b/src/sec_adapter_utils_time.c @@ -0,0 +1,48 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_adapter_utils.h" + +#define SEC_ISO_TIME_FORMAT "%Y-%m-%dT%H:%M:%S" + +char* SecUtils_Epoch2IsoTime(SEC_SIZE epoch, char* iso_time, SEC_SIZE iso_time_size) { + time_t in_time = (time_t) epoch; + struct tm ts; + memset(iso_time, 0, iso_time_size); + gmtime_r(&in_time, &ts); + if (strftime(iso_time, iso_time_size, SEC_ISO_TIME_FORMAT "Z", &ts) == 0) { + memset(iso_time, 0, iso_time_size); + } + + return iso_time; +} + +SEC_SIZE SecUtils_IsoTime2Epoch(const char* iso_time) { + struct tm _tm = {0}; + char* strptimeResult = NULL; + SEC_SIZE epoch = SEC_INVALID_EPOCH; + + strptimeResult = strptime(iso_time, SEC_ISO_TIME_FORMAT, &_tm); + if (strptimeResult == NULL || *strptimeResult != 'Z') { + SEC_LOG_ERROR("Parse error for iso time '%s'", iso_time); + return epoch; + } + + epoch = (SEC_SIZE) (mktime(&_tm)); + return epoch; +} diff --git a/test/main/cpp/bundle.cpp b/test/main/cpp/bundle.cpp new file mode 100644 index 0000000..e12a9b2 --- /dev/null +++ b/test/main/cpp/bundle.cpp @@ -0,0 +1,129 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bundle.h" +#include "sec_adapter_utils.h" +#include "test_ctx.h" + +Sec_Result testBundleProvision(SEC_OBJECTID id, Sec_StorageLoc loc, SEC_SIZE size) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector bundle = TestCtx::random(size); + + Sec_BundleHandle* bundleHandle; + if ((bundleHandle = ctx.provisionBundle(id, loc, bundle)) == nullptr) { + SEC_LOG_ERROR("TestCtx.provisionBundle failed"); + return SEC_RESULT_FAILURE; + } + + //get bundle size + SEC_SIZE written; + if (SecBundle_Export(bundleHandle, nullptr, 0, &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecBundle_Export failed"); + return SEC_RESULT_FAILURE; + } + + //export bundle + std::vector out; + out.resize(written); + if (SecBundle_Export(bundleHandle, &out[0], size, &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecBundle_Export failed"); + return SEC_RESULT_FAILURE; + } + + out.resize(written); + + if (out != bundle) { + SEC_LOG_ERROR("Exported bundle does not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testBundleProvisionNoSha(SEC_OBJECTID id) { + TestCtx ctx; + if (ctx.init("/tmp/sec_api_test_global", "/tmp/sec_api_test_app") != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector bundle = TestCtx::random(256); + + Sec_BundleHandle* bundleHandle; + if ((bundleHandle = ctx.provisionBundle(id, SEC_STORAGELOC_FILE, bundle)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionBundle failed"); + return SEC_RESULT_FAILURE; + } + + ctx.releaseBundle(bundleHandle); + + char file_name_verification[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name_verification, sizeof(file_name_verification), SEC_VERIFICATION_FILENAME_PATTERN, + "/tmp/sec_api_test_app/", id); SecUtils_RmFile(file_name_verification); + if ((bundleHandle = ctx.getBundle(id)) == nullptr) { + SEC_LOG_ERROR("ctx.getBundle failed"); + return SEC_RESULT_FAILURE; + } + + //get bundle size + SEC_SIZE written; + if (SecBundle_Export(bundleHandle, nullptr, 0, &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecBundle_Export failed"); + return SEC_RESULT_FAILURE; + } + + //export bundle + std::vector out; + out.resize(written); + if (SecBundle_Export(bundleHandle, &out[0], 256, &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecBundle_Export failed"); + return SEC_RESULT_FAILURE; + } + + out.resize(written); + + if (out != bundle) { + SEC_LOG_ERROR("Exported bundle does not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testBundleProvisionNoAppDir(SEC_OBJECTID id, SEC_SIZE size) { + TestCtx ctx; + if (ctx.init("/tmp", nullptr) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector bundle = TestCtx::random(size); + + if (SecBundle_Provision(ctx.proc(), id, SEC_STORAGELOC_FILE, static_cast(&bundle[0]), bundle.size()) == + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecBundle_Provision succeeded, but expected to fail"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} diff --git a/test/main/cpp/bundle.h b/test/main/cpp/bundle.h new file mode 100644 index 0000000..26e0c41 --- /dev/null +++ b/test/main/cpp/bundle.h @@ -0,0 +1,30 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef BUNDLE_H +#define BUNDLE_H + +#include "sec_security.h" + +Sec_Result testBundleProvision(SEC_OBJECTID id, Sec_StorageLoc loc, SEC_SIZE size); + +Sec_Result testBundleProvisionNoSha(SEC_OBJECTID id); + +Sec_Result testBundleProvisionNoAppDir(SEC_OBJECTID id, SEC_SIZE size); + +#endif // BUNDLE_H diff --git a/test/main/cpp/cert.cpp b/test/main/cpp/cert.cpp new file mode 100644 index 0000000..ba6a1dd --- /dev/null +++ b/test/main/cpp/cert.cpp @@ -0,0 +1,298 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "cert.h" +#include "sec_adapter_utils.h" +#include "test_creds.h" +#include "test_ctx.h" + +static X509* DerToX509(SEC_BYTE* der, SEC_SIZE der_len) { + BIO* bio; + X509* x509; + + bio = BIO_new_mem_buf(der, static_cast(der_len)); + x509 = d2i_X509_bio(bio, nullptr); + SEC_BIO_FREE(bio); + + if (x509 == nullptr) { + SEC_LOG_ERROR("d2i_X509_bio failed"); + } + + return x509; +} + +Sec_Result testCertProvision(SEC_OBJECTID id, TestCert cert, Sec_StorageLoc loc) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if (ctx.provisionCert(id, loc, cert) == nullptr) { + SEC_LOG_ERROR("TestCtx.provisionCert failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testCertExport(SEC_OBJECTID id, TestCert cert, Sec_StorageLoc loc) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_CertificateHandle* certificateHandle; + if ((certificateHandle = ctx.provisionCert(id, loc, cert)) == nullptr) { + SEC_LOG_ERROR("TestCtx.provisionCert failed"); + return SEC_RESULT_FAILURE; + } + + SEC_SIZE written; + if (SecCertificate_Export(certificateHandle, nullptr, 0, &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_Export failed"); + return SEC_RESULT_FAILURE; + } + + std::vector out; + out.resize(written); + if (SecCertificate_Export(certificateHandle, &out[0], out.size(), &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecBundle_Export failed"); + return SEC_RESULT_FAILURE; + } + + X509* x509 = DerToX509(&out[0], out.size()); + if (x509 == nullptr) { + SEC_LOG_ERROR("DerToX509 failed"); + return SEC_RESULT_FAILURE; + } + SEC_X509_FREE(x509); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testCertExportNoSha(SEC_OBJECTID id, TestCert cert) { + TestCtx ctx; + if (ctx.init("/tmp/sec_api_test_global", "/tmp/sec_api_test_app") != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_CertificateHandle* certificateHandle; + if ((certificateHandle = ctx.provisionCert(id, SEC_STORAGELOC_FILE, cert)) == nullptr) { + SEC_LOG_ERROR("TestCtx.provisionCert failed"); + return SEC_RESULT_FAILURE; + } + + ctx.releaseCert(certificateHandle); + + char file_name_verification[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name_verification, sizeof(file_name_verification), SEC_VERIFICATION_FILENAME_PATTERN, + "/tmp/sec_api_test_app/", id); + SecUtils_RmFile(file_name_verification); + if ((certificateHandle = ctx.getCert(id)) == nullptr) { + SEC_LOG_ERROR("ctx.getCert failed"); + return SEC_RESULT_FAILURE; + } + + SEC_SIZE written; + if (SecCertificate_Export(certificateHandle, nullptr, 0, &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_Export failed"); + return SEC_RESULT_FAILURE; + } + + std::vector out; + out.resize(written); + if (SecCertificate_Export(certificateHandle, &out[0], out.size(), &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_Export failed"); + return SEC_RESULT_FAILURE; + } + + X509* x509 = DerToX509(&out[0], out.size()); + if (x509 == nullptr) { + SEC_LOG_ERROR("_DerToX509 failed"); + return SEC_RESULT_FAILURE; + } + SEC_X509_FREE(x509); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testCertVerify(SEC_OBJECTID id_cert, TestCert cert, SEC_OBJECTID id_key, TestKey key, Sec_StorageLoc loc) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_CertificateHandle* certificateHandle; + if ((certificateHandle = ctx.provisionCert(id_cert, SEC_STORAGELOC_RAM, cert)) == nullptr) { + SEC_LOG_ERROR("TestCtx.provisionCert failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle; + if ((keyHandle = ctx.provisionKey(id_key, loc, key, TESTKC_RAW)) == nullptr) { + SEC_LOG_ERROR("TestCtx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_IsEcc(TestCreds::getKeyType(key)) != 0) { + Sec_ECCRawPublicKey pub_key; + if (SecCertificate_ExtractECCPublicKey(certificateHandle, &pub_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_ExtractECCPublicKey failed"); + return SEC_RESULT_FAILURE; + } + + if (SecCertificate_VerifyWithRawECCPublicKey(certificateHandle, &pub_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_VerifyWithRawECCPublicKey failed"); + return SEC_RESULT_FAILURE; + } + } else { + Sec_RSARawPublicKey pub_key; + if (SecCertificate_ExtractRSAPublicKey(certificateHandle, &pub_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_ExtractRSAPublicKey failed"); + return SEC_RESULT_FAILURE; + } + + if (SecCertificate_VerifyWithRawRSAPublicKey(certificateHandle, &pub_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_VerifyWithRawRSAPublicKey failed"); + return SEC_RESULT_FAILURE; + } + } + + if (SecCertificate_Verify(certificateHandle, keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_Verify failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testCertSignWithPkcs7(SEC_OBJECTID id_cert, TestCert cert, SEC_OBJECTID id_key, TestKey key, + Sec_StorageLoc loc) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_CertificateHandle* certificateHandle; + if ((certificateHandle = ctx.provisionCert(id_cert, SEC_STORAGELOC_RAM, cert)) == nullptr) { + SEC_LOG_ERROR("TestCtx.provisionCert failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle; + if ((keyHandle = ctx.provisionKey(id_key, loc, key, TESTKC_RAW)) == nullptr) { + SEC_LOG_ERROR("TestCtx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + X509* x509Cert = nullptr; + RSA* rsa; + EVP_PKEY* evp; + BIO* bio = nullptr; + BIO* out = nullptr; + PKCS7* pkcs7 = nullptr; + int length; + Sec_Result result; + std::vector input = TestCtx::random(SEC_AES_BLOCK_SIZE); + unsigned char* message = nullptr; + unsigned char* messagePtr; + + do { + rsa = SecKey_ToEngineRSAWithCert(keyHandle, certificateHandle); + evp = EVP_PKEY_new(); + if (EVP_PKEY_assign(evp, EVP_PKEY_RSA, rsa) != 1) { + SEC_LOG_ERROR("EVP_PKEY_assign failed"); + result = SEC_RESULT_FAILURE; + break; + } + + rsa = nullptr; + + x509Cert = SecCertificate_ToX509(certificateHandle); + if (x509Cert == nullptr) { + SEC_LOG_ERROR("SecCertificate_ToX509 failed"); + result = SEC_RESULT_FAILURE; + break; + } + + /* signed message */ + bio = BIO_new_mem_buf(input.data(), static_cast(input.size())); + if (bio == nullptr) { + SEC_LOG_ERROR("BIO_new_mem_buf failed"); + result = SEC_RESULT_FAILURE; + break; + } + + pkcs7 = PKCS7_sign(x509Cert, evp, nullptr, bio, PKCS7_BINARY); + if (pkcs7 == nullptr) { + SEC_LOG_ERROR("PKCS7_sign failed: %s", ERR_error_string(ERR_get_error(), NULL)); + result = SEC_RESULT_FAILURE; + break; + } + + length = i2d_PKCS7(pkcs7, nullptr); + if (length < 0) { + SEC_LOG_ERROR("i2d_PKCS7 failed"); + result = SEC_RESULT_FAILURE; + break; + } + + message = static_cast(malloc(length)); + messagePtr = message; + length = i2d_PKCS7(pkcs7, &messagePtr); + if (length < 0) { + SEC_LOG_ERROR("i2d_PKCS7 failed"); + result = SEC_RESULT_FAILURE; + break; + } + + out = BIO_new(BIO_s_mem()); + if (PKCS7_verify(pkcs7, nullptr, nullptr, nullptr, out, PKCS7_NOVERIFY) != 1) { + SEC_LOG_ERROR("PKCS7_verify failed"); + result = SEC_RESULT_FAILURE; + break; + } + + BUF_MEM* bptr; + BIO_ctrl(out, BIO_C_GET_BUF_MEM_PTR, 0, &bptr); + if (memcmp(bptr->data, input.data(), input.size()) != 0) { + SEC_LOG_ERROR("data mismatch"); + result = SEC_RESULT_FAILURE; + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + SEC_RSA_FREE(rsa); + SEC_EVPPKEY_FREE(evp); + SEC_X509_FREE(x509Cert); + SEC_BIO_FREE(bio); + SEC_BIO_FREE(out); + if (pkcs7 != nullptr) + PKCS7_free(pkcs7); + + if (message != nullptr) + free(message); + return result; +} diff --git a/test/main/cpp/cert.h b/test/main/cpp/cert.h new file mode 100644 index 0000000..eab03cc --- /dev/null +++ b/test/main/cpp/cert.h @@ -0,0 +1,36 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef CERT_H +#define CERT_H + +#include "sec_security.h" +#include "test_creds.h" + +Sec_Result testCertProvision(SEC_OBJECTID id, TestCert cert, Sec_StorageLoc loc); + +Sec_Result testCertExport(SEC_OBJECTID id, TestCert cert, Sec_StorageLoc loc); + +Sec_Result testCertExportNoSha(SEC_OBJECTID id, TestCert cert); + +Sec_Result testCertVerify(SEC_OBJECTID id_cert, TestCert cert, SEC_OBJECTID id_key, TestKey key, Sec_StorageLoc loc); + +Sec_Result testCertSignWithPkcs7(SEC_OBJECTID id_cert, TestCert cert, SEC_OBJECTID id_key, TestKey key, + Sec_StorageLoc loc); + +#endif // CERT_H diff --git a/test/main/cpp/cipher.cpp b/test/main/cpp/cipher.cpp new file mode 100644 index 0000000..c82c83b --- /dev/null +++ b/test/main/cpp/cipher.cpp @@ -0,0 +1,1373 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "cipher.h" +#include "digest.h" +#include "test_ctx.h" +#include +#include +#include + +#define BUFFER_SIZE 4096 +#define SUBSAMPLE_SIZE 256 + +static std::vector opensslAesCbc(TestKey key, Sec_CipherMode mode, bool padding, SEC_BYTE* iv, + const std::vector& input) { + + std::vector openssl_key = TestCreds::asOpenSslAes(key); + if (openssl_key.empty()) { + SEC_LOG_ERROR("TestCreds::asOpenSslAes failed"); + return {}; + } + const EVP_CIPHER* evp_cipher; + if (openssl_key.size() == 16) + evp_cipher = EVP_aes_128_cbc(); + else + evp_cipher = EVP_aes_256_cbc(); + + std::shared_ptr p_evp_ctx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); + + if (EVP_CipherInit_ex(p_evp_ctx.get(), evp_cipher, nullptr, nullptr, nullptr, + (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM) ? 1 : 0) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherInit failed"); + return {}; + } + + if (EVP_CIPHER_CTX_set_padding(p_evp_ctx.get(), padding ? 1 : 0) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CIPHER_CTX_set_padding failed"); + return {}; + } + + if (EVP_CipherInit_ex(p_evp_ctx.get(), nullptr, nullptr, &openssl_key[0], iv, + (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM) ? 1 : 0) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherInit failed"); + return {}; + } + + std::vector output; + output.resize(input.size() + SEC_AES_BLOCK_SIZE); + + SEC_SIZE written = 0; + int outlen = 0; + + if (EVP_CipherUpdate(p_evp_ctx.get(), &output[0], &outlen, &input[0], static_cast(input.size())) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherUpdate failed"); + return {}; + } + written += outlen; + outlen = 0; + + if (EVP_CipherFinal_ex(p_evp_ctx.get(), &output[written], &outlen) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherFinal failed"); + return {}; + } + + written += outlen; + output.resize(written); + return output; +} + +std::vector opensslAesEcb(const std::vector& openssl_key, Sec_CipherMode mode, SEC_BOOL padding, + SEC_BYTE* iv, const std::vector& input) { + std::vector output; + const EVP_CIPHER* evp_cipher; + + if (openssl_key.size() == 16) { + evp_cipher = EVP_aes_128_ecb(); + } else { + evp_cipher = EVP_aes_256_ecb(); + } + + std::shared_ptr p_evp_ctx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); + if (EVP_CipherInit_ex(p_evp_ctx.get(), evp_cipher, nullptr, nullptr, nullptr, + (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM) ? 1 : 0) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherInit failed"); + return {}; + } + + if (EVP_CIPHER_CTX_set_padding(p_evp_ctx.get(), padding) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CIPHER_CTX_set_padding failed"); + return {}; + } + + if (EVP_CipherInit_ex(p_evp_ctx.get(), nullptr, nullptr, &openssl_key[0], iv, + (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM) ? 1 : 0) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherInit failed"); + return {}; + } + + output.resize(input.size() + SEC_AES_BLOCK_SIZE); + + SEC_SIZE written = 0; + int outlen = 0; + if (EVP_CipherUpdate(p_evp_ctx.get(), &output[0], &outlen, &input[0], static_cast(input.size())) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherUpdate failed"); + return {}; + } + written += outlen; + outlen = 0; + + if (EVP_CipherFinal_ex(p_evp_ctx.get(), &output[written], &outlen) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherFinal failed"); + return {}; + } + + written += outlen; + output.resize(written); + + return output; +} + +std::vector opensslAesEcb(TestKey key, Sec_CipherMode mode, bool padding, SEC_BYTE* iv, + const std::vector& input) { + std::vector output; + std::vector openssl_key = TestCreds::asOpenSslAes(key); + const EVP_CIPHER* evp_cipher; + + if (openssl_key.size() == 16) { + evp_cipher = EVP_aes_128_ecb(); + } else { + evp_cipher = EVP_aes_256_ecb(); + } + + std::shared_ptr p_evp_ctx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); + if (EVP_CipherInit_ex(p_evp_ctx.get(), evp_cipher, nullptr, nullptr, nullptr, + (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM) ? 1 : 0) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherInit failed"); + return {}; + } + + if (EVP_CIPHER_CTX_set_padding(p_evp_ctx.get(), padding ? 1 : 0) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CIPHER_CTX_set_padding failed"); + return {}; + } + + if (EVP_CipherInit_ex(p_evp_ctx.get(), nullptr, nullptr, &openssl_key[0], iv, + (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM) ? 1 : 0) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherInit failed"); + return {}; + } + + output.resize(input.size() + SEC_AES_BLOCK_SIZE); + + SEC_SIZE written = 0; + int outlen = 0; + if (EVP_CipherUpdate(p_evp_ctx.get(), &output[0], &outlen, &input[0], static_cast(input.size())) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherUpdate failed"); + return {}; + } + written += outlen; + outlen = 0; + + if (EVP_CipherFinal_ex(p_evp_ctx.get(), &output[written], &outlen) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherFinal failed"); + return {}; + } + + written += outlen; + output.resize(written); + return output; +} + +static std::vector opensslAesCtr(TestKey key, Sec_CipherMode mode, SEC_BYTE* iv, + const std::vector& input) { + + std::vector openssl_key = TestCreds::asOpenSslAes(key); + SEC_BYTE ivToUse[SEC_AES_BLOCK_SIZE]; + memcpy(ivToUse, iv, SEC_AES_BLOCK_SIZE); + + std::vector output; + output.resize(input.size()); + + std::shared_ptr evp_ctx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); + EVP_CIPHER_CTX_init(evp_ctx.get()); + if (EVP_CipherInit_ex(evp_ctx.get(), (openssl_key.size() == 16) ? EVP_aes_128_ctr() : EVP_aes_256_ctr(), nullptr, + &openssl_key[0], ivToUse, + (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM) ? 1 : 0) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherInit_ex failed"); + return {}; + } + + int out_len = 0; + if (EVP_CipherUpdate(evp_ctx.get(), &output[0], &out_len, &input[0], static_cast(input.size())) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherUpdate failed"); + return {}; + } + + if (EVP_CipherFinal_ex(evp_ctx.get(), &output[out_len], &out_len) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherFinal failed"); + return {}; + } + + return output; +} + +std::vector opensslAesGcm(const std::vector& key, Sec_CipherMode mode, SEC_BYTE* iv, SEC_BYTE* aad, + SEC_SIZE aad_length, SEC_BYTE* tag, SEC_SIZE tag_length, const std::vector& input) { + + SEC_BYTE ivToUse[SEC_AES_BLOCK_SIZE]; + memcpy(ivToUse, iv, SEC_AES_BLOCK_SIZE); + + std::vector output; + output.resize(input.size()); + + std::shared_ptr evp_ctx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); + + // init cipher + const EVP_CIPHER* cipher = (key.size() == 16) ? EVP_aes_128_gcm() : EVP_aes_256_gcm(); + if (EVP_EncryptInit_ex(evp_ctx.get(), cipher, nullptr, nullptr, nullptr) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_EncryptInit_ex failed"); + return {}; + } + + // set iv length + if (EVP_CIPHER_CTX_ctrl(evp_ctx.get(), EVP_CTRL_GCM_SET_IVLEN, 12, nullptr) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CIPHER_CTX_ctrl failed"); + return {}; + } + + // init key and iv + if (EVP_EncryptInit_ex(evp_ctx.get(), cipher, nullptr, key.data(), iv) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_EncryptInit_ex failed"); + return {}; + } + + // turn off padding + if (EVP_CIPHER_CTX_set_padding(evp_ctx.get(), 0) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CIPHER_CTX_set_padding failed"); + return {}; + } + + int out_length = static_cast(input.size()); + if (aad != nullptr) { + if (EVP_EncryptUpdate(evp_ctx.get(), nullptr, &out_length, aad, static_cast(aad_length)) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_EncryptUpdate failed"); + return {}; + } + } + + if (EVP_CipherUpdate(evp_ctx.get(), &output[0], &out_length, &input[0], static_cast(input.size())) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EVP_CipherUpdate failed"); + return {}; + } + + if (tag != nullptr) { + // get tag + if (EVP_EncryptFinal_ex(evp_ctx.get(), nullptr, &out_length) != 1) { + SEC_LOG_ERROR("EVP_EncryptFinal_ex failed"); + return {}; + } + + uint8_t local_tag[16]; + if (EVP_CIPHER_CTX_ctrl(evp_ctx.get(), EVP_CTRL_GCM_GET_TAG, sizeof(local_tag), local_tag) != 1) { + return {}; + } + + memcpy(tag, local_tag, tag_length); + } + + return output; +} + +static std::vector opensslRsaCrypt(TestKey key, Sec_CipherAlgorithm algorithm, Sec_CipherMode mode, + const std::vector& input) { + + std::shared_ptr rsa(TestCreds::asOpenSslRsa(key), RSA_free); + if (rsa == nullptr) { + SEC_LOG_ERROR("TestCreds::asOpenSslRsa failed"); + return {}; + } + + int padding; + if (algorithm == SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING) { + padding = RSA_PKCS1_PADDING; + } else { + padding = RSA_PKCS1_OAEP_PADDING; + } + + int openssl_res; + std::vector output; + output.resize(RSA_size(rsa.get())); + + if (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM) { + openssl_res = RSA_public_encrypt(static_cast(input.size()), &input[0], &output[0], rsa.get(), padding); + } else { + openssl_res = RSA_private_decrypt(static_cast(input.size()), &input[0], &output[0], rsa.get(), padding); + } + + if (openssl_res < 0) { + SEC_LOG_ERROR("%s", ERR_error_string(ERR_get_error(), nullptr)); + return {}; + } + + output.resize(openssl_res); + + return output; +} + +std::vector cipherOpenSSL(TestKey key, Sec_CipherAlgorithm alg, Sec_CipherMode mode, SEC_BYTE* iv, + const std::vector& input) { + + switch (alg) { + case SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING: + case SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING: + return opensslAesCbc(key, mode, alg == SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING, iv, input); + + case SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING: + case SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING: + return opensslAesEcb(key, mode, alg == SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING, iv, input); + + case SEC_CIPHERALGORITHM_AES_CTR: + return opensslAesCtr(key, mode, iv, input); + + case SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING: + case SEC_CIPHERALGORITHM_RSA_OAEP_PADDING: + return opensslRsaCrypt(key, alg, mode, input); + + default: + break; + } + + SEC_LOG_ERROR("Unimplemented"); + return {}; +} + +std::vector cipherSecApi(TestCtx* ctx, Sec_KeyHandle* keyHandle, Sec_CipherAlgorithm alg, + Sec_CipherMode mode, const std::vector& iv, const std::vector& input, + const std::vector& inputSizes, SEC_BOOL inplace) { + + std::vector output = input; + output.resize(input.size() + BUFFER_SIZE); + + SEC_SIZE inputProcessed = 0; + SEC_SIZE outputWritten = 0; + SEC_SIZE written = 0; + + Sec_CipherHandle* cipherHandle = ctx->acquireCipher(alg, mode, keyHandle, const_cast(&iv[0])); + if (cipherHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireCipher failed"); + return {}; + } + + for (unsigned int i = 0; i < inputSizes.size() - 1; ++i) { + if (inputSizes[i] > 0) { + Sec_Result result = SecCipher_Process(cipherHandle, + const_cast(inplace == SEC_TRUE ? &output[inputProcessed] : &input[inputProcessed]), + inputSizes[i], SEC_FALSE, &output[outputWritten], output.size() - outputWritten, &written); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_Process failed"); + return {}; + } + + outputWritten += written; + } + + inputProcessed += inputSizes[i]; + } + + //last input + Sec_Result result = SecCipher_Process(cipherHandle, + const_cast(inplace == SEC_TRUE ? &output[inputProcessed] : &input[inputProcessed]), + input.size() - inputProcessed, SEC_TRUE, &output[outputWritten], output.size() - outputWritten, &written); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_Process failed"); + return {}; + } + + outputWritten += written; + output.resize(outputWritten); + ctx->releaseCipher(cipherHandle); + return output; +} + +std::vector cipherSecApiSingle(TestCtx* ctx, Sec_KeyHandle* keyHandle, Sec_CipherAlgorithm alg, + Sec_CipherMode mode, const std::vector& iv, const std::vector& input, SEC_BOOL inplace) { + + std::vector output = input; + output.resize(input.size() + BUFFER_SIZE); + + SEC_SIZE written = 0; + + Sec_CipherHandle* cipherHandle = ctx->acquireCipher(alg, mode, keyHandle, const_cast(&iv[0])); + if (cipherHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireCipher failed"); + return {}; + } + + if (SecCipher_Process(cipherHandle, const_cast(inplace == SEC_TRUE ? &output[0] : &input[0]), + input.size(), SEC_TRUE, &output[0], output.size(), &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_Process failed"); + return {}; + } + + output.resize(written); + + ctx->releaseCipher(cipherHandle); + + return output; +} + +std::vector cipherSecApiSingle(TestCtx* ctx, Sec_CipherHandle* cipherHandle, const std::vector& iv, + const std::vector& input, SEC_BOOL inplace) { + + std::vector output = input; + output.resize(input.size() + BUFFER_SIZE); + + SEC_SIZE written = 0; + + if (!iv.empty()) { + if (SecCipher_UpdateIV(cipherHandle, const_cast(&iv[0])) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_UpdateIV failed"); + return {}; + } + } + + Sec_Result result = SecCipher_Process(cipherHandle, + const_cast(inplace == SEC_TRUE ? &output[0] : &input[0]), input.size(), SEC_FALSE, &output[0], + output.size(), &written); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_Process failed"); + return {}; + } + + output.resize(written); + + return output; +} + +Sec_Result testCipherSingle(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_CipherAlgorithm alg, + Sec_CipherMode mode, SEC_SIZE inputSize, SEC_BOOL inplace) { + + std::vector inputSizes; + inputSizes.resize(1); + inputSizes[0] = inputSize; + + return testCipherMult(id, key, kc, loc, alg, mode, inputSizes, inplace); +} + +Sec_Result testCtrRollover(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_CipherMode mode, + SEC_SIZE inputSize, SEC_BOOL inplace) { + + std::vector inputSizes; + inputSizes.resize(3); + inputSizes[0] = 16; + inputSize -= inputSizes[0]; + inputSizes[1] = 16; + inputSize -= inputSizes[1]; + inputSizes[2] = inputSize; + + return testCipherMult(id, key, kc, loc, SEC_CIPHERALGORITHM_AES_CTR, mode, inputSizes, inplace, SEC_TRUE); +} + +Sec_Result testCipherSingle(SEC_OBJECTID id, TestKey pub, TestKey priv, TestKc kc, Sec_StorageLoc loc, + Sec_CipherAlgorithm alg, Sec_CipherMode mode, SEC_SIZE inputSize, SEC_BOOL inplace) { + + std::vector inputSizes; + inputSizes.resize(1); + inputSizes[0] = inputSize; + + return testCipherMult(id, pub, priv, kc, loc, alg, mode, inputSizes, inplace); +} + +Sec_Result testCipherMult(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_CipherAlgorithm alg, + Sec_CipherMode mode, const std::vector& inputSizes, SEC_BOOL inplace, + SEC_BOOL testCtrRollover) { + + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle; + if ((keyHandle = ctx.provisionKey(id, loc, key, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + std::vector openssl_key = TestCreds::asOpenSslAes(key); + TestCtx::printHex("key", openssl_key); + + //gen iv + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + if (alg == SEC_CIPHERALGORITHM_AES_CTR && testCtrRollover == SEC_TRUE) { + //set iv to rollover + memset(&iv[8], 0xff, 8); + } + + TestCtx::printHex("iv", iv); + + //mode + bool testEncrypt = (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM); + + //gen clear input + std::vector clear = TestCtx::random(TestCtx::coalesceInputSizes(inputSizes)); + TestCtx::printHex("clear", clear); + + //encrypt + std::vector encrypted; + if (testEncrypt) { + encrypted = cipherSecApi(&ctx, keyHandle, alg, SEC_CIPHERMODE_ENCRYPT, iv, clear, inputSizes, inplace); + } else { + //use openssl to encrypt + encrypted = cipherOpenSSL(key, alg, SEC_CIPHERMODE_ENCRYPT, &iv[0], clear); + } + + TestCtx::printHex("encrypted", encrypted); + + //decrypt + std::vector decrypted; + if (testEncrypt) { + //use openssl to decrypt + decrypted = cipherOpenSSL(key, alg, SEC_CIPHERMODE_DECRYPT, &iv[0], encrypted); + } else { + //use sec api to decrypt + decrypted = cipherSecApi(&ctx, keyHandle, alg, SEC_CIPHERMODE_DECRYPT, iv, encrypted, inputSizes, inplace); + } + + TestCtx::printHex("decrypted", decrypted); + + //check if results match + if (clear != decrypted) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testCipherMult(SEC_OBJECTID id, TestKey pub, TestKey priv, TestKc kc, Sec_StorageLoc loc, + Sec_CipherAlgorithm alg, Sec_CipherMode mode, const std::vector& inputSizes, SEC_BOOL inplace) { + + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + //mode + bool testEncrypt = (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM); + + Sec_KeyHandle* keyHandle; + if (testEncrypt) { + if ((keyHandle = ctx.provisionKey(id, loc, pub, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + } else { + if ((keyHandle = ctx.provisionKey(id, loc, priv, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + } + + //gen iv + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + TestCtx::printHex("iv", iv); + + //gen clear input + std::vector clear = TestCtx::random(TestCtx::coalesceInputSizes(inputSizes)); + TestCtx::printHex("clear", clear); + + //encrypt + std::vector encrypted; + if (testEncrypt) { + encrypted = cipherSecApi(&ctx, keyHandle, alg, SEC_CIPHERMODE_ENCRYPT, iv, clear, inputSizes, inplace); + } else { + //use openssl to encrypt + encrypted = cipherOpenSSL(pub, alg, SEC_CIPHERMODE_ENCRYPT, &iv[0], clear); + } + + TestCtx::printHex("encrypted", encrypted); + + //decrypt + std::vector decrypted; + if (testEncrypt) { + //use openssl to decrypt + decrypted = cipherOpenSSL(priv, alg, SEC_CIPHERMODE_DECRYPT, &iv[0], encrypted); + } else { + //use sec api to decrypt + decrypted = cipherSecApi(&ctx, keyHandle, alg, SEC_CIPHERMODE_DECRYPT, iv, encrypted, inputSizes, inplace); + } + + TestCtx::printHex("decrypted", decrypted); + + //check if results match + if (clear != decrypted) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result cipherEncDecSingle(TestCtx* ctx, SEC_OBJECTID id, Sec_CipherAlgorithm alg, SEC_SIZE inputSize, + SEC_BOOL inplace) { + + std::vector inputSizes; + inputSizes.resize(1); + inputSizes[0] = inputSize; + + Sec_Result result = cipherEncDecMult(ctx, id, alg, inputSizes, inplace); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecMult failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result cipherEncDecSingle(TestCtx* ctx, SEC_OBJECTID id_pub, SEC_OBJECTID id_priv, Sec_CipherAlgorithm alg, + SEC_SIZE inputSize, SEC_BOOL inplace) { + + std::vector inputSizes; + inputSizes.resize(1); + inputSizes[0] = inputSize; + + Sec_Result result = cipherEncDecMult(ctx, id_pub, id_priv, alg, inputSizes, inplace); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecMult failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result cipherEncDecMult(TestCtx* ctx, SEC_OBJECTID id, Sec_CipherAlgorithm alg, + const std::vector& inputSizes, SEC_BOOL inplace) { + + //gen iv + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + TestCtx::printHex("iv", iv); + + //gen clear input + std::vector clear = TestCtx::random(TestCtx::coalesceInputSizes(inputSizes)); + TestCtx::printHex("clear", clear); + + //encrypt + std::vector encrypted = cipherSecApi(ctx, ctx->getKey(id), alg, SEC_CIPHERMODE_ENCRYPT, iv, clear, + inputSizes, inplace); + if (encrypted.empty()) { + SEC_LOG_ERROR("CipherSecApi failed"); + return SEC_RESULT_FAILURE; + } + TestCtx::printHex("encrypted", encrypted); + + //decrypt + std::vector decrypted = cipherSecApi(ctx, ctx->getKey(id), alg, SEC_CIPHERMODE_DECRYPT, iv, encrypted, + inputSizes, inplace); + if (decrypted.empty()) { + SEC_LOG_ERROR("CipherSecApi failed"); + return SEC_RESULT_FAILURE; + } + + TestCtx::printHex("decrypted", decrypted); + + //check if results match + if (clear != decrypted) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result cipherEncDecMult(TestCtx* ctx, SEC_OBJECTID id_pub, SEC_OBJECTID id_priv, Sec_CipherAlgorithm alg, + const std::vector& inputSizes, SEC_BOOL inplace) { + + //gen clear input + std::vector clear = TestCtx::random(TestCtx::coalesceInputSizes(inputSizes)); + TestCtx::printHex("clear", clear); + + //encrypt + std::vector encrypted = cipherSecApi(ctx, ctx->getKey(id_pub), alg, SEC_CIPHERMODE_ENCRYPT, + {}, clear, inputSizes, inplace); + if (encrypted.empty()) { + SEC_LOG_ERROR("CipherSecApi failed"); + return SEC_RESULT_FAILURE; + } + + TestCtx::printHex("encrypted", encrypted); + + //decrypt + std::vector decrypted = cipherSecApi(ctx, ctx->getKey(id_priv), alg, SEC_CIPHERMODE_DECRYPT, + {}, encrypted, inputSizes, inplace); + if (decrypted.empty()) { + SEC_LOG_ERROR("CipherSecApi failed"); + return SEC_RESULT_FAILURE; + } + + TestCtx::printHex("decrypted", decrypted); + + //check if results match + if (clear != decrypted) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testCipherBandwidth(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_CipherAlgorithm alg, + Sec_CipherMode mode, SEC_SIZE inputSize, SEC_SIZE intervalS) { + + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle; + if ((keyHandle = ctx.provisionKey(id, loc, key, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //gen iv + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + TestCtx::printHex("iv", iv); + + //mode + bool testEncrypt = (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM); + + //gen clear input + std::vector clear = TestCtx::random(inputSize); + TestCtx::printHex("clear", clear); + + //encrypt + std::vector encrypted; + time_t start_t = 0; + time_t end_t = 0; + int loops = 0; + if (testEncrypt) { + start_t = time(nullptr); + end_t = start_t; + + while ((end_t - start_t) < static_cast(intervalS)) { + encrypted = cipherSecApiSingle(&ctx, keyHandle, alg, SEC_CIPHERMODE_ENCRYPT, iv, clear, SEC_FALSE); + ++loops; + end_t = time(nullptr); + } + } else { + //use openssl to encrypt + encrypted = cipherOpenSSL(key, alg, SEC_CIPHERMODE_ENCRYPT, &iv[0], clear); + } + + TestCtx::printHex("encrypted", encrypted); + + //decrypt + std::vector decrypted; + if (testEncrypt) { + //use openssl to decrypt + decrypted = cipherOpenSSL(key, alg, SEC_CIPHERMODE_DECRYPT, &iv[0], encrypted); + } else { + start_t = time(nullptr); + end_t = start_t; + + while ((end_t - start_t) < static_cast(intervalS)) { + decrypted = cipherSecApiSingle(&ctx, keyHandle, alg, SEC_CIPHERMODE_DECRYPT, iv, encrypted, SEC_FALSE); + ++loops; + end_t = time(nullptr); + } + } + + TestCtx::printHex("decrypted", decrypted); + + //check if results match + if (clear != decrypted) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + //print timing data + SEC_PRINT("Data processed: %d MB\n", (inputSize * loops) / (1024 * 1024)); + SEC_PRINT("Time elapsed: %d s\n", end_t - start_t); + if (end_t != start_t) { + SEC_PRINT("Bandwidth: %d MB/s\n", + ((inputSize * loops) / (1024 * 1024)) / (end_t - start_t)); + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testCipherBandwidthSingleCipher(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, + Sec_CipherAlgorithm alg, Sec_CipherMode mode, SEC_SIZE inputSize, + SEC_SIZE intervalS) { + + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle; + if ((keyHandle = ctx.provisionKey(id, loc, key, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //gen iv + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + TestCtx::printHex("iv", iv); + + //mode + bool testEncrypt = (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM); + + //gen clear input + std::vector clear = TestCtx::random(inputSize); + TestCtx::printHex("clear", clear); + + //encrypt + std::vector encrypted; + time_t start_t = 0; + time_t end_t = 0; + int loops = 0; + if (testEncrypt) { + start_t = time(nullptr); + end_t = start_t; + + Sec_CipherHandle* cipherHandle = ctx.acquireCipher(alg, mode, keyHandle, &iv[0]); + if (cipherHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireCipher failed"); + return SEC_RESULT_FAILURE; + } + + while ((end_t - start_t) < static_cast(intervalS)) { + encrypted = cipherSecApiSingle(&ctx, cipherHandle, iv, clear, SEC_FALSE); + ++loops; + end_t = time(nullptr); + } + } else { + //use openssl to encrypt + encrypted = cipherOpenSSL(key, alg, SEC_CIPHERMODE_ENCRYPT, &iv[0], clear); + } + + //decrypt + std::vector decrypted; + if (testEncrypt) { + //use openssl to decrypt + decrypted = cipherOpenSSL(key, alg, SEC_CIPHERMODE_DECRYPT, &iv[0], encrypted); + } else { + start_t = time(nullptr); + end_t = start_t; + + Sec_CipherHandle* cipherHandle = ctx.acquireCipher(alg, mode, keyHandle, &iv[0]); + if (cipherHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireCipher failed"); + return SEC_RESULT_FAILURE; + } + + while ((end_t - start_t) < static_cast(intervalS)) { + decrypted = cipherSecApiSingle(&ctx, cipherHandle, iv, encrypted, SEC_FALSE); + ++loops; + end_t = time(nullptr); + } + } + + //print timing data + SEC_PRINT("Data processed: %d MB\n", (inputSize * loops) / (1024 * 1024)); + SEC_PRINT("Time elapsed: %d s\n", end_t - start_t); + if (end_t != start_t) { + SEC_PRINT("Bandwidth: %d MB/s\n", + ((inputSize * loops) / (1024 * 1024)) / (end_t - start_t)); + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testCipherUpdateIV(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_CipherAlgorithm alg, + Sec_CipherMode mode, SEC_SIZE inputSize, SEC_BOOL inplace) { + + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle; + if ((keyHandle = ctx.provisionKey(id, loc, key, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //gen ivs + std::vector iv1 = TestCtx::random(SEC_AES_BLOCK_SIZE); + TestCtx::printHex("iv1", iv1); + std::vector iv2 = TestCtx::random(SEC_AES_BLOCK_SIZE); + TestCtx::printHex("iv2", iv2); + + //mode + bool testEncrypt = (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM); + + //gen clear input + std::vector clear = TestCtx::random(inputSize); + TestCtx::printHex("clear", clear); + + //encrypt + std::vector encrypted1; + std::vector encrypted2; + if (testEncrypt) { + Sec_CipherHandle* cipherHandle = ctx.acquireCipher(alg, SEC_CIPHERMODE_ENCRYPT, keyHandle, &iv1[0]); + if (cipherHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireCipher failed"); + return SEC_RESULT_FAILURE; + } + + encrypted1 = cipherSecApiSingle(&ctx, cipherHandle, iv1, clear, SEC_FALSE); + if (encrypted1.empty()) { + SEC_LOG_ERROR("CipherSecApiSingle failed"); + return SEC_RESULT_FAILURE; + } + encrypted2 = cipherSecApiSingle(&ctx, cipherHandle, iv2, clear, SEC_FALSE); + if (encrypted2.empty()) { + SEC_LOG_ERROR("CipherSecApiSingle failed"); + return SEC_RESULT_FAILURE; + } + } else { + //use openssl to encrypt + encrypted1 = cipherOpenSSL(key, alg, SEC_CIPHERMODE_ENCRYPT, &iv1[0], clear); + if (encrypted1.empty()) { + SEC_LOG_ERROR("CipherOpenSSL failed"); + return SEC_RESULT_FAILURE; + } + encrypted2 = cipherOpenSSL(key, alg, SEC_CIPHERMODE_ENCRYPT, &iv2[0], clear); + if (encrypted2.empty()) { + SEC_LOG_ERROR("CipherOpenSSL failed"); + return SEC_RESULT_FAILURE; + } + } + + TestCtx::printHex("encrypted1", encrypted1); + TestCtx::printHex("encrypted2", encrypted2); + + //decrypt + std::vector decrypted1; + std::vector decrypted2; + if (testEncrypt) { + //use openssl to decrypt + decrypted1 = cipherOpenSSL(key, alg, SEC_CIPHERMODE_DECRYPT, &iv1[0], encrypted1); + if (decrypted1.empty()) { + SEC_LOG_ERROR("CipherOpenSSL failed"); + return SEC_RESULT_FAILURE; + } + decrypted2 = cipherOpenSSL(key, alg, SEC_CIPHERMODE_DECRYPT, &iv2[0], encrypted2); + if (decrypted2.empty()) { + SEC_LOG_ERROR("CipherOpenSSL failed"); + return SEC_RESULT_FAILURE; + } + } else { + //use sec api to decrypt + Sec_CipherHandle* cipherHandle = ctx.acquireCipher(alg, SEC_CIPHERMODE_DECRYPT, keyHandle, &iv1[0]); + if (cipherHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireCipher failed"); + return SEC_RESULT_FAILURE; + } + + decrypted1 = cipherSecApiSingle(&ctx, cipherHandle, iv1, encrypted1, SEC_FALSE); + if (decrypted1.empty()) { + SEC_LOG_ERROR("CipherSecApiSingle failed"); + return SEC_RESULT_FAILURE; + } + decrypted2 = cipherSecApiSingle(&ctx, cipherHandle, iv2, encrypted2, SEC_FALSE); + if (decrypted2.empty()) { + SEC_LOG_ERROR("CipherSecApiSingle failed"); + return SEC_RESULT_FAILURE; + } + } + + TestCtx::printHex("decrypted1", decrypted1); + TestCtx::printHex("decrypted2", decrypted2); + + //check if results match + if (clear != decrypted1 || clear != decrypted2 || encrypted1 == encrypted2) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +std::vector cipherSecApiCtrSubBlock(TestCtx* ctx, Sec_KeyHandle* keyHandle, Sec_CipherMode mode, + const std::vector& iv, const std::vector& input, SEC_BOOL inplace) { + + std::vector output = input; + output.resize(input.size() + BUFFER_SIZE); + + SEC_SIZE inputProcessed = 0; + SEC_SIZE outputWritten = 0; + SEC_SIZE written = 0; + + Sec_CipherHandle* cipherHandle = ctx->acquireCipher(SEC_CIPHERALGORITHM_AES_CTR, mode, keyHandle, + const_cast(&iv[0])); + if (cipherHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireCipher failed"); + return {}; + } + + //calculate the offset and make sure it is not on the SEC_AES_BLOCK_SIZE boundary + SEC_SIZE split_offset = input.size() / 2; + if (split_offset % SEC_AES_BLOCK_SIZE == 0) { + split_offset -= 1; + } + + SEC_PRINT("init ctr: %d\n", Sec_BEBytesToUint64(const_cast(&iv[8]))); + uint64_t init_counter = Sec_BEBytesToUint64(const_cast(&iv[8])); + + if (SecCipher_Process(cipherHandle, const_cast(inplace == SEC_TRUE ? &output[0] : &input[0]), + split_offset, SEC_FALSE, &output[0], output.size(), &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_Process failed"); + return {}; + } + + outputWritten += written; + inputProcessed += split_offset; + + //set the iv + uint64_t counter; + counter = init_counter + split_offset / SEC_AES_BLOCK_SIZE; + Sec_Uint64ToBEBytes(counter, const_cast(&iv[8])); + + SEC_PRINT("updated ctr: %d\n", Sec_BEBytesToUint64(const_cast(&iv[8]))); + + /* TODO + if (SecCipher_UpdateIV(cipherHandle, (SEC_BYTE*) &iv[0]) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_UpdateIV failed"); + return {}; + } + */ + + //last input + Sec_Result result = SecCipher_ProcessCtrWithDataShift(cipherHandle, + const_cast(inplace == SEC_TRUE ? &output[inputProcessed] : &input[inputProcessed]), + input.size() - inputProcessed, &output[outputWritten], output.size() - outputWritten, &written, + split_offset % SEC_AES_BLOCK_SIZE); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_Process failed"); + return {}; + } + + outputWritten += written; + + output.resize(outputWritten); + + ctx->releaseCipher(cipherHandle); + + return output; +} + +Sec_Result testProcessCtrWithDataShift(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_CipherMode mode, + SEC_BOOL inplace) { +#if 0 // Feature not implemented in SecApi 2 Adapter + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle *keyHandle = nullptr; + if ((keyHandle = ctx.provisionKey(id, loc, key, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //gen iv + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + TestCtx::printHex("iv", iv); + + /* TODO + //set the counter to ff to test rollover + memset(&iv[8], 0xff, 8); + */ + + //mode + SEC_BOOL testEncrypt = (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM); + + //gen clear input + std::vector clear = TestCtx::random(SEC_AES_BLOCK_SIZE * 3); + TestCtx::printHex("clear", clear); + + //encrypt + std::vector encrypted; + std::vector ivCopy = iv; + if (testEncrypt) { + encrypted = cipherSecApiCtrSubBlock(&ctx, keyHandle, SEC_CIPHERMODE_ENCRYPT, ivCopy, clear, inplace); + } else { + //use openssl to encrypt + encrypted = cipherOpenSSL(key, SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_ENCRYPT, &ivCopy[0], clear); + } + + TestCtx::printHex("encrypted", encrypted); + TestCtx::printHex("iv", iv); + + //decrypt + std::vector decrypted; + if (testEncrypt) { + //use openssl to decrypt + decrypted = cipherOpenSSL(key, SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_DECRYPT, &iv[0], encrypted); + } else { + //use sec api to decrypt + decrypted = cipherSecApiCtrSubBlock(&ctx, keyHandle, SEC_CIPHERMODE_DECRYPT, iv, encrypted, inplace); + } + + TestCtx::printHex("decrypted", decrypted); + + //check if results match + if (clear != decrypted) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } +#endif + return SEC_RESULT_SUCCESS; +} + +Sec_Result testProcessOpaqueWithMap(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, + Sec_CipherAlgorithm alg, SEC_SIZE subsampleCount, SEC_SIZE bytesOfClearData) { + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle = nullptr; + if ((keyHandle = ctx.provisionKey(id, loc, key, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //gen iv + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + TestCtx::printHex("iv", iv); + + //gen clear input + std::vector clear = TestCtx::random(SUBSAMPLE_SIZE * subsampleCount); + TestCtx::printHex("clear", clear); + + //encrypt + std::vector encrypted; + std::vector ivCopy = iv; + + //use openssl to encrypt + std::vector temp; + for (size_t i = 0; i < subsampleCount; i++) { + temp.insert(temp.end(), + clear.begin() + static_cast(i * SUBSAMPLE_SIZE + bytesOfClearData), + clear.begin() + static_cast((i + 1) * SUBSAMPLE_SIZE)); + } + + std::vector encryptedTemp = cipherOpenSSL(key, alg, SEC_CIPHERMODE_ENCRYPT, &ivCopy[0], temp); + SEC_SIZE bytesOfProtectedData = SUBSAMPLE_SIZE - bytesOfClearData; + for (size_t i = 0; i < subsampleCount; i++) { + encrypted.insert(encrypted.end(), + clear.begin() + static_cast(i * SUBSAMPLE_SIZE), + clear.begin() + static_cast(i * SUBSAMPLE_SIZE + bytesOfClearData)); + encrypted.insert(encrypted.end(), + encryptedTemp.begin() + static_cast(i * bytesOfProtectedData), + encryptedTemp.begin() + static_cast((i + 1) * bytesOfProtectedData)); + } + + TestCtx::printHex("encrypted", encrypted); + TestCtx::printHex("iv", iv); + + Sec_CipherHandle* cipherHandle = ctx.acquireCipher(alg, SEC_CIPHERMODE_DECRYPT, keyHandle, &iv[0]); + if (cipherHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireCipher failed"); + return SEC_RESULT_FAILURE; + } + + auto* map = new SEC_MAP[subsampleCount]; + if (map == nullptr) { + SEC_LOG_ERROR("malloc failed"); + return SEC_RESULT_FAILURE; + } + + for (SEC_SIZE i = 0; i < subsampleCount; i++) { + map[i].clear = bytesOfClearData; + map[i].encrypted = SUBSAMPLE_SIZE - bytesOfClearData; + } + + //decrypt + std::vector decrypted; + Sec_OpaqueBufferHandle* opaqueBufferHandle; + SEC_SIZE bytesWritten = 0; + Sec_Result result = SecCipher_ProcessOpaqueWithMap(cipherHandle, iv.data(), encrypted.data(), encrypted.size(), + SEC_TRUE, map, subsampleCount, &opaqueBufferHandle, &bytesWritten); + if (result != SEC_RESULT_SUCCESS) { + delete[] map; + SEC_LOG_ERROR("SecCipher_ProcessOpaqueWithMap failed"); + return result; + } + + //check if results match + auto expected = digestOpenSSL(SEC_DIGESTALGORITHM_SHA256, clear); + result = SecOpaqueBuffer_Check(SEC_DIGESTALGORITHM_SHA256, opaqueBufferHandle, clear.size(), expected.data(), + expected.size()); + delete[] map; + SecOpaqueBuffer_Free(opaqueBufferHandle); + return result; +} + +Sec_Result testProcessOpaqueWithMapVariable(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, + Sec_CipherAlgorithm alg) { + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle = nullptr; + if ((keyHandle = ctx.provisionKey(id, loc, key, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + size_t subsampleCount = 5; + size_t bytesOfClearDataCtr[5] = {0, 16, 20, 50, 256}; + size_t bytesOfClearDataCbc[5] = {0, 16, 32, 48, 256}; + size_t* bytesOfClearData = + (alg == SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING) ? bytesOfClearDataCbc : bytesOfClearDataCtr; + + //gen iv + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + TestCtx::printHex("iv", iv); + + //gen clear input + std::vector clear = TestCtx::random(SUBSAMPLE_SIZE * subsampleCount); + TestCtx::printHex("clear", clear); + + //encrypt + std::vector encrypted; + std::vector ivCopy = iv; + + //use openssl to encrypt + std::vector temp; + for (size_t i = 0; i < subsampleCount; i++) { + temp.insert(temp.end(), + clear.begin() + static_cast(i * SUBSAMPLE_SIZE + bytesOfClearData[i]), + clear.begin() + static_cast((i + 1) * SUBSAMPLE_SIZE)); + } + + std::vector encryptedTemp = cipherOpenSSL(key, alg, SEC_CIPHERMODE_ENCRYPT, &ivCopy[0], temp); + int64_t pos = 0; + for (size_t i = 0; i < subsampleCount; i++) { + SEC_SIZE bytesOfProtectedData = SUBSAMPLE_SIZE - bytesOfClearData[i]; + encrypted.insert(encrypted.end(), + clear.begin() + static_cast(i * SUBSAMPLE_SIZE), + clear.begin() + static_cast(i * SUBSAMPLE_SIZE + bytesOfClearData[i])); + encrypted.insert(encrypted.end(), + encryptedTemp.begin() + pos, + encryptedTemp.begin() + pos + bytesOfProtectedData); + pos += bytesOfProtectedData; + } + + TestCtx::printHex("encrypted", encrypted); + TestCtx::printHex("iv", iv); + + Sec_CipherHandle* cipherHandle = ctx.acquireCipher(alg, SEC_CIPHERMODE_DECRYPT, keyHandle, &iv[0]); + if (cipherHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireCipher failed"); + return SEC_RESULT_FAILURE; + } + + auto* map = new SEC_MAP[subsampleCount]; + if (map == nullptr) { + SEC_LOG_ERROR("malloc failed"); + return SEC_RESULT_FAILURE; + } + + for (SEC_SIZE i = 0; i < subsampleCount; i++) { + map[i].clear = bytesOfClearData[i]; + map[i].encrypted = SUBSAMPLE_SIZE - bytesOfClearData[i]; + } + + //decrypt + std::vector decrypted; + Sec_OpaqueBufferHandle* opaqueBufferHandle; + SEC_SIZE bytesWritten = 0; + Sec_Result result = SecCipher_ProcessOpaqueWithMap(cipherHandle, iv.data(), encrypted.data(), encrypted.size(), + SEC_TRUE, map, subsampleCount, &opaqueBufferHandle, &bytesWritten); + if (result != SEC_RESULT_SUCCESS) { + delete[] map; + SEC_LOG_ERROR("SecCipher_ProcessOpaqueWithMap failed"); + return result; + } + + //check if results match + auto expected = digestOpenSSL(SEC_DIGESTALGORITHM_SHA256, clear); + result = SecOpaqueBuffer_Check(SEC_DIGESTALGORITHM_SHA256, opaqueBufferHandle, clear.size(), expected.data(), + expected.size()); + delete[] map; + SecOpaqueBuffer_Free(opaqueBufferHandle); + return result; +} + +Sec_Result aesKeyCheck(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID id, SEC_BYTE* key, SEC_SIZE key_len) { + SEC_PRINT("--- aes key check ---\n"); + + std::vector clear = TestCtx::random(SEC_AES_BLOCK_SIZE); + TestCtx::printHex("clear", clear); + + std::vector cipher_secapi; + cipher_secapi.resize(SEC_AES_BLOCK_SIZE); + SEC_SIZE cipher_secapi_len; + + if (SecCipher_SingleInputId(processorHandle, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_ENCRYPT, id, + nullptr, &clear[0], clear.size(), &cipher_secapi[0], cipher_secapi.size(), &cipher_secapi_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_SingleInputId failed"); + return SEC_RESULT_FAILURE; + } + cipher_secapi.resize(cipher_secapi_len); + TestCtx::printHex("cipher_secapi", cipher_secapi); + + std::vector openssl_key = std::vector(key, key + key_len); + + std::vector cipher_ssl = opensslAesEcb(openssl_key, SEC_CIPHERMODE_ENCRYPT, SEC_FALSE, nullptr, clear); + + TestCtx::printHex("cipher_ssl", cipher_ssl); + + SEC_PRINT("---------------------\n"); + + //check if results match + if (cipher_secapi != cipher_ssl) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} diff --git a/test/main/cpp/cipher.h b/test/main/cpp/cipher.h new file mode 100644 index 0000000..afc80ff --- /dev/null +++ b/test/main/cpp/cipher.h @@ -0,0 +1,84 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef CIPHER_H +#define CIPHER_H + +#include "sec_security.h" +#include "test_creds.h" + +Sec_Result testCipherSingle(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_CipherAlgorithm alg, + Sec_CipherMode mode, SEC_SIZE inputSize, SEC_BOOL inplace = SEC_FALSE); + +Sec_Result testCipherSingle(SEC_OBJECTID id, TestKey pub, TestKey priv, TestKc kc, Sec_StorageLoc loc, + Sec_CipherAlgorithm alg, Sec_CipherMode mode, SEC_SIZE inputSize, SEC_BOOL inplace = SEC_FALSE); + +Sec_Result testCipherMult(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_CipherAlgorithm alg, + Sec_CipherMode mode, const std::vector& inputSizes, SEC_BOOL inplace = SEC_FALSE, + SEC_BOOL testCtrRollover = SEC_FALSE); + +Sec_Result testCipherMult(SEC_OBJECTID id, TestKey pub, TestKey priv, TestKc kc, Sec_StorageLoc loc, + Sec_CipherAlgorithm alg, Sec_CipherMode mode, const std::vector& inputSizes, + SEC_BOOL inplace = SEC_FALSE); + +Sec_Result testCipherBandwidth(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_CipherAlgorithm alg, + Sec_CipherMode mode, SEC_SIZE inputSize, SEC_SIZE intervalS); + +Sec_Result testCipherBandwidthSingleCipher(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, + Sec_CipherAlgorithm alg, Sec_CipherMode mode, SEC_SIZE inputSize, SEC_SIZE intervalS); + +Sec_Result cipherEncDecSingle(TestCtx* ctx, SEC_OBJECTID id, Sec_CipherAlgorithm alg, SEC_SIZE inputSize, + SEC_BOOL inplace = SEC_FALSE); + +Sec_Result cipherEncDecMult(TestCtx* ctx, SEC_OBJECTID id, Sec_CipherAlgorithm alg, + const std::vector& inputSizes, SEC_BOOL inplace = SEC_FALSE); + +Sec_Result cipherEncDecSingle(TestCtx* ctx, SEC_OBJECTID id_pub, SEC_OBJECTID id_priv, Sec_CipherAlgorithm alg, + SEC_SIZE inputSize, SEC_BOOL inplace = SEC_FALSE); + +Sec_Result cipherEncDecMult(TestCtx* ctx, SEC_OBJECTID id_pub, SEC_OBJECTID id_priv, Sec_CipherAlgorithm alg, + const std::vector& inputSizes, SEC_BOOL inplace = SEC_FALSE); + +Sec_Result testCipherUpdateIV(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_CipherAlgorithm alg, + Sec_CipherMode mode, SEC_SIZE inputSize, SEC_BOOL inplace); + +Sec_Result testProcessCtrWithDataShift(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_CipherMode mode, + SEC_BOOL inplace); + +std::vector opensslAesEcb(TestKey key, Sec_CipherMode mode, bool padding, SEC_BYTE* iv, + const std::vector& input); + +std::vector opensslAesEcb(const std::vector& key, Sec_CipherMode mode, SEC_BOOL padding, + SEC_BYTE* iv, const std::vector& input); + +std::vector opensslAesGcm(const std::vector& key, Sec_CipherMode mode, SEC_BYTE* iv, SEC_BYTE* aad, + SEC_SIZE aad_length, SEC_BYTE* tag, SEC_SIZE tag_length, + const std::vector& input); + +Sec_Result aesKeyCheck(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID id, SEC_BYTE* key, SEC_SIZE key_len); + +Sec_Result testCtrRollover(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_CipherMode mode, + SEC_SIZE inputSize, SEC_BOOL inplace); + +Sec_Result testProcessOpaqueWithMap(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, + Sec_CipherAlgorithm alg, SEC_SIZE subsampleCount, SEC_SIZE bytesOfClearData); + +Sec_Result testProcessOpaqueWithMapVariable(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, + Sec_CipherAlgorithm alg); + +#endif // CIPHER_H diff --git a/test/main/cpp/concurrent.cpp b/test/main/cpp/concurrent.cpp new file mode 100644 index 0000000..e7443fc --- /dev/null +++ b/test/main/cpp/concurrent.cpp @@ -0,0 +1,131 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "concurrent.h" // NOLINT +#include "cipher.h" +#include "key.h" + +struct Vendor128Args { // NOLINT(altera-struct-pack-align) + SEC_OBJECTID id; + Sec_Result result; +}; + +void* concurrent_vendor128(void* arg) { + auto* args = static_cast(arg); + + args->result = testKeyDeriveKeyLadderAes128( + args->id, + SEC_KEYTYPE_AES_128, + SEC_STORAGELOC_RAM, + SEC_KEYLADDERROOT_UNIQUE, + SEC_TRUE); + + return nullptr; +} + +Sec_Result testConcurrentVendor128(SEC_SIZE numThreads) { + + std::vector threads; + std::vector args; + threads.resize(numThreads); + args.resize(numThreads); + + SEC_PRINT("Spawning %d threads\n", numThreads); + for (unsigned int i = 0; i < threads.size(); ++i) { + args[i].id = SEC_OBJECTID_USER_BASE + i; + + pthread_create(&threads[i], nullptr, concurrent_vendor128, &args[i]); + } + + SEC_PRINT("Waiting for threads to complete\n"); + for (auto& thread : threads) { + pthread_join(thread, nullptr); + } + + SEC_PRINT("Threads completed\n"); + + //check results + SEC_PRINT("Checking results\n"); + for (auto& arg : args) { + if (arg.result != SEC_RESULT_SUCCESS) { + return SEC_RESULT_FAILURE; + } + } + + return SEC_RESULT_SUCCESS; +} + +struct RsaArgs { + SEC_OBJECTID id; + TestKey pub; + TestKey priv; + TestKc kc; + + Sec_Result result; +} __attribute__((aligned(32))); + +void* concurrent_rsa(void* arg) { + auto* args = static_cast(arg); + + args->result = testCipherSingle( + args->id, + args->pub, + args->priv, + args->kc, + SEC_STORAGELOC_RAM, + SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING, + SEC_CIPHERMODE_DECRYPT, + SEC_AES_BLOCK_SIZE); + + return nullptr; +} + +Sec_Result testConcurrentRsa(TestKey pub, TestKey priv, TestKc kc, SEC_SIZE numThreads) { + + std::vector threads; + std::vector args; + threads.resize(numThreads); + args.resize(numThreads); + + SEC_PRINT("Spawning %d threads\n", numThreads); + for (unsigned int i = 0; i < threads.size(); ++i) { + args[i].id = SEC_OBJECTID_USER_BASE + i; + args[i].pub = pub; + args[i].priv = priv; + args[i].kc = kc; + + pthread_create(&threads[i], nullptr, concurrent_rsa, &args[i]); + } + + SEC_PRINT("Waiting for threads to complete\n"); + for (auto& thread : threads) { + pthread_join(thread, nullptr); + } + + SEC_PRINT("Threads completed\n"); + + //check results + SEC_PRINT("Checking results\n"); + for (auto& arg : args) { + if (arg.result != SEC_RESULT_SUCCESS) { + return SEC_RESULT_FAILURE; + } + } + + return SEC_RESULT_SUCCESS; +} diff --git a/test/main/cpp/concurrent.h b/test/main/cpp/concurrent.h new file mode 100644 index 0000000..8431037 --- /dev/null +++ b/test/main/cpp/concurrent.h @@ -0,0 +1,29 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef CONCURRENT_H +#define CONCURRENT_H + +#include "sec_security.h" +#include "test_creds.h" + +Sec_Result testConcurrentVendor128(SEC_SIZE numThreads); + +Sec_Result testConcurrentRsa(TestKey pub, TestKey priv, TestKc kc, SEC_SIZE numThreads); + +#endif // CONCURRENT_H diff --git a/test/main/cpp/digest.cpp b/test/main/cpp/digest.cpp new file mode 100644 index 0000000..1269b79 --- /dev/null +++ b/test/main/cpp/digest.cpp @@ -0,0 +1,188 @@ +/** + * Copyright 2020-2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "digest.h" +#include "test_ctx.h" +#include + +std::vector digestOpenSSL(Sec_DigestAlgorithm alg, const std::vector& input) { + std::vector digest; + + switch (alg) { + case SEC_DIGESTALGORITHM_SHA1: + digest.resize(20); + SHA1(&input[0], input.size(), &digest[0]); + return digest; + + case SEC_DIGESTALGORITHM_SHA256: + digest.resize(32); + SHA256(&input[0], input.size(), &digest[0]); + return digest; + + default: + break; + } + + SEC_LOG_ERROR("Unimplemented"); + return {}; +} + +std::vector digestSecApi(TestCtx* ctx, Sec_DigestAlgorithm alg, const std::vector& input, + const std::vector& inputSizes) { + + std::vector output; + output.resize(SEC_DIGEST_MAX_LEN); + + SEC_SIZE inputProcessed = 0; + SEC_SIZE written = 0; + + Sec_DigestHandle* digestHandle = ctx->acquireDigest(alg); + if (digestHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireDigest failed"); + return {}; + } + + for (unsigned int inputSize : inputSizes) { + if (inputSize > 0) { + if (SecDigest_Update(digestHandle, const_cast(&input[inputProcessed]), inputSize) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_Update failed"); + return {}; + } + } + + inputProcessed += inputSize; + } + + //last input + if (ctx->releaseDigest(digestHandle, &output[0], &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_Process failed"); + return {}; + } + + output.resize(written); + + return output; +} + +std::vector digestSecApi(TestCtx* ctx, Sec_DigestAlgorithm alg, Sec_KeyHandle* keyHandle) { + + std::vector output; + output.resize(SEC_DIGEST_MAX_LEN); + + SEC_SIZE written = 0; + + Sec_DigestHandle* digestHandle = ctx->acquireDigest(alg); + if (digestHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireDigest failed"); + return {}; + } + + if (SecDigest_UpdateWithKey(digestHandle, keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_Update failed"); + return {}; + } + + if (ctx->releaseDigest(digestHandle, &output[0], &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_Process failed"); + return {}; + } + + output.resize(written); + + return output; +} + +Sec_Result testDigestOverKey(Sec_DigestAlgorithm alg, SEC_OBJECTID id, TestKey key, Sec_StorageLoc loc) { + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle *keyHandle; + + if (TestCreds::supports(CAPABILITY_DIGEST_OVER_HWKEY)) { + if ((keyHandle = ctx.provisionKey(id, loc, key, TESTKC_RAW)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + } else { + if ((keyHandle = ctx.provisionKey(id, loc, key, TESTKC_RAW, SEC_TRUE)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + } + + //gen clear input + std::vector clear = TestCreds::asOpenSslAes(key); + TestCtx::printHex("key", clear); + + //digest + std::vector digestSA = digestSecApi(&ctx, alg, keyHandle); + TestCtx::printHex("digestSecApi", digestSA); + + std::vector digestOS = digestOpenSSL(alg, clear); + TestCtx::printHex("digestOpenssl", digestOS); + + //check if results match + if (digestSA != digestOS) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testDigestSingle(Sec_DigestAlgorithm alg, SEC_SIZE inputSize) { + std::vector inputSizes; + inputSizes.resize(1); + inputSizes[0] = inputSize; + + return testDigestMult(alg, inputSizes); +} + +Sec_Result testDigestMult(Sec_DigestAlgorithm alg, const std::vector& inputSizes) { + + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + //gen clear input + std::vector clear = TestCtx::random(TestCtx::coalesceInputSizes(inputSizes)); + TestCtx::printHex("clear", clear); + + //digest + std::vector digestSA = digestSecApi(&ctx, alg, clear, inputSizes); + TestCtx::printHex("digestSecApi", digestSA); + + std::vector digestOS = digestOpenSSL(alg, clear); + TestCtx::printHex("digestOpenssl", digestOS); + + //check if results match + if (digestSA != digestOS) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} diff --git a/test/main/cpp/digest.h b/test/main/cpp/digest.h new file mode 100644 index 0000000..8d38f62 --- /dev/null +++ b/test/main/cpp/digest.h @@ -0,0 +1,34 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef DIGEST_H +#define DIGEST_H + +#include "sec_security.h" +#include "test_creds.h" +#include + +Sec_Result testDigestSingle(Sec_DigestAlgorithm alg, SEC_SIZE inputSize); + +Sec_Result testDigestMult(Sec_DigestAlgorithm alg, const std::vector& inputSizes); + +Sec_Result testDigestOverKey(Sec_DigestAlgorithm alg, SEC_OBJECTID id, TestKey key, Sec_StorageLoc loc); + +std::vector digestOpenSSL(Sec_DigestAlgorithm alg, const std::vector& input); + +#endif // DIGEST_H diff --git a/test/main/cpp/exchange.cpp b/test/main/cpp/exchange.cpp new file mode 100644 index 0000000..29e5d97 --- /dev/null +++ b/test/main/cpp/exchange.cpp @@ -0,0 +1,663 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "exchange.h" // NOLINT +#include "cipher.h" +#include "mac.h" +#include "sec_adapter_utils.h" +#include "test_ctx.h" + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +#include +#endif + +// Some of the values here are derived from RFC 3526 which is +// Copyright (C) The Internet Society (2003). All Rights Reserved. +static SEC_BYTE g_dh_p[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, + 0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, + 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6, + 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, + 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, + 0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, + 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4, 0x4c, 0x42, 0xe9, + 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, + 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, + 0x7c, 0x4b, 0x1f, 0xe6, 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, + 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, 0x98, 0xda, 0x48, 0x36, + 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, + 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, + 0x20, 0x85, 0x52, 0xbb, 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, + 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, 0xf1, 0x74, 0x6c, 0x08, + 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b, + 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, + 0xec, 0x07, 0xa2, 0x8f, 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, + 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7c, + 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10, + 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xaa, 0xc4, 0x2d, 0xad, 0x33, 0x17, 0x0d, + 0x04, 0x50, 0x7a, 0x33, 0xa8, 0x55, 0x21, 0xab, 0xdf, 0x1c, 0xba, 0x64, + 0xec, 0xfb, 0x85, 0x04, 0x58, 0xdb, 0xef, 0x0a, 0x8a, 0xea, 0x71, 0x57, + 0x5d, 0x06, 0x0c, 0x7d, 0xb3, 0x97, 0x0f, 0x85, 0xa6, 0xe1, 0xe4, 0xc7, + 0xab, 0xf5, 0xae, 0x8c, 0xdb, 0x09, 0x33, 0xd7, 0x1e, 0x8c, 0x94, 0xe0, + 0x4a, 0x25, 0x61, 0x9d, 0xce, 0xe3, 0xd2, 0x26, 0x1a, 0xd2, 0xee, 0x6b, + 0xf1, 0x2f, 0xfa, 0x06, 0xd9, 0x8a, 0x08, 0x64, 0xd8, 0x76, 0x02, 0x73, + 0x3e, 0xc8, 0x6a, 0x64, 0x52, 0x1f, 0x2b, 0x18, 0x17, 0x7b, 0x20, 0x0c, + 0xbb, 0xe1, 0x17, 0x57, 0x7a, 0x61, 0x5d, 0x6c, 0x77, 0x09, 0x88, 0xc0, + 0xba, 0xd9, 0x46, 0xe2, 0x08, 0xe2, 0x4f, 0xa0, 0x74, 0xe5, 0xab, 0x31, + 0x43, 0xdb, 0x5b, 0xfc, 0xe0, 0xfd, 0x10, 0x8e, 0x4b, 0x82, 0xd1, 0x20, + 0xa9, 0x3a, 0xd2, 0xca, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +static SEC_BYTE g_dh_g[] = { + 0x02, +}; + +static EC_KEY* ECCFromPubBinary(Sec_ECCRawPublicKey* binary) { + BN_CTX* ctx = BN_CTX_new(); + + if (binary->type != SEC_KEYTYPE_ECC_NISTP256_PUBLIC && binary->type != SEC_KEYTYPE_ECC_NISTP256) + return nullptr; + + EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); //create ec_key structure with NIST p256 curve; + const EC_GROUP* group = EC_KEY_get0_group(ec_key); + EC_POINT* ec_point = EC_POINT_new(group); + BN_CTX_start(ctx); + BIGNUM* xp; + BIGNUM* yp; + do { + if (((xp = BN_CTX_get(ctx)) == nullptr) || ((yp = BN_CTX_get(ctx)) == nullptr)) + break; + + EC_POINT_set_affine_coordinates_GFp(group, ec_point, + BN_bin2bn(binary->x, static_cast(Sec_BEBytesToUint32(binary->key_len)), xp), + BN_bin2bn(binary->y, static_cast(Sec_BEBytesToUint32(binary->key_len)), yp), ctx); + EC_KEY_set_public_key(ec_key, ec_point); + } while (false); + + EC_POINT_free(ec_point); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + + return ec_key; +} + +static Sec_KeyType GroupToKeyType(const EC_GROUP* group) { + if (nullptr == group) + return SEC_KEYTYPE_NUM; + switch (EC_GROUP_get_curve_name(group)) { + case NID_X9_62_prime256v1: + return SEC_KEYTYPE_ECC_NISTP256_PUBLIC; + case 0: + default: + return SEC_KEYTYPE_NUM; + } +} + +static Sec_Result BigNumToBuffer(const BIGNUM* bignum, SEC_BYTE* buffer, SEC_SIZE buffer_len) { + SEC_SIZE num_bytes; + + memset(buffer, 0, buffer_len); + num_bytes = BN_num_bytes(bignum); + + if (num_bytes > buffer_len) { + SEC_LOG_ERROR("Buffer not large enough. needed: %d, actual: %d", num_bytes, buffer_len); + return SEC_RESULT_FAILURE; + } + + BN_bn2bin(bignum, buffer + buffer_len - num_bytes); + + return SEC_RESULT_SUCCESS; +} + +static Sec_Result Extract_EC_KEY_X_Y(const EC_KEY* ec_key, BIGNUM** xp, BIGNUM** yp, Sec_KeyType* keyTypep) { + const EC_GROUP* group; + const EC_POINT* ec_point; + BN_CTX* ctx = nullptr; + Sec_Result result = SEC_RESULT_FAILURE; + + do { + if (xp == nullptr) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: X cannot be NULL"); + break; + } + + group = EC_KEY_get0_group(ec_key); + if (group == nullptr) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: EC_KEY_get0_group: %s", + ERR_error_string(ERR_get_error(), nullptr)); + break; + } + + ec_point = EC_KEY_get0_public_key(ec_key); + if (ec_point == nullptr) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: EC_KEY_get0_public_key: %s", + ERR_error_string(ERR_get_error(), nullptr)); + break; + } + + ctx = BN_CTX_new(); + if (ctx == nullptr) { + SEC_LOG_ERROR("BN_CTX_new() failed"); + break; + } + + *xp = BN_new(); + if (*xp == nullptr) { + SEC_LOG_ERROR("BN_new() failed"); + break; + } + + if (nullptr != yp) { // if caller wants y coordinate returned + *yp = BN_new(); + if (*yp == nullptr) { + SEC_LOG_ERROR("BN_new() failed"); + break; + } + } + + if (nullptr != keyTypep) { // if caller wants key type returned + *keyTypep = GroupToKeyType(group); + } + + // Get the X coordinate and optionally the Y coordinate + if (EC_POINT_get_affine_coordinates_GFp(group, ec_point, *xp, yp != nullptr ? *yp : nullptr, ctx) != 1) { + BN_clear_free(*xp); + if (nullptr != yp) + BN_clear_free(*yp); + + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: EC_POINT_get_affine_coordinates_GFp: %s", + ERR_error_string(ERR_get_error(), nullptr)); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (nullptr != ctx) + BN_CTX_free(ctx); + + return result; +} + +static Sec_Result ECCToPubBinary(EC_KEY* ec_key, Sec_ECCRawPublicKey* binary) { + BIGNUM* x = nullptr; + BIGNUM* y = nullptr; + Sec_KeyType keyType; + + if (Extract_EC_KEY_X_Y(ec_key, &x, &y, &keyType) != SEC_RESULT_SUCCESS) { + + SEC_LOG_ERROR("_Extract_EC_KEY_X_Y failed"); + return SEC_RESULT_FAILURE; + } + + binary->type = keyType; + Sec_Uint32ToBEBytes(SecKey_GetKeyLenForKeyType(keyType), binary->key_len); + BigNumToBuffer(x, binary->x, Sec_BEBytesToUint32(binary->key_len)); + BigNumToBuffer(y, binary->y, Sec_BEBytesToUint32(binary->key_len)); + + BN_free(y); + BN_free(x); + return SEC_RESULT_SUCCESS; +} + +static DH* DH_create(SEC_BYTE* p, SEC_SIZE p_len, SEC_BYTE* g, SEC_SIZE g_len) { + DH* dh; + + if ((dh = DH_new()) == nullptr) { + SEC_LOG_ERROR("DH_new failed"); + return nullptr; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + dh->p = BN_bin2bn(p, static_cast(p_len), nullptr); + dh->g = BN_bin2bn(g, static_cast(g_len), nullptr); + + if ((dh->p == nullptr) || (dh->g == nullptr)) { + SEC_LOG_ERROR("BN_bin2bn failed"); + DH_free(dh); + return nullptr; + } + + dh->length = static_cast(p_len) * 8L; +#else + BIGNUM* bnp = BN_bin2bn(p, static_cast(p_len), nullptr); + BIGNUM* bng = BN_bin2bn(g, static_cast(g_len), nullptr); + DH_set0_pqg(dh, bnp, nullptr, bng); +#endif + + return dh; +} + +static Sec_Result DH_generate_key(DH* dh, SEC_BYTE* publicKey, SEC_SIZE pubKeySize, SEC_SIZE* out_len) { + if (DH_generate_key(dh) == 0) { + SEC_LOG_ERROR("DH_generate_key failed"); + DH_free(dh); + return SEC_RESULT_FAILURE; + } + + SEC_SIZE modulus_size = DH_size(dh); + if (pubKeySize < modulus_size) { + SEC_LOG_ERROR("Buffer to small"); + return SEC_RESULT_FAILURE; + } + + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + int temp_len = BN_bn2bin(dh->pub_key, publicKey); +#else + const BIGNUM* pub_key = nullptr; + DH_get0_key(dh, &pub_key, nullptr); + + int temp_len = BN_bn2bin(pub_key, publicKey); +#endif + if (temp_len <= 0) { + SEC_LOG_ERROR("DH_compute_key failed"); + return SEC_RESULT_FAILURE; + } + + if (temp_len < modulus_size) { + memmove(publicKey + modulus_size - temp_len, publicKey, temp_len); + memset(publicKey, 0, modulus_size - temp_len); + } + + *out_len = modulus_size; + return SEC_RESULT_SUCCESS; +} + +static Sec_Result DH_compute(DH* dh, SEC_BYTE* pub_key, SEC_SIZE pub_key_len, SEC_BYTE* key, SEC_SIZE key_len, + SEC_SIZE* written) { + SEC_SIZE modulus_size = DH_size(dh); + if (key_len < modulus_size) { + SEC_LOG_ERROR("Key_len is not large enough to hold the computed DH key: %d", DH_size(dh)); + return SEC_RESULT_FAILURE; + } + + BIGNUM* pub_key_bn = BN_bin2bn(pub_key, static_cast(pub_key_len), nullptr); + if (pub_key_bn == nullptr) { + SEC_LOG_ERROR("BN_bin2bn failed"); + return SEC_RESULT_FAILURE; + } + + int temp_len = DH_compute_key(key, pub_key_bn, dh); + BN_free(pub_key_bn); + + if (temp_len <= 0) { + SEC_LOG_ERROR("DH_compute_key failed"); + return SEC_RESULT_FAILURE; + } + + if (temp_len < modulus_size) { + memmove(key + modulus_size - temp_len, key, temp_len); + memset(key, 0, modulus_size - temp_len); + } + + *written = modulus_size; + return SEC_RESULT_SUCCESS; +} + +static Sec_Result hkdf(SEC_BYTE* key, SEC_SIZE key_len, SEC_BYTE* out, const SEC_SIZE* out_len, bool use_salt) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + /* Extract */ + const EVP_MD* evp_md = EVP_sha256(); + + SEC_BYTE prk[SEC_MAC_MAX_LEN]; + SEC_SIZE prk_len; + + if (HMAC(evp_md, use_salt ? "salt" : "", use_salt ? 4 : 0, key, key_len, prk, &prk_len) == nullptr) { + SEC_LOG_ERROR("HMAC failed"); + return SEC_RESULT_FAILURE; + } + + /* Expand */ + size_t digest_length = SHA256_DIGEST_LENGTH; + size_t r = *out_len / digest_length + ((*out_len % digest_length == 0) ? 0 : 1); + SEC_BYTE t[SEC_MAC_MAX_LEN]; + unsigned int t_len = 0; + + Sec_Result result = SEC_RESULT_SUCCESS; + for (size_t i = 1; i <= r; i++) { + SEC_BYTE loop = i; + SEC_SIZE cp_len; + + if (i == r) { + SEC_SIZE mod = *out_len % digest_length; + cp_len = (mod == 0) ? digest_length : mod; + } else { + cp_len = digest_length; + } + + HMAC_CTX _ctx; + HMAC_CTX* ctx = &_ctx; + HMAC_CTX_init(ctx); + if (HMAC_Init_ex(ctx, prk, static_cast(prk_len), evp_md, nullptr) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("HMAC_Init_ex failed"); + result = SEC_RESULT_FAILURE; + break; + } + + if (t_len > 0 && OPENSSL_SUCCESS != HMAC_Update(ctx, t, t_len)) { + SEC_LOG_ERROR("HMAC_Update failed"); + result = SEC_RESULT_FAILURE; + break; + } + + if (HMAC_Update(ctx, (unsigned char*) "label", 5) != OPENSSL_SUCCESS) { // NOLINT + SEC_LOG_ERROR("HMAC_Update failed"); + result = SEC_RESULT_FAILURE; + break; + } + + if (HMAC_Update(ctx, &loop, 1) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("HMAC_Update failed"); + result = SEC_RESULT_FAILURE; + break; + } + + if (HMAC_Final(ctx, t, &t_len) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("HMAC_Final failed"); + result = SEC_RESULT_FAILURE; + break; + } + + HMAC_CTX_cleanup(ctx); + + memcpy(out + (i - 1) * digest_length, t, cp_len); + } + + Sec_Memset(prk, 0, sizeof(prk)); + Sec_Memset(t, 0, sizeof(t)); + return result; +#else + EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr); + + if (EVP_PKEY_derive_init(pctx) <= 0) { + EVP_PKEY_CTX_free(pctx); + return SEC_RESULT_FAILURE; + } + + if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) { + EVP_PKEY_CTX_free(pctx); + return SEC_RESULT_FAILURE; + } + + if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, use_salt ? "salt" : nullptr, use_salt ? 4 : 0) <= 0) { + EVP_PKEY_CTX_free(pctx); + return SEC_RESULT_FAILURE; + } + + if (EVP_PKEY_CTX_set1_hkdf_key(pctx, key, key_len) <= 0) { + EVP_PKEY_CTX_free(pctx); + return SEC_RESULT_FAILURE; + } + + if (EVP_PKEY_CTX_add1_hkdf_info(pctx, "label", 5) <= 0) { + EVP_PKEY_CTX_free(pctx); + return SEC_RESULT_FAILURE; + } + + size_t length = *out_len; + if (EVP_PKEY_derive(pctx, out, &length) <= 0) { + EVP_PKEY_CTX_free(pctx); + return SEC_RESULT_FAILURE; + } + + EVP_PKEY_CTX_free(pctx); + return SEC_RESULT_SUCCESS; +#endif +} + +Sec_Result testKeyExchangeDH(SEC_OBJECTID idComputed, Sec_StorageLoc loc, Sec_KeyType typeComputed, bool useSalt) { + + TestCtx ctx; + Sec_KeyExchangeHandle* keyExchangeHandle = nullptr; + DH* dh = nullptr; + Sec_Result result = SEC_RESULT_FAILURE; + + Sec_DHParameters dh_params; + memcpy(dh_params.p, g_dh_p, sizeof(g_dh_p)); + dh_params.pLen = sizeof(g_dh_p); + memcpy(dh_params.g, g_dh_g, sizeof(g_dh_g)); + dh_params.gLen = sizeof(g_dh_g); + + SEC_BYTE pub_secapi[dh_params.pLen]; + SEC_BYTE pub_test[dh_params.pLen]; + SEC_BYTE ss_test[dh_params.pLen]; + SEC_SIZE ss_len; + SEC_BYTE derived_key[SecKey_GetKeyLenForKeyType(typeComputed)]; + SEC_SIZE derived_key_len = sizeof(derived_key); + SEC_SIZE out_len; + SEC_OBJECTID hkdf_key_id = SEC_OBJECTID_INVALID; + + do { + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + break; + } + + result = SecKeyExchange_GetInstance(ctx.proc(), SEC_KEYEXCHANGE_DH, &dh_params, &keyExchangeHandle); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKeyExchange_GetInstance failed"); + break; + } + + result = SecKeyExchange_GenerateKeys(keyExchangeHandle, pub_secapi, sizeof(pub_secapi)); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKeyExchange_GenerateKeys failed"); + break; + } + + //create other side info + dh = DH_create(g_dh_p, sizeof(g_dh_p), g_dh_g, sizeof(g_dh_g)); + if (dh == nullptr) { + SEC_LOG_ERROR("_DH_create failed"); + break; + } + + result = DH_generate_key(dh, pub_test, sizeof(pub_test), &out_len); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("_DH_generate_key failed"); + break; + } + + //compute shared secret + result = SecKeyExchange_ComputeSecret(keyExchangeHandle, pub_test, out_len, typeComputed, idComputed, loc); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKeyExchange_ComputeSecret failed"); + break; + } + + hkdf_key_id = SecKey_ObtainFreeObjectId(ctx.proc(), SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_TOP); + if (hkdf_key_id == SEC_OBJECTID_INVALID) { + SEC_LOG_ERROR("SecKeyExchange_ComputeSecret failed"); + break; + } + + result = SecKey_Derive_HKDF_BaseKey(ctx.proc(), hkdf_key_id, typeComputed, loc, SEC_MACALGORITHM_HMAC_SHA256, + (SEC_BYTE*) (useSalt == SEC_TRUE ? "salt" : nullptr), useSalt == SEC_TRUE ? 4 : 0, // NOLINT + (SEC_BYTE*) "label", 5, idComputed); // NOLINT + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_HKDF_BaseKey failed"); + break; + } + + result = DH_compute(dh, pub_secapi, sizeof(pub_secapi), ss_test, sizeof(ss_test), &ss_len); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("_DH_compute failed"); + break; + } + + result = hkdf(ss_test, ss_len, derived_key, &derived_key_len, useSalt); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("_DH_compute failed"); + break; + } + + //test enc/dec or mac + if (SecKey_IsAES(typeComputed) == SEC_TRUE) { + result = aesKeyCheck(ctx.proc(), hkdf_key_id, derived_key, derived_key_len); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + break; + } + } else { + result = macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, hkdf_key_id, derived_key, derived_key_len); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + break; + } + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (keyExchangeHandle != nullptr) + SecKeyExchange_Release(keyExchangeHandle); + + if (hkdf_key_id != SEC_OBJECTID_INVALID) + SecKey_Delete(ctx.proc(), hkdf_key_id); + + if (dh != nullptr) + DH_free(dh); + + return result; +} + +Sec_Result testKeyExchangeECDH(SEC_OBJECTID idComputed, Sec_StorageLoc loc, Sec_KeyType typeComputed, + bool useSalt) { + TestCtx ctx; + Sec_KeyExchangeHandle* keyExchangeHandle = nullptr; + EC_KEY* priv_test = nullptr; + EC_KEY* pub_secapi_key = nullptr; + Sec_Result result = SEC_RESULT_FAILURE; + + Sec_ECCRawPublicKey pub_secapi; + Sec_ECCRawPublicKey pub_test; + SEC_BYTE ss_test[32]; + SEC_SIZE ss_len; + SEC_OBJECTID hkdf_key_id = SEC_OBJECTID_INVALID; + SEC_BYTE derived_key[SecKey_GetKeyLenForKeyType(typeComputed)]; + SEC_SIZE derived_key_len = sizeof(derived_key); + + Sec_ECDHParameters ecdh_params; + ecdh_params.curve = NISTP256; + + do { + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + break; + } + + result = SecKeyExchange_GetInstance(ctx.proc(), SEC_KEYEXCHANGE_ECDH, &ecdh_params, &keyExchangeHandle); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKeyExchange_GetInstance failed"); + break; + } + + result = SecKeyExchange_GenerateKeys(keyExchangeHandle, reinterpret_cast(&pub_secapi), + sizeof(pub_secapi)); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKeyExchange_GenerateKeys failed"); + break; + } + + //create other side info + if ((priv_test = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == nullptr) { + SEC_LOG_ERROR("EC_KEY_new_by_curve_name failed"); + break; + } + + if (EC_KEY_generate_key(priv_test) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("EC_KEY_generate_key failed"); + break; + } + + if (ECCToPubBinary(priv_test, &pub_test) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("_ECCToPubBinary failed"); + break; + } + + //compute shared secret + result = SecKeyExchange_ComputeSecret(keyExchangeHandle, reinterpret_cast(&pub_test), + sizeof(pub_test), typeComputed, idComputed, loc); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKeyExchange_ComputeSecret failed"); + break; + } + + pub_secapi_key = ECCFromPubBinary(&pub_secapi); + if (pub_secapi_key == nullptr) { + SEC_LOG_ERROR("SecUtils_ECCFromPubBinary failed"); + break; + } + + /* Derive the shared secret */ + ss_len = ECDH_compute_key(ss_test, sizeof(ss_test), EC_KEY_get0_public_key(pub_secapi_key), priv_test, nullptr); + if (ss_len <= 0) { + SEC_LOG_ERROR("ECDH_compute_key failed"); + break; + } + + hkdf_key_id = SecKey_ObtainFreeObjectId(ctx.proc(), SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_TOP); + if (hkdf_key_id == SEC_OBJECTID_INVALID) { + SEC_LOG_ERROR("SecKeyExchange_ComputeSecret failed"); + break; + } + + result = SecKey_Derive_HKDF_BaseKey(ctx.proc(), hkdf_key_id, typeComputed, loc, SEC_MACALGORITHM_HMAC_SHA256, + (unsigned char*) (useSalt == SEC_TRUE ? "salt" : nullptr), useSalt == SEC_TRUE ? 4 : 0, // NOLINT + (unsigned char*) "label", 5, idComputed); // NOLINT + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_HKDF_BaseKey failed"); + break; + } + + result = hkdf(ss_test, ss_len, derived_key, &derived_key_len, useSalt); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("_DH_compute failed"); + break; + } + + //test enc/dec or mac + if (SecKey_IsAES(typeComputed) == SEC_TRUE) { + result = aesKeyCheck(ctx.proc(), hkdf_key_id, derived_key, derived_key_len); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + break; + } + } else { + result = macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, hkdf_key_id, derived_key, derived_key_len); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + break; + } + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (keyExchangeHandle != nullptr) { + SecKeyExchange_Release(keyExchangeHandle); + } + + if (hkdf_key_id != SEC_OBJECTID_INVALID) + SecKey_Delete(ctx.proc(), hkdf_key_id); + + SEC_ECC_FREE(priv_test); + SEC_ECC_FREE(pub_secapi_key); + + return result; +} diff --git a/test/main/cpp/exchange.h b/test/main/cpp/exchange.h new file mode 100644 index 0000000..31f7159 --- /dev/null +++ b/test/main/cpp/exchange.h @@ -0,0 +1,30 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef EXCHANGE_H +#define EXCHANGE_H + +#include "sec_security.h" +#include "test_creds.h" +#include + +Sec_Result testKeyExchangeDH(SEC_OBJECTID idComputed, Sec_StorageLoc loc, Sec_KeyType typeComputed, bool useSalt); + +Sec_Result testKeyExchangeECDH(SEC_OBJECTID idComputed, Sec_StorageLoc loc, Sec_KeyType typeComputed, bool useSalt); + +#endif // EXCHANGE_H diff --git a/test/main/cpp/jtype.cpp b/test/main/cpp/jtype.cpp new file mode 100644 index 0000000..7499006 --- /dev/null +++ b/test/main/cpp/jtype.cpp @@ -0,0 +1,471 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "jtype.h" // NOLINT +#include "cipher.h" +#include "mac.h" +#include "sec_adapter_utils.h" +#include "sec_security_comcastids.h" +#include "test_ctx.h" +#include + +#define BUFFER_SIZE 4096 + +std::string toB64(const SEC_BYTE* data, SEC_SIZE len) { + std::string res; + SEC_SIZE res_len; + res.resize(SEC_KEYCONTAINER_MAX_LEN); + + if (SecUtils_Base64Encode(data, len, (SEC_BYTE*) res.data(), res.size(), &res_len) != // NOLINT + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSrv_B64Encode failed"); + return ""; + } + + res.resize(res_len); + + return res; +} + +std::string createJTypeHeader(const char* kid, const char* alg) { + std::string res; + + res += R"({"kid":")"; + res += kid; + res += R"(","alg":")"; + res += alg; + res += "\"}"; + + return toB64((SEC_BYTE*) res.c_str(), res.size()); // NOLINT +} + +std::string createJTypeBodyV1(const char* contentKey, const char* contentKeyId, const char* contentKeyRights, + SEC_BOOL cacheable, int contentKeyUsage, const char* contentKeyNotBefore, const char* contentKeyNotOnOrAfter) { + std::string res; + char tmp[BUFFER_SIZE]; + + res += R"({"contentKeyNotOnOrAfter":")"; + res += contentKeyNotOnOrAfter; + + res += R"(","contentKey":")"; + res += contentKey; + + res += R"(","contentKeyId":")"; + res += contentKeyId; + + res += R"(","contentKeyRights":")"; + res += contentKeyRights; + + res += R"(","contentKeyCacheable":)"; + res += (cacheable == SEC_TRUE ? "true" : "false"); + + sprintf(tmp, "%d", contentKeyUsage); + + res += ",\"contentKeyUsage\":"; + res += tmp; + + res += R"(,"contentKeyNotBefore":")"; + res += contentKeyNotBefore; + + res += "\"}"; + + return toB64((SEC_BYTE*) res.c_str(), res.size()); // NOLINT +} + +std::string createJTypeBodyV2(const char* contentKey, const char* contentKeyId, const char* contentKeyRights, + SEC_BOOL cacheable, int contentKeyUsage, const char* contentKeyNotBefore, const char* contentKeyNotOnOrAfter, + int cklen, const char* alg, const char* iv) { + std::string res; + char tmp[BUFFER_SIZE]; + + res += "{"; + + res += "\"contentKeyContainerVersion\":2"; + + res += R"(,"contentKeyNotOnOrAfter":")"; + res += contentKeyNotOnOrAfter; + + res += R"(","contentKey":")"; + res += contentKey; + + res += R"(","contentKeyId":")"; + res += contentKeyId; + + res += R"(","contentKeyRights":")"; + res += contentKeyRights; + + res += R"(","contentKeyCacheable":)"; + res += (cacheable == SEC_TRUE ? "true" : "false"); + + sprintf(tmp, "%d", contentKeyUsage); + + res += ",\"contentKeyUsage\":"; + res += tmp; + + res += R"(,"contentKeyNotBefore":")"; + res += contentKeyNotBefore; + + res += R"(","contentKeyLength":)"; + sprintf(tmp, "%d", cklen); + res += tmp; + res += ""; + + res += R"(,"contentKeyTransportAlgorithm":")"; + res += alg; + + if (iv != nullptr) { + res += R"(","contentKeyTransportIv":")"; + res += iv; + } + + res += "\"}"; + + return toB64((SEC_BYTE*) res.c_str(), res.size()); // NOLINT +} + +std::string createContentKeyV1(TestKey contentKey, TestKey encryptionKey) { + std::vector conK = TestCreds::asOpenSslAes(contentKey); + if (conK.empty()) { + SEC_LOG_ERROR("TestCreds::asOpenSslAes failed"); + return {}; + } + + if (conK.size() != 16) { + SEC_LOG_ERROR("V1 Jtype cannot support keys that are not 128 bits"); + return {}; + } + + std::vector encK = TestCreds::asOpenSslAes(encryptionKey); + if (encK.empty()) { + SEC_LOG_ERROR("TestCreds::asOpenSslAes failed"); + return {}; + } + + std::vector encConK = opensslAesEcb(encryptionKey, SEC_CIPHERMODE_ENCRYPT, SEC_FALSE, nullptr, conK); + if (encConK.empty()) { + SEC_LOG_ERROR("OpensslAesEcb failed"); + return {}; + } + + return toB64(encConK.data(), encConK.size()); +} + +std::string createContentKeyV2(TestKey contentKey, TestKey encryptionKey, Sec_CipherAlgorithm alg, SEC_BYTE* iv) { + std::vector conK = TestCreds::asOpenSslAes(contentKey); + if (conK.empty()) { + SEC_LOG_ERROR("TestCreds::asOpenSslAes failed"); + return {}; + } + + std::vector encK = TestCreds::asOpenSslAes(encryptionKey); + if (encK.empty()) { + SEC_LOG_ERROR("TestCreds::asOpenSslAes failed"); + return {}; + } + + std::vector encConK; + + if (alg == SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING) { + encConK = opensslAesEcb(encryptionKey, SEC_CIPHERMODE_ENCRYPT, SEC_FALSE, iv, conK); + if (encConK.empty()) { + SEC_LOG_ERROR("OpensslAesEcb failed"); + return {}; + } + } else if (alg == SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING) { + encConK = opensslAesEcb(encryptionKey, SEC_CIPHERMODE_ENCRYPT, SEC_TRUE, iv, conK); + if (encConK.empty()) { + SEC_LOG_ERROR("OpensslAesEcb failed"); + return {}; + } + } else { + SEC_LOG_ERROR("Unexpected algorithm encountered: %d", alg); + } + + return toB64(encConK.data(), encConK.size()); +} + +std::string createJTypeMac(const std::string& header, const std::string& body, TestKey macKey) { + std::string data = header + "." + body; + std::vector input((SEC_BYTE*) data.data(), (SEC_BYTE*) (data.data() + data.size())); // NOLINT + std::vector mac = macOpenSSL(SEC_MACALGORITHM_HMAC_SHA256, macKey, input); + return toB64(mac.data(), mac.size()); +} + +std::string createJTypeContainer(const char* kid, const char* macalg, TestKey contentKey, TestKey encryptionKey, + const char* contentKeyId, const char* contentKeyRights, SEC_BOOL cacheable, int contentKeyUsage, + const char* contentKeyNotBefore, const char* contentKeyNotOnOrAfter, TestKey macKey, int version, + const char* alg) { + + std::string header_b64 = createJTypeHeader(kid, macalg); + if (header_b64.empty()) { + SEC_LOG_ERROR("CreateJTypeHeader failed"); + return {}; + } + + std::string body_b64; + if (version == 1) { + std::string encConK = createContentKeyV1(contentKey, encryptionKey); + if (encConK.empty()) { + SEC_LOG_ERROR("CreateContentKeyV1 failed"); + return {}; + } + + body_b64 = createJTypeBodyV1(encConK.c_str(), contentKeyId, contentKeyRights, cacheable, contentKeyUsage, + contentKeyNotBefore, contentKeyNotOnOrAfter); + if (body_b64.empty()) { + SEC_LOG_ERROR("CreateJTypeBody failed"); + return {}; + } + } else if (version == 2) { + Sec_CipherAlgorithm salg; + if (strcmp(alg, "aesEcbNone") == 0) { + salg = SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING; + } else if (strcmp(alg, "aesEcbPkcs5") == 0) { + salg = SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING; + } else { + SEC_LOG_ERROR("Unknown algorithm encountered: %s", alg); + return {}; + } + + std::string encConK = createContentKeyV2(contentKey, encryptionKey, salg, nullptr); + if (encConK.empty()) { + SEC_LOG_ERROR("CreateContentKeyV2 failed"); + return {}; + } + + std::vector conK = TestCreds::asOpenSslAes(contentKey); + if (conK.empty()) { + SEC_LOG_ERROR("TestCreds::asOpenSslAes failed"); + return {}; + } + + body_b64 = createJTypeBodyV2(encConK.c_str(), contentKeyId, contentKeyRights, cacheable, contentKeyUsage, + contentKeyNotBefore, contentKeyNotOnOrAfter, static_cast(conK.size()), alg, nullptr); + if (body_b64.empty()) { + SEC_LOG_ERROR("CreateJTypeBody failed"); + return {}; + } + } else { + SEC_LOG_ERROR("Unknown version encountered: %d", version); + return {}; + } + + std::string mac_b64 = createJTypeMac(header_b64, body_b64, macKey); + if (mac_b64.empty()) { + SEC_LOG_ERROR("CreateJTypeMac failed"); + return {}; + } + + return header_b64 + "." + body_b64 + "." + mac_b64; +} + +Sec_Result testProvisionJType(TestKey contentKey, TestKey encryptionKey, TestKc encKc, TestKey macKey, TestKc macKc, + int version, const char* alg) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", contentKey, encryptionKey, + "9c621060-3a17-4813-8dcb-2e9187aaa903", createDefaultRights(TestCreds::getKeyType(contentKey)).c_str(), + SEC_FALSE, 1, "2010-12-09T19:53:06Z", "2037-12-09T19:53:06Z", macKey, version, alg); + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + return SEC_RESULT_FAILURE; + } + + //provision encryption key + if (ctx.provisionKey(SEC_OBJECTID_COMCAST_XCALSESSIONENCKEY, SEC_STORAGELOC_RAM, encryptionKey, encKc) == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //provision maccing key + if (ctx.provisionKey(SEC_OBJECTID_COMCAST_XCALSESSIONMACKEY, SEC_STORAGELOC_RAM, macKey, macKc) == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //provsion j-type key + Sec_Result result = SecKey_Provision(ctx.proc(), SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_JTYPE, + reinterpret_cast(&jtype[0]), jtype.size()); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testExportKey(TestKey contentKey, TestKey encryptionKey, TestKc encKc, TestKey macKey, TestKc macKc, + Sec_CipherAlgorithm alg, SEC_SIZE input_len, int version, const char* calg) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", contentKey, encryptionKey, + "9c621060-3a17-4813-8dcb-2e9187aaa903", createDefaultRights(TestCreds::getKeyType(contentKey)).c_str(), + SEC_TRUE, 1, "2010-12-09T19:53:06Z", "2037-12-09T19:53:06Z", + macKey, version, calg); + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + return SEC_RESULT_FAILURE; + } + + //provision encryption key + if (ctx.provisionKey(SEC_OBJECTID_COMCAST_XCALSESSIONENCKEY, SEC_STORAGELOC_RAM, encryptionKey, encKc) == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //provision maccing key + if (ctx.provisionKey(SEC_OBJECTID_COMCAST_XCALSESSIONMACKEY, SEC_STORAGELOC_RAM, macKey, macKc) == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //provsion j-type key + Sec_Result result = SecKey_Provision(ctx.proc(), SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_JTYPE, + reinterpret_cast(&jtype[0]), jtype.size()); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle; + if (SecKey_GetInstance(ctx.proc(), SEC_OBJECTID_USER_BASE, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + //get properties from j-type + Sec_KeyProperties jtype_props; + if (SecKey_GetProperties(keyHandle, &jtype_props) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetProperties failed"); + return SEC_RESULT_FAILURE; + } + + //export j-type key + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector exported_key; + exported_key.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE exported_len; + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], &exported_key[0], exported_key.size(), &exported_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failed"); + return SEC_RESULT_FAILURE; + } + exported_key.resize(exported_len); + + //provision exported + if (SecKey_Provision(ctx.proc(), SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, + &exported_key[0], exported_key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* exportedKeyHandle; + if (SecKey_GetInstance(ctx.proc(), SEC_OBJECTID_USER_BASE, &exportedKeyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + //grab properties from exported + Sec_KeyProperties exported_props; + if (SecKey_GetProperties(exportedKeyHandle, &exported_props) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetProperties failed"); + return SEC_RESULT_FAILURE; + } + + if (memcmp(&jtype_props, &exported_props, sizeof(Sec_KeyProperties)) != 0) { + SEC_LOG_ERROR("Key properties on jtype and exported container do not match"); + return SEC_RESULT_FAILURE; + } + + //test exported encryption + if (cipherEncDecSingle(&ctx, SEC_OBJECTID_USER_BASE, alg, input_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + + SecKey_Release(keyHandle); + SecKey_Release(exportedKeyHandle); + return SEC_RESULT_SUCCESS; +} + +std::string createDefaultRights(Sec_KeyType kt) { + SEC_BYTE allow_all_rights[] = { + SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_DTCP_ALLOWED, + SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_1_4_ALLOWED, + SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_2_2_ALLOWED, + SEC_KEYOUTPUTRIGHT_ANALOG_OUTPUT_ALLOWED}; + return toB64(allow_all_rights, sizeof(allow_all_rights)); +} + +Sec_Result testDecryptJType(TestKey contentKey, TestKey encryptionKey, TestKc encKc, TestKey macKey, TestKc macKc, + Sec_CipherAlgorithm alg, SEC_SIZE input_len, int version, const char* calg) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", contentKey, encryptionKey, + "9c621060-3a17-4813-8dcb-2e9187aaa903", + createDefaultRights(TestCreds::getKeyType(contentKey)).c_str(), + SEC_FALSE, 1, "2010-12-09T19:53:06Z", "2037-12-09T19:53:06Z", macKey, + version, calg); + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + return SEC_RESULT_FAILURE; + } + + Sec_StorageLoc loc = SEC_STORAGELOC_RAM; + + //provision encryption key + if (ctx.provisionKey(SEC_OBJECTID_COMCAST_XCALSESSIONENCKEY, loc, encryptionKey, encKc) == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //provision maccing key + if (ctx.provisionKey(SEC_OBJECTID_COMCAST_XCALSESSIONMACKEY, loc, macKey, macKc) == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //provision jtype key + Sec_Result result = SecKey_Provision(ctx.proc(), SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_JTYPE, + reinterpret_cast(&jtype[0]), jtype.size()); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + //test encryption + if (cipherEncDecSingle(&ctx, SEC_OBJECTID_USER_BASE, alg, input_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} diff --git a/test/main/cpp/jtype.h b/test/main/cpp/jtype.h new file mode 100644 index 0000000..fb64ab6 --- /dev/null +++ b/test/main/cpp/jtype.h @@ -0,0 +1,42 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef JTYPE_H +#define JTYPE_H + +#include "sec_security.h" +#include "test_creds.h" +#include + +Sec_Result testProvisionJType(TestKey contentKey, TestKey encryptionKey, TestKc encKc, TestKey macKey, TestKc macKc, + int version, const char* valg); + +Sec_Result testDecryptJType(TestKey contentKey, TestKey encryptionKey, TestKc encKc, TestKey macKey, TestKc macKc, + Sec_CipherAlgorithm alg, SEC_SIZE input_len, int version, const char* calg); + +Sec_Result testExportKey(TestKey contentKey, TestKey encryptionKey, TestKc encKc, TestKey macKey, TestKc macKc, + Sec_CipherAlgorithm alg, SEC_SIZE input_len, int version, const char* calg); + +std::string createJTypeContainer(const char* kid, const char* macalg, TestKey contentKey, TestKey encryptionKey, + const char* contentKeyId, const char* contentKeyRights, SEC_BOOL cacheable, int contentKeyUsage, + const char* contentKeyNotBefore, const char* contentKeyNotOnOrAfter, TestKey macKey, int version, + const char* alg); + +std::string createDefaultRights(Sec_KeyType kt); + +#endif // JTYPE_H diff --git a/test/main/cpp/key.cpp b/test/main/cpp/key.cpp new file mode 100644 index 0000000..66f588f --- /dev/null +++ b/test/main/cpp/key.cpp @@ -0,0 +1,1283 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "key.h" // NOLINT +#include "cipher.h" +#include "sec_adapter_store.h" +#include "sec_adapter_utils.h" +#include "test_ctx.h" +#include +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#include +#endif + +static int Sec_DisablePassphrasePrompt(char* buf, int size, int rwflag, void* u) { + return 0; +} + +static Sec_Result BigNumToBuffer(const BIGNUM* bignum, SEC_BYTE* buffer, SEC_SIZE buffer_len) { + SEC_SIZE num_bytes; + + memset(buffer, 0, buffer_len); + num_bytes = BN_num_bytes(bignum); + + if (num_bytes > buffer_len) { + SEC_LOG_ERROR("Buffer not large enough. needed: %d, actual: %d", num_bytes, buffer_len); + return SEC_RESULT_FAILURE; + } + + BN_bn2bin(bignum, buffer + buffer_len - num_bytes); + + return SEC_RESULT_SUCCESS; +} + +static void RSAToPubBinary(RSA* rsa, Sec_RSARawPublicKey* binary) { + Sec_Uint32ToBEBytes(RSA_size(rsa), binary->modulus_len_be); + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + BigNumToBuffer(rsa->n, binary->n, Sec_BEBytesToUint32(binary->modulus_len_be)); + BigNumToBuffer(rsa->e, binary->e, 4); +#else + const BIGNUM* n = nullptr; + const BIGNUM* e = nullptr; + RSA_get0_key(rsa, &n, &e, nullptr); + BigNumToBuffer(const_cast(n), binary->n, Sec_BEBytesToUint32(binary->modulus_len_be)); + BigNumToBuffer(const_cast(e), binary->e, 4); +#endif +} + +static Sec_Result aesKeyCheck(Sec_ProcessorHandle* processorHandle, SEC_OBJECTID id_first, SEC_OBJECTID id_second) { + SEC_PRINT("--- aes key check ---\n"); + + std::vector clear = TestCtx::random(SEC_AES_BLOCK_SIZE); + TestCtx::printHex("clear", clear); + + std::vector cipher_first; + cipher_first.resize(SEC_AES_BLOCK_SIZE); + SEC_SIZE cipher_first_len; + + if (SecCipher_SingleInputId(processorHandle, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_ENCRYPT, + id_first, nullptr, &clear[0], clear.size(), &cipher_first[0], cipher_first.size(), &cipher_first_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_SingleInputId failed"); + return SEC_RESULT_FAILURE; + } + + cipher_first.resize(cipher_first_len); + TestCtx::printHex("cipher_first", cipher_first); + + std::vector cipher_second; + cipher_second.resize(SEC_AES_BLOCK_SIZE); + SEC_SIZE cipher_second_len; + + if (SecCipher_SingleInputId(processorHandle, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_ENCRYPT, + id_second, nullptr, &clear[0], clear.size(), &cipher_second[0], cipher_second.size(), + &cipher_second_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_SingleInputId failed"); + return SEC_RESULT_FAILURE; + } + + cipher_second.resize(cipher_second_len); + TestCtx::printHex("cipher_second", cipher_second); + + SEC_PRINT("---------------------\n"); + + //check if results match + if (cipher_first != cipher_second) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +static Sec_Result macCheck(Sec_ProcessorHandle* processorHandle, Sec_MacAlgorithm alg, SEC_OBJECTID id_first, + SEC_OBJECTID id_second) { + std::vector clear = TestCtx::random(256); + TestCtx::printHex("clear", clear); + + std::vector mac_first; + mac_first.resize(SEC_MAC_MAX_LEN); + SEC_SIZE mac_first_len; + if (SecMac_SingleInputId(processorHandle, alg, id_first, &clear[0], clear.size(), &mac_first[0], &mac_first_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecMac_SingleInputId failed"); + return SEC_RESULT_FAILURE; + } + + mac_first.resize(mac_first_len); + TestCtx::printHex("mac_first", mac_first); + + std::vector mac_second; + mac_second.resize(SEC_MAC_MAX_LEN); + SEC_SIZE mac_second_len; + if (SecMac_SingleInputId(processorHandle, alg, id_first, &clear[0], clear.size(), &mac_second[0], + &mac_second_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecMac_SingleInputId failed"); + return SEC_RESULT_FAILURE; + } + + mac_second.resize(mac_second_len); + TestCtx::printHex("macSecApi", mac_second); + + //check if results match + if (mac_first != mac_second) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testStore(SEC_BOOL encrypt, SEC_BOOL mac) { + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector data = TestCtx::random(20); + TestCtx::printHex("data: ", data); + + //fill header + SecUtils_KeyStoreHeader keystore_header; + if (SecUtils_FillKeyStoreUserHeader(ctx.proc(), &keystore_header, SEC_KEYCONTAINER_RAW_HMAC_160) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_FillKeyStoreUserHeader failed"); + return SEC_RESULT_FAILURE; + } + + //write store + std::vector store; + store.resize(SEC_KEYCONTAINER_MAX_LEN); + Sec_Result result = SecStore_StoreData(ctx.proc(), encrypt, mac, (SEC_BYTE*) SEC_UTILS_KEYSTORE_MAGIC, // NOLINT + &keystore_header, sizeof(keystore_header), &data[0], data.size(), &store[0], store.size()); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecStore_StoreData failed"); + return SEC_RESULT_FAILURE; + } + + store.resize(SecStore_GetStoreLen(&store[0])); + TestCtx::printHex("store: ", store); + + //read from store + SecUtils_KeyStoreHeader keystore_header2; + std::vector extracted_data; + extracted_data.resize(SEC_KEYCONTAINER_MAX_LEN); + if (SecStore_RetrieveData(ctx.proc(), mac, &keystore_header2, sizeof(keystore_header2), &extracted_data[0], + extracted_data.size(), &store[0], store.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecStore_RetrieveData failed"); + return SEC_RESULT_FAILURE; + } + + extracted_data.resize(SecStore_GetDataLen(&store[0])); + TestCtx::printHex("extracted_data: ", extracted_data); + + if (data != extracted_data) { + SEC_LOG_ERROR("Extracted data does not match what was put into the store"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testStoreProvision(SEC_OBJECTID id, SEC_BOOL encrypt, SEC_BOOL mac) { + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector data = TestCtx::random(20); + TestCtx::printHex("data: ", data); + + //fill header + SecUtils_KeyStoreHeader keystore_header; + if (SecUtils_FillKeyStoreUserHeader(ctx.proc(), &keystore_header, SEC_KEYCONTAINER_RAW_HMAC_160) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_FillKeyStoreUserHeader failed"); + return SEC_RESULT_FAILURE; + } + + //write store + std::vector store; + store.resize(SEC_KEYCONTAINER_MAX_LEN); + if (SecStore_StoreData(ctx.proc(), encrypt, mac, (SEC_BYTE*) SEC_UTILS_KEYSTORE_MAGIC, &keystore_header, // NOLINT + sizeof(keystore_header), &data[0], data.size(), &store[0], store.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecStore_StoreData failed"); + return SEC_RESULT_FAILURE; + } + store.resize(SecStore_GetStoreLen(&store[0])); + TestCtx::printHex("store: ", store); + + //provision store + if (SecKey_Provision(ctx.proc(), id, SEC_STORAGELOC_RAM_SOFT_WRAPPED, SEC_KEYCONTAINER_STORE, &store[0], + store.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyProvision(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if (ctx.provisionKey(id, loc, key, kc) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyProvisionDouble(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, TestKey key2, TestKc kc2, + Sec_StorageLoc loc2) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle = ctx.provisionKey(id, loc, key, kc); + if (keyHandle == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + ctx.releaseKey(keyHandle); + + keyHandle = ctx.provisionKey(SEC_OBJECTID_USER_BASE, loc2, key2, kc2); + if (keyHandle == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyGetKeyInfo(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle = ctx.provisionKey(id, loc, key, kc); + if (keyHandle == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType kt = SecKey_GetKeyType(keyHandle); + if (kt < 0 || kt >= SEC_KEYTYPE_NUM) { + SEC_LOG_ERROR("Invalid key type"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_GetKeyLen(keyHandle) == 0) { + SEC_LOG_ERROR("Invalid key length"); + return SEC_RESULT_FAILURE; + } + + if (ctx.proc() != SecKey_GetProcessor(keyHandle)) { + SEC_LOG_ERROR("SecKey_GetProcessor failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyNoSha(SEC_OBJECTID id, TestKey key, TestKc kc) { + TestCtx ctx; + if (ctx.init("/tmp/sec_api_test_global", "/tmp/sec_api_test_app") != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle = ctx.provisionKey(id, SEC_STORAGELOC_FILE, key, kc); + if (keyHandle == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + ctx.releaseKey(keyHandle); + + char file_name_verification[SEC_MAX_FILE_PATH_LEN]; + snprintf(file_name_verification, sizeof(file_name_verification), SEC_VERIFICATION_FILENAME_PATTERN, + "/tmp/sec_api_test_app/", id); + SecUtils_RmFile(file_name_verification); + if (ctx.getKey(id) != nullptr) { + SEC_LOG_ERROR("ctx.getKey was supposed to fail but didn't"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +static Sec_KeyType GroupToKeyType(const EC_GROUP* group) { + if (nullptr == group) + return SEC_KEYTYPE_NUM; + switch (EC_GROUP_get_curve_name(group)) { + case NID_X9_62_prime256v1: + return SEC_KEYTYPE_ECC_NISTP256_PUBLIC; + case 0: + default: + return SEC_KEYTYPE_NUM; + } +} + +static Sec_Result Extract_EC_KEY_X_Y(const EC_KEY* ec_key, BIGNUM** xp, BIGNUM** yp, Sec_KeyType* keyTypep) { + const EC_GROUP* group; + const EC_POINT* ec_point = nullptr; + BN_CTX* ctx = nullptr; + Sec_Result result = SEC_RESULT_FAILURE; + + do { + if (xp == nullptr) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: X cannot be NULL"); + break; + } + + group = EC_KEY_get0_group(ec_key); + if (group == nullptr) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: EC_KEY_get0_group: %s", + ERR_error_string(ERR_get_error(), NULL)); + break; + } + + ec_point = EC_KEY_get0_public_key(ec_key); + if (ec_point == nullptr) { + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: EC_KEY_get0_public_key: %s", + ERR_error_string(ERR_get_error(), NULL)); + break; + } + + ctx = BN_CTX_new(); + if (ctx == nullptr) { + SEC_LOG_ERROR("BN_CTX_new() failed"); + break; + } + + *xp = BN_new(); + if (*xp == nullptr) { + SEC_LOG_ERROR("BN_new() failed"); + break; + } + + if (nullptr != yp) { // if caller wants y coordinate returned + *yp = BN_new(); + if (*yp == nullptr) { + SEC_LOG_ERROR("BN_new() failed"); + break; + } + } + + if (nullptr != keyTypep) { // if caller wants key type returned + *keyTypep = GroupToKeyType(group); + } + + // Get the X coordinate and optionally the Y coordinate + if (EC_POINT_get_affine_coordinates_GFp(group, ec_point, *xp, yp != nullptr ? *yp : nullptr, ctx) != 1) { + BN_clear_free(*xp); + if (nullptr != yp) + BN_clear_free(*yp); + + SEC_LOG_ERROR("SecUtils_ExtractEcc_Key_X_Y: EC_POINT_get_affine_coordinates_GFp: %s", + ERR_error_string(ERR_get_error(), NULL)); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (nullptr != ctx) + BN_CTX_free(ctx); + + return result; +} + +static Sec_Result ECCToPubBinary(EC_KEY* ec_key, Sec_ECCRawPublicKey* binary) { + BIGNUM* x = nullptr; + BIGNUM* y = nullptr; + Sec_KeyType keyType; + + if (Extract_EC_KEY_X_Y(ec_key, &x, &y, &keyType) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("_Extract_EC_KEY_X_Y failed"); + return SEC_RESULT_FAILURE; + } + + binary->type = keyType; + Sec_Uint32ToBEBytes(SecKey_GetKeyLenForKeyType(keyType), binary->key_len); + BigNumToBuffer(x, binary->x, Sec_BEBytesToUint32(binary->key_len)); + BigNumToBuffer(y, binary->y, Sec_BEBytesToUint32(binary->key_len)); + + BN_free(y); + BN_free(x); + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyExtractPublicKey(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle = ctx.provisionKey(id, loc, key, kc); + if (keyHandle == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_IsEcc(TestCreds::getKeyType(key)) == SEC_TRUE) { + Sec_ECCRawPublicKey public_key; + memset(&public_key, 0, sizeof(public_key)); + if (SecKey_ExtractECCPublicKey(keyHandle, &public_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExtractECCPublicKey failed"); + return SEC_RESULT_FAILURE; + } + + std::vector secapi(reinterpret_cast(&public_key), + (reinterpret_cast(&public_key)) + sizeof(Sec_ECCRawPublicKey)); + + TestCtx::printHex("secapi", secapi); + + if (kc != TESTKC_GENERATED) { + EC_KEY* ec = TestCreds::asOpenSslEcKey(key); + if (ec == nullptr) { + SEC_LOG_ERROR("TestCreds::asOpenSslEc failed"); + return SEC_RESULT_FAILURE; + } + + memset(&public_key, 0, sizeof(public_key)); + ECCToPubBinary(ec, &public_key); + SEC_ECC_FREE(ec); + + std::vector openssl(reinterpret_cast(&public_key), + (reinterpret_cast(&public_key)) + sizeof(Sec_ECCRawPublicKey)); + TestCtx::printHex("openssl", openssl); + + if (secapi != openssl) { + return SEC_RESULT_FAILURE; + } + } + } else { + Sec_RSARawPublicKey public_key; + memset(&public_key, 0, sizeof(public_key)); + if (SecKey_ExtractRSAPublicKey(keyHandle, &public_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExtractRSAPublicKey failed"); + return SEC_RESULT_FAILURE; + } + + std::vector secapi(reinterpret_cast(&public_key), + (reinterpret_cast(&public_key)) + sizeof(Sec_RSARawPublicKey)); + + TestCtx::printHex("secapi", secapi); + + RSA* rsa = TestCreds::asOpenSslRsa(key); + if (rsa == nullptr) { + SEC_LOG_ERROR("TestCreds::asOpenSslRsa failed"); + return SEC_RESULT_FAILURE; + } + + memset(&public_key, 0, sizeof(public_key)); + RSAToPubBinary(rsa, &public_key); + SEC_RSA_FREE(rsa); + + std::vector openssl(reinterpret_cast(&public_key), + (reinterpret_cast(&public_key)) + sizeof(Sec_RSARawPublicKey)); + TestCtx::printHex("openssl", openssl); + + if (secapi != openssl) { + return SEC_RESULT_FAILURE; + } + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyGenerate(SEC_OBJECTID id, Sec_KeyType keyType, Sec_StorageLoc loc, bool testEncDec) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Generate(ctx.proc(), id, keyType, loc) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Generate failed"); + return SEC_RESULT_FAILURE; + } + + if (testEncDec) { + if (cipherEncDecSingle(&ctx, id, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + } + + if (keyType == SEC_KEYTYPE_ECC_NISTP256) { + Sec_KeyHandle* keyHandle; + if (SecKey_GetInstance(ctx.proc(), id, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + SecKey_Delete(ctx.proc(), id); + return SEC_RESULT_FAILURE; + } + + Sec_ECCRawPublicKey public_key; + if (SecKey_ExtractECCPublicKey(keyHandle, &public_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExtractECCPublicKey failed"); + SecKey_Release(keyHandle); + SecKey_Delete(ctx.proc(), id); + return SEC_RESULT_FAILURE; + } + + SecKey_Release(keyHandle); + } + + SecKey_Delete(ctx.proc(), id); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyDeriveHKDF(SEC_OBJECTID id, Sec_KeyType keyType, Sec_StorageLoc loc, Sec_MacAlgorithm macAlgorithm, + bool testEncDec, bool useSalt) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector nonce = TestCtx::random(SEC_NONCE_LEN); + TestCtx::printHex("nonce", nonce); + std::vector salt = TestCtx::random(25); + TestCtx::printHex("salt", salt); + std::vector info = TestCtx::random(17); + TestCtx::printHex("info", info); + + if (SecKey_Derive_HKDF(ctx.proc(), id, keyType, loc, macAlgorithm, &nonce[0], useSalt ? &salt[0] : nullptr, + useSalt ? salt.size() : 0, &info[0], info.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_HKDF failed"); + return SEC_RESULT_FAILURE; + } + + if (testEncDec) { + if (cipherEncDecSingle(&ctx, id, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + } + + SecKey_Delete(ctx.proc(), id); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyDeriveConcatKDF(SEC_OBJECTID id, Sec_KeyType keyType, Sec_StorageLoc loc, + Sec_DigestAlgorithm digestAlgorithm, bool testEncDec) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector nonce = TestCtx::random(SEC_NONCE_LEN); + TestCtx::printHex("nonce", nonce); + std::vector otherInfo = TestCtx::random(17); + TestCtx::printHex("otherInfo", otherInfo); + + if (SecKey_Derive_ConcatKDF(ctx.proc(), id, keyType, loc, digestAlgorithm, &nonce[0], &otherInfo[0], + otherInfo.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_ConcatKDF failed"); + return SEC_RESULT_FAILURE; + } + + if (testEncDec) { + if (cipherEncDecSingle(&ctx, id, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + } + + SecKey_Delete(ctx.proc(), id); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyDeriveVendorAes128(SEC_OBJECTID id, Sec_KeyType keyType, Sec_StorageLoc loc, + Sec_MacAlgorithm macAlgorithm, bool testEncDec) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector input = TestCtx::random(25); + TestCtx::printHex("input", input); + + if (SecKey_Derive_VendorAes128(ctx.proc(), id, loc, &input[0], input.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_VendorAes128 failed"); + return SEC_RESULT_FAILURE; + } + + if (testEncDec) { + if (cipherEncDecSingle(&ctx, id, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + } + + SecKey_Delete(ctx.proc(), id); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyDeriveKeyLadderAes128(SEC_OBJECTID id, Sec_KeyType keyType, Sec_StorageLoc loc, + Sec_KeyLadderRoot root, bool testEncDec) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector> inputs; + inputs.resize(SecProcessor_GetKeyLadderMaxDepth(ctx.proc(), root)); + + if (inputs.empty()) { + SEC_LOG_ERROR("Key ladder not available"); + return SEC_RESULT_FAILURE; + } + + for (unsigned int i = 0; i < inputs.size(); ++i) { + inputs[i] = TestCtx::random(SEC_AES_BLOCK_SIZE); + + char buf[8]; + sprintf(buf, "%d", i); + + std::string name; + name += "input["; + name += buf; + name += "]"; + TestCtx::printHex(name.c_str(), inputs[i]); + } + + size_t inputs_size = inputs.size(); + Sec_Result result = SecKey_Derive_KeyLadderAes128(ctx.proc(), id, loc, root, + inputs_size > 0 ? &(inputs[0])[0] : nullptr, + inputs_size > 1 ? &(inputs[1])[0] : nullptr, + inputs_size > 2 ? &(inputs[2])[0] : nullptr, + inputs_size > 3 ? &(inputs[3])[0] : nullptr); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_KeyLadderAes128 failed"); + return SEC_RESULT_FAILURE; + } + + if (testEncDec) { + if (cipherEncDecSingle(&ctx, id, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + } + + SecKey_Delete(ctx.proc(), id); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyComputeBaseKeyDigest(SEC_OBJECTID id, Sec_DigestAlgorithm alg) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector nonce = TestCtx::random(SEC_NONCE_LEN); + TestCtx::printHex("nonce", nonce); + + std::vector digest; + digest.resize(SEC_DIGEST_MAX_LEN); + SEC_SIZE digestLen; + + if (SecKey_ComputeBaseKeyDigest(ctx.proc(), &nonce[0], alg, &digest[0], &digestLen) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ComputeBaseKeyDigest failed"); + return SEC_RESULT_FAILURE; + } + + digest.resize(digestLen); + TestCtx::printHex("digest", digest); + + return SEC_RESULT_SUCCESS; +} + +static EC_KEY* ECCFromDERPriv(const SEC_BYTE* der, SEC_SIZE der_len) { + const auto* p = der; + PKCS8_PRIV_KEY_INFO* p8 = nullptr; + EVP_PKEY* evp_key = nullptr; + EC_KEY* ecc = nullptr; + + do { + p8 = d2i_PKCS8_PRIV_KEY_INFO(nullptr, &p, der_len); + if (p8 != nullptr) { + evp_key = EVP_PKCS82PKEY(p8); + if (evp_key == nullptr) { + SEC_LOG_ERROR("EVP_PKCS82PKEY failed"); + break; + } + } else { + evp_key = d2i_AutoPrivateKey(nullptr, &p, der_len); + if (evp_key == nullptr) { + SEC_LOG_ERROR("d2i_AutoPrivateKey failed"); + break; + } + } + + ecc = EVP_PKEY_get1_EC_KEY(evp_key); + if (ecc == nullptr) { + SEC_LOG_ERROR("EVP_PKEY_get1_EC_KEY failed"); + break; + } + } while (false); + + SEC_EVPPKEY_FREE(evp_key); + + if (p8 != nullptr) { + PKCS8_PRIV_KEY_INFO_free(p8); + } + + return ecc; +} + +static EC_KEY* ECCFromPEMPub(SEC_BYTE* pem, SEC_SIZE pem_len) { + BIO* bio = nullptr; + EC_KEY* ec_key = nullptr; + + bio = BIO_new_mem_buf(pem, static_cast(pem_len)); + do { + ec_key = PEM_read_bio_EC_PUBKEY(bio, &ec_key, Sec_DisablePassphrasePrompt, nullptr); + + if (ec_key == nullptr) { + SEC_LOG_ERROR("Invalid ECC key container"); + break; + } + } while (false); + + SEC_BIO_FREE(bio); + + return ec_key; +} + +static EC_KEY* ECCFromDERPub(const SEC_BYTE* der, SEC_SIZE der_len) { + const auto* p = der; + EC_KEY* ec_key = nullptr; + do { + ec_key = d2i_EC_PUBKEY(&ec_key, &p, der_len); + + if (ec_key == nullptr) { + SEC_LOG_ERROR("Invalid ECC key container"); + break; + } + } while (false); + + return ec_key; +} + +static EC_KEY* ECCFromPrivBinary(Sec_ECCRawPrivateKey* binary) { + BN_CTX* ctx = BN_CTX_new(); + + // Note that SEC_KEYTYPE_ECC_NISTP256_PUBLIC is not acceptable, + // because it won't have a private key value + if (binary->type != SEC_KEYTYPE_ECC_NISTP256) + return nullptr; + + EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); //create ec_key structure with NIST p256 curve; + const EC_GROUP* group = EC_KEY_get0_group(ec_key); + EC_POINT* ec_point; + do { + ec_point = EC_POINT_new(group); + BN_CTX_start(ctx); + BIGNUM* xp; + BIGNUM* yp; + BIGNUM* prvp; + if (((xp = BN_CTX_get(ctx)) == nullptr) || ((yp = BN_CTX_get(ctx)) == nullptr) || + ((prvp = BN_CTX_get(ctx)) == nullptr)) + break; + + EC_POINT_set_affine_coordinates_GFp(group, ec_point, + BN_bin2bn(binary->x, static_cast(Sec_BEBytesToUint32(binary->key_len)), xp), + BN_bin2bn(binary->y, static_cast(Sec_BEBytesToUint32(binary->key_len)), yp), ctx); + EC_KEY_set_public_key(ec_key, ec_point); + + EC_KEY_set_private_key(ec_key, + BN_bin2bn(binary->prv, static_cast(Sec_BEBytesToUint32(binary->key_len)), prvp)); + } while (false); + + EC_POINT_free(ec_point); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + + return ec_key; +} + +static EC_KEY* ECCFromPubBinary(Sec_ECCRawPublicKey* binary) { + BN_CTX* ctx = BN_CTX_new(); + + if (binary->type != SEC_KEYTYPE_ECC_NISTP256_PUBLIC && binary->type != SEC_KEYTYPE_ECC_NISTP256) + return nullptr; + + EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); //create ec_key structure with NIST p256 curve; + const EC_GROUP* group = EC_KEY_get0_group(ec_key); + EC_POINT* ec_point; + do { + ec_point = EC_POINT_new(group); + BN_CTX_start(ctx); + BIGNUM* xp; + BIGNUM* yp; + + if (((xp = BN_CTX_get(ctx)) == nullptr) || ((yp = BN_CTX_get(ctx)) == nullptr)) + break; + + EC_POINT_set_affine_coordinates_GFp(group, ec_point, + BN_bin2bn(binary->x, static_cast(Sec_BEBytesToUint32(binary->key_len)), xp), + BN_bin2bn(binary->y, static_cast(Sec_BEBytesToUint32(binary->key_len)), yp), ctx); + EC_KEY_set_public_key(ec_key, ec_point); + } while (false); + + EC_POINT_free(ec_point); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + + return ec_key; +} + +static EC_KEY* ECCFromPEMPriv(SEC_BYTE* pem, SEC_SIZE pem_len) { + BIO* bio = nullptr; + EC_KEY* ec_key = nullptr; + + do { + bio = BIO_new_mem_buf(pem, static_cast(pem_len)); + ec_key = PEM_read_bio_ECPrivateKey(bio, &ec_key, Sec_DisablePassphrasePrompt, nullptr); + + if (ec_key == nullptr) { + SEC_LOG_ERROR("Invalid ECC key container"); + break; + } + } while (false); + + SEC_BIO_FREE(bio); + + return ec_key; +} + +static EC_KEY* ECCFromClearKC(Sec_ProcessorHandle* processorHandle, Sec_KeyContainer kc, SEC_BYTE* data, + SEC_SIZE data_len) { + EC_KEY* ec_key = nullptr; + SecUtils_KeyStoreHeader store_header; + SEC_BYTE store_data[SEC_KEYCONTAINER_MAX_LEN]; + + do { + if (kc == SEC_KEYCONTAINER_DER_ECC_NISTP256) { + ec_key = ECCFromDERPriv(data, data_len); + if (ec_key == nullptr) { + SEC_LOG_ERROR("SecUtils_ECCFromDERPriv failed"); + break; + } + } else if (kc == SEC_KEYCONTAINER_DER_ECC_NISTP256_PUBLIC) { + ec_key = ECCFromDERPub(data, data_len); + if (ec_key == nullptr) { + SEC_LOG_ERROR("SecUtils_ECCFromDERPub failed"); + break; + } + } else if (kc == SEC_KEYCONTAINER_RAW_ECC_NISTP256) { + if (data_len == sizeof(Sec_ECCRawPrivateKey)) { + ec_key = ECCFromPrivBinary(reinterpret_cast(data)); + if (ec_key == nullptr) { + SEC_LOG_ERROR("SecUtils_ECCFromPrivBinary failed"); + break; + } + } else { + SEC_LOG_ERROR("Invalid priv key structure size: %d", data_len); + break; + } + } else if (kc == SEC_KEYCONTAINER_RAW_ECC_NISTP256_PUBLIC) { + if (data_len != sizeof(Sec_ECCRawPublicKey)) { + SEC_LOG_ERROR("Invalid pub key structure size: %d", data_len); + break; + } + + ec_key = ECCFromPubBinary(reinterpret_cast(data)); + if (ec_key == nullptr) { + SEC_LOG_ERROR("SecUtils_ECCFromPubBinary failed"); + break; + } + } else if (kc == SEC_KEYCONTAINER_PEM_ECC_NISTP256) { + ec_key = ECCFromPEMPriv(data, data_len); + if (ec_key == nullptr) { + SEC_LOG_ERROR("SecUtils_ECCFromPEMPriv failed"); + break; + } + } else if (kc == SEC_KEYCONTAINER_PEM_ECC_NISTP256_PUBLIC) { + ec_key = ECCFromPEMPub(data, data_len); + if (ec_key == nullptr) { + SEC_LOG_ERROR("SecUtils_ECCFromPEMPub failed"); + break; + } + } else if (kc == SEC_KEYCONTAINER_STORE) { + // Store Key containers are not supported + Sec_Result result = SecStore_RetrieveData(processorHandle, SEC_FALSE, &store_header, sizeof(store_header), + store_data, sizeof(store_data), data, data_len); + if (result != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecStore_RetrieveData failed"); + break; + } + + ec_key = ECCFromClearKC(processorHandle, static_cast(store_header.inner_kc_type), + store_data, SecStore_GetDataLen(data)); + if (ec_key == nullptr) { + SEC_LOG_ERROR("_ECCFromClearKC failed"); + break; + } + + SEC_LOG_ERROR("Store key containers are not supported"); + break; + } else { + SEC_LOG_ERROR("Unknown container type"); + break; + } + } while (false); + + return ec_key; +} + +Sec_Result testKeyECDHKeyAgreementWithKDF(SEC_OBJECTID id_derived, SEC_OBJECTID id_priv, TestKey priv, TestKc priv_kc, + TestKey pub, Sec_KeyType keyType, Sec_StorageLoc loc, Sec_DigestAlgorithm digestAlgorithm, bool testEncDec) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle = nullptr; + if ((keyHandle = ctx.provisionKey(id_priv, loc, priv, priv_kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + ProvKey* keyPubRaw = TestCreds::getKey(pub, TESTKC_RAW, SEC_OBJECTID_INVALID); + EC_KEY* ec_key_pub = ECCFromClearKC(ctx.proc(), keyPubRaw->kc, &(keyPubRaw->key[0]), keyPubRaw->key.size()); + if (ec_key_pub == nullptr) { + SEC_ECC_FREE(ec_key_pub); + SEC_LOG_ERROR("_ECCFromClearKC failed"); + delete keyPubRaw; + return SEC_RESULT_FAILURE; + } + + delete keyPubRaw; + + Sec_ECCRawPublicKey pub_other; + if (ECCToPubBinary(ec_key_pub, &pub_other) != SEC_RESULT_SUCCESS) { + SEC_ECC_FREE(ec_key_pub); + SEC_LOG_ERROR("_ECCToPubBinary failed"); + return SEC_RESULT_FAILURE; + } + + SEC_ECC_FREE(ec_key_pub); + std::vector other_info = TestCtx::random(52); + TestCtx::printHex("other_info: ", other_info); + + if (SecKey_ECDHKeyAgreementWithKDF(keyHandle, &pub_other, keyType, id_derived, loc, SEC_KDF_CONCAT, + digestAlgorithm, &other_info[0], other_info.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ECDHKeyAgreementWithKDF failed"); + return SEC_RESULT_FAILURE; + } + + if (testEncDec) { + if (cipherEncDecSingle(&ctx, id_derived, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + } + + SecKey_Delete(ctx.proc(), id_derived); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyDeriveCMACAES128(SEC_OBJECTID idDerived, SEC_OBJECTID idBase, TestKc baseKc, Sec_KeyType keyType, + Sec_StorageLoc loc, bool testEncDec, SEC_BYTE counter, uint32_t L) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if (ctx.provisionKey(idBase, loc, TESTKEY_AES128, baseKc) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //base key + TestCtx::printHex("baseKey", TestCreds::asOpenSslAes(TESTKEY_AES128)); + + //label + std::vector otherData = TestCtx::random(10); + //separator + otherData.push_back(0); + //ctx + std::vector ctx2 = TestCtx::random(32); + otherData.insert(otherData.end(), ctx2.begin(), ctx2.end()); + + otherData.push_back(0); + otherData.push_back(0); + otherData.push_back(0); + otherData.push_back(0); + Sec_Uint32ToBEBytes(L, &otherData[otherData.size() - 4]); + + TestCtx::printHex("otherData", otherData); + + if (SecKey_Derive_CMAC_AES128(ctx.proc(), idDerived, keyType, loc, idBase, &otherData[0], otherData.size(), + &counter, 1) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_CMAC_AES128 failed"); + return SEC_RESULT_FAILURE; + } + + if (testEncDec) { + if (SecKey_IsAES(keyType) == SEC_TRUE) { + if (cipherEncDecSingle(&ctx, idDerived, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + } + } + + SecKey_Delete(ctx.proc(), idDerived); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyDeriveBaseKey(SEC_OBJECTID idDerived, Sec_StorageLoc loc) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector nonce = TestCtx::random(SEC_NONCE_LEN); + TestCtx::printHex("nonce", nonce); + + if (SecKey_Derive_BaseKey(ctx.proc(), idDerived, SEC_KEYTYPE_HMAC_128, loc, &nonce[0]) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_BaseKey failed"); + return SEC_RESULT_FAILURE; + } + + SecKey_Delete(ctx.proc(), idDerived); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyDeriveHKDFBaseKey(Sec_KeyType typeDerived, Sec_StorageLoc loc, Sec_MacAlgorithm macAlgorithm) { + Sec_Result result = SEC_RESULT_FAILURE; + SEC_OBJECTID baseKeyId = SEC_OBJECTID_USER_BASE; + SEC_OBJECTID idDerived = baseKeyId + 1; + SEC_OBJECTID idDerivedSecond = idDerived + 1; + + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector nonce = TestCtx::random(SEC_NONCE_LEN); + TestCtx::printHex("nonce", nonce); + + std::vector salt = TestCtx::random(20); + TestCtx::printHex("salt", salt); + + std::vector info = TestCtx::random(40); + TestCtx::printHex("info", info); + do { + if (SecKey_Derive_BaseKey(ctx.proc(), baseKeyId, SEC_KEYTYPE_HMAC_128, loc, &nonce[0]) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_BaseKey failed"); + break; + } + + if (SecKey_Derive_HKDF_BaseKey(ctx.proc(), idDerived, typeDerived, loc, macAlgorithm, &salt[0], salt.size(), + &info[0], info.size(), baseKeyId) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_HKDF_BaseKey failed"); + break; + } + + //repeat derivation + if (SecKey_Derive_BaseKey(ctx.proc(), baseKeyId, SEC_KEYTYPE_HMAC_128, loc, &nonce[0]) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_BaseKey failed"); + break; + } + + if (SecKey_Derive_HKDF_BaseKey(ctx.proc(), idDerivedSecond, typeDerived, loc, macAlgorithm, &salt[0], + salt.size(), &info[0], info.size(), baseKeyId) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_HKDF_BaseKey failed"); + break; + } + + //test enc/dec or mac + if (SecKey_IsAES(typeDerived) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), idDerived, idDerivedSecond) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + break; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, idDerived, idDerivedSecond) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + break; + } + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + SecKey_Delete(ctx.proc(), baseKeyId); + SecKey_Delete(ctx.proc(), idDerived); + SecKey_Delete(ctx.proc(), idDerivedSecond); + + return result; +} + +Sec_Result testKeyDeriveConcatKDFBaseKey(Sec_KeyType typeDerived, Sec_StorageLoc loc, + Sec_DigestAlgorithm digestAlgorithm) { + SEC_OBJECTID baseKeyId = SEC_OBJECTID_USER_BASE; + SEC_OBJECTID idDerived = baseKeyId + 1; + SEC_OBJECTID idDerivedSecond = idDerived + 1; + Sec_Result result = SEC_RESULT_FAILURE; + + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector nonce = TestCtx::random(SEC_NONCE_LEN); + TestCtx::printHex("nonce", nonce); + + std::vector info = TestCtx::random(40); + TestCtx::printHex("info", info); + do { + if (SecKey_Derive_BaseKey(ctx.proc(), baseKeyId, SEC_KEYTYPE_AES_128, loc, &nonce[0]) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_BaseKey failed"); + break; + } + + if (SecKey_Derive_ConcatKDF_BaseKey(ctx.proc(), idDerived, typeDerived, loc, digestAlgorithm, &info[0], + info.size(), baseKeyId) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_ConcatKDF_BaseKey failed"); + SecKey_Delete(ctx.proc(), baseKeyId); + break; + } + + //repeat derivation + if (SecKey_Derive_BaseKey(ctx.proc(), baseKeyId, SEC_KEYTYPE_AES_128, loc, &nonce[0]) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_BaseKey failed"); + break; + } + + if (SecKey_Derive_ConcatKDF_BaseKey(ctx.proc(), idDerivedSecond, typeDerived, loc, digestAlgorithm, &info[0], + info.size(), baseKeyId) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_ConcatKDF_BaseKey failed"); + SecKey_Delete(ctx.proc(), baseKeyId); + break; + } + + //test enc/dec or mac + if (SecKey_IsAES(typeDerived) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), idDerived, idDerivedSecond) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + break; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, idDerived, idDerivedSecond) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + break; + } + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + SecKey_Delete(ctx.proc(), baseKeyId); + SecKey_Delete(ctx.proc(), idDerived); + SecKey_Delete(ctx.proc(), idDerivedSecond); + + return result; +} + +Sec_Result testExportProvisionedKey(TestKey key, TestKc kc) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + //provision encryption key + Sec_KeyHandle* keyHandle = ctx.provisionKey(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, key, kc); + if (keyHandle == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //export j-type key + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector exported_key; + exported_key.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE exported_len; + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], &exported_key[0], exported_key.size(), &exported_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failed"); + return SEC_RESULT_FAILURE; + } + exported_key.resize(exported_len); + + //provision exported + if (SecKey_Provision(ctx.proc(), SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, + &exported_key[0], exported_key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* exportedKeyHandle; + if (SecKey_GetInstance(ctx.proc(), SEC_OBJECTID_USER_BASE, &exportedKeyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + //grab properties from exported + Sec_KeyProperties exported_props; + if (SecKey_GetProperties(exportedKeyHandle, &exported_props) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetProperties failed"); + return SEC_RESULT_FAILURE; + } + + SecKey_Release(exportedKeyHandle); + return SEC_RESULT_SUCCESS; +} diff --git a/test/main/cpp/key.h b/test/main/cpp/key.h new file mode 100644 index 0000000..0ed1ed8 --- /dev/null +++ b/test/main/cpp/key.h @@ -0,0 +1,71 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef KEY_H +#define KEY_H + +#include "sec_security.h" +#include "test_creds.h" + +Sec_Result testStore(SEC_BOOL encrypt, SEC_BOOL mac); + +Sec_Result testStoreProvision(SEC_OBJECTID id, SEC_BOOL encrypt, SEC_BOOL mac); + +Sec_Result testKeyProvision(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc); + +Sec_Result testKeyProvisionDouble(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, TestKey key2, TestKc kc2, + Sec_StorageLoc loc2); + +Sec_Result testKeyGetKeyInfo(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc); + +Sec_Result testKeyNoSha(SEC_OBJECTID id, TestKey key, TestKc kc); + +Sec_Result testKeyExtractPublicKey(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc); + +Sec_Result testKeyGenerate(SEC_OBJECTID id, Sec_KeyType keyType, Sec_StorageLoc loc, bool testEncDec); + +Sec_Result testKeyDeriveHKDF(SEC_OBJECTID id, Sec_KeyType keyType, Sec_StorageLoc loc, Sec_MacAlgorithm macAlgorithm, + bool testEncDec, bool useSalt); + +Sec_Result testKeyDeriveConcatKDF(SEC_OBJECTID id, Sec_KeyType keyType, Sec_StorageLoc loc, + Sec_DigestAlgorithm digestAlgorithm, bool testEncDec); + +Sec_Result testKeyDeriveVendorAes128(SEC_OBJECTID id, Sec_KeyType keyType, Sec_StorageLoc loc, + Sec_MacAlgorithm macAlgorithm, bool testEncDec); + +Sec_Result testKeyDeriveKeyLadderAes128(SEC_OBJECTID id, Sec_KeyType keyType, Sec_StorageLoc loc, + Sec_KeyLadderRoot root, bool testEncDec); + +Sec_Result testKeyComputeBaseKeyDigest(SEC_OBJECTID id, Sec_DigestAlgorithm alg); + +Sec_Result testKeyECDHKeyAgreementWithKDF(SEC_OBJECTID id_derived, SEC_OBJECTID id_priv, TestKey priv, TestKc priv_kc, + TestKey pub, Sec_KeyType keyType, Sec_StorageLoc loc, Sec_DigestAlgorithm digestAlgorithm, bool testEncDec); + +Sec_Result testKeyDeriveCMACAES128(SEC_OBJECTID idDerived, SEC_OBJECTID idBase, TestKc baseKc, Sec_KeyType keyType, + Sec_StorageLoc loc, bool testEncDec, SEC_BYTE counter, uint32_t L); + +Sec_Result testKeyDeriveBaseKey(SEC_OBJECTID idDerived, Sec_StorageLoc loc); + +Sec_Result testKeyDeriveHKDFBaseKey(Sec_KeyType typeDerived, Sec_StorageLoc loc, Sec_MacAlgorithm macAlgorithm); + +Sec_Result testKeyDeriveConcatKDFBaseKey(Sec_KeyType typeDerived, Sec_StorageLoc loc, + Sec_DigestAlgorithm digestAlgorithm); + +Sec_Result testExportProvisionedKey(TestKey key, TestKc kc); + +#endif // KEY_H diff --git a/test/main/cpp/keyctrl.cpp b/test/main/cpp/keyctrl.cpp new file mode 100644 index 0000000..561f14a --- /dev/null +++ b/test/main/cpp/keyctrl.cpp @@ -0,0 +1,1813 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "keyctrl.h" // NOLINT +#include "jtype.h" +#include "sec_adapter_utils.h" +#include "sec_security_comcastids.h" +#include "sign.h" +#include "test_ctx.h" + +#define BUFFER_SIZE 4096 + +// default params for jtype key container +struct default_jtype_data_struct { + TestKey contentKey; + TestKey encryptionKey; + TestKc encKc; + TestKey macKey; + TestKc macKc; + SEC_OBJECTID provisionId; +} __attribute__((aligned(32))) g_default_jtype_data = { + .contentKey = TESTKEY_AES128, + .encryptionKey = TESTKEY_AES128, + .encKc = TESTKC_CONDITIONAL, + .macKey = TESTKEY_HMAC160, + .macKc = TESTKC_RAW, + .provisionId = SEC_OBJECTID_USER_BASE}; + +static char opr_str[8][64] = { + "Not-set", + "SVP", + "DTCP", + "HDCP-1.4", + "HDCP-2.2", + "Analog", + "Transcription-copy", + "Unrestricted-copy"}; + +#define RIGHTS_INIT(x) memset(x, 0, SEC_KEYOUTPUTRIGHT_NUM) + +std::string toB64(const SEC_BYTE* data, SEC_SIZE len); + +/* Convenience function to provision the jtype key and session keys using the default + * settings. Since the jtype is a wrapped key, a check is performed to test if the + * platform supports wrapped keys in the clear. if it doesn't, SOC key container + * needs to be used. + */ +static Sec_KeyHandle* provisionJTypeAndSession(TestCtx& ctx, std::string& jtypeKey) { + Sec_KeyHandle* keyHandle = nullptr; + + do { + if (SecKey_IsProvisioned(ctx.proc(), SEC_OBJECTID_COMCAST_XCALSESSIONENCKEY) == SEC_TRUE && + SecKey_IsProvisioned(ctx.proc(), SEC_OBJECTID_COMCAST_XCALSESSIONMACKEY) == SEC_TRUE) { + SEC_PRINT("Session ENC and MAC keys are already provisioned. Not provisioning again.\n"); + } else { + SEC_PRINT("Provisioning session ENC and MAC.\n"); + + //provision encryption key + if (ctx.provisionKey(SEC_OBJECTID_COMCAST_XCALSESSIONENCKEY, SEC_STORAGELOC_RAM, + g_default_jtype_data.encryptionKey, g_default_jtype_data.encKc) == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + break; + } + + //provision maccing key + if (ctx.provisionKey(SEC_OBJECTID_COMCAST_XCALSESSIONMACKEY, SEC_STORAGELOC_RAM, + g_default_jtype_data.macKey, g_default_jtype_data.macKc) == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + break; + } + } + + //provision jtype key + if (SecKey_Provision(ctx.proc(), g_default_jtype_data.provisionId, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_JTYPE, + reinterpret_cast(&jtypeKey[0]), jtypeKey.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + break; + } + + if (SecKey_GetInstance(ctx.proc(), g_default_jtype_data.provisionId, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed for jtype key"); + break; + } + } while (false); + + return keyHandle; +} + +/* SecCipher_GetInstance should fail with notBefore date in the future */ +Sec_Result testKeyCtrlKeyNotYetAvail(int version, const char* alg) { + Sec_Result result = SEC_RESULT_FAILURE; + TestCtx ctx; + Sec_CipherHandle* cipherHandle = nullptr; + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE iv[SEC_AES_BLOCK_SIZE] = {0x01}; + const char* notBeforeTimeStr = "2022-12-09T19:53:06Z"; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + do { + /* key avail in one hour */ + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", g_default_jtype_data.contentKey, + g_default_jtype_data.encryptionKey, "9c621060-3a17-4813-8dcb-2e9187aaa903", + createDefaultRights(SEC_KEYTYPE_AES_128).c_str(), SEC_FALSE, 1, notBeforeTimeStr, + "2030-12-09T19:53:06Z", g_default_jtype_data.macKey, version, alg); + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + break; + } + + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + break; + } + + if (SecCipher_GetInstance(ctx.proc(), SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, keyHandle, + iv, &cipherHandle) != SEC_RESULT_FAILURE) { + SEC_LOG_ERROR("expected SecCipher_GetInstance to fail for jtype key with notBefore [%s]", notBeforeTimeStr); + + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (cipherHandle != nullptr) + SecCipher_Release(cipherHandle); + + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + return result; +} + +/* Generate a jtype key with usage of key only. SecCipher_GetInstance should fail. */ +Sec_Result testKeyCtrlKeyOnlyUsage(int version, const char* alg) { + Sec_Result result = SEC_RESULT_FAILURE; + TestCtx ctx; + Sec_CipherHandle* cipherHandle = nullptr; + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE iv[SEC_AES_BLOCK_SIZE] = {0x01}; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + do { + /* expired key */ + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", g_default_jtype_data.contentKey, + g_default_jtype_data.encryptionKey, "9c621060-3a17-4813-8dcb-2e9187aaa903", + createDefaultRights(SEC_KEYTYPE_AES_128).c_str(), SEC_FALSE, SEC_KEYUSAGE_KEY, "2010-12-09T19:53:06Z", + "2025-12-09T01:02:03Z", g_default_jtype_data.macKey, version, alg); + + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + break; + } + + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + break; + } + + if (SecCipher_GetInstance(ctx.proc(), SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, keyHandle, + iv, &cipherHandle) == SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR( + "expected Seccipher_GetInstance to fail for key with usage flag for 'key' only"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + if (cipherHandle != nullptr) + SecCipher_Release(cipherHandle); + + return result; +} + +/* Generate a jtype key with usage of data only. */ +Sec_Result testKeyCtrlUnwrapWithKeyUsage(int version, const char* alg, TestKey contentKey) { + Sec_Result result = SEC_RESULT_FAILURE; + TestCtx ctx; + Sec_CipherHandle* cipherHandle = nullptr; + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE iv[SEC_AES_BLOCK_SIZE] = {0x01}; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", contentKey, + g_default_jtype_data.encryptionKey, "9c621060-3a17-4813-8dcb-2e9187aaa903", + createDefaultRights(TestCreds::getKeyType(contentKey)).c_str(), SEC_FALSE, SEC_KEYUSAGE_KEY, + "2010-12-09T19:53:06Z", "2025-12-09T01:02:03Z", g_default_jtype_data.macKey, version, alg); + + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + return SEC_RESULT_FAILURE; + } + + /* FIXME: actually encrypt the key bytes first */ + + //create wrapped asn1 key + std::vector wrapped = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector asn1; + asn1.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE asn1_len; + SEC_BYTE input[SEC_AES_BLOCK_SIZE]; + SEC_BYTE output[SEC_AES_BLOCK_SIZE]; + SEC_SIZE output_len; + + do { + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + break; + } + + if (SecKey_GenerateWrappedKeyAsn1(&wrapped[0], wrapped.size(), SEC_KEYTYPE_AES_128, + g_default_jtype_data.provisionId, nullptr, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, + &asn1[0], asn1.size(), &asn1_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GenerateWrappedKeyAsn1 failed"); + break; + } + asn1.resize(asn1_len); + + //provision wrapped + SEC_PRINT("Provisioning wrapped\n"); + if (SecKey_Provision(ctx.proc(), SEC_OBJECTID_USER_BASE + 1, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_ASN1, + &asn1[0], asn1.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + break; + } + + SEC_PRINT("Wielding wrapped\n"); + if (SecCipher_SingleInputId(ctx.proc(), SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, + SEC_OBJECTID_USER_BASE + 1, nullptr, input, sizeof(input), output, sizeof(output), + &output_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_SingleInputId failed"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + return result; +} + +Sec_Result testKeyCtrlUnwrapWithDataUsage(int version, const char* alg) { + Sec_Result result = SEC_RESULT_FAILURE; + TestCtx ctx; + Sec_CipherHandle* cipherHandle = nullptr; + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE iv[SEC_AES_BLOCK_SIZE] = {0x01}; + //create wrapped asn1 key + std::vector wrapped = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector asn1; + asn1.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE asn1_len; + SEC_BYTE input[SEC_AES_BLOCK_SIZE]; + SEC_BYTE output[SEC_AES_BLOCK_SIZE]; + SEC_SIZE output_len; + std::string jtype; + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector exported_key(SEC_KEYCONTAINER_MAX_LEN, 0); + SEC_SIZE exported_len = 0; + + do { + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + break; + } + + jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", g_default_jtype_data.contentKey, + g_default_jtype_data.encryptionKey, "9c621060-3a17-4813-8dcb-2e9187aaa903", + createDefaultRights(SEC_KEYTYPE_AES_128).c_str(), SEC_TRUE, SEC_KEYUSAGE_DATA, "2010-12-09T19:53:06Z", + "2025-12-09T01:02:03Z", g_default_jtype_data.macKey, version, alg); + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + return SEC_RESULT_FAILURE; + } + + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + break; + } + + if (SecKey_GenerateWrappedKeyAsn1(&wrapped[0], wrapped.size(), SEC_KEYTYPE_AES_128, SEC_OBJECTID_USER_BASE, + nullptr, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, &asn1[0], asn1.size(), &asn1_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GenerateWrappedKeyAsn1 failed"); + break; + } + asn1.resize(asn1_len); + + //provision wrapped + SEC_PRINT("Provisioning wrapped\n"); + if (SecKey_Provision(ctx.proc(), SEC_OBJECTID_USER_BASE + 1, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_ASN1, + &asn1[0], asn1.size()) != SEC_RESULT_SUCCESS) { + //this will fail on some platforms, others will fail when wielding cipher + result = SEC_RESULT_SUCCESS; + break; + } + + SEC_PRINT("Wielding wrapped\n"); + if (SecCipher_SingleInputId(ctx.proc(), SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, + SEC_OBJECTID_USER_BASE + 1, nullptr, input, sizeof(input), output, sizeof(output), + &output_len) == SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Expected provisioning or wielding cipher to fail"); + break; + } + + /* export the jtype and re-provision as exported to test exported logic as well */ + if (SecKey_ExportKey(keyHandle, &derivation_input[0], &exported_key[0], exported_key.size(), &exported_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Export failed"); + break; + } + SecKey_Release(keyHandle); + keyHandle = nullptr; + SecKey_Delete(ctx.proc(), g_default_jtype_data.provisionId); + + /* provision exported */ + if (SecKey_Provision(ctx.proc(), g_default_jtype_data.provisionId, SEC_STORAGELOC_RAM, + SEC_KEYCONTAINER_EXPORTED, &exported_key[0], exported_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed for exported key"); + break; + } + + if (SecCipher_SingleInputId(ctx.proc(), SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, + SEC_OBJECTID_USER_BASE + 1, nullptr, input, sizeof(input), output, sizeof(output), + &output_len) == SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Expected provisioning or wielding cipher to fail"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + return result; +} + +/* SecCipher_Getinstance should fail with notOnOrAfter date < now */ +Sec_Result testKeyCtrlKeyExpired(int version, const char* alg) { + TestCtx ctx; + + Sec_CipherHandle* cipherHandle = nullptr; + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE iv[SEC_AES_BLOCK_SIZE] = {0x01}; + const char* notOnOrAfter = "2015-12-09T19:53:06Z"; + Sec_Result result = SEC_RESULT_FAILURE; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + /* expired key */ + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", g_default_jtype_data.contentKey, + g_default_jtype_data.encryptionKey, "9c621060-3a17-4813-8dcb-2e9187aaa903", + createDefaultRights(SEC_KEYTYPE_AES_128).c_str(), SEC_FALSE, 1, "2010-12-09T19:53:06Z", notOnOrAfter, + g_default_jtype_data.macKey, version, alg); + + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + return SEC_RESULT_FAILURE; + } + + do { + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + break; + } + + if (SecCipher_GetInstance(ctx.proc(), SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, keyHandle, + iv, &cipherHandle) == SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Expected Seccipher_GetInstance to fail for jtype key with expired notOnOrAfter [%s]", + notOnOrAfter); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (cipherHandle != nullptr) + SecCipher_Release(cipherHandle); + + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + return result; +} + +/* On 32bit machine, provision should fail if key contains date > 2038/01/19 */ +Sec_Result testKeyCtrlProvision32bit2038(int version, const char* alg) { + TestCtx ctx; + + Sec_CipherHandle* cipherHandle = nullptr; + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE iv[SEC_AES_BLOCK_SIZE] = {0x01}; + TestKey contentKey = TESTKEY_AES128; + TestKey encryptionKey = TESTKEY_AES128; + TestKc encKc = TestCreds::supports(CAPABILITY_CLEAR_JTYPE_WRAPPING) ? g_default_jtype_data.encKc : TESTKC_SOC; + TestKey macKey = TESTKEY_HMAC160; + TestKc macKc = TESTKC_RAW; + const char* notOnOrAfter = "2038-12-09T19:53:06Z"; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + /* expired key */ + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", contentKey, encryptionKey, + "9c621060-3a17-4813-8dcb-2e9187aaa903", createDefaultRights(SEC_KEYTYPE_AES_128).c_str(), SEC_FALSE, 1, + "2010-12-09T19:53:06Z", notOnOrAfter, macKey, version, alg); + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + return SEC_RESULT_FAILURE; + } + + //provision encryption key + if (ctx.provisionKey(SEC_OBJECTID_COMCAST_XCALSESSIONENCKEY, SEC_STORAGELOC_RAM, encryptionKey, encKc) == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + return SEC_RESULT_FAILURE; + } + //provision maccing key + if (ctx.provisionKey(SEC_OBJECTID_COMCAST_XCALSESSIONMACKEY, SEC_STORAGELOC_RAM, macKey, macKc) == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //provision jtype key +#if defined(__x86_64__) || defined(__ppc64__) + if (SecKey_Provision(ctx.proc(), SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_JTYPE, + reinterpret_cast(&jtype[0]), jtype.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } +#else + if (SecKey_Provision(ctx.proc(), SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_JTYPE, + reinterpret_cast(&jtype[0]), jtype.size()) == SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Expecting SecKey_Provision to fail on jtype with date '%s'", notOnOrAfter); + return SEC_RESULT_FAILURE; + } +#endif + + return SEC_RESULT_SUCCESS; +} + +/* test that export fails with a jtype key where is_cacheable is false */ +Sec_Result testKeyCtrlExportUnCachable(int version, const char* alg) { + Sec_Result result = SEC_RESULT_FAILURE; + TestCtx ctx; + Sec_KeyHandle* keyHandle = nullptr; + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector exported_key(SEC_KEYCONTAINER_MAX_LEN, 0); + SEC_SIZE exported_len = 0; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", g_default_jtype_data.contentKey, + g_default_jtype_data.encryptionKey, "9c621060-3a17-4813-8dcb-2e9187aaa903", + createDefaultRights(SEC_KEYTYPE_AES_128).c_str(), SEC_FALSE, 1, "2010-12-09T19:53:06Z", + "2037-12-09T19:53:06Z", g_default_jtype_data.macKey, version, alg); + + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + return SEC_RESULT_FAILURE; + } + + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + return SEC_RESULT_FAILURE; + } + + do { + //get properties from j-type + Sec_KeyProperties jtype_props; + if (SecKey_GetProperties(keyHandle, &jtype_props) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetProperties failed"); + break; + } + + //export j-type key + if (SecKey_ExportKey(keyHandle, &derivation_input[0], &exported_key[0], exported_key.size(), &exported_len) == + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Expected SecKey_ExportKey to fail with cachable flag set to false"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + return result; +} + +Sec_Result testKeyCtrlExpectedJTypeProperties(int version, const char* alg, TestKey contentKey) { + TestCtx ctx; + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE iv[SEC_AES_BLOCK_SIZE] = {0x01}; + const char* notOnOrAfter = "2025-12-09T19:53:06Z"; + const char* notBefore = "2010-12-09T19:53:06Z"; + const char* keyId = "9c621060-3a17-4813-8dcb-2e9187aaa903"; + Sec_KeyProperties keyProps; + SEC_BOOL cacheable = SEC_FALSE; + Sec_KeyUsage keyUsage = SEC_KEYUSAGE_KEY; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + /* expired key */ + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", contentKey, + g_default_jtype_data.encryptionKey, keyId, createDefaultRights(TestCreds::getKeyType(contentKey)).c_str(), + cacheable, keyUsage, notBefore, notOnOrAfter, g_default_jtype_data.macKey, version, alg); + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + return SEC_RESULT_FAILURE; + } + + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + return SEC_RESULT_FAILURE; + } + + SecKey_GetProperties(keyHandle, &keyProps); + SecKey_Release(keyHandle); + + if (strcmp(keyId, keyProps.keyId) != 0) { + SEC_LOG_ERROR("Keyid mismatch expecting '%s', received '%s'", keyId, keyProps.keyId); + return SEC_RESULT_FAILURE; + } + if (strcmp(notOnOrAfter, keyProps.notOnOrAfter) != 0) { + SEC_LOG_ERROR("NotOnOrAfter mismatch expecting '%s', received '%s'", notOnOrAfter, keyProps.notOnOrAfter); + return SEC_RESULT_FAILURE; + } + if (strcmp(notBefore, keyProps.notBefore) != 0) { + SEC_LOG_ERROR("NotBefore mismatch expecting '%s', received '%s'", notBefore, keyProps.notBefore); + return SEC_RESULT_FAILURE; + } + if (TestCreds::getKeyType(contentKey) != keyProps.keyType) { + SEC_LOG_ERROR("KeyType mismatch. got %d, expected %d", keyProps.keyType, TestCreds::getKeyType(contentKey)); + return SEC_RESULT_FAILURE; + } + if (SecKey_GetKeyLenForKeyType(TestCreds::getKeyType(contentKey)) != keyProps.keyLength) { + SEC_LOG_ERROR("KeyLength mismatch expecting %d, received %d", + SecKey_GetKeyLenForKeyType(TestCreds::getKeyType(contentKey)), keyProps.keyLength); + return SEC_RESULT_FAILURE; + } + if (cacheable != keyProps.cacheable) { + SEC_LOG_ERROR("Cacheable mismatch, expecting %d", cacheable); + return SEC_RESULT_FAILURE; + } + if (keyUsage != keyProps.usage) { + SEC_LOG_ERROR("Usage mismatch, expecting %d, received %d", keyUsage, keyProps.usage); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyCtrlBadB64Jtype(int version, const char* alg) { + TestCtx ctx; + TestKey contentKey = TESTKEY_AES128; + TestKey encryptionKey = TESTKEY_AES128; + TestKey macKey = TESTKEY_HMAC160; + TestKc macKc = TESTKC_RAW; + SEC_SIZE input_len = 256; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::string jtype = "B" + createJTypeContainer("1WXQ46EYW65SENER", "HS256", contentKey, encryptionKey, + "9c621060-3a17-4813-8dcb-2e9187aaa903", + createDefaultRights(SEC_KEYTYPE_AES_128).c_str(), SEC_FALSE, 1, + "2010-12-09T19:53:06Z", "2037-12-09T19:53:06Z", macKey, version, alg); + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_IsProvisioned(ctx.proc(), SEC_OBJECTID_COMCAST_XCALSESSIONENCKEY) == SEC_TRUE && + SecKey_IsProvisioned(ctx.proc(), SEC_OBJECTID_COMCAST_XCALSESSIONMACKEY) == SEC_TRUE) { + SEC_PRINT("Session ENC and MAC keys are already provisioned. Not provisioning again.\n"); + } else { + SEC_PRINT("Provisioning session ENC and MAC.\n"); + + //provision encryption key + if (ctx.provisionKey(SEC_OBJECTID_COMCAST_XCALSESSIONENCKEY, SEC_STORAGELOC_RAM, + g_default_jtype_data.encryptionKey, g_default_jtype_data.encKc) == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //provision maccing key + if (ctx.provisionKey(SEC_OBJECTID_COMCAST_XCALSESSIONMACKEY, SEC_STORAGELOC_RAM, g_default_jtype_data.macKey, + g_default_jtype_data.macKc) == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + return SEC_RESULT_FAILURE; + } + } + + //provision jtype key + if (SecKey_Provision(ctx.proc(), SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_JTYPE, + reinterpret_cast(&jtype[0]), jtype.size()) != SEC_RESULT_FAILURE) { + SEC_LOG_ERROR("Expected provisionKey to failed with bad base64"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyCtrlExportEcc(TestKc kc) { + Sec_Result result = SEC_RESULT_FAILURE; + TestCtx ctx; + SEC_OBJECTID priv_id = SEC_OBJECTID_USER_BASE + 1; + Sec_KeyHandle* keyHandle = nullptr; + TestKey pub = TESTKEY_EC_PUB; + TestKey priv = TESTKEY_EC_PRIV; + std::vector clear = TestCtx::random(32); + std::vector derivation_input = TestCtx::random(16); + SEC_BYTE exported_buffer[1024]; + std::vector signature; + signature.resize(512); + SEC_SIZE exported_size = 0; + SEC_SIZE cipher_output_written = 0; + SEC_SIZE signature_size = 0; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if ((keyHandle = ctx.provisionKey(priv_id, SEC_STORAGELOC_RAM, priv, kc)) == nullptr) { + SEC_LOG_ERROR("Provision priv key failed"); + return SEC_RESULT_FAILURE; + } + + if (SecSignature_SingleInputId(ctx.proc(), SEC_SIGNATUREALGORITHM_ECDSA_NISTP256, SEC_SIGNATUREMODE_SIGN, priv_id, + &clear[0], clear.size(), &signature[0], &signature_size) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_SingleInputId failed on signing with priv ecc key"); + return SEC_RESULT_FAILURE; + } + signature.resize(signature_size); + + //verify + if (verifyOpenSSL(SEC_SIGNATUREALGORITHM_ECDSA_NISTP256, pub, clear, signature) != SEC_TRUE) { + SEC_LOG_ERROR("VerifyOpenSSL failed"); + return SEC_RESULT_FAILURE; + } + + /* export priv key */ + if (SecKey_ExportKey(keyHandle, &derivation_input[0], exported_buffer, sizeof(exported_buffer), + &exported_size) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Export failed for private ecc"); + return SEC_RESULT_FAILURE; + } + SecKey_Delete(ctx.proc(), priv_id); + + if (SecKey_Provision(ctx.proc(), priv_id, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, exported_buffer, + exported_size) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + signature.resize(512); + if (SecSignature_SingleInputId(ctx.proc(), SEC_SIGNATUREALGORITHM_ECDSA_NISTP256, SEC_SIGNATUREMODE_VERIFY, priv_id, + &clear[0], clear.size(), &signature[0], &signature_size) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_SingleInputId failed on verification with priv ecc key"); + return SEC_RESULT_FAILURE; + } + signature.resize(signature_size); + + //verify + if (verifyOpenSSL(SEC_SIGNATUREALGORITHM_ECDSA_NISTP256, pub, clear, signature) != SEC_TRUE) { + SEC_LOG_ERROR("VerifyOpenSSL failed"); + return SEC_RESULT_FAILURE; + } + + result = SEC_RESULT_SUCCESS; + + return result; +} + +Sec_Result testKeyCtrlExportAes(TestKey aesKey, Sec_StorageLoc location) { + Sec_Result result = SEC_RESULT_FAILURE; + Sec_KeyHandle* keyHandle = nullptr; + int i = 0; + + TestCtx ctx; + SEC_OBJECTID id = SEC_OBJECTID_USER_BASE; + SEC_BYTE exported_key[BUFFER_SIZE]; + SEC_SIZE exported_key_len = 0; + SEC_SIZE exported_key_len2 = 0; + Sec_CipherAlgorithm alg = SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING; + Sec_KeyProperties keyProps; + std::vector encrypted(SEC_AES_BLOCK_SIZE); + std::vector decrypted(SEC_AES_BLOCK_SIZE); + SEC_SIZE enc_len = 0; + Sec_KeyContainer keyContainerType = SEC_KEYCONTAINER_RAW_AES_128; + + memset(&keyProps, 0, sizeof(Sec_KeyProperties)); + + if (keyHandle != nullptr) { + SecKey_Release(keyHandle); + } + keyHandle = nullptr; + + // input to export function + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + + //gen iv + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + + //gen clear input + std::vector clear = TestCtx::random(SEC_AES_BLOCK_SIZE); + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + SEC_PRINT("SEC_KEYCONTAINER_RAW_AES_128\n"); + ProvKey* p = TestCreds::getKey(aesKey, TESTKC_RAW, id); + SEC_PRINT("provisioning " SEC_OBJECTID_PATTERN "\n", id); + + if (SecKey_Provision(ctx.proc(), id, location, + aesKey == TESTKEY_AES128 ? SEC_KEYCONTAINER_RAW_AES_128 : SEC_KEYCONTAINER_RAW_AES_256, + &p->key[0], p->key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + delete p; + return SEC_RESULT_FAILURE; + } + + delete p; + + do { + //encrypt + if (SecCipher_SingleInputId(ctx.proc(), alg, SEC_CIPHERMODE_ENCRYPT, id, nullptr, &clear[0], clear.size(), + &encrypted[0], encrypted.size(), &enc_len) != SEC_RESULT_SUCCESS) { + + SEC_LOG_ERROR("Encrypt failed"); + break; + } + + if (SecKey_GetInstance(ctx.proc(), id, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + break; + } + // get size + if (SecKey_ExportKey(keyHandle, &derivation_input[0], nullptr, 0, &exported_key_len2) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failed for key size"); + break; + } + if (SecKey_ExportKey(keyHandle, &derivation_input[0], exported_key, sizeof(exported_key), &exported_key_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failed"); + break; + } + SecKey_Release(keyHandle); + keyHandle = nullptr; + + if (exported_key_len2 != exported_key_len) { + SEC_LOG_ERROR("Exported key length mismatch, expected %d, received %d", + exported_key_len2, exported_key_len); + break; + } + + // NOTE: on intel, exported keys MUST be provisioned with the same object_id as when + // they were originally provisioned. + SEC_PRINT("provisioning exported " SEC_OBJECTID_PATTERN "\n", id); + if (SecKey_Provision(ctx.proc(), id, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, exported_key, + exported_key_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + // test decrypt with exported key + if (SecCipher_SingleInputId(ctx.proc(), alg, SEC_CIPHERMODE_DECRYPT, id, nullptr, &encrypted[0], + encrypted.size(), &decrypted[0], decrypted.size(), &enc_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Decrypt failed"); + break; + } + TestCtx::printHex("derivation input", derivation_input); + TestCtx::printHex(" encrypted", encrypted); + TestCtx::printHex(" decrypted", decrypted); + TestCtx::printHex(" clear", clear); + if (clear != decrypted) { + SEC_LOG_ERROR("Decrypted vector mismatch"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + return result; +} + +Sec_Result testKeyCtrlExportDerived() { + Sec_Result result = SEC_RESULT_FAILURE; + SEC_OBJECTID id = SEC_OBJECTID_USER_BASE; + SEC_BYTE exported_key[BUFFER_SIZE]; + SEC_SIZE exported_key_len = 0; + Sec_KeyHandle* keyHandle = nullptr; + TestCtx ctx; + SEC_BYTE enc_output[256]; + SEC_SIZE enc_output_len = 0; + SEC_BYTE enc_output2[256]; + SEC_SIZE enc_output_len2 = 0; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + + std::vector input = TestCtx::random(25); + TestCtx::printHex("input", input); + + if (SecKey_Derive_VendorAes128(ctx.proc(), id, SEC_STORAGELOC_RAM, &input[0], input.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_VendorAes128 failed"); + return SEC_RESULT_FAILURE; + } + + do { + if (SecKey_GetInstance(ctx.proc(), id, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + break; + } + + if (SecCipher_SingleInputId(ctx.proc(), SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_ENCRYPT, id, + nullptr, &derivation_input[0], derivation_input.size(), enc_output, sizeof(enc_output), + &enc_output_len) != SEC_RESULT_SUCCESS) { + + SEC_LOG_ERROR("Encrypt failed"); + break; + } + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], exported_key, sizeof(exported_key), &exported_key_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failed for derived key type"); + break; + } + SecKey_Release(keyHandle); + keyHandle = nullptr; + SecKey_Delete(ctx.proc(), id); + + /* import exported derived key */ + if (SecKey_Provision(ctx.proc(), id, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, exported_key, + exported_key_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed for exported key"); + return SEC_RESULT_FAILURE; + } + if (SecKey_GetInstance(ctx.proc(), id, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + break; + } + if (SecCipher_SingleInputId(ctx.proc(), SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, id, + nullptr, enc_output, enc_output_len, enc_output2, sizeof(enc_output2), &enc_output_len2) != + SEC_RESULT_SUCCESS) { + + SEC_LOG_ERROR("Decrypt failed"); + break; + } + if (derivation_input.size() != enc_output_len2) { + SEC_LOG_ERROR("Enc output size mismatch, expected %d, %d", derivation_input.size(), enc_output_len2); + break; + } + Sec_PrintHex(&derivation_input[0], derivation_input.size()); + SEC_PRINT("\n"); + Sec_PrintHex(enc_output2, enc_output_len2); + SEC_PRINT("\n"); + if (memcmp(&derivation_input[0], enc_output2, enc_output_len2) != 0) { + SEC_LOG_ERROR("Enc output mismatch"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + return result; +} + +Sec_Result testKeyCtrlExpectedExportedProperties(int version, const char* alg, TestKey contentKey) { + TestCtx ctx; + + SEC_BYTE jtypeRights[SEC_KEYOUTPUTRIGHT_NUM]; + std::string b64rights; + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE iv[SEC_AES_BLOCK_SIZE] = {0x01}; + const char* notOnOrAfter = "2025-12-09T19:53:06Z"; + const char* notBefore = "2010-12-09T19:53:06Z"; + const char* keyId = "9c621060-3a17-4813-8dcb-2e9187aaa903"; + Sec_KeyProperties keyProps; + SEC_BOOL cacheable = SEC_TRUE; + Sec_KeyUsage keyUsage = SEC_KEYUSAGE_KEY; + SEC_BYTE exported_key[BUFFER_SIZE]; + SEC_SIZE exported_key_len = 0; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + RIGHTS_INIT(jtypeRights); + jtypeRights[0] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_1_4_ALLOWED; + jtypeRights[1] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_2_2_ALLOWED; + jtypeRights[2] = SEC_KEYOUTPUTRIGHT_ANALOG_OUTPUT_ALLOWED; + b64rights = toB64(jtypeRights, SEC_KEYOUTPUTRIGHT_NUM); + + /* expired key */ + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", contentKey, + g_default_jtype_data.encryptionKey, keyId, b64rights.c_str(), cacheable, keyUsage, notBefore, notOnOrAfter, + g_default_jtype_data.macKey, version, alg); + + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + return SEC_RESULT_FAILURE; + } + + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + return SEC_RESULT_FAILURE; + } + + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], exported_key, sizeof(exported_key), &exported_key_len) != + SEC_RESULT_SUCCESS) { + SecKey_Release(keyHandle); + SEC_LOG_ERROR("SecKey_ExportKey failed"); + return SEC_RESULT_FAILURE; + } + + SecKey_Release(keyHandle); + keyHandle = nullptr; + SecKey_Delete(ctx.proc(), SEC_OBJECTID_USER_BASE); + + // reprovision exported + if (SecKey_Provision(ctx.proc(), SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, + exported_key, exported_key_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed for exported key"); + return SEC_RESULT_FAILURE; + } + if (SecKey_GetInstance(ctx.proc(), SEC_OBJECTID_USER_BASE, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed for exported key"); + return SEC_RESULT_FAILURE; + } + + SecKey_GetProperties(keyHandle, &keyProps); + SecKey_Release(keyHandle); + keyHandle = nullptr; + + if (strcmp(keyId, keyProps.keyId) != 0) { + SEC_LOG_ERROR("Keyid mismatch expecting '%s', received '%s'", keyId, keyProps.keyId); + return SEC_RESULT_FAILURE; + } + if (strcmp(notOnOrAfter, keyProps.notOnOrAfter) != 0) { + SEC_LOG_ERROR("NotOnOrAfter mismatch expecting '%s', received '%s'", notOnOrAfter, keyProps.notOnOrAfter); + return SEC_RESULT_FAILURE; + } + if (strcmp(notBefore, keyProps.notBefore) != 0) { + SEC_LOG_ERROR("NotBefore mismatch expecting '%s', received '%s'", notBefore, keyProps.notBefore); + return SEC_RESULT_FAILURE; + } + if (TestCreds::getKeyType(contentKey) != keyProps.keyType) { + SEC_LOG_ERROR("KeyType mismatch. got %d, expected %d", keyProps.keyType, TestCreds::getKeyType(contentKey)); + return SEC_RESULT_FAILURE; + } + if (SecKey_GetKeyLenForKeyType(TestCreds::getKeyType(contentKey)) != keyProps.keyLength) { + SEC_LOG_ERROR("KeyLength mismatch expecting %d, received %d", + SecKey_GetKeyLenForKeyType(TestCreds::getKeyType(contentKey)), keyProps.keyLength); + return SEC_RESULT_FAILURE; + } + if (cacheable != keyProps.cacheable) { + SEC_LOG_ERROR("Cacheable mismatch, expecting %d", cacheable); + return SEC_RESULT_FAILURE; + } + if (keyUsage != keyProps.usage) { + SEC_LOG_ERROR("Usage mismatch, expecting %d, received %d", keyUsage, keyProps.usage); + return SEC_RESULT_FAILURE; + } + + if (memcmp(keyProps.rights, jtypeRights, 8) != 0) { + SEC_LOG_ERROR("Keyrights mismatch"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +/* export a key, re-provision it, then export it again + */ +Sec_Result testKeyCtrlExportProvisionExport(int version, const char* alg, TestKey contentKey) { + TestCtx ctx; + + SEC_BYTE jtypeRights[SEC_KEYOUTPUTRIGHT_NUM]; + std::string b64rights; + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE iv[SEC_AES_BLOCK_SIZE] = {0x01}; + const char* notOnOrAfter = "2025-12-09T19:53:06Z"; + const char* notBefore = "2010-12-09T19:53:06Z"; + const char* keyId = "9c621060-3a17-4813-8dcb-2e9187aaa903"; + Sec_KeyProperties keyProps; + SEC_BOOL cacheable = SEC_TRUE; + Sec_KeyUsage keyUsage = SEC_KEYUSAGE_KEY; + SEC_BYTE exported_key[BUFFER_SIZE]; + SEC_SIZE exported_key_len = 0; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + RIGHTS_INIT(jtypeRights); + jtypeRights[0] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_1_4_ALLOWED; + jtypeRights[1] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_2_2_ALLOWED; + jtypeRights[2] = SEC_KEYOUTPUTRIGHT_ANALOG_OUTPUT_ALLOWED; + b64rights = toB64(jtypeRights, SEC_KEYOUTPUTRIGHT_NUM); + + /* expired key */ + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", contentKey, + g_default_jtype_data.encryptionKey, keyId, b64rights.c_str(), cacheable, keyUsage, notBefore, notOnOrAfter, + g_default_jtype_data.macKey, version, alg); + + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + return SEC_RESULT_FAILURE; + } + + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + return SEC_RESULT_FAILURE; + } + + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], exported_key, sizeof(exported_key), &exported_key_len) != + SEC_RESULT_SUCCESS) { + SecKey_Release(keyHandle); + SEC_LOG_ERROR("SecKey_ExportKey failed"); + return SEC_RESULT_FAILURE; + } + + SecKey_Release(keyHandle); + keyHandle = nullptr; + SecKey_Delete(ctx.proc(), SEC_OBJECTID_USER_BASE); + + // reprovision exported + if (SecKey_Provision(ctx.proc(), SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, + exported_key, exported_key_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed for exported key"); + return SEC_RESULT_FAILURE; + } + if (SecKey_GetInstance(ctx.proc(), SEC_OBJECTID_USER_BASE, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed for exported key"); + return SEC_RESULT_FAILURE; + } + + // export it again + derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + if (SecKey_ExportKey(keyHandle, &derivation_input[0], exported_key, sizeof(exported_key), &exported_key_len) != + SEC_RESULT_SUCCESS) { + SecKey_Release(keyHandle); + SEC_LOG_ERROR("SecKey_ExportKey failed"); + return SEC_RESULT_FAILURE; + } + SecKey_Release(keyHandle); + keyHandle = nullptr; + SecKey_Delete(ctx.proc(), SEC_OBJECTID_USER_BASE); + + // reprovision exported + if (SecKey_Provision(ctx.proc(), SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, + exported_key, exported_key_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed for exported key"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_GetInstance(ctx.proc(), SEC_OBJECTID_USER_BASE, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed for exported key"); + return SEC_RESULT_FAILURE; + } + + SecKey_GetProperties(keyHandle, &keyProps); + SecKey_Release(keyHandle); + keyHandle = nullptr; + + if (strcmp(keyId, keyProps.keyId) != 0) { + SEC_LOG_ERROR("Keyid mismatch expecting '%s', received '%s'", keyId, keyProps.keyId); + return SEC_RESULT_FAILURE; + } + if (strcmp(notOnOrAfter, keyProps.notOnOrAfter) != 0) { + SEC_LOG_ERROR("NotOnOrAfter mismatch expecting '%s', received '%s'", notOnOrAfter, keyProps.notOnOrAfter); + return SEC_RESULT_FAILURE; + } + if (strcmp(notBefore, keyProps.notBefore) != 0) { + SEC_LOG_ERROR("NotBefore mismatch expecting '%s', received '%s'", notBefore, keyProps.notBefore); + return SEC_RESULT_FAILURE; + } + if (TestCreds::getKeyType(contentKey) != keyProps.keyType) { + SEC_LOG_ERROR("KeyType mismatch. got %d, expected %d", keyProps.keyType, TestCreds::getKeyType(contentKey)); + return SEC_RESULT_FAILURE; + } + if (SecKey_GetKeyLenForKeyType(TestCreds::getKeyType(contentKey)) != keyProps.keyLength) { + SEC_LOG_ERROR("KeyLength mismatch expecting %d, received %d", + SecKey_GetKeyLenForKeyType(TestCreds::getKeyType(contentKey)), keyProps.keyLength); + return SEC_RESULT_FAILURE; + } + if (cacheable != keyProps.cacheable) { + SEC_LOG_ERROR("Cacheable mismatch, expecting %d", cacheable); + return SEC_RESULT_FAILURE; + } + if (keyUsage != keyProps.usage) { + SEC_LOG_ERROR("Usage mismatch, expecting %d, received %d", keyUsage, keyProps.usage); + return SEC_RESULT_FAILURE; + } + + if (memcmp(keyProps.rights, jtypeRights, 8) != 0) { + SEC_LOG_ERROR("Keyrights mismatch"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +// get just size needed for key by passing NULL out buffer to KeyExport call +Sec_Result testKeyCtrlKeyExportGetSize(int version, const char* alg) { + TestCtx ctx; + + SEC_BYTE jtypeRights[SEC_KEYOUTPUTRIGHT_NUM]; + std::string b64rights; + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE iv[SEC_AES_BLOCK_SIZE] = {0x01}; + const char* notOnOrAfter = "2025-12-09T19:53:06Z"; + const char* notBefore = "2010-12-09T19:53:06Z"; + const char* keyId = "9c621060-3a17-4813-8dcb-2e9187aaa903"; + Sec_KeyProperties keyProps; + SEC_BOOL cacheable = SEC_TRUE; + Sec_KeyUsage keyUsage = SEC_KEYUSAGE_KEY; + SEC_BYTE exported_key[BUFFER_SIZE]; + SEC_SIZE exported_key_len = 0; + SEC_SIZE exported_key_len2 = 0; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + RIGHTS_INIT(jtypeRights); + jtypeRights[0] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_1_4_ALLOWED; + jtypeRights[1] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_2_2_ALLOWED; + jtypeRights[2] = SEC_KEYOUTPUTRIGHT_ANALOG_OUTPUT_ALLOWED; + b64rights = toB64(jtypeRights, SEC_KEYOUTPUTRIGHT_NUM); + + /* expired key */ + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", g_default_jtype_data.contentKey, + g_default_jtype_data.encryptionKey, keyId, b64rights.c_str(), cacheable, + keyUsage, notBefore, notOnOrAfter, g_default_jtype_data.macKey, version, + alg); + + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + return SEC_RESULT_FAILURE; + } + + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + return SEC_RESULT_FAILURE; + } + + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + + // get size + if (SecKey_ExportKey(keyHandle, &derivation_input[0], nullptr, 0, &exported_key_len2) != SEC_RESULT_SUCCESS) { + SecKey_Release(keyHandle); + SEC_LOG_ERROR("SecKey_ExportKey failed while getting key length"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], exported_key, sizeof(exported_key), &exported_key_len) != + SEC_RESULT_SUCCESS) { + SecKey_Release(keyHandle); + SEC_LOG_ERROR("SecKey_ExportKey failed"); + return SEC_RESULT_FAILURE; + } + SecKey_Release(keyHandle); + keyHandle = nullptr; + + if (exported_key_len != exported_key_len2) { + SEC_LOG_ERROR("Exported key length mismatch, expected %d, received %d", exported_key_len2, exported_key_len); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyCtrlKeyExportHmac(TestKey macKey, Sec_StorageLoc location) { + TestCtx ctx; + Sec_Result result = SEC_RESULT_FAILURE; + + Sec_KeyHandle* keyHandle = nullptr; + TestKc macKc = TESTKC_RAW; + Sec_KeyProperties keyProps; + SEC_BYTE exported_key[BUFFER_SIZE]; + SEC_SIZE exported_key_len = 0; + SEC_SIZE exported_key_len2 = 0; + Sec_MacHandle* macHandle = nullptr; + SEC_BYTE mac_output[256]; + SEC_SIZE mac_output_len = 0; + SEC_BYTE mac_output2[256]; + SEC_SIZE mac_output_len2 = 0; + SEC_OBJECTID id = SEC_OBJECTID_COMCAST_XCALSESSIONMACKEY; + + memset(mac_output, 0, sizeof(mac_output)); + memset(mac_output2, 0, sizeof(mac_output2)); + + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + + do { + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + break; + } + + //provision maccing key + if (ctx.provisionKey(id, location, macKey, macKc) == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + break; + } + + if (SecKey_GetInstance(ctx.proc(), id, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed for session mac key"); + break; + } + + if (SecMac_GetInstance(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA1, keyHandle, &macHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecMac_GetInstance failed for hmac key"); + break; + } + if (SecMac_Update(macHandle, &derivation_input[0], derivation_input.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecMac_GetInstance failed for hmac key"); + break; + } + + SecMac_Release(macHandle, mac_output, &mac_output_len); + macHandle = nullptr; + + // get size + if (SecKey_ExportKey(keyHandle, &derivation_input[0], nullptr, 0, &exported_key_len2) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failed while getting key length"); + break; + } + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], exported_key, sizeof(exported_key), &exported_key_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failedi for mac key"); + break; + } + SecKey_Release(keyHandle); + keyHandle = nullptr; + + SecKey_Delete(ctx.proc(), id); + + if (exported_key_len != exported_key_len2) { + SEC_LOG_ERROR("Exported key length mismatch, expected %d, received %d", + exported_key_len2, exported_key_len); + break; + } + + if (SecKey_Provision(ctx.proc(), id, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, exported_key, + exported_key_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed for exported hmac key"); + return SEC_RESULT_FAILURE; + } + if (SecKey_GetInstance(ctx.proc(), id, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed for session mac key"); + break; + } + if (SecMac_GetInstance(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA1, keyHandle, &macHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecMac_GetInstance failed for hmac key"); + break; + } + if (SecMac_Update(macHandle, &derivation_input[0], derivation_input.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecMac_Update failed for hmac key"); + break; + } + + SecMac_Release(macHandle, mac_output2, &mac_output_len2); + macHandle = nullptr; + + if (mac_output_len != mac_output_len2) { + SEC_LOG_ERROR("Mac output size mismatch, %d, %d", mac_output_len, mac_output_len2); + break; + } + Sec_PrintHex(mac_output, mac_output_len); + SEC_PRINT("\n"); + Sec_PrintHex(mac_output2, mac_output_len2); + SEC_PRINT("\n"); + if (memcmp(mac_output, mac_output2, mac_output_len2) != 0) { + SEC_LOG_ERROR("Mac output mismatch"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (macHandle != nullptr) + SecMac_Release(macHandle, mac_output, &mac_output_len); + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + return result; +} + +/* Only Opaque buffers can be used when SVP is required */ +Sec_Result testKeyCtrlCipherFailsSvpNonOpaque(int version, const char* alg, Sec_CipherAlgorithm cipher_algorithm) { + Sec_Result result = SEC_RESULT_FAILURE; + TestCtx ctx; + Sec_CipherHandle* cipherHandle = nullptr; + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE iv[SEC_AES_BLOCK_SIZE] = {0x01}; + SEC_BYTE clear_text[SEC_AES_BLOCK_SIZE] = {0x01}; + SEC_BYTE cipher_text[SEC_AES_BLOCK_SIZE]; + SEC_SIZE bytesWritten = 0; + const char* notBeforeTimeStr = "2010-12-09T19:53:06Z"; + const char* notOnOrAfterTimeStr = "2022-12-09T19:53:06Z"; + SEC_BYTE jtypeRights[SEC_KEYOUTPUTRIGHT_NUM]; + std::string b64rights; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + RIGHTS_INIT(jtypeRights); + jtypeRights[0] = SEC_KEYOUTPUTRIGHT_SVP_REQUIRED; + jtypeRights[1] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_1_4_ALLOWED; + jtypeRights[2] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_2_2_ALLOWED; + b64rights = toB64(jtypeRights, SEC_KEYOUTPUTRIGHT_NUM); + + do { + /* key avail in one hour */ + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", g_default_jtype_data.contentKey, + g_default_jtype_data.encryptionKey, "9c621060-3a17-4813-8dcb-2e9187aaa903", b64rights.c_str(), SEC_TRUE, + 1, notBeforeTimeStr, notOnOrAfterTimeStr, g_default_jtype_data.macKey, version, alg); + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + break; + } + + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + break; + } + + if (SecCipher_GetInstance(ctx.proc(), cipher_algorithm, SEC_CIPHERMODE_ENCRYPT, keyHandle, iv, &cipherHandle) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_GetInstance failed"); + break; + } + + if (SecCipher_Process(cipherHandle, clear_text, sizeof(clear_text), SEC_TRUE, cipher_text, sizeof(cipher_text), + &bytesWritten) == SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("expected SecCipher_Process to fail when processing non-opaque buffer with SVP required"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (cipherHandle != nullptr) + SecCipher_Release(cipherHandle); + + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + return result; +} + +/* cipher process succeeds with svp required and opaque buffer */ +Sec_Result testKeyCtrlCipherSvpOpaque(int version, const char* alg, TestKey contentKey, + Sec_CipherAlgorithm cipher_algorithm) { + Sec_Result result = SEC_RESULT_FAILURE; + TestCtx ctx; + Sec_CipherHandle* cipherHandle = nullptr; + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE iv[SEC_AES_BLOCK_SIZE] = {0x01}; + Sec_OpaqueBufferHandle* clearOpaqueBufferHandle = nullptr; + Sec_OpaqueBufferHandle* cipherOpaqueBufferHandle = nullptr; + SEC_SIZE bytesWritten = 0; + const char* notBeforeTimeStr = "2010-12-09T19:53:06Z"; + const char* notOnOrAfterTimeStr = "2022-12-09T19:53:06Z"; + SEC_BYTE jtypeRights[SEC_KEYOUTPUTRIGHT_NUM]; + std::string b64rights; + SEC_BYTE clear_data[SEC_AES_BLOCK_SIZE] = {0x01}; + + RIGHTS_INIT(jtypeRights); + jtypeRights[0] = SEC_KEYOUTPUTRIGHT_SVP_REQUIRED; + jtypeRights[1] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_1_4_ALLOWED; + jtypeRights[2] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_2_2_ALLOWED; + b64rights = toB64(jtypeRights, SEC_KEYOUTPUTRIGHT_NUM); + + do { + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", contentKey, + g_default_jtype_data.encryptionKey, "9c621060-3a17-4813-8dcb-2e9187aaa903", b64rights.c_str(), SEC_TRUE, + 1, notBeforeTimeStr, notOnOrAfterTimeStr, g_default_jtype_data.macKey, version, alg); + + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + break; + } + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + break; + } + + /* init opaque buffers */ + if (SecOpaqueBuffer_Malloc(SEC_AES_BLOCK_SIZE, &clearOpaqueBufferHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecOpaqueBuffer_Malloc failed"); + break; + } + if (SecOpaqueBuffer_Malloc(SEC_AES_BLOCK_SIZE, &cipherOpaqueBufferHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecOpaqueBuffer_Malloc failed"); + break; + } + if (SecOpaqueBuffer_Write(clearOpaqueBufferHandle, 0, clear_data, SEC_AES_BLOCK_SIZE) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecOpaqueBuffer_Write failed"); + break; + } + + if (SecCipher_GetInstance(ctx.proc(), cipher_algorithm, SEC_CIPHERMODE_ENCRYPT, keyHandle, iv, &cipherHandle) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_GetInstance failed"); + break; + } + + if (SecCipher_ProcessOpaque(cipherHandle, clearOpaqueBufferHandle, cipherOpaqueBufferHandle, SEC_AES_BLOCK_SIZE, + SEC_TRUE, &bytesWritten) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_ProcessOpaque failed"); + break; + } + if (bytesWritten != SEC_AES_BLOCK_SIZE) { + SEC_LOG_ERROR("Expected output size to be 16, received %d", (int) bytesWritten); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (cipherHandle != nullptr) + SecCipher_Release(cipherHandle); + + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + if (clearOpaqueBufferHandle != nullptr) + SecOpaqueBuffer_Free(clearOpaqueBufferHandle); + + if (cipherOpaqueBufferHandle != nullptr) + SecOpaqueBuffer_Free(cipherOpaqueBufferHandle); + + return result; +} + +Sec_Result testKeyCtrlCipherSvpDataShiftOpaque(int version, const char* alg) { + // Data Shift not supported +#if 0 + Sec_Result result = SEC_RESULT_FAILURE; + SEC_BYTE jtypeRights[SEC_KEYOUTPUTRIGHT_NUM]; + std::string b64rights; + const char *notBeforeTimeStr = "2010-12-09T19:53:06Z"; + const char *notOnOrAfterTimeStr = "2022-12-09T19:53:06Z"; + TestCtx ctx; + Sec_OpaqueBufferHandle *inputHandle1 = nullptr; + Sec_OpaqueBufferHandle *inputHandle2 = nullptr; + Sec_OpaqueBufferHandle *outputHandle = nullptr; + SEC_SIZE written = 0; + Sec_CipherHandle* cipherHandle = nullptr; + Sec_KeyHandle *handle = nullptr; + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + + RIGHTS_INIT(jtypeRights); + jtypeRights[0]= SEC_KEYOUTPUTRIGHT_SVP_REQUIRED; + jtypeRights[1]= SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_1_4_ALLOWED; + jtypeRights[2]= SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_2_2_ALLOWED; + b64rights = toB64(jtypeRights, SEC_KEYOUTPUTRIGHT_NUM); + +do { + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", + "HS256", + g_default_jtype_data.contentKey, + g_default_jtype_data.encryptionKey, + "9c621060-3a17-4813-8dcb-2e9187aaa903", + b64rights.c_str(), SEC_TRUE, 1, + notBeforeTimeStr, + notOnOrAfterTimeStr, + g_default_jtype_data.macKey, + version, alg); + + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + break; + } + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if (nullptr == (handle = _provisionJTypeAndSession(ctx, jtype))) + { + break; + } + + if (!= SEC_RESULT_SUCCESS) SecCipher_GetInstance(ctx.proc(), + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_DECRYPT, handle, + &iv[0], &cipherHandle)) { + SEC_LOG_ERROR("SecCipher_GetInstance failed"); + break; + } + + if (SecOpaqueBuffer_Malloc(8, &inputHandle1) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecOpaqueBuffer_Malloc failed"); + break; + } + + if (SecOpaqueBuffer_Malloc(256-8, &inputHandle2) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecOpaqueBuffer_Malloc failed"); + break; + } + + if (SecOpaqueBuffer_Malloc(256, &outputHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecOpaqueBuffer_Malloc failed"); + break; + } + + if (!= SEC_RESULT_SUCCESS) SecCipher_ProcessOpaque(cipherHandle, + inputHandle1, outputHandle, 8, SEC_FALSE, + &written)) { + SEC_LOG_ERROR("SecCipher_ProcessOpaque failed"); + break; + } + + if (SecCipher_ProcessCtrWithOpaqueDataShift(cipherHandle, inputHandle2, outputHandle, 256-8, &written, 8) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_ProcessCtrWithOpaqueDataShift failed"); + break; + } + + result = SEC_RESULT_SUCCESS; + + } while(false); + + + if (cipherHandle) + SecCipher_Release(cipherHandle); + if (handle) + SecKey_Release(handle); + if (outputHandle) + SecOpaqueBuffer_Free(outputHandle); + if (inputHandle1) + SecOpaqueBuffer_Free(inputHandle1); + if (inputHandle2) + SecOpaqueBuffer_Free(inputHandle2); + + return result; +#endif + SEC_LOG_ERROR("SecCipher_ProcessCtrWithOpaqueDataShift not supported"); + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeyCtrlSvpCheckOpaque(int version, const char* alg, TestKey contentKey) { + TestCtx ctx; + Sec_Result result = SEC_RESULT_FAILURE; + SEC_BYTE jtypeRights[SEC_KEYOUTPUTRIGHT_NUM]; + std::string b64rights; + const char* notBeforeTimeStr = "2010-12-09T19:53:06Z"; + const char* notOnOrAfterTimeStr = "2022-12-09T19:53:06Z"; + SEC_SIZE written = 0; + Sec_CipherHandle* cipherHandle = nullptr; + Sec_KeyHandle* keyHandle = nullptr; + Sec_KeyHandle* keyHandle2 = nullptr; + std::vector expected = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector input(SEC_AES_BLOCK_SIZE, 0); + SEC_SIZE bytesWritten = 0; + Sec_OpaqueBufferHandle* opaqueBufferHandle = nullptr; + + RIGHTS_INIT(jtypeRights); + jtypeRights[0] = SEC_KEYOUTPUTRIGHT_SVP_REQUIRED; + jtypeRights[1] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_1_4_ALLOWED; + jtypeRights[2] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_2_2_ALLOWED; + b64rights = toB64(jtypeRights, SEC_KEYOUTPUTRIGHT_NUM); + + do { + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", contentKey, + g_default_jtype_data.encryptionKey, "9c621060-3a17-4813-8dcb-2e9187aaa903", b64rights.c_str(), SEC_TRUE, + 1, notBeforeTimeStr, notOnOrAfterTimeStr, g_default_jtype_data.macKey, version, alg); + + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + break; + } + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + break; + } + + /* provision key setup expected test data */ + keyHandle2 = ctx.provisionKey(SEC_OBJECTID_USER_BASE + 1, SEC_STORAGELOC_RAM, contentKey, TESTKC_RAW, + SEC_FALSE); + if (keyHandle2 == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + break; + } + + if (SecCipher_SingleInput(ctx.proc(), SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_ENCRYPT, + keyHandle2, nullptr, &expected[0], expected.size(), &input[0], input.size(), &bytesWritten) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_SingleInputId failed"); + break; + } + + /* cipher handle using jtype key */ + if (SecCipher_GetInstance(ctx.proc(), SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, keyHandle, + nullptr, &cipherHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + TestCtx::printHex(" input", input); + TestCtx::printHex("expected", expected); + + if (SecOpaqueBuffer_Malloc(input.size(), &opaqueBufferHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecOpaqueBuffer_Malloc failed"); + return SEC_RESULT_FAILURE; + } + + if (SecOpaqueBuffer_Write(opaqueBufferHandle, 0, &input[0], input.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecOpaqueBuffer_Write failed"); + return SEC_RESULT_FAILURE; + } + + if (SecCipher_KeyCheckOpaque(cipherHandle, opaqueBufferHandle, SEC_AES_BLOCK_SIZE, &expected[0]) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_KeyCheckOpaque failed"); + return SEC_RESULT_FAILURE; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (cipherHandle != nullptr) + SecCipher_Release(cipherHandle); + if (opaqueBufferHandle != nullptr) + SecOpaqueBuffer_Free(opaqueBufferHandle); + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + return result; +} + +/* Only Opaque buffers can be used when SVP is required */ +Sec_Result testKeyCtrlProcessCtrDataShiftFailsSvpNonOpaque(int version, const char* alg) { + Sec_Result result = SEC_RESULT_FAILURE; + TestCtx ctx; + Sec_CipherHandle* cipherHandle = nullptr; + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE iv[SEC_AES_BLOCK_SIZE] = {0x01}; + SEC_BYTE clear_text[SEC_AES_BLOCK_SIZE] = {0x01}; + SEC_BYTE cipher_text[SEC_AES_BLOCK_SIZE]; + SEC_SIZE bytesWritten = 0; + const char* notBeforeTimeStr = "2010-12-09T19:53:06Z"; + const char* notOnOrAfterTimeStr = "2022-12-09T19:53:06Z"; + SEC_BYTE jtypeRights[SEC_KEYOUTPUTRIGHT_NUM]; + std::string b64rights; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + RIGHTS_INIT(jtypeRights); + jtypeRights[0] = SEC_KEYOUTPUTRIGHT_SVP_REQUIRED; + jtypeRights[1] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_1_4_ALLOWED; + jtypeRights[2] = SEC_KEYOUTPUTRIGHT_DIGITAL_OPL_HDCP_2_2_ALLOWED; + b64rights = toB64(jtypeRights, SEC_KEYOUTPUTRIGHT_NUM); + + do { + /* key avail in one hour */ + std::string jtype = createJTypeContainer("1WXQ46EYW65SENER", "HS256", g_default_jtype_data.contentKey, + g_default_jtype_data.encryptionKey, "9c621060-3a17-4813-8dcb-2e9187aaa903", b64rights.c_str(), SEC_TRUE, + 1, notBeforeTimeStr, notOnOrAfterTimeStr, g_default_jtype_data.macKey, version, alg); + if (jtype.empty()) { + SEC_LOG_ERROR("CreateJTypeContainer failed"); + break; + } + + if ((keyHandle = provisionJTypeAndSession(ctx, jtype)) == nullptr) { + break; + } + + if (SecCipher_GetInstance(ctx.proc(), SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_ENCRYPT, keyHandle, iv, + &cipherHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_GetInstance failed"); + break; + } + + if (SecCipher_ProcessCtrWithDataShift(cipherHandle, clear_text, sizeof(clear_text), cipher_text, + sizeof(cipher_text), &bytesWritten, 8) == SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Expected SecCipher_ProcessCtrWithDataShift to fail when processing non-opaque buffer with" + " SVP required"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (cipherHandle != nullptr) + SecCipher_Release(cipherHandle); + + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + return result; +} + +Sec_Result testKeyCtrlKeyExportSmallBuffer() { + Sec_Result result = SEC_RESULT_FAILURE; + + Sec_KeyHandle* keyHandle = nullptr; + SEC_BYTE exported_key[8]; + SEC_SIZE exported_key_len = 0; + SEC_OBJECTID id = SEC_OBJECTID_USER_BASE + 50; + Sec_ProcessorHandle* processorHandle = nullptr; + Sec_StorageLoc location = SEC_STORAGELOC_RAM; + TestCtx ctx; + + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector aesKey = TestCtx::random(SEC_AES_BLOCK_SIZE); + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(ctx.proc(), id, location, SEC_KEYCONTAINER_RAW_AES_128, &aesKey[0], aesKey.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + do { + if (SecKey_GetInstance(ctx.proc(), id, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + break; + } + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], exported_key, sizeof(exported_key), &exported_key_len) == + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Expected SecKey_ExportKey to fail with under-sized output buffer"); + break; + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + if (keyHandle != nullptr) + SecKey_Release(keyHandle); + + return result; +} diff --git a/test/main/cpp/keyctrl.h b/test/main/cpp/keyctrl.h new file mode 100644 index 0000000..9322211 --- /dev/null +++ b/test/main/cpp/keyctrl.h @@ -0,0 +1,106 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef KEYCTRL_H +#define KEYCTRL_H + +#include "sec_security.h" +#include "test_creds.h" +#include + +Sec_Result testKeyCtrlDefaultProperties(int version, const char *alg); + +Sec_Result testKeyCtrlFalseDefaultProperties(int version, const char *alg); + +/* test expected platform default rights */ +Sec_Result testKeyCtrlRightsFromPlatformDefault(int version, const char *alg); + +/* gets output rights from either default (hard-coded) values or devicesettings + * depending on compile directive -DSEC_KEYCTRL_ENABLE_DEVICE_SETTINGS + */ +Sec_Result testKeyCtrlGetOutputRights(int version, const char *alg); + +/* Sec_GetCipherInstance fails with request right that is not allowed on device */ +Sec_Result testKeyCtrlUnSupportedOutputRight(int version, const char *alg); + +/* En/Decrypt successful with key requesting a right that is allowed on the device */ +Sec_Result testKeyCtrlDecryptSupportedOutputRight(int version, const char *alg); + +/* SecKey_Export fails if cacheable flag == 0 */ +Sec_Result testKeyCtrlExportUnCachable(int version, const char *alg); + +/* general rights combinations */ +Sec_Result testKeyCtrlAllowedRights(int version, const char *alg); + +/* SecCipher_GetInstance fails for key with usage for 'key' only */ +Sec_Result testKeyCtrlKeyOnlyUsage(int version, const char *alg); + +/* SecCipher_Getinstance should fail with notOnOrAfter date < now */ +Sec_Result testKeyCtrlKeyExpired(int version, const char *alg); + +/* SecCipher_GetInstance should fail with notBefore date in the future */ +Sec_Result testKeyCtrlKeyNotYetAvail(int version, const char *alg); + +/* test provision fails for date > 2038/01/19 date on 32bit machines. */ +Sec_Result testKeyCtrlProvision32bit2038(int version, const char *alg); + +/* key properties vaidation */ +Sec_Result testKeyCtrlValidatePropertiesBadNotBefore(int version, const char *alg); + +Sec_Result testKeyCtrlValidatePropertiesBadNotOnOrAfter(int version, const char *alg); + +Sec_Result testKeyCtrlValidatePropertiesDefaults(int version, const char *alg); + +Sec_Result testKeyCtrlUtilsTime(int version, const char *alg); + +Sec_Result testKeyCtrlUnwrapWithKeyUsage(int version, const char *alg, TestKey key); + +Sec_Result testKeyCtrlUnwrapWithDataUsage(int version, const char *alg); + +Sec_Result testKeyCtrlBadB64Jtype(int version, const char *alg); + +Sec_Result testKeyCtrlExportDerived(); + +Sec_Result testKeyCtrlExpectedJTypeProperties(int version, const char *alg, TestKey key); + +Sec_Result testKeyCtrlExpectedExportedProperties(int version, const char *alg, TestKey key); + +Sec_Result testKeyCtrlExportProvisionExport(int version, const char *alg, TestKey contentKey); + +Sec_Result testKeyCtrlKeyExportGetSize(int version, const char *alg); + +Sec_Result testKeyCtrlExportAes(TestKey aesKey, Sec_StorageLoc location); + +Sec_Result testKeyCtrlKeyExportHmac(TestKey macKey, Sec_StorageLoc location); + +Sec_Result testKeyCtrlCipherFailsSvpNonOpaque(int version, const char *alg, Sec_CipherAlgorithm cipher_algorithm); + +Sec_Result testKeyCtrlCipherSvpOpaque(int version, const char *alg, TestKey contentKey, + Sec_CipherAlgorithm cipher_algorithm); + +Sec_Result testKeyCtrlCipherSvpDataShiftOpaque(int version, const char *alg); + +Sec_Result testKeyCtrlSvpCheckOpaque(int version, const char *alg, TestKey contentKey); + +Sec_Result testKeyCtrlProcessCtrDataShiftFailsSvpNonOpaque(int version, const char *alg); + +Sec_Result testKeyCtrlExportEcc(TestKc kc); + +Sec_Result testKeyCtrlKeyExportSmallBuffer(); + +#endif // KEYCTRL_H diff --git a/test/main/cpp/mac.cpp b/test/main/cpp/mac.cpp new file mode 100644 index 0000000..e1058fd --- /dev/null +++ b/test/main/cpp/mac.cpp @@ -0,0 +1,306 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mac.h" +#include "test_ctx.h" +#include +#include + +std::vector macOpenSSL(Sec_MacAlgorithm alg, const std::vector& openssl_key, + const std::vector& input) { + std::vector mac; + SEC_SIZE mac_len; + CMAC_CTX* cmac_ctx; + + switch (alg) { + case SEC_MACALGORITHM_HMAC_SHA1: + mac.resize(20); + HMAC(EVP_sha1(), &openssl_key[0], static_cast(openssl_key.size()), &input[0], input.size(), &mac[0], + &mac_len); + return mac; + + case SEC_MACALGORITHM_HMAC_SHA256: + mac.resize(32); + HMAC(EVP_sha256(), &openssl_key[0], static_cast(openssl_key.size()), &input[0], input.size(), &mac[0], + &mac_len); + return mac; + + case SEC_MACALGORITHM_CMAC_AES_128: { + mac.resize(SEC_AES_BLOCK_SIZE); + cmac_ctx = CMAC_CTX_new(); + if (CMAC_Init(cmac_ctx, &openssl_key[0], openssl_key.size(), + openssl_key.size() == SEC_AES_BLOCK_SIZE ? EVP_aes_128_cbc() : EVP_aes_256_cbc(), nullptr) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("Comcast_CMAC_Init failed"); + return {}; + } + CMAC_Update(cmac_ctx, &input[0], input.size()); + size_t outl; + CMAC_Final(cmac_ctx, &mac[0], &outl); + mac_len = outl; + CMAC_CTX_free(cmac_ctx); + return mac; + } + default: + break; + } + + SEC_LOG_ERROR("Unimplemented"); + return {}; +} + +std::vector macOpenSSL(Sec_MacAlgorithm alg, TestKey key, const std::vector& input) { + std::vector mac; + SEC_SIZE mac_len; + CMAC_CTX* cmac_ctx; + + std::vector openssl_key = TestCreds::asOpenSslAes(key); + if (openssl_key.empty()) { + SEC_LOG_ERROR("TestCreds::asOpenSslAes failed"); + return {}; + } + + switch (alg) { + case SEC_MACALGORITHM_HMAC_SHA1: + mac.resize(20); + HMAC(EVP_sha1(), &openssl_key[0], static_cast(openssl_key.size()), &input[0], input.size(), &mac[0], + &mac_len); + return mac; + + case SEC_MACALGORITHM_HMAC_SHA256: + mac.resize(32); + HMAC(EVP_sha256(), &openssl_key[0], static_cast(openssl_key.size()), &input[0], input.size(), &mac[0], + &mac_len); + return mac; + + case SEC_MACALGORITHM_CMAC_AES_128: { + mac.resize(SEC_AES_BLOCK_SIZE); + cmac_ctx = CMAC_CTX_new(); + if (CMAC_Init(cmac_ctx, &openssl_key[0], openssl_key.size(), + openssl_key.size() == SEC_AES_BLOCK_SIZE ? EVP_aes_128_cbc() : EVP_aes_256_cbc(), nullptr) != + OPENSSL_SUCCESS) { + SEC_LOG_ERROR("Comcast_CMAC_Init failed"); + return {}; + } + CMAC_Update(cmac_ctx, &input[0], input.size()); + size_t outl; + CMAC_Final(cmac_ctx, &mac[0], &outl); + mac_len = outl; + CMAC_CTX_free(cmac_ctx); + return mac; + } + default: + break; + } + + SEC_LOG_ERROR("Unimplemented"); + return {}; +} + +std::vector macSecApi(TestCtx* ctx, Sec_MacAlgorithm alg, Sec_KeyHandle* keyHandle, + const std::vector& input, const std::vector& inputSizes) { + + std::vector output; + output.resize(SEC_MAC_MAX_LEN); + + SEC_SIZE inputProcessed = 0; + SEC_SIZE written = 0; + + Sec_MacHandle* macHandle = ctx->acquireMac(alg, keyHandle); + if (macHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireMac failed"); + return {}; + } + + for (unsigned int inputSize : inputSizes) { + if (inputSize > 0) { + if (SecMac_Update(macHandle, const_cast(&input[inputProcessed]), inputSize) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecMac_Update failed"); + return {}; + } + } + + inputProcessed += inputSize; + } + + if (ctx->releaseMac(macHandle, &output[0], &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_Process failed"); + return {}; + } + + output.resize(written); + + return output; +} + +std::vector macSecApi(TestCtx* ctx, Sec_MacAlgorithm alg, Sec_KeyHandle* keyHandle, + Sec_KeyHandle* paylodKeyHandle) { + + std::vector output; + output.resize(SEC_MAC_MAX_LEN); + + SEC_SIZE written = 0; + + Sec_MacHandle* macHandle = ctx->acquireMac(alg, keyHandle); + if (macHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireMac failed"); + return {}; + } + + if (SecMac_UpdateWithKey(macHandle, paylodKeyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_Update failed"); + return {}; + } + + if (ctx->releaseMac(macHandle, &output[0], &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_Process failed"); + return {}; + } + + output.resize(written); + + return output; +} + +Sec_Result testMacOverKey(Sec_MacAlgorithm alg, SEC_OBJECTID id_mac, TestKey keyMac, + TestKc kc, SEC_OBJECTID id_payload, TestKey keyPayload, + Sec_StorageLoc loc) { + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandleMac; + if ((keyHandleMac = ctx.provisionKey(id_mac, loc, keyMac, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandlePayload; + if ((TestCreds::supports(CAPABILITY_HMAC_OVER_HWKEY) && alg != SEC_MACALGORITHM_CMAC_AES_128) || + (TestCreds::supports(CAPABILITY_CMAC_OVER_HWKEY) && alg == SEC_MACALGORITHM_CMAC_AES_128)) { + if ((keyHandlePayload = ctx.provisionKey(id_payload, loc, keyPayload, TESTKC_RAW)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + } else { + if ((keyHandlePayload = ctx.provisionKey(id_payload, loc, keyPayload, TESTKC_RAW, SEC_TRUE)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + } + + //gen clear input + std::vector clear = TestCreds::asOpenSslAes(keyPayload); + TestCtx::printHex("key", clear); + + //mac + std::vector macSA = macSecApi(&ctx, alg, keyHandleMac, keyHandlePayload); + TestCtx::printHex("macSecApi", macSA); + + std::vector macOS = macOpenSSL(alg, keyMac, clear); + TestCtx::printHex("macOpenssl", macOS); + + //check if results match + if (macSA != macOS || macSA.empty()) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testMacSingle(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_MacAlgorithm alg, + SEC_SIZE inputSize) { + std::vector inputSizes; + inputSizes.resize(1); + inputSizes[0] = inputSize; + + return testMacMult(id, key, kc, loc, alg, inputSizes); +} + +Sec_Result testMacMult(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_MacAlgorithm alg, + const std::vector& inputSizes) { + + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle; + if ((keyHandle = ctx.provisionKey(id, loc, key, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //gen clear input + std::vector clear = TestCtx::random(TestCtx::coalesceInputSizes(inputSizes)); + TestCtx::printHex("clear", clear); + + //mac + std::vector macSA = macSecApi(&ctx, alg, keyHandle, clear, inputSizes); + TestCtx::printHex("macSecApi", macSA); + + std::vector macOS = macOpenSSL(alg, key, clear); + TestCtx::printHex("macOpenssl", macOS); + + //check if results match + if (macSA != macOS) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result macCheck(Sec_ProcessorHandle* processorHandle, Sec_MacAlgorithm alg, SEC_OBJECTID id, SEC_BYTE* key, + SEC_SIZE key_len) { + std::vector mac_secapi; + mac_secapi.resize(SEC_MAC_MAX_LEN); + SEC_SIZE mac_len; + + std::vector clear = TestCtx::random(256); + TestCtx::printHex("clear", clear); + + if (SecMac_SingleInputId(processorHandle, alg, id, &clear[0], clear.size(), &mac_secapi[0], &mac_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecMac_SingleInputId failed"); + return SEC_RESULT_FAILURE; + } + + mac_secapi.resize(mac_len); + TestCtx::printHex("macSecApi", mac_secapi); + + std::vector openssl_key = std::vector(key, key + key_len); + + std::vector macOS = macOpenSSL(alg, openssl_key, clear); + TestCtx::printHex("macOpenssl", macOS); + + //check if results match + if (mac_secapi != macOS) { + SEC_LOG_ERROR("Results do not match"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} diff --git a/test/main/cpp/mac.h b/test/main/cpp/mac.h new file mode 100644 index 0000000..aee9c90 --- /dev/null +++ b/test/main/cpp/mac.h @@ -0,0 +1,40 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef MAC_H +#define MAC_H + +#include "sec_security.h" +#include "test_creds.h" +#include + +Sec_Result testMacOverKey(Sec_MacAlgorithm alg, SEC_OBJECTID id_mac, TestKey keyMac, TestKc kc, SEC_OBJECTID id_payload, + TestKey keyPayload, Sec_StorageLoc loc); + +Sec_Result testMacSingle(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_MacAlgorithm alg, + SEC_SIZE inputSize); + +Sec_Result testMacMult(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, Sec_MacAlgorithm alg, + const std::vector& inputSizes); + +std::vector macOpenSSL(Sec_MacAlgorithm alg, TestKey key, const std::vector& input); + +Sec_Result macCheck(Sec_ProcessorHandle* processorHandle, Sec_MacAlgorithm alg, SEC_OBJECTID id, SEC_BYTE* key, + SEC_SIZE key_len); + +#endif // MAC_H diff --git a/test/main/cpp/processor.cpp b/test/main/cpp/processor.cpp new file mode 100644 index 0000000..cf2531a --- /dev/null +++ b/test/main/cpp/processor.cpp @@ -0,0 +1,114 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "processor.h" +#include "test_ctx.h" + +Sec_Result testProcessorPrintInfo() { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if (SecProcessor_PrintInfo(ctx.proc()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecProcessor_PrintInfo failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testProcessorGetInfo() { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_ProcessorInfo info; + + if (SecProcessor_GetInfo(ctx.proc(), &info) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecProcessor_GetInfo failed"); + return SEC_RESULT_FAILURE; + } + + SEC_PRINT("version: %s\n", info.version); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testProcessorGetDeviceId() { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + std::vector deviceId(SEC_DEVICEID_LEN); + std::vector zeros(SEC_DEVICEID_LEN); + + if (SecProcessor_GetDeviceId(ctx.proc(), &deviceId[0]) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecProcessor_GetDeviceId failed"); + return SEC_RESULT_FAILURE; + } + + TestCtx::printHex("deviceid", deviceId); + + if (deviceId == zeros) { + SEC_LOG_ERROR("DeviceId is all zeros"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testProcessorGetKeyLadderMinMaxDepth(Sec_KeyLadderRoot root) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + SEC_SIZE depthMin = SecProcessor_GetKeyLadderMinDepth(ctx.proc(), root); + SEC_SIZE depthMax = SecProcessor_GetKeyLadderMaxDepth(ctx.proc(), root); + + SEC_PRINT("min:%d\nmax:%d\n", depthMin, depthMax); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testProcessorNativeMallocFree() { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + void* mem = Sec_NativeMalloc(ctx.proc(), 256); + memset(mem, 0, 256); + + if (mem == nullptr) { + SEC_LOG_ERROR("Sec_NativeMalloc failed"); + return SEC_RESULT_FAILURE; + } + + Sec_NativeFree(ctx.proc(), mem); + + return SEC_RESULT_SUCCESS; +} diff --git a/test/main/cpp/processor.h b/test/main/cpp/processor.h new file mode 100644 index 0000000..dbfaa88 --- /dev/null +++ b/test/main/cpp/processor.h @@ -0,0 +1,34 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef PROCESSOR_H +#define PROCESSOR_H + +#include "sec_security.h" + +Sec_Result testProcessorPrintInfo(); + +Sec_Result testProcessorGetDeviceId(); + +Sec_Result testProcessorGetKeyLadderMinMaxDepth(Sec_KeyLadderRoot root); + +Sec_Result testProcessorNativeMallocFree(); + +Sec_Result testProcessorGetInfo(); + +#endif // PROCESSOR_H diff --git a/test/main/cpp/random.cpp b/test/main/cpp/random.cpp new file mode 100644 index 0000000..ee26755 --- /dev/null +++ b/test/main/cpp/random.cpp @@ -0,0 +1,47 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "random.h" +#include "test_ctx.h" + +Sec_Result testRandom(Sec_RandomAlgorithm alg, SEC_SIZE size) { + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_RandomHandle* randomHandle; + if ((randomHandle = ctx.acquireRandom(alg)) == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireRandom failed"); + return SEC_RESULT_FAILURE; + } + + std::vector out; + out.resize(size); + + if (SecRandom_Process(randomHandle, &out[0], out.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecRandom_Process failed"); + return SEC_RESULT_FAILURE; + } + + TestCtx::printHex("out", out); + + return SEC_RESULT_SUCCESS; +} diff --git a/test/main/cpp/random.h b/test/main/cpp/random.h new file mode 100644 index 0000000..b13f5c5 --- /dev/null +++ b/test/main/cpp/random.h @@ -0,0 +1,26 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef RANDOM_H +#define RANDOM_H + +#include "sec_security.h" + +Sec_Result testRandom(Sec_RandomAlgorithm alg, SEC_SIZE size); + +#endif // RANDOM_H diff --git a/test/main/cpp/sec_api_utest_main.cpp b/test/main/cpp/sec_api_utest_main.cpp new file mode 100644 index 0000000..bc9f67b --- /dev/null +++ b/test/main/cpp/sec_api_utest_main.cpp @@ -0,0 +1,1365 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_api_utest_main.h" // NOLINT +#include "bundle.h" +#include "cert.h" +#include "cipher.h" +#include "concurrent.h" +#include "digest.h" +#include "exchange.h" +#include "jtype.h" +#include "key.h" +#include "keyctrl.h" +#include "mac.h" +#include "processor.h" +#include "random.h" +#include "sec_security.h" +#include "sign.h" +#include "svp.h" +#include "test_creds.h" +#include "test_ctx.h" +#include "wrapped.h" +#include +#include + +#ifdef SEC_PLATFORM_OPENSSL +#include "sec_adapter_openssl.h" +#endif + +void runProcessorTests(SuiteCtx* suite) { + RUN_TEST(suite, testProcessorPrintInfo()) + RUN_TEST(suite, testProcessorGetInfo()) + RUN_TEST(suite, testProcessorGetDeviceId()) + RUN_TEST(suite, testProcessorGetKeyLadderMinMaxDepth(SEC_KEYLADDERROOT_UNIQUE)) + RUN_TEST(suite, testProcessorGetKeyLadderMinMaxDepth(SEC_KEYLADDERROOT_SHARED)) + RUN_TEST(suite, testProcessorNativeMallocFree()) +} + +void runBundleTests(SuiteCtx* suite) { + RUN_TEST(suite, testBundleProvision(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, 256)) + RUN_TEST(suite, testBundleProvision(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_FILE, 256)) + RUN_TEST(suite, testBundleProvisionNoAppDir(SEC_OBJECTID_USER_BASE, 256)) + RUN_TEST(suite, testBundleProvisionNoSha(SEC_OBJECTID_USER_BASE)) +} + +#define CERT_TESTS_LOC(suite, cert, loc) \ + do { \ + RUN_TEST(suite, testCertProvision(SEC_OBJECTID_USER_BASE, cert, loc)); \ + RUN_TEST(suite, testCertExport(SEC_OBJECTID_USER_BASE, cert, loc)); \ + RUN_TEST(suite, testCertExportNoSha(SEC_OBJECTID_USER_BASE, cert)); \ + } while (0) + +#define CERT_TESTS(suite, cert, pub_key) \ + do { \ + CERT_TESTS_LOC(suite, cert, SEC_STORAGELOC_RAM); \ + CERT_TESTS_LOC(suite, cert, SEC_STORAGELOC_FILE); \ + RUN_TEST(suite, testCertVerify(SEC_OBJECTID_USER_BASE, cert, SEC_OBJECTID_USER_BASE + 1, pub_key, \ + SEC_STORAGELOC_RAM)); \ + RUN_TEST(suite, testCertVerify(SEC_OBJECTID_USER_BASE, cert, SEC_OBJECTID_USER_BASE + 1, pub_key, \ + SEC_STORAGELOC_RAM_SOFT_WRAPPED)); \ + } while (0) + +void runCertTests(SuiteCtx* suite) { // NOLINT + if (TestCreds::supports(CAPABILITY_RSA_1024)) { + CERT_TESTS(suite, TESTCERT_RSA1024, TESTKEY_RSA1024_SGN_PUB); + } + CERT_TESTS(suite, TESTCERT_RSA2048, TESTKEY_RSA2048_SGN_PUB); + CERT_TESTS(suite, TESTCERT_EC, TESTKEY_EC_PUB); + RUN_TEST(suite, testCertSignWithPkcs7(SEC_OBJECTID_USER_BASE, TESTCERT_RSA2048, SEC_OBJECTID_USER_BASE + 1, \ + TESTKEY_RSA2048_SGN_PRIV, SEC_STORAGELOC_RAM_SOFT_WRAPPED)) +} + +#define KEY_EXPORT_TESTS(suite, contentKey, encryptionKey, encKc, macKey, version, alg) \ + do { \ + RUN_TEST(suite, testExportKey(contentKey, encryptionKey, encKc, macKey, TESTKC_RAW, SEC_CIPHERALGORITHM_AES_CTR, \ + 20, version, alg)); \ + RUN_TEST(suite, testExportKey(contentKey, encryptionKey, encKc, macKey, TESTKC_RAW, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 32, version, alg)); \ + RUN_TEST(suite, testExportKey(contentKey, encryptionKey, encKc, macKey, TESTKC_RAW, \ + SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING, 20, version, alg)); \ + RUN_TEST(suite, testExportKey(contentKey, encryptionKey, encKc, macKey, TESTKC_RAW, \ + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 32, version, alg)); \ + RUN_TEST(suite, testExportKey(contentKey, encryptionKey, encKc, macKey, TESTKC_RAW, \ + SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING, 20, version, alg)); \ + } while (0) + +#define JTYPE_DECRYPT_TESTS(suite, contentKey, encryptionKey, encKc, macKey, version, alg) \ + do { \ + RUN_TEST(suite, testDecryptJType(contentKey, encryptionKey, encKc, macKey, TESTKC_RAW, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 256, version, alg)); \ + RUN_TEST(suite, testDecryptJType(contentKey, encryptionKey, encKc, macKey, TESTKC_RAW, \ + SEC_CIPHERALGORITHM_AES_CTR, 256, version, alg)); \ + RUN_TEST(suite, testDecryptJType(contentKey, encryptionKey, encKc, macKey, TESTKC_RAW, \ + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256, version, alg)); \ + } while (0) + +#define JTYPE_TESTS(suite, contentKey, encryptionKey, macKey, version) \ + do { \ + if ((version) == 1) { \ + if (TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC)) { \ + RUN_TEST(suite, testProvisionJType(contentKey, encryptionKey, TESTKC_CONDITIONAL, macKey, TESTKC_RAW, 1, \ + "aesEcbNone")); \ + KEY_EXPORT_TESTS(suite, contentKey, encryptionKey, TESTKC_CONDITIONAL, macKey, 1, "aesEcbNone"); \ + JTYPE_DECRYPT_TESTS(suite, contentKey, encryptionKey, TESTKC_CONDITIONAL, macKey, 1, "aesEcbNone"); \ + } \ + if (TestCreds::supports(CAPABILITY_CLEAR_JTYPE_WRAPPING)) { \ + RUN_TEST(suite, testProvisionJType(contentKey, encryptionKey, TESTKC_RAW, macKey, TESTKC_RAW, 1, \ + "aesEcbNone")); \ + KEY_EXPORT_TESTS(suite, contentKey, encryptionKey, TESTKC_RAW, macKey, 1, "aesEcbNone"); \ + JTYPE_DECRYPT_TESTS(suite, contentKey, encryptionKey, TESTKC_RAW, macKey, 1, "aesEcbNone"); \ + } \ + } else if ((version) == 2) { \ + if (TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC)) { \ + RUN_TEST(suite, testProvisionJType(contentKey, encryptionKey, TESTKC_CONDITIONAL, macKey, TESTKC_RAW, 2, \ + "aesEcbNone")); \ + RUN_TEST(suite, testProvisionJType(contentKey, encryptionKey, TESTKC_CONDITIONAL, macKey, TESTKC_RAW, 2, \ + "aesEcbPkcs5")); \ + KEY_EXPORT_TESTS(suite, contentKey, encryptionKey, TESTKC_CONDITIONAL, macKey, 2, "aesEcbNone"); \ + KEY_EXPORT_TESTS(suite, contentKey, encryptionKey, TESTKC_CONDITIONAL, macKey, 2, "aesEcbPkcs5"); \ + JTYPE_DECRYPT_TESTS(suite, contentKey, encryptionKey, TESTKC_CONDITIONAL, macKey, 2, "aesEcbNone"); \ + JTYPE_DECRYPT_TESTS(suite, contentKey, encryptionKey, TESTKC_CONDITIONAL, macKey, 2, "aesEcbPkcs5"); \ + } \ + if (TestCreds::supports(CAPABILITY_CLEAR_JTYPE_WRAPPING)) { \ + RUN_TEST(suite, testProvisionJType(contentKey, encryptionKey, TESTKC_RAW, macKey, TESTKC_RAW, 2, \ + "aesEcbNone")); \ + RUN_TEST(suite, testProvisionJType(contentKey, encryptionKey, TESTKC_RAW, macKey, TESTKC_RAW, 2, \ + "aesEcbPkcs5")); \ + KEY_EXPORT_TESTS(suite, contentKey, encryptionKey, TESTKC_RAW, macKey, 2, "aesEcbNone"); \ + KEY_EXPORT_TESTS(suite, contentKey, encryptionKey, TESTKC_RAW, macKey, 2, "aesEcbPkcs5"); \ + JTYPE_DECRYPT_TESTS(suite, contentKey, encryptionKey, TESTKC_RAW, macKey, 2, "aesEcbNone"); \ + JTYPE_DECRYPT_TESTS(suite, contentKey, encryptionKey, TESTKC_RAW, macKey, 2, "aesEcbPkcs5"); \ + } \ + } \ + } while (0) + +void runProvisionedKeyExportTest(SuiteCtx* suite) { + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_AES128, TESTKC_RAW)) + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_AES256, TESTKC_RAW)) + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_HMAC128, TESTKC_RAW)) + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_HMAC160, TESTKC_RAW)) + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_HMAC256, TESTKC_RAW)) + + if (TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC)) { + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_AES128, TESTKC_CONDITIONAL)) + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_AES256, TESTKC_CONDITIONAL)) + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_HMAC128, TESTKC_CONDITIONAL)) + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_HMAC160, TESTKC_CONDITIONAL)) + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_HMAC256, TESTKC_CONDITIONAL)) + + if (TestCreds::supports(CAPABILITY_EXPORT_RSA) && TestCreds::supports(CAPABILITY_RSA_1024)) { + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_RSA1024_SGN_PRIV, TESTKC_CONDITIONAL)) + } + if (TestCreds::supports(CAPABILITY_EXPORT_RSA)) { + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_RSA2048_SGN_PRIV, TESTKC_CONDITIONAL)) + } + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_EC_PRIV, TESTKC_CONDITIONAL)) + } + + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_AES128, TESTKC_GENERATED)) + RUN_TEST(suite, testExportProvisionedKey(TESTKEY_AES256, TESTKC_GENERATED)) +} + +void runKeyTests(SuiteCtx* suite) { // NOLINT + RUN_TEST(suite, testStore(SEC_TRUE, SEC_TRUE)) + RUN_TEST(suite, testStore(SEC_TRUE, SEC_FALSE)) + RUN_TEST(suite, testStore(SEC_FALSE, SEC_TRUE)) + RUN_TEST(suite, testStore(SEC_FALSE, SEC_FALSE)) + RUN_TEST(suite, testStoreProvision(SEC_OBJECTID_USER_BASE, SEC_TRUE, SEC_TRUE)) + RUN_TEST(suite, testStoreProvision(SEC_OBJECTID_USER_BASE, SEC_TRUE, SEC_FALSE)) + RUN_TEST(suite, testStoreProvision(SEC_OBJECTID_USER_BASE, SEC_FALSE, SEC_TRUE)) + RUN_TEST(suite, testStoreProvision(SEC_OBJECTID_USER_BASE, SEC_FALSE, SEC_FALSE)) + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, SEC_STORAGELOC_FILE)) + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, + SEC_STORAGELOC_FILE_SOFT_WRAPPED)) + + RUN_TEST(suite, testKeyProvisionDouble(SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, + SEC_STORAGELOC_RAM_SOFT_WRAPPED, TESTKEY_AES128, TESTKC_RAW, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_HMAC128, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_HMAC160, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_HMAC256, TESTKC_RAW, SEC_STORAGELOC_RAM)) + + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_HMAC128, TESTKC_RAW, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_HMAC160, TESTKC_RAW, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_HMAC256, TESTKC_RAW, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + + JTYPE_TESTS(suite, TESTKEY_AES128, TESTKEY_AES128, TESTKEY_HMAC160, 1); + JTYPE_TESTS(suite, TESTKEY_AES128, TESTKEY_AES128, TESTKEY_HMAC160, 2); + JTYPE_TESTS(suite, TESTKEY_AES256, TESTKEY_AES128, TESTKEY_HMAC160, 2); + + RUN_TEST(suite, testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_HMAC128, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_HMAC160, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_HMAC256, TESTKC_RAW, SEC_STORAGELOC_RAM)) + + RUN_TEST(suite, + testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, + testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_HMAC128, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, + testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_HMAC160, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, + testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_HMAC256, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + + RUN_TEST(suite, testKeyNoSha(SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW)) + RUN_TEST(suite, testKeyGenerate(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, SEC_TRUE)) + RUN_TEST(suite, testKeyGenerate(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_HMAC_128, SEC_STORAGELOC_RAM, SEC_FALSE)) + RUN_TEST(suite, testKeyGenerate(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_HMAC_160, SEC_STORAGELOC_RAM, SEC_FALSE)) + RUN_TEST(suite, testKeyGenerate(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_HMAC_256, SEC_STORAGELOC_RAM, SEC_FALSE)) + RUN_TEST(suite, testKeyGenerate(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_ECC_NISTP256, SEC_STORAGELOC_RAM, SEC_FALSE)) + + RUN_TEST(suite, testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_GENERATED, SEC_STORAGELOC_RAM)) + + RUN_TEST(suite, testKeyDeriveHKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_MACALGORITHM_HMAC_SHA1, SEC_TRUE, SEC_TRUE)) + RUN_TEST(suite, testKeyDeriveHKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_MACALGORITHM_HMAC_SHA1, SEC_TRUE, SEC_FALSE)) + RUN_TEST(suite, testKeyDeriveHKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_MACALGORITHM_HMAC_SHA256, SEC_TRUE, SEC_TRUE)) + RUN_TEST(suite, testKeyDeriveHKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_MACALGORITHM_HMAC_SHA256, SEC_TRUE, SEC_FALSE)) + if (TestCreds::supports(CAPABILITY_HKDF_CMAC)) { + RUN_TEST(suite, testKeyDeriveHKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_MACALGORITHM_CMAC_AES_128, SEC_TRUE, SEC_TRUE)) + RUN_TEST(suite, testKeyDeriveHKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_MACALGORITHM_CMAC_AES_128, SEC_TRUE, SEC_FALSE)) + } + RUN_TEST(suite, testKeyDeriveConcatKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_DIGESTALGORITHM_SHA1, SEC_TRUE)) + RUN_TEST(suite, testKeyDeriveConcatKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_DIGESTALGORITHM_SHA256, SEC_TRUE)) + + RUN_TEST(suite, testKeyDeriveHKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + SEC_MACALGORITHM_HMAC_SHA1, SEC_TRUE, SEC_TRUE)) + RUN_TEST(suite, testKeyDeriveHKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + SEC_MACALGORITHM_HMAC_SHA1, SEC_TRUE, SEC_FALSE)) + RUN_TEST(suite, testKeyDeriveHKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + SEC_MACALGORITHM_HMAC_SHA256, SEC_TRUE, SEC_TRUE)) + RUN_TEST(suite, testKeyDeriveHKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + SEC_MACALGORITHM_HMAC_SHA256, SEC_TRUE, SEC_FALSE)) + if (TestCreds::supports(CAPABILITY_HKDF_CMAC)) { + RUN_TEST(suite, testKeyDeriveHKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + SEC_MACALGORITHM_CMAC_AES_128, SEC_TRUE, SEC_TRUE)) + RUN_TEST(suite, testKeyDeriveHKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + SEC_MACALGORITHM_CMAC_AES_128, SEC_TRUE, SEC_FALSE)) + } + RUN_TEST(suite, testKeyDeriveConcatKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + SEC_DIGESTALGORITHM_SHA1, SEC_TRUE)) + RUN_TEST(suite, testKeyDeriveConcatKDF(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + SEC_DIGESTALGORITHM_SHA256, SEC_TRUE)) + + RUN_TEST(suite, testKeyDeriveVendorAes128(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + SEC_MACALGORITHM_HMAC_SHA1, SEC_TRUE)) + RUN_TEST(suite, testKeyDeriveVendorAes128(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + SEC_MACALGORITHM_HMAC_SHA256, SEC_TRUE)) + RUN_TEST(suite, testKeyDeriveKeyLadderAes128(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + SEC_KEYLADDERROOT_UNIQUE, SEC_TRUE)) + + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, SEC_TRUE, 1, 256)) + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, SEC_TRUE, 2, 256)) + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, SEC_TRUE, 3, 512)) + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, SEC_TRUE, 4, 512)) + + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_GENERATED, + SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, SEC_TRUE, 1, 256)) + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_GENERATED, + SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, SEC_TRUE, 2, 256)) + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_GENERATED, + SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, SEC_TRUE, 3, 512)) + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_GENERATED, + SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, SEC_TRUE, 4, 512)) + + if (TestCreds::supports(CAPABILITY_AES256)) { + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_AES_256, SEC_STORAGELOC_RAM, SEC_TRUE, 1, 256)) + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_AES_256, SEC_STORAGELOC_RAM, SEC_TRUE, 2, 512)) + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_AES_256, SEC_STORAGELOC_RAM, SEC_TRUE, 3, 512)) + } + + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_HMAC_128, SEC_STORAGELOC_RAM, SEC_FALSE, 1, 256)) + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_HMAC_128, SEC_STORAGELOC_RAM, SEC_FALSE, 2, 256)) + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_HMAC_128, SEC_STORAGELOC_RAM, SEC_FALSE, 3, 512)) + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_HMAC_128, SEC_STORAGELOC_RAM, SEC_FALSE, 4, 512)) + + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_HMAC_256, SEC_STORAGELOC_RAM, SEC_FALSE, 1, 512)) + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_HMAC_256, SEC_STORAGELOC_RAM, SEC_FALSE, 2, 512)) + RUN_TEST(suite, testKeyDeriveCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKC_CONDITIONAL, + SEC_KEYTYPE_HMAC_256, SEC_STORAGELOC_RAM, SEC_FALSE, 3, 512)) + + RUN_TEST(suite, testKeyDeriveBaseKey(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM)) + + RUN_TEST(suite, testKeyDeriveHKDFBaseKey(SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA1)) + if (TestCreds::supports(CAPABILITY_AES256)) { + RUN_TEST(suite, testKeyDeriveHKDFBaseKey(SEC_KEYTYPE_AES_256, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA1)) + } + RUN_TEST(suite, testKeyDeriveHKDFBaseKey(SEC_KEYTYPE_HMAC_128, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA1)) + RUN_TEST(suite, testKeyDeriveHKDFBaseKey(SEC_KEYTYPE_HMAC_160, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA1)) + RUN_TEST(suite, testKeyDeriveHKDFBaseKey(SEC_KEYTYPE_HMAC_256, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA1)) + + RUN_TEST(suite, testKeyDeriveHKDFBaseKey(SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA256)) + if (TestCreds::supports(CAPABILITY_AES256)) { + RUN_TEST(suite, + testKeyDeriveHKDFBaseKey(SEC_KEYTYPE_AES_256, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA256)) + } + RUN_TEST(suite, testKeyDeriveHKDFBaseKey(SEC_KEYTYPE_HMAC_128, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA256)) + RUN_TEST(suite, testKeyDeriveHKDFBaseKey(SEC_KEYTYPE_HMAC_160, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA256)) + RUN_TEST(suite, testKeyDeriveHKDFBaseKey(SEC_KEYTYPE_HMAC_256, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA256)) + + RUN_TEST(suite, testKeyDeriveConcatKDFBaseKey(SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, SEC_DIGESTALGORITHM_SHA1)) + if (TestCreds::supports(CAPABILITY_AES256)) { + RUN_TEST(suite, + testKeyDeriveConcatKDFBaseKey(SEC_KEYTYPE_AES_256, SEC_STORAGELOC_RAM, SEC_DIGESTALGORITHM_SHA1)) + } + RUN_TEST(suite, testKeyDeriveConcatKDFBaseKey(SEC_KEYTYPE_HMAC_128, SEC_STORAGELOC_RAM, SEC_DIGESTALGORITHM_SHA1)) + RUN_TEST(suite, testKeyDeriveConcatKDFBaseKey(SEC_KEYTYPE_HMAC_160, SEC_STORAGELOC_RAM, SEC_DIGESTALGORITHM_SHA1)) + RUN_TEST(suite, testKeyDeriveConcatKDFBaseKey(SEC_KEYTYPE_HMAC_256, SEC_STORAGELOC_RAM, SEC_DIGESTALGORITHM_SHA1)) + + RUN_TEST(suite, testKeyDeriveConcatKDFBaseKey(SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, SEC_DIGESTALGORITHM_SHA256)) + if (TestCreds::supports(CAPABILITY_AES256)) { + RUN_TEST(suite, + testKeyDeriveConcatKDFBaseKey(SEC_KEYTYPE_AES_256, SEC_STORAGELOC_RAM, SEC_DIGESTALGORITHM_SHA256)) + } + RUN_TEST(suite, + testKeyDeriveConcatKDFBaseKey(SEC_KEYTYPE_HMAC_128, SEC_STORAGELOC_RAM, SEC_DIGESTALGORITHM_SHA256)) + RUN_TEST(suite, + testKeyDeriveConcatKDFBaseKey(SEC_KEYTYPE_HMAC_160, SEC_STORAGELOC_RAM, SEC_DIGESTALGORITHM_SHA256)) + RUN_TEST(suite, + testKeyDeriveConcatKDFBaseKey(SEC_KEYTYPE_HMAC_256, SEC_STORAGELOC_RAM, SEC_DIGESTALGORITHM_SHA256)) + + if (TestCreds::supports(CAPABILITY_AES256)) { + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_AES256, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_AES256, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyGenerate(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_256, SEC_STORAGELOC_RAM, SEC_TRUE)) + + RUN_TEST(suite, + testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_AES256, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_AES256, TESTKC_RAW, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyGenerate(SEC_OBJECTID_USER_BASE, SEC_KEYTYPE_AES_256, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_TRUE)) + } + + if (TestCreds::supports(CAPABILITY_RSA_1024)) { + RUN_TEST(suite, + testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_RSA1024_SGN_PUB, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, + testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_RSA1024_SGN_PUB, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyExtractPublicKey(SEC_OBJECTID_USER_BASE, TESTKEY_RSA1024_SGN_PUB, TESTKC_RAW, + SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_RSA1024_SGN_PUB, TESTKC_RAW, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_RSA1024_SGN_PUB, TESTKC_RAW, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyExtractPublicKey(SEC_OBJECTID_USER_BASE, TESTKEY_RSA1024_SGN_PUB, TESTKC_RAW, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, + testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_RSA1024_SGN_PRIV, TESTKC_CONDITIONAL, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_RSA1024_SGN_PRIV, TESTKC_CONDITIONAL, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, + testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_RSA1024_SGN_PRIV, TESTKC_CONDITIONAL, SEC_STORAGELOC_RAM)) + } + + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_RSA2048_SGN_PUB, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_RSA2048_SGN_PUB, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, + testKeyExtractPublicKey(SEC_OBJECTID_USER_BASE, TESTKEY_RSA2048_SGN_PUB, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_RSA2048_SGN_PUB, TESTKC_RAW, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_RSA2048_SGN_PUB, TESTKC_RAW, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyExtractPublicKey(SEC_OBJECTID_USER_BASE, TESTKEY_RSA2048_SGN_PUB, TESTKC_RAW, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_RSA2048_SGN_PRIV, TESTKC_CONDITIONAL, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_RSA2048_SGN_PRIV, TESTKC_CONDITIONAL, + SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, + testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_RSA2048_SGN_PRIV, TESTKC_CONDITIONAL, SEC_STORAGELOC_RAM)) + + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_EC_PUB, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, + testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_EC_PUB, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_EC_PUB, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyExtractPublicKey(SEC_OBJECTID_USER_BASE, TESTKEY_EC_PUB, TESTKC_RAW, SEC_STORAGELOC_RAM)) + + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_EC_PRIV, TESTKC_CONDITIONAL, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_EC_PRIV, TESTKC_CONDITIONAL, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyExtractPublicKey(SEC_OBJECTID_USER_BASE, TESTKEY_EC_PRIV, TESTKC_CONDITIONAL, SEC_STORAGELOC_RAM)) + + RUN_TEST(suite, testKeyProvision(SEC_OBJECTID_USER_BASE, TESTKEY_EC_PRIV, TESTKC_GENERATED, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyGetKeyInfo(SEC_OBJECTID_USER_BASE, TESTKEY_EC_PRIV, TESTKC_GENERATED, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, + testKeyExtractPublicKey(SEC_OBJECTID_USER_BASE, TESTKEY_EC_PRIV, TESTKC_GENERATED, SEC_STORAGELOC_RAM)) + + RUN_TEST(suite, testKeyComputeBaseKeyDigest(SEC_OBJECTID_USER_BASE, SEC_DIGESTALGORITHM_SHA1)) + RUN_TEST(suite, testKeyComputeBaseKeyDigest(SEC_OBJECTID_USER_BASE, SEC_DIGESTALGORITHM_SHA256)) + + RUN_TEST(suite, testKeyECDHKeyAgreementWithKDF(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKEY_EC_PRIV, + TESTKC_CONDITIONAL, TESTKEY_EC_PUB, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + SEC_DIGESTALGORITHM_SHA1, SEC_TRUE)) + RUN_TEST(suite, testKeyECDHKeyAgreementWithKDF(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKEY_EC_PRIV, + TESTKC_CONDITIONAL, TESTKEY_EC_PUB, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, + SEC_DIGESTALGORITHM_SHA256, SEC_TRUE)) + + RUN_TEST(suite, testKeyECDHKeyAgreementWithKDF(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKEY_EC_PRIV, + TESTKC_GENERATED, TESTKEY_EC_PUB, SEC_KEYTYPE_AES_128, + SEC_STORAGELOC_RAM, SEC_DIGESTALGORITHM_SHA1, SEC_TRUE)) + RUN_TEST(suite, testKeyECDHKeyAgreementWithKDF(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, TESTKEY_EC_PRIV, + TESTKC_GENERATED, TESTKEY_EC_PUB, SEC_KEYTYPE_AES_128, + SEC_STORAGELOC_RAM, SEC_DIGESTALGORITHM_SHA256, SEC_TRUE)) + + runProvisionedKeyExportTest(suite); +} + +void runKeyCtrlTests(SuiteCtx* suite) { // NOLINT + RUN_TEST(suite, testKeyCtrlExportUnCachable(1, "aesEcbNone")) + RUN_TEST(suite, testKeyCtrlKeyOnlyUsage(1, "aesEcbNone")) + RUN_TEST(suite, testKeyCtrlKeyExpired(1, "aesEcbNone")) + RUN_TEST(suite, testKeyCtrlKeyNotYetAvail(1, "aesEcbNone")) + RUN_TEST(suite, testKeyCtrlProvision32bit2038(1, "aesEcbNone")) + RUN_TEST(suite, testKeyCtrlExpectedJTypeProperties(1, "aesEcbNone", TESTKEY_AES128)) + RUN_TEST(suite, testKeyCtrlExpectedJTypeProperties(2, "aesEcbNone", TESTKEY_AES128)) + RUN_TEST(suite, testKeyCtrlExpectedJTypeProperties(2, "aesEcbNone", TESTKEY_AES256)) + RUN_TEST(suite, testKeyCtrlExpectedExportedProperties(1, "aesEcbNone", TESTKEY_AES128)) + RUN_TEST(suite, testKeyCtrlExpectedExportedProperties(2, "aesEcbNone", TESTKEY_AES128)) + RUN_TEST(suite, testKeyCtrlExpectedExportedProperties(2, "aesEcbNone", TESTKEY_AES256)) + RUN_TEST(suite, testKeyCtrlUnwrapWithKeyUsage(1, "aesEcbNone", TESTKEY_AES128)) + RUN_TEST(suite, testKeyCtrlUnwrapWithKeyUsage(2, "aesEcbNone", TESTKEY_AES128)) + RUN_TEST(suite, testKeyCtrlUnwrapWithKeyUsage(2, "aesEcbNone", TESTKEY_AES256)) + RUN_TEST(suite, testKeyCtrlUnwrapWithDataUsage(1, "aesEcbNone")) + RUN_TEST(suite, testKeyCtrlBadB64Jtype(1, "aesEcbNone")) + RUN_TEST(suite, testKeyCtrlExportDerived()) + RUN_TEST(suite, testKeyCtrlExportProvisionExport(1, "aesEcbNone", TESTKEY_AES128)) + RUN_TEST(suite, testKeyCtrlExportProvisionExport(2, "aesEcbNone", TESTKEY_AES128)) + RUN_TEST(suite, testKeyCtrlExportProvisionExport(2, "aesEcbNone", TESTKEY_AES256)) + RUN_TEST(suite, testKeyCtrlKeyExportGetSize(1, "aesEcbNone")) + RUN_TEST(suite, testKeyCtrlKeyExportGetSize(2, "aesEcbNone")) + RUN_TEST(suite, testKeyCtrlExportAes(TESTKEY_AES128, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyCtrlExportAes(TESTKEY_AES256, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyCtrlExportAes(TESTKEY_AES128, SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyCtrlExportAes(TESTKEY_AES256, SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyCtrlKeyExportHmac(TESTKEY_HMAC128, SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyCtrlKeyExportHmac(TESTKEY_HMAC160, SEC_STORAGELOC_RAM_SOFT_WRAPPED)) + RUN_TEST(suite, testKeyCtrlKeyExportHmac(TESTKEY_HMAC128, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testKeyCtrlKeyExportHmac(TESTKEY_HMAC160, SEC_STORAGELOC_RAM)) + if (TestCreds::supports(CAPABILITY_SVP)) { + RUN_TEST(suite, testKeyCtrlCipherFailsSvpNonOpaque(1, "aesEcbNone", SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING)) + RUN_TEST(suite, + testKeyCtrlCipherSvpOpaque(1, "aesEcbNone", TESTKEY_AES128, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING)) + RUN_TEST(suite, + testKeyCtrlCipherSvpOpaque(2, "aesEcbNone", TESTKEY_AES128, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING)) + RUN_TEST(suite, + testKeyCtrlCipherSvpOpaque(2, "aesEcbNone", TESTKEY_AES256, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING)) + RUN_TEST(suite, testKeyCtrlCipherSvpDataShiftOpaque(1, "aesEcbNone")) + RUN_TEST(suite, testKeyCtrlSvpCheckOpaque(1, "aesEcbNone", TESTKEY_AES128)) + RUN_TEST(suite, testKeyCtrlSvpCheckOpaque(2, "aesEcbNone", TESTKEY_AES128)) + RUN_TEST(suite, testKeyCtrlSvpCheckOpaque(2, "aesEcbNone", TESTKEY_AES256)) + RUN_TEST(suite, testKeyCtrlProcessCtrDataShiftFailsSvpNonOpaque(1, "aesEcbNone")) + RUN_TEST(suite, testKeyCtrlCipherFailsSvpNonOpaque(1, "aesEcbNone", SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING)) + RUN_TEST(suite, + testKeyCtrlCipherSvpOpaque(1, "aesEcbNone", TESTKEY_AES128, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING)) + RUN_TEST(suite, + testKeyCtrlCipherSvpOpaque(2, "aesEcbNone", TESTKEY_AES128, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING)) + RUN_TEST(suite, + testKeyCtrlCipherSvpOpaque(2, "aesEcbNone", TESTKEY_AES256, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING)) + RUN_TEST(suite, testKeyCtrlCipherSvpDataShiftOpaque(1, "aesEcbNone")) + RUN_TEST(suite, testKeyCtrlProcessCtrDataShiftFailsSvpNonOpaque(1, "aesEcbNone")) + } + RUN_TEST(suite, testKeyCtrlExportEcc(TESTKC_CONDITIONAL)) + RUN_TEST(suite, testKeyCtrlKeyExportSmallBuffer()) +} + +#define DIGEST_TESTS(suite, alg) \ + do { \ + RUN_TEST(suite, testDigestSingle(alg, 0)); \ + RUN_TEST(suite, testDigestSingle(alg, 256)); \ + RUN_TEST(suite, testDigestSingle(alg, 259)); \ +\ + SEC_SIZE inputSizes[] = {16, 16, 8, 0, 3, 16}; \ + RUN_TEST(suite, testDigestMult(alg, std::vector(inputSizes, inputSizes + sizeof(inputSizes) / sizeof(SEC_SIZE)))); \ +\ + RUN_TEST(suite, testDigestOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_AES128, SEC_STORAGELOC_RAM)); \ + if (TestCreds::supports(CAPABILITY_AES256)) { \ + RUN_TEST(suite, testDigestOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_AES256, SEC_STORAGELOC_RAM)); \ + } \ + RUN_TEST(suite, testDigestOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC128, SEC_STORAGELOC_RAM)); \ + RUN_TEST(suite, testDigestOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC160, SEC_STORAGELOC_RAM)); \ + RUN_TEST(suite, testDigestOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC256, SEC_STORAGELOC_RAM)); \ + } while (0) + +#define MAC_TESTS(suite, key, kc, loc, alg) \ + do { \ + RUN_TEST(suite, testMacSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, alg, 256)); \ + RUN_TEST(suite, testMacSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, alg, 259)); \ +\ + SEC_SIZE inputSizes[] = {16, 16, 8, 0, 3, 16}; \ + RUN_TEST(suite, testMacMult(SEC_OBJECTID_USER_BASE, key, kc, loc, alg, \ + std::vector(inputSizes, inputSizes + sizeof(inputSizes) / sizeof(SEC_SIZE)))); \ +\ + if ((alg) != SEC_MACALGORITHM_CMAC_AES_128) { \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC128, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_AES128, loc)); \ + if (TestCreds::supports(CAPABILITY_AES256)) { \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC128, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_AES256, loc)); \ + } \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC128, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC128, loc)); \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC128, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC160, loc)); \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC128, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC256, loc)); \ +\ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC160, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_AES128, loc)); \ + if (TestCreds::supports(CAPABILITY_AES256)) { \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC160, TESTKC_RAW, \ +\ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_AES256, loc)); \ + } \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC160, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC128, loc)); \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC160, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC160, loc)); \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC160, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC256, loc)); \ +\ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC256, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_AES128, loc)); \ + if (TestCreds::supports(CAPABILITY_AES256)) { \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC256, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_AES256, loc)); \ + } \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC256, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC128, loc)); \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC256, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC160, loc)); \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_HMAC256, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC256, loc)); \ + } else { \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_AES128, loc)); \ + if (TestCreds::supports(CAPABILITY_AES256)) { \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_AES256, loc)); \ + } \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC128, loc)); \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC160, loc)); \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC256, loc)); \ +\ + if (TestCreds::supports(CAPABILITY_AES256)) { \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_AES256, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_AES128, loc)); \ + if (TestCreds::supports(CAPABILITY_AES256)) { \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_AES256, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_AES256, loc)); \ + } \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_AES256, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC128, loc)); \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_AES256, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC160, loc)); \ + RUN_TEST(suite, testMacOverKey(alg, SEC_OBJECTID_USER_BASE, TESTKEY_AES256, TESTKC_RAW, \ + SEC_OBJECTID_USER_BASE + 1, TESTKEY_HMAC256, loc)); \ + } \ + } \ + } while (0); + +#define AESCTR_SPECIFIC_TESTS(suite, key, kc, loc, mode, inplace) \ + do { \ + if ((kc) != TESTKC_GENERATED) { \ + RUN_TEST(suite, testProcessCtrWithDataShift(SEC_OBJECTID_USER_BASE, key, kc, loc, mode, inplace)); \ + RUN_TEST(suite, testCtrRollover(SEC_OBJECTID_USER_BASE, key, kc, loc, mode, 256, inplace)); \ + } \ + } while (0) + +#define AESCTR_INPLACE_TESTS(suite, key, kc, loc, mode) \ + do { \ + AESCTR_SPECIFIC_TESTS(suite, key, kc, loc, mode, SEC_TRUE); \ + AESCTR_SPECIFIC_TESTS(suite, key, kc, loc, mode, SEC_FALSE); \ + } while (0) + +#define AESCTR_MODE_TESTS(suite, key, kc, loc) \ + do { \ + AESCTR_INPLACE_TESTS(suite, key, kc, loc, SEC_CIPHERMODE_ENCRYPT); \ + AESCTR_INPLACE_TESTS(suite, key, kc, loc, SEC_CIPHERMODE_DECRYPT); \ + } while (0) + +#define AESCTR_LOC_TESTS(suite, key, kc) \ + do { \ + AESCTR_MODE_TESTS(suite, key, kc, SEC_STORAGELOC_RAM); \ + if ((kc) == TESTKC_RAW) { \ + AESCTR_MODE_TESTS(suite, key, kc, SEC_STORAGELOC_RAM_SOFT_WRAPPED); \ + } \ + } while (0) + +#define AESCTR_KC_TESTS(suite, key) \ + do { \ + AESCTR_LOC_TESTS(suite, key, TESTKC_RAW); \ + if (TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC)) { \ + AESCTR_LOC_TESTS(suite, key, TESTKC_CONDITIONAL); \ + } \ + AESCTR_LOC_TESTS(suite, key, TESTKC_GENERATED); \ + } while (0) + +#define AESCTR_TESTS(suite) \ + do { \ + AESCTR_KC_TESTS(suite, TESTKEY_AES128); \ + if (TestCreds::supports(CAPABILITY_AES256)) { \ + AESCTR_KC_TESTS(suite, TESTKEY_AES256); \ + } \ + } while (0) + +#define AES_TESTS(suite, key, kc, loc) \ + do { \ + SEC_SIZE inputSizesBlock[] = {0, 16, 32, 64, 128}; \ + SEC_SIZE inputSizes[] = {0, 16, 32, 64, 128, 5}; \ + SEC_SIZE inputSizesStream[] = {0, 6, 16, 17, 32, 128, 3}; \ + std::vector blocked(inputSizesBlock, inputSizesBlock + sizeof(inputSizesBlock) / sizeof(SEC_SIZE)); \ + std::vector unaligned(inputSizes, inputSizes + sizeof(inputSizes) / sizeof(SEC_SIZE)); \ +\ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, \ + SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, \ + SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE, SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, \ + SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE * 2)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, \ + SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, \ + SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE, SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, \ + SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE * 2)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, \ + SEC_CIPHERMODE_ENCRYPT, blocked)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, \ + SEC_CIPHERMODE_DECRYPT, blocked)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, \ + SEC_CIPHERMODE_ENCRYPT, blocked, SEC_TRUE)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, \ + SEC_CIPHERMODE_DECRYPT, blocked, SEC_TRUE)); \ + RUN_TEST(suite, testCipherUpdateIV(SEC_OBJECTID_USER_BASE, key, kc, loc, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, \ + SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE * 2, SEC_FALSE)); \ + RUN_TEST(suite, testCipherUpdateIV(SEC_OBJECTID_USER_BASE, key, kc, loc, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, \ + SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE * 2, SEC_FALSE)); \ +\ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE, SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE * 2 + 8)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE, SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE * 2 + 8)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING, SEC_CIPHERMODE_ENCRYPT, unaligned)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING, SEC_CIPHERMODE_DECRYPT, unaligned)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING, SEC_CIPHERMODE_ENCRYPT, unaligned, SEC_TRUE)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING, SEC_CIPHERMODE_DECRYPT, unaligned, SEC_TRUE)); \ +\ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE, SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE * 2)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE, SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE * 2)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_ENCRYPT, blocked)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, blocked)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_ENCRYPT, blocked, SEC_TRUE)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, blocked, SEC_TRUE)); \ +\ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE, SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE * 2 + 8)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE, SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE * 2 + 8)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING, SEC_CIPHERMODE_ENCRYPT, unaligned)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING, SEC_CIPHERMODE_DECRYPT, unaligned)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING, SEC_CIPHERMODE_ENCRYPT, unaligned, SEC_TRUE)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_ECB_PKCS7_PADDING, SEC_CIPHERMODE_DECRYPT, unaligned, SEC_TRUE)); \ +\ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE + 1)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE, SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE + 1, SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE * 2)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE + 1)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE, SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE + 1, SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE * 2)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_ENCRYPT, blocked)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_ENCRYPT, unaligned)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_DECRYPT, blocked)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_DECRYPT, unaligned)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_ENCRYPT, blocked, SEC_TRUE)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_ENCRYPT, unaligned, SEC_TRUE)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_DECRYPT, blocked, SEC_TRUE)); \ + RUN_TEST(suite, testCipherMult(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_DECRYPT, unaligned, SEC_TRUE)); \ + RUN_TEST(suite, testCipherUpdateIV(SEC_OBJECTID_USER_BASE, key, kc, \ + loc, SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE * 2, SEC_FALSE)); \ + RUN_TEST(suite, testCipherUpdateIV(SEC_OBJECTID_USER_BASE, key, kc, \ + loc, SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE * 2, SEC_FALSE)); \ + } while (0) + +#define RSA_ENCRYPT_TESTS(suite, pub, priv, kc) \ + do { \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE, \ + SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_CIPHERALGORITHM_RSA_OAEP_PADDING, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_CIPHERALGORITHM_RSA_OAEP_PADDING, SEC_CIPHERMODE_ENCRYPT, SEC_AES_BLOCK_SIZE, \ + SEC_TRUE)); \ + } while (0) + +#define RSA_DECRYPT_TESTS(suite, pub, priv, kc) \ + do { \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE, \ + SEC_TRUE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_CIPHERALGORITHM_RSA_OAEP_PADDING, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE)); \ + RUN_TEST(suite, testCipherSingle(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_CIPHERALGORITHM_RSA_OAEP_PADDING, SEC_CIPHERMODE_DECRYPT, SEC_AES_BLOCK_SIZE, \ + SEC_TRUE)); \ + } while (0) + +void runRandomTests(SuiteCtx* suite) { + RUN_TEST(suite, testRandom(SEC_RANDOMALGORITHM_TRUE, 17)) + RUN_TEST(suite, testRandom(SEC_RANDOMALGORITHM_PRNG, 17)) +} + +void runSVPTests(SuiteCtx* suite) { + if (TestCreds::supports(CAPABILITY_SVP)) { + RUN_TEST(suite, testOpaqueMalloc()) + RUN_TEST(suite, testCopyOpaque()) + RUN_TEST(suite, testSecureBootEnabled()) + RUN_TEST(suite, testSetTime()) + RUN_TEST(suite, testKeycheckOpaque(SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, SEC_STORAGELOC_RAM)) + RUN_TEST(suite, testProcessOpaque(SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, SEC_STORAGELOC_RAM, + SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING)) + RUN_TEST(suite, testProcessOpaque(SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, SEC_STORAGELOC_RAM, + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING)) + RUN_TEST(suite, + testProcessDataShiftOpaque(SEC_OBJECTID_USER_BASE, TESTKEY_AES128, TESTKC_RAW, SEC_STORAGELOC_RAM)) + } +} + +#define RSAAESRSAAESAES_SPECIFIC_TESTS(suite, rsa_type, rsa_alg, sym_type, sym_alg, ck_sym_alg, wkfv) \ + do { \ + TestKey key = (sym_type) == SEC_KEYTYPE_AES_128 ? TESTKEY_AES128 : TESTKEY_AES256; \ + RUN_TEST(suite, testWrappedCipherSingleRsaAesRsaAesAes(key, TESTKC_SOC, rsa_type, rsa_alg, sym_type, \ + sym_alg, ck_sym_alg, wkfv)); \ + } while (0) + +#define RSAAESRSAAESAES_WKFV_TESTS(suite, rsa_type, rsa_alg, sym_type, sym_alg, ck_sym_alg) \ + do { \ + RSAAESRSAAESAES_SPECIFIC_TESTS(suite, rsa_type, rsa_alg, sym_type, sym_alg, ck_sym_alg, WKFV_V2); \ + if (TestCreds::supports(CAPABILITY_WRAPPED_KEY_FORMAT_V3)) { \ + RSAAESRSAAESAES_SPECIFIC_TESTS(suite, rsa_type, rsa_alg, sym_type, sym_alg, ck_sym_alg, WKFV_V3); \ + } \ + } while (0) + +#define RSAAESRSAAESAES_CKSYMALG_TESTS(suite, rsa_type, rsa_alg, sym_type, sym_alg) \ + do { \ + if (TestCreds::supports(CAPABILITY_RSA_AESCBC_AES)) { \ + RSAAESRSAAESAES_WKFV_TESTS(suite, rsa_type, rsa_alg, sym_type, sym_alg, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING); \ + RSAAESRSAAESAES_WKFV_TESTS(suite, rsa_type, rsa_alg, sym_type, sym_alg, SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING); \ + } \ + RSAAESRSAAESAES_WKFV_TESTS(suite, rsa_type, rsa_alg, sym_type, sym_alg, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING); \ + if (TestCreds::supports(CAPABILITY_RSA_AESCTR_AES)) { \ + RSAAESRSAAESAES_WKFV_TESTS(suite, rsa_type, rsa_alg, sym_type, sym_alg, SEC_CIPHERALGORITHM_AES_CTR); \ + } \ + } while (0) + +#define RSAAESRSAAESAES_SYMALG_TESTS(suite, rsa_type, rsa_alg, sym_type) \ + do { \ + RSAAESRSAAESAES_CKSYMALG_TESTS(suite, rsa_type, rsa_alg, sym_type, SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING); \ + if (TestCreds::supports(CAPABILITY_RSA_AESCTR_RSA)) { \ + RSAAESRSAAESAES_CKSYMALG_TESTS(suite, rsa_type, rsa_alg, sym_type, SEC_CIPHERALGORITHM_AES_CTR); \ + } \ + } while (0) + +#define RSAAESRSAAESAES_SYMTYPE_TESTS(suite, rsa_type, rsa_alg) \ + do { \ + RSAAESRSAAESAES_SYMALG_TESTS(suite, rsa_type, rsa_alg, SEC_KEYTYPE_AES_128); \ + if (TestCreds::supports(CAPABILITY_AES256)) { \ + RSAAESRSAAESAES_SYMALG_TESTS(suite, rsa_type, rsa_alg, SEC_KEYTYPE_AES_256); \ + } \ + } while (0) + +#define RSAAESRSAAESAES_ASYMALG_TESTS(suite, rsa_type) \ + do { \ + RSAAESRSAAESAES_SYMTYPE_TESTS(suite, rsa_type, SEC_CIPHERALGORITHM_RSA_OAEP_PADDING); \ + RSAAESRSAAESAES_SYMTYPE_TESTS(suite, rsa_type, SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING); \ + } while (0) + +#define RSAAESRSAAESAES_ASYMTYPE_TESTS(suite) \ + do { \ + if (TestCreds::supports(CAPABILITY_RSA_1024)) { \ + RSAAESRSAAESAES_ASYMALG_TESTS(suite, SEC_KEYTYPE_RSA_1024); \ + } \ + RSAAESRSAAESAES_ASYMALG_TESTS(suite, SEC_KEYTYPE_RSA_2048); \ + } while (0) + +#define RSAAES_SPECIFIC_TESTS(suite, con_key, rsa_type, rsa_alg, wkfv) \ + do { \ + RUN_TEST(suite, testWrappedCipherSingleRsaAes(con_key, TESTKC_SOC, rsa_type, rsa_alg, wkfv)); \ + } while (0) + +#define RSAAES_WKFV_TESTS(suite, con_key, rsa_type, rsa_alg) \ + do { \ + RSAAES_SPECIFIC_TESTS(suite, con_key, rsa_type, rsa_alg, WKFV_V2); \ + if (TestCreds::supports(CAPABILITY_WRAPPED_KEY_FORMAT_V3)) { \ + RSAAES_SPECIFIC_TESTS(suite, con_key, rsa_type, rsa_alg, WKFV_V3); \ + } \ + } while (0) + +#define RSAAES_ASYMALG_TESTS(suite, con_key, rsa_type) \ + do { \ + RSAAES_WKFV_TESTS(suite, con_key, rsa_type, SEC_CIPHERALGORITHM_RSA_OAEP_PADDING); \ + RSAAES_WKFV_TESTS(suite, con_key, rsa_type, SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING); \ + } while (0) + +#define RSAAES_ASYTYPE_TESTS(suite, con_key) \ + do { \ + if (TestCreds::supports(CAPABILITY_RSA_1024)) { \ + RSAAES_ASYMALG_TESTS(suite, con_key, SEC_KEYTYPE_RSA_1024); \ + } \ + RSAAES_ASYMALG_TESTS(suite, con_key, SEC_KEYTYPE_RSA_2048); \ + } while (0) + +#define RSAAES_TESTS(suite) \ + do { \ + if (TestCreds::supports(CAPABILITY_RSA_AES_M2M)) { \ + RSAAES_ASYTYPE_TESTS(suite, TESTKEY_AES128); \ + if (TestCreds::supports(CAPABILITY_AES256)) { \ + RSAAES_ASYTYPE_TESTS(suite, TESTKEY_AES256); \ + } \ + } \ + } while (0) + +#define RSAAESAES_SPECIFIC_TESTS(suite, rsa_type, rsa_alg, sym_type, sym_alg, wkfv) \ + do { \ + TestKey key = (sym_type) == SEC_KEYTYPE_AES_128 ? TESTKEY_AES128 : TESTKEY_AES256; \ + RUN_TEST(suite, testWrappedCipherSingleRsaAesAes(key, TESTKC_SOC, rsa_type, rsa_alg, sym_type, sym_alg, wkfv)); \ + RUN_TEST(suite, testExportWrappedRsaAesAes(key, TESTKC_SOC, rsa_type, rsa_alg, sym_type, sym_alg, wkfv)); \ + } while (0) + +#define RSAAESAES_WKFV_TESTS(suite, rsa_type, rsa_alg, sym_type, sym_alg) \ + do { \ + RSAAESAES_SPECIFIC_TESTS(suite, rsa_type, rsa_alg, sym_type, sym_alg, WKFV_V2); \ + if (TestCreds::supports(CAPABILITY_WRAPPED_KEY_FORMAT_V3)) { \ + RSAAESAES_SPECIFIC_TESTS(suite, rsa_type, rsa_alg, sym_type, sym_alg, WKFV_V3); \ + } \ + } while (0) + +#define RSAAESAES_SYMALG_TESTS(suite, rsa_type, rsa_alg, sym_type) \ + do { \ + RSAAESAES_WKFV_TESTS(suite, rsa_type, rsa_alg, sym_type, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING); \ + if (TestCreds::supports(CAPABILITY_RSA_AESCBC_AES)) { \ + RSAAESAES_WKFV_TESTS(suite, rsa_type, rsa_alg, sym_type, SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING); \ + RSAAESAES_WKFV_TESTS(suite, rsa_type, rsa_alg, sym_type, SEC_CIPHERALGORITHM_AES_CBC_PKCS7_PADDING); \ + } \ + if (TestCreds::supports(CAPABILITY_RSA_AESCTR_AES)) { \ + RSAAESAES_WKFV_TESTS(suite, rsa_type, rsa_alg, sym_type, SEC_CIPHERALGORITHM_AES_CTR); \ + } \ + } while (0) + +#define RSAAESAES_SYMTYPE_TESTS(suite, rsa_type, rsa_alg) \ + do { \ + RSAAESAES_SYMALG_TESTS(suite, rsa_type, rsa_alg, SEC_KEYTYPE_AES_128); \ + if (TestCreds::supports(CAPABILITY_AES256)) { \ + RSAAESAES_SYMALG_TESTS(suite, rsa_type, rsa_alg, SEC_KEYTYPE_AES_256); \ + } \ + } while (0) + +#define RSAAESAES_ASYMALG_TESTS(suite, rsa_type) \ + do { \ + RSAAESAES_SYMTYPE_TESTS(suite, rsa_type, SEC_CIPHERALGORITHM_RSA_OAEP_PADDING); \ + RSAAESAES_SYMTYPE_TESTS(suite, rsa_type, SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING); \ + } while (0) + +#define RSAAESAES_TESTS(suite) \ + do { \ + if (TestCreds::supports(CAPABILITY_RSA_1024)) { \ + RSAAESAES_ASYMALG_TESTS(suite, SEC_KEYTYPE_RSA_1024); \ + } \ + RSAAESAES_ASYMALG_TESTS(suite, SEC_KEYTYPE_RSA_2048); \ + } while (0) + +#define ECAESAES_SPECIFIC_TESTS(suite, con_key, asym_alg, sym_alg, wkfv) \ + do { \ + RUN_TEST(suite, testWrappedCipherSingleEcAesAes(con_key, TESTKC_SOC, SEC_KEYTYPE_AES_128, asym_alg, sym_alg, \ + wkfv)); \ + RUN_TEST(suite, testExportWrappedEccAesAes(con_key, TESTKC_SOC, asym_alg, SEC_KEYTYPE_AES_128, sym_alg, \ + wkfv)); \ + RUN_TEST(suite, testExportWrappedGeneratedEccAesAes(con_key, asym_alg, SEC_KEYTYPE_AES_128, sym_alg, wkfv)); \ + if (TestCreds::supports(CAPABILITY_AES256)) { \ + RUN_TEST(suite, testWrappedCipherSingleEcAesAes(con_key, TESTKC_SOC, SEC_KEYTYPE_AES_256, asym_alg, \ + sym_alg, wkfv)); \ + RUN_TEST(suite, testExportWrappedEccAesAes(con_key, TESTKC_SOC, asym_alg, SEC_KEYTYPE_AES_256, sym_alg, \ + wkfv)); \ + RUN_TEST(suite, testExportWrappedGeneratedEccAesAes(con_key, asym_alg, SEC_KEYTYPE_AES_256, sym_alg, \ + wkfv)); \ + } \ + } while (0) + +#define ECAES_SPECIFIC_TESTS(suite, con_key, asym_alg, wkfv) \ + do { \ + RUN_TEST(suite, testWrappedCipherSingleEcAes(con_key, TESTKC_SOC, asym_alg, wkfv)); \ + RUN_TEST(suite, testExportWrappedEccAes(con_key, TESTKC_SOC, asym_alg, wkfv)); \ + RUN_TEST(suite, testExportWrappedGeneratedEccAes(con_key, asym_alg, wkfv)); \ + } while (0) + +#define ECAES_WKFV_TESTS(suite, con_key, asym_alg) \ + do { \ + ECAES_SPECIFIC_TESTS(suite, con_key, asym_alg, WKFV_V2); \ + ECAESAES_SPECIFIC_TESTS(suite, con_key, asym_alg, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, WKFV_V2); \ + ECAESAES_SPECIFIC_TESTS(suite, con_key, asym_alg, SEC_CIPHERALGORITHM_AES_CTR, WKFV_V2); \ + if (TestCreds::supports(CAPABILITY_WRAPPED_KEY_FORMAT_V3)) { \ + ECAES_SPECIFIC_TESTS(suite, con_key, asym_alg, WKFV_V3); \ + ECAESAES_SPECIFIC_TESTS(suite, con_key, asym_alg, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, WKFV_V3); \ + ECAESAES_SPECIFIC_TESTS(suite, con_key, asym_alg, SEC_CIPHERALGORITHM_AES_CTR, WKFV_V3); \ + } \ + } while (0) + +#define ECAES_ASYMALG_TESTS(suite, con_key) \ + do { \ + ECAES_WKFV_TESTS(suite, con_key, SEC_CIPHERALGORITHM_ECC_ELGAMAL); \ + } while (0) + +#define ECAES_TESTS(suite) \ + do { \ + ECAES_ASYMALG_TESTS(suite, TESTKEY_AES128); \ + if (TestCreds::supports(CAPABILITY_AES256)) { \ + ECAES_ASYMALG_TESTS(suite, TESTKEY_AES256); \ + } \ + } while (0) + +#define OPAQUE_WITH_MAP_TESTS(suite, key, kc, loc) \ + do { \ +\ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 1, 0)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 1, 16)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 1, 20)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 1, 256)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 2, 0)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 2, 16)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 2, 20)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 2, 256)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 5, 0)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 5, 16)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 5, 20)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 5, 256)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 10, 0)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 10, 16)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 10, 20)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR, 10, 256)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 1, 0)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 1, 16)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 1, 256)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 2, 0)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 2, 16)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 2, 256)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 5, 0)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 5, 16)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 5, 256)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 10, 0)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 10, 16)); \ + RUN_TEST(suite, testProcessOpaqueWithMap(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING, 10, 256)); \ +\ + RUN_TEST(suite, testProcessOpaqueWithMapVariable(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CTR)); \ + RUN_TEST(suite, testProcessOpaqueWithMapVariable(SEC_OBJECTID_USER_BASE, key, kc, loc, \ + SEC_CIPHERALGORITHM_AES_CBC_NO_PADDING)); \ + } while (0) + +void runWrappedTests(SuiteCtx* suite) { // NOLINT + RSAAESRSAAESAES_ASYMTYPE_TESTS(suite); + RSAAESAES_TESTS(suite); + RSAAES_TESTS(suite); + ECAES_TESTS(suite); + RUN_TEST(suite, + testWrappedKDFCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, SEC_OBJECTID_USER_BASE + 2, + SEC_KEYTYPE_AES_128, 1, 256)) + RUN_TEST(suite, + testWrappedKDFCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, SEC_OBJECTID_USER_BASE + 2, + SEC_KEYTYPE_HMAC_128, 1, 256)) + RUN_TEST(suite, + testExportedKDFCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, SEC_OBJECTID_USER_BASE + 2, + SEC_KEYTYPE_AES_128, 1, 256)) + RUN_TEST(suite, + testExportedKDFCMACAES128(SEC_OBJECTID_USER_BASE, SEC_OBJECTID_USER_BASE + 1, SEC_OBJECTID_USER_BASE + 2, + SEC_KEYTYPE_HMAC_128, 1, 256)) +} + +void runConcurrentTests(SuiteCtx* suite) { + RUN_TEST(suite, testConcurrentVendor128(40)) + + if (TestCreds::supports(CAPABILITY_RSA_1024)) { + + RUN_TEST(suite, testConcurrentRsa(TESTKEY_RSA1024_ENC_PUB, TESTKEY_RSA1024_ENC_PRIV, TESTKC_CONDITIONAL, 10)) + } + RUN_TEST(suite, testConcurrentRsa(TESTKEY_RSA2048_ENC_PUB, TESTKEY_RSA2048_ENC_PRIV, TESTKC_CONDITIONAL, 10)) +} + +void runExchangeTests(SuiteCtx* suite) { + RUN_TEST(suite, testKeyExchangeDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_AES_128, SEC_TRUE)) + RUN_TEST(suite, testKeyExchangeDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_AES_128, SEC_FALSE)) + if (TestCreds::supports(CAPABILITY_AES256)) { + RUN_TEST(suite, testKeyExchangeDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_AES_256, SEC_TRUE)) + RUN_TEST(suite, testKeyExchangeDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_AES_256, SEC_FALSE)) + } + RUN_TEST(suite, testKeyExchangeDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_HMAC_128, SEC_TRUE)) + RUN_TEST(suite, testKeyExchangeDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_HMAC_128, SEC_FALSE)) + RUN_TEST(suite, testKeyExchangeDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_HMAC_160, SEC_TRUE)) + RUN_TEST(suite, testKeyExchangeDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_HMAC_160, SEC_FALSE)) + + RUN_TEST(suite, testKeyExchangeECDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_AES_128, SEC_TRUE)) + RUN_TEST(suite, testKeyExchangeECDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_AES_128, SEC_FALSE)) + if (TestCreds::supports(CAPABILITY_AES256)) { + RUN_TEST(suite, testKeyExchangeECDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_AES_256, SEC_TRUE)) + RUN_TEST(suite, testKeyExchangeECDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_AES_256, + SEC_FALSE)) + } + RUN_TEST(suite, testKeyExchangeECDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_HMAC_128, SEC_TRUE)) + RUN_TEST(suite, testKeyExchangeECDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_HMAC_128, SEC_FALSE)) + RUN_TEST(suite, testKeyExchangeECDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_HMAC_160, SEC_TRUE)) + RUN_TEST(suite, testKeyExchangeECDH(SEC_OBJECTID_USER_BASE, SEC_STORAGELOC_RAM, SEC_KEYTYPE_HMAC_160, SEC_FALSE)) +} + +#define RSA_SIGNATURE_TESTS(suite, pub, priv, kc, keySize) \ + do { \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS_DIGEST, SEC_SIGNATUREMODE_SIGN, 20)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, TESTKC_RAW, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS_DIGEST, SEC_SIGNATUREMODE_VERIFY, 20)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS, SEC_SIGNATUREMODE_SIGN, 2049)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, TESTKC_RAW, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS, SEC_SIGNATUREMODE_VERIFY, 2049)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS_DIGEST, SEC_SIGNATUREMODE_SIGN, 32)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, TESTKC_RAW, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS_DIGEST, SEC_SIGNATUREMODE_VERIFY, 32)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS, SEC_SIGNATUREMODE_SIGN, 2049)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, TESTKC_RAW, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS, SEC_SIGNATUREMODE_VERIFY, 2049)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST, SEC_SIGNATUREMODE_SIGN, 20)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, TESTKC_RAW, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST, SEC_SIGNATUREMODE_VERIFY, 20)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS, SEC_SIGNATUREMODE_SIGN, 2049)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, TESTKC_RAW, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS, SEC_SIGNATUREMODE_VERIFY, 2049)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST, SEC_SIGNATUREMODE_SIGN, 32)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, TESTKC_RAW, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST, SEC_SIGNATUREMODE_VERIFY, 32)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS, SEC_SIGNATUREMODE_SIGN, 2049)); \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, TESTKC_RAW, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS, SEC_SIGNATUREMODE_VERIFY, 2049)); \ + } while (0) + +#define EC_SIGNATURE_TESTS(suite, pub, priv, kc, keySize) \ + do { \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST, SEC_SIGNATUREMODE_SIGN, 32)); \ + if ((kc) != TESTKC_GENERATED) { \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, TESTKC_RAW, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST, SEC_SIGNATUREMODE_VERIFY, 32)); \ + } \ +\ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, kc, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_ECDSA_NISTP256, SEC_SIGNATUREMODE_SIGN, 2049)); \ + if ((kc) != TESTKC_GENERATED) { \ + RUN_TEST(suite, testSignature(SEC_OBJECTID_USER_BASE, pub, priv, TESTKC_RAW, SEC_STORAGELOC_RAM, \ + SEC_SIGNATUREALGORITHM_ECDSA_NISTP256, SEC_SIGNATUREMODE_VERIFY, 2049)); \ + } \ + } while (0) + +int testIt(int argc, char* argv[]) { // NOLINT + int nParams = argc - 1; + std::vector runParams; + SEC_PRINT("Number of runParams: %d\n", nParams); + for (int i = 1; i < (nParams + 1); ++i) { + auto x = strtol(argv[i], nullptr, 0); + runParams.push_back(x); + } + + SEC_PRINT("==============================================\n"); + + //print SecApi info + { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return 1; + } + + if (SecProcessor_PrintInfo(ctx.proc()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecProcessor_PrintInfo failed"); + return 1; + } + } + + TestCreds::init(); + SuiteCtx suite; + suite.setRunParams(runParams); + + SEC_PRINT("\n"); + SEC_PRINT("CAPABILITY_AES256: %d\n", TestCreds::supports(CAPABILITY_AES256)); + SEC_PRINT("CAPABILITY_HMAC_IN_HW: %d\n", TestCreds::supports(CAPABILITY_HMAC_IN_HW)); + SEC_PRINT("CAPABILITY_CMAC_IN_HW: %d\n", TestCreds::supports(CAPABILITY_CMAC_IN_HW)); + SEC_PRINT("CAPABILITY_DIGEST_OVER_HWKEY: %d\n", TestCreds::supports(CAPABILITY_DIGEST_OVER_HWKEY)); + SEC_PRINT("CAPABILITY_HMAC_OVER_HWKEY: %d\n", TestCreds::supports(CAPABILITY_HMAC_OVER_HWKEY)); + SEC_PRINT("CAPABILITY_CMAC_OVER_HWKEY: %d\n", TestCreds::supports(CAPABILITY_CMAC_OVER_HWKEY)); + SEC_PRINT("CAPABILITY_HKDF_CMAC: %d\n", TestCreds::supports(CAPABILITY_HKDF_CMAC)); + SEC_PRINT("CAPABILITY_EXTRACT_RSA_PUB: %d\n", TestCreds::supports(CAPABILITY_EXTRACT_RSA_PUB)); + SEC_PRINT("CAPABILITY_WRAPPED_KEY_FORMAT_V3: %d\n", TestCreds::supports(CAPABILITY_WRAPPED_KEY_FORMAT_V3)); + SEC_PRINT("CAPABILITY_RSA_AES_M2M: %d\n", TestCreds::supports(CAPABILITY_RSA_AES_M2M)); + SEC_PRINT("CAPABILITY_CLEAR_JTYPE_WRAPPING: %d\n", TestCreds::supports(CAPABILITY_CLEAR_JTYPE_WRAPPING)); + SEC_PRINT("CAPABILITY_SVP: %d\n", TestCreds::supports(CAPABILITY_SVP)); + SEC_PRINT("CAPABILITY_LOAD_SYM_SOC_KC: %d\n", TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC)); + SEC_PRINT("CAPABILITY_EXPORT_RSA: %d\n", TestCreds::supports(CAPABILITY_EXPORT_RSA)); + SEC_PRINT("CAPABILITY_RSA_1024: %d\n", TestCreds::supports(CAPABILITY_RSA_1024)); + SEC_PRINT("CAPABILITY_RSA_AESCBC_AES: %d\n", TestCreds::supports(CAPABILITY_RSA_AESCBC_AES)); + SEC_PRINT("CAPABILITY_RSA_AESCTR_AES: %d\n", TestCreds::supports(CAPABILITY_RSA_AESCTR_AES)); + SEC_PRINT("CAPABILITY_RSA_AESCTR_RSA: %d\n", TestCreds::supports(CAPABILITY_RSA_AESCTR_RSA)); + SEC_PRINT("\n"); + + runProcessorTests(&suite); + + runRandomTests(&suite); + + runBundleTests(&suite); + + runCertTests(&suite); + + runKeyTests(&suite); + + DIGEST_TESTS(&suite, SEC_DIGESTALGORITHM_SHA1); + DIGEST_TESTS(&suite, SEC_DIGESTALGORITHM_SHA256); + + if (TestCreds::supports(CAPABILITY_HMAC_IN_HW)) { + MAC_TESTS(&suite, TESTKEY_HMAC128, TESTKC_RAW, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA1) + MAC_TESTS(&suite, TESTKEY_HMAC160, TESTKC_RAW, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA1) + MAC_TESTS(&suite, TESTKEY_HMAC256, TESTKC_RAW, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA1) + MAC_TESTS(&suite, TESTKEY_HMAC128, TESTKC_RAW, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA256) + MAC_TESTS(&suite, TESTKEY_HMAC160, TESTKC_RAW, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA256) + MAC_TESTS(&suite, TESTKEY_HMAC256, TESTKC_RAW, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_HMAC_SHA256) + } else { + MAC_TESTS(&suite, TESTKEY_HMAC128, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED, SEC_MACALGORITHM_HMAC_SHA1) + MAC_TESTS(&suite, TESTKEY_HMAC160, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED, SEC_MACALGORITHM_HMAC_SHA1) + MAC_TESTS(&suite, TESTKEY_HMAC256, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED, SEC_MACALGORITHM_HMAC_SHA1) + MAC_TESTS(&suite, TESTKEY_HMAC128, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED, SEC_MACALGORITHM_HMAC_SHA256) + MAC_TESTS(&suite, TESTKEY_HMAC160, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED, SEC_MACALGORITHM_HMAC_SHA256) + MAC_TESTS(&suite, TESTKEY_HMAC256, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED, SEC_MACALGORITHM_HMAC_SHA256) + } + + if (TestCreds::supports(CAPABILITY_CMAC_IN_HW)) { + MAC_TESTS(&suite, TESTKEY_AES128, TESTKC_RAW, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_CMAC_AES_128) + if (TestCreds::supports(CAPABILITY_AES256)) { + MAC_TESTS(&suite, TESTKEY_AES256, TESTKC_RAW, SEC_STORAGELOC_RAM, SEC_MACALGORITHM_CMAC_AES_128) + } + } else { + MAC_TESTS(&suite, TESTKEY_AES128, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED, SEC_MACALGORITHM_CMAC_AES_128) + if (TestCreds::supports(CAPABILITY_AES256)) { + MAC_TESTS(&suite, TESTKEY_AES256, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED, + SEC_MACALGORITHM_CMAC_AES_128) + } + } + + //aes + AES_TESTS(&suite, TESTKEY_AES128, TESTKC_RAW, SEC_STORAGELOC_RAM); + AES_TESTS(&suite, TESTKEY_AES128, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED); + + if (TestCreds::supports(CAPABILITY_AES256)) { + AES_TESTS(&suite, TESTKEY_AES256, TESTKC_RAW, SEC_STORAGELOC_RAM); + AES_TESTS(&suite, TESTKEY_AES256, TESTKC_RAW, SEC_STORAGELOC_RAM_SOFT_WRAPPED); + } + + AESCTR_TESTS(&suite); + + //rsa + if (TestCreds::supports(CAPABILITY_RSA_1024)) { + RSA_ENCRYPT_TESTS(&suite, TESTKEY_RSA1024_ENC_PUB, TESTKEY_RSA1024_ENC_PRIV, TESTKC_RAW); + RSA_DECRYPT_TESTS(&suite, TESTKEY_RSA1024_ENC_PUB, TESTKEY_RSA1024_ENC_PRIV, TESTKC_CONDITIONAL); + } + + RSA_ENCRYPT_TESTS(&suite, TESTKEY_RSA2048_ENC_PUB, TESTKEY_RSA2048_ENC_PRIV, TESTKC_RAW); + RSA_DECRYPT_TESTS(&suite, TESTKEY_RSA2048_ENC_PUB, TESTKEY_RSA2048_ENC_PRIV, TESTKC_CONDITIONAL); + + //sign + if (TestCreds::supports(CAPABILITY_RSA_1024)) { + RSA_SIGNATURE_TESTS(&suite, TESTKEY_RSA1024_SGN_PUB, TESTKEY_RSA1024_SGN_PRIV, TESTKC_CONDITIONAL, 128); + } + RSA_SIGNATURE_TESTS(&suite, TESTKEY_RSA2048_SGN_PUB, TESTKEY_RSA2048_SGN_PRIV, TESTKC_CONDITIONAL, 256); + + EC_SIGNATURE_TESTS(&suite, TESTKEY_EC_PUB, TESTKEY_EC_PRIV, TESTKC_CONDITIONAL, 128); + EC_SIGNATURE_TESTS(&suite, TESTKEY_EC_PUB, TESTKEY_EC_PRIV, TESTKC_GENERATED, 128); + EC_SIGNATURE_TESTS(&suite, TESTKEY_EC_PUB, TESTKEY_EC_PRIV, TESTKC_EXPORTED, 128); + + OPAQUE_WITH_MAP_TESTS(&suite, TESTKEY_AES128, TESTKC_RAW, SEC_STORAGELOC_RAM); + + runWrappedTests(&suite); + + runKeyCtrlTests(&suite); + + runSVPTests(&suite); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + runConcurrentTests(&suite); +#endif + runExchangeTests(&suite); + + //todo: fragmented processing + //todo: add pem containers, raw formats for keys, certs + + SEC_PRINT("==============================================\n"); + SEC_PRINT("Test summary: %d/%d succeeded, %d skipped\n", suite.getSucceeded().size(), suite.getAttempted().size(), + suite.getSkipped().size()); + + std::vector failed = suite.getFailed(); + if (failed.empty()) { + SEC_PRINT("\nFailed Tests: [%d]\n", failed.size()); + for (unsigned int i = 0; i < failed.size(); ++i) { + SEC_PRINT("%d ", failed[i]); + if (i % SEC_AES_BLOCK_SIZE == 15) { + SEC_PRINT("\n"); + } + } + SEC_PRINT("\n-------------------\n"); + + for (int i : failed) { + SEC_PRINT("%d: %s\n", i, suite.getTestEntry(i).first); + } + } + + TestCreds::shutdown(); + + return static_cast(failed.size()); +} + +int main(int argc, char* argv[]) { + return testIt(argc, argv); +} diff --git a/test/main/cpp/sec_api_utest_main.h b/test/main/cpp/sec_api_utest_main.h new file mode 100644 index 0000000..f9e0fe3 --- /dev/null +++ b/test/main/cpp/sec_api_utest_main.h @@ -0,0 +1,31 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SEC_API_UTEST_MAIN_H +#define SEC_API_UTEST_MAIN_H + +#ifdef __cplusplus +extern "C" { +#endif + +int testIt(int argc, char* argv[]); + +#ifdef __cplusplus +} +#endif +#endif // SEC_API_UTEST_MAIN_H diff --git a/test/main/cpp/sign.cpp b/test/main/cpp/sign.cpp new file mode 100644 index 0000000..1a79023 --- /dev/null +++ b/test/main/cpp/sign.cpp @@ -0,0 +1,483 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sign.h" // NOLINT +#include "digest.h" +#include "sec_adapter_utils.h" +#include "test_ctx.h" + +static Sec_Result BigNumToBuffer(const BIGNUM* bignum, SEC_BYTE* buffer, SEC_SIZE buffer_len) { + SEC_SIZE num_bytes; + + memset(buffer, 0, buffer_len); + num_bytes = BN_num_bytes(bignum); + + if (num_bytes > buffer_len) { + SEC_LOG_ERROR("Buffer not large enough. needed: %d, actual: %d", num_bytes, buffer_len); + return SEC_RESULT_FAILURE; + } + + BN_bn2bin(bignum, buffer + buffer_len - num_bytes); + + return SEC_RESULT_SUCCESS; +} + +std::vector signOpenSSL(Sec_SignatureAlgorithm alg, TestKey key, const std::vector& input) { + std::vector digest; + const EVP_MD* evp_md; + + bool pss = (alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS); + + if (alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST) { + digest = input; + evp_md = EVP_sha1(); + } else if (alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST) { + digest = input; + evp_md = EVP_sha256(); + } else if (alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS) { + digest = digestOpenSSL(SEC_DIGESTALGORITHM_SHA1, input); + evp_md = EVP_sha1(); + } else if (alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS || + alg == SEC_SIGNATUREALGORITHM_ECDSA_NISTP256) { + digest = digestOpenSSL(SEC_DIGESTALGORITHM_SHA256, input); + evp_md = EVP_sha256(); + } else { + SEC_LOG_ERROR("Unknown signature algorithm"); + return {}; + } + + TestCtx::printHex("digest to sign", digest); + + if (SecKey_IsEcc(TestCreds::getKeyType(key)) == SEC_TRUE) { + ECDSA_SIG* esig; + + EC_KEY* ec_key = TestCreds::asOpenSslEcKey(key); + if (ec_key == nullptr) { + SEC_LOG_ERROR("TestCreds::asOpenSslEcKey failed"); + return {}; + } + + esig = ECDSA_do_sign(&digest[0], static_cast(digest.size()), ec_key); + SEC_ECC_FREE(ec_key); + + if (esig == nullptr) { + SEC_LOG_ERROR("ECDSA_do_sign failed"); + return {}; + } + + std::vector sig; + sig.resize(256); + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + BigNumToBuffer(esig->r, &sig[0], SEC_ECC_NISTP256_KEY_LEN); + BigNumToBuffer(esig->s, &sig[SEC_ECC_NISTP256_KEY_LEN], SEC_ECC_NISTP256_KEY_LEN); +#else + const BIGNUM* esigr = nullptr; + const BIGNUM* esigs = nullptr; + ECDSA_SIG_get0(esig, &esigr, &esigs); + BigNumToBuffer(const_cast(esigr), &sig[0], SEC_ECC_NISTP256_KEY_LEN); + BigNumToBuffer(const_cast(esigs), &sig[SEC_ECC_NISTP256_KEY_LEN], SEC_ECC_NISTP256_KEY_LEN); +#endif + ECDSA_SIG_free(esig); + + sig.resize(SecSignature_GetEccSignatureSize(alg)); + return sig; + } + + EVP_PKEY* evp_pkey = TestCreds::asOpenSslEvpPkey(key); + if (evp_pkey == nullptr) { + SEC_LOG_ERROR("TestCreds::asOpenSslEvpPkey failed"); + return {}; + } + + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(evp_pkey, nullptr); + if (ctx == nullptr) { + EVP_PKEY_free(evp_pkey); + SEC_LOG_ERROR("EVP_PKEY_CTX_new failed"); + return {}; + } + + if (EVP_PKEY_sign_init(ctx) <= 0 || + EVP_PKEY_CTX_set_rsa_padding(ctx, pss ? RSA_PKCS1_PSS_PADDING : RSA_PKCS1_PADDING) <= 0 || + EVP_PKEY_CTX_set_signature_md(ctx, evp_md) <= 0) { + EVP_PKEY_free(evp_pkey); + EVP_PKEY_CTX_free(ctx); + SEC_LOG_ERROR("Could not setup EVP_PKEY_CTX"); + return {}; + } + + if (pss && EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, evp_md == EVP_sha1() ? 20 : 32) <= 0) { + EVP_PKEY_free(evp_pkey); + EVP_PKEY_CTX_free(ctx); + SEC_LOG_ERROR("Could not setup EVP_PKEY_CTX"); + return {}; + } + + std::vector sig; + sig.resize(256); + size_t siglen = 256; + + if (EVP_PKEY_sign(ctx, nullptr, &siglen, &digest[0], digest.size()) <= 0 || + EVP_PKEY_sign(ctx, &sig[0], &siglen, &digest[0], digest.size()) <= 0) { + EVP_PKEY_free(evp_pkey); + EVP_PKEY_CTX_free(ctx); + SEC_LOG_ERROR("EVP_PKEY_sign failed"); + return {}; + } + + EVP_PKEY_free(evp_pkey); + EVP_PKEY_CTX_free(ctx); + + sig.resize(siglen); + + return sig; +} + +static EC_KEY* ECCFromPubBinary(Sec_ECCRawPublicKey* binary) { + BN_CTX* ctx = BN_CTX_new(); + + if (binary->type != SEC_KEYTYPE_ECC_NISTP256_PUBLIC && binary->type != SEC_KEYTYPE_ECC_NISTP256) + return nullptr; + + EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); //create ec_key structure with NIST p256 curve; + const EC_GROUP* group = EC_KEY_get0_group(ec_key); + EC_POINT* ec_point = EC_POINT_new(group); + BN_CTX_start(ctx); + BIGNUM* xp; + BIGNUM* yp; + + do { + if (((xp = BN_CTX_get(ctx)) == nullptr) || ((yp = BN_CTX_get(ctx)) == nullptr)) + break; + + EC_POINT_set_affine_coordinates_GFp(group, ec_point, + BN_bin2bn(binary->x, static_cast(Sec_BEBytesToUint32(binary->key_len)), xp), + BN_bin2bn(binary->y, static_cast(Sec_BEBytesToUint32(binary->key_len)), yp), ctx); + EC_KEY_set_public_key(ec_key, ec_point); + + } while (false); + + EC_POINT_free(ec_point); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + + return ec_key; +} + +static SEC_BOOL verifyOpenSSLEccPub(Sec_SignatureAlgorithm alg, Sec_ECCRawPublicKey* eccPub, + const std::vector& input, const std::vector& sig) { + + std::vector digest; + + if (alg == SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST) { + digest = input; + } else if (alg == SEC_SIGNATUREALGORITHM_ECDSA_NISTP256) { + digest = digestOpenSSL(SEC_DIGESTALGORITHM_SHA256, input); + } else { + SEC_LOG_ERROR("Unknown signature algorithm"); + return SEC_FALSE; + } + + TestCtx::printHex("digest to verify", digest); + + if (sig.size() != SecSignature_GetEccSignatureSize(alg)) { + SEC_LOG_ERROR("Incorrect ECC signature size"); + return SEC_FALSE; + } + + EC_KEY* ec_key = ECCFromPubBinary(eccPub); + if (ec_key == nullptr) { + SEC_LOG_ERROR("TestCreds::_ECCFromPubBinary failed"); + return SEC_FALSE; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + ECDSA_SIG esig; + esig.r = BN_new(); + esig.s = BN_new(); + BN_bin2bn(&sig[0], SEC_ECC_NISTP256_KEY_LEN, esig.r); + BN_bin2bn(&sig[SEC_ECC_NISTP256_KEY_LEN], SEC_ECC_NISTP256_KEY_LEN, esig.s); + int openssl_res = ECDSA_do_verify(&digest[0], static_cast(digest.size()), &esig, ec_key); + BN_free(esig.r); + BN_free(esig.s); +#else + ECDSA_SIG* esig = ECDSA_SIG_new(); + BIGNUM* r = BN_new(); + BIGNUM* s = BN_new(); + BN_bin2bn(&sig[0], SEC_ECC_NISTP256_KEY_LEN, r); + BN_bin2bn(&sig[SEC_ECC_NISTP256_KEY_LEN], SEC_ECC_NISTP256_KEY_LEN, s); + ECDSA_SIG_set0(esig, r, s); + int openssl_res = ECDSA_do_verify(&digest[0], static_cast(digest.size()), esig, ec_key); + ECDSA_SIG_free(esig); +#endif + SEC_ECC_FREE(ec_key); + + if (openssl_res != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("ECDSA_do_verify failed"); + return SEC_FALSE; + } + + return SEC_TRUE; +} + +SEC_BOOL verifyOpenSSL(Sec_SignatureAlgorithm alg, TestKey key, const std::vector& input, + const std::vector& sig) { + + std::vector digest; + const EVP_MD* evp_md; + + bool pss = (alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS); + + if (alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS_DIGEST || alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST) { + digest = input; + evp_md = EVP_sha1(); + } else if (alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST) { + digest = input; + evp_md = EVP_sha256(); + } else if (alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS || alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS) { + digest = digestOpenSSL(SEC_DIGESTALGORITHM_SHA1, input); + evp_md = EVP_sha1(); + } else if (alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS || alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS || + alg == SEC_SIGNATUREALGORITHM_ECDSA_NISTP256) { + digest = digestOpenSSL(SEC_DIGESTALGORITHM_SHA256, input); + evp_md = EVP_sha256(); + } else { + SEC_LOG_ERROR("Unknown signature algorithm"); + return SEC_FALSE; + } + + TestCtx::printHex("digest to verify", digest); + + if (SecKey_IsEcc(TestCreds::getKeyType(key)) == SEC_TRUE) { + if (sig.size() != SecSignature_GetEccSignatureSize(alg)) { + SEC_LOG_ERROR("Incorrect ECC signature size"); + return SEC_FALSE; + } + + EC_KEY* ec_key = TestCreds::asOpenSslEcKey(key); + if (ec_key == nullptr) { + SEC_LOG_ERROR("TestCreds::asOpenSslEcKey failed"); + return SEC_FALSE; + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + ECDSA_SIG esig; + esig.r = BN_new(); + esig.s = BN_new(); + BN_bin2bn(&sig[0], SEC_ECC_NISTP256_KEY_LEN, esig.r); + BN_bin2bn(&sig[SEC_ECC_NISTP256_KEY_LEN], SEC_ECC_NISTP256_KEY_LEN, esig.s); + int openssl_res = ECDSA_do_verify(&digest[0], static_cast(digest.size()), &esig, ec_key); + BN_free(esig.r); + BN_free(esig.s); +#else + ECDSA_SIG* esig = ECDSA_SIG_new(); + BIGNUM* r = BN_new(); + BIGNUM* s = BN_new(); + BN_bin2bn(&sig[0], SEC_ECC_NISTP256_KEY_LEN, r); + BN_bin2bn(&sig[SEC_ECC_NISTP256_KEY_LEN], SEC_ECC_NISTP256_KEY_LEN, s); + ECDSA_SIG_set0(esig, r, s); + int openssl_res = ECDSA_do_verify(&digest[0], static_cast(digest.size()), esig, ec_key); + ECDSA_SIG_free(esig); +#endif + SEC_ECC_FREE(ec_key); + + if (openssl_res != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("ECDSA_do_verify failed"); + return SEC_FALSE; + } + + return SEC_TRUE; + } + + EVP_PKEY* evp_pkey = TestCreds::asOpenSslEvpPkey(key); + if (evp_pkey == nullptr) { + SEC_LOG_ERROR("TestCreds::asOpenSslEvpPkey failed"); + return SEC_FALSE; + } + + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(evp_pkey, nullptr); + if (ctx == nullptr) { + EVP_PKEY_free(evp_pkey); + SEC_LOG_ERROR("EVP_PKEY_CTX_new failed"); + return SEC_FALSE; + } + + if (EVP_PKEY_verify_init(ctx) <= 0 || + EVP_PKEY_CTX_set_rsa_padding(ctx, pss ? RSA_PKCS1_PSS_PADDING : RSA_PKCS1_PADDING) <= 0 || + EVP_PKEY_CTX_set_signature_md(ctx, evp_md) <= 0) { + EVP_PKEY_free(evp_pkey); + EVP_PKEY_CTX_free(ctx); + SEC_LOG_ERROR("Could not setup EVP_PKEY_CTX"); + return SEC_FALSE; + } + + if (pss && EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, evp_md == EVP_sha1() ? 20 : 32) <= 0) { + EVP_PKEY_free(evp_pkey); + EVP_PKEY_CTX_free(ctx); + SEC_LOG_ERROR("Could not setup EVP_PKEY_CTX"); + return SEC_FALSE; + } + + if (EVP_PKEY_verify(ctx, &sig[0], sig.size(), &digest[0], digest.size()) <= 0) { + EVP_PKEY_free(evp_pkey); + EVP_PKEY_CTX_free(ctx); + SEC_LOG_ERROR("EVP_PKEY_verify failed"); + return SEC_FALSE; + } + + EVP_PKEY_free(evp_pkey); + EVP_PKEY_CTX_free(ctx); + + return SEC_TRUE; +} + +std::vector signSecApi(TestCtx* ctx, Sec_SignatureAlgorithm alg, Sec_KeyHandle* keyHandle, + const std::vector& input) { + + Sec_SignatureHandle* signatureHandle = ctx->acquireSignature(alg, SEC_SIGNATUREMODE_SIGN, keyHandle); + if (signatureHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireSignature failed"); + return {}; + } + + std::vector sig; + sig.resize(256); + SEC_SIZE sig_len; + + if (SecSignature_Process(signatureHandle, const_cast(&input[0]), input.size(), &sig[0], &sig_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_Process failed"); + return {}; + } + + sig.resize(sig_len); + + return sig; +} + +SEC_BOOL verifySecApi(TestCtx* ctx, Sec_SignatureAlgorithm alg, Sec_KeyHandle* keyHandle, + const std::vector& input, const std::vector& sig) { + + Sec_SignatureHandle* signatureHandle = ctx->acquireSignature(alg, SEC_SIGNATUREMODE_VERIFY, keyHandle); + if (signatureHandle == nullptr) { + SEC_LOG_ERROR("TestCtx::acquireSignature failed"); + return SEC_FALSE; + } + + SEC_SIZE sig_len = sig.size(); + + if (SecSignature_Process(signatureHandle, const_cast(&input[0]), input.size(), + const_cast(&sig[0]), &sig_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_Process failed"); + return SEC_FALSE; + } + + return SEC_TRUE; +} + +Sec_Result testSignature(SEC_OBJECTID id, TestKey pub, TestKey priv, TestKc kc, Sec_StorageLoc loc, + Sec_SignatureAlgorithm alg, Sec_SignatureMode mode, SEC_SIZE inputSize) { + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if (mode == SEC_SIGNATUREMODE_SIGN && + (alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PKCS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PKCS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA1_PSS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_RSA_SHA256_PSS_DIGEST || + alg == SEC_SIGNATUREALGORITHM_ECDSA_NISTP256_DIGEST)) { + SEC_LOG_ERROR("Signature digest is not supported in SecApi 3"); + return SEC_RESULT_SUCCESS; + } + //mode + bool testSign = (mode == SEC_SIGNATUREMODE_SIGN); + + Sec_KeyHandle* keyHandle; + if (testSign) { + if ((keyHandle = ctx.provisionKey(id, loc, priv, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + } else { + if ((keyHandle = ctx.provisionKey(id, loc, pub, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + } + + //gen clear input + std::vector clear = TestCtx::random(inputSize); + TestCtx::printHex("clear", clear); + + //sign + std::vector sig; + if (testSign) { + sig = signSecApi(&ctx, alg, keyHandle, clear); + } else { + //use openssl to sign + sig = signOpenSSL(alg, priv, clear); + } + + TestCtx::printHex("sig", sig); + + //verify + SEC_BOOL ver_res; + if (testSign) { + //use openssl to verify + if (kc == TESTKC_GENERATED) { + //extract pub from the generated key + Sec_ECCRawPublicKey eccPub; + if (SecKey_ExtractECCPublicKey(keyHandle, &eccPub) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExtractECCPublicKey failed"); + return SEC_RESULT_FAILURE; + } + + ver_res = verifyOpenSSLEccPub(alg, &eccPub, clear, sig); + } else { + ver_res = verifyOpenSSL(alg, pub, clear, sig); + } + } else { + //use sec api to verify + ver_res = verifySecApi(&ctx, alg, keyHandle, clear, sig); + } + + //check if results match + if (ver_res == SEC_FALSE) { + SEC_LOG_ERROR("Verification failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} diff --git a/test/main/cpp/sign.h b/test/main/cpp/sign.h new file mode 100644 index 0000000..ab5562a --- /dev/null +++ b/test/main/cpp/sign.h @@ -0,0 +1,34 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SIGN_H +#define SIGN_H + +#include "sec_security.h" +#include "test_creds.h" +#include + +std::vector signOpenSSL(Sec_SignatureAlgorithm alg, TestKey key, const std::vector& input); + +SEC_BOOL verifyOpenSSL(Sec_SignatureAlgorithm alg, TestKey key, const std::vector& input, + const std::vector& sig); + +Sec_Result testSignature(SEC_OBJECTID id, TestKey pub, TestKey priv, TestKc kc, Sec_StorageLoc loc, + Sec_SignatureAlgorithm alg, Sec_SignatureMode mode, SEC_SIZE inputSize); + +#endif // SIGN_H diff --git a/test/main/cpp/svp.cpp b/test/main/cpp/svp.cpp new file mode 100644 index 0000000..40aae1a --- /dev/null +++ b/test/main/cpp/svp.cpp @@ -0,0 +1,335 @@ +/** + * Copyright 2020-2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "svp.h" +#include "cipher.h" +#include "test_ctx.h" + +#define MAX_BUFFER_SIZE (64 * 1024) + +Sec_Result testOpaqueMalloc() { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_OpaqueBufferHandle* opaqueBufferHandle = nullptr; + SEC_BYTE input[MAX_BUFFER_SIZE]; + + if (SecOpaqueBuffer_Malloc(sizeof(input), &opaqueBufferHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Sec_OpaqueBufferMalloc failed"); + return SEC_RESULT_FAILURE; + } + + if (SecOpaqueBuffer_Write(opaqueBufferHandle, 0, input, sizeof(input)) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Sec_OpaqueBufferWrite failed"); + return SEC_RESULT_FAILURE; + } + + if (SecOpaqueBuffer_Free(opaqueBufferHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Sec_OpaqueBufferFree failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testSecureBootEnabled() { +// SecCodeIntegrity_SecureBootEnabled is an unimplemented feature +#if 0 + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if (SecCodeIntegrity_SecureBootEnabled() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCodeIntegrity_SecureBootEnabled failed"); + return SEC_RESULT_FAILURE; + } +#endif + return SEC_RESULT_SUCCESS; +} + +Sec_Result testSetTime() { +// SecSVP_SetTime is an unimplemented feature +#if 0 + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if (SecSVP_SetTime(time(nullptr)) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSVP_SetTime failed"); + return SEC_RESULT_FAILURE; + } +#endif + return SEC_RESULT_SUCCESS; +} + +Sec_Result testKeycheckOpaque(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle; + if ((keyHandle = ctx.provisionKey(id, loc, key, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + Sec_CipherHandle* cipherHandle = nullptr; + if (SecCipher_GetInstance(ctx.proc(), SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, SEC_CIPHERMODE_DECRYPT, keyHandle, + nullptr, + &cipherHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + std::vector expected = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector input = opensslAesEcb(key, SEC_CIPHERMODE_ENCRYPT, SEC_FALSE, nullptr, expected); + + TestCtx::printHex("input", input); + TestCtx::printHex("expected", expected); + + Sec_OpaqueBufferHandle* opaqueBufferHandle = nullptr; + if (SecOpaqueBuffer_Malloc(256, &opaqueBufferHandle) != SEC_RESULT_SUCCESS) { + SecCipher_Release(cipherHandle); + SEC_LOG_ERROR("Sec_OpaqueBufferMalloc failed"); + return SEC_RESULT_FAILURE; + } + + if (SecOpaqueBuffer_Write(opaqueBufferHandle, 0, &input[0], input.size()) != SEC_RESULT_SUCCESS) { + SecOpaqueBuffer_Free(opaqueBufferHandle); + SecCipher_Release(cipherHandle); + SEC_LOG_ERROR("Sec_OpaqueBufferWrite failed"); + return SEC_RESULT_FAILURE; + } + + if (SecCipher_KeyCheckOpaque(cipherHandle, opaqueBufferHandle, SEC_AES_BLOCK_SIZE, &expected[0]) != + SEC_RESULT_SUCCESS) { + SecOpaqueBuffer_Free(opaqueBufferHandle); + SecCipher_Release(cipherHandle); + SEC_LOG_ERROR("SecCipher_KeyCheckOpaque failed"); + return SEC_RESULT_FAILURE; + } + + /* 2.2 checks for 'checkLength' arg */ + if (SecCipher_KeyCheckOpaque(cipherHandle, opaqueBufferHandle, 8, &expected[0]) != SEC_RESULT_SUCCESS) { + SecOpaqueBuffer_Free(opaqueBufferHandle); + SecCipher_Release(cipherHandle); + SEC_LOG_ERROR("SecCipher_KeyCheckOpaque failed"); + return SEC_RESULT_FAILURE; + } + if (SecCipher_KeyCheckOpaque(cipherHandle, opaqueBufferHandle, 7, &expected[0]) == SEC_RESULT_SUCCESS) { + SecOpaqueBuffer_Free(opaqueBufferHandle); + SecCipher_Release(cipherHandle); + SEC_LOG_ERROR("Expected SecCipher_KeyCheckOpaque to fail with checkLength < 8"); + return SEC_RESULT_FAILURE; + } + if (SecCipher_KeyCheckOpaque(cipherHandle, opaqueBufferHandle, 17, &expected[0]) == SEC_RESULT_SUCCESS) { + SecOpaqueBuffer_Free(opaqueBufferHandle); + SecCipher_Release(cipherHandle); + SEC_LOG_ERROR("Expected SecCipher_KeyCheckOpaque to fail with checkLength > 16"); + return SEC_RESULT_FAILURE; + } + + SecOpaqueBuffer_Free(opaqueBufferHandle); + SecCipher_Release(cipherHandle); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testProcessOpaque(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, + Sec_CipherAlgorithm cipher_algorithm) { + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle* keyHandle; + if ((keyHandle = ctx.provisionKey(id, loc, key, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + Sec_CipherHandle* cipherHandle = nullptr; + if (SecCipher_GetInstance(ctx.proc(), cipher_algorithm, SEC_CIPHERMODE_DECRYPT, keyHandle, &iv[0], &cipherHandle) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + Sec_OpaqueBufferHandle* inOpaqueBufferHandle = nullptr; + if (SecOpaqueBuffer_Malloc(256, &inOpaqueBufferHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Sec_OpaqueBufferMalloc failed"); + SecCipher_Release(cipherHandle); + return SEC_RESULT_FAILURE; + } + + Sec_OpaqueBufferHandle* outOpaqueBufferHandle = nullptr; + if (SecOpaqueBuffer_Malloc(256, &outOpaqueBufferHandle) != SEC_RESULT_SUCCESS) { + SecOpaqueBuffer_Free(outOpaqueBufferHandle); + SecCipher_Release(cipherHandle); + SEC_LOG_ERROR("Sec_OpaqueBufferMalloc failed"); + return SEC_RESULT_FAILURE; + } + + SEC_SIZE written = 0; + + if (SecCipher_ProcessOpaque(cipherHandle, inOpaqueBufferHandle, outOpaqueBufferHandle, 256, SEC_TRUE, &written) != + SEC_RESULT_SUCCESS) { + SecOpaqueBuffer_Free(inOpaqueBufferHandle); + SecOpaqueBuffer_Free(outOpaqueBufferHandle); + SecCipher_Release(cipherHandle); + SEC_LOG_ERROR("SecCipher_ProcessOpaque failed"); + return SEC_RESULT_FAILURE; + } + + SecOpaqueBuffer_Free(inOpaqueBufferHandle); + SecOpaqueBuffer_Free(outOpaqueBufferHandle); + SecCipher_Release(cipherHandle); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testCopyOpaque() { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_OpaqueBufferHandle* inOpaqueBufferHandle = nullptr; + if (SecOpaqueBuffer_Malloc(256, &inOpaqueBufferHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Sec_OpaqueBufferMalloc failed"); + return SEC_RESULT_FAILURE; + } + + Sec_OpaqueBufferHandle* outOpaqueBufferHandle = nullptr; + if (SecOpaqueBuffer_Malloc(256, &outOpaqueBufferHandle) != SEC_RESULT_SUCCESS) { + SecOpaqueBuffer_Free(inOpaqueBufferHandle); + SEC_LOG_ERROR("Sec_OpaqueBufferMalloc failed"); + return SEC_RESULT_FAILURE; + } + + SEC_BYTE tmp[128]; + if (SecOpaqueBuffer_Write(inOpaqueBufferHandle, 128, tmp, 128) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecOpaqueBuffer_Write failed"); + SecOpaqueBuffer_Free(inOpaqueBufferHandle); + SecOpaqueBuffer_Free(outOpaqueBufferHandle); + return SEC_RESULT_FAILURE; + } + + if (SecOpaqueBuffer_Copy(outOpaqueBufferHandle, 0, inOpaqueBufferHandle, 128, 128) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecOpaqueBuffer_Copy failed"); + SecOpaqueBuffer_Free(inOpaqueBufferHandle); + SecOpaqueBuffer_Free(outOpaqueBufferHandle); + return SEC_RESULT_FAILURE; + } + + SecOpaqueBuffer_Free(inOpaqueBufferHandle); + SecOpaqueBuffer_Free(outOpaqueBufferHandle); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testProcessDataShiftOpaque(SEC_OBJECTID id, TestKey key, TestKc kc, + Sec_StorageLoc loc) { +// SecCipher_ProcessCtrWithOpaqueDataShift is an unimplemented feature. +#if 0 + TestCtx ctx; + Sec_Result result = SEC_RESULT_FAILURE; + Sec_OpaqueBufferHandle *inputHandle1 = nullptr; + Sec_OpaqueBufferHandle *inputHandle2 = nullptr; + Sec_OpaqueBufferHandle *outputHandle = nullptr; + SEC_SIZE written = 0; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyHandle *handle = nullptr; + if ((handle = ctx.provisionKey(id, loc, key, kc)) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + Sec_CipherHandle *cipherHandle = nullptr; + if (!= SEC_RESULT_SUCCESS) SecCipher_GetInstance(ctx.proc(), + SEC_CIPHERALGORITHM_AES_CTR, SEC_CIPHERMODE_DECRYPT, handle, + &iv[0], &cipherHandle)) { + SEC_LOG_ERROR("SecCipher_GetInstance failed"); + break; + } + + if (SecOpaqueBuffer_Malloc(8, &inputHandle1) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Sec_OpaqueBufferMalloc failed"); + break; + } + + if (SecOpaqueBuffer_Malloc(256 - 8, &inputHandle2) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Sec_OpaqueBufferMalloc failed"); + break; + } + + if (SecOpaqueBuffer_Malloc(256, &outputHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("Sec_OpaqueBufferMalloc failed"); + break; + } + + if (!= SEC_RESULT_SUCCESS) SecCipher_ProcessOpaque(cipherHandle, + inputHandle1, outputHandle, 8, SEC_FALSE, &written)) { + SEC_LOG_ERROR("SecCipher_ProcessOpaque failed"); + break; + } + + if (!= SEC_RESULT_SUCCESS) + SecCipher_ProcessCtrWithOpaqueDataShift(cipherHandle, inputHandle2, outputHandle, 256 - 8, &written, 8)) { + SEC_LOG_ERROR("SecCipher_ProcessCtrWithOpaqueDataShift failed"); + break; + } + + + result = SEC_RESULT_SUCCESS; + + } while(false); + + + if (inputHandle1) + SecOpaqueBuffer_Free(inputHandle1); + if (inputHandle2) + SecOpaqueBuffer_Free(inputHandle2); + if (outputHandle) + SecOpaqueBuffer_Free(outputHandle); + if (cipherHandle) + SecCipher_Release(cipherHandle); + + return result; +#endif + return SEC_RESULT_SUCCESS; +} diff --git a/test/main/cpp/svp.h b/test/main/cpp/svp.h new file mode 100644 index 0000000..fcd01f1 --- /dev/null +++ b/test/main/cpp/svp.h @@ -0,0 +1,41 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SVP_H +#define SVP_H + +#include "sec_security.h" +#include "test_creds.h" +#include + +Sec_Result testOpaqueMalloc(); + +Sec_Result testCopyOpaque(); + +Sec_Result testSecureBootEnabled(); + +Sec_Result testSetTime(); + +Sec_Result testKeycheckOpaque(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc); + +Sec_Result testProcessOpaque(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc, + Sec_CipherAlgorithm cipher_algorithm); + +Sec_Result testProcessDataShiftOpaque(SEC_OBJECTID id, TestKey key, TestKc kc, Sec_StorageLoc loc); + +#endif // SVP_H diff --git a/test/main/cpp/test_creds_clear.cpp b/test/main/cpp/test_creds_clear.cpp new file mode 100644 index 0000000..a5077a6 --- /dev/null +++ b/test/main/cpp/test_creds_clear.cpp @@ -0,0 +1,1303 @@ +/** + * Copyright 2020-2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sec_adapter_utils.h" +#include "test_creds.h" +#include "test_ctx.h" +#include + +unsigned char sym128_a_bin[] = { + 0x05, 0xe8, 0x38, 0x84, 0xc4, 0x0c, 0x0a, 0xd8, 0x20, 0x79, 0x9e, 0xb5, 0x81, 0xf8, 0x74, 0x28}; + +unsigned int sym128_a_bin_len = 16; + +unsigned char sym160_a_bin[] = { + 0xaa, 0xbe, 0xf6, 0x0b, 0x97, 0x3a, 0x55, 0xc0, 0x3d, 0xa0, 0xdd, 0x85, 0xef, 0xb3, 0xbd, 0xc0, + 0x2f, 0x09, 0x32, 0x88}; + +unsigned int sym160_a_bin_len = 20; + +unsigned char sym256_a_bin[] = { + 0x6f, 0x08, 0x2b, 0xb5, 0xa9, 0xb9, 0x27, 0x3b, 0xd5, 0x4b, 0xaf, 0xfb, 0xe9, 0x63, 0xc7, 0xd2, + 0xad, 0xde, 0xf1, 0x68, 0x87, 0x07, 0xdb, 0xc5, 0xbf, 0x7c, 0x2b, 0x33, 0x5f, 0x29, 0x99, 0x44}; + +unsigned int sym256_a_bin_len = 32; + +unsigned char rsa1024_der[] = { + 0x30, 0x82, 0x02, 0x5d, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xc4, 0x32, 0x70, 0x15, 0xb3, + 0x53, 0xd5, 0xaf, 0x26, 0xc2, 0xcd, 0x6e, 0x87, 0x9f, 0x13, 0x10, 0x9e, 0x3d, 0x8d, 0x6c, 0xb4, + 0x1e, 0xc8, 0xbb, 0xf1, 0xbf, 0x7a, 0xc0, 0xce, 0xbf, 0x5c, 0x00, 0x1f, 0x83, 0xd8, 0xe3, 0xf7, + 0xe8, 0xa3, 0x79, 0x61, 0xd4, 0x3a, 0xae, 0x49, 0x6d, 0x38, 0x1d, 0x12, 0x74, 0xba, 0x9c, 0xb4, + 0x38, 0x61, 0x6b, 0x44, 0x1d, 0xac, 0xf7, 0xa7, 0x7d, 0x8a, 0x80, 0x9f, 0x56, 0x67, 0xb2, 0xe5, + 0x45, 0xbc, 0x0d, 0xde, 0xde, 0x63, 0x06, 0x13, 0x4d, 0x06, 0x2e, 0xe2, 0xf9, 0xfa, 0xe4, 0x3b, + 0xa6, 0xa0, 0x49, 0xbb, 0x11, 0x23, 0xf8, 0x68, 0x85, 0x3c, 0x1b, 0x92, 0xe1, 0x6c, 0x42, 0x37, + 0xe3, 0x1b, 0x7c, 0x7a, 0x25, 0x91, 0x30, 0xd5, 0xa5, 0xf3, 0xbb, 0x91, 0x23, 0xdf, 0x23, 0x94, + 0xb8, 0xf2, 0x61, 0x4f, 0xba, 0x73, 0xd1, 0x4b, 0x2b, 0x89, 0x8f, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x81, 0x81, 0x00, 0x8c, 0x8d, 0xad, 0xaa, 0x7e, 0x2b, 0xe2, 0xfb, 0x75, 0x83, 0x3c, 0xf4, + 0xa0, 0x08, 0x1f, 0xfa, 0x59, 0xc2, 0xb2, 0xdc, 0x5b, 0x35, 0x6a, 0x8c, 0xea, 0x25, 0x48, 0xe5, + 0x73, 0xb7, 0xb7, 0x4b, 0x07, 0x48, 0xc7, 0x4a, 0x99, 0xc1, 0x79, 0xcb, 0x6d, 0x80, 0x43, 0x01, + 0xb4, 0xec, 0x9f, 0xb4, 0x84, 0x12, 0x47, 0xd6, 0x17, 0x6e, 0x04, 0xac, 0x79, 0xc1, 0xe0, 0xb6, + 0x12, 0xb1, 0x67, 0x54, 0x77, 0xa2, 0xd4, 0x80, 0xf2, 0x87, 0xb9, 0x56, 0xcc, 0xde, 0xc2, 0x52, + 0x09, 0x2d, 0x5b, 0x7b, 0x0e, 0xfa, 0xe2, 0xd8, 0x9e, 0x41, 0xaf, 0xfc, 0x42, 0x0d, 0x24, 0x6c, + 0xe2, 0x8b, 0x3a, 0xae, 0x5c, 0x17, 0x11, 0xbb, 0x33, 0x13, 0xb8, 0x66, 0xd6, 0xc6, 0xb1, 0x2f, + 0xef, 0xf0, 0x68, 0x0e, 0x2c, 0xf9, 0x41, 0xd2, 0x7f, 0xe0, 0x15, 0xe2, 0x33, 0xf5, 0xd8, 0xb6, + 0x01, 0xb0, 0x64, 0x91, 0x02, 0x41, 0x00, 0xf3, 0x37, 0x23, 0xf9, 0xff, 0x24, 0x37, 0x63, 0x10, + 0x19, 0x6f, 0x6c, 0x35, 0xa0, 0x41, 0x3c, 0x2c, 0x00, 0xa8, 0x71, 0xa9, 0x09, 0x0e, 0x1f, 0xc7, + 0x87, 0x6e, 0x67, 0xf3, 0x8a, 0x76, 0x5f, 0xfb, 0x69, 0x44, 0x22, 0x88, 0x36, 0x1d, 0x31, 0xb9, + 0x79, 0xd3, 0x8c, 0x92, 0xb4, 0x0c, 0x0b, 0x72, 0xdd, 0x62, 0x47, 0x86, 0xd7, 0x7d, 0x63, 0xb1, + 0xe3, 0x30, 0xb4, 0x8f, 0x89, 0x63, 0x3b, 0x02, 0x41, 0x00, 0xce, 0x82, 0x96, 0xa8, 0x5c, 0x6a, + 0x8a, 0x50, 0x31, 0xf1, 0x9c, 0xe3, 0xaa, 0x0d, 0x89, 0xe4, 0xe2, 0x68, 0xe2, 0x25, 0xf7, 0xec, + 0x5e, 0xe8, 0xde, 0x68, 0x29, 0x84, 0xf2, 0x58, 0x68, 0xa8, 0xb3, 0x1b, 0x36, 0x68, 0x7c, 0x2d, + 0x21, 0xea, 0x92, 0xb5, 0x3a, 0x80, 0xc2, 0x45, 0xbb, 0xc4, 0xfc, 0x38, 0xb0, 0x33, 0xe2, 0xf1, + 0x93, 0x83, 0x48, 0x5d, 0x91, 0x31, 0xc4, 0x55, 0x65, 0xbd, 0x02, 0x40, 0x0e, 0x66, 0x2d, 0x53, + 0x17, 0xaf, 0xe5, 0x37, 0x90, 0x34, 0x71, 0x4c, 0x4e, 0xc0, 0x76, 0x1c, 0x41, 0xde, 0xa8, 0x1a, + 0x52, 0x8f, 0x9e, 0xae, 0x72, 0xf9, 0xa9, 0xa7, 0xad, 0xdb, 0x7c, 0xb6, 0xa2, 0x03, 0xd1, 0x6c, + 0xd9, 0xf3, 0x9a, 0x36, 0xdf, 0x6c, 0x3f, 0x02, 0x0b, 0x8d, 0x6d, 0x49, 0x20, 0x3b, 0xcb, 0x1d, + 0xc0, 0xf5, 0xf1, 0x0e, 0x7d, 0xf1, 0x9d, 0x68, 0x93, 0x36, 0xe7, 0x11, 0x02, 0x40, 0x4c, 0x12, + 0x93, 0x09, 0x26, 0x32, 0x21, 0x0d, 0x75, 0xb8, 0x79, 0x80, 0xec, 0x4d, 0xdc, 0x74, 0x32, 0x6b, + 0x4c, 0x93, 0x8c, 0x06, 0xc8, 0xd7, 0xa3, 0xc6, 0x5f, 0x35, 0x18, 0x49, 0x35, 0x14, 0xa0, 0x15, + 0xf0, 0x2f, 0x01, 0x3f, 0x66, 0xf5, 0x10, 0x62, 0x2e, 0x50, 0xec, 0x3f, 0xdf, 0xf1, 0xaa, 0xaf, + 0xff, 0x48, 0xbd, 0xdb, 0x1b, 0xea, 0x0a, 0xa8, 0x5d, 0x2a, 0x26, 0x17, 0x07, 0x49, 0x02, 0x41, + 0x00, 0xb6, 0xc4, 0x4b, 0x68, 0x82, 0xe8, 0x40, 0xc0, 0x70, 0x58, 0xdb, 0x68, 0x49, 0x30, 0x7d, + 0x6a, 0xf1, 0xfc, 0x9d, 0x66, 0x33, 0x10, 0x28, 0x1b, 0x54, 0x1d, 0x81, 0xf1, 0x88, 0x9a, 0x6b, + 0xb7, 0x1b, 0x7f, 0x36, 0x79, 0xce, 0x02, 0xec, 0x7c, 0x7e, 0x71, 0x37, 0x05, 0x46, 0x33, 0xee, + 0x3d, 0x71, 0x8f, 0xb6, 0x16, 0x6c, 0xa6, 0x64, 0xa9, 0xe4, 0x04, 0xc8, 0x12, 0xd7, 0x14, 0xcf, + 0xed}; + +unsigned int rsa1024_der_len = 609; + +unsigned char rsa1024_pub_der[] = { + 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xc4, 0x32, 0x70, + 0x15, 0xb3, 0x53, 0xd5, 0xaf, 0x26, 0xc2, 0xcd, 0x6e, 0x87, 0x9f, 0x13, 0x10, 0x9e, 0x3d, 0x8d, + 0x6c, 0xb4, 0x1e, 0xc8, 0xbb, 0xf1, 0xbf, 0x7a, 0xc0, 0xce, 0xbf, 0x5c, 0x00, 0x1f, 0x83, 0xd8, + 0xe3, 0xf7, 0xe8, 0xa3, 0x79, 0x61, 0xd4, 0x3a, 0xae, 0x49, 0x6d, 0x38, 0x1d, 0x12, 0x74, 0xba, + 0x9c, 0xb4, 0x38, 0x61, 0x6b, 0x44, 0x1d, 0xac, 0xf7, 0xa7, 0x7d, 0x8a, 0x80, 0x9f, 0x56, 0x67, + 0xb2, 0xe5, 0x45, 0xbc, 0x0d, 0xde, 0xde, 0x63, 0x06, 0x13, 0x4d, 0x06, 0x2e, 0xe2, 0xf9, 0xfa, + 0xe4, 0x3b, 0xa6, 0xa0, 0x49, 0xbb, 0x11, 0x23, 0xf8, 0x68, 0x85, 0x3c, 0x1b, 0x92, 0xe1, 0x6c, + 0x42, 0x37, 0xe3, 0x1b, 0x7c, 0x7a, 0x25, 0x91, 0x30, 0xd5, 0xa5, 0xf3, 0xbb, 0x91, 0x23, 0xdf, + 0x23, 0x94, 0xb8, 0xf2, 0x61, 0x4f, 0xba, 0x73, 0xd1, 0x4b, 0x2b, 0x89, 0x8f, 0x02, 0x03, 0x01, + 0x00, 0x01}; + +unsigned int rsa1024_pub_der_len = 162; + +unsigned char rsa2048_der[] = { + 0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xf2, 0x6a, 0x52, 0xa0, + 0x96, 0xc5, 0xb7, 0x86, 0x6f, 0xc4, 0x35, 0x39, 0x5e, 0x28, 0xd3, 0xa6, 0xab, 0x80, 0x6a, 0xbb, + 0x07, 0x10, 0x74, 0xea, 0xe0, 0xfc, 0xef, 0x54, 0xb0, 0x5e, 0x66, 0x34, 0x21, 0x89, 0x35, 0x09, + 0xb7, 0x46, 0xf8, 0xb6, 0xf8, 0x86, 0xc0, 0x55, 0xc6, 0xc5, 0x2e, 0x36, 0xcc, 0xb7, 0x24, 0x5e, + 0x56, 0xba, 0x2b, 0x8d, 0xfb, 0x8d, 0x7b, 0xe1, 0x4b, 0x9e, 0xdf, 0x08, 0x5c, 0xb2, 0x28, 0xc0, + 0x79, 0x45, 0xd1, 0xf3, 0x9c, 0x70, 0xd8, 0xaf, 0x50, 0x1c, 0xc3, 0x10, 0x24, 0x84, 0xc1, 0xb1, + 0x36, 0x3f, 0xd4, 0xbf, 0x10, 0x18, 0xf9, 0xd0, 0x72, 0xea, 0xce, 0xbf, 0x34, 0x8e, 0xb5, 0x97, + 0xcb, 0x8a, 0x5e, 0x8e, 0x7d, 0xed, 0x1f, 0xe3, 0xa9, 0xde, 0x5b, 0x3f, 0xc9, 0x33, 0x4c, 0x9b, + 0x3e, 0xd8, 0x29, 0x61, 0x6b, 0x30, 0xba, 0xa1, 0x36, 0xbd, 0x22, 0x33, 0xa1, 0x25, 0x73, 0xab, + 0x5f, 0x6f, 0x35, 0x48, 0xf1, 0xad, 0x96, 0x5f, 0x0d, 0x0c, 0xe5, 0xe9, 0x8d, 0xf9, 0xed, 0x0c, + 0xcf, 0x50, 0x3c, 0x24, 0x34, 0x9b, 0x9e, 0xbc, 0xf9, 0xfa, 0x19, 0x51, 0x37, 0x03, 0x5c, 0xd0, + 0x25, 0x1f, 0xe6, 0xb2, 0xf7, 0xff, 0x96, 0xb0, 0x61, 0x42, 0xbb, 0xc5, 0x64, 0x5f, 0x3d, 0xaf, + 0x6f, 0x14, 0xcc, 0x92, 0x74, 0x6a, 0x06, 0xfa, 0xb2, 0xd9, 0xe1, 0x4d, 0x42, 0x76, 0xab, 0x88, + 0xe3, 0x08, 0xa2, 0xad, 0x00, 0xb6, 0xf7, 0x78, 0x0c, 0xba, 0x08, 0x71, 0xbc, 0x8d, 0x98, 0x43, + 0x12, 0x63, 0x8d, 0x5e, 0x1f, 0x49, 0xb9, 0x5f, 0x0d, 0x55, 0x1a, 0x90, 0x63, 0x4c, 0xd8, 0xe8, + 0x0b, 0x9f, 0x3d, 0x10, 0xb4, 0xf9, 0x1b, 0x44, 0x3f, 0x43, 0xad, 0x35, 0x42, 0x3d, 0x3d, 0x21, + 0x8e, 0xa8, 0x08, 0x28, 0x47, 0x19, 0x14, 0x28, 0x1c, 0x95, 0xb7, 0x5d, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x02, 0x82, 0x01, 0x00, 0x17, 0xd3, 0x1d, 0x45, 0xcd, 0x2b, 0xa7, 0x6a, 0xde, 0x07, 0x31, + 0x97, 0xee, 0xca, 0x22, 0x6b, 0x0a, 0x05, 0xf7, 0xf7, 0x63, 0xad, 0x59, 0x60, 0x50, 0x36, 0x6b, + 0xab, 0x2e, 0x1e, 0x48, 0xfb, 0x2c, 0xa4, 0x98, 0x82, 0xd8, 0xf1, 0x6f, 0xb6, 0x43, 0xdc, 0xb2, + 0x80, 0x7b, 0xa5, 0x9b, 0x72, 0x25, 0x52, 0x86, 0x8f, 0x83, 0x04, 0x8f, 0x0d, 0x48, 0x40, 0xd2, + 0x81, 0x04, 0xf6, 0x9b, 0x42, 0xee, 0x21, 0x50, 0x62, 0x02, 0x49, 0x3b, 0x66, 0x2e, 0xe4, 0xe4, + 0x56, 0xee, 0x8f, 0xb5, 0x18, 0x1c, 0x0e, 0xb4, 0x1e, 0x9f, 0x8f, 0x4c, 0x70, 0x6e, 0xbb, 0x95, + 0xa9, 0xc7, 0xf1, 0x8d, 0xda, 0x3a, 0xe7, 0x1f, 0xaf, 0xc8, 0x80, 0x53, 0xe4, 0xbb, 0x39, 0x97, + 0xdd, 0xbf, 0x2f, 0x2c, 0x56, 0xab, 0x86, 0x90, 0x2b, 0xdd, 0xd4, 0x4e, 0xd4, 0xd5, 0x3d, 0x17, + 0xca, 0x8a, 0x5f, 0xb2, 0x41, 0xa1, 0xc6, 0x52, 0x80, 0xe5, 0xf0, 0x7a, 0xda, 0x35, 0xa5, 0x36, + 0xb9, 0x61, 0x15, 0x60, 0x39, 0x2f, 0x04, 0x1f, 0x6e, 0xee, 0x99, 0xee, 0xa5, 0x21, 0xcc, 0x4f, + 0xa3, 0x4e, 0x06, 0xc5, 0xbc, 0xa5, 0x34, 0xd6, 0x10, 0x57, 0x36, 0x85, 0xf7, 0x0a, 0x34, 0xfb, + 0xa5, 0x6b, 0x71, 0x9c, 0x4a, 0x43, 0x4a, 0xd0, 0xcb, 0xb4, 0x9a, 0xac, 0x4f, 0x9a, 0x29, 0xe8, + 0x68, 0x15, 0x00, 0xe4, 0x6c, 0x34, 0xc3, 0x73, 0x7e, 0xb5, 0x16, 0x2a, 0x17, 0x37, 0xc0, 0xb6, + 0xaf, 0x81, 0xb4, 0x73, 0xf2, 0xd3, 0x9f, 0xaa, 0x40, 0x73, 0x28, 0x0d, 0xfd, 0xb4, 0x67, 0x6e, + 0x82, 0xa8, 0x13, 0x34, 0xc0, 0x54, 0x88, 0xaf, 0x85, 0x6f, 0xd8, 0x33, 0x84, 0x82, 0xb5, 0xd0, + 0x5f, 0x7f, 0x21, 0xca, 0x6d, 0x86, 0x74, 0xb3, 0x89, 0x81, 0x19, 0x45, 0x47, 0x38, 0x41, 0x53, + 0x47, 0x86, 0xc6, 0xcd, 0x9d, 0x02, 0x81, 0x81, 0x00, 0xfd, 0x90, 0x23, 0xf9, 0xba, 0xc5, 0x69, + 0x7d, 0x11, 0xed, 0x69, 0x57, 0x73, 0x1d, 0x06, 0xe8, 0x1b, 0x37, 0x00, 0x69, 0x95, 0xc5, 0x63, + 0x86, 0x3f, 0xda, 0xb4, 0x11, 0xad, 0x69, 0x55, 0xf4, 0x76, 0x3c, 0xa0, 0x95, 0x65, 0x60, 0x29, + 0xb9, 0x69, 0x79, 0xca, 0x74, 0x50, 0xe3, 0x98, 0xc7, 0x90, 0xd7, 0xb8, 0xd4, 0xc5, 0x51, 0x10, + 0xa3, 0x10, 0x72, 0xf3, 0xdc, 0x91, 0xb4, 0xee, 0xdd, 0x9a, 0x57, 0xf1, 0x1b, 0x0e, 0xba, 0x0f, + 0x67, 0x1a, 0x94, 0x86, 0x36, 0x69, 0x20, 0xd5, 0x6b, 0x80, 0x50, 0x7d, 0x20, 0x53, 0xeb, 0xed, + 0x8a, 0xc5, 0xbc, 0xcb, 0x13, 0xb9, 0x0d, 0x49, 0x22, 0x7c, 0xeb, 0xa6, 0x97, 0x5a, 0x5b, 0x92, + 0x3a, 0x3d, 0x93, 0x5b, 0x86, 0x2c, 0x75, 0x0b, 0xfa, 0x68, 0xa6, 0xc1, 0x68, 0x07, 0x37, 0x01, + 0x9b, 0xd1, 0x51, 0xfe, 0xc0, 0x4c, 0x2d, 0x15, 0x9f, 0x02, 0x81, 0x81, 0x00, 0xf4, 0xbe, 0xc1, + 0x32, 0xad, 0xca, 0x28, 0x37, 0x7c, 0x17, 0x99, 0xde, 0x64, 0x9b, 0xc1, 0xf8, 0x23, 0x6a, 0xde, + 0xd8, 0x01, 0x59, 0x09, 0x10, 0x67, 0xb8, 0x27, 0xc5, 0xe6, 0xee, 0xf8, 0x16, 0x7c, 0x46, 0xa0, + 0x0b, 0x00, 0xf9, 0xff, 0x72, 0x4b, 0xde, 0x65, 0x65, 0xb6, 0xaa, 0x44, 0x1a, 0xbc, 0xdf, 0x13, + 0x15, 0x08, 0x80, 0xa5, 0xb4, 0xdd, 0xcd, 0xe0, 0x34, 0x4a, 0x85, 0x31, 0x82, 0x17, 0x57, 0x5a, + 0xea, 0x21, 0x38, 0x26, 0xdf, 0x48, 0x59, 0x1d, 0x58, 0x5b, 0x9f, 0x04, 0x12, 0xee, 0x5b, 0xa2, + 0xa9, 0xb3, 0xfe, 0x83, 0xa7, 0xda, 0x47, 0xc6, 0xfb, 0x89, 0x77, 0x1f, 0x3a, 0xbc, 0x7c, 0xc7, + 0x13, 0xc9, 0xb8, 0xdc, 0xb0, 0x9e, 0xa7, 0x2a, 0xa5, 0x2c, 0x3b, 0x96, 0xe2, 0x2e, 0xc0, 0xcf, + 0xfa, 0x0c, 0xe0, 0x2d, 0xad, 0xaa, 0x7f, 0x85, 0x4b, 0x6f, 0x04, 0xf9, 0x83, 0x02, 0x81, 0x81, + 0x00, 0xca, 0x8f, 0xd2, 0xdd, 0x96, 0xc2, 0x4c, 0x52, 0x58, 0xf7, 0x03, 0x77, 0x94, 0x03, 0x75, + 0x58, 0x09, 0x1d, 0xb8, 0x5b, 0xa1, 0x05, 0x4e, 0x4b, 0x9a, 0x49, 0x5a, 0x1e, 0xe1, 0x1c, 0xcc, + 0x48, 0xfe, 0x07, 0xd2, 0xf5, 0x66, 0x46, 0x1b, 0x2e, 0xfe, 0xbf, 0x07, 0xe8, 0x42, 0x12, 0x80, + 0x23, 0xb5, 0x67, 0x50, 0x45, 0xd1, 0xd7, 0xbf, 0xe5, 0xb4, 0xa7, 0x50, 0x7f, 0x3d, 0x7b, 0x80, + 0x73, 0x51, 0xf0, 0xbd, 0x31, 0x33, 0x63, 0x9a, 0x18, 0x70, 0xd8, 0x84, 0x1b, 0x85, 0x82, 0x70, + 0x5c, 0x6f, 0xe6, 0x5c, 0xff, 0x6d, 0x6b, 0x85, 0x99, 0x9d, 0xfc, 0x03, 0x73, 0x9f, 0x1a, 0xa8, + 0xf9, 0x3f, 0x38, 0x76, 0x1e, 0x65, 0x55, 0xd0, 0x0c, 0x6c, 0xd2, 0x82, 0x84, 0x3e, 0x91, 0xc6, + 0xde, 0xe5, 0xa9, 0x89, 0xca, 0x54, 0x31, 0x32, 0x3c, 0xcb, 0x4c, 0xa8, 0xa9, 0xf0, 0x70, 0xf7, + 0xdb, 0x02, 0x81, 0x80, 0x62, 0x16, 0xd9, 0xa3, 0x90, 0x17, 0x87, 0x8a, 0xc5, 0x47, 0x48, 0x39, + 0xe3, 0xa4, 0xb1, 0x56, 0xdc, 0x0b, 0x07, 0x09, 0x17, 0x71, 0x31, 0xba, 0xcb, 0x76, 0xf9, 0x29, + 0xcd, 0xe6, 0x54, 0xb3, 0xde, 0x57, 0x07, 0xe9, 0xa1, 0x18, 0x91, 0x12, 0xa3, 0xe9, 0x9c, 0x45, + 0xe9, 0xb1, 0xcf, 0xdd, 0xcf, 0x78, 0xb0, 0x53, 0x2b, 0xb7, 0x33, 0xb2, 0x8b, 0x7a, 0xe9, 0xb2, + 0xcb, 0x73, 0x1a, 0x66, 0x83, 0x28, 0x55, 0x9d, 0xa4, 0x76, 0xb6, 0xcd, 0xa2, 0x57, 0x64, 0xf5, + 0x76, 0xb3, 0x02, 0x94, 0xfb, 0xc6, 0xea, 0x28, 0x8d, 0xa5, 0x21, 0xea, 0xf2, 0x06, 0xf6, 0x6e, + 0xf5, 0xa7, 0x32, 0x72, 0xfd, 0xfc, 0x1c, 0x65, 0x87, 0xe1, 0x79, 0x41, 0xa8, 0x34, 0x63, 0x59, + 0x13, 0xa3, 0x46, 0x53, 0x9c, 0x5d, 0x56, 0x7d, 0x67, 0x0f, 0xa9, 0xff, 0x02, 0x68, 0x5d, 0x69, + 0xe0, 0x32, 0xe4, 0x35, 0x02, 0x81, 0x80, 0x55, 0xf8, 0x25, 0x84, 0x94, 0xad, 0x50, 0xab, 0x49, + 0x38, 0x30, 0xaf, 0xde, 0x53, 0xdc, 0x27, 0x08, 0x25, 0x27, 0x5a, 0xd2, 0xe5, 0x21, 0x50, 0xde, + 0x6d, 0x6b, 0xc7, 0x17, 0xae, 0x21, 0xb6, 0xcb, 0xe9, 0x24, 0x7c, 0x76, 0x32, 0xe3, 0xcb, 0xba, + 0xee, 0x61, 0xd2, 0x79, 0xf2, 0x88, 0x12, 0x1c, 0x9f, 0x29, 0xb3, 0x6a, 0x73, 0x16, 0xfc, 0x35, + 0xcf, 0xe7, 0x68, 0xec, 0x47, 0xf7, 0xfb, 0x2d, 0x18, 0xff, 0xa8, 0xbf, 0xe1, 0xe8, 0x64, 0xa0, + 0x98, 0x4a, 0x70, 0x07, 0x5d, 0xb1, 0x8e, 0x6e, 0x15, 0x4e, 0x0d, 0x28, 0x61, 0xb1, 0x37, 0x21, + 0xcb, 0xba, 0x1d, 0x1b, 0xff, 0x06, 0x9f, 0xbe, 0xbc, 0xa9, 0x93, 0x77, 0x07, 0xc1, 0xc0, 0x1a, + 0x4b, 0x13, 0xee, 0xa3, 0xdb, 0x7e, 0x99, 0x94, 0xcc, 0x5b, 0x88, 0xfb, 0xb9, 0x77, 0x3c, 0x81, + 0x80, 0x5d, 0xef, 0x3e, 0xf8, 0x6e, 0xfa}; + +unsigned int rsa2048_der_len = 1191; + +unsigned char rsa2048_pub_der[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xf2, 0x6a, 0x52, 0xa0, 0x96, 0xc5, 0xb7, 0x86, 0x6f, 0xc4, 0x35, 0x39, 0x5e, 0x28, 0xd3, + 0xa6, 0xab, 0x80, 0x6a, 0xbb, 0x07, 0x10, 0x74, 0xea, 0xe0, 0xfc, 0xef, 0x54, 0xb0, 0x5e, 0x66, + 0x34, 0x21, 0x89, 0x35, 0x09, 0xb7, 0x46, 0xf8, 0xb6, 0xf8, 0x86, 0xc0, 0x55, 0xc6, 0xc5, 0x2e, + 0x36, 0xcc, 0xb7, 0x24, 0x5e, 0x56, 0xba, 0x2b, 0x8d, 0xfb, 0x8d, 0x7b, 0xe1, 0x4b, 0x9e, 0xdf, + 0x08, 0x5c, 0xb2, 0x28, 0xc0, 0x79, 0x45, 0xd1, 0xf3, 0x9c, 0x70, 0xd8, 0xaf, 0x50, 0x1c, 0xc3, + 0x10, 0x24, 0x84, 0xc1, 0xb1, 0x36, 0x3f, 0xd4, 0xbf, 0x10, 0x18, 0xf9, 0xd0, 0x72, 0xea, 0xce, + 0xbf, 0x34, 0x8e, 0xb5, 0x97, 0xcb, 0x8a, 0x5e, 0x8e, 0x7d, 0xed, 0x1f, 0xe3, 0xa9, 0xde, 0x5b, + 0x3f, 0xc9, 0x33, 0x4c, 0x9b, 0x3e, 0xd8, 0x29, 0x61, 0x6b, 0x30, 0xba, 0xa1, 0x36, 0xbd, 0x22, + 0x33, 0xa1, 0x25, 0x73, 0xab, 0x5f, 0x6f, 0x35, 0x48, 0xf1, 0xad, 0x96, 0x5f, 0x0d, 0x0c, 0xe5, + 0xe9, 0x8d, 0xf9, 0xed, 0x0c, 0xcf, 0x50, 0x3c, 0x24, 0x34, 0x9b, 0x9e, 0xbc, 0xf9, 0xfa, 0x19, + 0x51, 0x37, 0x03, 0x5c, 0xd0, 0x25, 0x1f, 0xe6, 0xb2, 0xf7, 0xff, 0x96, 0xb0, 0x61, 0x42, 0xbb, + 0xc5, 0x64, 0x5f, 0x3d, 0xaf, 0x6f, 0x14, 0xcc, 0x92, 0x74, 0x6a, 0x06, 0xfa, 0xb2, 0xd9, 0xe1, + 0x4d, 0x42, 0x76, 0xab, 0x88, 0xe3, 0x08, 0xa2, 0xad, 0x00, 0xb6, 0xf7, 0x78, 0x0c, 0xba, 0x08, + 0x71, 0xbc, 0x8d, 0x98, 0x43, 0x12, 0x63, 0x8d, 0x5e, 0x1f, 0x49, 0xb9, 0x5f, 0x0d, 0x55, 0x1a, + 0x90, 0x63, 0x4c, 0xd8, 0xe8, 0x0b, 0x9f, 0x3d, 0x10, 0xb4, 0xf9, 0x1b, 0x44, 0x3f, 0x43, 0xad, + 0x35, 0x42, 0x3d, 0x3d, 0x21, 0x8e, 0xa8, 0x08, 0x28, 0x47, 0x19, 0x14, 0x28, 0x1c, 0x95, 0xb7, + 0x5d, 0x02, 0x03, 0x01, 0x00, 0x01}; +unsigned int rsa2048_pub_der_len = 294; + +unsigned char rsa1024_cert_der[] = { + 0x30, 0x82, 0x02, 0xb4, 0x30, 0x82, 0x02, 0x1d, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, + 0xc4, 0x52, 0xc1, 0x4a, 0x9e, 0x74, 0xe8, 0xa8, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x73, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, + 0x08, 0x4d, 0x61, 0x72, 0x79, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, + 0x04, 0x07, 0x0c, 0x08, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x62, 0x69, 0x61, 0x31, 0x10, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x07, 0x43, 0x6f, 0x6d, 0x63, 0x61, 0x73, 0x74, 0x31, 0x0f, + 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x06, 0x43, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x31, + 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x12, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, + 0x2e, 0x63, 0x6f, 0x6d, 0x63, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x35, 0x30, 0x36, 0x32, 0x33, 0x30, 0x33, 0x30, 0x35, 0x31, 0x36, 0x5a, 0x17, 0x0d, 0x31, + 0x35, 0x30, 0x37, 0x32, 0x33, 0x30, 0x33, 0x30, 0x35, 0x31, 0x36, 0x5a, 0x30, 0x73, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x08, 0x4d, 0x61, 0x72, 0x79, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x11, + 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x62, 0x69, + 0x61, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x07, 0x43, 0x6f, 0x6d, 0x63, + 0x61, 0x73, 0x74, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x06, 0x43, 0x6f, + 0x6e, 0x73, 0x65, 0x63, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x12, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x2e, 0x63, 0x6f, 0x6d, 0x63, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xc4, 0x32, + 0x70, 0x15, 0xb3, 0x53, 0xd5, 0xaf, 0x26, 0xc2, 0xcd, 0x6e, 0x87, 0x9f, 0x13, 0x10, 0x9e, 0x3d, + 0x8d, 0x6c, 0xb4, 0x1e, 0xc8, 0xbb, 0xf1, 0xbf, 0x7a, 0xc0, 0xce, 0xbf, 0x5c, 0x00, 0x1f, 0x83, + 0xd8, 0xe3, 0xf7, 0xe8, 0xa3, 0x79, 0x61, 0xd4, 0x3a, 0xae, 0x49, 0x6d, 0x38, 0x1d, 0x12, 0x74, + 0xba, 0x9c, 0xb4, 0x38, 0x61, 0x6b, 0x44, 0x1d, 0xac, 0xf7, 0xa7, 0x7d, 0x8a, 0x80, 0x9f, 0x56, + 0x67, 0xb2, 0xe5, 0x45, 0xbc, 0x0d, 0xde, 0xde, 0x63, 0x06, 0x13, 0x4d, 0x06, 0x2e, 0xe2, 0xf9, + 0xfa, 0xe4, 0x3b, 0xa6, 0xa0, 0x49, 0xbb, 0x11, 0x23, 0xf8, 0x68, 0x85, 0x3c, 0x1b, 0x92, 0xe1, + 0x6c, 0x42, 0x37, 0xe3, 0x1b, 0x7c, 0x7a, 0x25, 0x91, 0x30, 0xd5, 0xa5, 0xf3, 0xbb, 0x91, 0x23, + 0xdf, 0x23, 0x94, 0xb8, 0xf2, 0x61, 0x4f, 0xba, 0x73, 0xd1, 0x4b, 0x2b, 0x89, 0x8f, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, + 0x04, 0x14, 0x76, 0x63, 0x9b, 0x06, 0xa5, 0x12, 0x26, 0xa5, 0x89, 0xc6, 0x6c, 0x57, 0x5b, 0x4a, + 0x3e, 0x47, 0xb0, 0xa4, 0x0b, 0x1a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0x76, 0x63, 0x9b, 0x06, 0xa5, 0x12, 0x26, 0xa5, 0x89, 0xc6, 0x6c, 0x57, 0x5b, + 0x4a, 0x3e, 0x47, 0xb0, 0xa4, 0x0b, 0x1a, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x33, 0xbe, 0xfe, 0x79, 0xdb, 0x9b, 0xc2, 0x09, + 0xb8, 0x9c, 0x51, 0x6a, 0x2b, 0x5f, 0xe5, 0xc6, 0xc6, 0xd2, 0x72, 0x5d, 0x56, 0x24, 0xa1, 0xa5, + 0xac, 0x28, 0xbc, 0xba, 0x60, 0xb9, 0xd6, 0x30, 0x39, 0x4e, 0x89, 0xc9, 0x41, 0x89, 0x37, 0xf0, + 0xeb, 0xee, 0x09, 0x31, 0xb0, 0x34, 0xed, 0xf6, 0x8f, 0x70, 0xd3, 0x65, 0x81, 0x18, 0x1c, 0xbe, + 0xd0, 0x08, 0x94, 0x1d, 0x80, 0x92, 0x72, 0x8f, 0x16, 0xa4, 0xac, 0xf4, 0x7d, 0x1e, 0xe1, 0x8b, + 0xe7, 0x79, 0x26, 0x4b, 0xc8, 0x17, 0x3e, 0x19, 0xf5, 0x50, 0x07, 0xd0, 0x85, 0xb6, 0xeb, 0x56, + 0xff, 0xdb, 0xe2, 0x28, 0x42, 0x58, 0x52, 0xf3, 0xef, 0x6a, 0xf8, 0x86, 0xae, 0x9e, 0xa2, 0x7c, + 0xa2, 0xb7, 0x87, 0x90, 0x1b, 0x48, 0xdc, 0x7d, 0x2a, 0xf2, 0x0c, 0x55, 0xf3, 0x34, 0x79, 0xb3, + 0x87, 0x67, 0x18, 0x29, 0xc8, 0x3a, 0xc9, 0xe3}; +unsigned int rsa1024_cert_der_len = 696; + +unsigned char rsa2048_cert_der[] = { + 0x30, 0x82, 0x03, 0xb9, 0x30, 0x82, 0x02, 0xa1, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, + 0xee, 0x91, 0x7a, 0x08, 0x9a, 0x55, 0x9f, 0x5f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x73, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, + 0x08, 0x4d, 0x61, 0x72, 0x79, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, + 0x04, 0x07, 0x0c, 0x08, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x62, 0x69, 0x61, 0x31, 0x0f, 0x30, 0x0d, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x43, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x31, 0x10, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x07, 0x43, 0x6f, 0x6d, 0x63, 0x61, 0x73, 0x74, 0x31, + 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x12, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, + 0x2e, 0x63, 0x6f, 0x6d, 0x63, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x35, 0x30, 0x36, 0x32, 0x33, 0x30, 0x33, 0x30, 0x36, 0x30, 0x37, 0x5a, 0x17, 0x0d, 0x31, + 0x35, 0x30, 0x37, 0x32, 0x33, 0x30, 0x33, 0x30, 0x36, 0x30, 0x37, 0x5a, 0x30, 0x73, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x08, 0x4d, 0x61, 0x72, 0x79, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x11, + 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x62, 0x69, + 0x61, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x43, 0x6f, 0x6e, 0x73, + 0x65, 0x63, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x07, 0x43, 0x6f, 0x6d, + 0x63, 0x61, 0x73, 0x74, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x12, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x2e, 0x63, 0x6f, 0x6d, 0x63, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xf2, 0x6a, 0x52, 0xa0, 0x96, 0xc5, 0xb7, 0x86, 0x6f, 0xc4, 0x35, 0x39, 0x5e, 0x28, + 0xd3, 0xa6, 0xab, 0x80, 0x6a, 0xbb, 0x07, 0x10, 0x74, 0xea, 0xe0, 0xfc, 0xef, 0x54, 0xb0, 0x5e, + 0x66, 0x34, 0x21, 0x89, 0x35, 0x09, 0xb7, 0x46, 0xf8, 0xb6, 0xf8, 0x86, 0xc0, 0x55, 0xc6, 0xc5, + 0x2e, 0x36, 0xcc, 0xb7, 0x24, 0x5e, 0x56, 0xba, 0x2b, 0x8d, 0xfb, 0x8d, 0x7b, 0xe1, 0x4b, 0x9e, + 0xdf, 0x08, 0x5c, 0xb2, 0x28, 0xc0, 0x79, 0x45, 0xd1, 0xf3, 0x9c, 0x70, 0xd8, 0xaf, 0x50, 0x1c, + 0xc3, 0x10, 0x24, 0x84, 0xc1, 0xb1, 0x36, 0x3f, 0xd4, 0xbf, 0x10, 0x18, 0xf9, 0xd0, 0x72, 0xea, + 0xce, 0xbf, 0x34, 0x8e, 0xb5, 0x97, 0xcb, 0x8a, 0x5e, 0x8e, 0x7d, 0xed, 0x1f, 0xe3, 0xa9, 0xde, + 0x5b, 0x3f, 0xc9, 0x33, 0x4c, 0x9b, 0x3e, 0xd8, 0x29, 0x61, 0x6b, 0x30, 0xba, 0xa1, 0x36, 0xbd, + 0x22, 0x33, 0xa1, 0x25, 0x73, 0xab, 0x5f, 0x6f, 0x35, 0x48, 0xf1, 0xad, 0x96, 0x5f, 0x0d, 0x0c, + 0xe5, 0xe9, 0x8d, 0xf9, 0xed, 0x0c, 0xcf, 0x50, 0x3c, 0x24, 0x34, 0x9b, 0x9e, 0xbc, 0xf9, 0xfa, + 0x19, 0x51, 0x37, 0x03, 0x5c, 0xd0, 0x25, 0x1f, 0xe6, 0xb2, 0xf7, 0xff, 0x96, 0xb0, 0x61, 0x42, + 0xbb, 0xc5, 0x64, 0x5f, 0x3d, 0xaf, 0x6f, 0x14, 0xcc, 0x92, 0x74, 0x6a, 0x06, 0xfa, 0xb2, 0xd9, + 0xe1, 0x4d, 0x42, 0x76, 0xab, 0x88, 0xe3, 0x08, 0xa2, 0xad, 0x00, 0xb6, 0xf7, 0x78, 0x0c, 0xba, + 0x08, 0x71, 0xbc, 0x8d, 0x98, 0x43, 0x12, 0x63, 0x8d, 0x5e, 0x1f, 0x49, 0xb9, 0x5f, 0x0d, 0x55, + 0x1a, 0x90, 0x63, 0x4c, 0xd8, 0xe8, 0x0b, 0x9f, 0x3d, 0x10, 0xb4, 0xf9, 0x1b, 0x44, 0x3f, 0x43, + 0xad, 0x35, 0x42, 0x3d, 0x3d, 0x21, 0x8e, 0xa8, 0x08, 0x28, 0x47, 0x19, 0x14, 0x28, 0x1c, 0x95, + 0xb7, 0x5d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xf0, 0xe1, 0x4e, 0x26, 0x9b, 0xa4, 0xc8, 0x7c, 0x24, 0x2d, + 0xa2, 0xdc, 0x5e, 0x3e, 0xb8, 0x64, 0xb0, 0xb7, 0x3e, 0x96, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xf0, 0xe1, 0x4e, 0x26, 0x9b, 0xa4, 0xc8, 0x7c, 0x24, + 0x2d, 0xa2, 0xdc, 0x5e, 0x3e, 0xb8, 0x64, 0xb0, 0xb7, 0x3e, 0x96, 0x30, 0x0c, 0x06, 0x03, 0x55, + 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x68, 0x60, 0x3a, + 0x9a, 0xcf, 0x75, 0x6c, 0x09, 0xab, 0x9a, 0x4a, 0x0d, 0x9b, 0xe4, 0x7f, 0xdc, 0x7a, 0xa9, 0xfa, + 0x06, 0xb4, 0xc4, 0xca, 0xcf, 0xa0, 0x38, 0x7f, 0xdf, 0xc2, 0x4d, 0x4a, 0x94, 0x82, 0x33, 0x83, + 0x9a, 0x7b, 0x9b, 0x84, 0xf7, 0xfd, 0xb3, 0x93, 0x22, 0xd0, 0xb2, 0x73, 0xa8, 0x54, 0x45, 0xb0, + 0x05, 0xac, 0x1a, 0xff, 0x60, 0x03, 0x54, 0xa7, 0x0d, 0xcb, 0x8d, 0xeb, 0x62, 0x59, 0xcb, 0x8b, + 0xfa, 0x7f, 0x8d, 0xd8, 0xa4, 0x64, 0x3a, 0x3e, 0xdc, 0x5d, 0xb9, 0xc0, 0x27, 0x50, 0xd0, 0x54, + 0x62, 0x1e, 0xe7, 0x7f, 0x66, 0xa8, 0xdf, 0xbc, 0x6a, 0x17, 0xf1, 0xcd, 0x45, 0x76, 0x34, 0xc3, + 0x6c, 0x6f, 0xac, 0x85, 0x50, 0xdc, 0xd4, 0xf0, 0xdd, 0x85, 0xfa, 0x75, 0x23, 0x45, 0x12, 0x90, + 0x0d, 0xf2, 0xc9, 0x43, 0x83, 0x4c, 0x97, 0xfd, 0x76, 0x64, 0xa8, 0x80, 0x15, 0x45, 0x4a, 0xa4, + 0x36, 0xc7, 0xa5, 0x12, 0x09, 0x4d, 0xc1, 0xdb, 0xc0, 0xbe, 0x58, 0x9b, 0x16, 0xe6, 0x6f, 0x63, + 0xed, 0x5c, 0x62, 0xd6, 0x23, 0xb0, 0x26, 0x46, 0xb1, 0x98, 0x5c, 0x94, 0x72, 0x86, 0x2f, 0x26, + 0xa1, 0x91, 0x68, 0x4a, 0x5c, 0x01, 0x8b, 0x44, 0x00, 0xea, 0xa4, 0x61, 0x85, 0x4b, 0xd4, 0xc3, + 0x50, 0x16, 0x97, 0xa4, 0x6f, 0x56, 0x2f, 0x2a, 0xf0, 0x6e, 0x3f, 0x35, 0x6e, 0xe5, 0x27, 0x3c, + 0x76, 0xa9, 0x91, 0x6b, 0x1f, 0x1e, 0x73, 0x82, 0x61, 0xcc, 0x06, 0x2d, 0xae, 0x12, 0x49, 0x66, + 0xa6, 0x86, 0x39, 0x65, 0x00, 0x77, 0x22, 0x92, 0x64, 0x9c, 0x12, 0x30, 0x24, 0x75, 0x8b, 0xd7, + 0x02, 0x7a, 0x5b, 0xbe, 0x15, 0xda, 0x0e, 0xac, 0x9c, 0xfe, 0xb1, 0xc8, 0x6c, 0x25, 0xf8, 0xea, + 0x0a, 0x69, 0x64, 0xb7, 0x5e, 0x60, 0x65, 0xba, 0x02, 0xe4, 0xf5, 0x66, 0x07}; + +unsigned int rsa2048_cert_der_len = 957; + +unsigned char ecc_der[] = { + 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x4d, 0xef, 0x8c, 0x19, 0x60, 0xf5, 0xd6, 0x78, 0x18, + 0x84, 0x4d, 0xe6, 0xe3, 0x2f, 0x19, 0x4f, 0xcd, 0x10, 0xc5, 0xfd, 0x55, 0x54, 0xc1, 0x7e, 0x95, + 0x4e, 0xbc, 0xe1, 0xba, 0xba, 0x56, 0xc6, 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0x7f, 0xd8, 0x8a, 0x21, 0x09, 0x41, 0xb2, + 0x02, 0x5b, 0x1d, 0xb0, 0x2e, 0xa5, 0x04, 0x6e, 0xae, 0xae, 0x21, 0xd8, 0x02, 0x8e, 0xaa, 0x3b, + 0x68, 0x03, 0x6e, 0x25, 0xd6, 0x2c, 0x08, 0xff, 0xa6, 0x13, 0x5a, 0x8c, 0x14, 0xe0, 0x93, 0x66, + 0xa2, 0x25, 0x46, 0x75, 0x8f, 0xd6, 0x87, 0x79, 0xb4, 0xc2, 0x62, 0x7e, 0xa8, 0xed, 0x45, 0xb1, + 0xe5, 0x91, 0xb6, 0x2b, 0xd4, 0x2a, 0xbb, 0x2d, 0x94}; + +unsigned int ecc_der_len = 121; + +unsigned char ecc_pub_der[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x7f, 0xd8, 0x8a, 0x21, 0x09, + 0x41, 0xb2, 0x02, 0x5b, 0x1d, 0xb0, 0x2e, 0xa5, 0x04, 0x6e, 0xae, 0xae, 0x21, 0xd8, 0x02, 0x8e, + 0xaa, 0x3b, 0x68, 0x03, 0x6e, 0x25, 0xd6, 0x2c, 0x08, 0xff, 0xa6, 0x13, 0x5a, 0x8c, 0x14, 0xe0, + 0x93, 0x66, 0xa2, 0x25, 0x46, 0x75, 0x8f, 0xd6, 0x87, 0x79, 0xb4, 0xc2, 0x62, 0x7e, 0xa8, 0xed, + 0x45, 0xb1, 0xe5, 0x91, 0xb6, 0x2b, 0xd4, 0x2a, 0xbb, 0x2d, 0x94}; + +unsigned int ecc_pub_der_len = 91; + +unsigned char ecc_cert_der[] = { + 0x30, 0x82, 0x01, 0x7a, 0x30, 0x82, 0x01, 0x20, 0x02, 0x09, 0x00, 0xa6, 0x4e, 0x60, 0x8c, 0xa5, + 0xe7, 0x04, 0x98, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x31, 0x30, 0x30, 0x39, + 0x31, 0x38, 0x31, 0x35, 0x33, 0x31, 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x31, 0x31, 0x30, 0x38, 0x31, + 0x38, 0x31, 0x35, 0x33, 0x31, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, + 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, + 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x59, 0x30, + 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, + 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x7f, 0xd8, 0x8a, 0x21, 0x09, 0x41, 0xb2, 0x02, + 0x5b, 0x1d, 0xb0, 0x2e, 0xa5, 0x04, 0x6e, 0xae, 0xae, 0x21, 0xd8, 0x02, 0x8e, 0xaa, 0x3b, 0x68, + 0x03, 0x6e, 0x25, 0xd6, 0x2c, 0x08, 0xff, 0xa6, 0x13, 0x5a, 0x8c, 0x14, 0xe0, 0x93, 0x66, 0xa2, + 0x25, 0x46, 0x75, 0x8f, 0xd6, 0x87, 0x79, 0xb4, 0xc2, 0x62, 0x7e, 0xa8, 0xed, 0x45, 0xb1, 0xe5, + 0x91, 0xb6, 0x2b, 0xd4, 0x2a, 0xbb, 0x2d, 0x94, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, + 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x21, 0x00, 0xad, 0xfc, 0x19, 0xb8, + 0xc3, 0x87, 0x8d, 0x52, 0x3a, 0xf5, 0x83, 0xc6, 0x58, 0xc5, 0x86, 0x3b, 0xd5, 0xce, 0x74, 0x85, + 0x66, 0xd5, 0xbc, 0xfe, 0x5f, 0xd1, 0x75, 0xe3, 0x42, 0xe1, 0x5a, 0x00, 0x02, 0x20, 0x7c, 0x9c, + 0xb8, 0x45, 0x9e, 0xe7, 0x3a, 0x52, 0x9b, 0x2c, 0x92, 0xf3, 0x1b, 0x93, 0x65, 0x47, 0xb3, 0xf1, + 0x1b, 0xf4, 0xf6, 0x50, 0xd2, 0xf4, 0xcb, 0x25, 0x38, 0x21, 0xcb, 0x43, 0xae, 0x7a}; + +unsigned int ecc_cert_der_len = 382; + +unsigned char test_root[] = { + 0xe7, 0x9b, 0x03, 0x18, 0x85, 0x1b, 0x9d, 0xbd, 0xd7, 0x17, 0x18, 0xf9, 0xec, 0x72, 0xf0, 0x3d}; + +unsigned int test_root_len = 16; + +struct TestKeyData { + Sec_KeyType type; + Sec_KeyContainer kc; + SEC_BYTE* buffer; + SEC_SIZE buffer_len; +} __attribute__((aligned(32))); + +static TestKeyData g_keyData[TESTKEY_NUM] = { + {SEC_KEYTYPE_AES_128, SEC_KEYCONTAINER_RAW_AES_128, sym128_a_bin, sym128_a_bin_len}, //TESTKEY_AES128_A + {SEC_KEYTYPE_AES_256, SEC_KEYCONTAINER_RAW_AES_256, sym256_a_bin, sym256_a_bin_len}, //TESTKEY_AES256_A + {SEC_KEYTYPE_HMAC_128, SEC_KEYCONTAINER_RAW_HMAC_128, sym128_a_bin, sym128_a_bin_len}, //TESTKEY_HMAC128_A + {SEC_KEYTYPE_HMAC_160, SEC_KEYCONTAINER_RAW_HMAC_160, sym160_a_bin, sym160_a_bin_len}, //TESTKEY_HMAC160_A + {SEC_KEYTYPE_HMAC_256, SEC_KEYCONTAINER_RAW_HMAC_256, sym256_a_bin, sym256_a_bin_len}, //TESTKEY_HMAC256_A + {SEC_KEYTYPE_RSA_1024, SEC_KEYCONTAINER_DER_RSA_1024, rsa1024_der, rsa1024_der_len}, //TESTKEY_RSA1024_SGN_PRIV + {SEC_KEYTYPE_RSA_1024_PUBLIC, SEC_KEYCONTAINER_DER_RSA_1024_PUBLIC, rsa1024_pub_der, rsa1024_pub_der_len}, //TESTKEY_RSA1024_SGN_PUB + {SEC_KEYTYPE_RSA_1024, SEC_KEYCONTAINER_DER_RSA_1024, rsa1024_der, rsa1024_der_len}, //TESTKEY_RSA1024_ENC_PRIV + {SEC_KEYTYPE_RSA_1024_PUBLIC, SEC_KEYCONTAINER_DER_RSA_1024_PUBLIC, rsa1024_pub_der, rsa1024_pub_der_len}, //TESTKEY_RSA1024_ENC_PUB + {SEC_KEYTYPE_RSA_1024, SEC_KEYCONTAINER_DER_RSA_1024, rsa1024_der, rsa1024_der_len}, //TESTKEY_RSA1024_KEK_PRIV + {SEC_KEYTYPE_RSA_1024_PUBLIC, SEC_KEYCONTAINER_DER_RSA_1024_PUBLIC, rsa1024_pub_der, rsa1024_pub_der_len}, //TESTKEY_RSA1024_KEK_PUB + {SEC_KEYTYPE_RSA_2048, SEC_KEYCONTAINER_DER_RSA_2048, rsa2048_der, rsa2048_der_len}, //TESTKEY_RSA2048_SGN_PRIV + {SEC_KEYTYPE_RSA_2048_PUBLIC, SEC_KEYCONTAINER_DER_RSA_2048_PUBLIC, rsa2048_pub_der, rsa2048_pub_der_len}, //TESTKEY_RSA2048_SGN_PUB + {SEC_KEYTYPE_RSA_2048, SEC_KEYCONTAINER_DER_RSA_2048, rsa2048_der, rsa2048_der_len}, //TESTKEY_RSA2048_ENC_PRIV + {SEC_KEYTYPE_RSA_2048_PUBLIC, SEC_KEYCONTAINER_DER_RSA_2048_PUBLIC, rsa2048_pub_der, rsa2048_pub_der_len}, //TESTKEY_RSA2048_ENC_PUB + {SEC_KEYTYPE_RSA_2048, SEC_KEYCONTAINER_DER_RSA_2048, rsa2048_der, rsa2048_der_len}, //TESTKEY_RSA2048_KEK_PRIV + {SEC_KEYTYPE_RSA_2048_PUBLIC, SEC_KEYCONTAINER_DER_RSA_2048_PUBLIC, rsa2048_pub_der, rsa2048_pub_der_len}, //TESTKEY_RSA2048_KEK_PUB + {SEC_KEYTYPE_ECC_NISTP256, SEC_KEYCONTAINER_DER_ECC_NISTP256, ecc_der, ecc_der_len}, //TESTKEY_ECC + {SEC_KEYTYPE_ECC_NISTP256_PUBLIC, SEC_KEYCONTAINER_DER_ECC_NISTP256_PUBLIC, ecc_pub_der, ecc_pub_der_len}, //TESTKEY_ECC_PUB + {SEC_KEYTYPE_AES_128, SEC_KEYCONTAINER_RAW_AES_128, test_root, test_root_len}, //TESTKEY_ECC_PUB +}; + +struct TestCertData { // NOLINT + Sec_CertificateContainer cc; + SEC_BYTE* buffer; + SEC_SIZE buffer_len; +} __attribute__((aligned(16))); + +static TestCertData g_certData[TESTKEY_NUM] = { + {SEC_CERTIFICATECONTAINER_X509_DER, rsa1024_cert_der, rsa1024_cert_der_len}, //TESTCERT_RSA1024 + {SEC_CERTIFICATECONTAINER_X509_DER, rsa2048_cert_der, rsa2048_cert_der_len}, //TESTCERT_RSA2048 + {SEC_CERTIFICATECONTAINER_X509_DER, ecc_cert_der, ecc_cert_der_len}, //TESTCERT_EC +}; + +static Sec_Result BigNumToBuffer(const BIGNUM* bignum, SEC_BYTE* buffer, SEC_SIZE buffer_len) { + SEC_SIZE num_bytes; + + memset(buffer, 0, buffer_len); + num_bytes = BN_num_bytes(bignum); + + if (num_bytes > buffer_len) { + SEC_LOG_ERROR("Buffer not large enough. needed: %d, actual: %d", num_bytes, buffer_len); + return SEC_RESULT_FAILURE; + } + + BN_bn2bin(bignum, buffer + buffer_len - num_bytes); + + return SEC_RESULT_SUCCESS; +} + +ProvKey* TestCreds::getKey(TestKey key, TestKc kc, SEC_OBJECTID id) { + if (key >= TESTKEY_NUM) { + SEC_LOG_ERROR("Invalid key: %d", key); + return nullptr; + } + + switch (kc) { + case TESTKC_RAW: { + auto keyVector = std::vector(g_keyData[key].buffer, + g_keyData[key].buffer + g_keyData[key].buffer_len); + return new ProvKey(keyVector, g_keyData[key].kc); + } + case TESTKC_SOC: + return TestCreds::getSocKey(key, id); + break; + + case TESTKC_GENERATED: + case TESTKC_STORE: + default: + break; + } + + SEC_LOG_ERROR("Unimplemented"); + return nullptr; +} + +Sec_KeyType TestCreds::getKeyType(TestKey key) { + if (key >= TESTKEY_NUM) { + SEC_LOG_ERROR("Invalid key: %d", key); + return SEC_KEYTYPE_NUM; + } + + return g_keyData[key].type; +} + +ProvCert* TestCreds::getCert(TestCert cert) { + if (cert >= TESTCERT_NUM) { + SEC_LOG_ERROR("Invalid cert: %d", cert); + return nullptr; + } + + auto certVector = std::vector(g_certData[cert].buffer, + g_certData[cert].buffer + g_certData[cert].buffer_len); + return new ProvCert(certVector, g_certData[cert].cc); +} + +std::vector TestCreds::asOpenSslAes(TestKey key) { + return {g_keyData[key].buffer, g_keyData[key].buffer + g_keyData[key].buffer_len}; +} + +static RSA* RSAFromDERPub(const SEC_BYTE* der, SEC_SIZE der_len) { + const auto* p = der; + RSA* rsa = nullptr; + + rsa = d2i_RSAPublicKey(&rsa, &p, der_len); + + if (rsa == nullptr) { + p = der; + rsa = d2i_RSA_PUBKEY(&rsa, &p, der_len); + } + + do { + if (rsa == nullptr) { + SEC_LOG_ERROR("Invalid RSA key container"); + break; + } + } while (false); + + return rsa; +} + +static RSA* RSAFromDERPriv(const SEC_BYTE* der, SEC_SIZE der_len) { + const auto* p = der; + PKCS8_PRIV_KEY_INFO* p8 = nullptr; + EVP_PKEY* evp_key = nullptr; + RSA* rsa = nullptr; + + do { + p8 = d2i_PKCS8_PRIV_KEY_INFO(nullptr, &p, der_len); + if (p8 != nullptr) { + evp_key = EVP_PKCS82PKEY(p8); + if (evp_key == nullptr) { + SEC_LOG_ERROR("EVP_PKCS82PKEY failed"); + break; + } + } else { + evp_key = d2i_AutoPrivateKey(nullptr, &p, der_len); + if (evp_key == nullptr) { + SEC_LOG_ERROR("d2i_AutoPrivateKey failed"); + break; + } + } + + rsa = EVP_PKEY_get1_RSA(evp_key); + if (rsa == nullptr) { + SEC_LOG_ERROR("EVP_PKEY_get1_RSA failed"); + break; + } + } while (false); + + SEC_EVPPKEY_FREE(evp_key); + + if (p8 != nullptr) { + PKCS8_PRIV_KEY_INFO_free(p8); + } + + return rsa; +} + +RSA* TestCreds::asOpenSslRsa(TestKey key) { + RSA* rsa = nullptr; + if (g_keyData[key].kc == SEC_KEYCONTAINER_DER_RSA_1024_PUBLIC || + g_keyData[key].kc == SEC_KEYCONTAINER_DER_RSA_2048_PUBLIC) { + rsa = RSAFromDERPub(g_keyData[key].buffer, g_keyData[key].buffer_len); + if (rsa == nullptr) { + SEC_LOG_ERROR("SecUtils_RSAFromDERPub failed"); + } + } else { + rsa = RSAFromDERPriv(g_keyData[key].buffer, g_keyData[key].buffer_len); + if (rsa == nullptr) { + SEC_LOG_ERROR("SecUtils_RSAFromDERPriv failed"); + } + } + + return rsa; +} + +static EC_KEY* ECCFromDERPub(const SEC_BYTE* der, SEC_SIZE der_len) { + const auto* p = der; + EC_KEY* ec_key = nullptr; + + ec_key = d2i_EC_PUBKEY(&ec_key, &p, der_len); + + do { + if (ec_key == nullptr) { + SEC_LOG_ERROR("Invalid ECC key container"); + break; + } + } while (false); + + return ec_key; +} + +static EC_KEY* ECCFromDERPriv(const SEC_BYTE* der, SEC_SIZE der_len) { + const auto* p = der; + PKCS8_PRIV_KEY_INFO* p8 = nullptr; + EVP_PKEY* evp_key = nullptr; + EC_KEY* ecc = nullptr; + + do { + p8 = d2i_PKCS8_PRIV_KEY_INFO(nullptr, &p, der_len); + if (p8 != nullptr) { + evp_key = EVP_PKCS82PKEY(p8); + if (evp_key == nullptr) { + SEC_LOG_ERROR("EVP_PKCS82PKEY failed"); + break; + } + } else { + evp_key = d2i_AutoPrivateKey(nullptr, &p, der_len); + if (evp_key == nullptr) { + SEC_LOG_ERROR("d2i_AutoPrivateKey failed"); + break; + } + } + + ecc = EVP_PKEY_get1_EC_KEY(evp_key); + if (ecc == nullptr) { + SEC_LOG_ERROR("EVP_PKEY_get1_EC_KEY failed"); + break; + } + } while (false); + + SEC_EVPPKEY_FREE(evp_key); + + if (p8 != nullptr) { + PKCS8_PRIV_KEY_INFO_free(p8); + } + + return ecc; +} + +EC_KEY* TestCreds::asOpenSslEcKey(TestKey key) { + EC_KEY* ec = nullptr; + if (g_keyData[key].kc == SEC_KEYCONTAINER_DER_ECC_NISTP256_PUBLIC) { + ec = ECCFromDERPub(g_keyData[key].buffer, g_keyData[key].buffer_len); + if (ec == nullptr) { + SEC_LOG_ERROR("SecUtils_ECCFromDERPub failed"); + } + } else { + ec = ECCFromDERPriv(g_keyData[key].buffer, g_keyData[key].buffer_len); + if (ec == nullptr) { + SEC_LOG_ERROR("SecUtils_ECCFromDERPriv failed"); + } + } + + return ec; +} + +EVP_PKEY* TestCreds::asOpenSslEvpPkey(TestKey key) { + EVP_PKEY* evp_key = nullptr; + + if (SecKey_IsEcc(g_keyData[key].type) == SEC_TRUE) { + EC_KEY* ec = TestCreds::asOpenSslEcKey(key); + if (ec == nullptr) { + SEC_LOG_ERROR("TestCreds::asOpenSslEcKey failed"); + return nullptr; + } + + evp_key = EVP_PKEY_new(); + if (evp_key == nullptr) { + SEC_LOG_ERROR("EVP_PKEY_new failed"); + return nullptr; + } + + if (EVP_PKEY_set1_EC_KEY(evp_key, ec) == 0) { + SEC_EVPPKEY_FREE(evp_key); + SEC_ECC_FREE(ec); + SEC_LOG_ERROR("EVP_PKEY_set1_EC failed"); + return nullptr; + } + } else if (SecKey_IsRsa(g_keyData[key].type) == SEC_TRUE) { + RSA* rsa = TestCreds::asOpenSslRsa(key); + if (rsa == nullptr) { + SEC_LOG_ERROR("TestCreds::asOpenSslRsa failed"); + return nullptr; + } + + evp_key = EVP_PKEY_new(); + if (evp_key == nullptr) { + SEC_LOG_ERROR("EVP_PKEY_new failed"); + SEC_RSA_FREE(rsa); + return nullptr; + } + + if (EVP_PKEY_set1_RSA(evp_key, rsa) == 0) { + SEC_EVPPKEY_FREE(evp_key); + SEC_RSA_FREE(rsa); + SEC_LOG_ERROR("EVP_PKEY_set1_RSA failed"); + return nullptr; + } + + SEC_RSA_FREE(rsa); + } else { + SEC_LOG_ERROR("Not an asymetric key type"); + return nullptr; + } + + return evp_key; +} + +static bool Is_Valid_Point(EC_KEY* ec_key, const std::vector& data) { + if (data.size() != SEC_ECC_NISTP256_KEY_LEN) { + SEC_LOG_ERROR("Input size needed != One BIGNUM"); + return false; + } + + // Convert the input buffer to be encrypted to a BIGNUM + std::shared_ptr inputAsBN(BN_new(), BN_free); + if (inputAsBN == nullptr) { + SEC_LOG_ERROR("BN_new failed"); + return false; + } + + if (BN_bin2bn(&data[0], static_cast(data.size()), inputAsBN.get()) == nullptr) { + SEC_LOG_ERROR("BN_bin2bn failed. Error: %s", ERR_error_string(ERR_get_error(), nullptr)); + return false; + } + + const EC_GROUP* group = EC_KEY_get0_group(ec_key); + if (group == nullptr) { + SEC_LOG_ERROR("EC_KEY_get0_group failed"); + return false; + } + + std::shared_ptr ctx(BN_CTX_new(), BN_CTX_free); + if (ctx == nullptr) { + SEC_LOG_ERROR("BN_CTX_new failed"); + return false; + } + + std::shared_ptr pt(EC_POINT_new(group), EC_POINT_free); + if (pt == nullptr) { + SEC_LOG_ERROR("EC_POINT_new failed"); + return false; + } + + if (EC_POINT_set_compressed_coordinates_GFp(group, pt.get(), inputAsBN.get(), 0, ctx.get()) != 1) { + SEC_LOG_ERROR("EC_POINT_set_compressed_coordinates_GFp failed"); + return false; + } + + return true; +} + +static int ElGamal_Encrypt_Rand(EC_KEY* ec_key, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BYTE* output, + SEC_SIZE outputSize, BIGNUM* sender_rand) { + int res = -1; + BIGNUM* inputAsBN = nullptr; + const EC_GROUP* group = nullptr; + const EC_POINT* P = nullptr; + const EC_POINT* PK_recipient = nullptr; + EC_POINT* shared_secret = nullptr; + EC_POINT* key_2_wrap_point = nullptr; + EC_POINT* sender_share = nullptr; + EC_POINT* wrapped_key = nullptr; + BIGNUM* x = nullptr; + BIGNUM* y = nullptr; + BN_CTX* ctx = nullptr; + + do { + if (inputSize != SEC_ECC_NISTP256_KEY_LEN) { + SEC_LOG_ERROR("Input size needed != One BIGNUM"); + break; + } + + if (outputSize < 4 * SEC_ECC_NISTP256_KEY_LEN) { + SEC_LOG_ERROR("Output size needed < Four BIGNUMs"); + break; + } + + // Convert the input buffer to be encrypted to a BIGNUM + inputAsBN = BN_new(); + if (inputAsBN == nullptr) { + SEC_LOG_ERROR("BN_new failed"); + break; + } + if (BN_bin2bn(input, static_cast(inputSize), inputAsBN) == nullptr) { + SEC_LOG_ERROR("BN_bin2bn failed. Error: %s", + ERR_error_string(ERR_get_error(), nullptr)); + break; + } + + group = EC_KEY_get0_group(ec_key); + if (group == nullptr) { + SEC_LOG_ERROR("EC_KEY_get0_group failed"); + break; + } + + ctx = BN_CTX_new(); + if (ctx == nullptr) { + SEC_LOG_ERROR("BN_CTX_new failed"); + break; + } + + // Convert the X coordinate to an EC Point. This takes the desired Y value in 1 bit (to choose + // which of the two possible Y values to use). This *calculates* an actual Y value for the point. + key_2_wrap_point = EC_POINT_new(group); + if (key_2_wrap_point == nullptr) { + SEC_LOG_ERROR("EC_POINT_new failed"); + break; + } + + if (EC_POINT_set_compressed_coordinates_GFp(group, key_2_wrap_point, inputAsBN, 0, ctx) != 1) { + // Don't print an error message if the error is "point not on curve" 100A906E, but still fail + if (ERR_get_error() != 0x100A906E) // i.e. error:100A906E:lib(16):func(169):reason(110) + { + SEC_LOG_ERROR("Set EC_POINT_set_compressed_coordinates_GFp failed. Error: %s", + ERR_error_string(ERR_get_error(), nullptr)); + } + break; + } + + // Calc sender's shared point 'wP' => this gets sent back to receiver + sender_share = EC_POINT_new(group); + if (sender_share == nullptr) { + SEC_LOG_ERROR("EC_POINT_new failed"); + break; + } + + P = EC_GROUP_get0_generator(group); + if (P == nullptr) { + SEC_LOG_ERROR("EC_GROUP_get0_generator failed"); + break; + } + EC_POINT_mul(group, sender_share, nullptr, P, sender_rand, ctx); + + // Calc sender's Shared Secret 'wRr' => this hides the key I want to send + shared_secret = EC_POINT_new(group); + if (shared_secret == nullptr) { + SEC_LOG_ERROR("EC_POINT_new failed"); + break; + } + + PK_recipient = EC_KEY_get0_public_key(ec_key); + if (PK_recipient == nullptr) { + SEC_LOG_ERROR("EC_KEY_get0_public_key failed"); + break; + } + EC_POINT_mul(group, shared_secret, nullptr, PK_recipient, sender_rand, ctx); + + // key_2_wrap_point is a point on the curve, we add the shared_secret + // to it and send the result, the wrapped_key, to the receiver. + wrapped_key = EC_POINT_new(group); + if (wrapped_key == nullptr) { + SEC_LOG_ERROR("EC_POINT_new failed"); + break; + } + EC_POINT_add(group, wrapped_key, key_2_wrap_point, shared_secret, ctx); + + // Dissect the wrapped point to get its coordinates + x = BN_new(); + if (x == nullptr) { + SEC_LOG_ERROR("BN_new failed"); + break; + } + y = BN_new(); + if (y == nullptr) { + SEC_LOG_ERROR("BN_new failed"); + break; + } + + // Dissect shared_secret to get its coordinates and output them + EC_POINT_get_affine_coordinates_GFp(group, sender_share, x, y, ctx); + + if (BigNumToBuffer(x, &output[0 * SEC_ECC_NISTP256_KEY_LEN], SEC_ECC_NISTP256_KEY_LEN) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_BigNumToBuffer failed"); + break; + } + + if (BigNumToBuffer(y, &output[1 * SEC_ECC_NISTP256_KEY_LEN], SEC_ECC_NISTP256_KEY_LEN) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_BigNumToBuffer failed"); + break; + } + + // Dissect wrapped_key to get its coordinates and output them + EC_POINT_get_affine_coordinates_GFp(group, wrapped_key, x, y, ctx); + + if (BigNumToBuffer(x, &output[2 * SEC_ECC_NISTP256_KEY_LEN], SEC_ECC_NISTP256_KEY_LEN) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_BigNumToBuffer failed"); + break; + } + + if (BigNumToBuffer(y, &output[3 * SEC_ECC_NISTP256_KEY_LEN], SEC_ECC_NISTP256_KEY_LEN) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_BigNumToBuffer failed"); + break; + } + + res = 4 * SEC_ECC_NISTP256_KEY_LEN; + } while (false); + + if (nullptr != x) + BN_free(x); + if (nullptr != y) + BN_free(y); + if (nullptr != inputAsBN) + BN_free(inputAsBN); + if (nullptr != sender_rand) + BN_free(sender_rand); + if (nullptr != shared_secret) + EC_POINT_free(shared_secret); + if (nullptr != sender_share) + EC_POINT_free(sender_share); + if (nullptr != key_2_wrap_point) + EC_POINT_free(key_2_wrap_point); + if (nullptr != wrapped_key) + EC_POINT_free(wrapped_key); + BN_CTX_free(ctx); + + return res; +} + +static int ElGamal_Encrypt(EC_KEY* ec_key, SEC_BYTE* input, SEC_SIZE inputSize, SEC_BYTE* output, + SEC_SIZE outputSize) { + // Generate random number 'w' (multiplier) for the sender + BIGNUM* sender_rand = BN_new(); + + if (sender_rand == nullptr) { + SEC_LOG_ERROR("BN_new failed"); + return -1; + } + if (BN_rand(sender_rand, 256, -1, 0) == 0) { + SEC_LOG_ERROR("BN_rand failed"); + if (nullptr != sender_rand) + BN_free(sender_rand); + return -1; + } + + return ElGamal_Encrypt_Rand(ec_key, input, inputSize, output, outputSize, sender_rand); +} + +static std::vector opensslRsaCrypt(RSA* rsa, Sec_CipherAlgorithm algorithm, Sec_CipherMode mode, + const std::vector& input) { + int padding; + if (algorithm == SEC_CIPHERALGORITHM_RSA_PKCS1_PADDING) { + padding = RSA_PKCS1_PADDING; + } else { + padding = RSA_PKCS1_OAEP_PADDING; + } + + int openssl_res; + std::vector output; + output.resize(RSA_size(rsa)); + + if (mode == SEC_CIPHERMODE_ENCRYPT || mode == SEC_CIPHERMODE_ENCRYPT_NATIVEMEM) { + openssl_res = RSA_public_encrypt(static_cast(input.size()), &input[0], &output[0], rsa, padding); + } else { + openssl_res = RSA_private_decrypt(static_cast(input.size()), &input[0], &output[0], rsa, padding); + } + + if (openssl_res < 0) { + SEC_LOG_ERROR("%s", ERR_error_string(ERR_get_error(), nullptr)); + return {}; + } + + output.resize(openssl_res); + + return output; +} + +static Sec_Result RSAToDERPrivKeyInfo(RSA* rsa, SEC_BYTE* output, SEC_SIZE out_len, SEC_SIZE* written) { + BIO* bio = nullptr; + EVP_PKEY* evp_key = nullptr; + BUF_MEM* bptr = nullptr; + Sec_Result result = SEC_RESULT_FAILURE; + + do { + evp_key = EVP_PKEY_new(); + if (EVP_PKEY_set1_RSA(evp_key, rsa) == 0) { + SEC_LOG_ERROR("EVP_PKEY_set1_RSA failed"); + break; + } + + bio = BIO_new(BIO_s_mem()); + if (bio == nullptr) { + SEC_LOG_ERROR("BIO_new(BIO_s_mem()) failed"); + break; + } + + if (i2d_PKCS8PrivateKeyInfo_bio(bio, evp_key) < 0) { + SEC_LOG_ERROR("I2d_PKCS8_PRIV_KEY_INFO_bio failed"); + break; + } + + BIO_flush(bio); + BIO_get_mem_ptr(bio, &bptr); // NOLINT + + *written = bptr->length; + + if (output != nullptr) { + if (out_len < bptr->length) { + SEC_LOG_ERROR("Output buffer is not large enough"); + break; + } + memcpy(output, bptr->data, bptr->length); + } + + result = SEC_RESULT_SUCCESS; + } while (false); + + SEC_EVPPKEY_FREE(evp_key); + SEC_BIO_FREE(bio); + + return result; +} + +static std::vector toPkcs8(RSA* rsa) { + std::vector pkcs8; + pkcs8.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE pkcs8_len; + + if (RSAToDERPrivKeyInfo(rsa, &pkcs8[0], pkcs8.size(), &pkcs8_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_RSAToDERPriv failed"); + return {}; + } + + pkcs8.resize(pkcs8_len); + + return pkcs8; +} + +template +std::vector concat(std::vector& a, std::vector& b) { + std::vector ret = std::vector(); + std::copy(a.begin(), a.end(), std::back_inserter(ret)); + std::copy(b.begin(), b.end(), std::back_inserter(ret)); + return ret; +} + +ProvKey* TestCreds::wrapAesWithEc(const SEC_BYTE* clear, Sec_KeyType type, EC_KEY* ec_key, SEC_OBJECTID wrappingId, + Sec_CipherAlgorithm asymAlg) { + std::vector payload; + payload.resize(32); + + if (type == SEC_KEYTYPE_AES_128) { + memcpy(&payload[0], clear, SEC_AES_BLOCK_SIZE); + memcpy(&payload[16], clear, SEC_AES_BLOCK_SIZE); + } else { + memcpy(&payload[0], clear, 32); + } + + std::vector encrypted; + encrypted.resize(SEC_ECC_NISTP256_KEY_LEN * 4); + + int encrypted_len = ElGamal_Encrypt(ec_key, &payload[0], payload.size(), &encrypted[0], + encrypted.size()); + + if (encrypted_len <= 0) { + SEC_LOG_ERROR("_ElGamal_Encrypt failed"); + return nullptr; + } + + encrypted.resize(encrypted_len); + + std::vector res; + res.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE res_len; + if (SecKey_GenerateWrappedKeyAsn1Off(&encrypted[0], encrypted.size(), type, wrappingId, nullptr, asymAlg, &res[0], + res.size(), &res_len, 32 - SecKey_GetKeyLenForKeyType(type)) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GenerateWrappedKeyAsn1 failed"); + return nullptr; + } + res.resize(res_len); + + return new ProvKey(res, SEC_KEYCONTAINER_ASN1); +} + +ProvKey* TestCreds::wrapAesWithRsa(const SEC_BYTE* data, Sec_KeyType type, RSA* rsa_key, SEC_OBJECTID wrappingId, + Sec_CipherAlgorithm asymAlg) { + std::vector clear(data, data + SecKey_GetKeyLenForKeyType(type)); + std::vector wrapped = opensslRsaCrypt(rsa_key, asymAlg, SEC_CIPHERMODE_ENCRYPT, clear); + + if (wrapped.empty()) { + SEC_LOG_ERROR("OpensslRsaCrypt failed"); + return nullptr; + } + + std::vector res; + res.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE res_len; + + if (SecKey_GenerateWrappedKeyAsn1(&wrapped[0], wrapped.size(), type, wrappingId, nullptr, asymAlg, &res[0], + res.size(), &res_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GenerateWrappedKeyAsn1 failed"); + return nullptr; + } + + return new ProvKey(res, SEC_KEYCONTAINER_ASN1); +} + +ProvKey* TestCreds::wrapAesWithAes(const SEC_BYTE* clear, Sec_KeyType type, const SEC_BYTE* wrapping, + Sec_KeyType wrappingType, SEC_OBJECTID wrappingId, Sec_CipherAlgorithm symAlg) { + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + + TestCtx ctx; + ctx.init(); + + if (ctx.provisionKey(wrappingId, SEC_STORAGELOC_RAM, wrapping, SecKey_GetKeyLenForKeyType(wrappingType), + wrappingType == SEC_KEYTYPE_AES_128 ? SEC_KEYCONTAINER_RAW_AES_128 : SEC_KEYCONTAINER_RAW_AES_256) == + nullptr) { + SEC_LOG_ERROR("TestCtx::provisionKey failed"); + return nullptr; + } + + std::vector wrapped; + wrapped.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE wrapped_len; + if (SecUtils_WrapSymetric(ctx.proc(), wrappingId, symAlg, &iv[0], const_cast(clear), + SecKey_GetKeyLenForKeyType(type), &wrapped[0], wrapped.size(), &wrapped_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_WrapSymetric failed"); + return nullptr; + } + wrapped.resize(wrapped_len); + + std::vector asn1; + asn1.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE asn1_len; + + if (SecKey_GenerateWrappedKeyAsn1(&wrapped[0], wrapped.size(), type, wrappingId, &iv[0], symAlg, &asn1[0], + asn1.size(), &asn1_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GenerateWrappedKeyAsn1 failed"); + return nullptr; + } + asn1.resize(asn1_len); + + return new ProvKey(asn1, SEC_KEYCONTAINER_ASN1); +} + +ProvKey* TestCreds::wrapRsaWithAes(RSA* rsa, const SEC_BYTE* wrapping, Sec_KeyType wrappingType, + SEC_OBJECTID wrappingId, Sec_CipherAlgorithm symAlg) { + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + + TestCtx ctx; + ctx.init(); + + if (ctx.provisionKey(wrappingId, SEC_STORAGELOC_RAM, wrapping, SecKey_GetKeyLenForKeyType(wrappingType), + wrappingType == SEC_KEYTYPE_AES_128 ? SEC_KEYCONTAINER_RAW_AES_128 : SEC_KEYCONTAINER_RAW_AES_256) == + nullptr) { + SEC_LOG_ERROR("TestCtx::provisionKey failed"); + return nullptr; + } + + std::vector pkcs8 = toPkcs8(rsa); + if (pkcs8.empty()) { + SEC_LOG_ERROR("ToPkcs8 failed"); + return nullptr; + } + Sec_KeyType type = (RSA_size(rsa) == 128) ? SEC_KEYTYPE_RSA_1024 : SEC_KEYTYPE_RSA_2048; + + std::vector wrapped; + wrapped.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE wrapped_len; + if (SecUtils_WrapSymetric(ctx.proc(), wrappingId, symAlg, &iv[0], &pkcs8[0], pkcs8.size(), &wrapped[0], + wrapped.size(), &wrapped_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_WrapSymetric failed"); + return nullptr; + } + wrapped.resize(wrapped_len); + + std::vector asn1; + asn1.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE asn1_len; + + if (SecKey_GenerateWrappedKeyAsn1(&wrapped[0], wrapped.size(), type, wrappingId, &iv[0], symAlg, &asn1[0], + asn1.size(), &asn1_len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GenerateWrappedKeyAsn1 failed"); + return nullptr; + } + asn1.resize(asn1_len); + + return new ProvKey(asn1, SEC_KEYCONTAINER_ASN1); +} + +std::vector TestCreds::getWrappedContentKeyChainEcAes(TestKey contentKey, TestKc kc, SEC_OBJECTID base_id, + Sec_CipherAlgorithm asymAlg) { + std::vector res; + + //generate key0 + std::shared_ptr provKey(TestCreds::getKey(TESTKEY_EC_PRIV, kc, base_id)); + res.push_back(*provKey); + + //generate key1 + std::shared_ptr ec_key(TestCreds::asOpenSslEcKey(TESTKEY_EC_PRIV), EC_KEY_free); + std::shared_ptr wrappedKey1( + wrapAesWithEc(TestCreds::asOpenSslAes(contentKey).data(), TestCreds::getKeyType(contentKey), ec_key.get(), + base_id, asymAlg)); + res.push_back(*wrappedKey1); + + return res; +} + +std::vector TestCreds::getWrappedContentKeyChainEcAesAes(TestKey contentKey, TestKc kc, SEC_OBJECTID base_id, + Sec_KeyType aesType, Sec_CipherAlgorithm asymAlg, Sec_CipherAlgorithm symAlg) { + std::vector res; + + //generate key0 + std::shared_ptr provKey(TestCreds::getKey(TESTKEY_EC_PRIV, kc, base_id)); + + res.push_back(*provKey); + + //generate key1 + std::shared_ptr ec_key(TestCreds::asOpenSslEcKey(TESTKEY_EC_PRIV), EC_KEY_free); + std::vector key1 = TestCtx::random(SecKey_GetKeyLenForKeyType(aesType)); + while (!Is_Valid_Point(ec_key.get(), + (SecKey_GetKeyLenForKeyType(aesType) == SEC_AES_BLOCK_SIZE) ? concat(key1, key1) : key1)) { + SEC_PRINT("Not a valid point. Regenerating.\n"); + key1 = TestCtx::random(SecKey_GetKeyLenForKeyType(aesType)); + } + + std::shared_ptr wrappedKey1(wrapAesWithEc(&key1[0], aesType, ec_key.get(), base_id, asymAlg)); + if (wrappedKey1 == nullptr) + return res; + + res.push_back(*wrappedKey1); + + //generate key2 + std::shared_ptr wrappedKey2( + wrapAesWithAes(TestCreds::asOpenSslAes(contentKey).data(), TestCreds::getKeyType(contentKey), &key1[0], + aesType, base_id + 1, symAlg)); + if (wrappedKey2 == nullptr) + return res; + + res.push_back(*wrappedKey2); + + return res; +} + +std::vector TestCreds::getWrappedContentKeyChainRsaAesRsaAesAes(TestKey contentKey, TestKc kc, + SEC_OBJECTID base_id, Sec_KeyType rsaType, + Sec_CipherAlgorithm asymAlg, + Sec_KeyType aesType, + Sec_CipherAlgorithm symAlg, + Sec_CipherAlgorithm ckSymAlg) { + std::vector res; + + //generate key0 + TestKey rsaPrivKey = (rsaType == SEC_KEYTYPE_RSA_1024) ? TESTKEY_RSA1024_KEK_PRIV : TESTKEY_RSA2048_KEK_PRIV; + std::shared_ptr provKey(TestCreds::getKey(rsaPrivKey, kc, base_id)); + res.push_back(*provKey); + + //generate key1 + std::vector key1 = TestCtx::random(SecKey_GetKeyLenForKeyType(aesType)); + std::shared_ptr rsa_key(TestCreds::asOpenSslRsa(rsaPrivKey), RSA_free); + std::shared_ptr wrappedKey1(wrapAesWithRsa(&key1[0], aesType, rsa_key.get(), base_id, asymAlg)); + if (wrappedKey1 == nullptr) + return res; + + res.push_back(*wrappedKey1); + + //generate key2 + std::shared_ptr wrappedKey2(wrapRsaWithAes(rsa_key.get(), &key1[0], aesType, base_id + 1, symAlg)); + if (wrappedKey2 == nullptr) + return res; + + res.push_back(*wrappedKey2); + + //generate key3 + std::vector key3 = TestCtx::random(SecKey_GetKeyLenForKeyType(aesType)); + std::shared_ptr wrappedKey3(wrapAesWithRsa(&key3[0], aesType, rsa_key.get(), base_id + 2, asymAlg)); + if (wrappedKey3 == nullptr) + return res; + + res.push_back(*wrappedKey3); + + //generate key4 + std::shared_ptr wrappedKey4( + wrapAesWithAes(TestCreds::asOpenSslAes(contentKey).data(), TestCreds::getKeyType(contentKey), &key3[0], + aesType, base_id + 3, ckSymAlg)); + if (wrappedKey4 == nullptr) + return res; + + res.push_back(*wrappedKey4); + + return res; +} + +std::vector TestCreds::getWrappedContentKeyChainRsaAes(TestKey contentKey, TestKc kc, SEC_OBJECTID base_id, + Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg) { + std::vector res; + + //generate key0 + TestKey rsaKey = (rsaType == SEC_KEYTYPE_RSA_1024) ? TESTKEY_RSA1024_KEK_PRIV : TESTKEY_RSA2048_KEK_PRIV; + std::shared_ptr provKey(TestCreds::getKey(rsaKey, kc, base_id)); + res.push_back(*provKey); + + //generate key1 + std::shared_ptr rsa(TestCreds::asOpenSslRsa(rsaKey), RSA_free); + std::shared_ptr wrappedKey1( + wrapAesWithRsa(TestCreds::asOpenSslAes(contentKey).data(), TestCreds::getKeyType(contentKey), rsa.get(), + base_id, asymAlg)); + res.push_back(*wrappedKey1); + + return res; +} + +std::vector TestCreds::getWrappedContentKeyChainRsaAesAes(TestKey contentKey, TestKc kc, SEC_OBJECTID base_id, + Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg, + Sec_KeyType aesType, Sec_CipherAlgorithm symAlg) { + std::vector res; + + //generate key0 + TestKey rsaPrivKey = (rsaType == SEC_KEYTYPE_RSA_1024) ? TESTKEY_RSA1024_KEK_PRIV : TESTKEY_RSA2048_KEK_PRIV; + std::shared_ptr provKey(TestCreds::getKey(rsaPrivKey, kc, base_id)); + res.push_back(*provKey); + + //generate key1 + std::vector key1 = TestCtx::random(SecKey_GetKeyLenForKeyType(aesType)); + + std::shared_ptr rsa_key(TestCreds::asOpenSslRsa(rsaPrivKey), RSA_free); + std::shared_ptr wrappedKey1(wrapAesWithRsa(&key1[0], aesType, rsa_key.get(), base_id, asymAlg)); + if (wrappedKey1 == nullptr) + return res; + + res.push_back(*wrappedKey1); + + //generate key2 + std::shared_ptr wrappedKey2( + wrapAesWithAes(TestCreds::asOpenSslAes(contentKey).data(), TestCreds::getKeyType(contentKey), &key1[0], + aesType, base_id + 1, symAlg)); + if (wrappedKey2 == nullptr) + return res; + + res.push_back(*wrappedKey2); + + return res; +} diff --git a/test/main/cpp/test_ctx.cpp b/test/main/cpp/test_ctx.cpp new file mode 100644 index 0000000..bf0d1f5 --- /dev/null +++ b/test/main/cpp/test_ctx.cpp @@ -0,0 +1,493 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "test_ctx.h" + +#include +#include + +std::string g_log_output; + +static void variable_logger(const char* fmt, ...) { // NOLINT + static pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER; + + pthread_mutex_lock(&_log_mutex); + + va_list args; + va_start(args, fmt); + + char tmp[65536]; + memset(tmp, 0, sizeof(tmp)); + vsnprintf(tmp, sizeof(tmp) - 1, fmt, args); // NOLINT + + va_end(args); + + g_log_output += std::string(tmp); + + pthread_mutex_unlock(&_log_mutex); +} + +void Logger::init() { + g_log_output.clear(); + Sec_SetLogger(variable_logger); +} + +void Logger::shutdown() { + Sec_SetLogger(Sec_DefaultLoggerCb); +} + +const char* Logger::output() { + return g_log_output.c_str(); +} + +std::vector SuiteCtx::getFailed() const { + std::vector res; + + for (int i = 0; i < tests_.size(); ++i) { + if (tests_[i].second == TESTRESULT_FAILED) { + res.push_back(i + 1); + } + } + + return res; +} + +std::vector SuiteCtx::getSucceeded() const { + std::vector res; + + for (int i = 0; i < tests_.size(); ++i) { + if (tests_[i].second == TESTRESULT_SUCCEEDED) { + res.push_back(i + 1); + } + } + + return res; +} + +std::vector SuiteCtx::getSkipped() const { + std::vector res; + + for (int i = 0; i < tests_.size(); ++i) { + if (tests_[i].second == TESTRESULT_SKIPPED) { + res.push_back(i + 1); + } + } + + return res; +} + +std::vector SuiteCtx::getAttempted() const { + std::vector res; + + for (int i = 0; i < tests_.size(); ++i) { + if (tests_[i].second == TESTRESULT_SUCCEEDED || tests_[i].second == TESTRESULT_FAILED) { + res.push_back(i + 1); + } + } + + return res; +} + +std::vector SuiteCtx::getAll() const { + std::vector res; + + for (int i = 0; i < tests_.size(); ++i) { + res.push_back(i + 1); // NOLINT + } + + return res; +} + +Sec_Result TestCtx::init(const char* global_dir, const char* app_dir) { + if (SecProcessor_GetInstance_Directories(&proc_, global_dir, app_dir) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecProcessor_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + if (TestCreds::preprovisionSoc(this) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCreds::preprovisionSoc failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +TestCtx::~TestCtx() { + while (!macs_.empty()) { + SEC_BYTE mac[SEC_MAC_MAX_LEN]; + SEC_SIZE mac_len; + + SecMac_Release(macs_.back(), mac, &mac_len); + macs_.pop_back(); + } + + while (!ciphers_.empty()) { + releaseCipher(ciphers_.back()); + } + + while (!sigs_.empty()) { + releaseSignature(sigs_.back()); + } + + while (!digests_.empty()) { + releaseDigest(digests_.back()); + } + + while (!randoms_.empty()) { + releaseRandom(randoms_.back()); + } + + while (!keys_.empty()) { + releaseKey(keys_.back()); + } + + while (!provisionedKeys_.empty()) { + deleteKey(provisionedKeys_.back()); + } + + while (!certs_.empty()) { + releaseCert(certs_.back()); + } + + while (!provisionedCerts_.empty()) { + deleteCert(provisionedCerts_.back()); + } + + while (!bundles_.empty()) { + releaseBundle(bundles_.back()); + } + + while (!provisionedBundles_.empty()) { + deleteBundle(provisionedBundles_.back()); + } + + if (proc_ != nullptr) { + SecProcessor_Release(proc_); + proc_ = nullptr; + } +} + +Sec_KeyHandle* TestCtx::provisionKey(SEC_OBJECTID id, Sec_StorageLoc loc, const SEC_BYTE* data, SEC_SIZE len, + Sec_KeyContainer kc, bool softWrap) { + Sec_StorageLoc locToUse = loc; + + if (softWrap) { + if (loc == SEC_STORAGELOC_RAM) { + locToUse = SEC_STORAGELOC_RAM_SOFT_WRAPPED; + } else { + locToUse = SEC_STORAGELOC_FILE_SOFT_WRAPPED; + } + } + + if (SecKey_Provision(proc_, id, locToUse, kc, const_cast(data), len) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return nullptr; + } + + provisionedKeys_.push_back(id); + + return getKey(id); +} + +Sec_KeyHandle* TestCtx::provisionKey(SEC_OBJECTID id, Sec_StorageLoc loc, TestKey key, TestKc kc, bool softWrap) { + if (kc == TESTKC_GENERATED) { + Sec_StorageLoc locToUse = loc; + + //generate key with same key type as the known key + if (SecKey_Generate(proc_, id, TestCreds::getKeyType(key), locToUse) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Generate failed"); + return nullptr; + } + + provisionedKeys_.push_back(id); + + return getKey(id); + } + + if (kc == TESTKC_EXPORTED) { + std::shared_ptr prov(TestCreds::getKey(key, TESTKC_CONDITIONAL, id)); + if (!prov) { + SEC_LOG_ERROR("TestCreds::getKey failed"); + return nullptr; + } + + Sec_KeyHandle* keyHandle = provisionKey(id, loc, &prov->key[0], prov->key.size(), prov->kc, softWrap); + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector exported_key; + exported_key.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE exported_len; + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], &exported_key[0], exported_key.size(), &exported_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failed"); + return nullptr; + } + exported_key.resize(exported_len); + + return provisionKey(id, loc, &exported_key[0], exported_key.size(), SEC_KEYCONTAINER_EXPORTED, SEC_FALSE); + } + + std::shared_ptr prov(TestCreds::getKey(key, kc, id)); + if (!prov) { + SEC_LOG_ERROR("TestCreds::getKey failed"); + return nullptr; + } + + return provisionKey(id, loc, &prov->key[0], prov->key.size(), prov->kc, softWrap); +} + +Sec_KeyHandle* TestCtx::getKey(SEC_OBJECTID id) { + Sec_KeyHandle* keyHandle = nullptr; + + if (SecKey_GetInstance(proc_, id, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return nullptr; + } + + keys_.push_back(keyHandle); + + return keyHandle; +} + +void TestCtx::releaseKey(Sec_KeyHandle* keyHandle) { + keys_.remove(keyHandle); + SecKey_Release(keyHandle); +} + +void TestCtx::deleteKey(SEC_OBJECTID id) { + provisionedKeys_.remove(id); + SecKey_Delete(proc_, id); +} + +void TestCtx::releaseCert(Sec_CertificateHandle* certificateHandle) { + certs_.remove(certificateHandle); + SecCertificate_Release(certificateHandle); +} + +Sec_CertificateHandle* TestCtx::provisionCert(SEC_OBJECTID id, Sec_StorageLoc loc, TestCert cert) { + std::shared_ptr prov(TestCreds::getCert(cert)); + if (!prov) { + SEC_LOG_ERROR("TestCreds::getCert failed"); + return nullptr; + } + + if (SecCertificate_Provision(proc_, id, loc, prov->cc, &prov->cert[0], prov->cert.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_Provision failed"); + return nullptr; + } + + provisionedCerts_.push_back(id); + + return getCert(id); +} + +Sec_CertificateHandle* TestCtx::getCert(SEC_OBJECTID id) { + Sec_CertificateHandle* certificateHandle = nullptr; + + if (SecCertificate_GetInstance(proc_, id, &certificateHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCertificate_GetInstance failed"); + return nullptr; + } + + certs_.push_back(certificateHandle); + + return certificateHandle; +} + +void TestCtx::deleteCert(SEC_OBJECTID id) { + provisionedCerts_.remove(id); + SecCertificate_Delete(proc_, id); +} + +Sec_BundleHandle* TestCtx::provisionBundle(SEC_OBJECTID id, Sec_StorageLoc location, + const std::vector& bundle) { + if (SecBundle_Provision(proc_, id, location, const_cast(&bundle[0]), bundle.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecBundle_Provision failed"); + return nullptr; + } + + provisionedBundles_.push_back(id); + + return getBundle(id); +} + +Sec_BundleHandle* TestCtx::getBundle(SEC_OBJECTID id) { + Sec_BundleHandle* bundleHandle = nullptr; + + if (SecBundle_GetInstance(proc_, id, &bundleHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecBundle_GetInstance failed"); + return nullptr; + } + + bundles_.push_back(bundleHandle); + + return bundleHandle; +} + +void TestCtx::releaseBundle(Sec_BundleHandle* bundleHandle) { + bundles_.remove(bundleHandle); + SecBundle_Release(bundleHandle); +} + +void TestCtx::deleteBundle(SEC_OBJECTID id) { + provisionedBundles_.remove(id); + SecBundle_Delete(proc_, id); +} + +Sec_CipherHandle* TestCtx::acquireCipher(Sec_CipherAlgorithm algorithm, Sec_CipherMode mode, Sec_KeyHandle* keyHandle, + SEC_BYTE* iv) { + Sec_CipherHandle* cipherHandle = nullptr; + + if (SecCipher_GetInstance(proc_, algorithm, mode, keyHandle, iv, &cipherHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_GetInstance failed"); + return nullptr; + } + + ciphers_.push_back(cipherHandle); + + return cipherHandle; +} + +void TestCtx::releaseCipher(Sec_CipherHandle* cipherHandle) { + ciphers_.remove(cipherHandle); + SecCipher_Release(cipherHandle); +} + +Sec_RandomHandle* TestCtx::acquireRandom(Sec_RandomAlgorithm algorithm) { + Sec_RandomHandle* randomHandle = nullptr; + + if (SecRandom_GetInstance(proc_, algorithm, &randomHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecRandom_GetInstance failed"); + return nullptr; + } + + randoms_.push_back(randomHandle); + + return randomHandle; +} + +void TestCtx::releaseRandom(Sec_RandomHandle* randomHandle) { + randoms_.remove(randomHandle); + SecRandom_Release(randomHandle); +} + +Sec_SignatureHandle* TestCtx::acquireSignature(Sec_SignatureAlgorithm algorithm, Sec_SignatureMode mode, + Sec_KeyHandle* keyHandle) { + Sec_SignatureHandle* signatureHandle = nullptr; + + if (SecSignature_GetInstance(proc_, algorithm, mode, keyHandle, &signatureHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecSignature_GetInstance failed"); + return nullptr; + } + + sigs_.push_back(signatureHandle); + + return signatureHandle; +} + +void TestCtx::releaseSignature(Sec_SignatureHandle* signatureHandle) { + sigs_.remove(signatureHandle); + SecSignature_Release(signatureHandle); +} + +Sec_MacHandle* TestCtx::acquireMac(Sec_MacAlgorithm algorithm, Sec_KeyHandle* keyHandle) { + Sec_MacHandle* macHandle = nullptr; + + if (SecMac_GetInstance(proc_, algorithm, keyHandle, &macHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecMac_GetInstance failed"); + return nullptr; + } + + macs_.push_back(macHandle); + + return macHandle; +} + +Sec_Result TestCtx::releaseMac(Sec_MacHandle* macHandle, SEC_BYTE* macBuffer, SEC_SIZE* macSize) { + macs_.remove(macHandle); + return SecMac_Release(macHandle, macBuffer, macSize); +} + +void TestCtx::releaseMac(Sec_MacHandle* macHandle) { + SEC_BYTE macBuffer[SEC_DIGEST_MAX_LEN]; + SEC_SIZE macSize; + + releaseMac(macHandle, macBuffer, &macSize); +} + +Sec_DigestHandle* TestCtx::acquireDigest(Sec_DigestAlgorithm algorithm) { + Sec_DigestHandle* digestHandle = nullptr; + + if (SecDigest_GetInstance(proc_, algorithm, &digestHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecDigest_GetInstance failed"); + return nullptr; + } + + digests_.push_back(digestHandle); + + return digestHandle; +} + +Sec_Result TestCtx::releaseDigest(Sec_DigestHandle* digestHandle, SEC_BYTE* digestOutput, SEC_SIZE* digestSize) { + digests_.remove(digestHandle); + return SecDigest_Release(digestHandle, digestOutput, digestSize); +} + +void TestCtx::releaseDigest(Sec_DigestHandle* digestHandle) { + SEC_BYTE digestOutput[SEC_DIGEST_MAX_LEN]; + SEC_SIZE digestSize; + + releaseDigest(digestHandle, digestOutput, &digestSize); +} + +void TestCtx::printHex(const char* label, const std::vector& data) { + SEC_PRINT("%s[%d]: ", label, data.size()); + Sec_PrintHex((void*) &data[0], data.size()); // NOLINT + SEC_PRINT("\n"); +} + +std::vector TestCtx::random(SEC_SIZE len) { + std::vector output; + + output.resize(len); + + RAND_bytes(&output[0], static_cast(output.size())); + + return output; +} + +std::vector TestCtx::coalesceInputs(const std::vector>& inputs) { + std::vector input; + + for (const auto& i : inputs) { + input.insert(input.end(), i.begin(), i.end()); + } + + return input; +} + +SEC_SIZE TestCtx::coalesceInputSizes(const std::vector& inputSizes) { + SEC_SIZE out = 0; + + for (unsigned int inputSize : inputSizes) { + out += inputSize; + } + + return out; +} diff --git a/test/main/cpp/wrapped.cpp b/test/main/cpp/wrapped.cpp new file mode 100644 index 0000000..f476202 --- /dev/null +++ b/test/main/cpp/wrapped.cpp @@ -0,0 +1,1370 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "wrapped.h" // NOLINT +#include "cipher.h" +#include "mac.h" +#include "test_ctx.h" +#include + +static ProvKey convertV2ToV3(const std::vector& v2) { + ProvKey lastKey; + + for (unsigned int i = 0; i < v2.size(); ++i) { + const ProvKey& key = v2[i]; + + if (key.kc == SEC_KEYCONTAINER_ASN1) { + SEC_BYTE payload[SEC_KEYCONTAINER_MAX_LEN]; + SEC_SIZE payloadLen; + Sec_KeyType wrappedKeyType; + SEC_OBJECTID wrappingId; + SEC_BYTE wrappingIv[SEC_AES_BLOCK_SIZE]; + Sec_CipherAlgorithm wrappingAlg; + SEC_SIZE key_offset; + + if (SecKey_ExtractWrappedKeyParamsAsn1BufferOff(const_cast(&key.key[0]), key.key.size(), payload, + sizeof(payload), &payloadLen, &wrappedKeyType, &wrappingId, wrappingIv, &wrappingAlg, + &key_offset) != SEC_RESULT_SUCCESS) { + + SEC_LOG_ERROR("SecKey_ExtractWrappedKeyParamsAsn1BufferOff failed"); + return {}; + } + + SEC_PRINT("%d: wrappingId=" SEC_OBJECTID_PATTERN "\n", i, wrappingId); + + //special case for wrapping root key + if (wrappingId == 0) { + lastKey = key; + continue; + } + + std::vector v3Key; + v3Key.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE v3KeyLen; + + if (i == 0) { + lastKey = key; + } else { + if (SecKey_GenerateWrappedKeyAsn1V3(payload, payloadLen, wrappedKeyType, &lastKey.key[0], + lastKey.key.size(), wrappingIv, wrappingAlg, &v3Key[0], v3Key.size(), &v3KeyLen, + key_offset) != SEC_RESULT_SUCCESS) { + + SEC_LOG_ERROR("SecKey_GenerateWrappedKeyAsn1V3 failed"); + return {}; + } + + v3Key.resize(v3KeyLen); + + lastKey = ProvKey(v3Key, SEC_KEYCONTAINER_ASN1); + } + } else { + lastKey = key; + } + } + + return lastKey; +} + +Sec_Result testWrappedCipherSingleRsaAesRsaAesAes(TestKey key, TestKc kc, Sec_KeyType rsaType, + Sec_CipherAlgorithm asymAlg, Sec_KeyType aesType, Sec_CipherAlgorithm symAlg, Sec_CipherAlgorithm ckSymAlg, + WrappedKeyFormatVersion wkfv) { + TestCtx ctx; + + if (!TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC)) + return SEC_RESULT_SUCCESS; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID id = SEC_OBJECTID_USER_BASE; + + std::vector keys = TestCreds::getWrappedContentKeyChainRsaAesRsaAesAes(key, kc, id, rsaType, asymAlg, + aesType, symAlg, ckSymAlg); + if (keys.empty()) { + SEC_LOG_ERROR("TestCreds::getWrappedContentKeyChain failed"); + return SEC_RESULT_FAILURE; + } + + switch (wkfv) { + case WKFV_V2: { + for (unsigned int i = 0; i < keys.size(); ++i) { + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, keys[i].kc, &keys[i].key[0], + keys[i].key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision[%d] failed", i); + return SEC_RESULT_FAILURE; + } + } + + break; + } + + case WKFV_V3: { + ProvKey v3Key = convertV2ToV3(keys); + if (v3Key.key.empty()) { + SEC_LOG_ERROR("ConvertV2ToV3 failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, v3Key.kc, &v3Key.key[0], v3Key.key.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + break; + } + + default: + SEC_LOG_ERROR("Unknown WKFT encountered: %d", wkfv); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType keyType = TestCreds::getKeyType(key); + std::vector clear = TestCreds::asOpenSslAes(key); + + if (cipherEncDecSingle(&ctx, id - 1, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_IsAES(keyType) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), id - 1, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + return SEC_RESULT_FAILURE; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, id - 1, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + return SEC_RESULT_FAILURE; + } + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testWrappedCipherSingleRsaAes(TestKey key, TestKc kc, Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg, + WrappedKeyFormatVersion wkfv) { + TestCtx ctx; + + if (!TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC)) + return SEC_RESULT_SUCCESS; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID id = SEC_OBJECTID_USER_BASE; + + std::vector keys = TestCreds::getWrappedContentKeyChainRsaAes(key, kc, id, rsaType, asymAlg); + if (keys.empty()) { + SEC_LOG_ERROR("TestCreds::getWrappedContentKeyChain failed"); + return SEC_RESULT_FAILURE; + } + + switch (wkfv) { + case WKFV_V2: { + for (unsigned int i = 0; i < keys.size(); ++i) { + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, keys[i].kc, &keys[i].key[0], + keys[i].key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision[%d] failed", i); + return SEC_RESULT_FAILURE; + } + } + + break; + } + + case WKFV_V3: { + ProvKey v3Key = convertV2ToV3(keys); + if (v3Key.key.empty()) { + SEC_LOG_ERROR("ConvertV2ToV3 failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, v3Key.kc, &v3Key.key[0], v3Key.key.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + break; + } + + default: + SEC_LOG_ERROR("Unknown WKFT encountered: %d", wkfv); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType keyType = TestCreds::getKeyType(key); + std::vector clear = TestCreds::asOpenSslAes(key); + + if (cipherEncDecSingle(&ctx, id - 1, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_IsAES(keyType) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), id - 1, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + return SEC_RESULT_FAILURE; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, id - 1, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + return SEC_RESULT_FAILURE; + } + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testWrappedCipherSingleEcAes(TestKey key, TestKc kc, Sec_CipherAlgorithm asymAlg, + WrappedKeyFormatVersion wkfv) { + TestCtx ctx; + + if (!TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC)) + return SEC_RESULT_SUCCESS; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID id = SEC_OBJECTID_USER_BASE; + + std::vector keys = TestCreds::getWrappedContentKeyChainEcAes(key, kc, id, asymAlg); + if (keys.empty()) { + SEC_LOG_ERROR("TestCreds::getWrappedContentKeyChain failed"); + return SEC_RESULT_FAILURE; + } + + switch (wkfv) { + case WKFV_V2: { + for (unsigned int i = 0; i < keys.size(); ++i) { + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, keys[i].kc, &keys[i].key[0], + keys[i].key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision[%d] failed", i); + return SEC_RESULT_FAILURE; + } + } + + break; + } + + case WKFV_V3: { + ProvKey v3Key = convertV2ToV3(keys); + if (v3Key.key.empty()) { + SEC_LOG_ERROR("ConvertV2ToV3 failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, v3Key.kc, &v3Key.key[0], v3Key.key.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + break; + } + + default: + SEC_LOG_ERROR("Unknown WKFT encountered: %d", wkfv); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType keyType = TestCreds::getKeyType(key); + std::vector clear = TestCreds::asOpenSslAes(key); + + if (SecKey_IsAES(keyType) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), id - 1, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + return SEC_RESULT_FAILURE; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, id - 1, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + return SEC_RESULT_FAILURE; + } + } + + //check enc/dec + if (cipherEncDecSingle(&ctx, id - 1, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testWrappedCipherSingleEcAesAes(TestKey key, TestKc kc, Sec_KeyType aesType, Sec_CipherAlgorithm asymAlg, + Sec_CipherAlgorithm symAlg, WrappedKeyFormatVersion wkfv) { + TestCtx ctx; + + if (!TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC)) + return SEC_RESULT_SUCCESS; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID id = SEC_OBJECTID_USER_BASE; + + std::vector keys = TestCreds::getWrappedContentKeyChainEcAesAes(key, kc, id, aesType, asymAlg, symAlg); + if (keys.empty()) { + SEC_LOG_ERROR("TestCreds::getWrappedContentKeyChain failed"); + return SEC_RESULT_FAILURE; + } + + switch (wkfv) { + case WKFV_V2: { + for (unsigned int i = 0; i < keys.size(); ++i) { + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, keys[i].kc, &keys[i].key[0], + keys[i].key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision[%d] failed", i); + return SEC_RESULT_FAILURE; + } + } + + break; + } + + case WKFV_V3: { + ProvKey v3Key = convertV2ToV3(keys); + if (v3Key.key.empty()) { + SEC_LOG_ERROR("ConvertV2ToV3 failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, v3Key.kc, &v3Key.key[0], v3Key.key.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + break; + } + + default: + SEC_LOG_ERROR("Unknown WKFT encountered: %d", wkfv); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType keyType = TestCreds::getKeyType(key); + std::vector clear = TestCreds::asOpenSslAes(key); + + if (SecKey_IsAES(keyType) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), id - 1, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + return SEC_RESULT_FAILURE; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, id - 1, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + return SEC_RESULT_FAILURE; + } + } + + //check enc/dec + if (cipherEncDecSingle(&ctx, id - 1, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testExportWrappedRsaAesAes(TestKey key, TestKc kc, Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg, + Sec_KeyType aesType, Sec_CipherAlgorithm symAlg, WrappedKeyFormatVersion wkfv) { + TestCtx ctx; + + if (!TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC)) + return SEC_RESULT_SUCCESS; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID id = SEC_OBJECTID_USER_BASE; + + std::vector keys = TestCreds::getWrappedContentKeyChainRsaAesAes(key, kc, id, rsaType, asymAlg, aesType, + symAlg); + if (keys.empty()) { + SEC_LOG_ERROR("TestCreds::getWrappedContentKeyChain failed"); + return SEC_RESULT_FAILURE; + } + + switch (wkfv) { + case WKFV_V2: { + for (unsigned int i = 0; i < keys.size(); ++i) { + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, keys[i].kc, &keys[i].key[0], + keys[i].key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision[%d] failed", i); + return SEC_RESULT_FAILURE; + } + } + + break; + } + + case WKFV_V3: { + ProvKey v3Key = convertV2ToV3(keys); + if (v3Key.key.empty()) { + SEC_LOG_ERROR("ConvertV2ToV3 failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, v3Key.kc, &v3Key.key[0], v3Key.key.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + break; + } + + default: + SEC_LOG_ERROR("Unknown WKFT encountered: %d", wkfv); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID idContentKey = id - 1; + + //export content key + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector exported_key; + exported_key.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE exported_len; + + Sec_KeyHandle* keyHandle; + if (SecKey_GetInstance(ctx.proc(), idContentKey, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], &exported_key[0], exported_key.size(), &exported_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failed"); + SecKey_Release(keyHandle); + return SEC_RESULT_FAILURE; + } + exported_key.resize(exported_len); + SecKey_Release(keyHandle); + + //provision exported + if (SecKey_Provision(ctx.proc(), idContentKey, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, &exported_key[0], + exported_key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType keyType = TestCreds::getKeyType(key); + std::vector clear = TestCreds::asOpenSslAes(key); + + if (cipherEncDecSingle(&ctx, idContentKey, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_IsAES(keyType) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), idContentKey, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + return SEC_RESULT_FAILURE; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, idContentKey, &clear[0], clear.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + return SEC_RESULT_FAILURE; + } + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testWrappedCipherSingleRsaAesAes(TestKey key, TestKc kc, Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg, + Sec_KeyType aesType, Sec_CipherAlgorithm symAlg, + WrappedKeyFormatVersion wkfv) { + TestCtx ctx; + + if (!TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC)) + return SEC_RESULT_SUCCESS; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID id = SEC_OBJECTID_USER_BASE; + + std::vector keys = TestCreds::getWrappedContentKeyChainRsaAesAes(key, kc, id, rsaType, asymAlg, aesType, + symAlg); + if (keys.empty()) { + SEC_LOG_ERROR("TestCreds::getWrappedContentKeyChainRsaAesAes failed"); + return SEC_RESULT_FAILURE; + } + + switch (wkfv) { + case WKFV_V2: { + for (unsigned int i = 0; i < keys.size(); ++i) { + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, keys[i].kc, &keys[i].key[0], + keys[i].key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision[%d] failed", i); + return SEC_RESULT_FAILURE; + } + } + + break; + } + + case WKFV_V3: { + ProvKey v3Key = convertV2ToV3(keys); + if (v3Key.key.empty()) { + SEC_LOG_ERROR("ConvertV2ToV3 failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, v3Key.kc, &v3Key.key[0], + v3Key.key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + break; + } + + default: + SEC_LOG_ERROR("Unknown WKFT encountered: %d", wkfv); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType keyType = TestCreds::getKeyType(key); + std::vector clear = TestCreds::asOpenSslAes(key); + + if (cipherEncDecSingle(&ctx, id - 1, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_IsAES(keyType) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), id - 1, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + return SEC_RESULT_FAILURE; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, id - 1, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + return SEC_RESULT_FAILURE; + } + } + + return SEC_RESULT_SUCCESS; +} + +static std::vector asn1(Sec_ProcessorHandle* processorHandle, const std::vector& clear, + Sec_KeyType type, SEC_OBJECTID wrappingId, Sec_CipherAlgorithm algorithm) { + std::vector wrapped; + wrapped.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE wrapped_len; + + std::vector iv = TestCtx::random(SEC_AES_BLOCK_SIZE); + + if (SecCipher_SingleInputId(processorHandle, algorithm, SEC_CIPHERMODE_ENCRYPT, wrappingId, &iv[0], + (SEC_BYTE*) &clear[0], clear.size(), &wrapped[0], wrapped.size(), &wrapped_len) != // NOLINT + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecCipher_SingleInputId failed"); + return {}; + } + wrapped.resize(wrapped_len); + + std::vector res; + SEC_SIZE written; + res.resize(SEC_KEYCONTAINER_MAX_LEN); + + if (SecKey_GenerateWrappedKeyAsn1Off(&wrapped[0], wrapped.size(), type, wrappingId, &iv[0], algorithm, &res[0], + res.size(), &written, 0) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GenerateWrappedKeyAsn1Off failed"); + return {}; + } + + res.resize(written); + + return res; +} + +Sec_Result testWrappedKDFCMACAES128(SEC_OBJECTID idDerived, SEC_OBJECTID idBase, SEC_OBJECTID idWrapped, + Sec_KeyType keyType, SEC_BYTE counter, uint32_t L) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if (ctx.provisionKey(idBase, SEC_STORAGELOC_RAM, TESTKEY_AES128, TESTKC_CONDITIONAL) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //base key + TestCtx::printHex("baseKey", TestCreds::asOpenSslAes(TESTKEY_AES128)); + + //label + std::vector otherData = TestCtx::random(10); + //separator + otherData.push_back(0); + //ctx + std::vector ctx2 = TestCtx::random(32); + otherData.insert(otherData.end(), ctx2.begin(), ctx2.end()); + + otherData.push_back(0); + otherData.push_back(0); + otherData.push_back(0); + otherData.push_back(0); + Sec_Uint32ToBEBytes(L, &otherData[otherData.size() - 4]); + + TestCtx::printHex("otherData", otherData); + + if (SecKey_Derive_CMAC_AES128(ctx.proc(), idDerived, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, idBase, &otherData[0], + otherData.size(), &counter, 1) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_CMAC_AES128 failed"); + return SEC_RESULT_FAILURE; + } + + //create wrapped key protected by CMAC_AES128 derived key + std::vector clear = TestCtx::random(SecKey_GetKeyLenForKeyType(keyType)); + TestCtx::printHex("key", clear); + + std::vector wrapped = asn1(ctx.proc(), clear, keyType, idDerived, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING); + if (wrapped.empty()) { + SEC_LOG_ERROR("Asn1 failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(ctx.proc(), idWrapped, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_ASN1, &wrapped[0], + wrapped.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_IsAES(keyType) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), idWrapped, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + return SEC_RESULT_FAILURE; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, idWrapped, &clear[0], clear.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + return SEC_RESULT_FAILURE; + } + } + + SecKey_Delete(ctx.proc(), idDerived); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testExportedKDFCMACAES128(SEC_OBJECTID idDerived, SEC_OBJECTID idBase, SEC_OBJECTID idWrapped, + Sec_KeyType keyType, SEC_BYTE counter, uint32_t L) { + TestCtx ctx; + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + if (ctx.provisionKey(idBase, SEC_STORAGELOC_RAM, TESTKEY_AES128, TESTKC_CONDITIONAL) == nullptr) { + SEC_LOG_ERROR("ctx.provisionKey failed"); + return SEC_RESULT_FAILURE; + } + + //export content key + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector exported_key; + exported_key.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE exported_len; + + Sec_KeyHandle* keyHandle; + if (SecKey_GetInstance(ctx.proc(), idBase, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], &exported_key[0], exported_key.size(), &exported_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failed"); + SecKey_Release(keyHandle); + return SEC_RESULT_FAILURE; + } + exported_key.resize(exported_len); + SecKey_Release(keyHandle); + + //provision exported + if (SecKey_Provision(ctx.proc(), idBase, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, &exported_key[0], + exported_key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + //base key + TestCtx::printHex("baseKey", TestCreds::asOpenSslAes(TESTKEY_AES128)); + + //label + std::vector otherData = TestCtx::random(10); + //separator + otherData.push_back(0); + //ctx + std::vector ctx2 = TestCtx::random(32); + otherData.insert(otherData.end(), ctx2.begin(), ctx2.end()); + + otherData.push_back(0); + otherData.push_back(0); + otherData.push_back(0); + otherData.push_back(0); + Sec_Uint32ToBEBytes(L, &otherData[otherData.size() - 4]); + + TestCtx::printHex("otherData", otherData); + + if (SecKey_Derive_CMAC_AES128(ctx.proc(), idDerived, SEC_KEYTYPE_AES_128, SEC_STORAGELOC_RAM, idBase, &otherData[0], + otherData.size(), &counter, 1) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Derive_CMAC_AES128 failed"); + return SEC_RESULT_FAILURE; + } + + //create wrapped key protected by CMAC_AES128 derived key + std::vector clear = TestCtx::random(SecKey_GetKeyLenForKeyType(keyType)); + TestCtx::printHex("key", clear); + + std::vector wrapped = asn1(ctx.proc(), clear, keyType, idDerived, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING); + if (wrapped.empty()) { + SEC_LOG_ERROR("Asn1 failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(ctx.proc(), idWrapped, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_ASN1, &wrapped[0], + wrapped.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_IsAES(keyType) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), idWrapped, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + return SEC_RESULT_FAILURE; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, idWrapped, &clear[0], clear.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + return SEC_RESULT_FAILURE; + } + } + + SecKey_Delete(ctx.proc(), idDerived); + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testExportWrappedEccAesAes(TestKey key, TestKc kc, Sec_CipherAlgorithm asymAlg, Sec_KeyType aesType, + Sec_CipherAlgorithm symAlg, WrappedKeyFormatVersion wkfv) { + TestCtx ctx; + + if (!TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC)) + return SEC_RESULT_SUCCESS; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID id = SEC_OBJECTID_USER_BASE; + + std::vector keys = TestCreds::getWrappedContentKeyChainEcAesAes(key, kc, id, aesType, asymAlg, symAlg); + if (keys.empty()) { + SEC_LOG_ERROR("TestCreds::getWrappedContentKeyChainEcAesAes failed"); + return SEC_RESULT_FAILURE; + } + + switch (wkfv) { + case WKFV_V2: { + for (unsigned int i = 0; i < keys.size(); ++i) { + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, keys[i].kc, &keys[i].key[0], + keys[i].key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision[%d] failed", i); + return SEC_RESULT_FAILURE; + } + } + + break; + } + + case WKFV_V3: { + ProvKey v3Key = convertV2ToV3(keys); + if (v3Key.key.empty()) { + SEC_LOG_ERROR("ConvertV2ToV3 failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, v3Key.kc, &v3Key.key[0], v3Key.key.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + break; + } + + default: + SEC_LOG_ERROR("Unknown WKFT encountered: %d", wkfv); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID idContentKey = id - 1; + + //export content key + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector exported_key; + exported_key.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE exported_len; + + Sec_KeyHandle* keyHandle; + if (SecKey_GetInstance(ctx.proc(), idContentKey, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], &exported_key[0], exported_key.size(), &exported_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failed"); + SecKey_Release(keyHandle); + return SEC_RESULT_FAILURE; + } + exported_key.resize(exported_len); + SecKey_Release(keyHandle); + + //provision exported + if (SecKey_Provision(ctx.proc(), idContentKey, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, &exported_key[0], + exported_key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType keyType = TestCreds::getKeyType(key); + std::vector clear = TestCreds::asOpenSslAes(key); + + if (cipherEncDecSingle(&ctx, idContentKey, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_IsAES(keyType) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), idContentKey, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + return SEC_RESULT_FAILURE; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, idContentKey, &clear[0], clear.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + return SEC_RESULT_FAILURE; + } + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testExportWrappedEccAes(TestKey key, TestKc kc, Sec_CipherAlgorithm asymAlg, WrappedKeyFormatVersion wkfv) { + TestCtx ctx; + + if (!TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC)) + return SEC_RESULT_SUCCESS; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID id = SEC_OBJECTID_USER_BASE; + + std::vector keys = TestCreds::getWrappedContentKeyChainEcAes(key, kc, id, asymAlg); + if (keys.empty()) { + SEC_LOG_ERROR("TestCreds::getWrappedContentKeyChain failed"); + return SEC_RESULT_FAILURE; + } + + switch (wkfv) { + case WKFV_V2: { + for (unsigned int i = 0; i < keys.size(); ++i) { + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, keys[i].kc, &keys[i].key[0], + keys[i].key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision[%d] failed", i); + return SEC_RESULT_FAILURE; + } + } + + break; + } + + case WKFV_V3: { + ProvKey v3Key = convertV2ToV3(keys); + if (v3Key.key.empty()) { + SEC_LOG_ERROR("ConvertV2ToV3 failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, v3Key.kc, &v3Key.key[0], v3Key.key.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + break; + } + + default: + SEC_LOG_ERROR("Unknown WKFT encountered: %d", wkfv); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID idContentKey = id - 1; + + //export content key + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector exported_key; + exported_key.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE exported_len; + + Sec_KeyHandle* keyHandle; + if (SecKey_GetInstance(ctx.proc(), idContentKey, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], &exported_key[0], exported_key.size(), &exported_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failed"); + SecKey_Release(keyHandle); + return SEC_RESULT_FAILURE; + } + exported_key.resize(exported_len); + SecKey_Release(keyHandle); + + //provision exported + if (SecKey_Provision(ctx.proc(), idContentKey, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, &exported_key[0], + exported_key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType keyType = TestCreds::getKeyType(key); + std::vector clear = TestCreds::asOpenSslAes(key); + + if (cipherEncDecSingle(&ctx, idContentKey, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_IsAES(keyType) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), idContentKey, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + return SEC_RESULT_FAILURE; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, idContentKey, &clear[0], clear.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + return SEC_RESULT_FAILURE; + } + } + + return SEC_RESULT_SUCCESS; +} + +static std::vector getWrappedContentKeyChainGeneratedEcAes(TestKey contentKey, EC_KEY* ec_key, + SEC_OBJECTID base_id, Sec_CipherAlgorithm asymAlg) { + std::vector res; + + //generate key1 + std::shared_ptr wrappedKey1( + TestCreds::wrapAesWithEc(TestCreds::asOpenSslAes(contentKey).data(), TestCreds::getKeyType(contentKey), + ec_key, base_id, asymAlg)); + res.push_back(*wrappedKey1); + + return res; +} + +static bool Is_Valid_Point(EC_KEY* ec_key, const std::vector& data) { + if (data.size() != SEC_ECC_NISTP256_KEY_LEN) { + SEC_LOG_ERROR("Input size needed != One BIGNUM"); + return false; + } + + // Convert the input buffer to be encrypted to a BIGNUM + std::shared_ptr inputAsBN(BN_new(), BN_free); + if (inputAsBN == nullptr) { + SEC_LOG_ERROR("BN_new failed"); + return false; + } + + if (BN_bin2bn(&data[0], static_cast(data.size()), inputAsBN.get()) == nullptr) { + SEC_LOG_ERROR("BN_bin2bn failed."); + return false; + } + + const EC_GROUP* group = EC_KEY_get0_group(ec_key); + if (group == nullptr) { + SEC_LOG_ERROR("EC_KEY_get0_group failed"); + return false; + } + + std::shared_ptr ctx(BN_CTX_new(), BN_CTX_free); + if (ctx == nullptr) { + SEC_LOG_ERROR("BN_CTX_new failed"); + return false; + } + + std::shared_ptr pt(EC_POINT_new(group), EC_POINT_free); + if (pt == nullptr) { + SEC_LOG_ERROR("EC_POINT_new failed"); + return false; + } + + if (EC_POINT_set_compressed_coordinates_GFp(group, pt.get(), inputAsBN.get(), 0, ctx.get()) != 1) { + SEC_LOG_ERROR("EC_POINT_set_compressed_coordinates_GFp failed"); + return false; + } + + return true; +} + +template +std::vector concat(std::vector& a, std::vector& b) { + std::vector ret = std::vector(); + std::copy(a.begin(), a.end(), std::back_inserter(ret)); + std::copy(b.begin(), b.end(), std::back_inserter(ret)); + return ret; +} + +static std::vector getWrappedContentKeyChainGeneratedEcAesAes(TestKey contentKey, EC_KEY* ec_key, + SEC_OBJECTID base_id, Sec_KeyType aesType, + Sec_CipherAlgorithm asymAlg, + Sec_CipherAlgorithm symAlg) { + std::vector res; + + //generate key1 + std::vector key1 = TestCtx::random(SecKey_GetKeyLenForKeyType(aesType)); + while (!Is_Valid_Point(ec_key, + (SecKey_GetKeyLenForKeyType(aesType) == SEC_AES_BLOCK_SIZE) ? concat(key1, key1) : key1)) { + SEC_PRINT("Not a valid point. Regenerating.\n"); + key1 = TestCtx::random(SecKey_GetKeyLenForKeyType(aesType)); + } + + std::shared_ptr wrappedKey1(TestCreds::wrapAesWithEc(&key1[0], aesType, ec_key, base_id, asymAlg)); + if (wrappedKey1 == nullptr) + return res; + + res.push_back(*wrappedKey1); + + //generate key2 + std::shared_ptr wrappedKey2( + TestCreds::wrapAesWithAes(TestCreds::asOpenSslAes(contentKey).data(), TestCreds::getKeyType(contentKey), + &key1[0], aesType, base_id + 1, symAlg)); + if (wrappedKey2 == nullptr) + return res; + + res.push_back(*wrappedKey2); + + return res; +} + +static EC_KEY* ECCFromPubBinary(Sec_ECCRawPublicKey* binary) { + BN_CTX* ctx = BN_CTX_new(); + + if (binary->type != SEC_KEYTYPE_ECC_NISTP256_PUBLIC && binary->type != SEC_KEYTYPE_ECC_NISTP256) + return nullptr; + + EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); //create ec_key structure with NIST p256 curve; + const EC_GROUP* group = EC_KEY_get0_group(ec_key); + EC_POINT* ec_point = EC_POINT_new(group); + BN_CTX_start(ctx); + BIGNUM* xp; + BIGNUM* yp; + + do { + if (((xp = BN_CTX_get(ctx)) == nullptr) || ((yp = BN_CTX_get(ctx)) == nullptr)) + break; + + EC_POINT_set_affine_coordinates_GFp(group, ec_point, + BN_bin2bn(binary->x, static_cast(Sec_BEBytesToUint32(binary->key_len)), xp), + BN_bin2bn(binary->y, static_cast(Sec_BEBytesToUint32(binary->key_len)), yp), ctx); + EC_KEY_set_public_key(ec_key, ec_point); + } while (false); + + EC_POINT_free(ec_point); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + + return ec_key; +} + +static EC_KEY* generateEcAndGetPublic(TestCtx& ctx, SEC_OBJECTID id) { + Sec_KeyHandle* keyHandle = ctx.provisionKey(id, SEC_STORAGELOC_RAM, TESTKEY_EC_PRIV, TESTKC_GENERATED, SEC_FALSE); + if (keyHandle == nullptr) { + SEC_LOG_ERROR("ProvisionKey failed"); + return nullptr; + } + + Sec_ECCRawPublicKey public_key; + memset(&public_key, 0, sizeof(public_key)); + if (SecKey_ExtractECCPublicKey(keyHandle, &public_key) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExtractECCPublicKey failed"); + return nullptr; + } + + EC_KEY* openssl_key = ECCFromPubBinary(&public_key); + if (openssl_key == nullptr) { + SEC_LOG_ERROR("_ECCFromPubBinary failed"); + return nullptr; + } + + return openssl_key; +} + +Sec_Result testExportWrappedGeneratedEccAesAes(TestKey key, Sec_CipherAlgorithm asymAlg, Sec_KeyType aesType, + Sec_CipherAlgorithm symAlg, WrappedKeyFormatVersion wkfv) { + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID id = SEC_OBJECTID_USER_BASE; + + EC_KEY* pub_ec = generateEcAndGetPublic(ctx, id); + std::vector keys = getWrappedContentKeyChainGeneratedEcAesAes(key, pub_ec, id, aesType, asymAlg, symAlg); + SEC_ECC_FREE(pub_ec); + + if (keys.empty()) { + SEC_LOG_ERROR("TestCreds::getWrappedContentKeyChain failed"); + return SEC_RESULT_FAILURE; + } + + //generated key is alreay provisioned + id++; + + switch (wkfv) { + case WKFV_V2: { + for (unsigned int i = 0; i < keys.size(); ++i) { + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, keys[i].kc, &keys[i].key[0], + keys[i].key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision[%d] failed", i); + return SEC_RESULT_FAILURE; + } + } + + break; + } + + case WKFV_V3: { + ProvKey v3Key = convertV2ToV3(keys); + if (v3Key.key.empty()) { + SEC_LOG_ERROR("ConvertV2ToV3 failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, v3Key.kc, &v3Key.key[0], v3Key.key.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + break; + } + + default: + SEC_LOG_ERROR("Unknown WKFT encountered: %d", wkfv); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID idContentKey = id - 1; + + //export content key + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector exported_key; + exported_key.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE exported_len; + + Sec_KeyHandle* keyHandle; + if (SecKey_GetInstance(ctx.proc(), idContentKey, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], &exported_key[0], exported_key.size(), &exported_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failed"); + SecKey_Release(keyHandle); + return SEC_RESULT_FAILURE; + } + exported_key.resize(exported_len); + SecKey_Release(keyHandle); + + //provision exported + if (SecKey_Provision(ctx.proc(), idContentKey, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, &exported_key[0], + exported_key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType keyType = TestCreds::getKeyType(key); + std::vector clear = TestCreds::asOpenSslAes(key); + + if (cipherEncDecSingle(&ctx, idContentKey, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_IsAES(keyType) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), idContentKey, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + return SEC_RESULT_FAILURE; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, idContentKey, &clear[0], clear.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + return SEC_RESULT_FAILURE; + } + } + + return SEC_RESULT_SUCCESS; +} + +Sec_Result testExportWrappedGeneratedEccAes(TestKey key, Sec_CipherAlgorithm asymAlg, WrappedKeyFormatVersion wkfv) { + TestCtx ctx; + + if (ctx.init() != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("TestCtx.init failed"); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID id = SEC_OBJECTID_USER_BASE; + + EC_KEY* pub_ec = generateEcAndGetPublic(ctx, id); + std::vector keys = getWrappedContentKeyChainGeneratedEcAes(key, pub_ec, id, asymAlg); + SEC_ECC_FREE(pub_ec); + + if (keys.empty()) { + SEC_LOG_ERROR("TestCreds::getWrappedContentKeyChain failed"); + return SEC_RESULT_FAILURE; + } + + //generated key is alreay provisioned + id++; + + switch (wkfv) { + case WKFV_V2: { + for (unsigned int i = 0; i < keys.size(); ++i) { + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, keys[i].kc, &keys[i].key[0], + keys[i].key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision[%d] failed", i); + return SEC_RESULT_FAILURE; + } + } + + break; + } + + case WKFV_V3: { + ProvKey v3Key = convertV2ToV3(keys); + if (v3Key.key.empty()) { + SEC_LOG_ERROR("ConvertV2ToV3 failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_Provision(ctx.proc(), id++, SEC_STORAGELOC_RAM, v3Key.kc, &v3Key.key[0], + v3Key.key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + break; + } + + default: + SEC_LOG_ERROR("Unknown WKFT encountered: %d", wkfv); + return SEC_RESULT_FAILURE; + } + + SEC_OBJECTID idContentKey = id - 1; + + //export content key + std::vector derivation_input = TestCtx::random(SEC_AES_BLOCK_SIZE); + std::vector exported_key; + exported_key.resize(SEC_KEYCONTAINER_MAX_LEN); + SEC_SIZE exported_len; + + Sec_KeyHandle* keyHandle; + if (SecKey_GetInstance(ctx.proc(), idContentKey, &keyHandle) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_GetInstance failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_ExportKey(keyHandle, &derivation_input[0], &exported_key[0], exported_key.size(), &exported_len) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_ExportKey failed"); + SecKey_Release(keyHandle); + return SEC_RESULT_FAILURE; + } + exported_key.resize(exported_len); + SecKey_Release(keyHandle); + + //provision exported + if (SecKey_Provision(ctx.proc(), idContentKey, SEC_STORAGELOC_RAM, SEC_KEYCONTAINER_EXPORTED, &exported_key[0], + exported_key.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecKey_Provision failed"); + return SEC_RESULT_FAILURE; + } + + Sec_KeyType keyType = TestCreds::getKeyType(key); + std::vector clear = TestCreds::asOpenSslAes(key); + + if (cipherEncDecSingle(&ctx, idContentKey, SEC_CIPHERALGORITHM_AES_ECB_NO_PADDING, 256) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("CipherEncDecSingle failed"); + return SEC_RESULT_FAILURE; + } + + if (SecKey_IsAES(keyType) == SEC_TRUE) { + if (aesKeyCheck(ctx.proc(), idContentKey, &clear[0], clear.size()) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("AesKeyCheck failed"); + return SEC_RESULT_FAILURE; + } + } else { + if (macCheck(ctx.proc(), SEC_MACALGORITHM_HMAC_SHA256, idContentKey, &clear[0], clear.size()) != + SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("MacCheck failed"); + return SEC_RESULT_FAILURE; + } + } + + return SEC_RESULT_SUCCESS; +} diff --git a/test/main/cpp/wrapped.h b/test/main/cpp/wrapped.h new file mode 100644 index 0000000..01b565c --- /dev/null +++ b/test/main/cpp/wrapped.h @@ -0,0 +1,60 @@ +/** + * Copyright 2020 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef WRAPPED_H +#define WRAPPED_H + +#include "sec_security.h" +#include "test_creds.h" + +Sec_Result testWrappedCipherSingleRsaAes(TestKey key, TestKc kc, Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg, + WrappedKeyFormatVersion wkfv); + +Sec_Result testWrappedCipherSingleRsaAesAes(TestKey key, TestKc kc, Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg, + Sec_KeyType aesType, Sec_CipherAlgorithm symAlg, WrappedKeyFormatVersion wkfv); + +Sec_Result testWrappedCipherSingleRsaAesRsaAesAes(TestKey key, TestKc kc, Sec_KeyType rsaType, + Sec_CipherAlgorithm asymAlg, Sec_KeyType aesType, Sec_CipherAlgorithm symAlg, Sec_CipherAlgorithm ckSymAlg, + WrappedKeyFormatVersion wkfv); + +Sec_Result testWrappedCipherSingleEcAes(TestKey key, TestKc kc, Sec_CipherAlgorithm asymAlg, + WrappedKeyFormatVersion wkfv); + +Sec_Result testWrappedCipherSingleEcAesAes(TestKey key, TestKc kc, Sec_KeyType aesType, Sec_CipherAlgorithm asymAlg, + Sec_CipherAlgorithm symAlg, WrappedKeyFormatVersion wkfv); + +Sec_Result testExportWrappedRsaAesAes(TestKey key, TestKc kc, Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg, + Sec_KeyType aesType, Sec_CipherAlgorithm symAlg, WrappedKeyFormatVersion wkfv); + +Sec_Result testWrappedKDFCMACAES128(SEC_OBJECTID idDerived, SEC_OBJECTID idBase, SEC_OBJECTID idWrapped, + Sec_KeyType keyType, SEC_BYTE counter, uint32_t L); + +Sec_Result testExportedKDFCMACAES128(SEC_OBJECTID idDerived, SEC_OBJECTID idBase, SEC_OBJECTID idWrapped, + Sec_KeyType keyType, SEC_BYTE counter, uint32_t L); + +Sec_Result testExportWrappedEccAesAes(TestKey key, TestKc kc, Sec_CipherAlgorithm asymAlg, Sec_KeyType aesType, + Sec_CipherAlgorithm symAlg, WrappedKeyFormatVersion wkfv); + +Sec_Result testExportWrappedEccAes(TestKey key, TestKc kc, Sec_CipherAlgorithm asymAlg, WrappedKeyFormatVersion wkfv); + +Sec_Result testExportWrappedGeneratedEccAesAes(TestKey key, Sec_CipherAlgorithm asymAlg, Sec_KeyType aesType, + Sec_CipherAlgorithm symAlg, WrappedKeyFormatVersion wkfv); + +Sec_Result testExportWrappedGeneratedEccAes(TestKey key, Sec_CipherAlgorithm asymAlg, WrappedKeyFormatVersion wkfv); + +#endif // WRAPPED_H diff --git a/test/main/headers/test_creds.h b/test/main/headers/test_creds.h new file mode 100644 index 0000000..ccd5f28 --- /dev/null +++ b/test/main/headers/test_creds.h @@ -0,0 +1,161 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef TEST_CREDS_H +#define TEST_CREDS_H + +#include "sec_security.h" +#include +#include +#include +#include + +#define TESTKC_CONDITIONAL TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC) ? TESTKC_SOC : TESTKC_RAW + +enum TestKey { + TESTKEY_AES128 = 0, + TESTKEY_AES256, + TESTKEY_HMAC128, + TESTKEY_HMAC160, + TESTKEY_HMAC256, + TESTKEY_RSA1024_SGN_PRIV, + TESTKEY_RSA1024_SGN_PUB, + TESTKEY_RSA1024_ENC_PRIV, + TESTKEY_RSA1024_ENC_PUB, + TESTKEY_RSA1024_KEK_PRIV, + TESTKEY_RSA1024_KEK_PUB, + TESTKEY_RSA2048_SGN_PRIV, + TESTKEY_RSA2048_SGN_PUB, + TESTKEY_RSA2048_ENC_PRIV, + TESTKEY_RSA2048_ENC_PUB, + TESTKEY_RSA2048_KEK_PRIV, + TESTKEY_RSA2048_KEK_PUB, + TESTKEY_EC_PRIV, + TESTKEY_EC_PUB, + TESTKEY_NUM +}; + +enum TestKc { + TESTKC_RAW, + TESTKC_STORE, + TESTKC_SOC, + TESTKC_GENERATED, + TESTKC_EXPORTED, + TESTKC_NUM +}; + +enum TestCert { + TESTCERT_RSA1024 = 0, + TESTCERT_RSA2048, + TESTCERT_EC, + TESTCERT_NUM +}; + +enum WrappedKeyFormatVersion { + WKFV_V2 = 2, //with offset field + WKFV_V3 //with embedded wrappedKey +}; + +enum Capability { + CAPABILITY_AES256 = 0, + CAPABILITY_HMAC_IN_HW, + CAPABILITY_CMAC_IN_HW, + CAPABILITY_DIGEST_OVER_HWKEY, + CAPABILITY_HMAC_OVER_HWKEY, + CAPABILITY_CMAC_OVER_HWKEY, + CAPABILITY_HKDF_CMAC, + CAPABILITY_EXTRACT_RSA_PUB, + CAPABILITY_WRAPPED_KEY_FORMAT_V3, //nested key containers + CAPABILITY_RSA_AES_M2M, + CAPABILITY_CLEAR_JTYPE_WRAPPING, // SOC only + CAPABILITY_SVP, + CAPABILITY_LOAD_SYM_SOC_KC, + CAPABILITY_EXPORT_RSA, + CAPABILITY_RSA_1024, + CAPABILITY_RSA_AESCBC_AES, + CAPABILITY_RSA_AESCTR_AES, + CAPABILITY_RSA_AESCTR_RSA, + CAPABILITY_NUM +}; + +class TestCtx; + +class ProvKey { +public: + ProvKey() { + } + + ProvKey(std::vector _key, Sec_KeyContainer _kc) { + key = _key; + kc = _kc; + } + std::vector key; + Sec_KeyContainer kc; +}; + +class ProvCert { +public: + ProvCert(std::vector _cert, Sec_CertificateContainer _cc) { + cert = _cert; + cc = _cc; + } + std::vector cert; + Sec_CertificateContainer cc; +}; + +class TestCreds { +public: + static ProvKey* getKey(TestKey key, TestKc kc, SEC_OBJECTID id); + static Sec_KeyType getKeyType(TestKey key); + static ProvCert* getCert(TestCert cert); + + static std::vector asOpenSslAes(TestKey key); + static RSA* asOpenSslRsa(TestKey key); + static EC_KEY* asOpenSslEcKey(TestKey key); + static EVP_PKEY* asOpenSslEvpPkey(TestKey key); + + //following stubs should be implemented by the SOC vendors in test_creds_soc.cpp + static ProvKey* getSocKey(TestKey key, SEC_OBJECTID id); + static Sec_Result preprovisionSoc(TestCtx* testCtx); + static SEC_BOOL supports(Capability cap); + static void init(); + static void shutdown(); + + static std::vector getWrappedContentKeyChainRsaAes(TestKey contentKey, TestKc kc, SEC_OBJECTID id, + Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg); + static std::vector getWrappedContentKeyChainRsaAesAes(TestKey contentKey, TestKc kc, SEC_OBJECTID id, + Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg, Sec_KeyType aesType, Sec_CipherAlgorithm symAlg); + static std::vector getWrappedContentKeyChainRsaAesRsaAesAes(TestKey contentKey, TestKc kc, SEC_OBJECTID id, + Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg, Sec_KeyType aesType, Sec_CipherAlgorithm symAlg, + Sec_CipherAlgorithm ckSymAlg); + static std::vector getWrappedContentKeyChainEcAes(TestKey contentKey, TestKc kc, SEC_OBJECTID id, + Sec_CipherAlgorithm asymAlg); + static std::vector getWrappedContentKeyChainEcAesAes(TestKey contentKey, TestKc kc, SEC_OBJECTID base_id, + Sec_KeyType aesType, Sec_CipherAlgorithm asymAlg, Sec_CipherAlgorithm symAlg); + + static ProvKey* wrapAesWithAes(const SEC_BYTE* clear, Sec_KeyType type, const SEC_BYTE* wrapping, + Sec_KeyType wrappingType, SEC_OBJECTID wrappingId, Sec_CipherAlgorithm symAlg); + static ProvKey* wrapAesWithEc(const SEC_BYTE* clear, Sec_KeyType type, EC_KEY* ec_key, SEC_OBJECTID wrappingId, + Sec_CipherAlgorithm asymAlg); + static ProvKey* wrapAesWithRsa(const SEC_BYTE* data, Sec_KeyType type, RSA* rsa_key, SEC_OBJECTID wrappingId, + Sec_CipherAlgorithm asymAlg); + static ProvKey* wrapRsaWithAes(RSA* rsa, const SEC_BYTE* wrapping, Sec_KeyType wrappingType, + SEC_OBJECTID wrappingId, Sec_CipherAlgorithm symAlg); +}; + +#endif // TEST_CREDS_H diff --git a/test/main/headers/test_ctx.h b/test/main/headers/test_ctx.h new file mode 100644 index 0000000..ff65798 --- /dev/null +++ b/test/main/headers/test_ctx.h @@ -0,0 +1,192 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef TEST_CTX_H +#define TEST_CTX_H + +#include "sec_security.h" // NOLINT +#include "test_creds.h" +#include +#include +#include +#include +#include + +//#define ENABLE_FULL_LOGS + +#ifdef ENABLE_FULL_LOGS +#define DELAYED_LOG 0 +#else +#define DELAYED_LOG 1 +#endif + +enum TestState { + TESTRESULT_SUCCEEDED = 0, + TESTRESULT_FAILED, + TESTRESULT_SKIPPED, + TESTRESULT_NUM +}; + +#define SEC_PRINT_HEX(name, ptr, size) \ + SEC_PRINT("%s[%d]: ", name, size); \ + Sec_PrintHex(ptr, size); \ + SEC_PRINT("\n"); +#define RUN_TEST(suite, function) \ + { \ + int testIdx = (suite)->addTest(#function); \ +\ + if ((suite)->shouldRun(testIdx)) { \ + if (DELAYED_LOG) { \ + Logger::init(); \ + } \ + SEC_PRINT("\n"); \ + SEC_PRINT("%d: " #function " STARTING\n", testIdx); \ + Sec_Result result = function; \ + std::string output = DELAYED_LOG ? Logger::output() : std::string(); \ + if (DELAYED_LOG) { \ + Logger::shutdown(); \ + } \ + if (res == SEC_RESULT_SUCCESS) { \ + SEC_PRINT("%d: " #function " SUCCEEDED\n", testIdx); \ + (suite)->setTestState(testIdx, TESTRESULT_SUCCEEDED); \ + } else { \ + SEC_PRINT(output.c_str()); \ + SEC_PRINT("%d: " #function " FAILED\n\n", testIdx); \ + (suite)->setTestState(testIdx, TESTRESULT_FAILED); \ + } \ + } else { \ + (suite)->setTestState(testIdx, TESTRESULT_SKIPPED); \ + if ((suite)->shouldPrint(testIdx)) { \ + SEC_PRINT("%d: " #function "\n", testIdx); \ + } \ + } \ + } + +class SuiteCtx { + typedef std::pair TestEntry; + +public: + SuiteCtx() = default; + ~SuiteCtx() = default; + + void setRunParams(const std::vector& runParams) { + runParams_ = runParams; + } + bool shouldRun(int id) const { + return runParams_.empty() || std::find(runParams_.begin(), runParams_.end(), id) != runParams_.end(); + } + bool shouldPrint(int id) const { + return shouldRun(id) || (runParams_.size() == 1 && runParams_[0] <= 0); + } + size_t addTest(const char* name) { + tests_.emplace_back(name, TESTRESULT_NUM); + return tests_.size(); + } + void setTestState(int id, TestState state) { + tests_[id - 1].second = state; + } + TestEntry getTestEntry(int id) const { + return tests_[id - 1]; + } + std::vector getFailed() const; + std::vector getSucceeded() const; + std::vector getSkipped() const; + std::vector getAttempted() const; + std::vector getAll() const; + +private: + std::vector tests_; + std::vector runParams_; +}; + +class TestCtx { +public: + TestCtx(); + ~TestCtx(); + + Sec_Result init(const char* global_dir = "/tmp/sec_api_test_global", const char* app_dir = "/tmp/sec_api_test_app"); + + Sec_KeyHandle* provisionKey(SEC_OBJECTID id, Sec_StorageLoc location, TestKey key, TestKc, + bool softWrap = SEC_FALSE); + Sec_KeyHandle* provisionKey(SEC_OBJECTID id, Sec_StorageLoc location, const SEC_BYTE* data, SEC_SIZE len, + Sec_KeyContainer kc, bool softWrap = SEC_FALSE); + Sec_KeyHandle* getKey(SEC_OBJECTID id); + void releaseKey(Sec_KeyHandle* keyHandle); + void deleteKey(SEC_OBJECTID id); + + Sec_CertificateHandle* provisionCert(SEC_OBJECTID id, Sec_StorageLoc location, TestCert cert); + Sec_CertificateHandle* getCert(SEC_OBJECTID id); + void releaseCert(Sec_CertificateHandle* certificateHandle); + void deleteCert(SEC_OBJECTID id); + + Sec_BundleHandle* provisionBundle(SEC_OBJECTID id, Sec_StorageLoc location, const std::vector& bundle); + Sec_BundleHandle* getBundle(SEC_OBJECTID id); + void releaseBundle(Sec_BundleHandle* bundleHandle); + void deleteBundle(SEC_OBJECTID id); + + Sec_MacHandle* acquireMac(Sec_MacAlgorithm algorithm, Sec_KeyHandle* keyHandle); + Sec_Result releaseMac(Sec_MacHandle* macHandle, SEC_BYTE* macBuffer, SEC_SIZE* macSize); + void releaseMac(Sec_MacHandle* macHandle); + + Sec_CipherHandle* acquireCipher(Sec_CipherAlgorithm algorithm, Sec_CipherMode mode, Sec_KeyHandle* keyHandle, + SEC_BYTE* iv); + void releaseCipher(Sec_CipherHandle* cipherHandle); + + Sec_SignatureHandle* signatureHandleacquireSignature(Sec_SignatureAlgorithm algorithm, Sec_SignatureMode mode, + Sec_KeyHandle* keyHandle); void releaseSignature(Sec_SignatureHandle* signatureHandlesig); + + Sec_DigestHandle* acquireDigest(Sec_DigestAlgorithm algorithm); + Sec_Result releaseDigest(Sec_DigestHandle* digestHandle, SEC_BYTE* digestOutput, SEC_SIZE* digestSize); + void releaseDigest(Sec_DigestHandle* digestHandle); + + Sec_RandomHandle* acquireRandom(Sec_RandomAlgorithm algorithm); + void releaseRandom(Sec_RandomHandle* randomHandle); + + Sec_ProcessorHandle* proc() { + return proc_; + } + + //utils + static void printHex(const char* label, const std::vector& data); + static std::vector random(SEC_SIZE len); + static std::vector coalesceInputs(const std::vector>& inputs); + static SEC_SIZE coalesceInputSizes(const std::vector& inputSizes); + +private: + Sec_ProcessorHandle* proc_; + std::list provisionedKeys_; + std::list keys_; + std::list provisionedCerts_; + std::list certs_; + std::list provisionedBundles_; + std::list bundles_; + std::list macs_; + std::list ciphers_; + std::list sigs_; + std::list digests_; + std::list randoms_; +}; + +class Logger { +public: + static void init(); + static void shutdown(); + static const char* output(); +}; + +#endif // TEST_CTX_H diff --git a/test/openssl/headers/sa_soc_key_container.h b/test/openssl/headers/sa_soc_key_container.h new file mode 100644 index 0000000..9c53688 --- /dev/null +++ b/test/openssl/headers/sa_soc_key_container.h @@ -0,0 +1,29 @@ +/** + * Copyright 2020-2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef SA_SOC_KEY_CONTAINER_H +#define SA_SOC_KEY_CONTAINER_H + +#include +#include +#include + +std::vector generate_sa_soc_key_container(std::vector& key_clear, std::string& key_type, + std::vector& tag); + +#endif diff --git a/test/openssl/headers/test_creds.h b/test/openssl/headers/test_creds.h new file mode 100644 index 0000000..c9d23ba --- /dev/null +++ b/test/openssl/headers/test_creds.h @@ -0,0 +1,164 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef TEST_CREDS_H +#define TEST_CREDS_H + +#include "sec_security.h" +#include +#include +#include +#include +#include +#include + +#define TESTKC_CONDITIONAL TestCreds::supports(CAPABILITY_LOAD_SYM_SOC_KC) ? TESTKC_SOC : TESTKC_RAW + +enum TestKey { + TESTKEY_AES128 = 0, + TESTKEY_AES256, + TESTKEY_HMAC128, + TESTKEY_HMAC160, + TESTKEY_HMAC256, + TESTKEY_RSA1024_SGN_PRIV, + TESTKEY_RSA1024_SGN_PUB, + TESTKEY_RSA1024_ENC_PRIV, + TESTKEY_RSA1024_ENC_PUB, + TESTKEY_RSA1024_KEK_PRIV, + TESTKEY_RSA1024_KEK_PUB, + TESTKEY_RSA2048_SGN_PRIV, + TESTKEY_RSA2048_SGN_PUB, + TESTKEY_RSA2048_ENC_PRIV, + TESTKEY_RSA2048_ENC_PUB, + TESTKEY_RSA2048_KEK_PRIV, + TESTKEY_RSA2048_KEK_PUB, + TESTKEY_EC_PRIV, + TESTKEY_EC_PUB, + TESTKEY_ROOT, + TESTKEY_NUM +}; + +enum TestKc { + TESTKC_RAW, + TESTKC_STORE, + TESTKC_SOC, + TESTKC_GENERATED, + TESTKC_EXPORTED, + TESTKC_NUM +}; + +enum TestCert { + TESTCERT_RSA1024 = 0, + TESTCERT_RSA2048, + TESTCERT_EC, + TESTCERT_NUM +}; + +enum WrappedKeyFormatVersion { + WKFV_V2 = 2, //with offset field + WKFV_V3 //with embedded wrappedKey +}; + +enum Capability { + CAPABILITY_AES256 = 0, + CAPABILITY_HMAC_IN_HW, + CAPABILITY_CMAC_IN_HW, + CAPABILITY_DIGEST_OVER_HWKEY, + CAPABILITY_HMAC_OVER_HWKEY, + CAPABILITY_CMAC_OVER_HWKEY, + CAPABILITY_HKDF_CMAC, + CAPABILITY_EXTRACT_RSA_PUB, + CAPABILITY_WRAPPED_KEY_FORMAT_V3, //nested key containers + CAPABILITY_RSA_AES_M2M, + CAPABILITY_CLEAR_JTYPE_WRAPPING, // SOC only + CAPABILITY_SVP, + CAPABILITY_LOAD_SYM_SOC_KC, + CAPABILITY_EXPORT_RSA, + CAPABILITY_RSA_1024, + CAPABILITY_RSA_AESCBC_AES, + CAPABILITY_RSA_AESCTR_AES, + CAPABILITY_RSA_AESCTR_RSA, + CAPABILITY_NUM +}; + +class TestCtx; + +class ProvKey { // NOLINT +public: + ProvKey() = default; // NOLINT + + ProvKey(std::vector _key, Sec_KeyContainer _kc) + : key(std::move(_key)), kc(_kc) { + } + + std::vector key; // NOLINT + Sec_KeyContainer kc; // NOLINT +}; + +class ProvCert { +public: + ProvCert(std::vector _cert, Sec_CertificateContainer _cc) + : cert(std::move(_cert)), cc(_cc) { + } + + std::vector cert; // NOLINT + Sec_CertificateContainer cc; // NOLINT +}; + +class TestCreds { +public: + static ProvKey* getKey(TestKey key, TestKc kc, SEC_OBJECTID id); + static Sec_KeyType getKeyType(TestKey key); + static ProvCert* getCert(TestCert cert); + + static std::vector asOpenSslAes(TestKey key); + static RSA* asOpenSslRsa(TestKey key); + static EC_KEY* asOpenSslEcKey(TestKey key); + static EVP_PKEY* asOpenSslEvpPkey(TestKey key); + + //following stubs should be implemented by the SOC vendors in test_creds_soc.cpp + static ProvKey* getSocKey(TestKey key, SEC_OBJECTID id); + static Sec_Result preprovisionSoc(TestCtx* testCtx); + static bool supports(Capability cap); + static void init(); + static void shutdown(); + + static std::vector getWrappedContentKeyChainRsaAes(TestKey contentKey, TestKc kc, SEC_OBJECTID id, + Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg); + static std::vector getWrappedContentKeyChainRsaAesAes(TestKey contentKey, TestKc kc, SEC_OBJECTID id, + Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg, Sec_KeyType aesType, Sec_CipherAlgorithm symAlg); + static std::vector getWrappedContentKeyChainRsaAesRsaAesAes(TestKey contentKey, TestKc kc, SEC_OBJECTID id, + Sec_KeyType rsaType, Sec_CipherAlgorithm asymAlg, Sec_KeyType aesType, Sec_CipherAlgorithm symAlg, + Sec_CipherAlgorithm ckSymAlg); + static std::vector getWrappedContentKeyChainEcAes(TestKey contentKey, TestKc kc, SEC_OBJECTID id, + Sec_CipherAlgorithm asymAlg); + static std::vector getWrappedContentKeyChainEcAesAes(TestKey contentKey, TestKc kc, SEC_OBJECTID base_id, + Sec_KeyType aesType, Sec_CipherAlgorithm asymAlg, Sec_CipherAlgorithm symAlg); + + static ProvKey* wrapAesWithAes(const SEC_BYTE* clear, Sec_KeyType type, const SEC_BYTE* wrapping, + Sec_KeyType wrappingType, SEC_OBJECTID wrappingId, Sec_CipherAlgorithm symAlg); + static ProvKey* wrapAesWithEc(const SEC_BYTE* clear, Sec_KeyType type, EC_KEY* ec_key, SEC_OBJECTID wrappingId, + Sec_CipherAlgorithm asymAlg); + static ProvKey* wrapAesWithRsa(const SEC_BYTE* data, Sec_KeyType type, RSA* rsa_key, SEC_OBJECTID wrappingId, + Sec_CipherAlgorithm asymAlg); + static ProvKey* wrapRsaWithAes(RSA* rsa, const SEC_BYTE* wrapping, Sec_KeyType wrappingType, + SEC_OBJECTID wrappingId, Sec_CipherAlgorithm symAlg); + static std::string b64_encode(const void* in, size_t in_length); +}; + +#endif // TEST_CREDS_H diff --git a/test/openssl/headers/test_ctx.h b/test/openssl/headers/test_ctx.h new file mode 100644 index 0000000..b3bd4ef --- /dev/null +++ b/test/openssl/headers/test_ctx.h @@ -0,0 +1,192 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef TEST_CTX_H +#define TEST_CTX_H + +#include "test_creds.h" +#include +#include +#include +#include +#include + +//#define ENABLE_FULL_LOGS + +#ifdef ENABLE_FULL_LOGS +#define DELAYED_LOG 0 +#else +#define DELAYED_LOG 1 +#endif + +enum TestState { + TESTRESULT_SUCCEEDED = 0, + TESTRESULT_FAILED, + TESTRESULT_SKIPPED, + TESTRESULT_NUM +}; + +#define SEC_PRINT_HEX(name, ptr, size) \ + SEC_PRINT("%s[%d]: ", name, size); \ + Sec_PrintHex(ptr, size); \ + SEC_PRINT("\n"); +#define RUN_TEST(suite, function) \ + { \ + int testIdx = (suite)->addTest(#function); \ +\ + if ((suite)->shouldRun(testIdx)) { \ + if (DELAYED_LOG) { \ + Logger::init(); \ + } \ + SEC_PRINT("\n"); \ + SEC_PRINT("%d: " #function " STARTING\n", testIdx); \ + Sec_Result result = function; \ + std::string output = DELAYED_LOG ? Logger::output() : std::string(); \ + if (DELAYED_LOG) { \ + Logger::shutdown(); \ + } \ + if (result == SEC_RESULT_SUCCESS) { \ + SEC_PRINT("%d: " #function " SUCCEEDED\n", testIdx); \ + (suite)->setTestState(testIdx, TESTRESULT_SUCCEEDED); \ + } else { \ + SEC_PRINT(output.c_str()); \ + SEC_PRINT("%d: " #function " FAILED\n\n", testIdx); \ + (suite)->setTestState(testIdx, TESTRESULT_FAILED); \ + } \ + } else { \ + (suite)->setTestState(testIdx, TESTRESULT_SKIPPED); \ + if ((suite)->shouldPrint(testIdx)) { \ + SEC_PRINT("%d: " #function "\n", testIdx); \ + } \ + } \ + } + +class SuiteCtx { + typedef std::pair TestEntry; + +public: + SuiteCtx() = default; + ~SuiteCtx() = default; + + void setRunParams(const std::vector& runParams) { + runParams_ = runParams; + } + bool shouldRun(int id) const { + return runParams_.empty() || std::find(runParams_.begin(), runParams_.end(), id) != runParams_.end(); + } + bool shouldPrint(int id) const { + return shouldRun(id) || (runParams_.size() == 1 && runParams_[0] <= 0); + } + size_t addTest(const char* name) { + tests_.emplace_back(name, TESTRESULT_NUM); + return tests_.size(); + } + void setTestState(int id, TestState state) { + tests_[id - 1].second = state; + } + TestEntry getTestEntry(int id) const { + return tests_[id - 1]; + } + std::vector getFailed() const; + std::vector getSucceeded() const; + std::vector getSkipped() const; + std::vector getAttempted() const; + std::vector getAll() const; + +private: + std::vector tests_; + std::vector runParams_; +}; + +class TestCtx { +public: + TestCtx() = default; + ~TestCtx(); + + Sec_Result init(const char* global_dir = "/tmp/sec_api_test_global", const char* app_dir = "/tmp/sec_api_test_app"); + + Sec_KeyHandle* provisionKey(SEC_OBJECTID id, Sec_StorageLoc location, TestKey key, TestKc kc, + bool softWrap = false); + Sec_KeyHandle* provisionKey(SEC_OBJECTID id, Sec_StorageLoc location, const SEC_BYTE* data, SEC_SIZE len, + Sec_KeyContainer kc, bool softWrap = false); + Sec_KeyHandle* getKey(SEC_OBJECTID id); + void releaseKey(Sec_KeyHandle* keyHandle); + void deleteKey(SEC_OBJECTID id); + + Sec_CertificateHandle* provisionCert(SEC_OBJECTID id, Sec_StorageLoc location, TestCert cert); + Sec_CertificateHandle* getCert(SEC_OBJECTID id); + void releaseCert(Sec_CertificateHandle* certificateHandle); + void deleteCert(SEC_OBJECTID id); + + Sec_BundleHandle* provisionBundle(SEC_OBJECTID id, Sec_StorageLoc location, const std::vector& bundle); + Sec_BundleHandle* getBundle(SEC_OBJECTID id); + void releaseBundle(Sec_BundleHandle* bundleHandle); + void deleteBundle(SEC_OBJECTID id); + + Sec_MacHandle* acquireMac(Sec_MacAlgorithm algorithm, Sec_KeyHandle* keyHandle); + Sec_Result releaseMac(Sec_MacHandle* macHandle, SEC_BYTE* macBuffer, SEC_SIZE* macSize); + void releaseMac(Sec_MacHandle* macHandle); + + Sec_CipherHandle* acquireCipher(Sec_CipherAlgorithm algorithm, Sec_CipherMode mode, Sec_KeyHandle* keyHandle, + SEC_BYTE* iv); + void releaseCipher(Sec_CipherHandle* cipherHandle); + + Sec_SignatureHandle* acquireSignature(Sec_SignatureAlgorithm algorithm, Sec_SignatureMode mode, + Sec_KeyHandle* keyHandle); + void releaseSignature(Sec_SignatureHandle* signatureHandle); + + Sec_DigestHandle* acquireDigest(Sec_DigestAlgorithm algorithm); + Sec_Result releaseDigest(Sec_DigestHandle* digestHandle, SEC_BYTE* digestOutput, SEC_SIZE* digestSize); + void releaseDigest(Sec_DigestHandle* digestHandle); + + Sec_RandomHandle* acquireRandom(Sec_RandomAlgorithm algorithm); + void releaseRandom(Sec_RandomHandle* randomHandle); + + Sec_ProcessorHandle* proc() { + return proc_; + } + + //utils + static void printHex(const char* label, const std::vector& data); + static std::vector random(SEC_SIZE len); + static std::vector coalesceInputs(const std::vector>& inputs); + static SEC_SIZE coalesceInputSizes(const std::vector& inputSizes); + +private: + Sec_ProcessorHandle* proc_ = nullptr; + std::list provisionedKeys_; + std::list keys_; + std::list provisionedCerts_; + std::list certs_; + std::list provisionedBundles_; + std::list bundles_; + std::list macs_; + std::list ciphers_; + std::list sigs_; + std::list digests_; + std::list randoms_; +}; + +class Logger { +public: + static void init(); + static void shutdown(); + static const char* output(); +}; + +#endif // TEST_CTX_H diff --git a/test/openssl/src/sa_soc_key_container.cpp b/test/openssl/src/sa_soc_key_container.cpp new file mode 100644 index 0000000..cda1235 --- /dev/null +++ b/test/openssl/src/sa_soc_key_container.cpp @@ -0,0 +1,189 @@ +/** + * Copyright 2020-2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sa_soc_key_container.h" // NOLINT +#include "cipher.h" +#include +#include + +static std::vector ENTITLED_TA_IDS = { + "157f768f-bad0-470b-929d-0d7dec29d220", + "157f768f-bad0-470b-929d-0d7dec29d221", + "157f768f-bad0-470b-929d-0d7dec29d222", + "157f768f-bad0-470b-929d-0d7dec29d223", + "157f768f-bad0-470b-929d-0d7dec29d224", + "157f768f-bad0-470b-929d-0d7dec29d225", + "157f768f-bad0-470b-929d-0d7dec29d226", + "00000000-0000-0000-0000-000000000001"}; + +static std::vector TEST_C1 = { + 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef}; + +static std::vector TEST_C2 = { + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}; + +static std::vector TEST_C3 = { + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33}; + +static std::vector TEST_IV = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC}; + +static std::vector derive_container_encryption_key( + std::vector& c1, + std::vector& c2, + std::vector& c3) { + + auto stage1 = opensslAesEcb(TESTKEY_ROOT, SEC_CIPHERMODE_DECRYPT, SEC_FALSE, nullptr, c1); + if (stage1.empty() || stage1.size() != 16) + return {}; + + auto stage2 = opensslAesEcb(stage1, SEC_CIPHERMODE_DECRYPT, SEC_FALSE, nullptr, c2); + if (stage2.empty() || stage2.size() != 16) + return {}; + + auto stage3 = opensslAesEcb(stage2, SEC_CIPHERMODE_DECRYPT, SEC_FALSE, nullptr, c3); + if (stage3.empty() || stage3.size() != 16) + return {}; + + return stage3; +} + +std::string generate_encrypted_key( + uint8_t container_version, + std::string& key_type, + std::vector& key, + std::vector& iv, + uint8_t key_usage, + uint8_t decrypted_key_usage, + std::vector& entitled_ta_ids, + std::vector& c1, + std::vector& c2, + std::vector& c3, + std::vector& tag) { + + std::string alg = "A128GCM"; + std::vector aad; + aad.insert(aad.end(), alg.begin(), alg.end()); + aad.insert(aad.end(), container_version); + aad.insert(aad.end(), key_type.begin(), key_type.end()); + aad.insert(aad.end(), key_usage); + if (container_version >= 3 && key_usage == 2) + aad.insert(aad.end(), decrypted_key_usage); + + aad.insert(aad.end(), iv.begin(), iv.end()); + aad.insert(aad.end(), c1.begin(), c1.end()); + aad.insert(aad.end(), c2.begin(), c2.end()); + aad.insert(aad.end(), c3.begin(), c3.end()); + for (auto& ta_id : entitled_ta_ids) { + aad.insert(aad.end(), ta_id.begin(), ta_id.end()); + } + + tag.resize(16); + + auto derived_key = derive_container_encryption_key(c1, c2, c3); + if (derived_key.empty()) + return ""; + + std::vector encrypted_key = opensslAesGcm(derived_key, SEC_CIPHERMODE_ENCRYPT, iv.data(), aad.data(), + aad.size(), tag.data(), tag.size(), key); + + if (encrypted_key.empty()) + return ""; + + return TestCreds::b64_encode(encrypted_key.data(), encrypted_key.size()); +} + +std::string generate_header() { + std::string hdr = R"({"alg": "A128GCM"})"; + return TestCreds::b64_encode(hdr.data(), hdr.size()); +} + +std::string generate_payload( + uint8_t container_version, + std::string& key_type, + std::vector& key, + std::vector& iv, + uint8_t key_usage, + uint8_t decrypted_key_usage, + std::vector& entitled_ta_ids, + std::vector& c1, + std::vector& c2, + std::vector& c3, + std::vector& tag) { + + std::ostringstream oss; + + oss << R"({"containerVersion": )" << static_cast(container_version); + if (!key_type.empty()) + oss << R"(, "keyType": ")" << key_type << "\""; + + std::string encrypted_key = generate_encrypted_key(container_version, key_type, key, iv, key_usage, + decrypted_key_usage, entitled_ta_ids, c1, c2, c3, tag); + if (encrypted_key.empty()) + return ""; + + oss << R"(, "encryptedKey": ")" << encrypted_key << "\""; + + if (!iv.empty()) + oss << R"(, "iv": ")" << TestCreds::b64_encode(iv.data(), iv.size()) << "\""; + + oss << R"(, "keyUsage": )" << static_cast(key_usage); + + if (container_version >= 3 && key_usage == 2) + oss << R"(, "decryptedKeyUsage": )" << static_cast(decrypted_key_usage); + + if (!entitled_ta_ids.empty()) { + oss << R"(, "entitledTaIds": [)"; + for (size_t i = 0; i < entitled_ta_ids.size(); i++) { + if (i > 0) + oss << ", "; + + oss << "\"" << entitled_ta_ids[i] << "\""; + } + + oss << "]"; + } + + if (!c1.empty()) + oss << R"(, "c1": ")" << TestCreds::b64_encode(c1.data(), c1.size()) << "\""; + + if (!c2.empty()) + oss << R"(, "c2": ")" << TestCreds::b64_encode(c2.data(), c2.size()) << "\""; + + if (!c1.empty()) + oss << R"(, "c3": ")" << TestCreds::b64_encode(c3.data(), c3.size()) << "\""; + + oss << "}"; + + return TestCreds::b64_encode(oss.str().data(), oss.str().size()); +} + +std::vector generate_sa_soc_key_container(std::vector& key_clear, + std::string& key_type, + std::vector& tag) { + auto jwt_header = generate_header(); + auto jwt_payload = generate_payload(3, key_type, key_clear, TEST_IV, 3, 3, ENTITLED_TA_IDS, TEST_C1, TEST_C2, + TEST_C3, tag); + std::string key_container = jwt_header + "." + jwt_payload + "." + TestCreds::b64_encode(tag.data(), tag.size()); + + auto kc = std::vector(key_container.begin(), key_container.end()); + return kc; +} diff --git a/test/openssl/src/test_creds_soc.cpp b/test/openssl/src/test_creds_soc.cpp new file mode 100644 index 0000000..6a18124 --- /dev/null +++ b/test/openssl/src/test_creds_soc.cpp @@ -0,0 +1,225 @@ +/** + * Copyright 2020-2022 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sa.h" +#include "sec_adapter_utils.h" +#include "test_creds.h" +#include "test_ctx.h" +#include +#include + +#define SEC_OBJECTID_OPENSSL_KPK SEC_OBJECTID_RESERVEDPLATFORM_7 + +static std::vector random(SEC_SIZE len) { + std::vector res; + res.resize(len); + + if (RAND_bytes(&res[0], static_cast(len)) != OPENSSL_SUCCESS) { + SEC_LOG_ERROR("RAND_bytes failed"); + return {}; + } + + return res; +} + +static void rights_set_allow_all(sa_rights* rights, Sec_KeyContainer key_type) { + memset(rights->id, 0, sizeof(rights->id)); + + rights->not_before = 0; + rights->not_on_or_after = UINT64_MAX; + + rights->usage_flags = 0; + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_KEY_EXCHANGE); + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_DERIVE); + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_UNWRAP); + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_ENCRYPT); + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_DECRYPT); + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_SIGN); + rights->usage_flags |= SA_USAGE_OUTPUT_PROTECTIONS_MASK; + SA_USAGE_BIT_SET(rights->usage_flags, SA_USAGE_FLAG_CACHEABLE); + + memset(rights->allowed_tas, 0, sizeof(rights->allowed_tas)); + + const sa_uuid ALL_MATCH = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + memcpy(&rights->allowed_tas[7], &ALL_MATCH, sizeof(sa_uuid)); +} + +std::string TestCreds::b64_encode( + const void* in, + size_t in_length) { + + if (in == nullptr) { + SEC_LOG_ERROR("NULL in"); + throw; + } + + std::shared_ptr b64(BIO_new(BIO_f_base64()), BIO_free_all); + BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL); + + BIO* sink = BIO_new(BIO_s_mem()); + if (sink == nullptr) { + SEC_LOG_ERROR("BIO_new failed"); + throw; + } + + BIO_push(b64.get(), sink); + + if (BIO_write(b64.get(), in, static_cast(in_length)) < 0) { + SEC_LOG_ERROR("BIO_write failed"); + throw; + } + + if (BIO_flush(b64.get()) < 0) { + SEC_LOG_ERROR("BIO_flush failed"); + throw; + } + + const char* encoded; + const uint64_t len = BIO_get_mem_data(sink, &encoded); // NOLINT + return {encoded, len}; +} + +ProvKey* TestCreds::getSocKey(TestKey key, SEC_OBJECTID id) { + ProvKey* pk = TestCreds::getKey(key, TESTKC_RAW, id); + if (pk == nullptr) { + return nullptr; + } + + std::vector key_clear; + + std::string key_type; + switch (pk->kc) { + case SEC_KEYCONTAINER_DER_RSA_1024: + case SEC_KEYCONTAINER_DER_RSA_2048: { + RSA* rsa = SecUtils_RSAFromDERPriv(&pk->key[0], pk->key.size()); + if (rsa == nullptr) { + SEC_LOG_ERROR("SecUtils_RSAFromDERPriv failed "); + delete pk; + return nullptr; + } + + SEC_SIZE written; + key_clear.resize(SEC_KEYCONTAINER_MAX_LEN); + if (SecUtils_RSAToDERPrivKeyInfo(rsa, &key_clear[0], key_clear.size(), &written) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_RSAToDERPrivKeyInfo failed"); + SEC_RSA_FREE(rsa); + delete pk; + return nullptr; + } + + SEC_RSA_FREE(rsa); + + key_clear.resize(written); + key_type = pk->kc == SEC_KEYCONTAINER_DER_RSA_1024 ? "RSA-1024" : "RSA-2048"; + } break; + + case SEC_KEYCONTAINER_DER_ECC_NISTP256: { + EC_KEY* ec_key = SecUtils_ECCFromDERPriv(&pk->key[0], pk->key.size()); + if (ec_key == nullptr) { + SEC_LOG_ERROR("SecUtils_ECCFromDERPriv failed "); + delete pk; + return nullptr; + } + + Sec_ECCRawPrivateKey ec_bin; + if (SecUtils_ECCToPrivBinary(ec_key, &ec_bin) != SEC_RESULT_SUCCESS) { + SEC_LOG_ERROR("SecUtils_ECCToPrivBinary failed"); + SEC_ECC_FREE(ec_key); + delete pk; + return nullptr; + } + + SEC_ECC_FREE(ec_key); + + key_clear.resize(SEC_ECC_NISTP256_KEY_LEN); + memcpy(&key_clear[0], &ec_bin.prv[0], SEC_ECC_NISTP256_KEY_LEN); + key_type = "ECC-P256"; + } break; + + case SEC_KEYCONTAINER_RAW_AES_128: { + key_clear.resize(pk->key.size()); + memcpy(&key_clear[0], &pk->key[0], pk->key.size()); + key_type = "AES-128"; + } break; + + case SEC_KEYCONTAINER_RAW_AES_256: { + key_clear.resize(pk->key.size()); + memcpy(&key_clear[0], &pk->key[0], pk->key.size()); + key_type = "AES-256"; + } break; + + case SEC_KEYCONTAINER_RAW_HMAC_128: { + key_clear.resize(pk->key.size()); + memcpy(&key_clear[0], &pk->key[0], pk->key.size()); + key_type = "HMAC-128"; + } break; + + case SEC_KEYCONTAINER_RAW_HMAC_160: { + key_clear.resize(pk->key.size()); + memcpy(&key_clear[0], &pk->key[0], pk->key.size()); + key_type = "HMAC-160"; + } break; + + case SEC_KEYCONTAINER_RAW_HMAC_256: { + key_clear.resize(pk->key.size()); + memcpy(&key_clear[0], &pk->key[0], pk->key.size()); + key_type = "HMAC-256"; + } break; + + default: + SEC_LOG_ERROR("Unexpected kc encountered"); + delete pk; + return nullptr; + } + + std::vector tag; + std::vector kc = generate_sa_soc_key_container(key_clear, key_type, tag); + + delete pk; + return new ProvKey(kc, SEC_KEYCONTAINER_SOC); +} + +Sec_Result TestCreds::preprovisionSoc(TestCtx* ctx) { + //Here the soc vendors should add code to preprovision any credentials that + //are required for the rest of the system to operate properly. + + //For most platforms this can stay a NOP + + //provision kpk + ctx->provisionKey(SEC_OBJECTID_OPENSSL_KPK, SEC_STORAGELOC_RAM, TESTKEY_AES128, TESTKC_RAW, SEC_TRUE); + + return SEC_RESULT_SUCCESS; +} + +bool TestCreds::supports(Capability cap) { + //return whether a specific capability is supported in the target soc +#ifdef ENABLE_SOC_KEY_TESTS + return cap != CAPABILITY_HKDF_CMAC; +#else + return cap != CAPABILITY_HKDF_CMAC && cap != CAPABILITY_LOAD_SYM_SOC_KC; +#endif +} + +void TestCreds::init() { +} + +void TestCreds::shutdown() { +} diff --git a/test/valgrind.supp b/test/valgrind.supp new file mode 100644 index 0000000..aa0dfda --- /dev/null +++ b/test/valgrind.supp @@ -0,0 +1,70 @@ +{ + object_store_shutdown + Memcheck:Cond + fun:_store_remove + fun:object_store_shutdown + fun:*_store_shutdown + ... +} +{ + object_store_remove + Memcheck:Cond + fun:_store_remove + fun:object_store_remove + fun:*_store_remove +} +{ + object_store_release + Memcheck:Cond + fun:object_store_release + fun:key_store_unwrap + ... +} +{ + gmtsub-malloc + Memcheck:Leak + match-leak-kinds: possible + fun:malloc + ... + fun:gmt_init + fun:__pthread_once_handler + fun:_os_once_callout + fun:pthread_once + fun:gmtsub + fun:SecUtils_Epoch2IsoTime + fun:SecKey_GetProperties + fun:SecCipher_GetInstance + fun:SecCipher_SingleInput + fun:SecCipher_SingleInputId + fun:SecStore_ComputeMacKey + fun:SecStore_StoreDataWithKey + fun:SecStore_StoreData + fun:_Z9testStorehh + fun:_Z11runKeyTestsP8SuiteCtx + fun:testIt + fun:main +} +{ + gmtsub-calloc + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + ... + fun:gmt_init + fun:__pthread_once_handler + fun:_os_once_callout + fun:pthread_once + fun:gmtsub + fun:SecUtils_Epoch2IsoTime + fun:SecKey_GetProperties + fun:SecCipher_GetInstance + fun:SecCipher_SingleInput + fun:SecCipher_SingleInputId + fun:SecStore_ComputeMacKey + fun:SecStore_StoreDataWithKey + fun:SecStore_StoreData + fun:_Z9testStorehh + fun:_Z11runKeyTestsP8SuiteCtx + fun:testIt + fun:main +} \ No newline at end of file