none
Agile Signature in Office Files with Userforms and problems in Documentation RRS feed

  • Question

  • Hello!

    I have new problems with verifying/creating an agile signature.

    First of all I want to report some mistakes in MS-OVBA.

    In 2.4.2: Contents Hash there is a slight error in the Description of CryptographicDigest. The size is stated with 16 Bytes. This ist only true for legacy signatures! The hash of a MD5 algorithm is 16 Bytes, 32 of a SHA256, etc.

    The bigger problem is the CryprograhicDigest pesudo code.

    For the module stream the text representaion for the decompressed source ist to be taken.

    This ist not true! You need to take the bytestream an to process this.

    I came across this problem when I got a macro which was written in Germany and had ths sing “ in it (ASCII 147) in its code.

    The file was send to china and saved with the codepage 936 (Simplified Chinese).

    if you convert this Byte to Simplified Chinese you get an wide char and converting ist back results in a different byte code which later results in a bad CryprograhicDigest! So better keep with the byte-code!

    Another problem is when you try to calculate the CryprograhicDigest for a document containing an Userform.

    It is enough to create a document with an empty userform.

    The CryprograhicDigest is OK for the legacy MD5 signature, but not for the aglie one.

    So the documentation is missing something, that I cannot find out.

    Is there someone that can help me to find the missing link?

    Kind Regards

    Alexander Petzelt


    • Edited by AlexNbg Monday, January 21, 2019 2:24 PM Error in Caption
    Monday, January 21, 2019 2:24 PM

Answers

  • For the benefit of the community here, Alex and I discussed offline. Alex had reported initially: 

    "It is enough to create a document with an empty userform.

    The CryprograhicDigest is OK for the legacy MD5 signature, but not for the aglie one.

    So the documentation is missing something, that I cannot find out."

    Indeed [MS-OVBA] only described the MD5 hash algorithm for the CryptographicDigest. Per [MS-OSHARED], the digest can be "agile" and this algorithm was not described in the latest version of [MS-OVBA]. 

    I am providing a draft of this for reference by implementers here. An upcoming version of [MS-OVBA] will contain the final algorithm for agile digests as well as MD5 digests. 

    Load bcrypt and crypt32 DLLs
    Call CryptFindOIDInfo with CRYPT_OID_INFO_OID_KEY, the ObjectID of hashing algorithm (for example, "1.3.14.3.2.26" is SHA1), and CRYPT_HASH_ALG_OID_GROUP_ID arguments.
    Get the algorithm name (i.e. pwszCNGAlgid member) from the OidInfo returned (for example, "SHA1" would be returned for the example above)
    Call BCryptOpenAlgorithmProvider with the algorithm name to retrieve a handle to the hash algorithm provider (using null pszImplementation field and 0 dwFlags field)
    Call BCryptCreateHash with the algorithm provider handle to retrieve the hash handle used to construct the data hash. All reference to the act of "hashing" below involves calling BCryptHashData using this hash handle on the data.
    Iterate sequentially through the records of the "dirs" stream of the project. For each:
    	if the record ID is BIN_PROJ_NAME (4), hash the characters of the name field.
    	if the record ID is BIN_PROJ_CONSTANTS (12), hash the characters of the constants data field.
    	if the record ID is BIN_PROJ_LIBID_REGISTERED (13), parse the LIBID record and isolate the GUID component, including opening '{' character and closing '}' character. Hash the characters of this GUID portion of the field.
    	if the record ID is BIN_MOD_END (43), take the mod data described by the preceeding module-related records including BIN_MOD_UNICODE_STREAM and BIN_MOD_TEXTOFFSET records and do the following:
    		open the module stream with the name specified
    		seek the read pointer of the opened stream to the offset specified by the module text offset value.
    		decompress the text portion of the module stream from the text offset to the end according to the algorithm specified in the document.
    		for each line of text in the decompressed module text:
    			if the line starts with "Attribute " (case-insensitive), skip the line.
    			if the line does not start with "Attribute ", then hash the characters for that line (disregarding the terminating newline character sequence)
    	For all other records, skip the record
    Open the "PROJECT" stream of the project storage.
    Iterate sequentially through the records of the "PROJECT" stream of the project. For each:
    	if the record identifier is "BaseClass", do the following:
    		take the record value and open the substorage of the project storage with that name (for example, if the record was "BaseClass=MyUserForm", you open the substorage named "MyUserForm")
    		enumerate the subelements of the opened storage. For each subelement of the storage:
    			if the subelement is a stream
    				until end of stream is reached:
    					allocate a buffer of size 1023 bytes and initialize all of its contents to 0.
    					read from the stream 1023 bytes, or however many bytes remain in the stream, whichever is smaller.
    					if at least one byte was read, hash the entire buffer of 1023 bytes (regardless of how many bytes were actually read from the stream)
    			if the subelement is a storage, then treat it recursively as described here, enumerating all of its elements, hashing the streams, or recursing into its substorages to do the same.
    	For all other records, skip the record
    One completed, call BCryptGetProperty using the hash handle, and the BCRYPT_HASH_LENGTH argument to retrieve the byte size of the resulting hash.
    Allocate a buffer of that size to hold the resulting hash value and call BCryptGetProperty again, this time with the hash handle, the allocated buffer, and allocation size to retrieve the final hash result.
    To complete the procedure, call BCryptDestroyHash using the hash handle, followed by BCryptCloseAlgorithmProvider on the algorithm provider handle.
    
    The hash calculated by this algorithm is the agile source hash of the project and is subsequently used in creating the indirect data of the agile signature of the VBA project.
    
    

    Thank you Alex for bringing this to our attention! 

    Tom

     
    Tuesday, March 12, 2019 11:09 PM
    Moderator

