passwordsafe 0.0.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/.gitignore +3 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +24 -0
- data/README.md +5 -0
- data/Rakefile +2 -0
- data/lib/passwordsafe/encryptor.rb +27 -0
- data/lib/passwordsafe/keyring.rb +44 -0
- data/lib/passwordsafe/safe.rb +30 -0
- data/lib/passwordsafe/version.rb +3 -0
- data/lib/passwordsafe.rb +3 -0
- data/passwordsafe.gemspec +24 -0
- data/safefile +1 -0
- data/spec/encryptor_spec.rb +56 -0
- data/spec/keyring_spec.rb +81 -0
- data/spec/safe_spec.rb +68 -0
- data/spec/spec_helper.rb +9 -0
- metadata +93 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
passwordsafe (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.1.2)
|
10
|
+
rspec (2.3.0)
|
11
|
+
rspec-core (~> 2.3.0)
|
12
|
+
rspec-expectations (~> 2.3.0)
|
13
|
+
rspec-mocks (~> 2.3.0)
|
14
|
+
rspec-core (2.3.1)
|
15
|
+
rspec-expectations (2.3.0)
|
16
|
+
diff-lcs (~> 1.1.2)
|
17
|
+
rspec-mocks (2.3.0)
|
18
|
+
|
19
|
+
PLATFORMS
|
20
|
+
ruby
|
21
|
+
|
22
|
+
DEPENDENCIES
|
23
|
+
passwordsafe!
|
24
|
+
rspec
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module PasswordSafe
|
4
|
+
module Encryptor; extend self;
|
5
|
+
|
6
|
+
def decrypt(data, pwhash)
|
7
|
+
crypt :decrypt, data, pwhash
|
8
|
+
end
|
9
|
+
|
10
|
+
def encrypt(data, pwhash)
|
11
|
+
crypt :encrypt, data, pwhash
|
12
|
+
end
|
13
|
+
|
14
|
+
def hash (plain)
|
15
|
+
Digest::SHA512.digest(plain)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def crypt(mode, data, pwhash)
|
20
|
+
cipher = OpenSSL::Cipher.new 'AES256'
|
21
|
+
cipher.send mode.to_sym
|
22
|
+
cipher.key = pwhash
|
23
|
+
cipher.update(data) << cipher.final
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'passwordsafe/safe'
|
2
|
+
|
3
|
+
module PasswordSafe
|
4
|
+
class Keyring
|
5
|
+
|
6
|
+
def initialize safe = nil
|
7
|
+
@safe = safe
|
8
|
+
@ring = load_from_safe
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_a_safe?
|
12
|
+
@safe.is_a? PasswordSafe::Safe
|
13
|
+
end
|
14
|
+
|
15
|
+
def length
|
16
|
+
@ring.length
|
17
|
+
end
|
18
|
+
|
19
|
+
def add name, password
|
20
|
+
raise KeyExistsException, "Key already exists in keyring, if you'd like to add it remove the existing key", caller if @ring.has_key?(name)
|
21
|
+
@ring.store(name, password)
|
22
|
+
@safe.write_safe @ring
|
23
|
+
end
|
24
|
+
|
25
|
+
def get name
|
26
|
+
@ring[name]
|
27
|
+
end
|
28
|
+
|
29
|
+
def list
|
30
|
+
@ring.keys
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove name
|
34
|
+
@ring.delete(name)
|
35
|
+
@safe.write_safe @ring
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def load_from_safe
|
40
|
+
@safe.read_safe
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'passwordsafe/encryptor'
|
2
|
+
|
3
|
+
module PasswordSafe
|
4
|
+
class Safe
|
5
|
+
include PasswordSafe::Encryptor
|
6
|
+
|
7
|
+
def initialize filename, masterpass
|
8
|
+
@safefile = File.expand_path(filename)
|
9
|
+
@mphash = hash(masterpass)
|
10
|
+
end
|
11
|
+
|
12
|
+
def access_safe
|
13
|
+
unless File.file? @safefile
|
14
|
+
FileUtils.touch @safefile
|
15
|
+
write_safe
|
16
|
+
end
|
17
|
+
end
|
18
|
+
def write_safe data = {}
|
19
|
+
dump = Marshal.dump(data)
|
20
|
+
access_safe
|
21
|
+
encrypted_data = encrypt(dump, @mphash)
|
22
|
+
File.open(@safefile, 'w') {|f| f.write encrypted_data}
|
23
|
+
end
|
24
|
+
def read_safe
|
25
|
+
access_safe
|
26
|
+
Marshal.load decrypt(File.read(@safefile), @mphash) || {}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
data/lib/passwordsafe.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "passwordsafe/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "passwordsafe"
|
7
|
+
s.version = Passwordsafe::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["thecatwasnot"]
|
10
|
+
s.email = ["thecatwasnot@gmail.com"]
|
11
|
+
s.homepage = "http://rubygems.org/gems/passwordsafe"
|
12
|
+
s.summary = %q{Small command line app for storing passwords}
|
13
|
+
s.description = %q{Small command line app for storing passwords}
|
14
|
+
|
15
|
+
s.rubyforge_project = "passwordsafe"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_development_dependency('rspec')
|
23
|
+
end
|
24
|
+
|
data/safefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
�������U�vZ�H�
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'passwordsafe/encryptor'
|
3
|
+
|
4
|
+
describe PasswordSafe::Encryptor do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
klass = Class.new { include PasswordSafe::Encryptor}
|
8
|
+
@encryptor = klass.new
|
9
|
+
end
|
10
|
+
context "hash" do
|
11
|
+
it "creates a password hash" do
|
12
|
+
@encryptor.should respond_to(:hash).with(1).argument
|
13
|
+
@encryptor.hash("test").should be_a(String)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "creates the same hash given the same text" do
|
17
|
+
@encryptor.hash("test").should eq(@encryptor.hash("test"))
|
18
|
+
end
|
19
|
+
|
20
|
+
it "creates a different hash given different text" do
|
21
|
+
@encryptor.hash("test").should_not eq(@encryptor.hash("another"))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
context "encrypt" do
|
25
|
+
before(:each) do
|
26
|
+
@string = "teststring"
|
27
|
+
@hash = @encryptor.hash("hashstring")
|
28
|
+
end
|
29
|
+
|
30
|
+
it "encrypts a string" do
|
31
|
+
@encryptor.should respond_to(:encrypt).with(2).arguments
|
32
|
+
@encryptor.encrypt(@string, @hash).should be_a(String)
|
33
|
+
end
|
34
|
+
it "creates the same string given the same text" do
|
35
|
+
@encryptor.encrypt(@string, @hash).should eq(@encryptor.encrypt(@string, @hash))
|
36
|
+
end
|
37
|
+
it "creates a different string given different text" do
|
38
|
+
@encryptor.encrypt(@string, @hash).should_not eq(@encryptor.encrypt("anotherstring", @hash))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
context "decrypt" do
|
42
|
+
before(:each) do
|
43
|
+
@string = "teststring"
|
44
|
+
@hash = @encryptor.hash("hashstring")
|
45
|
+
@data = @encryptor.encrypt(@string, @hash)
|
46
|
+
end
|
47
|
+
it "decrypts a string" do
|
48
|
+
@encryptor.should respond_to(:decrypt).with(2).arguments
|
49
|
+
@encryptor.decrypt(@data, @hash).should be_a(String)
|
50
|
+
end
|
51
|
+
it "decrypts an encrypted string" do
|
52
|
+
@encryptor.decrypt(@data, @hash).should eq(@string)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end # describe PasswordSafe::Encryptor
|
56
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'passwordsafe/keyring'
|
3
|
+
|
4
|
+
describe PasswordSafe::Keyring do
|
5
|
+
before(:each) do
|
6
|
+
#set up a safe that will clear initilaization and send empty data
|
7
|
+
@safe = mock PasswordSafe::Safe
|
8
|
+
@safe.stub(:read_safe).and_return({})
|
9
|
+
@safe.stub(:write_safe)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "expects to get a Safe to read/write" do
|
13
|
+
@safe.should_receive(:is_a?).and_return(true)
|
14
|
+
PasswordSafe::Keyring.should respond_to(:new).with(1).argument
|
15
|
+
keyring = PasswordSafe::Keyring.new(@safe)
|
16
|
+
keyring.should have_a_safe
|
17
|
+
end
|
18
|
+
|
19
|
+
context "length" do
|
20
|
+
it "returns the number of keys in the keyring" do
|
21
|
+
keyring = PasswordSafe::Keyring.new(@safe)
|
22
|
+
keyring.should respond_to(:length)
|
23
|
+
keyring.length.should be_a(Integer)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "add" do
|
28
|
+
before(:each) do
|
29
|
+
@keyring = PasswordSafe::Keyring.new(@safe)
|
30
|
+
end
|
31
|
+
it "adds a key to the keyring" do
|
32
|
+
@keyring.should respond_to(:add).with(2).arguments
|
33
|
+
@keyring.add("name", "password")
|
34
|
+
@keyring.length.should eq(1)
|
35
|
+
end
|
36
|
+
it "throws an error when adding a duplicate key name" do
|
37
|
+
@keyring.add("name", "password")
|
38
|
+
expect{@keyring.add("name", "password")}.to raise_error()
|
39
|
+
end
|
40
|
+
it "saves the modified keyring to the safe" do
|
41
|
+
@safe.should_receive(:write_safe).with({"name" => "password"})
|
42
|
+
@keyring.add("name", "password")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "get" do
|
47
|
+
it "gets a key from the keyring" do
|
48
|
+
@safe.stub(:read_safe).and_return({"name" => "password"})
|
49
|
+
@keyring = PasswordSafe::Keyring.new(@safe)
|
50
|
+
@keyring.get("name").should eq("password")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "list" do
|
55
|
+
it "returns a list of existing key names" do
|
56
|
+
@safe.should_receive(:read_safe).and_return({"first" => "password", "second" => "password"})
|
57
|
+
@keyring = PasswordSafe::Keyring.new(@safe)
|
58
|
+
@keyring.list.should eq(["first", "second"])
|
59
|
+
end
|
60
|
+
it "returns an empty array if there are no keys" do
|
61
|
+
@keyring = PasswordSafe::Keyring.new(@safe)
|
62
|
+
@keyring.list.should eq([])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "remove" do
|
67
|
+
it "removes an existing key" do
|
68
|
+
@safe.should_receive(:read_safe).and_return({"first" => "password", "second" => "password"})
|
69
|
+
@keyring = PasswordSafe::Keyring.new(@safe)
|
70
|
+
@keyring.remove("first")
|
71
|
+
@keyring.get("first").should eq(nil)
|
72
|
+
end
|
73
|
+
it "saves the modified keyring to the safe" do
|
74
|
+
@safe.should_receive(:read_safe).and_return({"first" => "password", "second" => "password"})
|
75
|
+
@safe.should_receive(:write_safe).with({"second" => "password"})
|
76
|
+
@keyring = PasswordSafe::Keyring.new(@safe)
|
77
|
+
@keyring.remove("first")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
data/spec/safe_spec.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'passwordsafe/safe'
|
3
|
+
|
4
|
+
describe PasswordSafe::Safe do
|
5
|
+
before (:each) do
|
6
|
+
@file = 'passwordsafe'
|
7
|
+
@filename = File.expand_path(@file)
|
8
|
+
@masterpass = 'masterpass'
|
9
|
+
end
|
10
|
+
|
11
|
+
it "takes a safe filename and master password at initilaization" do
|
12
|
+
PasswordSafe::Safe.should respond_to(:new).with(2).arguments
|
13
|
+
end
|
14
|
+
|
15
|
+
context "access_safe" do
|
16
|
+
before(:each) do
|
17
|
+
@safe = PasswordSafe::Safe.new(@file, @masterpass)
|
18
|
+
end
|
19
|
+
it "creates a safe if none exists" do
|
20
|
+
@safe.access_safe
|
21
|
+
File.file?(@filename).should be_true
|
22
|
+
end
|
23
|
+
it "doesn't modify an existing safe" do
|
24
|
+
content = "some existing file content"
|
25
|
+
File.open(@filename, 'w') {|f| f.write content}
|
26
|
+
@safe.access_safe
|
27
|
+
File.read(@filename).should eq(content)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "write_safe" do
|
32
|
+
before(:each) do
|
33
|
+
@safe = PasswordSafe::Safe.new(@file, @masterpass)
|
34
|
+
klass = Class.new { include PasswordSafe::Encryptor}
|
35
|
+
@encryptor = klass.new
|
36
|
+
@data = {"data" => "encrypt"}
|
37
|
+
end
|
38
|
+
|
39
|
+
it "creates a safe file to write to" do
|
40
|
+
@safe.write_safe(@data)
|
41
|
+
File.file?(@filename).should be_true
|
42
|
+
end
|
43
|
+
|
44
|
+
it "writes encrypted data to a safe" do
|
45
|
+
@safe.write_safe(@data)
|
46
|
+
|
47
|
+
#we'll use our encryptor to check the contents
|
48
|
+
hash = @encryptor.hash(@masterpass)
|
49
|
+
Marshal.load(@encryptor.decrypt(File.read(@filename), hash)).should eq(@data)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "read_safe" do
|
54
|
+
it "reads encrypted data out of an existing safe" do
|
55
|
+
@safe = PasswordSafe::Safe.new(@file, @masterpass)
|
56
|
+
data = {"data" => "encrypt"}
|
57
|
+
@safe.write_safe(data)
|
58
|
+
#got data into an existing safe...now read it out again
|
59
|
+
@safe.read_safe.should eq(data)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
after(:each) do
|
64
|
+
#We check on the existance of files when we expect them to exist so this seems ok
|
65
|
+
File.delete @filename if File.file?(@filename)
|
66
|
+
end
|
67
|
+
end # describe PasswordSafe::Safe
|
68
|
+
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: passwordsafe
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- thecatwasnot
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-12-22 00:00:00 -06:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id001
|
33
|
+
description: Small command line app for storing passwords
|
34
|
+
email:
|
35
|
+
- thecatwasnot@gmail.com
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files: []
|
41
|
+
|
42
|
+
files:
|
43
|
+
- .gitignore
|
44
|
+
- .rspec
|
45
|
+
- Gemfile
|
46
|
+
- Gemfile.lock
|
47
|
+
- README.md
|
48
|
+
- Rakefile
|
49
|
+
- lib/passwordsafe.rb
|
50
|
+
- lib/passwordsafe/encryptor.rb
|
51
|
+
- lib/passwordsafe/keyring.rb
|
52
|
+
- lib/passwordsafe/safe.rb
|
53
|
+
- lib/passwordsafe/version.rb
|
54
|
+
- passwordsafe.gemspec
|
55
|
+
- safefile
|
56
|
+
- spec/encryptor_spec.rb
|
57
|
+
- spec/keyring_spec.rb
|
58
|
+
- spec/safe_spec.rb
|
59
|
+
- spec/spec_helper.rb
|
60
|
+
has_rdoc: true
|
61
|
+
homepage: http://rubygems.org/gems/passwordsafe
|
62
|
+
licenses: []
|
63
|
+
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
85
|
+
requirements: []
|
86
|
+
|
87
|
+
rubyforge_project: passwordsafe
|
88
|
+
rubygems_version: 1.3.7
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: Small command line app for storing passwords
|
92
|
+
test_files: []
|
93
|
+
|