RSA encryption and decryption algorithm implementation

This code is a large-scale RSA encryption and decryption. The selection of q and p uses pseudo-randomly generated large numbers and performs primality testing; the three characters in the plaintext are divided into blocks, and the characters in the block are not satisfied. Three are filled, and the filling scheme used is PKCS#7 filling.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <math.h>
using namespace std;
long long int N;
long long int D;
// Generate pseudo prime numbers
const int MAX_ROW = 50;

size_t Pseudoprime()
{
    bool ifprime = false;
    size_t a = 0;
    int arr[MAX_ROW]; //The array arr is {3, 4, 5, 6...52}
    for (int i = 0; i < MAX_ROW; + + i)
    {
        arr[i] = i + 3;
    }
    while (!ifprime)
    {
        srand((unsigned)time(0));
        ifprime = true;
        a = (rand() % 10000) * 3 + 10000; //Generate an odd number in the range of 10000 to 40000
        for (int j = 0; j < MAX_ROW; + + j)
        {
            if (a % arr[j] == 0)
            {
                ifprime = false;
                break;
            }
        }
    }
    return a;
}

size_t repeatMod(size_t base, size_t n, size_t mod)//modular repeat square algorithm to find (b^n)%m
{
    size_t a = 1;
    while(n)
    {
        if (n & 1)
        {
            a = (a * base) % mod;
        }
        base = (base * base) % mod;
        n = n >> 1;
    }
    return a;
}

//Miller-Rabin prime number detection
bool rabinmiller(size_t n, size_t k)
{

    int s = 0;
    int temp = n - 1;
    while ((temp & amp; 0x1) == 0 & amp; & amp; temp)
    {
        temp = temp >> 1;
        s++;
    } //Represent n-1 as (2^s)*t
    size_t t = temp;

    while (k--) //Judge that the probability of misjudgment in round k is not greater than (1/4)^k
    {
        srand((unsigned)time(0));
        size_t b = rand() % (n - 2) + 2; //Generate a b(2≤a ≤n-2)

        size_t y = repeatMod(b, t, n);
        if (y == 1 || y == (n - 1))
            return true;
        for (int j = 1; j <= (s - 1) & amp; & amp; y != (n - 1); + + j)
        {
            y = repeatMod(y, 2, n);
            if(y==1)
                return false;
        }
        if (y != (n - 1))
            return false;
    }
    return true;
}

/*
//Simple prime number detection method
bool isprime(size_t n)
{
    if(n==2)
        return true;
    for (int i = 2; i <= (int)sqrt((float)n); + + i)
    {
        if (n % i == 0)
            return false;
    }
    return true;
}*/

// Calculate the greatest common divisor
unsigned long long gcd(unsigned long long a, unsigned long long b) {
    if(b==0)
        return a;
    return gcd(b, a % b);
}

// Calculate modular inverse elements
unsigned long long modInverse(unsigned long long a, unsigned long long m) {
    long long m0 = m;
    long long y = 0, x = 1;

    if(m==1)
        return 0;

    while (a > 1) {
        long long q = a / m;
        long long t = m;

        m = a % m;
        a = t;
        t = y;

        y = x - q * y;
        x = t;
    }

    if (x < 0)
        x + = m0;

    return x;
}

// Fast modular exponentiation
unsigned long long modExp(unsigned long long base, unsigned long long exponent, unsigned long long modulus) {
    unsigned long long result = 1;
    base = base % modulus;

    while (exponent > 0) {
        if (exponent & 1) {
            result = (result * base) % modulus;
        }

        exponent = exponent >> 1;
        base = (base * base) % modulus;
    }

    return result;
}

// RSA segmented encryption function (including padding)
unsigned long long* encryptString(const char* plaintext, unsigned long long n, unsigned long long e, int blockSize, int* numBlocks) {
    int plaintextLength = strlen(plaintext);
    int paddingLength = blockSize - (plaintextLength % blockSize);
    *numBlocks = (int)ceil((double)(plaintextLength + paddingLength) / blockSize);

    unsigned long long* ciphertext = new unsigned long long[*numBlocks];

    for (int i = 0; i < *numBlocks; i + + ) {
        int startIndex = i * blockSize;
        int endIndex = (i + 1) * blockSize;
        if (endIndex > plaintextLength) {
            endIndex = plaintextLength;
        }

        unsigned long long blockValue = 0;
        for (int j = startIndex; j < endIndex; j + + ) {
            blockValue = blockValue * 256 + (unsigned long long)plaintext[j];
        }

        if (i == (*numBlocks - 1)) {
            //The last block is filled
            for (int j = 0; j < paddingLength; j + + ) {
                blockValue = blockValue * 256 + paddingLength;
            }
        }

        ciphertext[i] = modExp(blockValue, e, n);
    }

    return ciphertext;
}

