Generate XKCD style password

This commit is contained in:
Yishi Lin 2020-02-23 03:06:23 +08:00
parent 71c793029a
commit fe21f1c8da
7 changed files with 7866 additions and 24 deletions

View file

@ -163,7 +163,6 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
PasswordGeneratorFlavor.allCases.forEach { flavor in
let actionTitlePrefix = Defaults.passwordGeneratorFlavor
var actionTitle = flavor.longNameLocalized
if Defaults.passwordGeneratorFlavor == flavor {
actionTitle = "" + actionTitle

View file

@ -33,6 +33,9 @@ class OpenSourceComponentsTableViewController: BasicStaticTableViewController {
["SwiftyUserDefaults",
"https://github.com/radex/SwiftyUserDefaults",
"https://github.com/radex/SwiftyUserDefaults/blob/master/LICENSE"],
["EFF's Wordlists",
"https://www.eff.org/deeplinks/2016/07/new-wordlists-random-passphrases",
"http://creativecommons.org/licenses/by/3.0"],
]
override func viewDidLoad() {

View file

@ -12,7 +12,7 @@ import OneTimePassword
import passKit
enum PasswordEditorCellType {
case nameCell, fillPasswordCell, passwordLengthCell, additionsCell, deletePasswordCell, scanQRCodeCell, memorablePasswordGeneratorCell
case nameCell, fillPasswordCell, passwordLengthCell, additionsCell, deletePasswordCell, scanQRCodeCell, passwordFlavorCell
}
enum PasswordEditorCellKey {
@ -41,7 +41,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
var additionsCell: TextViewTableViewCell?
private var deletePasswordCell: UITableViewCell?
private var scanQRCodeCell: UITableViewCell?
private var memorablePasswordGeneratorCell: UITableViewCell?
private var passwordFlavorCell: UITableViewCell?
var plainText: String {
var plainText = (fillPasswordCell?.getContent())!
@ -69,14 +69,16 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
scanQRCodeCell?.selectionStyle = .default
scanQRCodeCell?.accessoryType = .disclosureIndicator
memorablePasswordGeneratorCell = UITableViewCell(style: .default, reuseIdentifier: "default")
memorablePasswordGeneratorCell?.textLabel?.text = "GetMemorableOne".localize()
memorablePasswordGeneratorCell?.textLabel?.textColor = Colors.systemBlue
memorablePasswordGeneratorCell?.selectionStyle = .default
memorablePasswordGeneratorCell?.accessoryType = .disclosureIndicator
passwordFlavorCell = UITableViewCell(style: .value1, reuseIdentifier: "default")
passwordFlavorCell?.textLabel?.text = "PasswordGeneratorFlavor".localize()
passwordFlavorCell?.textLabel?.textColor = Colors.systemBlue
passwordFlavorCell?.selectionStyle = .none
passwordFlavorCell?.accessoryType = .disclosureIndicator
passwordFlavorCell?.detailTextLabel?.text = Defaults.passwordGeneratorFlavor.localized
}
override func viewDidLoad() {
print("viewDidLoad")
super.viewDidLoad()
if navigationItemTitle != nil {
navigationItem.title = navigationItemTitle
@ -94,15 +96,15 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
tableData = [
[[.type: PasswordEditorCellType.nameCell, .title: "Name".localize(), .content: password?.namePath ?? ""]],
[[.type: PasswordEditorCellType.fillPasswordCell, .title: "Password".localize(), .content: password?.password ?? ""]],
[
[.type: PasswordEditorCellType.fillPasswordCell, .title: "Password".localize(), .content: password?.password ?? ""]],
[[.type: PasswordEditorCellType.additionsCell, .title: "Additions".localize(), .content: password?.additionsPlainText ?? ""]],
[[.type: PasswordEditorCellType.scanQRCodeCell],
[.type: PasswordEditorCellType.deletePasswordCell]]
]
if Defaults.passwordGeneratorFlavor == .random {
tableData[1].append([.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"])
}
tableData[1].append([.type: PasswordEditorCellType.memorablePasswordGeneratorCell])
tableData[1].append([.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"])
tableData[1].append([.type: PasswordEditorCellType.passwordFlavorCell])
}
override func viewDidLayoutSubviews() {
@ -144,8 +146,8 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
defaultValue: defaultLength)
passwordLengthCell?.delegate = self
return passwordLengthCell!
case .memorablePasswordGeneratorCell:
return memorablePasswordGeneratorCell!
case .passwordFlavorCell:
return passwordFlavorCell!
case .additionsCell:
additionsCell = tableView.dequeueReusableCell(withIdentifier: "textViewCell", for: indexPath) as?TextViewTableViewCell
additionsCell?.contentTextView.delegate = self
@ -186,6 +188,8 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath)
tableView.deselectRow(at: indexPath, animated: true)
if selectedCell == deletePasswordCell {
let alert = UIAlertController(title: "DeletePassword?".localize(), message: nil, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Delete".localize(), style: UIAlertAction.Style.destructive, handler: {[unowned self] (action) -> Void in
@ -195,16 +199,32 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
self.present(alert, animated: true, completion: nil)
} else if selectedCell == scanQRCodeCell {
self.performSegue(withIdentifier: "showQRScannerSegue", sender: self)
} else if selectedCell == memorablePasswordGeneratorCell {
// open the webpage
if let url = URL(string: "https://xkpasswd.net/") {
let vc = SFSafariViewController(url: url, entersReaderIfAvailable: false)
vc.delegate = self
present(vc, animated: true)
}
} else if selectedCell == passwordFlavorCell {
showPasswordGeneratorFlavorActionSheet(sourceCell: selectedCell!, tableView: tableView)
}
tableView.deselectRow(at: indexPath, animated: true)
}
func showPasswordGeneratorFlavorActionSheet(sourceCell: UITableViewCell, tableView: UITableView) {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
PasswordGeneratorFlavor.allCases.forEach { flavor in
var actionTitle = flavor.longNameLocalized
if Defaults.passwordGeneratorFlavor == flavor {
actionTitle = "" + actionTitle
}
let action = UIAlertAction(title: actionTitle, style: .default) { _ in
Defaults.passwordGeneratorFlavor = flavor
sourceCell.detailTextLabel?.text = Defaults.passwordGeneratorFlavor.localized
tableView.reloadSections([self.passwordSection], with: .none)
}
optionMenu.addAction(action)
}
let cancelAction = UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil)
optionMenu.addAction(cancelAction)
optionMenu.popoverPresentationController?.sourceView = sourceCell
self.present(optionMenu, animated: true, completion: nil)
}
// generate password, copy to pasteboard, and set the cell

View file

@ -51,6 +51,8 @@
"Random" = "Random";
"RandomString" = "Random String";
"ApplesKeychainStyle" = "Apple's Keychain Style";
"XKCD" = "XKCD";
"XKCDStyle" = "XKCD Style";
// Git
"FailedToFetchPasswords" = "Failed to fetch passwords";

View file

@ -0,0 +1,13 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"data" : [
{
"idiom" : "universal",
"filename" : "EN_wordlist.txt",
"universal-type-identifier" : "public.plain-text"
}
]
}

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,7 @@ import KeychainAccess
public enum PasswordGeneratorFlavor: String {
case apple = "Apple"
case random = "Random"
case xkcd = "XKCD"
public var localized: String {
return rawValue.localize()
@ -22,6 +23,8 @@ public enum PasswordGeneratorFlavor: String {
return "ApplesKeychainStyle".localize()
case .random:
return "RandomString".localize()
case .xkcd:
return "XKCDStyle".localize()
}
}
@ -31,6 +34,8 @@ public enum PasswordGeneratorFlavor: String {
return (15, 15, 15)
case .random:
return (4, 64, 16)
case .xkcd:
return (2, 5, 3)
}
}
@ -40,6 +45,8 @@ public enum PasswordGeneratorFlavor: String {
return Keychain.generatePassword()
case .random:
return PasswordGeneratorFlavor.generateRandom(length: length)
case .xkcd:
return PasswordGeneratorFlavor.generateXKCD(length: length)
}
}
@ -47,6 +54,28 @@ public enum PasswordGeneratorFlavor: String {
let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_+-="
return String((0..<length).map { _ in chars.randomElement()! })
}
private static func generateXKCD(length: Int) -> String {
// Get the word list
let bundle = Bundle(identifier: Globals.passKitBundleIdentifier)!
guard let asset = NSDataAsset(name: "WordLists", bundle: bundle),
let data = String(data: asset.data, encoding: .utf8) else {
return ""
}
let words = data.splitByNewline()
// Generate a password
let delimiters = "0123456789!@#$%^&*_+-="
var password = ""
(0..<length).forEach { _ in
var word = words.randomElement()!
if Bool.random() {
word = word.uppercased()
}
password += word + String(delimiters.randomElement()!)
}
return password
}
}
extension PasswordGeneratorFlavor: CaseIterable {}