All replies

  • Hello Alexander,
    Thank you for your inquiry about Microsoft office specifications. Our team has created incidents to investigate these issues. One of the Open specifications team member will contact you shortly.
     
     
    Regards,
    Sreekanth Nadendla
    Microsoft Windows Open specifications
    Monday, January 21, 2019 2:39 PM
    Moderator
  • Hello,

    there is another problem in the documentation.

    2.3.4.2.2.2 REFERENCENAME Record.

    If the macro was created on a mac the unicode part is missing.

    Kind regards

    Monday, January 21, 2019 3:13 PM
  • Hi Alexander, 

    Thanks for reporting these issues. I will take a look at the content, verify and get back to your shortly with more information. 

    Best regards,
    Tom Jebo
    Sr Escalation Engineer
    Microsoft Open Specifications

    Tuesday, January 22, 2019 7:23 PM
    Moderator
  • Hi Tom,

    feel free to tell me if you want some files for tests.

    Kind regards
    Alex

    Tuesday, January 22, 2019 7:53 PM
  • Hi Alex, 

    Thanks, can you please send email to dochelp at microsoft dot com, reference the URL for this thread and my name? I will provide a share on which you can upload sample files. That would help.


    Best regards,
    Tom Jebo
    Sr Escalation Engineer
    Microsoft Open Specifications

    Tuesday, January 22, 2019 8:04 PM
    Moderator
  • For the benefit of the community here, Alex and I discussed offline. Alex had reported initially: 

    "It is enough to create a document with an empty userform.

    The CryprograhicDigest is OK for the legacy MD5 signature, but not for the aglie one.

    So the documentation is missing something, that I cannot find out."

    Indeed [MS-OVBA] only described the MD5 hash algorithm for the CryptographicDigest. Per [MS-OSHARED], the digest can be "agile" and this algorithm was not described in the latest version of [MS-OVBA]. 

    I am providing a draft of this for reference by implementers here. An upcoming version of [MS-OVBA] will contain the final algorithm for agile digests as well as MD5 digests. 

    Load bcrypt and crypt32 DLLs
    Call CryptFindOIDInfo with CRYPT_OID_INFO_OID_KEY, the ObjectID of hashing algorithm (for example, "1.3.14.3.2.26" is SHA1), and CRYPT_HASH_ALG_OID_GROUP_ID arguments.
    Get the algorithm name (i.e. pwszCNGAlgid member) from the OidInfo returned (for example, "SHA1" would be returned for the example above)
    Call BCryptOpenAlgorithmProvider with the algorithm name to retrieve a handle to the hash algorithm provider (using null pszImplementation field and 0 dwFlags field)
    Call BCryptCreateHash with the algorithm provider handle to retrieve the hash handle used to construct the data hash. All reference to the act of "hashing" below involves calling BCryptHashData using this hash handle on the data.
    Iterate sequentially through the records of the "dirs" stream of the project. For each:
    	if the record ID is BIN_PROJ_NAME (4), hash the characters of the name field.
    	if the record ID is BIN_PROJ_CONSTANTS (12), hash the characters of the constants data field.
    	if the record ID is BIN_PROJ_LIBID_REGISTERED (13), parse the LIBID record and isolate the GUID component, including opening '{' character and closing '}' character. Hash the characters of this GUID portion of the field.
    	if the record ID is BIN_MOD_END (43), take the mod data described by the preceeding module-related records including BIN_MOD_UNICODE_STREAM and BIN_MOD_TEXTOFFSET records and do the following:
    		open the module stream with the name specified
    		seek the read pointer of the opened stream to the offset specified by the module text offset value.
    		decompress the text portion of the module stream from the text offset to the end according to the algorithm specified in the document.
    		for each line of text in the decompressed module text:
    			if the line starts with "Attribute " (case-insensitive), skip the line.
    			if the line does not start with "Attribute ", then hash the characters for that line (disregarding the terminating newline character sequence)
    	For all other records, skip the record
    Open the "PROJECT" stream of the project storage.
    Iterate sequentially through the records of the "PROJECT" stream of the project. For each:
    	if the record identifier is "BaseClass", do the following:
    		take the record value and open the substorage of the project storage with that name (for example, if the record was "BaseClass=MyUserForm", you open the substorage named "MyUserForm")
    		enumerate the subelements of the opened storage. For each subelement of the storage:
    			if the subelement is a stream
    				until end of stream is reached:
    					allocate a buffer of size 1023 bytes and initialize all of its contents to 0.
    					read from the stream 1023 bytes, or however many bytes remain in the stream, whichever is smaller.
    					if at least one byte was read, hash the entire buffer of 1023 bytes (regardless of how many bytes were actually read from the stream)
    			if the subelement is a storage, then treat it recursively as described here, enumerating all of its elements, hashing the streams, or recursing into its substorages to do the same.
    	For all other records, skip the record
    One completed, call BCryptGetProperty using the hash handle, and the BCRYPT_HASH_LENGTH argument to retrieve the byte size of the resulting hash.
    Allocate a buffer of that size to hold the resulting hash value and call BCryptGetProperty again, this time with the hash handle, the allocated buffer, and allocation size to retrieve the final hash result.
    To complete the procedure, call BCryptDestroyHash using the hash handle, followed by BCryptCloseAlgorithmProvider on the algorithm provider handle.
    
    The hash calculated by this algorithm is the agile source hash of the project and is subsequently used in creating the indirect data of the agile signature of the VBA project.
    
    

    Thank you Alex for bringing this to our attention! 

    Tom

     
    Tuesday, March 12, 2019 11:09 PM
    Moderator