CryptoPassphrase  Check-in [5d68f3d40a]

Overview
Comment:iOS: Make more use of native Swift types
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 5d68f3d40a8d51848be36846102c8eb46d47a0fcf8f64685ead4a2fa1b661561
User & Date: js on 2019-06-19 23:15:26
Other Links: manifest | tags
Context
2019-12-28
22:46
Adjust to ObjFW changes check-in: 31d77b5098 user: js tags: trunk
2019-06-19
23:15
iOS: Make more use of native Swift types check-in: 5d68f3d40a user: js tags: trunk
23:11
Adjust to ObjFW changes check-in: 1b57a79561 user: js tags: trunk
Changes

Modified iOS/AddSiteController.swift from [209b94f8a1] to [7294aad127].

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
                                      preferredStyle: .alert)
        alert.addAction(
            UIAlertAction(title: "OK", style: .default, handler: nil))
        controller.present(alert, animated: true, completion: nil)
    }

    @IBAction func done(_ sender: Any) {
        guard let name = nameField?.text?.ofObject else { return }
        guard let lengthString = lengthField?.text?.ofObject else { return }

        guard name.length > 0 else {
            showAlert(controller: self, title: "Name missing",
                      message: "Please enter a name.")
            return
        }

        var lengthValid = true
        var length: size_t = 0
        OFException.try({
            length = lengthString.decimalValue

            if length < 3 || length > 64 {
                lengthValid = false
            }
        }, catch: { (OFException) in
            lengthValid = false
        })

        guard lengthValid else {
            showAlert(controller: self, title: "Invalid length",
                      message: "Please enter a number between 3 and 64.")
            return
        }

        guard let siteStorage = mainViewController?.siteStorage else { return }

        guard !siteStorage.hasSite(name) else {
            showAlert(controller: self, title: "Site Already Exists",
                      message: "Please pick a name that does not exist yet.")
            return
        }

        let keyFile = self.keyFile?.ofObject
        siteStorage.setSite(name, length: length,
                            legacy: legacySwitch?.isOn ?? false,
                            keyFile: keyFile)
        mainViewController?.reset()
        navigationController?.popViewController(animated: true)
    }

    @IBAction func cancel(_ sender: Any) {
        navigationController?.popViewController(animated: true)
    }







|
|

|





<
<
<
<
|
<
<
<
<
<
<
<
<













<

|
|







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73




74








75
76
77
78
79
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95
96
97
                                      preferredStyle: .alert)
        alert.addAction(
            UIAlertAction(title: "OK", style: .default, handler: nil))
        controller.present(alert, animated: true, completion: nil)
    }

    @IBAction func done(_ sender: Any) {
        guard let name = nameField?.text else { return }
        guard let lengthString = lengthField?.text else { return }

        guard name.count > 0 else {
            showAlert(controller: self, title: "Name missing",
                      message: "Please enter a name.")
            return
        }





        guard let length = UInt(lengthString), length >= 3, length <= 64 else {








            showAlert(controller: self, title: "Invalid length",
                      message: "Please enter a number between 3 and 64.")
            return
        }

        guard let siteStorage = mainViewController?.siteStorage else { return }

        guard !siteStorage.hasSite(name) else {
            showAlert(controller: self, title: "Site Already Exists",
                      message: "Please pick a name that does not exist yet.")
            return
        }


        siteStorage.setSite(name, length: length,
                            isLegacy: legacySwitch?.isOn ?? false,
                            keyFile: self.keyFile)
        mainViewController?.reset()
        navigationController?.popViewController(animated: true)
    }

    @IBAction func cancel(_ sender: Any) {
        navigationController?.popViewController(animated: true)
    }

Modified iOS/MainViewController.swift from [9c2bb5d1c2] to [2595a319b1].

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 */

import UIKit
import ObjFW

