2011-09-15 29 views
5

Để kiểm tra đơn vị của một tiện ích mã hóa, tôi muốn có thể buộc máy phát số ngẫu nhiên mã hóa của OpenSSL (cả hai số RAND_bytesRAND_pseudo_bytes) trả về các chuỗi byte có thể lặp lại, dự đoán các thuật toán mã hóa lần lượt có thể dự đoán được và có thể được đưa vào các vectơ thử nghiệm. (Tất cả các tài liệu chính khác đều nằm trong tầm kiểm soát của tôi.)Bắt buộc các RNG của openssl trả về một chuỗi byte lặp lại

Tôi biết điều này hoàn toàn đánh mất an ninh. Điều này sẽ chỉ được sử dụng cho các bài kiểm tra đơn vị.

tôi có thể không chỉ đơn giản gọi RAND_seed với một hạt giống cố định trước mỗi bài kiểm tra, bởi vì (nó xuất hiện) các RNG tự động hạt chính nó từ /dev/urandom liệu tôi có muốn nó hay không, và dù sao RAND_seed không reset sự RNG, nó chỉ thêm hạt giống vào hồ bơi entropy.

Có cách nào để thực hiện việc này không? (Trong thái cực, có vẻ như tôi có thể viết động cơ PRNG của riêng tôi, nhưng tôi muốn nghĩ rằng có một lựa chọn đơn giản hơn.)

+0

Bạn không cần PRNG. Bạn chỉ cần một luồng giá trị dự đoán được; ngay cả một quầy sẽ làm. –

Trả lời

5

Bạn có thể buộc FIPS ANSI X9.31 RNG vào chế độ thử nghiệm khi chạy, nhưng không phải là SSLNG RNG (mặc định). Nếu bạn biên dịch lại OpenSSL với -DPREDICT, RNG mặc định sẽ xuất ra một chuỗi số có thể dự đoán được, nhưng điều đó không thuận tiện lắm.

Chức năng RAND_pseudo_bytes tạo chuỗi số có thể dự đoán được, có nghĩa là nó không thêm entropy vào chính nó tự động như RAND_bytes. Nhưng giống như bạn nhận thấy nó chỉ có thể thêm entropy vào hạt giống, không cung cấp hạt giống một cách rõ ràng, vì vậy giữa các lần chạy chương trình bạn sẽ nhận được các số khác nhau. Cũng không hữu ích.

Nhưng viết động cơ RNG có thể dự đoán của riêng bạn thì không khó. Trong thực tế, tôi sẽ đưa bạn qua nó bằng cách làm cho một động cơ rand với stdlib của rand() tại cốt lõi của nó:

#include <cstdio> 
#include <cstdlib> 
#include <cassert> 
#include <openssl/rand.h> 

// These don't need to do anything if you don't have anything for them to do. 
static void stdlib_rand_cleanup() {} 
static void stdlib_rand_add(const void *buf, int num, double add_entropy) {} 
static int stdlib_rand_status() { return 1; } 

// Seed the RNG. srand() takes an unsigned int, so we just use the first 
// sizeof(unsigned int) bytes in the buffer to seed the RNG. 
static void stdlib_rand_seed(const void *buf, int num) 
{ 
     assert(num >= sizeof(unsigned int)); 
     srand(*((unsigned int *) buf)); 
} 

// Fill the buffer with random bytes. For each byte in the buffer, we generate 
// a random number and clamp it to the range of a byte, 0-255. 
static int stdlib_rand_bytes(unsigned char *buf, int num) 
{ 
     for(int index = 0; index < num; ++index) 
     { 
       buf[index] = rand() % 256; 
     } 
     return 1; 
} 

// Create the table that will link OpenSSL's rand API to our functions. 
RAND_METHOD stdlib_rand_meth = { 
     stdlib_rand_seed, 
     stdlib_rand_bytes, 
     stdlib_rand_cleanup, 
     stdlib_rand_add, 
     stdlib_rand_bytes, 
     stdlib_rand_status 
}; 

// This is a public-scope accessor method for our table. 
RAND_METHOD *RAND_stdlib() { return &stdlib_rand_meth; } 

int main() 
{ 
     // If we're in test mode, tell OpenSSL to use our special RNG. If we 
     // don't call this function, OpenSSL uses the SSLeay RNG. 
     int test_mode = 1; 
     if(test_mode) 
     { 
       RAND_set_rand_method(RAND_stdlib()); 
     } 

     unsigned int seed = 0x00beef00; 
     unsigned int rnum[5]; 

     RAND_seed(&seed, sizeof(seed)); 
     RAND_bytes((unsigned char *)&rnum[0], sizeof(rnum)); 
     printf("%u %u %u %u %u\n", rnum[0], rnum[1], rnum[2], rnum[3], rnum[4]); 

     return 0; 
} 

Mỗi khi bạn chạy chương trình này, nó giống srand() với cùng số lượng và do đó cung cấp cho bạn cùng một chuỗi số ngẫu nhiên mỗi lần.

corruptor:scratch indiv$ g++ rand.cpp -o r -lcrypto -g 
corruptor:scratch indiv$ ./r 
1547399009 981369121 2368920148 925292993 788088604 
corruptor:scratch indiv$ ./r 
1547399009 981369121 2368920148 925292993 788088604 
corruptor:scratch indiv$ 
+0

Cảm ơn! Tài liệu OpenSSL đã cho tôi ấn tượng rằng việc ghi đè RNG nội bộ khó hơn nhiều, đó là lý do tại sao tôi rất miễn cưỡng làm điều đó. – zwol

+0

Tài liệu cũng ngụ ý rằng người ta nên làm điều này với các động cơ hơn là với RAND_METHODs, nhưng tôi không thể tìm ra cách để thực hiện ENGINE của riêng tôi. Bạn có thể bình luận về điều đó? – zwol

+0

@Zack: ENGINE là một vùng chứa và nó hiển thị một giao diện để tải và giải phóng các triển khai thuật toán một cách linh hoạt. Đó là nó. Vì vậy, nếu bạn đã thực hiện một động cơ RNG như một đối tượng ENGINE, trước tiên bạn sẽ thực hiện những gì tôi đã cho bạn ở trên, và sau đó bạn sẽ thực hiện một wrapper ENGINE xung quanh nó để có được tất cả những lợi ích của giao diện ENGINE. Giao diện thuật toán vẫn không thay đổi (các hàm 'RAND_ *' và bảng 'RAND_METHOD'). Nếu bạn triển khai một ENGINE, bạn vẫn nên hỗ trợ người dùng OpenSSL đã biên dịch bằng '-DOPENSSL_NO_ENGINE' bằng cách vẫn cung cấp trình truy cập bảng. – indiv

1

Viết một wrapper quanh thư viện. Sau đó, thay thế nó tại thời gian thử nghiệm cho mô hình của riêng bạn mà trả về giá trị huyền diệu của bạn.

Hãy nhớ rằng, trong một bài kiểm tra đơn vị bạn không cố gắng kiểm tra OpenSSL. Bạn đang cố kiểm tra mã của mình.

+0

Điều này sẽ khó khăn như viết động cơ PRNG của riêng tôi cho OpenSSL. Bắt PRSS của OpenSSL để làm những gì tôi muốn _để được_ dễ dàng hơn nhiều. – zwol

+0

Được rồi, sau đó lừa trình liên kết của dự án thử nghiệm. Thực hiện RAND_bytes() và RAND_pseudo_bytes() của riêng bạn trong một mô-đun nguồn và nó sẽ được liên kết trước khi bất kỳ thư viện nào được đưa vào. –

+0

"Triển khai [PRNG] của riêng bạn" là thứ khó như viết ENGINE của riêng tôi. Điểm của câu hỏi là tôi không muốn phải làm điều đó nếu nó có thể tránh được. – zwol

Các vấn đề liên quan