1 #include "php_snuffleupagus.h"
2
generate_key(unsigned char * key)3 void generate_key(unsigned char *key) {
4 PHP_SHA256_CTX ctx;
5 const char *user_agent = getenv("HTTP_USER_AGENT");
6 const zend_string *env_var_zend =
7 SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var;
8 const zend_string *encryption_key_zend =
9 SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key;
10 const char *env_var = (env_var_zend ? getenv(ZSTR_VAL(env_var_zend)) : NULL);
11 const char *encryption_key =
12 (encryption_key_zend ? ZSTR_VAL(encryption_key_zend) : NULL);
13
14 assert(32 == crypto_secretbox_KEYBYTES); // 32 is the size of a SHA256.
15 assert(encryption_key); // Encryption key can't be NULL
16
17 PHP_SHA256Init(&ctx);
18
19 if (user_agent) {
20 PHP_SHA256Update(&ctx, (unsigned char *)user_agent, strlen(user_agent));
21 }
22
23 if (env_var) {
24 PHP_SHA256Update(&ctx, (unsigned char *)env_var, strlen(env_var));
25 } else {
26 sp_log_warn("cookie_encryption",
27 "The environment variable '%s' "
28 "is empty, cookies are weakly encrypted",
29 ZSTR_VAL(env_var_zend));
30 }
31
32 if (encryption_key) {
33 PHP_SHA256Update(&ctx, (const unsigned char *)encryption_key,
34 strlen(encryption_key));
35 }
36
37 PHP_SHA256Final((unsigned char *)key, &ctx);
38 }
39
40 // This function return 0 upon success , non-zero otherwise
decrypt_zval(zval * pDest,bool simulation,zend_hash_key * hash_key)41 int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hash_key) {
42 unsigned char key[crypto_secretbox_KEYBYTES] = {0};
43 unsigned char *decrypted;
44 zend_string *debase64;
45 int ret = 0;
46
47 debase64 = php_base64_decode((unsigned char *)(Z_STRVAL_P(pDest)),
48 Z_STRLEN_P(pDest));
49
50 if (ZSTR_LEN(debase64) < crypto_secretbox_NONCEBYTES) {
51 if (true == simulation) {
52 sp_log_simulation(
53 "cookie_encryption",
54 "Buffer underflow tentative detected in cookie encryption handling "
55 "for %s. Using the cookie 'as it' instead of decrypting it",
56 hash_key ? ZSTR_VAL(hash_key->key) : "the session");
57 return ZEND_HASH_APPLY_KEEP;
58 } else {
59 // LCOV_EXCL_START
60 sp_log_drop(
61 "cookie_encryption",
62 "Buffer underflow tentative detected in cookie encryption handling");
63 return ZEND_HASH_APPLY_REMOVE;
64 // LCOV_EXCL_STOP
65 }
66 }
67
68 // LCOV_EXCL_START
69 if (ZSTR_LEN(debase64) + (size_t)crypto_secretbox_ZEROBYTES <
70 ZSTR_LEN(debase64)) {
71 if (true == simulation) {
72 sp_log_simulation(
73 "cookie_encryption",
74 "Integer overflow tentative detected in cookie encryption handling "
75 "for %s. Using the cookie 'as it' instead of decrypting it.",
76 hash_key ? ZSTR_VAL(hash_key->key) : "the session");
77 return ZEND_HASH_APPLY_KEEP;
78 } else {
79 sp_log_drop(
80 "cookie_encryption",
81 "Integer overflow tentative detected in cookie encryption handling.");
82 return ZEND_HASH_APPLY_REMOVE;
83 }
84 }
85 // LCOV_EXCL_STOP
86
87 generate_key(key);
88
89 decrypted = ecalloc(ZSTR_LEN(debase64) + crypto_secretbox_ZEROBYTES, 1);
90 char *backup = ecalloc(ZSTR_LEN(debase64), 1);
91 memcpy(backup, ZSTR_VAL(debase64), ZSTR_LEN(debase64));
92
93 ret = crypto_secretbox_open(
94 decrypted,
95 (unsigned char *)(ZSTR_VAL(debase64) + crypto_secretbox_NONCEBYTES),
96 ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES,
97 (unsigned char *)ZSTR_VAL(debase64), key);
98
99 if (-1 == ret) {
100 if (true == simulation) {
101 sp_log_simulation(
102 "cookie_encryption",
103 "Something went wrong with the decryption of %s. Using the cookie "
104 "'as it' instead of decrypting it",
105 hash_key ? ZSTR_VAL(hash_key->key) : "the session");
106 memcpy(ZSTR_VAL(debase64), backup, ZSTR_LEN(debase64));
107 efree(backup);
108 return ZEND_HASH_APPLY_KEEP;
109 } else {
110 sp_log_warn("cookie_encryption",
111 "Something went wrong with the decryption of %s",
112 hash_key ? ZSTR_VAL(hash_key->key) : "the session");
113 efree(backup);
114 return ZEND_HASH_APPLY_REMOVE;
115 }
116 }
117 efree(backup);
118
119 ZVAL_STRINGL(pDest, (char *)(decrypted + crypto_secretbox_ZEROBYTES),
120 ZSTR_LEN(debase64) - crypto_secretbox_NONCEBYTES - 1 -
121 crypto_secretbox_ZEROBYTES);
122
123 efree(decrypted);
124
125 return ZEND_HASH_APPLY_KEEP;
126 }
127
128 /*
129 ** This function will return the `data` of length `data_len` encrypted in the
130 ** form `base64(nonce | encrypted_data)` (with `|` being the concatenation
131 ** operation).
132 */
encrypt_zval(zend_string * data)133 zend_string *encrypt_zval(zend_string *data) {
134 const size_t encrypted_msg_len =
135 crypto_secretbox_ZEROBYTES + ZSTR_LEN(data) + 1;
136 // FIXME : We know that this len is too long
137 const size_t emsg_and_nonce_len =
138 encrypted_msg_len + crypto_secretbox_NONCEBYTES;
139
140 unsigned char key[crypto_secretbox_KEYBYTES] = {0};
141 unsigned char nonce[crypto_secretbox_NONCEBYTES] = {0};
142 unsigned char *data_to_encrypt = ecalloc(encrypted_msg_len, 1);
143 unsigned char *encrypted_data = ecalloc(emsg_and_nonce_len, 1);
144
145 generate_key(key);
146
147 // Put random bytes in the nonce
148 php_random_bytes(nonce, sizeof(nonce), 0);
149
150 /* tweetnacl's API requires the message to be padded with
151 crypto_secretbox_ZEROBYTES zeroes. */
152 memcpy(data_to_encrypt + crypto_secretbox_ZEROBYTES, ZSTR_VAL(data),
153 ZSTR_LEN(data));
154
155 assert(sizeof(zend_long) <= crypto_secretbox_NONCEBYTES);
156
157 memcpy(encrypted_data, nonce, crypto_secretbox_NONCEBYTES);
158
159 crypto_secretbox(encrypted_data + crypto_secretbox_NONCEBYTES,
160 data_to_encrypt, encrypted_msg_len, nonce, key);
161
162 zend_string *z = php_base64_encode(encrypted_data, emsg_and_nonce_len);
163
164 return z;
165 }
166