1
0
mirror of https://github.com/postgres/postgres.git synced 2025-12-19 17:02:53 +03:00

Add support for base64url encoding and decoding

This adds support for base64url encoding and decoding, a base64
variant which is safe to use in filenames and URLs.  base64url
replaces '+' in the base64 alphabet with '-' and '/' with '_',
thus making it safe for URL addresses and file systems.

Support for base64url was originally suggested by Przemysław Sztoch.

Author: Florents Tselai <florents.tselai@gmail.com>
Reviewed-by: Aleksander Alekseev <aleksander@timescale.com>
Reviewed-by: David E. Wheeler <david@justatheory.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Reviewed-by: Chao Li (Evan) <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/70f2b6a8-486a-4fdb-a951-84cef35e22ab@sztoch.pl
This commit is contained in:
Daniel Gustafsson
2025-09-20 23:19:32 +02:00
parent 261f89a976
commit e1d917182c
4 changed files with 359 additions and 21 deletions

View File

@@ -2517,6 +2517,156 @@ SELECT decode(encode('\x1234567890abcdef00', 'escape'), 'escape');
\x1234567890abcdef00
(1 row)
--
-- base64url encoding/decoding
--
SET bytea_output TO hex;
-- Simple encoding/decoding
SELECT encode('\x69b73eff', 'base64url'); -- abc-_w
encode
--------
abc-_w
(1 row)
SELECT decode('abc-_w', 'base64url'); -- \x69b73eff
decode
------------
\x69b73eff
(1 row)
-- Round-trip: decode(encode(x)) = x
SELECT decode(encode('\x1234567890abcdef00', 'base64url'), 'base64url'); -- \x1234567890abcdef00
decode
----------------------
\x1234567890abcdef00
(1 row)
-- Empty input
SELECT encode('', 'base64url'); -- ''
encode
--------
(1 row)
SELECT decode('', 'base64url'); -- ''
decode
--------
\x
(1 row)
-- 1 byte input
SELECT encode('\x01', 'base64url'); -- AQ
encode
--------
AQ
(1 row)
SELECT decode('AQ', 'base64url'); -- \x01
decode
--------
\x01
(1 row)
-- 2 byte input
SELECT encode('\x0102'::bytea, 'base64url'); -- AQI
encode
--------
AQI
(1 row)
SELECT decode('AQI', 'base64url'); -- \x0102
decode
--------
\x0102
(1 row)
-- 3 byte input (no padding needed)
SELECT encode('\x010203'::bytea, 'base64url'); -- AQID
encode
--------
AQID
(1 row)
SELECT decode('AQID', 'base64url'); -- \x010203
decode
----------
\x010203
(1 row)
-- 4 byte input (results in 6 base64 chars)
SELECT encode('\xdeadbeef'::bytea, 'base64url'); -- 3q2-7w
encode
--------
3q2-7w
(1 row)
SELECT decode('3q2-7w', 'base64url'); -- \xdeadbeef
decode
------------
\xdeadbeef
(1 row)
-- Round-trip test for all lengths from 04
SELECT encode(decode(encode(E'\\x', 'base64url'), 'base64url'), 'base64url');
encode
--------
(1 row)
SELECT encode(decode(encode(E'\\x00', 'base64url'), 'base64url'), 'base64url');
encode
--------
AA
(1 row)
SELECT encode(decode(encode(E'\\x0001', 'base64url'), 'base64url'), 'base64url');
encode
--------
AAE
(1 row)
SELECT encode(decode(encode(E'\\x000102', 'base64url'), 'base64url'), 'base64url');
encode
--------
AAEC
(1 row)
SELECT encode(decode(encode(E'\\x00010203', 'base64url'), 'base64url'), 'base64url');
encode
--------
AAECAw
(1 row)
-- Invalid inputs (should ERROR)
-- invalid character '@'
SELECT decode('QQ@=', 'base64url');
ERROR: invalid symbol "@" found while decoding base64url sequence
-- missing characters (incomplete group)
SELECT decode('QQ', 'base64url'); -- ok (1 byte)
decode
--------
\x41
(1 row)
SELECT decode('QQI', 'base64url'); -- ok (2 bytes)
decode
--------
\x4102
(1 row)
SELECT decode('QQIDQ', 'base64url'); -- ERROR: invalid base64url end sequence
ERROR: invalid base64url end sequence
HINT: Input data is missing padding, is truncated, or is otherwise corrupted.
-- unexpected '=' at start
SELECT decode('=QQQ', 'base64url');
ERROR: unexpected "=" while decoding base64url sequence
-- valid base64 padding in base64url (optional, but accepted)
SELECT decode('abc-_w==', 'base64url'); -- should decode to \x69b73eff
decode
------------
\x69b73eff
(1 row)
--
-- get_bit/set_bit etc
--

View File

@@ -799,6 +799,60 @@ SELECT decode(encode(('\x' || repeat('1234567890abcdef0001', 7))::bytea,
SELECT encode('\x1234567890abcdef00', 'escape');
SELECT decode(encode('\x1234567890abcdef00', 'escape'), 'escape');
--
-- base64url encoding/decoding
--
SET bytea_output TO hex;
-- Simple encoding/decoding
SELECT encode('\x69b73eff', 'base64url'); -- abc-_w
SELECT decode('abc-_w', 'base64url'); -- \x69b73eff
-- Round-trip: decode(encode(x)) = x
SELECT decode(encode('\x1234567890abcdef00', 'base64url'), 'base64url'); -- \x1234567890abcdef00
-- Empty input
SELECT encode('', 'base64url'); -- ''
SELECT decode('', 'base64url'); -- ''
-- 1 byte input
SELECT encode('\x01', 'base64url'); -- AQ
SELECT decode('AQ', 'base64url'); -- \x01
-- 2 byte input
SELECT encode('\x0102'::bytea, 'base64url'); -- AQI
SELECT decode('AQI', 'base64url'); -- \x0102
-- 3 byte input (no padding needed)
SELECT encode('\x010203'::bytea, 'base64url'); -- AQID
SELECT decode('AQID', 'base64url'); -- \x010203
-- 4 byte input (results in 6 base64 chars)
SELECT encode('\xdeadbeef'::bytea, 'base64url'); -- 3q2-7w
SELECT decode('3q2-7w', 'base64url'); -- \xdeadbeef
-- Round-trip test for all lengths from 04
SELECT encode(decode(encode(E'\\x', 'base64url'), 'base64url'), 'base64url');
SELECT encode(decode(encode(E'\\x00', 'base64url'), 'base64url'), 'base64url');
SELECT encode(decode(encode(E'\\x0001', 'base64url'), 'base64url'), 'base64url');
SELECT encode(decode(encode(E'\\x000102', 'base64url'), 'base64url'), 'base64url');
SELECT encode(decode(encode(E'\\x00010203', 'base64url'), 'base64url'), 'base64url');
-- Invalid inputs (should ERROR)
-- invalid character '@'
SELECT decode('QQ@=', 'base64url');
-- missing characters (incomplete group)
SELECT decode('QQ', 'base64url'); -- ok (1 byte)
SELECT decode('QQI', 'base64url'); -- ok (2 bytes)
SELECT decode('QQIDQ', 'base64url'); -- ERROR: invalid base64url end sequence
-- unexpected '=' at start
SELECT decode('=QQQ', 'base64url');
-- valid base64 padding in base64url (optional, but accepted)
SELECT decode('abc-_w==', 'base64url'); -- should decode to \x69b73eff
--
-- get_bit/set_bit etc
--