├── .gitignore ├── .gitmodules ├── HACKING.md ├── LICENSE ├── LaunchScreen.xib ├── Makefile ├── NSTask.h ├── PassDataController.h ├── PassDataController.mm ├── PassEntry.h ├── PassEntry.mm ├── PassEntryViewController.h ├── PassEntryViewController.mm ├── PasswordsViewController.h ├── PasswordsViewController.mm ├── README.md ├── Resources ├── Icon.png ├── Icon@2x.png ├── Icon@3x.png └── Info.plist ├── control ├── ent.xml ├── icon ├── lock-ios8.svg └── lock.svg ├── main.m ├── passwordstoreApplication.mm └── screenshots ├── 1_main_screen.png ├── 2_subdir.png ├── 3_entry.png └── 4_gpg.png /.gitignore: -------------------------------------------------------------------------------- 1 | .theos/* 2 | _/* 3 | obj/* 4 | *.deb 5 | *.swp 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "modules/Valet"] 2 | path = modules/Valet 3 | url = https://github.com/Square/Valet.git 4 | [submodule "modules/theos"] 5 | path = modules/theos 6 | url = https://github.com/rpetrich/theos.git 7 | -------------------------------------------------------------------------------- /HACKING.md: -------------------------------------------------------------------------------- 1 | Hacking on this project 2 | ======================= 3 | 4 | * Coding style: http://www.webkit.org/coding/coding-style.html 5 | 6 | * Theos Development suite: http://iphonedevwiki.net/index.php/Theos 7 | 8 | * Theos Reference: https://github.com/theiostream/theos-ref/ 9 | 10 | * Theos Makefiles: http://uv.howett.net/ipf.html 11 | 12 | * Objective C: http://cocoadevcentral.com/d/learn_objectivec/ 13 | 14 | * SimpleKeychain: https://github.com/auth0/SimpleKeychain 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Password Store is Copyright (C) 2012-2016 David Beitey . 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "{}" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright {yyyy} {name of copyright owner} 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ARCHS=armv7 armv7s arm64 2 | TARGET=iphone:latest 3 | 4 | APPLICATION_NAME = pass 5 | pass_FILES = main.m passwordstoreApplication.mm PasswordsViewController.mm PassEntry.mm PassDataController.mm PassEntryViewController.mm modules/Valet/Valet/VALValet.m modules/Valet/Valet/VALSecureEnclaveValet.m 6 | pass_FRAMEWORKS = UIKit CoreGraphics Security 7 | pass_CFLAGS=-fobjc-arc -I modules/Valet -I modules/Valet/Valet -I modules/Valet/Other 8 | 9 | TARGET_CODESIGN_FLAGS = -Sent.xml 10 | 11 | include modules/theos/makefiles/common.mk 12 | include $(THEOS_MAKE_PATH)/application.mk 13 | 14 | build-install: clean package install 15 | 16 | -------------------------------------------------------------------------------- /NSTask.h: -------------------------------------------------------------------------------- 1 | /* NSTask.h 2 | Copyright (c) 1996-2007, Apple Inc. All rights reserved. 3 | */ 4 | 5 | #import 6 | 7 | @class NSString, NSArray, NSDictionary; 8 | 9 | @interface NSTask : NSObject 10 | 11 | // Create an NSTask which can be run at a later time 12 | // An NSTask can only be run once. Subsequent attempts to 13 | // run an NSTask will raise. 14 | // Upon task death a notification will be sent 15 | // { Name = NSTaskDidTerminateNotification; object = task; } 16 | // 17 | 18 | - (instancetype)init; 19 | 20 | // set parameters 21 | // these methods can only be done before a launch 22 | - (void)setLaunchPath:(NSString *)path; 23 | - (void)setArguments:(NSArray *)arguments; 24 | - (void)setEnvironment:(NSDictionary *)dict; 25 | // if not set, use current 26 | - (void)setCurrentDirectoryPath:(NSString *)path; 27 | // if not set, use current 28 | 29 | // set standard I/O channels; may be either an NSFileHandle or an NSPipe 30 | - (void)setStandardInput:(id)input; 31 | - (void)setStandardOutput:(id)output; 32 | - (void)setStandardError:(id)error; 33 | 34 | // get parameters 35 | - (NSString *)launchPath; 36 | - (NSArray *)arguments; 37 | - (NSDictionary *)environment; 38 | - (NSString *)currentDirectoryPath; 39 | 40 | // get standard I/O channels; could be either an NSFileHandle or an NSPipe 41 | - (id)standardInput; 42 | - (id)standardOutput; 43 | - (id)standardError; 44 | 45 | // actions 46 | - (void)launch; 47 | 48 | - (void)interrupt; // Not always possible. Sends SIGINT. 49 | - (void)terminate; // Not always possible. Sends SIGTERM. 50 | 51 | - (BOOL)suspend; 52 | - (BOOL)resume; 53 | 54 | // status 55 | - (int)processIdentifier; 56 | - (BOOL)isRunning; 57 | 58 | - (int)terminationStatus; 59 | 60 | @end 61 | 62 | @interface NSTask (NSTaskConveniences) 63 | 64 | + (NSTask *)launchedTaskWithLaunchPath:(NSString *)path arguments:(NSArray *)arguments; 65 | // convenience; create and launch 66 | 67 | - (void)waitUntilExit; 68 | // poll the runLoop in defaultMode until task completes 69 | 70 | @end 71 | 72 | FOUNDATION_EXPORT NSString * const NSTaskDidTerminateNotification; 73 | 74 | -------------------------------------------------------------------------------- /PassDataController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Brian A. Mattern . 3 | * Copyright (C) 2015 David Beitey . 4 | * All Rights Reserved. 5 | * This file is licensed under the GPLv2+. 6 | * Please see COPYING for more information 7 | */ 8 | @class PassEntry; 9 | 10 | @interface PassDataController : NSObject 11 | - (id)initWithPath:(NSString *)path; 12 | - (unsigned)numEntries; 13 | - (PassEntry *)entryAtIndex:(unsigned)index; 14 | @end 15 | -------------------------------------------------------------------------------- /PassDataController.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Brian A. Mattern . 3 | * Copyright (C) 2015 David Beitey . 4 | * All Rights Reserved. 5 | * This file is licensed under the GPLv2+. 6 | * Please see COPYING for more information 7 | */ 8 | #import "PassDataController.h" 9 | #import "PassEntry.h" 10 | #import 11 | 12 | @interface PassDataController () 13 | 14 | @property (nonatomic, copy, readwrite) NSMutableArray *entries; 15 | 16 | - (void) readEntries:(NSString *)path; 17 | 18 | @end 19 | 20 | @implementation PassDataController 21 | 22 | @synthesize entries; 23 | 24 | - (id)initWithPath:(NSString *)path { 25 | if ( (self = [super init]) ) { 26 | [self readEntries:path]; 27 | } 28 | 29 | return self; 30 | } 31 | 32 | - (void)readEntries:(NSString *)path { 33 | DIR *d; 34 | struct dirent *dent; 35 | PassEntry *entry; 36 | 37 | NSMutableArray *list = [[NSMutableArray alloc] init]; 38 | 39 | d = opendir([path cStringUsingEncoding:NSUTF8StringEncoding]); 40 | if (!d) { 41 | // XXX handle error! 42 | return; 43 | } 44 | 45 | while ( (dent = readdir(d)) ) { 46 | if (dent->d_name[0] == '.') continue; // skip hidden files 47 | 48 | entry = [[PassEntry alloc] init]; 49 | entry.name = [[NSString alloc] initWithCString:dent->d_name 50 | encoding:NSUTF8StringEncoding]; 51 | entry.path = [NSString stringWithFormat:@"%@/%s", path, dent->d_name]; 52 | entry.is_dir = (dent->d_type == DT_DIR ? YES : NO); 53 | 54 | [list addObject:entry]; 55 | } 56 | 57 | self.entries = list; 58 | } 59 | 60 | - (unsigned) numEntries { 61 | return [self.entries count]; 62 | } 63 | 64 | - (PassEntry *)entryAtIndex:(unsigned)index { 65 | return [self.entries objectAtIndex:index]; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /PassEntry.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Brian A. Mattern . 3 | * Copyright (C) 2015 David Beitey . 4 | * All Rights Reserved. 5 | * This file is licensed under the GPLv2+. 6 | * Please see COPYING for more information 7 | */ 8 | @interface PassEntry : NSObject { 9 | NSString *name; 10 | } 11 | 12 | @property (nonatomic,retain) NSString *name; 13 | @property (nonatomic,retain) NSString *path; 14 | @property (nonatomic,assign) BOOL is_dir; 15 | @property (nonatomic,readonly) NSString *pass; 16 | 17 | - (NSString *)passWithPassphrase:(NSString *)passphrase passwordOnly:(BOOL)passwordOnly; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /PassEntry.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Brian A. Mattern . 3 | * Copyright (C) 2015 David Beitey . 4 | * All Rights Reserved. 5 | * This file is licensed under the GPLv2+. 6 | * Please see COPYING for more information 7 | */ 8 | #import "PassEntry.h" 9 | #import "NSTask.h" // Not documented on iOS in Foundation but still available 10 | 11 | @implementation PassEntry 12 | 13 | @synthesize name, path, is_dir, pass; 14 | 15 | - (NSString *)name 16 | { 17 | if ([name hasSuffix:@".gpg"]) 18 | return [name substringToIndex:[name length] - 4]; 19 | else 20 | return name; 21 | } 22 | 23 | - (NSString *)passWithPassphrase:(NSString *)passphrase passwordOnly:(BOOL)passwordOnly 24 | { 25 | NSTask *task = [[NSTask alloc] init]; 26 | [task setLaunchPath:@"/usr/bin/gpg"]; 27 | [task setArguments:[NSArray arrayWithObjects: 28 | @"--passphrase",passphrase,@"-d",@"--batch", 29 | @"--quiet",@"--no-tty",self.path,nil]]; 30 | 31 | NSPipe *opipe = [NSPipe pipe]; 32 | [task setStandardOutput:opipe]; 33 | NSPipe *erroPipe = [NSPipe pipe]; 34 | [task setStandardError:erroPipe]; 35 | 36 | [task launch]; 37 | [task waitUntilExit]; 38 | 39 | int status = [task terminationStatus]; 40 | 41 | if (status == 0) { 42 | NSFileHandle *ofile = [opipe fileHandleForReading]; 43 | NSData *data = [ofile readDataToEndOfFile]; 44 | NSString *str= [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 45 | // return first line of file or all of file 46 | return passwordOnly ? [[str componentsSeparatedByString:@"\n"] objectAtIndex:0] : str; 47 | } else { 48 | NSFileHandle *ofile = [erroPipe fileHandleForReading]; 49 | NSData *data = [ofile readDataToEndOfFile]; 50 | NSString *str= [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 51 | NSLog(@"error string: %@", str); 52 | // XXX handle error 53 | return nil; 54 | } 55 | } 56 | 57 | @end 58 | 59 | -------------------------------------------------------------------------------- /PassEntryViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Brian A. Mattern . 3 | * Copyright (C) 2015 David Beitey . 4 | * All Rights Reserved. 5 | * This file is licensed under the GPLv2+. 6 | * Please see COPYING for more information 7 | */ 8 | #import 9 | @class PassEntry; 10 | 11 | @interface PassEntryViewController: UITableViewController { 12 | } 13 | 14 | @property(nonatomic,retain) PassEntry *entry; 15 | @property(nonatomic,retain) UIPasteboard *pasteboard; 16 | @property(nonatomic,assign) BOOL useTouchID; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /PassEntryViewController.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Brian A. Mattern . 3 | * Copyright (C) 2015 David Beitey . 4 | * All Rights Reserved. 5 | * This file is licensed under the GPLv2+. 6 | * Please see COPYING for more information 7 | */ 8 | #import "PassEntryViewController.h" 9 | #import "PassEntry.h" 10 | #import "Valet/Valet.h" 11 | 12 | @interface PassEntryViewController() 13 | @property (nonatomic,retain) VALSecureEnclaveValet *keychain; 14 | @property (nonatomic,retain) NSString *keychain_key; 15 | @property (nonatomic) UIBackgroundTaskIdentifier backgroundTaskIdentifier; 16 | - (void)copyName; 17 | - (void)showAlertWithMessage:(NSString *)message alertTitle:(NSString *)title; 18 | - (void)decryptGpgWithPasswordOnly:(BOOL)passwordOnly copyToPasteboard:(BOOL)pasteboard showInAlert:(BOOL)showAlert; 19 | - (void)requestGpgPassphrase:(BOOL)passwordOnly entryTitle:(NSString *)title copyToPasteboard:(BOOL)pasteboard showInAlert:(BOOL)showAlert; 20 | @end 21 | 22 | 23 | @implementation PassEntryViewController 24 | @synthesize entry; 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | // self.title = NSLocalizedString(@"Passwords", @"Password title"); 29 | self.backgroundTaskIdentifier = 0; 30 | 31 | self.keychain = [[VALSecureEnclaveValet alloc] initWithIdentifier:@"Pass" accessControl:VALAccessControlUserPresence]; 32 | self.useTouchID = [[self.keychain class] supportsSecureEnclaveKeychainItems]; 33 | self.pasteboard = [UIPasteboard generalPasteboard]; 34 | 35 | // TODO Further work required for non-TouchID devices 36 | if (self.useTouchID) { 37 | // Local TouchID authentication 38 | self.keychain_key = @"gpg-passphrase-touchid"; 39 | } else { 40 | self.keychain_key = @"passphrase"; 41 | } 42 | } 43 | 44 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 45 | { 46 | return 1; 47 | } 48 | 49 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 50 | return 5; 51 | } 52 | 53 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 54 | static NSString *CellIdentifier = @"EntryDetailCell"; 55 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 56 | if (cell == nil) { 57 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:CellIdentifier]; 58 | } 59 | 60 | switch(indexPath.row) { 61 | case 0: 62 | cell.textLabel.text = @"Name"; 63 | cell.detailTextLabel.text = self.entry.name; 64 | break; 65 | case 1: 66 | cell.textLabel.text = @"Password"; 67 | cell.detailTextLabel.text = @"Tap to show"; 68 | break; 69 | case 2: 70 | cell.textLabel.text = @"Password"; 71 | cell.detailTextLabel.text = @"Tap to copy"; 72 | break; 73 | case 3: 74 | cell.textLabel.text = @"Full text"; 75 | cell.detailTextLabel.text = @"Tap to show"; 76 | break; 77 | case 4: 78 | cell.textLabel.text = @"Full text"; 79 | cell.detailTextLabel.text = @"Tap to copy"; 80 | break; 81 | default: 82 | break; 83 | } 84 | 85 | return cell; 86 | } 87 | 88 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 89 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 90 | 91 | switch(indexPath.row) { 92 | case 0: 93 | // Name 94 | [self copyName]; 95 | break; 96 | case 1: 97 | // Password, first line only, alert 98 | [self decryptGpgWithPasswordOnly:YES copyToPasteboard:NO showInAlert:YES]; 99 | break; 100 | case 2: 101 | // Password, first line only, pasteboard 102 | [self decryptGpgWithPasswordOnly:YES copyToPasteboard:YES showInAlert:NO]; 103 | break; 104 | case 3: 105 | // Full text, all lines, alert 106 | [self decryptGpgWithPasswordOnly:NO copyToPasteboard:NO showInAlert:YES]; 107 | break; 108 | case 4: 109 | // Full text, all lines, passboard 110 | [self decryptGpgWithPasswordOnly:NO copyToPasteboard:YES showInAlert:NO]; 111 | break; 112 | default: 113 | break; 114 | } 115 | } 116 | 117 | - (void)copyToPasteboard:(NSString *)string { 118 | self.pasteboard.string = string; 119 | } 120 | 121 | - (void)copyToPasteboard:(NSString *)string clearTimeout:(double)timeout { 122 | // Store the original value for restoration later 123 | NSString *originalPasteboard = self.pasteboard.string; 124 | 125 | // Copy the password onto the pasteboard 126 | [self copyToPasteboard:string]; 127 | 128 | NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init]; 129 | [userInfo setObject:originalPasteboard forKey:@"originalPasteboard"]; 130 | 131 | __weak UIViewController *weakSelf = self; 132 | self.backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ 133 | // Once run, invalidate 134 | [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier]; 135 | self.backgroundTaskIdentifier = UIBackgroundTaskInvalid; 136 | }]; 137 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 138 | NSTimer *timer = [NSTimer timerWithTimeInterval:timeout target:weakSelf selector:@selector(restorePasteboardWithTimer:) userInfo:userInfo repeats:NO]; 139 | [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; 140 | }); 141 | } 142 | 143 | - (void)restorePasteboardWithTimer:(NSTimer *)timer { 144 | NSDictionary *dict = [timer userInfo]; 145 | NSString *originalPasteboard = [dict objectForKey:@"originalPasteboard"]; 146 | 147 | // Replace the original string. We can't access the pasteboard to 148 | // actually determine whether we should replace or not, so do it anyway. 149 | // TODO Implement an option 150 | [self copyToPasteboard:originalPasteboard]; 151 | 152 | // Once run, invalidate this task 153 | [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier]; 154 | self.backgroundTaskIdentifier = UIBackgroundTaskInvalid; 155 | } 156 | 157 | - (void)showAlertWithMessage:(NSString *)message alertTitle:(NSString *)title { 158 | UIAlertController* alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; 159 | UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {}]; 160 | [alert addAction:defaultAction]; 161 | [self presentViewController:alert animated:YES completion:nil]; 162 | } 163 | 164 | - (void)copyName { 165 | [self copyToPasteboard:self.entry.name]; 166 | } 167 | 168 | - (void) performPasswordAction:(NSString *)password entryTitle:(NSString *)title copyToPasteboard:(BOOL)pasteboard showInAlert:(BOOL)showAlert { 169 | if (pasteboard) { 170 | [self copyToPasteboard:password clearTimeout:45.0]; 171 | } 172 | if (showAlert) { 173 | [self showAlertWithMessage:password alertTitle:title]; 174 | } 175 | } 176 | 177 | - (void)decryptGpgWithPasswordOnly:(BOOL)passwordOnly copyToPasteboard:(BOOL)pasteboard showInAlert:(BOOL)showAlert { 178 | BOOL result = NO; 179 | NSString *password; // Decryped password 180 | NSString *keychain_passphrase; // iOS keychain passphrase 181 | 182 | if (self.useTouchID) { 183 | keychain_passphrase = [self.keychain stringForKey:self.keychain_key userPrompt:@"Unlock your keychain to access this password."]; 184 | } else { 185 | keychain_passphrase = [self.keychain stringForKey:self.keychain_key]; 186 | } 187 | 188 | if (keychain_passphrase) { 189 | password = [self.entry passWithPassphrase:keychain_passphrase passwordOnly:passwordOnly]; 190 | if (password) { 191 | [self performPasswordAction:password entryTitle:self.entry.name copyToPasteboard:pasteboard showInAlert:showAlert]; 192 | result = YES; 193 | } 194 | } 195 | 196 | if (!result) { 197 | // GPG decryption failed with stored keychain passphrase or no keychain passphrase present 198 | // so try requesting the passphase 199 | [self requestGpgPassphrase:passwordOnly entryTitle:self.entry.name copyToPasteboard:pasteboard showInAlert:showAlert]; 200 | } 201 | } 202 | 203 | - (void)requestGpgPassphrase:(BOOL)passwordOnly entryTitle:(NSString *)title copyToPasteboard:(BOOL)pasteboard showInAlert:(BOOL)showAlert { 204 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Passphrase" message:@"Enter passphrase for your GPG key" preferredStyle:UIAlertControllerStyleAlert]; 205 | 206 | UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {}]; 207 | [alert addAction:cancelAction]; 208 | 209 | UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { 210 | NSString *keychain_passphrase = ((UITextField *)[alert.textFields objectAtIndex:0]).text; 211 | // If the passphrase decrypts the entry, save it 212 | NSString *password = [self.entry passWithPassphrase:keychain_passphrase passwordOnly:YES]; 213 | if (password) { 214 | if (self.useTouchID) { 215 | [self.keychain setString:keychain_passphrase forKey:self.keychain_key]; //userPrompt:@"Securely store your GPG passphrase"]; 216 | } else { 217 | [self.keychain setString:keychain_passphrase forKey:self.keychain_key]; 218 | } 219 | 220 | [self performPasswordAction:password entryTitle:title copyToPasteboard:pasteboard showInAlert:showAlert]; 221 | 222 | } else { 223 | [self showAlertWithMessage:@"Passphrase invalid" alertTitle:@"Passphrase"]; 224 | } 225 | }]; 226 | [alert addAction:defaultAction]; 227 | 228 | [alert addTextFieldWithConfigurationHandler: ^(UITextField *textField) { 229 | textField.placeholder = @"Passphrase"; 230 | textField.secureTextEntry = YES; 231 | }]; 232 | 233 | [self presentViewController:alert animated:YES completion:nil]; 234 | } 235 | 236 | @end 237 | -------------------------------------------------------------------------------- /PasswordsViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Brian A. Mattern . 3 | * Copyright (C) 2015 David Beitey . 4 | * All Rights Reserved. 5 | * This file is licensed under the GPLv2+. 6 | * Please see COPYING for more information 7 | */ 8 | #import 9 | @class PassDataController; 10 | 11 | @interface PasswordsViewController: UITableViewController { 12 | } 13 | 14 | @property (nonatomic, retain) PassDataController *entries; 15 | @end 16 | -------------------------------------------------------------------------------- /PasswordsViewController.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Brian A. Mattern . 3 | * Copyright (C) 2015 David Beitey . 4 | * All Rights Reserved. 5 | * This file is licensed under the GPLv2+. 6 | * Please see COPYING for more information 7 | */ 8 | #import 9 | #import "PasswordsViewController.h" 10 | #import "PassDataController.h" 11 | #import "PassEntry.h" 12 | #import "PassEntryViewController.h" 13 | #import "Valet/Valet.h" 14 | 15 | @implementation PasswordsViewController 16 | 17 | @synthesize entries; 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | if (self.title == nil) { 22 | self.title = NSLocalizedString(@"Passwords", @"Password title"); 23 | } 24 | 25 | UIBarButtonItem *clearButton = [[UIBarButtonItem alloc] initWithTitle:@"Clear Keychain" style:UIBarButtonItemStylePlain target:self action:@selector(clearPassphrase) ]; 26 | self.navigationItem.rightBarButtonItem = clearButton; 27 | } 28 | 29 | - (void)clearPassphrase { 30 | // TODO Refactor into shared function 31 | VALSecureEnclaveValet *keychain = [[VALSecureEnclaveValet alloc] initWithIdentifier:@"Pass" accessControl:VALAccessControlUserPresence]; 32 | [keychain removeObjectForKey:@"gpg-passphrase-touchid"]; 33 | 34 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Keychain cleared" message:@"Passphrase has been removed from the keychain" preferredStyle:UIAlertControllerStyleAlert]; 35 | UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {}]; 36 | [alert addAction:defaultAction]; 37 | [self presentViewController:alert animated:YES completion:nil]; 38 | } 39 | 40 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 41 | { 42 | return 1; 43 | } 44 | 45 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 46 | 47 | return [self.entries numEntries]; 48 | } 49 | 50 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 51 | static NSString *CellIdentifier = @"EntryCell"; 52 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 53 | if (cell == nil) { 54 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; 55 | } 56 | 57 | PassEntry *entry = [self.entries entryAtIndex:indexPath.row]; 58 | 59 | cell.textLabel.text = entry.name; 60 | if (entry.is_dir) 61 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 62 | else 63 | cell.accessoryType = UITableViewCellAccessoryNone; 64 | 65 | return cell; 66 | } 67 | 68 | - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { 69 | // Return unique, capitalised first letters of entries 70 | NSMutableArray *firstLetters = [[NSMutableArray alloc] init]; 71 | [firstLetters addObject:UITableViewIndexSearch]; 72 | for (int i = 0; i < [self.entries numEntries]; i++) { 73 | NSString *letterString = [[[self.entries entryAtIndex:i].name substringToIndex:1] uppercaseString]; 74 | if (![firstLetters containsObject:letterString]) { 75 | [firstLetters addObject:letterString]; 76 | } 77 | } 78 | return firstLetters; 79 | } 80 | 81 | - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index { 82 | for (int i = 0; i < [self.entries numEntries]; i++) { 83 | NSString *letterString = [[[self.entries entryAtIndex:i].name substringToIndex:1] uppercaseString]; 84 | if ([letterString isEqualToString:title]) { 85 | [tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; 86 | break; 87 | } 88 | } 89 | return 1; 90 | } 91 | 92 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 93 | [tableView deselectRowAtIndexPath:indexPath animated:NO]; 94 | 95 | PassEntry *entry = [self.entries entryAtIndex:indexPath.row]; 96 | 97 | if (entry.is_dir) { 98 | // push subdir view onto stack 99 | PasswordsViewController *subviewController = [[PasswordsViewController alloc] init]; 100 | subviewController.entries = [[PassDataController alloc] initWithPath:entry.path]; 101 | subviewController.title = entry.name; 102 | [[self navigationController] pushViewController:subviewController animated:YES]; 103 | } else { 104 | PassEntryViewController *detailController = [[PassEntryViewController alloc] init]; 105 | detailController.entry = entry; 106 | [[self navigationController] pushViewController:detailController animated:YES]; 107 | } 108 | } 109 | 110 | @end 111 | 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pass for iOS 2 | ============ 3 | 4 | ![Icon](https://raw.github.com/davidjb/pass-ios/master/Resources/Icon@3x.png) 5 | 6 | View your [pass password store][] passwords on your iDevice. 7 | 8 | [pass password store]: http://www.passwordstore.org 9 | 10 | Features 11 | -------- 12 | 13 | * Access and decrypt GPG-based files within your Pass-compatible store 14 | * Copy passwords to pasteboard or display on screen 15 | * View or copy multi-line Pass content 16 | * Resets pasteboard contents after 45 seconds when copying data 17 | * TouchID authentication for storing GPG passphrase 18 | 19 | Dependencies 20 | ------------ 21 | 22 | The following packages are available from Cydia: 23 | 24 | * gnupg (required) 25 | * git (optional) 26 | 27 | Setup 28 | ----- 29 | 30 | 1. Install the app itself. 31 | 32 | See https://github.com/davidjb/pass-ios/releases for a list of available 33 | pre-built `.deb` packages, or follow the instructions below to build your 34 | own. 35 | 36 | 2. Copy your `pass` password-store to `/var/mobile/.password-store`. 37 | 38 | The simplest way to do this is to store your passwords in a `git` repository, 39 | which you can then clone onto your device. Alternatively, you can use SCP, 40 | iFile or any other method to transfer the passwords over. 41 | 42 | 3. Set up your gpg key: 43 | 44 | 1. Export your *private* key from your desktop, laptop or other computer: 45 | 46 | ``` 47 | (desktop)$ gpg --export-secret-key --armor ${KEY_ID} > ${KEY_ID}.asc 48 | ``` 49 | 50 | 2. Copy this file to your iOS device 51 | 52 | 3. On the device, import the key: 53 | 54 | ``` 55 | (device)$ gpg --import ${KEY_ID}.asc 56 | ``` 57 | 58 | 4. Delete the key file 59 | 60 | 5. Test decrypting one of your passwords 61 | 62 | ``` 63 | (device)$ gpg -d ~/.password-store/ENTRY.gpg 64 | ``` 65 | 66 | 4. Launch and begin using the Pass app! 67 | 68 | Using the app 69 | ------------- 70 | 71 | After launching the app, you will be presented with a listing of files and 72 | directories in `~/.password-store`. Files starting with '.' are hidden, and 73 | `.gpg` extensions are stripped. 74 | 75 | ![Main Screen](/screenshots/1_main_screen.png?raw=true) 76 | 77 | Clicking on a directory will show its contents. 78 | 79 | ![Subdirectory Listing](/screenshots/2_subdir.png?raw=true) 80 | 81 | Clicking on a password file will show a screen with the password file details (name and \*'d out password). 82 | 83 | ![Subdirectory Listing](/screenshots/3_entry.png?raw=true) 84 | 85 | Clicking on the name or password box will copy the respective contents to the pasteboard (clipboard). Since the password is encrypted, you will have to enter you passphrase before it can be copied. 86 | 87 | ![Subdirectory Listing](/screenshots/4_gpg.png?raw=true) 88 | 89 | Building 90 | -------- 91 | 92 | 1. Obtain the iPhone SDK, usually done via Xcode. 93 | 94 | 2. Install [Homebrew](http://brew.sh/) and install `ldid`: 95 | 96 | brew update 97 | brew install ldid 98 | 99 | If on another platform, consult this [setup documentation](http://iphonedevwiki.net/index.php/Theos/Setup). 100 | 101 | 3. Build using the following: 102 | 103 | ``` 104 | git clone --recursive https://github.com/davidjb/pass-ios.git 105 | cd pass-ios 106 | make package 107 | ``` 108 | 109 | This clones and configures the theos environment for building, producing a 110 | `.deb` package. 111 | 112 | 3. Install directly your device over SSH with: 113 | 114 | ``` 115 | export THEOS_DEVICE_IP=[device IP] 116 | make install 117 | 118 | # or to clean, build and install in one 119 | make build-install 120 | ``` 121 | 122 | Ensure that you have access to root on your device via SSH. 123 | 124 | If your newly installed app doesn't appear, run `uicache` on your device via 125 | SSH. This will typically only be on first install or if you've updated a 126 | visual aspect that would appear on the home screen. 127 | 128 | Contributing 129 | ------------ 130 | 131 | You're awesome -- all help is greatly appreciated! Just fork and submit a 132 | pull request on GitHub. For major changes or new features, consider opening 133 | an issue first for discussion; this may save you a bunch of time in coding! 134 | 135 | If you're not sure where to start contributing, take a look at the 136 | [issue tracker](https://github.com/davidjb/pass-ios/issues) 137 | to see the current list of bugs to solve or features to implement, and consult 138 | the todo list below. 139 | 140 | Todo 141 | ---- 142 | 143 | * Simplify initial setup 144 | 145 | - enter git repo url to clone 146 | - paste gpg key 147 | - investigate becoming an official App Store app 148 | 149 | * Better details screen 150 | 151 | - Change UI table cells when displaying passwords (temporarily) 152 | 153 | * Configurable Settings 154 | 155 | - allow showing passwords on screen rather than copying 156 | - base directory location (other than /var/mobile/.password-store) 157 | - whether to store passphrase in keychain 158 | - passphrase storage duration (X minutes or forever) 159 | - pasteboard reset time 160 | 161 | * Password editing / adding 162 | - auto-commit ala pass bash script 163 | - trigger 'git pull' from app (also 'git push' after editing is implemented) 164 | -------------------------------------------------------------------------------- /Resources/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidjb/pass-ios/e439d0641ec43b5701de6626d2e8fb54f6049a9a/Resources/Icon.png -------------------------------------------------------------------------------- /Resources/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidjb/pass-ios/e439d0641ec43b5701de6626d2e8fb54f6049a9a/Resources/Icon@2x.png -------------------------------------------------------------------------------- /Resources/Icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidjb/pass-ios/e439d0641ec43b5701de6626d2e8fb54f6049a9a/Resources/Icon@3x.png -------------------------------------------------------------------------------- /Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | pass 9 | CFBundleExecutable 10 | pass 11 | CFBundleIconFiles 12 | 13 | Icon 14 | 15 | CFBundleIdentifier 16 | com.davidjb.pass 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundlePackageType 20 | APPL 21 | CFBundleVersion 22 | 1.0 23 | LSRequiresIPhoneOS 24 | 1 25 | UIRequiredDeviceCapabilities 26 | 27 | 28 | UIStatusBarStyle 29 | UIStatusBarStyleDefault 30 | UISupportedInterfaceOrientations 31 | 32 | UIInterfaceOrientationPortrait 33 | UIInterfaceOrientationPortraitUpsideDown 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UILaunchStoryboardName 38 | LaunchScreen 39 | CFBundleName 40 | pass 41 | CFBundleSignature 42 | ???? 43 | CFBundleSupportedPlatforms 44 | 45 | iPhoneOS 46 | 47 | DTPlatformName 48 | iphoneos 49 | DTSDKName 50 | iphoneos3.0 51 | MinimumOSVersion 52 | 3.0 53 | 54 | 55 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: com.davidjb.pass 2 | Name: Pass 3 | Depends: gnupg 4 | Version: 0.2.1 5 | Architecture: iphoneos-arm 6 | Description: iOS app to access your password-store 7 | Maintainer: davidjb 8 | Author: davidjb 9 | Section: Utilities 10 | -------------------------------------------------------------------------------- /ent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | application-identifier 5 | com.davidjb.pass 6 | 7 | 8 | -------------------------------------------------------------------------------- /icon/lock-ios8.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 23 | 27 | 31 | 32 | 34 | 38 | 42 | 43 | 45 | 49 | 53 | 54 | 56 | 60 | 64 | 68 | 72 | 73 | 75 | 79 | 83 | 84 | 86 | 90 | 94 | 95 | 97 | 101 | 105 | 106 | 108 | 112 | 116 | 117 | 119 | 123 | 127 | 128 | 130 | 134 | 138 | 139 | 141 | 145 | 149 | 150 | 152 | 156 | 160 | 161 | 170 | 181 | 190 | 200 | 209 | 220 | 229 | 239 | 250 | 253 | 256 | 263 | 264 | 265 | 274 | 277 | 286 | 287 | 290 | 299 | 300 | 310 | 313 | 322 | 323 | 327 | 337 | 347 | 356 | 367 | 376 | 377 | 399 | 401 | 402 | 404 | image/svg+xml 405 | 407 | 408 | 409 | 410 | 411 | 416 | 425 | 429 | 439 | 449 | 456 | 457 | 458 | -------------------------------------------------------------------------------- /icon/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 23 | 27 | 31 | 32 | 34 | 38 | 42 | 43 | 45 | 49 | 53 | 57 | 61 | 62 | 64 | 68 | 72 | 73 | 75 | 79 | 83 | 84 | 86 | 90 | 94 | 95 | 97 | 101 | 105 | 106 | 108 | 112 | 116 | 117 | 119 | 123 | 127 | 128 | 130 | 134 | 138 | 139 | 141 | 145 | 149 | 150 | 160 | 170 | 179 | 190 | 199 | 209 | 219 | 228 | 239 | 248 | 258 | 267 | 278 | 281 | 285 | 286 | 295 | 298 | 302 | 303 | 314 | 325 | 336 | 346 | 355 | 358 | 361 | 368 | 369 | 370 | 379 | 388 | 399 | 409 | 412 | 421 | 422 | 425 | 434 | 435 | 446 | 456 | 466 | 469 | 478 | 479 | 489 | 498 | 509 | 518 | 528 | 539 | 543 | 548 | 554 | 559 | 564 | 570 | 571 | 581 | 591 | 600 | 611 | 620 | 630 | 641 | 652 | 662 | 672 | 682 | 691 | 702 | 711 | 721 | 732 | 743 | 753 | 754 | 772 | 774 | 775 | 777 | image/svg+xml 778 | 780 | 781 | 782 | 783 | 784 | 788 | 796 | 798 | 801 | 807 | 816 | 817 | 826 | 835 | 838 | 841 | 843 | 848 | 849 | 854 | 855 | 858 | 860 | 865 | 866 | 871 | 872 | 875 | 877 | 882 | 883 | 888 | 889 | 892 | 894 | 899 | 900 | 905 | 906 | 909 | 911 | 916 | 917 | 922 | 923 | 926 | 928 | 933 | 934 | 939 | 940 | 943 | 945 | 950 | 951 | 956 | 957 | 960 | 962 | 967 | 968 | 973 | 974 | 975 | 984 | 986 | 992 | 998 | 1004 | 1005 | 1014 | 1015 | 1024 | 1033 | 1036 | 1043 | 1044 | 1063 | 1067 | 1074 | 1075 | 1094 | 1103 | 1108 | 1117 | 1136 | 1140 | 1143 | 1152 | 1161 | 1164 | 1167 | 1169 | 1174 | 1175 | 1180 | 1181 | 1184 | 1186 | 1191 | 1192 | 1197 | 1198 | 1201 | 1203 | 1208 | 1209 | 1214 | 1215 | 1218 | 1220 | 1225 | 1226 | 1231 | 1232 | 1235 | 1237 | 1242 | 1243 | 1248 | 1249 | 1252 | 1254 | 1259 | 1260 | 1265 | 1266 | 1269 | 1271 | 1276 | 1277 | 1282 | 1283 | 1286 | 1288 | 1293 | 1294 | 1299 | 1300 | 1301 | 1310 | 1312 | 1318 | 1324 | 1330 | 1331 | 1340 | 1341 | 1350 | 1351 | 1354 | 1363 | 1382 | 1386 | 1389 | 1398 | 1407 | 1410 | 1413 | 1415 | 1420 | 1421 | 1426 | 1427 | 1430 | 1432 | 1437 | 1438 | 1443 | 1444 | 1447 | 1449 | 1454 | 1455 | 1460 | 1461 | 1464 | 1466 | 1471 | 1472 | 1477 | 1478 | 1481 | 1483 | 1488 | 1489 | 1494 | 1495 | 1498 | 1500 | 1505 | 1506 | 1511 | 1512 | 1515 | 1517 | 1522 | 1523 | 1528 | 1529 | 1532 | 1534 | 1539 | 1540 | 1545 | 1546 | 1547 | 1556 | 1558 | 1564 | 1570 | 1576 | 1577 | 1586 | 1587 | 1596 | 1597 | 1600 | 1609 | 1628 | 1632 | 1635 | 1644 | 1653 | 1656 | 1659 | 1661 | 1666 | 1667 | 1672 | 1673 | 1676 | 1678 | 1683 | 1684 | 1689 | 1690 | 1693 | 1695 | 1700 | 1701 | 1706 | 1707 | 1710 | 1712 | 1717 | 1718 | 1723 | 1724 | 1727 | 1729 | 1734 | 1735 | 1740 | 1741 | 1744 | 1746 | 1751 | 1752 | 1757 | 1758 | 1761 | 1763 | 1768 | 1769 | 1774 | 1775 | 1778 | 1780 | 1785 | 1786 | 1791 | 1792 | 1793 | 1802 | 1804 | 1810 | 1816 | 1822 | 1823 | 1832 | 1833 | 1842 | 1843 | 1844 | 1845 | -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Brian A. Mattern . 3 | * Copyright (C) 2015 David Beitey . 4 | * All Rights Reserved. 5 | * This file is licensed under the GPLv2+. 6 | * Please see COPYING for more information 7 | */ 8 | #import 9 | #import 10 | int main(int argc, char **argv) { 11 | @autoreleasepool { 12 | return UIApplicationMain(argc, argv, @"passwordstoreApplication", @"passwordstoreApplication"); 13 | } 14 | } 15 | 16 | // vim:ft=objc 17 | -------------------------------------------------------------------------------- /passwordstoreApplication.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Brian A. Mattern . 3 | * Copyright (C) 2015 David Beitey . 4 | * All Rights Reserved. 5 | * This file is licensed under the GPLv2+. 6 | * Please see COPYING for more information 7 | */ 8 | #import "PasswordsViewController.h" 9 | #import "PassDataController.h" 10 | 11 | #define PASS_DIR @"/var/mobile/.password-store" 12 | 13 | @interface passwordstoreApplication: UIApplication 14 | { 15 | UIWindow *_window; 16 | PasswordsViewController *_viewController; 17 | PassDataController *_entries; 18 | } 19 | @property (nonatomic, retain) UIWindow *window; 20 | @end 21 | 22 | @implementation passwordstoreApplication 23 | @synthesize window = _window; 24 | - (void)applicationDidFinishLaunching:(UIApplication *)application 25 | { 26 | _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 27 | _entries = [[PassDataController alloc] initWithPath:PASS_DIR]; 28 | 29 | _viewController = [[PasswordsViewController alloc] init]; 30 | _viewController.entries = _entries; 31 | 32 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:_viewController]; 33 | 34 | // Ensures app is able to change orientation; subviews don't work 35 | _window.rootViewController = navigationController; 36 | [_window makeKeyAndVisible]; 37 | } 38 | 39 | - (void)applicationWillTerminate:(UIApplication *)application 40 | { 41 | // remove passphrase on app exit for now 42 | // NSLog(@"App will terminate"); 43 | // [[PDKeychainBindings sharedKeychainBindings] removeObjectForKey:@"passphrase"]; 44 | } 45 | 46 | @end 47 | 48 | // vim:ft=objc 49 | -------------------------------------------------------------------------------- /screenshots/1_main_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidjb/pass-ios/e439d0641ec43b5701de6626d2e8fb54f6049a9a/screenshots/1_main_screen.png -------------------------------------------------------------------------------- /screenshots/2_subdir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidjb/pass-ios/e439d0641ec43b5701de6626d2e8fb54f6049a9a/screenshots/2_subdir.png -------------------------------------------------------------------------------- /screenshots/3_entry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidjb/pass-ios/e439d0641ec43b5701de6626d2e8fb54f6049a9a/screenshots/3_entry.png -------------------------------------------------------------------------------- /screenshots/4_gpg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidjb/pass-ios/e439d0641ec43b5701de6626d2e8fb54f6049a9a/screenshots/4_gpg.png --------------------------------------------------------------------------------