1--TEST--
2openssl_error_string() tests
3--SKIPIF--
4<?php if (!extension_loaded("openssl")) print "skip"; ?>
5--FILE--
6<?php
7// helper function to check openssl errors
8function expect_openssl_errors($name, $expected_error_codes) {
9    $expected_errors = array_fill_keys($expected_error_codes, false);
10    while (($error_string = openssl_error_string()) !== false) {
11	if (preg_match(",.+:([0-9A-F]+):.+,", $error_string, $m) > 0) {
12            $error_code = $m[1];
13            if (isset($expected_errors[$error_code])) {
14                $expected_errors[$error_code] = true;
15            }
16        }
17    }
18
19    $fail = false;
20    foreach ($expected_errors as $error_code => $error_code_found) {
21        if (!$error_code_found) {
22            $fail = true;
23            echo "$name: no error code $error_code\n";
24        }
25    }
26
27    if (!$fail) {
28        echo "$name: ok\n";
29    }
30}
31
32// helper for debugging errors
33function dump_openssl_errors($name) {
34    echo "\n$name\n";
35    while (($error_string = openssl_error_string()) !== false) {
36        var_dump($error_string);
37    }
38}
39
40// common output file
41$output_file =  __DIR__ . "/openssl_error_string_basic_output.tmp";
42// invalid file for read is something that does not exist in current directory
43$invalid_file_for_read = __DIR__ . "/invalid_file_for_read_operation.txt";
44// invalid file for is the test dir as writting file to existing dir should alway fail
45$invalid_file_for_write = __DIR__;
46// crt file
47$crt_file = "file://" . __DIR__ . "/cert.crt";
48// csr file
49$csr_file = "file://" . __DIR__ . "/cert.csr";
50// public key file
51$public_key_file = "file://" .__DIR__ . "/public.key";
52// private key file
53$private_key_file = "file://" .__DIR__ . "/private_rsa_1024.key";
54// private key file with password (password is 'php')
55$private_key_file_with_pass = "file://" .__DIR__ . "/private_rsa_2048_pass_php.key";
56
57// ENCRYPTION
58$data = "test";
59$method = "AES-128-ECB";
60$enc_key = str_repeat('x', 40);
61// error because password is longer then key length and
62// EVP_CIPHER_CTX_set_key_length fails for AES
63openssl_encrypt($data, $method, $enc_key);
64$enc_error = openssl_error_string();
65var_dump($enc_error);
66// make sure that error is cleared now
67var_dump(openssl_error_string());
68// internally OpenSSL ERR won't save more than 15 (16 - 1) errors so lets test it
69for ($i = 0; $i < 20; $i++) {
70	openssl_encrypt($data, $method, $enc_key);
71}
72$error_queue_size = 0;
73while (($enc_error_new = openssl_error_string()) !== false) {
74    if ($enc_error_new !== $enc_error) {
75        echo "The new encoding error doesn't match the expected one\n";
76    }
77    ++$error_queue_size;
78}
79var_dump($error_queue_size);
80echo "\n";
81
82$is_111 = OPENSSL_VERSION_NUMBER >= 0x10101000;
83$err_pem_no_start_line = $is_111 ? '0909006C': '0906D06C';
84
85// PKEY
86echo "PKEY errors\n";
87// file for pkey (file:///) fails when opennig (BIO_new_file)
88@openssl_pkey_export_to_file("file://" . $invalid_file_for_read, $output_file);
89expect_openssl_errors('openssl_pkey_export_to_file opening', ['02001002', '2006D080']);
90// file or private pkey is not correct PEM - failing PEM_read_bio_PrivateKey
91@openssl_pkey_export_to_file($csr_file, $output_file);
92expect_openssl_errors('openssl_pkey_export_to_file pem', [$err_pem_no_start_line]);
93// file to export cannot be written
94@openssl_pkey_export_to_file($private_key_file, $invalid_file_for_write);
95expect_openssl_errors('openssl_pkey_export_to_file write', ['2006D002']);
96// succesful export
97@openssl_pkey_export($private_key_file_with_pass, $out, 'wrong pwd');
98expect_openssl_errors('openssl_pkey_export', ['06065064', '0906A065']);
99// invalid x509 for getting public key
100@openssl_pkey_get_public($private_key_file);
101expect_openssl_errors('openssl_pkey_get_public', [$err_pem_no_start_line]);
102// private encrypt with unknown padding
103@openssl_private_encrypt("data", $crypted, $private_key_file, 1000);
104expect_openssl_errors('openssl_private_encrypt', ['04066076']);
105// private decrypt with failed padding check
106@openssl_private_decrypt("data", $crypted, $private_key_file);
107expect_openssl_errors('openssl_private_decrypt', ['04065072']);
108// public encrypt and decrypt with failed padding check and padding
109@openssl_public_encrypt("data", $crypted, $public_key_file, 1000);
110@openssl_public_decrypt("data", $crypted, $public_key_file);
111expect_openssl_errors('openssl_private_(en|de)crypt padding', [$err_pem_no_start_line, '04068076', '04067072']);
112
113// X509
114echo "X509 errors\n";
115// file for x509 (file:///) fails when opennig (BIO_new_file)
116@openssl_x509_export_to_file("file://" . $invalid_file_for_read, $output_file);
117expect_openssl_errors('openssl_x509_export_to_file open', ['02001002']);
118// file or str cert is not correct PEM - failing PEM_read_bio_X509 or PEM_ASN1_read_bio
119@openssl_x509_export_to_file($csr_file, $output_file);
120expect_openssl_errors('openssl_x509_export_to_file pem', [$err_pem_no_start_line]);
121// file to export cannot be written
122@openssl_x509_export_to_file($crt_file, $invalid_file_for_write);
123expect_openssl_errors('openssl_x509_export_to_file write', ['2006D002']);
124// checking purpose fails because there is no such purpose 1000
125@openssl_x509_checkpurpose($crt_file, 1000);
126expect_openssl_errors('openssl_x509_checkpurpose purpose', ['0B086079']);
127
128// CSR
129echo "CSR errors\n";
130// file for csr (file:///) fails when opennig (BIO_new_file)
131@openssl_csr_get_subject("file://" . $invalid_file_for_read);
132expect_openssl_errors('openssl_csr_get_subject open', ['02001002', '2006D080']);
133// file or str csr is not correct PEM - failing PEM_read_bio_X509_REQ
134@openssl_csr_get_subject($crt_file);
135expect_openssl_errors('openssl_csr_get_subjec pem', [$err_pem_no_start_line]);
136
137// other possible cuases that are difficult to catch:
138// - ASN1_STRING_to_UTF8 fails in add_assoc_name_entry
139// - invalid php_x509_request field (NULL) would cause error with CONF_get_string
140
141?>
142--CLEAN--
143<?php
144$output_file =  __DIR__ . "/openssl_error_string_basic_output.tmp";
145if (is_file($output_file)) {
146	unlink($output_file);
147}
148?>
149--EXPECT--
150string(89) "error:0607A082:digital envelope routines:EVP_CIPHER_CTX_set_key_length:invalid key length"
151bool(false)
152int(15)
153
154PKEY errors
155openssl_pkey_export_to_file opening: ok
156openssl_pkey_export_to_file pem: ok
157openssl_pkey_export_to_file write: ok
158openssl_pkey_export: ok
159openssl_pkey_get_public: ok
160openssl_private_encrypt: ok
161openssl_private_decrypt: ok
162openssl_private_(en|de)crypt padding: ok
163X509 errors
164openssl_x509_export_to_file open: ok
165openssl_x509_export_to_file pem: ok
166openssl_x509_export_to_file write: ok
167openssl_x509_checkpurpose purpose: ok
168CSR errors
169openssl_csr_get_subject open: ok
170openssl_csr_get_subjec pem: ok
171