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.
session-ios/SessionUtilitiesKit/Database/Models/Identity.swift

176 lines
5.8 KiB
Swift

// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
public struct Identity: Codable, Identifiable, FetchableRecord, PersistableRecord, TableRecord, ColumnExpressible {
public static var databaseTableName: String { "identity" }
public typealias Columns = CodingKeys
public enum CodingKeys: String, CodingKey, ColumnExpression {
case variant
case data
}
public enum Variant: String, Codable, CaseIterable, DatabaseValueConvertible {
case seed
case ed25519SecretKey
case ed25519PublicKey
case x25519PrivateKey
case x25519PublicKey
}
public var id: Variant { variant }
public let variant: Variant
public let data: Data
// MARK: - Initialization
public init(
variant: Variant,
data: Data
) {
self.variant = variant
self.data = data
}
}
// MARK: - GRDB Interactions
public extension Identity {
static func generate(
from seed: Data,
using dependencies: Dependencies = Dependencies()
) throws -> (ed25519KeyPair: KeyPair, x25519KeyPair: KeyPair) {
guard (seed.count == 16) else { throw GeneralError.invalidSeed }
Merge remote-tracking branch 'upstream/dev' into feature/updated-user-config-handling # Conflicts: # Session/Media Viewing & Editing/PhotoCapture.swift # Session/Meta/Translations/de.lproj/Localizable.strings # Session/Meta/Translations/en.lproj/Localizable.strings # Session/Meta/Translations/es.lproj/Localizable.strings # Session/Meta/Translations/fa.lproj/Localizable.strings # Session/Meta/Translations/fi.lproj/Localizable.strings # Session/Meta/Translations/fr.lproj/Localizable.strings # Session/Meta/Translations/hi.lproj/Localizable.strings # Session/Meta/Translations/hr.lproj/Localizable.strings # Session/Meta/Translations/id-ID.lproj/Localizable.strings # Session/Meta/Translations/it.lproj/Localizable.strings # Session/Meta/Translations/ja.lproj/Localizable.strings # Session/Meta/Translations/nl.lproj/Localizable.strings # Session/Meta/Translations/pl.lproj/Localizable.strings # Session/Meta/Translations/pt_BR.lproj/Localizable.strings # Session/Meta/Translations/ru.lproj/Localizable.strings # Session/Meta/Translations/si.lproj/Localizable.strings # Session/Meta/Translations/sk.lproj/Localizable.strings # Session/Meta/Translations/sv.lproj/Localizable.strings # Session/Meta/Translations/th.lproj/Localizable.strings # Session/Meta/Translations/vi-VN.lproj/Localizable.strings # Session/Meta/Translations/zh-Hant.lproj/Localizable.strings # Session/Meta/Translations/zh_CN.lproj/Localizable.strings # Session/Notifications/AppNotifications.swift # Session/Onboarding/RestoreVC.swift # Session/Shared/SessionTableViewController.swift # Session/Shared/SessionTableViewModel.swift # SessionMessagingKit/Calls/WebRTCSession.swift # SessionMessagingKit/Database/Models/Attachment.swift # SessionMessagingKit/Database/Models/DisappearingMessageConfiguration.swift # SessionMessagingKit/File Server/FileServerAPI.swift # SessionMessagingKit/Jobs/Types/AttachmentDownloadJob.swift # SessionMessagingKit/Open Groups/OpenGroupAPI.swift # SessionMessagingKit/Sending & Receiving/Message Handling/MessageSender+ClosedGroups.swift # SessionMessagingKit/Sending & Receiving/MessageReceiver.swift # SessionMessagingKit/Sending & Receiving/MessageSender.swift # SessionSnodeKit/OnionRequestAPI.swift # SessionSnodeKit/SnodeAPI.swift # SessionUtilitiesKit/Database/Models/Identity.swift # SessionUtilitiesKit/JobRunner/JobRunner.swift
2 years ago
let padding = Data(repeating: 0, count: 16)
guard
let ed25519KeyPair: KeyPair = dependencies[singleton: .crypto].generate(
.ed25519KeyPair(seed: (seed + padding), using: dependencies)
),
let x25519PublicKey: [UInt8] = dependencies[singleton: .crypto].generate(
.x25519(ed25519PublicKey: ed25519KeyPair.publicKey)
),
let x25519SecretKey: [UInt8] = dependencies[singleton: .crypto].generate(
.x25519(ed25519SecretKey: ed25519KeyPair.secretKey)
)
else { throw GeneralError.keyGenerationFailed }
return (
ed25519KeyPair: KeyPair(
publicKey: ed25519KeyPair.publicKey,
secretKey: ed25519KeyPair.secretKey
),
x25519KeyPair: KeyPair(
publicKey: x25519PublicKey,
secretKey: x25519SecretKey
)
)
}
static func store(_ db: Database, seed: Data, ed25519KeyPair: KeyPair, x25519KeyPair: KeyPair) throws {
try Identity(variant: .seed, data: seed).upsert(db)
try Identity(variant: .ed25519SecretKey, data: Data(ed25519KeyPair.secretKey)).upsert(db)
try Identity(variant: .ed25519PublicKey, data: Data(ed25519KeyPair.publicKey)).upsert(db)
try Identity(variant: .x25519PrivateKey, data: Data(x25519KeyPair.secretKey)).upsert(db)
try Identity(variant: .x25519PublicKey, data: Data(x25519KeyPair.publicKey)).upsert(db)
}
static func userExists(
_ db: Database? = nil,
using dependencies: Dependencies = Dependencies()
) -> Bool {
return (fetchUserKeyPair(db, using: dependencies) != nil)
}
static func fetchUserPublicKey(
_ db: Database? = nil,
using dependencies: Dependencies = Dependencies()
) -> Data? {
guard let db: Database = db else {
return dependencies[singleton: .storage].read { db in fetchUserPublicKey(db, using: dependencies) }
}
return try? Identity.fetchOne(db, id: .x25519PublicKey)?.data
}
static func fetchUserPrivateKey(
_ db: Database? = nil,
using dependencies: Dependencies = Dependencies()
) -> Data? {
guard let db: Database = db else {
return dependencies[singleton: .storage].read { db in fetchUserPrivateKey(db, using: dependencies) }
}
return try? Identity.fetchOne(db, id: .x25519PrivateKey)?.data
}
static func fetchUserKeyPair(
_ db: Database? = nil,
using dependencies: Dependencies = Dependencies()
) -> KeyPair? {
guard let db: Database = db else {
return dependencies[singleton: .storage].read { db in fetchUserKeyPair(db, using: dependencies) }
}
guard
let publicKey: Data = fetchUserPublicKey(db, using: dependencies),
let privateKey: Data = fetchUserPrivateKey(db, using: dependencies)
else { return nil }
return KeyPair(
publicKey: publicKey.bytes,
secretKey: privateKey.bytes
)
}
static func fetchUserEd25519KeyPair(
_ db: Database? = nil,
using dependencies: Dependencies = Dependencies()
) -> KeyPair? {
guard let db: Database = db else {
return dependencies[singleton: .storage].read { db in fetchUserEd25519KeyPair(db, using: dependencies) }
}
guard
let publicKey: Data = try? Identity.fetchOne(db, id: .ed25519PublicKey)?.data,
let secretKey: Data = try? Identity.fetchOne(db, id: .ed25519SecretKey)?.data
else { return nil }
return KeyPair(
publicKey: publicKey.bytes,
secretKey: secretKey.bytes
)
}
static func fetchHexEncodedSeed(
_ db: Database? = nil,
using dependencies: Dependencies
) -> String? {
guard let db: Database = db else {
return dependencies[singleton: .storage].read { db in
fetchHexEncodedSeed(db, using: dependencies)
}
}
guard let data: Data = try? Identity.fetchOne(db, id: .seed)?.data else {
return nil
}
return data.toHexString()
}
}
// MARK: - Convenience
public extension Notification.Name {
static let registrationStateDidChange = Notification.Name("registrationStateDidChange")
}
public extension Identity {
static func didRegister() {
NotificationCenter.default.post(name: .registrationStateDidChange, object: nil, userInfo: nil)
}
}