lsd0009

LSD0009: The GNU Taler Protocol
Log | Files | Refs | README

commit 879739666fd6f37b2f193716d62c06c0447cae71
parent 443203a6b584700ce027524f3b902e91f77164ef
Author: Mikolai Gütschow <mikolai.guetschow@tu-dresden.de>
Date:   Fri, 13 Jun 2025 15:53:10 +0200

protocol: add payment and deposit

Diffstat:
Mdraft-guetschow-taler-protocol.md | 146++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Mdraft-guetschow-taler-protocol.xml | 360+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
2 files changed, 383 insertions(+), 123 deletions(-)

diff --git a/draft-guetschow-taler-protocol.md b/draft-guetschow-taler-protocol.md @@ -371,36 +371,38 @@ persist (reserve, value) | master_secret = random(256) | persist master_secret | *(coin, blind_secret) = GenerateCoin(master_secret, ?*) | +*coin.h_denom = SHA-512(bigEndian(32, 0) | bigEndian(32, 1) | denom.pub) *blind_coin = RSA-FDH-Blind(SHA-512(coin.pub), blind_secret, denom.pub) -sig = EdDSA-Sign(reserve.priv, msg) | +sig0 = EdDSA-Sign(reserve.priv, msg0) | | | |-------------- /withdraw -------------->| - | (reserve.pub, *SHA-512(denom.pub), | - | *blind_coin, sig) | + | (reserve.pub, *coin.h_denom, | + | *blind_coin, sig) | | | + | *denom = Denom-Lookup(coin.h_denom) | check *denom.pub known and not withdrawal-expired | check EdDSA-Verify(reserve.pub, msg, sig) | check reserve KYC status ok or not needed - | check reserve.balance >= sum(*denom.valueAndFee) - | reserve.balance -= sum(*denom.valueAndFee) - | *blind_sig = RSA-FDH-Sign(b, denom.priv) + | check reserve.balance >= sum(*denom.value + *denom.fee_withdraw) + | reserve.balance -= sum(*denom.value + *denom.fee_withdraw) + | *blind_sig = RSA-FDH-Sign(blind_coin, denom.priv) | persist withdrawal | | |<------------ *blind_sig ---------------| | | -*coin_sig = RSA-FDH-Unblind(blind_sig, blind_secret, denom.pub) -check *RSA-FDH-Verify(SHA-512(coin.pub), coin_sig, denom.pub) -persist *(coin, blind_secret, coin_sig) +*coin.sig = RSA-FDH-Unblind(blind_sig, blind_secret, denom.pub) +check *RSA-FDH-Verify(SHA-512(coin.pub), coin.sig, denom.pub) +persist *(coin, blind_secret) ~~~ -where `msg` is formed as follows: +where `msg0` is formed as follows: ~~~ -msg = bigEndian(32, 160) | bigEndian(32, 1200) /* TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW */ - | bigEndianAmount(sum(*denom.value)) | bigEndianAmount(sum(*denom.fee_withdrawal)) - | SHA-512( *(SHA-512(denom.pub) | bigEndian(32, 0x1) | blind_coin) ) - | bigEndian(256, 0x0) - | bigEndian(32, 0x0) | bigEndian(32, 0x0) +msg0 = bigEndian(32, 160) | bigEndian(32, 1200) /* TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW */ + | bigEndianAmount(sum(*denom.value)) | bigEndianAmount(sum(*denom.fee_withdraw)) + | SHA-512( *(SHA-512(denom.pub) | bigEndian(32, 0x1) | blind_coin) ) + | bigEndian(256, 0x0) + | bigEndian(32, 0x0) | bigEndian(32, 0x0) ~~~ The wallet derives coins and blinding secrets using `GenerateCoin` from a master secret and an integer index. @@ -431,6 +433,120 @@ coin.pub = EdDSA-GetPub(coin.priv) (for RSA, without age-restriction) +## Payment + +~~~ + wallet merchant +knows merchant.pub knows merchant.priv +knows valid *coin knows exchange, payto + | | + | wire_salt = random(128) + | persist order = (id, price, info, token?, wire_salt) + | | + |<-------- (order.{id,token?}) ----------| + | | +nonce = EdDSA-Keygen() | +persist nonce.priv | + | | + |------- /orders/{order.id}/claim ------>| + | (nonce, token?) | + | | + | h_wire = HKDF(wire_salt, payto, + | "merchant-wire-signature", 64) + | determine timestamp, refund_deadline, wire_deadline + | contract = (order.{id,price,info,token?}, + | exchange, h_wire, timestamp, + | refund_deadline, wire_deadline) + | check contract.token = token? + | contract.nonce = nonce + | persist contract + | sig0 = EdDSA-Sign(merchant.priv, msg0) + | | + |<----------- contract, sig0 ------------| + | | +check EdDSA-Verify(merchant.pub, msg0, sig0) | +check contract.nonce = nonce | +TODO: double-check extra hash check? | +*(coin, fraction) = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here +*sig1 = EdDSA-Sign(*coin.priv, *msg1) | +*deposit = *(coin.{pub,sig,h_denom}, fraction, sig1) | +persist (contract, sig, *deposit) | + | | + |------- /orders/{order.id}/pay -------->| + | (*deposit) | + | | + | check sum(*deposit.fraction) = contract.price + | *check Deposit(deposit) + | sig2 = EdDSA-Sign(merchant.priv, msg2) + | | + |<---------------- sig2 -----------------| + | | +check EdDSA-Verify(merchant.pub, msg2, sig2) | +~~~ + +TODO: discuss - nonce doesn't strictly need to be EdDSA keypair? + +where `msg0`, `*msg1`, and `msg2` are formed as follows: + +~~~ +h_contract = SHA-512(canonicalJSON(contract)) + +msg0 = bigEndian(32, 72) | bigEndian(32, 1101) /* TALER_SIGNATURE_MERCHANT_CONTRACT */ + | h_contract +*msg1 = bigEndian(32, 456) | bigEndian(32, 1201) /* TALER_SIGNATURE_WALLET_COIN_DEPOSIT */ + | h_contract | bigEndian(256, 0x0) + | bigEndian(512, 0x0) | contract.h_wire | *coin.h_denom + | bigEndianTime(contract.timestamp) | bigEndianTime(contract.refund_deadline) + | bigEndianAmount(*fraction + *denom.fee_deposit) + | bigEndianAmount(*denom.fee_deposit) | merchant.pub | bigEndian(512, 0x0) +msg2 = bigEndian(32, 72) | bigEndian(32, 1104) /* TALER_SIGNATURE_MERCHANT_PAYMENT_OK */ + | h_contract +~~~ + +(without age restriction, policy and wallet data hash) + +## Deposit + +~~~ + merchant/wallet exchange +knows exchange.pub knows exchange.priv +knows merchant.pub knows *denom.pub +knows payto, wire_salt, sig2, h_contract +knows contract, *deposit from Payment + | | + |----------- /batch-deposit ------------>| + | (contract.{timestamp,wire_deadline,refund_deadline} + | h_contract, merchant.pub, sig2, | + | payto, *deposit, wire_salt) | + | | + | *denom = Denom-Lookup(*deposit.coin.h_denom) + | check *denom.pub known and not deposit-expired + | contract.h_wire = HKDF(wire_salt, payto, + | "merchant-wire-signature", 64) + | check *EdDSA-Verify(deposit.coin.pub, msg1, sig1) + | check *RSA-FDH-Verify(SHA-512(deposit.coin.pub), + | deposit.coin.sig, denom.pub) + | check not overspending + | persist deposit-record, mark-spent + | schedule bank transfer + | exchange_timestamp = now() + | sig3 = EdDSA-Sign(exchange.priv, msg3) + | | + |<----- sig3, exchange_timestamp --------| + | | +check EdDSA-Verify(exchange.pub, msg3, sig3) | +~~~ + +where `msg3` is formed as follows: + +~~~ +msg3 = bigEndian(32, 344) | bigEndian(32, 1033) /* TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT */ + | h_contract | contract.h_wire | bigEndian(512, 0x0) + | bigEndianTime(exchange_timestamp) | bigEndianTime(contract.wire_deadline) + | bigEndianTime(contract.refund_deadline) + | bigEndianAmount(sum(*deposit.fraction - *denom.fee_deposit)) + | SHA-512(*deposit.sig1) | merchant.pub +~~~ # Security Considerations diff --git a/draft-guetschow-taler-protocol.xml b/draft-guetschow-taler-protocol.xml @@ -29,7 +29,7 @@ </address> </author> - <date year="2025" month="May" day="26"/> + <date year="2025" month="June" day="13"/> <workgroup>independent</workgroup> @@ -402,36 +402,38 @@ persist (reserve, value) | master_secret = random(256) | persist master_secret | *(coin, blind_secret) = GenerateCoin(master_secret, ?*) | +*coin.h_denom = SHA-512(bigEndian(32, 0) | bigEndian(32, 1) | denom.pub) *blind_coin = RSA-FDH-Blind(SHA-512(coin.pub), blind_secret, denom.pub) -sig = EdDSA-Sign(reserve.priv, msg) | +sig0 = EdDSA-Sign(reserve.priv, msg0) | | | |-------------- /withdraw -------------->| - | (reserve.pub, *SHA-512(denom.pub), | - | *blind_coin, sig) | + | (reserve.pub, *coin.h_denom, | + | *blind_coin, sig) | | | + | *denom = Denom-Lookup(coin.h_denom) | check *denom.pub known and not withdrawal-expired | check EdDSA-Verify(reserve.pub, msg, sig) | check reserve KYC status ok or not needed - | check reserve.balance >= sum(*denom.valueAndFee) - | reserve.balance -= sum(*denom.valueAndFee) - | *blind_sig = RSA-FDH-Sign(b, denom.priv) + | check reserve.balance >= sum(*denom.value + *denom.fee_withdraw) + | reserve.balance -= sum(*denom.value + *denom.fee_withdraw) + | *blind_sig = RSA-FDH-Sign(blind_coin, denom.priv) | persist withdrawal | | |<------------ *blind_sig ---------------| | | -*coin_sig = RSA-FDH-Unblind(blind_sig, blind_secret, denom.pub) -check *RSA-FDH-Verify(SHA-512(coin.pub), coin_sig, denom.pub) -persist *(coin, blind_secret, coin_sig) +*coin.sig = RSA-FDH-Unblind(blind_sig, blind_secret, denom.pub) +check *RSA-FDH-Verify(SHA-512(coin.pub), coin.sig, denom.pub) +persist *(coin, blind_secret) ]]></artwork></figure> -<t>where <spanx style="verb">msg</spanx> is formed as follows:</t> +<t>where <spanx style="verb">msg0</spanx> is formed as follows:</t> <figure><artwork><![CDATA[ -msg = bigEndian(32, 160) | bigEndian(32, 1200) /* TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW */ - | bigEndianAmount(sum(*denom.value)) | bigEndianAmount(sum(*denom.fee_withdrawal)) - | SHA-512( *(SHA-512(denom.pub) | bigEndian(32, 0x1) | blind_coin) ) - | bigEndian(256, 0x0) - | bigEndian(32, 0x0) | bigEndian(32, 0x0) +msg0 = bigEndian(32, 160) | bigEndian(32, 1200) /* TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW */ + | bigEndianAmount(sum(*denom.value)) | bigEndianAmount(sum(*denom.fee_withdraw)) + | SHA-512( *(SHA-512(denom.pub) | bigEndian(32, 0x1) | blind_coin) ) + | bigEndian(256, 0x0) + | bigEndian(32, 0x0) | bigEndian(32, 0x0) ]]></artwork></figure> <t>The wallet derives coins and blinding secrets using <spanx style="verb">GenerateCoin</spanx> from a master secret and an integer index. @@ -463,6 +465,123 @@ coin.pub = EdDSA-GetPub(coin.priv) <t>(for RSA, without age-restriction)</t> </section> +<section anchor="payment"><name>Payment</name> + +<figure><artwork><![CDATA[ + wallet merchant +knows merchant.pub knows merchant.priv +knows valid *coin knows exchange, payto + | | + | wire_salt = random(128) + | persist order = (id, price, info, token?, wire_salt) + | | + |<-------- (order.{id,token?}) ----------| + | | +nonce = EdDSA-Keygen() | +persist nonce.priv | + | | + |------- /orders/{order.id}/claim ------>| + | (nonce, token?) | + | | + | h_wire = HKDF(wire_salt, payto, + | "merchant-wire-signature", 64) + | determine timestamp, refund_deadline, wire_deadline + | contract = (order.{id,price,info,token?}, + | exchange, h_wire, timestamp, + | refund_deadline, wire_deadline) + | check contract.token = token? + | contract.nonce = nonce + | persist contract + | sig0 = EdDSA-Sign(merchant.priv, msg0) + | | + |<----------- contract, sig0 ------------| + | | +check EdDSA-Verify(merchant.pub, msg0, sig0) | +check contract.nonce = nonce | +TODO: double-check extra hash check? | +*(coin, fraction) = CoinSelection(contract.{exchange,price}) TODO: include MarkDirty here +*sig1 = EdDSA-Sign(*coin.priv, *msg1) | +*deposit = *(coin.{pub,sig,h_denom}, fraction, sig1) | +persist (contract, sig, *deposit) | + | | + |------- /orders/{order.id}/pay -------->| + | (*deposit) | + | | + | check sum(*deposit.fraction) = contract.price + | *check Deposit(deposit) + | sig2 = EdDSA-Sign(merchant.priv, msg2) + | | + |<---------------- sig2 -----------------| + | | +check EdDSA-Verify(merchant.pub, msg2, sig2) | +]]></artwork></figure> + +<t>TODO: discuss - nonce doesn't strictly need to be EdDSA keypair?</t> + +<t>where <spanx style="verb">msg0</spanx>, <spanx style="verb">*msg1</spanx>, and <spanx style="verb">msg2</spanx> are formed as follows:</t> + +<figure><artwork><![CDATA[ +h_contract = SHA-512(canonicalJSON(contract)) + +msg0 = bigEndian(32, 72) | bigEndian(32, 1101) /* TALER_SIGNATURE_MERCHANT_CONTRACT */ + | h_contract +*msg1 = bigEndian(32, 456) | bigEndian(32, 1201) /* TALER_SIGNATURE_WALLET_COIN_DEPOSIT */ + | h_contract | bigEndian(256, 0x0) + | bigEndian(512, 0x0) | contract.h_wire | *coin.h_denom + | bigEndianTime(contract.timestamp) | bigEndianTime(contract.refund_deadline) + | bigEndianAmount(*fraction + *denom.fee_deposit) + | bigEndianAmount(*denom.fee_deposit) | merchant.pub | bigEndian(512, 0x0) +msg2 = bigEndian(32, 72) | bigEndian(32, 1104) /* TALER_SIGNATURE_MERCHANT_PAYMENT_OK */ + | h_contract +]]></artwork></figure> + +<t>(without age restriction, policy and wallet data hash)</t> + +</section> +<section anchor="deposit"><name>Deposit</name> + +<figure><artwork><![CDATA[ + merchant/wallet exchange +knows exchange.pub knows exchange.priv +knows merchant.pub knows *denom.pub +knows payto, wire_salt, sig2, h_contract +knows contract, *deposit from Payment + | | + |----------- /batch-deposit ------------>| + | (contract.{timestamp,wire_deadline,refund_deadline} + | h_contract, merchant.pub, sig2, | + | payto, *deposit, wire_salt) | + | | + | *denom = Denom-Lookup(*deposit.coin.h_denom) + | check *denom.pub known and not deposit-expired + | contract.h_wire = HKDF(wire_salt, payto, + | "merchant-wire-signature", 64) + | check *EdDSA-Verify(deposit.coin.pub, msg1, sig1) + | check *RSA-FDH-Verify(SHA-512(deposit.coin.pub), + | deposit.coin.sig, denom.pub) + | check not overspending + | persist deposit-record, mark-spent + | schedule bank transfer + | exchange_timestamp = now() + | sig3 = EdDSA-Sign(exchange.priv, msg3) + | | + |<----- sig3, exchange_timestamp --------| + | | +check EdDSA-Verify(exchange.pub, msg3, sig3) | +]]></artwork></figure> + +<t>where <spanx style="verb">msg3</spanx> is formed as follows:</t> + +<figure><artwork><![CDATA[ +msg3 = bigEndian(32, 344) | bigEndian(32, 1033) /* TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT */ + | h_contract | contract.h_wire | bigEndian(512, 0x0) + | bigEndianTime(exchange_timestamp) | bigEndianTime(contract.wire_deadline) + | bigEndianTime(contract.refund_deadline) + | bigEndianAmount(sum(*deposit.fraction - *denom.fee_deposit)) + | SHA-512(*deposit.sig1) | merchant.pub +]]></artwork></figure> + +</section> </section> <section anchor="security-considerations"><name>Security Considerations</name> @@ -570,7 +689,7 @@ coin.pub = EdDSA-GetPub(coin.priv) -<?line 443?> +<?line 559?> <section anchor="change-log"><name>Change log</name> @@ -588,97 +707,122 @@ Education and Research (BMBF) within the project Concrete Contracts.</t> </back> <!-- ##markdown-source: -H4sIAAAAAAAAA81a3XbbNhK+51Ogzg3lFWVJdtxEG7Xr+Cf2SaL22E5zdlNv -BJGQxDVFavljW23dJ9u7fbH9ZgBQpCzHzqbdrY+PRIGDwWDmm8EMAM/znKue -2HacPMwj1RMb51MlXg3eiXMZqVR8nyZ54ifRhhMkfixnoAhSOc69SaHyzJ8m -115OhN7cEDq+zNUkSRc9EcbjxHHCedoTeVpkebfdft7uOtdJejlJk2JOFIGa -K3zEuZPlqZKzetulWoA66DlCeILH4Sc/XczzZJLK+XTBDcqX2ZSf5nIxQ8/M -cZ5cqbhQPeeJEKmaJz0xzfN51tvamoR5axIXscpbSTrZirKgDcFaaN4i4gjy -Z/mSHO/XkG85jizyaZJCNg8jC6GV8za8TCIZilf//pdWD79Dx544f3cgDlKV -YWbiXRxeqTQL84VIxuJc+dM4iZLJgqnlaJSqK+pg6bmZFKQg2LGKZtMkyn9C -Q0t02vzSB6tejdxPAshz4LU77d3npqWIczLMK5XOZKwHUzMZRj0x03K3SrP+ -JS+8QLNrBcpx4gR9ckhNxjg92u+27UOnvWMenz7bfW4ed7vb3Hr8+uAIUnx3 -0uq08d/+euv518+8bW93p+t1dkDlff1xeweEZ8dnJd1uu/tsa3Bydt46Ovn+ -rNV51vZ2ACTAqZTBcTzPg6agA+nnjvPjB3H+8r348UK/mIVBEEHqJ+IEU06C -ws/DJK6RvVTXMlUin8ocH2EmAPCCsCPwnOVhFAlCqhfGhO0JVJEJGQdiJhfQ -ZJzLMBYqTZM0aznvMiXAZpEUqUiuY5GG2eVXNPogyaUe2RPDDTnyN4YCGk0A -MQyqRBTmKpUR2TaMJ2IIiqFQMdkuEDITe2f7JyfiA+v7gnhI8YsY1XlAGPK5 -mAciOElxHeZTMSL6uQz+ptLE7XSbQjbqHUeLXNmRZVP8BDoP9DR0nmjxVDwB -J/DsdJk8I56jMM/cGzBzh9xGz40a51kYhzNMKy5mIwQR9Kc+wmXyhoiVD23K -dEHjwDmhW9I79yyiPJxHSqDRDzOaURgjoIDLjR57chgHoYzdzm5T3KzMqLML -kWWWiyycxOE49CXY8tAQgQhKXqWSYUW4Nlla6wPhBu9djOMpHqhRG3ZvRk7k -Sv7C6MAKoRKMxmkyg+rH4Y0KvHmCkZZTK00z1B2HADysW5nN7g7sw+9aVzIq -VIPsXL7d7pZvx4R3cCOCmnH1a79IU0xt0Rg2Mcb1VAHiwypjFplUESexF6sJ -+1Opl7lMc6urkQSqLT/iNlwRgVlN0J1sJIo4JDOgcxIrMS1iRA94C/wIpACR -21Hes8a9zMm1hiuTGArtoUpse52OcQZ/Kml8RE9RZNpLLD3HPJiRu6gbUMYT -1SL7pWCfzNzu090KYqTQzdFCTFQMP8zBLlP/LMBLkaAgZ/Rox9sUI+HOkkAM -VlBnMAu0kVaaTFsj1uLMoZc4DytuSnMeNQXIiigRAwoY+8ulLfSx+oazkAxE -69nqy2OseeKoiNkWTPAEUXTPI6l/fpJNJR5uHefXX391TLM7yyYN4X0jprRc -OifxvMh7vArgBX3BjmgSM/LOCavARIA34oXo/n23IxJonlbX74q87EzcmInp -FoQTrKDUm53B8iB53wArfbHdtXxIOGdIDEpcJsyZetvJwMJzYPNM6ZmKnVan -KZ7Sxy59kBZ3W13q8cGsPBetpTqeInSxOvBQUQd+fak6Ot2nv40+dncerQ+a -zV19dEkfXdLHttXHzif0wVp1kZPxwhHY5kapJ4/f/UG1tYKe86WOjOL8BOEg -g7sFmV3IxmEKnmVPG4Xu6jZQ4zDWy8LPPxvQ3Pa0InI1m2P8qj4clr4v6NWH -dm+7c6GFwrpfTS2SOUUX8vsgHI8pdPFqYThtGZBXxv6AdOiCHvSihRiA1ZTH -4iAOc1I8eGtUtodMlEKLDkBiH1HQxIPjt3v7sOp0Jq01qcUjZbpIrpsQ/CZn -q0IVsMucGGi7HBu7+LWgwzKMTdDReYZRYt1ONYBgJAZIpvxU5fxziQ6ZmzXb -9iRKEmsJqUDmkiNmimhMaYPuWscRxBDm29h0RSLtW3jNruXLyC8ihv+K6k1O -e2GU/BrSHqg0vNLKXQm4lN+Sgi+D8a3GIg0HTvd0Ey71aOilC6PpKiuk1BI/ -s1xWV4gPJqe+cFjRyFIMfihbqlmCsxAGy+ENZ8QcBQ5v5vSV5WpOk8yKCbmW -mSbJcdFzhvTtmV5Dkgt5CYME6AQ4h01kESESMktILFfpAGBkg/64BadhrN02 -WgZu6ORmMsqb4uT12ybXhE3xhiH33eu3BiaZNiHRsQkZhpQWUwMjXriSMxYD -Ib1w61eNP3Pn5V84BmmODDK5CpHiYVCd06MfwoG0KS8UjMBLWS8yeOoHAStx -DCglKhQcsKKMHP1mnNQEpEKAoEpqlvMyBxDZHNkr8k9RFi1JvCKli+RUjFA3 -6MTbgFXL1mDaN4ZymYUbbK+IRuY0EXFliBd95DBPNxH3+uJZZ7fdqLvMd2bC -9/F1MeQbw7pxJ9pS7zuONE6iKLnOTMT8/vQ1Rq4ibImFho4exzqA2vjrVlfF -hkNjlAwIeS5YVlC0yoIXtupSYcS2ruq9TQKUfpK8L9HRlT24FvbZmW+bOnSX -sQ6wK9KYHZWk4uBOqEKdQy6M3zFsqdPhT1Qxg4pjkDTuoClW/GO9cwysVe9n -XnOi/7cXPcKN/gd+9Hl4rxlzUIE80IH3Ns0AH15DAoWOMwaOfQwzWoYjlBQj -CzKpN14ozOuFPPyJfCVneQ0aLEVftJ0gQS2VhxGL+0IMtOw3xg9Wg2mtTqSa -2LBqNHXB7g4ajXLy/QotV/GDBhXRy90hiPCnvuhYpxEDgOVlFMIqZ6ioJTzA -ZhaHASJL5zlTrac4Pdvzjg6OsTqmmfTGwfSW25GFFvN5kuak+spqSkOaLpRa -NcW8GMFA7AzoXHcGm3KapJHbNL2gcekZGOJMA9DKyCoaq1xrFRm8iaBmyGxt -JlQdLxiWRxkXUeTBU2jLh5ddYgQJkisqmXnY1sDkF+izml+USLA+r7M/HWg8 -SHR7ezd0sm37YkOrZE8cnc+z919tOOzM/RWba0tbSRqNO6CoESiDCNpBMF2W -j8qhWfdLWUuuNkyxbThMLd0DNqegRDtYPH/jkpzkUAZOCb2cSEpw4A5wstAP -E1jBmAr8HVTt0GY4Zo80mbMSk1TxdiysOJtR+oyyG14mc2Q77sQPeD9Bj8kb -CFZYNkGnVUOVx+mYckeXWQ1cnPlWwQUCVs+IYE3iEIxMyKxWNdVS5PcBoM1p -1wGQpFwHwDUJ7hcB8KXVAvqW8Ku1Qnxe22ETrjcgzcZDKGIjVFDEgcEyrZuN -W3VIeNB06+LCH8+cttRliWjrMLFWfIQNbVg1JrSxdY0FuWzqi3VB1UmXL9b5 -hZN+VKBIaSPLBga9n1UGGYdmAxIQbuoKbeX90q60MtwxKzW61A+DYnxr0iyc -1E3KrO03ggniCu3uUkYil2lVbeU2/OASbEf9S9uRCzL1sCENXWnJoG5JSCDM -t17zyDIsIeKYGdDYEiRrCs66pYhdX/cnhRvxA6NRO5uqSt/Fo7XOYtpdcHyE -u9hpcJ/lZP6gblPE1mGWkn6qqK/r+GHEh/EVaPCp0oz3r9L1kCetbQpNfy/m -f7CLWd0+uln7Itvoc6PZGuSNrc55OUCn39kOeVqoJtUHJAttXVAFET7KKr9x -FIOk1h4PxSlX8+nT7CpF4X4kkaV4Z3QAm6a0A0/5jN6V0dvt5RG442xtIQAF -SY94RLQWz+kMN8Ny5k+Vf8m58HtUpEEqr1HqcGp0TXEJ+YtOZMQwFt+INu1R -hrE+T0zpwCHL6dVwqUKzQ1g9yGg6MkdVMyrYeLqeM9ubhpvvJym7KpqJmz9N -MlVJmijRbrFUeZKjntE8SIjrUmoxVtCgrYdXzlJ437nKruHMCmRnFJNNAMZS -hgmBjxFNH+mgHktSXV9TYwYhI04bVXpl8kRaBJdiaDGzxWyURGK4OaSe0Eic -65MTX6V8AMvnVTMl48wU5FyjpmquGG6khDycqeX2GKuFtNV0zOHYt5v10xyK -MDf25HLYFi/64ttN1GHx0KST1eLTWPfBP6tA5zIGssUm67AFjN7XoU6HaLVS -9SJhf+TfL45Vcx9F2wHc6LVaTFTsNh7uafAtXMOiafYKHu7530u72uAt/4Q7 -kvEl4g/sPUaJKyrvvlkzppsVo38oP+9ZpJHG7VFpr5zLbyntfZSrmtSSaAm+ -YPiZzFCzfzTLcl9Uzjkf6GkFqnN4zJibLjlQUycGpmMDY78yZ6j7eOvW2Dbh -QQ3qqbtQ98pirPN7e6pCL0k5jfoATVG6TMOkSxrMnEaWSoWjcIW6fva/EywJ -mVs2dIn6i7WwrGFg0858OcHmvdJWNMj5w+8/z/soecWrBjKKWDEvJrRtuIzk -HhbLELH/Mzlr65p0qaYwmzs93m80RxsGX/91H8sRVlnUz5e0XpG8sVLBZ8to -xRrJSNKVgW+QixQz1yiFnXsvDo7UZ7j4Kkvvy1kazGinqVVfo9Kr4DePZ2hD -x9LGvyHeXtTcqiJ73a+8L8H4JvnPikJs4VQO+In4Y7C/ktOviWB2nFpvq751 -cXTZxeSoJkkB5qt3je4kyVQp9FcuDNF5z51bRJ1uG61bm+J8783h6cezk1eD -vfN3p4cf3++9eXN4/vH08Ozw9Af8PDk/Pjjdey82txyt3dUrUKuwrO863iVC -XvlxiRizBflLeZoOddwNhHfEb990uLGMgg3RWJWPVj+ibN99o1msUQtTl9uZ -JqsLuErMKrl6WQtra9ENJN70rC59Q3sTTC+BtmTmk43lfgVnmZTkwqh83zAN -/Txa0IGHvOQLeaCdzSM1K6+PBQopb0S5s68vRdUHAJdYUQGY4hP1C++8yhhV -IWj1za4FSgi6wRhOppyzm/pAb6qUo9Fphj1ljhatStkThJlfZJk+dTuKkhTq -61H2LqoLMKGztvx/qzFayw8s3MPghmtf6wuXWWNli8IkJuYbsmqzsFVY1fpI -Kbhh5+ZWVm69duV2IezGjresiOcyTO19EKKqbX6YE7JP74Fs27uRuuz1GQS8 -HV11bX2Z7VM7FDlfL+FDnjo6SUlNsZJSbeh715WFlgb2gvLOwUaTj1BNPOLM -iNRLtTBG+tDb7l40BT1td3sXCGombJWZ1SuVf1+Mlt2Ng7hUSiH0NRkGVFzL -ifIAAIYw14RURp8pv0jpfvM+bTgE5v5LZq7gHvAV3CfiZG+wd4dikMR0w4Xi -/0hyWS32dfkZJRP6tedTsgGIT/Rd7597ulxTQX9jLKNMbdzWxmEv4yue13wN -go+fdDXKBaSpcvW1aHGkAr6T+5ZP81K6q+IcBoU5kSS7ngLtMvWnwn359uWR -PoU2le08TajioDmRmRQ98OY4Cu//AMn8RM1sLwAA +H4sIAAAAAAAAA80821bbypLv+oo+5GFkjmV8IezEE3aGcAlMgskCcjJnchjc +ltq2DrLk0QXwzmZ/2bzNj01VdbeklgWY3GazsmK7VV1dXfeqbttxHOu6z3qW +lfppIPps7Xwq2NvBR3bOAxGzD3GURm4UrFle5IZ8BhBezMepM8lEmrjT6MZJ +EdCZK0DL5amYRPGiz/xwHFmWP4/7LI2zJO222y/bXesmiq8mcZTNEcITcwH/ +hamVpLHgM3PsSiwA2utbjDmM1qF3bryYp9Ek5vPpggaEy5MpvZvzxQxmJpb1 +7FqEmehbzxiLxTzqs2mazpP+xsbET1uTMAtF2oriyUaQeG0grAXDGwgcAP1J +WoDD8xrwDcviWTqNYqDNgZUZk8w59q+igPvs7f/+j2QPPYOJfXb+cY/txSKB +nbGPoX8t4sRPFywas3PhTsMoiCYLguajUSyucYKGp2FkkADCDkUwm0ZB+hsM +tFinTQ9dQNU3wN3IA3r2nHanvfVSjWRhioJ5K+IZD+ViYsb9oM9mku5WLtZ/ +SzPHk+hanrCsMII5KVCNwjg92O229ZtOe1O9ff5i66V6u9Xt0ejhu70DoOLk +qNVpw7/2Lxsvf3nh9Jytza7T2QQo55fL3iYAnh2e5XBb7e6LjcHR2Xnr4OjD +Wavzou1sgiKBOuU0WJbjOMAp4AF3U8v6x2d2/uYT+8eFfDDzPS8Aqp+xI9hy +5GVu6kehAfZG3PBYsHTKU/jPTxgoeIa6w+B9kvpBwFBTHT9E3Z4AKxLGQ4/N ++AI4GabcD5mI4yhOWtbHRDBAs4iymEU3IYv95OovuPogSrlc2WHDNT5y14YM +OBqBisGiggV+KmIeoGz9cMKGADFkIkTZeYwnbOds9+iIfSZ+XyAOzn5nIxMH +EIM2F9JCqE6c3fjplI0Qfs69/xRxZHe6TcYb5sTRIhV6Zd5kvwGcA/C4dBpJ +8kQ4AUyAs9Ml8ARxjvw0sW8BmT2kMXzfMDDP/NCfwbbCbDYCJwLzcQ6zCbzB +QuECN3m8wHXAOIG3yHeamQWpPw8Eg0HXT3BHfggOBbDcyrUn+6Hn89DubDXZ +bWVHnS0gmScpS/xJ6I99lwNaWhpIQIAcV85kkCKYNkpa8gPcDTy3YR1H0EIN +Y9mdGRqRzekFVgddQa0EROM4mgHrx/6t8Jx5BCsVW8tFM5QTh6DwIN3SbrY2 +QT70rHXNg0w0UM750143fzpGfQdsCGAIVz52sziGrS0awyascTMVoOLDMmIi +GVkRRqETignZU86XOY9TzasRB63W+BDbsEICoZrAdJQRy0IfxQCTo1CwaRaC +9wBrATsCUFAiuyOcF417kaNpDSubGDJpoYL1nE5HGYM75bg+eE+WJdJKNDz5 +PBAjTRG3ABlORAvlFwP6aGZ3n2+VNIYzORws2ESEYIcpoEvEf2eASyChAE7a +Iw1vnY2YPYs8NqhondJZ0DbkSpNgDWBJzhz4EqZ+yUxxz6MmA7AsiNgAHcZu +Edp8F6KvP/NRQBjPqg8PIeaxgywkWRDAM/CiOw5S/eVZMuXw5s6y/vjjD0sN +27Nk0mDOr2yK4dI6CudZ2qcoAA/wBeQIQ2yG1jkhFigP8J69Yt3/2uqwCDiP +0fUkS/PJiI2QqGmeP4EIirPJGDQOpPc96Mo263U1HiTOGiKCXC8jwoyz9WZA +wnPQzTMhd8o2W50me47/beF/yMWtVhdnfFaR56JVsOM5uC5iB7wpsQM+fSs7 +Ot3n34cfW5sr8wN3s8yPLvKji/zoaX5sPsAP4qoNORkFDk8PN3I+OfTsT8qt +ivacFzxSjHMjcAcJmJuX6EA29mPAmc/UXmiZt54Y+6EMC1++KKW560tGpGI2 +h/XL/LCI+m2Gjz63+73OhSQK4n45tYjm6F3Q7j1/PEbXRdFCYdpQSl5a+zOk +Qxf4RgYt8AEQTWktcuIgTvQHx4plO5CJomuRDojtghdU/uDweGcXpDqdcS1N +HHGQmTYk100g/DYlqQIrQC5zRCDlcqjk4hpOh2gYK6cj8wzFRFNOhoLASqQg +iXBjkdLHQjt4qmK2nomQSFahUh5POXnMGLwxpg1yqqlHQAZTr0qmFYqkbcFj +Mi2XB24WkPpXWK9y2gvF5HdA7Z6I/WvJ3IrDxfwWGXzlje+kLuJygOmeaczG +GQ0ZumA1WWX5mFrCxyTl5QjxWeXUFxYxGrIUpT+YLRmSoCyElGX/ljJi8gL7 +t3N8SVIxx00m2QRNS20T6bjoW0N8ddSsIdIFeQkpCWgnKOewCVmEDwmZBkSU +VThQYMgG3XELjIZ07a7RUuoGk+yEB2mTHb07blJN2GTvSeVO3h0rNUmkCBGO +REhqiGkxDpDGM5tTxqJUSAZu+ajxrzS5+PPHAJpCBhld+5DiwaIyp4d54A64 +TnmBweB4MeuFDB7nAYElPwZailBQcIAUeWDJJ+PIIBALAVRVZDOf5zkAS+aQ +vUL+yfKiJQorVNqQnLIR1A0y8VbKKmlrEOx7BVlk4Uq3K6ShOJVHrCzxahty +mOfr4Pe22YvOVrthmsyJ2vB9eG1Y8r1C3Vjytjh7yZDGURBEN4nymB9O38HK +ZQ0rdKEhvcehdKDa/9rlqNiwcI0cAWqeDShLWlRFQYGtHCoU2dpUnePIg9KP +o/VF0ruSBRtun4z5rildd+7rQO2yOCRDRarIuaNWQZ2DJgyfQ5ClTIcfqGIG +JcNAauxBk1Xso944Blqq9yM3jOj/24pWMKOfYEdP03dDmIOSyoN2wHOdZgAe +iiGegIkzUhz91k8wDAdQUoy0knHZeEE3LwO5/xvaSkr0Km3QENusbXkR1FKp +HxC5r9hA0n6r7KDqTI06EWtiharRlAW7PWg08s1vl2Cpih80sIguukNAwl+3 +WUcbDRuAsrwJfJDKGVTUHCxAZxb7HniWzkuCqoc4PdtxDvYOITrGCXfG3vSO +xiELzebzKE6R9aVoikuqKZhaNdk8G4GAyBhgsmkMOuVUSSONSXiG6+J70CHK +NEC1EpSK1FWqtbIErAlVTYHp2owJU19gWVplnAWBA5aCLR8Ku4gIKIiusWSm +ZVsDlV/AnGp+kWuCtnmZ/UlH4wBFd3fLrpNku83WJEt22MH5PPn0lzWLjHm7 +InMpaU1Jo7GkFAaAUBqBHQQ1pXgrLNz1dk5rjlW7KZINuanCPEDm6JSwg0X7 +VyZJSQ5m4JjQ8wnHBAfMAYzMd/0IpKBEBfgtqNqBm/6YLFJlzoJNYkHtWJDi +bIbpM5TdYGU8hWzHnrge9RPkmtRA0MSSCDotQ6scSseEPbpKDOWizLesXABA +7BmhWiM5qEbKZZarmnIp8mMUUOe0dQqIVNYpYE2C+00K+EZzAebm6meMAvkU +20EmVG8ANWuPaREJoaRF5Bg0UlNsNCpdwqOiq/MLfz5x6lKXKMLWYaSluIIM +tVtVItS+tUaCVDZtszqnasXFgzq7sOJLARAxNrK0Y5D9rNzJWLgbAAHAdVmh +VZ4XcsXIsCRWHLRxHiwK62uRJv7EFCmh1q/gTMCvYHcXMxJepFVG5Fb4wCRI +jvKTlCMVZOJxQSq4XJKeKUmggKlXGfNQMkQh+DG1oJIlgNQUnKakEN22nI8M +V+R7iqN6N2WWfgxHtcaixm3AuIK56G3QnGIzf1KzyUJtMAWlDxX1Jo8f13g/ +vAYY+F/ECfWv4nqVR66tMwl/r87/TQczUz5yWNoiyeip3qxG88aa5xQOYNIP +lkMaZ6KJ9QHSgq0LrCD8laTynb0YUKrl8ZifsiWebdxdqSjcDThkKc4ZHsDG +MXbgMZ+RXRnZbs+PwC1rYwMckBf1EUeAsXiOZ7gJhDN3KtwryoU/QUXqxfwG +Sh1KjW7QL0H+IhMZNgzZr6yNPUo/lOeJMR44JCk+GhYsVB3C8kFG0+IpVDWj +jIQn6znV3lTYXDeKyVRhGLG50ygRpaQJE+0WUZVGKdQzEgcScZNTzcYCOKjr +4cpZCvWdy+ga1iyD7Ax9snLAEMpgQ4BHkSaPdKAei2JZX+NgAkQGlDaK+Frl +iRgECzIkmcliNooCNlwf4kzgSJjKkxNXxHQAS+dVM8HDRBXkVKPGYi5I3ZAJ +qT8TRXuM2ILcalrqcOz1unmagx7mVp9cDtvs1TZ7vQ51WDhU6WS5+FTSffRP +M9C6CkGz2TrxsAU6et8EEw68VaXqhYR9xb/fLc3mbSja9sCM3onFRIR24/GZ +Sr+ZrVA0Va/g8ZlfT211wCn+mD3i4RX4H5D3GEpcVnr2a82adpKN/inctK81 +DTmuj0r7+V6+J7X3QVY5KSmRFHzD8jOeQM1+qcLyNiudcz4yUxNkYlhlzXUb +DagpEwM1sQFrv1VnqLvw1DbQNsGCGjgTJ7aml6TVpZMU86C7vXz23cGh3GYa +1rpcG9GVorosFDRSWguhTUqbZTzgbtu5WVBCmosHTI5q3XYdI3+QfqOKb2gf +yMwHdfpNf6ZGGSxurkJtiZeUkpT3+zMsY11rwx6+Ou+j6Cqb2+VtrG4gFIbL +3hXdaEgRDnuZRXhxIIL7EJCeiFkqisrhDL7rhO6ptGrf/O7vuxAjIfRDUX+F +QRTpDYXwnkyjJmvEA473GH6FBCmb2YopMuT/VfMIgv2l5srqpFeXcL7/Ekor +ZWFklIxldS3C4+qYteMrlOE7Kvkrw5ZLmzCN2fkWw5ImbnJGl335gg84PWUk +lYqkxm3qdYzZmn21UUDm1SqxQudZviC1lNkjgNHKJFe/Vef/u20Y3Vhn5zvv +908vz47eDnbOP57uX37aef9+//zydP9s//Rv8PHo/HDvdOcTW9/QHc7qza2q +oprN0mUgQ33zvqnmFrBBvy14tER++5YCWKG5DdZYog+DNoK2ax5JJDWMIfC8 +DauyUY+q26RUY+Q1vJQT3pyiZm05ZA/1DTYZunWpTycyRZ+FsmNMzkGudE8y +9t00WOBBDb+ii4QAO5sHYpZfe/MEpOoB5vyuvMxlLgBYQoGFawz/Q91FHWMe +QjULsPJG2gJKH7x56U+mVGuoukY2g/LV8BRGn44Hi1apXPP8xM2SRJ4WHgRR +DOzrY9XByuEeFdRIW15LNTXyGm1PvndLNbu2gqukUWmtqIRKvQKtUiwkFWK1 +PArzbknaNErMNWtuGmdMN6ScopKfcz/W91gQymjaqJO9h3s3PX2nU5brLikB +tdHLRi0v4T3UWUnpWgwdTpnaiUxqskoquCbvi5diMS7sePldibUmHf0qT0R5 +GLIXa3hY6XO/171oMnzX6/YvwJ0ph5XncW9F+iEbFdOVgdhYAoLTa5IaYFOA +T4QDCkAqTLUs1u8f5AXyry/1ZiLGWi9VpZ7+eH+xV4XDYk+OybbKutaBe+fm +HQK8/p5GPyFpu4Hk6VKdBaiao9N98fQgLG/bgmR9j9rArtCH+2l0JcLXzWKl +bymTqgN5mGY2UdD6AuvLFe/KVeW3hOkwQn/31Jq7XJoRBumaVlzz66mtDuTl +CPEn2fgi+eR7dxtuwP0Ze6QqkX82bUEL09z8z6gtppeoPto55aqk7KT5lRSs +aVt1EKOT9+yU41oVaX57QLaoUj6bNyEgjTNwvZ7gHvhgofRff1y9FIhCeQ1s +u6zh0sDIvpSyfx0LCn8jGdws7eCrED6866fWVHrzLdok3s6kzT6ZeS1twvT6 +ZN+m0aw8cbkbYQQF1Y74QdVKTm5T0vG9qpWawrkcEuWm5JqNupn1wnh4zfOT +vRPI+iLIlYQj0dARtTw7p4HX9TN1VVN8y2KbYd53JgJ529rO6fmSWwGZFcQN +uSykuUHmCXbM46s9H5JXhuWQtQ477JjSXS9lOOvAhk59qwkKkXmU+GjLkrzW +F2QcFmaqQXJXEEyclIhK7VtDtk2mMd4XjX5SIAEnnKvZI4EEQsm9RP+MQCKV +SJWFREarrCK5UpAqrN7kkGj3JEZbb/ApDqP7mMPo/iiHIcVKJFRHf7jD6JIi +dysOQ9bB0vZVxecod+FFIgn/JS3qVWytqQKSltIV1WuzfdFkQ7LMofxaBQ52 +ZUF0X1NjelmKvXlThQMZeC/w389OBrk1NqDiqO2B/NKtaYF02p3aFsjx/unu +4c7g/HL3ZHB+urN7Xmp+FMRYtI2llTbxoKCm21K/lOq27J4cDS739j+cnB0V +ixmrPdTXMJ497xSdjdyIVNb2u9nMXp59DklH4Y/zFKRxP0wlz6ihSDWA1rV1 +m33MioXWzFuGBSijBqzdPupBd1U92HxYDz7s/P14H15P3tVrgqyGS0UwKxXB +kBpHge8u5Kmw6ifhyTmGTlkhK3dlVMh6hxurVMmVw9D8i3qPVMgFXFEhr1pd +F0cCaqIsAFipJECH0izzSQIWoTOPw9Qn032Cqv96eOslwAcPOjdGPHWnjl7x +sYOgUlZSZOJGDt2sqP5dDeHF3pvMdLmSN/cRzjQ3NYfKVfuDW/56Zt0HWX+a +lAftH3KspJA//Uyp4vC+c5ma/32nelVxwYjMBl91dO6oJPSpmO85kaiu0fhW +hhgIq+cbT6MZxY+3rhL8xQq87LXqdJ2aa92JBd7dwWZpfOUgtieUjUCKlwX4 +nerS5YiVp2u3epl7Diqybuwn5aE9Mw81fDUpRe/756G0brNuAz8oDy1HKrkp +0vReXR5aZJG9xw7BekuBv7e5WRP5271ebeTf/w8M/G/3MQM8ODo9XsrNKqnZ +cqJVl5GomWYmtczrB7KtuibOE1Oze4/mzBoM8vyazGvptC6fJ2tkMzFTdwLx +a9VZjD/Msos3JT31xd1E/XbIHv12yDN2tDPYWYIYRCF+NRdVc8TpPiDblffm +gmiCn3ZcjByB8CbyR2q+9OU9M+Ftr415kIi1O2MdOmaj36a4oe9v0vdm5DU6 +uvmmrufJ33NhB8KjHxM5pq8hxfglW2vfy9RXqTBanYpEcNgys98cvzmQX59T +V/LmcYRXpXBPeE4j8A1JJmlZ/weR36loJUgAAA== -->