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.
@@ -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