Commit 7541b068 authored by Pierre Téchoueyres's avatar Pierre Téchoueyres Committed by Eli Zaretskii

Add support for base64url variant of base-64 encoding/decoding

Implement the RFC4648 variant of base64 encoding used by URLs.
* doc/lispref/text.texi (base64url-encode-region,
base64url-encode-string): Document new functions.
(base64-decode-region, base64-decode-string): Document new optional
parameter 'base64url' used to use url variant when decoding data.

* src/fns.c (base64url-encode-region, base64url-encode-region): New
functions to manage url variant.
(base64-decode-region, base64-decode-string): Add optional
parameter to indicate use of url-variant.
(base64_encode_region_1, base64_encode_string_1): Internal functions
with extracted code from 'base64_encode_region' and
'base64_encode_string' and optional parameters to manage padding and
url variant.
(base64-encode-region, base64-encode-string) : Use
base64_encode_region_1 and base64_encode_string_1.
(base64-encode-1): Add parameters to manage padding and url variant.
(base64-decode-1): Add parameter to manage url variant.

* test/src/fns-tests.el (fns-tests--with-region): New helper macro to
test region variant of base64 encode / decode functions.
(fns-tests--string-repeat): Helper function used in base64 tests.
(fns-tests-base64-encode-region, fns-tests-base64-encode-string):
Tests for standard base64 function.
(fns-test-base64url-encode-region,
fns-test-base64url-encode-string): Tests for url variant.
(fns-tests-base64-decode-string): Tests for decoding part.
parent faf10bd8
...@@ -4541,7 +4541,7 @@ Internet informational document describing a standard. RFCs are ...@@ -4541,7 +4541,7 @@ Internet informational document describing a standard. RFCs are
usually written by technical experts acting on their own initiative, usually written by technical experts acting on their own initiative,
and are traditionally written in a pragmatic, experience-driven and are traditionally written in a pragmatic, experience-driven
manner. manner.
}2045. This section describes the functions for }2045 and also in RFC4648. This section describes the functions for
converting to and from this code. converting to and from this code.
@deffn Command base64-encode-region beg end &optional no-line-break @deffn Command base64-encode-region beg end &optional no-line-break
...@@ -4558,6 +4558,22 @@ text, to avoid overlong lines. However, if the optional argument ...@@ -4558,6 +4558,22 @@ text, to avoid overlong lines. However, if the optional argument
the output is just one long line. the output is just one long line.
@end deffn @end deffn
@deffn Command base64url-encode-region beg end &optional no-pad
This function converts the region from @var{beg} to @var{end} into base
64 code. It returns the length of the encoded text. An error is
signaled if a character in the region is multibyte, i.e., in a
multibyte buffer the region must contain only characters from the
charsets @code{ascii}, @code{eight-bit-control} and
@code{eight-bit-graphic}.
Contrary to the function @code{base64-encode-region}, this function
doesnt inserts newline characters into the encoded text, so the output
is just one long line.
If the optional argument @var{no-pad} is non-@code{nil} then padding
(@code{=}) isn't generated.
@end deffn
@defun base64-encode-string string &optional no-line-break @defun base64-encode-string string &optional no-line-break
This function converts the string @var{string} into base 64 code. It This function converts the string @var{string} into base 64 code. It
returns a string containing the encoded text. As for returns a string containing the encoded text. As for
...@@ -4570,20 +4586,40 @@ text, to avoid overlong lines. However, if the optional argument ...@@ -4570,20 +4586,40 @@ text, to avoid overlong lines. However, if the optional argument
the result string is just one long line. the result string is just one long line.
@end defun @end defun
@deffn Command base64-decode-region beg end @defun base64url-encode-string string &optional no-pad
This function converts the string @var{string} into base 64 url code
(see RFC4648). It returns a string containing the encoded text. As
for @code{base64url-encode-region}, an error is signaled if a
character in the string is multibyte.
Contrary to @code{base64-encode-string}, this function doesnt inserts
newline characters into the encoded text, so the result string is just
one long line.
If the optional argument @var{no-pad} is non-@code{nil} then padding
(@code{=}) isn't generated.
@end defun
@deffn Command base64-decode-region beg end &optional base64url
This function converts the region from @var{beg} to @var{end} from base This function converts the region from @var{beg} to @var{end} from base
64 code into the corresponding decoded text. It returns the length of 64 code into the corresponding decoded text. It returns the length of
the decoded text. the decoded text.
The decoding functions ignore newline characters in the encoded text. The decoding functions ignore newline characters in the encoded text.
If optional argument @var{base64url} is is non-@code{nil} then padding
become optionnal and url variant is used (see RFC4648).
@end deffn @end deffn
@defun base64-decode-string string @defun base64-decode-string string &optional base64url
This function converts the string @var{string} from base 64 code into This function converts the string @var{string} from base 64 code into
the corresponding decoded text. It returns a unibyte string containing the the corresponding decoded text. It returns a unibyte string containing the
decoded text. decoded text.
The decoding functions ignore newline characters in the encoded text. The decoding functions ignore newline characters in the encoded text.
If optional argument @var{base64url} is is non-@code{nil} then padding
become optionnal and url variant is used (see RFC4648).
@end defun @end defun
@node Checksum/Hash @node Checksum/Hash
......
...@@ -3189,7 +3189,7 @@ The data read from the system are decoded using `locale-coding-system'. */) ...@@ -3189,7 +3189,7 @@ The data read from the system are decoded using `locale-coding-system'. */)
#define IS_ASCII(Character) \ #define IS_ASCII(Character) \
((Character) < 128) ((Character) < 128)
#define IS_BASE64(Character) \ #define IS_BASE64(Character) \
(IS_ASCII (Character) && base64_char_to_value[Character] >= 0) (IS_ASCII (Character) && b64_char_to_value[Character] >= 0)
#define IS_BASE64_IGNORABLE(Character) \ #define IS_BASE64_IGNORABLE(Character) \
((Character) == ' ' || (Character) == '\t' || (Character) == '\n' \ ((Character) == ' ' || (Character) == '\t' || (Character) == '\n' \
|| (Character) == '\f' || (Character) == '\r') || (Character) == '\f' || (Character) == '\r')
...@@ -3222,6 +3222,17 @@ static const char base64_value_to_char[64] = ...@@ -3222,6 +3222,17 @@ static const char base64_value_to_char[64] =
'8', '9', '+', '/' /* 60-63 */ '8', '9', '+', '/' /* 60-63 */
}; };
static const char base64url_value_to_char[64] =
{
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', /* 0- 9 */
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', /* 10-19 */
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', /* 20-29 */
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', /* 30-39 */
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', /* 40-49 */
'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', /* 50-59 */
'8', '9', '-', '_' /* 60-63 */
};
/* Table of base64 values for first 128 characters. */ /* Table of base64 values for first 128 characters. */
static const short base64_char_to_value[128] = static const short base64_char_to_value[128] =
{ {
...@@ -3240,6 +3251,23 @@ static const short base64_char_to_value[128] = ...@@ -3240,6 +3251,23 @@ static const short base64_char_to_value[128] =
49, 50, 51, -1, -1, -1, -1, -1 /* 120-127 */ 49, 50, 51, -1, -1, -1, -1, -1 /* 120-127 */
}; };
static const short base64url_char_to_value[128] =
{
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0- 9 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10- 19 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 20- 29 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 30- 39 */
-1, -1, -1, -1, -1, 62, -1, -1, 52, 53, /* 40- 49 */
54, 55, 56, 57, 58, 59, 60, 61, -1, -1, /* 50- 59 */
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4, /* 60- 69 */
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 70- 79 */
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, /* 80- 89 */
25, -1, -1, -1, -1, 63, -1, 26, 27, 28, /* 90- 99 */
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, /* 100-109 */
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, /* 110-119 */
49, 50, 51, -1, -1, -1, -1, -1 /* 120-127 */
};
/* The following diagram shows the logical steps by which three octets /* The following diagram shows the logical steps by which three octets
get transformed into four base64 characters. get transformed into four base64 characters.
...@@ -3259,9 +3287,17 @@ static const short base64_char_to_value[128] = ...@@ -3259,9 +3287,17 @@ static const short base64_char_to_value[128] =
base64 characters. */ base64 characters. */
static ptrdiff_t base64_encode_1 (const char *, char *, ptrdiff_t, bool, bool); static ptrdiff_t base64_encode_1 (const char *, char *, ptrdiff_t, bool, bool,
bool, bool);
static ptrdiff_t base64_decode_1 (const char *, char *, ptrdiff_t, bool, static ptrdiff_t base64_decode_1 (const char *, char *, ptrdiff_t, bool,
ptrdiff_t *); bool, ptrdiff_t *);
Lisp_Object base64_encode_region_1 (Lisp_Object, Lisp_Object, bool,
bool, bool);
Lisp_Object base64_encode_string_1(Lisp_Object, bool,
bool, bool);
DEFUN ("base64-encode-region", Fbase64_encode_region, Sbase64_encode_region, DEFUN ("base64-encode-region", Fbase64_encode_region, Sbase64_encode_region,
2, 3, "r", 2, 3, "r",
...@@ -3270,6 +3306,26 @@ Return the length of the encoded text. ...@@ -3270,6 +3306,26 @@ Return the length of the encoded text.
Optional third argument NO-LINE-BREAK means do not break long lines Optional third argument NO-LINE-BREAK means do not break long lines
into shorter lines. */) into shorter lines. */)
(Lisp_Object beg, Lisp_Object end, Lisp_Object no_line_break) (Lisp_Object beg, Lisp_Object end, Lisp_Object no_line_break)
{
return base64_encode_region_1(beg, end, NILP (no_line_break), true, false);
}
DEFUN ("base64url-encode-region", Fbase64url_encode_region, Sbase64url_encode_region,
2, 3, "r",
doc: /* Base64url-encode the region between BEG and END.
Return the length of the encoded text.
Optional second argument NO-PAD means do not add padding char =.
This is the variant defined in RFC4648. */)
(Lisp_Object beg, Lisp_Object end, Lisp_Object no_pad)
{
return base64_encode_region_1(beg, end, false, NILP(no_pad), true);
}
Lisp_Object
base64_encode_region_1 (Lisp_Object beg, Lisp_Object end, bool line_break,
bool pad, bool base64url)
{ {
char *encoded; char *encoded;
ptrdiff_t allength, length; ptrdiff_t allength, length;
...@@ -3292,7 +3348,8 @@ into shorter lines. */) ...@@ -3292,7 +3348,8 @@ into shorter lines. */)
encoded = SAFE_ALLOCA (allength); encoded = SAFE_ALLOCA (allength);
encoded_length = base64_encode_1 ((char *) BYTE_POS_ADDR (ibeg), encoded_length = base64_encode_1 ((char *) BYTE_POS_ADDR (ibeg),
encoded, length, NILP (no_line_break), encoded, length, line_break,
pad, base64url,
!NILP (BVAR (current_buffer, enable_multibyte_characters))); !NILP (BVAR (current_buffer, enable_multibyte_characters)));
if (encoded_length > allength) if (encoded_length > allength)
emacs_abort (); emacs_abort ();
...@@ -3330,6 +3387,26 @@ Optional second argument NO-LINE-BREAK means do not break long lines ...@@ -3330,6 +3387,26 @@ Optional second argument NO-LINE-BREAK means do not break long lines
into shorter lines. */) into shorter lines. */)
(Lisp_Object string, Lisp_Object no_line_break) (Lisp_Object string, Lisp_Object no_line_break)
{ {
return base64_encode_string_1(string, NILP (no_line_break), true, false);
}
DEFUN ("base64url-encode-string", Fbase64url_encode_string, Sbase64url_encode_string,
1, 2, 0,
doc: /* Base64url-encode STRING and return the result.
Optional second argument NO-PAD means do not add padding char =.
This is the variant defined in RFC4648. */)
(Lisp_Object string, Lisp_Object no_pad)
{
return base64_encode_string_1(string, false, NILP(no_pad), true);
}
Lisp_Object
base64_encode_string_1(Lisp_Object string, bool line_break,
bool pad, bool base64url)
{
ptrdiff_t allength, length, encoded_length; ptrdiff_t allength, length, encoded_length;
char *encoded; char *encoded;
Lisp_Object encoded_string; Lisp_Object encoded_string;
...@@ -3348,7 +3425,8 @@ into shorter lines. */) ...@@ -3348,7 +3425,8 @@ into shorter lines. */)
encoded = SAFE_ALLOCA (allength); encoded = SAFE_ALLOCA (allength);
encoded_length = base64_encode_1 (SSDATA (string), encoded_length = base64_encode_1 (SSDATA (string),
encoded, length, NILP (no_line_break), encoded, length, line_break,
pad, base64url,
STRING_MULTIBYTE (string)); STRING_MULTIBYTE (string));
if (encoded_length > allength) if (encoded_length > allength)
emacs_abort (); emacs_abort ();
...@@ -3367,7 +3445,8 @@ into shorter lines. */) ...@@ -3367,7 +3445,8 @@ into shorter lines. */)
static ptrdiff_t static ptrdiff_t
base64_encode_1 (const char *from, char *to, ptrdiff_t length, base64_encode_1 (const char *from, char *to, ptrdiff_t length,
bool line_break, bool multibyte) bool line_break, bool pad, bool base64url,
bool multibyte)
{ {
int counter = 0; int counter = 0;
ptrdiff_t i = 0; ptrdiff_t i = 0;
...@@ -3375,6 +3454,7 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length, ...@@ -3375,6 +3454,7 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length,
int c; int c;
unsigned int value; unsigned int value;
int bytes; int bytes;
char const *b64_value_to_char = (base64url) ? base64url_value_to_char : base64_value_to_char;
while (i < length) while (i < length)
{ {
...@@ -3405,16 +3485,19 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length, ...@@ -3405,16 +3485,19 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length,
/* Process first byte of a triplet. */ /* Process first byte of a triplet. */
*e++ = base64_value_to_char[0x3f & c >> 2]; *e++ = b64_value_to_char[0x3f & c >> 2];
value = (0x03 & c) << 4; value = (0x03 & c) << 4;
/* Process second byte of a triplet. */ /* Process second byte of a triplet. */
if (i == length) if (i == length)
{ {
*e++ = base64_value_to_char[value]; *e++ = b64_value_to_char[value];
*e++ = '='; if (pad)
*e++ = '='; {
*e++ = '=';
*e++ = '=';
}
break; break;
} }
...@@ -3430,15 +3513,18 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length, ...@@ -3430,15 +3513,18 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length,
else else
c = from[i++]; c = from[i++];
*e++ = base64_value_to_char[value | (0x0f & c >> 4)]; *e++ = b64_value_to_char[value | (0x0f & c >> 4)];
value = (0x0f & c) << 2; value = (0x0f & c) << 2;
/* Process third byte of a triplet. */ /* Process third byte of a triplet. */
if (i == length) if (i == length)
{ {
*e++ = base64_value_to_char[value]; *e++ = b64_value_to_char[value];
*e++ = '='; if (pad)
{
*e++ = '=';
}
break; break;
} }
...@@ -3454,8 +3540,8 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length, ...@@ -3454,8 +3540,8 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length,
else else
c = from[i++]; c = from[i++];
*e++ = base64_value_to_char[value | (0x03 & c >> 6)]; *e++ = b64_value_to_char[value | (0x03 & c >> 6)];
*e++ = base64_value_to_char[0x3f & c]; *e++ = b64_value_to_char[0x3f & c];
} }
return e - to; return e - to;
...@@ -3463,11 +3549,13 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length, ...@@ -3463,11 +3549,13 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length,
DEFUN ("base64-decode-region", Fbase64_decode_region, Sbase64_decode_region, DEFUN ("base64-decode-region", Fbase64_decode_region, Sbase64_decode_region,
2, 2, "r", 2, 3, "r",
doc: /* Base64-decode the region between BEG and END. doc: /* Base64-decode the region between BEG and END.
Return the length of the decoded text. Return the length of the decoded text.
If the region can't be decoded, signal an error and don't modify the buffer. */) If the region can't be decoded, signal an error and don't modify the buffer.
(Lisp_Object beg, Lisp_Object end) Optional third argument BASE64URL define if base64Url variant will be used
see RFC4648. */)
(Lisp_Object beg, Lisp_Object end, Lisp_Object base64url)
{ {
ptrdiff_t ibeg, iend, length, allength; ptrdiff_t ibeg, iend, length, allength;
char *decoded; char *decoded;
...@@ -3492,7 +3580,7 @@ If the region can't be decoded, signal an error and don't modify the buffer. */ ...@@ -3492,7 +3580,7 @@ If the region can't be decoded, signal an error and don't modify the buffer. */
move_gap_both (XFIXNAT (beg), ibeg); move_gap_both (XFIXNAT (beg), ibeg);
decoded_length = base64_decode_1 ((char *) BYTE_POS_ADDR (ibeg), decoded_length = base64_decode_1 ((char *) BYTE_POS_ADDR (ibeg),
decoded, length, decoded, length, !NILP (base64url),
multibyte, &inserted_chars); multibyte, &inserted_chars);
if (decoded_length > allength) if (decoded_length > allength)
emacs_abort (); emacs_abort ();
...@@ -3526,9 +3614,11 @@ If the region can't be decoded, signal an error and don't modify the buffer. */ ...@@ -3526,9 +3614,11 @@ If the region can't be decoded, signal an error and don't modify the buffer. */
} }
DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string, DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string,
1, 1, 0, 1, 2, 0,
doc: /* Base64-decode STRING and return the result. */) doc: /* Base64-decode STRING and return the result
(Lisp_Object string) Optional argument BASE64URL define if base64Url variant will be used
see RFC4648. */)
(Lisp_Object string, Lisp_Object base64url)
{ {
char *decoded; char *decoded;
ptrdiff_t length, decoded_length; ptrdiff_t length, decoded_length;
...@@ -3543,7 +3633,7 @@ DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string, ...@@ -3543,7 +3633,7 @@ DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string,
/* The decoded result should be unibyte. */ /* The decoded result should be unibyte. */
decoded_length = base64_decode_1 (SSDATA (string), decoded, length, decoded_length = base64_decode_1 (SSDATA (string), decoded, length,
0, NULL); !NILP (base64url), 0, NULL);
if (decoded_length > length) if (decoded_length > length)
emacs_abort (); emacs_abort ();
else if (decoded_length >= 0) else if (decoded_length >= 0)
...@@ -3565,6 +3655,7 @@ DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string, ...@@ -3565,6 +3655,7 @@ DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string,
static ptrdiff_t static ptrdiff_t
base64_decode_1 (const char *from, char *to, ptrdiff_t length, base64_decode_1 (const char *from, char *to, ptrdiff_t length,
bool base64url,
bool multibyte, ptrdiff_t *nchars_return) bool multibyte, ptrdiff_t *nchars_return)
{ {
ptrdiff_t i = 0; /* Used inside READ_QUADRUPLET_BYTE */ ptrdiff_t i = 0; /* Used inside READ_QUADRUPLET_BYTE */
...@@ -3572,6 +3663,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length, ...@@ -3572,6 +3663,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length,
unsigned char c; unsigned char c;
unsigned long value; unsigned long value;
ptrdiff_t nchars = 0; ptrdiff_t nchars = 0;
short const *b64_char_to_value = (base64url) ? base64url_char_to_value : base64_char_to_value;
while (1) while (1)
{ {
...@@ -3581,7 +3673,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length, ...@@ -3581,7 +3673,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length,
if (!IS_BASE64 (c)) if (!IS_BASE64 (c))
return -1; return -1;
value = base64_char_to_value[c] << 18; value = b64_char_to_value[c] << 18;
/* Process second byte of a quadruplet. */ /* Process second byte of a quadruplet. */
...@@ -3589,7 +3681,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length, ...@@ -3589,7 +3681,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length,
if (!IS_BASE64 (c)) if (!IS_BASE64 (c))
return -1; return -1;
value |= base64_char_to_value[c] << 12; value |= b64_char_to_value[c] << 12;
c = (unsigned char) (value >> 16); c = (unsigned char) (value >> 16);
if (multibyte && c >= 128) if (multibyte && c >= 128)
...@@ -3600,7 +3692,14 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length, ...@@ -3600,7 +3692,14 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length,
/* Process third byte of a quadruplet. */ /* Process third byte of a quadruplet. */
READ_QUADRUPLET_BYTE (-1); if (!base64url)
{
READ_QUADRUPLET_BYTE (-1);
}
else
{
READ_QUADRUPLET_BYTE (e-to);
}
if (c == '=') if (c == '=')
{ {
...@@ -3613,7 +3712,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length, ...@@ -3613,7 +3712,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length,
if (!IS_BASE64 (c)) if (!IS_BASE64 (c))
return -1; return -1;
value |= base64_char_to_value[c] << 6; value |= b64_char_to_value[c] << 6;
c = (unsigned char) (0xff & value >> 8); c = (unsigned char) (0xff & value >> 8);
if (multibyte && c >= 128) if (multibyte && c >= 128)
...@@ -3624,14 +3723,21 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length, ...@@ -3624,14 +3723,21 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length,
/* Process fourth byte of a quadruplet. */ /* Process fourth byte of a quadruplet. */
READ_QUADRUPLET_BYTE (-1); if (!base64url)
{
READ_QUADRUPLET_BYTE (-1);
}
else
{
READ_QUADRUPLET_BYTE (e-to);
}
if (c == '=') if (c == '=')
continue; continue;
if (!IS_BASE64 (c)) if (!IS_BASE64 (c))
return -1; return -1;
value |= base64_char_to_value[c]; value |= b64_char_to_value[c];
c = (unsigned char) (0xff & value); c = (unsigned char) (0xff & value);
if (multibyte && c >= 128) if (multibyte && c >= 128)
...@@ -5461,6 +5567,8 @@ this variable. */); ...@@ -5461,6 +5567,8 @@ this variable. */);
defsubr (&Sbase64_decode_region); defsubr (&Sbase64_decode_region);
defsubr (&Sbase64_encode_string); defsubr (&Sbase64_encode_string);
defsubr (&Sbase64_decode_string); defsubr (&Sbase64_decode_string);
defsubr (&Sbase64url_encode_region);
defsubr (&Sbase64url_encode_string);
defsubr (&Smd5); defsubr (&Smd5);
defsubr (&Ssecure_hash_algorithms); defsubr (&Ssecure_hash_algorithms);
defsubr (&Ssecure_hash); defsubr (&Ssecure_hash);
......
...@@ -233,6 +233,185 @@ ...@@ -233,6 +233,185 @@
(should (equal (func-arity (eval (lambda (x &optional y)) t)) '(1 . 2))) (should (equal (func-arity (eval (lambda (x &optional y)) t)) '(1 . 2)))
(should (equal (func-arity 'let) '(1 . unevalled)))) (should (equal (func-arity 'let) '(1 . unevalled))))
(defun fns-tests--string-repeat (s o)
(apply 'concat (make-list o s)))
(defmacro fns-tests--with-region (funcname string &rest args)
"Apply FUNCNAME in a temp bufer on the region produced by STRING."
(declare (indent 1))
`(with-temp-buffer
(insert ,string)
(,funcname (point-min) (point-max) ,@args)
(buffer-string)))
(ert-deftest fns-tests-base64-encode-region ()
;; standard variant RFC2045
(should (equal (fns-tests--with-region base64-encode-region "") ""))
(should (equal (fns-tests--with-region base64-encode-region "f") "Zg=="))
(should (equal (fns-tests--with-region base64-encode-region "fo") "Zm8="))
(should (equal (fns-tests--with-region base64-encode-region "foo") "Zm9v"))
(should (equal (fns-tests--with-region base64-encode-region "foob") "Zm9vYg=="))
(should (equal (fns-tests--with-region base64-encode-region "fooba") "Zm9vYmE="))
(should (equal (fns-tests--with-region base64-encode-region "foobar") "Zm9vYmFy"))
(should (equal (fns-tests--with-region base64-encode-region "\x14\xfb\x9c\x03\xd9\x7e") "FPucA9l+"))
(should (equal (fns-tests--with-region base64-encode-region "\x14\xfb\x9c\x03\xd9\x7f") "FPucA9l/")))
(ert-deftest fns-tests-base64-encode-string ()
;; standard variant RFC2045
(should (equal (base64-encode-string "") ""))
(should (equal (base64-encode-string "f") "Zg=="))
(should (equal (base64-encode-string "fo") "Zm8="))
(should (equal (base64-encode-string "foo") "Zm9v"))
(should (equal (base64-encode-string "foob") "Zm9vYg=="))
(should (equal (base64-encode-string "fooba") "Zm9vYmE="))
(should (equal (base64-encode-string "foobar") "Zm9vYmFy"))
(should (equal (base64-encode-string "\x14\xfb\x9c\x03\xd9\x7e") "FPucA9l+"))
(should (equal (base64-encode-string "\x14\xfb\x9c\x03\xd9\x7f") "FPucA9l/")))
(ert-deftest fns-test-base64url-encode-region ()
;; url variant wih padding
(should (equal (fns-tests--with-region base64url-encode-region "") ""))
(should (equal (fns-tests--with-region base64url-encode-region "f") "Zg=="))
(should (equal (fns-tests--with-region base64url-encode-region "fo") "Zm8="))
(should (equal (fns-tests--with-region base64url-encode-region "foo") "Zm9v"))
(should (equal (fns-tests--with-region base64url-encode-region "foob") "Zm9vYg=="))
(should (equal (fns-tests--with-region base64url-encode-region "fooba") "Zm9vYmE="))
(should (equal (fns-tests--with-region base64url-encode-region "foobar") "Zm9vYmFy"))
(should (equal (fns-tests--with-region base64url-encode-region "\x14\xfb\x9c\x03\xd9\x7e") "FPucA9l-"))
(should (equal (fns-tests--with-region base64url-encode-region "\x14\xfb\x9c\x03\xd9\x7f") "FPucA9l_"))
;; url variant no padding
(should (equal (fns-tests--with-region base64url-encode-region "" t) ""))
(should (equal (fns-tests--with-region base64url-encode-region "f" t) "Zg"))
(should (equal (fns-tests--with-region base64url-encode-region "fo" t) "Zm8"))
(should (equal (fns-tests--with-region base64url-encode-region "foo" t) "Zm9v"))
(should (equal (fns-tests--with-region base64url-encode-region "foob" t) "Zm9vYg"))
(should (equal (fns-tests--with-region base64url-encode-region "fooba" t) "Zm9vYmE"))
(should (equal (fns-tests--with-region base64url-encode-region "foobar" t) "Zm9vYmFy"))
(should (equal (fns-tests--with-region base64url-encode-region "\x14\xfb\x9c\x03\xd9\x7e" t) "FPucA9l-"))
(should (equal (fns-tests--with-region base64url-encode-region "\x14\xfb\x9c\x03\xd9\x7f" t) "FPucA9l_"))
;; url variant no line break no padding
(should (equal (fns-tests--with-region base64url-encode-region (fns-tests--string-repeat "f" 100) t)
(concat (fns-tests--string-repeat "Zm" 66) "Zg")))
(should (equal (fns-tests--with-region base64url-encode-region (fns-tests--string-repeat "fo" 50) t)
(concat (fns-tests--string-repeat "Zm9mb2Zv" 16) "Zm9mbw")))
(should (equal (fns-tests--with-region base64url-encode-region (fns-tests--string-repeat "foo" 25) t)
(fns-tests--string-repeat "Zm9v" 25)))
(should (equal (fns-tests--with-region base64url-encode-region (fns-tests--string-repeat "foob" 15) t)
(fns-tests--string-repeat "Zm9vYmZvb2Jmb29i" 5)))
(should (equal (fns-tests--with-region base64url-encode-region (fns-tests--string-repeat "fooba" 15) t)
(fns-tests--string-repeat "Zm9vYmFmb29iYWZvb2Jh" 5)))
(should (equal (fns-tests--with-region base64url-encode-region (fns-tests--string-repeat "foobar" 15) t)
(concat (fns-tests--string-repeat "Zm9vYmFyZm9vYmFy" 7) "Zm9vYmFy")))
(should (equal (fns-tests--with-region base64url-encode-region (fns-tests--string-repeat "\x14\xfb\x9c\x03\xd9\x7e" 10) t)
(fns-tests--string-repeat "FPucA9l-" 10)))
(should (equal (fns-tests--with-region base64url-encode-region (fns-tests--string-repeat "\x14\xfb\x9c\x03\xd9\x7f" 10) t)
(fns-tests--string-repeat "FPucA9l_" 10))))
(ert-deftest fns-test-base64url-encode-string ()
;; url variant wih padding
(should (equal (base64url-encode-string "") ""))
(should (equal (base64url-encode-string "f") "Zg=="))
(should (equal (base64url-encode-string "fo") "Zm8="))
(should (equal (base64url-encode-string "foo") "Zm9v"))
(should (equal (base64url-encode-string "foob") "Zm9vYg=="))
(should (equal (base64url-encode-string "fooba") "Zm9vYmE="))
(should (equal (base64url-encode-string "foobar") "Zm9vYmFy"))
(should (equal (base64url-encode-string "\x14\xfb\x9c\x03\xd9\x7e") "FPucA9l-"))
(should (equal (base64url-encode-string "\x14\xfb\x9c\x03\xd9\x7f") "FPucA9l_"))
;; url variant no padding
(should (equal (base64url-encode-string "" t) ""))
(should (equal (base64url-encode-string "f" t) "Zg"))
(should (equal (base64url-encode-string "fo" t) "Zm8"))
(should (equal (base64url-encode-string "foo" t) "Zm9v"))
(should (equal (base64url-encode-string "foob" t) "Zm9vYg"))
(should (equal (base64url-encode-string "fooba" t) "Zm9vYmE"))
(should (equal (base64url-encode-string "foobar" t) "Zm9vYmFy"))