test_sync_signatures.c (13493B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file util/test_sync_signatures.c 18 * @brief tests for the sync signatures and the object reference hash 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include <gnunet/gnunet_util_lib.h> 23 #include "sync/sync_service.h" 24 #include "sync/sync_signatures.h" 25 #include "sync/sync_util.h" 26 27 28 #define FAILIF(cond) \ 29 do { \ 30 if (! (cond)) break; \ 31 GNUNET_break (0); \ 32 return 1; \ 33 } while (0) 34 35 36 /** 37 * Check #SYNC_object_refs_hash(): insensitive to the order the 38 * references arrive in, sensitive to every UID and adjustment. 39 * 40 * @return 0 on success 41 */ 42 static int 43 test_refs_hash (void) 44 { 45 struct SYNC_ObjectUID uids[3]; 46 struct SYNC_ObjectUID shuffled[3]; 47 const int16_t incs[3] = { 1, -2, 3 }; 48 int16_t shuffled_incs[3]; 49 struct GNUNET_HashCode h; 50 struct GNUNET_HashCode h2; 51 52 for (unsigned int i = 0; i < 3; i++) 53 GNUNET_CRYPTO_random_block (&uids[i], 54 sizeof (uids[i])); 55 56 /* An empty reference set hashes deterministically */ 57 FAILIF (GNUNET_OK != 58 SYNC_object_refs_hash (0, 59 NULL, 60 NULL, 61 &h)); 62 FAILIF (GNUNET_OK != 63 SYNC_object_refs_hash (0, 64 uids, 65 incs, 66 &h2)); 67 FAILIF (0 != GNUNET_memcmp (&h, 68 &h2)); 69 70 /* ... and differs from any non-empty set */ 71 FAILIF (GNUNET_OK != 72 SYNC_object_refs_hash (3, 73 uids, 74 incs, 75 &h)); 76 FAILIF (0 == GNUNET_memcmp (&h, 77 &h2)); 78 79 /* JSON object member order is not preserved, so reordering the 80 references must not change the hash */ 81 shuffled[0] = uids[2]; 82 shuffled[1] = uids[0]; 83 shuffled[2] = uids[1]; 84 shuffled_incs[0] = incs[2]; 85 shuffled_incs[1] = incs[0]; 86 shuffled_incs[2] = incs[1]; 87 FAILIF (GNUNET_OK != 88 SYNC_object_refs_hash (3, 89 shuffled, 90 shuffled_incs, 91 &h2)); 92 FAILIF (0 != GNUNET_memcmp (&h, 93 &h2)); 94 95 /* Changing an adjustment changes the hash */ 96 shuffled_incs[0] = (int16_t) (shuffled_incs[0] + 1); 97 FAILIF (GNUNET_OK != 98 SYNC_object_refs_hash (3, 99 shuffled, 100 shuffled_incs, 101 &h2)); 102 FAILIF (0 == GNUNET_memcmp (&h, 103 &h2)); 104 105 /* Changing a UID changes the hash */ 106 shuffled_incs[0] = incs[2]; 107 GNUNET_CRYPTO_random_block (&shuffled[0], 108 sizeof (shuffled[0])); 109 FAILIF (GNUNET_OK != 110 SYNC_object_refs_hash (3, 111 shuffled, 112 shuffled_incs, 113 &h2)); 114 FAILIF (0 == GNUNET_memcmp (&h, 115 &h2)); 116 117 /* Dropping a reference changes the hash */ 118 FAILIF (GNUNET_OK != 119 SYNC_object_refs_hash (2, 120 uids, 121 incs, 122 &h2)); 123 FAILIF (0 == GNUNET_memcmp (&h, 124 &h2)); 125 126 /* A repeated UID cannot be carried on the wire */ 127 shuffled[0] = uids[0]; 128 shuffled[1] = uids[0]; 129 shuffled[2] = uids[1]; 130 FAILIF (GNUNET_SYSERR != 131 SYNC_object_refs_hash (3, 132 shuffled, 133 shuffled_incs, 134 &h2)); 135 return 0; 136 } 137 138 139 /** 140 * Check that references survive the wire encoding with their hash 141 * intact; this is what makes the signature verify server-side. 142 * 143 * @return 0 on success 144 */ 145 static int 146 test_refs_json_roundtrip (void) 147 { 148 struct SYNC_ObjectUID uids[3]; 149 const int16_t incs[3] = { 5, -1, 32767 }; 150 struct GNUNET_HashCode h; 151 struct GNUNET_HashCode h2; 152 json_t *refs; 153 size_t parsed_len; 154 struct SYNC_ObjectUID *parsed_uids; 155 int16_t *parsed_incs; 156 157 for (unsigned int i = 0; i < 3; i++) 158 GNUNET_CRYPTO_random_block (&uids[i], 159 sizeof (uids[i])); 160 FAILIF (GNUNET_OK != 161 SYNC_object_refs_hash (3, 162 uids, 163 incs, 164 &h)); 165 refs = SYNC_object_refs_serialize (3, 166 uids, 167 incs); 168 FAILIF (NULL == refs); 169 FAILIF (GNUNET_OK != 170 SYNC_object_refs_parse (refs, 171 &parsed_len, 172 &parsed_uids, 173 &parsed_incs)); 174 json_decref (refs); 175 FAILIF (3 != parsed_len); 176 FAILIF (GNUNET_OK != 177 SYNC_object_refs_hash (parsed_len, 178 parsed_uids, 179 parsed_incs, 180 &h2)); 181 GNUNET_free (parsed_uids); 182 GNUNET_free (parsed_incs); 183 FAILIF (0 != GNUNET_memcmp (&h, 184 &h2)); 185 return 0; 186 } 187 188 189 /** 190 * Check that the block upload signature covers the object references. 191 * 192 * @return 0 on success 193 */ 194 static int 195 test_upload_sig (void) 196 { 197 struct SYNC_AccountPrivateKeyP priv; 198 struct SYNC_AccountPublicKeyP pub; 199 struct SYNC_AccountSignatureP sig; 200 struct SYNC_BlockNonce nonce; 201 struct SYNC_BlockNonce prev; 202 struct GNUNET_HashCode old_hash; 203 struct GNUNET_HashCode new_hash; 204 struct SYNC_ObjectUID uid; 205 int16_t inc = 1; 206 int16_t tampered_inc = 2; 207 struct GNUNET_HashCode refs_hash; 208 struct GNUNET_HashCode other_refs_hash; 209 210 GNUNET_CRYPTO_eddsa_key_create (&priv.eddsa_priv); 211 GNUNET_CRYPTO_eddsa_key_get_public (&priv.eddsa_priv, 212 &pub.eddsa_pub); 213 GNUNET_CRYPTO_random_block (&nonce, 214 sizeof (nonce)); 215 GNUNET_CRYPTO_random_block (&prev, 216 sizeof (prev)); 217 GNUNET_CRYPTO_random_block (&uid, 218 sizeof (uid)); 219 GNUNET_CRYPTO_hash ("old", 3, &old_hash); 220 GNUNET_CRYPTO_hash ("new", 3, &new_hash); 221 FAILIF (GNUNET_OK != 222 SYNC_object_refs_hash (1, 223 &uid, 224 &inc, 225 &refs_hash)); 226 FAILIF (GNUNET_OK != 227 SYNC_object_refs_hash (1, 228 &uid, 229 &tampered_inc, 230 &other_refs_hash)); 231 SYNC_block_upload_sign (&priv, 232 &nonce, 233 &prev, 234 NULL, 235 &old_hash, 236 &new_hash, 237 &refs_hash, 238 &sig); 239 FAILIF (GNUNET_OK != 240 SYNC_block_upload_verify (&pub, 241 &nonce, 242 &prev, 243 NULL, 244 &old_hash, 245 &new_hash, 246 &refs_hash, 247 &sig)); 248 /* Altering the adjustment invalidates the signature */ 249 FAILIF (GNUNET_OK == 250 SYNC_block_upload_verify (&pub, 251 &nonce, 252 &prev, 253 NULL, 254 &old_hash, 255 &new_hash, 256 &other_refs_hash, 257 &sig)); 258 /* So does stripping the references altogether */ 259 FAILIF (GNUNET_OK != 260 SYNC_object_refs_hash (0, 261 NULL, 262 NULL, 263 &other_refs_hash)); 264 FAILIF (GNUNET_OK == 265 SYNC_block_upload_verify (&pub, 266 &nonce, 267 &prev, 268 NULL, 269 &old_hash, 270 &new_hash, 271 &other_refs_hash, 272 &sig)); 273 /* The rest of the signed data is still covered */ 274 FAILIF (GNUNET_OK == 275 SYNC_block_upload_verify (&pub, 276 &nonce, 277 NULL, 278 NULL, 279 &old_hash, 280 &new_hash, 281 &refs_hash, 282 &sig)); 283 return 0; 284 } 285 286 287 /** 288 * Check that the block delete signature covers the object references. 289 * 290 * @return 0 on success 291 */ 292 static int 293 test_delete_sig (void) 294 { 295 struct SYNC_AccountPrivateKeyP priv; 296 struct SYNC_AccountPublicKeyP pub; 297 struct SYNC_AccountSignatureP sig; 298 struct SYNC_BlockNonce nonce; 299 struct GNUNET_HashCode block_hash; 300 struct SYNC_ObjectUID uid; 301 int16_t dec = -1; 302 struct GNUNET_HashCode refs_hash; 303 struct GNUNET_HashCode empty_refs_hash; 304 305 GNUNET_CRYPTO_eddsa_key_create (&priv.eddsa_priv); 306 GNUNET_CRYPTO_eddsa_key_get_public (&priv.eddsa_priv, 307 &pub.eddsa_pub); 308 GNUNET_CRYPTO_random_block (&nonce, 309 sizeof (nonce)); 310 GNUNET_CRYPTO_random_block (&uid, 311 sizeof (uid)); 312 GNUNET_CRYPTO_hash ("block", 5, &block_hash); 313 FAILIF (GNUNET_OK != 314 SYNC_object_refs_hash (1, 315 &uid, 316 &dec, 317 &refs_hash)); 318 FAILIF (GNUNET_OK != 319 SYNC_object_refs_hash (0, 320 NULL, 321 NULL, 322 &empty_refs_hash)); 323 SYNC_block_delete_sign (&priv, 324 &nonce, 325 NULL, 326 NULL, 327 &block_hash, 328 &refs_hash, 329 &sig); 330 FAILIF (GNUNET_OK != 331 SYNC_block_delete_verify (&pub, 332 &nonce, 333 NULL, 334 NULL, 335 &block_hash, 336 &refs_hash, 337 &sig)); 338 FAILIF (GNUNET_OK == 339 SYNC_block_delete_verify (&pub, 340 &nonce, 341 NULL, 342 NULL, 343 &block_hash, 344 &empty_refs_hash, 345 &sig)); 346 return 0; 347 } 348 349 350 /** 351 * Check the object upload signature round trip. 352 * 353 * @return 0 on success 354 */ 355 static int 356 test_object_sig (void) 357 { 358 struct SYNC_AccountPrivateKeyP priv; 359 struct SYNC_AccountPublicKeyP pub; 360 struct SYNC_AccountSignatureP sig; 361 struct SYNC_ObjectUID uid; 362 struct SYNC_ObjectUID other_uid; 363 struct GNUNET_HashCode hash; 364 365 GNUNET_CRYPTO_eddsa_key_create (&priv.eddsa_priv); 366 GNUNET_CRYPTO_eddsa_key_get_public (&priv.eddsa_priv, 367 &pub.eddsa_pub); 368 GNUNET_CRYPTO_random_block (&uid, 369 sizeof (uid)); 370 GNUNET_CRYPTO_random_block (&other_uid, 371 sizeof (other_uid)); 372 GNUNET_CRYPTO_hash ("object", 6, &hash); 373 SYNC_object_upload_sign (&priv, 374 &uid, 375 &hash, 376 &sig); 377 FAILIF (GNUNET_OK != 378 SYNC_object_upload_verify (&pub, 379 &uid, 380 &hash, 381 &sig)); 382 FAILIF (GNUNET_OK == 383 SYNC_object_upload_verify (&pub, 384 &other_uid, 385 &hash, 386 &sig)); 387 return 0; 388 } 389 390 391 int 392 main (int argc, 393 char *const argv[]) 394 { 395 (void) argc; 396 GNUNET_log_setup (argv[0], 397 "WARNING", 398 NULL); 399 if (0 != test_refs_hash ()) 400 return 1; 401 if (0 != test_refs_json_roundtrip ()) 402 return 1; 403 if (0 != test_upload_sig ()) 404 return 1; 405 if (0 != test_delete_sig ()) 406 return 1; 407 if (0 != test_object_sig ()) 408 return 1; 409 return 0; 410 } 411 412 413 /* end of test_sync_signatures.c */