Skip to content

Instantly share code, notes, and snippets.

@allfro
Last active June 17, 2024 02:38
Show Gist options
  • Save allfro/3912f94d0cfcf98702271f4120285a68 to your computer and use it in GitHub Desktop.
Save allfro/3912f94d0cfcf98702271f4120285a68 to your computer and use it in GitHub Desktop.
Generate wireguard keypairs using pure postgres plpgsql
-- A pure PL/pgSQL implementation of Curve25519
--
-- This module supports both a low-level interface through _raw_curve25519(base_point, secret)
-- and wg_gen_pubkey(secret) that takes a 32-byte block of data as input. It also provides
-- wg_gen_key() function that generates a random 32-byte private key.
--
-- imports gen_random_bytes function
create extension pgcrypto;
-- tuple data type
create type tuple2 as
(
one numeric,
two numeric
);
-- cast support for bytea to numeric
create or replace function bytea2numeric(b bytea) returns numeric as
$$
declare
r numeric := 0;
i bigint := 0;
begin
for i in reverse (length(b) - 1)..0
loop
r = (r << 8) | get_byte(b, i);
end loop;
return r;
end;
$$ language plpgsql;
create cast (bytea as numeric) with function bytea2numeric;
-- cast support for numeric to bytea
create or replace function numeric2bytea(n numeric) returns bytea as
$$
declare
r bytea := '\x';
begin
while n != 0
loop
r := set_byte(r || bytea '0', length(r), (n & 0xff)::int);
n := n >> 8;
end loop;
return r;
end;
$$ language plpgsql;
create cast (numeric as bytea) with function numeric2bytea;
-- implementation of bitwise & operator for the numeric data type
create or replace function numeric_and(leftarg numeric, rightarg numeric) returns numeric as
$$
declare
width int := 32;
modulo bigint := 2 ^ width;
b1 bigint := 0;
b2 bigint := 0;
r numeric := 0;
i integer := 0;
begin
while leftarg != 0 and rightarg != 0
loop
b1 := leftarg % modulo;
b2 := rightarg % modulo;
r := r + ((b1 & b2)::numeric << (width * i));
leftarg = leftarg >> width;
rightarg = rightarg >> width;
i := i + 1;
end loop;
return r;
end;
$$ language plpgsql;
create operator & (
function = numeric_and,
leftarg = numeric,
rightarg = numeric
);
-- implementation of bitwise | operator for the numeric data type
create or replace function numeric_or(leftarg numeric, rightarg numeric) returns numeric as
$$
declare
width int := 32;
modulo bigint := 2 ^ width;
b1 bigint := 0;
b2 bigint := 0;
r numeric := 0;
i integer := 0;
begin
while leftarg != 0 or rightarg != 0
loop
b1 := leftarg % modulo;
b2 := rightarg % modulo;
r := r + ((b1 | b2)::numeric << (width * i));
leftarg = leftarg >> width;
rightarg = rightarg >> width;
i := i + 1;
end loop;
return r;
end;
$$ language plpgsql;
create operator | (
function = numeric_or,
leftarg = numeric,
rightarg = numeric
);
-- implementation of bitwise # operator for the numeric data type
create or replace function numeric_xor(leftarg numeric, rightarg numeric) returns numeric as
$$
declare
width int := 32;
modulo bigint := 2 ^ width;
b1 bigint := 0;
b2 bigint := 0;
r numeric := 0;
i integer := 0;
begin
while leftarg != 0 or rightarg != 0
loop
b1 := leftarg % modulo;
b2 := rightarg % modulo;
r := r + ((b1 # b2)::numeric << (width * i));
leftarg = leftarg >> width;
rightarg = rightarg >> width;
i := i + 1;
end loop;
return r;
end;
$$ language plpgsql;
create operator # (
function = numeric_xor,
leftarg = numeric,
rightarg = numeric
);
-- implementation of bitwise >> operator for the numeric data type
create or replace function numeric_shift_right(leftarg numeric, rightarg numeric) returns numeric as
$$
begin
return floor(leftarg / (2 ^ rightarg));
end;
$$ language plpgsql;
create operator >> (
function = numeric_shift_right,
leftarg = numeric,
rightarg = numeric
);
-- implementation of bitwise << operator for the numeric data type
create or replace function numeric_shift_left(leftarg numeric, rightarg numeric) returns numeric as
$$
begin
return leftarg * (2 ^ rightarg);
end;
$$ language plpgsql;
create operator << (
function = numeric_shift_left,
leftarg = numeric,
rightarg = numeric
);
-- modular exponentiation using the left-to-right binary method
-- credit: https://en.wikipedia.org/wiki/Modular_exponentiation#Left-to-right_binary_method
create or replace function pow_mod(base numeric, exponent numeric,
modulus numeric) returns numeric
as
$$
declare
result numeric := 1;
begin
if modulus = 1 then
return 0;
end if;
base := base % modulus;
while exponent > 0
loop
if exponent % 2 = 1 then
result := (result * base) % modulus;
end if;
exponent := exponent >> 1;
base := (base * base) % modulus;
end loop;
return result;
end;
$$ language plpgsql;
-- port of Curve25519 functions
-- credit: https://gist.github.com/nickovs/cc3c22d15f239a2640c185035c06f8a3
-- point addition
create or replace function _point_add(point_n tuple2, point_m tuple2, point_diff tuple2) returns tuple2
language plpgsql as
$$
declare
P numeric := 2 ^ 255::numeric - 19;
xn numeric := point_n.one;
zn numeric := point_n.two;
xm numeric := point_m.one;
zm numeric := point_m.two;
x_diff numeric := point_diff.one;
z_diff numeric := point_diff.two;
x numeric := (z_diff * 4) * (xm * xn - zm * zn) ^ 2;
y numeric := (x_diff * 4) * (xm * zn - zm * xn) ^ 2;
begin
return ((x % P), (y % P));
end;
$$;
-- point swap
create or replace function _const_time_swap(a tuple2, b tuple2, swap int) returns tuple2[] as
$$
declare
temp tuple2[] := array [a, b, b, a];
index int := swap * 2;
begin
return array [temp[index + 1], temp[index + 2]];
end;
$$ language plpgsql;
-- point double
create or replace function _point_double(point_n tuple2) returns tuple2 as
$$
declare
P numeric := 2 ^ 255::numeric - 19;
_A numeric := 486662;
xn numeric := point_n.one;
zn numeric := point_n.two;
xn2 numeric = xn ^ 2;
zn2 numeric = zn ^ 2;
x numeric = (xn2 - zn2) ^ 2;
xzn numeric = xn * zn;
z numeric = 4 * xzn * (xn2 + _A * xzn + zn2);
begin
return ((x % P), (z % P));
end
$$ language plpgsql;
-- calculate Curve25519
create or replace function _raw_curve25519(base numeric, n numeric) returns numeric as
$$
declare
P numeric := 2 ^ 255::numeric - 19;
zero tuple2 := (1, 0);
one tuple2 := (base, 1);
mP tuple2 := zero;
m1P tuple2 := one;
bn int;
result tuple2[];
x numeric;
z numeric;
inv_z numeric;
begin
for i in reverse 255..0
loop
bn := (n >> i) & 1;
result := _const_time_swap(mP, m1P, bn);
mP := result[1];
m1P := result[2];
m1P := _point_add(mP, m1P, one);
mP := _point_double(mP);
result := _const_time_swap(mP, M1P, bn);
mP := result[1];
m1P := result[2];
end loop;
x = mP.one;
z = mP.two;
inv_z := pow_mod(z, P - 2, P);
return ((x * inv_z) % P);
end;
$$ language plpgsql;
-- similar to bash utility wg genkey
create or replace function wg_gen_key() returns text as
$$
declare
key text;
begin
select encode(set_byte(set_byte(r.b, 0, get_byte(r.b, 0) & 248), 31, (get_byte(r.b, 31) & 127) | 64), 'base64')
into key
from (select gen_random_bytes(32) as b) r;
return key;
end;
$$ language plpgsql;
-- similar to echo key | wg pubkey
create or replace function wg_pub_key(private_key text) returns text as
$$
begin
return encode(_raw_curve25519(9, decode(private_key, 'base64')::numeric)::bytea, 'base64');
end;
$$ language plpgsql;
do
$$
begin
assert 129401205912050919051920510251920501925::numeric &
12591205901209501209490409120949109249012040124::numeric = 375224963989548894071315746773172388;
assert 129401205912050919051920510251920501925::numeric #
12591205901209501209490409120949109249012040124::numeric =
12591206029860257193562230384726988007386197273;
assert 129401205912050919051920510251920501925::numeric |
12591205901209501209490409120949109249012040124::numeric =
12591206030235482157551779278798303754159369661;
assert 129401205912050919051920510251920501925::numeric >> 27 = 964114113986871533482674585669;
assert 129401205912050919051920510251920501925::numeric << 27 = 17367935857975642175460684922613477404993126400;
assert wg_pub_key('KEJOq1AEEStt7JYyYBIjYQzZSqUq4vGV3g+/eSjbv2c=') =
'R/No1YXsCbxEaWAq7cUsTjyoeZ69pYXzYhgefhnWUjo=';
end;
$$;
select 129401205912050919051920510251920501925::numeric & 12591205901209501209490409120949109249012040124::numeric,
129401205912050919051920510251920501925::numeric # 12591205901209501209490409120949109249012040124::numeric,
129401205912050919051920510251920501925::numeric | 12591205901209501209490409120949109249012040124::numeric,
129401205912050919051920510251920501925::numeric >> 27,
129401205912050919051920510251920501925::numeric << 27,
k,
wg_pub_key(k)
from (select wg_gen_key() k) w;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment