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