串行版本和并行结果不显示相同的输出 [英] The Serial Version and The Parallel Result Not Showing the same output

查看:96
本文介绍了串行版本和并行结果不显示相同的输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我实际上是 openmp 的新手,我有一个有效的 aes-128-cbc 玩具代码,可以将硬编码的密文解密为 12345,.请帮忙.

解决方案

您只有一个用于试用密码 (plainpassword) 的缓冲区,并且您的 中的所有线程都与 正在尝试同时使用它(通过指针 password).这里有大量的数据竞争,由此产生的行为是不确定的.

一种解决方案是将缓冲区声明为内部并行循环而不是外部,因为并行区域的本地变量自动是私有的——每个线程都有自己的.

 #pragma omp parallel for shared (key, iv)for (int i=0; i<dict_len; i++) {无符号字符密码[5] = { i };for (int j=0; j

还要注意

  • 同时拥有 plainpasswordpassword 对您的原始代码没有明显的好处.上面只使用了一个数组,而不是一个单独的指针,它选择名称password";
  • *(x + y),其中 xy 是主要的,意味着与 x[y] (和 y[x]).下标形式更易于阅读,这几乎总是使其在风格上更好.

更新:

我还观察到并行版本的这段代码没有意义,尤其是与串行代码相比:

<块引用>

 #pragma omp parallel if (strncmp((const char*)ciphertext,Salted__",8) == 0) shared(ciphertext, plaintext, ciphertext_base64) private(salt){//找到关键字Salted__";memcpy(salt,&密文[8],8);密文 += 16;cipher_len -= 16;}

原始代码每次运行执行一次 if 语句,并且每次运行最多执行一次其主体,因此如果并行版本多次执行主体,并且它有副作用(它确实),那么程序的结果就会不同.这部分需要恢复原代码.

I am actually new to openmp, I have a working aes-128-cbc toy code that decrypts an hardcoded ciphertext to 12345, this book was recommended for me by one of the community user, I also came across this openmp reference guide and lastly I was heavily guided by one of the community user. From those books and the guide, I was trying to parallelize the serial code below

SERIAL WORKING CODE:

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <string.h>
#include <stdio.h>
#include <time.h>


int success = 0;

void handleOpenSSLErrors(void)
{
    ERR_print_errors_fp(stderr);
    abort();
}

unsigned char* decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv ){

    EVP_CIPHER_CTX *ctx;
    unsigned char *plaintexts;
    int len;
    int plaintext_len;

    unsigned char* plaintext = malloc(ciphertext_len);
    bzero(plaintext,ciphertext_len);

    /* Create and initialise the context */

    if(!(ctx = EVP_CIPHER_CTX_new())) handleOpenSSLErrors();

    /* Initialise the decryption operation. IMPORTANT - ensure you use a key
    * and IV size appropriate for your cipher
    * IV size for *most* modes is the same as the block size. For AES this
    * is 128 bits */

    if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
        handleOpenSSLErrors();


    EVP_CIPHER_CTX_set_key_length(ctx, EVP_MAX_KEY_LENGTH);

    /* Provide the message to be decrypted, and obtain the plaintext output.
    * EVP_DecryptUpdate can be called multiple times if necessary
    */
    if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
        handleOpenSSLErrors();

    plaintext_len = len;

    /* Finalise the decryption. Further plaintext bytes may be written at
    * this stage.
    */

    // return 1 if decryption successful, otherwise 0
    if(1 == EVP_DecryptFinal_ex(ctx, plaintext + len, &len))
        success = 1;
    plaintext_len += len;


    /* Add the null terminator */
    plaintext[plaintext_len] = 0;

    /* Clean up */
    EVP_CIPHER_CTX_free(ctx);
    //delete [] plaintext;
    return plaintext;
}


size_t calcDecodeLength(char* b64input) {
    size_t len = strlen(b64input), padding = 0;

    if (b64input[len-1] == '=' && b64input[len-2] == '=') //last two chars are =
        padding = 2;
    else if (b64input[len-1] == '=') //last char is =
        padding = 1;
    return (len*3)/4 - padding;
}

