InCTF 2021 | Encrypted Operations

#inctf2021

#include <vector>
#include <string>
#include <random>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <boost/lexical_cast.hpp>

#include "utils.cpp"

#define HIGH 9
#define LOW 0

using namespace std;

//################################################################################################

int level2()
{
    int MAT_SIZE = 5;
    vector<int64_t> p;
    vector<int64_t> msgvector;

    vector<vector<int64_t>> m1(MAT_SIZE, vector<int64_t>(MAT_SIZE));
    vector<int> row = {0, 1, 2, 3, 4};

    random_device rd;
    mt19937 g(rd());

    shuffle(row.begin(), row.end(), g);

    cout << "                   =============================\n";
    cout << "                       --------LEVEL2--------\n";
    cout << "                   =============================\n";

    for (int x = 0; x < MAT_SIZE; x++)
    {
        for (int y = 0; y < MAT_SIZE; y++)
        {
            if ((x == row[0]) || (x == row[1]))
                m1[x][y] = 0;
            else
                m1[x][y] = Genrand(LOW, HIGH);
        }
    }

    long int userInp;
    msgvector = m1[row[2]];

    FheEncrypt(msgvector);
    EncryptedOperations();

    p = FheDecrypt();
    p = vector<int64_t>(p.begin(), p.begin() + MAT_SIZE + 1);
    userInp = VecToNum(p);

    msgvector = m1[row[3]];

    FheEncrypt(msgvector);
    EncryptedOperations();

    vector<int64_t> p1;
    p = FheDecrypt();

    m1[row[1]] = p1 = vector<int64_t>(p.begin(), p.begin() + MAT_SIZE);

    msgvector = m1[row[4]];

    FheEncrypt(msgvector);
    EncryptedOperations();

    vector<int64_t> p2;
    p = FheDecrypt();

    m1[row[0]] = p2 = vector<int64_t>(p.begin(), p.begin() + MAT_SIZE);

    vector<int> numVec;

    for (int i = 0; i < 5; i++)
        if (i == row[1])
        {
            numVec.push_back(VecToNum(p1));
        }

        else if (i == row[0])
        {
            numVec.push_back(VecToNum(p2));
        }

        else
            numVec.push_back(VecToNum(m1[i]));

    long int sum = accumulate(numVec.begin(), numVec.end(), 0);

    if (sum == userInp)
    {
        cout << "\n\nYou have mastered encrypted operatoins!!\n";
        cout << "Here's the flag: " << printFlag();
    }

    else
    {
        cout << "That was close, great going, But no flag for you!!\n\n";
        exit(0);
    }

    return 0;
}

//################################################################################################
//################################################################################################

long int calc_determinant(long int a, long int x)
{
    long int res = a * ((pow(a, 2) - (a * x * (-1)))) - -1 * ((pow(a, 2) * x) - (pow(x, 2) * a * (-1))) + 0 * ((pow(a * x, 2)) - (pow(a, 2) * pow(x, 2)));
    return res;
}

//-------------------------------------------------------------------------------------------------

void gen_hint(long int a, long int x)
{

    long int hint;
    char opt;
    vector<int64_t> v = {a, x};

    FheEncrypt(v);
    EncryptedOperations();
    v = FheDecrypt();

    cout << "\nchoose(+,-,*,/): ";
    cin >> opt;

    if (opt == '+')
        hint = v[0] + v[1];
    else if (opt == '*')
        hint = v[0] * v[1];
    else if (opt == '-')
        hint = v[0] - v[1];
    else if (opt == '/')
        hint = v[0] / v[1];
    else
    {
        cout << "\n\nERROR!! "
             << "Unrecognized Operation!!";
        exit(0);
    }

    cout << "\n\nHere you go: " << hint * (a * x) << "\n";
}

//################################################################################################
//################################################################################################

