/* * 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 decodeBase64(const AString &s) { size_t n = s.size(); if ((n % 4) != 0) { return NULL; } size_t bufSize = n / 4 * 3; sp 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