xref: /linux/lib/base64.c (revision f1e2ca801c54dfc09d6a5540207cec25e8d43f6f)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * base64.c - Base64 with support for multiple variants
4  *
5  * Copyright (c) 2020 Hannes Reinecke, SUSE
6  *
7  * Based on the base64url routines from fs/crypto/fname.c
8  * (which are using the URL-safe Base64 encoding),
9  * modified to support multiple Base64 variants.
10  */
11 
12 #include <linux/kernel.h>
13 #include <linux/types.h>
14 #include <linux/export.h>
15 #include <linux/string.h>
16 #include <linux/base64.h>
17 
18 static const char base64_tables[][65] = {
19 	[BASE64_STD] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
20 	[BASE64_URLSAFE] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
21 	[BASE64_IMAP] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,",
22 };
23 
24 /**
25  * base64_encode() - Base64-encode some binary data
26  * @src: the binary data to encode
27  * @srclen: the length of @src in bytes
28  * @dst: (output) the Base64-encoded string.  Not NUL-terminated.
29  * @padding: whether to append '=' padding characters
30  * @variant: which base64 variant to use
31  *
32  * Encodes data using the selected Base64 variant.
33  *
34  * Return: the length of the resulting Base64-encoded string in bytes.
35  */
36 int base64_encode(const u8 *src, int srclen, char *dst, bool padding, enum base64_variant variant)
37 {
38 	u32 ac = 0;
39 	int bits = 0;
40 	int i;
41 	char *cp = dst;
42 	const char *base64_table = base64_tables[variant];
43 
44 	for (i = 0; i < srclen; i++) {
45 		ac = (ac << 8) | src[i];
46 		bits += 8;
47 		do {
48 			bits -= 6;
49 			*cp++ = base64_table[(ac >> bits) & 0x3f];
50 		} while (bits >= 6);
51 	}
52 	if (bits) {
53 		*cp++ = base64_table[(ac << (6 - bits)) & 0x3f];
54 		bits -= 6;
55 	}
56 	if (padding) {
57 		while (bits < 0) {
58 			*cp++ = '=';
59 			bits += 2;
60 		}
61 	}
62 	return cp - dst;
63 }
64 EXPORT_SYMBOL_GPL(base64_encode);
65 
66 /**
67  * base64_decode() - Base64-decode a string
68  * @src: the string to decode.  Doesn't need to be NUL-terminated.
69  * @srclen: the length of @src in bytes
70  * @dst: (output) the decoded binary data
71  * @padding: whether to append '=' padding characters
72  * @variant: which base64 variant to use
73  *
74  * Decodes a string using the selected Base64 variant.
75  *
76  * This implementation hasn't been optimized for performance.
77  *
78  * Return: the length of the resulting decoded binary data in bytes,
79  *	   or -1 if the string isn't a valid Base64 string.
80  */
81 int base64_decode(const char *src, int srclen, u8 *dst, bool padding, enum base64_variant variant)
82 {
83 	u32 ac = 0;
84 	int bits = 0;
85 	int i;
86 	u8 *bp = dst;
87 	const char *base64_table = base64_tables[variant];
88 
89 	for (i = 0; i < srclen; i++) {
90 		const char *p = strchr(base64_table, src[i]);
91 		if (padding) {
92 			if (src[i] == '=') {
93 				ac = (ac << 6);
94 				bits += 6;
95 				if (bits >= 8)
96 					bits -= 8;
97 				continue;
98 			}
99 		}
100 		if (p == NULL || src[i] == 0)
101 			return -1;
102 		ac = (ac << 6) | (p - base64_table);
103 		bits += 6;
104 		if (bits >= 8) {
105 			bits -= 8;
106 			*bp++ = (u8)(ac >> bits);
107 		}
108 	}
109 	if (ac & ((1 << bits) - 1))
110 		return -1;
111 	return bp - dst;
112 }
113 EXPORT_SYMBOL_GPL(base64_decode);
114