Commit 1603086c authored by George Nachman's avatar George Nachman

Put intermediate certs into signed archives

parent d5766abe
......@@ -71,6 +71,16 @@
if (![self writeCertificate:certificate toStream:writeStream error:error]) {
return NO;
}
// NOTE: The signing certificate must be first. This is a requirement of SecTrustCreateWithCertificates
// which is implicit in the file format.
SIGCertificate *issuerCertificate = _identity.signingCertificate.issuer;
while (issuerCertificate != nil) {
if (![self writeCertificate:issuerCertificate.data toStream:writeStream error:error]) {
return NO;
}
issuerCertificate = issuerCertificate.issuer;
}
[writeStream close];
return YES;
}
......
......@@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable NSString *)metadata:(out NSError **)error;
- (nullable NSData *)signature:(out NSError **)error;
- (nullable NSInputStream *)payloadInputStream:(out NSError **)error;
- (nullable NSData *)signingCertificate:(out NSError **)error;
- (nullable NSArray<NSData *> *)signingCertificates:(out NSError **)error;
@end
......
......@@ -97,15 +97,23 @@
return [chunk data:error];
}
- (NSData *)signingCertificate:(out NSError * _Nullable __autoreleasing *)error {
SIGArchiveChunk *chunk = [self chunkWithTag:SIGArchiveTagCertificate];
if (!chunk) {
- (NSArray<NSData *> *)signingCertificates:(out NSError * _Nullable __autoreleasing *)error {
NSArray<SIGArchiveChunk *> *chunks = [self chunksWithTag:SIGArchiveTagCertificate];
if (!chunks.count) {
if (error) {
*error = [SIGError errorWithCode:SIGErrorCodeNoCertificate];
}
return nil;
}
return [chunk data:error];
NSMutableArray<NSData *> *datas = [NSMutableArray array];
for (SIGArchiveChunk *chunk in chunks) {
NSData *data = [chunk data:error];
if (!data) {
return nil;
}
[datas addObject:data];
}
return datas;
}
- (NSInputStream *)payloadInputStream:(out NSError **)error {
......@@ -185,4 +193,14 @@
return _chunks[index];
}
- (NSArray<SIGArchiveChunk *> *)chunksWithTag:(SIGArchiveTag)tag {
NSIndexSet *indexes = [_chunks indexesOfObjectsPassingTest:^BOOL(SIGArchiveChunk * _Nonnull obj,
NSUInteger idx,
BOOL * _Nonnull stop) {
return obj.tag == tag;
}];
return [_chunks objectsAtIndexes:indexes];
}
@end
......@@ -26,7 +26,7 @@ static NSInteger SIGArchiveVerifiedLowestSupportedVersion = 1;
NSError *_readerLoadError;
SIGTrust *_trust;
NSInputStream *_payloadInputStream;
SIGCertificate *_certificate;
NSArray<SIGCertificate *> *_certificates;
NSData *_signatureData;
BOOL _called;
BOOL _prepared;
......@@ -255,22 +255,30 @@ static NSInteger SIGArchiveVerifiedLowestSupportedVersion = 1;
return NO;
}
NSData *certificateData = [_reader signingCertificate:error];
if (!certificateData) {
NSArray<NSData *> *certificateDatas = [_reader signingCertificates:error];
if (!certificateDatas) {
return NO;
}
if (certificateDatas.count == 0) {
*error = [SIGError errorWithCode:SIGErrorCodeNoCertificate];
}
_certificate = [[SIGCertificate alloc] initWithData:certificateData];
if (!_certificate) {
if (error) {
*error = [SIGError errorWithCode:SIGErrorCodeInputMalformedCertificate];
NSMutableArray<SIGCertificate *> *certs = [NSMutableArray array];
for (NSData *certificateData in certificateDatas) {
SIGCertificate *certificate = [[SIGCertificate alloc] initWithData:certificateData];
if (!certificate) {
if (error) {
*error = [SIGError errorWithCode:SIGErrorCodeInputMalformedCertificate];
}
return NO;
}
return NO;
[certs addObject:certificate];
}
_certificates = certs;
SIGX509Policy *x509 = [[SIGX509Policy alloc] init];
SIGCRLPolicy *crl = [[SIGCRLPolicy alloc] init];
_trust = [[SIGTrust alloc] initWithCertificates:@[ _certificate ]
_trust = [[SIGTrust alloc] initWithCertificates:certs
policies:@[ x509, crl ]
error:error];
if (!_trust) {
......@@ -298,7 +306,7 @@ static NSInteger SIGArchiveVerifiedLowestSupportedVersion = 1;
}
return [algorithm verifyInputStream:_payloadInputStream
signatureData:_signatureData
publicKey:_certificate.publicKey.secKey
publicKey:_certificates.firstObject.publicKey.secKey
error:error];
}
......
......@@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly, nullable) NSString *longDescription;
@property (nonatomic, readonly, nullable) NSString *name;
@property (nonatomic, readonly, nullable) NSData *serialNumber;
@property (nonatomic, readonly, nullable) SIGCertificate *issuer;
- (nullable instancetype)initWithSecCertificate:(SecCertificateRef)secCertificate NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithData:(NSData *)data NS_DESIGNATED_INITIALIZER;
......
......@@ -9,6 +9,7 @@
#import "SIGCertificate.h"
#import "SIGKey.h"
#import "SIGKeychain.h"
#import "SIGTrust.h"
@implementation SIGCertificate {
......@@ -99,4 +100,82 @@
return value;
}
+ (NSDictionary *)queryForCertWithName:(CFDataRef)name {
return @{ (__bridge id)kSecClass: (__bridge id)kSecClassCertificate,
(__bridge id)kSecAttrSubject: (__bridge NSData *)name,
(__bridge id)kSecReturnRef: (__bridge id)kCFBooleanTrue,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll };
}
+ (BOOL)certificateInArray:(CFArrayRef)array atIndex:(NSInteger)i hasName:(CFDataRef)name {
SecCertificateRef secCertificate = (SecCertificateRef)CFArrayGetValueAtIndex(array, i);
CFDataRef subjectContent = SecCertificateCopyNormalizedSubjectContent(secCertificate, NULL);
const BOOL result = CFEqual(subjectContent, name);
if (subjectContent) {
CFRelease(subjectContent);
}
return result;
}
+ (BOOL)certificateIsRootWithName:(CFDataRef)name {
CFArrayRef array;
OSStatus err = SecTrustCopyAnchorCertificates(&array);
if (err != noErr) {
NSLog(@"Failed to get root certs: %@",
(__bridge_transfer NSString *)SecCopyErrorMessageString(err, NULL));
return NO;
}
for (NSInteger i = 0; i < CFArrayGetCount(array); i++) {
if ([self certificateInArray:array atIndex:i hasName:name]) {
return YES;
}
}
return NO;
}
+ (SecCertificateRef)secCertificateWithName:(CFDataRef)name {
if (name == NULL) {
return NULL;
}
NSDictionary *query = [self queryForCertWithName:name];
CFTypeRef result = NULL;
OSErr err = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
if (err != noErr) {
if (![self certificateIsRootWithName:name]) {
NSLog(@"Failed to get certificate: %@",
(__bridge_transfer NSString *)SecCopyErrorMessageString(err, NULL));
}
return nil;
}
CFArrayRef array = (CFArrayRef)result;
for (NSInteger i = 0; i < CFArrayGetCount(array); i++) {
if ([SIGCertificate certificateInArray:array atIndex:i hasName:name]) {
return (SecCertificateRef)CFArrayGetValueAtIndex(array, i);
}
}
return NULL;
}
- (SIGCertificate *)issuer {
if (!_secCertificate) {
return nil;
}
SIGKeychain *keychain = [SIGKeychain sharedInstance];
if (keychain == nil) {
return nil;
}
CFDataRef issuerName = SecCertificateCopyNormalizedIssuerSequence(_secCertificate);
if (!issuerName) {
return nil;
}
SecCertificateRef secCertificate = [SIGCertificate secCertificateWithName:issuerName];
if (secCertificate == NULL) {
return nil;
}
return [[SIGCertificate alloc] initWithSecCertificate:secCertificate];
}
@end
......@@ -51,7 +51,7 @@
}
NSError *trustError = nil;
// Don't use the CRL policy because it would need to make a network round-trip.
SIGTrust *trust = [[SIGTrust alloc] initWithCertificates:@[ identity.signingCertificate]
SIGTrust *trust = [[SIGTrust alloc] initWithCertificates:@[ identity.signingCertificate ]
policies:@[ [[SIGX509Policy alloc] init] ]
error:&trustError];
if (!trust || trustError) {
......
......@@ -102,7 +102,18 @@ static BOOL sInstallingScript;
}
if (requireSignature) {
NSData *data = [[verifier.reader signingCertificates:nil] firstObject];
if (!data) {
completion(nil, @"Could not find certificate after verficiation (nil data)", NO);
return;
}
SIGCertificate *cert = [[SIGCertificate alloc] initWithData:data];
if (!cert) {
completion(nil, @"Could not find certificate after verficiation (bad data)", NO);
return;
}
[self confirmInstallationOfVerifiedArchive:verifier.reader
withCertificate:cert
completion:^(BOOL ok) {
if (!ok) {
completion(nil, @"Installation canceled by user request.", NO);
......@@ -136,9 +147,11 @@ static BOOL sInstallingScript;
}
+ (void)confirmInstallationOfVerifiedArchive:(SIGArchiveReader *)reader
withCertificate:(SIGCertificate *)cert
completion:(void (^)(BOOL ok))completion {
SIGCertificate *cert = [[SIGCertificate alloc] initWithData:[reader signingCertificate:nil]];
NSString *body = [NSString stringWithFormat:@"The signature of ”%@” has been verified. The author is:\n\n%@\n\nWould you like to install it?", reader.url.lastPathComponent, ((cert.name ?: cert.longDescription) ?: @"Unknown")];
NSString *body = [NSString stringWithFormat:@"The signature of ”%@” has been verified. The author is:\n\n%@\n\nWould you like to install it?",
reader.url.lastPathComponent,
((cert.name ?: cert.longDescription) ?: @"Unknown")];
iTermWarningSelection selection = [iTermWarning showWarningWithTitle:body
actions:@[ @"OK", @"Cancel" ]
accessory:nil
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment