b22d0e4041bb917e30152783c1aa9cedf3137afb
[tinc] / src / gcrypt / pem.c
1 #include "pem.h"
2 #include "utils.h"
3
4 // Base64 decoding table
5
6 static const uint8_t b64dec[128] = {
7         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
8         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
9         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
10         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
11         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
12         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
13         0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
14         0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
15         0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
16         0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
17         0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
18         0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
19         0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
20         0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
21         0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
22         0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
23         0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
24         0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
25         0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
26         0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
27         0x31, 0x32, 0x33, 0xff, 0xff, 0xff,
28         0xff, 0xff
29 };
30
31 static const char b64enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
32                              "abcdefghijklmnopqrstuvwxyz"
33                              "0123456789+/";
34
35 // Heavily based on code by Jouni Malinen <j@w1.fi>
36 // https://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c
37 static size_t b64encode(char *dst, const void *src, const size_t length) {
38         const uint8_t *end = src + length;
39         const uint8_t *in = src;
40         char *pos = dst;
41
42         while(end - in >= 3) {
43                 *pos++ = b64enc[in[0] >> 2];
44                 *pos++ = b64enc[((in[0] & 0x03) << 4) | (in[1] >> 4)];
45                 *pos++ = b64enc[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
46                 *pos++ = b64enc[in[2] & 0x3f];
47                 in += 3;
48         }
49
50         if(end - in) {
51                 *pos++ = b64enc[in[0] >> 2];
52
53                 if(end - in == 1) {
54                         *pos++ = b64enc[(in[0] & 0x03) << 4];
55                         *pos++ = '=';
56                 } else {
57                         *pos++ = b64enc[((in[0] & 0x03) << 4) | (in[1] >> 4)];
58                         *pos++ = b64enc[(in[1] & 0x0f) << 2];
59                 }
60
61                 *pos++ = '=';
62         }
63
64         *pos = '\0';
65
66         return pos - dst;
67 }
68
69
70 bool pem_encode(FILE *fp, const char *header, uint8_t *buf, size_t size) {
71         if(fprintf(fp, "-----BEGIN %s-----\n", header) <= 0) {
72                 return false;
73         }
74
75         char b64[B64_SIZE(size)];
76         const size_t b64len = b64encode(b64, buf, size);
77
78         for(char *p = b64; p < b64 + b64len; p += 64) {
79                 if(fprintf(fp, "%.64s\n", p) <= 0) {
80                         return false;
81                 }
82         }
83
84         return fprintf(fp, "-----END %s-----\n", header) > 0;
85 }
86
87 bool pem_decode(FILE *fp, const char *header, uint8_t *buf, size_t size, size_t *outsize) {
88         bool decode = false;
89         char line[1024];
90         uint16_t word = 0;
91         int shift = 10;
92         size_t i, j = 0;
93
94         while(!feof(fp)) {
95                 if(!fgets(line, sizeof(line), fp)) {
96                         return false;
97                 }
98
99                 if(!decode && !strncmp(line, "-----BEGIN ", 11)) {
100                         if(!strncmp(line + 11, header, strlen(header))) {
101                                 decode = true;
102                         }
103
104                         continue;
105                 }
106
107                 if(decode && !strncmp(line, "-----END", 8)) {
108                         break;
109                 }
110
111                 if(!decode) {
112                         continue;
113                 }
114
115                 for(i = 0; line[i] >= ' '; i++) {
116                         if((signed char)line[i] < 0 || b64dec[(int)line[i]] == 0xff) {
117                                 break;
118                         }
119
120                         word |= b64dec[(int)line[i]] << shift;
121                         shift -= 6;
122
123                         if(shift <= 2) {
124                                 if(j > size) {
125                                         errno = ENOMEM;
126                                         return false;
127                                 }
128
129                                 buf[j++] = word >> 8;
130                                 word = (uint16_t)(word << 8);
131                                 shift += 8;
132                         }
133                 }
134         }
135
136         if(outsize) {
137                 *outsize = j;
138         }
139
140         return true;
141 }