C++: Add query tests.

This commit is contained in:
Geoffrey White
2023-11-29 16:34:00 +00:00
parent b0514de094
commit 3c6f318cb2
4 changed files with 208 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
| tests_crypto.cpp:11:6:11:18 | encryptString | This may be a custom implementation of a cryptographic primitive. |
| tests_crypto.cpp:30:6:30:14 | MyEncrypt | This may be a custom implementation of a cryptographic primitive. |
| tests_crypto.cpp:83:6:83:18 | init_aes_sbox | This may be a custom implementation of a cryptographic primitive. |

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-1240/CustomCryptographicPrimitive.ql

View File

@@ -0,0 +1,97 @@
// Cryptography snippets. All (non-stub) functions in this file should be flagged by the query.
typedef unsigned char uint8_t;
int strlen(const char *string);
// ---
// the following function is homebrew crypto written for this test. This is a bad algorithm
// on multiple levels and should never be used in cryptography.
void encryptString(char *string, unsigned int key) {
char *ptr = string;
int len = strlen(string);
while (len >= 4) {
// encrypt block by XOR-ing with the key
ptr[0] = ptr[0] ^ (key >> 0);
ptr[1] = ptr[1] ^ (key >> 8);
ptr[2] = ptr[2] ^ (key >> 16);
ptr[3] = ptr[3] ^ (key >> 24);
// move on
ptr += 4;
len -= 4;
}
}
// the following function is homebrew crypto written for this test. This is a bad algorithm
// on multiple levels and should never be used in cryptography.
void MyEncrypt(const unsigned int *dataIn, unsigned int *dataOut, unsigned int dataSize, unsigned int key[2]) {
unsigned int state[2];
unsigned int t;
state[0] = key[0];
state[1] = key[1];
for (unsigned int i = 0; i < dataSize; i++) {
// mix state
t = state[0];
state[0] = (state[0] << 1) | (state[1] >> 31);
state[1] = (state[1] << 1) | (t >> 31);
// encrypt data
dataOut[i] = dataIn[i] ^ state[0];
}
}
// the following function resembles an implementation of the AES "mix columns"
// step. It is not accurate, efficient or safe and should never be used in
// cryptography.
void mix_columns(const uint8_t inputs[4], uint8_t outputs[4]) {
// The "mix columns" step takes four bytes as inputs. Each byte represents a
// polynomial with 8 one-bit coefficients, e.g. input bits 00001101
// represent the polynomial x^3 + x^2 + 1. Arithmetic is reduced modulo
// x^8 + x^4 + x^3 + x + 1 (= 0x11b).
//
// The "mix columns" step multiplies each input by 2 (in the field described
// above) to produce four more values. The output is then four values
// produced by XOR-ing specific combinations of five of these eight values.
// The exact values selected here do not match the actual AES algorithm.
//
// We avoid control flow decisions that depend on the inputs.
uint8_t vs[4];
vs[0] = inputs[0] << 1; // multiply by two
vs[0] ^= (inputs[0] >> 7) * 0x1b; // reduce modulo 0x11b; the top bit was removed in the shift.
vs[1] = inputs[1] << 1;
vs[1] ^= (inputs[1] >> 7) * 0x1b;
vs[2] = inputs[2] << 1;
vs[2] ^= (inputs[2] >> 7) * 0x1b;
vs[3] = inputs[3] << 1;
vs[3] ^= (inputs[3] >> 7) * 0x1b;
outputs[0] = inputs[0] ^ inputs[1] ^ inputs[2] ^ vs[0] ^ vs[1];
outputs[1] = inputs[1] ^ inputs[2] ^ inputs[3] ^ vs[1] ^ vs[2];
outputs[2] = inputs[2] ^ inputs[3] ^ inputs[0] ^ vs[2] ^ vs[3];
outputs[3] = inputs[3] ^ inputs[0] ^ inputs[1] ^ vs[3] ^ vs[0];
}
// the following function resembles initialization of an S-box as may be done
// in an implementation of DES, AES and other encryption algorithms. It is not
// accurate, efficient or safe and should never be used in cryptography.
void init_aes_sbox(unsigned char data[256]) {
// initialize `data` in a loop using lots of ^, ^= and << operations and
// a few fixed constants.
unsigned int state = 0x12345678;
for (int i = 0; i < 256; i++)
{
state ^= (i ^ 0x86) << 24;
state ^= (i ^ 0xb9) << 16;
state ^= (i ^ 0x11) << 8;
state ^= (i ^ 0x23) << 0;
state = (state << 1) ^ (state >> 31);
data[i] = state & 0xff;
}
}

