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