@@ -237,6 +237,7 @@ struct appx_ctx_st {
237237 u_char * existingDataHash ;
238238 u_char * existingCIHash ;
239239 int isBundle ;
240+ int isAppxBlockMap ;
240241 const EVP_MD * md ;
241242 int hashlen ;
242243} appx_ctx_t ;
@@ -282,7 +283,8 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con
282283static int appx_compare_hashes (FILE_FORMAT_CTX * ctx );
283284static int appx_remove_ct_signature_entry (ZIP_FILE * zip , ZIP_CENTRAL_DIRECTORY_ENTRY * entry );
284285static int appx_append_ct_signature_entry (ZIP_FILE * zip , ZIP_CENTRAL_DIRECTORY_ENTRY * entry );
285- static const EVP_MD * appx_get_md (ZIP_FILE * zip );
286+ static const EVP_MD * appx_get_md (FILE_FORMAT_CTX * ctx , const EVP_MD * md , ZIP_FILE * zip );
287+ static const EVP_MD * appx_get_md_from_signature (FILE_FORMAT_CTX * ctx );
286288static ZIP_CENTRAL_DIRECTORY_ENTRY * zipGetCDEntryByName (ZIP_FILE * zip , const char * name );
287289static void zipWriteCentralDirectoryEntry (BIO * bio , uint64_t * sizeOnDisk , ZIP_CENTRAL_DIRECTORY_ENTRY * entry , uint64_t offsetDiff );
288290static int zipAppendSignatureFile (BIO * bio , ZIP_FILE * zip , uint8_t * data , uint64_t dataSize );
@@ -332,7 +334,6 @@ static void bioAddU16(BIO *bio, uint16_t v);
332334static FILE_FORMAT_CTX * appx_ctx_new (GLOBAL_OPTIONS * options , BIO * hash , BIO * outdata )
333335{
334336 FILE_FORMAT_CTX * ctx ;
335- const EVP_MD * md ;
336337 ZIP_FILE * zip = openZip (options -> infile );
337338
338339 /* squash unused parameter warnings */
@@ -345,22 +346,18 @@ static FILE_FORMAT_CTX *appx_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *ou
345346 if (options -> verbose ) {
346347 zipPrintCentralDirectory (zip );
347348 }
348- md = appx_get_md (zip );
349- if (!md ) {
350- freeZip (zip );
351- return NULL ; /* FAILED */
352- }
353349 ctx = OPENSSL_malloc (sizeof (FILE_FORMAT_CTX ));
354350 ctx -> appx_ctx = OPENSSL_zalloc (sizeof (appx_ctx_t ));
355351 ctx -> appx_ctx -> zip = zip ;
356352 ctx -> format = & file_format_appx ;
357353 ctx -> options = options ;
358- ctx -> appx_ctx -> md = md ;
354+ ctx -> appx_ctx -> md = appx_get_md ( ctx , options -> md , zip ) ;
359355 if (zipGetCDEntryByName (zip , APPXBUNDLE_MANIFEST_FILENAME )) {
360356 ctx -> appx_ctx -> isBundle = 1 ;
361357 }
362- if (options -> cmd == CMD_SIGN || options -> cmd == CMD_ATTACH
363- || options -> cmd == CMD_ADD || options -> cmd == CMD_EXTRACT_DATA ) {
358+ if (ctx -> appx_ctx -> isAppxBlockMap &&
359+ (options -> cmd == CMD_SIGN || options -> cmd == CMD_ATTACH
360+ || options -> cmd == CMD_ADD || options -> cmd == CMD_EXTRACT_DATA )) {
364361 printf ("Warning: Ignore -h option, use the hash algorithm specified in AppxBlockMap.xml\n" );
365362 }
366363 if (options -> pagehash == 1 )
@@ -513,7 +510,6 @@ static PKCS7 *appx_pkcs7_extract(FILE_FORMAT_CTX *ctx)
513510
514511 /* Check if the signature exists */
515512 if (!zipEntryExist (ctx -> appx_ctx -> zip , APP_SIGNATURE_FILENAME )) {
516- fprintf (stderr , "%s does not exist\n" , APP_SIGNATURE_FILENAME );
517513 return NULL ; /* FAILED */
518514 }
519515 dataSize = zipReadFileDataByName (& data , ctx -> appx_ctx -> zip , APP_SIGNATURE_FILENAME );
@@ -798,14 +794,17 @@ static BIO *appx_calculate_hashes(FILE_FORMAT_CTX *ctx)
798794{
799795 uint64_t cdOffset = 0 ;
800796
801- ctx -> appx_ctx -> calculatedBMHash = zipCalcDigest (ctx -> appx_ctx -> zip , BLOCK_MAP_FILENAME , ctx -> appx_ctx -> md );
797+ if (ctx -> appx_ctx -> isAppxBlockMap ) {
798+ ctx -> appx_ctx -> calculatedBMHash = zipCalcDigest (ctx -> appx_ctx -> zip , BLOCK_MAP_FILENAME , ctx -> appx_ctx -> md );
799+ }
802800 ctx -> appx_ctx -> calculatedCTHash = zipCalcDigest (ctx -> appx_ctx -> zip , CONTENT_TYPES_FILENAME , ctx -> appx_ctx -> md );
803801 ctx -> appx_ctx -> calculatedDataHash = appx_calc_zip_data_hash (& cdOffset , ctx -> appx_ctx -> zip , ctx -> appx_ctx -> md );
804802 ctx -> appx_ctx -> calculatedCDHash = appx_calc_zip_central_directory_hash (ctx -> appx_ctx -> zip , ctx -> appx_ctx -> md , cdOffset );
805803 ctx -> appx_ctx -> calculatedCIHash = zipCalcDigest (ctx -> appx_ctx -> zip , CODE_INTEGRITY_FILENAME , ctx -> appx_ctx -> md );
806804
807- if (!ctx -> appx_ctx -> calculatedBMHash || !ctx -> appx_ctx -> calculatedCTHash
808- || !ctx -> appx_ctx -> calculatedCDHash || !ctx -> appx_ctx -> calculatedDataHash ) {
805+ if ((ctx -> appx_ctx -> isAppxBlockMap && !ctx -> appx_ctx -> calculatedBMHash )
806+ || !ctx -> appx_ctx -> calculatedCTHash || !ctx -> appx_ctx -> calculatedCDHash
807+ || !ctx -> appx_ctx -> calculatedDataHash ) {
809808 fprintf (stderr , "One or more hashes calculation failed\n" );
810809 return NULL ; /* FAILED */
811810 }
@@ -843,18 +842,20 @@ static BIO *appx_hash_blob_get(FILE_FORMAT_CTX *ctx)
843842 pos += 4 ;
844843 memcpy (data + pos , ctx -> appx_ctx -> calculatedCTHash , (size_t )mdlen );
845844 pos += mdlen ;
846- memcpy (data + pos , AXBM_SIGNATURE , 4 );
847- pos += 4 ;
848- memcpy (data + pos , ctx -> appx_ctx -> calculatedBMHash , (size_t )mdlen );
849- pos += mdlen ;
845+ if (ctx -> appx_ctx -> calculatedBMHash ) {
846+ memcpy (data + pos , AXBM_SIGNATURE , 4 );
847+ pos += 4 ;
848+ memcpy (data + pos , ctx -> appx_ctx -> calculatedBMHash , (size_t )mdlen );
849+ pos += mdlen ;
850+ }
850851 if (ctx -> appx_ctx -> calculatedCIHash ) {
851852 memcpy (data + pos , AXCI_SIGNATURE , 4 );
852853 pos += 4 ;
853854 memcpy (data + pos , ctx -> appx_ctx -> calculatedCIHash , (size_t )mdlen );
854855 pos += mdlen ;
855856 }
856857 if (ctx -> options -> verbose ) {
857- print_hash ("Hash of file: " , "\n" , data , pos );
858+ print_hash ("Hash of file " , "\n" , data , pos );
858859 }
859860 ctx -> appx_ctx -> hashlen = BIO_write (hashes , data , pos );
860861 OPENSSL_free (data );
@@ -1081,9 +1082,11 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con
10811082 uint8_t * data = content -> messageDigest -> digest -> data ;
10821083 int mdlen = EVP_MD_size (ctx -> appx_ctx -> md );
10831084 int pos = 4 ;
1085+ int expected_hashes = ctx -> appx_ctx -> isAppxBlockMap ? 4 : 3 ;
10841086
1085- /* we are expecting at least 4 hashes + 4 byte header */
1086- if (length < 4 * mdlen + 4 ) {
1087+ /* Expecting 4 hashes + 4-byte header if isAppxBlockMap is set,
1088+ * otherwise 3 hashes + 4-byte header */
1089+ if (length < expected_hashes * mdlen + 4 ) {
10871090 fprintf (stderr , "Hash too short\n" );
10881091 return 0 ; /* FAILED */
10891092 }
@@ -1121,7 +1124,7 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con
11211124 fprintf (stderr , "Central directory hash missing\n" );
11221125 return 0 ; /* FAILED */
11231126 }
1124- if (!ctx -> appx_ctx -> existingBMHash ) {
1127+ if (ctx -> appx_ctx -> isAppxBlockMap && !ctx -> appx_ctx -> existingBMHash ) {
11251128 fprintf (stderr , "Block map hash missing\n" );
11261129 return 0 ; /* FAILED */
11271130 }
@@ -1145,14 +1148,16 @@ static int appx_compare_hashes(FILE_FORMAT_CTX *ctx)
11451148{
11461149 int mdtype = EVP_MD_nid (ctx -> appx_ctx -> md );
11471150
1148- if (ctx -> appx_ctx -> calculatedBMHash && ctx -> appx_ctx -> existingBMHash ) {
1149- printf ("Checking Block Map hashes:\n" );
1150- if (!compare_digests (ctx -> appx_ctx -> existingBMHash , ctx -> appx_ctx -> calculatedBMHash , mdtype )) {
1151+ if (ctx -> appx_ctx -> isAppxBlockMap ) {
1152+ if (ctx -> appx_ctx -> calculatedBMHash && ctx -> appx_ctx -> existingBMHash ) {
1153+ printf ("Checking Block Map hashes:\n" );
1154+ if (!compare_digests (ctx -> appx_ctx -> existingBMHash , ctx -> appx_ctx -> calculatedBMHash , mdtype )) {
1155+ return 0 ; /* FAILED */
1156+ }
1157+ } else {
1158+ fprintf (stderr , "Block map hash missing ctx->appx_ctx->isAppxBlockMap=%d\n" , ctx -> appx_ctx -> isAppxBlockMap );
11511159 return 0 ; /* FAILED */
11521160 }
1153- } else {
1154- fprintf (stderr , "Block map hash missing\n" );
1155- return 0 ; /* FAILED */
11561161 }
11571162 if (ctx -> appx_ctx -> calculatedCTHash && ctx -> appx_ctx -> existingCTHash ) {
11581163 printf ("Checking Content Types hashes:\n" );
@@ -1270,23 +1275,35 @@ static int appx_append_ct_signature_entry(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_E
12701275}
12711276
12721277/*
1273- * Get a hash algorithm specified in the AppxBlockMap.xml file.
1278+ * Determine the message digest algorithm to use when verifying or signing
1279+ * 1. From the AppxBlockMap.xml (if available)
1280+ * 2. From the PKCS7 signature (if present)
1281+ * 3. Fall back to the provided default digest
1282+ * [in, out] ctx: structure holds input and output data
1283+ * [in] md: default digest algorithm to use if none found elsewhere
12741284 * [in] zip: structure holds specific ZIP data
12751285 * [returns] one of SHA256/SHA384/SHA512 digest algorithms
12761286 */
1277- static const EVP_MD * appx_get_md (ZIP_FILE * zip )
1287+ static const EVP_MD * appx_get_md (FILE_FORMAT_CTX * ctx , const EVP_MD * md , ZIP_FILE * zip )
12781288{
12791289 uint8_t * data = NULL ;
12801290 char * start , * end , * pos ;
12811291 char * valueStart = NULL , * valueEnd = NULL ;
1282- const EVP_MD * md = NULL ;
1292+ const EVP_MD * appx_md = NULL ;
12831293 size_t slen , dataSize ;
12841294
1295+ /* Try to get a hash algorithm specified in the AppxBlockMap.xml file */
12851296 dataSize = zipReadFileDataByName (& data , zip , BLOCK_MAP_FILENAME );
12861297 if (dataSize <= 0 ) {
1287- fprintf (stderr , "Could not read: %s\n" , BLOCK_MAP_FILENAME );
1288- return NULL ; /* FAILED */
1298+ /* Excel Spreadsheet Macro-enabled (XLSM) file */
1299+ appx_md = appx_get_md_from_signature (ctx ); /* signed XLSM file */
1300+ if (!appx_md )
1301+ appx_md = md ; /* unsigned, use default message digest algorithm */
1302+ printf ("Message digest algorithm: %s\n" , EVP_MD_get0_name (appx_md ));
1303+ return appx_md ;
12891304 }
1305+ ctx -> appx_ctx -> isAppxBlockMap = 1 ; /* AppxBlockMap.xml detected */
1306+
12901307 start = strstr ((const char * )data , HASH_METHOD_TAG );
12911308 if (!start ) {
12921309 fprintf (stderr , "Parse error: tag: %s not found in %s\n" , HASH_METHOD_TAG , BLOCK_MAP_FILENAME );
@@ -1322,20 +1339,52 @@ static const EVP_MD *appx_get_md(ZIP_FILE *zip)
13221339 slen = (size_t )(valueEnd - valueStart + 1 );
13231340 if (strlen (HASH_METHOD_SHA256 ) == slen && !memcmp (valueStart , HASH_METHOD_SHA256 , slen )) {
13241341 printf ("Hash method is SHA256\n" );
1325- md = EVP_sha256 ();
1342+ appx_md = EVP_sha256 ();
13261343 } else if (strlen (HASH_METHOD_SHA384 ) == slen && !memcmp (valueStart , HASH_METHOD_SHA384 , slen )) {
13271344 printf ("Hash method is SHA384\n" );
1328- md = EVP_sha384 ();
1345+ appx_md = EVP_sha384 ();
13291346 } else if (strlen (HASH_METHOD_SHA512 ) == slen && !memcmp (valueStart , HASH_METHOD_SHA512 , slen )) {
13301347 printf ("Hash method is SHA512\n" );
1331- md = EVP_sha512 ();
1348+ appx_md = EVP_sha512 ();
13321349 } else {
13331350 fprintf (stderr , "Unsupported hash method\n" );
13341351 OPENSSL_free (data );
13351352 return NULL ; /* FAILED */
13361353 }
13371354 OPENSSL_free (data );
1338- return md ;
1355+ return appx_md ;
1356+ }
1357+
1358+ /*
1359+ * Extract the message digest algorithm used in the PKCS7 signature
1360+ * [in] ctx: structure holds input and output data
1361+ */
1362+ static const EVP_MD * appx_get_md_from_signature (FILE_FORMAT_CTX * ctx )
1363+ {
1364+
1365+ int mdtype = -1 ;
1366+ PKCS7 * p7 = appx_pkcs7_extract (ctx );
1367+
1368+ if (!p7 )
1369+ return NULL ; /* FAILED */
1370+
1371+ if (is_content_type (p7 , SPC_INDIRECT_DATA_OBJID )) {
1372+ ASN1_STRING * content_val = p7 -> d .sign -> contents -> d .other -> value .sequence ;
1373+ const u_char * p = content_val -> data ;
1374+ SpcIndirectDataContent * idc = d2i_SpcIndirectDataContent (NULL , & p , content_val -> length );
1375+
1376+ if (idc ) {
1377+ if (idc -> messageDigest && idc -> messageDigest -> digest && idc -> messageDigest -> digestAlgorithm ) {
1378+ mdtype = OBJ_obj2nid (idc -> messageDigest -> digestAlgorithm -> algorithm );
1379+ }
1380+ SpcIndirectDataContent_free (idc );
1381+ }
1382+ }
1383+ if (mdtype == -1 ) {
1384+ fprintf (stderr , "Failed to extract current message digest\n" );
1385+ return NULL ; /* FAILED */
1386+ }
1387+ return EVP_get_digestbynid (mdtype );
13391388}
13401389
13411390/*
0 commit comments