class MainViewController: UIViewController, UISearchBarDelegate,
                          UITableViewDelegate, UITableViewDataSource {
    public var sites = OFArray<OFString>()
    public var siteStorage = SiteStorage()
    @IBOutlet var searchBar: UISearchBar?
    @IBOutlet var tableView: UITableView?

    override func viewDidLoad() {
        super.viewDidLoad()








|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 */

import UIKit
import ObjFW

class MainViewController: UIViewController, UISearchBarDelegate,
                          UITableViewDelegate, UITableViewDataSource {
    public var sites: [String] = []
    public var siteStorage = SiteStorage()
    @IBOutlet var searchBar: UISearchBar?
    @IBOutlet var tableView: UITableView?

    override func viewDidLoad() {
        super.viewDidLoad()

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
        return sites.count
    }

    func tableView(_ tableView: UITableView,
                   cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "site") ??
            UITableViewCell(style: .default, reuseIdentifier: "site")
        cell.textLabel?.text = sites[indexPath.row].nsObject
        return cell
    }

    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        sites = siteStorage.sites(withFilter: searchBar.text?.ofObject)
        tableView?.reloadData()
    }

    func tableView(_ tableView: UITableView,
                   didSelectRowAt indexPath: IndexPath) {
        self.performSegue(withIdentifier: "showDetails", sender: self)
    }







|




|







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
        return sites.count
    }

    func tableView(_ tableView: UITableView,
                   cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "site") ??
            UITableViewCell(style: .default, reuseIdentifier: "site")
        cell.textLabel?.text = sites[indexPath.row]
        return cell
    }

    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        sites = siteStorage.sites(withFilter: searchBar.text)
        tableView?.reloadData()
    }

    func tableView(_ tableView: UITableView,
                   didSelectRowAt indexPath: IndexPath) {
        self.performSegue(withIdentifier: "showDetails", sender: self)
    }

Modified iOS/ShowDetailsController.swift from [028af44e3c] to [3b6682d3d0].

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    @IBOutlet var nameField: UITextField?
    @IBOutlet var lengthField: UITextField?
    @IBOutlet var legacySwitch: UISwitch?
    @IBOutlet var keyFileField: UITextField?
    @IBOutlet var passphraseField: UITextField?
    public var mainViewController: MainViewController?

    private var name: OFString = "".ofObject
    private var length: size_t = 0
    private var isLegacy: Bool = false
    private var keyFile: OFString? = nil

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        guard let mainViewController = self.mainViewController else { return }
        guard let tableView = mainViewController.tableView else { return }
        let siteStorage = mainViewController.siteStorage
        guard let indexPath = tableView.indexPathForSelectedRow else { return }

        name = mainViewController.sites[indexPath.row]
        length = siteStorage.length(forSite: name)
        isLegacy = siteStorage.isLegacy(site: name)
        keyFile = siteStorage.keyFile(forSite: name)

        nameField?.text = name.nsObject
        lengthField?.text = "\(length)"
        legacySwitch?.isOn = isLegacy
        keyFileField?.text = keyFile?.nsObject

        tableView.deselectRow(at: indexPath, animated: true)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return false







|
|

|














|


