Implement GPG key QR transfer
This commit is contained in:
@@ -580,6 +580,24 @@ fileprivate struct FfiConverterString: FfiConverter {
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
fileprivate struct FfiConverterData: FfiConverterRustBuffer {
|
||||
typealias SwiftType = Data
|
||||
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Data {
|
||||
let len: Int32 = try readInt(&buf)
|
||||
return Data(try readBytes(&buf, count: Int(len)))
|
||||
}
|
||||
|
||||
public static func write(_ value: Data, into buf: inout [UInt8]) {
|
||||
let len = Int32(value.count)
|
||||
writeInt(&buf, len)
|
||||
writeBytes(&buf, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1172,6 +1190,279 @@ public func FfiConverterTypeMobileHomeOperation_lower(_ value: MobileHomeOperati
|
||||
|
||||
|
||||
|
||||
public protocol MobileKeyTransferProtocol: AnyObject, Sendable {
|
||||
|
||||
func export(fingerprint: String, kind: MobileKeyTransferKind, passphrase: String?) throws -> MobileKeyTransferExport
|
||||
|
||||
func importer() -> MobileKeyTransferImport
|
||||
|
||||
func keys() -> [MobileKeyTransferKey]
|
||||
|
||||
}
|
||||
open class MobileKeyTransfer: MobileKeyTransferProtocol, @unchecked Sendable {
|
||||
fileprivate let handle: UInt64
|
||||
|
||||
/// Used to instantiate a [FFIObject] without an actual handle, for fakes in tests, mostly.
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct NoHandle {
|
||||
public init() {}
|
||||
}
|
||||
|
||||
// TODO: We'd like this to be `private` but for Swifty reasons,
|
||||
// we can't implement `FfiConverter` without making this `required` and we can't
|
||||
// make it `required` without making it `public`.
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
required public init(unsafeFromHandle handle: UInt64) {
|
||||
self.handle = handle
|
||||
}
|
||||
|
||||
// This constructor can be used to instantiate a fake object.
|
||||
// - Parameter noHandle: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject].
|
||||
//
|
||||
// - Warning:
|
||||
// Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing handle the FFI lower functions will crash.
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public init(noHandle: NoHandle) {
|
||||
self.handle = 0
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func uniffiCloneHandle() -> UInt64 {
|
||||
return try! rustCall { uniffi_ironstorage_apple_fn_clone_mobilekeytransfer(self.handle, $0) }
|
||||
}
|
||||
// No primary constructor declared for this class.
|
||||
|
||||
deinit {
|
||||
if handle == 0 {
|
||||
// Mock objects have handle=0 don't try to free them
|
||||
return
|
||||
}
|
||||
|
||||
try! rustCall { uniffi_ironstorage_apple_fn_free_mobilekeytransfer(handle, $0) }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
open func export(fingerprint: String, kind: MobileKeyTransferKind, passphrase: String?)throws -> MobileKeyTransferExport {
|
||||
return try FfiConverterTypeMobileKeyTransferExport_lift(try rustCallWithError(FfiConverterTypeMobileKeyTransferFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
uniffi_ironstorage_apple_fn_method_mobilekeytransfer_export(
|
||||
self.uniffiCloneHandle(),
|
||||
FfiConverterString.lower(fingerprint),
|
||||
FfiConverterTypeMobileKeyTransferKind_lower(kind),
|
||||
FfiConverterOptionString.lower(passphrase),uniffiCallStatus
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
open func importer() -> MobileKeyTransferImport {
|
||||
return try! FfiConverterTypeMobileKeyTransferImport_lift(try! rustCall() {
|
||||
uniffiCallStatus in
|
||||
uniffi_ironstorage_apple_fn_method_mobilekeytransfer_importer(
|
||||
self.uniffiCloneHandle(),uniffiCallStatus
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
open func keys() -> [MobileKeyTransferKey] {
|
||||
return try! FfiConverterSequenceTypeMobileKeyTransferKey.lift(try! rustCall() {
|
||||
uniffiCallStatus in
|
||||
uniffi_ironstorage_apple_fn_method_mobilekeytransfer_keys(
|
||||
self.uniffiCloneHandle(),uniffiCallStatus
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct FfiConverterTypeMobileKeyTransfer: FfiConverter {
|
||||
typealias FfiType = UInt64
|
||||
typealias SwiftType = MobileKeyTransfer
|
||||
|
||||
public static func lift(_ handle: UInt64) throws -> MobileKeyTransfer {
|
||||
return MobileKeyTransfer(unsafeFromHandle: handle)
|
||||
}
|
||||
|
||||
public static func lower(_ value: MobileKeyTransfer) -> UInt64 {
|
||||
return value.uniffiCloneHandle()
|
||||
}
|
||||
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileKeyTransfer {
|
||||
let handle: UInt64 = try readInt(&buf)
|
||||
return try lift(handle)
|
||||
}
|
||||
|
||||
public static func write(_ value: MobileKeyTransfer, into buf: inout [UInt8]) {
|
||||
writeInt(&buf, lower(value))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransfer_lift(_ handle: UInt64) throws -> MobileKeyTransfer {
|
||||
return try FfiConverterTypeMobileKeyTransfer.lift(handle)
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransfer_lower(_ value: MobileKeyTransfer) -> UInt64 {
|
||||
return FfiConverterTypeMobileKeyTransfer.lower(value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public protocol MobileKeyTransferImportProtocol: AnyObject, Sendable {
|
||||
|
||||
func addFrame(payload: String) throws -> MobileKeyTransferProgress
|
||||
|
||||
func `import`(passphrase: String?, makeDefault: Bool) throws -> MobileKeyTransferOutcome
|
||||
|
||||
}
|
||||
open class MobileKeyTransferImport: MobileKeyTransferImportProtocol, @unchecked Sendable {
|
||||
fileprivate let handle: UInt64
|
||||
|
||||
/// Used to instantiate a [FFIObject] without an actual handle, for fakes in tests, mostly.
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct NoHandle {
|
||||
public init() {}
|
||||
}
|
||||
|
||||
// TODO: We'd like this to be `private` but for Swifty reasons,
|
||||
// we can't implement `FfiConverter` without making this `required` and we can't
|
||||
// make it `required` without making it `public`.
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
required public init(unsafeFromHandle handle: UInt64) {
|
||||
self.handle = handle
|
||||
}
|
||||
|
||||
// This constructor can be used to instantiate a fake object.
|
||||
// - Parameter noHandle: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject].
|
||||
//
|
||||
// - Warning:
|
||||
// Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing handle the FFI lower functions will crash.
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public init(noHandle: NoHandle) {
|
||||
self.handle = 0
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func uniffiCloneHandle() -> UInt64 {
|
||||
return try! rustCall { uniffi_ironstorage_apple_fn_clone_mobilekeytransferimport(self.handle, $0) }
|
||||
}
|
||||
// No primary constructor declared for this class.
|
||||
|
||||
deinit {
|
||||
if handle == 0 {
|
||||
// Mock objects have handle=0 don't try to free them
|
||||
return
|
||||
}
|
||||
|
||||
try! rustCall { uniffi_ironstorage_apple_fn_free_mobilekeytransferimport(handle, $0) }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
open func addFrame(payload: String)throws -> MobileKeyTransferProgress {
|
||||
return try FfiConverterTypeMobileKeyTransferProgress_lift(try rustCallWithError(FfiConverterTypeMobileKeyTransferFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
uniffi_ironstorage_apple_fn_method_mobilekeytransferimport_add_frame(
|
||||
self.uniffiCloneHandle(),
|
||||
FfiConverterString.lower(payload),uniffiCallStatus
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
open func `import`(passphrase: String?, makeDefault: Bool)throws -> MobileKeyTransferOutcome {
|
||||
return try FfiConverterTypeMobileKeyTransferOutcome_lift(try rustCallWithError(FfiConverterTypeMobileKeyTransferFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
uniffi_ironstorage_apple_fn_method_mobilekeytransferimport_import(
|
||||
self.uniffiCloneHandle(),
|
||||
FfiConverterOptionString.lower(passphrase),
|
||||
FfiConverterBool.lower(makeDefault),uniffiCallStatus
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct FfiConverterTypeMobileKeyTransferImport: FfiConverter {
|
||||
typealias FfiType = UInt64
|
||||
typealias SwiftType = MobileKeyTransferImport
|
||||
|
||||
public static func lift(_ handle: UInt64) throws -> MobileKeyTransferImport {
|
||||
return MobileKeyTransferImport(unsafeFromHandle: handle)
|
||||
}
|
||||
|
||||
public static func lower(_ value: MobileKeyTransferImport) -> UInt64 {
|
||||
return value.uniffiCloneHandle()
|
||||
}
|
||||
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileKeyTransferImport {
|
||||
let handle: UInt64 = try readInt(&buf)
|
||||
return try lift(handle)
|
||||
}
|
||||
|
||||
public static func write(_ value: MobileKeyTransferImport, into buf: inout [UInt8]) {
|
||||
writeInt(&buf, lower(value))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferImport_lift(_ handle: UInt64) throws -> MobileKeyTransferImport {
|
||||
return try FfiConverterTypeMobileKeyTransferImport.lift(handle)
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferImport_lower(_ value: MobileKeyTransferImport) -> UInt64 {
|
||||
return FfiConverterTypeMobileKeyTransferImport.lower(value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public protocol MobileOnboardingOperationProtocol: AnyObject, Sendable {
|
||||
|
||||
func cancel()
|
||||
@@ -2319,6 +2610,304 @@ public func FfiConverterTypeMobileHomeSummaryRow_lower(_ value: MobileHomeSummar
|
||||
}
|
||||
|
||||
|
||||
public struct MobileKeyTransferExport: Equatable, Hashable {
|
||||
public var key: MobileKeyTransferKey
|
||||
public var frames: [MobileKeyTransferFrame]
|
||||
|
||||
// Default memberwise initializers are never public by default, so we
|
||||
// declare one manually.
|
||||
public init(key: MobileKeyTransferKey, frames: [MobileKeyTransferFrame]) {
|
||||
self.key = key
|
||||
self.frames = frames
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#if compiler(>=6)
|
||||
extension MobileKeyTransferExport: Sendable {}
|
||||
#endif
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct FfiConverterTypeMobileKeyTransferExport: FfiConverterRustBuffer {
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileKeyTransferExport {
|
||||
return
|
||||
try MobileKeyTransferExport(
|
||||
key: FfiConverterTypeMobileKeyTransferKey.read(from: &buf),
|
||||
frames: FfiConverterSequenceTypeMobileKeyTransferFrame.read(from: &buf)
|
||||
)
|
||||
}
|
||||
|
||||
public static func write(_ value: MobileKeyTransferExport, into buf: inout [UInt8]) {
|
||||
FfiConverterTypeMobileKeyTransferKey.write(value.key, into: &buf)
|
||||
FfiConverterSequenceTypeMobileKeyTransferFrame.write(value.frames, into: &buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferExport_lift(_ buf: RustBuffer) throws -> MobileKeyTransferExport {
|
||||
return try FfiConverterTypeMobileKeyTransferExport.lift(buf)
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferExport_lower(_ value: MobileKeyTransferExport) -> RustBuffer {
|
||||
return FfiConverterTypeMobileKeyTransferExport.lower(value)
|
||||
}
|
||||
|
||||
|
||||
public struct MobileKeyTransferFrame: Equatable, Hashable {
|
||||
public var sequence: UInt32
|
||||
public var total: UInt32
|
||||
public var width: UInt32
|
||||
public var modules: Data
|
||||
|
||||
// Default memberwise initializers are never public by default, so we
|
||||
// declare one manually.
|
||||
public init(sequence: UInt32, total: UInt32, width: UInt32, modules: Data) {
|
||||
self.sequence = sequence
|
||||
self.total = total
|
||||
self.width = width
|
||||
self.modules = modules
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#if compiler(>=6)
|
||||
extension MobileKeyTransferFrame: Sendable {}
|
||||
#endif
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct FfiConverterTypeMobileKeyTransferFrame: FfiConverterRustBuffer {
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileKeyTransferFrame {
|
||||
return
|
||||
try MobileKeyTransferFrame(
|
||||
sequence: FfiConverterUInt32.read(from: &buf),
|
||||
total: FfiConverterUInt32.read(from: &buf),
|
||||
width: FfiConverterUInt32.read(from: &buf),
|
||||
modules: FfiConverterData.read(from: &buf)
|
||||
)
|
||||
}
|
||||
|
||||
public static func write(_ value: MobileKeyTransferFrame, into buf: inout [UInt8]) {
|
||||
FfiConverterUInt32.write(value.sequence, into: &buf)
|
||||
FfiConverterUInt32.write(value.total, into: &buf)
|
||||
FfiConverterUInt32.write(value.width, into: &buf)
|
||||
FfiConverterData.write(value.modules, into: &buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferFrame_lift(_ buf: RustBuffer) throws -> MobileKeyTransferFrame {
|
||||
return try FfiConverterTypeMobileKeyTransferFrame.lift(buf)
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferFrame_lower(_ value: MobileKeyTransferFrame) -> RustBuffer {
|
||||
return FfiConverterTypeMobileKeyTransferFrame.lower(value)
|
||||
}
|
||||
|
||||
|
||||
public struct MobileKeyTransferKey: Equatable, Hashable {
|
||||
public var fingerprint: String
|
||||
public var title: String
|
||||
public var detail: String
|
||||
public var kind: MobileKeyTransferKind
|
||||
public var requiresPassphrase: Bool
|
||||
|
||||
// Default memberwise initializers are never public by default, so we
|
||||
// declare one manually.
|
||||
public init(fingerprint: String, title: String, detail: String, kind: MobileKeyTransferKind, requiresPassphrase: Bool) {
|
||||
self.fingerprint = fingerprint
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.kind = kind
|
||||
self.requiresPassphrase = requiresPassphrase
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#if compiler(>=6)
|
||||
extension MobileKeyTransferKey: Sendable {}
|
||||
#endif
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct FfiConverterTypeMobileKeyTransferKey: FfiConverterRustBuffer {
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileKeyTransferKey {
|
||||
return
|
||||
try MobileKeyTransferKey(
|
||||
fingerprint: FfiConverterString.read(from: &buf),
|
||||
title: FfiConverterString.read(from: &buf),
|
||||
detail: FfiConverterString.read(from: &buf),
|
||||
kind: FfiConverterTypeMobileKeyTransferKind.read(from: &buf),
|
||||
requiresPassphrase: FfiConverterBool.read(from: &buf)
|
||||
)
|
||||
}
|
||||
|
||||
public static func write(_ value: MobileKeyTransferKey, into buf: inout [UInt8]) {
|
||||
FfiConverterString.write(value.fingerprint, into: &buf)
|
||||
FfiConverterString.write(value.title, into: &buf)
|
||||
FfiConverterString.write(value.detail, into: &buf)
|
||||
FfiConverterTypeMobileKeyTransferKind.write(value.kind, into: &buf)
|
||||
FfiConverterBool.write(value.requiresPassphrase, into: &buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferKey_lift(_ buf: RustBuffer) throws -> MobileKeyTransferKey {
|
||||
return try FfiConverterTypeMobileKeyTransferKey.lift(buf)
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferKey_lower(_ value: MobileKeyTransferKey) -> RustBuffer {
|
||||
return FfiConverterTypeMobileKeyTransferKey.lower(value)
|
||||
}
|
||||
|
||||
|
||||
public struct MobileKeyTransferOutcome: Equatable, Hashable {
|
||||
public var title: String
|
||||
public var detail: String
|
||||
|
||||
// Default memberwise initializers are never public by default, so we
|
||||
// declare one manually.
|
||||
public init(title: String, detail: String) {
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#if compiler(>=6)
|
||||
extension MobileKeyTransferOutcome: Sendable {}
|
||||
#endif
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct FfiConverterTypeMobileKeyTransferOutcome: FfiConverterRustBuffer {
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileKeyTransferOutcome {
|
||||
return
|
||||
try MobileKeyTransferOutcome(
|
||||
title: FfiConverterString.read(from: &buf),
|
||||
detail: FfiConverterString.read(from: &buf)
|
||||
)
|
||||
}
|
||||
|
||||
public static func write(_ value: MobileKeyTransferOutcome, into buf: inout [UInt8]) {
|
||||
FfiConverterString.write(value.title, into: &buf)
|
||||
FfiConverterString.write(value.detail, into: &buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferOutcome_lift(_ buf: RustBuffer) throws -> MobileKeyTransferOutcome {
|
||||
return try FfiConverterTypeMobileKeyTransferOutcome.lift(buf)
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferOutcome_lower(_ value: MobileKeyTransferOutcome) -> RustBuffer {
|
||||
return FfiConverterTypeMobileKeyTransferOutcome.lower(value)
|
||||
}
|
||||
|
||||
|
||||
public struct MobileKeyTransferProgress: Equatable, Hashable {
|
||||
public var received: UInt32
|
||||
public var total: UInt32
|
||||
public var duplicate: Bool
|
||||
public var key: MobileKeyTransferKey?
|
||||
|
||||
// Default memberwise initializers are never public by default, so we
|
||||
// declare one manually.
|
||||
public init(received: UInt32, total: UInt32, duplicate: Bool, key: MobileKeyTransferKey?) {
|
||||
self.received = received
|
||||
self.total = total
|
||||
self.duplicate = duplicate
|
||||
self.key = key
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#if compiler(>=6)
|
||||
extension MobileKeyTransferProgress: Sendable {}
|
||||
#endif
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct FfiConverterTypeMobileKeyTransferProgress: FfiConverterRustBuffer {
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileKeyTransferProgress {
|
||||
return
|
||||
try MobileKeyTransferProgress(
|
||||
received: FfiConverterUInt32.read(from: &buf),
|
||||
total: FfiConverterUInt32.read(from: &buf),
|
||||
duplicate: FfiConverterBool.read(from: &buf),
|
||||
key: FfiConverterOptionTypeMobileKeyTransferKey.read(from: &buf)
|
||||
)
|
||||
}
|
||||
|
||||
public static func write(_ value: MobileKeyTransferProgress, into buf: inout [UInt8]) {
|
||||
FfiConverterUInt32.write(value.received, into: &buf)
|
||||
FfiConverterUInt32.write(value.total, into: &buf)
|
||||
FfiConverterBool.write(value.duplicate, into: &buf)
|
||||
FfiConverterOptionTypeMobileKeyTransferKey.write(value.key, into: &buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferProgress_lift(_ buf: RustBuffer) throws -> MobileKeyTransferProgress {
|
||||
return try FfiConverterTypeMobileKeyTransferProgress.lift(buf)
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferProgress_lower(_ value: MobileKeyTransferProgress) -> RustBuffer {
|
||||
return FfiConverterTypeMobileKeyTransferProgress.lower(value)
|
||||
}
|
||||
|
||||
|
||||
public struct MobileOnboardingDiscovery: Equatable, Hashable {
|
||||
public var branches: [String]
|
||||
public var selectedBranch: UInt32
|
||||
@@ -3880,6 +4469,147 @@ public func FfiConverterTypeMobileHomePhase_lower(_ value: MobileHomePhase) -> R
|
||||
|
||||
|
||||
|
||||
public
|
||||
enum MobileKeyTransferFfiError: Swift.Error, Equatable, Hashable, Foundation.LocalizedError {
|
||||
|
||||
|
||||
|
||||
case Failed(message: String
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public var errorDescription: String? {
|
||||
String(reflecting: self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if compiler(>=6)
|
||||
extension MobileKeyTransferFfiError: Sendable {}
|
||||
#endif
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct FfiConverterTypeMobileKeyTransferFfiError: FfiConverterRustBuffer {
|
||||
typealias SwiftType = MobileKeyTransferFfiError
|
||||
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileKeyTransferFfiError {
|
||||
let variant: Int32 = try readInt(&buf)
|
||||
switch variant {
|
||||
|
||||
|
||||
|
||||
|
||||
case 1: return .Failed(
|
||||
message: try FfiConverterString.read(from: &buf)
|
||||
)
|
||||
|
||||
default: throw UniffiInternalError.unexpectedEnumCase
|
||||
}
|
||||
}
|
||||
|
||||
public static func write(_ value: MobileKeyTransferFfiError, into buf: inout [UInt8]) {
|
||||
switch value {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
case let .Failed(message):
|
||||
writeInt(&buf, Int32(1))
|
||||
FfiConverterString.write(message, into: &buf)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferFfiError_lift(_ buf: RustBuffer) throws -> MobileKeyTransferFfiError {
|
||||
return try FfiConverterTypeMobileKeyTransferFfiError.lift(buf)
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferFfiError_lower(_ value: MobileKeyTransferFfiError) -> RustBuffer {
|
||||
return FfiConverterTypeMobileKeyTransferFfiError.lower(value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
public enum MobileKeyTransferKind: Equatable, Hashable {
|
||||
|
||||
case `public`
|
||||
case `private`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#if compiler(>=6)
|
||||
extension MobileKeyTransferKind: Sendable {}
|
||||
#endif
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct FfiConverterTypeMobileKeyTransferKind: FfiConverterRustBuffer {
|
||||
typealias SwiftType = MobileKeyTransferKind
|
||||
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileKeyTransferKind {
|
||||
let variant: Int32 = try readInt(&buf)
|
||||
switch variant {
|
||||
|
||||
case 1: return .`public`
|
||||
|
||||
case 2: return .`private`
|
||||
|
||||
default: throw UniffiInternalError.unexpectedEnumCase
|
||||
}
|
||||
}
|
||||
|
||||
public static func write(_ value: MobileKeyTransferKind, into buf: inout [UInt8]) {
|
||||
switch value {
|
||||
|
||||
|
||||
case .`public`:
|
||||
writeInt(&buf, Int32(1))
|
||||
|
||||
|
||||
case .`private`:
|
||||
writeInt(&buf, Int32(2))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferKind_lift(_ buf: RustBuffer) throws -> MobileKeyTransferKind {
|
||||
return try FfiConverterTypeMobileKeyTransferKind.lift(buf)
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeMobileKeyTransferKind_lower(_ value: MobileKeyTransferKind) -> RustBuffer {
|
||||
return FfiConverterTypeMobileKeyTransferKind.lower(value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public enum MobileOnboardingErrorKind: Equatable, Hashable {
|
||||
|
||||
@@ -4796,6 +5526,30 @@ fileprivate struct FfiConverterOptionTypeMobileHomeNotice: FfiConverterRustBuffe
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
fileprivate struct FfiConverterOptionTypeMobileKeyTransferKey: FfiConverterRustBuffer {
|
||||
typealias SwiftType = MobileKeyTransferKey?
|
||||
|
||||
public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
|
||||
guard let value = value else {
|
||||
writeInt(&buf, Int8(0))
|
||||
return
|
||||
}
|
||||
writeInt(&buf, Int8(1))
|
||||
FfiConverterTypeMobileKeyTransferKey.write(value, into: &buf)
|
||||
}
|
||||
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
|
||||
switch try readInt(&buf) as Int8 {
|
||||
case 0: return nil
|
||||
case 1: return try FfiConverterTypeMobileKeyTransferKey.read(from: &buf)
|
||||
default: throw UniffiInternalError.unexpectedOptionalTag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
@@ -4996,6 +5750,56 @@ fileprivate struct FfiConverterSequenceTypeMobileHomeSummaryRow: FfiConverterRus
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
fileprivate struct FfiConverterSequenceTypeMobileKeyTransferFrame: FfiConverterRustBuffer {
|
||||
typealias SwiftType = [MobileKeyTransferFrame]
|
||||
|
||||
public static func write(_ value: [MobileKeyTransferFrame], into buf: inout [UInt8]) {
|
||||
let len = Int32(value.count)
|
||||
writeInt(&buf, len)
|
||||
for item in value {
|
||||
FfiConverterTypeMobileKeyTransferFrame.write(item, into: &buf)
|
||||
}
|
||||
}
|
||||
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [MobileKeyTransferFrame] {
|
||||
let len: Int32 = try readInt(&buf)
|
||||
var seq = [MobileKeyTransferFrame]()
|
||||
seq.reserveCapacity(Int(len))
|
||||
for _ in 0 ..< len {
|
||||
seq.append(try FfiConverterTypeMobileKeyTransferFrame.read(from: &buf))
|
||||
}
|
||||
return seq
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
fileprivate struct FfiConverterSequenceTypeMobileKeyTransferKey: FfiConverterRustBuffer {
|
||||
typealias SwiftType = [MobileKeyTransferKey]
|
||||
|
||||
public static func write(_ value: [MobileKeyTransferKey], into buf: inout [UInt8]) {
|
||||
let len = Int32(value.count)
|
||||
writeInt(&buf, len)
|
||||
for item in value {
|
||||
FfiConverterTypeMobileKeyTransferKey.write(item, into: &buf)
|
||||
}
|
||||
}
|
||||
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [MobileKeyTransferKey] {
|
||||
let len: Int32 = try readInt(&buf)
|
||||
var seq = [MobileKeyTransferKey]()
|
||||
seq.reserveCapacity(Int(len))
|
||||
for _ in 0 ..< len {
|
||||
seq.append(try FfiConverterTypeMobileKeyTransferKey.read(from: &buf))
|
||||
}
|
||||
return seq
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
@@ -5084,6 +5888,13 @@ public func mobileHomeOperation() -> MobileHomeOperation {
|
||||
)
|
||||
})
|
||||
}
|
||||
public func mobileKeyTransfer()throws -> MobileKeyTransfer {
|
||||
return try FfiConverterTypeMobileKeyTransfer_lift(try rustCallWithError(FfiConverterTypeMobileKeyTransferFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
uniffi_ironstorage_apple_fn_func_mobile_key_transfer(uniffiCallStatus
|
||||
)
|
||||
})
|
||||
}
|
||||
public func mobileOnboardingOperation(serverUrl: String, account: String, repositoryPath: String, applicationToken: String)throws -> MobileOnboardingOperation {
|
||||
return try FfiConverterTypeMobileOnboardingOperation_lift(try rustCallWithError(FfiConverterTypeMobileOnboardingFfiError_lift) {
|
||||
uniffiCallStatus in
|
||||
@@ -5162,6 +5973,9 @@ private let initializationResult: InitializationResult = {
|
||||
if (uniffi_ironstorage_apple_checksum_func_mobile_home_operation() != 10595) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_func_mobile_key_transfer() != 57389) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_func_mobile_onboarding_operation() != 8354) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
@@ -5276,6 +6090,21 @@ private let initializationResult: InitializationResult = {
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobilehomeoperation_refresh_if_stale() != 27818) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobilekeytransfer_export() != 50467) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobilekeytransfer_importer() != 55825) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobilekeytransfer_keys() != 1261) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobilekeytransferimport_add_frame() != 27319) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobilekeytransferimport_import() != 37403) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_ironstorage_apple_checksum_method_mobileonboardingoperation_cancel() != 49755) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
|
||||
@@ -418,6 +418,51 @@ RustBuffer uniffi_ironstorage_apple_fn_method_mobilehomeoperation_refresh(uint64
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobilehomeoperation_refresh_if_stale(uint64_t ptr, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_CLONE_MOBILEKEYTRANSFER
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_CLONE_MOBILEKEYTRANSFER
|
||||
uint64_t uniffi_ironstorage_apple_fn_clone_mobilekeytransfer(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_FREE_MOBILEKEYTRANSFER
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_FREE_MOBILEKEYTRANSFER
|
||||
void uniffi_ironstorage_apple_fn_free_mobilekeytransfer(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEKEYTRANSFER_EXPORT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEKEYTRANSFER_EXPORT
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobilekeytransfer_export(uint64_t ptr, RustBuffer fingerprint, RustBuffer kind, RustBuffer passphrase, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEKEYTRANSFER_IMPORTER
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEKEYTRANSFER_IMPORTER
|
||||
uint64_t uniffi_ironstorage_apple_fn_method_mobilekeytransfer_importer(uint64_t ptr, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEKEYTRANSFER_KEYS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEKEYTRANSFER_KEYS
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobilekeytransfer_keys(uint64_t ptr, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_CLONE_MOBILEKEYTRANSFERIMPORT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_CLONE_MOBILEKEYTRANSFERIMPORT
|
||||
uint64_t uniffi_ironstorage_apple_fn_clone_mobilekeytransferimport(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_FREE_MOBILEKEYTRANSFERIMPORT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_FREE_MOBILEKEYTRANSFERIMPORT
|
||||
void uniffi_ironstorage_apple_fn_free_mobilekeytransferimport(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEKEYTRANSFERIMPORT_ADD_FRAME
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEKEYTRANSFERIMPORT_ADD_FRAME
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobilekeytransferimport_add_frame(uint64_t ptr, RustBuffer payload, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEKEYTRANSFERIMPORT_IMPORT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_METHOD_MOBILEKEYTRANSFERIMPORT_IMPORT
|
||||
RustBuffer uniffi_ironstorage_apple_fn_method_mobilekeytransferimport_import(uint64_t ptr, RustBuffer passphrase, int8_t make_default, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_CLONE_MOBILEONBOARDINGOPERATION
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_CLONE_MOBILEONBOARDINGOPERATION
|
||||
uint64_t uniffi_ironstorage_apple_fn_clone_mobileonboardingoperation(uint64_t handle, RustCallStatus *_Nonnull out_status
|
||||
@@ -458,6 +503,12 @@ uint64_t uniffi_ironstorage_apple_fn_func_mobile_authentication(RustCallStatus *
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_FUNC_MOBILE_HOME_OPERATION
|
||||
uint64_t uniffi_ironstorage_apple_fn_func_mobile_home_operation(RustCallStatus *_Nonnull out_status
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_FUNC_MOBILE_KEY_TRANSFER
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_FUNC_MOBILE_KEY_TRANSFER
|
||||
uint64_t uniffi_ironstorage_apple_fn_func_mobile_key_transfer(RustCallStatus *_Nonnull out_status
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_FN_FUNC_MOBILE_ONBOARDING_OPERATION
|
||||
@@ -767,6 +818,12 @@ uint16_t uniffi_ironstorage_apple_checksum_func_mobile_authentication(void
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_FUNC_MOBILE_HOME_OPERATION
|
||||
uint16_t uniffi_ironstorage_apple_checksum_func_mobile_home_operation(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_FUNC_MOBILE_KEY_TRANSFER
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_FUNC_MOBILE_KEY_TRANSFER
|
||||
uint16_t uniffi_ironstorage_apple_checksum_func_mobile_key_transfer(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_FUNC_MOBILE_ONBOARDING_OPERATION
|
||||
@@ -995,6 +1052,36 @@ uint16_t uniffi_ironstorage_apple_checksum_method_mobilehomeoperation_refresh(vo
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEHOMEOPERATION_REFRESH_IF_STALE
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobilehomeoperation_refresh_if_stale(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEKEYTRANSFER_EXPORT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEKEYTRANSFER_EXPORT
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobilekeytransfer_export(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEKEYTRANSFER_IMPORTER
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEKEYTRANSFER_IMPORTER
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobilekeytransfer_importer(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEKEYTRANSFER_KEYS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEKEYTRANSFER_KEYS
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobilekeytransfer_keys(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEKEYTRANSFERIMPORT_ADD_FRAME
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEKEYTRANSFERIMPORT_ADD_FRAME
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobilekeytransferimport_add_frame(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEKEYTRANSFERIMPORT_IMPORT
|
||||
#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEKEYTRANSFERIMPORT_IMPORT
|
||||
uint16_t uniffi_ironstorage_apple_checksum_method_mobilekeytransferimport_import(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_APPLE_CHECKSUM_METHOD_MOBILEONBOARDINGOPERATION_CANCEL
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
<false/>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>Unlock your GPG key for protected password operations.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Scan GPG key transfer QR codes that you choose to import.</string>
|
||||
<key>UILaunchScreen</key>
|
||||
<dict/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
import Vision
|
||||
import VisionKit
|
||||
|
||||
extension Notification.Name {
|
||||
static let ironStorageLocalStoreDidChange = Notification.Name(
|
||||
@@ -10,6 +13,9 @@ extension Notification.Name {
|
||||
static let ironStorageWatchSnapshotDidChange = Notification.Name(
|
||||
"de.rfc1437.ironstorage.watch-snapshot-did-change"
|
||||
)
|
||||
static let ironStorageKeyMaterialDidChange = Notification.Name(
|
||||
"de.rfc1437.ironstorage.key-material-did-change"
|
||||
)
|
||||
}
|
||||
|
||||
@main
|
||||
@@ -17,6 +23,20 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
var window: UIWindow?
|
||||
private var context: AppContext?
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(keyMaterialDidChange),
|
||||
name: .ironStorageKeyMaterialDidChange,
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
||||
@@ -33,6 +53,14 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||
context?.lockForBackground()
|
||||
}
|
||||
|
||||
@objc private func keyMaterialDidChange() {
|
||||
guard let window else { return }
|
||||
context?.lockForBackground()
|
||||
let context = AppContext()
|
||||
self.context = context
|
||||
window.rootViewController = context.makeRootController()
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@@ -676,6 +704,7 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
fileprivate let shellTab = MobileTab.preferences
|
||||
private var page: MobilePage
|
||||
private let authentication: MobileAuthentication?
|
||||
private let keyTransfer = try? mobileKeyTransfer()
|
||||
private var state: MobileAuthenticationState?
|
||||
private var preferenceTask: Task<Void, Never>?
|
||||
private var loadTask: Task<Void, Never>?
|
||||
@@ -718,21 +747,25 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
}
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
page.state == .ready ? 2 : 0
|
||||
page.state == .ready ? 3 : 0
|
||||
}
|
||||
|
||||
override func tableView(
|
||||
_ tableView: UITableView,
|
||||
numberOfRowsInSection section: Int
|
||||
) -> Int {
|
||||
section == 0 ? 1 : 2
|
||||
section == 0 ? 2 : (section == 1 ? 1 : 2)
|
||||
}
|
||||
|
||||
override func tableView(
|
||||
_ tableView: UITableView,
|
||||
titleForHeaderInSection section: Int
|
||||
) -> String? {
|
||||
section == 0 ? "Secure Unlock" : "Authentication Session"
|
||||
switch section {
|
||||
case 0: "GPG Key Transfer"
|
||||
case 1: "Secure Unlock"
|
||||
default: "Authentication Session"
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(
|
||||
@@ -740,6 +773,9 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
titleForFooterInSection section: Int
|
||||
) -> String? {
|
||||
if section == 0 {
|
||||
return "Scan or display ASCII-armored GPG keys. Private-key transfers require explicit confirmation and passphrase validation."
|
||||
}
|
||||
if section == 1 {
|
||||
return "When enabled, the GPG passphrase is device-only, requires a device passcode, and is invalidated when enrolled biometrics change."
|
||||
}
|
||||
return "Manual lock and inactivity expiry immediately revoke the shared Rust authentication lease."
|
||||
@@ -752,6 +788,16 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
|
||||
var content = cell.defaultContentConfiguration()
|
||||
if indexPath.section == 0 {
|
||||
let importing = indexPath.row == 0
|
||||
content.image = UIImage(systemName: importing ? "qrcode.viewfinder" : "qrcode")
|
||||
content.text = importing ? "Import GPG Key" : "Export GPG Key"
|
||||
content.secondaryText = importing
|
||||
? "Scan one or more transfer QR codes"
|
||||
: "Display public or private key armor"
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
cell.isUserInteractionEnabled = keyTransfer != nil
|
||||
cell.contentView.alpha = keyTransfer == nil ? 0.45 : 1
|
||||
} else if indexPath.section == 1 {
|
||||
content.image = UIImage(systemName: "faceid")
|
||||
content.text = "Biometric Unlock"
|
||||
content.secondaryText = state?.biometricUnlockEnabled == true
|
||||
@@ -788,7 +834,19 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
guard indexPath.section == 1, indexPath.row == 1, let authentication else { return }
|
||||
if indexPath.section == 0 {
|
||||
guard let keyTransfer else { return }
|
||||
if indexPath.row == 0 {
|
||||
requestKeyScanner(keyTransfer)
|
||||
} else {
|
||||
navigationController?.pushViewController(
|
||||
KeyExportListViewController(transfer: keyTransfer),
|
||||
animated: true
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
guard indexPath.section == 2, indexPath.row == 1, let authentication else { return }
|
||||
do {
|
||||
try authentication.manualLock()
|
||||
refreshState()
|
||||
@@ -845,6 +903,67 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
navigationController?.pushViewController(TokenUpdateViewController(), animated: true)
|
||||
}
|
||||
|
||||
private func requestKeyScanner(_ transfer: MobileKeyTransfer) {
|
||||
guard DataScannerViewController.isSupported else {
|
||||
presentMessage(
|
||||
title: "Key Scanning Is Unavailable",
|
||||
detail: "This device does not support live QR scanning."
|
||||
)
|
||||
return
|
||||
}
|
||||
switch AVCaptureDevice.authorizationStatus(for: .video) {
|
||||
case .authorized:
|
||||
showKeyScanner(transfer)
|
||||
case .notDetermined:
|
||||
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
|
||||
Task { @MainActor in
|
||||
guard let self else { return }
|
||||
if granted {
|
||||
self.showKeyScanner(transfer)
|
||||
} else {
|
||||
self.showCameraDenied()
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
showCameraDenied()
|
||||
}
|
||||
}
|
||||
|
||||
private func showKeyScanner(_ transfer: MobileKeyTransfer) {
|
||||
guard DataScannerViewController.isAvailable else {
|
||||
presentMessage(
|
||||
title: "Camera Is Unavailable",
|
||||
detail: "Close other camera apps and try again."
|
||||
)
|
||||
return
|
||||
}
|
||||
navigationController?.pushViewController(
|
||||
KeyImportScannerViewController(transfer: transfer),
|
||||
animated: true
|
||||
)
|
||||
}
|
||||
|
||||
private func showCameraDenied() {
|
||||
let alert = UIAlertController(
|
||||
title: "Camera Access Is Off",
|
||||
message: "Allow camera access in Settings to scan GPG key QR codes.",
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
||||
alert.addAction(UIAlertAction(title: "Open Settings", style: .default) { _ in
|
||||
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
|
||||
UIApplication.shared.open(url)
|
||||
})
|
||||
present(alert, animated: true)
|
||||
}
|
||||
|
||||
private func presentMessage(title: String, detail: String) {
|
||||
let alert = UIAlertController(title: title, message: detail, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default))
|
||||
present(alert, animated: true)
|
||||
}
|
||||
|
||||
@objc private func authenticationDidChange() {
|
||||
refreshState()
|
||||
}
|
||||
@@ -893,6 +1012,569 @@ private final class PreferencesViewController: UITableViewController, MobileTabR
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class KeyImportScannerViewController: UIViewController,
|
||||
DataScannerViewControllerDelegate
|
||||
{
|
||||
private let importer: MobileKeyTransferImport
|
||||
private let scanner = DataScannerViewController(
|
||||
recognizedDataTypes: [.barcode(symbologies: [.qr])],
|
||||
qualityLevel: .balanced,
|
||||
recognizesMultipleItems: true,
|
||||
isHighFrameRateTrackingEnabled: false,
|
||||
isPinchToZoomEnabled: true,
|
||||
isGuidanceEnabled: true,
|
||||
isHighlightingEnabled: true
|
||||
)
|
||||
private let progressLabel = UILabel()
|
||||
private let shield = UIVisualEffectView(effect: UIBlurEffect(style: .systemChromeMaterial))
|
||||
private var completing = false
|
||||
|
||||
init(transfer: MobileKeyTransfer) {
|
||||
importer = transfer.importer()
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
title = "Import GPG Key"
|
||||
navigationItem.largeTitleDisplayMode = .never
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) is not supported")
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = .systemBackground
|
||||
scanner.delegate = self
|
||||
addChild(scanner)
|
||||
scanner.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(scanner.view)
|
||||
scanner.didMove(toParent: self)
|
||||
|
||||
let material = UIBlurEffect(style: .systemMaterial)
|
||||
let status = UIVisualEffectView(effect: material)
|
||||
status.layer.cornerRadius = 12
|
||||
status.clipsToBounds = true
|
||||
status.translatesAutoresizingMaskIntoConstraints = false
|
||||
progressLabel.text = "Point the camera at a key-transfer QR code"
|
||||
progressLabel.font = .preferredFont(forTextStyle: .callout)
|
||||
progressLabel.adjustsFontForContentSizeCategory = true
|
||||
progressLabel.numberOfLines = 0
|
||||
progressLabel.textAlignment = .center
|
||||
progressLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
status.contentView.addSubview(progressLabel)
|
||||
view.addSubview(status)
|
||||
|
||||
let shieldLabel = UILabel()
|
||||
shieldLabel.text = "Camera hidden while IronStorage is inactive or the screen is captured"
|
||||
shieldLabel.font = .preferredFont(forTextStyle: .headline)
|
||||
shieldLabel.adjustsFontForContentSizeCategory = true
|
||||
shieldLabel.numberOfLines = 0
|
||||
shieldLabel.textAlignment = .center
|
||||
shieldLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
shield.contentView.addSubview(shieldLabel)
|
||||
shield.translatesAutoresizingMaskIntoConstraints = false
|
||||
shield.isHidden = true
|
||||
shield.accessibilityViewIsModal = true
|
||||
view.addSubview(shield)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
scanner.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
scanner.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
scanner.view.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
scanner.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
status.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20),
|
||||
status.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20),
|
||||
status.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20),
|
||||
progressLabel.leadingAnchor.constraint(equalTo: status.contentView.leadingAnchor, constant: 16),
|
||||
progressLabel.trailingAnchor.constraint(equalTo: status.contentView.trailingAnchor, constant: -16),
|
||||
progressLabel.topAnchor.constraint(equalTo: status.contentView.topAnchor, constant: 12),
|
||||
progressLabel.bottomAnchor.constraint(equalTo: status.contentView.bottomAnchor, constant: -12),
|
||||
shield.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
shield.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
shield.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
shield.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
shieldLabel.leadingAnchor.constraint(equalTo: shield.contentView.leadingAnchor, constant: 32),
|
||||
shieldLabel.trailingAnchor.constraint(equalTo: shield.contentView.trailingAnchor, constant: -32),
|
||||
shieldLabel.centerYAnchor.constraint(equalTo: shield.contentView.centerYAnchor),
|
||||
])
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(appWillResignActive),
|
||||
name: UIApplication.willResignActiveNotification,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(appDidBecomeActive),
|
||||
name: UIApplication.didBecomeActiveNotification,
|
||||
object: nil
|
||||
)
|
||||
registerForTraitChanges([UITraitSceneCaptureState.self]) {
|
||||
(controller: KeyImportScannerViewController, _: UITraitCollection) in
|
||||
controller.updateCaptureShield()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
if !completing {
|
||||
do {
|
||||
try scanner.startScanning()
|
||||
} catch {
|
||||
presentFailure(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
scanner.stopScanning()
|
||||
super.viewWillDisappear(animated)
|
||||
}
|
||||
|
||||
func dataScanner(
|
||||
_ dataScanner: DataScannerViewController,
|
||||
didAdd addedItems: [RecognizedItem],
|
||||
allItems: [RecognizedItem]
|
||||
) {
|
||||
guard !completing else { return }
|
||||
for item in addedItems {
|
||||
guard case let .barcode(barcode) = item, let payload = barcode.payloadStringValue else {
|
||||
continue
|
||||
}
|
||||
do {
|
||||
let progress = try importer.addFrame(payload: payload)
|
||||
if progress.duplicate {
|
||||
progressLabel.text = "Already scanned — show the next QR code"
|
||||
} else {
|
||||
progressLabel.text = progress.key == nil
|
||||
? "Scanned \(progress.received) of \(progress.total)"
|
||||
: "Key transfer complete"
|
||||
}
|
||||
UIAccessibility.post(notification: .announcement, argument: progressLabel.text)
|
||||
if let key = progress.key {
|
||||
completing = true
|
||||
scanner.stopScanning()
|
||||
confirmImport(key)
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
progressLabel.text = "That QR code could not be added"
|
||||
UIAccessibility.post(notification: .announcement, argument: progressLabel.text)
|
||||
presentFailure(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dataScanner(
|
||||
_ dataScanner: DataScannerViewController,
|
||||
becameUnavailableWithError error: DataScannerViewController.ScanningUnavailable
|
||||
) {
|
||||
presentFailure("Live scanning became unavailable. Try again when the camera is free.")
|
||||
}
|
||||
|
||||
@objc private func appWillResignActive() {
|
||||
shield.isHidden = false
|
||||
scanner.stopScanning()
|
||||
}
|
||||
|
||||
@objc private func appDidBecomeActive() {
|
||||
updateCaptureShield()
|
||||
}
|
||||
|
||||
private func updateCaptureShield() {
|
||||
let captured = traitCollection.sceneCaptureState == .active
|
||||
shield.isHidden = !captured
|
||||
if captured || completing || view.window == nil {
|
||||
scanner.stopScanning()
|
||||
} else {
|
||||
try? scanner.startScanning()
|
||||
}
|
||||
}
|
||||
|
||||
private func confirmImport(_ key: MobileKeyTransferKey) {
|
||||
let privateKey = key.kind == .private
|
||||
let warning = privateKey
|
||||
? "Import this private key only if you trust the device that displayed it."
|
||||
: "Import this public key?"
|
||||
let alert = UIAlertController(
|
||||
title: privateKey ? "Import Private GPG Key?" : "Import Public GPG Key?",
|
||||
message: "\(warning)\n\n\(key.title)\n\(key.detail)",
|
||||
preferredStyle: .alert
|
||||
)
|
||||
if privateKey && key.requiresPassphrase {
|
||||
alert.addTextField { field in
|
||||
field.placeholder = "GPG key passphrase"
|
||||
field.isSecureTextEntry = true
|
||||
field.textContentType = .password
|
||||
field.clearButtonMode = .whileEditing
|
||||
}
|
||||
}
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { [weak self] _ in
|
||||
self?.navigationController?.popViewController(animated: true)
|
||||
})
|
||||
alert.addAction(UIAlertAction(title: "Import", style: .default) { [weak self, weak alert] _ in
|
||||
self?.finishImport(passphrase: alert?.textFields?.first?.text, makeDefault: false)
|
||||
})
|
||||
if privateKey {
|
||||
alert.addAction(UIAlertAction(title: "Import as Default", style: .default) {
|
||||
[weak self, weak alert] _ in
|
||||
self?.finishImport(passphrase: alert?.textFields?.first?.text, makeDefault: true)
|
||||
})
|
||||
}
|
||||
present(alert, animated: true)
|
||||
}
|
||||
|
||||
private func finishImport(passphrase: String?, makeDefault: Bool) {
|
||||
do {
|
||||
let outcome = try importer.import(passphrase: passphrase, makeDefault: makeDefault)
|
||||
let alert = UIAlertController(
|
||||
title: outcome.title,
|
||||
message: outcome.detail,
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: "Done", style: .default) { [weak self] _ in
|
||||
guard self != nil else { return }
|
||||
NotificationCenter.default.post(name: .ironStorageKeyMaterialDidChange, object: nil)
|
||||
})
|
||||
present(alert, animated: true)
|
||||
} catch {
|
||||
completing = false
|
||||
presentFailure(error.localizedDescription) { [weak self] in
|
||||
guard let self else { return }
|
||||
try? scanner.startScanning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func presentFailure(_ detail: String, completion: (() -> Void)? = nil) {
|
||||
guard presentedViewController == nil else { return }
|
||||
let alert = UIAlertController(
|
||||
title: "GPG Key Transfer Failed",
|
||||
message: detail,
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in completion?() })
|
||||
present(alert, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class KeyExportListViewController: UITableViewController {
|
||||
private let transfer: MobileKeyTransfer
|
||||
private let keys: [MobileKeyTransferKey]
|
||||
|
||||
init(transfer: MobileKeyTransfer) {
|
||||
self.transfer = transfer
|
||||
keys = transfer.keys()
|
||||
super.init(style: .insetGrouped)
|
||||
title = "Export GPG Key"
|
||||
navigationItem.largeTitleDisplayMode = .never
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) is not supported")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
if keys.isEmpty {
|
||||
var configuration = UIContentUnavailableConfiguration.empty()
|
||||
configuration.image = UIImage(systemName: "key.slash")
|
||||
configuration.text = "No GPG Keys"
|
||||
configuration.secondaryText = "Import a key before exporting one."
|
||||
contentUnavailableConfiguration = configuration
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
keys.count
|
||||
}
|
||||
|
||||
override func tableView(
|
||||
_ tableView: UITableView,
|
||||
cellForRowAt indexPath: IndexPath
|
||||
) -> UITableViewCell {
|
||||
let key = keys[indexPath.row]
|
||||
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
|
||||
var content = cell.defaultContentConfiguration()
|
||||
content.image = UIImage(systemName: key.kind == .private ? "key.fill" : "key")
|
||||
content.text = key.title
|
||||
content.secondaryText = key.detail
|
||||
content.secondaryTextProperties.numberOfLines = 0
|
||||
cell.contentConfiguration = content
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
let key = keys[indexPath.row]
|
||||
let sheet = UIAlertController(title: key.title, message: key.detail, preferredStyle: .actionSheet)
|
||||
sheet.addAction(UIAlertAction(title: "Export Public Key", style: .default) {
|
||||
[weak self] _ in self?.export(key, kind: .public, passphrase: nil)
|
||||
})
|
||||
if key.kind == .private {
|
||||
sheet.addAction(UIAlertAction(title: "Export Private Key…", style: .destructive) {
|
||||
[weak self] _ in self?.confirmPrivateExport(key)
|
||||
})
|
||||
}
|
||||
sheet.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
||||
if let popover = sheet.popoverPresentationController {
|
||||
popover.sourceView = tableView.cellForRow(at: indexPath)
|
||||
popover.sourceRect = tableView.cellForRow(at: indexPath)?.bounds ?? .zero
|
||||
}
|
||||
present(sheet, animated: true)
|
||||
}
|
||||
|
||||
private func confirmPrivateExport(_ key: MobileKeyTransferKey) {
|
||||
let alert = UIAlertController(
|
||||
title: "Display Private GPG Key?",
|
||||
message: "Anyone who scans these QR codes can use this private key.\n\n\(key.title)\n\(key.detail)",
|
||||
preferredStyle: .alert
|
||||
)
|
||||
if key.requiresPassphrase {
|
||||
alert.addTextField { field in
|
||||
field.placeholder = "GPG key passphrase"
|
||||
field.isSecureTextEntry = true
|
||||
field.textContentType = .password
|
||||
field.clearButtonMode = .whileEditing
|
||||
}
|
||||
}
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
||||
alert.addAction(UIAlertAction(title: "Display", style: .destructive) {
|
||||
[weak self, weak alert] _ in
|
||||
self?.export(key, kind: .private, passphrase: alert?.textFields?.first?.text)
|
||||
})
|
||||
present(alert, animated: true)
|
||||
}
|
||||
|
||||
private func export(
|
||||
_ key: MobileKeyTransferKey,
|
||||
kind: MobileKeyTransferKind,
|
||||
passphrase: String?
|
||||
) {
|
||||
do {
|
||||
let exported = try transfer.export(
|
||||
fingerprint: key.fingerprint,
|
||||
kind: kind,
|
||||
passphrase: passphrase
|
||||
)
|
||||
navigationController?.pushViewController(
|
||||
KeyQrExportViewController(exported: exported),
|
||||
animated: true
|
||||
)
|
||||
} catch {
|
||||
let alert = UIAlertController(
|
||||
title: "GPG Key Could Not Be Exported",
|
||||
message: error.localizedDescription,
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default))
|
||||
present(alert, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class KeyQrExportViewController: UIViewController {
|
||||
private let exported: MobileKeyTransferExport
|
||||
private let qrView = KeyQrView()
|
||||
private let pageControl = UIPageControl()
|
||||
private let progressLabel = UILabel()
|
||||
private let shield = UIVisualEffectView(effect: UIBlurEffect(style: .systemChromeMaterial))
|
||||
private var index = 0
|
||||
|
||||
init(exported: MobileKeyTransferExport) {
|
||||
self.exported = exported
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
title = exported.key.kind == .private ? "Private GPG Key" : "Public GPG Key"
|
||||
navigationItem.largeTitleDisplayMode = .never
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) is not supported")
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = .systemBackground
|
||||
qrView.translatesAutoresizingMaskIntoConstraints = false
|
||||
qrView.layer.cornerRadius = 16
|
||||
qrView.clipsToBounds = true
|
||||
|
||||
let identity = UILabel()
|
||||
identity.text = exported.key.title
|
||||
identity.font = .preferredFont(forTextStyle: .headline)
|
||||
identity.adjustsFontForContentSizeCategory = true
|
||||
identity.numberOfLines = 0
|
||||
identity.textAlignment = .center
|
||||
|
||||
let fingerprint = UILabel()
|
||||
fingerprint.text = exported.key.detail
|
||||
fingerprint.font = .preferredFont(forTextStyle: .footnote)
|
||||
fingerprint.adjustsFontForContentSizeCategory = true
|
||||
fingerprint.numberOfLines = 0
|
||||
fingerprint.textAlignment = .center
|
||||
fingerprint.textColor = .secondaryLabel
|
||||
|
||||
progressLabel.font = .preferredFont(forTextStyle: .callout)
|
||||
progressLabel.adjustsFontForContentSizeCategory = true
|
||||
progressLabel.textAlignment = .center
|
||||
pageControl.numberOfPages = exported.frames.count
|
||||
pageControl.addTarget(self, action: #selector(pageChanged), for: .valueChanged)
|
||||
pageControl.isHidden = exported.frames.count == 1
|
||||
|
||||
let stack = UIStackView(arrangedSubviews: [identity, fingerprint, qrView, progressLabel, pageControl])
|
||||
stack.axis = .vertical
|
||||
stack.alignment = .fill
|
||||
stack.spacing = 14
|
||||
stack.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(stack)
|
||||
|
||||
let shieldLabel = UILabel()
|
||||
shieldLabel.text = "Private key hidden while IronStorage is inactive or the screen is captured"
|
||||
shieldLabel.font = .preferredFont(forTextStyle: .headline)
|
||||
shieldLabel.adjustsFontForContentSizeCategory = true
|
||||
shieldLabel.numberOfLines = 0
|
||||
shieldLabel.textAlignment = .center
|
||||
shieldLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
shield.contentView.addSubview(shieldLabel)
|
||||
shield.translatesAutoresizingMaskIntoConstraints = false
|
||||
shield.isHidden = true
|
||||
shield.accessibilityViewIsModal = true
|
||||
view.addSubview(shield)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
stack.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 24),
|
||||
stack.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -24),
|
||||
stack.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor),
|
||||
qrView.widthAnchor.constraint(equalTo: qrView.heightAnchor),
|
||||
qrView.widthAnchor.constraint(lessThanOrEqualTo: view.safeAreaLayoutGuide.widthAnchor, constant: -48),
|
||||
shield.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
shield.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
shield.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
shield.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
shieldLabel.leadingAnchor.constraint(equalTo: shield.contentView.leadingAnchor, constant: 32),
|
||||
shieldLabel.trailingAnchor.constraint(equalTo: shield.contentView.trailingAnchor, constant: -32),
|
||||
shieldLabel.centerYAnchor.constraint(equalTo: shield.contentView.centerYAnchor),
|
||||
])
|
||||
|
||||
let left = UISwipeGestureRecognizer(target: self, action: #selector(swipedLeft))
|
||||
left.direction = .left
|
||||
let right = UISwipeGestureRecognizer(target: self, action: #selector(swipedRight))
|
||||
right.direction = .right
|
||||
qrView.addGestureRecognizer(left)
|
||||
qrView.addGestureRecognizer(right)
|
||||
qrView.isUserInteractionEnabled = true
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(appWillResignActive),
|
||||
name: UIApplication.willResignActiveNotification,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(appDidBecomeActive),
|
||||
name: UIApplication.didBecomeActiveNotification,
|
||||
object: nil
|
||||
)
|
||||
registerForTraitChanges([UITraitSceneCaptureState.self]) {
|
||||
(controller: KeyQrExportViewController, _: UITraitCollection) in
|
||||
controller.updateCaptureShield()
|
||||
}
|
||||
showFrame(0)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
updateCaptureShield()
|
||||
}
|
||||
|
||||
@objc private func pageChanged() {
|
||||
showFrame(pageControl.currentPage)
|
||||
}
|
||||
|
||||
@objc private func swipedLeft() {
|
||||
showFrame(min(index + 1, exported.frames.count - 1))
|
||||
}
|
||||
|
||||
@objc private func swipedRight() {
|
||||
showFrame(max(index - 1, 0))
|
||||
}
|
||||
|
||||
private func showFrame(_ index: Int) {
|
||||
self.index = index
|
||||
pageControl.currentPage = index
|
||||
qrView.transferFrame = exported.frames[index]
|
||||
progressLabel.text = exported.frames.count == 1
|
||||
? "Scan this QR code"
|
||||
: "QR code \(index + 1) of \(exported.frames.count)"
|
||||
qrView.accessibilityLabel = progressLabel.text
|
||||
UIAccessibility.post(notification: .pageScrolled, argument: progressLabel.text)
|
||||
}
|
||||
|
||||
@objc private func appWillResignActive() {
|
||||
if exported.key.kind == .private {
|
||||
shield.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func appDidBecomeActive() {
|
||||
updateCaptureShield()
|
||||
}
|
||||
|
||||
private func updateCaptureShield() {
|
||||
shield.isHidden = exported.key.kind != .private || traitCollection.sceneCaptureState != .active
|
||||
}
|
||||
}
|
||||
|
||||
private final class KeyQrView: UIView {
|
||||
var transferFrame: MobileKeyTransferFrame? {
|
||||
didSet { setNeedsDisplay() }
|
||||
}
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
UIColor.white.setFill()
|
||||
UIRectFill(rect)
|
||||
guard let transferFrame, transferFrame.width > 0 else { return }
|
||||
let width = Int(transferFrame.width)
|
||||
let padded = width + 8
|
||||
let scale = floor(min(rect.width, rect.height) / CGFloat(padded))
|
||||
guard scale >= 1 else { return }
|
||||
let symbolSize = CGFloat(padded) * scale
|
||||
let origin = CGPoint(
|
||||
x: rect.midX - symbolSize / 2 + 4 * scale,
|
||||
y: rect.midY - symbolSize / 2 + 4 * scale
|
||||
)
|
||||
guard let context = UIGraphicsGetCurrentContext() else { return }
|
||||
context.setFillColor(UIColor.black.cgColor)
|
||||
context.interpolationQuality = .none
|
||||
for y in 0..<width {
|
||||
for x in 0..<width where transferFrame.modules[y * width + x] != 0 {
|
||||
context.fill(CGRect(
|
||||
x: origin.x + CGFloat(x) * scale,
|
||||
y: origin.y + CGFloat(y) * scale,
|
||||
width: scale,
|
||||
height: scale
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class TotpListViewController: UITableViewController, MobileTabRoot {
|
||||
fileprivate let shellTab = MobileTab.totp
|
||||
|
||||
@@ -23,6 +23,7 @@ targets:
|
||||
CFBundleDisplayName: IronStorage
|
||||
ITSAppUsesNonExemptEncryption: false
|
||||
NSFaceIDUsageDescription: Unlock your GPG key for protected password operations.
|
||||
NSCameraUsageDescription: Scan GPG key transfer QR codes that you choose to import.
|
||||
UILaunchScreen: {}
|
||||
UISupportedInterfaceOrientations:
|
||||
- UIInterfaceOrientationPortrait
|
||||
|
||||
Reference in New Issue
Block a user