skeletonkey 1.0.0
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.
- checksums.yaml +7 -0
- data/bin/skeletonkey +7 -0
- data/lib/skeletonkey/cli.rb +54 -0
- data/lib/skeletonkey/key_pair.rb +41 -0
- data/lib/skeletonkey/password.rb +79 -0
- data/lib/skeletonkey/safe.rb +50 -0
- metadata +49 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7248579cc226d9cb384f36a30a94f438036140cc
|
4
|
+
data.tar.gz: 9fdc1c0cc5aa4d1e3de85487788bf2f149e4ee9a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 32e54091eb0e1d65fab248cb73bf61d4dd4ed7ec0af279e5ad83b9592add31138463fa74729a2a5e70b6ffc1c912e5da781bf597054e98ccc9a1871a820500b0
|
7
|
+
data.tar.gz: 1cca1778429025ca6309fbdb25b8d30f66b3bfc3cec47bd053f1446ef4f05d317e0a614d0b393c86bd515a3c9a6cfdd64c16113f1872dc99068c458ba6e0264e
|
data/bin/skeletonkey
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'clipboard'
|
3
|
+
require 'io/console'
|
4
|
+
require 'skeletonkey/safe'
|
5
|
+
require 'skeletonkey/password'
|
6
|
+
|
7
|
+
module Lockbox
|
8
|
+
class Cli < Thor
|
9
|
+
def initialize(*args)
|
10
|
+
super(*args)
|
11
|
+
|
12
|
+
@safe ||= Safe.new
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "add KEY", "add a new password for KEY"
|
16
|
+
def add(key)
|
17
|
+
STDIN.noecho do |io|
|
18
|
+
puts "Please Enter a Password for #{key}: "
|
19
|
+
first_attempt = io.gets.chomp
|
20
|
+
|
21
|
+
puts "Please confirm your password: "
|
22
|
+
second_attempt = io.gets.chomp
|
23
|
+
|
24
|
+
on_invalid = ->(message){ puts message }
|
25
|
+
|
26
|
+
if Password.valid?(first_attempt, second_attempt, &on_invalid)
|
27
|
+
@safe.add(key, Password.fromString(first_attempt))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "remove KEY", "remove a password for KEY"
|
33
|
+
def remove(key)
|
34
|
+
@safe.remove(key)
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "get KEY", "retrieve a password for KEY"
|
38
|
+
option :copy, :type => :boolean
|
39
|
+
def get(key)
|
40
|
+
password = @safe.get(key).to_str
|
41
|
+
|
42
|
+
if options[:copy]
|
43
|
+
Clipboard.copy(password)
|
44
|
+
else
|
45
|
+
puts password
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "list", "list all KEYs"
|
50
|
+
def list
|
51
|
+
puts @safe.list
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Lockbox
|
2
|
+
class KeyPair
|
3
|
+
class << self
|
4
|
+
def generate
|
5
|
+
return if File.exists?(private_key_path) && File.exists?(public_key_path)
|
6
|
+
|
7
|
+
key = OpenSSL::PKey::RSA.new 2048
|
8
|
+
|
9
|
+
File.open(private_key_path, 'w', 0600) do |file|
|
10
|
+
file.write(key.to_pem)
|
11
|
+
end
|
12
|
+
|
13
|
+
File.open(public_key_path, 'w', 0600) do |file|
|
14
|
+
file.write(key.public_key.to_pem)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def public_encrypt(data)
|
19
|
+
generate
|
20
|
+
key = OpenSSL::PKey::RSA.new(File.read(public_key_path))
|
21
|
+
key.public_encrypt(data)
|
22
|
+
end
|
23
|
+
|
24
|
+
def private_decrypt(data)
|
25
|
+
generate
|
26
|
+
key = OpenSSL::PKey::RSA.new(File.read(private_key_path))
|
27
|
+
key.private_decrypt(data)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def private_key_path
|
33
|
+
File.join(Lockbox::DIRECTORY, "private_key.pem")
|
34
|
+
end
|
35
|
+
|
36
|
+
def public_key_path
|
37
|
+
File.join(Lockbox::DIRECTORY, "public_key.pem")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'skeletonkey/key_pair'
|
3
|
+
|
4
|
+
module Lockbox
|
5
|
+
class Password
|
6
|
+
attr_accessor :contents, :encrypted_contents, :iv, :key
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def valid?(password, confirmed_password, &on_invalid)
|
10
|
+
valid = true
|
11
|
+
|
12
|
+
if password != confirmed_password
|
13
|
+
on_invalid.call("Your passwords do not match! Please try again")
|
14
|
+
valid = false
|
15
|
+
elsif password === ''
|
16
|
+
on_invalid.call("Your password cannot be empty! Please try again")
|
17
|
+
valid = false
|
18
|
+
end
|
19
|
+
|
20
|
+
valid
|
21
|
+
end
|
22
|
+
|
23
|
+
def fromHash(password_hash)
|
24
|
+
password = new
|
25
|
+
password.encrypted_contents = password_hash[:encrypted_contents]
|
26
|
+
password.key = password_hash[:key]
|
27
|
+
password.iv = password_hash[:iv]
|
28
|
+
|
29
|
+
password.decrypt
|
30
|
+
password
|
31
|
+
end
|
32
|
+
|
33
|
+
def fromString(password_str)
|
34
|
+
password = new
|
35
|
+
password.contents = password_str
|
36
|
+
|
37
|
+
password.encrypt
|
38
|
+
password
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_str
|
43
|
+
@contents
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_hash
|
47
|
+
{
|
48
|
+
:encrypted_contents => @encrypted_contents,
|
49
|
+
:iv => @iv,
|
50
|
+
:key => @key
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def encrypt
|
55
|
+
cipher.encrypt
|
56
|
+
|
57
|
+
# set random key/iv on cipher, return for public encryption
|
58
|
+
# and store encrypted key/iv in instance variables
|
59
|
+
@key = KeyPair.public_encrypt(cipher.random_key)
|
60
|
+
@iv = KeyPair.public_encrypt(cipher.random_iv)
|
61
|
+
|
62
|
+
@encrypted_contents = cipher.update(@contents) + cipher.final
|
63
|
+
end
|
64
|
+
|
65
|
+
def decrypt
|
66
|
+
decipher = cipher
|
67
|
+
decipher.decrypt
|
68
|
+
|
69
|
+
decipher.key = KeyPair.private_decrypt(@key)
|
70
|
+
decipher.iv = KeyPair.private_decrypt(@iv)
|
71
|
+
|
72
|
+
@contents = decipher.update(@encrypted_contents) + decipher.final
|
73
|
+
end
|
74
|
+
|
75
|
+
def cipher
|
76
|
+
@cipher ||= OpenSSL::Cipher.new('AES-256-CBC')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Lockbox
|
2
|
+
DIRECTORY = File.join(Dir.home, ".lockbox")
|
3
|
+
|
4
|
+
class Safe
|
5
|
+
def initialize()
|
6
|
+
Dir.mkdir(Lockbox::DIRECTORY) unless Dir.exists?(Lockbox::DIRECTORY)
|
7
|
+
|
8
|
+
@file = File.join(Lockbox::DIRECTORY, "safe")
|
9
|
+
@storage = {}
|
10
|
+
|
11
|
+
reload
|
12
|
+
end
|
13
|
+
|
14
|
+
def add(key, password)
|
15
|
+
@storage[key] = password.to_hash
|
16
|
+
update
|
17
|
+
end
|
18
|
+
|
19
|
+
def get(key)
|
20
|
+
Password.fromHash(@storage[key])
|
21
|
+
end
|
22
|
+
|
23
|
+
def remove(key)
|
24
|
+
@storage.delete(key)
|
25
|
+
update
|
26
|
+
end
|
27
|
+
|
28
|
+
def list
|
29
|
+
@storage.keys * "\n"
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def reload
|
35
|
+
@storage = {}
|
36
|
+
|
37
|
+
return if !File.exists?(@file)
|
38
|
+
|
39
|
+
File.open(@file, 'r') do |file|
|
40
|
+
@storage = Marshal.load(file)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def update
|
45
|
+
File.open(@file, 'w', 0600) do |file|
|
46
|
+
file << Marshal.dump(@storage)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: skeletonkey
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dennis Heckman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-06 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Simple password storage and retrieval
|
14
|
+
email: denheck@gmail.com
|
15
|
+
executables:
|
16
|
+
- skeletonkey
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/skeletonkey
|
21
|
+
- lib/skeletonkey/cli.rb
|
22
|
+
- lib/skeletonkey/key_pair.rb
|
23
|
+
- lib/skeletonkey/password.rb
|
24
|
+
- lib/skeletonkey/safe.rb
|
25
|
+
homepage: http://rubygems.org/gems/skeletonkey
|
26
|
+
licenses:
|
27
|
+
- MIT
|
28
|
+
metadata: {}
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubyforge_project:
|
45
|
+
rubygems_version: 2.2.2
|
46
|
+
signing_key:
|
47
|
+
specification_version: 4
|
48
|
+
summary: Password Protection
|
49
|
+
test_files: []
|