passw3rd 0.1.3 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +10 -0
- data/README.md +4 -0
- data/bin/passw3rd +1 -1
- data/lib/passw3rd.rb +0 -1
- data/lib/passw3rd/password_client.rb +3 -5
- data/lib/passw3rd/password_service.rb +65 -14
- data/lib/passw3rd/version.rb +1 -1
- data/spec/password_service_spec.rb +4 -0
- data/test/password_service_test.rb +16 -18
- metadata +3 -4
- data/lib/passw3rd/key_loader.rb +0 -51
- data/spec/config_spec.rb +0 -3
data/History.txt
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
=== 0.2.1 / 2011-11-8
|
|
2
|
+
|
|
3
|
+
Added option to fail silently. Pretty common need in a Rails environment.
|
|
4
|
+
|
|
5
|
+
=== 0.2.0 / 2011-11-5
|
|
6
|
+
|
|
7
|
+
Added acceptance tests as a contract for future features.
|
|
8
|
+
Collapsed the KeyLoader class, eliminated the KeyPair class. If for some reason you referenced those classes, things will break. But you wouldn't do that, would you?
|
|
9
|
+
More testing goodness.
|
|
10
|
+
|
|
1
11
|
=== 0.1.3 / 2011-11-4
|
|
2
12
|
|
|
3
13
|
From key rotation, arose cipher changing
|
data/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
+
It's only failing because it can't find the system ruby. If you have a solution, checkout bin/passw3rd please :)
|
|
4
|
+
|
|
5
|
+
It will still work on your machine.
|
|
6
|
+
|
|
3
7
|
Introduction
|
|
4
8
|
------------------------------------------------------------------------------
|
|
5
9
|
|
data/bin/passw3rd
CHANGED
data/lib/passw3rd.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require 'base64'
|
|
2
|
-
require 'openssl'
|
|
3
1
|
require 'optparse'
|
|
4
2
|
|
|
5
3
|
module Passw3rd
|
|
@@ -25,7 +23,7 @@ module Passw3rd
|
|
|
25
23
|
|
|
26
24
|
opts.on('-k', '--key-dir KEY_PATH', 'Use the keys specificed in this directory for encryption or decryption (default is ~/)') do |opt|
|
|
27
25
|
key_path = opt
|
|
28
|
-
|
|
26
|
+
unless File.directory?(File.expand_path(key_path))
|
|
29
27
|
raise "#{opt} must be a directory"
|
|
30
28
|
end
|
|
31
29
|
end
|
|
@@ -57,13 +55,13 @@ module Passw3rd
|
|
|
57
55
|
|
|
58
56
|
# generate key/IV
|
|
59
57
|
if gen_key_path
|
|
60
|
-
::Passw3rd::
|
|
58
|
+
::Passw3rd::PasswordService.create_key_iv_file(gen_key_path)
|
|
61
59
|
puts "generated keys in #{gen_key_path}"
|
|
62
60
|
end
|
|
63
61
|
|
|
64
62
|
# decrypt password_file using the key/IV in key_path
|
|
65
63
|
if mode == "decrypt"
|
|
66
|
-
decrypted =::Passw3rd::PasswordService.get_password(password_file, key_path)
|
|
64
|
+
decrypted =::Passw3rd::PasswordService.get_password(password_file, :key_path => key_path)
|
|
67
65
|
puts "The password is: #{decrypted}"
|
|
68
66
|
end
|
|
69
67
|
|
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
require 'open-uri'
|
|
2
2
|
|
|
3
3
|
module Passw3rd
|
|
4
|
+
KEY_FILE = ".passw3rd-encryptionKey"
|
|
5
|
+
IV_FILE = ".passw3rd-encryptionIV"
|
|
6
|
+
# more preferred ciphers first
|
|
7
|
+
APPROVED_CIPHERS = %w{aes-256-cbc aes-256-cfb aes-128-cbc aes-128-cfb}
|
|
8
|
+
|
|
4
9
|
class PasswordService
|
|
10
|
+
|
|
5
11
|
class << self
|
|
6
12
|
attr_writer :password_file_dir
|
|
7
13
|
def password_file_dir
|
|
8
|
-
@password_file_dir
|
|
14
|
+
defined?(@password_file_dir) ? @password_file_dir : ENV.fetch("HOME")
|
|
9
15
|
end
|
|
10
16
|
|
|
11
17
|
attr_writer :key_file_dir
|
|
12
18
|
def key_file_dir
|
|
13
|
-
@key_file_dir
|
|
19
|
+
defined?(@key_file_dir) ? @key_file_dir : ENV.fetch("HOME")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def cipher_name= (cipher_name)
|
|
23
|
+
raise "Hey man, you can only use #{APPROVED_CIPHERS}, you supplied #{cipher_name}" if cipher_name.nil? || !APPROVED_CIPHERS.include?(cipher_name)
|
|
24
|
+
@cipher_name = cipher_name
|
|
14
25
|
end
|
|
15
26
|
|
|
16
|
-
attr_writer :cipher_name
|
|
17
27
|
def cipher_name
|
|
18
|
-
@cipher_name
|
|
28
|
+
defined?(@cipher_name) ? @cipher_name : APPROVED_CIPHERS.first
|
|
19
29
|
end
|
|
20
30
|
end
|
|
21
31
|
|
|
@@ -23,10 +33,12 @@ module Passw3rd
|
|
|
23
33
|
instance_eval &block
|
|
24
34
|
end
|
|
25
35
|
|
|
26
|
-
def self.get_password (password_file,
|
|
36
|
+
def self.get_password (password_file, options = {:key_path => key_file_dir, :force => false})
|
|
27
37
|
uri = _parse_uri(password_file)
|
|
28
|
-
encoded_password = Base64.decode64(open(uri
|
|
29
|
-
decrypt(encoded_password, key_path)
|
|
38
|
+
encoded_password = Base64.decode64(open(uri) { |f| f.read })
|
|
39
|
+
decrypt(encoded_password, options[:key_path])
|
|
40
|
+
rescue
|
|
41
|
+
raise ArgumentError, "Could not decrypt passw3rd file" if options[:force]
|
|
30
42
|
end
|
|
31
43
|
|
|
32
44
|
def self.write_password_file(password, output_path, key_path = key_file_dir)
|
|
@@ -60,7 +72,7 @@ module Passw3rd
|
|
|
60
72
|
raise err
|
|
61
73
|
end
|
|
62
74
|
end
|
|
63
|
-
|
|
75
|
+
|
|
64
76
|
def self.rotate_keys(args = {})
|
|
65
77
|
unless args.empty?
|
|
66
78
|
::Passw3rd::PasswordService.configure do |c|
|
|
@@ -80,28 +92,67 @@ module Passw3rd
|
|
|
80
92
|
|
|
81
93
|
::Passw3rd::PasswordService.cipher_name = args[:new_cipher] if args[:new_cipher]
|
|
82
94
|
|
|
83
|
-
path =
|
|
95
|
+
path = self.create_key_iv_file
|
|
84
96
|
puts "Wrote new keys to #{path}"
|
|
85
97
|
|
|
86
98
|
passwords.each do |password|
|
|
87
99
|
full_path = File.join(::Passw3rd::PasswordService.password_file_dir, password[:file])
|
|
88
|
-
FileUtils::rm(full_path)
|
|
89
100
|
::Passw3rd::PasswordService.write_password_file(password[:clear_password], password[:file])
|
|
90
101
|
puts "Wrote new password to #{full_path}"
|
|
91
102
|
end
|
|
92
103
|
end
|
|
93
104
|
|
|
105
|
+
def self.create_key_iv_file(path=nil)
|
|
106
|
+
unless path
|
|
107
|
+
path = ::Passw3rd::PasswordService.key_file_dir || ENV['HOME']
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# d'oh!
|
|
111
|
+
cipher = OpenSSL::Cipher::Cipher.new(::Passw3rd::PasswordService.cipher_name)
|
|
112
|
+
iv = cipher.random_iv
|
|
113
|
+
key = cipher.random_key
|
|
114
|
+
|
|
115
|
+
begin
|
|
116
|
+
File.open(key_path, 'w') {|f| f.write(key.unpack("H*").join) }
|
|
117
|
+
File.open(iv_path, 'w') {|f| f.write(iv.unpack("H*").join) }
|
|
118
|
+
rescue
|
|
119
|
+
puts "Couldn't write key/IV to #{path}\n"
|
|
120
|
+
raise $!
|
|
121
|
+
end
|
|
122
|
+
path
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.key_path
|
|
126
|
+
File.join(::Passw3rd::PasswordService.key_file_dir, KEY_FILE)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def self.iv_path
|
|
130
|
+
File.join(::Passw3rd::PasswordService.key_file_dir, IV_FILE)
|
|
131
|
+
end
|
|
132
|
+
|
|
94
133
|
protected
|
|
134
|
+
|
|
135
|
+
def self.load_key(path=::Passw3rd::PasswordService.key_file_dir)
|
|
136
|
+
begin
|
|
137
|
+
key = IO.readlines(File.expand_path(self.key_path))[0]
|
|
138
|
+
iv = IO.readlines(self.iv_path)[0]
|
|
139
|
+
rescue Errno::ENOENT
|
|
140
|
+
puts "Couldn't read key/iv from #{path}. Have they been generated?\n"
|
|
141
|
+
raise $!
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
{:key => [key].pack("H*"), :iv => [iv].pack("H*")}
|
|
145
|
+
end
|
|
95
146
|
|
|
96
147
|
def self.cipher_setup(method, key_path)
|
|
97
|
-
pair =
|
|
148
|
+
pair = self.load_key(key_path)
|
|
98
149
|
cipher = OpenSSL::Cipher::Cipher.new(cipher_name)
|
|
99
150
|
cipher.send(method)
|
|
100
|
-
cipher.key = pair
|
|
101
|
-
cipher.iv = pair
|
|
151
|
+
cipher.key = pair[:key]
|
|
152
|
+
cipher.iv = pair[:iv]
|
|
102
153
|
cipher
|
|
103
154
|
end
|
|
104
|
-
|
|
155
|
+
|
|
105
156
|
def self._parse_uri password_file
|
|
106
157
|
unless (password_file =~ URI::regexp(['ftp', 'http', 'https', 'file'])).nil?
|
|
107
158
|
URI.parse(password_file)
|
data/lib/passw3rd/version.rb
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe ::Passw3rd::PasswordService do
|
|
4
|
+
it "restricts assigning ciphers to just a select few" do
|
|
5
|
+
lambda { ::Passw3rd::PasswordService.cipher_name = "aes-256-donkey" }.should raise_error
|
|
6
|
+
end
|
|
7
|
+
|
|
4
8
|
describe "#_parse_uri" do
|
|
5
9
|
it "detects uris" do
|
|
6
10
|
::Passw3rd::PasswordService._parse_uri("http://example.com").should be_is_a URI::HTTP
|
|
@@ -6,12 +6,10 @@ require File.expand_path('../../lib/passw3rd.rb', __FILE__)
|
|
|
6
6
|
|
|
7
7
|
class PasswordServiceTest < Test::Unit::TestCase
|
|
8
8
|
def setup
|
|
9
|
-
random_num = sudorandumb
|
|
10
9
|
@random_string = sudorandumb
|
|
11
|
-
|
|
12
|
-
::Passw3rd::
|
|
13
|
-
::Passw3rd::PasswordService.
|
|
14
|
-
::Passw3rd::PasswordService.cipher_name = 'aes-256-cbc'
|
|
10
|
+
::Passw3rd::PasswordService.key_file_dir = Dir.tmpdir
|
|
11
|
+
::Passw3rd::PasswordService.password_file_dir = Dir.tmpdir
|
|
12
|
+
::Passw3rd::PasswordService.create_key_iv_file
|
|
15
13
|
end
|
|
16
14
|
|
|
17
15
|
def test_enc_dec
|
|
@@ -36,27 +34,27 @@ class PasswordServiceTest < Test::Unit::TestCase
|
|
|
36
34
|
end
|
|
37
35
|
|
|
38
36
|
def test_set_and_get_password_custom_dir
|
|
39
|
-
|
|
37
|
+
dir = "#{Dir.tmpdir}/#{sudorandumb}"
|
|
38
|
+
|
|
39
|
+
FileUtils.mkdir_p(dir)
|
|
40
|
+
::Passw3rd::PasswordService.password_file_dir = dir
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
password_file_path = ::Passw3rd::PasswordService.write_password_file(@random_string, "test2")
|
|
43
|
+
assert_match(Regexp.new(dir), password_file_path)
|
|
44
|
+
|
|
42
45
|
decrypted = ::Passw3rd::PasswordService.get_password("test2")
|
|
43
46
|
assert_equal(@random_string, decrypted)
|
|
47
|
+
FileUtils.rm_rf(dir)
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
def test_configure_with_block
|
|
47
51
|
::Passw3rd::PasswordService.configure do |c|
|
|
48
|
-
c.password_file_dir =
|
|
49
|
-
c.
|
|
52
|
+
c.password_file_dir = Dir.tmpdir
|
|
53
|
+
c.key_file_dir = Dir.tmpdir
|
|
54
|
+
c.cipher_name = ::Passw3rd::APPROVED_CIPHERS.first
|
|
50
55
|
end
|
|
51
|
-
assert_equal(::Passw3rd::PasswordService.password_file_dir,
|
|
52
|
-
assert_equal(::Passw3rd::PasswordService.cipher_name,
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def test_gen_key
|
|
56
|
-
enc = ::Passw3rd::PasswordService.encrypt(@random_string)
|
|
57
|
-
dec = ::Passw3rd::PasswordService.decrypt(enc)
|
|
58
|
-
|
|
59
|
-
assert_equal(@random_string, dec)
|
|
56
|
+
assert_equal(::Passw3rd::PasswordService.password_file_dir, Dir.tmpdir)
|
|
57
|
+
assert_equal(::Passw3rd::PasswordService.cipher_name, ::Passw3rd::APPROVED_CIPHERS.first)
|
|
60
58
|
end
|
|
61
59
|
|
|
62
60
|
def sudorandumb
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: passw3rd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2011-11-
|
|
12
|
+
date: 2011-11-08 00:00:00.000000000Z
|
|
13
13
|
dependencies: []
|
|
14
14
|
description: Generate a key/iv file, generate passwords, and store encrypted files
|
|
15
15
|
in source control, keep the key/iv safe!
|
|
@@ -24,14 +24,12 @@ files:
|
|
|
24
24
|
- LICENSE
|
|
25
25
|
- EXAMPLES.md
|
|
26
26
|
- History.txt
|
|
27
|
-
- lib/passw3rd/key_loader.rb
|
|
28
27
|
- lib/passw3rd/password_client.rb
|
|
29
28
|
- lib/passw3rd/password_service.rb
|
|
30
29
|
- lib/passw3rd/version.rb
|
|
31
30
|
- lib/passw3rd.rb
|
|
32
31
|
- bin/passw3rd
|
|
33
32
|
- test/password_service_test.rb
|
|
34
|
-
- spec/config_spec.rb
|
|
35
33
|
- spec/password_service_spec.rb
|
|
36
34
|
- spec/spec_helper.rb
|
|
37
35
|
homepage: https://github.com/oreoshake/passw3rd
|
|
@@ -59,3 +57,4 @@ signing_key:
|
|
|
59
57
|
specification_version: 3
|
|
60
58
|
summary: A simple "keep the passwords out of source code and config files".
|
|
61
59
|
test_files: []
|
|
60
|
+
has_rdoc:
|
data/lib/passw3rd/key_loader.rb
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
require 'openssl'
|
|
2
|
-
module Passw3rd
|
|
3
|
-
class KeyLoader
|
|
4
|
-
KEY_FILE = "/.passw3rd-encryptionKey"
|
|
5
|
-
IV_FILE = "/.passw3rd-encryptionIV"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def self.load(path=nil)
|
|
9
|
-
if path.nil?
|
|
10
|
-
path = ::Passw3rd::PasswordService.key_file_dir
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
begin
|
|
14
|
-
key = IO.readlines(File.expand_path(path + KEY_FILE))[0]
|
|
15
|
-
iv = IO.readlines(File.expand_path(path + IV_FILE))[0]
|
|
16
|
-
rescue Errno::ENOENT
|
|
17
|
-
puts "Couldn't read key/iv from #{path}. Have they been generated?\n"
|
|
18
|
-
raise $!
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
pair = KeyIVPair.new
|
|
22
|
-
pair.key = [key].pack("H*")
|
|
23
|
-
pair.iv = [iv].pack("H*")
|
|
24
|
-
pair
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def self.create_key_iv_file(path=nil)
|
|
28
|
-
unless path
|
|
29
|
-
path = ::Passw3rd::PasswordService.key_file_dir || ENV['HOME']
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# d'oh!
|
|
33
|
-
cipher = OpenSSL::Cipher::Cipher.new(::Passw3rd::PasswordService.cipher_name)
|
|
34
|
-
iv = cipher.random_iv
|
|
35
|
-
key = cipher.random_key
|
|
36
|
-
|
|
37
|
-
begin
|
|
38
|
-
File.open(path + KEY_FILE, 'w') {|f| f.write(key.unpack("H*").join) }
|
|
39
|
-
File.open(path + IV_FILE, 'w') {|f| f.write(iv.unpack("H*").join) }
|
|
40
|
-
rescue
|
|
41
|
-
puts "Couldn't write key/IV to #{path}\n"
|
|
42
|
-
raise $!
|
|
43
|
-
end
|
|
44
|
-
path
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
class KeyIVPair
|
|
49
|
-
attr_accessor :key, :iv
|
|
50
|
-
end
|
|
51
|
-
end
|
data/spec/config_spec.rb
DELETED