diff options
author | Ladislav Zezula <ladislav.zezula@avg.com> | 2014-08-27 14:00:15 +0200 |
---|---|---|
committer | Ladislav Zezula <ladislav.zezula@avg.com> | 2014-08-27 14:00:15 +0200 |
commit | d740634db4313d7adde780fbd3daae2bb9b9d520 (patch) | |
tree | 6bc23cf6a35a60b76d5d60050d1286e394661cbf /src/SFileVerify.cpp | |
parent | d0e8db518d33913c54b96886808bcf2c1dd683e9 (diff) |
+ Added support for signing MPQ archive (weak signature).v9.10
+ Added test cases for signature support
+ Release 9.10
Diffstat (limited to 'src/SFileVerify.cpp')
-rw-r--r-- | src/SFileVerify.cpp | 168 |
1 files changed, 152 insertions, 16 deletions
diff --git a/src/SFileVerify.cpp b/src/SFileVerify.cpp index 8d7e950..a506fc3 100644 --- a/src/SFileVerify.cpp +++ b/src/SFileVerify.cpp @@ -726,9 +726,8 @@ bool QueryMpqSignatureInfo( TMPQArchive * ha, PMPQ_SIGNATURE_INFO pSI) { + TFileEntry * pFileEntry; ULONGLONG ExtraBytes; - TMPQFile * hf; - HANDLE hFile; DWORD dwFileSize; // Make sure it's all zeroed @@ -738,23 +737,25 @@ bool QueryMpqSignatureInfo( CalculateArchiveRange(ha, pSI); // If there is "(signature)" file in the MPQ, it has a weak signature - if(SFileOpenFileEx((HANDLE)ha, SIGNATURE_NAME, SFILE_OPEN_BASE_FILE, &hFile)) + pFileEntry = GetFileEntryLocale(ha, SIGNATURE_NAME, LANG_NEUTRAL); + if(pFileEntry != NULL) { - // Get the content of the signature - SFileReadFile(hFile, pSI->Signature, sizeof(pSI->Signature), &pSI->cbSignatureSize, NULL); + // Calculate the begin and end of the signature file itself + pSI->BeginExclude = ha->MpqPos + pFileEntry->ByteOffset; + pSI->EndExclude = pSI->BeginExclude + pFileEntry->dwCmpSize; + dwFileSize = (DWORD)(pSI->EndExclude - pSI->BeginExclude); - // Verify the size of the signature - hf = (TMPQFile *)hFile; - - // We have to exclude the signature file from the digest - pSI->BeginExclude = ha->MpqPos + hf->pFileEntry->ByteOffset; - pSI->EndExclude = pSI->BeginExclude + hf->pFileEntry->dwCmpSize; - dwFileSize = hf->dwDataSize; + // Does the signature have proper size? + if(dwFileSize == MPQ_SIGNATURE_FILE_SIZE) + { + // Read the weak signature + if(!FileStream_Read(ha->pStream, &pSI->BeginExclude, pSI->Signature, dwFileSize)) + return false; - // Close the file - SFileCloseFile(hFile); - pSI->SignatureTypes |= SIGNATURE_TYPE_WEAK; - return (dwFileSize == (MPQ_WEAK_SIGNATURE_SIZE + 8)) ? true : false; + pSI->cbSignatureSize = dwFileSize; + pSI->SignatureTypes |= SIGNATURE_TYPE_WEAK; + return true; + } } // If there is extra bytes beyond the end of the archive, @@ -779,6 +780,96 @@ bool QueryMpqSignatureInfo( } //----------------------------------------------------------------------------- +// Support for weak signature + +int SSignFileCreate(TMPQArchive * ha) +{ + TMPQFile * hf = NULL; + BYTE EmptySignature[MPQ_SIGNATURE_FILE_SIZE]; + int nError = ERROR_SUCCESS; + + // Only save the signature if we should do so + if(ha->dwFileFlags3 != 0) + { + // The (signature) file must be non-encrypted and non-compressed + assert(ha->dwFileFlags3 == MPQ_FILE_EXISTS); + + // Create the (signature) file file in the MPQ + // Note that the file must not be compressed or encrypted + nError = SFileAddFile_Init(ha, SIGNATURE_NAME, + 0, + sizeof(EmptySignature), + LANG_NEUTRAL, + ha->dwFileFlags3 | MPQ_FILE_REPLACEEXISTING, + &hf); + + // Write the empty signature file to the archive + if(nError == ERROR_SUCCESS) + { + // Write the empty zeroed fiel to the MPQ + memset(EmptySignature, 0, sizeof(EmptySignature)); + nError = SFileAddFile_Write(hf, EmptySignature, (DWORD)sizeof(EmptySignature), 0); + } + + // If the save process succeeded, we clear the MPQ_FLAG_ATTRIBUTE_INVALID flag + if(nError == ERROR_SUCCESS) + { + ha->dwFlags &= ~MPQ_FLAG_SIGNATURE_INVALID; + ha->dwReservedFiles--; + } + + // Free the file + if(hf != NULL) + SFileAddFile_Finish(hf); + } + + return nError; +} + +int SSignFileFinish(TMPQArchive * ha) +{ + MPQ_SIGNATURE_INFO si; + unsigned long signature_len = MPQ_WEAK_SIGNATURE_SIZE; + BYTE WeakSignature[MPQ_SIGNATURE_FILE_SIZE]; + BYTE Md5Digest[MD5_DIGEST_SIZE]; + rsa_key key; + int hash_idx = find_hash("md5"); + + // Sanity checks + assert((ha->dwFlags & MPQ_FLAG_CHANGED) == 0); + assert(ha->dwFileFlags3 == MPQ_FILE_EXISTS); + + // Query the weak signature info + memset(&si, 0, sizeof(MPQ_SIGNATURE_INFO)); + if(!QueryMpqSignatureInfo(ha, &si)) + return ERROR_FILE_CORRUPT; + + // There must be exactly one signature + if(si.SignatureTypes != SIGNATURE_TYPE_WEAK) + return ERROR_FILE_CORRUPT; + + // Calculate MD5 of the entire archive + if(!CalculateMpqHashMd5(ha, &si, Md5Digest)) + return ERROR_VERIFY_FAILED; + + // Decode the private key + if(!decode_base64_key(szBlizzardWeakPrivateKey, &key)) + return ERROR_VERIFY_FAILED; + + // Sign the hash + memset(WeakSignature, 0, sizeof(WeakSignature)); + rsa_sign_hash_ex(Md5Digest, sizeof(Md5Digest), WeakSignature + 8, &signature_len, LTC_LTC_PKCS_1_V1_5, 0, 0, hash_idx, 0, &key); + memrev(WeakSignature + 8, MPQ_WEAK_SIGNATURE_SIZE); + rsa_free(&key); + + // Write the signature to the MPQ. Don't use SFile* functions, but write the hash directly + if(!FileStream_Write(ha->pStream, &si.BeginExclude, WeakSignature, MPQ_SIGNATURE_FILE_SIZE)) + return GetLastError(); + + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- // Public (exported) functions bool WINAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWORD pdwCrc32, char * pMD5) @@ -900,6 +991,10 @@ DWORD WINAPI SFileVerifyArchive(HANDLE hMpq) if(!IsValidMpqHandle(hMpq)) return ERROR_VERIFY_FAILED; + // If the archive was modified, we need to flush it + if(ha->dwFlags & MPQ_FLAG_CHANGED) + SFileFlushArchive(hMpq); + // Get the MPQ signature and signature type memset(&si, 0, sizeof(MPQ_SIGNATURE_INFO)); if(!QueryMpqSignatureInfo(ha, &si)) @@ -922,3 +1017,44 @@ DWORD WINAPI SFileVerifyArchive(HANDLE hMpq) return ERROR_NO_SIGNATURE; } + +// Verifies the archive against the signature +bool WINAPI SFileSignArchive(HANDLE hMpq, DWORD dwSignatureType) +{ + TMPQArchive * ha; + + // Verify the archive handle + ha = IsValidMpqHandle(hMpq); + if(ha == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // We only support weak signature, and only for MPQs version 1.0 + if(dwSignatureType != SIGNATURE_TYPE_WEAK) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // The archive must not be malformed and must not be read-only + if(ha->dwFlags & (MPQ_FLAG_READ_ONLY | MPQ_FLAG_MALFORMED)) + { + SetLastError(ERROR_ACCESS_DENIED); + return false; + } + + // If the signature is not there yet + if(ha->dwFileFlags3 == 0) + { + // Turn the signature on. The signature will + // be applied when the archive is closed + ha->dwFlags |= MPQ_FLAG_SIGNATURE_INVALID | MPQ_FLAG_CHANGED; + ha->dwFileFlags3 = MPQ_FILE_EXISTS; + ha->dwReservedFiles++; + } + + return true; +} + |