Skip to content

Instantly share code, notes, and snippets.

@hrishioa
Created June 30, 2023 15:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hrishioa/1d0452415c47eb0d6a8b78cc6ddc3f4b to your computer and use it in GitHub Desktop.
Save hrishioa/1d0452415c47eb0d6a8b78cc6ddc3f4b to your computer and use it in GitHub Desktop.
Implementation for generating short, human-readable and pronounceable random strings from integers, and back again.
// Proquint implementation based on https://arxiv.org/html/0901.4016
const uint2consonant: string[] = [
'b',
'd',
'f',
'g',
'h',
'j',
'k',
'l',
'm',
'n',
'p',
'r',
's',
't',
'v',
'z',
];
const uint2vowel: string[] = ['a', 'i', 'o', 'u'];
const MASK_FIRST4 = 0xf0000000;
const MASK_FIRST2 = 0xc0000000;
function quint2hex(inp: string): string {
let ret = 'x';
let remaining = inp;
while (remaining.length > 0) {
let current = remaining.substring(0, 11);
if (
!current.match(/^[bdfghjklmnprstvzaiou]{5}-[bdfghjklmnprstvzaiou]{5}$/)
) {
console.error(
'bad quint format detected. expected format cvcvc-cvcvc - found: ' +
current
);
throw 'bad quint format for: ' + inp;
}
if (remaining.length > 12) remaining = remaining.substring(12);
else remaining = remaining.substring(11);
let currentHex = quint2uint(current).toString(16);
while (currentHex.length < 8) currentHex = '0' + currentHex;
ret += currentHex;
}
return ret;
}
function hex2quint(hex: string): string {
let sep = '-';
let ret = '';
if (!hex.match(/^x(([0-9a-f]{2}){2})+$/)) {
console.error('Bad hex length: ' + hex.length);
throw 'Bad hex length: ' + hex.length;
}
let remaining = hex.substring(1); //skip x
while (remaining.length > 0) {
if (remaining.length < 8 && remaining.length % 8 != 0) {
console.error('Bad hex length: ' + remaining.length);
throw 'Bad hex length: ' + hex;
}
let current = remaining.substring(0, 8);
remaining = remaining.substring(8);
let currentQuint = uint2quint(parseInt(current, 16) >> 0);
ret += currentQuint;
if (sep) ret += sep;
}
if (ret.endsWith(sep)) ret = ret.substring(0, ret.length - sep.length);
return ret;
}
var crypto = require('crypto');
export function getRandom32UInt() {
return crypto.randomBytes(4).readUInt32BE(0, true);
}
// 32 bits of entropy, add more quints to get to more
export function uint2quint(i: number): string {
let j: number;
let quint = '';
j = i & MASK_FIRST4;
i <<= 4;
j >>>= 28;
quint += uint2consonant[j];
j = i & MASK_FIRST2;
i <<= 2;
j >>>= 30;
quint += uint2vowel[j];
j = i & MASK_FIRST4;
i <<= 4;
j >>>= 28;
quint += uint2consonant[j];
j = i & MASK_FIRST2;
i <<= 2;
j >>>= 30;
quint += uint2vowel[j];
j = i & MASK_FIRST4;
i <<= 4;
j >>>= 28;
quint += uint2consonant[j];
quint += '-';
j = i & MASK_FIRST4;
i <<= 4;
j >>>= 28;
quint += uint2consonant[j];
j = i & MASK_FIRST2;
i <<= 2;
j >>>= 30;
quint += uint2vowel[j];
j = i & MASK_FIRST4;
i <<= 4;
j >>>= 28;
quint += uint2consonant[j];
j = i & MASK_FIRST2;
i <<= 2;
j >>>= 30;
quint += uint2vowel[j];
j = i & MASK_FIRST4;
i <<= 4;
j >>>= 28;
quint += uint2consonant[j];
return quint;
}
export function quint2uint(quint: string): number {
let res = 0;
let remaining = quint.length;
let i = 0;
while (remaining > 0) {
let c = quint[i++];
remaining--;
switch (c) {
/* consonants */
case 'b':
res <<= 4;
res += 0;
break;
case 'd':
res <<= 4;
res += 1;
break;
case 'f':
res <<= 4;
res += 2;
break;
case 'g':
res <<= 4;
res += 3;
break;
case 'h':
res <<= 4;
res += 4;
break;
case 'j':
res <<= 4;
res += 5;
break;
case 'k':
res <<= 4;
res += 6;
break;
case 'l':
res <<= 4;
res += 7;
break;
case 'm':
res <<= 4;
res += 8;
break;
case 'n':
res <<= 4;
res += 9;
break;
case 'p':
res <<= 4;
res += 10;
break;
case 'r':
res <<= 4;
res += 11;
break;
case 's':
res <<= 4;
res += 12;
break;
case 't':
res <<= 4;
res += 13;
break;
case 'v':
res <<= 4;
res += 14;
break;
case 'z':
res <<= 4;
res += 15;
break;
/* vowels */
case 'a':
res <<= 2;
res += 0;
break;
case 'i':
res <<= 2;
res += 1;
break;
case 'o':
res <<= 2;
res += 2;
break;
case 'u':
res <<= 2;
res += 3;
break;
/* separators */
default:
break;
}
}
return res >>> 0; //the unsigned right shift fixes signed int issue - http://stackoverflow.com/a/17106974/1777150
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment