paivana-httpd_cookie.c (6665B)
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.abs_value_us = u * 1000LLU * 1000LLU; 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 = strncasecmp (website, 158 "https://", 159 strlen ("https://")); 160 struct GNUNET_TIME_Relative duration 161 = GNUNET_TIME_absolute_get_remaining (cur_time.abs_time); 162 163 if (! PH_global_cookie) 164 { 165 const char *dslash = strstr (website, 166 "//"); 167 if (NULL != dslash) 168 url = strchr (dslash + 2, 169 '/'); 170 } 171 compute_cookie_hash (cur_time, 172 website, 173 ca_len, 174 ca, 175 &h); 176 end = GNUNET_STRINGS_data_to_string (&h, 177 sizeof (h), 178 cstr, 179 sizeof (cstr)); 180 *end = '\0'; 181 GNUNET_asprintf ( 182 &res, 183 "Paivana-Cookie=%llu-%s; %sPath=%s; Max-Age=%llu;", 184 (unsigned long long) (cur_time.abs_time.abs_value_us / 1000LLU / 1000LLU), 185 cstr, 186 use_https 187 ? "Secure; " 188 : "", 189 url, 190 (unsigned long long) (duration.rel_value_us / 1000 / 1000)); 191 return res; 192 } 193 194 195 char * 196 PAIVANA_HTTPD_compute_paivana_id (struct GNUNET_TIME_Timestamp cur_time, 197 const char *website, 198 const struct PAIVANA_Nonce *nonce) 199 { 200 struct GNUNET_TIME_AbsoluteNBO e; 201 char *res; 202 gcry_md_hd_t hd; 203 const void *sha256; 204 char *cstr; 205 size_t clen; 206 207 e = GNUNET_TIME_absolute_hton (cur_time.abs_time); 208 GNUNET_assert (0 == 209 gcry_md_open (&hd, 210 GCRY_MD_SHA256, 211 0)); 212 gcry_md_write (hd, 213 nonce, 214 sizeof (*nonce)); 215 gcry_md_write (hd, 216 website, 217 strlen (website) + 1); 218 gcry_md_write (hd, 219 &e, 220 sizeof (e)); 221 sha256 = gcry_md_read (hd, 222 0); 223 cstr = NULL; 224 clen = GNUNET_STRINGS_base64url_encode (sha256, 225 256 / 8, 226 &cstr); 227 GNUNET_asprintf ( 228 &res, 229 "%llu-%.*s", 230 (unsigned long long) (cur_time.abs_time.abs_value_us / 1000LLU / 1000LLU), 231 (int) clen, 232 cstr); 233 GNUNET_free (cstr); 234 gcry_md_close (hd); 235 return res; 236 }