From 5767c40764fba728ae60a87c76cc0351759eb683 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Tue, 28 Jul 2020 14:48:37 +0300 Subject: [PATCH] Verify CMS signatures (#45) IB-6521 Signed-off-by: Raul Metsma --- .travis.yml | 5 ++- CMakeLists.txt | 21 +++------ RELEASE-NOTES.md | 6 +++ cmake | 2 +- common | 2 +- prefPane/id-updater-helper.m | 20 ++++----- prefPane/id-updater.m | 60 ++++++++++++++++--------- prefPane/update.h | 3 +- prefPane/update.m | 85 ++++++++++++++++++++++++------------ 9 files changed, 127 insertions(+), 77 deletions(-) diff --git a/.travis.yml b/.travis.yml index 09e2daf..f9d8973 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,8 @@ language: objective-c -osx_image: xcode11.4 +osx_image: xcode11.6 +env: + global: + - MACOSX_DEPLOYMENT_TARGET=10.13 script: - mkdir build - cd build diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b0b1eb..4fbb13d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,8 @@ -cmake_minimum_required(VERSION 3.5) -if(POLICY CMP0071) - cmake_policy(SET CMP0071 NEW) -endif() +cmake_minimum_required(VERSION 3.16) if(POLICY CMP0074) cmake_policy(SET CMP0074 NEW) endif() -project(id-updater VERSION 3.12.9) +project(id-updater VERSION 3.13.0 LANGUAGES CXX) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") @@ -19,18 +16,15 @@ string( REPLACE ".json" ".pub" PUB_URL ${CONFIG_URL} ) file( DOWNLOAD ${PUB_URL} ${CMAKE_CURRENT_BINARY_DIR}/config.pub ) if( APPLE ) + enable_language(OBJC) add_custom_command( OUTPUT config.h COMMAND xxd -i config.pub config.h COMMENT "Generating config.h" ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) - set_source_files_properties( prefPane/id-updater-helper.m prefPane/id-updater.m prefPane/update.m PROPERTIES LANGUAGE C ) add_executable( id-updater-helper prefPane/id-updater-helper.m prefPane/update.m config.h ) - set_target_properties( id-updater-helper PROPERTIES - COMPILE_FLAGS "-Wno-unused-parameter -fobjc-arc" - LINKER_LANGUAGE C - ) - target_link_libraries( id-updater-helper "-framework Foundation -framework PCSC -framework Security" ) + target_compile_options(id-updater-helper PRIVATE "-Wno-unused-parameter" "-fobjc-arc") + target_link_libraries(id-updater-helper "-framework Foundation -framework CryptoTokenKit -framework Security") add_custom_command( OUTPUT ID_updater.nib COMMAND ibtool --errors --warnings --notices --output-format human-readable-text @@ -70,12 +64,11 @@ if( APPLE ) BUNDLE_EXTENSION prefPane RESOURCES prefPane/Icon.icns COMPILE_FLAGS "-Wno-unused-parameter -fobjc-arc" - LINKER_LANGUAGE C XCODE_ATTRIBUTE_WRAPPER_EXTENSION prefPane MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/prefPane/Info.plist.cmake ) - target_link_libraries( ${PROGNAME} ${XAR} - "-framework Security -framework Cocoa -framework PreferencePanes -framework PCSC -framework Security" + target_link_libraries(${PROGNAME} ${XAR} + "-framework Cocoa -framework PreferencePanes -framework CryptoTokenKit -framework Security" ) add_custom_command( TARGET ${PROGNAME} POST_BUILD COMMAND cp $ $/../Resources ) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 6ec79b6..b3c6cea 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,3 +1,9 @@ +Id-updater version [3.13.0](https://github.com/open-eid/updater/releases/tag/v3.13.0) release notes +-------------------------------------- +- Implement CMS based macOS package signature check + +[Full Changelog](https://github.com/open-eid/updater/compare/v3.12.9...v3.13.0) + Id-updater version [3.12.9](https://github.com/open-eid/updater/releases/tag/v3.12.9) release notes -------------------------------------- - Enable dark mode when building old sdk (#42) diff --git a/cmake b/cmake index 2475e1b..9af4598 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 2475e1bbf4934f9dc9c7d24008ed9c506b95e48e +Subproject commit 9af459839edc19107e57fb9685fe8f571092d2df diff --git a/common b/common index 0a626b5..f982a4b 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 0a626b598fc5beb46423480791c7f5efdc6150c7 +Subproject commit f982a4b426fb42a51bd17c5d421836ba31145038 diff --git a/prefPane/id-updater-helper.m b/prefPane/id-updater-helper.m index f4a2b86..4a5117d 100644 --- a/prefPane/id-updater-helper.m +++ b/prefPane/id-updater-helper.m @@ -65,13 +65,13 @@ int main(int argc, const char * argv[]) @autoreleasepool { if (strcmp(argv[1], "-task") == 0) { - Updater *updater __attribute__((unused)) = [[Updater alloc] initWithPath:[[NSString stringWithFormat:@"%s/../../..", argv[0]] stringByStandardizingPath]]; + Updater *updater __attribute__((unused)) = [[Updater alloc] initWithPath:[NSString stringWithFormat:@"%s/../../..", argv[0]].stringByStandardizingPath]; [NSRunLoop.mainRunLoop run]; return 0; } - NSString *PATH = [@"~/Library/LaunchAgents/ee.ria.id-updater.plist" stringByStandardizingPath]; - [NSFileManager.defaultManager createDirectoryAtPath:[@"~/Library/LaunchAgents" stringByStandardizingPath] withIntermediateDirectories:YES attributes:nil error:nil]; + NSString *PATH = (@"~/Library/LaunchAgents/ee.ria.id-updater.plist").stringByStandardizingPath; + [NSFileManager.defaultManager createDirectoryAtPath:(@"~/Library/LaunchAgents").stringByStandardizingPath withIntermediateDirectories:YES attributes:nil error:nil]; if (strcmp(argv[1], "-remove") == 0) { [[NSTask launchedTaskWithLaunchPath:@"/bin/launchctl" arguments:@[@"unload", @"-w", PATH]] waitUntilExit]; NSError *error; @@ -82,22 +82,22 @@ int main(int argc, const char * argv[]) NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitWeekday|NSCalendarUnitDay fromDate:[NSDate date]]; - NSNumber *hour = [NSNumber numberWithInteger:[components hour]]; - NSNumber *minute = [NSNumber numberWithInteger:[components minute]]; - NSNumber *weekday = [NSNumber numberWithInteger:[components weekday]]; - NSNumber *day = [NSNumber numberWithInteger:[components day]]; + NSNumber *hour = @(components.hour); + NSNumber *minute = @(components.minute); + NSNumber *weekday = @(components.weekday); + NSNumber *day = @(components.day); NSDictionary *settings = nil; if (strcmp(argv[1], "-daily") == 0) { settings = @{@"Label": UPDATER_ID, - @"ProgramArguments": @[[NSString stringWithUTF8String:argv[0]], @"-task"], + @"ProgramArguments": @[@(argv[0]), @"-task"], @"StartCalendarInterval": @{@"Hour": hour, @"Minute": minute}}; } else if (strcmp(argv[1], "-weekly") == 0) { settings = @{@"Label": UPDATER_ID, - @"ProgramArguments": @[[NSString stringWithUTF8String:argv[0]], @"-task"], + @"ProgramArguments": @[@(argv[0]), @"-task"], @"StartCalendarInterval": @{@"Hour": hour, @"Minute": minute, @"Weekday": weekday}}; } else if (strcmp(argv[1], "-monthly") == 0) { settings = @{@"Label": UPDATER_ID, - @"ProgramArguments": @[[NSString stringWithUTF8String:argv[0]], @"-task"], + @"ProgramArguments": @[@(argv[0]), @"-task"], @"StartCalendarInterval": @{@"Hour": hour, @"Minute": minute, @"Day": day}}; } else { return 0; diff --git a/prefPane/id-updater.m b/prefPane/id-updater.m index de3328d..e9e52d1 100644 --- a/prefPane/id-updater.m +++ b/prefPane/id-updater.m @@ -55,12 +55,12 @@ @interface ID_updater : NSPreferencePane )delegate; - (void)request; - (NSString *)userAgent; -- (BOOL)verifySignature:(NSData *)signature data:(NSData *)data key:(SecKeyRef)key digest:(CFNumberRef)digest error:(NSError **)error; +- (BOOL)verifyCMSSignature:(NSData *)signatureData data:(NSData *)data cert:(NSData *)cert; +- (BOOL)verifySignature:(NSData *)signature data:(NSData *)data key:(SecKeyRef)key digestSize:(CFNumberRef)digestSize error:(NSError **)error; - (NSString*)versionInfo:(NSString *)pkg; @property(retain) NSString *baseversion; diff --git a/prefPane/update.m b/prefPane/update.m index 3241e11..f73ea41 100644 --- a/prefPane/update.m +++ b/prefPane/update.m @@ -19,9 +19,8 @@ #import "update.h" -#import - -#include +#import +#import #include @@ -61,19 +60,7 @@ - (NSString*)userAgent { struct utsname unameData; uname(&unameData); - SCARDCONTEXT ctx = 0; - SCardEstablishContext(SCARD_SCOPE_SYSTEM, 0, 0, &ctx); - uint32_t size = 0; - SCardListReaders(ctx, 0, 0, &size); - char *readers = (char*)malloc(size * sizeof(char)); - SCardListReaders(ctx, 0, readers, &size); - NSMutableArray *list = [NSMutableArray array]; - for (char *p = readers; *p; p += strlen(p) + 1) { - [list addObject:[NSString stringWithCString:p encoding:NSUTF8StringEncoding]]; - } - free(readers); - SCardReleaseContext(ctx); - + NSString *devices = [TKSmartCardSlotManager.defaultManager.slotNames componentsJoinedByString:@"/"]; NSMutableArray *agent = [NSMutableArray arrayWithObject:[NSString stringWithFormat:@"id-updater/%@", self.baseversion]]; if (self.clientversion) { [agent addObject:[NSString stringWithFormat:@"qdigidocclient/%@", self.clientversion]]; @@ -85,13 +72,13 @@ - (NSString*)userAgent { [agent addObject:[NSString stringWithFormat:@"qdigidoc4/%@", self.digidoc4]]; } [agent addObject:[NSString stringWithFormat:@"(Mac OS %@(%lu/%s)) Locale: %@ Devices: %@", - [os objectForKey:@"ProductVersion"], sizeof(void *)<<3, unameData.machine, @"UTF-8", [list componentsJoinedByString:@"/"]]]; + os[@"ProductVersion"], sizeof(void *)<<3, unameData.machine, @"UTF-8", devices]]; return [agent componentsJoinedByString:@" "]; } - (BOOL)verify:(NSData *)data error:(NSError **)error { - NSString *pem = [NSString stringWithUTF8String:(char*)config_pub]; + NSString *pem = @((char*)config_pub); pem = [pem stringByReplacingOccurrencesOfString:@"-----BEGIN RSA PUBLIC KEY-----" withString:@""]; pem = [pem stringByReplacingOccurrencesOfString:@"-----END RSA PUBLIC KEY-----" withString:@""]; pem = [NSString stringWithFormat:@"%@%@", @"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A", pem]; @@ -103,34 +90,74 @@ - (BOOL)verify:(NSData *)data error:(NSError **)error CFErrorRef err = 0; id key = CFBridgingRelease(SecKeyCreateFromData((__bridge CFDictionaryRef)parameters, (__bridge CFDataRef)keyData, &err)); if (err) { if(error) *error = CFBridgingRelease(err); return false; } - return [self verifySignature:[[NSData alloc] initWithBase64EncodedString:signature options:NSDataBase64DecodingIgnoreUnknownCharacters] data:data key:(__bridge SecKeyRef)key digest:(__bridge CFNumberRef)@512 error:error]; + return [self verifySignature:[[NSData alloc] initWithBase64EncodedString:signature options:NSDataBase64DecodingIgnoreUnknownCharacters] data:data key:(__bridge SecKeyRef)key digestSize:(__bridge CFNumberRef)@512 error:error]; +} + +- (BOOL)verifyCMSSignature:(NSData *)signatureData data:(NSData *)data cert:(NSData *)cert { + #define RETURN_IF_OERROR(MSG) if (oserr) { NSLog(MSG); return false; } + CMSDecoderRef decoderRef; + OSStatus oserr = CMSDecoderCreate(&decoderRef); + RETURN_IF_OERROR(@"CMSDecoderCreate") + id decoder = CFBridgingRelease(decoderRef); + + oserr = CMSDecoderUpdateMessage((__bridge CMSDecoderRef)decoder, signatureData.bytes, signatureData.length); + RETURN_IF_OERROR(@"CMSDecoderUpdateMessage") + oserr = CMSDecoderFinalizeMessage((__bridge CMSDecoderRef)decoder); + RETURN_IF_OERROR(@"CMSDecoderFinalizeMessage") + oserr = CMSDecoderSetDetachedContent((__bridge CMSDecoderRef)decoder, (__bridge CFDataRef)data); + RETURN_IF_OERROR(@"CMSDecoderSetDetachedContent") + + size_t numSignersOut = 0; + oserr = CMSDecoderGetNumSigners((__bridge CMSDecoderRef)decoder, &numSignersOut); + RETURN_IF_OERROR(@"CMSDecoderGetNumSigners") + if (numSignersOut != 1) { + NSLog(@"Invalid number of signers: %lu", numSignersOut); + return false; + } + + SecPolicyRef policy = SecPolicyCreateBasicX509(); + CMSSignerStatus status; + oserr = CMSDecoderCopySignerStatus((__bridge CMSDecoderRef)decoder, 0, policy, TRUE, &status, nil, nil); + CFRelease(policy); + RETURN_IF_OERROR(@"CMSDecoderCopySignerStatus") + bool isValid = status == kCMSSignerValid; + + SecCertificateRef signerCert; + oserr = CMSDecoderCopySignerCert((__bridge CMSDecoderRef)decoder, 0, &signerCert); + RETURN_IF_OERROR(@"CMSDecoderCopySignerCert") + bool isSameCert = [cert isEqualToData:CFBridgingRelease(SecCertificateCopyData(signerCert))]; + CFRelease(signerCert); + + NSLog(@"Signature is (%d) and cert is equal(%d)", isValid, isSameCert); + return isValid && isSameCert; } -- (BOOL)verifySignature:(NSData *)signatureData data:(NSData *)data key:(SecKeyRef)key digest:(CFNumberRef)digest error:(NSError **)error { +- (BOOL)verifySignature:(NSData *)signatureData data:(NSData *)data key:(SecKeyRef)key digestSize:(CFNumberRef)digestSize error:(NSError **)error { CFErrorRef err = nil; + #define RETURN_IF_ERROR if (err) { if(error) *error = CFBridgingRelease(err); return false; } id verifier = CFBridgingRelease(SecVerifyTransformCreate(key, (__bridge CFDataRef)signatureData, &err)); - if (err) { if(error) *error = CFBridgingRelease(err); return false; } + RETURN_IF_ERROR SecTransformSetAttribute((__bridge SecTransformRef)verifier, kSecTransformInputAttributeName, (__bridge CFDataRef)data, &err); - if (err) { if(error) *error = CFBridgingRelease(err); return false; } - if (digest != nil) { + RETURN_IF_ERROR + if (digestSize != nil) { SecTransformSetAttribute((__bridge SecTransformRef)verifier, kSecDigestTypeAttribute, kSecDigestSHA2, &err); - if (err) { if(error) *error = CFBridgingRelease(err); return false; } - SecTransformSetAttribute((__bridge SecTransformRef)verifier, kSecDigestLengthAttribute, digest, &err); - if (err) { if(error) *error = CFBridgingRelease(err); return false; } + RETURN_IF_ERROR + SecTransformSetAttribute((__bridge SecTransformRef)verifier, kSecDigestLengthAttribute, digestSize, &err); + RETURN_IF_ERROR } else { SecTransformSetAttribute((__bridge SecTransformRef)verifier, kSecInputIsAttributeName, kSecInputIsDigest, &err); - if (err) { if(error) *error = CFBridgingRelease(err); return false; } + RETURN_IF_ERROR } CFTypeRef result = SecTransformExecute((__bridge SecTransformRef)verifier, &err); bool isValid = result == kCFBooleanTrue; CFRelease(result); - if (err) { if(error) *error = CFBridgingRelease(err); return false; } + RETURN_IF_ERROR return isValid; } - (NSString*)versionInfo:(NSString *)pkg { NSDictionary *list = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"/var/db/receipts/%@.plist", pkg]]; - return list ? [list objectForKey:@"PackageVersion"] : [NSString string]; + return list ? list[@"PackageVersion"] : [NSString string]; } - (void)receivedData:(NSData *)data withResponse:(NSURLResponse *)response