mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			168 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			168 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Swift
		
	
| // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
 | |
| 
 | |
| import Foundation
 | |
| import YapDatabase
 | |
| 
 | |
| public enum SUKLegacy {
 | |
|     // MARK: - YapDatabase
 | |
|     
 | |
|     private static let keychainService = "TSKeyChainService"
 | |
|     private static let keychainDBCipherKeySpec = "OWSDatabaseCipherKeySpec"
 | |
|     private static let sqlCipherKeySpecLength = 48
 | |
|     
 | |
|     private static var database: Atomic<YapDatabase>?
 | |
|     
 | |
|     // MARK: - Collections and Keys
 | |
|     
 | |
|     internal static let userAccountRegisteredNumberKey = "TSStorageRegisteredNumberKey"
 | |
|     internal static let userAccountCollection = "TSStorageUserAccountCollection"
 | |
|     
 | |
|     internal static let identityKeyStoreSeedKey = "LKLokiSeed"
 | |
|     internal static let identityKeyStoreEd25519SecretKey = "LKED25519SecretKey"
 | |
|     internal static let identityKeyStoreEd25519PublicKey = "LKED25519PublicKey"
 | |
|     internal static let identityKeyStoreIdentityKey = "TSStorageManagerIdentityKeyStoreIdentityKey"
 | |
|     internal static let identityKeyStoreCollection = "TSStorageManagerIdentityKeyStoreCollection"
 | |
|     
 | |
|     // MARK: - Database Functions
 | |
|     
 | |
|     public static var legacyDatabaseFilepath: String {
 | |
|         let sharedDirUrl: URL = URL(fileURLWithPath: OWSFileSystem.appSharedDataDirectoryPath())
 | |
|         
 | |
|         return sharedDirUrl
 | |
|             .appendingPathComponent("database")
 | |
|             .appendingPathComponent("Signal.sqlite")
 | |
|             .path
 | |
|     }
 | |
|     
 | |
|     private static let legacyDatabaseDeserializer: YapDatabaseDeserializer = {
 | |
|         return { (collection: String, key: String, data: Data) -> Any in
 | |
|             /// **Note:** The old `init(forReadingWith:)` method has been deprecated with `init(forReadingFrom:)`
 | |
|             /// and Apple changed the default of `requiresSecureCoding` to be true, this results in some of the types from failing
 | |
|             /// to decode, as a result we need to set it to false here
 | |
|             let unarchiver: NSKeyedUnarchiver? = try? NSKeyedUnarchiver(forReadingFrom: data)
 | |
|             unarchiver?.requiresSecureCoding = false
 | |
|             
 | |
|             guard !data.isEmpty, let result = unarchiver?.decodeObject(forKey: "root") else {
 | |
|                 return UnknownDBObject()
 | |
|             }
 | |
|             
 | |
|             return result
 | |
|         }
 | |
|     }()
 | |
|     
 | |
|     public static var hasLegacyDatabaseFile: Bool {
 | |
|         return FileManager.default.fileExists(atPath: legacyDatabaseFilepath)
 | |
|     }
 | |
|     
 | |
|     @discardableResult public static func loadDatabaseIfNeeded() -> Bool {
 | |
|         guard SUKLegacy.database == nil else { return true }
 | |
|         
 | |
|         /// Ensure the databaseKeySpec exists
 | |
|         var maybeKeyData: Data? = try? SSKDefaultKeychainStorage.shared.data(
 | |
|             forService: keychainService,
 | |
|             key: keychainDBCipherKeySpec
 | |
|         )
 | |
|         defer { if maybeKeyData != nil { maybeKeyData!.resetBytes(in: 0..<maybeKeyData!.count) } }
 | |
|         
 | |
|         guard maybeKeyData != nil, maybeKeyData?.count == sqlCipherKeySpecLength else { return false }
 | |
|         
 | |
|         // Setup the database options
 | |
|         let options: YapDatabaseOptions = YapDatabaseOptions()
 | |
|         options.corruptAction = .fail
 | |
|         options.enableMultiProcessSupport = true
 | |
|         options.cipherUnencryptedHeaderLength = kSqliteHeaderLength // Needed for iOS to support SQLite writes
 | |
|         options.legacyCipherCompatibilityVersion = 3    // Old DB was SQLCipher V3
 | |
|         options.cipherKeySpecBlock = {
 | |
|             /// To avoid holding the keySpec in memory too long we load it as needed, since we have already confirmed
 | |
|             /// it's existence we can force-try here (the database will crash if it's invalid anyway)
 | |
|             var keySpec: Data = try! SSKDefaultKeychainStorage.shared.data(
 | |
|                 forService: keychainService,
 | |
|                 key: keychainDBCipherKeySpec
 | |
|             )
 | |
|             defer { keySpec.resetBytes(in: 0..<keySpec.count) }
 | |
|             
 | |
|             return keySpec
 | |
|         }
 | |
|         
 | |
|         let maybeDatabase: YapDatabase? = YapDatabase(
 | |
|             path: legacyDatabaseFilepath,
 | |
|             serializer: nil,
 | |
|             deserializer: legacyDatabaseDeserializer,
 | |
|             options: options
 | |
|         )
 | |
|         
 | |
|         guard let database: YapDatabase = maybeDatabase else { return false }
 | |
|         
 | |
|         // Store the database instance atomically
 | |
|         SUKLegacy.database = Atomic(database)
 | |
|         
 | |
|         return true
 | |
|     }
 | |
|     
 | |
|     public static func newDatabaseConnection() -> YapDatabaseConnection? {
 | |
|         SUKLegacy.loadDatabaseIfNeeded()
 | |
|         
 | |
|         return self.database?.wrappedValue.newConnection()
 | |
|     }
 | |
|     
 | |
|     public static func clearLegacyDatabaseInstance() {
 | |
|         self.database = nil
 | |
|     }
 | |
|     
 | |
|     public static func deleteLegacyDatabaseFilesAndKey() throws {
 | |
|         OWSFileSystem.deleteFile(legacyDatabaseFilepath)
 | |
|         OWSFileSystem.deleteFile("\(legacyDatabaseFilepath)-shm")
 | |
|         OWSFileSystem.deleteFile("\(legacyDatabaseFilepath)-wal")
 | |
|         try SSKDefaultKeychainStorage.shared.remove(service: keychainService, key: keychainDBCipherKeySpec)
 | |
|     }
 | |
|     
 | |
|     // MARK: - UnknownDBObject
 | |
|     
 | |
|     @objc(LegacyUnknownDBObject)
 | |
|     public class UnknownDBObject: NSObject, NSCoding {
 | |
|         override public init() {}
 | |
|         public required init?(coder: NSCoder) {}
 | |
|         public func encode(with coder: NSCoder) { fatalError("Shouldn't be encoding this type") }
 | |
|     }
 | |
|     
 | |
|     // MARK: - LagacyKeyPair
 | |
|     
 | |
|     @objc(LegacyKeyPair)
 | |
|     public class KeyPair: NSObject, NSCoding {
 | |
|         private static let keyLength: Int = 32
 | |
|         private static let publicKeyKey: String = "TSECKeyPairPublicKey"
 | |
|         private static let privateKeyKey: String = "TSECKeyPairPrivateKey"
 | |
|         
 | |
|         public let publicKey: Data
 | |
|         public let privateKey: Data
 | |
|         
 | |
|         public init(
 | |
|             publicKeyData: Data,
 | |
|             privateKeyData: Data
 | |
|         ) {
 | |
|             publicKey = publicKeyData
 | |
|             privateKey = privateKeyData
 | |
|         }
 | |
|         
 | |
|         public required init?(coder: NSCoder) {
 | |
|             var pubKeyLength: Int = 0
 | |
|             var privKeyLength: Int = 0
 | |
|             
 | |
|             guard
 | |
|                 let pubKeyBytes: UnsafePointer<UInt8> = coder.decodeBytes(forKey: KeyPair.publicKeyKey, returnedLength: &pubKeyLength),
 | |
|                 let privateKeyBytes: UnsafePointer<UInt8> = coder.decodeBytes(forKey: KeyPair.privateKeyKey, returnedLength: &privKeyLength),
 | |
|                 pubKeyLength == KeyPair.keyLength,
 | |
|                 privKeyLength == KeyPair.keyLength
 | |
|             else {
 | |
|                 // Fail if the keys aren't the correct length
 | |
|                 return nil
 | |
|             }
 | |
|             
 | |
|             publicKey = Data(bytes: pubKeyBytes, count: pubKeyLength)
 | |
|             privateKey = Data(bytes: privateKeyBytes, count: privKeyLength)
 | |
|         }
 | |
|         
 | |
|         public func encode(with coder: NSCoder) { fatalError("Shouldn't be encoding this type") }
 | |
|     }
 | |
| }
 |