21 Commits

Author SHA1 Message Date
olszomal bf209e0fc8 Refactor SpcIndirectDataContent parsing helpers 2026-05-26 17:27:46 +02:00
olszomal 68a6826cd1 Use ASN1_STRING and ASN1_INTEGER APIs instead of direct struct access 2026-05-26 17:27:46 +02:00
olszomal ea5d15862d cmake: add documentation generation, GNU install dirs, and uninstall target 2026-05-26 17:27:46 +02:00
olszomal bdde95635f attach-signature flow moved to digest-only verification semantics
attach-signature now uses digest-only verification instead of full
signature validation; output file is not kept if digest verification fails
2026-04-02 17:43:43 +02:00
olszomal d0ef178a9a Remove redundant NULL assignments, CID 1669015 2026-02-25 14:07:58 +01:00
olszomal e126ab3e4a Fix memory leak of compressedData on error paths, CID 1669014 2026-02-25 14:07:58 +01:00
Michał Trojnara a1fb6600fb Fix handling OpenSSL initialization errors 2026-02-11 16:23:57 +01:00
Michał Trojnara 8227c68ceb Do not export osslsigncode.c consts 2026-02-11 15:50:25 +01:00
Michał Trojnara c988b48063 Refactor UI method cleanup 2026-02-11 15:48:17 +01:00
Michał Trojnara 6cf70b4af2 Initial 2.14-dev commit 2026-02-10 23:17:56 +01:00
Michał Trojnara 97a9ade6ec Release 2.13
Signed-off-by: Michał Trojnara <Michal.Trojnara@stunnel.org>
2026-02-10 22:51:25 +01:00
Michał Trojnara f2f33bb131 Move providers_cleanup() back 2026-02-10 19:10:00 +01:00
Michał Trojnara 202b2c2866 Separate OpenSSL and functional initialization 2026-02-10 18:37:48 +01:00
olszomal 2a5409b7c4 Fix header bounds validation in PE page hash calculation 2026-02-09 16:15:21 +01:00
olszomal 87bce8e372 Fix memory leaks on error paths in PE signing 2026-02-09 16:15:21 +01:00
olszomal f7ace57c81 Validate PE attribute certificate table bounds 2026-02-09 16:15:21 +01:00
olszomal 92f8761b47 Fix heap corruption in PE page hash calculation 2026-02-06 15:41:01 +01:00
Antoni Klajn 09d3312fd9 Fixed integer overflow, integer underflow and Out-of-Bounds Read 2026-02-06 11:39:49 +01:00
olszomal 9d02a20aec Fix unsafe ZIP size handling and allocation checks 2026-02-04 10:13:03 +01:00
olszomal f190ec5d87 Fix double free in ZIP local header 2026-02-04 10:13:03 +01:00
Michał Trojnara 4b30d6be28 Initial 2.12-dev commit 2026-02-03 17:20:29 +01:00
16 changed files with 1491 additions and 421 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ on:
env: env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release BUILD_TYPE: Release
version: osslsigncode-2.12 version: osslsigncode-2.14-dev
jobs: jobs:
build: build:
+25 -5
View File
@@ -10,13 +10,13 @@ set(BUILTIN_SOCKET ON CACHE BOOL "") # for static Python
# configure basic project information # configure basic project information
project(osslsigncode project(osslsigncode
VERSION 2.12 VERSION 2.14
DESCRIPTION "OpenSSL based Authenticode signing for PE, CAB, CAT, MSI, APPX and script files" DESCRIPTION "OpenSSL based Authenticode signing for PE, CAB, CAT, MSI, APPX and script files"
HOMEPAGE_URL "https://github.com/mtrojnar/osslsigncode" HOMEPAGE_URL "https://github.com/mtrojnar/osslsigncode"
LANGUAGES C) LANGUAGES C)
# force nonstandard version format for development packages # force nonstandard version format for development packages
set(DEV "") set(DEV "-dev")
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}${DEV}") set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}${DEV}")
# version and contact information # version and contact information
@@ -97,14 +97,25 @@ set_target_properties(osslsigncode PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE)
# testing with CTest # testing with CTest
include(CMakeTest) include(CMakeTest)
# documentation with Pandoc
include(CMakeDoc)
# installation rules for a project # installation rules for a project
set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin") include(GNUInstallDirs)
install(TARGETS osslsigncode RUNTIME DESTINATION ${BINDIR})
install(TARGETS osslsigncode RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES
"${PROJECT_SOURCE_DIR}/README.md"
"${PROJECT_SOURCE_DIR}/NEWS.md"
DESTINATION "${CMAKE_INSTALL_DOCDIR}")
if(UNIX) if(UNIX)
include(CMakeDist) include(CMakeDist)
else(UNIX) else(UNIX)
install( install(
DIRECTORY ${PROJECT_BINARY_DIR}/ DESTINATION ${BINDIR} DIRECTORY ${PROJECT_BINARY_DIR}/
DESTINATION ${CMAKE_INSTALL_BINDIR}
FILES_MATCHING FILES_MATCHING
PATTERN "*.dll" PATTERN "*.dll"
PATTERN "vcpkg_installed" EXCLUDE PATTERN "vcpkg_installed" EXCLUDE
@@ -112,6 +123,15 @@ else(UNIX)
PATTERN "Testing" EXCLUDE) PATTERN "Testing" EXCLUDE)
endif(UNIX) endif(UNIX)
# uninstall target
configure_file(
"${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
"${PROJECT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake")
#[[ #[[
Local Variables: Local Variables:
c-basic-offset: 4 c-basic-offset: 4
+20
View File
@@ -1,5 +1,25 @@
# osslsigncode change log # osslsigncode change log
### 2.14 (unreleased)
- attach-signature now uses digest-only verification instead of full signature
validation; output file is not kept if digest verification fails
### 2.13 (2026.02.10)
**MULTIPLE SECURITY VULNERABILITIES**
This release includes important security fixes. Users are strongly encouraged
to upgrade, as the issues below may be exploitable when processing untrusted
files.
- fixed integer overflows when processing APPX compressed data streams
(by Małgorzata Olszówka)
- fixed double-free vulnerabilities in APPX file processing
(by Małgorzata Olszówka)
- fixed multiple memory corruption issues in PE page hash computation
(by Antoni Klajn (Opera) and Małgorzata Olszówka)
### 2.12 (2026.02.02) ### 2.12 (2026.02.02)
**CRITICAL SECURITY VULNERABILITY** **CRITICAL SECURITY VULNERABILITY**
+80 -33
View File
@@ -470,32 +470,33 @@ static int appx_hash_length_get(FILE_FORMAT_CTX *ctx)
*/ */
static int appx_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7) static int appx_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
{ {
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) { SpcIndirectDataContent *idc;
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence; BIO *hashes;
const u_char *p = content_val->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
if (idc) { idc = pkcs7_get_indirect_data_content(p7);
BIO *hashes; if (!idc)
if (!appx_extract_hashes(ctx, idc)) { return 1; /* OK - no SpcIndirectDataContent */
fprintf(stderr, "Failed to extract hashes from the signature\n");
SpcIndirectDataContent_free(idc); if (!appx_extract_hashes(ctx, idc)) {
return 0; /* FAILED */ fprintf(stderr, "Failed to extract hashes from the signature\n");
} SpcIndirectDataContent_free(idc);
hashes = appx_calculate_hashes(ctx); return 0; /* FAILED */
if (!hashes) {
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
BIO_free_all(hashes);
if (!appx_compare_hashes(ctx)) {
fprintf(stderr, "Signature hash verification failed\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
}
} }
hashes = appx_calculate_hashes(ctx);
if (!hashes) {
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
BIO_free_all(hashes);
if (!appx_compare_hashes(ctx)) {
fprintf(stderr, "Signature hash verification failed\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
return 1; /* OK */ return 1; /* OK */
} }
@@ -1077,13 +1078,13 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con
AppxSpcSipInfo_free(si); AppxSpcSipInfo_free(si);
BIO_free_all(stdbio); BIO_free_all(stdbio);
#endif #endif
int length = content->messageDigest->digest->length; int len = ASN1_STRING_length(content->messageDigest->digest);
uint8_t *data = content->messageDigest->digest->data; const uint8_t *data = ASN1_STRING_get0_data(content->messageDigest->digest);
int mdlen = EVP_MD_size(ctx->appx_ctx->md); int mdlen = EVP_MD_size(ctx->appx_ctx->md);
int pos = 4; int pos = 4;
/* we are expecting at least 4 hashes + 4 byte header */ /* we are expecting at least 4 hashes + 4 byte header */
if (length < 4 * mdlen + 4) { if (len < 4 * mdlen + 4) {
fprintf(stderr, "Hash too short\n"); fprintf(stderr, "Hash too short\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
@@ -1091,7 +1092,7 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con
fprintf(stderr, "Hash signature does not match\n"); fprintf(stderr, "Hash signature does not match\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
while (pos + mdlen + 4 <= length) { while (pos + mdlen + 4 <= len) {
if (!memcmp(data + pos, AXPC_SIGNATURE, 4)) { if (!memcmp(data + pos, AXPC_SIGNATURE, 4)) {
ctx->appx_ctx->existingDataHash = OPENSSL_malloc((size_t)mdlen); ctx->appx_ctx->existingDataHash = OPENSSL_malloc((size_t)mdlen);
memcpy(ctx->appx_ctx->existingDataHash, data + pos + 4, (size_t)mdlen); memcpy(ctx->appx_ctx->existingDataHash, data + pos + 4, (size_t)mdlen);
@@ -1503,6 +1504,7 @@ static int zipAppendSignatureFile(BIO *bio, ZIP_FILE *zip, uint8_t *data, uint64
if (!get_current_position(bio, &offset)) { if (!get_current_position(bio, &offset)) {
fprintf(stderr, "Unable to get offset\n"); fprintf(stderr, "Unable to get offset\n");
OPENSSL_free(header.fileName); OPENSSL_free(header.fileName);
header.fileName = NULL;
OPENSSL_free(dataToWrite); OPENSSL_free(dataToWrite);
return 0; /* FAILED */ return 0; /* FAILED */
} }
@@ -1513,6 +1515,7 @@ static int zipAppendSignatureFile(BIO *bio, ZIP_FILE *zip, uint8_t *data, uint64
if (!BIO_write_ex(bio, dataToWrite + written, toWrite, &check) if (!BIO_write_ex(bio, dataToWrite + written, toWrite, &check)
|| check != toWrite) { || check != toWrite) {
OPENSSL_free(header.fileName); OPENSSL_free(header.fileName);
header.fileName = NULL;
OPENSSL_free(dataToWrite); OPENSSL_free(dataToWrite);
return 0; /* FAILED */ return 0; /* FAILED */
} }
@@ -1685,6 +1688,8 @@ static int zipRewriteData(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_ENTRY *entry, BIO
out: out:
OPENSSL_free(header.fileName); OPENSSL_free(header.fileName);
OPENSSL_free(header.extraField); OPENSSL_free(header.extraField);
header.fileName = NULL;
header.extraField = NULL;
return ret; return ret;
} }
@@ -1852,6 +1857,11 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
} }
if (entry->overrideData) { if (entry->overrideData) {
compressedSize = entry->overrideData->compressedSize; compressedSize = entry->overrideData->compressedSize;
/* Validate sizes for safe allocation */
if (compressedSize > (uint64_t)(SIZE_MAX - 1)) {
fprintf(stderr, "Corrupted compressedSize : %" PRIu64"\n", compressedSize);
return 0; /* FAILED */
}
uncompressedSize = entry->overrideData->uncompressedSize; uncompressedSize = entry->overrideData->uncompressedSize;
compressedData = OPENSSL_zalloc(compressedSize + 1); compressedData = OPENSSL_zalloc(compressedSize + 1);
memcpy(compressedData, entry->overrideData->data, compressedSize); memcpy(compressedData, entry->overrideData->data, compressedSize);
@@ -1863,6 +1873,8 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
if (!zipReadLocalHeader(&header, zip, compressedSize)) { if (!zipReadLocalHeader(&header, zip, compressedSize)) {
OPENSSL_free(header.fileName); OPENSSL_free(header.fileName);
OPENSSL_free(header.extraField); OPENSSL_free(header.extraField);
header.fileName = NULL;
header.extraField = NULL;
return 0; /* FAILED */ return 0; /* FAILED */
} }
if (header.fileNameLen != entry->fileNameLen if (header.fileNameLen != entry->fileNameLen
@@ -1873,14 +1885,20 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
fprintf(stderr, "Local header does not match central directory entry\n"); fprintf(stderr, "Local header does not match central directory entry\n");
OPENSSL_free(header.fileName); OPENSSL_free(header.fileName);
OPENSSL_free(header.extraField); OPENSSL_free(header.extraField);
header.fileName = NULL;
header.extraField = NULL;
return 0; /* FAILED */ return 0; /* FAILED */
} }
/* we don't really need those */ /* we don't really need those */
OPENSSL_free(header.fileName); OPENSSL_free(header.fileName);
OPENSSL_free(header.extraField); OPENSSL_free(header.extraField);
header.fileName = NULL;
header.extraField = NULL;
if (compressedSize > (uint64_t)zip->fileSize - entry->offsetOfLocalHeader) { /* Validate sizes for safe allocation */
fprintf(stderr, "Corrupted compressedSize : 0x%08" PRIX64 "\n", entry->compressedSize); if (compressedSize > (uint64_t)(SIZE_MAX - 1)
|| compressedSize > (uint64_t)zip->fileSize - entry->offsetOfLocalHeader) {
fprintf(stderr, "Corrupted compressedSize : %" PRIu64"\n", compressedSize);
return 0; /* FAILED */ return 0; /* FAILED */
} }
compressedData = OPENSSL_zalloc(compressedSize + 1); compressedData = OPENSSL_zalloc(compressedSize + 1);
@@ -1899,11 +1917,26 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
*pData = compressedData; *pData = compressedData;
dataSize = compressedSize; dataSize = compressedSize;
} else if (entry->compression == COMPRESSION_DEFLATE) { } else if (entry->compression == COMPRESSION_DEFLATE) {
uint8_t *uncompressedData = OPENSSL_zalloc(uncompressedSize + 1); uint8_t *uncompressedData;
uint64_t destLen = uncompressedSize; uint64_t destLen, sourceLen;
uint64_t sourceLen = compressedSize;
int ret; int ret;
/* Validate sizes for safe allocation */
if (uncompressedSize > (uint64_t)(SIZE_MAX - 1)) {
fprintf(stderr, "Corrupted uncompressedSize : %" PRIu64"\n", uncompressedSize);
OPENSSL_free(compressedData);
return 0; /* FAILED */
}
/* Detect suspicious compression ratio (zip bomb protection) */
if (uncompressedSize > 1024 * 1024 && uncompressedSize / 100 >= compressedSize) {
fprintf(stderr, "Error: suspicious compression ratio\n");
OPENSSL_free(compressedData);
return 0; /* FAILED */
}
uncompressedData = OPENSSL_zalloc(uncompressedSize + 1);
destLen = uncompressedSize;
sourceLen = compressedSize;
ret = zipInflate(uncompressedData, &destLen, compressedData, (uLong *)&sourceLen); ret = zipInflate(uncompressedData, &destLen, compressedData, (uLong *)&sourceLen);
OPENSSL_free(compressedData); OPENSSL_free(compressedData);
@@ -1970,6 +2003,8 @@ static int zipReadLocalHeader(ZIP_LOCAL_HEADER *header, ZIP_FILE *zip, uint64_t
header->extraFieldLen = fileGetU16(file); header->extraFieldLen = fileGetU16(file);
/* file name (variable size) */ /* file name (variable size) */
if (header->fileNameLen > 0) { if (header->fileNameLen > 0) {
/* fileNameLen is uint16_t (ZIP spec, 2-byte field),
* so fileNameLen + 1 cannot overflow size_t */
header->fileName = OPENSSL_zalloc(header->fileNameLen + 1); header->fileName = OPENSSL_zalloc(header->fileNameLen + 1);
size = fread(header->fileName, 1, header->fileNameLen, file); size = fread(header->fileName, 1, header->fileNameLen, file);
if (size != header->fileNameLen) { if (size != header->fileNameLen) {
@@ -1981,6 +2016,8 @@ static int zipReadLocalHeader(ZIP_LOCAL_HEADER *header, ZIP_FILE *zip, uint64_t
} }
/* extra field (variable size) */ /* extra field (variable size) */
if (header->extraFieldLen > 0) { if (header->extraFieldLen > 0) {
/* extraFieldLen is uint16_t (ZIP spec, 2-byte field),
* so extraFieldLen + 1 cannot overflow size_t */
header->extraField = OPENSSL_zalloc(header->extraFieldLen + 1); header->extraField = OPENSSL_zalloc(header->extraFieldLen + 1);
size = fread(header->extraField, 1, header->extraFieldLen, file); size = fread(header->extraField, 1, header->extraFieldLen, file);
if (size != header->extraFieldLen) { if (size != header->extraFieldLen) {
@@ -2011,6 +2048,8 @@ static int zipReadLocalHeader(ZIP_LOCAL_HEADER *header, ZIP_FILE *zip, uint64_t
fprintf(stderr, "The input file is not a valid zip file - flags indicate data descriptor, but data descriptor signature does not match\n"); fprintf(stderr, "The input file is not a valid zip file - flags indicate data descriptor, but data descriptor signature does not match\n");
OPENSSL_free(header->fileName); OPENSSL_free(header->fileName);
OPENSSL_free(header->extraField); OPENSSL_free(header->extraField);
header->fileName = NULL;
header->extraField = NULL;
return 0; /* FAILED */ return 0; /* FAILED */
} }
header->crc32 = fileGetU32(file); header->crc32 = fileGetU32(file);
@@ -2477,6 +2516,8 @@ static ZIP_CENTRAL_DIRECTORY_ENTRY *zipReadNextCentralDirectoryEntry(FILE *file)
entry->offsetOfLocalHeader = fileGetU32(file); entry->offsetOfLocalHeader = fileGetU32(file);
/* file name (variable size) */ /* file name (variable size) */
if (entry->fileNameLen > 0) { if (entry->fileNameLen > 0) {
/* fileNameLen is uint16_t (ZIP spec, 2-byte field),
* so fileNameLen + 1 cannot overflow size_t */
entry->fileName = OPENSSL_zalloc(entry->fileNameLen + 1); entry->fileName = OPENSSL_zalloc(entry->fileNameLen + 1);
size = fread(entry->fileName, 1, entry->fileNameLen, file); size = fread(entry->fileName, 1, entry->fileNameLen, file);
if (size != entry->fileNameLen) { if (size != entry->fileNameLen) {
@@ -2487,6 +2528,8 @@ static ZIP_CENTRAL_DIRECTORY_ENTRY *zipReadNextCentralDirectoryEntry(FILE *file)
} }
/* extra field (variable size) */ /* extra field (variable size) */
if (entry->extraFieldLen > 0) { if (entry->extraFieldLen > 0) {
/* extraFieldLen is uint16_t (ZIP spec, 2-byte field),
* so extraFieldLen + 1 cannot overflow size_t */
entry->extraField = OPENSSL_zalloc(entry->extraFieldLen + 1); entry->extraField = OPENSSL_zalloc(entry->extraFieldLen + 1);
size = fread(entry->extraField, 1, entry->extraFieldLen, file); size = fread(entry->extraField, 1, entry->extraFieldLen, file);
if (size != entry->extraFieldLen) { if (size != entry->extraFieldLen) {
@@ -2497,6 +2540,8 @@ static ZIP_CENTRAL_DIRECTORY_ENTRY *zipReadNextCentralDirectoryEntry(FILE *file)
} }
/* file comment (variable size) */ /* file comment (variable size) */
if (entry->fileCommentLen > 0) { if (entry->fileCommentLen > 0) {
/* fileCommentLen is uint16_t (ZIP spec, 2-byte field),
* so fileCommentLen + 1 cannot overflow size_t */
entry->fileComment = OPENSSL_zalloc(entry->fileCommentLen + 1); entry->fileComment = OPENSSL_zalloc(entry->fileCommentLen + 1);
size = fread(entry->fileComment, 1, entry->fileCommentLen, file); size = fread(entry->fileComment, 1, entry->fileCommentLen, file);
if (size != entry->fileCommentLen) { if (size != entry->fileCommentLen) {
@@ -2635,6 +2680,8 @@ static int readZipEOCDR(ZIP_EOCDR *eocdr, FILE *file)
} }
#endif #endif
if (eocdr->commentLen > 0) { if (eocdr->commentLen > 0) {
/* ZIP_EOCDR commentLen is uint16_t (ZIP spec, 2-byte field),
* so fileCommentLen + 1 cannot overflow size_t */
eocdr->comment = OPENSSL_zalloc(eocdr->commentLen + 1); eocdr->comment = OPENSSL_zalloc(eocdr->commentLen + 1);
size = fread(eocdr->comment, 1, eocdr->commentLen, file); size = fread(eocdr->comment, 1, eocdr->commentLen, file);
if (size != eocdr->commentLen) { if (size != eocdr->commentLen) {
+1 -13
View File
@@ -337,19 +337,7 @@ static int cab_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
u_char mdbuf[EVP_MAX_MD_SIZE]; u_char mdbuf[EVP_MAX_MD_SIZE];
u_char *cmdbuf; u_char *cmdbuf;
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) { if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
const u_char *p = content_val->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
if (idc) {
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) {
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
}
}
if (mdtype == -1) {
fprintf(stderr, "Failed to extract current message digest\n\n"); fprintf(stderr, "Failed to extract current message digest\n\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
+26 -18
View File
@@ -297,27 +297,39 @@ static int cat_add_content_type(PKCS7 *p7, PKCS7 *cursig)
*/ */
static int cat_sign_content(PKCS7 *p7, PKCS7 *contents) static int cat_sign_content(PKCS7 *p7, PKCS7 *contents)
{ {
u_char *content; const unsigned char *sequence_data;
int seqhdrlen, content_length; const unsigned char *content;
ASN1_STRING *sequence;
int seqhdrlen, sequence_len, content_length;
if (!contents->d.other || !contents->d.other->value.sequence if (!contents->d.other || !contents->d.other->value.sequence) {
|| !contents->d.other->value.sequence->data) {
fprintf(stderr, "Failed to get content value\n"); fprintf(stderr, "Failed to get content value\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
seqhdrlen = asn1_simple_hdr_len(contents->d.other->value.sequence->data,
contents->d.other->value.sequence->length); sequence = contents->d.other->value.sequence;
content = contents->d.other->value.sequence->data + seqhdrlen; sequence_data = ASN1_STRING_get0_data(sequence);
content_length = contents->d.other->value.sequence->length - seqhdrlen; sequence_len = ASN1_STRING_length(sequence);
if (!sequence_data) {
fprintf(stderr, "Failed to get content value\n");
return 0; /* FAILED */
}
seqhdrlen = asn1_simple_hdr_len(sequence_data, sequence_len);
content = (const unsigned char *)sequence_data + seqhdrlen;
content_length = sequence_len - seqhdrlen;
if (!pkcs7_sign_content(p7, content, content_length)) { if (!pkcs7_sign_content(p7, content, content_length)) {
fprintf(stderr, "Failed to sign content\n"); fprintf(stderr, "Failed to sign content\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
if (!PKCS7_set_content(p7, PKCS7_dup(contents))) { if (!PKCS7_set_content(p7, PKCS7_dup(contents))) {
fprintf(stderr, "PKCS7_set_content failed\n"); fprintf(stderr, "PKCS7_set_content failed\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
return 1; /* OK */ return 1; /* OK */
} }
@@ -381,26 +393,22 @@ static int cat_print_content_member_digest(ASN1_TYPE *content)
{ {
SpcIndirectDataContent *idc; SpcIndirectDataContent *idc;
u_char mdbuf[EVP_MAX_MD_SIZE]; u_char mdbuf[EVP_MAX_MD_SIZE];
const u_char *data ;
int mdtype = -1; int mdtype = -1;
ASN1_STRING *value;
value = content->value.sequence; idc = asn1_type_get_indirect_data_content(content);
data = ASN1_STRING_get0_data(value);
idc = d2i_SpcIndirectDataContent(NULL, &data, ASN1_STRING_length(value));
if (!idc) if (!idc)
return 0; /* FAILED */ return 0; /* FAILED */
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) {
if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
fprintf(stderr, "Failed to extract message digest from signature\n\n");
SpcIndirectDataContent_free(idc); SpcIndirectDataContent_free(idc);
return 0; /* FAILED */ return 0; /* FAILED */
} }
SpcIndirectDataContent_free(idc); SpcIndirectDataContent_free(idc);
if (mdtype == -1) {
fprintf(stderr, "Failed to extract current message digest\n\n");
return 0; /* FAILED */
}
printf("\tHash algorithm: %s\n", OBJ_nid2sn(mdtype)); printf("\tHash algorithm: %s\n", OBJ_nid2sn(mdtype));
print_hash("\tMessage digest", "", mdbuf, EVP_MD_size(EVP_get_digestbynid(mdtype))); print_hash("\tMessage digest", "", mdbuf, EVP_MD_size(EVP_get_digestbynid(mdtype)));
return 1; /* OK */ return 1; /* OK */
} }
+44
View File
@@ -0,0 +1,44 @@
# documentation with Pandoc
# cmake --build .
find_program(PANDOC pandoc)
if(NOT PANDOC)
message(WARNING "CMakeDoc: pandoc not found, documentation disabled")
return()
endif(NOT PANDOC)
set(DOC_MD "${PROJECT_SOURCE_DIR}/osslsigncode.md")
if(NOT EXISTS "${DOC_MD}")
message(WARNING "CMakeDoc: markdown source not found: ${DOC_MD}")
return()
endif(NOT EXISTS "${DOC_MD}")
set(MAN_PAGE "${PROJECT_BINARY_DIR}/osslsigncode.1")
set(HTML_PAGE "${PROJECT_BINARY_DIR}/osslsigncode.html")
add_custom_command(
OUTPUT "${MAN_PAGE}"
COMMAND "${PANDOC}" -s "${DOC_MD}" -t man -o "${MAN_PAGE}"
DEPENDS "${DOC_MD}"
COMMENT "CMakeDoc: generating man page"
VERBATIM)
add_custom_command(
OUTPUT "${HTML_PAGE}"
COMMAND "${PANDOC}" -s --toc --toc-depth=2 "${DOC_MD}" -t html -o "${HTML_PAGE}"
DEPENDS "${DOC_MD}"
COMMENT "CMakeDoc: generating HTML documentation"
VERBATIM)
add_custom_target(docs ALL DEPENDS "${MAN_PAGE}" "${HTML_PAGE}")
#[[
Local Variables:
c-basic-offset: 4
tab-width: 4
indent-tabs-mode: nil
End:
vim: set ts=4 expandtab:
]]
+2 -14
View File
@@ -196,18 +196,12 @@ if(Python3_FOUND AND NOT cryptography_error)
foreach(format ${formats}) foreach(format ${formats})
add_test(NAME "attached_${format}_${ext}" add_test(NAME "attached_${format}_${ext}"
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature" COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature"
# sign options
"-add-msi-dse" "-add-msi-dse"
"-h" "sha512" "-h" "sha512"
"-nest" "-nest"
"-sigin" "${FILES}/${ext}.${format}" "-sigin" "${FILES}/${ext}.${format}"
"-in" "${FILES}/signed.${ext}" "-in" "${FILES}/signed.${ext}"
"-out" "${FILES}/attached_${format}.${ext}" "-out" "${FILES}/attached_${format}.${ext}")
# verify options
"-require-leaf-hash" "FILE ${CERTS}/leafhash.txt"
"-time" "1567296000" # Signature verification time: Sep 1 00:00:00 2019 GMT
"-CAfile" "${CERTS}/CACert.pem"
"-CRLfile" "${CERTS}/CACertCRL.pem")
set_tests_properties("attached_${format}_${ext}" PROPERTIES set_tests_properties("attached_${format}_${ext}" PROPERTIES
DEPENDS "signed_${ext};extract_pem_${ext};extract_der_${ext}") DEPENDS "signed_${ext};extract_pem_${ext};extract_der_${ext}")
list(APPEND ALL_TESTS "attached_${format}_${ext}") list(APPEND ALL_TESTS "attached_${format}_${ext}")
@@ -413,17 +407,11 @@ if(Python3_FOUND AND NOT cryptography_error)
foreach(format ${formats}) foreach(format ${formats})
add_test(NAME "attached_data_${ext}_${data_format}_${format}" add_test(NAME "attached_data_${ext}_${data_format}_${format}"
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature" COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature"
# sign options
"-add-msi-dse" "-add-msi-dse"
"-h" "sha384" "-h" "sha384"
"-sigin" "${FILES}/signed_data_${ext}_${data_format}.${format}" "-sigin" "${FILES}/signed_data_${ext}_${data_format}.${format}"
"-in" "${FILES}/unsigned.${ext}" "-in" "${FILES}/unsigned.${ext}"
"-out" "${FILES}/attached_data_${data_format}_${format}.${ext}" "-out" "${FILES}/attached_data_${data_format}_${format}.${ext}")
# verify options
"-require-leaf-hash" "FILE ${CERTS}/leafhash.txt"
"-time" "1567296000" # Signature verification time: Sep 1 00:00:00 2019 GMT
"-CAfile" "${CERTS}/CACert.pem"
"-CRLfile" "${CERTS}/CACertCRL.pem")
set_tests_properties("attached_data_${ext}_${data_format}_${format}" PROPERTIES set_tests_properties("attached_data_${ext}_${data_format}_${format}" PROPERTIES
DEPENDS "signed_data_${ext}_${data_format};signed_data_pem_${ext}_${data_format}") DEPENDS "signed_data_${ext}_${data_format};signed_data_pem_${ext}_${data_format}")
list(APPEND ALL_TESTS "attached_data_${ext}_${data_format}_${format}") list(APPEND ALL_TESTS "attached_data_${ext}_${data_format}_${format}")
+24
View File
@@ -0,0 +1,24 @@
# uninstall target
#
# CMake does not provide a built-in uninstall target.
# This target removes files listed in install_manifest.txt,
# generated by the install step.
#
# cmake --build . --target uninstall
if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest")
endif()
file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
string(REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Removing ${file}")
if(EXISTS "${file}" OR IS_SYMLINK "${file}")
file(REMOVE "${file}")
else()
message(STATUS "File does not exist: ${file}")
endif()
endforeach()
+131 -29
View File
@@ -341,6 +341,91 @@ PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content)
return p7; return p7;
} }
/*
* Retrieve the message digest and digest algorithm from PKCS7
* SpcIndirectDataContent.
*
* [in] p7: PKCS7 structure containing SPC_INDIRECT_DATA_OBJID content
* [out] mdbuf: message digest buffer, at least EVP_MAX_MD_SIZE bytes
* [out] mdtype: OpenSSL NID of the digest algorithm
* [returns] 0 on error or 1 on success
*/
int pkcs7_get_content_digest(PKCS7 *p7, u_char *mdbuf, int *mdtype)
{
SpcIndirectDataContent *idc;
if (!mdbuf || !mdtype)
return 0; /* FAILED */
*mdtype = -1;
idc = pkcs7_get_indirect_data_content(p7);
if (!idc) {
fprintf(stderr, "Failed to decode SpcIndirectDataContent\n\n");
return 0; /* FAILED */
}
if (spc_indirect_data_content_get_digest(idc, mdbuf, mdtype) < 0) {
fprintf(stderr, "Failed to extract message digest from signature\n\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
if (*mdtype == -1) {
fprintf(stderr, "Failed to extract current message digest\n\n");
return 0; /* FAILED */
}
return 1; /* OK */
}
/*
* Decode SpcIndirectDataContent from a PKCS7 signedData content.
*
* [in] p7: PKCS7 structure containing SPC_INDIRECT_DATA_OBJID content
* [returns] newly allocated SpcIndirectDataContent, or NULL on error
*
* The caller is responsible for freeing the returned object with
* SpcIndirectDataContent_free().
*/
SpcIndirectDataContent *pkcs7_get_indirect_data_content(PKCS7 *p7)
{
if (!is_content_type(p7, SPC_INDIRECT_DATA_OBJID))
return NULL;
if (!p7->d.sign || !p7->d.sign->contents || !p7->d.sign->contents->d.other)
return NULL;
return asn1_type_get_indirect_data_content(p7->d.sign->contents->d.other);
}
/*
* Decode SpcIndirectDataContent from an ASN1_TYPE object.
* The ASN1_TYPE is expected to contain a V_ASN1_SEQUENCE value.
*
* [in] content: ASN1_TYPE containing DER-encoded SpcIndirectDataContent
* [returns] newly allocated SpcIndirectDataContent, or NULL on error
*
* The caller is responsible for freeing the returned object with
* SpcIndirectDataContent_free().
*/
SpcIndirectDataContent *asn1_type_get_indirect_data_content(ASN1_TYPE *content)
{
ASN1_STRING *value;
const unsigned char *data;
int len;
if (!content || content->type != V_ASN1_SEQUENCE)
return NULL;
value = content->value.sequence;
if (!value)
return NULL;
data = ASN1_STRING_get0_data(value);
len = ASN1_STRING_length(value);
return d2i_SpcIndirectDataContent(NULL, &data, len);
}
/* /*
* Return spcIndirectDataContent. * Return spcIndirectDataContent.
* [in] hash: message digest BIO * [in] hash: message digest BIO
@@ -548,33 +633,6 @@ SpcLink *spc_link_obsolete_get(void)
return link; return link;
} }
/*
* Safely extract digest from SpcIndirectDataContent
* [in] idc: parsed SpcIndirectDataContent
* [out] mdbuf: output buffer (must be EVP_MAX_MD_SIZE bytes)
* [out] mdtype: digest algorithm's NID
* [returns] -1 on error or digest length on success
*/
int spc_extract_digest_safe(SpcIndirectDataContent *idc,
u_char *mdbuf, int *mdtype)
{
int digest_len;
if (!idc || !idc->messageDigest || !idc->messageDigest->digest ||
!idc->messageDigest->digestAlgorithm) {
fprintf(stderr, "Missing digest data\n");
return -1;
}
digest_len = idc->messageDigest->digest->length;
if (digest_len <= 0 || digest_len > EVP_MAX_MD_SIZE) {
fprintf(stderr, "Invalid digest length: %d\n", digest_len);
return -1;
}
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)digest_len);
*mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
return digest_len;
}
/* /*
* [in] mdbuf, cmdbuf: message digests * [in] mdbuf, cmdbuf: message digests
* [in] mdtype: message digest algorithm type * [in] mdtype: message digest algorithm type
@@ -590,6 +648,42 @@ int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype)
return mdok; return mdok;
} }
/*
* Safely extract digest from SpcIndirectDataContent with bounds checking.
* This function validates that the digest length from the ASN.1 structure
* does not exceed the destination buffer size, preventing buffer overflows
* from maliciously crafted signatures.
* [in] idc: parsed SpcIndirectDataContent structure
* [out] mdbuf: output buffer (must be at least EVP_MAX_MD_SIZE bytes)
* [out] mdtype: digest algorithm NID
* [returns] digest length on success, -1 on error
*/
int spc_indirect_data_content_get_digest(SpcIndirectDataContent *idc, u_char *mdbuf, int *mdtype)
{
ASN1_OCTET_STRING *digest_asn1;
const unsigned char *digest_data;
int digest_len;
if (!idc || !idc->messageDigest || !idc->messageDigest->digest ||
!idc->messageDigest->digestAlgorithm) {
return -1; /* FAILED */
}
digest_asn1 = idc->messageDigest->digest;
digest_len = ASN1_STRING_length((ASN1_STRING *)digest_asn1);
/* Validate digest length to prevent buffer overflow */
if (digest_len <= 0 || digest_len > EVP_MAX_MD_SIZE) {
fprintf(stderr, "Invalid digest length in signature: %d (expected 1-%d)\n",
digest_len, EVP_MAX_MD_SIZE);
return -1; /* FAILED */
}
digest_data = ASN1_STRING_get0_data((ASN1_STRING *)digest_asn1);
*mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
memcpy(mdbuf, digest_data, (size_t)digest_len);
return digest_len; /* OK */
}
/* /*
* Helper functions * Helper functions
*/ */
@@ -645,8 +739,16 @@ static int spc_indirect_data_content_create(u_char **blob, int *len, FILE_FORMAT
idc->data->value->type = V_ASN1_SEQUENCE; idc->data->value->type = V_ASN1_SEQUENCE;
idc->data->value->value.sequence = ASN1_STRING_new(); idc->data->value->value.sequence = ASN1_STRING_new();
idc->data->type = ctx->format->data_blob_get(&p, &l, ctx); idc->data->type = ctx->format->data_blob_get(&p, &l, ctx);
idc->data->value->value.sequence->data = p; if (!idc->data->type) {
idc->data->value->value.sequence->length = l; SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
if (!ASN1_STRING_set(idc->data->value->value.sequence, p, l)) {
OPENSSL_free(p);
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
OPENSSL_free(p);
idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(mdtype); idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(mdtype);
idc->messageDigest->digestAlgorithm->parameters = ASN1_TYPE_new(); idc->messageDigest->digestAlgorithm->parameters = ASN1_TYPE_new();
idc->messageDigest->digestAlgorithm->parameters->type = V_ASN1_NULL; idc->messageDigest->digestAlgorithm->parameters->type = V_ASN1_NULL;
+4 -2
View File
@@ -15,6 +15,9 @@ PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx);
int add_indirect_data_object(PKCS7 *p7); int add_indirect_data_object(PKCS7 *p7);
int sign_spc_indirect_data_content(PKCS7 *p7, ASN1_OCTET_STRING *content); int sign_spc_indirect_data_content(PKCS7 *p7, ASN1_OCTET_STRING *content);
PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content); PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content);
int pkcs7_get_content_digest(PKCS7 *p7, u_char *mdbuf, int *mdtype);
SpcIndirectDataContent *pkcs7_get_indirect_data_content(PKCS7 *p7);
SpcIndirectDataContent *asn1_type_get_indirect_data_content(ASN1_TYPE *content);
ASN1_OCTET_STRING *spc_indirect_data_content_get(BIO *hash, FILE_FORMAT_CTX *ctx); ASN1_OCTET_STRING *spc_indirect_data_content_get(BIO *hash, FILE_FORMAT_CTX *ctx);
int pkcs7_sign_content(PKCS7 *p7, const u_char *data, int len); int pkcs7_sign_content(PKCS7 *p7, const u_char *data, int len);
int asn1_simple_hdr_len(const u_char *p, int len); int asn1_simple_hdr_len(const u_char *p, int len);
@@ -24,9 +27,8 @@ int is_content_type(PKCS7 *p7, const char *objid);
MsCtlContent *ms_ctl_content_get(PKCS7 *p7); MsCtlContent *ms_ctl_content_get(PKCS7 *p7);
ASN1_TYPE *catalog_content_get(CatalogAuthAttr *attribute); ASN1_TYPE *catalog_content_get(CatalogAuthAttr *attribute);
SpcLink *spc_link_obsolete_get(void); SpcLink *spc_link_obsolete_get(void);
int spc_extract_digest_safe(SpcIndirectDataContent *idc,
u_char *mdbuf, int *mdtype);
int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype); int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype);
int spc_indirect_data_content_get_digest(SpcIndirectDataContent *idc, u_char *mdbuf, int *mdtype);
/* /*
Local Variables: Local Variables:
+1 -13
View File
@@ -414,19 +414,7 @@ static int msi_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
const EVP_MD *md; const EVP_MD *md;
BIO *hash; BIO *hash;
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) { if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
const u_char *p = content_val->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
if (idc) {
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) {
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
}
}
if (mdtype == -1) {
fprintf(stderr, "Failed to extract current message digest\n\n"); fprintf(stderr, "Failed to extract current message digest\n\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
+278 -197
View File
@@ -67,7 +67,7 @@
* 2:d=1 hl=2 l= 2 prim: BIT STRING * 2:d=1 hl=2 l= 2 prim: BIT STRING
* 6:d=1 hl=2 l= 0 cons: SEQUENCE * 6:d=1 hl=2 l= 0 cons: SEQUENCE
*/ */
const u_char java_attrs_low[] = { static const u_char java_attrs_low[] = {
0x30, 0x06, 0x03, 0x02, 0x00, 0x01, 0x30, 0x00 0x30, 0x06, 0x03, 0x02, 0x00, 0x01, 0x30, 0x00
}; };
@@ -76,7 +76,7 @@ const u_char java_attrs_low[] = {
* 0:d=0 hl=2 l= 12 cons: SEQUENCE * 0:d=0 hl=2 l= 12 cons: SEQUENCE
* 2:d=1 hl=2 l= 10 prim: OBJECT :Microsoft Individual Code Signing * 2:d=1 hl=2 l= 10 prim: OBJECT :Microsoft Individual Code Signing
*/ */
const u_char purpose_ind[] = { static const u_char purpose_ind[] = {
0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
0x01, 0x82, 0x37, 0x02, 0x01, 0x15 0x01, 0x82, 0x37, 0x02, 0x01, 0x15
}; };
@@ -86,11 +86,13 @@ const u_char purpose_ind[] = {
* 0:d=0 hl=2 l= 12 cons: SEQUENCE * 0:d=0 hl=2 l= 12 cons: SEQUENCE
* 2:d=1 hl=2 l= 10 prim: OBJECT :Microsoft Commercial Code Signing * 2:d=1 hl=2 l= 10 prim: OBJECT :Microsoft Commercial Code Signing
*/ */
const u_char purpose_comm[] = { static const u_char purpose_comm[] = {
0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
0x01, 0x82, 0x37, 0x02, 0x01, 0x16 0x01, 0x82, 0x37, 0x02, 0x01, 0x16
}; };
static UI_METHOD *ui_method;
/* /*
* ASN.1 definitions (more or less from official MS Authenticode docs) * ASN.1 definitions (more or less from official MS Authenticode docs)
*/ */
@@ -230,7 +232,7 @@ static ASN1_INTEGER *create_nonce(int bits);
static char *clrdp_url_get_x509(X509 *cert); static char *clrdp_url_get_x509(X509 *cert);
static time_t time_t_get_asn1_time(const ASN1_TIME *s); static time_t time_t_get_asn1_time(const ASN1_TIME *s);
static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si); static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si);
static ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si); static const ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si);
static time_t time_t_get_cms_time(CMS_ContentInfo *cms); static time_t time_t_get_cms_time(CMS_ContentInfo *cms);
static CMS_ContentInfo *cms_get_timestamp(PKCS7_SIGNED *p7_signed, static CMS_ContentInfo *cms_get_timestamp(PKCS7_SIGNED *p7_signed,
PKCS7_SIGNER_INFO *countersignature); PKCS7_SIGNER_INFO *countersignature);
@@ -241,6 +243,7 @@ static int X509_attribute_chain_append_object(STACK_OF(X509_ATTRIBUTE) **unauth_
static STACK_OF(PKCS7) *signature_list_create(PKCS7 *p7); static STACK_OF(PKCS7) *signature_list_create(PKCS7 *p7);
static int PKCS7_compare(const PKCS7 *const *a, const PKCS7 *const *b); static int PKCS7_compare(const PKCS7 *const *a, const PKCS7 *const *b);
static PKCS7 *pkcs7_get_sigfile(FILE_FORMAT_CTX *ctx); static PKCS7 *pkcs7_get_sigfile(FILE_FORMAT_CTX *ctx);
static STACK_OF(PKCS7) *get_signature_list(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options);
static void print_cert(X509 *cert, int i); static void print_cert(X509 *cert, int i);
static int x509_store_load_crlfile(X509_STORE *store, char *cafile, char *crlfile); static int x509_store_load_crlfile(X509_STORE *store, char *cafile, char *crlfile);
static void load_objects_from_store(const char *url, char *pass, EVP_PKEY **pkey, STACK_OF(X509) *certs, STACK_OF(X509_CRL) *crls); static void load_objects_from_store(const char *url, char *pass, EVP_PKEY **pkey, STACK_OF(X509) *certs, STACK_OF(X509_CRL) *crls);
@@ -293,7 +296,8 @@ static BIO *bio_encode_rfc3161_request(PKCS7 *p7, const EVP_MD *md)
TS_REQ *req = NULL; TS_REQ *req = NULL;
BIO *bout = NULL, *bhash = NULL; BIO *bout = NULL, *bhash = NULL;
u_char *p; u_char *p;
int len; const u_char *digest;
int digest_len, len;
signer_info = PKCS7_get_signer_info(p7); signer_info = PKCS7_get_signer_info(p7);
if (!signer_info) if (!signer_info)
@@ -303,6 +307,9 @@ static BIO *bio_encode_rfc3161_request(PKCS7 *p7, const EVP_MD *md)
if (!si) if (!si)
goto out; goto out;
digest = ASN1_STRING_get0_data(si->enc_digest);
digest_len = ASN1_STRING_length(si->enc_digest);
bhash = BIO_new(BIO_f_md()); bhash = BIO_new(BIO_f_md());
#if defined(__GNUC__) #if defined(__GNUC__)
#pragma GCC diagnostic push #pragma GCC diagnostic push
@@ -316,7 +323,7 @@ static BIO *bio_encode_rfc3161_request(PKCS7 *p7, const EVP_MD *md)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
BIO_push(bhash, BIO_new(BIO_s_null())); BIO_push(bhash, BIO_new(BIO_s_null()));
BIO_write(bhash, si->enc_digest->data, si->enc_digest->length); BIO_write(bhash, digest, digest_len);
BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md)); BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md));
req = TS_REQ_new(); req = TS_REQ_new();
@@ -374,6 +381,7 @@ static ASN1_INTEGER *create_nonce(int bits)
{ {
unsigned char buf[20]; unsigned char buf[20];
ASN1_INTEGER *nonce = NULL; ASN1_INTEGER *nonce = NULL;
BIGNUM *bn = NULL;
int len = (bits - 1) / 8 + 1; int len = (bits - 1) / 8 + 1;
int i; int i;
@@ -388,15 +396,21 @@ static ASN1_INTEGER *create_nonce(int bits)
/* Find the first non-zero byte and creating ASN1_INTEGER object. */ /* Find the first non-zero byte and creating ASN1_INTEGER object. */
for (i = 0; i < len && !buf[i]; ++i) { for (i = 0; i < len && !buf[i]; ++i) {
} }
nonce = ASN1_INTEGER_new();
bn = BN_bin2bn(buf + i, len - i, NULL);
if (!bn) {
fprintf(stderr, "Could not create nonce BIGNUM\n");
return NULL;
}
nonce = BN_to_ASN1_INTEGER(bn, NULL);
BN_free(bn);
if (!nonce) { if (!nonce) {
fprintf(stderr, "Could not create nonce\n"); fprintf(stderr, "Could not create nonce\n");
return NULL; return NULL;
} }
OPENSSL_free(nonce->data);
nonce->length = len - i;
nonce->data = OPENSSL_malloc((size_t)nonce->length + 1);
memcpy(nonce->data, buf + i, (size_t)nonce->length);
return nonce; return nonce;
} }
@@ -1621,7 +1635,7 @@ static int X509_attribute_chain_append_object(STACK_OF(X509_ATTRIBUTE) **unauth_
u_char *p, int len, const char *oid) u_char *p, int len, const char *oid)
{ {
X509_ATTRIBUTE *attr = NULL; X509_ATTRIBUTE *attr = NULL;
ASN1_OBJECT *object; const ASN1_OBJECT *object;
char object_txt[128]; char object_txt[128];
if (*unauth_attr == NULL) { if (*unauth_attr == NULL) {
@@ -1632,7 +1646,7 @@ static int X509_attribute_chain_append_object(STACK_OF(X509_ATTRIBUTE) **unauth_
int i; int i;
for (i = 0; i < X509at_get_attr_count(*unauth_attr); i++) { for (i = 0; i < X509at_get_attr_count(*unauth_attr); i++) {
attr = X509at_get_attr(*unauth_attr, i); attr = X509at_get_attr(*unauth_attr, i);
object = X509_ATTRIBUTE_get0_object(attr); object = (const ASN1_OBJECT *)X509_ATTRIBUTE_get0_object(attr);
if (object == NULL) if (object == NULL)
continue; continue;
object_txt[0] = 0x00; object_txt[0] = 0x00;
@@ -2196,18 +2210,22 @@ static int verify_timestamp_token(PKCS7 *p7, CMS_ContentInfo *timestamp)
/* get the embedded content */ /* get the embedded content */
pos = CMS_get0_content(timestamp); pos = CMS_get0_content(timestamp);
if (pos != NULL && *pos != NULL) { if (pos != NULL && *pos != NULL) {
const u_char *p = (*pos)->data; const u_char *p = ASN1_STRING_get0_data(*pos);
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, (*pos)->length); int len = ASN1_STRING_length(*pos);
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, len);
if (token) { if (token) {
BIO *bhash; BIO *bhash;
u_char mdbuf[EVP_MAX_MD_SIZE]; u_char mdbuf[EVP_MAX_MD_SIZE];
ASN1_OCTET_STRING *hash; ASN1_OCTET_STRING *hash;
const ASN1_OBJECT *aoid; const ASN1_OBJECT *aoid;
int md_nid; const u_char *hash_data;
int hash_len, md_nid;
const EVP_MD *md; const EVP_MD *md;
TS_MSG_IMPRINT *msg_imprint = TS_TST_INFO_get_msg_imprint(token); TS_MSG_IMPRINT *msg_imprint = TS_TST_INFO_get_msg_imprint(token);
const X509_ALGOR *alg = TS_MSG_IMPRINT_get_algo(msg_imprint); const X509_ALGOR *alg = TS_MSG_IMPRINT_get_algo(msg_imprint);
const u_char *digest = ASN1_STRING_get0_data(si->enc_digest);
int digest_len = ASN1_STRING_length(si->enc_digest);
X509_ALGOR_get0(&aoid, NULL, NULL, alg); X509_ALGOR_get0(&aoid, NULL, NULL, alg);
md_nid = OBJ_obj2nid(aoid); md_nid = OBJ_obj2nid(aoid);
@@ -2229,17 +2247,19 @@ static int verify_timestamp_token(PKCS7 *p7, CMS_ContentInfo *timestamp)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
BIO_push(bhash, BIO_new(BIO_s_null())); BIO_push(bhash, BIO_new(BIO_s_null()));
BIO_write(bhash, si->enc_digest->data, si->enc_digest->length); BIO_write(bhash, digest, digest_len);
BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md)); BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md));
BIO_free_all(bhash); BIO_free_all(bhash);
/* compare the provided hash against the computed hash */ /* compare the provided hash against the computed hash */
hash =TS_MSG_IMPRINT_get_msg(msg_imprint); hash =TS_MSG_IMPRINT_get_msg(msg_imprint);
if (memcmp(mdbuf, hash->data, (size_t)hash->length)) { hash_data = ASN1_STRING_get0_data(hash);
hash_len = ASN1_STRING_length(hash);
if (memcmp(mdbuf, hash_data, (size_t)hash_len)) {
printf("Hash value mismatch:\n\tMessage digest algorithm: %s\n", printf("Hash value mismatch:\n\tMessage digest algorithm: %s\n",
(md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid)); (md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid));
print_hash("\tComputed message digest", "", mdbuf, EVP_MD_size(md)); print_hash("\tComputed message digest", "", mdbuf, EVP_MD_size(md));
print_hash("\tReceived message digest", "", hash->data, hash->length); print_hash("\tReceived message digest", "", hash_data, hash_len);
printf("\nFile's message digest verification: failed\n"); printf("\nFile's message digest verification: failed\n");
TS_TST_INFO_free(token); TS_TST_INFO_free(token);
return 0; /* FAILED */ return 0; /* FAILED */
@@ -2413,7 +2433,7 @@ static int verify_pkcs7_data(PKCS7 *p7, X509_STORE *store)
&& (contents->d.other->value.sequence->length > 0)) { && (contents->d.other->value.sequence->length > 0)) {
if (contents->d.other->type == V_ASN1_SEQUENCE) { if (contents->d.other->type == V_ASN1_SEQUENCE) {
/* only verify the content of the sequence */ /* only verify the content of the sequence */
const unsigned char *data = contents->d.other->value.sequence->data; const u_char *data = contents->d.other->value.sequence->data;
long len; long len;
int inf, tag, class; int inf, tag, class;
@@ -2685,11 +2705,12 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
{ {
STACK_OF(PKCS7_SIGNER_INFO) *signer_info; STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
PKCS7_SIGNER_INFO *si; PKCS7_SIGNER_INFO *si;
int md_nid, i; int md_nid, i, len;
STACK_OF(X509_ATTRIBUTE) *auth_attr, *unauth_attr; STACK_OF(X509_ATTRIBUTE) *auth_attr, *unauth_attr;
X509_ATTRIBUTE *attr; X509_ATTRIBUTE *attr;
ASN1_OBJECT *object; const ASN1_OBJECT *object;
ASN1_STRING *value; const ASN1_STRING *value;
const u_char *data;
char object_txt[128]; char object_txt[128];
time_t time = INVALID_TIME; time_t time = INVALID_TIME;
@@ -2708,24 +2729,24 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
printf("\nAuthenticated attributes:\n"); printf("\nAuthenticated attributes:\n");
for (i=0; i<X509at_get_attr_count(auth_attr); i++) { for (i=0; i<X509at_get_attr_count(auth_attr); i++) {
attr = X509at_get_attr(auth_attr, i); attr = X509at_get_attr(auth_attr, i);
object = X509_ATTRIBUTE_get0_object(attr); object = (const ASN1_OBJECT *)X509_ATTRIBUTE_get0_object(attr);
if (object == NULL) if (object == NULL)
continue; continue;
object_txt[0] = 0x00; object_txt[0] = 0x00;
OBJ_obj2txt(object_txt, sizeof object_txt, object, 1); OBJ_obj2txt(object_txt, sizeof object_txt, object, 1);
if (!strcmp(object_txt, PKCS9_MESSAGE_DIGEST)) { if (!strcmp(object_txt, PKCS9_MESSAGE_DIGEST)) {
/* PKCS#9 message digest - Policy OID: 1.2.840.113549.1.9.4 */ /* PKCS#9 message digest - Policy OID: 1.2.840.113549.1.9.4 */
const u_char *mdbuf; value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_OCTET_STRING, NULL);
int len; if (value == NULL)
ASN1_STRING *digest = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_OCTET_STRING, NULL);
if (digest == NULL)
continue; continue;
mdbuf = ASN1_STRING_get0_data(digest); data = ASN1_STRING_get0_data(value);
len = ASN1_STRING_length(digest); len = ASN1_STRING_length(value);
print_hash("\tMessage digest", "", mdbuf, len); print_hash("\tMessage digest", "", data, len);
} else if (!strcmp(object_txt, PKCS9_SIGNING_TIME)) { } else if (!strcmp(object_txt, PKCS9_SIGNING_TIME)) {
/* PKCS#9 signing time - Policy OID: 1.2.840.113549.1.9.5 */ /* PKCS#9 signing time - Policy OID: 1.2.840.113549.1.9.5 */
ASN1_UTCTIME *signtime = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL); const ASN1_UTCTIME *signtime;
signtime = (const ASN1_UTCTIME *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL);
if (signtime == NULL) if (signtime == NULL)
continue; continue;
printf("\tSigning time: "); printf("\tSigning time: ");
@@ -2733,30 +2754,39 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
} else if (!strcmp(object_txt, SPC_SP_OPUS_INFO_OBJID)) { } else if (!strcmp(object_txt, SPC_SP_OPUS_INFO_OBJID)) {
/* Microsoft OID: 1.3.6.1.4.1.311.2.1.12 */ /* Microsoft OID: 1.3.6.1.4.1.311.2.1.12 */
SpcSpOpusInfo *opus; SpcSpOpusInfo *opus;
const u_char *data;
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL); value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
if (value == NULL) if (value == NULL)
continue; continue;
data = ASN1_STRING_get0_data(value); data = ASN1_STRING_get0_data(value);
opus = d2i_SpcSpOpusInfo(NULL, &data, ASN1_STRING_length(value)); len = ASN1_STRING_length(value);
opus = d2i_SpcSpOpusInfo(NULL, &data, len);
if (opus == NULL) if (opus == NULL)
continue; continue;
if (opus->moreInfo && opus->moreInfo->type == 0) { if (opus->moreInfo && opus->moreInfo->type == 0) {
char *url = OPENSSL_strdup((char *)opus->moreInfo->value.url->data); ASN1_IA5STRING *url_asn1 = opus->moreInfo->value.url;
printf("\tURL description: %s\n", url); const u_char *url_data = ASN1_STRING_get0_data((ASN1_STRING *)url_asn1);
OPENSSL_free(url); int url_length = ASN1_STRING_length((ASN1_STRING *)url_asn1);
printf("\tURL description: %.*s\n", url_length, url_data);
} }
if (opus->programName) { if (opus->programName) {
char *desc = NULL; char *desc = NULL;
if (opus->programName->type == 0) { if (opus->programName->type == 0) {
u_char *opusdata; u_char *opus_data;
int len = ASN1_STRING_to_UTF8(&opusdata, opus->programName->value.unicode); int opus_len = ASN1_STRING_to_UTF8(&opus_data, opus->programName->value.unicode);
if (len >= 0) {
desc = OPENSSL_strndup((char *)opusdata, (size_t)len); if (opus_len >= 0) {
OPENSSL_free(opusdata); desc = OPENSSL_strndup((char *)opus_data, (size_t)opus_len);
OPENSSL_free(opus_data);
} }
} else { } else {
desc = OPENSSL_strdup((char *)opus->programName->value.ascii->data); ASN1_IA5STRING *desc_asn1 = opus->programName->value.ascii;
const u_char *desc_data = ASN1_STRING_get0_data((ASN1_STRING *)desc_asn1);
int desc_len = ASN1_STRING_length((ASN1_STRING *)desc_asn1);
desc = OPENSSL_strndup((const char *)desc_data, (size_t)desc_len);
} }
if (desc) { if (desc) {
printf("\tText description: %s\n", desc); printf("\tText description: %s\n", desc);
@@ -2766,31 +2796,31 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
SpcSpOpusInfo_free(opus); SpcSpOpusInfo_free(opus);
} else if (!strcmp(object_txt, SPC_STATEMENT_TYPE_OBJID)) { } else if (!strcmp(object_txt, SPC_STATEMENT_TYPE_OBJID)) {
/* Microsoft OID: 1.3.6.1.4.1.311.2.1.11 */ /* Microsoft OID: 1.3.6.1.4.1.311.2.1.11 */
const u_char *purpose; value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
if (value == NULL) if (value == NULL)
continue; continue;
purpose = ASN1_STRING_get0_data(value); data = ASN1_STRING_get0_data(value);
if (!memcmp(purpose, purpose_comm, sizeof purpose_comm)) if (!memcmp(data, purpose_comm, sizeof purpose_comm))
printf("\tMicrosoft Commercial Code Signing purpose\n"); printf("\tMicrosoft Commercial Code Signing purpose\n");
else if (!memcmp(purpose, purpose_ind, sizeof purpose_ind)) else if (!memcmp(data, purpose_ind, sizeof purpose_ind))
printf("\tMicrosoft Individual Code Signing purpose\n"); printf("\tMicrosoft Individual Code Signing purpose\n");
else else
printf("\tUnrecognized Code Signing purpose\n"); printf("\tUnrecognized Code Signing purpose\n");
} else if (!strcmp(object_txt, MS_JAVA_SOMETHING)) { } else if (!strcmp(object_txt, MS_JAVA_SOMETHING)) {
/* Microsoft OID: 1.3.6.1.4.1.311.15.1 */ /* Microsoft OID: 1.3.6.1.4.1.311.15.1 */
const u_char *level; value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
if (value == NULL) if (value == NULL)
continue; continue;
level = ASN1_STRING_get0_data(value); data = ASN1_STRING_get0_data(value);
if (!memcmp(level, java_attrs_low, sizeof java_attrs_low)) if (!memcmp(data, java_attrs_low, sizeof java_attrs_low))
printf("\tLow level of permissions in Microsoft Internet Explorer 4.x for CAB files\n"); printf("\tLow level of permissions in Microsoft Internet Explorer 4.x for CAB files\n");
else else
printf("\tUnrecognized level of permissions in Microsoft Internet Explorer 4.x for CAB files\n"); printf("\tUnrecognized level of permissions in Microsoft Internet Explorer 4.x for CAB files\n");
} else if (!strcmp(object_txt, PKCS9_SEQUENCE_NUMBER)) { } else if (!strcmp(object_txt, PKCS9_SEQUENCE_NUMBER)) {
/* PKCS#9 sequence number - Policy OID: 1.2.840.113549.1.9.25.4 */ /* PKCS#9 sequence number - Policy OID: 1.2.840.113549.1.9.25.4 */
ASN1_INTEGER *number = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_INTEGER, NULL); const ASN1_INTEGER *number;
number = (const ASN1_INTEGER *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_INTEGER, NULL);
if (number == NULL) if (number == NULL)
continue; continue;
printf("\tSequence number: %ld\n", ASN1_INTEGER_get(number)); printf("\tSequence number: %ld\n", ASN1_INTEGER_get(number));
@@ -2801,17 +2831,17 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
unauth_attr = PKCS7_get_attributes(si); /* cont[1] */ unauth_attr = PKCS7_get_attributes(si); /* cont[1] */
for (i=0; i<X509at_get_attr_count(unauth_attr); i++) { for (i=0; i<X509at_get_attr_count(unauth_attr); i++) {
attr = X509at_get_attr(unauth_attr, i); attr = X509at_get_attr(unauth_attr, i);
object = X509_ATTRIBUTE_get0_object(attr); object = (const ASN1_OBJECT *)X509_ATTRIBUTE_get0_object(attr);
if (object == NULL) if (object == NULL)
continue; continue;
object_txt[0] = 0x00; object_txt[0] = 0x00;
OBJ_obj2txt(object_txt, sizeof object_txt, object, 1); OBJ_obj2txt(object_txt, sizeof object_txt, object, 1);
if (!strcmp(object_txt, PKCS9_COUNTER_SIGNATURE)) { if (!strcmp(object_txt, PKCS9_COUNTER_SIGNATURE)) {
/* Authenticode Timestamp - Policy OID: 1.2.840.113549.1.9.6 */ /* Authenticode Timestamp - Policy OID: 1.2.840.113549.1.9.6 */
const u_char *data;
CMS_ContentInfo *cms; CMS_ContentInfo *cms;
PKCS7_SIGNER_INFO *countersi; PKCS7_SIGNER_INFO *countersi;
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
if (value == NULL) if (value == NULL)
continue; continue;
data = ASN1_STRING_get0_data(value); data = ASN1_STRING_get0_data(value);
@@ -2841,9 +2871,9 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
} }
} else if (!strcmp(object_txt, SPC_RFC3161_OBJID)) { } else if (!strcmp(object_txt, SPC_RFC3161_OBJID)) {
/* RFC3161 Timestamp - Policy OID: 1.3.6.1.4.1.311.3.3.1 */ /* RFC3161 Timestamp - Policy OID: 1.3.6.1.4.1.311.3.3.1 */
const u_char *data;
CMS_ContentInfo *cms; CMS_ContentInfo *cms;
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
if (value == NULL) if (value == NULL)
continue; continue;
data = ASN1_STRING_get0_data(value); data = ASN1_STRING_get0_data(value);
@@ -2869,27 +2899,35 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
} }
} else if (!strcmp(object_txt, SPC_UNAUTHENTICATED_DATA_BLOB_OBJID)) { } else if (!strcmp(object_txt, SPC_UNAUTHENTICATED_DATA_BLOB_OBJID)) {
/* Unauthenticated Data Blob - Policy OID: 1.3.6.1.4.1.42921.1.2.1 */ /* Unauthenticated Data Blob - Policy OID: 1.3.6.1.4.1.42921.1.2.1 */
ASN1_STRING *blob = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTF8STRING, NULL); value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTF8STRING, NULL);
if (blob == NULL) { if (value == NULL) {
printf("Warning: Unauthenticated Data Blob could not be decoded correctly\n"); printf("Warning: Unauthenticated Data Blob could not be decoded correctly\n");
continue; continue;
} }
len = ASN1_STRING_length(value);
if (verbose) { if (verbose) {
char *data_blob = OPENSSL_buf2hexstr(blob->data, blob->length); char *data_blob;
data = ASN1_STRING_get0_data(value);
data_blob = OPENSSL_buf2hexstr(data, len);
printf("\nUnauthenticated Data Blob:\n%s\n", data_blob); printf("\nUnauthenticated Data Blob:\n%s\n", data_blob);
OPENSSL_free(data_blob); OPENSSL_free(data_blob);
} else { } else {
printf("\nUnauthenticated Data Blob length: %d bytes\n", blob->length); printf("\nUnauthenticated Data Blob length: %d bytes\n", len);
} }
} }
} }
/* Signature */ /* Signature */
if (verbose) { if (verbose) {
data = ASN1_STRING_get0_data(si->enc_digest);
len = ASN1_STRING_length(si->enc_digest);
md_nid = OBJ_obj2nid(si->digest_enc_alg->algorithm); md_nid = OBJ_obj2nid(si->digest_enc_alg->algorithm);
printf("\nDigest encryption algorithm: %s\n", printf("\nDigest encryption algorithm: %s\n",
(md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2sn(md_nid)); (md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2sn(md_nid));
print_hash("Signature", "", ASN1_STRING_get0_data(si->enc_digest), ASN1_STRING_length(si->enc_digest)); print_hash("Signature", "", data, len);
} }
return time; return time;
@@ -2925,7 +2963,7 @@ static time_t time_t_get_asn1_time(const ASN1_TIME *s)
*/ */
static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si) static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si)
{ {
ASN1_UTCTIME *time = asn1_time_get_si_time(si); const ASN1_UTCTIME *time = asn1_time_get_si_time(si);
if (time == NULL) if (time == NULL)
return INVALID_TIME; /* FAILED */ return INVALID_TIME; /* FAILED */
@@ -2937,7 +2975,7 @@ static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si)
* [in] si: PKCS7_SIGNER_INFO structure * [in] si: PKCS7_SIGNER_INFO structure
* [returns] NULL on error or ASN1_UTCTIME on success * [returns] NULL on error or ASN1_UTCTIME on success
*/ */
static ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si) static const ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si)
{ {
STACK_OF(X509_ATTRIBUTE) *auth_attr = PKCS7_get_signed_attributes(si); STACK_OF(X509_ATTRIBUTE) *auth_attr = PKCS7_get_signed_attributes(si);
if (auth_attr) { if (auth_attr) {
@@ -2947,7 +2985,7 @@ static ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si)
X509_ATTRIBUTE *attr = X509at_get_attr(auth_attr, i); X509_ATTRIBUTE *attr = X509at_get_attr(auth_attr, i);
if (OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attr)) == nid) { if (OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attr)) == nid) {
/* PKCS#9 signing time - Policy OID: 1.2.840.113549.1.9.5 */ /* PKCS#9 signing time - Policy OID: 1.2.840.113549.1.9.5 */
return X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL); return (const ASN1_UTCTIME *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL);
} }
} }
} }
@@ -2987,10 +3025,13 @@ static time_t time_t_get_cms_time(CMS_ContentInfo *cms)
ASN1_OCTET_STRING **pos = CMS_get0_content(cms); ASN1_OCTET_STRING **pos = CMS_get0_content(cms);
if (pos != NULL && *pos != NULL) { if (pos != NULL && *pos != NULL) {
const u_char *p = (*pos)->data; const u_char *p = ASN1_STRING_get0_data(*pos);
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, (*pos)->length); int len = ASN1_STRING_length(*pos);
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, len);
if (token) { if (token) {
const ASN1_GENERALIZEDTIME *asn1_time = TS_TST_INFO_get_time(token); const ASN1_GENERALIZEDTIME *asn1_time = TS_TST_INFO_get_time(token);
posix_time = time_t_get_asn1_time(asn1_time); posix_time = time_t_get_asn1_time(asn1_time);
TS_TST_INFO_free(token); TS_TST_INFO_free(token);
} }
@@ -3082,12 +3123,8 @@ static int verify_content_member_digest(FILE_FORMAT_CTX *ctx, ASN1_TYPE *content
fprintf(stderr, "Failed to extract SpcIndirectDataContent data\n"); fprintf(stderr, "Failed to extract SpcIndirectDataContent data\n");
return 1; /* FAILED */ return 1; /* FAILED */
} }
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) { if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
SpcIndirectDataContent_free(idc); fprintf(stderr, "Failed to extract message digest from signature\n\n");
return 1; /* FAILED */
}
if (mdtype == -1) {
fprintf(stderr, "Failed to extract current message digest\n\n");
SpcIndirectDataContent_free(idc); SpcIndirectDataContent_free(idc);
return 1; /* FAILED */ return 1; /* FAILED */
} }
@@ -3241,91 +3278,67 @@ static int verify_signature(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
} }
/* /*
* [in] ctx: structure holds input and output data * Verifies signatures in a file.
* [in] ctx: file format context (input/output data)
* [in] options: verification options
* [in] verify_signature_flag:
* - non-zero: verify both digest and cryptographic signature
* - zero: verify only digest/content consistency (no signature validation)
* [returns] 1 on error or 0 on success * [returns] 1 on error or 0 on success
*/ */
static int verify_signed_file(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options) static int verify_signed_file(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options,
int verify_signature_flag)
{ {
int i, ret = 1, verified = 0; int i, ret = 1, verified = 0;
PKCS7 *p7;
STACK_OF(PKCS7) *signatures = NULL;
int detached = options->catalog ? 1 : 0; int detached = options->catalog ? 1 : 0;
STACK_OF(PKCS7) *signatures = NULL;
if (detached) { signatures = get_signature_list(ctx, options);
GLOBAL_OPTIONS *cat_options; if (!signatures)
FILE_FORMAT_CTX *cat_ctx;
if (!ctx->format->is_detaching_supported || !ctx->format->is_detaching_supported()) {
fprintf(stderr, "This format does not support detached PKCS#7 signature\n");
return 1; /* FAILED */
}
printf("Checking the specified catalog file\n\n");
cat_options = OPENSSL_memdup(options, sizeof(GLOBAL_OPTIONS));
if (!cat_options) {
fprintf(stderr, "OPENSSL_memdup error.\n");
return 1; /* FAILED */
}
cat_options->infile = options->catalog;
cat_options->cmd = CMD_EXTRACT;
cat_ctx = file_format_cat.ctx_new(cat_options, NULL, NULL);
if (!cat_ctx) {
fprintf(stderr, "CAT file initialization error\n");
return 1; /* FAILED */
}
if (!cat_ctx->format->pkcs7_extract) {
fprintf(stderr, "Unsupported command: extract-signature\n");
return 1; /* FAILED */
}
p7 = cat_ctx->format->pkcs7_extract(cat_ctx);
cat_ctx->format->ctx_cleanup(cat_ctx);
OPENSSL_free(cat_options);
} else {
if (!ctx->format->pkcs7_extract) {
fprintf(stderr, "Unsupported command: extract-signature\n");
return 1; /* FAILED */
}
p7 = ctx->format->pkcs7_extract(ctx);
}
if (!p7) {
fprintf(stderr, "Unable to extract existing signature\n");
return 1; /* FAILED */ return 1; /* FAILED */
}
signatures = signature_list_create(p7); if (!detached && !ctx->format->verify_digests) {
if (!signatures) { fprintf(stderr, "Unsupported method: verify_digests\n");
fprintf(stderr, "Failed to create signature list\n\n");
sk_PKCS7_pop_free(signatures, PKCS7_free); sk_PKCS7_pop_free(signatures, PKCS7_free);
return 1; /* FAILED */ return 1; /* FAILED */
} }
for (i = 0; i < sk_PKCS7_num(signatures); i++) { for (i = 0; i < sk_PKCS7_num(signatures); i++) {
PKCS7 *sig; PKCS7 *sig;
int digest_ok = 0;
if (options->index >= 0 && options->index != i) { if (options->index >= 0 && options->index != i) {
printf("Warning: signature verification at index %d was skipped\n", i); printf("Warning: signature verification at index %d was skipped\n", i);
continue; continue;
} }
sig = sk_PKCS7_value(signatures, i); sig = sk_PKCS7_value(signatures, i);
printf("\nSignature Index: %d %s\n\n", i, i == 0 ? " (Primary Signature)" : "");
if (detached) { if (detached) {
if (!verify_content(ctx, sig)) { digest_ok = (verify_content(ctx, sig) == 0);
ret &= verify_signature(ctx, sig); if (!digest_ok)
} else {
printf("Catalog verification: failed\n\n"); printf("Catalog verification: failed\n\n");
} } else {
verified++; digest_ok = (ctx->format->verify_digests(ctx, sig) != 0);
} else if (ctx->format->verify_digests) { }
printf("\nSignature Index: %d %s\n\n", i, i==0 ? " (Primary Signature)" : ""); if (digest_ok) {
if (ctx->format->verify_digests(ctx, sig)) { if (!verify_signature_flag) {
/* success: at least one digest matches */
ret = 0;
} else {
/* success if at least one valid signature is found */
ret &= verify_signature(ctx, sig); ret &= verify_signature(ctx, sig);
} }
verified++;
} else {
fprintf(stderr, "Unsupported method: verify_digests\n");
return 1; /* FAILED */
} }
verified++;
} }
printf("Number of verified signatures: %d\n", verified); printf("Number of verified signatures: %d\n", verified);
sk_PKCS7_pop_free(signatures, PKCS7_free); sk_PKCS7_pop_free(signatures, PKCS7_free);
if (ret) if (ret)
ERR_print_errors_fp(stderr); ERR_print_errors_fp(stderr);
return ret; return ret;
} }
@@ -3367,11 +3380,11 @@ static STACK_OF(PKCS7) *signature_list_create(PKCS7 *p7)
int j; int j;
for (j=0; j<X509_ATTRIBUTE_count(attr); j++) { for (j=0; j<X509_ATTRIBUTE_count(attr); j++) {
ASN1_STRING *value; const ASN1_STRING *value;
const u_char *data; const u_char *data;
PKCS7 *nested; PKCS7 *nested;
value = X509_ATTRIBUTE_get0_data(attr, j, V_ASN1_SEQUENCE, NULL); value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, j, V_ASN1_SEQUENCE, NULL);
if (value == NULL) if (value == NULL)
continue; continue;
data = ASN1_STRING_get0_data(value); data = ASN1_STRING_get0_data(value);
@@ -3502,8 +3515,72 @@ static PKCS7 *pkcs7_get_sigfile(FILE_FORMAT_CTX *ctx)
} }
/* /*
* Extracts PKCS#7 object and creates a list of signatures
* [in] ctx: file format context
* [in] options: structure holds input parameters
* [returns] STACK_OF(PKCS7) on success or NULL on error
*/
static STACK_OF(PKCS7) *get_signature_list(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options)
{
PKCS7 *p7;
STACK_OF(PKCS7) *signatures;
if (options->catalog) {
GLOBAL_OPTIONS *cat_options;
FILE_FORMAT_CTX *cat_ctx;
if (!ctx->format->is_detaching_supported || !ctx->format->is_detaching_supported()) {
fprintf(stderr, "This format does not support detached PKCS#7 signature\n");
return NULL;
}
printf("Checking the specified catalog file\n\n");
cat_options = OPENSSL_memdup(options, sizeof(GLOBAL_OPTIONS));
if (!cat_options) {
fprintf(stderr, "OPENSSL_memdup error\n");
return NULL;
}
cat_options->infile = options->catalog;
cat_options->cmd = CMD_EXTRACT;
cat_ctx = file_format_cat.ctx_new(cat_options, NULL, NULL);
if (!cat_ctx) {
fprintf(stderr, "CAT file initialization error\n");
OPENSSL_free(cat_options);
return NULL;
}
if (!cat_ctx->format->pkcs7_extract) {
fprintf(stderr, "Unsupported command: extract-signature\n");
cat_ctx->format->ctx_cleanup(cat_ctx);
OPENSSL_free(cat_options);
return NULL;
}
p7 = cat_ctx->format->pkcs7_extract(cat_ctx);
cat_ctx->format->ctx_cleanup(cat_ctx);
OPENSSL_free(cat_options);
} else {
if (!ctx->format->pkcs7_extract) {
fprintf(stderr, "Unsupported command: extract-signature\n");
return NULL;
}
p7 = ctx->format->pkcs7_extract(ctx);
}
if (!p7) {
fprintf(stderr, "Unable to extract existing signature\n");
return NULL;
}
signatures = signature_list_create(p7);
if (!signatures) {
fprintf(stderr, "Failed to create signature list\n\n");
PKCS7_free(p7);
return NULL;
}
return signatures;
}
/*
* Verifies integrity of data after attaching a signature.
* [in] options: structure holds the input data * [in] options: structure holds the input data
* [returns] 1 on error or 0 on success * [returns] 0 on success, 1 on error or mismatch
*/ */
static int check_attached_data(GLOBAL_OPTIONS *options) static int check_attached_data(GLOBAL_OPTIONS *options)
{ {
@@ -3534,7 +3611,8 @@ static int check_attached_data(GLOBAL_OPTIONS *options)
OPENSSL_free(tmp_options); OPENSSL_free(tmp_options);
return 1; /* FAILED */ return 1; /* FAILED */
} }
if (verify_signed_file(ctx, tmp_options)) { /* Check that attached data matches PKCS#7 digest (no signature validation) */
if (verify_signed_file(ctx, tmp_options, 0)) {
fprintf(stderr, "Signature mismatch\n"); fprintf(stderr, "Signature mismatch\n");
ctx->format->ctx_cleanup(ctx); ctx->format->ctx_cleanup(ctx);
OPENSSL_free(tmp_options); OPENSSL_free(tmp_options);
@@ -3665,13 +3743,7 @@ static void usage(const char *argv0, const char *cmd)
} }
if (on_list(cmd, cmds_attach)) { if (on_list(cmd, cmds_attach)) {
printf("%1sattach-signature [ -sigin ] <file>\n", ""); printf("%1sattach-signature [ -sigin ] <file>\n", "");
printf("%12s[ -CAfile <file> ]\n", "");
printf("%12s[ -CRLfile <file> ]\n", "");
printf("%12s[ -TSA-CAfile <file> ]\n", "");
printf("%12s[ -TSA-CRLfile <file> ]\n", "");
printf("%12s[ -time <unix-time> ]\n", "");
printf("%12s[ -h {md5,sha1,sha2(56),sha384,sha512} ]\n", ""); printf("%12s[ -h {md5,sha1,sha2(56),sha384,sha512} ]\n", "");
printf("%12s[ -require-leaf-hash {md5,sha1,sha2(56),sha384,sha512}:XXXXXXXXXXXX... ]\n", "");
printf("%12s[ -nest ]\n", ""); printf("%12s[ -nest ]\n", "");
printf("%12s[ -add-msi-dse ]\n", ""); printf("%12s[ -add-msi-dse ]\n", "");
printf("%12s[ -in ] <file> [ -out ] <file>\n\n", ""); printf("%12s[ -in ] <file> [ -out ] <file>\n\n", "");
@@ -3722,13 +3794,13 @@ static void help_for(const char *argv0, const char *cmd)
#ifdef PROVIDE_ASKPASS #ifdef PROVIDE_ASKPASS
const char *cmds_askpass[] = {"sign", NULL}; const char *cmds_askpass[] = {"sign", NULL};
#endif /* PROVIDE_ASKPASS */ #endif /* PROVIDE_ASKPASS */
const char *cmds_CAfile[] = {"attach-signature", "verify", NULL}; const char *cmds_CAfile[] = {"verify", NULL};
const char *cmds_catalog[] = {"verify", NULL}; const char *cmds_catalog[] = {"verify", NULL};
const char *cmds_certs[] = {"sign", NULL}; const char *cmds_certs[] = {"sign", NULL};
const char *cmds_comm[] = {"sign", NULL}; const char *cmds_comm[] = {"sign", NULL};
const char *cmds_CRLfile[] = {"attach-signature", "verify", NULL}; const char *cmds_CRLfile[] = {"verify", NULL};
const char *cmds_CRLfileHTTPS[] = {"add", "sign", "verify", NULL}; const char *cmds_CRLfileHTTPS[] = {"add", "sign", "verify", NULL};
const char *cmds_CRLfileTSA[] = {"attach-signature", "verify", NULL}; const char *cmds_CRLfileTSA[] = {"verify", NULL};
const char *cmds_h[] = {"add", "attach-signature", "sign", "extract-data", NULL}; const char *cmds_h[] = {"add", "attach-signature", "sign", "extract-data", NULL};
const char *cmds_i[] = {"sign", NULL}; const char *cmds_i[] = {"sign", NULL};
const char *cmds_in[] = {"add", "attach-signature", "extract-signature", const char *cmds_in[] = {"add", "attach-signature", "extract-signature",
@@ -3762,16 +3834,16 @@ static void help_for(const char *argv0, const char *cmd)
#endif /* OPENSSL_NO_ENGINE */ #endif /* OPENSSL_NO_ENGINE */
const char *cmds_pkcs12[] = {"sign", NULL}; const char *cmds_pkcs12[] = {"sign", NULL};
const char *cmds_readpass[] = {"sign", NULL}; const char *cmds_readpass[] = {"sign", NULL};
const char *cmds_require_leaf_hash[] = {"attach-signature", "verify", NULL}; const char *cmds_require_leaf_hash[] = {"verify", NULL};
const char *cmds_sigin[] = {"attach-signature", NULL}; const char *cmds_sigin[] = {"attach-signature", NULL};
const char *cmds_time[] = {"attach-signature", "sign", "verify", NULL}; const char *cmds_time[] = {"sign", "verify", NULL};
const char *cmds_ignore_timestamp[] = {"verify", NULL}; const char *cmds_ignore_timestamp[] = {"verify", NULL};
const char *cmds_ignore_cdp[] = {"verify", NULL}; const char *cmds_ignore_cdp[] = {"verify", NULL};
const char *cmds_ignore_crl[] = {"verify", NULL}; const char *cmds_ignore_crl[] = {"verify", NULL};
const char *cmds_t[] = {"add", "sign", NULL}; const char *cmds_t[] = {"add", "sign", NULL};
const char *cmds_ts[] = {"add", "sign", NULL}; const char *cmds_ts[] = {"add", "sign", NULL};
const char *cmds_CAfileHTTPS[] = {"add", "sign", "verify", NULL}; const char *cmds_CAfileHTTPS[] = {"add", "sign", "verify", NULL};
const char *cmds_CAfileTSA[] = {"attach-signature", "verify", NULL}; const char *cmds_CAfileTSA[] = {"verify", NULL};
const char *cmds_certsTSA[] = {"add", "sign", NULL}; const char *cmds_certsTSA[] = {"add", "sign", NULL};
const char *cmds_keyTSA[] = {"add", "sign", NULL}; const char *cmds_keyTSA[] = {"add", "sign", NULL};
const char *cmds_timeTSA[] = {"add", "sign", NULL}; const char *cmds_timeTSA[] = {"add", "sign", NULL};
@@ -3791,6 +3863,7 @@ static void help_for(const char *argv0, const char *cmd)
printf("%-22s = add an unauthenticated blob or a timestamp to a previously-signed file\n", "add"); printf("%-22s = add an unauthenticated blob or a timestamp to a previously-signed file\n", "add");
printf("%-22s = sign file using a given signature\n", "attach-signature"); printf("%-22s = sign file using a given signature\n", "attach-signature");
printf("%-22s = extract signature from a previously-signed file\n", "extract-signature"); printf("%-22s = extract signature from a previously-signed file\n", "extract-signature");
printf("%-22s = extract a data content to be signed\n", "extract-data");
printf("%-22s = remove sections of the embedded signature on a file\n", "remove-signature"); printf("%-22s = remove sections of the embedded signature on a file\n", "remove-signature");
printf("%-22s = digitally sign a file\n", "sign"); printf("%-22s = digitally sign a file\n", "sign");
printf("%-22s = verifies the digital signature of a file\n\n", "verify"); printf("%-22s = verifies the digital signature of a file\n\n", "verify");
@@ -3802,8 +3875,6 @@ static void help_for(const char *argv0, const char *cmd)
} }
if (on_list(cmd, cmds_attach)) { if (on_list(cmd, cmds_attach)) {
printf("\nUse the \"attach-signature\" command to attach the signature stored in the \"sigin\" file.\n"); printf("\nUse the \"attach-signature\" command to attach the signature stored in the \"sigin\" file.\n");
printf("In order to verify this signature you should specify how to find needed CA or TSA\n");
printf("certificates, if appropriate.\n\n");
printf("Options:\n"); printf("Options:\n");
} }
if (on_list(cmd, cmds_extract)) { if (on_list(cmd, cmds_extract)) {
@@ -4433,19 +4504,15 @@ static int ui_read(UI *ui, UI_STRING *uis)
} }
static UI_METHOD *ui_osslsigncode(void) { static UI_METHOD *ui_osslsigncode(void) {
static UI_METHOD *ui_method=NULL; UI_METHOD *ui = UI_create_method("osslsigncode UI");
if (ui_method) /* already initialized */ if (ui) {
return ui_method; UI_method_set_opener(ui, UI_method_get_opener(UI_OpenSSL()));
ui_method = UI_create_method("osslsigncode UI"); UI_method_set_writer(ui, UI_method_get_writer(UI_OpenSSL()));
if (!ui_method) { UI_method_set_reader(ui, ui_read);
return NULL; UI_method_set_closer(ui, UI_method_get_closer(UI_OpenSSL()));
} }
UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL())); return ui;
UI_method_set_writer(ui_method, UI_method_get_writer(UI_OpenSSL()));
UI_method_set_reader(ui_method, ui_read);
UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL()));
return ui_method;
} }
/* store_type == 0 means here multiple types of credentials are to be loaded */ /* store_type == 0 means here multiple types of credentials are to be loaded */
@@ -4456,7 +4523,7 @@ static void load_objects_from_store(const char *url, char *pass, EVP_PKEY **pkey
if (!url) if (!url)
return; return;
store_ctx = OSSL_STORE_open(url, ui_osslsigncode(), pass, NULL, NULL); store_ctx = OSSL_STORE_open(url, ui_method, pass, NULL, NULL);
if (!store_ctx) if (!store_ctx)
return; return;
@@ -4557,7 +4624,6 @@ static void providers_cleanup(void)
{ {
sk_OSSL_PROVIDER_pop_free(providers, provider_free); sk_OSSL_PROVIDER_pop_free(providers, provider_free);
providers = NULL; providers = NULL;
UI_destroy_method(ui_osslsigncode());
} }
static int provider_load(const char *pname) static int provider_load(const char *pname)
@@ -4662,7 +4728,7 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
if (cmd == CMD_HELP) { if (cmd == CMD_HELP) {
return 0; /* FAILED */ return 0; /* FAILED */
} }
if (cmd == CMD_SIGN || cmd == CMD_VERIFY || cmd == CMD_ATTACH) { if (cmd == CMD_SIGN || cmd == CMD_VERIFY) {
options->cafile = get_cafile(); options->cafile = get_cafile();
options->https_cafile = get_cafile(); options->https_cafile = get_cafile();
options->tsa_cafile = get_cafile(); options->tsa_cafile = get_cafile();
@@ -4827,7 +4893,7 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
return 0; /* FAILED */ return 0; /* FAILED */
} }
options->url = *(++argv); options->url = *(++argv);
} else if ((cmd == CMD_ATTACH || cmd == CMD_SIGN || cmd == CMD_VERIFY) } else if ((cmd == CMD_SIGN || cmd == CMD_VERIFY)
&& (!strcmp(*argv, "-time") || !strcmp(*argv, "-st"))) { && (!strcmp(*argv, "-time") || !strcmp(*argv, "-st"))) {
if (--argc < 1) { if (--argc < 1) {
usage(argv0, "all"); usage(argv0, "all");
@@ -4896,14 +4962,14 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
return 0; /* FAILED */ return 0; /* FAILED */
} }
options->catalog = *(++argv); options->catalog = *(++argv);
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && !strcmp(*argv, "-CAfile")) { } else if (cmd == CMD_VERIFY && !strcmp(*argv, "-CAfile")) {
if (--argc < 1) { if (--argc < 1) {
usage(argv0, "all"); usage(argv0, "all");
return 0; /* FAILED */ return 0; /* FAILED */
} }
OPENSSL_free(options->cafile); OPENSSL_free(options->cafile);
options->cafile = OPENSSL_strdup(*++argv); options->cafile = OPENSSL_strdup(*++argv);
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && !strcmp(*argv, "-CRLfile")) { } else if (cmd == CMD_VERIFY && !strcmp(*argv, "-CRLfile")) {
if (--argc < 1) { if (--argc < 1) {
usage(argv0, "all"); usage(argv0, "all");
return 0; /* FAILED */ return 0; /* FAILED */
@@ -4924,20 +4990,20 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
return 0; /* FAILED */ return 0; /* FAILED */
} }
options->https_crlfile = OPENSSL_strdup(*++argv); options->https_crlfile = OPENSSL_strdup(*++argv);
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && (!strcmp(*argv, "-untrusted") || !strcmp(*argv, "-TSA-CAfile"))) { } else if (cmd == CMD_VERIFY && (!strcmp(*argv, "-untrusted") || !strcmp(*argv, "-TSA-CAfile"))) {
if (--argc < 1) { if (--argc < 1) {
usage(argv0, "all"); usage(argv0, "all");
return 0; /* FAILED */ return 0; /* FAILED */
} }
OPENSSL_free(options->tsa_cafile); OPENSSL_free(options->tsa_cafile);
options->tsa_cafile = OPENSSL_strdup(*++argv); options->tsa_cafile = OPENSSL_strdup(*++argv);
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && (!strcmp(*argv, "-CRLuntrusted") || !strcmp(*argv, "-TSA-CRLfile"))) { } else if (cmd == CMD_VERIFY && (!strcmp(*argv, "-CRLuntrusted") || !strcmp(*argv, "-TSA-CRLfile"))) {
if (--argc < 1) { if (--argc < 1) {
usage(argv0, "all"); usage(argv0, "all");
return 0; /* FAILED */ return 0; /* FAILED */
} }
options->tsa_crlfile = OPENSSL_strdup(*++argv); options->tsa_crlfile = OPENSSL_strdup(*++argv);
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && !strcmp(*argv, "-require-leaf-hash")) { } else if (cmd == CMD_VERIFY && !strcmp(*argv, "-require-leaf-hash")) {
if (--argc < 1) { if (--argc < 1) {
usage(argv0, "all"); usage(argv0, "all");
return 0; /* FAILED */ return 0; /* FAILED */
@@ -5054,7 +5120,7 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
return 0; /* FAILED */ return 0; /* FAILED */
} }
#ifndef WIN32 #ifndef WIN32
if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && access(options->cafile, R_OK)) { if (cmd == CMD_VERIFY && access(options->cafile, R_OK)) {
printf("Use the \"-CAfile\" option to add one or more trusted CA certificates to verify the signature.\n"); printf("Use the \"-CAfile\" option to add one or more trusted CA certificates to verify the signature.\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
@@ -5082,7 +5148,7 @@ static void engine_control_set(GLOBAL_OPTIONS *options, const char *arg)
} }
#endif /* OPENSSL_NO_ENGINE */ #endif /* OPENSSL_NO_ENGINE */
int main(int argc, char **argv) static int main_execute(int argc, char **argv)
{ {
FILE_FORMAT_CTX *ctx = NULL; FILE_FORMAT_CTX *ctx = NULL;
GLOBAL_OPTIONS options; GLOBAL_OPTIONS options;
@@ -5094,24 +5160,6 @@ int main(int argc, char **argv)
/* reset options */ /* reset options */
memset(&options, 0, sizeof(GLOBAL_OPTIONS)); memset(&options, 0, sizeof(GLOBAL_OPTIONS));
/* Set up OpenSSL */
if (!OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS
| OPENSSL_INIT_ADD_ALL_CIPHERS
| OPENSSL_INIT_ADD_ALL_DIGESTS
| OPENSSL_INIT_LOAD_CONFIG, NULL))
DO_EXIT_0("Failed to init crypto\n");
/* create some MS Authenticode OIDS we need later on */
if (!OBJ_create(SPC_STATEMENT_TYPE_OBJID, NULL, NULL)
/* PKCS9_COUNTER_SIGNATURE exists as OpenSSL OBJ_pkcs9_countersignature */
|| !OBJ_create(MS_JAVA_SOMETHING, NULL, NULL)
|| !OBJ_create(SPC_SP_OPUS_INFO_OBJID, NULL, NULL)
|| !OBJ_create(SPC_NESTED_SIGNATURE_OBJID, NULL, NULL)
|| !OBJ_create(SPC_UNAUTHENTICATED_DATA_BLOB_OBJID, NULL, NULL)
|| !OBJ_create(SPC_RFC3161_OBJID, NULL, NULL)
|| !OBJ_create(PKCS9_SEQUENCE_NUMBER, NULL, NULL))
DO_EXIT_0("Failed to create objects\n");
/* commands and options initialization */ /* commands and options initialization */
if (!main_configure(argc, argv, &options)) if (!main_configure(argc, argv, &options))
goto err_cleanup; goto err_cleanup;
@@ -5166,7 +5214,7 @@ int main(int argc, char **argv)
DO_EXIT_0("Initialization error or unsupported input file type.\n"); DO_EXIT_0("Initialization error or unsupported input file type.\n");
} }
if (options.cmd == CMD_VERIFY) { if (options.cmd == CMD_VERIFY) {
ret = verify_signed_file(ctx, &options); ret = verify_signed_file(ctx, &options, 1);
goto skip_signing; goto skip_signing;
} else if (options.cmd == CMD_EXTRACT_DATA) { } else if (options.cmd == CMD_EXTRACT_DATA) {
if (!ctx->format->pkcs7_contents_get) { if (!ctx->format->pkcs7_contents_get) {
@@ -5313,13 +5361,15 @@ skip_signing:
} }
if (!ret && options.cmd == CMD_ATTACH) { if (!ret && options.cmd == CMD_ATTACH) {
ret = check_attached_data(&options); ret = check_attached_data(&options);
if (!ret) if (!ret) {
printf("Signature successfully attached\n"); printf("Signature successfully attached\n");
/* else } else {
* the new PKCS#7 signature has been successfully appended to the outfile /*
* but only its verification failed (incorrect verification parameters?) * The output file was created, but its content does not match
* so the output file is not deleted * the attached PKCS#7 digest, so it is treated as invalid and removed.
*/ */
remove_file(options.outfile);
}
} }
err_cleanup: err_cleanup:
@@ -5359,6 +5409,37 @@ err_cleanup:
return ret; return ret;
} }
int main(int argc, char **argv)
{
int ret = -1;
/* one-time OpenSSL initialization */
if (!OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS
| OPENSSL_INIT_ADD_ALL_CIPHERS
| OPENSSL_INIT_ADD_ALL_DIGESTS
| OPENSSL_INIT_LOAD_CONFIG, NULL))
DO_EXIT_0("Failed to init crypto\n");
/* create some MS Authenticode OIDS we need later on */
if (!OBJ_create(SPC_STATEMENT_TYPE_OBJID, NULL, NULL)
/* PKCS9_COUNTER_SIGNATURE exists as OpenSSL OBJ_pkcs9_countersignature */
|| !OBJ_create(MS_JAVA_SOMETHING, NULL, NULL)
|| !OBJ_create(SPC_SP_OPUS_INFO_OBJID, NULL, NULL)
|| !OBJ_create(SPC_NESTED_SIGNATURE_OBJID, NULL, NULL)
|| !OBJ_create(SPC_UNAUTHENTICATED_DATA_BLOB_OBJID, NULL, NULL)
|| !OBJ_create(SPC_RFC3161_OBJID, NULL, NULL)
|| !OBJ_create(PKCS9_SEQUENCE_NUMBER, NULL, NULL))
DO_EXIT_0("Failed to create objects\n");
/* perform the requested operation */
ui_method = ui_osslsigncode();
ret = main_execute(argc, argv);
UI_destroy_method(ui_method);
err_cleanup:
return ret;
}
/* /*
Local Variables: Local Variables:
c-basic-offset: 4 c-basic-offset: 4
+554
View File
@@ -0,0 +1,554 @@
---
title: osslsigncode
lang: en-US
---
# NAME
osslsigncode - Authenticode signing, timestamping, extraction, attachment, removal, and verification tool
# SYNOPSIS
`osslsigncode` [`--help`] [`--version`]
`osslsigncode` `sign`
[`-certs` *file* | `-spc` *file* | `-pkcs12` *file*]
[`-key` *file-or-URI*]
[`-ac` *file*]
[`-pass` *password* | `-readpass` *file* | `-askpass`]
[`-pkcs11module` *module*] [`-pkcs11cert` *URI*]
[`-engine` *engine*] [`-provider` *provider*]
[`-login`] [`-engineCtrl` *command*[:*parameter*]]
[`-h` *digest*]
[`-n` *description*] [`-i` *URL*]
[`-jp` `low`] [`-comm`] [`-ph`]
[`-t` *URL* ... | `-ts` *URL* ...]
[`-TSA-certs` *file* `-TSA-key` *file-or-URI* [`-TSA-time` *unix-time*]]
[`-HTTPS-CAfile` *file*] [`-HTTPS-CRLfile` *file*]
[`-time` *unix-time*]
[`-addUnauthenticatedBlob` [`-blobFile` *file*]]
[`-nest`] [`-add-msi-dse`] [`-verbose`] [`-pem`]
`-in` *input* `-out` *output*
`osslsigncode` `extract-data`
[`-pem`] [`-h` *digest*] [`-ph`] [`-add-msi-dse`]
`-in` *input* `-out` *output*
`osslsigncode` `add`
[`-addUnauthenticatedBlob` [`-blobFile` *file*]]
[`-t` *URL* ... | `-ts` *URL* ...]
[`-TSA-certs` *file* `-TSA-key` *file-or-URI* [`-TSA-time` *unix-time*]]
[`-HTTPS-CAfile` *file*] [`-HTTPS-CRLfile` *file*]
[`-h` *digest*] [`-index` *n*] [`-verbose`] [`-add-msi-dse`]
`-in` *input* `-out` *output*
`osslsigncode` `attach-signature`
`-sigin` *signature*
[`-h` *digest*] [`-nest`] [`-add-msi-dse`]
`-in` *input* `-out` *output*
`osslsigncode` `extract-signature`
[`-pem`]
`-in` *input* `-out` *output*
`osslsigncode` `remove-signature`
`-in` *input* `-out` *output*
`osslsigncode` `verify`
`-in` *input*
[`-c` | `-catalog` *catalog-file*]
[`-CAfile` *file*] [`-CRLfile` *file*]
[`-HTTPS-CAfile` *file*] [`-HTTPS-CRLfile` *file*]
[`-TSA-CAfile` *file*] [`-TSA-CRLfile` *file*]
[`-p` *proxy*] [`-index` *n*]
[`-ignore-timestamp`] [`-ignore-cdp`] [`-ignore-crl`]
[`-time` *unix-time*]
[`-require-leaf-hash` *alg*:*hex*]
[`-verbose`]
# DESCRIPTION
`osslsigncode` signs and verifies Microsoft Authenticode signatures on
supported file formats. It can also extract data for detached signing,
attach an externally produced signature, add timestamps or unauthenticated
blobs to an existing signature, and remove an embedded signature.
Supported input formats include PE files such as EXE, DLL, and SYS, CAB,
CAT, MSI, APPX, and several script file types, including `.ps1`, `.ps1xml`,
`.psc1`, `.psd1`, `.psm1`, `.cdxml`, `.mof`, and `.js`.
The program supports these common workflows:
- direct signing of an unsigned file
- detached signing via `extract-data`, `sign`, and `attach-signature`
- post-sign timestamping with `add`
- verification of embedded signatures or catalog signatures with `verify`
If no subcommand is given, `sign` is assumed.
# FORMATS
Support is not identical across all file formats.
In particular, detached-signature workflows, nested signatures, catalog-based
verification, and signature removal are format-dependent features. A command
that is valid for one supported file type may be unsupported for another.
CAT files are a special case. They are detached catalog containers for
hashes of other files, not ordinary embedded-signature payloads. A CAT
file is itself a PKCS#7 structure containing authenticated entries for one
or more external files. In practice, the catalog signs file digests
recorded in the catalog, rather than embedding a signature into each
covered file.
Because of this, CAT files behave differently from embedded-signature
formats. They do not support `attach-signature`, `remove-signature`,
`extract-data`, or nested signatures.
MSI files are also a special case. They support an extended signature mode
controlled by `-add-msi-dse`. In this mode, the MSI signature covers file
metadata as well as file content. Detached-signing workflows and any later
re-signing or nesting operations must use a mode consistent with the MSI
file's existing signature structure.
# COMMANDS
## `sign`
Create a new Authenticode signature.
This command can sign a normal unsigned file, or it can sign PKCS#7 data
previously produced by `extract-data`.
## `extract-data`
Extract the PKCS#7 content to be signed later. This is used for detached
signing workflows.
## `add`
Add unauthenticated attributes to an existing signature, typically an
Authenticode timestamp, an RFC 3161 timestamp, or an unauthenticated blob.
With `-index`, the selected signature in a multi-signature file is updated.
## `attach-signature`
Attach a detached PKCS#7 signature to an input file.
With `-nest`, the new signature is attached as a nested signature instead of
replacing the primary one, if the file format supports nested signatures.
## `extract-signature`
Extract the embedded PKCS#7 signature from a signed file.
## `remove-signature`
Remove the embedded signature from a signed file.
## `verify`
Verify an embedded signature or a catalog signature.
Verification may include digest consistency, certificate chain validation,
certificate revocation checking, timestamp validation, and optional checking
of the signer's leaf certificate hash.
When verifying that a file is covered by a catalog, use `verify -catalog
catalog.cat -in file`. Verifying the CAT file by itself validates the
catalog signature; verifying with `-catalog` checks whether the specified
input file is covered by that catalog.
# OPTIONS
Some options are available only in particular builds or OpenSSL versions.
In particular, `-askpass` is build-dependent, `-provider` and `-nolegacy`
require OpenSSL 3, and engine-related options depend on engine support in the
build.
## General options
`--help`
: Show help text. With a subcommand, show help for that subcommand.
`-v`, `--version`
: Show version information.
`-in` *file*
: Input file.
`-out` *file*
: Output file. Required for all commands except `verify`.
`-verbose`
: Produce more detailed diagnostic output.
## Signing material
`-pkcs12` *file*
: Read the signing certificate and private key from a PKCS#12 container.
`-certs`, `-spc` *file*
: Read the signing certificate chain. The historical alias `-spc` is accepted.
`-key` *file-or-URI*
: Read the private key. This may also be a store or PKCS#11 URI.
`-ac` *file*
: Add extra certificates to the signature block.
`-pass` *password*
: Password or PIN for the key, token, or PKCS#12 container.
`-readpass` *file*
: Read the password or PIN from *file*. Use `-` to read from standard input.
`-askpass`
: Prompt for the password interactively.
## PKCS#11, engines, and providers
`-pkcs11module` *module*
: Path to a PKCS#11 module.
`-pkcs11cert` *URI*
: PKCS#11 URI identifying the certificate object.
`-provider` *provider*
: OpenSSL 3 provider to load. This is the preferred modern interface for
provider-based PKCS#11 use.
`-engine`, `-pkcs11engine` *engine*
: OpenSSL engine identifier or path to a dynamic engine module. This
interface is retained for compatibility with builds and deployments that
still support engines.
`-login`
: Force login to the token for engine-based PKCS#11 use.
`-engineCtrl` *command*[:*parameter*]
: Pass a control command to the selected engine.
`-nolegacy`
: On OpenSSL 3 builds, do not automatically load the legacy provider.
## Signature contents and digest control
`-h` `md5` | `sha1` | `sha2` | `sha256` | `sha384` | `sha512`
: Select the digest algorithm. The default is `sha256`. `sha2` and
`sha256` are equivalent.
`-n` *description*
: Description of the signed content.
`-i` *URL*
: Informational URL associated with the signed content.
`-comm`
: Use Microsoft Commercial Code Signing purpose instead of the default
individual purpose.
`-jp` `low`
: Add the Java CAB permission attribute. Only `low` is currently supported.
`-ph`
: Generate page hashes for executable files.
`-add-msi-dse`
: For MSI files, enable the `MsiDigitalSignatureEx` signing mode. In this
mode, the signature covers MSI metadata as well as file content. The
metadata portion includes stream names, sizes, and selected timestamps in
the MSI structure. This option changes the MSI signature format and should
be used consistently in any detached-signing workflow involving
`extract-data`, `sign`, `attach-signature`, or `add`.
For a newly signed MSI, this mode is generally preferred because it extends
signing coverage beyond file content alone. For an already signed MSI,
however, the chosen mode must match the file's existing signature
structure. Switching between basic MSI signing and `MsiDigitalSignatureEx`
during re-signing or nested-signature operations can invalidate the
existing signature.
`-pem`
: Write PKCS#7 output in PEM format instead of DER.
## Timestamping and network options
The following timestamping modes are **mutually exclusive** within a single
`sign` or `add` invocation:
- Authenticode timestamping with `-t`
- RFC 3161 timestamping with `-ts`
- built-in RFC 3161 timestamp generation with `-TSA-certs` and `-TSA-key`
`-t` *URL*
: Add an Authenticode timestamp from the specified URL. May be repeated.
`-ts` *URL*
: Add an RFC 3161 timestamp from the specified URL. May be repeated.
`-p` *proxy*
: Proxy used for timestamp or CRL retrieval.
`-noverifypeer`
: Do not verify the TLS certificate of the remote timestamp service.
`-HTTPS-CAfile` *file*
: PEM bundle used to verify HTTPS peers contacted by `osslsigncode`.
`-HTTPS-CRLfile` *file*
: PEM CRL file used while verifying HTTPS peers.
`-TSA-certs` *file*
: PEM certificate chain for locally generated RFC 3161 timestamps.
`-TSA-key` *file-or-URI*
: Private key for locally generated RFC 3161 timestamps.
`-TSA-time` *unix-time*
: Timestamp time for locally generated RFC 3161 responses.
## Nested signatures and indexed operations
`-nest`
: Add a nested signature instead of replacing the primary signature.
`-index` *n*
: Select a signature by index for `add` or `verify`. Index 0 is the primary
signature.
## Unauthenticated blob options
`-addUnauthenticatedBlob`
: Add an unauthenticated blob to the signature.
`-blobFile` *file*
: Read blob contents from *file*. If omitted, a placeholder blob is created.
## Verification options
`-c`, `-catalog` *file*
: Verify the input file against the specified catalog file.
`-CAfile` *file*
: PEM bundle of trusted CA certificates for signer validation.
`-CRLfile` *file*
: PEM file containing CRLs for signer validation.
`-TSA-CAfile`, `-untrusted` *file*
: PEM bundle of trusted CA certificates for timestamp validation.
`-TSA-CRLfile`, `-CRLuntrusted` *file*
: PEM file containing CRLs for timestamp validation.
`-time`, `-st` *unix-time*
: Verification time. If a valid timestamp is present and used, chain
validation is normally performed at the timestamp time.
`-ignore-timestamp`
: Skip verification of the timestamp signature.
`-ignore-cdp`
: Do not fetch CRLs from CRL Distribution Points.
`-ignore-crl`
: Disable CRL retrieval and CRL validation.
`-require-leaf-hash` *alg*:*hex*
: Require the signer's leaf certificate to hash to the specified value.
The hash is computed over the DER encoding of the leaf certificate.
# EXIT STATUS
`0`
: Success.
non-zero
: Failure.
# DIAGNOSTICS
Common causes of failure include:
missing CA trust bundle
: On Unix-like systems, `verify` expects a readable CA bundle, either from
`-CAfile` or from a detected system default.
detached-signing mismatch
: `extract-data`, `sign`, and `attach-signature` must use compatible
digest-affecting options such as `-h`, and where relevant `-ph` and
`-add-msi-dse`.
unsupported format feature
: Some file formats do not support every subcommand or every signature mode.
missing TSA trust chain
: Timestamp verification may fail unless the appropriate TSA trust anchors
are supplied with `-TSA-CAfile`, and where needed `-TSA-CRLfile`.
conflicting timestamp modes
: `-t`, `-ts`, and built-in TSA signing cannot be combined in one command.
MSI signature mode mismatch
: Re-signing or nesting an MSI signature must be consistent with whether the
file already uses `MsiDigitalSignatureEx`. Mixing modes may invalidate the
existing signature.
# ENVIRONMENT
`HTTP_PROXY`, `http_proxy`
: Default proxy for HTTP access if `-p` is not given.
`HTTPS_PROXY`, `https_proxy`
: Default proxy for HTTPS access if `-p` is not given.
`OPENSSL_ENGINES`
: May help OpenSSL find engine modules.
# FILES
On Unix-like systems, `osslsigncode` tries common CA bundle locations for
its default `-CAfile`, including:
- `/etc/ssl/certs/ca-certificates.crt`
- `/etc/pki/tls/certs/ca-bundle.crt`
- `/usr/share/ssl/certs/ca-bundle.crt`
- `/usr/local/share/certs/ca-root-nss.crt`
- `/etc/ssl/cert.pem`
If no readable CA bundle is available, `verify` may require an explicit
`-CAfile`.
# NOTES
Use `extract-data` when you need to create a new detached signature object.
Use `extract-signature` when you need to copy an existing embedded PKCS#7
signature out of a file.
For safer secret handling, prefer `-readpass` or `-askpass` over `-pass`.
Data added with `-addUnauthenticatedBlob` is not protected by the signature
and must not be treated as trusted.
For new MSI signatures, `-add-msi-dse` is generally preferred because it
extends signing coverage to MSI metadata as well as file content. However,
it is format-affecting rather than cosmetic, so existing signed MSI files
should be re-signed only in a mode consistent with their current signature
structure.
Output files are not overwritten.
# EXAMPLES
## Sign and verify a file
```sh
osslsigncode sign \
-pkcs12 signer.p12 \
-readpass p12-pass.txt \
-n "Example Application" \
-i "https://example.com/" \
-ts "https://tsa.example.net/" \
-in app.exe \
-out app-signed.exe
osslsigncode verify \
-CAfile ca-bundle.pem \
-TSA-CAfile tsa-ca-bundle.pem \
-in app-signed.exe
```
## Detached signing workflow
```sh
osslsigncode extract-data \
-h sha384 \
-ph \
-in app.exe \
-out app-data.der
osslsigncode sign \
-pkcs12 signer.p12 \
-readpass p12-pass.txt \
-h sha384 \
-in app-data.der \
-out app-sig.der
osslsigncode attach-signature \
-h sha384 \
-sigin app-sig.der \
-in app.exe \
-out app-signed.exe
osslsigncode verify \
-CAfile ca-bundle.pem \
-in app-signed.exe
```
## Sign a new MSI with extended MSI metadata coverage
```sh
osslsigncode sign \
-pkcs12 signer.p12 \
-readpass p12-pass.txt \
-add-msi-dse \
-in installer.msi \
-out installer-signed.msi
```
## Use a PKCS#11 provider
```sh
osslsigncode sign \
-provider /path/to/pkcs11prov.so \
-pkcs11module /path/to/opensc-pkcs11.so \
-pkcs11cert 'pkcs11:token=my-token;object=cert' \
-key 'pkcs11:token=my-token;object=key' \
-readpass pin.txt \
-in app.exe \
-out app-signed.exe
```
## Add a timestamp to an already signed file
```sh
osslsigncode add \
-ts "https://tsa.example.net/" \
-in app-signed.exe \
-out app-signed-ts.exe
```
## Verify that a file is covered by a catalog
```sh
osslsigncode verify \
-catalog drivers.cat \
-CAfile ca-bundle.pem \
-CRLfile ca-crl.pem \
-in driver.sys
```
# REPORTING BUGS
Report bugs and suspected issues via the project issue tracker:
<https://github.com/mtrojnar/osslsigncode/issues>
# AUTHORS
Originally written by Per Allansson.
Maintained and extended by Michał Trojnara.
Major contributions by Małgorzata Olszówka.
Additional contributions by other project contributors.
# SEE ALSO
**OpenSSL** Library
<https://openssl-library.org/>
+299 -82
View File
@@ -87,6 +87,7 @@ static uint32_t pe_calc_checksum(BIO *bio, uint32_t header_size);
static uint32_t pe_calc_realchecksum(FILE_FORMAT_CTX *ctx); static uint32_t pe_calc_realchecksum(FILE_FORMAT_CTX *ctx);
static int pe_modify_header(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata); static int pe_modify_header(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata);
static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md); static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md);
static int pkcs7_get_page_hash(PKCS7 *p7, u_char **ph, int *phlen, int *phtype);
static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj); static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj);
static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype); static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype);
static int pe_verify_page_hash(FILE_FORMAT_CTX *ctx, u_char *ph, int phlen, int phtype); static int pe_verify_page_hash(FILE_FORMAT_CTX *ctx, u_char *ph, int phlen, int phtype);
@@ -163,8 +164,10 @@ static ASN1_OBJECT *pe_spc_image_data_get(u_char **p, int *plen, FILE_FORMAT_CTX
if (EVP_MD_size(ctx->options->md) > EVP_MD_size(EVP_sha1())) if (EVP_MD_size(ctx->options->md) > EVP_MD_size(EVP_sha1()))
phtype = NID_sha256; phtype = NID_sha256;
link = pe_page_hash_link_get(ctx, phtype); link = pe_page_hash_link_get(ctx, phtype);
if (!link) if (!link) {
SpcPeImageData_free(pid);
return NULL; /* FAILED */ return NULL; /* FAILED */
}
pid->file = link; pid->file = link;
} else { } else {
pid->file = spc_link_obsolete_get(); pid->file = spc_link_obsolete_get();
@@ -245,49 +248,34 @@ static int pe_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
u_char *cmdbuf = NULL; u_char *cmdbuf = NULL;
u_char *ph = NULL; u_char *ph = NULL;
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) { if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
const u_char *p = content_val->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
if (idc) {
if (!pe_page_hash_get(&ph, &phlen, &phtype, idc->data)) {
fprintf(stderr, "Failed to extract a page hash\n\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) {
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
}
}
if (mdtype == -1) {
fprintf(stderr, "Failed to extract current message digest\n\n"); fprintf(stderr, "Failed to extract current message digest\n\n");
OPENSSL_free(ph);
return 0; /* FAILED */ return 0; /* FAILED */
} }
md = EVP_get_digestbynid(mdtype); md = EVP_get_digestbynid(mdtype);
cmdbuf = pe_digest_calc(ctx, md); cmdbuf = pe_digest_calc(ctx, md);
if (!cmdbuf) { if (!cmdbuf) {
fprintf(stderr, "Failed to calculate message digest\n\n"); fprintf(stderr, "Failed to calculate message digest\n\n");
OPENSSL_free(ph);
return 0; /* FAILED */ return 0; /* FAILED */
} }
if (!compare_digests(mdbuf, cmdbuf, mdtype)) { if (!compare_digests(mdbuf, cmdbuf, mdtype)) {
fprintf(stderr, "Signature verification: failed\n\n"); fprintf(stderr, "Signature verification: failed\n\n");
OPENSSL_free(ph);
OPENSSL_free(cmdbuf); OPENSSL_free(cmdbuf);
return 0; /* FAILED */ return 0; /* FAILED */
} }
OPENSSL_free(cmdbuf);
if (!pkcs7_get_page_hash(p7, &ph, &phlen, &phtype)) {
fprintf(stderr, "Failed to extract page hash\n\n");
return 0; /* FAILED */
}
if (!pe_verify_page_hash(ctx, ph, phlen, phtype)) { if (!pe_verify_page_hash(ctx, ph, phlen, phtype)) {
fprintf(stderr, "Signature verification: failed\n\n"); fprintf(stderr, "Signature verification: failed\n\n");
OPENSSL_free(ph); OPENSSL_free(ph);
OPENSSL_free(cmdbuf);
return 0; /* FAILED */ return 0; /* FAILED */
} }
OPENSSL_free(ph); OPENSSL_free(ph);
OPENSSL_free(cmdbuf);
return 1; /* OK */ return 1; /* OK */
} }
@@ -402,6 +390,7 @@ static PKCS7 *pe_pkcs7_signature_new(FILE_FORMAT_CTX *ctx, BIO *hash)
content = spc_indirect_data_content_get(hash, ctx); content = spc_indirect_data_content_get(hash, ctx);
if (!content) { if (!content) {
fprintf(stderr, "Failed to get spcIndirectDataContent\n"); fprintf(stderr, "Failed to get spcIndirectDataContent\n");
PKCS7_free(p7);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!sign_spc_indirect_data_content(p7, content)) { if (!sign_spc_indirect_data_content(p7, content)) {
@@ -834,6 +823,36 @@ static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md)
* Page hash support * Page hash support
*/ */
/*
* Retrieve a page hash from PKCS7 SPC_INDIRECT_DATA structure.
* [in] p7: PKCS7 signature
* [out] ph: page hash
* [out] phlen: page hash length
* [out] phtype: NID_sha1 or NID_sha256
* [returns] 0 on error or 1 on success
*/
static int pkcs7_get_page_hash(PKCS7 *p7, u_char **ph, int *phlen, int *phtype)
{
SpcIndirectDataContent *idc = pkcs7_get_indirect_data_content(p7);
if (!idc) {
fprintf(stderr, "Failed to decode SpcIndirectDataContent\n\n");
return 0; /* FAILED */
}
if (!idc->data) {
fprintf(stderr, "Missing SpcIndirectDataContent data\n\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
if (!pe_page_hash_get(ph, phlen, phtype, idc->data)) {
fprintf(stderr, "Failed to extract a page hash\n\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
return 1; /* OK */
}
/* /*
* Retrieve a page hash from SPC_INDIRECT_DATA structure. * Retrieve a page hash from SPC_INDIRECT_DATA structure.
* [out] ph: page hash * [out] ph: page hash
@@ -842,43 +861,71 @@ static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md)
* [in] obj: SPC_INDIRECT_DATA OID: 1.3.6.1.4.1.311.2.1.4 containing page hash * [in] obj: SPC_INDIRECT_DATA OID: 1.3.6.1.4.1.311.2.1.4 containing page hash
* [returns] 0 on error or 1 on success * [returns] 0 on error or 1 on success
*/ */
static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj) static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype,
SpcAttributeTypeAndOptionalValue *obj)
{ {
const u_char *blob; const unsigned char *blob;
const unsigned char *sequence_data;
const unsigned char *classid_data;
const unsigned char *serialized_data;
SpcPeImageData *id; SpcPeImageData *id;
SpcSerializedObject *so; SpcSerializedObject *so;
int l, l2; int sequence_len, classid_len, serialized_len, l, l2;
char buf[128]; char buf[128];
/* Validate input object */
if (!obj || !obj->value) if (!obj || !obj->value)
return 0; /* FAILED */ return 0; /* FAILED */
blob = obj->value->value.sequence->data;
id = d2i_SpcPeImageData(NULL, &blob, obj->value->value.sequence->length); /* Decode SpcPeImageData from ASN.1 sequence */
if (!id) { sequence_data = ASN1_STRING_get0_data(obj->value->value.sequence);
sequence_len = ASN1_STRING_length(obj->value->value.sequence);
/* d2i_* modifies the input pointer, so use a temporary variable */
blob = sequence_data;
id = d2i_SpcPeImageData(NULL, &blob, sequence_len);
if (!id)
return 0; /* FAILED */ return 0; /* FAILED */
}
/* Validate SpcPeImageData contents */
if (!id->file) { if (!id->file) {
SpcPeImageData_free(id); SpcPeImageData_free(id);
return 0; /* FAILED */ return 0; /* FAILED */
} }
/* Type 1 means SpcSerializedObject */
if (id->file->type != 1) { if (id->file->type != 1) {
SpcPeImageData_free(id); SpcPeImageData_free(id);
return 1; /* OK - This is not SpcSerializedObject structure that contains page hashes */ return 1; /* OK - no page hashes present */
} }
so = id->file->value.moniker; so = id->file->value.moniker;
if (so->classId->length != sizeof classid_page_hash ||
memcmp(so->classId->data, classid_page_hash, sizeof classid_page_hash)) { /* Validate serialized object class ID */
classid_data = ASN1_STRING_get0_data((ASN1_STRING *)so->classId);
classid_len = ASN1_STRING_length((ASN1_STRING *)so->classId);
if (classid_len != sizeof classid_page_hash ||
memcmp(classid_data, classid_page_hash, sizeof classid_page_hash)) {
SpcPeImageData_free(id); SpcPeImageData_free(id);
return 0; /* FAILED */ return 0; /* FAILED */
} }
/* skip ASN.1 SET hdr */
l = asn1_simple_hdr_len(so->serializedData->data, so->serializedData->length); /*Get serialized ASN.1 blob */
blob = so->serializedData->data + l; serialized_data = ASN1_STRING_get0_data((ASN1_STRING *)so->serializedData);
obj = d2i_SpcAttributeTypeAndOptionalValue(NULL, &blob, so->serializedData->length - l); serialized_len = ASN1_STRING_length((ASN1_STRING *)so->serializedData);
/* Skip ASN.1 SET header */
l = asn1_simple_hdr_len(serialized_data, serialized_len);
blob = serialized_data + l;
/* Decode nested SpcAttributeTypeAndOptionalValue */
obj = d2i_SpcAttributeTypeAndOptionalValue(NULL, &blob, serialized_len - l);
SpcPeImageData_free(id); SpcPeImageData_free(id);
if (!obj) if (!obj)
return 0; /* FAILED */ return 0; /* FAILED */
/* Determine page hash algorithm */
*phtype = 0; *phtype = 0;
buf[0] = 0x00; buf[0] = 0x00;
OBJ_obj2txt(buf, sizeof buf, obj->type, 1); OBJ_obj2txt(buf, sizeof buf, obj->type, 1);
@@ -890,15 +937,30 @@ static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTy
SpcAttributeTypeAndOptionalValue_free(obj); SpcAttributeTypeAndOptionalValue_free(obj);
return 0; /* FAILED */ return 0; /* FAILED */
} }
/* Skip ASN.1 SET hdr */
l2 = asn1_simple_hdr_len(obj->value->value.sequence->data, obj->value->value.sequence->length); /* IMPORTANT:
/* Skip ASN.1 OCTET STRING hdr */ * obj now points to the newly decoded structure,
l = asn1_simple_hdr_len(obj->value->value.sequence->data + l2, obj->value->value.sequence->length - l2); * so refresh sequence_data/sequence_len */
sequence_data = ASN1_STRING_get0_data(obj->value->value.sequence);
sequence_len = ASN1_STRING_length(obj->value->value.sequence);
/* Skip ASN.1 SET header */
l2 = asn1_simple_hdr_len(sequence_data, sequence_len);
/* Skip ASN.1 OCTET STRING header */
l = asn1_simple_hdr_len(sequence_data + l2, sequence_len - l2);
l += l2; l += l2;
*phlen = obj->value->value.sequence->length - l;
/* Extract raw page hash blob */
*phlen = sequence_len - l;
*ph = OPENSSL_malloc((size_t)*phlen); *ph = OPENSSL_malloc((size_t)*phlen);
memcpy(*ph, obj->value->value.sequence->data + l, (size_t)*phlen); if (!*ph) {
SpcAttributeTypeAndOptionalValue_free(obj);
return 0; /* FAILED */
}
memcpy(*ph, sequence_data + l, (size_t)*phlen);
SpcAttributeTypeAndOptionalValue_free(obj); SpcAttributeTypeAndOptionalValue_free(obj);
return 1; /* OK */ return 1; /* OK */
} }
@@ -914,12 +976,25 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
uint16_t nsections, opthdr_size; uint16_t nsections, opthdr_size;
uint32_t alignment, pagesize, hdrsize; uint32_t alignment, pagesize, hdrsize;
uint32_t rs, ro, l, lastpos = 0; uint32_t rs, ro, l, lastpos = 0;
int pphlen, phlen, i, pi = 1; int mdlen, pphlen, phlen, i, pi = 1;
size_t written; size_t written, off, sect_off, sect_tbl, need;
u_char *res, *zeroes; u_char *res = NULL, *zeroes = NULL;
char *sections; char *sections;
const EVP_MD *md = EVP_get_digestbynid(phtype); const EVP_MD *md = EVP_get_digestbynid(phtype);
BIO *bhash; BIO *bhash = NULL;
uint32_t filebound;
size_t pphlen_sz, sections_factor;
if (rphlen == NULL || ctx == NULL || ctx->options == NULL || ctx->pe_ctx == NULL
|| ctx->options->indata == NULL)
return NULL;
if (md == NULL)
return NULL;
mdlen = EVP_MD_size(md);
if (mdlen <= 0)
return NULL;
/* NumberOfSections indicates the size of the section table, /* NumberOfSections indicates the size of the section table,
* which immediately follows the headers, can be up to 65535 under Vista and later */ * which immediately follows the headers, can be up to 65535 under Vista and later */
@@ -961,10 +1036,46 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
fprintf(stderr, "Corrupted optional header size: 0x%08X\n", opthdr_size); fprintf(stderr, "Corrupted optional header size: 0x%08X\n", opthdr_size);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
pphlen = 4 + EVP_MD_size(md); /* Validate that pagesize >= hdrsize to prevent integer underflow */
phlen = pphlen * (3 + (int)nsections + (int)(ctx->pe_ctx->fileend / pagesize)); if (pagesize < hdrsize) {
fprintf(stderr, "Page size (0x%08X) is smaller than header size (0x%08X)\n",
pagesize, hdrsize);
return NULL; /* FAILED */
}
pphlen = 4 + mdlen;
/* Compute an upper bound for result size and guard overflow */
pphlen_sz = (size_t)pphlen;
sections_factor = 3 + (size_t)nsections + ((size_t)ctx->pe_ctx->fileend / pagesize);
if (sections_factor > SIZE_MAX / pphlen_sz) {
fprintf(stderr, "Page hash allocation size would overflow\n");
return NULL; /* FAILED */
}
phlen = (int)(pphlen_sz * sections_factor);
/* Sanity limit - page hash shouldn't exceed reasonable size (16 MB) */
if (phlen < 0 || (size_t)phlen > SIZE_16M) {
fprintf(stderr, "Page hash size exceeds limit: %d\n", phlen);
return NULL; /* FAILED */
}
/* Determine the file boundary for section data validation */
filebound = ctx->pe_ctx->sigpos ? ctx->pe_ctx->sigpos : ctx->pe_ctx->fileend;
/* Validate section table bounds before reading section headers */
sect_off = (size_t)ctx->pe_ctx->header_size + 24u + (size_t)opthdr_size;
sect_tbl = (size_t)nsections * 40u;
if (sect_off > (size_t)filebound || sect_tbl > (size_t)filebound - sect_off) {
fprintf(stderr, "Section table out of bounds: off=%zu size=%zu filebound=%u\n",
sect_off, sect_tbl, filebound);
return NULL; /* FAILED */
}
sections = (char *)ctx->options->indata + sect_off;
bhash = BIO_new(BIO_f_md()); bhash = BIO_new(BIO_f_md());
if (bhash == NULL)
return NULL;
#if defined(__GNUC__) #if defined(__GNUC__)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual" #pragma GCC diagnostic ignored "-Wcast-qual"
@@ -977,7 +1088,10 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
#if defined(__GNUC__) #if defined(__GNUC__)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
BIO_push(bhash, BIO_new(BIO_s_null())); if (BIO_push(bhash, BIO_new(BIO_s_null())) == NULL) {
BIO_free_all(bhash);
return NULL;
}
if (!BIO_write_ex(bhash, ctx->options->indata, ctx->pe_ctx->header_size + 88, &written) if (!BIO_write_ex(bhash, ctx->options->indata, ctx->pe_ctx->header_size + 88, &written)
|| written != ctx->pe_ctx->header_size + 88) { || written != ctx->pe_ctx->header_size + 88) {
BIO_free_all(bhash); BIO_free_all(bhash);
@@ -989,36 +1103,83 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
BIO_free_all(bhash); BIO_free_all(bhash);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!BIO_write_ex(bhash, off = ctx->pe_ctx->header_size + 160 + (size_t)ctx->pe_ctx->pe32plus * 16;
ctx->options->indata + ctx->pe_ctx->header_size + 160 + ctx->pe_ctx->pe32plus*16, if (hdrsize < off || hdrsize > filebound) {
hdrsize - (ctx->pe_ctx->header_size + 160 + ctx->pe_ctx->pe32plus*16), &written) BIO_free_all(bhash);
|| written != hdrsize - (ctx->pe_ctx->header_size + 160 + ctx->pe_ctx->pe32plus*16)) { return NULL; /* FAILED: header too small */
}
if (!BIO_write_ex(bhash, ctx->options->indata + off, (size_t)hdrsize - off, &written)
|| written != hdrsize - off) {
BIO_free_all(bhash); BIO_free_all(bhash);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (pagesize < hdrsize) {
BIO_free_all(bhash);
return NULL; /* FAILED: header larger than page */
}
zeroes = OPENSSL_zalloc((size_t)pagesize); zeroes = OPENSSL_zalloc((size_t)pagesize);
if (!BIO_write_ex(bhash, zeroes, pagesize - hdrsize, &written) if (zeroes == NULL) {
|| written != pagesize - hdrsize) { BIO_free_all(bhash);
return NULL; /* FAILED */
}
if (!BIO_write_ex(bhash, zeroes, (size_t)pagesize - (size_t)hdrsize, &written)
|| written != (size_t)pagesize - (size_t)hdrsize) {
BIO_free_all(bhash); BIO_free_all(bhash);
OPENSSL_free(zeroes); OPENSSL_free(zeroes);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
res = OPENSSL_malloc((size_t)phlen); res = OPENSSL_malloc((size_t)phlen);
if (res == NULL) {
BIO_free_all(bhash);
OPENSSL_free(zeroes);
return NULL; /* FAILED */
}
memset(res, 0, 4); memset(res, 0, 4);
BIO_gets(bhash, (char*)res + 4, EVP_MD_size(md)); if (BIO_gets(bhash, (char *)res + 4, mdlen) != mdlen) {
BIO_free_all(bhash);
OPENSSL_free(zeroes);
OPENSSL_free(res);
return NULL; /* FAILED */
}
BIO_free_all(bhash); BIO_free_all(bhash);
sections = ctx->options->indata + ctx->pe_ctx->header_size + 24 + opthdr_size;
for (i=0; i<nsections; i++) { for (i = 0; i < (int)nsections; i++) {
/* Resource Table address and size */ /* SizeOfRawData and PointerToRawData from section header */
rs = GET_UINT32_LE(sections + 16); rs = GET_UINT32_LE(sections + 16);
ro = GET_UINT32_LE(sections + 20); ro = GET_UINT32_LE(sections + 20);
if (rs == 0 || rs >= UINT32_MAX) { if (rs == 0) {
sections += 40; sections += 40;
continue; continue;
} }
for (l=0; l<rs; l+=pagesize, pi++) { /* Validate section bounds against file size to prevent OOB read */
PUT_UINT32_LE(ro + l, res + pi*pphlen); if (ro >= filebound || rs > filebound - ro) {
fprintf(stderr, "Section %d has invalid bounds: offset=0x%08X, size=0x%08X, fileend=0x%08X\n",
i, ro, rs, filebound);
OPENSSL_free(zeroes);
OPENSSL_free(res);
return NULL; /* FAILED */
}
for (l = 0; l < rs; l += pagesize, pi++) {
need = (size_t)(pi + 1) * (size_t)pphlen;
/* Prevent OOB write into res if pi grows beyond allocated factor */
if (need > (size_t)phlen) {
fprintf(stderr, "Page hash buffer overflow prevented: pi=%d need=%zu phlen=%d\n",
pi, need, phlen);
OPENSSL_free(zeroes);
OPENSSL_free(res);
return NULL; /* FAILED */
}
PUT_UINT32_LE(ro + l, res + (size_t)pi * (size_t)pphlen);
bhash = BIO_new(BIO_f_md()); bhash = BIO_new(BIO_f_md());
if (bhash == NULL) {
OPENSSL_free(zeroes);
OPENSSL_free(res);
return NULL;
}
#if defined(__GNUC__) #if defined(__GNUC__)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual" #pragma GCC diagnostic ignored "-Wcast-qual"
@@ -1033,17 +1194,24 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
#if defined(__GNUC__) #if defined(__GNUC__)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
BIO_push(bhash, BIO_new(BIO_s_null())); if (BIO_push(bhash, BIO_new(BIO_s_null())) == NULL) {
if (rs - l < pagesize) { BIO_free_all(bhash);
if (!BIO_write_ex(bhash, ctx->options->indata + ro + l, rs - l, &written) OPENSSL_free(zeroes);
|| written != rs - l) { OPENSSL_free(res);
return NULL;
}
if (l < rs && rs - l < pagesize) {
size_t tail = (size_t)(rs - l);
if (!BIO_write_ex(bhash, ctx->options->indata + ro + l, tail, &written)
|| written != tail) {
BIO_free_all(bhash); BIO_free_all(bhash);
OPENSSL_free(zeroes); OPENSSL_free(zeroes);
OPENSSL_free(res); OPENSSL_free(res);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!BIO_write_ex(bhash, zeroes, pagesize - (rs - l), &written) if (!BIO_write_ex(bhash, zeroes, pagesize - tail, &written)
|| written != pagesize - (rs - l)) { || written != pagesize - tail) {
BIO_free_all(bhash); BIO_free_all(bhash);
OPENSSL_free(zeroes); OPENSSL_free(zeroes);
OPENSSL_free(res); OPENSSL_free(res);
@@ -1058,17 +1226,34 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
} }
BIO_gets(bhash, (char*)res + pi*pphlen + 4, EVP_MD_size(md)); if (BIO_gets(bhash, (char *)res + (size_t)pi * (size_t)pphlen + 4, mdlen) != mdlen) {
BIO_free_all(bhash);
OPENSSL_free(zeroes);
OPENSSL_free(res);
return NULL; /* FAILED */
}
BIO_free_all(bhash); BIO_free_all(bhash);
} }
lastpos = ro + rs; lastpos = ro + rs;
sections += 40; sections += 40;
} }
PUT_UINT32_LE(lastpos, res + pi*pphlen); /* Final entry */
memset(res + pi*pphlen + 4, 0, (size_t)EVP_MD_size(md)); need = (size_t)(pi + 1) * (size_t)pphlen;
if (need > (size_t)phlen) {
fprintf(stderr, "Page hash buffer overflow prevented at final entry: pi=%d need=%zu phlen=%d\n",
pi, need, phlen);
OPENSSL_free(zeroes);
OPENSSL_free(res);
return NULL; /* FAILED */
}
PUT_UINT32_LE(lastpos, res + (size_t)pi * (size_t)pphlen);
memset(res + (size_t)pi * (size_t)pphlen + 4, 0, (size_t)mdlen);
pi++; pi++;
OPENSSL_free(zeroes); OPENSSL_free(zeroes);
*rphlen = pi*pphlen; *rphlen = pi * pphlen;
return res; return res;
} }
@@ -1088,6 +1273,10 @@ static int pe_verify_page_hash(FILE_FORMAT_CTX *ctx, u_char *ph, int phlen, int
if (!ph) if (!ph)
return 1; /* OK */ return 1; /* OK */
cph = pe_page_hash_calc(&cphlen, ctx, phtype); cph = pe_page_hash_calc(&cphlen, ctx, phtype);
if (!cph) {
fprintf(stderr, "Page hash verification failed: could not calculate page hash\n");
return 0; /* FAILED */
}
mdok = (phlen == cphlen) && !memcmp(ph, cph, (size_t)phlen); mdok = (phlen == cphlen) && !memcmp(ph, cph, (size_t)phlen);
printf("Page hash algorithm : %s\n", OBJ_nid2sn(phtype)); printf("Page hash algorithm : %s\n", OBJ_nid2sn(phtype));
if (ctx->options->verbose) { if (ctx->options->verbose) {
@@ -1190,7 +1379,8 @@ static int pe_check_file(FILE_FORMAT_CTX *ctx)
{ {
uint32_t real_pe_checksum, sum = 0; uint32_t real_pe_checksum, sum = 0;
if (!ctx) { if (ctx == NULL || ctx->pe_ctx == NULL || ctx->options == NULL
|| ctx->options->indata == NULL) {
fprintf(stderr, "Init error\n"); fprintf(stderr, "Init error\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
@@ -1202,25 +1392,52 @@ static int pe_check_file(FILE_FORMAT_CTX *ctx)
printf("Calculated PE checksum: %08X\n", real_pe_checksum); printf("Calculated PE checksum: %08X\n", real_pe_checksum);
printf("Warning: invalid PE checksum\n"); printf("Warning: invalid PE checksum\n");
} }
/* Signature directory bounds */
if (ctx->pe_ctx->sigpos == 0 || ctx->pe_ctx->siglen == 0 if (ctx->pe_ctx->sigpos == 0 || ctx->pe_ctx->siglen == 0
|| ctx->pe_ctx->sigpos > ctx->pe_ctx->fileend) { || ctx->pe_ctx->sigpos > ctx->pe_ctx->fileend
|| ctx->pe_ctx->siglen > ctx->pe_ctx->fileend - ctx->pe_ctx->sigpos) {
fprintf(stderr, "No signature found\n"); fprintf(stderr, "No signature found\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
/* /*
* Validate WIN_CERTIFICATE chain.
* If the sum of the rounded dwLength values does not equal the Size value, * If the sum of the rounded dwLength values does not equal the Size value,
* then either the attribute certificate table or the Size field is corrupted. * then either the attribute certificate table or the Size field is corrupted.
*/ */
while (sum < ctx->pe_ctx->siglen) { while (sum < ctx->pe_ctx->siglen) {
uint32_t len = GET_UINT32_LE(ctx->options->indata + ctx->pe_ctx->sigpos + sum); uint32_t len, off;
if (ctx->pe_ctx->siglen - len > 8) {
/* Prevent overflow in sigpos + sum */
if (sum > UINT32_MAX - ctx->pe_ctx->sigpos) {
fprintf(stderr, "Corrupted attribute certificate table\n"); fprintf(stderr, "Corrupted attribute certificate table\n");
fprintf(stderr, "Attribute certificate table size : %08X\n", ctx->pe_ctx->siglen);
fprintf(stderr, "Attribute certificate entry length: %08X\n\n", len);
return 0; /* FAILED */ return 0; /* FAILED */
} }
/* quadword align data */ off = ctx->pe_ctx->sigpos + sum;
len += len % 8 ? 8 - len % 8 : 0;
/* Need at least 4 bytes to read dwLength */
if (off > ctx->pe_ctx->fileend || ctx->pe_ctx->fileend - off < 4) {
fprintf(stderr, "Corrupted attribute certificate table\n");
return 0; /* FAILED */
}
len = GET_UINT32_LE(ctx->options->indata + off);
/* dwLength must include the 8-byte WIN_CERTIFICATE header */
if (len < 8 || len > ctx->pe_ctx->siglen - sum || len > ctx->pe_ctx->fileend - off) {
fprintf(stderr, "Corrupted attribute certificate table\n");
return 0; /* FAILED */
}
/* Quadword align data */
if (len % 8) {
uint32_t pad = 8 - (len % 8);
/* Ensure quadword alignment does not overflow or exceed remaining table size */
if (pad > ctx->pe_ctx->siglen - sum - len) {
fprintf(stderr, "Corrupted attribute certificate table\n");
return 0; /* FAILED */
}
len += pad;
}
sum += len; sum += len;
} }
if (sum != ctx->pe_ctx->siglen) { if (sum != ctx->pe_ctx->siglen) {
+1 -14
View File
@@ -288,20 +288,7 @@ static int script_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
const EVP_MD *md; const EVP_MD *md;
BIO *bhash; BIO *bhash;
/* FIXME: this shared code most likely belongs in osslsigncode.c */ if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
const u_char *p = content_val->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
if (idc) {
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) {
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
}
}
if (mdtype == -1) {
fprintf(stderr, "Failed to extract current message digest\n\n"); fprintf(stderr, "Failed to extract current message digest\n\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }