=T0k3n!z3R=
A quick and dirty password key derivation tool.
Only one good master password for an infinity of strong unique tokens.
No subscription - no account - no storage - no leaks - works offline & from terminal
« One Ring to rule them all, One Ring to find them, One Ring to bring them all and in the darkness bind them. »
— J.R.R. Tolkien
Node.js ▾
#!/usr/bin/node
"use strict";
const util = require('util');
const strenc = new util.TextEncoder('utf-8');
const {subtle} = require('crypto').webcrypto;
var readline = require('readline');
var reader = readline.createInterface({input: process.stdin, output: process.stdout, terminal: true});
var prompt = (query) => new Promise((resolve) => reader.question(query, resolve));
(async () => {
const buff2hex = function(buffer) { return [...new Uint8Array(buffer)].map(x => x.toString(16).padStart(2, '0')).join(''); }
const buff2pin = function(token_buffer)
{
var hex_data = buff2hex(token_buffer);
var hex_word = hex_data.match(/(.{1,6})/g);
var hexpin = parseInt(hex_word.shift(), 16);
for (var i=0; i<hex_word.length; i++) hexpin = (hexpin ^ parseInt(hex_word[i], 16));
return hexpin;
}
const tokenizer = function(auth, host, key)
{
const pbkdf2_pref = {name: 'PBKDF2', hash: 'SHA-256', salt: strenc.encode(key), iterations: 300000};
const hmac_pref = {name: 'HMAC', hash: 'SHA-512'};
return new Promise(function(resolve)
{
subtle.importKey('raw', strenc.encode(auth), 'PBKDF2', false, ['deriveBits'])
.then(pbkdf2_key => subtle.deriveBits(pbkdf2_pref, pbkdf2_key, 64*8))
.then(pbkdf2_hash => subtle.importKey('raw', strenc.encode(buff2hex(pbkdf2_hash)), hmac_pref, false, ['sign']))
.then(hmac_key => subtle.sign('HMAC', hmac_key, strenc.encode(host)))
.then(hmac_hash => subtle.digest('SHA-1', hmac_hash))
.then(function(token_sha1)
{
var token = btoa(String.fromCharCode(...new Uint8Array(token_sha1)));
var pin = token.substring(token.length - 4) + buff2pin(token_sha1);
resolve({token: token, pin: pin});
});
})
}
const digest = async function(user, login, server, password)
{
const key = await subtle.digest('SHA-512', strenc.encode(password));
const host = await subtle.digest('SHA-512', strenc.encode(login + server));
const auth = await subtle.digest('SHA-512', strenc.encode(user + buff2hex(host)));
return {auth: auth, host: host, key: key};
}
//--[ User inputs ]-----------------------------------------------
const user = await prompt("User: ");
console.log("Password:");
reader._writeToOutput = function(input){this.output.write('')};
const password = await prompt("\n");
reader._writeToOutput = function(input){this.output.write(input)};
const login = await prompt("\nLogin: ");
const server = await prompt("Server: ");
//--[ SHA512 digest + Tokenize with PBKDF2 / HMAC ]---------------
digest(user, login, server, password)
.then(hash => tokenizer(buff2hex(hash.auth), buff2hex(hash.host), buff2hex(hash.key)))
.then(function(results)
{
console.log("\nToken: !" + results.token);
console.log("PIN: !" + results.pin);
});
reader.close();
})()
(use Web Crypto API)
JavaScript ▾
function tokenizer(auth, host, key)
{
var pbkdf2 = CryptoJS.PBKDF2(auth, key, {hasher: CryptoJS.algo.SHA256, keySize: 512/32, iterations: 300000});
var hmac512 = CryptoJS.HmacSHA512(host, pbkdf2.toString());
return CryptoJS.SHA1(hmac512).toString(CryptoJS.enc.Base64);
}
function token2pin(token_b64)
{
var hex_data = CryptoJS.enc.Base64.parse(token_b64).toString();
var hex_word = hex_data.match(/(.{1,6})/g);
var hexpin = parseInt(hex_word.shift(), 16);
for (var i=0; i<hex_word.length; i++) hexpin = (hexpin ^ parseInt(hex_word[i], 16));
return token_b64.substring(token_b64.length - 4) + hexpin;
}
function get_token(user, login, server, password)
{
var host = CryptoJS.SHA512(login + server).toString();
var auth = CryptoJS.SHA512(user + host).toString();
var key = CryptoJS.SHA512(password).toString();
var token = tokenizer(auth, host, key);
var pin = token2pin(token);
return {token: token, pin: pin}
}
(requires Crypto-JS 🐢)
Linux shell ▾
#!/bin/bash
set +o allexport
#--[ User inputs ]------------------------------------------------------------------------------
read -p "User: " USER
read -s -p "Password: " PASS
echo -e "\n"
read -p "Login: " LOGIN
read -p "Server: " HOST
#--[ SHA512 digest inputs ]---------------------------------------------------------------------
PASS=$(echo -n "$PASS" | sha512sum | cut -d ' ' -f 1)
HOST=$(echo -n "$LOGIN$HOST" | sha512sum | cut -d ' ' -f 1)
USER=$(echo -n "$USER$HOST" | sha512sum | cut -d ' ' -f 1)
#--[ Tokenize with PBKDF2 / HMAC ]--------------------------------------------------------------
PBKDF2=$(echo -n "$USER" | nettle-pbkdf2 --iterations=300000 --length=64 "$PASS" | sed 's/ //g')
HMAC512=$(echo -n "$HOST" | openssl dgst -sha512 -hmac "$PBKDF2" | cut -d ' ' -f 2)
#--[ SHA1 + BASE64 for convenient output ]------------------------------------------------------
TOKEN=$(echo -n "$HMAC512" | xxd -r -p | sha1sum -b | cut -d ' ' -f 1 | xxd -r -p | base64 -w 0)
#--[ User output ]------------------------------------------------------------------------------
echo -e "\nToken: !$TOKEN"
#--[ PIN alternative ]---------------------------------------------------------------------------------------------
HEXA=$(echo "$TOKEN" | base64 -d | xxd -p -u)
HWORD=$(for ((i=0; i<=$(( ${#HEXA} / 6 )); i++)); do echo "$HEXA" | cut -c $(( i * 6 + 1 ))-$(( i * 6 + 6 )); done)
XWORD=$(echo $HWORD | sed 's/ / ^ 0x/g')
echo -e "PIN: ${TOKEN: -4}$(( 0x$XWORD ))"
(requires Nettle and OpenSSL)
Python script ▾
#!/usr/bin/python3
import getpass
import hashlib
import base64
import hmac
#--[ User inputs ]------------------------------------------------------------------------------------------
user = input("User: ")
password = getpass.getpass()
login = input("\nLogin: ")
host = input("Server: ")
#--[ SHA512 digest inputs ]---------------------------------------------------------------------------------
hash_password = hashlib.sha512(password.encode('utf-8')).hexdigest()
hash_host = hashlib.sha512(login.encode('utf-8') + host.encode('utf-8')).hexdigest()
hash_user = hashlib.sha512(user.encode('utf-8') + hash_host.encode('utf-8')).hexdigest()
#--[ Tokenize with PBKDF2/HMAC ]----------------------------------------------------------------------------
pbkdf2 = hashlib.pbkdf2_hmac('sha256', hash_user.encode('utf-8'), hash_password.encode('utf-8'), 300000, 64)
hmac512 = hmac.new(pbkdf2.hex().encode('utf-8'), hash_host.encode('utf-8'), hashlib.sha512)
#--[ SHA1 + BASE64 for convenient output ]------------------------------------------------------------------
sha1_token = hashlib.sha1(hmac512.digest())
b64_token = base64.b64encode(sha1_token.digest()).decode('utf-8')
#--[ User output ]------------------------------------------------------------------------------------------
print("\nToken: !" + b64_token)
#--[ PIN alternative ]--------------------------------------------------------------------------------------
hex_word = []
hex_token = sha1_token.hexdigest().upper()
for i in range(0, len(hex_token), 6): hex_word.append(hex_token[i:i+6])
pin_eval = '^0x'.join(str(n) for n in hex_word)
pin_token = eval('0x' + pin_eval)
print("PIN: " + b64_token[-4:] + str(pin_token))
(use hashlib and hmac modules)
PHP script ▾
#!/usr/bin/php
<?php if ( php_sapi_name() !== 'cli' ) die('This script should not be run remotely.');
#--[ Tokenize with PBKDF2 / HMAC ]-----------------------------------------------------
function tokenizer($auth, $host, $key)
{
$pbkdf2 = hash_pbkdf2('sha256', $auth, $key, 300000, 128);
$hmac = hash_hmac('sha512', $host, $pbkdf2);
return base64_encode(hash('sha1', hex2bin($hmac), true));
}
#--[ PIN alternative ]-----------------------------------------------------------------
function token2pin($token)
{
$hex_data = strtoupper(bin2hex(base64_decode($token)));
$hex_word = explode(':', chunk_split($hex_data, 6, ':'));
$hex_pin = hexdec(array_shift($hex_word));
foreach($hex_word as $word) if (!empty($word)) $hex_pin = $hex_pin ^ hexdec($word);
return(substr($token, -4) . $hex_pin);
}
#--[ User inputs ]---------------------------------------------------------------------
$user = readline('User: ');
echo "Password: ";
system('stty -echo');
$password = trim(fgets(STDIN));
system('stty echo');
echo "\n\n";
$login = readline('Login: ');
$server = readline('Server: ');
#--[ SHA512 digest inputs ]------------------------------------------------------------
$host = hash('sha512', $login . $server);
$auth = hash('sha512', $user . $host);
$key = hash('sha512', $password);
#--[ User output ]---------------------------------------------------------------------
$token = tokenizer($auth, $host, $key);
$pin = token2pin($token);
echo "\nToken: !$token";
echo "\nPIN: $pin\n";
(use crypto stuff from PHP core)