1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #define LOG_TAG "clearkey-Base64"
17
18 #include "Base64.h"
19
20 #include <string>
21
22 namespace clearkeydrm {
23
decodeBase64(const std::string & s)24 ::android::sp<Buffer> decodeBase64(const std::string& s) {
25 size_t n = s.size();
26
27 if ((n % 4) != 0) {
28 return nullptr;
29 }
30
31 size_t padding = 0;
32 if (n >= 1 && s.c_str()[n - 1] == '=') {
33 padding = 1;
34
35 if (n >= 2 && s.c_str()[n - 2] == '=') {
36 padding = 2;
37
38 if (n >= 3 && s.c_str()[n - 3] == '=') {
39 padding = 3;
40 }
41 }
42 }
43
44 // We divide first to avoid overflow. It's OK to do this because we
45 // already made sure that n % 4 == 0.
46 size_t outLen = (n / 4) * 3 - padding;
47
48 ::android::sp<Buffer> buffer = new Buffer(outLen);
49 uint8_t* out = buffer->data();
50 if (out == nullptr || buffer->size() < outLen) {
51 return nullptr;
52 }
53
54 size_t j = 0;
55 uint32_t accum = 0;
56 for (size_t i = 0; i < n; ++i) {
57 char c = s.c_str()[i];
58 unsigned value;
59 if (c >= 'A' && c <= 'Z') {
60 value = c - 'A';
61 } else if (c >= 'a' && c <= 'z') {
62 value = 26 + c - 'a';
63 } else if (c >= '0' && c <= '9') {
64 value = 52 + c - '0';
65 } else if (c == '+' || c == '-') {
66 value = 62;
67 } else if (c == '/' || c == '_') {
68 value = 63;
69 } else if (c != '=') {
70 return nullptr;
71 } else {
72 if (i < n - padding) {
73 return nullptr;
74 }
75
76 value = 0;
77 }
78
79 accum = (accum << 6) | value;
80
81 if (((i + 1) % 4) == 0) {
82 if (j < outLen) {
83 out[j++] = (accum >> 16);
84 }
85 if (j < outLen) {
86 out[j++] = (accum >> 8) & 0xff;
87 }
88 if (j < outLen) {
89 out[j++] = accum & 0xff;
90 }
91
92 accum = 0;
93 }
94 }
95
96 return buffer;
97 }
98
encode6Bit(unsigned x)99 static char encode6Bit(unsigned x) {
100 if (x <= 25) {
101 return 'A' + x;
102 } else if (x <= 51) {
103 return 'a' + x - 26;
104 } else if (x <= 61) {
105 return '0' + x - 52;
106 } else if (x == 62) {
107 return '+';
108 } else {
109 return '/';
110 }
111 }
112
encodeBase64(const void * _data,size_t size,std::string * out)113 void encodeBase64(const void* _data, size_t size, std::string* out) {
114 out->clear();
115
116 const uint8_t* data = (const uint8_t*)_data;
117
118 size_t i;
119 for (i = 0; i < (size / 3) * 3; i += 3) {
120 uint8_t x1 = data[i];
121 uint8_t x2 = data[i + 1];
122 uint8_t x3 = data[i + 2];
123
124 out->push_back(encode6Bit(x1 >> 2));
125 out->push_back(encode6Bit((x1 << 4 | x2 >> 4) & 0x3f));
126 out->push_back(encode6Bit((x2 << 2 | x3 >> 6) & 0x3f));
127 out->push_back(encode6Bit(x3 & 0x3f));
128 }
129 switch (size % 3) {
130 case 0:
131 break;
132 case 2: {
133 uint8_t x1 = data[i];
134 uint8_t x2 = data[i + 1];
135 out->push_back(encode6Bit(x1 >> 2));
136 out->push_back(encode6Bit((x1 << 4 | x2 >> 4) & 0x3f));
137 out->push_back(encode6Bit((x2 << 2) & 0x3f));
138 out->push_back('=');
139 break;
140 }
141 default: {
142 uint8_t x1 = data[i];
143 out->push_back(encode6Bit(x1 >> 2));
144 out->push_back(encode6Bit((x1 << 4) & 0x3f));
145 out->append("==");
146 break;
147 }
148 }
149 }
150
encodeBase64Url(const void * _data,size_t size,std::string * out)151 void encodeBase64Url(const void* _data, size_t size, std::string* out) {
152 encodeBase64(_data, size, out);
153
154 if ((std::string::npos != out->find("+")) || (std::string::npos != out->find("/"))) {
155 size_t outLen = out->size();
156 char* base64url = new char[outLen];
157 for (size_t i = 0; i < outLen; ++i) {
158 if (out->c_str()[i] == '+')
159 base64url[i] = '-';
160 else if (out->c_str()[i] == '/')
161 base64url[i] = '_';
162 else
163 base64url[i] = out->c_str()[i];
164 }
165
166 out->assign(base64url, outLen);
167 delete[] base64url;
168 }
169 }
170
171 } // namespace clearkeydrm
172