//----------------------------------------------------------------------------- // Copyright (c) 2013 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #include "io/zip/zipCryptStream.h" #include "algorithm/crctab.h" #include "console/console.h" ////////////////////////////////////////////////////////////////////////// // Constructor/Destructor ////////////////////////////////////////////////////////////////////////// ZipCryptRStream::ZipCryptRStream() : mStream(NULL), mFileEndPos(0), mPassword(NULL) { } ZipCryptRStream::~ZipCryptRStream() { } ////////////////////////////////////////////////////////////////////////// // Private Methods ////////////////////////////////////////////////////////////////////////// U32 ZipCryptRStream::fillBuffer(const U32 in_attemptSize, void *pBuffer) { AssertFatal(mStream != NULL, "No stream to fill from?"); AssertFatal(mStream->getStatus() != Stream::Closed, "Fill from a closed stream?"); U32 currPos = mStream->getPosition(); U32 actualReadSize; if (in_attemptSize + currPos > (U32)mFileEndPos) { actualReadSize = mFileEndPos - currPos; } else { actualReadSize = in_attemptSize; } if (mStream->read(actualReadSize, pBuffer) == true) { return actualReadSize; } else { AssertWarn(false, "Read failed while trying to fill buffer"); return 0; } } ////////////////////////////////////////////////////////////////////////// // Public Methods ////////////////////////////////////////////////////////////////////////// void ZipCryptRStream::setPassword(const char *password) { mKeys[0] = 305419896; mKeys[1] = 591751049; mKeys[2] = 878082192; mPassword = password; const char *pPtr = password; while(*pPtr) { updateKeys(*pPtr); pPtr++; } } bool ZipCryptRStream::attachStream(Stream* io_pSlaveStream) { mStream = io_pSlaveStream; mStreamStartPos = mStream->getPosition(); // [tom, 12/20/2005] Encrypted zip files have an extra 12 bytes // of entropy before the file data. U8 buffer[12]; if(mStream->read(sizeof(buffer), &buffer)) { // Initialize keys for(S32 i = 0;i < sizeof(buffer);i++) { updateKeys(buffer[i] ^= decryptByte()); } // if(buffer[11] !) mFileStartPos = mStream->getPosition(); setStatus(Ok); return true; } return false; } void ZipCryptRStream::detachStream() { mStream = NULL; // Clear keys, just in case dMemset(&mKeys, 0, sizeof(mKeys)); setStatus(Closed); } U32 ZipCryptRStream::getPosition() const { return mStream->getPosition(); } bool ZipCryptRStream::setPosition(const U32 in_newPosition) { if(in_newPosition > (U32)mFileEndPos) return false; U32 curPos = getPosition(); U32 readSize = in_newPosition - mFileStartPos; bool ret = true; if(in_newPosition < curPos) { // Reposition to start of stream Stream *stream = getStream(); U32 startPos = mStreamStartPos; const char *password = mPassword; detachStream(); setPassword(password); stream->setPosition(startPos); ret = attachStream(stream); if(in_newPosition == mFileStartPos) return ret; } // Read until we reach the new position U8 *buffer = new U8 [1024]; while(readSize >= 1024) { readSize -= 1024; ret = _read(1024, buffer); if(! ret) break; } if(readSize > 0 && ret) { ret = _read(readSize, buffer); } delete [] buffer; return ret; } ////////////////////////////////////////////////////////////////////////// // Protected Methods ////////////////////////////////////////////////////////////////////////// void ZipCryptRStream::updateKeys(const U8 c) { mKeys[0] = ZC_CRC32(mKeys[0], c); mKeys[1] += mKeys[0] & 0x000000ff; mKeys[1] = mKeys[1] * 134775813 + 1; U32 k = mKeys[1] >> 24; mKeys[2] = ZC_CRC32(mKeys[2], k); } U8 ZipCryptRStream::decryptByte() { U16 temp; temp = (mKeys[2] & 0xffff) | 2; return (temp * (temp ^ 1)) >> 8; } bool ZipCryptRStream::_read(const U32 in_numBytes, void* out_pBuffer) { U32 numRead = fillBuffer(in_numBytes, out_pBuffer); if(numRead > 0) { // Decrypt U8 *pBytes = (U8 *)out_pBuffer; for(U32 i = 0;i < numRead;i++) { updateKeys(pBytes[i] ^= decryptByte()); } return true; } return false; }