diff --git a/crypto.cc b/crypto.cc index 8dd4b0b..0dc93ba 100644 --- a/crypto.cc +++ b/crypto.cc @@ -45,6 +45,7 @@ #else #include #include +#include #include #define BCM2UTILS_USE_OPENSSL #endif @@ -369,6 +370,14 @@ string hash_md5(const string& buf) return md5; #endif } +extern "C" void hash_sha256(const unsigned char *buf, int buf_size, unsigned char *hash) +{ +#ifndef BCM2UTILS_USE_WINCRYPT + SHA256(buf, buf_size, hash); +#else + #error TODO: SHA256 using winapi +#endif +} string crypt_3des_ecb(const string& ibuf, const string& key, bool encrypt) { diff --git a/crypto.h b/crypto.h index 93343b0..a88e2a2 100644 --- a/crypto.h +++ b/crypto.h @@ -19,9 +19,13 @@ #ifndef BCM2UTILS_CRYPTO_H #define BCM2UTILS_CRYPTO_H + + +// extern "C" void hash_sha256(const unsigned char *buf, int buf_size, unsigned char *hash); + +#ifdef __cplusplus #include namespace bcm2utils { - std::string hash_md5(const std::string& buf); std::string crypt_aes_256_ecb(const std::string& buf, const std::string& key, bool encrypt); @@ -32,6 +36,6 @@ std::string crypt_des_ecb(const std::string& buf, const std::string& key, bool e std::string crypt_motorola(std::string buf, const std::string& key); std::string crypt_sub_16x16(std::string buf, bool encrypt); std::string crypt_xor_char(std::string buf, const std::string& key); - } #endif +#endif diff --git a/gwsettings.cc b/gwsettings.cc index 1b00c2f..e3594db 100644 --- a/gwsettings.cc +++ b/gwsettings.cc @@ -180,7 +180,14 @@ string gws_decrypt(string buf, string& checksum, string& key, const csp key = buf.back(); } buf = crypt_motorola(buf.substr(0, buf.size() - 1), key); - } else { + } + else if (p->name() == "CGA2121") + { + auto temp = gws_crypt(buf.substr(0x50), key, enc, false); + buf = buf.substr(0x4A,6) + temp; + } + else + { buf = gws_crypt(buf, key, enc, false); } @@ -242,7 +249,15 @@ string gws_encrypt(string buf, const string& key, const csp& p, bool pa } } - buf = gws_crypt(buf, key, enc, true); + if (flags & BCM2_CFG_ENC_AES256_ECB) + { + buf = buf.substr(0, 0x50) + gws_crypt(buf.substr(0x50, string::npos), key, enc, true); + } + else + { + buf = gws_crypt(buf, key, enc, true); + } + } else { throw user_error("profile " + p->name() + " does not support encryption"); } @@ -665,16 +680,38 @@ class gwsettings : public encryptable_settings clip_circumfix(buf); validate_checksum_and_detect_profile(buf); validate_magic(buf); + + m_encrypted = !m_magic_valid; + if (m_profile->name() == "CGA2121") // The magic string and the header aren't encrypted so there's no easy way to know if its encrypted + { + m_encrypted = false; + try + { + // Determine if the file is encrypted by trying to read it. + istringstream temp_str(buf.substr(0x60)); + read_header(temp_str, buf.size()); + settings::read(temp_str); + } catch (...) + { + m_encrypted = true; + } + list d; + groups(d); + if (d.size() < 4) // Basic check + m_encrypted = true; + } - if (!m_magic_valid && !decrypt_and_detect_profile(buf)) { + + + if (m_encrypted && !decrypt_and_detect_profile(buf)) { m_key = m_pw = ""; return is; } else if (!m_encrypted) { m_key = m_pw = ""; } - istringstream istr(buf.substr(m_magic.size())); + istringstream istr(buf.substr(m_profile->name() == "CGA2121" && m_encrypted ? 0 : m_magic.size())); read_header(istr, buf.size()); if (!m_checksum_valid) { @@ -778,6 +815,7 @@ class gwsettings : public encryptable_settings // DNA // CLARO // SFR-PC20 + // SBB - Serbia const string magic2_part2 = "056t9p48jp4ee6u9ee659jy9e-54e4j6r0j069k-056"; const vector magics { @@ -835,6 +873,13 @@ class gwsettings : public encryptable_settings auto v = m_version.num(); logger::t("version=%d.%d, size=%d\n", v >> 8, v & 0xff, m_size.num()); + if (m_profile->name() == "CGA2121") + { + // for some reason m_size also include the size of the magic string + // So we adjust bufsize + bufsize += 0x4A; + } + m_size_valid = m_size.num() == bufsize; if (!m_size_valid && bufsize > m_size.num()) { diff --git a/lonesha256.h b/lonesha256.h new file mode 100644 index 0000000..7f6b34d --- /dev/null +++ b/lonesha256.h @@ -0,0 +1,148 @@ +// Taken from here: https://github.com/BareRose/lonesha256/blob/master/lonesha256.h + +/* +lonesha256.h - Portable, endian-proof, single-file, single-function sha256 implementation, originally based on LibTomCrypt + +To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring +rights to this software to the public domain worldwide. This software is distributed without any warranty. +You should have received a copy of the CC0 Public Domain Dedication along with this software. +If not, see . +*/ + +/* +lonesha256 supports the following three configurations: +#define LONESHA256_EXTERN + Default, should be used when using lonesha256 in multiple compilation units within the same project. +#define LONESHA256_IMPLEMENTATION + Must be defined in exactly one source file within a project for lonesha256 to be found by the linker. +#define LONESHA256_STATIC + Defines the lonesha256() function as static, useful if lonesha256 is only used in a single compilation unit. + +lonesha256 function: +(static|extern) int lonesha256 (unsigned char out[32], const unsigned char* in, size_t len) + writes the sha256 hash of the first "len" bytes in buffer "in" to buffer "out" + returns 0 on success, may return non-zero in future versions to indicate error +*/ +#pragma once + +#define LONESHA256_IMPLEMENTATION + +//header section +#ifndef LONESHA256_H +#define LONESHA256_H + +//process configuration +#ifdef LONESHA256_STATIC + #define LONESHA256_IMPLEMENTATION + #define LSHA256DEF static +#else //LONESHA256_EXTERN + #define LSHA256DEF extern +#endif + +//includes +#include //size_t + +//lonesha256 declaration +LSHA256DEF int lonesha256(unsigned char[32], const unsigned char*, size_t); + +#endif //LONESHA256_H + +//implementation section +#ifdef LONESHA256_IMPLEMENTATION +#undef LONESHA256_IMPLEMENTATION + +//macros +#define S(x, n) (((((uint32_t)(x)&0xFFFFFFFFUL)>>(uint32_t)((n)&31))|((uint32_t)(x)<<(uint32_t)((32-((n)&31))&31)))&0xFFFFFFFFUL) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + (S(e, 6) ^ S(e, 11) ^ S(e, 25)) + (g ^ (e & (f ^ g))) + K[i] + W[i]; \ + t1 = (S(a, 2) ^ S(a, 13) ^ S(a, 22)) + (((a | b) & c) | (a & b)); \ + d += t0; \ + h = t0 + t1; +#define STORE32H(x, y) \ + (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \ + (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); +#define LOAD32H(x, y) \ + x = ((uint32_t)((y)[0]&255)<<24)|((uint32_t)((y)[1]&255)<<16)|((uint32_t)((y)[2]&255)<<8)|((uint32_t)((y)[3]&255)); +#define STORE64H(x, y) \ + (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); +#define SHA256_COMPRESS(buff) \ + for (int i = 0; i < 8; i++) S[i] = sha256_state[i]; \ + for (int i = 0; i < 16; i++) LOAD32H(W[i], buff + (4*i)); \ + for (int i = 16; i < 64; i++) W[i] = Gamma1(W[i-2]) + W[i-7] + Gamma0(W[i-15]) + W[i-16]; \ + for (int i = 0; i < 64; i++) { \ + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i); \ + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; \ + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; \ + } \ + for (int i = 0; i < 8; i++) sha256_state[i] = sha256_state[i] + S[i]; + +//includes +#include //uint32_t, uint64_t +#include //memcpy + +//lonesha256 function +LSHA256DEF int lonesha256 (unsigned char out[32], const unsigned char* in, size_t len) { + //writes the sha256 hash of the first "len" bytes in buffer "in" to buffer "out" + //returns 0 on success, may return non-zero in future versions to indicate error + const uint32_t K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL + }; + uint64_t sha256_length = 0; + uint32_t sha256_state[8] = { + 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, + 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL + }, S[8], W[64], t0, t1, t; + unsigned char sha256_buf[64]; + //process input in 64 byte chunks + while (len >= 64) { + SHA256_COMPRESS(in); + sha256_length += 64 * 8; + in += 64; + len -= 64; + } + //copy remaining bytes into sha256_buf + memcpy(sha256_buf, in, len); + //finish up (len now number of bytes in sha256_buf) + sha256_length += len * 8; + sha256_buf[len++] = 0x80; + //pad then compress if length is above 56 bytes + if (len > 56) { + while (len < 64) sha256_buf[len++] = 0; + SHA256_COMPRESS(sha256_buf); + len = 0; + } + //pad up to 56 bytes + while (len < 56) sha256_buf[len++] = 0; + //store length and compress + STORE64H(sha256_length, sha256_buf + 56); + SHA256_COMPRESS(sha256_buf); + //copy output + for (int i = 0; i < 8; i++) { + STORE32H(sha256_state[i], out + 4*i); + } + //return + return 0; +} + +#endif //LONESHA256_IMPLEMENTATION \ No newline at end of file diff --git a/profiledef.c b/profiledef.c index e438ce7..a436f1b 100644 --- a/profiledef.c +++ b/profiledef.c @@ -18,6 +18,8 @@ */ #include "profile.h" +//#include "crypto.h" +#include "lonesha256.h" static bool keyfun_tc7200(const char *password, unsigned char *key, size_t size) { @@ -37,6 +39,17 @@ static bool keyfun_tc7200(const char *password, unsigned char *key, size_t size) return true; } +static bool keyfun_cga2121(const char *password, unsigned char *key, size_t size) +{ + if (password == nullptr || key == nullptr) + return false; + + //hash_sha256((const unsigned char*)password, strlen(password), key); + lonesha256(key, (const unsigned char*)password, strlen(password)); + + return true; +} + struct bcm2_profile bcm2_profiles[] = { { .name = "generic", @@ -77,6 +90,20 @@ struct bcm2_profile bcm2_profiles[] = { } }, }, + { + .name = "CGA2121", + .pretty = "Technicolor CGA2121", + .baudrate = 115200, + .cfg_md5key = "3250736c633b752865676d64302d2778", + .cfg_flags = BCM2_CFG_ENC_AES256_ECB | BCM2_CFG_PAD_ZERO, + .cfg_keyfun = &keyfun_cga2121, + .spaces = { + { + .name = "ram", + .parts = {} + }, + } + }, #if 1 { .name = "debug", @@ -1420,13 +1447,13 @@ struct bcm2_profile bcm2_profiles[] = { .blsig = 0x3384, .baudrate = 115200, .spaces = { - { + { .name = "ram", .min = 0x80000000, .size = 128 * 1024 * 1024, .parts = { { "bootloader", 0x83f80000, 0x10000 }, - }, + }, }, { .name = "flash",