├── .gitignore ├── bin └── 1password2pass ├── lib ├── insert.js ├── index.js └── parse.js ├── README.md ├── package.json ├── LICENSE └── test └── decrypt1password.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | node_modules 4 | test/* 5 | npm-debug.log 6 | -------------------------------------------------------------------------------- /bin/1password2pass: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | require('../lib')(); 6 | -------------------------------------------------------------------------------- /lib/insert.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var execSync = require('child_process').execSync; 5 | 6 | module.exports = function(creds) { 7 | 8 | creds.forEach(function(c) { 9 | var cmd = ''; 10 | if (c.username) { 11 | cmd = 'pass insert -e "' + c.location + '/' + c.username + '"'; 12 | } else { 13 | cmd = 'pass insert -e "Passwords/' + c.location + '"'; 14 | } 15 | execSync(cmd, {input: c.password}); 16 | console.log('INFO: ' + (c.username || 'Password entry') + ' at ' + c.location + ' imported.'); 17 | }); 18 | 19 | }; 20 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var argv = require('minimist')(process.argv.slice(2)); 4 | 5 | var fs = require('fs'); 6 | 7 | var parse = require('./parse'); 8 | var insert = require('./insert'); 9 | 10 | module.exports = function() { 11 | 12 | if (argv._[0]) { 13 | 14 | try { 15 | var onepifdata = fs.readFileSync(argv._[0], 'utf8'); 16 | } catch (e) { 17 | console.error(e.message); 18 | process.exit(1); 19 | } 20 | 21 | insert(parse(onepifdata)); 22 | 23 | } else { 24 | printHelp(); 25 | } 26 | 27 | }; 28 | 29 | function printHelp() { 30 | console.log('Usage: 1password2pass [path/to/your.1pif]'); 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 1password2pass 2 | ============== 3 | 4 | 1password2pass is a tool to import passwords from 1password keychain to pass (passwordstore.org) 5 | 6 | ### Instruction 7 | 8 | 1. `npm install -g 1password2pass` 9 | 2. Export your 1password data to `.1pif` 10 | 3. `1password2pass /path/to/export.1pif/data.1pif` 11 | 12 | Currently only passwords and login credentials will be imported. 13 | 14 | ### Why 15 | 16 | AgileBits seems refusing to develop a Linux version of 1Password with 'reasons', so I decided to switch to `pass`. 17 | 18 | However the [1password2pass.rb](http://git.zx2c4.com/password-store/tree/contrib/importers/1password2pass.rb) yield error on my Mac. I have to wrote one for myself. 19 | 20 | ### License 21 | 22 | MIT. 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "1password2pass", 3 | "version": "0.0.3", 4 | "description": "1password2pass is a tool to import passwords from 1password keychain to pass (passwordstore.org)", 5 | "author": "Phoenix Nemo ", 6 | "contributors": [ 7 | "Steve Streza " 8 | ], 9 | "bin": { 10 | "minori": "./bin/1password2pass" 11 | }, 12 | "main": "lib/index", 13 | "repository": { 14 | "type": "git", 15 | "url": "git://github.com/phoenixlzx/1password2pass.git" 16 | }, 17 | "homepage": "https://github.com/phoenixlzx/1password2pass", 18 | "bugs": { 19 | "url": "https://github.com/phoenixlzx/1password2pass/issues" 20 | }, 21 | "keywords": [ 22 | "1password", 23 | "pass" 24 | ], 25 | "dependencies": { 26 | "minimist": "1.1.1" 27 | }, 28 | "preferGlobal": true, 29 | "private": false, 30 | "license": "MIT" 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Phoenix Nemo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /test/decrypt1password.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | 5 | var onepifdata = fs.readFileSync('./data.1pif', 'utf8'); 6 | 7 | var items = onepifdata.split(/\n\*\*\*.*?\*\*\*\n/); 8 | 9 | 10 | items.forEach(function(item, index) { 11 | if (item) { 12 | var i = JSON.parse(item); 13 | if (i.secureContents) { 14 | var f = { 15 | location: i.locationKey, 16 | username: '', 17 | password: '' 18 | }; 19 | if (i.secureContents.password) { 20 | f.password = i.secureContents.password; 21 | console.log(JSON.stringify(f)); 22 | } 23 | if (i.secureContents.fields) { 24 | i.secureContents.fields.forEach(function(field) { 25 | // walk through every obj to find username and password 26 | if (field.designation === 'username') { 27 | f.username = field.value; 28 | } 29 | if (field.designation === 'password') { 30 | f.password = field.value; 31 | } 32 | }); 33 | console.log(JSON.stringify(f)); 34 | } 35 | console.log(index); 36 | } 37 | } 38 | }); 39 | -------------------------------------------------------------------------------- /lib/parse.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(onepifdata) { 4 | 5 | var items = onepifdata.split(/\*\*\*[a-zA-Z0-9\-]{30,40}?\*\*\*/gm); 6 | 7 | var creds = []; 8 | var count_password = 0; 9 | var count_credential = 0; 10 | 11 | items.forEach(function(item) { 12 | if (item) { 13 | var i = JSON.parse(item.trim()); 14 | var f = { 15 | location: i.locationKey, 16 | username: '', 17 | password: '' 18 | }; 19 | 20 | if (i.typeName === 'passwords.Password') { 21 | f.password = i.secureContents.password; 22 | creds.push(f); 23 | count_password++; 24 | console.log('INFO: Password for ' + i.locationKey + ' collected.'); 25 | } else if (i.typeName === 'webforms.WebForm') { 26 | i.secureContents.fields.forEach(function(field) { 27 | 28 | if (field.designation === 'username') { 29 | f.username = field.value; 30 | } 31 | if (field.designation === 'password') { 32 | f.password = field.value; 33 | } 34 | 35 | }); 36 | creds.push(f); 37 | count_credential++; 38 | console.log('INFO: Credential for ' + i.locationKey + ' collected.'); 39 | } else { 40 | console.log('WARN: Unknown type "' + i.typeName + '" item skipped.'); 41 | } 42 | } 43 | }); 44 | 45 | console.log('INFO: ' + items.length + ' items parsed\n' + 46 | 'INFO: ' + count_credential + ' credentials collected\n' + 47 | 'INFO: ' + count_password + ' passwords collected\n' + 48 | 'INFO: Total ' + (count_credential + count_password) + ' collected. Now continuing...'); 49 | 50 | return creds; 51 | 52 | }; 53 | --------------------------------------------------------------------------------