-
Notifications
You must be signed in to change notification settings - Fork 121
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Simple web interface using the API should be mobile compatible. A simple GUI interface using the original python script as the basis.
- Loading branch information
1 parent
2fa784b
commit b036453
Showing
2 changed files
with
231 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,166 @@ | ||
<!DOCTYPE html> | ||
<!-- FYI must be serverd via HTTPS to function properly --> | ||
<html> | ||
<head> | ||
<meta charset='UTF-8'/> | ||
<title>Pwned Password API lookup.</title> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0"> | ||
<style> | ||
* { | ||
box-sizing: border-box; | ||
} | ||
|
||
html, body { | ||
background-color:lightblue; | ||
cursor:pointer; | ||
height:100%; | ||
width:100%; | ||
padding:0; | ||
margin:0; | ||
} | ||
|
||
b { | ||
color : red; | ||
} | ||
|
||
#bodyDiv { | ||
border:2px solid blue; | ||
border-radius:7px; | ||
height:150px; | ||
width:600px; | ||
margin:80px; | ||
padding:25px; | ||
background-color:darkgrey; | ||
color:black; | ||
} | ||
|
||
@media only screen and (max-width : 700px) { | ||
|
||
#bodyDiv { | ||
border:2px solid blue; | ||
border-radius:7px; | ||
width:100%; | ||
height:80%; | ||
margin:0; | ||
padding:25px; | ||
background-color:darkgrey; | ||
color:black; | ||
} | ||
|
||
#hash { | ||
font-size:10px; | ||
} | ||
|
||
} | ||
|
||
</style> | ||
</head> | ||
<body> | ||
<div id="bodyDiv"> | ||
<span title="Password does not leave the browser, but it will show on the screen.">?</span> | ||
<input id="inPut" type="text" placeholder="Password..."> | ||
<button title="Click to send part of hashed password.">Check</button> | ||
<div> | ||
<p id="outPut"></p> | ||
</div> | ||
|
||
</div> | ||
<a href="https://github.com/mikepound/pwned-search">The inspiration!</a> | ||
<script> | ||
let first = ""; | ||
let last = ""; | ||
let hash = ""; | ||
function hexString(buffer) { | ||
// creates an array of 8-bit unsigned integers | ||
const byteArray = new Uint8Array(buffer); | ||
// turns that hash into hex | ||
const hexCodes = [...byteArray].map(value => { | ||
// eash element in array is converted to base 16 string | ||
const hexCode = value.toString(16); | ||
// pad beggining with 0 why? | ||
const paddedHexCode = hexCode.padStart(2, '0'); | ||
// return upper case hex hash | ||
return paddedHexCode.toUpperCase(); | ||
}); | ||
// return a string not an array | ||
return hexCodes.join(''); | ||
} | ||
|
||
function digestMessage(message) { | ||
// Returns a newly constructed TextEncoder that will generate a byte stream with utf-8 encoding. | ||
const encoder = new TextEncoder(); | ||
// Takes a USVString as input, and returns a Uint8Array containing utf-8 encoded text. | ||
const data = encoder.encode(message); | ||
// returns the hash | ||
//The digest() method of the SubtleCrypto interface generates a digest of the given data. | ||
// A digest is a short fixed-length value derived from some variable-length input. | ||
return window.crypto.subtle.digest('SHA-1', data); | ||
} | ||
|
||
function runCheck() { | ||
let outPut = document.getElementById("outPut"); | ||
let inPut = document.getElementById("inPut"); | ||
let text = inPut.value; | ||
if (text === "") return false; | ||
inPut.value = ""; | ||
|
||
|
||
digestMessage(text).then(digestValue => { | ||
hash = hexString(digestValue); | ||
first = hash.substring(0, 5); | ||
last = hash.substring(5); | ||
fetch('https://api.pwnedpasswords.com/range/' + first) | ||
.then( | ||
function(response) { | ||
if (response.status !== 200) { | ||
console.log('Looks like there was a problem. Status Code: ' + | ||
response.status); | ||
return; | ||
} | ||
return response.text(); | ||
}) | ||
.then(function(text) { | ||
return text.split("\r\n"); | ||
}) | ||
.then(function(arr){ | ||
|
||
document.getElementById("outPut") | ||
.innerHTML = "The password : " + text + | ||
"SHA1 Hash : " + hash + | ||
"<br>Was not found!"; | ||
|
||
arr.forEach((s)=>{ | ||
|
||
let a = s.split(":"); | ||
|
||
if(a[0] == last) { | ||
|
||
document.getElementById("outPut") | ||
.innerHTML = "The password : " + text + | ||
"<br>SHA1 Hash : <span id='hash'>" + hash + | ||
"</span><br>Was found <b>" + a[1] + "</b> times!"; | ||
|
||
return true; | ||
|
||
} | ||
|
||
}); | ||
|
||
}) | ||
.catch(function(error) { | ||
log('Request failed', error) | ||
}); | ||
|
||
}); | ||
|
||
outPut.value = ""; | ||
|
||
} | ||
|
||
document.querySelector("button") | ||
.addEventListener("click", runCheck); | ||
|
||
</script> | ||
</body> | ||
</html> | ||
|
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,65 @@ | ||
#!/usr/bin/env python | ||
from tkinter import Tk, Label, Button, Entry, StringVar, IntVar, END, W, E | ||
from pwned import lookup_pwned_api | ||
import hashlib | ||
import sys | ||
|
||
try: | ||
import requests | ||
except ModuleNotFoundError: | ||
print("### pip install requests ###") | ||
raise | ||
|
||
|
||
class Calculator: | ||
|
||
def __init__(self, master): | ||
self.master = master | ||
master.title("pwned GUI by PPC") | ||
self.p = ""; | ||
|
||
self.head_label_text = "" | ||
self.head_label = Label(master, textvariable=self.head_label_text) | ||
self.label = Label(master, text="Enter the password to check : ") | ||
|
||
vcmd = master.register(self.validate) # we have to wrap the command | ||
self.entry = Entry(master, validate="key", validatecommand=(vcmd, '%P')) | ||
|
||
self.submit_button = Button(master, text="Check", command=lambda: self.update("submit")) | ||
|
||
self.total_label_text = "Number of times Password found : ...." | ||
self.total_text = StringVar() | ||
self.total_text.set(self.total_label_text) | ||
self.total_label = Label(master, textvariable=self.total_text) | ||
self.total = Label(master, text="") | ||
|
||
# LAYOUT | ||
|
||
self.label.grid(row=0, column=0, sticky=W, padx=(25, 25), pady=(25, 10)) | ||
|
||
self.head_label.grid(row=0, column=1, columnspan=4, sticky=E, padx=(25, 25), pady=(10, 10)) | ||
|
||
self.entry.grid(row=2, column=0, columnspan=3, sticky=W+E, padx=(25, 25), pady=(10, 10)) | ||
|
||
self.submit_button.grid(row=2, column=4, sticky=W+E, padx=(25, 25), pady=(10, 10)) | ||
|
||
self.total_label.grid(row=4, column=0, columnspan=3, sticky=W, padx=(25, 25), pady=(10, 20)) | ||
|
||
def validate(self, new_text): | ||
if not new_text: # the field is being cleared | ||
return True | ||
try: | ||
self.p = new_text | ||
return True | ||
except ValueError: | ||
return False | ||
|
||
def update(self, method): | ||
self.total = "Number of times Password found : " + str(lookup_pwned_api(self.p)[1]) | ||
self.total_text.set(self.total) | ||
self.entry.delete(0, END) | ||
|
||
|
||
root = Tk() | ||
my_gui = Calculator(root) | ||
root.mainloop() |