|







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    @IBOutlet var nameField: UITextField?
    @IBOutlet var lengthField: UITextField?
    @IBOutlet var legacySwitch: UISwitch?
    @IBOutlet var keyFileField: UITextField?
    @IBOutlet var passphraseField: UITextField?
    public var mainViewController: MainViewController?

    private var name: String = ""
    private var length: UInt = 0
    private var isLegacy: Bool = false
    private var keyFile: String? = nil

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        guard let mainViewController = self.mainViewController else { return }
        guard let tableView = mainViewController.tableView else { return }
        let siteStorage = mainViewController.siteStorage
        guard let indexPath = tableView.indexPathForSelectedRow else { return }

        name = mainViewController.sites[indexPath.row]
        length = siteStorage.length(forSite: name)
        isLegacy = siteStorage.isLegacy(site: name)
        keyFile = siteStorage.keyFile(forSite: name)

        nameField?.text = name
        lengthField?.text = "\(length)"
        legacySwitch?.isOn = isLegacy
        keyFileField?.text = keyFile

        tableView.deselectRow(at: indexPath, animated: true)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return false
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
            }
        }
    }

    private func generateWithCallback(_ block: (_: NSMutableString) -> ()) {
        let generator: PasswordGenerator = isLegacy ?
            LegacyPasswordGenerator() : NewPasswordGenerator()
        generator.site = name
        generator.length = length

        if let keyFile = keyFile {
            guard let documentDirectory = NSSearchPathForDirectoriesInDomains(
                .documentDirectory, .userDomainMask, true).first
            else {
                print("Could not get key files: No documents directory")
                return
            }


            let keyFilePath = documentDirectory.ofObject.appending(keyFile)
            generator.keyFile = OFMutableData(contentsOfFile: keyFilePath)
        }

        let passphraseText = (passphraseField?.text ?? "") as NSString
        let passphrase = of_strdup(passphraseText.utf8String!)!
        generator.passphrase = UnsafePointer<CChar>(passphrase)








|
|









>
|







130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
            }
        }
    }

    private func generateWithCallback(_ block: (_: NSMutableString) -> ()) {
        let generator: PasswordGenerator = isLegacy ?
            LegacyPasswordGenerator() : NewPasswordGenerator()
        generator.site = name.ofObject
        generator.length = size_t(length)

        if let keyFile = keyFile {
            guard let documentDirectory = NSSearchPathForDirectoriesInDomains(
                .documentDirectory, .userDomainMask, true).first
            else {
                print("Could not get key files: No documents directory")
                return
            }

            let keyFilePath =
                documentDirectory.ofObject.appending(keyFile.ofObject)
            generator.keyFile = OFMutableData(contentsOfFile: keyFilePath)
        }

        let passphraseText = (passphraseField?.text ?? "") as NSString
        let passphrase = of_strdup(passphraseText.utf8String!)!
        generator.passphrase = UnsafePointer<CChar>(passphrase)

Modified iOS/SiteStorage.swift from [0a36bee6de] to [0072dc8d4f].

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

49
50



51
52
53
54
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91


92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
130
131


132
133
134
135
 * POSSIBILITY OF SUCH DAMAGE.
 */

import ObjFW
import ObjFWBridge

class SiteStorage: OFObject {
    private typealias Storage =
        OFMutableDictionary<OFString, OFDictionary<OFNumber, AnyObject>>

    private static let lengthField = OFNumber(uInt8: 0)
    private static let legacyField = OFNumber(uInt8: 1)
    private static let keyFileField = OFNumber(uInt8: 2)

    private var path: OFString
    private var storage: Storage
    private var sites: OFArray<OFString>

    override init() {
        let fileManager = OFFileManager.default
        let userDataPath = OFSystemInfo.userDataPath!

        if !fileManager.directoryExists(atPath: userDataPath) {
            fileManager.createDirectory(atPath: userDataPath)
        }

        let path = userDataPath.appendingPathComponent(
            OFString(utf8String: "sites.msgpack"))
        var storage: Storage? = nil

        OFException.try({
            storage = OFData(contentsOfFile: path).messagePackValue as? Storage



        }, catch: { (OFException) in
            storage = OFMutableDictionary()
        })

        self.path = path
        self.storage = storage!
        self.sites = self.storage.allKeys.sorted
    }

    func sites(withFilter filter: OFString?) -> OFArray<OFString> {
        // FIXME: We need case folding here, but there is no method for it yet.

        let filter = filter?.lowercase

        return storage.allKeys.sorted.filteredArray({
            (name: Any, index: size_t) -> Bool in
            if filter == nil {
                return true
            }

            let name = name as! OFString
            return name.lowercase.contains(filter!)
        })
    }

    func hasSite(_ name: OFString) -> Bool {
        return (storage[name] != nil)
    }

    func length(forSite name: OFString) -> size_t {
        guard let site = storage[name] else {
            OFInvalidArgumentException().throw()
        }

        return (site[SiteStorage.lengthField] as! OFNumber).sizeValue
    }

    func isLegacy(site name: OFString) -> Bool {
        guard let site = storage[name] else {
            OFInvalidArgumentException().throw()
        }



        return (site[SiteStorage.legacyField] as! OFNumber).boolValue
    }

    func keyFile(forSite name: OFString) -> OFString? {
        guard let site = storage[name] else {
            OFInvalidArgumentException().throw()
        }

        let keyFile = site[SiteStorage.keyFileField]
        if keyFile is OFNull {
            return nil
        }

        return keyFile as? OFString
    }

    func setSite(_ name: OFString, length: size_t, legacy: Bool,
                 keyFile: OFString?) {
        let siteDictionary = OFMutableDictionary<OFNumber, AnyObject>()

        siteDictionary.setObject(OFNumber(size: length),
                                 forKey: SiteStorage.lengthField)
        siteDictionary.setObject(OFNumber(bool: legacy),
                                 forKey: SiteStorage.legacyField)
        if keyFile != nil {
            siteDictionary.setObject(keyFile!, forKey: SiteStorage.keyFileField)
        }

        siteDictionary.makeImmutable()
        storage.setObject(siteDictionary, forKey: name)


        self.update()
    }

    func removeSite(_ name: OFString) {
        self.storage.removeObject(forKey: name)
        self.update()
    }

    private func update() {


        storage.messagePackRepresentation.write(toFile: path)
        sites = storage.allKeys.sorted
    }
}







<
<
<
|
|
|


|
|











|
>

|
>
>
>

|




|


|
<
>
|
|
<
<
<
<
<
|
<
|



|



|




|

<
<
<
<
<

>
>
|


|
|
<
|
<
|
|



|


|
|
<
<
|
|
<
|
<
<
<
|
|
<

>



|
|




>
>
|
|


20
21
22
23
24
25
26



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

62
63
64





65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80





81
82
83
84
85
86
87
88

89

90
91
92
93
94
95
96
97
98
99


100
101

102



103
104

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
 * POSSIBILITY OF SUCH DAMAGE.
 */

import ObjFW
import ObjFWBridge

class SiteStorage: OFObject {



    private static let lengthField = NSNumber(value: 0)
    private static let legacyField = NSNumber(value: 1)
    private static let keyFileField = NSNumber(value: 2)

    private var path: OFString
    private var storage: [String: [NSNumber: AnyObject]]
    private var sites: [String]

    override init() {
        let fileManager = OFFileManager.default
        let userDataPath = OFSystemInfo.userDataPath!

        if !fileManager.directoryExists(atPath: userDataPath) {
            fileManager.createDirectory(atPath: userDataPath)
        }

        let path = userDataPath.appendingPathComponent(
            OFString(utf8String: "sites.msgpack"))

        var storage: [String: [NSNumber: AnyObject]]? = nil
        OFException.try({
            let decoded = (OFData(contentsOfFile: path).messagePackValue)
                as? OFDictionary<OFString, OFDictionary<OFNumber, AnyObject>>
            storage =
                (decoded?.nsObject as? [String: [NSNumber: AnyObject]]) ?? [:]
        }, catch: { (OFException) in
            storage = [:]
        })

        self.path = path
        self.storage = storage!
        self.sites = self.storage.keys.sorted()
    }

    func sites(withFilter filter: String?) -> [String] {

        return storage.keys.sorted().filter({ (name) in
            if let filter = filter {
                return name.localizedCaseInsensitiveContains(filter)





            }

            return true
        })
    }

    func hasSite(_ name: String) -> Bool {
        return (storage[name] != nil)
    }

    func length(forSite name: String) -> UInt {
        guard let site = storage[name] else {
            OFInvalidArgumentException().throw()
        }

        return (site[SiteStorage.lengthField] as! NSNumber).uintValue
    }






    func isLegacy(site name: String) -> Bool {
        guard let site = storage[name] else { return false }
        return (site[SiteStorage.legacyField] as! NSNumber).boolValue
    }

    func keyFile(forSite name: String) -> String? {
        guard let site = storage[name] else { return nil }



        guard let keyFile = site[SiteStorage.keyFileField], !(keyFile is NSNull)
        else {
            return nil
        }

        return keyFile as? String
    }

    func setSite(_ name: String, length: UInt, isLegacy: Bool,
                 keyFile: String?) {


        var siteDictionary: [NSNumber: AnyObject] = [
            SiteStorage.lengthField: NSNumber(value: length),

            SiteStorage.legacyField: NSNumber(value: isLegacy),



        ]
        siteDictionary[SiteStorage.keyFileField] = keyFile as AnyObject?


        storage[name] = siteDictionary
        self.update()
    }

    func removeSite(_ name: String) {
        storage[name] = nil
        self.update()
    }

    private func update() {
        let ofStorage = (storage as NSDictionary).ofObject

        ofStorage.messagePackRepresentation.write(toFile: path)
        sites = storage.keys.sorted()
    }
}