int level1()
{

    int SIZE = 20;
    int val = Genrand(1, 500);
    int idx;
    long int num;
    long int sum1 = 0;
    long int sum2 = 0;
    long int a = Genrand(1, 20);
    long int x = Genrand(1, 20);

    vector<vector<int>> m(SIZE, vector<int>(SIZE));
    vector<vector<int>> v(169, vector<int>(9));
    vector<int> mat;
    vector<int64_t> msgvector;

    cout << "                     =============================\n";
    cout << "                         --------LEVEL1--------\n";
    cout << "                     =============================\n";

    for (int x = 0; x < SIZE; x++)
    {
        for (int y = 0; y < SIZE; y++)
        {
            m[x][y] = ++val;
        }
    }

    int d = m.size();
    int r = 3;
    int c = 3;

    for (int i = 0; i < d - r + 1; i++)
    {
        for (int j = 0; j < d - c + 1; j++)
        {
            for (int p = 0; p < r; p++)
            {
                for (int q = 0; q < c; q++)
                {
                    mat.push_back(m[i + p][j + q]);
                }
            }
        }
    }

    for (int j = 0; j < int(mat.size()); j += 9)
    {
        v.push_back(slice(mat, j, j + 9));
    }

    idx = Genrand(0, v.size() - 1);
    vector<int64_t> temp1(begin(v[idx]), end(v[idx]));
    vector<int64_t> mvector = temp1;

    sum1 = accumulate(mvector.begin(), mvector.end(), 0);

    FheEncrypt(mvector);

    EncryptedOperations();

    vector<int64_t> p = FheDecrypt();

    if (sum1 == 0)
    {
        cout << "\n\nCHALLENGE CORRUPTED!!!!";
        exit(0);
    }

    if (p[0] == sum1)
        cout << "\n\nYou got all the encrypted operations right! Great!!\n\nNow on to the next\n\n";
    else
        exit(0);

    v = {};

    mat = {};
    r = 5;
    c = 4;

    for (int i = 0; i < d - r + 1; i++)
    {
        for (int j = 0; j < d - c + 1; j++)
        {
            for (int p = 0; p < r; p++)
            {
                for (int q = 0; q < c; q++)
                {
                    mat.push_back(m[i + p][j + q]);
                }
            }
        }
    }

    for (int j = 0; j < int(mat.size()); j += 20)
    {
        v.push_back(slice(mat, j, j + 20));
    }

    idx = Genrand(0, v.size() - 1);
    vector<int64_t> temp2(begin(v[idx]), end(v[idx]));
    vector<int64_t> mvector2 = temp2;

    sum2 = accumulate(mvector2.begin(), mvector2.end(), 0);

    num = mvector2[0];

    long int res = calc_determinant(a, 2 * x) - calc_determinant(a, x);
    gen_hint(a, x);

    cout << "This might make it easier: " << res * num << "\n\n";

    FheEncrypt(mvector2);

    EncryptedOperations();

    p = FheDecrypt();

    if (sum2 == 0)
    {
        cout << "\n\nCHALLENGE CORRUPTED!!!!";
        exit(0);
    }

    if (p[0] == sum2)
        cout << "Seems like you got a hang of this!!\nCongrats on completing the first level.\nGo get the flag!\n\n";
    else
        exit(0);

    return 0;
}

//#########################################################################################
//###################################### MAIN() ###########################################

int main()
{

    cout << "               _______________________________________\n";
    cout << "\n               WELCOME TO 'ENCRYPTED OPERATIONS SERVER'\n";
    cout << "               _______________________________________\n\n";

    cout << "\nThe server provides you with a set of operations to perform on encrypted data\n";
    cout << "All operations are performed on vector(long int) type data.\nThe server parses a string to convert it into vector\n";
    cout << "\n\nEx of vector input ->\n```Enter the operand vector: 12 13 14 15```\nThis will be parsed into `vector<int64_t>`\n\nLoading the service......\n\n";

    atexit(sayonara);

    int ret = level1();
    ret = level2();

    if (ret == 0)
        atexit(arigato);

    return 0;
}

//###############################------------------------------#############################
//##############################------------END-----------------############################
#include <vector>
#include <string>
#include <random>
#include <sstream>
#include <fstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>

#include "homomorphic_system.cpp" // c++ pallisade library is used to implement FHE

homomorphic_system r1;
/* 
  If u want to check out pallisade library and test out is functionalities the following documentation specifies the build procedure :
    https://gitlab.com/palisade/palisade-release#build-instructions ( for installing pallisade locally )
    pls be aware that installation can take a while and and on PC's with lower specs might slow down PC performance drastically/crash during installation

    PLS NOTE: pallisade installation is NOT NECESSARY in any way for solving this challenge!
*/

