lsd0009

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

commit 78f571ddd833bb028b78ccac48ea0be773f61236
parent 619f168b3efaae4643cc251dd8d6af23407507a8
Author: Mikolai Gütschow <mikolai.guetschow@tu-dresden.de>
Date:   Wed, 22 Jan 2025 20:16:13 +0100

crypto primitives: cleanup for RSA-FDH

Diffstat:
Mdraft-guetschow-taler-protocol.md | 53++++++++++++++++++++++++++++++++++++++---------------
Mdraft-guetschow-taler-protocol.xml | 200+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
2 files changed, 156 insertions(+), 97 deletions(-)

diff --git a/draft-guetschow-taler-protocol.md b/draft-guetschow-taler-protocol.md @@ -30,6 +30,7 @@ author: email: mikolai.guetschow@tu-dresden.de normative: + RFC20: RFC2104: RFC5869: RFC6234: @@ -54,7 +55,12 @@ Use at your own risk! # Notation +- `"abc"` denotes the literal string `abc` encoded as ASCII [RFC20] - `a | b` denotes the concatenation of a with b +- `bits(x)` denotes the minimal number of bits necessary to represent the multiple precision integer x +- `bytes(x)` denotes the minimal number of bytes necessary to represent the multiple precision integer x +- `bigEndian(16, x)` denotes the 16 least significant bits of the integer x encoded in network byte order (big-endian) +- `a * b (mod N)` denotes the multiplication, `a ** b (mod N)` the exponentiation of a and b, modulo N # Cryptographic Primitives @@ -164,13 +170,13 @@ OKM = HKDF-Expand(PRK, info, L) with Hash = SHA-256 (HashLen = 32) ### HKDF-Mod -Based on the HKDF defined in {{hkdf}}, this function returns an OKM that is smaller than a given big number N. +Based on the HKDF defined in {{hkdf}}, this function returns an OKM that is smaller than a given multiple precision integer N. ~~~ HKDF-Mod(N, salt, IKM, info) -> OKM Inputs: - N big number; Nbit denotes the length of N in bits + N multiple precision integer salt optional salt value (a non-secret random value); if not provided, it is set to a string of 64 zeros. IKM input keying material @@ -181,14 +187,13 @@ Output: OKM output keying material (smaller than N) ~~~ -The final output `OKM` is determined deterministically based on a counter initialized at zero, -where `c` denotes the two least significant octets of the counter in network-byte order +The final output `OKM` is determined deterministically based on a counter initialized at zero. ~~~ counter = 0 do until OKM < N: - x = HKDF(salt, IKM, info | c, ceil(Nbits/8)) - OKM = Nbits least significant bits of x + x = HKDF(salt, IKM, info | bigEndian(16, counter), bytes(N)) + OKM = bigEndian(bits(N), x) counter += 1 ~~~ @@ -216,8 +221,9 @@ Output: `fdh` is calculated based on HKDF-Mod from {{hkdf-mod}} as follows: ~~~ -info = 0x5253412d46444120465470735721 ("RSA-FDA FTpsW!" encoded as UTF-8) -salt = length(pubkey.N) | length(pubkey.e) | pubkey.N | pubkey.e +info = "RSA-FDA FTpsW!" +salt = bigEndian(16, bytes(pubkey.N)) | bigEndian(16, bytes(pubkey.e)) + | pubkey.N | pubkey.e fdh = HKDF-Mod(pubkey.N, salt, msg, info) ~~~ @@ -238,8 +244,8 @@ Output: `out` is calculated based on HKDF-Mod from {{hkdf-mod}} as follows: ~~~ -info = 0x426c696e64696e67204b4446 ("Blinding KDF" encoded as UTF-8) -salt = 0x426c696e64696e67204b444620657874726163746f7220484d4143206b6579 ("Blinding KDF extractor HMAC key" encoded as UTF-8) +info = "Blinding KDF" +salt = "Blinding KDF extractor HMAC key" fdh = HKDF-Mod(pubkey.N, salt, bks, info) ~~~ @@ -262,12 +268,29 @@ Output: ~~~ data = RSA-FDH(msg, pubkey) r = RSA-FDH-Derive(bks, pubkey) -r_e = r ** pubkey.e (mod N) -out = r_e * data (mod N) +r_e = r ** pubkey.e (mod pubkey.N) +out = r_e * data (mod pubkey.N) ~~~ #### Signing +~~~ +RSA-FDH-Sign(data, privkey) -> sig + +Inputs: + data data to be signed, an integer smaller than privkey.N + privkey RSA private key consisting of modulus N and private exponent d + +Output: + sig signature on data by privkey +~~~ + +`sig` is calculated as follows: + +~~~ +sig = data ** privkey.d (mod privkey.N) +~~~ + #### Unblinding ~~~ @@ -286,8 +309,8 @@ Output: ~~~ r = RSA-FDH-Derive(bks, pubkey) -r_inv = inverse of r (mod N) -out = sig * r_inv (mod N) +r_inv = inverse of r (mod pubkey.N) +out = sig * r_inv (mod pubkey.N) ~~~ #### Verifying @@ -308,7 +331,7 @@ Output: ~~~ data = RSA-FDH(msg, pubkey) -exp = sig ** pubkey.e (mod N) +exp = sig ** pubkey.e (mod pubkey.N) out = (data == exp) ~~~ diff --git a/draft-guetschow-taler-protocol.xml b/draft-guetschow-taler-protocol.xml @@ -29,7 +29,7 @@ </address> </author> - <date year="2024" month="July" day="08"/> + <date year="2025" month="January" day="22"/> <workgroup>independent</workgroup> @@ -38,7 +38,7 @@ <abstract> -<?line 42?> +<?line 43?> <t>[ TBW ]</t> @@ -53,7 +53,7 @@ <middle> -<?line 46?> +<?line 47?> <section anchor="introduction"><name>Introduction</name> @@ -66,7 +66,12 @@ Use at your own risk!</t> <section anchor="notation"><name>Notation</name> <t><list style="symbols"> + <t><spanx style="verb">"abc"</spanx> denotes the literal string <spanx style="verb">abc</spanx> encoded as ASCII <xref target="RFC20"></xref></t> <t><spanx style="verb">a | b</spanx> denotes the concatenation of a with b</t> + <t><spanx style="verb">bits(x)</spanx> denotes the minimal number of bits necessary to represent the multiple precision integer x</t> + <t><spanx style="verb">bytes(x)</spanx> denotes the minimal number of bytes necessary to represent the multiple precision integer x</t> + <t><spanx style="verb">bigEndian(16, x)</spanx> denotes the 16 least significant bits of the integer x encoded in network byte order (big-endian)</t> + <t><spanx style="verb">a * b (mod N)</spanx> denotes the multiplication, <spanx style="verb">a ** b (mod N)</spanx> the exponentiation of a and b, modulo N</t> </list></t> </section> @@ -183,13 +188,13 @@ OKM = HKDF-Expand(PRK, info, L) with Hash = SHA-256 (HashLen = 32) </section> <section anchor="hkdf-mod"><name>HKDF-Mod</name> -<t>Based on the HKDF defined in <xref target="hkdf"/>, this function returns an OKM that is smaller than a given big number N.</t> +<t>Based on the HKDF defined in <xref target="hkdf"/>, this function returns an OKM that is smaller than a given multiple precision integer N.</t> <figure><artwork><![CDATA[ HKDF-Mod(N, salt, IKM, info) -> OKM Inputs: - N big number; Nbit denotes the length of N in bits + N multiple precision integer salt optional salt value (a non-secret random value); if not provided, it is set to a string of 64 zeros. IKM input keying material @@ -200,14 +205,13 @@ Output: OKM output keying material (smaller than N) ]]></artwork></figure> -<t>The final output <spanx style="verb">OKM</spanx> is determined deterministically based on a counter initialized at zero, -where <spanx style="verb">c</spanx> denotes the two least significant octets of the counter in network-byte order</t> +<t>The final output <spanx style="verb">OKM</spanx> is determined deterministically based on a counter initialized at zero.</t> <figure><artwork><![CDATA[ counter = 0 do until OKM < N: - x = HKDF(salt, IKM, info | c, ceil(Nbits/8)) - OKM = Nbits least significant bits of x + x = HKDF(salt, IKM, info | bigEndian(16, counter), bytes(N)) + OKM = bigEndian(bits(N), x) counter += 1 ]]></artwork></figure> @@ -239,8 +243,9 @@ Output: <t><spanx style="verb">fdh</spanx> is calculated based on HKDF-Mod from <xref target="hkdf-mod"/> as follows:</t> <figure><artwork><![CDATA[ -info = 0x5253412d46444120465470735721 ("RSA-FDA FTpsW!" encoded as UTF-8) -salt = length(pubkey.N) | length(pubkey.e) | pubkey.N | pubkey.e +info = "RSA-FDA FTpsW!" +salt = bigEndian(16, bytes(pubkey.N)) | bigEndian(16, bytes(pubkey.e)) + | pubkey.N | pubkey.e fdh = HKDF-Mod(pubkey.N, salt, msg, info) ]]></artwork></figure> @@ -261,8 +266,8 @@ Output: <t><spanx style="verb">out</spanx> is calculated based on HKDF-Mod from <xref target="hkdf-mod"/> as follows:</t> <figure><artwork><![CDATA[ -info = 0x426c696e64696e67204b4446 ("Blinding KDF" encoded as UTF-8) -salt = 0x426c696e64696e67204b444620657874726163746f7220484d4143206b6579 ("Blinding KDF extractor HMAC key" encoded as UTF-8) +info = "Blinding KDF" +salt = "Blinding KDF extractor HMAC key" fdh = HKDF-Mod(pubkey.N, salt, bks, info) ]]></artwork></figure> @@ -286,13 +291,30 @@ Output: <figure><artwork><![CDATA[ data = RSA-FDH(msg, pubkey) r = RSA-FDH-Derive(bks, pubkey) -r_e = r ** pubkey.e (mod N) -out = r_e * data (mod N) +r_e = r ** pubkey.e (mod pubkey.N) +out = r_e * data (mod pubkey.N) ]]></artwork></figure> </section> <section anchor="signing"><name>Signing</name> +<figure><artwork><![CDATA[ +RSA-FDH-Sign(data, privkey) -> sig + +Inputs: + data data to be signed, an integer smaller than privkey.N + privkey RSA private key consisting of modulus N and private exponent d + +Output: + sig signature on data by privkey +]]></artwork></figure> + +<t><spanx style="verb">sig</spanx> is calculated as follows:</t> + +<figure><artwork><![CDATA[ +sig = data ** privkey.d (mod privkey.N) +]]></artwork></figure> + </section> <section anchor="unblinding"><name>Unblinding</name> @@ -312,8 +334,8 @@ Output: <figure><artwork><![CDATA[ r = RSA-FDH-Derive(bks, pubkey) -r_inv = inverse of r (mod N) -out = sig * r_inv (mod N) +r_inv = inverse of r (mod pubkey.N) +out = sig * r_inv (mod pubkey.N) ]]></artwork></figure> </section> @@ -335,7 +357,7 @@ Output: <figure><artwork><![CDATA[ data = RSA-FDH(msg, pubkey) -exp = sig ** pubkey.e (mod N) +exp = sig ** pubkey.e (mod pubkey.N) out = (data == exp) ]]></artwork></figure> @@ -373,6 +395,17 @@ out = (data == exp) +<reference anchor="RFC20"> + <front> + <title>ASCII format for network interchange</title> + <author fullname="V.G. Cerf" initials="V.G." surname="Cerf"/> + <date month="October" year="1969"/> + </front> + <seriesInfo name="STD" value="80"/> + <seriesInfo name="RFC" value="20"/> + <seriesInfo name="DOI" value="10.17487/RFC0020"/> +</reference> + <reference anchor="RFC2104"> <front> <title>HMAC: Keyed-Hashing for Message Authentication</title> @@ -424,21 +457,21 @@ out = (data == exp) </author> <date year="2010"/> </front> - <seriesInfo name="Advances in Cryptology – CRYPTO 2010" value="pp. 631-648"/> + <seriesInfo name="Lecture Notes in Computer Science" value="pp. 631-648"/> <seriesInfo name="DOI" value="10.1007/978-3-642-14623-7_34"/> <seriesInfo name="ISBN" value="[&quot;9783642146220&quot;, &quot;9783642146237&quot;]"/> <refcontent>Springer Berlin Heidelberg</refcontent></reference> <reference anchor="SHS"> <front> - <title>Secure Hash Standard</title> - <author fullname="Quynh H. Dang" initials="Q." surname="Dang"> + <title>Secure hash standard</title> + <author> <organization/> </author> - <date month="July" year="2015"/> + <date year="2015"/> </front> <seriesInfo name="DOI" value="10.6028/nist.fips.180-4"/> -<refcontent>National Institute of Standards and Technology</refcontent></reference> +<refcontent>National Institute of Standards and Technology (U.S.)</refcontent></reference> @@ -447,7 +480,7 @@ out = (data == exp) -<?line 329?> +<?line 352?> <section anchor="change-log"><name>Change log</name> @@ -465,64 +498,67 @@ Education and Research (BMBF) within the project Concrete Contracts.</t> </back> <!-- ##markdown-source: -H4sIAAAAAAAAA81ZW1fbSBJ+16+oIS92FhnbGAOeYc+SEAIngeQEsnnIsENb -atsaZMmn1QKcLPll+7Z/bL+qbvkW5zJnZneHw7Gl7uqu6qqvbu0wDIPbHm0H -gU1sqnu0cTnS9Pz8LV2qVBt6bXKbR3m6EcR5lKkxKGKjBjYcltoW0Si/Cy0T -hhNPGETK6mFupj1KskEeBMnE9MiasrDtZnO/2Q7ucnMzNHk5YYpYTzQ+MhsU -1mg1Xh670VNQx72AKCThI0+RmU5sPjRqMprKgI5UMZKniZqOsbIIgkd0q7NS -9/BAZPQk79HI2knR29oaJrYxzMpM20ZuhltpETchWgPDW0Kd4giFndODYA39 -VhCo0o5yA/FCMCdy+jlLbvJUJfT83/9yGpI5LOzR5dsjOjK6wOHobZbcalMk -dkr5gC51NMryNB9OhVr1+0bf8oKKXoZZRxqCneh0PMpT+wEDDWo1ZTLCVr0l -8iiPIc9R2Gw1u/t+pMws2+a5NmOVOWZ6rJK0R2Mnd2Nm2b/ZMozddo1YB0GW -Y42F1GyPN8dP261mxz/u7HX3/WO3vS2jJy+OjsH81Wmj1cR/c3drf3cv3A67 -nXbY6oAq3P1luwPCi5OLGV232d7bOj+9uGwcn76+aLT2mmEHEAKQZqyDIAxD -KAhHV5ENgp/f0+WTd/TzlZsYJ3Gcajb/KU6ax2VkkzxbInui75TRZEfK4iMp -CNAuGTWE58ImaUqM0TDJGNVDaKAglcU0VlMoMLMqyUgbk5uiEbwtNGGbaV4a -yu8yMklx8wNzP8+tcpxDulb0T+pfExSZA1lgqnkj9pRMiBgBiu4SO6I+L346 -B3gSwQeTccKHZ1SvTp4A+XRcZnJMIXgEjR6G7Z0ufXxUjBQeHoLg06dPgR+u -jYthncK/0oidJjjNJqXtCRAwwV9wQQzRGOdWQ82ypTobQraX9BO1/9FtUR5Z -zT72qrSzxbybbOKXxckQTsSrB8m9jqs9WN6XgP8BbberfVi44Jo3uGYTsHpy -2ZlXV4dRBU0QkC60Oyl1Gq1N2uGPLn+wgbqNNq9471F41ZirY6fVdurAw4I6 -8PZ71dFq7/wx+uh2vlsffJrP9dFmfbRZH9uVPjpf0YdotYbILECMq+H6TE+h -zP1JtbWCnsu5jrziotzAcyd5FkODuShxkBjsOVvJu6/XbawHSQaucPSPHz1o -HnpOEVaPJ+C/qI9ApD8gnnrf7G23rpxQiAGLYSaHvZy7x8lggNhPA5OPq522 -PMgXeL9HaLziB5YyyRADVOo0davSUsOcHA/OvMoOkYwQxJLI8XiK4O/jwcnZ -4VNYdTRWlTV5JGRl1pBiNyH4vRWrQhWwy4Q3cHY58XaJloKOyDDwQcfFLa/E -ZTstAQScBCCFjoy28jpHh+KlCtapVjIlizWHVKyskkBp+olVZuqXLuMIYpD/ -9jZdkcj5FqbFtSKVRmUq8F9Rvc9vV17JLyDtkTbJrVPuSsDlXMcKvokHDw6L -zA47fWEZ1XhFncrCcXO1VsJpBq+FVTDjLDG89/n1KhBF27vc44cz1pIlAKjc -CFie3Ut2lCjw7H7CX4XVEz5kUQ7ZtfwxWY6rXnDN36Ffdc1yFXQtIAE6Ac7r -TbobJammipC3XKUDgK+pFg0acBrB2kO94eGGRbVCpXaTTl+cbUpluEkvBXKv -Xpx5mBTOhEwnJhQYAvEyIIinmqIsz0IPIQMh4EAyVf9RFs//kgFILSGD3yax -jsHU5XesQzhQXEsl2ZAVjMD7QZsc2ZzXQcCFOAaUMhWKD1hRpYGbGeRLAnJR -wFBlNavJJK1csJjoKBnAX2YFTJ6tSFmLYPI+aggRIfRgdbLVhfalp/RTkNdj -e0U0NqePiCssfjqg9s7OY8S9A9prdZv1ZZd55Q/8pX1rYPnSb13/LNry6s8c -aZCnaX5X+Ij5+s0LcF5E2BwLdRc9TlwAreJvbTEr1gPmMduAkVfDlgsoWt1C -EttiqvBiV64anuUxykDF3pe76CoevBT2xZkfNl3onsU6wK40mTgqSyXBnVE1 -Vim7MN4z2HKIYg1mTYaUleM+xs8XHIG51843acUf1jvDeWXF+WY/0jkC4FIx -OcfGOQuP6WLJlf7fvvQdzvQ/8Kbfhvolk54vAB8YwXxVbGAfySSxxsKxwKd6 -TApOxmk6pX4FNeU6MA72Lp0nH9hjrMi7GdyNENXpOlpuFTjiu+RYJMOMVYD8 -sFLDzLcltKnSvvSnqD7QPaNpFuErkgNqopknvCSpqOEnOnc6ufdethqq0b1E -mxTpJK0x9IqtvXp9psQDgWOxRkIZhnz389YT7P9yQK3KHdElZeGTFC0/XWCh -gm9VNcuzGDGrtS9U6yneXByGx0cnyLumUOEgHj3IOOrbcjLJjWVzLuRpZumX -cNG2SZOyD6OL22HxsttVxawvR2XM0RPz5WfgUmoYwLVgSzv8j9FxpmUBP2T4 -ejJ9jzKUs7VexiDYCpdBmaYhvI8bS0novBEkyG+hL8e2ce4rF6xZrVxm6Kqi -i6srXQgLIdHDw+dBWewKKNzvtHe2O6123Ol2Ovhudro7nd3m7vbObrtFtQ2n -skM6vpwU737YIJ3xrYJE+beXx+FePZCQcuBjUK0Stw7QLA9pHqqm5486YDUc -zISf7VBFSDGWRMi5DwIEZSo6dwrxfi/1FBf73DuooeJaCj4HT06iJIdZvO2w -f9CfEtSbDMTtfZGuCd2+XP7ArOMxV+rwQ7iysiisasMorrNtHE828HUlrNik -1ViCWSiVn671b4oltEmRvYg2ELgYzzhncRhXPi4vNlBI34sp/o8HZFU9rwMk -C7kOkGtK6d8HyE67G3X3u7rbkc9dQLIPaCKlbzypFIR9vwLFL+/RbnZ3dvd2 -O7vtbqu7vdvpDnbbmNvrxJ1WZxuzfczvr3CCuqRqAQSkk8Lp1zH/BowFBQsw -llBVsVnGjYy6IPVN7KyLVH86PFVdvQgEpXGb4t3w2yCq4rzHUBXs10BIOsQD -WhflAzOfWOeXgflFg8LQ48ezwEQ1HJZTPx8Dc6B47LrQamJuSE5OYkd5e5v1 -1xrWj9eQJb/DtKCam1Eyq6S/P6eJy6wy7lzQr/Xay5b7tnWS7BY0+NSmkGsl -s2Ie1tZjcoSf2+fvVahfNogbdkgRo/xWV6tsNDs0i+a1LNESi/7Lmrem1Jtc -orMsfIfARXzyXXb4g30MklaG+KIX1dwGB3yshbbsaaqQvMML/hXEGL4D5zTv -7kXchffspygpCd+h5YuNulP8ypegpeFfUp6yOmN/zVb4W/8jufV/RKeH54ef -UaAC5Ys0/umgr6IbuX1HxY9glebsznQY3WT5Xarjofth6WPPtWE6PtgYqLTQ -Gw9LfC65X+QCnO7ktkVqUddUTpRBVTyVQsP9AEPHmmVJ6UzaBcNXYsGzuPQt -D6PgjS60MtGIak/Onhy7ZtdfCaIf+1VHls/Ejq/5QTIV2q//ABmMIxbZGwAA +H4sIAAAAAAAAA81ZUXfTyhF+16+YG15sajm2kxhwcU9DQkgOxHBIKA+56fVa +WsvbyFqf1SqJoeGX9a1/rDOzK9tyDOEeaHs5nFhazc7Mznzz7awUhmFw3YOd +ILDKprIHW+cTCa8GH+BcpNLAO6OtjnS6FcQ6ysQUJWIjxjZMCmnzaKJvQkuC +4cwLBpGwMtFm3gOVjXUQqJnpgTVFbjut1rNWJ7jR5ioxupiRRCxnEv9kNsit +kWJaHbuSc5SOewFACGyHryIzn1mdGDGbzHlARiKf8NVMzKc4Mw+CR3Ats0L2 +8ALAyJnuwcTaWd7b3k6UbSZZkUnb1CbZTvO4ha41cXibpVNcQm6X8iiwQX47 +CERhJ9qgeyEaB3DxOVVXOhUKXv37Xy5C/Awn9uD8wyEcGpnj4uBDpq6lyZWd +gx7DuYwmmU51MmdpMRoZeU0TSnkephhJdOxYptOJTu0nHGhCu8UPI1TVq4hH +OkZ/DsNWu9V95keKzFJuXkkzFZkzJqdCpT2YOr+bi8z+1RZh7NQ1YxkEmcY5 +Fr2mfLw/Oui0yot2a9df7j3tPvOX3c4Ojx6/PjxCL96eNNst/N96sv3sydNw +J+zudsL2LkqFT37b2UXBs+OzhVy31Xm6PTg5O28enbw7a7aftsJdxBIiauFD +EIRhiJHCGIjIBsGvF3D+4iP8eukeTFUcp5JwcIJL1nERWaWzitgLeSOMBDsR +Fv+oHBDjBcEH8Dq3Kk2BwBqqjOCdYChyEFkMUzHHSGZWqAykMdrkzeBDLgHV +zHVhQN9kYFR+9QtZH2grnOUQhltiFG0NASOqEWJoVEKqrDQipdyqLIEhSgxB +ZpS7GEQO+2cHJydwwfG+JB0C/gmjqg50hsouY0MEJwE3yk5gRPIjZfPabb06 +Y6oyNUWrWTEdYZnjFBKDTEa4SGHmYDUVDS6ZwsEzitSqWSoBByOVkyGVYanj +7Fs2M0fd32OH5H7IkEpeZrESWa3dbcC6wXYXUilyC7lKMjVWkUC1vDa0TQIL +XYsgYxaxtCnT7ByWaozPa2gnlGyo7sL+GEZQm+oYBuuLdB6jLYp/g2UrwiQk +b2c6wzWqlSQRmEYNQLEi1TAguBwsuU1FSL9qqgjuRGjrD4+R9OCoyBjYLPAI +a2g/7Ox14fOjfCLw4i4Ivnz5Evjh2jRP6hD+BSbEl8FJNitsjzkAH9APxgKH +YEq5SSQ5mcosQSS9gefQ+Xu3DTqykuj1bWEXk0kbK/HTYpUgf9LssbrFAHsd +5O8bZL4+7HRKPeRcMCQFQyo6CpRmzTS7XAxWwQwzcibdSmG32W7AHv3p0h+K +YrfZoRkXnncum8tw7LU7Lhx4sRIOvPvRcLQ7ez8nHt3d744HreZ+PDoUjw7F +Y6eMx+434sFRreGmzLQRl8P1RZxCfvYHjdYaes6XMfKBi7RBPsFyi3OiFwri +WBnUuZhZksH92MZyrDJHCp8/e9Dc9VwgrJzO0P5qPAL2vg/06KLV22lfOqeQ +9Vc3Fo35cnUfq/EYt30YGz0tNW17kK/YvsDN8JIuHGUhByCJsq1rkRYS00l8 +cOpDto99CFGLIyA4QFrzfHB8un+AWZ1MRZlNGgkpmDXsrhro+K3lrGIoMC8z +UuDycuzzElVIh30Ye9Jxu4wPYjVPFYCgJQZILiMjLd8u0SGsZ+xyJkmSW0tI +xcIKZkyDTE6bhptaxRG6Af7X53TNI1db+JhLKxJpVKQM/7XQ+47m0gf5NXp7 +KI26dsFdI1zqbijAV/H4zmGRzKGmr0yDGs2oQ5E7a67NVtRY4G1uxeoOceE7 +qsuAA417lMcP7ZWVTCCgtGGwvLzlfohZ4OXtjH5yK2e0yLxIqLT8MsmPy14w +pN/QzxqSXzkMGSSITgTnsAE3E4XbcSlIKtflEMBDqEXjJhYNY+2u3vRww0m1 +XKS2ASevTxt8KGjAG4bc29enHia5SyHJcQoZhtQU0QAjHmoCMp2FHkIGncAC +4kf1P/Pk5T81RlGL/YO+VrjBo1HX0eE8pANRtloYYCTeT9Jo7N9oHjq4wmOI +UpLCdhOzKNLAPRnrioPUBhJUKcxitugBIJ9h74LdByxaVp2teVnD1gRG2DWy +C6EHq/OtzrJvvKR/hP56bK+5Run0jLhm4nkfOnt7j5H3+vC03W3VqyXz1i/4 +a3praPKNV12/x7Y0+14hjXWa6pvcM+a796/R8irCllioO/Y4dgRa8m9tdVes +B2RjoYCQV0OVKyhaV8Eb2+pW4d0uSzU81TE2/oKqTzt25Qqu0D4X813DUfeC +6xB2hcm4UMkrJndCFba3VMJ4n2EuE2zWsm/1sIOVwiBvaoMGrNXH5uIYlFn9 +uvJKEf2/q+g7yuh/UEe/D++VZA5WII/owOdlm4F6eA+JJU6cMnDKS5XTNpym +cxiVIBPu2E007zZy9YlqxbK/Hg2lRB9aQawBb1TK7j6HgfP91tfBOpnSabBy +IvKq6g131KoN6vXF4vsrsnwuHNTpCLV8N4Au/KkP7bJo8PSahS9ShVk5w/OU +wAooO4uXMTJL+xlLbZZ4f7YfHh0e4+5ochGO48kdj2MXWsxm2lgK/cpuSib9 +FGqtGjArRpggLgacXC2GsuX0TSOPOXkgu3SNGOJOA6GVU1YcVvmsVeRYTQQ1 +L1aezUBW8YJm2cq4SNMQK4UO/LztkiL0QF9jvJzZ5sD3Fzhnvb9YIKGsedf9 +OaIJ0aO7u/vUybntw5YLyT4cnc/yj79sBVzM/bWcu0yXntTr90BREZAeEShV +TlleyoBW3V/4utBa0hTnhmlqWR6YcyIlen/B6/clyU0OdeDU0ItEUIOD5YBF +piKlMQs+Vag/GM0Bo6nGXJG+c5aQGMkv4zCL0ym1z3jsxioTFrudWhLFdUqF +s0n5HJbOcgrazQqqQm7HZG10lVfAxZ3vKrhQgMMzIliTOwQjT5mrpxrcU1f3 +3Z+Pv7Kl3YQ/cnIT/jb0tz+EvxdlEHDuAn2VUXSft3ZMCR830Juth0DEOVgB +EfNCqbSaNR51jPBg5jbRwh8um+VBlx3CxFDn7ovg4RSWpOozWDLrhgTyoakP +myg1MMsHm6oiML9JlDD0GqukBfc2a0ExAa0GRVDwsTufrT1fppX2hXtZpcEa +zUOjaL/MaK6SakZZdfmLVIKsQm/2qB8Ry6aqsm97fVgRnEd35/LIxzH5cCK9 +3CKTcTWT6AH4X7fjUWbYQ2Qxb9DnEkU2HDermSJ1fTefAu7dj31Ey9WshvRD +NtpYK368hhq/o1rKZfCc5WL+mFVTZGW9LB391om+GuKHAa+ya5TBv9Lk/PLK +bEY8Be0xOPmvQv5v5U5WTY8bdqXIKfq9XLYBeOMy5rwZ4KT/ch6sKWSDDgfk +C723oOOD+q6s/GQSQ0/LfDxEUzWnp0+rWzkRHqQCW5TwjL69GUOv36mZca9k +3Lv2xQdQ7nM/4mkzNuJG0C29fy0Mfb87oKjG/g1f7j8xHfInpkdwsj/YvyeB +bTW9w6PvVCMRXfGLf6Qu3BRSndDdfnSV6ZtUxon7nPm5576gyLi/NRZpLrfu +KnbO6ajKnzBu+EUPN9juPDsTxhIvUTvlPvvBkYz5m9Mpn1cMvY0LXsaFP3MR +GN7LXAoTTaD24vTFkTtn+7eReCD8h4wsrYnYQNIF7/94/vsPBoP99U8eAAA= -->