Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lib/base64: add support for multiple variants

Patch series " lib/base64: add generic encoder/decoder, migrate users", v5.

This series introduces a generic Base64 encoder/decoder to the kernel
library, eliminating duplicated implementations and delivering significant
performance improvements.

The Base64 API has been extended to support multiple variants (Standard,
URL-safe, and IMAP) as defined in RFC 4648 and RFC 3501. The API now
takes a variant parameter and an option to control padding. As part of
this series, users are migrated to the new interface while preserving
their specific formats: fscrypt now uses BASE64_URLSAFE, Ceph uses
BASE64_IMAP, and NVMe is updated to BASE64_STD.

On the encoder side, the implementation processes input in 3-byte blocks,
mapping 24 bits directly to 4 output symbols. This avoids bit-by-bit
streaming and reduces loop overhead, achieving about a 2.7x speedup
compared to previous implementations.

On the decoder side, replace strchr() lookups with per-variant reverse
tables and process input in 4-character groups. Each group is mapped to
numeric values and combined into 3 bytes. Padded and unpadded forms are
validated explicitly, rejecting invalid '=' usage and enforcing tail
rules. This improves throughput by ~43-52x.


This patch (of 6):

Extend the base64 API to support multiple variants (standard, URL-safe,
and IMAP) as defined in RFC 4648 and RFC 3501. The API now takes a
variant parameter and an option to control padding. Update NVMe auth code
to use the new interface with BASE64_STD.

Link: https://lkml.kernel.org/r/20251114055829.87814-1-409411716@gms.tku.edu.tw
Link: https://lkml.kernel.org/r/20251114060045.88792-1-409411716@gms.tku.edu.tw
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
Co-developed-by: Guan-Chun Wu <409411716@gms.tku.edu.tw>
Signed-off-by: Guan-Chun Wu <409411716@gms.tku.edu.tw>
Reviewed-by: David Laight <david.laight.linux@gmail.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Eric Biggers <ebiggers@kernel.org>
Cc: Ilya Dryomov <idryomov@gmail.com>
Cc: Jaegeuk Kim <jaegeuk@kernel.org>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Keith Busch <kbusch@kernel.org>
Cc: Sagi Grimberg <sagi@grimberg.me>
Cc: "Theodore Y. Ts'o" <tytso@mit.edu>
Cc: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
Cc: Xiubo Li <xiubli@redhat.com>
Cc: Yu-Sheng Huang <home7438072@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Kuan-Wei Chiu and committed by
Andrew Morton
f1e2ca80 03ef32d6

