// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine License // Agreement. #include "string_conversions.h" #include #include #include #include #include #include "log.h" #include "platform.h" namespace wvutil { namespace { // Base64 character set, indexed for their 6-bit mapping, plus '='. const char kBase64Codes[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // URL safe Base64 character set. const char kBase64SafeCodes[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_="; // Gets the low |n| bits of |in|. #define GET_LOW_BITS(in, n) ((in) & ((1 << (n)) - 1)) // Gets the given (zero-indexed) bits [a, b) of |in|. #define GET_BITS(in, a, b) GET_LOW_BITS((in) >> (a), (b) - (a)) // Calculates a/b using round-up division (only works for positive numbers). #define CEIL_DIVIDE(a, b) ((((a)-1) / (b)) + 1) // Decodes a single Base64 encoded character into its 6-bit value. // The provided |codes| must be a Base64 character map. int DecodeBase64Char(char c, const char* codes) { const char* c_in_codes = strchr(codes, c); if (c_in_codes == nullptr) return -1; const uintptr_t c_in_codes_int = reinterpret_cast(c_in_codes); const uintptr_t codes_int = reinterpret_cast(codes); return static_cast(c_in_codes_int - codes_int); } bool DecodeHexChar(char ch, uint8_t* digit) { if (ch >= '0' && ch <= '9') { *digit = ch - '0'; return true; } ch = tolower(ch); if ((ch >= 'a') && (ch <= 'f')) { *digit = ch - 'a' + 10; return true; } return false; } // Encode for standard base64 encoding (RFC4648). // https://en.wikipedia.org/wiki/Base64 // Text | M | a | n | // ASCI | 77 (0x4d) | 97 (0x61) | 110 (0x6e) | // Bits | 0 1 0 0 1 1 0 1 0 1 1 0 0 0 0 1 0 1 1 0 1 1 1 0 | // Index | 19 | 22 | 5 | 46 | // Base64 | T | W | F | u | // | <----------------- 24-bits -----------------> | // The provided |codes| must be a Base64 character map. std::string Base64EncodeInternal(const uint8_t* data, size_t length, const char* codes) { // |temp| stores a 24-bit block that is treated as an array where insertions // occur from high to low. uint32_t temp = 0; size_t out_index = 0; const size_t out_size = CEIL_DIVIDE(length, 3) * 4; std::string result(out_size, '\0'); for (size_t i = 0; i < length; i++) { // "insert" 8-bits of data temp |= (data[i] << ((2 - (i % 3)) * 8)); if (i % 3 == 2) { result[out_index++] = codes[GET_BITS(temp, 18, 24)]; result[out_index++] = codes[GET_BITS(temp, 12, 18)]; result[out_index++] = codes[GET_BITS(temp, 6, 12)]; result[out_index++] = codes[GET_BITS(temp, 0, 6)]; temp = 0; } } if (length % 3 == 1) { result[out_index++] = codes[GET_BITS(temp, 18, 24)]; result[out_index++] = codes[GET_BITS(temp, 12, 18)]; result[out_index++] = '='; result[out_index++] = '='; } else if (length % 3 == 2) { result[out_index++] = codes[GET_BITS(temp, 18, 24)]; result[out_index++] = codes[GET_BITS(temp, 12, 18)]; result[out_index++] = codes[GET_BITS(temp, 6, 12)]; result[out_index++] = '='; } return result; } std::vector Base64DecodeInternal(const char* encoded, size_t length, const char* codes) { const size_t out_size_max = CEIL_DIVIDE(length * 3, 4); std::vector result(out_size_max, '\0'); // |temp| stores 24-bits of data that is treated as an array where insertions // occur from high to low. uint32_t temp = 0; size_t out_index = 0; size_t i; for (i = 0; i < length; i++) { if (encoded[i] == '=') { // Verify an '=' only appears at the end. We want i to remain at the // first '=', so we need an inner loop. for (size_t j = i; j < length; j++) { if (encoded[j] != '=') { LOGE("base64Decode failed"); return std::vector(); } } if (length % 4 != 0) { // If padded, then the length must be a multiple of 4. // Unpadded messages are OK. LOGE("base64Decode failed"); return std::vector(); } break; } const int decoded = DecodeBase64Char(encoded[i], codes); if (decoded < 0) { LOGE("base64Decode failed"); return std::vector(); } // "insert" 6-bits of data temp |= (decoded << ((3 - (i % 4)) * 6)); if (i % 4 == 3) { result[out_index++] = GET_BITS(temp, 16, 24); result[out_index++] = GET_BITS(temp, 8, 16); result[out_index++] = GET_BITS(temp, 0, 8); temp = 0; } } switch (i % 4) { case 1: LOGE("base64Decode failed"); return std::vector(); case 2: result[out_index++] = GET_BITS(temp, 16, 24); break; case 3: result[out_index++] = GET_BITS(temp, 16, 24); result[out_index++] = GET_BITS(temp, 8, 16); break; } result.resize(out_index); return result; } } // namespace // 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; size_t count = byte.size(); if (count == 0 || (count % 2) != 0) { LOGE("Invalid input size %zu for string %s", count, byte.c_str()); return array; } for (size_t i = 0; i < count / 2; ++i) { unsigned char msb = 0; // most significant 4 bits unsigned char lsb = 0; // least significant 4 bits if (!DecodeHexChar(byte[i * 2], &msb) || !DecodeHexChar(byte[i * 2 + 1], &lsb)) { LOGE("Invalid hex value %c%c at index %zu", byte[i * 2], byte[i * 2 + 1], i); return array; } array.push_back((msb << 4) | lsb); } return array; } // converts an ascii hex string(2 bytes per digit) into a decimal byte string // dump the string with the label. std::vector a2b_hex(const std::string& label, const std::string& byte) { std::cout << std::endl << "[[DUMP: " << label << " ]= \"" << byte << "\"]" << std::endl << std::endl; return a2b_hex(byte); } 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) { if (byte.empty()) return ""; return HexEncode(byte.data(), byte.size()); } std::string unlimited_b2a_hex(const std::vector& byte) { if (byte.empty()) return ""; return UnlimitedHexEncode(byte.data(), byte.size()); } std::string b2a_hex(const std::string& byte) { if (byte.empty()) return ""; return HexEncode(reinterpret_cast(byte.data()), byte.length()); } std::string unlimited_b2a_hex(const std::string& byte) { if (byte.empty()) return ""; return UnlimitedHexEncode(reinterpret_cast(byte.data()), byte.length()); } std::string HexEncode(const uint8_t* in_buffer, size_t size) { constexpr unsigned int kMaxSafeSize = 2048; if (size > kMaxSafeSize) size = kMaxSafeSize; return UnlimitedHexEncode(in_buffer, size); } std::string UnlimitedHexEncode(const uint8_t* in_buffer, size_t size) { static const char kHexChars[] = "0123456789ABCDEF"; if (size == 0) return ""; // 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; } // Standard Base64 encoding and decoding. std::string Base64Encode(const std::vector& bin_input) { if (bin_input.empty()) { return std::string(); } return Base64EncodeInternal(bin_input.data(), bin_input.size(), kBase64Codes); } std::string Base64Encode(const std::string& bin_input) { if (bin_input.empty()) { return std::string(); } return Base64EncodeInternal( reinterpret_cast(bin_input.data()), bin_input.size(), kBase64Codes); } // Decode for standard base64 encoding (RFC4648). std::vector Base64Decode(const std::string& b64_input) { if (b64_input.empty()) { return std::vector(); } return Base64DecodeInternal(b64_input.data(), b64_input.size(), kBase64Codes); } // URL/Filename Safe Base64 encoding and decoding. // This is the encoding required to interface with the provisioning server, as // well as 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) { if (bin_input.empty()) { return std::string(); } return Base64EncodeInternal(bin_input.data(), bin_input.size(), kBase64SafeCodes); } std::string Base64SafeEncode(const std::string& bin_input) { if (bin_input.empty()) { return std::string(); } return Base64EncodeInternal( reinterpret_cast(bin_input.data()), bin_input.size(), kBase64SafeCodes); } std::vector Base64SafeDecode(const std::string& b64_input) { if (b64_input.empty()) { return std::vector(); } return Base64DecodeInternal(b64_input.data(), b64_input.size(), kBase64SafeCodes); } // URL/Filename Safe Base64 encoding without padding. std::string Base64SafeEncodeNoPad(const std::vector& bin_input) { std::string b64_output = Base64SafeEncode(bin_input); // Output size: ceiling [ bin_input.size() * 4 / 3 ]. b64_output.resize((bin_input.size() * 4 + 2) / 3); return b64_output; } std::string Base64SafeEncodeNoPad(const std::string& bin_input) { std::string b64_output = Base64SafeEncode(bin_input); // Output size: ceiling [ bin_input.size() * 4 / 3 ]. b64_output.resize((bin_input.size() * 4 + 2) / 3); return b64_output; } // Host to Network/Network to Host conversion. // Convert to big endian (network-byte-order) int64_t htonll64(int64_t x) { union { uint32_t array[2]; int64_t number; } mixed; mixed.number = 1; if (mixed.array[0] == 1) { // Little Endian. mixed.number = x; uint32_t temp = mixed.array[0]; mixed.array[0] = htonl(mixed.array[1]); mixed.array[1] = htonl(temp); return mixed.number; } else { // Big Endian. return x; } } // Encode unsigned integer into a big endian formatted string std::string EncodeUint32(unsigned int u) { std::string s; s.push_back((u >> 24) & 0xFF); s.push_back((u >> 16) & 0xFF); s.push_back((u >> 8) & 0xFF); s.push_back(u & 0xFF); return s; } } // namespace wvutil