File size: 4,450 Bytes
78d2150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include "../../unity/unity.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/* setUp and tearDown required by Unity */
void setUp(void) {
  /* No global setup needed */
}
void tearDown(void) {
  /* No global teardown needed */
}

/* Helper: compare encoded output to expected C string literal */
static void assert_encoded_equals_literal(const char *input, size_t inlen, const char *expected_literal)
{
  idx_t outlen = BASE64_LENGTH((idx_t)inlen);
  size_t expected_len = strlen(expected_literal);
  TEST_ASSERT_EQUAL_size_t(expected_len, (size_t)outlen);

  char *out = (char *)malloc(outlen ? outlen : 1);
  TEST_ASSERT_NOT_NULL(out);

  base64url_encode(input, (idx_t)inlen, out, outlen);

  TEST_ASSERT_EQUAL_MEMORY(expected_literal, out, outlen);

  free(out);
}

/* Test 1: Empty input should produce empty output and not write anything */
static void test_base64url_encode_empty(void)
{
  const char *in = "";
  idx_t outlen = BASE64_LENGTH((idx_t)0);
  TEST_ASSERT_EQUAL_INT(0, (int)outlen);

  char sentinel = (char)0xA5;
  char *out = &sentinel;

  base64url_encode(in, 0, out, outlen);

  /* Since outlen==0, output buffer must remain untouched */
  TEST_ASSERT_EQUAL_HEX8(0xA5, (unsigned char)sentinel);
}

/* Test 2: RFC 4648 basic vectors (no URL-specific changes needed) */
static void test_base64url_encode_rfc4648_vectors(void)
{
  assert_encoded_equals_literal("f",    1, "Zg==");
  assert_encoded_equals_literal("fo",   2, "Zm8=");
  assert_encoded_equals_literal("foo",  3, "Zm9v");
  assert_encoded_equals_literal("foob", 4, "Zm9vYg==");
  assert_encoded_equals_literal("fooba",5, "Zm9vYmE=");
  assert_encoded_equals_literal("foobar",6,"Zm9vYmFy");
}

/* Test 3: Verify translation of '+' -> '-' and '/' -> '_' */
static void test_base64url_encode_translates_plus_and_slash(void)
{
  /* Crafted 3-byte input producing "+/+A" in standard base64:
     0xFB, 0xFF, 0x80
     g0=62('+'), g1=63('/'), g2=62('+'), g3=0('A') */
  unsigned char in[3] = { 0xFB, 0xFF, 0x80 };
  idx_t outlen = BASE64_LENGTH((idx_t)sizeof(in));
  TEST_ASSERT_EQUAL_INT(4, (int)outlen);

  char *out = (char *)malloc(outlen);
  TEST_ASSERT_NOT_NULL(out);

  base64url_encode((const char *)in, (idx_t)sizeof(in), out, outlen);

  const char expected[4] = { '-', '_', '-', 'A' };
  TEST_ASSERT_EQUAL_MEMORY(expected, out, 4);

  free(out);
}

/* Test 4: Repeated pattern to ensure replacement across the entire buffer */
static void test_base64url_encode_repeated_pattern(void)
{
  /* Repeat the 3-byte pattern {0xFB,0xFF,0x80} 10 times */
  enum { REPS = 10 };
  unsigned char in[3 * REPS];
  for (int i = 0; i < REPS; i++) {
    in[3*i + 0] = 0xFB;
    in[3*i + 1] = 0xFF;
    in[3*i + 2] = 0x80;
  }

  idx_t outlen = BASE64_LENGTH((idx_t)sizeof(in));
  TEST_ASSERT_EQUAL_INT(4 * REPS, (int)outlen);

  char *out = (char *)malloc(outlen);
  TEST_ASSERT_NOT_NULL(out);

  base64url_encode((const char *)in, (idx_t)sizeof(in), out, outlen);

  /* Expected is "-_-A" repeated REPS times */
  char expected[4 * REPS];
  for (int i = 0; i < REPS; i++) {
    expected[4*i + 0] = '-';
    expected[4*i + 1] = '_';
    expected[4*i + 2] = '-';
    expected[4*i + 3] = 'A';
  }

  TEST_ASSERT_EQUAL_MEMORY(expected, out, outlen);

  free(out);
}

/* Test 5: For a broader input range, compare to base64 + manual URL translation */
static void test_base64url_encode_matches_base64_then_translate(void)
{
  /* Use a variety of byte values */
  enum { LEN = 256 };
  unsigned char in[LEN];
  for (int i = 0; i < LEN; i++)
    in[i] = (unsigned char)i;

  idx_t outlen = BASE64_LENGTH((idx_t)LEN);

  char *got = (char *)malloc(outlen);
  char *ref = (char *)malloc(outlen);
  TEST_ASSERT_NOT_NULL(got);
  TEST_ASSERT_NOT_NULL(ref);

  /* Compute reference: base64 then translate '+'->'-', '/'->'_' */
  base64_encode((const char *)in, (idx_t)LEN, ref, outlen);
  for (idx_t i = 0; i < outlen; i++) {
    if (ref[i] == '+') ref[i] = '-';
    else if (ref[i] == '/') ref[i] = '_';
  }

  base64url_encode((const char *)in, (idx_t)LEN, got, outlen);

  TEST_ASSERT_EQUAL_MEMORY(ref, got, outlen);

  free(got);
  free(ref);
}

int main(void)
{
  UNITY_BEGIN();
  RUN_TEST(test_base64url_encode_empty);
  RUN_TEST(test_base64url_encode_rfc4648_vectors);
  RUN_TEST(test_base64url_encode_translates_plus_and_slash);
  RUN_TEST(test_base64url_encode_repeated_pattern);
  RUN_TEST(test_base64url_encode_matches_base64_then_translate);
  return UNITY_END();
}