// Copyright 2013 Google Inc. All Rights Reserved. #include "string_conversions.h" #include #include #include #include #include #include #include "log.h" namespace { /* * Returns a 8-bit char that is mapped to the 6-bit base64 in_ch. * * Extracted from http://www.ietf.org/rfc/rfc3548.txt. * The "URL and Filename safe" Base 64 Alphabet Value Encoding Value Encoding Value Encoding Value Encoding 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 - (minus) 12 M 29 d 46 u 63 _ 13 N 30 e 47 v (underline) 14 O 31 f 48 w 15 P 32 g 49 x 16 Q 33 h 50 y (pad) = */ char B64ToBin(char in_ch) { if (in_ch >= 'A' && in_ch <= 'Z') return in_ch - 'A'; if (in_ch >= 'a' && in_ch <= 'z') return in_ch - 'a' + 26; if (in_ch >= '0' && in_ch <= '9') return in_ch - '0' + 52; if (in_ch == '-') return 62; if (in_ch == '_') return 63; // arbitrary delimiter not in Base64 encoded alphabet, do not pick 0 return '?'; } } namespace wvcdm { static bool CharToDigit(char ch, unsigned char* digit) { if (ch >= '0' && ch <= '9') { *digit = ch - '0'; } else { ch = tolower(ch); if ((ch >= 'a') && (ch <= 'f')) { *digit = ch - 'a' + 10; } else { return false; } } return true; } // converts an ascii hex string(2 bytes per digit) into a decimal byte string std::vector a2b_hex(const std::string& byte) { std::vector array; unsigned int count = byte.size(); if (count == 0 || (count % 2) != 0) { LOGE("Invalid input size %u for string %s", count, byte.c_str()); return array; } for (unsigned int i = 0; i < count / 2; ++i) { unsigned char msb = 0; // most significant 4 bits unsigned char lsb = 0; // least significant 4 bits if (!CharToDigit(byte[i * 2], &msb) || !CharToDigit(byte[i * 2 + 1], &lsb)) { LOGE("Invalid hex value %c%c at index %d", byte[i*2], byte[i*2+1], i); return array; } array.push_back((msb << 4) | lsb); } return array; } std::string a2bs_hex(const std::string& byte) { std::vector array = a2b_hex(byte); return std::string(array.begin(), array.end()); } std::string b2a_hex(const std::vector& byte) { return HexEncode(&byte[0], byte.size()); } std::string b2a_hex(const std::string& byte) { return HexEncode(reinterpret_cast(byte.data()), byte.length()); } // Filename-friendly base64 encoding (RFC4648), commonly referred as // Base64WebSafeEncode. // This is the encoding required by GooglePlay for certain // license server transactions. It is also used for logging // certain strings. // The difference between web safe encoding vs regular encoding is that // the web safe version replaces '+' with '-' and '/' with '_'. std::string Base64SafeEncode(const std::vector& bin_input) { static const char kBase64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789-_"; if (bin_input.empty()) { return std::string(); } int in_size = bin_input.size(); int final_quantum_in_bytes = in_size % 3; int full_in_chunks = in_size / 3; int out_size = full_in_chunks * 4; if (final_quantum_in_bytes) out_size += 4; std::string b64_output(out_size, '\0'); int in_index = 0; int out_index = 0; unsigned long buffer; unsigned char out_cc; static const unsigned long kInMask = 0xff; static const unsigned long kOutMask = 0x3f; for (int i = 0; i < full_in_chunks; ++i) { // up to 3 bytes (0..255) in buffer = (bin_input.at(in_index) & kInMask); buffer <<= 8; buffer |= (++in_index >= in_size) ? 0 : (bin_input.at(in_index) & kInMask); buffer <<= 8; buffer |= (++in_index >= in_size) ? 0 : (bin_input.at(in_index) & kInMask); ++in_index; // up to 4 bytes (0..63) out out_cc = (buffer >> 18) & kOutMask; b64_output.at(out_index) = kBase64Chars[out_cc]; if (++out_index >= out_size) break; out_cc = (buffer >> 12) & kOutMask; b64_output.at(out_index) = kBase64Chars[out_cc]; if (++out_index >= out_size) break; out_cc = (buffer >> 6) & kOutMask; b64_output.at(out_index) = kBase64Chars[out_cc]; if (++out_index >= out_size) break; out_cc = buffer & kOutMask; b64_output.at(out_index) = kBase64Chars[out_cc]; ++out_index; } if (final_quantum_in_bytes) { switch(final_quantum_in_bytes) { case 1: { // reads 24-bits data, which is made up of one 8-bits char buffer = (bin_input.at(in_index++) & kInMask); buffer <<= 16; // writes two 6-bits chars followed by two '=' padding char out_cc = (buffer >> 18) & kOutMask; b64_output.at(out_index++) = kBase64Chars[out_cc]; out_cc = (buffer >> 12) & kOutMask; b64_output.at(out_index++) = kBase64Chars[out_cc]; b64_output.at(out_index++) = '='; b64_output.at(out_index) = '='; break; } case 2: { // reads 24-bits data, which is made up of two 8-bits chars buffer = (bin_input.at(in_index++) & kInMask); buffer <<= 8; buffer |= (bin_input.at(in_index++) & kInMask); buffer <<= 8; // writes three 6-bits chars followed by one '=' padding char out_cc = (buffer >> 18) & kOutMask; b64_output.at(out_index++) = kBase64Chars[out_cc]; out_cc = (buffer >> 12) & kOutMask; b64_output.at(out_index++) = kBase64Chars[out_cc]; out_cc = (buffer >> 6) & kOutMask; b64_output.at(out_index++) = kBase64Chars[out_cc]; b64_output.at(out_index) = '='; break; } default: break; } } return b64_output; } // Decode for Filename-friendly base64 encoding (RFC4648), commonly referred // as Base64WebSafeDecode. // This is the encoding required by GooglePlay for certain // license server transactions. It is also used for logging // certain strings. std::vector Base64SafeDecode(const std::string& b64_input) { if (b64_input.empty()) { return std::vector(); } int in_size = b64_input.size(); int out_size = in_size; std::vector bin_output(out_size, '\0'); int in_index = 0; int out_index = 0; unsigned long buffer; unsigned char out_cc; static const unsigned long kOutMask = 0xff; int counter = 0; size_t delimiter_pos = b64_input.rfind('='); if (delimiter_pos != std::string::npos) { // Special case for partial last quantum indicated by '=' // at the end of encoded input. counter = 1; } for (; counter < (in_size / 4); ++counter) { // up to 4 bytes (0..63) in buffer = B64ToBin(b64_input.at(in_index)); buffer <<= 6; buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index)); buffer <<= 6; buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index)); buffer <<= 6; buffer |= (++in_index >= in_size) ? 0 : B64ToBin(b64_input.at(in_index)); ++in_index; // up to 3 bytes (0..255) out out_cc = (buffer >> 16) & kOutMask; bin_output.at(out_index) = out_cc; if (++out_index >= out_size) break; out_cc = (buffer >> 8) & kOutMask; bin_output.at(out_index) = out_cc; if (++out_index >= out_size) break; out_cc = buffer & kOutMask; bin_output.at(out_index) = out_cc; ++out_index; } if (delimiter_pos != std::string::npos) { // it is either 2 chars plus 2 '=' or 3 chars plus one '=' buffer = B64ToBin(b64_input.at(in_index++)); buffer <<= 6; buffer |= B64ToBin(b64_input.at(in_index++)); buffer <<= 6; char special_char = b64_input.at(in_index++); if ('=' == special_char) { // we have 2 chars and 2 '=' buffer <<= 6; out_cc = (buffer >> 16) & kOutMask; bin_output.at(out_index++) = out_cc; out_cc = (buffer >> 8) & kOutMask; bin_output.at(out_index) = out_cc; } else { // we have 3 chars and 1 '=' buffer |= B64ToBin(special_char); buffer <<= 6; buffer |= B64ToBin(b64_input.at(in_index)); out_cc = (buffer >> 16) & kOutMask; bin_output.at(out_index++) = out_cc; out_cc = (buffer >> 8) & kOutMask; bin_output.at(out_index++) = out_cc; out_cc = buffer & kOutMask; bin_output.at(out_index) = out_cc; } } // adjust vector to reflect true size bin_output.resize(out_index); return bin_output; } std::string HexEncode(const uint8_t* in_buffer, unsigned int size) { static const char kHexChars[] = "0123456789ABCDEF"; // Each input byte creates two output hex characters. std::string out_buffer(size * 2, '\0'); for (unsigned int i = 0; i < size; ++i) { char byte = in_buffer[i]; out_buffer[(i << 1)] = kHexChars[(byte >> 4) & 0xf]; out_buffer[(i << 1) + 1] = kHexChars[byte & 0xf]; } return out_buffer; } std::string IntToString(int value) { // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. // So round up to allocate 3 output characters per byte, plus 1 for '-'. const int kOutputBufSize = 3 * sizeof(int) + 1; char buffer[kOutputBufSize]; memset(buffer, 0, kOutputBufSize); snprintf(buffer, kOutputBufSize, "%d", value); std::string out_string(buffer, sizeof(buffer)); return out_string; } std::string UintToString(unsigned int value) { // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. // So round up to allocate 3 output characters per byte. const int kOutputBufSize = 3 * sizeof(unsigned int); char buffer[kOutputBufSize]; memset(buffer, 0, kOutputBufSize); snprintf(buffer, kOutputBufSize, "%u", value); std::string out_string(buffer, sizeof(buffer)); return out_string; } }; // namespace wvcdm