-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
597c25e
commit 86aa1d3
Showing
7 changed files
with
515 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# DOmain Hash password generator (DOH) | ||
|
||
The Domain Hash Password Generator (DOH) uses hashes to generate unique passwords for each domain from a single master password. | ||
|
||
## Features | ||
|
||
* Passwords are cryptographically secure. | ||
* Passwords are never stored anywhere. Not even on your own computer. | ||
* Passwords are guaranteed to be accepted by the website.\* | ||
* Passwords are reproducible from a single master password. | ||
* Multiple hashing strategies are available. | ||
|
||
\* Given a correct domain specification (see below). | ||
|
||
## Visuals | ||
|
||
After installation, the patented "DOH it!" button appears in password fields. | ||
|
||
 | ||
|
||
Pressing "DOH it!" instantly generates your secure password. | ||
|
||
 | ||
|
||
## Description | ||
|
||
DOH generates passwords based on a domain, master password, and salt. Each site has different passwords, and DOH can generate passwords that are guaranteed to have the right number of special characters, uppercase letters, numbers, etc. The algorithm does not save any state, so any computer you use can generate your passwords with information from your head -- without any syncing. This also means there is no database to be hacked or service provider you need to trust. | ||
|
||
DOH is entirely opensource, so you can audit the code yourself to make sure your passwords are safe. | ||
|
||
The following have been implemented: | ||
|
||
* Pure javascript HTML webpage generator | ||
* Chromium plugin | ||
* Firefox plugin (unfinished; see branches) | ||
|
||
|
||
|
||
##Installation | ||
|
||
You need some flavor of ruby installed (it converts the yaml file to json). | ||
|
||
git clone https://github.com/onionjake/doh.git | ||
cd doh | ||
./setup | ||
|
||
### Chromium Plugin | ||
|
||
<launch chrome/chromium> | ||
Type in 'chrome://extensions' in the address bar | ||
Check 'Developer mode' | ||
Load unpacked extension | ||
Browse to 'doh/chromium' and click Open | ||
|
||
### HTML (any browser) | ||
|
||
<launch browser> | ||
Type 'file://<cloned location>/doh/html/index.html' into the address bar | ||
<or> | ||
Goto 'https://onionjake.github.io/doh/' | ||
|
||
|
||
## Terminology | ||
|
||
Master Password: Your secret password. This should be long, secure, and follow all good password conventions. | ||
|
||
Salt: This is something unique to you. It does not need to be secret or hard to guess, just memorable. Something like your email address or your favorite online username is good. | ||
|
||
Domain: This is part of the URL of the website. It does not include www or anything else ahead of the domain. | ||
|
||
Examples: | ||
|
||
https://www.github.com/onionjake/doh - github.com | ||
|
||
https://login.yahoo.com - yahoo.com | ||
|
||
## Use | ||
|
||
### Chromium Plugin | ||
|
||
TODO | ||
|
||
### HTML (any browser) | ||
|
||
Read terminology section above | ||
Enter Master Password | ||
Enter Salt | ||
Enter Domain | ||
Enter Seqeunce String (optional) | ||
Click 'Generate Password' | ||
Copy and paste generated password into website. | ||
|
||
## The algorithm | ||
|
||
Here is the algorithm in a nutshell: | ||
|
||
1. User enters salt (username, name, or something memorable but unique), master password, and domain, and an optional sequence number. | ||
2. Compute PBKDF2(sha512, sha256(salt + master password), sequence + domain + salt, 2000, <varies on domain spec>) and output in base64.\* Remove padding characters ('='). | ||
3. Based on a domain specification (shipped with program or provided by user), translate standard base64 to a new base64 with characters from the required character set for given domain. | ||
4. Search result for password that meets all requirements set in the domain spec. | ||
5. If password cannot be found, rotate a bit and search again. | ||
6. If password cannot be found after 5 rotations, rehash and search again. | ||
|
||
\* Where '+' is concatenation. See http://en.wikipedia.org/wiki/PBKDF2 on PBKDF2 function definition. | ||
|
||
The expected number of characters needed to fulfill typical password requirements is 10 characters. Determining each domain's expected number of minimum characters to meet requirements can be done using the coupon collectors problem. | ||
|
||
## Domain Specifications | ||
|
||
TODO | ||
|
||
## License | ||
|
||
Copyright (c) 2016 Jake Willoughby | ||
|
||
This file is part of DOH. | ||
|
||
DOH is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
any later version. | ||
|
||
DOH is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
|
||
You should have received a copy of the GNU General Public License | ||
along with DOH. See gpl3.txt. If not, see <http://www.gnu.org/licenses/>. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
#!/usr/bin/env ruby | ||
# Copyright (c) 2016 Jake Willoughby | ||
# | ||
# This file is part of DOH. | ||
# | ||
# DOH is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# any later version. | ||
# | ||
# DOH is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with DOH. See gpl3.txt. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
require "pp" | ||
require "optparse" | ||
require "set" | ||
|
||
OptionParser.new do |o| | ||
o.on('--not [characters]') { |b| $not = b } | ||
o.on('-h') { puts o; exit } | ||
o.parse! | ||
end | ||
|
||
$a = {} | ||
$a["lower"] = "abcdefghijklmnopqrstuvwxyz" | ||
$a["upper"] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
$a["number"] = "0123456789" | ||
$a["special"] = " !\"#\$%&'()*+,-./:;<=>?@[\\]^_`{|}~" | ||
|
||
$a = $a.map do |k,v| | ||
[k, v.chars.sort.join] | ||
end.to_h | ||
TOTAL = 64 | ||
|
||
$out = "" | ||
|
||
puts "Give me one or more of '#{$a.keys.join ' '}' as arguments" if ARGV.length == 0 | ||
|
||
def red(s) | ||
"\e[31m#{s}\e[0m" | ||
end | ||
def yellow(s) | ||
"\e[33m#{s}\e[0m" | ||
end | ||
|
||
sets = ARGV.map {|b| | ||
abort red("Error: #{b} is not one of '#{$a.keys.join ' '}'.") unless $a.has_key? b | ||
$a[b] | ||
} | ||
|
||
sets.sort_by { |v| | ||
v.length | ||
}.each_with_index { |b, i| | ||
b.delete! $not if $not | ||
want = (TOTAL - $out.length) / (sets.length - i) | ||
#puts "want #{want} = #{(TOTAL - $out.length)} / #{(sets.length - i)}" | ||
want = b.length if b.length < want | ||
$out += b.chars.sort.join[0..want-1] | ||
} | ||
|
||
if $out.length != 64 | ||
abort red("Error: Got '#{$out}' of length #{$out.length} which != 64") | ||
end | ||
|
||
|
||
u = Set.new $out.chars | ||
# figure out which characters are duplicated | ||
dups = $out.chars.reject { |c| u.delete(c) if u.include?(c) } | ||
dl = dups.length | ||
if dl > 0 | ||
abort red("Error: The character#{dl>1?'s':''} '#{dups.join}' occur#{dl>1?'':'s'} more than once. This reduces entropy and is not safe!") | ||
end | ||
|
||
$out = $out.chars.sort.join | ||
|
||
puts yellow("#{' '*$out.index(' ')}/---- Note: don't forget to include the space") if $out =~ / / | ||
|
||
puts $out |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#!/usr/bin/env ruby | ||
# Copyright (c) 2016 Jake Willoughby | ||
# | ||
# This file is part of DOH. | ||
# | ||
# DOH is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# any later version. | ||
# | ||
# DOH is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with DOH. See gpl3.txt. If not, see <http://www.gnu.org/licenses/>. | ||
require "json" | ||
require "yaml" | ||
require "pp" | ||
|
||
$specs = YAML.load(File.open("domain_specs.yaml")) | ||
|
||
puts JSON.pretty_generate($specs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
#!/usr/bin/env ruby | ||
# Copyright (c) 2016 Jake Willoughby | ||
# | ||
# This file is part of DOH. | ||
# | ||
# DOH is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# any later version. | ||
# | ||
# DOH is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with DOH. See gpl3.txt. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
require 'openssl' | ||
require "base64" | ||
require "open3" | ||
require 'io/console' | ||
require "yaml" | ||
require 'date' | ||
|
||
require_relative "./doh" | ||
|
||
file = __FILE__ | ||
file = File.readlink(file) if File.symlink? file | ||
|
||
SCRIPT_DIR = File.dirname(file) | ||
|
||
def red(s) | ||
"\e[31m#{s}\e[0m" | ||
end | ||
def yellow(s) | ||
"\e[33m#{s}\e[0m" | ||
end | ||
def green(s) | ||
"\e[32m#{s}\e[0m" | ||
end | ||
|
||
salt = ARGV.shift | ||
unless salt | ||
puts "Usage: #{$0} <salt>" | ||
abort red("Error: no salt given") | ||
end | ||
|
||
$specs = YAML.load(File.open(SCRIPT_DIR + "/domain_specs.yaml")) | ||
|
||
print "Password: " | ||
pass = $stdin.noecho(&:gets).strip | ||
puts | ||
|
||
iterations = 10000 | ||
|
||
digest = OpenSSL::Digest::SHA256.new | ||
ss = Digest::SHA256.digest(salt + pass) | ||
id = Digest::SHA256.hexdigest(salt + pass)[-4..-1] | ||
puts "Your master password id is #{red(id)}" | ||
|
||
print "Pin: " | ||
pin = $stdin.noecho(&:gets).strip | ||
puts | ||
puts | ||
|
||
$pin_attempts = 0 | ||
|
||
xclip = File.exist?("/usr/bin/xclip") | ||
pbcopy = File.exist?("/usr/bin/pbcopy") | ||
|
||
|
||
while true | ||
print "Domain: " | ||
domain = $stdin.gets.strip | ||
unless domain | ||
puts red("Error: no domain given") | ||
end | ||
|
||
if $specs.has_key? domain | ||
myspec = $specs[domain] | ||
puts "got domain" | ||
else | ||
myspec = $specs["defaults"] | ||
end | ||
print "Sequence: " | ||
seq = $stdin.gets.strip | ||
seq = "" unless seq | ||
|
||
print "Pin: " | ||
check_pin = $stdin.noecho(&:gets).strip | ||
puts | ||
|
||
if pin.strip != check_pin.strip | ||
$pin_attempts += 1 | ||
if $pin_attempts >= 3 | ||
abort red("Too many pin attempts!") | ||
end | ||
puts yellow("Incorrect pin!") | ||
puts | ||
next | ||
end | ||
$pin_attempts = 0 | ||
|
||
puts myspec['length'] | ||
pwd = doh(myspec, ss, seq, domain, salt, iterations, myspec['length'], digest) | ||
|
||
File.open(ENV["HOME"] + "/.dohlog",'a') do |f| | ||
f.puts "#{DateTime.now} #{domain} #{seq}" | ||
end | ||
|
||
|
||
if xclip || pbcopy | ||
if xclip | ||
Open3.popen2("xclip -i -selection clipboard") do |i,o,t| | ||
i.print pwd | ||
i.flush | ||
i.close | ||
t.value | ||
end | ||
end | ||
if pbcopy | ||
Open3.popen2("pbcopy") do |i,o,t| | ||
i.print pwd | ||
i.flush | ||
i.close | ||
t.value | ||
end | ||
end | ||
puts "Password Copied to clipboard." | ||
else | ||
puts yellow("Generated Password:") | ||
puts yellow("#{' '*pwd.index(' ')}/---- Note: don't forget to include the space") if pwd =~ / / | ||
puts pwd | ||
end | ||
|
||
puts | ||
end | ||
|
Oops, something went wrong.