Coverage Report

Created: 2025-10-12 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cryptsetup/lib/crypto_backend/base64.c
Line
Count
Source
1
// SPDX-License-Identifier: LGPL-2.1-or-later
2
/*
3
 * Base64 "Not encryption" helpers, copied and adapted from systemd project.
4
 *
5
 * Copyright (C) 2010 Lennart Poettering
6
 *
7
 * cryptsetup related changes
8
 * Copyright (C) 2021-2025 Milan Broz
9
 */
10
11
#include <errno.h>
12
#include <stdlib.h>
13
#include <limits.h>
14
15
#include "crypto_backend.h"
16
17
0
#define WHITESPACE " \t\n\r"
18
19
/* https://tools.ietf.org/html/rfc4648#section-4 */
20
static char base64char(int x)
21
0
{
22
0
  static const char table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
23
0
              "abcdefghijklmnopqrstuvwxyz"
24
0
              "0123456789+/";
25
0
  return table[x & 63];
26
0
}
27
28
static int unbase64char(char c)
29
0
{
30
0
  unsigned offset;
31
32
0
  if (c >= 'A' && c <= 'Z')
33
0
    return c - 'A';
34
35
0
  offset = 'Z' - 'A' + 1;
36
37
0
  if (c >= 'a' && c <= 'z')
38
0
    return c - 'a' + offset;
39
40
0
  offset += 'z' - 'a' + 1;
41
42
0
  if (c >= '0' && c <= '9')
43
0
    return c - '0' + offset;
44
45
0
  offset += '9' - '0' + 1;
46
47
0
  if (c == '+')
48
0
    return offset;
49
50
0
  offset++;
51
52
0
  if (c == '/')
53
0
    return offset;
54
55
0
  return -EINVAL;
56
0
}
57
58
int crypt_base64_encode(char **out, size_t *out_length, const char *in, size_t in_length)
59
0
{
60
0
  char *r, *z;
61
0
  const uint8_t *x;
62
63
0
  assert(in || in_length == 0);
64
0
  assert(out);
65
66
  /* three input bytes makes four output bytes, padding is added so we must round up */
67
0
  z = r = malloc(4 * (in_length + 2) / 3 + 1);
68
0
  if (!r)
69
0
    return -ENOMEM;
70
71
0
  for (x = (const uint8_t *)in; x < (const uint8_t*)in + (in_length / 3) * 3; x += 3) {
72
    /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
73
0
    *(z++) = base64char(x[0] >> 2);                    /* 00XXXXXX */
74
0
    *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4);  /* 00XXYYYY */
75
0
    *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
76
0
    *(z++) = base64char(x[2] & 63);                    /* 00ZZZZZZ */
77
0
  }
78
79
0
  switch (in_length % 3) {
80
0
  case 2:
81
0
    *(z++) = base64char(x[0] >> 2);                   /* 00XXXXXX */
82
0
    *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
83
0
    *(z++) = base64char((x[1] & 15) << 2);            /* 00YYYY00 */
84
0
    *(z++) = '=';
85
86
0
    break;
87
0
  case 1:
88
0
    *(z++) = base64char(x[0] >> 2);        /* 00XXXXXX */
89
0
    *(z++) = base64char((x[0] & 3) << 4);  /* 00XX0000 */
90
0
    *(z++) = '=';
91
0
    *(z++) = '=';
92
93
0
    break;
94
0
  }
95
96
0
  *z = 0;
97
0
  *out = r;
98
0
  if (out_length)
99
0
    *out_length = z - r;
100
0
  return 0;
