adamantite 0.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/adamantite +4 -0
- data/lib/adamantite.rb +134 -0
- data/lib/adamantite_command_line.rb +95 -0
- data/lib/base/adamantite.rb +59 -0
- data/lib/file_utils/file_utils.rb +55 -0
- data/lib/gui/request/add_password_request.rb +24 -0
- data/lib/gui/request/login_request.rb +23 -0
- data/lib/gui/request/set_master_password_request.rb +20 -0
- data/lib/gui/request/update_master_password_request.rb +14 -0
- data/lib/gui/screen/copy_screen.rb +18 -0
- data/lib/gui/screen/login_screen.rb +38 -0
- data/lib/gui/screen/set_master_password_screen.rb +41 -0
- data/lib/gui/screen/show_screen.rb +19 -0
- data/lib/gui/screen/update_master_password_screen.rb +43 -0
- data/lib/pw_utils/pw_utils.rb +40 -0
- metadata +58 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 93fd9127b45b4717d382be6dae9f956d6f19b8fa9e9afe2cae036f3b7acfd082
|
4
|
+
data.tar.gz: ef419d87bc500e2f0120b7b924bcf63eb8e75a86a35330b51cc1b2282cec46d9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4a05165d913cbb1c2b19b286805643057107c8ff1847291003f5585916d6d6c81c789bdb9bb4216a0192ebc57951d7abbd214b76cbb895ea06aa2a5ffa60c701
|
7
|
+
data.tar.gz: 5818cc7e919e296f6bf8f0653bdaae0313e131a0bdeee2cb1445605c7e18c8ef0b36ba1da68fa3dfc4db8eb26ea0a147f1f5afc927555b9588aaf394761c214b
|
data/bin/adamantite
ADDED
data/lib/adamantite.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
require "glimmer-dsl-libui"
|
2
|
+
require "bcrypt"
|
3
|
+
require "openssl"
|
4
|
+
require "base64"
|
5
|
+
require "json"
|
6
|
+
require "io/console"
|
7
|
+
|
8
|
+
require "file_utils/file_utils"
|
9
|
+
require "pw_utils/pw_utils"
|
10
|
+
require "base/adamantite"
|
11
|
+
require "gui/screen/login_screen"
|
12
|
+
require "gui/screen/copy_screen"
|
13
|
+
require "gui/screen/show_screen"
|
14
|
+
require "gui/screen/set_master_password_screen"
|
15
|
+
require "gui/screen/update_master_password_screen"
|
16
|
+
require "gui/request/login_request"
|
17
|
+
require "gui/request/add_password_request"
|
18
|
+
require "gui/request/update_master_password_request"
|
19
|
+
require "gui/request/set_master_password_request"
|
20
|
+
|
21
|
+
include Adamantite::FileUtils
|
22
|
+
include Adamantite::PWUtils
|
23
|
+
|
24
|
+
class AdamantiteApp
|
25
|
+
include Glimmer::LibUI::Application
|
26
|
+
|
27
|
+
attr_accessor :add_password_request, :stored_passwords
|
28
|
+
|
29
|
+
before_body do
|
30
|
+
if !pw_file_exists?('master')
|
31
|
+
set_master_password_request = Adamantite::GUI::Request::SetMasterPasswordRequest.new
|
32
|
+
set_master_password_screen(set_master_password_request: set_master_password_request).show
|
33
|
+
end
|
34
|
+
|
35
|
+
login_request = Adamantite::GUI::Request::LoginRequest.new
|
36
|
+
login_screen(login_request: login_request).show
|
37
|
+
|
38
|
+
if !login_request.authenticated
|
39
|
+
exit(0)
|
40
|
+
end
|
41
|
+
|
42
|
+
@stored_passwords = get_stored_pws.map do |title|
|
43
|
+
pw_info = get_pw_file(title)
|
44
|
+
[title, pw_info["username"], 'Copy', 'Show', 'Delete']
|
45
|
+
end
|
46
|
+
@master_password = login_request.master_password
|
47
|
+
@master_password_salt = login_request.master_password_salt
|
48
|
+
@adamantite_object = Adamantite::Base::Adamantite.new(@master_password)
|
49
|
+
@adamantite_object.authenticate!
|
50
|
+
@add_password_request = Adamantite::GUI::Request::AddPasswordRequest.new(@master_password, @master_password_salt)
|
51
|
+
end
|
52
|
+
|
53
|
+
body {
|
54
|
+
window('Adamantite', 600, 400) {
|
55
|
+
margined true
|
56
|
+
|
57
|
+
vertical_box {
|
58
|
+
table {
|
59
|
+
text_column('Title')
|
60
|
+
text_column('Username')
|
61
|
+
button_column('Copy') {
|
62
|
+
on_clicked do |row|
|
63
|
+
password_title = @stored_passwords[row].first
|
64
|
+
pw_info = get_pw_file(password_title)
|
65
|
+
stored_pw_selection = decrypt_pw(pw_info["iv"], pw_info["password"], @master_password, @master_password_salt)
|
66
|
+
IO.popen('pbcopy', 'w') { |f| f << stored_pw_selection }
|
67
|
+
copy_screen(password_title: password_title).show
|
68
|
+
end
|
69
|
+
}
|
70
|
+
button_column('Show') {
|
71
|
+
on_clicked do |row|
|
72
|
+
pw_info = get_pw_file(@stored_passwords[row].first)
|
73
|
+
stored_pw_selection = decrypt_pw(pw_info["iv"], pw_info["password"], @master_password, @master_password_salt)
|
74
|
+
show_screen(password: stored_pw_selection).show
|
75
|
+
end
|
76
|
+
}
|
77
|
+
button_column('Delete') {
|
78
|
+
on_clicked do |row|
|
79
|
+
delete_pw_file(@stored_passwords[row].first)
|
80
|
+
@stored_passwords.delete_at(row)
|
81
|
+
end
|
82
|
+
}
|
83
|
+
|
84
|
+
cell_rows <=> [self, :stored_passwords]
|
85
|
+
|
86
|
+
}
|
87
|
+
vertical_box {
|
88
|
+
form {
|
89
|
+
entry {
|
90
|
+
label 'Website Title'
|
91
|
+
text <=> [@add_password_request, :website_title]
|
92
|
+
}
|
93
|
+
entry {
|
94
|
+
label 'Username'
|
95
|
+
text <=> [@add_password_request, :username]
|
96
|
+
}
|
97
|
+
password_entry {
|
98
|
+
label 'Password'
|
99
|
+
text <=> [@add_password_request, :password]
|
100
|
+
}
|
101
|
+
password_entry {
|
102
|
+
label 'Confirm Password'
|
103
|
+
text <=> [@add_password_request, :password_confirmation]
|
104
|
+
}
|
105
|
+
}
|
106
|
+
horizontal_box {
|
107
|
+
button('Add Password') {
|
108
|
+
on_clicked do
|
109
|
+
@add_password_request.confirm_and_add_password!
|
110
|
+
if @add_password_request.password_saved
|
111
|
+
new_stored_password = [@add_password_request.website_title, @add_password_request.username]
|
112
|
+
new_stored_password << 'Copy'
|
113
|
+
new_stored_password << 'Show'
|
114
|
+
new_stored_password << 'Delete'
|
115
|
+
@stored_passwords << new_stored_password
|
116
|
+
@add_password_request.website_title = ''
|
117
|
+
@add_password_request.username = ''
|
118
|
+
@add_password_request.password = ''
|
119
|
+
@add_password_request.password_confirmation = ''
|
120
|
+
end
|
121
|
+
end
|
122
|
+
}
|
123
|
+
button('Update Master Password') {
|
124
|
+
on_clicked do
|
125
|
+
update_master_password_request = Adamantite::GUI::Request::UpdateMasterPasswordRequest.new(@adamantite_object)
|
126
|
+
update_master_password_screen(update_master_password_request: update_master_password_request).show
|
127
|
+
end
|
128
|
+
}
|
129
|
+
}
|
130
|
+
}
|
131
|
+
}
|
132
|
+
}
|
133
|
+
}
|
134
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require "bcrypt"
|
2
|
+
require "openssl"
|
3
|
+
require "base64"
|
4
|
+
require "json"
|
5
|
+
require "io/console"
|
6
|
+
|
7
|
+
require "file_utils/file_utils"
|
8
|
+
require "pw_utils/pw_utils"
|
9
|
+
|
10
|
+
include Adamantite::FileUtils
|
11
|
+
include Adamantite::PWUtils
|
12
|
+
|
13
|
+
puts "Welcome to Adamantite."
|
14
|
+
|
15
|
+
if pw_file_exists?('master')
|
16
|
+
user_master_pw_info = get_master_pw_info
|
17
|
+
master_pw_hash = user_master_pw_info['password']
|
18
|
+
master_pw_salt = user_master_pw_info['salt']
|
19
|
+
|
20
|
+
master_pw = IO::console.getpass("Please enter your master password:")
|
21
|
+
master_pw_comparator = BCrypt::Password.new(master_pw_hash)
|
22
|
+
|
23
|
+
while master_pw_comparator != master_pw + master_pw_salt
|
24
|
+
puts "Entered password didn't match."
|
25
|
+
master_pw = IO::console.getpass("Please enter your master password:")
|
26
|
+
master_pw_hash = BCrypt::Password.create(master_pw + user_master_pw_info['salt'])
|
27
|
+
end
|
28
|
+
|
29
|
+
puts "Master password successfully entered."
|
30
|
+
puts "Here are your stored passwords:"
|
31
|
+
get_stored_pws.each_with_index do |pw, index|
|
32
|
+
puts "#{index + 1}. #{pw}"
|
33
|
+
end
|
34
|
+
|
35
|
+
puts "Would you like to enter another password? (Y/N)"
|
36
|
+
response = gets.chomp
|
37
|
+
while !["Y", "N"].include?(response)
|
38
|
+
puts "Please enter Y or N"
|
39
|
+
end
|
40
|
+
|
41
|
+
if response == "Y"
|
42
|
+
puts "What do you want to call this password?"
|
43
|
+
title = gets.chomp
|
44
|
+
puts "What is the username for #{title}?"
|
45
|
+
username = gets.chomp
|
46
|
+
pw = IO::console.getpass("Enter the password for this site.")
|
47
|
+
pw_confirmation = IO::console.getpass("Confirm the password for this site.")
|
48
|
+
|
49
|
+
while pw != pw_confirmation
|
50
|
+
puts "Those didn't match, please enter them again."
|
51
|
+
pw = IO::console.getpass("Enter the password for this site.")
|
52
|
+
pw_confirmation = IO::console.getpass("Confirm the password for this site.")
|
53
|
+
end
|
54
|
+
|
55
|
+
pw_info_for_file = make_pw_info(username, pw, master_pw, master_pw_salt)
|
56
|
+
write_pw_to_file(title, **pw_info_for_file)
|
57
|
+
puts "Successfully stored password for #{title}."
|
58
|
+
|
59
|
+
elsif response == "N"
|
60
|
+
puts "Exiting"
|
61
|
+
end
|
62
|
+
|
63
|
+
puts "Here are your stored passwords:"
|
64
|
+
stored_pws = get_stored_pws
|
65
|
+
stored_pws.each_with_index do |pw, index|
|
66
|
+
puts "#{index + 1}. #{pw}"
|
67
|
+
end
|
68
|
+
|
69
|
+
puts "Enter the number of the password that you would like to retrieve."
|
70
|
+
pw_entry = gets.chomp.to_i
|
71
|
+
|
72
|
+
pw_info = get_pw_file(stored_pws[pw_entry - 1])
|
73
|
+
stored_pw_selection = decrypt_pw(pw_info["iv"], pw_info['password'], master_pw, master_pw_salt)
|
74
|
+
|
75
|
+
IO.popen('pbcopy', 'w') { |f| f << stored_pw_selection }
|
76
|
+
|
77
|
+
puts "Your password has been copied to your clipboard."
|
78
|
+
|
79
|
+
else
|
80
|
+
puts "You don't have a master password. Please enter one now."
|
81
|
+
master_pw = IO::console.getpass("Enter your master password:")
|
82
|
+
master_pw_confirmation = IO::console.getpass("Confirm your master password:")
|
83
|
+
|
84
|
+
while master_pw != master_pw_confirmation
|
85
|
+
puts "Those didn't match, please enter them again."
|
86
|
+
master_pw = IO::console.getpass("Enter your master password:")
|
87
|
+
master_pw_confirmation = IO::console.getpass("Confirm your master password:")
|
88
|
+
end
|
89
|
+
|
90
|
+
master_pw_info = generate_master_pw_hash(master_pw)
|
91
|
+
|
92
|
+
write_pw_to_file('master', password: master_pw_info[:master_pw_hash], salt: master_pw_info[:salt])
|
93
|
+
|
94
|
+
puts "Wrote master pw to file."
|
95
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "file_utils/file_utils"
|
2
|
+
require "pw_utils/pw_utils"
|
3
|
+
|
4
|
+
include Adamantite::FileUtils
|
5
|
+
include Adamantite::PWUtils
|
6
|
+
|
7
|
+
module Adamantite
|
8
|
+
module Base
|
9
|
+
class Adamantite
|
10
|
+
|
11
|
+
attr_accessor :authenticated
|
12
|
+
|
13
|
+
def initialize(master_pw)
|
14
|
+
@master_pw = master_pw
|
15
|
+
@authenticated = false
|
16
|
+
@master_pw_exists = pw_file_exists?('master')
|
17
|
+
end
|
18
|
+
|
19
|
+
def authenticate!
|
20
|
+
return false unless @master_pw_exists
|
21
|
+
master_pw_info = get_master_pw_info
|
22
|
+
master_pw_hash = master_pw_info['password']
|
23
|
+
master_pw_salt = master_pw_info['salt']
|
24
|
+
master_pw_comparator = generate_master_pw_comparator(master_pw_hash)
|
25
|
+
|
26
|
+
if master_pw_comparator == @master_pw + master_pw_salt
|
27
|
+
@authenticated = true
|
28
|
+
@master_pw_hash = master_pw_hash
|
29
|
+
@master_pw_salt = master_pw_salt
|
30
|
+
@stored_passwords = get_stored_pws
|
31
|
+
true
|
32
|
+
else
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def update_master_password!(new_master_pw, new_master_pw_confirmation)
|
38
|
+
return false unless new_master_pw == new_master_pw_confirmation && @authenticated
|
39
|
+
|
40
|
+
new_master_pw_info = generate_master_pw_hash(new_master_pw)
|
41
|
+
new_master_pw_hash = new_master_pw_info[:master_pw_hash]
|
42
|
+
new_master_pw_salt = new_master_pw_info[:salt]
|
43
|
+
|
44
|
+
@stored_passwords.each do |stored_password|
|
45
|
+
pw_info = get_pw_file(stored_password)
|
46
|
+
pw = decrypt_pw(pw_info['iv'], pw_info['password'], @master_pw, @master_pw_salt)
|
47
|
+
pw_info_for_file = make_pw_info(pw_info['username'], pw, new_master_pw, new_master_pw_salt)
|
48
|
+
write_pw_to_file(stored_password, **pw_info_for_file)
|
49
|
+
end
|
50
|
+
|
51
|
+
write_pw_to_file('master', password: new_master_pw_hash, salt: new_master_pw_salt)
|
52
|
+
@master_pw_hash = get_master_pw_info
|
53
|
+
@master_pw = new_master_pw
|
54
|
+
@master_pw_salt = new_master_pw_salt
|
55
|
+
true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module Adamantite
|
4
|
+
module FileUtils
|
5
|
+
def home_dir
|
6
|
+
ENV['HOME']
|
7
|
+
end
|
8
|
+
|
9
|
+
def pwmanager_dir
|
10
|
+
File.join(home_dir, '.pwmanager')
|
11
|
+
end
|
12
|
+
|
13
|
+
def pwmanager_dir_exists?
|
14
|
+
Dir.exists?(pwmanager_dir)
|
15
|
+
end
|
16
|
+
|
17
|
+
def make_pwmanager_dir
|
18
|
+
Dir.mkdir(pwmanager_dir)
|
19
|
+
end
|
20
|
+
|
21
|
+
def pw_file(title)
|
22
|
+
File.join(pwmanager_dir, title)
|
23
|
+
end
|
24
|
+
|
25
|
+
def pw_file_exists?(title)
|
26
|
+
File.exists?(pw_file(title))
|
27
|
+
end
|
28
|
+
|
29
|
+
def write_pw_to_file(title, **kwargs)
|
30
|
+
if !pwmanager_dir_exists?
|
31
|
+
make_pwmanager_dir
|
32
|
+
end
|
33
|
+
|
34
|
+
File.open(pw_file(title), "w") do |f|
|
35
|
+
JSON.dump(kwargs, f)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete_pw_file(title)
|
40
|
+
File.delete(pw_file(title))
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_pw_file(title)
|
44
|
+
JSON.load_file(pw_file(title))
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_master_pw_info
|
48
|
+
get_pw_file('master')
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_stored_pws
|
52
|
+
Dir.entries(pwmanager_dir).filter { |f| ![".", "..", "master"].include?(f) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Adamantite
|
2
|
+
module GUI
|
3
|
+
module Request
|
4
|
+
class AddPasswordRequest
|
5
|
+
|
6
|
+
attr_accessor :website_title, :username, :password, :password_confirmation, :password_saved
|
7
|
+
|
8
|
+
def initialize(master_password, master_password_salt)
|
9
|
+
@master_password = master_password
|
10
|
+
@master_password_salt = master_password_salt
|
11
|
+
@password_saved = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def confirm_and_add_password!
|
15
|
+
if @password == @password_confirmation
|
16
|
+
@password_saved = true
|
17
|
+
pw_info_for_file = make_pw_info(@username, @password, @master_password, @master_password_salt)
|
18
|
+
write_pw_to_file(@website_title, **pw_info_for_file)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Adamantite
|
2
|
+
module GUI
|
3
|
+
module Request
|
4
|
+
class LoginRequest
|
5
|
+
|
6
|
+
attr_accessor :master_password, :master_password_salt, :authenticated
|
7
|
+
|
8
|
+
def authenticate!
|
9
|
+
user_master_pw_info = get_master_pw_info
|
10
|
+
master_pw_hash = user_master_pw_info['password']
|
11
|
+
master_pw_salt = user_master_pw_info['salt']
|
12
|
+
master_pw_comparator = generate_master_pw_comparator(master_pw_hash)
|
13
|
+
|
14
|
+
if master_pw_comparator == master_password + master_pw_salt
|
15
|
+
@authenticated = true
|
16
|
+
@master_password = master_password
|
17
|
+
@master_password_salt = master_pw_salt
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Adamantite
|
2
|
+
module GUI
|
3
|
+
module Request
|
4
|
+
class SetMasterPasswordRequest
|
5
|
+
|
6
|
+
attr_accessor :new_master_pw, :new_master_pw_confirmation, :success
|
7
|
+
|
8
|
+
def set_master_password!
|
9
|
+
@success = false
|
10
|
+
if @new_master_pw == @new_master_pw_confirmation
|
11
|
+
master_pw_info = generate_master_pw_hash(@new_master_pw)
|
12
|
+
write_pw_to_file('master', password: master_pw_info[:master_pw_hash], salt: master_pw_info[:salt])
|
13
|
+
@success = true
|
14
|
+
end
|
15
|
+
@success
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Adamantite
|
2
|
+
module GUI
|
3
|
+
module Request
|
4
|
+
class UpdateMasterPasswordRequest
|
5
|
+
|
6
|
+
attr_accessor :new_master_pw, :new_master_pw_confirmation, :adamantite_object
|
7
|
+
|
8
|
+
def initialize(adamantite_object)
|
9
|
+
@adamantite_object = adamantite_object
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Adamantite
|
2
|
+
module GUI
|
3
|
+
module Screen
|
4
|
+
class CopyScreen
|
5
|
+
include Glimmer::LibUI::CustomWindow
|
6
|
+
|
7
|
+
option :password_title
|
8
|
+
|
9
|
+
body {
|
10
|
+
window('Copy', 400, 100) {
|
11
|
+
margined true
|
12
|
+
label("Copied password for #{password_title} to your clipboard.")
|
13
|
+
}
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
module PWManager
|
3
|
+
module GUI
|
4
|
+
module Screen
|
5
|
+
class LoginScreen
|
6
|
+
include Glimmer::LibUI::CustomWindow
|
7
|
+
|
8
|
+
option :login_request
|
9
|
+
|
10
|
+
body {
|
11
|
+
window('Adamantite', 400, 100) {
|
12
|
+
margined true
|
13
|
+
|
14
|
+
vertical_box {
|
15
|
+
form {
|
16
|
+
password_entry {
|
17
|
+
label 'Master Password'
|
18
|
+
text <=> [login_request, :master_password]
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
button('Login') {
|
23
|
+
on_clicked do
|
24
|
+
login_request.authenticate!
|
25
|
+
# Destroy window if password is correct.
|
26
|
+
if login_request.authenticated
|
27
|
+
body_root.destroy
|
28
|
+
::LibUI.quit
|
29
|
+
end
|
30
|
+
end
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Adamantite
|
2
|
+
module GUI
|
3
|
+
module Screen
|
4
|
+
class SetMasterPasswordScreen
|
5
|
+
include Glimmer::LibUI::CustomWindow
|
6
|
+
|
7
|
+
option :set_master_password_request
|
8
|
+
|
9
|
+
body {
|
10
|
+
window('Adamantite - Create Master Password', 450, 150) {
|
11
|
+
margined true
|
12
|
+
vertical_box {
|
13
|
+
form {
|
14
|
+
password_entry {
|
15
|
+
label 'Master Password'
|
16
|
+
text <=> [set_master_password_request, :new_master_pw]
|
17
|
+
}
|
18
|
+
password_entry {
|
19
|
+
label 'Master Password Confirmation'
|
20
|
+
text <=> [set_master_password_request, :new_master_pw_confirmation]
|
21
|
+
}
|
22
|
+
}
|
23
|
+
button('Set Master Password') {
|
24
|
+
on_clicked do
|
25
|
+
set_master_password_request.set_master_password!
|
26
|
+
if set_master_password_request.success
|
27
|
+
body_root.destroy
|
28
|
+
::LibUI.quit
|
29
|
+
else
|
30
|
+
set_master_password_request.new_master_pw = ''
|
31
|
+
set_master_password_request.new_master_pw_confirmation = ''
|
32
|
+
end
|
33
|
+
end
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Adamantite
|
2
|
+
module GUI
|
3
|
+
module Screen
|
4
|
+
class ShowScreen
|
5
|
+
include Glimmer::LibUI::CustomWindow
|
6
|
+
|
7
|
+
option :password
|
8
|
+
|
9
|
+
body {
|
10
|
+
window('Show', 400, 100) {
|
11
|
+
margined true
|
12
|
+
|
13
|
+
label("#{password}")
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Adamantite
|
2
|
+
module GUI
|
3
|
+
module Screen
|
4
|
+
class UpdateMasterPasswordScreen
|
5
|
+
include Glimmer::LibUI::CustomWindow
|
6
|
+
|
7
|
+
option :update_master_password_request
|
8
|
+
|
9
|
+
body {
|
10
|
+
window('Adamantite - Update Master Password', 450, 150) {
|
11
|
+
margined true
|
12
|
+
vertical_box {
|
13
|
+
form {
|
14
|
+
password_entry {
|
15
|
+
label 'New Master Password'
|
16
|
+
text <=> [update_master_password_request, :new_master_pw]
|
17
|
+
}
|
18
|
+
password_entry {
|
19
|
+
label 'New Master Password Confirmation'
|
20
|
+
text <=> [update_master_password_request, :new_master_pw_confirmation]
|
21
|
+
}
|
22
|
+
}
|
23
|
+
button('Update') {
|
24
|
+
on_clicked do
|
25
|
+
new_master_pw = update_master_password_request.new_master_pw
|
26
|
+
new_master_pw_confirmation = update_master_password_request.new_master_pw_confirmation
|
27
|
+
success = update_master_password_request.adamantite_object.update_master_password!(new_master_pw, new_master_pw_confirmation)
|
28
|
+
if success
|
29
|
+
body_root.destroy
|
30
|
+
::LibUI.quit
|
31
|
+
else
|
32
|
+
update_master_password_request.new_master_pw = ''
|
33
|
+
update_master_password_request.new_master_pw_confirmation = ''
|
34
|
+
end
|
35
|
+
end
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "bcrypt"
|
2
|
+
require "openssl"
|
3
|
+
require "base64"
|
4
|
+
|
5
|
+
module Adamantite
|
6
|
+
module PWUtils
|
7
|
+
|
8
|
+
def make_pw_info(username, pw, master_pw, master_pw_salt)
|
9
|
+
cipher = OpenSSL::Cipher::AES256.new(:CBC)
|
10
|
+
cipher.encrypt
|
11
|
+
iv = cipher.random_iv
|
12
|
+
cipher.key = Digest::MD5.hexdigest(master_pw + master_pw_salt)
|
13
|
+
cipher_text = cipher.update(pw) + cipher.final
|
14
|
+
utf8_cipher_text = Base64.encode64(cipher_text).encode('utf-8')
|
15
|
+
utf8_iv = Base64.encode64(iv).encode('utf-8')
|
16
|
+
|
17
|
+
{username: username, password: utf8_cipher_text, iv: utf8_iv}
|
18
|
+
end
|
19
|
+
|
20
|
+
def decrypt_pw(iv, pw_hash, master_pw, master_pw_salt)
|
21
|
+
decrypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
|
22
|
+
decrypt_cipher.decrypt
|
23
|
+
iv = Base64.decode64(iv.encode('ascii-8bit'))
|
24
|
+
decrypt_cipher.iv = iv
|
25
|
+
decrypt_cipher.key = Digest::MD5.hexdigest(master_pw + master_pw_salt)
|
26
|
+
decrypt_text = Base64.decode64(pw_hash.encode('ascii-8bit'))
|
27
|
+
decrypt_cipher.update(decrypt_text) + decrypt_cipher.final
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate_master_pw_hash(master_pw)
|
31
|
+
salt = BCrypt::Engine.generate_salt
|
32
|
+
master_pw_hash = BCrypt::Password.create(master_pw + salt)
|
33
|
+
{'salt': salt, 'master_pw_hash': master_pw_hash}
|
34
|
+
end
|
35
|
+
|
36
|
+
def generate_master_pw_comparator(master_pw_hash)
|
37
|
+
BCrypt::Password.new(master_pw_hash)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: adamantite
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jake Bruemmer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-11-08 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A local password manager written in Ruby.
|
14
|
+
email: jakebruemmer@gmail.com
|
15
|
+
executables:
|
16
|
+
- adamantite
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/adamantite
|
21
|
+
- lib/adamantite.rb
|
22
|
+
- lib/adamantite_command_line.rb
|
23
|
+
- lib/base/adamantite.rb
|
24
|
+
- lib/file_utils/file_utils.rb
|
25
|
+
- lib/gui/request/add_password_request.rb
|
26
|
+
- lib/gui/request/login_request.rb
|
27
|
+
- lib/gui/request/set_master_password_request.rb
|
28
|
+
- lib/gui/request/update_master_password_request.rb
|
29
|
+
- lib/gui/screen/copy_screen.rb
|
30
|
+
- lib/gui/screen/login_screen.rb
|
31
|
+
- lib/gui/screen/set_master_password_screen.rb
|
32
|
+
- lib/gui/screen/show_screen.rb
|
33
|
+
- lib/gui/screen/update_master_password_screen.rb
|
34
|
+
- lib/pw_utils/pw_utils.rb
|
35
|
+
homepage: https://x.com/jakebruemmer
|
36
|
+
licenses:
|
37
|
+
- MIT
|
38
|
+
metadata: {}
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
requirements: []
|
54
|
+
rubygems_version: 3.3.26
|
55
|
+
signing_key:
|
56
|
+
specification_version: 4
|
57
|
+
summary: Yet another password manager.
|
58
|
+
test_files: []
|