main v/snac0 / tests / b64_vectors.c
  1#define XS_IMPLEMENTATION
  2#include "../xs.h"
  3#include "../xs_bearssl.h"
  4
  5#include <stdio.h>
  6#include <stdlib.h>
  7#include <string.h>
  8
  9enum encoding_relation {
 10  // canonical indicates that the encoding is the expected encoding of the
 11  // input.
 12  canonical,
 13  // valid indicates that the encoding is /a/ valid encoding of the input, but
 14  // need not be the canonical one.
 15  valid,
 16  // invalid indicates that the encoded data is valid.
 17  invalid,
 18};
 19
 20struct Base64TestVector {
 21  enum encoding_relation relation;
 22  const char *decoded;
 23  const char *encoded;
 24};
 25
 26static const struct Base64TestVector kTestVectors[] = {
 27    // Test vectors from RFC 4648, section 10.
 28    {canonical, "", ""},
 29    {canonical, "f", "Zg==\n"},
 30    {canonical, "fo", "Zm8=\n"},
 31    {canonical, "foo", "Zm9v\n"},
 32    {canonical, "foob", "Zm9vYg==\n"},
 33    {canonical, "fooba", "Zm9vYmE=\n"},
 34    {canonical, "foobar", "Zm9vYmFy\n"},
 35
 36    {invalid, "", "Zm9vYmFy=\n"},
 37    {invalid, "", "Zm9vYmFy==\n"},
 38    {invalid, "", "Zm9vYmFy===\n"},
 39
 40    // valid non-canonical encodings due to arbitrary whitespace
 41    {valid, "foobar", "Zm9vYmFy\n\n"},
 42    {valid, "foobar", " Zm9vYmFy\n\n"},
 43    {valid, "foobar", " Z m 9 v Y m F y\n\n"},
 44    {valid, "foobar", "Zm9vYmFy\r\n"},
 45
 46    // The following "valid" encodings are arguably invalid, but they are
 47    // commonly accepted by parsers, in particular by OpenSSL.
 48    {valid, "v", "dv==\n"},
 49    {canonical, "w", "dw==\n"},
 50    {valid, "w", "dx==\n"},
 51    {valid, "w", "d+==\n"},
 52    {valid, "w", "d/==\n"},
 53    {invalid, "", "d===\n"},
 54    {canonical, "w`", "d2A=\n"},
 55    {valid, "w`", "d2B=\n"},
 56    {valid, "w`", "d2C=\n"},
 57    {valid, "w`", "d2D=\n"},
 58    {canonical, "wa", "d2E=\n"},
 59
 60    {invalid, "", "Z"},
 61    {invalid, "", "Z\n"},
 62    {invalid, "", "ab!c"},
 63    {invalid, "", "ab=c"},
 64    {invalid, "", "abc"},
 65
 66    {canonical, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 67     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA==\n"},
 68    {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 69     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA\n==\n"},
 70    {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 71     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=\n=\n"},
 72    {invalid, "",
 73     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=\n==\n"},
 74    {canonical, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 75     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh"
 76     "4eHh4eHh4\n"},
 77    {canonical,
 78     "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 79     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh"
 80     "4eHh4eHh4eHh4eA==\n"},
 81    {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 82     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh\n4eHh4eHh"
 83     "4eHh4eHh4eHh4eA==\n"},
 84    {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
 85     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e"
 86     "Hh4eHh4eHh4eA==\n"},
 87    {invalid, "",
 88     "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=="
 89     "\neHh4eHh4eHh4eHh4eHh4eHh4\n"},
 90
 91    // A '-' has traditionally been treated as the end of the data by OpenSSL
 92    // and anything following would be ignored. BoringSSL does not accept this
 93    // non-standard extension.
 94    {invalid, "", "Zm9vYmFy-anythinggoes"},
 95    {invalid, "", "Zm9vYmFy\n-anythinggoes"},
 96
 97    // CVE-2015-0292
 98    {invalid, "",
 99     "ZW5jb2RlIG1lCg==========================================================="
100     "=======\n"},
101};
102
103static int fails;
104
105// RemoveNewlines returns a copy of `in` with all '\n' characters removed.
106static char *RemoveNewlines(const char *in) {
107    size_t n = strlen(in);
108    char *out = malloc(n + 1);
109    size_t j = 0;
110
111    for (size_t i = 0; i < n; i++)
112        if (in[i] != '\n')
113            out[j++] = in[i];
114
115    out[j] = '\0';
116    return out;
117}
118
119static void EncodeBlock(const struct Base64TestVector *t) {
120    if (t->relation != canonical)
121        return;
122
123    xs_str *out = xs_base64_enc((char *)t->decoded, strlen(t->decoded));
124    char *want = RemoveNewlines(t->encoded);
125
126    if (strcmp(out, want) != 0) {
127        printf("EncodeBlock FAIL: decoded=\"%s\" got=\"%s\" want=\"%s\"\n",
128               t->decoded, out, want);
129        fails++;
130    }
131
132    xs_free(out);
133    free(want);
134}
135
136static void DecodeBase64(const struct Base64TestVector *t) {
137    char *in = RemoveNewlines(t->encoded);
138    int len = 0;
139    xs_val *out = xs_base64_dec(in, &len);
140
141    switch (t->relation) {
142    case invalid:
143        if (out != NULL) {
144            printf("DecodeBase64 FAIL: invalid input accepted: \"%s\"\n",
145                   t->encoded);
146            fails++;
147        }
148        break;
149
150    case canonical:
151    case valid:
152        if (out == NULL || len != (int)strlen(t->decoded) ||
153            memcmp(out, t->decoded, len) != 0) {
154            printf("DecodeBase64 FAIL: encoded=\"%s\" got %d bytes, want \"%s\"\n",
155                   t->encoded, len, t->decoded);
156            fails++;
157        }
158        break;
159    }
160
161    xs_free(out);
162    free(in);
163}
164
165int main(void) {
166    for (size_t i = 0; i < sizeof kTestVectors / sizeof kTestVectors[0]; i++) {
167        EncodeBlock(&kTestVectors[i]);
168        DecodeBase64(&kTestVectors[i]);
169    }
170
171    printf("%d failure(s)\n", fails);
172    return fails != 0;
173}