101
0
}
102
103
static int unbase64_next(const char **p, size_t *l)
104
0
{
105
0
  int ret;
106
107
0
  assert(p);
108
0
  assert(l);
109
110
  /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We
111
   * greedily skip all preceding and all following whitespace. */
112
113
0
  for (;;) {
114
0
    if (*l == 0)
115
0
      return -EPIPE;
116
117
0
    if (!strchr(WHITESPACE, **p))
118
0
      break;
119
120
    /* Skip leading whitespace */
121
0
    (*p)++, (*l)--;
122
0
  }
123
124
0
  if (**p == '=')
125
0
    ret = INT_MAX; /* return padding as INT_MAX */
126
0
  else {
127
0
    ret = unbase64char(**p);
128
0
    if (ret < 0)
129
0
      return ret;
130
0
  }
131
132
0
  for (;;) {
133
0
    (*p)++, (*l)--;
134
135
0
    if (*l == 0)
136
0
      break;
137
0
    if (!strchr(WHITESPACE, **p))
138
0
      break;
139
140
    /* Skip following whitespace */
141
0
  }
142
143
0
  return ret;
144
0
}
145
146
int crypt_base64_decode(char **out, size_t *out_length, const char *in, size_t in_length)
147
0
{
148
0
  uint8_t *buf = NULL;
149
0
  const char *x;
150
0
  uint8_t *z;
151
0
  size_t len;
152
0
  int r;
153
154
0
  assert(in || in_length == 0);
155
0
  assert(out);
156
0
  assert(out_length);
157
158
0
  if (in_length == (size_t) -1)
159
0
    in_length = strlen(in);
160
161
  /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra
162
   * bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */
163
0
  len = (in_length / 4) * 3 + (in_length % 4 != 0 ? (in_length % 4) - 1 : 0);
164
165
0
  buf = malloc(len + 1);
166
0
  if (!buf)
167
0
    return -ENOMEM;
168
169
0
  for (x = in, z = buf;;) {
170
0
    int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
171
172
0
    a = unbase64_next(&x, &in_length);
173
0
    if (a == -EPIPE) /* End of string */
174
0
      break;
175
0
    if (a < 0) {
176
0
      r = a;
177
0
      goto err;
178
0
    }
179
0
    if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */
180
0
      r = -EINVAL;
181
0
      goto err;
182
0
    }
183
184
0
    b = unbase64_next(&x, &in_length);
185
0
    if (b < 0) {
186
0
      r = b;
187
0
      goto err;
188
0
    }
189
0
    if (b == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */
190
0
      r = -EINVAL;
191
0
      goto err;
192
0
    }
193
194
0
    c = unbase64_next(&x, &in_length);
195
0
    if (c < 0) {
196
0
      r = c;
197
0
      goto err;
198
0
    }
199
200
0
    d = unbase64_next(&x, &in_length);
201
0
    if (d < 0) {
202
0
      r = d;
203
0
      goto err;
204
0
    }
205
206
0
    if (c == INT_MAX) { /* Padding at the third character */
207
208
0
      if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */
209
0
        r = -EINVAL;
210
0
        goto err;
211
0
      }
212
213
      /* b == 00YY0000 */
214
0
      if (b & 15) {
215
0
        r = -EINVAL;
216
0
        goto err;
217
0
      }
218
219
0
      if (in_length > 0) { /* Trailing rubbish? */
220
0
        r = -ENAMETOOLONG;
221
0
        goto err;
222
0
      }
223
224
0
      *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
225
0
      break;
226
0
    }
227
228
0
    if (d == INT_MAX) {
229
      /* c == 00ZZZZ00 */
230
0
      if (c & 3) {
231
0
        r = -EINVAL;
232
0
        goto err;
233
0
      }
234
235
0
      if (in_length > 0) { /* Trailing rubbish? */
236
0
        r = -ENAMETOOLONG;
237
0
        goto err;
238
0
      }
239
240
0
      *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
241
0
      *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
242
0
      break;
243
0
    }
244
245
0
    *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
246
0
    *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
247
0
    *(z++) = (uint8_t) c << 6 | (uint8_t) d;      /* ZZWWWWWW */
248
0
  }
249
250
0
  *z = 0;
251
252
0
  *out_length = (size_t) (z - buf);
253
0
  *out = (char *)buf;
254
0
  return 0;
255
0
err:
256
0
  free(buf);
257
258
  /* Ignore other errors in crypt_backend */
259
0
  if (r != -ENOMEM)
260
0
    r = -EINVAL;
261
262
0
  return r;
263
0
}