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