blob: 834b88ff705024810fca87a3ca833062afd62c4b [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "base64.h"
#include "ABuffer.h"
#include "ADebug.h"
namespace android {
sp<ABuffer> decodeBase64(const AString &s) {
size_t n = s.size();
if ((n % 4) != 0) {
return NULL;
}
size_t bufSize = n / 4 * 3;
sp<ABuffer> buf = new ABuffer(bufSize);
if (decodeBase64(buf->data(), &bufSize, s.c_str())) {
buf->setRange(0, bufSize);
return buf;
}
return NULL;
}
bool decodeBase64(uint8_t *out, size_t *inOutBufSize, const char* s) {
size_t n = strlen(s);
if ((n % 4) != 0) {
return false;
}
size_t padding = 0;
if (n >= 1 && s[n - 1] == '=') {
padding = 1;
if (n >= 2 && s[n - 2] == '=') {
padding = 2;
if (n >= 3 && s[n - 3] == '=') {
padding = 3;
}
}
}
// We divide first to avoid overflow. It's OK to do this because we
// already made sure that n % 4 == 0.
size_t outLen = (n / 4) * 3 - padding;
if (out == NULL || *inOutBufSize < outLen) {
return false;
}
size_t j = 0;
uint32_t accum = 0;
for (size_t i = 0; i < n; ++i) {
char c = s[i];
unsigned value;
if (c >= 'A' && c <= 'Z') {
value = c - 'A';
} else if (c >= 'a' && c <= 'z') {
value = 26 + c - 'a';
} else if (c >= '0' && c <= '9') {
value = 52 + c - '0';
} else if (c == '+' || c == '-') {
value = 62;
} else if (c == '/' || c == '_') {
value = 63;
} else if (c != '=') {
return false;
} else {
if (i < n - padding) {
return false;
}
value = 0;
}
accum = (accum << 6) | value;
if (((i + 1) % 4) == 0) {
if (j < outLen) { out[j++] = (accum >> 16); }
if (j < outLen) { out[j++] = (accum >> 8) & 0xff; }
if (j < outLen) { out[j++] = accum & 0xff; }
accum = 0;
}
}
*inOutBufSize = j;
return true;
}
static char encode6Bit(unsigned x) {
if (x <= 25) {
return 'A' + x;
} else if (x <= 51) {
return 'a' + x - 26;
} else if (x <= 61) {
return '0' + x - 52;
} else if (x == 62) {
return '+';
} else {
return '/';
}
}
void encodeBase64(
const void *_data, size_t size, AString *out) {
out->clear();
const uint8_t *data = (const uint8_t *)_data;
size_t i;
for (i = 0; i < (size / 3) * 3; i += 3) {
uint8_t x1 = data[i];
uint8_t x2 = data[i + 1];
uint8_t x3 = data[i + 2];
out->append(encode6Bit(x1 >> 2));
out->append(encode6Bit((x1 << 4 | x2 >> 4) & 0x3f));
out->append(encode6Bit((x2 << 2 | x3 >> 6) & 0x3f));
out->append(encode6Bit(x3 & 0x3f));
}
switch (size % 3) {
case 0:
break;
case 2:
{
uint8_t x1 = data[i];
uint8_t x2 = data[i + 1];
out->append(encode6Bit(x1 >> 2));
out->append(encode6Bit((x1 << 4 | x2 >> 4) & 0x3f));
out->append(encode6Bit((x2 << 2) & 0x3f));
out->append('=');
break;
}
default:
{
uint8_t x1 = data[i];
out->append(encode6Bit(x1 >> 2));
out->append(encode6Bit((x1 << 4) & 0x3f));
out->append("==");
break;
}
}
}
void encodeBase64Url(
const void *_data, size_t size, AString *out) {
encodeBase64(_data, size, out);
if ((-1 != out->find("+")) || (-1 != out->find("/"))) {
size_t outLen = out->size();
char *base64url = new char[outLen];
for (size_t i = 0; i < outLen; ++i) {
if (out->c_str()[i] == '+')
base64url[i] = '-';
else if (out->c_str()[i] == '/')
base64url[i] = '_';
else
base64url[i] = out->c_str()[i];
}
out->setTo(base64url, outLen);
delete[] base64url;
}
}
} // namespace android