roken: Add URL-safe base64
This commit is contained in:
@@ -167,8 +167,132 @@ rk_base64_decode(const char *str, void *data)
|
||||
return q - (unsigned char *) data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Base64URL encoding/decoding (RFC 4648 Section 5)
|
||||
*
|
||||
* Uses '-' and '_' instead of '+' and '/', and omits padding.
|
||||
* This is the encoding used by JWT/JWS (RFC 7515).
|
||||
*/
|
||||
|
||||
#define base64url_chars "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
rk_base64url_encode(const void *data, int size, char **str)
|
||||
{
|
||||
char *s, *p;
|
||||
int i;
|
||||
int c;
|
||||
const unsigned char *q;
|
||||
|
||||
if (size > INT_MAX/4 || size < 0) {
|
||||
*str = NULL;
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = s = (char *) malloc(size * 4 / 3 + 4);
|
||||
if (p == NULL) {
|
||||
*str = NULL;
|
||||
return -1;
|
||||
}
|
||||
q = (const unsigned char *) data;
|
||||
|
||||
for (i = 0; i < size;) {
|
||||
c = q[i++];
|
||||
c *= 256;
|
||||
if (i < size)
|
||||
c += q[i];
|
||||
i++;
|
||||
c *= 256;
|
||||
if (i < size)
|
||||
c += q[i];
|
||||
i++;
|
||||
p[0] = base64url_chars[(c & 0x00fc0000) >> 18];
|
||||
p[1] = base64url_chars[(c & 0x0003f000) >> 12];
|
||||
/* Omit padding characters */
|
||||
if (i > size + 1) {
|
||||
p += 2;
|
||||
break;
|
||||
}
|
||||
p[2] = base64url_chars[(c & 0x00000fc0) >> 6];
|
||||
if (i > size) {
|
||||
p += 3;
|
||||
break;
|
||||
}
|
||||
p[3] = base64url_chars[(c & 0x0000003f) >> 0];
|
||||
p += 4;
|
||||
}
|
||||
*p = 0;
|
||||
*str = s;
|
||||
return (int) strlen(s);
|
||||
}
|
||||
|
||||
static int
|
||||
pos_url(char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return c - 'A';
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return ('Z' + 1 - 'A') + c - 'a';
|
||||
if (c >= '0' && c <= '9')
|
||||
return ('Z' + 1 - 'A') + ('z' + 1 - 'a') + c - '0';
|
||||
if (c == '-')
|
||||
return 62;
|
||||
if (c == '_')
|
||||
return 63;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
rk_base64url_decode(const char *str, void *data)
|
||||
{
|
||||
const char *p;
|
||||
unsigned char *q;
|
||||
int i;
|
||||
unsigned int val;
|
||||
int marker;
|
||||
|
||||
q = data;
|
||||
|
||||
for (p = str; *p && pos_url(*p) != -1; ) {
|
||||
val = 0;
|
||||
marker = 0;
|
||||
|
||||
/* Decode up to 4 characters, handling missing padding */
|
||||
for (i = 0; i < 4; i++) {
|
||||
val *= 64;
|
||||
if (*p && pos_url(*p) != -1) {
|
||||
val += pos_url(*p);
|
||||
p++;
|
||||
} else {
|
||||
/* Implicit padding */
|
||||
marker++;
|
||||
}
|
||||
}
|
||||
|
||||
*q++ = (val >> 16) & 0xff;
|
||||
if (marker < 2)
|
||||
*q++ = (val >> 8) & 0xff;
|
||||
if (marker < 1)
|
||||
*q++ = val & 0xff;
|
||||
}
|
||||
|
||||
/* Check for invalid trailing characters */
|
||||
if (*p != '\0') {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (q - (unsigned char *) data > INT_MAX) {
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
return q - (unsigned char *) data;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
static int decode_flag;
|
||||
static int url_safe_flag;
|
||||
static int help_flag;
|
||||
|
||||
/*
|
||||
@@ -177,6 +301,7 @@ static int help_flag;
|
||||
*/
|
||||
static struct getargs args[] = {
|
||||
{ "decode", 'd', arg_flag, &decode_flag, "Decode", NULL },
|
||||
{ "url-safe", 'u', arg_flag, &url_safe_flag, "Use URL-safe alphabet", NULL },
|
||||
{ "help", 'h', arg_flag, &help_flag, "Print help message", NULL },
|
||||
};
|
||||
static size_t num_args = sizeof(args)/sizeof(args[0]);
|
||||
@@ -248,16 +373,24 @@ main(int argc, char **argv)
|
||||
if ((d = malloc(buflen * 3 / 4 + 4)) == NULL)
|
||||
err(1, "Could not decode data");
|
||||
|
||||
if ((ret = rk_base64_decode((const char *)buf, d)) < 0)
|
||||
if (url_safe_flag) {
|
||||
if ((ret = rk_base64url_decode((const char *)buf, d)) < 0)
|
||||
err(1, "Could not decode data");
|
||||
} else if ((ret = rk_base64_decode((const char *)buf, d)) < 0) {
|
||||
err(1, "Could not decode data");
|
||||
}
|
||||
if (fwrite(d, ret, 1, stdout) != 1)
|
||||
err(1, "Could not write decoded data");
|
||||
free(d);
|
||||
} else if (buf) { /* buf can be NULL if we read from an empty file */
|
||||
char *e;
|
||||
|
||||
if ((ret = rk_base64_encode(buf, buflen, &e)) < 0)
|
||||
if (url_safe_flag) {
|
||||
if ((ret = rk_base64url_encode(buf, buflen, &e)) < 0)
|
||||
err(1, "Could not encode data");
|
||||
} else if ((ret = rk_base64_encode(buf, buflen, &e)) < 0) {
|
||||
err(1, "Could not encode data");
|
||||
}
|
||||
if (fwrite(e, ret, 1, stdout) != 1)
|
||||
err(1, "Could not write decoded data");
|
||||
free(e);
|
||||
|
||||
@@ -52,4 +52,15 @@ rk_base64_encode(const void *, int, char **);
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
rk_base64_decode(const char *, void *);
|
||||
|
||||
/*
|
||||
* Base64URL encoding/decoding (RFC 4648 Section 5)
|
||||
* Uses '-' and '_' instead of '+' and '/', omits padding.
|
||||
* Used by JWT/JWS (RFC 7515).
|
||||
*/
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
rk_base64url_encode(const void *, int, char **);
|
||||
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
rk_base64url_decode(const char *, void *);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -38,6 +38,8 @@ HEIMDAL_ROKEN_2.0 {
|
||||
rk_base32_encode;
|
||||
rk_base64_decode;
|
||||
rk_base64_encode;
|
||||
rk_base64url_decode;
|
||||
rk_base64url_encode;
|
||||
rk_bswap16;
|
||||
rk_bswap32;
|
||||
rk_bswap64;
|
||||
|
||||
Reference in New Issue
Block a user