diff options
author | Christian Grothoff <christian@grothoff.org> | 2015-11-24 14:48:35 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2015-11-24 14:48:35 +0100 |
commit | 4c80119b30c4a9946cb4bfb05363c543ba955bb6 (patch) | |
tree | ea71d9658f205917ba500786288dfdb65def4034 | |
parent | 3961d3df7edfc710223b75cfa2756703a8c74db9 (diff) | |
parent | 1ca54c5df12dc7fd5e9891b64d3e8eb61848e08e (diff) | |
download | exchange-4c80119b30c4a9946cb4bfb05363c543ba955bb6.tar.xz |
Merge branch 'master' of git+ssh://taler.net/var/git/mint
-rw-r--r-- | src/include/taler_json_lib.h | 15 | ||||
-rw-r--r-- | src/include/taler_signatures.h | 62 | ||||
-rw-r--r-- | src/mint-lib/mint_api_common.c | 126 | ||||
-rw-r--r-- | src/mint-lib/mint_api_deposit.c | 33 | ||||
-rw-r--r-- | src/mint-lib/mint_api_json.c | 71 | ||||
-rw-r--r-- | src/mint-lib/mint_api_json.h | 19 | ||||
-rw-r--r-- | src/mint-lib/mint_api_refresh.c | 1 | ||||
-rw-r--r-- | src/mint-lib/mint_api_reserve.c | 27 | ||||
-rw-r--r-- | src/mint/Makefile.am | 3 | ||||
-rw-r--r-- | src/mint/taler-mint-httpd.c | 14 | ||||
-rw-r--r-- | src/mint/taler-mint-httpd_responses.c | 41 | ||||
-rw-r--r-- | src/mint/taler-mint-httpd_tracking.c | 75 | ||||
-rw-r--r-- | src/mint/taler-mint-httpd_tracking.h | 65 | ||||
-rw-r--r-- | src/util/json.c | 34 | ||||
-rw-r--r-- | website/README | 3 | ||||
-rw-r--r-- | website/fake_wire_transfer.php | 79 | ||||
-rw-r--r-- | website/index.html | 114 | ||||
-rw-r--r-- | website/style.css | 328 |
18 files changed, 369 insertions, 741 deletions
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index 63cb81791..8bdcf2709 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -53,18 +53,6 @@ TALER_json_from_abs (struct GNUNET_TIME_Absolute stamp); /** - * Convert a signature (with purpose) to a JSON object representation. - * - * @param purpose purpose of the signature - * @param signature the signature - * @return the JSON reporesentation of the signature with purpose - */ -json_t * -TALER_json_from_eddsa_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, - const struct GNUNET_CRYPTO_EddsaSignature *signature); - - -/** * Convert RSA public key to JSON. * * @param pk public key to convert @@ -93,7 +81,8 @@ TALER_json_from_rsa_signature (struct GNUNET_CRYPTO_rsa_Signature *sig); * @return json string that encodes @a data */ json_t * -TALER_json_from_data (const void *data, size_t size); +TALER_json_from_data (const void *data, + size_t size); /** diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index 3bdc4eee1..ea9ed600a 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -132,6 +132,12 @@ */ #define TALER_SIGNATURE_MERCHANT_REFUND 1102 +/** + * Signature where the merchant confirms that he needs the wire + * transfer identifier for a deposit operation. + */ +#define TALER_SIGNATURE_MERCHANT_DEPOSIT_WTID 1103 + /*********************/ /* Wallet signatures */ @@ -300,8 +306,7 @@ struct TALER_DepositRequestPS /** * The Merchant's public key. Allows the merchant to later refund - * the transaction. All zeros if nobody is allowed to refund the - * transaction later. + * the transaction or to inquire about the wire transfer identifier. */ struct TALER_MerchantPublicKeyP merchant; @@ -372,8 +377,7 @@ struct TALER_DepositConfirmationPS /** * The Merchant's public key. Allows the merchant to later refund - * the transaction. All zeros if nobody is allowed to refund the - * transaction later. + * the transaction or to inquire about the wire transfer identifier. */ struct TALER_MerchantPublicKeyP merchant; @@ -791,6 +795,56 @@ struct TALER_MintWireSupportMethodsPS }; +/** + * @brief Format used to generate the signature on a request to obtain + * the wire transfer identifier associated with a deposit. + */ +struct TALER_DepositTrackPS +{ + /** + * Purpose must be #TALER_SIGNATURE_MERCHANT_DEPOSIT_WTID. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Hash over the contract for which this deposit is made. + */ + struct GNUNET_HashCode h_contract GNUNET_PACKED; + + /** + * Hash over the wiring information of the merchant. + */ + struct GNUNET_HashCode h_wire GNUNET_PACKED; + + /** + * Merchant-generated transaction ID to detect duplicate + * transactions. The merchant must communicate a merchant-unique ID + * to the customer for each transaction. Note that different coins + * that are part of the same transaction can use the same + * transaction ID. The transaction ID is useful for later disputes, + * and the merchant's contract offer (@e h_contract) with the + * customer should include the offer's term and transaction ID + * signed with a key from the merchant. + */ + uint64_t transaction_id GNUNET_PACKED; + + /** + * The Merchant's public key. The deposit inquiry request is to be + * signed by the corresponding private key (using EdDSA). + */ + struct TALER_MerchantPublicKeyP merchant; + + /** + * The coin's public key. This is the value that must have been + * signed (blindly) by the Mint. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + +}; + + + + GNUNET_NETWORK_STRUCT_END #endif diff --git a/src/mint-lib/mint_api_common.c b/src/mint-lib/mint_api_common.c index d8e83c785..fd85fbdc4 100644 --- a/src/mint-lib/mint_api_common.c +++ b/src/mint-lib/mint_api_common.c @@ -62,13 +62,20 @@ TALER_MINT_verify_coin_history_ (const char *currency, { json_t *transaction; struct TALER_Amount amount; - struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; + struct TALER_CoinSpendSignatureP sig; + void *details; + size_t details_size; + const char *type; struct MAJ_Specification spec[] = { MAJ_spec_amount ("amount", &amount), - MAJ_spec_eddsa_signed_purpose ("signature", - &purpose, - &coin_pub->eddsa_pub), + MAJ_spec_string ("type", + &type), + MAJ_spec_fixed_auto ("signature", + &sig), + MAJ_spec_varsize ("details", + &details, + &details_size), MAJ_spec_end }; @@ -81,57 +88,90 @@ TALER_MINT_verify_coin_history_ (const char *currency, GNUNET_break_op (0); return GNUNET_SYSERR; } - switch (ntohl (purpose->purpose)) + if (0 == strcasecmp (type, + "DEPOSIT")) { - case TALER_SIGNATURE_WALLET_COIN_DEPOSIT: - { - const struct TALER_DepositRequestPS *dr; - struct TALER_Amount dr_amount; + const struct TALER_DepositRequestPS *dr; + struct TALER_Amount dr_amount; - if (ntohl (purpose->size) != sizeof (struct TALER_DepositRequestPS)) - { - GNUNET_break (0); - MAJ_parse_free (spec); - return GNUNET_SYSERR; - } - dr = (const struct TALER_DepositRequestPS *) purpose; - TALER_amount_ntoh (&dr_amount, - &dr->amount_with_fee); - if (0 != TALER_amount_cmp (&dr_amount, - &amount)) - { - GNUNET_break (0); - MAJ_parse_free (spec); - return GNUNET_SYSERR; - } + if (details_size != sizeof (struct TALER_DepositRequestPS)) + { + GNUNET_break_op (0); + MAJ_parse_free (spec); + return GNUNET_SYSERR; } - break; - case TALER_SIGNATURE_WALLET_COIN_MELT: + dr = (const struct TALER_DepositRequestPS *) details; + if (details_size != ntohl (dr->purpose.size)) { - const struct TALER_RefreshMeltCoinAffirmationPS *rm; - struct TALER_Amount rm_amount; - - if (ntohl (purpose->size) != sizeof (struct TALER_RefreshMeltCoinAffirmationPS)) + GNUNET_break_op (0); + MAJ_parse_free (spec); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT, + &dr->purpose, + &sig.eddsa_signature, + &coin_pub->eddsa_pub)) { - GNUNET_break (0); - MAJ_parse_free (spec); - return GNUNET_SYSERR; - } - rm = (const struct TALER_RefreshMeltCoinAffirmationPS *) purpose; - TALER_amount_ntoh (&rm_amount, - &rm->amount_with_fee); - if (0 != TALER_amount_cmp (&rm_amount, - &amount)) + GNUNET_break_op (0); + MAJ_parse_free (spec); + return GNUNET_SYSERR; + } + + // FIXME: check sig! + TALER_amount_ntoh (&dr_amount, + &dr->amount_with_fee); + if (0 != TALER_amount_cmp (&dr_amount, + &amount)) { GNUNET_break (0); MAJ_parse_free (spec); return GNUNET_SYSERR; } + } + else if (0 == strcasecmp (type, + "MELT")) + { + const struct TALER_RefreshMeltCoinAffirmationPS *rm; + struct TALER_Amount rm_amount; + + if (details_size != sizeof (struct TALER_RefreshMeltCoinAffirmationPS)) + { + GNUNET_break_op (0); + MAJ_parse_free (spec); + return GNUNET_SYSERR; + } + rm = (const struct TALER_RefreshMeltCoinAffirmationPS *) details; + if (details_size != ntohl (rm->purpose.size)) + { + GNUNET_break_op (0); + MAJ_parse_free (spec); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT, + &rm->purpose, + &sig.eddsa_signature, + &coin_pub->eddsa_pub)) + { + GNUNET_break_op (0); + MAJ_parse_free (spec); + return GNUNET_SYSERR; + } + TALER_amount_ntoh (&rm_amount, + &rm->amount_with_fee); + if (0 != TALER_amount_cmp (&rm_amount, + &amount)) + { + GNUNET_break_op (0); + MAJ_parse_free (spec); + return GNUNET_SYSERR; } - break; - default: + } + else + { /* signature not supported, new version on server? */ - GNUNET_break (0); + GNUNET_break_op (0); MAJ_parse_free (spec); return GNUNET_SYSERR; } diff --git a/src/mint-lib/mint_api_deposit.c b/src/mint-lib/mint_api_deposit.c index 541317763..bf00ffa28 100644 --- a/src/mint-lib/mint_api_deposit.c +++ b/src/mint-lib/mint_api_deposit.c @@ -312,6 +312,39 @@ verify_signatures (const struct TALER_MINT_DenomPublicKey *dki, &dki->fee_deposit); dr.merchant = *merchant_pub; dr.coin_pub = *coin_pub; + + char *contract_str = GNUNET_STRINGS_data_to_string_alloc (h_contract, + sizeof (struct GNUNET_HashCode)); + char *wire_str = GNUNET_STRINGS_data_to_string_alloc (h_wire, + sizeof (struct GNUNET_HashCode)); + char *merchant_pub_str = GNUNET_STRINGS_data_to_string_alloc (merchant_pub, + sizeof (struct TALER_MerchantPublicKeyP)); + char *coin_pub_str = GNUNET_STRINGS_data_to_string_alloc (coin_pub, + sizeof (struct TALER_CoinSpendPublicKeyP)); + printf ("verifying:\ncontract [%s]\nwire [%s]\n" + "timestamp [%llu]\nrefund deadline [%llu]\n" + "transaction id [%llu]\namount [%s %llu.%lu]\n" + "fee deposit [%s %llu.%lu]\nmerch pub [%s]\n" + "coin pub [%s]\n", + contract_str, + wire_str, + timestamp.abs_value_us, + refund_deadline.abs_value_us, + transaction_id, + amount->currency, + amount->value, + amount->fraction, + dki->fee_deposit.currency, + dki->fee_deposit.value, + dki->fee_deposit.fraction, + merchant_pub_str, + coin_pub_str); + + GNUNET_free (contract_str); + GNUNET_free (wire_str); + GNUNET_free (merchant_pub_str); + GNUNET_free (coin_pub_str); + if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT, &dr.purpose, diff --git a/src/mint-lib/mint_api_json.c b/src/mint-lib/mint_api_json.c index 46f54d948..a728a5495 100644 --- a/src/mint-lib/mint_api_json.c +++ b/src/mint-lib/mint_api_json.c @@ -213,49 +213,6 @@ parse_json (json_t *root, } break; - case MAJ_CMD_EDDSA_SIGNATURE: - { - struct TALER_CoinSpendSignatureP sig; - struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; - size_t size; - struct MAJ_Specification sig_spec[] = { - MAJ_spec_fixed_auto ("eddsa_sig", &sig), - MAJ_spec_varsize ("eddsa_val", (void**) &purpose, &size), - MAJ_spec_end - }; - - if (GNUNET_OK != - MAJ_parse_json (pos, - sig_spec)) - { - GNUNET_break_op (0); - MAJ_parse_free (sig_spec); - return i; - } - if (size != ntohl (purpose->size)) - { - GNUNET_break_op (0); - MAJ_parse_free (sig_spec); - return i; - } - - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (ntohl (purpose->purpose), - purpose, - &sig.eddsa_signature, - spec[i].details.eddsa_signature.pub_key)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to verify signature of purpose %u\n", - ntohl (purpose->purpose)); - GNUNET_break_op (0); - MAJ_parse_free (sig_spec); - return i; - } - *spec[i].details.eddsa_signature.purpose_p = purpose; - } - break; - case MAJ_CMD_UINT16: { json_int_t val; @@ -337,10 +294,6 @@ parse_free (struct MAJ_Specification *spec, GNUNET_CRYPTO_rsa_signature_free (*spec[i].details.rsa_signature); *spec[i].details.rsa_signature = NULL; break; - case MAJ_CMD_EDDSA_SIGNATURE: - GNUNET_free (*spec[i].details.eddsa_signature.purpose_p); - *spec[i].details.eddsa_signature.purpose_p = NULL; - break; case MAJ_CMD_JSON_OBJECT: json_decref (*spec[i].details.obj); *spec[i].details.obj = NULL; @@ -535,28 +488,4 @@ MAJ_spec_rsa_signature (const char *name, } -/** - * Specification for parsing an EdDSA object signature with purpose. - * Also validates the signature (!). - * - * @param name name of the JSON field - * @param purpose_p where to store the purpose - * @param pub_key public key to use for validation - */ -struct MAJ_Specification -MAJ_spec_eddsa_signed_purpose (const char *name, - struct GNUNET_CRYPTO_EccSignaturePurpose **purpose_p, - const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key) -{ - struct MAJ_Specification ret = - { - .cmd = MAJ_CMD_EDDSA_SIGNATURE, - .field = name, - .details.eddsa_signature.purpose_p = purpose_p, - .details.eddsa_signature.pub_key = pub_key - }; - return ret; -} - - /* end of mint_api_json.c */ diff --git a/src/mint-lib/mint_api_json.h b/src/mint-lib/mint_api_json.h index 2af5588e5..68809059e 100644 --- a/src/mint-lib/mint_api_json.h +++ b/src/mint-lib/mint_api_json.h @@ -69,11 +69,6 @@ enum MAJ_Command MAJ_CMD_RSA_SIGNATURE, /** - * Parse object with EdDSA signature and purpose at current position. - */ - MAJ_CMD_EDDSA_SIGNATURE, - - /** * Parse `const char *` JSON string at current position. */ MAJ_CMD_STRING, @@ -310,20 +305,6 @@ MAJ_spec_amount (const char *name, /** - * Specification for parsing an EdDSA object signature with purpose. - * Also validates the signature (!). - * - * @param name name of the JSON field - * @param purpose_p where to store the purpose - * @param pub_key public key to use for validation - */ -struct MAJ_Specification -MAJ_spec_eddsa_signed_purpose (const char *name, - struct GNUNET_CRYPTO_EccSignaturePurpose **purpose_p, - const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key); - - -/** * Specification for parsing an RSA public key. * * @param name name of the JSON field diff --git a/src/mint-lib/mint_api_refresh.c b/src/mint-lib/mint_api_refresh.c index c33d1714a..a779bdbc3 100644 --- a/src/mint-lib/mint_api_refresh.c +++ b/src/mint-lib/mint_api_refresh.c @@ -676,6 +676,7 @@ serialize_melt_data (const struct MeltData *md, unsigned int j; size = 0; + asize = (size_t) -1; /* make the compiler happy */ buf = NULL; /* we do 2 iterations, #1 to determine total size, #2 to actually construct the buffer */ diff --git a/src/mint-lib/mint_api_reserve.c b/src/mint-lib/mint_api_reserve.c index 57e8552cc..a726eca46 100644 --- a/src/mint-lib/mint_api_reserve.c +++ b/src/mint-lib/mint_api_reserve.c @@ -167,13 +167,14 @@ parse_reserve_history (json_t *history, else if (0 == strcasecmp (type, "WITHDRAW")) { - struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; - const struct TALER_WithdrawRequestPS *withdraw_purpose; + struct TALER_ReserveSignatureP sig; + struct TALER_WithdrawRequestPS withdraw_purpose; struct TALER_Amount amount_from_purpose; struct MAJ_Specification withdraw_spec[] = { - MAJ_spec_eddsa_signed_purpose ("signature", - &purpose, - &reserve_pub->eddsa_pub), + MAJ_spec_fixed_auto ("signature", + &sig), + MAJ_spec_fixed_auto ("details", + &withdraw_purpose), MAJ_spec_end }; unsigned int i; @@ -186,17 +187,19 @@ parse_reserve_history (json_t *history, GNUNET_break_op (0); return GNUNET_SYSERR; } - /* Check that the signature actually signed a withdraw request */ - if ( (ntohl (purpose->purpose) != TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW) || - (ntohl (purpose->size) != sizeof (struct TALER_WithdrawRequestPS)) ) + /* Check that the signature is a valid withdraw request */ + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW, + &withdraw_purpose.purpose, + &sig.eddsa_signature, + &reserve_pub->eddsa_pub)) { GNUNET_break_op (0); MAJ_parse_free (withdraw_spec); return GNUNET_SYSERR; } - withdraw_purpose = (const struct TALER_WithdrawRequestPS *) purpose; TALER_amount_ntoh (&amount_from_purpose, - &withdraw_purpose->amount_with_fee); + &withdraw_purpose.amount_with_fee); if (0 != TALER_amount_cmp (&amount, &amount_from_purpose)) { @@ -211,8 +214,8 @@ parse_reserve_history (json_t *history, "uuid" array to remember the hashes of all purposes, and compare the hashes to find duplicates. */ - GNUNET_CRYPTO_hash (withdraw_purpose, - ntohl (withdraw_purpose->purpose.size), + GNUNET_CRYPTO_hash (&withdraw_purpose, + ntohl (withdraw_purpose.purpose.size), &uuid[uuid_off]); for (i=0;i<uuid_off;i++) { diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am index 1c7e1ca87..a115d63a0 100644 --- a/src/mint/Makefile.am +++ b/src/mint/Makefile.am @@ -19,6 +19,7 @@ taler_mint_httpd_SOURCES = \ taler-mint-httpd_admin.c taler-mint-httpd_admin.h \ taler-mint-httpd_deposit.c taler-mint-httpd_deposit.h \ taler-mint-httpd_reserve.c taler-mint-httpd_reserve.h \ + taler-mint-httpd_tracking.c taler-mint-httpd_tracking.h \ taler-mint-httpd_wire.c taler-mint-httpd_wire.h \ taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h taler_mint_httpd_LDADD = \ @@ -36,7 +37,7 @@ taler_mint_httpd_SOURCES += \ endif check_SCRIPTS = \ - test_taler_mint_httpd.sh + test_taler_mint_httpd.sh if HAVE_EXPENSIVE_TESTS check_SCRIPTS += \ diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c index b55004d91..26a440c95 100644 --- a/src/mint/taler-mint-httpd.c +++ b/src/mint/taler-mint-httpd.c @@ -33,6 +33,7 @@ #include "taler-mint-httpd_reserve.h" #include "taler-mint-httpd_wire.h" #include "taler-mint-httpd_refresh.h" +#include "taler-mint-httpd_tracking.h" #include "taler-mint-httpd_keystate.h" #if HAVE_DEVELOPER #include "taler-mint-httpd_test.h" @@ -256,6 +257,19 @@ handle_mhd_request (void *cls, "Only POST is allowed", 0, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, + { "/wire/deposits", MHD_HTTP_METHOD_GET, "application/json", + NULL, 0, + &TMH_TRACKING_handler_wire_deposits, MHD_HTTP_OK }, + { "/wire/deposits", NULL, "text/plain", + "Only GET is allowed", 0, + &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, + { "/deposit/wtid", MHD_HTTP_METHOD_POST, "application/json", + NULL, 0, + &TMH_TRACKING_handler_deposit_wtid, MHD_HTTP_OK }, + { "/deposit/wtid", NULL, "text/plain", + "Only POST is allowed", 0, + &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, + #if HAVE_DEVELOPER /* Client crypto-interoperability test functions */ { "/test", MHD_HTTP_METHOD_POST, "application/json", diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index a018f68d3..3b04fdb3c 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -404,10 +404,11 @@ TMH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection, static json_t * compile_transaction_history (const struct TALER_MINTDB_TransactionList *tl) { - json_t *transaction; + json_t *details; const char *type; struct TALER_Amount value; json_t *history; + const struct TALER_CoinSpendSignatureP *sig; const struct TALER_MINTDB_TransactionList *pos; history = json_array (); @@ -420,7 +421,7 @@ compile_transaction_history (const struct TALER_MINTDB_TransactionList *tl) struct TALER_DepositRequestPS dr; const struct TALER_MINTDB_Deposit *deposit = pos->details.deposit; - type = "deposit"; + type = "DEPOSIT"; value = deposit->amount_with_fee; dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS)); @@ -435,12 +436,12 @@ compile_transaction_history (const struct TALER_MINTDB_TransactionList *tl) &deposit->deposit_fee); dr.merchant = deposit->merchant_pub; dr.coin_pub = deposit->coin.coin_pub; - + sig = &deposit->csig; /* internal sanity check before we hand out a bogus sig... */ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT, &dr.purpose, - &deposit->csig.eddsa_signature, + &sig->eddsa_signature, &deposit->coin.coin_pub.eddsa_pub)) { GNUNET_break (0); @@ -448,8 +449,8 @@ compile_transaction_history (const struct TALER_MINTDB_TransactionList *tl) return NULL; } - transaction = TALER_json_from_eddsa_sig (&dr.purpose, - &deposit->csig.eddsa_signature); + details = TALER_json_from_data (&dr.purpose, + sizeof (struct TALER_DepositRequestPS)); break; } case TALER_MINTDB_TT_REFRESH_MELT: @@ -457,7 +458,7 @@ compile_transaction_history (const struct TALER_MINTDB_TransactionList *tl) struct TALER_RefreshMeltCoinAffirmationPS ms; const struct TALER_MINTDB_RefreshMelt *melt = pos->details.melt; - type = "melt"; + type = "MELT"; value = melt->amount_with_fee; ms.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT); ms.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS)); @@ -467,12 +468,12 @@ compile_transaction_history (const struct TALER_MINTDB_TransactionList *tl) TALER_amount_hton (&ms.melt_fee, &melt->melt_fee); ms.coin_pub = melt->coin.coin_pub; - + sig = &melt->coin_sig; /* internal sanity check before we hand out a bogus sig... */ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT, &ms.purpose, - &melt->coin_sig.eddsa_signature, + &sig->eddsa_signature, &melt->coin.coin_pub.eddsa_pub)) { GNUNET_break (0); @@ -480,18 +481,20 @@ compile_transaction_history (const struct TALER_MINTDB_TransactionList *tl) return NULL; } - transaction = TALER_json_from_eddsa_sig (&ms.purpose, - &melt->coin_sig.eddsa_signature); + details = TALER_json_from_data (&ms.purpose, + sizeof (struct TALER_RefreshMeltCoinAffirmationPS)); } break; default: GNUNET_assert (0); } json_array_append_new (history, - json_pack ("{s:s, s:o, s:o}", + json_pack ("{s:s, s:o, s:o, s:o}", "type", type, "amount", TALER_json_from_amount (&value), - "signature", transaction)); + "signature", TALER_json_from_data (sig, + sizeof (struct TALER_CoinSpendSignatureP)), + "details", details)); } return history; } @@ -539,7 +542,6 @@ compile_reserve_history (const struct TALER_MINTDB_ReserveHistory *rh, struct TALER_Amount withdraw_total; struct TALER_Amount value; json_t *json_history; - json_t *transaction; int ret; const struct TALER_MINTDB_ReserveHistory *pos; struct TALER_WithdrawRequestPS wr; @@ -609,14 +611,13 @@ compile_reserve_history (const struct TALER_MINTDB_ReserveHistory *rh, GNUNET_CRYPTO_rsa_public_key_hash (pos->details.withdraw->denom_pub.rsa_public_key, &wr.h_denomination_pub); wr.h_coin_envelope = pos->details.withdraw->h_coin_envelope; - - transaction = TALER_json_from_eddsa_sig (&wr.purpose, - &pos->details.withdraw->reserve_sig.eddsa_signature); - json_array_append_new (json_history, - json_pack ("{s:s, s:o, s:o}", + json_pack ("{s:s, s:o, s:o, s:o}", "type", "WITHDRAW", - "signature", transaction, + "signature", TALER_json_from_data (&pos->details.withdraw->reserve_sig, + sizeof (struct TALER_ReserveSignatureP)), + "details", TALER_json_from_data (&wr, + sizeof (wr)), "amount", TALER_json_from_amount (&value))); break; } diff --git a/src/mint/taler-mint-httpd_tracking.c b/src/mint/taler-mint-httpd_tracking.c new file mode 100644 index 000000000..beb4b4dd1 --- /dev/null +++ b/src/mint/taler-mint-httpd_tracking.c @@ -0,0 +1,75 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-mint-httpd_tracking.c + * @brief Handle wire transfer tracking-related requests + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include <pthread.h> +#include "taler-mint-httpd_parsing.h" +#include "taler-mint-httpd_tracking.h" +#include "taler-mint-httpd_responses.h" + + +/** + * Handle a "/wire/deposits" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_TRACKING_handler_wire_deposits (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + GNUNET_break (0); // not implemented + return MHD_NO; +} + + +/** + * Handle a "/deposit/wtid" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_TRACKING_handler_deposit_wtid (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + GNUNET_break (0); // not implemented + return MHD_NO; +} + + +/* end of taler-mint-httpd_tracking.c */ diff --git a/src/mint/taler-mint-httpd_tracking.h b/src/mint/taler-mint-httpd_tracking.h new file mode 100644 index 000000000..9ec4c6827 --- /dev/null +++ b/src/mint/taler-mint-httpd_tracking.h @@ -0,0 +1,65 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-mint-httpd_tracking.h + * @brief Handle wire transfer tracking-related requests + * @author Christian Grothoff + */ +#ifndef TALER_MINT_HTTPD_TRACKING_H +#define TALER_MINT_HTTPD_TRACKING_H + +#include <gnunet/gnunet_util_lib.h> +#include <microhttpd.h> +#include "taler-mint-httpd.h" + + +/** + * Handle a "/wire/deposits" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_TRACKING_handler_wire_deposits (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + + +/** + * Handle a "/deposit/wtid" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_TRACKING_handler_deposit_wtid (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + + +#endif diff --git a/src/util/json.c b/src/util/json.c index 9f7623b2a..cea0118e1 100644 --- a/src/util/json.c +++ b/src/util/json.c @@ -104,40 +104,6 @@ TALER_json_from_abs (struct GNUNET_TIME_Absolute stamp) /** - * Convert a signature (with purpose) to a JSON object representation. - * - * @param purpose purpose of the signature - * @param signature the signature - * @return the JSON reporesentation of the signature with purpose - */ -json_t * -TALER_json_from_eddsa_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, - const struct GNUNET_CRYPTO_EddsaSignature *signature) -{ - json_t *root; - json_t *el; - - root = json_object (); - - el = json_integer ((json_int_t) ntohl (purpose->size)); - json_object_set_new (root, "size", el); - - el = json_integer ((json_int_t) ntohl (purpose->purpose)); - json_object_set_new (root, "purpose", el); - - el = TALER_json_from_data (purpose, - ntohl (purpose->size)); - json_object_set_new (root, "eddsa_val", el); - - el = TALER_json_from_data (signature, - sizeof (struct GNUNET_CRYPTO_EddsaSignature)); - json_object_set_new (root, "eddsa_sig", el); - - return root; -} - - -/** * Convert RSA public key to JSON. * * @param pk public key to convert diff --git a/website/README b/website/README deleted file mode 100644 index 0031645dd..000000000 --- a/website/README +++ /dev/null @@ -1,3 +0,0 @@ -This directory is the root of the mint's website, mainly -intended to provide a way to the user to wire funds to the -mints in the act of creating a reserve. diff --git a/website/fake_wire_transfer.php b/website/fake_wire_transfer.php deleted file mode 100644 index a788a372e..000000000 --- a/website/fake_wire_transfer.php +++ /dev/null @@ -1,79 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<title>Fake Wire Transfer</title> - <script> - /* - @licstart The following is the entire license notice for the - JavaScript code in this page. - - Copyright (C) 2014,2015 GNUnet e.V. - - The JavaScript code in this page is free software: you can - redistribute it and/or modify it under the terms of the GNU - Lesser General Public License (GNU LGPL) as published by the Free Software - Foundation, either version 2.1 of the License, or (at your option) - any later version. The code is distributed WITHOUT ANY WARRANTY; - without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU LGPL for more details. - - As additional permission under GNU LGPL version 2.1 section 7, you - may distribute non-source (e.g., minimized or compacted) forms of - that code without the copy of the GNU LGPL normally required by - section 4, provided you include this license notice and a URL - through which recipients can access the Corresponding Source. - - @licend The above is the entire license notice - for the JavaScript code in this page. - */ - </script> -</head> -<body> -<!-- - This page's main aim is to forward the fake wire transfer - request to the demonstrator and to inform the customer - about the result. In a real-world deployment, this - page would not be required as the customer would do a - wire transfer with his bank instead. - --> -<?php - -// Evaluate form -$reserve_pk = $_POST['reserve_pk']; -$kudos_amount = $_POST['kudos_amount']; -$mint = $_SERVER['SERVER_NAME']; - -// pack the JSON -$json = json_encode (array ('reserve_pub' => $reserve_pk, - 'execution_date' => "/Date(" . time() . ")/", - 'wire' => array ('type' => 'test'), - 'amount' => array ('value' => intval($kudos_amount), - 'fraction' => 0, - 'currency' => 'KUDOS'))); // TODO 'KUDOS' example needs 'KUDOS' denom keys .. - -// craft the HTTP request -$req = new http\Client\Request ("POST", - "http://" . $mint . "/admin/add/incoming", - array ("Content-Type" => "application/json")); -$req->getBody()->append ($json); - -// execute HTTP request -$client = new http\Client; -$client->enqueue($req)->send (); -$resp = $client->getResponse (); - -// evaluate response -$status_code = $resp->getResponseCode (); -http_response_code ($status_code); - -if ($status_code != 200) -{ - echo "Error $status_code when faking the wire transfer. Please report to taler@gnu.org"; -} -else -{ - echo "Pretend wire transfer successful. Exit through the <a href=\"http://toy.taler.net\">gift shop</a> and enjoy shopping!"; -} -?> -</body> -</html> diff --git a/website/index.html b/website/index.html deleted file mode 100644 index c79f3bbbb..000000000 --- a/website/index.html +++ /dev/null @@ -1,114 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <title>Taler's "Toy" Shop</title> - <link rel="stylesheet" type="text/css" href="style.css"> - <script> - /* - @licstart The following is the entire license notice for the - JavaScript code in this page. - - Copyright (C) 2015 GNUnet e.V. - - The JavaScript code in this page is free software: you can - redistribute it and/or modify it under the terms of the GNU - Lesser General Public License (GNU LGPL) as published by the Free Software - Foundation, either version 2.1 of the License, or (at your option) - any later version. The code is distributed WITHOUT ANY WARRANTY; - without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU LGPL for more details. - - As additional permission under GNU LGPL version 2.1 section 7, you - may distribute non-source (e.g., minimized or compacted) forms of - that code without the copy of the GNU LGPL normally required by - section 4, provided you include this license notice and a URL - through which recipients can access the Corresponding Source. - - @licend The above is the entire license notice - for the JavaScript code in this page. - */ - </script> -</head> -<body id="css-zen-garden" onload="signal_me()"> - <div class="explanation" id="zen-explanation" role="article"> - <h1>Welcome to the Taler "Demo" Mint</h1> - <p>Through this page, you can wire funds to our demonstrator mint - in order to create your reserves from where withdraw your coins. - Once the reserve is created, the withdrawing of coins will be - entirely handled by the wallet, with no intervention required - to the user. - </p> - <p> - By clicking the button "Wire funds", your wallet will be triggered to - create a new reserve's key which needs to be pasted in the dedicated - field. Fill the desired amount to fund into the key's dialog, in order - to make the wallet check when the mint get all the funds for the reserve. - </p> - - <input id="wire-funds-button" type="button" value="Wire funds" disabled=true - onclick='wire_funds();'></input> - </div> - <form id="reserve-form" name="tform" action="/fake_wire_transfer.php" method="POST"> - <div class="participation" id="fake-wire"> - <br> - Paste your reserve public key here (right-click, "paste"): - <input type="text" name="reserve_pk"></input> - <br> - Amount to credit to your reserve: - <select id="amount" name="kudos_amount"> - <option value="1">1 KUDOS</option> - <option value="2">2 KUDOS</option> - <option value="5">5 KUDOS</option> - <option value="10">10 KUDOS</option> - <option value="1000">1000 KUDOS</option> - </select> - <br> - <input type="submit" value="Submit"></input> - <br> - </div> - </form> - </div> - <script type="text/javascript"> - - /* The wallet should send this signal only when: - - it gets installed - - it gets a 'taler-wire-mfirst' event */ - - document.body.addEventListener("taler-wallet-present", - has_taler_wallet_cb, - false); - - document.body.addEventListener("taler-unload", - no_taler_wallet_cb, - false); - - document.body.addEventListener("taler-load", - signal_me, - false); - - - - function wire_funds(){ - var trigger = new Event("taler-create-reserve"); - document.body.dispatchEvent(trigger); - }; - - /* The mint signals itself to the wallet */ - function signal_me(){ - var eve = new Event('taler-checkout-probe'); - document.body.dispatchEvent(eve); - }; - - function has_taler_wallet_cb(){ - var button = document.getElementById("wire-funds-button"); - button.removeAttribute("disabled"); - } - - function no_taler_wallet_cb(){ - var button = document.getElementById("wire-funds-button"); - button.setAttribute("disabled", "true"); - } - - </script> -</body> -</html> diff --git a/website/style.css b/website/style.css deleted file mode 100644 index a476a630e..000000000 --- a/website/style.css +++ /dev/null @@ -1,328 +0,0 @@ -/* css Zen Garden submission 020 - 'Friendly Beaches' by Sophie G - www.sophie-g.net */ -/* css released under Creative Commons ShareAlike License v1.0 - http://creativecommons.org/licenses/sa/1.0/ */ -/* All associated graphics copyright 2003, Sophie G */ - - -/* IMPORTANT */ -/* This design is not a template. You may not reproduce it elsewhere without the designer's written permission. */ -/* The CSS itself may freely be used for anything you wish, but the associated graphics belong to the designer. */ -/* What you may use - .css files. What you may not use - .jpg, .gif, and .png files. */ - - - -body { - background-color: #FFFFFF; - margin: 0px; - padding: 10px; - font-family: Ubuntu, Tahoma, Sans; - font-size: 90%; -} - -p { - line-height: 180%; -} -.page-wrapper { - background-color: #EDEEF0; - border: 1px solid #DAD5D9; - padding: 0px; - margin: 0px; -} -abbr { - border-bottom: 1px #6BA0D2 dashed; -} -header,h1 { - font-family: Impact, Georgia, serif; - background-color: #FFFFFF; -} -header h1 { - height: 190px; - margin: 0px; - background-color: black; - background-image: url(Gala.jpg); - background-repeat: no-repeat; -} -header h1, header h2 { - text-indent: 100%; - white-space: nowrap; - overflow: hidden; -} -header h2 { - background-color: #EDEEF0; - margin: 0px; - height: 24px; - padding: 0px 14px 2px 14px; - background-image: url(headerh2.gif); - background-repeat: no-repeat; -} -header h2 abbr { - color: #FFF; - border-bottom: 1px #FFF dashed; -} -.summary p:first-child { - position: absolute; - right: 21px; - top: 21px; - width: 12em; - height: 165px; - padding: 3px 0 0 0; - margin: 0px; - border: 1px solid #0C2953; - text-align: center; - font-size: 90%; - font-family: Impact, Georgia, serif; - line-height: 110%; - color: #FFF; - background-image: url(summary.jpg); - background-repeat: no-repeat; - background-position: 50% 100%; - background-color: #0C2953; -} -.preamble { - padding: 0px 15em 0px 3em; -} -.preamble h3 { - margin: 15px 0px 0px 0px; - padding: 6px 20px 2px 6px; - text-align: left; - font-size: 180%; - font-variant: small-caps; - color: #6BA0D2; - background-image: url(bordDroitPreambleh3.gif); - background-repeat: no-repeat; - background-position: 100% 0px; - background-color: #F6F7F7; -} -.preamble h3::before { - content: " "; - display: inline-block; - padding: 35px 0px 30px 48px; - background-image: url(preambleShell.jpg); - background-repeat: no-repeat; - background-position: 0px 50%; - vertical-align: middle; -} -.preamble p { - text-align: justify; -} -.preamble p:nth-child(2), .preamble p:nth-child(3) { - margin: 0px; - padding: 10px 20px 2px 6px; - background-image: url(bordDroitPreamble.gif); - background-repeat: repeat-y; - background-position: 100% 0px; - background-color: #F6F7F7; -} -.preamble p:nth-child(4) { - margin: 0px; - padding: 0px; - background-image: url(bordBasPreamble.gif); - background-repeat: no-repeat; - background-position: 100% 100%; - background-color: #F6F7F7; - padding: 10px 20px 20px 6px; -} -.preamble p:nth-child(4)::after { - content: " "; - display: block; - margin: 0px; - padding: 0 20px 0 6px; - background-image: url(bordBasPreambleGauche.gif); - background-repeat: no-repeat; - background-position: 0% 100%; -} -.supporting { - margin: 0px; - padding: 10px 16em 0px 1em; - font-size: 90%; -} -.explanation, .participation, .benefits, .requirements { - border: 2px solid #FFF; - padding: 0px; -} -.explanation { - margin: 0px 0px 10px 0px; -} -.participation { - margin: 0px 0px 10px 0px; - float: left; - width: 30%; -} -.benefits { - margin: 0px 0px 10px 32%; -} -.requirements { - margin: 0px 0px 10px 32%; -} -.supporting h3 { - margin: 0px; - padding: 5px 30px 1px 2px; - text-align: left; - font-size: 120%; - font-variant: small-caps; - color: #6BA0D2; - border-bottom: 1px solid #6BA0D2; - background-color: #F6F7F7; - background-repeat: no-repeat; - background-position: 100% 50%; -} -.explanation h3 { - background-image: url(explanationShell.jpg); -} -.supporting form { - padding: 30px; - display: inline-block; - clear: both; - width: 120em; -} -.supporting input { - margin: 8px; -} -.participation h3 { - background-image: url(participationShell.jpg); -} -.benefits h3 { - background-image: url(benefitsShell.jpg); -} -.requirements h3 { - background-image: url(requirementsRock.jpg); -} -.supporting p { - text-align: justify; - margin: 10px 0px 0px 0px; - padding: 1px 3px 2px 3px; -} -.supporting a:link { - color: #0083FF; - font-weight: bold; -} -.supporting a:visited { - color: #204160; - font-weight: bold; -} -.supporting a:hover, .supporting a:active { - color: #8C0000; - font-weight: bold; - text-decoration: none; -} -.sidebar, .summary p:last-child { - font-size: 90%; - font-family: Impact, Georgia, serif; - position: absolute; - right: 21px; - width: 12em; - border: 1px solid #0C2953; - padding: 0px; - margin: 0px; - background-color: #A4ACB3; -} -.summary p:last-child { - height: 50px; - text-align: center; - top: 217px; - font-size: 90%; - display: block; - padding: 3px 0; - color: #FFF; -} -.sidebar { - top: 266px; -} -.sidebar h3 { - background-color: #0C2953; - color: #FFF; - margin: 0px; - padding: 30px 1px 1px 1px; - background-repeat: no-repeat; -} -.sidebar h3.select { - background-image: url(selecth3.jpg); - background-position: 50% 0%; -} -.sidebar h3.favorites { - border-top: 1px solid #0C2953; - background-image: url(favoritesh3.jpg); - background-position: 40% 100%; -} -.sidebar h3.archives { - border-top: 1px solid #0C2953; - background-image: url(archivesh3.jpg); - background-position: 40% 100%; -} -.sidebar h3.resources { - border-top: 1px solid #0C2953; - background-image: url(resourcesh3.jpg); - background-position: 50% 30%; -} -.sidebar ul { - list-style-type: none; - font-size: 90%; - color: #FFF; - margin: 0px; - padding: 0px; - background-color: #A4ACB3; -} -.sidebar li { - padding: 3px 2px 3px 2px; - margin-bottom: 4px; -} -.sidebar li:hover { - padding: 2px 1px 2px 1px; - border: 1px dotted #0C2953; - background-color: #6BA0D2; -} -.summary p:last-child a:link { - color: #FFF; - font-weight: bold; -} -.summary p:last-child a:visited { - color: #204160; -} -.summary p:last-child a:hover, .summary p:last-child a:active { - color: #FFD800; - font-weight: bold; - text-decoration: none; -} -.sidebar a:link, .sidebar a:visited { - border-left: 6px solid #FFF; - padding-left: 2px; - font-weight: bold; - color: #FFF; -} -.sidebar a:visited { - color: #204160; -} -.sidebar a:hover, .sidebar a:active { - border-left: 6px solid #FFD800; - padding-left: 2px; - color: #FFD800; - text-decoration: none; - font-weight: bold; -} -.sidebar a.designer-name:link, .sidebar a.designer-name:visited { - border-left: none; - padding-left: 0px; - font-weight: normal; - color: #FFF; -} -.sidebar a.designer-name:hover, .sidebar a.designer-name:active { - border-left: none; - padding-left: 0px; - color: #FFD800; - text-decoration: none; -} -.sidebar abbr { - border-bottom: 1px #FFF dashed; -} -footer { - clear: both; - text-align: right; - margin: 0px -16em 0px 0px; - padding: 25px 0px 0px 0px; - background-repeat: no-repeat; - background-image: url(signSoph.gif); - background-position: 0% 95%; -} -footer a { - font-size: 70%; - font-family: Impact, Georgia, serif; -} |