paivana-httpd_cookie.c (6885B)
1 /* 2 This file is part of GNU Taler 3 Copyright (C) 2026 Taler Systems SA 4 5 GNU Taler is free software; you can redistribute it and/or 6 modify it under the terms of the GNU General Public License 7 as published by the Free Software Foundation; either version 8 3, or (at your option) any later version. 9 10 GNU Taler is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public 16 License along with GNU Taler; see the file COPYING. If not, 17 write to the Free Software Foundation, Inc., 51 Franklin 18 Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 */ 20 21 /** 22 * @author Christian Grothoff 23 * @file src/backend/paivana-httpd_cookie.c 24 * @brief Cookie computation logic for paivana 25 */ 26 #include "platform.h" 27 #include <curl/curl.h> 28 #include <gcrypt.h> 29 #include <gnunet/gnunet_util_lib.h> 30 #include <taler/taler_mhd_lib.h> 31 #include "paivana-httpd_cookie.h" 32 #include "paivana-httpd.h" 33 34 35 /** 36 * Secret for the cookie generation. 37 */ 38 struct GNUNET_HashCode paivana_secret; 39 40 41 /** 42 * Compute access cookie hash for the given @a cur_time, the 43 * @a website and @a ca. 44 * 45 * @param cur_time current time used in the cookie 46 * @param website URL the cookie is valid for 47 * @param ca_len number of bytes in @a ca 48 * @param ca client (IP) address 49 * @param[out] c set to the cookie hash 50 */ 51 static void 52 compute_cookie_hash (struct GNUNET_TIME_Timestamp cur_time, 53 const char *website, 54 size_t ca_len, 55 const void *ca, 56 struct GNUNET_HashCode *c) 57 { 58 struct GNUNET_TIME_AbsoluteNBO e; 59 60 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 61 "Computing cookie for %s at %llu and client %s\n", 62 website, 63 (unsigned long long) cur_time.abs_time.abs_value_us, 64 TALER_b2s (ca, 65 ca_len)); 66 e = GNUNET_TIME_absolute_hton (cur_time.abs_time); 67 if (PH_global_cookie) 68 website = ""; 69 GNUNET_assert (GNUNET_YES == 70 GNUNET_CRYPTO_hkdf_gnunet ( 71 c, /* result */ 72 sizeof (*c), 73 &e, /* salt */ 74 sizeof (e), 75 &paivana_secret, /* source key material */ 76 sizeof (paivana_secret), 77 GNUNET_CRYPTO_kdf_arg (website, 78 strlen (website) + 1), 79 GNUNET_CRYPTO_kdf_arg (ca, 80 ca_len))); 81 } 82 83 84 bool 85 PAIVANA_HTTPD_check_cookie (const char *cookie, 86 const char *website, 87 size_t ca_len, 88 const void *ca) 89 { 90 const char *dash; 91 unsigned long long u; 92 struct GNUNET_HashCode h; 93 struct GNUNET_HashCode c; 94 struct GNUNET_TIME_Timestamp a; 95 96 dash = strchr (cookie, 97 '-'); 98 if (NULL == dash) 99 { 100 GNUNET_break_op (0); 101 return false; 102 } 103 dash++; 104 if (1 != 105 sscanf (cookie, 106 "%llu-", 107 &u)) 108 { 109 GNUNET_break_op (0); 110 return false; 111 } 112 a.abs_time = GNUNET_TIME_absolute_from_s (u); 113 if (GNUNET_TIME_absolute_is_past (a.abs_time)) 114 { 115 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 116 "Cookie expired %s ago\n", 117 GNUNET_TIME_relative2s ( 118 GNUNET_TIME_absolute_get_duration (a.abs_time), 119 true)); 120 return false; 121 } 122 if (GNUNET_OK != 123 GNUNET_STRINGS_string_to_data (dash, 124 strlen (dash), 125 &c, 126 sizeof (c))) 127 { 128 GNUNET_break_op (0); 129 return false; 130 } 131 compute_cookie_hash (a, 132 website, 133 ca_len, 134 ca, 135 &h); 136 if (0 == 137 GNUNET_memcmp (&c, 138 &h)) 139 return true; 140 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 141 "Cookie hash does not match!\n"); 142 return false; 143 } 144 145 146 char * 147 PAIVANA_HTTPD_compute_cookie (struct GNUNET_TIME_Timestamp cur_time, 148 const char *website, 149 size_t ca_len, 150 const void *ca) 151 { 152 struct GNUNET_HashCode h; 153 char *end; 154 char cstr[128]; 155 char *res; 156 const char *url = "/"; 157 bool use_https = (0 == 158 strncasecmp (website, 159 "https://", 160 strlen ("https://"))); 161 struct GNUNET_TIME_Relative duration 162 = GNUNET_TIME_absolute_get_remaining (cur_time.abs_time); 163 164 if (! PH_global_cookie) 165 { 166 const char *dslash = strstr (website, 167 "//"); 168 if (NULL != dslash) 169 url = strchr (dslash + 2, 170 '/'); 171 if (NULL == url) 172 url = "/"; 173 } 174 compute_cookie_hash (cur_time, 175 website, 176 ca_len, 177 ca, 178 &h); 179 end = GNUNET_STRINGS_data_to_string (&h, 180 sizeof (h), 181 cstr, 182 sizeof (cstr)); 183 *end = '\0'; 184 GNUNET_asprintf ( 185 &res, 186 "Paivana-Cookie=%llu-%s; %sPath=%s; Max-Age=%llu; HttpOnly;", 187 (unsigned long long) (cur_time.abs_time.abs_value_us / 1000LLU / 1000LLU), 188 cstr, 189 use_https 190 ? "Secure; " 191 : "", 192 url, 193 (unsigned long long) (duration.rel_value_us / 1000 / 1000)); 194 return res; 195 } 196 197 198 char * 199 PAIVANA_HTTPD_compute_paivana_id (struct GNUNET_TIME_Timestamp cur_time, 200 const char *website, 201 const struct PAIVANA_Nonce *nonce) 202 { 203 struct GNUNET_TIME_AbsoluteNBO e; 204 char *res; 205 gcry_md_hd_t hd; 206 const void *sha256; 207 char *cstr; 208 size_t clen; 209 210 e = GNUNET_TIME_absolute_hton (cur_time.abs_time); 211 GNUNET_assert (0 == 212 gcry_md_open (&hd, 213 GCRY_MD_SHA256, 214 0)); 215 gcry_md_write (hd, 216 nonce, 217 sizeof (*nonce)); 218 gcry_md_write (hd, 219 website, 220 strlen (website) + 1); 221 gcry_md_write (hd, 222 &e, 223 sizeof (e)); 224 sha256 = gcry_md_read (hd, 225 0); 226 cstr = NULL; 227 clen = GNUNET_STRINGS_base64url_encode (sha256, 228 256 / 8, 229 &cstr); 230 GNUNET_asprintf ( 231 &res, 232 "%llu-%.*s", 233 (unsigned long long) (cur_time.abs_time.abs_value_us / 1000LLU / 1000LLU), 234 (int) clen, 235 cstr); 236 GNUNET_free (cstr); 237 gcry_md_close (hd); 238 return res; 239 }