19 Commits
2.6 ... 2.7

Author SHA1 Message Date
9ebd79ad18 Release 2.7
Signed-off-by: Michał Trojnara <Michal.Trojnara@stunnel.org>
2023-09-19 21:51:05 +02:00
1700455533 APPX support (#303)
Co-authored-by: Maciej Panek <Maciej.panek@punxworks.com>
Co-authored-by: olszomal <Malgorzata.Olszowka@stunnel.org>
2023-09-19 21:23:32 +02:00
a6f767f5a3 Mark the result as not tainted 2023-09-08 14:09:53 +02:00
4c5b329bc4 fixed mixed declarations and code 2023-09-08 11:51:20 +02:00
5626482e82 fixed a function declaration with a void parameter 2023-09-08 11:51:20 +02:00
2d21a2121c squash gcc debugger warnings
remove nsections>UINT16_MAX check
2023-09-08 11:51:20 +02:00
5d2bf2c80f Fix insufficient MSI_ENTRY comparison 2023-09-04 16:10:25 +02:00
5b8376ce32 Fix construction of signed CAB header
Commit 0f51a06 ("Separate common and format-dependent functions")
performed a substantial amount of refactoring.  Within the CFFOLDER
header construction loop in cab_add_header(), the line

   tmp = GET_UINT32_LE(indata + i);

seems to have been accidentally deleted, instead of being refactored
to become

   tmp = GET_UINT32_LE(ctx->options->indata + i);

with the result that adding a signature to a .cab file will currently
produce an invalid .cab file.

Fix by adding back in the missing line of code.

Signed-off-by: Michael Brown <mbrown@fensystems.co.uk>
2023-08-31 12:27:01 +02:00
1fc2c937f2 Secrets are not available in PRs 2023-08-25 09:49:10 +02:00
2ed54490a6 Use TS_TST_INFO struct 2023-07-31 17:46:01 +02:00
a096aa8a33 Set signing digest to generate RFC3161 response 2023-07-31 17:46:01 +02:00
aa08566a63 Use TS_REQ struct 2023-07-31 17:46:01 +02:00
c04b229ce2 Built-in TSA response generation (#281) 2023-07-28 16:03:04 +02:00
adcfd9a33f Apply suggestions from code review
Added more detailed error messages.
Fixed formatting and indentation.

Co-authored-by: Małgorzata Olszówka <Malgorzata.Olszowka@stunnel.org>
2023-07-21 11:44:55 +02:00
f2f3a8891c Fixed validation of supported command 2023-07-21 11:44:55 +02:00
29eedf9059 Fixed DIFAT sectors writing 2023-06-30 11:30:02 +02:00
d6f94d71f7 doc: correct minimum CMake version in README.md
It's 3.17, not 3.6.
2023-06-01 19:43:42 +02:00
a509a66c65 Initial 2.7-dev commit 2023-05-30 17:23:46 +02:00
7bf4c92d83 Fixed CI version 2023-05-29 23:25:42 +02:00
16 changed files with 3278 additions and 246 deletions

View File

@ -7,7 +7,7 @@ on:
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
version: osslsigncode-2.6-dev
version: osslsigncode-2.7
jobs:
build:

View File

@ -2,7 +2,7 @@ name: Coverity Scan
on:
push:
pull_request:
workflow_dispatch:
jobs:
coverity:

View File

@ -10,7 +10,7 @@ set(BUILTIN_SOCKET ON CACHE BOOL "") # for static Python
# configure basic project information
project(osslsigncode
VERSION 2.6
VERSION 2.7
DESCRIPTION "OpenSSL based Authenticode signing for PE, CAB, CAT and MSI files"
HOMEPAGE_URL "https://github.com/mtrojnar/osslsigncode"
LANGUAGES C)
@ -30,6 +30,7 @@ set(CMAKE_C_STANDARD_REQUIRED ON)
# load CMake library modules
include(FindOpenSSL)
include(FindCURL)
include(FindZLIB)
# load CMake project modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
@ -47,7 +48,7 @@ configure_file(Config.h.in config.h)
target_compile_definitions(osslsigncode PRIVATE HAVE_CONFIG_H=1)
# set sources
target_sources(osslsigncode PRIVATE osslsigncode.c helpers.c msi.c pe.c cab.c cat.c)
target_sources(osslsigncode PRIVATE osslsigncode.c helpers.c msi.c pe.c cab.c cat.c appx.c)
if(NOT UNIX)
target_sources(osslsigncode PRIVATE applink.c)
endif(NOT UNIX)
@ -72,6 +73,12 @@ else(CURL_FOUND)
message(STATUS "cURL support disabled (library not found)")
endif(CURL_FOUND)
if(NOT ZLIB_FOUND)
message(FATAL_ERROR "Zlib library not found")
endif(NOT ZLIB_FOUND)
target_include_directories(osslsigncode PRIVATE ${ZLIB_INCLUDE_DIR})
target_link_libraries(osslsigncode PRIVATE ${ZLIB_LIBRARIES})
# add paths to linker search and installed rpath
set_target_properties(osslsigncode PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE)

View File

@ -1,5 +1,14 @@
# osslsigncode change log
### 2.7 (2023.09.19)
- fixed signing CAB files (by Michael Brown)
- fixed handling of unsupported commands (by Maxim Bagryantsev)
- fixed writing DIFAT sectors
- added APPX support (by Maciej Panek and Małgorzata Olszówka)
- added a built-in TSA response generation (-TSA-certs, -TSA-key
and -TSA-time options)
### 2.6 (2023.05.29)
- modular architecture implemented to simplify adding file formats

View File

@ -46,7 +46,7 @@ We highly recommend downloading a [release tarball](https://github.com/mtrojnar/
brew install cmake pkg-config openssl@1.1
export PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig"
```
**NOTE:** osslsigncode requires CMake 3.6 or newer.
**NOTE:** osslsigncode requires CMake 3.17 or newer.
You may need to use `cmake3` instead of `cmake` to complete the following steps on your system.
* Navigate to the build directory and run CMake to configure the osslsigncode project

2684
appx.c Normal file

File diff suppressed because it is too large Load Diff

12
cab.c
View File

@ -43,6 +43,7 @@ struct cab_ctx_st {
/* FILE_FORMAT method prototypes */
static FILE_FORMAT_CTX *cab_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *outdata);
static ASN1_OBJECT *cab_obsolete_link_get(u_char **p, int *plen, FILE_FORMAT_CTX *ctx);
static int cab_hash_length_get(FILE_FORMAT_CTX *ctx);
static int cab_check_file(FILE_FORMAT_CTX *ctx, int detached);
static u_char *cab_digest_calc(FILE_FORMAT_CTX *ctx, const EVP_MD *md);
static int cab_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7);
@ -57,6 +58,7 @@ static void cab_ctx_cleanup(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata);
FILE_FORMAT file_format_cab = {
.ctx_new = cab_ctx_new,
.data_blob_get = cab_obsolete_link_get,
.hash_length_get = cab_hash_length_get,
.check_file = cab_check_file,
.digest_calc = cab_digest_calc,
.verify_digests = cab_verify_digests,
@ -149,6 +151,15 @@ static ASN1_OBJECT *cab_obsolete_link_get(u_char **p, int *plen, FILE_FORMAT_CTX
return dtype; /* OK */
}
/*
* [in] ctx: structure holds input and output data
* [returns] the size of the message digest when passed an EVP_MD structure (the size of the hash)
*/
static int cab_hash_length_get(FILE_FORMAT_CTX *ctx)
{
return EVP_MD_size(ctx->options->md);
}
/*
* Check if the signature exists.
* [in, out] ctx: structure holds input and output data
@ -900,6 +911,7 @@ static int cab_add_header(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata)
*/
nfolders = GET_UINT16_LE(ctx->options->indata + 26);
while (nfolders) {
tmp = GET_UINT32_LE(ctx->options->indata + i);
tmp += 24;
PUT_UINT32_LE(tmp, buf);
BIO_write(hash, buf, 4);

2
cat.c
View File

@ -99,7 +99,7 @@ static FILE_FORMAT_CTX *cat_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *out
if (options->nest)
/* I've not tried using set_nested_signature as signtool won't do this */
printf("Warning: CAT files do not support nesting\n");
printf("Warning: CAT files do not support nesting (multiple signature)\n");
if (options->jp >= 0)
printf("Warning: -jp option is only valid for CAB files\n");
if (options->pagehash == 1)

View File

@ -123,8 +123,9 @@ string(SUBSTRING ${sha256sum} 0 64 leafhash)
enable_testing()
set(extensions_4 "exe" "ex_" "msi" "cat")
set(extensions_3 "exe" "ex_" "msi")
set(extensions_all "exe" "ex_" "msi" "256appx" "512appx" "cat")
set(extensions_nocat "exe" "ex_" "msi" "256appx" "512appx")
set(extensions_nocatappx "exe" "ex_" "msi")
# Test 1
# Print osslsigncode version
@ -135,7 +136,7 @@ add_test(NAME version
# Tests 2-5
# Sign with PKCS#12 container with legacy RC2-40-CBC private key and certificate encryption algorithm
foreach(ext ${extensions_4})
foreach(ext ${extensions_all})
add_test(
NAME legacy_${ext}
COMMAND osslsigncode "sign"
@ -151,7 +152,7 @@ foreach(ext ${extensions_4})
"-n" "osslsigncode"
"-in" "${FILES}/unsigned.${ext}"
"-out" "${FILES}/legacy.${ext}")
endforeach(ext ${extensions_4})
endforeach(ext ${extensions_all})
# Tests 6-9
# Sign with PKCS#12 container with legacy RC2-40-CBC private key and certificate encryption algorithm
@ -159,7 +160,7 @@ endforeach(ext ${extensions_4})
# Option "-nolegacy" requires OpenSSL 3.0.0 or later
# This tests are expected to fail
if(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.0.0)
foreach(ext ${extensions_4})
foreach(ext ${extensions_all})
add_test(
NAME nolegacy_${ext}
COMMAND osslsigncode "sign"
@ -180,12 +181,12 @@ if(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.0.0)
nolegacy_${ext}
PROPERTIES
WILL_FAIL TRUE)
endforeach(ext ${extensions_4})
endforeach(ext ${extensions_all})
endif(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.0.0)
# Tests 10-13
# Sign with PKCS#12 container with AES-256-CBC private key and certificate encryption algorithm
foreach(ext ${extensions_4})
foreach(ext ${extensions_all})
add_test(
NAME signed_${ext}
COMMAND osslsigncode "sign"
@ -201,11 +202,11 @@ foreach(ext ${extensions_4})
"-n" "osslsigncode"
"-in" "${FILES}/unsigned.${ext}"
"-out" "${FILES}/signed.${ext}")
endforeach(ext ${extensions_4})
endforeach(ext ${extensions_all})
# Tests 14-17
# Sign with revoked certificate
foreach(ext ${extensions_4})
foreach(ext ${extensions_all})
add_test(
NAME revoked_${ext}
COMMAND osslsigncode "sign"
@ -222,12 +223,12 @@ foreach(ext ${extensions_4})
"-n" "osslsigncode"
"-in" "${FILES}/unsigned.${ext}"
"-out" "${FILES}/revoked.${ext}")
endforeach(ext ${extensions_4})
endforeach(ext ${extensions_all})
# Tests 18-20
# Remove signature
# Unsupported command for CAT files
foreach(ext ${extensions_3})
foreach(ext ${extensions_nocat})
add_test(
NAME removed_${ext}
COMMAND osslsigncode "remove-signature"
@ -238,11 +239,11 @@ foreach(ext ${extensions_3})
PROPERTIES
DEPENDS "signed_${ext}"
REQUIRED_FILES "${FILES}/signed.${ext}")
endforeach(ext ${extensions_3})
endforeach(ext ${extensions_nocat})
# Tests 21-24
# Extract PKCS#7 signature in PEM format
foreach(ext ${extensions_4})
foreach(ext ${extensions_all})
add_test(
NAME extract_pem_${ext}
COMMAND osslsigncode "extract-signature"
@ -254,11 +255,11 @@ foreach(ext ${extensions_4})
PROPERTIES
DEPENDS "signed_${ext}"
REQUIRED_FILES "${FILES}/signed.${ext}")
endforeach(ext ${extensions_4})
endforeach(ext ${extensions_all})
# Tests 25-28
# Extract PKCS#7 signature in default DER format
foreach(ext ${extensions_4})
foreach(ext ${extensions_all})
add_test(
NAME extract_der_${ext}
COMMAND osslsigncode "extract-signature"
@ -269,13 +270,13 @@ foreach(ext ${extensions_4})
PROPERTIES
DEPENDS "signed_${ext}"
REQUIRED_FILES "${FILES}/signed.${ext}")
endforeach(ext ${extensions_4})
endforeach(ext ${extensions_all})
# Tests 29-34
# Attach signature in PEM or DER format
# Unsupported command for CAT files
set(formats "pem" "der")
foreach(ext ${extensions_3})
foreach(ext ${extensions_nocat})
foreach(format ${formats})
add_test(
NAME attached_${format}_${ext}
@ -299,11 +300,11 @@ foreach(ext ${extensions_3})
REQUIRED_FILES "${FILES}/signed.${ext}"
REQUIRED_FILES "${FILES}/${ext}.${format}")
endforeach(format ${formats})
endforeach(ext ${extensions_3})
endforeach(ext ${extensions_nocat})
# Tests 35-38
# Add an unauthenticated blob to a previously-signed file
foreach(ext ${extensions_4})
foreach(ext ${extensions_all})
add_test(
NAME added_${ext}
COMMAND osslsigncode "add"
@ -316,11 +317,11 @@ foreach(ext ${extensions_4})
PROPERTIES
DEPENDS "signed_${ext}"
REQUIRED_FILES "${FILES}/signed.${ext}")
endforeach(ext ${extensions_4})
endforeach(ext ${extensions_all})
# Tests 39-42
# Add the new nested signature instead of replacing the first one
foreach(ext ${extensions_4})
foreach(ext ${extensions_all})
add_test(
NAME nested_${ext}
COMMAND osslsigncode "sign"
@ -344,14 +345,15 @@ foreach(ext ${extensions_4})
PROPERTIES
DEPENDS "signed_${ext}"
REQUIRED_FILES "${FILES}/signed.${ext}")
endforeach(ext ${extensions_4})
endforeach(ext ${extensions_all})
### Verify signature ###
# Tests 43-45
# Verify PE/MSI/CAB files signed in the catalog file
foreach(ext ${extensions_3})
# APPX does not support detached PKCS#7 signature
foreach(ext ${extensions_nocatappx})
add_test(
NAME verify_catalog_${ext}
COMMAND osslsigncode "verify"
@ -367,13 +369,13 @@ foreach(ext ${extensions_3})
DEPENDS "signed_${ext}"
REQUIRED_FILES "${FILES}/signed.cat"
REQUIRED_FILES "${FILES}/unsigned.${ext}")
endforeach(ext ${extensions_3})
endforeach(ext ${extensions_nocatappx})
# Tests 46-69
# Verify signature
set(files "legacy" "signed" "nested" "added" "removed" "revoked" "attached_pem" "attached_der")
foreach(file ${files})
foreach(ext ${extensions_3})
foreach(ext ${extensions_nocat})
add_test(
NAME verify_${file}_${ext}
COMMAND osslsigncode "verify"
@ -386,18 +388,18 @@ foreach(file ${files})
PROPERTIES
DEPENDS "${file}_${ext}"
REQUIRED_FILES "${FILES}/${file}.${ext}")
endforeach(ext ${extensions_3})
endforeach(ext ${extensions_nocat})
endforeach(file ${files})
# "Removed" and "revoked" tests are expected to fail
set(files "removed" "revoked")
foreach(file ${files})
foreach(ext ${extensions_3})
foreach(ext ${extensions_nocat})
set_tests_properties(
verify_${file}_${ext}
PROPERTIES
WILL_FAIL TRUE)
endforeach(ext ${extensions_3})
endforeach(ext ${extensions_nocat})
endforeach(file ${files})
if(Python3_FOUND OR server_error)
@ -409,7 +411,7 @@ if(Python3_FOUND OR server_error)
# Use "cert" "expired" "revoked" without X509v3 CRL Distribution Points extension
# and "cert_crldp" "revoked_crldp" contain X509v3 CRL Distribution Points extension
set(pem_certs "cert" "expired" "revoked" "cert_crldp" "revoked_crldp")
foreach(ext ${extensions_4})
foreach(ext ${extensions_all})
foreach(cert ${pem_certs})
add_test(
NAME sign_ts_${cert}_${ext}
@ -432,14 +434,14 @@ if(Python3_FOUND OR server_error)
PROPERTIES
REQUIRED_FILES "${LOGS}/port.log")
endforeach(cert ${pem_certs})
endforeach(ext ${extensions_4})
endforeach(ext ${extensions_all})
### Verify Time-Stamp Authority ###
# Tests 90-92
# Signature verification time: Sep 1 00:00:00 2019 GMT
foreach(ext ${extensions_3})
foreach(ext ${extensions_nocat})
add_test(
NAME verify_ts_cert_${ext}
COMMAND osslsigncode "verify"
@ -453,11 +455,11 @@ if(Python3_FOUND OR server_error)
DEPENDS "sign_ts_cert_${ext}"
REQUIRED_FILES "${FILES}/ts_cert.${ext}"
REQUIRED_FILES "${LOGS}/port.log")
endforeach(ext ${extensions_3})
endforeach(ext ${extensions_nocat})
# Tests 93-95
# Signature verification time: Jan 1 00:00:00 2035 GMT
foreach(ext ${extensions_3})
foreach(ext ${extensions_nocat})
add_test(
NAME verify_ts_future_${ext}
COMMAND osslsigncode "verify"
@ -471,12 +473,12 @@ if(Python3_FOUND OR server_error)
DEPENDS "sign_ts_cert_${ext}"
REQUIRED_FILES "${FILES}/ts_cert.${ext}"
REQUIRED_FILES "${LOGS}/port.log")
endforeach(ext ${extensions_3})
endforeach(ext ${extensions_nocat})
# Tests 96-98
# Verify with ignored timestamp
# This tests are expected to fail
foreach(ext ${extensions_3})
foreach(ext ${extensions_nocat})
add_test(
NAME verify_ts_ignore_${ext}
COMMAND osslsigncode "verify"
@ -492,7 +494,7 @@ if(Python3_FOUND OR server_error)
REQUIRED_FILES "${FILES}/ts_cert.${ext}"
REQUIRED_FILES "${LOGS}/port.log"
WILL_FAIL TRUE)
endforeach(ext ${extensions_3})
endforeach(ext ${extensions_nocat})
### Verify CRL Distribution Points ###
@ -501,7 +503,7 @@ if(Python3_FOUND OR server_error)
# Verify file signed with X509v3 CRL Distribution Points extension
# Signature verification time: Sep 1 00:00:00 2019 GMT
# Check X509v3 CRL Distribution Points extension, don't use "-CRLfile" and "-TSA-CRLfile" options
foreach(ext ${extensions_3})
foreach(ext ${extensions_nocat})
add_test(
NAME verify_ts_cert_crldp_${ext}
COMMAND osslsigncode "verify"
@ -515,13 +517,13 @@ if(Python3_FOUND OR server_error)
DEPENDS "sign_ts_cert_crldp_${ext}"
REQUIRED_FILES "${FILES}/ts_cert_crldp.${ext}"
REQUIRED_FILES "${LOGS}/port.log")
endforeach(ext ${extensions_3})
endforeach(ext ${extensions_nocat})
# Tests 102-107
# Verify with expired or revoked certificate without X509v3 CRL Distribution Points extension
# This tests are expected to fail
set(failed_certs "expired" "revoked")
foreach(ext ${extensions_3})
foreach(ext ${extensions_nocat})
foreach(cert ${failed_certs})
add_test(
NAME verify_ts_${cert}_${ext}
@ -539,13 +541,13 @@ if(Python3_FOUND OR server_error)
REQUIRED_FILES "${LOGS}/port.log"
WILL_FAIL TRUE)
endforeach(cert ${failed_certs})
endforeach(ext ${extensions_3})
endforeach(ext ${extensions_nocat})
# Tests 108-110
# Verify with revoked certificate contains X509v3 CRL Distribution Points extension
# Check X509v3 CRL Distribution Points extension, don't use "-CRLfile" and "-TSA-CRLfile" options
# This test is expected to fail
foreach(ext ${extensions_3})
foreach(ext ${extensions_nocat})
add_test(
NAME verify_ts_revoked_crldp_${ext}
COMMAND osslsigncode "verify"
@ -560,7 +562,7 @@ if(Python3_FOUND OR server_error)
REQUIRED_FILES "${FILES}/ts_revoked_crldp.${ext}"
REQUIRED_FILES "${LOGS}/port.log"
WILL_FAIL TRUE)
endforeach(ext ${extensions_3})
endforeach(ext ${extensions_nocat})
### Cleanup ###
@ -585,7 +587,7 @@ endif(Python3_FOUND OR server_error)
# Test 112
# Delete test files
foreach(ext ${extensions_4})
foreach(ext ${extensions_all})
set(OUTPUT_FILES ${OUTPUT_FILES} "${FILES}/legacy.${ext}")
set(OUTPUT_FILES ${OUTPUT_FILES} "${FILES}/signed.${ext}")
set(OUTPUT_FILES ${OUTPUT_FILES} "${FILES}/signed_crldp.${ext}")
@ -603,7 +605,7 @@ foreach(ext ${extensions_4})
endforeach(format ${formats})
set(OUTPUT_FILES ${OUTPUT_FILES} "${FILES}/jreq.tsq")
set(OUTPUT_FILES ${OUTPUT_FILES} "${FILES}/jresp.tsr")
endforeach(ext ${extensions_4})
endforeach(ext ${extensions_all})
add_test(NAME remove_files
COMMAND ${CMAKE_COMMAND} -E rm -f ${OUTPUT_FILES})

View File

@ -12,7 +12,7 @@
static int pkcs7_set_content_blob(PKCS7 *sig, PKCS7 *cursig);
static SpcSpOpusInfo *spc_sp_opus_info_create(FILE_FORMAT_CTX *ctx);
static int spc_indirect_data_content_get(u_char **blob, int *len, FILE_FORMAT_CTX *ctx);
static int pkcs7_set_spc_indirect_data_content(PKCS7 *p7, BIO *hash, u_char *buf, int len);
static int pkcs7_set_spc_indirect_data_content(PKCS7 *p7, BIO *hash, u_char *buf, int len, FILE_FORMAT_CTX *ctx);
static int pkcs7_signer_info_add_spc_sp_opus_info(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx);
static int pkcs7_signer_info_add_purpose(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx);
@ -298,6 +298,7 @@ PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx)
/*
* [in, out] p7: new PKCS#7 signature
* [in] hash: message digest BIO
* [in] ctx: structure holds input and output data
* [returns] 0 on error or 1 on success
*/
int add_indirect_data_object(PKCS7 *p7, BIO *hash, FILE_FORMAT_CTX *ctx)
@ -471,7 +472,7 @@ int pkcs7_set_data_content(PKCS7 *p7, BIO *hash, FILE_FORMAT_CTX *ctx)
buf = OPENSSL_malloc(SIZE_64K);
memcpy(buf, p, (size_t)len);
OPENSSL_free(p);
if (!pkcs7_set_spc_indirect_data_content(p7, hash, buf, len)) {
if (!pkcs7_set_spc_indirect_data_content(p7, hash, buf, len, ctx)) {
OPENSSL_free(buf);
return 0; /* FAILED */
}
@ -573,6 +574,7 @@ static int spc_indirect_data_content_get(u_char **blob, int *len, FILE_FORMAT_CT
{
u_char *p = NULL;
int hashlen, l = 0;
int mdtype = EVP_MD_nid(ctx->options->md);
void *hash;
SpcIndirectDataContent *idc = SpcIndirectDataContent_new();
@ -582,13 +584,12 @@ static int spc_indirect_data_content_get(u_char **blob, int *len, FILE_FORMAT_CT
idc->data->type = ctx->format->data_blob_get(&p, &l, ctx);
idc->data->value->value.sequence->data = p;
idc->data->value->value.sequence->length = l;
idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(EVP_MD_nid(ctx->options->md));
idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(mdtype);
idc->messageDigest->digestAlgorithm->parameters = ASN1_TYPE_new();
idc->messageDigest->digestAlgorithm->parameters->type = V_ASN1_NULL;
hashlen = EVP_MD_size(ctx->options->md);
hash = OPENSSL_malloc((size_t)hashlen);
memset(hash, 0, (size_t)hashlen);
hashlen = ctx->format->hash_length_get(ctx);
hash = OPENSSL_zalloc((size_t)hashlen);
ASN1_OCTET_STRING_set(idc->messageDigest->digest, hash, hashlen);
OPENSSL_free(hash);
@ -597,7 +598,7 @@ static int spc_indirect_data_content_get(u_char **blob, int *len, FILE_FORMAT_CT
p = *blob;
i2d_SpcIndirectDataContent(idc, &p);
SpcIndirectDataContent_free(idc);
*len -= EVP_MD_size(ctx->options->md);
*len -= hashlen;
return 1; /* OK */
}
@ -607,17 +608,24 @@ static int spc_indirect_data_content_get(u_char **blob, int *len, FILE_FORMAT_CT
* [in] hash: message digest BIO
* [in] blob: SpcIndirectDataContent data
* [in] len: SpcIndirectDataContent data length
* [in] ctx: FILE_FORMAT_CTX structure
* [returns] 0 on error or 1 on success
*/
static int pkcs7_set_spc_indirect_data_content(PKCS7 *p7, BIO *hash, u_char *buf, int len)
static int pkcs7_set_spc_indirect_data_content(PKCS7 *p7, BIO *hash, u_char *buf, int len, FILE_FORMAT_CTX *ctx)
{
u_char mdbuf[EVP_MAX_MD_SIZE];
int mdlen, seqhdrlen;
u_char mdbuf[5 * EVP_MAX_MD_SIZE + 24];
int mdlen, seqhdrlen, hashlen;
BIO *bio;
PKCS7 *td7;
mdlen = BIO_gets(hash, (char*)mdbuf, EVP_MAX_MD_SIZE);
memcpy(buf+len, mdbuf, (size_t)mdlen);
hashlen = ctx->format->hash_length_get(ctx);
if (hashlen > EVP_MAX_MD_SIZE) {
/* APPX format specific */
mdlen = BIO_read(hash, (char*)mdbuf, hashlen);
} else {
mdlen = BIO_gets(hash, (char*)mdbuf, EVP_MAX_MD_SIZE);
}
memcpy(buf + len, mdbuf, (size_t)mdlen);
seqhdrlen = asn1_simple_hdr_len(buf, len);
if ((bio = PKCS7_dataInit(p7, NULL)) == NULL) {
@ -638,7 +646,7 @@ static int pkcs7_set_spc_indirect_data_content(PKCS7 *p7, BIO *hash, u_char *buf
td7->d.other = ASN1_TYPE_new();
td7->d.other->type = V_ASN1_SEQUENCE;
td7->d.other->value.sequence = ASN1_STRING_new();
ASN1_STRING_set(td7->d.other->value.sequence, buf, len+mdlen);
ASN1_STRING_set(td7->d.other->value.sequence, buf, len + mdlen);
if (!PKCS7_set_content(p7, td7)) {
PKCS7_free(td7);
printf("PKCS7_set_content failed\n");

52
msi.c
View File

@ -186,13 +186,16 @@ typedef struct {
char *ministream;
char *minifat;
char *fat;
char *difat;
uint32_t dirtreeLen;
uint32_t miniStreamLen;
uint32_t minifatLen;
uint32_t fatLen;
uint32_t difatLen;
uint32_t ministreamsMemallocCount;
uint32_t minifatMemallocCount;
uint32_t fatMemallocCount;
uint32_t difatMemallocCount;
uint32_t dirtreeSectorsCount;
uint32_t minifatSectorsCount;
uint32_t fatSectorsCount;
@ -212,6 +215,7 @@ struct msi_ctx_st {
/* FILE_FORMAT method prototypes */
static FILE_FORMAT_CTX *msi_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *outdata);
static ASN1_OBJECT *msi_spc_sip_info_get(u_char **p, int *plen, FILE_FORMAT_CTX *ctx);
static int msi_hash_length_get(FILE_FORMAT_CTX *ctx);
static int msi_check_file(FILE_FORMAT_CTX *ctx, int detached);
static u_char *msi_digest_calc(FILE_FORMAT_CTX *ctx, const EVP_MD *md);
static int msi_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7);
@ -225,6 +229,7 @@ static void msi_ctx_cleanup(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata);
FILE_FORMAT file_format_msi = {
.ctx_new = msi_ctx_new,
.data_blob_get = msi_spc_sip_info_get,
.hash_length_get = msi_hash_length_get,
.check_file = msi_check_file,
.digest_calc = msi_digest_calc,
.verify_digests = msi_verify_digests,
@ -343,6 +348,15 @@ static ASN1_OBJECT *msi_spc_sip_info_get(u_char **p, int *plen, FILE_FORMAT_CTX
return dtype; /* OK */
}
/*
* [in] ctx: structure holds input and output data
* [returns] the size of the message digest when passed an EVP_MD structure (the size of the hash)
*/
static int msi_hash_length_get(FILE_FORMAT_CTX *ctx)
{
return EVP_MD_size(ctx->options->md);
}
/*
* Get DigitalSignature and MsiDigitalSignatureEx streams,
* check if the signature exists.
@ -1266,9 +1280,7 @@ static int msi_dirent_new(MSI_FILE *msi, MSI_ENTRY *entry, MSI_DIRENT *parent, M
}
/* detect cycles in previously visited entries (parents, siblings) */
if (!ret) { /* initialized (non-root entry) */
if ((entry->leftSiblingID != NOSTREAM && tortoise->entry->leftSiblingID == entry->leftSiblingID)
|| (entry->rightSiblingID != NOSTREAM && tortoise->entry->rightSiblingID == entry->rightSiblingID)
|| (entry->childID != NOSTREAM && tortoise->entry->childID == entry->childID)) {
if (!memcmp(entry, tortoise->entry, sizeof(MSI_ENTRY))) {
printf("MSI_ENTRY cycle detected at level %d\n", cnt);
OPENSSL_free(entry);
return 0; /* FAILED */
@ -1542,6 +1554,16 @@ static void fat_append(MSI_OUT *out, char *buf, uint32_t len)
out->fatLen += len;
}
static void difat_append(MSI_OUT *out, char *buf, uint32_t len)
{
if (out->difatLen == (uint64_t)out->difatMemallocCount * out->sectorSize) {
out->difatMemallocCount += 1;
out->difat = OPENSSL_realloc(out->difat, (size_t)(out->difatMemallocCount * out->sectorSize));
}
memcpy(out->difat + out->difatLen, buf, (size_t)len);
out->difatLen += len;
}
static int msi_dirent_delete(MSI_DIRENT *dirent, const u_char *name, uint16_t nameLen)
{
int i;
@ -1828,7 +1850,7 @@ static char *msi_dirent_get(MSI_ENTRY *entry)
return data;
}
static char *msi_unused_dirent_get()
static char *msi_unused_dirent_get(void)
{
char *data = OPENSSL_malloc(DIRENT_SIZE);
@ -1938,25 +1960,14 @@ static void dirtree_save(MSI_DIRENT *dirent, BIO *outdata, MSI_OUT *out)
out->sectorNum += out->dirtreeSectorsCount;
}
static void fat_pad_last_sector(MSI_OUT *out, int padValue, char *buf)
{
if (out->fatLen % out->sectorSize > 0) {
uint32_t remain = out->sectorSize - out->fatLen % out->sectorSize;
memset(buf, padValue, (size_t)remain);
fat_append(out, buf, remain);
}
}
static int fat_save(BIO *outdata, MSI_OUT *out)
{
char buf[MAX_SECTOR_SIZE];
uint32_t i, j, remain, difatSectors, difatEntriesPerSector, fatSectorIndex, lastFatSectorIndex;
uint32_t i, j, remain, difatSectors, difatEntriesPerSector = 0, fatSectorIndex, lastFatSectorIndex;
remain = (out->fatLen + out->sectorSize - 1) / out->sectorSize;
out->fatSectorsCount = (out->fatLen + remain * 4 + out->sectorSize - 1) / out->sectorSize;
fat_pad_last_sector(out, 0, buf);
if (out->fatSectorsCount > DIFAT_IN_HEADER) {
difatEntriesPerSector = (out->sectorSize / 4) - 1;
difatSectors = (out->fatSectorsCount - DIFAT_IN_HEADER + difatEntriesPerSector - 1) / difatEntriesPerSector;
@ -2001,7 +2012,7 @@ static int fat_save(BIO *outdata, MSI_OUT *out)
PUT_UINT32_LE(out->sectorNum + 1, buf + out->sectorSize - 4);
}
fat_append(out, buf, out->sectorSize);
difat_append(out, buf, out->sectorSize);
out->sectorNum++;
}
}
@ -2019,9 +2030,14 @@ static int fat_save(BIO *outdata, MSI_OUT *out)
}
/* empty unallocated free sectors in the last FAT sector */
fat_pad_last_sector(out, (int)FREESECT, buf);
if (out->fatLen % out->sectorSize > 0) {
remain = out->sectorSize - out->fatLen % out->sectorSize;
memset(buf, (int)FREESECT, (size_t)remain);
fat_append(out, buf, remain);
}
BIO_write(outdata, out->fat, (int)out->fatLen);
BIO_write(outdata, out->difat, (int)out->difatLen);
return 1; /* OK */
}

View File

@ -158,15 +158,10 @@ ASN1_SEQUENCE(CatalogAuthAttr) = {
IMPLEMENT_ASN1_FUNCTIONS(CatalogAuthAttr)
ASN1_SEQUENCE(MessageImprint) = {
ASN1_SIMPLE(MessageImprint, digestAlgorithm, AlgorithmIdentifier),
ASN1_SIMPLE(MessageImprint, digest, ASN1_OCTET_STRING)
} ASN1_SEQUENCE_END(MessageImprint)
IMPLEMENT_ASN1_FUNCTIONS(MessageImprint)
#ifdef ENABLE_CURL
/*
* Structures for Authenticode Timestamp
*/
ASN1_SEQUENCE(TimeStampRequestBlob) = {
ASN1_SIMPLE(TimeStampRequestBlob, type, ASN1_OBJECT),
ASN1_EXP_OPT(TimeStampRequestBlob, signature, ASN1_OCTET_STRING, 0)
@ -181,59 +176,8 @@ ASN1_SEQUENCE(TimeStampRequest) = {
IMPLEMENT_ASN1_FUNCTIONS(TimeStampRequest)
/* RFC3161 Time stamping */
ASN1_SEQUENCE(PKIStatusInfo) = {
ASN1_SIMPLE(PKIStatusInfo, status, ASN1_INTEGER),
ASN1_SEQUENCE_OF_OPT(PKIStatusInfo, statusString, ASN1_UTF8STRING),
ASN1_OPT(PKIStatusInfo, failInfo, ASN1_BIT_STRING)
} ASN1_SEQUENCE_END(PKIStatusInfo)
IMPLEMENT_ASN1_FUNCTIONS(PKIStatusInfo)
ASN1_SEQUENCE(TimeStampResp) = {
ASN1_SIMPLE(TimeStampResp, status, PKIStatusInfo),
ASN1_OPT(TimeStampResp, token, PKCS7)
} ASN1_SEQUENCE_END(TimeStampResp)
IMPLEMENT_ASN1_FUNCTIONS(TimeStampResp)
ASN1_SEQUENCE(TimeStampReq) = {
ASN1_SIMPLE(TimeStampReq, version, ASN1_INTEGER),
ASN1_SIMPLE(TimeStampReq, messageImprint, MessageImprint),
ASN1_OPT (TimeStampReq, reqPolicy, ASN1_OBJECT),
ASN1_OPT (TimeStampReq, nonce, ASN1_INTEGER),
ASN1_SIMPLE(TimeStampReq, certReq, ASN1_FBOOLEAN),
ASN1_IMP_SEQUENCE_OF_OPT(TimeStampReq, extensions, X509_EXTENSION, 0)
} ASN1_SEQUENCE_END(TimeStampReq)
IMPLEMENT_ASN1_FUNCTIONS(TimeStampReq)
#endif /* ENABLE_CURL */
ASN1_SEQUENCE(TimeStampAccuracy) = {
ASN1_OPT(TimeStampAccuracy, seconds, ASN1_INTEGER),
ASN1_IMP_OPT(TimeStampAccuracy, millis, ASN1_INTEGER, 0),
ASN1_IMP_OPT(TimeStampAccuracy, micros, ASN1_INTEGER, 1)
} ASN1_SEQUENCE_END(TimeStampAccuracy)
IMPLEMENT_ASN1_FUNCTIONS(TimeStampAccuracy)
ASN1_SEQUENCE(TimeStampToken) = {
ASN1_SIMPLE(TimeStampToken, version, ASN1_INTEGER),
ASN1_SIMPLE(TimeStampToken, policy_id, ASN1_OBJECT),
ASN1_SIMPLE(TimeStampToken, messageImprint, MessageImprint),
ASN1_SIMPLE(TimeStampToken, serial, ASN1_INTEGER),
ASN1_SIMPLE(TimeStampToken, time, ASN1_GENERALIZEDTIME),
ASN1_OPT(TimeStampToken, accuracy, TimeStampAccuracy),
ASN1_OPT(TimeStampToken, ordering, ASN1_FBOOLEAN),
ASN1_OPT(TimeStampToken, nonce, ASN1_INTEGER),
ASN1_EXP_OPT(TimeStampToken, tsa, GENERAL_NAME, 0),
ASN1_IMP_SEQUENCE_OF_OPT(TimeStampToken, extensions, X509_EXTENSION, 1)
} ASN1_SEQUENCE_END(TimeStampToken)
IMPLEMENT_ASN1_FUNCTIONS(TimeStampToken)
ASN1_SEQUENCE(CatalogInfo) = {
ASN1_SIMPLE(CatalogInfo, digest, ASN1_OCTET_STRING),
ASN1_SET_OF(CatalogInfo, attributes, CatalogAuthAttr)
@ -317,48 +261,71 @@ static BIO *bio_encode_rfc3161_request(PKCS7 *p7, const EVP_MD *md)
STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
PKCS7_SIGNER_INFO *si;
u_char mdbuf[EVP_MAX_MD_SIZE];
TimeStampReq *req;
BIO *bout, *bhash;
TS_MSG_IMPRINT *msg_imprint = NULL;
X509_ALGOR *alg = NULL;
TS_REQ *req = NULL;
BIO *bout = NULL, *bhash = NULL;
u_char *p;
int len;
signer_info = PKCS7_get_signer_info(p7);
if (!signer_info)
return NULL; /* FAILED */
goto out;
si = sk_PKCS7_SIGNER_INFO_value(signer_info, 0);
if (!si)
return NULL; /* FAILED */
goto out;
bhash = BIO_new(BIO_f_md());
if (!BIO_set_md(bhash, md)) {
printf("Unable to set the message digest of BIO\n");
BIO_free_all(bhash);
return NULL; /* FAILED */
goto out;
}
BIO_push(bhash, BIO_new(BIO_s_null()));
BIO_write(bhash, si->enc_digest->data, si->enc_digest->length);
BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md));
BIO_free_all(bhash);
req = TimeStampReq_new();
ASN1_INTEGER_set(req->version, 1);
req->messageImprint->digestAlgorithm->algorithm = OBJ_nid2obj(EVP_MD_nid(md));
req->messageImprint->digestAlgorithm->parameters = ASN1_TYPE_new();
req->messageImprint->digestAlgorithm->parameters->type = V_ASN1_NULL;
ASN1_OCTET_STRING_set(req->messageImprint->digest, mdbuf, EVP_MD_size(md));
req->certReq = 0xFF;
req = TS_REQ_new();
if (!req)
goto out;
if (!TS_REQ_set_version(req, 1))
goto out;
len = i2d_TimeStampReq(req, NULL);
msg_imprint = TS_MSG_IMPRINT_new();
if (!msg_imprint)
goto out;
alg = X509_ALGOR_new();
if (!alg)
goto out;
X509_ALGOR_set_md(alg, md);
if (!X509_ALGOR_set0(alg, OBJ_nid2obj(EVP_MD_nid(md)), V_ASN1_NULL, NULL))
goto out;
if (!TS_MSG_IMPRINT_set_algo(msg_imprint, alg))
goto out;
if (!TS_MSG_IMPRINT_set_msg(msg_imprint, mdbuf, EVP_MD_size(md)))
goto out;
if (!TS_REQ_set_msg_imprint(req, msg_imprint))
goto out;
/* TSA is expected to include its signing certificate in the response, flag 0xFF */
if (!TS_REQ_set_cert_req(req, 1))
goto out;
len = i2d_TS_REQ(req, NULL);
p = OPENSSL_malloc((size_t)len);
len = i2d_TimeStampReq(req, &p);
len = i2d_TS_REQ(req, &p);
p -= len;
TimeStampReq_free(req);
bout = BIO_new(BIO_s_mem());
BIO_write(bout, p, len);
OPENSSL_free(p);
(void)BIO_flush(bout);
out:
BIO_free_all(bhash);
TS_MSG_IMPRINT_free(msg_imprint);
X509_ALGOR_free(alg);
TS_REQ_free(req);
return bout;
}
@ -406,19 +373,19 @@ static BIO *bio_encode_authenticode_request(PKCS7 *p7)
}
/*
* Decode a RFC 3161 response from BIO.
* If successful the RFC 3161 timestamp will be written into
* the PKCS7 SignerInfo structure as an unauthorized attribute - cont[1].
* [in, out] p7: new PKCS#7 signature
* [in] bin: BIO with http data
* [in] response: RFC3161 response
* [in] verbose: additional output mode
* [returns] 1 on error or 0 on success
*/
static int decode_rfc3161_response(PKCS7 *p7, BIO *bin, int verbose)
static int attach_rfc3161_response(PKCS7 *p7, TS_RESP *response, int verbose)
{
PKCS7_SIGNER_INFO *si;
STACK_OF(X509_ATTRIBUTE) *attrs;
TimeStampResp *reply;
TS_STATUS_INFO *status;
PKCS7 *token;
u_char *p;
int i, len;
STACK_OF(PKCS7_SIGNER_INFO) *signer_info = PKCS7_get_signer_info(p7);
@ -428,32 +395,31 @@ static int decode_rfc3161_response(PKCS7 *p7, BIO *bin, int verbose)
si = sk_PKCS7_SIGNER_INFO_value(signer_info, 0);
if (!si)
return 1; /* FAILED */
reply = ASN1_item_d2i_bio(ASN1_ITEM_rptr(TimeStampResp), bin, NULL);
if (!reply || !reply->status)
if (!response)
return 1; /* FAILED */
if (ASN1_INTEGER_get(reply->status->status) != 0) {
status = TS_RESP_get_status_info(response);
if (ASN1_INTEGER_get(TS_STATUS_INFO_get0_status(status)) != 0) {
if (verbose) {
printf("Timestamping failed: status %ld\n", ASN1_INTEGER_get(reply->status->status));
for (i = 0; i < sk_ASN1_UTF8STRING_num(reply->status->statusString); i++) {
ASN1_UTF8STRING *status = sk_ASN1_UTF8STRING_value(reply->status->statusString, i);
printf("%s\n", ASN1_STRING_get0_data(status));
const STACK_OF(ASN1_UTF8STRING) *reasons = TS_STATUS_INFO_get0_text(status);
printf("Timestamping failed: status %ld\n", ASN1_INTEGER_get(TS_STATUS_INFO_get0_status(status)));
for (i = 0; i < sk_ASN1_UTF8STRING_num(reasons); i++) {
ASN1_UTF8STRING *reason = sk_ASN1_UTF8STRING_value(reasons, i);
printf("%s\n", ASN1_STRING_get0_data(reason));
}
}
TimeStampResp_free(reply);
return 1; /* FAILED */
}
if (((len = i2d_PKCS7(reply->token, NULL)) <= 0) || (p = OPENSSL_malloc((size_t)len)) == NULL) {
token = TS_RESP_get_token(response);
if (((len = i2d_PKCS7(token, NULL)) <= 0) || (p = OPENSSL_malloc((size_t)len)) == NULL) {
if (verbose) {
printf("Failed to convert pkcs7: %d\n", len);
ERR_print_errors_fp(stdout);
}
TimeStampResp_free(reply);
return 1; /* FAILED */
}
len = i2d_PKCS7(reply->token, &p);
len = i2d_PKCS7(token, &p);
p -= len;
TimeStampResp_free(reply);
attrs = sk_X509_ATTRIBUTE_new_null();
attrs = X509at_add1_attr_by_txt(&attrs, SPC_RFC3161_OBJID, V_ASN1_SET, p, len);
@ -465,26 +431,22 @@ static int decode_rfc3161_response(PKCS7 *p7, BIO *bin, int verbose)
}
/*
* Decode an authenticode response from BIO.
* If successful the authenticode timestamp will be written into
* the PKCS7 SignerInfo structure as an unauthorized attribute - cont[1]:
* p7->d.sign->signer_info->unauth_attr
* [in, out] p7: new PKCS#7 signature
* [in] bin: base64 BIO with http data
* [in] resp: PKCS#7 authenticode response
* [in] verbose: additional output mode
* [returns] 1 on error or 0 on success
*/
static int decode_authenticode_response(PKCS7 *p7, BIO *b64_bin, int verbose)
static int attach_authenticode_response(PKCS7 *p7, PKCS7 *resp, int verbose)
{
PKCS7 *resp;
PKCS7_SIGNER_INFO *info, *si;
STACK_OF(X509_ATTRIBUTE) *attrs;
u_char *p;
int len, i;
STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
resp = d2i_PKCS7_bio(b64_bin, NULL);
if (!resp) {
return 1; /* FAILED */
}
@ -689,15 +651,25 @@ static int add_timestamp(PKCS7 *p7, FILE_FORMAT_CTX *ctx, char *url, int rfc3161
if (bin) {
if (rfc3161) {
res = decode_rfc3161_response(p7, bin, verbose);
/* decode a RFC 3161 response from BIO */
TS_RESP *response = d2i_TS_RESP_bio(bin, NULL);
BIO_free_all(bin);
res = attach_rfc3161_response(p7, response, verbose);
TS_RESP_free(response);
} else {
BIO *b64 = BIO_new(BIO_f_base64());
/* decode an authenticode response from BIO */
PKCS7 *response;
BIO *b64, *b64_bin;
b64 = BIO_new(BIO_f_base64());
if (!blob_has_nl)
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
BIO *b64_bin = BIO_push(b64, bin);
res = decode_authenticode_response(p7, b64_bin, verbose);
b64_bin = BIO_push(b64, bin);
response = d2i_PKCS7_bio(b64_bin, NULL);
BIO_free_all(b64_bin);
res = attach_authenticode_response(p7, response, verbose);
}
if (res && verbose) {
if (http_code != -1) {
@ -744,6 +716,240 @@ static int add_timestamp_rfc3161(PKCS7 *p7, FILE_FORMAT_CTX *ctx)
}
#endif /* ENABLE_CURL */
/*
* [in] resp_ctx: a response context that can be used for generating responses
* [in] data: unused
* [returns] hexadecimal serial number
*/
static ASN1_INTEGER *serial_cb(TS_RESP_CTX *resp_ctx, void *data)
{
int ret = 0;
uint64_t buf;
ASN1_INTEGER *serial = NULL;
/* squash unused parameter warning */
(void)data;
if (RAND_bytes((unsigned char *)&buf, sizeof buf) <= 0) {
printf("RAND_bytes failed\n");
goto out;
}
serial = ASN1_INTEGER_new();
if (!serial)
goto out;
ASN1_INTEGER_set_uint64(serial, buf);
ret = 1;
out:
if (!ret) {
TS_RESP_CTX_set_status_info(resp_ctx, TS_STATUS_REJECTION,
"Error during serial number generation.");
TS_RESP_CTX_add_failure_info(resp_ctx, TS_INFO_ADD_INFO_NOT_AVAILABLE);
ASN1_INTEGER_free(serial);
return NULL; /* FAILED */
}
return serial;
}
/*
* This must return the seconds and microseconds since Jan 1, 1970 in the sec
* and usec variables allocated by the caller.
* [in] resp_ctx: a response context that can be used for generating responses
* [in] data: timestamping time
* [out] sec: total of seconds since Jan 1, 1970
* [out] usec: microseconds (unused)
* [returns] 0 on error or 1 on success
*/
static int time_cb(TS_RESP_CTX *resp_ctx, void *data, long *sec, long *usec)
{
time_t *time = (time_t *)data;
if(!*time) {
TS_RESP_CTX_set_status_info(resp_ctx, TS_STATUS_REJECTION,
"Time is not available.");
TS_RESP_CTX_add_failure_info(resp_ctx, TS_INFO_TIME_NOT_AVAILABLE);
return 0; /* FAILED */
}
*sec = (long int)*time;
*usec = 0;
return 1; /* OK */
}
/*
* [in] ctx: structure holds input and output data
* [in] signer_cert: the signer certificate of the TSA in PEM format
* [in] signer_key: the private key of the TSA in PEM format
* [in] chain: the certificate chain that will all be included in the response
* [in] bout: timestamp request
* [returns] RFC3161 response
*/
static TS_RESP *get_rfc3161_response(FILE_FORMAT_CTX *ctx, X509 *signer_cert,
EVP_PKEY *signer_key, STACK_OF(X509) *chain, BIO *bout)
{
TS_RESP_CTX *resp_ctx = NULL;
TS_RESP *response = NULL;
ASN1_OBJECT *policy_obj = NULL;
resp_ctx = TS_RESP_CTX_new();
if (!resp_ctx)
goto out;
TS_RESP_CTX_set_serial_cb(resp_ctx, serial_cb, NULL);
if (!TS_RESP_CTX_set_signer_cert(resp_ctx, signer_cert)) {
goto out;
}
if (!TS_RESP_CTX_set_signer_key(resp_ctx, signer_key)) {
goto out;
}
if (!TS_RESP_CTX_set_certs(resp_ctx, chain)) {
goto out;
}
/* message digest algorithm that the TSA accepts */
if (!TS_RESP_CTX_add_md(resp_ctx, ctx->options->md)) {
goto out;
}
/* signing digest to use */
if (!TS_RESP_CTX_set_signer_digest(resp_ctx, ctx->options->md)) {
goto out;
}
/* default policy to use when the request does not mandate any policy
* tsa_policy1 = 1.2.3.4.1 */
policy_obj = OBJ_txt2obj(TSA_POLICY1, 0);
if (!policy_obj) {
goto out;
}
if (!TS_RESP_CTX_set_def_policy(resp_ctx, policy_obj)) {
goto out;
}
/* the accuracy of the time source of the TSA in seconds, milliseconds
* and microseconds; e.g. secs:1, millisecs:500, microsecs:100;
* 0 means not specified */
if (!TS_RESP_CTX_set_accuracy(resp_ctx, 1, 500, 100)) {
goto out;
}
if (ctx->options->tsa_time) {
TS_RESP_CTX_set_time_cb(resp_ctx, time_cb, &(ctx->options->tsa_time));
}
/* generate RFC3161 response with embedded TS_TST_INFO structure */
response = TS_RESP_create_response(resp_ctx, bout);
if (!response) {
printf("Failed to create RFC3161 response\n");
}
out:
ASN1_OBJECT_free(policy_obj);
TS_RESP_CTX_free(resp_ctx);
return response;
}
/*
* [in] bin: certfile BIO
* [in] certpass: NULL
* [returns] pointer to STACK_OF(X509) structure
*/
static STACK_OF(X509) *X509_chain_read_certs(BIO *bin, char *certpass)
{
STACK_OF(X509) *certs = sk_X509_new_null();
X509 *x509;
(void)BIO_seek(bin, 0);
x509 = PEM_read_bio_X509(bin, NULL, NULL, certpass);
while (x509) {
sk_X509_push(certs, x509);
x509 = PEM_read_bio_X509(bin, NULL, NULL, certpass);
}
ERR_clear_error();
if (!sk_X509_num(certs)) {
sk_X509_free(certs);
return NULL;
}
return certs;
}
/*
* [in, out] p7: new PKCS#7 signature
* [in] ctx: structure holds input and output data
* [returns] 1 on error or 0 on success
*/
static int add_timestamp_builtin(PKCS7 *p7, FILE_FORMAT_CTX *ctx)
{
BIO *btmp, *bout;
STACK_OF(X509) *chain;
X509 *signer_cert = NULL;
EVP_PKEY *signer_key;
TS_RESP *response = NULL;
int i, res = 1;
btmp = BIO_new_file(ctx->options->tsa_certfile, "rb");
if (!btmp) {
printf("Failed to read Time-Stamp Authority certificate file: %s\n", ctx->options->tsa_certfile);
return 0; /* FAILED */
}
/* .pem certificate file */
chain = X509_chain_read_certs(btmp, NULL);
BIO_free(btmp);
btmp = BIO_new_file(ctx->options->tsa_keyfile, "rb");
if (!btmp) {
printf("Failed to read private key file: %s\n", ctx->options->tsa_keyfile);
return 0; /* FAILED */
}
signer_key = PEM_read_bio_PrivateKey(btmp, NULL, NULL, NULL);
BIO_free(btmp);
if(!chain || !signer_key) {
printf("Failed to load Time-Stamp Authority crypto parameters\n");
return 0; /* FAILED */
}
/* find the signer's certificate located somewhere in the whole certificate chain */
for (i=0; i<sk_X509_num(chain); i++) {
X509 *cert = sk_X509_value(chain, i);
if (X509_check_private_key(cert, signer_key)) {
signer_cert = cert;
break;
}
}
if(!signer_cert) {
printf("Failed to checking the consistency of a TSA private key with a public key in any X509 certificate\n");
goto out;
}
/* The TSA signing certificate must have exactly one extended key usage
* assigned to it: timeStamping. The extended key usage must also be critical,
* otherwise the certificate is going to be refused. */
/* check X509_PURPOSE_TIMESTAMP_SIGN certificate purpose */
if (X509_check_purpose(signer_cert, X509_PURPOSE_TIMESTAMP_SIGN, 0) != 1) {
printf("Unsupported TSA signer's certificate purpose X509_PURPOSE_TIMESTAMP_SIGN\n");
goto out;
}
/* check extended key usage flag XKU_TIMESTAMP */
if (!(X509_get_extended_key_usage(signer_cert) & XKU_TIMESTAMP)) {
printf("Unsupported Signer's certificate purpose XKU_TIMESTAMP\n");
goto out;
}
/* encode timestamp request */
bout = bio_encode_rfc3161_request(p7, ctx->options->md);
if (!bout) {
printf("Failed to encode timestamp request\n");
goto out;
}
response = get_rfc3161_response(ctx, signer_cert, signer_key, chain, bout);
BIO_free_all(bout);
if (response) {
res = attach_rfc3161_response(p7, response, ctx->options->verbose);
if (res) {
printf("Failed to convert timestamp reply\n");
ERR_print_errors_fp(stdout);
}
} else {
printf("Failed to obtain RFC3161 response\n");
}
out:
sk_X509_pop_free(chain, X509_free);
EVP_PKEY_free(signer_key);
TS_RESP_free(response);
return res;
}
/*
* If successful the unauthenticated blob will be written into
* the PKCS7 SignerInfo structure as an unauthorized attribute - cont[1]:
@ -801,6 +1007,10 @@ static int add_timestamp_and_blob(PKCS7 *p7, FILE_FORMAT_CTX *ctx)
"Use the \"-t\" option to add the Authenticode Time-Stamp Authority or choose another one RFC3161 Time-Stamp Authority");
return 1; /* FAILED */
}
if (ctx->options->tsa_certfile && ctx->options->tsa_keyfile && add_timestamp_builtin(p7, ctx)) {
printf("Built-in timestamping failed\n");
return 1; /* FAILED */
}
#endif /* ENABLE_CURL */
if (ctx->options->addBlob && !add_unauthenticated_blob(p7)) {
printf("Adding unauthenticated blob failed\n");
@ -1224,6 +1434,20 @@ static STACK_OF(X509_CRL) *x509_crl_list_get(PKCS7 *p7, X509_CRL *crl)
return crls;
}
static void print_timestamp_serial_number(TS_TST_INFO *token)
{
BIGNUM *serialbn;
char *number;
if (!token)
return;
serialbn = ASN1_INTEGER_to_BN(TS_TST_INFO_get_serial(token), NULL);
number = BN_bn2hex(serialbn);
printf("Timestamp serial number: %s\n", number);
BN_free(serialbn);
OPENSSL_free(number);
}
/*
* Compare the hash provided from the TSTInfo object against the hash computed
* from the signature created by the signing certificate's private key
@ -1235,35 +1459,42 @@ static int verify_timestamp_token(PKCS7 *p7, CMS_ContentInfo *timestamp)
{
STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
PKCS7_SIGNER_INFO *si;
ASN1_OCTET_STRING *hash, **pos;
TimeStampToken *token = NULL;
const u_char *p = NULL;
u_char mdbuf[EVP_MAX_MD_SIZE];
const EVP_MD *md;
int md_nid;
BIO *bhash;
ASN1_OCTET_STRING **pos;
signer_info = PKCS7_get_signer_info(p7);
if (!signer_info)
return 0; /* FAILED */
si = sk_PKCS7_SIGNER_INFO_value(signer_info, 0);
if (!si)
return 0; /* FAILED */
/* get the embedded content */
pos = CMS_get0_content(timestamp);
if (pos != NULL && *pos != NULL) {
p = (*pos)->data;
token = d2i_TimeStampToken(NULL, &p, (*pos)->length);
const u_char *p = (*pos)->data;
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, (*pos)->length);
if (token) {
/* compute a hash from the encrypted message digest value of the file */
md_nid = OBJ_obj2nid(token->messageImprint->digestAlgorithm->algorithm);
BIO *bhash;
u_char mdbuf[EVP_MAX_MD_SIZE];
ASN1_OCTET_STRING *hash;
const ASN1_OBJECT *aoid;
int md_nid;
const EVP_MD *md;
TS_MSG_IMPRINT *msg_imprint = TS_TST_INFO_get_msg_imprint(token);
const X509_ALGOR *alg = TS_MSG_IMPRINT_get_algo(msg_imprint);
X509_ALGOR_get0(&aoid, NULL, NULL, alg);
md_nid = OBJ_obj2nid(aoid);
md = EVP_get_digestbynid(md_nid);
/* compute a hash from the encrypted message digest value of the file */
bhash = BIO_new(BIO_f_md());
if (!BIO_set_md(bhash, md)) {
printf("Unable to set the message digest of BIO\n");
BIO_free_all(bhash);
return 0; /* FAILED */
TS_TST_INFO_free(token);
return 0; /* FAILED */
}
BIO_push(bhash, BIO_new(BIO_s_null()));
BIO_write(bhash, si->enc_digest->data, si->enc_digest->length);
@ -1271,23 +1502,25 @@ static int verify_timestamp_token(PKCS7 *p7, CMS_ContentInfo *timestamp)
BIO_free_all(bhash);
/* compare the provided hash against the computed hash */
hash = token->messageImprint->digest;
/* hash->length == EVP_MD_size(md) */
hash =TS_MSG_IMPRINT_get_msg(msg_imprint);
if (memcmp(mdbuf, hash->data, (size_t)hash->length)) {
printf("Hash value mismatch:\n\tMessage digest algorithm: %s\n",
(md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid));
print_hash("\tComputed message digest", "", mdbuf, EVP_MD_size(md));
print_hash("\tReceived message digest", "", hash->data, hash->length);
printf("\nFile's message digest verification: failed\n");
TimeStampToken_free(token);
TS_TST_INFO_free(token);
return 0; /* FAILED */
} /* else Computed and received message digests matched */
TimeStampToken_free(token);
print_timestamp_serial_number(token);
TS_TST_INFO_free(token);
} else
/* our CMS_ContentInfo struct created for Authenticode Timestamp
* does not contain any TimeStampToken as specified in RFC 3161 */
* does not contain any TS_TST_INFO struct as specified in RFC 3161 */
ERR_clear_error();
}
return 1; /* OK */
}
@ -1353,8 +1586,7 @@ static int verify_timestamp(FILE_FORMAT_CTX *ctx, PKCS7 *p7, CMS_ContentInfo *ti
crl = x509_crl_get(url);
OPENSSL_free(url);
if (!crl && !ctx->options->tsa_crlfile) {
printf("Use the \"-TSA-CRLfile\" option to add one or more Time-Stamp Authority CRLs in PEM format.\n\n");
goto out;
printf("Use the \"-TSA-CRLfile\" option to add one or more Time-Stamp Authority CRLs in PEM format.\n");
}
}
#endif /* ENABLE_CURL */
@ -1465,7 +1697,7 @@ static int verify_authenticode(FILE_FORMAT_CTX *ctx, PKCS7 *p7, time_t time, X50
crl = x509_crl_get(url);
OPENSSL_free(url);
if (!crl && !ctx->options->crlfile) {
printf("Use the \"-CRLfile\" option to add one or more CRLs in PEM format.\n\n");
printf("Use the \"-CRLfile\" option to add one or more CRLs in PEM format.\n");
goto out;
}
}
@ -1878,20 +2110,16 @@ static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si)
*/
static time_t time_t_get_cms_time(CMS_ContentInfo *cms)
{
ASN1_OCTET_STRING **pos;
const u_char *p = NULL;
TimeStampToken *token = NULL;
ASN1_GENERALIZEDTIME *asn1_time = NULL;
time_t posix_time = INVALID_TIME;
ASN1_OCTET_STRING **pos = CMS_get0_content(cms);
pos = CMS_get0_content(cms);
if (pos != NULL && *pos != NULL) {
p = (*pos)->data;
token = d2i_TimeStampToken(NULL, &p, (*pos)->length);
const u_char *p = (*pos)->data;
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, (*pos)->length);
if (token) {
asn1_time = token->time;
const ASN1_GENERALIZEDTIME *asn1_time = TS_TST_INFO_get_time(token);
posix_time = time_t_get_asn1_time(asn1_time);
TimeStampToken_free(token);
TS_TST_INFO_free(token);
}
}
return posix_time;
@ -1899,7 +2127,7 @@ static time_t time_t_get_cms_time(CMS_ContentInfo *cms)
/*
* Create new CMS_ContentInfo struct for Authenticode Timestamp.
* This struct does not contain any TimeStampToken as specified in RFC 3161.
* This struct does not contain any TS_TST_INFO as specified in RFC 3161.
* [in] p7_signed: PKCS#7 signedData structure
* [in] countersignature: Authenticode Timestamp decoded to PKCS7_SIGNER_INFO
* [returns] pointer to CMS_ContentInfo structure
@ -2002,6 +2230,10 @@ static int verify_member(FILE_FORMAT_CTX *ctx, CatalogAuthAttr *attribute)
printf("Failed to extract current message digest\n\n");
return 1; /* FAILED */
}
if (!ctx->format->digest_calc) {
printf("Unsupported method: digest_calc\n");
return 1; /* FAILED */
}
md = EVP_get_digestbynid(mdtype);
cmdbuf = ctx->format->digest_calc(ctx, md);
if (!cmdbuf) {
@ -2038,6 +2270,7 @@ static int verify_member(FILE_FORMAT_CTX *ctx, CatalogAuthAttr *attribute)
static int verify_content(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
{
ASN1_STRING *value;
ASN1_OBJECT *indir_objid;
const u_char *data;
MsCtlContent *ctlc;
int i, j;
@ -2053,7 +2286,7 @@ static int verify_content(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
printf("Failed to extract MS_CTL_OBJID data\n");
return 1; /* FAILED */
}
ASN1_OBJECT *indir_objid = OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1);
indir_objid = OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1);
for (i = 0; i < sk_CatalogInfo_num(ctlc->header_attributes); i++) {
STACK_OF(CatalogAuthAttr) *attributes;
CatalogInfo *header_attr = sk_CatalogInfo_value(ctlc->header_attributes, i);
@ -2220,6 +2453,11 @@ static int verify_signed_file(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options)
STACK_OF(PKCS7) *signatures;
int detached = options->catalog ? 1 : 0;
if (!ctx->format->check_file) {
printf("Unsupported method: check_file\n");
return 1; /* FAILED */
}
if (!ctx->format->check_file(ctx, detached))
return 1; /* FAILED */
@ -2238,10 +2476,18 @@ static int verify_signed_file(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options)
printf("CAT file initialization error\n");
return 1; /* Failed */
}
if (!cat_ctx->format->pkcs7_extract) {
printf("Unsupported command: extract-signature\n");
return 1; /* FAILED */
}
p7 = cat_ctx->format->pkcs7_extract(cat_ctx);
cat_ctx->format->ctx_cleanup(cat_ctx, NULL, NULL);
OPENSSL_free(cat_options);
} else {
if (!ctx->format->pkcs7_extract) {
printf("Unsupported command: extract-signature\n");
return 1; /* FAILED */
}
p7 = ctx->format->pkcs7_extract(ctx);
}
if (!p7) {
@ -2262,9 +2508,14 @@ static int verify_signed_file(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options)
} else {
printf("Catalog verification: failed\n\n");
}
} else if (ctx->format->verify_digests(ctx, sig)) {
printf("Signature Index: %d %s\n", i, i==0 ? " (Primary Signature)" : "");
ret &= verify_signature(ctx, sig);
} else if (ctx->format->verify_digests) {
if (ctx->format->verify_digests(ctx, sig)) {
printf("Signature Index: %d %s\n", i, i==0 ? " (Primary Signature)" : "");
ret &= verify_signature(ctx, sig);
}
} else {
printf("Unsupported method: verify_digests\n");
return 1; /* FAILED */
}
}
printf("Number of verified signatures: %d\n", i);
@ -2320,6 +2571,8 @@ static int check_attached_data(GLOBAL_OPTIONS *options)
ctx = file_format_pe.ctx_new(tmp_options, NULL, NULL);
if (!ctx)
ctx = file_format_cab.ctx_new(tmp_options, NULL, NULL);
if (!ctx)
ctx = file_format_appx.ctx_new(tmp_options, NULL, NULL);
if (!ctx)
ctx = file_format_cat.ctx_new(tmp_options, NULL, NULL);
if (!ctx) {
@ -2419,6 +2672,8 @@ static void usage(const char *argv0, const char *cmd)
printf("%12s[ -t <timestampurl> [ -t ... ] [ -p <proxy> ] [ -noverifypeer ]\n", "");
printf("%12s[ -ts <timestampurl> [ -ts ... ] [ -p <proxy> ] [ -noverifypeer ] ]\n", "");
#endif /* ENABLE_CURL */
printf("%12s[ -TSA-certs <TSA-certfile> ] [ -TSA-key <TSA-keyfile> ]\n", "");
printf("%12s[ -TSA-time <unix-time> ]\n", "");
printf("%12s[ -time <unix-time> ]\n", "");
printf("%12s[ -addUnauthenticatedBlob ]\n", "");
printf("%12s[ -nest ]\n", "");
@ -2432,6 +2687,8 @@ static void usage(const char *argv0, const char *cmd)
printf("%12s[ -t <timestampurl> [ -t ... ] [ -p <proxy> ] [ -noverifypeer ]\n", "");
printf("%12s[ -ts <timestampurl> [ -ts ... ] [ -p <proxy> ] [ -noverifypeer ] ]\n", "");
#endif /* ENABLE_CURL */
printf("%12s[ -TSA-certs <TSA-certfile> ] [ -TSA-key <TSA-keyfile> ]\n", "");
printf("%12s[ -TSA-time <unix-time> ]\n", "");
printf("%12s[ -h {md5,sha1,sha2(56),sha384,sha512} ]\n", "");
printf("%12s[ -verbose ]\n", "");
printf("%12s[ -add-msi-dse ]\n", "");
@ -2529,6 +2786,9 @@ static void help_for(const char *argv0, const char *cmd)
const char *cmds_ts[] = {"add", "sign", NULL};
#endif /* ENABLE_CURL */
const char *cmds_CAfileTSA[] = {"attach-signature", "verify", NULL};
const char *cmds_certsTSA[] = {"add", "sign", NULL};
const char *cmds_keyTSA[] = {"add", "sign", NULL};
const char *cmds_timeTSA[] = {"add", "sign", NULL};
const char *cmds_verbose[] = {"add", "sign", "verify", NULL};
if (on_list(cmd, cmds_all)) {
@ -2678,34 +2938,17 @@ static void help_for(const char *argv0, const char *cmd)
printf("%-24s= the file containing one or more Time-Stamp Authority certificates in PEM format\n", "-TSA-CAfile");
if (on_list(cmd, cmds_CRLfileTSA))
printf("%-24s= the file containing one or more Time-Stamp Authority CRLs in PEM format\n", "-TSA-CRLfile");
if (on_list(cmd, cmds_certsTSA))
printf("%-24s= Time-Stamp Authority signing certificate\n", "-TSA-certs");
if (on_list(cmd, cmds_keyTSA))
printf("%-24s= Time-Stamp Authority private key or PKCS#11 URI identifies a key in the token\n", "-TSA-key");
if (on_list(cmd, cmds_timeTSA))
printf("%-24s= the unix-time to set the Time-Stamp Authority signing\n", "-TSA-time");
if (on_list(cmd, cmds_verbose))
printf("%-24s= include additional output in the log\n", "-verbose");
usage(argv0, cmd);
}
/*
* [in] bin: certfile BIO
* [in] certpass: NULL
* [returns] pointer to STACK_OF(X509) structure
*/
static STACK_OF(X509) *X509_chain_read_certs(BIO *bin, char *certpass)
{
STACK_OF(X509) *certs = sk_X509_new_null();
X509 *x509;
(void)BIO_seek(bin, 0);
x509 = PEM_read_bio_X509(bin, NULL, NULL, certpass);
while (x509) {
sk_X509_push(certs, x509);
x509 = PEM_read_bio_X509(bin, NULL, NULL, certpass);
}
ERR_clear_error();
if (!sk_X509_num(certs)) {
sk_X509_free(certs);
return NULL;
}
return certs;
}
#ifdef PROVIDE_ASKPASS
/*
* [in] prompt: "Password: "
@ -3091,7 +3334,7 @@ static ENGINE *engine_dynamic(GLOBAL_OPTIONS *options)
* [in] none
* [returns] pointer to ENGINE
*/
static ENGINE *engine_pkcs11()
static ENGINE *engine_pkcs11(void)
{
ENGINE *engine = ENGINE_by_id("pkcs11");
if (!engine) {
@ -3586,6 +3829,24 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
return 0; /* FAILED */
}
options->leafhash = (*++argv);
} else if ((cmd == CMD_SIGN || cmd == CMD_ADD) && !strcmp(*argv, "-TSA-certs")) {
if (--argc < 1) {
usage(argv0, "all");
return 0; /* FAILED */
}
options->tsa_certfile = *(++argv);
} else if ((cmd == CMD_SIGN || cmd == CMD_ADD) && !strcmp(*argv, "-TSA-key")) {
if (--argc < 1) {
usage(argv0, "all");
return 0; /* FAILED */
}
options->tsa_keyfile = *(++argv);
} else if ((cmd == CMD_SIGN || cmd == CMD_ADD) && !strcmp(*argv, "-TSA-time")) {
if (--argc < 1) {
usage(argv0, "all");
return 0; /* FAILED */
}
options->tsa_time = (time_t)strtoull(*(++argv), NULL, 10);
} else if ((cmd == CMD_ADD) && !strcmp(*argv, "--help")) {
help_for(argv0, "add");
cmd = CMD_HELP;
@ -3651,6 +3912,8 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
if (argc > 0 ||
#ifdef ENABLE_CURL
(options->nturl && options->ntsurl) ||
(options->nturl && options->tsa_certfile && options->tsa_keyfile) ||
(options->ntsurl && options->tsa_certfile && options->tsa_keyfile) ||
#endif
!options->infile ||
(cmd != CMD_VERIFY && !options->outfile) ||
@ -3733,6 +3996,8 @@ int main(int argc, char **argv)
ctx = file_format_pe.ctx_new(&options, hash, outdata);
if (!ctx)
ctx = file_format_cab.ctx_new(&options, hash, outdata);
if (!ctx)
ctx = file_format_appx.ctx_new(&options, hash, outdata);
if (!ctx)
ctx = file_format_cat.ctx_new(&options, hash, outdata);
if (!ctx) {
@ -3744,7 +4009,10 @@ int main(int argc, char **argv)
if (options.cmd == CMD_VERIFY) {
ret = verify_signed_file(ctx, &options);
goto skip_signing;
} else if (options.cmd == CMD_EXTRACT && ctx->format->pkcs7_extract) {
} else if (options.cmd == CMD_EXTRACT) {
if (!ctx->format->pkcs7_extract) {
DO_EXIT_0("Unsupported command: extract-signature\n");
}
p7 = ctx->format->pkcs7_extract(ctx);
if (!p7) {
DO_EXIT_0("Unable to extract existing signature\n");
@ -3752,7 +4020,10 @@ int main(int argc, char **argv)
ret = save_extracted_pkcs7(ctx, outdata, p7);
PKCS7_free(p7);
goto skip_signing;
} else if (options.cmd == CMD_REMOVE && ctx->format->remove_pkcs7) {
} else if (options.cmd == CMD_REMOVE) {
if (!ctx->format->remove_pkcs7) {
DO_EXIT_0("Unsupported command: remove-signature\n");
}
ret = ctx->format->remove_pkcs7(ctx, hash, outdata);
if (ctx->format->update_data_size) {
ctx->format->update_data_size(ctx, outdata, NULL);

View File

@ -60,7 +60,9 @@
#if OPENSSL_VERSION_NUMBER>=0x30000000L
#include <openssl/provider.h>
#endif /* OPENSSL_VERSION_NUMBER>=0x30000000L */
#include <openssl/rand.h>
#include <openssl/safestack.h>
#include <openssl/ts.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h> /* X509_PURPOSE */
@ -210,6 +212,9 @@
#define DO_EXIT_1(x, y) { printf(x, y); goto err_cleanup; }
#define DO_EXIT_2(x, y, z) { printf(x, y, z); goto err_cleanup; }
/* Default policy if request did not specify it. */
#define TSA_POLICY1 "1.2.3.4.1"
typedef enum {
CMD_SIGN,
CMD_EXTRACT,
@ -278,6 +283,9 @@ typedef struct {
cmd_type_t cmd;
char *indata;
PKCS7 *prevsig;
char *tsa_certfile;
char *tsa_keyfile;
time_t tsa_time;
} GLOBAL_OPTIONS;
/*
@ -458,6 +466,7 @@ typedef struct msi_ctx_st MSI_CTX;
typedef struct pe_ctx_st PE_CTX;
typedef struct cab_ctx_st CAB_CTX;
typedef struct cat_ctx_st CAT_CTX;
typedef struct appx_ctx_st APPX_CTX;
typedef struct {
FILE_FORMAT *format;
@ -467,6 +476,7 @@ typedef struct {
PE_CTX *pe_ctx;
CAB_CTX *cab_ctx;
CAT_CTX *cat_ctx;
APPX_CTX *appx_ctx;
};
} FILE_FORMAT_CTX;
@ -474,10 +484,12 @@ extern FILE_FORMAT file_format_msi;
extern FILE_FORMAT file_format_pe;
extern FILE_FORMAT file_format_cab;
extern FILE_FORMAT file_format_cat;
extern FILE_FORMAT file_format_appx;
struct file_format_st {
FILE_FORMAT_CTX *(*ctx_new) (GLOBAL_OPTIONS *option, BIO *hash, BIO *outdata);
ASN1_OBJECT *(*data_blob_get) (u_char **p, int *plen, FILE_FORMAT_CTX *ctx);
int (*hash_length_get) (FILE_FORMAT_CTX *ctx);
int (*check_file) (FILE_FORMAT_CTX *ctx, int detached);
u_char *(*digest_calc) (FILE_FORMAT_CTX *ctx, const EVP_MD *md);
int (*verify_digests) (FILE_FORMAT_CTX *ctx, PKCS7 *p7);

13
pe.c
View File

@ -44,6 +44,7 @@ struct pe_ctx_st {
/* FILE_FORMAT method prototypes */
static FILE_FORMAT_CTX *pe_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *outdata);
static ASN1_OBJECT *pe_spc_image_data_get(u_char **p, int *plen, FILE_FORMAT_CTX *ctx);
static int pe_hash_length_get(FILE_FORMAT_CTX *ctx);
static int pe_check_file(FILE_FORMAT_CTX *ctx, int detached);
static u_char *pe_digest_calc(FILE_FORMAT_CTX *ctx, const EVP_MD *md);
static int pe_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7);
@ -59,6 +60,7 @@ static void pe_ctx_cleanup(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata);
FILE_FORMAT file_format_pe = {
.ctx_new = pe_ctx_new,
.data_blob_get = pe_spc_image_data_get,
.hash_length_get = pe_hash_length_get,
.check_file = pe_check_file,
.digest_calc = pe_digest_calc,
.verify_digests = pe_verify_digests,
@ -168,6 +170,15 @@ static ASN1_OBJECT *pe_spc_image_data_get(u_char **p, int *plen, FILE_FORMAT_CTX
return dtype; /* OK */
}
/*
* [in] ctx: structure holds input and output data
* [returns] the size of the message digest when passed an EVP_MD structure (the size of the hash)
*/
static int pe_hash_length_get(FILE_FORMAT_CTX *ctx)
{
return EVP_MD_size(ctx->options->md);
}
/*
* Print current and calculated PE checksum,
* check if the signature exists.
@ -919,7 +930,7 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
/* NumberOfSections indicates the size of the section table,
* which immediately follows the headers, can be up to 65535 under Vista and later */
nsections = GET_UINT16_LE(ctx->options->indata + ctx->pe_ctx->header_size + 6);
if (nsections == 0 || nsections > UINT16_MAX) {
if (nsections == 0) {
printf("Corrupted number of sections: 0x%08X\n", nsections);
return NULL; /* FAILED */
}

BIN
tests/files/unsigned.256appx Executable file

Binary file not shown.

BIN
tests/files/unsigned.512appx Executable file

Binary file not shown.