From 26debf502e7997c8dba4543f4aef855728b6af1a Mon Sep 17 00:00:00 2001 From: Sergey Tolmachev Date: Fri, 4 Oct 2019 17:47:36 +0300 Subject: [PATCH 1/3] vxeddsa VRF was ported to java --- .../BaseJavaCurve25519Provider.java | 18 +- .../whispersystems/curve25519/Curve25519.java | 2 + .../curve25519/java/elligator.java | 74 ++++ .../curve25519/java/fe_isequal.java | 15 + .../curve25519/java/fe_isreduced.java | 14 + .../curve25519/java/fe_mont_rhs.java | 18 + .../curve25519/java/fe_montx_to_edy.java | 20 + .../curve25519/java/fe_sqrt.java | 34 ++ .../curve25519/java/ge_isneutral.java | 17 + .../curve25519/java/ge_montx_to_p3.java | 39 ++ .../curve25519/java/ge_neg.java | 11 + .../curve25519/java/ge_p3_add.java | 14 + .../curve25519/java/ge_scalarmult.java | 131 ++++++ .../java/ge_scalarmult_cofactor.java | 22 + .../curve25519/java/gen_labelset.java | 86 ++++ .../whispersystems/curve25519/java/gen_x.java | 79 ++++ .../curve25519/java/point_isreduced.java | 13 + .../curve25519/java/sc_cmov.java | 22 + .../curve25519/java/sc_isreduced.java | 13 + .../curve25519/java/sc_neg.java | 15 + .../curve25519/java/veddsa.java | 401 ++++++++++++++++++ gradle/wrapper/gradle-wrapper.jar | Bin 51010 -> 55616 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 94 ++-- gradlew.bat | 30 +- java/build.gradle | 24 +- .../JavaCurve25519ProviderBenchmark.java | 88 ++++ .../curve25519/Curve25519ProviderTest.java | 139 ++++++ 28 files changed, 1386 insertions(+), 50 deletions(-) create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/elligator.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/fe_isequal.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/fe_isreduced.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/fe_mont_rhs.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/fe_montx_to_edy.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/fe_sqrt.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/ge_isneutral.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/ge_montx_to_p3.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/ge_neg.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/ge_p3_add.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/ge_scalarmult.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/ge_scalarmult_cofactor.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/gen_labelset.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/gen_x.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/point_isreduced.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/sc_cmov.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/sc_isreduced.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/sc_neg.java create mode 100644 common/src/main/java/org/whispersystems/curve25519/java/veddsa.java create mode 100644 java/src/jmh/java/org/whispersystems/curve25519/JavaCurve25519ProviderBenchmark.java diff --git a/common/src/main/java/org/whispersystems/curve25519/BaseJavaCurve25519Provider.java b/common/src/main/java/org/whispersystems/curve25519/BaseJavaCurve25519Provider.java index 18e7941..139accc 100644 --- a/common/src/main/java/org/whispersystems/curve25519/BaseJavaCurve25519Provider.java +++ b/common/src/main/java/org/whispersystems/curve25519/BaseJavaCurve25519Provider.java @@ -9,6 +9,7 @@ import org.whispersystems.curve25519.java.Sha512; import org.whispersystems.curve25519.java.curve_sigs; import org.whispersystems.curve25519.java.scalarmult; +import org.whispersystems.curve25519.java.gen_x; abstract class BaseJavaCurve25519Provider implements Curve25519Provider { @@ -74,13 +75,26 @@ public boolean verifySignature(byte[] publicKey, byte[] message, byte[] signatur } public byte[] calculateVrfSignature(byte[] random, byte[] privateKey, byte[] message) { - throw new AssertionError("NYI"); + byte[] result = new byte[96]; + byte[] random32 = new byte[32]; + if (random.length >= 32) { + System.arraycopy(random, 0, random32, 0, 32); + } else throw new IllegalArgumentException("too small random"); + if (gen_x.generalized_xveddsa_25519_sign(sha512provider, result, privateKey, message, random32)) { + return result; + } else { + throw new IllegalArgumentException(); + } } public byte[] verifyVrfSignature(byte[] publicKey, byte[] message, byte[] signature) throws VrfSignatureVerificationFailedException { - throw new AssertionError("NYI"); + byte[] result = new byte[32]; + if (gen_x.generalized_xveddsa_25519_verify(sha512provider, result, signature, publicKey, message) != 0) { + throw new VrfSignatureVerificationFailedException(); + } + return result; } public byte[] getRandom(int length) { diff --git a/common/src/main/java/org/whispersystems/curve25519/Curve25519.java b/common/src/main/java/org/whispersystems/curve25519/Curve25519.java index 78f7b25..ebbb581 100644 --- a/common/src/main/java/org/whispersystems/curve25519/Curve25519.java +++ b/common/src/main/java/org/whispersystems/curve25519/Curve25519.java @@ -140,6 +140,8 @@ public byte[] calculateVrfSignature(byte[] privateKey, byte[] message) { * @param message The message that was signed. * @param signature The unique signature to verify. * + * @throws VrfSignatureVerificationFailedException when verification is failed + * * @return The vrf for this signature. */ public byte[] verifyVrfSignature(byte[] publicKey, byte[] message, byte[] signature) diff --git a/common/src/main/java/org/whispersystems/curve25519/java/elligator.java b/common/src/main/java/org/whispersystems/curve25519/java/elligator.java new file mode 100644 index 0000000..2ae2770 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/elligator.java @@ -0,0 +1,74 @@ +package org.whispersystems.curve25519.java; + +public class elligator { + public static int legendre_is_nonsquare(int[] in) + { + int[] temp = new int[10]; + byte[] bytes = new byte[32]; + fe_pow22523.fe_pow22523(temp, in); /* temp = in^((q-5)/8) */ + fe_sq.fe_sq(temp, temp); /* in^((q-5)/4) */ + fe_sq.fe_sq(temp, temp); /* in^((q-5)/2) */ + fe_mul.fe_mul(temp, temp, in); /* in^((q-3)/2) */ + fe_mul.fe_mul(temp, temp, in); /* in^((q-1)/2) */ + + /* temp is now the Legendre symbol: + * 1 = square + * 0 = input is zero + * -1 = nonsquare + */ + fe_tobytes.fe_tobytes(bytes, temp); + return 1 & bytes[31]; + } + + public static void elligator(int[] u, int[] r) + { + /* r = input + * gen_x = -A/(1+2r^2) # 2 is nonsquare + * e = (gen_x^3 + Ax^2 + gen_x)^((q-1)/2) # legendre symbol + * if e == 1 (square) or e == 0 (because gen_x == 0 and 2r^2 + 1 == 0) + * u = gen_x + * if e == -1 (nonsquare) + * u = -gen_x - A + */ + int[] A = new int[10], one = new int[10], twor2 = new int[10], twor2plus1 = new int[10], twor2plus1inv = new int[10]; + int[] x = new int[10], e = new int[10], Atemp = new int[10], uneg = new int[10]; + int nonsquare; + + fe_1.fe_1(one); + fe_0.fe_0(A); + A[0] = 486662; /* A = 486662 */ + + fe_sq2.fe_sq2(twor2, r); /* 2r^2 */ + fe_add.fe_add(twor2plus1, twor2, one); /* 1+2r^2 */ + fe_invert.fe_invert(twor2plus1inv, twor2plus1); /* 1/(1+2r^2) */ + fe_mul.fe_mul(x, twor2plus1inv, A); /* A/(1+2r^2) */ + fe_neg.fe_neg(x, x); /* gen_x = -A/(1+2r^2) */ + + fe_mont_rhs.fe_mont_rhs(e, x); /* e = gen_x^3 + Ax^2 + gen_x */ + nonsquare = legendre_is_nonsquare(e); + + fe_0.fe_0(Atemp); + fe_cmov.fe_cmov(Atemp, A, nonsquare); /* 0, or A if nonsquare */ + fe_add.fe_add(u, x, Atemp); /* gen_x, or gen_x+A if nonsquare */ + fe_neg.fe_neg(uneg, u); /* -gen_x, or -gen_x-A if nonsquare */ + fe_cmov.fe_cmov(u, uneg, nonsquare); /* gen_x, or -gen_x-A if nonsquare */ + } + + public static void hash_to_point(Sha512 sha512provider, ge_p3 p, byte[] in) + { + byte[] hash = new byte[64]; + int[] h = new int[10], u = new int[10]; + ge_p3 p3 = new ge_p3(); + + sha512provider.calculateDigest(hash, in, in.length); + + /* take the high bit as Edwards sign bit */ + byte sign_bit = (byte)((hash[31] & 0x80) >> 7); + hash[31] &= 0x7F; + fe_frombytes.fe_frombytes(h, hash); + elligator(u, h); + + ge_montx_to_p3.ge_montx_to_p3(p3, u, sign_bit); + ge_scalarmult_cofactor.ge_scalarmult_cofactor(p, p3); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/fe_isequal.java b/common/src/main/java/org/whispersystems/curve25519/java/fe_isequal.java new file mode 100644 index 0000000..d360900 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/fe_isequal.java @@ -0,0 +1,15 @@ +package org.whispersystems.curve25519.java; + +public class fe_isequal { + /* + return 1 if f == g + return 0 if f != g + */ + public static int fe_isequal(int[] f, int[] g) + { + int[] h = new int[10]; + fe_sub.fe_sub(h, f, g); + return 1 ^ (1 & (fe_isnonzero.fe_isnonzero(h) >> 8)); + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/fe_isreduced.java b/common/src/main/java/org/whispersystems/curve25519/java/fe_isreduced.java new file mode 100644 index 0000000..d3fca92 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/fe_isreduced.java @@ -0,0 +1,14 @@ +package org.whispersystems.curve25519.java; + +public class fe_isreduced { + public static boolean fe_isreduced(byte[] s) + { + int[] f = new int[10]; + byte[] strict = new byte[32]; + + fe_frombytes.fe_frombytes(f, s); + fe_tobytes.fe_tobytes(strict, f); + return crypto_verify_32.crypto_verify_32(strict, s) == 0; + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/fe_mont_rhs.java b/common/src/main/java/org/whispersystems/curve25519/java/fe_mont_rhs.java new file mode 100644 index 0000000..fdf5f9e --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/fe_mont_rhs.java @@ -0,0 +1,18 @@ +package org.whispersystems.curve25519.java; + +public class fe_mont_rhs { + public static void fe_mont_rhs(int[] v2, int[] u) { + int[] A = new int[10], one= new int[10]; + int[] u2= new int[10], Au= new int[10], inner= new int[10]; + + fe_1.fe_1(one); + fe_0.fe_0(A); + A[0] = 486662; /* A = 486662 */ + + fe_sq.fe_sq(u2, u); /* u^2 */ + fe_mul.fe_mul(Au, A, u); /* Au */ + fe_add.fe_add(inner, u2, Au); /* u^2 + Au */ + fe_add.fe_add(inner, inner, one); /* u^2 + Au + 1 */ + fe_mul.fe_mul(v2, u, inner); /* u(u^2 + Au + 1) */ + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/fe_montx_to_edy.java b/common/src/main/java/org/whispersystems/curve25519/java/fe_montx_to_edy.java new file mode 100644 index 0000000..2e0e80c --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/fe_montx_to_edy.java @@ -0,0 +1,20 @@ +package org.whispersystems.curve25519.java; + +public class fe_montx_to_edy { + public static void fe_montx_to_edy(int[] y, int[] u) + { + /* + y = (u - 1) / (u + 1) + + NOTE: u=-1 is converted to y=0 since fe_invert is mod-exp + */ + int[] one = new int[10], um1 = new int[10], up1 = new int[10]; + + fe_1.fe_1(one); + fe_sub.fe_sub(um1, u, one); + fe_add.fe_add(up1, u, one); + fe_invert.fe_invert(up1, up1); + fe_mul.fe_mul(y, um1, up1); + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/fe_sqrt.java b/common/src/main/java/org/whispersystems/curve25519/java/fe_sqrt.java new file mode 100644 index 0000000..eac6d39 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/fe_sqrt.java @@ -0,0 +1,34 @@ +package org.whispersystems.curve25519.java; + +public class fe_sqrt { + + /* sqrt(-1) */ + public static byte[] i_bytes = { + (byte)0xb0, (byte)0xa0, 0x0e, 0x4a, 0x27, 0x1b, (byte)0xee, (byte)0xc4, + 0x78, (byte)0xe4, 0x2f, (byte)0xad, 0x06, 0x18, 0x43, 0x2f, + (byte)0xa7, (byte)0xd7, (byte)0xfb, 0x3d, (byte)0x99, 0x00, 0x4d, 0x2b, + 0x0b, (byte)0xdf, (byte)0xc1, 0x4f, (byte)0x80, 0x24, (byte)0x83, 0x2b + }; + + /* Preconditions: a is square or zero */ + + public static void fe_sqrt(int[] out, int[] a) + { + int[] exp = new int[10], b = new int[10], b2 = new int[10], bi = new int[10], i = new int[10]; + + fe_frombytes.fe_frombytes(i, i_bytes); + fe_pow22523.fe_pow22523(exp, a); /* b = a^(q-5)/8 */ + + + fe_mul.fe_mul(b, a, exp); /* b = a * a^(q-5)/8 */ + fe_sq.fe_sq(b2, b); /* b^2 = a * a^(q-1)/4 */ + + /* note b^4 == a^2, so b^2 == a or -a + * if b^2 != a, multiply it by sqrt(-1) */ + fe_mul.fe_mul(bi, b, i); + fe_cmov.fe_cmov(b, bi, 1 ^ fe_isequal.fe_isequal(b2, a)); + fe_copy.fe_copy(out, b); + + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ge_isneutral.java b/common/src/main/java/org/whispersystems/curve25519/java/ge_isneutral.java new file mode 100644 index 0000000..0921993 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ge_isneutral.java @@ -0,0 +1,17 @@ +package org.whispersystems.curve25519.java; + +public class ge_isneutral { + /* + return 1 if p is the neutral point + return 0 otherwise + */ + + public static boolean ge_isneutral(ge_p3 p) + { + int[] zero = new int[10]; + fe_0.fe_0(zero); + + /* Check if p == neutral element == (0, 1) */ + return (fe_isequal.fe_isequal(p.X, zero) & fe_isequal.fe_isequal(p.Y, p.Z)) == 1; + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ge_montx_to_p3.java b/common/src/main/java/org/whispersystems/curve25519/java/ge_montx_to_p3.java new file mode 100644 index 0000000..7b5c4d6 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ge_montx_to_p3.java @@ -0,0 +1,39 @@ +package org.whispersystems.curve25519.java; + +public class ge_montx_to_p3 { + /* sqrt(-(A+2)) */ + private static byte[] A_bytes = { + 0x06, 0x7e, 0x45, (byte)0xff, (byte)0xaa, 0x04, 0x6e, (byte)0xcc, + (byte)0x82, 0x1a, 0x7d, 0x4b, (byte)0xd1, (byte)0xd3, (byte)0xa1, (byte)0xc5, + 0x7e, 0x4f, (byte)0xfc, 0x03, (byte)0xdc, 0x08, 0x7b, (byte)0xd2, + (byte)0xbb, 0x06, (byte)0xa0, 0x60, (byte)0xf4, (byte)0xed, 0x26, 0x0f + }; + + public static void ge_montx_to_p3(ge_p3 p, int[] u, byte ed_sign_bit) + { + int[] x = new int[10], y = new int[10], A = new int[10], v = new int[10], v2 = new int[10], iv = new int[10], nx = new int[10]; + + fe_frombytes.fe_frombytes(A, A_bytes); + + /* given u, recover edwards y */ + /* given u, recover v */ + /* given u and v, recover edwards gen_x */ + + fe_montx_to_edy.fe_montx_to_edy(y, u); /* y = (u - 1) / (u + 1) */ + + fe_mont_rhs.fe_mont_rhs(v2, u); /* v^2 = u(u^2 + Au + 1) */ + fe_sqrt.fe_sqrt(v, v2); /* v = sqrt(v^2) */ + + fe_mul.fe_mul(x, u, A); /* gen_x = u * sqrt(-(A+2)) */ + fe_invert.fe_invert(iv, v); /* 1/v */ + fe_mul.fe_mul(x, x, iv); /* gen_x = (u/v) * sqrt(-(A+2)) */ + + fe_neg.fe_neg(nx, x); /* negate gen_x to match sign bit */ + fe_cmov.fe_cmov(x, nx, fe_isnegative.fe_isnegative(x) ^ ed_sign_bit); + + fe_copy.fe_copy(p.X, x); + fe_copy.fe_copy(p.Y, y); + fe_1.fe_1(p.Z); + fe_mul.fe_mul(p.T, p.X, p.Y); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ge_neg.java b/common/src/main/java/org/whispersystems/curve25519/java/ge_neg.java new file mode 100644 index 0000000..9d1eb89 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ge_neg.java @@ -0,0 +1,11 @@ +package org.whispersystems.curve25519.java; + +public class ge_neg { + public static void ge_neg(ge_p3 r, ge_p3 p) + { + fe_neg.fe_neg(r.X, p.X); + fe_copy.fe_copy(r.Y, p.Y); + fe_copy.fe_copy(r.Z, p.Z); + fe_neg.fe_neg(r.T, p.T); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ge_p3_add.java b/common/src/main/java/org/whispersystems/curve25519/java/ge_p3_add.java new file mode 100644 index 0000000..77ce114 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ge_p3_add.java @@ -0,0 +1,14 @@ +package org.whispersystems.curve25519.java; + +public class ge_p3_add { + public static void ge_p3_add(ge_p3 r, ge_p3 p, ge_p3 q) + { + ge_cached p_cached = new ge_cached(); + ge_p1p1 r_p1p1 = new ge_p1p1(); + + ge_p3_to_cached.ge_p3_to_cached(p_cached, p); + ge_add.ge_add(r_p1p1, q, p_cached); + ge_p1p1_to_p3.ge_p1p1_to_p3(r, r_p1p1); + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ge_scalarmult.java b/common/src/main/java/org/whispersystems/curve25519/java/ge_scalarmult.java new file mode 100644 index 0000000..6ecaf3b --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ge_scalarmult.java @@ -0,0 +1,131 @@ +package org.whispersystems.curve25519.java; + +import static org.whispersystems.curve25519.java.ge_scalarmult_base.equal; +import static org.whispersystems.curve25519.java.ge_scalarmult_base.negative; + +public class ge_scalarmult { + + static void cmov(ge_cached t, ge_cached u, int b) { + fe_cmov.fe_cmov(t.YplusX, u.YplusX, b); + fe_cmov.fe_cmov(t.YminusX, u.YminusX, b); + fe_cmov.fe_cmov(t.Z, u.Z, b); + fe_cmov.fe_cmov(t.T2d, u.T2d, b); + } + + static void select(ge_cached t, ge_cached[] pre, byte b) { + ge_cached minust = new ge_cached(); + int bnegative = negative(b); + int babs = b - (((-bnegative) & b) << 1); + + fe_1.fe_1(t.YplusX); + fe_1.fe_1(t.YminusX); + fe_1.fe_1(t.Z); + fe_0.fe_0(t.T2d); + + cmov(t, pre[0], equal((byte) babs, (byte) 1)); + cmov(t, pre[1], equal((byte) babs, (byte) 2)); + cmov(t, pre[2], equal((byte) babs, (byte) 3)); + cmov(t, pre[3], equal((byte) babs, (byte) 4)); + cmov(t, pre[4], equal((byte) babs, (byte) 5)); + cmov(t, pre[5], equal((byte) babs, (byte) 6)); + cmov(t, pre[6], equal((byte) babs, (byte) 7)); + cmov(t, pre[7], equal((byte) babs, (byte) 8)); + fe_copy.fe_copy(minust.YplusX, t.YminusX); + fe_copy.fe_copy(minust.YminusX, t.YplusX); + fe_copy.fe_copy(minust.Z, t.Z); + fe_neg.fe_neg(minust.T2d, t.T2d); + cmov(t, minust, bnegative); + } + +/* +h = a * B +where a = a[0]+256*a[1]+...+256^31 a[31] +B is the Ed25519 base point (x,4/5) with x positive. + +Preconditions: + a[31] <= 127 +*/ + + public static void ge_scalarmult(ge_p3 h, byte[] a, ge_p3 A) { + byte[] e = new byte[64]; + byte carry; + ge_p1p1 r = new ge_p1p1(); + ge_p2 s = new ge_p2(); + ge_p3 t0 = new ge_p3(), t1 = new ge_p3(), t2 = new ge_p3(); + ge_cached t = new ge_cached(); + ge_cached[] pre = new ge_cached[8]; + for (int i = 0; i < 8; i++) { + pre[i] = new ge_cached(); + } + int i; + + for (i = 0; i < 32; ++i) { + e[2 * i + 0] = (byte) ((a[i] >>> 0) & 15); + e[2 * i + 1] = (byte) ((a[i] >>> 4) & 15); + } + /* each e[i] is between 0 and 15 */ + /* e[63] is between 0 and 7 */ + + carry = 0; + for (i = 0; i < 63; ++i) { + e[i] += carry; + carry = (byte) (e[i] + 8); + carry >>= 4; + e[i] -= carry << 4; + } + e[63] += carry; + /* each e[i] is between -8 and 8 */ + + // Precomputation: + ge_p3_to_cached.ge_p3_to_cached(pre[0], A); // A + + ge_p3_dbl.ge_p3_dbl(r, A); + ge_p1p1_to_p3.ge_p1p1_to_p3(t0, r); + ge_p3_to_cached.ge_p3_to_cached(pre[1], t0); // 2A + + ge_add.ge_add(r, A, pre[1]); + ge_p1p1_to_p3.ge_p1p1_to_p3(t1, r); + ge_p3_to_cached.ge_p3_to_cached(pre[2], t1); // 3A + + ge_p3_dbl.ge_p3_dbl(r, t0); + ge_p1p1_to_p3.ge_p1p1_to_p3(t0, r); + ge_p3_to_cached.ge_p3_to_cached(pre[3], t0); // 4A + + ge_add.ge_add(r, A, pre[3]); + ge_p1p1_to_p3.ge_p1p1_to_p3(t2, r); + ge_p3_to_cached.ge_p3_to_cached(pre[4], t2); // 5A + + ge_p3_dbl.ge_p3_dbl(r, t1); + ge_p1p1_to_p3.ge_p1p1_to_p3(t1, r); + ge_p3_to_cached.ge_p3_to_cached(pre[5], t1); // 6A + + ge_add.ge_add(r, A, pre[5]); + ge_p1p1_to_p3.ge_p1p1_to_p3(t1, r); + ge_p3_to_cached.ge_p3_to_cached(pre[6], t1); // 7A + + ge_p3_dbl.ge_p3_dbl(r, t0); + ge_p1p1_to_p3.ge_p1p1_to_p3(t0, r); + ge_p3_to_cached.ge_p3_to_cached(pre[7], t0); // 8A + + ge_p3_0.ge_p3_0(h); + + for (i = 63; i > 0; i--) { + select(t, pre, e[i]); + ge_add.ge_add(r, h, t); + ge_p1p1_to_p2.ge_p1p1_to_p2(s, r); + + ge_p2_dbl.ge_p2_dbl(r, s); + ge_p1p1_to_p2.ge_p1p1_to_p2(s, r); + ge_p2_dbl.ge_p2_dbl(r, s); + ge_p1p1_to_p2.ge_p1p1_to_p2(s, r); + ge_p2_dbl.ge_p2_dbl(r, s); + ge_p1p1_to_p2.ge_p1p1_to_p2(s, r); + ge_p2_dbl.ge_p2_dbl(r, s); + ge_p1p1_to_p3.ge_p1p1_to_p3(h, r); + } + select(t, pre, e[0]); + ge_add.ge_add(r, h, t); + ge_p1p1_to_p3.ge_p1p1_to_p3(h, r); + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/ge_scalarmult_cofactor.java b/common/src/main/java/org/whispersystems/curve25519/java/ge_scalarmult_cofactor.java new file mode 100644 index 0000000..963ba26 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/ge_scalarmult_cofactor.java @@ -0,0 +1,22 @@ +package org.whispersystems.curve25519.java; + +public class ge_scalarmult_cofactor { + /* + return 8 * p + */ + public static void ge_scalarmult_cofactor(ge_p3 q, ge_p3 p) + { + ge_p1p1 p1p1 = new ge_p1p1(); + ge_p2 p2 = new ge_p2(); + + ge_p3_dbl.ge_p3_dbl(p1p1, p); + ge_p1p1_to_p2.ge_p1p1_to_p2(p2, p1p1); + + ge_p2_dbl.ge_p2_dbl(p1p1, p2); + ge_p1p1_to_p2.ge_p1p1_to_p2(p2, p1p1); + + ge_p2_dbl.ge_p2_dbl(p1p1, p2); + ge_p1p1_to_p3.ge_p1p1_to_p3(q, p1p1); + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/gen_labelset.java b/common/src/main/java/org/whispersystems/curve25519/java/gen_labelset.java new file mode 100644 index 0000000..3d49652 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/gen_labelset.java @@ -0,0 +1,86 @@ +package org.whispersystems.curve25519.java; + +import java.nio.ByteBuffer; + +public class gen_labelset { + final static int LABELSETMAXLEN = 512; + final static int LABELMAXLEN = 128; + + final static byte[] B_bytes = { + 0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + }; + + public static boolean labelset_is_empty(byte[] bb) { + return bb.length == 3; + } + + public static boolean labelset_validate(byte[] labelset) + { + if (labelset == null) + return false; + if (labelset.length < 3 || labelset.length > LABELSETMAXLEN) + return false; + + int num_labels = labelset[0]; + int offset = 1; + for (int count = 0; count < num_labels; count++) { + int label_len = labelset[offset]; + offset += 1 + label_len; + if (offset > labelset.length) + return false; + } + return offset == labelset.length; + } + + + public static byte[] labelset_new(String protocol_name, + byte[] customization_label) { + if (LABELSETMAXLEN < 3 + protocol_name.length() + customization_label.length) + throw new IllegalArgumentException(); + if (protocol_name.length() > LABELMAXLEN) + throw new IllegalArgumentException(); + if (customization_label.length > LABELMAXLEN) + throw new IllegalArgumentException(); + + byte[] protocol_name_bytes = protocol_name.getBytes(); + + ByteBuffer byteBuffer = ByteBuffer.allocate(3 + protocol_name_bytes.length + customization_label.length); + byteBuffer.put((byte)2); + byteBuffer.put((byte)protocol_name_bytes.length); + byteBuffer.put(protocol_name_bytes); + if (byteBuffer.position() < LABELSETMAXLEN) { + byteBuffer.put((byte)customization_label.length); + } + + byteBuffer.put(customization_label); + + assert byteBuffer.position() == 3 + protocol_name.length() + customization_label.length; + + return byteBuffer.array(); + } + + public static byte[] labelset_add(byte[] labelset, String label) + { + if (labelset.length > LABELSETMAXLEN) + throw new IllegalStateException(); + if (labelset.length >= LABELMAXLEN || labelset.length + label.length() + 1 > LABELSETMAXLEN) + throw new IllegalStateException(); + if (labelset.length < 3 || LABELSETMAXLEN < 4) + throw new IllegalStateException(); + if (label.length() > LABELMAXLEN) + throw new IllegalStateException(); + + ByteBuffer bb = ByteBuffer.allocate(labelset.length + label.length() + 1); + bb.put((byte)(labelset[0]+1)); + bb.put(labelset, 1, labelset.length - 1); + bb.put((byte)label.getBytes().length); + bb.put(label.getBytes()); + + assert bb.position() < LABELSETMAXLEN; + + return bb.array(); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/gen_x.java b/common/src/main/java/org/whispersystems/curve25519/java/gen_x.java new file mode 100644 index 0000000..07ab606 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/gen_x.java @@ -0,0 +1,79 @@ +package org.whispersystems.curve25519.java; + +public class gen_x { + public static final int SCALARLEN = 32; + public static final int POINTLEN = 32; + + /* + * Convert the X25519 public key into an Ed25519 public key. + * y = (u - 1) / (u + 1) + * NOTE: u=-1 is converted to y=0 since fe_invert is mod-exp + */ + public static int convert_25519_pubkey(byte[] ed_pubkey_bytes, byte[] x25519_pubkey_bytes) { + int[] u = new int[10]; + int[] y = new int[10]; + + if (!fe_isreduced.fe_isreduced(x25519_pubkey_bytes)) + return -1; + + fe_frombytes.fe_frombytes(u, x25519_pubkey_bytes); + + fe_montx_to_edy.fe_montx_to_edy(y, u); + + fe_tobytes.fe_tobytes(ed_pubkey_bytes, y); + return 0; + } + + public static int calculate_25519_keypair(byte[] K_bytes, byte[] k_scalar, + byte[] x25519_privkey_scalar) { + byte[] kneg = new byte[SCALARLEN]; + ge_p3 ed_pubkey_point = new ge_p3(); /* Ed25519 pubkey point */ + + /* Convert the Curve25519 privkey to an Ed25519 public key */ + ge_scalarmult_base.ge_scalarmult_base(ed_pubkey_point, x25519_privkey_scalar); + ge_p3_tobytes.ge_p3_tobytes(K_bytes, ed_pubkey_point); + + /* Force Edwards sign bit to zero */ + byte sign_bit = (byte) ((K_bytes[31] & 0x80) >> 7); + System.arraycopy(x25519_privkey_scalar, 0, k_scalar, 0, 32); + sc_neg.sc_neg(kneg, k_scalar); + sc_cmov.sc_cmov(k_scalar, kneg, sign_bit); + K_bytes[31] &= 0x7F; + + Arrays.fill(kneg, (byte) 0); + return 0; + } + + public static boolean generalized_xveddsa_25519_sign( + Sha512 sha512provider, + byte[] signature_out, + byte[] x25519_privkey_scalar, + byte[] msg, + byte[] random) { + byte[] K_bytes = new byte[POINTLEN]; + byte[] k_scalar = new byte[SCALARLEN]; + if (calculate_25519_keypair(K_bytes, k_scalar, x25519_privkey_scalar) != 0) + return false; + + boolean retval = veddsa.generalized_veddsa_25519_sign(sha512provider, signature_out, K_bytes, k_scalar, + msg, random, new byte[]{}); + Arrays.fill(k_scalar, (byte) 0); + return retval; + } + + public static int generalized_xveddsa_25519_verify( + Sha512 sha512provider, + byte[] vrf_output, + byte[] signature, + byte[] x25519_pubkey_bytes, + byte[] msg) { + byte[] K_bytes = new byte[POINTLEN]; + + if (convert_25519_pubkey(K_bytes, x25519_pubkey_bytes) != 0) + return -1; + + return veddsa.generalized_veddsa_25519_verify(sha512provider, vrf_output, signature, K_bytes, msg, + new byte[]{}); + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/point_isreduced.java b/common/src/main/java/org/whispersystems/curve25519/java/point_isreduced.java new file mode 100644 index 0000000..cdec22b --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/point_isreduced.java @@ -0,0 +1,13 @@ +package org.whispersystems.curve25519.java; + +public class point_isreduced { + public static boolean point_isreduced(byte[] p) + { + byte[] strict = new byte[32]; + + System.arraycopy(p, 0, strict, 0, 32); + strict[31] &= 0x7F; /* mask off sign bit */ + return fe_isreduced.fe_isreduced(strict); + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/sc_cmov.java b/common/src/main/java/org/whispersystems/curve25519/java/sc_cmov.java new file mode 100644 index 0000000..a549e0b --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/sc_cmov.java @@ -0,0 +1,22 @@ +package org.whispersystems.curve25519.java; + +public class sc_cmov { + /* + Replace (f,g) with (g,g) if b == 1; + replace (f,g) with (f,g) if b == 0. + + Preconditions: b in {0,1}. + */ + + public static void sc_cmov(byte[] f, byte[] g, byte b) { + int count = 32; + byte[] x = new byte[32]; + for (count = 0; count < 32; count++) + x[count] = (byte) (f[count] ^ g[count]); + b = (byte)-b; + for (count = 0; count < 32; count++) + x[count] &= b; + for (count = 0; count < 32; count++) + f[count] = (byte) (f[count] ^ x[count]); + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/sc_isreduced.java b/common/src/main/java/org/whispersystems/curve25519/java/sc_isreduced.java new file mode 100644 index 0000000..2d35f1b --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/sc_isreduced.java @@ -0,0 +1,13 @@ +package org.whispersystems.curve25519.java; + +public class sc_isreduced { + public static boolean sc_isreduced(byte[] s) + { + byte[] strict = new byte[64]; + + System.arraycopy(s, 0, strict, 0, 32); + sc_reduce.sc_reduce(strict); + return crypto_verify_32.crypto_verify_32(strict, s) == 0; + } + +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/sc_neg.java b/common/src/main/java/org/whispersystems/curve25519/java/sc_neg.java new file mode 100644 index 0000000..f3b0187 --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/sc_neg.java @@ -0,0 +1,15 @@ +package org.whispersystems.curve25519.java; + +public class sc_neg { + static byte[] lminus1 = {(byte)0xec, (byte)0xd3, (byte)0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, + (byte)0xd6, (byte)0x9c, (byte)0xf7, (byte)0xa2, (byte)0xde, (byte)0xf9, (byte)0xde, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; + + /* b = -a (mod l) */ + public static void sc_neg(byte[] b, byte[] a) + { + byte[] zero = new byte[32]; + sc_muladd.sc_muladd(b, lminus1, a, zero); /* b = (-1)a + 0 (mod l) */ + } +} diff --git a/common/src/main/java/org/whispersystems/curve25519/java/veddsa.java b/common/src/main/java/org/whispersystems/curve25519/java/veddsa.java new file mode 100644 index 0000000..d33620a --- /dev/null +++ b/common/src/main/java/org/whispersystems/curve25519/java/veddsa.java @@ -0,0 +1,401 @@ +package org.whispersystems.curve25519.java; + +public class veddsa { + final static int BLOCKLEN = 128; /* SHA512 */ + final static int HASHLEN = 64; /* SHA512 */ + final static int POINTLEN = 32; + final static int SCALARLEN = 32; + final static int RANDLEN = 32; + final static int MSTART = 2048; + final static int MSGMAXLEN = 1048576; + final static int BUFLEN = 1024; + final static int VRFOUTPUTLEN = 32; + + /* B: base point + * R: commitment (point), + r: private nonce (scalar) + K: encoded public key + k: private key (scalar) + Z: 32-bytes random + M: buffer containing message, message starts at M_start, continues for M_len + r = hash(B || gen_labelset || Z || pad1 || k || pad2 || gen_labelset || K || extra || M) (mod q) + */ + public static boolean generalized_commit(Sha512 sha512provider, byte[] R_bytes, byte[] r_scalar, + byte[] labelset, + byte[] extra, int extra_len, + byte[] K_bytes, byte[] k_scalar, + byte[] Z, byte[] msg, + byte[] M_buf, int M_start, int M_len) { + ge_p3 R_point = new ge_p3(); + byte[] hash = new byte[64]; + + if (!gen_labelset.labelset_validate(labelset)) { + return false; + } + if (R_bytes == null || r_scalar == null || + K_bytes == null || k_scalar == null || + Z == null) { + return false; + } + if (extra == null || extra.length == 0) { + return false; + } + if (gen_labelset.labelset_is_empty(labelset)) { + return false; + } + + int prefix_len = 0; + prefix_len += POINTLEN + labelset.length + RANDLEN; + int pad_len1 = ((BLOCKLEN - (prefix_len % BLOCKLEN)) % BLOCKLEN); + prefix_len += pad_len1; + prefix_len += SCALARLEN; + int pad_len2 = ((BLOCKLEN - (prefix_len % BLOCKLEN)) % BLOCKLEN); + prefix_len += pad_len2; + prefix_len += labelset.length + POINTLEN + extra_len; + if (prefix_len > M_start) { + return false; + } + + int startIndex = M_start - prefix_len; + + System.arraycopy(gen_labelset.B_bytes, 0, M_buf, startIndex, POINTLEN); + System.arraycopy(labelset, 0, M_buf, startIndex + POINTLEN, labelset.length); + System.arraycopy(Z, 0, M_buf, startIndex + POINTLEN + labelset.length, RANDLEN); + System.arraycopy(k_scalar, 0, M_buf, startIndex + POINTLEN + labelset.length + pad_len1 + RANDLEN, POINTLEN); + System.arraycopy(labelset, 0, M_buf, startIndex + 2 * POINTLEN + labelset.length + pad_len1 + RANDLEN + pad_len2, labelset.length); + System.arraycopy(K_bytes, 0, M_buf, startIndex + 2 * POINTLEN + 2 * labelset.length + pad_len1 + RANDLEN + pad_len2, POINTLEN); + System.arraycopy(extra, 0, M_buf, startIndex + 3 * POINTLEN + 2 * labelset.length + pad_len1 + RANDLEN + pad_len2, extra_len); + + byte[] in = java.util.Arrays.copyOfRange(M_buf, startIndex, M_start + M_len); + sha512provider.calculateDigest(hash, in, in.length); + + sc_reduce.sc_reduce(hash); + ge_scalarmult_base.ge_scalarmult_base(R_point, hash); + ge_p3_tobytes.ge_p3_tobytes(R_bytes, R_point); + System.arraycopy(hash, 0, r_scalar, 0, SCALARLEN); + return true; + } + + + + /* if is_labelset_empty(gen_labelset): + return hash(R || K || M) (mod q) + else: + return hash(B || gen_labelset || R || gen_labelset || K || extra || M) (mod q) + */ + public static int generalized_challenge(Sha512 sha512provider, byte[] h_scalar, + byte[] labelset, + byte[] extra, + byte[] R_bytes, + byte[] K_bytes, + byte[] M_buf, int M_start, int M_len) { + + byte[] hash = new byte[HASHLEN]; + + if (h_scalar == null) return -1; + + if (!gen_labelset.labelset_validate(labelset)) return -1; + if (R_bytes == null || K_bytes == null) return -1; + if (extra != null && gen_labelset.labelset_is_empty(labelset)) return -1; + + int prefix_len; + + if (gen_labelset.labelset_is_empty(labelset)) { + if (2 * POINTLEN > MSTART) return -1; + prefix_len = 2 * POINTLEN; + int startIndex = M_start - prefix_len; + System.arraycopy(R_bytes, 0, M_buf, startIndex, POINTLEN); + System.arraycopy(K_bytes, 0, M_buf, startIndex + POINTLEN, POINTLEN); + } else { + prefix_len = 3 * POINTLEN + 2 * labelset.length + extra.length; + int startIndex = M_start - prefix_len; + System.arraycopy(gen_labelset.B_bytes, 0, M_buf, startIndex, POINTLEN); + System.arraycopy(labelset, 0, M_buf, startIndex + POINTLEN, labelset.length); + System.arraycopy(R_bytes, 0, M_buf, startIndex + POINTLEN + labelset.length, POINTLEN); + System.arraycopy(labelset, 0, M_buf, startIndex + 2 * POINTLEN + labelset.length, labelset.length); + System.arraycopy(K_bytes, 0, M_buf, startIndex + 2 * POINTLEN + 2 * labelset.length, POINTLEN); + System.arraycopy(extra, 0, M_buf, startIndex + 3 * POINTLEN + 2 * labelset.length, extra.length); + } + + byte[] in = java.util.Arrays.copyOfRange(M_buf, M_start - prefix_len, M_start + M_len); + sha512provider.calculateDigest(hash, in, in.length); + sc_reduce.sc_reduce(hash); + System.arraycopy(hash, 0, h_scalar, 0, SCALARLEN); + return 0; + } + + /* return r + kh (mod q) */ + public static int generalized_prove(byte[] out_scalar, byte[] r_scalar, byte[] k_scalar, byte[] h_scalar) { + sc_muladd.sc_muladd(out_scalar, h_scalar, k_scalar, r_scalar); + return 0; + } + + /* R = s*B - h*K */ + public static int generalized_solve_commitment(byte[] R_bytes_out, ge_p3 K_point_out, + ge_p3 B_point, byte[] s_scalar, + byte[] K_bytes, byte[] h_scalar) { + ge_p3 Kneg_point = new ge_p3(); + ge_p2 R_calc_point_p2 = new ge_p2(); + + ge_p3 sB = new ge_p3(); + ge_p3 hK = new ge_p3(); + ge_p3 R_calc_point_p3 = new ge_p3(); + + if (ge_frombytes.ge_frombytes_negate_vartime(Kneg_point, K_bytes) != 0) { + return -1; + } + + if (B_point == null) { + ge_double_scalarmult.ge_double_scalarmult_vartime(R_calc_point_p2, h_scalar, Kneg_point, s_scalar); + ge_tobytes.ge_tobytes(R_bytes_out, R_calc_point_p2); + } else { + // s * Bv + ge_scalarmult.ge_scalarmult(sB, s_scalar, B_point); + + // h * -K + ge_scalarmult.ge_scalarmult(hK, h_scalar, Kneg_point); + + // R = sB - hK + ge_p3_add.ge_p3_add(R_calc_point_p3, sB, hK); + ge_p3_tobytes.ge_p3_tobytes(R_bytes_out, R_calc_point_p3); + } + + if (K_point_out != null) { + ge_neg.ge_neg(K_point_out, Kneg_point); + } + + return 0; + } + + public static boolean generalized_calculate_Bv(Sha512 sha512provider, ge_p3 Bv_point, + byte[] labelset, byte[] K_bytes, + byte[] M_buf, int M_start, int M_len) { + if (!gen_labelset.labelset_validate(labelset)) + return false; + if (Bv_point == null || K_bytes == null || M_buf == null) + return false; + + int prefix_len = 2 * POINTLEN + labelset.length; + if (prefix_len > M_start) + return false; + + int startIndex = M_start - prefix_len; + System.arraycopy(gen_labelset.B_bytes, 0, M_buf, startIndex, POINTLEN); + System.arraycopy(labelset, 0, M_buf, startIndex + POINTLEN, labelset.length); + System.arraycopy(K_bytes, 0, M_buf, startIndex + POINTLEN + labelset.length, POINTLEN); + + byte[] in = java.util.Arrays.copyOfRange(M_buf, startIndex, M_start + M_len); + System.arraycopy(M_buf, M_start, in, in.length - M_len, M_len); + elligator.hash_to_point(sha512provider, Bv_point, in); + return !ge_isneutral.ge_isneutral(Bv_point); + } + + public static int generalized_calculate_vrf_output(Sha512 sha512provider, + byte[] vrf_output, + byte[] labelset, + ge_p3 cKv_point) { + byte[] cKv_bytes = new byte[POINTLEN]; + byte[] hash = new byte[HASHLEN]; + + if (vrf_output == null) return -1; + Arrays.fill(vrf_output, (byte) 0); + + if (labelset.length + 2 * POINTLEN > BUFLEN) + return -1; + if (!gen_labelset.labelset_validate(labelset)) + return -1; + if (cKv_point == null) + return -1; + + ge_p3_tobytes.ge_p3_tobytes(cKv_bytes, cKv_point); + + byte[] buf = new byte[2 * POINTLEN + labelset.length]; + System.arraycopy(gen_labelset.B_bytes, 0, buf, 0, POINTLEN); + System.arraycopy(labelset, 0, buf, POINTLEN, labelset.length); + System.arraycopy(cKv_bytes, 0, buf, POINTLEN + labelset.length, POINTLEN); + + sha512provider.calculateDigest(hash, buf, buf.length); + System.arraycopy(hash, 0, vrf_output, 0, VRFOUTPUTLEN); + return 0; + } + + public static boolean generalized_veddsa_25519_sign( + Sha512 sha512provider, + byte[] signature_out, + byte[] eddsa_25519_pubkey_bytes, + byte[] eddsa_25519_privkey_scalar, + byte[] msg, + byte[] random, + byte[] customization_label) { + if (signature_out == null) { + return false; + } + + if (eddsa_25519_pubkey_bytes == null) { + return false; + } + if (eddsa_25519_privkey_scalar == null) { + return false; + } + if (customization_label == null || customization_label.length > gen_labelset.LABELMAXLEN) { + return false; + } + if (msg == null || msg.length > MSGMAXLEN) { + return false; + } + + ge_p3 Bv_point = new ge_p3(); + ge_p3 Kv_point = new ge_p3(); + ge_p3 Rv_point = new ge_p3(); + + byte[] Bv_bytes = new byte[POINTLEN]; + byte[] Kv_bytes = new byte[POINTLEN]; + byte[] Rv_bytes = new byte[POINTLEN]; + byte[] R_bytes = new byte[POINTLEN]; + byte[] r_scalar = new byte[SCALARLEN]; + byte[] h_scalar = new byte[SCALARLEN]; + byte[] s_scalar = new byte[SCALARLEN]; + byte[] extra = new byte[3 * POINTLEN]; + byte[] M_buf = new byte[msg.length + MSTART]; + String protocol_name = "VEdDSA_25519_SHA512_Elligator2"; + + System.arraycopy(msg, 0, M_buf, MSTART, msg.length); + + byte[] labelset = gen_labelset.labelset_new(protocol_name, customization_label); + + // labelset1 = add_label(labels, "1") + // Bv = hash(hash(labelset1 || K) || M) + // Kv = k * Bv + labelset = gen_labelset.labelset_add(labelset, "1"); + generalized_calculate_Bv(sha512provider, Bv_point, labelset, + eddsa_25519_pubkey_bytes, M_buf, MSTART, msg.length); + ge_scalarmult.ge_scalarmult(Kv_point, eddsa_25519_privkey_scalar, Bv_point); + ge_p3_tobytes.ge_p3_tobytes(Bv_bytes, Bv_point); + ge_p3_tobytes.ge_p3_tobytes(Kv_bytes, Kv_point); + + // labelset2 = add_label(labels, "2") + // R, r = commit(labelset2, (Bv || Kv), (K,k), Z, M) + labelset[labelset.length - 1] = '2'; + System.arraycopy(Bv_bytes, 0, extra, 0, POINTLEN); + System.arraycopy(Kv_bytes, 0, extra, POINTLEN, POINTLEN); + if (!generalized_commit(sha512provider, R_bytes, r_scalar, + labelset, + extra, 2 * POINTLEN, + eddsa_25519_pubkey_bytes, eddsa_25519_privkey_scalar, + random, msg, M_buf, MSTART, msg.length)) { + return false; + } + + // Rv = r * Bv + ge_scalarmult.ge_scalarmult(Rv_point, r_scalar, Bv_point); + ge_p3_tobytes.ge_p3_tobytes(Rv_bytes, Rv_point); + + // labelset3 = add_label(labels, "3") + // h = challenge(labelset3, (Bv || Kv || Rv), R, K, M) + labelset[labelset.length - 1] = '3'; +// memcpy(extra + 2*POINTLEN, Rv_bytes, POINTLEN); + System.arraycopy(Rv_bytes, 0, extra, 2 * POINTLEN, POINTLEN); + if (generalized_challenge(sha512provider, h_scalar, + labelset, extra, R_bytes, eddsa_25519_pubkey_bytes, M_buf, MSTART, msg.length) != 0) { + return false; + } + + // s = prove(r, k, h) + if (generalized_prove(s_scalar, r_scalar, eddsa_25519_privkey_scalar, h_scalar) != 0) { + return false; + } + + // return (Kv || h || s) + System.arraycopy(Kv_bytes, 0, signature_out, 0, POINTLEN); + System.arraycopy(h_scalar, 0, signature_out, POINTLEN, SCALARLEN); + System.arraycopy(s_scalar, 0, signature_out, POINTLEN + SCALARLEN, SCALARLEN); + + Arrays.fill(r_scalar, (byte) 0); + + return true; + } + + public static int generalized_veddsa_25519_verify( + Sha512 sha512provider, + byte[] vrf_output, + byte[] signature, + byte[] eddsa_25519_pubkey_bytes, + byte[] msg, + byte[] customization_label) { + if (signature == null) return -1; + if (eddsa_25519_pubkey_bytes == null) return -1; + if (msg == null) return -1; + if (customization_label == null) return -1; + if (customization_label.length > gen_labelset.LABELMAXLEN) return -1; + if (msg.length > MSGMAXLEN) return -1; + + ge_p3 Bv_point = new ge_p3(); + ge_p3 K_point = new ge_p3(); + ge_p3 Kv_point = new ge_p3(); + ge_p3 cK_point = new ge_p3(); + ge_p3 cKv_point = new ge_p3(); + + byte[] Bv_bytes = new byte[POINTLEN]; + byte[] R_calc_bytes = new byte[POINTLEN]; + byte[] Rv_calc_bytes = new byte[POINTLEN]; + byte[] h_calc_scalar = new byte[SCALARLEN]; + byte[] extra = new byte[3 * POINTLEN]; + String protocol_name = "VEdDSA_25519_SHA512_Elligator2"; + + byte[] M_buf = new byte[msg.length + MSTART]; + System.arraycopy(msg, 0, M_buf, MSTART, msg.length); + + byte[] Kv_bytes = new byte[POINTLEN]; + System.arraycopy(signature, 0, Kv_bytes, 0, POINTLEN); + byte[] h_scalar = new byte[SCALARLEN]; + System.arraycopy(signature, POINTLEN, h_scalar, 0, SCALARLEN); + byte[] s_scalar = new byte[SCALARLEN]; + System.arraycopy(signature, POINTLEN + SCALARLEN, s_scalar, 0, SCALARLEN); + + if (!point_isreduced.point_isreduced(eddsa_25519_pubkey_bytes)) return -1; + if (!point_isreduced.point_isreduced(Kv_bytes)) return -1; + if (!sc_isreduced.sc_isreduced(h_scalar)) return -1; + if (!sc_isreduced.sc_isreduced(s_scalar)) return -1; + + // gen_labelset = new_labelset(protocol_name, customization_label) + byte[] labelset = gen_labelset.labelset_new(protocol_name, customization_label); + + // labelset1 = add_label(labels, "1") + // Bv = hash(hash(labelset1 || K) || M) + labelset = gen_labelset.labelset_add(labelset, "1"); + if (!generalized_calculate_Bv(sha512provider, Bv_point, labelset, eddsa_25519_pubkey_bytes, M_buf, MSTART, msg.length)) return -1; + ge_p3_tobytes.ge_p3_tobytes(Bv_bytes, Bv_point); + + // R = solve_commitment(B, s, K, h) + if (generalized_solve_commitment(R_calc_bytes, K_point, null, + s_scalar, eddsa_25519_pubkey_bytes, h_scalar) != 0) return -1; + + // Rv = solve_commitment(Bv, s, Kv, h) + if (generalized_solve_commitment(Rv_calc_bytes, Kv_point, Bv_point, + s_scalar, Kv_bytes, h_scalar) != 0) return -1; + + ge_scalarmult_cofactor.ge_scalarmult_cofactor(cK_point, K_point); + ge_scalarmult_cofactor.ge_scalarmult_cofactor(cKv_point, Kv_point); + if (ge_isneutral.ge_isneutral(cK_point) || ge_isneutral.ge_isneutral(cKv_point) || ge_isneutral.ge_isneutral(Bv_point)) return -1; + + // labelset3 = add_label(labels, "3") + // h = challenge(labelset3, (Bv || Kv || Rv), R, K, M) + labelset[labelset.length - 1] = '3'; + System.arraycopy(Bv_bytes, 0, extra, 0, POINTLEN); + System.arraycopy(Kv_bytes, 0, extra, POINTLEN, POINTLEN); + System.arraycopy(Rv_calc_bytes, 0, extra, 2 * POINTLEN, POINTLEN); + if (generalized_challenge(sha512provider, h_calc_scalar, + labelset, + extra, + R_calc_bytes, eddsa_25519_pubkey_bytes, M_buf, MSTART, msg.length) != 0) return -1; + + // if bytes_equal(h, h') + if (crypto_verify_32.crypto_verify_32(h_scalar, h_calc_scalar) != 0) return -1; + + // labelset4 = add_label(labels, "4") + // v = hash(labelset4 || c*Kv) + labelset[labelset.length - 1] = '4'; + + return generalized_calculate_vrf_output(sha512provider, vrf_output, labelset, cKv_point); + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 2322723c7ed5f591adfa4c87b6c51932f591249a..5c2d1cf016b3885f6930543d57b744ea8c220a1a 100644 GIT binary patch literal 55616 zcmafaW0WS*vSoFbZJS-TZP!<}ZQEV8ZQHihW!tvx>6!c9%-lQoy;&DmfdT@8fB*sl68LLCKtKQ283+jS?^Q-bNq|NIAW8=eB==8_)^)r*{C^$z z{u;{v?IMYnO`JhmPq7|LA_@Iz75S9h~8`iX>QrjrmMeu{>hn4U;+$dor zz+`T8Q0f}p^Ao)LsYq74!W*)&dTnv}E8;7H*Zetclpo2zf_f>9>HT8;`O^F8;M%l@ z57Z8dk34kG-~Wg7n48qF2xwPp;SOUpd1}9Moir5$VSyf4gF)Mp-?`wO3;2x9gYj59oFwG>?Leva43@e(z{mjm0b*@OAYLC`O9q|s+FQLOE z!+*Y;%_0(6Sr<(cxE0c=lS&-FGBFGWd_R<5$vwHRJG=tB&Mi8@hq_U7@IMyVyKkOo6wgR(<% zQw1O!nnQl3T9QJ)Vh=(`cZM{nsEKChjbJhx@UQH+G>6p z;beBQ1L!3Zl>^&*?cSZjy$B3(1=Zyn~>@`!j%5v7IBRt6X`O)yDpVLS^9EqmHxBcisVG$TRwiip#ViN|4( zYn!Av841_Z@Ys=T7w#>RT&iXvNgDq3*d?$N(SznG^wR`x{%w<6^qj&|g})La;iD?`M=p>99p><39r9+e z`dNhQ&tol5)P#;x8{tT47i*blMHaDKqJs8!Pi*F{#)9%USFxTVMfMOy{mp2ZrLR40 z2a9?TJgFyqgx~|j0eA6SegKVk@|Pd|_6P$HvwTrLTK)Re`~%kg8o9`EAE1oAiY5Jgo=H}0*D?tSCn^=SIN~fvv453Ia(<1|s07aTVVtsRxY6+tT3589iQdi^ zC92D$ewm9O6FA*u*{Fe_=b`%q`pmFvAz@hfF@OC_${IPmD#QMpPNo0mE9U=Ch;k0L zZteokPG-h7PUeRCPPYG%H!WswC?cp7M|w42pbtwj!m_&4%hB6MdLQe&}@5-h~! zkOt;w0BbDc0H!RBw;1UeVckHpJ@^|j%FBZlC} zsm?nFOT$`F_i#1_gh4|n$rDe>0md6HvA=B%hlX*3Z%y@a&W>Rq`Fe(8smIgxTGb#8 zZ`->%h!?QCk>v*~{!qp=w?a*};Y**1uH`)OX`Gi+L%-d6{rV?@}MU#qfCU(!hLz;kWH=0A%W7E^pA zD;A%Jg5SsRe!O*0TyYkAHe&O9z*Ij-YA$%-rR?sc`xz_v{>x%xY39!8g#!Z0#03H( z{O=drKfb0cbx1F*5%q81xvTDy#rfUGw(fesh1!xiS2XT;7_wBi(Rh4i(!rR^9=C+- z+**b9;icxfq@<7}Y!PW-0rTW+A^$o*#ZKenSkxLB$Qi$%gJSL>x!jc86`GmGGhai9 zOHq~hxh}KqQHJeN$2U{M>qd*t8_e&lyCs69{bm1?KGTYoj=c0`rTg>pS6G&J4&)xp zLEGIHSTEjC0-s-@+e6o&w=h1sEWWvJUvezID1&exb$)ahF9`(6`?3KLyVL$|c)CjS zx(bsy87~n8TQNOKle(BM^>1I!2-CZ^{x6zdA}qeDBIdrfd-(n@Vjl^9zO1(%2pP9@ zKBc~ozr$+4ZfjmzEIzoth(k?pbI87=d5OfjVZ`Bn)J|urr8yJq`ol^>_VAl^P)>2r)s+*3z5d<3rP+-fniCkjmk=2hTYRa@t zCQcSxF&w%mHmA?!vaXnj7ZA$)te}ds+n8$2lH{NeD4mwk$>xZCBFhRy$8PE>q$wS`}8pI%45Y;Mg;HH+}Dp=PL)m77nKF68FggQ-l3iXlVZuM2BDrR8AQbK;bn1%jzahl0; zqz0(mNe;f~h8(fPzPKKf2qRsG8`+Ca)>|<&lw>KEqM&Lpnvig>69%YQpK6fx=8YFj zHKrfzy>(7h2OhUVasdwKY`praH?>qU0326-kiSyOU_Qh>ytIs^htlBA62xU6xg?*l z)&REdn*f9U3?u4$j-@ndD#D3l!viAUtw}i5*Vgd0Y6`^hHF5R=No7j8G-*$NWl%?t z`7Nilf_Yre@Oe}QT3z+jOUVgYtT_Ym3PS5(D>kDLLas8~F+5kW%~ZYppSrf1C$gL* zCVy}fWpZ3s%2rPL-E63^tA|8OdqKsZ4TH5fny47ENs1#^C`_NLg~H^uf3&bAj#fGV zDe&#Ot%_Vhj$}yBrC3J1Xqj>Y%&k{B?lhxKrtYy;^E9DkyNHk5#6`4cuP&V7S8ce9 zTUF5PQIRO7TT4P2a*4;M&hk;Q7&{(83hJe5BSm=9qt~;U)NTf=4uKUcnxC`;iPJeI zW#~w?HIOM+0j3ptB0{UU{^6_#B*Q2gs;1x^YFey(%DJHNWz@e_NEL?$fv?CDxG`jk zH|52WFdVsZR;n!Up;K;4E$|w4h>ZIN+@Z}EwFXI{w_`?5x+SJFY_e4J@|f8U08%dd z#Qsa9JLdO$jv)?4F@&z_^{Q($tG`?|9bzt8ZfH9P`epY`soPYqi1`oC3x&|@m{hc6 zs0R!t$g>sR@#SPfNV6Pf`a^E?q3QIaY30IO%yKjx#Njj@gro1YH2Q(0+7D7mM~c>C zk&_?9Ye>B%*MA+77$Pa!?G~5tm`=p{NaZsUsOgm6Yzclr_P^2)r(7r%n(0?4B#$e7 z!fP;+l)$)0kPbMk#WOjm07+e?{E)(v)2|Ijo{o1+Z8#8ET#=kcT*OwM#K68fSNo%< zvZFdHrOrr;>`zq!_welWh!X}=oN5+V01WJn7=;z5uo6l_$7wSNkXuh=8Y>`TjDbO< z!yF}c42&QWYXl}XaRr0uL?BNPXlGw=QpDUMo`v8pXzzG(=!G;t+mfCsg8 zJb9v&a)E!zg8|%9#U?SJqW!|oBHMsOu}U2Uwq8}RnWeUBJ>FtHKAhP~;&T4mn(9pB zu9jPnnnH0`8ywm-4OWV91y1GY$!qiQCOB04DzfDDFlNy}S{$Vg9o^AY!XHMueN<{y zYPo$cJZ6f7``tmlR5h8WUGm;G*i}ff!h`}L#ypFyV7iuca!J+C-4m@7*Pmj9>m+jh zlpWbud)8j9zvQ`8-oQF#u=4!uK4kMFh>qS_pZciyq3NC(dQ{577lr-!+HD*QO_zB9 z_Rv<#qB{AAEF8Gbr7xQly%nMA%oR`a-i7nJw95F3iH&IX5hhy3CCV5y>mK4)&5aC*12 zI`{(g%MHq<(ocY5+@OK-Qn-$%!Nl%AGCgHl>e8ogTgepIKOf3)WoaOkuRJQt%MN8W z=N-kW+FLw=1^}yN@*-_c>;0N{-B!aXy#O}`%_~Nk?{e|O=JmU8@+92Q-Y6h)>@omP=9i~ zi`krLQK^!=@2BH?-R83DyFkejZkhHJqV%^} zUa&K22zwz7b*@CQV6BQ9X*RB177VCVa{Z!Lf?*c~PwS~V3K{id1TB^WZh=aMqiws5)qWylK#^SG9!tqg3-)p_o(ABJsC!0;0v36;0tC= z!zMQ_@se(*`KkTxJ~$nIx$7ez&_2EI+{4=uI~dwKD$deb5?mwLJ~ema_0Z z6A8Q$1~=tY&l5_EBZ?nAvn$3hIExWo_ZH2R)tYPjxTH5mAw#3n-*sOMVjpUrdnj1DBm4G!J+Ke}a|oQN9f?!p-TcYej+(6FNh_A? zJ3C%AOjc<8%9SPJ)U(md`W5_pzYpLEMwK<_jgeg-VXSX1Nk1oX-{yHz z-;CW!^2ds%PH{L{#12WonyeK5A=`O@s0Uc%s!@22etgSZW!K<%0(FHC+5(BxsXW@e zAvMWiO~XSkmcz%-@s{|F76uFaBJ8L5H>nq6QM-8FsX08ug_=E)r#DC>d_!6Nr+rXe zzUt30Du_d0oSfX~u>qOVR*BmrPBwL@WhF^5+dHjWRB;kB$`m8|46efLBXLkiF|*W= zg|Hd(W}ZnlJLotYZCYKoL7YsQdLXZ!F`rLqLf8n$OZOyAzK`uKcbC-n0qoH!5-rh&k-`VADETKHxrhK<5C zhF0BB4azs%j~_q_HA#fYPO0r;YTlaa-eb)Le+!IeP>4S{b8&STp|Y0if*`-A&DQ$^ z-%=i73HvEMf_V6zSEF?G>G-Eqn+|k`0=q?(^|ZcqWsuLlMF2!E*8dDAx%)}y=lyMa z$Nn0_f8YN8g<4D>8IL3)GPf#dJYU@|NZqIX$;Lco?Qj=?W6J;D@pa`T=Yh z-ybpFyFr*3^gRt!9NnbSJWs2R-S?Y4+s~J8vfrPd_&_*)HBQ{&rW(2X>P-_CZU8Y9 z-32><7|wL*K+3{ZXE5}nn~t@NNT#Bc0F6kKI4pVwLrpU@C#T-&f{Vm}0h1N3#89@d zgcx3QyS;Pb?V*XAq;3(W&rjLBazm69XX;%^n6r}0!CR2zTU1!x#TypCr`yrII%wk8 z+g)fyQ!&xIX(*>?T}HYL^>wGC2E}euj{DD_RYKK@w=yF+44367X17)GP8DCmBK!xS zE{WRfQ(WB-v>DAr!{F2-cQKHIjIUnLk^D}7XcTI#HyjSiEX)BO^GBI9NjxojYfQza zWsX@GkLc7EqtP8(UM^cq5zP~{?j~*2T^Bb={@PV)DTkrP<9&hxDwN2@hEq~8(ZiF! z3FuQH_iHyQ_s-#EmAC5~K$j_$cw{+!T>dm#8`t%CYA+->rWp09jvXY`AJQ-l%C{SJ z1c~@<5*7$`1%b}n7ivSo(1(j8k+*Gek(m^rQ!+LPvb=xA@co<|(XDK+(tb46xJ4) zcw7w<0p3=Idb_FjQ@ttoyDmF?cT4JRGrX5xl&|ViA@Lg!vRR}p#$A?0=Qe+1)Mizl zn;!zhm`B&9t0GA67GF09t_ceE(bGdJ0mbXYrUoV2iuc3c69e;!%)xNOGG*?x*@5k( zh)snvm0s&gRq^{yyeE)>hk~w8)nTN`8HJRtY0~1f`f9ue%RV4~V(K*B;jFfJY4dBb z*BGFK`9M-tpWzayiD>p_`U(29f$R|V-qEB;+_4T939BPb=XRw~8n2cGiRi`o$2qm~ zN&5N7JU{L*QGM@lO8VI)fUA0D7bPrhV(GjJ$+@=dcE5vAVyCy6r&R#4D=GyoEVOnu z8``8q`PN-pEy>xiA_@+EN?EJpY<#}BhrsUJC0afQFx7-pBeLXR9Mr+#w@!wSNR7vxHy@r`!9MFecB4O zh9jye3iSzL0@t3)OZ=OxFjjyK#KSF|zz@K}-+HaY6gW+O{T6%Zky@gD$6SW)Jq;V0 zt&LAG*YFO^+=ULohZZW*=3>7YgND-!$2}2)Mt~c>JO3j6QiPC-*ayH2xBF)2m7+}# z`@m#q{J9r~Dr^eBgrF(l^#sOjlVNFgDs5NR*Xp;V*wr~HqBx7?qBUZ8w)%vIbhhe) zt4(#1S~c$Cq7b_A%wpuah1Qn(X9#obljoY)VUoK%OiQZ#Fa|@ZvGD0_oxR=vz{>U* znC(W7HaUDTc5F!T77GswL-jj7e0#83DH2+lS-T@_^SaWfROz9btt*5zDGck${}*njAwf}3hLqKGLTeV&5(8FC+IP>s;p{L@a~RyCu)MIa zs~vA?_JQ1^2Xc&^cjDq02tT_Z0gkElR0Aa$v@VHi+5*)1(@&}gEXxP5Xon?lxE@is z9sxd|h#w2&P5uHJxWgmtVZJv5w>cl2ALzri;r57qg){6`urTu(2}EI?D?##g=!Sbh z*L*>c9xN1a3CH$u7C~u_!g81`W|xp=54oZl9CM)&V9~ATCC-Q!yfKD@vp#2EKh0(S zgt~aJ^oq-TM0IBol!w1S2j7tJ8H7;SR7yn4-H}iz&U^*zW95HrHiT!H&E|rSlnCYr z7Y1|V7xebn=TFbkH;>WIH6H>8;0?HS#b6lCke9rSsH%3AM1#2U-^*NVhXEIDSFtE^ z=jOo1>j!c__Bub(R*dHyGa)@3h?!ls1&M)d2{?W5#1|M@6|ENYYa`X=2EA_oJUw=I zjQ)K6;C!@>^i7vdf`pBOjH>Ts$97}B=lkb07<&;&?f#cy3I0p5{1=?O*#8m$C_5TE zh}&8lOWWF7I@|pRC$G2;Sm#IJfhKW@^jk=jfM1MdJP(v2fIrYTc{;e5;5gsp`}X8-!{9{S1{h+)<@?+D13s^B zq9(1Pu(Dfl#&z|~qJGuGSWDT&u{sq|huEsbJhiqMUae}K*g+R(vG7P$p6g}w*eYWn zQ7luPl1@{vX?PMK%-IBt+N7TMn~GB z!Ldy^(2Mp{fw_0;<$dgHAv1gZgyJAx%}dA?jR=NPW1K`FkoY zNDgag#YWI6-a2#&_E9NMIE~gQ+*)i<>0c)dSRUMHpg!+AL;a;^u|M1jp#0b<+#14z z+#LuQ1jCyV_GNj#lHWG3e9P@H34~n0VgP#(SBX=v|RSuOiY>L87 z#KA{JDDj2EOBX^{`a;xQxHtY1?q5^B5?up1akjEPhi1-KUsK|J9XEBAbt%^F`t0I- zjRYYKI4OB7Zq3FqJFBZwbI=RuT~J|4tA8x)(v2yB^^+TYYJS>Et`_&yge##PuQ%0I z^|X!Vtof}`UuIxPjoH8kofw4u1pT5h`Ip}d8;l>WcG^qTe>@x63s#zoJiGmDM@_h= zo;8IZR`@AJRLnBNtatipUvL^(1P_a;q8P%&voqy#R!0(bNBTlV&*W9QU?kRV1B*~I zWvI?SNo2cB<7bgVY{F_CF$7z!02Qxfw-Ew#p!8PC#! z1sRfOl`d-Y@&=)l(Sl4CS=>fVvor5lYm61C!!iF3NMocKQHUYr0%QM}a4v2>rzPfM zUO}YRDb7-NEqW+p_;e0{Zi%0C$&B3CKx6|4BW`@`AwsxE?Vu}@Jm<3%T5O&05z+Yq zkK!QF(vlN}Rm}m_J+*W4`8i~R&`P0&5!;^@S#>7qkfb9wxFv@(wN@$k%2*sEwen$a zQnWymf+#Uyv)0lQVd?L1gpS}jMQZ(NHHCKRyu zjK|Zai0|N_)5iv)67(zDBCK4Ktm#ygP|0(m5tU`*AzR&{TSeSY8W=v5^=Ic`ahxM-LBWO+uoL~wxZmgcSJMUF9q%<%>jsvh9Dnp^_e>J_V=ySx4p?SF0Y zg4ZpZt@!h>WR76~P3_YchYOak7oOzR|`t+h!BbN}?zd zq+vMTt0!duALNWDwWVIA$O=%{lWJEj;5(QD()huhFL5=6x_=1h|5ESMW&S|*oxgF# z-0GRIb ziolwI13hJ-Rl(4Rj@*^=&Zz3vD$RX8bFWvBM{niz(%?z0gWNh_vUvpBDoa>-N=P4c zbw-XEJ@txIbc<`wC883;&yE4ayVh>+N($SJ01m}fumz!#!aOg*;y4Hl{V{b;&ux3& zBEmSq2jQ7#IbVm3TPBw?2vVN z0wzj|Y6EBS(V%Pb+@OPkMvEKHW~%DZk#u|A18pZMmCrjWh%7J4Ph>vG61 zRBgJ6w^8dNRg2*=K$Wvh$t>$Q^SMaIX*UpBG)0bqcvY%*by=$EfZAy{ZOA#^tB(D( zh}T(SZgdTj?bG9u+G{Avs5Yr1x=f3k7%K|eJp^>BHK#~dsG<&+=`mM@>kQ-cAJ2k) zT+Ht5liXdc^(aMi9su~{pJUhe)!^U&qn%mV6PS%lye+Iw5F@Xv8E zdR4#?iz+R4--iiHDQmQWfNre=iofAbF~1oGTa1Ce?hId~W^kPuN(5vhNx++ZLkn?l zUA7L~{0x|qA%%%P=8+-Ck{&2$UHn#OQncFS@uUVuE39c9o~#hl)v#!$X(X*4ban2c z{buYr9!`H2;6n73n^W3Vg(!gdBV7$e#v3qubWALaUEAf@`ava{UTx%2~VVQbEE(*Q8_ zv#me9i+0=QnY)$IT+@3vP1l9Wrne+MlZNGO6|zUVG+v&lm7Xw3P*+gS6e#6mVx~(w zyuaXogGTw4!!&P3oZ1|4oc_sGEa&m3Jsqy^lzUdJ^y8RlvUjDmbC^NZ0AmO-c*&m( zSI%4P9f|s!B#073b>Eet`T@J;3qY!NrABuUaED6M^=s-Q^2oZS`jVzuA z>g&g$!Tc>`u-Q9PmKu0SLu-X(tZeZ<%7F+$j3qOOftaoXO5=4!+P!%Cx0rNU+@E~{ zxCclYb~G(Ci%o{}4PC(Bu>TyX9slm5A^2Yi$$kCq-M#Jl)a2W9L-bq5%@Pw^ zh*iuuAz`x6N_rJ1LZ7J^MU9~}RYh+EVIVP+-62u+7IC%1p@;xmmQ`dGCx$QpnIUtK z0`++;Ddz7{_R^~KDh%_yo8WM$IQhcNOALCIGC$3_PtUs?Y44@Osw;OZ()Lk=(H&Vc zXjkHt+^1@M|J%Q&?4>;%T-i%#h|Tb1u;pO5rKst8(Cv2!3U{TRXdm&>fWTJG)n*q&wQPjRzg%pS1RO9}U0*C6fhUi&f#qoV`1{U<&mWKS<$oVFW>{&*$6)r6Rx)F4W zdUL8Mm_qNk6ycFVkI5F?V+cYFUch$92|8O^-Z1JC94GU+Nuk zA#n3Z1q4<6zRiv%W5`NGk*Ym{#0E~IA6*)H-=RmfWIY%mEC0? zSih7uchi`9-WkF2@z1ev6J_N~u;d$QfSNLMgPVpHZoh9oH-8D*;EhoCr~*kJ<|-VD z_jklPveOxWZq40E!SV@0XXy+~Vfn!7nZ1GXsn~U$>#u0d*f?RL9!NMlz^qxYmz|xt zz6A&MUAV#eD%^GcP#@5}QH5e7AV`}(N2#(3xpc!7dDmgu7C3TpgX5Z|$%Vu8=&SQI zdxUk*XS-#C^-cM*O>k}WD5K81e2ayyRA)R&5>KT1QL!T!%@}fw{>BsF+-pzu>;7{g z^CCSWfH;YtJGT@+An0Ded#zM9>UEFOdR_Xq zS~!5R*{p1Whq62ynHo|n$4p7&d|bal{iGsxAY?opi3R${)Zt*8YyOU!$TWMYXF?|i zPXYr}wJp#EH;keSG5WYJ*(~oiu#GDR>C4%-HpIWr7v`W`lzQN-lb?*vpoit z8FqJ)`LC4w8fO8Fu}AYV`awF2NLMS4$f+?=KisU4P6@#+_t)5WDz@f*qE|NG0*hwO z&gv^k^kC6Fg;5>Gr`Q46C{6>3F(p0QukG6NM07rxa&?)_C*eyU(jtli>9Zh#eUb(y zt9NbC-bp0>^m?i`?$aJUyBmF`N0zQ% zvF_;vLVI{tq%Ji%u*8s2p4iBirv*uD(?t~PEz$CfxVa=@R z^HQu6-+I9w>a35kX!P)TfnJDD!)j8!%38(vWNe9vK0{k*`FS$ABZ`rdwfQe@IGDki zssfXnsa6teKXCZUTd^qhhhUZ}>GG_>F0~LG7*<*x;8e39nb-0Bka(l)%+QZ_IVy3q zcmm2uKO0p)9|HGxk*e_$mX2?->&-MXe`=Fz3FRTFfM!$_y}G?{F9jmNgD+L%R`jM1 zIP-kb=3Hlsb35Q&qo(%Ja(LwQj>~!GI|Hgq65J9^A!ibChYB3kxLn@&=#pr}BwON0Q=e5;#sF8GGGuzx6O}z%u3l?jlKF&8Y#lUA)Cs6ZiW8DgOk|q z=YBPAMsO7AoAhWgnSKae2I7%7*Xk>#AyLX-InyBO?OD_^2^nI4#;G|tBvg3C0ldO0 z*`$g(q^es4VqXH2t~0-u^m5cfK8eECh3Rb2h1kW%%^8A!+ya3OHLw$8kHorx4(vJO zAlVu$nC>D{7i?7xDg3116Y2e+)Zb4FPAdZaX}qA!WW{$d?u+sK(iIKqOE-YM zH7y^hkny24==(1;qEacfFU{W{xSXhffC&DJV&oqw`u~WAl@=HIel>KC-mLs2ggFld zsSm-03=Jd^XNDA4i$vKqJ|e|TBc19bglw{)QL${Q(xlN?E;lPumO~;4w_McND6d+R zsc2p*&uRWd`wTDszTcWKiii1mNBrF7n&LQp$2Z<}zkv=8k2s6-^+#siy_K1`5R+n( z++5VOU^LDo(kt3ok?@$3drI`<%+SWcF*`CUWqAJxl3PAq!X|q{al;8%HfgxxM#2Vb zeBS756iU|BzB>bN2NP=AX&!{uZXS;|F`LLd9F^97UTMnNks_t7EPnjZF`2ocD2*u+ z?oKP{xXrD*AKGYGkZtlnvCuazg6g16ZAF{Nu%w+LCZ+v_*`0R$NK)tOh_c#cze;o$ z)kY(eZ5Viv<5zl1XfL(#GO|2FlXL#w3T?hpj3BZ&OAl^L!7@ zy;+iJWYQYP?$(`li_!|bfn!h~k#=v-#XXyjTLd+_txOqZZETqSEp>m+O0ji7MxZ*W zSdq+yqEmafrsLErZG8&;kH2kbCwluSa<@1yU3^Q#5HmW(hYVR0E6!4ZvH;Cr<$`qf zSvqRc`Pq_9b+xrtN3qLmds9;d7HdtlR!2NV$rZPCh6>(7f7M}>C^LeM_5^b$B~mn| z#)?`E=zeo9(9?{O_ko>51~h|c?8{F=2=_-o(-eRc z9p)o51krhCmff^U2oUi#$AG2p-*wSq8DZ(i!Jmu1wzD*)#%J&r)yZTq`3e|v4>EI- z=c|^$Qhv}lEyG@!{G~@}Wbx~vxTxwKoe9zn%5_Z^H$F1?JG_Kadc(G8#|@yaf2-4< zM1bdQF$b5R!W1f`j(S>Id;CHMzfpyjYEC_95VQ*$U3y5piVy=9Rdwg7g&)%#6;U%b2W}_VVdh}qPnM4FY9zFP(5eR zWuCEFox6e;COjs$1RV}IbpE0EV;}5IP}Oq|zcb*77PEDIZU{;@_;8*22{~JRvG~1t zc+ln^I+)Q*+Ha>(@=ra&L&a-kD;l$WEN;YL0q^GE8+})U_A_StHjX_gO{)N>tx4&F zRK?99!6JqktfeS-IsD@74yuq*aFJoV{5&K(W`6Oa2Qy0O5JG>O`zZ-p7vBGh!MxS;}}h6(96Wp`dci3DY?|B@1p8fVsDf$|0S zfE{WL5g3<9&{~yygYyR?jK!>;eZ2L#tpL2)H#89*b zycE?VViXbH7M}m33{#tI69PUPD=r)EVPTBku={Qh{ zKi*pht1jJ+yRhVE)1=Y()iS9j`FesMo$bjLSqPMF-i<42Hxl6%y7{#vw5YT(C}x0? z$rJU7fFmoiR&%b|Y*pG?7O&+Jb#Z%S8&%o~fc?S9c`Dwdnc4BJC7njo7?3bp#Yonz zPC>y`DVK~nzN^n}jB5RhE4N>LzhCZD#WQseohYXvqp5^%Ns!q^B z&8zQN(jgPS(2ty~g2t9!x9;Dao~lYVujG-QEq{vZp<1Nlp;oj#kFVsBnJssU^p-4% zKF_A?5sRmA>d*~^og-I95z$>T*K*33TGBPzs{OMoV2i+(P6K|95UwSj$Zn<@Rt(g%|iY z$SkSjYVJ)I<@S(kMQ6md{HxAa8S`^lXGV?ktLX!ngTVI~%WW+p#A#XTWaFWeBAl%U z&rVhve#Yse*h4BC4nrq7A1n>Rlf^ErbOceJC`o#fyCu@H;y)`E#a#)w)3eg^{Hw&E7);N5*6V+z%olvLj zp^aJ4`h*4L4ij)K+uYvdpil(Z{EO@u{BcMI&}5{ephilI%zCkBhBMCvOQT#zp|!18 zuNl=idd81|{FpGkt%ty=$fnZnWXxem!t4x{ zat@68CPmac(xYaOIeF}@O1j8O?2jbR!KkMSuix;L8x?m01}|bS2=&gsjg^t2O|+0{ zlzfu5r5_l4)py8uPb5~NHPG>!lYVynw;;T-gk1Pl6PQ39Mwgd2O+iHDB397H)2grN zHwbd>8i%GY>Pfy7;y5X7AN>qGLZVH>N_ZuJZ-`z9UA> zfyb$nbmPqxyF2F;UW}7`Cu>SS%0W6h^Wq5e{PWAjxlh=#Fq+6SiPa-L*551SZKX&w zc9TkPv4eao?kqomkZ#X%tA{`UIvf|_=Y7p~mHZKqO>i_;q4PrwVtUDTk?M7NCssa?Y4uxYrsXj!+k@`Cxl;&{NLs*6!R<6k9$Bq z%grLhxJ#G_j~ytJpiND8neLfvD0+xu>wa$-%5v;4;RYYM66PUab)c9ruUm%d{^s{# zTBBY??@^foRv9H}iEf{w_J%rV<%T1wv^`)Jm#snLTIifjgRkX``x2wV(D6(=VTLL4 zI-o}&5WuwBl~(XSLIn5~{cGWorl#z+=(vXuBXC#lp}SdW=_)~8Z(Vv!#3h2@pdA3d z{cIPYK@Ojc9(ph=H3T7;aY>(S3~iuIn05Puh^32WObj%hVN(Y{Ty?n?Cm#!kGNZFa zW6Ybz!tq|@erhtMo4xAus|H8V_c+XfE5mu|lYe|{$V3mKnb1~fqoFim;&_ZHN_=?t zysQwC4qO}rTi}k8_f=R&i27RdBB)@bTeV9Wcd}Rysvod}7I%ujwYbTI*cN7Kbp_hO z=eU521!#cx$0O@k9b$;pnCTRtLIzv){nVW6Ux1<0@te6`S5%Ew3{Z^9=lbL5$NFvd4eUtK?%zgmB;_I&p`)YtpN`2Im(?jPN<(7Ua_ZWJRF(CChv`(gHfWodK%+joy>8Vaa;H1w zIJ?!kA|x7V;4U1BNr(UrhfvjPii7YENLIm`LtnL9Sx z5E9TYaILoB2nSwDe|BVmrpLT43*dJ8;T@1l zJE)4LEzIE{IN}+Nvpo3=ZtV!U#D;rB@9OXYw^4QH+(52&pQEcZq&~u9bTg63ikW9! z=!_RjN2xO=F+bk>fSPhsjQA;)%M1My#34T`I7tUf>Q_L>DRa=>Eo(sapm>}}LUsN% zVw!C~a)xcca`G#g*Xqo>_uCJTz>LoWGSKOwp-tv`yvfqw{17t`9Z}U4o+q2JGP^&9 z(m}|d13XhYSnEm$_8vH-Lq$A^>oWUz1)bnv|AVn_0FwM$vYu&8+qUg$+qP}nwrykD zwmIF?wr$()X@33oz1@B9zi+?Th^nZnsES)rb@O*K^JL~ZH|pRRk$i0+ohh?Il)y&~ zQaq{}9YxPt5~_2|+r#{k#~SUhO6yFq)uBGtYMMg4h1qddg!`TGHocYROyNFJtYjNe z3oezNpq6%TP5V1g(?^5DMeKV|i6vdBq)aGJ)BRv;K(EL0_q7$h@s?BV$)w31*c(jd z{@hDGl3QdXxS=#?0y3KmPd4JL(q(>0ikTk6nt98ptq$6_M|qrPi)N>HY>wKFbnCKY z%0`~`9p)MDESQJ#A`_>@iL7qOCmCJ(p^>f+zqaMuDRk!z01Nd2A_W^D%~M73jTqC* zKu8u$$r({vP~TE8rPk?8RSjlRvG*BLF}ye~Su%s~rivmjg2F z24dhh6-1EQF(c>Z1E8DWY)Jw#9U#wR<@6J)3hjA&2qN$X%piJ4s={|>d-|Gzl~RNu z##iR(m;9TN3|zh+>HgTI&82iR>$YVoOq$a(2%l*2mNP(AsV=lR^>=tIP-R9Tw!BYnZROx`PN*JiNH>8bG}&@h0_v$yOTk#@1;Mh;-={ZU7e@JE(~@@y0AuETvsqQV@7hbKe2wiWk@QvV=Kz`%@$rN z_0Hadkl?7oEdp5eaaMqBm;#Xj^`fxNO^GQ9S3|Fb#%{lN;1b`~yxLGEcy8~!cz{!! z=7tS!I)Qq%w(t9sTSMWNhoV#f=l5+a{a=}--?S!rA0w}QF!_Eq>V4NbmYKV&^OndM z4WiLbqeC5+P@g_!_rs01AY6HwF7)$~%Ok^(NPD9I@fn5I?f$(rcOQjP+z?_|V0DiN zb}l0fy*el9E3Q7fVRKw$EIlb&T0fG~fDJZL7Qn8*a5{)vUblM)*)NTLf1ll$ zpQ^(0pkSTol`|t~`Y4wzl;%NRn>689mpQrW=SJ*rB;7}w zVHB?&sVa2%-q@ANA~v)FXb`?Nz8M1rHKiZB4xC9<{Q3T!XaS#fEk=sXI4IFMnlRqG+yaFw< zF{}7tcMjV04!-_FFD8(FtuOZx+|CjF@-xl6-{qSFF!r7L3yD()=*Ss6fT?lDhy(h$ zt#%F575$U(3-e2LsJd>ksuUZZ%=c}2dWvu8f!V%>z3gajZ!Dlk zm=0|(wKY`c?r$|pX6XVo6padb9{EH}px)jIsdHoqG^(XH(7}r^bRa8BC(%M+wtcB? z6G2%tui|Tx6C3*#RFgNZi9emm*v~txI}~xV4C`Ns)qEoczZ>j*r zqQCa5k90Gntl?EX!{iWh=1t$~jVoXjs&*jKu0Ay`^k)hC^v_y0xU~brMZ6PPcmt5$ z@_h`f#qnI$6BD(`#IR0PrITIV^~O{uo=)+Bi$oHA$G* zH0a^PRoeYD3jU_k%!rTFh)v#@cq`P3_y=6D(M~GBud;4 zCk$LuxPgJ5=8OEDlnU!R^4QDM4jGni}~C zy;t2E%Qy;A^bz_5HSb5pq{x{g59U!ReE?6ULOw58DJcJy;H?g*ofr(X7+8wF;*3{rx>j&27Syl6A~{|w{pHb zeFgu0E>OC81~6a9(2F13r7NZDGdQxR8T68&t`-BK zE>ZV0*0Ba9HkF_(AwfAds-r=|dA&p`G&B_zn5f9Zfrz9n#Rvso`x%u~SwE4SzYj!G zVQ0@jrLwbYP=awX$21Aq!I%M{x?|C`narFWhp4n;=>Sj!0_J!k7|A0;N4!+z%Oqlk z1>l=MHhw3bi1vT}1!}zR=6JOIYSm==qEN#7_fVsht?7SFCj=*2+Ro}B4}HR=D%%)F z?eHy=I#Qx(vvx)@Fc3?MT_@D))w@oOCRR5zRw7614#?(-nC?RH`r(bb{Zzn+VV0bm zJ93!(bfrDH;^p=IZkCH73f*GR8nDKoBo|!}($3^s*hV$c45Zu>6QCV(JhBW=3(Tpf z=4PT6@|s1Uz+U=zJXil3K(N6;ePhAJhCIo`%XDJYW@x#7Za);~`ANTvi$N4(Fy!K- z?CQ3KeEK64F0@ykv$-0oWCWhYI-5ZC1pDqui@B|+LVJmU`WJ=&C|{I_))TlREOc4* zSd%N=pJ_5$G5d^3XK+yj2UZasg2) zXMLtMp<5XWWfh-o@ywb*nCnGdK{&S{YI54Wh2|h}yZ})+NCM;~i9H@1GMCgYf`d5n zwOR(*EEkE4-V#R2+Rc>@cAEho+GAS2L!tzisLl${42Y=A7v}h;#@71_Gh2MV=hPr0_a% z0!={Fcv5^GwuEU^5rD|sP;+y<%5o9;#m>ssbtVR2g<420(I-@fSqfBVMv z?`>61-^q;M(b3r2z{=QxSjyH=-%99fpvb}8z}d;%_8$$J$qJg1Sp3KzlO_!nCn|g8 zzg8skdHNsfgkf8A7PWs;YBz_S$S%!hWQ@G>guCgS--P!!Ui9#%GQ#Jh?s!U-4)7ozR?i>JXHU$| zg0^vuti{!=N|kWorZNFX`dJgdphgic#(8sOBHQdBkY}Qzp3V%T{DFb{nGPgS;QwnH9B9;-Xhy{? z(QVwtzkn9I)vHEmjY!T3ifk1l5B?%%TgP#;CqG-?16lTz;S_mHOzu#MY0w}XuF{lk z*dt`2?&plYn(B>FFXo+fd&CS3q^hquSLVEn6TMAZ6e*WC{Q2e&U7l|)*W;^4l~|Q= zt+yFlLVqPz!I40}NHv zE2t1meCuGH%<`5iJ(~8ji#VD{?uhP%F(TnG#uRZW-V}1=N%ev&+Gd4v!0(f`2Ar-Y z)GO6eYj7S{T_vxV?5^%l6TF{ygS_9e2DXT>9caP~xq*~oE<5KkngGtsv)sdCC zaQH#kSL%c*gLj6tV)zE6SGq|0iX*DPV|I`byc9kn_tNQkPU%y<`rj zMC}lD<93=Oj+D6Y2GNMZb|m$^)RVdi`&0*}mxNy0BW#0iq!GGN2BGx5I0LS>I|4op z(6^xWULBr=QRpbxIJDK~?h;K#>LwQI4N<8V?%3>9I5l+e*yG zFOZTIM0c3(q?y9f7qDHKX|%zsUF%2zN9jDa7%AK*qrI5@z~IruFP+IJy7!s~TE%V3 z_PSSxXlr!FU|Za>G_JL>DD3KVZ7u&}6VWbwWmSg?5;MabycEB)JT(eK8wg`^wvw!Q zH5h24_E$2cuib&9>Ue&@%Cly}6YZN-oO_ei5#33VvqV%L*~ZehqMe;)m;$9)$HBsM zfJ96Hk8GJyWwQ0$iiGjwhxGgQX$sN8ij%XJzW`pxqgwW=79hgMOMnC|0Q@ed%Y~=_ z?OnjUB|5rS+R$Q-p)vvM(eFS+Qr{_w$?#Y;0Iknw3u(+wA=2?gPyl~NyYa3me{-Su zhH#8;01jEm%r#5g5oy-f&F>VA5TE_9=a0aO4!|gJpu470WIrfGo~v}HkF91m6qEG2 zK4j=7C?wWUMG$kYbIp^+@)<#ArZ$3k^EQxraLk0qav9TynuE7T79%MsBxl3|nRn?L zD&8kt6*RJB6*a7=5c57wp!pg)p6O?WHQarI{o9@3a32zQ3FH8cK@P!DZ?CPN_LtmC6U4F zlv8T2?sau&+(i@EL6+tvP^&=|aq3@QgL4 zOu6S3wSWeYtgCnKqg*H4ifIQlR4hd^n{F+3>h3;u_q~qw-Sh;4dYtp^VYymX12$`? z;V2_NiRt82RC=yC+aG?=t&a81!gso$hQUb)LM2D4Z{)S zI1S9f020mSm(Dn$&Rlj0UX}H@ zv={G+fFC>Sad0~8yB%62V(NB4Z|b%6%Co8j!>D(VyAvjFBP%gB+`b*&KnJ zU8s}&F+?iFKE(AT913mq;57|)q?ZrA&8YD3Hw*$yhkm;p5G6PNiO3VdFlnH-&U#JH zEX+y>hB(4$R<6k|pt0?$?8l@zeWk&1Y5tlbgs3540F>A@@rfvY;KdnVncEh@N6Mfi zY)8tFRY~Z?Qw!{@{sE~vQy)0&fKsJpj?yR`Yj+H5SDO1PBId3~d!yjh>FcI#Ug|^M z7-%>aeyQhL8Zmj1!O0D7A2pZE-$>+-6m<#`QX8(n)Fg>}l404xFmPR~at%$(h$hYD zoTzbxo`O{S{E}s8Mv6WviXMP}(YPZoL11xfd>bggPx;#&pFd;*#Yx%TtN1cp)MuHf z+Z*5CG_AFPwk624V9@&aL0;=@Ql=2h6aJoqWx|hPQQzdF{e7|fe(m){0==hk_!$ou zI|p_?kzdO9&d^GBS1u+$>JE-6Ov*o{mu@MF-?$r9V>i%;>>Fo~U`ac2hD*X}-gx*v z1&;@ey`rA0qNcD9-5;3_K&jg|qvn@m^+t?8(GTF0l#|({Zwp^5Ywik@bW9mN+5`MU zJ#_Ju|jtsq{tv)xA zY$5SnHgHj}c%qlQG72VS_(OSv;H~1GLUAegygT3T-J{<#h}))pk$FjfRQ+Kr%`2ZiI)@$96Nivh82#K@t>ze^H?R8wHii6Pxy z0o#T(lh=V>ZD6EXf0U}sG~nQ1dFI`bx;vivBkYSVkxXn?yx1aGxbUiNBawMGad;6? zm{zp?xqAoogt=I2H0g@826=7z^DmTTLB11byYvAO;ir|O0xmNN3Ec0w%yHO({-%q(go%?_X{LP?=E1uXoQgrEGOfL1?~ zI%uPHC23dn-RC@UPs;mxq6cFr{UrgG@e3ONEL^SoxFm%kE^LBhe_D6+Ia+u0J=)BC zf8FB!0J$dYg33jb2SxfmkB|8qeN&De!%r5|@H@GiqReK(YEpnXC;-v~*o<#JmYuze zW}p-K=9?0=*fZyYTE7A}?QR6}m_vMPK!r~y*6%My)d;x4R?-=~MMLC_02KejX9q6= z4sUB4AD0+H4ulSYz4;6mL8uaD07eXFvpy*i5X@dmx--+9`ur@rcJ5<L#s%nq3MRi4Dpr;#28}dl36M{MkVs4+Fm3Pjo5qSV)h}i(2^$Ty|<7N z>*LiBzFKH30D!$@n^3B@HYI_V1?yM(G$2Ml{oZ}?frfPU+{i|dHQOP^M0N2#NN_$+ zs*E=MXUOd=$Z2F4jSA^XIW=?KN=w6{_vJ4f(ZYhLxvFtPozPJv9k%7+z!Zj+_0|HC zMU0(8`8c`Sa=%e$|Mu2+CT22Ifbac@7Vn*he`|6Bl81j`44IRcTu8aw_Y%;I$Hnyd zdWz~I!tkWuGZx4Yjof(?jM;exFlUsrj5qO=@2F;56&^gM9D^ZUQ!6TMMUw19zslEu zwB^^D&nG96Y+Qwbvgk?Zmkn9%d{+V;DGKmBE(yBWX6H#wbaAm&O1U^ zS4YS7j2!1LDC6|>cfdQa`}_^satOz6vc$BfFIG07LoU^IhVMS_u+N=|QCJao0{F>p z-^UkM)ODJW9#9*o;?LPCRV1y~k9B`&U)jbTdvuxG&2%!n_Z&udT=0mb@e;tZ$_l3bj6d0K2;Ya!&)q`A${SmdG_*4WfjubB)Mn+vaLV+)L5$yD zYSTGxpVok&fJDG9iS8#oMN{vQneO|W{Y_xL2Hhb%YhQJgq7j~X7?bcA|B||C?R=Eo z!z;=sSeKiw4mM$Qm>|aIP3nw36Tbh6Eml?hL#&PlR5xf9^vQGN6J8op1dpLfwFg}p zlqYx$610Zf?=vCbB_^~~(e4IMic7C}X(L6~AjDp^;|=d$`=!gd%iwCi5E9<6Y~z0! zX8p$qprEadiMgq>gZ_V~n$d~YUqqqsL#BE6t9ufXIUrs@DCTfGg^-Yh5Ms(wD1xAf zTX8g52V!jr9TlWLl+whcUDv?Rc~JmYs3haeG*UnV;4bI=;__i?OSk)bF3=c9;qTdP zeW1exJwD+;Q3yAw9j_42Zj9nuvs%qGF=6I@($2Ue(a9QGRMZTd4ZAlxbT5W~7(alP1u<^YY!c3B7QV z@jm$vn34XnA6Gh1I)NBgTmgmR=O1PKp#dT*mYDPRZ=}~X3B8}H*e_;;BHlr$FO}Eq zJ9oWk0y#h;N1~ho724x~d)A4Z-{V%F6#e5?Z^(`GGC}sYp5%DKnnB+i-NWxwL-CuF+^JWNl`t@VbXZ{K3#aIX+h9-{T*+t(b0BM&MymW9AA*{p^&-9 zWpWQ?*z(Yw!y%AoeoYS|E!(3IlLksr@?Z9Hqlig?Q4|cGe;0rg#FC}tXTmTNfpE}; z$sfUYEG@hLHUb$(K{A{R%~%6MQN|Bu949`f#H6YC*E(p3lBBKcx z-~Bsd6^QsKzB0)$FteBf*b3i7CN4hccSa-&lfQz4qHm>eC|_X!_E#?=`M(bZ{$cvU zZpMbr|4omp`s9mrgz@>4=Fk3~8Y7q$G{T@?oE0<(I91_t+U}xYlT{c&6}zPAE8ikT z3DP!l#>}i!A(eGT+@;fWdK#(~CTkwjs?*i4SJVBuNB2$6!bCRmcm6AnpHHvnN8G<| zuh4YCYC%5}Zo;BO1>L0hQ8p>}tRVx~O89!${_NXhT!HUoGj0}bLvL2)qRNt|g*q~B z7U&U7E+8Ixy1U`QT^&W@ZSRN|`_Ko$-Mk^^c%`YzhF(KY9l5))1jSyz$&>mWJHZzHt0Jje%BQFxEV}C00{|qo5_Hz7c!FlJ|T(JD^0*yjkDm zL}4S%JU(mBV|3G2jVWU>DX413;d+h0C3{g3v|U8cUj`tZL37Sf@1d*jpwt4^B)`bK zZdlwnPB6jfc7rIKsldW81$C$a9BukX%=V}yPnaBz|i6(h>S)+Bn44@i8RtBZf0XetH&kAb?iAL zD%Ge{>Jo3sy2hgrD?15PM}X_)(6$LV`&t*D`IP)m}bzM)+x-xRJ zavhA)>hu2cD;LUTvN38FEtB94ee|~lIvk~3MBPzmTsN|7V}Kzi!h&za#NyY zX^0BnB+lfBuW!oR#8G&S#Er2bCVtA@5FI`Q+a-e?G)LhzW_chWN-ZQmjtR

eWu-UOPu^G}|k=o=;ffg>8|Z*qev7qS&oqA7%Z{4Ezb!t$f3& z^NuT8CSNp`VHScyikB1YO{BgaBVJR&>dNIEEBwYkfOkWN;(I8CJ|vIfD}STN z{097)R9iC@6($s$#dsb*4BXBx7 zb{6S2O}QUk>upEfij9C2tjqWy7%%V@Xfpe)vo6}PG+hmuY1Tc}peynUJLLmm)8pshG zb}HWl^|sOPtYk)CD-7{L+l(=F zOp}fX8)|n{JDa&9uI!*@jh^^9qP&SbZ(xxDhR)y|bjnn|K3MeR3gl6xcvh9uqzb#K zYkVjnK$;lUky~??mcqN-)d5~mk{wXhrf^<)!Jjqc zG~hX0P_@KvOKwV=X9H&KR3GnP3U)DfqafBt$e10}iuVRFBXx@uBQ)sn0J%%c<;R+! zQz;ETTVa+ma>+VF%U43w?_F6s0=x@N2(oisjA7LUOM<$|6iE|$WcO67W|KY8JUV_# zg7P9K3Yo-c*;EmbsqT!M4(WT`%9uk+s9Em-yB0bE{B%F4X<8fT!%4??vezaJ(wJhj zfOb%wKfkY3RU}7^FRq`UEbB-#A-%7)NJQwQd1As=!$u#~2vQ*CE~qp`u=_kL<`{OL zk>753UqJVx1-4~+d@(pnX-i zV4&=eRWbJ)9YEGMV53poXpv$vd@^yd05z$$@i5J7%>gYKBx?mR2qGv&BPn!tE-_aW zg*C!Z&!B zH>3J16dTJC(@M0*kIc}Jn}jf=f*agba|!HVm|^@+7A?V>Woo!$SJko*Jv1mu>;d}z z^vF{3u5Mvo_94`4kq2&R2`32oyoWc2lJco3`Ls0Ew4E7*AdiMbn^LCV%7%mU)hr4S3UVJjDLUoIKRQ)gm?^{1Z}OYzd$1?a~tEY ztjXmIM*2_qC|OC{7V%430T?RsY?ZLN$w!bkDOQ0}wiq69){Kdu3SqW?NMC))S}zq^ zu)w!>E1!;OrXO!RmT?m&PA;YKUjJy5-Seu=@o;m4*Vp$0OipBl4~Ub)1xBdWkZ47=UkJd$`Z}O8ZbpGN$i_WtY^00`S8=EHG#Ff{&MU1L(^wYjTchB zMTK%1LZ(eLLP($0UR2JVLaL|C2~IFbWirNjp|^=Fl48~Sp9zNOCZ@t&;;^avfN(NpNfq}~VYA{q%yjHo4D>JB>XEv(~Z!`1~SoY=9v zTq;hrjObE_h)cmHXLJ>LC_&XQ2BgGfV}e#v}ZF}iF97bG`Nog&O+SA`2zsn%bbB309}I$ zYi;vW$k@fC^muYBL?XB#CBuhC&^H)F4E&vw(5Q^PF{7~}(b&lF4^%DQzL0(BVk?lM zTHXTo4?Ps|dRICEiux#y77_RF8?5!1D-*h5UY&gRY`WO|V`xxB{f{DHzBwvt1W==r zdfAUyd({^*>Y7lObr;_fO zxDDw7X^dO`n!PLqHZ`by0h#BJ-@bAFPs{yJQ~Ylj^M5zWsxO_WFHG}8hH>OK{Q)9` zSRP94d{AM(q-2x0yhK@aNMv!qGA5@~2tB;X?l{Pf?DM5Y*QK`{mGA? zjx;gwnR~#Nep12dFk<^@-U{`&`P1Z}Z3T2~m8^J&7y}GaMElsTXg|GqfF3>E#HG=j zMt;6hfbfjHSQ&pN9(AT8q$FLKXo`N(WNHDY!K6;JrHZCO&ISBdX`g8sXvIf?|8 zX$-W^ut!FhBxY|+R49o44IgWHt}$1BuE|6|kvn1OR#zhyrw}4H*~cpmFk%K(CTGYc zNkJ8L$eS;UYDa=ZHWZy`rO`!w0oIcgZnK&xC|93#nHvfb^n1xgxf{$LB`H1ao+OGb zKG_}>N-RHSqL(RBdlc7J-Z$Gaay`wEGJ_u-lo88{`aQ*+T~+x(H5j?Q{uRA~>2R+} zB+{wM2m?$->unwg8-GaFrG%ZmoHEceOj{W21)Mi2lAfT)EQuNVo+Do%nHPuq7Ttt7 z%^6J5Yo64dH671tOUrA7I2hL@HKZq;S#Ejxt;*m-l*pPj?=i`=E~FAXAb#QH+a}-% z#3u^pFlg%p{hGiIp>05T$RiE*V7bPXtkz(G<+^E}Risi6F!R~Mbf(Qz*<@2&F#vDr zaL#!8!&ughWxjA(o9xtK{BzzYwm_z2t*c>2jI)c0-xo8ahnEqZ&K;8uF*!Hg0?Gd* z=eJK`FkAr>7$_i$;kq3Ks5NNJkNBnw|1f-&Ys56c9Y@tdM3VTTuXOCbWqye9va6+ZSeF0eh} zYb^ct&4lQTfNZ3M3(9?{;s><(zq%hza7zcxlZ+`F8J*>%4wq8s$cC6Z=F@ zhbvdv;n$%vEI$B~B)Q&LkTse!8Vt};7Szv2@YB!_Ztp@JA>rc(#R1`EZcIdE+JiI% zC2!hgYt+~@%xU?;ir+g92W`*j z3`@S;I6@2rO28zqj&SWO^CvA5MeNEhBF+8-U0O0Q1Co=I^WvPl%#}UFDMBVl z5iXV@d|`QTa$>iw;m$^}6JeuW zjr;{)S2TfK0Q%xgHvONSJb#NA|LOmg{U=k;R?&1tQbylMEY4<1*9mJh&(qo`G#9{X zYRs)#*PtEHnO;PV0G~6G`ca%tpKgb6<@)xc^SQY58lTo*S$*sv5w7bG+8YLKYU`8{ zNBVlvgaDu7icvyf;N&%42z2L4(rR<*Jd48X8Jnw zN>!R$%MZ@~Xu9jH?$2Se&I|ZcW>!26BJP?H7og0hT(S`nXh6{sR36O^7%v=31T+eL z)~BeC)15v>1m#(LN>OEwYFG?TE0_z)MrT%3SkMBBjvCd6!uD+03Jz#!s#Y~b1jf>S z&Rz5&8rbLj5!Y;(Hx|UY(2aw~W(8!3q3D}LRE%XX(@h5TnP@PhDoLVQx;6|r^+Bvs zaR55cR%Db9hZ<<|I%dDkone+8Sq7dqPOMnGoHk~-R*#a8w$c)`>4U`k+o?2|E>Sd4 zZ0ZVT{95pY$qKJ54K}3JB!(WcES>F+x56oJBRg))tMJ^#Qc(2rVcd5add=Us6vpBNkIg9b#ulk%!XBU zV^fH1uY(rGIAiFew|z#MM!qsVv%ZNb#why9%9In4Kj-hDYtMdirWLFzn~de!nnH(V zv0>I3;X#N)bo1$dFzqo(tzmvqNUKraAz~?)OSv42MeM!OYu;2VKn2-s7#fucX`|l~ zplxtG1Pgk#(;V=`P_PZ`MV{Bt4$a7;aLvG@KQo%E=;7ZO&Ws-r@XL+AhnPn>PAKc7 zQ_iQ4mXa-a4)QS>cJzt_j;AjuVCp8g^|dIV=DI0>v-f_|w5YWAX61lNBjZEZax3aV znher(j)f+a9_s8n#|u=kj0(unR1P-*L7`{F28xv054|#DMh}q=@rs@-fbyf(2+52L zN>hn3v!I~%jfOV=j(@xLOsl$Jv-+yR5{3pX)$rIdDarl7(C3)})P`QoHN|y<<2n;` zJ0UrF=Zv}d=F(Uj}~Yv9(@1pqUSRa5_bB*AvQ|Z-6YZ*N%p(U z<;Bpqr9iEBe^LFF!t{1UnRtaH-9=@p35fMQJ~1^&)(2D|^&z?m z855r&diVS6}jmt2)A7LZDiv;&Ys6@W5P{JHY!!n7W zvj3(2{1R9Y=TJ|{^2DK&be*ZaMiRHw>WVI^701fC) zAp1?8?oiU%Faj?Qhou6S^d11_7@tEK-XQ~%q!!7hha-Im^>NcRF7OH7s{IO7arZQ{ zE8n?2><7*!*lH}~usWPWZ}2&M+)VQo7C!AWJSQc>8g_r-P`N&uybK5)p$5_o;+58Q z-Ux2l<3i|hxqqur*qAfHq=)?GDchq}ShV#m6&w|mi~ar~`EO_S=fb~<}66U>5i7$H#m~wR;L~4yHL2R&;L*u7-SPdHxLS&Iy76q$2j#Pe)$WulRiCICG*t+ zeehM8`!{**KRL{Q{8WCEFLXu3+`-XF(b?c1Z~wg?c0lD!21y?NLq?O$STk3NzmrHM zsCgQS5I+nxDH0iyU;KKjzS24GJmG?{D`08|N-v+Egy92lBku)fnAM<}tELA_U`)xKYb=pq|hejMCT1-rg0Edt6(*E9l9WCKI1a=@c99swp2t6Tx zFHy`8Hb#iXS(8c>F~({`NV@F4w0lu5X;MH6I$&|h*qfx{~DJ*h5e|61t1QP}tZEIcjC%!Fa)omJTfpX%aI+OD*Y(l|xc0$1Zip;4rx; zV=qI!5tSuXG7h?jLR)pBEx!B15HCoVycD&Z2dlqN*MFQDb!|yi0j~JciNC!>){~ zQQgmZvc}0l$XB0VIWdg&ShDTbTkArryp3x)T8%ulR;Z?6APx{JZyUm=LC-ACkFm`6 z(x7zm5ULIU-xGi*V6x|eF~CN`PUM%`!4S;Uv_J>b#&OT9IT=jx5#nydC4=0htcDme zDUH*Hk-`Jsa>&Z<7zJ{K4AZE1BVW%zk&MZ^lHyj8mWmk|Pq8WwHROz0Kwj-AFqvR)H2gDN*6dzVk>R3@_CV zw3Z@6s^73xW)XY->AFwUlk^4Q=hXE;ckW=|RcZFchyOM0vqBW{2l*QR#v^SZNnT6j zZv|?ZO1-C_wLWVuYORQryj29JA; zS4BsxfVl@X!W{!2GkG9fL4}58Srv{$-GYngg>JuHz!7ZPQbfIQr4@6ZC4T$`;Vr@t zD#-uJ8A!kSM*gA&^6yWi|F}&59^*Rx{qn3z{(JYxrzg!X2b#uGd>&O0e=0k_2*N?3 zYXV{v={ONL{rW~z_FtFj7kSSJZ?s);LL@W&aND7blR8rlvkAb48RwJZlOHA~t~RfC zOD%ZcOzhYEV&s9%qns0&ste5U!^MFWYn`Od()5RwIz6%@Ek+Pn`s79unJY-$7n-Uf z&eUYvtd)f7h7zG_hDiFC!psCg#q&0c=GHKOik~$$>$Fw*k z;G)HS$IR)Cu72HH|JjeeauX;U6IgZ_IfxFCE_bGPAU25$!j8Etsl0Rk@R`$jXuHo8 z3Hhj-rTR$Gq(x)4Tu6;6rHQhoCvL4Q+h0Y+@Zdt=KTb0~wj7-(Z9G%J+aQu05@k6JHeCC|YRFWGdDCV}ja;-yl^9<`>f=AwOqML1a~* z9@cQYb?!+Fmkf}9VQrL8$uyq8k(r8)#;##xG9lJ-B)Fg@15&To(@xgk9SP*bkHlxiy8I*wJQylh(+9X~H-Is!g&C!q*eIYuhl&fS&|w)dAzXBdGJ&Mp$+8D| zZaD<+RtjI90QT{R0YLk6_dm=GfCg>7;$ zlyLsNYf@MfLH<}ott5)t2CXiQos zFLt^`%ygB2Vy^I$W3J_Rt4olRn~Gh}AW(`F@LsUN{d$sR%bU&3;rsD=2KCL+4c`zv zlI%D>9-)U&R3;>d1Vdd5b{DeR!HXDm44Vq*u?`wziLLsFUEp4El;*S0;I~D#TgG0s zBXYZS{o|Hy0A?LVNS)V4c_CFwyYj-E#)4SQq9yaf`Y2Yhk7yHSdos~|fImZG5_3~~o<@jTOH@Mc7`*xn-aO5F zyFT-|LBsm(NbWkL^oB-Nd31djBaYebhIGXhsJyn~`SQ6_4>{fqIjRp#Vb|~+Qi}Mdz!Zsw= zz?5L%F{c{;Cv3Q8ab>dsHp)z`DEKHf%e9sT(aE6$az?A}3P`Lm(~W$8Jr=;d8#?dm_cmv>2673NqAOenze z=&QW`?TQAu5~LzFLJvaJ zaBU3mQFtl5z?4XQDBWNPaH4y)McRpX#$(3o5Nx@hVoOYOL&-P+gqS1cQ~J;~1roGH zVzi46?FaI@w-MJ0Y7BuAg*3;D%?<_OGsB3)c|^s3A{UoAOLP8scn`!5?MFa|^cTvq z#%bYG3m3UO9(sH@LyK9-LSnlVcm#5^NRs9BXFtRN9kBY2mPO|@b7K#IH{B{=0W06) zl|s#cIYcreZ5p3j>@Ly@35wr-q8z5f9=R42IsII=->1stLo@Q%VooDvg@*K(H@*5g zUPS&cM~k4oqp`S+qp^*nxzm^0mg3h8ppEHQ@cXyQ=YKV-6)FB*$KCa{POe2^EHr{J zOxcVd)s3Mzs8m`iV?MSp=qV59blW9$+$P+2;PZDRUD~sr*CQUr&EDiCSfH@wuHez+ z`d5p(r;I7D@8>nbZ&DVhT6qe+accH;<}q$8Nzz|d1twqW?UV%FMP4Y@NQ`3(+5*i8 zP9*yIMP7frrneG3M9 zf>GsjA!O#Bifr5np-H~9lR(>#9vhE6W-r`EjjeQ_wdWp+rt{{L5t5t(Ho|4O24@}4 z_^=_CkbI`3;~sXTnnsv=^b3J}`;IYyvb1gM>#J9{$l#Zd*W!;meMn&yXO7x`Epx_Y zm-1wlu~@Ii_7D}>%tzlXW;zQT=uQXSG@t$<#6-W*^vy7Vr2TCpnix@7!_|aNXEnN<-m?Oq;DpN*x6f>w za1Wa5entFEDtA0SD%iZv#3{wl-S`0{{i3a9cmgNW`!TH{J*~{@|5f%CKy@uk*8~af zt_d34U4y&3y9IZ5cXxLQ?(XjH5?q3Z0KxK~y!-CUyWG6{<)5lkhbox0HnV&7^zNBn zjc|?X!Y=63(Vg>#&Wx%=LUr5{i@~OdzT#?P8xu#P*I_?Jl7xM4dq)4vi}3Wj_c=XI zSbc)@Q2Et4=(nBDU{aD(F&*%Ix!53_^0`+nOFk)}*34#b0Egffld|t_RV91}S0m)0 zap{cQDWzW$geKzYMcDZDAw480!1e1!1Onpv9fK9Ov~sfi!~OeXb(FW)wKx335nNY! za6*~K{k~=pw`~3z!Uq%?MMzSl#s%rZM{gzB7nB*A83XIGyNbi|H8X>a5i?}Rs+z^; z2iXrmK4|eDOu@{MdS+?@(!-Ar4P4?H_yjTEMqm7`rbV4P275(-#TW##v#Dt14Yn9UB-Sg3`WmL0+H~N;iC`Mg%pBl?1AAOfZ&e; z*G=dR>=h_Mz@i;lrGpIOQwezI=S=R8#);d*;G8I(39ZZGIpWU)y?qew(t!j23B9fD z?Uo?-Gx3}6r8u1fUy!u)7LthD2(}boE#uhO&mKBau8W8`XV7vO>zb^ZVWiH-DOjl2 zf~^o1CYVU8eBdmpAB=T%i(=y}!@3N%G-*{BT_|f=egqtucEtjRJJhSf)tiBhpPDpgzOpG12UgvOFnab&16Zn^2ZHjs)pbd&W1jpx%%EXmE^ zdn#R73^BHp3w%&v!0~azw(Fg*TT*~5#dJw%-UdxX&^^(~V&C4hBpc+bPcLRZizWlc zjR;$4X3Sw*Rp4-o+a4$cUmrz05RucTNoXRINYG*DPpzM&;d1GNHFiyl(_x#wspacQ zL)wVFXz2Rh0k5i>?Ao5zEVzT)R(4Pjmjv5pzPrav{T(bgr|CM4jH1wDp6z*_jnN{V ziN56m1T)PBp1%`OCFYcJJ+T09`=&=Y$Z#!0l0J2sIuGQtAr>dLfq5S;{XGJzNk@a^ zk^eHlC4Gch`t+ue3RviiOlhz81CD9z~d|n5;A>AGtkZMUQ#f>5M14f2d}2 z8<*LNZvYVob!p9lbmb!0jt)xn6O&JS)`}7v}j+csS3e;&Awj zoNyjnqLzC(QQ;!jvEYUTy73t_%16p)qMb?ihbU{y$i?=a7@JJoXS!#CE#y}PGMK~3 zeeqqmo7G-W_S97s2eed^erB2qeh4P25)RO1>MH7ai5cZJTEevogLNii=oKG)0(&f` z&hh8cO{of0;6KiNWZ6q$cO(1)9r{`}Q&%p*O0W7N--sw3Us;)EJgB)6iSOg(9p_mc zRw{M^qf|?rs2wGPtjVKTOMAfQ+ZNNkb$Ok0;Pe=dNc7__TPCzw^H$5J0l4D z%p(_0w(oLmn0)YDwrcFsc*8q)J@ORBRoZ54GkJpxSvnagp|8H5sxB|ZKirp%_mQt_ z81+*Y8{0Oy!r8Gmih48VuRPwoO$dDW@h53$C)duL4_(osryhwZSj%~KsZ?2n?b`Z* z#C8aMdZxYmCWSM{mFNw1ov*W}Dl=%GQpp90qgZ{(T}GOS8#>sbiEU;zYvA?=wbD5g+ahbd1#s`=| zV6&f#ofJC261~Ua6>0M$w?V1j##jh-lBJ2vQ%&z`7pO%frhLP-1l)wMs=3Q&?oth1 zefkPr@3Z(&OL@~|<0X-)?!AdK)ShtFJ;84G2(izo3cCuKc{>`+aDoziL z6gLTL(=RYeD7x^FYA%sPXswOKhVa4i(S4>h&mLvS##6-H?w8q!B<8Alk>nQEwUG)SFXK zETfcTwi=R3!ck|hSM`|-^N3NWLav&UTO{a9=&Tuz-Kq963;XaRFq#-1R18fi^Gb-; zVO>Q{Oe<^b0WA!hkBi9iJp3`kGwacXX2CVQ0xQn@Y2OhrM%e4)Ea7Y*Df$dY2BpbL zv$kX}*#`R1uNA(7lk_FAk~{~9Z*Si5xd(WKQdD&I?8Y^cK|9H&huMU1I(251D7(LL z+){kRc=ALmD;#SH#YJ+|7EJL6e~w!D7_IrK5Q=1DCulUcN(3j`+D_a|GP}?KYx}V+ zx_vLTYCLb0C?h;e<{K0`)-|-qfM16y{mnfX(GGs2H-;-lRMXyb@kiY^D;i1haxoEk zsQ7C_o2wv?;3KS_0w^G5#Qgf*>u)3bT<3kGQL-z#YiN9QH7<(oDdNlSdeHD zQJN-U*_wJM_cU}1YOH=m>DW~{%MAPxL;gLdU6S5xLb$gJt#4c2KYaEaL8ORWf=^(l z-2`8^J;&YG@vb9em%s~QpU)gG@24BQD69;*y&-#0NBkxumqg#YYomd2tyo0NGCr8N z5<5-E%utH?Ixt!(Y4x>zIz4R^9SABVMpLl(>oXnBNWs8w&xygh_e4*I$y_cVm?W-^ ze!9mPy^vTLRclXRGf$>g%Y{(#Bbm2xxr_Mrsvd7ci|X|`qGe5=54Zt2Tb)N zlykxE&re1ny+O7g#`6e_zyjVjRi5!DeTvSJ9^BJqQ*ovJ%?dkaQl!8r{F`@KuDEJB3#ho5 zmT$A&L=?}gF+!YACb=%Y@}8{SnhaGCHRmmuAh{LxAn0sg#R6P_^cJ-9)+-{YU@<^- zlYnH&^;mLVYE+tyjFj4gaAPCD4CnwP75BBXA`O*H(ULnYD!7K14C!kGL_&hak)udZ zkQN8)EAh&9I|TY~F{Z6mBv7sz3?<^o(#(NXGL898S3yZPTaT|CzZpZ~pK~*9Zcf2F zgwuG)jy^OTZD`|wf&bEdq4Vt$ir-+qM7BosXvu`>W1;iFN7yTvcpN_#at)Q4n+(Jh zYX1A-24l9H5jgY?wdEbW{(6U1=Kc?Utren80bP`K?J0+v@{-RDA7Y8yJYafdI<7-I z_XA!xeh#R4N7>rJ_?(VECa6iWhMJ$qdK0Ms27xG&$gLAy(|SO7_M|AH`fIY)1FGDp zlsLwIDshDU;*n`dF@8vV;B4~jRFpiHrJhQ6TcEm%OjWTi+KmE7+X{19 z>e!sg0--lE2(S0tK}zD&ov-{6bMUc%dNFIn{2^vjXWlt>+uxw#d)T6HNk6MjsfN~4 zDlq#Jjp_!wn}$wfs!f8NX3Rk#9)Q6-jD;D9D=1{$`3?o~caZjXU*U32^JkJ$ZzJ_% zQWNfcImxb!AV1DRBq`-qTV@g1#BT>TlvktYOBviCY!13Bv?_hGYDK}MINVi;pg)V- z($Bx1Tj`c?1I3pYg+i_cvFtcQ$SV9%%9QBPg&8R~Ig$eL+xKZY!C=;M1|r)$&9J2x z;l^a*Ph+isNl*%y1T4SviuK1Nco_spQ25v5-}7u?T9zHB5~{-+W*y3p{yjn{1obqf zYL`J^Uz8zZZN8c4Dxy~)k3Ws)E5eYi+V2C!+7Sm0uu{xq)S8o{9uszFTnE>lPhY=5 zdke-B8_*KwWOd%tQs_zf0x9+YixHp+Qi_V$aYVc$P-1mg?2|_{BUr$6WtLdIX2FaF zGmPRTrdIz)DNE)j*_>b9E}sp*(1-16}u za`dgT`KtA3;+e~9{KV48RT=CGPaVt;>-35}%nlFUMK0y7nOjoYds7&Ft~#>0$^ciZ zM}!J5Mz{&|&lyG^bnmh?YtR z*Z5EfDxkrI{QS#Iq752aiA~V)DRlC*2jlA|nCU!@CJwxO#<=j6ssn;muv zhBT9~35VtwsoSLf*(7vl&{u7d_K_CSBMbzr zzyjt&V5O#8VswCRK3AvVbS7U5(KvTPyUc0BhQ}wy0z3LjcdqH8`6F3!`)b3(mOSxL z>i4f8xor(#V+&#ph~ycJMcj#qeehjxt=~Na>dx#Tcq6Xi4?BnDeu5WBBxt603*BY& zZ#;o1kv?qpZjwK-E{8r4v1@g*lwb|8w@oR3BTDcbiGKs)a>Fpxfzh&b ziQANuJ_tNHdx;a*JeCo^RkGC$(TXS;jnxk=dx++D8|dmPP<0@ z$wh#ZYI%Rx$NKe-)BlJzB*bot0ras3I%`#HTMDthGtM_G6u-(tSroGp1Lz+W1Y`$@ zP`9NK^|IHbBrJ#AL3!X*g3{arc@)nuqa{=*2y+DvSwE=f*{>z1HX(>V zNE$>bbc}_yAu4OVn;8LG^naq5HZY zh{Hec==MD+kJhy6t=Nro&+V)RqORK&ssAxioc7-L#UQuPi#3V2pzfh6Ar400@iuV5 z@r>+{-yOZ%XQhsSfw%;|a4}XHaloW#uGluLKux0II9S1W4w=X9J=(k&8KU()m}b{H zFtoD$u5JlGfpX^&SXHlp$J~wk|DL^YVNh2w(oZ~1*W156YRmenU;g=mI zw({B(QVo2JpJ?pJqu9vijk$Cn+%PSw&b4c@uU6vw)DjGm2WJKt!X}uZ43XYlDIz%& z=~RlgZpU-tu_rD`5!t?289PTyQ zZgAEp=zMK>RW9^~gyc*x%vG;l+c-V?}Bm;^{RpgbEnt_B!FqvnvSy)T=R zGa!5GACDk{9801o@j>L8IbKp#!*Td5@vgFKI4w!5?R{>@^hd8ax{l=vQnd2RDHopo zwA+qb2cu4Rx9^Bu1WNYT`a(g}=&&vT`&Sqn-irxzX_j1=tIE#li`Hn=ht4KQXp zzZj`JO+wojs0dRA#(bXBOFn**o+7rPY{bM9m<+UBF{orv$#yF8)AiOWfuas5Fo`CJ zqa;jAZU^!bh8sjE7fsoPn%Tw11+vufr;NMm3*zC=;jB{R49e~BDeMR+H6MGzDlcA^ zKg>JEL~6_6iaR4i`tSfUhkgPaLXZ<@L7poRF?dw_DzodYG{Gp7#24<}=18PBT}aY` z{)rrt`g}930jr3^RBQNA$j!vzTh#Mo1VL`QCA&US?;<2`P+xy8b9D_Hz>FGHC2r$m zW>S9ywTSdQI5hh%7^e`#r#2906T?))i59O(V^Rpxw42rCAu-+I3y#Pg6cm#&AX%dy ze=hv0cUMxxxh1NQEIYXR{IBM&Bk8FK3NZI3z+M>r@A$ocd*e%x-?W;M0pv50p+MVt zugo<@_ij*6RZ;IPtT_sOf2Zv}-3R_1=sW37GgaF9Ti(>V z1L4ju8RzM%&(B}JpnHSVSs2LH#_&@`4Kg1)>*)^i`9-^JiPE@=4l$+?NbAP?44hX&XAZy&?}1;=8c(e0#-3bltVWg6h=k!(mCx=6DqOJ-I!-(g;*f~DDe={{JGtH7=UY|0F zNk(YyXsGi;g%hB8x)QLpp;;`~4rx>zr3?A|W$>xj>^D~%CyzRctVqtiIz7O3pc@r@JdGJiH@%XR_9vaYoV?J3K1cT%g1xOYqhXfSa`fg=bCLy% zWG74UTdouXiH$?H()lyx6QXt}AS)cOa~3IdBxddcQp;(H-O}btpXR-iwZ5E)di9Jf zfToEu%bOR11xf=Knw7JovRJJ#xZDgAvhBDF<8mDu+Q|!}Z?m_=Oy%Ur4p<71cD@0OGZW+{-1QT?U%_PJJ8T!0d2*a9I2;%|A z9LrfBU!r9qh4=3Mm3nR_~X-EyNc<;?m`?dKUNetCnS)}_-%QcWuOpw zAdZF`4c_24z&m{H9-LIL`=Hrx%{IjrNZ~U<7k6p{_wRkR84g>`eUBOQd3x5 zT^kISYq)gGw?IB8(lu1=$#Vl?iZdrx$H0%NxW)?MO$MhRHn8$F^&mzfMCu>|`{)FL z`ZgOt`z%W~^&kzMAuWy9=q~$ldBftH0}T#(K5e8;j~!x$JjyspJ1IISI?ON5OIPB$ z-5_|YUMb+QUsiv3R%Ys4tVYW+x$}dg;hw%EdoH%SXMp`)v?cxR4wic{X9pVBH>=`#`Kcj!}x4 zV!`6tj|*q?jZdG(CSevn(}4Ogij5 z-kp;sZs}7oNu0x+NHs~(aWaKGV@l~TBkmW&mPj==N!f|1e1SndS6(rPxsn7dz$q_{ zL0jSrihO)1t?gh8N zosMjR3n#YC()CVKv zos2TbnL&)lHEIiYdz|%6N^vAUvTs6?s|~kwI4uXjc9fim`KCqW3D838Xu{48p$2?I zOeEqQe1}JUZECrZSO_m=2<$^rB#B6?nrFXFpi8jw)NmoKV^*Utg6i8aEW|^QNJuW& z4cbXpHSp4|7~TW(%JP%q9W2~@&@5Y5%cXL#fMhV59AGj<3$Hhtfa>24DLk{7GZUtr z5ql**-e58|mbz%5Kk~|f!;g+Ze^b);F+5~^jdoq#m+s?Y*+=d5ruym%-Tnn8htCV; zDyyUrWydgDNM&bI{yp<_wd-q&?Ig+BN-^JjWo6Zu3%Eov^Ja>%eKqrk&7kUqeM8PL zs5D}lTe_Yx;e=K`TDya!-u%y$)r*Cr4bSfN*eZk$XT(Lv2Y}qj&_UaiTevxs_=HXjnOuBpmT> zBg|ty8?|1rD1~Ev^6=C$L9%+RkmBSQxlnj3j$XN?%QBstXdx+Vl!N$f2Ey`i3p@!f zzqhI3jC(TZUx|sP%yValu^nzEV96o%*CljO>I_YKa8wMfc3$_L()k4PB6kglP@IT#wBd*3RITYADL}g+hlzLYxFmCt=_XWS}=jg8`RgJefB57z(2n&&q>m ze&F(YMmoRZW7sQ;cZgd(!A9>7mQ2d#!-?$%G8IQ0`p1|*L&P$GnU0i0^(S;Rua4v8 z_7Qhmv#@+kjS-M|($c*ZOo?V2PgT;GKJyP1REABlZhPyf!kR(0UA7Bww~R<7_u6#t z{XNbiKT&tjne(&=UDZ+gNxf&@9EV|fblS^gxNhI-DH;|`1!YNlMcC{d7I{u_E~cJOalFEzDY|I?S3kHtbrN&}R3k zK(Ph_Ty}*L3Et6$cUW`0}**BY@44KtwEy(jW@pAt`>g> z&8>-TmJiDwc;H%Ae%k6$ndZlfKruu1GocgZrLN=sYI52}_I%d)~ z6z40!%W4I6ch$CE2m>Dl3iwWIbcm27QNY#J!}3hqc&~(F8K{^gIT6E&L!APVaQhj^ zjTJEO&?**pivl^xqfD(rpLu;`Tm1MV+Wtd4u>X6u5V{Yp%)xH$k410o{pGoKdtY0t@GgqFN zO=!hTcYoa^dEPKvPX4ukgUTmR#q840gRMMi%{3kvh9gt(wK;Fniqu9A%BMsq?U&B5DFXC8t8FBN1&UIwS#=S zF(6^Eyn8T}p)4)yRvs2rCXZ{L?N6{hgE_dkH_HA#L3a0$@UMoBw6RE9h|k_rx~%rB zUqeEPL|!Pbp|up2Q=8AcUxflck(fPNJYP1OM_4I(bc24a**Qnd-@;Bkb^2z8Xv?;3yZp*| zoy9KhLo=;8n0rPdQ}yAoS8eb zAtG5QYB|~z@Z(Fxdu`LmoO>f&(JzsO|v0V?1HYsfMvF!3| zka=}6U13(l@$9&=1!CLTCMS~L01CMs@Abl4^Q^YgVgizWaJa%{7t)2sVcZg0mh7>d z(tN=$5$r?s={yA@IX~2ot9`ZGjUgVlul$IU4N}{ zIFBzY3O0;g$BZ#X|VjuTPKyw*|IJ+&pQ` z(NpzU`o=D86kZ3E5#!3Ry$#0AW!6wZe)_xZ8EPidvJ0f+MQJZ6|ZJ$CEV6;Yt{OJnL`dewc1k>AGbkK9Gf5BbB-fg? zgC4#CPYX+9%LLHg@=c;_Vai_~#ksI~)5|9k(W()g6ylc(wP2uSeJ$QLATtq%e#zpT zp^6Y)bV+e_pqIE7#-hURQhfQvIZpMUzD8&-t$esrKJ}4`ZhT|woYi>rP~y~LRf`*2!6 z6prDzJ~1VOlYhYAuBHcu9m>k_F>;N3rpLg>pr;{EDkeQPHfPv~woj$?UTF=txmaZy z?RrVthxVcqUM;X*(=UNg4(L|0d250Xk)6GF&DKD@r6{aZo;(}dnO5@CP7pMmdsI)- zeYH*@#+|)L8x7)@GNBu0Npyyh6r z^~!3$x&w8N)T;|LVgnwx1jHmZn{b2V zO|8s#F0NZhvux?0W9NH5;qZ?P_JtPW86)4J>AS{0F1S0d}=L2`{F z_y;o;17%{j4I)znptnB z%No1W>o}H2%?~CFo~0j?pzWk?dV4ayb!s{#>Yj`ZJ!H)xn}*Z_gFHy~JDis)?9-P=z4iOQg{26~n?dTms7)+F}? zcXvnHHnnbNTzc!$t+V}=<2L<7l(84v1I3b;-)F*Q?cwLNlgg{zi#iS)*rQ5AFWe&~ zWHPPGy{8wEC9JSL?qNVY76=es`bA{vUr~L7f9G@mP}2MNF0Qhv6Sgs`r_k!qRbSXK zv16Qqq`rFM9!4zCrCeiVS~P2e{Pw^A8I?p?NSVR{XfwlQo*wj|Ctqz4X-j+dU7eGkC(2y`(P?FM?P4gKki3Msw#fM6paBq#VNc>T2@``L{DlnnA-_*i10Kre&@-H!Z7gzn9pRF61?^^ z8dJ5kEeVKb%Bly}6NLV}<0(*eZM$QTLcH#+@iWS^>$Of_@Mu1JwM!>&3evymgY6>C_)sK+n|A5G6(3RJz0k>(z2uLdzXeTw)e4*g!h} zn*UvIx-Ozx<3rCF#C`khSv`Y-b&R4gX>d5osr$6jlq^8vi!M$QGx05pJZoY#RGr*J zsJmOhfodAzYQxv-MoU?m_|h^aEwgEHt5h_HMkHwtE+OA03(7{hm1V?AlYAS7G$u5n zO+6?51qo@aQK5#l6pM`kD5OmI28g!J2Z{5kNlSuKl=Yj3QZ|bvVHU}FlM+{QV=<=) z+b|%Q!R)FE z@ycDMSKV2?*XfcAc5@IOrSI&3&aR$|oAD8WNA6O;p~q-J@ll{x`jP<*eEpIYOYnT zer_t=dYw6a0avjQtKN&#n&(KJ5Kr$RXPOp1@Fq#0Of zTXQkq4qQxKWR>x#d{Hyh?6Y)U07;Q$?BTl7mx2bSPY_juXub1 z%-$)NKXzE<%}q>RX25*oeMVjiz&r_z;BrQV-(u>!U>C*OisXNU*UftsrH6vAhTEm@ zoKA`?fZL1sdd!+G@*NNvZa>}37u^x8^T>VH0_6Bx{3@x5NAg&55{2jUE-w3zCJNJi z^IlU=+DJz-9K&4c@7iKj(zlj@%V}27?vYmxo*;!jZVXJMeDg;5T!4Y1rxNV-e$WAu zkk6^Xao8HC=w2hpLvM(!xwo|~$eG6jJj39zyQHf)E+NPJlfspUhzRv&_qr8+Z1`DA zz`EV=A)d=;2&J;eypNx~q&Ir_7e_^xXg(L9>k=X4pxZ3y#-ch$^TN}i>X&uwF%75c(9cjO6`E5 z16vbMYb!lEIM?jxn)^+Ld8*hmEXR4a8TSfqwBg1(@^8$p&#@?iyGd}uhWTVS`Mlpa zGc+kV)K7DJwd46aco@=?iASsx?sDjbHoDVU9=+^tk46|Fxxey1u)_}c1j z^(`5~PU%og1LdSBE5x4N&5&%Nh$sy0oANXwUcGa>@CCMqP`4W$ZPSaykK|giiuMIw zu#j)&VRKWP55I(5K1^cog|iXgaK1Z%wm%T;;M3X`-`TTWaI}NtIZj;CS)S%S(h}qq zRFQ#{m4Qk$7;1i*0PC^|X1@a1pcMq1aiRSCHq+mnfj^FS{oxWs0McCN-lK4>SDp#` z7=Duh)kXC;lr1g3dqogzBBDg6>et<<>m>KO^|bI5X{+eMd^-$2xfoP*&e$vdQc7J% zmFO~OHf7aqlIvg%P`Gu|3n;lKjtRd@;;x#$>_xU(HpZos7?ShZlQSU)bY?qyQM3cHh5twS6^bF8NBKDnJgXHa)? zBYv=GjsZuYC2QFS+jc#uCsaEPEzLSJCL=}SIk9!*2Eo(V*SAUqKw#?um$mUIbqQQb zF1Nn(y?7;gP#@ws$W76>TuGcG=U_f6q2uJq?j#mv7g;llvqu{Yk~Mo>id)jMD7;T> zSB$1!g)QpIf*f}IgmV;!B+3u(ifW%xrD=`RKt*PDC?M5KI)DO`VXw(7X-OMLd3iVU z0CihUN(eNrY;m?vwK{55MU`p1;JDF=6ITN$+!q8W#`iIsN8;W7H?`htf%RS9Lh+KQ z_p_4?qO4#*`t+8l-N|kAKDcOt zoHsqz_oO&n?@4^Mr*4YrkDX44BeS*0zaA1j@*c}{$;jUxRXx1rq7z^*NX6d`DcQ}L z6*cN7e%`2#_J4z8=^GM6>%*i>>X^_0u9qn%0JTUo)c0zIz|7a`%_UnB)-I1cc+ z0}jAK0}jBl|6-2VT759oxBnf%-;7vs>7Mr}0h3^$0`5FAy}2h{ps5%RJA|^~6uCqg zxBMK5bQVD{Aduh1lu4)`Up*&( zCJQ>nafDb#MuhSZ5>YmD@|TcrNv~Q%!tca;tyy8Iy2vu2CeA+AsV^q*Wohg%69XYq zP0ppEDEYJ9>Se&X(v=U#ibxg()m=83pLc*|otbG;`CYZ z*YgsakGO$E$E_$|3bns7`m9ARe%myU3$DE;RoQ<6hR8e;%`pxO1{GXb$cCZl9lVnJ$(c` z``G?|PhXaz`>)rb7jm2#v7=(W?@ zjUhrNndRFMQ}%^^(-nmD&J>}9w@)>l;mhRr@$}|4ueOd?U9ZfO-oi%^n4{#V`i}#f zqh<@f^%~(MnS?Z0xsQI|Fghrby<&{FA+e4a>c(yxFL!Pi#?DW!!YI{OmR{xEC7T7k zS_g*9VWI}d0IvIXx*d5<7$5Vs=2^=ews4qZGmAVyC^9e;wxJ%BmB(F5*&!yyABCtLVGL@`qW>X9K zpv=W~+EszGef=am3LG+#yIq5oLXMnZ_dxSLQ_&bwjC^0e8qN@v!p?7mg02H<9`uaJ zy0GKA&YQV2CxynI3T&J*m!rf4@J*eo235*!cB1zEMQZ%h5>GBF;8r37K0h?@|E*0A zIHUg0y7zm(rFKvJS48W7RJwl!i~<6X2Zw+Fbm9ekev0M;#MS=Y5P(kq^(#q11zsvq zDIppe@xOMnsOIK+5BTFB=cWLalK#{3eE>&7fd11>l2=MpNKjsZT2kmG!jCQh`~Fu0 z9P0ab`$3!r`1yz8>_7DYsO|h$kIsMh__s*^KXv?Z1O8|~sEz?Y{+GDzze^GPjk$E$ zXbA-1gd77#=tn)YKU=;JE?}De0)WrT%H9s3`fn|%YibEdyZov3|MJ>QWS>290eCZj z58i<*>dC9=kz?s$sP_9kK1p>nV3qvbleExyq56|o+oQsb{ZVmuu1n~JG z0sUvo_i4fSM>xRs8rvG$*+~GZof}&ISxn(2JU*K{L<3+b{bBw{68H&Uiup@;fWWl5 zgB?IWMab0LkXK(Hz#yq>scZbd2%=B?DO~^q9tarlzZysN+g}n0+v);JhbjUT8AYrt z3?;0r%p9zLJv1r$%q&HKF@;3~0wVwO!U5m;J`Mm|`Nc^80sZd+Wj}21*SPoF82hCF zoK?Vw;4ioafdAkZxT1er-LLVi-*0`@2Ur&*!b?0U>R;no+S%)xoBuBxRw$?weN-u~tKE}8xb@7Gs%(aC;e1-LIlSfXDK(faFW)mnHdrLc3`F z6ZBsT^u0uVS&il=>YVX^*5`k!P4g1)2LQmz{?&dgf`7JrA4ZeE0sikL`k!Eb6r=g0 z{aCy_0I>fxSAXQYz3lw5G|ivg^L@(x-uch!AphH+d;E4`175`R0#b^)Zp>EM1Ks=zx6_261>!7 z{7F#a{Tl@Tpw9S`>7_i|PbScS-(dPJv9_0-FBP_aa@Gg^2IoKNZM~#=sW$SH3MJ|{ zsQy8F43lX7hYx<{v^Q9`2QsMzeen3cGpiTgzVp- z`aj3&Wv0(he1qKI!2jpGpO-i0Wpcz%vdn`2o9x&3;^nsZPt3cUrq%6pMBGm|2w}jqWsUdWuU2f;lKUM{|0GNJSj#J5DF%#7!nnzP3z7W z2)7!V+fV37%)lpXD{2cPEnU%_1&6fzdkEd>g7280V9K}lB4Vpz`lfV{w3f5R7MY#YX1$EhP+|BY_u@z=9+Uiy1_4qsRCAKL?A`d5pyX z0|9Xa2LTcL{|5l7dbzlo*(o_YIGQ=TTA8{0TX}j3ySZAL*}Gbq7`Zw)Gnm*KxwsUo z+Bu*KBl}}eg^kYdmm5~6tgm>h9Ys3K8{)u>3?!R z^&uM+`;PFoxNU>+zQ+)`>t^A+!#_gB=xt#_i#ssGSGx;EiFa;A^GMwJZJob=$tvEA z_6MJPq71%&5vlB3Du(J2r%;mI$3iXVo`(y0$=8UC8+OBj5!(pFkqBp>JSnwowH(qL zwE2y9iUHjWfzp5o#3!$35`l(sJh;%84V%Bj~B!^|npbhWdI84jozhWTusZZh$V~f=1XI zr(;HyygrV&ZeT>EX@dMG*nh+48Rl|fFm7v;#F?1*GWZkaT9&K;>fIt4*e(-YdE!}@8QT~S# z`hWK0CEZS-17lQm6mf*nza$d$R-ROb-;E0XkV!>4oy}>s6i9KD4K@A)G&0K z{?X6eUKka4lw{{0RG}RU>Kt#M_ULgro|?K56!Ztv8mI_Vo*MTo*rm>u4yT4T z)R)kGjX;*DBid^Zj0fpP_Ewfqv6LT>g^`6%(s_S=2MW^|tdG|?6hAHqE6k?4FXp+1 zI-aj_v9nFqRcg<;$ai*3Fm1)qnl+q7=T2kF`gkW8SuWP&<{J4x3|c$y_lB&#zzMLm znXi|;B>Wc4rD6i-$blt~E|SZ-;j{_coW>2q!1UuX8>GRiq5g1Z+QrX62o;-fs?=U) zr@>ei0Cq}r&*<5e9ucs$bzUraPB@_Rt)p0A#ZZ3GXU4YJX=gLtYyYwKC_cr$zY20%WUBY*VSf%T#y*r#i!xSIvRbrf* zXkv_BKXTM=`*BZ3q8z9A(>6buP|N8WCc5p227VAHq1=Notm{#qr4hQVcV%z<&+tgA zM8GhX)j-mgBoO_8^Mi;ugG)3q34LCQb>RmbrlEmOJk_`%EmtfQnL6PwYCbaj0XBkW zV3MM?=_A{qU)m|-^=F`Wh)_ZqWKVEVT0D>Fz$@g>^wkg&F?bSrel3c{OMBV+F@xOf#Z(na? z7y<<3{a+Si{f8rmq-0Xmqz!ChpoXj?ni<}=T_%N9haIsHv63xFNRv_^6~YA~o zc0NKe1fX@CL$6~iCQDw{k4WJBuiNcuP9X~^y%y96wxr}}5YIut-M}z5V=fjW!O%`? zR%#DE;v2#D$<6mWhv3)WWdsnUgCwGs-;H1>MMDcmk{-zVcrOQH$O5jBz)5MRbC`V1 zODkb+^3)z8g~FY<0FW7QWzukVF5DqjPYx;7SqLY@*}uQRnW)fNK}|z4am46DX%Yq< zk#S};jlnwz3;nVD(@|H7VkvoM-N;n0PP`S}T*>~D2dkW1+UI4Uy5ft{5^!ZI>tGg%$EY_`ids;a3CdtI_9fOi-I?N3TYGCXo1z;|QW-~I0qZqqqsOo?gQ*zC()*(P zyT}gAx=4%`WyI5V>qpFVhbsMb6Yp^J(-Qhr{kw<{abUn01A{e7*{e zgpK{ZONfq309xdxQHKPV{LOSi9Nf=MRKftT*G`l_!-zjBT3Ht$M1{>NCfJ)LPd&|t z9ue^5-8z^?&~zkss}-sVF;};oC6ZlgwG3loq2lxKBraD4{`i^-Ec2#>ZQ+>+Wt0!1 zQb;Sg$fTVrr5a;GZ6xin<49Bw^}VP7n~JiNFUmf-0tNu8QwZ9;gcb)cQI0FR@+)aB zE@BAK5|+i2D$OcFcQ+v6O7xqS(IWY$u=N;L{TjUC42B%>(^|w*-JtoXfECosF2j)WY%FiDh$LNW(SiZ%`#sWqHlSg zzy3}G9cTS<(|1jsJln^~gdi9fK&=)Ws9U1MjnLj1u}Q6#le(`@t?!SpJS{*?01ee6 z3B}mlGs3u+p(gMNKh0W(Z3$bQF|yvqd6^?Z1=J@fIp>jlzn(RssYUW>S1Pu>$KlZJ z;U&Ffo6dL72PbMu$PF2%wuKGxq9~YdG;G=2bisE+-3J&IY@TD0QH)aTZMh)Vypj;; z>Qi{K>T@gB-BYl>ahEtnWpsq>!s*CsVQ#7~C840Y(Hz@bHF%XJb)F3W#L}o@KxAey&9e1huglG6G z2LWYuG)pgWEXGI|QZMQvaqQCW7}Voe|OgHrm6ygM$NZKHVyrYMzVd2<81Y@pNo_!f;Xmqu4Eev*10(5n8V4AYxM9C2gdA#-#HE1GIrE#I{zT|^tj;-P8;G)c zG6!Cfkg|wQMAn3K^aagY!qu-K)c~>JAqb>CQDXB|g)BPU2)Nm)nHj+cuge{v{}_>=d7wGcz|LsX-WxwfNv*5wTZh&f|0ePV z#R5>}C#YYAsz9)xRYaG~*Ys&L*PPes8bw>6Zl}Tkm{96R=(fDN=ES88yOOv#JnFv) zg?&&WViMeD|6KYFi-WF4rJ`CufkYF~{Qbekb!3bNmBZSJD-8ISg#dmV!l*Q1OHlKq zd(qzw52dBg{`fs+iFxZ9?G^ZydG{k+Ss{kVE}4}yUMt(I{v&1_bDm+3NXQ716oSF% z!Yo?j78Gv=$H*O<{~DfwQdI8oQGumL^!mc9kxo`W2U+%HpEf;im{%*OV0q*n+8L7y zc5+)xKE+KdvN92oZ35U>1D;R=C$zvfR)Ks78H@a@$O{>ty&0S6F=(z|oj80b$QD}I zK0=s`z!Z-C37be8%vlFI@kp2eR#;=0=qJG;iZ#J32sH(fM=%i!QUg7BJ1t~_SA1#N zpBzvX8KISQiEp_7_CsYqe5<2>^cVXd{b2eJh0oI<1YWqHsiS|}Wtk4TPZlc4$b>+H zS-pq<0@%XR%*hlZktxvycE~#qIupAn8F?v-Z)}*D zNTpQ<>4MdAZG>U1040%=uwbGkW*w3MHpz}s^w-#pMv?wd=0#MrkjrSt85~w@=k<}x zqJ#_TS|Z|_lT{;&@nzKFbz$Cu0GvE{JF{GVKHw zP(#bUIc+>+lH^Qf+q5U&P&On;S(7W&Mt0+={m9j9$yCjvD!;g@ek|Aw#TsSK8Su-l z8o3sPnspqT!J!$)%(;UsQud)Dkl{EN!!7@P{kM1BIk)7Qquzkr&rlu5Qxnf8Aa!%d@(KMJM(5 z?almBbk`bi{}RJ?a}70UUh7644JOvchH-Z!-q@?)lf?m83tN-ahFcl8L6_*Ga6~9C zX-6YUy+O>3p&^=>dk>Gl@vG6E~z?>64Va2P|=xE#TeKh>Mz@NUMvu=2OKunsR zB_~zqo`F@Gp7}fap2a(UeVb?39jDV%*Ua)RdSwzgn%`-d(Os(won)n}* z6t#vMk!APnvIsyHlapdr>{#)w9x1;@*VXz7sZpE9Nk%vJ&wQISE!zyKZW8ta1d_7d zY&g?Bo~DF6WH{Ho#;#(V*rVSbbu8}(R)f;vl@Lbc;z4*C5?1&O2Z~U24l~Ja&!u>s zecWpV*mhbWG0lAYi6|Nq@;(ThGMR~QH=lX$j3>&V*_^+!{)EFKaxxht0Y&*{cNoTc zDX5$U60Y~CN7VPu;{<~5J1Mtiacf_WHsfhyZYc7z*k(w>5_tLWaG=*G zFmM3oS6+~_@|x_tf}bO{fg~*g5@CVD24gQ+8|a(;Fzs*&M>lY9QKxjqe+`MysR}L- zpM$}ANOE!2M6FSHuM4^5PL)r5Q_fr1IY*l3|HC-2oOzTmHfl*FF%JKL*WR#Ga z$0~Tro5@!?EL9Tq7^A|zWU+A*K#MySc*3at11Ddwyy!huCtsYrgkcNu4*cO0(=T7V z(c1=igYf+*Wnb!0#?kqZ+M!!@<*!V51I(-)@ zLz}k(frHa$Gn=1mzO11segjnJY$#rvOb9Opfg@@!Re^K)z?9PUFO;{?V0){NpcSOXeaToxmqw=s9AHi^(8vMa1P3|B&z{OL0SQYC) zeB1u#u;OiGBm~{P%xVD^74~qKedTNU=?A=^g+v35KXE18YjLDO#G_4D!R}P2Mcdk| zJo-Q7k~yICXGfC4GnwqFt~hg#qo{#v;cHIh&H+-_YoTbI#GNDnIFP4ab)XIa_8S12 zJS9`l7ocbVxIXDBa$ZC+|@6pl3%~X=($FbEGftK(AHL zy|u(g(KOc=zTxfI1??@;YhO`wB<{5CaKIOB{DLqZyk~}lBNPR0^>j6ku0GUtZ&Ypo zRU3m6v#3Z{>9c}WZv%J|^zqA&96tr_=Dw}W{SU^y;*Lp7__}>0Jg!2S&sh9L;s2?^ z z|AgXw=oqD@w|(9lEZ6nxy|)IxTri*mT8WpAY?}wxUREt;9-L45qf**Np*ya+gR(>& zd~EYfICM%h?SFd0SJ7x9j5+$*Z*-&yx@$}@U)Rq&e-LTLIcji)Edr=&y(REw{uiUD*`KvIufIhvC|>pNBjV?EX8D?ldqra4?UL% zw5s`WsNACiI`@;Rd}>^AcuJI=y6zyJ$@L z9gKGY-!;)4A_EA}1)C*|f@V`xq%@~M+K$5y$i*Y-d>8Y;tUTfX#;kSY(wMhXf-98c?2a|zF#ZdE{NE>&Qkwh(=B9>f zMiNCVCFUbZL|@kG>n&X^Mg+5ql2~E+Ub@O&enY)#Av;_F-)UY7EG)^=$ilPuLkqRP zjrfyJpERaLUc`3zO=&%EI+DqEefQbUa!_jW{v5uMB3Z`^$d(8QNY<`u1$ku?#h!=Y zuZ!>m`znEM@dEL09<)ZoQks3yC%*qr0qS+@Ama943e6z`0U`QN8U->y0sptWl%oaX zi+p$W`}kqra#s)fpL}H)H1Zy!y@m9M)vIgCApq})zwKlI}<2&$>rqeXiI5j z>Al+|f6h~EzeT~-VybgDU*fjdB)>?(pV*>+OUdtV=e=Vk`t4w@XvI8oI{M=GfPeGR z`_kjkdq0?FKwd!qaR($4KT|AvD`DG7JXVyQ|Hz2`;*)ZFED_mxMg|@{5kZ_XnqDy$ zzbV9T7u-Oe`8;Y0d&hriXZ9+eIs~Z+11WXB^?%=_%?r*6(mX1HzFUNRkxRuq)(UIO ze^Wes6Xbu#e+dHKYl*5$IfK;Xzv-vHr5=9#{A-Y~{|)vingMLDV4|J^Wq_%JLc2s* zFn^XlKx!BWP+>UAA21U>FXH@BRd51msMnRC4eE-dMXyn7lWEa}vf^9?x5BK|XycIb z5f9YU#oR>}@Qh)j1@ur$@pozLxe1>iG=u6B?jnF9LiJx9t89<83enaeIR-su1#g&J z;Q;Jm7pOR87lA4x)teOZodfv7IHiR7dkTNr#P_OU9pDy}|KRRjIU7Pf9_I*e4|g89 zc*Hv#F}!V(7H+}bn87|h-P{lm^0_uR7+%ix2jt#~{vaKsl|+ptInC{G1fNj zFqvwJ9(p7w0d$*_SlPh*t;3K#1MP{SMrNO-FfPF`(*_D!YA@n1reegL;akw#nM<_t zK)x?rIQvR8D!Nf7dHa%-=*Zzl;bTqj1c}-_{u$SzvC5u*!Da6DaaK{J2x|)FphxG4 zR6gxJ291BXVMh(^jw^4yyBhB9v6T@QxFI~O7W zC16Gv2?A{~SZ#Zn4FC0;gyjiaLnAf2qJnf%*#s!jV$-YT_wRkom$P?K~B*y04iMu9$scN`YhPIl+Lf6IW>`y zL>H*1()!opX*`+!J^EZ@Q;K5v7^GjtIxK`7!d#4r zKvOlWM^*~obOIU7k3IV@Vod%jh`=Hw)N7xf3qrn$Qo1WP?@IwO4 zowbsZik10GWP^3Y=|W;;SdNP5$D$ZHeg%!ID$Oi_HdUAmt29#54EdH1asQZbpo+Zo zwRn9DwwpHl+*`wu*EqNC3Dc%;-%|W*1Jz3aYcZ+UkCC{ zd`770&K_eN=~5`zq`4@Hx*Zj9vvS6d9qi;TQaku7I+g2ok;7)^P$91iX95R6iJu8B z`gP?lahLEmRz2L^Sr9O4QO|V!2G0sjTU0aY7i_2N*Ga0Ld*~|*+If51%q{J@1*$KO z6X2`uL@KW&Eb*uDw;94-HtRm`g@KvG%?F`J@q;Gj6PZhT`Q>MRR}J#TdHH#2XK;^Q zGQDE66_3jA=`xTx8fSy9!sJOeAf31!KgWLCr|c&yoe4xzZx%W;zEE#6e)^}}9R8rW zXRz0>sjU|0|0OJP&QH^u21V@NqXCOl&L=lZ^mY{Q&XiIY)0Qi*BKNX=HU~sJ(X6;V z4m~_}Tv|%n3$_Y#Vyt5)MOi_0Su;ONYN2M{Ah^!U=?^17*f95m9jC3DwN7%kW#}UB zbg01>XBY*V?z5Gs6YMFYd@!US-D54@*}x_o=YYep;sHHS%Y4H}6JT8Wu3G5q6y_@L zjl3m}NPSx;P_XfjOlUp>wSe?oMq~@2n94W8_njID)JpRR=0#KEs!nKL+l@A(h$(Ro zFu$Euj(mQqdWsxL6R2`kETk2UEiv%t72^>s+F7PhXYFO^Zow;NXlQC3QRiyBp<@v} z3O}yOcjr9%7QZn3&J7;&6I7c6lScu>>_L=`#s3_3S6@qk-;sBBjCB0a*08*t= z=>N2QT~(5)y3}SwisR>#*A~)Tw-z$%$%0XtYcY%lhNF`hG1$t5oJ^^`(&^&{hM+&M zl|DM+Xde9d_1l@$&Bo8t3SNOQ&P`n ziTvT#^QWj^*up@kt+u20I4-Gn*uW=i7*-Ih<8!44;mc;&EaN!EA44Y@qbD^*N^8cu+xg`gofBQ7%8K_ZFHoYifg==IAdw z^9oIM6mw)pi5}zd1!p{lHiy+?agn{P_L^6D-DtHG9T{Nng(=6cr_=lg&kY|@WZh@* zWUZ;5e=P){s-z$gM_Jnm&CjN~dlHdF5MC;=ac02#2_0mr_2Yam3~VTIno&}qqA~$& zMVtUHi(5<^wj)8KG^V`xj*I_GMELr4xGSUIW*(~uH;}={!0iyXaQDiSepd~%kQ}ozoqDC3#u+vg^;U|U zXNA=LA*UI>J}S_mT;7#cygW9%3!NmVB-{$efZR2b=13vVsaSVBhv3EKk+y!c2)pU! zfL1}^K7VJfOGxH{1cD@`81Q%m0ox;FHcihMUkhx1f>5dD5~FXbboxaFA9_)ab1B+{ zQm8|9HpNpkR)U_uii)y^9f3@amG`^xG~~&GO343lL+99PSn1)R$XH2WlS7fVv&&M` z?TzUt#y!1?coFv@oRqis8-drkkrn8Tj!e}*{^JqF8~%}i(o4n3`xs^_dCZ6L zDqu&lHvdMy=qUkWoe3N&m~o!Nl@qD8yJPX<&spW&vS(g_qfU&2O`70gUTI94lpVsG zL4l6U>6jOxLC^cuO7`sqexT&ux(V_Z0oM_i+X74n;9$dQKzSGBnN0=456}3o(JK(+ z9pxQ-W_8}YLrM_#$v$u)Z%9gaBS*c7x7re9Nvu1u%KLf>E&Yud3QI8d+w-Uc@xUQa zmil-HY~+G5;bnqET~eC{7sGN98HsV>IQ$!69qmbzHA1pIaa>z1oMf>G8yiF-ez zs*}cem{s~rX?5VTR43U+mN z&$ytE*H$fya&Z;q3bCEP9mTQa$H@Hne%z@wm`M88g*?vHcKQU;swGbnvC6dgDhGBg)d zl;5vwQTLZP{ev0n(*n6taWN>LLvohLSRm}qB|Pf{g_Uvf@0t4L2S&jOuVWOKd+3B? z4}SkGncNpL`&{X1Y2O`KbzvUl4M^J-Qbk^9RA=8e>tmzi9qZa(^iRCwTn`CF-dBIx zi|5wU#@LO@Q=7*JtcXy95ILYK;y)fDKYFC>n*;8Y=9gQ;MRGHdX@jZzSVww~X{baw z_h#>y2;J5uRfu9*-KndKpM1zpF+SP1i%g0X)2Sus8(}S;r18cDrgwa1Z}@l-g$~*m zCjq;Hak>V!wRq%!=mj)8_Z;gLq6ELBWL$*Be5Mqgut}@+S4rQdO#-z*hwXBQz`=(? zA0ohTjTeAE)fEDQDzwKpulSq7gGC4anx^R^10P!&#I6Pzw)-`R@3`#5b;ed@Iv|4a z4rQ1ZW*GNSHw^KZ_e-1wyDay*1k;kBg+KPbNr;ih>cw~^787_@?5f=jT?com@mo*q z{Y5BL@(j?olSPlhuQkfbk)uU8nk`iHdqCCz9=@2_Vws!l1JeiHbGC!%anuyAv6+kAs!0~aydd`4l=@ekb7)iX{aWA6P= z1j-C^aa}+ojo zrbOC3KaHJwqh}vn-6KdAIjmQvH%(wOC04&zRXr-ty>KxPFnSaDgU3LWo>qjCaeoIp zM=-y(R8Pw?FOgOprn}n@ob_$w8}ieLnYf+n$_6^(k07J-^EY!meMA+w^qY^=$mI~! z*Qelr>(5(#auGxS>TxMB|GzG*0E!3vsk5Ph%s?5^&zV#x#iYei_LlBZJpv zW~$rd^KI6F5wt6S(CpPoQ-aZ)tc%e|LxT3$baSvP`ky{%BpnLHj6JEfH~0zk=G#pS zd(uilPGce~M6p3#F|##P9e{e0a^#GAts4#-eR@?Ik$lw}pnOt#bJmf-Ohi=y^Ht6K z{bv!3@Aqveh|`E1GiVI+v>|Q#6i4IAl3fnjS9*rp6T>!}PYre;seNVYbd@ydHk|?NDU#jr^U@FYib@F{oL2g2Da8)d z>F60No-EhfGL1}?e09PA=w z4DcnZruYN%8JPy|6SaJeJ(A6!K%yFIPpDRKSKW9XP?>Uxe#8Y-cYx(u(ydrPY&2L9 zl5jfeGr$*CRv5#T()$#MBfeQ7{s?w3#&0oOPc^ppYwlqQtvd3O+A~rVRQ_cD?}m>W z42&eKz~G6vX$ad~$ZE4)>^>ZX!9Fd->@EA9^I#mtV4OrK0h*WW04HqEu!q{^14t$J z;0nuSl_8k9#Z-1KEKUjjQu!qZ+^bOH|My$dX-xJHTH(Xy=_hp>S5+!q?=4Js$X@H1T>0VwTXX_)S|G}ZXD~O&`I?iBRK;TJ8G`9<%%sls52Vd3io@O>8fat zb{nU1%kMsTMQfpftNDu^w_i{q%hRQih5_33zPi50>62E zE7_ubb|weV#rCWde)U9hs00-yL3iZp&f*$T=`PEXk#rZ_L+a*!HBxM;Q5UjeFPDS~ z5o}l&Kck>*GSjnE5NVOdu|wyI*PQwOSUvq-LHc?S+NF;Hj$AZFEzr$f;`4;w24c9K z7S5kQ^Uf(2?&BDO)Go@!p%FLihkaA4ej zJLWXq3^3IIjp4T*r{_coLAnvAyf9@zDGWlG4~2Hf{-Ok44vI!EME=DS4Q`8Kd)b5Z zOYt1nM6=(QZ@{6AcXxA0u+JtVk=~|Yd{k5#+hP`fobPgtTp6nCy~4Wka;1yw%6^WH zvNL{}J;k>|b8xQk(!GPaKmCB|`Sr4TQgA!j3(P4nq224%Ci4xvHhKJT(g<*eg@Z*a z+M+_~625Sn;4kHoHwpsx2psct$~gZgfAXx#9qy1zAKM#wu zBHe}Cu$|7AZBF2hr=tJmI1~YxV+|_o!3vW;pue)Ptlws!jXqKvFIuGE255HTX4EAR z$QgV$LS;XSue`K3Lxr(xh~*GzjDon5a6iq`B_7&3am%sSBOBToa(IQ(C3@^JG1GZ? zp#*-D`o_=F#TocA{^5@;fZPvd_7x8wHrPfPh!iyw2Yu76?7APW#ZGU)dhccc{t`8e z6wFM^hBP*wbN@8YL=e623&e}wCC(s>-8Dh+Y zWvO%vz{s|~AlyjBAjUjuJdGqblKU{}Sc zYRA#Q*05d7Hl^@03e$&;Rx&I!(!IwrO^mz7e&XawR|3}q`lNJvQ^7=}Khu9P^EA7z zNlxQt)5MCbfv2_I1=uR8+QhcOQW~I$$nU>q)10Pjrb*1VkZc_~Cr%@~_^{CBd zP{C3U?wE$h*I9BneC*#}YO{}l?F65ev-wkSlYQ*K67e3%DT2ky*^FwrH_} z*To0TTCmk=l~Lf9vd!Hn3G9&jspYq_#4^|;X2(s<2GJUxU{ZXxt|pD<_3k{S?BU~w zwPU_S>ADdI1?=Bd5XYdgwDIGi#$CZ5i`Y`H*2LF&5l(zBLNaX(u+S^9S`?rc%#V=F zB&Ui9BA(79B0&JRpZl zX!7A0o*X#fwUX;PsuGdqGjYyfy=v=8d)w|8W1PgnNlV2TK~nob7DNpZG%wFFov2)k ztyGIP21Z3zua)fo!BRCUEF0)!00{OgmeU>3HZWA@SH5BsXx=$*mhGPu)C3x9HBzd7 zh*0@chn+V|2#BY{h(b_)6R9X8}O9=ckhPKHS-^(192&B>&pFt{QpVwhG@>g8Hp|$H^xEC1F;oeme&9 z5YpN_&B8duT8u%k>Y^rIfiZiU`s^v&T)*s-TTJgnpH|LCt+TO!LYT7z=j|JpFg-H* z1Z*o-%;+qyGt6v{eRb5ZSUVUFw8(tq&}YH7I3hF1gFd(k+!j>ZBza|g!YruWljZs@ z-#2*m!1`9cGbpd}TLX+F(R_^xQAJiPQ9YpB;;m>0Pu|(ieSu~m!8~I^^ROtmZRY>mlbD7_KA%v&%S#8yMSBq0c5eK zNhsZ%l1>T$EpL`58G^Q&kyRAFFS3`eHB)jN;y5Yj+OvXqNpICKnQK(-=k_n(O3F-G zsHHw{Z_+o?7{2g#QorY6L_e$dC2xi7>CJ|R;UW?`tD-j2ar#Ju8%(FDzv45`!4v!g zwa#n=+i?=8&(lZ=)ahBu6_B(AZJKlEN})gO6E>3KTS zjeA|0)-CnDP!AtBo4iTiY@L~+UCr=9bJ3T|neBHOzvHWcWX{v=i~LDt(pZKqJ9Iov z#1^$)k3Hp(CHz`P>H#(Sb_l9{>l-Vp%U~Ic_=ZYf1s0uViVMxFHa^5g0`xHaLw7!6 zJUjNir2i^&dVI+ z9jPxiUy9eiFZLH@2-Qw+9j1ZXoMsxBB&V#?1D)sHHxpGoRqbQ~-1S7q#C}?d9(LM5kJVNEb7bf!Ib(#}f5=KAN7wFuQ+1B)oR>dA? z+@3`AQecDwbIqjt+p3Aso_3V(*z)4{vwm4y^x4`;)Q0tJq;|>O^4LNob&W{23m>O! zN3H8Kh%hIHtU}=d6~=5q(s)L8KQ}Aa3sW^uEsr{6xpR01*M6Cr9!joEZ-KA$Jld4i z9$)_B?7r{uI7Gx+2ylrh#$t$JKR&$rNduF&(hX4gv~B$(%t}`T1($k%A>et9)nt%PyH-kD6uA}L-rwvQ zFuHSgw+ahn2R<|l9IvB3z(rIz@yiho{|feK7eJ`)35j@x~=n`68O6fK21 zbH6m1ids|eV+v$o6`#;2R^>ku*&^y;5O`L0RPN2!IsutEC2Iy3?8Ei(s`T5{N=N&4 z0+?B{I9dELu+^yVngS62O*vx+l5a`=QO*zy5D=RGd2r)w!D!)ZWNK^1n3PLO421DS z8wGqpP3n7fLz3d!3fBRcc7M=GiJ;nw7~4YG2HV-zUBsu>6c1~}H^nyXOC^ldF;7q`o5HDprapMMM__u>U!Jx+=h=6e48T$X^DKI zao@!N=-m2pcL^afs@;+Rj-1=^fQ%ZqsEqpO!`05uEC9w)TJ$az@#f9ik;=pSO7$+t zT6M=P*u2IM9zeHew^*khqK8`It^&Yg4*~XC<5q(4I^n`Wr5?vY!?8A!Vzbm3uwAfQ zO|v4C-09dG*y?rX4!F2O1l$aD9@ctH?(sW&)?$`+c?yMKB291yY?tl>KVN@E!kep4 z?FG>g40Ht|QG03)7ki2gTlua<#f`BNKJA;p3NQfuMeGG=I0=)4;=J#qVIHX&2{do5 z$|vcaU!yTO$8RjUU$Y};58z;XPKVPD_ZTZ*VS8x8YRg}FdVcR`y`~C%UME~{EA8wB z75b5ey}(~x@ba$0T-M};5#C8G=lSuLS&Xc71_s2%Kz|V$m+% zxQ&W<9a}=7!FnWcBoOXGMG@+PC}hiIq`0!d{EXKqcrjJFJZo!{xhGv_z41nDZEXB! zr6kGl1Pog<9mm6d#$)<2T~DTnXxWX$I5jc@Yv%EA_81*}@S@j^uD8YB1v99O)> z_|Rfvut*dxO<$W=jvi6*s2Jg_2ZItkN?Tgq`6RbYFu(q+8K|sP8!2xm(f? ztwF_GRR}@g;;qX^piv$2hR@^Dqkx~pAmd$dsKMSU&c0`C=tX~0Z@&ZJLt*gd4k|#M zL3oR?QjdE>|jDgUEOe)oDbC2yaof92oHB(-;nTWSe-jN}Qrs7%m~t76Hn;1Bwtnsmb+CFZ)o(cuh&NWU$G_(P)jq-;M#fwr=Jg+yjtUlHfDk(GE zVijp0T>gL~*eIo~ve8>j4Y_*tOO>=ay}i9nT!A&)8dXcKcuP7@*w%ZqR{F@F4D@oO z2~&HMVK*y4Mp;3xc)G(%^1kso)kuP2Cg(F;qSo%o`BW*2!5-bjz2TNEB8dX^+7WYe z8mF`<{+hxRciC{DYv1&_F7RC+j;|TV`;TH5?pcX;#Mt^T^_;q0fkU({>j31Rfsx>}nPpMcrPQa_q&w=6D+G5Wn zAj@HA)p-1Q<#w~)b^H^Pd7|J1ODBwO`KJNYJ7zB>82oaB^dtiM_`#PNwpQ4f*Mm_1o`L`hsks92Bz_0nZVfBf$^+|frzz%}e- zfsYY7S{D943Q*JaD=GlyW;nC{!Ew+~NsQU=q(AKZu~tfS7qm|)GVP{*3fd5CS!m80 zn`O^v)oSDY67j3Br_35VN0X!SHCr8?@DCaIqW!q&Df&lFt0`fI4wu0RY!34JPbU@nZmca zB!-GeN7S`v(l%I*ZKrh^5t$BLN0%W(-Yk8D%N=W*_~!C~EXcbhWa6#Cxdcj*&niZb zK9PH>&$MDbkw!z*rJ2RIliY$Zp`F&OluoJD2Z%axF`c2W{Ns*(xZoBDv7SklxeZNKe` zWLbWOR@T3gh~1(VLcMYuYk$NOUQzet>P>XcK=x1TC)-=w$y{UfUZYk%aEAv^PJYh@ z2lqJxrOQ8BHX{bs5E!No07r^QFth( zwgt|uM(8{W=D}@F=;~SZo(t3LYxVK0@xK^{n?7{#gz-YKC`n+G6wgh}4l30pB=Cly zvTKAW-ZE0jpFa3m4rRSm!G( zR1z}`RP1n0)&_TF@@W*Hep4r1k9uL@9apR~8Pj5*G9*vS`X_roA>=qDUR>|n!XCkj zs!h1nS@O=(#^qqQ%~qST(BYYDn^{ykFbhFpr@CN&ml@-5H7d_#l(tk-Z+MW~9eSat z=K!aZb~MFcc-J106mqULyFxFfi}2QblCWSmW4I|axUfbIG**4!-!OQuL`zT}D=^Pt zkftptU@G9Vt(66?0RJ6fUV3}9V}JYlW?=tsECPTY5ukRWj-rnG#m7v<6cs_KRY_UZ z7(zMFR24u8qGcrt4&K!4Dw%EQk{X@>6}I8tvAo)~?AzofMuYT_nVr4Y;&)^G74ZpU zdo@8SC|o!c->-eyb@k-u?s+vq0MLJf>%rE^4(2}JGZWz8Tfc`u?E6Uo5Ai!fgr67J zpWv5=6ChP0_W6eLDYkAOk^`H=cU=_pSa=*^B%z=IQQ{u~w>AOHHYcnaLSk#ZHc%HVbuib($+y zB>ykFQkSll)M(|#VSUjk1QuaMi`%U8qSQ|N*eZY!XtlGtZzkFZ3SO?)g3R5N^1EJuzC2oM7N4OvV?e28FK@bj*wv* zFo5`E%C9qMV{``yWE7-_0Zs=puc{u9rN1ENK|HvY)-k3HFaBl(Db3dWYl+OsHVq-N z{j)|OyNFEAm7=2X(t%Eb-_4&+1IY24h99;1^O4GQ-Xmim?kT#W)Y~gbAhT3S1 z^pYnV&K3lF0cvxGV!!mMnNNz#YgH-&L#YQ6Lz&CxV!1}J7aH7pP@NW#yHgfYnd7%3 zb_O`QT>W`*AKTuVQae&YbA<&orJRdrx;uFK5;uBt@;uCG}^Q;E+ z^f~GWN87rC7a$a-sF1^+HAF1Oh$u8gKv|D{4i|3R;w_>;t}KvU;zXzN8)V>*DsK&w zq4qOPBzJQik!6!fQch*~bBcu`4(KFLkwp#b+10)h@hNDY$6XP3C9kbJ9=UXVQyb$g zA5wNfgluwL<+=s?9-GI=n8yw_t`pzPbSmTg4JJE1OtwcGWIs8d|5Z{^QZuXuN_Wqb zOoOa}PwO_Z{}ZW+nQtX|Of7^SZ&8Ww`=`M`ecwiLfQi=cLq`c+#CQN1-q54LDmd_rJU}(7idzGip!l+o&PqIgkqYJuaJ4&=zC4xCK8! z#b12{jQ)C~JG3ZQF5nCA+-bJF8F6Qbm**NFo}WIF3Bx~4`aL@K^7yONe)#bo!oL_O7G@1uPJfIouNl{zcTJZQu zxx#EP^5zG2Zw{o(P&^t@^7(gI2Qr+##7Xb8X=33{2mDtrP3plUh-IQ`>3kv68?-M` z+4 zd@O)rB&0!whCg~8)J**ozjwck<@31{AQ&mvG1!fP}2-V!g?qPu_diaul2ibAbPZ{Huz`4EXvV`8rmF zR(~6R{`jGmKp-at*jR9>aBON`)o%0*vp!qh)s_)L1|w<=Mkny0l#M5nZ(o5+kc<41 z?)`n8_npP0%F7ZmTZ-tjoaX#S@4P(r4+HR{6Ij7DZcqh@pdfcYjyvVsd9O(RY|Azr ztCGXGbsUjHtftvngZ$#RUQQ!-eQV5CdXC7Bopw2ZN4FXdhyqy7T*=@8kX~*!4G6s_ zM6Cur2~^5&V}rU*?{B1LMr`i>ZZFn1lhU%PPz%6$%a#3ufk$X0T_JG_kG9nxmQqR?;1 zvl}@h%|u)Qu($pYt;OoIP1AR_?s1x#V2gIHaYDtN%h6`|X)F+`pt2T_@va(m+ zgtaSnPG)B;A)kpIg#rXqMwK*eas71yQeT8+YPMgL(NSbM z=OUHiQY5w1YTLeCMyDeZ&;PNF@WJo_W4pxzDP_P%zuV?~#q~sc*L{@T_Vqfof$$@0 zhk*N)h!}GqA2-pqP1|hK89Uq1_`K& zm(zb#^a_LBUAiaJN*>B^_RZs92YhjC;N+dvSU$JMq};&a)of>Ai|_AhJ8e}c-Ij;g zTYJd$yR-VJ_dl_IH~DAOez|+09wNXDYQMtWA?Vy5NzG&ja%e#D8ML~P{%iSJ_->SO zO3>ax@YqUH1;yxQeQl@D;=PT8kc-*lI7>sq$^A>X`BAX~&2J@WH`(Rnv$SLmd-TzK zvEuSvSD(xIvXPa$llc{?2>2!%3l^E8fcwOjD$52+)PZ99)Rgg=086^)Vm=CcI=q5f z!?jKao$sMV&Nx8Z^QtvKB>c_xGUa}X22y^RYtSNNRNJUsM+b2d=1{md*2;XMuk&R8 zpq(1lYs@ph)_v41Sv&=oMIEsq1+SPmb0*=@w89{Ri@b%xT08QksGg*`f3IXE{%>59 z&VpcXw^e)cwfK>l@`B3U<8hgf(6eQ^8T}DtN?n0=3o@WT)1j+V^#O0gszNZQGaiks z%tiHGROroYIq?8$h#6+QS=?seK-kOWDsmE=QFx_nG7pI7sr%x& zvRZ*>)IPvu415m7IYX2BSo)r$0xb({!Adj*=P)=aA!)U}uC$!{wA@Q-N0TVeEZAPHC+yysFx(MT!ePD##fkH00J`pD=d7fZTJX3wPyz1SP^j+k z{u8a~>x+OJ%T^4?0NWYdqWV7DQ*&(%`px0MWda7J(EhPp!&f)3a_rzfwV}3&Ye`BN zKd_dY_-~EvAW(l=Z0o*{^k`<1fZg(i_=2dsuk`3UjQ8j}iU8$6uXgMq^m^#j2EroN zAgDD|9fx|h9G7~q)!-^+G<@*I=~XqE$}6Z>ZE?nobn{m)!wwqqotwpMUOVq`9V8H1 zy8z*!mrP&({uz#v{H}>(fx*zQr{3ha{>fjs18PC&A=g;e?sXN&iK{%V8LQ2cs9nFZ zo#sFbRWtgi3jQmDwV~K`GsWY#{;I&Cd;I09=h|YEtGWjTFkh4Yn)HFyrI7B;hd|so zsrK5my||wb=j?I9SmR1~XBVim>G-kf?gp3>Y6_)-^khc>1R8chxsqPmhunzX6)P!J zSxw!EBUX8-E7p2zhtc7cbhL$Rge;Foq>P?7y6WZoT!a)CZs&zOZLU^vXN0YQ;QjLr zK|UIMQ;x2(3khOpvO}CJCsl8>Qo{qwufP&z?Kt1(b1G!EpgsU-`AC3wn^J1m>H_MT zhb!auVG1L5cyf6~-Su}ifL$wA3oO?9;WDjo za>b5pj+lqFlg2pu#M2~rd*})t^-w4N>0N__h20Q`QSZ&@4lLnx6WI)ZWL(x0y=G+i z{dQc9F+_E-!8kPc+zPNgz3->`lm$S*UJv={!nsFu6jjrpw+CMHi7w@uC8}DuBbE(C zzVZ_UhkV>SO~)GBEUWHOruIl(Ci5S)YG2yplnd76J^=sp=BZ+?Z9L3v{5T4Pvus(I zwd*HMP<~%w@kmalnhZ?j9eO#LFc=kp0>WD@CRvhUVOJVRFn5Mnn8Dd%i34o)#fDsX zb%bcKNJFxu*zluVfeX-XgMnx+6e4(`t4mFV=v?_jB92u7q0F%oAohE8cE0BOrgveR zRqlrBOt*vD$36%h#(Ze!H)6YC{J2Y6ZsOF--XH&-3NLq{AAurIxj+%Ld-yGpF9+~g z9SQwei*mIhZaW{3ge&AeHbCVypW1Vhb{UarUg#dBy79UrW>sYg{-QF$8KscXkmRxA z>?`Y;d8Wts^l8EXi_D1FcOHaSv}Ct1tv*Ri4GNGWd>h*I>h~kPDF31pW*ux4SN420Es)l#c=4ZsPbN7s+W)bRdB zo>3u3sC$aqdE$Cym{y>3VfLMaEu9w$v}KwZ?abJhpTIkD&&5Vy$b`{NkYBhi@D?cs zFCKv{*R_Q!>sGj%d(H_wF}7omj3N=MGTG0&x#k9Yh})8FpEKR9fyZ}0vr zu)H#mv&ROr9jTA}#U?&~r4Zh>1GEI{PGLf?sXq6ytPi>s-pnfl_4xZWVJ?(8r!@cS z4c=%@3g5d%ElNOLF+%BH@4)?KO_PXm89PMGwL`R6D#aO)$o={sx*}AxRE^)?!yWmA zGjnQY<;e*JstGAoNh!Ip@iE$w@u@wf$B8NB>oEppM%LeqtaZ%n&CKk|4sLLYjLeLz zJJbp27L)+A?<7#2EisCb?+?QIhGCfB&ql76HYRjVrpBh04leW#|9_Mwn?jdeKOy4g zm1;R5eC8^iM!jlzedDPXo`YK$tcHITzh4%4+r`_`3fuT+r}!AKFcb4U`WZUTx}X)4 ztWHEUT+4W=Yxtw*08_;KDP$O4BA5ga^BL{J;Sz%jU&{J0+CI~8QRHL#D@8{;e|YdN z#E5#|9KB5z@zgxONY9CdWFl!t8pqeUjEEvIALCYndicQy1`yQvdAVI?ofpK5`_?}Y zF5^&}=wrGx?)xLcn-{|R;P&4Z?!{q9?FNs$$_iy(_A}CQ+W*s|g8%r>50DUPK?C`p z7k`KElM}ivBynh(!)&XzrV$PbT-Ny0ge4(oEQHIEW(S5$u@Z2%vKk=rLo`gNQ@I3Lr@x($rfDbyFreTM-C9ql> zR}LWyA!j!UhBXsnV^;1^QXD>Or`cM4s&7!GBU6|DdGpu7#@v0UE^U_Xx($o?l7=hw zmrq{FONu70TRca?P5?!-<<{|srmviXz*rB$;G*`Pc-7)9!7Sw!= zF_z_yN30f8E`kurNHW~n`sT2JYN=Uk`UO7P;pitirtJVjYrUTF-mPCus9nd2Nm!Ls z!Dn25nkg+!81;PXJoVeVIi~A|94bz@v&ljOs?QB9+4Dz#xc~^LtM+OmtT5Y!WlLei z=Ln!P{<*0VG^jt=>Muz3_1K1tyDlY(LcK|)a!T&v!(&Boq#}mQcan!(kckG5jf*Y> zv79?9$Y%({p%44s1LDQHBc|ujr`H_^WU>?a+Q`u*2*7kHLC6_>y(j~2rF51cQV$OFy#Arc$~x^1{C5Hb zW}9ap9w5Vrp^ze?LB}?B&D*wBskOs$Ytp)x))N$#cx$dCSMlHNi!he@*=f(lLL!k_ zP*Gh{X(cUMr0~M-k!KQ;V3@mB!$gux3?9)*oUoK`_+HUYJ_&2#m!#Aw7pRZwQk(L?(pP#RAxj|@XO#7+>ieU+?WD6D4$f?QZVpJ2NOgKO`2;izK zoQx>b5aa!FLJq)TZRP?lrGq>0Q4n%NkELzz7`|U;rM?1KK0#Bgx6Ty3o32{ns7Qq) z{PaN{BbZDz8bJj6gx)XQOdx2NKvZw0N|_^M+1jnXws=wEvsRd}Q{U7-A!mo9gaA)+ z2@^Pr=V`(jLsZUx+pG&$u63R*CZRx0rn+a};yP=y1OP}V)*qz2qcNoGs6^sa%h`>! z(S1ziTcmTuq@H2kT99vZN0`GVGz}+D=*SmtGI!2U z)G`0jf7czdxlyuDCw@E;)tQ7z9<$e%Yp3D;7~?nfHRZcN9 ztLI~gI&CtlDTYZQ+L*wyeDwzssatBu2aq2d%Bx4p9U2DDBp*1742s8fZkUE*z8im#RRZ$ zo;g~`fJ}-+C}0t$ef@F30}U?YPQ0>hRlujez+rb7`9VRJRERi&!HDl~4*@kumg*GM zEF!>hfN3wd7mYjDikgeg%O|K9Gxgb`kaT$tV{~2-I_D!okOCrSNbJ8(G*-J+3tIcp zY$B=0p}M?YJEpf+ZpZ&r zb7!R-bKG||2Y%;{^8N4AXplt)Xg4*q&ATlyXbRBE`)xu+C@CFQ7!Fm3gaRABS$t6p ztLZj!nfzSSGm`Pk`vxSOe6RtKcr)QvC2VcQ>9U#e)a`k-X1{qqG{vy>1NCOS2k{UN z(`u$J*LcQ1gsdr*J6Sc|MsX-2Mm6LQ-N~>)22pa3fWh>r-G&inndFccV1c( zWaV0Pqj@}?POc9hrldN(3q9N3vu!g;8rF+$U2(3>=Rh(OdJbK_--bkHBko0)doN@m zBElCVH$jgG>0M+A26AW<@UdB`W8J9ES8pX$#*>eI^E9UJEy|TdRJ4SNn0)s30*r4D zTL1ci74m4>k0hiVUjM*QJ|VbzNI=%o9=u$vG}yg%Z?y^PKgM)NyCcS)s~dK|Y=IAd&KI$KuKU#&faflv0Kql0U~ingA#{7sgFO~ACa_dyl<{H+LQSLnewtoCbd_b53-4>o4SR) z$LNt55WR92EPZl50U|vTOyfg;5B5?IP=@cMP+w5hJjqEL7wxc-lr~9=h2wczMer${ zDu%b|XU2#_z4tB4@QAac!~PY@M%g&!UE{qHf6y~7pT0b>*cy~*##73NPQ)i>s4=p! zDelPC^TsDUB(rQlxIhn<8N}Q&@xtFUq{7(Z8=J785(xh+AlT<4jGQOjF!-tP_vOE6 zcK%ba$0TsK39t4lfTg5>umHx^+M+iX?BcjIQD&A?>o)4v>z_W9Oj0svbOD;{``PPn z8z{G*H?I)euoKA8%glyrVjE(CJzPftC42#|c>o21G7Lo|uMZVRB)8ZQuwWT3GceU0 z7`F@{Nil>As%6pPn7_D4dF#lL5mDB4(DpzI^}Vpz5Yk-$Aj~_3Ii?H~XE+$TAvOH| zl*<5tDkWcLdyrPj!lLY-4q{`9$6GNhxio*;%c28Qu4cZ}LlGKSQTMn?Pj%bP+UwYI z+0R79AYhauu_$=|!BB#=0!970l)v$5@_S9T$@~sBJm5d>f7ShQAu>!N;@u$IBw% ztR3gvmiYh2JuHA>@ip{KM1`XK-;;kIiVv{%Ks`Y7`)87%Ne&tk{Ns;WN%~(CXM~@T z(~U&=g&~4CvQdqzhVHTu94u>TD`4k9Y3+**y=ite?>fj;uRqer!kzPawfPT=`3hf; zp0B*VuUk1)eCB3u4H+A?;R0_*8FyJ%>)p<8lb67+Y`{-Jp3S5>aQEt5GNJ+$V`P9V z7Li7j)a|XOo{b)^o=&lX6efbPa32!Z(sn5m5*Y*Eg?fvR}|J@h+{LD!pK)8pApLlJI z<8sp@MBS zLffZfwXeLQx=0xk=ms|`0}YtrMdOZa>M0Hpt-dG_m_*Y`>x;Br?5;X8h0lQGB;U{` z$11i1?C?eYe#a9Vpg)t5mE9tYdi4il{KJS)u`q($TVDt#nwjkzGCT1kww;Z4v%P>ZbUkj*l~G>< zC?iS((C>-Z2?u88;n}T)7N-UDW3GBHTx(n&1=!+vX#HAqQ^C6V^YIe2Tb@L(nvJ>x zo*U(Icr4Yia7%y?EEFrfoicZV8$tMq&~ppe&;%5>`iEN8;sUp;)%q;nLZi;Y!sWc` zgvuP&mhguo|K1ZGcO_tGxPEI}iv5ZTTwxFEPu50pc`VsR0T#Qfu=9MOZ8VM}U-hQp zij@u00v)nE4QJpMbt^arjR(32^rVqwUt&*lKK+(8C%kD=4wuV8Es0u=9lcnY$f( z9w@LQ9CP(?t9Z@a@iFBg)+^RYUF$~#tWJyzJ7a%X1}$hW-)4IcPFVtHFI>Qi!Cp#e z#wW=cSTG~@0HeYbsA?b`yIH%cnsM&3kfeFS~9`V&Le9D`fD0PZj`b}9rS zH)ctAUyQ-v`v=gz+r@0bI=^a3S7L)XS2}0Ua4n8^uC3vEZ-eIHRSw-{Ps^K^L2_AI zQjwyUvW^upt;O5z&r=nmmj%fuz7Zb87UvmzT$c=M&Vlh_iqikDq^FmRljQ40uN;)7 zQA5@}GyQRr-D-tTW-$rt2egONPSLF%9yZK~ zDS45fb1$IRAL)6k`6HJ^0pQ4zc z0tvuvEEK(cCNLctHZHfRgr#M97#~qV2w5TFp`=bTQz*p)y=%A9J-$3VVZXgC~ zDGDzcgfusAQLe?aboTf#m3gNJRir@X&OZQ|ZJ~!hu9Q5w>G`nKys74~cU61tlG^nr zF8iw%$HR*aLH>*@Ho>F+RIq%Zy`L#Jh&ozK5A6nucZS&wlA5+y&q9D%jrA)|Mv0>H z=?CD&$ciC1FgM7CSnQoWM*tpjfDr?z7t!-BuHty%4SO=?)+l zR=qd>_C@+T8==n?6(S5qgG8^mvP-fKfYuVZ#7Lw!SPvHMl4&j=iZrAuLj+5jqBA z8qtk0)&%W|)~wq=&JMy(q<)X2(ersMarPR!bFK7_i1!rV5!Y1PQ)eTf+V2D6cTY?d z>!Hd0tvbRX8SOA)9i=hU7_y$UMqJ$P8>*g^#@@9nlo%8avL1_zEoSl#z?PZR>BSeK z&V=9;))%dR);%K{bmOHGFSQV>beU~peF-=4N~Gx6mrW;Re6!AcS#87)*E;5K1wK%z znRbIJE&c2?NTYmNUbl*8HLt9KZSFVJ+H|D|{$avlYdec)LcuWM!aE-L>)(!G!fV<= zl3UZ=M&Gs`Mx?9h0ESsz01m_xb}6nUMY@%hnr@X>jNwA$pJ+nmHuk;E-Z;Ixh%9VB z=$*Ab*YfE>I&dfz_-B_oO&V0s1@bEpljCY^SDCwX-h!kryrvVZ;@28joEyzmQmZyA zj;!=<%oLDlMOJ0 z*0Nf!S!cruZi;h;$2b;9h#Ixc*YBaT$Z40D=Wx~5>QhVk>$DcPaJQBAQ?es(!lWwJpK$ybkZf`qL-awz#h{Z@S3?|CyEU@0#c3j3##kPCM z*pGzDZ78YrJFaY_D4&vJqdn5_G+XYChHDps5#dfrg(;nU8}sCB7Rr)1bzB7hDg#Sd zM6SCcsrVq+XUyOM>qcn9Ne!3D`k%RCsib|lD=5Qn&z9rQXSDd!+(MqR%2f4$rrxf#!=?ePi-*5ohRB6-l$jm;8qr$@F^rsI5(aNnPdhK!as(t9VIJh4Z84yIH^1 zgS42BluWur2jtZ3Ez{#oUp+miXlbbyJ|&KE|lG9YOfiJ@y-Z4Qk8qcGGU>V2t!!$y4+oI^nz9(4;!em*M{2>3AJKzNKNsp~m_!Ns;9oUD{-VO=sVX%N zKh2ZfB1z`^vetbx(#+7ycIzAe`rBB?7u0wT!0%gq#^1-|{`;3WBS4nr6N&i^EZA3^ z<1K*y5v}A{wgTDUhxrcx8NJD;v<}q^jiPK=zPTfPE4PfEiPX+glQ%JOU-$P8kq6do zQ_k4lA89au>ziYTGf1|tTn*YFx&=p7dY$)+5_$xD9Lm#wd&fmZenzHZUiq8FISrse z0c=y|SH&y{6rFB~3Wzn96$Vyn+T)Q`)lMZD=zrnSYWxfaAWKc#c}@-SGZAyFP7E1#Ud5kjMK-?ua1bwI@YmEu)o)lWjrWTjrFI3av8 zdm($n-u)lnEdk)`6~{m2#uk@{k<6GohF#XxfM@f#2i7e3zSv`&%mMTc0JT0qcjF;5 zxCXTzLTxN0&L1;*S{Cq^m8grPFyDiAFn7p7f^sXm7qOhoaS;_kMqFyp8`-2{|2SDQ zd78~nqO0PNI>v6lnAKcEcqmbiRkg&(8u@YYA6}Uq+yd=kF|Ia8WIu(cu;PW0sT51r z$uBbeRm56swt1&C@(gTO011{Ov*Prqr?z{qq-4m3vgWjObFzByNTpTAymZ)^S$Dl) z*0LlAWV?lS(ro1=tu*I4J_zlBE9*{5mgXdNrWKE#u~*X zVjCkxx}3*W5<^Nn;>jMZQzu>*`M=h35^rjA2H{nCl$->v=D>GdYg6DI+lr)C4fS!_iAi?XQ5vt@b<-c$;Pxg+%kJ=s;e*x(M>cAAx2&6X?LyEo8>vms z!M^nt?wBe?0?g^2h897Ghb}EC`eQBxwJzwE zL!8)py35IQH~`%ot#+1!rhj$Q^cy5Y_WXNGWaTHp10dF6FwGVoS9k>e*A7==ItroQGKQ91$zJ_nE#_c&H82MZlexbHc zN8%sWXJ~|kAMh42X?2Q5!qSDrZC|B*@TMJRs5_)She1m)g0;@d-?cgK{DSJl8~KXtpg6pm4KGyaejp6 zz5vRb>8SglGT~EMzm$fpmUr@0m+jQwIavbf9EEK-7q=2NV3pe}m@C4Y4P$V54I;(4 zF+cKZCJ*27oX*7+s5mu}qt~~};!=O2yH~0A1aPL*tfM#Xr(N22E^HWEe#YBfx$HKz zNuMC+h;;9xJb@m;pJIPN?22ygsvco(p8t_eNY<-1xI93A>9rE7OFQl7X7m>e-!Qud z=}_}icaTem!t>A}?w>FN_-KtM6wo2k?MMeCa|oQFNx4_gEwr4aS5LnAxG0r+=bZRO z3*hS3N8`rf9OWe69g6E2B>5H#Cz=60-?3KnRCMUK+(MxBoMwA^#xij8Z5(X*ZXK-9z%`G3bjAUc#`#>8q7+0$Rd$ z>zwb2CIv;&+1h+-g=}O7zDBuNT`FJQ!-~v6kg!k(qALBeth5Q##xc(udEN6#nq^aO z|77^@I409ZXR73T?p&N8kSYjJ(v`vahA$184Q_IBjp@7YQB>kocBuNK#1?|60nl;0 z&+Jkm7sF<1W)x&!1^)^ag8838Ukf7K*?%IzSi0d)W_288kMX&GzJGkcZX@dZnAD#1 z1B*lBLv2IO%T#7FnM}&PZ6R8ap1?%9sgk~h-#o%wck-fXK(qA&#SDP8<<(xHb@xb+ z;m+g8u*QaC7UTx=`gF~Dlaeq8aW-iH<c20{p}k;ER{?upP_QmS~r63z@kPOs_s49Whm#= z@4Z>UI~K31jHr6L?H9Ze^l&e(*eRH!2@-~sa>Q+9UVZfEDZ$3(#Oi$Q;6Wx#W{-g0 z7pXsiD~Tq)Uw;r#kHo>ApCMZt*=Y+~!xX6K4Y`2%6BxA4rnEwS!pBj-m4CoUl0He^ zXtlcJfd3=)TMPRJ zK7;jQMwOffL9_*&a$3KTw7!?;Fm00-4ldiy{)`6x&Lb*7VzNxAmP#p5di~S(J#Jrn%XXd$1H2!_0w5e<|1JC-sE>%X zf;4Oc)l5{av$9zy6t*$W|j7##imEFp)pt-57vj!>zoX5|uQqdZP+l1A&V z;wlbksInjGv|TApyv+{mOT0+DB^#nlo_IG}85OYPRBiB>)zf6627cc+*$GH6d-v;2 zr>Uw9yIv9ti`xywGD&aR3LqfT5{GC@OyrqLw!xRj7VZfviK=MX5Y4!@d(`Y^sUE{nt|oEWmlPs30NCK1Ts z=2U3mMgM@!;&T(mCAVjJ{iJ(OFskA6MBUw5-wY9L+ki%r%Xn3|Y1=ym-G%Cu3XU)$ z@DVlaMfEckT!d+Y4sWk~+TdKP^Iff}*Y*CVM89d-<|HsPiYo0Q^lwubfAfe`~!@Rc5nwOn3oTo%kD zAYJ8r9^m}F{XS9lX+D`KzO+U94wGx$trgB+SGkMn}rMS6opcJqL(d~+KClGK?8rhVMBTs(R7DY5`M zcMRduV}OXR1U>@ga>I*JpJSA3Z&l2%8++7x?7*{zE{;VVc0Z;H1}ze&e+l$((NR2t9eFhI`UTdMNhV*iXJW z4J`AktuZENy95_ZgnM!u3h#TvsOvi{4z*wPULa@99>z1-kn>#EQ7|ReffqzcHElZo zA>aeQ_eZ`KhGhube2hTAU?=0dc&u8=Sj=|G`z3U5+v z>`SMPMC(#~&YX6}Z}jId5sL_Zk^APhq?n!7uSN+{U5n1#Xr#G0Zdc3ej`n_A=JG$ zC?h0@cOV4$pA;~>(7>nw1Ggr5j#X#I@PSBRxKo<*C=OntBLTpS`~{RVDyVghkor;> zb0=H9y-r||@5<5m3z4-NVXrH3>lfw6eeUi4!MPme${OUvmgqA#F=_#-@lu{r6hQgB z75-wF&V@R4awDL6n|e}dxoErQIK3^}?(`f#8n^3DwzecCW^{3Wx1N@ylH-&G?VGo# zQk}{^x>tYdTI=0ZG|xY8bz-i`BS`KSS)zchcg<{3f=9!@Q%`&Nvb%a3ocFg=zxE*`Tm$@OhHV%qX57U@^GKbWTa0tk2(rWlF*X4WOj_HHV65q>7H?<>0Jac?Rl^vR)fu_{}rj6+)3u&9_3r z;ou?se zl3+T8-yUJHzdD!&2(Dpkl}4^QuI4{<3u}eYC?l#T6gEF;jD-sK--Rf`(PdkgG{?Ma zU7l6l-B$wr3Z=Yp-rK90mC-m3gXuH5LSSmUJXznW{bGkWL8intq zQF1TEbL~#_uG>j%EB~u2;y+wwq!61;0#Cmd2sTPXO25NTr2o9hY@^;R{muD+eRsR! z|C{r1V$t?bQHZB5k^ZJ36ok0&prKw$TVBAbxZl) zzXR9?rlda`_&iErUD4vm!a$&iqXn<5X-!(0OVAPo+X@VLN8_zPy3)=n>Y1juqlkW%|zV9fn<>g*QI8zPD$RICJ1c| zLRkYK4?7xD;|Tig?XwIQY~moy>?jAG9!0|Zwa~D~NKmq+`9)lLD*v8AFvLY68V^af z3AyT@t}8a>+AE2^b=wO-4g}>7Ead{sA`aSgGm)g`dJv;utRJ zFTBHa6L}{|6n?|x=;P-c(G!We^vj`jD%<++=$sNtS5*c1$B#1VZ(xU(AQ+F7uwDcS zfbvmUX#46OcQUu5MDqN-L!t z4g5^6sJWUFiYcPWwPvw-^}@Bzw`S@|&vwiE!usrt`~z^bnKmvphwIJxupG z`|>&3pNP%jkr#D-o*=x){Ysh2Ik>|Byu7Ox`~yF^y04Sx=gymhC(lYxWt*InN?9yO z_#^aKuTcDfZH@iwmTS0OzJN==1caRxKTjDccKk?D=kHWO+i(673tsUOoL0&nW7qVa zJy!ghITLtrkAQc6ReqFUqj-7soBbaOadAd3ndBtOpuBKOxA#A$rk`E1s8dak0BP7M z$0|VFZ6(XGePwzY>V0J@STm?C>X&hJu7!547v7YqzY2c_%}*PHjx2Sm=9sfnW%+7B9Zlm-x`_3_w zN4K7{+GRH{FOAs@kk{1FXGLp-0PSk{YwS30TYrYct!V6;xf5nXA0!M(`>xh}9j^my zd&p~GzIuXul~4FRJA_AqhIzwe$}M^pgE32wwixnrf#wlDJLhC zvbtD^)*^v|Es-`@8t2@1BU)c)O|yT>SU_h`yKWT?toptWjV(dvFPZn$7Q3@#Uo=4_ zVM*q+WJ@ab8te{EL+IcDO%F89m1VjucEDgb**w*?@hme{b!ObM2yNTN%W^g|WF;{S zrs#;3KKd*98_XmjU3<3x@?}iB$n=qFPF9^bzK-QjET#fmRrWx!3Zt9m^5)hRLtL0c@(W0ZpvUs` zCJ%0fk_gVOA#C6S)f`#kuGPuPPEZ@0QSa_2aL0nPEpbFysXco{8^gLzR5NU0dZBJ1 zq6S6SripsdMtm{jruN+pqHE-laUk^-s(mFp?m}kPiHmr|54+{L2IYRG_X71}AqvVq-#$n$WNboD55ahA7;Ev-1L$tagu~m?gAv@Lp&I zl-`eKH6ZFwhdObi(RX&hSVtIxWzCAHb1nPf`2|z3h@J;GGc5^m694e4*vXC!8Po|{wy!fx% z|JB%6N5!#hU*qoXmf-Fd+#v*a3+@C94uiW7?k<4?@Z zcOo7I2RurI0b^nBhXv)g^~j^RzMdZDHWilLyubw*>R>hjm8FF+xP}^puur|!Xddqw zR*m{pWK3ehHGLb@x5H+qm z-zo~Aa>@b+g)_v64|oR696ng#alEMs47P8FEBE^`ZFpjqmSin5cb1uzKOr-S(^R_Q z^1ZPli~H*QXY*JKBaUPsBh!$k0d9nNx!JZuL#zupJ!v$&gCkY^*VSg^_fR)8027Mi z@MVGZ@W4bSlLws^ZJj>qEm+%H-O)jpugZcZ*{6_5xXEEgBg>reLxyy}O4<1`!`&}byUV;elLJBOjGFCsU}iBxL~G#RjP?#5zxWfOvW%y~*0;TT}r z^rZKqzE!xfTk&+Ae+$t3jsXL+0+4aVclJGD*+XYugkBB5V<#5A2<=P|nNGNqH0e(a?H>v7h zfiW1<2u1V!S(9AqF^D-{SCBZHJ$Wk|P`T+%U|@U3*}Aqqeo^P!xLn0{3vlux7R{-i zT0gvlDH^oS1H&NdAUBmP5^=B6PsmA>b4vcL4drynTU$ zV`}c4h@i>Y$^&JVReAX8R;5%pmf@~$$5o+b(@>n_IE9qmG=!656A%>L(J9>;U>L4 zMNvIJHHji5nBnFCw^QPN= zbLBD2&+??WLlhY{T;bv!>nAsRzP&o5#=DH)W?ic1mM1bjUB8cRqA!5cEd}~J?$epl z+Qb2?Xh%S|Nnwd>C&?v^J0dqxnO$y72xaAM8zSA`i6~6lQYMC2cqMrx8U4u#S>^AL zG7EUHQ~R6N4p?gd-gtdu6LWpJr!7u1c0mD!!lPI&72BN?48Pba4ub-uz&nr4^12o? zRb#kHklU1g8NZh))$!AtFm(BpNa*n{wV4$su`!_{d62L-nu$GrkX*V65sDsG>o~h1 zEV!q!NiT!&y|b-6Pp*z?^;F$53HfAZZZ{(w#B692l9}=Sq%F@hqD#-BgBnQQ&GKuKNPknBtYee0Lk0W(`VN?j53@@ZK(lH8A*}(S9OY0$ z_2Ii=f5jzC)tSmr8u3iU9$fO_m&o_?xrs{K=8wl@QPvKh#YjJ@hD=qV-&#qee`O$C z7&$zah|uMkE53~yUZ(=!LKM|%hS@J$$$)Hr!JAofIp65cB0pfpuWmDwIujh{P*Ge= zLzTu)jsO9Ke5@-)_YGS@Oov|p!gWE8$<%as%*mn;B+%NrqvwwpK8)nd9(at0)CSY& z7dP=gOPT@ z*ApehQ#OyXt+V*${+8+@9w{VU4~AC~hn4HkVplZofu*}(cQm9nt&ge`GYa|X!&#IH zq}Xh~rfAzZirTZo(}w99ll!?6ZYFtj9a_mhkw%&G4Steg?!Ee)rnpsxbmy@*6Y$_R zsKEpH7D7c_;O~>R{8p}*gJ{Q-MR!@On79Bw2kB1uU0q1g?5)@nNg9g309i^doLP_; zw4BG@l~sql7F2kuXjxD#nvXlPinUzTo-PBOTEqvHTK#M>gnII9A$jg?xJ2SS3dQ7r zd$jEv6=)*0JBlRVl742ZkOyxLdQ@g#nf<=|8Km>&Y-WztI}3QWw`E6p%ZuuZ{9S_;N!l zN~#2#nL`tkgBO$kAxoUUF=bO%Rwhe9Z&{k({mt>F?aXMVGw*33{VmL5Pt1hA4^l_W zl%Vc@<%Wi(P>0yud5E*XbF|&2&;;WHK%+TPsA%c(?QhC!&)APEgTa+kb7yS21@%F>WOuH{VA7tZZlPFTiv=@a^z zb%fMq^`2_`L}!VNOKm?eF*v9y3yh}^i(Qcfdo^;LeTvl?h%KDHO`ee&@7j5!1%xW8 zj+>j7McFgw05bHQh0RGM-)_~hG1t=)fbNT!zHCX|9?N zF#!yjb?Hl2z(XO}T}^f0Ujz?t&FU-ERSs(W^hTrA`kAh?RVlr#E>AL}mmo!W{Tr=A zsB$mA?>%xNSIR`AEUa7-T(|1g1YoL%T9Ae?bf~n73w#KoYSd)jlxxw-&5-e-3f!k1 z%BMvG+!5_EC|bi={DQk`7?OrL_mh9WT)rjD!zF0`jToN1j~&UFD>H0P(nlVYnZBnM zlnE~WWPRqNzo>FONH`q&X{x~{!c9GZbRuExq}UYV$sAnJ8oX>I-Piwf8^GB`%#^_3 zw*~b3Nrl}`=iMJ}^og8SzJ}2PtMOUJXdRj_Dk+A4~|ajFt!sEH+Ax6-c(%n_|ahxWC& zS&it1jobUqaQe}m7_smaeqz!a%F^7p@U1Y0h7`8>($jj-+n3(i2YyG1}_AKVKrwo_Ds7Z!GG?$d%YVdOTV0 z_*4wJU&zkIO6;}zg=h`#9yQ(QTgx$V>yf+Z+6*Dcl10x)B4e%??x}aHQrZU>{zo%H z9m0sbdk;R%NMjs_ZE_<9ZvY#MYNp<`oHZ=D_#X_Fx6++M{jmW&m+9Q9gNAby0;sXu zA!p^BU`#b{Z6!#gDLkWlEAsa>z+wQf6JB6BcM7Wh&q2e-bG810s;im^mUN1CkMxg( z&WO3EzJk%i28P6?tFTpHJNq2(QO^b;T{oPhsP^dZ4f5-9pTvBR(C?!ltB z+c$5O73vxW`S$@Li)uVp@Dr5ub12S4wqbh5MBf!R7CN9au27)h{qe=Y_Ese zH73?Vs8MfMo1Cu~eT`7-IdK9VGoI6?eCGgre0ku^xoFKIh=1%;n=RqVxDzqqgL3@Y zqVi^E64^os^`3BY6{`6cQGx4Y7vR6%iYPBmy9TI&Q~99PP+H)qw;%vy>pAe;KzO(Z zLr4=kj_5Cpn2?(n%BtiAHzbbf9M;O}YE3*_!Rab-{^nNCi#$v=FO0Mk)%0CMS$=h# zKdZdQIPcB!8t?9^(^7Uv`y-fQe>e>GG(mOBKDCSnYZ-ZzJ2DlHYJwUA+s^}^njwL* zO*gI{w?16t8uG^NMKi$4=kL_IwKmqD*UW>+okq?x*139qI#>UGpGe+zpaaD$-E;3! zV-``|x)X2OxH;FPv)vu%Ve%eU=R*WJVAu3x#K0$rft!Nn9rt?8&+cWWKr50+q6mB{ za;nWk+Cw8?N96(`IIhUjAPbwGd*7Pl^tOJ_GF;#mr@9#zzOpWW!^BY!hy>Y=D^}9j z%`%g;q-4pKL%`)3>)9*Ilo!ExvleqPez)3u`dCVJqXFaUIh#bpOd`{#2E$TLypaMN z+OEP*$b`P5V}DO%3Lj9v@hPJDu`})~>*6*$bHw*IA+26|Bl)Ch;grGK;`i*Qb-Nvx z9vQ3ihAM3RpRu_C7n=R@!A-`$$a;<(>az{cZ&yUPdE7Pdi<&=NRbA3i-m!Y^%^p7I z8g72Qt=xC%D6)C>k52Zg@4IOIbJY=T{C&=#;F}RJOH>&Zi|_k3 zsNr^AB38+&&;eH@;^FE)CI${BQi32BB$rNTHlfgDF-G=y0by})&Eol2%F3ktx&ZPbZQXaYHoyp%#fjvuc@$G_~>q$bUd z;z8{<0~$EyO%42*i3GGyg9WG@fB0j;IgMJMP^UMXOq1rXw#aBoyGb3|Pk{Lb91{uV z8TnW<$;LH(rEf3)7V0TcoNTfe_AEEjwTNDH3F_8qca`7iL|}L9`>BwS4|vJu0$D{) z0+n%5oQPJi6Rql0N=Tt0IWn@T<(DajB72Q2y(xQx9>`#xn{=e6I)K1aoDPa>`VX_c zB|E27!%pjv`rY^%qZXIOVHTYufw^>ATh_N$`eH05b2b{~rwuNF&q=2|qAnK)%L|+B zj9WCRaIVL`B5R7s{H8Qs+s)mTyzk(kjQVX|o zV|;of%$36I)1?n$w|rS2)drB?vsQ&iAEr#5CFDBN9w7CDEc*cbo_Qo}-!p{RJw_yW z?ef!!_fbSoLb~|*mrd~wotg@7u7=%P7?)utk6m-9u`P>AcgDAZ-#s=WJ^sLxPw1D? z|L`I8N5OE^L^@%iMS333SFKAthZ?WYKweikmz*49gruAs-ax2n*1-~*k&hV{cPL1h zjW9F{^@hX1D5IZ%X8wM68P*uZD5;4vF7 z{tXT?>u-jAVy81vZY+0Ld09AL9hogjL{4F1vLUGXKZ=EqO06TuOLga_ZZ{eCqBO6k z`KM>Tp=O_8As!X(iA?jmvdi^tjO(^t-ZzxoY)-W_PmwQUnqvJ6wp15~X35^e(>esaR(AW})KG0jCw)o(6}Awz_f}*^k6eD4(k?fNKXYt*4G#T+(ZjTXh~6d!z}TW z&Y#=Vhst)mDGjJ($c(WPE|QF_yjC#~uCvTob;t3{X!Y3=jkKb0hqd1&HrS}p&ZieC zsmbf8d^OEiR=T3#Zn4>eeXBYO1?bU~5m4t_E?G>=WseMblj^-K9F~mThBdxeRFo|J zxx3Iqst)^WISzi2N4XZXinSII1Kxdw`j=EFUMxZQddS5hBkjNl%#BJuxuRBg>uNi%?ZV=kl_JqPC-O+UV- zIrlirB{g;=dWhIkV4WkIa+f7K_qkO6-XgqQC|(+*?W(v&9$aXOThl;vq1ePx8a_yN zcPix1Ki=L1=ymIBFuQ-TCjwm8NRBd;xN>H7TL5sq{g&d-xv20Y6&4fEnTkxmf7I?Z z#y=H9k4sh(R_bRkQ2Iun*Q;?YZozhaE-tPm3Ed1QCW*Z^%+gia$%xNog%s=HdulS{ zFqnRdKjz#=|Hj{)&##zH2kV-U<@m?RgluY|w#4l5b(mR-k6vA_BO$&ug+DSPW z{^+KLU87DnOHSPvn#8sWWa3%Q^(5sX%BLT*AnU3g)IG!5Oqm;%`SydnBHY>gekQRh zTx`pyw=)*MqpCbzv-<#M6JO57^x1+5h29SR6&L)!SO#jG`<1 z1Jf5*tbz{rGyj!r`Ae}SvlY7)Gv)r)s~=36qT#_Xh2Qj~_7=V##==X7c_9Z91;; zvUfatJ%fKlr>8YhR8#_)e~6;qvt;LO2ZyK2Fcif4X7w{NP&w%MoR&1wcijA}F4$}j z2f!j=cz(BW&=4uA9=T3gy$CGT2s~t@-z!DvG14E|Krn0eN%pT9Vi2qJh^~sCW$;@RPpYcrUo; zfNIXVXu^PPVEdO^%j8FOJ!M-C0Ye>CCV-{=ClvxkmK5fUWO0%YXyO?He$Htb0=TW6 z+G(?g^wE=6_9H4R2|_XP(ZF)#p(i;7$B?F6BdbY5=+ZAMj#dv%vBY>Sod^CVQ!Igo z(2&Je8bl$-azh07akKWsH3(E6$|};SxKp%#r3}oi{%9PR2=(RAXlP2Wv0-lLxdymJ zYyIYZW@8u>RiPuXa)XM0N6;kO~mZQT5B$iyZtxo@apm`zXJ1f^LnR=8Yupn_5tG z#tNgEUDBSq*fM-aM(hr9AKz8uxAoNr5B}GsdJZWZ z{#K=``PsJvrJ^L5#IwnZvjemw=Vgb^@z0y_zY-WWV2kt^PCiE5Tez9ZDodZbJ0j9Z~c^VVb3k>Q!C zr>E53tz{lSu6#&*`h(kp|1`t-9eO(WF>$J_vcsONn}+~v>hc$XfKWrcRJ!AHjnmYS)u%}g;^CqY^x<+$? zHI$vt&RDfCFPf5}Omp&EIA?H>;&zmIJ8eC~#@)6$r!+g#4We8fKN36j*1LD41SzY4 zV_B5Nx;~Y$Sn_9P^U_TcNIa?@4SxPLeNfPEVq_Nvn>lvu%Ok&}eV#jfU1J&t{T&e}`@ z5_yUiF~JtqyMy%T6tY~l?@t5pkLW1?l0Kx93L=apNf!*LQKMviW~`#$khV*Lk0f#) z-*Qs)y2Iy#Un1OLV6zu>2;WU?M2J+VeT$&}u@SbxR-u@+LDvtFC4f|`B(cSwC(bFH zht5Ns|GuifA zZ-X&LpjZ|fd^PlDn*`4O?Wfn1L~1B$Bw(zA44_h;R$2)Q0s(%tVZYf% zhP!$t;fV|N2@H_XD4z=j6va)-bgwyWGu+v|IV9{97$bHj&cMictAbd8oV$itY}56S zK)ca_SFymeEduXJtl(}0QHg-g7c&wFAV@833Sjpql*vsQ;-p@lg-4auG^tgkz=Lrh za=XUJ@-!?80x)x=7c46%?{}~Y>;jdz(pfp-3m2Pe4v7o6+AA{qL$D;4G_Z}rlr8o; zJ*lr?96GGtm1im^Z1Zg>u+`mz3{EFK;C0t3Gy_MD$I>eUPXLJBqb{2PICqisH4Ccg z!WJJtr!k6RGNvCdl$)M&5fO4$8K^F&$quNa*lYG$ll+7m=<~dQeO)_ZlQ=sEkYHe5 zz)oKYfUOEhHr>ar+TNkYLK-=-eqK62SRYQLOc)IjJr*O(Hi2utL0)ru)?{wM<_jQK zDEGO0tTrI2^jgRKzVJC;C~w-y)fkSds>`9s;AEr;HAj7YS4 zo(rM|{QKk6UmniEh79J~iK>0du4UR^bZ1qxYSdMtRx<1?8q~@vwD3il4}$HOS<4kX zdJLp^c2>A_&8*TzhSORIdhiYBt!8A?K1(sG4PgwYn$|Uc)oxDVj*8D2LDQc+Nl&UT z0_c2hnV{kHW}_ht&edA@qFreXm45ja|M(#E0|5=Ga7*a4J5I33G|l`M(_PYqW=ngo zF_xdvE1I_WyoK!oN8)OvR^*xG%F1`YZtYay53Tu_{@HR+vj_jY+xsT7xoin z89HqTRbHhH#*UZ73P`sW>7dKil!7GX2M$0&U6ZAAulc~gzvIdFCau=O3_q{!$T$|! ztcOk|EtH7pr1L#u6Lv;VsJ~(Bh(iduI|Y8LioI4sGCg5I!U`x%1xy6 zoSQ{TOcc|Q+erx}G%gF3ruPV%5MO7tn&k{fkG{9^kF@M3(P`6yRGcBY4GOz42`W(R zRm^Fmu7HQ{$s`u0>0A8Jbg~pisT*H*CNnC6d!4#;br9z@M9xxTvb zh%w^^?Fs9L^#OvIaTJcap4mse-Le;_jiU6#NOeSg^#))~2#YwojT5|j@Ra~{l!QG} zjq_Lc+%OYTZS|<7cnVblSGIjVey8~NnB!gEw~&yCQ=wPXQx$hJ3UJ3_O#PMwq3HVV z%`sg@d$m{mV_1(v-Rw{|ELV=Cxq0((L~;7$YwnAAo+KH2W?=`&k=1IE-#FjS^Rd|k ztVZyLJeekIMzjaRzsIm;P0Itsp@JWg1w#>gC;XH#B2Y$qtJUn@`0lIpjFS|jOOASn zeGg713oGsvLJzCl0lT^L(4<@>)0XkBr>=vSZePeuWvq+&KCf36gVMnO<+-lDaE5*# z?EyNGuN#v&IY|#PL0C;v7rCDs<+4ArwmxTiTm$7zZ2Ug&N! zf=cif_7*g)wOud?fU=y*BE87~?LucPtpR+VAnd^gBng(d*yhLbthVr77-kqb;8HcR zuJYyrRofgyS@^pqD9c?0_DS?3e^=bo+zWcju6!hWz|4JTtB?UObKVIGT>hq*TjmiL zI-mMXR#?W@4kwn3s-l@W&_w)PpOeb?FlTyrqDBKD_R@zhdax2D&Ai%sd?p2|0N8YpUdEsP2(hi=D#1)3!8b z;@j-RBK|Zas~l_xWX_FGIU)R19pvY&op|f3SjHU8XK`ptn4OLJmYts{@<>3)=Nkt;E;QzUS5vgR9ozk5PP(W>cl5%{%lbcH6# zCcqOf!ot3Y+1?c0Uu&a-9`aF>F93K3Zr2wH> z0YSws!U{w8?Y;0~hN;D#8SO9PojDzrbk^^Z81bFGiv%Zrc=p7oM(ZIoVg}Jtb4O|4 zMo(?c_*K5s_%T=vj>>GxCG{74P5pU+m3_sT&5@OZQ7jO()nK1&icc$8YS>rHn;-m} zfwV@P{5P(m91bKDD5x78+{-JFWIf!C0B8gmh6gRe{ISi%0DkZw1}IM}qCI`kfhz!) zh3@4yGR_*0Hit(JtAYjR* zce89|*Lb?zc$vG_wD5L3dHqHq3zk414ds}^z@GFEm97?H4z`!;tnyvbWv3JEO!4K1 z=$CMFPzVhD4M74Y4Pco`|1={?Haw9z6{Y*ue6Y-&GJdoyuAu!Jxpf(>nhK6deh!{P zHHil2Z0i8a?4iw5We}x4{EZsB>Cnf zR@0vMpVGcmz)Q2$F1C7O)5>>yETn4#*M{R-%eN`;rxojTTXm;)kKFOO?$(o{h!k}3 zNxXSis;P@*Mdff!XWQFsF>%jg!I2KlFE%oJBDDH8JaD(=BAEk_{IthdL-D;w2I+uw zl_}9|>EKx;@Bp6-;JR3uuM`FqE9FP+ZX^4sAFxKNP!TRtvQ#$2$|6#*g}vcDCuu5>`wlR}Ly?0rMoT58upcX3Sx&3v z^lVcq-3bx(tr2!GO8s&=|-eUXv|{o<(#Qq}>s{%9-DuP2RzZfX$;+{bO5 zJ%LUyLBWYor=GrD!qG#nMfvGfc~yj9!5(a+AlHp2X;0imOWPg3`cS@WDE%Z6*dab? zscW8?2wy@ToL)8%Vc8> ztV8P3aaRiw0#N#3O@r&0sBv@G_lQ?}0kO8m6l$J+Bs0f*(vKfrSBkr-tBJ6;iDr)m zq7l9!CjSaY@4bq}L&3+s?nVNEUrTs#MdydDI)Yez5yh!# zAV;q@58e)`<;}gL@Cfam6Q(x80|!|3p;5Ob@9CXMX(M5h;F^UMz1#ek%tCUQK))nN z&r=+@+fViXrLkMh#;X258oMY1I4Ix{Sdf49TQ-mtKhSl69r@3fsnsMqArQAJuwj4` z>gA`i0X)t}ku=cBKM@-hn!N+&EGK4vDS|W92oZo8wh}f2eJ-fzJOzNuA*^3 ztoES6Nc$co@NAgu1xU21x6}t(>bVD1Bl{~LJlqT85}41HC=JW+g>=0iTUhy|5~I6I*VBvc?Covbk2KhMJ zp@4x=y#j-l+Q-`*ck|vNd{V;dIbcrz0@odEKvMA#tV3*^L? zKK#WF3kNJ3`KvbmQM3jDUpGo4b>NvfB@k{LiTRHw%)b?@|0b^WyHx*|el_rM3|H?@ znm?Godj{&?30`{7g~qE0sm3;zue#o!#D9dL-+@xAfEpp|KZ*ZT61gVOVO)sp#XskN z1%O|H%C&)$A#Z?YlbA2S|0?Z*fxUYLOwoA(j!#p+0AGoiz5=G{14Yu9fy=W*FT&L? z;*sPNfBIOkA&7zqNM?@M>-_+J;8 ze`jkL1Oie3VgHluKlTWqhs5h=)LrrmBjpsy3*&z`e*XqI7~@!{*&K73mKph>2+J)EPc7`Jzv8Ab45Tf{?tFW=mFHFcwgmBRRL6( zmwKtESFb>?KzEfd%nCW^K;P-Vn)rWb!~d=$#p)OE2M>b(Q;dH`cMv=2s~mRffJgo0 ZIEr%6px*FjbYKDNK?4J`tpD@h{{w_99OD20 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a1a7687..7c4388a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Oct 17 15:10:52 PDT 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/gradlew b/gradlew index 91a7e26..8e25e6c 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,20 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## ## @@ -6,20 +22,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +64,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +75,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +105,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +129,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +170,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index aec9973..24467a1 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -8,14 +24,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +62,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +75,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/java/build.gradle b/java/build.gradle index 3373133..d1fa4df 100644 --- a/java/build.gradle +++ b/java/build.gradle @@ -20,12 +20,34 @@ sourceSets { } } +buildscript { + repositories { + jcenter() + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "me.champeau.gradle:jmh-gradle-plugin:0.5.0-rc-2" + } +} + +apply plugin: "me.champeau.gradle.jmh" + +jmh { + dependencies { + compileClasspath 'org.openjdk.jmh:jmh-core:1.4.1' + } + humanOutputFile = null + duplicateClassesStrategy = 'exclude' +} + dependencies { testCompile project(':tests') } signing { - required { has("release") && gradle.taskGraph.hasTask("uploadArchives") } + required { hasProperty("release") && gradle.taskGraph.hasTask("uploadArchives") } sign configurations.archives } diff --git a/java/src/jmh/java/org/whispersystems/curve25519/JavaCurve25519ProviderBenchmark.java b/java/src/jmh/java/org/whispersystems/curve25519/JavaCurve25519ProviderBenchmark.java new file mode 100644 index 0000000..b8d5a4e --- /dev/null +++ b/java/src/jmh/java/org/whispersystems/curve25519/JavaCurve25519ProviderBenchmark.java @@ -0,0 +1,88 @@ +package org.whispersystems.curve25519; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@BenchmarkMode(Mode.AverageTime) +@Threads(4) +@Fork(1) +@Warmup(iterations = 10) +@Measurement(iterations = 5) +public class JavaCurve25519ProviderBenchmark { + @State(Scope.Thread) + public static class BenchmarkState { + public JavaCurve25519Provider provider = new JavaCurve25519Provider(); + public byte[] privateKey; + public byte[] publicKey; + public byte[] random = new byte[64]; + public byte[] message = new byte[256]; + public byte[] editedMessage = new byte[256]; + public byte[] signature; + public byte[] vrfSignature; + + @Setup(Level.Iteration) + public void doSetup() { + Random r = new Random(); + r.nextBytes(random); + r.nextBytes(message); + System.arraycopy(message, 0, editedMessage, 0, 256); + // invert 1 byte + editedMessage[0] ^= 0xff; + privateKey = provider.generatePrivateKey(random); + publicKey = provider.generatePublicKey(privateKey); + signature = provider.calculateSignature(random, privateKey, message); + vrfSignature = provider.calculateVrfSignature(random, privateKey, message); + } + } + + @Benchmark + public void generatePrivateKey(Blackhole blackhole, BenchmarkState bs) { + blackhole.consume(bs.provider.generatePrivateKey()); + } + + @Benchmark + public void generatePrivateKeyWithRandom(Blackhole blackhole, BenchmarkState bs) { + blackhole.consume(bs.provider.generatePrivateKey(bs.random)); + } + + @Benchmark + public void generatePublicKey(Blackhole blackhole, BenchmarkState bs) { + blackhole.consume(bs.provider.generatePublicKey(bs.privateKey)); + } + + @Benchmark + public void calculateSignature(Blackhole blackhole, BenchmarkState bs) { + blackhole.consume(bs.provider.calculateSignature(bs.random, bs.privateKey, bs.message)); + } + + @Benchmark + public void verifyValidSignature(Blackhole blackhole, BenchmarkState bs) { + blackhole.consume(bs.provider.verifySignature(bs.publicKey, bs.message, bs.signature)); + } + + @Benchmark + public void verifyInvalidSignature(Blackhole blackhole, BenchmarkState bs) { + blackhole.consume(bs.provider.verifySignature(bs.publicKey, bs.editedMessage, bs.signature)); + } + + @Benchmark + public void calculateVRFSignature(Blackhole blackhole, BenchmarkState bs) { + blackhole.consume(bs.provider.calculateVrfSignature(bs.random, bs.privateKey, bs.message)); + } + + @Benchmark + public void verifyValidVrfSignature(Blackhole blackhole, BenchmarkState bs) throws VrfSignatureVerificationFailedException { + blackhole.consume(bs.provider.verifyVrfSignature(bs.publicKey, bs.message, bs.vrfSignature)); + } + + @Benchmark + public void verifyInvalidVrfSignature(Blackhole blackhole, BenchmarkState bs) { + try { + blackhole.consume(bs.provider.verifyVrfSignature(bs.publicKey, bs.editedMessage, bs.vrfSignature)); + } catch (VrfSignatureVerificationFailedException ignored) {} + } +} diff --git a/tests/src/main/java/org/whispersystems/curve25519/Curve25519ProviderTest.java b/tests/src/main/java/org/whispersystems/curve25519/Curve25519ProviderTest.java index 65f4aec..33aef3c 100644 --- a/tests/src/main/java/org/whispersystems/curve25519/Curve25519ProviderTest.java +++ b/tests/src/main/java/org/whispersystems/curve25519/Curve25519ProviderTest.java @@ -1,7 +1,11 @@ package org.whispersystems.curve25519; +import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin; import junit.framework.TestCase; +import java.util.Arrays; +import java.util.Random; + import static org.fest.assertions.Assertions.assertThat; public abstract class Curve25519ProviderTest extends TestCase { @@ -86,4 +90,139 @@ public void testSignVerify() throws NoSuchProviderException { System.arraycopy(sig_out, 0, privkey, 0, 32); } } + + public void testVRFSign() throws NoSuchProviderException, VrfSignatureVerificationFailedException { + Curve25519Provider provider = createProvider(); + + byte[] msg = HexBin.decode("5468697320697320756E697175652E"); + byte[] privkey = HexBin.decode("38611D253BEA85A203805343B74A936D3B13B9E3121453E9740B6B827E337E5D"); + byte[] signature = HexBin.decode("5D501685D744424DE3EF5CA49ECDDD880FA7421C975CDF94BAE48CA16EC0899737721200EED1A8B0D2D6852826A1EAB78B0DF27F35B3F3E89C96E7AE3DAAA30F037297547886E554AFFC81DE54B575768FB30493C537ECDD5A87577DEB7D8E03"); + + byte[] sig_out = provider.calculateVrfSignature(new byte[32], privkey, msg); + assertTrue(Arrays.equals(signature, sig_out)); + } + + public void testVRFVerify() throws NoSuchProviderException, VrfSignatureVerificationFailedException { + Curve25519Provider provider = createProvider(); + + byte[] msg = HexBin.decode("5468697320697320756E697175652E"); + byte[] pubkey = HexBin.decode("21F7345F56D9602F1523298F4F6FCECB14DDE2D5B9A9B48BCA8242681492B920"); + byte[] vrf = HexBin.decode("45DC7B816B01B36CFA1645DCAE8AC9BC8E523CD86D007D19953F03E7D54554A0"); + byte[] signature = HexBin.decode("5D501685D744424DE3EF5CA49ECDDD880FA7421C975CDF94BAE48CA16EC0899737721200EED1A8B0D2D6852826A1EAB78B0DF27F35B3F3E89C96E7AE3DAAA30F037297547886E554AFFC81DE54B575768FB30493C537ECDD5A87577DEB7D8E03"); + + byte[] calc_vrf = provider.verifyVrfSignature(pubkey, msg, signature ); + assertTrue(Arrays.equals(vrf, calc_vrf)); + } + + public void testVRFSignVerify() throws NoSuchProviderException, VrfSignatureVerificationFailedException { + Curve25519Provider provider = createProvider(); + + byte[] msg = HexBin.decode("CE0827E6381654D3FFBE22F546E00199B5761C1E541108E56D5A66213A1569E969A02B1D27D91553B69984010F25331A13EA62BA53B6F5B86DA11F8C22ABBF11D6839E11626D0FEF191BD2D5251D371F57C53240F7CD2B435BE6213C7C8F36D47F3DE23A"); + byte[] privkey = HexBin.decode("C80827E6381654D3FFBE22F546E00199B5761C1E541108E56D5A66213A156969"); + byte[] publickey = provider.generatePublicKey(privkey); + byte[] random = HexBin.decode("B33734A591BAB70644D731530B67E8734C157DD72B796B7FA3D7FF6885D4C122"); + byte[] vrf = HexBin.decode("5669EC30C0F39E2696BB048B574236DEFA325D307116D6A89612958793192FF5"); + + byte[] sig_out = provider.calculateVrfSignature(random, privkey, msg); + byte[] calc_vrf = provider.verifyVrfSignature(publickey, msg, sig_out); + assertTrue(Arrays.equals(calc_vrf, vrf)); + } + + public void testVRFFailedVerifyByMessage() throws NoSuchProviderException, VrfSignatureVerificationFailedException { + Curve25519Provider provider = createProvider(); + + byte[] msg = HexBin.decode("5468697320697320756E697175652E"); + byte[] pubkey = HexBin.decode("21F7345F56D9602F1523298F4F6FCECB14DDE2D5B9A9B48BCA8242681492B920"); + byte[] signature = HexBin.decode("5D501685D744424DE3EF5CA49ECDDD880FA7421C975CDF94BAE48CA16EC0899737721200EED1A8B0D2D6852826A1EAB78B0DF27F35B3F3E89C96E7AE3DAAA30F037297547886E554AFFC81DE54B575768FB30493C537ECDD5A87577DEB7D8E03"); + + msg[4] ^= 0xff; + + try { + provider.verifyVrfSignature(pubkey, msg, signature); + fail(); + } catch(VrfSignatureVerificationFailedException ignored) {} + } + + public void testVRFFailedVerifyByPublicKey() throws NoSuchProviderException, VrfSignatureVerificationFailedException { + Curve25519Provider provider = createProvider(); + + byte[] msg = HexBin.decode("5468697320697320756E697175652E"); + byte[] pubkey = HexBin.decode("21F7345F56D9602F1523298F4F6FCECB14DDE2D5B9A9B48BCA8242681492B920"); + byte[] signature = HexBin.decode("5D501685D744424DE3EF5CA49ECDDD880FA7421C975CDF94BAE48CA16EC0899737721200EED1A8B0D2D6852826A1EAB78B0DF27F35B3F3E89C96E7AE3DAAA30F037297547886E554AFFC81DE54B575768FB30493C537ECDD5A87577DEB7D8E03"); + + pubkey[4] ^= 0xff; + + try { + provider.verifyVrfSignature(pubkey, msg, signature); + fail(); + } catch(VrfSignatureVerificationFailedException ignored) {} + } + + public void testVRFFailedVerifyBySignature() throws NoSuchProviderException, VrfSignatureVerificationFailedException { + Curve25519Provider provider = createProvider(); + + byte[] msg = HexBin.decode("5468697320697320756E697175652E"); + byte[] pubkey = HexBin.decode("21F7345F56D9602F1523298F4F6FCECB14DDE2D5B9A9B48BCA8242681492B920"); + byte[] signature = HexBin.decode("5D501685D744424DE3EF5CA49ECDDD880FA7421C975CDF94BAE48CA16EC0899737721200EED1A8B0D2D6852826A1EAB78B0DF27F35B3F3E89C96E7AE3DAAA30F037297547886E554AFFC81DE54B575768FB30493C537ECDD5A87577DEB7D8E03"); + + signature[4] ^= 0xff; + + try { + provider.verifyVrfSignature(pubkey, msg, signature); + fail(); + } catch(VrfSignatureVerificationFailedException ignored) {} + } + + public void testVRFIntegrationTest() throws NoSuchProviderException, VrfSignatureVerificationFailedException { + Curve25519Provider provider = createProvider(); + Random r = new Random(1244); + + byte[] msg = new byte[100]; + byte[] privkey; + byte[] pubkey; + byte[] random = new byte[64]; + + for (int count=0; count < 1000; count++) { + r.nextBytes(random); + r.nextBytes(msg); + + privkey = provider.generatePrivateKey(msg); + pubkey = provider.generatePublicKey(privkey); + + byte[] sig_out = provider.calculateVrfSignature(random, privkey, msg); + byte[] sig_out2 = provider.calculateVrfSignature(random, privkey, msg); + r.nextBytes(random); + byte[] sig_out3 = provider.calculateVrfSignature(random, privkey, msg); + + assertTrue(Arrays.equals(sig_out, sig_out2)); + assertFalse(Arrays.equals(sig_out, sig_out3)); + + byte[] vrf1 = provider.verifyVrfSignature(pubkey, msg, sig_out); + byte[] vrf2 = provider.verifyVrfSignature(pubkey, msg, sig_out); + + assertTrue(Arrays.equals(vrf1, vrf2)); + assertFalse(provider.verifySignature(pubkey, msg, sig_out)); + + try { + byte[] wrongPubkey = pubkey.clone(); + wrongPubkey[3] ^= 0xff; + provider.verifyVrfSignature(wrongPubkey, msg, sig_out); + fail(); + } catch (VrfSignatureVerificationFailedException ignored) {} + + try { + byte[] wrongMsg = msg.clone(); + wrongMsg[3] ^= 0xff; + provider.verifyVrfSignature(pubkey, wrongMsg, sig_out); + fail(); + } catch (VrfSignatureVerificationFailedException ignored) {} + + try { + byte[] wrongSignature = sig_out.clone(); + wrongSignature[3] ^= 0xff; + provider.verifyVrfSignature(pubkey, msg, wrongSignature); + fail(); + } catch (VrfSignatureVerificationFailedException ignored) {} + } + } } From f6bab63228b9cfd71c2e14de901b1085537a979f Mon Sep 17 00:00:00 2001 From: Sergey Tolmachev Date: Fri, 22 Nov 2019 13:57:57 +0300 Subject: [PATCH 2/3] Update fe_isequal.java --- .../java/org/whispersystems/curve25519/java/fe_isequal.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/org/whispersystems/curve25519/java/fe_isequal.java b/common/src/main/java/org/whispersystems/curve25519/java/fe_isequal.java index d360900..1d4f7fb 100644 --- a/common/src/main/java/org/whispersystems/curve25519/java/fe_isequal.java +++ b/common/src/main/java/org/whispersystems/curve25519/java/fe_isequal.java @@ -9,7 +9,7 @@ public static int fe_isequal(int[] f, int[] g) { int[] h = new int[10]; fe_sub.fe_sub(h, f, g); - return 1 ^ (1 & (fe_isnonzero.fe_isnonzero(h) >> 8)); + return 1 & (((fe_isnonzero.fe_isnonzero(h) & 0xff) - 1) >> 8); } } From 12f67c8fc6a75bf0a0baffe8a077197437c2d1cc Mon Sep 17 00:00:00 2001 From: Sergey Tolmachev Date: Fri, 22 Nov 2019 13:59:10 +0300 Subject: [PATCH 3/3] fe is equal tests were added --- .../curve25519/Curve25519ProviderTest.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/src/main/java/org/whispersystems/curve25519/Curve25519ProviderTest.java b/tests/src/main/java/org/whispersystems/curve25519/Curve25519ProviderTest.java index 33aef3c..0d6b652 100644 --- a/tests/src/main/java/org/whispersystems/curve25519/Curve25519ProviderTest.java +++ b/tests/src/main/java/org/whispersystems/curve25519/Curve25519ProviderTest.java @@ -225,4 +225,27 @@ public void testVRFIntegrationTest() throws NoSuchProviderException, VrfSignatur } catch (VrfSignatureVerificationFailedException ignored) {} } } + + public void testFeIsequal1() { + int[] one = new int[] {0,0,0,0,0,0,0,0,0,1}; + int[] zero = new int[] {0,0,0,0,0,0,0,0,0,0}; + assertTrue(fe_isequal.fe_isequal(one, zero) == 0); + } + public void testFeIsequal2() { + int[] one = new int[] {0,0,0,0,0,0,0,0,0,1}; + int[] zero = new int[] {0,0,0,0,0,0,0,0,0,1}; + assertTrue(fe_isequal.fe_isequal(one, zero) == 1); + } + + public void testFeIsequal3() { + int[] one = new int[] {0,0,0,0,0,0,0,0,0,0}; + int[] zero = new int[] {0,0,0,0,0,0,0,0,0,0}; + assertTrue(fe_isequal.fe_isequal(one, zero) == 1); + } + + public void testFeIsequal4() { + int[] one = new int[] {0,0,0,0,0,0,0,0,0,0}; + int[] zero = new int[] {0,0,0,0,0,0,0,0,0,1}; + assertTrue(fe_isequal.fe_isequal(one, zero) == 0); + } }