Line data Source code
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 exchangedb/test_exchange_sign_keys.c
18 : * @brief tests for the exchangedb functions whose primary table is
19 : * `exchange_sign_keys`
20 : * @author Christian Grothoff
21 : *
22 : * Covers #TALER_EXCHANGEDB_insert_signkey(),
23 : * #TALER_EXCHANGEDB_get_signkey() and
24 : * #TALER_EXCHANGEDB_iterate_active_signkeys().
25 : *
26 : * `exchange_sign_keys` has no foreign keys. The interesting part is what
27 : * "active" means for the iterator: not expired for signing, and not
28 : * revoked. The revocation half is covered by test_signkey_revocations.c.
29 : */
30 : #include "test_common.h"
31 : #include "exchange-database/insert_signkey.h"
32 : #include "exchange-database/get_signkey.h"
33 : #include "exchange-database/iterate_active_signkeys.h"
34 :
35 :
36 : /**
37 : * Build meta data for a signing key that is valid from an hour ago until
38 : * @a sign_offset from now.
39 : *
40 : * @param sign_offset how long the key remains valid for signing; may be
41 : * negative to build an already expired key
42 : * @return the meta data
43 : */
44 : static struct TALER_EXCHANGEDB_SignkeyMetaData
45 2 : make_meta (struct GNUNET_TIME_Relative sign_offset)
46 : {
47 2 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
48 : struct TALER_EXCHANGEDB_SignkeyMetaData meta;
49 :
50 : meta.start
51 2 : = GNUNET_TIME_absolute_to_timestamp (
52 : GNUNET_TIME_absolute_subtract (now,
53 : GNUNET_TIME_UNIT_HOURS));
54 : meta.expire_sign
55 2 : = GNUNET_TIME_absolute_to_timestamp (
56 : GNUNET_TIME_absolute_add (now,
57 : sign_offset));
58 : meta.expire_legal
59 2 : = GNUNET_TIME_absolute_to_timestamp (
60 : GNUNET_TIME_absolute_add (now,
61 : GNUNET_TIME_relative_multiply (
62 : GNUNET_TIME_UNIT_HOURS,
63 : 24)));
64 2 : return meta;
65 : }
66 :
67 :
68 : /**
69 : * Nothing is known about a signing key that was never inserted.
70 : *
71 : * @param pg the database context
72 : * @return 0 on success
73 : */
74 : static int
75 1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
76 : {
77 : struct TALER_ExchangePublicKeyP exchange_pub;
78 : struct TALER_EXCHANGEDB_SignkeyMetaData meta;
79 :
80 1 : TDB_FILL (exchange_pub,
81 : 1);
82 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
83 : TALER_EXCHANGEDB_get_signkey (pg,
84 : &exchange_pub,
85 : &meta));
86 1 : FAILIF (0 != TDB_count (pg,
87 : "FROM exchange_sign_keys"));
88 1 : return 0;
89 : }
90 :
91 :
92 : /**
93 : * Closure for #count_signkeys_cb().
94 : */
95 : struct CountContext
96 : {
97 : /**
98 : * Key we are looking for.
99 : */
100 : const struct TALER_ExchangePublicKeyP *exchange_pub;
101 :
102 : /**
103 : * How many keys did the callback see in total?
104 : */
105 : unsigned int total;
106 :
107 : /**
108 : * How many times did we see @e exchange_pub?
109 : */
110 : unsigned int matched;
111 :
112 : /**
113 : * Set if @e exchange_pub was reported with unexpected data.
114 : */
115 : bool mismatch;
116 :
117 : /**
118 : * Meta data reported for @e exchange_pub.
119 : */
120 : struct TALER_EXCHANGEDB_SignkeyMetaData meta;
121 : };
122 :
123 :
124 : /**
125 : * Callback for #TALER_EXCHANGEDB_iterate_active_signkeys().
126 : *
127 : * @param cls a `struct CountContext *`
128 : * @param exchange_pub public key of the exchange
129 : * @param meta meta data of @a exchange_pub
130 : * @param master_sig master signature over @a exchange_pub
131 : */
132 : static void
133 2 : count_signkeys_cb (void *cls,
134 : const struct TALER_ExchangePublicKeyP *exchange_pub,
135 : const struct TALER_EXCHANGEDB_SignkeyMetaData *meta,
136 : const struct TALER_MasterSignatureP *master_sig)
137 : {
138 2 : struct CountContext *ctx = cls;
139 : struct TALER_MasterSignatureP expected;
140 :
141 2 : ctx->total++;
142 2 : if (0 != GNUNET_memcmp (exchange_pub,
143 : ctx->exchange_pub))
144 1 : return;
145 1 : ctx->matched++;
146 1 : ctx->meta = *meta;
147 1 : TDB_FILL (expected,
148 : 2);
149 1 : if (0 != GNUNET_memcmp (master_sig,
150 : &expected))
151 0 : ctx->mismatch = true;
152 : }
153 :
154 :
155 : /**
156 : * Insert a signing key and read it back.
157 : *
158 : * @param pg the database context
159 : * @return 0 on success
160 : */
161 : static int
162 1 : check_insert_and_lookup (struct TALER_EXCHANGEDB_PostgresContext *pg)
163 : {
164 : struct TALER_ExchangePublicKeyP exchange_pub;
165 : struct TALER_MasterSignatureP master_sig;
166 : struct TALER_EXCHANGEDB_SignkeyMetaData meta;
167 : struct TALER_EXCHANGEDB_SignkeyMetaData got;
168 : struct CountContext ctx;
169 :
170 1 : TDB_FILL (exchange_pub,
171 : 2);
172 1 : TDB_FILL (master_sig,
173 : 2);
174 1 : meta = make_meta (GNUNET_TIME_UNIT_HOURS);
175 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
176 : TALER_EXCHANGEDB_insert_signkey (pg,
177 : &exchange_pub,
178 : &meta,
179 : &master_sig));
180 1 : memset (&got,
181 : 0,
182 : sizeof (got));
183 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
184 : TALER_EXCHANGEDB_get_signkey (pg,
185 : &exchange_pub,
186 : &got));
187 1 : FAILIF (0 != GNUNET_memcmp (&got,
188 : &meta));
189 :
190 1 : memset (&ctx,
191 : 0,
192 : sizeof (ctx));
193 1 : ctx.exchange_pub = &exchange_pub;
194 1 : FAILIF (0 >=
195 : TALER_EXCHANGEDB_iterate_active_signkeys (pg,
196 : &count_signkeys_cb,
197 : &ctx));
198 1 : FAILIF (1 != ctx.matched);
199 1 : FAILIF (ctx.mismatch);
200 1 : FAILIF (0 != GNUNET_memcmp (&ctx.meta,
201 : &meta));
202 1 : return 0;
203 : }
204 :
205 :
206 : /**
207 : * A key whose signing period is over is on file but not active.
208 : *
209 : * @param pg the database context
210 : * @return 0 on success
211 : */
212 : static int
213 1 : check_expired_is_inactive (struct TALER_EXCHANGEDB_PostgresContext *pg)
214 : {
215 : struct TALER_ExchangePublicKeyP exchange_pub;
216 : struct TALER_MasterSignatureP master_sig;
217 : struct TALER_EXCHANGEDB_SignkeyMetaData meta;
218 : struct TALER_EXCHANGEDB_SignkeyMetaData got;
219 : struct CountContext ctx;
220 :
221 1 : TDB_FILL (exchange_pub,
222 : 3);
223 1 : TDB_FILL (master_sig,
224 : 3);
225 : /* expired for signing half an hour ago */
226 1 : meta = make_meta (GNUNET_TIME_UNIT_ZERO);
227 : meta.expire_sign
228 1 : = GNUNET_TIME_absolute_to_timestamp (
229 : GNUNET_TIME_absolute_subtract (
230 : GNUNET_TIME_absolute_get (),
231 : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES,
232 : 30)));
233 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
234 : TALER_EXCHANGEDB_insert_signkey (pg,
235 : &exchange_pub,
236 : &meta,
237 : &master_sig));
238 : /* the row is there... */
239 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
240 : TALER_EXCHANGEDB_get_signkey (pg,
241 : &exchange_pub,
242 : &got));
243 : /* ...but the iterator skips it */
244 1 : memset (&ctx,
245 : 0,
246 : sizeof (ctx));
247 1 : ctx.exchange_pub = &exchange_pub;
248 1 : FAILIF (0 >=
249 : TALER_EXCHANGEDB_iterate_active_signkeys (pg,
250 : &count_signkeys_cb,
251 : &ctx));
252 1 : FAILIF (0 != ctx.matched);
253 1 : FAILIF (ctx.total !=
254 : TDB_count (pg,
255 : "FROM exchange_sign_keys"
256 : " WHERE expire_sign > %llu",
257 : (unsigned long long)
258 : GNUNET_TIME_absolute_get ().abs_value_us));
259 1 : FAILIF (2 != TDB_count (pg,
260 : "FROM exchange_sign_keys"));
261 1 : return 0;
262 : }
263 :
264 :
265 : /**
266 : * The checks to run, in order.
267 : */
268 : static const struct TDB_Test tests[] = {
269 : { "exchange-sign-keys-empty",
270 : &check_empty },
271 : { "exchange-sign-keys-insert-and-lookup",
272 : &check_insert_and_lookup },
273 : { "exchange-sign-keys-expired-is-inactive",
274 : &check_expired_is_inactive },
275 : { NULL, NULL }
276 : };
277 :
278 :
279 : int
280 1 : main (int argc,
281 : char *const *argv)
282 : {
283 1 : return TDB_main (argc,
284 : argv,
285 : "test-exchange-sign-keys",
286 : "Tests for the exchangedb `exchange_sign_keys' table",
287 : tests);
288 : }
289 :
290 :
291 : /* end of test_exchange_sign_keys.c */
|