=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

Private & secret

User: (your name)
Password: (secret key or passphrase)

Public & known

Login: (e-mail or alias or pseudonym)
Server: (domain or hostname)
 
(may take a while...)
 
Alternative PIN code:
☞ powered by https://developer.mozilla.org/en-US/docs/Web/API/Crypto

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)

☞ check spectre.app or lesspass.com for serious alternatives

512kb.club wtfpl.net w3c.org