View File

@@ -0,0 +1,107 @@
// Non-cryptography snippets. Nothing in this file should be flagged by the query.
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned long size_t;
// a very cut down stub for `std::cout`
namespace std
{
template<class charT> struct char_traits;
template <class charT, class traits = char_traits<charT> >
class basic_ostream {
public:
typedef charT char_type;
};
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
typedef basic_ostream<char> ostream;
extern ostream cout;
}
// ---
uint32_t lookup[256];
uint8_t computeCRC32(const uint8_t *data, size_t dataLen) {
// This function has "RC3" in its name, but is not an implementation of the (broken) RC3 encryption algorithm.
uint32_t result = 0xFFFFFFFF;
for (size_t i = 0; i < dataLen; i++) {
result = (result >> 8) + lookup[(result ^ data[i]) & 0xFF];
result = (result >> 8) + lookup[(result ^ data[i]) & 0xFF]; // artificial extra compute
result = (result >> 8) + lookup[(result ^ data[i]) & 0xFF]; // artificial extra compute
result = (result >> 8) + lookup[(result ^ data[i]) & 0xFF]; // artificial extra compute
}
return result ^ 0xFFFFFFFF;
}
void convert_image_universal(uint32_t *img, int width, int height) {
// This function has "rsa" in its name, but is nothing to do with the RSA encryption algorithm.
uint32_t *pixel_ptr = img;
uint32_t num_pixels = width * height;
// convert pixels RGBA -> ARGB (with probably unhelpful loop unrolling)
while (num_pixels >= 4) {
pixel_ptr[0] = (pixel_ptr[0] >> 8) ^ (pixel_ptr[0] << 24);
pixel_ptr[1] = (pixel_ptr[1] >> 8) ^ (pixel_ptr[1] << 24);
pixel_ptr[2] = (pixel_ptr[2] >> 8) ^ (pixel_ptr[2] << 24);
pixel_ptr[3] = (pixel_ptr[3] >> 8) ^ (pixel_ptr[3] << 24);
num_pixels -= 4;
}
if (num_pixels >= 2) {
pixel_ptr[0] = (pixel_ptr[0] >> 8) ^ (pixel_ptr[0] << 24);
pixel_ptr[1] = (pixel_ptr[1] >> 8) ^ (pixel_ptr[1] << 24);
num_pixels -= 2;
}
if (num_pixels >= 1) {
pixel_ptr[2] = (pixel_ptr[2] >> 8) ^ (pixel_ptr[2] << 24);
}
}
const char* yes_no_setting() { return "no"; }
void output_encrypt_decrypt_algorithms() {
// This function has "encrypt" and "decrypt" in its name, but no encryption is done.
// This function uses `<<` heavily, but not as an integer shift left.
const char *indent = " ";
std::cout << "Supported algorithms:\n";
std::cout << indent << "DES (" << yes_no_setting() << ")\n";
std::cout << indent << "3DES (" << yes_no_setting() << ")\n";
std::cout << indent << "AES (" << yes_no_setting() << ")\n";
std::cout << indent << "RSA (" << yes_no_setting() << ")\n";
std::cout << indent << "Blowfish (" << yes_no_setting() << ")\n";
std::cout << indent << "Twofish (" << yes_no_setting() << ")\n";
std::cout << indent << "Chacha (" << yes_no_setting() << ")\n";
}
// this macro expands to some compute operations that look a bit like cryptography
#define COMPUTE(v) \
v[0] ^= v[1] ^ v[2] ^ v[3] ^ v[4]; \
v[1] ^= v[2] ^ v[3] ^ v[4] ^ v[5]; \
v[2] ^= v[3] ^ v[4] ^ v[5] ^ v[6]; \
v[3] ^= v[4] ^ v[5] ^ v[6] ^ v[7];
void wideStringCharsAt(int *v) {
// This function has "des" and "rsa" in the name.
COMPUTE(v)
}
void bitcastVariable(int *v) {
// This function has "aria" and "cast" in the name.
COMPUTE(v)
}
void dividesVariance(int *v) {
// This function has "des" and "aria" in the name.
COMPUTE(v)
}
void broadcastNodes(int *v) {
// This function has "cast" and "des" in the name.
COMPUTE(v)
}