vector-of-bool / cmrc

A Resource Compiler in a Single CMake Script
MIT License
674 stars 74 forks source link

add an option to encrypt a resource #46

Open WilliamTambellini opened 1 year ago

WilliamTambellini commented 1 year ago

add an option to encrypt a resource using a given key

PanForPancakes commented 1 year ago

As a workaround, why not just do it manually?

In my opinion file encryption goes out of scope for this project and would make no sense anyway, since to decrypt this data you have to ship key with resources.

xiaozhuai commented 1 year ago

I would vote for this feature, simple xor would be enough for my use case.

tay10r commented 7 months ago

Obfuscation would probably be a good middle ground. A simple XOR cipher with an 8-bit key would be great.

Anything that makes it less easy do something like:

cat ./some_app | strings | grep "private info"

In the case of having an XOR key, I would leave the "decrypting" to the calling environment.

auto fs = get_filesystem();
auto key = fs.get_key() /* uint8_t */;
auto f = fs.open("some_file.bin");
for (auto a : f) {
  auto b = reinterpret_cast<unsigned char>(a) ^ key;
  /* b is original file byte */
}

Or perhaps have a function to optionally "decrypt" everything:

auto unpack(const cmrc::file& f, const uint8_t key) -> std::vector<char>
{
  std::vector<char> output(f.size());
  auto op = [key](auto c) { return reinterpret_cast<char>(reinterpret_cast<uint8_t>(c) ^ key); };
  std::transform(f.begin(), f.end(), output.begin(), op);
  return output;
}

While it's obviously not meant for protecting things like private keys, it can be useful for adding some protection to software IP. There are files like shader source code or ONNX models that take a lot of time and money to develop. You may not want to offer them up on a silver platter.

WilliamTambellini commented 7 months ago

tks @tay10r and @xiaozhuai @PanForPancakes

In my opinion file encryption goes out of scope for this project

Apparently I m not the only one thinking it s in the scope

and would make no sense anyway, since to decrypt this data you have to ship key with resources.

not necessarily: the decrypt key could come at runtime from a license.

tay10r commented 7 months ago

Supporting any modifying to the resource file is tough. There's two parts to supporting something like this:

Encryption

Currently, by design, the conversion of the resource file to a C++ file that can be compiled is done entirely in CMake. This is convenient specifically because in cross compiling environments, nothing special has to be done. Whereas having a compiled program do the conversion will break everytime your building for a different instruction set.

So if the file either gets encrypted using AES or a simple XOR mechanism, it needs to be done on in CMake's script mode (the function that does this is at the top of CMakeRC.cmake). Nothing in CMake's current set of commands really seem to support this behavior. There's a chance you could do it with the XOR operator in the math command, but it would have to be done on a byte by byte basis, and probably be very slow.

You could also do it by using execute_process and pass the file contents to a Python script. In terms of requiring dependencies, Python isn't light weight but it is relatively common. Supporting either forms of encryption with Python would be relatively seemless, but whether or not Python should be a dependency of this script is debatable.

Another option would be to submit a patch to support a variant of the string command that either does the XOR operation or encrypts it. In this case, I think the XOR would be a little niche for a CMake command. Using AES would probably have a higher chance of getting accepted as a command feature, since it has a higher chance of getting used outside of this project.

Decryption

There are a few issues to consider when supporting decrypting of the encrypted resource file. If it's XOR, then the solution is pretty straight forward. No dependencies, no fuss. With AES, it's less simple.

There are several crypto libraries out there. Personally I've used OpenSSL and mbedTLS, but there are probably more. OpenSSL is pretty popular, but is an absolute pain to build on Windows and I'm not entirely sure if it works on MCUs. In any case, if you pick one, someone will eventually want support for the other. You could include a lightweight AES decrypt function, but in general it's advisable to use the more robust libraries for crypto.


After doing a little bit more digging on what would go into it, I think the most sensible approach would be to submit a patch to support encryption (maybe even compression too) on strings. Something like:

string(ENCRYPT ALGO <algo> KEY <key> IV <iv> <string> <out-var>)

I would then leave the process of decrypting the resource file entirely to the caller, since the means of doing this is going to differ greatly depending on the platform and application.