Added python GUI and HTML versions
Simple web interface using the API should be mobile compatible. A simple GUI interface using the original python script as the basis.
This commit is contained in:
parent
2fa784ba35
commit
b036453851
166
html/index.html
Normal file
166
html/index.html
Normal file
@ -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>
|
||||||
|
|
65
pwnedGUI.py
Normal file
65
pwnedGUI.py
Normal file
@ -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()
|
Loading…
Reference in New Issue
Block a user