From f0a05b8694fc6c65a6643e62ae309e48399d7066 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 1 Feb 2024 23:19:49 +0100 Subject: add new TOTP-specfic JSON parsers --- src/include/taler_crypto_lib.h | 16 +++++ src/include/taler_json_lib.h | 31 ++++++++- src/json/Makefile.am | 2 +- src/json/json_helper.c | 150 +++++++++++++++++++++++++++++++++++++++++ src/util/Makefile.am | 2 +- src/util/crypto_confirmation.c | 41 ++++++----- 6 files changed, 217 insertions(+), 25 deletions(-) diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index b872af08d..e3ae829fd 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -937,6 +937,22 @@ struct TALER_GlobalFeeSetNBOP GNUNET_NETWORK_STRUCT_END +/** + * Compute RFC 3548 base32 decoding of @a val and write + * result to @a udata. + * + * @param val value to decode + * @param val_size number of bytes in @a val + * @param key is the val in bits + * @param key_len is the size of @a key + */ +int +TALER_rfc3548_base32decode (const char *val, + size_t val_size, + void *key, + size_t key_len); + + /** * @brief Builds POS confirmation token to verify payment. * diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index 859ec8879..98e565f0c 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015, 2016, 2021, 2022 Taler Systems SA + Copyright (C) 2014-2024 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 @@ -59,7 +59,6 @@ struct TALER_EncryptedContract */ size_t econtract_size; - }; @@ -286,6 +285,34 @@ TALER_JSON_spec_age_commitment (const char *name, struct TALER_AgeCommitment *age_commitment); +/** + * Provide specification to parse an OTP key. + * An OTP key must be an RFC 3548 base32-encoded + * value (so NOT our usual Crockford-base32 encoding!). + * + * @param name name of the OTP key field in the JSON + * @param[out] otp_key where to store the OTP key + * @return spec for parsing an age commitment + */ +struct GNUNET_JSON_Specification +TALER_JSON_spec_otp_key (const char *name, + const char **otp_key); + + +/** + * Provide specification to parse an OTP method type. + * The value could be provided as an integer or + * as a descriptive string. + * + * @param name name of the OTP method type in the JSON + * @param[out] mca where to store the method type + * @return spec for parsing an age commitment + */ +struct GNUNET_JSON_Specification +TALER_JSON_spec_otp_type (const char *name, + enum TALER_MerchantConfirmationAlgorithm *mca); + + /** * Generate specification to parse all fees for * a denomination under a prefix @a pfx. diff --git a/src/json/Makefile.am b/src/json/Makefile.am index 6886b285a..ce863cb7e 100644 --- a/src/json/Makefile.am +++ b/src/json/Makefile.am @@ -16,7 +16,7 @@ libtalerjson_la_SOURCES = \ json_pack.c \ json_wire.c libtalerjson_la_LDFLAGS = \ - -version-info 2:0:0 \ + -version-info 3:0:1 \ -no-undefined libtalerjson_la_LIBADD = \ $(top_builddir)/src/util/libtalerutil.la \ diff --git a/src/json/json_helper.c b/src/json/json_helper.c index 01ca45f2b..0a533610b 100644 --- a/src/json/json_helper.c +++ b/src/json/json_helper.c @@ -1666,4 +1666,154 @@ TALER_JSON_spec_version (const char *field, } +/** + * Parse given JSON object to an OTP key. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_otp_key (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + const char *pos_key; + + (void) cls; + pos_key = json_string_value (root); + if (NULL == pos_key) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + { + size_t pos_key_length = strlen (pos_key); + void *key; /* pos_key in binary */ + size_t key_len; /* length of the key */ + int dret; + + key_len = pos_key_length * 5 / 8; + key = GNUNET_malloc (key_len); + dret = TALER_rfc3548_base32decode (pos_key, + pos_key_length, + key, + key_len); + if (-1 == dret) + { + GNUNET_free (key); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + GNUNET_free (key); + } + *(const char **) spec->ptr = pos_key; + return GNUNET_OK; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_otp_key (const char *name, + const char **otp_key) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_otp_key, + .field = name, + .ptr = otp_key + }; + + *otp_key = NULL; + return ret; +} + + +/** + * Parse given JSON object to `enum TALER_MerchantConfirmationAlgorithm` + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_otp_type (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + static const struct Entry + { + const char *name; + enum TALER_MerchantConfirmationAlgorithm val; + } lt [] = { + { .name = "NONE", + .val = TALER_MCA_NONE }, + { .name = "TOTP_WITHOUT_PRICE", + .val = TALER_MCA_WITHOUT_PRICE }, + { .name = "TOTP_WITH_PRICE", + .val = TALER_MCA_WITH_PRICE }, + { .name = NULL, + .val = TALER_MCA_NONE }, + }; + enum TALER_MerchantConfirmationAlgorithm *res + = (enum TALER_MerchantConfirmationAlgorithm *) spec->ptr; + + (void) cls; + if (json_is_string (root)) + { + const char *str; + + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + for (unsigned int i = 0; NULL != lt[i].name; i++) + { + if (0 == strcasecmp (str, + lt[i].name)) + { + *res = lt[i].val; + return GNUNET_OK; + } + } + GNUNET_break_op (0); + } + if (json_is_integer (root)) + { + json_int_t val; + + val = json_integer_value (root); + for (unsigned int i = 0; NULL != lt[i].name; i++) + { + if (val == lt[i].val) + { + *res = lt[i].val; + return GNUNET_OK; + } + } + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + GNUNET_break_op (0); + return GNUNET_SYSERR; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_otp_type (const char *name, + enum TALER_MerchantConfirmationAlgorithm *mca) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_otp_type, + .field = name, + .ptr = mca + }; + + *mca = TALER_MCA_NONE; + return ret; +} + + /* end of json/json_helper.c */ diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 478f75cfe..914ddfdf1 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -120,7 +120,7 @@ libtalerutil_la_LIBADD = \ -lm libtalerutil_la_LDFLAGS = \ - -version-info 1:0:0 \ + -version-info 2:0:1 \ -no-undefined diff --git a/src/util/crypto_confirmation.c b/src/util/crypto_confirmation.c index f19fc4a3c..204c373da 100644 --- a/src/util/crypto_confirmation.c +++ b/src/util/crypto_confirmation.c @@ -112,20 +112,11 @@ compute_totp (struct GNUNET_TIME_Timestamp ts, } -/** - * Compute RFC 3548 base32 decoding of @a val and write - * result to @a udata. - * - * @param val value to decode - * @param val_size number of bytes in @a val - * @param key is the val in bits - * @param key_len is the size of @a key - */ -static int -base32decode (const char *val, - size_t val_size, - void *key, - size_t key_len) +int +TALER_rfc3548_base32decode (const char *val, + size_t val_size, + void *key, + size_t key_len) { /** * 32 characters for decoding, using RFC 3548. @@ -142,13 +133,21 @@ base32decode (const char *val, if ((rpos < val_size) && (vbit < 8)) { char c = val[rpos++]; - if (c == '=') // padding character + + if (c == '=') { - break; + /* padding character */ + if (rpos == val_size) + break; /* Ok, 1x '=' padding is allowed */ + if ( ('=' == val[rpos]) && + (rpos + 1 == val_size) ) + break; /* Ok, 2x '=' padding is allowed */ + return -1; /* invalid padding */ } const char *p = strchr (decTable__, toupper (c)); if (! p) - { // invalid character + { + /* invalid character */ return -1; } bits = (bits << 5) | (p - decTable__); @@ -226,10 +225,10 @@ TALER_build_pos_confirmation (const char *pos_key, return NULL; key_len = pos_key_length * 5 / 8; key = GNUNET_malloc (key_len); - dret = base32decode (pos_key, - pos_key_length, - key, - key_len); + dret = TALER_rfc3548_base32decode (pos_key, + pos_key_length, + key, + key_len); if (-1 == dret) { GNUNET_free (key); -- cgit v1.2.3