Add setup method to UD manager. Try to verify server certificate expiration.
This commit is contained in:
parent
7fd15d2fd9
commit
f7379deb69
|
@ -44,6 +44,7 @@
|
|||
#import <SignalServiceKit/OWSPrimaryStorage+Calling.h>
|
||||
#import <SignalServiceKit/OWSReadReceiptManager.h>
|
||||
#import <SignalServiceKit/SSKEnvironment.h>
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
#import <SignalServiceKit/TSAccountManager.h>
|
||||
#import <SignalServiceKit/TSDatabaseView.h>
|
||||
#import <SignalServiceKit/TSPreKeyManager.h>
|
||||
|
@ -1087,6 +1088,8 @@ static NSTimeInterval launchStartedAt;
|
|||
// Resume lazy restore.
|
||||
[OWSBackupLazyRestoreJob runAsync];
|
||||
#endif
|
||||
|
||||
[SSKEnvironment.shared.udManager setup];
|
||||
}
|
||||
|
||||
- (void)registrationStateDidChange
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSCertificateExpiration : NSObject
|
||||
|
||||
+ (nullable NSDate *)expirationDataForCertificate:(NSData *)certificateData;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,109 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSCertificateExpiration.h"
|
||||
#import "OWSFileSystem.h"
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
#import <SignalCoreKit/Cryptography.h>
|
||||
#import <SignalCoreKit/NSData+OWS.h>
|
||||
#import <openssl/x509.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation OWSCertificateExpiration
|
||||
|
||||
// PEM is just a series of blocks of base-64 encoded DER data.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail
|
||||
+ (nullable NSArray<NSData *> *)convertPemToDer:(NSString *)pemString
|
||||
{
|
||||
NSMutableArray<NSData *> *certificateDatas = [NSMutableArray new];
|
||||
|
||||
NSError *error;
|
||||
// We use ? for non-greedy matching.
|
||||
NSRegularExpression *_Nullable regex = [NSRegularExpression
|
||||
regularExpressionWithPattern:@"-----BEGIN.*?-----(.+?)-----END.*?-----"
|
||||
options:NSRegularExpressionCaseInsensitive | NSRegularExpressionDotMatchesLineSeparators
|
||||
error:&error];
|
||||
if (!regex || error) {
|
||||
OWSFailDebug(@"could parse regex: %@.", error);
|
||||
return nil;
|
||||
}
|
||||
|
||||
[regex enumerateMatchesInString:pemString
|
||||
options:0
|
||||
range:NSMakeRange(0, pemString.length)
|
||||
usingBlock:^(NSTextCheckingResult *_Nullable result, NSMatchingFlags flags, BOOL *stop) {
|
||||
if (result.numberOfRanges != 2) {
|
||||
OWSFailDebug(@"invalid PEM regex match.");
|
||||
return;
|
||||
}
|
||||
NSString *_Nullable derString = [pemString substringWithRange:[result rangeAtIndex:1]];
|
||||
if (derString.length < 1) {
|
||||
OWSFailDebug(@"empty PEM match.");
|
||||
return;
|
||||
}
|
||||
// dataFromBase64String will ignore whitespace, which is
|
||||
// necessary.
|
||||
NSData *_Nullable derData = [NSData dataFromBase64String:derString];
|
||||
if (derData.length < 1) {
|
||||
OWSFailDebug(@"could not parse PEM match.");
|
||||
return;
|
||||
}
|
||||
[certificateDatas addObject:derData];
|
||||
}];
|
||||
|
||||
return certificateDatas;
|
||||
}
|
||||
|
||||
+ (nullable NSDate *)expirationDataForCertificate:(NSData *)certificateData
|
||||
{
|
||||
OWSAssertDebug(certificateData);
|
||||
|
||||
NSString *temporaryFilePath = [OWSFileSystem temporaryFilePath];
|
||||
[certificateData writeToFile:temporaryFilePath atomically:YES];
|
||||
OWSLogInfo(@"temporaryFilePath: %@", temporaryFilePath);
|
||||
|
||||
OWSLogInfo(@"certificateData: %@", certificateData.hexadecimalString);
|
||||
NSString *pemString = [[NSString alloc] initWithData:certificateData encoding:NSUTF8StringEncoding];
|
||||
OWSLogInfo(@"pemString: %@", pemString);
|
||||
[DDLog flushLog];
|
||||
|
||||
if (certificateData.length >= UINT32_MAX) {
|
||||
OWSFailDebug(@"certificate data is too long.");
|
||||
return nil;
|
||||
}
|
||||
const unsigned char *certificateDataBytes = (const unsigned char *)[certificateData bytes];
|
||||
X509 *_Nullable certificateX509 = d2i_X509(NULL, &certificateDataBytes, [certificateData length]);
|
||||
if (!certificateX509) {
|
||||
OWSFailDebug(@"could not parse certificate.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
ASN1_TIME *not_after = X509_get_notAfter(certificateX509);
|
||||
OWSAssert(not_after);
|
||||
|
||||
BIO *b = BIO_new(BIO_s_mem());
|
||||
int rc = ASN1_TIME_print(b, not_after);
|
||||
if (rc <= 0) {
|
||||
OWSLogError(@"ASN1_TIME_print() failed.");
|
||||
BIO_free(b);
|
||||
return nil;
|
||||
}
|
||||
|
||||
const NSUInteger kASN1TimeBufferLength = 128;
|
||||
char buffer[kASN1TimeBufferLength];
|
||||
rc = BIO_gets(b, buffer, kASN1TimeBufferLength);
|
||||
if (rc <= 0) {
|
||||
OWSLogError(@"BIO_gets() failed.");
|
||||
BIO_free(b);
|
||||
return nil;
|
||||
}
|
||||
BIO_free(b);
|
||||
|
||||
return nil;
|
||||
}
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -11,6 +11,10 @@ public enum OWSUDError: Error {
|
|||
|
||||
@objc public protocol OWSUDManager: class {
|
||||
|
||||
@objc func setup()
|
||||
|
||||
// MARK: - Recipient state
|
||||
|
||||
@objc func isUDRecipientId(_ recipientId: String) -> Bool
|
||||
|
||||
// No-op if this recipient id is already marked as a "UD recipient".
|
||||
|
@ -18,6 +22,9 @@ public enum OWSUDError: Error {
|
|||
|
||||
// No-op if this recipient id is already marked as _NOT_ a "UD recipient".
|
||||
@objc func removeUDRecipientId(_ recipientId: String)
|
||||
|
||||
@objc func ensureServerCertificateObjC(success:@escaping (Data) -> Void,
|
||||
failure:@escaping (Error) -> Void)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
@ -40,6 +47,26 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
|
|||
SwiftSingletons.register(self)
|
||||
}
|
||||
|
||||
@objc public func setup() {
|
||||
AppReadiness.runNowOrWhenAppIsReady {
|
||||
guard TSAccountManager.isRegistered() else {
|
||||
return
|
||||
}
|
||||
self.ensureServerCertificate().retainUntilComplete()
|
||||
}
|
||||
NotificationCenter.default.addObserver(self,
|
||||
selector: #selector(registrationStateDidChange),
|
||||
name: .RegistrationStateDidChange,
|
||||
object: nil)
|
||||
}
|
||||
|
||||
@objc
|
||||
func registrationStateDidChange() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
ensureServerCertificate().retainUntilComplete()
|
||||
}
|
||||
|
||||
// MARK: - Singletons
|
||||
|
||||
private var networkManager: TSNetworkManager {
|
||||
|
@ -73,10 +100,17 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
|
|||
#endif
|
||||
|
||||
private func serverCertificate() -> Data? {
|
||||
return nil
|
||||
guard let certificateData = dbConnection.object(forKey: kUDCurrentServerCertificateKey, inCollection: kUDCollection) as? Data else {
|
||||
return nil
|
||||
}
|
||||
// TODO: Parse certificate and ensure that it is still valid.
|
||||
|
||||
// Parse certificate and ensure that it is still valid.
|
||||
guard !isCertificateExpired(certificateData: certificateData) else {
|
||||
Logger.warn("Current server certificate has expired.")
|
||||
return nil
|
||||
}
|
||||
|
||||
return certificateData
|
||||
}
|
||||
|
||||
|
@ -98,7 +132,7 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
|
|||
|
||||
public func ensureServerCertificate() -> Promise<Data> {
|
||||
return Promise { fulfill, reject in
|
||||
// If there is an existing server certificate, use that.
|
||||
// If there is a valid cached server certificate, use that.
|
||||
if let certificateData = serverCertificate() {
|
||||
fulfill(certificateData)
|
||||
return
|
||||
|
@ -123,9 +157,16 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
|
|||
do {
|
||||
let certificateData = try self.parseServerCertificateResponse(responseObject: responseObject)
|
||||
|
||||
guard !self.isCertificateExpired(certificateData: certificateData) else {
|
||||
reject (OWSUDError.assertionError(description: "Invalid server certificate returned by server"))
|
||||
return
|
||||
}
|
||||
|
||||
// Cache the current server certificate.
|
||||
self.setServerCertificate(certificateData)
|
||||
|
||||
fulfill(certificateData)
|
||||
} catch {
|
||||
|
||||
reject(error)
|
||||
}
|
||||
},
|
||||
|
@ -147,4 +188,12 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
|
|||
|
||||
return try parser.requiredBase64EncodedData(key: "certificate")
|
||||
}
|
||||
|
||||
private func isCertificateExpired(certificateData: Data) -> Bool {
|
||||
guard let expirationData = OWSCertificateExpiration.expirationData(forCertificate: certificateData) else {
|
||||
return true
|
||||
}
|
||||
// TODO:
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -26,24 +26,24 @@ typedef NS_ENUM(NSInteger, TSWhisperMessageType) {
|
|||
|
||||
//#ifndef DEBUG
|
||||
|
||||
// Production
|
||||
#define textSecureWebSocketAPI @"wss://textsecure-service.whispersystems.org/v1/websocket/"
|
||||
#define textSecureServerURL @"https://textsecure-service.whispersystems.org/"
|
||||
#define textSecureCDNServerURL @"https://cdn.signal.org"
|
||||
// Use same reflector for service and CDN
|
||||
#define textSecureServiceReflectorHost @"textsecure-service-reflected.whispersystems.org"
|
||||
#define textSecureCDNReflectorHost @"textsecure-service-reflected.whispersystems.org"
|
||||
#define contactDiscoveryURL @"https://api.directory.signal.org"
|
||||
//// Production
|
||||
//#define textSecureWebSocketAPI @"wss://textsecure-service.whispersystems.org/v1/websocket/"
|
||||
//#define textSecureServerURL @"https://textsecure-service.whispersystems.org/"
|
||||
//#define textSecureCDNServerURL @"https://cdn.signal.org"
|
||||
//// Use same reflector for service and CDN
|
||||
//#define textSecureServiceReflectorHost @"textsecure-service-reflected.whispersystems.org"
|
||||
//#define textSecureCDNReflectorHost @"textsecure-service-reflected.whispersystems.org"
|
||||
//#define contactDiscoveryURL @"https://api.directory.signal.org"
|
||||
|
||||
//#else
|
||||
//
|
||||
//// Staging
|
||||
//#define textSecureWebSocketAPI @"wss://textsecure-service-staging.whispersystems.org/v1/websocket/"
|
||||
//#define textSecureServerURL @"https://textsecure-service-staging.whispersystems.org/"
|
||||
//#define textSecureCDNServerURL @"https://cdn-staging.signal.org"
|
||||
//#define textSecureServiceReflectorHost @"meek-signal-service-staging.appspot.com";
|
||||
//#define textSecureCDNReflectorHost @"meek-signal-cdn-staging.appspot.com";
|
||||
//#define contactDiscoveryURL @"https://api-staging.directory.signal.org"
|
||||
#define textSecureWebSocketAPI @"wss://textsecure-service-staging.whispersystems.org/v1/websocket/"
|
||||
#define textSecureServerURL @"https://textsecure-service-staging.whispersystems.org/"
|
||||
#define textSecureCDNServerURL @"https://cdn-staging.signal.org"
|
||||
#define textSecureServiceReflectorHost @"meek-signal-service-staging.appspot.com";
|
||||
#define textSecureCDNReflectorHost @"meek-signal-cdn-staging.appspot.com";
|
||||
#define contactDiscoveryURL @"https://api-staging.directory.signal.org"
|
||||
//
|
||||
//#endif
|
||||
|
||||
|
|
|
@ -9,9 +9,11 @@ import Foundation
|
|||
@objc
|
||||
public class OWSFakeUDManager: NSObject, OWSUDManager {
|
||||
|
||||
private var udRecipientSet = Set<String>()
|
||||
@objc public func setup() {}
|
||||
|
||||
// MARK: -
|
||||
// MARK: - Recipient state
|
||||
|
||||
private var udRecipientSet = Set<String>()
|
||||
|
||||
@objc
|
||||
public func isUDRecipientId(_ recipientId: String) -> Bool {
|
||||
|
@ -27,6 +29,20 @@ public class OWSFakeUDManager: NSObject, OWSUDManager {
|
|||
public func removeUDRecipientId(_ recipientId: String) {
|
||||
udRecipientSet.remove(recipientId)
|
||||
}
|
||||
|
||||
// MARK: - Server Certificate
|
||||
|
||||
// Tests can control the behavior of this mock by setting this property.
|
||||
@objc public var nextServerCertificate: Data?
|
||||
|
||||
@objc public func ensureServerCertificateObjC(success:@escaping (Data) -> Void,
|
||||
failure:@escaping (Error) -> Void) {
|
||||
guard let certificateData = nextServerCertificate else {
|
||||
failure(OWSUDError.assertionError(description: "No mock server certificate data"))
|
||||
return
|
||||
}
|
||||
success(certificateData)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue