keybox 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,132 @@
1
+ require 'keybox'
2
+ require 'keybox/application/password_generator'
3
+
4
+ context "Keybox Password Generator Application" do
5
+ setup do
6
+ end
7
+
8
+ specify "nil argv should do nothing" do
9
+ kpg = Keybox::Application::PasswordGenerator.new(nil)
10
+ kpg.error_message.should_be nil
11
+ end
12
+
13
+ specify "invalid options set the error message, exit 1 and have output on stderr" do
14
+ kpg = Keybox::Application::PasswordGenerator.new(["--invalid-option"])
15
+ kpg.stderr = StringIO.new
16
+ begin
17
+ kpg.run
18
+ rescue SystemExit => se
19
+ kpg.error_message.should_satisfy { |msg| msg =~ /Try.*--help/m }
20
+ kpg.stderr.string.should_satisfy { |msg| msg =~ /Try.*--help/m }
21
+ se.status.should == 1
22
+ end
23
+ end
24
+
25
+ specify "can set the algorithm" do
26
+ kpg = Keybox::Application::PasswordGenerator.new(["--alg", "pron"])
27
+ kpg.stdout = StringIO.new
28
+ kpg.run
29
+ kpg.options.algorithm.should == :pronounceable
30
+ end
31
+
32
+ specify "can specify the number of passwords created " do
33
+ kpg = Keybox::Application::PasswordGenerator.new(["--num", "4"])
34
+ kpg.stdout = StringIO.new
35
+ kpg.run
36
+ kpg.options.number_to_generate.should_eql 4
37
+ kpg.stdout.string.split(/\s+/).size.should == 4
38
+ end
39
+
40
+ specify "help has output on stdout and exits 0" do
41
+ kpg = Keybox::Application::PasswordGenerator.new(["--h"])
42
+ kpg.stdout = StringIO.new
43
+ begin
44
+ kpg.run
45
+ rescue SystemExit => se
46
+ se.status.should_eql 0
47
+ kpg.stdout.string.length.should_be > 0
48
+ end
49
+ kpg.stdout.string.should_satisfy { |msg| msg =~ /--help/m }
50
+ end
51
+
52
+ specify "version has output on stdout and exits 0" do
53
+ kpg = Keybox::Application::PasswordGenerator.new(["--version"])
54
+ kpg.stdout = StringIO.new
55
+ begin
56
+ kpg.run
57
+ rescue SystemExit => se
58
+ se.status.should_eql 0
59
+ end
60
+ kpg.stdout.string.should_satisfy { |msg| msg =~ /version 1.0.0/m }
61
+ end
62
+
63
+ specify "minimum length can be set and all generated passwords will have length >= minimum length" do
64
+ kpg = Keybox::Application::PasswordGenerator.new(["--min", "4"])
65
+ kpg.stdout = StringIO.new
66
+ kpg.run
67
+
68
+ kpg.options.min_length.should_eql 4
69
+ kpg.stdout.string.split("\n").each do |pass|
70
+ pass.length.should_satisfy { |s| s >= kpg.options.min_length }
71
+ end
72
+ end
73
+
74
+ specify "maximum length can be set and all generated passwords will have length <= maximum length" do
75
+ kpg = Keybox::Application::PasswordGenerator.new(["--max", "4", "--min", "3"])
76
+ kpg.stdout = StringIO.new
77
+ kpg.run
78
+
79
+ kpg.options.max_length.should_eql 4
80
+ kpg.stdout.string.split("\n").each do |pass|
81
+ pass.length.should_satisfy { |s| s <= 4 }
82
+ end
83
+ end
84
+
85
+ specify "setting an invalid required symbol set exits 1 and outputs data on stderr" do
86
+ kpg = Keybox::Application::PasswordGenerator.new(["--req","bunk"])
87
+ kpg.stderr = StringIO.new
88
+ begin
89
+ kpg.run
90
+ rescue SystemExit => se
91
+ kpg.error_message.should_satisfy { |msg| msg =~ /Try.*--help/m }
92
+ kpg.stderr.string.should_satisfy { |msg| msg =~ /Try.*--help/m }
93
+ se.status.should == 1
94
+ end
95
+
96
+ end
97
+
98
+ specify "setting an invalid use symbol set exits 1 and outputs data on stderr" do
99
+ kpg = Keybox::Application::PasswordGenerator.new(["--use","bunk"])
100
+ kpg.stderr = StringIO.new
101
+ begin
102
+ kpg.run
103
+ rescue SystemExit => se
104
+ kpg.error_message.should_satisfy { |msg| msg =~ /Try.*--help/m }
105
+ kpg.stderr.string.should_satisfy { |msg| msg =~ /Try.*--help/m }
106
+ se.status.should == 1
107
+ end
108
+
109
+ end
110
+
111
+ specify "setting an valid use symbol works" do
112
+ kpg = Keybox::Application::PasswordGenerator.new(["--use","l"])
113
+ kpg.stdout = StringIO.new
114
+ kpg.run
115
+ kpg.options.use_symbols.should_include Keybox::SymbolSet::LOWER_ASCII
116
+ kpg.stdout.string.split(/\s+/).size.should == 6
117
+ end
118
+
119
+ specify "setting an valid required symbol works" do
120
+ kpg = Keybox::Application::PasswordGenerator.new(["--req","l"])
121
+ kpg.stdout = StringIO.new
122
+ kpg.run
123
+ kpg.options.require_symbols.should_include Keybox::SymbolSet::LOWER_ASCII
124
+ kpg.stdout.string.split(/\s+/).size.should == 6
125
+ end
126
+
127
+
128
+
129
+
130
+
131
+ end
132
+
@@ -0,0 +1,11 @@
1
+ require 'keybox'
2
+ context "PasswordHash" do
3
+ setup do
4
+ @pwd_hash = Keybox::PasswordHash.new("i love ruby")
5
+ end
6
+
7
+ specify "creates string for password" do
8
+ pwd = @pwd_hash.password_for_url("http://www.nytimes.com")
9
+ pwd.should == "2f85a2e2f"
10
+ end
11
+ end
@@ -0,0 +1,116 @@
1
+ require 'keybox/randomizer'
2
+
3
+ context "a random device class" do
4
+ specify "should have a default source" do
5
+ Keybox::RandomDevice.default.should_eql "/dev/urandom"
6
+ end
7
+
8
+ specify "should produce strings" do
9
+ Keybox::RandomDevice.random_bytes.should_be_an_instance_of(String)
10
+ end
11
+
12
+ specify "should produce strings of a given length" do
13
+ Keybox::RandomDevice.random_bytes(4).size.should_eql 4
14
+ end
15
+
16
+ specify "should raise exception when given an invalid device" do
17
+ Proc.new {Keybox::RandomDevice.default = "/tmp/junk" }.should_raise ArgumentError
18
+ end
19
+
20
+ specify "should be able to assign a new default source" do
21
+ (Keybox::RandomDevice.default = "/dev/random").should_eql "/dev/random"
22
+ Keybox::RandomDevice.default = "/dev/urandom"
23
+ end
24
+ end
25
+
26
+ context "a random device instance" do
27
+ setup do
28
+ @random_device = Keybox::RandomDevice.new
29
+ end
30
+ specify "should have a source" do
31
+ @random_device.source.should_eql "/dev/urandom"
32
+ end
33
+
34
+ specify "should produce strings" do
35
+ @random_device.random_bytes.should_be_an_instance_of(String)
36
+ end
37
+
38
+ specify "should produce strings of a given length" do
39
+ @random_device.random_bytes(20).size.should_eql 20
40
+ end
41
+
42
+ specify "should default to the RandomDevice default when given an invalid device " do
43
+ rd = Keybox::RandomDevice.new("/tmp/bad-random-device")
44
+ rd.source.should_eql Keybox::RandomDevice.default
45
+ end
46
+
47
+ specify "should accept a valid readable device" do
48
+ rd = Keybox::RandomDevice.new("/dev/random")
49
+ rd.source.should_eql "/dev/random"
50
+ end
51
+ end
52
+
53
+ context "a random source class " do
54
+ setup do
55
+ @random_source_class = mock("JunkRandomSource")
56
+ @random_source_class.should_receive("random_bytes").at_least(0).times
57
+ end
58
+ specify "should have a default" do
59
+ Keybox::RandomSource.source.should_not_equal nil
60
+ end
61
+
62
+ specify "invalid default class should throw exception " do
63
+ lambda { Keybox::RandomSource.source = String }.should_raise ArgumentError
64
+ end
65
+
66
+ specify "valid class should allow default to be set" do
67
+ (Keybox::RandomSource.source = @random_source_class).should_eql @random_source_class
68
+ Keybox::RandomSource.source_classes.should_have(3).entries
69
+ Keybox::RandomSource.source_classes.delete(@random_source_class)
70
+ Keybox::RandomSource.source_classes.should_have(2).entries
71
+ Keybox::RandomSource.source.should_eql ::Keybox::RandomDevice
72
+ end
73
+
74
+ specify "rand with no parameters should return a value between 0 and 1" do
75
+ r = Keybox::RandomSource.rand
76
+ r.should_satisfy { |arg| arg >= 0.0 and arg < 1.0 }
77
+ end
78
+
79
+ specify "rand with parameters should return an integer value between 0 and that value" do
80
+ r = Keybox::RandomSource.rand(42)
81
+ r.should_be_a_kind_of(Integer)
82
+ r.should_be < 42
83
+ end
84
+ end
85
+
86
+ context "a randomzier instance" do
87
+ setup do
88
+ @randomizer = Keybox::Randomizer.new
89
+ @hash = { :stuff => "stuff", :things => "things" }
90
+ @array = ["foo", "bar", "baz" ]
91
+ end
92
+ specify "should have a default random source" do
93
+ @randomizer.random_source.should_not_be nil
94
+ end
95
+
96
+ specify "giving an invalid default random source should raise an exception" do
97
+ lambda { r = Keybox::Randomizer.new(Array) }.should_raise ArgumentError
98
+ end
99
+
100
+ specify "picking from a non-array should raise an exception" do
101
+ lambda { @randomizer.pick_count_from(@hash) }.should_raise ArgumentError
102
+ end
103
+
104
+ specify "picking one from an array should be okay" do
105
+ @randomizer.pick_one_from(@array).should_satisfy do |x|
106
+ @array.include?(x)
107
+ end
108
+ end
109
+
110
+ specify "picking N from an array should result in an array" do
111
+ @randomizer.pick_count_from(@array,3).should_have(3).entries
112
+ @randomizer.pick_count_from(@array,9).each do |arg|
113
+ @array.should_include(arg)
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,99 @@
1
+ require 'keybox'
2
+ require 'keybox/storage'
3
+ require 'keybox/error'
4
+ require 'keybox/entry'
5
+ require 'openssl'
6
+ require 'tempfile'
7
+ require 'yaml'
8
+
9
+ context 'a storage container' do
10
+ setup do
11
+ @passphrase = "i love ruby"
12
+ @keybox_file = Tempfile.new("keybox").path
13
+ @testing_file = "/tmp/testing.yml"
14
+ @container = Keybox::Storage::Container.new(@passphrase, @keybox_file)
15
+ @container << Keybox::HostAccountEntry.new("test account","localhost","guest", "rubyrocks")
16
+ @container << Keybox::URLAccountEntry.new("the times", "http://www.nytimes.com", "rubyhacker")
17
+ @container.save
18
+ end
19
+
20
+ teardown do
21
+ File.unlink(@testing_file) if File.exists?(@testing_file)
22
+ File.unlink(@keybox_file) if File.exists?(@keybox_file)
23
+ end
24
+
25
+ specify 'should have a uuid' do
26
+ @container.uuid.should_satisfy { |uuid| uuid.to_s.length == 36 }
27
+ end
28
+
29
+ specify 'should have a valid key ' do
30
+ @container.key_digest.should_satisfy { |kd| kd.length == 64 }
31
+ end
32
+
33
+ specify 'should save correctly to a file' do
34
+ @container.save(@testing_file)
35
+ File.size(@testing_file).should_satisfy { |s| s > 0 }
36
+ end
37
+
38
+ specify "should load correctly from a file" do
39
+ @container.save(@testing_file)
40
+ new_container = Keybox::Storage::Container.new(@passphrase,@testing_file)
41
+ new_container.should_be_not_modified
42
+ new_container.uuid.should == @container.uuid
43
+ end
44
+
45
+ specify "should validate passphrase" do
46
+ nc = Keybox::Storage::Container.new("i love ruby", @keybox_file)
47
+ nc.save(@testing_file)
48
+ nc.key_digest.should_eql @container.key_digest
49
+ lambda { Keybox::Storage::Container.new("i hate ruby", @testing_file) }.should_raise Keybox::ValidationError
50
+
51
+ end
52
+
53
+ specify "url accounts should have the correct password after save" do
54
+ @container.save(@testing_file)
55
+ new_container = Keybox::Storage::Container.new(@passphrase, @testing_file)
56
+ recs = new_container.find_by_url("nytimes")
57
+ new_container.records.size.should_eql 2
58
+ recs.size.should_eql 1
59
+ recs[0].password.should == "2f85a2e2f"
60
+ end
61
+
62
+ specify "can find matching records" do
63
+ matches = @container.find(/times/)
64
+ matches.size.should_be 1
65
+ end
66
+
67
+ specify "changing the password is safe" do
68
+ @container.save(@testing_file)
69
+ copy_of_container= Keybox::Storage::Container.new(@passphrase, @testing_file)
70
+ times_1 = copy_of_container.find_by_url("nytimes").first
71
+
72
+ @container.passphrase = "I love ruby too!"
73
+ @container.save(@keybox_file)
74
+ @container = Keybox::Storage::Container.new("I love ruby too!", @keybox_file)
75
+ times_2 = @container.find_by_url("nytimes").first
76
+ times_1.should_eql times_2
77
+ end
78
+
79
+ specify "should not be modified upon load" do
80
+ @container.modified?.should_eql false
81
+ end
82
+
83
+ specify "a modified db can be detected" do
84
+ l1 = @container.find("localhost").first
85
+ l1.username = "new username"
86
+ @container.modified?.should_eql true
87
+ end
88
+
89
+ specify "deleting an item should modify the container" do
90
+ ll = @container.find("localhost").first
91
+ @container.delete(ll)
92
+ @container.modified?.should_eql true
93
+ end
94
+
95
+ specify "able to see how many items are in the container" do
96
+ @container.size.should_eql 2
97
+ @container.length.should_eql 2
98
+ end
99
+ end
@@ -0,0 +1,63 @@
1
+ require 'keybox/storage'
2
+
3
+ context 'a storage record entry' do
4
+ setup do
5
+ @data_fields = %w(title username password url additional_data)
6
+ end
7
+ specify 'has a creation date set on instantiation' do
8
+ e = Keybox::Storage::Record.new
9
+ e.creation_time.should_be_instance_of(Time)
10
+ end
11
+
12
+ specify "assigning to a non-existant field creates the appropriate member " do
13
+ e = Keybox::Storage::Record.new
14
+ e.junk = "junk"
15
+ e.junk.should == "junk"
16
+ end
17
+
18
+ specify 'default values for non-existant fields is nil' do
19
+ e = Keybox::Storage::Record.new
20
+ @data_fields.each do |f|
21
+ e.send(f).should_be nil
22
+ end
23
+ end
24
+
25
+ specify "assigning to a field makes the modification time > creation time" do
26
+ e = Keybox::Storage::Record.new
27
+ sleep 1
28
+ e.testing = "testing"
29
+ e.modification_time.should_satisfy { |m| m > e.creation_time }
30
+ e.last_access_time.should == e.modification_time
31
+ end
32
+
33
+ specify "reading a field after assignment the access time > modification time " do
34
+ e = Keybox::Storage::Record.new
35
+ e.testing = "testing"
36
+ sleep 1
37
+ e.testing
38
+ e.last_access_time.should_satisfy { |la| la > e.creation_time }
39
+ e.last_access_time.should_satisfy { |la| la > e.modification_time }
40
+ end
41
+
42
+ specify "assigning to a modification, creation or acces_time should raise and exception " do
43
+ e = Keybox::Storage::Record.new
44
+ lambda {e.modification_time = Time.now}.should_raise NoMethodError
45
+ end
46
+
47
+ specify "assiging multiple items should raise an argument exception" do
48
+ e = Keybox::Storage::Record.new
49
+ lambda {e.send(:stuff=,1,2)}.should_raise ArgumentError
50
+ end
51
+
52
+ specify "calling a method with arguments should raise exception" do
53
+ e = Keybox::Storage::Record.new
54
+ lambda {e.stuff(1,2)}.should_raise NoMethodError
55
+ end
56
+
57
+ specify "comparison between records is valid" do
58
+ e = Keybox::Storage::Record.new
59
+ f = e.dup
60
+ e.should_eql e.uuid
61
+ e.should_eql f
62
+ end
63
+ end
@@ -0,0 +1,114 @@
1
+ require 'keybox'
2
+ context "string generator" do
3
+ setup do
4
+ @generator = Keybox::StringGenerator.new
5
+ end
6
+
7
+ specify "should not be used alone" do
8
+ lambda { @generator.generate }.should_raise Keybox::KeyboxError
9
+ end
10
+
11
+ specify "cannot have a min length greater than a max length" do
12
+ @generator.min_length = 90
13
+ lambda { @generator.generate }.should_raise Keybox::ValidationError
14
+ @generator.min_length = 8
15
+ end
16
+
17
+ specify "cannot have a max length less than a min length" do
18
+ @generator.max_length = 2
19
+ lambda { @generator.generate }.should_raise Keybox::ValidationError
20
+ @generator.max_length = 10
21
+ end
22
+
23
+ specify "initially there are no chunks" do
24
+ @generator.chunks.should_have(0).entries
25
+ end
26
+ end
27
+
28
+ context "chargram generator" do
29
+ setup do
30
+ @generator = Keybox::CharGramGenerator.new
31
+ end
32
+
33
+ specify "should have a positive size" do
34
+ @generator.size.should_be > 26
35
+ end
36
+
37
+ specify "should emit a string with length > 0" do
38
+ @generator.generate_chunk.size.should_be > 0
39
+ end
40
+
41
+ specify "should emit an array " do
42
+ @generator.generate_chunk.should_be_instance_of String
43
+ end
44
+
45
+ specify "2 succesive emits should have a common first and last character" do
46
+ one = @generator.generate_chunk
47
+ two = @generator.generate_chunk
48
+ one[-1].should == two[0]
49
+ end
50
+
51
+ specify "2 calls to generate_chunk should have a string that is 1 less than the 2 chunks" do
52
+ one = @generator.generate_chunk
53
+ two = @generator.generate_chunk
54
+ @generator.to_s.length.should == (one.length + two.length - 1)
55
+ end
56
+ end
57
+
58
+ context "SymbolSetGenerator" do
59
+ setup do
60
+ @generator = Keybox::SymbolSetGenerator.new
61
+ end
62
+
63
+ specify "symbol sets have the right number or characters" do
64
+ Keybox::SymbolSetGenerator::ALL.size.should == 92
65
+ end
66
+
67
+ specify "generating chunks should produce an array" do
68
+ 12.times do
69
+ @generator.generate_chunk
70
+ end
71
+ @generator.chunks.should_have(12).entries
72
+ end
73
+
74
+ specify "generate should produce a string" do
75
+ @generator.generate.should_be_instance_of(String)
76
+ @generator.to_s.size.should_be > 0
77
+ end
78
+
79
+ specify "generating chunks can be cleared" do
80
+ @generator.generate
81
+ @generator.clear
82
+ @generator.chunks.should_have(0).entries
83
+ end
84
+
85
+ specify "min and max lengths are respected" do
86
+ @generator.max_length = 25
87
+ @generator.min_length = 25
88
+ @generator.generate.size.should_be == 25
89
+ end
90
+
91
+ specify "required sets are utilized" do
92
+ gen = Keybox::SymbolSetGenerator.new([Keybox::SymbolSet::NUMERAL_ASCII, Keybox::SymbolSet::LOWER_ASCII])
93
+ gen.required_sets << Keybox::SymbolSet::UPPER_ASCII
94
+ p = gen.generate
95
+ gen.required_sets.flatten.uniq.should_include(p[0].chr)
96
+ end
97
+ specify "required sets are merged with symbol sets" do
98
+ gen = Keybox::SymbolSetGenerator.new([Keybox::SymbolSet::NUMERAL_ASCII, Keybox::SymbolSet::LOWER_ASCII])
99
+ gen.required_sets << Keybox::SymbolSet::UPPER_ASCII
100
+ gen.required_sets.flatten.uniq.each do |c|
101
+ gen.symbols.should_include(c)
102
+ end
103
+ end
104
+
105
+ specify "generated passwords autoclear" do
106
+ @generator.generate.should_not == @generator.generate
107
+ end
108
+
109
+ specify "setting min and max should not affect " do
110
+ g = Keybox::SymbolSetGenerator.new(Keybox::SymbolSet::ALL)
111
+ g.generate.should_be_instance_of(String)
112
+ end
113
+
114
+ end