Enable and fix many extra warnings supported by GCC and Clang.
[tinc] / src / gcrypt / pem.c
1 /*
2     pem.c -- PEM encoding and decoding
3     Copyright (C) 2007-2022 Guus Sliepen <guus@tinc-vpn.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "../system.h"
21
22 #include "pem.h"
23 #include "../utils.h"
24
25 // Base64 decoding table
26
27 static const uint8_t b64dec[128] = {
28         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
29         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
30         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
31         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
32         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
33         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
34         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
35         0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
36         0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
37         0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
38         0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
39         0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
40         0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
41         0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
42         0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
43         0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
44         0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
45         0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
46         0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
47         0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
48         0x31, 0x32, 0x33, 0xff, 0xff, 0xff,
49         0xff, 0xff
50 };
51
52 static const char b64enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
53                              "abcdefghijklmnopqrstuvwxyz"
54                              "0123456789+/";
55
56 // Heavily based on code by Jouni Malinen <j@w1.fi>
57 // https://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c
58 static size_t b64encode(char *dst, const void *src, const size_t length) {
59         const uint8_t *end = (const uint8_t *)src + length;
60         const uint8_t *in = src;
61         char *pos = dst;
62
63         while(end - in >= 3) {
64                 *pos++ = b64enc[in[0] >> 2];
65                 *pos++ = b64enc[((in[0] & 0x03) << 4) | (in[1] >> 4)];
66                 *pos++ = b64enc[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
67                 *pos++ = b64enc[in[2] & 0x3f];
68                 in += 3;
69         }
70
71         if(end - in) {
72                 *pos++ = b64enc[in[0] >> 2];
73
74                 if(end - in == 1) {
75                         *pos++ = b64enc[(in[0] & 0x03) << 4];
76                         *pos++ = '=';
77                 } else {
78                         *pos++ = b64enc[((in[0] & 0x03) << 4) | (in[1] >> 4)];
79                         *pos++ = b64enc[(in[1] & 0x0f) << 2];
80                 }
81
82                 *pos++ = '=';
83         }
84
85         *pos = '\0';
86
87         return pos - dst;
88 }
89
90
91 bool pem_encode(FILE *fp, const char *header, uint8_t *buf, size_t size) {
92         if(fprintf(fp, "-----BEGIN %s-----\n", header) <= 0) {
93                 return false;
94         }
95
96         char b64[B64_SIZE(size)];
97         const size_t b64len = b64encode(b64, buf, size);
98
99         for(char *p = b64; p < b64 + b64len; p += 64) {
100                 if(fprintf(fp, "%.64s\n", p) <= 0) {
101                         return false;
102                 }
103         }
104
105         return fprintf(fp, "-----END %s-----\n", header) > 0;
106 }
107
108 bool pem_decode(FILE *fp, const char *header, uint8_t *buf, size_t size, size_t *outsize) {
109         bool decode = false;
110         char line[1024];
111         uint16_t word = 0;
112         int shift = 10;
113         size_t i, j = 0;
114
115         while(!feof(fp)) {
116                 if(!fgets(line, sizeof(line), fp)) {
117                         return false;
118                 }
119
120                 if(!decode && !strncmp(line, "-----BEGIN ", 11)) {
121                         if(!strncmp(line + 11, header, strlen(header))) {
122                                 decode = true;
123                         }
124
125                         continue;
126                 }
127
128                 if(decode && !strncmp(line, "-----END", 8)) {
129                         break;
130                 }
131
132                 if(!decode) {
133                         continue;
134                 }
135
136                 for(i = 0; line[i] >= ' '; i++) {
137                         if((signed char)line[i] < 0 || b64dec[(int)line[i]] == 0xff) {
138                                 break;
139                         }
140
141                         word |= b64dec[(int)line[i]] << shift;
142                         shift -= 6;
143
144                         if(shift <= 2) {
145                                 if(j > size) {
146                                         errno = ENOMEM;
147                                         return false;
148                                 }
149
150                                 buf[j++] = word >> 8;
151                                 word = (uint16_t)(word << 8);
152                                 shift += 8;
153                         }
154                 }
155         }
156
157         if(outsize) {
158                 *outsize = j;
159         }
160
161         return true;
162 }