From 261f5dc797bc3e44da08c69b72d094e1383e505f Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Wed, 19 Feb 2020 18:14:40 -0800 Subject: [PATCH] Push SUSignature through SUAppcastItem (like 8ad1f57 on 1.x) --- Autoupdate/AppInstaller.m | 10 +++---- Autoupdate/SPUInstallationInputData.h | 8 +++-- Autoupdate/SPUInstallationInputData.m | 18 ++++++------ Autoupdate/SUDSAVerifier.h | 2 +- Autoupdate/SUDSAVerifier.m | 11 ++----- Sparkle.xcodeproj/project.pbxproj | 2 ++ Sparkle/SPUInstallerDriver.m | 6 ++-- Sparkle/SUAppcastItem.h | 3 +- Sparkle/SUAppcastItem.m | 13 +++++---- Sparkle/SUSignatures.h | 2 +- Sparkle/SUSignatures.m | 42 +++++++++++++++++++++++++++ Sparkle/SUUpdateValidator.h | 3 +- Sparkle/SUUpdateValidator.m | 20 +++++++------ Tests/SUDSAVerifierTest.m | 3 +- 14 files changed, 93 insertions(+), 50 deletions(-) diff --git a/Autoupdate/AppInstaller.m b/Autoupdate/AppInstaller.m index b01196b7..c1e7ba18 100644 --- a/Autoupdate/AppInstaller.m +++ b/Autoupdate/AppInstaller.m @@ -58,7 +58,7 @@ static const NSTimeInterval SUDisplayProgressTimeDelay = 0.7; @property (nonatomic, copy) NSString *updateDirectoryPath; @property (nonatomic, copy) NSString *downloadName; @property (nonatomic, copy) NSString *decryptionPassword; -@property (nonatomic, copy) NSString *dsaSignature; +@property (nonatomic, strong) SUSignatures *signatures; @property (nonatomic, copy) NSString *relaunchPath; @property (nonatomic, copy) NSString *installationType; @property (nonatomic, assign) BOOL shouldRelaunch; @@ -91,7 +91,7 @@ static const NSTimeInterval SUDisplayProgressTimeDelay = 0.7; @synthesize updateDirectoryPath = _updateDirectoryPath; @synthesize downloadName = _downloadName; @synthesize decryptionPassword = _decryptionPassword; -@synthesize dsaSignature = _dsaSignature; +@synthesize signatures = _signatures; @synthesize relaunchPath = _relaunchPath; @synthesize installationType = _installationType; @synthesize shouldRelaunch = _shouldRelaunch; @@ -198,7 +198,7 @@ static const NSTimeInterval SUDisplayProgressTimeDelay = 0.7; // Normal application updates are a bit more lenient allowing developers to change one of apple dev ID or DSA keys BOOL needsPrevalidation = [[unarchiver class] unsafeIfArchiveIsNotValidated] || ![self.installationType isEqualToString:SPUInstallationTypeApplication]; - self.updateValidator = [[SUUpdateValidator alloc] initWithDownloadPath:archivePath dsaSignature:self.dsaSignature host:self.host performingPrevalidation:needsPrevalidation]; + self.updateValidator = [[SUUpdateValidator alloc] initWithDownloadPath:archivePath signatures:self.signatures host:self.host performingPrevalidation:needsPrevalidation]; success = self.updateValidator.canValidate; } @@ -251,7 +251,7 @@ static const NSTimeInterval SUDisplayProgressTimeDelay = 0.7; self.updateDirectoryPath = nil; self.downloadName = nil; self.decryptionPassword = nil; - self.dsaSignature = nil; + self.signatures = nil; self.relaunchPath = nil; self.host = nil; @@ -387,7 +387,7 @@ static const NSTimeInterval SUDisplayProgressTimeDelay = 0.7; self.installationType = installationType; self.relaunchPath = installationData.relaunchPath; self.downloadName = installationData.downloadName; - self.dsaSignature = installationData.dsaSignature; + self.signatures = installationData.signatures; self.updateDirectoryPath = cacheInstallationPath; self.host = [[SUHost alloc] initWithBundle:hostBundle]; diff --git a/Autoupdate/SPUInstallationInputData.h b/Autoupdate/SPUInstallationInputData.h index 99af197c..6e75e0cd 100644 --- a/Autoupdate/SPUInstallationInputData.h +++ b/Autoupdate/SPUInstallationInputData.h @@ -8,6 +8,8 @@ #import +@class SUSignatures; + NS_ASSUME_NONNULL_BEGIN @interface SPUInstallationInputData : NSObject @@ -17,17 +19,17 @@ NS_ASSUME_NONNULL_BEGIN * hostBundlePath - path to host bundle to update & replace * updateDirectoryPath - path to update directory (i.e, temporary directory containing the new update archive) * downloadName - name of update archive in update directory - * dsaSignature - DSA signature for the update that came from the appcast item + * signatures - signatures for the update that came from the appcast item * decryptionPassword - optional decryption password for dmg archives */ -- (instancetype)initWithRelaunchPath:(NSString *)relaunchPath hostBundlePath:(NSString *)hostBundlePath updateDirectoryPath:(NSString *)updateDirectoryPath downloadName:(NSString *)downloadName installationType:(NSString *)installationType dsaSignature:(NSString *)dsaSignature decryptionPassword:(nullable NSString *)decryptionPassword; +- (instancetype)initWithRelaunchPath:(NSString *)relaunchPath hostBundlePath:(NSString *)hostBundlePath updateDirectoryPath:(NSString *)updateDirectoryPath downloadName:(NSString *)downloadName installationType:(NSString *)installationType signatures:(SUSignatures *)signatures decryptionPassword:(nullable NSString *)decryptionPassword; @property (nonatomic, copy, readonly) NSString *relaunchPath; @property (nonatomic, copy, readonly) NSString *hostBundlePath; @property (nonatomic, copy, readonly) NSString *updateDirectoryPath; @property (nonatomic, copy, readonly) NSString *downloadName; @property (nonatomic, copy, readonly) NSString *installationType; -@property (nonatomic, copy, readonly) NSString *dsaSignature; +@property (nonatomic, strong, readonly) SUSignatures *signatures; @property (nonatomic, copy, readonly, nullable) NSString *decryptionPassword; @end diff --git a/Autoupdate/SPUInstallationInputData.m b/Autoupdate/SPUInstallationInputData.m index 812a7580..9cf7ba08 100644 --- a/Autoupdate/SPUInstallationInputData.m +++ b/Autoupdate/SPUInstallationInputData.m @@ -8,7 +8,7 @@ #import "SPUInstallationInputData.h" #import "SPUInstallationType.h" - +#import "SUSignatures.h" #include "AppKitPrevention.h" @@ -16,7 +16,7 @@ static NSString *SURelaunchPathKey = @"SURelaunchPath"; static NSString *SUHostBundlePathKey = @"SUHostBundlePath"; static NSString *SUUpdateDirectoryPathKey = @"SUUpdateDirectoryPath"; static NSString *SUDownloadNameKey = @"SUDownloadName"; -static NSString *SUDSASignatureKey = @"SUDSASignature"; +static NSString *SUSignaturesKey = @"SUSignatures"; static NSString *SUDecryptionPasswordKey = @"SUDecryptionPassword"; static NSString *SUInstallationTypeKey = @"SUInstallationType"; @@ -26,11 +26,11 @@ static NSString *SUInstallationTypeKey = @"SUInstallationType"; @synthesize hostBundlePath = _hostBundlePath; @synthesize updateDirectoryPath = _updateDirectoryPath; @synthesize downloadName = _downloadName; -@synthesize dsaSignature = _dsaSignature; +@synthesize signatures = _signatures; @synthesize decryptionPassword = _decryptionPassword; @synthesize installationType = _installationType; -- (instancetype)initWithRelaunchPath:(NSString *)relaunchPath hostBundlePath:(NSString *)hostBundlePath updateDirectoryPath:(NSString *)updateDirectoryPath downloadName:(NSString *)downloadName installationType:(NSString *)installationType dsaSignature:(NSString *)dsaSignature decryptionPassword:(nullable NSString *)decryptionPassword +- (instancetype)initWithRelaunchPath:(NSString *)relaunchPath hostBundlePath:(NSString *)hostBundlePath updateDirectoryPath:(NSString *)updateDirectoryPath downloadName:(NSString *)downloadName installationType:(NSString *)installationType signatures:(SUSignatures *)signatures decryptionPassword:(nullable NSString *)decryptionPassword { self = [super init]; if (self != nil) { @@ -42,7 +42,7 @@ static NSString *SUInstallationTypeKey = @"SUInstallationType"; _installationType = [installationType copy]; assert(SPUValidInstallationType(_installationType)); - _dsaSignature = [dsaSignature copy]; + _signatures = signatures; _decryptionPassword = [decryptionPassword copy]; } return self; @@ -75,14 +75,14 @@ static NSString *SUInstallationTypeKey = @"SUInstallationType"; return nil; } - NSString *dsaSignature = [decoder decodeObjectOfClass:[NSString class] forKey:SUDSASignatureKey]; - if (dsaSignature == nil) { + SUSignatures *signatures = [decoder decodeObjectOfClass:[SUSignatures class] forKey:SUSignaturesKey]; + if (signatures == nil) { return nil; } NSString *decryptionPassword = [decoder decodeObjectOfClass:[NSString class] forKey:SUDecryptionPasswordKey]; - return [self initWithRelaunchPath:relaunchPath hostBundlePath:hostBundlePath updateDirectoryPath:updateDirectoryPath downloadName:downloadName installationType:installationType dsaSignature:dsaSignature decryptionPassword:decryptionPassword]; + return [self initWithRelaunchPath:relaunchPath hostBundlePath:hostBundlePath updateDirectoryPath:updateDirectoryPath downloadName:downloadName installationType:installationType signatures:signatures decryptionPassword:decryptionPassword]; } - (void)encodeWithCoder:(NSCoder *)coder @@ -92,7 +92,7 @@ static NSString *SUInstallationTypeKey = @"SUInstallationType"; [coder encodeObject:self.updateDirectoryPath forKey:SUUpdateDirectoryPathKey]; [coder encodeObject:self.installationType forKey:SUInstallationTypeKey]; [coder encodeObject:self.downloadName forKey:SUDownloadNameKey]; - [coder encodeObject:self.dsaSignature forKey:SUDSASignatureKey]; + [coder encodeObject:self.signatures forKey:SUSignaturesKey]; if (self.decryptionPassword != nil) { [coder encodeObject:self.decryptionPassword forKey:SUDecryptionPasswordKey]; } diff --git a/Autoupdate/SUDSAVerifier.h b/Autoupdate/SUDSAVerifier.h index 1b4665b9..46e6b0de 100644 --- a/Autoupdate/SUDSAVerifier.h +++ b/Autoupdate/SUDSAVerifier.h @@ -25,7 +25,7 @@ @interface SUDSAVerifier : NSObject -+ (BOOL)validatePath:(NSString *)path withEncodedDSASignature:(NSString *)encodedSignature withPublicDSAKey:(NSString *)pkeyString; ++ (BOOL)validatePath:(NSString *)path withDSASignature:(NSData *)signature withPublicDSAKey:(NSString *)pkeyString; - (instancetype)initWithPublicKeyData:(NSData *)data; diff --git a/Autoupdate/SUDSAVerifier.m b/Autoupdate/SUDSAVerifier.m index e1b8756d..d23ec689 100644 --- a/Autoupdate/SUDSAVerifier.m +++ b/Autoupdate/SUDSAVerifier.m @@ -23,9 +23,9 @@ SecKeyRef _secKey; } -+ (BOOL)validatePath:(NSString *)path withEncodedDSASignature:(NSString *)encodedSignature withPublicDSAKey:(NSString *)pkeyString ++ (BOOL)validatePath:(NSString *)path withDSASignature:(NSData *)signature withPublicDSAKey:(NSString *)pkeyString { - if (!encodedSignature) { + if (!signature) { SULog(SULogLevelError, @"There is no DSA signature to check"); return NO; } @@ -40,13 +40,6 @@ return NO; } - NSString *strippedSignature = [encodedSignature stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]; - NSData *signature = -#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_9 - [[NSData alloc] initWithBase64Encoding:strippedSignature]; -#else - [[NSData alloc] initWithBase64EncodedString:strippedSignature options:NSDataBase64DecodingIgnoreUnknownCharacters]; -#endif return [verifier verifyFileAtPath:path signature:signature]; } diff --git a/Sparkle.xcodeproj/project.pbxproj b/Sparkle.xcodeproj/project.pbxproj index 8be9a384..bcaa255a 100644 --- a/Sparkle.xcodeproj/project.pbxproj +++ b/Sparkle.xcodeproj/project.pbxproj @@ -51,6 +51,7 @@ 55C14F07136EF6DB00649790 /* SULog.m in Sources */ = {isa = PBXBuildFile; fileRef = 55C14F05136EF6DB00649790 /* SULog.m */; }; 55E6F33319EC9F6C00005E76 /* SUErrors.h in Headers */ = {isa = PBXBuildFile; fileRef = 55E6F33219EC9F6C00005E76 /* SUErrors.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5A4094481C74EA5200983BE0 /* SUAppcastTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AA4DCD01C73E5510078F128 /* SUAppcastTest.swift */; }; + 5A6DD17123FE1FFC000AEF33 /* SUSignatures.m in Sources */ = {isa = PBXBuildFile; fileRef = EA1E286D22B665E8004AA304 /* SUSignatures.m */; }; 5AD0FA7F1C73F2E2004BCEFF /* testappcast.xml in Resources */ = {isa = PBXBuildFile; fileRef = 5AD0FA7E1C73F2E2004BCEFF /* testappcast.xml */; }; 5AE459001C34118500E3BB47 /* SUUpdaterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 14950074195FDF5900BC5B5B /* SUUpdaterTest.m */; }; 5AE459021C34118500E3BB47 /* SUVersionComparisonTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 61227A150DB548B800AB99EA /* SUVersionComparisonTest.m */; }; @@ -3494,6 +3495,7 @@ 7267E5C21D3D8B2700D1BF90 /* SUGuidedPackageInstaller.m in Sources */, 7267E5D71D3D8D3F00D1BF90 /* SUHost.m in Sources */, 7267E5C31D3D8B2700D1BF90 /* SUInstaller.m in Sources */, + 5A6DD17123FE1FFC000AEF33 /* SUSignatures.m in Sources */, 7267E5CE1D3D8C7500D1BF90 /* SULog.m in Sources */, 7267E5C41D3D8B2700D1BF90 /* SUPackageInstaller.m in Sources */, 7267E5871D3D89B300D1BF90 /* SUPipedUnarchiver.m in Sources */, diff --git a/Sparkle/SPUInstallerDriver.m b/Sparkle/SPUInstallerDriver.m index 34a1a62e..6995c44f 100644 --- a/Sparkle/SPUInstallerDriver.m +++ b/Sparkle/SPUInstallerDriver.m @@ -171,15 +171,13 @@ pathToRelaunch = relaunchPath; } } - - NSString *dsaSignature = (self.updateItem.DSASignature == nil) ? @"" : self.updateItem.DSASignature; - + NSString *decryptionPassword = nil; if ([self.updaterDelegate respondsToSelector:@selector(decryptionPasswordForUpdater:)]) { decryptionPassword = [self.updaterDelegate decryptionPasswordForUpdater:self.updater]; } - SPUInstallationInputData *installationData = [[SPUInstallationInputData alloc] initWithRelaunchPath:pathToRelaunch hostBundlePath:self.host.bundlePath updateDirectoryPath:self.temporaryDirectory downloadName:self.downloadName installationType:self.updateItem.installationType dsaSignature:dsaSignature decryptionPassword:decryptionPassword]; + SPUInstallationInputData *installationData = [[SPUInstallationInputData alloc] initWithRelaunchPath:pathToRelaunch hostBundlePath:self.host.bundlePath updateDirectoryPath:self.temporaryDirectory downloadName:self.downloadName installationType:self.updateItem.installationType signatures:self.updateItem.signatures decryptionPassword:decryptionPassword]; NSData *archivedData = SPUArchiveRootObjectSecurely(installationData); if (archivedData == nil) { diff --git a/Sparkle/SUAppcastItem.h b/Sparkle/SUAppcastItem.h index 4c01147a..c259ff8c 100644 --- a/Sparkle/SUAppcastItem.h +++ b/Sparkle/SUAppcastItem.h @@ -11,13 +11,14 @@ #import #import +@class SUSignatures; SU_EXPORT @interface SUAppcastItem : NSObject @property (copy, readonly) NSString *title; @property (copy, readonly) NSString *dateString; @property (copy, readonly) NSString *itemDescription; @property (strong, readonly) NSURL *releaseNotesURL; -@property (copy, readonly) NSString *DSASignature; +@property (strong, readonly) SUSignatures *signatures; @property (copy, readonly) NSString *minimumSystemVersion; @property (copy, readonly) NSString *maximumSystemVersion; @property (strong, readonly) NSURL *fileURL; diff --git a/Sparkle/SUAppcastItem.m b/Sparkle/SUAppcastItem.m index dd7c1489..b519d839 100644 --- a/Sparkle/SUAppcastItem.m +++ b/Sparkle/SUAppcastItem.m @@ -10,6 +10,7 @@ #import #import "SULog.h" #import "SUConstants.h" +#import "SUSignatures.h" #import "SPUInstallationType.h" @@ -17,7 +18,7 @@ static NSString *SUAppcastItemDeltaUpdatesKey = @"deltaUpdates"; static NSString *SUAppcastItemDisplayVersionStringKey = @"displayVersionString"; -static NSString *SUAppcastItemDSASignatureKey = @"DSASignature"; +static NSString *SUAppcastItemSignaturesKey = @"signatures"; static NSString *SUAppcastItemFileURLKey = @"fileURL"; static NSString *SUAppcastItemInfoURLKey = @"infoURL"; static NSString *SUAppcastItemContentLengthKey = @"contentLength"; @@ -35,7 +36,7 @@ static NSString *SUAppcastItemInstallationTypeKey = @"SUAppcastItemInstallationT @synthesize dateString = _dateString; @synthesize deltaUpdates = _deltaUpdates; @synthesize displayVersionString = _displayVersionString; -@synthesize DSASignature = _DSASignature; +@synthesize signatures = _signatures; @synthesize fileURL = _fileURL; @synthesize contentLength = _contentLength; @synthesize infoURL = _infoURL; @@ -61,7 +62,7 @@ static NSString *SUAppcastItemInstallationTypeKey = @"SUAppcastItemInstallationT if (self != nil) { _deltaUpdates = [decoder decodeObjectOfClasses:[NSSet setWithArray:@[[NSDictionary class], [SUAppcastItem class]]] forKey:SUAppcastItemDeltaUpdatesKey]; _displayVersionString = [(NSString *)[decoder decodeObjectOfClass:[NSString class] forKey:SUAppcastItemDisplayVersionStringKey] copy]; - _DSASignature = [(NSString *)[decoder decodeObjectOfClass:[NSString class] forKey:SUAppcastItemDSASignatureKey] copy]; + _signatures = (SUSignatures *)[decoder decodeObjectOfClass:[SUSignatures class] forKey:SUAppcastItemSignaturesKey]; _fileURL = [decoder decodeObjectOfClass:[NSURL class] forKey:SUAppcastItemFileURLKey]; _infoURL = [decoder decodeObjectOfClass:[NSURL class] forKey:SUAppcastItemInfoURLKey]; @@ -94,8 +95,8 @@ static NSString *SUAppcastItemInstallationTypeKey = @"SUAppcastItemInstallationT [encoder encodeObject:self.displayVersionString forKey:SUAppcastItemDisplayVersionStringKey]; } - if (self.DSASignature != nil) { - [encoder encodeObject:self.DSASignature forKey:SUAppcastItemDSASignatureKey]; + if (self.signatures != nil) { + [encoder encodeObject:self.signatures forKey:SUAppcastItemSignaturesKey]; } if (self.fileURL != nil) { @@ -250,7 +251,7 @@ static NSString *SUAppcastItemInstallationTypeKey = @"SUAppcastItemInstallationT _fileURL = [NSURL URLWithString:fileURLString]; } if (enclosure) { - _DSASignature = [(NSString *)[enclosure objectForKey:SUAppcastAttributeDSASignature] copy]; + _signatures = [[SUSignatures alloc] initWithDsa:[enclosure objectForKey:SUAppcastAttributeDSASignature] ed:nil]; } _versionString = [(NSString *)newVersion copy]; diff --git a/Sparkle/SUSignatures.h b/Sparkle/SUSignatures.h index 5b3e40ca..11a6216d 100644 --- a/Sparkle/SUSignatures.h +++ b/Sparkle/SUSignatures.h @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface SUSignatures : NSObject { +@interface SUSignatures : NSObject { unsigned char ed25519_signature[64]; } @property (strong, readonly, nullable) NSData *dsaSignature; diff --git a/Sparkle/SUSignatures.m b/Sparkle/SUSignatures.m index 8e07d7e7..c8935761 100644 --- a/Sparkle/SUSignatures.m +++ b/Sparkle/SUSignatures.m @@ -10,6 +10,9 @@ #import #import "SULog.h" +static NSString *SUDSASignatureKey = @"SUDSASignature"; +static NSString *SUEDSignatureKey = @"SUEDSignature"; + @implementation SUSignatures @synthesize dsaSignature = _dsaSignature; @@ -51,6 +54,45 @@ static NSData *decode(NSString *str) { #pragma clang diagnostic pop } +- (instancetype)initWithCoder:(NSCoder *)decoder +{ + self = [super init]; + if (self) { + NSData *dsaSignature = [decoder decodeObjectOfClass:[NSData class] forKey:SUDSASignatureKey]; + if (dsaSignature) { + _dsaSignature = dsaSignature; + } + + NSData *edSignature = [decoder decodeObjectOfClass:[NSData class] forKey:SUEDSignatureKey]; + if (edSignature) { + if (edSignature.length != sizeof(self->ed25519_signature)) { + return nil; + } + [edSignature getBytes:self->ed25519_signature]; + } + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder +{ + if (self.dsaSignature) { + [coder encodeObject:self.dsaSignature forKey:SUDSASignatureKey]; + } + if (self.ed25519Signature) { +// Xcode may enable this in pedantic mode +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdirect-ivar-access" + NSData *edSignature = [NSData dataWithBytesNoCopy:&self->ed25519_signature length:sizeof(self->ed25519_signature) freeWhenDone:false]; +#pragma clang diagnostic pop + [coder encodeObject:edSignature forKey:SUEDSignatureKey]; + } +} + ++ (BOOL)supportsSecureCoding { + return YES; +} + @end @implementation SUPublicKeys diff --git a/Sparkle/SUUpdateValidator.h b/Sparkle/SUUpdateValidator.h index 0a5f14c6..040b1220 100644 --- a/Sparkle/SUUpdateValidator.h +++ b/Sparkle/SUUpdateValidator.h @@ -9,11 +9,12 @@ #import @class SUHost; +@class SUSignatures; @interface SUUpdateValidator : NSObject // Pass YES to performingPrevalidation if archive validation must be done immediately, before extraction -- (instancetype)initWithDownloadPath:(NSString *)downloadPath dsaSignature:(NSString *)dsaSignature host:(SUHost *)host performingPrevalidation:(BOOL)performingPrevalidation; +- (instancetype)initWithDownloadPath:(NSString *)downloadPath signatures:(SUSignatures *)signatures host:(SUHost *)host performingPrevalidation:(BOOL)performingPrevalidation; // Indicates whether we can perform (post) validation later @property (nonatomic, readonly) BOOL canValidate; diff --git a/Sparkle/SUUpdateValidator.m b/Sparkle/SUUpdateValidator.m index 1b1cdbe8..0cde47ea 100644 --- a/Sparkle/SUUpdateValidator.m +++ b/Sparkle/SUUpdateValidator.m @@ -12,6 +12,7 @@ #import "SUInstaller.h" #import "SUHost.h" #import "SULog.h" +#import "SUSignatures.h" #include "AppKitPrevention.h" @@ -20,7 +21,7 @@ @property (nonatomic, readonly) SUHost *host; @property (nonatomic, readonly) BOOL prevalidatedDsaSignature; -@property (nonatomic, readonly) NSString *dsaSignature; +@property (nonatomic, readonly) SUSignatures *signatures; @property (nonatomic, readonly) NSString *downloadPath; @end @@ -30,10 +31,10 @@ @synthesize host = _host; @synthesize canValidate = _canValidate; @synthesize prevalidatedDsaSignature = _prevalidatedDsaSignature; -@synthesize dsaSignature = _dsaSignature; +@synthesize signatures = _signatures; @synthesize downloadPath = _downloadPath; -- (instancetype)initWithDownloadPath:(NSString *)downloadPath dsaSignature:(NSString *)dsaSignature host:(SUHost *)host performingPrevalidation:(BOOL)performingPrevalidation +- (instancetype)initWithDownloadPath:(NSString *)downloadPath signatures:(SUSignatures *)signatures host:(SUHost *)host performingPrevalidation:(BOOL)performingPrevalidation { self = [super init]; if (self != nil) { @@ -41,6 +42,7 @@ BOOL prevalidatedDsaSignature; if (performingPrevalidation) { NSString *publicDSAKey = host.publicDSAKey; + NSData *dsaSignature = signatures.dsaSignature; if (publicDSAKey == nil) { prevalidatedDsaSignature = NO; @@ -49,7 +51,7 @@ prevalidatedDsaSignature = NO; SULog(SULogLevelError, @"Failed to validate update before unarchiving because no DSA signature was found"); } else { - prevalidatedDsaSignature = [SUDSAVerifier validatePath:downloadPath withEncodedDSASignature:dsaSignature withPublicDSAKey:host.publicDSAKey]; + prevalidatedDsaSignature = [SUDSAVerifier validatePath:downloadPath withDSASignature:dsaSignature withPublicDSAKey:host.publicDSAKey]; if (!prevalidatedDsaSignature) { SULog(SULogLevelError, @"DSA signature validation before unarchiving failed for update %@", downloadPath); } @@ -64,7 +66,7 @@ _canValidate = canValidate; _prevalidatedDsaSignature = prevalidatedDsaSignature; _downloadPath = [downloadPath copy]; - _dsaSignature = [dsaSignature copy]; + _signatures = signatures; _host = host; } return self; @@ -74,7 +76,7 @@ { assert(self.canValidate); - NSString *DSASignature = self.dsaSignature; + NSData *DSASignature = self.signatures.dsaSignature; NSString *downloadPath = self.downloadPath; SUHost *host = self.host; @@ -96,7 +98,7 @@ if (isPackage) { // If we get here, then the appcast installation type was lying to us.. This error will be caught later when starting the installer. // For package type updates, all we do is check if the DSA signature is valid - BOOL validationCheckSuccess = [SUDSAVerifier validatePath:downloadPath withEncodedDSASignature:DSASignature withPublicDSAKey:host.publicDSAKey]; + BOOL validationCheckSuccess = [SUDSAVerifier validatePath:downloadPath withDSASignature:DSASignature withPublicDSAKey:host.publicDSAKey]; if (!validationCheckSuccess) { SULog(SULogLevelError, @"DSA signature validation of the package failed. The update contains an installer package, and valid DSA signatures are mandatory for all installer packages. The update will be rejected. Sign the installer with a valid DSA key or use an .app bundle update instead."); } @@ -121,7 +123,7 @@ } } -- (BOOL)validateBundleUpdateForHost:(SUHost *)host newBundleURL:(NSURL *)newBundleURL archivePath:(NSString *)archivePath DSASignature:(NSString *)DSASignature +- (BOOL)validateBundleUpdateForHost:(SUHost *)host newBundleURL:(NSURL *)newBundleURL archivePath:(NSString *)archivePath DSASignature:(NSData *)DSASignature { NSBundle *newBundle = [NSBundle bundleWithURL:newBundleURL]; if (newBundle == nil) { @@ -146,7 +148,7 @@ // In that case, the check ensures that the app author has correctly used DSA keys, so that the app will be updateable in the next version. // However if the new and old DSA keys are the same, then this is a security measure. if (newPublicDSAKey != nil) { - if (![SUDSAVerifier validatePath:archivePath withEncodedDSASignature:DSASignature withPublicDSAKey:newPublicDSAKey]) { + if (![SUDSAVerifier validatePath:archivePath withDSASignature:DSASignature withPublicDSAKey:newPublicDSAKey]) { SULog(SULogLevelError, @"DSA signature validation failed. The update has a public DSA key and is signed with a DSA key, but the %@ doesn't match the signature. The update will be rejected.", dsaKeysMatch ? @"public key" : @"new public key shipped with the update"); return NO; diff --git a/Tests/SUDSAVerifierTest.m b/Tests/SUDSAVerifierTest.m index e7b6f840..0335cdcb 100644 --- a/Tests/SUDSAVerifierTest.m +++ b/Tests/SUDSAVerifierTest.m @@ -75,9 +75,10 @@ - (void)testValidatePath { NSString *pubkey = [NSString stringWithContentsOfFile:self.pubKeyFile encoding:NSASCIIStringEncoding error:nil]; + NSData *signature = [[NSData alloc] initWithBase64EncodedString:@"MC0CFFMF3ha5kjvrJ9JTpTR8BenPN9QUAhUAzY06JRdtP17MJewxhK0twhvbKIE=" options:(NSDataBase64DecodingOptions)0]; XCTAssertTrue([SUDSAVerifier validatePath:self.testFile - withEncodedDSASignature:@"MC0CFFMF3ha5kjvrJ9JTpTR8BenPN9QUAhUAzY06JRdtP17MJewxhK0twhvbKIE=" + withDSASignature:signature withPublicDSAKey:pubkey], @"Expected valid signature"); }