Browse Source

Create SignalUtilitiesKit

pull/311/head
nielsandriesse 2 years ago
parent
commit
c475f895e8
  1. 20
      Podfile
  2. 4
      Podfile.lock
  3. 2
      Pods
  4. 2
      SessionMessagingKit/Configuration.swift
  5. 6
      SessionMessagingKit/Sending & Receiving/MessageSender.swift
  6. 6
      SessionProtocolKit/Meta/SessionProtocolKit.h
  7. 2
      SessionProtocolKit/Shared Sender Keys/ClosedGroupSenderKey.swift
  8. 2
      SessionProtocolKit/Signal/Utility/Cryptography.m
  9. 20
      SessionProtocolKit/Signal/Utility/Randomness.h
  10. 24
      SessionProtocolKit/Signal/Utility/Randomness.m
  11. 8
      SessionSnodeKit/Snode.swift
  12. 97
      SessionSnodeKit/SnodeAPI.swift
  13. 10
      SessionSnodeKit/Utilities/Promise+Threading.swift
  14. 2
      SessionUtilitiesKit/Promise+Delaying.swift
  15. 2003
      Signal.xcodeproj/project.pbxproj
  16. 34
      SignalUtilitiesKit/AccountServiceClient.swift
  17. 10
      SignalUtilitiesKit/AnyPromise+Conversion.swift
  18. 130
      SignalUtilitiesKit/AppContext.h
  19. 61
      SignalUtilitiesKit/AppContext.m
  20. 38
      SignalUtilitiesKit/AppReadiness.h
  21. 144
      SignalUtilitiesKit/AppReadiness.m
  22. 32
      SignalUtilitiesKit/AppVersion.h
  23. 133
      SignalUtilitiesKit/AppVersion.m
  24. 7
      SignalUtilitiesKit/Array+Description.swift
  25. 21
      SignalUtilitiesKit/BuildConfiguration.swift
  26. 40
      SignalUtilitiesKit/ByteParser.h
  27. 143
      SignalUtilitiesKit/ByteParser.m
  28. 34
      SignalUtilitiesKit/CDSQuote.h
  29. 192
      SignalUtilitiesKit/CDSQuote.m
  30. 32
      SignalUtilitiesKit/CDSSigningCertificate.h
  31. 391
      SignalUtilitiesKit/CDSSigningCertificate.m
  32. 30
      SignalUtilitiesKit/ClosedGroupParser.swift
  33. 79
      SignalUtilitiesKit/ClosedGroupPoller.swift
  34. 132
      SignalUtilitiesKit/ClosedGroupUpdateMessage.swift
  35. 71
      SignalUtilitiesKit/ClosedGroupUtilities.swift
  36. 486
      SignalUtilitiesKit/ClosedGroupsProtocol.swift
  37. 58
      SignalUtilitiesKit/Contact.h
  38. 434
      SignalUtilitiesKit/Contact.m
  39. 65
      SignalUtilitiesKit/ContactDiscoveryService.h
  40. 774
      SignalUtilitiesKit/ContactDiscoveryService.m
  41. 28
      SignalUtilitiesKit/ContactParser.swift
  42. 37
      SignalUtilitiesKit/ContactsManagerProtocol.h
  43. 25
      SignalUtilitiesKit/ContactsUpdater.h
  44. 120
      SignalUtilitiesKit/ContactsUpdater.m
  45. 127
      SignalUtilitiesKit/ContentProxy.swift
  46. 57
      SignalUtilitiesKit/CreatePreKeysOperation.swift
  47. 12
      SignalUtilitiesKit/Data+SecureRandom.swift
  48. 22
      SignalUtilitiesKit/Data+Streaming.swift
  49. 67
      SignalUtilitiesKit/DataSource.h
  50. 401
      SignalUtilitiesKit/DataSource.m
  51. 12
      SignalUtilitiesKit/Debugging.swift
  52. 18
      SignalUtilitiesKit/DecryptionUtilities.swift
  53. 96
      SignalUtilitiesKit/DeviceLink.swift
  54. 43
      SignalUtilitiesKit/DeviceLinkIndex.swift
  55. 69
      SignalUtilitiesKit/DeviceLinkingSession.swift
  56. 6
      SignalUtilitiesKit/DeviceLinkingSessionDelegate.swift
  57. 57
      SignalUtilitiesKit/DeviceLinkingUtilities.swift
  58. 217
      SignalUtilitiesKit/DeviceNames.swift
  59. 13
      SignalUtilitiesKit/Dictionary+Description.swift
  60. 68
      SignalUtilitiesKit/DisplayNameUtilities.swift
  61. 28
      SignalUtilitiesKit/DisplayNameUtilities2.swift
  62. 22
      SignalUtilitiesKit/ECKeyPair+Hexadecimal.swift
  63. 38
      SignalUtilitiesKit/EncryptionUtilities.swift
  64. 32
      SignalUtilitiesKit/FeatureFlags.swift
  65. 148
      SignalUtilitiesKit/FileServerAPI+Deprecated.swift
  66. 164
      SignalUtilitiesKit/Fingerprint.pb.swift
  67. 235
      SignalUtilitiesKit/FingerprintProto.swift
  68. 274
      SignalUtilitiesKit/FullTextSearchFinder.swift
  69. 27
      SignalUtilitiesKit/FunctionalUtil.h
  70. 98
      SignalUtilitiesKit/FunctionalUtil.m
  71. 7
      SignalUtilitiesKit/GeneralUtilities.swift
  72. 25
      SignalUtilitiesKit/GroupUtilities.swift
  73. 411
      SignalUtilitiesKit/JobQueue.swift
  74. 19
      SignalUtilitiesKit/LKDeviceLinkMessage.h
  75. 89
      SignalUtilitiesKit/LKDeviceLinkMessage.m
  76. 26
      SignalUtilitiesKit/LKGroupUtilities.h
  77. 77
      SignalUtilitiesKit/LKGroupUtilities.m
  78. 14
      SignalUtilitiesKit/LKSyncOpenGroupsMessage.h
  79. 43
      SignalUtilitiesKit/LKSyncOpenGroupsMessage.m
  80. 12
      SignalUtilitiesKit/LKUnlinkDeviceMessage.h
  81. 27
      SignalUtilitiesKit/LKUnlinkDeviceMessage.m
  82. 73
      SignalUtilitiesKit/LKUserDefaults.swift
  83. 105
      SignalUtilitiesKit/LRUCache.swift
  84. 98
      SignalUtilitiesKit/LokiDatabaseUtilities.swift
  85. 75
      SignalUtilitiesKit/LokiMessage.swift
  86. 153
      SignalUtilitiesKit/LokiPushNotificationManager.swift
  87. 48
      SignalUtilitiesKit/LokiSessionResetImplementation.swift
  88. 68
      SignalUtilitiesKit/MIMETypeUtil.h
  89. 2626
      SignalUtilitiesKit/MIMETypeUtil.m
  90. 15
      SignalUtilitiesKit/Mention.swift
  91. 92
      SignalUtilitiesKit/MentionsManager.swift
  92. 25
      SignalUtilitiesKit/MessageSender+Promise.swift
  93. 256
      SignalUtilitiesKit/MessageSenderJobQueue.swift
  94. 72
      SignalUtilitiesKit/MessageWrapper.swift
  95. 22
      SignalUtilitiesKit/Meta/Info.plist
  96. 72
      SignalUtilitiesKit/Meta/SignalUtilitiesKit.h
  97. 162
      SignalUtilitiesKit/Mnemonic.swift
  98. 274
      SignalUtilitiesKit/MultiDeviceProtocol.swift
  99. 9
      SignalUtilitiesKit/NSArray+Functional.h
  100. 32
      SignalUtilitiesKit/NSArray+Functional.m
  101. Some files were not shown because too many files have changed in this diff Show More

