├── .gitignore ├── authenticode-config.cmake ├── SECURITY.md ├── examples ├── CMakeLists.txt └── authenticode_dumper.c ├── dev_scripts.py ├── hex_to_c.py └── dump_pe_signature.py ├── .clang-format ├── tests ├── CMakeLists.txt ├── unit │ ├── countersignature.cpp │ ├── certificate.cpp │ └── helper.cpp └── integration │ ├── test_microsoft.cpp │ └── test_non_microsoft.cpp ├── LICENSE ├── src ├── certificate.h ├── countersignature.h ├── helper.h ├── helper.c ├── structs.c ├── structs.h ├── certificate.c ├── authenticode.c └── countersignature.c ├── CMakeLists.txt ├── .github └── workflows │ └── cmake.yml ├── README.md └── include └── authenticode-parser └── authenticode.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | examples/build 3 | -------------------------------------------------------------------------------- /authenticode-config.cmake: -------------------------------------------------------------------------------- 1 | find_package(OpenSSL 1.1.1 REQUIRED) 2 | include(${CMAKE_CURRENT_LIST_DIR}/authenticode-targets.cmake) 3 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | Please use [this submission form](https://www.nortonlifelock.com/us/en/contact-us/report-a-security-vulnerability/) to report any (potential) security vulnerabilities. Provide as much details as possible. 2 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(authenticode_dumper LANGUAGES C) 4 | 5 | add_executable(authenticode_dumper authenticode_dumper.c) 6 | target_compile_options(authenticode_dumper PRIVATE -Wall) 7 | 8 | find_package(authenticode REQUIRED) 9 | 10 | target_link_libraries(authenticode_dumper 11 | PUBLIC 12 | authenticode 13 | ) 14 | -------------------------------------------------------------------------------- /dev_scripts.py/hex_to_c.py: -------------------------------------------------------------------------------- 1 | """Convert hex string like c7fef94e329bd9b66b281539265f989313356cbd9c345df9e670e9c4b6e0edce to C array init""" 2 | import sys 3 | 4 | 5 | def hex_to_c_array(hex_string: str) -> str: 6 | # Split the hex string into bytes 7 | bytes = [hex_string[i : i + 2] for i in range(0, len(hex_string), 2)] 8 | 9 | # Format the bytes as a C array 10 | c_array = ", ".join("0x" + byte for byte in bytes) 11 | c_array = "uint8_t array[] = {" + c_array + "};" 12 | 13 | return c_array 14 | 15 | 16 | # Use the function 17 | hex_string = sys.argv[1] 18 | print(hex_to_c_array(hex_string)) 19 | -------------------------------------------------------------------------------- /dev_scripts.py/dump_pe_signature.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pefile 3 | 4 | 5 | # Function to dump the signature from a PE file for tests 6 | def dump_signature(path: str): 7 | pe = pefile.PE(path) 8 | security_directory = pe.OPTIONAL_HEADER.DATA_DIRECTORY[ 9 | pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_SECURITY"] 10 | ] 11 | win_certificate = pe.__data__[ 12 | security_directory.VirtualAddress 13 | + 8 : security_directory.VirtualAddress 14 | + security_directory.Size 15 | ] # Extract WIN_CERTIFICATE 16 | with open("dump.pkcs7.der", "wb") as fp: 17 | fp.write(win_certificate) 18 | 19 | 20 | # Use the function 21 | file_path = sys.argv[1] 22 | # To convert to ASCII PEM to use in tests, use 23 | # openssl pkcs7 -inform der -in dump.pkcs7.der -out sig.pem 24 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # clang-format configuration applied to all source files in this project. 2 | # Requires clang-format version 10.0.0 or newer. 3 | --- 4 | Language: Cpp 5 | BasedOnStyle: Llvm 6 | AlignConsecutiveMacros: AcrossComments 7 | AlignAfterOpenBracket: AlwaysBreak 8 | 9 | AllowShortFunctionsOnASingleLine: Inline 10 | AllowShortLambdasOnASingleLine: All 11 | AllowShortIfStatementsOnASingleLine: Never 12 | 13 | BinPackArguments: false 14 | BinPackParameters: false 15 | 16 | BreakBeforeBraces: Linux 17 | BreakStringLiterals: true 18 | 19 | ColumnLimit: 100 20 | ContinuationIndentWidth: 4 21 | DerivePointerAlignment: true 22 | IndentCaseLabels: false 23 | IndentWidth: 4 24 | IndentWrappedFunctionNames: false 25 | MaxEmptyLinesToKeep: 1 26 | PointerAlignment: Left 27 | ReflowComments: true 28 | SortIncludes: true 29 | TabWidth: 4 30 | UseTab: Never 31 | ... 32 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(test) 2 | 3 | set(CMAKE_CXX_STANDARD 11) 4 | 5 | find_package(OpenSSL 1.1.1 REQUIRED) 6 | 7 | include(FetchContent) 8 | FetchContent_Declare( 9 | googletest 10 | GIT_REPOSITORY https://github.com/google/googletest.git 11 | GIT_TAG e2239ee6043f73722e7aa812a459f54a28552929 # release-1.11.0 12 | ) 13 | 14 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 15 | 16 | FetchContent_MakeAvailable(googletest) 17 | 18 | add_executable(tests 19 | integration/test_microsoft.cpp 20 | integration/test_non_microsoft.cpp 21 | unit/countersignature.cpp 22 | unit/certificate.cpp 23 | unit/helper.cpp) 24 | 25 | 26 | target_link_libraries(tests 27 | PRIVATE 28 | gtest_main 29 | authenticode 30 | OpenSSL::Crypto) 31 | 32 | include(GoogleTest) 33 | gtest_add_tests(TARGET tests) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Avast Software 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /tests/unit/countersignature.cpp: -------------------------------------------------------------------------------- 1 | #include "../../src/countersignature.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | TEST(CountersignatureModule, countersignature_array_insert) 8 | { 9 | CountersignatureArray array; 10 | array.counters = nullptr; 11 | array.count = 0; 12 | 13 | Countersignature countersig1 = {}; 14 | 15 | int res = countersignature_array_insert(&array, &countersig1); 16 | EXPECT_EQ(res, 0); 17 | ASSERT_EQ(array.count, 1); 18 | ASSERT_TRUE(array.counters); 19 | EXPECT_EQ(array.counters[0], &countersig1); 20 | 21 | Countersignature countersig2; 22 | 23 | res = countersignature_array_insert(&array, &countersig2); 24 | EXPECT_EQ(res, 0); 25 | ASSERT_EQ(array.count, 2); 26 | ASSERT_TRUE(array.counters); 27 | EXPECT_EQ(array.counters[1], &countersig2); 28 | 29 | free(array.counters); 30 | } 31 | 32 | TEST(CountersignatureModule, countersignature_array_move) 33 | { 34 | CountersignatureArray array1; 35 | array1.counters = nullptr; 36 | array1.count = 0; 37 | 38 | CountersignatureArray array2; 39 | array2.counters = nullptr; 40 | array2.count = 0; 41 | 42 | Countersignature countersig1; 43 | Countersignature countersig2; 44 | 45 | int res = countersignature_array_insert(&array2, &countersig1); 46 | EXPECT_EQ(res, 0); 47 | 48 | res = countersignature_array_insert(&array2, &countersig2); 49 | EXPECT_EQ(res, 0); 50 | 51 | res = countersignature_array_move(&array1, &array2); 52 | EXPECT_EQ(res, 0); 53 | 54 | ASSERT_TRUE(array1.counters); 55 | ASSERT_EQ(array1.count, 2); 56 | 57 | EXPECT_EQ(array1.counters[0], &countersig1); 58 | EXPECT_EQ(array1.counters[1], &countersig2); 59 | 60 | EXPECT_EQ(array2.count, 0); 61 | EXPECT_FALSE(array2.counters); 62 | 63 | free(array1.counters); 64 | } -------------------------------------------------------------------------------- /src/certificate.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 Avast Software 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | #ifndef AUTHENTICODE_PARSER_CERTIFICATE_H 23 | #define AUTHENTICODE_PARSER_CERTIFICATE_H 24 | 25 | #include 26 | 27 | #include 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | Certificate* certificate_new(X509* x509); 34 | Certificate* certificate_copy(Certificate* cert); 35 | void certificate_free(Certificate* cert); 36 | 37 | void parse_x509_certificates(const STACK_OF(X509) * certs, CertificateArray* result); 38 | 39 | CertificateArray* parse_signer_chain(X509* signer_cert, STACK_OF(X509) * certs); 40 | int certificate_array_move(CertificateArray* dst, CertificateArray* src); 41 | int certificate_array_append(CertificateArray* dst, CertificateArray* src); 42 | CertificateArray* certificate_array_new(int certCount); 43 | void certificate_array_free(CertificateArray* arr); 44 | 45 | #ifdef __cplusplus 46 | } 47 | #endif 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/countersignature.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 Avast Software 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | #ifndef AUTHENTICODE_PARSER_COUNTERSIGNATURE_H 23 | #define AUTHENTICODE_PARSER_COUNTERSIGNATURE_H 24 | 25 | #include "certificate.h" 26 | #include "helper.h" 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | Countersignature* pkcs9_countersig_new( 39 | const uint8_t* data, long size, STACK_OF(X509) * certs, ASN1_STRING* enc_digest); 40 | Countersignature* ms_countersig_new(const uint8_t* data, long size, ASN1_STRING* enc_digest); 41 | 42 | int countersignature_array_insert(CountersignatureArray* arr, Countersignature* sig); 43 | /* Moves all countersignatures of src and inserts them into dst */ 44 | int countersignature_array_move(CountersignatureArray* dst, CountersignatureArray* src); 45 | 46 | void countersignature_free(Countersignature* sig); 47 | void countersignature_array_free(CountersignatureArray* arr); 48 | 49 | #ifdef __cplusplus 50 | } 51 | #endif 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(authenticode_parser VERSION 1.0.0 LANGUAGES C) 4 | 5 | find_package(OpenSSL 1.1.1 REQUIRED) 6 | 7 | message(STATUS "SSL library is ${OPENSSL_SSL_LIBRARY}") 8 | message(STATUS "Crypto library is ${OPENSSL_SSL_LIBRARY}") 9 | message(STATUS "All openssl libraries are ${OPENSSL_SSL_LIBRARY}") 10 | message(STATUS "OpenSSL version is ${OPENSSL_SSL_LIBRARY}") 11 | 12 | include(GNUInstallDirs) 13 | 14 | add_library(authenticode STATIC 15 | src/authenticode.c 16 | src/helper.c 17 | src/structs.c 18 | src/countersignature.c 19 | src/certificate.c 20 | ) 21 | 22 | include (TestBigEndian) 23 | TEST_BIG_ENDIAN(IS_BIG_ENDIAN) 24 | if(IS_BIG_ENDIAN) 25 | target_compile_definitions(-DWORDS_BIGENDIAN) 26 | endif() 27 | 28 | if(MSVC) 29 | target_compile_options(authenticode PRIVATE /W4 -fpie) 30 | else() 31 | target_compile_options(authenticode PRIVATE -Wall -Wextra -Wpedantic -fpie) 32 | endif() 33 | 34 | target_compile_features(authenticode PRIVATE c_std_11) 35 | 36 | target_include_directories(authenticode 37 | PUBLIC 38 | $ 39 | $ 40 | PRIVATE 41 | ${CMAKE_CURRENT_SOURCE_DIR}/src 42 | ) 43 | 44 | target_link_libraries(authenticode 45 | PRIVATE 46 | OpenSSL::Crypto 47 | ) 48 | 49 | install( 50 | DIRECTORY include/ 51 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 52 | ) 53 | 54 | install(TARGETS authenticode 55 | EXPORT authenticode-targets 56 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 57 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 58 | ) 59 | 60 | set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/authenticode) 61 | 62 | install(EXPORT authenticode-targets 63 | FILE "authenticode-targets.cmake" 64 | DESTINATION ${INSTALL_CONFIGDIR} 65 | ) 66 | 67 | include(CMakePackageConfigHelpers) 68 | 69 | configure_file( 70 | "authenticode-config.cmake" 71 | "${CMAKE_CURRENT_BINARY_DIR}/authenticode-config.cmake" 72 | @ONLY 73 | ) 74 | install( 75 | FILES "${CMAKE_CURRENT_BINARY_DIR}/authenticode-config.cmake" 76 | DESTINATION ${INSTALL_CONFIGDIR} 77 | ) 78 | 79 | if(BUILD_TESTS) 80 | enable_testing() 81 | add_subdirectory(tests) 82 | endif() 83 | -------------------------------------------------------------------------------- /src/helper.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 Avast Software 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | #ifndef AUTHENTICODE_PARSER_HELPER_H 23 | #define AUTHENTICODE_PARSER_HELPER_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | #ifdef _WIN32 33 | #define timegm _mkgmtime 34 | #endif 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | /* Endianity related functions for PE reading */ 41 | uint16_t bswap16(uint16_t d); 42 | uint32_t bswap32(uint32_t d); 43 | 44 | #if defined(WORDS_BIGENDIAN) 45 | #define letoh16(x) bswap16(x) 46 | #define letoh32(x) bswap32(x) 47 | #define betoh16(x) (x) 48 | #define betoh32(x) (x) 49 | #else 50 | #define letoh16(x) (x) 51 | #define letoh32(x) (x) 52 | #define betoh16(x) bswap16(x) 53 | #define betoh32(x) bswap32(x) 54 | #endif 55 | 56 | /* Calculates digest md of data, return bytes written to digest or 0 on error 57 | * Maximum of EVP_MAX_MD_SIZE will be written to digest */ 58 | int calculate_digest(const EVP_MD* md, const uint8_t* data, size_t len, uint8_t* digest); 59 | /* Copies data of length len into already existing arr */ 60 | int byte_array_init(ByteArray* arr, const uint8_t* data, int len); 61 | /* Converts ASN1_TIME string time into a unix timestamp */ 62 | int64_t ASN1_TIME_to_int64_t(const ASN1_TIME* time); 63 | 64 | #ifdef __cplusplus 65 | } 66 | #endif 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /src/helper.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 Avast Software 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | #include "helper.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | uint16_t bswap16(uint16_t d) 32 | { 33 | return (d << 8) | (d >> 8); 34 | } 35 | 36 | uint32_t bswap32(uint32_t d) 37 | { 38 | return (((d)&0xff000000) >> 24) | (((d)&0x00ff0000) >> 8) | (((d)&0x0000ff00) << 8) | 39 | (((d)&0x000000ff) << 24); 40 | } 41 | 42 | int calculate_digest(const EVP_MD* md, const uint8_t* data, size_t len, uint8_t* digest) 43 | { 44 | unsigned int outLen = 0; 45 | 46 | EVP_MD_CTX* mdCtx = EVP_MD_CTX_new(); 47 | if (!mdCtx) 48 | goto end; 49 | 50 | if (!EVP_DigestInit_ex(mdCtx, md, NULL) || !EVP_DigestUpdate(mdCtx, data, len) || 51 | !EVP_DigestFinal_ex(mdCtx, digest, &outLen)) 52 | goto end; 53 | 54 | end: 55 | EVP_MD_CTX_free(mdCtx); 56 | return (int)outLen; 57 | } 58 | 59 | int byte_array_init(ByteArray* arr, const uint8_t* data, int len) 60 | { 61 | if (len == 0) { 62 | arr->data = NULL; 63 | arr->len = 0; 64 | return 0; 65 | } 66 | 67 | arr->data = (uint8_t*)malloc(len); 68 | if (!arr->data) 69 | return -1; 70 | 71 | arr->len = len; 72 | memcpy(arr->data, data, len); 73 | return 0; 74 | } 75 | 76 | int64_t ASN1_TIME_to_int64_t(const ASN1_TIME* time) 77 | { 78 | struct tm t = {0}; 79 | if (!time) 80 | return timegm(&t); 81 | 82 | ASN1_TIME_to_tm(time, &t); 83 | return timegm(&t); 84 | } 85 | -------------------------------------------------------------------------------- /tests/unit/certificate.cpp: -------------------------------------------------------------------------------- 1 | #include "../../src/certificate.h" 2 | #include "../data.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | TEST(CertificateModule, certificate_array_move) 13 | { 14 | CertificateArray array1; 15 | array1.certs = nullptr; 16 | array1.count = 0; 17 | 18 | CertificateArray array2; 19 | array2.certs = nullptr; 20 | array2.count = 0; 21 | 22 | Certificate cert1 = {0}; 23 | Certificate cert2 = {0}; 24 | 25 | array2.count = 2; 26 | array2.certs = (Certificate **)malloc(sizeof(Certificate *) * 2); 27 | 28 | array2.certs[0] = &cert1; 29 | array2.certs[1] = &cert2; 30 | 31 | int res = certificate_array_move(&array1, &array2); 32 | EXPECT_EQ(res, 0); 33 | 34 | ASSERT_TRUE(array1.certs); 35 | ASSERT_EQ(array1.count, 2); 36 | 37 | EXPECT_EQ(array1.certs[0], &cert1); 38 | EXPECT_EQ(array1.certs[1], &cert2); 39 | 40 | EXPECT_EQ(array2.count, 0); 41 | EXPECT_FALSE(array2.certs); 42 | 43 | free(array1.certs); 44 | } 45 | 46 | TEST(CertificateModule, certificate_new) 47 | { 48 | BIO *bio = BIO_new(BIO_s_mem()); 49 | ASSERT_TRUE(bio); 50 | 51 | BIO_write(bio, CERTIFICATE_PEM, std::strlen(CERTIFICATE_PEM)); 52 | 53 | X509 *x509 = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); 54 | ASSERT_TRUE(x509); 55 | 56 | Certificate *cert = certificate_new(x509); 57 | ASSERT_TRUE(cert); 58 | 59 | EXPECT_EQ(cert->version, 2); 60 | EXPECT_STREQ(cert->serial, "38:63:de:f8"); 61 | EXPECT_EQ(cert->not_before, 946057851); 62 | EXPECT_EQ(cert->not_after, 1879596912); 63 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 64 | EXPECT_STREQ(cert->sig_alg, "sha1WithRSAEncryption"); 65 | EXPECT_STREQ( 66 | cert->issuer, 67 | "/O=Entrust.net/OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/OU=(c) 1999 " 68 | "Entrust.net Limited/CN=Entrust.net Certification Authority (2048)"); 69 | EXPECT_STREQ( 70 | cert->subject, 71 | "/O=Entrust.net/OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/OU=(c) 1999 " 72 | "Entrust.net Limited/CN=Entrust.net Certification Authority (2048)"); 73 | 74 | uint8_t sha1[20] = {0x50, 0x30, 0x06, 0x09, 0x1D, 0x97, 0xD4, 0xF5, 0xAE, 0x39, 75 | 0xF7, 0xCB, 0xE7, 0x92, 0x7D, 0x7D, 0x65, 0x2D, 0x34, 0x31}; 76 | 77 | ASSERT_TRUE(cert->sha1.data); 78 | ASSERT_EQ(cert->sha1.len, 20); 79 | EXPECT_TRUE(std::memcmp(cert->sha1.data, sha1, 20) == 0); 80 | 81 | X509_free(x509); 82 | certificate_free(cert); 83 | BIO_free_all(bio); 84 | } -------------------------------------------------------------------------------- /tests/unit/helper.cpp: -------------------------------------------------------------------------------- 1 | #include "../../src/helper.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | TEST(HelperModule, byte_array_init_0) 9 | { 10 | ByteArray array; 11 | uint8_t data[10] = {0x1, 0x2, 0x3}; 12 | int res = byte_array_init(&array, data, 10); 13 | EXPECT_EQ(res, 0); 14 | EXPECT_EQ(array.len, 10); 15 | ASSERT_TRUE(array.data); 16 | EXPECT_TRUE(std::memcmp(array.data, data, array.len) == 0); 17 | free(array.data); 18 | } 19 | 20 | TEST(HelperModule, byte_array_init_1) 21 | { 22 | ByteArray array; 23 | uint8_t data[1] = {0x1}; 24 | int res = byte_array_init(&array, data, 1); 25 | EXPECT_EQ(res, 0); 26 | EXPECT_EQ(array.len, 1); 27 | ASSERT_TRUE(array.data); 28 | EXPECT_TRUE(std::memcmp(array.data, data, array.len) == 0); 29 | free(array.data); 30 | } 31 | 32 | TEST(HelperModule, byte_array_init_2) 33 | { 34 | ByteArray array; 35 | uint8_t data[10000] = {0x1, 0x5, 0x10, 0x11}; 36 | int res = byte_array_init(&array, data, 10000); 37 | EXPECT_EQ(res, 0); 38 | EXPECT_EQ(array.len, 10000); 39 | ASSERT_TRUE(array.data); 40 | EXPECT_TRUE(std::memcmp(array.data, data, array.len) == 0); 41 | free(array.data); 42 | } 43 | 44 | TEST(HelperModule, asn1_time_get_int64_t_0) 45 | { 46 | auto asn1time = ASN1_TIME_new(); 47 | ASN1_TIME_set(asn1time, 1527779085); 48 | int64_t res = ASN1_TIME_to_int64_t(asn1time); 49 | EXPECT_EQ(res, 1527779085); 50 | ASN1_TIME_free(asn1time); 51 | } 52 | 53 | TEST(HelperModule, asn1_time_get_int64_t_1) 54 | { 55 | auto asn1time = ASN1_TIME_new(); 56 | int succ = ASN1_TIME_set_string(asn1time, "211014101955Z"); 57 | EXPECT_TRUE(succ); 58 | int64_t res = ASN1_TIME_to_int64_t(asn1time); 59 | EXPECT_EQ(res, 1634206795); 60 | ASN1_TIME_free(asn1time); 61 | } 62 | 63 | TEST(HelperModule, asn1_time_get_int64_t_2) 64 | { 65 | auto asn1time = ASN1_TIME_new(); 66 | int succ = ASN1_TIME_set_string_X509(asn1time, "19700102212340Z"); 67 | EXPECT_TRUE(succ); 68 | int64_t res = ASN1_TIME_to_int64_t(asn1time); 69 | EXPECT_EQ(res, 163420); 70 | ASN1_TIME_free(asn1time); 71 | } 72 | 73 | TEST(HelperModule, calculate_digest) 74 | { 75 | uint8_t data[3] = {'a', 'b', 'c'}; 76 | const int sha1_len = 20; 77 | uint8_t hash[sha1_len]; 78 | int res = calculate_digest(EVP_sha1(), data, 3, hash); 79 | EXPECT_EQ(res, sha1_len); 80 | uint8_t correct_hash[sha1_len] = {0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 81 | 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D}; 82 | EXPECT_TRUE(std::memcmp(hash, correct_hash, sha1_len) == 0); 83 | } 84 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | # Use Debug type as Windows extra debug assertions can uncover extra bugs. 11 | BUILD_TYPE: Debug 12 | 13 | jobs: 14 | test-openssl1-1: 15 | strategy: 16 | matrix: 17 | os: [macos-latest, windows-latest] 18 | # Stops killing other jobs when one fails 19 | fail-fast: false 20 | 21 | runs-on: ${{ matrix.os }} 22 | 23 | steps: 24 | - uses: actions/checkout@v2 25 | 26 | - name: Install OpenSSL on MacOS 27 | if: matrix.os == 'macos-latest' 28 | run: | 29 | brew uninstall openssl --ignore-dependencies openssl 30 | brew install openssl@1.1 31 | 32 | - name: Install OpenSSL on Windows 33 | if: matrix.os == 'windows-latest' 34 | run: | 35 | rd -r "C:/Program Files/OpenSSL" 36 | choco install openssl --version=1.1.1.2100 37 | 38 | # Copy-Item -Path "C:/Program Files/OpenSSL/lib/VC/x64/MD/*" -Destination "C:/Program Files/OpenSSL/lib/VC" -Recurse 39 | 40 | - name: Configure Windows CMake 41 | if: matrix.os == 'windows-latest' 42 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_TESTS=ON 43 | 44 | - name: Configure MacOS CMake 45 | if: matrix.os == 'macos-latest' 46 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_TESTS=ON -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@1.1 47 | 48 | - name: Build 49 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 50 | 51 | - name: Test 52 | working-directory: ${{github.workspace}}/build 53 | run: ctest -C ${{env.BUILD_TYPE}} -VV 54 | 55 | test-openssl3: 56 | strategy: 57 | matrix: 58 | os: [macos-latest, ubuntu-latest] 59 | # Stops killing other jobs when one fails 60 | fail-fast: false 61 | 62 | runs-on: ${{ matrix.os }} 63 | 64 | steps: 65 | - uses: actions/checkout@v2 66 | 67 | - name: Install OpenSSL on MacOS 68 | if: matrix.os == 'macos-latest' 69 | run: brew install openssl@3 70 | 71 | - name: Configure MacOS CMake 72 | if: matrix.os == 'macos-latest' 73 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_TESTS=ON -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl 74 | 75 | - name: Configure Ubuntu CMake 76 | if: matrix.os == 'ubuntu-latest' 77 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_TESTS=ON 78 | 79 | - name: Build 80 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 81 | 82 | - name: Test 83 | working-directory: ${{github.workspace}}/build 84 | run: ctest -C ${{env.BUILD_TYPE}} -VV 85 | -------------------------------------------------------------------------------- /src/structs.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 Avast Software 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | #include "structs.h" 23 | 24 | ASN1_CHOICE(SpcString) = { 25 | ASN1_IMP_OPT(SpcString, value.unicode, ASN1_BMPSTRING, 0), 26 | ASN1_IMP_OPT(SpcString, value.ascii, ASN1_IA5STRING, 1) 27 | } ASN1_CHOICE_END(SpcString) 28 | 29 | ASN1_SEQUENCE(SpcSerializedObject) = { 30 | ASN1_SIMPLE(SpcSerializedObject, classId, ASN1_OCTET_STRING), 31 | ASN1_SIMPLE(SpcSerializedObject, serializedData, ASN1_OCTET_STRING) 32 | } ASN1_SEQUENCE_END(SpcSerializedObject) 33 | 34 | ASN1_CHOICE(SpcLink) = { 35 | ASN1_IMP_OPT(SpcLink, value.url, ASN1_IA5STRING, 0), 36 | ASN1_IMP_OPT(SpcLink, value.moniker, SpcSerializedObject, 1), 37 | ASN1_EXP_OPT(SpcLink, value.file, SpcString, 2) 38 | } ASN1_CHOICE_END(SpcLink) 39 | 40 | ASN1_SEQUENCE(SpcAttributeTypeAndOptionalValue) = { 41 | ASN1_SIMPLE(SpcAttributeTypeAndOptionalValue, type, ASN1_OBJECT), 42 | ASN1_OPT(SpcAttributeTypeAndOptionalValue, value, ASN1_ANY) 43 | } ASN1_SEQUENCE_END(SpcAttributeTypeAndOptionalValue) 44 | 45 | ASN1_SEQUENCE(SpcPeImageData) = { 46 | ASN1_SIMPLE(SpcPeImageData, flags, ASN1_BIT_STRING), 47 | ASN1_EXP_OPT(SpcPeImageData, file, SpcLink, 0) 48 | } ASN1_SEQUENCE_END(SpcPeImageData) 49 | 50 | ASN1_SEQUENCE(AlgorithmIdentifier) = { 51 | ASN1_SIMPLE(AlgorithmIdentifier, algorithm, ASN1_OBJECT), 52 | ASN1_OPT(AlgorithmIdentifier, parameters, ASN1_ANY) 53 | } ASN1_SEQUENCE_END(AlgorithmIdentifier) 54 | 55 | ASN1_SEQUENCE(DigestInfo) = { 56 | ASN1_SIMPLE(DigestInfo, digestAlgorithm, AlgorithmIdentifier), 57 | ASN1_SIMPLE(DigestInfo, digest, ASN1_OCTET_STRING) 58 | } ASN1_SEQUENCE_END(DigestInfo) 59 | 60 | ASN1_SEQUENCE(SpcIndirectDataContent) = { 61 | ASN1_SIMPLE(SpcIndirectDataContent, data, SpcAttributeTypeAndOptionalValue), 62 | ASN1_SIMPLE(SpcIndirectDataContent, messageDigest, DigestInfo) 63 | } ASN1_SEQUENCE_END(SpcIndirectDataContent) 64 | 65 | ASN1_SEQUENCE(SpcSpOpusInfo) = { 66 | ASN1_EXP_OPT(SpcSpOpusInfo, programName, SpcString, 0), 67 | ASN1_EXP_OPT(SpcSpOpusInfo, moreInfo, SpcLink, 1) 68 | } ASN1_SEQUENCE_END(SpcSpOpusInfo) 69 | 70 | IMPLEMENT_ASN1_FUNCTIONS(SpcString) 71 | IMPLEMENT_ASN1_FUNCTIONS(SpcSerializedObject) 72 | IMPLEMENT_ASN1_FUNCTIONS(SpcLink) 73 | IMPLEMENT_ASN1_FUNCTIONS(SpcAttributeTypeAndOptionalValue) 74 | IMPLEMENT_ASN1_FUNCTIONS(SpcPeImageData) 75 | IMPLEMENT_ASN1_FUNCTIONS(AlgorithmIdentifier) 76 | IMPLEMENT_ASN1_FUNCTIONS(DigestInfo) 77 | IMPLEMENT_ASN1_FUNCTIONS(SpcIndirectDataContent) 78 | IMPLEMENT_ASN1_FUNCTIONS(SpcSpOpusInfo) 79 | -------------------------------------------------------------------------------- /src/structs.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 Avast Software 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | #ifndef AUTHENTICODE_PARSER_STRUCTS_H 23 | #define AUTHENTICODE_PARSER_STRUCTS_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | #define NID_spc_info "1.3.6.1.4.1.311.2.1.12" 35 | #define NID_spc_ms_countersignature "1.3.6.1.4.1.311.3.3.1" 36 | #define NID_spc_nested_signature "1.3.6.1.4.1.311.2.4.1" 37 | #define NID_spc_indirect_data "1.3.6.1.4.1.311.2.1.4" 38 | 39 | typedef struct { 40 | int type; 41 | union { 42 | ASN1_BMPSTRING *unicode; 43 | ASN1_IA5STRING *ascii; 44 | } value; 45 | } SpcString; 46 | 47 | typedef struct { 48 | ASN1_OCTET_STRING *classId; 49 | ASN1_OCTET_STRING *serializedData; 50 | } SpcSerializedObject; 51 | 52 | typedef struct { 53 | int type; 54 | union { 55 | ASN1_IA5STRING *url; 56 | SpcSerializedObject *moniker; 57 | SpcString *file; 58 | } value; 59 | } SpcLink; 60 | 61 | typedef struct { 62 | ASN1_OBJECT *type; 63 | ASN1_TYPE *value; 64 | } SpcAttributeTypeAndOptionalValue; 65 | 66 | typedef struct { 67 | ASN1_BIT_STRING *flags; 68 | SpcLink *file; 69 | } SpcPeImageData; 70 | 71 | typedef struct { 72 | ASN1_OBJECT *algorithm; 73 | ASN1_TYPE *parameters; 74 | } AlgorithmIdentifier; 75 | 76 | typedef struct { 77 | AlgorithmIdentifier *digestAlgorithm; 78 | ASN1_OCTET_STRING *digest; 79 | } DigestInfo; 80 | 81 | typedef struct { 82 | SpcAttributeTypeAndOptionalValue *data; 83 | DigestInfo *messageDigest; 84 | } SpcIndirectDataContent; 85 | 86 | typedef struct { 87 | ASN1_OBJECT *contentType; 88 | SpcIndirectDataContent *content; 89 | } SpcContentInfo; 90 | 91 | typedef struct { 92 | SpcString *programName; 93 | SpcLink *moreInfo; 94 | } SpcSpOpusInfo; 95 | 96 | DECLARE_ASN1_FUNCTIONS(SpcString) 97 | DECLARE_ASN1_FUNCTIONS(SpcSerializedObject) 98 | DECLARE_ASN1_FUNCTIONS(SpcLink) 99 | DECLARE_ASN1_FUNCTIONS(SpcAttributeTypeAndOptionalValue) 100 | DECLARE_ASN1_FUNCTIONS(SpcPeImageData) 101 | DECLARE_ASN1_FUNCTIONS(AlgorithmIdentifier) 102 | DECLARE_ASN1_FUNCTIONS(DigestInfo) 103 | DECLARE_ASN1_FUNCTIONS(SpcIndirectDataContent) 104 | DECLARE_ASN1_FUNCTIONS(SpcSpOpusInfo) 105 | DECLARE_ASN1_FUNCTIONS(SpcContentInfo) 106 | 107 | #ifdef __cplusplus 108 | } 109 | #endif 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Master](https://github.com/avast/authenticode-parser/actions/workflows/cmake.yml/badge.svg?branch=master) 2 | 3 | # Authenticode parser 4 | Authenticode-parser is a C library used to parse Microsoft digital signature format, that is being used to sign PE files on Windows systems. 5 | 6 | The library interface takes binary data with Authenticode signature as input, which is then verified and parsed into an internal representation. 7 | 8 | Features: 9 | * Parsing of Authenticode signature - digests, signerInfo, certificates, building certificate chain 10 | * Extracting further Nested Authenticode signature (through unauthenticated attributes) 11 | * Parsing of PKCS9 timestamp counter-signatures 12 | * Parsing of Microsoft timestamp counter-signatures 13 | * Verification of the Authenticode signatures, PKCS9 and Microsoft timestamp counter-signatures (That hashes match, etc.) 14 | 15 | Important note: Certificate chain is only built, but not verified as we cannot complete the verification without trust anchors anyway. 16 | 17 | ## Use of the library 18 | Integrating the library is very easy through CMake. If you installed the library into a standard installation location of your system (e.g. `/usr`, `/usr/local`), all you need to do in order to use its components is: 19 | 20 | ```cmake 21 | find_package(authenticode REQUIRED) 22 | 23 | target_link_libraries(your-project 24 | PUBLIC 25 | authenticode 26 | [...] 27 | ) 28 | ``` 29 | 30 | If your library is in different location, you can pass the path to your CMake `-Dauthenticode_DIR=` or set a `CMAKE_PREFIX_PATH`. 31 | 32 | A simple example of library use, that dumps all the parsed information, and integration can be found [here](https://github.com/avast/authenticode-parser/tree/master/examples). 33 | 34 | ## Build, Installation and Testing 35 | 36 | ### Requirements 37 | 38 | * A C++ and a C compiler 39 | * [OpenSSL](https://www.openssl.org/) (version >= 1.1.1) 40 | * [CMake](https://cmake.org/) (version >= 3.14) 41 | 42 | On Debian-based distributions (e.g. Ubuntu), the required packages can be installed with `apt-get`: 43 | 44 | ```sh 45 | sudo apt-get install build-essential cmake git openssl libssl-dev 46 | ``` 47 | 48 | On Windows, the required packages can be installed with [Chocolatey](https://chocolatey.org/) - `choco` 49 | 50 | ```sh 51 | choco install openssl cmake git 52 | ``` 53 | 54 | On MacOS, the required packages can be installed with `brew` 55 | 56 | ```sh 57 | brew install openssl@1.1 cmake git 58 | ``` 59 | 60 | ### Build and Installation 61 | * Clone the repository: 62 | * `git clone https://github.com/avast/authenticode-parser/` 63 | * Linux and MacOS: 64 | * `cd authenticode-parser` 65 | * `mkdir build && cd build` 66 | * `cmake .. -DCMAKE_INSTALL_PREFIX=` 67 | * `make install` 68 | * Windows: 69 | * `cd authenticode-parser` 70 | * `mkdir build && cd build` 71 | * `cmake .. -DCMAKE_INSTALL_PREFIX=` 72 | * `cmake --build . --config Debug --target install` 73 | 74 | If you wish to also build tests, pass `-DBUILD_TESTS=ON` option to CMake. For MacOS, if CMake can't find OpenSSL on PATH, you can pass it to the CMake with `-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl` option 75 | 76 | ### Testing 77 | Authenticode-parser is using [GoogleTest](https://github.com/google/googletest) as testing framework. Tests can be built using `-DBUILD_TESTS=ON` CMake option. 78 | 79 | To run the tests go to the `build/` folder and run: 80 | ```sh 81 | ctest -V 82 | ``` 83 | 84 | On Windows you will need to specify the configuration: 85 | ```sh 86 | ctest -C Debug -V 87 | ``` 88 | 89 | 90 | ## License 91 | 92 | Copyright (c) 2021 Avast Software, licensed under the MIT license. See the LICENSE file for more details. 93 | -------------------------------------------------------------------------------- /examples/authenticode_dumper.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 Avast Software 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | void print_bytes(const ByteArray *bytes) 28 | { 29 | if (bytes->data) { 30 | for (int i = 0; i < bytes->len; ++i) { 31 | printf("%02x", bytes->data[i]); 32 | } 33 | puts(""); 34 | } else { 35 | puts("(null)"); 36 | } 37 | } 38 | 39 | void print_certificate(Certificate *cert, char *indent) 40 | { 41 | printf("%sVersion : %ld\n", indent, cert->version); 42 | printf("%sSubject : %s\n", indent, cert->subject); 43 | printf("%sIssuer : %s\n", indent, cert->issuer); 44 | printf("%sSerial : %s\n", indent, cert->serial); 45 | printf("%sNot After : %lu\n", indent, cert->not_after); 46 | printf("%sNot Before : %lu\n", indent, cert->not_before); 47 | printf("%sSHA1 : ", indent); 48 | print_bytes(&cert->sha1); 49 | 50 | printf("%sSHA256 : ", indent); 51 | print_bytes(&cert->sha256); 52 | 53 | printf("%sKey Algorithm : %s\n", indent, cert->key_alg); 54 | printf("%sSignature Algorithm : %s\n", indent, cert->sig_alg); 55 | printf("%sPublic key : %s\n", indent, cert->key); 56 | } 57 | 58 | void print_authenticode(Authenticode *auth) 59 | { 60 | char *indent = " "; 61 | 62 | printf("%sPKCS7 Signature:\n", indent); 63 | 64 | indent = " "; 65 | 66 | printf("%sVersion : %d\n", indent, auth->version); 67 | printf("%sDigest : ", indent); 68 | print_bytes(&auth->digest); 69 | printf("%sFile Digest : ", indent); 70 | print_bytes(&auth->file_digest); 71 | printf("%sDigest Algorithm : %s\n", indent, auth->digest_alg); 72 | printf("%sVerify flags : %d\n", indent, auth->verify_flags); 73 | if (auth->signer->program_name) { 74 | printf("%sProgram name : %s\n", indent, auth->signer->program_name); 75 | } 76 | printf("\n"); 77 | 78 | if (auth->certs) { 79 | printf("%sCertificate count : %ld\n", indent, auth->certs->count); 80 | printf("%sCertificates: \n\n", indent); 81 | 82 | for (size_t i = 0; i < auth->certs->count; ++i) { 83 | char *indent = " "; 84 | 85 | printf("%sCertificate %lu:\n", indent, i); 86 | print_certificate(auth->certs->certs[i], " "); 87 | } 88 | } 89 | 90 | if (auth->signer) { 91 | printf("%sSigner Info:\n", indent); 92 | 93 | char *indent = " "; 94 | 95 | printf("%sDigest : ", indent); 96 | print_bytes(&auth->signer->digest); 97 | printf("%sDigest Algo : %s\n", indent, auth->signer->digest_alg); 98 | printf("%sProgram name : %s\n", indent, auth->signer->program_name); 99 | 100 | if (auth->signer->chain) { 101 | printf("%sChain size : %lu\n", indent, auth->signer->chain->count); 102 | printf("%sChain:\n", indent); 103 | 104 | for (size_t i = 0; i < auth->signer->chain->count; ++i) { 105 | char *indent = " "; 106 | 107 | printf("%sCertificate %lu:\n", indent, i); 108 | print_certificate(auth->signer->chain->certs[i], " "); 109 | } 110 | } 111 | } 112 | 113 | puts("\n"); 114 | 115 | if (auth->countersigs) { 116 | for (size_t i = 0; i < auth->countersigs->count; ++i) { 117 | Countersignature *counter = auth->countersigs->counters[i]; 118 | printf("%sCountersignature:\n", indent); 119 | char *indent = " "; 120 | 121 | printf("%sDigest : ", indent); 122 | print_bytes(&counter->digest); 123 | printf("%sDigest Algorithm : %s\n", indent, counter->digest_alg); 124 | printf("%sSigning Time : %lu\n", indent, counter->sign_time); 125 | printf("%sVerify flags : %d\n", indent, counter->verify_flags); 126 | 127 | if (counter->chain) { 128 | printf("%sChain size : %lu\n", indent, counter->chain->count); 129 | printf("%sChain:\n", indent); 130 | 131 | for (size_t i = 0; i < counter->chain->count; ++i) { 132 | char *indent = " "; 133 | 134 | printf("%sCertificate %lu:\n", indent, i); 135 | print_certificate(counter->chain->certs[i], " "); 136 | } 137 | } 138 | } 139 | } 140 | } 141 | 142 | int main(int argc, char **argv) 143 | { 144 | if (argc != 2) { 145 | printf("Missing file argument\n"); 146 | return 1; 147 | } 148 | 149 | FILE *fp = fopen(argv[1], "rb"); 150 | if (!fp) { 151 | printf("File not found.\n"); 152 | return 1; 153 | } 154 | 155 | fseek(fp, 0, SEEK_END); 156 | long fsize = ftell(fp); 157 | fseek(fp, 0, SEEK_SET); 158 | 159 | uint8_t *data = malloc(fsize); 160 | if (!data) { 161 | printf("Allocation failure.\n"); 162 | return 1; 163 | } 164 | 165 | fread(data, 1, fsize, fp); 166 | fclose(fp); 167 | /* initialize all global openssl objects */ 168 | initialize_authenticode_parser(); 169 | AuthenticodeArray *auth = parse_authenticode(data, fsize); 170 | if (!auth) { 171 | printf("Couldn't parse any signatures.\n"); 172 | return 0; 173 | } 174 | 175 | printf("Signature count: %lu\n", auth->count); 176 | 177 | for (size_t i = 0; i < auth->count; ++i) 178 | print_authenticode(auth->signatures[i]); 179 | 180 | authenticode_array_free(auth); 181 | free(data); 182 | 183 | return 0; 184 | } 185 | -------------------------------------------------------------------------------- /include/authenticode-parser/authenticode.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 Avast Software 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | #ifndef AUTHENTICODE_PARSER_AUTHENTICODE_H 23 | #define AUTHENTICODE_PARSER_AUTHENTICODE_H 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include 30 | #include 31 | 32 | /* Signature is valid */ 33 | #define AUTHENTICODE_VFY_VALID 0 34 | /* Parsing error (from OpenSSL functions) */ 35 | #define AUTHENTICODE_VFY_CANT_PARSE 1 36 | /* Signers certificate is missing */ 37 | #define AUTHENTICODE_VFY_NO_SIGNER_CERT 2 38 | /* No digest saved inside the signature */ 39 | #define AUTHENTICODE_VFY_DIGEST_MISSING 3 40 | /* Non verification errors - allocations etc. */ 41 | #define AUTHENTICODE_VFY_INTERNAL_ERROR 4 42 | /* SignerInfo part of PKCS7 is missing */ 43 | #define AUTHENTICODE_VFY_NO_SIGNER_INFO 5 44 | /* PKCS7 doesn't have type of SignedData, can't proceed */ 45 | #define AUTHENTICODE_VFY_WRONG_PKCS7_TYPE 6 46 | /* PKCS7 doesn't have corrent content, can't proceed */ 47 | #define AUTHENTICODE_VFY_BAD_CONTENT 7 48 | /* Contained and calculated digest don't match */ 49 | #define AUTHENTICODE_VFY_INVALID 8 50 | /* Signature hash and file hash doesn't match */ 51 | #define AUTHENTICODE_VFY_WRONG_FILE_DIGEST 9 52 | /* Unknown algorithm, can't proceed with verification */ 53 | #define AUTHENTICODE_VFY_UNKNOWN_ALGORITHM 10 54 | 55 | /* Countersignature is valid */ 56 | #define COUNTERSIGNATURE_VFY_VALID 0 57 | /* Parsing error (from OpenSSL functions) */ 58 | #define COUNTERSIGNATURE_VFY_CANT_PARSE 1 59 | /* Signers certificate is missing */ 60 | #define COUNTERSIGNATURE_VFY_NO_SIGNER_CERT 2 61 | /* Unknown algorithm, can't proceed with verification */ 62 | #define COUNTERSIGNATURE_VFY_UNKNOWN_ALGORITHM 3 63 | /* Verification failed, digest mismatch */ 64 | #define COUNTERSIGNATURE_VFY_INVALID 4 65 | /* Failed to decrypt countersignature enc_digest for verification */ 66 | #define COUNTERSIGNATURE_VFY_CANT_DECRYPT_DIGEST 5 67 | /* No digest saved inside the countersignature */ 68 | #define COUNTERSIGNATURE_VFY_DIGEST_MISSING 6 69 | /* Message digest inside countersignature doesn't match signature it countersigns */ 70 | #define COUNTERSIGNATURE_VFY_DOESNT_MATCH_SIGNATURE 7 71 | /* Non verification errors - allocations etc. */ 72 | #define COUNTERSIGNATURE_VFY_INTERNAL_ERROR 8 73 | /* Time is missing in the timestamp signature */ 74 | #define COUNTERSIGNATURE_VFY_TIME_MISSING 9 75 | 76 | typedef struct { 77 | uint8_t* data; 78 | int len; 79 | } ByteArray; 80 | 81 | typedef struct { /* Various X509 attributes parsed out in raw bytes*/ 82 | ByteArray country; 83 | ByteArray organization; 84 | ByteArray organizationalUnit; 85 | ByteArray nameQualifier; 86 | ByteArray state; 87 | ByteArray commonName; 88 | ByteArray serialNumber; 89 | ByteArray locality; 90 | ByteArray title; 91 | ByteArray surname; 92 | ByteArray givenName; 93 | ByteArray initials; 94 | ByteArray pseudonym; 95 | ByteArray generationQualifier; 96 | ByteArray emailAddress; 97 | } Attributes; 98 | 99 | typedef struct { 100 | long version; /* Raw version of X509 */ 101 | char* issuer; /* Oneline name of Issuer */ 102 | char* subject; /* Oneline name of Subject */ 103 | char* serial; /* Serial number in format 00:01:02:03:04... */ 104 | ByteArray sha1; /* SHA1 of the DER representation of the cert */ 105 | ByteArray sha256; /* SHA256 of the DER representation of the cert */ 106 | char* key_alg; /* Name of the key algorithm */ 107 | char* sig_alg; /* Name of the signature algorithm */ 108 | char* sig_alg_oid; /* OID of the signature algorithm */ 109 | int64_t not_before; /* NotBefore validity */ 110 | int64_t not_after; /* NotAfter validity */ 111 | char* key; /* PEM encoded public key */ 112 | Attributes issuer_attrs; /* Parsed X509 Attributes of Issuer */ 113 | Attributes subject_attrs; /* Parsed X509 Attributes of Subject */ 114 | } Certificate; 115 | 116 | typedef struct { 117 | Certificate** certs; 118 | size_t count; 119 | } CertificateArray; 120 | 121 | typedef struct { 122 | int verify_flags; /* COUNTERISGNATURE_VFY_ flag */ 123 | int64_t sign_time; /* Signing time of the timestamp countersignature */ 124 | char* digest_alg; /* Name of the digest algorithm used */ 125 | ByteArray digest; /* Stored message digest */ 126 | CertificateArray* chain; /* Certificate chain of the signer */ 127 | CertificateArray* certs; /* All certs stored inside Countersignature, this can be superset 128 | of chain in case of non PKCS9 countersignature*/ 129 | } Countersignature; 130 | 131 | typedef struct { 132 | Countersignature** counters; 133 | size_t count; 134 | } CountersignatureArray; 135 | 136 | typedef struct { /* Represents SignerInfo structure */ 137 | ByteArray digest; /* Message Digest of the SignerInfo */ 138 | char* digest_alg; /* name of the digest algorithm */ 139 | char* program_name; /* Program name stored in SpcOpusInfo structure of Authenticode */ 140 | CertificateArray* chain; /* Certificate chain of the signer */ 141 | } Signer; 142 | 143 | typedef struct { 144 | int verify_flags; /* AUTHENTICODE_VFY_ flag */ 145 | int version; /* Raw PKCS7 version */ 146 | char* digest_alg; /* name of the digest algorithm */ 147 | ByteArray digest; /* File Digest stored in the Signature */ 148 | ByteArray file_digest; /* Actual calculated file digest */ 149 | Signer* signer; /* SignerInfo information of the Authenticode */ 150 | CertificateArray* certs; /* All certificates in the Signature including the ones in timestamp 151 | countersignatures */ 152 | CountersignatureArray* countersigs; /* Array of timestamp countersignatures */ 153 | } Authenticode; 154 | 155 | typedef struct { 156 | Authenticode** signatures; 157 | size_t count; 158 | } AuthenticodeArray; 159 | 160 | /** 161 | * @brief Initializes all globals OpenSSl objects we need for parsing, this is not thread-safe and 162 | * needs to be called only once, before any multithreading environment 163 | * https://github.com/openssl/openssl/issues/13524 164 | */ 165 | void initialize_authenticode_parser(); 166 | 167 | /** 168 | * @brief Constructs AuthenticodeArray from PE file data. Authenticode can 169 | * contains nested Authenticode signatures as its unsigned attribute, 170 | * which can also contain nested signatures. For this reason the function returns 171 | * an Array of parsed Authenticode signatures. Any field of the parsed out 172 | * structures can be NULL, depending on the input data. 173 | * Verification result is stored in verify_flags with the first verification error. 174 | * 175 | * @param pe_data PE binary data 176 | * @param pe_len 177 | * @return AuthenticodeArray* 178 | */ 179 | AuthenticodeArray* parse_authenticode(const uint8_t* pe_data, uint64_t pe_len); 180 | 181 | /** 182 | * @brief Constructs AuthenticodeArray from binary data containing Authenticode 183 | * signature. Authenticode can contains nested Authenticode signatures 184 | * as its unsigned attribute, which can also contain nested signatures. 185 | * For this reason the function return an Array of parsed Authenticode signatures. 186 | * Any field of the parsed out structures can be NULL, depending on the input data. 187 | * WARNING: in case of this interface, the file and signature digest comparison is 188 | * up to the library user, as there is no pe data to calculate file digest from. 189 | * Verification result is stored in verify_flags with the first verification error 190 | * 191 | * @param data Binary data containing Authenticode signature 192 | * @param len 193 | * @return AuthenticodeArray* 194 | */ 195 | AuthenticodeArray* authenticode_new(const uint8_t* data, int32_t len); 196 | 197 | /** 198 | * @brief Deallocates AuthenticodeArray and all it's allocated members 199 | * 200 | * @param auth 201 | */ 202 | void authenticode_array_free(AuthenticodeArray* auth); 203 | 204 | #ifdef __cplusplus 205 | } 206 | #endif 207 | 208 | #endif 209 | -------------------------------------------------------------------------------- /tests/integration/test_microsoft.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 Avast Software 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | #include "../data.h" 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | 37 | class MicrosoftSignatureTest : public testing::Test 38 | { 39 | protected: 40 | unsigned char *data = nullptr; 41 | long data_len = 0; 42 | 43 | void SetUp() override 44 | { 45 | BIO *bio = BIO_new(BIO_s_mem()); 46 | BIO_write( 47 | bio, 48 | VALID_SIGNATURE_PEM_MICROSOFT_COUNTER, 49 | std::strlen(VALID_SIGNATURE_PEM_MICROSOFT_COUNTER)); 50 | char *name = nullptr; 51 | char *header = nullptr; 52 | PEM_read_bio(bio, &name, &header, &data, &data_len); 53 | BIO_free_all(bio); 54 | OPENSSL_free(name); 55 | OPENSSL_free(header); 56 | 57 | initialize_authenticode_parser(); 58 | } 59 | 60 | void TearDown() override { OPENSSL_free(data); } 61 | }; 62 | 63 | TEST_F(MicrosoftSignatureTest, ResultOverview) 64 | { 65 | AuthenticodeArray *auth = authenticode_new(data, data_len); 66 | ASSERT_NE(auth, nullptr); 67 | 68 | ASSERT_EQ(auth->count, 1); 69 | ASSERT_NE(auth->signatures, nullptr); 70 | 71 | for (size_t i = 0; i < auth->count; ++i) { 72 | ASSERT_TRUE(auth->signatures[i]); 73 | } 74 | 75 | authenticode_array_free(auth); 76 | } 77 | 78 | TEST_F(MicrosoftSignatureTest, SignatureContent) 79 | { 80 | AuthenticodeArray *auth = authenticode_new(data, data_len); 81 | ASSERT_NE(auth, nullptr); 82 | 83 | ASSERT_EQ(auth->count, 1); 84 | ASSERT_NE(auth->signatures, nullptr); 85 | 86 | const Authenticode *first_sig = auth->signatures[0]; 87 | ASSERT_TRUE(first_sig); 88 | 89 | //***********************************// 90 | // Check the first signature content // 91 | EXPECT_EQ(first_sig->version, 1); 92 | 93 | EXPECT_TRUE(first_sig->digest.data); 94 | uint8_t file_digest[32] = {0xc7, 0xfe, 0xf9, 0x4e, 0x32, 0x9b, 0xd9, 0xb6, 0x6b, 0x28, 0x15, 95 | 0x39, 0x26, 0x5f, 0x98, 0x93, 0x13, 0x35, 0x6c, 0xbd, 0x9c, 0x34, 96 | 0x5d, 0xf9, 0xe6, 0x70, 0xe9, 0xc4, 0xb6, 0xe0, 0xed, 0xce}; 97 | EXPECT_EQ(first_sig->digest.len, 32); 98 | EXPECT_TRUE(std::memcmp(file_digest, first_sig->digest.data, 32) == 0); 99 | EXPECT_STREQ(first_sig->digest_alg, "sha256"); 100 | 101 | EXPECT_EQ(first_sig->verify_flags, AUTHENTICODE_VFY_VALID); 102 | 103 | //****************************// 104 | // Check SignerInfo structure // 105 | ASSERT_TRUE(first_sig->signer); 106 | EXPECT_STREQ(first_sig->signer->digest_alg, "sha256"); 107 | 108 | ASSERT_TRUE(first_sig->signer->digest.data); 109 | ASSERT_EQ(first_sig->signer->digest.len, 32); 110 | uint8_t message_digest[32] = {0x16, 0xef, 0xc5, 0x25, 0x0c, 0x4d, 0x4a, 0x99, 0xa0, 0x0e, 0xd2, 111 | 0xad, 0x9a, 0x0e, 0x3d, 0x8f, 0xbc, 0x21, 0xda, 0x5b, 0xe9, 0x5a, 112 | 0xc3, 0x5a, 0xd3, 0x3b, 0x3d, 0x9c, 0x3f, 0x37, 0x19, 0xa1}; 113 | EXPECT_TRUE(std::memcmp(message_digest, first_sig->signer->digest.data, 32) == 0); 114 | ASSERT_TRUE(first_sig->signer->program_name); 115 | ASSERT_STREQ(first_sig->signer->program_name, "Procexp"); 116 | 117 | //******************************************// 118 | // Test all certificates of first signature // 119 | ASSERT_TRUE(first_sig->certs); 120 | ASSERT_TRUE(first_sig->certs->certs); 121 | ASSERT_EQ(first_sig->certs->count, 4); 122 | 123 | //**************************// 124 | // Check the 1. certificate // 125 | const Certificate *cert = first_sig->certs->certs[0]; 126 | ASSERT_TRUE(cert->sha1.data); 127 | ASSERT_EQ(cert->sha1.len, 20); 128 | unsigned char first_cert_sha1[20] = {0x92, 0xd7, 0x19, 0x2a, 0x7c, 0x31, 0x80, 129 | 0x91, 0x2f, 0xf8, 0x41, 0x4f, 0x79, 0x09, 130 | 0x73, 0xa0, 0x5c, 0x28, 0xf8, 0xb0}; 131 | EXPECT_TRUE(std::memcmp(first_cert_sha1, cert->sha1.data, 20) == 0); 132 | EXPECT_EQ(cert->version, 2); 133 | EXPECT_STREQ( 134 | cert->subject, 135 | "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Windows Hardware " 136 | "Compatibility Publisher"); 137 | EXPECT_STREQ( 138 | cert->issuer, 139 | "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Windows Third Party " 140 | "Component CA 2012"); 141 | 142 | //**************************// 143 | // Check the 2. certificate // 144 | cert = first_sig->certs->certs[1]; 145 | ASSERT_TRUE(cert->sha1.data); 146 | ASSERT_EQ(cert->sha1.len, 20); 147 | unsigned char second_cert_sha1[20] = {0x77, 0xa1, 0x0e, 0xbf, 0x07, 0x54, 0x27, 148 | 0x25, 0x21, 0x8c, 0xd8, 0x3a, 0x01, 0xb5, 149 | 0x21, 0xc5, 0x7b, 0xc6, 0x7f, 0x73}; 150 | EXPECT_TRUE(std::memcmp(second_cert_sha1, cert->sha1.data, 20) == 0); 151 | 152 | //**************************// 153 | // Check the 3. certificate // 154 | cert = first_sig->certs->certs[1]; 155 | ASSERT_TRUE(cert->sha1.data); 156 | ASSERT_EQ(cert->sha1.len, 20); 157 | unsigned char third_cert_sha1[20] = {0x9a, 0xb3, 0xfa, 0x0a, 0x1a, 0xdb, 0xcf, 158 | 0x46, 0xb1, 0xee, 0xce, 0x7b, 0x9f, 0x93, 159 | 0xe8, 0xa7, 0x75, 0x42, 0xf2, 0x0c}; 160 | EXPECT_TRUE(std::memcmp(second_cert_sha1, cert->sha1.data, 20) == 0); 161 | 162 | //**************************// 163 | // Check the 4. certificate // 164 | cert = first_sig->certs->certs[1]; 165 | ASSERT_TRUE(cert->sha1.data); 166 | ASSERT_EQ(cert->sha1.len, 20); 167 | unsigned char fourth_cert_sha1[20] = {0x2a, 0xa7, 0x52, 0xfe, 0x64, 0xc4, 0x9a, 168 | 0xbe, 0x82, 0x91, 0x3c, 0x46, 0x35, 0x29, 169 | 0xcf, 0x10, 0xff, 0x2f, 0x04, 0xee}; 170 | EXPECT_TRUE(std::memcmp(second_cert_sha1, cert->sha1.data, 20) == 0); 171 | 172 | //**************************// 173 | // Check the Counter signature // 174 | const Countersignature *countersig = first_sig->countersigs->counters[0]; 175 | 176 | EXPECT_EQ(countersig->verify_flags, COUNTERSIGNATURE_VFY_VALID); 177 | EXPECT_STREQ(countersig->digest_alg, "sha256"); 178 | EXPECT_EQ(countersig->sign_time, 1629165693); 179 | unsigned char first_countersig_digest[32] = {0xed, 0xdf, 0x8a, 0x45, 0x34, 0x0e, 0x16, 0xb3, 180 | 0x55, 0x6a, 0x8e, 0x52, 0xb3, 0xfc, 0xd2, 0xe7, 181 | 0x3c, 0x5c, 0x47, 0xd3, 0x6a, 0xa6, 0x71, 0x4f, 182 | 0xfe, 0xef, 0x2c, 0x19, 0x60, 0x37, 0x67, 0x6f}; 183 | ASSERT_TRUE(countersig->digest.data); 184 | ASSERT_EQ(countersig->digest.len, 32); 185 | EXPECT_TRUE(std::memcmp(first_countersig_digest, countersig->digest.data, 32) == 0); 186 | 187 | ASSERT_TRUE(countersig->chain); 188 | EXPECT_EQ(countersig->chain->count, 2); 189 | 190 | //**************************// 191 | // Check the 1. certificate // 192 | cert = countersig->chain->certs[0]; 193 | ASSERT_TRUE(cert->sha1.data); 194 | ASSERT_EQ(cert->sha1.len, 20); 195 | unsigned char first_countercert_sha1[20] = {0x9a, 0xb3, 0xfa, 0x0a, 0x1a, 0xdb, 0xcf, 196 | 0x46, 0xb1, 0xee, 0xce, 0x7b, 0x9f, 0x93, 197 | 0xe8, 0xa7, 0x75, 0x42, 0xf2, 0x0c}; 198 | EXPECT_TRUE(std::memcmp(first_countercert_sha1, cert->sha1.data, 20) == 0); 199 | ASSERT_EQ(cert->sha256.len, 32); 200 | unsigned char first_countercert_sha256[32] = {0x8a, 0xaa, 0x18, 0x95, 0xfb, 0x3c, 0x0d, 0x0e, 201 | 0xba, 0x54, 0xec, 0x34, 0x41, 0xec, 0xc8, 0xb9, 202 | 0xef, 0x18, 0xba, 0x18, 0x13, 0x58, 0xb0, 0x68, 203 | 0xe0, 0x66, 0xaa, 0xb6, 0xa9, 0x53, 0x0a, 0x32}; 204 | EXPECT_TRUE(std::memcmp(first_countercert_sha256, cert->sha256.data, 32) == 0); 205 | 206 | EXPECT_EQ(cert->version, 2); 207 | EXPECT_STREQ( 208 | cert->subject, 209 | "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/OU=Microsoft Operations Puerto " 210 | "Rico/OU=Thales TSS ESN:32BD-E3D5-3B1D/CN=Microsoft Time-Stamp Service"); 211 | EXPECT_STREQ( 212 | cert->issuer, 213 | "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Time-Stamp PCA 2010"); 214 | EXPECT_EQ(cert->not_after, 1649703742); 215 | EXPECT_EQ(cert->not_before, 1610650942); 216 | EXPECT_STREQ( 217 | cert->key, 218 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA74ah1Pa5wvcyvYNCy/" 219 | "YQs1tK8rIGlh1Qq1QFaJmYVXLXykb+m5yCStzmL227wJjsalZX8JA2YcbaZV5Icwm9vAJz8AC/sk/" 220 | "dsUK3pmDvkhtVI04YDV6otuZCILpQB9Ipcs3d0e1Dl2KKFvdibOk0/0rRxU9l+/" 221 | "Yxeb5lVTRERLxzI+Rd6Xv5QQYT6Sp2IE0N1vzIFd3yyO773T5XifNgL5lZbtIUnYUVmUBKlVoemO/" 222 | "54aiFeVBpIG+" 223 | "YzhDTF7cuHNAzxWIbP1wt4VIqAV9JjuqLMvvBSD56pi8NTKM9fxrERAeaTS2HbfBYfmnRZ27Czjeo0ijQ5DSZGi0Er" 224 | "vWfKQIDAQAB"); 225 | EXPECT_STREQ(cert->serial, "33:00:00:01:62:d0:fe:02:f3:01:e5:cd:49:00:00:00:00:01:62"); 226 | EXPECT_STREQ(cert->sig_alg, "sha256WithRSAEncryption"); 227 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 228 | 229 | //**************************// 230 | // Check the 2. certificate // 231 | cert = countersig->chain->certs[1]; 232 | ASSERT_TRUE(cert->sha1.data); 233 | ASSERT_EQ(cert->sha1.len, 20); 234 | unsigned char second_countercert_sha1[20] = {0x2a, 0xa7, 0x52, 0xfe, 0x64, 0xc4, 0x9a, 235 | 0xbe, 0x82, 0x91, 0x3c, 0x46, 0x35, 0x29, 236 | 0xcf, 0x10, 0xff, 0x2f, 0x04, 0xee}; 237 | EXPECT_TRUE(std::memcmp(second_countercert_sha1, cert->sha1.data, 20) == 0); 238 | 239 | ASSERT_TRUE(cert->sha256.data); 240 | ASSERT_EQ(cert->sha256.len, 32); 241 | unsigned char second_countercert_sha256[32] = {0x86, 0xec, 0x11, 0x8d, 0x1e, 0xe6, 0x96, 0x70, 242 | 0xa4, 0x6e, 0x2b, 0xe2, 0x9c, 0x4b, 0x42, 0x08, 243 | 0xbe, 0x04, 0x3e, 0x36, 0x60, 0x0d, 0x4e, 0x1d, 244 | 0xd3, 0xf3, 0xd5, 0x15, 0xca, 0x11, 0x90, 0x20}; 245 | EXPECT_TRUE(std::memcmp(second_countercert_sha256, cert->sha256.data, 32) == 0); 246 | 247 | EXPECT_EQ(cert->version, 2); 248 | EXPECT_STREQ( 249 | cert->subject, 250 | "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Time-Stamp PCA 2010"); 251 | EXPECT_STREQ( 252 | cert->issuer, 253 | "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Root Certificate " 254 | "Authority 2010"); 255 | EXPECT_EQ(cert->not_after, 1751406415); 256 | EXPECT_EQ(cert->not_before, 1278020215); 257 | EXPECT_STREQ( 258 | cert->key, 259 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/" 260 | "X6f2mUa3RUENWlCgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBED/" 261 | "FgiIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50YWeRX4FUsc+" 262 | "TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd/XcfPfBXday9ikJNQFHRD5wGPmd/" 263 | "9WbAA5ZEfu/QS/" 264 | "1u5ZrKsajyeioKMfDaTgaRtogINeh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQAB"); 265 | EXPECT_STREQ(cert->serial, "61:09:81:2a:00:00:00:00:00:02"); 266 | EXPECT_STREQ(cert->sig_alg, "sha256WithRSAEncryption"); 267 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 268 | 269 | authenticode_array_free(auth); 270 | } 271 | -------------------------------------------------------------------------------- /src/certificate.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 Avast Software 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | #include "certificate.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "helper.h" 33 | 34 | #if OPENSSL_VERSION_NUMBER >= 0x3000000fL 35 | /* Removes any escaping \/ -> / that is happening with oneline() functions 36 | from OpenSSL 3.0 */ 37 | static void parse_oneline_string(char* string) 38 | { 39 | size_t len = strlen(string); 40 | char* tmp = string; 41 | while (true) { 42 | char* ptr = strstr(tmp, "\\/"); 43 | if (!ptr) 44 | break; 45 | 46 | memmove(ptr, ptr + 1, strlen(ptr + 1)); 47 | tmp = ptr + 1; 48 | len--; 49 | } 50 | 51 | string[len] = 0; 52 | } 53 | #endif 54 | 55 | static void parse_name_attributes(X509_NAME* raw, Attributes* attr) 56 | { 57 | if (!raw || !attr) 58 | return; 59 | 60 | int entryCount = X509_NAME_entry_count(raw); 61 | for (int i = entryCount - 1; i >= 0; --i) { 62 | X509_NAME_ENTRY* entryName = X509_NAME_get_entry(raw, i); 63 | ASN1_STRING* asn1String = X509_NAME_ENTRY_get_data(entryName); 64 | 65 | const char* key = OBJ_nid2sn(OBJ_obj2nid(X509_NAME_ENTRY_get_object(entryName))); 66 | 67 | ByteArray array = {0}; 68 | if (byte_array_init(&array, asn1String->data, asn1String->length) == -1) 69 | break; 70 | 71 | if (strcmp(key, "C") == 0 && !attr->country.data) 72 | attr->country = array; 73 | else if (strcmp(key, "O") == 0 && !attr->organization.data) 74 | attr->organization = array; 75 | else if (strcmp(key, "OU") == 0 && !attr->organizationalUnit.data) 76 | attr->organizationalUnit = array; 77 | else if (strcmp(key, "dnQualifier") == 0 && !attr->nameQualifier.data) 78 | attr->nameQualifier = array; 79 | else if (strcmp(key, "ST") == 0 && !attr->state.data) 80 | attr->state = array; 81 | else if (strcmp(key, "CN") == 0 && !attr->commonName.data) 82 | attr->commonName = array; 83 | else if (strcmp(key, "serialNumber") == 0 && !attr->serialNumber.data) 84 | attr->serialNumber = array; 85 | else if (strcmp(key, "L") == 0 && !attr->locality.data) 86 | attr->locality = array; 87 | else if (strcmp(key, "title") == 0 && !attr->title.data) 88 | attr->title = array; 89 | else if (strcmp(key, "SN") == 0 && !attr->surname.data) 90 | attr->surname = array; 91 | else if (strcmp(key, "GN") == 0 && !attr->givenName.data) 92 | attr->givenName = array; 93 | else if (strcmp(key, "initials") == 0 && !attr->initials.data) 94 | attr->initials = array; 95 | else if (strcmp(key, "pseudonym") == 0 && !attr->pseudonym.data) 96 | attr->pseudonym = array; 97 | else if (strcmp(key, "generationQualifier") == 0 && !attr->generationQualifier.data) 98 | attr->generationQualifier = array; 99 | else if (strcmp(key, "emailAddress") == 0 && !attr->emailAddress.data) 100 | attr->emailAddress = array; 101 | else 102 | free(array.data); 103 | } 104 | } 105 | 106 | /* Reconstructs signers certificate chain */ 107 | CertificateArray* parse_signer_chain(X509* signCert, STACK_OF(X509) * certs) 108 | { 109 | if (!signCert || !certs) 110 | return NULL; 111 | 112 | X509_STORE* store = X509_STORE_new(); 113 | if (!store) 114 | return NULL; 115 | 116 | X509_STORE_CTX* storeCtx = X509_STORE_CTX_new(); 117 | if (!storeCtx) { 118 | X509_STORE_CTX_free(storeCtx); 119 | return NULL; 120 | } 121 | 122 | X509_STORE_CTX_init(storeCtx, store, signCert, certs); 123 | 124 | /* I can't find ability to use this function for static verification with missing trust anchors, 125 | * because roots are generally not part of the PKCS7 signatures, so the return value is 126 | * currently ignored and the function is only used to build the certificate chain */ 127 | X509_verify_cert(storeCtx); 128 | 129 | STACK_OF(X509)* chain = X509_STORE_CTX_get_chain(storeCtx); 130 | 131 | int certCount = sk_X509_num(chain); 132 | 133 | CertificateArray* result = (CertificateArray*)calloc(1, sizeof(*result)); 134 | if (!result) 135 | goto error; 136 | 137 | result->certs = (Certificate**)calloc(certCount, sizeof(Certificate*)); 138 | if (!result->certs) 139 | goto error; 140 | 141 | /* Convert each certificate to internal representation */ 142 | for (int i = 0; i < certCount; ++i) { 143 | Certificate* cert = certificate_new(sk_X509_value(chain, i)); 144 | if (!cert) 145 | goto error; 146 | 147 | result->certs[i] = cert; 148 | result->count++; 149 | } 150 | 151 | X509_STORE_free(store); 152 | X509_STORE_CTX_free(storeCtx); 153 | return result; 154 | 155 | error: /* In case of error, return nothing */ 156 | if (result) { 157 | for (size_t i = 0; i < result->count; ++i) { 158 | certificate_free(result->certs[i]); 159 | } 160 | free(result->certs); 161 | free(result); 162 | } 163 | X509_STORE_free(store); 164 | X509_STORE_CTX_free(storeCtx); 165 | 166 | return NULL; 167 | } 168 | 169 | /* Taken from YARA for compatibility */ 170 | static char* integer_to_serial(ASN1_INTEGER* serial) 171 | { 172 | int bytes = i2d_ASN1_INTEGER(serial, NULL); 173 | 174 | char* res = NULL; 175 | /* According to X.509 specification the maximum length for the 176 | * serial number is 20 octets. Add two bytes to account for 177 | * DER type and length information. */ 178 | if (bytes < 2 || bytes > 22) 179 | return NULL; 180 | 181 | /* Now that we know the size of the serial number allocate enough 182 | * space to hold it, and use i2d_ASN1_INTEGER() one last time to 183 | * hold it in the allocated buffer. */ 184 | uint8_t* serial_der = (uint8_t*)malloc(bytes); 185 | if (!serial_der) 186 | return NULL; 187 | 188 | uint8_t* serial_bytes; 189 | 190 | bytes = i2d_ASN1_INTEGER(serial, &serial_der); 191 | 192 | /* i2d_ASN1_INTEGER() moves the pointer as it writes into 193 | serial_bytes. Move it back. */ 194 | serial_der -= bytes; 195 | 196 | /* Skip over DER type, length information */ 197 | serial_bytes = serial_der + 2; 198 | bytes -= 2; 199 | 200 | /* Also allocate space to hold the "common" string format: 201 | * 00:01:02:03:04... 202 | * 203 | * For each byte in the serial to convert to hexlified format we 204 | * need three bytes, two for the byte itself and one for colon. 205 | * The last one doesn't have the colon, but the extra byte is used 206 | * for the NULL terminator. */ 207 | res = (char*)malloc(bytes * 3); 208 | if (res) { 209 | for (int i = 0; i < bytes; i++) { 210 | /* Don't put the colon on the last one. */ 211 | if (i < bytes - 1) 212 | snprintf(res + 3 * i, 4, "%02x:", serial_bytes[i]); 213 | else 214 | snprintf(res + 3 * i, 3, "%02x", serial_bytes[i]); 215 | } 216 | } 217 | free(serial_der); 218 | 219 | return (char*)res; 220 | } 221 | 222 | /* Converts the pubkey to pem, which is just 223 | * Base64 encoding of the DER representation */ 224 | static char* pubkey_to_pem(EVP_PKEY* pubkey) 225 | { 226 | uint8_t* der = NULL; 227 | int len = i2d_PUBKEY(pubkey, &der); /* Convert to DER */ 228 | if (len <= 0) 229 | return NULL; 230 | 231 | /* Approximate the result length (padding, newlines, 4 out bytes for every 3 in) */ 232 | uint8_t* result = (uint8_t*)malloc(len * 3 / 2); 233 | if (!result) { 234 | OPENSSL_free(der); 235 | return NULL; 236 | } 237 | 238 | /* Base64 encode the DER data */ 239 | EVP_ENCODE_CTX* ctx = EVP_ENCODE_CTX_new(); 240 | if (!ctx) { 241 | OPENSSL_free(der); 242 | free(result); 243 | return NULL; 244 | } 245 | 246 | int resultLen = 0; 247 | int tmp = 0; 248 | EVP_EncodeInit(ctx); 249 | EVP_EncodeUpdate(ctx, result, &tmp, der, len); 250 | resultLen += tmp; 251 | EVP_EncodeFinal(ctx, result + resultLen, &tmp); 252 | resultLen += tmp; 253 | 254 | EVP_ENCODE_CTX_free(ctx); 255 | OPENSSL_free(der); 256 | 257 | /* Remove all newlines from the encoded base64 258 | * resultLen is excluding NULL terminator */ 259 | for (int i = 0; result[i] != 0; i++) { 260 | if (result[i] == '\n') 261 | memmove(result + i, result + i + 1, resultLen - i); 262 | } 263 | 264 | return (char*)result; 265 | } 266 | 267 | Certificate* certificate_new(X509* x509) 268 | { 269 | Certificate* result = (Certificate*)calloc(1, sizeof(*result)); 270 | if (!result) 271 | return NULL; 272 | 273 | /* Calculate SHA1 and SHA256 digests of the X509 structure */ 274 | result->sha1.data = (uint8_t*)malloc(SHA_DIGEST_LENGTH); 275 | if (result->sha1.data) { 276 | X509_digest(x509, EVP_sha1(), result->sha1.data, NULL); 277 | result->sha1.len = SHA_DIGEST_LENGTH; 278 | } 279 | 280 | result->sha256.data = (uint8_t*)malloc(SHA256_DIGEST_LENGTH); 281 | if (result->sha256.data) { 282 | X509_digest(x509, EVP_sha256(), result->sha256.data, NULL); 283 | result->sha256.len = SHA256_DIGEST_LENGTH; 284 | } 285 | 286 | /* 256 bytes should be enough for any name */ 287 | char buffer[256]; 288 | 289 | /* X509_NAME_online is deprecated and shouldn't be used per OpenSSL docs 290 | * but we want to comply with existing YARA code */ 291 | X509_NAME* issuerName = X509_get_issuer_name(x509); 292 | X509_NAME_oneline(issuerName, buffer, sizeof(buffer)); 293 | 294 | result->issuer = strdup(buffer); 295 | /* This is a little ugly hack for 3.0 compatibility */ 296 | #if OPENSSL_VERSION_NUMBER >= 0x3000000fL 297 | parse_oneline_string(result->issuer); 298 | #endif 299 | 300 | X509_NAME* subjectName = X509_get_subject_name(x509); 301 | X509_NAME_oneline(subjectName, buffer, sizeof(buffer)); 302 | result->subject = strdup(buffer); 303 | #if OPENSSL_VERSION_NUMBER >= 0x3000000fL 304 | parse_oneline_string(result->subject); 305 | #endif 306 | 307 | parse_name_attributes(issuerName, &result->issuer_attrs); 308 | parse_name_attributes(subjectName, &result->subject_attrs); 309 | 310 | result->version = X509_get_version(x509); 311 | result->serial = integer_to_serial(X509_get_serialNumber(x509)); 312 | result->not_after = ASN1_TIME_to_int64_t(X509_get0_notAfter(x509)); 313 | result->not_before = ASN1_TIME_to_int64_t(X509_get0_notBefore(x509)); 314 | int sig_nid = X509_get_signature_nid(x509); 315 | result->sig_alg = strdup(OBJ_nid2ln(sig_nid)); 316 | 317 | OBJ_obj2txt(buffer, sizeof(buffer), OBJ_nid2obj(sig_nid), 1); 318 | result->sig_alg_oid = strdup(buffer); 319 | 320 | EVP_PKEY* pkey = X509_get0_pubkey(x509); 321 | if (pkey) { 322 | result->key = pubkey_to_pem(pkey); 323 | #if OPENSSL_VERSION_NUMBER >= 0x3000000fL 324 | result->key_alg = strdup(OBJ_nid2sn(EVP_PKEY_get_base_id(pkey))); 325 | #else 326 | result->key_alg = strdup(OBJ_nid2sn(EVP_PKEY_base_id(pkey))); 327 | #endif 328 | } 329 | 330 | return result; 331 | } 332 | 333 | void attributes_copy(Attributes* dst, Attributes* src) 334 | { 335 | byte_array_init(&dst->country, src->country.data, src->country.len); 336 | byte_array_init(&dst->organization, src->organization.data, src->organization.len); 337 | byte_array_init( 338 | &dst->organizationalUnit, src->organizationalUnit.data, src->organizationalUnit.len); 339 | byte_array_init(&dst->nameQualifier, src->nameQualifier.data, src->nameQualifier.len); 340 | byte_array_init(&dst->state, src->state.data, src->state.len); 341 | byte_array_init(&dst->commonName, src->commonName.data, src->commonName.len); 342 | byte_array_init(&dst->serialNumber, src->serialNumber.data, src->serialNumber.len); 343 | byte_array_init(&dst->locality, src->locality.data, src->locality.len); 344 | byte_array_init(&dst->title, src->title.data, src->title.len); 345 | byte_array_init(&dst->surname, src->surname.data, src->surname.len); 346 | byte_array_init(&dst->givenName, src->givenName.data, src->givenName.len); 347 | byte_array_init(&dst->initials, src->initials.data, src->initials.len); 348 | byte_array_init(&dst->pseudonym, src->pseudonym.data, src->pseudonym.len); 349 | byte_array_init( 350 | &dst->generationQualifier, src->generationQualifier.data, src->generationQualifier.len); 351 | byte_array_init(&dst->emailAddress, src->emailAddress.data, src->emailAddress.len); 352 | } 353 | 354 | /* Parses X509* certs into internal representation and inserts into CertificateArray 355 | * Array is assumed to have enough space to hold all certificates storted in the STACK */ 356 | void parse_x509_certificates(const STACK_OF(X509) * certs, CertificateArray* result) 357 | { 358 | int certCount = sk_X509_num(certs); 359 | int i = 0; 360 | for (; i < certCount; ++i) { 361 | Certificate* cert = certificate_new(sk_X509_value(certs, i)); 362 | if (!cert) 363 | break; 364 | 365 | /* Write to the result */ 366 | result->certs[i] = cert; 367 | } 368 | result->count = i; 369 | } 370 | 371 | /* Creates deep copy of a certificate */ 372 | Certificate* certificate_copy(Certificate* cert) 373 | { 374 | if (!cert) 375 | return NULL; 376 | 377 | Certificate* result = (Certificate*)calloc(1, sizeof(*result)); 378 | if (!result) 379 | return NULL; 380 | 381 | result->version = cert->version; 382 | result->issuer = cert->issuer ? strdup(cert->issuer) : NULL; 383 | result->subject = cert->subject ? strdup(cert->subject) : NULL; 384 | result->serial = cert->serial ? strdup(cert->serial) : NULL; 385 | result->not_after = cert->not_after; 386 | result->not_before = cert->not_before; 387 | result->sig_alg = cert->sig_alg ? strdup(cert->sig_alg) : NULL; 388 | result->sig_alg_oid = cert->sig_alg_oid ? strdup(cert->sig_alg_oid) : NULL; 389 | result->key_alg = cert->key_alg ? strdup(cert->key_alg) : NULL; 390 | result->key = cert->key ? strdup(cert->key) : NULL; 391 | byte_array_init(&result->sha1, cert->sha1.data, cert->sha1.len); 392 | byte_array_init(&result->sha256, cert->sha256.data, cert->sha256.len); 393 | attributes_copy(&result->issuer_attrs, &cert->issuer_attrs); 394 | attributes_copy(&result->subject_attrs, &cert->subject_attrs); 395 | 396 | return result; 397 | } 398 | 399 | /* Moves certificates from src to dst, returns 0 on success, 400 | * else 1. If error occurs, arguments are unchanged */ 401 | int certificate_array_move(CertificateArray* dst, CertificateArray* src) 402 | { 403 | if (!dst || !src) 404 | return 1; 405 | 406 | if (!src->certs || !src->count) 407 | return 0; 408 | 409 | size_t newCount = dst->count + src->count; 410 | 411 | Certificate** tmp = (Certificate**)realloc(dst->certs, newCount * sizeof(Certificate*)); 412 | if (!tmp) 413 | return 1; 414 | 415 | dst->certs = tmp; 416 | 417 | for (size_t i = 0; i < src->count; ++i) 418 | dst->certs[i + dst->count] = src->certs[i]; 419 | 420 | dst->count = newCount; 421 | 422 | free(src->certs); 423 | src->certs = NULL; 424 | src->count = 0; 425 | 426 | return 0; 427 | } 428 | 429 | /* Copies certificates from src and appends to dst, returns 0 on success, 430 | * else 1. If error occurs, arguments are unchanged */ 431 | int certificate_array_append(CertificateArray* dst, CertificateArray* src) 432 | { 433 | if (!dst || !src) 434 | return 1; 435 | 436 | if (!src->certs || !src->count) 437 | return 0; 438 | 439 | size_t newCount = dst->count + src->count; 440 | 441 | Certificate** tmp = (Certificate**)realloc(dst->certs, newCount * sizeof(Certificate*)); 442 | if (!tmp) 443 | return 1; 444 | 445 | dst->certs = tmp; 446 | 447 | for (size_t i = 0; i < src->count; ++i) 448 | dst->certs[i + dst->count] = certificate_copy(src->certs[i]); 449 | 450 | dst->count = newCount; 451 | 452 | return 0; 453 | } 454 | 455 | /* Allocates empty certificate array with reserved space for certCount certs */ 456 | CertificateArray* certificate_array_new(int certCount) 457 | { 458 | CertificateArray* arr = (CertificateArray*)malloc(sizeof(*arr)); 459 | if (!arr) 460 | return NULL; 461 | 462 | arr->certs = (Certificate**)malloc(sizeof(Certificate*) * certCount); 463 | if (!arr->certs) { 464 | free(arr); 465 | return NULL; 466 | } 467 | 468 | arr->count = certCount; 469 | 470 | return arr; 471 | } 472 | 473 | static void certificate_attributes_free(Attributes attrs) 474 | { 475 | free(attrs.country.data); 476 | free(attrs.organization.data); 477 | free(attrs.organizationalUnit.data); 478 | free(attrs.nameQualifier.data); 479 | free(attrs.state.data); 480 | free(attrs.commonName.data); 481 | free(attrs.serialNumber.data); 482 | free(attrs.locality.data); 483 | free(attrs.title.data); 484 | free(attrs.surname.data); 485 | free(attrs.givenName.data); 486 | free(attrs.initials.data); 487 | free(attrs.pseudonym.data); 488 | free(attrs.generationQualifier.data); 489 | free(attrs.emailAddress.data); 490 | } 491 | 492 | void certificate_free(Certificate* cert) 493 | { 494 | if (cert) { 495 | free(cert->issuer); 496 | free(cert->subject); 497 | free(cert->sig_alg); 498 | free(cert->sig_alg_oid); 499 | free(cert->key_alg); 500 | free(cert->key); 501 | free(cert->sha1.data); 502 | free(cert->sha256.data); 503 | free(cert->serial); 504 | certificate_attributes_free(cert->issuer_attrs); 505 | certificate_attributes_free(cert->subject_attrs); 506 | free(cert); 507 | } 508 | } 509 | 510 | void certificate_array_free(CertificateArray* arr) 511 | { 512 | if (arr) { 513 | for (size_t i = 0; i < arr->count; ++i) { 514 | certificate_free(arr->certs[i]); 515 | } 516 | free(arr->certs); 517 | free(arr); 518 | } 519 | } 520 | -------------------------------------------------------------------------------- /src/authenticode.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 Avast Software 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | 38 | #include "certificate.h" 39 | #include "countersignature.h" 40 | #include "helper.h" 41 | #include "structs.h" 42 | 43 | #define MAX_NESTED_COUNT 16 44 | 45 | /* Moves signatures from src to dst, returns 0 on success, 46 | * else 1. If error occurs, arguments are unchanged */ 47 | static int authenticode_array_move(AuthenticodeArray* dst, AuthenticodeArray* src) 48 | { 49 | size_t newCount = dst->count + src->count; 50 | 51 | Authenticode** tmp = (Authenticode**)realloc(dst->signatures, newCount * sizeof(Authenticode*)); 52 | if (!tmp) 53 | return 1; 54 | 55 | dst->signatures = tmp; 56 | 57 | for (size_t i = 0; i < src->count; ++i) 58 | dst->signatures[i + dst->count] = src->signatures[i]; 59 | 60 | dst->count = newCount; 61 | 62 | free(src->signatures); 63 | src->signatures = NULL; 64 | src->count = 0; 65 | 66 | return 0; 67 | } 68 | 69 | static SpcIndirectDataContent* get_content(PKCS7* content) 70 | { 71 | if (!content) 72 | return NULL; 73 | 74 | if (OBJ_obj2nid(content->type) != OBJ_txt2nid(NID_spc_indirect_data)) 75 | return NULL; 76 | 77 | SpcIndirectDataContent* spcContent = SpcIndirectDataContent_new(); 78 | if (!spcContent) 79 | return NULL; 80 | 81 | int len = content->d.other->value.sequence->length; 82 | const uint8_t* data = content->d.other->value.sequence->data; 83 | 84 | d2i_SpcIndirectDataContent(&spcContent, &data, len); 85 | 86 | return spcContent; 87 | } 88 | 89 | static char* parse_program_name(ASN1_TYPE* spcAttr) 90 | { 91 | const uint8_t* spcData = spcAttr->value.sequence->data; 92 | int spcLen = spcAttr->value.sequence->length; 93 | SpcSpOpusInfo* spcInfo = d2i_SpcSpOpusInfo(NULL, &spcData, spcLen); 94 | if (!spcInfo) 95 | return NULL; 96 | 97 | char* result = NULL; 98 | 99 | if (spcInfo->programName) { 100 | uint8_t* data = NULL; 101 | /* Should be Windows UTF16..., try to convert it to UTF8 */ 102 | int nameLen = ASN1_STRING_to_UTF8(&data, spcInfo->programName->value.unicode); 103 | if (nameLen >= 0 && nameLen < spcLen) { 104 | result = (char*)malloc(nameLen + 1); 105 | if (result) { 106 | memcpy(result, data, nameLen); 107 | result[nameLen] = 0; 108 | } 109 | OPENSSL_free(data); 110 | } 111 | } 112 | 113 | SpcSpOpusInfo_free(spcInfo); 114 | return result; 115 | } 116 | 117 | static void parse_nested_authenticode(PKCS7_SIGNER_INFO* si, AuthenticodeArray* result) 118 | { 119 | STACK_OF(X509_ATTRIBUTE)* attrs = PKCS7_get_attributes(si); 120 | int idx = X509at_get_attr_by_NID(attrs, OBJ_txt2nid(NID_spc_nested_signature), -1); 121 | X509_ATTRIBUTE* attr = X509at_get_attr(attrs, idx); 122 | 123 | int attrCount = X509_ATTRIBUTE_count(attr); 124 | if (!attrCount) 125 | return; 126 | 127 | /* Limit the maximum amount of nested attributes to be safe from malformed samples */ 128 | attrCount = attrCount > MAX_NESTED_COUNT ? MAX_NESTED_COUNT : attrCount; 129 | 130 | for (int i = 0; i < attrCount; ++i) { 131 | ASN1_TYPE* nested = X509_ATTRIBUTE_get0_type(attr, i); 132 | if (nested == NULL) 133 | break; 134 | int len = nested->value.sequence->length; 135 | const uint8_t* data = nested->value.sequence->data; 136 | AuthenticodeArray* auth = authenticode_new(data, len); 137 | if (!auth) 138 | continue; 139 | 140 | authenticode_array_move(result, auth); 141 | authenticode_array_free(auth); 142 | } 143 | } 144 | 145 | static void parse_pkcs9_countersig(PKCS7* p7, Authenticode* auth) 146 | { 147 | PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value(PKCS7_get_signer_info(p7), 0); 148 | 149 | STACK_OF(X509_ATTRIBUTE)* attrs = PKCS7_get_attributes(si); 150 | 151 | int idx = X509at_get_attr_by_NID(attrs, NID_pkcs9_countersignature, -1); 152 | X509_ATTRIBUTE* attr = X509at_get_attr(attrs, idx); 153 | 154 | int attrCount = X509_ATTRIBUTE_count(attr); 155 | if (!attrCount) 156 | return; 157 | 158 | /* Limit the maximum amount of nested attributes to be safe from malformed samples */ 159 | attrCount = attrCount > MAX_NESTED_COUNT ? MAX_NESTED_COUNT : attrCount; 160 | 161 | for (int i = 0; i < attrCount; ++i) { 162 | ASN1_TYPE* nested = X509_ATTRIBUTE_get0_type(attr, i); 163 | if (nested == NULL) 164 | break; 165 | int len = nested->value.sequence->length; 166 | const uint8_t* data = nested->value.sequence->data; 167 | 168 | Countersignature* sig = pkcs9_countersig_new(data, len, p7->d.sign->cert, si->enc_digest); 169 | if (!sig) 170 | continue; 171 | 172 | countersignature_array_insert(auth->countersigs, sig); 173 | } 174 | } 175 | 176 | static void parse_ms_countersig(PKCS7* p7, Authenticode* auth) 177 | { 178 | PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value(PKCS7_get_signer_info(p7), 0); 179 | 180 | STACK_OF(X509_ATTRIBUTE)* attrs = PKCS7_get_attributes(si); 181 | 182 | int idx = X509at_get_attr_by_NID(attrs, OBJ_txt2nid(NID_spc_ms_countersignature), -1); 183 | X509_ATTRIBUTE* attr = X509at_get_attr(attrs, idx); 184 | 185 | int attrCount = X509_ATTRIBUTE_count(attr); 186 | if (!attrCount) 187 | return; 188 | 189 | /* Limit the maximum amount of nested attributes to be safe from malformed samples */ 190 | attrCount = attrCount > MAX_NESTED_COUNT ? MAX_NESTED_COUNT : attrCount; 191 | 192 | for (int i = 0; i < attrCount; ++i) { 193 | ASN1_TYPE* nested = X509_ATTRIBUTE_get0_type(attr, i); 194 | if (nested == NULL) 195 | break; 196 | int len = nested->value.sequence->length; 197 | const uint8_t* data = nested->value.sequence->data; 198 | 199 | Countersignature* csig = ms_countersig_new(data, len, si->enc_digest); 200 | if (!csig) 201 | return; 202 | 203 | countersignature_array_insert(auth->countersigs, csig); 204 | /* Because MS TimeStamp countersignature has it's own SET of certificates 205 | * extract it back into parent signature for consistency with PKCS9 */ 206 | certificate_array_append(auth->certs, csig->certs); 207 | } 208 | } 209 | 210 | static bool authenticode_verify(PKCS7* p7, PKCS7_SIGNER_INFO* si, X509* signCert) 211 | { 212 | const uint8_t* contentData = p7->d.sign->contents->d.other->value.sequence->data; 213 | long contentLen = p7->d.sign->contents->d.other->value.sequence->length; 214 | 215 | uint64_t version = 0; 216 | ASN1_INTEGER_get_uint64(&version, p7->d.sign->version); 217 | if (version == 1) { 218 | /* Move the pointer to the actual contents - skip OID and length */ 219 | int pclass = 0, ptag = 0; 220 | ASN1_get_object(&contentData, &contentLen, &ptag, &pclass, contentLen); 221 | } 222 | 223 | BIO* contentBio = BIO_new_mem_buf(contentData, contentLen); 224 | /* Create `digest` type BIO to calculate content digest for verification */ 225 | BIO* p7bio = PKCS7_dataInit(p7, contentBio); 226 | 227 | char buf[4096]; 228 | /* We now have to 'read' from p7bio to calculate content digest */ 229 | while (BIO_read(p7bio, buf, sizeof(buf)) > 0) 230 | continue; 231 | 232 | /* Pass it to the PKCS7_signatureVerify, to do the hard work for us */ 233 | bool isValid = PKCS7_signatureVerify(p7bio, p7, si, signCert) == 1; 234 | 235 | BIO_free_all(p7bio); 236 | 237 | return isValid; 238 | } 239 | 240 | /* Creates all the Authenticode objects so we can parse them with OpenSSL, is not thread-safe, needs 241 | * to be called once before any multi-threading environmentt - 242 | * https://github.com/openssl/openssl/issues/13524 */ 243 | void initialize_authenticode_parser() 244 | { 245 | OBJ_create("1.3.6.1.4.1.311.2.1.12", "spcSpOpusInfo", "SPC_SP_OPUS_INFO_OBJID"); 246 | OBJ_create("1.3.6.1.4.1.311.3.3.1", "spcMsCountersignature", "SPC_MICROSOFT_COUNTERSIGNATURE"); 247 | OBJ_create("1.3.6.1.4.1.311.2.4.1", "spcNestedSignature", "SPC_NESTED_SIGNATUREs"); 248 | OBJ_create("1.3.6.1.4.1.311.2.1.4", "spcIndirectData", "SPC_INDIRECT_DATA"); 249 | } 250 | 251 | /* Return array of Authenticode signatures stored in the data, there can be multiple 252 | * of signatures as Authenticode signatures are often nested through unauth attributes */ 253 | AuthenticodeArray* authenticode_new(const uint8_t* data, int32_t len) 254 | { 255 | if (!data || len <= 0) 256 | return NULL; 257 | 258 | AuthenticodeArray* result = (AuthenticodeArray*)calloc(1, sizeof(*result)); 259 | if (!result) 260 | return NULL; 261 | 262 | result->signatures = (Authenticode**)malloc(sizeof(Authenticode*)); 263 | if (!result->signatures) { 264 | free(result); 265 | return NULL; 266 | } 267 | 268 | Authenticode* auth = (Authenticode*)calloc(1, sizeof(*auth)); 269 | if (!auth) { 270 | free(result->signatures); 271 | free(result); 272 | return NULL; 273 | } 274 | 275 | result->count = 1; 276 | result->signatures[0] = auth; 277 | 278 | /* Let openssl parse the PKCS7 structure */ 279 | PKCS7* p7 = d2i_PKCS7(NULL, &data, len); 280 | if (!p7) { 281 | auth->verify_flags = AUTHENTICODE_VFY_CANT_PARSE; 282 | goto end; 283 | } 284 | 285 | /* We expect SignedData type of PKCS7 */ 286 | if (!PKCS7_type_is_signed(p7) || !p7->d.sign) { 287 | auth->verify_flags = AUTHENTICODE_VFY_WRONG_PKCS7_TYPE; 288 | goto end; 289 | } 290 | 291 | PKCS7_SIGNED* p7data = p7->d.sign; 292 | 293 | uint64_t version = 0; 294 | if (ASN1_INTEGER_get_uint64(&version, p7data->version)) 295 | auth->version = version; 296 | 297 | STACK_OF(X509)* certs = p7data->cert; 298 | 299 | auth->certs = certificate_array_new(sk_X509_num(certs)); 300 | if (!auth->certs) { 301 | auth->verify_flags = AUTHENTICODE_VFY_INTERNAL_ERROR; 302 | goto end; 303 | } 304 | parse_x509_certificates(certs, auth->certs); 305 | 306 | /* Get Signature content that contains the message digest and it's algorithm */ 307 | SpcIndirectDataContent* dataContent = get_content(p7data->contents); 308 | if (!dataContent) { 309 | auth->verify_flags = AUTHENTICODE_VFY_BAD_CONTENT; 310 | goto end; 311 | } 312 | 313 | DigestInfo* messageDigest = dataContent->messageDigest; 314 | 315 | int digestnid = OBJ_obj2nid(messageDigest->digestAlgorithm->algorithm); 316 | auth->digest_alg = strdup(OBJ_nid2ln(digestnid)); 317 | 318 | int digestLen = messageDigest->digest->length; 319 | const uint8_t* digestData = messageDigest->digest->data; 320 | byte_array_init(&auth->digest, digestData, digestLen); 321 | 322 | SpcIndirectDataContent_free(dataContent); 323 | 324 | Signer* signer = (Signer*)calloc(1, sizeof(Signer)); 325 | if (!signer) { 326 | auth->verify_flags = AUTHENTICODE_VFY_INTERNAL_ERROR; 327 | goto end; 328 | } 329 | auth->signer = signer; 330 | 331 | /* Authenticode is supposed to have only one SignerInfo value 332 | * that contains all information for actual signing purposes 333 | * and nested signatures or countersignatures */ 334 | PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value(PKCS7_get_signer_info(p7), 0); 335 | if (!si) { 336 | auth->verify_flags = AUTHENTICODE_VFY_NO_SIGNER_INFO; 337 | goto end; 338 | } 339 | 340 | auth->countersigs = (CountersignatureArray*)calloc(1, sizeof(CountersignatureArray)); 341 | if (!auth->countersigs) { 342 | auth->verify_flags = AUTHENTICODE_VFY_INTERNAL_ERROR; 343 | goto end; 344 | } 345 | /* Authenticode can contain SET of nested Authenticode signatures 346 | * and countersignatures in unauthenticated attributes */ 347 | parse_nested_authenticode(si, result); 348 | parse_pkcs9_countersig(p7, auth); 349 | parse_ms_countersig(p7, auth); 350 | 351 | /* Get the signing certificate for the first SignerInfo */ 352 | STACK_OF(X509)* signCertStack = PKCS7_get0_signers(p7, certs, 0); 353 | 354 | X509* signCert = sk_X509_value(signCertStack, 0); 355 | if (!signCert) { 356 | auth->verify_flags = AUTHENTICODE_VFY_NO_SIGNER_CERT; 357 | sk_X509_free(signCertStack); 358 | goto end; 359 | } 360 | 361 | sk_X509_free(signCertStack); 362 | 363 | signer->chain = parse_signer_chain(signCert, certs); 364 | 365 | /* Get the Signers digest of Authenticode content */ 366 | ASN1_TYPE* digest = PKCS7_get_signed_attribute(si, NID_pkcs9_messageDigest); 367 | if (!digest) { 368 | auth->verify_flags = AUTHENTICODE_VFY_DIGEST_MISSING; 369 | goto end; 370 | } 371 | 372 | digestnid = OBJ_obj2nid(si->digest_alg->algorithm); 373 | signer->digest_alg = strdup(OBJ_nid2ln(digestnid)); 374 | 375 | digestLen = digest->value.asn1_string->length; 376 | digestData = digest->value.asn1_string->data; 377 | byte_array_init(&signer->digest, digestData, digestLen); 378 | 379 | /* Authenticode stores optional programName in non-optional SpcSpOpusInfo attribute */ 380 | ASN1_TYPE* spcInfo = PKCS7_get_signed_attribute(si, OBJ_txt2nid(NID_spc_info)); 381 | if (spcInfo) 382 | signer->program_name = parse_program_name(spcInfo); 383 | 384 | /* If we got to this point, we got all we need to start verifying */ 385 | bool isValid = authenticode_verify(p7, si, signCert); 386 | if (!isValid) 387 | auth->verify_flags = AUTHENTICODE_VFY_INVALID; 388 | 389 | end: 390 | PKCS7_free(p7); 391 | return result; 392 | } 393 | 394 | static int authenticode_digest( 395 | const EVP_MD* md, 396 | const uint8_t* pe_data, 397 | uint32_t pe_hdr_offset, 398 | bool is_64bit, 399 | uint32_t cert_table_addr, 400 | uint8_t* digest) 401 | { 402 | uint32_t buffer_size = 0xFFFF; 403 | uint8_t* buffer = (uint8_t*)malloc(buffer_size); 404 | 405 | /* BIO with the file data */ 406 | BIO* bio = BIO_new_mem_buf(pe_data, cert_table_addr); 407 | 408 | EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); 409 | if (!buffer || !bio || !mdctx) 410 | goto error; 411 | 412 | if (!EVP_DigestInit(mdctx, md)) 413 | goto error; 414 | 415 | /* Calculate size of the space between file start and PE header */ 416 | /* Checksum starts at 0x58th byte of the header */ 417 | uint32_t pe_checksum_offset = pe_hdr_offset + 0x58; 418 | /* Space between DOS and PE header could have arbitrary amount of data, read in chunks */ 419 | uint32_t fpos = 0; 420 | while (fpos < pe_checksum_offset) { 421 | uint32_t len_to_read = pe_checksum_offset - fpos; 422 | if (len_to_read > buffer_size) 423 | len_to_read = buffer_size; 424 | 425 | int rlen = BIO_read(bio, buffer, len_to_read); 426 | if (rlen <= 0) 427 | goto error; 428 | 429 | if (!EVP_DigestUpdate(mdctx, buffer, rlen)) 430 | goto error; 431 | 432 | fpos += rlen; 433 | } 434 | 435 | /* Skip the checksum */ 436 | if (BIO_read(bio, buffer, 4) <= 0) 437 | goto error; 438 | 439 | /* 64bit PE file is larger than 32bit */ 440 | uint32_t pe64_extra = is_64bit ? 16 : 0; 441 | 442 | /* Read up to certificate table*/ 443 | uint32_t cert_table_offset = 0x3c + pe64_extra; 444 | 445 | if (BIO_read(bio, buffer, cert_table_offset) <= 0) 446 | goto error; 447 | 448 | if (!EVP_DigestUpdate(mdctx, buffer, cert_table_offset)) 449 | goto error; 450 | 451 | /* Skip certificate table */ 452 | if (BIO_read(bio, buffer, 8) <= 0) 453 | goto error; 454 | 455 | /* PE header with check sum + checksum + cert table offset + cert table len */ 456 | fpos = pe_checksum_offset + 4 + cert_table_offset + 8; 457 | 458 | /* Hash everything up to the signature (assuming signature is stored in the 459 | * end of the file) */ 460 | /* Read chunks of the file in case the file is large */ 461 | while (fpos < cert_table_addr) { 462 | uint32_t len_to_read = cert_table_addr - fpos; 463 | if (len_to_read > buffer_size) 464 | len_to_read = buffer_size; 465 | 466 | int rlen = BIO_read(bio, buffer, len_to_read); 467 | if (rlen <= 0) 468 | goto error; 469 | 470 | if (!EVP_DigestUpdate(mdctx, buffer, rlen)) 471 | goto error; 472 | fpos += rlen; 473 | } 474 | 475 | /* Calculate the digest, write it into digest */ 476 | if (!EVP_DigestFinal(mdctx, digest, NULL)) 477 | goto error; 478 | 479 | EVP_MD_CTX_free(mdctx); 480 | BIO_free_all(bio); 481 | free(buffer); 482 | return 0; 483 | 484 | error: 485 | EVP_MD_CTX_free(mdctx); 486 | BIO_free_all(bio); 487 | free(buffer); 488 | return 1; 489 | } 490 | 491 | AuthenticodeArray* parse_authenticode(const uint8_t* pe_data, uint64_t pe_len) 492 | { 493 | const uint64_t dos_hdr_size = 0x40; 494 | if (pe_len < dos_hdr_size) 495 | return NULL; 496 | 497 | /* Check if it has DOS signature, so we don't parse random gibberish */ 498 | uint8_t dos_prefix[] = {0x4d, 0x5a}; 499 | if (memcmp(pe_data, dos_prefix, sizeof(dos_prefix)) != 0) 500 | return NULL; 501 | 502 | /* offset to pointer in DOS header, that points to PE header */ 503 | const int pe_hdr_ptr_offset = 0x3c; 504 | /* Read the PE offset */ 505 | uint32_t pe_offset = letoh32(*(uint32_t*)(pe_data + pe_hdr_ptr_offset)); 506 | /* Offset to Magic, to know the PE class (32/64bit) */ 507 | uint32_t magic_addr = pe_offset + 0x18; 508 | 509 | if (pe_len < magic_addr + sizeof(uint16_t)) 510 | return NULL; 511 | 512 | /* Read the magic and check if we have 64bit PE */ 513 | uint16_t magic = letoh16(*(uint16_t*)(pe_data + magic_addr)); 514 | bool is_64bit = magic == 0x20b; 515 | /* If PE is 64bit, header is 16 bytes larger */ 516 | uint8_t pe64_extra = is_64bit ? 16 : 0; 517 | 518 | /* Calculate offset to certificate table directory */ 519 | uint32_t pe_cert_table_addr = pe_offset + pe64_extra + 0x98; 520 | 521 | if (pe_len < pe_cert_table_addr + 2 * sizeof(uint32_t)) 522 | return NULL; 523 | 524 | /* Use 64bit type due to the potential overflow in crafted binaries */ 525 | uint64_t cert_addr = letoh32(*(uint32_t*)(pe_data + pe_cert_table_addr)); 526 | uint64_t cert_len = letoh32(*(uint32_t*)(pe_data + pe_cert_table_addr + 4)); 527 | 528 | /* we need atleast 8 bytes to read dwLength, revision and certType */ 529 | if (cert_len < 8 || pe_len < cert_addr + 8) 530 | return NULL; 531 | 532 | uint32_t dwLength = letoh32(*(uint32_t*)(pe_data + cert_addr)); 533 | if (pe_len < cert_addr + dwLength) 534 | return NULL; 535 | /* dwLength = offsetof(WIN_CERTIFICATE, bCertificate) + (size of the variable-length binary 536 | * array contained within bCertificate) */ 537 | AuthenticodeArray* auth_array = authenticode_new(pe_data + cert_addr + 0x8, dwLength - 0x8); 538 | if (!auth_array) 539 | return NULL; 540 | 541 | /* Compare valid signatures file digests to actual file digest, to complete verification */ 542 | for (size_t i = 0; i < auth_array->count; ++i) { 543 | Authenticode* sig = auth_array->signatures[i]; 544 | 545 | const EVP_MD* md = EVP_get_digestbyname(sig->digest_alg); 546 | if (!md || !sig->digest.len || !sig->digest.data) { 547 | /* If there is an verification error, keep the first error */ 548 | if (sig->verify_flags == AUTHENTICODE_VFY_VALID) 549 | sig->verify_flags = AUTHENTICODE_VFY_UNKNOWN_ALGORITHM; 550 | 551 | continue; 552 | } 553 | 554 | #if OPENSSL_VERSION_NUMBER >= 0x3000000fL 555 | int mdlen = EVP_MD_get_size(md); 556 | #else 557 | int mdlen = EVP_MD_size(md); 558 | #endif 559 | sig->file_digest.len = mdlen; 560 | sig->file_digest.data = (uint8_t*)malloc(mdlen); 561 | if (!sig->file_digest.data) 562 | continue; 563 | 564 | if (authenticode_digest( 565 | md, pe_data, pe_offset, is_64bit, cert_addr, sig->file_digest.data)) { 566 | 567 | /* If there is an verification error, keep the first error */ 568 | if (sig->verify_flags == AUTHENTICODE_VFY_VALID) 569 | sig->verify_flags = AUTHENTICODE_VFY_INTERNAL_ERROR; 570 | break; 571 | } 572 | 573 | /* Complete the verification */ 574 | if (memcmp(sig->file_digest.data, sig->digest.data, mdlen) != 0) 575 | sig->verify_flags = AUTHENTICODE_VFY_WRONG_FILE_DIGEST; 576 | } 577 | 578 | return auth_array; 579 | } 580 | 581 | static void signer_free(Signer* si) 582 | { 583 | if (si) { 584 | free(si->digest.data); 585 | free(si->digest_alg); 586 | free(si->program_name); 587 | certificate_array_free(si->chain); 588 | free(si); 589 | } 590 | } 591 | 592 | static void authenticode_free(Authenticode* auth) 593 | { 594 | if (auth) { 595 | free(auth->digest.data); 596 | free(auth->file_digest.data); 597 | free(auth->digest_alg); 598 | signer_free(auth->signer); 599 | certificate_array_free(auth->certs); 600 | countersignature_array_free(auth->countersigs); 601 | free(auth); 602 | } 603 | } 604 | 605 | void authenticode_array_free(AuthenticodeArray* arr) 606 | { 607 | if (arr) { 608 | for (size_t i = 0; i < arr->count; ++i) { 609 | authenticode_free(arr->signatures[i]); 610 | } 611 | free(arr->signatures); 612 | free(arr); 613 | } 614 | } 615 | -------------------------------------------------------------------------------- /src/countersignature.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 Avast Software 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | #include "countersignature.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "certificate.h" 39 | #include "helper.h" 40 | #include "structs.h" 41 | 42 | struct CountersignatureImplStruct; 43 | 44 | typedef TS_TST_INFO* get_ts_tst_info_func(struct CountersignatureImplStruct*); 45 | typedef STACK_OF(X509) * get_signers_func(struct CountersignatureImplStruct*); 46 | typedef STACK_OF(X509) * get_certs_func(struct CountersignatureImplStruct*); 47 | typedef int 48 | verify_digest_func(struct CountersignatureImplStruct*, uint8_t* digest, size_t digest_size); 49 | typedef BIO* verify_signature_init_func(struct CountersignatureImplStruct*); 50 | typedef int 51 | verify_signature_finish_func(struct CountersignatureImplStruct*, BIO* bio, X509* signer); 52 | 53 | #define IMPL_FUNC_NAME(func, type) ms_countersig_impl_##func##_##type##_ 54 | 55 | #define DECLARE_FUNCS(type) \ 56 | get_ts_tst_info_func IMPL_FUNC_NAME(get_ts_tst_info, type); \ 57 | get_signers_func IMPL_FUNC_NAME(get_signers, type); \ 58 | get_certs_func IMPL_FUNC_NAME(get_certs, type); \ 59 | verify_digest_func IMPL_FUNC_NAME(verify_digest, type); \ 60 | verify_signature_init_func IMPL_FUNC_NAME(verify_signature_init, type); \ 61 | verify_signature_finish_func IMPL_FUNC_NAME(verify_signature_finish, type); 62 | 63 | DECLARE_FUNCS(pkcs7) 64 | DECLARE_FUNCS(cms) 65 | 66 | typedef struct { 67 | get_ts_tst_info_func* get_ts_tst_info; 68 | get_signers_func* get_signers; 69 | get_certs_func* get_certs; 70 | verify_digest_func* verify_digest; 71 | verify_signature_init_func* verify_signature_init; 72 | verify_signature_finish_func* verify_signature_finish; 73 | } CountersignatureImplFuncs; 74 | 75 | #define FUNC_ARRAY_NAME_FOR_IMPL(type) countersig_impl_funcs_##type##_ 76 | #define FUNC_ARRAY_FOR_IMPL(type) \ 77 | static const CountersignatureImplFuncs FUNC_ARRAY_NAME_FOR_IMPL(type) = { \ 78 | &IMPL_FUNC_NAME(get_ts_tst_info, type), \ 79 | &IMPL_FUNC_NAME(get_signers, type), \ 80 | &IMPL_FUNC_NAME(get_certs, type), \ 81 | &IMPL_FUNC_NAME(verify_digest, type), \ 82 | &IMPL_FUNC_NAME(verify_signature_init, type), \ 83 | &IMPL_FUNC_NAME(verify_signature_finish, type), \ 84 | }; 85 | 86 | FUNC_ARRAY_FOR_IMPL(pkcs7) 87 | FUNC_ARRAY_FOR_IMPL(cms) 88 | 89 | typedef enum { 90 | CS_IMPL_PKCS7, 91 | CS_IMPL_CMS, 92 | } CountersignatureImplType; 93 | 94 | typedef struct CountersignatureImplStruct { 95 | CountersignatureImplType type; 96 | const CountersignatureImplFuncs* funcs; 97 | union { 98 | PKCS7* pkcs7; 99 | CMS_ContentInfo* cms; 100 | }; 101 | // this is here to serve as a cache for CMS because the only way to obtain 102 | // certs from CMS is to use CMS_get1_certs which leaves the deallocation 103 | // to the caller but it just complicates things if you need to remember to 104 | // deallocate also certs. This makes it easier if CountersignatureImpl itself 105 | // is an owner of this thing. 106 | STACK_OF(X509) * _certs; 107 | } CountersignatureImpl; 108 | 109 | Countersignature* pkcs9_countersig_new( 110 | const uint8_t* data, long size, STACK_OF(X509) * certs, ASN1_STRING* enc_digest) 111 | { 112 | Countersignature* result = (Countersignature*)calloc(1, sizeof(*result)); 113 | if (!result) 114 | return NULL; 115 | 116 | PKCS7_SIGNER_INFO* si = d2i_PKCS7_SIGNER_INFO(NULL, &data, size); 117 | if (!si) { 118 | result->verify_flags = COUNTERSIGNATURE_VFY_CANT_PARSE; 119 | return result; 120 | } 121 | 122 | int digestnid = OBJ_obj2nid(si->digest_alg->algorithm); 123 | result->digest_alg = strdup(OBJ_nid2ln(digestnid)); 124 | 125 | const ASN1_TYPE* sign_time = PKCS7_get_signed_attribute(si, NID_pkcs9_signingTime); 126 | if (!sign_time) { 127 | result->verify_flags = COUNTERSIGNATURE_VFY_TIME_MISSING; 128 | goto end; 129 | } 130 | 131 | result->sign_time = ASN1_TIME_to_int64_t(sign_time->value.utctime); 132 | 133 | X509* signCert = X509_find_by_issuer_and_serial( 134 | certs, si->issuer_and_serial->issuer, si->issuer_and_serial->serial); 135 | if (!signCert) { 136 | result->verify_flags = COUNTERSIGNATURE_VFY_NO_SIGNER_CERT; 137 | goto end; 138 | } 139 | 140 | /* PKCS9 stores certificates in the corresponding PKCS7 it countersigns */ 141 | result->chain = parse_signer_chain(signCert, certs); 142 | 143 | /* Get digest that corresponds to decrypted encrypted digest in signature */ 144 | ASN1_TYPE* messageDigest = PKCS7_get_signed_attribute(si, NID_pkcs9_messageDigest); 145 | if (!messageDigest) { 146 | result->verify_flags = COUNTERSIGNATURE_VFY_DIGEST_MISSING; 147 | goto end; 148 | } 149 | 150 | size_t digestLen = messageDigest->value.octet_string->length; 151 | 152 | if (!digestLen) { 153 | result->verify_flags = COUNTERSIGNATURE_VFY_DIGEST_MISSING; 154 | goto end; 155 | } 156 | 157 | const EVP_MD* md = EVP_get_digestbynid(digestnid); 158 | if (!md) { 159 | result->verify_flags = COUNTERSIGNATURE_VFY_UNKNOWN_ALGORITHM; 160 | goto end; 161 | } 162 | 163 | const uint8_t* digestData = messageDigest->value.octet_string->data; 164 | byte_array_init(&result->digest, digestData, digestLen); 165 | 166 | /* By this point we all necessary things for verification 167 | * Get DER representation of the authenticated attributes to calculate its 168 | * digest that should correspond with the one encrypted in SignerInfo */ 169 | uint8_t* authAttrsData = NULL; 170 | int authAttrsLen = ASN1_item_i2d( 171 | (ASN1_VALUE*)si->auth_attr, &authAttrsData, ASN1_ITEM_rptr(PKCS7_ATTR_VERIFY)); 172 | 173 | uint8_t calc_digest[EVP_MAX_MD_SIZE]; 174 | calculate_digest(md, authAttrsData, authAttrsLen, calc_digest); 175 | OPENSSL_free(authAttrsData); 176 | 177 | /* Get public key to decrypt encrypted digest of auth attrs */ 178 | EVP_PKEY* pkey = X509_get0_pubkey(signCert); 179 | EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL); 180 | 181 | /* TODO try to get rid of hardcoded length bound */ 182 | size_t decLen = 65536; 183 | uint8_t* decData = (uint8_t*)malloc(decLen); 184 | if (!decData) { 185 | EVP_PKEY_CTX_free(ctx); 186 | result->verify_flags = COUNTERSIGNATURE_VFY_INTERNAL_ERROR; 187 | goto end; 188 | } 189 | 190 | uint8_t* encData = si->enc_digest->data; 191 | size_t encLen = si->enc_digest->length; 192 | 193 | /* Decrypt the encrypted digest */ 194 | EVP_PKEY_verify_recover_init(ctx); 195 | bool isDecrypted = EVP_PKEY_verify_recover(ctx, decData, &decLen, encData, encLen) == 1; 196 | EVP_PKEY_CTX_free(ctx); 197 | 198 | if (!isDecrypted) { 199 | free(decData); 200 | result->verify_flags = COUNTERSIGNATURE_VFY_CANT_DECRYPT_DIGEST; 201 | goto end; 202 | } 203 | 204 | /* compare the encrypted digest and calculated digest */ 205 | bool isValid = false; 206 | 207 | #if OPENSSL_VERSION_NUMBER >= 0x3000000fL 208 | size_t mdLen = EVP_MD_get_size(md); 209 | #else 210 | size_t mdLen = EVP_MD_size(md); 211 | #endif 212 | /* Sometimes signed data contains DER encoded DigestInfo structure which contains hash of 213 | * authenticated attributes (39c9d136f026a9ad18fb9f41a64f76dd8418e8de625dce5d3a372bd242fc5edd) 214 | * but other times it is just purely and I didn't find another way to distinguish it but only 215 | * based on the length of data we get. Found mention of this in openssl mailing list: 216 | * https://mta.openssl.org/pipermail/openssl-users/2015-September/002054.html */ 217 | if (mdLen == decLen) { 218 | isValid = !memcmp(calc_digest, decData, mdLen); 219 | } else { 220 | const uint8_t* data_ptr = decData; 221 | DigestInfo* digest_info = d2i_DigestInfo(NULL, &data_ptr, decLen); 222 | if (digest_info) { 223 | isValid = !memcmp(digest_info->digest->data, calc_digest, mdLen); 224 | DigestInfo_free(digest_info); 225 | } else { 226 | isValid = false; 227 | } 228 | } 229 | free(decData); 230 | 231 | if (!isValid) { 232 | result->verify_flags = COUNTERSIGNATURE_VFY_INVALID; 233 | goto end; 234 | } 235 | 236 | /* Now check the countersignature message-digest that should correspond 237 | * to Signatures encrypted digest it countersigns */ 238 | calculate_digest(md, enc_digest->data, enc_digest->length, calc_digest); 239 | 240 | /* Check if calculated one matches the stored one */ 241 | if (digestLen != mdLen || memcmp(calc_digest, digestData, mdLen) != 0) { 242 | result->verify_flags = COUNTERSIGNATURE_VFY_DOESNT_MATCH_SIGNATURE; 243 | goto end; 244 | } 245 | 246 | end: 247 | PKCS7_SIGNER_INFO_free(si); 248 | return result; 249 | } 250 | 251 | TS_TST_INFO* IMPL_FUNC_NAME(get_ts_tst_info, pkcs7)(CountersignatureImpl* impl) 252 | { 253 | assert(impl->type == CS_IMPL_PKCS7); 254 | 255 | return PKCS7_to_TS_TST_INFO(impl->pkcs7); 256 | } 257 | 258 | TS_TST_INFO* IMPL_FUNC_NAME(get_ts_tst_info, cms)(CountersignatureImpl* impl) 259 | { 260 | assert(impl->type == CS_IMPL_CMS); 261 | 262 | const ASN1_OBJECT* content_type = CMS_get0_eContentType(impl->cms); 263 | if (!content_type || OBJ_obj2nid(content_type) != NID_id_smime_ct_TSTInfo) { 264 | return NULL; 265 | } 266 | 267 | ASN1_OCTET_STRING** content = CMS_get0_content(impl->cms); 268 | if (!content || !*content) { 269 | return NULL; 270 | } 271 | 272 | const uint8_t* data = (*content)->data; 273 | TS_TST_INFO* ts_tst_info = d2i_TS_TST_INFO(NULL, &data, (*content)->length); 274 | if (!ts_tst_info) { 275 | return NULL; 276 | } 277 | 278 | return ts_tst_info; 279 | } 280 | 281 | STACK_OF(X509) * IMPL_FUNC_NAME(get_signers, pkcs7)(CountersignatureImpl* impl) 282 | { 283 | assert(impl->type == CS_IMPL_PKCS7); 284 | 285 | return PKCS7_get0_signers(impl->pkcs7, impl->pkcs7->d.sign->cert, 0); 286 | } 287 | 288 | STACK_OF(X509) * IMPL_FUNC_NAME(get_signers, cms)(CountersignatureImpl* impl) 289 | { 290 | assert(impl->type == CS_IMPL_CMS); 291 | 292 | STACK_OF(CMS_SignerInfo)* signer_infos = CMS_get0_SignerInfos(impl->cms); 293 | if (!signer_infos) { 294 | return NULL; 295 | } 296 | 297 | // Use our func points to cache the certs and don't create another copy 298 | STACK_OF(X509)* certs = impl->funcs->get_certs(impl); 299 | 300 | int si_count = sk_CMS_SignerInfo_num(signer_infos); 301 | int cert_count = certs ? sk_X509_num(certs) : 0; 302 | STACK_OF(X509)* result = sk_X509_new_null(); 303 | 304 | // PKCS7_get0_signers() lets us specify the certificate array and looks up signer certificate 305 | // there With CMS_ContentInfo, we don't have direct access to signer certificate, just all the 306 | // certificates The only thing we can do is to go through all signer infos and find those which 307 | // match some certificate in all certificates. It essentially simulates what 308 | // PKCS7_get0_signers() does. 309 | for (int i = 0; i < si_count; ++i) { 310 | CMS_SignerInfo* si = sk_CMS_SignerInfo_value(signer_infos, i); 311 | if (!si) { 312 | continue; 313 | } 314 | 315 | if (certs) { 316 | for (int j = 0; j < cert_count; ++j) { 317 | X509* cert = sk_X509_value(certs, j); 318 | if (!cert) { 319 | continue; 320 | } 321 | 322 | if (CMS_SignerInfo_cert_cmp(si, cert) == 0) { 323 | if (!sk_X509_push(result, cert)) { 324 | return NULL; 325 | } 326 | } 327 | } 328 | } 329 | } 330 | 331 | return result; 332 | } 333 | 334 | STACK_OF(X509) * IMPL_FUNC_NAME(get_certs, pkcs7)(CountersignatureImpl* impl) 335 | { 336 | assert(impl->type == CS_IMPL_PKCS7); 337 | 338 | return impl->pkcs7->d.sign->cert; 339 | } 340 | 341 | STACK_OF(X509) * IMPL_FUNC_NAME(get_certs, cms)(CountersignatureImpl* impl) 342 | { 343 | assert(impl->type == CS_IMPL_CMS); 344 | 345 | if (impl->_certs) { 346 | return impl->_certs; 347 | } 348 | 349 | impl->_certs = CMS_get1_certs(impl->cms); 350 | return impl->_certs; 351 | } 352 | 353 | int IMPL_FUNC_NAME(verify_digest, pkcs7)( 354 | CountersignatureImpl* impl, uint8_t* digest, size_t digest_size) 355 | { 356 | assert(impl->type == CS_IMPL_PKCS7); 357 | 358 | X509_STORE* store = X509_STORE_new(); 359 | TS_VERIFY_CTX* ctx = TS_VERIFY_CTX_new(); 360 | 361 | TS_VERIFY_CTX_set_flags(ctx, TS_VFY_VERSION | TS_VFY_IMPRINT); 362 | TS_VERIFY_CTX_set_store(ctx, store); 363 | #if OPENSSL_VERSION_NUMBER >= 0x3000000fL 364 | TS_VERIFY_CTX_set_certs(ctx, impl->funcs->get_certs(impl)); 365 | #else 366 | TS_VERIFY_CTS_set_certs(ctx, impl->funcs->get_certs(impl)); 367 | #endif 368 | TS_VERIFY_CTX_set_imprint(ctx, digest, digest_size); 369 | 370 | int result = TS_RESP_verify_token(ctx, impl->pkcs7); 371 | 372 | X509_STORE_free(store); 373 | OPENSSL_free(ctx); 374 | 375 | return result; 376 | } 377 | 378 | int IMPL_FUNC_NAME(verify_digest, cms)( 379 | CountersignatureImpl* impl, uint8_t* digest, size_t digest_size) 380 | { 381 | assert(impl->type == CS_IMPL_CMS); 382 | 383 | // This is essentially just reimplementation of TS_RESP_verify_token() from OpenSSL 384 | TS_TST_INFO* ts_tst_info = impl->funcs->get_ts_tst_info(impl); 385 | if (!ts_tst_info || TS_TST_INFO_get_version(ts_tst_info) != 1) { 386 | if (ts_tst_info) 387 | TS_TST_INFO_free(ts_tst_info); 388 | return 0; 389 | } 390 | 391 | TS_MSG_IMPRINT* ts_imprint = TS_TST_INFO_get_msg_imprint(ts_tst_info); 392 | if (!ts_imprint) { 393 | TS_TST_INFO_free(ts_tst_info); 394 | return 0; 395 | } 396 | 397 | ASN1_OCTET_STRING* ts_imprint_digest = TS_MSG_IMPRINT_get_msg(ts_imprint); 398 | if (!ts_imprint_digest) { 399 | TS_TST_INFO_free(ts_tst_info); 400 | return 0; 401 | } 402 | 403 | if (ts_imprint_digest->length != (int)digest_size || 404 | memcmp(ts_imprint_digest->data, digest, digest_size) != 0) { 405 | TS_TST_INFO_free(ts_tst_info); 406 | return 0; 407 | } 408 | 409 | TS_TST_INFO_free(ts_tst_info); 410 | return 1; 411 | } 412 | 413 | BIO* IMPL_FUNC_NAME(verify_signature_init, pkcs7)(CountersignatureImpl* impl) 414 | { 415 | assert(impl->type == CS_IMPL_PKCS7); 416 | 417 | return PKCS7_dataInit(impl->pkcs7, NULL); 418 | } 419 | 420 | BIO* IMPL_FUNC_NAME(verify_signature_init, cms)(CountersignatureImpl* impl) 421 | { 422 | assert(impl->type == CS_IMPL_CMS); 423 | 424 | return CMS_dataInit(impl->cms, NULL); 425 | } 426 | 427 | int IMPL_FUNC_NAME(verify_signature_finish, pkcs7)( 428 | CountersignatureImpl* impl, BIO* bio, X509* signer) 429 | { 430 | assert(impl->type == CS_IMPL_PKCS7); 431 | 432 | /* Verify signature with PKCS7_signatureVerify 433 | because TS_RESP_verify_token would try to verify 434 | chain and without trust anchors it always fails */ 435 | PKCS7_SIGNER_INFO* si = sk_PKCS7_SIGNER_INFO_value(PKCS7_get_signer_info(impl->pkcs7), 0); 436 | return PKCS7_signatureVerify(bio, impl->pkcs7, si, signer); 437 | } 438 | 439 | int IMPL_FUNC_NAME(verify_signature_finish, cms)(CountersignatureImpl* impl, BIO* bio, X509* signer) 440 | { 441 | assert(impl->type == CS_IMPL_CMS); 442 | 443 | (void)signer; 444 | CMS_SignerInfo* si = sk_CMS_SignerInfo_value(CMS_get0_SignerInfos(impl->cms), 0); 445 | return CMS_SignerInfo_verify_content(si, bio); 446 | } 447 | 448 | CountersignatureImpl* ms_countersig_impl_new(const uint8_t* data, long size) 449 | { 450 | const uint8_t* d = data; 451 | PKCS7* p7 = d2i_PKCS7(NULL, &d, size); 452 | if (p7 && PKCS7_type_is_signed(p7) && p7->d.sign) { 453 | CountersignatureImpl* result = 454 | (CountersignatureImpl*)calloc(1, sizeof(CountersignatureImpl)); 455 | result->type = CS_IMPL_PKCS7; 456 | result->funcs = &FUNC_ARRAY_NAME_FOR_IMPL(pkcs7); 457 | result->pkcs7 = p7; 458 | return result; 459 | } else if (p7) { 460 | PKCS7_free(p7); 461 | return NULL; 462 | } 463 | 464 | d = data; 465 | CMS_ContentInfo* cms = d2i_CMS_ContentInfo(NULL, &d, size); 466 | if (cms) { 467 | CountersignatureImpl* result = 468 | (CountersignatureImpl*)calloc(1, sizeof(CountersignatureImpl)); 469 | result->type = CS_IMPL_CMS; 470 | result->funcs = &FUNC_ARRAY_NAME_FOR_IMPL(cms); 471 | result->cms = cms; 472 | return result; 473 | } 474 | 475 | return NULL; 476 | } 477 | 478 | void ms_countersig_impl_free(CountersignatureImpl* impl) 479 | { 480 | switch (impl->type) { 481 | case CS_IMPL_PKCS7: 482 | PKCS7_free(impl->pkcs7); 483 | break; 484 | case CS_IMPL_CMS: 485 | if (impl->_certs) { 486 | sk_X509_pop_free(impl->_certs, X509_free); 487 | } 488 | CMS_ContentInfo_free(impl->cms); 489 | break; 490 | } 491 | 492 | free(impl); 493 | } 494 | 495 | Countersignature* ms_countersig_new(const uint8_t* data, long size, ASN1_STRING* enc_digest) 496 | { 497 | Countersignature* result = (Countersignature*)calloc(1, sizeof(*result)); 498 | if (!result) 499 | return NULL; 500 | 501 | CountersignatureImpl* impl = ms_countersig_impl_new(data, size); 502 | if (!impl) { 503 | result->verify_flags = COUNTERSIGNATURE_VFY_CANT_PARSE; 504 | return result; 505 | } 506 | 507 | TS_TST_INFO* ts = impl->funcs->get_ts_tst_info(impl); 508 | if (!ts) { 509 | result->verify_flags = COUNTERSIGNATURE_VFY_CANT_PARSE; 510 | ms_countersig_impl_free(impl); 511 | return result; 512 | } 513 | 514 | const ASN1_TIME* rawTime = TS_TST_INFO_get_time(ts); 515 | if (!rawTime) { 516 | result->verify_flags = COUNTERSIGNATURE_VFY_TIME_MISSING; 517 | TS_TST_INFO_free(ts); 518 | ms_countersig_impl_free(impl); 519 | return result; 520 | } 521 | 522 | result->sign_time = ASN1_TIME_to_int64_t(rawTime); 523 | 524 | STACK_OF(X509)* sigs = impl->funcs->get_signers(impl); 525 | X509* signCert = sk_X509_value(sigs, 0); 526 | if (!signCert) { 527 | result->verify_flags = COUNTERSIGNATURE_VFY_NO_SIGNER_CERT; 528 | goto end; 529 | } 530 | 531 | STACK_OF(X509)* certs = impl->funcs->get_certs(impl); 532 | 533 | /* MS Counter signatures (PKCS7/CMS) can have extra certificates that are not part of a chain */ 534 | result->certs = certificate_array_new(sk_X509_num(certs)); 535 | if (!result->certs) { 536 | result->verify_flags = AUTHENTICODE_VFY_INTERNAL_ERROR; 537 | goto end; 538 | } 539 | 540 | parse_x509_certificates(certs, result->certs); 541 | 542 | result->chain = parse_signer_chain(signCert, certs); 543 | 544 | /* Imprint == digest */ 545 | TS_MSG_IMPRINT* imprint = TS_TST_INFO_get_msg_imprint(ts); 546 | if (!imprint) { 547 | result->verify_flags = COUNTERSIGNATURE_VFY_DIGEST_MISSING; 548 | goto end; 549 | } 550 | 551 | X509_ALGOR* digestAlg = TS_MSG_IMPRINT_get_algo(imprint); 552 | int digestnid = OBJ_obj2nid(digestAlg->algorithm); 553 | result->digest_alg = strdup(OBJ_nid2ln(digestnid)); 554 | 555 | ASN1_STRING* rawDigest = TS_MSG_IMPRINT_get_msg(imprint); 556 | 557 | int digestLen = rawDigest->length; 558 | uint8_t* digestData = rawDigest->data; 559 | 560 | byte_array_init(&result->digest, digestData, digestLen); 561 | 562 | if (!digestLen) { 563 | result->verify_flags = COUNTERSIGNATURE_VFY_DIGEST_MISSING; 564 | goto end; 565 | } 566 | 567 | const EVP_MD* md = EVP_get_digestbynid(digestnid); 568 | if (!md) { 569 | result->verify_flags = COUNTERSIGNATURE_VFY_UNKNOWN_ALGORITHM; 570 | goto end; 571 | } 572 | 573 | uint8_t calc_digest[EVP_MAX_MD_SIZE]; 574 | calculate_digest(md, enc_digest->data, enc_digest->length, calc_digest); 575 | 576 | #if OPENSSL_VERSION_NUMBER >= 0x3000000fL 577 | int mdLen = EVP_MD_get_size(md); 578 | #else 579 | int mdLen = EVP_MD_size(md); 580 | #endif 581 | 582 | if (digestLen != mdLen || memcmp(calc_digest, digestData, mdLen) != 0) { 583 | result->verify_flags = COUNTERSIGNATURE_VFY_DOESNT_MATCH_SIGNATURE; 584 | goto end; 585 | } 586 | 587 | bool isValid = impl->funcs->verify_digest(impl, calc_digest, mdLen) == 1; 588 | if (!isValid) { 589 | result->verify_flags = COUNTERSIGNATURE_VFY_INVALID; 590 | goto end; 591 | } 592 | 593 | BIO* bio = impl->funcs->verify_signature_init(impl); 594 | 595 | char buf[4096]; 596 | /* We now have to 'read' from bio to calculate digests etc. */ 597 | while (BIO_read(bio, buf, sizeof(buf)) > 0) 598 | continue; 599 | 600 | isValid = impl->funcs->verify_signature_finish(impl, bio, signCert) == 1; 601 | 602 | BIO_free_all(bio); 603 | 604 | if (!isValid) 605 | result->verify_flags = COUNTERSIGNATURE_VFY_INVALID; 606 | 607 | end: 608 | sk_X509_free(sigs); 609 | TS_TST_INFO_free(ts); 610 | ms_countersig_impl_free(impl); 611 | return result; 612 | } 613 | 614 | int countersignature_array_insert(CountersignatureArray* arr, Countersignature* sig) 615 | { 616 | Countersignature** tmp = 617 | (Countersignature**)realloc(arr->counters, (arr->count + 1) * sizeof(Countersignature*)); 618 | if (!tmp) 619 | return 1; 620 | 621 | arr->counters = tmp; 622 | arr->counters[arr->count] = sig; 623 | arr->count++; 624 | 625 | return 0; 626 | } 627 | 628 | int countersignature_array_move(CountersignatureArray* dst, CountersignatureArray* src) 629 | { 630 | size_t newCount = dst->count + src->count; 631 | 632 | Countersignature** tmp = 633 | (Countersignature**)realloc(dst->counters, newCount * sizeof(Countersignature*)); 634 | if (!tmp) 635 | return 1; 636 | 637 | dst->counters = tmp; 638 | 639 | for (size_t i = 0; i < src->count; ++i) 640 | dst->counters[i + dst->count] = src->counters[i]; 641 | 642 | dst->count = newCount; 643 | 644 | free(src->counters); 645 | src->counters = NULL; 646 | src->count = 0; 647 | 648 | return 0; 649 | } 650 | 651 | void countersignature_free(Countersignature* sig) 652 | { 653 | if (sig) { 654 | free(sig->digest_alg); 655 | free(sig->digest.data); 656 | certificate_array_free(sig->chain); 657 | certificate_array_free(sig->certs); 658 | free(sig); 659 | } 660 | } 661 | 662 | void countersignature_array_free(CountersignatureArray* arr) 663 | { 664 | if (arr) { 665 | for (size_t i = 0; i < arr->count; ++i) { 666 | countersignature_free(arr->counters[i]); 667 | } 668 | free(arr->counters); 669 | free(arr); 670 | } 671 | } 672 | -------------------------------------------------------------------------------- /tests/integration/test_non_microsoft.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2021 Avast Software 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | */ 21 | 22 | #include "../data.h" 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | 37 | class SignatureTest : public testing::Test 38 | { 39 | protected: 40 | unsigned char *data = nullptr; 41 | long data_len = 0; 42 | 43 | void SetUp() override 44 | { 45 | BIO *bio = BIO_new(BIO_s_mem()); 46 | BIO_write(bio, VALID_SIGNATURE_PEM, std::strlen(VALID_SIGNATURE_PEM)); 47 | char *name = nullptr; 48 | char *header = nullptr; 49 | PEM_read_bio(bio, &name, &header, &data, &data_len); 50 | BIO_free_all(bio); 51 | OPENSSL_free(name); 52 | OPENSSL_free(header); 53 | 54 | initialize_authenticode_parser(); 55 | } 56 | 57 | void TearDown() override { OPENSSL_free(data); } 58 | }; 59 | 60 | TEST_F(SignatureTest, ResultOverview) 61 | { 62 | AuthenticodeArray *auth = authenticode_new(data, data_len); 63 | ASSERT_NE(auth, nullptr); 64 | 65 | ASSERT_EQ(auth->count, 3); 66 | ASSERT_NE(auth->signatures, nullptr); 67 | 68 | for (size_t i = 0; i < auth->count; ++i) { 69 | ASSERT_TRUE(auth->signatures[i]); 70 | } 71 | 72 | authenticode_array_free(auth); 73 | } 74 | 75 | TEST_F(SignatureTest, FirstSignatureContent) 76 | { 77 | AuthenticodeArray *auth = authenticode_new(data, data_len); 78 | ASSERT_NE(auth, nullptr); 79 | 80 | ASSERT_EQ(auth->count, 3); 81 | ASSERT_NE(auth->signatures, nullptr); 82 | 83 | const Authenticode *first_sig = auth->signatures[0]; 84 | ASSERT_TRUE(first_sig); 85 | 86 | //***********************************// 87 | // Check the first signature content // 88 | EXPECT_EQ(first_sig->version, 1); 89 | 90 | EXPECT_TRUE(first_sig->digest.data); 91 | unsigned char file_digest[20] = {0xfb, 0xf0, 0x17, 0xe2, 0x1d, 0x7b, 0xe9, 0x8d, 0xee, 0x4a, 92 | 0x29, 0xe8, 0xf2, 0x9f, 0x05, 0xe5, 0xa4, 0x3b, 0x16, 0x9f}; 93 | EXPECT_EQ(first_sig->digest.len, 20); 94 | EXPECT_TRUE(std::memcmp(file_digest, first_sig->digest.data, 20) == 0); 95 | EXPECT_STREQ(first_sig->digest_alg, "sha1"); 96 | 97 | EXPECT_EQ(first_sig->verify_flags, AUTHENTICODE_VFY_VALID); 98 | 99 | //****************************// 100 | // Check SignerInfo structure // 101 | ASSERT_TRUE(first_sig->signer); 102 | EXPECT_STREQ(first_sig->signer->digest_alg, "sha1"); 103 | 104 | ASSERT_TRUE(first_sig->signer->digest.data); 105 | ASSERT_EQ(first_sig->signer->digest.len, 20); 106 | unsigned char message_digest[20] = {0x26, 0x74, 0x14, 0x28, 0x0c, 0xa4, 0x8e, 0xa7, 0xa6, 0xff, 107 | 0x1c, 0x67, 0xf3, 0x71, 0x32, 0x6d, 0x58, 0xe1, 0xe9, 0x60}; 108 | 109 | EXPECT_TRUE(std::memcmp(message_digest, first_sig->signer->digest.data, 20) == 0); 110 | ASSERT_FALSE(first_sig->signer->program_name); 111 | 112 | //******************************************// 113 | // Test all certificates of first signature // 114 | ASSERT_TRUE(first_sig->certs); 115 | ASSERT_TRUE(first_sig->certs->certs); 116 | ASSERT_EQ(first_sig->certs->count, 4); 117 | 118 | //**************************// 119 | // Check the 1. certificate // 120 | const Certificate *cert = first_sig->certs->certs[0]; 121 | ASSERT_TRUE(cert->sha1.data); 122 | ASSERT_EQ(cert->sha1.len, 20); 123 | unsigned char first_cert_sha1[20] = {0x6c, 0x07, 0x45, 0x3f, 0xfd, 0xda, 0x08, 124 | 0xb8, 0x37, 0x07, 0xc0, 0x9b, 0x82, 0xfb, 125 | 0x3d, 0x15, 0xf3, 0x53, 0x36, 0xb1}; 126 | EXPECT_TRUE(std::memcmp(first_cert_sha1, cert->sha1.data, 20) == 0); 127 | 128 | ASSERT_TRUE(cert->sha256.data); 129 | ASSERT_EQ(cert->sha256.len, 32); 130 | unsigned char first_cert_sha256[32] = {0x06, 0x25, 0xfe, 0xe1, 0xa8, 0x0d, 0x7b, 0x89, 131 | 0x7a, 0x97, 0x12, 0x24, 0x9c, 0x2f, 0x55, 0xff, 132 | 0x39, 0x1d, 0x66, 0x61, 0xdb, 0xd8, 0xb8, 0x7f, 133 | 0x9b, 0xe6, 0xf2, 0x52, 0xd8, 0x8c, 0xed, 0x95}; 134 | EXPECT_TRUE(std::memcmp(first_cert_sha256, cert->sha256.data, 32) == 0); 135 | 136 | EXPECT_EQ(cert->version, 2); 137 | EXPECT_STREQ( 138 | cert->subject, "/C=US/O=Symantec Corporation/CN=Symantec Time Stamping Services CA - G2"); 139 | EXPECT_STREQ( 140 | cert->issuer, 141 | "/C=ZA/ST=Western Cape/L=Durbanville/O=Thawte/OU=Thawte Certification/CN=Thawte " 142 | "Timestamping CA"); 143 | EXPECT_EQ(cert->not_after, 1609372799); 144 | EXPECT_EQ(cert->not_before, 1356048000); 145 | EXPECT_STREQ( 146 | cert->key, 147 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsayzSVRLlxwSCtgleZEiVypv3LgmxENza8K/" 148 | "LlBa+xTCdo5DASVDtKHiRfTot3vDdMwi17SUAAL3Te2/" 149 | "tLdEJGvNX0U70UTOQxJzF4KLabQry5kerHIbJk1xH7Ex3ftRYQJTpqr1SSwFeEWlL4nO55nn/" 150 | "oziVz89xpLcSvh7M+R5CvvwdYhBnP/" 151 | "FA1GZqtdsn5Nph2Upg4XCYBTEyMk7FNrAgfAfDXTekiKryvf7dHwn5vdKG3+" 152 | "nw54trorqpuaqJxZ9YfeYcRG84lChS+Vd+uUOpyyfqmUg09iW6Mh8pU5IRP8Z4kQHkgvXaISAXWp4ZEXNYEZ+" 153 | "VMETfMV58cnBcQIDAQAB"); 154 | EXPECT_STREQ(cert->serial, "7e:93:eb:fb:7c:c6:4e:59:ea:4b:9a:77:d4:06:fc:3b"); 155 | EXPECT_STREQ(cert->sig_alg, "sha1WithRSAEncryption"); 156 | EXPECT_STREQ(cert->sig_alg_oid, "1.2.840.113549.1.1.5"); 157 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 158 | 159 | //**************************// 160 | // Check the 2. certificate // 161 | cert = first_sig->certs->certs[1]; 162 | ASSERT_TRUE(cert->sha1.data); 163 | ASSERT_EQ(cert->sha1.len, 20); 164 | unsigned char second_cert_sha1[20] = {0x65, 0x43, 0x99, 0x29, 0xb6, 0x79, 0x73, 165 | 0xeb, 0x19, 0x2d, 0x6f, 0xf2, 0x43, 0xe6, 166 | 0x76, 0x7a, 0xdf, 0x08, 0x34, 0xe4}; 167 | EXPECT_TRUE(std::memcmp(second_cert_sha1, cert->sha1.data, 20) == 0); 168 | 169 | ASSERT_TRUE(cert->sha256.data); 170 | ASSERT_EQ(cert->sha256.len, 32); 171 | unsigned char second_cert_sha256[32] = {0x03, 0x74, 0x88, 0x1c, 0x9b, 0x74, 0xd3, 0x1f, 172 | 0x28, 0xdc, 0x58, 0x0b, 0x0f, 0x2b, 0x9d, 0x2b, 173 | 0x14, 0xa9, 0x7c, 0xe3, 0x1c, 0xbe, 0xc2, 0xa0, 174 | 0x5a, 0xeb, 0x37, 0x7d, 0xcd, 0xdc, 0xc2, 0xb0}; 175 | EXPECT_TRUE(std::memcmp(second_cert_sha256, cert->sha256.data, 32) == 0); 176 | 177 | EXPECT_EQ(cert->version, 2); 178 | EXPECT_STREQ( 179 | cert->subject, 180 | "/C=US/O=Symantec Corporation/CN=Symantec Time Stamping Services Signer - G4"); 181 | EXPECT_STREQ( 182 | cert->issuer, "/C=US/O=Symantec Corporation/CN=Symantec Time Stamping Services CA - G2"); 183 | EXPECT_EQ(cert->not_after, 1609286399); 184 | EXPECT_EQ(cert->not_before, 1350518400); 185 | EXPECT_STREQ( 186 | cert->key, 187 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomMLOUS4uyOnREm7Dv+h8GEKU5OwmNutLA9KxW7/" 188 | "hjxTVQ8VzgQ/K/2plpbZvmF5C1vJTIZ25eBDSyKV7sIrQ8Gf2Gi0jkBP7oU4uRHFI/" 189 | "JkWPAVMm9OV6GuiKQC1yoezUvh3WPVF4kyW7BemVqonShQDhfultthO0VRHc8SVguSR/" 190 | "yrrvZmPUescHLnkudfzRC5xINklBm9JYDh6NIipdC6Anqhd5NbZcPuF3S8QYYq3AhMjJKMkS2ed0QfaNaodHfbDlsy" 191 | "i1aLM73ZY8hJnTrFxeozC9Lxoxv0i77Zs1eLO94Ep3oisiSuLsdwxb5OgyYI+wu9qU+ZCOEQKHKqzQIDAQAB"); 192 | EXPECT_STREQ(cert->serial, "0e:cf:f4:38:c8:fe:bf:35:6e:04:d8:6a:98:1b:1a:50"); 193 | EXPECT_STREQ(cert->sig_alg, "sha1WithRSAEncryption"); 194 | EXPECT_STREQ(cert->sig_alg_oid, "1.2.840.113549.1.1.5"); 195 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 196 | 197 | //**************************// 198 | // Check the 3. certificate // 199 | cert = first_sig->certs->certs[2]; 200 | ASSERT_TRUE(cert->sha1.data); 201 | ASSERT_EQ(cert->sha1.len, 20); 202 | unsigned char third_cert_sha1[20] = {0x33, 0xe2, 0x4f, 0xe6, 0x6e, 0x01, 0x17, 203 | 0xfd, 0xd4, 0x27, 0x86, 0x99, 0xad, 0x42, 204 | 0x3e, 0xf2, 0x66, 0x9f, 0xd2, 0x58}; 205 | EXPECT_TRUE(std::memcmp(third_cert_sha1, cert->sha1.data, 20) == 0); 206 | 207 | ASSERT_TRUE(cert->sha256.data); 208 | ASSERT_EQ(cert->sha256.len, 32); 209 | unsigned char third_cert_sha256[32] = {0x51, 0xb4, 0xb0, 0xdf, 0x44, 0xa7, 0x40, 0xbc, 210 | 0x08, 0x88, 0x17, 0x8c, 0xbe, 0xac, 0xe8, 0x31, 211 | 0x08, 0xa2, 0x49, 0x9e, 0xc2, 0x2f, 0x39, 0x53, 212 | 0x89, 0xa9, 0xd7, 0xc6, 0xab, 0x31, 0xbc, 0x42}; 213 | EXPECT_TRUE(std::memcmp(third_cert_sha256, cert->sha256.data, 32) == 0); 214 | 215 | EXPECT_EQ(cert->version, 2); 216 | EXPECT_STREQ( 217 | cert->subject, 218 | "/C=US/ST=New York/L=New York/O=Slimware Utilities Holdings, Inc./CN=Slimware Utilities " 219 | "Holdings, Inc."); 220 | EXPECT_STREQ( 221 | cert->issuer, 222 | "/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at " 223 | "https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Code Signing 2010 CA"); 224 | EXPECT_EQ(cert->not_after, 1546905599); 225 | EXPECT_EQ(cert->not_before, 1513123200); 226 | EXPECT_STREQ( 227 | cert->key, 228 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArkSLGMoBwKcg6EAppcjBQKHB2cOrlhmGjxdVSCVE+" 229 | "zOHHWVEx+5YP9KYiqShQ2ZPLw2SI9duq2ikRsShHboPgi6SfDb4OU44lsBP/H/" 230 | "sV9OrH2gaDi9IwN+XGzjKbOeIZ828m2GEf/t+kvoRmlxT0ivfiwzolsGqWsp3ELPrI/" 231 | "f+sVMFWrvPZPBGteH67qS+lwq5+4SX7DYJf2NPcJh9o+kYtU6FsY6MWe5oJSr3rhcTqknPhm8BYIKR/" 232 | "fRyjR+" 233 | "P2VYlUoytqjbM7QSACfMsa1Z6OZTMFEJV2iw7V14cyLNptCAU0w1mNtFD7RFYQKzjwkwPUm8dvBvaWSsSgqokZQIDA" 234 | "QAB"); 235 | EXPECT_STREQ(cert->serial, "30:63:b3:a7:40:c1:cd:fd:f8:bb:9e:6c:33:1a:d7:de"); 236 | EXPECT_STREQ(cert->sig_alg, "sha1WithRSAEncryption"); 237 | EXPECT_STREQ(cert->sig_alg_oid, "1.2.840.113549.1.1.5"); 238 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 239 | 240 | //**************************// 241 | // Check the 4. certificate // 242 | cert = first_sig->certs->certs[3]; 243 | ASSERT_TRUE(cert->sha1.data); 244 | ASSERT_EQ(cert->sha1.len, 20); 245 | unsigned char fourth_cert_sha1[20] = {0x49, 0x58, 0x47, 0xa9, 0x31, 0x87, 0xcf, 246 | 0xb8, 0xc7, 0x1f, 0x84, 0x0c, 0xb7, 0xb4, 247 | 0x14, 0x97, 0xad, 0x95, 0xc6, 0x4f}; 248 | EXPECT_TRUE(std::memcmp(fourth_cert_sha1, cert->sha1.data, 20) == 0); 249 | 250 | ASSERT_TRUE(cert->sha256.data); 251 | ASSERT_EQ(cert->sha256.len, 32); 252 | unsigned char fourth_cert_sha256[32] = {0x0c, 0xfc, 0x19, 0xdb, 0x68, 0x1b, 0x01, 0x4b, 253 | 0xfe, 0x3f, 0x23, 0xcb, 0x3a, 0x78, 0xb6, 0x72, 254 | 0x08, 0xb4, 0xe3, 0xd8, 0xd7, 0xb6, 0xa7, 0xb1, 255 | 0x80, 0x7f, 0x7c, 0xd6, 0xec, 0xb2, 0xa5, 0x4e}; 256 | EXPECT_TRUE(std::memcmp(fourth_cert_sha256, cert->sha256.data, 32) == 0); 257 | 258 | EXPECT_EQ(cert->version, 2); 259 | EXPECT_STREQ( 260 | cert->subject, 261 | "/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at " 262 | "https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Code Signing 2010 CA"); 263 | EXPECT_STREQ( 264 | cert->issuer, 265 | "/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For " 266 | "authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5"); 267 | EXPECT_EQ(cert->not_after, 1581119999); 268 | EXPECT_EQ(cert->not_before, 1265587200); 269 | EXPECT_STREQ( 270 | cert->key, 271 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9SNLXqXXirsy6dRX9+/kxyZ+rRmY/" 272 | "qidfZT2NmsQ13WBMH8EaH/LK3UezR0IjN9plKc3o5x7gOCZ4e43TV/" 273 | "OOxTuhtTQ9Sc1vCULOKeMY50Xowilq7D7zWpigkzVIdob2fHjhDuKKk+FW5ABT8mndhB/" 274 | "JwN8vq5+fcHd+QW8G0icaefApDw8QQA+35blxeSUcdZVAccAJkpAPLWhJqkMp22AjpAle8+/" 275 | "PxzrL5b65Yd3xrVWsno7VDBTG99iNP8e0fRakyiF5UwXTn5b/aSTmX/fze+kde/vFfZH5/" 276 | "gZctguNBqmtKdMfr27Tww9V/Ew1qY2jtaAdtcZLqXNfjQtiQIDAQAB"); 277 | 278 | EXPECT_STREQ(cert->serial, "52:00:e5:aa:25:56:fc:1a:86:ed:96:c9:d4:4b:33:c7"); 279 | EXPECT_STREQ(cert->sig_alg, "sha1WithRSAEncryption"); 280 | EXPECT_STREQ(cert->sig_alg_oid, "1.2.840.113549.1.1.5"); 281 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 282 | 283 | //*******************************************// 284 | // Test the first signature countersignature // 285 | ASSERT_TRUE(first_sig->countersigs); 286 | ASSERT_TRUE(first_sig->countersigs->counters); 287 | ASSERT_EQ(first_sig->countersigs->count, 1); 288 | 289 | const Countersignature *countersig = first_sig->countersigs->counters[0]; 290 | 291 | EXPECT_EQ(countersig->verify_flags, COUNTERSIGNATURE_VFY_VALID); 292 | EXPECT_STREQ(countersig->digest_alg, "sha1"); 293 | EXPECT_EQ(countersig->sign_time, 1527779084); 294 | unsigned char first_countersig_digest[20] = {0xe0, 0x11, 0x73, 0x6f, 0xf0, 0x95, 0x6e, 295 | 0x4f, 0x97, 0xd3, 0x81, 0xc0, 0xd9, 0x8d, 296 | 0x46, 0x1d, 0xc2, 0x94, 0x69, 0x1b}; 297 | ASSERT_TRUE(countersig->digest.data); 298 | ASSERT_EQ(countersig->digest.len, 20); 299 | EXPECT_TRUE(std::memcmp(first_countersig_digest, countersig->digest.data, 20) == 0); 300 | 301 | ASSERT_TRUE(countersig->chain); 302 | EXPECT_EQ(countersig->chain->count, 2); 303 | 304 | //**************************// 305 | // Check the 1. certificate // 306 | cert = countersig->chain->certs[0]; 307 | ASSERT_TRUE(cert->sha1.data); 308 | ASSERT_EQ(cert->sha1.len, 20); 309 | unsigned char first_countercert_sha1[20] = {0x65, 0x43, 0x99, 0x29, 0xb6, 0x79, 0x73, 310 | 0xeb, 0x19, 0x2d, 0x6f, 0xf2, 0x43, 0xe6, 311 | 0x76, 0x7a, 0xdf, 0x08, 0x34, 0xe4}; 312 | EXPECT_TRUE(std::memcmp(first_countercert_sha1, cert->sha1.data, 20) == 0); 313 | 314 | ASSERT_TRUE(cert->sha256.data); 315 | ASSERT_EQ(cert->sha256.len, 32); 316 | unsigned char first_countercert_sha256[32] = {0x03, 0x74, 0x88, 0x1c, 0x9b, 0x74, 0xd3, 0x1f, 317 | 0x28, 0xdc, 0x58, 0x0b, 0x0f, 0x2b, 0x9d, 0x2b, 318 | 0x14, 0xa9, 0x7c, 0xe3, 0x1c, 0xbe, 0xc2, 0xa0, 319 | 0x5a, 0xeb, 0x37, 0x7d, 0xcd, 0xdc, 0xc2, 0xb0}; 320 | EXPECT_TRUE(std::memcmp(first_countercert_sha256, cert->sha256.data, 32) == 0); 321 | 322 | EXPECT_EQ(cert->version, 2); 323 | EXPECT_STREQ( 324 | cert->subject, 325 | "/C=US/O=Symantec Corporation/CN=Symantec Time Stamping Services Signer - G4"); 326 | EXPECT_STREQ( 327 | cert->issuer, "/C=US/O=Symantec Corporation/CN=Symantec Time Stamping Services CA - G2"); 328 | EXPECT_EQ(cert->not_after, 1609286399); 329 | EXPECT_EQ(cert->not_before, 1350518400); 330 | EXPECT_STREQ( 331 | cert->key, 332 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomMLOUS4uyOnREm7Dv+h8GEKU5OwmNutLA9KxW7/" 333 | "hjxTVQ8VzgQ/K/2plpbZvmF5C1vJTIZ25eBDSyKV7sIrQ8Gf2Gi0jkBP7oU4uRHFI/" 334 | "JkWPAVMm9OV6GuiKQC1yoezUvh3WPVF4kyW7BemVqonShQDhfultthO0VRHc8SVguSR/" 335 | "yrrvZmPUescHLnkudfzRC5xINklBm9JYDh6NIipdC6Anqhd5NbZcPuF3S8QYYq3AhMjJKMkS2ed0QfaNaodHfbDlsy" 336 | "i1aLM73ZY8hJnTrFxeozC9Lxoxv0i77Zs1eLO94Ep3oisiSuLsdwxb5OgyYI+wu9qU+ZCOEQKHKqzQIDAQAB"); 337 | EXPECT_STREQ(cert->serial, "0e:cf:f4:38:c8:fe:bf:35:6e:04:d8:6a:98:1b:1a:50"); 338 | EXPECT_STREQ(cert->sig_alg, "sha1WithRSAEncryption"); 339 | EXPECT_STREQ(cert->sig_alg_oid, "1.2.840.113549.1.1.5"); 340 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 341 | 342 | //**************************// 343 | // Check the 2. certificate // 344 | cert = countersig->chain->certs[1]; 345 | ASSERT_TRUE(cert->sha1.data); 346 | ASSERT_EQ(cert->sha1.len, 20); 347 | unsigned char second_countercert_sha1[20] = {0x6c, 0x07, 0x45, 0x3f, 0xfd, 0xda, 0x08, 348 | 0xb8, 0x37, 0x07, 0xc0, 0x9b, 0x82, 0xfb, 349 | 0x3d, 0x15, 0xf3, 0x53, 0x36, 0xb1}; 350 | EXPECT_TRUE(std::memcmp(second_countercert_sha1, cert->sha1.data, 20) == 0); 351 | 352 | ASSERT_TRUE(cert->sha256.data); 353 | ASSERT_EQ(cert->sha256.len, 32); 354 | unsigned char second_countercert_sha256[32] = {0x06, 0x25, 0xfe, 0xe1, 0xa8, 0x0d, 0x7b, 0x89, 355 | 0x7a, 0x97, 0x12, 0x24, 0x9c, 0x2f, 0x55, 0xff, 356 | 0x39, 0x1d, 0x66, 0x61, 0xdb, 0xd8, 0xb8, 0x7f, 357 | 0x9b, 0xe6, 0xf2, 0x52, 0xd8, 0x8c, 0xed, 0x95}; 358 | EXPECT_TRUE(std::memcmp(second_countercert_sha256, cert->sha256.data, 32) == 0); 359 | 360 | EXPECT_EQ(cert->version, 2); 361 | EXPECT_STREQ( 362 | cert->subject, "/C=US/O=Symantec Corporation/CN=Symantec Time Stamping Services CA - G2"); 363 | EXPECT_STREQ( 364 | cert->issuer, 365 | "/C=ZA/ST=Western Cape/L=Durbanville/O=Thawte/OU=Thawte Certification/CN=Thawte " 366 | "Timestamping CA"); 367 | EXPECT_EQ(cert->not_after, 1609372799); 368 | EXPECT_EQ(cert->not_before, 1356048000); 369 | EXPECT_STREQ( 370 | cert->key, 371 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsayzSVRLlxwSCtgleZEiVypv3LgmxENza8K/" 372 | "LlBa+xTCdo5DASVDtKHiRfTot3vDdMwi17SUAAL3Te2/" 373 | "tLdEJGvNX0U70UTOQxJzF4KLabQry5kerHIbJk1xH7Ex3ftRYQJTpqr1SSwFeEWlL4nO55nn/" 374 | "oziVz89xpLcSvh7M+R5CvvwdYhBnP/" 375 | "FA1GZqtdsn5Nph2Upg4XCYBTEyMk7FNrAgfAfDXTekiKryvf7dHwn5vdKG3+" 376 | "nw54trorqpuaqJxZ9YfeYcRG84lChS+Vd+uUOpyyfqmUg09iW6Mh8pU5IRP8Z4kQHkgvXaISAXWp4ZEXNYEZ+" 377 | "VMETfMV58cnBcQIDAQAB"); 378 | EXPECT_STREQ(cert->serial, "7e:93:eb:fb:7c:c6:4e:59:ea:4b:9a:77:d4:06:fc:3b"); 379 | EXPECT_STREQ(cert->sig_alg, "sha1WithRSAEncryption"); 380 | EXPECT_STREQ(cert->sig_alg_oid, "1.2.840.113549.1.1.5"); 381 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 382 | 383 | authenticode_array_free(auth); 384 | } 385 | 386 | TEST_F(SignatureTest, SecondSignatureContent) 387 | { 388 | AuthenticodeArray *auth = authenticode_new(data, data_len); 389 | ASSERT_NE(auth, nullptr); 390 | 391 | ASSERT_EQ(auth->count, 3); 392 | ASSERT_NE(auth->signatures, nullptr); 393 | 394 | const Authenticode *second_sig = auth->signatures[1]; 395 | ASSERT_TRUE(second_sig); 396 | 397 | //***********************************// 398 | // Check the second signature content // 399 | EXPECT_EQ(second_sig->version, 1); 400 | 401 | EXPECT_TRUE(second_sig->digest.data); 402 | unsigned char file_digest[20] = {0xfb, 0xf0, 0x17, 0xe2, 0x1d, 0x7b, 0xe9, 0x8d, 0xee, 0x4a, 403 | 0x29, 0xe8, 0xf2, 0x9f, 0x05, 0xe5, 0xa4, 0x3b, 0x16, 0x9f}; 404 | EXPECT_EQ(second_sig->digest.len, 20); 405 | EXPECT_TRUE(std::memcmp(file_digest, second_sig->digest.data, 20) == 0); 406 | EXPECT_STREQ(second_sig->digest_alg, "sha1"); 407 | 408 | EXPECT_EQ(second_sig->verify_flags, AUTHENTICODE_VFY_VALID); 409 | 410 | //****************************// 411 | // Check SignerInfo structure // 412 | ASSERT_TRUE(second_sig->signer); 413 | EXPECT_STREQ(second_sig->signer->digest_alg, "sha1"); 414 | 415 | ASSERT_TRUE(second_sig->signer->digest.data); 416 | ASSERT_EQ(second_sig->signer->digest.len, 20); 417 | unsigned char message_digest[20] = {0x26, 0x74, 0x14, 0x28, 0x0c, 0xa4, 0x8e, 0xa7, 0xa6, 0xff, 418 | 0x1c, 0x67, 0xf3, 0x71, 0x32, 0x6d, 0x58, 0xe1, 0xe9, 0x60}; 419 | 420 | EXPECT_TRUE(std::memcmp(message_digest, second_sig->signer->digest.data, 20) == 0); 421 | ASSERT_FALSE(second_sig->signer->program_name); 422 | 423 | EXPECT_TRUE(second_sig->signer->chain); 424 | EXPECT_EQ(second_sig->signer->chain->count, 1); 425 | 426 | //******************************************// 427 | // Test all certificates of first signature // 428 | ASSERT_TRUE(second_sig->certs); 429 | ASSERT_TRUE(second_sig->certs->certs); 430 | ASSERT_EQ(second_sig->certs->count, 3); 431 | 432 | //**************************// 433 | // Check the 1. certificate // 434 | const Certificate *cert = second_sig->certs->certs[0]; 435 | ASSERT_TRUE(cert->sha1.data); 436 | ASSERT_EQ(cert->sha1.len, 20); 437 | unsigned char first_cert_sha1[20] = {0xa1, 0x02, 0x1a, 0x18, 0x6a, 0x74, 0xc1, 438 | 0x00, 0x51, 0x28, 0xf7, 0xb4, 0x59, 0xb4, 439 | 0x9e, 0x2e, 0x2f, 0xec, 0xfb, 0x16}; 440 | EXPECT_TRUE(std::memcmp(first_cert_sha1, cert->sha1.data, 20) == 0); 441 | 442 | ASSERT_TRUE(cert->sha256.data); 443 | ASSERT_EQ(cert->sha256.len, 32); 444 | unsigned char first_cert_sha256[32] = {0x7d, 0x77, 0x40, 0x36, 0x16, 0xf1, 0xac, 0x63, 445 | 0xb3, 0xd1, 0x09, 0x9b, 0x93, 0xe1, 0x75, 0xb0, 446 | 0x38, 0x82, 0xe0, 0xd9, 0xf7, 0x9c, 0xc5, 0x9c, 447 | 0x1b, 0x7a, 0xfe, 0x22, 0x27, 0xc7, 0x69, 0x2d}; 448 | EXPECT_TRUE(std::memcmp(first_cert_sha256, cert->sha256.data, 32) == 0); 449 | 450 | EXPECT_EQ(cert->version, 2); 451 | EXPECT_STREQ( 452 | cert->subject, 453 | "/C=US/ST=New York/L=New York City/O=Slimware Utilities Holdings, Inc./OU=White Label " 454 | "License/CN=Slimware Utilities Holdings, " 455 | "Inc./emailAddress=licensing@slimwareutilities.com"); 456 | EXPECT_STREQ( 457 | cert->issuer, 458 | "/C=US/ST=MS/L=Ocean Springs/O=SlimWare Utilities Holdings, Inc./OU=White Label License " 459 | "Authority/CN=SlimWare Services License " 460 | "Authority/emailAddress=licensing@slimwareutilities.com"); 461 | EXPECT_EQ(cert->not_after, 1533859199); 462 | EXPECT_EQ(cert->not_before, 1502323200); 463 | EXPECT_STREQ( 464 | cert->key, 465 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyVjfnMBR+R+2lWS/" 466 | "tHl2ErRB8HqdwMnlePECoSfck4ZmLQp5+O6xOsJ/" 467 | "5R7gnle0mhdSkGslRdJwXo0ytH7f8r0+" 468 | "5m6YPbbGXIBj7CJI0C4aMW7kZ8Ykj6940FeqGfIWcRl50UysMsOrXXVy8cmbfsHL/" 469 | "kvOLb+IFgHvFQR2vaC86vRKUt7NAh7pEnxFyyAZZYVeDwSq0MSmCb4Cl9S6PifKK/" 470 | "Xtxu95Ae7AdiTrIFwgkafH6LHtoEdOq/" 471 | "2C5+c07XDlDeQ6yV9NdllYPvF87xeUazNO+lwL0ak4r6HByNogAGOedX7KADztmJTyXy2VFu+P/3zDqGxhm/" 472 | "HLEtEekQIDAQAB"); 473 | EXPECT_STREQ(cert->serial, "06"); 474 | EXPECT_STREQ(cert->sig_alg, "sha1WithRSAEncryption"); 475 | EXPECT_STREQ(cert->sig_alg_oid, "1.2.840.113549.1.1.5"); 476 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 477 | 478 | //**************************// 479 | // Check the 2. certificate // 480 | cert = second_sig->certs->certs[1]; 481 | ASSERT_TRUE(cert->sha1.data); 482 | ASSERT_EQ(cert->sha1.len, 20); 483 | unsigned char second_cert_sha1[20] = {0x6f, 0xc9, 0xed, 0xb5, 0xe0, 0x0a, 0xb6, 484 | 0x41, 0x51, 0xc1, 0xcd, 0xfc, 0xac, 0x74, 485 | 0xad, 0x2c, 0x7b, 0x7e, 0x3b, 0xe4}; 486 | EXPECT_TRUE(std::memcmp(second_cert_sha1, cert->sha1.data, 20) == 0); 487 | 488 | ASSERT_TRUE(cert->sha256.data); 489 | ASSERT_EQ(cert->sha256.len, 32); 490 | unsigned char second_cert_sha256[32] = {0xf3, 0x51, 0x6d, 0xdc, 0xc8, 0xaf, 0xc8, 0x08, 491 | 0x78, 0x8b, 0xd8, 0xb0, 0xe8, 0x40, 0xbd, 0xa2, 492 | 0xb5, 0xe2, 0x3c, 0x62, 0x44, 0x25, 0x2c, 0xa3, 493 | 0x00, 0x0b, 0xb6, 0xc8, 0x71, 0x70, 0x40, 0x2a}; 494 | EXPECT_TRUE(std::memcmp(second_cert_sha256, cert->sha256.data, 32) == 0); 495 | 496 | EXPECT_EQ(cert->version, 2); 497 | EXPECT_STREQ( 498 | cert->subject, 499 | "/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec SHA256 TimeStamping " 500 | "CA"); 501 | EXPECT_STREQ( 502 | cert->issuer, 503 | "/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2008 VeriSign, Inc. - For " 504 | "authorized use only/CN=VeriSign Universal Root Certification Authority"); 505 | EXPECT_EQ(cert->not_after, 1925942399); 506 | EXPECT_EQ(cert->not_before, 1452556800); 507 | EXPECT_STREQ( 508 | cert->key, 509 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1mdWVVPnYxyXRqBoutV87ABrTxxrDKPBWuGmicAMpdqTc" 510 | "lkFEspu8LZKbku7GOz4c8/C1aQ+GIbfuumB+Lef15tQDjUkQbnQXx5HMvLrRu/2JWR8/" 511 | "DubPitljkuf8EnuHg5xYSl7e2vh47Ojcdt6tKYtTofHjmdw/SaqPSE4cTRfHHGBim0P+SDDSbDewg+TfkKtzNJ/" 512 | "8o71PWym0vhiJka9cDpMxTW38eA25Hu/" 513 | "rySV3J39M2ozP4J9ZM3vpWIasXc9LFL1M7oCZFftYR5NYp4rBkyjyPBMkEbWQ6pPrHM+" 514 | "dYr77fY5NUdbRE6kvaTyZzjSO67Uw7UNpeGeMWhNwIDAQAB"); 515 | EXPECT_STREQ(cert->serial, "7b:05:b1:d4:49:68:51:44:f7:c9:89:d2:9c:19:9d:12"); 516 | EXPECT_STREQ(cert->sig_alg, "sha256WithRSAEncryption"); 517 | EXPECT_STREQ(cert->sig_alg_oid, "1.2.840.113549.1.1.11"); 518 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 519 | 520 | //**************************// 521 | // Check the 3. certificate // 522 | cert = second_sig->certs->certs[2]; 523 | ASSERT_TRUE(cert->sha1.data); 524 | ASSERT_EQ(cert->sha1.len, 20); 525 | unsigned char third_cert_sha1[20] = {0xa9, 0xa4, 0x12, 0x10, 0x63, 0xd7, 0x1d, 526 | 0x48, 0xe8, 0x52, 0x9a, 0x46, 0x81, 0xde, 527 | 0x80, 0x3e, 0x3e, 0x79, 0x54, 0xb0}; 528 | EXPECT_TRUE(std::memcmp(third_cert_sha1, cert->sha1.data, 20) == 0); 529 | 530 | ASSERT_TRUE(cert->sha256.data); 531 | ASSERT_EQ(cert->sha256.len, 32); 532 | unsigned char third_cert_sha256[32] = {0xc4, 0x74, 0xce, 0x76, 0x00, 0x7d, 0x02, 0x39, 533 | 0x4e, 0x0d, 0xa5, 0xe4, 0xde, 0x7c, 0x14, 0xc6, 534 | 0x80, 0xf9, 0xe2, 0x82, 0x01, 0x3c, 0xfe, 0xf6, 535 | 0x53, 0xef, 0x5d, 0xb7, 0x1f, 0xdf, 0x61, 0xf8}; 536 | EXPECT_TRUE(std::memcmp(third_cert_sha256, cert->sha256.data, 32) == 0); 537 | 538 | EXPECT_EQ(cert->version, 2); 539 | EXPECT_STREQ( 540 | cert->subject, 541 | "/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec SHA256 TimeStamping " 542 | "Signer - G3"); 543 | EXPECT_STREQ( 544 | cert->issuer, 545 | "/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec SHA256 TimeStamping " 546 | "CA"); 547 | EXPECT_EQ(cert->not_after, 1868918399); 548 | EXPECT_EQ(cert->not_before, 1513987200); 549 | EXPECT_STREQ( 550 | cert->key, 551 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArw6Kqvjcv2l7VBdxRwm9jTyB+" 552 | "HQVd2eQnP3eTgKeS3b25TY+ZdUkIG0w+d0dg+k/" 553 | "J0ozTm0WiuSNQI0iqr6nCxvSB7Y8tRokKPgbclE9yAmIJgg6+fpDI3VHcAyzX1uPCB1ySFdlTa8CPED39N0yOJM/" 554 | "5Sym81kjy4DeE035EMmqChhsVWFX0fECLMS1q/JsI9KfDQ8ZbK2FYmn9ToXBilIxq1vYyXRS41dsIr9Vf2/KBqs/" 555 | "SrcidmXs7DbylpWBJiz9u5iqATjTryVAmwlT8ClXhVhe6oVIQSGH5d600yaye0BTWHmOUjEGTZQDRcTOPAPstwDyOi" 556 | "LFtG/l77CKmwIDAQAB"); 557 | EXPECT_STREQ(cert->serial, "7b:d4:e5:af:ba:cc:07:3f:a1:01:23:04:22:41:4d:12"); 558 | EXPECT_STREQ(cert->sig_alg, "sha256WithRSAEncryption"); 559 | EXPECT_STREQ(cert->sig_alg_oid, "1.2.840.113549.1.1.11"); 560 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 561 | 562 | //*******************************************// 563 | // Test the first signature countersignature // 564 | ASSERT_TRUE(second_sig->countersigs); 565 | ASSERT_TRUE(second_sig->countersigs->counters); 566 | ASSERT_EQ(second_sig->countersigs->count, 1); 567 | 568 | const Countersignature *countersig = second_sig->countersigs->counters[0]; 569 | 570 | EXPECT_EQ(countersig->verify_flags, COUNTERSIGNATURE_VFY_VALID); 571 | EXPECT_STREQ(countersig->digest_alg, "sha1"); 572 | EXPECT_EQ(countersig->sign_time, 1527779086); 573 | unsigned char first_countersig_digest[20] = {0x75, 0x86, 0x41, 0xf1, 0x7b, 0x4a, 0x7a, 574 | 0x86, 0x12, 0xd7, 0x02, 0xaf, 0x8b, 0x9f, 575 | 0xaf, 0x84, 0xb5, 0x06, 0xb5, 0xff}; 576 | ASSERT_TRUE(countersig->digest.data); 577 | ASSERT_EQ(countersig->digest.len, 20); 578 | EXPECT_TRUE(std::memcmp(first_countersig_digest, countersig->digest.data, 20) == 0); 579 | 580 | ASSERT_TRUE(countersig->chain); 581 | EXPECT_EQ(countersig->chain->count, 2); 582 | 583 | //**************************// 584 | // Check the 1. certificate // 585 | cert = countersig->chain->certs[0]; 586 | ASSERT_TRUE(cert->sha1.data); 587 | ASSERT_EQ(cert->sha1.len, 20); 588 | unsigned char first_countercert_sha1[20] = {0xa9, 0xa4, 0x12, 0x10, 0x63, 0xd7, 0x1d, 589 | 0x48, 0xe8, 0x52, 0x9a, 0x46, 0x81, 0xde, 590 | 0x80, 0x3e, 0x3e, 0x79, 0x54, 0xb0}; 591 | EXPECT_TRUE(std::memcmp(first_countercert_sha1, cert->sha1.data, 20) == 0); 592 | 593 | ASSERT_TRUE(cert->sha256.data); 594 | ASSERT_EQ(cert->sha256.len, 32); 595 | unsigned char first_countercert_sha256[32] = {0xc4, 0x74, 0xce, 0x76, 0x00, 0x7d, 0x02, 0x39, 596 | 0x4e, 0x0d, 0xa5, 0xe4, 0xde, 0x7c, 0x14, 0xc6, 597 | 0x80, 0xf9, 0xe2, 0x82, 0x01, 0x3c, 0xfe, 0xf6, 598 | 0x53, 0xef, 0x5d, 0xb7, 0x1f, 0xdf, 0x61, 0xf8}; 599 | EXPECT_TRUE(std::memcmp(first_countercert_sha256, cert->sha256.data, 32) == 0); 600 | 601 | EXPECT_EQ(cert->version, 2); 602 | EXPECT_STREQ( 603 | cert->subject, 604 | "/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec SHA256 TimeStamping " 605 | "Signer - G3"); 606 | EXPECT_STREQ( 607 | cert->issuer, 608 | "/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec SHA256 TimeStamping " 609 | "CA"); 610 | EXPECT_EQ(cert->not_after, 1868918399); 611 | EXPECT_EQ(cert->not_before, 1513987200); 612 | EXPECT_STREQ( 613 | cert->key, 614 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArw6Kqvjcv2l7VBdxRwm9jTyB+" 615 | "HQVd2eQnP3eTgKeS3b25TY+ZdUkIG0w+d0dg+k/" 616 | "J0ozTm0WiuSNQI0iqr6nCxvSB7Y8tRokKPgbclE9yAmIJgg6+fpDI3VHcAyzX1uPCB1ySFdlTa8CPED39N0yOJM/" 617 | "5Sym81kjy4DeE035EMmqChhsVWFX0fECLMS1q/JsI9KfDQ8ZbK2FYmn9ToXBilIxq1vYyXRS41dsIr9Vf2/KBqs/" 618 | "SrcidmXs7DbylpWBJiz9u5iqATjTryVAmwlT8ClXhVhe6oVIQSGH5d600yaye0BTWHmOUjEGTZQDRcTOPAPstwDyOi" 619 | "LFtG/l77CKmwIDAQAB"); 620 | EXPECT_STREQ(cert->serial, "7b:d4:e5:af:ba:cc:07:3f:a1:01:23:04:22:41:4d:12"); 621 | EXPECT_STREQ(cert->sig_alg, "sha256WithRSAEncryption"); 622 | EXPECT_STREQ(cert->sig_alg_oid, "1.2.840.113549.1.1.11"); 623 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 624 | 625 | //**************************// 626 | // Check the 2. certificate // 627 | cert = countersig->chain->certs[1]; 628 | ASSERT_TRUE(cert->sha1.data); 629 | ASSERT_EQ(cert->sha1.len, 20); 630 | unsigned char second_countercert_sha1[20] = {0x6f, 0xc9, 0xed, 0xb5, 0xe0, 0x0a, 0xb6, 631 | 0x41, 0x51, 0xc1, 0xcd, 0xfc, 0xac, 0x74, 632 | 0xad, 0x2c, 0x7b, 0x7e, 0x3b, 0xe4}; 633 | EXPECT_TRUE(std::memcmp(second_countercert_sha1, cert->sha1.data, 20) == 0); 634 | 635 | ASSERT_TRUE(cert->sha256.data); 636 | ASSERT_EQ(cert->sha256.len, 32); 637 | unsigned char second_countercert_sha256[32] = {0xf3, 0x51, 0x6d, 0xdc, 0xc8, 0xaf, 0xc8, 0x08, 638 | 0x78, 0x8b, 0xd8, 0xb0, 0xe8, 0x40, 0xbd, 0xa2, 639 | 0xb5, 0xe2, 0x3c, 0x62, 0x44, 0x25, 0x2c, 0xa3, 640 | 0x00, 0x0b, 0xb6, 0xc8, 0x71, 0x70, 0x40, 0x2a}; 641 | EXPECT_TRUE(std::memcmp(second_countercert_sha256, cert->sha256.data, 32) == 0); 642 | 643 | EXPECT_EQ(cert->version, 2); 644 | EXPECT_STREQ( 645 | cert->subject, 646 | "/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec SHA256 TimeStamping " 647 | "CA"); 648 | EXPECT_STREQ( 649 | cert->issuer, 650 | "/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2008 VeriSign, Inc. - For " 651 | "authorized use only/CN=VeriSign Universal Root Certification Authority"); 652 | EXPECT_EQ(cert->not_after, 1925942399); 653 | EXPECT_EQ(cert->not_before, 1452556800); 654 | EXPECT_STREQ( 655 | cert->key, 656 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1mdWVVPnYxyXRqBoutV87ABrTxxrDKPBWuGmicAMpdqTc" 657 | "lkFEspu8LZKbku7GOz4c8/C1aQ+GIbfuumB+Lef15tQDjUkQbnQXx5HMvLrRu/2JWR8/" 658 | "DubPitljkuf8EnuHg5xYSl7e2vh47Ojcdt6tKYtTofHjmdw/SaqPSE4cTRfHHGBim0P+SDDSbDewg+TfkKtzNJ/" 659 | "8o71PWym0vhiJka9cDpMxTW38eA25Hu/" 660 | "rySV3J39M2ozP4J9ZM3vpWIasXc9LFL1M7oCZFftYR5NYp4rBkyjyPBMkEbWQ6pPrHM+" 661 | "dYr77fY5NUdbRE6kvaTyZzjSO67Uw7UNpeGeMWhNwIDAQAB"); 662 | EXPECT_STREQ(cert->serial, "7b:05:b1:d4:49:68:51:44:f7:c9:89:d2:9c:19:9d:12"); 663 | EXPECT_STREQ(cert->sig_alg, "sha256WithRSAEncryption"); 664 | EXPECT_STREQ(cert->sig_alg_oid, "1.2.840.113549.1.1.11"); 665 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 666 | 667 | authenticode_array_free(auth); 668 | } 669 | 670 | TEST_F(SignatureTest, ThirdSignatureContent) 671 | { 672 | AuthenticodeArray *auth = authenticode_new(data, data_len); 673 | ASSERT_NE(auth, nullptr); 674 | 675 | ASSERT_EQ(auth->count, 3); 676 | ASSERT_NE(auth->signatures, nullptr); 677 | 678 | const Authenticode *third_sig = auth->signatures[2]; 679 | ASSERT_TRUE(third_sig); 680 | 681 | //***********************************// 682 | // Check the third signature content // 683 | EXPECT_EQ(third_sig->version, 1); 684 | 685 | EXPECT_TRUE(third_sig->digest.data); 686 | unsigned char file_digest[32] = {0x3c, 0x3a, 0x78, 0x3f, 0x91, 0x69, 0x22, 0xc6, 687 | 0x57, 0x1d, 0x6d, 0x1d, 0xcb, 0xb4, 0x25, 0xe8, 688 | 0xb3, 0x42, 0xd1, 0x54, 0x66, 0xa3, 0x30, 0xe2, 689 | 0xd4, 0x82, 0x1c, 0x3a, 0xc9, 0xef, 0x32, 0xe8}; 690 | EXPECT_EQ(third_sig->digest.len, 32); 691 | EXPECT_TRUE(std::memcmp(file_digest, third_sig->digest.data, 32) == 0); 692 | EXPECT_STREQ(third_sig->digest_alg, "sha256"); 693 | 694 | EXPECT_EQ(third_sig->verify_flags, AUTHENTICODE_VFY_VALID); 695 | 696 | //****************************// 697 | // Check SignerInfo structure // 698 | ASSERT_TRUE(third_sig->signer); 699 | EXPECT_STREQ(third_sig->signer->digest_alg, "sha256"); 700 | 701 | ASSERT_TRUE(third_sig->signer->digest.data); 702 | ASSERT_EQ(third_sig->signer->digest.len, 32); 703 | unsigned char message_digest[32] = {0xec, 0xbe, 0x17, 0x5e, 0xc3, 0x50, 0x8b, 0xdc, 704 | 0xff, 0xb4, 0x31, 0x2e, 0x91, 0x0f, 0x13, 0xb4, 705 | 0x3a, 0x65, 0xd3, 0xc1, 0x95, 0xcd, 0x15, 0x31, 706 | 0xca, 0x34, 0xfd, 0x83, 0x72, 0x0b, 0x6a, 0x06}; 707 | 708 | EXPECT_TRUE(std::memcmp(message_digest, third_sig->signer->digest.data, 32) == 0); 709 | ASSERT_FALSE(third_sig->signer->program_name); 710 | 711 | EXPECT_TRUE(third_sig->signer->chain); 712 | EXPECT_EQ(third_sig->signer->chain->count, 2); 713 | 714 | //************************************// 715 | // Test the signer ceritificate chain // 716 | 717 | //**************************// 718 | // Check the 1. certificate // 719 | const Certificate *cert = third_sig->signer->chain->certs[0]; 720 | ASSERT_TRUE(cert->sha1.data); 721 | ASSERT_EQ(cert->sha1.len, 20); 722 | unsigned char first_cert_sha1[20] = {0x38, 0xee, 0x42, 0xb7, 0x35, 0xf3, 0x64, 723 | 0x16, 0xeb, 0xb2, 0xf1, 0xb5, 0xd7, 0x3c, 724 | 0xdf, 0xf4, 0x04, 0x34, 0x19, 0xb2}; 725 | EXPECT_TRUE(std::memcmp(first_cert_sha1, cert->sha1.data, 20) == 0); 726 | 727 | ASSERT_TRUE(cert->sha256.data); 728 | ASSERT_EQ(cert->sha256.len, 32); 729 | unsigned char first_cert_sha256[32] = {0x10, 0x38, 0xf9, 0x71, 0xdc, 0x51, 0xb4, 0xe7, 730 | 0xbd, 0xd3, 0x8b, 0x8c, 0x38, 0x74, 0xf5, 0x10, 731 | 0x90, 0x1c, 0x1b, 0xc4, 0x1c, 0x0d, 0xbb, 0xb2, 732 | 0x50, 0x7b, 0x2b, 0x09, 0x27, 0xca, 0xd1, 0xae}; 733 | EXPECT_TRUE(std::memcmp(first_cert_sha256, cert->sha256.data, 32) == 0); 734 | 735 | EXPECT_EQ(cert->version, 2); 736 | EXPECT_STREQ( 737 | cert->subject, 738 | "/C=US/ST=New York/L=New York/O=Slimware Utilities Holdings, Inc./CN=Slimware Utilities " 739 | "Holdings, Inc."); 740 | EXPECT_STREQ( 741 | cert->issuer, 742 | "/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 SHA256 Code " 743 | "Signing CA"); 744 | EXPECT_EQ(cert->not_after, 1579046399); 745 | EXPECT_EQ(cert->not_before, 1513036800); 746 | EXPECT_STREQ( 747 | cert->key, 748 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvNL1QMjGVITZlLmtDZ71zX5tlNvwDdaPIE9TqY742KxKtm" 749 | "MhfGtkBfk52jopOWYVnZMKvOrmP8cAfh5nRpYRWXxt9qKk1oR46y/" 750 | "plOsC1rZItp1fRbCdZENLxp2K1tFXHmbL6EhDN0GZRZliqZzSRljEIHblWeFahl/YZKbwpaT/NfNMu3ShQ8nO/" 751 | "9SUssi0w0z8EyIm1SZBEgByJ2C8YV0Hw19jiKm09lLLY8zJLfSZxi9uM5wdJoJNVRALCr4+yyKvwE+" 752 | "uSqHn5HrLf0OSQbUwmw2e6FhCpdlDr/Ojx8fT/rX7Nqs7T+wGjP6zk8CU/NXzC4IlnTypJ/gVpkVP4QIDAQAB"); 753 | EXPECT_STREQ(cert->serial, "38:bf:a6:1b:82:b8:0f:60:57:15:e4:8a:a1:0d:e1:53"); 754 | EXPECT_STREQ(cert->sig_alg, "sha256WithRSAEncryption"); 755 | EXPECT_STREQ(cert->sig_alg_oid, "1.2.840.113549.1.1.11"); 756 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 757 | 758 | //**************************// 759 | // Check the 2. certificate // 760 | cert = third_sig->signer->chain->certs[1]; 761 | ASSERT_TRUE(cert->sha1.data); 762 | ASSERT_EQ(cert->sha1.len, 20); 763 | unsigned char second_cert_sha1[20] = {0x00, 0x77, 0x90, 0xf6, 0x56, 0x1d, 0xad, 764 | 0x89, 0xb0, 0xbc, 0xd8, 0x55, 0x85, 0x76, 765 | 0x24, 0x95, 0xe3, 0x58, 0xf8, 0xa5}; 766 | EXPECT_TRUE(std::memcmp(second_cert_sha1, cert->sha1.data, 20) == 0); 767 | 768 | ASSERT_TRUE(cert->sha256.data); 769 | ASSERT_EQ(cert->sha256.len, 32); 770 | unsigned char second_cert_sha256[32] = {0x58, 0x2d, 0xc1, 0xd9, 0x7a, 0x79, 0x0e, 0xf0, 771 | 0x4f, 0xe2, 0x56, 0x7b, 0x1e, 0xc8, 0x8c, 0x26, 772 | 0xb0, 0x3b, 0xf6, 0xe9, 0x99, 0x37, 0xca, 0xe6, 773 | 0xa0, 0xb5, 0x03, 0x97, 0xad, 0x20, 0xbb, 0xf8}; 774 | EXPECT_TRUE(std::memcmp(second_cert_sha256, cert->sha256.data, 32) == 0); 775 | 776 | EXPECT_EQ(cert->version, 2); 777 | EXPECT_STREQ( 778 | cert->subject, 779 | "/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 SHA256 Code " 780 | "Signing CA"); 781 | EXPECT_STREQ( 782 | cert->issuer, 783 | "/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For " 784 | "authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5"); 785 | EXPECT_EQ(cert->not_after, 1702166399); 786 | EXPECT_EQ(cert->not_before, 1386633600); 787 | EXPECT_STREQ( 788 | cert->key, 789 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl4MeABavLLHSCMTXaJNRYB5x9uJHtNtYTSNiarS/" 790 | "WhtR96MNGHdou9g2qy8hUNqe8+" 791 | "dfJ04LwpfICXCTqdpcDU6kDZGgtOwUzpFyVC7Oo9tE6VIbP0E8ykrkqsDoOatTzCHQzM9/" 792 | "m+bCzFhqghXuPTbPHMWXBySO8Xu+" 793 | "MS09bty1mUKfS2GVXxxw7hd924vlYYl4x2gbrxF4GpiuxFVHU9mzMtahDkZAxZeSitFTp5lbhTVX0+" 794 | "qTYmEgCscwdyQRTWKDtrp7aIIx7mXK3/nVjbI13Iwrb2pyXGCEnPIMlF7AVlIASMzT+KV93i/" 795 | "XE+Q4qITVRrgThsIbnepaON2b2wIDAQAB"); 796 | EXPECT_STREQ(cert->serial, "3d:78:d7:f9:76:49:60:b2:61:7d:f4:f0:1e:ca:86:2a"); 797 | EXPECT_STREQ(cert->sig_alg, "sha256WithRSAEncryption"); 798 | EXPECT_STREQ(cert->sig_alg_oid, "1.2.840.113549.1.1.11"); 799 | EXPECT_STREQ(cert->key_alg, "rsaEncryption"); 800 | 801 | ASSERT_TRUE(third_sig->certs); 802 | ASSERT_TRUE(third_sig->certs->certs); 803 | ASSERT_EQ(third_sig->certs->count, 4); 804 | 805 | //*******************************************// 806 | // Test the first signature countersignature // 807 | ASSERT_TRUE(third_sig->countersigs); 808 | ASSERT_TRUE(third_sig->countersigs->counters); 809 | ASSERT_EQ(third_sig->countersigs->count, 1); 810 | 811 | const Countersignature *countersig = third_sig->countersigs->counters[0]; 812 | 813 | EXPECT_EQ(countersig->verify_flags, COUNTERSIGNATURE_VFY_VALID); 814 | EXPECT_STREQ(countersig->digest_alg, "sha256"); 815 | EXPECT_EQ(countersig->sign_time, 1527779085); 816 | unsigned char first_countersig_digest[32] = {0xea, 0x26, 0x7f, 0x65, 0x17, 0xc1, 0x84, 0x57, 817 | 0x1f, 0x2f, 0x2e, 0x83, 0xf9, 0x6e, 0x75, 0xdf, 818 | 0xcf, 0xcd, 0x57, 0x17, 0xe1, 0xa0, 0xf7, 0x46, 819 | 0x0f, 0xb4, 0x37, 0x6f, 0xe9, 0x64, 0x06, 0xbb}; 820 | ASSERT_TRUE(countersig->digest.data); 821 | ASSERT_EQ(countersig->digest.len, 32); 822 | EXPECT_TRUE(std::memcmp(first_countersig_digest, countersig->digest.data, 32) == 0); 823 | 824 | ASSERT_TRUE(countersig->chain); 825 | EXPECT_EQ(countersig->chain->count, 2); 826 | 827 | authenticode_array_free(auth); 828 | } 829 | 830 | TEST(PefileTest, ResultOverview) 831 | { 832 | initialize_authenticode_parser(); 833 | 834 | AuthenticodeArray *auth = parse_authenticode(PE_FILE_1, PE_FILE_1_LEN); 835 | ASSERT_NE(auth, nullptr); 836 | 837 | ASSERT_EQ(auth->count, 2); 838 | ASSERT_NE(auth->signatures, nullptr); 839 | 840 | const Authenticode *sig = auth->signatures[0]; 841 | { 842 | ASSERT_TRUE(sig); 843 | EXPECT_EQ(sig->verify_flags, AUTHENTICODE_VFY_WRONG_FILE_DIGEST); 844 | EXPECT_EQ(sig->digest.len, 20); 845 | EXPECT_STREQ(sig->digest_alg, "sha1"); 846 | 847 | EXPECT_STREQ(sig->certs->certs[0]->sig_alg_oid, "1.2.840.113549.1.1.5"); 848 | EXPECT_STREQ(sig->certs->certs[0]->sig_alg, "sha1WithRSAEncryption"); 849 | 850 | unsigned char sig_digest[] = {0xD6, 0x43, 0x40, 0x50, 0x56, 0xA4, 0xA1, 0x60, 0x42, 0xD4, 851 | 0x79, 0x42, 0xA8, 0xC6, 0xA5, 0x95, 0x24, 0xBD, 0xA6, 0x4A}; 852 | EXPECT_TRUE(std::memcmp(sig->digest.data, sig_digest, sig->digest.len) == 0); 853 | 854 | unsigned char real_file_digest[] = {0x9a, 0xd3, 0x54, 0xc6, 0xd1, 0xd3, 0xe5, 855 | 0xe5, 0x8b, 0xc4, 0x7e, 0x1c, 0xd3, 0x80, 856 | 0xd1, 0x2b, 0x75, 0xe5, 0x05, 0x1c}; 857 | 858 | EXPECT_TRUE( 859 | std::memcmp(sig->file_digest.data, real_file_digest, sizeof(real_file_digest)) == 0); 860 | } 861 | sig = auth->signatures[1]; 862 | { 863 | ASSERT_TRUE(sig); 864 | EXPECT_EQ(sig->verify_flags, AUTHENTICODE_VFY_WRONG_FILE_DIGEST); 865 | 866 | EXPECT_STREQ(sig->certs->certs[0]->sig_alg_oid, "1.2.840.113549.1.1.5"); 867 | EXPECT_STREQ(sig->certs->certs[0]->sig_alg, "sha1WithRSAEncryption"); 868 | 869 | unsigned char sig_digest[] = {0x75, 0xCA, 0xCD, 0xF5, 0xBE, 0x7B, 0xAE, 0xEC, 870 | 0xB8, 0x9C, 0x70, 0xBC, 0x01, 0x34, 0x3F, 0xB7, 871 | 0xC9, 0xE8, 0xFD, 0x00, 0x0C, 0xC1, 0x91, 0xF0, 872 | 0x8D, 0x2A, 0x99, 0x63, 0x59, 0xD6, 0x17, 0xFE}; 873 | 874 | EXPECT_TRUE(std::memcmp(sig->digest.data, sig_digest, sizeof(sig_digest)) == 0); 875 | 876 | unsigned char real_file_digest[] = {0x29, 0xc3, 0x24, 0xac, 0xc3, 0xbd, 0x59, 0x6c, 877 | 0xce, 0xbd, 0x28, 0xe7, 0xd8, 0xa8, 0x8b, 0x87, 878 | 0xb0, 0x6a, 0x87, 0xf2, 0xfd, 0x1f, 0xc2, 0x81, 879 | 0x52, 0x5c, 0xe0, 0xda, 0xe4, 0x2b, 0x46, 0xb3}; 880 | 881 | EXPECT_TRUE( 882 | std::memcmp(sig->file_digest.data, real_file_digest, sizeof(real_file_digest)) == 0); 883 | } 884 | 885 | authenticode_array_free(auth); 886 | } 887 | --------------------------------------------------------------------------------