125f78d91Sagc /*********************************************************************\
225f78d91Sagc
325f78d91Sagc MODULE NAME: b64.c
425f78d91Sagc
525f78d91Sagc AUTHOR: Bob Trower 08/04/01
625f78d91Sagc
725f78d91Sagc PROJECT: Crypt Data Packaging
825f78d91Sagc
925f78d91Sagc COPYRIGHT: Copyright (c) Trantor Standard Systems Inc., 2001
1025f78d91Sagc
1125f78d91Sagc NOTE: This source code may be used as you wish, subject to
1225f78d91Sagc the MIT license. See the LICENCE section below.
1325f78d91Sagc
1425f78d91Sagc DESCRIPTION:
1525f78d91Sagc This little utility implements the Base64
1625f78d91Sagc Content-Transfer-Encoding standard described in
1725f78d91Sagc RFC1113 (http://www.faqs.org/rfcs/rfc1113.html).
1825f78d91Sagc
1925f78d91Sagc This is the coding scheme used by MIME to allow
2025f78d91Sagc binary data to be transferred by SMTP mail.
2125f78d91Sagc
2225f78d91Sagc Groups of 3 bytes from a binary stream are coded as
2325f78d91Sagc groups of 4 bytes in a text stream.
2425f78d91Sagc
2525f78d91Sagc The input stream is 'padded' with zeros to create
2625f78d91Sagc an input that is an even multiple of 3.
2725f78d91Sagc
2825f78d91Sagc A special character ('=') is used to denote padding so
2925f78d91Sagc that the stream can be decoded back to its exact size.
3025f78d91Sagc
3125f78d91Sagc Encoded output is formatted in lines which should
3225f78d91Sagc be a maximum of 72 characters to conform to the
3325f78d91Sagc specification. This program defaults to 72 characters,
3425f78d91Sagc but will allow more or less through the use of a
3525f78d91Sagc switch. The program enforces a minimum line size
3625f78d91Sagc of 4 characters.
3725f78d91Sagc
3825f78d91Sagc Example encoding:
3925f78d91Sagc
4025f78d91Sagc The stream 'ABCD' is 32 bits long. It is mapped as
4125f78d91Sagc follows:
4225f78d91Sagc
4325f78d91Sagc ABCD
4425f78d91Sagc
4525f78d91Sagc A (65) B (66) C (67) D (68) (None) (None)
4625f78d91Sagc 01000001 01000010 01000011 01000100
4725f78d91Sagc
4825f78d91Sagc 16 (Q) 20 (U) 9 (J) 3 (D) 17 (R) 0 (A) NA (=) NA (=)
4925f78d91Sagc 010000 010100 001001 000011 010001 000000 000000 000000
5025f78d91Sagc
5125f78d91Sagc
5225f78d91Sagc QUJDRA==
5325f78d91Sagc
5425f78d91Sagc Decoding is the process in reverse. A 'decode' lookup
5525f78d91Sagc table has been created to avoid string scans.
5625f78d91Sagc
5725f78d91Sagc DESIGN GOALS: Specifically:
5825f78d91Sagc Code is a stand-alone utility to perform base64
5925f78d91Sagc encoding/decoding. It should be genuinely useful
6025f78d91Sagc when the need arises and it meets a need that is
6125f78d91Sagc likely to occur for some users.
6225f78d91Sagc Code acts as sample code to show the author's
6325f78d91Sagc design and coding style.
6425f78d91Sagc
6525f78d91Sagc Generally:
6625f78d91Sagc This program is designed to survive:
6725f78d91Sagc Everything you need is in a single source file.
6825f78d91Sagc It compiles cleanly using a vanilla ANSI C compiler.
6925f78d91Sagc It does its job correctly with a minimum of fuss.
7025f78d91Sagc The code is not overly clever, not overly simplistic
7125f78d91Sagc and not overly verbose.
7225f78d91Sagc Access is 'cut and paste' from a web page.
7325f78d91Sagc Terms of use are reasonable.
7425f78d91Sagc
7525f78d91Sagc VALIDATION: Non-trivial code is never without errors. This
7625f78d91Sagc file likely has some problems, since it has only
7725f78d91Sagc been tested by the author. It is expected with most
7825f78d91Sagc source code that there is a period of 'burn-in' when
7925f78d91Sagc problems are identified and corrected. That being
8025f78d91Sagc said, it is possible to have 'reasonably correct'
8125f78d91Sagc code by following a regime of unit test that covers
8225f78d91Sagc the most likely cases and regression testing prior
8325f78d91Sagc to release. This has been done with this code and
8425f78d91Sagc it has a good probability of performing as expected.
8525f78d91Sagc
8625f78d91Sagc Unit Test Cases:
8725f78d91Sagc
8825f78d91Sagc case 0:empty file:
8925f78d91Sagc CASE0.DAT -> ->
9025f78d91Sagc (Zero length target file created
9125f78d91Sagc on both encode and decode.)
9225f78d91Sagc
9325f78d91Sagc case 1:One input character:
9425f78d91Sagc CASE1.DAT A -> QQ== -> A
9525f78d91Sagc
9625f78d91Sagc case 2:Two input characters:
9725f78d91Sagc CASE2.DAT AB -> QUJD -> AB
9825f78d91Sagc
9925f78d91Sagc case 3:Three input characters:
10025f78d91Sagc CASE3.DAT ABC -> QUJD -> ABC
10125f78d91Sagc
10225f78d91Sagc case 4:Four input characters:
10325f78d91Sagc case4.dat ABCD -> QUJDRA== -> ABCD
10425f78d91Sagc
10525f78d91Sagc case 5:All chars from 0 to ff, linesize set to 50:
10625f78d91Sagc
10725f78d91Sagc AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIj
10825f78d91Sagc JCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZH
10925f78d91Sagc SElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWpr
11025f78d91Sagc bG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P
11125f78d91Sagc kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz
11225f78d91Sagc tLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX
11325f78d91Sagc 2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7
11425f78d91Sagc /P3+/w==
11525f78d91Sagc
11625f78d91Sagc case 6:Mime Block from e-mail:
11725f78d91Sagc (Data same as test case 5)
11825f78d91Sagc
11925f78d91Sagc case 7: Large files:
12025f78d91Sagc Tested 28 MB file in/out.
12125f78d91Sagc
12225f78d91Sagc case 8: Random Binary Integrity:
12325f78d91Sagc This binary program (b64.exe) was encoded to base64,
12425f78d91Sagc back to binary and then executed.
12525f78d91Sagc
12625f78d91Sagc case 9 Stress:
12725f78d91Sagc All files in a working directory encoded/decoded
12825f78d91Sagc and compared with file comparison utility to
12925f78d91Sagc ensure that multiple runs do not cause problems
13025f78d91Sagc such as exhausting file handles, tmp storage, etc.
13125f78d91Sagc
13225f78d91Sagc -------------
13325f78d91Sagc
13425f78d91Sagc Syntax, operation and failure:
13525f78d91Sagc All options/switches tested. Performs as
13625f78d91Sagc expected.
13725f78d91Sagc
13825f78d91Sagc case 10:
13925f78d91Sagc No Args -- Shows Usage Screen
14025f78d91Sagc Return Code 1 (Invalid Syntax)
14125f78d91Sagc case 11:
14225f78d91Sagc One Arg (invalid) -- Shows Usage Screen
14325f78d91Sagc Return Code 1 (Invalid Syntax)
14425f78d91Sagc case 12:
14525f78d91Sagc One Arg Help (-?) -- Shows detailed Usage Screen.
14625f78d91Sagc Return Code 0 (Success -- help request is valid).
14725f78d91Sagc case 13:
14825f78d91Sagc One Arg Help (-h) -- Shows detailed Usage Screen.
14925f78d91Sagc Return Code 0 (Success -- help request is valid).
15025f78d91Sagc case 14:
15125f78d91Sagc One Arg (valid) -- Uses stdin/stdout (filter)
15225f78d91Sagc Return Code 0 (Sucess)
15325f78d91Sagc case 15:
15425f78d91Sagc Two Args (invalid file) -- shows system error.
15525f78d91Sagc Return Code 2 (File Error)
15625f78d91Sagc case 16:
15725f78d91Sagc Encode non-existent file -- shows system error.
15825f78d91Sagc Return Code 2 (File Error)
15925f78d91Sagc case 17:
16025f78d91Sagc Out of disk space -- shows system error.
16125f78d91Sagc Return Code 3 (File I/O Error)
16225f78d91Sagc
16325f78d91Sagc -------------
16425f78d91Sagc
16525f78d91Sagc Compile/Regression test:
16625f78d91Sagc gcc compiled binary under Cygwin
16725f78d91Sagc Microsoft Visual Studio under Windows 2000
16825f78d91Sagc Microsoft Version 6.0 C under Windows 2000
16925f78d91Sagc
17025f78d91Sagc DEPENDENCIES: None
17125f78d91Sagc
17225f78d91Sagc LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
17325f78d91Sagc
17425f78d91Sagc Permission is hereby granted, free of charge, to any person
17525f78d91Sagc obtaining a copy of this software and associated
17625f78d91Sagc documentation files (the "Software"), to deal in the
17725f78d91Sagc Software without restriction, including without limitation
17825f78d91Sagc the rights to use, copy, modify, merge, publish, distribute,
17925f78d91Sagc sublicense, and/or sell copies of the Software, and to
18025f78d91Sagc permit persons to whom the Software is furnished to do so,
18125f78d91Sagc subject to the following conditions:
18225f78d91Sagc
18325f78d91Sagc The above copyright notice and this permission notice shall
18425f78d91Sagc be included in all copies or substantial portions of the
18525f78d91Sagc Software.
18625f78d91Sagc
18725f78d91Sagc THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
18825f78d91Sagc KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18925f78d91Sagc WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
19025f78d91Sagc PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
19125f78d91Sagc OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19225f78d91Sagc OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19325f78d91Sagc OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
19425f78d91Sagc SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19525f78d91Sagc
19625f78d91Sagc VERSION HISTORY:
19725f78d91Sagc Bob Trower 08/04/01 -- Create Version 0.00.00B
19825f78d91Sagc
19925f78d91Sagc \******************************************************************* */
20025f78d91Sagc
20125f78d91Sagc #include <inttypes.h>
20225f78d91Sagc #include <stdio.h>
20325f78d91Sagc #include <stdlib.h>
20425f78d91Sagc
20525f78d91Sagc #include "b64.h"
20625f78d91Sagc
20725f78d91Sagc /*
20825f78d91Sagc ** Translation Table as described in RFC1113
20925f78d91Sagc */
21025f78d91Sagc static const char cb64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
21125f78d91Sagc
21225f78d91Sagc /*
21325f78d91Sagc ** Translation Table to decode (created by author)
21425f78d91Sagc */
21525f78d91Sagc static const char cd64[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
21625f78d91Sagc
21725f78d91Sagc /*
21825f78d91Sagc ** encodeblock
21925f78d91Sagc **
22025f78d91Sagc ** encode 3 8-bit binary bytes as 4 '6-bit' characters
22125f78d91Sagc */
22225f78d91Sagc static void
encodeblock(uint8_t * wordin,uint8_t * wordout,int wordlen)22325f78d91Sagc encodeblock(uint8_t *wordin, uint8_t *wordout, int wordlen)
22425f78d91Sagc {
22525f78d91Sagc wordout[0] = cb64[(unsigned)wordin[0] >> 2];
22625f78d91Sagc wordout[1] = cb64[((unsigned)(wordin[0] & 0x03) << 4) | ((unsigned)(wordin[1] & 0xf0) >> 4)];
22725f78d91Sagc wordout[2] = (uint8_t)(wordlen > 1) ?
22825f78d91Sagc cb64[((unsigned)(wordin[1] & 0x0f) << 2) | ((unsigned)(wordin[2] & 0xc0) >> 6)] : '=';
22925f78d91Sagc wordout[3] = (uint8_t)(wordlen > 2) ? cb64[wordin[2] & 0x3f] : '=';
23025f78d91Sagc }
23125f78d91Sagc
23225f78d91Sagc /*
23325f78d91Sagc ** encode
23425f78d91Sagc **
23525f78d91Sagc ** base64 encode a stream adding padding and line breaks as per spec.
23625f78d91Sagc */
23725f78d91Sagc int
netpgpv_b64encode(const char * in,const size_t insize,void * vp,size_t outsize,int linesize)238*472564b2Sagc netpgpv_b64encode(const char *in, const size_t insize, void *vp, size_t outsize, int linesize)
23925f78d91Sagc {
24025f78d91Sagc const char *inp;
24125f78d91Sagc unsigned i;
24225f78d91Sagc uint8_t wordout[4];
24325f78d91Sagc uint8_t wordin[3];
24425f78d91Sagc char *out = vp;
24525f78d91Sagc char *outp;
24625f78d91Sagc int blocksout;
24725f78d91Sagc int wordlen;
24825f78d91Sagc
24925f78d91Sagc if (in == NULL || vp == NULL) {
25025f78d91Sagc return 0;
25125f78d91Sagc }
25225f78d91Sagc wordlen = 0;
25325f78d91Sagc for (blocksout = 0, inp = in, outp = out; (size_t)(inp - in) < insize && (size_t)(outp - out) < outsize;) {
25425f78d91Sagc for (wordlen = 0, i = 0; i < sizeof(wordin); i++) {
25525f78d91Sagc wordin[i] = (uint8_t) *inp++;
25625f78d91Sagc if ((size_t)(inp - in) <= insize) {
25725f78d91Sagc wordlen++;
25825f78d91Sagc } else {
25925f78d91Sagc wordin[i] = 0x0;
26025f78d91Sagc }
26125f78d91Sagc }
26225f78d91Sagc if (wordlen > 0) {
26325f78d91Sagc encodeblock(wordin, wordout, wordlen);
26425f78d91Sagc for (i = 0; i < sizeof(wordout) ; i++) {
26525f78d91Sagc *outp++ = wordout[i];
26625f78d91Sagc }
26725f78d91Sagc blocksout++;
26825f78d91Sagc }
26925f78d91Sagc if (linesize > 0) {
27025f78d91Sagc if (blocksout >= (int)(linesize / sizeof(wordout)) ||
27125f78d91Sagc (size_t)(inp - in) >= insize) {
27225f78d91Sagc if (blocksout) {
27325f78d91Sagc *outp++ = '\r';
27425f78d91Sagc *outp++ = '\n';
27525f78d91Sagc }
27625f78d91Sagc blocksout = 0;
27725f78d91Sagc }
27825f78d91Sagc }
27925f78d91Sagc }
28025f78d91Sagc return (int)(outp - out);
28125f78d91Sagc }
28225f78d91Sagc
28325f78d91Sagc /*
28425f78d91Sagc ** decodeblock
28525f78d91Sagc **
28625f78d91Sagc ** decode 4 '6-bit' characters into 3 8-bit binary bytes
28725f78d91Sagc */
28825f78d91Sagc static void
decodeblock(uint8_t wordin[4],uint8_t wordout[3])28925f78d91Sagc decodeblock(uint8_t wordin[4], uint8_t wordout[3])
29025f78d91Sagc {
29125f78d91Sagc wordout[0] = (uint8_t) ((unsigned)wordin[0] << 2 | (unsigned)wordin[1] >> 4);
29225f78d91Sagc wordout[1] = (uint8_t) ((unsigned)wordin[1] << 4 | (unsigned)wordin[2] >> 2);
29325f78d91Sagc wordout[2] = (uint8_t) (((wordin[2] << 6) & 0xc0) | wordin[3]);
29425f78d91Sagc }
29525f78d91Sagc
29625f78d91Sagc /*
29725f78d91Sagc ** decode
29825f78d91Sagc **
29925f78d91Sagc ** decode a base64 encoded stream discarding padding, line breaks and noise
30025f78d91Sagc */
30125f78d91Sagc int
netpgpv_b64decode(const char * in,const size_t insize,void * vp,size_t outsize)302*472564b2Sagc netpgpv_b64decode(const char *in, const size_t insize, void *vp, size_t outsize)
30325f78d91Sagc {
30425f78d91Sagc const char *inp;
30525f78d91Sagc unsigned wordlen;
30625f78d91Sagc unsigned i;
30725f78d91Sagc uint8_t wordout[3];
30825f78d91Sagc uint8_t wordin[4];
30925f78d91Sagc uint8_t v;
31025f78d91Sagc char *out = vp;
31125f78d91Sagc char *outp;
31225f78d91Sagc
31325f78d91Sagc if (in == NULL || vp == NULL) {
31425f78d91Sagc return 0;
31525f78d91Sagc }
31625f78d91Sagc for (inp = in, outp = out ; (size_t)(inp - in) < insize && (size_t)(outp - out) < outsize ; ) {
31725f78d91Sagc for (wordlen = 0, i = 0 ; i < sizeof(wordin) && (size_t)(inp - in) < insize ; i++) {
31825f78d91Sagc /* get a single character */
31925f78d91Sagc for (v = 0; (size_t)(inp - in) <= insize && v == 0 ; ) {
32025f78d91Sagc if (*inp == '\r' && *(inp + 1) == '\n') {
32125f78d91Sagc inp += 2;
32225f78d91Sagc } else {
32325f78d91Sagc v = (uint8_t) *inp++;
32425f78d91Sagc v = (uint8_t) ((v < 43 || v > 122) ? 0 : cd64[v - 43]);
32525f78d91Sagc if (v) {
32625f78d91Sagc v = (uint8_t) ((v == '$') ? 0 : v - 61);
32725f78d91Sagc }
32825f78d91Sagc }
32925f78d91Sagc }
33025f78d91Sagc /* perhaps 0x0 pad */
33125f78d91Sagc if ((size_t)(inp - in) <= insize) {
33225f78d91Sagc wordlen += 1;
33325f78d91Sagc if (v) {
33425f78d91Sagc wordin[i] = (uint8_t) (v - 1);
33525f78d91Sagc }
33625f78d91Sagc } else {
33725f78d91Sagc wordin[i] = 0x0;
33825f78d91Sagc }
33925f78d91Sagc }
34025f78d91Sagc if (wordlen > 0) {
34125f78d91Sagc decodeblock(wordin, wordout);
34225f78d91Sagc for (i = 0; i < wordlen - 1 ; i++) {
34325f78d91Sagc *outp++ = wordout[i];
34425f78d91Sagc }
34525f78d91Sagc }
34625f78d91Sagc }
34725f78d91Sagc return (int)(outp - out);
34825f78d91Sagc }
34925f78d91Sagc
35025f78d91Sagc /* return the encoded size for n bytes input */
35125f78d91Sagc int
netpgpv_b64_encsize(unsigned n)352*472564b2Sagc netpgpv_b64_encsize(unsigned n)
35325f78d91Sagc {
35425f78d91Sagc return ((4 * n) / 3) + 4;
35525f78d91Sagc }
356