MINOR: sample: add ub64dec and ub64enc converters

ub64dec and ub64enc are the base64url equivalent of b64dec and base64
converters. base64url encoding is the "URL and Filename Safe Alphabet"
variant of base64 encoding. It is also used in in JWT (JSON Web Token)
standard.
RFC1421 mention in base64.c file is deprecated so it was replaced with
RFC4648 to which existing converters, base64/b64dec, still apply.

Example:
  HAProxy:
    http-request return content-type text/plain lf-string %[req.hdr(Authorization),word(2,.),ub64dec]
  Client:
    Token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZm9vIiwia2V5IjoiY2hhZTZBaFhhaTZlIn0.5VsVj7mdxVvo1wP5c0dVHnr-S_khnIdFkThqvwukmdg
    $ curl -H "Authorization: Bearer ${TOKEN}" http://haproxy.local
    {"user":"foo","key":"chae6AhXai6e"}
This commit is contained in:
Moemen MHEDHBI 2021-04-01 20:53:59 +02:00 committed by Willy Tarreau
parent b28430591d
commit 92f7d43c5d
5 changed files with 218 additions and 1 deletions

View File

@ -15660,11 +15660,14 @@ and(<value>)
b64dec
Converts (decodes) a base64 encoded input string to its binary
representation. It performs the inverse operation of base64().
For base64url("URL and Filename Safe Alphabet" (RFC 4648)) variant
see "ub64dec".
base64
Converts a binary input sample to a base64 string. It is used to log or
transfer binary content in a way that can be reliably transferred (e.g.
an SSL ID can be copied in a header).
an SSL ID can be copied in a header). For base64url("URL and Filename
Safe Alphabet" (RFC 4648)) variant see "ub64enc".
bool
Returns a boolean TRUE if the input value of type signed integer is
@ -16565,6 +16568,18 @@ table_trackers(<table>)
connections there are from a given address for example. See also the
sc_trackers sample fetch keyword.
ub64dec
This converter is the base64url variant of b64dec converter. base64url
encoding is the "URL and Filename Safe Alphabet" variant of base64 encoding.
It is also the encoding used in JWT (JSON Web Token) standard.
Example:
# Decoding a JWT payload:
http-request set-var(txn.token_payload) req.hdr(Authorization),word(2,.),ub64dec
ub64enc
This converter is the base64url variant of base64 converter.
upper
Convert a string sample to upper case. This can only be placed after a string
sample fetch function or after a transformation keyword returning a string

View File

@ -17,7 +17,9 @@
#include <haproxy/api.h>
int a2base64(char *in, int ilen, char *out, int olen);
int a2base64url(const char *in, size_t ilen, char *out, size_t olen);
int base64dec(const char *in, size_t ilen, char *out, size_t olen);
int base64urldec(const char *in, size_t ilen, char *out, size_t olen);
const char *s30tob64(int in, char *out);
int b64tos30(const char *in);

View File

@ -0,0 +1,51 @@
varnishtest "ub64dec sample fetche Test"
#REQUIRE_VERSION=2.4
feature ignore_unknown_macro
haproxy h1 -conf {
defaults
mode http
timeout connect 1s
timeout client 1s
timeout server 1s
frontend fe
bind "fd@${fe}"
acl input hdr(encode) -m found
http-request return content-type text/plain hdr encode %[hdr(encode),ub64enc] hdr decode %[hdr(decode),ub64dec] if input
http-request return content-type text/plain hdr encode %[bin(14fb9c03d97f12d97e),ub64enc] hdr decode %[str(FPucA9l_Etl-),ub64dec,hex,lower] if !input
} -start
client c1 -connect ${h1_fe_sock} {
txreq -hdr "encode: f" -hdr "decode: Zg"
rxresp
expect resp.http.encode == "Zg"
expect resp.http.decode == "f"
txreq -hdr "encode: fo" -hdr "decode: Zm8"
rxresp
expect resp.http.encode == "Zm8"
expect resp.http.decode == "fo"
txreq -hdr "encode: foo" -hdr "decode: Zm9v"
rxresp
expect resp.http.encode == "Zm9v"
expect resp.http.decode == "foo"
txreq -hdr "encode: foob" -hdr "decode: Zm9vYg"
rxresp
expect resp.http.encode == "Zm9vYg"
expect resp.http.decode == "foob"
txreq -hdr "encode: fooba" -hdr "decode: Zm9vYmE"
rxresp
expect resp.http.encode == "Zm9vYmE"
expect resp.http.decode == "fooba"
txreq -hdr "encode: foobar" -hdr "decode: Zm9vYmFy"
rxresp
expect resp.http.encode == "Zm9vYmFy"
expect resp.http.decode == "foobar"
txreq
rxresp
expect resp.http.encode == "FPucA9l_Etl-"
expect resp.http.decode == "14fb9c03d97f12d97e"
} -run

View File

@ -19,11 +19,14 @@
#define B64BASE '#' /* arbitrary chosen base value */
#define B64CMIN '+'
#define UB64CMIN '-'
#define B64CMAX 'z'
#define B64PADV 64 /* Base64 chosen special pad value */
const char base64tab[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const char base64rev[]="b###cXYZ[\\]^_`a###d###$%&'()*+,-./0123456789:;<=######>?@ABCDEFGHIJKLMNOPQRSTUVW";
const char ubase64tab[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
const char ubase64rev[]="b##XYZ[\\]^_`a###c###$%&'()*+,-./0123456789:;<=####c#>?@ABCDEFGHIJKLMNOPQRSTUVW";
/* Encodes <ilen> bytes from <in> to <out> for at most <olen> chars (including
* the trailing zero). Returns the number of bytes written. No check is made
@ -68,6 +71,48 @@ int a2base64(char *in, int ilen, char *out, int olen)
return convlen;
}
/* url variant of a2base64 */
int a2base64url(const char *in, size_t ilen, char *out, size_t olen)
{
int convlen;
convlen = ((ilen + 2) / 3) * 4;
if (convlen >= olen)
return -1;
/* we don't need to check olen anymore */
while (ilen >= 3) {
out[0] = ubase64tab[(((unsigned char)in[0]) >> 2)];
out[1] = ubase64tab[(((unsigned char)in[0] & 0x03) << 4) | (((unsigned char)in[1]) >> 4)];
out[2] = ubase64tab[(((unsigned char)in[1] & 0x0F) << 2) | (((unsigned char)in[2]) >> 6)];
out[3] = ubase64tab[(((unsigned char)in[2] & 0x3F))];
out += 4;
in += 3;
ilen -= 3;
}
if (!ilen) {
out[0] = '\0';
return convlen;
}
out[0] = ubase64tab[((unsigned char)in[0]) >> 2];
if (ilen == 1) {
out[1] = ubase64tab[((unsigned char)in[0] & 0x03) << 4];
out[2] = '\0';
convlen -= 2;
} else {
out[1] = ubase64tab[(((unsigned char)in[0] & 0x03) << 4) |
(((unsigned char)in[1]) >> 4)];
out[2] = ubase64tab[((unsigned char)in[1] & 0x0F) << 2];
out[3] = '\0';
convlen -= 1;
}
return convlen;
}
/* Decodes <ilen> bytes from <in> to <out> for at most <olen> chars.
* Returns the number of bytes converted. No check is made for
* <in> or <out> to be NULL. Returns -1 if <in> is invalid or ilen
@ -138,6 +183,72 @@ int base64dec(const char *in, size_t ilen, char *out, size_t olen) {
return convlen;
}
/* url variant of base64dec */
/* The reverse tab used to decode base64 is generated via /dev/base64/base64rev-gen.c */
int base64urldec(const char *in, size_t ilen, char *out, size_t olen)
{
unsigned char t[4];
signed char b;
int convlen = 0, i = 0, pad = 0, padlen = 0;
if (olen < ((ilen / 4 * 3)))
return -2;
switch (ilen % 4) {
case 0:
break;
case 2:
padlen = pad = 2;
break;
case 3:
padlen = pad = 1;
break;
default:
return -1;
}
while (ilen + pad) {
if (ilen) {
/* if (*p < UB64CMIN || *p > B64CMAX) */
b = (signed char) * in - UB64CMIN;
if ((unsigned char)b > (B64CMAX - UB64CMIN))
return -1;
b = ubase64rev[b] - B64BASE - 1;
/* b == -1: invalid character */
if (b < 0)
return -1;
in++;
ilen--;
} else {
b = B64PADV;
pad--;
}
t[i++] = b;
if (i == 4) {
/*
* WARNING: we allow to write little more data than we
* should, but the checks from the beginning of the
* functions guarantee that we can safely do that.
*/
/* xx000000 xx001111 xx111122 xx222222 */
out[convlen] = ((t[0] << 2) + (t[1] >> 4));
out[convlen + 1] = ((t[1] << 4) + (t[2] >> 2));
out[convlen + 2] = ((t[2] << 6) + (t[3] >> 0));
convlen += 3;
i = 0;
}
}
convlen -= padlen;
return convlen;
}
/* Converts the lower 30 bits of an integer to a 5-char base64 string. The
* caller is responsible for ensuring that the output buffer can accept 6 bytes

View File

@ -1567,6 +1567,24 @@ static int sample_conv_base642bin(const struct arg *arg_p, struct sample *smp, v
return 1;
}
static int sample_conv_base64url2bin(const struct arg *arg_p, struct sample *smp, void *private)
{
struct buffer *trash = get_trash_chunk();
int bin_len;
trash->data = 0;
bin_len = base64urldec(smp->data.u.str.area, smp->data.u.str.data,
trash->area, trash->size);
if (bin_len < 0)
return 0;
trash->data = bin_len;
smp->data.u.str = *trash;
smp->data.type = SMP_T_BIN;
smp->flags &= ~SMP_F_CONST;
return 1;
}
static int sample_conv_bin2base64(const struct arg *arg_p, struct sample *smp, void *private)
{
struct buffer *trash = get_trash_chunk();
@ -1585,6 +1603,24 @@ static int sample_conv_bin2base64(const struct arg *arg_p, struct sample *smp, v
return 1;
}
static int sample_conv_bin2base64url(const struct arg *arg_p, struct sample *smp, void *private)
{
struct buffer *trash = get_trash_chunk();
int b64_len;
trash->data = 0;
b64_len = a2base64url(smp->data.u.str.area, smp->data.u.str.data,
trash->area, trash->size);
if (b64_len < 0)
return 0;
trash->data = b64_len;
smp->data.u.str = *trash;
smp->data.type = SMP_T_STR;
smp->flags &= ~SMP_F_CONST;
return 1;
}
static int sample_conv_sha1(const struct arg *arg_p, struct sample *smp, void *private)
{
blk_SHA_CTX ctx;
@ -4096,6 +4132,8 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, {
{ "debug", sample_conv_debug, ARG2(0,STR,STR), smp_check_debug, SMP_T_ANY, SMP_T_ANY },
{ "b64dec", sample_conv_base642bin,0, NULL, SMP_T_STR, SMP_T_BIN },
{ "base64", sample_conv_bin2base64,0, NULL, SMP_T_BIN, SMP_T_STR },
{ "ub64dec", sample_conv_base64url2bin,0, NULL, SMP_T_STR, SMP_T_BIN },
{ "ub64enc", sample_conv_bin2base64url,0, NULL, SMP_T_BIN, SMP_T_STR },
{ "upper", sample_conv_str2upper, 0, NULL, SMP_T_STR, SMP_T_STR },
{ "lower", sample_conv_str2lower, 0, NULL, SMP_T_STR, SMP_T_STR },
{ "length", sample_conv_length, 0, NULL, SMP_T_STR, SMP_T_SINT },