CryptoContext<DCRTPoly> cryptoContext = r1.genCC();      // Generates the cryptocontext
LPKeyPair<DCRTPoly> keyPair = r1.genKeys(cryptoContext); // Generates the private key

//################################################################################################

int VecToNum(vector<int64_t> vec)
{
    std::vector<int> values(begin(vec), end(vec));

    int res = std::accumulate(values.begin(), values.end(), 0, [](int acc, int val)
                              { return 10 * acc + val; });

    return res;
}

//################################################################################################

int Genrand(int lr, int hr)
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> distr(lr, hr);

    int rnum = distr(gen);
    return rnum;
}

//################################################################################################

vector<int> slice(const vector<int> &v, int start = 0, int end = -1)
{
    int olen = v.size();
    int nlen;

    if (end == -1 or end >= olen)
    {
        nlen = olen - start;
    }
    else
    {
        nlen = end - start;
    }

    vector<int> nv(nlen);

    for (int i = 0; i < nlen; i++)
    {
        nv[i] = v[start + i];
    }

    return nv;
}

//################################################################################################

/* operations list:
   - '+' adds operand vector to encrypted vector 
   - '*' multiplies operand vector to encrypted vector
   - '<' shifts the elements of encrypted vector to left by value specified in 'samt'
   - '>' shifts elements of encrypted vector to right by value specified in 'samt'
*/
/* eg operation:

    if the following vector -> {1,1,1,1} is encrypted
    and operand vector is given as {-2,-2,-2,-2} 
    operations is '+'
    shift amount can be any value in specified range ( since not used )
    resultant vector after decrypting will be {-1,-1,-1,-1}
    
    if above operation was '<'
    samt = 1
    resultant vector after decrypting will be {1,1,1,0}
    while shifting operand vector can be given as 0 i.e {0} as it is not used
 */

void EncryptedOperations()
{
    char choice = 'y';
    char operation; // '+','*','<',">"
    int samt;       // shift amount
    int cnt = 0;

    std::cout << "\n\nThe plaintext has been encrypted.\n\n"
              << std::flush;
    ;

    while (choice == 'y' && cnt < 4)
    {
        std::cout << "\nEnter the operand vector: " << std::flush;
        std::string s;
        std::getline(std::cin >> std::ws, s);

        std::stringstream iss(s);
        int number;
        std::vector<int64_t> operandVector;
        while (iss >> number)
            operandVector.push_back(number);

        assert(1 <= operandVector.size() && operandVector.size() <= 20);

        std::cout << "\nSpecify Operation: ";
        std::cin >> operation;

        std::cout << "\nEnter shift amount: ";
        std::cin >> samt;

        assert(1 <= samt && samt < 20);

        r1.compute(cryptoContext, operation, samt, operandVector); // performs the operation specified on encrypted vector/ on encrypted and operand vector

        std::cout << "\nperform another operation(y/n): ";
        std::cin >> choice;

        assert(choice == 'y' || choice == 'n');
        cnt += 1;
    };
    std::cout << "\n";
}

//################################################################################################

// msg vector is encrypted using fhe

void FheEncrypt(vector<int64_t> msgvector)
{
    r1.genCiphertexts(msgvector, keyPair, cryptoContext);
}

//################################################################################################

// resultant vector after performing the operations is decrypted

vector<int64_t> FheDecrypt()
{
    std::vector<int64_t> dt;
    dt = r1.decrypt(keyPair, cryptoContext);

    return dt;
}

//################################################################################################

std::string printFlag()
{
    std::fstream flagfile;
    flagfile.open("flag.txt", std::ios::in);

    std::string flag;
    if (flagfile.is_open())
    {

        std::string l;
        while (getline(flagfile, l))
        {
            flag += l;
        }

        flagfile.close();
    }

    return flag;
}

//################################################################################################

void sayonara(void)
{
    std::cout << "\n\n\nExiting!!";
}

//################################################################################################

void arigato(void)
{
    std::cout << "\n\n\n"
              << "Thankyou for using the service! Sucessfully performed all operatoions!!";
}

//#############################--------------------------------#####################################
//#############################--------------END---------------#####################################