dpass 0.0.1.alpha07

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ # - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ ## v0.0.1alphaXX
2
+
3
+ * Early testing and development versions - DO NOT USE...
4
+
5
+ ## v0.0.1
6
+
7
+ * Initial release - COMING SOON...
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dpass.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Info Access LLC
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # WARNING!!! DO NOT USE! NEITHER COMPLETE OR WELL-TESTED!
2
+ Please check back soon a usable version...
3
+
4
+ # Dpass [![Build Status](https://secure.travis-ci.org/Spikels/dpass.png)](http://travis-ci.org/Spikels/dpass)
5
+
6
+ Application specific passwords derived from your secret master password using [PBKDF2](http://en.wikipedia.org/wiki/PBKDF2) from OpenSSL already installed on your computer.
7
+
8
+ ## Installation
9
+
10
+ Dpass currently requires Ruby 1.9.x but should soon work for all versions.
11
+
12
+ Install the gem:
13
+
14
+ $ gem install dpass --pre
15
+
16
+ You need a personal dpass salt file in your home directory (~/.dpass). The SAME salt file must be installed in the home directory on each machine you want to run dpass or else you will get different derived passwords. So if you already have one you use, copy any perviously created salt file to your home directory.
17
+
18
+ If this is the first time you are using dpass, go ahead and create a new personal salt file:
19
+
20
+ $ dpass -new_salt
21
+
22
+ ## Usage
23
+
24
+ $ dpass <application_1>[...<application_N>]
25
+
26
+ For example to generate a password for Gmail and Yahoo
27
+
28
+ $ dpass gmail yahoo
29
+
30
+ You will be asked for your master password then a password will be generated for each application specified.
31
+
32
+ ## Background
33
+
34
+ The theft of passwords from internet sites seems to be an [increasingly](http://press.linkedin.com/node/1212) [common](http://ycorpblog.com/2012/07/13/yahoo-0713201/) [occurance](http://us.blizzard.com/en-us/securityupdate.html). Combined with the common practice of using either the same or related passwords across this is a serious security risk.
35
+
36
+ Clearly you should have a independent and strong password for every site you visit. I recently took Dan Boneh's excellent Cryptography class on [Coursera](https://www.coursera.org/course/crypto) and learned that this well known problem, known as "password based key derivation", has standard solutions.
37
+
38
+ Inspired by a [broken idea](http://news.ycombinator.com/item?id=4373909) on Hacker News and ignoring warnings to never even think about building your own crypto I made dpass.
39
+
40
+ ## Other Password Managers
41
+
42
+ [LastPass](https://lastpass.com/), [KeePass](http://keepass.info/), [PwdHash](https://www.pwdhash.com/)
43
+
44
+ ## References
45
+
46
+ * Dan Boneh - Key Derivation Lecture - http://www.youtube.com/watch?v=ZorKf6IaP0Q
47
+ * PKCS #5 RFC - http://tools.ietf.org/html/rfc2898
48
+ * PBKDF2 Test Vectors RFC - http://tools.ietf.org/html/rfc6070
49
+ * OpenSSL Gem PKCS5 - http://www.ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/PKCS5.html
50
+
51
+ ## Contributing
52
+
53
+ 1. Fork it
54
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
55
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
56
+ 4. Push to the branch (`git push origin my-new-feature`)
57
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
data/TODO.md ADDED
@@ -0,0 +1,64 @@
1
+ # TODO
2
+
3
+
4
+ * General
5
+ * Make work under Ruby 1.8.x (must be using 1.9 syntax)
6
+
7
+
8
+ * Tests
9
+ * DONE-Add Travis CI
10
+ * Figure out how to mock the 2 file system tests
11
+ * Add tests to new code & test frist going forward!
12
+ * Test under Ruby 1.8.x (Travis)
13
+ * ??? - Test on Windows
14
+
15
+
16
+ * Command-line
17
+ * DONE-Allow multiple applications (i.e. dpass gmail yahoo)
18
+ * Add --init command (test install, new-salt & print basic instructions)
19
+ * Add --test command (check ruby version, openssl, ...)
20
+
21
+
22
+ * Create salt file (~/.dpass)
23
+ * DONE-Check if file already exists. If so abort with message to delete manually if you really want a NEW salt file.
24
+ * DONE-Generate 128-bit random salt (32 hex chars)
25
+ * DONE-Save to file ~/.dpass
26
+ * DONE-Set permission to 600
27
+ * Handle File.open errors
28
+ * Handle File.chown errors
29
+ * ???-Append new salts so old salts are not lost (w/ create date?)
30
+
31
+
32
+ * Validate salt file
33
+ * DONE - Warn if salt file permission is not 0600 (Is this OS specific?)
34
+ * Check salt is valid length
35
+ * Check salt is hex character set only
36
+ * ???-Check that salt seems random (how?)
37
+
38
+ * Read salt file (~/.dpass)
39
+ * DONE-Check that file exists. If not abort with message to copy existing salt or create new salt if you REALLY want to.
40
+ * DONE-Read salt file
41
+ * DONE-Warn if salt file permission is not 0600 (Is this OS specific?)
42
+
43
+ * Master password
44
+ * DONE-Remove highline dependency
45
+ * DONE-Clear from memory ASAP string.replace
46
+ * Allow delete/backspace when entering master password
47
+
48
+ * Settings
49
+ * Remove calculated settings from setting.rb (put in dpass.rb)
50
+ * Check HASH_ITER is reasonable before use
51
+ * Check PASS_LENGTH is reasonable before use
52
+ * Check SYMBOL_COUNT is reasonable before use
53
+ * Check SYMBOLS.length is reasonable before use
54
+ * Check SYMBOLS is reasonable before use
55
+
56
+ * Input validation
57
+ * DONE-Warn if master pass length < WARN_MASTER_PASS_LENGTH
58
+ * ???-Fail if master pass length < MIN_MASTER_PASS_LENGTH
59
+
60
+ * Output password
61
+ * DONE-Fixed length (PASS_LENGTH_SYMBOLS) - gen too long then truncate
62
+ * DONE-Copy password to clipboard (only for single passwords)
63
+ * DONE-Erase password from clipboard after WIPE_CLIPBOARD_DELAY seconds
64
+ * Guarantee specified bits of randomness (SYMBOL_COUNT, PASS_LENGTH)
data/bin/dpass ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ require 'dpass'
3
+ require 'trollop'
4
+
5
+ opts = Trollop::options do
6
+ version Dpass::VERSION
7
+ banner <<-EOS
8
+ dpass derives application specific passwords from your master password
9
+ Usage: dpass [options] <app_name> [<app_name_2>...<app_name_N>]
10
+ where [options] are:
11
+ EOS
12
+ opt :new_salt, "Generate new salt file (~/.dpass)"
13
+ end
14
+
15
+ if opts[:new_salt]
16
+ Dpass.new_salt
17
+ else
18
+ Trollop::die "must supply at least one app_name" if ARGV.length == 0
19
+ Dpass.derive
20
+ end
data/dpass.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/dpass/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Spikels"]
6
+ gem.email = ["spikels@infoaccess.org"]
7
+ gem.description = %q{Derive application specific passwords from secret master password using PBKDF2 in OpenSSL}
8
+ gem.summary = %q{Derive application specific passwords}
9
+ gem.homepage = "https://github.com/Spikels/dpass"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = ['dpass']
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "dpass"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Dpass::VERSION
17
+
18
+ gem.add_development_dependency('rspec')
19
+ gem.add_development_dependency('rake')
20
+ gem.add_dependency('trollop')
21
+ gem.add_dependency('clipboard')
22
+ end
data/lib/dpass.rb ADDED
@@ -0,0 +1,120 @@
1
+ require 'dpass/version'
2
+ require 'dpass/settings'
3
+ require 'openssl'
4
+ require 'securerandom'
5
+ require 'clipboard'
6
+
7
+ SALT = Dpass::SALT_PATH
8
+
9
+ module Dpass
10
+
11
+ def self.derive
12
+ verify_settings()
13
+ salt = read_salt()
14
+ master_pass = get_password_masked()
15
+ ARGV.each do |app_name|
16
+ pass_bytes = Dpass.derive_raw_hex(master_pass,
17
+ salt+app_name,
18
+ Dpass::HASH_ITER,
19
+ Dpass::PASS_LENGTH_BYTES)
20
+ pass_syms = Dpass.rebase_bytes(pass_bytes, Dpass::SYMBOL_COUNT)
21
+ pass = pass_syms.map {|c| Dpass::SYMBOLS[c]}.join
22
+ pass = pass[0..(PASS_LENGTH_SYMBOLS-1)]
23
+ puts "#{app_name}: #{pass}"
24
+ Clipboard.copy pass
25
+ delay_wipe_clipboard()
26
+ end
27
+ # Overwrite master password in memory with random bytes
28
+ master_pass.replace SecureRandom.random_bytes(master_pass.length)
29
+ end
30
+
31
+ def self.rebase_bytes(hex_string, new_base)
32
+ num = hex_string.to_i(16)
33
+ result = []
34
+ while num > 0
35
+ result.unshift num % new_base
36
+ num /= new_base
37
+ end
38
+ result.shift #Remove biased-downward first digit (not always needed)
39
+ return result
40
+ end
41
+
42
+ def self.get_password_masked(mask='*')
43
+ buffering = $stdout.sync
44
+ $stdout.sync = true
45
+ stty_settings = %x[stty -g]
46
+ begin
47
+ %x[stty -echo]
48
+ %x[stty -icanon]
49
+ print 'Enter Master Password: '
50
+ password = ""
51
+ while ( char = $stdin.getc ) != "\n" # break after [Enter]
52
+ putc mask
53
+ password << char
54
+ end
55
+ ensure
56
+ %x[stty #{stty_settings}]
57
+ $stdout.sync = buffering
58
+ puts
59
+ end
60
+ password.chomp
61
+ if password.length < WARN_MASTER_PASS_LENGTH
62
+ puts "WARNING: Your master password should be at least #{WARN_MASTER_PASS_LENGTH} characters long."
63
+ end
64
+ return password
65
+ end
66
+
67
+ def self.derive_raw_hex(pass, salt, iter, keylen)
68
+ OpenSSL::PKCS5.pbkdf2_hmac_sha1(pass,salt,iter,keylen).unpack('H*')[0]
69
+ end
70
+
71
+ def self.generate_salt
72
+ SecureRandom.hex(Dpass::SALT_SIZE)
73
+ end
74
+
75
+ def self.read_salt
76
+ Dpass.verify_salt
77
+ File.open(SALT, 'rb') {|f| f.read.strip}
78
+ end
79
+
80
+ def self.new_salt
81
+ if File.exists?(SALT)
82
+ raise StandardError, "ERROR: dpass salt file (#{SALT}) already exists. A NEW SALT FILE WILL BREAK ALL EXISTING DERIVED PASSWORDS! If you really want this, you must manually delete or move the existing salt file."
83
+ else
84
+ new_value = Dpass.generate_salt
85
+ File.open(SALT, 'wb', 0600) {|f| f.write new_value + "\n"}
86
+ # Verify salt file
87
+ Dpass.verify_salt(new_value)
88
+ puts "Generated new random salt: #{new_value}"
89
+ puts "Saved to #{SALT}, set permission (0600) and verfied"
90
+ end
91
+ end
92
+
93
+ def self.verify_salt(expected_value = nil)
94
+ if !File.exists?(SALT)
95
+ raise StandardError, "ERROR: salt file (#{SALT}) does not exist. Use 'dpass salt' command to create a new one"
96
+ end
97
+ currrent_salt = File.open(SALT, 'rb')
98
+ if expected_value && expected_value != currrent_salt
99
+ raise StandardError, "ERROR: salt in file does not match new salt just create"
100
+ end
101
+ # WARNING: PERMISSION BITS ARE PROBABLY OS SPECIFIC...
102
+ if (mode = File.stat(SALT).mode) != 33152 # 100600 in octal
103
+ puts "WARNING: salt file (#{SALT}) permission not strict enough (#{sprintf("%o",mode)[2..-1]}). Use 'chmod 0600 ~/.dpass' limit access only to user."
104
+ end
105
+ ### Check length
106
+ ### Check character set
107
+ end
108
+
109
+ def self.verify_settings
110
+ # TODO-Check: HASH_ITER, PASS_LENGTH, SYMBOL_COUNT, SYMBOLS.length
111
+ end
112
+
113
+ def self.delay_wipe_clipboard
114
+ job1 = fork do
115
+ sleep WIPE_CLIPBOARD_DELAY
116
+ Clipboard.clear
117
+ end
118
+ Process.detach(job1)
119
+ end
120
+ end
@@ -0,0 +1,26 @@
1
+ module Dpass
2
+ SYMBOL_SETS = {
3
+ 'Printable'=>"!\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
4
+ 'Selectable'=>"+-./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\\_abcdefghijklmnopqrstuvwxyz~",
5
+ 'Alphanumeric'=>"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
6
+ 'Alphabet'=>"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
7
+ 'AlphanumericUppercase'=>"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
8
+ 'AlphanumericLowercase'=>"0123456789abcdefghijklmnopqrstuvwxyz",
9
+ 'AlphabetUppercase'=>"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
10
+ 'AlphabetLowercase'=>"abcdefghijklmnopqrstuvwxyz",
11
+ 'HexadecimalUppercase'=>"0123456789ABCDEF",
12
+ 'HexadecimalLowercase'=>"0123456789abcdef",
13
+ 'Numerals'=>"0123456789"}
14
+ SYMBOL_SET = 'Alphanumeric'
15
+ SYMBOLS = SYMBOL_SETS[SYMBOL_SET] # *** CALC - NOT SETTING! ***
16
+ SYMBOL_COUNT = SYMBOLS.length # *** CALC - NOT SETTING! ***
17
+ HASH_ITER = 4096
18
+ PASS_LENGTH_SYMBOLS = 14 # Base SYMBOL_COUNT
19
+ # NEED TO CALCULATE REQUIRED BYTE LENGTH (ROUNDING UP!)
20
+ PASS_LENGTH_BYTES = 12 # Base 256 *** CALC - NOT SETTING! ***
21
+ SALT_PATH = ENV['HOME']+'/.dpass'
22
+ SALT_SIZE = 16 # in bytes - hex is double this
23
+ MIN_MASTER_PASS_LENGTH = 8
24
+ WARN_MASTER_PASS_LENGTH = 10
25
+ WIPE_CLIPBOARD_DELAY = 30
26
+ end
@@ -0,0 +1,3 @@
1
+ module Dpass
2
+ VERSION = "0.0.1.alpha07"
3
+ end
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ TEST_VECTORS = [
5
+ ['password','salt',1,20,'0c60c80f961f0e71f3a9b524af6012062fe037a6'],
6
+ ['password','salt',2,20,'ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957'],
7
+ ['password','salt',4096,20,'4b007901b765489abead49d926f721d065a429c1'],
8
+ # ['password','salt',16777216,20,'eefe3d61cd4da4e4e9945b3d6ba2158c2634e984'],
9
+ ['passwordPASSWORDpassword','saltSALTsaltSALTsaltSALTsaltSALTsalt',4096, 25,'3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038'],
10
+ ["pass\0word","sa\0lt",4096,16,'56fa6aa75548099dcc37d7f03425e0c3']
11
+ ]
12
+
13
+ describe Dpass do
14
+ describe "Verify RFC 6070 test vectors" do
15
+ i = 0
16
+ TEST_VECTORS.each do |data|
17
+ i += 1
18
+ it "matches test vector #{i}" do
19
+ Dpass.derive_raw_hex(data[0],data[1],data[2],data[3]).should eq(data[4])
20
+ end
21
+ end
22
+ end
23
+
24
+ describe "Reading salt file" do
25
+ it "should raise error if file does not exist" do
26
+ File.stub!(:exists?).and_return(false)
27
+ expect{Dpass.read_salt}.to raise_error(StandardError)
28
+ end
29
+ # This only works with actual salt file. Need to figure out how to
30
+ # mock files...
31
+ # it "should have correct length" do
32
+ # Dpass.read_salt.length.should eq(32)
33
+ # end
34
+ # it "should contain only hexidecimal characters" do
35
+ # Dpass.read_salt.gsub(/[0-9a-f]/,'').length.should eq(0)
36
+ # end
37
+ end
38
+
39
+ describe "Generating new salt file" do
40
+ describe "generate a random salt string" do
41
+ it "should be non-nil" do
42
+ Dpass.generate_salt.length.should eq(32)
43
+ end
44
+ it "should contain only hexidecimal characters" do
45
+ Dpass.generate_salt.gsub(/[0-9a-f]/,'').length.should eq(0)
46
+ end
47
+ end
48
+ it "should not change an existing salt file" do
49
+ File.stub!(:exists?).and_return(true)
50
+ expect{Dpass.new_salt}.to raise_error(StandardError)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1 @@
1
+ require 'dpass'
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dpass
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.alpha07
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Spikels
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: trollop
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: clipboard
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Derive application specific passwords from secret master password using
79
+ PBKDF2 in OpenSSL
80
+ email:
81
+ - spikels@infoaccess.org
82
+ executables:
83
+ - dpass
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - .gitignore
88
+ - .rspec
89
+ - .travis.yml
90
+ - CHANGELOG.md
91
+ - Gemfile
92
+ - LICENSE
93
+ - README.md
94
+ - Rakefile
95
+ - TODO.md
96
+ - bin/dpass
97
+ - dpass.gemspec
98
+ - lib/dpass.rb
99
+ - lib/dpass/settings.rb
100
+ - lib/dpass/version.rb
101
+ - spec/dpass_spec.rb
102
+ - spec/spec_helper.rb
103
+ homepage: https://github.com/Spikels/dpass
104
+ licenses: []
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>'
119
+ - !ruby/object:Gem::Version
120
+ version: 1.3.1
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 1.8.24
124
+ signing_key:
125
+ specification_version: 3
126
+ summary: Derive application specific passwords
127
+ test_files:
128
+ - spec/dpass_spec.rb
129
+ - spec/spec_helper.rb