Skip to content

Commit

Permalink
Verify CMS signatures (#45)
Browse files Browse the repository at this point in the history
IB-6521

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma authored Jul 28, 2020
1 parent fa779ce commit 5767c40
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 77 deletions.
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
21 changes: 7 additions & 14 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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")

Expand All @@ -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
Expand Down Expand Up @@ -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 $<TARGET_FILE:id-updater-helper> $<TARGET_FILE_DIR:${PROGNAME}>/../Resources )
Expand Down
6 changes: 6 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
2 changes: 1 addition & 1 deletion cmake
Submodule cmake updated 1 files
+10 −17 modules/FindPKCS11.cmake
2 changes: 1 addition & 1 deletion common
20 changes: 10 additions & 10 deletions prefPane/id-updater-helper.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
60 changes: 40 additions & 20 deletions prefPane/id-updater.m
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ @interface ID_updater : NSPreferencePane <UpdateDelegate, NSURLSessionDownloadDe
@implementation ID_updater

- (void)mainViewDidLoad {
NSDictionary *schedule = [NSDictionary dictionaryWithContentsOfFile:[@"~/Library/LaunchAgents/ee.ria.id-updater.plist" stringByStandardizingPath]];
NSDictionary *schedule = [NSDictionary dictionaryWithContentsOfFile:(@"~/Library/LaunchAgents/ee.ria.id-updater.plist").stringByStandardizingPath];
if (!schedule) {
[changeSchedule selectItemAtIndex:3];
} else if ([(NSDictionary*)[schedule objectForKey:@"StartCalendarInterval"] objectForKey:@"Weekday"]) {
} else if (((NSDictionary*)schedule[@"StartCalendarInterval"])[@"Weekday"]) {
[changeSchedule selectItemAtIndex:1];
} else if ([(NSDictionary*)[schedule objectForKey:@"StartCalendarInterval"] objectForKey:@"Day"]) {
} else if (((NSDictionary*)schedule[@"StartCalendarInterval"])[@"Day"]) {
[changeSchedule selectItemAtIndex:2];
}

Expand All @@ -69,9 +69,9 @@ - (void)mainViewDidLoad {
bundlelang = self.bundle;
NSArray *languages = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"];
NSLog(@"Languages %@", languages);
if([@"et" isEqualToString:[languages objectAtIndex:0]]) {
NSLog(@"Estonian %@", [self.bundle pathForResource:[languages objectAtIndex:0] ofType:@"lproj"]);
bundlelang = [NSBundle bundleWithPath:[self.bundle pathForResource:[languages objectAtIndex:0] ofType:@"lproj"]];
if([@"et" isEqualToString:languages[0]]) {
NSLog(@"Estonian %@", [self.bundle pathForResource:languages[0] ofType:@"lproj"]);
bundlelang = [NSBundle bundleWithPath:[self.bundle pathForResource:languages[0] ofType:@"lproj"]];
}
status.stringValue = NSLocalizedString(status.stringValue, nil);
changeScheduleLabel.stringValue = NSLocalizedString(changeScheduleLabel.stringValue, nil);
Expand All @@ -88,7 +88,7 @@ - (void)mainViewDidLoad {

NSMutableAttributedString *changelogurl = [[NSMutableAttributedString alloc]
initWithString:NSLocalizedString(@"http://www.id.ee/eng/changelog", nil)];
[changelogurl addAttribute:NSLinkAttributeName value:[changelogurl string] range:NSMakeRange(0, [changelogurl length])];
[changelogurl addAttribute:NSLinkAttributeName value:changelogurl.string range:NSMakeRange(0, changelogurl.length)];
[changelog.textStorage setAttributedString:changelogurl];

NSDictionary *versions = @{
Expand All @@ -113,7 +113,7 @@ - (void)mainViewDidLoad {
};
NSMutableArray *list = [[NSMutableArray alloc] init];
[versions enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL *stop) {
if (object != nil && [(NSString*)object length] != 0)
if (object != nil && ((NSString*)object).length != 0)
[list addObject:[NSString stringWithFormat:@"%@ (%@)", key, object]];
}];
info.stringValue = [list componentsJoinedByString:@"\n"];
Expand All @@ -138,13 +138,13 @@ - (void)didFinish:(NSError *)error {
case InvalidSignature:
status.stringValue = NSLocalizedString(@"The configuration file located on the server cannot be validated.", nil);
break;

case FileNotFound:
status.stringValue = NSLocalizedString(@"File not found", nil);
break;

default:
status.stringValue = [error localizedDescription];
status.stringValue = error.localizedDescription;
break;
}
}
Expand Down Expand Up @@ -196,7 +196,7 @@ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTas
[progress stopAnimation:self];
[timer invalidate];
timer = nil;
NSString *tmp = [NSString stringWithFormat:@"%@/%@", NSTemporaryDirectory(), [filename lastPathComponent]];
NSString *tmp = [NSString stringWithFormat:@"%@/%@", NSTemporaryDirectory(), filename.lastPathComponent];
[NSFileManager.defaultManager removeItemAtPath:tmp error:nil];
[NSFileManager.defaultManager moveItemAtPath:location.path toPath:tmp error:nil];

Expand All @@ -220,44 +220,64 @@ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTas
return;
}

id certref = nil;
NSData *certData;
xar_signature_t sig = xar_signature_first(xar);
for (int32_t i = 0, count = xar_signature_get_x509certificate_count(sig); i < count; ++i) {
xar_signature_t next = xar_signature_next(sig);
if(next && strcmp("CMS", xar_signature_type(next)) == 0)
sig = next;
NSString *signatureType = @(xar_signature_type(sig));
NSLog(@"Signature type %@", signatureType);
for (int32_t i = 0, count = xar_signature_get_x509certificate_count(sig); i < count; ++i) {
uint32_t size = 0;
const uint8_t *data = nil;
if (xar_signature_get_x509certificate_data(sig, i, &data, &size))
continue;

NSData *der = [NSData dataWithBytesNoCopy:(uint8_t*)data length:size freeWhenDone:NO];
if ([update.centralConfig[@"CERT-BUNDLE"] containsObject:[der base64EncodedStringWithOptions:0]])
certref = CFAutorelease(SecCertificateCreateWithData(0, (__bridge CFDataRef)der));
certData = [NSData dataWithBytes:(uint8_t*)data length:size]; // Make copy of memory will be lost after xar_close
}

if (!certref) {
if (!certData) {
status.stringValue = NSLocalizedString(@"No matching certificate", nil);
xar_close(xar);
return;
}

uint8_t *data = nil, *signature = nil;
uint32_t dataSize = 0, signatureSize = 0;
uint8_t *signedData = nil, *signatureData = nil;
uint32_t signedDataSize = 0, signatureDataSize = 0;
off_t offset = 0;
uint8_t err = xar_signature_copy_signed_data(sig, &data, &dataSize, &signature, &signatureSize, &offset);
uint8_t err = xar_signature_copy_signed_data(sig, &signedData, &signedDataSize, &signatureData, &signatureDataSize, &offset);
NSData *signature = [NSData dataWithBytesNoCopy:signatureData length:signatureDataSize];
NSData *data = [NSData dataWithBytesNoCopy:signedData length:signedDataSize];
xar_close(xar);
if (err) {
status.stringValue = NSLocalizedString(@"Failed to copy signature", nil);
return;
}

if([signatureType isEqualToString:@"CMS"]) {
if ([update verifyCMSSignature:signature data:data cert:certData])
[NSTask launchedTaskWithLaunchPath:@"/usr/bin/open" arguments:@[path]];
else
{
NSLog(@"CMS Verify error");
status.stringValue = NSLocalizedString(@"Failed to verify signature", nil);
}
return;
}

SecCertificateRef certref = SecCertificateCreateWithData(0, (__bridge CFDataRef)certData);
SecKeyRef publickey = nil;
OSStatus oserr = SecCertificateCopyPublicKey((__bridge SecCertificateRef)certref, &publickey);
OSStatus oserr = SecCertificateCopyPublicKey(certref, &publickey);
CFRelease(certref);
if (oserr) {
status.stringValue = NSLocalizedString(@"Failed to copy public key", nil);
return;
}

NSError *error = nil;
bool isValid = [update verifySignature:[NSData dataWithBytesNoCopy:signature length:signatureSize] data:[NSData dataWithBytesNoCopy:data length:dataSize] key:publickey digest:nil error:&error];
bool isValid = [update verifySignature:signature data:data key:publickey digestSize:nil error:&error];
CFRelease(publickey);
if (isValid)
[NSTask launchedTaskWithLaunchPath:@"/usr/bin/open" arguments:@[path]];
Expand Down
3 changes: 2 additions & 1 deletion prefPane/update.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ enum {
- (id)initWithDelegate:(id <UpdateDelegate>)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;
Expand Down
Loading

0 comments on commit 5767c40

Please sign in to comment.