20
Podfile

@ -98,6 +98,26 @@ target 'SignalMessaging' do
shared_pods
end
target 'SignalUtilitiesKit' do
pod 'AFNetworking', inhibit_warnings: true
pod 'CryptoSwift', :inhibit_warnings => true
pod 'Curve25519Kit', :inhibit_warnings => true
pod 'GRKOpenSSLFramework', :inhibit_warnings => true
pod 'HKDFKit', :inhibit_warnings => true
pod 'libPhoneNumber-iOS', :inhibit_warnings => true
pod 'Mantle', git: 'https://github.com/signalapp/Mantle', branch: 'signal-master', :inhibit_warnings => true
pod 'PromiseKit', :inhibit_warnings => true
pod 'Reachability', :inhibit_warnings => true
pod 'SAMKeychain', :inhibit_warnings => true
pod 'Starscream', git: 'https://github.com/signalapp/Starscream.git', branch: 'signal-release', :inhibit_warnings => true
pod 'SwiftProtobuf', '~> 1.5.0', :inhibit_warnings => true
pod 'YapDatabase/SQLCipher', :git => 'https://github.com/signalapp/YapDatabase.git', branch: 'signal-release', :inhibit_warnings => true
end
target 'SessionUIKit' do
end
target 'SessionMessagingKit' do
pod 'AFNetworking', inhibit_warnings: true
pod 'CryptoSwift', :inhibit_warnings => true

4
Podfile.lock

@ -206,12 +206,14 @@ DEPENDENCIES:
- FeedKit (~> 8.1)
- GRKOpenSSLFramework
- HKDFKit
- libPhoneNumber-iOS
- Mantle (from `https://github.com/signalapp/Mantle`, branch `signal-master`)
- NVActivityIndicatorView (~> 4.7)
- PromiseKit
- PromiseKit (= 6.5.3)
- PureLayout (~> 3.1.4)
- Reachability
- SAMKeychain
- SessionAxolotlKit (from `https://github.com/loki-project/session-ios-protocol-kit.git`, branch `master`)
- SessionAxolotlKit/Tests (from `https://github.com/loki-project/session-ios-protocol-kit.git`, branch `master`)
- SessionCoreKit (from `https://github.com/loki-project/session-ios-core-kit.git`)
@ -333,6 +335,6 @@ SPEC CHECKSUMS:
YYImage: 6db68da66f20d9f169ceb94dfb9947c3867b9665
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
PODFILE CHECKSUM: 8fc5917e97576b902a46b328af80664381ede889
PODFILE CHECKSUM: d78dc9a752cd3ce8f01fa327b8518dff3f5236d5
COCOAPODS: 1.10.0.rc.1

2
Pods

@ -1 +1 @@
Subproject commit 0c79ca436b633fdf1b0daf90e86fd323dcc60c55
Subproject commit e28da414f77b9cba508c92e90b16b815847cde7e

2
SessionMessagingKit/Configuration.swift

