passw3rd 0.1.3 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  ![Build status](https://secure.travis-ci.org/oreoshake/passw3rd.png)
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
 
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby -w
1
+ #!/usr/bin/env ruby
2
2
 
3
3
  require File.expand_path('../../lib/passw3rd', __FILE__)
4
4
  require File.expand_path('../../lib/passw3rd/password_client.rb', __FILE__)
@@ -1,6 +1,5 @@
1
1
  require 'base64'
2
2
  require 'openssl'
3
3
  require 'optparse'
4
- require File.expand_path('../passw3rd/key_loader', __FILE__)
5
4
  require File.expand_path('../passw3rd/password_service', __FILE__)
6
5
 
@@ -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
- if !File.directory?(File.expand_path(key_path))
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::KeyLoader.create_key_iv_file(gen_key_path)
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 || ENV.fetch("HOME")
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 || ENV.fetch("HOME")
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 || 'aes-256-cbc'
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, key_path = key_file_dir)
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.to_s) { |f| f.read })
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 = ::Passw3rd::KeyLoader.create_key_iv_file
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 = KeyLoader.load(key_path)
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.key
101
- cipher.iv = pair.iv
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)
@@ -1,3 +1,3 @@
1
1
  module Passw3rd
2
- Version = VERSION = '0.1.3'
2
+ Version = VERSION = '0.2.1'
3
3
  end
@@ -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::KeyLoader.create_key_iv_file(Dir.tmpdir)
13
- ::Passw3rd::PasswordService.key_file_dir = Dir.tmpdir
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
- ::Passw3rd::PasswordService.password_file_dir = Dir.tmpdir
37
+ dir = "#{Dir.tmpdir}/#{sudorandumb}"
38
+
39
+ FileUtils.mkdir_p(dir)
40
+ ::Passw3rd::PasswordService.password_file_dir = dir
40
41
 
41
- password_file = ::Passw3rd::PasswordService.write_password_file(@random_string, "test2")
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 = "/tmp/"
49
- c.cipher_name = "aes-256-cbc"
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, "/tmp/")
52
- assert_equal(::Passw3rd::PasswordService.cipher_name, "aes-256-cbc")
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.3
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-05 00:00:00.000000000Z
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:
@@ -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
@@ -1,3 +0,0 @@
1
- describe "Config" do
2
- it "does stuff"
3
- end