+46 -30
+2 -2
drivers/nvme/common/auth.c
··· 178 178 if (!key) 179 179 return ERR_PTR(-ENOMEM); 180 180 181 - key_len = base64_decode(secret, allocated_len, key->key); 181 + key_len = base64_decode(secret, allocated_len, key->key, true, BASE64_STD); 182 182 if (key_len < 0) { 183 183 pr_debug("base64 key decoding error %d\n", 184 184 key_len); ··· 663 663 if (ret) 664 664 goto out_free_digest; 665 665 666 - ret = base64_encode(digest, digest_len, enc); 666 + ret = base64_encode(digest, digest_len, enc, true, BASE64_STD); 667 667 if (ret < hmac_len) { 668 668 ret = -ENOKEY; 669 669 goto out_free_digest;
+8 -2
include/linux/base64.h
··· 8 8 9 9 #include <linux/types.h> 10 10 11 + enum base64_variant { 12 + BASE64_STD, /* RFC 4648 (standard) */ 13 + BASE64_URLSAFE, /* RFC 4648 (base64url) */ 14 + BASE64_IMAP, /* RFC 3501 */ 15 + }; 16 + 11 17 #define BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3) 12 18 13 - int base64_encode(const u8 *src, int len, char *dst); 14 - int base64_decode(const char *src, int len, u8 *dst); 19 + int base64_encode(const u8 *src, int len, char *dst, bool padding, enum base64_variant variant); 20 + int base64_decode(const char *src, int len, u8 *dst, bool padding, enum base64_variant variant); 15 21 16 22 #endif /* _LINUX_BASE64_H */
+36 -26
lib/base64.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 /* 3 - * base64.c - RFC4648-compliant base64 encoding 3 + * base64.c - Base64 with support for multiple variants 4 4 * 5 5 * Copyright (c) 2020 Hannes Reinecke, SUSE 6 6 * 7 7 * Based on the base64url routines from fs/crypto/fname.c 8 - * (which are using the URL-safe base64 encoding), 9 - * modified to use the standard coding table from RFC4648 section 4. 8 + * (which are using the URL-safe Base64 encoding), 9 + * modified to support multiple Base64 variants. 10 10 */ 11 11 12 12 #include <linux/kernel.h> ··· 15 15 #include <linux/string.h> 16 16 #include <linux/base64.h> 17 17 18 - static const char base64_table[65] = 19 - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 18 + static const char base64_tables[][65] = { 19 + [BASE64_STD] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 20 + [BASE64_URLSAFE] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", 21 + [BASE64_IMAP] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,", 22 + }; 20 23 21 24 /** 22 - * base64_encode() - base64-encode some binary data 25 + * base64_encode() - Base64-encode some binary data 23 26 * @src: the binary data to encode 24 27 * @srclen: the length of @src in bytes 25 - * @dst: (output) the base64-encoded string. Not NUL-terminated. 28 + * @dst: (output) the Base64-encoded string. Not NUL-terminated. 29 + * @padding: whether to append '=' padding characters 30 + * @variant: which base64 variant to use 26 31 * 27 - * Encodes data using base64 encoding, i.e. the "Base 64 Encoding" specified 28 - * by RFC 4648, including the '='-padding. 32 + * Encodes data using the selected Base64 variant. 29 33 * 30 - * Return: the length of the resulting base64-encoded string in bytes. 34 + * Return: the length of the resulting Base64-encoded string in bytes. 31 35 */ 32 - int base64_encode(const u8 *src, int srclen, char *dst) 36 + int base64_encode(const u8 *src, int srclen, char *dst, bool padding, enum base64_variant variant) 33 37 { 34 38 u32 ac = 0; 35 39 int bits = 0; 36 40 int i; 37 41 char *cp = dst; 42 + const char *base64_table = base64_tables[variant]; 38 43 39 44 for (i = 0; i < srclen; i++) { 40 45 ac = (ac << 8) | src[i]; ··· 53 48 *cp++ = base64_table[(ac << (6 - bits)) & 0x3f]; 54 49 bits -= 6; 55 50 } 56 - while (bits < 0) { 57 - *cp++ = '='; 58 - bits += 2; 51 + if (padding) { 52 + while (bits < 0) { 53 + *cp++ = '='; 54 + bits += 2; 55 + } 59 56 } 60 57 return cp - dst; 61 58 } 62 59 EXPORT_SYMBOL_GPL(base64_encode); 63 60 64 61 /** 65 - * base64_decode() - base64-decode a string 62 + * base64_decode() - Base64-decode a string 66 63 * @src: the string to decode. Doesn't need to be NUL-terminated. 67 64 * @srclen: the length of @src in bytes 68 65 * @dst: (output) the decoded binary data 66 + * @padding: whether to append '=' padding characters 67 + * @variant: which base64 variant to use 69 68 * 70 - * Decodes a string using base64 encoding, i.e. the "Base 64 Encoding" 71 - * specified by RFC 4648, including the '='-padding. 69 + * Decodes a string using the selected Base64 variant. 72 70 * 73 71 * This implementation hasn't been optimized for performance. 74 72 * 75 73 * Return: the length of the resulting decoded binary data in bytes, 76 - * or -1 if the string isn't a valid base64 string. 74 + * or -1 if the string isn't a valid Base64 string. 77 75 */ 78 - int base64_decode(const char *src, int srclen, u8 *dst) 76 + int base64_decode(const char *src, int srclen, u8 *dst, bool padding, enum base64_variant variant) 79 77 { 80 78 u32 ac = 0; 81 79 int bits = 0; 82 80 int i; 83 81 u8 *bp = dst; 82 + const char *base64_table = base64_tables[variant]; 84 83 85 84 for (i = 0; i < srclen; i++) { 86 85 const char *p = strchr(base64_table, src[i]); 87 - 88 - if (src[i] == '=') { 89 - ac = (ac << 6); 90 - bits += 6; 91 - if (bits >= 8) 92 - bits -= 8; 93 - continue; 86 + if (padding) { 87 + if (src[i] == '=') { 88 + ac = (ac << 6); 89 + bits += 6; 90 + if (bits >= 8) 91 + bits -= 8; 92 + continue; 93 + } 94 94 } 95 95 if (p == NULL || src[i] == 0) 96 96 return -1;