@ -13,7 +13,7 @@ public struct Configuration {
internal static var shared: Configuration!
}
public enum SessionMessagingKit { // Just to make the external API nice
public enum SessionMessagingKitX { // Just to make the external API nice
public static func configure(
storage: SessionMessagingKitStorageProtocol,

6
SessionMessagingKit/Sending & Receiving/MessageSender.swift

@ -2,15 +2,15 @@ import PromiseKit
import SessionSnodeKit
import SessionUtilitiesKit
internal enum MessageSender {
public enum MessageSender {
internal enum Error : LocalizedError {
public enum Error : LocalizedError {
case invalidMessage
case protoConversionFailed
case proofOfWorkCalculationFailed
case noUserPublicKey
internal var errorDescription: String? {
public var errorDescription: String? {
switch self {
case .invalidMessage: return "Invalid message."
case .protoConversionFailed: return "Couldn't convert message to proto."

6
SessionProtocolKit/Meta/SessionProtocolKit.h

@ -4,14 +4,20 @@ FOUNDATION_EXPORT double SessionProtocolKitVersionNumber;
FOUNDATION_EXPORT const unsigned char SessionProtocolKitVersionString[];
#import <SessionProtocolKit/AxolotlStore.h>
#import <SessionProtocolKit/AxolotlExceptions.h>
#import <SessionProtocolKit/ClosedGroupCiphertextMessage.h>
#import <SessionProtocolKit/Cryptography.h>
#import <SessionProtocolKit/FallbackMessage.h>
#import <SessionProtocolKit/NSData+keyVersionByte.h>
#import <SessionProtocolKit/NSData+messagePadding.h>
#import <SessionProtocolKit/NSData+OWS.h>
#import <SessionProtocolKit/NSDate+OWS.h>
#import <SessionProtocolKit/NSObject+OWS.h>
#import <SessionProtocolKit/NSString+OWS.h>
#import <SessionProtocolKit/OWSAsserts.h>
#import <SessionProtocolKit/OWSLogs.h>
#import <SessionProtocolKit/PreKeyBundle.h>
#import <SessionProtocolKit/SerializationUtilities.h>
#import <SessionProtocolKit/SessionBuilder.h>
#import <SessionProtocolKit/SessionCipher.h>
#import <SessionProtocolKit/Threading.h>

2
SessionProtocolKit/Shared Sender Keys/ClosedGroupSenderKey.swift

@ -5,7 +5,7 @@ public final class ClosedGroupSenderKey : NSObject, NSCoding { // NSObject/NSCod
public let publicKey: Data
// MARK: Initialization
init(chainKey: Data, keyIndex: UInt, publicKey: Data) {
public init(chainKey: Data, keyIndex: UInt, publicKey: Data) {
self.chainKey = chainKey
self.keyIndex = keyIndex
self.publicKey = publicKey

2
SessionProtocolKit/Signal/Utility/Cryptography.m

@ -7,7 +7,7 @@
#import "NSData+OWS.h"
#import <CommonCrypto/CommonCryptor.h>
#import <CommonCrypto/CommonHMAC.h>
#import <SessionProtocolKit/Randomness.h>
#import <Curve25519Kit/Randomness.h>
#import <openssl/evp.h>
#import <SessionProtocolKit/OWSAsserts.h>

20
SessionProtocolKit/Signal/Utility/Randomness.h

@ -1,20 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Randomness : NSObject
/**
* Generates a given number of cryptographically secure bytes using SecRandomCopyBytes.
*
* @param numberBytes The number of bytes to be generated.
*
* @return Random Bytes.
*/
+ (NSData *)generateRandomBytes:(int)numberBytes;
@end

24
SessionProtocolKit/Signal/Utility/Randomness.m

@ -1,24 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "Randomness.h"
#import <SessionProtocolKit/OWSAsserts.h>
@implementation Randomness
+ (NSData *)generateRandomBytes:(int)numberBytes
{
NSMutableData *_Nullable randomBytes = [NSMutableData dataWithLength:numberBytes];
if (!randomBytes) {
OWSFail(@"Could not allocate buffer for random bytes.");
}
int err = 0;
err = SecRandomCopyBytes(kSecRandomDefault, numberBytes, [randomBytes mutableBytes]);
if (err != noErr || randomBytes.length != numberBytes) {
OWSFail(@"Could not generate random bytes.");
}
return [randomBytes copy];
}
@end

8
SessionSnodeKit/Snode.swift

@ -3,7 +3,7 @@ import Foundation
public final class Snode : NSObject, NSCoding { // NSObject/NSCoding conformance is needed for YapDatabase compatibility
public let address: String
public let port: UInt16
internal let publicKeySet: KeySet
public let publicKeySet: KeySet
public var ip: String {
address.removingPrefix("https://")
@ -16,9 +16,9 @@ public final class Snode : NSObject, NSCoding { // NSObject/NSCoding conformance
case sendMessage = "store"
}
internal struct KeySet {
let ed25519Key: String
let x25519Key: String
public struct KeySet {
public let ed25519Key: String
public let x25519Key: String
}
// MARK: Initialization

97
SessionSnodeKit/SnodeAPI.swift

@ -1,15 +1,18 @@
import PromiseKit
import SessionUtilitiesKit
public enum SnodeAPI {
@objc(SNSnodeAPI)
public final class SnodeAPI : NSObject {
/// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions.
internal static var snodeFailureCount: [Snode:UInt] = [:]
/// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions.
internal static var snodePool: Set<Snode> = []
/// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions.
internal static var swarmCache: [String:Set<Snode>] = [:]
public static var swarmCache: [String:Set<Snode>] = [:]
public static var workQueue: DispatchQueue { Threading.workQueue }
// MARK: Settings
private static let maxRetryCount: UInt = 4
private static let minimumSnodePoolCount = 64
@ -25,11 +28,13 @@ public enum SnodeAPI {
// MARK: Error
public enum Error : LocalizedError {
case generic
case clockOutOfSync
case randomSnodePoolUpdatingFailed
public var errorDescription: String? {
switch self {
case .generic: return "An error occurred."
case .clockOutOfSync: return "Your clock is out of sync with the service node network."
case .randomSnodePoolUpdatingFailed: return "Failed to update random service node pool."
}
@ -41,8 +46,8 @@ public enum SnodeAPI {
public typealias RawResponse = Any
public typealias RawResponsePromise = Promise<RawResponse>
// MARK: Core
internal static func invoke(_ method: Snode.Method, on snode: Snode, associatedWith publicKey: String, parameters: JSON) -> RawResponsePromise {
// MARK: Internal API
public static func invoke(_ method: Snode.Method, on snode: Snode, associatedWith publicKey: String, parameters: JSON) -> RawResponsePromise {
if useOnionRequests {
return OnionRequestAPI.sendOnionRequest(to: snode, invoking: method, with: parameters, associatedWith: publicKey).map2 { $0 as Any }
} else {
@ -109,35 +114,6 @@ public enum SnodeAPI {
}
}
internal static func getSwarm(for publicKey: String, isForcedReload: Bool = false) -> Promise<Set<Snode>> {
if swarmCache[publicKey] == nil {
swarmCache[publicKey] = Configuration.shared.storage.getSwarm(for: publicKey)
}
if let cachedSwarm = swarmCache[publicKey], cachedSwarm.count >= minimumSwarmSnodeCount && !isForcedReload {
return Promise<Set<Snode>> { $0.fulfill(cachedSwarm) }
} else {
SNLog("Getting swarm for: \((publicKey == Configuration.shared.storage.getUserPublicKey()) ? "self" : publicKey).")
let parameters: [String:Any] = [ "pubKey" : publicKey ]
return getRandomSnode().then2 { snode in
attempt(maxRetryCount: 4, recoveringOn: Threading.workQueue) {
invoke(.getSwarm, on: snode, associatedWith: publicKey, parameters: parameters)
}
}.map2 { rawSnodes in
let swarm = parseSnodes(from: rawSnodes)
swarmCache[publicKey] = swarm
Configuration.shared.storage.with { transaction in
Configuration.shared.storage.setSwarm(to: swarm, for: publicKey, using: transaction)
}
return swarm
}
}
}
internal static func getTargetSnodes(for publicKey: String) -> Promise<[Snode]> {
// shuffled() uses the system's default random generator, which is cryptographically secure
return getSwarm(for: publicKey).map2 { Array($0.shuffled().prefix(targetSwarmSnodeCount)) }
}
internal static func dropSnodeFromSnodePool(_ snode: Snode) {
var snodePool = SnodeAPI.snodePool
snodePool.remove(snode)
@ -154,7 +130,8 @@ public enum SnodeAPI {
}
}
internal static func dropSnodeFromSwarmIfNeeded(_ snode: Snode, publicKey: String) {
// MARK: Public API
public static func dropSnodeFromSwarmIfNeeded(_ snode: Snode, publicKey: String) {
let swarm = SnodeAPI.swarmCache[publicKey]
if var swarm = swarm, let index = swarm.firstIndex(of: snode) {
swarm.remove(at: index)
@ -165,16 +142,55 @@ public enum SnodeAPI {
}
}
// MARK: Receiving
public static func getTargetSnodes(for publicKey: String) -> Promise<[Snode]> {
// shuffled() uses the system's default random generator, which is cryptographically secure
return getSwarm(for: publicKey).map2 { Array($0.shuffled().prefix(targetSwarmSnodeCount)) }
}
public static func getSwarm(for publicKey: String, isForcedReload: Bool = false) -> Promise<Set<Snode>> {
if swarmCache[publicKey] == nil {
swarmCache[publicKey] = Configuration.shared.storage.getSwarm(for: publicKey)
}
if let cachedSwarm = swarmCache[publicKey], cachedSwarm.count >= minimumSwarmSnodeCount && !isForcedReload {
return Promise<Set<Snode>> { $0.fulfill(cachedSwarm) }
} else {
SNLog("Getting swarm for: \((publicKey == Configuration.shared.storage.getUserPublicKey()) ? "self" : publicKey).")
let parameters: [String:Any] = [ "pubKey" : publicKey ]
return getRandomSnode().then2 { snode in
attempt(maxRetryCount: 4, recoveringOn: Threading.workQueue) {
invoke(.getSwarm, on: snode, associatedWith: publicKey, parameters: parameters)
}
}.map2 { rawSnodes in
let swarm = parseSnodes(from: rawSnodes)
swarmCache[publicKey] = swarm
Configuration.shared.storage.with { transaction in
Configuration.shared.storage.setSwarm(to: swarm, for: publicKey, using: transaction)
}
return swarm
}
}
}
public static func getRawMessages(from snode: Snode, associatedWith publicKey: String) -> RawResponsePromise {
let storage = Configuration.shared.storage
storage.with { transaction in
storage.pruneLastMessageHashInfoIfExpired(for: snode, associatedWith: publicKey, using: transaction)
}
let lastHash = storage.getLastMessageHash(for: snode, associatedWith: publicKey) ?? ""
let parameters = [ "pubKey" : publicKey, "lastHash" : lastHash ]
return invoke(.getMessages, on: snode, associatedWith: publicKey, parameters: parameters)
}
public static func getMessages(for publicKey: String) -> Promise<Set<MessageListPromise>> {
let (promise, seal) = Promise<Set<MessageListPromise>>.pending()
let storage = Configuration.shared.storage
Threading.workQueue.async {
attempt(maxRetryCount: maxRetryCount, recoveringOn: Threading.workQueue) {
getTargetSnodes(for: publicKey).mapValues2 { targetSnode in
Configuration.shared.storage.with { transaction in
Configuration.shared.storage.pruneLastMessageHashInfoIfExpired(for: targetSnode, associatedWith: publicKey, using: transaction)
storage.with { transaction in
storage.pruneLastMessageHashInfoIfExpired(for: targetSnode, associatedWith: publicKey, using: transaction)
}
let lastHash = Configuration.shared.storage.getLastMessageHash(for: targetSnode, associatedWith: publicKey) ?? ""
let lastHash = storage.getLastMessageHash(for: targetSnode, associatedWith: publicKey) ?? ""
let parameters = [ "pubKey" : publicKey, "lastHash" : lastHash ]
return invoke(.getMessages, on: targetSnode, associatedWith: publicKey, parameters: parameters).map2 { rawResponse in
parseRawMessagesResponse(rawResponse, from: targetSnode, associatedWith: publicKey)
@ -185,7 +201,6 @@ public enum SnodeAPI {
return promise
}
// MARK: Sending
public static func sendMessage(_ message: SnodeMessage) -> Promise<Set<RawResponsePromise>> {
let (promise, seal) = Promise<Set<RawResponsePromise>>.pending()
let publicKey = message.recipient
@ -230,7 +245,7 @@ public enum SnodeAPI {
})
}
internal static func parseRawMessagesResponse(_ rawResponse: Any, from snode: Snode, associatedWith publicKey: String) -> [JSON] {
public static func parseRawMessagesResponse(_ rawResponse: Any, from snode: Snode, associatedWith publicKey: String) -> [JSON] {
guard let json = rawResponse as? JSON, let rawMessages = json["messages"] as? [JSON] else { return [] }
updateLastMessageHashValueIfPossible(for: snode, associatedWith: publicKey, from: rawMessages)
return removeDuplicates(from: rawMessages, associatedWith: publicKey)

10
SessionSnodeKit/Utilities/Promise+Threading.swift

@ -1,6 +1,6 @@
import PromiseKit
internal extension Thenable {
public extension Thenable {
@discardableResult
func then2<U>(_ body: @escaping (T) throws -> U) -> Promise<U.T> where U : Thenable {
@ -23,7 +23,7 @@ internal extension Thenable {
}
}
internal extension Thenable where T: Sequence {
public extension Thenable where T: Sequence {
@discardableResult
func mapValues2<U>(_ transform: @escaping (T.Iterator.Element) throws -> U) -> Promise<[U]> {
@ -31,7 +31,7 @@ internal extension Thenable where T: Sequence {
}
}
internal extension Guarantee {
public extension Guarantee {
@discardableResult
func then2<U>(_ body: @escaping (T) -> Guarantee<U>) -> Guarantee<U> {
@ -54,7 +54,7 @@ internal extension Guarantee {
}
}
internal extension CatchMixin {
public extension CatchMixin {
@discardableResult
func catch2(_ body: @escaping (Error) -> Void) -> PMKFinalizer {
@ -77,7 +77,7 @@ internal extension CatchMixin {
}
}
internal extension CatchMixin where T == Void {
public extension CatchMixin where T == Void {
@discardableResult
func recover2(_ body: @escaping(Error) -> Void) -> Guarantee<Void> {

2
SessionSnodeKit/Utilities/Promise+Delaying.swift → SessionUtilitiesKit/Promise+Delaying.swift

@ -1,7 +1,7 @@
import PromiseKit
/// Delay the execution of the promise constructed in `body` by `delay` seconds.
internal func withDelay<T>(_ delay: TimeInterval, completionQueue: DispatchQueue, body: @escaping () -> Promise<T>) -> Promise<T> {
public func withDelay<T>(_ delay: TimeInterval, completionQueue: DispatchQueue, body: @escaping () -> Promise<T>) -> Promise<T> {
#if DEBUG
assert(Thread.current.isMainThread) // Timers don't do well on background queues
#endif

2003
Signal.xcodeproj/project.pbxproj

File diff suppressed because it is too large Load Diff

34
SignalUtilitiesKit/AccountServiceClient.swift

@ -0,0 +1,34 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
import PromiseKit
// TODO define actual type, and validate length
public typealias IdentityKey = Data
/// based on libsignal-service-java's AccountManager class
@objc(SSKAccountServiceClient)
public class AccountServiceClient: NSObject {
static var shared = AccountServiceClient()
private let serviceClient: SignalServiceClient
override init() {
self.serviceClient = SignalServiceRestClient()
}
public func getPreKeysCount() -> Promise<Int> {
return serviceClient.getAvailablePreKeys()
}
public func setPreKeys(identityKey: IdentityKey, signedPreKeyRecord: SignedPreKeyRecord, preKeyRecords: [PreKeyRecord]) -> Promise<Void> {
return serviceClient.registerPreKeys(identityKey: identityKey, signedPreKeyRecord: signedPreKeyRecord, preKeyRecords: preKeyRecords)
}
public func setSignedPreKey(_ signedPreKey: SignedPreKeyRecord) -> Promise<Void> {
return serviceClient.setCurrentSignedPreKey(signedPreKey)
}
}

10
SignalUtilitiesKit/AnyPromise+Conversion.swift

@ -0,0 +1,10 @@
import PromiseKit
public extension AnyPromise {
public static func from<T : Any>(_ promise: Promise<T>) -> AnyPromise {
let result = AnyPromise(promise)
result.retainUntilComplete()
return result
}
}

130
SignalUtilitiesKit/AppContext.h

@ -0,0 +1,130 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
static inline BOOL OWSIsDebugBuild()
{
#ifdef DEBUG
return YES;
#else
return NO;
#endif
}
// These are fired whenever the corresponding "main app" or "app extension"
// notification is fired.
//
// 1. This saves you the work of observing both.
// 2. This allows us to ensure that any critical work (e.g. re-opening
// databases) has been done before app re-enters foreground, etc.
extern NSString *const OWSApplicationDidEnterBackgroundNotification;
extern NSString *const OWSApplicationWillEnterForegroundNotification;
extern NSString *const OWSApplicationWillResignActiveNotification;
extern NSString *const OWSApplicationDidBecomeActiveNotification;
typedef void (^BackgroundTaskExpirationHandler)(void);
typedef void (^AppActiveBlock)(void);
NSString *NSStringForUIApplicationState(UIApplicationState value);
@class OWSAES256Key;
@protocol SSKKeychainStorage;
@protocol AppContext <NSObject>
@property (nonatomic, readonly) BOOL isMainApp;
@property (nonatomic, readonly) BOOL isMainAppAndActive;
/// Whether the app was woken up by a silent push notification. This is important for
/// determining whether attachments should be downloaded or not.
@property (nonatomic) BOOL wasWokenUpByPushNotification;
// Whether the user is using a right-to-left language like Arabic.
@property (nonatomic, readonly) BOOL isRTL;
@property (nonatomic, readonly) BOOL isRunningTests;
@property (atomic, nullable) UIWindow *mainWindow;
// Unlike UIApplication.applicationState, this is thread-safe.
// It contains the "last known" application state.
//
// Because it is updated in response to "will/did-style" events, it is
// conservative and skews toward less-active and not-foreground:
//
// * It doesn't report "is active" until the app is active
// and reports "inactive" as soon as it _will become_ inactive.
// * It doesn't report "is foreground (but inactive)" until the app is
// foreground & inactive and reports "background" as soon as it _will
// enter_ background.
//
// This conservatism is useful, since we want to err on the side of
// caution when, for example, we do work that should only be done
// when the app is foreground and active.
@property (atomic, readonly) UIApplicationState reportedApplicationState;
// A convenience accessor for reportedApplicationState.
//
// This method is thread-safe.
- (BOOL)isInBackground;
// A convenience accessor for reportedApplicationState.
//
// This method is thread-safe.
- (BOOL)isAppForegroundAndActive;
// Should start a background task if isMainApp is YES.
// Should just return UIBackgroundTaskInvalid if isMainApp is NO.
- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:
(BackgroundTaskExpirationHandler)expirationHandler;
// Should be a NOOP if isMainApp is NO.
- (void)endBackgroundTask:(UIBackgroundTaskIdentifier)backgroundTaskIdentifier;
// Should be a NOOP if isMainApp is NO.
- (void)ensureSleepBlocking:(BOOL)shouldBeBlocking blockingObjects:(NSArray<id> *)blockingObjects;
// Should only be called if isMainApp is YES.
- (void)setMainAppBadgeNumber:(NSInteger)value;
- (void)setStatusBarHidden:(BOOL)isHidden animated:(BOOL)isAnimated;
@property (nonatomic, readonly) CGFloat statusBarHeight;
// Returns the VC that should be used to present alerts, modals, etc.
- (nullable UIViewController *)frontmostViewController;
// Returns nil if isMainApp is NO
@property (nullable, nonatomic, readonly) UIAlertAction *openSystemSettingsAction;
// Should be a NOOP if isMainApp is NO.
- (void)setNetworkActivityIndicatorVisible:(BOOL)value;
- (void)runNowOrWhenMainAppIsActive:(AppActiveBlock)block;
@property (atomic, readonly) NSDate *appLaunchTime;
- (id<SSKKeychainStorage>)keychainStorage;
- (NSString *)appDocumentDirectoryPath;
- (NSString *)appSharedDataDirectoryPath;
- (NSUserDefaults *)appUserDefaults;
@end
id<AppContext> CurrentAppContext(void);
void SetCurrentAppContext(id<AppContext> appContext);
void ExitShareExtension(void);
#ifdef DEBUG
void ClearCurrentAppContextForTests(void);
#endif
NS_ASSUME_NONNULL_END

61
SignalUtilitiesKit/AppContext.m

@ -0,0 +1,61 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "AppContext.h"
#import <SessionProtocolKit/SessionProtocolKit.h>
NS_ASSUME_NONNULL_BEGIN
NSString *const OWSApplicationDidEnterBackgroundNotification = @"OWSApplicationDidEnterBackgroundNotification";
NSString *const OWSApplicationWillEnterForegroundNotification = @"OWSApplicationWillEnterForegroundNotification";
NSString *const OWSApplicationWillResignActiveNotification = @"OWSApplicationWillResignActiveNotification";
NSString *const OWSApplicationDidBecomeActiveNotification = @"OWSApplicationDidBecomeActiveNotification";
NSString *NSStringForUIApplicationState(UIApplicationState value)
{
switch (value) {
case UIApplicationStateActive:
return @"UIApplicationStateActive";
case UIApplicationStateInactive:
return @"UIApplicationStateInactive";
case UIApplicationStateBackground:
return @"UIApplicationStateBackground";
}
}
static id<AppContext> currentAppContext = nil;
id<AppContext> CurrentAppContext(void)
{
OWSCAssertDebug(currentAppContext);
return currentAppContext;
}
void SetCurrentAppContext(id<AppContext> appContext)
{
// The main app context should only be set once.
//
// App extensions may be opened multiple times in the same process,
// so statics will persist.
OWSCAssertDebug(!currentAppContext || !currentAppContext.isMainApp);
currentAppContext = appContext;
}
#ifdef DEBUG
void ClearCurrentAppContextForTests()
{
currentAppContext = nil;
}
#endif
void ExitShareExtension(void)
{
OWSLogInfo(@"ExitShareExtension");
[DDLog flushLog];
exit(0);
}
NS_ASSUME_NONNULL_END

38
SignalUtilitiesKit/AppReadiness.h

@ -0,0 +1,38 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void (^AppReadyBlock)(void);
@interface AppReadiness : NSObject
- (instancetype)init NS_UNAVAILABLE;
// This method can be called on any thread.
+ (BOOL)isAppReady;
// This method should only be called on the main thread.
+ (void)setAppIsReady;
// If the app is ready, the block is called immediately;
// otherwise it is called when the app becomes ready.
//
// This method should only be called on the main thread.
// The block will always be called on the main thread.
//
// * The "will become ready" blocks are called before the "did become ready" blocks.
// * The "will become ready" blocks should be used for internal setup of components
// so that they are ready to interact with other components of the system.
// * The "did become ready" blocks should be used for any work that should be done
// on app launch, especially work that uses other components.
// * We should usually use "did become ready" blocks since they are safer.
+ (void)runNowOrWhenAppWillBecomeReady:(AppReadyBlock)block NS_SWIFT_NAME(runNowOrWhenAppWillBecomeReady(_:));
+ (void)runNowOrWhenAppDidBecomeReady:(AppReadyBlock)block NS_SWIFT_NAME(runNowOrWhenAppDidBecomeReady(_:));
@end
NS_ASSUME_NONNULL_END

144
SignalUtilitiesKit/AppReadiness.m

@ -0,0 +1,144 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "AppReadiness.h"
#import <SessionProtocolKit/SessionProtocolKit.h>
#import "AppContext.h"
#import "SSKAsserts.h"
NS_ASSUME_NONNULL_BEGIN
@interface AppReadiness ()
@property (atomic) BOOL isAppReady;
@property (nonatomic) NSMutableArray<AppReadyBlock> *appWillBecomeReadyBlocks;
@property (nonatomic) NSMutableArray<AppReadyBlock> *appDidBecomeReadyBlocks;
@end
#pragma mark -
@implementation AppReadiness
+ (instancetype)sharedManager
{
static AppReadiness *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] initDefault];
});
return sharedMyManager;
}
- (instancetype)initDefault
{
self = [super init];
if (!self) {
return self;
}
OWSSingletonAssert();
self.appWillBecomeReadyBlocks = [NSMutableArray new];
self.appDidBecomeReadyBlocks = [NSMutableArray new];
return self;
}
+ (BOOL)isAppReady
{
return [self.sharedManager isAppReady];
}
+ (void)runNowOrWhenAppWillBecomeReady:(AppReadyBlock)block
{
DispatchMainThreadSafe(^{
[self.sharedManager runNowOrWhenAppWillBecomeReady:block];
});
}
- (void)runNowOrWhenAppWillBecomeReady:(AppReadyBlock)block
{
OWSAssertIsOnMainThread();
OWSAssertDebug(block);
if (CurrentAppContext().isRunningTests) {
// We don't need to do any "on app ready" work in the tests.
return;
}
if (self.isAppReady) {
block();
return;
}
[self.appWillBecomeReadyBlocks addObject:block];
}
+ (void)runNowOrWhenAppDidBecomeReady:(AppReadyBlock)block
{
DispatchMainThreadSafe(^{
[self.sharedManager runNowOrWhenAppDidBecomeReady:block];
});
}
- (void)runNowOrWhenAppDidBecomeReady:(AppReadyBlock)block
{
OWSAssertIsOnMainThread();
OWSAssertDebug(block);
if (CurrentAppContext().isRunningTests) {
// We don't need to do any "on app ready" work in the tests.
return;
}
if (self.isAppReady) {
block();
return;
}
[self.appDidBecomeReadyBlocks addObject:block];
}
+ (void)setAppIsReady
{
[self.sharedManager setAppIsReady];
}
- (void)setAppIsReady
{
OWSAssertIsOnMainThread();
OWSAssertDebug(!self.isAppReady);
OWSLogInfo(@"");
self.isAppReady = YES;
[self runAppReadyBlocks];
}
- (void)runAppReadyBlocks
{
OWSAssertIsOnMainThread();
OWSAssertDebug(self.isAppReady);
NSArray<AppReadyBlock> *appWillBecomeReadyBlocks = [self.appWillBecomeReadyBlocks copy];
[self.appWillBecomeReadyBlocks removeAllObjects];
NSArray<AppReadyBlock> *appDidBecomeReadyBlocks = [self.appDidBecomeReadyBlocks copy];
[self.appDidBecomeReadyBlocks removeAllObjects];
// We invoke the _will become_ blocks before the _did become_ blocks.
for (AppReadyBlock block in appWillBecomeReadyBlocks) {
block();
}
for (AppReadyBlock block in appDidBecomeReadyBlocks) {
block();
}
}
@end
NS_ASSUME_NONNULL_END

32
SignalUtilitiesKit/AppVersion.h

@ -0,0 +1,32 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface AppVersion : NSObject
// The properties are updated immediately after launch.
@property (atomic, readonly) NSString *firstAppVersion;
@property (atomic, nullable, readonly) NSString *lastAppVersion;
@property (atomic, readonly) NSString *currentAppVersion;
// There properties aren't updated until appLaunchDidComplete is called.
@property (atomic, nullable, readonly) NSString *lastCompletedLaunchAppVersion;
@property (atomic, nullable, readonly) NSString *lastCompletedLaunchMainAppVersion;
@property (atomic, nullable, readonly) NSString *lastCompletedLaunchSAEAppVersion;
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)sharedInstance;
- (void)mainAppLaunchDidComplete;
- (void)saeLaunchDidComplete;
- (BOOL)isFirstLaunch;
@end
NS_ASSUME_NONNULL_END

133
SignalUtilitiesKit/AppVersion.m

@ -0,0 +1,133 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "AppVersion.h"
#import "NSUserDefaults+OWS.h"
#import <SessionProtocolKit/SessionProtocolKit.h>
NS_ASSUME_NONNULL_BEGIN
NSString *const kNSUserDefaults_FirstAppVersion = @"kNSUserDefaults_FirstAppVersion";
NSString *const kNSUserDefaults_LastAppVersion = @"kNSUserDefaults_LastVersion";
NSString *const kNSUserDefaults_LastCompletedLaunchAppVersion = @"kNSUserDefaults_LastCompletedLaunchAppVersion";
NSString *const kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp
= @"kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp";
NSString *const kNSUserDefaults_LastCompletedLaunchAppVersion_SAE
= @"kNSUserDefaults_LastCompletedLaunchAppVersion_SAE";
@interface AppVersion ()
@property (atomic) NSString *firstAppVersion;
@property (atomic, nullable) NSString *lastAppVersion;
@property (atomic) NSString *currentAppVersion;
@property (atomic, nullable) NSString *lastCompletedLaunchAppVersion;
@property (atomic, nullable) NSString *lastCompletedLaunchMainAppVersion;
@property (atomic, nullable) NSString *lastCompletedLaunchSAEAppVersion;
@end
#pragma mark -
@implementation AppVersion
+ (instancetype)sharedInstance
{
static AppVersion *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [AppVersion new];
[instance configure];
});
return instance;
}
- (void)configure {
OWSAssertIsOnMainThread();
self.currentAppVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
// The version of the app when it was first launched.
// nil if the app has never been launched before.
self.firstAppVersion = [[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_FirstAppVersion];
// The version of the app the last time it was launched.
// nil if the app has never been launched before.
self.lastAppVersion = [[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastAppVersion];
self.lastCompletedLaunchAppVersion =
[[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastCompletedLaunchAppVersion];
self.lastCompletedLaunchMainAppVersion =
[[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp];
self.lastCompletedLaunchSAEAppVersion =
[[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastCompletedLaunchAppVersion_SAE];
// Ensure the value for the "first launched version".
if (!self.firstAppVersion) {
self.firstAppVersion = self.currentAppVersion;
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion forKey:kNSUserDefaults_FirstAppVersion];
}
// Update the value for the "most recently launched version".
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion forKey:kNSUserDefaults_LastAppVersion];
[[NSUserDefaults appUserDefaults] synchronize];
// The long version string looks like an IPv4 address.
// To prevent the log scrubber from scrubbing it,
// we replace . with _.
NSString *longVersionString = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]
stringByReplacingOccurrencesOfString:@"."
withString:@"_"];
OWSLogInfo(@"firstAppVersion: %@", self.firstAppVersion);
OWSLogInfo(@"lastAppVersion: %@", self.lastAppVersion);
OWSLogInfo(@"currentAppVersion: %@ (%@)", self.currentAppVersion, longVersionString);
OWSLogInfo(@"lastCompletedLaunchAppVersion: %@", self.lastCompletedLaunchAppVersion);
OWSLogInfo(@"lastCompletedLaunchMainAppVersion: %@", self.lastCompletedLaunchMainAppVersion);
OWSLogInfo(@"lastCompletedLaunchSAEAppVersion: %@", self.lastCompletedLaunchSAEAppVersion);
}
- (void)appLaunchDidComplete
{
OWSAssertIsOnMainThread();
OWSLogInfo(@"appLaunchDidComplete");
self.lastCompletedLaunchAppVersion = self.currentAppVersion;
// Update the value for the "most recently launch-completed version".
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion
forKey:kNSUserDefaults_LastCompletedLaunchAppVersion];
[[NSUserDefaults appUserDefaults] synchronize];
}
- (void)mainAppLaunchDidComplete
{
OWSAssertIsOnMainThread();
self.lastCompletedLaunchMainAppVersion = self.currentAppVersion;
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion
forKey:kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp];
[self appLaunchDidComplete];
}
- (void)saeLaunchDidComplete
{
OWSAssertIsOnMainThread();
self.lastCompletedLaunchSAEAppVersion = self.currentAppVersion;
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion
forKey:kNSUserDefaults_LastCompletedLaunchAppVersion_SAE];
[self appLaunchDidComplete];
}
- (BOOL)isFirstLaunch
{
return self.firstAppVersion != nil;
}
@end
NS_ASSUME_NONNULL_END

7
SignalUtilitiesKit/Array+Description.swift

@ -0,0 +1,7 @@
public extension Array where Element : CustomStringConvertible {
public var prettifiedDescription: String {
return "[ " + map { $0.description }.joined(separator: ", ") + " ]"
}
}

21
SignalUtilitiesKit/BuildConfiguration.swift

@ -0,0 +1,21 @@
public enum BuildConfiguration : String, CustomStringConvertible {
case debug, production
public static let current: BuildConfiguration = {
#if DEBUG
return .debug
#else
return .production
#endif
}()
public var description: String { return rawValue }
}
@objc public final class LKBuildConfiguration : NSObject {
override private init() { }
@objc public static var current: String { return BuildConfiguration.current.description }
}

40
SignalUtilitiesKit/ByteParser.h

@ -0,0 +1,40 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ByteParser : NSObject
@property (nonatomic, readonly) BOOL hasError;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithData:(NSData *)data littleEndian:(BOOL)littleEndian;
#pragma mark - Short
- (uint16_t)shortAtIndex:(NSUInteger)index;
- (uint16_t)nextShort;
#pragma mark - Int
- (uint32_t)intAtIndex:(NSUInteger)index;
- (uint32_t)nextInt;
#pragma mark - Long
- (uint64_t)longAtIndex:(NSUInteger)index;
- (uint64_t)nextLong;
#pragma mark -
- (BOOL)readZero:(NSUInteger)length;
- (nullable NSData *)readBytes:(NSUInteger)length;
@end
NS_ASSUME_NONNULL_END

143
SignalUtilitiesKit/ByteParser.m

@ -0,0 +1,143 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "ByteParser.h"
#import <SessionProtocolKit/SessionProtocolKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ByteParser ()
@property (nonatomic, readonly) BOOL littleEndian;
@property (nonatomic, readonly) NSData *data;
@property (nonatomic) NSUInteger cursor;
@property (nonatomic) BOOL hasError;
@end
#pragma mark -
@implementation ByteParser
- (instancetype)initWithData:(NSData *)data littleEndian:(BOOL)littleEndian
{
if (self = [super init]) {
_littleEndian = littleEndian;
_data = data;
}
return self;
}
#pragma mark - Short
- (uint16_t)shortAtIndex:(NSUInteger)index
{
uint16_t value;
const size_t valueSize = sizeof(value);
OWSAssertDebug(valueSize == 2);
if (index + valueSize > self.data.length) {
self.hasError = YES;
return 0;
}
[self.data getBytes:&value range:NSMakeRange(index, valueSize)];
if (self.littleEndian) {
return CFSwapInt16LittleToHost(value);
} else {
return CFSwapInt16BigToHost(value);
}
}
- (uint16_t)nextShort
{
uint16_t value = [self shortAtIndex:self.cursor];
self.cursor += sizeof(value);
return value;
}
#pragma mark - Int
- (uint32_t)intAtIndex:(NSUInteger)index
{
uint32_t value;
const size_t valueSize = sizeof(value);
OWSAssertDebug(valueSize == 4);
if (index + valueSize > self.data.length) {
self.hasError = YES;
return 0;
}
[self.data getBytes:&value range:NSMakeRange(index, valueSize)];
if (self.littleEndian) {
return CFSwapInt32LittleToHost(value);
} else {
return CFSwapInt32BigToHost(value);
}
}
- (uint32_t)nextInt
{
uint32_t value = [self intAtIndex:self.cursor];
self.cursor += sizeof(value);
return value;
}
#pragma mark - Long
- (uint64_t)longAtIndex:(NSUInteger)index
{
uint64_t value;
const size_t valueSize = sizeof(value);
OWSAssertDebug(valueSize == 8);
if (index + valueSize > self.data.length) {
self.hasError = YES;
return 0;
}
[self.data getBytes:&value range:NSMakeRange(index, valueSize)];
if (self.littleEndian) {
return CFSwapInt64LittleToHost(value);
} else {
return CFSwapInt64BigToHost(value);
}
}
- (uint64_t)nextLong
{
uint64_t value = [self longAtIndex:self.cursor];
self.cursor += sizeof(value);
return value;
}
#pragma mark -
- (BOOL)readZero:(NSUInteger)length
{
NSData *_Nullable subdata = [self readBytes:length];
if (!subdata) {
return NO;
}
uint8_t bytes[length];
[subdata getBytes:bytes range:NSMakeRange(0, length)];
for (int i = 0; i < length; i++) {
if (bytes[i] != 0) {
return NO;
}
}
return YES;
}
- (nullable NSData *)readBytes:(NSUInteger)length
{
NSUInteger index = self.cursor;
if (index + length > self.data.length) {
self.hasError = YES;
return nil;
}
NSData *_Nullable subdata = [self.data subdataWithRange:NSMakeRange(index, length)];
self.cursor += length;
return subdata;
}
@end
NS_ASSUME_NONNULL_END

34
SignalUtilitiesKit/CDSQuote.h

@ -0,0 +1,34 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface CDSQuote : NSObject
@property (nonatomic, readonly) uint16_t version;
@property (nonatomic, readonly) uint16_t signType;
@property (nonatomic, readonly) BOOL isSigLinkable;
@property (nonatomic, readonly) uint32_t gid;
@property (nonatomic, readonly) uint16_t qeSvn;
@property (nonatomic, readonly) uint16_t pceSvn;
@property (nonatomic, readonly) NSData *basename;
@property (nonatomic, readonly) NSData *cpuSvn;
@property (nonatomic, readonly) uint64_t flags;
@property (nonatomic, readonly) uint64_t xfrm;
@property (nonatomic, readonly) NSData *mrenclave;
@property (nonatomic, readonly) NSData *mrsigner;
@property (nonatomic, readonly) uint16_t isvProdId;
@property (nonatomic, readonly) uint16_t isvSvn;
@property (nonatomic, readonly) NSData *reportData;
@property (nonatomic, readonly) NSData *signature;
<