void Base64Decode( char* b64message, unsigned char** buffer, size_t* length) {


    BIO *bio, *b64;  // A BIO is an I/O strean abstraction

    int decodeLen = calcDecodeLength(b64message);
    *buffer = (unsigned char*)malloc(decodeLen + 1);
    (*buffer)[decodeLen] = '\0';

    bio = BIO_new_mem_buf(b64message, -1);
    b64 = BIO_new(BIO_f_base64());
    bio = BIO_push(b64, bio);

    //BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer
    *length = BIO_read(bio, *buffer, strlen(b64message));
    BIO_free_all(bio);
}

void initAES(const unsigned char *pass, unsigned char* salt, unsigned char* key, unsigned char* iv )
{
    //initialisatio of key and iv with 0
    bzero(key,sizeof(key));
    bzero(iv,sizeof(iv));

    EVP_BytesToKey(EVP_aes_128_cbc(), EVP_sha1(), salt, pass, strlen(pass), 1, key, iv);
}


int checkPlaintext(char* plaintext, char* result){

    int length = 10; // we just check the first then characters
    return strncmp(plaintext, result, length);

}

int main (void)
{

    // password 12345
    // it took 9 seconds to work out
    char* ciphertext_base64 = (char*) "U2FsdGVkX19q3SzS6GhhzAKgK/YhFVTkM3RLVxxZ+nM6yXdfLZtvhyRR4oGohDotiifnR1iKyitSpiBM3hng+eoFfGbtgCu3Zh9DwIhgfS5A+OTl5a4L7pRFG4yL432HsMGRC1hy1RNPSzA0U5YyWA==\n";

    char* plaintext = "This is the top seret message in parallel computing! Please keep it in a safe place.";
    char dict[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; // changed

    int decryptedtext_len, ciphertext_len, dict_len;

    // cipher (binary) pointer and length
    size_t cipher_len; // size_t is sizeof(type)
    unsigned char* ciphertext;

    unsigned char salt[8];

    ERR_load_crypto_strings();

    Base64Decode(ciphertext_base64, &ciphertext, &cipher_len);

    unsigned char key[16];
    unsigned char iv[16];

    unsigned char plainpassword[] = "00000";
    unsigned char* password = &plainpassword[0];

    // retrive the slater from ciphertext (binary)
    if (strncmp((const char*)ciphertext,"Salted__",8) == 0) { // find the keyword "Salted__"

        memcpy(salt,&ciphertext[8],8);
        ciphertext += 16;
        cipher_len -= 16;

    }

    dict_len = strlen(dict);

    time_t begin = time(NULL);


    for(int i=0; i<dict_len; i++)
        for(int j=0; j<dict_len; j++)
            for(int k=0; k<dict_len; k++)
                for(int l=0; l<dict_len; l++)
                    for(int m=0; m<dict_len; m++){
                        *password = dict[i];
                        *(password+1) = dict[j];
                        *(password+2) = dict[k];
                        *(password+3) = dict[l];
                        *(password+4) = dict[m];


                        initAES(password, salt, key, iv);
                        unsigned char* result = decrypt(ciphertext, cipher_len, key, iv);

                        if (success == 1){
                            if(checkPlaintext(plaintext, result)==0){

                                printf("Password is %s\n", password);

                                time_t end = time(NULL);
                                printf("Time elpased is %ld seconds", (end - begin));

                                return 0;
                            }

                        }

                        free(result);
                    }


    // Clean up

    EVP_cleanup();
    ERR_free_strings();


    return 0;
}

THIS IS THE PARALLEL VERSION:

int main (void)
{

    // password 12345
    // it took 9 seconds to work out
    char* ciphertext_base64 = (char*) "U2FsdGVkX19q3SzS6GhhzAKgK/YhFVTkM3RLVxxZ+nM6yXdfLZtvhyRR4oGohDotiifnR1iKyitSpiBM3hng+eoFfGbtgCu3Zh9DwIhgfS5A+OTl5a4L7pRFG4yL432HsMGRC1hy1RNPSzA0U5YyWA==\n";

    char* plaintext = "This is the top seret message in parallel computing! Please keep it in a safe place.";
    char dict[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; // changed

    int decryptedtext_len, ciphertext_len, dict_len;

    // cipher (binary) pointer and length
    size_t cipher_len; // size_t is sizeof(type)
    unsigned char* ciphertext;

    unsigned char salt[8];

    ERR_load_crypto_strings();

    Base64Decode(ciphertext_base64, &ciphertext, &cipher_len);

    unsigned char key[16];
    unsigned char iv[16];

    omp_set_num_threads(NUM_THREADS);

    double begin = omp_get_wtime();

    
    // retrive the slater from ciphertext (binary)
    if (strncmp((const char*)ciphertext,"Salted__",8) == 0)
    { // find the keyword "Salted__"

        memcpy(salt,&ciphertext[8],8);
        ciphertext += 16;
        cipher_len -= 16;

    }

    dict_len = strlen(dict);

    #pragma omp parallel for shared (key, iv)

    for (int i=0; i<dict_len; i++) 
    {
     unsigned char password[5] = {i};

        for(int j=0; j<dict_len; j++)
        {
         password[1] = dict[j];
            for(int k=0; k<dict_len; k++)
            {
                password[2] = dict[k];
                for(int l=0; l<dict_len; l++)
                {
                  password[3] = dict[l];
                    for(int m=0; m<dict_len; m++)
                    {
                      password[4] = dict[m];
                        {
                            *password = dict[i];
                            *(password+1) = dict[j];
                            *(password+2) = dict[k];
                            *(password+3) = dict[l];
                            *(password+4) = dict[m];


                            initAES(password, salt, key, iv);
                            unsigned char* result = decrypt(ciphertext, cipher_len, key, iv);

                                #pragma omp if(checkPlaintext(plaintext, result)==0)
                                {
                                    
                                    printf("\nPassword is %s\n\n", password);
                                    success == 1;
                                    strcpy(result,password); // Copy thread-private copy to shared variable
                                    time_t end = time(NULL);
                                    printf("\nTime elpased is %ld seconds\n", (end - begin));

                                    exit(0);

                                }

                            free(result);
                        }
                    }
                }
            }
          #pragma omp cancellation point for
        }
    }

// Clean up
EVP_cleanup();
ERR_free_strings();
return 0;
}

Should bring my expected password, but when I run the code it does not output the expected . Kindly help.

解决方案

You have only one buffer for the trial password (plainpassword), and all of the threads in your parallel for are trying to use it at the same time (via pointer password). There are massive data races here, and the resulting behavior is undefined.

One solution would be to declare the buffer inside the parallel loop instead of outside, as variables local to a parallel region are automatically private -- each thread will have its own.

    #pragma omp parallel for shared (key, iv)
    for (int i=0; i<dict_len; i++) {
        unsigned char password[5] = { i };

        for (int j=0; j<dict_len; j++) {
            password[1] = dict[j];

            for (int k=0; k<dict_len; k++) {
                password[2] = dict[k];

                for (int l=0; l<dict_len; l++) {
                    password[3] = dict[l];

                    for (int m=0; m<dict_len; m++) {
                        password[4] = dict[m];

                        // ...

                    }
                }
            }
        }
    }

Note also that

  • there is no apparent benefit in your original code to having both plainpassword and password. The above uses just an array, not a separate pointer as well, and it chooses the name "password" for that.
  • *(x + y), where x and y are primaries, means exactly the same thing as x[y] (and y[x]). The subscript form is easier to read, and that almost always makes it stylistically better.

Update:

I observe also that this code from the parallel version does not make sense, especially as compared to the serial code:

    #pragma omp parallel if (strncmp((const char*)ciphertext,"Salted__",8) == 0) shared(ciphertext, plaintext, ciphertext_base64) private(salt)
    { // find the keyword "Salted__"

        memcpy(salt,&ciphertext[8],8);
        ciphertext += 16;
        cipher_len -= 16;

    }

The original code executes the if statement once per run, and its body at most once per run, so if the parallel version executes the body multiple times, and it has side effects (which it does), then the result of the program will be different. The original code should be restored for this part.

这篇关于串行版本和并行结果不显示相同的输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