// RSA segmented decryption function (including depadding)
char* decryptString(unsigned long long* ciphertext, unsigned long long n, unsigned long long d, int blockSize, int numBlocks) {
    char* plaintext = new char[numBlocks * blockSize + 1]; // Add 1 to store the null terminator
    for (int i = 0; i < numBlocks; i + + ) {
        unsigned long long decryptedBlock = modExp(ciphertext[i], d, n);

        for (int j = blockSize - 1; j >= 0; j--) {
            plaintext[i * blockSize + j] = (char)(decryptedBlock % 256);
            decryptedBlock = decryptedBlock / 256;
        }
    }

    //Remove padding bytes
    int padding = (int)plaintext[numBlocks * blockSize - 1]; //The last byte indicates the padding length
    int plaintextLength = numBlocks * blockSize - padding;
    plaintext[plaintextLength] = '\0';

    return plaintext;
}

void encryptNumber(long M, unsigned long long n, unsigned long long e) {
    int r = 1;
    e = e + 1;
    while (e != 1)
    {
        r = r * M;
        r = r % n;
        e--;
    }
    printf("Original message: %ld\
", M);
    printf("Encrypted message:");
    cout << r << endl;

}

// Various parameters of RSA encryption and decryption
void rsa(int mode) {
    cout << "Generating RSA encrypted public and private keys, please wait..." << endl;
    unsigned long long int p = 0;
    unsigned long long int q = 0;
    for (int i = 0; i <= 1; i + + ) {
        size_t ret = Pseudoprime();
        if (i == 0 & amp; & amp; p == 0) {
            if (rabinmiller(ret, 10)) {
                cout << "generated" << ret << "is a prime number" << endl;
                cout << "p is:" << ret << endl;
                p = ret;
                cout << "---------------------------------------------" << endl ;
            }
            else {
                i--;
            }
        }
        else if (i == 1 & amp; & amp; q == 0 & amp; & amp; p != 0) {
            if (rabinmiller(ret, 10) & amp; & amp; ret != p) {
                cout << "generated" << ret << "is a prime number" << endl;
                cout << "q is:" << ret << endl;
                q = ret;
                cout << "---------------------------------------------" << endl ;
            }
            else {
                i--;
            }
        }
    }
    unsigned long long int n = p * q; // Calculate n
    unsigned long long int phi = (p - 1) * (q - 1); // Calculate Euler function phi(n)
    unsigned long long int e = 0;
    while (1) {
        e = rand() % phi; // Randomly select an encryption index e
        if (gcd(e, phi) == 1) // Ensure that e and phi are mutually prime
            break;
    }
    cout << "n is:" << n << endl;
    N = n;
    cout << "Randomly generate e:" << e << endl;
    cout << "The phi value is:" << phi << endl;
    // Calculate the decryption index d
    unsigned long long d = modInverse(e, phi);
    printf("Private key (d): %llu\
", d);
    D = d;
    cout << "---------------------------------------------" << endl ;
    if (mode == 10) {
        char filename[256];
        printf("Please enter the local file name containing plain text:");
        scanf("%5s", filename);

        FILE* file = fopen(filename, "r");
        if (file == NULL) {
            printf("Cannot open file %s\
", filename);
            return;
        }
        fseek(file, 0, SEEK_END);
        long fileSize = ftell(file);
        fseek(file, 0, SEEK_SET);

        char* plaintext = (char*)malloc((fileSize + 1) * sizeof(char));
        fread(plaintext, sizeof(char), fileSize, file);
        plaintext[fileSize] = '\0';

        fclose(file);
        int blockSize = 3;
        int numBlocks;
        unsigned long long* encrypted = encryptString(plaintext, n, e, blockSize, & amp;numBlocks);

        printf("Original message: %s\
", plaintext);
        printf("Encrypted message:");
        for (int i = 0; i < numBlocks; i + + ) {
            printf("%llu ", encrypted[i]);
        }
        printf("\
");
        //Write to txt local file
        printf("Please enter the file name to save the ciphertext:");
        char filename1[256];
        scanf("%5s", filename1);

        FILE* file1 = fopen(filename1, "w");
        if (file == NULL) {
            printf("Cannot open file %s\
", filename1);
            return;
        }
        for (int i = 0; i < numBlocks; i + + ) {
            fprintf(file1, "%llu ", encrypted[i]);
        }
        fclose(file1);
        printf("The ciphertext has been saved to file %s\
", filename1);
        free(plaintext);
        free(encrypted);
    }
    if (mode == 21) {
        char plaintext[256];
        printf("Please enter the plain text to be encrypted:");
        scanf("%5s", plaintext);

        int numBlocks;
        unsigned long long* encrypted = encryptString(plaintext, n, e, 3, & amp;numBlocks);
        printf("Original message: %s\
", plaintext);
        printf("Encrypted message:");
        for (int i = 0; i < numBlocks; i + + ) {
            printf("%llu ", encrypted[i]);
        }
        cout << endl;
        //Write to txt local file
        printf("Please enter the file name to save the ciphertext:");
        char filename[256];
        scanf("%5s", filename);
        FILE* file = fopen(filename, "w");
        if (file == NULL) {
            printf("Cannot open file %s\
", filename);
            return;
        }
        for (int i = 0; i < numBlocks; i + + ) {
            fprintf(file, "%llu ", encrypted[i]);
        }
        fclose(file);
        printf("The ciphertext has been saved to file %s\
", filename);
        free(encrypted);
    }
    if (mode == 22) {
        long M;
        cout << "Please enter a number:";
        scanf("%ld", & amp;M);
        encryptNumber(M, n, e);
    }
}

