Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2022 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/age_restriction.c
18 : * @brief Functions that are used for age restriction
19 : * @author Özgür Kesim
20 : */
21 : #include "platform.h"
22 : #include "taler_util.h"
23 : #include "taler_signatures.h"
24 : #include <gcrypt.h>
25 :
26 : void
27 2 : TALER_age_commitment_hash (
28 : const struct TALER_AgeCommitment *commitment,
29 : struct TALER_AgeCommitmentHash *ahash)
30 : {
31 : struct GNUNET_HashContext *hash_context;
32 : struct GNUNET_HashCode hash;
33 :
34 2 : GNUNET_assert (NULL != ahash);
35 2 : if (NULL == commitment)
36 : {
37 0 : memset (ahash, 0, sizeof(struct TALER_AgeCommitmentHash));
38 0 : return;
39 : }
40 :
41 2 : GNUNET_assert (__builtin_popcount (commitment->mask.bits) - 1 ==
42 : (int) commitment->num);
43 :
44 2 : hash_context = GNUNET_CRYPTO_hash_context_start ();
45 :
46 16 : for (size_t i = 0; i < commitment->num; i++)
47 : {
48 14 : GNUNET_CRYPTO_hash_context_read (hash_context,
49 14 : &commitment->keys[i],
50 : sizeof(commitment->keys[i]));
51 : }
52 :
53 2 : GNUNET_CRYPTO_hash_context_finish (hash_context,
54 : &hash);
55 2 : GNUNET_memcpy (&ahash->shash.bits,
56 : &hash.bits,
57 : sizeof(ahash->shash.bits));
58 : }
59 :
60 :
61 : /* To a given age value between 0 and 31, returns the index of the age group
62 : * defined by the given mask.
63 : */
64 : uint8_t
65 6081 : get_age_group (
66 : const struct TALER_AgeMask *mask,
67 : uint8_t age)
68 : {
69 6081 : uint32_t m = mask->bits;
70 6081 : uint8_t i = 0;
71 :
72 67438 : while (m > 0)
73 : {
74 67398 : if (0 >= age)
75 6041 : break;
76 61357 : m = m >> 1;
77 61357 : i += m & 1;
78 61357 : age--;
79 : }
80 6081 : return i;
81 : }
82 :
83 :
84 : enum GNUNET_GenericReturnValue
85 35 : TALER_age_restriction_commit (
86 : const struct TALER_AgeMask *mask,
87 : const uint8_t age,
88 : const struct GNUNET_HashCode *seed,
89 : struct TALER_AgeCommitmentProof *new)
90 : {
91 : struct GNUNET_HashCode seed_i;
92 35 : uint8_t num_pub = __builtin_popcount (mask->bits) - 1;
93 35 : uint8_t num_priv = get_age_group (mask, age);
94 : size_t i;
95 :
96 35 : GNUNET_assert (NULL != seed);
97 35 : GNUNET_assert (NULL != new);
98 35 : GNUNET_assert (mask->bits & 1); /* fist bit must have been set */
99 35 : GNUNET_assert (31 > num_priv);
100 35 : GNUNET_assert (num_priv <= num_pub);
101 :
102 35 : seed_i = *seed;
103 35 : new->commitment.mask.bits = mask->bits;
104 35 : new->commitment.num = num_pub;
105 35 : new->proof.num = num_priv;
106 35 : new->proof.keys = NULL;
107 :
108 35 : new->commitment.keys = GNUNET_new_array (
109 : num_pub,
110 : struct TALER_AgeCommitmentPublicKeyP);
111 :
112 35 : if (0 < num_priv)
113 27 : new->proof.keys = GNUNET_new_array (
114 : num_priv,
115 : struct TALER_AgeCommitmentPrivateKeyP);
116 :
117 : /* Create as many private keys as we need and fill the rest of the
118 : * public keys with valid curve points.
119 : * We need to make sure that the public keys are proper points on the
120 : * elliptic curve, so we can't simply fill the struct with random values. */
121 280 : for (i = 0; i < num_pub; i++)
122 : {
123 245 : struct TALER_AgeCommitmentPrivateKeyP key = {0};
124 245 : struct TALER_AgeCommitmentPrivateKeyP *pkey = &key;
125 :
126 : /* Only save the private keys for age groups less than num_priv */
127 245 : if (i < num_priv)
128 138 : pkey = &new->proof.keys[i];
129 :
130 : #ifndef AGE_RESTRICTION_WITH_ECDSA
131 245 : GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i,
132 : sizeof(seed_i),
133 : &pkey->priv);
134 245 : GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv,
135 245 : &new->commitment.keys[i].pub);
136 245 : seed_i.bits[0] += 1;
137 : }
138 :
139 35 : return GNUNET_OK;
140 : #else
141 : if (GNUNET_OK !=
142 : GNUNET_CRYPTO_kdf (pkey,
143 : sizeof (*pkey),
144 : &salti,
145 : sizeof (salti),
146 : "age commitment",
147 : strlen ("age commitment"),
148 : NULL, 0))
149 : goto FAIL;
150 :
151 : /* See GNUNET_CRYPTO_ecdsa_key_create */
152 : pkey->priv.d[0] &= 248;
153 : pkey->priv.d[31] &= 127;
154 : pkey->priv.d[31] |= 64;
155 :
156 : GNUNET_CRYPTO_ecdsa_key_get_public (&pkey->priv,
157 : &new->commitment.keys[i].pub);
158 :
159 : }
160 :
161 : return GNUNET_OK;
162 :
163 : FAIL:
164 : GNUNET_free (new->commitment.keys);
165 : if (NULL != new->proof.keys)
166 : GNUNET_free (new->proof.keys);
167 : return GNUNET_SYSERR;
168 : #endif
169 : }
170 :
171 :
172 : enum GNUNET_GenericReturnValue
173 66 : TALER_age_commitment_derive (
174 : const struct TALER_AgeCommitmentProof *orig,
175 : const struct GNUNET_HashCode *salt,
176 : struct TALER_AgeCommitmentProof *newacp)
177 : {
178 66 : GNUNET_assert (NULL != newacp);
179 66 : GNUNET_assert (orig->proof.num <=
180 : orig->commitment.num);
181 66 : GNUNET_assert (((int) orig->commitment.num) ==
182 : __builtin_popcount (orig->commitment.mask.bits) - 1);
183 :
184 66 : newacp->commitment.mask = orig->commitment.mask;
185 66 : newacp->commitment.num = orig->commitment.num;
186 66 : newacp->commitment.keys = GNUNET_new_array (
187 : newacp->commitment.num,
188 : struct TALER_AgeCommitmentPublicKeyP);
189 :
190 66 : newacp->proof.num = orig->proof.num;
191 66 : newacp->proof.keys = NULL;
192 66 : if (0 != newacp->proof.num)
193 50 : newacp->proof.keys = GNUNET_new_array (
194 : newacp->proof.num,
195 : struct TALER_AgeCommitmentPrivateKeyP);
196 :
197 : #ifndef AGE_RESTRICTION_WITH_ECDSA
198 : /* 1. Derive the public keys */
199 528 : for (size_t i = 0; i < orig->commitment.num; i++)
200 : {
201 462 : GNUNET_CRYPTO_edx25519_public_key_derive (
202 462 : &orig->commitment.keys[i].pub,
203 : salt,
204 : sizeof(*salt),
205 462 : &newacp->commitment.keys[i].pub);
206 : }
207 :
208 : /* 2. Derive the private keys */
209 330 : for (size_t i = 0; i < orig->proof.num; i++)
210 : {
211 264 : GNUNET_CRYPTO_edx25519_private_key_derive (
212 264 : &orig->proof.keys[i].priv,
213 : salt,
214 : sizeof(*salt),
215 264 : &newacp->proof.keys[i].priv);
216 : }
217 : #else
218 : char label[sizeof(uint64_t) + 1] = {0};
219 :
220 : /* Because GNUNET_CRYPTO_ecdsa_public_key_derive expects char * (and calls
221 : * strlen on it), we must avoid 0's in the label. */
222 : uint64_t nz_salt = salt | 0x8040201008040201;
223 : memcpy (label, &nz_salt, sizeof(nz_salt));
224 :
225 : /* 1. Derive the public keys */
226 : for (size_t i = 0; i < orig->commitment.num; i++)
227 : {
228 : GNUNET_CRYPTO_ecdsa_public_key_derive (
229 : &orig->commitment.keys[i].pub,
230 : label,
231 : "age commitment derive",
232 : &newacp->commitment.keys[i].pub);
233 : }
234 :
235 : /* 2. Derive the private keys */
236 : for (size_t i = 0; i < orig->proof.num; i++)
237 : {
238 : struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
239 : priv = GNUNET_CRYPTO_ecdsa_private_key_derive (
240 : &orig->proof.keys[i].priv,
241 : label,
242 : "age commitment derive");
243 : newacp->proof.keys[i].priv = *priv;
244 : GNUNET_free (priv);
245 : }
246 : #endif
247 :
248 66 : return GNUNET_OK;
249 : }
250 :
251 :
252 : GNUNET_NETWORK_STRUCT_BEGIN
253 :
254 : /**
255 : * Age group mask in network byte order.
256 : */
257 : struct TALER_AgeMaskNBO
258 : {
259 : uint32_t bits_nbo;
260 : };
261 :
262 : /**
263 : * Used for attestation of a particular age
264 : */
265 : struct TALER_AgeAttestationPS
266 : {
267 : /**
268 : * Purpose must be #TALER_SIGNATURE_WALLET_AGE_ATTESTATION.
269 : * (no GNUNET_PACKED here because the struct is already packed)
270 : */
271 : struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
272 :
273 : /**
274 : * Age mask that defines the underlying age groups
275 : */
276 : struct TALER_AgeMaskNBO mask GNUNET_PACKED;
277 :
278 : /**
279 : * The particular age that this attestation is for.
280 : * We use uint32_t here for alignment.
281 : */
282 : uint32_t age GNUNET_PACKED;
283 : };
284 :
285 : GNUNET_NETWORK_STRUCT_END
286 :
287 :
288 : enum GNUNET_GenericReturnValue
289 2178 : TALER_age_commitment_attest (
290 : const struct TALER_AgeCommitmentProof *cp,
291 : uint8_t age,
292 : struct TALER_AgeAttestation *attest)
293 : {
294 : uint8_t group;
295 :
296 2178 : GNUNET_assert (NULL != attest);
297 2178 : GNUNET_assert (NULL != cp);
298 :
299 2178 : group = get_age_group (&cp->commitment.mask,
300 : age);
301 :
302 2178 : GNUNET_assert (group < 32);
303 :
304 2178 : if (0 == group)
305 : {
306 : /* Age group 0 means: no attestation necessary.
307 : * We set the signature to zero and communicate success. */
308 792 : memset (attest,
309 : 0,
310 : sizeof(struct TALER_AgeAttestation));
311 792 : return GNUNET_OK;
312 : }
313 :
314 1386 : if (group > cp->proof.num)
315 585 : return GNUNET_NO;
316 :
317 : {
318 801 : struct TALER_AgeAttestationPS at = {
319 801 : .purpose.size = htonl (sizeof(at)),
320 801 : .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_AGE_ATTESTATION),
321 801 : .mask.bits_nbo = htonl (cp->commitment.mask.bits),
322 801 : .age = htonl (age),
323 : };
324 :
325 : #ifndef AGE_RESTRICTION_WITH_ECDSA
326 : #define sign(a,b,c) GNUNET_CRYPTO_edx25519_sign (a,b,c)
327 : #else
328 : #define sign(a,b,c) GNUNET_CRYPTO_ecdsa_sign (a,b,c)
329 : #endif
330 801 : sign (&cp->proof.keys[group - 1].priv,
331 : &at,
332 : &attest->signature);
333 : }
334 :
335 801 : return GNUNET_OK;
336 : }
337 :
338 :
339 : enum GNUNET_GenericReturnValue
340 1593 : TALER_age_commitment_verify (
341 : const struct TALER_AgeCommitment *comm,
342 : uint8_t age,
343 : const struct TALER_AgeAttestation *attest)
344 : {
345 : uint8_t group;
346 :
347 1593 : GNUNET_assert (NULL != attest);
348 1593 : GNUNET_assert (NULL != comm);
349 :
350 1593 : group = get_age_group (&comm->mask,
351 : age);
352 :
353 1593 : GNUNET_assert (group < 32);
354 :
355 : /* Age group 0 means: no attestation necessary. */
356 1593 : if (0 == group)
357 792 : return GNUNET_OK;
358 :
359 801 : if (group > comm->num)
360 : {
361 0 : GNUNET_break_op (0);
362 0 : return GNUNET_NO;
363 : }
364 :
365 : {
366 801 : struct TALER_AgeAttestationPS at = {
367 801 : .purpose.size = htonl (sizeof(at)),
368 801 : .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_AGE_ATTESTATION),
369 801 : .mask.bits_nbo = htonl (comm->mask.bits),
370 801 : .age = htonl (age),
371 : };
372 :
373 : #ifndef AGE_RESTRICTION_WITH_ECDSA
374 : #define verify(a,b,c,d) GNUNET_CRYPTO_edx25519_verify ((a),(b),(c),(d))
375 : #else
376 : #define verify(a,b,c,d) GNUNET_CRYPTO_ecdsa_verify ((a),(b),(c),(d))
377 : #endif
378 801 : return verify (TALER_SIGNATURE_WALLET_AGE_ATTESTATION,
379 : &at,
380 : &attest->signature,
381 : &comm->keys[group - 1].pub);
382 : }
383 : }
384 :
385 :
386 : void
387 0 : TALER_age_commitment_free (
388 : struct TALER_AgeCommitment *commitment)
389 : {
390 0 : if (NULL == commitment)
391 0 : return;
392 :
393 0 : if (NULL != commitment->keys)
394 : {
395 0 : GNUNET_free (commitment->keys);
396 0 : commitment->keys = NULL;
397 : }
398 0 : GNUNET_free (commitment);
399 : }
400 :
401 :
402 : void
403 0 : TALER_age_proof_free (
404 : struct TALER_AgeProof *proof)
405 : {
406 0 : if (NULL != proof->keys)
407 : {
408 0 : GNUNET_CRYPTO_zero_keys (
409 0 : proof->keys,
410 0 : sizeof(*proof->keys) * proof->num);
411 :
412 0 : GNUNET_free (proof->keys);
413 0 : proof->keys = NULL;
414 : }
415 0 : GNUNET_free (proof);
416 0 : }
417 :
418 :
419 : void
420 101 : TALER_age_commitment_proof_free (
421 : struct TALER_AgeCommitmentProof *cp)
422 : {
423 101 : if (NULL != cp->proof.keys)
424 : {
425 77 : GNUNET_CRYPTO_zero_keys (
426 77 : cp->proof.keys,
427 77 : sizeof(*cp->proof.keys) * cp->proof.num);
428 :
429 77 : GNUNET_free (cp->proof.keys);
430 77 : cp->proof.keys = NULL;
431 : }
432 :
433 101 : if (NULL != cp->commitment.keys)
434 : {
435 101 : GNUNET_free (cp->commitment.keys);
436 101 : cp->commitment.keys = NULL;
437 : }
438 101 : }
|