diff options
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/age_restriction.c | 89 | ||||
-rw-r--r-- | src/util/exchange_signatures.c | 28 | ||||
-rw-r--r-- | src/util/test_age_restriction.c | 169 | ||||
-rw-r--r-- | src/util/wallet_signatures.c | 35 |
4 files changed, 296 insertions, 25 deletions
diff --git a/src/util/age_restriction.c b/src/util/age_restriction.c index f0d99fe66..6f0702298 100644 --- a/src/util/age_restriction.c +++ b/src/util/age_restriction.c @@ -23,6 +23,7 @@ #include "taler_signatures.h" #include <gnunet/gnunet_json_lib.h> #include <gcrypt.h> +#include <stdint.h> struct #ifndef AGE_RESTRICTION_WITH_ECDSA @@ -76,7 +77,7 @@ TALER_age_commitment_hash ( * defined by the given mask. */ uint8_t -get_age_group ( +TALER_get_age_group ( const struct TALER_AgeMask *mask, uint8_t age) { @@ -95,6 +96,27 @@ get_age_group ( } +uint8_t +TALER_get_lowest_age ( + const struct TALER_AgeMask *mask, + uint8_t age) +{ + uint32_t m = mask->bits; + uint8_t group = TALER_get_age_group (mask, age); + uint8_t lowest = 0; + + while (group > 0) + { + m = m >> 1; + if (m & 1) + group--; + lowest++; + } + + return lowest; +} + + #ifdef AGE_RESTRICTION_WITH_ECDSA /* @brief Helper function to generate a ECDSA private key * @@ -150,7 +172,7 @@ TALER_age_restriction_commit ( GNUNET_assert (mask->bits & 1); /* first bit must have been set */ num_pub = __builtin_popcount (mask->bits) - 1; - num_priv = get_age_group (mask, age); + num_priv = TALER_get_age_group (mask, age); GNUNET_assert (31 > num_priv); GNUNET_assert (num_priv <= num_pub); @@ -335,8 +357,8 @@ TALER_age_commitment_attest ( GNUNET_assert (NULL != attest); GNUNET_assert (NULL != cp); - group = get_age_group (&cp->commitment.mask, - age); + group = TALER_get_age_group (&cp->commitment.mask, + age); GNUNET_assert (group < 32); @@ -386,8 +408,8 @@ TALER_age_commitment_verify ( GNUNET_assert (NULL != attest); GNUNET_assert (NULL != comm); - group = get_age_group (&comm->mask, - age); + group = TALER_get_age_group (&comm->mask, + age); GNUNET_assert (group < 32); @@ -604,7 +626,7 @@ TALER_age_restriction_from_secret ( GNUNET_assert (mask->bits & 1); /* fist bit must have been set */ num_pub = __builtin_popcount (mask->bits) - 1; - num_priv = get_age_group (mask, max_age); + num_priv = TALER_get_age_group (mask, max_age); GNUNET_assert (31 > num_priv); GNUNET_assert (num_priv <= num_pub); @@ -689,4 +711,57 @@ TALER_age_restriction_from_secret ( } +enum GNUNET_GenericReturnValue +TALER_parse_coarse_date ( + const char *in, + const struct TALER_AgeMask *mask, + uint32_t *out) +{ + struct tm date = {0}; + struct tm limit = {0}; + time_t seconds; + + if (NULL == in) + { + /* FIXME[oec]: correct behaviour? */ + *out = 0; + return GNUNET_OK; + } + + GNUNET_assert (NULL !=mask); + GNUNET_assert (NULL !=out); + + if (NULL == strptime (in, "%Y-%0m-%0d", &date)) + { + if (NULL == strptime (in, "%Y-%0m-00", &date)) + if (NULL == strptime (in, "%Y-00-00", &date)) + return GNUNET_SYSERR; + + /* turns out that the day is off by one in the last two cases */ + date.tm_mday += 1; + } + + seconds = mktime (&date); + if (-1 == seconds) + return GNUNET_SYSERR; + + /* calculate the limit date for the largest age group */ + localtime_r (&(time_t){time (NULL)}, &limit); + limit.tm_year -= TALER_adult_age (mask); + GNUNET_assert (-1 != mktime (&limit)); + + if ((limit.tm_year < date.tm_year) + || ((limit.tm_year == date.tm_year) + && (limit.tm_mon < date.tm_mon)) + || ((limit.tm_year == date.tm_year) + && (limit.tm_mon == date.tm_mon) + && (limit.tm_mday < date.tm_mday))) + *out = seconds / 60 / 60 / 24; + else + *out = 0; + + return GNUNET_OK; +} + + /* end util/age_restriction.c */ diff --git a/src/util/exchange_signatures.c b/src/util/exchange_signatures.c index 6f8ebdaff..3aa464aa7 100644 --- a/src/util/exchange_signatures.c +++ b/src/util/exchange_signatures.c @@ -413,6 +413,34 @@ TALER_exchange_online_age_withdraw_confirmation_sign ( } +enum GNUNET_GenericReturnValue +TALER_exchange_online_age_withdraw_confirmation_verify ( + const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, + uint32_t noreveal_index, + const struct TALER_ExchangePublicKeyP *exchange_pub, + const struct TALER_ExchangeSignatureP *exchange_sig) +{ + struct TALER_AgeWithdrawConfirmationPS confirm = { + .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_AGE_WITHDRAW), + .purpose.size = htonl (sizeof (confirm)), + .h_commitment = *h_commitment, + .noreveal_index = htonl (noreveal_index) + }; + + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify ( + TALER_SIGNATURE_EXCHANGE_CONFIRM_AGE_WITHDRAW, + &confirm, + &exchange_sig->eddsa_signature, + &exchange_pub->eddsa_pub)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + /* TODO:oec: add signature for age-withdraw, age-reveal */ diff --git a/src/util/test_age_restriction.c b/src/util/test_age_restriction.c index 77717616f..53cacc6d5 100644 --- a/src/util/test_age_restriction.c +++ b/src/util/test_age_restriction.c @@ -21,11 +21,7 @@ */ #include "platform.h" #include "taler_util.h" - -extern uint8_t -get_age_group ( - const struct TALER_AgeMask *mask, - uint8_t age); +#include <gnunet/gnunet_common.h> /** * Encodes the age mask into a string, like "8:10:12:14:16:18:21" @@ -113,10 +109,10 @@ test_groups (void) for (uint8_t i = 0; i < 32; i++) { - uint8_t r = get_age_group (&mask, i); + uint8_t r = TALER_get_age_group (&mask, i); char *m = age_mask_to_string (&mask); - printf ("get_age_group(%s, %2d) = %d vs %d (exp)\n", + printf ("TALER_get_age_group(%s, %2d) = %d vs %d (exp)\n", m, i, r, @@ -133,6 +129,153 @@ test_groups (void) } +enum GNUNET_GenericReturnValue +test_dates (void) +{ + struct TALER_AgeMask mask = { + .bits = 1 | 1 << 5 | 1 << 9 | 1 << 13 | 1 << 17 | 1 << 21 + }; + + struct + { + char *date; + uint32_t expected; + enum GNUNET_GenericReturnValue ret; + } + test [] = { + {.date = "abcd-00-00", .expected = 0, .ret = GNUNET_SYSERR}, + {.date = "1900-00-01", .expected = 0, .ret = GNUNET_SYSERR}, + {.date = "19000001", .expected = 0, .ret = GNUNET_SYSERR}, + {.date = "2001-33-05", .expected = 0, .ret = GNUNET_SYSERR}, + {.date = "2001-33-35", .expected = 0, .ret = GNUNET_SYSERR}, + + {.date = "1900-00-00", .expected = 0, .ret = GNUNET_OK}, + {.date = "2001-00-00", .expected = 0, .ret = GNUNET_OK}, + {.date = "2001-03-00", .expected = 0, .ret = GNUNET_OK}, + {.date = "2001-03-05", .expected = 0, .ret = GNUNET_OK}, + + /* These dates should be far enough for the near future so that + * the expected values are correct. Will need adjustment in 2044 :) */ + {.date = "2023-06-26", .expected = 19533, .ret = GNUNET_OK }, + {.date = "2023-06-01", .expected = 19508, .ret = GNUNET_OK }, + {.date = "2023-06-00", .expected = 19508, .ret = GNUNET_OK }, + {.date = "2023-01-01", .expected = 19357, .ret = GNUNET_OK }, + {.date = "2023-00-00", .expected = 19357, .ret = GNUNET_OK }, + }; + + for (uint8_t t = 0; t < sizeof(test) / sizeof(test[0]); t++) + { + uint32_t d; + enum GNUNET_GenericReturnValue ret; + + ret = TALER_parse_coarse_date (test[t].date, + &mask, + &d); + if (ret != test[t].ret) + { + printf ( + "dates[%d] for date `%s` expected parser to return: %d, got: %d\n", + t, test[t].date, test[t].ret, ret); + return GNUNET_SYSERR; + } + + if (ret == GNUNET_SYSERR) + continue; + + if (d != test[t].expected) + { + printf ( + "dates[%d] for date `%s` expected value %d, but got %d\n", + t, test[t].date, test[t].expected, d); + return GNUNET_SYSERR; + } + + printf ("dates[%d] for date `%s` got expected value %d\n", + t, test[t].date, d); + } + + printf ("done with dates\n"); + + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +test_lowest (void) +{ + struct TALER_AgeMask mask = { + .bits = 1 | 1 << 5 | 1 << 9 | 1 << 13 | 1 << 17 | 1 << 21 + }; + + struct { uint8_t age; uint8_t expected; } + test [] = { + {.age = 1, .expected = 0 }, + {.age = 2, .expected = 0 }, + {.age = 3, .expected = 0 }, + {.age = 4, .expected = 0 }, + {.age = 5, .expected = 5 }, + {.age = 6, .expected = 5 }, + {.age = 7, .expected = 5 }, + {.age = 8, .expected = 5 }, + {.age = 9, .expected = 9 }, + {.age = 10, .expected = 9 }, + {.age = 11, .expected = 9 }, + {.age = 12, .expected = 9 }, + {.age = 13, .expected = 13 }, + {.age = 14, .expected = 13 }, + {.age = 15, .expected = 13 }, + {.age = 16, .expected = 13 }, + {.age = 17, .expected = 17 }, + {.age = 18, .expected = 17 }, + {.age = 19, .expected = 17 }, + {.age = 20, .expected = 17 }, + {.age = 21, .expected = 21 }, + {.age = 22, .expected = 21 }, + }; + + for (uint8_t n = 0; n < sizeof(test) / sizeof(test[0]); n++) + { + uint8_t l = TALER_get_lowest_age (&mask, test[n].age); + printf ("lowest[%d] for age %d, expected lowest: %d, got: %d\n", + n, test[n].age, test[n].expected, l); + if (test[n].expected != l) + return GNUNET_SYSERR; + } + + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +test_adult (void) +{ + struct { struct TALER_AgeMask mask; uint8_t expected; } + test[] = { + { .mask = {.bits = 1 | 1 << 2}, + .expected = 2 }, + { .mask = {.bits = 1 | 1 << 2 | 1 << 3}, + .expected = 3 }, + { .mask = {.bits = 1 | 1 << 3}, + .expected = 3 }, + { .mask = {.bits = 1 | 1 << 22}, + .expected = 22 }, + { .mask = {.bits = 1 | 1 << 10 | 1 << 16 | 1 << 22}, + .expected = 22 }, + }; + for (uint8_t n = 0; n < sizeof(test) / sizeof(test[0]); n++) + { + uint8_t l = TALER_adult_age (&test[n].mask); + printf ("adult[%d] for mask %s, expected: %d, got: %d\n", + n, TALER_age_mask_to_string (&test[n].mask), test[n].expected, l); + if (test[n].expected != l) + return GNUNET_SYSERR; + } + printf ("done with adult\n"); + + return GNUNET_OK; +} + + static struct TALER_AgeMask age_mask = { .bits = 1 | 1 << 8 | 1 << 10 | 1 << 12 | 1 << 14 | 1 << 16 | 1 << 18 | 1 << 21 }; @@ -146,7 +289,7 @@ test_attestation (void) enum GNUNET_GenericReturnValue ret; struct TALER_AgeCommitmentProof acp[3] = {0}; struct TALER_AgeAttestation at = {0}; - uint8_t age_group = get_age_group (&age_mask, age); + uint8_t age_group = TALER_get_age_group (&age_mask, age); struct GNUNET_HashCode seed; @@ -183,7 +326,7 @@ test_attestation (void) { for (uint8_t min = 0; min < 22; min++) { - uint8_t min_group = get_age_group (&age_mask, min); + uint8_t min_group = TALER_get_age_group (&age_mask, min); ret = TALER_age_commitment_attest (&acp[i], min, @@ -259,11 +402,17 @@ main (int argc, NULL); if (GNUNET_OK != test_groups ()) return 1; + if (GNUNET_OK != test_lowest ()) + return 2; if (GNUNET_OK != test_attestation ()) { GNUNET_break (0); - return 2; + return 3; } + if (GNUNET_OK != test_dates ()) + return 4; + if (GNUNET_OK != test_adult ()) + return 5; return 0; } diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c index 221865e73..823641ed4 100644 --- a/src/util/wallet_signatures.c +++ b/src/util/wallet_signatures.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021, 2022 Taler Systems SA + Copyright (C) 2021-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -17,10 +17,12 @@ * @file wallet_signatures.c * @brief Utility functions for Taler wallet signatures * @author Christian Grothoff + * @author Özgür Kesim */ #include "platform.h" #include "taler_util.h" #include "taler_signatures.h" +#include <gnunet/gnunet_common.h> GNUNET_NETWORK_STRUCT_BEGIN @@ -620,9 +622,9 @@ struct TALER_AgeWithdrawRequestPS struct GNUNET_CRYPTO_EccSignaturePurpose purpose; /** - * Hash of the commitment of n*kappa coins + * The reserve's public key */ - struct TALER_AgeWithdrawCommitmentHashP h_commitment GNUNET_PACKED; + struct TALER_ReservePublicKeyP reserve_pub; /** * Value of the coin being exchanged (matching the denomination key) @@ -634,9 +636,19 @@ struct TALER_AgeWithdrawRequestPS struct TALER_AmountNBO amount_with_fee; /** + * Running SHA512 hash of the commitment of n*kappa coins + */ + struct TALER_AgeWithdrawCommitmentHashP h_commitment; + + /** + * The mask that defines the age groups. MUST be the same for all denominations. + */ + struct TALER_AgeMask mask; + + /** * Maximum age group that the coins are going to be restricted to. */ - uint32_t max_age_group; + uint8_t max_age_group; }; @@ -646,7 +658,8 @@ void TALER_wallet_age_withdraw_sign ( const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, const struct TALER_Amount *amount_with_fee, - uint32_t max_age_group, + const struct TALER_AgeMask *mask, + uint8_t max_age, const struct TALER_ReservePrivateKeyP *reserve_priv, struct TALER_ReserveSignatureP *reserve_sig) { @@ -654,9 +667,12 @@ TALER_wallet_age_withdraw_sign ( .purpose.size = htonl (sizeof (req)), .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW), .h_commitment = *h_commitment, - .max_age_group = max_age_group + .mask = *mask, + .max_age_group = TALER_get_age_group (mask, max_age) }; + GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, + &req.reserve_pub.eddsa_pub); TALER_amount_hton (&req.amount_with_fee, amount_with_fee); GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv, @@ -669,15 +685,18 @@ enum GNUNET_GenericReturnValue TALER_wallet_age_withdraw_verify ( const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, const struct TALER_Amount *amount_with_fee, - uint32_t max_age_group, + const struct TALER_AgeMask *mask, + uint8_t max_age, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReserveSignatureP *reserve_sig) { struct TALER_AgeWithdrawRequestPS awsrd = { .purpose.size = htonl (sizeof (awsrd)), .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW), + .reserve_pub = *reserve_pub, .h_commitment = *h_commitment, - .max_age_group = max_age_group + .mask = *mask, + .max_age_group = TALER_get_age_group (mask, max_age) }; TALER_amount_hton (&awsrd.amount_with_fee, |