//Main function
int main(void)
{
    int choice = 0;
    while (choice != 3) {
        printf("Welcome to RSA encryption and decryption system\
");
        cout << "------------------------------------------------- -------------------------------------------------- --------------------" << endl;
        printf("Please select function:\
");
        printf(" 1. Encryption\
");
        printf(" 2. Decrypt\
");
        printf(" 3. Exit\
");
        printf("Select:");
        scanf("%d", & amp;choice);

        switch (choice) {
        case 1: {
            int option = 0;
            printf("Please select input method:\
");
            printf("1. Read plain text from local file\
");
            printf("2. Directly output plain text\
");
            printf("Select:");
            scanf("%d", & amp;option);
            if (option == 1) {
                //Read plain text from file
               // char* plainText = readPlainTextFromFile(filename);
               // if (plainText == NULL) {
               // break;
              // }
              // free(plainText);
                rsa(10); break;
            }
            else if (option == 2) {
                int op = 0;
                bool flag = false;
                printf("Please select input method:\
");
                printf(" 1. Encrypted string\
");
                printf(" 2. Encrypted number\
");
                printf("Select:");
                int modeNumber = 22; //Encrypted number
                int modeString = 21; //Encrypted string
                while (op > -1 & amp; & amp; flag == false) {
                    scanf("%d", & amp;op);
                    if (op == 1) {
                        rsa(modeString); flag = 1; break;
                    }
                    else if (op == 2)
                        break;
                    else {
                        printf("Invalid selection, please re-enter:\
");
                    }
                }
                while (op > -1 & amp; & amp; flag == false) {
                    scanf("%d", & amp;op);
                    if (op == 2) {
                        rsa(modeNumber); flag = 1; break;
                    }
                    else {
                        printf("Invalid selection, please re-enter:\
");
                    }
                }
                break;
            }
        }
              //Decrypt the ciphertext
        case 2: {
            printf("Please select input method:\
");
            printf(" 1. Read ciphertext from local file\
");
            printf(" 2. Directly output the ciphertext\
");
            printf("Select:");
            int option = 0;
            scanf("%d", & amp;option);
            if (option == 2) {
                unsigned long long ciphertext[256];
                int numBlocks = 0;

                printf("Please enter the ciphertext to be decrypted (separated by spaces, stopped by newlines):");
                while (scanf("%llu", & ciphertext[numBlocks]) == 1) {
                    numBlocks + + ;
                    // Read and discard newlines
                    if (getchar() == '\
') {
                        break;
                    }
                }
                getchar(); // Clear newline characters in the input buffer
                // Get private keys d and n
                unsigned long long d, n;
                cout << "Please enter private keys d and n" << endl;
                cout << "d is: ";
                cin >> d;
                cout << "n is: ";
                cin >> n;
                //Perform decryption operation
                char* decrypted = decryptString(ciphertext, n, d, 3, numBlocks);
                printf("Decrypted message: %s\
", decrypted);

                free(decrypted);
                break;
            }
            else if (option == 1) {
                cout <<
                    "Private key PR={" << D << "," << N << "}" << endl;
                printf("Please enter the name of the ciphertext file to be read:");
                char filename[256];
                scanf("%5s", filename);

                FILE* file = fopen(filename, "r");
                if (file == NULL) {
                    printf("Cannot open file %s\
", filename);
                    return 1;
                }

                unsigned long long* ciphertext = NULL;
                int numBlocks = 0;
                int c;
                //Read ciphertext data
                unsigned long long block;
                while ((c = fgetc(file)) != EOF) {
                    if (isdigit(c) || c == '-') {
                        ungetc(c, file);
                        if (fscanf(file, "%llu", & block) == 1) {
                            numBlocks + + ;
                            ciphertext = (unsigned long long*)realloc(ciphertext, numBlocks * sizeof(unsigned long long));
                            ciphertext[numBlocks - 1] = block;
                        }
                    }
                    else if (c == ' ') {
                        continue; // ignore space characters
                    }
                }
                fclose(file);
                char* decrypted = decryptString(ciphertext, N, D, 3, numBlocks);
                printf("Decrypted plain text: %s\
", decrypted);
                free(ciphertext);
                free(decrypted);
                break;
            }
        }
        case 3: {
            printf("Exit the program.\
");
            break;
        }
        default: {
            printf("Invalid selection.\
");
            break;
        }
        }
        cout << endl;
    }
    return 0;
}

Any similarity is purely coincidental.