diff --git a/Tests/SUCodeSigningVerifierTest.m b/Tests/SUCodeSigningVerifierTest.m index 3a1601984..8209f4936 100644 --- a/Tests/SUCodeSigningVerifierTest.m +++ b/Tests/SUCodeSigningVerifierTest.m @@ -15,21 +15,19 @@ @interface SUCodeSigningVerifierTest : XCTestCase -@property (copy) NSString *notSignedAppPath; -@property (copy) NSString *notSignedAppURL; -@property (copy) NSString *validSignedAppPath; -@property (copy) NSString *invalidSignedAppPath; -@property (copy) NSString *calculatorCopyPath; +@property (copy) NSURL *notSignedAppURL; +@property (copy) NSURL *validSignedAppURL; +@property (copy) NSURL *invalidSignedAppURL; +@property (copy) NSURL *calculatorCopyURL; @end @implementation SUCodeSigningVerifierTest -@synthesize notSignedAppPath = _notSignedAppPath; @synthesize notSignedAppURL = _notSignedAppURL; -@synthesize validSignedAppPath = _validSignedAppPath; -@synthesize invalidSignedAppPath = _invalidSignedAppPath; -@synthesize calculatorCopyPath = _calculatorCopyPath; +@synthesize validSignedAppURL = _validSignedAppURL; +@synthesize invalidSignedAppURL = _invalidSignedAppURL; +@synthesize calculatorCopyURL = _calculatorCopyURL; - (void)setUp { @@ -37,32 +35,28 @@ - (void)setUp NSBundle *unitTestBundle = [NSBundle bundleForClass:[self class]]; NSString *unitTestBundleIdentifier = unitTestBundle.bundleIdentifier; - NSString *zippedAppPath = [unitTestBundle pathForResource:@"SparkleTestCodeSignApp" ofType:@"zip"]; - + NSString *zippedAppURL = [unitTestBundle pathForResource:@"SparkleTestCodeSignApp" ofType:@"zip"]; + SUFileManager *fileManager = [SUFileManager defaultManager]; - + NSError *tempError = nil; - NSURL *tempDir = [fileManager makeTemporaryDirectoryWithPreferredName:unitTestBundleIdentifier appropriateForDirectoryURL:[NSURL fileURLWithPath:zippedAppPath] error:&tempError]; - + NSURL *tempDir = [fileManager makeTemporaryDirectoryWithPreferredName:unitTestBundleIdentifier appropriateForDirectoryURL:[NSURL fileURLWithPath:zippedAppURL] error:&tempError]; + if (tempDir == nil) { XCTFail(@"Failed to create temporary directory with error: %@", tempError); return; } - - NSString *tempDirPath = tempDir.path; - XCTAssertNotNil(tempDirPath); - + NSError *error = nil; - if ([[NSFileManager defaultManager] createDirectoryAtPath:tempDirPath withIntermediateDirectories:YES attributes:nil error:&error]) { - if ([self unzip:zippedAppPath toPath:tempDirPath]) { - self.notSignedAppPath = [tempDirPath stringByAppendingPathComponent:@"SparkleTestCodeSignApp.app"]; - self.notSignedAppURL = [NSURL fileURLWithPath:self.notSignedAppPath]; + if ([[NSFileManager defaultManager] createDirectoryAtURL:tempDir withIntermediateDirectories:YES attributes:nil error:&error]) { + if ([self unzip:zippedAppURL toPath:tempDir.path]) { + self.notSignedAppURL = [tempDir URLByAppendingPathComponent:@"SparkleTestCodeSignApp.app"]; [self setupValidSignedApp]; [self setupCalculatorCopy]; [self setupInvalidSignedApp]; } else { - NSLog(@"Failed to unzip %@", zippedAppPath); + NSLog(@"Failed to unzip %@", zippedAppURL); } } else { @@ -73,87 +67,88 @@ - (void)setUp - (void)tearDown { [super tearDown]; - - if (self.notSignedAppPath) { - NSString *tempDir = [self.notSignedAppPath stringByDeletingLastPathComponent]; - [[NSFileManager defaultManager] removeItemAtPath:tempDir error:nil]; + + if (self.notSignedAppURL) { + NSURL *tempDir = [self.notSignedAppURL URLByDeletingLastPathComponent]; + [[NSFileManager defaultManager] removeItemAtURL:tempDir error:nil]; } } - (void)setupValidSignedApp { NSError *error = nil; - NSString *tempDir = [self.notSignedAppPath stringByDeletingLastPathComponent]; - NSString *signedAndValid = [tempDir stringByAppendingPathComponent:@"valid-signed.app"]; - - if ([[NSFileManager defaultManager] fileExistsAtPath:signedAndValid]) { - [[NSFileManager defaultManager] removeItemAtPath:signedAndValid error:NULL]; + NSString *tempDir = [self.notSignedAppURL URLByDeletingLastPathComponent]; + NSString *signedAndValid = [tempDir URLByAppendingPathComponent:@"valid-signed.app"]; + + if ([[NSFileManager defaultManager] fileExistsAtURL:signedAndValid]) { + [[NSFileManager defaultManager] removeItemAtURL:signedAndValid error:NULL]; } - - if (![[NSFileManager defaultManager] copyItemAtPath:self.notSignedAppPath toPath:signedAndValid error:&error]) { - XCTFail("Failed to copy %@ to %@ with error: %@", self.notSignedAppPath, signedAndValid, error); + + if (![[NSFileManager defaultManager] copyItemAtURL:self.notSignedAppURL toPath:signedAndValid error:&error]) { + XCTFail("Failed to copy %@ to %@ with error: %@", self.notSignedAppURL, signedAndValid, error); } - - self.validSignedAppPath = signedAndValid; - - if (![self codesignAppPath:self.validSignedAppPath]) { - XCTFail(@"Failed to codesign %@", self.validSignedAppPath); + + self.validSignedAppURL = signedAndValid; + self.validSignedAppURL = [NSURL fileURLWithPath:signedAndValid]; + + if (![self codesignAppURL:self.validSignedAppURL]) { + XCTFail(@"Failed to codesign %@", self.validSignedAppURL); } } - (void)setupCalculatorCopy { - NSString *tempDir = [self.notSignedAppPath stringByDeletingLastPathComponent]; - NSString *calculatorCopy = [tempDir stringByAppendingPathComponent:@"calc.app"]; - - if ([[NSFileManager defaultManager] fileExistsAtPath:calculatorCopy]) { - [[NSFileManager defaultManager] removeItemAtPath:calculatorCopy error:NULL]; + NSString *tempDir = [self.notSignedAppURL URLByDeletingLastPathComponent]; + NSString *calculatorCopy = [tempDir URLByAppendingPathComponent:@"calc.app"]; + + if ([[NSFileManager defaultManager] fileExistsAtURL:calculatorCopy]) { + [[NSFileManager defaultManager] removeItemAtURL:calculatorCopy error:NULL]; } - + // Make a copy of the signed calculator app so we can match signatures later // Matching signatures on ad-hoc signed apps does *not* work NSError *copyError = nil; // Don't check the return value of this operation - seems like on 10.11 the API can say it fails even though the operation really succeeds, // which sounds like some kind of (SIP / attribute?) bug - [[NSFileManager defaultManager] copyItemAtPath:CALCULATOR_PATH toPath:calculatorCopy error:©Error]; - - if (![[NSFileManager defaultManager] fileExistsAtPath:calculatorCopy]) { + [[NSFileManager defaultManager] copyItemAtURL:CALCULATOR_PATH toPath:calculatorCopy error:©Error]; + + if (![[NSFileManager defaultManager] fileExistsAtURL:calculatorCopy]) { XCTFail(@"Copied calculator application does not exist"); } - + // Alter the signed copy slightly, this won't invalidate signature matching (although it will invalidate the integrity part of the signature) // Which is what we want. If a user alters an app bundle, we should still be able to update as long as its identity is still valid NSError *removeError = nil; - if (![[NSFileManager defaultManager] removeItemAtPath:[[calculatorCopy stringByAppendingPathComponent:@"Contents"] stringByAppendingPathComponent:@"PkgInfo"] error:&removeError]) { + if (![[NSFileManager defaultManager] removeItemAtURL:[[calculatorCopy URLByAppendingPathComponent:@"Contents"] URLByAppendingPathComponent:@"PkgInfo"] error:&removeError]) { XCTFail(@"Failed to remove file in calculator copy with error: %@", removeError); } - - self.calculatorCopyPath = calculatorCopy; + + self.calculatorCopyURL = calculatorCopy; } - (void)setupInvalidSignedApp { NSError *error = nil; - NSString *tempDir = [self.notSignedAppPath stringByDeletingLastPathComponent]; - NSString *signedAndInvalid = [tempDir stringByAppendingPathComponent:@"invalid-signed.app"]; - - if ([[NSFileManager defaultManager] fileExistsAtPath:signedAndInvalid]) { - [[NSFileManager defaultManager] removeItemAtPath:signedAndInvalid error:NULL]; + NSString *tempDir = [self.notSignedAppURL URLByDeletingLastPathComponent]; + NSString *signedAndInvalid = [tempDir URLByAppendingPathComponent:@"invalid-signed.app"]; + + if ([[NSFileManager defaultManager] fileExistsAtURL:signedAndInvalid]) { + [[NSFileManager defaultManager] removeItemAtURL:signedAndInvalid error:NULL]; } - if ([[NSFileManager defaultManager] copyItemAtPath:self.notSignedAppPath toPath:signedAndInvalid error:&error]) { - self.invalidSignedAppPath = signedAndInvalid; - if ([self codesignAppPath:self.invalidSignedAppPath]) { - NSString *fileInAppBundleToRemove = [self.invalidSignedAppPath stringByAppendingPathComponent:@"Contents/Resources/test_app_only_dsa_pub.pem"]; - if (![[NSFileManager defaultManager] removeItemAtPath:fileInAppBundleToRemove error:&error]) { + if ([[NSFileManager defaultManager] copyItemAtURL:self.notSignedAppURL toPath:signedAndInvalid error:&error]) { + self.invalidSignedAppURL = signedAndInvalid; + if ([self codesignAppURL:self.invalidSignedAppURL]) { + NSString *fileInAppBundleToRemove = [self.invalidSignedAppURL URLByAppendingPathComponent:@"Contents/Resources/test_app_only_dsa_pub.pem"]; + if (![[NSFileManager defaultManager] removeItemAtURL:fileInAppBundleToRemove error:&error]) { NSLog(@"Failed to remove %@ with error %@", fileInAppBundleToRemove, error); } } else { - NSLog(@"Failed to codesign %@", self.invalidSignedAppPath); + NSLog(@"Failed to codesign %@", self.invalidSignedAppURL); } } else { - NSLog(@"Failed to copy %@ to %@ with error %@", self.notSignedAppPath, signedAndInvalid, error); + NSLog(@"Failed to copy %@ to %@ with error %@", self.notSignedAppURL, signedAndInvalid, error); } } @@ -166,7 +161,7 @@ - (BOOL)unzip:(NSString *)zipPath toPath:(NSString *)destPath task.launchPath = @"/usr/bin/unzip"; task.currentDirectoryPath = destPath; task.arguments = @[zipPath]; - + [task launch]; [task waitUntilExit]; success = (task.terminationStatus == 0); @@ -178,7 +173,7 @@ - (BOOL)unzip:(NSString *)zipPath toPath:(NSString *)destPath return success; } -- (BOOL)codesignAppPath:(NSString *)appPath +- (BOOL)codesignAppURL:(NSString *)appPath { BOOL success = NO; @try @@ -207,16 +202,16 @@ - (void)testUnsignedApp - (void)testValidSignedApp { - XCTAssertTrue([SUCodeSigningVerifier bundleAtURLIsCodeSigned:self.validSignedAppPath], @"App expected to be code signed"); + XCTAssertTrue([SUCodeSigningVerifier bundleAtURLIsCodeSigned:self.validSignedAppURL], @"App expected to be code signed"); NSError *error = nil; - XCTAssertTrue([SUCodeSigningVerifier codeSignatureIsValidAtBundleURL:self.validSignedAppPath error:&error], @"signature should be valid"); + XCTAssertTrue([SUCodeSigningVerifier codeSignatureIsValidAtBundleURL:self.validSignedAppURL error:&error], @"signature should be valid"); XCTAssertNil(error, @"error should be nil"); } - (void)testValidSignedCalculatorApp { - NSString *appPath = CALCULATOR_PATH; + NSURL *appPath = [NSURL fileURLWithPath:CALCULATOR_PATH]; XCTAssertTrue([SUCodeSigningVerifier bundleAtURLIsCodeSigned:appPath], @"App expected to be code signed"); NSError *error = nil; @@ -227,29 +222,32 @@ - (void)testValidSignedCalculatorApp - (void)testValidMatchingSelf { NSError *error = nil; - XCTAssertTrue([SUCodeSigningVerifier codeSignatureAtBundleURL:CALCULATOR_PATH matchesSignatureAtBundleURL:CALCULATOR_PATH error:&error], @"Our valid signed app expected to having matching signature to itself"); + NSURL *appPath = [NSURL fileURLWithPath:CALCULATOR_PATH]; + + XCTAssertTrue([SUCodeSigningVerifier codeSignatureAtBundleURL:appPath matchesSignatureAtBundleURL:appPath error:&error], @"Our valid signed app expected to having matching signature to itself"); } - (void)testValidMatching { // We can't test our own app because matching with ad-hoc signed apps understandably does not succeed NSError *error = nil; - XCTAssertTrue([SUCodeSigningVerifier codeSignatureAtBundleURL:CALCULATOR_PATH matchesSignatureAtBundleURL:self.calculatorCopyPath error:&error], @"The calculator app is expected to have matching identity signature to its altered copy"); + NSURL *appPath = [NSURL fileURLWithPath:CALCULATOR_PATH]; + XCTAssertTrue([SUCodeSigningVerifier codeSignatureAtBundleURL:appPath matchesSignatureAtBundleURL:self.calculatorCopyURL error:&error], @"The calculator app is expected to have matching identity signature to its altered copy"); } - (void)testInvalidMatching { - NSString *appPath = CALCULATOR_PATH; + NSURL *appPath = [NSURL fileURLWithPath:CALCULATOR_PATH]; NSError *error = nil; - XCTAssertFalse([SUCodeSigningVerifier codeSignatureAtBundleURL:appPath matchesSignatureAtBundleURL:self.validSignedAppPath error:&error], @"Calculator app bundle expected to have different signature than our valid signed app"); + XCTAssertFalse([SUCodeSigningVerifier codeSignatureAtBundleURL:appPath matchesSignatureAtBundleURL:self.validSignedAppURL error:&error], @"Calculator app bundle expected to have different signature than our valid signed app"); } - (void)testInvalidSignedApp { - XCTAssertTrue([SUCodeSigningVerifier bundleAtURLIsCodeSigned:self.invalidSignedAppPath], @"App expected to be code signed, but signature is invalid"); + XCTAssertTrue([SUCodeSigningVerifier bundleAtURLIsCodeSigned:self.invalidSignedAppURL], @"App expected to be code signed, but signature is invalid"); NSError *error = nil; - XCTAssertFalse([SUCodeSigningVerifier codeSignatureIsValidAtBundleURL:self.invalidSignedAppPath error:&error], @"signature should not be valid"); + XCTAssertFalse([SUCodeSigningVerifier codeSignatureIsValidAtBundleURL:self.invalidSignedAppURL error:&error], @"signature should not be valid"); XCTAssertNotNil(error, @"error should not be nil"); }