Skip to content

Instantly share code, notes, and snippets.

@mwgamera
Created February 9, 2020 09:30
Show Gist options
  • Save mwgamera/b228cbf3afb2059699ea0521de859789 to your computer and use it in GitHub Desktop.
Save mwgamera/b228cbf3afb2059699ea0521de859789 to your computer and use it in GitHub Desktop.
#!/usr/bin/env nickle
# klg, Jul 2015
int[16] ChaCha20(int[16] x)
/* ChaCha20 hash function. */
{
int[16] s = x;
const int M = 0xffffffff;
void arx(int a, int b, int c, int r) {
s[c] ^= s[a] = (s[a] + s[b]) & M;
s[c] = (s[c] << r | s[c] >> (32 - r)) & M;
}
void qround(int a, int b, int c, int d) {
arx(a, b, d, 16);
arx(c, d, b, 12);
arx(a, b, d, 8);
arx(c, d, b, 7);
}
for (int i = 0; i < 10; i++) {
qround(0, 4, 8,12);
qround(1, 5, 9,13);
qround(2, 6,10,14);
qround(3, 7,11,15);
qround(0, 5,10,15);
qround(1, 6,11,12);
qround(2, 7, 8,13);
qround(3, 4, 9,14);
}
for (int i = 0; i < 16; i++)
s[i] = (s[i] + x[i]) & M;
return s;
}
namespace Random {
global int[16] state = {
0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, 0, ... };
global int buffer = 0, buffill = 0;
void reseed() {
twixt(file f = File::open("/dev/urandom", "r"); File::close(f)) {
for (int i = 4; i < 12+3; i++) {
int s = 0;
for (int k = 0; k < 4; k++)
s <<= 8, s |= File::getb(f);
state[i] = s;
}
state[15] = 0;
}
}
int getbits(int n) {
while (buffill < n) {
if (state[15] == 0)
reseed();
int[] b = ChaCha20(state);
state[15] = (state[15] + 1) & 0xffffffff;
for (int i = 0; i < dim(b); i++) {
buffer <<= 32;
buffer ^= b[i];
buffill += 32;
}
}
int r = buffer & ((1 << n) - 1);
buffer >>= n;
buffill -= n;
return r;
}
public int randint(int range)
/* Return random integer in 0 .. 'range'-1 */
{
int x, b = 16 + bit_width(range),
W = (1 << b) - 1, M = W - (W % range);
do {
x = getbits(b);
} while (x >= M);
return x % range;
}
}
real log2(real a)
/* Base-2 logarithm of a with exact integer results. */
{
real c = Math::log2(a);
int z = floor(c + 0.5);
if (is_rational(a) && 2 ** z == a)
return z;
return c;
}
string mkpass(real strength, string alpha)
/* Create random password of given strength in bits. */
{
int N = String::length(alpha);
int L = ceil(strength / log2(N));
return String::new((int[L]){
[i] = alpha[Random::randint(N)] });
}
string alphabet(string desc)
/* Expand ranges in short description of the alphabet. */
{
void[int] h = { => <> };
for (int i = 0; i < String::length(desc); i++)
if (desc[i] == '-' &&
i-1 >= 0 && i+1 < String::length(desc)) {
for (int j = desc[i-1]; j < desc[i+1]; j++)
h[j] = <>;
} else {
h[desc[i]] = <>;
}
return String::new(hash_keys(h));
}
if (dim(argv) > 0) {
void die(string s ...) {
for (int i = 0; i < dim(s); i++)
File::fprintf(stderr, "%s: %s\n", argv[0], s[i]);
File::fprintf(stderr, "Usage: %s [strength] [alphabet]\n", argv[0]);
exit(1);
}
poly a = <>, s = <>;
if (dim(argv) > 2) {
s = string_to_real(argv[1]);
a = alphabet(argv[2]);
}
else if (dim(argv) > 1) {
bool q = String::length(argv[1]) > 0;
q &&= argv[1][0] != '0';
for (int i = 0; i < String::length(argv[1]); i++)
q &&= String::inchars(argv[1][i], "0123456789");
if (q)
s = string_to_real(argv[1]);
else
a = alphabet(argv[1]);
}
if (is_void(a))
a = alphabet("0-9A-Za-z");
else if (String::length(a) < 2)
die("Alphabet must have at least 2 symbols.");
if (is_void(s))
s = max(floor(15 * log2(String::length(a))), 80);
else if (s <= 0)
die("Strength must be a positive integer in bits.");
if (s <= 40)
File::fprintf(stderr,
"%s: Warning: Creating a very weak password.\n", argv[0]);
printf("%s\n", mkpass(s, a));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment