xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/netpgpverify/b64.c (revision 472564b29d7bf4bc9876355c8b02068f30473571)
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