aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPierre Téchoueyres2019-05-21 23:00:13 +0200
committerEli Zaretskii2019-06-08 10:49:35 +0300
commit7541b06872ba134bfaa51b1aca7755a617fff807 (patch)
tree0190a36f96e0b37f1ea7f97e6b89975a155436af /src
parentfaf10bd8eb3272880b774fe220fa9916ed1f00c0 (diff)
downloademacs-7541b06872ba134bfaa51b1aca7755a617fff807.tar.gz
emacs-7541b06872ba134bfaa51b1aca7755a617fff807.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/fns.c166
1 files changed, 137 insertions, 29 deletions
diff --git a/src/fns.c b/src/fns.c
index ddd08af994a..79b8939330f 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -3189,7 +3189,7 @@ The data read from the system are decoded using `locale-coding-system'. */)
3189#define IS_ASCII(Character) \ 3189#define IS_ASCII(Character) \
3190 ((Character) < 128) 3190 ((Character) < 128)
3191#define IS_BASE64(Character) \ 3191#define IS_BASE64(Character) \
3192 (IS_ASCII (Character) && base64_char_to_value[Character] >= 0) 3192 (IS_ASCII (Character) && b64_char_to_value[Character] >= 0)
3193#define IS_BASE64_IGNORABLE(Character) \ 3193#define IS_BASE64_IGNORABLE(Character) \
3194 ((Character) == ' ' || (Character) == '\t' || (Character) == '\n' \ 3194 ((Character) == ' ' || (Character) == '\t' || (Character) == '\n' \
3195 || (Character) == '\f' || (Character) == '\r') 3195 || (Character) == '\f' || (Character) == '\r')
@@ -3222,6 +3222,17 @@ static const char base64_value_to_char[64] =
3222 '8', '9', '+', '/' /* 60-63 */ 3222 '8', '9', '+', '/' /* 60-63 */
3223}; 3223};
3224 3224
3225static const char base64url_value_to_char[64] =
3226{
3227 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', /* 0- 9 */
3228 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', /* 10-19 */
3229 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', /* 20-29 */
3230 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', /* 30-39 */
3231 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', /* 40-49 */
3232 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', /* 50-59 */
3233 '8', '9', '-', '_' /* 60-63 */
3234};
3235
3225/* Table of base64 values for first 128 characters. */ 3236/* Table of base64 values for first 128 characters. */
3226static const short base64_char_to_value[128] = 3237static const short base64_char_to_value[128] =
3227{ 3238{
@@ -3240,6 +3251,23 @@ static const short base64_char_to_value[128] =
3240 49, 50, 51, -1, -1, -1, -1, -1 /* 120-127 */ 3251 49, 50, 51, -1, -1, -1, -1, -1 /* 120-127 */
3241}; 3252};
3242 3253
3254static const short base64url_char_to_value[128] =
3255{
3256 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0- 9 */
3257 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10- 19 */
3258 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 20- 29 */
3259 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 30- 39 */
3260 -1, -1, -1, -1, -1, 62, -1, -1, 52, 53, /* 40- 49 */
3261 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, /* 50- 59 */
3262 -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, /* 60- 69 */
3263 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 70- 79 */
3264 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, /* 80- 89 */
3265 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, /* 90- 99 */
3266 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, /* 100-109 */
3267 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, /* 110-119 */
3268 49, 50, 51, -1, -1, -1, -1, -1 /* 120-127 */
3269};
3270
3243/* The following diagram shows the logical steps by which three octets 3271/* The following diagram shows the logical steps by which three octets
3244 get transformed into four base64 characters. 3272 get transformed into four base64 characters.
3245 3273
@@ -3259,9 +3287,17 @@ static const short base64_char_to_value[128] =
3259 base64 characters. */ 3287 base64 characters. */
3260 3288
3261 3289
3262static ptrdiff_t base64_encode_1 (const char *, char *, ptrdiff_t, bool, bool); 3290static ptrdiff_t base64_encode_1 (const char *, char *, ptrdiff_t, bool, bool,
3291 bool, bool);
3263static ptrdiff_t base64_decode_1 (const char *, char *, ptrdiff_t, bool, 3292static ptrdiff_t base64_decode_1 (const char *, char *, ptrdiff_t, bool,
3264 ptrdiff_t *); 3293 bool, ptrdiff_t *);
3294
3295Lisp_Object base64_encode_region_1 (Lisp_Object, Lisp_Object, bool,
3296 bool, bool);
3297
3298Lisp_Object base64_encode_string_1(Lisp_Object, bool,
3299 bool, bool);
3300
3265 3301
3266DEFUN ("base64-encode-region", Fbase64_encode_region, Sbase64_encode_region, 3302DEFUN ("base64-encode-region", Fbase64_encode_region, Sbase64_encode_region,
3267 2, 3, "r", 3303 2, 3, "r",
@@ -3271,6 +3307,26 @@ Optional third argument NO-LINE-BREAK means do not break long lines
3271into shorter lines. */) 3307into shorter lines. */)
3272 (Lisp_Object beg, Lisp_Object end, Lisp_Object no_line_break) 3308 (Lisp_Object beg, Lisp_Object end, Lisp_Object no_line_break)
3273{ 3309{
3310 return base64_encode_region_1(beg, end, NILP (no_line_break), true, false);
3311}
3312
3313
3314DEFUN ("base64url-encode-region", Fbase64url_encode_region, Sbase64url_encode_region,
3315 2, 3, "r",
3316 doc: /* Base64url-encode the region between BEG and END.
3317Return the length of the encoded text.
3318Optional second argument NO-PAD means do not add padding char =.
3319
3320This is the variant defined in RFC4648. */)
3321 (Lisp_Object beg, Lisp_Object end, Lisp_Object no_pad)
3322{
3323 return base64_encode_region_1(beg, end, false, NILP(no_pad), true);
3324}
3325
3326Lisp_Object
3327base64_encode_region_1 (Lisp_Object beg, Lisp_Object end, bool line_break,
3328 bool pad, bool base64url)
3329{
3274 char *encoded; 3330 char *encoded;
3275 ptrdiff_t allength, length; 3331 ptrdiff_t allength, length;
3276 ptrdiff_t ibeg, iend, encoded_length; 3332 ptrdiff_t ibeg, iend, encoded_length;
@@ -3292,7 +3348,8 @@ into shorter lines. */)
3292 3348
3293 encoded = SAFE_ALLOCA (allength); 3349 encoded = SAFE_ALLOCA (allength);
3294 encoded_length = base64_encode_1 ((char *) BYTE_POS_ADDR (ibeg), 3350 encoded_length = base64_encode_1 ((char *) BYTE_POS_ADDR (ibeg),
3295 encoded, length, NILP (no_line_break), 3351 encoded, length, line_break,
3352 pad, base64url,
3296 !NILP (BVAR (current_buffer, enable_multibyte_characters))); 3353 !NILP (BVAR (current_buffer, enable_multibyte_characters)));
3297 if (encoded_length > allength) 3354 if (encoded_length > allength)
3298 emacs_abort (); 3355 emacs_abort ();
@@ -3330,6 +3387,26 @@ Optional second argument NO-LINE-BREAK means do not break long lines
3330into shorter lines. */) 3387into shorter lines. */)
3331 (Lisp_Object string, Lisp_Object no_line_break) 3388 (Lisp_Object string, Lisp_Object no_line_break)
3332{ 3389{
3390
3391 return base64_encode_string_1(string, NILP (no_line_break), true, false);
3392}
3393
3394DEFUN ("base64url-encode-string", Fbase64url_encode_string, Sbase64url_encode_string,
3395 1, 2, 0,
3396 doc: /* Base64url-encode STRING and return the result.
3397Optional second argument NO-PAD means do not add padding char =.
3398
3399This is the variant defined in RFC4648. */)
3400 (Lisp_Object string, Lisp_Object no_pad)
3401{
3402
3403 return base64_encode_string_1(string, false, NILP(no_pad), true);
3404}
3405
3406Lisp_Object
3407base64_encode_string_1(Lisp_Object string, bool line_break,
3408 bool pad, bool base64url)
3409{
3333 ptrdiff_t allength, length, encoded_length; 3410 ptrdiff_t allength, length, encoded_length;
3334 char *encoded; 3411 char *encoded;
3335 Lisp_Object encoded_string; 3412 Lisp_Object encoded_string;
@@ -3348,7 +3425,8 @@ into shorter lines. */)
3348 encoded = SAFE_ALLOCA (allength); 3425 encoded = SAFE_ALLOCA (allength);
3349 3426
3350 encoded_length = base64_encode_1 (SSDATA (string), 3427 encoded_length = base64_encode_1 (SSDATA (string),
3351 encoded, length, NILP (no_line_break), 3428 encoded, length, line_break,
3429 pad, base64url,
3352 STRING_MULTIBYTE (string)); 3430 STRING_MULTIBYTE (string));
3353 if (encoded_length > allength) 3431 if (encoded_length > allength)
3354 emacs_abort (); 3432 emacs_abort ();
@@ -3367,7 +3445,8 @@ into shorter lines. */)
3367 3445
3368static ptrdiff_t 3446static ptrdiff_t
3369base64_encode_1 (const char *from, char *to, ptrdiff_t length, 3447base64_encode_1 (const char *from, char *to, ptrdiff_t length,
3370 bool line_break, bool multibyte) 3448 bool line_break, bool pad, bool base64url,
3449 bool multibyte)
3371{ 3450{
3372 int counter = 0; 3451 int counter = 0;
3373 ptrdiff_t i = 0; 3452 ptrdiff_t i = 0;
@@ -3375,6 +3454,7 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length,
3375 int c; 3454 int c;
3376 unsigned int value; 3455 unsigned int value;
3377 int bytes; 3456 int bytes;
3457 char const *b64_value_to_char = (base64url) ? base64url_value_to_char : base64_value_to_char;
3378 3458
3379 while (i < length) 3459 while (i < length)
3380 { 3460 {
@@ -3405,16 +3485,19 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length,
3405 3485
3406 /* Process first byte of a triplet. */ 3486 /* Process first byte of a triplet. */
3407 3487
3408 *e++ = base64_value_to_char[0x3f & c >> 2]; 3488 *e++ = b64_value_to_char[0x3f & c >> 2];
3409 value = (0x03 & c) << 4; 3489 value = (0x03 & c) << 4;
3410 3490
3411 /* Process second byte of a triplet. */ 3491 /* Process second byte of a triplet. */
3412 3492
3413 if (i == length) 3493 if (i == length)
3414 { 3494 {
3415 *e++ = base64_value_to_char[value]; 3495 *e++ = b64_value_to_char[value];
3416 *e++ = '='; 3496 if (pad)
3417 *e++ = '='; 3497 {
3498 *e++ = '=';
3499 *e++ = '=';
3500 }
3418 break; 3501 break;
3419 } 3502 }
3420 3503
@@ -3430,15 +3513,18 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length,
3430 else 3513 else
3431 c = from[i++]; 3514 c = from[i++];
3432 3515
3433 *e++ = base64_value_to_char[value | (0x0f & c >> 4)]; 3516 *e++ = b64_value_to_char[value | (0x0f & c >> 4)];
3434 value = (0x0f & c) << 2; 3517 value = (0x0f & c) << 2;
3435 3518
3436 /* Process third byte of a triplet. */ 3519 /* Process third byte of a triplet. */
3437 3520
3438 if (i == length) 3521 if (i == length)
3439 { 3522 {
3440 *e++ = base64_value_to_char[value]; 3523 *e++ = b64_value_to_char[value];
3441 *e++ = '='; 3524 if (pad)
3525 {
3526 *e++ = '=';
3527 }
3442 break; 3528 break;
3443 } 3529 }
3444 3530
@@ -3454,8 +3540,8 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length,
3454 else 3540 else
3455 c = from[i++]; 3541 c = from[i++];
3456 3542
3457 *e++ = base64_value_to_char[value | (0x03 & c >> 6)]; 3543 *e++ = b64_value_to_char[value | (0x03 & c >> 6)];
3458 *e++ = base64_value_to_char[0x3f & c]; 3544 *e++ = b64_value_to_char[0x3f & c];
3459 } 3545 }
3460 3546
3461 return e - to; 3547 return e - to;
@@ -3463,11 +3549,13 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t length,
3463 3549
3464 3550
3465DEFUN ("base64-decode-region", Fbase64_decode_region, Sbase64_decode_region, 3551DEFUN ("base64-decode-region", Fbase64_decode_region, Sbase64_decode_region,
3466 2, 2, "r", 3552 2, 3, "r",
3467 doc: /* Base64-decode the region between BEG and END. 3553 doc: /* Base64-decode the region between BEG and END.
3468Return the length of the decoded text. 3554Return the length of the decoded text.
3469If the region can't be decoded, signal an error and don't modify the buffer. */) 3555If the region can't be decoded, signal an error and don't modify the buffer.
3470 (Lisp_Object beg, Lisp_Object end) 3556Optional third argument BASE64URL define if base64Url variant will be used
3557see RFC4648. */)
3558 (Lisp_Object beg, Lisp_Object end, Lisp_Object base64url)
3471{ 3559{
3472 ptrdiff_t ibeg, iend, length, allength; 3560 ptrdiff_t ibeg, iend, length, allength;
3473 char *decoded; 3561 char *decoded;
@@ -3492,7 +3580,7 @@ If the region can't be decoded, signal an error and don't modify the buffer. */
3492 3580
3493 move_gap_both (XFIXNAT (beg), ibeg); 3581 move_gap_both (XFIXNAT (beg), ibeg);
3494 decoded_length = base64_decode_1 ((char *) BYTE_POS_ADDR (ibeg), 3582 decoded_length = base64_decode_1 ((char *) BYTE_POS_ADDR (ibeg),
3495 decoded, length, 3583 decoded, length, !NILP (base64url),
3496 multibyte, &inserted_chars); 3584 multibyte, &inserted_chars);
3497 if (decoded_length > allength) 3585 if (decoded_length > allength)
3498 emacs_abort (); 3586 emacs_abort ();
@@ -3526,9 +3614,11 @@ If the region can't be decoded, signal an error and don't modify the buffer. */
3526} 3614}
3527 3615
3528DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string, 3616DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string,
3529 1, 1, 0, 3617 1, 2, 0,
3530 doc: /* Base64-decode STRING and return the result. */) 3618 doc: /* Base64-decode STRING and return the result
3531 (Lisp_Object string) 3619Optional argument BASE64URL define if base64Url variant will be used
3620see RFC4648. */)
3621 (Lisp_Object string, Lisp_Object base64url)
3532{ 3622{
3533 char *decoded; 3623 char *decoded;
3534 ptrdiff_t length, decoded_length; 3624 ptrdiff_t length, decoded_length;
@@ -3543,7 +3633,7 @@ DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string,
3543 3633
3544 /* The decoded result should be unibyte. */ 3634 /* The decoded result should be unibyte. */
3545 decoded_length = base64_decode_1 (SSDATA (string), decoded, length, 3635 decoded_length = base64_decode_1 (SSDATA (string), decoded, length,
3546 0, NULL); 3636 !NILP (base64url), 0, NULL);
3547 if (decoded_length > length) 3637 if (decoded_length > length)
3548 emacs_abort (); 3638 emacs_abort ();
3549 else if (decoded_length >= 0) 3639 else if (decoded_length >= 0)
@@ -3565,6 +3655,7 @@ DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string,
3565 3655
3566static ptrdiff_t 3656static ptrdiff_t
3567base64_decode_1 (const char *from, char *to, ptrdiff_t length, 3657base64_decode_1 (const char *from, char *to, ptrdiff_t length,
3658 bool base64url,
3568 bool multibyte, ptrdiff_t *nchars_return) 3659 bool multibyte, ptrdiff_t *nchars_return)
3569{ 3660{
3570 ptrdiff_t i = 0; /* Used inside READ_QUADRUPLET_BYTE */ 3661 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 unsigned char c; 3663 unsigned char c;
3573 unsigned long value; 3664 unsigned long value;
3574 ptrdiff_t nchars = 0; 3665 ptrdiff_t nchars = 0;
3666 short const *b64_char_to_value = (base64url) ? base64url_char_to_value : base64_char_to_value;
3575 3667
3576 while (1) 3668 while (1)
3577 { 3669 {
@@ -3581,7 +3673,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length,
3581 3673
3582 if (!IS_BASE64 (c)) 3674 if (!IS_BASE64 (c))
3583 return -1; 3675 return -1;
3584 value = base64_char_to_value[c] << 18; 3676 value = b64_char_to_value[c] << 18;
3585 3677
3586 /* Process second byte of a quadruplet. */ 3678 /* Process second byte of a quadruplet. */
3587 3679
@@ -3589,7 +3681,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length,
3589 3681
3590 if (!IS_BASE64 (c)) 3682 if (!IS_BASE64 (c))
3591 return -1; 3683 return -1;
3592 value |= base64_char_to_value[c] << 12; 3684 value |= b64_char_to_value[c] << 12;
3593 3685
3594 c = (unsigned char) (value >> 16); 3686 c = (unsigned char) (value >> 16);
3595 if (multibyte && c >= 128) 3687 if (multibyte && c >= 128)
@@ -3600,7 +3692,14 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length,
3600 3692
3601 /* Process third byte of a quadruplet. */ 3693 /* Process third byte of a quadruplet. */
3602 3694
3603 READ_QUADRUPLET_BYTE (-1); 3695 if (!base64url)
3696 {
3697 READ_QUADRUPLET_BYTE (-1);
3698 }
3699 else
3700 {
3701 READ_QUADRUPLET_BYTE (e-to);
3702 }
3604 3703
3605 if (c == '=') 3704 if (c == '=')
3606 { 3705 {
@@ -3613,7 +3712,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length,
3613 3712
3614 if (!IS_BASE64 (c)) 3713 if (!IS_BASE64 (c))
3615 return -1; 3714 return -1;
3616 value |= base64_char_to_value[c] << 6; 3715 value |= b64_char_to_value[c] << 6;
3617 3716
3618 c = (unsigned char) (0xff & value >> 8); 3717 c = (unsigned char) (0xff & value >> 8);
3619 if (multibyte && c >= 128) 3718 if (multibyte && c >= 128)
@@ -3624,14 +3723,21 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t length,
3624 3723
3625 /* Process fourth byte of a quadruplet. */ 3724 /* Process fourth byte of a quadruplet. */
3626 3725
3627 READ_QUADRUPLET_BYTE (-1); 3726 if (!base64url)
3727 {
3728 READ_QUADRUPLET_BYTE (-1);
3729 }
3730 else
3731 {
3732 READ_QUADRUPLET_BYTE (e-to);
3733 }
3628 3734
3629 if (c == '=') 3735 if (c == '=')
3630 continue; 3736 continue;
3631 3737
3632 if (!IS_BASE64 (c)) 3738 if (!IS_BASE64 (c))
3633 return -1; 3739 return -1;
3634 value |= base64_char_to_value[c]; 3740 value |= b64_char_to_value[c];
3635 3741
3636 c = (unsigned char) (0xff & value); 3742 c = (unsigned char) (0xff & value);
3637 if (multibyte && c >= 128) 3743 if (multibyte && c >= 128)
@@ -5461,6 +5567,8 @@ this variable. */);
5461 defsubr (&Sbase64_decode_region); 5567 defsubr (&Sbase64_decode_region);
5462 defsubr (&Sbase64_encode_string); 5568 defsubr (&Sbase64_encode_string);
5463 defsubr (&Sbase64_decode_string); 5569 defsubr (&Sbase64_decode_string);
5570 defsubr (&Sbase64url_encode_region);
5571 defsubr (&Sbase64url_encode_string);
5464 defsubr (&Smd5); 5572 defsubr (&Smd5);
5465 defsubr (&Ssecure_hash_algorithms); 5573 defsubr (&Ssecure_hash_algorithms);
5466 defsubr (&Ssecure_hash); 5574 defsubr (&Ssecure_hash);