commit 7e2c90a5605fecbc51cda8d514eee4d2d6066c80 Author: Donato Fiore <50346119+donato-fiore@users.noreply.github.com> Date: Thu Nov 30 21:06:53 2023 -0500 Initial Commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6c66155 --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +TARGET := iphone:clang:latest:14.0 +INSTALL_TARGET_PROCESSES = TrollDecrypt + +GO_EASY_ON_ME = 1 +PACKAGE_FORMAT = ipa + +include $(THEOS)/makefiles/common.mk + +APPLICATION_NAME = TrollDecrypt + +TrollDecrypt_FILES = SSZipArchive/minizip/unzip.c SSZipArchive/minizip/crypt.c SSZipArchive/minizip/ioapi_buf.c SSZipArchive/minizip/ioapi_mem.c SSZipArchive/minizip/ioapi.c SSZipArchive/minizip/minishared.c SSZipArchive/minizip/zip.c SSZipArchive/minizip/aes/aes_ni.c SSZipArchive/minizip/aes/aescrypt.c SSZipArchive/minizip/aes/aeskey.c SSZipArchive/minizip/aes/aestab.c SSZipArchive/minizip/aes/fileenc.c SSZipArchive/minizip/aes/hmac.c SSZipArchive/minizip/aes/prng.c SSZipArchive/minizip/aes/pwd2key.c SSZipArchive/minizip/aes/sha1.c SSZipArchive/SSZipArchive.m +TrollDecrypt_FILES += main.m TDAppDelegate.m TDRootViewController.m TDDumpDecrypted.m TDUtils.m TDFileManagerViewController.m +TrollDecrypt_FRAMEWORKS = UIKit CoreGraphics MobileCoreServices +TrollDecrypt_CFLAGS = -fobjc-arc +TrollDecrypt_CODESIGN_FLAGS = -Sentitlements.plist + +include $(THEOS_MAKE_PATH)/application.mk + +after-stage:: + rm -rf Payload + mkdir -p $(THEOS_STAGING_DIR)/Payload + ldid -Sentitlements.plist $(THEOS_STAGING_DIR)/Applications/TrollDecrypt.app/TrollDecrypt + cp -a $(THEOS_STAGING_DIR)/Applications/* $(THEOS_STAGING_DIR)/Payload + mv $(THEOS_STAGING_DIR)/Payload . + zip -q -r TrollDecrypt.ipa Payload diff --git a/Payload/TrollDecrypt.app/AppIcon60x60.png b/Payload/TrollDecrypt.app/AppIcon60x60.png new file mode 100644 index 0000000..9989629 Binary files /dev/null and b/Payload/TrollDecrypt.app/AppIcon60x60.png differ diff --git a/Payload/TrollDecrypt.app/AppIcon76x76~ipad.png b/Payload/TrollDecrypt.app/AppIcon76x76~ipad.png new file mode 100644 index 0000000..b2d07fb Binary files /dev/null and b/Payload/TrollDecrypt.app/AppIcon76x76~ipad.png differ diff --git a/Payload/TrollDecrypt.app/Info.plist b/Payload/TrollDecrypt.app/Info.plist new file mode 100644 index 0000000..598a85f --- /dev/null +++ b/Payload/TrollDecrypt.app/Info.plist @@ -0,0 +1,68 @@ + + + + + CFBundleExecutable + TrollDecrypt + CFBundleIcons + + CFBundlePrimaryIcon + + CFBundleIconFiles + + AppIcon60x60 + + UIPrerenderedIcon + + + + CFBundleIcons~ipad + + CFBundlePrimaryIcon + + CFBundleIconFiles + + AppIcon60x60 + AppIcon76x76~ipad + + UIPrerenderedIcon + + + + CFBundleIdentifier + com.fiore.trolldecrypt + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + iPhoneOS + + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIDeviceFamily + + 1 + 2 + + UIRequiredDeviceCapabilities + + armv7 + + UILaunchStoryboardName + LaunchScreen + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + + + diff --git a/Payload/TrollDecrypt.app/TrollDecrypt b/Payload/TrollDecrypt.app/TrollDecrypt new file mode 100644 index 0000000..33c01bc Binary files /dev/null and b/Payload/TrollDecrypt.app/TrollDecrypt differ diff --git a/Resources/AppIcon60x60.png b/Resources/AppIcon60x60.png new file mode 100644 index 0000000..9989629 Binary files /dev/null and b/Resources/AppIcon60x60.png differ diff --git a/Resources/AppIcon76x76~ipad.png b/Resources/AppIcon76x76~ipad.png new file mode 100644 index 0000000..b2d07fb Binary files /dev/null and b/Resources/AppIcon76x76~ipad.png differ diff --git a/Resources/Info.plist b/Resources/Info.plist new file mode 100644 index 0000000..598a85f --- /dev/null +++ b/Resources/Info.plist @@ -0,0 +1,68 @@ + + + + + CFBundleExecutable + TrollDecrypt + CFBundleIcons + + CFBundlePrimaryIcon + + CFBundleIconFiles + + AppIcon60x60 + + UIPrerenderedIcon + + + + CFBundleIcons~ipad + + CFBundlePrimaryIcon + + CFBundleIconFiles + + AppIcon60x60 + AppIcon76x76~ipad + + UIPrerenderedIcon + + + + CFBundleIdentifier + com.fiore.trolldecrypt + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + iPhoneOS + + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIDeviceFamily + + 1 + 2 + + UIRequiredDeviceCapabilities + + armv7 + + UILaunchStoryboardName + LaunchScreen + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + + + diff --git a/SSZipArchive/SSZipArchive.h b/SSZipArchive/SSZipArchive.h new file mode 100644 index 0000000..e5768b1 --- /dev/null +++ b/SSZipArchive/SSZipArchive.h @@ -0,0 +1,146 @@ +// +// SSZipArchive.h +// SSZipArchive +// +// Created by Sam Soffes on 7/21/10. +// Copyright (c) Sam Soffes 2010-2015. All rights reserved. +// + +#ifndef _SSZIPARCHIVE_H +#define _SSZIPARCHIVE_H + +#import +#include "SSZipCommon.h" + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const SSZipArchiveErrorDomain; +typedef NS_ENUM(NSInteger, SSZipArchiveErrorCode) { + SSZipArchiveErrorCodeFailedOpenZipFile = -1, + SSZipArchiveErrorCodeFailedOpenFileInZip = -2, + SSZipArchiveErrorCodeFileInfoNotLoadable = -3, + SSZipArchiveErrorCodeFileContentNotReadable = -4, + SSZipArchiveErrorCodeFailedToWriteFile = -5, + SSZipArchiveErrorCodeInvalidArguments = -6, +}; + +@protocol SSZipArchiveDelegate; + +@interface SSZipArchive : NSObject + +// Password check ++ (BOOL)isFilePasswordProtectedAtPath:(NSString *)path; ++ (BOOL)isPasswordValidForArchiveAtPath:(NSString *)path password:(NSString *)pw error:(NSError * _Nullable * _Nullable)error NS_SWIFT_NOTHROW; + +// Unzip ++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination; ++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(nullable id)delegate; + ++ (BOOL)unzipFileAtPath:(NSString *)path + toDestination:(NSString *)destination + overwrite:(BOOL)overwrite + password:(nullable NSString *)password + error:(NSError * *)error; + ++ (BOOL)unzipFileAtPath:(NSString *)path + toDestination:(NSString *)destination + overwrite:(BOOL)overwrite + password:(nullable NSString *)password + error:(NSError * *)error + delegate:(nullable id)delegate NS_REFINED_FOR_SWIFT; + ++ (BOOL)unzipFileAtPath:(NSString *)path + toDestination:(NSString *)destination + preserveAttributes:(BOOL)preserveAttributes + overwrite:(BOOL)overwrite + password:(nullable NSString *)password + error:(NSError * *)error + delegate:(nullable id)delegate; + ++ (BOOL)unzipFileAtPath:(NSString *)path + toDestination:(NSString *)destination + progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler + completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler; + ++ (BOOL)unzipFileAtPath:(NSString *)path + toDestination:(NSString *)destination + overwrite:(BOOL)overwrite + password:(nullable NSString *)password + progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler + completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler; + ++ (BOOL)unzipFileAtPath:(NSString *)path + toDestination:(NSString *)destination + preserveAttributes:(BOOL)preserveAttributes + overwrite:(BOOL)overwrite + nestedZipLevel:(NSInteger)nestedZipLevel + password:(nullable NSString *)password + error:(NSError **)error + delegate:(nullable id)delegate + progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler + completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler; + +// Zip +// default compression level is Z_DEFAULT_COMPRESSION (from "zlib.h") + +// without password ++ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)paths; ++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath; + ++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath keepParentDirectory:(BOOL)keepParentDirectory; + +// with optional password, default encryption is AES +// don't use AES if you need compatibility with native macOS unzip and Archive Utility ++ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)paths withPassword:(nullable NSString *)password; ++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath withPassword:(nullable NSString *)password; ++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath keepParentDirectory:(BOOL)keepParentDirectory withPassword:(nullable NSString *)password; ++ (BOOL)createZipFileAtPath:(NSString *)path + withContentsOfDirectory:(NSString *)directoryPath + keepParentDirectory:(BOOL)keepParentDirectory + withPassword:(nullable NSString *)password + andProgressHandler:(void(^ _Nullable)(NSUInteger entryNumber, NSUInteger total))progressHandler; ++ (BOOL)createZipFileAtPath:(NSString *)path + withContentsOfDirectory:(NSString *)directoryPath + keepParentDirectory:(BOOL)keepParentDirectory + compressionLevel:(int)compressionLevel + password:(nullable NSString *)password + AES:(BOOL)aes + progressHandler:(void(^ _Nullable)(NSUInteger entryNumber, NSUInteger total))progressHandler; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithPath:(NSString *)path NS_DESIGNATED_INITIALIZER; +- (BOOL)open; + +/// write empty folder +- (BOOL)writeFolderAtPath:(NSString *)path withFolderName:(NSString *)folderName withPassword:(nullable NSString *)password; +/// write file +- (BOOL)writeFile:(NSString *)path withPassword:(nullable NSString *)password; +- (BOOL)writeFileAtPath:(NSString *)path withFileName:(nullable NSString *)fileName withPassword:(nullable NSString *)password; +- (BOOL)writeFileAtPath:(NSString *)path withFileName:(nullable NSString *)fileName compressionLevel:(int)compressionLevel password:(nullable NSString *)password AES:(BOOL)aes; +/// write data +- (BOOL)writeData:(NSData *)data filename:(nullable NSString *)filename withPassword:(nullable NSString *)password; +- (BOOL)writeData:(NSData *)data filename:(nullable NSString *)filename compressionLevel:(int)compressionLevel password:(nullable NSString *)password AES:(BOOL)aes; + +- (BOOL)close; + +@end + +@protocol SSZipArchiveDelegate + +@optional + +- (void)zipArchiveWillUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo; +- (void)zipArchiveDidUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo unzippedPath:(NSString *)unzippedPath; + +- (BOOL)zipArchiveShouldUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo; +- (void)zipArchiveWillUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo; +- (void)zipArchiveDidUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath fileInfo:(unz_file_info)fileInfo; +- (void)zipArchiveDidUnzipFileAtIndex:(NSInteger)fileIndex totalFiles:(NSInteger)totalFiles archivePath:(NSString *)archivePath unzippedFilePath:(NSString *)unzippedFilePath; + +- (void)zipArchiveProgressEvent:(unsigned long long)loaded total:(unsigned long long)total; + +@end + +NS_ASSUME_NONNULL_END + +#endif /* _SSZIPARCHIVE_H */ diff --git a/SSZipArchive/SSZipArchive.m b/SSZipArchive/SSZipArchive.m new file mode 100644 index 0000000..79897d4 --- /dev/null +++ b/SSZipArchive/SSZipArchive.m @@ -0,0 +1,1069 @@ +// +// SSZipArchive.m +// SSZipArchive +// +// Created by Sam Soffes on 7/21/10. +// Copyright (c) Sam Soffes 2010-2015. All rights reserved. +// + +#import "SSZipArchive.h" +#include "./minizip/unzip.h" +#include "./minizip/zip.h" +#include "./minizip/minishared.h" + +#include + +NSString *const SSZipArchiveErrorDomain = @"SSZipArchiveErrorDomain"; + +#define CHUNK 16384 + +int _zipOpenEntry(zipFile entry, NSString *name, const zip_fileinfo *zipfi, int level, NSString *password, BOOL aes); +BOOL _fileIsSymbolicLink(const unz_file_info *fileInfo); + +#ifndef API_AVAILABLE +// Xcode 7- compatibility +#define API_AVAILABLE(...) +#endif + +@interface NSData(SSZipArchive) +- (NSString *)_base64RFC4648 API_AVAILABLE(macos(10.9), ios(7.0), watchos(2.0), tvos(9.0)); +- (NSString *)_hexString; +@end + +@interface SSZipArchive () +- (instancetype)init NS_DESIGNATED_INITIALIZER; +@end + +@implementation SSZipArchive +{ + /// path for zip file + NSString *_path; + zipFile _zip; +} + +#pragma mark - Password check + ++ (BOOL)isFilePasswordProtectedAtPath:(NSString *)path { + // Begin opening + zipFile zip = unzOpen(path.fileSystemRepresentation); + if (zip == NULL) { + return NO; + } + + int ret = unzGoToFirstFile(zip); + if (ret == UNZ_OK) { + do { + ret = unzOpenCurrentFile(zip); + if (ret != UNZ_OK) { + return NO; + } + unz_file_info fileInfo = {}; + ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0); + if (ret != UNZ_OK) { + return NO; + } else if ((fileInfo.flag & 1) == 1) { + return YES; + } + + unzCloseCurrentFile(zip); + ret = unzGoToNextFile(zip); + } while (ret == UNZ_OK); + } + + return NO; +} + ++ (BOOL)isPasswordValidForArchiveAtPath:(NSString *)path password:(NSString *)pw error:(NSError **)error { + if (error) { + *error = nil; + } + + zipFile zip = unzOpen(path.fileSystemRepresentation); + if (zip == NULL) { + if (error) { + *error = [NSError errorWithDomain:SSZipArchiveErrorDomain + code:SSZipArchiveErrorCodeFailedOpenZipFile + userInfo:@{NSLocalizedDescriptionKey: @"failed to open zip file"}]; + } + return NO; + } + + int ret = unzGoToFirstFile(zip); + if (ret == UNZ_OK) { + do { + if (pw.length == 0) { + ret = unzOpenCurrentFile(zip); + } else { + ret = unzOpenCurrentFilePassword(zip, [pw cStringUsingEncoding:NSUTF8StringEncoding]); + } + if (ret != UNZ_OK) { + if (ret != UNZ_BADPASSWORD) { + if (error) { + *error = [NSError errorWithDomain:SSZipArchiveErrorDomain + code:SSZipArchiveErrorCodeFailedOpenFileInZip + userInfo:@{NSLocalizedDescriptionKey: @"failed to open first file in zip file"}]; + } + } + return NO; + } + unz_file_info fileInfo = {}; + ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0); + if (ret != UNZ_OK) { + if (error) { + *error = [NSError errorWithDomain:SSZipArchiveErrorDomain + code:SSZipArchiveErrorCodeFileInfoNotLoadable + userInfo:@{NSLocalizedDescriptionKey: @"failed to retrieve info for file"}]; + } + return NO; + } else if ((fileInfo.flag & 1) == 1) { + unsigned char buffer[10] = {0}; + int readBytes = unzReadCurrentFile(zip, buffer, (unsigned)MIN(10UL,fileInfo.uncompressed_size)); + if (readBytes < 0) { + // Let's assume error Z_DATA_ERROR is caused by an invalid password + // Let's assume other errors are caused by Content Not Readable + if (readBytes != Z_DATA_ERROR) { + if (error) { + *error = [NSError errorWithDomain:SSZipArchiveErrorDomain + code:SSZipArchiveErrorCodeFileContentNotReadable + userInfo:@{NSLocalizedDescriptionKey: @"failed to read contents of file entry"}]; + } + } + return NO; + } + return YES; + } + + unzCloseCurrentFile(zip); + ret = unzGoToNextFile(zip); + } while (ret == UNZ_OK); + } + + // No password required + return YES; +} + +#pragma mark - Unzipping + ++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination +{ + return [self unzipFileAtPath:path toDestination:destination delegate:nil]; +} + ++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(nullable NSString *)password error:(NSError **)error +{ + return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:overwrite password:password error:error delegate:nil progressHandler:nil completionHandler:nil]; +} + ++ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(nullable id)delegate +{ + return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:YES password:nil error:nil delegate:delegate progressHandler:nil completionHandler:nil]; +} + ++ (BOOL)unzipFileAtPath:(NSString *)path + toDestination:(NSString *)destination + overwrite:(BOOL)overwrite + password:(nullable NSString *)password + error:(NSError **)error + delegate:(nullable id)delegate +{ + return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:overwrite password:password error:error delegate:delegate progressHandler:nil completionHandler:nil]; +} + ++ (BOOL)unzipFileAtPath:(NSString *)path + toDestination:(NSString *)destination + overwrite:(BOOL)overwrite + password:(NSString *)password + progressHandler:(void (^)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler + completionHandler:(void (^)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler +{ + return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:overwrite password:password error:nil delegate:nil progressHandler:progressHandler completionHandler:completionHandler]; +} + ++ (BOOL)unzipFileAtPath:(NSString *)path + toDestination:(NSString *)destination + progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler + completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler +{ + return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:YES password:nil error:nil delegate:nil progressHandler:progressHandler completionHandler:completionHandler]; +} + ++ (BOOL)unzipFileAtPath:(NSString *)path + toDestination:(NSString *)destination + preserveAttributes:(BOOL)preserveAttributes + overwrite:(BOOL)overwrite + password:(nullable NSString *)password + error:(NSError * *)error + delegate:(nullable id)delegate +{ + return [self unzipFileAtPath:path toDestination:destination preserveAttributes:preserveAttributes overwrite:overwrite password:password error:error delegate:delegate progressHandler:nil completionHandler:nil]; +} + ++ (BOOL)unzipFileAtPath:(NSString *)path + toDestination:(NSString *)destination + preserveAttributes:(BOOL)preserveAttributes + overwrite:(BOOL)overwrite + password:(nullable NSString *)password + error:(NSError **)error + delegate:(nullable id)delegate + progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler + completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler +{ + return [self unzipFileAtPath:path toDestination:destination preserveAttributes:preserveAttributes overwrite:overwrite nestedZipLevel:0 password:password error:error delegate:delegate progressHandler:progressHandler completionHandler:completionHandler]; +} + ++ (BOOL)unzipFileAtPath:(NSString *)path + toDestination:(NSString *)destination + preserveAttributes:(BOOL)preserveAttributes + overwrite:(BOOL)overwrite + nestedZipLevel:(NSInteger)nestedZipLevel + password:(nullable NSString *)password + error:(NSError **)error + delegate:(nullable id)delegate + progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler + completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler +{ + // Guard against empty strings + if (path.length == 0 || destination.length == 0) + { + NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"received invalid argument(s)"}; + NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeInvalidArguments userInfo:userInfo]; + if (error) + { + *error = err; + } + if (completionHandler) + { + completionHandler(nil, NO, err); + } + return NO; + } + + // Begin opening + zipFile zip = unzOpen(path.fileSystemRepresentation); + if (zip == NULL) + { + NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"failed to open zip file"}; + NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFailedOpenZipFile userInfo:userInfo]; + if (error) + { + *error = err; + } + if (completionHandler) + { + completionHandler(nil, NO, err); + } + return NO; + } + + NSDictionary * fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil]; + unsigned long long fileSize = [fileAttributes[NSFileSize] unsignedLongLongValue]; + unsigned long long currentPosition = 0; + + unz_global_info globalInfo = {}; + unzGetGlobalInfo(zip, &globalInfo); + + // Begin unzipping + int ret = 0; + ret = unzGoToFirstFile(zip); + if (ret != UNZ_OK && ret != UNZ_END_OF_LIST_OF_FILE) + { + NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"failed to open first file in zip file"}; + NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFailedOpenFileInZip userInfo:userInfo]; + if (error) + { + *error = err; + } + if (completionHandler) + { + completionHandler(nil, NO, err); + } + return NO; + } + + BOOL success = YES; + BOOL canceled = NO; + int crc_ret = 0; + unsigned char buffer[4096] = {0}; + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSMutableArray *directoriesModificationDates = [[NSMutableArray alloc] init]; + + // Message delegate + if ([delegate respondsToSelector:@selector(zipArchiveWillUnzipArchiveAtPath:zipInfo:)]) { + [delegate zipArchiveWillUnzipArchiveAtPath:path zipInfo:globalInfo]; + } + if ([delegate respondsToSelector:@selector(zipArchiveProgressEvent:total:)]) { + [delegate zipArchiveProgressEvent:currentPosition total:fileSize]; + } + + NSInteger currentFileNumber = -1; + NSError *unzippingError; + do { + currentFileNumber++; + if (ret == UNZ_END_OF_LIST_OF_FILE) + break; + @autoreleasepool { + if (password.length == 0) { + ret = unzOpenCurrentFile(zip); + } else { + ret = unzOpenCurrentFilePassword(zip, [password cStringUsingEncoding:NSUTF8StringEncoding]); + } + + if (ret != UNZ_OK) { + unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFailedOpenFileInZip userInfo:@{NSLocalizedDescriptionKey: @"failed to open file in zip file"}]; + success = NO; + break; + } + + // Reading data and write to file + unz_file_info fileInfo; + memset(&fileInfo, 0, sizeof(unz_file_info)); + + ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0); + if (ret != UNZ_OK) { + unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFileInfoNotLoadable userInfo:@{NSLocalizedDescriptionKey: @"failed to retrieve info for file"}]; + success = NO; + unzCloseCurrentFile(zip); + break; + } + + currentPosition += fileInfo.compressed_size; + + // Message delegate + if ([delegate respondsToSelector:@selector(zipArchiveShouldUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) { + if (![delegate zipArchiveShouldUnzipFileAtIndex:currentFileNumber + totalFiles:(NSInteger)globalInfo.number_entry + archivePath:path + fileInfo:fileInfo]) { + success = NO; + canceled = YES; + break; + } + } + if ([delegate respondsToSelector:@selector(zipArchiveWillUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) { + [delegate zipArchiveWillUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry + archivePath:path fileInfo:fileInfo]; + } + if ([delegate respondsToSelector:@selector(zipArchiveProgressEvent:total:)]) { + [delegate zipArchiveProgressEvent:(NSInteger)currentPosition total:(NSInteger)fileSize]; + } + + char *filename = (char *)malloc(fileInfo.size_filename + 1); + if (filename == NULL) + { + success = NO; + break; + } + + unzGetCurrentFileInfo(zip, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0); + filename[fileInfo.size_filename] = '\0'; + + BOOL fileIsSymbolicLink = _fileIsSymbolicLink(&fileInfo); + + NSString * strPath = [SSZipArchive _filenameStringWithCString:filename size:fileInfo.size_filename]; + if ([strPath hasPrefix:@"__MACOSX/"]) { + // ignoring resource forks: https://superuser.com/questions/104500/what-is-macosx-folder + unzCloseCurrentFile(zip); + ret = unzGoToNextFile(zip); + free(filename); + continue; + } + if (!strPath.length) { + // if filename data is unsalvageable, we default to currentFileNumber + strPath = @(currentFileNumber).stringValue; + } + + // Check if it contains directory + BOOL isDirectory = NO; + if (filename[fileInfo.size_filename-1] == '/' || filename[fileInfo.size_filename-1] == '\\') { + isDirectory = YES; + } + free(filename); + + // Contains a path + if ([strPath rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"/\\"]].location != NSNotFound) { + strPath = [strPath stringByReplacingOccurrencesOfString:@"\\" withString:@"/"]; + } + + NSString *fullPath = [destination stringByAppendingPathComponent:strPath]; + NSError *err = nil; + NSDictionary *directoryAttr; + if (preserveAttributes) { + NSDate *modDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dos_date]; + directoryAttr = @{NSFileCreationDate: modDate, NSFileModificationDate: modDate}; + [directoriesModificationDates addObject: @{@"path": fullPath, @"modDate": modDate}]; + } + if (isDirectory) { + [fileManager createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:directoryAttr error:&err]; + } else { + [fileManager createDirectoryAtPath:fullPath.stringByDeletingLastPathComponent withIntermediateDirectories:YES attributes:directoryAttr error:&err]; + } + if (nil != err) { + if ([err.domain isEqualToString:NSCocoaErrorDomain] && + err.code == 640) { + unzippingError = err; + unzCloseCurrentFile(zip); + success = NO; + break; + } + NSLog(@"[SSZipArchive] Error: %@", err.localizedDescription); + } + + if ([fileManager fileExistsAtPath:fullPath] && !isDirectory && !overwrite) { + //FIXME: couldBe CRC Check? + unzCloseCurrentFile(zip); + ret = unzGoToNextFile(zip); + continue; + } + + if (!fileIsSymbolicLink) { + // ensure we are not creating stale file entries + int readBytes = unzReadCurrentFile(zip, buffer, 4096); + if (readBytes >= 0) { + FILE *fp = fopen(fullPath.fileSystemRepresentation, "wb"); + while (fp) { + if (readBytes > 0) { + if (0 == fwrite(buffer, readBytes, 1, fp)) { + if (ferror(fp)) { + NSString *message = [NSString stringWithFormat:@"Failed to write file (check your free space)"]; + NSLog(@"[SSZipArchive] %@", message); + success = NO; + unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFailedToWriteFile userInfo:@{NSLocalizedDescriptionKey: message}]; + break; + } + } + } else { + break; + } + readBytes = unzReadCurrentFile(zip, buffer, 4096); + if (readBytes < 0) { + // Let's assume error Z_DATA_ERROR is caused by an invalid password + // Let's assume other errors are caused by Content Not Readable + success = NO; + } + } + + if (fp) { + fclose(fp); + + if (nestedZipLevel + && [fullPath.pathExtension.lowercaseString isEqualToString:@"zip"] + && [self unzipFileAtPath:fullPath + toDestination:fullPath.stringByDeletingLastPathComponent + preserveAttributes:preserveAttributes + overwrite:overwrite + nestedZipLevel:nestedZipLevel - 1 + password:password + error:nil + delegate:nil + progressHandler:nil + completionHandler:nil]) { + [directoriesModificationDates removeLastObject]; + [[NSFileManager defaultManager] removeItemAtPath:fullPath error:nil]; + } else if (preserveAttributes) { + + // Set the original datetime property + if (fileInfo.dos_date != 0) { + NSDate *orgDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dos_date]; + NSDictionary *attr = @{NSFileModificationDate: orgDate}; + + if (attr) { + if (![fileManager setAttributes:attr ofItemAtPath:fullPath error:nil]) { + // Can't set attributes + NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting modification date"); + } + } + } + + // Set the original permissions on the file (+read/write to solve #293) + uLong permissions = fileInfo.external_fa >> 16 | 0b110000000; + if (permissions != 0) { + // Store it into a NSNumber + NSNumber *permissionsValue = @(permissions); + + // Retrieve any existing attributes + NSMutableDictionary *attrs = [[NSMutableDictionary alloc] initWithDictionary:[fileManager attributesOfItemAtPath:fullPath error:nil]]; + + // Set the value in the attributes dict + attrs[NSFilePosixPermissions] = permissionsValue; + + // Update attributes + if (![fileManager setAttributes:attrs ofItemAtPath:fullPath error:nil]) { + // Unable to set the permissions attribute + NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting permissions"); + } + } + } + } + else + { + // if we couldn't open file descriptor we can validate global errno to see the reason + if (errno == ENOSPC) { + NSError *enospcError = [NSError errorWithDomain:NSPOSIXErrorDomain + code:ENOSPC + userInfo:nil]; + unzippingError = enospcError; + unzCloseCurrentFile(zip); + success = NO; + break; + } + } + } else { + // Let's assume error Z_DATA_ERROR is caused by an invalid password + // Let's assume other errors are caused by Content Not Readable + success = NO; + break; + } + } + else + { + // Assemble the path for the symbolic link + NSMutableString *destinationPath = [NSMutableString string]; + int bytesRead = 0; + while ((bytesRead = unzReadCurrentFile(zip, buffer, 4096)) > 0) + { + buffer[bytesRead] = 0; + [destinationPath appendString:@((const char *)buffer)]; + } + if (bytesRead < 0) { + // Let's assume error Z_DATA_ERROR is caused by an invalid password + // Let's assume other errors are caused by Content Not Readable + success = NO; + break; + } + + // Check if the symlink exists and delete it if we're overwriting + if (overwrite) + { + if ([fileManager fileExistsAtPath:fullPath]) + { + NSError *error = nil; + BOOL removeSuccess = [fileManager removeItemAtPath:fullPath error:&error]; + if (!removeSuccess) + { + NSString *message = [NSString stringWithFormat:@"Failed to delete existing symbolic link at \"%@\"", error.localizedDescription]; + NSLog(@"[SSZipArchive] %@", message); + success = NO; + unzippingError = [NSError errorWithDomain:SSZipArchiveErrorDomain code:error.code userInfo:@{NSLocalizedDescriptionKey: message}]; + } + } + } + + // Create the symbolic link (making sure it stays relative if it was relative before) + int symlinkError = symlink([destinationPath cStringUsingEncoding:NSUTF8StringEncoding], + [fullPath cStringUsingEncoding:NSUTF8StringEncoding]); + + if (symlinkError != 0) + { + // Bubble the error up to the completion handler + NSString *message = [NSString stringWithFormat:@"Failed to create symbolic link at \"%@\" to \"%@\" - symlink() error code: %d", fullPath, destinationPath, errno]; + NSLog(@"[SSZipArchive] %@", message); + success = NO; + unzippingError = [NSError errorWithDomain:NSPOSIXErrorDomain code:symlinkError userInfo:@{NSLocalizedDescriptionKey: message}]; + } + } + + crc_ret = unzCloseCurrentFile(zip); + if (crc_ret == UNZ_CRCERROR) { + // CRC ERROR + success = NO; + break; + } + ret = unzGoToNextFile(zip); + + // Message delegate + if ([delegate respondsToSelector:@selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) { + [delegate zipArchiveDidUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry + archivePath:path fileInfo:fileInfo]; + } else if ([delegate respondsToSelector: @selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:unzippedFilePath:)]) { + [delegate zipArchiveDidUnzipFileAtIndex: currentFileNumber totalFiles: (NSInteger)globalInfo.number_entry + archivePath:path unzippedFilePath: fullPath]; + } + + if (progressHandler) + { + progressHandler(strPath, fileInfo, currentFileNumber, globalInfo.number_entry); + } + } + } while (ret == UNZ_OK && success); + + // Close + unzClose(zip); + + // The process of decompressing the .zip archive causes the modification times on the folders + // to be set to the present time. So, when we are done, they need to be explicitly set. + // set the modification date on all of the directories. + if (success && preserveAttributes) { + NSError * err = nil; + for (NSDictionary * d in directoriesModificationDates) { + if (![[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate: d[@"modDate"]} ofItemAtPath:d[@"path"] error:&err]) { + NSLog(@"[SSZipArchive] Set attributes failed for directory: %@.", d[@"path"]); + } + if (err) { + NSLog(@"[SSZipArchive] Error setting directory file modification date attribute: %@", err.localizedDescription); + } + } + } + + // Message delegate + if (success && [delegate respondsToSelector:@selector(zipArchiveDidUnzipArchiveAtPath:zipInfo:unzippedPath:)]) { + [delegate zipArchiveDidUnzipArchiveAtPath:path zipInfo:globalInfo unzippedPath:destination]; + } + // final progress event = 100% + if (!canceled && [delegate respondsToSelector:@selector(zipArchiveProgressEvent:total:)]) { + [delegate zipArchiveProgressEvent:fileSize total:fileSize]; + } + + NSError *retErr = nil; + if (crc_ret == UNZ_CRCERROR) + { + NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"crc check failed for file"}; + retErr = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFileInfoNotLoadable userInfo:userInfo]; + } + + if (error) { + if (unzippingError) { + *error = unzippingError; + } + else { + *error = retErr; + } + } + if (completionHandler) + { + if (unzippingError) { + completionHandler(path, success, unzippingError); + } + else + { + completionHandler(path, success, retErr); + } + } + return success; +} + +#pragma mark - Zipping ++ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)paths +{ + return [SSZipArchive createZipFileAtPath:path withFilesAtPaths:paths withPassword:nil]; +} ++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath { + return [SSZipArchive createZipFileAtPath:path withContentsOfDirectory:directoryPath withPassword:nil]; +} + ++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath keepParentDirectory:(BOOL)keepParentDirectory { + return [SSZipArchive createZipFileAtPath:path withContentsOfDirectory:directoryPath keepParentDirectory:keepParentDirectory withPassword:nil]; +} + ++ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)paths withPassword:(NSString *)password +{ + SSZipArchive *zipArchive = [[SSZipArchive alloc] initWithPath:path]; + BOOL success = [zipArchive open]; + if (success) { + for (NSString *filePath in paths) { + success &= [zipArchive writeFile:filePath withPassword:password]; + } + success &= [zipArchive close]; + } + return success; +} + ++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath withPassword:(nullable NSString *)password { + return [SSZipArchive createZipFileAtPath:path withContentsOfDirectory:directoryPath keepParentDirectory:NO withPassword:password]; +} + + ++ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath keepParentDirectory:(BOOL)keepParentDirectory withPassword:(nullable NSString *)password { + return [SSZipArchive createZipFileAtPath:path + withContentsOfDirectory:directoryPath + keepParentDirectory:keepParentDirectory + withPassword:password + andProgressHandler:nil + ]; +} + ++ (BOOL)createZipFileAtPath:(NSString *)path + withContentsOfDirectory:(NSString *)directoryPath + keepParentDirectory:(BOOL)keepParentDirectory + withPassword:(nullable NSString *)password + andProgressHandler:(void(^ _Nullable)(NSUInteger entryNumber, NSUInteger total))progressHandler { + return [self createZipFileAtPath:path withContentsOfDirectory:directoryPath keepParentDirectory:keepParentDirectory compressionLevel:Z_DEFAULT_COMPRESSION password:password AES:YES progressHandler:progressHandler]; +} + ++ (BOOL)createZipFileAtPath:(NSString *)path + withContentsOfDirectory:(NSString *)directoryPath + keepParentDirectory:(BOOL)keepParentDirectory + compressionLevel:(int)compressionLevel + password:(nullable NSString *)password + AES:(BOOL)aes + progressHandler:(void(^ _Nullable)(NSUInteger entryNumber, NSUInteger total))progressHandler { + + SSZipArchive *zipArchive = [[SSZipArchive alloc] initWithPath:path]; + BOOL success = [zipArchive open]; + if (success) { + // use a local fileManager (queue/thread compatibility) + NSFileManager *fileManager = [[NSFileManager alloc] init]; + NSDirectoryEnumerator *dirEnumerator = [fileManager enumeratorAtPath:directoryPath]; + NSArray *allObjects = dirEnumerator.allObjects; + NSUInteger total = allObjects.count, complete = 0; + NSString *fileName; + for (fileName in allObjects) { + BOOL isDir; + NSString *fullFilePath = [directoryPath stringByAppendingPathComponent:fileName]; + [fileManager fileExistsAtPath:fullFilePath isDirectory:&isDir]; + + if (keepParentDirectory) + { + fileName = [directoryPath.lastPathComponent stringByAppendingPathComponent:fileName]; + } + + if (!isDir) { + success &= [zipArchive writeFileAtPath:fullFilePath withFileName:fileName compressionLevel:compressionLevel password:password AES:aes]; + } + else + { + if ([[NSFileManager defaultManager] subpathsOfDirectoryAtPath:fullFilePath error:nil].count == 0) + { + success &= [zipArchive writeFolderAtPath:fullFilePath withFolderName:fileName withPassword:password]; + } + } + complete++; + if (progressHandler) { + progressHandler(complete, total); + } + } + success &= [zipArchive close]; + } + return success; +} + +// disabling `init` because designated initializer is `initWithPath:` +- (instancetype)init { @throw nil; } + +// designated initializer +- (instancetype)initWithPath:(NSString *)path +{ + if ((self = [super init])) { + _path = [path copy]; + } + return self; +} + + +- (BOOL)open +{ + NSAssert((_zip == NULL), @"Attempting to open an archive which is already open"); + _zip = zipOpen(_path.fileSystemRepresentation, APPEND_STATUS_CREATE); + return (NULL != _zip); +} + +- (BOOL)writeFolderAtPath:(NSString *)path withFolderName:(NSString *)folderName withPassword:(nullable NSString *)password +{ + NSAssert((_zip != NULL), @"Attempting to write to an archive which was never opened"); + + zip_fileinfo zipInfo = {}; + + [SSZipArchive zipInfo:&zipInfo setAttributesOfItemAtPath:path]; + + int error = _zipOpenEntry(_zip, [folderName stringByAppendingString:@"/"], &zipInfo, Z_NO_COMPRESSION, password, 0); + const void *buffer = NULL; + zipWriteInFileInZip(_zip, buffer, 0); + zipCloseFileInZip(_zip); + return error == ZIP_OK; +} + +- (BOOL)writeFile:(NSString *)path withPassword:(nullable NSString *)password +{ + return [self writeFileAtPath:path withFileName:nil withPassword:password]; +} + +- (BOOL)writeFileAtPath:(NSString *)path withFileName:(nullable NSString *)fileName withPassword:(nullable NSString *)password +{ + return [self writeFileAtPath:path withFileName:fileName compressionLevel:Z_DEFAULT_COMPRESSION password:password AES:YES]; +} + +// supports writing files with logical folder/directory structure +// *path* is the absolute path of the file that will be compressed +// *fileName* is the relative name of the file how it is stored within the zip e.g. /folder/subfolder/text1.txt +- (BOOL)writeFileAtPath:(NSString *)path withFileName:(nullable NSString *)fileName compressionLevel:(int)compressionLevel password:(nullable NSString *)password AES:(BOOL)aes +{ + NSAssert((_zip != NULL), @"Attempting to write to an archive which was never opened"); + + FILE *input = fopen(path.fileSystemRepresentation, "r"); + if (NULL == input) { + return NO; + } + + if (!fileName) { + fileName = path.lastPathComponent; + } + + zip_fileinfo zipInfo = {}; + + [SSZipArchive zipInfo:&zipInfo setAttributesOfItemAtPath:path]; + + void *buffer = malloc(CHUNK); + if (buffer == NULL) + { + fclose(input); + return NO; + } + + int error = _zipOpenEntry(_zip, fileName, &zipInfo, compressionLevel, password, aes); + + while (!feof(input) && !ferror(input)) + { + unsigned int len = (unsigned int) fread(buffer, 1, CHUNK, input); + zipWriteInFileInZip(_zip, buffer, len); + } + + zipCloseFileInZip(_zip); + free(buffer); + fclose(input); + return error == ZIP_OK; +} + +- (BOOL)writeData:(NSData *)data filename:(nullable NSString *)filename withPassword:(nullable NSString *)password +{ + return [self writeData:data filename:filename compressionLevel:Z_DEFAULT_COMPRESSION password:password AES:YES]; +} + +- (BOOL)writeData:(NSData *)data filename:(nullable NSString *)filename compressionLevel:(int)compressionLevel password:(nullable NSString *)password AES:(BOOL)aes +{ + if (!_zip) { + return NO; + } + if (!data) { + return NO; + } + zip_fileinfo zipInfo = {}; + [SSZipArchive zipInfo:&zipInfo setDate:[NSDate date]]; + + int error = _zipOpenEntry(_zip, filename, &zipInfo, compressionLevel, password, aes); + + zipWriteInFileInZip(_zip, data.bytes, (unsigned int)data.length); + + zipCloseFileInZip(_zip); + return error == ZIP_OK; +} + +- (BOOL)close +{ + NSAssert((_zip != NULL), @"[SSZipArchive] Attempting to close an archive which was never opened"); + int error = zipClose(_zip, NULL); + _zip = nil; + return error == UNZ_OK; +} + +#pragma mark - Private + ++ (NSString *)_filenameStringWithCString:(const char *)filename size:(uint16_t)size_filename +{ + // attempting unicode encoding + NSString * strPath = @(filename); + if (strPath) { + return strPath; + } + + // if filename is non-unicode, detect and transform Encoding + NSData *data = [NSData dataWithBytes:(const void *)filename length:sizeof(unsigned char) * size_filename]; +#ifdef __MAC_10_13 + // Xcode 9+ + if (@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)) { + // supported encodings are in [NSString availableStringEncodings] + [NSString stringEncodingForData:data encodingOptions:nil convertedString:&strPath usedLossyConversion:nil]; + } +#else + // Xcode 8- + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_9_2) { + // supported encodings are in [NSString availableStringEncodings] + [NSString stringEncodingForData:data encodingOptions:nil convertedString:&strPath usedLossyConversion:nil]; + } +#endif + else { + // fallback to a simple manual detect for macOS 10.9 or older + NSArray *encodings = @[@(kCFStringEncodingGB_18030_2000), @(kCFStringEncodingShiftJIS)]; + for (NSNumber *encoding in encodings) { + strPath = [NSString stringWithCString:filename encoding:(NSStringEncoding)CFStringConvertEncodingToNSStringEncoding(encoding.unsignedIntValue)]; + if (strPath) { + break; + } + } + } + if (strPath) { + return strPath; + } + + // if filename encoding is non-detected, we default to something based on data + // _hexString is more readable than _base64RFC4648 for debugging unknown encodings + strPath = [data _hexString]; + return strPath; +} + ++ (void)zipInfo:(zip_fileinfo *)zipInfo setAttributesOfItemAtPath:(NSString *)path +{ + NSDictionary *attr = [[NSFileManager defaultManager] attributesOfItemAtPath:path error: nil]; + if (attr) + { + NSDate *fileDate = (NSDate *)attr[NSFileModificationDate]; + if (fileDate) + { + [self zipInfo:zipInfo setDate:fileDate]; + } + + // Write permissions into the external attributes, for details on this see here: http://unix.stackexchange.com/a/14727 + // Get the permissions value from the files attributes + NSNumber *permissionsValue = (NSNumber *)attr[NSFilePosixPermissions]; + if (permissionsValue != nil) { + // Get the short value for the permissions + short permissionsShort = permissionsValue.shortValue; + + // Convert this into an octal by adding 010000, 010000 being the flag for a regular file + NSInteger permissionsOctal = 0100000 + permissionsShort; + + // Convert this into a long value + uLong permissionsLong = @(permissionsOctal).unsignedLongValue; + + // Store this into the external file attributes once it has been shifted 16 places left to form part of the second from last byte + + // Casted back to an unsigned int to match type of external_fa in minizip + zipInfo->external_fa = (unsigned int)(permissionsLong << 16L); + } + } +} + ++ (void)zipInfo:(zip_fileinfo *)zipInfo setDate:(NSDate *)date +{ + NSCalendar *currentCalendar = SSZipArchive._gregorian; + NSCalendarUnit flags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond; + NSDateComponents *components = [currentCalendar components:flags fromDate:date]; + struct tm tmz_date; + tmz_date.tm_sec = (unsigned int)components.second; + tmz_date.tm_min = (unsigned int)components.minute; + tmz_date.tm_hour = (unsigned int)components.hour; + tmz_date.tm_mday = (unsigned int)components.day; + // ISO/IEC 9899 struct tm is 0-indexed for January but NSDateComponents for gregorianCalendar is 1-indexed for January + tmz_date.tm_mon = (unsigned int)components.month - 1; + // ISO/IEC 9899 struct tm is 0-indexed for AD 1900 but NSDateComponents for gregorianCalendar is 1-indexed for AD 1 + tmz_date.tm_year = (unsigned int)components.year - 1900; + zipInfo->dos_date = tm_to_dosdate(&tmz_date); +} + ++ (NSCalendar *)_gregorian +{ + static NSCalendar *gregorian; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + }); + + return gregorian; +} + +// Format from http://newsgroups.derkeiler.com/Archive/Comp/comp.os.msdos.programmer/2009-04/msg00060.html +// Two consecutive words, or a longword, YYYYYYYMMMMDDDDD hhhhhmmmmmmsssss +// YYYYYYY is years from 1980 = 0 +// sssss is (seconds/2). +// +// 3658 = 0011 0110 0101 1000 = 0011011 0010 11000 = 27 2 24 = 2007-02-24 +// 7423 = 0111 0100 0010 0011 - 01110 100001 00011 = 14 33 3 = 14:33:06 ++ (NSDate *)_dateWithMSDOSFormat:(UInt32)msdosDateTime +{ + // the whole `_dateWithMSDOSFormat:` method is equivalent but faster than this one line, + // essentially because `mktime` is slow: + //NSDate *date = [NSDate dateWithTimeIntervalSince1970:dosdate_to_time_t(msdosDateTime)]; + static const UInt32 kYearMask = 0xFE000000; + static const UInt32 kMonthMask = 0x1E00000; + static const UInt32 kDayMask = 0x1F0000; + static const UInt32 kHourMask = 0xF800; + static const UInt32 kMinuteMask = 0x7E0; + static const UInt32 kSecondMask = 0x1F; + + NSAssert(0xFFFFFFFF == (kYearMask | kMonthMask | kDayMask | kHourMask | kMinuteMask | kSecondMask), @"[SSZipArchive] MSDOS date masks don't add up"); + + NSDateComponents *components = [[NSDateComponents alloc] init]; + components.year = 1980 + ((msdosDateTime & kYearMask) >> 25); + components.month = (msdosDateTime & kMonthMask) >> 21; + components.day = (msdosDateTime & kDayMask) >> 16; + components.hour = (msdosDateTime & kHourMask) >> 11; + components.minute = (msdosDateTime & kMinuteMask) >> 5; + components.second = (msdosDateTime & kSecondMask) * 2; + + NSDate *date = [self._gregorian dateFromComponents:components]; + return date; +} + +@end + +int _zipOpenEntry(zipFile entry, NSString *name, const zip_fileinfo *zipfi, int level, NSString *password, BOOL aes) +{ + return zipOpenNewFileInZip5(entry, name.fileSystemRepresentation, zipfi, NULL, 0, NULL, 0, NULL, 0, 0, Z_DEFLATED, level, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, password.UTF8String, aes, 0); +} + +#pragma mark - Private tools for file info + +BOOL _fileIsSymbolicLink(const unz_file_info *fileInfo) +{ + // + // Determine whether this is a symbolic link: + // - File is stored with 'version made by' value of UNIX (3), + // as per http://www.pkware.com/documents/casestudies/APPNOTE.TXT + // in the upper byte of the version field. + // - BSD4.4 st_mode constants are stored in the high 16 bits of the + // external file attributes (defacto standard, verified against libarchive) + // + // The original constants can be found here: + // http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.4BSD/usr/include/sys/stat.h + // + const uLong ZipUNIXVersion = 3; + const uLong BSD_SFMT = 0170000; + const uLong BSD_IFLNK = 0120000; + + BOOL fileIsSymbolicLink = ((fileInfo->version >> 8) == ZipUNIXVersion) && BSD_IFLNK == (BSD_SFMT & (fileInfo->external_fa >> 16)); + return fileIsSymbolicLink; +} + +#pragma mark - Private tools for unreadable encodings + +@implementation NSData (SSZipArchive) + +// `base64EncodedStringWithOptions` uses a base64 alphabet with '+' and '/'. +// we got those alternatives to make it compatible with filenames: https://en.wikipedia.org/wiki/Base64 +// * modified Base64 encoding for IMAP mailbox names (RFC 3501): uses '+' and ',' +// * modified Base64 for URL and filenames (RFC 4648): uses '-' and '_' +- (NSString *)_base64RFC4648 +{ + NSString *strName = [self base64EncodedStringWithOptions:0]; + strName = [strName stringByReplacingOccurrencesOfString:@"+" withString:@"-"]; + strName = [strName stringByReplacingOccurrencesOfString:@"/" withString:@"_"]; + return strName; +} + +// initWithBytesNoCopy from NSProgrammer, Jan 25 '12: https://stackoverflow.com/a/9009321/1033581 +// hexChars from Peter, Aug 19 '14: https://stackoverflow.com/a/25378464/1033581 +// not implemented as too lengthy: a potential mapping improvement from Moose, Nov 3 '15: https://stackoverflow.com/a/33501154/1033581 +- (NSString *)_hexString +{ + const char *hexChars = "0123456789ABCDEF"; + NSUInteger length = self.length; + const unsigned char *bytes = self.bytes; + char *chars = malloc(length * 2); + if (chars == NULL) { + // we directly raise an exception instead of using NSAssert to make sure assertion is not disabled as this is irrecoverable + [NSException raise:@"NSInternalInconsistencyException" format:@"failed malloc" arguments:nil]; + return nil; + } + char *s = chars; + NSUInteger i = length; + while (i--) { + *s++ = hexChars[*bytes >> 4]; + *s++ = hexChars[*bytes & 0xF]; + bytes++; + } + NSString *str = [[NSString alloc] initWithBytesNoCopy:chars + length:length * 2 + encoding:NSASCIIStringEncoding + freeWhenDone:YES]; + return str; +} + +@end diff --git a/SSZipArchive/SSZipCommon.h b/SSZipArchive/SSZipCommon.h new file mode 100644 index 0000000..91670e1 --- /dev/null +++ b/SSZipArchive/SSZipCommon.h @@ -0,0 +1,66 @@ +#ifndef SSZipCommon +#define SSZipCommon + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info64_s +{ + uint64_t number_entry; /* total number of entries in the central dir on this disk */ + uint32_t number_disk_with_CD; /* number the the disk with central dir, used for spanning ZIP*/ + uint16_t size_comment; /* size of the global comment of the zipfile */ +} unz_global_info64; + +typedef struct unz_global_info_s +{ + uint32_t number_entry; /* total number of entries in the central dir on this disk */ + uint32_t number_disk_with_CD; /* number the the disk with central dir, used for spanning ZIP*/ + uint16_t size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + +/* unz_file_info contain information about a file in the zipfile */ +/* https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT */ +typedef struct unz_file_info64_s +{ + uint16_t version; /* version made by 2 bytes */ + uint16_t version_needed; /* version needed to extract 2 bytes */ + uint16_t flag; /* general purpose bit flag 2 bytes */ + uint16_t compression_method; /* compression method 2 bytes */ + uint32_t dos_date; /* last mod file date in Dos fmt 4 bytes */ + uint32_t crc; /* crc-32 4 bytes */ + uint64_t compressed_size; /* compressed size 8 bytes */ + uint64_t uncompressed_size; /* uncompressed size 8 bytes */ + uint16_t size_filename; /* filename length 2 bytes */ + uint16_t size_file_extra; /* extra field length 2 bytes */ + uint16_t size_file_comment; /* file comment length 2 bytes */ + + uint32_t disk_num_start; /* disk number start 4 bytes */ + uint16_t internal_fa; /* internal file attributes 2 bytes */ + uint32_t external_fa; /* external file attributes 4 bytes */ + + uint64_t disk_offset; + + uint16_t size_file_extra_internal; +} unz_file_info64; + +typedef struct unz_file_info_s +{ + uint16_t version; /* version made by 2 bytes */ + uint16_t version_needed; /* version needed to extract 2 bytes */ + uint16_t flag; /* general purpose bit flag 2 bytes */ + uint16_t compression_method; /* compression method 2 bytes */ + uint32_t dos_date; /* last mod file date in Dos fmt 4 bytes */ + uint32_t crc; /* crc-32 4 bytes */ + uint32_t compressed_size; /* compressed size 4 bytes */ + uint32_t uncompressed_size; /* uncompressed size 4 bytes */ + uint16_t size_filename; /* filename length 2 bytes */ + uint16_t size_file_extra; /* extra field length 2 bytes */ + uint16_t size_file_comment; /* file comment length 2 bytes */ + + uint16_t disk_num_start; /* disk number start 2 bytes */ + uint16_t internal_fa; /* internal file attributes 2 bytes */ + uint32_t external_fa; /* external file attributes 4 bytes */ + + uint64_t disk_offset; +} unz_file_info; + +#endif diff --git a/SSZipArchive/ZipArchive.h b/SSZipArchive/ZipArchive.h new file mode 100644 index 0000000..7a53df8 --- /dev/null +++ b/SSZipArchive/ZipArchive.h @@ -0,0 +1,19 @@ +// +// ZipArchive.h +// ZipArchive +// +// Created by Serhii Mumriak on 12/1/15. +// Copyright © 2015 smumryak. All rights reserved. +// + +#import + +//! Project version number for ZipArchive. +FOUNDATION_EXPORT double ZipArchiveVersionNumber; + +//! Project version string for ZipArchive. +FOUNDATION_EXPORT const unsigned char ZipArchiveVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +#import "SSZipArchive.h" diff --git a/SSZipArchive/minizip/aes/aes.h b/SSZipArchive/minizip/aes/aes.h new file mode 100644 index 0000000..92e23c2 --- /dev/null +++ b/SSZipArchive/minizip/aes/aes.h @@ -0,0 +1,270 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + + This file contains the definitions required to use AES in C. See aesopt.h + for optimisation details. +*/ + +#ifndef _AES_H +#define _AES_H + +#include + +/* This include is used to find 8 & 32 bit unsigned integer types */ +#include "brg_types.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#define AES_128 /* if a fast 128 bit key scheduler is needed */ +#define AES_192 /* if a fast 192 bit key scheduler is needed */ +#define AES_256 /* if a fast 256 bit key scheduler is needed */ +#define AES_VAR /* if variable key size scheduler is needed */ +#define AES_MODES /* if support is needed for modes */ + +/* The following must also be set in assembler files if being used */ + +#define AES_ENCRYPT /* if support for encryption is needed */ +#define AES_DECRYPT /* if support for decryption is needed */ + +#define AES_BLOCK_SIZE 16 /* the AES block size in bytes */ +#define N_COLS 4 /* the number of columns in the state */ + +/* The key schedule length is 11, 13 or 15 16-byte blocks for 128, */ +/* 192 or 256-bit keys respectively. That is 176, 208 or 240 bytes */ +/* or 44, 52 or 60 32-bit words. */ + +#if defined( AES_VAR ) || defined( AES_256 ) +#define KS_LENGTH 60 +#elif defined( AES_192 ) +#define KS_LENGTH 52 +#else +#define KS_LENGTH 44 +#endif + +#define AES_RETURN INT_RETURN + +/* the character array 'inf' in the following structures is used */ +/* to hold AES context information. This AES code uses cx->inf.b[0] */ +/* to hold the number of rounds multiplied by 16. The other three */ +/* elements can be used by code that implements additional modes */ + +typedef union +{ uint32_t l; + uint8_t b[4]; +} aes_inf; + +#ifdef _MSC_VER +# pragma warning( disable : 4324 ) +#endif + +#if defined(_MSC_VER) && defined(_WIN64) +#define ALIGNED_(x) __declspec(align(x)) +#elif defined(__GNUC__) && defined(__x86_64__) +#define ALIGNED_(x) __attribute__ ((aligned(x))) +#else +#define ALIGNED_(x) +#endif + +typedef struct ALIGNED_(16) +{ uint32_t ks[KS_LENGTH]; + aes_inf inf; +} aes_encrypt_ctx; + +typedef struct ALIGNED_(16) +{ uint32_t ks[KS_LENGTH]; + aes_inf inf; +} aes_decrypt_ctx; + +#ifdef _MSC_VER +# pragma warning( default : 4324 ) +#endif + +/* This routine must be called before first use if non-static */ +/* tables are being used */ + +AES_RETURN aes_init(void); + +/* Key lengths in the range 16 <= key_len <= 32 are given in bytes, */ +/* those in the range 128 <= key_len <= 256 are given in bits */ + +#if defined( AES_ENCRYPT ) + +#if defined( AES_128 ) || defined( AES_VAR) +AES_RETURN aes_encrypt_key128(const unsigned char *key, aes_encrypt_ctx cx[1]); +#endif + +#if defined( AES_192 ) || defined( AES_VAR) +AES_RETURN aes_encrypt_key192(const unsigned char *key, aes_encrypt_ctx cx[1]); +#endif + +#if defined( AES_256 ) || defined( AES_VAR) +AES_RETURN aes_encrypt_key256(const unsigned char *key, aes_encrypt_ctx cx[1]); +#endif + +#if defined( AES_VAR ) +AES_RETURN aes_encrypt_key(const unsigned char *key, int key_len, aes_encrypt_ctx cx[1]); +#endif + +AES_RETURN aes_encrypt(const unsigned char *in, unsigned char *out, const aes_encrypt_ctx cx[1]); + +#endif + +#if defined( AES_DECRYPT ) + +#if defined( AES_128 ) || defined( AES_VAR) +AES_RETURN aes_decrypt_key128(const unsigned char *key, aes_decrypt_ctx cx[1]); +#endif + +#if defined( AES_192 ) || defined( AES_VAR) +AES_RETURN aes_decrypt_key192(const unsigned char *key, aes_decrypt_ctx cx[1]); +#endif + +#if defined( AES_256 ) || defined( AES_VAR) +AES_RETURN aes_decrypt_key256(const unsigned char *key, aes_decrypt_ctx cx[1]); +#endif + +#if defined( AES_VAR ) +AES_RETURN aes_decrypt_key(const unsigned char *key, int key_len, aes_decrypt_ctx cx[1]); +#endif + +AES_RETURN aes_decrypt(const unsigned char *in, unsigned char *out, const aes_decrypt_ctx cx[1]); + +#endif + +#if defined( AES_MODES ) + +/* Multiple calls to the following subroutines for multiple block */ +/* ECB, CBC, CFB, OFB and CTR mode encryption can be used to handle */ +/* long messages incrementally provided that the context AND the iv */ +/* are preserved between all such calls. For the ECB and CBC modes */ +/* each individual call within a series of incremental calls must */ +/* process only full blocks (i.e. len must be a multiple of 16) but */ +/* the CFB, OFB and CTR mode calls can handle multiple incremental */ +/* calls of any length. Each mode is reset when a new AES key is */ +/* set but ECB needs no reset and CBC can be reset without setting */ +/* a new key by setting a new IV value. To reset CFB, OFB and CTR */ +/* without setting the key, aes_mode_reset() must be called and the */ +/* IV must be set. NOTE: All these calls update the IV on exit so */ +/* this has to be reset if a new operation with the same IV as the */ +/* previous one is required (or decryption follows encryption with */ +/* the same IV array). */ + +AES_RETURN aes_test_alignment_detection(unsigned int n); + +AES_RETURN aes_ecb_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, const aes_encrypt_ctx cx[1]); + +AES_RETURN aes_ecb_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, const aes_decrypt_ctx cx[1]); + +AES_RETURN aes_cbc_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, const aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cbc_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, const aes_decrypt_ctx cx[1]); + +AES_RETURN aes_mode_reset(aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cfb_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cfb_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]); + +#define aes_ofb_encrypt aes_ofb_crypt +#define aes_ofb_decrypt aes_ofb_crypt + +AES_RETURN aes_ofb_crypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]); + +typedef void cbuf_inc(unsigned char *cbuf); + +#define aes_ctr_encrypt aes_ctr_crypt +#define aes_ctr_decrypt aes_ctr_crypt + +AES_RETURN aes_ctr_crypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *cbuf, cbuf_inc ctr_inc, aes_encrypt_ctx cx[1]); + +#endif + +#if 0 +# define ADD_AESNI_MODE_CALLS +#endif + +#if 0 && defined( ADD_AESNI_MODE_CALLS ) +# define USE_AES_CONTEXT +#endif + +#ifdef ADD_AESNI_MODE_CALLS +# ifdef USE_AES_CONTEXT + +AES_RETURN aes_CBC_encrypt(const unsigned char *in, + unsigned char *out, + unsigned char ivec[16], + unsigned long length, + const aes_encrypt_ctx cx[1]); + +AES_RETURN aes_CBC_decrypt(const unsigned char *in, + unsigned char *out, + unsigned char ivec[16], + unsigned long length, + const aes_decrypt_ctx cx[1]); + +AES_RETURN AES_CTR_encrypt(const unsigned char *in, + unsigned char *out, + const unsigned char ivec[8], + const unsigned char nonce[4], + unsigned long length, + const aes_encrypt_ctx cx[1]); + +# else + +void aes_CBC_encrypt(const unsigned char *in, + unsigned char *out, + unsigned char ivec[16], + unsigned long length, + unsigned char *key, + int number_of_rounds); + +void aes_CBC_decrypt(const unsigned char *in, + unsigned char *out, + unsigned char ivec[16], + unsigned long length, + unsigned char *key, + int number_of_rounds); + +void AES_CTR_encrypt(const unsigned char *in, + unsigned char *out, + const unsigned char ivec[8], + const unsigned char nonce[4], + unsigned long length, + const unsigned char *key, + int number_of_rounds); + +# endif +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/SSZipArchive/minizip/aes/aes_ni.c b/SSZipArchive/minizip/aes/aes_ni.c new file mode 100644 index 0000000..8afae91 --- /dev/null +++ b/SSZipArchive/minizip/aes/aes_ni.c @@ -0,0 +1,687 @@ +/* +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 09/09/2014 +*/ + +#include "aes_ni.h" + +#if defined( USE_INTEL_AES_IF_PRESENT ) + +#if defined(_MSC_VER) + +#include +#pragma intrinsic(__cpuid) +#define INLINE __inline + +INLINE int has_aes_ni(void) +{ + static int test = -1; + if(test < 0) + { + int cpu_info[4]; + __cpuid(cpu_info, 1); + test = cpu_info[2] & 0x02000000; + } + return test; +} + +#elif defined( __GNUC__ ) + +#include + +#if !defined(__clang__) +#pragma GCC target ("ssse3") +#pragma GCC target ("sse4.1") +#pragma GCC target ("aes") +#endif + +#include +#define INLINE static __inline + +INLINE int has_aes_ni() +{ + static int test = -1; + if(test < 0) + { + unsigned int a, b, c, d; + if(!__get_cpuid(1, &a, &b, &c, &d)) + test = 0; + else + test = (c & 0x2000000); + } + return test; +} + +#else +#error AES New Instructions require Microsoft, Intel, GNU C, or CLANG +#endif + +INLINE __m128i aes_128_assist(__m128i t1, __m128i t2) +{ + __m128i t3; + t2 = _mm_shuffle_epi32(t2, 0xff); + t3 = _mm_slli_si128(t1, 0x4); + t1 = _mm_xor_si128(t1, t3); + t3 = _mm_slli_si128(t3, 0x4); + t1 = _mm_xor_si128(t1, t3); + t3 = _mm_slli_si128(t3, 0x4); + t1 = _mm_xor_si128(t1, t3); + t1 = _mm_xor_si128(t1, t2); + return t1; +} + +AES_RETURN aes_ni(encrypt_key128)(const unsigned char *key, aes_encrypt_ctx cx[1]) +{ + __m128i t1, t2; + __m128i *ks = (__m128i*)cx->ks; + + if(!has_aes_ni()) + { + return aes_xi(encrypt_key128)(key, cx); + } + + t1 = _mm_loadu_si128((__m128i*)key); + + ks[0] = t1; + + t2 = _mm_aeskeygenassist_si128(t1, 0x1); + t1 = aes_128_assist(t1, t2); + ks[1] = t1; + + t2 = _mm_aeskeygenassist_si128(t1, 0x2); + t1 = aes_128_assist(t1, t2); + ks[2] = t1; + + t2 = _mm_aeskeygenassist_si128(t1, 0x4); + t1 = aes_128_assist(t1, t2); + ks[3] = t1; + + t2 = _mm_aeskeygenassist_si128(t1, 0x8); + t1 = aes_128_assist(t1, t2); + ks[4] = t1; + + t2 = _mm_aeskeygenassist_si128(t1, 0x10); + t1 = aes_128_assist(t1, t2); + ks[5] = t1; + + t2 = _mm_aeskeygenassist_si128(t1, 0x20); + t1 = aes_128_assist(t1, t2); + ks[6] = t1; + + t2 = _mm_aeskeygenassist_si128(t1, 0x40); + t1 = aes_128_assist(t1, t2); + ks[7] = t1; + + t2 = _mm_aeskeygenassist_si128(t1, 0x80); + t1 = aes_128_assist(t1, t2); + ks[8] = t1; + + t2 = _mm_aeskeygenassist_si128(t1, 0x1b); + t1 = aes_128_assist(t1, t2); + ks[9] = t1; + + t2 = _mm_aeskeygenassist_si128(t1, 0x36); + t1 = aes_128_assist(t1, t2); + ks[10] = t1; + + cx->inf.l = 0; + cx->inf.b[0] = 10 * 16; + return EXIT_SUCCESS; +} + +INLINE void aes_192_assist(__m128i* t1, __m128i * t2, __m128i * t3) +{ + __m128i t4; + *t2 = _mm_shuffle_epi32(*t2, 0x55); + t4 = _mm_slli_si128(*t1, 0x4); + *t1 = _mm_xor_si128(*t1, t4); + t4 = _mm_slli_si128(t4, 0x4); + *t1 = _mm_xor_si128(*t1, t4); + t4 = _mm_slli_si128(t4, 0x4); + *t1 = _mm_xor_si128(*t1, t4); + *t1 = _mm_xor_si128(*t1, *t2); + *t2 = _mm_shuffle_epi32(*t1, 0xff); + t4 = _mm_slli_si128(*t3, 0x4); + *t3 = _mm_xor_si128(*t3, t4); + *t3 = _mm_xor_si128(*t3, *t2); +} + +AES_RETURN aes_ni(encrypt_key192)(const unsigned char *key, aes_encrypt_ctx cx[1]) +{ + __m128i t1, t2, t3; + __m128i *ks = (__m128i*)cx->ks; + + if(!has_aes_ni()) + { + return aes_xi(encrypt_key192)(key, cx); + } + + t1 = _mm_loadu_si128((__m128i*)key); + t3 = _mm_loadu_si128((__m128i*)(key + 16)); + + ks[0] = t1; + ks[1] = t3; + + t2 = _mm_aeskeygenassist_si128(t3, 0x1); + aes_192_assist(&t1, &t2, &t3); + + ks[1] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(ks[1]), _mm_castsi128_pd(t1), 0)); + ks[2] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(t1), _mm_castsi128_pd(t3), 1)); + + t2 = _mm_aeskeygenassist_si128(t3, 0x2); + aes_192_assist(&t1, &t2, &t3); + ks[3] = t1; + ks[4] = t3; + + t2 = _mm_aeskeygenassist_si128(t3, 0x4); + aes_192_assist(&t1, &t2, &t3); + ks[4] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(ks[4]), _mm_castsi128_pd(t1), 0)); + ks[5] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(t1), _mm_castsi128_pd(t3), 1)); + + t2 = _mm_aeskeygenassist_si128(t3, 0x8); + aes_192_assist(&t1, &t2, &t3); + ks[6] = t1; + ks[7] = t3; + + t2 = _mm_aeskeygenassist_si128(t3, 0x10); + aes_192_assist(&t1, &t2, &t3); + ks[7] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(ks[7]), _mm_castsi128_pd(t1), 0)); + ks[8] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(t1), _mm_castsi128_pd(t3), 1)); + + t2 = _mm_aeskeygenassist_si128(t3, 0x20); + aes_192_assist(&t1, &t2, &t3); + ks[9] = t1; + ks[10] = t3; + + t2 = _mm_aeskeygenassist_si128(t3, 0x40); + aes_192_assist(&t1, &t2, &t3); + ks[10] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(ks[10]), _mm_castsi128_pd(t1), 0)); + ks[11] = _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(t1), _mm_castsi128_pd(t3), 1)); + + t2 = _mm_aeskeygenassist_si128(t3, 0x80); + aes_192_assist(&t1, &t2, &t3); + ks[12] = t1; + + cx->inf.l = 0; + cx->inf.b[0] = 12 * 16; + return EXIT_SUCCESS; +} + +INLINE void aes_256_assist1(__m128i* t1, __m128i * t2) +{ + __m128i t4; + *t2 = _mm_shuffle_epi32(*t2, 0xff); + t4 = _mm_slli_si128(*t1, 0x4); + *t1 = _mm_xor_si128(*t1, t4); + t4 = _mm_slli_si128(t4, 0x4); + *t1 = _mm_xor_si128(*t1, t4); + t4 = _mm_slli_si128(t4, 0x4); + *t1 = _mm_xor_si128(*t1, t4); + *t1 = _mm_xor_si128(*t1, *t2); +} + +INLINE void aes_256_assist2(__m128i* t1, __m128i * t3) +{ + __m128i t2, t4; + t4 = _mm_aeskeygenassist_si128(*t1, 0x0); + t2 = _mm_shuffle_epi32(t4, 0xaa); + t4 = _mm_slli_si128(*t3, 0x4); + *t3 = _mm_xor_si128(*t3, t4); + t4 = _mm_slli_si128(t4, 0x4); + *t3 = _mm_xor_si128(*t3, t4); + t4 = _mm_slli_si128(t4, 0x4); + *t3 = _mm_xor_si128(*t3, t4); + *t3 = _mm_xor_si128(*t3, t2); +} + +AES_RETURN aes_ni(encrypt_key256)(const unsigned char *key, aes_encrypt_ctx cx[1]) +{ + __m128i t1, t2, t3; + __m128i *ks = (__m128i*)cx->ks; + + if(!has_aes_ni()) + { + return aes_xi(encrypt_key256)(key, cx); + } + + t1 = _mm_loadu_si128((__m128i*)key); + t3 = _mm_loadu_si128((__m128i*)(key + 16)); + + ks[0] = t1; + ks[1] = t3; + + t2 = _mm_aeskeygenassist_si128(t3, 0x01); + aes_256_assist1(&t1, &t2); + ks[2] = t1; + aes_256_assist2(&t1, &t3); + ks[3] = t3; + + t2 = _mm_aeskeygenassist_si128(t3, 0x02); + aes_256_assist1(&t1, &t2); + ks[4] = t1; + aes_256_assist2(&t1, &t3); + ks[5] = t3; + + t2 = _mm_aeskeygenassist_si128(t3, 0x04); + aes_256_assist1(&t1, &t2); + ks[6] = t1; + aes_256_assist2(&t1, &t3); + ks[7] = t3; + + t2 = _mm_aeskeygenassist_si128(t3, 0x08); + aes_256_assist1(&t1, &t2); + ks[8] = t1; + aes_256_assist2(&t1, &t3); + ks[9] = t3; + + t2 = _mm_aeskeygenassist_si128(t3, 0x10); + aes_256_assist1(&t1, &t2); + ks[10] = t1; + aes_256_assist2(&t1, &t3); + ks[11] = t3; + + t2 = _mm_aeskeygenassist_si128(t3, 0x20); + aes_256_assist1(&t1, &t2); + ks[12] = t1; + aes_256_assist2(&t1, &t3); + ks[13] = t3; + + t2 = _mm_aeskeygenassist_si128(t3, 0x40); + aes_256_assist1(&t1, &t2); + ks[14] = t1; + + cx->inf.l = 0; + cx->inf.b[0] = 14 * 16; + return EXIT_SUCCESS; +} + +INLINE void enc_to_dec(aes_decrypt_ctx cx[1]) +{ + __m128i *ks = (__m128i*)cx->ks; + int j; + + for( j = 1 ; j < (cx->inf.b[0] >> 4) ; ++j ) + ks[j] = _mm_aesimc_si128(ks[j]); +} + +AES_RETURN aes_ni(decrypt_key128)(const unsigned char *key, aes_decrypt_ctx cx[1]) +{ + if(!has_aes_ni()) + { + return aes_xi(decrypt_key128)(key, cx); + } + + if(aes_ni(encrypt_key128)(key, (aes_encrypt_ctx*)cx) == EXIT_SUCCESS) + { + enc_to_dec(cx); + return EXIT_SUCCESS; + } + else + return EXIT_FAILURE; + +} + +AES_RETURN aes_ni(decrypt_key192)(const unsigned char *key, aes_decrypt_ctx cx[1]) +{ + if(!has_aes_ni()) + { + return aes_xi(decrypt_key192)(key, cx); + } + + if(aes_ni(encrypt_key192)(key, (aes_encrypt_ctx*)cx) == EXIT_SUCCESS) + { + enc_to_dec(cx); + return EXIT_SUCCESS; + } + else + return EXIT_FAILURE; +} + +AES_RETURN aes_ni(decrypt_key256)(const unsigned char *key, aes_decrypt_ctx cx[1]) +{ + if(!has_aes_ni()) + { + return aes_xi(decrypt_key256)(key, cx); + } + + if(aes_ni(encrypt_key256)(key, (aes_encrypt_ctx*)cx) == EXIT_SUCCESS) + { + enc_to_dec(cx); + return EXIT_SUCCESS; + } + else + return EXIT_FAILURE; +} + +AES_RETURN aes_ni(encrypt)(const unsigned char *in, unsigned char *out, const aes_encrypt_ctx cx[1]) +{ + __m128i *key = (__m128i*)cx->ks, t; + + if(cx->inf.b[0] != 10 * 16 && cx->inf.b[0] != 12 * 16 && cx->inf.b[0] != 14 * 16) + return EXIT_FAILURE; + + if(!has_aes_ni()) + { + return aes_xi(encrypt)(in, out, cx); + } + + t = _mm_xor_si128(_mm_loadu_si128((__m128i*)in), *(__m128i*)key); + + switch(cx->inf.b[0]) + { + case 14 * 16: + t = _mm_aesenc_si128(t, *(__m128i*)++key); + t = _mm_aesenc_si128(t, *(__m128i*)++key); + case 12 * 16: + t = _mm_aesenc_si128(t, *(__m128i*)++key); + t = _mm_aesenc_si128(t, *(__m128i*)++key); + case 10 * 16: + t = _mm_aesenc_si128(t, *(__m128i*)++key); + t = _mm_aesenc_si128(t, *(__m128i*)++key); + t = _mm_aesenc_si128(t, *(__m128i*)++key); + t = _mm_aesenc_si128(t, *(__m128i*)++key); + t = _mm_aesenc_si128(t, *(__m128i*)++key); + t = _mm_aesenc_si128(t, *(__m128i*)++key); + t = _mm_aesenc_si128(t, *(__m128i*)++key); + t = _mm_aesenc_si128(t, *(__m128i*)++key); + t = _mm_aesenc_si128(t, *(__m128i*)++key); + t = _mm_aesenclast_si128(t, *(__m128i*)++key); + } + + _mm_storeu_si128(&((__m128i*)out)[0], t); + return EXIT_SUCCESS; +} + +AES_RETURN aes_ni(decrypt)(const unsigned char *in, unsigned char *out, const aes_decrypt_ctx cx[1]) +{ + __m128i *key = (__m128i*)cx->ks + (cx->inf.b[0] >> 4), t; + + if(cx->inf.b[0] != 10 * 16 && cx->inf.b[0] != 12 * 16 && cx->inf.b[0] != 14 * 16) + return EXIT_FAILURE; + + if(!has_aes_ni()) + { + return aes_xi(decrypt)(in, out, cx); + } + + t = _mm_xor_si128(_mm_loadu_si128((__m128i*)in), *(__m128i*)key); + + switch(cx->inf.b[0]) + { + case 14 * 16: + t = _mm_aesdec_si128(t, *(__m128i*)--key); + t = _mm_aesdec_si128(t, *(__m128i*)--key); + case 12 * 16: + t = _mm_aesdec_si128(t, *(__m128i*)--key); + t = _mm_aesdec_si128(t, *(__m128i*)--key); + case 10 * 16: + t = _mm_aesdec_si128(t, *(__m128i*)--key); + t = _mm_aesdec_si128(t, *(__m128i*)--key); + t = _mm_aesdec_si128(t, *(__m128i*)--key); + t = _mm_aesdec_si128(t, *(__m128i*)--key); + t = _mm_aesdec_si128(t, *(__m128i*)--key); + t = _mm_aesdec_si128(t, *(__m128i*)--key); + t = _mm_aesdec_si128(t, *(__m128i*)--key); + t = _mm_aesdec_si128(t, *(__m128i*)--key); + t = _mm_aesdec_si128(t, *(__m128i*)--key); + t = _mm_aesdeclast_si128(t, *(__m128i*)--key); + } + + _mm_storeu_si128((__m128i*)out, t); + return EXIT_SUCCESS; +} + +#ifdef ADD_AESNI_MODE_CALLS +#ifdef USE_AES_CONTEXT + +AES_RETURN aes_CBC_encrypt(const unsigned char *in, + unsigned char *out, + unsigned char ivec[16], + unsigned long length, + const aes_encrypt_ctx cx[1]) +{ + __m128i feedback, data, *key = (__m128i*)cx->ks; + int number_of_rounds = cx->inf.b[0] >> 4, j; + unsigned long i; + + if(number_of_rounds != 10 && number_of_rounds != 12 && number_of_rounds != 14) + return EXIT_FAILURE; + + if(!has_aes_ni()) + { + return aes_cbc_encrypt(in, out, length, ivec, cx); + } + + if(length % 16) + length = length / 16 + 1; + else length /= 16; + feedback = _mm_loadu_si128((__m128i*)ivec); + for(i = 0; i < length; i++) + { + data = _mm_loadu_si128(&((__m128i*)in)[i]); + feedback = _mm_xor_si128(data, feedback); + feedback = _mm_xor_si128(feedback, ((__m128i*)key)[0]); + for(j = 1; j ks; + int number_of_rounds = cx->inf.b[0] >> 4, j; + unsigned long i; + + if(number_of_rounds != 10 && number_of_rounds != 12 && number_of_rounds != 14) + return EXIT_FAILURE; + + if(!has_aes_ni()) + { + return aes_cbc_decrypt(in, out, length, ivec, cx); + } + + if(length % 16) + length = length / 16 + 1; + else length /= 16; + feedback = _mm_loadu_si128((__m128i*)ivec); + for(i = 0; i < length; i++) + { + last_in = _mm_loadu_si128(&((__m128i*)in)[i]); + data = _mm_xor_si128(last_in, ((__m128i*)key)[number_of_rounds]); + for(j = number_of_rounds - 1; j > 0; j--) + { + data = _mm_aesdec_si128(data, ((__m128i*)key)[j]); + } + data = _mm_aesdeclast_si128(data, ((__m128i*)key)[0]); + data = _mm_xor_si128(data, feedback); + _mm_storeu_si128(&((__m128i*)out)[i], data); + feedback = last_in; + } + return EXIT_SUCCESS; +} + +static void ctr_inc(unsigned char *ctr_blk) +{ + uint32_t c; + + c = *(uint32_t*)(ctr_blk + 8); + c++; + *(uint32_t*)(ctr_blk + 8) = c; + + if(!c) + *(uint32_t*)(ctr_blk + 12) = *(uint32_t*)(ctr_blk + 12) + 1; +} + +AES_RETURN AES_CTR_encrypt(const unsigned char *in, + unsigned char *out, + const unsigned char ivec[8], + const unsigned char nonce[4], + unsigned long length, + const aes_encrypt_ctx cx[1]) +{ + __m128i ctr_block = { 0 }, *key = (__m128i*)cx->ks, tmp, ONE, BSWAP_EPI64; + int number_of_rounds = cx->inf.b[0] >> 4, j; + unsigned long i; + + if(number_of_rounds != 10 && number_of_rounds != 12 && number_of_rounds != 14) + return EXIT_FAILURE; + + if(!has_aes_ni()) + { + unsigned char ctr_blk[16]; + *(uint64_t*)ctr_blk = *(uint64_t*)ivec; + *(uint32_t*)(ctr_blk + 8) = *(uint32_t*)nonce; + return aes_ctr_crypt(in, out, length, (unsigned char*)ctr_blk, ctr_inc, cx); + } + + if(length % 16) + length = length / 16 + 1; + else length /= 16; + ONE = _mm_set_epi32(0, 1, 0, 0); + BSWAP_EPI64 = _mm_setr_epi8(7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8); +#ifdef _MSC_VER + ctr_block = _mm_insert_epi64(ctr_block, *(long long*)ivec, 1); +#else + ctr_block = _mm_set_epi64(*(__m64*)ivec, *(__m64*)&ctr_block); +#endif + ctr_block = _mm_insert_epi32(ctr_block, *(long*)nonce, 1); + ctr_block = _mm_srli_si128(ctr_block, 4); + ctr_block = _mm_shuffle_epi8(ctr_block, BSWAP_EPI64); + ctr_block = _mm_add_epi64(ctr_block, ONE); + for(i = 0; i < length; i++) + { + tmp = _mm_shuffle_epi8(ctr_block, BSWAP_EPI64); + ctr_block = _mm_add_epi64(ctr_block, ONE); + tmp = _mm_xor_si128(tmp, ((__m128i*)key)[0]); + for(j = 1; j 'aes_name_i') */ +#define aes_xi(x) aes_ ## x ## _i + +/* map names here to provide the external API ('name' -> 'aes_name') */ +#define aes_ni(x) aes_ ## x + +AES_RETURN aes_ni(encrypt_key128)(const unsigned char *key, aes_encrypt_ctx cx[1]); +AES_RETURN aes_ni(encrypt_key192)(const unsigned char *key, aes_encrypt_ctx cx[1]); +AES_RETURN aes_ni(encrypt_key256)(const unsigned char *key, aes_encrypt_ctx cx[1]); + +AES_RETURN aes_ni(decrypt_key128)(const unsigned char *key, aes_decrypt_ctx cx[1]); +AES_RETURN aes_ni(decrypt_key192)(const unsigned char *key, aes_decrypt_ctx cx[1]); +AES_RETURN aes_ni(decrypt_key256)(const unsigned char *key, aes_decrypt_ctx cx[1]); + +AES_RETURN aes_ni(encrypt)(const unsigned char *in, unsigned char *out, const aes_encrypt_ctx cx[1]); +AES_RETURN aes_ni(decrypt)(const unsigned char *in, unsigned char *out, const aes_decrypt_ctx cx[1]); + +AES_RETURN aes_xi(encrypt_key128)(const unsigned char *key, aes_encrypt_ctx cx[1]); +AES_RETURN aes_xi(encrypt_key192)(const unsigned char *key, aes_encrypt_ctx cx[1]); +AES_RETURN aes_xi(encrypt_key256)(const unsigned char *key, aes_encrypt_ctx cx[1]); + +AES_RETURN aes_xi(decrypt_key128)(const unsigned char *key, aes_decrypt_ctx cx[1]); +AES_RETURN aes_xi(decrypt_key192)(const unsigned char *key, aes_decrypt_ctx cx[1]); +AES_RETURN aes_xi(decrypt_key256)(const unsigned char *key, aes_decrypt_ctx cx[1]); + +AES_RETURN aes_xi(encrypt)(const unsigned char *in, unsigned char *out, const aes_encrypt_ctx cx[1]); +AES_RETURN aes_xi(decrypt)(const unsigned char *in, unsigned char *out, const aes_decrypt_ctx cx[1]); + +#endif + +#endif diff --git a/SSZipArchive/minizip/aes/aescrypt.c b/SSZipArchive/minizip/aes/aescrypt.c new file mode 100644 index 0000000..bd647e4 --- /dev/null +++ b/SSZipArchive/minizip/aes/aescrypt.c @@ -0,0 +1,301 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#include "aesopt.h" +#include "aestab.h" + +#if defined( USE_INTEL_AES_IF_PRESENT ) +# include "aes_ni.h" +#else +/* map names here to provide the external API ('name' -> 'aes_name') */ +# define aes_xi(x) aes_ ## x +#endif + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#define si(y,x,k,c) (s(y,c) = word_in(x, c) ^ (k)[c]) +#define so(y,x,c) word_out(y, c, s(x,c)) + +#if defined(ARRAYS) +#define locals(y,x) x[4],y[4] +#else +#define locals(y,x) x##0,x##1,x##2,x##3,y##0,y##1,y##2,y##3 +#endif + +#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \ + s(y,2) = s(x,2); s(y,3) = s(x,3); +#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3) +#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3) +#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3) + +#if ( FUNCS_IN_C & ENCRYPTION_IN_C ) + +/* Visual C++ .Net v7.1 provides the fastest encryption code when using + Pentium optimiation with small code but this is poor for decryption + so we need to control this with the following VC++ pragmas +*/ + +#if defined( _MSC_VER ) && !defined( _WIN64 ) +#pragma optimize( "s", on ) +#endif + +/* Given the column (c) of the output state variable, the following + macros give the input state variables which are needed in its + computation for each row (r) of the state. All the alternative + macros give the same end values but expand into different ways + of calculating these values. In particular the complex macro + used for dynamically variable block sizes is designed to expand + to a compile time constant whenever possible but will expand to + conditional clauses on some branches (I am grateful to Frank + Yellin for this construction) +*/ + +#define fwd_var(x,r,c)\ + ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\ + : r == 1 ? ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0))\ + : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\ + : ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2))) + +#if defined(FT4_SET) +#undef dec_fmvars +#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,n),fwd_var,rf1,c)) +#elif defined(FT1_SET) +#undef dec_fmvars +#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,upr,t_use(f,n),fwd_var,rf1,c)) +#else +#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ fwd_mcol(no_table(x,t_use(s,box),fwd_var,rf1,c))) +#endif + +#if defined(FL4_SET) +#define fwd_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,l),fwd_var,rf1,c)) +#elif defined(FL1_SET) +#define fwd_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,ups,t_use(f,l),fwd_var,rf1,c)) +#else +#define fwd_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ no_table(x,t_use(s,box),fwd_var,rf1,c)) +#endif + +AES_RETURN aes_xi(encrypt)(const unsigned char *in, unsigned char *out, const aes_encrypt_ctx cx[1]) +{ uint32_t locals(b0, b1); + const uint32_t *kp; +#if defined( dec_fmvars ) + dec_fmvars; /* declare variables for fwd_mcol() if needed */ +#endif + + if(cx->inf.b[0] != 10 * 16 && cx->inf.b[0] != 12 * 16 && cx->inf.b[0] != 14 * 16) + return EXIT_FAILURE; + + kp = cx->ks; + state_in(b0, in, kp); + +#if (ENC_UNROLL == FULL) + + switch(cx->inf.b[0]) + { + case 14 * 16: + round(fwd_rnd, b1, b0, kp + 1 * N_COLS); + round(fwd_rnd, b0, b1, kp + 2 * N_COLS); + kp += 2 * N_COLS; + case 12 * 16: + round(fwd_rnd, b1, b0, kp + 1 * N_COLS); + round(fwd_rnd, b0, b1, kp + 2 * N_COLS); + kp += 2 * N_COLS; + case 10 * 16: + round(fwd_rnd, b1, b0, kp + 1 * N_COLS); + round(fwd_rnd, b0, b1, kp + 2 * N_COLS); + round(fwd_rnd, b1, b0, kp + 3 * N_COLS); + round(fwd_rnd, b0, b1, kp + 4 * N_COLS); + round(fwd_rnd, b1, b0, kp + 5 * N_COLS); + round(fwd_rnd, b0, b1, kp + 6 * N_COLS); + round(fwd_rnd, b1, b0, kp + 7 * N_COLS); + round(fwd_rnd, b0, b1, kp + 8 * N_COLS); + round(fwd_rnd, b1, b0, kp + 9 * N_COLS); + round(fwd_lrnd, b0, b1, kp +10 * N_COLS); + } + +#else + +#if (ENC_UNROLL == PARTIAL) + { uint32_t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 5) - 1; ++rnd) + { + kp += N_COLS; + round(fwd_rnd, b1, b0, kp); + kp += N_COLS; + round(fwd_rnd, b0, b1, kp); + } + kp += N_COLS; + round(fwd_rnd, b1, b0, kp); +#else + { uint32_t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 4) - 1; ++rnd) + { + kp += N_COLS; + round(fwd_rnd, b1, b0, kp); + l_copy(b0, b1); + } +#endif + kp += N_COLS; + round(fwd_lrnd, b0, b1, kp); + } +#endif + + state_out(out, b0); + return EXIT_SUCCESS; +} + +#endif + +#if ( FUNCS_IN_C & DECRYPTION_IN_C) + +/* Visual C++ .Net v7.1 provides the fastest encryption code when using + Pentium optimiation with small code but this is poor for decryption + so we need to control this with the following VC++ pragmas +*/ + +#if defined( _MSC_VER ) && !defined( _WIN64 ) +#pragma optimize( "t", on ) +#endif + +/* Given the column (c) of the output state variable, the following + macros give the input state variables which are needed in its + computation for each row (r) of the state. All the alternative + macros give the same end values but expand into different ways + of calculating these values. In particular the complex macro + used for dynamically variable block sizes is designed to expand + to a compile time constant whenever possible but will expand to + conditional clauses on some branches (I am grateful to Frank + Yellin for this construction) +*/ + +#define inv_var(x,r,c)\ + ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\ + : r == 1 ? ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2))\ + : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\ + : ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0))) + +#if defined(IT4_SET) +#undef dec_imvars +#define inv_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(i,n),inv_var,rf1,c)) +#elif defined(IT1_SET) +#undef dec_imvars +#define inv_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,upr,t_use(i,n),inv_var,rf1,c)) +#else +#define inv_rnd(y,x,k,c) (s(y,c) = inv_mcol((k)[c] ^ no_table(x,t_use(i,box),inv_var,rf1,c))) +#endif + +#if defined(IL4_SET) +#define inv_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(i,l),inv_var,rf1,c)) +#elif defined(IL1_SET) +#define inv_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,ups,t_use(i,l),inv_var,rf1,c)) +#else +#define inv_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ no_table(x,t_use(i,box),inv_var,rf1,c)) +#endif + +/* This code can work with the decryption key schedule in the */ +/* order that is used for encrytpion (where the 1st decryption */ +/* round key is at the high end ot the schedule) or with a key */ +/* schedule that has been reversed to put the 1st decryption */ +/* round key at the low end of the schedule in memory (when */ +/* AES_REV_DKS is defined) */ + +#ifdef AES_REV_DKS +#define key_ofs 0 +#define rnd_key(n) (kp + n * N_COLS) +#else +#define key_ofs 1 +#define rnd_key(n) (kp - n * N_COLS) +#endif + +AES_RETURN aes_xi(decrypt)(const unsigned char *in, unsigned char *out, const aes_decrypt_ctx cx[1]) +{ uint32_t locals(b0, b1); +#if defined( dec_imvars ) + dec_imvars; /* declare variables for inv_mcol() if needed */ +#endif + const uint32_t *kp; + + if(cx->inf.b[0] != 10 * 16 && cx->inf.b[0] != 12 * 16 && cx->inf.b[0] != 14 * 16) + return EXIT_FAILURE; + + kp = cx->ks + (key_ofs ? (cx->inf.b[0] >> 2) : 0); + state_in(b0, in, kp); + +#if (DEC_UNROLL == FULL) + + kp = cx->ks + (key_ofs ? 0 : (cx->inf.b[0] >> 2)); + switch(cx->inf.b[0]) + { + case 14 * 16: + round(inv_rnd, b1, b0, rnd_key(-13)); + round(inv_rnd, b0, b1, rnd_key(-12)); + case 12 * 16: + round(inv_rnd, b1, b0, rnd_key(-11)); + round(inv_rnd, b0, b1, rnd_key(-10)); + case 10 * 16: + round(inv_rnd, b1, b0, rnd_key(-9)); + round(inv_rnd, b0, b1, rnd_key(-8)); + round(inv_rnd, b1, b0, rnd_key(-7)); + round(inv_rnd, b0, b1, rnd_key(-6)); + round(inv_rnd, b1, b0, rnd_key(-5)); + round(inv_rnd, b0, b1, rnd_key(-4)); + round(inv_rnd, b1, b0, rnd_key(-3)); + round(inv_rnd, b0, b1, rnd_key(-2)); + round(inv_rnd, b1, b0, rnd_key(-1)); + round(inv_lrnd, b0, b1, rnd_key( 0)); + } + +#else + +#if (DEC_UNROLL == PARTIAL) + { uint32_t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 5) - 1; ++rnd) + { + kp = rnd_key(1); + round(inv_rnd, b1, b0, kp); + kp = rnd_key(1); + round(inv_rnd, b0, b1, kp); + } + kp = rnd_key(1); + round(inv_rnd, b1, b0, kp); +#else + { uint32_t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 4) - 1; ++rnd) + { + kp = rnd_key(1); + round(inv_rnd, b1, b0, kp); + l_copy(b0, b1); + } +#endif + kp = rnd_key(1); + round(inv_lrnd, b0, b1, kp); + } +#endif + + state_out(out, b0); + return EXIT_SUCCESS; +} + +#endif + +#if defined(__cplusplus) +} +#endif diff --git a/SSZipArchive/minizip/aes/aeskey.c b/SSZipArchive/minizip/aes/aeskey.c new file mode 100644 index 0000000..16e9607 --- /dev/null +++ b/SSZipArchive/minizip/aes/aeskey.c @@ -0,0 +1,554 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#include "aesopt.h" +#include "aestab.h" + +#if defined( USE_INTEL_AES_IF_PRESENT ) +# include "aes_ni.h" +#else +/* map names here to provide the external API ('name' -> 'aes_name') */ +# define aes_xi(x) aes_ ## x +#endif + +#ifdef USE_VIA_ACE_IF_PRESENT +# include "aes_via_ace.h" +#endif + +#if defined(__cplusplus) +extern "C" +{ +#endif + +/* Initialise the key schedule from the user supplied key. The key + length can be specified in bytes, with legal values of 16, 24 + and 32, or in bits, with legal values of 128, 192 and 256. These + values correspond with Nk values of 4, 6 and 8 respectively. + + The following macros implement a single cycle in the key + schedule generation process. The number of cycles needed + for each cx->n_col and nk value is: + + nk = 4 5 6 7 8 + ------------------------------ + cx->n_col = 4 10 9 8 7 7 + cx->n_col = 5 14 11 10 9 9 + cx->n_col = 6 19 15 12 11 11 + cx->n_col = 7 21 19 16 13 14 + cx->n_col = 8 29 23 19 17 14 +*/ + +#if defined( REDUCE_CODE_SIZE ) +# define ls_box ls_sub + uint32_t ls_sub(const uint32_t t, const uint32_t n); +# define inv_mcol im_sub + uint32_t im_sub(const uint32_t x); +# ifdef ENC_KS_UNROLL +# undef ENC_KS_UNROLL +# endif +# ifdef DEC_KS_UNROLL +# undef DEC_KS_UNROLL +# endif +#endif + +#if (FUNCS_IN_C & ENC_KEYING_IN_C) + +#if defined(AES_128) || defined( AES_VAR ) + +#define ke4(k,i) \ +{ k[4*(i)+4] = ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; \ + k[4*(i)+5] = ss[1] ^= ss[0]; \ + k[4*(i)+6] = ss[2] ^= ss[1]; \ + k[4*(i)+7] = ss[3] ^= ss[2]; \ +} + +AES_RETURN aes_xi(encrypt_key128)(const unsigned char *key, aes_encrypt_ctx cx[1]) +{ uint32_t ss[4]; + + cx->ks[0] = ss[0] = word_in(key, 0); + cx->ks[1] = ss[1] = word_in(key, 1); + cx->ks[2] = ss[2] = word_in(key, 2); + cx->ks[3] = ss[3] = word_in(key, 3); + +#ifdef ENC_KS_UNROLL + ke4(cx->ks, 0); ke4(cx->ks, 1); + ke4(cx->ks, 2); ke4(cx->ks, 3); + ke4(cx->ks, 4); ke4(cx->ks, 5); + ke4(cx->ks, 6); ke4(cx->ks, 7); + ke4(cx->ks, 8); +#else + { uint32_t i; + for(i = 0; i < 9; ++i) + ke4(cx->ks, i); + } +#endif + ke4(cx->ks, 9); + cx->inf.l = 0; + cx->inf.b[0] = 10 * 16; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) + cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_192) || defined( AES_VAR ) + +#define kef6(k,i) \ +{ k[6*(i)+ 6] = ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; \ + k[6*(i)+ 7] = ss[1] ^= ss[0]; \ + k[6*(i)+ 8] = ss[2] ^= ss[1]; \ + k[6*(i)+ 9] = ss[3] ^= ss[2]; \ +} + +#define ke6(k,i) \ +{ kef6(k,i); \ + k[6*(i)+10] = ss[4] ^= ss[3]; \ + k[6*(i)+11] = ss[5] ^= ss[4]; \ +} + +AES_RETURN aes_xi(encrypt_key192)(const unsigned char *key, aes_encrypt_ctx cx[1]) +{ uint32_t ss[6]; + + cx->ks[0] = ss[0] = word_in(key, 0); + cx->ks[1] = ss[1] = word_in(key, 1); + cx->ks[2] = ss[2] = word_in(key, 2); + cx->ks[3] = ss[3] = word_in(key, 3); + cx->ks[4] = ss[4] = word_in(key, 4); + cx->ks[5] = ss[5] = word_in(key, 5); + +#ifdef ENC_KS_UNROLL + ke6(cx->ks, 0); ke6(cx->ks, 1); + ke6(cx->ks, 2); ke6(cx->ks, 3); + ke6(cx->ks, 4); ke6(cx->ks, 5); + ke6(cx->ks, 6); +#else + { uint32_t i; + for(i = 0; i < 7; ++i) + ke6(cx->ks, i); + } +#endif + kef6(cx->ks, 7); + cx->inf.l = 0; + cx->inf.b[0] = 12 * 16; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) + cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_256) || defined( AES_VAR ) + +#define kef8(k,i) \ +{ k[8*(i)+ 8] = ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; \ + k[8*(i)+ 9] = ss[1] ^= ss[0]; \ + k[8*(i)+10] = ss[2] ^= ss[1]; \ + k[8*(i)+11] = ss[3] ^= ss[2]; \ +} + +#define ke8(k,i) \ +{ kef8(k,i); \ + k[8*(i)+12] = ss[4] ^= ls_box(ss[3],0); \ + k[8*(i)+13] = ss[5] ^= ss[4]; \ + k[8*(i)+14] = ss[6] ^= ss[5]; \ + k[8*(i)+15] = ss[7] ^= ss[6]; \ +} + +AES_RETURN aes_xi(encrypt_key256)(const unsigned char *key, aes_encrypt_ctx cx[1]) +{ uint32_t ss[8]; + + cx->ks[0] = ss[0] = word_in(key, 0); + cx->ks[1] = ss[1] = word_in(key, 1); + cx->ks[2] = ss[2] = word_in(key, 2); + cx->ks[3] = ss[3] = word_in(key, 3); + cx->ks[4] = ss[4] = word_in(key, 4); + cx->ks[5] = ss[5] = word_in(key, 5); + cx->ks[6] = ss[6] = word_in(key, 6); + cx->ks[7] = ss[7] = word_in(key, 7); + +#ifdef ENC_KS_UNROLL + ke8(cx->ks, 0); ke8(cx->ks, 1); + ke8(cx->ks, 2); ke8(cx->ks, 3); + ke8(cx->ks, 4); ke8(cx->ks, 5); +#else + { uint32_t i; + for(i = 0; i < 6; ++i) + ke8(cx->ks, i); + } +#endif + kef8(cx->ks, 6); + cx->inf.l = 0; + cx->inf.b[0] = 14 * 16; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) + cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#endif + +#if (FUNCS_IN_C & DEC_KEYING_IN_C) + +/* this is used to store the decryption round keys */ +/* in forward or reverse order */ + +#ifdef AES_REV_DKS +#define v(n,i) ((n) - (i) + 2 * ((i) & 3)) +#else +#define v(n,i) (i) +#endif + +#if DEC_ROUND == NO_TABLES +#define ff(x) (x) +#else +#define ff(x) inv_mcol(x) +#if defined( dec_imvars ) +#define d_vars dec_imvars +#endif +#endif + +#if defined(AES_128) || defined( AES_VAR ) + +#define k4e(k,i) \ +{ k[v(40,(4*(i))+4)] = ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; \ + k[v(40,(4*(i))+5)] = ss[1] ^= ss[0]; \ + k[v(40,(4*(i))+6)] = ss[2] ^= ss[1]; \ + k[v(40,(4*(i))+7)] = ss[3] ^= ss[2]; \ +} + +#if 1 + +#define kdf4(k,i) \ +{ ss[0] = ss[0] ^ ss[2] ^ ss[1] ^ ss[3]; \ + ss[1] = ss[1] ^ ss[3]; \ + ss[2] = ss[2] ^ ss[3]; \ + ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; \ + ss[i % 4] ^= ss[4]; \ + ss[4] ^= k[v(40,(4*(i)))]; k[v(40,(4*(i))+4)] = ff(ss[4]); \ + ss[4] ^= k[v(40,(4*(i))+1)]; k[v(40,(4*(i))+5)] = ff(ss[4]); \ + ss[4] ^= k[v(40,(4*(i))+2)]; k[v(40,(4*(i))+6)] = ff(ss[4]); \ + ss[4] ^= k[v(40,(4*(i))+3)]; k[v(40,(4*(i))+7)] = ff(ss[4]); \ +} + +#define kd4(k,i) \ +{ ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; \ + ss[i % 4] ^= ss[4]; ss[4] = ff(ss[4]); \ + k[v(40,(4*(i))+4)] = ss[4] ^= k[v(40,(4*(i)))]; \ + k[v(40,(4*(i))+5)] = ss[4] ^= k[v(40,(4*(i))+1)]; \ + k[v(40,(4*(i))+6)] = ss[4] ^= k[v(40,(4*(i))+2)]; \ + k[v(40,(4*(i))+7)] = ss[4] ^= k[v(40,(4*(i))+3)]; \ +} + +#define kdl4(k,i) \ +{ ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; ss[i % 4] ^= ss[4]; \ + k[v(40,(4*(i))+4)] = (ss[0] ^= ss[1]) ^ ss[2] ^ ss[3]; \ + k[v(40,(4*(i))+5)] = ss[1] ^ ss[3]; \ + k[v(40,(4*(i))+6)] = ss[0]; \ + k[v(40,(4*(i))+7)] = ss[1]; \ +} + +#else + +#define kdf4(k,i) \ +{ ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[v(40,(4*(i))+ 4)] = ff(ss[0]); \ + ss[1] ^= ss[0]; k[v(40,(4*(i))+ 5)] = ff(ss[1]); \ + ss[2] ^= ss[1]; k[v(40,(4*(i))+ 6)] = ff(ss[2]); \ + ss[3] ^= ss[2]; k[v(40,(4*(i))+ 7)] = ff(ss[3]); \ +} + +#define kd4(k,i) \ +{ ss[4] = ls_box(ss[3],3) ^ t_use(r,c)[i]; \ + ss[0] ^= ss[4]; ss[4] = ff(ss[4]); k[v(40,(4*(i))+ 4)] = ss[4] ^= k[v(40,(4*(i)))]; \ + ss[1] ^= ss[0]; k[v(40,(4*(i))+ 5)] = ss[4] ^= k[v(40,(4*(i))+ 1)]; \ + ss[2] ^= ss[1]; k[v(40,(4*(i))+ 6)] = ss[4] ^= k[v(40,(4*(i))+ 2)]; \ + ss[3] ^= ss[2]; k[v(40,(4*(i))+ 7)] = ss[4] ^= k[v(40,(4*(i))+ 3)]; \ +} + +#define kdl4(k,i) \ +{ ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[v(40,(4*(i))+ 4)] = ss[0]; \ + ss[1] ^= ss[0]; k[v(40,(4*(i))+ 5)] = ss[1]; \ + ss[2] ^= ss[1]; k[v(40,(4*(i))+ 6)] = ss[2]; \ + ss[3] ^= ss[2]; k[v(40,(4*(i))+ 7)] = ss[3]; \ +} + +#endif + +AES_RETURN aes_xi(decrypt_key128)(const unsigned char *key, aes_decrypt_ctx cx[1]) +{ uint32_t ss[5]; +#if defined( d_vars ) + d_vars; +#endif + + cx->ks[v(40,(0))] = ss[0] = word_in(key, 0); + cx->ks[v(40,(1))] = ss[1] = word_in(key, 1); + cx->ks[v(40,(2))] = ss[2] = word_in(key, 2); + cx->ks[v(40,(3))] = ss[3] = word_in(key, 3); + +#ifdef DEC_KS_UNROLL + kdf4(cx->ks, 0); kd4(cx->ks, 1); + kd4(cx->ks, 2); kd4(cx->ks, 3); + kd4(cx->ks, 4); kd4(cx->ks, 5); + kd4(cx->ks, 6); kd4(cx->ks, 7); + kd4(cx->ks, 8); kdl4(cx->ks, 9); +#else + { uint32_t i; + for(i = 0; i < 10; ++i) + k4e(cx->ks, i); +#if !(DEC_ROUND == NO_TABLES) + for(i = N_COLS; i < 10 * N_COLS; ++i) + cx->ks[i] = inv_mcol(cx->ks[i]); +#endif + } +#endif + cx->inf.l = 0; + cx->inf.b[0] = 10 * 16; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) + cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_192) || defined( AES_VAR ) + +#define k6ef(k,i) \ +{ k[v(48,(6*(i))+ 6)] = ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; \ + k[v(48,(6*(i))+ 7)] = ss[1] ^= ss[0]; \ + k[v(48,(6*(i))+ 8)] = ss[2] ^= ss[1]; \ + k[v(48,(6*(i))+ 9)] = ss[3] ^= ss[2]; \ +} + +#define k6e(k,i) \ +{ k6ef(k,i); \ + k[v(48,(6*(i))+10)] = ss[4] ^= ss[3]; \ + k[v(48,(6*(i))+11)] = ss[5] ^= ss[4]; \ +} + +#define kdf6(k,i) \ +{ ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[v(48,(6*(i))+ 6)] = ff(ss[0]); \ + ss[1] ^= ss[0]; k[v(48,(6*(i))+ 7)] = ff(ss[1]); \ + ss[2] ^= ss[1]; k[v(48,(6*(i))+ 8)] = ff(ss[2]); \ + ss[3] ^= ss[2]; k[v(48,(6*(i))+ 9)] = ff(ss[3]); \ + ss[4] ^= ss[3]; k[v(48,(6*(i))+10)] = ff(ss[4]); \ + ss[5] ^= ss[4]; k[v(48,(6*(i))+11)] = ff(ss[5]); \ +} + +#define kd6(k,i) \ +{ ss[6] = ls_box(ss[5],3) ^ t_use(r,c)[i]; \ + ss[0] ^= ss[6]; ss[6] = ff(ss[6]); k[v(48,(6*(i))+ 6)] = ss[6] ^= k[v(48,(6*(i)))]; \ + ss[1] ^= ss[0]; k[v(48,(6*(i))+ 7)] = ss[6] ^= k[v(48,(6*(i))+ 1)]; \ + ss[2] ^= ss[1]; k[v(48,(6*(i))+ 8)] = ss[6] ^= k[v(48,(6*(i))+ 2)]; \ + ss[3] ^= ss[2]; k[v(48,(6*(i))+ 9)] = ss[6] ^= k[v(48,(6*(i))+ 3)]; \ + ss[4] ^= ss[3]; k[v(48,(6*(i))+10)] = ss[6] ^= k[v(48,(6*(i))+ 4)]; \ + ss[5] ^= ss[4]; k[v(48,(6*(i))+11)] = ss[6] ^= k[v(48,(6*(i))+ 5)]; \ +} + +#define kdl6(k,i) \ +{ ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[v(48,(6*(i))+ 6)] = ss[0]; \ + ss[1] ^= ss[0]; k[v(48,(6*(i))+ 7)] = ss[1]; \ + ss[2] ^= ss[1]; k[v(48,(6*(i))+ 8)] = ss[2]; \ + ss[3] ^= ss[2]; k[v(48,(6*(i))+ 9)] = ss[3]; \ +} + +AES_RETURN aes_xi(decrypt_key192)(const unsigned char *key, aes_decrypt_ctx cx[1]) +{ uint32_t ss[7]; +#if defined( d_vars ) + d_vars; +#endif + + cx->ks[v(48,(0))] = ss[0] = word_in(key, 0); + cx->ks[v(48,(1))] = ss[1] = word_in(key, 1); + cx->ks[v(48,(2))] = ss[2] = word_in(key, 2); + cx->ks[v(48,(3))] = ss[3] = word_in(key, 3); + +#ifdef DEC_KS_UNROLL + cx->ks[v(48,(4))] = ff(ss[4] = word_in(key, 4)); + cx->ks[v(48,(5))] = ff(ss[5] = word_in(key, 5)); + kdf6(cx->ks, 0); kd6(cx->ks, 1); + kd6(cx->ks, 2); kd6(cx->ks, 3); + kd6(cx->ks, 4); kd6(cx->ks, 5); + kd6(cx->ks, 6); kdl6(cx->ks, 7); +#else + cx->ks[v(48,(4))] = ss[4] = word_in(key, 4); + cx->ks[v(48,(5))] = ss[5] = word_in(key, 5); + { uint32_t i; + + for(i = 0; i < 7; ++i) + k6e(cx->ks, i); + k6ef(cx->ks, 7); +#if !(DEC_ROUND == NO_TABLES) + for(i = N_COLS; i < 12 * N_COLS; ++i) + cx->ks[i] = inv_mcol(cx->ks[i]); +#endif + } +#endif + cx->inf.l = 0; + cx->inf.b[0] = 12 * 16; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) + cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_256) || defined( AES_VAR ) + +#define k8ef(k,i) \ +{ k[v(56,(8*(i))+ 8)] = ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; \ + k[v(56,(8*(i))+ 9)] = ss[1] ^= ss[0]; \ + k[v(56,(8*(i))+10)] = ss[2] ^= ss[1]; \ + k[v(56,(8*(i))+11)] = ss[3] ^= ss[2]; \ +} + +#define k8e(k,i) \ +{ k8ef(k,i); \ + k[v(56,(8*(i))+12)] = ss[4] ^= ls_box(ss[3],0); \ + k[v(56,(8*(i))+13)] = ss[5] ^= ss[4]; \ + k[v(56,(8*(i))+14)] = ss[6] ^= ss[5]; \ + k[v(56,(8*(i))+15)] = ss[7] ^= ss[6]; \ +} + +#define kdf8(k,i) \ +{ ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[v(56,(8*(i))+ 8)] = ff(ss[0]); \ + ss[1] ^= ss[0]; k[v(56,(8*(i))+ 9)] = ff(ss[1]); \ + ss[2] ^= ss[1]; k[v(56,(8*(i))+10)] = ff(ss[2]); \ + ss[3] ^= ss[2]; k[v(56,(8*(i))+11)] = ff(ss[3]); \ + ss[4] ^= ls_box(ss[3],0); k[v(56,(8*(i))+12)] = ff(ss[4]); \ + ss[5] ^= ss[4]; k[v(56,(8*(i))+13)] = ff(ss[5]); \ + ss[6] ^= ss[5]; k[v(56,(8*(i))+14)] = ff(ss[6]); \ + ss[7] ^= ss[6]; k[v(56,(8*(i))+15)] = ff(ss[7]); \ +} + +#define kd8(k,i) \ +{ ss[8] = ls_box(ss[7],3) ^ t_use(r,c)[i]; \ + ss[0] ^= ss[8]; ss[8] = ff(ss[8]); k[v(56,(8*(i))+ 8)] = ss[8] ^= k[v(56,(8*(i)))]; \ + ss[1] ^= ss[0]; k[v(56,(8*(i))+ 9)] = ss[8] ^= k[v(56,(8*(i))+ 1)]; \ + ss[2] ^= ss[1]; k[v(56,(8*(i))+10)] = ss[8] ^= k[v(56,(8*(i))+ 2)]; \ + ss[3] ^= ss[2]; k[v(56,(8*(i))+11)] = ss[8] ^= k[v(56,(8*(i))+ 3)]; \ + ss[8] = ls_box(ss[3],0); \ + ss[4] ^= ss[8]; ss[8] = ff(ss[8]); k[v(56,(8*(i))+12)] = ss[8] ^= k[v(56,(8*(i))+ 4)]; \ + ss[5] ^= ss[4]; k[v(56,(8*(i))+13)] = ss[8] ^= k[v(56,(8*(i))+ 5)]; \ + ss[6] ^= ss[5]; k[v(56,(8*(i))+14)] = ss[8] ^= k[v(56,(8*(i))+ 6)]; \ + ss[7] ^= ss[6]; k[v(56,(8*(i))+15)] = ss[8] ^= k[v(56,(8*(i))+ 7)]; \ +} + +#define kdl8(k,i) \ +{ ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[v(56,(8*(i))+ 8)] = ss[0]; \ + ss[1] ^= ss[0]; k[v(56,(8*(i))+ 9)] = ss[1]; \ + ss[2] ^= ss[1]; k[v(56,(8*(i))+10)] = ss[2]; \ + ss[3] ^= ss[2]; k[v(56,(8*(i))+11)] = ss[3]; \ +} + +AES_RETURN aes_xi(decrypt_key256)(const unsigned char *key, aes_decrypt_ctx cx[1]) +{ uint32_t ss[9]; +#if defined( d_vars ) + d_vars; +#endif + + cx->ks[v(56,(0))] = ss[0] = word_in(key, 0); + cx->ks[v(56,(1))] = ss[1] = word_in(key, 1); + cx->ks[v(56,(2))] = ss[2] = word_in(key, 2); + cx->ks[v(56,(3))] = ss[3] = word_in(key, 3); + +#ifdef DEC_KS_UNROLL + cx->ks[v(56,(4))] = ff(ss[4] = word_in(key, 4)); + cx->ks[v(56,(5))] = ff(ss[5] = word_in(key, 5)); + cx->ks[v(56,(6))] = ff(ss[6] = word_in(key, 6)); + cx->ks[v(56,(7))] = ff(ss[7] = word_in(key, 7)); + kdf8(cx->ks, 0); kd8(cx->ks, 1); + kd8(cx->ks, 2); kd8(cx->ks, 3); + kd8(cx->ks, 4); kd8(cx->ks, 5); + kdl8(cx->ks, 6); +#else + cx->ks[v(56,(4))] = ss[4] = word_in(key, 4); + cx->ks[v(56,(5))] = ss[5] = word_in(key, 5); + cx->ks[v(56,(6))] = ss[6] = word_in(key, 6); + cx->ks[v(56,(7))] = ss[7] = word_in(key, 7); + { uint32_t i; + + for(i = 0; i < 6; ++i) + k8e(cx->ks, i); + k8ef(cx->ks, 6); +#if !(DEC_ROUND == NO_TABLES) + for(i = N_COLS; i < 14 * N_COLS; ++i) + cx->ks[i] = inv_mcol(cx->ks[i]); +#endif + } +#endif + cx->inf.l = 0; + cx->inf.b[0] = 14 * 16; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) + cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#endif + +#if defined( AES_VAR ) + +AES_RETURN aes_encrypt_key(const unsigned char *key, int key_len, aes_encrypt_ctx cx[1]) +{ + switch(key_len) + { + case 16: case 128: return aes_encrypt_key128(key, cx); + case 24: case 192: return aes_encrypt_key192(key, cx); + case 32: case 256: return aes_encrypt_key256(key, cx); + default: return EXIT_FAILURE; + } +} + +AES_RETURN aes_decrypt_key(const unsigned char *key, int key_len, aes_decrypt_ctx cx[1]) +{ + switch(key_len) + { + case 16: case 128: return aes_decrypt_key128(key, cx); + case 24: case 192: return aes_decrypt_key192(key, cx); + case 32: case 256: return aes_decrypt_key256(key, cx); + default: return EXIT_FAILURE; + } +} + +#endif + +#if defined(__cplusplus) +} +#endif diff --git a/SSZipArchive/minizip/aes/aesopt.h b/SSZipArchive/minizip/aes/aesopt.h new file mode 100644 index 0000000..3678e74 --- /dev/null +++ b/SSZipArchive/minizip/aes/aesopt.h @@ -0,0 +1,776 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + + This file contains the compilation options for AES (Rijndael) and code + that is common across encryption, key scheduling and table generation. + + OPERATION + + These source code files implement the AES algorithm Rijndael designed by + Joan Daemen and Vincent Rijmen. This version is designed for the standard + block size of 16 bytes and for key sizes of 128, 192 and 256 bits (16, 24 + and 32 bytes). + + This version is designed for flexibility and speed using operations on + 32-bit words rather than operations on bytes. It can be compiled with + either big or little endian internal byte order but is faster when the + native byte order for the processor is used. + + THE CIPHER INTERFACE + + The cipher interface is implemented as an array of bytes in which lower + AES bit sequence indexes map to higher numeric significance within bytes. + + uint8_t (an unsigned 8-bit type) + uint32_t (an unsigned 32-bit type) + struct aes_encrypt_ctx (structure for the cipher encryption context) + struct aes_decrypt_ctx (structure for the cipher decryption context) + AES_RETURN the function return type + + C subroutine calls: + + AES_RETURN aes_encrypt_key128(const unsigned char *key, aes_encrypt_ctx cx[1]); + AES_RETURN aes_encrypt_key192(const unsigned char *key, aes_encrypt_ctx cx[1]); + AES_RETURN aes_encrypt_key256(const unsigned char *key, aes_encrypt_ctx cx[1]); + AES_RETURN aes_encrypt(const unsigned char *in, unsigned char *out, + const aes_encrypt_ctx cx[1]); + + AES_RETURN aes_decrypt_key128(const unsigned char *key, aes_decrypt_ctx cx[1]); + AES_RETURN aes_decrypt_key192(const unsigned char *key, aes_decrypt_ctx cx[1]); + AES_RETURN aes_decrypt_key256(const unsigned char *key, aes_decrypt_ctx cx[1]); + AES_RETURN aes_decrypt(const unsigned char *in, unsigned char *out, + const aes_decrypt_ctx cx[1]); + + IMPORTANT NOTE: If you are using this C interface with dynamic tables make sure that + you call aes_init() before AES is used so that the tables are initialised. + + C++ aes class subroutines: + + Class AESencrypt for encryption + + Construtors: + AESencrypt(void) + AESencrypt(const unsigned char *key) - 128 bit key + Members: + AES_RETURN key128(const unsigned char *key) + AES_RETURN key192(const unsigned char *key) + AES_RETURN key256(const unsigned char *key) + AES_RETURN encrypt(const unsigned char *in, unsigned char *out) const + + Class AESdecrypt for encryption + Construtors: + AESdecrypt(void) + AESdecrypt(const unsigned char *key) - 128 bit key + Members: + AES_RETURN key128(const unsigned char *key) + AES_RETURN key192(const unsigned char *key) + AES_RETURN key256(const unsigned char *key) + AES_RETURN decrypt(const unsigned char *in, unsigned char *out) const +*/ + +#if !defined( _AESOPT_H ) +#define _AESOPT_H + +#if defined( __cplusplus ) +#include "aescpp.h" +#else +#include "aes.h" +#endif + +/* PLATFORM SPECIFIC INCLUDES */ + +#include "brg_endian.h" + +/* CONFIGURATION - THE USE OF DEFINES + + Later in this section there are a number of defines that control the + operation of the code. In each section, the purpose of each define is + explained so that the relevant form can be included or excluded by + setting either 1's or 0's respectively on the branches of the related + #if clauses. The following local defines should not be changed. +*/ + +#define ENCRYPTION_IN_C 1 +#define DECRYPTION_IN_C 2 +#define ENC_KEYING_IN_C 4 +#define DEC_KEYING_IN_C 8 + +#define NO_TABLES 0 +#define ONE_TABLE 1 +#define FOUR_TABLES 4 +#define NONE 0 +#define PARTIAL 1 +#define FULL 2 + +/* --- START OF USER CONFIGURED OPTIONS --- */ + +/* 1. BYTE ORDER WITHIN 32 BIT WORDS + + The fundamental data processing units in Rijndael are 8-bit bytes. The + input, output and key input are all enumerated arrays of bytes in which + bytes are numbered starting at zero and increasing to one less than the + number of bytes in the array in question. This enumeration is only used + for naming bytes and does not imply any adjacency or order relationship + from one byte to another. When these inputs and outputs are considered + as bit sequences, bits 8*n to 8*n+7 of the bit sequence are mapped to + byte[n] with bit 8n+i in the sequence mapped to bit 7-i within the byte. + In this implementation bits are numbered from 0 to 7 starting at the + numerically least significant end of each byte (bit n represents 2^n). + + However, Rijndael can be implemented more efficiently using 32-bit + words by packing bytes into words so that bytes 4*n to 4*n+3 are placed + into word[n]. While in principle these bytes can be assembled into words + in any positions, this implementation only supports the two formats in + which bytes in adjacent positions within words also have adjacent byte + numbers. This order is called big-endian if the lowest numbered bytes + in words have the highest numeric significance and little-endian if the + opposite applies. + + This code can work in either order irrespective of the order used by the + machine on which it runs. Normally the internal byte order will be set + to the order of the processor on which the code is to be run but this + define can be used to reverse this in special situations + + WARNING: Assembler code versions rely on PLATFORM_BYTE_ORDER being set. + This define will hence be redefined later (in section 4) if necessary +*/ + +#if 1 +# define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER +#elif 0 +# define ALGORITHM_BYTE_ORDER IS_LITTLE_ENDIAN +#elif 0 +# define ALGORITHM_BYTE_ORDER IS_BIG_ENDIAN +#else +# error The algorithm byte order is not defined +#endif + +/* 2. Intel AES AND VIA ACE SUPPORT */ + +#if defined( __GNUC__ ) && defined( __i386__ ) \ + || defined( _WIN32 ) && defined( _M_IX86 ) && !(defined( _WIN64 ) \ + || defined( _WIN32_WCE ) || defined( _MSC_VER ) && ( _MSC_VER <= 800 )) +# define VIA_ACE_POSSIBLE +#endif + +#if (defined( _WIN64 ) && defined( _MSC_VER )) \ + || (defined( __GNUC__ ) && defined( __x86_64__ )) && !(defined( __APPLE__ ))\ + && !(defined( INTEL_AES_POSSIBLE )) +# define INTEL_AES_POSSIBLE +#endif + +/* Define this option if support for the Intel AESNI is required + If USE_INTEL_AES_IF_PRESENT is defined then AESNI will be used + if it is detected (both present and enabled). + + AESNI uses a decryption key schedule with the first decryption + round key at the high end of the key scedule with the following + round keys at lower positions in memory. So AES_REV_DKS must NOT + be defined when AESNI will be used. ALthough it is unlikely that + assembler code will be used with an AESNI build, if it is then + AES_REV_DKS must NOT be defined when the assembler files are + built +*/ + +#if 1 && defined( INTEL_AES_POSSIBLE ) && !defined( USE_INTEL_AES_IF_PRESENT ) +# define USE_INTEL_AES_IF_PRESENT +#endif + +/* Define this option if support for the VIA ACE is required. This uses + inline assembler instructions and is only implemented for the Microsoft, + Intel and GCC compilers. If VIA ACE is known to be present, then defining + ASSUME_VIA_ACE_PRESENT will remove the ordinary encryption/decryption + code. If USE_VIA_ACE_IF_PRESENT is defined then VIA ACE will be used if + it is detected (both present and enabled) but the normal AES code will + also be present. + + When VIA ACE is to be used, all AES encryption contexts MUST be 16 byte + aligned; other input/output buffers do not need to be 16 byte aligned + but there are very large performance gains if this can be arranged. + VIA ACE also requires the decryption key schedule to be in reverse + order (which later checks below ensure). + + AES_REV_DKS must be set for assembler code used with a VIA ACE build +*/ + +#if 0 && defined( VIA_ACE_POSSIBLE ) && !defined( USE_VIA_ACE_IF_PRESENT ) +# define USE_VIA_ACE_IF_PRESENT +#endif + +#if 0 && defined( VIA_ACE_POSSIBLE ) && !defined( ASSUME_VIA_ACE_PRESENT ) +# define ASSUME_VIA_ACE_PRESENT +# endif + +/* 3. ASSEMBLER SUPPORT + + This define (which can be on the command line) enables the use of the + assembler code routines for encryption, decryption and key scheduling + as follows: + + ASM_X86_V1C uses the assembler (aes_x86_v1.asm) with large tables for + encryption and decryption and but with key scheduling in C + ASM_X86_V2 uses assembler (aes_x86_v2.asm) with compressed tables for + encryption, decryption and key scheduling + ASM_X86_V2C uses assembler (aes_x86_v2.asm) with compressed tables for + encryption and decryption and but with key scheduling in C + ASM_AMD64_C uses assembler (aes_amd64.asm) with compressed tables for + encryption and decryption and but with key scheduling in C + + Change one 'if 0' below to 'if 1' to select the version or define + as a compilation option. +*/ + +#if 0 && !defined( ASM_X86_V1C ) +# define ASM_X86_V1C +#elif 0 && !defined( ASM_X86_V2 ) +# define ASM_X86_V2 +#elif 0 && !defined( ASM_X86_V2C ) +# define ASM_X86_V2C +#elif 0 && !defined( ASM_AMD64_C ) +# define ASM_AMD64_C +#endif + +#if defined( __i386 ) || defined( _M_IX86 ) +# define A32_ +#elif defined( __x86_64__ ) || defined( _M_X64 ) +# define A64_ +#endif + +#if (defined ( ASM_X86_V1C ) || defined( ASM_X86_V2 ) || defined( ASM_X86_V2C )) \ + && !defined( A32_ ) || defined( ASM_AMD64_C ) && !defined( A64_ ) +# error Assembler code is only available for x86 and AMD64 systems +#endif + +/* 4. FAST INPUT/OUTPUT OPERATIONS. + + On some machines it is possible to improve speed by transferring the + bytes in the input and output arrays to and from the internal 32-bit + variables by addressing these arrays as if they are arrays of 32-bit + words. On some machines this will always be possible but there may + be a large performance penalty if the byte arrays are not aligned on + the normal word boundaries. On other machines this technique will + lead to memory access errors when such 32-bit word accesses are not + properly aligned. The option SAFE_IO avoids such problems but will + often be slower on those machines that support misaligned access + (especially so if care is taken to align the input and output byte + arrays on 32-bit word boundaries). If SAFE_IO is not defined it is + assumed that access to byte arrays as if they are arrays of 32-bit + words will not cause problems when such accesses are misaligned. +*/ +#if 1 && !defined( _MSC_VER ) +# define SAFE_IO +#endif + +/* 5. LOOP UNROLLING + + The code for encryption and decrytpion cycles through a number of rounds + that can be implemented either in a loop or by expanding the code into a + long sequence of instructions, the latter producing a larger program but + one that will often be much faster. The latter is called loop unrolling. + There are also potential speed advantages in expanding two iterations in + a loop with half the number of iterations, which is called partial loop + unrolling. The following options allow partial or full loop unrolling + to be set independently for encryption and decryption +*/ +#if 1 +# define ENC_UNROLL FULL +#elif 0 +# define ENC_UNROLL PARTIAL +#else +# define ENC_UNROLL NONE +#endif + +#if 1 +# define DEC_UNROLL FULL +#elif 0 +# define DEC_UNROLL PARTIAL +#else +# define DEC_UNROLL NONE +#endif + +#if 1 +# define ENC_KS_UNROLL +#endif + +#if 1 +# define DEC_KS_UNROLL +#endif + +/* 6. FAST FINITE FIELD OPERATIONS + + If this section is included, tables are used to provide faster finite + field arithmetic (this has no effect if STATIC_TABLES is defined). +*/ +#if 1 +# define FF_TABLES +#endif + +/* 7. INTERNAL STATE VARIABLE FORMAT + + The internal state of Rijndael is stored in a number of local 32-bit + word varaibles which can be defined either as an array or as individual + names variables. Include this section if you want to store these local + varaibles in arrays. Otherwise individual local variables will be used. +*/ +#if 1 +# define ARRAYS +#endif + +/* 8. FIXED OR DYNAMIC TABLES + + When this section is included the tables used by the code are compiled + statically into the binary file. Otherwise the subroutine aes_init() + must be called to compute them before the code is first used. +*/ +#if 1 && !(defined( _MSC_VER ) && ( _MSC_VER <= 800 )) +# define STATIC_TABLES +#endif + +/* 9. MASKING OR CASTING FROM LONGER VALUES TO BYTES + + In some systems it is better to mask longer values to extract bytes + rather than using a cast. This option allows this choice. +*/ +#if 0 +# define to_byte(x) ((uint8_t)(x)) +#else +# define to_byte(x) ((x) & 0xff) +#endif + +/* 10. TABLE ALIGNMENT + + On some sytsems speed will be improved by aligning the AES large lookup + tables on particular boundaries. This define should be set to a power of + two giving the desired alignment. It can be left undefined if alignment + is not needed. This option is specific to the Microsft VC++ compiler - + it seems to sometimes cause trouble for the VC++ version 6 compiler. +*/ + +#if 1 && defined( _MSC_VER ) && ( _MSC_VER >= 1300 ) +# define TABLE_ALIGN 32 +#endif + +/* 11. REDUCE CODE AND TABLE SIZE + + This replaces some expanded macros with function calls if AES_ASM_V2 or + AES_ASM_V2C are defined +*/ + +#if 1 && (defined( ASM_X86_V2 ) || defined( ASM_X86_V2C )) +# define REDUCE_CODE_SIZE +#endif + +/* 12. TABLE OPTIONS + + This cipher proceeds by repeating in a number of cycles known as 'rounds' + which are implemented by a round function which can optionally be speeded + up using tables. The basic tables are each 256 32-bit words, with either + one or four tables being required for each round function depending on + how much speed is required. The encryption and decryption round functions + are different and the last encryption and decrytpion round functions are + different again making four different round functions in all. + + This means that: + 1. Normal encryption and decryption rounds can each use either 0, 1 + or 4 tables and table spaces of 0, 1024 or 4096 bytes each. + 2. The last encryption and decryption rounds can also use either 0, 1 + or 4 tables and table spaces of 0, 1024 or 4096 bytes each. + + Include or exclude the appropriate definitions below to set the number + of tables used by this implementation. +*/ + +#if 1 /* set tables for the normal encryption round */ +# define ENC_ROUND FOUR_TABLES +#elif 0 +# define ENC_ROUND ONE_TABLE +#else +# define ENC_ROUND NO_TABLES +#endif + +#if 1 /* set tables for the last encryption round */ +# define LAST_ENC_ROUND FOUR_TABLES +#elif 0 +# define LAST_ENC_ROUND ONE_TABLE +#else +# define LAST_ENC_ROUND NO_TABLES +#endif + +#if 1 /* set tables for the normal decryption round */ +# define DEC_ROUND FOUR_TABLES +#elif 0 +# define DEC_ROUND ONE_TABLE +#else +# define DEC_ROUND NO_TABLES +#endif + +#if 1 /* set tables for the last decryption round */ +# define LAST_DEC_ROUND FOUR_TABLES +#elif 0 +# define LAST_DEC_ROUND ONE_TABLE +#else +# define LAST_DEC_ROUND NO_TABLES +#endif + +/* The decryption key schedule can be speeded up with tables in the same + way that the round functions can. Include or exclude the following + defines to set this requirement. +*/ +#if 1 +# define KEY_SCHED FOUR_TABLES +#elif 0 +# define KEY_SCHED ONE_TABLE +#else +# define KEY_SCHED NO_TABLES +#endif + +/* ---- END OF USER CONFIGURED OPTIONS ---- */ + +/* VIA ACE support is only available for VC++ and GCC */ + +#if !defined( _MSC_VER ) && !defined( __GNUC__ ) +# if defined( ASSUME_VIA_ACE_PRESENT ) +# undef ASSUME_VIA_ACE_PRESENT +# endif +# if defined( USE_VIA_ACE_IF_PRESENT ) +# undef USE_VIA_ACE_IF_PRESENT +# endif +#endif + +#if defined( ASSUME_VIA_ACE_PRESENT ) && !defined( USE_VIA_ACE_IF_PRESENT ) +# define USE_VIA_ACE_IF_PRESENT +#endif + +/* define to reverse decryption key schedule */ +#if 1 || defined( USE_VIA_ACE_IF_PRESENT ) && !defined ( AES_REV_DKS ) +# define AES_REV_DKS +#endif + +/* Intel AESNI uses a decryption key schedule in the encryption order */ +#if defined( USE_INTEL_AES_IF_PRESENT ) && defined ( AES_REV_DKS ) +# undef AES_REV_DKS +#endif + +/* Assembler support requires the use of platform byte order */ + +#if ( defined( ASM_X86_V1C ) || defined( ASM_X86_V2C ) || defined( ASM_AMD64_C ) ) \ + && (ALGORITHM_BYTE_ORDER != PLATFORM_BYTE_ORDER) +# undef ALGORITHM_BYTE_ORDER +# define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER +#endif + +/* In this implementation the columns of the state array are each held in + 32-bit words. The state array can be held in various ways: in an array + of words, in a number of individual word variables or in a number of + processor registers. The following define maps a variable name x and + a column number c to the way the state array variable is to be held. + The first define below maps the state into an array x[c] whereas the + second form maps the state into a number of individual variables x0, + x1, etc. Another form could map individual state colums to machine + register names. +*/ + +#if defined( ARRAYS ) +# define s(x,c) x[c] +#else +# define s(x,c) x##c +#endif + +/* This implementation provides subroutines for encryption, decryption + and for setting the three key lengths (separately) for encryption + and decryption. Since not all functions are needed, masks are set + up here to determine which will be implemented in C +*/ + +#if !defined( AES_ENCRYPT ) +# define EFUNCS_IN_C 0 +#elif defined( ASSUME_VIA_ACE_PRESENT ) || defined( ASM_X86_V1C ) \ + || defined( ASM_X86_V2C ) || defined( ASM_AMD64_C ) +# define EFUNCS_IN_C ENC_KEYING_IN_C +#elif !defined( ASM_X86_V2 ) +# define EFUNCS_IN_C ( ENCRYPTION_IN_C | ENC_KEYING_IN_C ) +#else +# define EFUNCS_IN_C 0 +#endif + +#if !defined( AES_DECRYPT ) +# define DFUNCS_IN_C 0 +#elif defined( ASSUME_VIA_ACE_PRESENT ) || defined( ASM_X86_V1C ) \ + || defined( ASM_X86_V2C ) || defined( ASM_AMD64_C ) +# define DFUNCS_IN_C DEC_KEYING_IN_C +#elif !defined( ASM_X86_V2 ) +# define DFUNCS_IN_C ( DECRYPTION_IN_C | DEC_KEYING_IN_C ) +#else +# define DFUNCS_IN_C 0 +#endif + +#define FUNCS_IN_C ( EFUNCS_IN_C | DFUNCS_IN_C ) + +/* END OF CONFIGURATION OPTIONS */ + +#define RC_LENGTH (5 * (AES_BLOCK_SIZE / 4 - 2)) + +/* Disable or report errors on some combinations of options */ + +#if ENC_ROUND == NO_TABLES && LAST_ENC_ROUND != NO_TABLES +# undef LAST_ENC_ROUND +# define LAST_ENC_ROUND NO_TABLES +#elif ENC_ROUND == ONE_TABLE && LAST_ENC_ROUND == FOUR_TABLES +# undef LAST_ENC_ROUND +# define LAST_ENC_ROUND ONE_TABLE +#endif + +#if ENC_ROUND == NO_TABLES && ENC_UNROLL != NONE +# undef ENC_UNROLL +# define ENC_UNROLL NONE +#endif + +#if DEC_ROUND == NO_TABLES && LAST_DEC_ROUND != NO_TABLES +# undef LAST_DEC_ROUND +# define LAST_DEC_ROUND NO_TABLES +#elif DEC_ROUND == ONE_TABLE && LAST_DEC_ROUND == FOUR_TABLES +# undef LAST_DEC_ROUND +# define LAST_DEC_ROUND ONE_TABLE +#endif + +#if DEC_ROUND == NO_TABLES && DEC_UNROLL != NONE +# undef DEC_UNROLL +# define DEC_UNROLL NONE +#endif + +#if defined( bswap32 ) +# define aes_sw32 bswap32 +#elif defined( bswap_32 ) +# define aes_sw32 bswap_32 +#else +# define brot(x,n) (((uint32_t)(x) << n) | ((uint32_t)(x) >> (32 - n))) +# define aes_sw32(x) ((brot((x),8) & 0x00ff00ff) | (brot((x),24) & 0xff00ff00)) +#endif + +/* upr(x,n): rotates bytes within words by n positions, moving bytes to + higher index positions with wrap around into low positions + ups(x,n): moves bytes by n positions to higher index positions in + words but without wrap around + bval(x,n): extracts a byte from a word + + WARNING: The definitions given here are intended only for use with + unsigned variables and with shift counts that are compile + time constants +*/ + +#if ( ALGORITHM_BYTE_ORDER == IS_LITTLE_ENDIAN ) +# define upr(x,n) (((uint32_t)(x) << (8 * (n))) | ((uint32_t)(x) >> (32 - 8 * (n)))) +# define ups(x,n) ((uint32_t) (x) << (8 * (n))) +# define bval(x,n) to_byte((x) >> (8 * (n))) +# define bytes2word(b0, b1, b2, b3) \ + (((uint32_t)(b3) << 24) | ((uint32_t)(b2) << 16) | ((uint32_t)(b1) << 8) | (b0)) +#endif + +#if ( ALGORITHM_BYTE_ORDER == IS_BIG_ENDIAN ) +# define upr(x,n) (((uint32_t)(x) >> (8 * (n))) | ((uint32_t)(x) << (32 - 8 * (n)))) +# define ups(x,n) ((uint32_t) (x) >> (8 * (n))) +# define bval(x,n) to_byte((x) >> (24 - 8 * (n))) +# define bytes2word(b0, b1, b2, b3) \ + (((uint32_t)(b0) << 24) | ((uint32_t)(b1) << 16) | ((uint32_t)(b2) << 8) | (b3)) +#endif + +#if defined( SAFE_IO ) +# define word_in(x,c) bytes2word(((const uint8_t*)(x)+4*c)[0], ((const uint8_t*)(x)+4*c)[1], \ + ((const uint8_t*)(x)+4*c)[2], ((const uint8_t*)(x)+4*c)[3]) +# define word_out(x,c,v) { ((uint8_t*)(x)+4*c)[0] = bval(v,0); ((uint8_t*)(x)+4*c)[1] = bval(v,1); \ + ((uint8_t*)(x)+4*c)[2] = bval(v,2); ((uint8_t*)(x)+4*c)[3] = bval(v,3); } +#elif ( ALGORITHM_BYTE_ORDER == PLATFORM_BYTE_ORDER ) +# define word_in(x,c) (*((uint32_t*)(x)+(c))) +# define word_out(x,c,v) (*((uint32_t*)(x)+(c)) = (v)) +#else +# define word_in(x,c) aes_sw32(*((uint32_t*)(x)+(c))) +# define word_out(x,c,v) (*((uint32_t*)(x)+(c)) = aes_sw32(v)) +#endif + +/* the finite field modular polynomial and elements */ + +#define WPOLY 0x011b +#define BPOLY 0x1b + +/* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + +#define gf_c1 0x80808080 +#define gf_c2 0x7f7f7f7f +#define gf_mulx(x) ((((x) & gf_c2) << 1) ^ ((((x) & gf_c1) >> 7) * BPOLY)) + +/* The following defines provide alternative definitions of gf_mulx that might + give improved performance if a fast 32-bit multiply is not available. Note + that a temporary variable u needs to be defined where gf_mulx is used. + +#define gf_mulx(x) (u = (x) & gf_c1, u |= (u >> 1), ((x) & gf_c2) << 1) ^ ((u >> 3) | (u >> 6)) +#define gf_c4 (0x01010101 * BPOLY) +#define gf_mulx(x) (u = (x) & gf_c1, ((x) & gf_c2) << 1) ^ ((u - (u >> 7)) & gf_c4) +*/ + +/* Work out which tables are needed for the different options */ + +#if defined( ASM_X86_V1C ) +# if defined( ENC_ROUND ) +# undef ENC_ROUND +# endif +# define ENC_ROUND FOUR_TABLES +# if defined( LAST_ENC_ROUND ) +# undef LAST_ENC_ROUND +# endif +# define LAST_ENC_ROUND FOUR_TABLES +# if defined( DEC_ROUND ) +# undef DEC_ROUND +# endif +# define DEC_ROUND FOUR_TABLES +# if defined( LAST_DEC_ROUND ) +# undef LAST_DEC_ROUND +# endif +# define LAST_DEC_ROUND FOUR_TABLES +# if defined( KEY_SCHED ) +# undef KEY_SCHED +# define KEY_SCHED FOUR_TABLES +# endif +#endif + +#if ( FUNCS_IN_C & ENCRYPTION_IN_C ) || defined( ASM_X86_V1C ) +# if ENC_ROUND == ONE_TABLE +# define FT1_SET +# elif ENC_ROUND == FOUR_TABLES +# define FT4_SET +# else +# define SBX_SET +# endif +# if LAST_ENC_ROUND == ONE_TABLE +# define FL1_SET +# elif LAST_ENC_ROUND == FOUR_TABLES +# define FL4_SET +# elif !defined( SBX_SET ) +# define SBX_SET +# endif +#endif + +#if ( FUNCS_IN_C & DECRYPTION_IN_C ) || defined( ASM_X86_V1C ) +# if DEC_ROUND == ONE_TABLE +# define IT1_SET +# elif DEC_ROUND == FOUR_TABLES +# define IT4_SET +# else +# define ISB_SET +# endif +# if LAST_DEC_ROUND == ONE_TABLE +# define IL1_SET +# elif LAST_DEC_ROUND == FOUR_TABLES +# define IL4_SET +# elif !defined(ISB_SET) +# define ISB_SET +# endif +#endif + +#if !(defined( REDUCE_CODE_SIZE ) && (defined( ASM_X86_V2 ) || defined( ASM_X86_V2C ))) +# if ((FUNCS_IN_C & ENC_KEYING_IN_C) || (FUNCS_IN_C & DEC_KEYING_IN_C)) +# if KEY_SCHED == ONE_TABLE +# if !defined( FL1_SET ) && !defined( FL4_SET ) +# define LS1_SET +# endif +# elif KEY_SCHED == FOUR_TABLES +# if !defined( FL4_SET ) +# define LS4_SET +# endif +# elif !defined( SBX_SET ) +# define SBX_SET +# endif +# endif +# if (FUNCS_IN_C & DEC_KEYING_IN_C) +# if KEY_SCHED == ONE_TABLE +# define IM1_SET +# elif KEY_SCHED == FOUR_TABLES +# define IM4_SET +# elif !defined( SBX_SET ) +# define SBX_SET +# endif +# endif +#endif + +/* generic definitions of Rijndael macros that use tables */ + +#define no_table(x,box,vf,rf,c) bytes2word( \ + box[bval(vf(x,0,c),rf(0,c))], \ + box[bval(vf(x,1,c),rf(1,c))], \ + box[bval(vf(x,2,c),rf(2,c))], \ + box[bval(vf(x,3,c),rf(3,c))]) + +#define one_table(x,op,tab,vf,rf,c) \ + ( tab[bval(vf(x,0,c),rf(0,c))] \ + ^ op(tab[bval(vf(x,1,c),rf(1,c))],1) \ + ^ op(tab[bval(vf(x,2,c),rf(2,c))],2) \ + ^ op(tab[bval(vf(x,3,c),rf(3,c))],3)) + +#define four_tables(x,tab,vf,rf,c) \ + ( tab[0][bval(vf(x,0,c),rf(0,c))] \ + ^ tab[1][bval(vf(x,1,c),rf(1,c))] \ + ^ tab[2][bval(vf(x,2,c),rf(2,c))] \ + ^ tab[3][bval(vf(x,3,c),rf(3,c))]) + +#define vf1(x,r,c) (x) +#define rf1(r,c) (r) +#define rf2(r,c) ((8+r-c)&3) + +/* perform forward and inverse column mix operation on four bytes in long word x in */ +/* parallel. NOTE: x must be a simple variable, NOT an expression in these macros. */ + +#if !(defined( REDUCE_CODE_SIZE ) && (defined( ASM_X86_V2 ) || defined( ASM_X86_V2C ))) + +#if defined( FM4_SET ) /* not currently used */ +# define fwd_mcol(x) four_tables(x,t_use(f,m),vf1,rf1,0) +#elif defined( FM1_SET ) /* not currently used */ +# define fwd_mcol(x) one_table(x,upr,t_use(f,m),vf1,rf1,0) +#else +# define dec_fmvars uint32_t g2 +# define fwd_mcol(x) (g2 = gf_mulx(x), g2 ^ upr((x) ^ g2, 3) ^ upr((x), 2) ^ upr((x), 1)) +#endif + +#if defined( IM4_SET ) +# define inv_mcol(x) four_tables(x,t_use(i,m),vf1,rf1,0) +#elif defined( IM1_SET ) +# define inv_mcol(x) one_table(x,upr,t_use(i,m),vf1,rf1,0) +#else +# define dec_imvars uint32_t g2, g4, g9 +# define inv_mcol(x) (g2 = gf_mulx(x), g4 = gf_mulx(g2), g9 = (x) ^ gf_mulx(g4), g4 ^= g9, \ + (x) ^ g2 ^ g4 ^ upr(g2 ^ g9, 3) ^ upr(g4, 2) ^ upr(g9, 1)) +#endif + +#if defined( FL4_SET ) +# define ls_box(x,c) four_tables(x,t_use(f,l),vf1,rf2,c) +#elif defined( LS4_SET ) +# define ls_box(x,c) four_tables(x,t_use(l,s),vf1,rf2,c) +#elif defined( FL1_SET ) +# define ls_box(x,c) one_table(x,upr,t_use(f,l),vf1,rf2,c) +#elif defined( LS1_SET ) +# define ls_box(x,c) one_table(x,upr,t_use(l,s),vf1,rf2,c) +#else +# define ls_box(x,c) no_table(x,t_use(s,box),vf1,rf2,c) +#endif + +#endif + +#if defined( ASM_X86_V1C ) && defined( AES_DECRYPT ) && !defined( ISB_SET ) +# define ISB_SET +#endif + +#endif diff --git a/SSZipArchive/minizip/aes/aestab.c b/SSZipArchive/minizip/aes/aestab.c new file mode 100644 index 0000000..3d48edf --- /dev/null +++ b/SSZipArchive/minizip/aes/aestab.c @@ -0,0 +1,418 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#define DO_TABLES + +#include "aes.h" +#include "aesopt.h" + +#if defined(STATIC_TABLES) + +#define sb_data(w) {\ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ + w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\ + w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\ + w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\ + w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\ + w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\ + w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\ + w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\ + w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\ + w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\ + w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\ + w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\ + w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\ + w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\ + w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\ + w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\ + w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\ + w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\ + w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\ + w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\ + w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\ + w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\ + w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\ + w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\ + w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) } + +#define isb_data(w) {\ + w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\ + w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\ + w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\ + w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\ + w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\ + w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\ + w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\ + w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\ + w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\ + w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\ + w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\ + w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\ + w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\ + w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\ + w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\ + w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\ + w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\ + w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\ + w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\ + w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\ + w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\ + w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\ + w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\ + w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\ + w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\ + w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\ + w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\ + w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\ + w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\ + w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\ + w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\ + w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d) } + +#define mm_data(w) {\ + w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\ + w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\ + w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\ + w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\ + w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\ + w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\ + w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\ + w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\ + w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\ + w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\ + w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\ + w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\ + w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\ + w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\ + w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\ + w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\ + w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\ + w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\ + w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\ + w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\ + w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\ + w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\ + w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\ + w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\ + w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\ + w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\ + w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\ + w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\ + w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\ + w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\ + w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\ + w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff) } + +#define rc_data(w) {\ + w(0x01), w(0x02), w(0x04), w(0x08), w(0x10),w(0x20), w(0x40), w(0x80),\ + w(0x1b), w(0x36) } + +#define h0(x) (x) + +#define w0(p) bytes2word(p, 0, 0, 0) +#define w1(p) bytes2word(0, p, 0, 0) +#define w2(p) bytes2word(0, 0, p, 0) +#define w3(p) bytes2word(0, 0, 0, p) + +#define u0(p) bytes2word(f2(p), p, p, f3(p)) +#define u1(p) bytes2word(f3(p), f2(p), p, p) +#define u2(p) bytes2word(p, f3(p), f2(p), p) +#define u3(p) bytes2word(p, p, f3(p), f2(p)) + +#define v0(p) bytes2word(fe(p), f9(p), fd(p), fb(p)) +#define v1(p) bytes2word(fb(p), fe(p), f9(p), fd(p)) +#define v2(p) bytes2word(fd(p), fb(p), fe(p), f9(p)) +#define v3(p) bytes2word(f9(p), fd(p), fb(p), fe(p)) + +#endif + +#if defined(STATIC_TABLES) || !defined(FF_TABLES) + +#define f2(x) ((x<<1) ^ (((x>>7) & 1) * WPOLY)) +#define f4(x) ((x<<2) ^ (((x>>6) & 1) * WPOLY) ^ (((x>>6) & 2) * WPOLY)) +#define f8(x) ((x<<3) ^ (((x>>5) & 1) * WPOLY) ^ (((x>>5) & 2) * WPOLY) \ + ^ (((x>>5) & 4) * WPOLY)) +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#else + +#define f2(x) ((x) ? pow[log[x] + 0x19] : 0) +#define f3(x) ((x) ? pow[log[x] + 0x01] : 0) +#define f9(x) ((x) ? pow[log[x] + 0xc7] : 0) +#define fb(x) ((x) ? pow[log[x] + 0x68] : 0) +#define fd(x) ((x) ? pow[log[x] + 0xee] : 0) +#define fe(x) ((x) ? pow[log[x] + 0xdf] : 0) + +#endif + +#include "aestab.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#if defined(STATIC_TABLES) + +/* implemented in case of wrong call for fixed tables */ + +AES_RETURN aes_init(void) +{ + return EXIT_SUCCESS; +} + +#else /* Generate the tables for the dynamic table option */ + +#if defined(FF_TABLES) + +#define gf_inv(x) ((x) ? pow[ 255 - log[x]] : 0) + +#else + +/* It will generally be sensible to use tables to compute finite + field multiplies and inverses but where memory is scarse this + code might sometimes be better. But it only has effect during + initialisation so its pretty unimportant in overall terms. +*/ + +/* return 2 ^ (n - 1) where n is the bit number of the highest bit + set in x with x in the range 1 < x < 0x00000200. This form is + used so that locals within fi can be bytes rather than words +*/ + +static uint8_t hibit(const uint32_t x) +{ uint8_t r = (uint8_t)((x >> 1) | (x >> 2)); + + r |= (r >> 2); + r |= (r >> 4); + return (r + 1) >> 1; +} + +/* return the inverse of the finite field element x */ + +static uint8_t gf_inv(const uint8_t x) +{ uint8_t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0; + + if(x < 2) + return x; + + for( ; ; ) + { + if(n1) + while(n2 >= n1) /* divide polynomial p2 by p1 */ + { + n2 /= n1; /* shift smaller polynomial left */ + p2 ^= (p1 * n2) & 0xff; /* and remove from larger one */ + v2 ^= v1 * n2; /* shift accumulated value and */ + n2 = hibit(p2); /* add into result */ + } + else + return v1; + + if(n2) /* repeat with values swapped */ + while(n1 >= n2) + { + n1 /= n2; + p1 ^= p2 * n1; + v1 ^= v2 * n1; + n1 = hibit(p1); + } + else + return v2; + } +} + +#endif + +/* The forward and inverse affine transformations used in the S-box */ +uint8_t fwd_affine(const uint8_t x) +{ uint32_t w = x; + w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4); + return 0x63 ^ ((w ^ (w >> 8)) & 0xff); +} + +uint8_t inv_affine(const uint8_t x) +{ uint32_t w = x; + w = (w << 1) ^ (w << 3) ^ (w << 6); + return 0x05 ^ ((w ^ (w >> 8)) & 0xff); +} + +static int init = 0; + +AES_RETURN aes_init(void) +{ uint32_t i, w; + +#if defined(FF_TABLES) + + uint8_t pow[512], log[256]; + + if(init) + return EXIT_SUCCESS; + /* log and power tables for GF(2^8) finite field with + WPOLY as modular polynomial - the simplest primitive + root is 0x03, used here to generate the tables + */ + + i = 0; w = 1; + do + { + pow[i] = (uint8_t)w; + pow[i + 255] = (uint8_t)w; + log[w] = (uint8_t)i++; + w ^= (w << 1) ^ (w & 0x80 ? WPOLY : 0); + } + while (w != 1); + +#else + if(init) + return EXIT_SUCCESS; +#endif + + for(i = 0, w = 1; i < RC_LENGTH; ++i) + { + t_set(r,c)[i] = bytes2word(w, 0, 0, 0); + w = f2(w); + } + + for(i = 0; i < 256; ++i) + { uint8_t b; + + b = fwd_affine(gf_inv((uint8_t)i)); + w = bytes2word(f2(b), b, b, f3(b)); + +#if defined( SBX_SET ) + t_set(s,box)[i] = b; +#endif + +#if defined( FT1_SET ) /* tables for a normal encryption round */ + t_set(f,n)[i] = w; +#endif +#if defined( FT4_SET ) + t_set(f,n)[0][i] = w; + t_set(f,n)[1][i] = upr(w,1); + t_set(f,n)[2][i] = upr(w,2); + t_set(f,n)[3][i] = upr(w,3); +#endif + w = bytes2word(b, 0, 0, 0); + +#if defined( FL1_SET ) /* tables for last encryption round (may also */ + t_set(f,l)[i] = w; /* be used in the key schedule) */ +#endif +#if defined( FL4_SET ) + t_set(f,l)[0][i] = w; + t_set(f,l)[1][i] = upr(w,1); + t_set(f,l)[2][i] = upr(w,2); + t_set(f,l)[3][i] = upr(w,3); +#endif + +#if defined( LS1_SET ) /* table for key schedule if t_set(f,l) above is*/ + t_set(l,s)[i] = w; /* not of the required form */ +#endif +#if defined( LS4_SET ) + t_set(l,s)[0][i] = w; + t_set(l,s)[1][i] = upr(w,1); + t_set(l,s)[2][i] = upr(w,2); + t_set(l,s)[3][i] = upr(w,3); +#endif + + b = gf_inv(inv_affine((uint8_t)i)); + w = bytes2word(fe(b), f9(b), fd(b), fb(b)); + +#if defined( IM1_SET ) /* tables for the inverse mix column operation */ + t_set(i,m)[b] = w; +#endif +#if defined( IM4_SET ) + t_set(i,m)[0][b] = w; + t_set(i,m)[1][b] = upr(w,1); + t_set(i,m)[2][b] = upr(w,2); + t_set(i,m)[3][b] = upr(w,3); +#endif + +#if defined( ISB_SET ) + t_set(i,box)[i] = b; +#endif +#if defined( IT1_SET ) /* tables for a normal decryption round */ + t_set(i,n)[i] = w; +#endif +#if defined( IT4_SET ) + t_set(i,n)[0][i] = w; + t_set(i,n)[1][i] = upr(w,1); + t_set(i,n)[2][i] = upr(w,2); + t_set(i,n)[3][i] = upr(w,3); +#endif + w = bytes2word(b, 0, 0, 0); +#if defined( IL1_SET ) /* tables for last decryption round */ + t_set(i,l)[i] = w; +#endif +#if defined( IL4_SET ) + t_set(i,l)[0][i] = w; + t_set(i,l)[1][i] = upr(w,1); + t_set(i,l)[2][i] = upr(w,2); + t_set(i,l)[3][i] = upr(w,3); +#endif + } + init = 1; + return EXIT_SUCCESS; +} + +/* + Automatic code initialisation (suggested by by Henrik S. Gaßmann) + based on code provided by Joe Lowe and placed in the public domain at: + http://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc +*/ + +#ifdef _MSC_VER + +#pragma section(".CRT$XCU", read) + +__declspec(allocate(".CRT$XCU")) void (__cdecl *aes_startup)(void) = aes_init; + +#elif defined(__GNUC__) + +static void aes_startup(void) __attribute__((constructor)); + +static void aes_startup(void) +{ + aes_init(); +} + +#else + +#pragma message( "dynamic tables must be initialised manually on your system" ) + +#endif + +#endif + +#if defined(__cplusplus) +} +#endif + diff --git a/SSZipArchive/minizip/aes/aestab.h b/SSZipArchive/minizip/aes/aestab.h new file mode 100644 index 0000000..8fe32d1 --- /dev/null +++ b/SSZipArchive/minizip/aes/aestab.h @@ -0,0 +1,173 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + + This file contains the code for declaring the tables needed to implement + AES. The file aesopt.h is assumed to be included before this header file. + If there are no global variables, the definitions here can be used to put + the AES tables in a structure so that a pointer can then be added to the + AES context to pass them to the AES routines that need them. If this + facility is used, the calling program has to ensure that this pointer is + managed appropriately. In particular, the value of the t_dec(in,it) item + in the table structure must be set to zero in order to ensure that the + tables are initialised. In practice the three code sequences in aeskey.c + that control the calls to aes_init() and the aes_init() routine itself will + have to be changed for a specific implementation. If global variables are + available it will generally be preferable to use them with the precomputed + STATIC_TABLES option that uses static global tables. + + The following defines can be used to control the way the tables + are defined, initialised and used in embedded environments that + require special features for these purposes + + the 't_dec' construction is used to declare fixed table arrays + the 't_set' construction is used to set fixed table values + the 't_use' construction is used to access fixed table values + + 256 byte tables: + + t_xxx(s,box) => forward S box + t_xxx(i,box) => inverse S box + + 256 32-bit word OR 4 x 256 32-bit word tables: + + t_xxx(f,n) => forward normal round + t_xxx(f,l) => forward last round + t_xxx(i,n) => inverse normal round + t_xxx(i,l) => inverse last round + t_xxx(l,s) => key schedule table + t_xxx(i,m) => key schedule table + + Other variables and tables: + + t_xxx(r,c) => the rcon table +*/ + +#if !defined( _AESTAB_H ) +#define _AESTAB_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#define t_dec(m,n) t_##m##n +#define t_set(m,n) t_##m##n +#define t_use(m,n) t_##m##n + +#if defined(STATIC_TABLES) +# if !defined( __GNUC__ ) && (defined( __MSDOS__ ) || defined( __WIN16__ )) +/* make tables far data to avoid using too much DGROUP space (PG) */ +# define CONST const far +# else +# define CONST const +# endif +#else +# define CONST +#endif + +#if defined(DO_TABLES) +# define EXTERN +#else +# define EXTERN extern +#endif + +#if defined(_MSC_VER) && defined(TABLE_ALIGN) +#define ALIGN __declspec(align(TABLE_ALIGN)) +#else +#define ALIGN +#endif + +#if defined( __WATCOMC__ ) && ( __WATCOMC__ >= 1100 ) +# define XP_DIR __cdecl +#else +# define XP_DIR +#endif + +#if defined(DO_TABLES) && defined(STATIC_TABLES) +#define d_1(t,n,b,e) EXTERN ALIGN CONST XP_DIR t n[256] = b(e) +#define d_4(t,n,b,e,f,g,h) EXTERN ALIGN CONST XP_DIR t n[4][256] = { b(e), b(f), b(g), b(h) } +EXTERN ALIGN CONST uint32_t t_dec(r,c)[RC_LENGTH] = rc_data(w0); +#else +#define d_1(t,n,b,e) EXTERN ALIGN CONST XP_DIR t n[256] +#define d_4(t,n,b,e,f,g,h) EXTERN ALIGN CONST XP_DIR t n[4][256] +EXTERN ALIGN CONST uint32_t t_dec(r,c)[RC_LENGTH]; +#endif + +#if defined( SBX_SET ) + d_1(uint8_t, t_dec(s,box), sb_data, h0); +#endif +#if defined( ISB_SET ) + d_1(uint8_t, t_dec(i,box), isb_data, h0); +#endif + +#if defined( FT1_SET ) + d_1(uint32_t, t_dec(f,n), sb_data, u0); +#endif +#if defined( FT4_SET ) + d_4(uint32_t, t_dec(f,n), sb_data, u0, u1, u2, u3); +#endif + +#if defined( FL1_SET ) + d_1(uint32_t, t_dec(f,l), sb_data, w0); +#endif +#if defined( FL4_SET ) + d_4(uint32_t, t_dec(f,l), sb_data, w0, w1, w2, w3); +#endif + +#if defined( IT1_SET ) + d_1(uint32_t, t_dec(i,n), isb_data, v0); +#endif +#if defined( IT4_SET ) + d_4(uint32_t, t_dec(i,n), isb_data, v0, v1, v2, v3); +#endif + +#if defined( IL1_SET ) + d_1(uint32_t, t_dec(i,l), isb_data, w0); +#endif +#if defined( IL4_SET ) + d_4(uint32_t, t_dec(i,l), isb_data, w0, w1, w2, w3); +#endif + +#if defined( LS1_SET ) +#if defined( FL1_SET ) +#undef LS1_SET +#else + d_1(uint32_t, t_dec(l,s), sb_data, w0); +#endif +#endif + +#if defined( LS4_SET ) +#if defined( FL4_SET ) +#undef LS4_SET +#else + d_4(uint32_t, t_dec(l,s), sb_data, w0, w1, w2, w3); +#endif +#endif + +#if defined( IM1_SET ) + d_1(uint32_t, t_dec(i,m), mm_data, v0); +#endif +#if defined( IM4_SET ) + d_4(uint32_t, t_dec(i,m), mm_data, v0, v1, v2, v3); +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/SSZipArchive/minizip/aes/brg_endian.h b/SSZipArchive/minizip/aes/brg_endian.h new file mode 100644 index 0000000..9ef4af5 --- /dev/null +++ b/SSZipArchive/minizip/aes/brg_endian.h @@ -0,0 +1,127 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#ifndef _BRG_ENDIAN_H +#define _BRG_ENDIAN_H + +#define IS_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */ +#define IS_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */ + +/* Include files where endian defines and byteswap functions may reside */ +#if defined( __sun ) +# include +#elif defined( __FreeBSD__ ) || defined( __OpenBSD__ ) || defined( __NetBSD__ ) +# include +#elif defined( BSD ) && ( BSD >= 199103 ) || defined( __APPLE__ ) || \ + defined( __CYGWIN32__ ) || defined( __DJGPP__ ) || defined( __osf__ ) || \ + defined(__pnacl__) +# include +#elif defined( __linux__ ) || defined( __GNUC__ ) || defined( __GNU_LIBRARY__ ) +# if !defined( __MINGW32__ ) && !defined( _AIX ) +# include +# if !defined( __BEOS__ ) +# include +# endif +# endif +#endif + +/* Now attempt to set the define for platform byte order using any */ +/* of the four forms SYMBOL, _SYMBOL, __SYMBOL & __SYMBOL__, which */ +/* seem to encompass most endian symbol definitions */ + +#if defined( BIG_ENDIAN ) && defined( LITTLE_ENDIAN ) +# if defined( BYTE_ORDER ) && BYTE_ORDER == BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( BYTE_ORDER ) && BYTE_ORDER == LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( BIG_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( LITTLE_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if defined( _BIG_ENDIAN ) && defined( _LITTLE_ENDIAN ) +# if defined( _BYTE_ORDER ) && _BYTE_ORDER == _BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( _BYTE_ORDER ) && _BYTE_ORDER == _LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( _BIG_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( _LITTLE_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if defined( __BIG_ENDIAN ) && defined( __LITTLE_ENDIAN ) +# if defined( __BYTE_ORDER ) && __BYTE_ORDER == __BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( __BYTE_ORDER ) && __BYTE_ORDER == __LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( __BIG_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( __LITTLE_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if defined( __BIG_ENDIAN__ ) && defined( __LITTLE_ENDIAN__ ) +# if defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __BIG_ENDIAN__ +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __LITTLE_ENDIAN__ +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( __BIG_ENDIAN__ ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( __LITTLE_ENDIAN__ ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +/* if the platform byte order could not be determined, then try to */ +/* set this define using common machine defines */ +#if !defined(PLATFORM_BYTE_ORDER) + +#if defined( __alpha__ ) || defined( __alpha ) || defined( i386 ) || \ + defined( __i386__ ) || defined( _M_I86 ) || defined( _M_IX86 ) || \ + defined( __OS2__ ) || defined( sun386 ) || defined( __TURBOC__ ) || \ + defined( vax ) || defined( vms ) || defined( VMS ) || \ + defined( __VMS ) || defined( _M_X64 ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN + +#elif defined( AMIGA ) || defined( applec ) || defined( __AS400__ ) || \ + defined( _CRAY ) || defined( __hppa ) || defined( __hp9000 ) || \ + defined( ibm370 ) || defined( mc68000 ) || defined( m68k ) || \ + defined( __MRC__ ) || defined( __MVS__ ) || defined( __MWERKS__ ) || \ + defined( sparc ) || defined( __sparc) || defined( SYMANTEC_C ) || \ + defined( __VOS__ ) || defined( __TIGCC__ ) || defined( __TANDEM ) || \ + defined( THINK_C ) || defined( __VMCMS__ ) || defined( _AIX ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN + +#elif 0 /* **** EDIT HERE IF NECESSARY **** */ +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#elif 0 /* **** EDIT HERE IF NECESSARY **** */ +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#else +# error Please edit lines 126 or 128 in brg_endian.h to set the platform byte order +#endif + +#endif + +#endif diff --git a/SSZipArchive/minizip/aes/brg_types.h b/SSZipArchive/minizip/aes/brg_types.h new file mode 100644 index 0000000..307319b --- /dev/null +++ b/SSZipArchive/minizip/aes/brg_types.h @@ -0,0 +1,191 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + + The unsigned integer types defined here are of the form uint_t where + is the length of the type; for example, the unsigned 32-bit type is + 'uint32_t'. These are NOT the same as the 'C99 integer types' that are + defined in the inttypes.h and stdint.h headers since attempts to use these + types have shown that support for them is still highly variable. However, + since the latter are of the form uint_t, a regular expression search + and replace (in VC++ search on 'uint_{:z}t' and replace with 'uint\1_t') + can be used to convert the types used here to the C99 standard types. +*/ + +#ifndef _BRG_TYPES_H +#define _BRG_TYPES_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include + +#if defined( _MSC_VER ) && ( _MSC_VER >= 1300 ) +# include +# define ptrint_t intptr_t +#elif defined( __ECOS__ ) +# define intptr_t unsigned int +# define ptrint_t intptr_t +#elif defined( __GNUC__ ) && ( __GNUC__ >= 3 ) +# define ptrint_t intptr_t +#else +# define ptrint_t int +#endif + +#ifndef BRG_UI32 +# define BRG_UI32 +# if UINT_MAX == 4294967295u +# define li_32(h) 0x##h##u +# elif ULONG_MAX == 4294967295u +# define li_32(h) 0x##h##ul +# elif defined( _CRAY ) +# error This code needs 32-bit data types, which Cray machines do not provide +# else +# error Please define uint32_t as a 32-bit unsigned integer type in brg_types.h +# endif +#endif + +#ifndef BRG_UI64 +# if defined( __BORLANDC__ ) && !defined( __MSDOS__ ) +# define BRG_UI64 +# define li_64(h) 0x##h##ui64 +# elif defined( _MSC_VER ) && ( _MSC_VER < 1300 ) /* 1300 == VC++ 7.0 */ +# define BRG_UI64 +# define li_64(h) 0x##h##ui64 +# elif defined( __sun ) && defined( ULONG_MAX ) && ULONG_MAX == 0xfffffffful +# define BRG_UI64 +# define li_64(h) 0x##h##ull +# elif defined( __MVS__ ) +# define BRG_UI64 +# define li_64(h) 0x##h##ull +# elif defined( UINT_MAX ) && UINT_MAX > 4294967295u +# if UINT_MAX == 18446744073709551615u +# define BRG_UI64 +# define li_64(h) 0x##h##u +# endif +# elif defined( ULONG_MAX ) && ULONG_MAX > 4294967295u +# if ULONG_MAX == 18446744073709551615ul +# define BRG_UI64 +# define li_64(h) 0x##h##ul +# endif +# elif defined( ULLONG_MAX ) && ULLONG_MAX > 4294967295u +# if ULLONG_MAX == 18446744073709551615ull +# define BRG_UI64 +# define li_64(h) 0x##h##ull +# endif +# elif defined( ULONG_LONG_MAX ) && ULONG_LONG_MAX > 4294967295u +# if ULONG_LONG_MAX == 18446744073709551615ull +# define BRG_UI64 +# define li_64(h) 0x##h##ull +# endif +# endif +#endif + +#if !defined( BRG_UI64 ) +# if defined( NEED_UINT_64T ) +# error Please define uint64_t as an unsigned 64 bit type in brg_types.h +# endif +#endif + +#ifndef RETURN_VALUES +# define RETURN_VALUES +# if defined( DLL_EXPORT ) +# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER ) +# define VOID_RETURN __declspec( dllexport ) void __stdcall +# define INT_RETURN __declspec( dllexport ) int __stdcall +# elif defined( __GNUC__ ) +# define VOID_RETURN __declspec( __dllexport__ ) void +# define INT_RETURN __declspec( __dllexport__ ) int +# else +# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers +# endif +# elif defined( DLL_IMPORT ) +# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER ) +# define VOID_RETURN __declspec( dllimport ) void __stdcall +# define INT_RETURN __declspec( dllimport ) int __stdcall +# elif defined( __GNUC__ ) +# define VOID_RETURN __declspec( __dllimport__ ) void +# define INT_RETURN __declspec( __dllimport__ ) int +# else +# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers +# endif +# elif defined( __WATCOMC__ ) +# define VOID_RETURN void __cdecl +# define INT_RETURN int __cdecl +# else +# define VOID_RETURN void +# define INT_RETURN int +# endif +#endif + +/* These defines are used to detect and set the memory alignment of pointers. + Note that offsets are in bytes. + + ALIGN_OFFSET(x,n) return the positive or zero offset of + the memory addressed by the pointer 'x' + from an address that is aligned on an + 'n' byte boundary ('n' is a power of 2) + + ALIGN_FLOOR(x,n) return a pointer that points to memory + that is aligned on an 'n' byte boundary + and is not higher than the memory address + pointed to by 'x' ('n' is a power of 2) + + ALIGN_CEIL(x,n) return a pointer that points to memory + that is aligned on an 'n' byte boundary + and is not lower than the memory address + pointed to by 'x' ('n' is a power of 2) +*/ + +#define ALIGN_OFFSET(x,n) (((ptrint_t)(x)) & ((n) - 1)) +#define ALIGN_FLOOR(x,n) ((uint8_t*)(x) - ( ((ptrint_t)(x)) & ((n) - 1))) +#define ALIGN_CEIL(x,n) ((uint8_t*)(x) + (-((ptrint_t)(x)) & ((n) - 1))) + +/* These defines are used to declare buffers in a way that allows + faster operations on longer variables to be used. In all these + defines 'size' must be a power of 2 and >= 8. NOTE that the + buffer size is in bytes but the type length is in bits + + UNIT_TYPEDEF(x,size) declares a variable 'x' of length + 'size' bits + + BUFR_TYPEDEF(x,size,bsize) declares a buffer 'x' of length 'bsize' + bytes defined as an array of variables + each of 'size' bits (bsize must be a + multiple of size / 8) + + UNIT_CAST(x,size) casts a variable to a type of + length 'size' bits + + UPTR_CAST(x,size) casts a pointer to a pointer to a + varaiable of length 'size' bits +*/ + +#define UI_TYPE(size) uint##size##_t +#define UNIT_TYPEDEF(x,size) typedef UI_TYPE(size) x +#define BUFR_TYPEDEF(x,size,bsize) typedef UI_TYPE(size) x[bsize / (size >> 3)] +#define UNIT_CAST(x,size) ((UI_TYPE(size) )(x)) +#define UPTR_CAST(x,size) ((UI_TYPE(size)*)(x)) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/SSZipArchive/minizip/aes/fileenc.c b/SSZipArchive/minizip/aes/fileenc.c new file mode 100644 index 0000000..31a8994 --- /dev/null +++ b/SSZipArchive/minizip/aes/fileenc.c @@ -0,0 +1,145 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK. + All rights reserved. + + LICENSE TERMS + + The free distribution and use of this software in both source and binary + form is allowed (with or without changes) provided that: + + 1. distributions of this source code include the above copyright + notice, this list of conditions and the following disclaimer; + + 2. distributions in binary form include the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other associated materials; + + 3. the copyright holder's name is not used to endorse products + built using this software without specific written permission. + + ALTERNATIVELY, provided that this notice is retained in full, this product + may be distributed under the terms of the GNU General Public License (GPL), + in which case the provisions of the GPL apply INSTEAD OF those given above. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + ------------------------------------------------------------------------- + Issue Date: 24/01/2003 + + This file implements password based file encryption and authentication + using AES in CTR mode, HMAC-SHA1 authentication and RFC2898 password + based key derivation. + +*/ + +#include + +#include "fileenc.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + +/* subroutine for data encryption/decryption */ +/* this could be speeded up a lot by aligning */ +/* buffers and using 32 bit operations */ + +static void encr_data(unsigned char data[], unsigned long d_len, fcrypt_ctx cx[1]) +{ + unsigned int i = 0, pos = cx->encr_pos; + + while (i < d_len) + { + if (pos == AES_BLOCK_SIZE) + { + unsigned int j = 0; + /* increment encryption nonce */ + while (j < 8 && !++cx->nonce[j]) + ++j; + /* encrypt the nonce to form next xor buffer */ + aes_encrypt(cx->nonce, cx->encr_bfr, cx->encr_ctx); + pos = 0; + } + + data[i++] ^= cx->encr_bfr[pos++]; + } + + cx->encr_pos = pos; +} + +int fcrypt_init( + int mode, /* the mode to be used (input) */ + const unsigned char pwd[], /* the user specified password (input) */ + unsigned int pwd_len, /* the length of the password (input) */ + const unsigned char salt[], /* the salt (input) */ +#ifdef PASSWORD_VERIFIER + unsigned char pwd_ver[PWD_VER_LENGTH], /* 2 byte password verifier (output) */ +#endif + fcrypt_ctx cx[1]) /* the file encryption context (output) */ +{ unsigned char kbuf[2 * MAX_KEY_LENGTH + PWD_VER_LENGTH]; + + if (pwd_len > MAX_PWD_LENGTH) + return PASSWORD_TOO_LONG; + + if (mode < 1 || mode > 3) + return BAD_MODE; + + cx->mode = mode; + cx->pwd_len = pwd_len; + + /* derive the encryption and authentication keys and the password verifier */ + derive_key(pwd, pwd_len, salt, SALT_LENGTH(mode), KEYING_ITERATIONS, + kbuf, 2 * KEY_LENGTH(mode) + PWD_VER_LENGTH); + + /* initialise the encryption nonce and buffer pos */ + cx->encr_pos = AES_BLOCK_SIZE; + /* if we need a random component in the encryption */ + /* nonce, this is where it would have to be set */ + memset(cx->nonce, 0, AES_BLOCK_SIZE * sizeof(unsigned char)); + + /* initialise for encryption using key 1 */ + aes_encrypt_key(kbuf, KEY_LENGTH(mode), cx->encr_ctx); + + /* initialise for authentication using key 2 */ + hmac_sha_begin(HMAC_SHA1, cx->auth_ctx); + hmac_sha_key(kbuf + KEY_LENGTH(mode), KEY_LENGTH(mode), cx->auth_ctx); + +#ifdef PASSWORD_VERIFIER + memcpy(pwd_ver, kbuf + 2 * KEY_LENGTH(mode), PWD_VER_LENGTH); +#endif + + return GOOD_RETURN; +} + +/* perform 'in place' encryption and authentication */ + +void fcrypt_encrypt(unsigned char data[], unsigned int data_len, fcrypt_ctx cx[1]) +{ + encr_data(data, data_len, cx); + hmac_sha_data(data, data_len, cx->auth_ctx); +} + +/* perform 'in place' authentication and decryption */ + +void fcrypt_decrypt(unsigned char data[], unsigned int data_len, fcrypt_ctx cx[1]) +{ + hmac_sha_data(data, data_len, cx->auth_ctx); + encr_data(data, data_len, cx); +} + +/* close encryption/decryption and return the MAC value */ + +int fcrypt_end(unsigned char mac[], fcrypt_ctx cx[1]) +{ + hmac_sha_end(mac, MAC_LENGTH(cx->mode), cx->auth_ctx); + return MAC_LENGTH(cx->mode); /* return MAC length in bytes */ +} + +#if defined(__cplusplus) +} +#endif diff --git a/SSZipArchive/minizip/aes/fileenc.h b/SSZipArchive/minizip/aes/fileenc.h new file mode 100644 index 0000000..9c0250e --- /dev/null +++ b/SSZipArchive/minizip/aes/fileenc.h @@ -0,0 +1,121 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK. + All rights reserved. + + LICENSE TERMS + + The free distribution and use of this software in both source and binary + form is allowed (with or without changes) provided that: + + 1. distributions of this source code include the above copyright + notice, this list of conditions and the following disclaimer; + + 2. distributions in binary form include the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other associated materials; + + 3. the copyright holder's name is not used to endorse products + built using this software without specific written permission. + + ALTERNATIVELY, provided that this notice is retained in full, this product + may be distributed under the terms of the GNU General Public License (GPL), + in which case the provisions of the GPL apply INSTEAD OF those given above. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 24/01/2003 + + This file contains the header file for fileenc.c, which implements password + based file encryption and authentication using AES in CTR mode, HMAC-SHA1 + authentication and RFC2898 password based key derivation. +*/ + +#ifndef _FENC_H +#define _FENC_H + +#include "aes.h" +#include "hmac.h" +#include "pwd2key.h" + +#define PASSWORD_VERIFIER + +#define MAX_KEY_LENGTH 32 +#define MAX_PWD_LENGTH 128 +#define MAX_SALT_LENGTH 16 +#define KEYING_ITERATIONS 1000 + +#ifdef PASSWORD_VERIFIER +#define PWD_VER_LENGTH 2 +#else +#define PWD_VER_LENGTH 0 +#endif + +#define GOOD_RETURN 0 +#define PASSWORD_TOO_LONG -100 +#define BAD_MODE -101 + +/* + Field lengths (in bytes) versus File Encryption Mode (0 < mode < 4) + + Mode Key Salt MAC Overhead + 1 16 8 10 18 + 2 24 12 10 22 + 3 32 16 10 26 + + The following macros assume that the mode value is correct. +*/ + +#define KEY_LENGTH(mode) (8 * (mode & 3) + 8) +#define SALT_LENGTH(mode) (4 * (mode & 3) + 4) +#define MAC_LENGTH(mode) (10) + +/* the context for file encryption */ + +#if defined(__cplusplus) +extern "C" +{ +#endif + +typedef struct +{ unsigned char nonce[AES_BLOCK_SIZE]; /* the CTR nonce */ + unsigned char encr_bfr[AES_BLOCK_SIZE]; /* encrypt buffer */ + aes_encrypt_ctx encr_ctx[1]; /* encryption context */ + hmac_ctx auth_ctx[1]; /* authentication context */ + unsigned int encr_pos; /* block position (enc) */ + unsigned int pwd_len; /* password length */ + unsigned int mode; /* File encryption mode */ +} fcrypt_ctx; + +/* initialise file encryption or decryption */ + +int fcrypt_init( + int mode, /* the mode to be used (input) */ + const unsigned char pwd[], /* the user specified password (input) */ + unsigned int pwd_len, /* the length of the password (input) */ + const unsigned char salt[], /* the salt (input) */ +#ifdef PASSWORD_VERIFIER + unsigned char pwd_ver[PWD_VER_LENGTH], /* 2 byte password verifier (output) */ +#endif + fcrypt_ctx cx[1]); /* the file encryption context (output) */ + +/* perform 'in place' encryption or decryption and authentication */ + +void fcrypt_encrypt(unsigned char data[], unsigned int data_len, fcrypt_ctx cx[1]); +void fcrypt_decrypt(unsigned char data[], unsigned int data_len, fcrypt_ctx cx[1]); + +/* close encryption/decryption and return the MAC value */ +/* the return value is the length of the MAC */ + +int fcrypt_end(unsigned char mac[], /* the MAC value (output) */ + fcrypt_ctx cx[1]); /* the context (input) */ + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/SSZipArchive/minizip/aes/hmac.c b/SSZipArchive/minizip/aes/hmac.c new file mode 100644 index 0000000..face1fb --- /dev/null +++ b/SSZipArchive/minizip/aes/hmac.c @@ -0,0 +1,209 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + +This is an implementation of HMAC, the FIPS standard keyed hash function +*/ + +#include "hmac.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + +/* initialise the HMAC context to zero */ +int hmac_sha_begin(enum hmac_hash hash, hmac_ctx cx[1]) +{ + memset(cx, 0, sizeof(hmac_ctx)); + switch(hash) + { +#ifdef SHA_1 + case HMAC_SHA1: + cx->f_begin = (hf_begin *)sha1_begin; + cx->f_hash = (hf_hash *)sha1_hash; + cx->f_end = (hf_end *)sha1_end; + cx->input_len = SHA1_BLOCK_SIZE; + cx->output_len = SHA1_DIGEST_SIZE; + break; +#endif +#ifdef SHA_224 + case HMAC_SHA224: + cx->f_begin = (hf_begin *)sha224_begin; + cx->f_hash = (hf_hash *)sha224_hash; + cx->f_end = (hf_end *)sha224_end; + cx->input_len = SHA224_BLOCK_SIZE; + cx->output_len = SHA224_DIGEST_SIZE; + break; +#endif +#ifdef SHA_256 + case HMAC_SHA256: + cx->f_begin = (hf_begin *)sha256_begin; + cx->f_hash = (hf_hash *)sha256_hash; + cx->f_end = (hf_end *)sha256_end; + cx->input_len = SHA256_BLOCK_SIZE; + cx->output_len = SHA256_DIGEST_SIZE; + break; +#endif +#ifdef SHA_384 + case HMAC_SHA384: + cx->f_begin = (hf_begin *)sha384_begin; + cx->f_hash = (hf_hash *)sha384_hash; + cx->f_end = (hf_end *)sha384_end; + cx->input_len = SHA384_BLOCK_SIZE; + cx->output_len = SHA384_DIGEST_SIZE; + break; +#endif +#ifdef SHA_512 + case HMAC_SHA512: + cx->f_begin = (hf_begin *)sha512_begin; + cx->f_hash = (hf_hash *)sha512_hash; + cx->f_end = (hf_end *)sha512_end; + cx->input_len = SHA512_BLOCK_SIZE; + cx->output_len = SHA512_DIGEST_SIZE; + break; + case HMAC_SHA512_256: + cx->f_begin = (hf_begin *)sha512_256_begin; + cx->f_hash = (hf_hash *)sha512_256_hash; + cx->f_end = (hf_end *)sha512_256_end; + cx->input_len = SHA512_256_BLOCK_SIZE; + cx->output_len = SHA512_256_DIGEST_SIZE; + break; + case HMAC_SHA512_224: + cx->f_begin = (hf_begin *)sha512_224_begin; + cx->f_hash = (hf_hash *)sha512_224_hash; + cx->f_end = (hf_end *)sha512_224_end; + cx->input_len = SHA512_224_BLOCK_SIZE; + cx->output_len = SHA512_224_DIGEST_SIZE; + break; + case HMAC_SHA512_192: + cx->f_begin = (hf_begin *)sha512_192_begin; + cx->f_hash = (hf_hash *)sha512_192_hash; + cx->f_end = (hf_end *)sha512_192_end; + cx->input_len = SHA512_192_BLOCK_SIZE; + cx->output_len = SHA512_192_DIGEST_SIZE; + break; + case HMAC_SHA512_128: + cx->f_begin = (hf_begin *)sha512_128_begin; + cx->f_hash = (hf_hash *)sha512_128_hash; + cx->f_end = (hf_begin *)sha512_128_end; + cx->input_len = SHA512_128_BLOCK_SIZE; + cx->output_len = SHA512_128_DIGEST_SIZE; + break; +#endif + } + return (int)cx->output_len; +} + +/* input the HMAC key (can be called multiple times) */ +int hmac_sha_key(const unsigned char key[], unsigned long key_len, hmac_ctx cx[1]) +{ + if(cx->klen == HMAC_IN_DATA) /* error if further key input */ + return EXIT_FAILURE; /* is attempted in data mode */ + + if(cx->klen + key_len > cx->input_len) /* if the key has to be hashed */ + { + if(cx->klen <= cx->input_len) /* if the hash has not yet been */ + { /* started, initialise it and */ + cx->f_begin(cx->sha_ctx); /* hash stored key characters */ + cx->f_hash(cx->key, cx->klen, cx->sha_ctx); + } + + cx->f_hash(key, key_len, cx->sha_ctx); /* hash long key data into hash */ + } + else /* otherwise store key data */ + memcpy(cx->key + cx->klen, key, key_len); + + cx->klen += key_len; /* update the key length count */ + return EXIT_SUCCESS; +} + +/* input the HMAC data (can be called multiple times) - */ +/* note that this call terminates the key input phase */ +void hmac_sha_data(const unsigned char data[], unsigned long data_len, hmac_ctx cx[1]) +{ unsigned int i; + + if(cx->klen != HMAC_IN_DATA) /* if not yet in data phase */ + { + if(cx->klen > cx->input_len) /* if key is being hashed */ + { /* complete the hash and */ + cx->f_end(cx->key, cx->sha_ctx); /* store the result as the */ + cx->klen = cx->output_len; /* key and set new length */ + } + + /* pad the key if necessary */ + memset(cx->key + cx->klen, 0, cx->input_len - cx->klen); + + /* xor ipad into key value */ + for(i = 0; i < (cx->input_len >> 2); ++i) + ((uint32_t*)cx->key)[i] ^= 0x36363636; + + /* and start hash operation */ + cx->f_begin(cx->sha_ctx); + cx->f_hash(cx->key, cx->input_len, cx->sha_ctx); + + /* mark as now in data mode */ + cx->klen = HMAC_IN_DATA; + } + + /* hash the data (if any) */ + if(data_len) + cx->f_hash(data, data_len, cx->sha_ctx); +} + +/* compute and output the MAC value */ +void hmac_sha_end(unsigned char mac[], unsigned long mac_len, hmac_ctx cx[1]) +{ unsigned char dig[HMAC_MAX_OUTPUT_SIZE]; + unsigned int i; + + /* if no data has been entered perform a null data phase */ + if(cx->klen != HMAC_IN_DATA) + hmac_sha_data((const unsigned char*)0, 0, cx); + + cx->f_end(dig, cx->sha_ctx); /* complete the inner hash */ + + /* set outer key value using opad and removing ipad */ + for(i = 0; i < (cx->input_len >> 2); ++i) + ((uint32_t*)cx->key)[i] ^= 0x36363636 ^ 0x5c5c5c5c; + + /* perform the outer hash operation */ + cx->f_begin(cx->sha_ctx); + cx->f_hash(cx->key, cx->input_len, cx->sha_ctx); + cx->f_hash(dig, cx->output_len, cx->sha_ctx); + cx->f_end(dig, cx->sha_ctx); + + /* output the hash value */ + for(i = 0; i < mac_len; ++i) + mac[i] = dig[i]; +} + +/* 'do it all in one go' subroutine */ +void hmac_sha(enum hmac_hash hash, const unsigned char key[], unsigned long key_len, + const unsigned char data[], unsigned long data_len, + unsigned char mac[], unsigned long mac_len) +{ hmac_ctx cx[1]; + + hmac_sha_begin(hash, cx); + hmac_sha_key(key, key_len, cx); + hmac_sha_data(data, data_len, cx); + hmac_sha_end(mac, mac_len, cx); +} + +#if defined(__cplusplus) +} +#endif diff --git a/SSZipArchive/minizip/aes/hmac.h b/SSZipArchive/minizip/aes/hmac.h new file mode 100644 index 0000000..46956c4 --- /dev/null +++ b/SSZipArchive/minizip/aes/hmac.h @@ -0,0 +1,119 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + +This is an implementation of HMAC, the FIPS standard keyed hash function +*/ + +#ifndef _HMAC2_H +#define _HMAC2_H + +#include +#include + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#include "sha1.h" + +#if defined(SHA_224) || defined(SHA_256) || defined(SHA_384) || defined(SHA_512) +#define HMAC_MAX_OUTPUT_SIZE SHA2_MAX_DIGEST_SIZE +#define HMAC_MAX_BLOCK_SIZE SHA2_MAX_BLOCK_SIZE +#else +#define HMAC_MAX_OUTPUT_SIZE SHA1_DIGEST_SIZE +#define HMAC_MAX_BLOCK_SIZE SHA1_BLOCK_SIZE +#endif + +#define HMAC_IN_DATA 0xffffffff + +enum hmac_hash +{ +#ifdef SHA_1 + HMAC_SHA1, +#endif +#ifdef SHA_224 + HMAC_SHA224, +#endif +#ifdef SHA_256 + HMAC_SHA256, +#endif +#ifdef SHA_384 + HMAC_SHA384, +#endif +#ifdef SHA_512 + HMAC_SHA512, + HMAC_SHA512_256, + HMAC_SHA512_224, + HMAC_SHA512_192, + HMAC_SHA512_128 +#endif +}; + +typedef VOID_RETURN hf_begin(void*); +typedef VOID_RETURN hf_hash(const void*, unsigned long len, void*); +typedef VOID_RETURN hf_end(void*, void*); + +typedef struct +{ hf_begin *f_begin; + hf_hash *f_hash; + hf_end *f_end; + unsigned char key[HMAC_MAX_BLOCK_SIZE]; + union + { +#ifdef SHA_1 + sha1_ctx u_sha1; +#endif +#ifdef SHA_224 + sha224_ctx u_sha224; +#endif +#ifdef SHA_256 + sha256_ctx u_sha256; +#endif +#ifdef SHA_384 + sha384_ctx u_sha384; +#endif +#ifdef SHA_512 + sha512_ctx u_sha512; +#endif + } sha_ctx[1]; + unsigned long input_len; + unsigned long output_len; + unsigned long klen; +} hmac_ctx; + +/* returns the length of hash digest for the hash used */ +/* mac_len must not be greater than this */ +int hmac_sha_begin(enum hmac_hash hash, hmac_ctx cx[1]); + +int hmac_sha_key(const unsigned char key[], unsigned long key_len, hmac_ctx cx[1]); + +void hmac_sha_data(const unsigned char data[], unsigned long data_len, hmac_ctx cx[1]); + +void hmac_sha_end(unsigned char mac[], unsigned long mac_len, hmac_ctx cx[1]); + +void hmac_sha(enum hmac_hash hash, const unsigned char key[], unsigned long key_len, + const unsigned char data[], unsigned long data_len, + unsigned char mac[], unsigned long mac_len); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/SSZipArchive/minizip/aes/prng.c b/SSZipArchive/minizip/aes/prng.c new file mode 100644 index 0000000..b90fcae --- /dev/null +++ b/SSZipArchive/minizip/aes/prng.c @@ -0,0 +1,155 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK. + All rights reserved. + + LICENSE TERMS + + The free distribution and use of this software in both source and binary + form is allowed (with or without changes) provided that: + + 1. distributions of this source code include the above copyright + notice, this list of conditions and the following disclaimer; + + 2. distributions in binary form include the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other associated materials; + + 3. the copyright holder's name is not used to endorse products + built using this software without specific written permission. + + ALTERNATIVELY, provided that this notice is retained in full, this product + may be distributed under the terms of the GNU General Public License (GPL), + in which case the provisions of the GPL apply INSTEAD OF those given above. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 24/01/2003 + + This file implements a random data pool based on the use of an external + entropy function. It is based on the ideas advocated by Peter Gutmann in + his work on pseudo random sequence generators. It is not a 'paranoid' + random sequence generator and no attempt is made to protect the pool + from prying eyes either by memory locking or by techniques to obscure + its location in memory. +*/ + +#include +#include "prng.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + +/* mix a random data pool using the SHA1 compression function (as */ +/* suggested by Peter Gutmann in his paper on random pools) */ + +static void prng_mix(unsigned char buf[]) +{ unsigned int i, len; + sha1_ctx ctx[1]; + + /*lint -e{663} unusual array to pointer conversion */ + for(i = 0; i < PRNG_POOL_SIZE; i += SHA1_DIGEST_SIZE) + { + /* copy digest size pool block into SHA1 hash block */ + memcpy(ctx->hash, buf + (i ? i : PRNG_POOL_SIZE) + - SHA1_DIGEST_SIZE, SHA1_DIGEST_SIZE); + + /* copy data from pool into the SHA1 data buffer */ + len = PRNG_POOL_SIZE - i; + memcpy(ctx->wbuf, buf + i, (len > SHA1_BLOCK_SIZE ? SHA1_BLOCK_SIZE : len)); + + if(len < SHA1_BLOCK_SIZE) + memcpy(((char*)ctx->wbuf) + len, buf, SHA1_BLOCK_SIZE - len); + + /* compress using the SHA1 compression function */ + sha1_compile(ctx); + + /* put digest size block back into the random pool */ + memcpy(buf + i, ctx->hash, SHA1_DIGEST_SIZE); + } +} + +/* refresh the output buffer and update the random pool by adding */ +/* entropy and remixing */ + +static void update_pool(prng_ctx ctx[1]) +{ unsigned int i = 0; + + /* transfer random pool data to the output buffer */ + memcpy(ctx->obuf, ctx->rbuf, PRNG_POOL_SIZE); + + /* enter entropy data into the pool */ + while(i < PRNG_POOL_SIZE) + i += ctx->entropy(ctx->rbuf + i, PRNG_POOL_SIZE - i); + + /* invert and xor the original pool data into the pool */ + for(i = 0; i < PRNG_POOL_SIZE; ++i) + ctx->rbuf[i] ^= ~ctx->obuf[i]; + + /* mix the pool and the output buffer */ + prng_mix(ctx->rbuf); + prng_mix(ctx->obuf); +} + +void prng_init(prng_entropy_fn fun, prng_ctx ctx[1]) +{ int i; + + /* clear the buffers and the counter in the context */ + memset(ctx, 0, sizeof(prng_ctx)); + + /* set the pointer to the entropy collection function */ + ctx->entropy = fun; + + /* initialise the random data pool */ + update_pool(ctx); + + /* mix the pool a minimum number of times */ + for(i = 0; i < PRNG_MIN_MIX; ++i) + prng_mix(ctx->rbuf); + + /* update the pool to prime the pool output buffer */ + update_pool(ctx); +} + +/* provide random bytes from the random data pool */ + +void prng_rand(unsigned char data[], unsigned int data_len, prng_ctx ctx[1]) +{ unsigned char *rp = data; + unsigned int len, pos = ctx->pos; + + while(data_len) + { + /* transfer 'data_len' bytes (or the number of bytes remaining */ + /* the pool output buffer if less) into the output */ + len = (data_len < PRNG_POOL_SIZE - pos ? data_len : PRNG_POOL_SIZE - pos); + memcpy(rp, ctx->obuf + pos, len); + rp += len; /* update ouput buffer position pointer */ + pos += len; /* update pool output buffer pointer */ + data_len -= len; /* update the remaining data count */ + + /* refresh the random pool if necessary */ + if(pos == PRNG_POOL_SIZE) + { + update_pool(ctx); pos = 0; + } + } + + ctx->pos = pos; +} + +void prng_end(prng_ctx ctx[1]) +{ + /* ensure the data in the context is destroyed */ + memset(ctx, 0, sizeof(prng_ctx)); +} + +#if defined(__cplusplus) +} +#endif + diff --git a/SSZipArchive/minizip/aes/prng.h b/SSZipArchive/minizip/aes/prng.h new file mode 100644 index 0000000..9a77426 --- /dev/null +++ b/SSZipArchive/minizip/aes/prng.h @@ -0,0 +1,82 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK. + All rights reserved. + + LICENSE TERMS + + The free distribution and use of this software in both source and binary + form is allowed (with or without changes) provided that: + + 1. distributions of this source code include the above copyright + notice, this list of conditions and the following disclaimer; + + 2. distributions in binary form include the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other associated materials; + + 3. the copyright holder's name is not used to endorse products + built using this software without specific written permission. + + ALTERNATIVELY, provided that this notice is retained in full, this product + may be distributed under the terms of the GNU General Public License (GPL), + in which case the provisions of the GPL apply INSTEAD OF those given above. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 24/01/2003 + + This is the header file for an implementation of a random data pool based on + the use of an external entropy function (inspired by Peter Gutmann's work). +*/ + +#ifndef _PRNG_H +#define _PRNG_H + +#include "sha1.h" + +#define PRNG_POOL_LEN 256 /* minimum random pool size */ +#define PRNG_MIN_MIX 20 /* min initial pool mixing iterations */ + +/* ensure that pool length is a multiple of the SHA1 digest size */ + +#define PRNG_POOL_SIZE (SHA1_DIGEST_SIZE * (1 + (PRNG_POOL_LEN - 1) / SHA1_DIGEST_SIZE)) + +#if defined(__cplusplus) +extern "C" +{ +#endif + +/* A function for providing entropy is a parameter in the prng_init() */ +/* call. This function has the following form and returns a maximum */ +/* of 'len' bytes of pseudo random data in the buffer 'buf'. It can */ +/* return less than 'len' bytes but will be repeatedly called for more */ +/* data in this case. */ + +typedef int (*prng_entropy_fn)(unsigned char buf[], unsigned int len); + +typedef struct +{ unsigned char rbuf[PRNG_POOL_SIZE]; /* the random pool */ + unsigned char obuf[PRNG_POOL_SIZE]; /* pool output buffer */ + unsigned int pos; /* output buffer position */ + prng_entropy_fn entropy; /* entropy function pointer */ +} prng_ctx; + +/* initialise the random stream generator */ +void prng_init(prng_entropy_fn fun, prng_ctx ctx[1]); + +/* obtain random bytes from the generator */ +void prng_rand(unsigned char data[], unsigned int data_len, prng_ctx ctx[1]); + +/* close the random stream generator */ +void prng_end(prng_ctx ctx[1]); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/SSZipArchive/minizip/aes/pwd2key.c b/SSZipArchive/minizip/aes/pwd2key.c new file mode 100644 index 0000000..86c97c4 --- /dev/null +++ b/SSZipArchive/minizip/aes/pwd2key.c @@ -0,0 +1,181 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + +This is an implementation of RFC2898, which specifies key derivation from +a password and a salt value. +*/ + +#include +#include "hmac.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + +void derive_key(const unsigned char pwd[], /* the PASSWORD */ + unsigned int pwd_len, /* and its length */ + const unsigned char salt[], /* the SALT and its */ + unsigned int salt_len, /* length */ + unsigned int iter, /* the number of iterations */ + unsigned char key[], /* space for the output key */ + unsigned int key_len)/* and its required length */ +{ + unsigned int i, j, k, n_blk, h_size; + unsigned char uu[HMAC_MAX_OUTPUT_SIZE], ux[HMAC_MAX_OUTPUT_SIZE]; + hmac_ctx c1[1], c2[1], c3[1]; + + /* set HMAC context (c1) for password */ + h_size = hmac_sha_begin(HMAC_SHA1, c1); + hmac_sha_key(pwd, pwd_len, c1); + + /* set HMAC context (c2) for password and salt */ + memcpy(c2, c1, sizeof(hmac_ctx)); + hmac_sha_data(salt, salt_len, c2); + + /* find the number of SHA blocks in the key */ + n_blk = 1 + (key_len - 1) / h_size; + + for(i = 0; i < n_blk; ++i) /* for each block in key */ + { + /* ux[] holds the running xor value */ + memset(ux, 0, h_size); + + /* set HMAC context (c3) for password and salt */ + memcpy(c3, c2, sizeof(hmac_ctx)); + + /* enter additional data for 1st block into uu */ + uu[0] = (unsigned char)((i + 1) >> 24); + uu[1] = (unsigned char)((i + 1) >> 16); + uu[2] = (unsigned char)((i + 1) >> 8); + uu[3] = (unsigned char)(i + 1); + + /* this is the key mixing iteration */ + for(j = 0, k = 4; j < iter; ++j) + { + /* add previous round data to HMAC */ + hmac_sha_data(uu, k, c3); + + /* obtain HMAC for uu[] */ + hmac_sha_end(uu, h_size, c3); + + /* xor into the running xor block */ + for(k = 0; k < h_size; ++k) + ux[k] ^= uu[k]; + + /* set HMAC context (c3) for password */ + memcpy(c3, c1, sizeof(hmac_ctx)); + } + + /* compile key blocks into the key output */ + j = 0; k = i * h_size; + while(j < h_size && k < key_len) + key[k++] = ux[j++]; + } +} + +#ifdef TEST + +#include + +struct +{ unsigned int pwd_len; + unsigned int salt_len; + unsigned int it_count; + unsigned char *pwd; + unsigned char salt[32]; + unsigned char key[32]; +} tests[] = +{ + { 8, 4, 5, (unsigned char*)"password", + { + 0x12, 0x34, 0x56, 0x78 + }, + { + 0x5c, 0x75, 0xce, 0xf0, 0x1a, 0x96, 0x0d, 0xf7, + 0x4c, 0xb6, 0xb4, 0x9b, 0x9e, 0x38, 0xe6, 0xb5 + } + }, + { 8, 8, 5, (unsigned char*)"password", + { + 0x12, 0x34, 0x56, 0x78, 0x78, 0x56, 0x34, 0x12 + }, + { + 0xd1, 0xda, 0xa7, 0x86, 0x15, 0xf2, 0x87, 0xe6, + 0xa1, 0xc8, 0xb1, 0x20, 0xd7, 0x06, 0x2a, 0x49 + } + }, + { 8, 21, 1, (unsigned char*)"password", + { + "ATHENA.MIT.EDUraeburn" + }, + { + 0xcd, 0xed, 0xb5, 0x28, 0x1b, 0xb2, 0xf8, 0x01, + 0x56, 0x5a, 0x11, 0x22, 0xb2, 0x56, 0x35, 0x15 + } + }, + { 8, 21, 2, (unsigned char*)"password", + { + "ATHENA.MIT.EDUraeburn" + }, + { + 0x01, 0xdb, 0xee, 0x7f, 0x4a, 0x9e, 0x24, 0x3e, + 0x98, 0x8b, 0x62, 0xc7, 0x3c, 0xda, 0x93, 0x5d + } + }, + { 8, 21, 1200, (unsigned char*)"password", + { + "ATHENA.MIT.EDUraeburn" + }, + { + 0x5c, 0x08, 0xeb, 0x61, 0xfd, 0xf7, 0x1e, 0x4e, + 0x4e, 0xc3, 0xcf, 0x6b, 0xa1, 0xf5, 0x51, 0x2b + } + } +}; + +int main() +{ unsigned int i, j, key_len = 256; + unsigned char key[256]; + + printf("\nTest of RFC2898 Password Based Key Derivation"); + for(i = 0; i < 5; ++i) + { + derive_key(tests[i].pwd, tests[i].pwd_len, tests[i].salt, + tests[i].salt_len, tests[i].it_count, key, key_len); + + printf("\ntest %i: ", i + 1); + printf("key %s", memcmp(tests[i].key, key, 16) ? "is bad" : "is good"); + for(j = 0; j < key_len && j < 64; j += 4) + { + if(j % 16 == 0) + printf("\n"); + printf("0x%02x%02x%02x%02x ", key[j], key[j + 1], key[j + 2], key[j + 3]); + } + printf(j < key_len ? " ... \n" : "\n"); + } + printf("\n"); + return 0; +} + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/SSZipArchive/minizip/aes/pwd2key.h b/SSZipArchive/minizip/aes/pwd2key.h new file mode 100644 index 0000000..b9ba421 --- /dev/null +++ b/SSZipArchive/minizip/aes/pwd2key.h @@ -0,0 +1,45 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + +This is an implementation of RFC2898, which specifies key derivation from +a password and a salt value. +*/ + +#ifndef PWD2KEY_H +#define PWD2KEY_H + +#if defined(__cplusplus) +extern "C" +{ +#endif + +void derive_key( + const unsigned char pwd[], /* the PASSWORD, and */ + unsigned int pwd_len, /* its length */ + const unsigned char salt[], /* the SALT and its */ + unsigned int salt_len, /* length */ + unsigned int iter, /* the number of iterations */ + unsigned char key[], /* space for the output key */ + unsigned int key_len); /* and its required length */ + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/SSZipArchive/minizip/aes/sha1.c b/SSZipArchive/minizip/aes/sha1.c new file mode 100644 index 0000000..3300363 --- /dev/null +++ b/SSZipArchive/minizip/aes/sha1.c @@ -0,0 +1,283 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#include /* for memcpy() etc. */ + +#include "sha1.h" +#include "brg_endian.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#if defined( _MSC_VER ) && ( _MSC_VER > 800 ) +#pragma intrinsic(memcpy) +#pragma intrinsic(memset) +#endif + +#if 0 && defined(_MSC_VER) +#define rotl32 _lrotl +#define rotr32 _lrotr +#else +#define rotl32(x,n) (((x) << n) | ((x) >> (32 - n))) +#define rotr32(x,n) (((x) >> n) | ((x) << (32 - n))) +#endif + +#if !defined(bswap_32) +#define bswap_32(x) ((rotr32((x), 24) & 0x00ff00ff) | (rotr32((x), 8) & 0xff00ff00)) +#endif + +#if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) +#define SWAP_BYTES +#else +#undef SWAP_BYTES +#endif + +#if defined(SWAP_BYTES) +#define bsw_32(p,n) \ + { int _i = (n); while(_i--) ((uint32_t*)p)[_i] = bswap_32(((uint32_t*)p)[_i]); } +#else +#define bsw_32(p,n) +#endif + +#define SHA1_MASK (SHA1_BLOCK_SIZE - 1) + +#if 0 + +#define ch(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define parity(x,y,z) ((x) ^ (y) ^ (z)) +#define maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +#else /* Discovered by Rich Schroeppel and Colin Plumb */ + +#define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define parity(x,y,z) ((x) ^ (y) ^ (z)) +#define maj(x,y,z) (((x) & (y)) | ((z) & ((x) ^ (y)))) + +#endif + +/* Compile 64 bytes of hash data into SHA1 context. Note */ +/* that this routine assumes that the byte order in the */ +/* ctx->wbuf[] at this point is in such an order that low */ +/* address bytes in the ORIGINAL byte stream will go in */ +/* this buffer to the high end of 32-bit words on BOTH big */ +/* and little endian systems */ + +#ifdef ARRAY +#define q(v,n) v[n] +#else +#define q(v,n) v##n +#endif + +#ifdef SHA_1 + +#define one_cycle(v,a,b,c,d,e,f,k,h) \ + q(v,e) += rotr32(q(v,a),27) + \ + f(q(v,b),q(v,c),q(v,d)) + k + h; \ + q(v,b) = rotr32(q(v,b), 2) + +#define five_cycle(v,f,k,i) \ + one_cycle(v, 0,1,2,3,4, f,k,hf(i )); \ + one_cycle(v, 4,0,1,2,3, f,k,hf(i+1)); \ + one_cycle(v, 3,4,0,1,2, f,k,hf(i+2)); \ + one_cycle(v, 2,3,4,0,1, f,k,hf(i+3)); \ + one_cycle(v, 1,2,3,4,0, f,k,hf(i+4)) + +VOID_RETURN sha1_compile(sha1_ctx ctx[1]) +{ uint32_t *w = ctx->wbuf; + +#ifdef ARRAY + uint32_t v[5]; + memcpy(v, ctx->hash, sizeof(ctx->hash)); +#else + uint32_t v0, v1, v2, v3, v4; + v0 = ctx->hash[0]; v1 = ctx->hash[1]; + v2 = ctx->hash[2]; v3 = ctx->hash[3]; + v4 = ctx->hash[4]; +#endif + +#define hf(i) w[i] + + five_cycle(v, ch, 0x5a827999, 0); + five_cycle(v, ch, 0x5a827999, 5); + five_cycle(v, ch, 0x5a827999, 10); + one_cycle(v,0,1,2,3,4, ch, 0x5a827999, hf(15)); \ + +#undef hf +#define hf(i) (w[(i) & 15] = rotl32( \ + w[((i) + 13) & 15] ^ w[((i) + 8) & 15] \ + ^ w[((i) + 2) & 15] ^ w[(i) & 15], 1)) + + one_cycle(v,4,0,1,2,3, ch, 0x5a827999, hf(16)); + one_cycle(v,3,4,0,1,2, ch, 0x5a827999, hf(17)); + one_cycle(v,2,3,4,0,1, ch, 0x5a827999, hf(18)); + one_cycle(v,1,2,3,4,0, ch, 0x5a827999, hf(19)); + + five_cycle(v, parity, 0x6ed9eba1, 20); + five_cycle(v, parity, 0x6ed9eba1, 25); + five_cycle(v, parity, 0x6ed9eba1, 30); + five_cycle(v, parity, 0x6ed9eba1, 35); + + five_cycle(v, maj, 0x8f1bbcdc, 40); + five_cycle(v, maj, 0x8f1bbcdc, 45); + five_cycle(v, maj, 0x8f1bbcdc, 50); + five_cycle(v, maj, 0x8f1bbcdc, 55); + + five_cycle(v, parity, 0xca62c1d6, 60); + five_cycle(v, parity, 0xca62c1d6, 65); + five_cycle(v, parity, 0xca62c1d6, 70); + five_cycle(v, parity, 0xca62c1d6, 75); + +#ifdef ARRAY + ctx->hash[0] += v[0]; ctx->hash[1] += v[1]; + ctx->hash[2] += v[2]; ctx->hash[3] += v[3]; + ctx->hash[4] += v[4]; +#else + ctx->hash[0] += v0; ctx->hash[1] += v1; + ctx->hash[2] += v2; ctx->hash[3] += v3; + ctx->hash[4] += v4; +#endif +} + +VOID_RETURN sha1_begin(sha1_ctx ctx[1]) +{ + memset(ctx, 0, sizeof(sha1_ctx)); + ctx->hash[0] = 0x67452301; + ctx->hash[1] = 0xefcdab89; + ctx->hash[2] = 0x98badcfe; + ctx->hash[3] = 0x10325476; + ctx->hash[4] = 0xc3d2e1f0; +} + +/* SHA1 hash data in an array of bytes into hash buffer and */ +/* call the hash_compile function as required. For both the */ +/* bit and byte orientated versions, the block length 'len' */ +/* must not be greater than 2^32 - 1 bits (2^29 - 1 bytes) */ + +VOID_RETURN sha1_hash(const unsigned char data[], unsigned long len, sha1_ctx ctx[1]) +{ uint32_t pos = (uint32_t)((ctx->count[0] >> 3) & SHA1_MASK); + const unsigned char *sp = data; + unsigned char *w = (unsigned char*)ctx->wbuf; +#if SHA1_BITS == 1 + uint32_t ofs = (ctx->count[0] & 7); +#else + len <<= 3; +#endif + if((ctx->count[0] += len) < len) + ++(ctx->count[1]); +#if SHA1_BITS == 1 + if(ofs) /* if not on a byte boundary */ + { + if(ofs + len < 8) /* if no added bytes are needed */ + { + w[pos] |= (*sp >> ofs); + } + else /* otherwise and add bytes */ + { unsigned char part = w[pos]; + + while((int)(ofs + (len -= 8)) >= 0) + { + w[pos++] = part | (*sp >> ofs); + part = *sp++ << (8 - ofs); + if(pos == SHA1_BLOCK_SIZE) + { + bsw_32(w, SHA1_BLOCK_SIZE >> 2); + sha1_compile(ctx); pos = 0; + } + } + + w[pos] = part; + } + } + else /* data is byte aligned */ +#endif + { uint32_t space = SHA1_BLOCK_SIZE - pos; + + while(len >= (space << 3)) + { + memcpy(w + pos, sp, space); + bsw_32(w, SHA1_BLOCK_SIZE >> 2); + sha1_compile(ctx); + sp += space; len -= (space << 3); + space = SHA1_BLOCK_SIZE; pos = 0; + } + memcpy(w + pos, sp, (len + 7 * SHA1_BITS) >> 3); + } +} + +/* SHA1 final padding and digest calculation */ + +VOID_RETURN sha1_end(unsigned char hval[], sha1_ctx ctx[1]) +{ uint32_t i = (uint32_t)((ctx->count[0] >> 3) & SHA1_MASK), m1; + + /* put bytes in the buffer in an order in which references to */ + /* 32-bit words will put bytes with lower addresses into the */ + /* top of 32 bit words on BOTH big and little endian machines */ + bsw_32(ctx->wbuf, (i + 3 + SHA1_BITS) >> 2); + + /* we now need to mask valid bytes and add the padding which is */ + /* a single 1 bit and as many zero bits as necessary. Note that */ + /* we can always add the first padding byte here because the */ + /* buffer always has at least one empty slot */ + m1 = (unsigned char)0x80 >> (ctx->count[0] & 7); + ctx->wbuf[i >> 2] &= ((0xffffff00 | (~m1 + 1)) << 8 * (~i & 3)); + ctx->wbuf[i >> 2] |= (m1 << 8 * (~i & 3)); + + /* we need 9 or more empty positions, one for the padding byte */ + /* (above) and eight for the length count. If there is not */ + /* enough space, pad and empty the buffer */ + if(i > SHA1_BLOCK_SIZE - 9) + { + if(i < 60) ctx->wbuf[15] = 0; + sha1_compile(ctx); + i = 0; + } + else /* compute a word index for the empty buffer positions */ + i = (i >> 2) + 1; + + while(i < 14) /* and zero pad all but last two positions */ + ctx->wbuf[i++] = 0; + + /* the following 32-bit length fields are assembled in the */ + /* wrong byte order on little endian machines but this is */ + /* corrected later since they are only ever used as 32-bit */ + /* word values. */ + ctx->wbuf[14] = ctx->count[1]; + ctx->wbuf[15] = ctx->count[0]; + sha1_compile(ctx); + + /* extract the hash value as bytes in case the hash buffer is */ + /* misaligned for 32-bit words */ + for(i = 0; i < SHA1_DIGEST_SIZE; ++i) + hval[i] = ((ctx->hash[i >> 2] >> (8 * (~i & 3))) & 0xff); +} + +VOID_RETURN sha1(unsigned char hval[], const unsigned char data[], unsigned long len) +{ sha1_ctx cx[1]; + + sha1_begin(cx); sha1_hash(data, len, cx); sha1_end(hval, cx); +} + +#endif + +#if defined(__cplusplus) +} +#endif diff --git a/SSZipArchive/minizip/aes/sha1.h b/SSZipArchive/minizip/aes/sha1.h new file mode 100644 index 0000000..e805ad9 --- /dev/null +++ b/SSZipArchive/minizip/aes/sha1.h @@ -0,0 +1,72 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#ifndef _SHA1_H +#define _SHA1_H + +#define SHA_1 + +/* define for bit or byte oriented SHA */ +#if 1 +# define SHA1_BITS 0 /* byte oriented */ +#else +# define SHA1_BITS 1 /* bit oriented */ +#endif + +#include +#include "brg_types.h" + +#define SHA1_BLOCK_SIZE 64 +#define SHA1_DIGEST_SIZE 20 + +#if defined(__cplusplus) +extern "C" +{ +#endif + +/* type to hold the SHA256 context */ + +typedef struct +{ uint32_t count[2]; + uint32_t hash[SHA1_DIGEST_SIZE >> 2]; + uint32_t wbuf[SHA1_BLOCK_SIZE >> 2]; +} sha1_ctx; + +/* Note that these prototypes are the same for both bit and */ +/* byte oriented implementations. However the length fields */ +/* are in bytes or bits as appropriate for the version used */ +/* and bit sequences are input as arrays of bytes in which */ +/* bit sequences run from the most to the least significant */ +/* end of each byte. The value 'len' in sha1_hash for the */ +/* byte oriented version of SHA1 is limited to 2^29 bytes, */ +/* but multiple calls will handle longer data blocks. */ + +VOID_RETURN sha1_compile(sha1_ctx ctx[1]); + +VOID_RETURN sha1_begin(sha1_ctx ctx[1]); +VOID_RETURN sha1_hash(const unsigned char data[], unsigned long len, sha1_ctx ctx[1]); +VOID_RETURN sha1_end(unsigned char hval[], sha1_ctx ctx[1]); +VOID_RETURN sha1(unsigned char hval[], const unsigned char data[], unsigned long len); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/SSZipArchive/minizip/crypt.c b/SSZipArchive/minizip/crypt.c new file mode 100644 index 0000000..9c1203b --- /dev/null +++ b/SSZipArchive/minizip/crypt.c @@ -0,0 +1,144 @@ +/* crypt.c -- base code for traditional PKWARE encryption + Version 1.2.0, September 16th, 2017 + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + Copyright (C) 1998-2005 Gilles Vollant + Modifications for Info-ZIP crypting + http://www.winimage.com/zLibDll/minizip.html + Copyright (C) 2003 Terry Thorsen + + This code is a modified version of crypting code in Info-ZIP distribution + + Copyright (C) 1990-2000 Info-ZIP. All rights reserved. + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. +*/ + +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include +#else +# include +# include +# include +#endif + +#include "zlib.h" + +#include "crypt.h" + +/***************************************************************************/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((uint32_t)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/***************************************************************************/ + +uint8_t decrypt_byte(uint32_t *pkeys) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((uint32_t)(*(pkeys+2)) & 0xffff) | 2; + return (uint8_t)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +uint8_t update_keys(uint32_t *pkeys, const z_crc_t *pcrc_32_tab, int32_t c) +{ + (*(pkeys+0)) = (uint32_t)CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int32_t keyshift = (int32_t)((*(pkeys + 1)) >> 24); + (*(pkeys+2)) = (uint32_t)CRC32((*(pkeys+2)), keyshift); + } + return c; +} + +void init_keys(const char *passwd, uint32_t *pkeys, const z_crc_t *pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != 0) + { + update_keys(pkeys, pcrc_32_tab, *passwd); + passwd += 1; + } +} + +/***************************************************************************/ + +int cryptrand(unsigned char *buf, unsigned int len) +{ +#ifdef _WIN32 + HCRYPTPROV provider; + unsigned __int64 pentium_tsc[1]; + int rlen = 0; + int result = 0; + + + if (CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + result = CryptGenRandom(provider, len, buf); + CryptReleaseContext(provider, 0); + if (result) + return len; + } + + for (rlen = 0; rlen < (int)len; ++rlen) + { + if (rlen % 8 == 0) + QueryPerformanceCounter((LARGE_INTEGER *)pentium_tsc); + buf[rlen] = ((unsigned char*)pentium_tsc)[rlen % 8]; + } + + return rlen; +#else + arc4random_buf(buf, len); + return len; +#endif +} + +int crypthead(const char *passwd, uint8_t *buf, int buf_size, uint32_t *pkeys, + const z_crc_t *pcrc_32_tab, uint8_t verify1, uint8_t verify2) +{ + uint8_t n = 0; /* index in random header */ + uint8_t header[RAND_HEAD_LEN-2]; /* random header */ + uint16_t t = 0; /* temporary */ + + if (buf_size < RAND_HEAD_LEN) + return 0; + + init_keys(passwd, pkeys, pcrc_32_tab); + + /* First generate RAND_HEAD_LEN-2 random bytes. */ + cryptrand(header, RAND_HEAD_LEN-2); + + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + + for (n = 0; n < RAND_HEAD_LEN-2; n++) + buf[n] = (uint8_t)zencode(pkeys, pcrc_32_tab, header[n], t); + + buf[n++] = (uint8_t)zencode(pkeys, pcrc_32_tab, verify1, t); + buf[n++] = (uint8_t)zencode(pkeys, pcrc_32_tab, verify2, t); + return n; +} + +/***************************************************************************/ diff --git a/SSZipArchive/minizip/crypt.h b/SSZipArchive/minizip/crypt.h new file mode 100644 index 0000000..0ba8260 --- /dev/null +++ b/SSZipArchive/minizip/crypt.h @@ -0,0 +1,64 @@ +/* crypt.h -- base code for traditional PKWARE encryption + Version 1.2.0, September 16th, 2017 + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + Copyright (C) 1998-2005 Gilles Vollant + Modifications for Info-ZIP crypting + http://www.winimage.com/zLibDll/minizip.html + Copyright (C) 2003 Terry Thorsen + + This code is a modified version of crypting code in Info-ZIP distribution + + Copyright (C) 1990-2000 Info-ZIP. All rights reserved. + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#ifndef _MINICRYPT_H +#define _MINICRYPT_H + +#if ZLIB_VERNUM < 0x1270 +typedef unsigned long z_crc_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define RAND_HEAD_LEN 12 + +/***************************************************************************/ + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab, c ^= decrypt_byte(pkeys))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t = decrypt_byte(pkeys), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +/***************************************************************************/ + +/* Return the next byte in the pseudo-random sequence */ +uint8_t decrypt_byte(uint32_t *pkeys); + +/* Update the encryption keys with the next byte of plain text */ +uint8_t update_keys(uint32_t *pkeys, const z_crc_t *pcrc_32_tab, int32_t c); + +/* Initialize the encryption keys and the random header according to the given password. */ +void init_keys(const char *passwd, uint32_t *pkeys, const z_crc_t *pcrc_32_tab); + +/* Generate cryptographically secure random numbers */ +int cryptrand(unsigned char *buf, unsigned int len); + +/* Create encryption header */ +int crypthead(const char *passwd, uint8_t *buf, int buf_size, uint32_t *pkeys, + const z_crc_t *pcrc_32_tab, uint8_t verify1, uint8_t verify2); + +/***************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/SSZipArchive/minizip/ioapi.c b/SSZipArchive/minizip/ioapi.c new file mode 100644 index 0000000..239bb05 --- /dev/null +++ b/SSZipArchive/minizip/ioapi.c @@ -0,0 +1,353 @@ +/* ioapi.c -- IO base function header for compress/uncompress .zip + part of the MiniZip project + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson + http://result42.com + Copyright (C) 1998-2010 Gilles Vollant + http://www.winimage.com/zLibDll/minizip.html + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#include +#include + +#if defined unix || defined __APPLE__ +#include +#include +#endif + +#include "ioapi.h" + +#if defined(_WIN32) +# define snprintf _snprintf +#endif + +voidpf call_zopen64(const zlib_filefunc64_32_def *pfilefunc, const void *filename, int mode) +{ + if (pfilefunc->zfile_func64.zopen64_file != NULL) + return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque, filename, mode); + return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque, (const char*)filename, mode); +} + +voidpf call_zopendisk64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream, uint32_t number_disk, int mode) +{ + if (pfilefunc->zfile_func64.zopendisk64_file != NULL) + return (*(pfilefunc->zfile_func64.zopendisk64_file)) (pfilefunc->zfile_func64.opaque, filestream, number_disk, mode); + return (*(pfilefunc->zopendisk32_file))(pfilefunc->zfile_func64.opaque, filestream, number_disk, mode); +} + +long call_zseek64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream, uint64_t offset, int origin) +{ + uint32_t offset_truncated = 0; + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); + offset_truncated = (uint32_t)offset; + if (offset_truncated != offset) + return -1; + return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream, offset_truncated, origin); +} + +uint64_t call_ztell64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream) +{ + uint64_t position; + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque, filestream); + position = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque, filestream); + if ((position) == UINT32_MAX) + return (uint64_t)-1; + return position; +} + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def *p_filefunc64_32, const zlib_filefunc_def *p_filefunc32) +{ + p_filefunc64_32->zfile_func64.zopen64_file = NULL; + p_filefunc64_32->zfile_func64.zopendisk64_file = NULL; + p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; + p_filefunc64_32->zopendisk32_file = p_filefunc32->zopendisk_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; + p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; + p_filefunc64_32->zfile_func64.ztell64_file = NULL; + p_filefunc64_32->zfile_func64.zseek64_file = NULL; + p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; + p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; + p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; +} + +static voidpf ZCALLBACK fopen_file_func(ZIP_UNUSED voidpf opaque, const char *filename, int mode); +static uint32_t ZCALLBACK fread_file_func(voidpf opaque, voidpf stream, void* buf, uint32_t size); +static uint32_t ZCALLBACK fwrite_file_func(voidpf opaque, voidpf stream, const void *buf, uint32_t size); +static uint64_t ZCALLBACK ftell64_file_func(voidpf opaque, voidpf stream); +static long ZCALLBACK fseek64_file_func(voidpf opaque, voidpf stream, uint64_t offset, int origin); +static int ZCALLBACK fclose_file_func(voidpf opaque, voidpf stream); +static int ZCALLBACK ferror_file_func(voidpf opaque, voidpf stream); + +typedef struct +{ + FILE *file; + int filenameLength; + void *filename; +} FILE_IOPOSIX; + +static voidpf file_build_ioposix(FILE *file, const char *filename) +{ + FILE_IOPOSIX *ioposix = NULL; + if (file == NULL) + return NULL; + ioposix = (FILE_IOPOSIX*)malloc(sizeof(FILE_IOPOSIX)); + ioposix->file = file; + ioposix->filenameLength = (int)strlen(filename) + 1; + ioposix->filename = (char*)malloc(ioposix->filenameLength * sizeof(char)); + strncpy((char*)ioposix->filename, filename, ioposix->filenameLength); + return (voidpf)ioposix; +} + +static voidpf ZCALLBACK fopen_file_func(ZIP_UNUSED voidpf opaque, const char *filename, int mode) +{ + FILE* file = NULL; + const char *mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename != NULL) && (mode_fopen != NULL)) + { + file = fopen(filename, mode_fopen); + return file_build_ioposix(file, filename); + } + return file; +} + +static voidpf ZCALLBACK fopen64_file_func(ZIP_UNUSED voidpf opaque, const void *filename, int mode) +{ + FILE* file = NULL; + const char *mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename != NULL) && (mode_fopen != NULL)) + { + file = fopen64((const char*)filename, mode_fopen); + return file_build_ioposix(file, (const char*)filename); + } + return file; +} + +static voidpf ZCALLBACK fopendisk64_file_func(voidpf opaque, voidpf stream, uint32_t number_disk, int mode) +{ + FILE_IOPOSIX *ioposix = NULL; + char *diskFilename = NULL; + voidpf ret = NULL; + int i = 0; + + if (stream == NULL) + return NULL; + ioposix = (FILE_IOPOSIX*)stream; + diskFilename = (char*)malloc(ioposix->filenameLength * sizeof(char)); + strncpy(diskFilename, (const char*)ioposix->filename, ioposix->filenameLength); + for (i = ioposix->filenameLength - 1; i >= 0; i -= 1) + { + if (diskFilename[i] != '.') + continue; + snprintf(&diskFilename[i], ioposix->filenameLength - i, ".z%02u", number_disk + 1); + break; + } + if (i >= 0) + ret = fopen64_file_func(opaque, diskFilename, mode); + free(diskFilename); + return ret; +} + +static voidpf ZCALLBACK fopendisk_file_func(voidpf opaque, voidpf stream, uint32_t number_disk, int mode) +{ + FILE_IOPOSIX *ioposix = NULL; + char *diskFilename = NULL; + voidpf ret = NULL; + int i = 0; + + if (stream == NULL) + return NULL; + ioposix = (FILE_IOPOSIX*)stream; + diskFilename = (char*)malloc(ioposix->filenameLength * sizeof(char)); + strncpy(diskFilename, (const char*)ioposix->filename, ioposix->filenameLength); + for (i = ioposix->filenameLength - 1; i >= 0; i -= 1) + { + if (diskFilename[i] != '.') + continue; + snprintf(&diskFilename[i], ioposix->filenameLength - i, ".z%02u", number_disk + 1); + break; + } + if (i >= 0) + ret = fopen_file_func(opaque, diskFilename, mode); + free(diskFilename); + return ret; +} + +static uint32_t ZCALLBACK fread_file_func(ZIP_UNUSED voidpf opaque, voidpf stream, void* buf, uint32_t size) +{ + FILE_IOPOSIX *ioposix = NULL; + uint32_t read = (uint32_t)-1; + if (stream == NULL) + return read; + ioposix = (FILE_IOPOSIX*)stream; + read = (uint32_t)fread(buf, 1, (size_t)size, ioposix->file); + return read; +} + +static uint32_t ZCALLBACK fwrite_file_func(ZIP_UNUSED voidpf opaque, voidpf stream, const void *buf, uint32_t size) +{ + FILE_IOPOSIX *ioposix = NULL; + uint32_t written = (uint32_t)-1; + if (stream == NULL) + return written; + ioposix = (FILE_IOPOSIX*)stream; + written = (uint32_t)fwrite(buf, 1, (size_t)size, ioposix->file); + return written; +} + +static long ZCALLBACK ftell_file_func(ZIP_UNUSED voidpf opaque, voidpf stream) +{ + FILE_IOPOSIX *ioposix = NULL; + long ret = -1; + if (stream == NULL) + return ret; + ioposix = (FILE_IOPOSIX*)stream; + ret = ftell(ioposix->file); + return ret; +} + +static uint64_t ZCALLBACK ftell64_file_func(ZIP_UNUSED voidpf opaque, voidpf stream) +{ + FILE_IOPOSIX *ioposix = NULL; + uint64_t ret = (uint64_t)-1; + if (stream == NULL) + return ret; + ioposix = (FILE_IOPOSIX*)stream; + ret = ftello64(ioposix->file); + return ret; +} + +static long ZCALLBACK fseek_file_func(ZIP_UNUSED voidpf opaque, voidpf stream, uint32_t offset, int origin) +{ + FILE_IOPOSIX *ioposix = NULL; + int fseek_origin = 0; + long ret = 0; + + if (stream == NULL) + return -1; + ioposix = (FILE_IOPOSIX*)stream; + + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR: + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END: + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET: + fseek_origin = SEEK_SET; + break; + default: + return -1; + } + if (fseek(ioposix->file, offset, fseek_origin) != 0) + ret = -1; + return ret; +} + +static long ZCALLBACK fseek64_file_func(ZIP_UNUSED voidpf opaque, voidpf stream, uint64_t offset, int origin) +{ + FILE_IOPOSIX *ioposix = NULL; + int fseek_origin = 0; + long ret = 0; + + if (stream == NULL) + return -1; + ioposix = (FILE_IOPOSIX*)stream; + + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR: + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END: + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET: + fseek_origin = SEEK_SET; + break; + default: + return -1; + } + + if (fseeko64(ioposix->file, offset, fseek_origin) != 0) + ret = -1; + + return ret; +} + +static int ZCALLBACK fclose_file_func(ZIP_UNUSED voidpf opaque, voidpf stream) +{ + FILE_IOPOSIX *ioposix = NULL; + int ret = -1; + if (stream == NULL) + return ret; + ioposix = (FILE_IOPOSIX*)stream; + if (ioposix->filename != NULL) + free(ioposix->filename); + ret = fclose(ioposix->file); + free(ioposix); + return ret; +} + +static int ZCALLBACK ferror_file_func(ZIP_UNUSED voidpf opaque, voidpf stream) +{ + FILE_IOPOSIX *ioposix = NULL; + int ret = -1; + if (stream == NULL) + return ret; + ioposix = (FILE_IOPOSIX*)stream; + ret = ferror(ioposix->file); + return ret; +} + +void fill_fopen_filefunc(zlib_filefunc_def *pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zopendisk_file = fopendisk_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_fopen64_filefunc(zlib_filefunc64_def *pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = fopen64_file_func; + pzlib_filefunc_def->zopendisk64_file = fopendisk64_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell64_file = ftell64_file_func; + pzlib_filefunc_def->zseek64_file = fseek64_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/SSZipArchive/minizip/ioapi.h b/SSZipArchive/minizip/ioapi.h new file mode 100644 index 0000000..c5698ad --- /dev/null +++ b/SSZipArchive/minizip/ioapi.h @@ -0,0 +1,154 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + Copyright (C) 2009-2010 Mathias Svensson + Modifications for Zip64 support + http://result42.com + Copyright (C) 1998-2010 Gilles Vollant + http://www.winimage.com/zLibDll/minizip.html + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#ifndef _ZLIBIOAPI64_H +#define _ZLIBIOAPI64_H + +#include +#include +#include + +#include "zlib.h" + +#ifdef __GNUC__ +# define ZIP_UNUSED __attribute__((__unused__)) +#else +# define ZIP_UNUSED +#endif + +#if defined(USE_FILE32API) +# define fopen64 fopen +# define ftello64 ftell +# define fseeko64 fseek +#else +# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__ANDROID__) +# define fopen64 fopen +# define ftello64 ftello +# define fseeko64 fseeko +# endif +# ifdef _MSC_VER +# define fopen64 fopen +# if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) +# define ftello64 _ftelli64 +# define fseeko64 _fseeki64 +# else /* old MSC */ +# define ftello64 ftell +# define fseeko64 fseek +# endif +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + +#ifndef ZCALLBACK +# if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || \ + defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) +# define ZCALLBACK CALLBACK +# else +# define ZCALLBACK +# endif +#endif + +typedef voidpf (ZCALLBACK *open_file_func) (voidpf opaque, const char *filename, int mode); +typedef voidpf (ZCALLBACK *opendisk_file_func) (voidpf opaque, voidpf stream, uint32_t number_disk, int mode); +typedef uint32_t (ZCALLBACK *read_file_func) (voidpf opaque, voidpf stream, void* buf, uint32_t size); +typedef uint32_t (ZCALLBACK *write_file_func) (voidpf opaque, voidpf stream, const void *buf, uint32_t size); +typedef int (ZCALLBACK *close_file_func) (voidpf opaque, voidpf stream); +typedef int (ZCALLBACK *error_file_func) (voidpf opaque, voidpf stream); + +typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream); +typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uint32_t offset, int origin); + +/* here is the "old" 32 bits structure structure */ +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + opendisk_file_func zopendisk_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + error_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + +typedef uint64_t (ZCALLBACK *tell64_file_func) (voidpf opaque, voidpf stream); +typedef long (ZCALLBACK *seek64_file_func) (voidpf opaque, voidpf stream, uint64_t offset, int origin); +typedef voidpf (ZCALLBACK *open64_file_func) (voidpf opaque, const void *filename, int mode); +typedef voidpf (ZCALLBACK *opendisk64_file_func)(voidpf opaque, voidpf stream, uint32_t number_disk, int mode); + +typedef struct zlib_filefunc64_def_s +{ + open64_file_func zopen64_file; + opendisk64_file_func zopendisk64_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell64_file_func ztell64_file; + seek64_file_func zseek64_file; + close_file_func zclose_file; + error_file_func zerror_file; + voidpf opaque; +} zlib_filefunc64_def; + +void fill_fopen_filefunc(zlib_filefunc_def *pzlib_filefunc_def); +void fill_fopen64_filefunc(zlib_filefunc64_def *pzlib_filefunc_def); + +/* now internal definition, only for zip.c and unzip.h */ +typedef struct zlib_filefunc64_32_def_s +{ + zlib_filefunc64_def zfile_func64; + open_file_func zopen32_file; + opendisk_file_func zopendisk32_file; + tell_file_func ztell32_file; + seek_file_func zseek32_file; +} zlib_filefunc64_32_def; + +#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +/*#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream))*/ +/*#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode))*/ +#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) +#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) + +voidpf call_zopen64(const zlib_filefunc64_32_def *pfilefunc,const void*filename, int mode); +voidpf call_zopendisk64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream, uint32_t number_disk, int mode); +long call_zseek64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream, uint64_t offset, int origin); +uint64_t call_ztell64(const zlib_filefunc64_32_def *pfilefunc, voidpf filestream); + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def *p_filefunc64_32, const zlib_filefunc_def *p_filefunc32); + +#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) +#define ZOPENDISK64(filefunc,filestream,diskn,mode) (call_zopendisk64((&(filefunc)),(filestream),(diskn),(mode))) +#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) +#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/SSZipArchive/minizip/ioapi_buf.c b/SSZipArchive/minizip/ioapi_buf.c new file mode 100644 index 0000000..074a930 --- /dev/null +++ b/SSZipArchive/minizip/ioapi_buf.c @@ -0,0 +1,461 @@ +/* ioapi_buf.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + This version of ioapi is designed to buffer IO. + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + + +#include +#include +#include +#include + +#include "zlib.h" +#include "ioapi.h" + +#include "ioapi_buf.h" + +#ifndef IOBUF_BUFFERSIZE +# define IOBUF_BUFFERSIZE (UINT16_MAX) +#endif + +#if defined(_WIN32) +# include +# define PRINTF _cprintf +# define VPRINTF _vcprintf +#else +# define PRINTF printf +# define VPRINTF vprintf +#endif + +//#define IOBUF_VERBOSE + +#ifdef __GNUC__ +#ifndef max +#define max(x,y) ({ \ +const __typeof__(x) _x = (x); \ +const __typeof__(y) _y = (y); \ +(void) (&_x == &_y); \ +_x > _y ? _x : _y; }) +#endif /* __GNUC__ */ + +#ifndef min +#define min(x,y) ({ \ +const __typeof__(x) _x = (x); \ +const __typeof__(y) _y = (y); \ +(void) (&_x == &_y); \ +_x < _y ? _x : _y; }) +#endif +#endif + +typedef struct ourstream_s { + char readbuf[IOBUF_BUFFERSIZE]; + uint32_t readbuf_len; + uint32_t readbuf_pos; + uint32_t readbuf_hits; + uint32_t readbuf_misses; + char writebuf[IOBUF_BUFFERSIZE]; + uint32_t writebuf_len; + uint32_t writebuf_pos; + uint32_t writebuf_hits; + uint32_t writebuf_misses; + uint64_t position; + voidpf stream; +} ourstream_t; + +#if defined(IOBUF_VERBOSE) +# define print_buf(o,s,f,...) print_buf_internal(o,s,f,__VA_ARGS__); +#else +# define print_buf(o,s,f,...) +#endif + +void print_buf_internal(ZIP_UNUSED voidpf opaque, voidpf stream, char *format, ...) +{ + ourstream_t *streamio = (ourstream_t *)stream; + va_list arglist; + PRINTF("Buf stream %p - ", streamio); + va_start(arglist, format); + VPRINTF(format, arglist); + va_end(arglist); +} + +voidpf fopen_buf_internal_func(ZIP_UNUSED voidpf opaque, voidpf stream, ZIP_UNUSED uint32_t number_disk, ZIP_UNUSED int mode) +{ + ourstream_t *streamio = NULL; + if (stream == NULL) + return NULL; + streamio = (ourstream_t *)malloc(sizeof(ourstream_t)); + if (streamio == NULL) + return NULL; + memset(streamio, 0, sizeof(ourstream_t)); + streamio->stream = stream; + print_buf(opaque, streamio, "open [num %d mode %d]\n", number_disk, mode); + return streamio; +} + +voidpf ZCALLBACK fopen_buf_func(voidpf opaque, const char *filename, int mode) +{ + ourbuffer_t *bufio = (ourbuffer_t *)opaque; + voidpf stream = bufio->filefunc.zopen_file(bufio->filefunc.opaque, filename, mode); + return fopen_buf_internal_func(opaque, stream, 0, mode); +} + +voidpf ZCALLBACK fopen64_buf_func(voidpf opaque, const void *filename, int mode) +{ + ourbuffer_t *bufio = (ourbuffer_t *)opaque; + voidpf stream = bufio->filefunc64.zopen64_file(bufio->filefunc64.opaque, filename, mode); + return fopen_buf_internal_func(opaque, stream, 0, mode); +} + +voidpf ZCALLBACK fopendisk_buf_func(voidpf opaque, voidpf stream_cd, uint32_t number_disk, int mode) +{ + ourbuffer_t *bufio = (ourbuffer_t *)opaque; + ourstream_t *streamio = (ourstream_t *)stream_cd; + voidpf *stream = bufio->filefunc.zopendisk_file(bufio->filefunc.opaque, streamio->stream, number_disk, mode); + return fopen_buf_internal_func(opaque, stream, number_disk, mode); +} + +voidpf ZCALLBACK fopendisk64_buf_func(voidpf opaque, voidpf stream_cd, uint32_t number_disk, int mode) +{ + ourbuffer_t *bufio = (ourbuffer_t *)opaque; + ourstream_t *streamio = (ourstream_t *)stream_cd; + voidpf stream = bufio->filefunc64.zopendisk64_file(bufio->filefunc64.opaque, streamio->stream, number_disk, mode); + return fopen_buf_internal_func(opaque, stream, number_disk, mode); +} + +long fflush_buf(voidpf opaque, voidpf stream) +{ + ourbuffer_t *bufio = (ourbuffer_t *)opaque; + ourstream_t *streamio = (ourstream_t *)stream; + uint32_t total_bytes_to_write = 0; + uint32_t bytes_to_write = streamio->writebuf_len; + uint32_t bytes_left_to_write = streamio->writebuf_len; + long bytes_written = 0; + + while (bytes_left_to_write > 0) + { + if (bufio->filefunc64.zwrite_file != NULL) + bytes_written = bufio->filefunc64.zwrite_file(bufio->filefunc64.opaque, streamio->stream, streamio->writebuf + (bytes_to_write - bytes_left_to_write), bytes_left_to_write); + else + bytes_written = bufio->filefunc.zwrite_file(bufio->filefunc.opaque, streamio->stream, streamio->writebuf + (bytes_to_write - bytes_left_to_write), bytes_left_to_write); + + streamio->writebuf_misses += 1; + + print_buf(opaque, stream, "write flush [%d:%d len %d]\n", bytes_to_write, bytes_left_to_write, streamio->writebuf_len); + + if (bytes_written < 0) + return bytes_written; + + total_bytes_to_write += bytes_written; + bytes_left_to_write -= bytes_written; + streamio->position += bytes_written; + } + streamio->writebuf_len = 0; + streamio->writebuf_pos = 0; + return total_bytes_to_write; +} + +uint32_t ZCALLBACK fread_buf_func(voidpf opaque, voidpf stream, void *buf, uint32_t size) +{ + ourbuffer_t *bufio = (ourbuffer_t *)opaque; + ourstream_t *streamio = (ourstream_t *)stream; + uint32_t buf_len = 0; + uint32_t bytes_to_read = 0; + uint32_t bytes_to_copy = 0; + uint32_t bytes_left_to_read = size; + uint32_t bytes_read = 0; + + print_buf(opaque, stream, "read [size %ld pos %lld]\n", size, streamio->position); + + if (streamio->writebuf_len > 0) + { + print_buf(opaque, stream, "switch from write to read, not yet supported [%lld]\n", streamio->position); + } + + while (bytes_left_to_read > 0) + { + if ((streamio->readbuf_len == 0) || (streamio->readbuf_pos == streamio->readbuf_len)) + { + if (streamio->readbuf_len == IOBUF_BUFFERSIZE) + { + streamio->readbuf_pos = 0; + streamio->readbuf_len = 0; + } + + bytes_to_read = IOBUF_BUFFERSIZE - (streamio->readbuf_len - streamio->readbuf_pos); + + if (bufio->filefunc64.zread_file != NULL) + bytes_read = bufio->filefunc64.zread_file(bufio->filefunc64.opaque, streamio->stream, streamio->readbuf + streamio->readbuf_pos, bytes_to_read); + else + bytes_read = bufio->filefunc.zread_file(bufio->filefunc.opaque, streamio->stream, streamio->readbuf + streamio->readbuf_pos, bytes_to_read); + + streamio->readbuf_misses += 1; + streamio->readbuf_len += bytes_read; + streamio->position += bytes_read; + + print_buf(opaque, stream, "filled [read %d/%d buf %d:%d pos %lld]\n", bytes_read, bytes_to_read, streamio->readbuf_pos, streamio->readbuf_len, streamio->position); + + if (bytes_read == 0) + break; + } + + if ((streamio->readbuf_len - streamio->readbuf_pos) > 0) + { + bytes_to_copy = min(bytes_left_to_read, (uint32_t)(streamio->readbuf_len - streamio->readbuf_pos)); + memcpy((char *)buf + buf_len, streamio->readbuf + streamio->readbuf_pos, bytes_to_copy); + + buf_len += bytes_to_copy; + bytes_left_to_read -= bytes_to_copy; + + streamio->readbuf_hits += 1; + streamio->readbuf_pos += bytes_to_copy; + + print_buf(opaque, stream, "emptied [copied %d remaining %d buf %d:%d pos %lld]\n", bytes_to_copy, bytes_left_to_read, streamio->readbuf_pos, streamio->readbuf_len, streamio->position); + } + } + + return size - bytes_left_to_read; +} + +uint32_t ZCALLBACK fwrite_buf_func(voidpf opaque, voidpf stream, const void *buf, uint32_t size) +{ + ourbuffer_t *bufio = (ourbuffer_t *)opaque; + ourstream_t *streamio = (ourstream_t *)stream; + uint32_t bytes_to_write = size; + uint32_t bytes_left_to_write = size; + uint32_t bytes_to_copy = 0; + int64_t ret = 0; + + print_buf(opaque, stream, "write [size %ld len %d pos %lld]\n", size, streamio->writebuf_len, streamio->position); + + if (streamio->readbuf_len > 0) + { + streamio->position -= streamio->readbuf_len; + streamio->position += streamio->readbuf_pos; + + streamio->readbuf_len = 0; + streamio->readbuf_pos = 0; + + print_buf(opaque, stream, "switch from read to write [%lld]\n", streamio->position); + + if (bufio->filefunc64.zseek64_file != NULL) + ret = bufio->filefunc64.zseek64_file(bufio->filefunc64.opaque, streamio->stream, streamio->position, ZLIB_FILEFUNC_SEEK_SET); + else + ret = bufio->filefunc.zseek_file(bufio->filefunc.opaque, streamio->stream, (uint32_t)streamio->position, ZLIB_FILEFUNC_SEEK_SET); + + if (ret != 0) + return (uint32_t)-1; + } + + while (bytes_left_to_write > 0) + { + bytes_to_copy = min(bytes_left_to_write, (uint32_t)(IOBUF_BUFFERSIZE - min(streamio->writebuf_len, streamio->writebuf_pos))); + + if (bytes_to_copy == 0) + { + if (fflush_buf(opaque, stream) <= 0) + return 0; + + continue; + } + + memcpy(streamio->writebuf + streamio->writebuf_pos, (char *)buf + (bytes_to_write - bytes_left_to_write), bytes_to_copy); + + print_buf(opaque, stream, "write copy [remaining %d write %d:%d len %d]\n", bytes_to_copy, bytes_to_write, bytes_left_to_write, streamio->writebuf_len); + + bytes_left_to_write -= bytes_to_copy; + + streamio->writebuf_pos += bytes_to_copy; + streamio->writebuf_hits += 1; + if (streamio->writebuf_pos > streamio->writebuf_len) + streamio->writebuf_len += streamio->writebuf_pos - streamio->writebuf_len; + } + + return size - bytes_left_to_write; +} + +uint64_t ftell_buf_internal_func(ZIP_UNUSED voidpf opaque, voidpf stream, uint64_t position) +{ + ourstream_t *streamio = (ourstream_t *)stream; + streamio->position = position; + print_buf(opaque, stream, "tell [pos %llu readpos %d writepos %d err %d]\n", streamio->position, streamio->readbuf_pos, streamio->writebuf_pos, errno); + if (streamio->readbuf_len > 0) + position -= (streamio->readbuf_len - streamio->readbuf_pos); + if (streamio->writebuf_len > 0) + position += streamio->writebuf_pos; + return position; +} + +long ZCALLBACK ftell_buf_func(voidpf opaque, voidpf stream) +{ + ourbuffer_t *bufio = (ourbuffer_t *)opaque; + ourstream_t *streamio = (ourstream_t *)stream; + uint64_t position = bufio->filefunc.ztell_file(bufio->filefunc.opaque, streamio->stream); + return (long)ftell_buf_internal_func(opaque, stream, position); +} + +uint64_t ZCALLBACK ftell64_buf_func(voidpf opaque, voidpf stream) +{ + ourbuffer_t *bufio = (ourbuffer_t *)opaque; + ourstream_t *streamio = (ourstream_t *)stream; + uint64_t position = bufio->filefunc64.ztell64_file(bufio->filefunc64.opaque, streamio->stream); + return ftell_buf_internal_func(opaque, stream, position); +} + +int fseek_buf_internal_func(voidpf opaque, voidpf stream, uint64_t offset, int origin) +{ + ourstream_t *streamio = (ourstream_t *)stream; + + print_buf(opaque, stream, "seek [origin %d offset %llu pos %lld]\n", origin, offset, streamio->position); + + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_SET: + + if (streamio->writebuf_len > 0) + { + if ((offset >= streamio->position) && (offset <= streamio->position + streamio->writebuf_len)) + { + streamio->writebuf_pos = (uint32_t)(offset - streamio->position); + return 0; + } + } + if ((streamio->readbuf_len > 0) && (offset < streamio->position) && (offset >= streamio->position - streamio->readbuf_len)) + { + streamio->readbuf_pos = (uint32_t)(offset - (streamio->position - streamio->readbuf_len)); + return 0; + } + if (fflush_buf(opaque, stream) < 0) + return -1; + streamio->position = offset; + break; + + case ZLIB_FILEFUNC_SEEK_CUR: + + if (streamio->readbuf_len > 0) + { + if (offset <= (streamio->readbuf_len - streamio->readbuf_pos)) + { + streamio->readbuf_pos += (uint32_t)offset; + return 0; + } + offset -= (streamio->readbuf_len - streamio->readbuf_pos); + streamio->position += offset; + } + if (streamio->writebuf_len > 0) + { + if (offset <= (streamio->writebuf_len - streamio->writebuf_pos)) + { + streamio->writebuf_pos += (uint32_t)offset; + return 0; + } + //offset -= (streamio->writebuf_len - streamio->writebuf_pos); + } + + if (fflush_buf(opaque, stream) < 0) + return -1; + + break; + + case ZLIB_FILEFUNC_SEEK_END: + + if (streamio->writebuf_len > 0) + { + streamio->writebuf_pos = streamio->writebuf_len; + return 0; + } + break; + } + + streamio->readbuf_len = 0; + streamio->readbuf_pos = 0; + streamio->writebuf_len = 0; + streamio->writebuf_pos = 0; + return 1; +} + +long ZCALLBACK fseek_buf_func(voidpf opaque, voidpf stream, uint32_t offset, int origin) +{ + ourbuffer_t *bufio = (ourbuffer_t *)opaque; + ourstream_t *streamio = (ourstream_t *)stream; + long ret = -1; + if (bufio->filefunc.zseek_file == NULL) + return ret; + ret = fseek_buf_internal_func(opaque, stream, offset, origin); + if (ret == 1) + ret = bufio->filefunc.zseek_file(bufio->filefunc.opaque, streamio->stream, offset, origin); + return ret; +} + +long ZCALLBACK fseek64_buf_func(voidpf opaque, voidpf stream, uint64_t offset, int origin) +{ + ourbuffer_t *bufio = (ourbuffer_t *)opaque; + ourstream_t *streamio = (ourstream_t *)stream; + long ret = -1; + if (bufio->filefunc64.zseek64_file == NULL) + return ret; + ret = fseek_buf_internal_func(opaque, stream, offset, origin); + if (ret == 1) + ret = bufio->filefunc64.zseek64_file(bufio->filefunc64.opaque, streamio->stream, offset, origin); + return ret; +} + +int ZCALLBACK fclose_buf_func(voidpf opaque, voidpf stream) +{ + ourbuffer_t *bufio = (ourbuffer_t *)opaque; + ourstream_t *streamio = (ourstream_t *)stream; + int ret = 0; + fflush_buf(opaque, stream); + print_buf(opaque, stream, "close\n"); + if (streamio->readbuf_hits + streamio->readbuf_misses > 0) + print_buf(opaque, stream, "read efficency %.02f%%\n", (streamio->readbuf_hits / ((float)streamio->readbuf_hits + streamio->readbuf_misses)) * 100); + if (streamio->writebuf_hits + streamio->writebuf_misses > 0) + print_buf(opaque, stream, "write efficency %.02f%%\n", (streamio->writebuf_hits / ((float)streamio->writebuf_hits + streamio->writebuf_misses)) * 100); + if (bufio->filefunc64.zclose_file != NULL) + ret = bufio->filefunc64.zclose_file(bufio->filefunc64.opaque, streamio->stream); + else + ret = bufio->filefunc.zclose_file(bufio->filefunc.opaque, streamio->stream); + free(streamio); + return ret; +} + +int ZCALLBACK ferror_buf_func(voidpf opaque, voidpf stream) +{ + ourbuffer_t *bufio = (ourbuffer_t *)opaque; + ourstream_t *streamio = (ourstream_t *)stream; + if (bufio->filefunc64.zerror_file != NULL) + return bufio->filefunc64.zerror_file(bufio->filefunc64.opaque, streamio->stream); + return bufio->filefunc.zerror_file(bufio->filefunc.opaque, streamio->stream); +} + +void fill_buffer_filefunc(zlib_filefunc_def *pzlib_filefunc_def, ourbuffer_t *ourbuf) +{ + pzlib_filefunc_def->zopen_file = fopen_buf_func; + pzlib_filefunc_def->zopendisk_file = fopendisk_buf_func; + pzlib_filefunc_def->zread_file = fread_buf_func; + pzlib_filefunc_def->zwrite_file = fwrite_buf_func; + pzlib_filefunc_def->ztell_file = ftell_buf_func; + pzlib_filefunc_def->zseek_file = fseek_buf_func; + pzlib_filefunc_def->zclose_file = fclose_buf_func; + pzlib_filefunc_def->zerror_file = ferror_buf_func; + pzlib_filefunc_def->opaque = ourbuf; +} + +void fill_buffer_filefunc64(zlib_filefunc64_def *pzlib_filefunc_def, ourbuffer_t *ourbuf) +{ + pzlib_filefunc_def->zopen64_file = fopen64_buf_func; + pzlib_filefunc_def->zopendisk64_file = fopendisk64_buf_func; + pzlib_filefunc_def->zread_file = fread_buf_func; + pzlib_filefunc_def->zwrite_file = fwrite_buf_func; + pzlib_filefunc_def->ztell64_file = ftell64_buf_func; + pzlib_filefunc_def->zseek64_file = fseek64_buf_func; + pzlib_filefunc_def->zclose_file = fclose_buf_func; + pzlib_filefunc_def->zerror_file = ferror_buf_func; + pzlib_filefunc_def->opaque = ourbuf; +} diff --git a/SSZipArchive/minizip/ioapi_buf.h b/SSZipArchive/minizip/ioapi_buf.h new file mode 100644 index 0000000..b0e7698 --- /dev/null +++ b/SSZipArchive/minizip/ioapi_buf.h @@ -0,0 +1,52 @@ +/* ioapi_buf.h -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + This version of ioapi is designed to buffer IO. + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#ifndef _IOAPI_BUF_H +#define _IOAPI_BUF_H + +#include +#include +#include + +#include "zlib.h" +#include "ioapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +voidpf ZCALLBACK fopen_buf_func(voidpf opaque, const char* filename, int mode); +voidpf ZCALLBACK fopen64_buf_func(voidpf opaque, const void* filename, int mode); +voidpf ZCALLBACK fopendisk_buf_func(voidpf opaque, voidpf stream_cd, uint32_t number_disk, int mode); +voidpf ZCALLBACK fopendisk64_buf_func(voidpf opaque, voidpf stream_cd, uint32_t number_disk, int mode); +uint32_t ZCALLBACK fread_buf_func(voidpf opaque, voidpf stream, void* buf, uint32_t size); +uint32_t ZCALLBACK fwrite_buf_func(voidpf opaque, voidpf stream, const void* buf, uint32_t size); +long ZCALLBACK ftell_buf_func(voidpf opaque, voidpf stream); +uint64_t ZCALLBACK ftell64_buf_func(voidpf opaque, voidpf stream); +long ZCALLBACK fseek_buf_func(voidpf opaque, voidpf stream, uint32_t offset, int origin); +long ZCALLBACK fseek64_buf_func(voidpf opaque, voidpf stream, uint64_t offset, int origin); +int ZCALLBACK fclose_buf_func(voidpf opaque,voidpf stream); +int ZCALLBACK ferror_buf_func(voidpf opaque,voidpf stream); + +typedef struct ourbuffer_s { + zlib_filefunc_def filefunc; + zlib_filefunc64_def filefunc64; +} ourbuffer_t; + +void fill_buffer_filefunc(zlib_filefunc_def* pzlib_filefunc_def, ourbuffer_t *ourbuf); +void fill_buffer_filefunc64(zlib_filefunc64_def* pzlib_filefunc_def, ourbuffer_t *ourbuf); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/SSZipArchive/minizip/ioapi_mem.c b/SSZipArchive/minizip/ioapi_mem.c new file mode 100644 index 0000000..ed53345 --- /dev/null +++ b/SSZipArchive/minizip/ioapi_mem.c @@ -0,0 +1,167 @@ +/* ioapi_mem.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + This version of ioapi is designed to access memory rather than files. + We do use a region of memory to put data in to and take it out of. We do + not have auto-extending buffers and do not inform anyone else that the + data has been written. It is really intended for accessing a zip archive + embedded in an application such that I can write an installer with no + external files. Creation of archives has not been attempted, although + parts of the framework are present. + + Based on Unzip ioapi.c version 0.22, May 19th, 2003 + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + Copyright (C) 2003 Justin Fletcher + Copyright (C) 1998-2003 Gilles Vollant + http://www.winimage.com/zLibDll/minizip.html + + This file is under the same license as the Unzip tool it is distributed + with. +*/ + + +#include +#include +#include + +#include "zlib.h" +#include "ioapi.h" + +#include "ioapi_mem.h" + +#ifndef IOMEM_BUFFERSIZE +# define IOMEM_BUFFERSIZE (UINT16_MAX) +#endif + +voidpf ZCALLBACK fopen_mem_func(voidpf opaque, ZIP_UNUSED const char *filename, int mode) +{ + ourmemory_t *mem = (ourmemory_t *)opaque; + if (mem == NULL) + return NULL; /* Mem structure passed in was null */ + + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + { + if (mem->grow) + { + mem->size = IOMEM_BUFFERSIZE; + mem->base = (char *)malloc(mem->size); + } + + mem->limit = 0; /* When writing we start with 0 bytes written */ + } + else + mem->limit = mem->size; + + mem->cur_offset = 0; + + return mem; +} + +voidpf ZCALLBACK fopendisk_mem_func(ZIP_UNUSED voidpf opaque, ZIP_UNUSED voidpf stream, ZIP_UNUSED uint32_t number_disk, ZIP_UNUSED int mode) +{ + /* Not used */ + return NULL; +} + +uint32_t ZCALLBACK fread_mem_func(ZIP_UNUSED voidpf opaque, voidpf stream, void *buf, uint32_t size) +{ + ourmemory_t *mem = (ourmemory_t *)stream; + + if (size > mem->size - mem->cur_offset) + size = mem->size - mem->cur_offset; + + memcpy(buf, mem->base + mem->cur_offset, size); + mem->cur_offset += size; + + return size; +} + +uint32_t ZCALLBACK fwrite_mem_func(ZIP_UNUSED voidpf opaque, voidpf stream, const void *buf, uint32_t size) +{ + ourmemory_t *mem = (ourmemory_t *)stream; + uint32_t newmemsize = 0; + char *newbase = NULL; + + if (size > mem->size - mem->cur_offset) + { + if (mem->grow) + { + newmemsize = mem->size; + if (size < IOMEM_BUFFERSIZE) + newmemsize += IOMEM_BUFFERSIZE; + else + newmemsize += size; + newbase = (char *)malloc(newmemsize); + memcpy(newbase, mem->base, mem->size); + free(mem->base); + mem->base = newbase; + mem->size = newmemsize; + } + else + size = mem->size - mem->cur_offset; + } + memcpy(mem->base + mem->cur_offset, buf, size); + mem->cur_offset += size; + if (mem->cur_offset > mem->limit) + mem->limit = mem->cur_offset; + + return size; +} + +long ZCALLBACK ftell_mem_func(ZIP_UNUSED voidpf opaque, voidpf stream) +{ + ourmemory_t *mem = (ourmemory_t *)stream; + return mem->cur_offset; +} + +long ZCALLBACK fseek_mem_func(ZIP_UNUSED voidpf opaque, voidpf stream, uint32_t offset, int origin) +{ + ourmemory_t *mem = (ourmemory_t *)stream; + uint32_t new_pos = 0; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR: + new_pos = mem->cur_offset + offset; + break; + case ZLIB_FILEFUNC_SEEK_END: + new_pos = mem->limit + offset; + break; + case ZLIB_FILEFUNC_SEEK_SET: + new_pos = offset; + break; + default: + return -1; + } + + if (new_pos > mem->size) + return 1; /* Failed to seek that far */ + mem->cur_offset = new_pos; + return 0; +} + +int ZCALLBACK fclose_mem_func(ZIP_UNUSED voidpf opaque, ZIP_UNUSED voidpf stream) +{ + /* Even with grow = 1, caller must always free() memory */ + return 0; +} + +int ZCALLBACK ferror_mem_func(ZIP_UNUSED voidpf opaque, ZIP_UNUSED voidpf stream) +{ + /* We never return errors */ + return 0; +} + +void fill_memory_filefunc(zlib_filefunc_def *pzlib_filefunc_def, ourmemory_t *ourmem) +{ + pzlib_filefunc_def->zopen_file = fopen_mem_func; + pzlib_filefunc_def->zopendisk_file = fopendisk_mem_func; + pzlib_filefunc_def->zread_file = fread_mem_func; + pzlib_filefunc_def->zwrite_file = fwrite_mem_func; + pzlib_filefunc_def->ztell_file = ftell_mem_func; + pzlib_filefunc_def->zseek_file = fseek_mem_func; + pzlib_filefunc_def->zclose_file = fclose_mem_func; + pzlib_filefunc_def->zerror_file = ferror_mem_func; + pzlib_filefunc_def->opaque = ourmem; +} diff --git a/SSZipArchive/minizip/ioapi_mem.h b/SSZipArchive/minizip/ioapi_mem.h new file mode 100644 index 0000000..7061d6f --- /dev/null +++ b/SSZipArchive/minizip/ioapi_mem.h @@ -0,0 +1,52 @@ +/* ioapi_mem.h -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + This version of ioapi is designed to access memory rather than files. + We do use a region of memory to put data in to and take it out of. + + Copyright (C) 2012-2017 Nathan Moinvaziri (https://github.com/nmoinvaz/minizip) + (C) 2003 Justin Fletcher + (C) 1998-2003 Gilles Vollant + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#ifndef _IOAPI_MEM_H +#define _IOAPI_MEM_H + +#include +#include +#include + +#include "zlib.h" +#include "ioapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +voidpf ZCALLBACK fopen_mem_func(voidpf opaque, const char* filename, int mode); +voidpf ZCALLBACK fopendisk_mem_func(voidpf opaque, voidpf stream, uint32_t number_disk, int mode); +uint32_t ZCALLBACK fread_mem_func(voidpf opaque, voidpf stream, void* buf, uint32_t size); +uint32_t ZCALLBACK fwrite_mem_func(voidpf opaque, voidpf stream, const void* buf, uint32_t size); +long ZCALLBACK ftell_mem_func(voidpf opaque, voidpf stream); +long ZCALLBACK fseek_mem_func(voidpf opaque, voidpf stream, uint32_t offset, int origin); +int ZCALLBACK fclose_mem_func(voidpf opaque, voidpf stream); +int ZCALLBACK ferror_mem_func(voidpf opaque, voidpf stream); + +typedef struct ourmemory_s { + char *base; /* Base of the region of memory we're using */ + uint32_t size; /* Size of the region of memory we're using */ + uint32_t limit; /* Furthest we've written */ + uint32_t cur_offset; /* Current offset in the area */ + int grow; /* Growable memory buffer */ +} ourmemory_t; + +void fill_memory_filefunc(zlib_filefunc_def* pzlib_filefunc_def, ourmemory_t *ourmem); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/SSZipArchive/minizip/minishared.c b/SSZipArchive/minizip/minishared.c new file mode 100644 index 0000000..beabddc --- /dev/null +++ b/SSZipArchive/minizip/minishared.c @@ -0,0 +1,292 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "zlib.h" +#include "ioapi.h" + +#ifdef _WIN32 +# include +# include +#else +# include +# include +# include +# include +#endif + +#include "minishared.h" + +#ifdef _WIN32 +# define USEWIN32IOAPI +# include "iowin32.h" +#endif + +uint32_t get_file_date(const char *path, uint32_t *dos_date) +{ + int ret = 0; +#ifdef _WIN32 + FILETIME ftm_local; + HANDLE find = NULL; + WIN32_FIND_DATAA ff32; + + find = FindFirstFileA(path, &ff32); + if (find != INVALID_HANDLE_VALUE) + { + FileTimeToLocalFileTime(&(ff32.ftLastWriteTime), &ftm_local); + FileTimeToDosDateTime(&ftm_local, ((LPWORD)dos_date) + 1, ((LPWORD)dos_date) + 0); + FindClose(find); + ret = 1; + } +#else + struct stat s; + struct tm *filedate = NULL; + time_t tm_t = 0; + + memset(&s, 0, sizeof(s)); + + if (strcmp(path, "-") != 0) + { + size_t len = strlen(path); + char *name = (char *)malloc(len + 1); + strncpy(name, path, len + 1); + name[len] = 0; + if (name[len - 1] == '/') + name[len - 1] = 0; + + /* Not all systems allow stat'ing a file with / appended */ + if (stat(name, &s) == 0) + { + tm_t = s.st_mtime; + ret = 1; + } + free(name); + } + + filedate = localtime(&tm_t); + *dos_date = tm_to_dosdate(filedate); +#endif + return ret; +} + +void change_file_date(const char *path, uint32_t dos_date) +{ +#ifdef _WIN32 + HANDLE handle = NULL; + FILETIME ftm, ftm_local, ftm_create, ftm_access, ftm_modified; + + handle = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (handle != INVALID_HANDLE_VALUE) + { + GetFileTime(handle, &ftm_create, &ftm_access, &ftm_modified); + DosDateTimeToFileTime((WORD)(dos_date >> 16), (WORD)dos_date, &ftm_local); + LocalFileTimeToFileTime(&ftm_local, &ftm); + SetFileTime(handle, &ftm, &ftm_access, &ftm); + CloseHandle(handle); + } +#else + struct utimbuf ut; + ut.actime = ut.modtime = dosdate_to_time_t(dos_date); + utime(path, &ut); +#endif +} + +int invalid_date(const struct tm *ptm) +{ +#define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max)) + return (!datevalue_in_range(0, 207, ptm->tm_year) || + !datevalue_in_range(0, 11, ptm->tm_mon) || + !datevalue_in_range(1, 31, ptm->tm_mday) || + !datevalue_in_range(0, 23, ptm->tm_hour) || + !datevalue_in_range(0, 59, ptm->tm_min) || + !datevalue_in_range(0, 59, ptm->tm_sec)); +#undef datevalue_in_range +} + +// Conversion without validation +void dosdate_to_raw_tm(uint64_t dos_date, struct tm *ptm) +{ + uint64_t date = (uint64_t)(dos_date >> 16); + + ptm->tm_mday = (uint16_t)(date & 0x1f); + ptm->tm_mon = (uint16_t)(((date & 0x1E0) / 0x20) - 1); + ptm->tm_year = (uint16_t)(((date & 0x0FE00) / 0x0200) + 80); + ptm->tm_hour = (uint16_t)((dos_date & 0xF800) / 0x800); + ptm->tm_min = (uint16_t)((dos_date & 0x7E0) / 0x20); + ptm->tm_sec = (uint16_t)(2 * (dos_date & 0x1f)); + ptm->tm_isdst = -1; +} + +int dosdate_to_tm(uint64_t dos_date, struct tm *ptm) +{ + dosdate_to_raw_tm(dos_date, ptm); + + if (invalid_date(ptm)) + { + // Invalid date stored, so don't return it. + memset(ptm, 0, sizeof(struct tm)); + return -1; + } + return 0; +} + +time_t dosdate_to_time_t(uint64_t dos_date) +{ + struct tm ptm; + dosdate_to_raw_tm(dos_date, &ptm); + return mktime(&ptm); +} + +uint32_t tm_to_dosdate(const struct tm *ptm) +{ + struct tm fixed_tm; + + /* Years supported: + * [00, 79] (assumed to be between 2000 and 2079) + * [80, 207] (assumed to be between 1980 and 2107, typical output of old + software that does 'year-1900' to get a double digit year) + * [1980, 2107] (due to the date format limitations, only years between 1980 and 2107 can be stored.) + */ + + memcpy(&fixed_tm, ptm, sizeof(struct tm)); + if (fixed_tm.tm_year >= 1980) /* range [1980, 2107] */ + fixed_tm.tm_year -= 1980; + else if (fixed_tm.tm_year >= 80) /* range [80, 99] */ + fixed_tm.tm_year -= 80; + else /* range [00, 79] */ + fixed_tm.tm_year += 20; + + if (invalid_date(ptm)) + return 0; + + return (uint32_t)(((fixed_tm.tm_mday) + (32 * (fixed_tm.tm_mon + 1)) + (512 * fixed_tm.tm_year)) << 16) | + ((fixed_tm.tm_sec / 2) + (32 * fixed_tm.tm_min) + (2048 * (uint32_t)fixed_tm.tm_hour)); +} + +int makedir(const char *newdir) +{ + char *buffer = NULL; + char *p = NULL; + int len = (int)strlen(newdir); + + if (len <= 0) + return 0; + + buffer = (char*)malloc(len + 1); + if (buffer == NULL) + { + printf("Error allocating memory\n"); + return -1; + } + + strcpy(buffer, newdir); + + if (buffer[len - 1] == '/') + buffer[len - 1] = 0; + + if (MKDIR(buffer) == 0) + { + free(buffer); + return 1; + } + + p = buffer + 1; + while (1) + { + char hold; + while (*p && *p != '\\' && *p != '/') + p++; + hold = *p; + *p = 0; + + if ((MKDIR(buffer) == -1) && (errno == ENOENT)) + { + printf("couldn't create directory %s (%d)\n", buffer, errno); + free(buffer); + return 0; + } + + if (hold == 0) + break; + + *p++ = hold; + } + + free(buffer); + return 1; +} + +FILE *get_file_handle(const char *path) +{ + FILE *handle = NULL; +#if defined(WIN32) + wchar_t *pathWide = NULL; + int pathLength = 0; + + pathLength = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0) + 1; + pathWide = (wchar_t*)calloc(pathLength, sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, path, -1, pathWide, pathLength); + handle = _wfopen((const wchar_t*)pathWide, L"rb"); + free(pathWide); +#else + handle = fopen64(path, "rb"); +#endif + + return handle; +} + +int check_file_exists(const char *path) +{ + FILE *handle = get_file_handle(path); + if (handle == NULL) + return 0; + fclose(handle); + return 1; +} + +int is_large_file(const char *path) +{ + FILE* handle = NULL; + uint64_t pos = 0; + + handle = get_file_handle(path); + if (handle == NULL) + return 0; + + fseeko64(handle, 0, SEEK_END); + pos = ftello64(handle); + fclose(handle); + + printf("file : %s is %lld bytes\n", path, pos); + + return (pos >= UINT32_MAX); +} + +void display_zpos64(uint64_t n, int size_char) +{ + /* To avoid compatibility problem we do here the conversion */ + char number[21] = { 0 }; + int offset = 19; + int pos_string = 19; + int size_display_string = 19; + + while (1) + { + number[offset] = (char)((n % 10) + '0'); + if (number[offset] != '0') + pos_string = offset; + n /= 10; + if (offset == 0) + break; + offset--; + } + + size_display_string -= pos_string; + while (size_char-- > size_display_string) + printf(" "); + printf("%s", &number[pos_string]); +} diff --git a/SSZipArchive/minizip/minishared.h b/SSZipArchive/minizip/minishared.h new file mode 100644 index 0000000..32fccb6 --- /dev/null +++ b/SSZipArchive/minizip/minishared.h @@ -0,0 +1,51 @@ +#ifndef _MINISHARED_H +#define _MINISHARED_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +# define MKDIR(d) _mkdir(d) +# define CHDIR(d) _chdir(d) +#else +# define MKDIR(d) mkdir(d, 0775) +# define CHDIR(d) chdir(d) +#endif + +/***************************************************************************/ + +/* Get a file's date and time in dos format */ +uint32_t get_file_date(const char *path, uint32_t *dos_date); + +/* Sets a file's date and time in dos format */ +void change_file_date(const char *path, uint32_t dos_date); + +/* Convert dos date/time format to struct tm */ +int dosdate_to_tm(uint64_t dos_date, struct tm *ptm); + +/* Convert dos date/time format to time_t */ +time_t dosdate_to_time_t(uint64_t dos_date); + +/* Convert struct tm to dos date/time format */ +uint32_t tm_to_dosdate(const struct tm *ptm); + +/* Create a directory and all subdirectories */ +int makedir(const char *newdir); + +/* Check to see if a file exists */ +int check_file_exists(const char *path); + +/* Check to see if a file is over 4GB and needs ZIP64 extension */ +int is_large_file(const char *path); + +/* Print a 64-bit number for compatibility */ +void display_zpos64(uint64_t n, int size_char); + +/***************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* _MINISHARED_H */ \ No newline at end of file diff --git a/SSZipArchive/minizip/unzip.c b/SSZipArchive/minizip/unzip.c new file mode 100644 index 0000000..90a7310 --- /dev/null +++ b/SSZipArchive/minizip/unzip.c @@ -0,0 +1,1999 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.2.0, September 16th, 2017 + part of the MiniZip project + + Copyright (C) 2010-2017 Nathan Moinvaziri + Modifications for AES, PKWARE disk spanning + https://github.com/nmoinvaz/minizip + Copyright (C) 2009-2010 Mathias Svensson + Modifications for Zip64 support on both zip and unzip + http://result42.com + Copyright (C) 2007-2008 Even Rouault + Modifications of Unzip for Zip64 + Copyright (C) 1998-2010 Gilles Vollant + http://www.winimage.com/zLibDll/minizip.html + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#include +#include +#include +#include +#include +#include + +#include "zlib.h" +#include "unzip.h" + +#ifdef HAVE_AES +# define AES_METHOD (99) +# define AES_PWVERIFYSIZE (2) +# define AES_MAXSALTLENGTH (16) +# define AES_AUTHCODESIZE (10) +# define AES_HEADERSIZE (11) +# define AES_KEYSIZE(mode) (64 + (mode * 64)) + +# include "aes/aes.h" +# include "aes/fileenc.h" +#endif +#ifdef HAVE_APPLE_COMPRESSION +# include +#endif + +#ifndef NOUNCRYPT +# include "crypt.h" +#endif + +#define DISKHEADERMAGIC (0x08074b50) +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) +#define ZIP64ENDHEADERMAGIC (0x06064b50) +#define ZIP64ENDLOCHEADERMAGIC (0x07064b50) + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZECENTRALHEADERLOCATOR (0x14) +#define SIZEZIPLOCALHEADER (0x1e) + +#ifndef BUFREADCOMMENT +# define BUFREADCOMMENT (0x400) +#endif + +#ifndef UNZ_BUFSIZE +# define UNZ_BUFSIZE (UINT16_MAX) +#endif +#ifndef UNZ_MAXFILENAMEINZIP +# define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +const char unz_copyright[] = " unzip 1.2.0 Copyright 1998-2017 - https://github.com/nmoinvaz/minizip"; + +/* unz_file_info_internal contain internal info about a file in zipfile*/ +typedef struct unz_file_info64_internal_s +{ + uint64_t offset_curfile; /* relative offset of local header 8 bytes */ + uint64_t byte_before_the_zipfile; /* byte before the zipfile, (>0 for sfx) */ +#ifdef HAVE_AES + uint8_t aes_encryption_mode; + uint16_t aes_compression_method; + uint16_t aes_version; +#endif +} unz_file_info64_internal; + +/* file_in_zip_read_info_s contain internal information about a file in zipfile */ +typedef struct +{ + uint8_t *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif +#ifdef HAVE_APPLE_COMPRESSION + compression_stream astream; /* libcompression stream structure */ +#endif +#ifdef HAVE_AES + fcrypt_ctx aes_ctx; +#endif + uint64_t pos_in_zipfile; /* position in byte on the zipfile, for fseek */ + uint8_t stream_initialised; /* flag set if stream structure is initialised */ + + uint64_t offset_local_extrafield; /* offset of the local extra field */ + uint16_t size_local_extrafield; /* size of the local extra field */ + uint64_t pos_local_extrafield; /* position in the local extra field in read */ + uint64_t total_out_64; + + uint32_t crc32; /* crc32 of all data uncompressed */ + uint32_t crc32_expected; /* crc32 we must obtain after decompress all */ + uint64_t rest_read_compressed; /* number of byte to be decompressed */ + uint64_t rest_read_uncompressed; /* number of byte to be obtained after decomp */ + + zlib_filefunc64_32_def z_filefunc; + + voidpf filestream; /* io structore of the zipfile */ + uint16_t compression_method; /* compression method (0==store) */ + uint64_t byte_before_the_zipfile; /* byte before the zipfile, (>0 for sfx) */ + int raw; +} file_in_zip64_read_info_s; + +/* unz64_s contain internal information about the zipfile */ +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + + voidpf filestream; /* io structure of the current zipfile */ + voidpf filestream_with_CD; /* io structure of the disk with the central directory */ + + unz_global_info64 gi; /* public global information */ + + uint64_t byte_before_the_zipfile; /* byte before the zipfile, (>0 for sfx) */ + uint64_t num_file; /* number of the current file in the zipfile */ + uint64_t pos_in_central_dir; /* pos of the current file in the central dir */ + uint64_t current_file_ok; /* flag about the usability of the current file */ + uint64_t central_pos; /* position of the beginning of the central dir */ + uint32_t number_disk; /* number of the current disk, used for spanning ZIP */ + uint64_t size_central_dir; /* size of the central directory */ + uint64_t offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info64 cur_file_info; /* public info about the current file in zip*/ + unz_file_info64_internal cur_file_info_internal; + /* private info about it*/ + file_in_zip64_read_info_s *pfile_in_zip_read; + /* structure about the current file if we are decompressing it */ + int is_zip64; /* is the current file zip64 */ +#ifndef NOUNCRYPT + uint32_t keys[3]; /* keys defining the pseudo-random sequence */ + const z_crc_t *pcrc_32_tab; +#endif +} unz64_internal; + +/* Read a byte from a gz_stream; Return EOF for end of file. */ +static int unzReadUInt8(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint8_t *value) +{ + uint8_t c = 0; + if (ZREAD64(*pzlib_filefunc_def, filestream, &c, 1) == 1) + { + *value = (uint8_t)c; + return UNZ_OK; + } + *value = 0; + if (ZERROR64(*pzlib_filefunc_def, filestream)) + return UNZ_ERRNO; + return UNZ_EOF; +} + +static int unzReadUInt16(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint16_t *value) +{ + uint16_t x; + uint8_t c = 0; + int err = UNZ_OK; + + err = unzReadUInt8(pzlib_filefunc_def, filestream, &c); + x = (uint16_t)c; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &c); + x |= ((uint16_t)c) << 8; + + if (err == UNZ_OK) + *value = x; + else + *value = 0; + return err; +} + +static int unzReadUInt32(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint32_t *value) +{ + uint32_t x = 0; + uint8_t c = 0; + int err = UNZ_OK; + + err = unzReadUInt8(pzlib_filefunc_def, filestream, &c); + x = (uint32_t)c; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &c); + x |= ((uint32_t)c) << 8; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &c); + x |= ((uint32_t)c) << 16; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &c); + x += ((uint32_t)c) << 24; + + if (err == UNZ_OK) + *value = x; + else + *value = 0; + return err; +} + +static int unzReadUInt64(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint64_t *value) +{ + uint64_t x = 0; + uint8_t i = 0; + int err = UNZ_OK; + + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x = (uint64_t)i; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 8; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 16; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 24; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 32; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 40; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 48; + if (err == UNZ_OK) + err = unzReadUInt8(pzlib_filefunc_def, filestream, &i); + x |= ((uint64_t)i) << 56; + + if (err == UNZ_OK) + *value = x; + else + *value = 0; + return err; +} + +/* Locate the Central directory of a zip file (at the end, just before the global comment) */ +static int unzSearchCentralDir(const zlib_filefunc64_32_def *pzlib_filefunc_def, uint64_t *pos_found, voidpf filestream) +{ + uint8_t buf[BUFREADCOMMENT + 4]; + uint64_t file_size = 0; + uint64_t back_read = 4; + uint64_t max_back = UINT16_MAX; /* maximum size of global comment */ + uint32_t read_size = 0; + uint64_t read_pos = 0; + uint32_t i = 0; + *pos_found = 0; + + if (ZSEEK64(*pzlib_filefunc_def, filestream, 0, ZLIB_FILEFUNC_SEEK_END) != 0) + return UNZ_ERRNO; + + file_size = ZTELL64(*pzlib_filefunc_def, filestream); + + if (max_back > file_size) + max_back = file_size; + + while (back_read < max_back) + { + if (back_read + BUFREADCOMMENT > max_back) + back_read = max_back; + else + back_read += BUFREADCOMMENT; + + read_pos = file_size - back_read; + read_size = ((BUFREADCOMMENT + 4) < (file_size - read_pos)) ? + (BUFREADCOMMENT + 4) : (uint32_t)(file_size - read_pos); + + if (ZSEEK64(*pzlib_filefunc_def, filestream, read_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + break; + if (ZREAD64(*pzlib_filefunc_def, filestream, buf, read_size) != read_size) + break; + + for (i = read_size - 3; (i--) > 0;) + if (((*(buf+i)) == (ENDHEADERMAGIC & 0xff)) && + ((*(buf+i+1)) == (ENDHEADERMAGIC >> 8 & 0xff)) && + ((*(buf+i+2)) == (ENDHEADERMAGIC >> 16 & 0xff)) && + ((*(buf+i+3)) == (ENDHEADERMAGIC >> 24 & 0xff))) + { + *pos_found = read_pos+i; + return UNZ_OK; + } + } + + return UNZ_ERRNO; +} + +/* Locate the Central directory 64 of a zipfile (at the end, just before the global comment) */ +static int unzSearchCentralDir64(const zlib_filefunc64_32_def *pzlib_filefunc_def, uint64_t *offset, voidpf filestream, + const uint64_t endcentraloffset) +{ + uint32_t value32 = 0; + *offset = 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def, filestream, endcentraloffset - SIZECENTRALHEADERLOCATOR, ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + + /* Read locator signature */ + if (unzReadUInt32(pzlib_filefunc_def, filestream, &value32) != UNZ_OK) + return UNZ_ERRNO; + if (value32 != ZIP64ENDLOCHEADERMAGIC) + return UNZ_ERRNO; + /* Number of the disk with the start of the zip64 end of central directory */ + if (unzReadUInt32(pzlib_filefunc_def, filestream, &value32) != UNZ_OK) + return UNZ_ERRNO; + /* Relative offset of the zip64 end of central directory record */ + if (unzReadUInt64(pzlib_filefunc_def, filestream, offset) != UNZ_OK) + return UNZ_ERRNO; + /* Total number of disks */ + if (unzReadUInt32(pzlib_filefunc_def, filestream, &value32) != UNZ_OK) + return UNZ_ERRNO; + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def, filestream, *offset, ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + /* The signature */ + if (unzReadUInt32(pzlib_filefunc_def, filestream, &value32) != UNZ_OK) + return UNZ_ERRNO; + if (value32 != ZIP64ENDHEADERMAGIC) + return UNZ_ERRNO; + + return UNZ_OK; +} + +static unzFile unzOpenInternal(const void *path, zlib_filefunc64_32_def *pzlib_filefunc64_32_def) +{ + unz64_internal us = { 0 }; + unz64_internal *s = NULL; + uint64_t central_pos = 0; + uint64_t central_pos64 = 0; + uint64_t number_entry_CD = 0; + uint16_t value16 = 0; + uint32_t value32 = 0; + uint64_t value64 = 0; + voidpf filestream = NULL; + int err = UNZ_OK; + int err64 = UNZ_OK; + + if (pzlib_filefunc64_32_def == NULL) + fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); + else + { + assert((pzlib_filefunc64_32_def->zopen32_file || pzlib_filefunc64_32_def->zfile_func64.zopen64_file) && + (pzlib_filefunc64_32_def->zopendisk32_file || pzlib_filefunc64_32_def->zfile_func64.zopendisk64_file) && + (pzlib_filefunc64_32_def->ztell32_file || pzlib_filefunc64_32_def->zfile_func64.ztell64_file) && + (pzlib_filefunc64_32_def->zseek32_file || pzlib_filefunc64_32_def->zfile_func64.zseek64_file)); + us.z_filefunc = *pzlib_filefunc64_32_def; + } + + us.filestream = ZOPEN64(us.z_filefunc, path, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); + + if (us.filestream == NULL) + return NULL; + + us.filestream_with_CD = us.filestream; + + /* Search for end of central directory header */ + err = unzSearchCentralDir(&us.z_filefunc, ¢ral_pos, us.filestream); + if (err == UNZ_OK) + { + if (ZSEEK64(us.z_filefunc, us.filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err = UNZ_ERRNO; + + /* The signature, already checked */ + if (unzReadUInt32(&us.z_filefunc, us.filestream, &value32) != UNZ_OK) + err = UNZ_ERRNO; + /* Number of this disk */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + us.number_disk = value16; + /* Number of the disk with the start of the central directory */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + us.gi.number_disk_with_CD = value16; + /* Total number of entries in the central directory on this disk */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + us.gi.number_entry = value16; + /* Total number of entries in the central directory */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + number_entry_CD = value16; + if (number_entry_CD != us.gi.number_entry) + err = UNZ_BADZIPFILE; + /* Size of the central directory */ + if (unzReadUInt32(&us.z_filefunc, us.filestream, &value32) != UNZ_OK) + err = UNZ_ERRNO; + us.size_central_dir = value32; + /* Offset of start of central directory with respect to the starting disk number */ + if (unzReadUInt32(&us.z_filefunc, us.filestream, &value32) != UNZ_OK) + err = UNZ_ERRNO; + us.offset_central_dir = value32; + /* Zipfile comment length */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &us.gi.size_comment) != UNZ_OK) + err = UNZ_ERRNO; + + if (err == UNZ_OK) + { + /* Search for Zip64 end of central directory header */ + err64 = unzSearchCentralDir64(&us.z_filefunc, ¢ral_pos64, us.filestream, central_pos); + if (err64 == UNZ_OK) + { + central_pos = central_pos64; + us.is_zip64 = 1; + + if (ZSEEK64(us.z_filefunc, us.filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err = UNZ_ERRNO; + + /* the signature, already checked */ + if (unzReadUInt32(&us.z_filefunc, us.filestream, &value32) != UNZ_OK) + err = UNZ_ERRNO; + /* size of zip64 end of central directory record */ + if (unzReadUInt64(&us.z_filefunc, us.filestream, &value64) != UNZ_OK) + err = UNZ_ERRNO; + /* version made by */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + /* version needed to extract */ + if (unzReadUInt16(&us.z_filefunc, us.filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + /* number of this disk */ + if (unzReadUInt32(&us.z_filefunc, us.filestream, &us.number_disk) != UNZ_OK) + err = UNZ_ERRNO; + /* number of the disk with the start of the central directory */ + if (unzReadUInt32(&us.z_filefunc, us.filestream, &us.gi.number_disk_with_CD) != UNZ_OK) + err = UNZ_ERRNO; + /* total number of entries in the central directory on this disk */ + if (unzReadUInt64(&us.z_filefunc, us.filestream, &us.gi.number_entry) != UNZ_OK) + err = UNZ_ERRNO; + /* total number of entries in the central directory */ + if (unzReadUInt64(&us.z_filefunc, us.filestream, &number_entry_CD) != UNZ_OK) + err = UNZ_ERRNO; + if (number_entry_CD != us.gi.number_entry) + err = UNZ_BADZIPFILE; + /* size of the central directory */ + if (unzReadUInt64(&us.z_filefunc, us.filestream, &us.size_central_dir) != UNZ_OK) + err = UNZ_ERRNO; + /* offset of start of central directory with respect to the starting disk number */ + if (unzReadUInt64(&us.z_filefunc, us.filestream, &us.offset_central_dir) != UNZ_OK) + err = UNZ_ERRNO; + } + else if ((us.size_central_dir == UINT16_MAX) || (us.offset_central_dir == UINT32_MAX)) + err = UNZ_BADZIPFILE; + } + } + else + err = UNZ_ERRNO; + + if ((err == UNZ_OK) && (central_pos < us.offset_central_dir + us.size_central_dir)) + err = UNZ_BADZIPFILE; + + if (err != UNZ_OK) + { + ZCLOSE64(us.z_filefunc, us.filestream); + return NULL; + } + + if (us.gi.number_disk_with_CD == 0) + { + /* If there is only one disk open another stream so we don't have to seek between the CD + and the file headers constantly */ + filestream = ZOPEN64(us.z_filefunc, path, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); + if (filestream != NULL) + us.filestream = filestream; + } + + /* Hack for zip files that have no respect for zip64 + if ((central_pos > 0xffffffff) && (us.offset_central_dir < 0xffffffff)) + us.offset_central_dir = central_pos - us.size_central_dir;*/ + + us.byte_before_the_zipfile = central_pos - (us.offset_central_dir + us.size_central_dir); + us.central_pos = central_pos; + + s = (unz64_internal*)ALLOC(sizeof(unz64_internal)); + if (s != NULL) + { + *s = us; + if (err64 != UNZ_OK) + // workaround incorrect count #184 + s->gi.number_entry = unzCountEntries(s); + + unzGoToFirstFile((unzFile)s); + } + return (unzFile)s; +} + +extern unzFile ZEXPORT unzOpen2(const char *path, zlib_filefunc_def *pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill = { 0 }; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill, pzlib_filefunc32_def); + return unzOpenInternal(path, &zlib_filefunc64_32_def_fill); + } + return unzOpenInternal(path, NULL); +} + +extern unzFile ZEXPORT unzOpen2_64(const void *path, zlib_filefunc64_def *pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill = { 0 }; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + return unzOpenInternal(path, &zlib_filefunc64_32_def_fill); + } + return unzOpenInternal(path, NULL); +} + +extern unzFile ZEXPORT unzOpen(const char *path) +{ + return unzOpenInternal(path, NULL); +} + +extern unzFile ZEXPORT unzOpen64(const void *path) +{ + return unzOpenInternal(path, NULL); +} + +extern int ZEXPORT unzClose(unzFile file) +{ + unz64_internal *s; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if ((s->filestream != NULL) && (s->filestream != s->filestream_with_CD)) + ZCLOSE64(s->z_filefunc, s->filestream); + if (s->filestream_with_CD != NULL) + ZCLOSE64(s->z_filefunc, s->filestream_with_CD); + + s->filestream = NULL; + s->filestream_with_CD = NULL; + TRYFREE(s); + return UNZ_OK; +} + +/* Goto to the next available disk for spanned archives */ +static int unzGoToNextDisk(unzFile file) +{ + unz64_internal *s; + uint32_t number_disk_next = 0; + + s = (unz64_internal*)file; + if (s == NULL) + return UNZ_PARAMERROR; + number_disk_next = s->number_disk; + + if ((s->pfile_in_zip_read != NULL) && (s->pfile_in_zip_read->rest_read_uncompressed > 0)) + /* We are currently reading a file and we need the next sequential disk */ + number_disk_next += 1; + else + /* Goto the disk for the current file */ + number_disk_next = s->cur_file_info.disk_num_start; + + if (number_disk_next != s->number_disk) + { + /* Switch disks */ + if ((s->filestream != NULL) && (s->filestream != s->filestream_with_CD)) + ZCLOSE64(s->z_filefunc, s->filestream); + + if (number_disk_next == s->gi.number_disk_with_CD) + { + s->filestream = s->filestream_with_CD; + } + else + { + s->filestream = ZOPENDISK64(s->z_filefunc, s->filestream_with_CD, number_disk_next, + ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); + } + + if (s->filestream == NULL) + return UNZ_ERRNO; + + s->number_disk = number_disk_next; + } + + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo(unzFile file, unz_global_info* pglobal_info32) +{ + unz64_internal *s = NULL; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + pglobal_info32->number_entry = (uint32_t)s->gi.number_entry; + pglobal_info32->size_comment = s->gi.size_comment; + pglobal_info32->number_disk_with_CD = s->gi.number_disk_with_CD; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo64(unzFile file, unz_global_info64 *pglobal_info) +{ + unz64_internal *s = NULL; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + *pglobal_info = s->gi; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalComment(unzFile file, char *comment, uint16_t comment_size) +{ + unz64_internal *s = NULL; + uint16_t bytes_to_read = comment_size; + if (file == NULL) + return (int)UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (bytes_to_read > s->gi.size_comment) + bytes_to_read = s->gi.size_comment; + + if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, s->central_pos + 22, ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + + if (bytes_to_read > 0) + { + *comment = 0; + if (ZREAD64(s->z_filefunc, s->filestream_with_CD, comment, bytes_to_read) != bytes_to_read) + return UNZ_ERRNO; + } + + if ((comment != NULL) && (comment_size > s->gi.size_comment)) + *(comment + s->gi.size_comment) = 0; + + return (int)bytes_to_read; +} + +static int unzGetCurrentFileInfoField(unzFile file, uint32_t *seek, void *field, uint16_t field_size, uint16_t size_file_field, int null_terminated_field) +{ + unz64_internal *s = NULL; + uint32_t bytes_to_read = 0; + int err = UNZ_OK; + + if (file == NULL) + return (int)UNZ_PARAMERROR; + s = (unz64_internal*)file; + + /* Read field */ + if (field != NULL) + { + if (size_file_field < field_size) + { + if (null_terminated_field) + *((char *)field+size_file_field) = 0; + + bytes_to_read = size_file_field; + } + else + bytes_to_read = field_size; + + if (*seek != 0) + { + if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, *seek, ZLIB_FILEFUNC_SEEK_CUR) == 0) + *seek = 0; + else + err = UNZ_ERRNO; + } + + if ((size_file_field > 0) && (field_size > 0)) + { + if (ZREAD64(s->z_filefunc, s->filestream_with_CD, field, bytes_to_read) != bytes_to_read) + err = UNZ_ERRNO; + } + *seek += size_file_field - bytes_to_read; + } + else + { + *seek += size_file_field; + } + + return err; +} + +/* Get info about the current file in the zipfile, with internal only info */ +static int unzGetCurrentFileInfoInternal(unzFile file, unz_file_info64 *pfile_info, + unz_file_info64_internal *pfile_info_internal, char *filename, uint16_t filename_size, void *extrafield, + uint16_t extrafield_size, char *comment, uint16_t comment_size) +{ + unz64_internal *s = NULL; + unz_file_info64 file_info; + unz_file_info64_internal file_info_internal; + uint32_t magic = 0; + uint64_t current_pos = 0; + uint32_t seek = 0; + uint32_t extra_pos = 0; + uint16_t extra_header_id = 0; + uint16_t extra_data_size = 0; + uint16_t value16 = 0; + uint32_t value32 = 0; + uint64_t value64 = 0; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, + s->pos_in_central_dir + s->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err = UNZ_ERRNO; + + /* Check the magic */ + if (err == UNZ_OK) + { + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &magic) != UNZ_OK) + err = UNZ_ERRNO; + else if (magic != CENTRALHEADERMAGIC) + err = UNZ_BADZIPFILE; + } + + /* Read central directory header */ + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.version) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.version_needed) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.flag) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.compression_method) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &file_info.dos_date) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &file_info.crc) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &value32) != UNZ_OK) + err = UNZ_ERRNO; + file_info.compressed_size = value32; + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &value32) != UNZ_OK) + err = UNZ_ERRNO; + file_info.uncompressed_size = value32; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.size_filename) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.size_file_extra) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.size_file_comment) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &value16) != UNZ_OK) + err = UNZ_ERRNO; + file_info.disk_num_start = value16; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &file_info.internal_fa) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &file_info.external_fa) != UNZ_OK) + err = UNZ_ERRNO; + /* Relative offset of local header */ + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &value32) != UNZ_OK) + err = UNZ_ERRNO; + + file_info.size_file_extra_internal = 0; + file_info.disk_offset = value32; + file_info_internal.offset_curfile = value32; +#ifdef HAVE_AES + file_info_internal.aes_compression_method = 0; + file_info_internal.aes_encryption_mode = 0; + file_info_internal.aes_version = 0; +#endif + + if (err == UNZ_OK) + err = unzGetCurrentFileInfoField(file, &seek, filename, filename_size, file_info.size_filename, 1); + + /* Read extrafield */ + if (err == UNZ_OK) + err = unzGetCurrentFileInfoField(file, &seek, extrafield, extrafield_size, file_info.size_file_extra, 0); + + if ((err == UNZ_OK) && (file_info.size_file_extra != 0)) + { + if (seek != 0) + { + if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, seek, ZLIB_FILEFUNC_SEEK_CUR) == 0) + seek = 0; + else + err = UNZ_ERRNO; + } + + /* We are going to parse the extra field so we need to move back */ + current_pos = ZTELL64(s->z_filefunc, s->filestream_with_CD); + if (current_pos < file_info.size_file_extra) + err = UNZ_ERRNO; + current_pos -= file_info.size_file_extra; + if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, current_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err = UNZ_ERRNO; + + while ((err != UNZ_ERRNO) && (extra_pos < file_info.size_file_extra)) + { + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &extra_header_id) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &extra_data_size) != UNZ_OK) + err = UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (extra_header_id == 0x0001) + { + /* Subtract size of ZIP64 field, since ZIP64 is handled internally */ + file_info.size_file_extra_internal += 2 + 2 + extra_data_size; + + if (file_info.uncompressed_size == UINT32_MAX) + { + if (unzReadUInt64(&s->z_filefunc, s->filestream_with_CD, &file_info.uncompressed_size) != UNZ_OK) + err = UNZ_ERRNO; + } + if (file_info.compressed_size == UINT32_MAX) + { + if (unzReadUInt64(&s->z_filefunc, s->filestream_with_CD, &file_info.compressed_size) != UNZ_OK) + err = UNZ_ERRNO; + } + if (file_info_internal.offset_curfile == UINT32_MAX) + { + /* Relative Header offset */ + if (unzReadUInt64(&s->z_filefunc, s->filestream_with_CD, &value64) != UNZ_OK) + err = UNZ_ERRNO; + file_info_internal.offset_curfile = value64; + file_info.disk_offset = value64; + } + if (file_info.disk_num_start == UINT32_MAX) + { + /* Disk Start Number */ + if (unzReadUInt32(&s->z_filefunc, s->filestream_with_CD, &file_info.disk_num_start) != UNZ_OK) + err = UNZ_ERRNO; + } + } +#ifdef HAVE_AES + /* AES header */ + else if (extra_header_id == 0x9901) + { + uint8_t value8 = 0; + + /* Subtract size of AES field, since AES is handled internally */ + file_info.size_file_extra_internal += 2 + 2 + extra_data_size; + + /* Verify version info */ + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &value16) != UNZ_OK) + err = UNZ_ERRNO; + /* Support AE-1 and AE-2 */ + if (value16 != 1 && value16 != 2) + err = UNZ_ERRNO; + file_info_internal.aes_version = value16; + if (unzReadUInt8(&s->z_filefunc, s->filestream_with_CD, &value8) != UNZ_OK) + err = UNZ_ERRNO; + if ((char)value8 != 'A') + err = UNZ_ERRNO; + if (unzReadUInt8(&s->z_filefunc, s->filestream_with_CD, &value8) != UNZ_OK) + err = UNZ_ERRNO; + if ((char)value8 != 'E') + err = UNZ_ERRNO; + /* Get AES encryption strength and actual compression method */ + if (unzReadUInt8(&s->z_filefunc, s->filestream_with_CD, &value8) != UNZ_OK) + err = UNZ_ERRNO; + file_info_internal.aes_encryption_mode = value8; + if (unzReadUInt16(&s->z_filefunc, s->filestream_with_CD, &value16) != UNZ_OK) + err = UNZ_ERRNO; + file_info_internal.aes_compression_method = value16; + } +#endif + else + { + if (ZSEEK64(s->z_filefunc, s->filestream_with_CD, extra_data_size, ZLIB_FILEFUNC_SEEK_CUR) != 0) + err = UNZ_ERRNO; + } + + extra_pos += 2 + 2 + extra_data_size; + } + } + + if (file_info.disk_num_start == s->gi.number_disk_with_CD) + file_info_internal.byte_before_the_zipfile = s->byte_before_the_zipfile; + else + file_info_internal.byte_before_the_zipfile = 0; + + if (err == UNZ_OK) + err = unzGetCurrentFileInfoField(file, &seek, comment, comment_size, file_info.size_file_comment, 1); + + if ((err == UNZ_OK) && (pfile_info != NULL)) + *pfile_info = file_info; + if ((err == UNZ_OK) && (pfile_info_internal != NULL)) + *pfile_info_internal = file_info_internal; + + return err; +} + +extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, unz_file_info *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size) +{ + unz_file_info64 file_info64; + int err = UNZ_OK; + + err = unzGetCurrentFileInfoInternal(file, &file_info64, NULL, filename, filename_size, + extrafield, extrafield_size, comment, comment_size); + + if ((err == UNZ_OK) && (pfile_info != NULL)) + { + pfile_info->version = file_info64.version; + pfile_info->version_needed = file_info64.version_needed; + pfile_info->flag = file_info64.flag; + pfile_info->compression_method = file_info64.compression_method; + pfile_info->dos_date = file_info64.dos_date; + pfile_info->crc = file_info64.crc; + + pfile_info->size_filename = file_info64.size_filename; + pfile_info->size_file_extra = file_info64.size_file_extra - file_info64.size_file_extra_internal; + pfile_info->size_file_comment = file_info64.size_file_comment; + + pfile_info->disk_num_start = (uint16_t)file_info64.disk_num_start; + pfile_info->internal_fa = file_info64.internal_fa; + pfile_info->external_fa = file_info64.external_fa; + + pfile_info->compressed_size = (uint32_t)file_info64.compressed_size; + pfile_info->uncompressed_size = (uint32_t)file_info64.uncompressed_size; + } + return err; +} + +extern int ZEXPORT unzGetCurrentFileInfo64(unzFile file, unz_file_info64 * pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size) +{ + return unzGetCurrentFileInfoInternal(file, pfile_info, NULL, filename, filename_size, + extrafield, extrafield_size, comment, comment_size); +} + +/* Read the local header of the current zipfile. Check the coherency of the local header and info in the + end of central directory about this file store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) */ +static int unzCheckCurrentFileCoherencyHeader(unz64_internal *s, uint32_t *psize_variable, uint64_t *poffset_local_extrafield, + uint16_t *psize_local_extrafield) +{ + uint32_t magic = 0; + uint16_t value16 = 0; + uint32_t value32 = 0; + uint32_t flags = 0; + uint16_t size_filename = 0; + uint16_t size_extra_field = 0; + uint16_t compression_method = 0; + int err = UNZ_OK; + + if (psize_variable == NULL) + return UNZ_PARAMERROR; + *psize_variable = 0; + if (poffset_local_extrafield == NULL) + return UNZ_PARAMERROR; + *poffset_local_extrafield = 0; + if (psize_local_extrafield == NULL) + return UNZ_PARAMERROR; + *psize_local_extrafield = 0; + + err = unzGoToNextDisk((unzFile)s); + if (err != UNZ_OK) + return err; + + if (ZSEEK64(s->z_filefunc, s->filestream, s->cur_file_info_internal.offset_curfile + + s->cur_file_info_internal.byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + + if (err == UNZ_OK) + { + if (unzReadUInt32(&s->z_filefunc, s->filestream, &magic) != UNZ_OK) + err = UNZ_ERRNO; + else if (magic != LOCALHEADERMAGIC) + err = UNZ_BADZIPFILE; + } + + if (unzReadUInt16(&s->z_filefunc, s->filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + if (unzReadUInt16(&s->z_filefunc, s->filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + flags = value16; + if (unzReadUInt16(&s->z_filefunc, s->filestream, &value16) != UNZ_OK) + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (value16 != s->cur_file_info.compression_method)) + err = UNZ_BADZIPFILE; + + compression_method = s->cur_file_info.compression_method; +#ifdef HAVE_AES + if (compression_method == AES_METHOD) + compression_method = s->cur_file_info_internal.aes_compression_method; +#endif + + if ((err == UNZ_OK) && (compression_method != 0) && (compression_method != Z_DEFLATED)) + { +#ifdef HAVE_BZIP2 + if (compression_method != Z_BZIP2ED) +#endif + err = UNZ_BADZIPFILE; + } + + if (unzReadUInt32(&s->z_filefunc, s->filestream, &value32) != UNZ_OK) /* date/time */ + err = UNZ_ERRNO; + if (unzReadUInt32(&s->z_filefunc, s->filestream, &value32) != UNZ_OK) /* crc */ + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (value32 != s->cur_file_info.crc) && ((flags & 8) == 0)) + err = UNZ_BADZIPFILE; + if (unzReadUInt32(&s->z_filefunc, s->filestream, &value32) != UNZ_OK) /* size compr */ + err = UNZ_ERRNO; + else if ((value32 != UINT32_MAX) && (err == UNZ_OK) && (value32 != s->cur_file_info.compressed_size) && ((flags & 8) == 0)) + err = UNZ_BADZIPFILE; + if (unzReadUInt32(&s->z_filefunc, s->filestream, &value32) != UNZ_OK) /* size uncompr */ + err = UNZ_ERRNO; + else if ((value32 != UINT32_MAX) && (err == UNZ_OK) && (value32 != s->cur_file_info.uncompressed_size) && ((flags & 8) == 0)) + err = UNZ_BADZIPFILE; + if (unzReadUInt16(&s->z_filefunc, s->filestream, &size_filename) != UNZ_OK) + err = UNZ_ERRNO; + + *psize_variable += size_filename; + + if (unzReadUInt16(&s->z_filefunc, s->filestream, &size_extra_field) != UNZ_OK) + err = UNZ_ERRNO; + + *poffset_local_extrafield = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = size_extra_field; + *psize_variable += size_extra_field; + + return err; +} + +extern uint64_t ZEXPORT unzCountEntries(const unzFile file) +{ + if (file == NULL) + return 0; + + unz64_internal s = *(unz64_internal*)file; + + s.pos_in_central_dir = s.offset_central_dir; + s.num_file = 0; + while (UNZ_OK == unzGetCurrentFileInfoInternal(&s, + &s.cur_file_info, + &s.cur_file_info_internal, + NULL, 0, NULL, 0, NULL, 0)) + { + s.pos_in_central_dir += SIZECENTRALDIRITEM + + s.cur_file_info.size_filename + + s.cur_file_info.size_file_extra + + s.cur_file_info.size_file_comment; + s.num_file += 1; + } + return s.num_file; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3(unzFile file, int *method, int *level, int raw, const char *password) +{ + unz64_internal *s = NULL; + file_in_zip64_read_info_s *pfile_in_zip_read_info = NULL; + uint16_t compression_method = 0; + uint64_t offset_local_extrafield = 0; + uint16_t size_local_extrafield = 0; + uint32_t size_variable = 0; + int err = UNZ_OK; +#ifndef NOUNCRYPT + char source[12]; +#else + if (password != NULL) + return UNZ_PARAMERROR; +#endif + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzCheckCurrentFileCoherencyHeader(s, &size_variable, &offset_local_extrafield, &size_local_extrafield) != UNZ_OK) + return UNZ_BADZIPFILE; + + compression_method = s->cur_file_info.compression_method; +#ifdef HAVE_AES + if (compression_method == AES_METHOD) + { + compression_method = s->cur_file_info_internal.aes_compression_method; + if (password == NULL) + { + return UNZ_PARAMERROR; + } + } +#endif + + if (method != NULL) + *method = compression_method; + + if (level != NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((compression_method != 0) && (compression_method != Z_DEFLATED)) + { +#ifdef HAVE_BZIP2 + if (compression_method != Z_BZIP2ED) +#endif + { + return UNZ_BADZIPFILE; + } + } + + pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); + if (pfile_in_zip_read_info == NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer = (uint8_t*)ALLOC(UNZ_BUFSIZE); + if (pfile_in_zip_read_info->read_buffer == NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised = 0; + + pfile_in_zip_read_info->filestream = s->filestream; + pfile_in_zip_read_info->z_filefunc = s->z_filefunc; + + pfile_in_zip_read_info->raw = raw; + pfile_in_zip_read_info->crc32 = 0; + pfile_in_zip_read_info->crc32_expected = s->cur_file_info.crc; + pfile_in_zip_read_info->total_out_64 = 0; + pfile_in_zip_read_info->compression_method = compression_method; + + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield = 0; + + pfile_in_zip_read_info->rest_read_compressed = s->cur_file_info.compressed_size; + pfile_in_zip_read_info->rest_read_uncompressed = s->cur_file_info.uncompressed_size; + pfile_in_zip_read_info->byte_before_the_zipfile = 0; + + if (s->number_disk == s->gi.number_disk_with_CD) + pfile_in_zip_read_info->byte_before_the_zipfile = s->byte_before_the_zipfile; + + pfile_in_zip_read_info->pos_in_zipfile = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_variable; + + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)s; + pfile_in_zip_read_info->stream.total_out = 0; + pfile_in_zip_read_info->stream.total_in = 0; + pfile_in_zip_read_info->stream.next_in = NULL; + pfile_in_zip_read_info->stream.avail_in = 0; + + if (!raw) + { + if (compression_method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; + pfile_in_zip_read_info->bstream.bzfree = (free_func)0; + pfile_in_zip_read_info->bstream.opaque = (voidpf)0; + pfile_in_zip_read_info->bstream.state = (voidpf)0; + + err = BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); + if (err == Z_OK) + { + pfile_in_zip_read_info->stream_initialised = Z_BZIP2ED; + } + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } +#else + pfile_in_zip_read_info->raw = 1; +#endif + } + else if (compression_method == Z_DEFLATED) + { +#ifdef HAVE_APPLE_COMPRESSION + err = compression_stream_init(&pfile_in_zip_read_info->astream, COMPRESSION_STREAM_DECODE, COMPRESSION_ZLIB); + if (err == COMPRESSION_STATUS_ERROR) + err = UNZ_INTERNALERROR; + else + err = Z_OK; +#else + err = inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); +#endif + if (err == Z_OK) + { + pfile_in_zip_read_info->stream_initialised = Z_DEFLATED; + } + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + } + + s->pfile_in_zip_read = pfile_in_zip_read_info; + +#ifndef NOUNCRYPT + s->pcrc_32_tab = NULL; + + if ((password != NULL) && ((s->cur_file_info.flag & 1) != 0)) + { + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + s->pfile_in_zip_read->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_INTERNALERROR; +#ifdef HAVE_AES + if (s->cur_file_info.compression_method == AES_METHOD) + { + unsigned char passverify_archive[AES_PWVERIFYSIZE]; + unsigned char passverify_password[AES_PWVERIFYSIZE]; + unsigned char salt_value[AES_MAXSALTLENGTH]; + uint32_t salt_length = 0; + + if ((s->cur_file_info_internal.aes_encryption_mode < 1) || + (s->cur_file_info_internal.aes_encryption_mode > 3)) + return UNZ_INTERNALERROR; + + salt_length = SALT_LENGTH(s->cur_file_info_internal.aes_encryption_mode); + + if (ZREAD64(s->z_filefunc, s->filestream, salt_value, salt_length) != salt_length) + return UNZ_INTERNALERROR; + if (ZREAD64(s->z_filefunc, s->filestream, passverify_archive, AES_PWVERIFYSIZE) != AES_PWVERIFYSIZE) + return UNZ_INTERNALERROR; + + fcrypt_init(s->cur_file_info_internal.aes_encryption_mode, (uint8_t *)password, + (uint32_t)strlen(password), salt_value, passverify_password, &s->pfile_in_zip_read->aes_ctx); + + if (memcmp(passverify_archive, passverify_password, AES_PWVERIFYSIZE) != 0) + return UNZ_BADPASSWORD; + + s->pfile_in_zip_read->rest_read_compressed -= salt_length + AES_PWVERIFYSIZE; + s->pfile_in_zip_read->rest_read_compressed -= AES_AUTHCODESIZE; + + s->pfile_in_zip_read->pos_in_zipfile += salt_length + AES_PWVERIFYSIZE; + } + else +#endif + { + int i; + s->pcrc_32_tab = (const z_crc_t*)get_crc_table(); + init_keys(password, s->keys, s->pcrc_32_tab); + + if (ZREAD64(s->z_filefunc, s->filestream, source, 12) < 12) + return UNZ_INTERNALERROR; + + for (i = 0; i < 12; i++) + zdecode(s->keys, s->pcrc_32_tab, source[i]); + + s->pfile_in_zip_read->rest_read_compressed -= 12; + s->pfile_in_zip_read->pos_in_zipfile += 12; + } + } +#endif + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile(unzFile file) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword(unzFile file, const char *password) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2(unzFile file, int *method, int *level, int raw) +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/* Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if some bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ +extern int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, uint32_t len) +{ + unz64_internal *s = NULL; + uint32_t read = 0; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (s->pfile_in_zip_read == NULL) + return UNZ_PARAMERROR; + if (s->pfile_in_zip_read->read_buffer == NULL) + return UNZ_END_OF_LIST_OF_FILE; + if (len == 0) + return 0; + if (len > UINT16_MAX) + return UNZ_PARAMERROR; + + s->pfile_in_zip_read->stream.next_out = (uint8_t*)buf; + s->pfile_in_zip_read->stream.avail_out = (uint16_t)len; + + if ((s->pfile_in_zip_read->compression_method == 0) || (s->pfile_in_zip_read->raw)) + { + if (len > s->pfile_in_zip_read->rest_read_compressed + s->pfile_in_zip_read->stream.avail_in) + s->pfile_in_zip_read->stream.avail_out = (uint16_t)s->pfile_in_zip_read->rest_read_compressed + + s->pfile_in_zip_read->stream.avail_in; + } + + do + { + if (s->pfile_in_zip_read->stream.avail_in == 0) + { + uint32_t bytes_to_read = UNZ_BUFSIZE; + uint32_t bytes_not_read = 0; + uint32_t bytes_read = 0; + uint32_t total_bytes_read = 0; + + if (s->pfile_in_zip_read->stream.next_in != NULL) + bytes_not_read = (uint32_t)(s->pfile_in_zip_read->read_buffer + UNZ_BUFSIZE - + s->pfile_in_zip_read->stream.next_in); + bytes_to_read -= bytes_not_read; + if (bytes_not_read > 0) + memcpy(s->pfile_in_zip_read->read_buffer, s->pfile_in_zip_read->stream.next_in, bytes_not_read); + if (s->pfile_in_zip_read->rest_read_compressed < bytes_to_read) + bytes_to_read = (uint16_t)s->pfile_in_zip_read->rest_read_compressed; + + while (total_bytes_read != bytes_to_read) + { + if (ZSEEK64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream, + s->pfile_in_zip_read->pos_in_zipfile + s->pfile_in_zip_read->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + + bytes_read = ZREAD64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream, + s->pfile_in_zip_read->read_buffer + bytes_not_read + total_bytes_read, + bytes_to_read - total_bytes_read); + + total_bytes_read += bytes_read; + s->pfile_in_zip_read->pos_in_zipfile += bytes_read; + + if (bytes_read == 0) + { + if (ZERROR64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream)) + return UNZ_ERRNO; + + err = unzGoToNextDisk(file); + if (err != UNZ_OK) + return err; + + s->pfile_in_zip_read->pos_in_zipfile = 0; + s->pfile_in_zip_read->filestream = s->filestream; + } + } + +#ifndef NOUNCRYPT + if ((s->cur_file_info.flag & 1) != 0) + { +#ifdef HAVE_AES + if (s->cur_file_info.compression_method == AES_METHOD) + { + fcrypt_decrypt(s->pfile_in_zip_read->read_buffer, bytes_to_read, &s->pfile_in_zip_read->aes_ctx); + } + else +#endif + if (s->pcrc_32_tab != NULL) + { + uint32_t i = 0; + + for (i = 0; i < total_bytes_read; i++) + s->pfile_in_zip_read->read_buffer[i] = + zdecode(s->keys, s->pcrc_32_tab, s->pfile_in_zip_read->read_buffer[i]); + } + } +#endif + + s->pfile_in_zip_read->rest_read_compressed -= total_bytes_read; + s->pfile_in_zip_read->stream.next_in = (uint8_t*)s->pfile_in_zip_read->read_buffer; + s->pfile_in_zip_read->stream.avail_in = (uint16_t)(bytes_not_read + total_bytes_read); + } + + if ((s->pfile_in_zip_read->compression_method == 0) || (s->pfile_in_zip_read->raw)) + { + uint32_t i = 0; + uint32_t copy = 0; + + if ((s->pfile_in_zip_read->stream.avail_in == 0) && + (s->pfile_in_zip_read->rest_read_compressed == 0)) + return (read == 0) ? UNZ_EOF : read; + + if (s->pfile_in_zip_read->stream.avail_out < s->pfile_in_zip_read->stream.avail_in) + copy = s->pfile_in_zip_read->stream.avail_out; + else + copy = s->pfile_in_zip_read->stream.avail_in; + + for (i = 0; i < copy; i++) + *(s->pfile_in_zip_read->stream.next_out + i) = + *(s->pfile_in_zip_read->stream.next_in + i); + + s->pfile_in_zip_read->total_out_64 = s->pfile_in_zip_read->total_out_64 + copy; + s->pfile_in_zip_read->rest_read_uncompressed -= copy; + s->pfile_in_zip_read->crc32 = (uint32_t)crc32(s->pfile_in_zip_read->crc32, + s->pfile_in_zip_read->stream.next_out, copy); + + s->pfile_in_zip_read->stream.avail_in -= copy; + s->pfile_in_zip_read->stream.avail_out -= copy; + s->pfile_in_zip_read->stream.next_out += copy; + s->pfile_in_zip_read->stream.next_in += copy; + s->pfile_in_zip_read->stream.total_out += copy; + + read += copy; + } + else if (s->pfile_in_zip_read->compression_method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + uint64_t total_out_before = 0; + uint64_t total_out_after = 0; + uint64_t out_bytes = 0; + const uint8_t *buf_before = NULL; + + s->pfile_in_zip_read->bstream.next_in = (char*)s->pfile_in_zip_read->stream.next_in; + s->pfile_in_zip_read->bstream.avail_in = s->pfile_in_zip_read->stream.avail_in; + s->pfile_in_zip_read->bstream.total_in_lo32 = (uint32_t)s->pfile_in_zip_read->stream.total_in; + s->pfile_in_zip_read->bstream.total_in_hi32 = s->pfile_in_zip_read->stream.total_in >> 32; + + s->pfile_in_zip_read->bstream.next_out = (char*)s->pfile_in_zip_read->stream.next_out; + s->pfile_in_zip_read->bstream.avail_out = s->pfile_in_zip_read->stream.avail_out; + s->pfile_in_zip_read->bstream.total_out_lo32 = (uint32_t)s->pfile_in_zip_read->stream.total_out; + s->pfile_in_zip_read->bstream.total_out_hi32 = s->pfile_in_zip_read->stream.total_out >> 32; + + total_out_before = s->pfile_in_zip_read->bstream.total_out_lo32 + + (((uint32_t)s->pfile_in_zip_read->bstream.total_out_hi32) << 32); + buf_before = (const uint8_t*)s->pfile_in_zip_read->bstream.next_out; + + err = BZ2_bzDecompress(&s->pfile_in_zip_read->bstream); + + total_out_after = s->pfile_in_zip_read->bstream.total_out_lo32 + + (((uint32_t)s->pfile_in_zip_read->bstream.total_out_hi32) << 32); + + out_bytes = total_out_after - total_out_before; + + s->pfile_in_zip_read->total_out_64 = s->pfile_in_zip_read->total_out_64 + out_bytes; + s->pfile_in_zip_read->rest_read_uncompressed -= out_bytes; + s->pfile_in_zip_read->crc32 = crc32(s->pfile_in_zip_read->crc32, buf_before, (uint32_t)out_bytes); + + read += (uint32_t)out_bytes; + + s->pfile_in_zip_read->stream.next_in = (uint8_t*)s->pfile_in_zip_read->bstream.next_in; + s->pfile_in_zip_read->stream.avail_in = s->pfile_in_zip_read->bstream.avail_in; + s->pfile_in_zip_read->stream.total_in = s->pfile_in_zip_read->bstream.total_in_lo32; + s->pfile_in_zip_read->stream.next_out = (uint8_t*)s->pfile_in_zip_read->bstream.next_out; + s->pfile_in_zip_read->stream.avail_out = s->pfile_in_zip_read->bstream.avail_out; + s->pfile_in_zip_read->stream.total_out = s->pfile_in_zip_read->bstream.total_out_lo32; + + if (err == BZ_STREAM_END) + return (read == 0) ? UNZ_EOF : read; + if (err != BZ_OK) + break; +#endif + } +#ifdef HAVE_APPLE_COMPRESSION + else + { + uint64_t total_out_before = 0; + uint64_t total_out_after = 0; + uint64_t out_bytes = 0; + const uint8_t *buf_before = NULL; + + s->pfile_in_zip_read->astream.src_ptr = s->pfile_in_zip_read->stream.next_in; + s->pfile_in_zip_read->astream.src_size = s->pfile_in_zip_read->stream.avail_in; + s->pfile_in_zip_read->astream.dst_ptr = s->pfile_in_zip_read->stream.next_out; + s->pfile_in_zip_read->astream.dst_size = len; + + total_out_before = s->pfile_in_zip_read->stream.total_out; + buf_before = s->pfile_in_zip_read->stream.next_out; + + compression_status status; + compression_stream_flags flags; + + if (s->pfile_in_zip_read->stream.avail_in == 0) + { + flags = COMPRESSION_STREAM_FINALIZE; + } + + status = compression_stream_process(&s->pfile_in_zip_read->astream, flags); + + total_out_after = len - s->pfile_in_zip_read->astream.dst_size; + out_bytes = total_out_after - total_out_before; + + s->pfile_in_zip_read->total_out_64 += out_bytes; + s->pfile_in_zip_read->rest_read_uncompressed -= out_bytes; + s->pfile_in_zip_read->crc32 = + crc32(s->pfile_in_zip_read->crc32, buf_before, (uint32_t)out_bytes); + + read += (uint32_t)out_bytes; + + s->pfile_in_zip_read->stream.next_in = s->pfile_in_zip_read->astream.src_ptr; + s->pfile_in_zip_read->stream.avail_in = s->pfile_in_zip_read->astream.src_size; + s->pfile_in_zip_read->stream.next_out = s->pfile_in_zip_read->astream.dst_ptr; + s->pfile_in_zip_read->stream.avail_out = s->pfile_in_zip_read->astream.dst_size; + + if (status == COMPRESSION_STATUS_END) + return (read == 0) ? UNZ_EOF : read; + if (status == COMPRESSION_STATUS_ERROR) + return Z_DATA_ERROR; + return read; + } +#else + else + { + uint64_t total_out_before = 0; + uint64_t total_out_after = 0; + uint64_t out_bytes = 0; + const uint8_t *buf_before = NULL; + int flush = Z_SYNC_FLUSH; + + total_out_before = s->pfile_in_zip_read->stream.total_out; + buf_before = s->pfile_in_zip_read->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err = inflate(&s->pfile_in_zip_read->stream, flush); + + if ((err >= 0) && (s->pfile_in_zip_read->stream.msg != NULL)) + err = Z_DATA_ERROR; + + total_out_after = s->pfile_in_zip_read->stream.total_out; + out_bytes = total_out_after - total_out_before; + + s->pfile_in_zip_read->total_out_64 += out_bytes; + s->pfile_in_zip_read->rest_read_uncompressed -= out_bytes; + s->pfile_in_zip_read->crc32 = + (uint32_t)crc32(s->pfile_in_zip_read->crc32, buf_before, (uint32_t)out_bytes); + + read += (uint32_t)out_bytes; + + if (err == Z_STREAM_END) + return (read == 0) ? UNZ_EOF : read; + if (err != Z_OK) + break; + } +#endif + } + while (s->pfile_in_zip_read->stream.avail_out > 0); + + if (err == Z_OK) + return read; + return err; +} + +extern int ZEXPORT unzGetLocalExtrafield(unzFile file, voidp buf, uint32_t len) +{ + unz64_internal *s = NULL; + uint64_t size_to_read = 0; + uint32_t read_now = 0; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (s->pfile_in_zip_read == NULL) + return UNZ_PARAMERROR; + + size_to_read = s->pfile_in_zip_read->size_local_extrafield - s->pfile_in_zip_read->pos_local_extrafield; + + if (buf == NULL) + return (int)size_to_read; + + if (len > size_to_read) + read_now = (uint32_t)size_to_read; + else + read_now = len; + + if (read_now == 0) + return 0; + + if (ZSEEK64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream, + s->pfile_in_zip_read->offset_local_extrafield + s->pfile_in_zip_read->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET) != 0) + return UNZ_ERRNO; + + if (ZREAD64(s->pfile_in_zip_read->z_filefunc, s->pfile_in_zip_read->filestream, buf, read_now) != read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +extern int ZEXPORT unzCloseCurrentFile(unzFile file) +{ + unz64_internal *s = NULL; + file_in_zip64_read_info_s *pfile_in_zip_read_info = NULL; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + pfile_in_zip_read_info = s->pfile_in_zip_read; + if (pfile_in_zip_read_info == NULL) + return UNZ_PARAMERROR; + +#ifdef HAVE_AES + if (s->cur_file_info.compression_method == AES_METHOD) + { + unsigned char authcode[AES_AUTHCODESIZE]; + unsigned char rauthcode[AES_AUTHCODESIZE]; + + if (ZREAD64(s->z_filefunc, s->filestream, authcode, AES_AUTHCODESIZE) != AES_AUTHCODESIZE) + return UNZ_ERRNO; + + if (fcrypt_end(rauthcode, &s->pfile_in_zip_read->aes_ctx) != AES_AUTHCODESIZE) + err = UNZ_CRCERROR; + if (memcmp(authcode, rauthcode, AES_AUTHCODESIZE) != 0) + err = UNZ_CRCERROR; + } + /* AES zip version AE-1 will expect a valid crc as well */ + if ((s->cur_file_info.compression_method != AES_METHOD) || + (s->cur_file_info_internal.aes_version == 0x0001)) +#endif + { + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_expected) + err = UNZ_CRCERROR; + } + } + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + { +#ifdef HAVE_APPLE_COMPRESSION + if (compression_stream_destroy) + compression_stream_destroy(&pfile_in_zip_read_info->astream); +#else + inflateEnd(&pfile_in_zip_read_info->stream); +#endif + + } +#ifdef HAVE_BZIP2 + else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) + BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); +#endif + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read = NULL; + + return err; +} + +extern int ZEXPORT unzGoToFirstFile2(unzFile file, unz_file_info64 *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size) +{ + unz64_internal *s = NULL; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (s->gi.number_entry == 0) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir = s->offset_central_dir; + s->num_file = 0; + + err = unzGetCurrentFileInfoInternal(file, &s->cur_file_info, &s->cur_file_info_internal, + filename, filename_size, extrafield, extrafield_size, comment, comment_size); + + s->current_file_ok = (err == UNZ_OK); + if ((err == UNZ_OK) && (pfile_info != NULL)) + memcpy(pfile_info, &s->cur_file_info, sizeof(unz_file_info64)); + + return err; +} + +extern int ZEXPORT unzGoToFirstFile(unzFile file) +{ + return unzGoToFirstFile2(file, NULL, NULL, 0, NULL, 0, NULL, 0); +} + +extern int ZEXPORT unzGoToNextFile2(unzFile file, unz_file_info64 *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size) +{ + unz64_internal *s = NULL; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != UINT16_MAX) /* 2^16 files overflow hack */ + { + if (s->num_file+1 == s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + } + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment; + s->num_file += 1; + + err = unzGetCurrentFileInfoInternal(file, &s->cur_file_info, &s->cur_file_info_internal, + filename, filename_size, extrafield, extrafield_size, comment, comment_size); + + s->current_file_ok = (err == UNZ_OK); + if ((err == UNZ_OK) && (pfile_info != NULL)) + memcpy(pfile_info, &s->cur_file_info, sizeof(unz_file_info64)); + + return err; +} + +extern int ZEXPORT unzGoToNextFile(unzFile file) +{ + return unzGoToNextFile2(file, NULL, NULL, 0, NULL, 0, NULL, 0); +} + +extern int ZEXPORT unzLocateFile(unzFile file, const char *filename, unzFileNameComparer filename_compare_func) +{ + unz64_internal *s = NULL; + unz_file_info64 cur_file_info_saved; + unz_file_info64_internal cur_file_info_internal_saved; + uint64_t num_file_saved = 0; + uint64_t pos_in_central_dir_saved = 0; + char current_filename[UNZ_MAXFILENAMEINZIP+1]; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + if (strlen(filename) >= UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_file_saved = s->num_file; + pos_in_central_dir_saved = s->pos_in_central_dir; + cur_file_info_saved = s->cur_file_info; + cur_file_info_internal_saved = s->cur_file_info_internal; + + err = unzGoToFirstFile2(file, NULL, current_filename, sizeof(current_filename)-1, NULL, 0, NULL, 0); + + while (err == UNZ_OK) + { + if (filename_compare_func != NULL) + err = filename_compare_func(file, current_filename, filename); + else + err = strcmp(current_filename, filename); + if (err == 0) + return UNZ_OK; + err = unzGoToNextFile2(file, NULL, current_filename, sizeof(current_filename)-1, NULL, 0, NULL, 0); + } + + /* We failed, so restore the state of the 'current file' to where we were. */ + s->num_file = num_file_saved; + s->pos_in_central_dir = pos_in_central_dir_saved; + s->cur_file_info = cur_file_info_saved; + s->cur_file_info_internal = cur_file_info_internal_saved; + return err; +} + +extern int ZEXPORT unzGetFilePos(unzFile file, unz_file_pos *file_pos) +{ + unz64_file_pos file_pos64; + int err = unzGetFilePos64(file, &file_pos64); + if (err == UNZ_OK) + { + file_pos->pos_in_zip_directory = (uint32_t)file_pos64.pos_in_zip_directory; + file_pos->num_of_file = (uint32_t)file_pos64.num_of_file; + } + return err; +} + +extern int ZEXPORT unzGoToFilePos(unzFile file, unz_file_pos *file_pos) +{ + unz64_file_pos file_pos64; + if (file_pos == NULL) + return UNZ_PARAMERROR; + file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; + file_pos64.num_of_file = file_pos->num_of_file; + return unzGoToFilePos64(file, &file_pos64); +} + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos *file_pos) +{ + unz64_internal *s = NULL; + + if (file == NULL || file_pos == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + return UNZ_OK; +} + +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos *file_pos) +{ + unz64_internal *s = NULL; + int err = UNZ_OK; + + if (file == NULL || file_pos == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + /* Jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* Set the current file */ + err = unzGetCurrentFileInfoInternal(file, &s->cur_file_info, &s->cur_file_info_internal, NULL, 0, NULL, 0, NULL, 0); + /* Return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int32_t ZEXPORT unzGetOffset(unzFile file) +{ + uint64_t offset64 = 0; + + if (file == NULL) + return UNZ_PARAMERROR; + offset64 = unzGetOffset64(file); + return (int32_t)offset64; +} + +extern int64_t ZEXPORT unzGetOffset64(unzFile file) +{ + unz64_internal *s = NULL; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != UINT16_MAX) + { + if (s->num_file == s->gi.number_entry) + return 0; + } + return s->pos_in_central_dir; +} + +extern int ZEXPORT unzSetOffset(unzFile file, uint32_t pos) +{ + return unzSetOffset64(file, pos); +} + +extern int ZEXPORT unzSetOffset64(unzFile file, uint64_t pos) +{ + unz64_internal *s = NULL; + int err = UNZ_OK; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + + err = unzGetCurrentFileInfoInternal(file, &s->cur_file_info, &s->cur_file_info_internal, NULL, 0, NULL, 0, NULL, 0); + + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int32_t ZEXPORT unzTell(unzFile file) +{ + unz64_internal *s = NULL; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (s->pfile_in_zip_read == NULL) + return UNZ_PARAMERROR; + return (int32_t)s->pfile_in_zip_read->stream.total_out; +} + +extern int64_t ZEXPORT unzTell64(unzFile file) +{ + unz64_internal *s = NULL; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (s->pfile_in_zip_read == NULL) + return UNZ_PARAMERROR; + return s->pfile_in_zip_read->total_out_64; +} + +extern int ZEXPORT unzSeek(unzFile file, uint32_t offset, int origin) +{ + return unzSeek64(file, offset, origin); +} + +extern int ZEXPORT unzSeek64(unzFile file, uint64_t offset, int origin) +{ + unz64_internal *s = NULL; + uint64_t stream_pos_begin = 0; + uint64_t stream_pos_end = 0; + uint64_t position = 0; + int is_within_buffer = 0; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + + if (s->pfile_in_zip_read == NULL) + return UNZ_ERRNO; + if (s->pfile_in_zip_read->compression_method != 0) + return UNZ_ERRNO; + + if (origin == SEEK_SET) + position = offset; + else if (origin == SEEK_CUR) + position = s->pfile_in_zip_read->total_out_64 + offset; + else if (origin == SEEK_END) + position = s->cur_file_info.compressed_size + offset; + else + return UNZ_PARAMERROR; + + if (position > s->cur_file_info.compressed_size) + return UNZ_PARAMERROR; + + stream_pos_end = s->pfile_in_zip_read->pos_in_zipfile; + stream_pos_begin = stream_pos_end; + + if (stream_pos_begin > UNZ_BUFSIZE) + stream_pos_begin -= UNZ_BUFSIZE; + else + stream_pos_begin = 0; + + is_within_buffer = + (s->pfile_in_zip_read->stream.avail_in != 0) && + (s->pfile_in_zip_read->rest_read_compressed != 0 || s->cur_file_info.compressed_size < UNZ_BUFSIZE) && + (position >= stream_pos_begin && position < stream_pos_end); + + if (is_within_buffer) + { + s->pfile_in_zip_read->stream.next_in += position - s->pfile_in_zip_read->total_out_64; + s->pfile_in_zip_read->stream.avail_in = (uInt)(stream_pos_end - position); + } + else + { + s->pfile_in_zip_read->stream.avail_in = 0; + s->pfile_in_zip_read->stream.next_in = 0; + + s->pfile_in_zip_read->pos_in_zipfile = s->pfile_in_zip_read->offset_local_extrafield + position; + s->pfile_in_zip_read->rest_read_compressed = s->cur_file_info.compressed_size - position; + } + + s->pfile_in_zip_read->rest_read_uncompressed -= (position - s->pfile_in_zip_read->total_out_64); + s->pfile_in_zip_read->stream.total_out = (uint32_t)position; + s->pfile_in_zip_read->total_out_64 = position; + + return UNZ_OK; +} + +extern int ZEXPORT unzEndOfFile(unzFile file) +{ + unz64_internal *s = NULL; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz64_internal*)file; + if (s->pfile_in_zip_read == NULL) + return UNZ_PARAMERROR; + if (s->pfile_in_zip_read->rest_read_uncompressed == 0) + return 1; + return 0; +} diff --git a/SSZipArchive/minizip/unzip.h b/SSZipArchive/minizip/unzip.h new file mode 100644 index 0000000..059a026 --- /dev/null +++ b/SSZipArchive/minizip/unzip.h @@ -0,0 +1,254 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.2.0, September 16th, 2017 + part of the MiniZip project + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + Copyright (C) 2009-2010 Mathias Svensson + Modifications for Zip64 support on both zip and unzip + http://result42.com + Copyright (C) 2007-2008 Even Rouault + Modifications of Unzip for Zip64 + Copyright (C) 1998-2010 Gilles Vollant + http://www.winimage.com/zLibDll/minizip.html + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#ifndef _UNZ_H +#define _UNZ_H + +#include "../SSZipCommon.h" + +#define HAVE_AES + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unz_file__; +typedef unz_file__ *unzFile; +#else +typedef voidp unzFile; +#endif + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) +#define UNZ_BADPASSWORD (-106) + + +/***************************************************************************/ +/* Opening and close a zip file */ + +extern unzFile ZEXPORT unzOpen(const char *path); +extern unzFile ZEXPORT unzOpen64(const void *path); +/* Open a Zip file. + + path should contain the full path (by example, on a Windows XP computer + "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". + return NULL if zipfile cannot be opened or doesn't exist + return unzFile handle if no error + + NOTE: The "64" function take a const void *pointer, because the path is just the value passed to the + open64_file_func callback. Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char *does not describe the reality */ + +extern unzFile ZEXPORT unzOpen2(const char *path, zlib_filefunc_def *pzlib_filefunc_def); +/* Open a Zip file, like unzOpen, but provide a set of file low level API for read/write operations */ +extern unzFile ZEXPORT unzOpen2_64(const void *path, zlib_filefunc64_def *pzlib_filefunc_def); +/* Open a Zip file, like unz64Open, but provide a set of file low level API for read/write 64-bit operations */ + +extern int ZEXPORT unzClose(unzFile file); +/* Close a ZipFile opened with unzOpen. If there is files inside the .Zip opened with unzOpenCurrentFile, + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + + return UNZ_OK if there is no error */ + +extern int ZEXPORT unzGetGlobalInfo(unzFile file, unz_global_info *pglobal_info); +extern int ZEXPORT unzGetGlobalInfo64(unzFile file, unz_global_info64 *pglobal_info); +/* Write info about the ZipFile in the *pglobal_info structure. + + return UNZ_OK if no error */ + +extern int ZEXPORT unzGetGlobalComment(unzFile file, char *comment, uint16_t comment_size); +/* Get the global comment string of the ZipFile, in the comment buffer. + + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 */ + +extern uint64_t ZEXPORT unzCountEntries(const unzFile file); + +/***************************************************************************/ +/* Reading the content of the current zipfile, you can open it, read data from it, and close it + (you can close it before reading all the file) */ + +extern int ZEXPORT unzOpenCurrentFile(unzFile file); +/* Open for reading data the current file in the zipfile. + + return UNZ_OK if no error */ + +extern int ZEXPORT unzOpenCurrentFilePassword(unzFile file, const char *password); +/* Open for reading data the current file in the zipfile. + password is a crypting password + + return UNZ_OK if no error */ + +extern int ZEXPORT unzOpenCurrentFile2(unzFile file, int *method, int *level, int raw); +/* Same as unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 *method will receive method of compression, *level will receive level of compression + + NOTE: you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL */ + +extern int ZEXPORT unzOpenCurrentFile3(unzFile file, int *method, int *level, int raw, const char *password); +/* Same as unzOpenCurrentFile, but takes extra parameter password for encrypted files */ + +extern int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, uint32_t len); +/* Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ + +extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, unz_file_info *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size); +extern int ZEXPORT unzGetCurrentFileInfo64(unzFile file, unz_file_info64 *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size); +/* Get Info about the current file + + pfile_info if != NULL, the *pfile_info structure will contain somes info about the current file + filename if != NULL, the file name string will be copied in filename + filename_size is the size of the filename buffer + extrafield if != NULL, the extra field information from the central header will be copied in to + extrafield_size is the size of the extraField buffer + comment if != NULL, the comment string of the file will be copied in to + comment_size is the size of the comment buffer */ + +extern int ZEXPORT unzGetLocalExtrafield(unzFile file, voidp buf, uint32_t len); +/* Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf == NULL, it return the size of the local extra field + if buf != NULL, len is the size of the buffer, the extra header is copied in buf. + + return number of bytes copied in buf, or (if <0) the error code */ + +extern int ZEXPORT unzCloseCurrentFile(unzFile file); +/* Close the file in zip opened with unzOpenCurrentFile + + return UNZ_CRCERROR if all the file was read but the CRC is not good */ + +/***************************************************************************/ +/* Browse the directory of the zipfile */ + +typedef int (*unzFileNameComparer)(unzFile file, const char *filename1, const char *filename2); +typedef int (*unzIteratorFunction)(unzFile file); +typedef int (*unzIteratorFunction2)(unzFile file, unz_file_info64 *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size); + +extern int ZEXPORT unzGoToFirstFile(unzFile file); +/* Set the current file of the zipfile to the first file. + + return UNZ_OK if no error */ + +extern int ZEXPORT unzGoToFirstFile2(unzFile file, unz_file_info64 *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size); +/* Set the current file of the zipfile to the first file and retrieves the current info on success. + Not as seek intensive as unzGoToFirstFile + unzGetCurrentFileInfo. + + return UNZ_OK if no error */ + +extern int ZEXPORT unzGoToNextFile(unzFile file); +/* Set the current file of the zipfile to the next file. + + return UNZ_OK if no error + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest */ + +extern int ZEXPORT unzGoToNextFile2(unzFile file, unz_file_info64 *pfile_info, char *filename, + uint16_t filename_size, void *extrafield, uint16_t extrafield_size, char *comment, uint16_t comment_size); +/* Set the current file of the zipfile to the next file and retrieves the current + info on success. Does less seeking around than unzGotoNextFile + unzGetCurrentFileInfo. + + return UNZ_OK if no error + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest */ + +extern int ZEXPORT unzLocateFile(unzFile file, const char *filename, unzFileNameComparer filename_compare_func); +/* Try locate the file szFileName in the zipfile. For custom filename comparison pass in comparison function. + + return UNZ_OK if the file is found (it becomes the current file) + return UNZ_END_OF_LIST_OF_FILE if the file is not found */ + +/***************************************************************************/ +/* Raw access to zip file */ + +typedef struct unz_file_pos_s +{ + uint32_t pos_in_zip_directory; /* offset in zip file directory */ + uint32_t num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos(unzFile file, unz_file_pos *file_pos); +extern int ZEXPORT unzGoToFilePos(unzFile file, unz_file_pos *file_pos); + +typedef struct unz64_file_pos_s +{ + uint64_t pos_in_zip_directory; /* offset in zip file directory */ + uint64_t num_of_file; /* # of file */ +} unz64_file_pos; + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos *file_pos); +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos *file_pos); + +extern int32_t ZEXPORT unzGetOffset(unzFile file); +extern int64_t ZEXPORT unzGetOffset64(unzFile file); +/* Get the current file offset */ + +extern int ZEXPORT unzSetOffset(unzFile file, uint32_t pos); +extern int ZEXPORT unzSetOffset64(unzFile file, uint64_t pos); +/* Set the current file offset */ + +extern int32_t ZEXPORT unzTell(unzFile file); +extern int64_t ZEXPORT unzTell64(unzFile file); +/* return current position in uncompressed data */ + +extern int ZEXPORT unzSeek(unzFile file, uint32_t offset, int origin); +extern int ZEXPORT unzSeek64(unzFile file, uint64_t offset, int origin); +/* Seek within the uncompressed data if compression method is storage */ + +extern int ZEXPORT unzEndOfFile(unzFile file); +/* return 1 if the end of file was reached, 0 elsewhere */ + +/***************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* _UNZ_H */ diff --git a/SSZipArchive/minizip/zip.c b/SSZipArchive/minizip/zip.c new file mode 100644 index 0000000..f4ff103 --- /dev/null +++ b/SSZipArchive/minizip/zip.c @@ -0,0 +1,2009 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.2.0, September 16th, 2017 + part of the MiniZip project + + Copyright (C) 2010-2017 Nathan Moinvaziri + Modifications for AES, PKWARE disk spanning + https://github.com/nmoinvaz/minizip + Copyright (C) 2009-2010 Mathias Svensson + Modifications for Zip64 support + http://result42.com + Copyright (C) 1998-2010 Gilles Vollant + http://www.winimage.com/zLibDll/minizip.html + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#include +#include +#include +#include +#include +#include + +#include "zlib.h" +#include "zip.h" + +#ifdef HAVE_AES +# define AES_METHOD (99) +# define AES_PWVERIFYSIZE (2) +# define AES_AUTHCODESIZE (10) +# define AES_MAXSALTLENGTH (16) +# define AES_VERSION (0x0001) +# define AES_ENCRYPTIONMODE (0x03) + +# include "aes/aes.h" +# include "aes/fileenc.h" +# include "aes/prng.h" +#endif +#ifdef HAVE_APPLE_COMPRESSION +# include +#endif + +#ifndef NOCRYPT +# include "crypt.h" +#endif + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define DISKHEADERMAGIC (0x08074b50) +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) +#define ZIP64ENDHEADERMAGIC (0x06064b50) +#define ZIP64ENDLOCHEADERMAGIC (0x07064b50) +#define DATADESCRIPTORMAGIC (0x08074b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ +#define SIZECENTRALHEADERLOCATOR (0x14) /* 20 */ +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + +#ifndef BUFREADCOMMENT +# define BUFREADCOMMENT (0x400) +#endif +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform dependent */ +#endif + +#ifndef Z_BUFSIZE +# define Z_BUFSIZE (UINT16_MAX) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* NOT sure that this work on ALL platform */ +#define MAKEULONG64(a, b) ((uint64_t)(((unsigned long)(a)) | ((uint64_t)((unsigned long)(b))) << 32)) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif + +const char zip_copyright[] = " zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s *next_datablock; + uint32_t avail_in_this_block; + uint32_t filled_in_this_block; + uint32_t unused; /* for future use and alignment */ + uint8_t data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal *first_block; + linkedlist_datablock_internal *last_block; +} linkedlist_data; + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif +#ifdef HAVE_APPLE_COMPRESSION + compression_stream astream; /* libcompression stream structure */ +#endif +#ifdef HAVE_AES + fcrypt_ctx aes_ctx; + prng_ctx aes_rng[1]; +#endif + int stream_initialised; /* 1 is stream is initialized */ + uint32_t pos_in_buffered_data; /* last written byte in buffered_data */ + + uint64_t pos_local_header; /* offset of the local header of the file currently writing */ + char *central_header; /* central header data for the current file */ + uint16_t size_centralextra; + uint16_t size_centralheader; /* size of the central header for cur file */ + uint16_t size_centralextrafree; /* Extra bytes allocated to the central header but that are not used */ + uint16_t size_comment; + uint16_t flag; /* flag of the file currently writing */ + + uint16_t method; /* compression method written to file.*/ + uint16_t compression_method; /* compression method to use */ + int raw; /* 1 for directly writing raw data */ + uint8_t buffered_data[Z_BUFSIZE]; /* buffer contain compressed data to be writ*/ + uint32_t dos_date; + uint32_t crc32; + int zip64; /* add ZIP64 extended information in the extra field */ + uint32_t number_disk; /* number of current disk used for spanning ZIP */ + uint64_t total_compressed; + uint64_t total_uncompressed; +#ifndef NOCRYPT + uint32_t keys[3]; /* keys defining the pseudo-random sequence */ + const z_crc_t *pcrc_32_tab; +#endif +} curfile64_info; + +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structure of the zipfile */ + voidpf filestream_with_CD; /* io structure of the zipfile with the central dir */ + linkedlist_data central_dir; /* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + int append; /* append mode */ + curfile64_info ci; /* info on the file currently writing */ + + uint64_t add_position_when_writting_offset; + uint64_t number_entry; + uint64_t disk_size; /* size of each disk */ + uint32_t number_disk; /* number of the current disk, used for spanning ZIP */ + uint32_t number_disk_with_CD; /* number the the disk with central dir, used for spanning ZIP */ +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif +} zip64_internal; + +/* Allocate a new data block */ +static linkedlist_datablock_internal *allocate_new_datablock(void) +{ + linkedlist_datablock_internal *ldi = NULL; + + ldi = (linkedlist_datablock_internal*)ALLOC(sizeof(linkedlist_datablock_internal)); + + if (ldi != NULL) + { + ldi->next_datablock = NULL; + ldi->filled_in_this_block = 0; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK; + } + return ldi; +} + +/* Free data block in linked list */ +static void free_datablock(linkedlist_datablock_internal *ldi) +{ + while (ldi != NULL) + { + linkedlist_datablock_internal *ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +/* Initialize linked list */ +static void init_linkedlist(linkedlist_data *ll) +{ + ll->first_block = ll->last_block = NULL; +} + +/* Free entire linked list and all data blocks */ +static void free_linkedlist(linkedlist_data *ll) +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} + +/* Add data to linked list data block */ +static int add_data_in_datablock(linkedlist_data *ll, const void *buf, uint32_t len) +{ + linkedlist_datablock_internal *ldi = NULL; + const unsigned char *from_copy = NULL; + + if (ll == NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len > 0) + { + uint32_t copy_this = 0; + uint32_t i = 0; + unsigned char *to_copy = NULL; + + if (ldi->avail_in_this_block == 0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = ldi->avail_in_this_block; + else + copy_this = len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i = 0; i < copy_this; i++) + *(to_copy+i) = *(from_copy+i); + + ldi->filled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this; + len -= copy_this; + } + return ZIP_OK; +} + +/* Inputs a long in LSB order to the given file: nbByte == 1, 2, 4 or 8 (byte, short or long, uint64_t) */ +static int zipWriteValue(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, + uint64_t x, uint32_t len) +{ + unsigned char buf[8]; + uint32_t n = 0; + + for (n = 0; n < len; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { + /* Data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < len; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE64(*pzlib_filefunc_def, filestream, buf, len) != len) + return ZIP_ERRNO; + + return ZIP_OK; +} + +static void zipWriteValueToMemory(void* dest, uint64_t x, uint32_t len) +{ + unsigned char *buf = (unsigned char*)dest; + uint32_t n = 0; + + for (n = 0; n < len; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { + /* data overflow - hack for ZIP64 */ + for (n = 0; n < len; n++) + { + buf[n] = 0xff; + } + } +} + +static void zipWriteValueToMemoryAndMove(unsigned char **dest_ptr, uint64_t x, uint32_t len) +{ + zipWriteValueToMemory(*dest_ptr, x, len); + *dest_ptr += len; +} + +static int zipReadUInt8(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint8_t *value) +{ + uint8_t c = 0; + if (ZREAD64(*pzlib_filefunc_def, filestream, &c, 1) == 1) + { + *value = (uint8_t)c; + return ZIP_OK; + } + if (ZERROR64(*pzlib_filefunc_def, filestream)) + return ZIP_ERRNO; + return ZIP_EOF; +} + +static int zipReadUInt16(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint16_t *value) +{ + uint16_t x = 0; + uint8_t c = 0; + int err = ZIP_OK; + + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x = (uint16_t)c; + if (err == ZIP_OK) + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x += ((uint16_t)c) << 8; + + if (err == ZIP_OK) + *value = x; + else + *value = 0; + return err; +} + +static int zipReadUInt32(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint32_t *value) +{ + uint32_t x = 0; + uint8_t c = 0; + int err = ZIP_OK; + + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x = (uint32_t)c; + if (err == ZIP_OK) + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x += ((uint32_t)c) << 8; + if (err == ZIP_OK) + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x += ((uint32_t)c) << 16; + if (err == ZIP_OK) + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x += ((uint32_t)c) << 24; + + if (err == ZIP_OK) + *value = x; + else + *value = 0; + return err; +} + +static int zipReadUInt64(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, uint64_t *value) +{ + uint64_t x = 0; + uint8_t c = 0; + int err = ZIP_OK; + + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x = (uint64_t)c; + if (err == ZIP_OK) + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x += ((uint64_t)c) << 8; + if (err == ZIP_OK) + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x += ((uint64_t)c) << 16; + if (err == ZIP_OK) + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x += ((uint64_t)c) << 24; + if (err == ZIP_OK) + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x += ((uint64_t)c) << 32; + if (err == ZIP_OK) + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x += ((uint64_t)c) << 40; + if (err == ZIP_OK) + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x += ((uint64_t)c) << 48; + if (err == ZIP_OK) + err = zipReadUInt8(pzlib_filefunc_def, filestream, &c); + x += ((uint64_t)c) << 56; + + if (err == ZIP_OK) + *value = x; + else + *value = 0; + + return err; +} + +/* Gets the amount of bytes left to write to the current disk for spanning archives */ +static void zipGetDiskSizeAvailable(zipFile file, uint64_t *size_available) +{ + zip64_internal *zi = NULL; + uint64_t current_disk_size = 0; + + zi = (zip64_internal*)file; + ZSEEK64(zi->z_filefunc, zi->filestream, 0, ZLIB_FILEFUNC_SEEK_END); + current_disk_size = ZTELL64(zi->z_filefunc, zi->filestream); + *size_available = zi->disk_size - current_disk_size; +} + +/* Goes to a specific disk number for spanning archives */ +static int zipGoToSpecificDisk(zipFile file, uint32_t number_disk, int open_existing) +{ + zip64_internal *zi = NULL; + int err = ZIP_OK; + + zi = (zip64_internal*)file; + if (zi->disk_size == 0) + return err; + + if ((zi->filestream != NULL) && (zi->filestream != zi->filestream_with_CD)) + ZCLOSE64(zi->z_filefunc, zi->filestream); + + zi->filestream = ZOPENDISK64(zi->z_filefunc, zi->filestream_with_CD, number_disk, (open_existing == 1) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE)); + + if (zi->filestream == NULL) + err = ZIP_ERRNO; + + return err; +} + +/* Goes to the first disk in a spanned archive */ +static int zipGoToFirstDisk(zipFile file) +{ + zip64_internal *zi = NULL; + uint32_t number_disk_next = 0; + int err = ZIP_OK; + + zi = (zip64_internal*)file; + + if (zi->disk_size == 0) + return err; + number_disk_next = 0; + if (zi->number_disk_with_CD > 0) + number_disk_next = zi->number_disk_with_CD - 1; + err = zipGoToSpecificDisk(file, number_disk_next, (zi->append == APPEND_STATUS_ADDINZIP)); + if ((err == ZIP_ERRNO) && (zi->append == APPEND_STATUS_ADDINZIP)) + err = zipGoToSpecificDisk(file, number_disk_next, 0); + if (err == ZIP_OK) + zi->number_disk = number_disk_next; + ZSEEK64(zi->z_filefunc, zi->filestream, 0, ZLIB_FILEFUNC_SEEK_END); + return err; +} + +/* Goes to the next disk in a spanned archive */ +static int zipGoToNextDisk(zipFile file) +{ + zip64_internal *zi = NULL; + uint64_t size_available_in_disk = 0; + uint32_t number_disk_next = 0; + int err = ZIP_OK; + + zi = (zip64_internal*)file; + if (zi->disk_size == 0) + return err; + + number_disk_next = zi->number_disk + 1; + + do + { + err = zipGoToSpecificDisk(file, number_disk_next, (zi->append == APPEND_STATUS_ADDINZIP)); + if ((err == ZIP_ERRNO) && (zi->append == APPEND_STATUS_ADDINZIP)) + err = zipGoToSpecificDisk(file, number_disk_next, 0); + if (err != ZIP_OK) + break; + zipGetDiskSizeAvailable(file, &size_available_in_disk); + zi->number_disk = number_disk_next; + zi->number_disk_with_CD = zi->number_disk + 1; + + number_disk_next += 1; + } + while (size_available_in_disk <= 0); + + return err; +} + +/* Locate the Central directory of a zipfile (at the end, just before the global comment) */ +static uint64_t zipSearchCentralDir(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream) +{ + unsigned char *buf = NULL; + uint64_t file_size = 0; + uint64_t back_read = 4; + uint64_t max_back = UINT16_MAX; /* maximum size of global comment */ + uint64_t pos_found = 0; + uint32_t read_size = 0; + uint64_t read_pos = 0; + uint32_t i = 0; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf == NULL) + return 0; + + if (ZSEEK64(*pzlib_filefunc_def, filestream, 0, ZLIB_FILEFUNC_SEEK_END) != 0) + { + TRYFREE(buf); + return 0; + } + + file_size = ZTELL64(*pzlib_filefunc_def, filestream); + + if (max_back > file_size) + max_back = file_size; + + while (back_read < max_back) + { + if (back_read + BUFREADCOMMENT > max_back) + back_read = max_back; + else + back_read += BUFREADCOMMENT; + + read_pos = file_size-back_read; + read_size = ((BUFREADCOMMENT+4) < (file_size - read_pos)) ? + (BUFREADCOMMENT+4) : (uint32_t)(file_size - read_pos); + + if (ZSEEK64(*pzlib_filefunc_def, filestream, read_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + break; + if (ZREAD64(*pzlib_filefunc_def, filestream, buf, read_size) != read_size) + break; + + for (i = read_size-3; (i--) > 0;) + if ((*(buf+i)) == (ENDHEADERMAGIC & 0xff) && + (*(buf+i+1)) == (ENDHEADERMAGIC >> 8 & 0xff) && + (*(buf+i+2)) == (ENDHEADERMAGIC >> 16 & 0xff) && + (*(buf+i+3)) == (ENDHEADERMAGIC >> 24 & 0xff)) + { + pos_found = read_pos+i; + break; + } + + if (pos_found != 0) + break; + } + TRYFREE(buf); + return pos_found; +} + +/* Locate the Central directory 64 of a zipfile (at the end, just before the global comment) */ +static uint64_t zipSearchCentralDir64(const zlib_filefunc64_32_def *pzlib_filefunc_def, voidpf filestream, + const uint64_t endcentraloffset) +{ + uint64_t offset = 0; + uint32_t value32 = 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def, filestream, endcentraloffset - SIZECENTRALHEADERLOCATOR, ZLIB_FILEFUNC_SEEK_SET) != 0) + return 0; + + /* Read locator signature */ + if (zipReadUInt32(pzlib_filefunc_def, filestream, &value32) != ZIP_OK) + return 0; + if (value32 != ZIP64ENDLOCHEADERMAGIC) + return 0; + /* Number of the disk with the start of the zip64 end of central directory */ + if (zipReadUInt32(pzlib_filefunc_def, filestream, &value32) != ZIP_OK) + return 0; + /* Relative offset of the zip64 end of central directory record */ + if (zipReadUInt64(pzlib_filefunc_def, filestream, &offset) != ZIP_OK) + return 0; + /* Total number of disks */ + if (zipReadUInt32(pzlib_filefunc_def, filestream, &value32) != ZIP_OK) + return 0; + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def, filestream, offset, ZLIB_FILEFUNC_SEEK_SET) != 0) + return 0; + /* The signature */ + if (zipReadUInt32(pzlib_filefunc_def, filestream, &value32) != ZIP_OK) + return 0; + if (value32 != ZIP64ENDHEADERMAGIC) + return 0; + + return offset; +} + +extern zipFile ZEXPORT zipOpen4(const void *path, int append, uint64_t disk_size, const char **globalcomment, + zlib_filefunc64_32_def *pzlib_filefunc64_32_def) +{ + zip64_internal ziinit = { 0 }; + zip64_internal *zi = NULL; +#ifndef NO_ADDFILEINEXISTINGZIP + uint64_t byte_before_the_zipfile = 0; /* byte before the zipfile, (>0 for sfx)*/ + uint64_t size_central_dir = 0; /* size of the central directory */ + uint64_t offset_central_dir = 0; /* offset of start of central directory */ + uint64_t number_entry_CD = 0; /* total number of entries in the central dir */ + uint64_t number_entry = 0; + uint64_t central_pos = 0; + uint64_t size_central_dir_to_read = 0; + uint16_t value16 = 0; + uint32_t value32 = 0; + uint16_t size_comment = 0; + size_t buf_size = SIZEDATA_INDATABLOCK; + void *buf_read = NULL; +#endif + int err = ZIP_OK; + int mode = 0; + + + if (pzlib_filefunc64_32_def == NULL) + fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64); + else + { + assert((pzlib_filefunc64_32_def->zopen32_file || pzlib_filefunc64_32_def->zfile_func64.zopen64_file) && + (pzlib_filefunc64_32_def->zopendisk32_file || pzlib_filefunc64_32_def->zfile_func64.zopendisk64_file) && + (pzlib_filefunc64_32_def->ztell32_file || pzlib_filefunc64_32_def->zfile_func64.ztell64_file) && + (pzlib_filefunc64_32_def->zseek32_file || pzlib_filefunc64_32_def->zfile_func64.zseek64_file)); + ziinit.z_filefunc = *pzlib_filefunc64_32_def; + } + + if (append == APPEND_STATUS_CREATE) + mode = (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE); + else + mode = (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING); + + ziinit.filestream = ZOPEN64(ziinit.z_filefunc, path, mode); + if (ziinit.filestream == NULL) + return NULL; + + if (append == APPEND_STATUS_CREATEAFTER) + { + /* Don't support spanning ZIP with APPEND_STATUS_CREATEAFTER */ + if (disk_size > 0) + return NULL; + + ZSEEK64(ziinit.z_filefunc, ziinit.filestream, 0, SEEK_END); + } + + + ziinit.filestream_with_CD = ziinit.filestream; + ziinit.append = append; + ziinit.disk_size = disk_size; + init_linkedlist(&(ziinit.central_dir)); + + zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); + if (zi == NULL) + { + ZCLOSE64(ziinit.z_filefunc, ziinit.filestream); + return NULL; + } + +#ifndef NO_ADDFILEINEXISTINGZIP + /* Add file in a zipfile */ + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + /* Read and Cache Central Directory Records */ + central_pos = zipSearchCentralDir(&ziinit.z_filefunc, ziinit.filestream); + /* Disable to allow appending to empty ZIP archive (must be standard zip, not zip64) + if (central_pos == 0) + err = ZIP_ERRNO; + */ + + if (err == ZIP_OK) + { + /* Read end of central directory info */ + if (ZSEEK64(ziinit.z_filefunc, ziinit.filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err = ZIP_ERRNO; + + /* The signature, already checked */ + if (zipReadUInt32(&ziinit.z_filefunc, ziinit.filestream, &value32) != ZIP_OK) + err = ZIP_ERRNO; + /* Number of this disk */ + if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &value16) != ZIP_OK) + err = ZIP_ERRNO; + ziinit.number_disk = value16; + /* Number of the disk with the start of the central directory */ + if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &value16) != ZIP_OK) + err = ZIP_ERRNO; + ziinit.number_disk_with_CD = value16; + /* Total number of entries in the central dir on this disk */ + number_entry = 0; + if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &value16) != ZIP_OK) + err = ZIP_ERRNO; + else + number_entry = value16; + /* Total number of entries in the central dir */ + number_entry_CD = 0; + if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &value16) != ZIP_OK) + err = ZIP_ERRNO; + else + number_entry_CD = value16; + if (number_entry_CD!=number_entry) + err = ZIP_BADZIPFILE; + /* Size of the central directory */ + size_central_dir = 0; + if (zipReadUInt32(&ziinit.z_filefunc, ziinit.filestream, &value32) != ZIP_OK) + err = ZIP_ERRNO; + else + size_central_dir = value32; + /* Offset of start of central directory with respect to the starting disk number */ + offset_central_dir = 0; + if (zipReadUInt32(&ziinit.z_filefunc, ziinit.filestream, &value32) != ZIP_OK) + err = ZIP_ERRNO; + else + offset_central_dir = value32; + /* Zipfile global comment length */ + if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &size_comment) != ZIP_OK) + err = ZIP_ERRNO; + + if ((err == ZIP_OK) && ((number_entry_CD == UINT16_MAX) || (offset_central_dir == UINT32_MAX))) + { + /* Format should be Zip64, as the central directory or file size is too large */ + central_pos = zipSearchCentralDir64(&ziinit.z_filefunc, ziinit.filestream, central_pos); + + if (central_pos) + { + uint64_t sizeEndOfCentralDirectory; + + if (ZSEEK64(ziinit.z_filefunc, ziinit.filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err = ZIP_ERRNO; + + /* The signature, already checked */ + if (zipReadUInt32(&ziinit.z_filefunc, ziinit.filestream, &value32) != ZIP_OK) + err = ZIP_ERRNO; + /* Size of zip64 end of central directory record */ + if (zipReadUInt64(&ziinit.z_filefunc, ziinit.filestream, &sizeEndOfCentralDirectory) != ZIP_OK) + err = ZIP_ERRNO; + /* Version made by */ + if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &value16) != ZIP_OK) + err = ZIP_ERRNO; + /* Version needed to extract */ + if (zipReadUInt16(&ziinit.z_filefunc, ziinit.filestream, &value16) != ZIP_OK) + err = ZIP_ERRNO; + /* Number of this disk */ + if (zipReadUInt32(&ziinit.z_filefunc, ziinit.filestream, &ziinit.number_disk) != ZIP_OK) + err = ZIP_ERRNO; + /* Number of the disk with the start of the central directory */ + if (zipReadUInt32(&ziinit.z_filefunc, ziinit.filestream, &ziinit.number_disk_with_CD) != ZIP_OK) + err = ZIP_ERRNO; + /* Total number of entries in the central directory on this disk */ + if (zipReadUInt64(&ziinit.z_filefunc, ziinit.filestream, &number_entry) != ZIP_OK) + err = ZIP_ERRNO; + /* Total number of entries in the central directory */ + if (zipReadUInt64(&ziinit.z_filefunc, ziinit.filestream, &number_entry_CD) != ZIP_OK) + err = ZIP_ERRNO; + if (number_entry_CD!=number_entry) + err = ZIP_BADZIPFILE; + /* Size of the central directory */ + if (zipReadUInt64(&ziinit.z_filefunc, ziinit.filestream, &size_central_dir) != ZIP_OK) + err = ZIP_ERRNO; + /* Offset of start of central directory with respect to the starting disk number */ + if (zipReadUInt64(&ziinit.z_filefunc, ziinit.filestream, &offset_central_dir) != ZIP_OK) + err = ZIP_ERRNO; + } + else + err = ZIP_BADZIPFILE; + } + } + + if ((err == ZIP_OK) && (central_pos < offset_central_dir + size_central_dir)) + err = ZIP_BADZIPFILE; + + if ((err == ZIP_OK) && (size_comment > 0)) + { + ziinit.globalcomment = (char*)ALLOC(size_comment+1); + if (ziinit.globalcomment) + { + if (ZREAD64(ziinit.z_filefunc, ziinit.filestream, ziinit.globalcomment, size_comment) != size_comment) + err = ZIP_ERRNO; + else + ziinit.globalcomment[size_comment] = 0; + } + } + + if (err != ZIP_OK) + { + ZCLOSE64(ziinit.z_filefunc, ziinit.filestream); + TRYFREE(ziinit.globalcomment); + TRYFREE(zi); + return NULL; + } + + byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); + ziinit.add_position_when_writting_offset = byte_before_the_zipfile; + + /* Store central directory in memory */ + size_central_dir_to_read = size_central_dir; + buf_size = SIZEDATA_INDATABLOCK; + buf_read = (void*)ALLOC(buf_size); + + if (ZSEEK64(ziinit.z_filefunc, ziinit.filestream, + offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err = ZIP_ERRNO; + + while ((size_central_dir_to_read > 0) && (err == ZIP_OK)) + { + uint64_t read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + + if (ZREAD64(ziinit.z_filefunc, ziinit.filestream, buf_read, (uint32_t)read_this) != read_this) + err = ZIP_ERRNO; + + if (err == ZIP_OK) + err = add_data_in_datablock(&ziinit.central_dir, buf_read, (uint32_t)read_this); + + size_central_dir_to_read -= read_this; + } + TRYFREE(buf_read); + + ziinit.number_entry = number_entry_CD; + + if (ZSEEK64(ziinit.z_filefunc, ziinit.filestream, + offset_central_dir+byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err = ZIP_ERRNO; + } + + if (globalcomment) + *globalcomment = ziinit.globalcomment; +#endif + + if (err != ZIP_OK) + { +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +#endif + TRYFREE(zi); + return NULL; + } + *zi = ziinit; + zipGoToFirstDisk((zipFile)zi); + + return(zipFile)zi; +} + +extern zipFile ZEXPORT zipOpen2(const char *path, int append, const char **globalcomment, + zlib_filefunc_def *pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill = { 0 }; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill, pzlib_filefunc32_def); + return zipOpen4(path, append, 0, globalcomment, &zlib_filefunc64_32_def_fill); + } + return zipOpen4(path, append, 0, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen2_64(const void *path, int append, const char **globalcomment, + zlib_filefunc64_def *pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill = { 0 }; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + return zipOpen4(path, append, 0, globalcomment, &zlib_filefunc64_32_def_fill); + } + return zipOpen4(path, append, 0, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen3(const char *path, int append, uint64_t disk_size, const char **globalcomment, + zlib_filefunc_def *pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill = { 0 }; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill, pzlib_filefunc32_def); + return zipOpen4(path, append, disk_size, globalcomment, &zlib_filefunc64_32_def_fill); + } + return zipOpen4(path, append, disk_size, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen3_64(const void *path, int append, uint64_t disk_size, const char **globalcomment, + zlib_filefunc64_def *pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill = { 0 }; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + return zipOpen4(path, append, disk_size, globalcomment, &zlib_filefunc64_32_def_fill); + } + return zipOpen4(path, append, disk_size, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen(const char *path, int append) +{ + return zipOpen3((const void*)path, append, 0, NULL, NULL); +} + +extern zipFile ZEXPORT zipOpen64(const void *path, int append) +{ + return zipOpen3(path, append, 0, NULL, NULL); +} + +extern int ZEXPORT zipOpenNewFileInZip5(zipFile file, + const char *filename, + const zip_fileinfo *zipfi, + const void *extrafield_local, + uint16_t size_extrafield_local, + const void *extrafield_global, + uint16_t size_extrafield_global, + const char *comment, + uint16_t flag_base, + int zip64, + uint16_t method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char *password, + int aes, + uint16_t version_madeby) +{ + zip64_internal *zi = NULL; + uint64_t size_available = 0; + uint64_t size_needed = 0; + uint16_t size_filename = 0; + uint16_t size_comment = 0; + uint16_t i = 0; + unsigned char *central_dir = NULL; + int err = ZIP_OK; + +#ifdef NOCRYPT + if (password != NULL) + return ZIP_PARAMERROR; +#endif + + if (file == NULL) + return ZIP_PARAMERROR; + + if ((method != 0) && +#ifdef HAVE_BZIP2 + (method != Z_BZIP2ED) && +#endif + (method != Z_DEFLATED)) + return ZIP_PARAMERROR; + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + if (filename == NULL) + filename = "-"; + if (comment != NULL) + size_comment = (uint16_t)strlen(comment); + + size_filename = (uint16_t)strlen(filename); + + if (zipfi == NULL) + zi->ci.dos_date = 0; + else + { + if (zipfi->dos_date != 0) + zi->ci.dos_date = zipfi->dos_date; + } + + zi->ci.method = method; + zi->ci.compression_method = method; + zi->ci.raw = raw; + zi->ci.flag = flag_base | 8; + if ((level == 8) || (level == 9)) + zi->ci.flag |= 2; + if (level == 2) + zi->ci.flag |= 4; + if (level == 1) + zi->ci.flag |= 6; + + if (password != NULL) + { + zi->ci.flag |= 1; +#ifdef HAVE_AES + if (aes) + zi->ci.method = AES_METHOD; +#endif + } + else + { + zi->ci.flag &= ~1; + } + + if (zi->disk_size > 0) + { + if ((zi->number_disk == 0) && (zi->number_entry == 0)) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)DISKHEADERMAGIC, 4); + + /* Make sure enough space available on current disk for local header */ + zipGetDiskSizeAvailable((zipFile)zi, &size_available); + size_needed = 30 + size_filename + size_extrafield_local; +#ifdef HAVE_AES + if (zi->ci.method == AES_METHOD) + size_needed += 11; +#endif + if (size_available < size_needed) + zipGoToNextDisk((zipFile)zi); + } + + zi->ci.zip64 = zip64; + + zi->ci.pos_local_header = ZTELL64(zi->z_filefunc, zi->filestream); + if (zi->ci.pos_local_header >= UINT32_MAX) + zi->ci.zip64 = 1; + + zi->ci.size_comment = size_comment; + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global; + zi->ci.size_centralextra = size_extrafield_global; + zi->ci.size_centralextrafree = 32; /* Extra space reserved for ZIP64 extra info */ +#ifdef HAVE_AES + if (zi->ci.method == AES_METHOD) + zi->ci.size_centralextrafree += 11; /* Extra space reserved for AES extra info */ +#endif + zi->ci.central_header = (char*)ALLOC((uint32_t)zi->ci.size_centralheader + zi->ci.size_centralextrafree + size_comment); + zi->ci.number_disk = zi->number_disk; + + /* Write central directory header */ + /* https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT */ + central_dir = (unsigned char*)zi->ci.central_header; + zipWriteValueToMemoryAndMove(¢ral_dir, (uint32_t)CENTRALHEADERMAGIC, 4); + zipWriteValueToMemoryAndMove(¢ral_dir, version_madeby, 2); + if (zi->ci.zip64) + zipWriteValueToMemoryAndMove(¢ral_dir, (uint16_t)45, 2); + else + zipWriteValueToMemoryAndMove(¢ral_dir, (uint16_t)20, 2); + zipWriteValueToMemoryAndMove(¢ral_dir, zi->ci.flag, 2); + zipWriteValueToMemoryAndMove(¢ral_dir, zi->ci.method, 2); + zipWriteValueToMemoryAndMove(¢ral_dir, zi->ci.dos_date, 4); + zipWriteValueToMemoryAndMove(¢ral_dir, (uint32_t)0, 4); /*crc*/ + zipWriteValueToMemoryAndMove(¢ral_dir, (uint32_t)0, 4); /*compr size*/ + zipWriteValueToMemoryAndMove(¢ral_dir, (uint32_t)0, 4); /*uncompr size*/ + zipWriteValueToMemoryAndMove(¢ral_dir, size_filename, 2); + zipWriteValueToMemoryAndMove(¢ral_dir, size_extrafield_global, 2); + zipWriteValueToMemoryAndMove(¢ral_dir, size_comment, 2); + zipWriteValueToMemoryAndMove(¢ral_dir, (uint16_t)zi->ci.number_disk, 2); /*disk nm start*/ + + if (zipfi == NULL) + zipWriteValueToMemoryAndMove(¢ral_dir, (uint16_t)0, 2); + else + zipWriteValueToMemoryAndMove(¢ral_dir, zipfi->internal_fa, 2); + if (zipfi == NULL) + zipWriteValueToMemoryAndMove(¢ral_dir, (uint32_t)0, 4); + else + zipWriteValueToMemoryAndMove(¢ral_dir, zipfi->external_fa, 4); + if (zi->ci.pos_local_header >= UINT32_MAX) + zipWriteValueToMemoryAndMove(¢ral_dir, UINT32_MAX, 4); + else + zipWriteValueToMemoryAndMove(¢ral_dir, + (uint32_t)(zi->ci.pos_local_header - zi->add_position_when_writting_offset), 4); + + for (i = 0; i < size_filename; i++) + zi->ci.central_header[SIZECENTRALHEADER+i] = filename[i]; + for (i = 0; i < size_extrafield_global; i++) + zi->ci.central_header[SIZECENTRALHEADER+size_filename+i] = + ((const char*)extrafield_global)[i]; + + /* Store comment at the end for later repositioning */ + for (i = 0; i < size_comment; i++) + zi->ci.central_header[zi->ci.size_centralheader+ + zi->ci.size_centralextrafree+i] = comment[i]; + + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + /* Write the local header */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)LOCALHEADERMAGIC, 4); + + if (err == ZIP_OK) + { + if (zi->ci.zip64) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)45, 2); /* version needed to extract */ + else + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)20, 2); /* version needed to extract */ + } + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->ci.flag, 2); + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->ci.method, 2); + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->ci.dos_date, 4); + + /* CRC & compressed size & uncompressed size is in data descriptor */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)0, 4); /* crc 32, unknown */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)0, 4); /* compressed size, unknown */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)0, 4); /* uncompressed size, unknown */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, size_filename, 2); + if (err == ZIP_OK) + { + uint64_t size_extrafield = size_extrafield_local; +#ifdef HAVE_AES + if (zi->ci.method == AES_METHOD) + size_extrafield += 11; +#endif + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)size_extrafield, 2); + } + if ((err == ZIP_OK) && (size_filename > 0)) + { + if (ZWRITE64(zi->z_filefunc, zi->filestream, filename, size_filename) != size_filename) + err = ZIP_ERRNO; + } + if ((err == ZIP_OK) && (size_extrafield_local > 0)) + { + if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) + err = ZIP_ERRNO; + } + +#ifdef HAVE_AES + /* Write the AES extended info */ + if ((err == ZIP_OK) && (zi->ci.method == AES_METHOD)) + { + int headerid = 0x9901; + short datasize = 7; + + err = zipWriteValue(&zi->z_filefunc, zi->filestream, headerid, 2); + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, datasize, 2); + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, AES_VERSION, 2); + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, 'A', 1); + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, 'E', 1); + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, AES_ENCRYPTIONMODE, 1); + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->ci.compression_method, 2); + } +#endif + + zi->ci.crc32 = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.total_compressed = 0; + zi->ci.total_uncompressed = 0; + +#ifdef HAVE_BZIP2 + zi->ci.bstream.avail_in = (uint16_t)0; + zi->ci.bstream.avail_out = (uint16_t)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + zi->ci.bstream.total_in_hi32 = 0; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_out_hi32 = 0; + zi->ci.bstream.total_out_lo32 = 0; +#endif + + zi->ci.stream.avail_in = (uint16_t)0; + zi->ci.stream.avail_out = Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + zi->ci.stream.data_type = Z_BINARY; + + if ((err == ZIP_OK) && (!zi->ci.raw)) + { + if (method == Z_DEFLATED) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)zi; + + if (windowBits > 0) + windowBits = -windowBits; + +#ifdef HAVE_APPLE_COMPRESSION + err = compression_stream_init(&zi->ci.astream, COMPRESSION_STREAM_ENCODE, COMPRESSION_ZLIB); + if (err == COMPRESSION_STATUS_ERROR) + err = Z_ERRNO; + else + err = Z_OK; +#else + err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); +#endif + if (err == Z_OK) + zi->ci.stream_initialised = Z_DEFLATED; + } + else if (method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + zi->ci.bstream.bzalloc = 0; + zi->ci.bstream.bzfree = 0; + zi->ci.bstream.opaque = (voidpf)0; + + err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0, 35); + if (err == BZ_OK) + zi->ci.stream_initialised = Z_BZIP2ED; +#endif + } + } + +#ifndef NOCRYPT + if ((err == Z_OK) && (password != NULL)) + { +#ifdef HAVE_AES + if (zi->ci.method == AES_METHOD) + { + unsigned char passverify[AES_PWVERIFYSIZE]; + unsigned char saltvalue[AES_MAXSALTLENGTH]; + uint16_t saltlength = 0; + + if ((AES_ENCRYPTIONMODE < 1) || (AES_ENCRYPTIONMODE > 3)) + return Z_ERRNO; + + saltlength = SALT_LENGTH(AES_ENCRYPTIONMODE); + + prng_init(cryptrand, zi->ci.aes_rng); + prng_rand(saltvalue, saltlength, zi->ci.aes_rng); + prng_end(zi->ci.aes_rng); + + fcrypt_init(AES_ENCRYPTIONMODE, (uint8_t *)password, (uint32_t)strlen(password), saltvalue, passverify, &zi->ci.aes_ctx); + + if (ZWRITE64(zi->z_filefunc, zi->filestream, saltvalue, saltlength) != saltlength) + err = ZIP_ERRNO; + if (ZWRITE64(zi->z_filefunc, zi->filestream, passverify, AES_PWVERIFYSIZE) != AES_PWVERIFYSIZE) + err = ZIP_ERRNO; + + zi->ci.total_compressed += saltlength + AES_PWVERIFYSIZE + AES_AUTHCODESIZE; + } + else +#endif + { + unsigned char buf_head[RAND_HEAD_LEN]; + uint32_t size_head = 0; + uint8_t verify1 = 0; + uint8_t verify2 = 0; + + zi->ci.pcrc_32_tab = get_crc_table(); + + /* + Info-ZIP modification to ZipCrypto format: + If bit 3 of the general purpose bit flag is set, it uses high byte of 16-bit File Time. + */ + verify1 = (uint8_t)((zi->ci.dos_date >> 16) & 0xff); + verify2 = (uint8_t)((zi->ci.dos_date >> 8) & 0xff); + + size_head = crypthead(password, buf_head, RAND_HEAD_LEN, zi->ci.keys, zi->ci.pcrc_32_tab, verify1, verify2); + zi->ci.total_compressed += size_head; + + if (ZWRITE64(zi->z_filefunc, zi->filestream, buf_head, size_head) != size_head) + err = ZIP_ERRNO; + } + } +#endif + + if (err == Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits, int memLevel, + int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting, uint16_t version_madeby, uint16_t flag_base, int zip64) +{ + uint8_t aes = 0; +#ifdef HAVE_AES + aes = 1; +#endif + return zipOpenNewFileInZip5(file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, + size_extrafield_global, comment, flag_base, zip64, method, level, raw, windowBits, memLevel, strategy, password, aes, + version_madeby); +} + +extern int ZEXPORT zipOpenNewFileInZip4(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits, + int memLevel, int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting, uint16_t version_madeby, uint16_t flag_base) +{ + return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, + strategy, password, crc_for_crypting, version_madeby, flag_base, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits, + int memLevel, int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting) +{ + return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, + strategy, password, crc_for_crypting, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits, + int memLevel, int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting, int zip64) +{ + return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy, + password, crc_for_crypting, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw) +{ + return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, comment, method, level, raw, -MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int zip64) +{ + return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, comment, method, level, raw, -MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip64(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int zip64) +{ + return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, comment, method, level, 0, -MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level) +{ + return zipOpenNewFileInZip4_64(file, filename, zipfi, extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, comment, method, level, 0, -MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, NULL, 0, VERSIONMADEBY, 0, 0); +} + +/* Flushes the write buffer to disk */ +static int zipFlushWriteBuffer(zip64_internal *zi) +{ + uint64_t size_available = 0; + uint32_t written = 0; + uint32_t total_written = 0; + uint32_t write = 0; + uint32_t max_write = 0; + int err = ZIP_OK; + + if ((zi->ci.flag & 1) != 0) + { +#ifndef NOCRYPT +#ifdef HAVE_AES + if (zi->ci.method == AES_METHOD) + { + fcrypt_encrypt(zi->ci.buffered_data, zi->ci.pos_in_buffered_data, &zi->ci.aes_ctx); + } + else +#endif + { + uint32_t i = 0; + uint8_t t = 0; + + for (i = 0; i < zi->ci.pos_in_buffered_data; i++) + zi->ci.buffered_data[i] = (uint8_t)zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i], t); + } +#endif + } + + write = zi->ci.pos_in_buffered_data; + + do + { + max_write = write; + + if (zi->disk_size > 0) + { + zipGetDiskSizeAvailable((zipFile)zi, &size_available); + + if (size_available == 0) + { + err = zipGoToNextDisk((zipFile)zi); + if (err != ZIP_OK) + return err; + } + + if (size_available < (uint64_t)max_write) + max_write = (uint32_t)size_available; + } + + written = ZWRITE64(zi->z_filefunc, zi->filestream, zi->ci.buffered_data + total_written, max_write); + if (written != max_write) + { + err = ZIP_ERRNO; + break; + } + + total_written += written; + write -= written; + } + while (write > 0); + + zi->ci.total_compressed += zi->ci.pos_in_buffered_data; + +#ifdef HAVE_BZIP2 + if (zi->ci.compression_method == Z_BZIP2ED) + { + zi->ci.total_uncompressed += zi->ci.bstream.total_in_lo32; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_in_hi32 = 0; + } + else +#endif + { + zi->ci.total_uncompressed += zi->ci.stream.total_in; + zi->ci.stream.total_in = 0; + } + + zi->ci.pos_in_buffered_data = 0; + + return err; +} + +extern int ZEXPORT zipWriteInFileInZip(zipFile file, const void *buf, uint32_t len) +{ + zip64_internal *zi = NULL; + int err = ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.crc32 = (uint32_t)crc32(zi->ci.crc32, buf, len); + +#ifdef HAVE_BZIP2 + if ((zi->ci.compression_method == Z_BZIP2ED) && (!zi->ci.raw)) + { + zi->ci.bstream.next_in = (void*)buf; + zi->ci.bstream.avail_in = len; + err = BZ_RUN_OK; + + while ((err == BZ_RUN_OK) && (zi->ci.bstream.avail_in > 0)) + { + if (zi->ci.bstream.avail_out == 0) + { + err = zipFlushWriteBuffer(zi); + + zi->ci.bstream.avail_out = (uint16_t)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + else + { + uint32_t total_out_before_lo = zi->ci.bstream.total_out_lo32; + uint32_t total_out_before_hi = zi->ci.bstream.total_out_hi32; + + err = BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); + + zi->ci.pos_in_buffered_data += (uint16_t)(zi->ci.bstream.total_out_lo32 - total_out_before_lo); + } + } + + if (err == BZ_RUN_OK) + err = ZIP_OK; + } + else +#endif + { + zi->ci.stream.next_in = (uint8_t*)buf; + zi->ci.stream.avail_in = len; + + while ((err == ZIP_OK) && (zi->ci.stream.avail_in > 0)) + { + if (zi->ci.stream.avail_out == 0) + { + err = zipFlushWriteBuffer(zi); + + zi->ci.stream.avail_out = Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + if (err != ZIP_OK) + break; + + if ((zi->ci.compression_method == Z_DEFLATED) && (!zi->ci.raw)) + { +#ifdef HAVE_APPLE_COMPRESSION + uLong total_out_before = zi->ci.stream.total_out; + + zi->ci.astream.src_ptr = zi->ci.stream.next_in; + zi->ci.astream.src_size = zi->ci.stream.avail_in; + zi->ci.astream.dst_ptr = zi->ci.stream.next_out; + zi->ci.astream.dst_size = zi->ci.stream.avail_out; + + compression_status status = 0; + compression_stream_flags flags = 0; + + status = compression_stream_process(&zi->ci.astream, flags); + + uLong total_out_after = len - zi->ci.astream.src_size; + + zi->ci.stream.next_in = zi->ci.astream.src_ptr; + zi->ci.stream.avail_in = zi->ci.astream.src_size; + zi->ci.stream.next_out = zi->ci.astream.dst_ptr; + zi->ci.stream.avail_out = zi->ci.astream.dst_size; + zi->ci.stream.total_in += total_out_after; + //zi->ci.stream.total_out += copy_this; + zi->ci.pos_in_buffered_data += total_out_after; + + if (status == COMPRESSION_STATUS_ERROR) + err = ZIP_INTERNALERROR; +#else + uint32_t total_out_before = (uint32_t)zi->ci.stream.total_out; + err = deflate(&zi->ci.stream, Z_NO_FLUSH); + zi->ci.pos_in_buffered_data += (uint32_t)(zi->ci.stream.total_out - total_out_before); +#endif + } + else + { + uint32_t copy_this = 0; + uint32_t i = 0; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + + for (i = 0; i < copy_this; i++) + *(((char*)zi->ci.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out -= copy_this; + zi->ci.stream.next_in += copy_this; + zi->ci.stream.next_out += copy_this; + zi->ci.stream.total_in += copy_this; + zi->ci.stream.total_out += copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, uint64_t uncompressed_size, uint32_t crc32) +{ + zip64_internal *zi = NULL; + uint16_t extra_data_size = 0; + uint32_t i = 0; + unsigned char *extra_info = NULL; + int err = ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if (!zi->ci.raw) + { + if (zi->ci.compression_method == Z_DEFLATED) + { + while (err == ZIP_OK) + { + uint32_t total_out_before = 0; + + if (zi->ci.stream.avail_out == 0) + { + err = zipFlushWriteBuffer(zi); + + zi->ci.stream.avail_out = Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + if (err != ZIP_OK) + break; + +#ifdef HAVE_APPLE_COMPRESSION + total_out_before = zi->ci.stream.total_out; + + zi->ci.astream.src_ptr = zi->ci.stream.next_in; + zi->ci.astream.src_size = zi->ci.stream.avail_in; + zi->ci.astream.dst_ptr = zi->ci.stream.next_out; + zi->ci.astream.dst_size = zi->ci.stream.avail_out; + + compression_status status = 0; + status = compression_stream_process(&zi->ci.astream, COMPRESSION_STREAM_FINALIZE); + + uint32_t total_out_after = Z_BUFSIZE - zi->ci.astream.dst_size; + + zi->ci.stream.next_in = zi->ci.astream.src_ptr; + zi->ci.stream.avail_in = zi->ci.astream.src_size; + zi->ci.stream.next_out = zi->ci.astream.dst_ptr; + zi->ci.stream.avail_out = zi->ci.astream.dst_size; + //zi->ci.stream.total_in += total_out_after; + //zi->ci.stream.total_out += copy_this; + zi->ci.pos_in_buffered_data += total_out_after; + + if (status == COMPRESSION_STATUS_ERROR) + { + err = ZIP_INTERNALERROR; + } + else if (status == COMPRESSION_STATUS_END) + { + err = Z_STREAM_END; + } +#else + total_out_before = (uint32_t)zi->ci.stream.total_out; + err = deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uint16_t)(zi->ci.stream.total_out - total_out_before); +#endif + } + } + else if (zi->ci.compression_method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + err = BZ_FINISH_OK; + while (err == BZ_FINISH_OK) + { + uint32_t total_out_before = 0; + + if (zi->ci.bstream.avail_out == 0) + { + err = zipFlushWriteBuffer(zi); + + zi->ci.bstream.avail_out = (uint16_t)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + + total_out_before = zi->ci.bstream.total_out_lo32; + err = BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); + if (err == BZ_STREAM_END) + err = Z_STREAM_END; + zi->ci.pos_in_buffered_data += (uint16_t)(zi->ci.bstream.total_out_lo32 - total_out_before); + } + + if (err == BZ_FINISH_OK) + err = ZIP_OK; +#endif + } + } + + if (err == Z_STREAM_END) + err = ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data > 0) && (err == ZIP_OK)) + { + err = zipFlushWriteBuffer(zi); + } + +#ifdef HAVE_AES + if (zi->ci.method == AES_METHOD) + { + unsigned char authcode[AES_AUTHCODESIZE]; + + fcrypt_end(authcode, &zi->ci.aes_ctx); + + if (ZWRITE64(zi->z_filefunc, zi->filestream, authcode, AES_AUTHCODESIZE) != AES_AUTHCODESIZE) + err = ZIP_ERRNO; + } +#endif + + if (!zi->ci.raw) + { + if (zi->ci.compression_method == Z_DEFLATED) + { + int tmp_err = 0; +#ifdef HAVE_APPLE_COMPRESSION + tmp_err = compression_stream_destroy(&zi->ci.astream); +#else + tmp_err = deflateEnd(&zi->ci.stream); +#endif + if (err == ZIP_OK) + err = tmp_err; + zi->ci.stream_initialised = 0; + } +#ifdef HAVE_BZIP2 + else if (zi->ci.compression_method == Z_BZIP2ED) + { + int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); + if (err == ZIP_OK) + err = tmperr; + zi->ci.stream_initialised = 0; + } +#endif + + crc32 = zi->ci.crc32; + uncompressed_size = zi->ci.total_uncompressed; + } + + /* Write data descriptor */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)DATADESCRIPTORMAGIC, 4); + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, crc32, 4); + if (err == ZIP_OK) + { + if (zi->ci.zip64) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->ci.total_compressed, 8); + else + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)zi->ci.total_compressed, 4); + } + if (err == ZIP_OK) + { + if (zi->ci.zip64) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); + else + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)uncompressed_size, 4); + } + + /* Update crc and sizes to central directory */ + zipWriteValueToMemory(zi->ci.central_header + 16, crc32, 4); /* crc */ + if (zi->ci.total_compressed >= UINT32_MAX) + zipWriteValueToMemory(zi->ci.central_header + 20, UINT32_MAX, 4); /* compr size */ + else + zipWriteValueToMemory(zi->ci.central_header + 20, zi->ci.total_compressed, 4); /* compr size */ + if (uncompressed_size >= UINT32_MAX) + zipWriteValueToMemory(zi->ci.central_header + 24, UINT32_MAX, 4); /* uncompr size */ + else + zipWriteValueToMemory(zi->ci.central_header + 24, uncompressed_size, 4); /* uncompr size */ + if (zi->ci.stream.data_type == Z_ASCII) + zipWriteValueToMemory(zi->ci.central_header + 36, (uint16_t)Z_ASCII, 2); /* internal file attrib */ + + /* Add ZIP64 extra info field for uncompressed size */ + if (uncompressed_size >= UINT32_MAX) + extra_data_size += 8; + /* Add ZIP64 extra info field for compressed size */ + if (zi->ci.total_compressed >= UINT32_MAX) + extra_data_size += 8; + /* Add ZIP64 extra info field for relative offset to local file header of current file */ + if (zi->ci.pos_local_header >= UINT32_MAX) + extra_data_size += 8; + + /* Add ZIP64 extra info header to central directory */ + if (extra_data_size > 0) + { + if ((uint32_t)(extra_data_size + 4) > zi->ci.size_centralextrafree) + return ZIP_BADZIPFILE; + + extra_info = (unsigned char*)zi->ci.central_header + zi->ci.size_centralheader; + + zipWriteValueToMemoryAndMove(&extra_info, 0x0001, 2); + zipWriteValueToMemoryAndMove(&extra_info, extra_data_size, 2); + + if (uncompressed_size >= UINT32_MAX) + zipWriteValueToMemoryAndMove(&extra_info, uncompressed_size, 8); + if (zi->ci.total_compressed >= UINT32_MAX) + zipWriteValueToMemoryAndMove(&extra_info, zi->ci.total_compressed, 8); + if (zi->ci.pos_local_header >= UINT32_MAX) + zipWriteValueToMemoryAndMove(&extra_info, zi->ci.pos_local_header, 8); + + zi->ci.size_centralextrafree -= extra_data_size + 4; + zi->ci.size_centralheader += extra_data_size + 4; + zi->ci.size_centralextra += extra_data_size + 4; + + zipWriteValueToMemory(zi->ci.central_header + 30, zi->ci.size_centralextra, 2); + } + +#ifdef HAVE_AES + /* Write AES extra info header to central directory */ + if (zi->ci.method == AES_METHOD) + { + extra_info = (unsigned char*)zi->ci.central_header + zi->ci.size_centralheader; + extra_data_size = 7; + + if ((uint32_t)(extra_data_size + 4) > zi->ci.size_centralextrafree) + return ZIP_BADZIPFILE; + + zipWriteValueToMemoryAndMove(&extra_info, 0x9901, 2); + zipWriteValueToMemoryAndMove(&extra_info, extra_data_size, 2); + zipWriteValueToMemoryAndMove(&extra_info, AES_VERSION, 2); + zipWriteValueToMemoryAndMove(&extra_info, 'A', 1); + zipWriteValueToMemoryAndMove(&extra_info, 'E', 1); + zipWriteValueToMemoryAndMove(&extra_info, AES_ENCRYPTIONMODE, 1); + zipWriteValueToMemoryAndMove(&extra_info, zi->ci.compression_method, 2); + + zi->ci.size_centralextrafree -= extra_data_size + 4; + zi->ci.size_centralheader += extra_data_size + 4; + zi->ci.size_centralextra += extra_data_size + 4; + + zipWriteValueToMemory(zi->ci.central_header + 30, zi->ci.size_centralextra, 2); + } +#endif + /* Restore comment to correct position */ + for (i = 0; i < zi->ci.size_comment; i++) + zi->ci.central_header[zi->ci.size_centralheader+i] = + zi->ci.central_header[zi->ci.size_centralheader+zi->ci.size_centralextrafree+i]; + zi->ci.size_centralheader += zi->ci.size_comment; + + if (err == ZIP_OK) + err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, zi->ci.size_centralheader); + + free(zi->ci.central_header); + + zi->number_entry++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, uint32_t uncompressed_size, uint32_t crc32) +{ + return zipCloseFileInZipRaw64(file, uncompressed_size, crc32); +} + +extern int ZEXPORT zipCloseFileInZip(zipFile file) +{ + return zipCloseFileInZipRaw(file, 0, 0); +} + +extern int ZEXPORT zipClose(zipFile file, const char *global_comment) +{ + return zipClose_64(file, global_comment); +} + +extern int ZEXPORT zipClose_64(zipFile file, const char *global_comment) +{ + return zipClose2_64(file, global_comment, VERSIONMADEBY); +} + +extern int ZEXPORT zipClose2_64(zipFile file, const char *global_comment, uint16_t version_madeby) +{ + zip64_internal *zi = NULL; + uint32_t size_centraldir = 0; + uint16_t size_global_comment = 0; + uint64_t centraldir_pos_inzip = 0; + uint64_t pos = 0; + uint64_t cd_pos = 0; + uint32_t write = 0; + int err = ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + err = zipCloseFileInZip(file); + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment == NULL) + global_comment = zi->globalcomment; +#endif + + if (zi->filestream != zi->filestream_with_CD) + { + if (ZCLOSE64(zi->z_filefunc, zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + if (zi->disk_size > 0) + zi->number_disk_with_CD = zi->number_disk + 1; + zi->filestream = zi->filestream_with_CD; + } + + centraldir_pos_inzip = ZTELL64(zi->z_filefunc, zi->filestream); + + if (err == ZIP_OK) + { + linkedlist_datablock_internal *ldi = zi->central_dir.first_block; + while (ldi != NULL) + { + if ((err == ZIP_OK) && (ldi->filled_in_this_block > 0)) + { + write = ZWRITE64(zi->z_filefunc, zi->filestream, ldi->data, ldi->filled_in_this_block); + if (write != ldi->filled_in_this_block) + err = ZIP_ERRNO; + } + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + + free_linkedlist(&(zi->central_dir)); + + pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + + /* Write the ZIP64 central directory header */ + if (pos >= UINT32_MAX || zi->number_entry > UINT32_MAX) + { + uint64_t zip64_eocd_pos_inzip = ZTELL64(zi->z_filefunc, zi->filestream); + uint32_t zip64_datasize = 44; + + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)ZIP64ENDHEADERMAGIC, 4); + + /* Size of this 'zip64 end of central directory' */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint64_t)zip64_datasize, 8); + /* Version made by */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, version_madeby, 2); + /* version needed */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)45, 2); + /* Number of this disk */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->number_disk_with_CD, 4); + /* Number of the disk with the start of the central directory */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->number_disk_with_CD, 4); + /* Total number of entries in the central dir on this disk */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + /* Total number of entries in the central dir */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + /* Size of the central directory */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint64_t)size_centraldir, 8); + + if (err == ZIP_OK) + { + /* Offset of start of central directory with respect to the starting disk number */ + cd_pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + err = zipWriteValue(&zi->z_filefunc, zi->filestream, cd_pos, 8); + } + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)ZIP64ENDLOCHEADERMAGIC, 4); + + /* Number of the disk with the start of the central directory */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->number_disk_with_CD, 4); + /* Relative offset to the Zip64EndOfCentralDirectory */ + if (err == ZIP_OK) + { + cd_pos = zip64_eocd_pos_inzip - zi->add_position_when_writting_offset; + err = zipWriteValue(&zi->z_filefunc, zi->filestream, cd_pos, 8); + } + /* Number of the disk with the start of the central directory */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, zi->number_disk_with_CD + 1, 4); + } + + /* Write the central directory header */ + + /* Signature */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)ENDHEADERMAGIC, 4); + /* Number of this disk */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)zi->number_disk_with_CD, 2); + /* Number of the disk with the start of the central directory */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)zi->number_disk_with_CD, 2); + /* Total number of entries in the central dir on this disk */ + if (err == ZIP_OK) + { + if (zi->number_entry >= UINT16_MAX) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, UINT16_MAX, 2); /* use value in ZIP64 record */ + else + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)zi->number_entry, 2); + } + /* Total number of entries in the central dir */ + if (err == ZIP_OK) + { + if (zi->number_entry >= UINT16_MAX) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, UINT16_MAX, 2); /* use value in ZIP64 record */ + else + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint16_t)zi->number_entry, 2); + } + /* Size of the central directory */ + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, size_centraldir, 4); + /* Offset of start of central directory with respect to the starting disk number */ + if (err == ZIP_OK) + { + cd_pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if (pos >= UINT32_MAX) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, UINT32_MAX, 4); + else + err = zipWriteValue(&zi->z_filefunc, zi->filestream, (uint32_t)cd_pos, 4); + } + + /* Write global comment */ + + if (global_comment != NULL) + size_global_comment = (uint16_t)strlen(global_comment); + if (err == ZIP_OK) + err = zipWriteValue(&zi->z_filefunc, zi->filestream, size_global_comment, 2); + if (err == ZIP_OK && size_global_comment > 0) + { + if (ZWRITE64(zi->z_filefunc, zi->filestream, global_comment, size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + } + + if ((ZCLOSE64(zi->z_filefunc, zi->filestream) != 0) && (err == ZIP_OK)) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} diff --git a/SSZipArchive/minizip/zip.h b/SSZipArchive/minizip/zip.h new file mode 100644 index 0000000..38805ad --- /dev/null +++ b/SSZipArchive/minizip/zip.h @@ -0,0 +1,215 @@ +/* zip.h -- IO on .zip files using zlib + Version 1.2.0, September 16th, 2017 + part of the MiniZip project + + Copyright (C) 2012-2017 Nathan Moinvaziri + https://github.com/nmoinvaz/minizip + Copyright (C) 2009-2010 Mathias Svensson + Modifications for Zip64 support + http://result42.com + Copyright (C) 1998-2010 Gilles Vollant + http://www.winimage.com/zLibDll/minizip.html + + This program is distributed under the terms of the same license as zlib. + See the accompanying LICENSE file for the full text of the license. +*/ + +#ifndef _ZIP_H +#define _ZIP_H + +#define HAVE_AES + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +# include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +# include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +# include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zip_file__; +typedef zip_file__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif + +typedef struct +{ + uint32_t dos_date; + uint16_t internal_fa; /* internal file attributes 2 bytes */ + uint32_t external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +/***************************************************************************/ +/* Writing a zip file */ + +extern zipFile ZEXPORT zipOpen(const char *path, int append); +extern zipFile ZEXPORT zipOpen64(const void *path, int append); +/* Create a zipfile. + + path should contain the full path (by example, on a Windows XP computer + "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". + + return NULL if zipfile cannot be opened + return zipFile handle if no error + + If the file path exist and append == APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. (useful if the file contain a self extractor code) + If the file path exist and append == APPEND_STATUS_ADDINZIP, we will add files in existing + zip (be sure you don't add file that doesn't exist) + + NOTE: There is no delete function into a zipfile. If you want delete file into a zipfile, + you must open a zipfile, and create another. Of course, you can use RAW reading and writing to copy + the file you did not want delete. */ + +extern zipFile ZEXPORT zipOpen2(const char *path, int append, const char **globalcomment, + zlib_filefunc_def *pzlib_filefunc_def); + +extern zipFile ZEXPORT zipOpen2_64(const void *path, int append, const char **globalcomment, + zlib_filefunc64_def *pzlib_filefunc_def); + +extern zipFile ZEXPORT zipOpen3(const char *path, int append, uint64_t disk_size, + const char **globalcomment, zlib_filefunc_def *pzlib_filefunc_def); +/* Same as zipOpen2 but allows specification of spanned zip size */ + +extern zipFile ZEXPORT zipOpen3_64(const void *path, int append, uint64_t disk_size, + const char **globalcomment, zlib_filefunc64_def *pzlib_filefunc_def); + +extern int ZEXPORT zipOpenNewFileInZip(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level); +/* Open a file in the ZIP for writing. + + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + extrafield_local buffer to store the local header extra field data, can be NULL + size_extrafield_local size of extrafield_local buffer + extrafield_global buffer to store the global header extra field data, can be NULL + size_extrafield_global size of extrafield_local buffer + comment buffer for comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) + zip64 is set to 1 if a zip64 extended information block should be added to the local file header. + this MUST be '1' if the uncompressed size is >= 0xffffffff. */ + +extern int ZEXPORT zipOpenNewFileInZip64(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int zip64); +/* Same as zipOpenNewFileInZip with zip64 support */ + +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw); +/* Same as zipOpenNewFileInZip, except if raw=1, we write raw file */ + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int zip64); +/* Same as zipOpenNewFileInZip3 with zip64 support */ + +extern int ZEXPORT zipOpenNewFileInZip3(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits, int memLevel, + int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting); +/* Same as zipOpenNewFileInZip2, except + windowBits, memLevel, strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crc_for_crypting : crc of file to compress (needed for crypting) */ + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits, int memLevel, + int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting, int zip64); +/* Same as zipOpenNewFileInZip3 with zip64 support */ + +extern int ZEXPORT zipOpenNewFileInZip4(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits, int memLevel, + int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting, uint16_t version_madeby, uint16_t flag_base); +/* Same as zipOpenNewFileInZip3 except versionMadeBy & flag fields */ + +extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, const char *filename, const zip_fileinfo *zipfi, + const void *extrafield_local, uint16_t size_extrafield_local, const void *extrafield_global, + uint16_t size_extrafield_global, const char *comment, uint16_t method, int level, int raw, int windowBits, int memLevel, + int strategy, const char *password, ZIP_UNUSED uint32_t crc_for_crypting, uint16_t version_madeby, uint16_t flag_base, int zip64); +/* Same as zipOpenNewFileInZip4 with zip64 support */ + +extern int ZEXPORT zipOpenNewFileInZip5(zipFile file, + const char *filename, + const zip_fileinfo *zipfi, + const void *extrafield_local, + uint16_t size_extrafield_local, + const void *extrafield_global, + uint16_t size_extrafield_global, + const char *comment, + uint16_t flag_base, + int zip64, + uint16_t method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char *password, + int aes, + uint16_t version_madeby); +/* Allowing optional aes */ + +extern int ZEXPORT zipWriteInFileInZip(zipFile file, const void *buf, uint32_t len); +/* Write data in the zipfile */ + +extern int ZEXPORT zipCloseFileInZip(zipFile file); +/* Close the current file in the zipfile */ + +extern int ZEXPORT zipCloseFileInZipRaw(zipFile file, uint32_t uncompressed_size, uint32_t crc32); +extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, uint64_t uncompressed_size, uint32_t crc32); +/* Close the current file in the zipfile, for file opened with parameter raw=1 in zipOpenNewFileInZip2 + where raw is compressed data. Parameters uncompressed_size and crc32 are value for the uncompressed data. */ + +extern int ZEXPORT zipClose(zipFile file, const char *global_comment); +/* Close the zipfile */ + +extern int ZEXPORT zipClose_64(zipFile file, const char *global_comment); + +extern int ZEXPORT zipClose2_64(zipFile file, const char *global_comment, uint16_t version_madeby); +/* Same as zipClose_64 except version_madeby field */ + +/***************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* _ZIP_H */ diff --git a/TDAppDelegate.h b/TDAppDelegate.h new file mode 100644 index 0000000..bc7e573 --- /dev/null +++ b/TDAppDelegate.h @@ -0,0 +1,8 @@ +#import + +@interface TDAppDelegate : UIResponder + +@property (nonatomic, strong) UIWindow *window; +@property (nonatomic, strong) UINavigationController *rootViewController; + +@end diff --git a/TDAppDelegate.m b/TDAppDelegate.m new file mode 100644 index 0000000..993c1b7 --- /dev/null +++ b/TDAppDelegate.m @@ -0,0 +1,14 @@ +#import "TDAppDelegate.h" +#import "TDRootViewController.h" + +@implementation TDAppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + _window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + _rootViewController = [[UINavigationController alloc] initWithRootViewController:[[TDRootViewController alloc] init]]; + _window.rootViewController = _rootViewController; + [_window makeKeyAndVisible]; + return YES; +} + +@end diff --git a/TDDumpDecrypted.h b/TDDumpDecrypted.h new file mode 100644 index 0000000..ffe588d --- /dev/null +++ b/TDDumpDecrypted.h @@ -0,0 +1,17 @@ +@interface DumpDecrypted : NSObject { + char decryptedAppPathStr[PATH_MAX]; + char *filename; + char *appDirName; + char *appDirPath; +} + +@property (assign) NSString *appPath; +@property (assign) NSString *docPath; +@property (assign) NSString *appName; +@property (assign) NSString *appVersion; + +- (id)initWithPathToBinary:(NSString *)pathToBinary appName:(NSString *)appName appVersion:(NSString *)appVersion; +- (void)createIPAFile:(pid_t)pid; +- (BOOL)dumpDecryptedImage:(const struct mach_header *)image_mh fileName:(const char *)encryptedImageFilenameStr image:(int)imageNum task:(vm_map_t)targetTask; +- (NSString *)IPAPath; +@end \ No newline at end of file diff --git a/TDDumpDecrypted.m b/TDDumpDecrypted.m new file mode 100644 index 0000000..36aabda --- /dev/null +++ b/TDDumpDecrypted.m @@ -0,0 +1,615 @@ +/* + bfinject - Inject shared libraries into running App Store apps on iOS 11.x < 11.2 + https://github.com/BishopFox/bfinject + + Carl Livitt @ Bishop Fox + + Based on code originally by 10n1c: https://github.com/stefanesser/dumpdecrypted/blob/master/dumpdecrypted.c + Now with the following enhancements: + - Dump ALL encrypted images in the target application: the app itself, its frameworks, etc. + - Create a valid .ipa containing the decrypted binaries. Save it in ~/Documents/decrypted-app.ipa + - The .ipa can be modified and re-signed with a developer cert for redeployment to non-jailbroken devices + - Auto detection of all the necessary sandbox paths + - Converted into an Objective-C class for ease of use. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#import "SSZipArchive/SSZipArchive.h" +#import +#import + +#include +#include +#include +#include +#include + +#include "TDDumpDecrypted.h" +#import "TDUtils.h" + +kern_return_t +mach_vm_read_overwrite(vm_map_t, mach_vm_address_t, mach_vm_size_t, mach_vm_address_t, mach_vm_size_t *); +// kern_return_t mach_vm_read(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, vm_offset_t *data, mach_msg_type_number_t *dataCnt); +kern_return_t +mach_vm_region(vm_map_read_t target_task, mach_vm_address_t *address, mach_vm_size_t *size, vm_region_flavor_t flavor, vm_region_info_t info, mach_msg_type_number_t *infoCnt, mach_port_t *object_name); + + +//#define DEBUG(...) NSLog(__VA_ARGS__); +// #define DEBUG(...) {} + +#define swap32(value) (((value & 0xFF000000) >> 24) | ((value & 0x00FF0000) >> 8) | ((value & 0x0000FF00) << 8) | ((value & 0x000000FF) << 24) ) + +unsigned char *readProcessMemory (mach_port_t proc, mach_vm_address_t addr, mach_msg_type_number_t* size) { + mach_msg_type_number_t dataCnt = (mach_msg_type_number_t) *size; + vm_offset_t readMem; + + kern_return_t kr = vm_read(proc, addr, *size, &readMem, &dataCnt); + + if (kr) { + //fprintf (stderr, "Unable to read target task's memory @%p - kr 0x%x\n", (void *) addr, kr); + return NULL; + } + + return ((unsigned char *) readMem); +} + +static kern_return_t +readmem(mach_vm_offset_t *buffer, mach_vm_address_t address, mach_vm_size_t size, vm_map_t targetTask, vm_region_basic_info_data_64_t *info) +{ + // get task for pid + vm_map_t port = targetTask; + + kern_return_t kr; + mach_msg_type_number_t info_cnt = sizeof (vm_region_basic_info_data_64_t); + mach_port_t object_name; + mach_vm_size_t size_info; + mach_vm_address_t address_info = address; + kr = mach_vm_region(port, &address_info, &size_info, VM_REGION_BASIC_INFO_64, (vm_region_info_t)info, &info_cnt, &object_name); + if (kr) + { + fprintf(stderr, "[ERROR] mach_vm_region failed with error %d\n", (int)kr); + return KERN_FAILURE; + } + + /* read memory - vm_read_overwrite because we supply the buffer */ + mach_vm_size_t nread; + kr = mach_vm_read_overwrite(port, address, size, (mach_vm_address_t)buffer, &nread); + if (kr) + { + fprintf(stderr, "[ERROR] vm_read failed! %d\n", kr); + return KERN_FAILURE; + } + else if (nread != size) + { + fprintf(stderr, "[ERROR] vm_read failed! requested size: 0x%llx read: 0x%llx\n", size, nread); + return KERN_FAILURE; + } + return KERN_SUCCESS; +} + +int64_t +get_image_size(mach_vm_address_t address, vm_map_t targetTask, uint64_t *vmaddr_slide) +{ + vm_region_basic_info_data_64_t region_info = {0}; + // allocate a buffer to read the header info + // NOTE: this is not exactly correct since the 64bit version has an extra 4 bytes + // but this will work for this purpose so no need for more complexity! + struct mach_header header = {0}; + if (readmem((mach_vm_offset_t*)&header, address, sizeof(struct mach_header), targetTask, ®ion_info)) + { + printf("Can't read header!\n"); + return -1; + } + + if (header.magic != MH_MAGIC && header.magic != MH_MAGIC_64) + { + printf("[ERROR] Target is not a mach-o binary!\n"); + return -1; + } + + int64_t imagefilesize = -1; + /* read the load commands */ + uint8_t *loadcmds = (uint8_t*)malloc(header.sizeofcmds); + uint16_t mach_header_size = sizeof(struct mach_header); + if (header.magic == MH_MAGIC_64) + { + mach_header_size = sizeof(struct mach_header_64); + } + if (readmem((mach_vm_offset_t*)loadcmds, address+mach_header_size, header.sizeofcmds, targetTask, ®ion_info)) + { + printf("Can't read load commands\n"); + free(loadcmds); + return -1; + } + + /* process and retrieve address and size of linkedit */ + uint8_t *loadCmdAddress = 0; + loadCmdAddress = (uint8_t*)loadcmds; + struct load_command *loadCommand = NULL; + struct segment_command *segCmd = NULL; + struct segment_command_64 *segCmd64 = NULL; + for (uint32_t i = 0; i < header.ncmds; i++) + { + loadCommand = (struct load_command*)loadCmdAddress; + if (loadCommand->cmd == LC_SEGMENT) + { + segCmd = (struct segment_command*)loadCmdAddress; + if (strncmp(segCmd->segname, "__PAGEZERO", 16) != 0) + { + if (strncmp(segCmd->segname, "__TEXT", 16) == 0) + { + *vmaddr_slide = address - segCmd->vmaddr; + } + imagefilesize += segCmd->filesize; + } + } + else if (loadCommand->cmd == LC_SEGMENT_64) + { + segCmd64 = (struct segment_command_64*)loadCmdAddress; + if (strncmp(segCmd64->segname, "__PAGEZERO", 16) != 0) + { + if (strncmp(segCmd64->segname, "__TEXT", 16) == 0) + { + *vmaddr_slide = address - segCmd64->vmaddr; + } + imagefilesize += segCmd64->filesize; + } + } + // advance to next command + loadCmdAddress += loadCommand->cmdsize; + } + free(loadcmds); + return imagefilesize; +} + +int find_off_cryptid(const char *filePath) { + NSLog(@"[trolldecrypt] %s filePath: %s", __FUNCTION__, filePath); + int off_cryptid = 0; + FILE* file = fopen(filePath, "rb"); + if (!file) return 1; + + fseek(file, 0, SEEK_END); + long fileSize = ftell(file); + fseek(file, 0, SEEK_SET); + + char* buffer = malloc(fileSize);//new char[fileSize]; + fread(buffer, 1, fileSize, file); + + struct mach_header_64* header = (struct mach_header_64*)buffer; + if (header->magic != MH_MAGIC_64) { + printf("[-] error: not a valid macho file\n"); + free(buffer); + fclose(file); + return 2; + } + + struct load_command* lc = (struct load_command*)((mach_vm_address_t)header + sizeof(struct mach_header_64)); + for (uint32_t i = 0; i < header->ncmds; i++) { + if (lc->cmd == LC_ENCRYPTION_INFO || lc->cmd == LC_ENCRYPTION_INFO_64) { + struct encryption_info_command *encryption_info = (struct encryption_info_command*) lc; + + + NSLog(@"[trolldecrypt] cryptid: %d\n", encryption_info->cryptid); + NSLog(@"[trolldecrypt] Found cryptid at offset: 0x%llx\n", (mach_vm_address_t)lc + offsetof(struct encryption_info_command, cryptid) - (mach_vm_address_t)header); + + off_cryptid = (mach_vm_address_t)lc + offsetof(struct encryption_info_command, cryptid) - (mach_vm_address_t)header; + break; + } + lc = (struct load_command*)((mach_vm_address_t)lc + lc->cmdsize); + } + free(buffer); + fclose(file); + + return off_cryptid; +} + +@implementation DumpDecrypted + +-(id) initWithPathToBinary:(NSString *)pathToBinary appName:(NSString *)appName appVersion:(NSString *)appVersion { + if(!self) { + self = [super init]; + } + + self.appName = appName; + self.appVersion = appVersion; + + [self setAppPath:[pathToBinary stringByDeletingLastPathComponent]]; + [self setDocPath:[NSString stringWithFormat:@"%@/Documents", NSHomeDirectory()]]; + + char *lastPartOfAppPath = strdup([[self appPath] UTF8String]); + lastPartOfAppPath = strrchr(lastPartOfAppPath, '/') + 1; + NSLog(@"[trolldecrypt] init: appDirName: %s", lastPartOfAppPath); + self->appDirName = strdup(lastPartOfAppPath); + + return self; +} + + +-(void) makeDirectories:(const char *)encryptedImageFilenameStr { + char *appPath = (char *)[[self appPath] UTF8String]; + char *docPath = (char *)[[self docPath] UTF8String]; + char *savePtr; + char *encryptedImagePathStr = savePtr = strdup(encryptedImageFilenameStr); + self->filename = strdup(strrchr(encryptedImagePathStr, '/') + 1); + + // Normalize the filenames + if(strstr(encryptedImagePathStr, "/private") == encryptedImagePathStr) + encryptedImagePathStr += 8; + if(strstr(appPath, "/private") == appPath) + appPath += 8; + + // Find start of image path, relative to the base of the app sandbox (ie. /var/mobile/.../FooBar.app/THIS_PART_HERE) + encryptedImagePathStr += strlen(appPath) + 1; // skip over the app path + char *p = strrchr(encryptedImagePathStr, '/'); + if(p) + *p = '\0'; + + NSLog(@"[trolldecrypt] encryptedImagePathStr: %s", encryptedImagePathStr); + + NSFileManager *fm = [[NSFileManager alloc] init]; + NSError *err; + char *lastPartOfAppPath = strdup(appPath); // Must free() + lastPartOfAppPath = strrchr(lastPartOfAppPath, '/'); + lastPartOfAppPath++; + NSString *path = [NSString stringWithFormat:@"%s/ipa/Payload/%s", docPath, lastPartOfAppPath]; + self->appDirPath = strdup([path UTF8String]); + if(p) + path = [NSString stringWithFormat:@"%@/%s", path, encryptedImagePathStr]; + + NSLog(@"[trolldecrypt] make_directories making dir: %@", path); + if(! [fm createDirectoryAtPath:path withIntermediateDirectories:true attributes:nil error:&err]) { + NSLog(@"[trolldecrypt] WARNING: make_directories failed to make directory %@. Error: %@", path, err); + } + + free(savePtr); + + snprintf(self->decryptedAppPathStr, PATH_MAX, "%s/%s", [path UTF8String], self->filename); + + return; +} + + +-(BOOL) dumpDecryptedImage:(vm_address_t)imageAddress fileName:(const char *)encryptedImageFilenameStr image:(int)imageNum task:(vm_map_t)targetTask{ + // struct load_command *lc; + struct encryption_info_command *eic; + struct fat_header *fh; + struct fat_arch *arch; + // struct mach_header *mh; + char buffer[1024]; + unsigned int fileoffs = 0, off_cryptid = 0, restsize; + int i, fd, outfd, r, n; + + struct mach_header header = {0}; + vm_region_basic_info_data_64_t region_info = {0}; + if (readmem((mach_vm_offset_t*)&header, imageAddress, sizeof(struct mach_header), targetTask, ®ion_info)) + { + NSLog(@"[trolldecrypt] Can't read header!"); + exit(1); + } + //XXX: change all image_mh -> header NOW + + struct load_command *lc = (struct load_command*)malloc(header.sizeofcmds); + + /* detect if this is a arm64 binary */ + if (header.magic == MH_MAGIC_64) { + // lc = (struct load_command *)((unsigned char *)header + sizeof(struct mach_header_64)); + readmem((mach_vm_offset_t*)lc, imageAddress+sizeof(struct mach_header_64), header.sizeofcmds, targetTask, ®ion_info); + NSLog(@"[trolldecrypt] detected 64bit ARM binary in memory.\n"); + } else if(header.magic == MH_MAGIC) { /* we might want to check for other errors here, too */ + // lc = (struct load_command *)((unsigned char *)header + sizeof(struct mach_header)); + readmem((mach_vm_offset_t*)lc, imageAddress+sizeof(struct mach_header), header.sizeofcmds, targetTask, ®ion_info); + NSLog(@"[trolldecrypt] detected 32bit ARM binary in memory.\n"); + } else { + NSLog(@"[trolldecrypt] No valid header found!!"); + return false; + } + + + // sleep(1);exit(1); + const struct mach_header *image_mh = &header; + + /* searching all load commands for an LC_ENCRYPTION_INFO load command */ + for (i=0; incmds; i++) { + if (lc->cmd == LC_ENCRYPTION_INFO || lc->cmd == LC_ENCRYPTION_INFO_64) { + eic = (struct encryption_info_command *)lc; + // struct encryption_info_command *eic = (uint8_t*)malloc(sizeof(struct encryption_info_command);); + // readmem((mach_vm_offset_t*)eic, imageAddress+sizeof(struct mach_header_64), header.sizeofcmds, targetTask, ®ion_info); + + const char *appFilename = strrchr(encryptedImageFilenameStr, '/'); + if(appFilename == NULL) { + NSLog(@"[trolldecrypt] There are no / in the filename. This is an error.\n"); + return false; + } + appFilename++; + + /* If this load command is present, but data is not crypted then exit */ + if (eic->cryptid == 0) { + NSLog(@"[trolldecrypt] CryptID = 0!! "); + return false; + } + + // Create a dir structure in ~ just like in /path/to/FooApp.app/Whatever + [self makeDirectories:encryptedImageFilenameStr]; + + //0x1518 + uint32_t aslr_slide = 0; + get_image_size(imageAddress, targetTask, &aslr_slide); + NSLog(@"[trolldecrypt] aslr_slide= 0x%x", aslr_slide); + + off_cryptid=find_off_cryptid(encryptedImageFilenameStr); + + NSLog(@"[trolldecrypt] offset to cryptid (%d) found in memory @ %p (from %p). off_cryptid = %u (0x%x)\n", eic->cryptid, &eic->cryptid, image_mh, off_cryptid, off_cryptid); + //NSLog(@"[trolldecrypt] Found encrypted data at offset %u 0x%08x. image_mh @ %p. cryptedData @ 0x%x. cryptsize = %u (0x%x) bytes.\n", eic->cryptoff, eic->cryptoff, image_mh, (unsigned int)image_mh + eic->cryptoff, eic->cryptsize, eic->cryptsize); + + NSLog(@"[trolldecrypt] Dumping: %s", encryptedImageFilenameStr); + NSLog(@"[trolldecrypt] Into: %s", self->decryptedAppPathStr); + fd = open(encryptedImageFilenameStr, O_RDONLY); + if (fd == -1) { + NSLog(@"[trolldecrypt] Failed to open %s", encryptedImageFilenameStr); + return false; + } + + NSLog(@"[trolldecrypt] Reading header"); + n = read(fd, (void *)buffer, sizeof(buffer)); + if (n != sizeof(buffer)) { + NSLog(@"[trolldecrypt] Warning read only %d of %lu bytes from encrypted file.\n", n, sizeof(buffer)); + return false; + } + + NSLog(@"[trolldecrypt] Detecting header type\n"); + fh = (struct fat_header *)buffer; + + /* Is this a FAT file - we assume the right endianess */ + if (fh->magic == FAT_CIGAM) { + NSLog(@"[trolldecrypt] Executable is a FAT image - searching for right architecture\n"); + arch = (struct fat_arch *)&fh[1]; + for (i=0; infat_arch); i++) { + if ((image_mh->cputype == swap32(arch->cputype)) && (image_mh->cpusubtype == swap32(arch->cpusubtype))) { + fileoffs = swap32(arch->offset); + NSLog(@"[trolldecrypt] Correct arch is at offset 0x%x in the file.\n", fileoffs); + break; + } + arch++; + } + if (fileoffs == 0) { + NSLog(@"[trolldecrypt] Could not find correct arch in FAT image\n"); + return false; + } + } else if (fh->magic == MH_MAGIC || fh->magic == MH_MAGIC_64) { + NSLog(@"[trolldecrypt] Executable is a plain MACH-O image, fileoffs = 0\n"); + } else { + NSLog(@"[trolldecrypt] Executable is of unknown type, fileoffs = 0\n"); + return false; + } + + NSLog(@"[trolldecrypt] Opening %s for writing.\n", decryptedAppPathStr); + outfd = open(decryptedAppPathStr, O_RDWR|O_CREAT|O_TRUNC, 0644); + if (outfd == -1) { + NSLog(@"[trolldecrypt] Failed opening: "); + return false; + } + + /* calculate address of beginning of crypted data */ + n = fileoffs + eic->cryptoff; + + restsize = lseek(fd, 0, SEEK_END) - n - eic->cryptsize; + //NSLog(@"[trolldecrypt] restsize = %u, n = %u, cryptsize = %u, total = %u", restsize, n, eic->cryptsize, n + eic->cryptsize + restsize); + lseek(fd, 0, SEEK_SET); + + NSLog(@"[trolldecrypt] Copying the not encrypted start of the file (%u bytes)\n", n); + + /* first copy all the data before the encrypted data */ + char *buf = (char *)malloc((size_t)n); + r = read(fd, buf, n); + if(r != n) { + NSLog(@"[trolldecrypt] Error reading start of file\n"); + return false; + } + r = write(outfd, buf, n); + if(r != n) { + NSLog(@"[trolldecrypt] Error writing start of file\n"); + return false; + } + free(buf); + + /* now write the previously encrypted data */ + + NSLog(@"[trolldecrypt] Dumping the decrypted data into the file (%u bytes)\n", eic->cryptsize); + buf = (char *)malloc((size_t)eic->cryptsize); + readmem((mach_vm_offset_t*)buf, imageAddress+eic->cryptoff, eic->cryptsize, targetTask, ®ion_info); + // r = write(outfd, (unsigned char *)image_mh + eic->cryptoff, eic->cryptsize); + r = write(outfd, buf, eic->cryptsize); + if (r != eic->cryptsize) { + NSLog(@"[trolldecrypt] Error writing encrypted part of file\n"); + return false; + } + free(buf); + + + /* and finish with the remainder of the file */ + NSLog(@"[trolldecrypt] Copying the not encrypted remainder of the file (%u bytes)\n", restsize); + lseek(fd, eic->cryptsize, SEEK_CUR); + buf = (char *)malloc((size_t)restsize); + r = read(fd, buf, restsize); + if (r != restsize) { + NSLog(@"[trolldecrypt] Error reading rest of file, got %u bytes\n", r); + return false; + } + r = write(outfd, buf, restsize); + if (r != restsize) { + NSLog(@"[trolldecrypt] Error writing rest of file\n"); + return false; + } + free(buf); + + + if (off_cryptid) { + uint32_t zero=0; + NSLog(@"[trolldecrypt] Setting the LC_ENCRYPTION_INFO->cryptid to 0 at offset 0x%x into file\n", off_cryptid); + if (lseek(outfd, off_cryptid, SEEK_SET) == off_cryptid) { + if(write(outfd, &zero, 4) != 4) { + NSLog(@"[trolldecrypt] Error writing cryptid value!!\n"); + // Not a fatal error, just warn + } + } else { + NSLog(@"[trolldecrypt] Failed to seek to cryptid offset!!"); + // this error is not treated as fatal + } + } + + close(fd); + close(outfd); + sync(); + // exit(1); + + return true; + } + + lc = (struct load_command *)((unsigned char *)lc+lc->cmdsize); + // readmem((mach_vm_offset_t*)lc, lc+lc->cmdsize, header.sizeofcmds, targetTask, ®ion_info); + } + // DEBUG(@"[!] This mach-o file is not encrypted. Nothing was decrypted.\n"); + return false; +} + + +-(void) dumpDecrypted:(pid_t)pid { + + vm_map_t targetTask = 0; + if (task_for_pid(mach_task_self(), pid, &targetTask)) + { + NSLog(@"[trolldecrypt] Can't execute task_for_pid! Do you have the right permissions/entitlements?\n"); + exit(1); + } + + //numberOfImages + struct task_dyld_info dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + + if(task_info(targetTask, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count) != KERN_SUCCESS) exit(1); + + mach_msg_type_number_t size = sizeof(struct dyld_all_image_infos); + uint8_t* data = readProcessMemory(targetTask, dyld_info.all_image_info_addr, &size); + struct dyld_all_image_infos* infos = (struct dyld_all_image_infos *) data; + + mach_msg_type_number_t size2 = sizeof(struct dyld_image_info) * infos->infoArrayCount; + uint8_t* info_addr = readProcessMemory(targetTask, (mach_vm_address_t) infos->infoArray, &size2); + struct dyld_image_info* info = (struct dyld_image_info*) info_addr; + + uint32_t numberOfImages = infos->infoArrayCount; + mach_vm_address_t imageAddress = 0; + const char *appPath = [[self appPath] UTF8String]; + + NSLog(@"[trolldecrypt] There are %d images mapped.", numberOfImages); + + for (int i = 0; i < numberOfImages; i++) { + NSLog(@"[trolldecrypt] image %d", i); + mach_msg_type_number_t size3 = PATH_MAX; + uint8_t *fpath_addr = readProcessMemory(targetTask, (mach_vm_address_t) info[i].imageFilePath, &size3); + + imageAddress = (struct mach_header *)info[i].imageLoadAddress; + const char *imageName = fpath_addr; + + if(!imageName || !imageAddress) + continue; + + NSLog(@"[trolldecrypt] Comparing %s to %s", imageName, appPath); + + if(strstr(imageName, appPath) != NULL) { + NSLog(@"[trolldecrypt] Dumping image %d: %s", i, imageName); + [self dumpDecryptedImage:imageAddress fileName:imageName image:i task: targetTask]; + } + } +} + + +-(BOOL)fileManager:(NSFileManager *)f shouldProceedAfterError:(BOOL)proceed copyingItemAtPath:(NSString *)path toPath:(NSString *)dest { + return true; +} + +-(NSString *)IPAPath { + return [NSString stringWithFormat:@"%@/%@_%@_decrypted.ipa", [self docPath], self.appName, self.appVersion]; +} + +-(void) createIPAFile:(pid_t)pid { + NSString *IPAFile = [self IPAPath]; + NSString *appDir = [self appPath]; + NSString *appCopyDir = [NSString stringWithFormat:@"%@/ipa/Payload/%s", [self docPath], self->appDirName]; + NSString *zipDir = [NSString stringWithFormat:@"%@/ipa", [self docPath]]; + NSFileManager *fm = [[NSFileManager alloc] init]; + NSError *err; + + [fm removeItemAtPath:IPAFile error:nil]; + [fm removeItemAtPath:appCopyDir error:nil]; + [fm createDirectoryAtPath:appCopyDir withIntermediateDirectories:true attributes:nil error:nil]; + + [fm setDelegate:(id)self]; + + NSLog(@"[trolldecrypt] ======== START FILE COPY - IGNORE ANY SANDBOX WARNINGS ========"); + NSLog(@"[trolldecrypt] IPAFile: %@", IPAFile); + NSLog(@"[trolldecrypt] appDir: %@", appDir); + NSLog(@"[trolldecrypt] appCopyDir: %@", appCopyDir); + NSLog(@"[trolldecrypt] zipDir: %@", zipDir); + + [fm copyItemAtPath:appDir toPath:appCopyDir error:&err]; + NSLog(@"[trolldecrypt] ======== END OF FILE COPY ========"); + // sleep(1); + // exit(1); + // Replace encrypted binaries with decrypted versions + NSLog(@"[trolldecrypt] ======== START DECRYPTION PROCESS ========"); + [self dumpDecrypted:pid]; + NSLog(@"[trolldecrypt] ======== DECRYPTION COMPLETE ========"); + + // ZIP it up + NSLog(@"[trolldecrypt] ======== STARTING ZIP ========"); + NSLog(@"[trolldecrypt] IPA file: %@", IPAFile); + NSLog(@"[trolldecrypt] ZIP dir: %@", zipDir); + unlink([IPAFile UTF8String]); + @try { + BOOL success = [SSZipArchive createZipFileAtPath:IPAFile + withContentsOfDirectory:zipDir + keepParentDirectory:NO + compressionLevel:1 + password:nil + AES:NO + progressHandler:nil + ]; + NSLog(@"[trolldecrypt] ======== ZIP operation complete: %s ========", (success)?"success":"failed"); + } + @catch(NSException *e) { + NSLog(@"[trolldecrypt] BAAAAAAAARF during ZIP operation!!! , %@", e); + } + + + // Clean up. Leave only the .ipa file. + [fm removeItemAtPath:zipDir error:nil]; + + NSLog(@"[trolldecrypt] ======== Wrote %@ ========", [self IPAPath]); + return; +} + +@end \ No newline at end of file diff --git a/TDFileManagerViewController.h b/TDFileManagerViewController.h new file mode 100644 index 0000000..cc3aaae --- /dev/null +++ b/TDFileManagerViewController.h @@ -0,0 +1,7 @@ +#import + +@interface TDFileManagerViewController : UITableViewController + +@property (nonatomic, strong) NSArray *fileList; + +@end \ No newline at end of file diff --git a/TDFileManagerViewController.m b/TDFileManagerViewController.m new file mode 100644 index 0000000..46bdac5 --- /dev/null +++ b/TDFileManagerViewController.m @@ -0,0 +1,97 @@ +#import "TDFileManagerViewController.h" +#import "TDUtils.h" + +@implementation TDFileManagerViewController + +- (void)loadView { + [super loadView]; + + self.title = @"Decrypted IPAs"; + self.fileList = decryptedFileList(); + + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(done)]; +} + +- (void)refresh { + self.fileList = decryptedFileList(); + [self.tableView reloadData]; +} + +- (void)done { + [self dismissViewControllerAnimated:YES completion:nil]; +} + +- (NSInteger)numberOfSelectionsInTableView:(UITableView *)tableView { + return 1; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return self.fileList.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + static NSString *cellIdentifier = @"FileCell"; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; + + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier]; + } + + NSString *path = [docPath() stringByAppendingPathComponent:self.fileList[indexPath.row]]; + NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil]; + NSDate *date = attributes[NSFileModificationDate]; + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:@"MMM d, yyyy h:mm a"]; + + NSNumber *fileSize = attributes[NSFileSize]; + + + cell.textLabel.text = self.fileList[indexPath.row]; + cell.detailTextLabel.text = [dateFormatter stringFromDate:date]; + cell.detailTextLabel.textColor = [UIColor systemGray2Color]; + cell.imageView.image = [UIImage systemImageNamed:@"doc.fill"]; + + UILabel *label = [[UILabel alloc] init]; + label.text = [NSString stringWithFormat:@"%.2f MB", [fileSize doubleValue] / 1000000.0f]; + label.textColor = [UIColor systemGray2Color]; + label.font = [UIFont systemFontOfSize:12.0f]; + [label sizeToFit]; + label.textAlignment = NSTextAlignmentCenter; + cell.accessoryView = label; + + + return cell; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + return 65.0f; +} + +- (bool)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { + return YES; +} + +- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath { + UIContextualAction *deleteAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive title:@"Delete" handler:^(UIContextualAction *action, UIView *sourceView, void (^completionHandler)(BOOL)) { + NSString *file = self.fileList[indexPath.row]; + NSString *path = [docPath() stringByAppendingPathComponent:file]; + [[NSFileManager defaultManager] removeItemAtPath:path error:nil]; + [self refresh]; + }]; + + UISwipeActionsConfiguration *swipeActions = [UISwipeActionsConfiguration configurationWithActions:@[deleteAction]]; + return swipeActions; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + NSString *file = self.fileList[indexPath.row]; + NSString *path = [docPath() stringByAppendingPathComponent:file]; + NSURL *url = [NSURL fileURLWithPath:path]; + + UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[url] applicationActivities:nil]; + [self presentViewController:activityViewController animated:YES completion:nil]; + + [tableView deselectRowAtIndexPath:indexPath animated:YES]; +} + +@end \ No newline at end of file diff --git a/TDRootViewController.h b/TDRootViewController.h new file mode 100644 index 0000000..7b50b79 --- /dev/null +++ b/TDRootViewController.h @@ -0,0 +1,7 @@ +#import + +@interface TDRootViewController : UITableViewController + +@property (nonatomic, strong) NSArray *apps; + +@end diff --git a/TDRootViewController.m b/TDRootViewController.m new file mode 100644 index 0000000..1a5e1d0 --- /dev/null +++ b/TDRootViewController.m @@ -0,0 +1,109 @@ +#import "TDRootViewController.h" +#import "TDFileManagerViewController.h" +#import "TDUtils.h" + +@implementation TDRootViewController + +- (void)loadView { + [super loadView]; + + self.apps = appList(); + self.title = @"TrollDecrypt"; + self.navigationController.navigationBar.prefersLargeTitles = YES; + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"info.circle"] style:UIBarButtonItemStylePlain target:self action:@selector(about:)]; + self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"folder"] style:UIBarButtonItemStylePlain target:self action:@selector(openDocs:)]; + + UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init]; + [refreshControl addTarget:self action:@selector(refreshApps:) forControlEvents:UIControlEventValueChanged]; + self.refreshControl = refreshControl; +} + +- (void)openDocs:(id)sender { + TDFileManagerViewController *fmVC = [[TDFileManagerViewController alloc] init]; + UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:fmVC]; + [self presentViewController:navController animated:YES completion:nil]; +} + +- (void)about:(id)sender { + UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"TrollDecrypt" message:@"by fiore\nIcon by @super.user\nbfdecrypt by @bishopfox\ndumpdecrypted by @i0n1c\nUpdated for TrollStore by @wh1te4ever" preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:@"Dismiss" style:UIAlertActionStyleCancel handler:nil]]; + [self presentViewController:alert animated:YES completion:nil]; +} + +- (void)refreshApps:(UIRefreshControl *)refreshControl { + self.apps = appList(); + [self.tableView reloadData]; + [refreshControl endRefreshing]; +} + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 1; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return self.apps.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + static NSString *cellIdentifier = @"AppCell"; + UITableViewCell *cell; + if (([self.apps count] - 1) != indexPath.row) { + cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; + if (cell == nil) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier]; + + NSDictionary *app = self.apps[indexPath.row]; + + cell.textLabel.text = app[@"name"]; + cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ • %@", app[@"version"], app[@"bundleID"]]; + cell.imageView.image = [UIImage _applicationIconImageForBundleIdentifier:app[@"bundleID"] format:iconFormat() scale:[UIScreen mainScreen].scale]; + } else { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil]; + + cell.textLabel.text = @"Advanced"; + cell.detailTextLabel.text = @"Decrypt app from a specified PID"; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + } + + return cell; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + return 80.0f; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + UIAlertController *alert; + + if (([self.apps count] - 1) != indexPath.row) { + NSDictionary *app = self.apps[indexPath.row]; + + alert = [UIAlertController alertControllerWithTitle:@"Decrypt" message:[NSString stringWithFormat:@"Decrypt %@?", app[@"name"]] preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; + UIAlertAction *decrypt = [UIAlertAction actionWithTitle:@"Yes" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { + decryptApp(app); + }]; + + [alert addAction:decrypt]; + [alert addAction:cancel]; + } else { + alert = [UIAlertController alertControllerWithTitle:@"Decrypt" message:@"Enter PID to decrypt" preferredStyle:UIAlertControllerStyleAlert]; + [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) { + textField.placeholder = @"PID"; + textField.keyboardType = UIKeyboardTypeNumberPad; + }]; + + UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; + UIAlertAction *decrypt = [UIAlertAction actionWithTitle:@"Decrypt" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { + NSString *pid = alert.textFields.firstObject.text; + decryptAppWithPID([pid intValue]); + }]; + + [alert addAction:decrypt]; + [alert addAction:cancel]; + } + + [self presentViewController:alert animated:YES completion:nil]; + [tableView deselectRowAtIndexPath:indexPath animated:YES]; +} + +@end \ No newline at end of file diff --git a/TDUtils.h b/TDUtils.h new file mode 100644 index 0000000..d7f61de --- /dev/null +++ b/TDUtils.h @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#import +#import +#import + +@interface UIApplication (tweakName) ++ (id)sharedApplication; +- (BOOL)launchApplicationWithIdentifier:(id)arg1 suspended:(BOOL)arg2; +@end + +@interface LSApplicationProxy : NSObject +@property (readonly, nonatomic) NSString *bundleIdentifier; // ivar: _bundleIdentifier +@property (readonly) NSString *shortVersionString; +@property (readonly, nonatomic) NSString *canonicalExecutablePath; +@property (getter=isLaunchProhibited) BOOL launchProhibited; +@property (readonly, nonatomic) NSArray *appTags; +@property(readonly) NSString * applicationType; +- (NSString *)localizedName; ++ (instancetype)applicationProxyForIdentifier:(NSString*)identifier; +@end + +@interface LSApplicationWorkspace : NSObject ++ (instancetype)defaultWorkspace; +- (NSMutableArray *)allApplications; +- (NSArray *)allInstalledApplications; +@end + +@interface UIImage (Private) ++ (UIImage *)_applicationIconImageForBundleIdentifier:(NSString *)bundleIdentifier format:(NSUInteger)format scale:(CGFloat)scale; +@end + +#define PROC_PIDPATHINFO 11 +#define PROC_PIDPATHINFO_SIZE (MAXPATHLEN) +#define PROC_PIDPATHINFO_MAXSIZE (4 * MAXPATHLEN) +#define PROC_ALL_PIDS 1 +#ifndef DEBUG +# define NSLog(...) (void)0 +#endif + +int proc_pidpath(int pid, void *buffer, uint32_t buffersize); +int proc_listpids(uint32_t type, uint32_t typeinfo, void *buffer, int buffersize); +NSArray *appList(void); +NSUInteger iconFormat(void); +NSArray *sysctl_ps(void); +void decryptApp(NSDictionary *app); +void decryptAppWithPID(pid_t pid); +void bfinject_rocknroll(pid_t pid, NSString *appName, NSString *version); +NSArray *decryptedFileList(void); +NSString *docPath(void); \ No newline at end of file diff --git a/TDUtils.m b/TDUtils.m new file mode 100644 index 0000000..c907ab7 --- /dev/null +++ b/TDUtils.m @@ -0,0 +1,372 @@ +#import "TDUtils.h" +#import "TDDumpDecrypted.h" + +UIWindow *alertWindow = NULL; +UIWindow *kw = NULL; +UIViewController *root = NULL; +UIAlertController *alertController = NULL; +UIAlertController *doneController = NULL; +UIAlertController *errorController = NULL; + +NSArray *appList(void) { + NSMutableArray *apps = [NSMutableArray array]; + + for (LSApplicationProxy *app in [[LSApplicationWorkspace defaultWorkspace] allInstalledApplications]) { + if (![[app applicationType] isEqualToString:@"User"]) continue; + + NSString *bundleID = app.bundleIdentifier; + NSString *name = [app localizedName]; + NSString *version = app.shortVersionString; + NSString *executable = [app canonicalExecutablePath]; + + NSDictionary *item = @{ + @"bundleID":bundleID, + @"name":name, + @"version":version, + @"executable":executable + }; + + [apps addObject:item]; + } + + NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]; + [apps sortUsingDescriptors:@[descriptor]]; + + [apps addObject:@{@"bundleID":@"", @"name":@"", @"version":@"", @"executable":@""}]; + + return [apps copy]; +} + +NSUInteger iconFormat(void) { + return (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) ? 8 : 10; +} + +NSArray *sysctl_ps(void) { + // int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; + // size_t miblen = 4; + // size_t size; + // int st = sysctl(mib, miblen, NULL, &size, NULL, 0); + // struct kinfo_proc * process = NULL; + // struct kinfo_proc * newprocess = NULL; + + // do { + // size += size / 10; + // newprocess = realloc(process, size); + // if (!newprocess){ + // if (process){ + // free(process); + // } + // return nil; + // } + // process = newprocess; + // st = sysctl(mib, miblen, process, &size, NULL, 0); + // } while (st == -1 && errno == ENOMEM); + + // if (st == 0) { + // if (size % sizeof(struct kinfo_proc) == 0){ + // int nprocess = size / sizeof(struct kinfo_proc); + // if (nprocess){ + // NSMutableArray * array = [[NSMutableArray alloc] init]; + // for (int i = nprocess - 1; i >= 0; i--){ + // NSString *processID = [[NSString alloc] initWithFormat:@"%d", process[i].kp_proc.p_pid]; + // NSString *processName = [[NSString alloc] initWithFormat:@"%s", process[i].kp_proc.p_comm]; + // NSDictionary *dict = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:processID, processName, nil] forKeys:[NSArray arrayWithObjects:@"pid", @"proc_name", nil]]; + // [array addObject:dict]; + // } + // free(process); + // return array; + // } + // } + // } + NSMutableArray *array = [[NSMutableArray alloc] init]; + + int numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0); + pid_t pids[numberOfProcesses]; + bzero(pids, sizeof(pids)); + proc_listpids(PROC_ALL_PIDS, 0, pids, sizeof(pids)); + for (int i = 0; i < numberOfProcesses; ++i) { + if (pids[i] == 0) { continue; } + char pathBuffer[PROC_PIDPATHINFO_MAXSIZE]; + bzero(pathBuffer, PROC_PIDPATHINFO_MAXSIZE); + proc_pidpath(pids[i], pathBuffer, sizeof(pathBuffer)); + + if (strlen(pathBuffer) > 0) { + NSString *processID = [[NSString alloc] initWithFormat:@"%d", pids[i]]; + NSString *processName = [[NSString stringWithUTF8String:pathBuffer] lastPathComponent]; + NSDictionary *dict = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:processID, processName, nil] forKeys:[NSArray arrayWithObjects:@"pid", @"proc_name", nil]]; + + [array addObject:dict]; + } + } + + return [array copy]; +} + +void decryptApp(NSDictionary *app) { + dispatch_async(dispatch_get_main_queue(), ^{ + alertWindow = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds]; + alertWindow.rootViewController = [UIViewController new]; + alertWindow.windowLevel = UIWindowLevelAlert + 1; + [alertWindow makeKeyAndVisible]; + + // Show a "Decrypting!" alert on the device and block the UI + + kw = alertWindow; + if([kw respondsToSelector:@selector(topmostPresentedViewController)]) + root = [kw performSelector:@selector(topmostPresentedViewController)]; + else + root = [kw rootViewController]; + root.modalPresentationStyle = UIModalPresentationFullScreen; + }); + + NSLog(@"[trolldecrypt] spawning thread to do decryption in background..."); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSLog(@"[trolldecrypt] inside decryption thread."); + + NSString *bundleID = app[@"bundleID"]; + NSString *name = app[@"name"]; + NSString *version = app[@"version"]; + NSString *executable = app[@"executable"]; + NSString *binaryName = [executable lastPathComponent]; + + NSLog(@"[trolldecrypt] bundleID: %@", bundleID); + NSLog(@"[trolldecrypt] name: %@", name); + NSLog(@"[trolldecrypt] version: %@", version); + NSLog(@"[trolldecrypt] executable: %@", executable); + NSLog(@"[trolldecrypt] binaryName: %@", binaryName); + + [[UIApplication sharedApplication] launchApplicationWithIdentifier:bundleID suspended:YES]; + sleep(1); + + pid_t pid = -1; + NSArray *processes = sysctl_ps(); + for (NSDictionary *process in processes) { + NSString *proc_name = process[@"proc_name"]; + if ([proc_name isEqualToString:binaryName]) { + pid = [process[@"pid"] intValue]; + break; + } + } + + if (pid == -1) { + dispatch_async(dispatch_get_main_queue(), ^{ + [alertController dismissViewControllerAnimated:NO completion:nil]; + NSLog(@"[trolldecrypt] failed to get pid for binary name: %@", binaryName); + + errorController = [UIAlertController alertControllerWithTitle:@"Error: -1" message:[NSString stringWithFormat:@"Failed to get PID for binary name: %@", binaryName] preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Ok", @"Ok") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { + NSLog(@"[trolldecrypt] Ok action"); + [errorController dismissViewControllerAnimated:NO completion:nil]; + [kw removeFromSuperview]; + kw.hidden = YES; + }]; + + [errorController addAction:okAction]; + [root presentViewController:errorController animated:YES completion:nil]; + }); + + return; + } + + NSLog(@"[trolldecrypt] pid: %d", pid); + + bfinject_rocknroll(pid, name, version); + }); +} + +void bfinject_rocknroll(pid_t pid, NSString *appName, NSString *version) { + NSLog(@"[trolldecrypt] Spawning thread to do decryption in the background..."); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSLog(@"[trolldecrypt] Inside decryption thread"); + + char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; + // int proc_pidpath(pid, pathbuf, sizeof(pathbuf)); + proc_pidpath(pid, pathbuf, sizeof(pathbuf)); + const char *fullPathStr = pathbuf; + + + NSLog(@"[trolldecrypt] fullPathStr: %s", fullPathStr); + DumpDecrypted *dd = [[DumpDecrypted alloc] initWithPathToBinary:[NSString stringWithUTF8String:fullPathStr] appName:appName appVersion:version]; + if(!dd) { + NSLog(@"[trolldecrypt] ERROR: failed to get DumpDecrypted instance"); + return; + } + + NSLog(@"[trolldecrypt] Full path to app: %s /// IPA File: %@", fullPathStr, [dd IPAPath]); + + dispatch_async(dispatch_get_main_queue(), ^{ + alertWindow = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds]; + alertWindow.rootViewController = [UIViewController new]; + alertWindow.windowLevel = UIWindowLevelAlert + 1; + [alertWindow makeKeyAndVisible]; + + // Show a "Decrypting!" alert on the device and block the UI + alertController = [UIAlertController + alertControllerWithTitle:@"Decrypting" + message:@"Please wait, this will take a few seconds..." + preferredStyle:UIAlertControllerStyleAlert]; + + kw = alertWindow; + if([kw respondsToSelector:@selector(topmostPresentedViewController)]) + root = [kw performSelector:@selector(topmostPresentedViewController)]; + else + root = [kw rootViewController]; + root.modalPresentationStyle = UIModalPresentationFullScreen; + [root presentViewController:alertController animated:YES completion:nil]; + }); + + // Do the decryption + [dd createIPAFile:pid]; + + // Dismiss the alert box + dispatch_async(dispatch_get_main_queue(), ^{ + [alertController dismissViewControllerAnimated:NO completion:nil]; + + doneController = [UIAlertController alertControllerWithTitle:@"Decryption Complete!" message:[NSString stringWithFormat:@"IPA file saved to:\n%@", [dd IPAPath]] preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Ok", @"Ok") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { + NSLog(@"[trolldecrypt] Ok action"); + [doneController dismissViewControllerAnimated:NO completion:nil]; + [kw removeFromSuperview]; + kw.hidden = YES; + }]; + + [doneController addAction:okAction]; + [root presentViewController:doneController animated:YES completion:nil]; + }); // dispatch on main + + NSLog(@"[trolldecrypt] Over and out."); + while(1) + sleep(9999999); + }); // dispatch in background + + NSLog(@"[trolldecrypt] All done, exiting constructor."); +} + +NSArray *decryptedFileList(void) { + NSMutableArray *files = [NSMutableArray array]; + + // iterate through all files in the Documents directory + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSDirectoryEnumerator *directoryEnumerator = [fileManager enumeratorAtPath:docPath()]; + + NSString *file; + while (file = [directoryEnumerator nextObject]) { + if ([[file pathExtension] isEqualToString:@"ipa"]) { + NSString *filePath = [[docPath() stringByAppendingPathComponent:file] stringByStandardizingPath]; + + NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:filePath error:nil]; + NSDate *modificationDate = [fileAttributes fileModificationDate]; + + NSDictionary *fileInfo = @{@"fileName": file, @"modificationDate": modificationDate}; + [files addObject:fileInfo]; + } + } + + // Sort the array based on modification date + NSArray *sortedFiles = [files sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { + NSDate *date1 = [obj1 objectForKey:@"modificationDate"]; + NSDate *date2 = [obj2 objectForKey:@"modificationDate"]; + return [date2 compare:date1]; + }]; + + return [sortedFiles valueForKey:@"fileName"]; +} + +NSString *docPath(void) { + return [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; +} + +void decryptAppWithPID(pid_t pid) { + // generate App NSDictionary object to pass into decryptApp() + // proc_pidpath(self.pid, buffer, sizeof(buffer)); + NSString *message = nil; + NSString *error = nil; + + dispatch_async(dispatch_get_main_queue(), ^{ + alertWindow = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds]; + alertWindow.rootViewController = [UIViewController new]; + alertWindow.windowLevel = UIWindowLevelAlert + 1; + [alertWindow makeKeyAndVisible]; + + // Show a "Decrypting!" alert on the device and block the UI + + kw = alertWindow; + if([kw respondsToSelector:@selector(topmostPresentedViewController)]) + root = [kw performSelector:@selector(topmostPresentedViewController)]; + else + root = [kw rootViewController]; + root.modalPresentationStyle = UIModalPresentationFullScreen; + }); + + NSLog(@"[trolldecrypt] pid: %d", pid); + + char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; + proc_pidpath(pid, pathbuf, sizeof(pathbuf)); + + NSString *executable = [NSString stringWithUTF8String:pathbuf]; + NSString *path = [executable stringByDeletingLastPathComponent]; + NSDictionary *infoPlist = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Info.plist"]]; + NSString *bundleID = infoPlist[@"CFBundleIdentifier"]; + + if (!bundleID) { + error = @"Error: -2"; + message = [NSString stringWithFormat:@"Failed to get bundle id for pid: %d", pid]; + } + + LSApplicationProxy *app = [LSApplicationProxy applicationProxyForIdentifier:bundleID]; + if (!app) { + error = @"Error: -3"; + message = [NSString stringWithFormat:@"Failed to get LSApplicationProxy for bundle id: %@", bundleID]; + } + + if (message) { + dispatch_async(dispatch_get_main_queue(), ^{ + [alertController dismissViewControllerAnimated:NO completion:nil]; + NSLog(@"[trolldecrypt] failed to get bundleid for pid: %d", pid); + + errorController = [UIAlertController alertControllerWithTitle:error message:message preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Ok", @"Ok") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { + NSLog(@"[trolldecrypt] Ok action"); + [errorController dismissViewControllerAnimated:NO completion:nil]; + [kw removeFromSuperview]; + kw.hidden = YES; + }]; + + [errorController addAction:okAction]; + [root presentViewController:errorController animated:YES completion:nil]; + }); + } + + NSLog(@"[trolldecrypt] app: %@", app); + + NSString *name = [app localizedName]; + NSString *version = app.shortVersionString; + if (!version) version = @"1.0"; + + NSString *exec = [app canonicalExecutablePath]; + + NSDictionary *appInfo = @{ + @"bundleID":bundleID, + @"name":name, + @"version":version, + @"executable":exec + }; + + NSLog(@"[trolldecrypt] appInfo: %@", appInfo); + + dispatch_async(dispatch_get_main_queue(), ^{ + [alertController dismissViewControllerAnimated:NO completion:nil]; + UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Decrypt" message:[NSString stringWithFormat:@"Decrypt %@?", appInfo[@"name"]] preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]; + UIAlertAction *decrypt = [UIAlertAction actionWithTitle:@"Yes" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { + decryptApp(appInfo); + }]; + + [alert addAction:decrypt]; + [alert addAction:cancel]; + + [root presentViewController:alert animated:YES completion:nil]; + }); +} \ No newline at end of file diff --git a/control b/control new file mode 100644 index 0000000..bee2982 --- /dev/null +++ b/control @@ -0,0 +1,8 @@ +Package: com.fiore.trolldecrypt +Name: TrollDecrypt +Version: 0.0.1 +Architecture: iphoneos-arm +Description: An awesome application! +Maintainer: fiore +Author: fiore +Section: Utilities diff --git a/entitlements.plist b/entitlements.plist new file mode 100644 index 0000000..357e5fb --- /dev/null +++ b/entitlements.plist @@ -0,0 +1,38 @@ + + + + + platform-application + + application-identifier + com.fiore.trolldecrypt + com.apple.security.exception.files.absolute-path.read-write + + / + + com.apple.private.security.no-sandbox + + com.apple.private.persona-mgmt + + com.apple.private.MobileContainerManager.allowed + + com.apple.private.security.container-manager + + com.apple.private.security.storage.AppBundles + + com.apple.private.security.storage.AppDataContainers + + task_for_pid-allow + + get-task-allow + + com.apple.security.get-task-allow + + proc_info-allow + + com.apple.system-task-ports + + com.apple.springboard.launchapplications + + + \ No newline at end of file diff --git a/main.m b/main.m new file mode 100644 index 0000000..ad03dbd --- /dev/null +++ b/main.m @@ -0,0 +1,8 @@ +#import +#import "TDAppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass(TDAppDelegate.class)); + } +}