From: Mark Wooding Date: Sat, 23 Dec 2023 14:33:00 +0000 (+0000) Subject: Merge branch '2.5.x' into HEAD X-Git-Url: https://git.distorted.org.uk/~mdw/catacomb/commitdiff_plain/81ceb2c35de440e701d2f4e6960001395d2b7e97?hp=abdc0d5fcaa72bef72d1dc49290c1db8d68229f3 Merge branch '2.5.x' into HEAD * 2.5.x: rand/rand.c (rand_gate): Evolve r->ibits in a more sensible manner. rand/rand.c (rand_getgood): Stretch the output buffer if necessary. --- diff --git a/base/Makefile.am b/base/Makefile.am index 145f9c35..8b7c0fcd 100644 --- a/base/Makefile.am +++ b/base/Makefile.am @@ -45,6 +45,12 @@ libbase_la_SOURCES += ct.c ct-test.c ## CPU-specific dispatch. pkginclude_HEADERS += dispatch.h libbase_la_SOURCES += dispatch.c +if CPUFAM_X86 +libbase_la_SOURCES += dispatch-x86ish.S +endif +if CPUFAM_AMD64 +libbase_la_SOURCES += dispatch-x86ish.S +endif ## Acceptable key-size descriptions. pkginclude_HEADERS += keysz.h diff --git a/base/asm-common.h b/base/asm-common.h index 44c223da..b4d4a909 100644 --- a/base/asm-common.h +++ b/base/asm-common.h @@ -45,26 +45,6 @@ #define _ENDLIT .text .L$_subsec #define _LTORG .L$_subsec = .L$_subsec + 2; .text .L$_subsec -// ELF section types. -#if __ELF__ -# if CPUFAM_ARMEL -# define _SECTTY(ty) %ty -# else -# define _SECTTY(ty) @ty -# endif -#endif - -// Section selection. -#define TEXT .text .L$_subsec -#if ABI_WIN -# define RODATA .section .rdata, "dr" -#elif __ELF__ -# define RODATA .section .rodata, "a", _SECTTY(progbits) -#else -# define RODATA TEXT -#endif -#define DATA .data - // Announcing an internal function. #define INTFUNC(name) \ TYPE_FUNC(name); \ @@ -106,14 +86,32 @@ name: #if __ELF__ -#if __PIC__ || __PIE__ -# define WANT_PIC 1 +// Section types. +#if CPUFAM_ARMEL +# define _SECTTY(ty) %ty +#else +# define _SECTTY(ty) @ty #endif -#define TYPE_FUNC(name) .type name, STT_FUNC +// Section selection. +#define RODATA .section .rodata, "a", _SECTTY(progbits) +// Additional symbol metadata. +#define TYPE_FUNC(name) .type name, STT_FUNC +#define TYPE_OBJ(name) .type name, STT_OBJECT #define SIZE_OBJ(name) .size name, . - name +// Special arrangements for position-independent code. +#if __PIC__ || __PIE__ +# define WANT_PIC 1 +#endif + +// Don't make the stack executable by default. +#ifndef FORCE_EXECUTABLE_STACK + .pushsection .note.GNU-stack, "", _SECTTY(progbits) + .popsection +#endif + #endif ///-------------------------------------------------------------------------- @@ -121,10 +119,14 @@ name: #if ABI_WIN +// Function names need decorating on 32-bit i386. #if CPUFAM_X86 # define F(name) _##name #endif +// Section selection. +#define RODATA .section .rdata, "dr" + #endif ///-------------------------------------------------------------------------- @@ -311,6 +313,7 @@ name: // R_r(decor) applies decoration decor to register r, which is an internal // register name. The internal register names are: `ip', `a', `b', `c', `d', // `si', `di', `bp', `sp', `r8'--`r15'. +#define R_nil(decor) nil #define R_ip(decor) _DECOR(ip, decor, ip) #define R_a(decor) _DECOR(abcd, decor, a) #define R_b(decor) _DECOR(abcd, decor, b) @@ -345,6 +348,8 @@ name: // assembler-level register name, in place of any decoration that register // name has already. +#define _REGFORM_nil(decor) R_nil(decor) + #define _REGFORM_ip(decor) R_ip(decor) #define _REGFORM_eip(decor) R_ip(decor) @@ -452,10 +457,20 @@ name: #endif #define WHOLE(reg) _REGFORM(reg, r) +// Macros for some common registers. +#define AX R_a(r) +#define BX R_b(r) +#define CX R_c(r) +#define DX R_d(r) +#define SI R_si(r) +#define DI R_di(r) +#define BP R_bp(r) +#define SP R_sp(r) + // Stack management and unwinding. -.macro setfp fp=R_bp(r), offset=0 +.macro setfp fp=BP, offset=0 .if \offset == 0 - mov \fp, R_sp(r) + mov \fp, SP #if __ELF__ .cfi_def_cfa_register \fp #endif @@ -463,7 +478,7 @@ name: .seh_setframe \fp, 0 #endif .else - lea \fp, [R_sp(r) + \offset] + lea \fp, [SP + \offset] #if __ELF__ .cfi_def_cfa_register \fp .cfi_adjust_cfa_offset -\offset @@ -478,14 +493,14 @@ name: .macro _dropfp fp, offset=0 .if \offset == 0 - mov R_sp(r), \fp + mov SP, \fp #if __ELF__ - .cfi_def_cfa_register R_sp(r) + .cfi_def_cfa_register SP #endif .else - lea R_sp(r), [\fp - \offset] + lea SP, [\fp - \offset] #if __ELF__ - .cfi_def_cfa_register R_sp(r) + .cfi_def_cfa_register SP .cfi_adjust_cfa_offset +\offset #endif .endif @@ -494,7 +509,7 @@ name: .endm .macro stalloc n - sub R_sp(r), \n + sub SP, \n #if __ELF__ .cfi_adjust_cfa_offset +\n #endif @@ -504,7 +519,7 @@ name: .endm .macro stfree n - add R_sp(r), \n + add SP, \n #if __ELF__ .cfi_adjust_cfa_offset -\n #endif @@ -530,14 +545,14 @@ name: .endm .macro savexmm r, offset - movdqa [R_sp(r) + \offset], \r + movdqa [SP + \offset], \r #if ABI_WIN && CPUFAM_AMD64 .seh_savexmm \r, \offset #endif .endm .macro rstrxmm r, offset - movdqa \r, [R_sp(r) + \offset] + movdqa \r, [SP + \offset] .endm .macro endprologue @@ -657,6 +672,8 @@ name: // Internal macros: `_REGFORM_r(decor)' applies decoration decor to register // name r. +#define _REGFORM_nil(decor) nil + #define _REGFORM_s0(decor) _DECOR(s, decor, 0) #define _REGFORM_s1(decor) _DECOR(s, decor, 1) #define _REGFORM_s2(decor) _DECOR(s, decor, 2) @@ -1099,6 +1116,59 @@ name: .macro endprologue .endm +// cmov RD, RN, CC: set RD to RN if CC is satisfied, otherwise do nothing +.macro cmov rd, rn, cc + csel \rd, \rn, \rd, \cc +.endm + +// Notational improvement: write `csel.CC' etc., rather than `csel ..., CC'. +#define _COND(_) \ + _(eq) _(ne) _(cs) _(cc) _(vs) _(vc) _(mi) _(pl) \ + _(ge) _(lt) _(gt) _(le) _(hi) _(ls) _(al) _(nv) \ + _(hs) _(lo) +#define _INST(_) \ + _(ccmp) _(ccmn) \ + _(csel) _(cmov) \ + _(csinc) _(cinc) _(cset) \ + _(csneg) _(cneg) \ + _(csinv) _(cinv) _(csetm) +#define _CONDVAR(cc) _definstvar cc; +#define _INSTVARS(inst) \ + .macro _definstvar cc; \ + .macro inst.\cc args:vararg; inst \args, \cc; .endm; \ + .endm; \ + _COND(_CONDVAR); \ + .purgem _definstvar; + _INST(_INSTVARS) +#undef _COND +#undef _INST +#undef _CONDVAR +#undef _INSTVARS + +// Flag bits for `ccmp' and friends. +#define CCMP_N 8 +#define CCMP_Z 4 +#define CCMP_C 2 +#define CCMP_V 1 + +// Flag settings for satisfying conditions. +#define CCMP_MI CCMP_N +#define CCMP_PL 0 +#define CCMP_EQ CCMP_Z +#define CCMP_NE 0 +#define CCMP_CS CCMP_C +#define CCMP_HS CCMP_C +#define CCMP_CC 0 +#define CCMP_LO 0 +#define CCMP_VS CCMP_V +#define CCMP_VC 0 +#define CCMP_HI CCMP_C +#define CCMP_LS 0 +#define CCMP_LT CCMP_N +#define CCMP_GE 0 +#define CCMP_LE CCMP_N +#define CCMP_GT 0 + #endif ///-------------------------------------------------------------------------- @@ -1115,6 +1185,18 @@ name: # define ENDFUNC_HOOK(_) #endif +// Section selection. +#ifndef TEXT +# define TEXT .text .L$_subsec +#endif +#ifndef RODATA +# define RODATA TEXT +#endif +#ifndef DATA +# define DATA .data +#endif + +// Symbol decoration. #ifndef F # ifdef SYM_USCORE # define F(name) _##name @@ -1126,16 +1208,13 @@ name: #ifndef TYPE_FUNC # define TYPE_FUNC(name) #endif - +#ifndef TYPE_OBJ +# define TYPE_OBJ(name) +#endif #ifndef SIZE_OBJ # define SIZE_OBJ(name) #endif -#if __ELF__ && !defined(WANT_EXECUTABLE_STACK) - .pushsection .note.GNU-stack, "", _SECTTY(progbits) - .popsection -#endif - ///----- That's all, folks -------------------------------------------------- #endif diff --git a/base/dispatch-x86ish.S b/base/dispatch-x86ish.S new file mode 100644 index 00000000..57d8d32b --- /dev/null +++ b/base/dispatch-x86ish.S @@ -0,0 +1,216 @@ +/// -*- mode: asm; asm-comment-char: ?/ -*- +/// +/// CPU dispatch support for x86 +/// +/// (c) 2019 Straylight/Edgeware +/// + +///----- Licensing notice --------------------------------------------------- +/// +/// This file is part of Catacomb. +/// +/// Catacomb is free software: you can redistribute it and/or modify it +/// under the terms of the GNU Library General Public License as published +/// by the Free Software Foundation; either version 2 of the License, or +/// (at your option) any later version. +/// +/// Catacomb is distributed in the hope that it will be useful, but +/// WITHOUT ANY WARRANTY; without even the implied warranty of +/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +/// Library General Public License for more details. +/// +/// You should have received a copy of the GNU Library General Public +/// License along with Catacomb. If not, write to the Free Software +/// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +/// USA. + +///-------------------------------------------------------------------------- +/// Preliminaries. + +#include "config.h" +#include "asm-common.h" + + EFLAGS_ID = 1 << 21 + + .text + +///-------------------------------------------------------------------------- +/// Probing for CPUID. + +FUNC(dispatch_x86ish_cpuid) + // Enter with a pointer to 16 bytes of storage for the output A, B, + // C, D values in the first argument, and input A and C values in the + // second and third. Fill the output buffer with `cpuid' results and + // return zero if we can; otherwise fill with zero and return -1. + +#if CPUFAM_X86 + pushreg ebx + pushreg edi + mov edi, [SP + 12] + mov eax, [SP + 16] + mov ecx, [SP + 20] +# define OUT edi +#endif +#if CPUFAM_AMD64 && ABI_SYSV + pushreg rbx + mov eax, esi + mov ecx, edx +# define OUT rdi +#endif +#if CPUFAM_AMD64 && ABI_WIN + pushreg rbx + mov r9, rcx + mov eax, edx + mov ecx, r8d +# define OUT r9 +#endif + endprologue + + // First, check that this is even a thing, using the complicated + // dance with the flags register. + pushf + pop DX // current flags in d + + or DX, EFLAGS_ID // force the id bit on and check it + push DX + popf + pushf + pop DX + test edx, EFLAGS_ID + jz 8f + + and DX, ~EFLAGS_ID // force the id bit off and check it + push DX + popf + pushf + pop DX + test edx, EFLAGS_ID + jnz 8f + + // OK, that seemed to work. + cpuid + + mov [OUT + 0], eax + mov [OUT + 4], ebx + mov [OUT + 8], ecx + mov [OUT + 12], edx + xor eax, eax + + // We're done. +9: +#if CPUFAM_X86 + popreg edi + popreg ebx +#endif +#if CPUFAM_AMD64 + popreg rbx +#endif + ret + + // Failed. +8: xor eax, eax + mov [OUT + 0], eax + mov [OUT + 4], eax + mov [OUT + 8], eax + mov [OUT + 12], eax + mov eax, -1 + jmp 9b +ENDFUNC + +///-------------------------------------------------------------------------- +/// Probing for XMM register availability. + +FUNC(dispatch_x86ish_xmmregisters_p) + // Enter with no arguments. Return nonzero if the XMM registers are + // usable. + + pushreg BP + setfp + stalloc 512 + and SP, ~15 + endprologue + + // Save the floating point and SIMD registers, and try to clobber + // xmm0. + lea DX, [SP + 160] + fxsave [SP] + mov eax, [DX] + xor dword ptr [DX], 0xaaaa5555 + fxrstor [SP] + + // Save them again, and read back the low word of xmm0. Undo the + // clobbering and restore. + fxsave [SP] + mov ecx, [DX] + mov [DX], eax + fxrstor [SP] + + // The register are live if we read different things. + xor eax, ecx + + // Done. + dropfp + popreg BP + ret +ENDFUNC + +///-------------------------------------------------------------------------- +/// Checking `rdrand'. + +FUNC(dispatch_x86ish_rdrand) + // Enter with two arguments: a code OP requesting either `rdrand' (0) + // or `rdseed' (1), and a pointer X_OUT to a 32-bit word. Try to + // generate a random word using the requested instruction'. If + // successful, set *X_OUT to the generated word, and return zero; + // otherwise, return -1. + +#if CPUFAM_X86 +# define OP eax +# define X_OUT edx +# define COUNT ecx + mov OP, [SP + 4] + mov X_OUT, [SP + 8] +#endif +#if CPUFAM_AMD64 && ABI_SYSV +# define OP edi +# define X_OUT rsi +# define COUNT ecx +#endif +#if CPUFAM_AMD64 && ABI_WIN +# define OP rcx +# define X_OUT rdx +# define COUNT r8d +#endif + endprologue + + cmp OP, 0 + mov COUNT, 16 // fairly persistent + jne 1f + +0: rdrand eax + jc 9f + dec COUNT + jnz 0b + jmp 8f + +1: rdseed eax + jc 9f + dec COUNT + jnz 1b + jmp 8f + + // Failed to come up with a random value. +8: mov eax, -1 + ret + + // Success. +9: mov [X_OUT], eax + xor eax, eax + ret + +#undef X_OUT +#undef COUNT + +ENDFUNC + +///----- That's all, folks -------------------------------------------------- diff --git a/base/dispatch.c b/base/dispatch.c index 131e3fdb..4ce60159 100644 --- a/base/dispatch.c +++ b/base/dispatch.c @@ -29,6 +29,7 @@ #include "config.h" +#include #include #include #include @@ -43,113 +44,71 @@ #if CPUFAM_X86 || CPUFAM_AMD64 -# define EFLAGS_ID (1u << 21) +enum { + CPUID_1_D, /* eax = 1 => edx&?? */ # define CPUID1D_SSE2 (1u << 26) # define CPUID1D_FXSR (1u << 24) + + CPUID_1_C, /* eax = 1 => ecx&?? */ # define CPUID1C_PCLMUL (1u << 1) # define CPUID1C_SSSE3 (1u << 9) # define CPUID1C_AESNI (1u << 25) # define CPUID1C_AVX (1u << 28) # define CPUID1C_RDRAND (1u << 30) -struct cpuid { unsigned a, b, c, d; }; - -/* --- @cpuid@ --- * - * - * Arguments: @struct cpuid *cc@ = where to write the result - * @unsigned a, c@ = EAX and ECX registers to set - * - * Returns: --- - * - * Use: Minimal C wrapper around the x86 `CPUID' instruction. Checks - * that the instruction is actually available before invoking - * it; fills the output structure with zero if it's not going to - * work. - */ + CPUID_7_0_B, /* eax = 7, ecx = 0 => ebx&?? */ +# define CPUID70B_RDSEED (1u << 18) +}; -#ifdef __GNUC__ -# if CPUFAM_X86 -static __inline__ unsigned getflags(void) - { unsigned f; __asm__ ("pushf; popl %0" : "=g" (f)); return (f); } -static __inline__ unsigned setflags(unsigned f) -{ - unsigned ff; - __asm__ ("pushf; pushl %1; popf; pushf; popl %0; popf" - : "=r" (ff) - : "r" (f)); - return (ff); -} -# else -static __inline__ unsigned long getflags(void) - { unsigned long f; __asm__ ("pushf; popq %0" : "=g" (f)); return (f); } -static __inline__ unsigned long long setflags(unsigned long f) -{ - unsigned long ff; - __asm__ ("pushf; pushq %1; popf; pushf; popq %0; popf" - : "=r" (ff) - : "r" (f)); - return (ff); -} -# endif -#endif +struct cpuid { unsigned a, b, c, d; }; +extern int dispatch_x86ish_cpuid(struct cpuid *, unsigned a, unsigned c); +extern int dispatch_x86ish_xmmregisters_p(void); +extern int dispatch_x86ish_rdrand(unsigned op, unsigned *); static void cpuid(struct cpuid *cc, unsigned a, unsigned c) { -#ifdef __GNUC__ - unsigned f; -#endif - - cc->a = cc->b = cc->c = cc->d = 0; - -#ifdef __GNUC__ - /* Stupid dance to detect whether the CPUID instruction is available. */ - f = getflags(); - if (!(setflags(f | EFLAGS_ID) & EFLAGS_ID) || - setflags(f & ~EFLAGS_ID) & EFLAGS_ID) { + int rc = dispatch_x86ish_cpuid(cc, a, c); + if (rc) dispatch_debug("CPUID instruction not available"); - return; - } - setflags(f); - - /* Alas, EBX is magical in PIC code, so abuse ESI instead. This isn't - * pretty, but it works. - */ -# if CPUFAM_X86 - __asm__ ("pushl %%ebx; cpuid; movl %%ebx, %%esi; popl %%ebx" - : "=a" (cc->a), "=S" (cc->b), "=c" (cc->c), "=d" (cc->d) - : "a" (a) , "c" (c)); -# elif CPUFAM_AMD64 - __asm__ ("pushq %%rbx; cpuid; movl %%ebx, %%esi; popq %%rbx" - : "=a" (cc->a), "=S" (cc->b), "=c" (cc->c), "=d" (cc->d) - : "a" (a) , "c" (c)); -# else -# error "I'm confused." -# endif - dispatch_debug("CPUID(%08x, %08x) -> %08x, %08x, %08x, %08x", - a, c, cc->a, cc->b, cc->c, cc->d); -#else - dispatch_debug("GNU inline assembler not available; can't CPUID"); -#endif + else + dispatch_debug("CPUID(%08x, %08x) -> %08x, %08x, %08x, %08x", + a, c, cc->a, cc->b, cc->c, cc->d); } static unsigned cpuid_maxleaf(void) { struct cpuid c; cpuid(&c, 0, 0); return (c.a); } -/* --- @cpuid_features_p@ --- * +/* --- @cpuid_feature_p@ --- * * - * Arguments: @unsigned dbits@ = bits to check in EDX - * @unsigned cbits@ = bits to check in ECX + * Arguments: @unsigned leaf@ = leaf to look up + * @unsigned bits@ = bits to check * - * Returns: Nonzero if all the requested bits are set in the CPUID result - * on leaf 1. + * Returns: Nonzero if all the requested bits are set in the requested + * CPUID result. */ -static int cpuid_features_p(unsigned dbits, unsigned cbits) +static int cpuid_feature_p(unsigned leaf, unsigned bits) { struct cpuid c; - if (cpuid_maxleaf() < 1) return (0); - cpuid(&c, 1, 0); - return ((c.d & dbits) == dbits && (c.c & cbits) == cbits); + unsigned r; + + switch (leaf) { + case CPUID_1_D: + if (cpuid_maxleaf() < 1) return (0); + cpuid(&c, 1, 0); r = c.d; + break; + case CPUID_1_C: + if (cpuid_maxleaf() < 1) return (0); + cpuid(&c, 1, 0); r = c.c; + break; + case CPUID_7_0_B: + if (cpuid_maxleaf() < 7) return (0); + cpuid(&c, 7, 0); r = c.b; + break; + default: + assert(!"unknown cpuid leaf"); + } + return ((r&bits) == bits); } /* --- @xmm_registers_available_p@ --- * @@ -162,43 +121,10 @@ static int cpuid_features_p(unsigned dbits, unsigned cbits) static int xmm_registers_available_p(void) { -#ifdef __GNUC__ - unsigned f; - /* This hack is by Agner Fog. Use FXSAVE/FXRSTOR to figure out whether the - * XMM registers are actually alive. - */ - if (!cpuid_features_p(CPUID1D_FXSR, 0)) return (0); -# if CPUFAM_X86 - __asm__ ("movl %%esp, %%edx; subl $512, %%esp; andl $~15, %%esp\n" - "fxsave (%%esp)\n" - "movl 160(%%esp), %%eax; xorl $0xaaaa5555, 160(%%esp)\n" - "fxrstor (%%esp); fxsave (%%esp)\n" - "movl 160(%%esp), %%ecx; movl %%eax, 160(%%esp)\n" - "fxrstor (%%esp); movl %%edx, %%esp\n" - "xorl %%ecx, %%eax" - : "=a" (f) - : /* no inputs */ - : "%ecx", "%edx"); -# elif CPUFAM_AMD64 - __asm__ ("movq %%rsp, %%rdx; subq $512, %%rsp; andq $~15, %%rsp\n" - "fxsave (%%rsp)\n" - "movl 160(%%rsp), %%eax; xorl $0xaaaa5555, 160(%%rsp)\n" - "fxrstor (%%rsp); fxsave (%%rsp)\n" - "movl 160(%%rsp), %%ecx; movl %%eax, 160(%%rsp)\n" - "fxrstor (%%rsp); movq %%rdx, %%rsp\n" - "xorl %%ecx, %%eax" - : "=a" (f) - : /* no inputs */ - : "%ecx", "%rdx"); -# else -# error "I'm confused." -# endif + int f = dispatch_x86ish_xmmregisters_p(); + dispatch_debug("XMM registers %savailable", f ? "" : "not "); return (f); -#else - dispatch_debug("GNU inline assembler not available; can't check for XMM"); - return (0); -#endif } /* --- @rdrand_works_p@ --- * @@ -210,47 +136,37 @@ static int xmm_registers_available_p(void) * that it's already been verified to be safe to issue. */ -#ifdef __GNUC__ -static int rdrand(unsigned *x) -{ - int i, rc; - unsigned _t; - - i = 16; - __asm__ ("" : "=g" (_t)); - __asm__ ("0: rdrand %2; jc 1f; decl %1; jnz 0b\n" - "mov $-1, %0; jmp 9f\n" - "1: movl %2, (%3); xorl %0, %0\n" - "9:" - : "=r" (rc), "+r" (i), "+r" (_t) - : "r" (x) - : "cc"); - return (rc); -} -#endif +enum { OP_RDRAND, OP_RDSEED }; -static int rdrand_works_p(void) +static int rdrand_works_p(unsigned op) { unsigned ref, x, i; + const char *what; + + switch (op) { + case OP_RDRAND: what = "RDRAND"; break; + case OP_RDSEED: what = "RDSEED"; break; + default: assert(!"unexpected op"); + } /* Check that it doesn't always give the same answer. Try four times: this * will fail with probability %$2^{-128}$% with a truly random generator, * which seems fair enough. */ - if (rdrand(&ref)) goto fail; + if (dispatch_x86ish_rdrand(op, &ref)) goto fail; for (i = 0; i < 4; i++) { - if (rdrand(&x)) goto fail; + if (dispatch_x86ish_rdrand(op, &x)) goto fail; if (x != ref) goto not_stuck; } - dispatch_debug("RDRAND always returns 0x%08x!", ref); + dispatch_debug("%s always returns 0x%08x!", what, ref); return (0); not_stuck: - dispatch_debug("RDRAND instruction looks plausible"); + dispatch_debug("%s instruction looks plausible", what); return (1); fail: - dispatch_debug("RDRAND instruction fails too often"); + dispatch_debug("%s instruction fails too often", what); return (0); } @@ -497,7 +413,7 @@ static unsigned get_hwcaps(void) unsigned hw; DISPATCH_LOAD(hwcaps, hw); - if (!(hwcaps & HF_PROBED)) { probe_hwcaps(); DISPATCH_LOAD(hwcaps, hw); } + if (!(hw & HF_PROBED)) { probe_hwcaps(); DISPATCH_LOAD(hwcaps, hw); } return (hw); } @@ -552,14 +468,14 @@ static int IGNORABLE check_env(const char *ftok) if (!p) return (-1); for (;;) { - while (isspace((unsigned char)*p)) p++; + while (ISSPACE(*p)) p++; if (!*p) return (-1); switch (*p) { case '+': d = +1; p++; break; case '-': d = 0; p++; break; default: d = -1; break; } - for (q = p; *q && !isspace((unsigned char)*q); q++); + for (q = p; *q && !ISSPACE(*q); q++); if (d >= 0) { for (pp = ftok; p < q && *pp && *p == *pp; p++, pp++); if ((p == q && !*pp) || (*p == '*' && p + 1 == q)) return (d); @@ -593,8 +509,7 @@ int cpu_feature_p(int feat) int IGNORABLE f; IGNORE(f); #define CASE_CPUFEAT(feat, ftok, cond) case CPUFEAT_##feat: \ - if ((f = feat_debug(ftok, "environment override", \ - check_env(ftok))) >= 0) \ + if ((f = feat_debug(ftok, "environment override", check_env(ftok))) >= 0) \ return (f); \ else \ return (feat_debug(ftok, "runtime probe", cond)); @@ -602,22 +517,26 @@ int cpu_feature_p(int feat) switch (feat) { #if CPUFAM_X86 || CPUFAM_AMD64 CASE_CPUFEAT(X86_SSE2, "x86:sse2", - cpuid_features_p(CPUID1D_SSE2, 0) && + cpuid_feature_p(CPUID_1_D, CPUID1D_SSE2) && xmm_registers_available_p()); CASE_CPUFEAT(X86_AESNI, "x86:aesni", - cpuid_features_p(CPUID1D_SSE2, CPUID1C_AESNI) && + cpuid_feature_p(CPUID_1_C, CPUID1C_AESNI) && xmm_registers_available_p()); CASE_CPUFEAT(X86_RDRAND, "x86:rdrand", - cpuid_features_p(0, CPUID1C_RDRAND) && rdrand_works_p()); + cpuid_feature_p(CPUID_1_C, CPUID1C_RDRAND) && + rdrand_works_p(OP_RDRAND)); CASE_CPUFEAT(X86_AVX, "x86:avx", - cpuid_features_p(0, CPUID1C_AVX) && + cpuid_feature_p(CPUID_1_C, CPUID1C_AVX) && xmm_registers_available_p()); CASE_CPUFEAT(X86_SSSE3, "x86:ssse3", - cpuid_features_p(0, CPUID1C_SSSE3) && + cpuid_feature_p(CPUID_1_C, CPUID1C_SSSE3) && xmm_registers_available_p()); CASE_CPUFEAT(X86_PCLMUL, "x86:pclmul", - cpuid_features_p(0, CPUID1C_PCLMUL) && + cpuid_feature_p(CPUID_1_C, CPUID1C_PCLMUL) && xmm_registers_available_p()); + CASE_CPUFEAT(X86_RDSEED, "x86:rdseed", + cpuid_feature_p(CPUID_7_0_B, CPUID70B_RDSEED) && + rdrand_works_p(OP_RDSEED)); #endif #ifdef CAPMAP # define FEATP__CASE(feat, tok) \ diff --git a/base/dispatch.h b/base/dispatch.h index 7c083821..2c78d92a 100644 --- a/base/dispatch.h +++ b/base/dispatch.h @@ -180,12 +180,13 @@ enum { CPUFEAT_ARM_NEON, /* Advanced SIMD (v1 or v2) */ CPUFEAT_ARM_V4, /* VFPv4 and/or SIMD v2 */ CPUFEAT_ARM_D32, /* 32 double registers, not 16 */ - CPUFEAT_X86_RDRAND, /* Built-in entropy source */ + CPUFEAT_X86_RDRAND, /* Built-in cooked entropy source */ CPUFEAT_ARM_AES, /* AES instructions */ CPUFEAT_X86_AVX, /* AVX 1 (i.e., 256-bit YMM regs) */ CPUFEAT_X86_SSSE3, /* Supplementary SSE 3 */ CPUFEAT_X86_PCLMUL, /* Carry-less multiplication */ - CPUFEAT_ARM_PMULL /* Polynomial multiplication */ + CPUFEAT_ARM_PMULL, /* Polynomial multiplication */ + CPUFEAT_X86_RDSEED /* Built-in raw entropy source */ }; extern int cpu_feature_p(int /*feat*/); diff --git a/base/keysz-conv.c b/base/keysz-conv.c index 2d405588..9ed9f582 100644 --- a/base/keysz-conv.c +++ b/base/keysz-conv.c @@ -140,6 +140,8 @@ CONVERSIONS(DEFINE_TO) #include #include +#include + static const struct entry { const char *name; double (*func)(double); @@ -162,7 +164,7 @@ int main(int argc, char *argv[]) putc('\n', stderr); return (1); } - for (e = tab; e->name && strcmp(e->name, argv[1]); e++); + for (e = tab; e->name && STRCMP(e->name, !=, argv[1]); e++); if (!e) { fprintf(stderr, "unknown conversion `%s'\n", argv[1]); return (1); diff --git a/base/keysz.h b/base/keysz.h index 4ad772a0..2986d614 100644 --- a/base/keysz.h +++ b/base/keysz.h @@ -59,7 +59,7 @@ enum { KSZ_ANY, /* Allows any key at all */ KSZ_RANGE, /* Allows keys within a range */ - KSZ_SET, /* Allows specific sizes of keys */ + KSZ_SET /* Allows specific sizes of keys */ }; #define KSZ_16BIT 0x20 /* Arguments are 16 bits long */ diff --git a/base/regdump-arm.S b/base/regdump-arm.S index 963a60ee..6adcbdda 100644 --- a/base/regdump-arm.S +++ b/base/regdump-arm.S @@ -60,15 +60,8 @@ FUNC(regdump_gpsave) add r0, r4, #REGDUMP_GPSIZE str r0, [r4, #13*4] - // Capture the status flags and return address. If the return - // address has its low bit set, then the caller was in Thumb state: - // clear the bit from the reconstructed PC, and set the corresponding - // CPSR bit. - mrs r0, cpsr - tst r14, #1 + // Clear the magic Thumb-state bit from the return address. bic r1, r14, #1 - orrne r0, r0, #0x00000020 - str r0, [r13, #4*REGIX_CPSR] str r1, [r13, #15*4] // Load the focus address and save it as r6. diff --git a/base/regdump-arm64.S b/base/regdump-arm64.S index 81c9f8e7..183d38f0 100644 --- a/base/regdump-arm64.S +++ b/base/regdump-arm64.S @@ -56,10 +56,6 @@ FUNC(regdump_gpsave) stp x2, x3, [sp, #16] stp x4, x5, [sp, #32] stp x6, x7, [sp, #48] - stp x8, x9, [sp, #64] - stp x10, x11, [sp, #80] - stp x12, x13, [sp, #96] - stp x14, x15, [sp, #112] stp x18, x19, [sp, #144] stp x20, x21, [sp, #160] stp x22, x23, [sp, #176] @@ -73,10 +69,6 @@ FUNC(regdump_gpsave) add x0, x20, #REGDUMP_GPSIZE str x0, [x20, #31*8] - // Capture the status flags. - mrs x0, nzcv - str x0, [x20, #8*REGIX_NZCV] - // Set the return address as our PC. str x30, [x20, #8*REGIX_PC] diff --git a/base/regdump-x86ish.S b/base/regdump-x86ish.S index e4dd8e80..67a4ae0e 100644 --- a/base/regdump-x86ish.S +++ b/base/regdump-x86ish.S @@ -56,48 +56,48 @@ FUNC(regdump_gpsave) cld // Save r/ebp and establish it pointing to the save area. - mov [R_sp(r) + WORDSZ + REGIX_BP*WORDSZ], R_bp(r) - lea R_bp(r), [R_sp(r) + WORDSZ] + mov [SP + WORDSZ + REGIX_BP*WORDSZ], BP + lea BP, [SP + WORDSZ] // Save the other easy general-purpose registers. #if !CPUFAM_X86 - mov [R_bp(r) + REGIX_BX*WORDSZ], R_b(r) + mov [BP + REGIX_BX*WORDSZ], BX #endif - mov [R_bp(r) + REGIX_CX*WORDSZ], R_c(r) - mov [R_bp(r) + REGIX_DX*WORDSZ], R_d(r) - mov [R_bp(r) + REGIX_SI*WORDSZ], R_si(r) - mov [R_bp(r) + REGIX_DI*WORDSZ], R_di(r) + mov [BP + REGIX_CX*WORDSZ], CX + mov [BP + REGIX_DX*WORDSZ], DX + mov [BP + REGIX_SI*WORDSZ], SI + mov [BP + REGIX_DI*WORDSZ], DI #if CPUFAM_AMD64 - mov [R_bp(r) + REGIX_R8*WORDSZ], R_r8(r) - mov [R_bp(r) + REGIX_R9*WORDSZ], R_r9(r) - mov [R_bp(r) + REGIX_R10*WORDSZ], R_r10(r) - mov [R_bp(r) + REGIX_R11*WORDSZ], R_r11(r) - mov [R_bp(r) + REGIX_R12*WORDSZ], R_r12(r) - mov [R_bp(r) + REGIX_R13*WORDSZ], R_r13(r) - mov [R_bp(r) + REGIX_R14*WORDSZ], R_r14(r) - mov [R_bp(r) + REGIX_R15*WORDSZ], R_r15(r) + mov [BP + REGIX_R8*WORDSZ], r8 + mov [BP + REGIX_R9*WORDSZ], r9 + mov [BP + REGIX_R10*WORDSZ], r10 + mov [BP + REGIX_R11*WORDSZ], r11 + mov [BP + REGIX_R12*WORDSZ], r12 + mov [BP + REGIX_R13*WORDSZ], r13 + mov [BP + REGIX_R14*WORDSZ], r14 + mov [BP + REGIX_R15*WORDSZ], r15 #endif // Determine the previous stack pointer and save it. #if CPUFAM_AMD64 && ABI_SYSV - lea R_a(r), [R_bp(r) + 128 + REGDUMP_GPSIZE] + lea AX, [BP + 128 + REGDUMP_GPSIZE] #else - lea R_a(r), [R_bp(r) + REGDUMP_GPSIZE] + lea AX, [BP + REGDUMP_GPSIZE] #endif - mov [R_bp(r) + REGIX_SP*WORDSZ], R_a(r) + mov [BP + REGIX_SP*WORDSZ], AX // Collect the return address and save it as r/eip. - mov R_a(r), [R_sp(r)] - mov [R_bp(r) + REGIX_IP*WORDSZ], R_a(r) + mov AX, [SP] + mov [BP + REGIX_IP*WORDSZ], AX // Save the segment registers. - lea R_a(r), [R_bp(r) + REGIX_GPLIM*WORDSZ] - mov [R_a(r) + 2*REGIX_CS], cs - mov [R_a(r) + 2*REGIX_DS], ds - mov [R_a(r) + 2*REGIX_SS], ss - mov [R_a(r) + 2*REGIX_ES], es - mov [R_a(r) + 2*REGIX_FS], fs - mov [R_a(r) + 2*REGIX_GS], gs + lea AX, [BP + REGIX_GPLIM*WORDSZ] + mov [AX + 2*REGIX_CS], cs + mov [AX + 2*REGIX_DS], ds + mov [AX + 2*REGIX_SS], ss + mov [AX + 2*REGIX_ES], es + mov [AX + 2*REGIX_FS], fs + mov [AX + 2*REGIX_GS], gs // Determine the extended save area size. Preserve ebx on 32-bit x86 // here, because the caller needs it for PLT-indirect calls. @@ -135,23 +135,23 @@ FUNC(regdump_gprstr) // We assume nobody actually fiddled with the segment registers. So // just the actual integer registers to do. - mov R_a(r), [R_bp(r) + REGIX_AX*WORDSZ] - mov R_b(r), [R_bp(r) + REGIX_BX*WORDSZ] - mov R_c(r), [R_bp(r) + REGIX_CX*WORDSZ] - mov R_d(r), [R_bp(r) + REGIX_DX*WORDSZ] - mov R_si(r), [R_bp(r) + REGIX_SI*WORDSZ] - mov R_di(r), [R_bp(r) + REGIX_DI*WORDSZ] + mov AX, [BP + REGIX_AX*WORDSZ] + mov BX, [BP + REGIX_BX*WORDSZ] + mov CX, [BP + REGIX_CX*WORDSZ] + mov DX, [BP + REGIX_DX*WORDSZ] + mov SI, [BP + REGIX_SI*WORDSZ] + mov DI, [BP + REGIX_DI*WORDSZ] #if CPUFAM_AMD64 - mov R_r8(r), [R_bp(r) + REGIX_R8*WORDSZ] - mov R_r9(r), [R_bp(r) + REGIX_R9*WORDSZ] - mov R_r10(r), [R_bp(r) + REGIX_R10*WORDSZ] - mov R_r11(r), [R_bp(r) + REGIX_R11*WORDSZ] - mov R_r12(r), [R_bp(r) + REGIX_R12*WORDSZ] - mov R_r13(r), [R_bp(r) + REGIX_R13*WORDSZ] - mov R_r14(r), [R_bp(r) + REGIX_R14*WORDSZ] - mov R_r15(r), [R_bp(r) + REGIX_R15*WORDSZ] + mov r8, [BP + REGIX_R8*WORDSZ] + mov r9, [BP + REGIX_R9*WORDSZ] + mov r10, [BP + REGIX_R10*WORDSZ] + mov r11, [BP + REGIX_R11*WORDSZ] + mov r12, [BP + REGIX_R12*WORDSZ] + mov r13, [BP + REGIX_R13*WORDSZ] + mov r14, [BP + REGIX_R14*WORDSZ] + mov r15, [BP + REGIX_R15*WORDSZ] #endif - mov R_bp(r), [R_bp(r) + REGIX_BP*WORDSZ] + mov BP, [BP + REGIX_BP*WORDSZ] // Done. ret @@ -175,11 +175,11 @@ FUNC(regdump_xtsave) // general registers are clobbered. // Start by filling in the easy parts of the map. - mov [R_sp(r) + WORDSZ + regmap_gp], R_bp(r) - lea R_bp(r), [R_sp(r) + WORDSZ] + mov [SP + WORDSZ + regmap_gp], BP + lea BP, [SP + WORDSZ] xor eax, eax // clears rax too on amd64 - mov [R_bp(r) + regmap_avx], R_a(r) + mov [BP + regmap_avx], AX // Find out whether we use `xsave'. (Preserve ebx.) #if CPUFAM_X86 @@ -191,40 +191,40 @@ FUNC(regdump_xtsave) je 5f // We have the `xsave' machinery. Select the base address. - lea R_si(r), [R_sp(r) + WORDSZ + regmap_size + 63] - and R_si(r), ~63 - mov [R_bp(r) + regmap_fx], R_si(r) + lea SI, [SP + WORDSZ + regmap_size + 63] + and SI, ~63 + mov [BP + regmap_fx], SI // Clear out the header area. xor eax, eax - lea R_di(r), [R_si(r) + 512] + lea DI, [SI + 512] mov ecx, 16 rep stosd // Save the registers. mov eax, 0x00000007 xor edx, edx - xsave [R_si(r)] + xsave [SI] // Establish the AVX pointer, if available. - test dword ptr [R_si(r) + 512], 4 // = xstate_bv + test dword ptr [SI + 512], 4 // = xstate_bv je 8f mov eax, 13 mov ecx, 2 cpuid - add R_b(r), R_si(r) - mov [R_bp(r) + regmap_avx], R_b(r) + add BX, SI + mov [BP + regmap_avx], BX jmp 8f // We have only `fxsave'. Set the base address. -5: lea R_si(r), [R_sp(r) + WORDSZ + regmap_size + 15] - and R_si(r), ~15 - mov [R_bp(r) + regmap_fx], R_si(r) +5: lea SI, [SP + WORDSZ + regmap_size + 15] + and SI, ~15 + mov [BP + regmap_fx], SI // Save the registers. - fxsave [R_si(r)] + fxsave [SI] // Clear the x87 state; otherwise it can cause trouble later. 8: fninit @@ -245,7 +245,7 @@ FUNC(regdump_xtrstr) // 32-bit x86, and the other general registers are clobbered. // Find the extended register dump. - mov R_si(r), [R_bp(r) + regmap_fx] + mov SI, [BP + regmap_fx] // Probe to find out whether we have `xsave'. #if CPUFAM_X86 @@ -259,14 +259,14 @@ FUNC(regdump_xtrstr) // We have the `xsave' machinery. mov eax, 0x00000007 xor edx, edx - xrstor [R_si(r)] + xrstor [SI] jmp 8f // We must fake it up. -1: fxrstor [R_si(r)] +1: fxrstor [SI] // Done. -8: mov R_bp(r), [R_bp(r) + regmap_gp] +8: mov BP, [BP + regmap_gp] #if CPUFAM_X86 pop ebx #endif diff --git a/base/regdump.c b/base/regdump.c index c591fd5a..0fa76df0 100644 --- a/base/regdump.c +++ b/base/regdump.c @@ -213,6 +213,7 @@ static const char *regname(char *buf, uint32 f) switch (src) { + case REGSRC_NONE: case REGSRC_ABS: return (0); @@ -374,6 +375,14 @@ static const char *regname(char *buf, uint32 f) # define GP(gp) (gp).u64 #endif +#define CF (1 << 0) +#define PF (1 << 2) +#define AF (1 << 4) +#define ZF (1 << 6) +#define SF (1 << 7) +#define DF (1 << 10) +#define OF (1 << 11) + void regdump_init(void) { ; } static void dump_flags(const char *lbl, const char *reg, gpreg f) @@ -390,6 +399,17 @@ static void dump_flags(const char *lbl, const char *reg, gpreg f) (GP(f) >> 7)&1u ? '+' : '-', (GP(f) >> 10)&1u ? '+' : '-', (GP(f) >> 11)&1u ? '+' : '-'); + printf(";;\t\tcond:"); + if (GP(f)&CF) printf(" c/b/nae"); else printf(" nc/ae/nb"); + if (GP(f)&ZF) printf(" e/z"); else printf(" ne/nz"); + if (GP(f)&SF) printf(" s"); else printf(" ns"); + if (GP(f)&OF) printf(" o"); else printf(" no"); + if (GP(f)&PF) printf(" p"); else printf(" np"); + if ((GP(f)&CF) || (GP(f)&ZF)) printf(" be/na"); else printf(" a/nbe"); + if (!(GP(f)&OF) == !(GP(f)&SF)) printf(" ge/nl"); else printf(" l/nge"); + if (!(GP(f)&OF) == !(GP(f)&SF) && !(GP(f)&ZF)) + printf(" g/nle"); else printf(" le/ng"); + putchar('\n'); printf(";;\t\tsystem: %ctf %cif iopl=%d %cnt " "%crf %cvm %cac %cvif %cvip %cid\n", (GP(f) >> 8)&1u ? '+' : '-', @@ -548,6 +568,11 @@ void regdump_simd(const struct regmap *map) #if CPUFAM_ARMEL +#define NF (1u << 31) +#define ZF (1u << 30) +#define CF (1u << 29) +#define VF (1u << 28) + unsigned regdump__flags = 0; void regdump_init(void) @@ -556,6 +581,17 @@ void regdump_init(void) if (cpu_feature_p(CPUFEAT_ARM_D32)) regdump__flags |= REGF_D32; } +static void dump_conditions(unsigned f) +{ + if (f&NF) printf(" mi"); else printf(" pl"); + if (f&ZF) printf(" eq"); else printf(" ne"); + if (f&CF) printf(" cs/hs"); else printf(" cc/lo"); + if (f&VF) printf(" vs"); else printf(" vc"); + if ((f&CF) && !(f&ZF)) printf(" hi"); else printf(" ls"); + if (!(f&VF) == !(f&NF)) printf(" ge"); else printf(" lt"); + if (!(f&VF) == !(f&NF) && !(f&ZF)) printf(" gt"); else printf(" le"); +} + static void dump_flags(const char *lbl, unsigned f) { static const char @@ -569,7 +605,7 @@ static void dump_flags(const char *lbl, unsigned f) printf(";; "); if (lbl) printf("%s: ", lbl); printf(" cpsr = 0x%08x\n", f); - printf(";;\t\tuser: %cn %cz %cc %cv %cq ge=%c%c%c%c\n", + printf(";;\t\tuser: %cn %cz %cc %cv %cq ge=%c%c%c%c;", (f >> 31)&1u ? '+' : '-', (f >> 30)&1u ? '+' : '-', (f >> 29)&1u ? '+' : '-', @@ -579,6 +615,7 @@ static void dump_flags(const char *lbl, unsigned f) (f >> 18)&1u ? '1' : '0', (f >> 17)&1u ? '1' : '0', (f >> 16)&1u ? '1' : '0'); + dump_conditions(f); putchar('\n'); printf(";;\t\tsystem: %cj it=%s:%c%c%c%c %ce %ca %ci %cf %ct m=%s\n", (f >> 24)&1u ? '+' : '-', condtab[(f >> 12)&15u], @@ -601,12 +638,13 @@ static void dump_fpflags(const char *lbl, unsigned f) printf(";; "); if (lbl) printf("%s: ", lbl); printf(" fpscr = 0x%08x\n", f); - printf(";;\t\tcond: %cn %cz %cc %cv %cqc\n", + printf(";;\t\tcond: %cn %cz %cc %cv %cqc;", (f >> 31)&1u ? '+' : '-', (f >> 30)&1u ? '+' : '-', (f >> 29)&1u ? '+' : '-', (f >> 28)&1u ? '+' : '-', (f >> 27)&1u ? '+' : '-'); + dump_conditions(f); putchar('\n'); printf(";;\t\ttrap: %cide %cixe %cufe %cofe %cdze %cioe\n", (f >> 15)&1u ? '+' : '-', (f >> 12)&1u ? '+' : '-', @@ -673,18 +711,35 @@ void regdump_simd(const struct regmap *map) { ; } #if CPUFAM_ARM64 +#define NF (1u << 31) +#define ZF (1u << 30) +#define CF (1u << 29) +#define VF (1u << 28) + void regdump_init(void) { ; } +static void dump_conditions(unsigned f) +{ + if (f&NF) printf(" mi"); else printf(" pl"); + if (f&ZF) printf(" eq"); else printf(" ne"); + if (f&CF) printf(" cs/hs"); else printf(" cc/lo"); + if (f&VF) printf(" vs"); else printf(" vc"); + if ((f&CF) && !(f&ZF)) printf(" hi"); else printf(" ls"); + if (!(f&VF) == !(f&NF)) printf(" ge"); else printf(" lt"); + if (!(f&VF) == !(f&NF) && !(f&ZF)) printf(" gt"); else printf(" le"); +} + static void dump_flags(const char *lbl, unsigned f) { printf(";; "); if (lbl) printf("%s: ", lbl); printf(" nzcv = 0x%08x\n", f); - printf(";;\t\tuser: %cn %cz %cc %cv\n", + printf(";;\t\tuser: %cn %cz %cc %cv;", (f >> 31)&1u ? '+' : '-', (f >> 30)&1u ? '+' : '-', (f >> 29)&1u ? '+' : '-', (f >> 28)&1u ? '+' : '-'); + dump_conditions(f); putchar('\n'); } static void dump_fpflags(const char *lbl, const struct fpsave *fp) @@ -695,12 +750,13 @@ static void dump_fpflags(const char *lbl, const struct fpsave *fp) printf(";; "); if (lbl) printf("%s: ", lbl); printf(" fpsr = 0x%08x\n", fp->fpsr); - printf(";;\t\tcond: %cn %cz %cc %cv %cqc\n", + printf(";;\t\tcond_a32: %cn %cz %cc %cv %cqc;", (fp->fpsr >> 31)&1u ? '+' : '-', (fp->fpsr >> 30)&1u ? '+' : '-', (fp->fpsr >> 29)&1u ? '+' : '-', (fp->fpsr >> 28)&1u ? '+' : '-', (fp->fpsr >> 27)&1u ? '+' : '-'); + dump_conditions(fp->fpsr); putchar('\n'); printf(";;\t\terror: %cidc %cixc %cufc %cofc %cdzc %cioc\n", (fp->fpsr >> 7)&1u ? '+' : '-', (fp->fpsr >> 4)&1u ? '+' : '-', @@ -803,6 +859,11 @@ void regdump(const void *base, const char *lbl, uint32 f) } switch (f®F_SRCMASK) { + + case REGSRC_NONE: + printf(";; %s\n", lbl); + return; + case REGSRC_ABS: p = base; break; diff --git a/base/regdump.h b/base/regdump.h index f5b33068..3a6d59cb 100644 --- a/base/regdump.h +++ b/base/regdump.h @@ -135,6 +135,7 @@ union gp64 { uint64 u64; int64 i64; PTR64 }; #define REGSRC_SIMD 0x04000000 /* SIMD vector register */ #define REGSRC_STMMX 0x05000000 /* x86-specific: x87/MMX register */ #define REGSRC_SEG 0x06000000 /* x86-specific: segment register */ +#define REGSRC_NONE 0x0f000000 /* just a message */ /* Where to find the values. */ #define REGF_WDMASK 0xf0000000 @@ -271,11 +272,11 @@ struct regmap { regmap_avx = 2*WORDSZ regmap_size = 3*WORDSZ -#define REGDEF_GPX86_COMMON(rn, RN) \ - regsrc.e##rn = REGSRC_GP | REGIX_##RN; \ +#define REGDEF_GPX86_COMMON(rn, ix) \ + regsrc.e##rn = REGSRC_GP | ix; \ regty.e##rn = REGF_32; \ regfmt.e##rn = REGF_HEX; \ - regsrc.r##rn = REGSRC_GP | REGIX_##RN; \ + regsrc.r##rn = REGSRC_GP | ix; \ regty.r##rn = REGF_64; \ regfmt.r##rn = REGF_HEX @@ -289,7 +290,7 @@ struct regmap { regsrc.rn##x = REGSRC_GP | REGIX_##RN##X; \ regty.rn##x = REGF_16; \ regfmt.rn##x = REGF_HEX; \ - REGDEF_GPX86_COMMON(rn##x, RN##X) + REGDEF_GPX86_COMMON(rn##x, REGIX_##RN##X) REGDEF_GPX86_ABCD(a, A) REGDEF_GPX86_ABCD(b, B) REGDEF_GPX86_ABCD(c, C) @@ -312,7 +313,7 @@ REGDEF_GPX86_ABCD(d, D) regsrc.rn = REGSRC_GP | REGIX_##RN; \ regty.rn = REGF_16; \ regfmt.rn = REGF_HEX; \ - REGDEF_GPX86_COMMON(rn, RN) + REGDEF_GPX86_COMMON(rn, REGIX_##RN) REGDEF_GPX86_XP(ip, IP) REGDEF_GPX86_XP(si, SI) REGDEF_GPX86_XP(di, DI) @@ -381,32 +382,32 @@ DO8(REGDEF_SIMD) // Stash r/eax. This is bletcherous: hope we don't get a signal in // the next few instructions. - mov [R_sp(r) - REGDUMP_SPADJ + (REGIX_AX - 1)*WORDSZ], R_a(r) + mov [SP - REGDUMP_SPADJ + (REGIX_AX - 1)*WORDSZ], AX .ifnes "\addr", "nil" // Collect the effective address for the following dump, leaving it // in the `addr' slot of the dump. - lea R_a(r), \addr - mov [R_sp(r) - REGDUMP_SPADJ + (REGIX_ADDR - 1)*WORDSZ], R_a(r) + lea AX, \addr + mov [SP - REGDUMP_SPADJ + (REGIX_ADDR - 1)*WORDSZ], AX .endif // Make space for the register save area. On AMD64 with System/V // ABI, also skip the red zone. Use `lea' here to preserve the // flags. - lea R_sp(r), [R_sp(r) - REGDUMP_SPADJ] + lea SP, [SP - REGDUMP_SPADJ] // Save flags and general-purpose registers. On 32-bit x86, we save // ebx here and establish a GOT pointer here for the benefit of the // PLT-indirect calls made later on. pushf # if CPUFAM_X86 - mov [esp + 4*REGIX_BX], ebx + mov [SP + 4*REGIX_BX], ebx ldgot # endif callext F(regdump_gpsave) // Make space for the extended registers. - sub R_sp(r), R_c(r) + sub SP, CX callext F(regdump_xtsave) // Prepare for calling back into C. On 32-bit x86, leave space for @@ -414,11 +415,11 @@ DO8(REGDEF_SIMD) // the `shadow space' for the called-function's arguments. Also, // forcibly align the stack pointer to a 16-byte boundary. # if CPUFAM_X86 - sub esp, 16 + sub SP, 16 # elif ABI_WIN - sub rsp, 32 + sub SP, 32 # endif - and R_sp(r), ~15 + and SP, ~15 .endm .macro _rstrregs @@ -426,27 +427,38 @@ DO8(REGDEF_SIMD) // We assume r/ebp still points to the register map. callext F(regdump_xtrstr) - mov R_sp(r), R_bp(r) + mov SP, BP callext F(regdump_gprstr) popf - lea R_sp(r), [R_sp(r) + REGDUMP_SPADJ] + lea SP, [SP + REGDUMP_SPADJ] +.endm + +.macro _nilbase +# if CPUFAM_X86 + xor eax, eax + mov [SP + 0], eax +# elif ABI_SYSV + xor edi, edi +# elif ABI_WIN + xor ecx, ecx +# endif .endm .macro _regbase # if CPUFAM_X86 - mov [esp + 0], ebp + mov [SP + 0], BP # elif ABI_SYSV - mov rdi, rbp + mov rdi, BP # elif ABI_WIN - mov rcx, rbp + mov rcx, BP # endif .endm .macro _membase - mov R_a(r), [R_bp(r) + regmap_gp] + mov AX, [BP + regmap_gp] # if CPUFAM_X86 mov eax, [eax + REGIX_ADDR*WORDSZ] - mov [esp + 0], eax + mov [SP + 0], eax # elif ABI_SYSV mov rdi, [rax + REGIX_ADDR*WORDSZ] # elif ABI_WIN @@ -457,7 +469,7 @@ DO8(REGDEF_SIMD) .macro _reglbl msg .ifeqs "\msg", "" # if CPUFAM_X86 - mov dword ptr [esp + 4], 0 + mov dword ptr [SP + 4], 0 # elif ABI_SYSV xor esi, esi # elif ABI_WIN @@ -466,7 +478,7 @@ DO8(REGDEF_SIMD) .else # if CPUFAM_X86 lea eax, [INTADDR(.L$_reglbl$\@)] - mov [esp + 4], eax + mov [SP + 4], eax # elif ABI_SYSV lea rsi, [INTADDR(.L$_reglbl$\@)] # elif ABI_WIN @@ -481,7 +493,7 @@ DO8(REGDEF_SIMD) .macro _regfmt arg # if CPUFAM_X86 - mov dword ptr [esp + 8], \arg + mov dword ptr [SP + 8], \arg # elif ABI_SYSV mov edx, \arg # elif ABI_WIN @@ -600,6 +612,8 @@ DO16(REGDEF_NEONQ) sub r13, r13, #REGDUMP_GPSIZE // Save flags and general-purpose registers. + mrs r14, cpsr + str r14, [r13, #4*REGIX_CPSR] str r12, [r13, #4*12] bl regdump_gpsave @@ -625,6 +639,10 @@ DO16(REGDEF_NEONQ) add r13, r13, #REGDUMP_GPSIZE .endm +.macro _nilbase + mov r0, #0 +.endm + .macro _regbase mov r0, r5 .endm @@ -756,8 +774,15 @@ DO32(REGDEF_FP) // Make space for the register save area. sub sp, sp, #REGDUMP_GPSIZE - // Save flags and general-purpose registers. - stp x16, x17, [sp, #8*16] + // Save flags and general-purpose registers. The PLT linkage code + // makes free with x8--x17, so we must save those here. + mrs x30, nzcv + str x30, [sp, #8*REGIX_NZCV] + stp x8, x9, [sp, #64] + stp x10, x11, [sp, #80] + stp x12, x13, [sp, #96] + stp x14, x15, [sp, #112] + stp x16, x17, [sp, #128] bl regdump_gpsave // Make space for the extended registers. @@ -776,6 +801,10 @@ DO32(REGDEF_FP) add sp, sp, #REGDUMP_GPSIZE .endm +.macro _nilbase + mov x0, #0 +.endm + .macro _regbase mov x0, x21 .endm @@ -889,6 +918,15 @@ extern void regdump_freshline(void); _rstrregs .endm +.macro msg lbl + _saveregs + _nilbase + _reglbl "\lbl" + _regfmt REGSRC_NONE | (1 << REGF_WDSHIFT) + callext F(regdump) + _rstrregs +.endm + .macro reg lbl, rn, fmt=0 _saveregs _regbase diff --git a/base/rsvr.c b/base/rsvr.c index a468db76..3be9ed15 100644 --- a/base/rsvr.c +++ b/base/rsvr.c @@ -231,6 +231,7 @@ int rsvr_done(rsvr_state *st) #include #include #include +#include #include #include @@ -305,12 +306,12 @@ static void parse_intlist(uint_v *v, const char *p) int e = errno; for (;;) { - while (isspace((unsigned char)*p)) p++; + while (ISSPACE(*p)) p++; if (!*p) break; if (*p == ',') p++; - while (isspace((unsigned char)*p)) p++; + while (ISSPACE(*p)) p++; errno = 0; n = strtoul(p, &q, 0); - if (errno || (*q && *q != ',' && !isspace((unsigned char)*q))) + if (errno || (*q && *q != ',' && !ISSPACE(*q))) die(1, "invalid int list"); p = q; DA_PUSH(v, n); } diff --git a/base/test-regdump-x86ish.S b/base/test-regdump-x86ish.S index a8c8d435..41ba77f7 100644 --- a/base/test-regdump-x86ish.S +++ b/base/test-regdump-x86ish.S @@ -10,9 +10,9 @@ vec: FUNC(main) - pushreg R_bp(r) + pushreg BP setfp - and R_sp(r), ~15 + and SP, ~15 endprologue fldz @@ -32,7 +32,7 @@ FUNC(main) xor eax, eax dropfp - popreg R_bp(r) + popreg BP ret ENDFUNC diff --git a/configure.ac b/configure.ac index 5335a2e9..4e8f4899 100644 --- a/configure.ac +++ b/configure.ac @@ -128,7 +128,7 @@ AC_CACHE_CHECK( .intel_syntax noprefix .globl foo foo: - adcd var, 0 + adc dword ptr var, 0 ret .data var: .long 1 @@ -430,7 +430,7 @@ dnl Set the master libraries we need. AC_SUBST([CATACOMB_LIBS]) dnl Necessary support libraries. -PKG_CHECK_MODULES([mLib], [mLib >= 2.3.0]) +PKG_CHECK_MODULES([mLib], [mLib >= 2.4.1]) AM_CFLAGS="$AM_CFLAGS $mLib_CFLAGS" dnl-------------------------------------------------------------------------- diff --git a/debian/.gitignore b/debian/.gitignore index b97ed0c2..4b961cd8 100644 --- a/debian/.gitignore +++ b/debian/.gitignore @@ -5,5 +5,6 @@ substvars *.debhelper catacomb catacomb-bin +catacomb-data catacomb-dev catacomb2 diff --git a/debian/catacomb2.symbols b/debian/catacomb2.symbols index 66f19beb..a8237d89 100644 --- a/debian/catacomb2.symbols +++ b/debian/catacomb2.symbols @@ -24,6 +24,7 @@ libcatacomb.so.2 catacomb2 #MINVER# cpu_feature_p@Base 2.2.3 dispatch_debug@Base 2.2.3 (optional|arch=i386 amd64)dispatch_x86ish_cpuid@Base 2.5.0 + (optional|arch=i386 amd64)dispatch_x86ish_rdrand@Base 2.6.0 (optional|arch=i386 amd64)dispatch_x86ish_xmmregisters_p@Base 2.5.0 ## regdump (available with `--enable-asm-debug') @@ -127,14 +128,20 @@ libcatacomb.so.2 catacomb2 #MINVER# (optional|arch=i386)mpx_umul4_x86_avx@Base 2.3.0 (optional|arch=amd64)mpx_umul4_amd64_sse2@Base 2.3.0 (optional|arch=amd64)mpx_umul4_amd64_avx@Base 2.3.0 + (optional|arch=armel armhf)mpx_umul4_arm_neon@Base 2.6.0 + (optional|arch=arm64)mpx_umul4_arm64_simd@Base 2.6.0 (optional|arch=i386)mpxmont_mul4_x86_sse2@Base 2.3.0 (optional|arch=i386)mpxmont_mul4_x86_avx@Base 2.3.0 (optional|arch=amd64)mpxmont_mul4_amd64_sse2@Base 2.3.0 (optional|arch=amd64)mpxmont_mul4_amd64_avx@Base 2.3.0 + (optional|arch=armel armhf)mpxmont_mul4_arm_neon@Base 2.6.0 + (optional|arch=arm64)mpxmont_mul4_arm64_simd@Base 2.6.0 (optional|arch=i386)mpxmont_redc4_x86_sse2@Base 2.3.0 (optional|arch=i386)mpxmont_redc4_x86_avx@Base 2.3.0 (optional|arch=amd64)mpxmont_redc4_amd64_sse2@Base 2.3.0 (optional|arch=amd64)mpxmont_redc4_amd64_avx@Base 2.3.0 + (optional|arch=armel armhf)mpxmont_redc4_arm_neon@Base 2.6.0 + (optional|arch=arm64)mpxmont_redc4_arm64_simd@Base 2.6.0 ## mparena mparena_create@Base 2.0.0 @@ -314,6 +321,10 @@ libcatacomb.so.2 catacomb2 #MINVER# ## mp-sqrt mp_sqrt@Base 2.1.1 +## mp-nthrt + mp_nthrt@Base 2.6.99~ + mp_perfect_power_p@Base 2.6.99~ + ## mptext mp_read@Base 2.1.2 mp_readdstr@Base 2.1.2 @@ -646,6 +657,7 @@ libcatacomb.so.2 catacomb2 #MINVER# rand_seed@Base 2.2.3 rand_quick@Base 2.5.2 (optional|arch=i386 amd64)rand_quick_x86ish_rdrand@Base 2.5.0 + (optional|arch=i386 amd64)rand_quick_x86ish_rdseed@Base 2.6.0 rand_key@Base 2.5.2 rand_add@Base 2.2.3 rand_goodbits@Base 2.2.3 @@ -4041,6 +4053,7 @@ libcatacomb.so.2 catacomb2 #MINVER# keccak1600_init@Base 2.4.0 keccak1600_p@Base 2.4.0 keccak1600_mix@Base 2.4.0 + keccak1600_set@Base 2.6.0 keccak1600_extract@Base 2.4.0 ## sha3 common @@ -4179,6 +4192,22 @@ libcatacomb.so.2 catacomb2 #MINVER# kmac128_rand@Base 2.0.0 kmac256_rand@Base 2.0.0 +## strobe + strobe_init@Base 2.6.0 + strobe_begin@Base 2.6.0 + strobe_process@Base 2.6.0 + strobe_done@Base 2.6.0 + strobe_key@Base 2.6.0 + strobe_ad@Base 2.6.0 + strobe_prf@Base 2.6.0 + strobe_clrout@Base 2.6.0 + strobe_clrin@Base 2.6.0 + strobe_encout@Base 2.6.0 + strobe_encin@Base 2.6.0 + strobe_macout@Base 2.6.0 + strobe_macin@Base 2.6.0 + strobe_ratchet@Base 2.6.0 + ## poly1305 poly1305_keyinit@Base 2.4.0 poly1305_keysz@Base 2.4.0 @@ -4409,7 +4438,9 @@ libcatacomb.so.2 catacomb2 #MINVER# key_close@Base 2.1.1 key_discard@Base 2.1.1 key_extract@Base 2.3.1 + key_extractline@Base 2.6.0 key_merge@Base 2.1.1 + key_mergeline@Base 2.6.0 ## key-misc key_byid@Base 2.1.1 diff --git a/debian/changelog b/debian/changelog index 3e08f899..19115e97 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,50 @@ +catacomb (2.6.99~) experimental; urgency=medium + + * (placeholder for next minor version) + + -- Mark Wooding Sat, 29 Aug 2020 00:42:11 +0100 + +catacomb (2.6.2) experimental; urgency=medium + + * catacomb: Fix incorrect feature test for AESNI on Intel processors. + (This was introduced in 2.6.0. Workaround for affected processors on + 2.6.0 and 2.6.1: set `CATACOMB_CPUFEAT' to `-x86:aesni'.) + + -- Mark Wooding Sat, 13 Jun 2020 18:10:53 +0100 + +catacomb (2.6.1) experimental; urgency=medium + + * catacomb: Fix segfault from `rand_quick' on i386. + + -- Mark Wooding Mon, 25 May 2020 17:45:02 +0100 + +catacomb (2.6.0) experimental; urgency=medium + + * catacomb: Introduce Mike Hamburg's `STROBE' syymetric encryption + framework, based on Keccak. + * catacomb: Fix KCDSA prime generation so that it makes primes of + exactly the right length. I think this is the last of the prime- + generation algorthms that needs fixing. + * catacomb: Inttroduce low-level key-file functions to accommodate + Python 3 bindings. + * catacomb: Support `tag:', `id:' and `type:' prefixes in `bytag' key + queries. + * catacomb-bin: Be consistent about metasyntax used to denote hash + function names. + * catacomb: Introduce fast SIMD multiplication for ARM32 and ARM64 + platforms. I think this finally means that X86 and ARM have similar + levels of optimization. + * catacomb: Check SIMD feature bit on ARM64 before using the optimized + code. I don't know of any ARM64 implementations which lack SIMD + instructions, but the bit must be there for a reason, so I might as + well use it. + * catacomb-dev: Allow reading the current number of passes from a + `dsarand' object. + * catacomb: Prefer X84 `rdseed' instruction for quick entropy over + `rdrand' if it's available. + + -- Mark Wooding Sat, 09 May 2020 17:38:45 +0100 + catacomb (2.5.2) experimental; urgency=medium * Merge changes from 2.4.5. diff --git a/debian/control b/debian/control index c22c2559..89903a11 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: catacomb Section: libs Priority: extra Build-Depends: debhelper (>= 10), python, valgrind [!armel], pkg-config, - mlib-dev (>= 2.3.0) + mlib-dev (>= 2.4.1) Maintainer: Mark Wooding Standards-Version: 3.1.1 diff --git a/key/key-attr.c b/key/key-attr.c index e884d699..567fb92f 100644 --- a/key/key-attr.c +++ b/key/key-attr.c @@ -34,6 +34,7 @@ #include #include +#include #include #include "key.h" @@ -54,7 +55,7 @@ int key_chkident(const char *p) if (!p || !*p || strlen(p) > 255) return (-1); while (*p) { - if (*p == ':' || *p == '.' || isspace((unsigned char)*p)) + if (*p == ':' || *p == '.' || ISSPACE(*p)) return (-1); p++; } @@ -252,8 +253,7 @@ int key_settag(key_file *f, key *k, const char *tag) /* --- See if the new tag is the same as the old one --- */ - if ((!tag && !k->tag) || - (tag && k->tag && strcmp(tag, k->tag) == 0)) + if ((!tag && !k->tag) || (tag && k->tag && STRCMP(tag, ==, k->tag))) return (0); /* --- Allocate an entry for the new tag --- */ diff --git a/key/key-binary.c b/key/key-binary.c index 046819a4..718dfb60 100644 --- a/key/key-binary.c +++ b/key/key-binary.c @@ -32,6 +32,7 @@ #include #include +#include #include #include diff --git a/key/key-flags.c b/key/key-flags.c index 169de2c5..7cedb856 100644 --- a/key/key-flags.c +++ b/key/key-flags.c @@ -32,6 +32,7 @@ #include #include +#include #include "key-data.h" @@ -104,10 +105,10 @@ int key_readflags(const char *p, char **pp, unsigned *ff, unsigned *mm) /* --- Look up the string in the flags table --- */ - if (sz == 4 && strncmp(p, "none", 4) == 0) + if (sz == 4 && STRNCMP(p, ==, "none", 4)) goto next; for (e = flagtab; e->name; e++) { - if (strncmp(e->name, p, sz) == 0) { + if (STRNCMP(e->name, ==, p, sz)) { if (e->name[sz] == 0) { ee = e; break; diff --git a/key/key-io.c b/key/key-io.c index 5a7ce5c3..3bb8e1d0 100644 --- a/key/key-io.c +++ b/key/key-io.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -142,234 +143,294 @@ static int insert(key_file *f, key *k) static time_t exptime(const char *p) { size_t sz = strlen(p); - if (strncmp(p, "expired", sz) == 0) + if (STRNCMP(p, ==, "expired", sz)) return (KEXP_EXPIRE); - else if (strncmp(p, "forever", sz) == 0) + else if (STRNCMP(p, ==, "forever", sz)) return (KEXP_FOREVER); else return (atol(p)); } -/* --- @key_merge@ --- * +/* --- @merge_core@ --- * * * Arguments: @key_file *f@ = pointer to file structure * @const char *file@ = name of file (for error messages) - * @FILE *fp@ = file handle to read from + * @int lno@ = line number + * @char *p@ = pointer into the line buffer (which will be + * clobbered) * @key_reporter *rep@ = error reporting function * @void *arg@ = argument for function + * @dstr *n, *v@ = scratch dynamic strings, which must be empty + * on entry and are left empty on exit * - * Returns: Error code (one of the @KERR@ constants). + * Returns: --- + * + * Use: This is the common core of @key_merge@ and @key_mergeline@. * - * Use: Reads keys from a file, and inserts them into the file. + * It is assumed that the caller has already verified that the + * keyring is writable. */ -int key_merge(key_file *f, const char *file, FILE *fp, - key_reporter *rep, void *arg) +static void merge_core(key_file *f, const char *file, int lno, char *p, + key_reporter *rep, void *arg, dstr *n, dstr *v) { - int line = 0; - dstr l = DSTR_INIT; - dstr n = DSTR_INIT, v = DSTR_INIT; + char *vf[6]; + key *k = 0; - if (!(f->f & KF_WRITE)) - return (KERR_READONLY); + /* --- Skip blank lines and comments --- * + * + * Quite what they're doing in what ought to be an automatically- + * maintained file I don't know. + */ + + while (ISSPACE(*p)) + p++; + if (!*p || *p == '#') + goto skip; + + /* --- Break the line into fields --- * + * + * There are currently six fields of interest: + * + * * The key's identification (id, tag and type). + * * The actual key data itself. + * * The key expiry time. + * * The key deletion time. + * * The attributes field. + * * Any further comments. + * + * All but the last field can contain no spaces. + */ - for (; dstr_putline(&l, fp) != EOF; DRESET(&l)) { - char *vf[6]; - char *p = l.buf; - key *k; - - /* --- Skip blank lines and comments --- * - * - * Quite what they're doing in what ought to be an automatically- - * maintained file I don't know. - */ - - line++; - while (isspace((unsigned char)*p)) - p++; - if (!*p || *p == '#') - continue; - - /* --- Break the line into fields --- * - * - * There are currently six fields of interest: - * - * * The key's identification (id, tag and type). - * * The actual key data itself. - * * The key expiry time. - * * The key deletion time. - * * The attributes field. - * * Any further comments. - * - * All but the last field can contain no spaces. - */ - - { - int n = str_split(p, vf, 5, &vf[5]); - if (n < 4) { - if (rep) - rep(file, line, "too few fields", arg); - goto skip_0; - } + { + int n = str_split(p, vf, 5, &vf[5]); + if (n < 4) { + if (rep) + rep(file, lno, "too few fields", arg); + goto skip; } + } - /* --- Allocate a new key block --- */ + /* --- Allocate a new key block --- */ - k = CREATE(key); + k = CREATE(key); + k->k = 0; + k->tag = 0; + k->type = 0; - /* --- Extract the key data into the block --- */ + /* --- Extract the key data into the block --- */ - if ((k->k = key_read(vf[1], 0)) == 0) { - if (rep) - rep(file, line, "bad key data", arg); - goto skip_1; - } + if ((k->k = key_read(vf[1], 0)) == 0) { + if (rep) + rep(file, lno, "bad key data", arg); + goto skip; + } - /* --- Decode the identification field --- * - * - * For compatibility, derive a keyid from the key data. This can only be - * done if the key encoding is binary (and presumably old-encoding binary - * at that). - */ - - { - char *q = strchr(vf[0], ':'); - char *qq; - - if (!q) { - if (k->k->e != KENC_BINARY) { - if (rep) - rep(file, line, "new-style key encoding but no keyid", arg); - goto skip_2; - } - k->id = crc32(0, k->k->u.k.k, k->k->u.k.sz); - k->type = xstrdup(vf[0]); + /* --- Decode the identification field --- * + * + * For compatibility, derive a keyid from the key data. This can only be + * done if the key encoding is binary (and presumably old-encoding binary + * at that). + */ + + { + char *q = strchr(vf[0], ':'); + char *qq; + + if (!q) { + if (k->k->e != KENC_BINARY) { + if (rep) + rep(file, lno, "new-style key encoding but no keyid", arg); + goto skip; + } + k->id = crc32(0, k->k->u.k.k, k->k->u.k.sz); + k->type = xstrdup(vf[0]); + k->tag = 0; + } else { + *q++ = 0; + k->id = strtoul(p, 0, 16); + if ((qq = strchr(q, ':')) == 0 || !qq[1]) { + if (qq) + *qq = 0; k->tag = 0; } else { - *q++ = 0; - k->id = strtoul(p, 0, 16); - if ((qq = strchr(q, ':')) == 0 || !qq[1]) { - if (qq) - *qq = 0; - k->tag = 0; - } else { - *qq++ = 0; - k->tag = xstrdup(qq); - } - k->type = xstrdup(q); + *qq++ = 0; + k->tag = xstrdup(qq); } + k->type = xstrdup(q); } + } - /* --- Get a key block for the new key --- */ + /* --- Get a key block for the new key --- */ - k->exp = exptime(vf[2]); - k->del = exptime(vf[3]); + k->exp = exptime(vf[2]); + k->del = exptime(vf[3]); - /* --- Insert the key block into the table --- */ + /* --- Insert the key block into the table --- */ - { - int err; + { + int err; - again: - if ((err = insert(f, k)) < 0) { - if (err == KERR_DUPTAG) { - if (rep) - rep(file, line, "duplicate key tag stripped", arg); - xfree(k->tag); - k->tag = 0; - goto again; - } + again: + if ((err = insert(f, k)) < 0) { + if (err == KERR_DUPTAG) { if (rep) - rep(file, line, key_strerror(err), arg); - goto skip_3; + rep(file, lno, "duplicate key tag stripped", arg); + xfree(k->tag); + k->tag = 0; + goto again; } + if (rep) + rep(file, lno, key_strerror(err), arg); + goto skip; } + } - /* --- Parse up the attributes, if specified --- */ + /* --- Parse up the attributes, if specified --- */ sym_create(&k->a); - if (vf[4] && strcmp(vf[4], "-") != 0) { + if (vf[4] && STRCMP(vf[4], !=, "-")) { url_dctx uc; - for (url_initdec(&uc, vf[4]); url_dec(&uc, &n, &v); ) { - key_putattr(f, k, n.buf, v.buf); - DRESET(&n); DRESET(&v); - } + for (url_initdec(&uc, vf[4]); url_dec(&uc, n, v); ) { + key_putattr(f, k, n->buf, v->buf); + DRESET(n); DRESET(v); } + } - /* --- Insert the comment --- */ + /* --- Insert the comment --- */ - if (vf[5]) - k->c = xstrdup(vf[5]); - else - k->c = 0; - continue; + if (vf[5]) + k->c = xstrdup(vf[5]); + else + k->c = 0; - /* --- Tidy up after something going wrong --- */ + /* --- Done --- */ - skip_3: - if (k->tag) - xfree(k->tag); - xfree(k->type); - skip_2: - key_drop(k->k); - skip_1: + f->f |= KF_MODIFIED; + return; + + /* --- Tidy up after something going wrong --- */ + +skip: + if (k) { + if (k->tag) xfree(k->tag); + if (k->type) xfree(k->type); + if (k->k) key_drop(k->k); DESTROY(k); - skip_0:; } +} + +/* --- @key_merge@, @key_mergeline@ --- * + * + * Arguments: @key_file *f@ = pointer to file structure + * @const char *file@ = name of file (for error messages) + * @int lno@ = line number (for error messages, @key_mergeline@) + * @FILE *fp@ = file handle to read from (@key_merge@) + * @const char *line@ = line from the input (@key_mergeline@) + * @key_reporter *rep@ = error reporting function + * @void *arg@ = argument for function + * + * Returns: Error code (one of the @KERR@ constants). + * + * Use: The @key_merge@ function reads keys from a file, and inserts + * them into the keyring. + * + * The @key_mergeline@ function reads a key from a single input + * line (which may, but need not, have a final newline), and + * adds it to the keyring. + * + * The @key_mergeline@ function is intended to help with + * interfacing Catacomb to runtimes which don't use C's @stdio@ + * streams, rather than as a general-purpose service, though if + * it turns out to be useful in other ways then so much the + * better. + */ - /* --- Extensive tidying up now required --- */ +int key_merge(key_file *f, const char *file, FILE *fp, + key_reporter *rep, void *arg) +{ + dstr n = DSTR_INIT, v = DSTR_INIT; + dstr l = DSTR_INIT; + int lno = 1; + + if (!(f->f & KF_WRITE)) + return (KERR_READONLY); + + for (; dstr_putline(&l, fp) != EOF; DRESET(&l)) + merge_core(f, file, lno++, l.buf, rep, arg, &n, &v); dstr_destroy(&l); - dstr_destroy(&n); - dstr_destroy(&v); - f->f |= KF_MODIFIED; + dstr_destroy(&n); dstr_destroy(&v); return (0); } -/* --- @key_extract@ --- * +int key_mergeline(key_file *f, const char *file, int lno, const char *line, + key_reporter *rep, void *arg) +{ + dstr n = DSTR_INIT, v = DSTR_INIT; + size_t len = strlen(line); + char *p; + + if (!(f->f & KF_WRITE)) return (KERR_READONLY); + + if (len && line[len - 1] == '\n') len--; + p = xmalloc(len); memcpy(p, line, len); p[len] = 0; + merge_core(f, file, lno, p, rep, arg, &n, &v); + xfree(p); dstr_destroy(&n); dstr_destroy(&v); + return (0); +} + +/* --- @key_extract@, @key_extractline@ --- * * * Arguments: @key_file *f@ = pointer to file structure * @key *k@ = key to extract - * @FILE *fp@ = file to write on + * @FILE *fp@ = file to write on (@key_extract@) + * @dstr *d@ = string to write on (@key_extractline@) * @const key_filter *kf@ = pointer to key selection block * - * Returns: Zero if OK, EOF on error. + * Returns: @key_extract@ returns zero if OK, EOF on error. + * @key_extractline@ does not return a value. + * + * Use: Extracts a key to an ouptut file or buffer. + * + * The @key_extractline@ includes a final newline in its output. * - * Use: Extracts a key to an ouptut file. + * The @key_extractline@ function is intended to help with + * interfacing Catacomb to runtimes which don't use C's @stdio@ + * streams, rather than as a general-purpose service, though if + * it turns out to be useful in other ways then so much the + * better. */ -int key_extract(key_file *f, key *k, FILE *fp, const key_filter *kf) +void key_extractline(key_file *f, key *k, dstr *d, const key_filter *kf) { - dstr d = DSTR_INIT; time_t t = time(0); /* --- Skip the key if it's deleted or unselected--- */ if (KEY_EXPIRED(t, k->del) || !key_match(k->k, kf)) - return (0); + return; /* --- Encode the key and write the easy stuff --- */ - key_fulltag(k, &d); - DPUTC(&d, ' '); - if (!key_write(k->k, &d, kf)) dstr_puts(&d, "struct:[]"); - DPUTC(&d, ' '); - dstr_write(&d, fp); - DRESET(&d); + key_fulltag(k, d); + DPUTC(d, ' '); + if (!key_write(k->k, d, kf)) dstr_puts(d, "struct:[]"); + DPUTC(d, ' '); /* --- Write out the expiry and deletion times --- */ if (KEY_EXPIRED(t, k->exp)) - fputs("expired ", fp); + dstr_puts(d, "expired "); else if (k->exp == KEXP_FOREVER) - fputs("forever ", fp); + dstr_puts(d, "forever "); else - fprintf(fp, "%li ", (long)k->exp); + dstr_putf(d, "%li ", (long)k->exp); if (k->del == KEXP_FOREVER) - fputs("forever ", fp); + dstr_puts(d, "forever "); else - fprintf(fp, "%li ", (long)k->del); + dstr_putf(d, "%li ", (long)k->del); /* --- Output the attributes --- */ @@ -382,19 +443,24 @@ int key_extract(key_file *f, key *k, FILE *fp, const key_filter *kf) url_initenc(&uc); for (sym_mkiter(&i, &k->a); (a = sym_next(&i)) != 0; ) { none = 0; - url_enc(&uc, &d, SYM_NAME(a), a->p); + url_enc(&uc, d, SYM_NAME(a), a->p); } if (none) - DPUTS(&d, "-"); - DWRITE(&d, fp); + DPUTC(d, '-'); } - dstr_destroy(&d); - if (k->c) { - putc(' ', fp); - fputs(k->c, fp); - } - putc('\n', fp); + if (k->c) + dstr_putf(d, " %s", k->c); + + DPUTC(d, '\n'); DPUTZ(d); +} + +int key_extract(key_file *f, key *k, FILE *fp, const key_filter *kf) +{ + dstr d = DSTR_INIT; + + key_extractline(f, k, &d, kf); + dstr_write(&d, fp); return (ferror(fp) ? EOF : 0); } diff --git a/key/key-misc.c b/key/key-misc.c index 0d969a87..0afad76e 100644 --- a/key/key-misc.c +++ b/key/key-misc.c @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -159,14 +160,29 @@ key *key_bytag(key_file *f, const char *tag) time_t t = time(0); char *p; uint32 id; - key_ref *kr = sym_find(&f->bytag, tag, -1, 0, 0); + enum { GUESS = -1, TAG, ID, TYPE }; int lookup; + key_ref *kr; key *k; - if (kr && !(KEY_EXPIRED(t, kr->k->exp) && KEY_EXPIRED(t, kr->k->del))) + if (STRNCMP(tag, ==, "tag:", 4)) { lookup = TAG; tag += 4; } + else if (STRNCMP(tag, ==, "id:", 3)) { lookup = ID; tag += 3; } + else if (STRNCMP(tag, ==, "type:", 5)) { lookup = TYPE; tag += 5; } + else lookup = GUESS; + + if ((lookup == GUESS || lookup == TAG) && + (kr = sym_find(&f->bytag, tag, -1, 0, 0)) != 0 && + !(KEY_EXPIRED(t, kr->k->exp) && KEY_EXPIRED(t, kr->k->del))) return (kr->k); - id = strtoul(tag, &p, 16); - if (!*p && (k = key_byid(f, id)) != 0) return (k); - return (key_bytype(f, tag)); + + if (lookup == GUESS || lookup == ID) { + id = strtoul(tag, &p, 16); + if (!*p && (k = key_byid(f, id)) != 0) return (k); + } + + if ((lookup == GUESS || lookup == TYPE) && (k = key_bytype(f, tag)) != 0) + return (k); + + return (0); } /* --- @key_qtag@ --- * diff --git a/key/key-pass.c b/key/key-pass.c index 665030e4..223486eb 100644 --- a/key/key-pass.c +++ b/key/key-pass.c @@ -29,6 +29,7 @@ #include +#include "ct.h" #include "key-data.h" #include "paranoia.h" #include "passphrase.h" @@ -172,8 +173,8 @@ int key_unlock(key_data **kt, key_data *k, const void *e, size_t esz) rmd160_macinit(&mc, &mk); rmd160_machash(&mc, k->u.k.k + RMD160_HASHSZ * 2, sz); rmd160_macdone(&mc, b + RMD160_HASHSZ); - if (memcmp(b + RMD160_HASHSZ, k->u.k.k + RMD160_HASHSZ, - RMD160_HASHSZ) != 0) { + if (!ct_memeq(b + RMD160_HASHSZ, + k->u.k.k + RMD160_HASHSZ, RMD160_HASHSZ)) { rc = KERR_BADPASS; goto fail; } diff --git a/key/key-text.c b/key/key-text.c index 98c1fba1..ace69ce1 100644 --- a/key/key-text.c +++ b/key/key-text.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -298,7 +299,7 @@ int key_write(key_data *k, dstr *d, const key_filter *kf) DPUTC(d, ':'); while (*p) { if (*p == ' ') DPUTC(d, '+'); - else if (!isalnum((unsigned char)*p)) dstr_putf(d, "%%%02x", *p); + else if (!ISALNUM(*p)) dstr_putf(d, "%%%02x", *p); else DPUTC(d, *p); p++; } diff --git a/key/key.h b/key/key.h index bed36325..96a2b5a2 100644 --- a/key/key.h +++ b/key/key.h @@ -177,36 +177,64 @@ typedef void key_reporter(const char */*file*/, int /*line*/, /*----- Reading and writing keys and files --------------------------------*/ -/* --- @key_merge@ --- * +/* --- @key_merge@, @key_mergeline@ --- * * * Arguments: @key_file *f@ = pointer to file structure * @const char *file@ = name of file (for error messages) - * @FILE *fp@ = file handle to read from + * @int lno@ = line number (for error messages, @key_mergeline@) + * @FILE *fp@ = file handle to read from (@key_merge@) + * @const char *line@ = line from the input (@key_mergeline@) * @key_reporter *rep@ = error reporting function * @void *arg@ = argument for function * * Returns: Error code (one of the @KERR@ constants). * - * Use: Reads keys from a file, and inserts them into the file. + * Use: The @key_merge@ function reads keys from a file, and inserts + * them into the keyring. + * + * The @key_mergeline@ function reads a key from a single input + * line (which may, but need not, have a final newline), and + * adds it to the keyring. + * + * The @key_mergeline@ function is intended to help with + * interfacing Catacomb to runtimes which don't use C's @stdio@ + * streams, rather than as a general-purpose service, though if + * it turns out to be useful in other ways then so much the + * better. */ extern int key_merge(key_file */*f*/, const char */*file*/, FILE */*fp*/, key_reporter */*rep*/, void */*arg*/); +extern int key_mergeline(key_file */*f*/, const char */*file*/, int /*lno*/, + const char */*line*/, + key_reporter */*rep*/, void */*arg*/); -/* --- @key_extract@ --- * +/* --- @key_extract@, @key_extractline@ --- * * * Arguments: @key_file *f@ = pointer to file structure * @key *k@ = key to extract - * @FILE *fp@ = file to write on + * @FILE *fp@ = file to write on (@key_extract@) + * @dstr *d@ = string to write on (@key_extractline@) * @const key_filter *kf@ = pointer to key selection block * - * Returns: Zero if OK, EOF on error. + * Returns: @key_extract@ returns zero if OK, EOF on error. + * @key_extractline@ does not return a value. + * + * Use: Extracts a key to an ouptut file or buffer. + * + * The @key_extractline@ includes a final newline in its output. * - * Use: Extracts a key to an ouptut file. + * The @key_extractline@ function is intended to help with + * interfacing Catacomb to runtimes which don't use C's @stdio@ + * streams, rather than as a general-purpose service, though if + * it turns out to be useful in other ways then so much the + * better. */ extern int key_extract(key_file */*f*/, key */*k*/, FILE */*fp*/, const key_filter */*kf*/); +extern void key_extractline(key_file */*f*/, key */*k*/, + dstr */*d*/, const key_filter */*kf*/); /* --- @key_open@ --- * * diff --git a/key/passphrase.c b/key/passphrase.c index 6f0780f9..3a287bde 100644 --- a/key/passphrase.c +++ b/key/passphrase.c @@ -35,6 +35,7 @@ #include #include +#include #include "passphrase.h" #include "pixie.h" @@ -122,7 +123,7 @@ int passphrase_read(const char *tag, unsigned mode, char *buf, size_t sz) char b[1024]; DRESET(&d); dstr_putf(&d, "Verify passphrase %s: ", tag); - if (pixie_getpass(d.buf, b, sizeof(b)) || strcmp(b, buf) != 0) { + if (pixie_getpass(d.buf, b, sizeof(b)) || STRCMP(b, !=, buf)) { memset(b, 0, sizeof(b)); goto fail; } diff --git a/key/pixie-common.c b/key/pixie-common.c index 2975c205..d3348bcf 100644 --- a/key/pixie-common.c +++ b/key/pixie-common.c @@ -44,6 +44,7 @@ #include #include +#include #include #include "pixie.h" @@ -283,11 +284,11 @@ again: p = buf; if ((q = str_getword(&p)) == 0) return (-1); - if (strcmp(q, "INFO") == 0) + if (STRCMP(q, ==, "INFO")) goto again; - else if (strcmp(q, "MISSING") == 0) + else if (STRCMP(q, ==, "MISSING")) return (+1); - else if (strcmp(q, "OK") != 0) + else if (STRCMP(q, !=, "OK")) return (-1); /* --- Return the final answer --- */ @@ -336,7 +337,7 @@ void pixie_set(int fd, const char *tag, const char *phrase) again: pixie_fdline(fd, buf, sizeof(buf)); p = buf; - if ((q = str_getword(&p)) != 0 && strcmp(q, "INFO") == 0) + if ((q = str_getword(&p)) != 0 && STRCMP(q, ==, "INFO")) goto again; } @@ -367,7 +368,7 @@ void pixie_cancel(int fd, const char *tag) again: pixie_fdline(fd, buf, sizeof(buf)); p = buf; - if ((q = str_getword(&p)) != 0 && strcmp(q, "INFO") == 0) + if ((q = str_getword(&p)) != 0 && STRCMP(q, ==, "INFO")) goto again; } diff --git a/m4/mdw-uint-bits.m4 b/m4/mdw-uint-bits.m4 index d5e678fa..31de01a2 100644 --- a/m4/mdw-uint-bits.m4 +++ b/m4/mdw-uint-bits.m4 @@ -2,7 +2,7 @@ dnl -*-autoconf-*- ### SYNOPSIS ### -### dnl mdw_UINT_BITS(TYPE, ABBREV) +### mdw_UINT_BITS(TYPE, ABBREV) ### ### DESCRIPTION ### diff --git a/math/Makefile.am b/math/Makefile.am index 7c4ffed4..8c53940c 100644 --- a/math/Makefile.am +++ b/math/Makefile.am @@ -100,6 +100,8 @@ libmath_la_SOURCES += mp-modsqrt.c TESTS += mp-modsqrt.t$(EXEEXT) libmath_la_SOURCES += mp-sqrt.c TESTS += mp-sqrt.t$(EXEEXT) +libmath_la_SOURCES += mp-nthrt.c +TESTS += mp-nthrt.t$(EXEEXT) libmath_la_SOURCES += mp-test.c EXTRA_DIST += t/mp @@ -192,6 +194,16 @@ MPX_MUL4_SOURCES = mpx-mul4-amd64-sse2.S check_PROGRAMS += mpx-mul4.t TESTS += mpx-mul4.t$(EXEEXT) endif +if CPUFAM_ARMEL +MPX_MUL4_SOURCES = mpx-mul4-arm-neon.S +check_PROGRAMS += mpx-mul4.t +TESTS += mpx-mul4.t$(EXEEXT) +endif +if CPUFAM_ARM64 +MPX_MUL4_SOURCES = mpx-mul4-arm64-simd.S +check_PROGRAMS += mpx-mul4.t +TESTS += mpx-mul4.t$(EXEEXT) +endif libmath_la_SOURCES += $(MPX_MUL4_SOURCES) mpx_mul4_t_SOURCES = mpx-mul4-test.c $(MPX_MUL4_SOURCES) mpx_mul4_t_CPPFLAGS = \ diff --git a/math/bittest.c b/math/bittest.c index 5f7b1d76..e335286e 100644 --- a/math/bittest.c +++ b/math/bittest.c @@ -29,6 +29,7 @@ #include #include +#include #include "bitops.h" #include "mpx.h" @@ -45,7 +46,7 @@ int main(void) buf[2] = B##string(1u, 0u) & 1u? '1' : '0'; \ buf[3] = B##string(1u, 1u) & 1u? '1' : '0'; \ buf[4] = 0; \ - if (strcmp(buf, ref) != 0) { \ + if (STRCMP(buf, !=, ref)) { \ fprintf(stderr, "mismatch ref `%s' != buf `%s'\n", ref, buf); \ rc = 1; \ } \ diff --git a/math/ec-test.c b/math/ec-test.c index d9cda6ff..ff01a2ea 100644 --- a/math/ec-test.c +++ b/math/ec-test.c @@ -215,6 +215,8 @@ const test_type type_ec = { eccvt, ecdump }; #ifdef TEST_RIG +#include + static void ecdestroy(ec_curve *c) { field *f = c->f; @@ -354,7 +356,7 @@ static int vec2osp(dstr v[]) int ok = 1; int win, wantwin; - if (strcmp(v[3].buf, "FAIL") == 0) wantwin = 0; + if (STRCMP(v[3].buf, ==, "FAIL")) wantwin = 0; else { wantwin = 1; type_hex.cvt(v[3].buf, &d); } dstr_ensure(&dd, n); buf_init(&b, dd.buf, n); @@ -362,7 +364,7 @@ static int vec2osp(dstr v[]) win = !ec_ec2osp(e, f, &b, p); if (!win != !wantwin || (win && (BLEN(&b) != d.len || - memcmp(BBASE(&b), d.buf, BLEN(&b)) != 0))) { + MEMCMP(BBASE(&b), !=, d.buf, BLEN(&b))))) { ok = 0; fprintf(stderr, "ec2osp failed"); fprintf(stderr, "\ncurve = "); type_ecurve.dump(v, stderr); @@ -393,7 +395,7 @@ static int vos2ecp(dstr v[]) int ok = 1; int win; - if (strcmp(v[3].buf, "FAIL") == 0) p = 0; + if (STRCMP(v[3].buf, ==, "FAIL")) p = 0; else { type_ec.cvt(v[3].buf, &d); p = (ec *)d.buf; } buf_init(&b, v[2].buf, v[2].len); diff --git a/math/f25519.c b/math/f25519.c index a886465e..a0f40fa0 100644 --- a/math/f25519.c +++ b/math/f25519.c @@ -183,18 +183,18 @@ void f25519_load(f25519 *z, const octet xv[32]) * and lower bounds are achievable. * * All of the x_i at this point are positive, so we don't need to do - * anything wierd when masking them. + * anything weird when masking them. */ - b = x9&B24; c = 19&((b >> 19) - (b >> 24)); x9 -= b << 1; - b = x8&B25; x9 += b >> 25; x8 -= b << 1; - b = x7&B24; x8 += b >> 24; x7 -= b << 1; - b = x6&B25; x7 += b >> 25; x6 -= b << 1; - b = x5&B24; x6 += b >> 24; x5 -= b << 1; - b = x4&B25; x5 += b >> 25; x4 -= b << 1; - b = x3&B24; x4 += b >> 24; x3 -= b << 1; - b = x2&B25; x3 += b >> 25; x2 -= b << 1; - b = x1&B24; x2 += b >> 24; x1 -= b << 1; - b = x0&B25; x1 += (b >> 25) + (x0 >> 26); x0 = (x0&M26) - (b << 1); + b = x9&B24; c = 19&((b >> 19) - (b >> 24)); x9 -= b << 1; + b = x8&B25; x9 += b >> 25; x8 -= b << 1; + b = x7&B24; x8 += b >> 24; x7 -= b << 1; + b = x6&B25; x7 += b >> 25; x6 -= b << 1; + b = x5&B24; x6 += b >> 24; x5 -= b << 1; + b = x4&B25; x5 += b >> 25; x4 -= b << 1; + b = x3&B24; x4 += b >> 24; x3 -= b << 1; + b = x2&B25; x3 += b >> 25; x2 -= b << 1; + b = x1&B24; x2 += b >> 24; x1 -= b << 1; + b = x0&B25; x1 += (b >> 25) + (x0 >> 26); x0 = (x0&M26) - (b << 1); x0 += c; /* And with that, we're done. */ @@ -756,7 +756,7 @@ static void carry_reduce(dblpiece x[NPIECE]) * signed. */ - /*For each piece, we bias it so that floor division (as done by an + /* For each piece, we bias it so that floor division (as done by an * arithmetic right shift) and modulus (as done by bitwise-AND) does the * right thing. */ @@ -1178,6 +1178,7 @@ int f25519_quosqrt(f25519 *z, const f25519 *x, const f25519 *y) #ifdef TEST_RIG +#include #include #include #include @@ -1218,7 +1219,7 @@ static void dump_f25519_ref(dstr *d, FILE *fp) } static int eq(const f25519 *x, dstr *d) - { octet b[32]; f25519_store(b, x); return (memcmp(b, d->buf, 32) == 0); } + { octet b[32]; f25519_store(b, x); return (MEMCMP(b, ==, d->buf, 32)); } static const test_type type_f25519 = { cvt_f25519, dump_f25519 }, diff --git a/math/fgoldi.c b/math/fgoldi.c index 1b09b58e..43309dcd 100644 --- a/math/fgoldi.c +++ b/math/fgoldi.c @@ -962,6 +962,7 @@ int fgoldi_quosqrt(fgoldi *z, const fgoldi *x, const fgoldi *y) #ifdef TEST_RIG +#include #include #include #include @@ -1002,7 +1003,7 @@ static void dump_fgoldi_ref(dstr *d, FILE *fp) } static int eq(const fgoldi *x, dstr *d) - { octet b[56]; fgoldi_store(b, x); return (memcmp(b, d->buf, 56) == 0); } + { octet b[56]; fgoldi_store(b, x); return (MEMCMP(b, ==, d->buf, 56)); } static const test_type type_fgoldi = { cvt_fgoldi, dump_fgoldi }, diff --git a/math/g-ec.c b/math/g-ec.c index 1b6c16e3..5d922ddd 100644 --- a/math/g-ec.c +++ b/math/g-ec.c @@ -29,6 +29,7 @@ #include +#include #include #define ge ec @@ -99,15 +100,15 @@ static int gread(group *gg, ec *d, const mptext_ops *ops, void *p) { int ch; ch = ops->get(p); - if (tolower(ch) == 'i') { - if (tolower(ops->get(p)) != 'n' || tolower(ops->get(p)) != 'f') + if (TOLOWER(ch) == 'i') { + if (TOLOWER(ops->get(p)) != 'n' || TOLOWER(ops->get(p)) != 'f') return (-1); EC_SETINF(d); return (0); } ops->unget(ch, p); if ((t.x = mp_read(MP_NEW, 0, ops, p)) == 0) goto done; - do ch = ops->get(p); while (ch == ',' || isspace(ch)); ops->unget(ch, p); + do ch = ops->get(p); while (ch == ',' || ISSPACE(ch)); ops->unget(ch, p); if ((t.y = mp_read(MP_NEW, 0, ops, p)) == 0) goto done; EC_IN(g->ei.c, &t, &t); if (EC_CHECK(g->ei.c, &t)) goto done; diff --git a/math/genprimes.c b/math/genprimes.c index 8b47ac94..1ed7b4d1 100644 --- a/math/genprimes.c +++ b/math/genprimes.c @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -123,8 +124,8 @@ int main(int argc, char *argv[]) if (!sym) { for (q = header; *q; q++) { int ch = (unsigned char)*q; - if (isalnum(ch)) - ch = toupper(ch); + if (ISALNUM(ch)) + ch = TOUPPER(ch); else ch = '_'; DPUTC(&d, ch); diff --git a/math/genwheel.c b/math/genwheel.c index 9bf3cbe1..0354cc28 100644 --- a/math/genwheel.c +++ b/math/genwheel.c @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -127,8 +128,8 @@ int main(int argc, char *argv[]) if (!sym) { for (q = header; *q; q++) { int ch = (unsigned char)*q; - if (isalnum(ch)) - ch = toupper(ch); + if (ISALNUM(ch)) + ch = TOUPPER(ch); else ch = '_'; DPUTC(&d, ch); diff --git a/math/gfreduce.c b/math/gfreduce.c index 99f3bea5..f29c1048 100644 --- a/math/gfreduce.c +++ b/math/gfreduce.c @@ -554,12 +554,16 @@ mp *gfreduce_quadsolve(const gfreduce *r, mp *d, mp *x) * * Choose %$\rho \inr \gf{2^m}$% with %$\Tr(\rho) = 1$%. Let * %$z = \sum_{0\le i +#include #include #include "group.h" @@ -108,7 +109,7 @@ static int vcheck(dstr *v) gr->ops->destroy(gr); if (!e) e = "ok"; G_DESTROYGROUP(g); - if (strcmp(e, v[1].buf)) { + if (STRCMP(e, !=, v[1].buf)) { ok = 0; fprintf(stderr, "*** check failed\n"); fprintf(stderr, "*** group: %s\n", v[0].buf); @@ -390,7 +391,7 @@ static int vtobuf(dstr *v) ic = G_TOBUF(g, &b, x); c.len = BLEN(&b); if (ic != ir || (!ic && (c.len != v[3].len || - memcmp(c.buf, v[3].buf, c.len)))) { + MEMCMP(c.buf, !=, v[3].buf, c.len)))) { ok = 0; fprintf(stderr, "*** tobuf failed\n"); fprintf(stderr, "*** group: %s\n", v[0].buf); @@ -455,7 +456,7 @@ static int vtoraw(dstr *v) ic = G_TORAW(g, &b, x); c.len = BLEN(&b); if (ic != ir || (!ic && (c.len != v[3].len || - memcmp(c.buf, v[3].buf, c.len)))) { + MEMCMP(c.buf, !=, v[3].buf, c.len)))) { ok = 0; fprintf(stderr, "*** toraw failed\n"); fprintf(stderr, "*** group: %s\n", v[0].buf); diff --git a/math/mp-arith.c b/math/mp-arith.c index 470620ef..048f4c66 100644 --- a/math/mp-arith.c +++ b/math/mp-arith.c @@ -700,6 +700,8 @@ mp *mp_leastcongruent(mp *d, mp *b, mp *r, mp *m) #ifdef TEST_RIG +#include + static int verify(const char *op, mp *expect, mp *result, mp *a, mp *b) { if (!MP_EQ(expect, result)) { @@ -787,11 +789,11 @@ MPX_DOBIN(DO) mp *r = *(mp **)v[3].buf; mp *c; - if (strcmp(v[0].buf, "and") == 0) op = 1; - else if (strcmp(v[0].buf, "or") == 0) op = 7; - else if (strcmp(v[0].buf, "nand") == 0) op = 14; - else if (strcmp(v[0].buf, "nor") == 0) op = 8; - else if (strcmp(v[0].buf, "xor") == 0) op = 6; + if (STRCMP(v[0].buf, ==, "and")) op = 1; + else if (STRCMP(v[0].buf, ==, "or")) op = 7; + else if (STRCMP(v[0].buf, ==, "nand")) op = 14; + else if (STRCMP(v[0].buf, ==, "nor")) op = 8; + else if (STRCMP(v[0].buf, ==, "xor")) op = 6; else { char *p = v[0].buf; while (*p) { diff --git a/math/mp-nthrt.c b/math/mp-nthrt.c new file mode 100644 index 00000000..b253d2fe --- /dev/null +++ b/math/mp-nthrt.c @@ -0,0 +1,315 @@ +/* -*-c-*- + * + * Calculating %$n$%th roots + * + * (c) 2020 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software: you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "mp.h" +#include "mpint.h" +#include "mplimits.h" +#include "primeiter.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @mp_nthrt@ --- * + * + * Arguments: @mp *d@ = fake destination + * @mp *a@ = an integer + * @mp *n@ = a strictly positive integer + * @int *exectp_out@ = set nonzero if an exact solution is found + * + * Returns: The integer %$\bigl\lfloor \sqrt[n]{a} \bigr\rfloor$%. + * + * Use: Return an approximation to the %$n$%th root of %$a$%. + * Specifically, it returns the largest integer %$x$% such that + * %$x^n \le a$%. If %$x^n = a$% then @*exactp_out@ is set + * nonzero; otherwise it is set zero. (If @exactp_out@ is null + * then this information is discarded.) + * + * The exponent %$n$% must be strictly positive: it's not clear + * to me what the right answer is for %$n \le 0$%. If %$a$% is + * negative then %$n$% must be odd; otherwise there is no real + * solution. + */ + +mp *mp_nthrt(mp *d, mp *a, mp *n, int *exactp_out) +{ + /* We want to find %$x$% such that %$x^n = a$%. Newton--Raphson says we + * start with a guess %$x_i$%, and refine it to a better guess %$x_{i+1}$% + * which is where the tangent to the curve %$y = x^n - a$% at %$x = x_i$% + * meets the %$x$%-axis. The tangent meets the curve at + * %($x_i, x_i^n - a)$%, and has gradient %$n x_i^{n-1}$%, and hence meets + * the %$x$%-axis at %$x_{i+1} = x_i - (x_i^n - a)/(n x_i^{n-1})$%. + * + * We're working with plain integers here, and we actually want + * %$\lfloor x \rfloor$%. We can check that we're close enough because + * %$x^n$% is monotonic for positive %$x$%: if %$x_i^n > a$% then we're too + * large; if %$(xi + i)^n \le a$% then we're too small; otherwise we're + * done. + */ + + mp *ai = MP_NEW, *bi = MP_NEW, *xi = MP_NEW, + *nm1 = MP_NEW, *delta = MP_NEW; + int cmp; + unsigned f = 0; +#define f_neg 1u + + /* Firstly, deal with a zero or negative %$xa%. */ + if (!MP_NEGP(a)) + MP_COPY(a); + else { + assert(MP_ODDP(n)); + f |= f_neg; + a = mp_neg(MP_NEW, a); + } + + /* Secondly, reject %$n \le 0$%. + * + * Clearly %$x^0 = 1$% for nonzero %$x$%, so if %$a$% is zero then we could + * return anything, but maybe %$1$% for concreteness; but what do we return + * for %$a \ne 1$%? For %$n < 0$% there's just no hope. + */ + assert(MP_POSP(n)); + + /* Pick a starting point. This is rather important to get right. In + * particular, for large %$n$%, if our initial guess is too small, then the + * next iteration is a wild overestimate and it takes a long time to + * converge back. + */ + nm1 = mp_sub(nm1, n, MP_ONE); + xi = mp_fromulong(xi, mp_bits(a)); + xi = mp_add(xi, xi, nm1); mp_div(&xi, 0, xi, n); + assert(MP_CMP(xi, <=, MP_ULONG_MAX)); + xi = mp_setbit(xi, MP_ZERO, mp_toulong(xi)); + + /* The main iteration. */ + for (;;) { + ai = mp_exp(ai, xi, n); + bi = mp_add(bi, xi, MP_ONE); bi = mp_exp(bi, bi, n); + cmp = mp_cmp(ai, a); + if (cmp <= 0 && MP_CMP(a, <, bi)) break; + ai = mp_sub(ai, ai, a); + bi = mp_exp(bi, xi, nm1); bi = mp_mul(bi, bi, n); + mp_div(&delta, 0, ai, bi); + if (MP_ZEROP(delta) && cmp > 0) xi = mp_sub(xi, xi, MP_ONE); + else xi = mp_sub(xi, xi, delta); + } + + /* Final sorting out of negative inputs. + * + * If the input %$a$% is not an exact %$n$%th root, then we must round + * %%\emph{up}%% so that, after negation, we implement the correct floor + * behaviour. + */ + if (f&f_neg) { + if (cmp) xi = mp_add(xi, xi, MP_ONE); + xi = mp_neg(xi, xi); + } + + /* Free up the temporary things. */ + MP_DROP(a); MP_DROP(ai); MP_DROP(bi); + MP_DROP(nm1); MP_DROP(delta); mp_drop(d); + + /* Done. */ + if (exactp_out) *exactp_out = (cmp == 0); + return (xi); + +#undef f_neg +} + +/* --- @mp_perfect_power_p@ --- * + * + * Arguments: @mp **x@ = where to write the base + * @mp **n@ = where to write the exponent + * @mp *a@ = an integer + * + * Returns: Nonzero if %$a$% is a perfect power. + * + * Use: Returns whether an integer %$a$% is a perfect power, i.e., + * whether it can be written in the form %$a = x^n$% where + * %$|x| > 1$% and %$n > 1$% are integers. If this is possible, + * then (a) store %$x$% and the largest such %$n$% in @*x@ and + * @*n@, and return nonzero; otherwise, store %$x = a$% and + * %$n = 1$% and return zero. (Either @x@ or @n@, or both, may + * be null to discard these outputs.) + * + * Note that %$-1$%, %$0$% and %$1$% are not considered perfect + * powers by this definition. (The exponent is not well-defined + * in these cases, but it seemed better to implement a function + * which worked for all integers.) Note also that %$-4$% is not + * a perfect power since it has no real square root. + */ + +int mp_perfect_power_p(mp **x, mp **n, mp *a) +{ + mp *r = MP_ONE, *p = MP_NEW, *t = MP_NEW; + int exactp, rc = 0; + primeiter pi; + unsigned f = 0; +#define f_neg 1u + + /* Initial setup. We'll modify @a@ as we go, so we have to destroy it when + * we're done; therefore we should take a copy now. + */ + MP_COPY(a); + + /* According to the definition above, @0@ is %%\emph{not}%% a perfect + * power. Dispose of this special case early. + */ + if (MP_ZEROP(a)) goto done; + + /* Start by initializing a prime iterator. If %$a$% is negative, then it + * can't be an even power so start iterating at 3; otherwise we must start + * with 2. + */ + primeiter_create(&pi, MP_NEGP(a) ? MP_THREE : MP_TWO); + if (MP_NEGP(a)) { a = mp_neg(a, a); f |= f_neg; } + + /* Prime the pump for the main loop. */ + p = primeiter_next(&pi, p); + + /* Here we go. */ + for (;;) { + + /* See if %$a$% is a perfect %$p$%th power. */ + t = mp_nthrt(t, a, p, &exactp); + if (MP_EQ(t, MP_ONE)) + /* No, and moreover %$2^p > a$%. We won't get anywhere by examining + * larger primes, so stop here. + */ + break; + + else if (!exactp) + /* No. Oh, well: let's try the next prime, then. */ + + p = primeiter_next(&pi, p); + else { + /* Yes! We've found that %$a = a'^p$% for some %$a'$%. We know + * (induction) that %$a'$% is not a perfect %$q$%th power for any prime + * %$q < p$%, so we can write %$a = x^n$% if and only if we can write + * %$a' = x'^{n'}$%, in which case we have %$a = a'^p = x'^{p n'}$%. + */ + r = mp_mul(r, r, p); + MP_DROP(a); a = t; t = MP_NEW; + rc = 1; + } + } + + /* We're all done. */ + primeiter_destroy(&pi); + +done: + /* Return the resulting decomposition. */ + if (x) { mp_drop(*x); *x = f&f_neg ? mp_neg(a, a) : a; a = 0; } + if (n) { mp_drop(*n); *n = r; r = 0; } + + /* Clean everything up. */ + mp_drop(a); mp_drop(p); mp_drop(r); mp_drop(t); + + /* All done. */ + return (rc); + +#undef f_neg +} + +/*----- Test rig ----------------------------------------------------------*/ + +#ifdef TEST_RIG + +#include + +static int vrf_nthrt(dstr *v) +{ + mp *a = *(mp **)v[0].buf; + mp *n = *(mp **)v[1].buf; + mp *r0 = *(mp **)v[2].buf; + int exactp0 = *(int *)v[3].buf, exactp; + mp *r = mp_nthrt(MP_NEW, a, n, &exactp); + int ok = 1; + + if (!MP_EQ(r, r0) || exactp != exactp0) { + ok = 0; + fputs("\n*** nthrt failed", stderr); + fputs("\n*** a = ", stderr); mp_writefile(a, stderr, 10); + fputs("\n*** n = ", stderr); mp_writefile(n, stderr, 10); + fputs("\n*** r calc = ", stderr); mp_writefile(r, stderr, 10); + fprintf(stderr, "\n*** ex calc = %d", exactp); + fputs("\n*** r exp = ", stderr); mp_writefile(r0, stderr, 10); + fprintf(stderr, "\n*** ex exp = %d", exactp0); + fputc('\n', stderr); + } + + mp_drop(a); mp_drop(n); mp_drop(r); mp_drop(r0); + assert(mparena_count(MPARENA_GLOBAL) == 0); + + return (ok); +} + +static int vrf_ppp(dstr *v) +{ + mp *a = *(mp **)v[0].buf; + int ret0 = *(int *)v[1].buf; + mp *x0 = *(mp **)v[2].buf; + mp *n0 = *(mp **)v[3].buf; + mp *x = MP_NEW, *n = MP_NEW; + int ret = mp_perfect_power_p(&x, &n, a); + int ok = 1; + + if (ret != ret0 || !MP_EQ(x, x0) || !MP_EQ(n, n0)) { + ok = 0; + fputs("\n*** perfect_power_p failed", stderr); + fputs("\n*** a = ", stderr); mp_writefile(a, stderr, 10); + fprintf(stderr, "\n*** r calc = %d", ret); + fputs("\n*** x calc = ", stderr); mp_writefile(x, stderr, 10); + fputs("\n*** n calc = ", stderr); mp_writefile(n, stderr, 10); + fprintf(stderr, "\n*** r exp = %d", ret0); + fputs("\n*** x exp = ", stderr); mp_writefile(x0, stderr, 10); + fputs("\n*** n exp = ", stderr); mp_writefile(n0, stderr, 10); + fputc('\n', stderr); + } + + mp_drop(a); mp_drop(x); mp_drop(n); mp_drop(x0); mp_drop(n0); + assert(mparena_count(MPARENA_GLOBAL) == 0); + + return (ok); +} + +static test_chunk tests[] = { + { "nthrt", vrf_nthrt, { &type_mp, &type_mp, &type_mp, &type_int, 0 } }, + { "perfect-power-p", vrf_ppp, { &type_mp, &type_int, &type_mp, &type_mp, 0 } }, + { 0, 0, { 0 } }, +}; + +int main(int argc, char *argv[]) +{ + sub_init(); + test_run(argc, argv, tests, SRCDIR "/t/mp"); + return (0); +} + +#endif + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/math/mp.h b/math/mp.h index 0434c282..23e71e36 100644 --- a/math/mp.h +++ b/math/mp.h @@ -923,6 +923,54 @@ extern mp *mp_leastcongruent(mp */*d*/, mp */*b*/, mp */*r*/, mp */*m*/); extern mp *mp_sqrt(mp */*d*/, mp */*a*/); +/* --- @mp_nthrt@ --- * + * + * Arguments: @mp *d@ = fake destination + * @mp *a@ = an integer + * @mp *n@ = a strictly positive integer + * @int *exectp_out@ = set nonzero if an exact solution is found + * + * Returns: The integer %$\bigl\lfloor \sqrt[n]{a} \bigr\rfloor$%. + * + * Use: Return an approximation to the %$n$%th root of %$a$%. + * Specifically, it returns the largest integer %$x$% such that + * %$x^n \le a$%. If %$x^n = a$% then @*exactp_out@ is set + * nonzero; otherwise it is set zero. (If @exactp_out@ is null + * then this information is discarded.) + * + * The exponent %$n$% must be strictly positive: it's not clear + * to me what the right answer is for %$n \le 0$%. If %$a$% is + * negative then %$n$% must be odd; otherwise there is no real + * solution. + */ + +extern mp *mp_nthrt(mp */*d*/, mp */*a*/, mp */*n*/, int */*exactp_out*/); + +/* --- @mp_perfect_power_p@ --- * + * + * Arguments: @mp **x@ = where to write the base + * @mp **n@ = where to write the exponent + * @mp *a@ = an integer + * + * Returns: Nonzero if %$a$% is a perfect power. + * + * Use: Returns whether an integer %$a$% is a perfect power, i.e., + * whether it can be written in the form %$a = x^n$% where + * %$|x| > 1$% and %$n > 1$% are integers. If this is possible, + * then (a) store %$x$% and the largest such %$n$% in @*x@ and + * @*n@, and return nonzero; otherwise, store %$x = a$% and + * %$n = 1$% and return zero. (Either @x@ or @n@, or both, may + * be null to discard these outputs.) + * + * Note that %$-1$%, %$0$% and %$1$% are not considered perfect + * powers by this definition. (The exponent is not well-defined + * in these cases, but it seemed better to implement a function + * which worked for all integers.) Note also that %$-4$% is not + * a perfect power since it has no real square root. + */ + +extern int mp_perfect_power_p(mp **/*x*/, mp **/*n*/, mp */*a*/); + /* --- @mp_gcd@ --- * * * Arguments: @mp **gcd, **xx, **yy@ = where to write the results diff --git a/math/mpgen b/math/mpgen index 7d11bfda..c7dbf889 100644 --- a/math/mpgen +++ b/math/mpgen @@ -28,10 +28,9 @@ from __future__ import with_statement import re as RX import optparse as OP +import sys as SYS; stdout = SYS.stdout import types as TY -from sys import stdout - ###-------------------------------------------------------------------------- ### Random utilities. @@ -65,6 +64,22 @@ def fix_name(name): """Replace non-alphanumeric characters in NAME with underscores.""" return R_IDBAD.sub('_', name) +if SYS.version_info >= (3,): + def func_name(func): return func.__name__ + xrange = range + long = int +else: + def func_name(func): return func.func_name + +def with_metaclass(meta, *supers): + return meta('#' % meta.__name__, + supers or (object,), dict()) + +def load(file, mod): + with open(file) as f: text = f.read() + code = compile(text, file, 'exec') + exec(code, mod) + ###-------------------------------------------------------------------------- ### Determining the appropriate types. @@ -83,7 +98,7 @@ class IntClass (type): except AttributeError: pass return c -class BasicIntType (object): +class BasicIntType (with_metaclass(IntClass)): """ A base class for integer-type classes, providing defaults and protocol. @@ -195,7 +210,7 @@ class TypeChoice (object): ## Load the captured type information. me.ti = TY.ModuleType('typeinfo') - execfile(opts.typeinfo, me.ti.__dict__) + load(opts.typeinfo, me.ti.__dict__) ## Build a map of the available types. tymap = {} @@ -226,14 +241,14 @@ class TypeChoice (object): ## Make sure we've not ended up somewhere really silly. if mpwbits < 16: - raise Exception, "`mpw' type is too small: your C environment is weird" + raise Exception("`mpw' type is too small: your C environment is weird") ## Now figure out suitable types for `mpw' and `mpd'. def find_type(bits, what): for ty in byrank: if ty.bits >= bits: return ty - raise Exception, \ - "failed to find suitable %d-bit type, for %s" % (bits, what) + raise Exception \ + ("failed to find suitable %d-bit type, for %s" % (bits, what)) ## Store our decisions. me.mpwbits = mpwbits @@ -327,7 +342,7 @@ def defmode(func): arguments from the command and are expected to write their output to stdout. """ - name = func.func_name + name = func_name(func) if name.startswith('m_'): name = name[2:] MODEMAP[name] = func return func @@ -483,7 +498,7 @@ class GroupTableClass (type): else: MODEMAP[c.mode] = c.run return c -class GroupTable (object): +class GroupTable (with_metaclass(GroupTableClass)): """ Base class for group tables objects. @@ -568,8 +583,6 @@ class GroupTable (object): ## _slotmap A dictionary mapping slot names to their ## descriptions. - __metaclass__ = GroupTableClass - ## Default values. keyword = 'group' slots = [] @@ -644,7 +657,7 @@ class GroupTable (object): if ff[0] == 'alias': ## An alias. Just remember this. - if len(ff) != 3: raise Exception, "wrong number of alias arguments" + if len(ff) != 3: raise Exception("wrong number of alias arguments") me._flush() me._names.append((ff[1], ff[2])) @@ -654,7 +667,7 @@ class GroupTable (object): ## Check the headline syntax. Headline slots may be set here, or ## later by name. if len(ff) < 2 or len(ff) > 2 + len(me._headslots): - raise Exception, "bad number of headline arguments" + raise Exception("bad number of headline arguments") ## Flush out the previous stanza. me._flush() @@ -670,14 +683,14 @@ class GroupTable (object): elif ff[0] in me._slotmap: ## A slot assignment. Get the slot to store a value. if me.st.name is None: - raise Exception, "no group currently being defined" + raise Exception("no group currently being defined") if len(ff) != 2: - raise Exception, "bad number of values for slot `%s'" % ff[0] + raise Exception("bad number of values for slot `%s'" % ff[0]) me._slotmap[ff[0]].set(me.st, ff[1]) else: ## Something incomprehensible. - raise Exception, "unknown keyword `%s'" % ff[0] + raise Exception("unknown keyword `%s'" % ff[0]) ## End of the input. Write out the final stanza. me._flush() @@ -687,7 +700,7 @@ class GroupTable (object): stdout.write("\nconst %s %s[] = {\n" % (me.entry_t, me.tabname)) for a, n in me._names: if n not in me._defs: - raise Exception, "alias `%s' refers to unknown group `%s'" % (a, n) + raise Exception("alias `%s' refers to unknown group `%s'" % (a, n)) stdout.write(' { "%s", &c_%s },\n' % (a, fix_name(n))) stdout.write(" { 0, 0 }\n};\n\n") @@ -747,7 +760,7 @@ class BaseSlot (object): Store a VALUE for the slot. """ if me._allowp and not me._allowp(st, value): - raise Exception, "slot `%s' not allowed here" % me.name + raise Exception("slot `%s' not allowed here" % me.name) st.d[me] = value def setup(me, st): @@ -755,7 +768,7 @@ class BaseSlot (object): Prepare the slot for output, checking its value and so on. """ if me not in st.d and (not me._omitp or not me._omitp(st)): - raise Exception, "missing slot `%s'" % me.name + raise Exception("missing slot `%s'" % me.name) class EnumSlot (BaseSlot): """ @@ -781,7 +794,7 @@ class EnumSlot (BaseSlot): Check that the VALUE is one of the ones we know. """ if value not in me._values: - raise Exception, "invalid %s value `%s'" % (me.name, value) + raise Exception("invalid %s value `%s'" % (me.name, value)) super(EnumSlot, me).set(st, value) def write(me, st): diff --git a/math/mpint.c b/math/mpint.c index 3511f1ee..971edabb 100644 --- a/math/mpint.c +++ b/math/mpint.c @@ -57,6 +57,7 @@ MPINT_CONVERSIONS(TO) #ifdef TEST_RIG +#include #include static int fromuint(dstr *v) diff --git a/math/mpmont-exp.c b/math/mpmont-exp.c index 6b5c8c43..ff15b2dc 100644 --- a/math/mpmont-exp.c +++ b/math/mpmont-exp.c @@ -93,6 +93,10 @@ mp *mpmont_exp(const mpmont *mm, mp *d, mp *a, mp *e) #ifdef TEST_RIG +#ifdef ENABLE_ASM_DEBUG +# include "regdump.h" +#endif + static int texp(dstr *v) { mp *m = *(mp **)v[0].buf; @@ -136,6 +140,9 @@ static test_chunk tests[] = { int main(int argc, char *argv[]) { sub_init(); +#ifdef ENABLE_ASM_DEBUG + regdump_init(); +#endif test_run(argc, argv, tests, SRCDIR "/t/mpmont"); return (0); } diff --git a/math/mpmont.c b/math/mpmont.c index 094ac401..b4bf982e 100644 --- a/math/mpmont.c +++ b/math/mpmont.c @@ -98,6 +98,14 @@ static void simple_redccore(mpw *dv, mpw *dvl, const mpw *mv, MAYBE_REDC4(amd64_avx) #endif +#if CPUFAM_ARMEL + MAYBE_REDC4(arm_neon) +#endif + +#if CPUFAM_ARM64 + MAYBE_REDC4(arm64_simd) +#endif + static redccore__functype *pick_redccore(void) { #if CPUFAM_X86 @@ -112,10 +120,18 @@ static redccore__functype *pick_redccore(void) DISPATCH_PICK_COND(mpmont_reduce, maybe_redc4_amd64_sse2, cpu_feature_p(CPUFEAT_X86_SSE2)); #endif +#if CPUFAM_ARMEL + DISPATCH_PICK_COND(mpmont_reduce, maybe_redc4_arm_neon, + cpu_feature_p(CPUFEAT_ARM_NEON)); +#endif +#if CPUFAM_ARM64 + DISPATCH_PICK_COND(mpmont_reduce, maybe_redc4_arm64_simd, + cpu_feature_p(CPUFEAT_ARM_NEON)); +#endif DISPATCH_PICK_FALLBACK(mpmont_reduce, simple_redccore); } -/* --- @redccore@ --- * +/* --- @mulcore@ --- * * * Arguments: @mpw *dv, *dvl@ = base and limit of source/destination * @const mpw *av, *avl@ = base and limit of first multiplicand @@ -204,6 +220,14 @@ static void simple_mulcore(mpw *dv, mpw *dvl, MAYBE_MUL4(amd64_avx) #endif +#if CPUFAM_ARMEL + MAYBE_MUL4(arm_neon) +#endif + +#if CPUFAM_ARM64 + MAYBE_MUL4(arm64_simd) +#endif + static mulcore__functype *pick_mulcore(void) { #if CPUFAM_X86 @@ -218,6 +242,14 @@ static mulcore__functype *pick_mulcore(void) DISPATCH_PICK_COND(mpmont_mul, maybe_mul4_amd64_sse2, cpu_feature_p(CPUFEAT_X86_SSE2)); #endif +#if CPUFAM_ARMEL + DISPATCH_PICK_COND(mpmont_mul, maybe_mul4_arm_neon, + cpu_feature_p(CPUFEAT_ARM_NEON)); +#endif +#if CPUFAM_ARM64 + DISPATCH_PICK_COND(mpmont_mul, maybe_mul4_arm64_simd, + cpu_feature_p(CPUFEAT_ARM_NEON)); +#endif DISPATCH_PICK_FALLBACK(mpmont_mul, simple_mulcore); } @@ -444,6 +476,10 @@ mp *mpmont_mul(const mpmont *mm, mp *d, mp *a, mp *b) #ifdef TEST_RIG +#ifdef ENABLE_ASM_DEBUG +# include "regdump.h" +#endif + static int tcreate(dstr *v) { mp *m = *(mp **)v[0].buf; @@ -539,7 +575,6 @@ static int tmul(dstr *v) mp_drop(mr); } - MP_DROP(m); MP_DROP(a); MP_DROP(b); @@ -558,6 +593,9 @@ static test_chunk tests[] = { int main(int argc, char *argv[]) { sub_init(); +#ifdef ENABLE_ASM_DEBUG + regdump_init(); +#endif test_run(argc, argv, tests, SRCDIR "/t/mpmont"); return (0); } diff --git a/math/mptext.c b/math/mptext.c index c902264d..a5c77db0 100644 --- a/math/mptext.c +++ b/math/mptext.c @@ -31,6 +31,8 @@ #include #include +#include + #include "mp.h" #include "mptext.h" #include "paranoia.h" @@ -340,10 +342,10 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p) /* --- If we're reading text, skip leading space, and maybe a sign --- */ if (radix >= 0) { - while (isspace(ch)) ch = ops->get(p); + while (ISSPACE(ch)) ch = ops->get(p); switch (ch) { case '-': f |= f_neg; /* and on */ - case '+': do ch = ops->get(p); while (isspace(ch)); + case '+': do ch = ops->get(p); while (ISSPACE(ch)); } } @@ -705,7 +707,7 @@ static int verify(dstr *v) ok = 0; } else { mp_writedstr(m, &d, ob); - if (d.len != v[3].len || memcmp(d.buf, v[3].buf, d.len) != 0) { + if (d.len != v[3].len || MEMCMP(d.buf, !=, v[3].buf, d.len)) { fprintf(stderr, "*** failed read or write\n" "*** input [%2i] = ", ib); if (ib < 0) @@ -746,7 +748,7 @@ static int verify(dstr *v) } if (v[1].len - off != v[4].len || - memcmp(v[1].buf + off, v[4].buf, v[4].len) != 0) { + MEMCMP(v[1].buf + off, !=, v[4].buf, v[4].len)) { fprintf(stderr, "*** leftovers incorrect\n" "*** input [%2i] = ", ib); if (ib < 0) diff --git a/math/mpx-mul4-amd64-sse2.S b/math/mpx-mul4-amd64-sse2.S index d313765f..5a748c60 100644 --- a/math/mpx-mul4-amd64-sse2.S +++ b/math/mpx-mul4-amd64-sse2.S @@ -41,11 +41,11 @@ /// construct more general variable-length multipliers. /// /// The basic trick is the same throughout. In an operand-scanning -/// multiplication, the inner multiplication loop multiplies a -/// multiple-precision operand by a single precision factor, and adds the -/// result, appropriately shifted, to the result. A `finely integrated -/// operand scanning' implementation of Montgomery multiplication also adds -/// the product of a single-precision `Montgomery factor' and the modulus, +/// multiplication, the inner multiplication loop multiplies a multiple- +/// precision operand by a single precision factor, and adds the result, +/// appropriately shifted, to the result. A `finely integrated operand +/// scanning' implementation of Montgomery multiplication also adds the +/// product of a single-precision `Montgomery factor' and the modulus, /// calculated in the same pass. The more common `coarsely integrated /// operand scanning' alternates main multiplication and Montgomery passes, /// which requires additional carry propagation. @@ -71,22 +71,65 @@ /// many products together before we must deal with carrying; it also allows /// for some calculations to be performed on the above expanded form. /// -/// ... +/// We maintain four `carry' registers XMM12--XMM15 accumulating intermediate +/// results. The registers' precise roles rotate during the computation; we +/// name them `c0', `c1', `c2', and `c3'. Each carry register holds two +/// 64-bit halves: the register c0, for example, holds c'_0 (low half) and +/// c''_0 (high half), and represents the value c_0 = c'_0 + c''_0 b; the +/// carry registers collectively represent the value c_0 + c_1 B + c_2 B^2 + +/// c_3 B^3. The `pmuluqdq' instruction acting on a scalar operand +/// (broadcast across all lanes of its vector) and an operand in the expanded +/// form above produces a result which can be added directly to the +/// appropriate carry register. Following a pass of four multiplications, we +/// perform some limited carry propagation: let t = c''_0 mod B, and let d = +/// c'_0 + t b; then we output z = d mod B, add (floor(d/B), floor(c''_0/B)) +/// to c1, and cycle the carry registers around, so that c1 becomes c0, and +/// the old (implicitly) zeroed c0 becomes c3. /// -/// We maintain four `carry' registers accumulating intermediate results. -/// The registers' precise roles rotate during the computation; we name them -/// `c0', `c1', `c2', and `c3'. Each carry register holds two 64-bit halves: -/// the register c0, for example, holds c'_0 (low half) and c''_0 (high -/// half), and represents the value c_0 = c'_0 + c''_0 b; the carry registers -/// collectively represent the value c_0 + c_1 B + c_2 B^2 + c_3 B^3. The -/// `pmuluqdq' instruction acting on a scalar operand (broadcast across all -/// lanes of its vector) and an operand in the expanded form above produces a -/// result which can be added directly to the appropriate carry register. -/// Following a pass of four multiplications, we perform some limited carry -/// propagation: let t = c''_0 mod B, and let d = c'_0 + t b; then we output -/// z = d mod B, add (floor(d/B), floor(c''_0/B)) to c1, and cycle the carry -/// registers around, so that c1 becomes c0, and the old c0 is (implicitly) -/// zeroed becomes c3. +/// On 64-bit AMD64, we have a reasonable number of registers: the expanded +/// operands are kept in registers. The packed operands are read from memory +/// into working registers XMM4 and XMM5; XMM0--XMM3 are used for the actual +/// multiplications; and XMM6 and XMM7 are used for combining the results. +/// The following conventional argument names and locations are used +/// throughout. +/// +/// Arg Format Location Notes +/// +/// U packed [RAX] +/// X packed [RBX] In Montgomery multiplication, X = N +/// V expanded XMM8/XMM9 +/// Y expanded XMM10/XMM11 In Montgomery multiplication, Y = (A + U V) M +/// M expanded (see below) Montgomery factor, M = -N^{-1} (mod B^4) +/// N Modulus, for Montgomery multiplication +/// A packed [RDI] Destination/accumulator +/// C carry XMM12--XMM15 +/// +/// The calculation is some variant of +/// +/// A' + C' B^4 <- U V + X Y + A + C +/// +/// The low-level functions fit into a fairly traditional (finely-integrated) +/// operand scanning loop over operand pairs (U, X) (indexed by j) and (V, Y) +/// (indexed by i). +/// +/// The variants are as follows. +/// +/// Function Variant Use i j +/// +/// mmul4 A = C = 0, Y = M Montgomery 0 0 +/// dmul4 A = 0 Montgomery 0 + +/// mmla4 C = 0, Y = M Montgomery + 0 +/// dmla4 exactly as shown Montgomery + + +/// mont4 U = C = 0, V = M Montgomery any 0 +/// +/// mul4zc U = V = A = C = 0 Plain 0 0 +/// mul4 U = V = A = 0 Plain 0 + +/// mla4zc U = V = C = 0 Plain + 0 +/// mla4 U = V = 0 Plain + + +/// +/// The `mmul4' and `mmla4' functions are also responsible for calculating +/// the Montgomery reduction factor Y = (A + U V) M used by the rest of the +/// inner loop. ///-------------------------------------------------------------------------- /// Macro definitions. @@ -319,7 +362,6 @@ INTFUNC(carryprop) movdqu [rdi], xmm0 ret - ENDFUNC INTFUNC(dmul4) @@ -357,7 +399,6 @@ INTFUNC(dmul4) movdqu [rdi], xmm6 ret - ENDFUNC INTFUNC(dmla4) @@ -398,7 +439,6 @@ INTFUNC(dmla4) movdqu [rdi], xmm6 ret - ENDFUNC INTFUNC(mul4zc) @@ -429,7 +469,6 @@ INTFUNC(mul4zc) movdqu [rdi], xmm6 ret - ENDFUNC INTFUNC(mul4) @@ -462,7 +501,6 @@ INTFUNC(mul4) movdqu [rdi], xmm6 ret - ENDFUNC INTFUNC(mla4zc) @@ -498,7 +536,6 @@ INTFUNC(mla4zc) movdqu [rdi], xmm6 ret - ENDFUNC INTFUNC(mla4) @@ -533,14 +570,13 @@ INTFUNC(mla4) movdqu [rdi], xmm6 ret - ENDFUNC INTFUNC(mmul4) // On entry, RDI points to the destination buffer; RAX and RBX point // to the packed operands U and N; and XMM8/XMM9 and XMM10/XMM11 hold - // the expanded operands V and M. The stack pointer must be 8 modulo 16 - // (as usual for AMD64 ABIs). + // the expanded operands V and M. The stack pointer must be 8 modulo + // 16 (as usual for AMD64 ABIs). // // On exit, we store Y = U V M mod B in XMM10/XMM11, and write the // low 128 bits of the sum U V + N Y to [RDI], leaving the remaining @@ -557,7 +593,6 @@ INTFUNC(mmul4) mulcore xmm4, 0, xmm8, xmm9, xmm12, xmm13, xmm14, xmm15 propout xmm7, lo, xmm12, xmm13 jmp 5f - ENDFUNC INTFUNC(mmla4) @@ -575,10 +610,10 @@ INTFUNC(mmla4) movdqu xmm4, [rax] #if ABI_WIN stalloc 48 + 8 // space for the carries -# define STKTMP(i) [rsp + i] +# define STKTMP(i) [SP + i] #endif #if ABI_SYSV -# define STKTMP(i) [rsp + i - 48 - 8] // use red zone +# define STKTMP(i) [SP + i - 48 - 8] // use red zone #endif endprologue @@ -744,7 +779,6 @@ INTFUNC(mont4) // And, with that, we're done. movdqu [rdi], xmm6 ret - ENDFUNC ///-------------------------------------------------------------------------- @@ -759,7 +793,7 @@ ENDFUNC FUNC(mpx_umul4_amd64_sse2) // void mpx_umul4_amd64_sse2(mpw *dv, const mpw *av, const mpw *avl, - // const mpw *bv, const mpw *bvl); + // const mpw *bv, const mpw *bvl); // Establish the arguments and do initial setup. // @@ -783,7 +817,6 @@ FUNC(mpx_umul4_amd64_sse2) endprologue mov DV, rdi - #endif #if ABI_WIN @@ -811,8 +844,7 @@ FUNC(mpx_umul4_amd64_sse2) endprologue mov rdi, DV - mov BVL, [rsp + 224] - + mov BVL, [SP + 224] #endif // Prepare for the first iteration. @@ -878,7 +910,6 @@ FUNC(mpx_umul4_amd64_sse2) #endif #if ABI_WIN - rstrxmm xmm6, 0 rstrxmm xmm7, 16 rstrxmm xmm8, 32 @@ -893,7 +924,6 @@ FUNC(mpx_umul4_amd64_sse2) stfree 160 + 8 popreg rdi popreg rbx - #endif ret @@ -946,7 +976,6 @@ FUNC(mpxmont_mul4_amd64_sse2) endprologue mov DV, rdi - #endif #if ABI_WIN @@ -978,9 +1007,8 @@ FUNC(mpxmont_mul4_amd64_sse2) endprologue mov rdi, DV - mov N, [rsp + 224] - mov MI, [rsp + 232] - + mov N, [SP + 224] + mov MI, [SP + 232] #endif // Establish the expanded operands. @@ -1062,7 +1090,6 @@ FUNC(mpxmont_mul4_amd64_sse2) #endif #if ABI_WIN - rstrxmm xmm6, 0 rstrxmm xmm7, 16 rstrxmm xmm8, 32 @@ -1078,7 +1105,6 @@ FUNC(mpxmont_mul4_amd64_sse2) popreg r12 popreg rdi popreg rbx - #endif ret @@ -1129,12 +1155,11 @@ FUNC(mpxmont_redc4_amd64_sse2) // outer loop dv r10 rcx // outer loop dv limit r11 r11 // nv base rdx r8 - // nv limit r9 r12* + // nv limit r9 r10* // n rcx r9 // c rcx r9 #if ABI_SYSV - # define DVL rax # define DVL4 rsi # define MI r8 @@ -1149,25 +1174,22 @@ FUNC(mpxmont_redc4_amd64_sse2) endprologue mov DV, rdi - #endif #if ABI_WIN - # define DVL rax # define DVL4 rdx # define MI r10 # define DV rcx # define DVLO r11 # define NV r8 -# define NVL r12 +# define NVL r10 # define N r9 # define C r9d pushreg rbx pushreg rdi - pushreg r12 - stalloc 160 + stalloc 168 savexmm xmm6, 0 savexmm xmm7, 16 @@ -1183,8 +1205,7 @@ FUNC(mpxmont_redc4_amd64_sse2) endprologue mov rdi, DV - mov MI, [rsp + 224] - + mov MI, [SP + 224] #endif // Establish the expanded operands and the blocks-of-4 dv limit. @@ -1230,29 +1251,29 @@ FUNC(mpxmont_redc4_amd64_sse2) // Continue carry propagation until the end of the buffer. 0: add [rdi], C mov C, 0 // preserves flags - adcd [rdi + 4], 0 - adcd [rdi + 8], 0 - adcd [rdi + 12], 0 + adc dword ptr [rdi + 4], 0 + adc dword ptr [rdi + 8], 0 + adc dword ptr [rdi + 12], 0 adc C, 0 add rdi, 16 cmp rdi, DVL4 jb 0b - // Deal with the tail end. + // Deal with the tail end. Note that the actual destination length + // won't be an exacty number of blocks of four, so it's safe to just + // drop through here. 7: add [rdi], C - mov C, 0 // preserves flags + mov C, 0 add rdi, 4 adc C, 0 cmp rdi, DVL jb 7b - // All done for this iteration. Start the next. (This must have at - // least one follow-on iteration, or we'd not have started this outer - // loop.) -8: mov rdi, DV // -> Z = dv[i] - mov rbx, NV // -> X = nv[0] - cmp rdi, DVLO // all done yet? + // All done for this iteration. Start the next. + cmp DV, DVLO // all done yet? jae 9f + mov rdi, DV // -> Z = dv[i] + mov rbx, NV // -> X = nv[0] add DV, 16 call mont4 add rdi, 16 @@ -1267,7 +1288,6 @@ FUNC(mpxmont_redc4_amd64_sse2) #endif #if ABI_WIN - rstrxmm xmm6, 0 rstrxmm xmm7, 16 rstrxmm xmm8, 32 @@ -1279,11 +1299,9 @@ FUNC(mpxmont_redc4_amd64_sse2) rstrxmm xmm14, 128 rstrxmm xmm15, 144 - stfree 160 - popreg r12 + stfree 168 popreg rdi popreg rbx - #endif ret @@ -1329,7 +1347,7 @@ ENDFUNC # define ARG8 STKARG(4) # define STKARG_OFFSET 224 #endif -#define STKARG(i) [rsp + STKARG_OFFSET + 8*(i)] +#define STKARG(i) [SP + STKARG_OFFSET + 8*(i)] // sysv win // dmul smul mmul mont dmul smul mmul mont @@ -1583,6 +1601,8 @@ FUNC(test_mmul4) testtop r11 call mmul4 testtail + pshufd xmm10, xmm10, SHUF(0, 2, 1, 3) + pshufd xmm11, xmm11, SHUF(0, 2, 1, 3) movdqu [r10 + 0], xmm10 movdqu [r10 + 16], xmm11 testcarryout @@ -1594,6 +1614,8 @@ FUNC(test_mmla4) testtop r11 call mmla4 testtail + pshufd xmm10, xmm10, SHUF(0, 2, 1, 3) + pshufd xmm11, xmm11, SHUF(0, 2, 1, 3) movdqu [r10 + 0], xmm10 movdqu [r10 + 16], xmm11 testcarryout @@ -1605,6 +1627,8 @@ FUNC(test_mont4) testtop call mont4 testtail + pshufd xmm10, xmm10, SHUF(0, 2, 1, 3) + pshufd xmm11, xmm11, SHUF(0, 2, 1, 3) movdqu [r10 + 0], xmm10 movdqu [r10 + 16], xmm11 testcarryout diff --git a/math/mpx-mul4-arm-neon.S b/math/mpx-mul4-arm-neon.S new file mode 100644 index 00000000..efca7902 --- /dev/null +++ b/math/mpx-mul4-arm-neon.S @@ -0,0 +1,1189 @@ +/// -*- mode: asm; asm-comment-char: ?/ -*- +/// +/// Large SIMD-based multiplications +/// +/// (c) 2019 Straylight/Edgeware +/// + +///----- Licensing notice --------------------------------------------------- +/// +/// This file is part of Catacomb. +/// +/// Catacomb is free software: you can redistribute it and/or modify it +/// under the terms of the GNU Library General Public License as published +/// by the Free Software Foundation; either version 2 of the License, or +/// (at your option) any later version. +/// +/// Catacomb is distributed in the hope that it will be useful, but +/// WITHOUT ANY WARRANTY; without even the implied warranty of +/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +/// Library General Public License for more details. +/// +/// You should have received a copy of the GNU Library General Public +/// License along with Catacomb. If not, write to the Free Software +/// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +/// USA. + +///-------------------------------------------------------------------------- +/// Preliminaries. + +#include "config.h" +#include "asm-common.h" + + .arch armv7-a + .fpu neon + + .text + +///-------------------------------------------------------------------------- +/// Theory. +/// +/// We define a number of primitive fixed-size multipliers from which we can +/// construct more general variable-length multipliers. +/// +/// The basic trick is the same throughout. In an operand-scanning +/// multiplication, the inner multiplication loop multiplies a multiple- +/// precision operand by a single precision factor, and adds the result, +/// appropriately shifted, to the result. A `finely integrated operand +/// scanning' implementation of Montgomery multiplication also adds the +/// product of a single-precision `Montgomery factor' and the modulus, +/// calculated in the same pass. The more common `coarsely integrated +/// operand scanning' alternates main multiplication and Montgomery passes, +/// which requires additional carry propagation. +/// +/// Throughout both plain-multiplication and Montgomery stages, then, one of +/// the factors remains constant throughout the operation, so we can afford +/// to take a little time to preprocess it. The transformation we perform is +/// as follows. Let b = 2^16, and B = b^2 = 2^32. Suppose we're given a +/// 128-bit factor v = v_0 + v_1 B + v_2 B^2 + v_3 B^3. Split each v_i into +/// two sixteen-bit pieces, so v_i = v'_i + v''_i b. These eight 16-bit +/// pieces are placed into 32-bit cells, and arranged as two 128-bit NEON +/// operands, as follows. +/// +/// Offset 0 4 8 12 +/// 0 v'_0 v''_0 v'_1 v''_1 +/// 16 v'_2 v''_2 v'_3 v''_3 +/// +/// The `vmull' and `vmlal' instructions can multiply a vector of two 32-bit +/// values by a 32-bit scalar, giving two 64-bit results; thus, it will act +/// on (say) v'_0 and v''_0 in a single instruction, to produce two 48-bit +/// results in 64-bit fields. The sixteen bits of headroom allows us to add +/// many products together before we must deal with carrying; it also allows +/// for some calculations to be performed on the above expanded form. +/// +/// We maintain three `carry' registers, q12--q14, accumulating intermediate +/// results; we name them `c0', `c1', and `c2'. Each carry register holds +/// two 64-bit halves: the register c0, for example, holds c'_0 (low half) +/// and c''_0 (high half), and represents the value c_0 = c'_0 + c''_0 b; the +/// carry registers collectively represent the value c_0 + c_1 B + c_2 B^2. +/// The `vmull' or `vmlal' instruction acting on a scalar operand and an +/// operand in the expanded form above produces a result which can be added +/// directly to the appropriate carry register. +/// +/// Multiplication is performed in product-scanning order, since ARM +/// processors commonly implement result forwarding for consecutive multiply- +/// and-accumulate instructions specifying the same destination. +/// Experimentally, this runs faster than operand-scanning in an attempt to +/// hide instruction latencies. +/// +/// On 32-bit ARM, we have a reasonable number of registers: the expanded +/// operands are kept in registers. The packed operands are read from memory +/// into working registers q0 and q1. The following conventional argument +/// names and locations are used throughout. +/// +/// Arg Format Location Notes +/// +/// U packed [r1] +/// X packed [r2] In Montgomery multiplication, X = N +/// V expanded q2/q3 +/// Y expanded q4/q5 In Montgomery multiplication, Y = (A + U V) M +/// M expanded q4/q5 -N^{-1} (mod B^4) +/// N Modulus, for Montgomery multiplication +/// A packed [r0] Destination/accumulator +/// C carry q13--q15 +/// +/// The calculation is some variant of +/// +/// A' + C' B^4 <- U V + X Y + A + C +/// +/// The low-level functions fit into a fairly traditional (finely-integrated) +/// operand scanning loop over operand pairs (U, X) (indexed by j) and (V, Y) +/// (indexed by i). +/// +/// The variants are as follows. +/// +/// Function Variant Use i j +/// +/// mmul4 A = C = 0 Montgomery 0 0 +/// dmul4 A = 0 Montgomery 0 + +/// mmla4 C = 0 Montgomery + 0 +/// dmla4 exactly as shown Montgomery + + +/// +/// mul4zc U = V = A = C = 0 Plain 0 0 +/// mul4 U = V = A = 0 Plain 0 + +/// mla4zc U = V = C = 0 Plain + 0 +/// mla4 U = V = 0 Plain + + +/// +/// The `mmul4' and `mmla4' functions are also responsible for calculating +/// the Montgomery reduction factor Y = (A + U V) M used by the rest of the +/// inner loop. + +///-------------------------------------------------------------------------- +/// Macro definitions. + +.macro mulacc z, u, v, x=nil, y=nil + // Set Z = Z + U V + X Y. X may be `nil' to omit the second + // operand. Z should be a 128-bit `qN' register; V and Y should be + // 64-bit `dN' registers; and U and X should be 32-bit `dN[I]' + // scalars; the multiplications produce two 64-bit elementwise + // products, which are added elementwise to Z. + + vmlal.u32 \z, \v, \u + .ifnes "\x", "nil" + vmlal.u32 \z, \y, \x + .endif +.endm + +.macro mulinit z, zinitp, u, v, x, y + // If ZINITP then set Z = Z + U V + X Y, as for `mulacc'; otherwise, + // set Z = U V + X Y. Operand requirements and detailed operation + // are as for `mulacc'. + + .ifeqs "\zinitp", "t" + mulacc \z, \u, \v, \x, \y + .else + vmull.u32 \z, \v, \u + .ifnes "\x", "nil" + vmlal.u32 \z, \y, \x + .endif + .endif +.endm + +// `MULI': accumulate the B^I and b B^i terms of the polynomial product sum U +// V + X Y, given that U = u_0 + B u_1 + B^2 u_2 + B^3 u_3 (and similarly for +// x), and V = v'_0 + b v''_0 + B (v'_1 + b v''_1) + B^2 (v'_2 + b v''_2) + +// B^3 (v'_3 + b v''_3) (and similarly for Y). The 64-bit coefficients are +// added into the low and high halves of the 128-bit register Z (if ZINIT is +// `nil' then simply set Z, as if it were initially zero). +#define MUL0(z, zinitp, u, v0, v1, x, y0, y1) \ + mulinit z, zinitp, QW(u, 0), D0(v0), QW(x, 0), D0(y0) +#define MUL1(z, zinitp, u, v0, v1, x, y0, y1) \ + mulinit z, zinitp, QW(u, 0), D1(v0), QW(x, 0), D1(y0); \ + mulacc z, QW(u, 1), D0(v0), QW(x, 1), D0(y0) +#define MUL2(z, zinitp, u, v0, v1, x, y0, y1) \ + mulinit z, zinitp, QW(u, 0), D0(v1), QW(x, 0), D0(y1); \ + mulacc z, QW(u, 1), D1(v0), QW(x, 1), D1(y0); \ + mulacc z, QW(u, 2), D0(v0), QW(x, 2), D0(y0) +#define MUL3(z, zinitp, u, v0, v1, x, y0, y1) \ + mulinit z, zinitp, QW(u, 0), D1(v1), QW(x, 0), D1(y1); \ + mulacc z, QW(u, 1), D0(v1), QW(x, 1), D0(y1); \ + mulacc z, QW(u, 2), D1(v0), QW(x, 2), D1(y0); \ + mulacc z, QW(u, 3), D0(v0), QW(x, 3), D0(y0) +#define MUL4(z, zinitp, u, v0, v1, x, y0, y1) \ + mulinit z, zinitp, QW(u, 1), D1(v1), QW(x, 1), D1(y1); \ + mulacc z, QW(u, 2), D0(v1), QW(x, 2), D0(y1); \ + mulacc z, QW(u, 3), D1(v0), QW(x, 3), D1(y0) +#define MUL5(z, zinitp, u, v0, v1, x, y0, y1) \ + mulinit z, zinitp, QW(u, 2), D1(v1), QW(x, 2), D1(y1); \ + mulacc z, QW(u, 3), D0(v1), QW(x, 3), D0(y1) +#define MUL6(z, zinitp, u, v0, v1, x, y0, y1) \ + mulinit z, zinitp, QW(u, 3), D1(v1), QW(x, 3), D1(y1) + +// Steps in the process of propagating carry bits from ZLO to ZHI (both +// 128-bit `qN' registers). Here, T is a 128-bit `qN' temporary register. +// Set the low 32 bits of the 64-bit `dN' register ZOUT to the completed +// coefficient z_i. +// +// In detail, what happens is as follows. Suppose initially that ZLO = +// (z'_i; z''_i) and ZHI = (z'_{i+1}; z''_{i+1}). Let t = z'_i + b z''_i; +// observe that floor(t/b) = floor(z'_i/b) + z''_i. Let z_i = t mod B, and +// add floor(t/B) = floor((floor(z'_i/b) + z''_i)/b) onto z'_{i+1}. This has +// a circuit depth of 4; I don't know how to do better. +.macro _carry0 zout, zlo0, zlo1, t0, t1 + // ZLO0 and ZLO1 are the low and high halves of a carry register. + // Extract a 32-bit output, in the bottom 32 bits of ZOUT, and set T1 + // so as to continue processing using `_carry1'. All operands are + // 64-bit `dN' registers. If ZOUT is `nil' then no output is + // produced; if T1 is `nil' then no further processing will be + // possible. + .ifnes "\zout", "nil" + vshl.i64 \t0, \zlo1, #16 + .endif + .ifnes "\t1", "nil" + vshr.u64 \t1, \zlo0, #16 + .endif + .ifnes "\zout", "nil" + vadd.i64 \zout, \zlo0, \t0 + .endif + .ifnes "\t1", "nil" + vadd.i64 \t1, \t1, \zlo1 + .endif +.endm +.macro _carry1 u, zhi0, t1 + // ZHI0 is the low half of a carry register, and T1 is the result of + // applying `_carry0' to the previous carry register. Set U to the + // result of propagating the carry into ZHI0. + vshr.u64 \t1, \t1, #16 + vadd.i64 \u, \zhi0, \t1 +.endm + +// More convenient wrappers for `_carry0' and `_carry1'. +// +// CARRY0(ZOUT, ZLO, T) +// Store a 32-bit output in ZOUT from carry ZLO, using T as a +// temporary. ZOUT is a 64-bit `dN' register; ZLO and T are 128-bit +// `qN' registers. +// +// CARRY1(ZHI, T) +// Propagate carry from T into ZHI. Both are 128-bit `qN' registers; +// ZHI is updated. +#define CARRY0(zout, zlo, t) \ + CASIDE0(zout, D0(zlo), zlo, t) +#define CARRY1(zhi, t) \ + CASIDE1(D0(zhi), zhi, t) + +// Versions of `CARRY0' and `CARRY1' which don't mutate their operands. +// +// CASIDE0(ZOUT, U, ZLO, T) +// As for `CARRY0', but the low half of ZLO is actually in U (a 64-bit +// `dN' register). +// +// CASIDE0E(ZOUT, U, ZLO, T) +// As for `CASIDE0', but only calculate the output word, and no +// carry-out. +// +// CASIDE1(U, ZHI, T) +// As for `CARRY1', but write the updated low half of ZHI to U. +#define CASIDE0(zout, u, zlo, t) \ + _carry0 zout, u, D1(zlo), D0(t), D1(t) +#define CASIDE0E(zout, u, zlo, t) \ + _carry0 zout, u, D1(zlo), D0(t), nil +#define CASIDE1(u, zhi, t) \ + _carry1 u, D0(zhi), D1(t) + +// Steps in spreading a packed 128-bit operand in A0 across A0, A1, A2, A3 in +// carry format. +#define SPREADACC0(a0, a1, a2, a3) \ + vmov.i32 a1, #0; \ + vmov.i32 a2, #0; \ + vmov.i32 a3, #0 +#define SPREADACC1(a0, a1, a2, a3) \ + vswp D1(a0), D0(a2); \ + vtrn.32 D0(a0), D0(a1); \ + vtrn.32 D0(a2), D0(a3) + +// Add the carry-format values A0, A1, A2 into the existing carries C0, C1, +// C2 (leaving A3 where it is). +#define CARRYACC(a0, a1, a2, a3, c0, c1, c2) \ + vadd.i64 c0, c0, a0; \ + vadd.i64 c1, c1, a1; \ + vadd.i64 c2, c2, a2 + +///-------------------------------------------------------------------------- +/// Primitive multipliers and related utilities. + +INTFUNC(carryprop) + // On entry, r0 points to a destination, and q13--q15 hold incoming + // carries c0--c2. On exit, the low 128 bits of the carry value are + // stored at [r0]; the remaining 16 bits of carry are left in d30; r0 + // is advanced by 16; and q10--q14 are clobbered. + endprologue + + CARRY0(D0(q10), q13, q12) + CARRY1(q14, q12) + CARRY0(D0(q11), q14, q12) + CARRY1(q15, q12) + CARRY0(D1(q10), q15, q12) + vshr.u64 D1(q11), D1(q12), #16 + vshr.u64 D0(q15), D1(q12), #48 + vtrn.32 q10, q11 + vst1.32 {q10}, [r0]! + bx r14 +ENDFUNC + +INTFUNC(dmul4) + // On entry, r0 points to the destination; r1 and r2 point to packed + // operands U and X; q2/q3 and q4/q5 hold expanded operands V and Y; + // and q13--q15 hold incoming carries c0--c2. On exit, the + // destination and carries are updated; r0, r1, r2 are each advanced + // by 16; q2--q5 are preserved; and the other NEON registers are + // clobbered. + endprologue + + // Start by loading the operand words from memory. + vld1.32 {q0}, [r1]! + vld1.32 {q1}, [r2]! + + // Do the multiplication. + MUL0(q13, t, q0, q2, q3, q1, q4, q5) + MUL1(q14, t, q0, q2, q3, q1, q4, q5) + CARRY0(D0(q8), q13, q6) + MUL2(q15, t, q0, q2, q3, q1, q4, q5) + CARRY1(q14, q6) + CARRY0(D0(q9), q14, q6) + MUL3(q12, nil, q0, q2, q3, q1, q4, q5) + CARRY1(q15, q6) + CARRY0(D1(q8), q15, q6) + MUL4(q13, nil, q0, q2, q3, q1, q4, q5) + CARRY1(q12, q6) + CARRY0(D1(q9), q12, q6) + MUL5(q14, nil, q0, q2, q3, q1, q4, q5) + CARRY1(q13, q6) + MUL6(q15, nil, q0, q2, q3, q1, q4, q5) + + // Finish up and store the result. + vtrn.32 q8, q9 + vst1.32 {q8}, [r0]! + + // All done. + bx r14 +ENDFUNC + +INTFUNC(dmla4) + // On entry, r0 points to the destination/accumulator; r1 and r2 + // point to packed operands U and X; q2/q3 and q4/q5 hold expanded + // operands V and Y; and q13--q15 hold incoming carries c0--c2. On + // exit, the accumulator and carries are updated; r0, r1, r2 are each + // advanced by 16; q2--q5 are preserved; and the other NEON registers + // are clobbered. + endprologue + + // Start by loading the operand words from memory. + vld1.32 {q9}, [r0] + SPREADACC0(q9, q10, q11, q12) + vld1.32 {q0}, [r1]! + vld1.32 {q1}, [r2]! + + // Add the accumulator input to the incoming carries. Split the + // accumulator into four pieces and add the carries onto them. + SPREADACC1(q9, q10, q11, q12) + CARRYACC(q9, q10, q11, q12, q13, q14, q15) + + // Do the multiplication. + MUL0(q13, t, q0, q2, q3, q1, q4, q5) + MUL1(q14, t, q0, q2, q3, q1, q4, q5) + CARRY0(D0(q8), q13, q6) + MUL2(q15, t, q0, q2, q3, q1, q4, q5) + CARRY1(q14, q6) + CARRY0(D0(q9), q14, q6) + MUL3(q12, t, q0, q2, q3, q1, q4, q5) + CARRY1(q15, q6) + CARRY0(D1(q8), q15, q6) + MUL4(q13, nil, q0, q2, q3, q1, q4, q5) + CARRY1(q12, q6) + CARRY0(D1(q9), q12, q6) + MUL5(q14, nil, q0, q2, q3, q1, q4, q5) + CARRY1(q13, q6) + MUL6(q15, nil, q0, q2, q3, q1, q4, q5) + + // Finish up and store the result. + vtrn.32 q8, q9 + vst1.32 {q8}, [r0]! + + // All done. + bx r14 +ENDFUNC + +INTFUNC(mul4) + // On entry, r0 points to the destination; r2 points to a packed + // operand X; q4/q5 holds an expanded operand Y; and q13--q15 hold + // incoming carries c0--c2. On exit, the destination and carries are + // updated; r0 and r2 are each advanced by 16; q4 and q5 are + // preserved; and the other NEON registers are clobbered. + endprologue + + // Start by loading the operand words from memory. + vld1.32 {q1}, [r2]! + + // Do the multiplication. + MUL0(q13, t, q1, q4, q5, nil, nil, nil) + MUL1(q14, t, q1, q4, q5, nil, nil, nil) + CARRY0(D0(q8), q13, q6) + MUL2(q15, t, q1, q4, q5, nil, nil, nil) + CARRY1(q14, q6) + CARRY0(D0(q9), q14, q6) + MUL3(q12, nil, q1, q4, q5, nil, nil, nil) + CARRY1(q15, q6) + CARRY0(D1(q8), q15, q6) + MUL4(q13, nil, q1, q4, q5, nil, nil, nil) + CARRY1(q12, q6) + CARRY0(D1(q9), q12, q6) + MUL5(q14, nil, q1, q4, q5, nil, nil, nil) + CARRY1(q13, q6) + MUL6(q15, nil, q1, q4, q5, nil, nil, nil) + + // Finish up and store the result. + vtrn.32 q8, q9 + vst1.32 {q8}, [r0]! + + // All done. + bx r14 +ENDFUNC + +INTFUNC(mul4zc) + // On entry, r0 points to the destination; r2 points to a packed + // operand X; and q4/q5 holds an expanded operand Y. On exit, the + // destination is updated; q13--q15 hold outgoing carries c0--c2; r0 + // and r2 are each advanced by 16; q4 and q5 are preserved; and the + // other NEON registers are clobbered. + endprologue + + // Start by loading the operand words from memory. + vld1.32 {q1}, [r2]! + + // Do the multiplication. + MUL0(q13, nil, q1, q4, q5, nil, nil, nil) + MUL1(q14, nil, q1, q4, q5, nil, nil, nil) + CARRY0(D0(q8), q13, q6) + MUL2(q15, nil, q1, q4, q5, nil, nil, nil) + CARRY1(q14, q6) + CARRY0(D0(q9), q14, q6) + MUL3(q12, nil, q1, q4, q5, nil, nil, nil) + CARRY1(q15, q6) + CARRY0(D1(q8), q15, q6) + MUL4(q13, nil, q1, q4, q5, nil, nil, nil) + CARRY1(q12, q6) + CARRY0(D1(q9), q12, q6) + MUL5(q14, nil, q1, q4, q5, nil, nil, nil) + CARRY1(q13, q6) + MUL6(q15, nil, q1, q4, q5, nil, nil, nil) + + // Finish up and store the result. + vtrn.32 q8, q9 + vst1.32 {q8}, [r0]! + + // All done. + bx r14 +ENDFUNC + +INTFUNC(mla4) + // On entry, r0 points to the destination/accumulator; r2 points to a + // packed operand X; q4/q5 holds an expanded operand Y; and q13--q15 + // hold incoming carries c0--c2. On exit, the accumulator and + // carries are updated; r0 and r2 are each advanced by 16; q4 and q5 + // are preserved; and the other NEON registers are clobbered. + endprologue + + // Start by loading the operand words from memory. + vld1.32 {q9}, [r0] + SPREADACC0(q9, q10, q11, q12) + vld1.32 {q1}, [r2]! + + // Add the accumulator input to the incoming carries. Split the + // accumulator into four pieces and add the carries onto them. + SPREADACC1(q9, q10, q11, q12) + CARRYACC(q9, q10, q11, q12, q13, q14, q15) + + // Do the multiplication. +mla4_common: + MUL0(q13, t, q1, q4, q5, nil, nil, nil) + MUL1(q14, t, q1, q4, q5, nil, nil, nil) + CARRY0(D0(q8), q13, q6) + MUL2(q15, t, q1, q4, q5, nil, nil, nil) + CARRY1(q14, q6) + CARRY0(D0(q9), q14, q6) + MUL3(q12, t, q1, q4, q5, nil, nil, nil) + CARRY1(q15, q6) + CARRY0(D1(q8), q15, q6) + MUL4(q13, nil, q1, q4, q5, nil, nil, nil) + CARRY1(q12, q6) + CARRY0(D1(q9), q12, q6) + MUL5(q14, nil, q1, q4, q5, nil, nil, nil) + CARRY1(q13, q6) + MUL6(q15, nil, q1, q4, q5, nil, nil, nil) + + // Finish up and store the result. + vtrn.32 q8, q9 + vst1.32 {q8}, [r0]! + + // All done. + bx r14 +ENDFUNC + +INTFUNC(mla4zc) + // On entry, r0 points to the destination/accumulator; r2 points to a + // packed operand X; and q4/q5 holds an expanded operand Y. On exit, + // the accumulator is updated; q13--q15 hold outgoing carries c0--c2; + // r0 and r2 are each advanced by 16; q4 and q5 are preserved; and + // the other NEON registers are clobbered. + endprologue + + // Start by loading the operand words from memory. + vld1.32 {q13}, [r0] + SPREADACC0(q13, q14, q15, q12) + vld1.32 {q1}, [r2]! + + // Move the accumulator input to the incoming carry slots. Split the + // accumulator into four pieces. + SPREADACC1(q13, q14, q15, q12) + + b mla4_common +ENDFUNC + +INTFUNC(mmul4) + // On entry, r0 points to the destination; r1 points to a packed + // operand U; r2 points to a packed operand X (the modulus); q2/q3 + // holds an expanded operand V; and q4/q5 holds an expanded operand M + // (the Montgomery factor -N^{-1} (mod B)). On exit, the destination + // is updated (to zero); q4/q5 hold an expanded factor Y = U V M (mod + // B); q13--q15 hold outgoing carries c0--c2; r0, r1, and r2 are each + // advanced by 16; q2 and q3 are preserved; and the other NEON + // registers are clobbered. + + // Start by loading the operand words from memory. + vld1.32 {q0}, [r1]! + + // Calculate the low half of W = A + U V, being careful to leave the + // carries in place. + MUL0(q13, nil, q0, q2, q3, nil, nil, nil) + MUL1(q14, nil, q0, q2, q3, nil, nil, nil) + CARRY0(D0(q6), q13, q8) + MUL2(q15, nil, q0, q2, q3, nil, nil, nil) + CASIDE1(D0(q9), q14, q8) + CASIDE0(D0(q7), D0(q9), q14, q8) + MUL3(q12, nil, q0, q2, q3, nil, nil, nil) + b mmla4_common +ENDFUNC + +INTFUNC(mmla4) + // On entry, r0 points to the destination/accumulator A; r1 points to + // a packed operand U; r2 points to a packed operand X (the modulus); + // q2/q3 holds an expanded operand V; and q4/q5 holds an expanded + // operand M (the Montgomery factor -N^{-1} (mod B)). On exit, the + // accumulator is updated (to zero); q4/q5 hold an expanded factor Y + // = (A + U V) M (mod B); q13--q15 hold outgoing carries c0--c2; r0, + // r1, and r2 are each advanced by 16; q2 and q3 are preserved; and + // the other NEON registers are clobbered. + endprologue + + // Start by loading the operand words from memory. + vld1.32 {q13}, [r0] + SPREADACC0(q13, q14, q15, q12) + vld1.32 {q0}, [r1]! + + // Move the accumulator input to the incoming carry slots. Split the + // accumulator into four pieces. + SPREADACC1(q13, q14, q15, q12) + + // Calculate the low half of W = A + U V, being careful to leave the + // carries in place. + MUL0(q13, t, q0, q2, q3, nil, nil, nil) + MUL1(q14, t, q0, q2, q3, nil, nil, nil) + CARRY0(D0(q6), q13, q8) + MUL2(q15, t, q0, q2, q3, nil, nil, nil) + CASIDE1(D0(q9), q14, q8) + CASIDE0(D0(q7), D0(q9), q14, q8) + MUL3(q12, t, q0, q2, q3, nil, nil, nil) +mmla4_common: + CASIDE1(D0(q9), q15, q8) + CASIDE0(D1(q6), D0(q9), q15, q8) + CASIDE1(D0(q9), q12, q8) + CASIDE0E(D1(q7), D0(q9), q12, q8) + vtrn.32 q6, q7 + + // Calculate the low half of the Montgomery factor Y = W M. At this + // point, registers are a little tight. + MUL0( q8, nil, q6, q4, q5, nil, nil, nil) + MUL1( q9, nil, q6, q4, q5, nil, nil, nil) + CARRY0(D0(q8), q8, q1) + MUL2(q10, nil, q6, q4, q5, nil, nil, nil) + CARRY1(q9, q1) + CARRY0(D0(q9), q9, q1) + MUL3(q11, nil, q6, q4, q5, nil, nil, nil) + CARRY1(q10, q1) + CARRY0(D1(q8), q10, q1) + vmov.i32 q5, #0 + CARRY1(q11, q1) + CARRY0(D1(q9), q11, q1) + vld1.32 {q1}, [r2]! + vtrn.32 q8, q9 + + // Expand Y. We'll put it in its proper place a bit later. + vzip.16 q8, q5 + + // Build up the product X Y in the carry slots. + MUL0(q13, t, q1, q8, q5, nil, nil, nil) + MUL1(q14, t, q1, q8, q5, nil, nil, nil) + CARRY0(nil, q13, q9) + MUL2(q15, t, q1, q8, q5, nil, nil, nil) + CARRY1(q14, q9) + vmov q4, q8 + CARRY0(nil, q14, q9) + MUL3(q12, t, q1, q8, q5, nil, nil, nil) + CARRY1(q15, q9) + CARRY0(nil, q15, q9) + vmov.u32 q6, #0 + + // And complete the calculation. + MUL4(q13, nil, q0, q2, q3, q1, q4, q5) + CARRY1(q12, q9) + CARRY0(nil, q12, q9) + MUL5(q14, nil, q0, q2, q3, q1, q4, q5) + CARRY1(q13, q9) + MUL6(q15, nil, q0, q2, q3, q1, q4, q5) + + // Finish up and store the result. + vst1.32 {q6}, [r0]! + + // All done. + bx r14 +ENDFUNC + +INTFUNC(mont4) + // On entry, r0 points to the destination/accumulator A; r2 points to + // a packed operand X (the modulus); and q2/q3 holds an expanded + // operand M (the Montgomery factor -N^{-1} (mod B)). On exit, the + // accumulator is updated (to zero); q4/q5 hold an expanded factor Y + // = A M (mod B); q13--q15 hold outgoing carries c0--c2; r0 and r2 + // are each advanced by 16; q2 and q3 are preserved; and the other + // NEON registers are clobbered. + endprologue + + // Start by loading the operand words from memory. + vld1.32 {q0}, [r0] + vld1.32 {q1}, [r2]! + + // Calculate Y = A M (mod B). + MUL0(q8, nil, q0, q2, q3, nil, nil, nil) + MUL1(q9, nil, q0, q2, q3, nil, nil, nil) + CARRY0(D0(q4), q8, q6) + MUL2(q10, nil, q0, q2, q3, nil, nil, nil) + CARRY1(q9, q6) + vmov q13, q0 + CARRY0(D0(q7), q9, q6) + MUL3(q11, nil, q0, q2, q3, nil, nil, nil) + CARRY1(q10, q6) + CARRY0(D1(q4), q10, q6) + SPREADACC0(q13, q14, q15, q12) + CARRY1(q11, q6) + CARRY0(D1(q7), q11, q6) + SPREADACC1(q13, q14, q15, q12) + vmov.i32 q5, #0 + vtrn.32 q4, q7 + vzip.16 q4, q5 + + // Calculate the actual result. Well, the carries, at least. + vmov.i32 q8, #0 + MUL0(q13, t, q1, q4, q5, nil, nil, nil) + MUL1(q14, t, q1, q4, q5, nil, nil, nil) + CARRY0(nil, q13, q6) + MUL2(q15, t, q1, q4, q5, nil, nil, nil) + CARRY1(q14, q6) + CARRY0(nil, q14, q6) + MUL3(q12, t, q1, q4, q5, nil, nil, nil) + CARRY1(q15, q6) + CARRY0(nil, q15, q6) + MUL4(q13, nil, q1, q4, q5, nil, nil, nil) + CARRY1(q12, q6) + CARRY0(nil, q12, q6) + MUL5(q14, nil, q1, q4, q5, nil, nil, nil) + CARRY1(q13, q6) + MUL6(q15, nil, q1, q4, q5, nil, nil, nil) + + // Finish up and store the result. + vst1.32 {q8}, [r0]! + + // All done. + bx r14 +ENDFUNC + +///-------------------------------------------------------------------------- +/// Bulk multipliers. + +FUNC(mpx_umul4_arm_neon) + // void mpx_umul4_arm_neon(mpw *dv, const mpw *av, const mpw *avl, + // const mpw *bv, const mpw *bvl); + + // Establish the arguments and do initial setup. + // + // inner loop dv r0 + // inner loop av r2 + // outer loop dv r5 + // outer loop bv r3 + // av base r1 + // av limit r12 + // bv limit r4 + pushreg r4, r5, r14 + pushvfp QQ(q4, q7) + endprologue + + // Prepare for the first iteration. + vld1.32 {q4}, [r3]! // = Y = bv[0] + vmov.i32 q5, #0 + // r0 // = dv for inner loop + // r1 // = av base + // r3 // = bv for outer loop + ldr r4, [sp, #76] // = bv limit + mov r12, r2 // = av limit + mov r2, r1 // = av for inner loop + add r5, r0, #16 // = dv for outer loop + vzip.16 q4, q5 // expand Y + bl mul4zc + cmp r2, r12 // all done? + bhs 8f + + // Continue with the first iteration. +0: bl mul4 + cmp r2, r12 // all done? + blo 0b + + // Write out the leftover carry. There can be no tail here. +8: bl carryprop + cmp r3, r4 // more passes to do? + bhs 9f + + // Set up for the next pass. +1: vld1.32 {q4}, [r3]! // = Y = bv[i] + vmov.i32 q5, #0 + mov r0, r5 // -> dv[i] + mov r2, r1 // -> av[0] + add r5, r5, #16 + vzip.16 q4, q5 // expand Y + bl mla4zc + cmp r2, r12 // done yet? + bhs 8f + + // Continue... +0: bl mla4 + cmp r2, r12 + blo 0b + + // Finish off this pass. There was no tail on the previous pass, and + // there can be done on this pass. +8: bl carryprop + cmp r3, r4 + blo 1b + + // All over. +9: popvfp QQ(q4, q7) + popreg r4, r5, r14 + bx r14 +ENDFUNC + +FUNC(mpxmont_mul4_arm_neon) + // void mpxmont_mul4_arm_neon(mpw *dv, const mpw *av, const mpw *bv, + // const mpw *nv, size_t n, const mpw *mi); + + // Establish the arguments and do initial setup. + // + // inner loop dv r0 + // inner loop av r1 + // inner loop nv r2 + // mi r5 + // outer loop dv r6 + // outer loop bv r7 + // av base r8 + // av limit r9 + // bv limit r4 + // nv base r3 + // n r4 + // c r10 + // 0 r12 + + pushreg r4-r10, r14 + pushvfp QQ(q4, q7) + endprologue + + // Establish the expanded operands. + ldrd r4, r5, [sp, #96] // r4 = n; r5 -> mi + vld1.32 {q2}, [r2] // = V = bv[0] + vmov.i32 q3, #0 + vmov.i32 q5, #0 + vld1.32 {q4}, [r5] // = M + + // Set up the outer loop state and prepare for the first iteration. + // r0 // -> dv for inner loop + // r1 // -> av for inner loop + add r7, r2, #16 // -> bv + // r3 // -> nv + add r6, r0, #16 // -> dv + mov r8, r1 // -> av + add r9, r1, r4, lsl #2 // -> av limit + add r4, r2, r4, lsl #2 // -> bv limit + mov r2, r3 // -> nv for inner loop + mov r12, #0 // = 0 + + vzip.16 q2, q3 // expand V + vzip.16 q4, q5 // expand M + bl mmul4 + cmp r1, r9 // done already? + bhs 8f + + // Complete the first inner loop. +0: bl dmul4 + cmp r1, r9 // done yet? + blo 0b + + // Still have carries left to propagate. Rather than store the tail + // end in memory, keep it in a general-purpose register for later. + bl carryprop + vmov.32 r10, QW(q15, 0) + + // Embark on the next iteration. (There must be one. If n = 1 then + // we would have bailed above, to label 8. Similarly, the subsequent + // iterations can fall into the inner loop immediately.) +1: vld1.32 {q2}, [r7]! // = V = bv[i] + vld1.32 {q4}, [r5] // = M + vmov.i32 q3, #0 + vmov.i32 q5, #0 + mov r0, r6 // -> dv[i] + add r6, r6, #16 + mov r1, r8 // -> av[0] + mov r2, r3 // -> nv[0] + vzip.16 q2, q3 // expand V + vzip.16 q4, q5 // expand M + bl mmla4 + + // Complete the next inner loop. +0: bl dmla4 + cmp r1, r9 // done yet? + blo 0b + + // Still have carries left to propagate, and they overlap the + // previous iteration's final tail, so read that and add it. + cmp r7, r4 + vmov.32 D0(q12), r10, r12 + vadd.i64 D0(q13), D0(q13), D0(q12) + bl carryprop + vmov.32 r10, QW(q15, 0) + + // Back again, maybe. + blo 1b + + // All done, almost. + str r10, [r0], #4 + popvfp QQ(q4, q7) + popreg r4-r10, r14 + bx r14 + + // First iteration was short. Write out the carries and we're done. + // (This could be folded into the main loop structure, but that would + // penalize small numbers more.) +8: bl carryprop + vst1.32 {QW(q15, 0)}, [r0]! + popvfp QQ(q4, q7) + popreg r4-r10, r14 + bx r14 +ENDFUNC + +FUNC(mpxmont_redc4_arm_neon) + // void mpxmont_redc4_arm_neon(mpw *dv, mpw *dvl, const mpw *nv, + // size_t n, const mpw *mi); + + // Establish the arguments and do initial setup. + // + // inner loop dv r0 + // dv limit r1 + // inner loop nv r2 + // blocks-of-4 dv limit r3 + // mi (r14) + // outer loop dv r4 + // outer loop dv limit r5 + // nv base r6 + // nv limit r7 + // n r3 + // c (r14) + // t0, t1, t2, t3 r2, r8, r9, r10 + // 0 r12 + + pushreg r4-r10, r14 + pushvfp QQ(q4, q7) + endprologue + + // Set up the outer loop state and prepare for the first iteration. + ldr r14, [sp, #96] // -> mi + vmov.i32 q3, #0 + sub r12, r1, r0 // total dv bytes + // r0 // -> dv for inner loop + // r1 // -> overall dv limit + // r2 // -> nv for inner loop + // r3 // = n (for now) + add r4, r0, #16 // -> dv for outer loop + add r5, r0, r3, lsl #2 // -> dv limit + bic r12, r12, #15 // dv blocks of 4 + vld1.32 {q2}, [r14] // = M + mov r6, r2 // -> nv + add r7, r2, r3, lsl #2 // -> nv limit + add r3, r0, r12 // -> dv blocks-of-4 limit + vzip.16 q2, q3 // expand M + mov r12, #0 // = 0 + bl mont4 + cmp r2, r7 // done already? + bhs 8f + +5: bl mla4 + cmp r2, r7 // done yet? + blo 5b + + // Still have carries left to propagate. Adding the accumulator + // block into the carries is a little different this time, because + // all four accumulator limbs have to be squished into the three + // carry registers for `carryprop' to do its thing. +8: vld1.32 {q9}, [r0] + SPREADACC0(q9, q10, q11, q12) + SPREADACC1(q9, q10, q11, q12) + vshl.u64 D0(q12), D0(q12), #16 + CARRYACC(q9, q10, q11, q12, q13, q14, q15) + vadd.u64 D1(q15), D1(q15), D0(q12) + + bl carryprop + vmov.32 r14, QW(q15, 0) + cmp r0, r3 + bhs 7f + + // Propagate the first group of carries. + ldmia r0, {r2, r8-r10} + adds r2, r2, r14 + adcs r8, r8, #0 + adcs r9, r9, #0 + adcs r10, r10, #0 + stmia r0!, {r2, r8-r10} + teq r0, r3 + beq 6f + + // Continue carry propagation until the end of the buffer. +0: ldmia r0, {r2, r8-r10} + adcs r2, r2, #0 + adcs r8, r8, #0 + adcs r9, r9, #0 + adcs r10, r10, #0 + stmia r0!, {r2, r8-r10} + teq r0, r3 + bne 0b + + // Deal with the tail end. Note that the actual destination length + // won't be an exacty number of blocks of four, so it's safe to just + // drop through here. +6: adc r14, r12, #0 +7: ldr r2, [r0] + adds r2, r2, r14 + str r2, [r0], #4 + teq r0, r1 + beq 8f +0: ldr r2, [r0] + adcs r2, r2, #0 + str r2, [r0], #4 + teq r0, r1 + bne 0b + + // All done for this iteration. Start the next. +8: cmp r4, r5 + bhs 9f + mov r0, r4 + add r4, r4, #16 + mov r2, r6 + bl mont4 + b 5b + + // All over. +9: popvfp QQ(q4, q7) + popreg r4-r10, r14 + bx r14 +ENDFUNC + +///-------------------------------------------------------------------------- +/// Testing and performance measurement. + +#ifdef TEST_MUL4 + +// dmul smul mmul mont +// z r0 r0 r0 r0 r0 +// c r4 r1 r1 r1 r1 +// y r3 -- -- r2 r2 +// u r1 r2 -- r3 -- +// x r2 r3 r2 stk0 r3 +// vv q2/q3 stk0 -- stk1 stk0 +// yy q4/q5 stk1 r3 stk2 -- +// n r5 stk2 stk0 stk3 stk1 +// cyv r6 stk3 stk1 stk4 stk2 + +#define STKARG(i) sp, #80 + 4*(i) + +.macro testprologue mode + pushreg r4-r6, r14 + pushvfp QQ(q4, q7) + endprologue + + .ifeqs "\mode", "dmul" + mov r4, r1 // -> c + mov r1, r2 // -> u + mov r2, r3 // -> x + + ldr r14, [STKARG(0)] // -> vv + vld1.32 {q2}, [r14] + vmov.i32 q3, #0 + vzip.16 q2, q3 // (v'_0, v''_0; v'_1, v''_1) + + ldr r14, [STKARG(1)] // -> yy + vld1.32 {q4}, [r14] + vmov.i32 q5, #0 + vzip.16 q4, q5 // (y'_0, y''_0; y'_1, y''_1) + + ldr r5, [STKARG(2)] // = n + ldr r6, [STKARG(3)] // -> cyv + .endif + + .ifeqs "\mode", "smul" + mov r4, r1 // -> c + // r2 // -> x + + vld1.32 {q4}, [r3] + vmov.i32 q5, #0 + vzip.16 q4, q5 // (y'_0, y''_0; y'_1, y''_1) + + ldr r5, [STKARG(0)] // = n + ldr r6, [STKARG(1)] // -> cyv + .endif + + .ifeqs "\mode", "mmul" + mov r4, r1 // -> c + mov r1, r3 // -> u + mov r3, r2 // -> y + ldr r2, [STKARG(0)] // -> x + + ldr r14, [STKARG(1)] // -> vv + vld1.32 {q2}, [r14] + vmov.i32 q3, #0 + vzip.16 q2, q3 // (v'_0, v''_0; v'_1, v''_1) + + ldr r14, [STKARG(2)] // -> yy + vld1.32 {q4}, [r14] + vmov.i32 q5, #0 + vzip.16 q4, q5 // (y'_0, y''_0; y'_1, y''_1) + + ldr r5, [STKARG(3)] // = n + ldr r6, [STKARG(4)] // -> cyv + .endif + + .ifeqs "\mode", "mont" + mov r4, r1 // -> c + mov r1, r3 // -> u + mov r14, r2 + mov r2, r3 // -> x + mov r3, r14 // -> y + + ldr r14, [STKARG(0)] // -> vv + vld1.32 {q2}, [r14] + vmov.i32 q3, #0 + vzip.16 q2, q3 // (v'_0, v''_0; v'_1, v''_1) + + ldr r5, [STKARG(1)] // = n + ldr r6, [STKARG(2)] // -> cyv + .endif +.endm + +.macro testldcarry + vldmia r4, {QQ(q13, q15)} // c0, c1, c2 +.endm + +.macro testtop +0: subs r5, r5, #1 +.endm + +.macro testtail + bne 0b +.endm + +.macro testcarryout + vstmia r4, {QQ(q13, q15)} +.endm + +.macro testepilogue + popvfp QQ(q4, q7) + popreg r4-r6, r14 + bx r14 +.endm + +FUNC(test_dmul4) + testprologue dmul + testldcarry + testtop + bl dmul4 + testtail + testcarryout + testepilogue +ENDFUNC + +FUNC(test_dmla4) + testprologue dmul + testldcarry + testtop + bl dmla4 + testtail + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mul4) + testprologue smul + testldcarry + testtop + bl mul4 + testtail + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mul4zc) + testprologue smul + testldcarry + testtop + bl mul4zc + testtail + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mla4) + testprologue smul + testldcarry + testtop + bl mla4 + testtail + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mla4zc) + testprologue smul + testldcarry + testtop + bl mla4zc + testtail + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mmul4) + testprologue mmul + testtop + bl mmul4 + testtail + vst1.32 {q4, q5}, [r3] + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mmla4) + testprologue mmul + testtop + bl mmla4 + testtail + vst1.32 {q4, q5}, [r3] + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mont4) + testprologue mont + testtop + bl mont4 + testtail + vst1.32 {q4, q5}, [r3] + testcarryout + testepilogue +ENDFUNC + +#endif + +///----- That's all, folks -------------------------------------------------- diff --git a/math/mpx-mul4-arm64-simd.S b/math/mpx-mul4-arm64-simd.S new file mode 100644 index 00000000..60eed208 --- /dev/null +++ b/math/mpx-mul4-arm64-simd.S @@ -0,0 +1,1217 @@ +/// -*- mode: asm; asm-comment-char: ?/ -*- +/// +/// Large SIMD-based multiplications +/// +/// (c) 2019 Straylight/Edgeware +/// + +///----- Licensing notice --------------------------------------------------- +/// +/// This file is part of Catacomb. +/// +/// Catacomb is free software: you can redistribute it and/or modify it +/// under the terms of the GNU Library General Public License as published +/// by the Free Software Foundation; either version 2 of the License, or +/// (at your option) any later version. +/// +/// Catacomb is distributed in the hope that it will be useful, but +/// WITHOUT ANY WARRANTY; without even the implied warranty of +/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +/// Library General Public License for more details. +/// +/// You should have received a copy of the GNU Library General Public +/// License along with Catacomb. If not, write to the Free Software +/// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +/// USA. + +///-------------------------------------------------------------------------- +/// Preliminaries. + +#include "config.h" +#include "asm-common.h" + + .text + +///-------------------------------------------------------------------------- +/// Theory. +/// +/// We define a number of primitive fixed-size multipliers from which we can +/// construct more general variable-length multipliers. +/// +/// The basic trick is the same throughout. In an operand-scanning +/// multiplication, the inner multiplication loop multiplies a multiple- +/// precision operand by a single precision factor, and adds the result, +/// appropriately shifted, to the result. A `finely integrated operand +/// scanning' implementation of Montgomery multiplication also adds the +/// product of a single-precision `Montgomery factor' and the modulus, +/// calculated in the same pass. The more common `coarsely integrated +/// operand scanning' alternates main multiplication and Montgomery passes, +/// which requires additional carry propagation. +/// +/// Throughout both plain-multiplication and Montgomery stages, then, one of +/// the factors remains constant throughout the operation, so we can afford +/// to take a little time to preprocess it. The transformation we perform is +/// as follows. Let b = 2^16, and B = b^2 = 2^32. Suppose we're given a +/// 128-bit factor v = v_0 + v_1 B + v_2 B^2 + v_3 B^3. Split each v_i into +/// two sixteen-bit pieces, so v_i = v'_i + v''_i b. These eight 16-bit +/// pieces are placed into 32-bit cells, and arranged as two 128-bit SIMD +/// operands, as follows. +/// +/// Offset 0 4 8 12 +/// 0 v'_0 v''_0 v'_1 v''_1 +/// 16 v'_2 v''_2 v'_3 v''_3 +/// +/// The `umull' and `umlal' instructions can multiply a vector of two 32-bit +/// values by a 32-bit scalar, giving two 64-bit results; thus, it will act +/// on (say) v'_0 and v''_0 in a single instruction, to produce two 48-bit +/// results in 64-bit fields. The sixteen bits of headroom allows us to add +/// many products together before we must deal with carrying; it also allows +/// for some calculations to be performed on the above expanded form. +/// +/// We maintain three `carry' registers, v28--v30, accumulating intermediate +/// results; we name them `c0', `c1', and `c2'. Each carry register holds +/// two 64-bit halves: the register c0, for example, holds c'_0 (low half) +/// and c''_0 (high half), and represents the value c_0 = c'_0 + c''_0 b; the +/// carry registers collectively represent the value c_0 + c_1 B + c_2 B^2. +/// The `umull' or `umlal' instruction acting on a scalar operand and an +/// operand in the expanded form above produces a result which can be added +/// directly to the appropriate carry register. +/// +/// An unusual feature of this code, as compared to the other `mul4' +/// implementations, is that it makes extensive use of the ARM64 +/// general-purpose registers for carry resolution and output construction. +/// As a result, an additional general-purpose register (typically x15) is +/// used as an additional carry, with the carry value in bits 16--63. +/// +/// Multiplication is performed in product-scanning order, since ARM +/// processors commonly implement result forwarding for consecutive multiply- +/// and-accumulate instructions specifying the same destination. +/// Experimentally, this runs faster than operand-scanning in an attempt to +/// hide instruction latencies. +/// +/// On 64-bit ARM, we have a vast supply number of registers: the expanded +/// operands are kept in registers. The packed operands are read from memory +/// into working registers v0 and v1. The following conventional argument +/// names and locations are used throughout. +/// +/// Arg Format Location Notes +/// +/// U packed [x1] +/// X packed [x2] In Montgomery multiplication, X = N +/// V expanded v2/v3 +/// Y expanded v4/v5 In Montgomery multiplication, Y = (A + U V) M +/// M expanded v6/v7 -N^{-1} (mod B^4) +/// N Modulus, for Montgomery multiplication +/// A packed [x0] Destination/accumulator +/// C carry v28--v30 +/// 0 v31 128-bit zero +/// +/// The calculation is some variant of +/// +/// A' + C' B^4 <- U V + X Y + A + C +/// +/// The low-level functions fit into a fairly traditional (finely-integrated) +/// operand scanning loop over operand pairs (U, X) (indexed by j) and (V, Y) +/// (indexed by i). +/// +/// The variants are as follows. +/// +/// Function Variant Use i j +/// +/// mmul4 A = C = 0 Montgomery 0 0 +/// dmul4 A = 0 Montgomery 0 + +/// mmla4 C = 0 Montgomery + 0 +/// dmla4 exactly as shown Montgomery + + +/// +/// mul4zc U = V = A = C = 0 Plain 0 0 +/// mul4 U = V = A = 0 Plain 0 + +/// mla4zc U = V = C = 0 Plain + 0 +/// mla4 U = V = 0 Plain + + +/// +/// The `mmul4' and `mmla4' functions are also responsible for calculating +/// the Montgomery reduction factor Y = (A + U V) M used by the rest of the +/// inner loop. + +///-------------------------------------------------------------------------- +/// Macro definitions. + +.macro mulacc z, u, v, x=nil, y=nil + // Set Z = Z + U V + X Y, using the low halves of V and Y. Y may be + // `nil' to omit the second operand. Z, V, and Y should be 128-bit + // `vN' registers; and U and X should be 32-bit `vN.s[I]' scalars; + // the multiplications produce two 64-bit elementwise products, which + // are added elementwise to Z. + + umlal \z\().2d, \v\().2s, \u + .ifnes "\y", "nil" + umlal \z\().2d, \y\().2s, \x + .endif +.endm + +.macro mulacc2 z, u, v, x=nil, y=nil + // Set Z = Z + U V + X Y, using the high halves of V and Y; see + // `mulacc'. + + umlal2 \z\().2d, \v\().4s, \u + .ifnes "\y", "nil" + umlal2 \z\().2d, \y\().4s, \x + .endif +.endm + +.macro mulinit z, zinitp, u, v=nil, x=nil, y=nil + // If ZINITP then set Z = Z + U V + X Y, as for `mulacc'; otherwise, + // set Z = U V + X Y. Operand requirements and detailed operation + // are as for `mulacc'. + + .ifeqs "\zinitp", "t" + mulacc \z, \u, \v, \x, \y + .else + umull \z\().2d, \v\().2s, \u + .ifnes "\y", "nil" + umlal \z\().2d, \y\().2s, \x + .endif + .endif +.endm + +.macro mulini2 z, zinitp, u, v=nil, x=nil, y=nil + // As `mulinit', but with the high halves of V and Y. + + .ifeqs "\zinitp", "t" + mulacc2 \z, \u, \v, \x, \y + .else + umull2 \z\().2d, \v\().4s, \u + .ifnes "\y", "nil" + umlal2 \z\().2d, \y\().4s, \x + .endif + .endif +.endm + +// `mulI': accumulate the B^I and b B^I terms of the polynomial product sum +// U V + X Y, given that U = u_0 + B u_1 + B^2 u_2 + B^3 u_3 (and similarly +// for x), and V = v'_0 + b v''_0 + B (v'_1 + b v''_1) + B^2 (v'_2 + b v''_2) +// + B^3 (v'_3 + b v''_3) (and similarly for Y). The 64-bit coefficients are +// added into the low and high halves of the 128-bit register Z (if ZINIT is +// `nil' then simply set Z, as if it were initially zero). +.macro mul0 z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil + mulinit \z, \zinitp, \u\().s[0], \v0, \x\().s[0], \y0 +.endm +.macro mul1 z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil + mulini2 \z, \zinitp, \u\().s[0], \v0, \x\().s[0], \y0 + mulacc \z, \u\().s[1], \v0, \x\().s[1], \y0 +.endm +.macro mul2 z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil + mulinit \z, \zinitp, \u\().s[0], \v1, \x\().s[0], \y1 + mulacc2 \z, \u\().s[1], \v0, \x\().s[1], \y0 + mulacc \z, \u\().s[2], \v0, \x\().s[2], \y0 +.endm +.macro mul3 z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil + mulini2 \z, \zinitp, \u\().s[0], \v1, \x\().s[0], \y1 + mulacc \z, \u\().s[1], \v1, \x\().s[1], \y1 + mulacc2 \z, \u\().s[2], \v0, \x\().s[2], \y0 + mulacc \z, \u\().s[3], \v0, \x\().s[3], \y0 +.endm +.macro mul4 z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil + mulini2 \z, \zinitp, \u\().s[1], \v1, \x\().s[1], \y1 + mulacc \z, \u\().s[2], \v1, \x\().s[2], \y1 + mulacc2 \z, \u\().s[3], \v0, \x\().s[3], \y0 +.endm +.macro mul5 z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil + mulini2 \z, \zinitp, \u\().s[2], \v1, \x\().s[2], \y1 + mulacc \z, \u\().s[3], \v1, \x\().s[3], \y1 +.endm +.macro mul6 z, zinitp, u, v0, v1, x=nil, y0=nil, y1=nil + mulini2 \z, \zinitp, \u\().s[3], \v1, \x\().s[3], \y1 +.endm + +// Steps in the process of propagating carry bits upwards from ZLO (a 128-bit +// `vN' register). Here, T0, T1, and CG are 64-bit `xN' general-purpose +// registers clobbered in the process. Set the low 32 bits of the 64-bit +// `xN' general-purpose register ZOUT to the completed coefficient z_1, +// leaving a carry in CG. +// +// In detail, what happens is as follows. Suppose initially that ZLO = +// (z'_i; z''_i) and ZHI = (z'_{i+1}; z''_{i+1}). Let t = z'_i + b z''_i; +// observe that floor(t/b) = floor(z'_i/b) + z''_i. Let z_i = t mod B, and +// add floor(t/B) = floor((floor(z'_i/b) + z''_i)/b) onto z'_{i+1}. This has +// a circuit depth of 3; I don't know how to do better. +// +// Output words are left in the low half of a 64-bit register, with rubbish +// in the high half. Two such results can be combined using the `bfi' +// instruction. +.macro carry0 zlo, cg=x15, t0=x16, t1=x17 + // Capture the values of carry-register ZLO and CG (if not `nil') in + // general-purpose registers T0 and T1, suitable for use in `carry1'. + mov \t0, \zlo\().d[0] + mov \t1, \zlo\().d[1] + .ifnes "\cg", "nil" + add \t0, \t0, \cg, lsr #16 + .endif +.endm +.macro carry1 zout, cg=x15, t0=x16, t1=x17 + // Collect a 32-bit output word in the low 32 bits of ZOUT (leaving + // rubbish in the high 32 bits), and update CG suitably to continue + // processing with the next carry register. + .ifnes "\zout", "nil" + add \zout, \t0, \t1, lsl #16 + .endif + .ifnes "\cg", "nil" + add \cg, \t1, \t0, lsr #16 + .endif +.endm + +.macro expand vlo, vhi, vz=v31 + // Expand the packed 128-bit operand in VLO to an expanded operand in + // VLO and VHI, assuming that VZ is all-bits-zero. All three are + // `vN' 128-bit SIMD registers. + zip2 \vhi\().8h, \vlo\().8h, \vz\().8h + zip1 \vlo\().8h, \vlo\().8h, \vz\().8h +.endm + +.macro sprdacc a0, a1, a2, a3=nil + // Spread the packed 128-bit operand in A0 into carry-format values + // in A0, A1, A2, A3. If A3 is `nil', then spread the same value + // into A0, A1, A2 only, clobbering x16. + .ifeqs "\a3", "nil" + mov w16, \a0\().s[3] + .endif + trn2 \a2\().2d, \a0\().2d, v31.2d + trn2 \a1\().2s, \a0\().2s, v31.2s + .ifeqs "\a3", "nil" + lsl x16, x16, #16 + .endif + trn1 \a0\().2s, \a0\().2s, v31.2s + .ifeqs "\a3", "nil" + mov \a2\().d[1], x16 + .else + trn2 \a3\().2s, \a2\().2s, v31.2s + .endif + mov \a2\().s[1], wzr +.endm + +.macro crryacc a0, a1, a2, a3, c0, c1, c2 + // Add the carry-format values A0, A1, A2 into the existing carries + // C0, C1, C2 (leaving A3 where it is). + add \c0\().2d, \c0\().2d, \a0\().2d + add \c1\().2d, \c1\().2d, \a1\().2d + add \c2\().2d, \c2\().2d, \a2\().2d +.endm + +///-------------------------------------------------------------------------- +/// Primitive multipliers and related utilities. + +INTFUNC(carryprop) + // On entry, x0 points to a destination, and v28--v30 and x15 hold + // incoming carries c0--c2 and cg. On exit, the low 128 bits of the + // carry value are stored at [x0]; the remaining 16 bits of carry are + // left in x10; x0 is advanced by 16; and x11--x17 are clobbered. + endprologue + + carry0 v28 + carry1 x11 + carry0 v29 + carry1 x13 + carry0 v30 + carry1 x12 + bfi x11, x13, #32, #32 + lsr x14, x15, #16 + lsr x10, x15, #48 + bfi x12, x14, #32, #32 + stp x11, x12, [x0], #16 + ret +ENDFUNC + +INTFUNC(dmul4) + // On entry, x0 points to the destination; x1 and x2 point to packed + // operands U and X; v2/v3 and v4/v5 hold expanded operands V and Y; + // v28--v30 and x15 hold incoming carries c0--c2 and cg; and v31 is + // zero. On exit, the destination and carries are updated; x0, x1, + // x2 are each advanced by 16; v2--v5 and v8--v15 are preserved; and + // x11--x14, x16, x17 and the other SIMD registers are clobbered. + endprologue + + // Start by loading the operand words from memory. + ldr q0, [x1], #16 + ldr q1, [x2], #16 + + // Do the multiplication. + mul0 v28, t, v0, v2, v3, v1, v4, v5 + mul1 v29, t, v0, v2, v3, v1, v4, v5 + carry0 v28 + mul2 v30, t, v0, v2, v3, v1, v4, v5 + carry1 x11 + carry0 v29 + mul3 v27, nil, v0, v2, v3, v1, v4, v5 + carry1 x13 + carry0 v30 + mul4 v28, nil, v0, v2, v3, v1, v4, v5 + carry1 x12 + carry0 v27 + mul5 v29, nil, v0, v2, v3, v1, v4, v5 + carry1 x14 + mul6 v30, nil, v0, v2, v3, v1, v4, v5 + + // Finish up and store the result. + bfi x11, x13, #32, #32 + bfi x12, x14, #32, #32 + stp x11, x12, [x0], #16 + + // All done. + ret +ENDFUNC + +INTFUNC(dmla4) + // On entry, x0 points to the destination/accumulator A; x1 and x2 + // point to packed operands U and X; v2/v3 and v4/v5 hold expanded + // operands V and Y; v28--v30 and x15 hold incoming carries c0--c2 + // and cg; and v31 is zero. On exit, the accumulator and carries are + // updated; x0, x1, x2 are each advanced by 16; v2--v5 and v8--v15 + // are preserved; and x11--x14, x16, x17 and the other SIMD registers + // are clobbered. + endprologue + + // Start by loading the operand words from memory. + ldr q24, [x0] + ldr q0, [x1], #16 + ldr q1, [x2], #16 + sprdacc v24, v25, v26, v27 + crryacc v24, v25, v26, v27, v28, v29, v30 + + // Do the multiplication. + mul0 v28, t, v0, v2, v3, v1, v4, v5 + mul1 v29, t, v0, v2, v3, v1, v4, v5 + carry0 v28 + mul2 v30, t, v0, v2, v3, v1, v4, v5 + carry1 x11 + carry0 v29 + mul3 v27, t, v0, v2, v3, v1, v4, v5 + carry1 x13 + carry0 v30 + mul4 v28, nil, v0, v2, v3, v1, v4, v5 + carry1 x12 + carry0 v27 + mul5 v29, nil, v0, v2, v3, v1, v4, v5 + carry1 x14 + mul6 v30, nil, v0, v2, v3, v1, v4, v5 + + // Finish up and store the result. + bfi x11, x13, #32, #32 + bfi x12, x14, #32, #32 + stp x11, x12, [x0], #16 + + // All done. + ret +ENDFUNC + +INTFUNC(mul4) + // On entry, x0 points to the destination; x2 points to a packed + // operand X; v4/v5 holds an expanded operand Y; v13--v15 and x15 + // hold incoming carries c0--c2 and cg; and v31 is zero. On exit, + // the destination and carries are updated; x0 and x2 are each + // advanced by 16; v4 and v5 and v8--v15 are preserved; and x11--x14, + // x16, x17 and the other SIMD registers are clobbered. + endprologue + + // Start by loading the operand words from memory. + ldr q1, [x2], #16 + + // Do the multiplication. + mul0 v28, t, v1, v4, v5 + mul1 v29, t, v1, v4, v5 + carry0 v28 + mul2 v30, t, v1, v4, v5 + carry1 x11 + carry0 v29 + mul3 v27, nil, v1, v4, v5 + carry1 x13 + carry0 v30 + mul4 v28, nil, v1, v4, v5 + carry1 x12 + carry0 v27 + mul5 v29, nil, v1, v4, v5 + carry1 x14 + mul6 v30, nil, v1, v4, v5 + + // Finish up and store the result. + bfi x11, x13, #32, #32 + bfi x12, x14, #32, #32 + stp x11, x12, [x0], #16 + + // All done. + ret +ENDFUNC + +INTFUNC(mul4zc) + // On entry, x0 points to the destination; x2 points to a packed + // operand X; v4/v5 holds an expanded operand Y; and v31 is zero. On + // exit, the destination is updated; v28--v30 and x15 hold outgoing + // carries c0--c2 and cg; x0 and x2 are each advanced by 16; v4 and + // v5 and v8--v15 are preserved; and x11--x14, x16, x17 and the other + // SIMD registers are clobbered. + endprologue + + // Start by loading the operand words from memory. + ldr q1, [x2], #16 + + // Do the multiplication. + mul0 v28, nil, v1, v4, v5 + mul1 v29, nil, v1, v4, v5 + carry0 v28, nil + mul2 v30, nil, v1, v4, v5 + carry1 x11 + carry0 v29 + mul3 v27, nil, v1, v4, v5 + carry1 x13 + carry0 v30 + mul4 v28, nil, v1, v4, v5 + carry1 x12 + carry0 v27 + mul5 v29, nil, v1, v4, v5 + carry1 x14 + mul6 v30, nil, v1, v4, v5 + + // Finish up and store the result. + bfi x11, x13, #32, #32 + bfi x12, x14, #32, #32 + stp x11, x12, [x0], #16 + + // All done. + ret +ENDFUNC + +INTFUNC(mla4) + // On entry, x0 points to the destination/accumulator A; x2 points to + // a packed operand X; v4/v5 holds an expanded operand Y; v13--v15 + // and x15 hold incoming carries c0--c2 and cg; and v31 is zero. On + // exit, the accumulator and carries are updated; x0 and x2 are each + // advanced by 16; v4 and v5 and v8--v15 are preserved; and x11--x14, + // x16, x17 and the other SIMD registers are clobbered. + endprologue + + // Start by loading the operand words from memory. + ldr q24, [x0] + ldr q1, [x2], #16 + sprdacc v24, v25, v26, v27 + crryacc v24, v25, v26, v27, v28, v29, v30 + + // Do the multiplication. + mul0 v28, t, v1, v4, v5 + mul1 v29, t, v1, v4, v5 + carry0 v28 + mul2 v30, t, v1, v4, v5 + carry1 x11 + carry0 v29 + mul3 v27, t, v1, v4, v5 + carry1 x13 + carry0 v30 + mul4 v28, nil, v1, v4, v5 + carry1 x12 + carry0 v27 + mul5 v29, nil, v1, v4, v5 + carry1 x14 + mul6 v30, nil, v1, v4, v5 + + // Finish up and store the result. + bfi x11, x13, #32, #32 + bfi x12, x14, #32, #32 + stp x11, x12, [x0], #16 + + // All done. + ret +ENDFUNC + +INTFUNC(mla4zc) + // On entry, x0 points to the destination/accumulator A; x2 points to + // a packed operand X; v4/v5 holds an expanded operand Y; and v31 is + // zero. On exit, the accumulator is updated; v28--v30 and x15 hold + // outgoing carries c0--c2 and cg; x0 and x2 are each advanced by 16; + // v4, v5, and v8--v15 are preserved; and x11--x14, x16, x17 and the + // other SIMD registers are clobbered. + endprologue + + // Start by loading the operand words from memory. + ldr q28, [x0] + ldr q1, [x2], #16 + sprdacc v28, v29, v30, v27 + + // Do the multiplication. + mul0 v28, t, v1, v4, v5 + mul1 v29, t, v1, v4, v5 + carry0 v28, nil + mul2 v30, t, v1, v4, v5 + carry1 x11 + carry0 v29 + mul3 v27, t, v1, v4, v5 + carry1 x13 + carry0 v30 + mul4 v28, nil, v1, v4, v5 + carry1 x12 + carry0 v27 + mul5 v29, nil, v1, v4, v5 + carry1 x14 + mul6 v30, nil, v1, v4, v5 + + // Finish up and store the result. + bfi x11, x13, #32, #32 + bfi x12, x14, #32, #32 + stp x11, x12, [x0], #16 + + // All done. + ret +ENDFUNC + +INTFUNC(mmul4) + // On entry, x0 points to the destination; x1 points to a packed + // operand U; x2 points to a packed operand X (the modulus); v2/v3 + // holds an expanded operand V; and v6/v7 holds an expanded operand M + // (the Montgomery factor -N^{-1} (mod B)). On exit, the destination + // is updated (to zero); v4/v5 hold an expanded factor Y = U V M (mod + // B); v28--v30 and x15 hold outgoing carries c0--c2 and cg; x0, x1, + // and x2 are each advanced by 16; v2, v3, and v8--v15 are preserved; + // and x11--x14, x16, x17 and the other SIMD registers are clobbered. + endprologue + + // Start by loading the operand words from memory. + ldr q0, [x1], #16 + ldr q1, [x2], #16 + + // Calculate the low half of W = A + U V, being careful to leave the + // carries in place. + mul0 v28, nil, v0, v2, v3 + mul1 v29, nil, v0, v2, v3 + carry0 v28, nil + mul2 v30, nil, v0, v2, v3 + carry1 x11 + carry0 v29 + mul3 v27, nil, v0, v2, v3 + b mmla4_common +ENDFUNC + +INTFUNC(mmla4) + // On entry, x0 points to the destination/accumulator A; x1 points to + // a packed operand U; x2 points to a packed operand X (the modulus); + // v2/v3 holds an expanded operand V; and v6/v7 holds an expanded + // operand M (the Montgomery factor -N^{-1} (mod B)). On exit, the + // accumulator is updated (to zero); v4/v5 hold an expanded factor Y + // = (A + U V) M (mod B); v28--v30 and x15 hold outgoing carries + // c0--c2 and cg; x0, x1, and x2 are each advanced by 16; v2, v3, v6, + // v7, and v8--v15 are preserved; and x11--x14, x16, x17 and the + // other SIMD registers are clobbered. + endprologue + + // Start by loading the operand words from memory. + ldr q28, [x0] + ldr q0, [x1], #16 + ldr q1, [x2], #16 + sprdacc v28, v29, v30, v27 + + // Calculate the low half of W = A + U V, being careful to leave the + // carries in place. + mul0 v28, t, v0, v2, v3 + mul1 v29, t, v0, v2, v3 + carry0 v28, nil + mul2 v30, t, v0, v2, v3 + carry1 x11 + carry0 v29 + mul3 v27, t, v0, v2, v3 +mmla4_common: + carry1 x13 + carry0 v30 + carry1 x12 + carry0 v27 + carry1 x14, nil + + // Piece the result together and ship it back. + bfi x11, x13, #32, #32 + bfi x12, x14, #32, #32 + mov v16.d[0], x11 + mov v16.d[1], x12 + + // Calculate the low half of the Montgomery factor Y = W M. + mul0 v18, nil, v16, v6, v7 + mul1 v19, nil, v16, v6, v7 + carry0 v18, nil + mul2 v20, nil, v16, v6, v7 + carry1 x11 + carry0 v19 + mul3 v21, nil, v16, v6, v7 + carry1 x13 + carry0 v20 + carry1 x12 + carry0 v21 + carry1 x14, nil + + // Piece the result together, ship it back, and expand. + bfi x11, x13, #32, #32 + bfi x12, x14, #32, #32 + mov v4.d[0], x11 + mov v4.d[1], x12 + expand v4, v5 + + // Build up the product X Y in the carry slots. + mul0 v28, t, v1, v4, v5 + mul1 v29, t, v1, v4, v5 + carry0 v28, nil + mul2 v30, t, v1, v4, v5 + carry1 nil + carry0 v29 + mul3 v27, t, v1, v4, v5 + carry1 nil + carry0 v30 + + // And complete the calculation. + mul4 v28, nil, v0, v2, v3, v1, v4, v5 + carry1 nil + carry0 v27 + mul5 v29, nil, v0, v2, v3, v1, v4, v5 + carry1 nil + mul6 v30, nil, v0, v2, v3, v1, v4, v5 + + // Finish up and store the result. + stp xzr, xzr, [x0], #16 + + // All done. + ret +ENDFUNC + +INTFUNC(mont4) + // On entry, x0 points to the destination/accumulator A; x2 points to + // a packed operand X (the modulus); and v6/v7 holds an expanded + // operand M (the Montgomery factor -N^{-1} (mod B)). On exit, the + // accumulator is updated (to zero); v4/v5 hold an expanded factor Y + // = A M (mod B); v28--v30 and x15 hold outgoing carries c0--c2 and + // cg; x0 and x2 are each advanced by 16; v6, v7, and v8--v15 are + // preserved; and x11--x14, x16, x17 and the other SIMD registers are + // clobbered. + endprologue + + // Start by loading the operand words from memory. + ldr q28, [x0] + ldr q1, [x2], #16 + + // Calculate Y = A M (mod B). + mul0 v18, nil, v28, v6, v7 + mul1 v19, nil, v28, v6, v7 + carry0 v18, nil + mul2 v20, nil, v28, v6, v7 + carry1 x11 + carry0 v19 + mul3 v21, nil, v28, v6, v7 + carry1 x13 + carry0 v20 + sprdacc v28, v29, v30, v27 + carry1 x12 + carry0 v21 + carry1 x14, nil + + // Piece the result together, ship it back, and expand. + bfi x11, x13, #32, #32 + bfi x12, x14, #32, #32 + mov v4.d[0], x11 + mov v4.d[1], x12 + expand v4, v5 + + // Calculate the actual result. Well, the carries, at least. + mul0 v28, t, v1, v4, v5 + mul1 v29, t, v1, v4, v5 + carry0 v28, nil + mul2 v30, t, v1, v4, v5 + carry1 nil + carry0 v29 + mul3 v27, t, v1, v4, v5 + carry1 nil + carry0 v30 + + // And complete the calculation. + mul4 v28, nil, v1, v4, v5 + carry1 nil + carry0 v27 + mul5 v29, nil, v1, v4, v5 + carry1 nil + mul6 v30, nil, v1, v4, v5 + + // Finish up and store the result. + stp xzr, xzr, [x0], #16 + + // All done. + ret +ENDFUNC + +///-------------------------------------------------------------------------- +/// Bulk multipliers. + +FUNC(mpx_umul4_arm64_simd) + // void mpx_umul4_arm64_simd(mpw *dv, const mpw *av, const mpw *avl, + // const mpw *bv, const mpw *bvl); + + // Establish the arguments and do initial setup. + // + // inner loop dv x0 + // inner loop av x2 + // outer loop dv x5 + // outer loop bv x3 + // av base x1 + // inner n x6 + // n base x7 + // outer n x4 + pushreg x29, x30 + setfp + endprologue + + // Prepare for the first iteration. + ldr q4, [x3], #16 // Y = bv[0] + movi v31.4s, #0 + sub x7, x2, x1 // = inner loop count base + // x0 // = dv for inner loop + // x1 // = av base + // x3 // = bv for outer loop + sub x4, x4, x3 // = outer loop count (decremented) + sub x6, x7, #16 // = inner loop count (decremented) + mov x2, x1 // = av for inner loop + add x5, x0, #16 // = dv for outer loop + expand v4, v5 // expand Y + bl mul4zc + cbz x6, 8f // all done? + + // Continue with the first iteration. +0: sub x6, x6, #16 + bl mul4 + cbnz x6, 0b + + // Write out the leftover carry. There can be no tail here. +8: bl carryprop + cbz x4, 9f + + // Set up for the next pass. +1: ldr q4, [x3], #16 // Y = bv[i] + mov x0, x5 // -> dv[i] + mov x2, x1 // -> av[0] + add x5, x5, #16 + sub x6, x7, #16 // = inner loop count (decremented) + sub x4, x4, #16 // outer loop count + expand v4, v5 // expand Y + bl mla4zc + cbz x6, 8f + + // Continue... +0: sub x6, x6, #16 + bl mla4 + cbnz x6, 0b + + // Finish off this pass. There was no tail on the previous pass, and + // there can be done on this pass. +8: bl carryprop + cbnz x4, 1b + + // All over. +9: popreg x29, x30 + ret +ENDFUNC + +FUNC(mpxmont_mul4_arm64_simd) + // void mpxmont_mul4_arm64_simd(mpw *dv, + // const mpw *av, const mpw *bv, + // const mpw *nv, size_t n, + // const mpw *mi); + + // Establish the arguments and do initial setup. + // + // inner loop dv x0 + // inner loop av x1 + // inner loop nv x2 + // nv base x3 + // base n x4 + // mi (x5) + // outer loop dv x5 + // outer loop bv x6 + // av base x7 + // inner n x8 + // outer n x9 + // c x10 + pushreg x29, x30 + setfp + endprologue + + // Set up the outer loop state and prepare for the first iteration. + ldr q2, [x2] // = V = bv[0] + ldr q6, [x5] // = M + movi v31.4s, #0 + // x0 // -> dv for inner loop + // x1 // -> av for inner loop + // x3 // -> nv base + // x4 // = n base + add x5, x0, #16 // -> dv + add x6, x2, #16 // -> bv + mov x2, x3 // -> nv[0] + mov x7, x1 // -> av base + sub x8, x4, #4 // = inner n (decremented) + sub x9, x4, #4 // = outer n (decremented) + expand v2, v3 // expand V + expand v6, v7 // expand M + bl mmul4 + cbz x8, 8f // done already? + + // Complete the first inner loop. +0: sub x8, x8, #4 + bl dmul4 + cbnz x8, 0b // done yet? + + // Still have carries left to propagate. Rather than store the tail + // end in memory, keep it in x10 for later. + bl carryprop + + // Embark on the next iteration. (There must be one. If n = 1 then + // we would have bailed above, to label 8. Similarly, the subsequent + // iterations can fall into the inner loop immediately.) +1: ldr q2, [x6], #16 // = Y = bv[i] + mov x0, x5 // -> dv[i] + mov x1, x7 // -> av[0] + mov x2, x3 // -> nv[0] + add x5, x5, #16 + sub x8, x4, #4 + sub x9, x9, #4 + expand v2, v3 + bl mmla4 + + // Complete the next inner loop. +0: sub x8, x8, #4 + bl dmla4 + cbnz x8, 0b + + // Still have carries left to propagate, and they overlap the + // previous iteration's final tail, so read that and add it. + add x15, x15, x10, lsl #16 + bl carryprop + + // Back again, maybe. + cbnz x9, 1b + + // All done, almost. + str w10, [x0], #4 + popreg x29, x30 + ret + + // First iteration was short. Write out the carries and we're done. + // (This could be folded into the main loop structure, but that would + // penalize small numbers more.) +8: bl carryprop + str w10, [x0], #4 + popreg x29, x30 + ret +ENDFUNC + +FUNC(mpxmont_redc4_arm64_simd) + // void mpxmont_redc4_arm64_simd(mpw *dv, mpw *dvl, const mpw *nv, + // size_t n, const mpw *mi); + + // Establish the arguments and do initial setup. + // + // inner loop dv x0 + // inner loop nv x2 + // blocks-of-4 count x6 + // tail count x7 + // mi (x4) + // outer loop dv x4 + // outer loop count x8 + // nv base x5 + // inner loop count x1 + // n x3 + // c x10 + // t0, t1 x11, x12 + + pushreg x29, x30 + setfp + endprologue + + // Set up the outer loop state and prepare for the first iteration. + ldr q6, [x4] // = M + movi v31.4s, #0 + // x0 // -> dv for inner loop + sub x6, x1, x0 // total dv bytes + sub x1, x3, #4 // inner loop counter + // x2 // -> nv for inner loop + // x3 // = n + add x4, x0, #16 // -> dv for outer loop + mov x5, x2 // -> nv base + sub x6, x6, x3, lsl #2 // dv carry range bytes + sub x8, x3, #4 // outer loop counter + sub x6, x6, #16 // dv steam-powered carry bytes + expand v6, v7 // expand M + and x7, x6, #15 // dv tail length in bytes + bic x6, x6, #15 // dv blocks-of-four length in bytes + + bl mont4 + cbz x1, 8f // done already? + +5: sub x1, x1, #4 + bl mla4 + cbnz x1, 5b // done yet? + + // Still have carries left to propagate. Adding the accumulator + // block into the carries is a little different this time, because + // all four accumulator limbs have to be squished into the three + // carry registers for `carryprop' to do its thing. +8: ldr q24, [x0] + sprdacc v24, v25, v26 + add v28.2d, v28.2d, v24.2d + add v29.2d, v29.2d, v25.2d + add v30.2d, v30.2d, v26.2d + bl carryprop + cbz x6, 7f + + // Propagate the first group of carries. + ldp x16, x17, [x0] + sub x1, x6, #16 + adds x16, x16, x10 + adcs x17, x17, xzr + stp x16, x17, [x0], #16 + cbz x1, 6f + + // Continue carry propagation until the end of the buffer. +0: ldp x16, x17, [x0] + sub x1, x1, #16 + adcs x16, x16, xzr + adcs x17, x17, xzr + stp x16, x17, [x0], #16 + cbnz x1, 0b + + // Deal with the tail end. Note that the actual destination length + // won't be an exacty number of blocks of four, so it's safe to just + // drop through here. +6: adc w10, wzr, wzr +7: ldr w16, [x0] + sub x1, x7, #4 + adds w16, w16, w10 + str w16, [x0], #4 + cbz x1, 8f +0: ldr w16, [x0] + sub x1, x1, #4 + adcs w16, w16, wzr + str w16, [x0], #4 + cbnz x1, 0b + + // All done for this iteration. Start the next. +8: cbz x8, 9f + mov x0, x4 + add x4, x4, #16 + sub x1, x3, #4 + mov x2, x5 + sub x8, x8, #4 + sub x6, x6, #16 + bl mont4 + b 5b + + // All over. +9: popreg x29, x30 + ret +ENDFUNC + +///-------------------------------------------------------------------------- +/// Testing and performance measurement. + +#ifdef TEST_MUL4 + +// dmul smul mmul mont +// z x0 x0 x0 x0 x0 +// c x3 x1 x1 x1 x1 +// y x4 -- -- x2 x2 +// u x1 x2 -- x3 -- +// x x2 x3 x2 x4 x3 +// vv v2/v3 x4 -- x5 -- +// yy v4/v5 x5 x3 x6 -- +// mm v6/v7 -- -- -- x4 +// n x5 x6 x4 x7 x5 +// cyv x6 x7 x5 stk0 x6 + +#define STKARG(i) sp, #16 + i + +.macro testprologue mode + pushreg x29, x30 + setfp + endprologue + movi v31.4s, #0 + + .ifeqs "\mode", "dmul" + ldr q2, [x4] + zip2 v3.8h, v2.8h, v31.8h // (v'_2, v''_2; v'_3, v''_3) + zip1 v2.8h, v2.8h, v31.8h // (v'_0, v''_0; v'_1, v''_1) + + ldr q4, [x5] + zip2 v5.8h, v4.8h, v31.8h // (y'_2, y''_2; y'_3, y''_3) + zip1 v4.8h, v4.8h, v31.8h // (y'_0, y''_0; y'_1, y''_1) + + mov x16, x1 + mov x1, x2 // -> u + mov x2, x3 // -> x + mov x3, x16 // -> c + + mov x5, x6 // = n + mov x6, x7 // -> cyv + .endif + + .ifeqs "\mode", "smul" + ldr q4, [x3] + zip2 v5.8h, v4.8h, v31.8h // (y'_2, y''_2; y'_3, y''_3) + zip1 v4.8h, v4.8h, v31.8h // (y'_0, y''_0; y'_1, y''_1) + + // x2 // -> x + mov x3, x1 // -> c + mov x6, x5 // -> cyv + mov x5, x4 // = n + .endif + + .ifeqs "\mode", "mmul" + ldr q2, [x5] + zip2 v3.8h, v2.8h, v31.8h // (v'_2, v''_2; v'_3, v''_3) + zip1 v2.8h, v2.8h, v31.8h // (v'_0, v''_0; v'_1, v''_1) + + ldr q6, [x6] + zip2 v7.8h, v6.8h, v31.8h // (y'_2, y''_2; y'_3, y''_3) + zip1 v6.8h, v6.8h, v31.8h // (y'_0, y''_0; y'_1, y''_1) + + mov x16, x1 + mov x1, x3 // -> u + mov x3, x16 // -> c + + mov x16, x2 + mov x2, x4 // -> x + mov x4, x16 // -> y + + mov x5, x7 // = n + ldr x6, [STKARG(0)] // -> cyv + .endif + + .ifeqs "\mode", "mont" + ldr q6, [x4] + zip2 v7.8h, v6.8h, v31.8h // (m'_2, m''_2; m'_3, m''_3) + zip1 v6.8h, v6.8h, v31.8h // (m'_0, m''_0; m'_1, m''_1) + + mov x4, x2 // -> y + mov x2, x3 // -> x + mov x3, x1 // -> c + + // x5 // = n + // x6 // -> cyv + .endif +.endm + +.macro testldcarry + ld1 {v28.2d-v30.2d}, [x3] + mov x15, #0 +.endm + +.macro testtop +0: sub x5, x5, #1 +.endm + +.macro testtail + cbnz x5, 0b +.endm + +.macro testcarryout + // More complicated than usual because we must mix the general- + // purpose carry back in. + lsr x15, x15, #16 + mov v0.d[0], x15 + mov v0.d[1], xzr + add v28.2d, v28.2d, v0.2d + st1 {v28.2d-v30.2d}, [x3] +.endm + +.macro testepilogue + popreg x29, x30 + ret +.endm + +FUNC(test_dmul4) + testprologue dmul + testldcarry + testtop + bl dmul4 + testtail + testcarryout + testepilogue +ENDFUNC + +FUNC(test_dmla4) + testprologue dmul + testldcarry + testtop + bl dmla4 + testtail + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mul4) + testprologue smul + testldcarry + testtop + bl mul4 + testtail + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mul4zc) + testprologue smul + testldcarry + testtop + bl mul4zc + testtail + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mla4) + testprologue smul + testldcarry + testtop + bl mla4 + testtail + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mla4zc) + testprologue smul + testldcarry + testtop + bl mla4zc + testtail + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mmul4) + testprologue mmul + testtop + bl mmul4 + testtail + stp q4, q5, [x4] + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mmla4) + testprologue mmul + testtop + bl mmla4 + testtail + stp q4, q5, [x4] + testcarryout + testepilogue +ENDFUNC + +FUNC(test_mont4) + testprologue mont + testtop + bl mont4 + testtail + stp q4, q5, [x4] + testcarryout + testepilogue +ENDFUNC + +#endif + +///----- That's all, folks -------------------------------------------------- diff --git a/math/mpx-mul4-test.c b/math/mpx-mul4-test.c index 414974bf..5325db0f 100644 --- a/math/mpx-mul4-test.c +++ b/math/mpx-mul4-test.c @@ -29,6 +29,10 @@ #include "config.h" +#ifdef ENABLE_ASM_DEBUG +# include "regdump.h" +#endif + #include #include #include @@ -57,6 +61,18 @@ static int cpu_features_p(void) { return (cpu_feature_p(CPUFEAT_X86_SSE2)); } static int cpu_features_p(void) { return (cpu_feature_p(CPUFEAT_X86_SSE2)); } #endif +#if CPUFAM_ARMEL +# define VARIANT _arm_neon +# define REPR_32 +static int cpu_features_p(void) { return (cpu_feature_p(CPUFEAT_ARM_NEON)); } +#endif + +#if CPUFAM_ARM64 +# define VARIANT _arm64_simd +# define REPR_32 +static int cpu_features_p(void) { return (1); } +#endif + #ifndef VARIANT # error "Unsupported CPU family." #endif @@ -107,7 +123,37 @@ TESTOPS(DECLSTUB) /*----- Conversion functions ----------------------------------------------*/ -#define DEFTYPE(ty, ld, st, nby) \ +static mp *combine_mpw(mp *d, const mpw *v, size_t n, unsigned off) +{ + size_t i; + unsigned o; + mp m, *t = d; + mpw w[1]; + + d = MP_ZERO; + for (i = 0, o = 0; i < n; i++, o += off) { + w[0] = v[i]; mp_build(&m, w, w + 1); + t = mp_lsl(t, &m, o); d = mp_add(d, d, t); + } + mp_drop(t); return (d); +} + +static mp *combine_mpd(mp *d, const mpd *v, size_t n, unsigned off) +{ + size_t i; + unsigned o; + mp m, *t = d; + mpw w[2]; + + d = MP_ZERO; + for (i = 0, o = 0; i < n; i++, o += off) { + w[0] = MPW(v[i]); w[1] = MPW(v[i] >> MPW_BITS); mp_build(&m, w, w + 2); + t = mp_lsl(t, &m, o); d = mp_add(d, d, t); + } + mp_drop(t); return (d); +} + +#define DEFTYPE(ty, ld, st, nby, combfn, off) \ \ static void cvt_##ty(const char *buf, dstr *d) \ { \ @@ -129,6 +175,7 @@ TESTOPS(DECLSTUB) dstr dd = DSTR_INIT; \ int i; \ const ty *x = (const ty *)d->buf; \ + mp *xx = combfn(MP_NEW, x->w, N(x->w), off); \ octet *p; \ \ dstr_ensure(&dd, N(x->w)*nby); p = (octet *)dd.buf; \ @@ -136,22 +183,26 @@ TESTOPS(DECLSTUB) dd.len = N(x->w)*nby; \ type_hex.dump(&dd, fp); \ dstr_destroy(&dd); \ + \ + fputs(" = 0x", fp); mp_writefile(xx, fp, 16); \ + fputs(" = ", fp); mp_writefile(xx, fp, 10); \ + MP_DROP(xx); \ } \ \ static int eq_##ty(const ty *x, const ty *y) \ { \ - int i; \ - \ - for (i = 0; i < N(x->w); i++) \ - if (x->w[i] != y->w[i]) return (0); \ - return (1); \ + mp *xx = combfn(MP_NEW, x->w, N(x->w), off), \ + *yy = combfn(MP_NEW, y->w, N(y->w), off); \ + int rc = MP_EQ(xx, yy); \ + MP_DROP(xx); MP_DROP(yy); \ + return (rc); \ } \ \ static const struct test_type type_##ty = { cvt_##ty, dump_##ty }; -DEFTYPE(p128, LDW, STW, NWBY) -DEFTYPE(x128, LDW, STW, NWBY) -DEFTYPE(carry, LDD, STD, NDBY) +DEFTYPE(p128, LDW, STW, NWBY, combine_mpw, MPW_BITS) +DEFTYPE(x128, LDW, STW, NWBY, combine_mpw, MPW_BITS/2) +DEFTYPE(carry, LDD, STD, NDBY, combine_mpd, MPW_BITS/2) /*----- Test functions ----------------------------------------------------*/ @@ -284,6 +335,9 @@ static test_chunk tests[] = { int main(int argc, char *argv[]) { sub_init(); +#ifdef ENABLE_ASM_DEBUG + regdump_init(); +#endif if (!cpu_features_p()) { fprintf(stderr, "required cpu feature not available\n"); exit(77); } test_run(argc, argv, tests, SRCDIR "/t/mpx-mul4"); diff --git a/math/mpx-mul4-x86-sse2.S b/math/mpx-mul4-x86-sse2.S index 904c0d0a..916adef9 100644 --- a/math/mpx-mul4-x86-sse2.S +++ b/math/mpx-mul4-x86-sse2.S @@ -40,11 +40,11 @@ /// construct more general variable-length multipliers. /// /// The basic trick is the same throughout. In an operand-scanning -/// multiplication, the inner multiplication loop multiplies a -/// multiple-precision operand by a single precision factor, and adds the -/// result, appropriately shifted, to the result. A `finely integrated -/// operand scanning' implementation of Montgomery multiplication also adds -/// the product of a single-precision `Montgomery factor' and the modulus, +/// multiplication, the inner multiplication loop multiplies a multiple- +/// precision operand by a single precision factor, and adds the result, +/// appropriately shifted, to the result. A `finely integrated operand +/// scanning' implementation of Montgomery multiplication also adds the +/// product of a single-precision `Montgomery factor' and the modulus, /// calculated in the same pass. The more common `coarsely integrated /// operand scanning' alternates main multiplication and Montgomery passes, /// which requires additional carry propagation. @@ -70,23 +70,64 @@ /// many products together before we must deal with carrying; it also allows /// for some calculations to be performed on the above expanded form. /// +/// We maintain four `carry' registers XMM4--XMM7 accumulating intermediate +/// results. The registers' precise roles rotate during the computation; we +/// name them `c0', `c1', `c2', and `c3'. Each carry register holds two +/// 64-bit halves: the register c0, for example, holds c'_0 (low half) and +/// c''_0 (high half), and represents the value c_0 = c'_0 + c''_0 b; the +/// carry registers collectively represent the value c_0 + c_1 B + c_2 B^2 + +/// c_3 B^3. The `pmuluqd' instruction acting on a scalar operand (broadcast +/// across all lanes of its vector) and an operand in the expanded form above +/// produces a result which can be added directly to the appropriate carry +/// register. Following a pass of four multiplications, we perform some +/// limited carry propagation: let t = c''_0 mod B, and let d = c'_0 + t b; +/// then we output z = d mod B, add (floor(d/B), floor(c''_0/B)) to c1, and +/// cycle the carry registers around, so that c1 becomes c0, and the old +/// (implicitly) zeroed c0 becomes c3. +/// /// On 32-bit x86, we are register starved: the expanded operands are kept in -/// memory, typically in warm L1 cache. +/// memory, typically in warm L1 cache. The packed operands are read from +/// memory into working registers XMM0--XMM3 and processed immediately. +/// The following conventional argument names and locations are used +/// throughout. +/// +/// Arg Format Location Notes +/// +/// U packed [EAX] +/// X packed [EBX] In Montgomery multiplication, X = N +/// V expanded [ECX] +/// Y expanded [EDX] In Montgomery multiplication, Y = (A + U V) M +/// M expanded [ESI] -N^{-1} (mod B^4) +/// N Modulus, for Montgomery multiplication +/// A packed [EDI] Destination/accumulator +/// C carry XMM4--XMM7 +/// +/// The calculation is some variant of +/// +/// A' + C' B^4 <- U V + X Y + A + C +/// +/// The low-level functions fit into a fairly traditional (finely-integrated) +/// operand scanning loop over operand pairs (U, X) (indexed by j) and (V, Y) +/// (indexed by i). /// -/// We maintain four `carry' registers accumulating intermediate results. -/// The registers' precise roles rotate during the computation; we name them -/// `c0', `c1', `c2', and `c3'. Each carry register holds two 64-bit halves: -/// the register c0, for example, holds c'_0 (low half) and c''_0 (high -/// half), and represents the value c_0 = c'_0 + c''_0 b; the carry registers -/// collectively represent the value c_0 + c_1 B + c_2 B^2 + c_3 B^3. The -/// `pmuluqd' instruction acting on a scalar operand (broadcast across all -/// lanes of its vector) and an operand in the expanded form above produces a -/// result which can be added directly to the appropriate carry register. -/// Following a pass of four multiplications, we perform some limited carry -/// propagation: let t = c''_0 mod B, and let d = c'_0 + t b; then we output -/// z = d mod B, add (floor(d/B), floor(c''_0/B)) to c1, and cycle the carry -/// registers around, so that c1 becomes c0, and the old c0 is (implicitly) -/// zeroed becomes c3. +/// The variants are as follows. +/// +/// Function Variant Use i j +/// +/// mmul4 A = C = 0 Montgomery 0 0 +/// dmul4 A = 0 Montgomery 0 + +/// mmla4 C = 0 Montgomery + 0 +/// dmla4 exactly as shown Montgomery + + +/// mont4 U = V = C = 0 Montgomery any 0 +/// +/// mul4zc U = V = A = C = 0 Plain 0 0 +/// mul4 U = V = A = 0 Plain 0 + +/// mla4zc U = V = C = 0 Plain + 0 +/// mla4 U = V = 0 Plain + + +/// +/// The `mmul4' and `mmla4' functions are also responsible for calculating +/// the Montgomery reduction factor Y = (A + U V) M used by the rest of the +/// inner loop. ///-------------------------------------------------------------------------- /// Macro definitions. @@ -316,7 +357,6 @@ INTFUNC(carryprop) propout [edi + 8], xmm6, nil endprop [edi + 12], xmm6, xmm4 ret - ENDFUNC INTFUNC(dmul4) @@ -348,7 +388,6 @@ INTFUNC(dmul4) propout [edi + 12], xmm7, xmm4 ret - ENDFUNC INTFUNC(dmla4) @@ -384,7 +423,6 @@ INTFUNC(dmla4) propout [edi + 12], xmm7, xmm4 ret - ENDFUNC INTFUNC(mul4zc) @@ -410,7 +448,6 @@ INTFUNC(mul4zc) propout [edi + 12], xmm7, xmm4 ret - ENDFUNC INTFUNC(mul4) @@ -438,7 +475,6 @@ INTFUNC(mul4) propout [edi + 12], xmm7, xmm4 ret - ENDFUNC INTFUNC(mla4zc) @@ -470,7 +506,6 @@ INTFUNC(mla4zc) propout [edi + 12], xmm7, xmm4 ret - ENDFUNC INTFUNC(mla4) @@ -501,7 +536,6 @@ INTFUNC(mla4) propout [edi + 12], xmm7, xmm4 ret - ENDFUNC INTFUNC(mmul4) @@ -523,7 +557,6 @@ INTFUNC(mmul4) mulcore [eax + 0], ecx, xmm4, xmm5, xmm6, xmm7 propout [edi + 0], xmm4, xmm5 jmp 5f - ENDFUNC INTFUNC(mmla4) @@ -561,9 +594,9 @@ INTFUNC(mmla4) mulacc [eax + 12], ecx, xmm7, xmm4, xmm5, xmm6, t propout [edi + 12], xmm7, xmm4 - movdqa [esp + 0], xmm4 - movdqa [esp + 16], xmm5 - movdqa [esp + 32], xmm6 + movdqa [SP + 0], xmm4 + movdqa [SP + 16], xmm5 + movdqa [SP + 32], xmm6 // Calculate Y = W M. mulcore [edi + 0], esi, xmm4, xmm5, xmm6, xmm7 @@ -606,14 +639,13 @@ INTFUNC(mmla4) propout [edi + 12], xmm7, xmm4 // Add add on the carry we calculated earlier. - paddq xmm4, [esp + 0] - paddq xmm5, [esp + 16] - paddq xmm6, [esp + 32] + paddq xmm4, [SP + 0] + paddq xmm5, [SP + 16] + paddq xmm6, [SP + 32] // And, with that, we're done. stfree 48 + 12 ret - ENDFUNC INTFUNC(mont4) @@ -670,7 +702,6 @@ INTFUNC(mont4) // And, with that, we're done. ret - ENDFUNC ///-------------------------------------------------------------------------- @@ -688,40 +719,40 @@ FUNC(mpx_umul4_x86_sse2) // void mpx_umul4_x86_sse2(mpw *dv, const mpw *av, const mpw *avl, // const mpw *bv, const mpw *bvl); - // Build a stack frame. Arguments will be relative to EBP, as + // Build a stack frame. Arguments will be relative to BP, as // follows. // - // ebp + 20 dv - // ebp + 24 av - // ebp + 28 avl - // ebp + 32 bv - // ebp + 36 bvl + // BP + 20 dv + // BP + 24 av + // BP + 28 avl + // BP + 32 bv + // BP + 36 bvl // - // Locals are relative to ESP, as follows. + // Locals are relative to SP, as follows. // - // esp + 0 expanded Y (32 bytes) - // esp + 32 (top of locals) - pushreg ebp + // SP + 0 expanded Y (32 bytes) + // SP + 32 (top of locals) + pushreg BP pushreg ebx pushreg esi pushreg edi setfp - and esp, ~15 - sub esp, 32 + stalloc 32 + and SP, ~15 endprologue // Prepare for the first iteration. - mov esi, [ebp + 32] // -> bv[0] + mov esi, [BP + 32] // -> bv[0] pxor xmm7, xmm7 movdqu xmm0, [esi] // bv[0] - mov edi, [ebp + 20] // -> dv[0] + mov edi, [BP + 20] // -> dv[0] mov ecx, edi // outer loop dv cursor expand xmm7, xmm0, xmm1 - mov ebx, [ebp + 24] // -> av[0] - mov eax, [ebp + 28] // -> av[m] = av limit - mov edx, esp // -> expanded Y = bv[0] - movdqa [esp + 0], xmm0 // bv[0] expanded low - movdqa [esp + 16], xmm1 // bv[0] expanded high + mov ebx, [BP + 24] // -> av[0] + mov eax, [BP + 28] // -> av[m] = av limit + mov edx, SP // -> expanded Y = bv[0] + movdqa [SP + 0], xmm0 // bv[0] expanded low + movdqa [SP + 16], xmm1 // bv[0] expanded high call mul4zc add ebx, 16 add edi, 16 @@ -740,7 +771,7 @@ FUNC(mpx_umul4_x86_sse2) // Write out the leftover carry. There can be no tail here. 8: call carryprop - cmp esi, [ebp + 36] // more passes to do? + cmp esi, [BP + 36] // more passes to do? jae 9f .p2align 4 @@ -749,9 +780,9 @@ FUNC(mpx_umul4_x86_sse2) mov edi, ecx // -> dv[i] pxor xmm7, xmm7 expand xmm7, xmm0, xmm1 - mov ebx, [ebp + 24] // -> av[0] - movdqa [esp + 0], xmm0 // bv[i] expanded low - movdqa [esp + 16], xmm1 // bv[i] expanded high + mov ebx, [BP + 24] // -> av[0] + movdqa [SP + 0], xmm0 // bv[i] expanded low + movdqa [SP + 16], xmm1 // bv[i] expanded high call mla4zc add edi, 16 add ebx, 16 @@ -771,7 +802,7 @@ FUNC(mpx_umul4_x86_sse2) // Finish off this pass. There was no tail on the previous pass, and // there can be none on this pass. 8: call carryprop - cmp esi, [ebp + 36] + cmp esi, [BP + 36] jb 1b // All over. @@ -779,9 +810,8 @@ FUNC(mpx_umul4_x86_sse2) pop edi pop esi pop ebx - pop ebp + pop BP ret - ENDFUNC FUNC(mpxmont_mul4_x86_avx) @@ -796,69 +826,69 @@ FUNC(mpxmont_mul4_x86_sse2) // void mpxmont_mul4_x86_sse2(mpw *dv, const mpw *av, const mpw *bv, // const mpw *nv, size_t n, const mpw *mi); - // Build a stack frame. Arguments will be relative to EBP, as + // Build a stack frame. Arguments will be relative to BP, as // follows. // - // ebp + 20 dv - // ebp + 24 av - // ebp + 28 bv - // ebp + 32 nv - // ebp + 36 n (nonzero multiple of 4) - // ebp + 40 mi + // BP + 20 dv + // BP + 24 av + // BP + 28 bv + // BP + 32 nv + // BP + 36 n (nonzero multiple of 4) + // BP + 40 mi // - // Locals are relative to ESP, which 16-byte aligned, as follows. + // Locals are relative to SP, which 16-byte aligned, as follows. // - // esp + 0 expanded V (32 bytes) - // esp + 32 expanded M (32 bytes) - // esp + 64 expanded Y (32 bytes) - // esp + 96 outer loop dv - // esp + 100 outer loop bv - // esp + 104 av limit (mostly in ESI) - // esp + 108 bv limit - // esp + 112 (top of locals) - pushreg ebp + // SP + 0 expanded V (32 bytes) + // SP + 32 expanded M (32 bytes) + // SP + 64 expanded Y (32 bytes) + // SP + 96 outer loop dv + // SP + 100 outer loop bv + // SP + 104 av limit (mostly in ESI) + // SP + 108 bv limit + // SP + 112 (top of locals) + pushreg BP pushreg ebx pushreg esi pushreg edi setfp - and esp, ~15 - sub esp, 112 + stalloc 112 + and SP, ~15 endprologue // Establish the expanded operands. pxor xmm7, xmm7 - mov ecx, [ebp + 28] // -> bv - mov edx, [ebp + 40] // -> mi + mov ecx, [BP + 28] // -> bv + mov edx, [BP + 40] // -> mi movdqu xmm0, [ecx] // bv[0] movdqu xmm2, [edx] // mi expand xmm7, xmm0, xmm1, xmm2, xmm3 - movdqa [esp + 0], xmm0 // bv[0] expanded low - movdqa [esp + 16], xmm1 // bv[0] expanded high - movdqa [esp + 32], xmm2 // mi expanded low - movdqa [esp + 48], xmm3 // mi expanded high + movdqa [SP + 0], xmm0 // bv[0] expanded low + movdqa [SP + 16], xmm1 // bv[0] expanded high + movdqa [SP + 32], xmm2 // mi expanded low + movdqa [SP + 48], xmm3 // mi expanded high // Set up the outer loop state and prepare for the first iteration. - mov edx, [ebp + 36] // n - mov eax, [ebp + 24] // -> U = av[0] - mov ebx, [ebp + 32] // -> X = nv[0] - mov edi, [ebp + 20] // -> Z = dv[0] - mov [esp + 100], ecx + mov edx, [BP + 36] // n + mov eax, [BP + 24] // -> U = av[0] + mov ebx, [BP + 32] // -> X = nv[0] + mov edi, [BP + 20] // -> Z = dv[0] + mov [SP + 100], ecx lea ecx, [ecx + 4*edx] // -> bv[n/4] = bv limit lea edx, [eax + 4*edx] // -> av[n/4] = av limit - mov [esp + 96], edi - mov [esp + 104], edx - mov [esp + 108], ecx - lea ecx, [esp + 0] // -> expanded V = bv[0] - lea esi, [esp + 32] // -> expanded M = mi - lea edx, [esp + 64] // -> space for Y + mov [SP + 96], edi + mov [SP + 104], edx + mov [SP + 108], ecx + lea ecx, [SP + 0] // -> expanded V = bv[0] + lea esi, [SP + 32] // -> expanded M = mi + lea edx, [SP + 64] // -> space for Y call mmul4 - mov esi, [esp + 104] // recover av limit + mov esi, [SP + 104] // recover av limit add edi, 16 add eax, 16 add ebx, 16 cmp eax, esi // done already? jae 8f - mov [esp + 96], edi + mov [SP + 96], edi .p2align 4 // Complete the first inner loop. @@ -877,26 +907,26 @@ FUNC(mpxmont_mul4_x86_sse2) // Embark on the next iteration. (There must be one. If n = 1, then // we would have bailed above, to label 8. Similarly, the subsequent // iterations can fall into the inner loop immediately.) -1: mov eax, [esp + 100] // -> bv[i - 1] - mov edi, [esp + 96] // -> Z = dv[i] +1: mov eax, [SP + 100] // -> bv[i - 1] + mov edi, [SP + 96] // -> Z = dv[i] add eax, 16 // -> bv[i] pxor xmm7, xmm7 - mov [esp + 100], eax - cmp eax, [esp + 108] // done yet? + mov [SP + 100], eax + cmp eax, [SP + 108] // done yet? jae 9f movdqu xmm0, [eax] // bv[i] - mov ebx, [ebp + 32] // -> X = nv[0] - lea esi, [esp + 32] // -> expanded M = mi - mov eax, [ebp + 24] // -> U = av[0] + mov ebx, [BP + 32] // -> X = nv[0] + lea esi, [SP + 32] // -> expanded M = mi + mov eax, [BP + 24] // -> U = av[0] expand xmm7, xmm0, xmm1 - movdqa [esp + 0], xmm0 // bv[i] expanded low - movdqa [esp + 16], xmm1 // bv[i] expanded high + movdqa [SP + 0], xmm0 // bv[i] expanded low + movdqa [SP + 16], xmm1 // bv[i] expanded high call mmla4 - mov esi, [esp + 104] // recover av limit + mov esi, [SP + 104] // recover av limit add edi, 16 add eax, 16 add ebx, 16 - mov [esp + 96], edi + mov [SP + 96], edi .p2align 4 // Complete the next inner loop. @@ -928,9 +958,8 @@ FUNC(mpxmont_mul4_x86_sse2) popreg edi popreg esi popreg ebx - popreg ebp + popreg BP ret - ENDFUNC FUNC(mpxmont_redc4_x86_avx) @@ -945,55 +974,55 @@ FUNC(mpxmont_redc4_x86_sse2) // void mpxmont_redc4_x86_sse2(mpw *dv, mpw *dvl, const mpw *nv, // size_t n, const mpw *mi); - // Build a stack frame. Arguments will be relative to EBP, as + // Build a stack frame. Arguments will be relative to BP, as // follows. // - // ebp + 20 dv - // ebp + 24 dvl - // ebp + 28 nv - // ebp + 32 n (nonzero multiple of 4) - // ebp + 36 mi + // BP + 20 dv + // BP + 24 dvl + // BP + 28 nv + // BP + 32 n (nonzero multiple of 4) + // BP + 36 mi // - // Locals are relative to ESP, as follows. + // Locals are relative to SP, as follows. // - // esp + 0 outer loop dv - // esp + 4 outer dv limit - // esp + 8 blocks-of-4 dv limit - // esp + 12 expanded M (32 bytes) - // esp + 44 expanded Y (32 bytes) - // esp + 76 (top of locals) - pushreg ebp + // SP + 0 outer loop dv + // SP + 4 outer dv limit + // SP + 8 blocks-of-4 dv limit + // SP + 12 expanded M (32 bytes) + // SP + 44 expanded Y (32 bytes) + // SP + 76 (top of locals) + pushreg BP pushreg ebx pushreg esi pushreg edi setfp - and esp, ~15 - sub esp, 76 + and SP, ~15 + stalloc 76 endprologue // Establish the expanded operands and the blocks-of-4 dv limit. - mov edi, [ebp + 20] // -> Z = dv[0] + mov edi, [BP + 20] // -> Z = dv[0] pxor xmm7, xmm7 - mov eax, [ebp + 24] // -> dv[n] = dv limit + mov eax, [BP + 24] // -> dv[n] = dv limit sub eax, edi // length of dv in bytes - mov edx, [ebp + 36] // -> mi + mov edx, [BP + 36] // -> mi movdqu xmm0, [edx] // mi and eax, ~15 // mask off the tail end expand xmm7, xmm0, xmm1 add eax, edi // find limit - movdqa [esp + 12], xmm0 // mi expanded low - movdqa [esp + 28], xmm1 // mi expanded high - mov [esp + 8], eax + movdqa [SP + 12], xmm0 // mi expanded low + movdqa [SP + 28], xmm1 // mi expanded high + mov [SP + 8], eax // Set up the outer loop state and prepare for the first iteration. - mov ecx, [ebp + 32] // n - mov ebx, [ebp + 28] // -> X = nv[0] + mov ecx, [BP + 32] // n + mov ebx, [BP + 28] // -> X = nv[0] lea edx, [edi + 4*ecx] // -> dv[n/4] = outer dv limit lea ecx, [ebx + 4*ecx] // -> nv[n/4] = nv limit - mov [esp + 0], edi - mov [esp + 4], edx - lea esi, [esp + 12] // -> expanded M = mi - lea edx, [esp + 44] // -> space for Y + mov [SP + 0], edi + mov [SP + 4], edx + lea esi, [SP + 12] // -> expanded M = mi + lea edx, [SP + 44] // -> space for Y call mont4 add ebx, 16 add edi, 16 @@ -1010,8 +1039,8 @@ FUNC(mpxmont_redc4_x86_sse2) // Still have carries left to propagate. 8: carryadd - mov esi, [esp + 8] // -> dv blocks limit - mov edx, [ebp + 24] // dv limit + mov esi, [SP + 8] // -> dv blocks limit + mov edx, [BP + 24] // dv limit psllq xmm7, 16 pslldq xmm7, 8 paddq xmm6, xmm7 @@ -1025,33 +1054,33 @@ FUNC(mpxmont_redc4_x86_sse2) // Continue carry propagation until the end of the buffer. 0: add [edi], eax mov eax, 0 // preserves flags - adcd [edi + 4], 0 - adcd [edi + 8], 0 - adcd [edi + 12], 0 + adc dword ptr [edi + 4], 0 + adc dword ptr [edi + 8], 0 + adc dword ptr [edi + 12], 0 adc eax, 0 add edi, 16 cmp edi, esi jb 0b - // Deal with the tail end. + // Deal with the tail end. Note that the actual destination length + // won't be an exact number of blocks of four, so it's safe to just + // drop through here. 7: add [edi], eax - mov eax, 0 // preserves flags + mov eax, 0 add edi, 4 adc eax, 0 cmp edi, edx jb 7b - // All done for this iteration. Start the next. (This must have at - // least one follow-on iteration, or we'd not have started this outer - // loop.) -8: mov edi, [esp + 0] // -> dv[i - 1] - mov ebx, [ebp + 28] // -> X = nv[0] - lea edx, [esp + 44] // -> space for Y - lea esi, [esp + 12] // -> expanded M = mi + // All done for this iteration. Start the next. +8: mov edi, [SP + 0] // -> dv[i - 1] + mov ebx, [BP + 28] // -> X = nv[0] + lea edx, [SP + 44] // -> space for Y + lea esi, [SP + 12] // -> expanded M = mi add edi, 16 // -> Z = dv[i] - cmp edi, [esp + 4] // all done yet? + cmp edi, [SP + 4] // all done yet? jae 9f - mov [esp + 0], edi + mov [SP + 0], edi call mont4 add edi, 16 add ebx, 16 @@ -1062,9 +1091,8 @@ FUNC(mpxmont_redc4_x86_sse2) popreg edi popreg esi popreg ebx - popreg ebp + popreg BP ret - ENDFUNC ///-------------------------------------------------------------------------- @@ -1091,22 +1119,22 @@ ENDFUNC .endm .macro testprologue n - pushreg ebp + pushreg BP pushreg ebx pushreg esi pushreg edi setfp - and esp, ~15 - sub esp, 3*32 + 4*4 + stalloc 3*32 + 4*4 + and SP, ~15 endprologue mov eax, \n - mov [esp + 104], eax + mov [SP + 104], eax // vars: - // esp + 0 = v expanded - // esp + 32 = y expanded - // esp + 64 = ? expanded - // esp + 96 = cycles - // esp + 104 = count + // SP + 0 = v expanded + // SP + 32 = y expanded + // SP + 64 = ? expanded + // SP + 96 = cycles + // SP + 104 = count .endm .macro testepilogue @@ -1114,7 +1142,7 @@ ENDFUNC popreg edi popreg esi popreg ebx - popreg ebp + popreg BP ret .endm @@ -1131,15 +1159,15 @@ ENDFUNC mov ecx, \v movdqu xmm0, [ecx] expand xmm7, xmm0, xmm1 - movdqa [esp + 0], xmm0 - movdqa [esp + 16], xmm1 + movdqa [SP + 0], xmm0 + movdqa [SP + 16], xmm1 .endif .ifnes "\y", "nil" mov edx, \y movdqu xmm2, [edx] expand xmm7, xmm2, xmm3 - movdqa [esp + 32], xmm2 - movdqa [esp + 48], xmm3 + movdqa [SP + 32], xmm2 + movdqa [SP + 48], xmm3 .endif .endm @@ -1147,25 +1175,25 @@ ENDFUNC .p2align 4 0: .ifnes "\u", "nil" - lea ecx, [esp + 0] + lea ecx, [SP + 0] .endif mov ebx, \x .ifeqs "\mode", "mont" - lea esi, [esp + 32] + lea esi, [SP + 32] .endif - cysetup esp + 96 + cysetup SP + 96 .ifnes "\u", "nil" mov eax, \u .endif .ifeqs "\mode", "mont" - lea edx, [esp + 64] + lea edx, [SP + 64] .else - lea edx, [esp + 32] + lea edx, [SP + 32] .endif .endm .macro testtail cyv - cystore esp + 96, \cyv, esp + 104 + cystore SP + 96, \cyv, SP + 104 jnz 0b .endm @@ -1177,122 +1205,128 @@ ENDFUNC .endm FUNC(test_dmul4) - testprologue [ebp + 44] - testldcarry [ebp + 24] - testexpand [ebp + 36], [ebp + 40] - mov edi, [ebp + 20] - testtop [ebp + 28], [ebp + 32] + testprologue [BP + 44] + testldcarry [BP + 24] + testexpand [BP + 36], [BP + 40] + mov edi, [BP + 20] + testtop [BP + 28], [BP + 32] call dmul4 - testtail [ebp + 48] - testcarryout [ebp + 24] + testtail [BP + 48] + testcarryout [BP + 24] testepilogue ENDFUNC FUNC(test_dmla4) - testprologue [ebp + 44] - testldcarry [ebp + 24] - testexpand [ebp + 36], [ebp + 40] - mov edi, [ebp + 20] - testtop [ebp + 28], [ebp + 32] + testprologue [BP + 44] + testldcarry [BP + 24] + testexpand [BP + 36], [BP + 40] + mov edi, [BP + 20] + testtop [BP + 28], [BP + 32] call dmla4 - testtail [ebp + 48] - testcarryout [ebp + 24] + testtail [BP + 48] + testcarryout [BP + 24] testepilogue ENDFUNC FUNC(test_mul4) - testprologue [ebp + 36] - testldcarry [ebp + 24] - testexpand nil, [ebp + 32] - mov edi, [ebp + 20] - testtop nil, [ebp + 28] + testprologue [BP + 36] + testldcarry [BP + 24] + testexpand nil, [BP + 32] + mov edi, [BP + 20] + testtop nil, [BP + 28] call mul4 - testtail [ebp + 40] - testcarryout [ebp + 24] + testtail [BP + 40] + testcarryout [BP + 24] testepilogue ENDFUNC FUNC(test_mul4zc) - testprologue [ebp + 36] - testldcarry [ebp + 24] - testexpand nil, [ebp + 32] - mov edi, [ebp + 20] - testtop nil, [ebp + 28] + testprologue [BP + 36] + testldcarry [BP + 24] + testexpand nil, [BP + 32] + mov edi, [BP + 20] + testtop nil, [BP + 28] call mul4zc - testtail [ebp + 40] - testcarryout [ebp + 24] + testtail [BP + 40] + testcarryout [BP + 24] testepilogue ENDFUNC FUNC(test_mla4) - testprologue [ebp + 36] - testldcarry [ebp + 24] - testexpand nil, [ebp + 32] - mov edi, [ebp + 20] - testtop nil, [ebp + 28] + testprologue [BP + 36] + testldcarry [BP + 24] + testexpand nil, [BP + 32] + mov edi, [BP + 20] + testtop nil, [BP + 28] call mla4 - testtail [ebp + 40] - testcarryout [ebp + 24] + testtail [BP + 40] + testcarryout [BP + 24] testepilogue ENDFUNC FUNC(test_mla4zc) - testprologue [ebp + 36] - testldcarry [ebp + 24] - testexpand nil, [ebp + 32] - mov edi, [ebp + 20] - testtop nil, [ebp + 28] + testprologue [BP + 36] + testldcarry [BP + 24] + testexpand nil, [BP + 32] + mov edi, [BP + 20] + testtop nil, [BP + 28] call mla4zc - testtail [ebp + 40] - testcarryout [ebp + 24] + testtail [BP + 40] + testcarryout [BP + 24] testepilogue ENDFUNC FUNC(test_mmul4) - testprologue [ebp + 48] - testexpand [ebp + 40], [ebp + 44] - mov edi, [ebp + 20] - testtop [ebp + 32], [ebp + 36], mont + testprologue [BP + 48] + testexpand [BP + 40], [BP + 44] + mov edi, [BP + 20] + testtop [BP + 32], [BP + 36], mont call mmul4 - testtail [ebp + 52] - mov edi, [ebp + 28] - movdqa xmm0, [esp + 64] - movdqa xmm1, [esp + 80] + testtail [BP + 52] + mov edi, [BP + 28] + movdqa xmm0, [SP + 64] + movdqa xmm1, [SP + 80] + pshufd xmm0, xmm0, SHUF(0, 2, 1, 3) + pshufd xmm1, xmm1, SHUF(0, 2, 1, 3) movdqu [edi], xmm0 movdqu [edi + 16], xmm1 - testcarryout [ebp + 24] + testcarryout [BP + 24] testepilogue ENDFUNC FUNC(test_mmla4) - testprologue [ebp + 48] - testexpand [ebp + 40], [ebp + 44] - mov edi, [ebp + 20] - testtop [ebp + 32], [ebp + 36], mont + testprologue [BP + 48] + testexpand [BP + 40], [BP + 44] + mov edi, [BP + 20] + testtop [BP + 32], [BP + 36], mont call mmla4 - testtail [ebp + 52] - mov edi, [ebp + 28] - movdqa xmm0, [esp + 64] - movdqa xmm1, [esp + 80] + testtail [BP + 52] + mov edi, [BP + 28] + movdqa xmm0, [SP + 64] + movdqa xmm1, [SP + 80] + pshufd xmm0, xmm0, SHUF(0, 2, 1, 3) + pshufd xmm1, xmm1, SHUF(0, 2, 1, 3) movdqu [edi], xmm0 movdqu [edi + 16], xmm1 - testcarryout [ebp + 24] + testcarryout [BP + 24] testepilogue ENDFUNC FUNC(test_mont4) - testprologue [ebp + 40] - testexpand nil, [ebp + 36] - mov edi, [ebp + 20] - testtop nil, [ebp + 32], mont + testprologue [BP + 40] + testexpand nil, [BP + 36] + mov edi, [BP + 20] + testtop nil, [BP + 32], mont call mont4 - testtail [ebp + 44] - mov edi, [ebp + 28] - movdqa xmm0, [esp + 64] - movdqa xmm1, [esp + 80] + testtail [BP + 44] + mov edi, [BP + 28] + movdqa xmm0, [SP + 64] + movdqa xmm1, [SP + 80] + pshufd xmm0, xmm0, SHUF(0, 2, 1, 3) + pshufd xmm1, xmm1, SHUF(0, 2, 1, 3) movdqu [edi], xmm0 movdqu [edi + 16], xmm1 - testcarryout [ebp + 24] + testcarryout [BP + 24] testepilogue ENDFUNC diff --git a/math/mpx.c b/math/mpx.c index 42948457..ac826883 100644 --- a/math/mpx.c +++ b/math/mpx.c @@ -818,7 +818,7 @@ void mpx_usub(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, void mpx_usubn(mpw *dv, mpw *dvl, mpw n) { MPX_USUBN(dv, dvl, n); } -/* --- @mpx_uaddnlsl@ --- * +/* --- @mpx_usubnlsl@ --- * * * Arguments: @mpw *dv, *dvl@ = destination and first argument vector * @mpw a@ = second argument @@ -931,6 +931,14 @@ static void simple_umul(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, MAYBE_UMUL4(amd64_avx) #endif +#if CPUFAM_ARMEL + MAYBE_UMUL4(arm_neon) +#endif + +#if CPUFAM_ARM64 + MAYBE_UMUL4(arm64_simd) +#endif + static mpx_umul__functype *pick_umul(void) { #if CPUFAM_X86 @@ -945,6 +953,14 @@ static mpx_umul__functype *pick_umul(void) DISPATCH_PICK_COND(mpx_umul, maybe_umul4_amd64_sse2, cpu_feature_p(CPUFEAT_X86_SSE2)); #endif +#if CPUFAM_ARMEL + DISPATCH_PICK_COND(mpx_umul, maybe_umul4_arm_neon, + cpu_feature_p(CPUFEAT_ARM_NEON)); +#endif +#if CPUFAM_ARM64 + DISPATCH_PICK_COND(mpx_umul, maybe_umul4_arm64_simd, + cpu_feature_p(CPUFEAT_ARM_NEON)); +#endif DISPATCH_PICK_FALLBACK(mpx_umul, simple_umul); } @@ -1274,9 +1290,14 @@ mpw mpx_udivn(mpw *qv, mpw *qvl, const mpw *rv, const mpw *rvl, mpw d) #include #include +#include #include #include +#ifdef ENABLE_ASM_DEBUG +# include "regdump.h" +#endif + #include "mpscan.h" #define ALLOC(v, vl, sz) do { \ @@ -1366,7 +1387,7 @@ static int loadstore(dstr *v) ok = 0; MPX_OCTETS(oct, m, ml); mpx_storel(m, ml, d.buf, d.sz); - if (memcmp(d.buf, v->buf, oct) != 0) { + if (MEMCMP(d.buf, !=, v->buf, oct)) { dumpbits("\n*** storel failed", d.buf, d.sz); ok = 0; } @@ -1376,7 +1397,7 @@ static int loadstore(dstr *v) ok = 0; MPX_OCTETS(oct, m, ml); mpx_storeb(m, ml, d.buf, d.sz); - if (memcmp(d.buf + d.sz - oct, v->buf + v->len - oct, oct) != 0) { + if (MEMCMP(d.buf + d.sz - oct, !=, v->buf + v->len - oct, oct)) { dumpbits("\n*** storeb failed", d.buf, d.sz); ok = 0; } @@ -1410,14 +1431,14 @@ static int twocl(dstr *v) mpx_loadl(m, ml0, v[0].buf, v[0].len); mpx_storel2cn(m, ml0, d.buf, v[1].len); - if (memcmp(d.buf, v[1].buf, v[1].len)) { + if (MEMCMP(d.buf, !=, v[1].buf, v[1].len)) { dumpbits("\n*** storel2cn failed", d.buf, v[1].len); ok = 0; } mpx_loadl2cn(m, ml1, v[1].buf, v[1].len); mpx_storel(m, ml1, d.buf, v[0].len); - if (memcmp(d.buf, v[0].buf, v[0].len)) { + if (MEMCMP(d.buf, !=, v[0].buf, v[0].len)) { dumpbits("\n*** loadl2cn failed", d.buf, v[0].len); ok = 0; } @@ -1454,14 +1475,14 @@ static int twocb(dstr *v) mpx_loadb(m, ml0, v[0].buf, v[0].len); mpx_storeb2cn(m, ml0, d.buf, v[1].len); - if (memcmp(d.buf, v[1].buf, v[1].len)) { + if (MEMCMP(d.buf, !=, v[1].buf, v[1].len)) { dumpbits("\n*** storeb2cn failed", d.buf, v[1].len); ok = 0; } mpx_loadb2cn(m, ml1, v[1].buf, v[1].len); mpx_storeb(m, ml1, d.buf, v[0].len); - if (memcmp(d.buf, v[0].buf, v[0].len)) { + if (MEMCMP(d.buf, !=, v[0].buf, v[0].len)) { dumpbits("\n*** loadb2cn failed", d.buf, v[0].len); ok = 0; } @@ -1708,6 +1729,9 @@ static test_chunk defs[] = { int main(int argc, char *argv[]) { +#ifdef ENABLE_ASM_DEBUG + regdump_init(); +#endif test_run(argc, argv, defs, SRCDIR"/t/mpx"); return (0); } diff --git a/math/qdparse.c b/math/qdparse.c index 217cc628..9be97427 100644 --- a/math/qdparse.c +++ b/math/qdparse.c @@ -30,6 +30,8 @@ #include #include +#include + #include "qdparse.h" /*----- Main code ---------------------------------------------------------*/ @@ -45,7 +47,7 @@ void qd_skipspc(qd_parse *qd) { - while (isspace((unsigned char)*qd->p)) + while (ISSPACE(*qd->p)) qd->p++; } @@ -90,7 +92,7 @@ int qd_enum(qd_parse *qd, const char *e) e += strspn(e, ", "); if (!*e) break; n = strcspn(e, ", "); - if (strncmp(qd->p, e, n) == 0 && !isalnum((unsigned char)qd->p[n])) { + if (STRNCMP(qd->p, ==, e, n) && !ISALNUM(qd->p[n])) { qd->p += n; return (i); } @@ -116,7 +118,7 @@ mp *qd_getmp(qd_parse *qd) qd_skipspc(qd); m = mp_readstring(MP_NEW, qd->p, &q, 0); - if (m && !isalnum((unsigned char)*q)) + if (m && !ISALNUM(*q)) qd->p = q; else { mp_drop(m); diff --git a/math/t/mp b/math/t/mp index b50ea8dd..8d0942d7 100644 --- a/math/t/mp +++ b/math/t/mp @@ -115,6 +115,39 @@ sqrt { 14565040310136678240 3816417208; } +nthrt { + 0 27 0 1; + 1 1 1 1; + 99 2 9 0; + 100 2 10 1; + 101 2 10 0; + + 2432442434617858985744608211960343276041892998697958012044143077567778256072696563501333460622383626492631158845093813667916645390906408185968436731121086804986194010729874783817632607960227495980162127756247771205609001938726 37 1234566 0; + 2432442434617858985744608211960343276041892998697958012044143077567778256072696563501333460622383626492631158845093813667916645390906408185968436731121086804986194010729874783817632607960227495980162127756247771205609001938727 37 1234567 1; + 2432442434617858985744608211960343276041892998697958012044143077567778256072696563501333460622383626492631158845093813667916645390906408185968436731121086804986194010729874783817632607960227495980162127756247771205609001938728 37 1234567 0; + + -26 3 -3 0; + -27 3 -3 1; + -28 3 -4 0; +} + +perfect-power-p { + 0 0 0 1; + 1 0 1 1; + -1 0 -1 1; + + -4 0 -4 1; + -8 1 -2 3; + -64 1 -4 3; + + 80 0 80 1; + 81 1 3 4; + 82 0 82 1; + + 2432442434617858985744608211960343276041892998697958012044143077567778256072696563501333460622383626492631158845093813667916645390906408185968436731121086804986194010729874783817632607960227495980162127756247771205609001938727 1 1234567 37; + 42467986438630307821661186973460619303572935864570185492440237295438188325895624954701633272891586038903309915601221633039963682793644006385615600911359153716020597273608200491915551536581527267184634993651215467730190125236224 1 12 210; +} + gcd { # --- Simple tests --- diff --git a/math/t/mpx-mul4 b/math/t/mpx-mul4 index 69ea238b..1d8a741e 100644 --- a/math/t/mpx-mul4 +++ b/math/t/mpx-mul4 @@ -7,7 +7,7 @@ dmul4 { d3d2d1d0d7d6d5d4dbdad9d8dfdedddc # v f3f2f1f0f7f6f5f4fbfaf9f8fffefdfc # y d4356fa018c7f681e0be24efecdaf6e0 # zz - 0004bb142333e4e00004c56cb3ac322d000335ca0eb0310000033cbfe475dfd00001a2236db667a00001a5a668a94f10; ## cc + 0004bb142333e4e00004c56cb3ac322d000335ca0eb0310000033cbfe475dfd00001a2236db667a00001a5a668a94f10; # cc } dmla4 { @@ -26,7 +26,7 @@ mul4 { e3e2e1e0e7e6e5e4ebeae9e8efeeedec # x f3f2f1f0f7f6f5f4fbfaf9f8fffefdfc # y 964a43a0b812545cd3c4a34a69e3ec23 # zz - 0002b2f3db03f8310002b880e3fffed70001d457394991000001d812a4ace8a80000ee0b505470500000efed0e0e2428; ## cc + 0002b2f3db03f8310002b880e3fffed70001d457394991000001d812a4ace8a80000ee0b505470500000efed0e0e2428; # cc } mul4zc { @@ -42,7 +42,7 @@ mla4 { e3e2e1e0e7e6e5e4ebeae9e8efeeedec # x f3f2f1f0f7f6f5f4fbfaf9f8fffefdfc # y 49fcf5506fc90a118f7f5d0329a2a9e0 # zz - 0002b2f3db03f8320002b880e3fffed70001d457394991000001d812a4ace8a80000ee0b505470500000efed0e0e2428; ## cc + 0002b2f3db03f8320002b880e3fffed70001d457394991000001d812a4ace8a80000ee0b505470500000efed0e0e2428; # cc } mla4zc { @@ -59,7 +59,7 @@ mmul4 { d3d2d1d0d7d6d5d4dbdad9d8dfdedddc # v 546f97b132b6ca1d10d519b5ca6ab8a9 # m 00000000000000000000000000000000 # zz - 00006c00000012ad00009a8d0000630c0000f0840000979d000077a400000caa # yy + 00006c0000009a8d000012ad0000630c0000f084000077a40000979d00000caa # yy 0003126be83bdbf40002a05c4867918e000259dfe01b01770001b7e463bf6b7a00011339f770da470000bdab9990cf26; # cc } @@ -70,7 +70,7 @@ mmla4 { d3d2d1d0d7d6d5d4dbdad9d8dfdedddc # v 546f97b132b6ca1d10d519b5ca6ab8a9 # m 00000000000000000000000000000000 # zz - 000016b00000d85500000b390000507000008de20000754b000057700000c5db # yy + 000016b000000b390000d8550000507000008de2000057700000754b0000c5db # yy 000338658ad352110002f9fbc6cd85d5000205e99c5e20d300021acac7b997550000fdb10c111c11000131df2708bb59; # cc } @@ -79,6 +79,6 @@ mont4 { acadaeafa8a9aaaba4a5a6a7a0a1a2a3 # n 546f97b132b6ca1d10d519b5ca6ab8a9 # m 00000000000000000000000000000000 # zz - 0000aab00000c5a7000070ab0000ed6400009d5d0000ddad0000dfcb0000b930 # yy + 0000aab0000070ab0000c5a70000ed6400009d5d0000dfcb0000ddad0000b930 # yy 0001734705fa761d00019ee57a6290e40000f14fc045d61200010386c155e29100008b1816a19f2700007432ecd64990; # cc } diff --git a/misc/gfshare.c b/misc/gfshare.c index 80a6ef6f..09acb401 100644 --- a/misc/gfshare.c +++ b/misc/gfshare.c @@ -258,6 +258,8 @@ void gfshare_combine(gfshare *s, void *buf) #ifdef TEST_RIG +#include + #include "fibrand.h" static int verify(grand *r) @@ -298,7 +300,7 @@ static int verify(grand *r) gfshare_combine(&s, sbuf); gfshare_destroy(&s); - if (memcmp(sec, sbuf, len) != 0) { + if (MEMCMP(sec, !=, sbuf, len)) { ok = 0; fprintf(stderr, "\nbad recombination of shares\n"); }; diff --git a/progs/catcrypt.c b/progs/catcrypt.c index c31a3d93..e4d974aa 100644 --- a/progs/catcrypt.c +++ b/progs/catcrypt.c @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -194,14 +195,14 @@ static int encrypt(int argc, char *argv[]) die(EXIT_FAILURE, "encoding `%s' not found", ef); fn = optind < argc ? argv[optind++] : "-"; - if (strcmp(fn, "-") == 0) + if (STRCMP(fn, ==, "-")) fp = stdin; else if ((fp = fopen(fn, "rb")) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", fn, strerror(errno)); } - if (!of || strcmp(of, "-") == 0) + if (!of || STRCMP(of, ==, "-")) ofp = stdout; else if ((ofp = fopen(of, eo->wmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", @@ -366,7 +367,7 @@ static int decrypt(int argc, char *argv[]) die(EXIT_FAILURE, "encoding `%s' not found", ef); fn = optind < argc ? argv[optind++] : "-"; - if (strcmp(fn, "-") == 0) + if (STRCMP(fn, ==, "-")) fp = stdin; else if ((fp = fopen(fn, eo->rmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", @@ -472,7 +473,7 @@ static int decrypt(int argc, char *argv[]) /* --- Now decrypt the main body --- */ - if (!of || strcmp(of, "-") == 0) { + if (!of || STRCMP(of, ==, "-")) { ofp = stdout; f |= f_buffer; } diff --git a/progs/catsign.c b/progs/catsign.c index 3cce9c93..d27016ff 100644 --- a/progs/catsign.c +++ b/progs/catsign.c @@ -161,7 +161,7 @@ static size_t textread(msgcanon *m, void *bp) if (n >= MSGBUFTHRESH) goto full; b[n++] = ch; f &= ~F_MIDLINE; - } else if (isspace(ch)) { + } else if (ISSPACE(ch)) { f |= F_MIDLINE; if (n >= MSGBUFSZ) goto full; b[n++] = ch; nsp++; @@ -222,7 +222,7 @@ static void nullwrite(msgcanon *m, const void *bp, size_t n) { ; } static void mcsetup_readfile(msgcanon *m, unsigned f, const char *fn) { m->f = F_DETACH | (f & (F_BINARY | F_PROGRESS)); - if (!fn || strcmp(fn, "-") == 0) { + if (!fn || STRCMP(fn, ==, "-")) { m->fp = stdin; m->f |= F_NOCLOSE; } else if ((m->fp = fopen(fn, (f & F_BINARY) ? "rb" : "r")) == 0) @@ -260,7 +260,7 @@ static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, } } else { m->read = binreaddetach; - if (!dfn || strcmp(dfn, "-") == 0) + if (!dfn || STRCMP(dfn, ==, "-")) m->fp = stdin; else if ((m->fp = fopen(dfn, "rb")) == 0) die(EXIT_FAILURE, "can't open `%s': %s", dfn, strerror(errno)); @@ -288,7 +288,7 @@ static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, } } } else { - if (!dfn || strcmp(dfn, "-") == 0) + if (!dfn || STRCMP(dfn, ==, "-")) m->fp = stdin; else if ((m->fp = fopen(dfn, "r")) == 0) die(EXIT_FAILURE, "can't read file `%s': %s", dfn, strerror(errno)); @@ -533,7 +533,7 @@ static int sign(int argc, char *argv[]) fn = (optind >= argc) ? 0 : argv[optind++]; - if (!of || strcmp(of, "-") == 0) + if (!of || STRCMP(of, ==, "-")) ofp = stdout; else if ((ofp = fopen(of, eo->wmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", @@ -599,14 +599,14 @@ static int vrfbdry(const char *b, void *p) { vrfctx *v = p; - if (strcmp(b, "CATSIGN MESSAGE") == 0) { + if (STRCMP(b, ==, "CATSIGN MESSAGE")) { v->f |= F_BINARY; v->m |= F_BINARY | F_DETACH; return (1); - } else if (strcmp(b, "CATSIGN MESSAGE HEADER") == 0) { + } else if (STRCMP(b, ==, "CATSIGN MESSAGE HEADER")) { v->m |= F_BINARY | F_DETACH; return (1); - } else if (strcmp(b, "CATSIGN SIGNATURE") == 0) { + } else if (STRCMP(b, ==, "CATSIGN SIGNATURE")) { v->f |= F_DETACH; v->m |= F_DETACH; return (1); @@ -668,7 +668,7 @@ static int verify(int argc, char *argv[]) case 'u': v.f |= F_UTC; break; case 'C': v.f |= F_NOCHECK; break; case 't': - if (strcmp(optarg, "always") == 0) t_fresh = 0; + if (STRCMP(optarg, ==, "always")) t_fresh = 0; else if ((t_fresh = get_date(optarg, 0)) < 0) die(EXIT_FAILURE, "bad freshness time"); break; @@ -685,7 +685,7 @@ static int verify(int argc, char *argv[]) die(EXIT_FAILURE, "encoding `%s' not found", ef); fn = optind < argc ? argv[optind++] : "-"; - if (strcmp(fn, "-") == 0) + if (STRCMP(fn, ==, "-")) fp = stdin; else if ((fp = fopen(fn, eo->rmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", @@ -730,7 +730,7 @@ static int verify(int argc, char *argv[]) printf("WARN verification key %s fails check: %s\n", d.buf, err); dstr_reset(&dd); keyhash(k, s.s, &dd); - if (dd.len != s.kh.len || memcmp(dd.buf, s.kh.buf, dd.len) != 0) { + if (dd.len != s.kh.len || MEMCMP(dd.buf, !=, s.kh.buf, dd.len)) { if (v.verb) printf("FAIL key hash mismatch\n"); exit(EXIT_FAILURE); } @@ -746,7 +746,7 @@ static int verify(int argc, char *argv[]) if (!of && (v.f & F_DETACH)) { rfp = ofp = 0; v.f &= ~F_BUFFER; - } else if (!of || strcmp(of, "-") == 0) { + } else if (!of || STRCMP(of, ==, "-")) { v.f |= F_BUFFER; ofp = stdout; } else if (of && !(v.f & F_BUFFER)) { @@ -893,7 +893,7 @@ static int format(int argc, char *argv[]) die(EXIT_FAILURE, "encoding `%s' not found", oef); fn = optind < argc ? argv[optind++] : "-"; - if (strcmp(fn, "-") == 0) + if (STRCMP(fn, ==, "-")) fp = stdin; else if ((fp = fopen(fn, ieo->rmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", @@ -927,7 +927,7 @@ static int format(int argc, char *argv[]) if (!of) mcsetup_writenull(&mc_out); else { - if (strcmp(of, "-") == 0) + if (STRCMP(of, ==, "-")) ofp = stdout; else if ((ofp = fopen(of, oeo->wmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", @@ -942,7 +942,7 @@ static int format(int argc, char *argv[]) } if (mf) { - if (strcmp(mf, "-") == 0) + if (STRCMP(mf, ==, "-")) mfp = stdout; else if ((mfp = fopen(mf, (f & F_BINARY) ? "wb" : "w")) == 0) { die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", @@ -1037,7 +1037,7 @@ static int info(int argc, char *argv[]) if (optind >= argc) fp = stdin; - else if (strcmp(argv[optind], "-") == 0) { + else if (STRCMP(argv[optind], ==, "-")) { fp = stdin; optind++; } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) { diff --git a/progs/cc-enc.c b/progs/cc-enc.c index f4b6119f..a008fbbf 100644 --- a/progs/cc-enc.c +++ b/progs/cc-enc.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -94,7 +95,7 @@ static enc *pem_encinit(FILE *fp, const char *msg) return (&pe->e); } -int checkbdry(const char *b, void *p) { return (!p || strcmp(b, p) == 0); } +int checkbdry(const char *b, void *p) { return (!p || STRCMP(b, ==, p)); } static enc *pem_decinit(FILE *fp, encbdryp *func, void *p) { @@ -133,7 +134,7 @@ banner: /* --- Check we have the right framing --- */ if (d != 5) goto top; - if (strncmp(buf, "BEGIN ", 6) != 0 || (func && !func(buf + 6, p))) + if (STRNCMP(buf, !=, "BEGIN ", 6) || (func && !func(buf + 6, p))) goto top; /* --- Ready --- */ @@ -248,7 +249,7 @@ static int pem_decdone(enc *e) } if (d != 5) goto fail; buf[i] = 0; - if (strncmp(buf, "END ", 4) != 0 || strcmp(buf + 4, pe->msg) != 0) + if (STRNCMP(buf, !=, "END ", 4) || STRCMP(buf + 4, !=, pe->msg)) goto fail; return (0); @@ -295,7 +296,7 @@ const encops *getenc(const char *enc) const encops *eo; for (eo = enctab; eo->name; eo++) { - if (strcmp(eo->name, enc) == 0) + if (STRCMP(eo->name, ==, enc)) goto e_found; } die(EXIT_FAILURE, "couldn't find encoding `%s'", enc); @@ -399,14 +400,14 @@ int cmd_encode(int argc, char *argv[]) die(EXIT_FAILURE, "encoding `%s' not found", ef); fn = optind < argc ? argv[optind++] : "-"; - if (strcmp(fn, "-") == 0) + if (STRCMP(fn, ==, "-")) fp = stdin; else if ((fp = fopen(fn, "rb")) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", fn, strerror(errno)); } - if (!of || strcmp(of, "-") == 0) + if (!of || STRCMP(of, ==, "-")) ofp = stdout; else if ((ofp = fopen(of, eo->wmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", @@ -481,14 +482,14 @@ int cmd_decode(int argc, char *argv[]) die(EXIT_FAILURE, "encoding `%s' not found", ef); fn = optind < argc ? argv[optind++] : "-"; - if (strcmp(fn, "-") == 0) + if (STRCMP(fn, ==, "-")) fp = stdin; else if ((fp = fopen(fn, eo->rmode)) == 0) { die(EXIT_FAILURE, "couldn't open file `%s': %s", fn, strerror(errno)); } - if (!of || strcmp(of, "-") == 0) + if (!of || STRCMP(of, ==, "-")) ofp = stdout; else if ((ofp = fopen(of, "wb")) == 0) { die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", diff --git a/progs/cc-hash.c b/progs/cc-hash.c index 64fea0ec..6d65f5bd 100644 --- a/progs/cc-hash.c +++ b/progs/cc-hash.c @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -67,9 +68,7 @@ static void puthex(const octet *buf, size_t sz, FILE *fp) static size_t gethex(const char *p, octet *q, size_t sz, char **pp) { size_t i = 0; - while (sz > 0 && - isxdigit((unsigned char)p[0]) && - isxdigit((unsigned char)p[1])) { + while (sz > 0 && ISXDIGIT(p[0]) && ISXDIGIT(p[1])) { char buf[3]; buf[0] = p[0]; buf[1] = p[1]; @@ -164,7 +163,7 @@ const encodeops *getencoding(const char *ename) const encodeops *e; for (e = encodingtab; e->name; e++) { - if (strcmp(ename, e->name) == 0) + if (STRCMP(ename, ==, e->name)) return (e); } return (0); @@ -198,7 +197,7 @@ const gchash *gethash(const char *name) const gchash *const *g, *gg = 0; size_t sz = strlen(name); for (g = ghashtab; *g; g++) { - if (strncmp(name, (*g)->name, sz) == 0) { + if (STRNCMP(name, ==, (*g)->name, sz)) { if ((*g)->name[sz] == 0) { gg = *g; break; @@ -292,7 +291,7 @@ int fhash(fhashstate *fh, const char *file, void *buf) size_t n; fprogress ff; - if (!file || strcmp(file, "-") == 0) + if (!file || STRCMP(file, ==, "-")) fp = stdin; else if ((fp = fopen(file, fh->f & FHF_BINARY ? "rb" : "r")) == 0) return (-1); @@ -303,7 +302,7 @@ int fhash(fhashstate *fh, const char *file, void *buf) if (fh->f & FHF_JUNK) { p = file; - if (strncmp(p, "./", 2) == 0) p += 2; + if (STRNCMP(p, ==, "./", 2)) p += 2; q = p; ffhe = &fh->ents; for (;;) { @@ -311,7 +310,7 @@ int fhash(fhashstate *fh, const char *file, void *buf) n = q - p; for (; *ffhe; ffhe = &(*ffhe)->next) { fhe = *ffhe; - if (strncmp(p, fhe->name, n) == 0 && fhe->name[n] == 0) + if (STRNCMP(p, ==, fhe->name, n) && fhe->name[n] == 0) goto found; } fhe = xmalloc(offsetof(struct fhent, name) + n + 1); @@ -383,10 +382,10 @@ static int fhjunk(struct fhjunk *fhj, struct fhent *ents) n++; } while (errno = 0, (d = readdir(dp)) != 0) { - if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) + if (STRCMP(d->d_name, ==, ".") || STRCMP(d->d_name, ==, "..")) continue; for (fhe = ents; fhe; fhe = fhe->next) { - if (strcmp(d->d_name, fhe->name) == 0) goto found; + if (STRCMP(d->d_name, ==, fhe->name)) goto found; } fhj->d->len = n; dstr_puts(fhj->d, d->d_name); @@ -464,17 +463,17 @@ int hfparse(hfpctx *hfp) if (*p == '#') { p++; if ((q = str_getword(&p)) == 0) return (HF_BAD); - if (strcmp(q, "hash") == 0) { + if (STRCMP(q, ==, "hash")) { if ((q = str_getword(&p)) == 0) return (HF_BAD); if ((gch = gethash(q)) == 0) return (HF_BAD); hfp->gch = gch; return (HF_HASH); - } else if (strcmp(q, "encoding") == 0) { + } else if (STRCMP(q, ==, "encoding")) { if ((q = str_getword(&p)) == 0) return (HF_BAD); if ((ee = getencoding(q)) == 0) return (HF_BAD); hfp->ee = ee; return (HF_ENC); - } else if (strcmp(q, "escape") == 0) { + } else if (STRCMP(q, ==, "escape")) { hfp->f |= HFF_ESCAPE; return (HF_ESC); } @@ -556,7 +555,7 @@ int getstring(void *in, dstr *d, unsigned f) again: ch = nextch(in); - while (isspace(ch)) + while (ISSPACE(ch)) ch = nextch(in); if (ch == '#') { do ch = nextch(in); while (ch != '\n' && ch != eofch); @@ -605,7 +604,7 @@ again: if (ch == q) break; - if (!q && isspace(ch)) + if (!q && ISSPACE(ch)) break; /* --- Otherwise contribute and continue --- */ @@ -649,7 +648,7 @@ void putstring(FILE *fp, const char *p, unsigned f) qq = 0; for (q = p; *q; q++) { - if (isspace((unsigned char)*q)) { + if (ISSPACE(*q)) { qq = '\"'; break; } diff --git a/progs/cc-kem.c b/progs/cc-kem.c index 9159ade7..36dfdb3d 100644 --- a/progs/cc-kem.c +++ b/progs/cc-kem.c @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -210,12 +211,12 @@ static bulk *naclbox_init(key *k, const char *calg, const char *halg) key_fulltag(k, &t); if ((q = key_getattr(0, k, "cipher")) != 0) calg = q; - if (!calg || strcmp(calg, "salsa20") == 0) aec = &salsa20_naclbox; - else if (strcmp(calg, "salsa20/12") == 0) aec = &salsa2012_naclbox; - else if (strcmp(calg, "salsa20/8") == 0) aec = &salsa208_naclbox; - else if (strcmp(calg, "chacha20") == 0) aec = &chacha20_naclbox; - else if (strcmp(calg, "chacha12") == 0) aec = &chacha12_naclbox; - else if (strcmp(calg, "chacha8") == 0) aec = &chacha8_naclbox; + if (!calg || STRCMP(calg, ==, "salsa20")) aec = &salsa20_naclbox; + else if (STRCMP(calg, ==, "salsa20/12")) aec = &salsa2012_naclbox; + else if (STRCMP(calg, ==, "salsa20/8")) aec = &salsa208_naclbox; + else if (STRCMP(calg, ==, "chacha20")) aec = &chacha20_naclbox; + else if (STRCMP(calg, ==, "chacha12")) aec = &chacha12_naclbox; + else if (STRCMP(calg, ==, "chacha8")) aec = &chacha8_naclbox; else { die(EXIT_FAILURE, "unknown or inappropriate encryption scheme `%s' in key `%s'", @@ -866,7 +867,7 @@ kem *getkem(key *k, const char *app, int wantpriv, bulk **bc) if ((q = key_getattr(0, k, "kem")) != 0) { dstr_puts(&d, q); p = d.buf; - } else if (strncmp(k->type, app, n) == 0 && k->type[n] == '-') { + } else if (STRNCMP(k->type, ==, app, n) && k->type[n] == '-') { dstr_puts(&d, k->type); p = d.buf + n + 1; } else @@ -897,7 +898,7 @@ kem *getkem(key *k, const char *app, int wantpriv, bulk **bc) /* --- Instantiate the KEM --- */ for (kt = kemtab; kt->name; kt++) { - if (strcmp(kt->name, kalg) == 0) + if (STRCMP(kt->name, ==, kalg)) goto k_found; } die(EXIT_FAILURE, "key encapsulation mechanism `%s' not found in key `%s'", @@ -934,10 +935,10 @@ k_found:; bt = bulktab; else { for (bt = bulktab, bo = 0; bt->name; bt++) { - if (strcmp(balg, bt->name) == 0) + if (STRCMP(balg, ==, bt->name)) { balg = 0; goto b_found; } n = strlen(bt->name); - if (strncmp(balg, bt->name, n) == 0 && balg[n] == '-') + if (STRNCMP(balg, ==, bt->name, n) && balg[n] == '-') { balg += n + 1; goto b_found; } } bt = bulktab; diff --git a/progs/cc-list.c b/progs/cc-list.c index 8bc22192..19ea7d5a 100644 --- a/progs/cc-list.c +++ b/progs/cc-list.c @@ -29,6 +29,7 @@ #define _FILE_OFFSET_BITS 64 +#include #include #include "cc.h" @@ -57,7 +58,7 @@ int displaylists(const struct listent *listtab, char *const argv[]) } else { for (i = 0; argv[i]; i++) { for (li = listtab; li->name; li++) { - if (strcmp(li->name, argv[i]) == 0) { + if (STRCMP(li->name, ==, argv[i])) { li->list(); goto cool; } diff --git a/progs/cc-sig.c b/progs/cc-sig.c index a416c6e5..4c1defc1 100644 --- a/progs/cc-sig.c +++ b/progs/cc-sig.c @@ -31,6 +31,7 @@ #include +#include #include #include "rand.h" @@ -809,7 +810,7 @@ sig *getsig(key *k, const char *app, int wantpriv) if ((q = key_getattr(0, k, "sig")) != 0) { dstr_puts(&d, q); p = d.buf; - } else if (strncmp(k->type, app, n) == 0 && k->type[n] == '-') { + } else if (STRNCMP(k->type, ==, app, n) && k->type[n] == '-') { dstr_puts(&d, k->type); p = d.buf + n + 1; } else @@ -832,7 +833,7 @@ sig *getsig(key *k, const char *app, int wantpriv) /* --- Look up the algorithms in the table --- */ for (st = sigtab; st->name; st++) { - if (strcmp(st->name, salg) == 0) + if (STRCMP(st->name, ==, salg)) goto s_found; } die(EXIT_FAILURE, "signature algorithm `%s' not found in key `%s'", diff --git a/progs/cc-subcmd.c b/progs/cc-subcmd.c index 83c4a789..d593a1a3 100644 --- a/progs/cc-subcmd.c +++ b/progs/cc-subcmd.c @@ -29,6 +29,7 @@ #define _FILE_OFFSET_BITS 64 +#include #include #include @@ -53,7 +54,7 @@ const cmd *findcmd(const cmd *cmds, const char *name) size_t sz = strlen(name); for (c = cmds; c->name; c++) { - if (strncmp(name, c->name, sz) == 0) { + if (STRNCMP(name, ==, c->name, sz)) { if (c->name[sz] == 0) { chosen = c; break; diff --git a/progs/cookie.c b/progs/cookie.c index c6912ffd..62e23b79 100644 --- a/progs/cookie.c +++ b/progs/cookie.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -178,7 +179,7 @@ static gmac *getmac(key *k, const char *app) if ((q = key_getattr(0, k, "mac")) != 0) { dstr_puts(&d, q); p = d.buf; - } else if (strncmp(k->type, app, n) == 0 && k->type[n] == '-') { + } else if (STRNCMP(k->type, ==, app, n) && k->type[n] == '-') { dstr_puts(&d, k->type); p = d.buf + n + 1; } else @@ -258,7 +259,7 @@ static int cmd_gen(int argc, char *argv[]) /* --- Fetch an expiry time --- */ case 'e': - if (strcmp(optarg, "forever") == 0) + if (STRCMP(optarg, ==, "forever")) c.exp = KEXP_FOREVER; else if ((c.exp = get_date(optarg, 0)) == -1) die(EXIT_FAILURE, "bad expiry date: `%s'", optarg); diff --git a/progs/dsig.c b/progs/dsig.c index 1377bcaa..3f217da6 100644 --- a/progs/dsig.c +++ b/progs/dsig.c @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -204,7 +205,7 @@ static int bget(block *b, FILE *fp, unsigned bin) if (getstring(fp, &d, GSF_FILE)) return (E_EOF); for (tag = 0; tagtab[tag]; tag++) { - if (strcmp(tagtab[tag], d.buf) == 0) + if (STRCMP(tagtab[tag], ==, d.buf)) goto done; } return (E_TAG); @@ -239,7 +240,7 @@ static int bget(block *b, FILE *fp, unsigned bin) } else { if (getstring(fp, &b->d, GSF_FILE)) return (E_EOF); - if (strcmp(b->d.buf, "forever") == 0) + if (STRCMP(b->d.buf, ==, "forever")) b->t = KEXP_FOREVER; else if ((b->t = get_date(b->d.buf, 0)) == -1) return (E_DATE); @@ -536,7 +537,7 @@ static int sign(int argc, char *argv[]) ki = optarg; break; case 'e': - if (strcmp(optarg, "forever") == 0) + if (STRCMP(optarg, ==, "forever")) exp = KEXP_FOREVER; else if ((exp = get_date(optarg, 0)) == -1) die(EXIT_FAILURE, "bad expiry time"); @@ -572,14 +573,14 @@ static int sign(int argc, char *argv[]) /* --- Open files --- */ if (hfile) ifile = hfile; - if (!ifile || strcmp(ifile, "-") == 0) + if (!ifile || STRCMP(ifile, ==, "-")) ifp = stdin; else if ((ifp = fopen(ifile, (f & f_bin) ? "rb" : "r")) == 0) { die(EXIT_FAILURE, "couldn't open input file `%s': %s", ifile, strerror(errno)); } - if (!ofile || strcmp(ofile, "-") == 0) + if (!ofile || STRCMP(ofile, ==, "-")) ofp = stdout; else if ((ofp = fopen(ofile, (f & f_bin) ? "wb" : "w")) == 0) { die(EXIT_FAILURE, "couldn't open output file `%s': %s", @@ -908,7 +909,7 @@ static int verify(int argc, char *argv[]) } f |= f_bogus; } else if (b.b.len != GH_CLASS(s->h)->hashsz || - memcmp(d.buf, b.b.buf, b.b.len) != 0) { + MEMCMP(d.buf, !=, b.b.buf, b.b.len)) { if (verb > 1) printf("BAD file `%s' has incorrect hash\n", b.d.buf); f |= f_bogus; diff --git a/progs/factorial.c b/progs/factorial.c index 975c698c..ece0b305 100644 --- a/progs/factorial.c +++ b/progs/factorial.c @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -130,10 +131,10 @@ int main(int argc, char *argv[]) } ulmax = mp_fromulong(MP_NEW, ULONG_MAX); p = argv[optind]; - while (isspace((unsigned char)*p)) + while (ISSPACE(*p)) p++; xx = mp_readstring(MP_NEW, argv[optind], &p, 0); - while (isspace((unsigned char)*p)) + while (ISSPACE(*p)) p++; if (!xx || *p || MP_CMP(xx, <, MP_ZERO) || MP_CMP(xx, >, ulmax)) die(EXIT_FAILURE, "bad integer `%s'", argv[optind]); diff --git a/progs/fibonacci.c b/progs/fibonacci.c index 4b5a9585..a494f625 100644 --- a/progs/fibonacci.c +++ b/progs/fibonacci.c @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -128,9 +129,9 @@ int main(int argc, char *argv[]) lmin = mp_fromlong(MP_NEW, LONG_MIN); lmax = mp_fromlong(MP_NEW, LONG_MAX); p = argv[optind]; - while (isspace((unsigned char)*p)) p++; + while (ISSPACE(*p)) p++; nn = mp_readstring(MP_NEW, argv[optind], &p, 0); - while (isspace((unsigned char)*p)) p++; + while (ISSPACE(*p)) p++; if (!nn || *p || MP_CMP(lmin, >, nn) || MP_CMP(nn, >, lmax)) die(EXIT_FAILURE, "bad integer `%s'", argv[optind]); n = mp_tolong(nn); diff --git a/progs/hashsum.c b/progs/hashsum.c index df1fdaea..deb27d50 100644 --- a/progs/hashsum.c +++ b/progs/hashsum.c @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -97,7 +98,7 @@ static int checkhash(fhashstate *fh, const char *file, const encodeops *e) unsigned long n = 0, nfail = 0; int hf; - if (!file || strcmp(file, "-") == 0) + if (!file || STRCMP(file, ==, "-")) hfp.fp = stdin; else if ((hfp.fp = fopen(file, fh->f & GSF_RAW ? "r" : "rb")) == 0) { moan("couldn't open `%s': %s", file, strerror(errno)); @@ -124,8 +125,8 @@ static int checkhash(fhashstate *fh, const char *file, const encodeops *e) rc = EXIT_FAILURE; continue; } - if (memcmp(hfp.hbuf, hfp.hbuf + hfp.gch->hashsz, - hfp.gch->hashsz) != 0) { + if (MEMCMP(hfp.hbuf, !=, hfp.hbuf + hfp.gch->hashsz, + hfp.gch->hashsz)) { if (hfp.f & f_verbose) fprintf(stderr, "FAIL %s\n", df.buf); else @@ -190,7 +191,7 @@ static int hashfiles(fhashstate *fh, const char *file, const encodeops *e) int rc = 0; int rrc; - if (!file || strcmp(file, "-") == 0) + if (!file || STRCMP(file, ==, "-")) fp = stdin; else if ((fp = fopen(file, fh->f & GSF_RAW ? "r" : "rb")) == 0) { moan("couldn't open `%s': %s", file, strerror(errno)); @@ -278,7 +279,7 @@ int main(int argc, char *argv[]) { char *q = xstrdup(QUIS); size_t len = strlen(q); - if (len > 3 && strcmp(q + len - 3, "sum") == 0) { + if (len > 3 && STRCMP(q + len - 3, ==, "sum")) { q[len - 3] = 0; fh.gch = gethash(q); } diff --git a/progs/key.1 b/progs/key.1 index 15552fd0..f5b64455 100644 --- a/progs/key.1 +++ b/progs/key.1 @@ -966,9 +966,9 @@ keyids, types, expiry and deletion dates, and comments. Additional .RB ` \-v ' options show more information, such as the exact time of day for expiry and deletion, key attributes, and a dump of the actual key data. If the -verbosity level is sufficiently high, passphrases are requested to -decrypt locked keys. Make sure nobody is looking over your shoulder -when you do this! +verbosity level is sufficiently high, secret parts of keys are printed, +and passphrases are requested to decrypt locked keys. Make sure nobody +is looking over your shoulder when you do this! .SS "fingerprint" Reports a fingerprint (secure hash) on components of requested keys. The following options are supported: diff --git a/progs/key.c b/progs/key.c index 0c817d4d..c38ed5db 100644 --- a/progs/key.c +++ b/progs/key.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -602,7 +603,7 @@ static void alg_dhparam(keyopts *k) group *g; const char *e; - if (strcmp(k->curve, "list") == 0) { + if (STRCMP(k->curve, ==, "list")) { unsigned i, w; LIST("Built-in prime fields", stdout, ptab[i].name, ptab[i].name); exit(0); @@ -770,7 +771,7 @@ static void alg_binparam(keyopts *k) /* --- Decide on a field --- */ if (!k->bits) k->bits = 128; - if (k->curve && strcmp(k->curve, "list") == 0) { + if (k->curve && STRCMP(k->curve, ==, "list")) { unsigned i, w; LIST("Built-in binary fields", stdout, bintab[i].name, bintab[i].name); @@ -869,7 +870,7 @@ static void alg_ecparam(keyopts *k) /* --- Decide on a curve --- */ if (!k->bits) k->bits = 256; - if (k->curve && strcmp(k->curve, "list") == 0) { + if (k->curve && STRCMP(k->curve, ==, "list")) { unsigned i, w; LIST("Built-in elliptic curves", stdout, ectab[i].name, ectab[i].name); @@ -1093,7 +1094,7 @@ static int cmd_add(int argc, char *argv[]) keyalg *a; size_t sz = strlen(optarg); - if (strcmp(optarg, "list") == 0) { + if (STRCMP(optarg, ==, "list")) { for (a = algtab; a->name; a++) printf("%-10s %s\n", a->name, a->help); return (0); @@ -1101,7 +1102,7 @@ static int cmd_add(int argc, char *argv[]) alg = 0; for (a = algtab; a->name; a++) { - if (strncmp(optarg, a->name, sz) == 0) { + if (STRNCMP(optarg, ==, a->name, sz)) { if (a->name[sz] == 0) { alg = a; break; @@ -1140,7 +1141,7 @@ static int cmd_add(int argc, char *argv[]) /* --- Expiry dates get passed to @get_date@ for parsing --- */ case 'e': - if (strcmp(optarg, "forever") == 0) + if (STRCMP(optarg, ==, "forever")) exp = KEXP_FOREVER; else { exp = get_date(optarg, 0); @@ -1178,7 +1179,7 @@ static int cmd_add(int argc, char *argv[]) case 'A': { const struct seedalg *ss; - if (strcmp(optarg, "list") == 0) { + if (STRCMP(optarg, ==, "list")) { printf("Seed algorithms:\n"); for (ss = seedtab; ss->p; ss++) printf(" %s\n", ss->p); @@ -1187,7 +1188,7 @@ static int cmd_add(int argc, char *argv[]) if (seed) die(EXIT_FAILURE, "seed already set -- put -A first"); sa = 0; for (ss = seedtab; ss->p; ss++) { - if (strcmp(optarg, ss->p) == 0) + if (STRCMP(optarg, ==, ss->p)) sa = ss; } if (!sa) @@ -1320,7 +1321,7 @@ static int cmd_add(int argc, char *argv[]) if (k.f & f_retag) { if ((kk = key_bytag(&f, tag)) != 0 && kk->tag && - strcmp(kk->tag, tag) == 0) + STRCMP(kk->tag, ==, tag)) key_settag(&f, kk, 0); } if ((err = key_settag(&f, k.k, tag)) != 0) @@ -1396,6 +1397,11 @@ static void showkeydata(key_data *k, int ind, listopts *o, dstr *d) } \ } while (0) + if ((k->e&KF_ENCMASK) == KENC_ENCRYPT && o->v <= 4) + { fputs(" encrypted\n", stdout); return; } + if ((k->e&KF_ENCMASK) != KENC_STRUCT && !(k->e&KF_NONSECRET) && o->v <= 3) + { fputs(" secret\n", stdout); return; } + switch (k->e & KF_ENCMASK) { /* --- Binary key data --- * @@ -1431,20 +1437,16 @@ static void showkeydata(key_data *k, int ind, listopts *o, dstr *d) * key. Otherwise just say that it's encrypted and move on. */ - case KENC_ENCRYPT: - if (o->v <= 3) - fputs(" encrypted\n", stdout); + case KENC_ENCRYPT: { + key_data *kd; + if (key_punlock(&kd, k, d->buf)) + printf(" \n", d->buf); else { - key_data *kd; - if (key_punlock(&kd, k, d->buf)) - printf(" \n", d->buf); - else { - fputs(" encrypted", stdout); - showkeydata(kd, ind, o, d); - key_drop(kd); - } + fputs(" encrypted", stdout); + showkeydata(kd, ind, o, d); + key_drop(kd); } - break; + } break; /* --- Integer keys --- * * @@ -1825,7 +1827,7 @@ static const struct fpres *lookup_fpres(const char *name) { const struct fpres *fpres; for (fpres = fprestab; fpres->name; fpres++) - if (strcmp(fpres->name, name) == 0) return (fpres); + if (STRCMP(fpres->name, ==, name)) return (fpres); die(EXIT_FAILURE, "unknown presentation syle `%s'", name); } @@ -1870,7 +1872,7 @@ static int cmd_finger(int argc, char *argv[]) argv += optind; argc -= optind; if (rc) { die(EXIT_FAILURE, - "Usage: fingerprint [-a HASHALG] [-p STYLE] [-f FILTER] [TAG...]"); + "Usage: fingerprint [-a HASH] [-p STYLE] [-f FILTER] [TAG...]"); } doopen(&f, KOPEN_READ); @@ -1944,7 +1946,7 @@ static int cmd_verify(int argc, char *argv[]) argv += optind; argc -= optind; if (rc || argc != 2) { die(EXIT_FAILURE, - "Usage: verify [-a HASHALG] [-p STYLE] [-f FILTER] TAG FINGERPRINT"); + "Usage: verify [-a HASH] [-p STYLE] [-f FILTER] TAG FINGERPRINT"); } doopen(&f, KOPEN_READ); @@ -1969,7 +1971,7 @@ static int cmd_verify(int argc, char *argv[]) if (!key_fingerprint(k, h, &kf)) die(EXIT_FAILURE, "key has no fingerprintable components (as filtered)"); fpr = GH_DONE(h, 0); - if (memcmp(fpr, d.buf, ch->hashsz) != 0) + if (MEMCMP(fpr, !=, d.buf, ch->hashsz)) die(EXIT_FAILURE, "key fingerprint mismatch"); dstr_destroy(&d); dstr_destroy(&dd); doclose(&f); @@ -2028,7 +2030,7 @@ static int cmd_tag(int argc, char *argv[]) die(EXIT_FAILURE, "Usage: tag [-r] TAG [NEW-TAG]"); doopen(&f, KOPEN_WRITE); if (flags & f_retag) { - if ((k = key_bytag(&f, argv[1])) != 0 && strcmp(k->tag, argv[1]) == 0) + if ((k = key_bytag(&f, argv[1])) != 0 && STRCMP(k->tag, ==, argv[1])) key_settag(&f, k, 0); } if ((k = key_bytag(&f, argv[0])) == 0) @@ -2122,7 +2124,7 @@ static int cmd_extract(int argc, char *argv[]) argv += optind; argc -= optind; if (rc || argc < 1) die(EXIT_FAILURE, "Usage: extract [-f FILTER] FILE [TAG...]"); - if (strcmp(*argv, "-") == 0) + if (STRCMP(*argv, ==, "-")) fp = stdout; else { outfile = *argv; @@ -2178,7 +2180,7 @@ static int cmd_merge(int argc, char *argv[]) if (argc != 2) die(EXIT_FAILURE, "Usage: merge FILE"); - if (strcmp(argv[1], "-") == 0) + if (STRCMP(argv[1], ==, "-")) fp = stdin; else if (!(fp = fopen(argv[1], "r"))) { die(EXIT_FAILURE, "couldn't open `%s' for reading: %s", @@ -2233,7 +2235,7 @@ Options:\n\ -v, --verbose Show more information.\n\ " }, { "fingerprint", cmd_finger, - "fingerprint [-a HASHALG] [-p STYLE] [-f FILTER] [TAG...]", "\ + "fingerprint [-a HASH] [-p STYLE] [-f FILTER] [TAG...]", "\ Options:\n\ \n\ -f, --filter=FILT Only hash key components matching FILT.\n\ diff --git a/progs/mkphrase.c b/progs/mkphrase.c index b84d850b..1b36cb24 100644 --- a/progs/mkphrase.c +++ b/progs/mkphrase.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -99,7 +100,7 @@ static void wordlist_scan(FILE *fp, void *p) for (;;) { int ch = getc(fp); - if (ch == EOF || isspace(ch)) { + if (ch == EOF || ISSPACE(ch)) { DPUTZ(&d); if (f && d.len >= min && d.len <= max) sym_find(&w->tab, d.buf, d.len + 1, sizeof(sym_base), 0); @@ -109,7 +110,7 @@ static void wordlist_scan(FILE *fp, void *p) break; continue; } - ch = tolower(ch); + ch = TOLOWER(ch); if (strchr(wchars, ch)) { DPUTC(&d, ch); f = 1; @@ -204,7 +205,7 @@ static void markov_scan(FILE *fp, void *p) const char *q; node *n = &(*model)[i][j][k]; - if (ch == EOF || isspace(ch)) { + if (ch == EOF || ISSPACE(ch)) { if (l != C_END) { l = C_END; n->count++; @@ -216,7 +217,7 @@ static void markov_scan(FILE *fp, void *p) continue; } - if ((q = strchr(wchars, tolower(ch))) == 0) + if ((q = strchr(wchars, TOLOWER(ch))) == 0) continue; l = q - wchars; n->count++; @@ -275,7 +276,7 @@ static void usage(FILE *fp) { pquis(fp, "\ Usage: $ [-p] [-b MIN[-MAX]] [-g GEN] [-n COUNT]\n\ -\t[-r [MIN-]MAX] WORDLIST...\n \ +\t[-r [MIN-]MAX] WORDLIST...\n\ "); } @@ -362,7 +363,7 @@ int main(int argc, char *argv[]) size_t n = strlen(optarg); ops = 0; for (p = ppgentab; *p; p++) { - if (strncmp(optarg, (*p)->name, n) == 0) { + if (STRNCMP(optarg, ==, (*p)->name, n)) { if (!(*p)->name[n]) { ops = *p; break; @@ -414,7 +415,7 @@ int main(int argc, char *argv[]) ctx = ops->init(); while (*argv) { - if (strcmp(*argv, "-") == 0) + if (STRCMP(*argv, ==, "-")) ops->scan(stdin, ctx); else { FILE *fp = fopen(*argv, "r"); diff --git a/progs/perftest.c b/progs/perftest.c index 6af4d515..b4a88ca9 100644 --- a/progs/perftest.c +++ b/progs/perftest.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -979,7 +980,7 @@ int main(int argc, char *argv[]) if (optind + 1 != argc) { usage(stderr); exit(1); } for (j = jobtab; j->name; j++) - if (strcmp(j->name, argv[optind]) == 0) break; + if (STRCMP(j->name, ==, argv[optind])) break; if (!j->name) die(1, "unknown job type `%s'", argv[optind]); p = j->init(&o); diff --git a/progs/pixie.c b/progs/pixie.c index 3fd32081..f827c201 100644 --- a/progs/pixie.c +++ b/progs/pixie.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -229,7 +230,7 @@ static phrase *p_find(const char *tag) phrase *p; for (p = p_head; p; p = p->next) { - if (strcmp(p->tag, tag) == 0) { + if (STRCMP(p->tag, ==, tag)) { if (p->t) { struct timeval tv; sel_rmtimer(&p->timer); @@ -311,7 +312,7 @@ static void p_flush(const char *tag) phrase *pp = p->next; if (!tag) p_free(p); - else if (strcmp(p->tag, tag) == 0) { + else if (STRCMP(p->tag, ==, tag)) { if (verbose > 1) pxlog("flushing passphrase `%s'", tag); p_free(p); @@ -445,7 +446,7 @@ static int p_request(const char *msg, const char *tag, char *buf, size_t sz) ok: { char *p = buf; size_t len; - while (isspace((unsigned char)*p)) + while (ISSPACE(*p)) p++; len = strlen(p); memmove(buf, p, len); @@ -517,7 +518,7 @@ static int p_get(const char **q, const char *tag, unsigned mode, time_t exp) goto fail; if (p_request("Verify passphrase", tag, pp, LBUFSZ) < 0) goto fail; - if (strcmp(pp, p->p) != 0) { + if (STRCMP(pp, !=, p->p)) { if (verbose) pxlog("passphrases for `%s' don't match", tag); p_free(p); @@ -677,11 +678,11 @@ static void pixserv_line(char *s, size_t len, void *p) if ((q = str_getword(&s)) == 0) return; for (qq = q; *qq; qq++) - *qq = tolower((unsigned char)*qq); + *qq = TOLOWER(*qq); /* --- Handle a help request --- */ - if (strcmp(q, "help") == 0) { + if (STRCMP(q, ==, "help")) { if (pixserv_write(px, "\ INFO Commands supported:\n\ INFO flush [TAG]\n\ @@ -698,7 +699,7 @@ OK\n\ /* --- List the passphrases --- */ - else if (strcmp(q, "list") == 0) { + else if (STRCMP(q, ==, "list")) { phrase *p; for (p = p_head; p; p = p->next) { @@ -718,8 +719,8 @@ OK\n\ /* --- Request a passphrase --- */ - else if ((mode = PMODE_READ, strcmp(q, "pass") == 0) || - (mode = PMODE_VERIFY, strcmp(q, "verify") == 0)) { + else if ((mode = PMODE_READ, STRCMP(q, ==, "pass")) || + (mode = PMODE_VERIFY, STRCMP(q, ==, "verify"))) { unsigned long t; const char *p; int rc; @@ -747,7 +748,7 @@ OK\n\ /* --- Flush existing passphrases --- */ - else if (strcmp(q, "flush") == 0) { + else if (STRCMP(q, ==, "flush")) { q = str_getword(&s); p_flush(q); if (pixserv_write(px, "OK\n")) goto close; @@ -755,7 +756,7 @@ OK\n\ /* --- Set a passphrase --- */ - else if (strcmp(q, "set") == 0) { + else if (STRCMP(q, ==, "set")) { char *tag; unsigned long t; if ((tag = str_getword(&s)) == 0) { @@ -763,14 +764,14 @@ OK\n\ } else if ((q = str_getword(&s)) == 0) { if (pixserv_write(px, "FAIL no passphrase\n")) goto close; } else { - if (strcmp(q, "--") != 0) { + if (STRCMP(q, !=, "--")) { t = pixserv_timeout(q); q = str_getword(&s); } else t = pixserv_timeout(0); if (!q) { if (pixserv_write(px, "FAIL no passphrase\n")) goto close; - } else if (strcmp(q, "--") != 0) { + } else if (STRCMP(q, !=, "--")) { if (pixserv_write(px, "FAIL rubbish found before passphrase\n")) goto close; } else { @@ -783,7 +784,7 @@ OK\n\ /* --- Shut the server down --- */ - else if (strcmp(q, "quit") == 0) { + else if (STRCMP(q, ==, "quit")) { if (verbose) { pxlog("%s client requested shutdown", px->f & px_stdin ? "local" : "remote"); @@ -1076,14 +1077,14 @@ static void c_sline(char *s, size_t len, void *p) puts(s); else { char *q = str_getword(&s); - if (strcmp(q, "FAIL") == 0) + if (STRCMP(q, ==, "FAIL")) die(1, "%s", s); - else if (strcmp(q, "INFO") == 0 || - strcmp(q, "ITEM") == 0) + else if (STRCMP(q, ==, "INFO") || + STRCMP(q, ==, "ITEM")) puts(s); - else if (strcmp(q, "OK") == 0) { + else if (STRCMP(q, ==, "OK")) { if (s && *s) puts(s); - } else if (strcmp(q, "MISSING") == 0) + } else if (STRCMP(q, ==, "MISSING")) ; else moan("unexpected output: %s %s", q, s); diff --git a/progs/rspit.c b/progs/rspit.c index 66a05389..812525fc 100644 --- a/progs/rspit.c +++ b/progs/rspit.c @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -435,7 +436,7 @@ static int opt(void) case 'o': if (flags & f_file) die(EXIT_FAILURE, "already set an output file"); - if (strcmp(optarg, "-") == 0) + if (STRCMP(optarg, ==, "-")) outfp = stdout; else { outfp = fopen(optarg, "w"); @@ -1591,7 +1592,7 @@ int main(int ac, char *av[]) g = 0; for (gg = generators; gg->name; gg++) { - if (strncmp(arg, gg->name, sz) == 0) { + if (STRNCMP(arg, ==, gg->name, sz)) { if (gg->name[sz] == 0) { g = gg; break; diff --git a/pub/bbs-rand.c b/pub/bbs-rand.c index c7dc86e6..c3ae2682 100644 --- a/pub/bbs-rand.c +++ b/pub/bbs-rand.c @@ -374,6 +374,8 @@ grand *bbs_rand(mp *m, mp *x) #ifdef TEST_RIG +#include + static int verify(dstr *v) { mp *n = *(mp **)v[0].buf; @@ -385,7 +387,7 @@ static int verify(dstr *v) dstr_ensure(&d, v[2].len); b->ops->fill(b, d.buf, v[2].len); d.len = v[2].len; - if (memcmp(d.buf, v[2].buf, v[2].len) != 0) { + if (MEMCMP(d.buf, !=, v[2].buf, v[2].len)) { fputs("\n*** bbs failure\n", stderr); fputs("n = ", stderr); mp_writefile(n, stderr, 10); fputc('\n', stderr); fputs("x = ", stderr); mp_writefile(x, stderr, 10); fputc('\n', stderr); diff --git a/pub/dh-kcdsa.c b/pub/dh-kcdsa.c index 5feda92f..d27bc7d8 100644 --- a/pub/dh-kcdsa.c +++ b/pub/dh-kcdsa.c @@ -66,32 +66,42 @@ int dh_kcdsagen(dh_param *dp, unsigned ql, unsigned pl, rabin rb; int rc = PGEN_ABORT; int i; - mp *x; + mp *x = MP_NEW, *t = MP_NEW; - /* --- First trick: find %$q$% --- */ + /* --- First trick: find %$v$% --- */ +retry: pf.step = 2; - x = mprand(MP_NEW, pl - ql, r, 1); + x = mprand(x, pl - ql - 1, r, 1); x = pgen("v", x, x, ev, ec, steps, pgen_filter, &pf, rabin_iters(pl - ql), pgen_test, &rb); if (!x) goto fail_0; - /* --- Second trick: find %$p$% and %$v$% --- */ + /* --- Second trick: find %$p$% and %$q$% --- */ x = mp_lsl(x, x, 1); sp[0].add = MP_ZERO; sp[0].mul = MP_ONE; sp[0].f = 0; - sp[1].add = MP_ONE; sp[1].mul = x; sp[1].f = PGENF_KEEP; + sp[1].add = MP_ONE; sp[1].mul = x; sp[1].f = PGENF_KEEP; x = MP_NEW; ss.step = MP_TWO; ss.v = sp; ss.n = N(sp); - x = mprand(MP_NEW, ql, r, 1); + do { + x = mprand(x, ql, r, 1); + t = mp_mul(t, x, sp[1].mul); + } while (mp_bits(t) != pl); dp->q = pgen("p", MP_NEW, x, ev, ec, steps, pgen_simulstep, &ss, rabin_iters(ql), pgen_simultest, &ss); mp_drop(sp[1].mul); + dp->p = sp[1].u.x; if (!dp->q) goto fail_1; - dp->p = sp[1].u.x; + if (mp_bits(dp->q) != ql || mp_bits(dp->p) != pl) { + if (steps) goto fail_1; + MP_DROP(dp->p); + MP_DROP(dp->q); + goto retry; + } /* --- Third trick: find a generator --- */ @@ -104,19 +114,20 @@ int dh_kcdsagen(dh_param *dp, unsigned ql, unsigned pl, 0, prim_step, &i, 1, prim_test, &pc); mpmont_destroy(&pc.mm); if (!dp->g) - goto fail_2; + goto fail_1; rc = PGEN_DONE; goto done; /* --- Tidying up and going home --- */ -fail_2: - mp_drop(dp->p); fail_1: + mp_drop(dp->p); + mp_drop(dp->q); fail_0: done: mp_drop(x); + mp_drop(t); return (rc); } diff --git a/pub/dh-param.c b/pub/dh-param.c index 69e24376..5b5e403b 100644 --- a/pub/dh-param.c +++ b/pub/dh-param.c @@ -108,6 +108,8 @@ found: #ifdef TEST_RIG +#include + #include "fibrand.h" int main(int argc, char *argv[]) @@ -127,7 +129,7 @@ int main(int argc, char *argv[]) dh_infofromdata(&dp, pe->data); g = group_prime(&dp); if (mp_bits(dp.p) > 3072 && - (!argv[1] || strcmp(argv[1], "keen") != 0)) { + (!argv[1] || STRCMP(argv[1], !=, "keen"))) { printf(" [%s skipped]", pe->name); fflush(stdout); continue; diff --git a/pub/dsa-gen.c b/pub/dsa-gen.c index 7c6c3ba8..8802003f 100644 --- a/pub/dsa-gen.c +++ b/pub/dsa-gen.c @@ -215,6 +215,8 @@ fail_q: #ifdef TEST_RIG +#include + static int verify(dstr *v) { mp *q = *(mp **)v[4].buf; @@ -231,7 +233,7 @@ static int verify(dstr *v) rc = dsa_gen(&dp, 160, l, 16, v[0].buf, v[0].len, &ds, pgen_evspin, 0); if (rc || ds.count != n || ds.sz != v[2].len || - memcmp(ds.p, v[2].buf, v[2].len) != 0 || + MEMCMP(ds.p, !=, v[2].buf, v[2].len) || !MP_EQ(q, dp.q) || !MP_EQ(p, dp.p) || !MP_EQ(g, dp.g)) { fputs("\n*** gen failed", stderr); fputs("\nseed_in = ", stderr); type_hex.dump(&v[0], stderr); diff --git a/pub/dsa-sign.c b/pub/dsa-sign.c index 7593a741..88054ebb 100644 --- a/pub/dsa-sign.c +++ b/pub/dsa-sign.c @@ -119,6 +119,7 @@ void dsa_sign(dsa_param *dp, mp *a, #ifdef TEST_RIG +#include #include #include "sha.h" @@ -145,8 +146,8 @@ static int verify(dstr *v) s.r, sizeof(s.r), s.s, sizeof(s.s)); if (v[6].len != sizeof(s.r) || v[7].len != sizeof(s.s) || - memcmp(s.r, v[6].buf, sizeof(s.r)) != 0 || - memcmp(s.s, v[7].buf, sizeof(s.s)) != 0) { + MEMCMP(s.r, !=, v[6].buf, sizeof(s.r)) || + MEMCMP(s.s, !=, v[7].buf, sizeof(s.s))) { fputs("\n*** signature failed", stderr); fputs("\nq = ", stderr); mp_writefile(dp.q, stderr, 16); fputs("\np = ", stderr); mp_writefile(dp.p, stderr, 16); diff --git a/pub/ed25519.c b/pub/ed25519.c index 676fe8ca..83741091 100644 --- a/pub/ed25519.c +++ b/pub/ed25519.c @@ -29,6 +29,8 @@ #include +#include + #include "f25519.h" #include "ed25519.h" #include "scaf.h" @@ -406,7 +408,7 @@ int ed25519ctx_verify(const octet K[ED25519_PUBSZ], scaf_loaddbl(tt, sig + 32, 32, 2*NPIECE, PIECEWD); scaf_reduce(s, tt, l, mu, NPIECE, PIECEWD, scratch); scaf_store(b, 32, s, NPIECE, PIECEWD); - if (memcmp(b, sig + 32, 32) != 0) return (-1); + if (MEMCMP(b, !=, sig + 32, 32)) return (-1); /* Check the signature. */ psz = prefix(b, phflag, p, psz); @@ -420,7 +422,7 @@ int ed25519ctx_verify(const octet K[ED25519_PUBSZ], scaf_reduce(t, tt, l, mu, NPIECE, PIECEWD, scratch); ptsimmul(&RX, &RY, &RZ, s, BX, BY, BZ, t, &AX, &AY, &AZ); ptencode(b, &RX, &RY, &RZ); - if (memcmp(b, sig, 32) != 0) return (-1); + if (MEMCMP(b, !=, sig, 32)) return (-1); /* All is good. */ return (0); @@ -454,7 +456,7 @@ static int vrf_pubkey(dstr dv[]) dstr_ensure(&dpub, ED25519_PUBSZ); dpub.len = ED25519_PUBSZ; ed25519_pubkey((octet *)dpub.buf, dv[0].buf, dv[0].len); ct_remedy(dpub.buf, dpub.len); - if (memcmp(dpub.buf, dv[1].buf, ED25519_PUBSZ) != 0) { + if (MEMCMP(dpub.buf, !=, dv[1].buf, ED25519_PUBSZ)) { ok = 0; fprintf(stderr, "failed!"); fprintf(stderr, "\n\tpriv = "); type_hex.dump(&dv[0], stderr); @@ -493,7 +495,7 @@ static int vrf_sign(dstr *priv, int phflag, dstr *perso, phflag, perso ? perso->buf : 0, perso ? perso->len : 0, m->buf, m->len); ct_remedy(dsig.buf, dsig.len); - if (memcmp(dsig.buf, want->buf, ED25519_SIGSZ) != 0) { + if (MEMCMP(dsig.buf, !=, want->buf, ED25519_SIGSZ)) { ok = 0; fprintf(stderr, "failed!"); fprintf(stderr, "\n\tpriv = "); type_hex.dump(priv, stderr); diff --git a/pub/ed448.c b/pub/ed448.c index 60328021..681a3ca2 100644 --- a/pub/ed448.c +++ b/pub/ed448.c @@ -29,6 +29,8 @@ #include +#include + #include "fgoldi.h" #include "ed448.h" #include "scaf.h" @@ -394,7 +396,7 @@ int ed448_verify(const octet K[ED448_PUBSZ], scaf_loaddbl(tt, sig + 57, 57, 2*NPIECE, PIECEWD); scaf_reduce(s, tt, l, mu, NPIECE, PIECEWD, scratch); scaf_store(b, 57, s, NPIECE, PIECEWD); - if (memcmp(b, sig + 57, 57) != 0) return (-1); + if (MEMCMP(b, !=, sig + 57, 57)) return (-1); /* Check the signature. */ psz = prefix(b, phflag, p, psz); @@ -408,7 +410,7 @@ int ed448_verify(const octet K[ED448_PUBSZ], scaf_reduce(t, tt, l, mu, NPIECE, PIECEWD, scratch); ptsimmul(&RX, &RY, &RZ, s, BX, BY, BZ, t, &AX, &AY, &AZ); ptencode(b, &RX, &RY, &RZ); - if (memcmp(b, sig, 57) != 0) return (-1); + if (MEMCMP(b, !=, sig, 57)) return (-1); /* All is good. */ return (0); @@ -437,7 +439,7 @@ static int vrf_pubkey(dstr dv[]) dstr_ensure(&dpub, ED448_PUBSZ); dpub.len = ED448_PUBSZ; ed448_pubkey((octet *)dpub.buf, dv[0].buf, dv[0].len); ct_remedy(dpub.buf, dpub.len); - if (memcmp(dpub.buf, dv[1].buf, ED448_PUBSZ) != 0) { + if (MEMCMP(dpub.buf, !=, dv[1].buf, ED448_PUBSZ)) { ok = 0; fprintf(stderr, "failed!"); fprintf(stderr, "\n\tpriv = "); type_hex.dump(&dv[0], stderr); @@ -476,7 +478,7 @@ static int vrf_sign(dstr *priv, int phflag, dstr *perso, phflag, perso ? perso->buf : 0, perso ? perso->len : 0, m->buf, m->len); ct_remedy(dsig.buf, dsig.len); - if (memcmp(dsig.buf, want->buf, ED448_SIGSZ) != 0) { + if (MEMCMP(dsig.buf, !=, want->buf, ED448_SIGSZ)) { ok = 0; fprintf(stderr, "failed!"); fprintf(stderr, "\n\tpriv = "); type_hex.dump(priv, stderr); diff --git a/pub/gkcdsa.c b/pub/gkcdsa.c index 94f22aa0..23994259 100644 --- a/pub/gkcdsa.c +++ b/pub/gkcdsa.c @@ -27,6 +27,8 @@ /*----- Header files ------------------------------------------------------*/ +#include + #include "dsa.h" #include "gkcdsa.h" #include "group.h" @@ -175,7 +177,7 @@ int gkcdsa_verify(const gkcdsa *c, const gkcdsa_sig *s, const void *m) e[1].base = g->g; e[1].exp = x; z = G_CREATE(g); G_MEXP(g, z, e, 2); h = hashge(g, c->h, z); p = GH_DONE(h, 0); - if (memcmp(p, s->r, hsz) == 0) rc = 0; + if (MEMCMP(p, ==, s->r, hsz)) rc = 0; mp_drop(x); mp_drop(y); G_DESTROY(g, z); GH_DESTROY(h); return (rc); } @@ -241,7 +243,7 @@ static int tsign(dstr *v) gkcdsa_endhash(&c, h); gkcdsa_sign(&c, &ss, GH_DONE(h, 0), k); GH_DESTROY(h); - if (memcmp(s.r, ss.r, c.h->hashsz) || !MP_EQ(s.s, ss.s)) { + if (MEMCMP(s.r, !=, ss.r, c.h->hashsz) || !MP_EQ(s.s, ss.s)) { ok = 0; fprintf(stderr, "*** sign failed!\n"); fprintf(stderr, "*** group: %s\n", v[0].buf); diff --git a/pub/pkcs1.c b/pub/pkcs1.c index 590c05e1..bb333514 100644 --- a/pub/pkcs1.c +++ b/pub/pkcs1.c @@ -31,6 +31,7 @@ #include #include +#include #include "ct.h" #include "grand.h" @@ -245,7 +246,7 @@ int pkcs1_sigdecode(mp *s, const void *m, size_t msz, octet *b, size_t sz, /* --- Check the encoding parameters --- */ - if (pp->ep && memcmp(q, pp->ep, pp->epsz) != 0) + if (pp->ep && MEMCMP(q, !=, pp->ep, pp->epsz)) return (-1); q += pp->epsz; diff --git a/pub/pss.c b/pub/pss.c index 8e77b78e..137b876f 100644 --- a/pub/pss.c +++ b/pub/pss.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "gcipher.h" #include "ghash.h" @@ -174,7 +175,7 @@ int pss_decode(mp *mi, const void *m, size_t msz, octet *b, size_t sz, GH_HASH(h, m, msz); GH_HASH(h, s, pp->ssz); s = GH_DONE(h, 0); - rc = !memcmp(s, r, hsz); + rc = MEMCMP(s, ==, r, hsz); GH_DESTROY(h); if (!rc) return (-1); diff --git a/pub/rsa-pub.c b/pub/rsa-pub.c index 5e13182d..bb402a3a 100644 --- a/pub/rsa-pub.c +++ b/pub/rsa-pub.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "mp.h" #include "mpmont.h" @@ -200,7 +201,7 @@ int rsa_verify(rsa_pubctx *rp, mp *s, const void *m, size_t msz, dstr_ensure(d, n); rc = e(p, m, msz, (octet *)d->buf + d->len, n, nb, earg); if (rc > 0 && m) { - if (rc != msz || memcmp(d->buf + d->len, m, msz) != 0) + if (rc != msz || MEMCMP(d->buf + d->len, !=, m, msz)) rc = -1; else rc = 0; diff --git a/pub/rsa-test.c b/pub/rsa-test.c index 34b2a1f7..2ee6bbc8 100644 --- a/pub/rsa-test.c +++ b/pub/rsa-test.c @@ -27,6 +27,8 @@ /*----- Header files ------------------------------------------------------*/ +#include + #include "fibrand.h" #include "rsa.h" @@ -69,7 +71,7 @@ static int tencpad(int nbits, #define tsigpad tencpad #define DSTR_EQ(x, y) \ - ((x)->len == (y)->len && !memcmp((x)->buf, (y)->buf, (x)->len)) + ((x)->len == (y)->len && MEMCMP((x)->buf, ==, (y)->buf, (x)->len)) static int tdecpad(int nbits, mp *c, int rc, dstr *p, diff --git a/pub/x25519.c b/pub/x25519.c index f8971298..cfdfea6f 100644 --- a/pub/x25519.c +++ b/pub/x25519.c @@ -111,6 +111,7 @@ void x25519(octet zz[X25519_OUTSZ], #include #include +#include #include #include @@ -131,7 +132,7 @@ static int vrf_x25519(dstr dv[]) (const octet *)dv[0].buf, (const octet *)dv[1].buf); ct_remedy(dz.buf, dz.len); - if (memcmp(dz.buf, dv[2].buf, X25519_OUTSZ) != 0) { + if (MEMCMP(dz.buf, !=, dv[2].buf, X25519_OUTSZ)) { ok = 0; fprintf(stderr, "failed!"); fprintf(stderr, "\n\t k = "); type_hex.dump(&dv[0], stderr); @@ -167,7 +168,7 @@ static int vrf_mct(dstr dv[]) } memcpy(d.buf, k, d.len); - if (memcmp(d.buf, dv[3].buf, d.len) != 0) { + if (MEMCMP(d.buf, !=, dv[3].buf, d.len)) { ok = 0; fprintf(stderr, "failed..."); fprintf(stderr, "\n\tinitial k = "); type_hex.dump(&dv[0], stderr); diff --git a/pub/x25519.h b/pub/x25519.h index 56008df3..192f8db0 100644 --- a/pub/x25519.h +++ b/pub/x25519.h @@ -41,6 +41,17 @@ * Since then, the name `Curve25519' has shifted somewhat, to refer to the * specific elliptic curve used, and the x-coordinate Diffie--Hellman * operation is now named `X25519'. + * + * The @x25519@ function essentially performs incompatible cofactor + * multiplication on the elliptic curve %$E(k)$% containing points %$(x, y)$% + * in %$\proj^2(k)$% satisfying the Montgomery-form equation + * + * %$y^3 = x^3 + 486662 x^2 + x$% , + * + * where $k = \gf{p}$, with $p = 2^{255} - 19$%. The curve has + * %$n = (p + 1) + 221938542218978828286815502327069187962$% points; this is + * eight times a prime %$\ell$%. The points with %$x$%-coordinate 9 have + * order %$\ell$%. */ /*----- Header files ------------------------------------------------------*/ diff --git a/pub/x448.c b/pub/x448.c index 6bef9dd3..70ec10f4 100644 --- a/pub/x448.c +++ b/pub/x448.c @@ -97,8 +97,8 @@ void x448(octet zz[X448_OUTSZ], #ifdef TEST_RIG +#include #include -#include #include #include "ct.h" @@ -118,7 +118,7 @@ static int vrf_x448(dstr dv[]) (const octet *)dv[0].buf, (const octet *)dv[1].buf); ct_remedy(dz.buf, dz.len); - if (memcmp(dz.buf, dv[2].buf, X448_OUTSZ) != 0) { + if (MEMCMP(dz.buf, !=, dv[2].buf, X448_OUTSZ)) { ok = 0; fprintf(stderr, "failed!"); fprintf(stderr, "\n\t k = "); type_hex.dump(&dv[0], stderr); @@ -153,7 +153,7 @@ static int vrf_mct(dstr dv[]) } memcpy(d.buf, k, d.len); - if (memcmp(d.buf, dv[3].buf, d.len) != 0) { + if (MEMCMP(d.buf, !=, dv[3].buf, d.len)) { ok = 0; fprintf(stderr, "failed..."); fprintf(stderr, "\n\tinitial k = "); type_hex.dump(&dv[0], stderr); diff --git a/pub/x448.h b/pub/x448.h index 4561d41a..42c9fb93 100644 --- a/pub/x448.h +++ b/pub/x448.h @@ -43,6 +43,18 @@ * described in Hamburg's paper, since it doesn't involve the `Decaf' * cofactor elimination procedure. Indeed, it looks very much like X25519 * with Hamburg's curve slotted in in place of Bernstein's. + * + * The @x448@ function essentially performs incompatible cofactor + * multiplication on the elliptic curve %$E(k)$% containing points %$(x, y)$% + * in %$\proj^2(k)$% satisfying the Montgomery-form equation + * + * %$y^3 = x^3 + 156326 x^2 + x$% , + * + * where $k = \gf{p}$, with $p = \phi^2 - \phi - 1$%, where + * %$\phi = 2^{224}$%. The curve has %$n = (p + 1) + {}$% + * %$28312320572429821613362531907042076847709625476988141958474579766324$% + * points; this is four times a prime %$\ell$%. The points with + * %$x$%-coordinate 5 have order %$\ell$%. */ /*----- Header files ------------------------------------------------------*/ diff --git a/rand/Makefile.am b/rand/Makefile.am index d97749e9..e02ccc59 100644 --- a/rand/Makefile.am +++ b/rand/Makefile.am @@ -67,6 +67,12 @@ librand_la_SOURCES += noise.c ## Cryptographic laundering for true random data generation. pkginclude_HEADERS += rand.h librand_la_SOURCES += rand.c +if CPUFAM_X86 +librand_la_SOURCES += rand-x86ish.S +endif +if CPUFAM_AMD64 +librand_la_SOURCES += rand-x86ish.S +endif librand_la_SOURCES += randgen.c ## The SSL v3 pseudorandom function. diff --git a/rand/dsarand.c b/rand/dsarand.c index 70ffaf81..a4328aa8 100644 --- a/rand/dsarand.c +++ b/rand/dsarand.c @@ -282,9 +282,11 @@ static int gmisc(grand *r, unsigned op, ...) grand *rr = va_arg(ap, grand *); rr->ops->fill(rr, g->d.p, g->d.sz); } break; - case DSARAND_PASSES: - g->d.passes = va_arg(ap, unsigned); - break; + case DSARAND_PASSES: { + unsigned n = va_arg(ap, unsigned); + rc = g->d.passes; + if (n > 0) g->d.passes = n; + } break; case DSARAND_SEEDSZ: rc = g->d.sz; break; diff --git a/rand/lcrand.c b/rand/lcrand.c index 1c76f65b..f916c9c5 100644 --- a/rand/lcrand.c +++ b/rand/lcrand.c @@ -33,6 +33,7 @@ #include #include +#include #include #include "grand.h" @@ -106,15 +107,11 @@ uint32 lcrand(uint32 x) /* --- Now reduce mod p --- * * - * I'm using shifts and adds to do the multiply step here. This needs to - * be changed if @D@ ever becomes something other than 5. + * I'm using shifts and adds to do the multiply step here. */ -#if D != 5 -# error "Change shift sequence!" -#endif - { + STATIC_ASSERT(D == 5, "Shift sequence doesn't match prime"); uint32 q; q = yy[1]; diff --git a/rand/rand-x86ish.S b/rand/rand-x86ish.S new file mode 100644 index 00000000..399ccb1d --- /dev/null +++ b/rand/rand-x86ish.S @@ -0,0 +1,151 @@ +/// -*- mode: asm; asm-comment-char: ?/ -*- +/// +/// Random-number support for x86 +/// +/// (c) 2019 Straylight/Edgeware +/// + +///----- Licensing notice --------------------------------------------------- +/// +/// This file is part of Catacomb. +/// +/// Catacomb is free software: you can redistribute it and/or modify it +/// under the terms of the GNU Library General Public License as published +/// by the Free Software Foundation; either version 2 of the License, or +/// (at your option) any later version. +/// +/// Catacomb is distributed in the hope that it will be useful, but +/// WITHOUT ANY WARRANTY; without even the implied warranty of +/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +/// Library General Public License for more details. +/// +/// You should have received a copy of the GNU Library General Public +/// License along with Catacomb. If not, write to the Free Software +/// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +/// USA. + +///-------------------------------------------------------------------------- +/// Preliminaries. + +#include "config.h" +#include "asm-common.h" + + .extern F(rand_add) + + .text + +///-------------------------------------------------------------------------- +/// Quick random generation. + +// Common register allocation. +#if CPUFAM_X86 +# define COUNT ecx +#endif +#if CPUFAM_AMD64 && ABI_SYSV +# define COUNT ecx +#endif +#if CPUFAM_AMD64 && ABI_WIN +# define COUNT r8d +#endif + +FUNC(rand_quick_x86ish_rdrand) + // Enter with a pointer to the random context in the first argument. + // Return zero on success, or -1 on error. + +#if CPUFAM_X86 + mov edx, [SP + 4] + push ebx + stalloc 24 +#endif +#if CPUFAM_AMD64 && ABI_SYSV + stalloc 8 +#endif +#if CPUFAM_AMD64 && ABI_WIN + stalloc 40 +#endif + endprologue + + // Try to fetch a random number. + mov COUNT, 16 +0: rdrand AX + jc 1f + dec COUNT + jnz 0b + + // Failed. + mov eax, -1 + jmp 9f +ENDFUNC + +FUNC(rand_quick_x86ish_rdseed) + // Enter with a pointer to the random context in the first argument. + // Return zero on success, or -1 on error. + +#if CPUFAM_X86 + mov edx, [SP + 4] + push ebx + stalloc 24 +#endif +#if CPUFAM_AMD64 && ABI_SYSV + stalloc 8 +#endif +#if CPUFAM_AMD64 && ABI_WIN + stalloc 40 +#endif + endprologue + + // Try to fetch a random number. + mov COUNT, 16 +0: rdseed AX + jc 1f + dec COUNT + jnz 0b + + // Failed. + mov eax, -1 + jmp 9f + + // Success. +1: +#if CPUFAM_X86 + ldgot ebx + mov [SP + 16], AX + lea ecx, [SP + 16] + mov dword ptr [SP + 12], 32 + mov dword ptr [SP + 8], 4 + mov [SP + 4], ecx + mov [SP + 0], edx +#endif +#if CPUFAM_AMD64 && ABI_SYSV + mov [SP + 0], AX + mov rsi, SP + mov edx, 8 + mov ecx, 64 +#endif +#if CPUFAM_AMD64 && ABI_WIN + mov [SP + 32], AX + lea rdx, [SP + 32] + mov r8d, 8 + mov r9d, 64 +#endif + callext F(rand_add) + xor eax, eax + + // Done. +9: +#if CPUFAM_X86 + stfree 24 + pop ebx +#endif +#if CPUFAM_AMD64 && ABI_SYSV + stfree 8 +#endif +#if CPUFAM_AMD64 && ABI_WIN + stfree 40 +#endif + ret +ENDFUNC + +#undef COUNT + +///----- That's all, folks -------------------------------------------------- diff --git a/rand/rand.c b/rand/rand.c index 01e6422a..90e81930 100644 --- a/rand/rand.c +++ b/rand/rand.c @@ -161,24 +161,17 @@ CPU_DISPATCH(static, return, int, quick, (rand_pool *r), (r), static int trivial_quick(rand_pool *r) { return (-1); } -#if __GNUC__ && (CPUFAM_X86 || CPUFAM_AMD64) -static int rdrand_quick(rand_pool *r) -{ - unsigned long rr; - int i = 16; - - __asm__ ("0: rdrand %0; jc 9f; dec %1; jnz 0b; 9:" - : "=r" (rr), "=r" (i) : "1" (i) : "cc"); - if (!i) return (-1); - rand_add(r, &rr, sizeof(rr), 8*sizeof(rr)); - return (0); -} +#if CPUFAM_X86 || CPUFAM_AMD64 +extern int rand_quick_x86ish_rdrand(rand_pool */*r*/); +extern int rand_quick_x86ish_rdseed(rand_pool */*r*/); #endif static quick__functype *pick_quick(void) { -#if __GNUC__ && (CPUFAM_X86 || CPUFAM_AMD64) - DISPATCH_PICK_COND(rand_quick, rdrand_quick, +#if CPUFAM_X86 || CPUFAM_AMD64 + DISPATCH_PICK_COND(rand_quick, rand_quick_x86ish_rdseed, + cpu_feature_p(CPUFEAT_X86_RDSEED)); + DISPATCH_PICK_COND(rand_quick, rand_quick_x86ish_rdrand, cpu_feature_p(CPUFEAT_X86_RDRAND)); #endif DISPATCH_PICK_FALLBACK(rand_quick, trivial_quick); @@ -261,9 +254,7 @@ void rand_add(rand_pool *r, const void *p, size_t sz, unsigned goodbits) const octet *c = p; int i, rot; -#if RAND_POOLSZ != 128 -# error Polynomial in rand_add is out of date. Fix it. -#endif + STATIC_ASSERT(RAND_POOLSZ == 128, "Polynomial doesn't match pool size"); RAND_RESOLVE(r); @@ -316,6 +307,8 @@ void rand_gate(rand_pool *r) HASH_CTX hc; CIPHER_CTX cc; + STATIC_ASSERT(CIPHER_KEYSZ <= HASH_SZ, "rand cipher keysize too long"); + RAND_RESOLVE(r); QUICK(r); @@ -331,7 +324,6 @@ void rand_gate(rand_pool *r) /* --- Now mangle all of the data based on the hash --- */ - assert(CIPHER_KEYSZ <= HASH_SZ); CIPHER_INIT(&cc, h, CIPHER_KEYSZ, 0); CIPHER_ENCRYPT(&cc, r->pool, r->pool, RAND_POOLSZ); CIPHER_ENCRYPT(&cc, r->buf, r->buf, RAND_BUFSZ); @@ -367,6 +359,8 @@ void rand_stretch(rand_pool *r) HASH_CTX hc; CIPHER_CTX cc; + STATIC_ASSERT(CIPHER_KEYSZ <= HASH_SZ, "rand cipher keysize too long"); + RAND_RESOLVE(r); QUICK(r); @@ -382,7 +376,6 @@ void rand_stretch(rand_pool *r) /* --- Now mangle the buffer based on the hash --- */ - assert(CIPHER_KEYSZ <= HASH_SZ); CIPHER_INIT(&cc, h, CIPHER_KEYSZ, 0); CIPHER_ENCRYPT(&cc, r->buf, r->buf, RAND_BUFSZ); BURN(cc); diff --git a/rand/sslprf.c b/rand/sslprf.c index 06187b39..9601f4ec 100644 --- a/rand/sslprf.c +++ b/rand/sslprf.c @@ -310,6 +310,7 @@ grand *sslprf_rand(const gchash *hco, const gchash *hci, #include #include +#include #include #include @@ -327,7 +328,7 @@ static int v_generate(dstr *v) d.len = v[2].len; g->ops->fill(g, d.buf, d.len); g->ops->destroy(g); - if (memcmp(v[2].buf, d.buf, d.len) != 0) { + if (MEMCMP(v[2].buf, !=, d.buf, d.len)) { ok = 0; printf("\nfail sslprf:" "\n\tkey = "); diff --git a/rand/tlsprf.c b/rand/tlsprf.c index fa28faf4..95f5b945 100644 --- a/rand/tlsprf.c +++ b/rand/tlsprf.c @@ -493,6 +493,7 @@ grand *tlsprf_rand(const gcmac *mcx, const gcmac *mcy, #include #include +#include #include #include @@ -511,7 +512,7 @@ static int v_generate(dstr *v) d.len = v[2].len; g->ops->fill(g, d.buf, d.len); g->ops->destroy(g); - if (memcmp(v[2].buf, d.buf, d.len) != 0) { + if (MEMCMP(v[2].buf, !=, d.buf, d.len)) { ok = 0; printf("\nfail tlsprf:" "\n\tkey = "); diff --git a/symm/Makefile.am b/symm/Makefile.am index 68d9267a..e87d7411 100644 --- a/symm/Makefile.am +++ b/symm/Makefile.am @@ -476,6 +476,12 @@ $(srcdir)/t/sha3: $(SHA3_TESTS) sha3-trans t/sha3.local cat t/sha3.local; } >t/sha3.new && \ mv t/sha3.new t/sha3 +## Mike Hamburg's STROBE protocol framework. +pkginclude_HEADERS += strobe.h +libsymm_la_SOURCES += strobe.c +TESTS += strobe.t$(EXEEXT) +EXTRA_DIST += t/strobe + ## Bellare, Canetti and Krawczyk's `HMAC' mode for message authentication. HASHMACMODES += hmac @@ -587,7 +593,7 @@ pkginclude_HEADERS += poly1305.h libsymm_la_SOURCES += poly1305.c TESTS += poly1305.t$(EXEEXT) TESTS += poly1305-p11.t$(EXEEXT) -EXTRA_DIST += t/poly1305 +EXTRA_DIST += t/poly1305 t/poly1305.slow check_PROGRAMS += poly1305-p11.t poly1305_p11_t_SOURCES = poly1305.c diff --git a/symm/blkc.h b/symm/blkc.h index bbba7631..444ec8d7 100644 --- a/symm/blkc.h +++ b/symm/blkc.h @@ -400,9 +400,14 @@ #include +#include #include #include +#ifndef BLKC_TESTHOOK +# define BLKC_TESTHOOK do ; while (0) +#endif + #define BLKC_VERIFY(PRE, pre) BLKC_VERIFYX(PRE, pre, #pre) #define BLKC_VERIFYX(PRE, pre, name) \ @@ -429,7 +434,7 @@ static int pre##_verify(dstr *v) \ BLKC_MOVE(PRE, d, p); \ pre##_eblk(&k, d, d); \ BLKC_STORE(PRE, b.buf, d); \ - if (memcmp(b.buf, v[2].buf, PRE##_BLKSZ)) { \ + if (MEMCMP(b.buf, !=, v[2].buf, PRE##_BLKSZ)) { \ ok = 0; \ printf("\nfail encryption:" \ "\n\tkey = "); \ @@ -445,7 +450,7 @@ static int pre##_verify(dstr *v) \ BLKC_MOVE(PRE, d, c); \ pre##_dblk(&k, d, d); \ BLKC_STORE(PRE, b.buf, d); \ - if (memcmp(b.buf, v[1].buf, PRE##_BLKSZ)) { \ + if (MEMCMP(b.buf, !=, v[1].buf, PRE##_BLKSZ)) { \ ok = 0; \ printf("\nfail decryption:" \ "\n\tkey = "); \ @@ -477,6 +482,7 @@ static const test_chunk defs[] = { \ \ int main(int argc, char *argv[]) \ { \ + BLKC_TESTHOOK; \ test_run(argc, argv, defs, SRCDIR"/t/" fname); \ return (0); \ } diff --git a/symm/ccm-def.h b/symm/ccm-def.h index 2d864fa6..7c6bd2ed 100644 --- a/symm/ccm-def.h +++ b/symm/ccm-def.h @@ -338,9 +338,9 @@ int pre##_ccmencrypt(pre##_ccmctx *ctx, \ /* Determine the buffering plan. Our buffer is going to do double- \ * duty here. The end portion is going to contain mask from the \ * encrypted counter which we mix into the plaintext to encrypt it; \ - * the start portion, which originally mask bytes we've already used, \ - * will hold the input plaintext, which will eventually be \ - * collected into the CBC-MAC state. \ + * the start portion, which originally contained mask bytes we've \ + * already used, will hold the input plaintext, which will \ + * eventually be collected into the CBC-MAC state. \ */ \ rsvr_mkplan(&plan, &pre##_ccmpolicy, ctx->off, sz); \ \ @@ -427,9 +427,9 @@ int pre##_ccmdecrypt(pre##_ccmctx *ctx, \ /* Determine the buffering plan. Our buffer is going to do double- \ * duty here. The end portion is going to contain mask from the \ * encrypted counter which we mix into the plaintext to encrypt it; \ - * the start portion, which originally mask bytes we've already used, \ - * will hold the recovered plaintext, which will eventually be \ - * collected into the CBC-MAC state. \ + * the start portion, which originally mask contained bytes we've \ + * already used, will hold the recovered plaintext, which will \ + * eventually be collected into the CBC-MAC state. \ */ \ rsvr_mkplan(&plan, &pre##_ccmpolicy, ctx->off, sz); \ \ @@ -715,13 +715,23 @@ static gaead_key *gckey(const void *k, size_t ksz) \ return (&key->k); \ } \ \ +static int gcszok(size_t nsz, size_t hsz, size_t msz, size_t tsz) \ +{ \ + ccm_params p; \ + \ + if (!gaead_szokcommon(&pre##_ccm, nsz, hsz, msz, tsz)) return (0); \ + p.hsz = hsz; p.msz = msz; p.bsz = PRE##_BLKSZ; p.nsz = nsz; p.tsz = tsz; \ + if (!ccm_check(&p)) return (0); \ + return (1); \ +} \ + \ const gcaead pre##_ccm = { \ name "-ccm", \ pre##_keysz, pre##_ccmnoncesz, pre##_ccmtagsz, \ PRE##_BLKSZ, 0, 0, \ AEADF_PCHSZ | AEADF_PCMSZ | AEADF_PCTSZ | \ AEADF_AADNDEP | AEADF_AADFIRST, \ - gckey \ + gckey, gcszok \ }; \ \ CCM_TESTX(PRE, pre, name, fname) @@ -742,6 +752,7 @@ CCM_TESTX(PRE, pre, name, fname) #include #include +#include #include #include @@ -802,8 +813,8 @@ static int ccmverify(dstr *v) \ d.len = BLEN(&b); \ \ if (d.len != v[4].len || \ - memcmp(d.buf, v[4].buf, v[4].len) != 0 || \ - memcmp(t.buf, v[5].buf, v[5].len) != 0) { \ + MEMCMP(d.buf, !=, v[4].buf, v[4].len) || \ + MEMCMP(t.buf, !=, v[5].buf, v[5].len)) { \ fail_enc: \ printf("\nfail encrypt:\n\tstep = %i", *ip); \ fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \ @@ -855,7 +866,7 @@ static int ccmverify(dstr *v) \ d.len = BLEN(&b); \ \ if (d.len != v[3].len || !win || \ - memcmp(d.buf, v[3].buf, v[3].len) != 0) { \ + MEMCMP(d.buf, !=, v[3].buf, v[3].len)) { \ fail_dec: \ printf("\nfail decrypt:\n\tstep = %i", *ip); \ fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \ diff --git a/symm/ccm.c b/symm/ccm.c index d65e0490..939335c4 100644 --- a/symm/ccm.c +++ b/symm/ccm.c @@ -81,7 +81,7 @@ * Then H = F || N || Q and C_i = 0^9 || [q - 1]_7 || N || [i]_{8q}. */ -/* --- @ccm_paramsok@ --- * +/* --- @ccm_check@ --- * * * Arguments: @const ccm_params *p@ = pointer to parameters * diff --git a/symm/chacha-arm-neon.S b/symm/chacha-arm-neon.S index a900db76..2160def6 100644 --- a/symm/chacha-arm-neon.S +++ b/symm/chacha-arm-neon.S @@ -36,7 +36,7 @@ .text ///-------------------------------------------------------------------------- -/// Main.code. +/// Main code. FUNC(chacha_core_arm_neon) diff --git a/symm/chacha-arm64.S b/symm/chacha-arm64.S index 61ac51a1..9fe39c92 100644 --- a/symm/chacha-arm64.S +++ b/symm/chacha-arm64.S @@ -35,7 +35,7 @@ .text ///-------------------------------------------------------------------------- -/// Main.code. +/// Main code. FUNC(chacha_core_arm64) diff --git a/symm/chacha-x86ish-sse2.S b/symm/chacha-x86ish-sse2.S index 3fb623af..974ec5b5 100644 --- a/symm/chacha-x86ish-sse2.S +++ b/symm/chacha-x86ish-sse2.S @@ -66,15 +66,15 @@ FUNC(chacha_core_x86ish_sse2) # define SAVE0 xmm5 # define SAVE1 xmm6 # define SAVE2 xmm7 -# define SAVE3 [esp] +# define SAVE3 [SP] - pushreg ebp + pushreg BP setfp - sub esp, 16 - mov IN, [ebp + 12] - mov OUT, [ebp + 16] - and esp, ~15 - mov NR, [ebp + 8] + stalloc 16 + mov IN, [BP + 12] + mov OUT, [BP + 16] + and SP, ~15 + mov NR, [BP + 8] #endif #if CPUFAM_AMD64 && ABI_SYSV @@ -105,9 +105,9 @@ FUNC(chacha_core_x86ish_sse2) # define IN rdx # define OUT r8 # define SAVE0 xmm5 -# define SAVE1 [rsp + 0] -# define SAVE2 [rsp + 16] -# define SAVE3 [rsp + 32] +# define SAVE1 [SP + 0] +# define SAVE2 [SP + 16] +# define SAVE3 [SP + 32] stalloc 48 + 8 #endif @@ -248,7 +248,7 @@ FUNC(chacha_core_x86ish_sse2) // Tidy things up. #if CPUFAM_X86 dropfp - popreg ebp + popreg BP #endif #if CPUFAM_AMD64 && ABI_WIN stfree 48 + 8 diff --git a/symm/chacha.c b/symm/chacha.c index 90a4c674..8cc16b8d 100644 --- a/symm/chacha.c +++ b/symm/chacha.c @@ -849,9 +849,14 @@ CHACHA_VARS(DEFXGRAND) #include #include +#include #include #include +#ifdef ENABLE_ASM_DEBUG +# include "regdump.h" +#endif + #define DEFVCORE(r) \ static int v_core_##r(dstr *v) \ { \ @@ -870,7 +875,7 @@ CHACHA_VARS(DEFXGRAND) } \ for (i = 0; i < CHACHA_OUTSZ/4; i++) STORE32_L(d.buf + 4*i, a[i]); \ \ - if (d.len != v[2].len || memcmp(d.buf, v[2].buf, v[2].len) != 0) { \ + if (d.len != v[2].len || MEMCMP(d.buf, !=, v[2].buf, v[2].len)) { \ ok = 0; \ printf("\nfail core:" \ "\n\titerations = %d" \ @@ -940,7 +945,7 @@ CHACHA_VARS(DEFVCORE) } \ if (sz) BASE##_ENCRYPT(r, &ctx, p, q, sz); \ \ - if (d.len != v[5].len || memcmp(d.buf, v[5].buf, v[5].len) != 0) { \ + if (d.len != v[5].len || MEMCMP(d.buf, !=, v[5].buf, v[5].len)) { \ ok = 0; \ printf("\nfail encrypt:" \ "\n\tstep = %lu" \ @@ -986,6 +991,9 @@ CHACHA_VARS(DEFXTAB) int main(int argc, char *argv[]) { +#ifdef ENABLE_ASM_DEBUG + regdump_init(); +#endif test_run(argc, argv, defs, SRCDIR"/t/chacha"); return (0); } diff --git a/symm/cmac-def.h b/symm/cmac-def.h index 701d3b52..ee8040a7 100644 --- a/symm/cmac-def.h +++ b/symm/cmac-def.h @@ -323,6 +323,7 @@ CMAC_TESTX(PRE, pre, name, fname) #include #include +#include #include #include @@ -362,7 +363,7 @@ static int macverify(dstr *v) \ csz -= i; \ } \ pre##_cmacdone(&cctx, d.buf); \ - if (memcmp(d.buf, v[2].buf, PRE##_BLKSZ) != 0) { \ + if (MEMCMP(d.buf, !=, v[2].buf, PRE##_BLKSZ)) { \ printf("\nfail:\n\tstep = %i\n\tkey = ", *ip); \ type_hex.dump(&v[0], stdout); \ fputs("\n\tinput = ", stdout); \ diff --git a/symm/eax-def.h b/symm/eax-def.h index 6e1c7ca4..8daf2d8c 100644 --- a/symm/eax-def.h +++ b/symm/eax-def.h @@ -123,7 +123,7 @@ void pre##_eaxsetkey(pre##_eaxkey *key, const void *k, size_t ksz) \ * \ * * %$z_i = E_K(t_0 \xor m_0)$% is the tweak with the `full final \ * buffer' mask applied, which is the final tag for a final empty \ - * message. \ + * message. \ */ \ BLKC_BSET(PRE, t, 0); pre##_eblk(&key->ctx, t, key->v0); \ BLKC_XMOVE(PRE, t, key->m0); pre##_eblk(&key->ctx, t, key->z0); \ @@ -275,9 +275,9 @@ int pre##_eaxencrypt(pre##_eaxctx *ctx, \ /* Determine the buffering plan. Our buffer is going to do double- \ * duty here. The end portion is going to contain mask from the \ * encrypted counter which we mix into the plaintext to encrypt it; \ - * the start portion, which originally mask bytes we've already used, \ - * will hold the output ciphertext, which will eventually be \ - * collected into the OMAC state. \ + * the start portion, which originally contained mask bytes we've \ + * already used, will hold the output ciphertext, which will \ + * eventually be collected into the OMAC state. \ */ \ rsvr_mkplan(&plan, &pre##_omacpolicy, ctx->off, sz); \ \ @@ -362,9 +362,9 @@ int pre##_eaxdecrypt(pre##_eaxctx *ctx, \ /* Determine the buffering plan. Our buffer is going to do double- \ * duty here. The end portion is going to contain mask from the \ * encrypted counter which we mix into the plaintext to encrypt it; \ - * the start portion, which originally mask bytes we've already used, \ - * will hold the input ciphertext, which will eventually be \ - * collected into the OMAC state. \ + * the start portion, which originally contained mask bytes we've \ + * already used, will hold the input ciphertext, which will \ + * eventually be collected into the OMAC state. \ */ \ rsvr_mkplan(&plan, &pre##_omacpolicy, ctx->off, sz); \ \ @@ -675,11 +675,14 @@ static gaead_key *gckey(const void *k, size_t ksz) \ return (&key->k); \ } \ \ +static int gcszok(size_t nsz, size_t hsz, size_t msz, size_t tsz) \ + { return (gaead_szokcommon(&pre##_eax, nsz, hsz, msz, tsz)); } \ + \ const gcaead pre##_eax = { \ name "-eax", \ pre##_keysz, pre##_eaxnoncesz, pre##_eaxtagsz, \ PRE##_BLKSZ, 0, 0, 0, \ - gckey \ + gckey, gcszok \ }; \ \ EAX_TESTX(PRE, pre, name, fname) @@ -700,6 +703,7 @@ EAX_TESTX(PRE, pre, name, fname) #include #include +#include #include #include @@ -761,8 +765,8 @@ static int eaxverify(dstr *v) \ d.len = BLEN(&b); \ \ if (d.len != v[4].len || \ - memcmp(d.buf, v[4].buf, v[4].len) != 0 || \ - memcmp(t.buf, v[5].buf, v[5].len) != 0) { \ + MEMCMP(d.buf, !=, v[4].buf, v[4].len) || \ + MEMCMP(t.buf, !=, v[5].buf, v[5].len)) { \ fail_enc: \ printf("\nfail encrypt:\n\tstep = %i", *ip); \ fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \ @@ -803,7 +807,7 @@ static int eaxverify(dstr *v) \ d.len = BLEN(&b); \ \ if (d.len != v[3].len || !win || \ - memcmp(d.buf, v[3].buf, v[3].len) != 0) { \ + MEMCMP(d.buf, !=, v[3].buf, v[3].len)) { \ fail_dec: \ printf("\nfail decrypt:\n\tstep = %i", *ip); \ fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \ diff --git a/symm/gaead.c b/symm/gaead.c index 45aeb614..d5130693 100644 --- a/symm/gaead.c +++ b/symm/gaead.c @@ -31,6 +31,27 @@ /*----- Main code ---------------------------------------------------------*/ +/* --- @gaead_szokcommon@ --- * + * + * Arguments: @const gcaead *aec@ = pointer to AEAD class + * @size_t nsz@, @size_t hsz@, @size_t msz@, @size_t tsz@ = + * nonce, header, message, and tag sizes + * + * Returns: Nonzero if the sizes are acceptable to the AEAD scheme in + * combination. + * + * Use: Generic implementation for sensible AEAD schemes. + */ + +int gaead_szokcommon(const gcaead *aec, + size_t nsz, size_t hsz, size_t msz, size_t tsz) +{ + if (keysz(nsz, aec->noncesz) != nsz) return (0); + if (keysz(tsz, aec->tagsz) != tsz) return (0); + if (hsz && (aec->f&AEADF_NOAAD)) return (0); + return (1); +} + /* --- @gaead_encrypt@ --- * * * Arguments: @const gaead_key *k@ = the AEAD key, already prepared diff --git a/symm/gcm-def.h b/symm/gcm-def.h index 34f95aa1..ffa008c3 100644 --- a/symm/gcm-def.h +++ b/symm/gcm-def.h @@ -392,9 +392,9 @@ int pre##_gcmencrypt(pre##_gcmctx *ctx, \ /* Determine the buffering plan. Our buffer is going to do double- \ * duty here. The end portion is going to contain mask from the \ * encrypted counter which we mix into the plaintext to encrypt it; \ - * the start portion, which originally mask bytes we've already used, \ - * will hold the output ciphertext, which will eventually be \ - * collected into the GHASH state. \ + * the start portion, which originally contained mask bytes we've \ + * already used, will hold the output ciphertext, which will \ + * eventually be collected into the GHASH state. \ */ \ rsvr_mkplan(&plan, &pre##_gcmpolicy, ctx->off, sz); \ \ @@ -477,9 +477,9 @@ int pre##_gcmdecrypt(pre##_gcmctx *ctx, \ /* Determine the buffering plan. Our buffer is going to do double- \ * duty here. The end portion is going to contain mask from the \ * encrypted counter which we mix into the plaintext to encrypt it; \ - * the start portion, which originally mask bytes we've already used, \ - * will hold the input ciphertext, which will eventually be \ - * collected into the GHASH state. \ + * the start portion, which originally contained mask bytes we've \ + * already used, will hold the input ciphertext, which will \ + * eventually be collected into the GHASH state. \ */ \ rsvr_mkplan(&plan, &pre##_gcmpolicy, ctx->off, sz); \ \ @@ -790,11 +790,14 @@ static gaead_key *gckey(const void *k, size_t ksz) \ return (&key->k); \ } \ \ +static int gcszok(size_t nsz, size_t hsz, size_t msz, size_t tsz) \ + { return (gaead_szokcommon(&pre##_gcm, nsz, hsz, msz, tsz)); } \ + \ const gcaead pre##_gcm = { \ name "-gcm", \ pre##_keysz, pre##_gcmnoncesz, pre##_gcmtagsz, \ PRE##_BLKSZ, 0, 0, 0, \ - gckey \ + gckey, gcszok \ }; \ \ GCM_TESTX(PRE, pre, name, fname) @@ -815,6 +818,7 @@ GCM_TESTX(PRE, pre, name, fname) #include #include +#include #include #include @@ -876,8 +880,8 @@ static int gcmverify(dstr *v) \ d.len = BLEN(&b); \ \ if (d.len != v[4].len || \ - memcmp(d.buf, v[4].buf, v[4].len) != 0 || \ - memcmp(t.buf, v[5].buf, v[5].len) != 0) { \ + MEMCMP(d.buf, !=, v[4].buf, v[4].len) || \ + MEMCMP(t.buf, !=, v[5].buf, v[5].len)) { \ fail_enc: \ printf("\nfail encrypt:\n\tstep = %i", *ip); \ fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \ @@ -918,7 +922,7 @@ static int gcmverify(dstr *v) \ d.len = BLEN(&b); \ \ if (d.len != v[3].len || !win || \ - memcmp(d.buf, v[3].buf, v[3].len) != 0) { \ + MEMCMP(d.buf, !=, v[3].buf, v[3].len)) { \ fail_dec: \ printf("\nfail decrypt:\n\tstep = %i", *ip); \ fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \ diff --git a/symm/gcm-x86ish-pclmul.S b/symm/gcm-x86ish-pclmul.S index e60b7cab..5edf56e1 100644 --- a/symm/gcm-x86ish-pclmul.S +++ b/symm/gcm-x86ish-pclmul.S @@ -576,7 +576,7 @@ // xmm3 = // v_0 = (v_01; v_00) movdqa xmm4, xmm0 // u_1 again #if CPUFAM_X86 - movdqa [esp + 0], xmm3 + movdqa [SP + 0], xmm3 #elif CPUFAM_AMD64 movdqa xmm8, xmm3 # define V0 xmm8 @@ -608,7 +608,7 @@ pclmullqlqdq xmm4, xmm2 // u_11 v_11 pclmulhqhqdq xmm7, xmm2 // u_10 v_10 #if CPUFAM_X86 - movdqa xmm2, [esp + 0] + movdqa xmm2, [SP + 0] # define V0 xmm2 #endif pxor xmm0, xmm3 // u_10 v_11 + u_11 v_10 @@ -771,8 +771,8 @@ SSEFUNC(gcm_mulk_128b_x86ish_pclmul) // A is updated with the product A K. #if CPUFAM_X86 - mov A, [esp + 4] - mov K, [esp + 8] + mov A, [SP + 4] + mov K, [SP + 8] #endif endprologue movdqu xmm0, [A] @@ -790,8 +790,8 @@ SSEFUNC(gcm_mulk_128l_x86ish_pclmul) // exit, A is updated with the product A K. #if CPUFAM_X86 - mov A, [esp + 4] - mov K, [esp + 8] + mov A, [SP + 4] + mov K, [SP + 8] ldgot ecx #endif endprologue @@ -811,8 +811,8 @@ SSEFUNC(gcm_mulk_64b_x86ish_pclmul) // A is updated with the product A K. #if CPUFAM_X86 - mov A, [esp + 4] - mov K, [esp + 8] + mov A, [SP + 4] + mov K, [SP + 8] #endif endprologue movq xmm0, [A] @@ -830,8 +830,8 @@ SSEFUNC(gcm_mulk_64l_x86ish_pclmul) // exit, A is updated with the product A K. #if CPUFAM_X86 - mov A, [esp + 4] - mov K, [esp + 8] + mov A, [SP + 4] + mov K, [SP + 8] ldgot ecx #endif endprologue @@ -852,8 +852,8 @@ SSEFUNC(gcm_mulk_96b_x86ish_pclmul) // with the product A K. #if CPUFAM_X86 - mov A, [esp + 4] - mov K, [esp + 8] + mov A, [SP + 4] + mov K, [SP + 8] #endif endprologue movq xmm0, [A + 0] @@ -876,8 +876,8 @@ SSEFUNC(gcm_mulk_96l_x86ish_pclmul) // updated with the product A K. #if CPUFAM_X86 - mov A, [esp + 4] - mov K, [esp + 8] + mov A, [SP + 4] + mov K, [SP + 8] ldgot ecx #endif endprologue @@ -901,8 +901,8 @@ SSEFUNC(gcm_mulk_192b_x86ish_pclmul) // A is updated with the product A K. #if CPUFAM_X86 - mov A, [esp + 4] - mov K, [esp + 8] + mov A, [SP + 4] + mov K, [SP + 8] #endif #if CPUFAM_AMD64 && ABI_WIN stalloc 2*16 + 8 @@ -935,8 +935,8 @@ SSEFUNC(gcm_mulk_192l_x86ish_pclmul) // exit, A is updated with the product A K. #if CPUFAM_X86 - mov A, [esp + 4] - mov K, [esp + 8] + mov A, [SP + 4] + mov K, [SP + 8] ldgot ecx #endif #if CPUFAM_AMD64 && ABI_WIN @@ -970,12 +970,12 @@ SSEFUNC(gcm_mulk_256b_x86ish_pclmul) // A is updated with the product A K. #if CPUFAM_X86 - pushreg ebp + pushreg BP setfp - mov A, [esp + 8] - mov K, [esp + 12] - and esp, ~15 - sub esp, 16 + mov A, [SP + 8] + mov K, [SP + 12] + stalloc 16 + and SP, ~15 #endif #if CPUFAM_AMD64 && ABI_WIN stalloc 3*16 + 8 @@ -997,7 +997,7 @@ SSEFUNC(gcm_mulk_256b_x86ish_pclmul) movdqu [A + 0], xmm1 #if CPUFAM_X86 dropfp - popreg ebp + popreg BP #endif #if CPUFAM_AMD64 && ABI_WIN rstrxmm xmm6, 0 @@ -1014,13 +1014,13 @@ SSEFUNC(gcm_mulk_256l_x86ish_pclmul) // exit, A is updated with the product A K. #if CPUFAM_X86 - pushreg ebp + pushreg BP setfp - mov A, [esp + 8] - mov K, [esp + 12] - and esp, ~15 + mov A, [SP + 8] + mov K, [SP + 12] + stalloc 16 ldgot ecx - sub esp, 16 + and SP, ~15 #endif #if CPUFAM_AMD64 && ABI_WIN stalloc 3*16 + 8 @@ -1044,7 +1044,7 @@ SSEFUNC(gcm_mulk_256l_x86ish_pclmul) movdqu [A + 0], xmm1 #if CPUFAM_X86 dropfp - popreg ebp + popreg BP #endif #if CPUFAM_AMD64 && ABI_WIN rstrxmm xmm6, 0 diff --git a/symm/gcm.c b/symm/gcm.c index 73b28517..b4384153 100644 --- a/symm/gcm.c +++ b/symm/gcm.c @@ -778,9 +778,14 @@ void gcm_concat(const gcm_params *p, uint32 *z, const uint32 *x, #ifdef TEST_RIG +#include #include #include +#ifdef ENABLE_ASM_DEBUG +# include "regdump.h" +#endif + static void report_failure(const char *test, unsigned nbits, const char *ref, dstr v[], dstr *d) { @@ -820,7 +825,7 @@ static int test_mul(uint32 poly, dstr v[]) #define CHECK(E, what, ref) do { \ for (i = 0; i < nbits/32; i++) STORE32_##E(d.buf + 4*i, z[i]); \ - if (memcmp(d.buf, v[I_##ref].buf, nbits/8) != 0) \ + if (MEMCMP(d.buf, !=, v[I_##ref].buf, nbits/8)) \ { ok = 0; report_failure(what, nbits, #ref, v, &d); } \ } while (0) @@ -873,6 +878,9 @@ GCM_WIDTHS(TEST) int main(int argc, char *argv[]) { ego(argv[0]); +#ifdef ENABLE_ASM_DEBUG + regdump_init(); +#endif test_run(argc, argv, defs, SRCDIR"/t/gcm"); return (0); } diff --git a/symm/gthingtab.c.in b/symm/gthingtab.c.in index 29b733b3..92fe792b 100644 --- a/symm/gthingtab.c.in +++ b/symm/gthingtab.c.in @@ -9,6 +9,8 @@ #include +#include + #include "@what.h" %repeat @@ -27,6 +29,6 @@ const @cls *@{what}_byname(const char *p) const @cls *const *c; for (c = @{what}tab; *c; c++) - if (strcmp(p, (*c)->name) == 0) return (*c); + if (STRCMP(p, ==, (*c)->name)) return (*c); return (0); } diff --git a/symm/hash.h b/symm/hash.h index 3d0c1179..5b2e7fb8 100644 --- a/symm/hash.h +++ b/symm/hash.h @@ -138,6 +138,7 @@ #ifdef TEST_RIG +#include #include #include @@ -178,7 +179,7 @@ static int vrf_##pre(dstr *v, const test_type *msgty) \ sz -= i; \ } \ pre##_done(&ctx, d.buf); \ - if (memcmp(d.buf, v[1].buf, PRE##_HASHSZ) != 0) { \ + if (MEMCMP(d.buf, !=, v[1].buf, PRE##_HASHSZ)) { \ printf("\nfail:\n\tstep = %i\n\tinput = ", *ip); \ msgty->dump(&v[0], stdout); \ printf("\n\texpected = "); \ @@ -222,7 +223,7 @@ static int vrf_##pre##_rep(dstr *v) \ while (n--) pre##_hash(&ctx, p, len); \ pre##_done(&ctx, d.buf); \ \ - if (memcmp(d.buf, v[2].buf, PRE##_HASHSZ) != 0) { \ + if (MEMCMP(d.buf, !=, v[2].buf, PRE##_HASHSZ)) { \ printf("\nfail:\n\tinput = `%s'\n\treps = `%i'\n\texpected = ", \ v[0].buf, *(int *)v[1].buf); \ type_hex.dump(&v[2], stdout); \ diff --git a/symm/hmac-def.h b/symm/hmac-def.h index e3f12ced..76d578d8 100644 --- a/symm/hmac-def.h +++ b/symm/hmac-def.h @@ -68,12 +68,11 @@ /* --- Useful constants --- */ \ \ const octet pre##_hmackeysz[] = \ - { KSZ_ANY | KSZ_16BIT, PRE##_STATESZ/256, PRE##_STATESZ%256 }; \ + { KSZ_ANY | KSZ_16BIT, PRE##_HASHSZ/256, PRE##_HASHSZ%256 }; \ const octet pre##_sslmackeysz[] = \ - { KSZ_ANY | KSZ_16BIT, PRE##_STATESZ/256, PRE##_STATESZ%256 }; \ + { KSZ_ANY | KSZ_16BIT, PRE##_HASHSZ/256, PRE##_HASHSZ%256 }; \ const octet pre##_nmackeysz[] = \ - { KSZ_SET | KSZ_16BIT, \ - 2*PRE##_STATESZ/256, 2*PRE##_STATESZ%256, 0, 0 }; \ + { KSZ_SET | KSZ_16BIT, 2*PRE##_HASHSZ/256, 2*PRE##_HASHSZ%256, 0, 0 }; \ \ /* --- @pre_nmacinit@ --- * \ * \ @@ -360,6 +359,7 @@ HMAC_TESTX(PRE, pre, name, fname) #include +#include #include #include #include @@ -400,7 +400,7 @@ static int macverify(dstr *v) \ csz -= i; \ } \ pre##_macdone(&cctx, d.buf); \ - if (memcmp(d.buf, v[2].buf, PRE##_HASHSZ) != 0) { \ + if (MEMCMP(d.buf, !=, v[2].buf, PRE##_HASHSZ)) { \ printf("\nfail:\n\tstep = %i\n\tinput = `%s'\n\tkey = ", \ *ip, v[0].buf); \ type_hex.dump(&v[1], stdout); \ diff --git a/symm/keccak1600.c b/symm/keccak1600.c index d58bc6f8..cfbfdefe 100644 --- a/symm/keccak1600.c +++ b/symm/keccak1600.c @@ -221,6 +221,8 @@ static const lane rcon[24] = { * `keccak1600_round' below for the details. */ +#define COMPL_MASK 0x00121106u + #define STATE_INIT(z) do { \ lane cmpl = LANE_CMPL; \ (z)->S[I(1, 0)] = cmpl; (z)->S[I(2, 0)] = cmpl; \ @@ -240,6 +242,8 @@ static const lane rcon[24] = { #else /* A target with fused and/not (`bic', `andc2'). Everything is simple. */ +#define COMPL_MASK 0u + #define STATE_INIT(z) do ; while (0) #define STATE_OUT(z) do ; while (0) @@ -585,6 +589,35 @@ void keccak1600_mix(keccak1600_state *s, const kludge64 *p, size_t n) { a = TO_LANE(p[i]); XOR_LANE(s->S[i], s->S[i], a); } } +/* --- @keccak1600_set@ --- * + * + * Arguments: @keccak1600_state *s@ = a state to update + * @const kludge64 *p@ = pointer to 64-bit words to mix in + * @size_t n@ = size of the input, in 64-bit words + * + * Returns: --- + * + * Use: Stores data into a %$\Keccak[r, 1600 - r]$% state. Note that + * it's the caller's responsibility to pass in no more than + * %$r$% bits of data. + * + * This is not the operation you wanted for ordinary hashing. + * It's provided for the use of higher-level protocols which use + * duplexing and other fancy sponge features. + */ + +void keccak1600_set(keccak1600_state *s, const kludge64 *p, size_t n) +{ + uint32 m = COMPL_MASK; + unsigned i; + lane a; + + for (i = 0; i < n; i++) { + a = TO_LANE(p[i]); if (m&1) NOT_LANE(a, a); + s->S[i] = a; m >>= 1; + } +} + /* --- @keccak1600_extract@ --- * * * Arguments: @const keccak1600_state *s@ = a state to extract output from @@ -600,11 +633,14 @@ void keccak1600_mix(keccak1600_state *s, const kludge64 *p, size_t n) void keccak1600_extract(const keccak1600_state *s, kludge64 *p, size_t n) { + uint32 m = COMPL_MASK; unsigned i; - keccak1600_state t; + lane t; - t = *s; STATE_OUT(&t); - for (i = 0; i < n; i++) p[i] = FROM_LANE(t.S[i]); + for (i = 0; i < n; i++) { + t = s->S[i]; if (m&1) NOT_LANE(t, t); + *p++ = FROM_LANE(t); m >>= 1; + } } /*----- Test rig ----------------------------------------------------------*/ @@ -613,6 +649,7 @@ void keccak1600_extract(const keccak1600_state *s, kludge64 *p, size_t n) #include +#include #include #include #include @@ -637,7 +674,7 @@ static int vrf_p(dstr v[]) keccak1600_p(&u, &u, n); keccak1600_extract(&u, t, 25); for (i = 0; i < 25; i++) STORE64_L_(d.buf + 8*i, t[i]); - if (memcmp(d.buf, v[2].buf, 200) != 0) { + if (MEMCMP(d.buf, !=, v[2].buf, 200)) { ok = 0; fprintf(stderr, "failed!"); fprintf(stderr, "\n\t input = "); type_hex.dump(&v[0], stderr); diff --git a/symm/keccak1600.h b/symm/keccak1600.h index 2867be96..f5aad98d 100644 --- a/symm/keccak1600.h +++ b/symm/keccak1600.h @@ -112,6 +112,26 @@ extern void keccak1600_init(keccak1600_state */*s*/); extern void keccak1600_mix(keccak1600_state */*s*/, const kludge64 */*p*/, size_t /*n*/); +/* --- @keccak1600_set@ --- * + * + * Arguments: @keccak1600_state *s@ = a state to update + * @const kludge64 *p@ = pointer to 64-bit words to mix in + * @size_t n@ = size of the input, in 64-bit words + * + * Returns: --- + * + * Use: Stores data into a %$\Keccak[r, 1600 - r]$% state. Note that + * it's the caller's responsibility to pass in no more than + * %$r$% bits of data. + * + * This is not the operation you wanted for ordinary hashing. + * It's provided for the use of higher-level protocols which use + * duplexing and other fancy sponge features. + */ + +extern void keccak1600_set(keccak1600_state */*s*/, + const kludge64 */*p*/, size_t /*n*/); + /* --- @keccak1600_extract@ --- * * * Arguments: @const keccak1600_state *s@ = a state to extract output from diff --git a/symm/latinpoly-def.h b/symm/latinpoly-def.h index af917fad..885b748d 100644 --- a/symm/latinpoly-def.h +++ b/symm/latinpoly-def.h @@ -445,19 +445,27 @@ static gaead_key *gkey_##latin##_common(const gaead_keyops *ops, \ static gaead_key *gkey_##latin##_poly1305(const void *k, size_t ksz) \ { return (gkey_##latin##_common(&gkops_##latin##_poly1305, k, ksz)); } \ \ +static int gszok_##latin##_poly1305(size_t nsz, size_t hsz, \ + size_t msz, size_t tsz) \ + { return (gaead_szokcommon(&latin##_poly1305, nsz, hsz, msz, tsz)); } \ + \ static gaead_key *gkey_##latin##_naclbox(const void *k, size_t ksz) \ { return (gkey_##latin##_common(&gkops_##latin##_naclbox, k, ksz)); } \ \ +static int gszok_##latin##_naclbox(size_t nsz, size_t hsz, \ + size_t msz, size_t tsz) \ + { return (gaead_szokcommon(&latin##_poly1305, nsz, hsz, msz, tsz)); } \ + \ const gcaead latin##_poly1305 = { \ name "-poly1305", latin##_keysz, latinpoly_noncesz, latinpoly_tagsz, \ 64, 0, 0, AEADF_AADNDEP, \ - gkey_##latin##_poly1305 \ + gkey_##latin##_poly1305, gszok_##latin##_poly1305 \ }; \ \ const gcaead latin##_naclbox = { \ name "-naclbox", latin##_keysz, latinpoly_noncesz, latinpoly_tagsz, \ 64, 0, 0, AEADF_AADNDEP | AEADF_NOAAD, \ - gkey_##latin##_naclbox \ + gkey_##latin##_naclbox, gszok_##latin##_naclbox \ }; /*----- That's all, folks -------------------------------------------------*/ diff --git a/symm/latinpoly-test.c b/symm/latinpoly-test.c index 5a914e50..f7a20763 100644 --- a/symm/latinpoly-test.c +++ b/symm/latinpoly-test.c @@ -27,6 +27,8 @@ /*----- Header files ------------------------------------------------------*/ +#include + #include "latinpoly-def.h" /*----- Main code ---------------------------------------------------------*/ @@ -62,8 +64,9 @@ int latinpoly_test(const gcaead *aec, dstr *v) if (rc) { printf("!! encryptdone reports failure\n"); goto encfail; } out.len = BLEN(&b); tag.len = POLY1305_TAGSZ; - if (out.len != v[4].len || memcmp(out.buf, v[4].buf, v[4].len) || - memcmp(tag.buf, v[5].buf, v[5].len)) { + if (out.len != v[4].len || + MEMCMP(out.buf, !=, v[4].buf, v[4].len) || + MEMCMP(tag.buf, !=, v[5].buf, v[5].len)) { encfail: ok = 0; printf("\n%s encrypt FAILED", aec->name); @@ -91,7 +94,8 @@ int latinpoly_test(const gcaead *aec, dstr *v) if (rc < 0) { printf("!! decryptdone reports failure\n"); goto decfail; } out.len = BLEN(&b); tag.len = POLY1305_TAGSZ; - if (out.len != v[3].len || memcmp(out.buf, v[3].buf, v[3].len) || !rc) { + if (out.len != v[3].len || MEMCMP(out.buf, !=, v[3].buf, v[3].len) || + !rc) { decfail: ok = 0; printf("\ndecrypt FAILED"); diff --git a/symm/modes-test.c b/symm/modes-test.c index b06d230a..a6c28bad 100644 --- a/symm/modes-test.c +++ b/symm/modes-test.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include "modes-test.h" @@ -164,7 +165,7 @@ static int regress_data(int rmode, FILE *fp, const char *what, if (!fread(t, sz, 1, fp)) die(1, "failed to read %s: %s", what, ferror(fp) ? strerror(errno) : "unexpected eof"); - if (memcmp(p, t, sz) != 0) return (-1); + if (MEMCMP(p, !=, t, sz)) return (-1); return (0); default: abort(); @@ -372,7 +373,7 @@ int test_encmode(const char *name, if (!p || !*p) { if (i >= argc) break; p = argv[i++]; - if (strcmp(p, "--") == 0) break; + if (STRCMP(p, ==, "--")) break; if (p[0] != '-' || p[1] == 0) { i--; break; } p++; } @@ -478,7 +479,7 @@ int test_encmode(const char *name, regr = 0; if ((f&TEMF_REFALIGN) && (sz0%blksz || sz1%blksz)) regr = 1; else if (!refp) { memcpy(ref, ct, TEXTSZ); regr = 1; refp = 1; } - else if (memcmp(ref, ct, TEXTSZ) != 0) { + else if (MEMCMP(ref, !=, ct, TEXTSZ)) { ok = 0; printf("\nConsistency failure (split = %lu/%lu/%lu)\n", (unsigned long)sz0, (unsigned long)sz1, (unsigned long)sz2); @@ -509,7 +510,7 @@ int test_encmode(const char *name, } if (sz2) dec(ct + sz0 + sz1, pt + sz0 + sz1, sz2); - if (memcmp(text, pt, TEXTSZ) != 0) { + if (MEMCMP(text, !=, pt, TEXTSZ)) { ok = 0; printf("\nRound-trip failure (split = %lu/%lu/%lu)\n", (unsigned long)sz0, (unsigned long)sz1, (unsigned long)sz2); diff --git a/symm/multigen b/symm/multigen index 4c1921ea..8af0f108 100755 --- a/symm/multigen +++ b/symm/multigen @@ -30,7 +30,9 @@ import itertools as IT import optparse as OP import os as OS import re as RX -from cStringIO import StringIO +import sys as SYS +if SYS.version_info >= (3,): from io import StringIO +else: from cStringIO import StringIO from sys import argv, exit, stderr ###-------------------------------------------------------------------------- @@ -49,6 +51,16 @@ def indexed(seq): """ return IT.izip(IT.count(), seq) +if SYS.version_info >= (3,): + def func_name(func): return func.__name__ + IT.izip = zip +else: + def func_name(func): return func.func_name + +try: next +except NameError: + def next(obj): return obj.next() + ###-------------------------------------------------------------------------- ### Reading the input values. @@ -363,7 +375,7 @@ class SequenceTemplate (BasicTemplate): if len(seq) == 1: return seq[0] else: - return super(SequenceTemplate, cls).__new__(cls, seq = seq, **kw) + return super(SequenceTemplate, cls).__new__(cls, **kw) def __init__(me, seq, **kw): """ @@ -461,7 +473,7 @@ class ParseState (object): Sets `curr' to the next line, or to None if the input is exhausted. """ - try: me.curr = me._it.next() + try: me.curr = next(me._it) except StopIteration: me.curr = None else: me._i += 1 @@ -504,7 +516,7 @@ def defop(func): An operator function is given the raw value as an argument and should return the transformed value. """ - name = func.func_name + name = func_name(func) if name.startswith('op_'): name = name[3:] OPMAP[name] = func return func @@ -576,7 +588,7 @@ def parse_text(ps): ## object. l = lit.getvalue() if l: tt.append(LiteralTemplate(l)) - lit.reset() + lit.seek(0) lit.truncate() ## Iterate over the lines of input. @@ -786,7 +798,7 @@ def filenames(filetempl): ## Main dispatch. if opts.mode == 'list': - for file, cs in filenames(filetempl): print file + for file, cs in filenames(filetempl): print(file) elif opts.mode == 'gen': with open(opts.input) as f: templ = RepeatTemplate(compile_template(opts.input, f.read())) diff --git a/symm/ocb1-def.h b/symm/ocb1-def.h index 7c7eb295..09b3824d 100644 --- a/symm/ocb1-def.h +++ b/symm/ocb1-def.h @@ -495,11 +495,14 @@ static gaead_key *gckey(const void *k, size_t ksz) \ return (&key->k); \ } \ \ +static int gcszok(size_t nsz, size_t hsz, size_t msz, size_t tsz) \ + { return (gaead_szokcommon(&pre##_ocb1, nsz, hsz, msz, tsz)); } \ + \ const gcaead pre##_ocb1 = { \ name "-ocb1", \ pre##_keysz, pre##_ocb1noncesz, pre##_ocb1tagsz, \ PRE##_BLKSZ, PRE##_BLKSZ, 0, 0, \ - gckey \ + gckey, gcszok \ }; \ \ OCB1_TESTX(PRE, pre, name, fname) @@ -520,6 +523,7 @@ OCB1_TESTX(PRE, pre, name, fname) #include #include +#include #include #include @@ -581,8 +585,8 @@ static int ocb1verify(dstr *v) \ d.len = BLEN(&b); \ \ if (d.len != v[4].len || \ - memcmp(d.buf, v[4].buf, v[4].len) != 0 || \ - memcmp(t.buf, v[5].buf, v[5].len) != 0) { \ + MEMCMP(d.buf, !=, v[4].buf, v[4].len) || \ + MEMCMP(t.buf, !=, v[5].buf, v[5].len)) { \ fail_enc: \ printf("\nfail encrypt:\n\tstep = %i", *ip); \ fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \ @@ -623,7 +627,7 @@ static int ocb1verify(dstr *v) \ d.len = BLEN(&b); \ \ if (d.len != v[3].len || !win || \ - memcmp(d.buf, v[3].buf, v[3].len) != 0) { \ + MEMCMP(d.buf, !=, v[3].buf, v[3].len)) { \ fail_dec: \ printf("\nfail decrypt:\n\tstep = %i", *ip); \ fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \ diff --git a/symm/ocb3-def.h b/symm/ocb3-def.h index 14a22f61..a5ee46e9 100644 --- a/symm/ocb3-def.h +++ b/symm/ocb3-def.h @@ -768,11 +768,14 @@ static gaead_key *gckey(const void *k, size_t ksz) \ return (&key->k); \ } \ \ +static int gcszok(size_t nsz, size_t hsz, size_t msz, size_t tsz) \ + { return (gaead_szokcommon(&pre##_ocb3, nsz, hsz, msz, tsz)); } \ + \ const gcaead pre##_ocb3 = { \ name "-ocb3", \ pre##_keysz, pre##_ocb3noncesz, pre##_ocb3tagsz, \ PRE##_BLKSZ, PRE##_BLKSZ - 1, 0, AEADF_PCTSZ, \ - gckey \ + gckey, gcszok \ }; \ \ OCB3_TESTX(PRE, pre, name, fname) @@ -854,8 +857,8 @@ static int ocb3verify(dstr *v) \ d.len = BLEN(&b); \ \ if (d.len != v[4].len || \ - memcmp(d.buf, v[4].buf, v[4].len) != 0 || \ - memcmp(t.buf, v[5].buf, v[5].len) != 0) { \ + MEMCMP(d.buf, !=, v[4].buf, v[4].len) || \ + MEMCMP(t.buf, !=, v[5].buf, v[5].len)) { \ fail_enc: \ printf("\nfail encrypt:\n\tstep = %i", *ip); \ fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \ @@ -896,7 +899,7 @@ static int ocb3verify(dstr *v) \ d.len = BLEN(&b); \ \ if (d.len != v[3].len || !win || \ - memcmp(d.buf, v[3].buf, v[3].len) != 0) { \ + MEMCMP(d.buf, !=, v[3].buf, v[3].len)) { \ fail_dec: \ printf("\nfail decrypt:\n\tstep = %i", *ip); \ fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \ @@ -965,7 +968,7 @@ static int ocb3mct(dstr *v) \ if (rc) goto fail; \ \ d.len = tsz; \ - if (memcmp(d.buf, v[1].buf, tsz) != 0) { \ + if (MEMCMP(d.buf, !=, v[1].buf, tsz)) { \ fail: \ printf("\nfail mct: ksz = %u, tsz = %u", ksz, tsz); \ fputs("\n\texp tag = ", stdout); type_hex.dump(&v[1], stdout); \ diff --git a/symm/pmac1-def.h b/symm/pmac1-def.h index 4b0da048..5df93385 100644 --- a/symm/pmac1-def.h +++ b/symm/pmac1-def.h @@ -288,6 +288,7 @@ PMAC1_TESTX(PRE, pre, name, fname) #include #include +#include #include #include @@ -327,7 +328,7 @@ static int macverify(dstr *v) \ csz -= i; \ } \ pre##_pmac1done(&cctx, d.buf); \ - if (memcmp(d.buf, v[2].buf, PRE##_BLKSZ) != 0) { \ + if (MEMCMP(d.buf, !=, v[2].buf, PRE##_BLKSZ)) { \ printf("\nfail:\n\tstep = %i\n\tkey = ", *ip); \ type_hex.dump(&v[0], stdout); \ fputs("\n\tinput = ", stdout); \ diff --git a/symm/poly1305.c b/symm/poly1305.c index c4a88a84..39c832f8 100644 --- a/symm/poly1305.c +++ b/symm/poly1305.c @@ -679,9 +679,9 @@ void poly1305_concat(poly1305_ctx *ctx, #define BIT (1ul << (ULONG_BITS - 1)) if (n) { i = ULONG_BITS; - while (!(n & BIT)) { n <<= 1; i--; } + while (!(n&BIT)) { n <<= 1; i--; } mul_r(prefix, x, x); n <<= 1; i--; - while (i--) { sqr(x, x); if (n & BIT) mul_r(prefix, x, x); n <<= 1; } + while (i--) { sqr(x, x); if (n&BIT) mul_r(prefix, x, x); n <<= 1; } } #undef BIT mul(x, prefix->u.P.h, x); @@ -789,10 +789,10 @@ void poly1305_done(poly1305_ctx *ctx, void *h) /* Convert this mess back into 32-bit words. We lose the top two bits, * but that's fine. */ - h0 = (h0 >> 0) | ((h1 & 0x0000003f) << 26); - h1 = (h1 >> 6) | ((h2 & 0x00000fff) << 20); - h2 = (h2 >> 12) | ((h3 & 0x0003ffff) << 14); - h3 = (h3 >> 18) | ((h4 & 0x00ffffff) << 8); + h0 = (h0 >> 0) | ((h1&0x0000003f) << 26); + h1 = (h1 >> 6) | ((h2&0x00000fff) << 20); + h2 = (h2 >> 12) | ((h3&0x0003ffff) << 14); + h3 = (h3 >> 18) | ((h4&0x00ffffff) << 8); /* All done. */ STORE32_L(p + 0, h0); STORE32_L(p + 4, h1); @@ -861,6 +861,7 @@ void poly1305_done(poly1305_ctx *ctx, void *h) #ifdef TEST_RIG +#include #include #include "ct.h" @@ -888,7 +889,7 @@ static int vrf_hash(dstr v[]) poly1305_hash(&ctx, v[2].buf + j, v[2].len - j); poly1305_done(&ctx, t.buf); ct_remedy(t.buf, t.len); - if (memcmp(t.buf, v[3].buf, 16) != 0) { + if (MEMCMP(t.buf, !=, v[3].buf, 16)) { fprintf(stderr, "failed..."); fprintf(stderr, "\n\tkey = "); type_hex.dump(&v[0], stderr); fprintf(stderr, "\n\tmask = "); type_hex.dump(&v[1], stderr); @@ -932,7 +933,7 @@ static int vrf_cat(dstr v[]) poly1305_concat(&ctx, &ctx, &cc[2]); } poly1305_done(&ctx, t.buf); - if (memcmp(t.buf, v[5].buf, 16) != 0) { + if (MEMCMP(t.buf, !=, v[5].buf, 16)) { fprintf(stderr, "failed..."); fprintf(stderr, "\n\tkey = "); type_hex.dump(&v[0], stderr); fprintf(stderr, "\n\tmask = "); type_hex.dump(&v[1], stderr); @@ -955,27 +956,38 @@ static int vrf_cat(dstr v[]) static int vrf_mct(dstr v[]) { unsigned j, msz; - unsigned long i, niter; + unsigned long i, start_iter, end_iter; rijndael_ecbctx rij; poly1305_key key; poly1305_ctx mac; - dstr d = DSTR_INIT; - octet k[16], r[16], n[16], s[16], *t, m[MSZMAX] = { 0 }; + dstr dk = DSTR_INIT, dr = DSTR_INIT, dn = DSTR_INIT, + dt = DSTR_INIT, dm = DSTR_INIT; + octet *k, *r, s[16], *n, *t, *m; int ok = 1; - if (v[0].len != sizeof(k)) { fprintf(stderr, "AES key len\n"); exit(2); } - if (v[1].len != sizeof(r)) { fprintf(stderr, "poly key len\n"); exit(2); } - if (v[2].len != sizeof(n)) { fprintf(stderr, "nonce len\n"); exit(2); } - if (v[4].len != sizeof(n)) { fprintf(stderr, "result len\n"); exit(2); } - memcpy(k, v[0].buf, sizeof(k)); - memcpy(r, v[1].buf, sizeof(k)); - memcpy(n, v[2].buf, sizeof(k)); - niter = *(unsigned long *)v[3].buf; - dstr_ensure(&d, 16); d.len = 16; t = (octet *)d.buf; - - rijndael_ecbinit(&rij, k, sizeof(k), 0); - poly1305_keyinit(&key, r, sizeof(r)); - for (i = 0; i < niter; i++) { + DENSURE(&dk, 16); k = (octet *)dk.buf; dk.len = 16; + DENSURE(&dr, 16); r = (octet *)dr.buf; dr.len = 16; + DENSURE(&dn, 16); n = (octet *)dn.buf; dn.len = 16; + DENSURE(&dt, 16); t = (octet *)dt.buf; dt.len = 16; + DENSURE(&dm, MSZMAX); m = (octet *)dm.buf; dm.len = MSZMAX; + memset(m, 0, MSZMAX); + + if (v[0].len != 16) { fprintf(stderr, "AES key len\n"); exit(2); } + if (v[1].len != 16) { fprintf(stderr, "poly key len\n"); exit(2); } + if (v[2].len != 16) { fprintf(stderr, "nonce len\n"); exit(2); } + if (v[3].len != MSZMAX) { fprintf(stderr, "msgbuf len\n"); exit(2); } + if (v[6].len != 16) { fprintf(stderr, "result len\n"); exit(2); } + memcpy(k, v[0].buf, 16); + memcpy(r, v[1].buf, 16); + memcpy(n, v[2].buf, 16); + memcpy(m, v[3].buf, MSZMAX); + start_iter = *(unsigned long *)v[4].buf; + end_iter = *(unsigned long *)v[5].buf; + if (end_iter < start_iter) { fprintf(stderr, "iter bounds\n"); exit(2); } + + rijndael_ecbinit(&rij, k, 16, 0); + poly1305_keyinit(&key, r, 16); + for (i = start_iter; i < end_iter; i++) { msz = 0; for (;;) { rijndael_ecbencrypt(&rij, n, s, 16); @@ -987,29 +999,39 @@ static int vrf_mct(dstr v[]) for (j = 0; j < 16; j++) n[j] ^= t[j]; if (msz%2) { for (j = 0; j < 16; j++) k[j] ^= t[j]; - rijndael_ecbinit(&rij, k, sizeof(k), 0); + rijndael_ecbinit(&rij, k, 16, 0); } if (msz%3) { for (j = 0; j < 16; j++) r[j] ^= t[j]; - poly1305_keyinit(&key, r, sizeof(r)); + poly1305_keyinit(&key, r, 16); } m[msz++] ^= t[0]; } } - if (memcmp(t, v[4].buf, 16) != 0) { + if (MEMCMP(t, !=, v[6].buf, 16)) { ok = 0; fprintf(stderr, "failed..."); fprintf(stderr, "\n\tinitial k = "); type_hex.dump(&v[0], stderr); fprintf(stderr, "\n\tinitial r = "); type_hex.dump(&v[1], stderr); fprintf(stderr, "\n\tinitial n = "); type_hex.dump(&v[2], stderr); - fprintf(stderr, "\n\titerations = %lu", niter); - fprintf(stderr, "\n\texpected = "); type_hex.dump(&v[4], stderr); - fprintf(stderr, "\n\tcalculated = "); type_hex.dump(&d, stderr); + fprintf(stderr, "\n\tinitial m = "); type_hex.dump(&v[3], stderr); + fprintf(stderr, "\n\tstart iter = %lu", start_iter); + fprintf(stderr, "\n\tend iter = %lu", end_iter); + fprintf(stderr, "\n\tfinal k = "); type_hex.dump(&dk, stderr); + fprintf(stderr, "\n\tfinal r = "); type_hex.dump(&dr, stderr); + fprintf(stderr, "\n\tfinal n = "); type_hex.dump(&dn, stderr); + fprintf(stderr, "\n\tfinal m = "); type_hex.dump(&dm, stderr); + fprintf(stderr, "\n\texpected = "); type_hex.dump(&v[6], stderr); + fprintf(stderr, "\n\tcalculated = "); type_hex.dump(&dt, stderr); fputc('\n', stderr); } - dstr_destroy(&d); + dstr_destroy(&dk); + dstr_destroy(&dr); + dstr_destroy(&dn); + dstr_destroy(&dt); + dstr_destroy(&dm); return (ok); } @@ -1019,7 +1041,8 @@ static const struct test_chunk tests[] = { { "poly1305-cat", vrf_cat, { &type_hex, &type_hex, &type_hex, &type_hex, &type_hex, &type_hex } }, { "poly1305-mct", vrf_mct, - { &type_hex, &type_hex, &type_hex, &type_ulong, &type_hex } }, + { &type_hex, &type_hex, &type_hex, &type_hex, + &type_ulong, &type_ulong, &type_hex } }, { 0, 0, { 0 } } }; diff --git a/symm/rc2.c b/symm/rc2.c index df594251..97963fef 100644 --- a/symm/rc2.c +++ b/symm/rc2.c @@ -250,6 +250,7 @@ void rc2_dblk(const rc2_ctx *k, const uint32 *s, uint32 *dst) #ifdef TEST_RIG +#include #include #include @@ -276,7 +277,7 @@ static int verify(dstr *v) BLKC_MOVE(RC2, d, p); rc2_eblk(&k, d, d); BLKC_STORE(RC2, b.buf, d); - if (memcmp(b.buf, v[3].buf, RC2_BLKSZ)) { + if (MEMCMP(b.buf, !=, v[3].buf, RC2_BLKSZ)) { ok = 0; printf("\nfail encryption:" "\n\tkey = "); @@ -293,7 +294,7 @@ static int verify(dstr *v) BLKC_MOVE(RC2, d, c); rc2_dblk(&k, d, d); BLKC_STORE(RC2, b.buf, d); - if (memcmp(b.buf, v[2].buf, RC2_BLKSZ)) { + if (MEMCMP(b.buf, !=, v[2].buf, RC2_BLKSZ)) { ok = 0; printf("\nfail decryption:" "\n\tkey = "); diff --git a/symm/rc4.c b/symm/rc4.c index 214dbc17..4cb0e87d 100644 --- a/symm/rc4.c +++ b/symm/rc4.c @@ -296,6 +296,7 @@ grand *rc4_rand(const void *k, size_t sz) #include #include +#include #include #include @@ -310,7 +311,7 @@ static int v_encrypt(dstr *v) d.len = v[1].len; rc4_encrypt(&ctx, v[1].buf, d.buf, d.len); - if (memcmp(v[2].buf, d.buf, d.len) != 0) { + if (MEMCMP(v[2].buf, !=, d.buf, d.len)) { ok = 0; printf("\nfail encryption:" "\n\tkey = "); @@ -336,7 +337,7 @@ static int v_generate(dstr *v) d.len = v[2].len; rc4_encrypt(&ctx, 0, d.buf, d.len); - if (memcmp(v[2].buf, d.buf, d.len) != 0) { + if (MEMCMP(v[2].buf, !=, d.buf, d.len)) { ok = 0; printf("\nfail generation:" "\n\tkey = "); diff --git a/symm/rijndael-arm64-crypto.S b/symm/rijndael-arm64-crypto.S index 98f61734..df0bb9d9 100644 --- a/symm/rijndael-arm64-crypto.S +++ b/symm/rijndael-arm64-crypto.S @@ -131,7 +131,7 @@ FUNC(rijndael_setup_arm64_crypto) sub x6, x6, #1 cmp x8, x3 cbz x6, 9f - csel x8, x8, xzr, cc + cmov.cs x8, xzr b 0b // Next job is to construct the decryption keys. The keys for the diff --git a/symm/rijndael-x86ish-aesni.S b/symm/rijndael-x86ish-aesni.S index 6d9b3b22..f5e5cc9c 100644 --- a/symm/rijndael-x86ish-aesni.S +++ b/symm/rijndael-x86ish-aesni.S @@ -70,15 +70,12 @@ ENDFUNC FUNC(rijndael_setup_x86ish_aesni) -#define SI WHOLE(si) -#define DI WHOLE(di) - #if CPUFAM_X86 // Arguments are on the stack. We'll need to stack the caller's // register veriables, but we'll manage. -# define CTX ebp // context pointer -# define BLKSZ [esp + 24] // block size +# define CTX BP // context pointer +# define BLKSZ [SP + 24] // block size # define KSZ ebx // key size # define NKW edx // total number of key words @@ -92,15 +89,15 @@ FUNC(rijndael_setup_x86ish_aesni) # define BLKOFF edx // block size in bytes // Stack the caller's registers. - pushreg ebp + pushreg BP pushreg ebx pushreg esi pushreg edi // Set up our own variables. - mov CTX, [esp + 20] // context base pointer - mov SI, [esp + 28] // key material - mov KSZ, [esp + 32] // key size, in words + mov CTX, [SP + 20] // context base pointer + mov SI, [SP + 28] // key material + mov KSZ, [SP + 32] // key size, in words #endif #if CPUFAM_AMD64 && ABI_SYSV @@ -330,7 +327,7 @@ FUNC(rijndael_setup_x86ish_aesni) popreg edi popreg esi popreg ebx - popreg ebp + popreg BP #endif #if CPUFAM_AMD64 && ABI_WIN popreg rdi @@ -389,8 +386,8 @@ ENDFUNC # define DST edx # define NR ecx - mov K, [esp + 4] - mov SRC, [esp + 8] + mov K, [SP + 4] + mov SRC, [SP + 8] #endif #if CPUFAM_AMD64 && ABI_SYSV @@ -428,7 +425,7 @@ ENDFUNC add K, 16 pxor xmm0, xmm1 #if CPUFAM_X86 - mov DST, [esp + 12] + mov DST, [SP + 12] #endif // Dispatch to the correct code. diff --git a/symm/rijndael.c b/symm/rijndael.c index 7db9e012..597ae98f 100644 --- a/symm/rijndael.c +++ b/symm/rijndael.c @@ -34,6 +34,11 @@ #include +#if defined(TEST_RIG) && defined(ENABLE_ASM_DEBUG) +# include "regdump.h" +# define BLKC_TESTHOOK do { regdump_init(); } while (0) +#endif + #include "blkc.h" #include "dispatch.h" #include "gcipher.h" diff --git a/symm/rijndael192.c b/symm/rijndael192.c index 424f8f90..46562feb 100644 --- a/symm/rijndael192.c +++ b/symm/rijndael192.c @@ -32,6 +32,11 @@ #include +#if defined(TEST_RIG) && defined(ENABLE_ASM_DEBUG) +# include "regdump.h" +# define BLKC_TESTHOOK do { regdump_init(); } while (0) +#endif + #include "blkc.h" #include "gcipher.h" #include "rijndael.h" diff --git a/symm/rijndael256.c b/symm/rijndael256.c index 9fb72983..4030ab2c 100644 --- a/symm/rijndael256.c +++ b/symm/rijndael256.c @@ -32,6 +32,11 @@ #include +#if defined(TEST_RIG) && defined(ENABLE_ASM_DEBUG) +# include "regdump.h" +# define BLKC_TESTHOOK do { regdump_init(); } while (0) +#endif + #include "blkc.h" #include "gcipher.h" #include "rijndael.h" diff --git a/symm/salsa20-arm-neon.S b/symm/salsa20-arm-neon.S index 3b6beb06..9725e8a8 100644 --- a/symm/salsa20-arm-neon.S +++ b/symm/salsa20-arm-neon.S @@ -36,7 +36,7 @@ .text ///-------------------------------------------------------------------------- -/// Main.code. +/// Main code. FUNC(salsa20_core_arm_neon) diff --git a/symm/salsa20-arm64.S b/symm/salsa20-arm64.S index 864c63c1..a71e53b4 100644 --- a/symm/salsa20-arm64.S +++ b/symm/salsa20-arm64.S @@ -35,7 +35,7 @@ .text ///-------------------------------------------------------------------------- -/// Main.code. +/// Main code. FUNC(salsa20_core_arm64) diff --git a/symm/salsa20-x86ish-sse2.S b/symm/salsa20-x86ish-sse2.S index 5dc9c17c..26bab892 100644 --- a/symm/salsa20-x86ish-sse2.S +++ b/symm/salsa20-x86ish-sse2.S @@ -65,16 +65,16 @@ FUNC(salsa20_core_x86ish_sse2) # define OUT edx # define SAVE0 xmm6 # define SAVE1 xmm7 -# define SAVE2 [esp + 0] -# define SAVE3 [esp + 16] +# define SAVE2 [SP + 0] +# define SAVE3 [SP + 16] - pushreg ebp + pushreg BP setfp - sub esp, 32 - mov IN, [ebp + 12] - mov OUT, [ebp + 16] - and esp, ~15 - mov NR, [ebp + 8] + stalloc 32 + mov IN, [BP + 12] + mov OUT, [BP + 16] + and SP, ~15 + mov NR, [BP + 8] #endif #if CPUFAM_AMD64 && ABI_SYSV @@ -107,8 +107,8 @@ FUNC(salsa20_core_x86ish_sse2) # define OUT r8 # define SAVE0 xmm6 # define SAVE1 xmm7 -# define SAVE2 [rsp + 32] -# define SAVE3 [rsp + 48] +# define SAVE2 [SP + 32] +# define SAVE3 [SP + 48] stalloc 64 + 8 savexmm xmm6, 0 @@ -301,7 +301,7 @@ FUNC(salsa20_core_x86ish_sse2) // Tidy things up. #if CPUFAM_X86 dropfp - popreg ebp + popreg BP #endif #if CPUFAM_AMD64 && ABI_WIN rstrxmm xmm6, 0 diff --git a/symm/salsa20.c b/symm/salsa20.c index e7c35f4f..f0fe3d7c 100644 --- a/symm/salsa20.c +++ b/symm/salsa20.c @@ -871,9 +871,14 @@ SALSA20_VARS(DEFXGRAND) #include #include +#include #include #include +#ifdef ENABLE_ASM_DEBUG +# include "regdump.h" +#endif + static const int perm[] = { 0, 13, 10, 7, 4, 1, 14, 11, @@ -900,7 +905,7 @@ static const int perm[] = { } \ for (i = 0; i < SALSA20_OUTSZ/4; i++) STORE32_L(d.buf + 4*i, b[i]); \ \ - if (d.len != v[2].len || memcmp(d.buf, v[2].buf, v[2].len) != 0) { \ + if (d.len != v[2].len || MEMCMP(d.buf, !=, v[2].buf, v[2].len)) { \ ok = 0; \ printf("\nfail core:" \ "\n\titerations = %d" \ @@ -970,7 +975,7 @@ SALSA20_VARS(DEFVCORE) } \ if (sz) BASE##_ENCRYPT(r, &ctx, p, q, sz); \ \ - if (d.len != v[5].len || memcmp(d.buf, v[5].buf, v[5].len) != 0) { \ + if (d.len != v[5].len || MEMCMP(d.buf, !=, v[5].buf, v[5].len)) { \ ok = 0; \ printf("\nfail encrypt:" \ "\n\tstep = %lu" \ @@ -1016,6 +1021,9 @@ SALSA20_VARS(DEFXTAB) int main(int argc, char *argv[]) { +#ifdef ENABLE_ASM_DEBUG + regdump_init(); +#endif test_run(argc, argv, defs, SRCDIR"/t/salsa20"); return (0); } diff --git a/symm/seal.c b/symm/seal.c index a4a0a3f8..070550dd 100644 --- a/symm/seal.c +++ b/symm/seal.c @@ -521,6 +521,7 @@ grand *seal_rand(const void *k, size_t sz, uint32 n) #include +#include #include static int verify(dstr *v) @@ -543,7 +544,7 @@ static int verify(dstr *v) seal_initctx(&c, &k, n); seal_encrypt(&c, 0, d.buf, i); seal_encrypt(&c, z.buf, d.buf + i, d.len - i); - if (memcmp(d.buf, v[2].buf, d.len) != 0) { + if (MEMCMP(d.buf, !=, v[2].buf, d.len)) { ok = 0; printf("*** seal failure\n"); printf("*** k = "); type_hex.dump(&v[0], stdout); putchar('\n'); diff --git a/symm/serpent-check.c b/symm/serpent-check.c index 7f95ffc7..190096fb 100644 --- a/symm/serpent-check.c +++ b/symm/serpent-check.c @@ -32,6 +32,7 @@ #include #include +#include #include "serpent-sbox.h" @@ -71,7 +72,7 @@ static int check(unsigned a, unsigned b, unsigned c, unsigned d, *q++ = (a & 1) | ((b & 1) << 1) | ((c & 1) << 2) | ((d & 1) << 3); a >>= 1; b >>= 1; c >>= 1; d >>= 1; } - return (memcmp(buf, p, sizeof(buf))); + return (MEMCMP(buf, ==, p, sizeof(buf)) ? 0 : -1); } #define CHECK(i) do { \ diff --git a/symm/sha3.c b/symm/sha3.c index 16967629..36bd430b 100644 --- a/symm/sha3.c +++ b/symm/sha3.c @@ -957,6 +957,7 @@ grand *kmac256_rand(const void *perso, size_t psz, const void *k, size_t sz) #include +#include #include #include @@ -979,7 +980,7 @@ static int vrf_sha3_mct(void (*initfn)(sha3_ctx *), sha3_done(&ctx, d.buf); } - if (memcmp(d.buf, out->buf, out->len) != 0) { + if (MEMCMP(d.buf, !=, out->buf, out->len)) { ok = 0; printf("\nfail\n\tsteps = %d\n\tinput = ", n); type_hex.dump(in, stdout); @@ -1037,7 +1038,7 @@ static int vrf_shaky(void (*initfn)(shake_ctx *, p += i; sz -= i; } - if (memcmp(d.buf, want->buf, want->len) != 0) { + if (MEMCMP(d.buf, !=, want->buf, want->len)) { ok = 0; printf("\nfail (get):\n\tstep = %i\n\tinput = ", *ip); type_hex.dump(m, stdout); @@ -1071,7 +1072,7 @@ static int vrf_shaky(void (*initfn)(shake_ctx *, p += i; sz -= i; } - if (memcmp(d.buf, want->buf, want->len) != 0) { + if (MEMCMP(d.buf, !=, want->buf, want->len)) { ok = 0; printf("\nfail (mask):\n\tstep = %i\n\tinput = ", *ip); type_hex.dump(m, stdout); @@ -1131,7 +1132,7 @@ static int vrf_kmac(void (*initfn)(kmac_ctx *, const void *, size_t, if (tsz) kmac_done(&ctx, d.buf, tsz); else { kmac_xof(&ctx); kmac_get(&ctx, d.buf, d.len); } - if (memcmp(d.buf, want->buf, want->len) != 0) { + if (MEMCMP(d.buf, !=, want->buf, want->len)) { ok = 0; printf("\nfail"); printf("\n\tperso = `%s'", perso->buf); diff --git a/symm/square-mktab.c b/symm/square-mktab.c index cc26f580..70d7e049 100644 --- a/symm/square-mktab.c +++ b/symm/square-mktab.c @@ -38,7 +38,7 @@ static octet s[256], si[256]; static uint32 t[4][256], ti[4][256]; static uint32 u[4][256]; -static octet rc[32]; +static octet rc[35]; /*----- Main code ---------------------------------------------------------*/ @@ -333,7 +333,7 @@ const uint32 square_u[4][256] = {\n\ fputs("\ /* --- The round constants --- */\n\ \n\ -const octet square_rcon[32] = {\n\ +const octet square_rcon[35] = {\n\ ", stdout); for (i = 0; i < sizeof(rc); i++) { printf("0x%02x", rc[i]); diff --git a/symm/square.c b/symm/square.c index 888d4957..34d313d7 100644 --- a/symm/square.c +++ b/symm/square.c @@ -47,7 +47,7 @@ const octet square_keysz[] = { KSZ_RANGE, SQUARE_KEYSZ, 4, 16, 4 }; extern const octet square_s[256], square_si[256]; extern const uint32 square_t[4][256], square_ti[4][256]; extern const uint32 square_u[4][256]; -extern const octet square_rcon[32]; +extern const octet square_rcon[35]; #define S square_s #define SI square_si diff --git a/symm/strobe.c b/symm/strobe.c new file mode 100644 index 00000000..de4dbb2d --- /dev/null +++ b/symm/strobe.c @@ -0,0 +1,695 @@ +/* -*-c-*- + * + * The STROBE protocol framework + * + * (c) 2018 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software: you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include + +#include + +#include "keccak1600.h" +#include "strobe.h" + +/*----- Magic constants ---------------------------------------------------*/ + +#define DDATA 0x04 +#define DRATE 0x80 + +/*----- Utilities ---------------------------------------------------------*/ + +/* --- @crank@ --- * + * + * Arguments: @strobe_ctx *ctx@ = pointer to context block to initialize + * + * Returns: --- + * + * Use: Cycle the Keccak-p[1600, n] duplex function. + */ + +static void crank(strobe_ctx *ctx) +{ + kludge64 t[25]; + octet *p; + unsigned i; + + /* Ensure that we've not overstepped the rate bound. */ + assert(ctx->n <= ctx->r - 2); + + /* Apply the cSHAKE and rate padding. */ + ctx->buf[ctx->n] ^= ctx->n0; + ctx->buf[ctx->n + 1] ^= DDATA; + ctx->buf[ctx->r - 1] ^= DRATE; + + /* Cycle the sponge. */ + for (i = 0, p = ctx->buf; i < ctx->r/8; i++) + { LOAD64_L_(t[i], p); p += 8; } + keccak1600_set(&ctx->k, t, ctx->r/8); + keccak1600_p(&ctx->k, &ctx->k, 24); + keccak1600_extract(&ctx->k, t, ctx->r/8); + for (i = 0, p = ctx->buf; i < ctx->r/8; i++) + { STORE64_L_(p, t[i]); p += 8; } + + /* Restart at the beginning of the buffer, and note this as a + * continuation. + */ + ctx->n = ctx->n0 = 0; +} + +/* --- @xorbuf@ --- * + * + * Arguments: @octet *z@ = pointer to output buffer + * @const octet *x, *y@ = pointer to input buffers + * @size_t sz@ = common buffer length + * + * Returns: --- + * + * Use: Store the bytewise XOR of the buffers @x@ and @y@ in @z@. + * The @x@ and @y@ may be equal, but otherwise the buffers must + * not overlap. + */ + +static void xorbuf(octet *z, const octet *x, const octet *y, size_t sz) + { size_t i; for (i = 0; i < sz; i++) *z++ = *x++ ^ *y++; } + +/* --- @nonzerop@ --- * + * + * Arguments: @const octet *x@ = pointer to input buffer + * @size_t sz@ = buffer length + * + * Returns: --- + * + * Use: If any byte of @x@ is nonzero, then return a nonzero value + * between 1 and 255 inclusive; otherwise return zero. + */ + +static unsigned nonzerop(const octet *x, size_t sz) +{ + unsigned z = 0; + size_t i; + + for (i = 0; i < sz; i++) z |= *x++; + return (z); +} + +/* --- @unequalp@ --- * + * + * Arguments: @const octet *x, *y@ = pointer to input buffers + * @size_t sz@ = common buffer length + * + * Returns: --- + * + * Use: If any respective bytes of @x@ and @y@ are unequal, then + * return a nonzero value between 1 and 255 inclusive; otherwise + * return zero. + */ + +static unsigned unequalp(const octet *x, const octet *y, size_t sz) +{ + unsigned z = 0; + size_t i; + + for (i = 0; i < sz; i++) z |= *x++ ^ *y++; + return (z); +} + +/* --- @process_buffer@ --- * + * + * Arguments: @strobe_ctx *ctx@ = pointer to context block + * @const octet *p@ = pointer to input buffer + * @octet *q@ = pointer to output buffer + * @size_t sz@ = common buffer length + * + * Returns: --- + * + * Use: Process a portion of a STROBE input small enough to be + * satisfied from the internal buffer. + */ + +static void process_buffer(strobe_ctx *ctx, + const octet *p, octet *q, size_t sz) +{ + octet *b = ctx->buf + ctx->n; + unsigned z = 0; + + if (!(ctx->f&STRBF_CRYPTO)) { + /* No crypto to do. The `output' would be equal to the input, so that's + * rather uninteresting (and, indeed, forbidden). If there's input, then + * mix it into the state. + */ + + if (p && (ctx->f&STRBF_VRFOUT)) z |= nonzerop(p, sz); + if (p) xorbuf(b, b, p, sz); + } else if (!(ctx->f&STRBF_MIXOUT)) { + /* Mix the input into the sponge state. That means that the new state + * will be equal to the output. + */ + + if (p) xorbuf(b, b, p, sz); + if (ctx->f&STRBF_VRFOUT) z |= nonzerop(b, sz); + if (q) memcpy(q, b, sz); + } else if (p) { + /* Mix the output into the sponge state, so the new state will in fact be + * equal to the input. If the input and output buffers are equal then we + * have a dance to do. + */ + + if (!q) { + if (ctx->f&STRBF_VRFOUT) z |= unequalp(p, b, sz); + memcpy(b, p, sz); + } else { + xorbuf(q, p, b, sz); + if (q != p) memcpy(b, p, sz); + else xorbuf(b, b, q, sz); + if (ctx->f&STRBF_VRFOUT) z |= nonzerop(q, sz); + } + } else { + /* As above, only the input is hardwired to zero. That means that we + * copy state bytes to the output (if any), and just clobber the state + * when we're done. + */ + + if (q) memcpy(q, b, sz); + memset(b, 0, sz); + } + + /* Set the @STRBF_NZERO@ flag if @z@ is nonzero. If @z@ is zero then + * subtracting one will set all of its bits, so, in particular, bits + * 8--15. Otherwise, @z@ is between 1 and 255, so bits 8--15 are clear and + * will remain so when we subtract one. + */ + if (ctx->f&STRBF_VRFOUT) ctx->f |= ((z - 1)&STRBF_NZERO) ^ STRBF_NZERO; + + /* Update the buffer cursor. */ + ctx->n += sz; +} + +/*----- Interface ---------------------------------------------------------*/ + +/* --- @strobe_init@ --- * + * + * Arguments: @strobe_ctx *ctx@ = pointer to context block to initialize + * @unsigned lambda@ = security parameter, in bits (must be a + * multiple of 32) + * + * Returns: --- + * + * Use: Initialize a STROBE context for use. + */ + +void strobe_init(strobe_ctx *ctx, unsigned lambda) +{ + const char v[] = "STROBEv1.0.2"; + kludge64 t[25]; + octet *p; + buf b; + unsigned n, i; + + /* Check the security parameter. */ + assert(lambda%32 == 0); assert(lambda <= 704); + ctx->r = (1600 - 2*lambda)/8; + + /* Set up the initial cSHAKE framing. */ + buf_init(&b, ctx->buf, ctx->r); + buf_putu8(&b, 1); buf_putu8(&b, ctx->r); + buf_putu8(&b, 1); buf_putu8(&b, 0); + buf_putu8(&b, 1); buf_putu8(&b, 8*(sizeof(v) - 1)); + buf_put(&b, v, sizeof(v) - 1); + assert(BOK(&b)); + n = BLEN(&b); if (n%8) memset(ctx->buf + n, 0, 8 - n%8); + + /* Cycle the sponge once initially, and get the first output buffer. */ + keccak1600_init(&ctx->k); + for (i = 0, p = ctx->buf; i < (n + 7)/8; i++) + { LOAD64_L_(t[i], p); p += 8; } + keccak1600_set(&ctx->k, t, (n + 7)/8); + keccak1600_p(&ctx->k, &ctx->k, 24); + keccak1600_extract(&ctx->k, t, ctx->r/8); + for (i = 0, p = ctx->buf; i < ctx->r/8; i++) + { STORE64_L_(p, t[i]); p += 8; } + + /* Initialize the other parts of the state. */ + ctx->n = ctx->n0 = 0; ctx->f = 0; +} + +/* --- @strobe_begin@ --- * + * + * Arguments: @strobe_ctx *ctx@ = pointer to context block + * @unsigned op@ = bitmask of flags + * + * Returns: --- + * + * Use: Begin a STROBE operation. The flags determine the behaviour + * of the @strobe_process@ and @strobe_done@ functions. + * + * * The @I@ bit determines the primary direction of data + * movement. If it's clear, data comes from the application + * into STROBE. If it's set, data comes from STROBE towards + * the application. + * + * * The @C@ bit activates cryptographic processing. If it's + * clear, then the input and output data would be equal, so + * @dest@ must be null. If it's set, then input data is + * XORed with the keystream on its way to the output. + * + * * The @A@ bit determines whether the application is + * engaged. If it's set, then the input or output buffer + * (according to whether @I@ is clear or set, respectively) + * holds the application data. If it's clear, and @I@ is + * clear, then zero bytes are fed in; if @I@ is set, then + * the output is compared with zero, and @strobe_done@ + * reports the outcome of this comparison. + * + * * The @T@ bit determines whether the transport is engaged. + * If it's set, then the input or output buffer (according + * to whether @I@ is set or clear, respectively) holds + * transport data. If it's clear, and @I@ is set, then zero + * bytes are fed in; if @I@ is clear, then the output is + * discarded. + * + * * The @M@ bit marks the data as metadata, but has no other + * effect. + */ + +void strobe_begin(strobe_ctx *ctx, unsigned op) +{ + /* Preliminary checking. We shouldn't have an operation underway, and the + * operation shouldn't have reserved bits set. + */ + assert(!(ctx->f&STRBF_ACTIVE)); assert(!(op&~STRBF_VALIDMASK)); + + /* Reset our operation state. */ + ctx->f &= STRBF_STMASK; + + /* Operation framing. Chain back to the start of the previous frame and + * write the new operation code. Set the sticky asymmetry bit here if + * necessary. + */ + ctx->buf[ctx->n++] ^= ctx->n0; ctx->n0 = ctx->n; + if (ctx->n >= ctx->r - 2) crank(ctx); + if (!(op&STRBF_T)) + ctx->buf[ctx->n++] ^= U8(op); + else { + if (!(ctx->f&STRBF_INIT)) ctx->f |= STRBF_INIT | (op&STRBF_I); + ctx->buf[ctx->n++] ^= U8(op ^ ctx->f); + } + if (ctx->n >= ctx->r - 2 || (op&STRBF_C)) crank(ctx); + + /* The operation is now underway. */ + ctx->f |= STRBF_ACTIVE; + + /* Determine whether we expect input and/or output. */ + if (op&(op&STRBF_I ? STRBF_T : STRBF_A)) + ctx->f |= STRBF_WANTIN; + if ((op&STRBF_C) && op&(op&STRBF_I ? STRBF_A : STRBF_T)) + ctx->f |= STRBF_WANTOUT; + + /* Determine whether the keystream is engaged, and how it fits in. */ + if (op&STRBF_C) { + ctx->f |= STRBF_CRYPTO; + if ((op&(STRBF_I | STRBF_T)) != STRBF_T) ctx->f |= STRBF_MIXOUT; + } + + /* Determine whether the output is supposed to be all-bytes-zero. */ + if ((op&(STRBF_I | STRBF_A | STRBF_T)) == (STRBF_I | STRBF_T)) + ctx->f |= STRBF_VRFOUT; + + /* The operation is now underway. */ + ctx->f |= STRBF_ACTIVE; +} + +/* --- @strobe_process@ --- * + * + * Arguments: @strobe_ctx *ctx@ = pointer to context block + * @const void *src@ = pointer to input data, or null + * @void *dest@ = pointer to output data, or null + * @size_t sz@ = common buffer length + * + * Returns: --- + * + * Use: Process data through the active STROBE operation. The exact + * behaviour depends on the flags passed to @strobe_begin@; see + * that function for details. If @src@ is null, then the + * behaviour is as if the input consists of @sz@ zero bytes. If + * @dest@ in null, then the output is discarded. + */ + +void strobe_process(strobe_ctx *ctx, const void *src, void *dest, size_t sz) +{ + const octet *p = src; octet *q = dest; + unsigned spare; + + /* Make sure that things are set up properly. */ + assert(ctx->f&STRBF_ACTIVE); + if (!(ctx->f&STRBF_WANTIN)) assert(!src); + if (!(ctx->f&STRBF_WANTOUT)) assert(!dest); + + /* Work through the input. */ + spare = ctx->r - ctx->n - 2; + if (sz < spare) + { process_buffer(ctx, p, q, sz); return; } + if (ctx->n) { + process_buffer(ctx, p, q, spare); crank(ctx); + if (p) { p += spare; } + if (q) { q += spare; } + sz -= spare; + } + + while (sz >= ctx->r - 2) { + process_buffer(ctx, p, q, ctx->r - 2); crank(ctx); + if (p) { p += ctx->r - 2; } + if (q) { q += ctx->r - 2; } + sz -= ctx->r - 2; + } + if (sz) process_buffer(ctx, p, q, sz); +} + +/* --- @strobe_done@ --- * + * + * Arguments: @strobe_ctx *ctx@ = pointer to context block + * + * Returns: Zero on success; @-1@ on verification failure (if @I@ and @T@ + * are set and @A@ is clear) + * + * Use: Concludes a STROBE operation, returning the result. + */ + +int strobe_done(strobe_ctx *ctx) +{ + assert(ctx->f&STRBF_ACTIVE); ctx->f &= ~STRBF_ACTIVE; + if (ctx->f&STRBF_VRFOUT) return (-(int)((ctx->f/STRBF_NZERO)&1u)); + else return (0); +} + +/* --- @strobe_key@, @strobe_ad@, @strobe_@prf@, @strobe_clrout@, + * @strobe_clrin@, @strobe_encout@, @strobe_encin@, @strobe_macout@, + * @strobe_macin@, @strobe_ratchet@ --- * + * + * Arguments: @strobe_ctx *ctx@ = pointer to context block + * + * Returns: @strobe_macin@ returns zero on success, or @-1@ on + * verification failure + * + * Use: Perform a STROBE operation on a single buffer. + */ + +static int op(strobe_ctx *ctx, unsigned f0, unsigned f1, + const void *src, void *dest, size_t sz) +{ + assert(!(f1&~STRBF_M)); + + strobe_begin(ctx, f0 | f1); + strobe_process(ctx, src, dest, sz); + return (strobe_done(ctx)); +} + +void strobe_key(strobe_ctx *ctx, unsigned f, const void *k, size_t sz) + { op(ctx, STROBE_KEY, f, k, 0, sz); } + +void strobe_ad(strobe_ctx *ctx, unsigned f, const void *h, size_t sz) + { op(ctx, STROBE_AD, f, h, 0, sz); } + +void strobe_prf(strobe_ctx *ctx, unsigned f, void *t, size_t sz) + { op(ctx, STROBE_PRF, f, 0, t, sz); } + +void strobe_clrout(strobe_ctx *ctx, unsigned f, const void *m, size_t sz) + { op(ctx, STROBE_CLROUT, f, m, 0, sz); } + +void strobe_clrin(strobe_ctx *ctx, unsigned f, const void *m, size_t sz) + { op(ctx, STROBE_CLRIN, f, m, 0, sz); } + +void strobe_encout(strobe_ctx *ctx, unsigned f, + const void *m, void *c, size_t sz) + { op(ctx, STROBE_ENCOUT, f, m, c, sz); } + +void strobe_encin(strobe_ctx *ctx, unsigned f, + const void *c, void *m, size_t sz) + { op(ctx, STROBE_ENCIN, f, c, m, sz); } + +void strobe_macout(strobe_ctx *ctx, unsigned f, void *t, size_t sz) + { op(ctx, STROBE_MACOUT, f, 0, t, sz); } + +int strobe_macin(strobe_ctx *ctx, unsigned f, const void *t, size_t sz) + { return (op(ctx, STROBE_MACIN, f, t, 0, sz)); } + +void strobe_ratchet(strobe_ctx *ctx, unsigned f, size_t sz) + { op(ctx, STROBE_RATCHET, f, 0, 0, sz); } + +/*----- Test rig ----------------------------------------------------------*/ + +#ifdef TEST_RIG + +#include +#include + +#include +#include +#include + +#define NSTATE 16 + +static strobe_ctx states[NSTATE]; + +static void dump(int rc, char win, const void *p, size_t sz) +{ + dstr d = DSTR_INIT; + const char *q = p; + size_t i; + codec *hex; + int printable; + + if (!p) { + if (!rc) putchar(win); + else putchar('-'); + } else { + for (i = 0, printable = 1; i < sz; i++) + if (!ISPRINT(q[i])) { printable = 0; break; } + if (printable) + printf("`%s'", q); + else { + hex = hex_class.encoder(CDCF_LOWERC, 0, 0); + hex->ops->code(hex, p, sz, &d); + dstr_write(&d, stdout); + hex->ops->destroy(hex); + } + } + dstr_destroy(&d); + putchar('\n'); +} + +typedef int opfunc(strobe_ctx *, unsigned, const void *, void *, size_t); + +static int op_init(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { strobe_init(ctx, sz); return (0); } + +static int op_copy(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { *ctx = states[sz]; return (0); } + +static int op_begin(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { strobe_begin(ctx, f); return (0); } + +static int op_process(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { strobe_process(ctx, p, q, sz); return (0); } + +static int op_done(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { return (strobe_done(ctx)); } + +static int op_key(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { strobe_key(ctx, f, p, sz); return (0); } + +static int op_ad(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { strobe_ad(ctx, f, p, sz); return (0); } + +static int op_prf(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { strobe_prf(ctx, f, q, sz); return (0); } + +static int op_clrout(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { strobe_clrout(ctx, f, p, sz); return (0); } + +static int op_clrin(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { strobe_clrin(ctx, f, p, sz); return (0); } + +static int op_encout(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { strobe_encout(ctx, f, p, q, sz); return (0); } + +static int op_encin(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { strobe_encin(ctx, f, p, q, sz); return (0); } + +static int op_macout(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { strobe_macout(ctx, f, q, sz); return (0); } + +static int op_macin(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { return (strobe_macin(ctx, f, p, sz)); } + +static int op_ratchet(strobe_ctx *ctx, unsigned f, + const void *p, void *q, size_t sz) + { strobe_ratchet(ctx, f, sz); return (0); } + +static const struct optab { + const char *name; + opfunc *op; +} optab[] = { +#define OP(op) { #op, op_##op } + OP(init), OP(copy), + OP(begin), OP(process), OP(done), + OP(key), OP(ad), OP(prf), + OP(clrout), OP(clrin), + OP(encout), OP(encin), + OP(macout), OP(macin), + OP(ratchet), + { 0 } +#undef OP +}; + +static int verify(dstr v[]) +{ + int r; + strobe_ctx *ctx; + const char *p; + char *q; + const struct optab *op; + dstr d0 = DSTR_INIT, d1 = DSTR_INIT; + codec *hex; + unsigned f; + const void *src, *destref; + void *dest; + size_t sz; + int rc, rcref; + int ok; + + /* First, get the register number. */ + r = *(int *)v[0].buf; ctx = &states[r]; + + /* Next job is to parse the command and flags. */ + q = v[1].buf; p = q; q += strcspn(q, "/"); if (*q) *q++ = 0; + for (op = optab; op->name; op++) + if (STRCMP(op->name, ==, p)) goto found_op; + abort(); +found_op: + + f = 0; + for (p = q; *p; p++) { + switch (*p) { + case 'I': f |= STRBF_I; break; + case 'C': f |= STRBF_C; break; + case 'A': f |= STRBF_A; break; + case 'T': f |= STRBF_T; break; + case 'M': f |= STRBF_M; break; + default: abort(); + } + } + + /* Convert the source parameter. */ + p = v[2].buf; + if (*p == '*') + { src = 0; sz = strtoul(p + 1, 0, 0); } + else if (*p == '=') + { src = p + 1; sz = v[2].len - 1; } + else if (*p == '!') { + hex = hex_class.decoder(CDCF_IGNCASE); + rc = hex->ops->code(hex, p + 1, v[2].len - 1, &d0); assert(!rc); + src = d0.buf; sz = d0.len; + hex->ops->destroy(hex); + } else + abort(); + + /* Convert the destination parameter. */ + p = v[3].buf; + if (*p == '+') + { destref = 0; rcref = 0; assert(v[3].len == 1); } + else if (*p == '-') + { destref = 0; rcref = -1; assert(v[3].len == 1); } + else if (*p == '=') + { destref = p + 1; assert(sz == v[3].len - 1); rcref = 0; } + else if (*p == '!') { + hex = hex_class.decoder(CDCF_IGNCASE); + rc = hex->ops->code(hex, p + 1, v[3].len - 1, &d1); assert(!rc); + destref = d1.buf; assert(sz == d1.len); + hex->ops->destroy(hex); + rcref = 0; + } else + abort(); + if (!destref) dest = 0; + else dest = xmalloc(sz); + + /* Do the operation. */ + rc = op->op(ctx, f, src, dest, sz); + + /* Check we got the right answer. */ + ok = (rc == rcref && (!destref || MEMCMP(dest, ==, destref, sz))); + if (!ok) { + printf("failed test\n"); + printf(" state = %d\n", r); + printf(" operation = %s%s%s%s%s%s%s\n", + op->name, + f ? "/" : "", + f&STRBF_I ? "I" : "", + f&STRBF_A ? "A" : "", + f&STRBF_C ? "C" : "", + f&STRBF_T ? "T" : "", + f&STRBF_M ? "M" : ""); + printf(" input = "); dump(0, '*', src, sz); + printf(" computed = "); dump(rc, '+', dest, sz); + printf(" expected = "); dump(rcref, '+', destref, sz); + } + + dstr_destroy(&d0); + dstr_destroy(&d1); + free(dest); + return (ok); +} + +static test_chunk tests[] = { + { "strobe", verify, + { &type_int, &type_string, &type_string, &type_string, 0 } }, + { 0, 0, { 0 } } +}; + +int main(int argc, char *argv[]) +{ + test_run(argc, argv, tests, SRCDIR "/t/strobe"); + return (0); +} + +#endif + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/symm/strobe.h b/symm/strobe.h new file mode 100644 index 00000000..c53fc399 --- /dev/null +++ b/symm/strobe.h @@ -0,0 +1,233 @@ +/* -*-c-*- + * + * The STROBE protocol framework + * + * (c) 2018 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software: you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/*----- Notes on the STROBE framework -------------------------------------* + * + * Mike Hamburg's STROBE framework is an attempt to do pretty much all the + * obvious parts of symmetric cryptography using a single primitive: the + * Keccak-p[1600, 24] sponge function which underlies the SHA3. This works + * excellently on tiny devices, because it's possible to implement + * Keccak-p[1600, 24] with a very small amount of code, and STROBE requires + * minimal additional state beyond the 200-byte Keccak-p[1600, n] state, + * which can be updated more-or-less in place. + * + * STROBE is stateful. The idea is that the two parties' STROBE states + * should evolve in lockstep with each other. This will obviously go wrong + * if messages cross over, or are reordered or duplicated. + * + * The model for a single STROBE-based endpoint consists of the application, + * and a transport for communicating with a peer. All data gets mixed into + * the STROBE state. Some operations mix input data with a keystream + * generated by the sponge function (used in duplexing mode), which can then + * be used to encrypt and authenticate messages. + */ + +#ifndef CATACOMB_STROBE_H +#define CATACOMB_STROBE_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#ifndef CATACOMB_KECCAK1600_H +# include "keccak1600.h" +#endif + +/*----- Data structures ---------------------------------------------------*/ + +#define STRBF_I 0x01 /* inbound */ +#define STRBF_A 0x02 /* application */ +#define STRBF_C 0x04 /* cipher */ +#define STRBF_T 0x08 /* transport */ +#define STRBF_M 0x10 /* metadata */ +#define STRBF_K 0x20 /* key-tree (not implemented) */ +#define STRBF_VALIDMASK 0x1f /* valid bits */ +#define STRBF_OPMASK 0xff /* observable operation bits */ + +typedef struct strobe_ctx { + keccak1600_state k; + octet buf[200]; + unsigned f, r, n0, n; +#define STRBF_I0 0x0001 /* initiator/responder role */ +#define STRBF_INIT 0x0100 /* initiator/responder role set? */ +#define STRBF_STMASK 0x01ff /* mask for persistent state bits */ +#define STRBF_ACTIVE 0x0200 /* operation in progress */ +#define STRBF_WANTIN 0x0400 /* expect input buffer */ +#define STRBF_WANTOUT 0x0800 /* expect output buffer */ +#define STRBF_VRFOUT 0x1000 /* verify that output is nonzero */ +#define STRBF_NZERO 0x2000 /* nonzero output from update */ +#define STRBF_MIXOUT 0x4000 /* mix output into state */ +#define STRBF_CRYPTO 0x8000 /* mix sponge state into output */ +} strobe_ctx; + +#define STROBE_KEY (STRBF_A | STRBF_C) +#define STROBE_AD (STRBF_A) +#define STROBE_PRF (STRBF_I | STRBF_A | STRBF_C) +#define STROBE_CLROUT (STRBF_A | STRBF_T) +#define STROBE_CLRIN (STRBF_I | STRBF_A | STRBF_T) +#define STROBE_ENCOUT (STRBF_A | STRBF_C | STRBF_T) +#define STROBE_ENCIN (STRBF_I | STRBF_A | STRBF_C | STRBF_T) +#define STROBE_MACOUT (STRBF_C | STRBF_T) +#define STROBE_MACIN (STRBF_I | STRBF_C | STRBF_T) +#define STROBE_RATCHET (STRBF_C) + +#define STROBE_ROLEMASK (STRBF_I0 | STRBF_INIT) +#define STRBRL_UNDCD (0) /* undecided */ +#define STRBRL_INIT (STRBF_INIT) /* initiator */ +#define STRBRL_RESP (STRBF_I0 | STRBF_INIT) /* responder */ + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @strobe_init@ --- * + * + * Arguments: @strobe_ctx *ctx@ = pointer to context block to initialize + * @unsigned lambda@ = security parameter, in bits (must be a + * multiple of 32) + * + * Returns: --- + * + * Use: Initialize a STROBE context for use. + */ + +extern void strobe_init(strobe_ctx */*ctx*/, unsigned /*lambda*/); + +/* --- @strobe_begin@ --- * + * + * Arguments: @strobe_ctx *ctx@ = pointer to context block + * @unsigned op@ = bitmask of flags + * + * Returns: --- + * + * Use: Begin a STROBE operation. The flags determine the behaviour + * of the @strobe_process@ and @strobe_done@ functions. + * + * * The @I@ bit determines the primary direction of data + * movement. If it's clear, data comes from the application + * into STROBE. If it's set, data comes from STROBE towards + * the application. + * + * * The @C@ bit activates cryptographic processing. If it's + * clear, then the input and output data would be equal, so + * @dest@ must be null. If it's set, then input data is + * XORed with the keystream on its way to the output. + * + * * The @A@ bit determines whether the application is + * engaged. If it's set, then the input or output buffer + * (according to whether @I@ is clear or set, respectively) + * holds the application data. If it's clear, and @I@ is + * clear, then zero bytes are fed in; if @I@ is set, then + * the output is compared with zero, and @strobe_done@ + * reports the outcome of this comparison. + * + * * The @T@ bit determines whether the transport is engaged. + * If it's set, then the input or output buffer (according + * to whether @I@ is set or clear, respectively) holds + * transport data. If it's clear, and @I@ is set, then zero + * bytes are fed in; if @I@ is clear, then the output is + * discarded. + * + * * The @M@ bit marks the data as metadata, but has no other + * effect. + */ + +extern void strobe_begin(strobe_ctx */*ctx*/, unsigned /*op*/); + +/* --- @strobe_process@ --- * + * + * Arguments: @strobe_ctx *ctx@ = pointer to context block + * @const void *src@ = pointer to input data, or null + * @void *dest@ = pointer to output data, or null + * @size_t sz@ = common buffer length + * + * Returns: --- + * + * Use: Process data through the active STROBE operation. The exact + * behaviour depends on the flags passed to @strobe_begin@; see + * that function for details. If @src@ is null, then the + * behaviour is as if the input consists of @sz@ zero bytes. If + * @dest@ in null, then the output is discarded. + */ + +extern void strobe_process(strobe_ctx */*ctx*/, const void */*src*/, + void */*dest*/, size_t /*sz*/); + +/* --- @strobe_done@ --- * + * + * Arguments: @strobe_ctx *ctx@ = pointer to context block + * + * Returns: Zero on success; @-1@ on verification failure (if @I@ and @T@ + * are set and @A@ is clear) + * + * Use: Concludes a STROBE operation, returning the result. + */ + +extern int strobe_done(strobe_ctx */*ctx*/); + +/* --- @strobe_key@, @strobe_ad@, @strobe_@prf@, @strobe_clrout@, + * @strobe_clrin@, @strobe_encout@, @strobe_encin@, @strobe_macout@, + * @strobe_macin@, @strobe_ratchet@ --- * + * + * Arguments: @strobe_ctx *ctx@ = pointer to context block + * + * Returns: @strobe_macin@ returns zero on success, or @-1@ on + * verification failure + * + * Use: Perform a STROBE operation on a single buffer. + */ + +extern void strobe_key(strobe_ctx */*ctx*/, unsigned /*f*/, + const void */*k*/, size_t /*sz*/); +extern void strobe_ad(strobe_ctx */*ctx*/, unsigned /*f*/, + const void */*h*/, size_t /*sz*/); +extern void strobe_prf(strobe_ctx */*ctx*/, unsigned /*f*/, + void */*t*/, size_t /*sz*/); +extern void strobe_clrout(strobe_ctx */*ctx*/, unsigned /*f*/, + const void */*m*/, size_t /*sz*/); +extern void strobe_clrin(strobe_ctx */*ctx*/, unsigned /*f*/, + const void */*m*/, size_t /*sz*/); +extern void strobe_encout(strobe_ctx */*ctx*/, unsigned /*f*/, + const void */*m*/, void */*c*/, size_t /*sz*/); +extern void strobe_encin(strobe_ctx */*ctx*/, unsigned /*f*/, + const void */*c*/, void */*m*/, size_t /*sz*/); +extern void strobe_macout(strobe_ctx */*ctx*/, unsigned /*f*/, + void */*t*/, size_t /*sz*/); +extern int strobe_macin(strobe_ctx */*ctx*/, unsigned /*f*/, + const void */*t*/, size_t /*sz*/); +extern void strobe_ratchet(strobe_ctx */*ctx*/, unsigned /*f*/, + size_t /*sz*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/symm/t/poly1305 b/symm/t/poly1305 index b03a0a83..4d23ac69 100644 --- a/symm/t/poly1305 +++ b/symm/t/poly1305 @@ -1,5 +1,7 @@ +### Poly1305 tests. + poly1305-hash { - ## The tests from Danial J. Bernstein, `The Poly1305-AES message- + ## The tests from Daniel J. Bernstein, `The Poly1305-AES message- ## authentication code', 2005-03-29, Appendix B, ## https://cr.yp.to/mac/poly1305-20050329.pdf 851fc40c3467ac0be05cc20404f3f700 580b3b0f9447bb1e69d095b5928b6dbc @@ -90,23 +92,21 @@ poly1305-mct { 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 - 1 596382b2c34704b87e291250fcb927fd; - 00000000000000000000000000000000 - 00000000000000000000000000000000 - 00000000000000000000000000000000 - 10 7f1d971da577bdd6fb24437aaac845f5; - 00000000000000000000000000000000 - 00000000000000000000000000000000 - 00000000000000000000000000000000 - 100 e1cb88ba2c498ade2091ab06cefa24fd; - 00000000000000000000000000000000 - 00000000000000000000000000000000 - 00000000000000000000000000000000 - 1000 f7064b7217e8a6b74b381c58175d9ff2; - - ## The full test. This takes aaaaages. - ##00000000000000000000000000000000 - ## 00000000000000000000000000000000 - ## 00000000000000000000000000000000 - ## 1000000 df62013a9d388ea6e82cb7295fa706ec; + 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 0 1 596382b2c34704b87e291250fcb927fd; + 87d0ce793cb4cb966c8dd6ad0e6c6799 + 18bf6ee80c6d2d8d11ffe4f5b7214cf2 + 2a3dbb3acef3b4172e5f7045a33bd038 + 66f75c0e0c7a40658629e3392f7f8e3349a02191ffd49f39879a8d9d1d0e23ea3caa4d240bd2ab8a8c4a6bb8d3288d9de4b793f05e97646dd4d98055defc3e0677d956b4c62664bac15962ab15d93ccbbc03aafdbde779162ed93b55361f0f8acaa41d50ef5175927fe79ea316186516eef15001cd04d3524a55e4fa3c5ca479d3aaa8a897c21807f721b6270ffc68b6889d81a116799f6aaa35d8e04c7a7dd5e6da2519e8759f54e906696f5772fee093283bcef7b930aed50323bcbc8c820c67422c1e16bdc022a9c0277c9d95fef0ea4ee11e2b27276da811523c5acb80154989f8a67ee9e3fa30b73b0c1c34bf46e3464d977cd7fcd0ac3b82721080bb0d9b982ee2c77feee983d7ba35da88ce86955002940652ab63bc56fb16f994da2b01d74356509d7d1b6d7956b0e5a557757bd1ced2eef8650bc5b6d426108c1518abcbd0befb6a0d5fd57a3e2dbf31458eab63df66613653d4beae73f5c40eb438fbcfdcf4a4ba46320184b9ca0da4dfae77de7ccc910356caea3243f33a3c81b064b3b7cedc7435c223f664227215715980e6e0bb570d459ba80d7512dbe458c8f0f3f52d659b6e8eef19ee71aea2ced85c7a42ffca6522a62db49a2a46eff72bd7f7e0883acd087183f0627f3537a4d558754ed63358e8182bee196735b361dc9bd64d5e34e1074a855655d2974cc6fa1653754cf40f561d8c7dc526aab2908ec2d2b977cde1a1fb1071e32f40e049ea20f30368ba1592b4fe57fb51595d23acbdace324cdd78060a17187c662368854e915402d9b52fb21e984663e41c26a109437e162cfaf071b53f77e50000a5388ff183b82ce7a1af476c416d7d204157b3633b2f4ec077b699b032816997e37bceded8d4a04976fd7d0c0b029f290794c3be504c5242287ea2f831f11ed5690d92775cd6e863d7731fd4da687ebfb13df4c41dc0fb8aec3d45ef6076ec131cdd55e724bae2931260484b73b12fdf2546d852cf089b64f622809c6ff963010d8aebc14e0cf4d5d06fc92e8e022a2a97dd822329d7bf26baf95f2f25fd2274cb4e10a596b570a97176273053be918c9a0a12ab3f61d83229e403e512b9f1fd89824ec9942ee782ee6972faf4cb5898e4a123c5e9e3f3fb71397d435b4de0f83627475ee6170f55646fc8aa52375fe4aae3711138b550c5f5654106b4ba7bc268ae7f63cf8a2401b2ba6c0894177e3f6696f9086760c3ce4e16844d1780c10b7785dcb3641fb488f077eacb89b8cf5b627a4449224923378dab62b90ef1c2dc7f1f7a196304e1805ee1a13f8cf12aed5526b82f62b5759ff12a573f68b8ccbe2d01d2e5615802edc3ef0a7778a1c565d9a836d770d1f5812e5698af213e398e0357834fffcde35d1cb77c821d21377401341f898475a4df74c58 + 1 10 7f1d971da577bdd6fb24437aaac845f5; + 9eff9b802b9d43922975aea18a62834a + 1f22e8534c98f80321384c88d00972d7 + 2b2f5dfbc2aeaead12f8dfaad90d4ec9 + b84d332e02eda38a6cf2763928ba014bc632a0f2c0266f9093edc731eb2ce1e61c9a10bcb4bbd2b4b69e3f84da37dcc011142fbf8222c68a31f9bc227ead19f629ff4d6d414e95d3e9a3b2998d8e6db6379112f2d1e5edb32294729ad7d1c087b87d1c26a7e3b491f9d4452a2da50df87027d0f52c75e281091e86c86cc15091b0a6459a94e74597f86cd4801b4413f6185a7dd87f5d6bd3dfc8717afabd833c08160428a0fd0c2f9934921e9a7e40d9bf8a4d61781985aa6691cec4f466ab0bd8fe4cce660b31aeedec1cdc8ae76d3b9a01e5b5ca1e5d6932096bef253f427da7e974eab8aca5c0f22c9f0b5f1598e857f28c4a0b313890c718130e6bd18a150e5aa18e2f19bec1cb88adaeade2122ee13193287bbab77b2a334ffd9ba26210d170b6e7c7edd2fb6808d03acac8fe38b87a7523926e04028bc8df5753ced4b1bdf0b8a8d2315d210433417509dc685c660bd97678ef2d470c5845497d7d726e5ffbcc6e4db02d5d1a6a8b4629580ca3fae7392b33a992e73afd9e07c1cdc3576aedf6d2330f8dcf5d148873799e76cd4b8dda4eba698178d5edd6b260ef84af4517fbb68810571cfa7df52e241d8c43d3e035e23684e386c5d57cd0c4755614a6b8fb3529ee54ae2b2393dba3638795ec5c30c84dd3651ad75f96a4675df0b2d7e67329c5eefd62c2b4c378e2212ffcd788690201ba9eadb62395e8b3ca8bdacddc3a96f36456d0f95edb90dd975dd685348ada9658dc0d452cc99288460e12749a433770560281e6cc60a0cc654c664e53ebfa5bbc71c67993418c32ce1eb2643f64e9357b1ecca7805de5b75a3b8b918d719a229662fa8f0100bf6d717823b41b72fb9e91edcc75eaaabb066f7a714bcb0dfecf8d6944295cd40e3e53b041ac373e4dfe54336c9ce2417f297b64b1a5f45223a3b135c2faa6dda33a8128a545fca0dafeec4c1e6d187b336712802abdc8f658e5b3cbfc8961c0324e138cfafe635e3d47537890ccb5557d71e77419da3f7f513d22c6300cd51397c2e9f07d4584cd54081ec5339a43d64d6607159b9227f32e112b48e6644c7543be64d2015bddb8e50093143761a48cacb3376f16a34b86f82f066c9087d74663082549784b44444ee744cf5b62a5efc25aea49238df87dbecb07d9977901b70db24f8d851cb78b50d814be55d91c0775b5b7acb6a5651b260427057f6c86cd9fa709205f1d40431cfef5e3f90bc124637bcf35cdf343e3f52ce4236db25ac29aba0a8c69a9b0f1ae180f5be6e9c2a79fa522078604855624d5ec85a113738ee0a81f2d6ca9be1caa26ba628b0b1429c06b371f5e2801fec0599588417c02d21b6412ba01b904cae803c61e83ddf17e083a9b42564255c49ca91c2f079f8fc9439fe93ae322de7480df086018 + 10 100 e1cb88ba2c498ade2091ab06cefa24fd; + 0c4ac91a4cbcd2ddec777ed12feaee3b + 0411d5def6fe88aff66611870f7b83d8 + 5ba282f23cef22af42c21616603b8f25 + 1d630211fdf32fb5c4432d71b34bfd6daa6aac7ef09bd1656e7b710a70166f0e926d61116e29486f407724d581acf140d70b1352195971ed43c0857c2fe1018cb570317da3f5eae2b83e795e3375106bad876f2fd2cec8b972681cd3b0f4f75442694294f3659b0d452c8e39728e73bf402783d9c9ba3bc913473c747f212aa29d5303614869abce329d8c872a0da70f7d3e0d48a99f076117e32f313584d8531de33d8f99fc5379cc4f0dc2fb785bbdc372868d7598d7c2773ec7c6717f45c45a0d1db3f9fe342c809c407b56af8bc5896ecbd27c7c2517884018142a37cc50ad0a754dc5f2fbbcfdc45a8a0f4815c1ab75ea15fb1585e475c6ecd452a75fe4fad2719e5915b044833b67174cd6eaf4106d6e3512c9986e34442da5d46700c68b391f6dd0beb025f0ec3b719cfb643313223dc859c824025dddfb3cef7084c5ae0b139930c51f8f925d4c5cf7e9c8b6af2e43eed9fb2bbead8999e9d629d1e346244f5ef6034a018517954508ea83a1f32a682b927b5814a67bf581980d620e6f2102344121c74f774721d2e24a7fe426fd216ffcd1d7a3abeec214e21e6db6d17b5237ece8ad09e19643e0cbdc1a25e72d1e58c85810900e965e2f42ee1a5d947ea20815c46019cafefbf808264cd151a551607d57295b08b130dcc936c32b9d9c586ecf2d2ae62f353b0fb9c0a9b2eb4dc50a7ac12cb1c782415adfe6a2ef7d58f4c893d2cb501785e5d5fc4439847018ffa0142707474dcb40736a379a408f92bf83e719e9599e35969f0f3ec47c1a1a78b2663468609f818f1b3fe267f2acd2ddcda6068f9b3d2e021f9d63b9c6d922f6a879f763f17c9fa4fc64c18ba9fee1ad1a383e9384b6978595bfddd54a08ec4a1b56fb7dbf7fe9de9b60cf4d8d3082b238864ef23f0336756f4b0e00e30872600f87b76112d6c4b2ec1cd5c234cd4e3062f886040b8e4a6d267edecad7966ee43e92428c44ef294c18434f4ee0317ab1856e8b01acc9a821b7594462da20b7cbca013b9e6ef07434dfba97cf4b41fdd36aadc8ffdfc95bec624b227af1ea06af10cae4fea72ada570f2df547c3e9fd8ddd84283cebb8de338d5e83e454faa25394dad66457f66a3813993888606876d2bec156e29755d251e88af6c1603c3ae0427db9de04bb2e83a85eab1d6180553f35d9d0ee1dca7a3083f47ea6e1c7686fd89910b4f0bb5a768ea7ee97d55ce24c0bb733239074f79f7ccb25ccdcddd0c2d2693e37546cf8237451828b4147a688f2a2a891e2e23108bafd413e512c4a50dfd101c44ec1b27acbf4d68b566a6a544c81d5f67ab7ed5d7b42dcd389d367539b360fbc70eee289699a9000cddff14bc212ec8a6e18ab58c19727c403b99a7b982fc660f3d8f54e096dad2a281308d4f5d50fc6e1 + 100 1000 f7064b7217e8a6b74b381c58175d9ff2; } diff --git a/symm/t/poly1305.slow b/symm/t/poly1305.slow new file mode 100644 index 00000000..c2443c0f --- /dev/null +++ b/symm/t/poly1305.slow @@ -0,0 +1,46 @@ +### Very slow tests for Poly1305. + +poly1305-mct { + ## Monte-Carlo test from https://cr.yp.to/mac/test.html, converted from + ## the original hash-the-transcript form; see `t/poly1305' for detais. + + ## These are duplicated from the basic `poly1305' set, as a simple smoke + ## test. + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + 0 1 596382b2c34704b87e291250fcb927fd; + 87d0ce793cb4cb966c8dd6ad0e6c6799 + 18bf6ee80c6d2d8d11ffe4f5b7214cf2 + 2a3dbb3acef3b4172e5f7045a33bd038 + 66f75c0e0c7a40658629e3392f7f8e3349a02191ffd49f39879a8d9d1d0e23ea3caa4d240bd2ab8a8c4a6bb8d3288d9de4b793f05e97646dd4d98055defc3e0677d956b4c62664bac15962ab15d93ccbbc03aafdbde779162ed93b55361f0f8acaa41d50ef5175927fe79ea316186516eef15001cd04d3524a55e4fa3c5ca479d3aaa8a897c21807f721b6270ffc68b6889d81a116799f6aaa35d8e04c7a7dd5e6da2519e8759f54e906696f5772fee093283bcef7b930aed50323bcbc8c820c67422c1e16bdc022a9c0277c9d95fef0ea4ee11e2b27276da811523c5acb80154989f8a67ee9e3fa30b73b0c1c34bf46e3464d977cd7fcd0ac3b82721080bb0d9b982ee2c77feee983d7ba35da88ce86955002940652ab63bc56fb16f994da2b01d74356509d7d1b6d7956b0e5a557757bd1ced2eef8650bc5b6d426108c1518abcbd0befb6a0d5fd57a3e2dbf31458eab63df66613653d4beae73f5c40eb438fbcfdcf4a4ba46320184b9ca0da4dfae77de7ccc910356caea3243f33a3c81b064b3b7cedc7435c223f664227215715980e6e0bb570d459ba80d7512dbe458c8f0f3f52d659b6e8eef19ee71aea2ced85c7a42ffca6522a62db49a2a46eff72bd7f7e0883acd087183f0627f3537a4d558754ed63358e8182bee196735b361dc9bd64d5e34e1074a855655d2974cc6fa1653754cf40f561d8c7dc526aab2908ec2d2b977cde1a1fb1071e32f40e049ea20f30368ba1592b4fe57fb51595d23acbdace324cdd78060a17187c662368854e915402d9b52fb21e984663e41c26a109437e162cfaf071b53f77e50000a5388ff183b82ce7a1af476c416d7d204157b3633b2f4ec077b699b032816997e37bceded8d4a04976fd7d0c0b029f290794c3be504c5242287ea2f831f11ed5690d92775cd6e863d7731fd4da687ebfb13df4c41dc0fb8aec3d45ef6076ec131cdd55e724bae2931260484b73b12fdf2546d852cf089b64f622809c6ff963010d8aebc14e0cf4d5d06fc92e8e022a2a97dd822329d7bf26baf95f2f25fd2274cb4e10a596b570a97176273053be918c9a0a12ab3f61d83229e403e512b9f1fd89824ec9942ee782ee6972faf4cb5898e4a123c5e9e3f3fb71397d435b4de0f83627475ee6170f55646fc8aa52375fe4aae3711138b550c5f5654106b4ba7bc268ae7f63cf8a2401b2ba6c0894177e3f6696f9086760c3ce4e16844d1780c10b7785dcb3641fb488f077eacb89b8cf5b627a4449224923378dab62b90ef1c2dc7f1f7a196304e1805ee1a13f8cf12aed5526b82f62b5759ff12a573f68b8ccbe2d01d2e5615802edc3ef0a7778a1c565d9a836d770d1f5812e5698af213e398e0357834fffcde35d1cb77c821d21377401341f898475a4df74c58 + 1 10 7f1d971da577bdd6fb24437aaac845f5; + 9eff9b802b9d43922975aea18a62834a + 1f22e8534c98f80321384c88d00972d7 + 2b2f5dfbc2aeaead12f8dfaad90d4ec9 + b84d332e02eda38a6cf2763928ba014bc632a0f2c0266f9093edc731eb2ce1e61c9a10bcb4bbd2b4b69e3f84da37dcc011142fbf8222c68a31f9bc227ead19f629ff4d6d414e95d3e9a3b2998d8e6db6379112f2d1e5edb32294729ad7d1c087b87d1c26a7e3b491f9d4452a2da50df87027d0f52c75e281091e86c86cc15091b0a6459a94e74597f86cd4801b4413f6185a7dd87f5d6bd3dfc8717afabd833c08160428a0fd0c2f9934921e9a7e40d9bf8a4d61781985aa6691cec4f466ab0bd8fe4cce660b31aeedec1cdc8ae76d3b9a01e5b5ca1e5d6932096bef253f427da7e974eab8aca5c0f22c9f0b5f1598e857f28c4a0b313890c718130e6bd18a150e5aa18e2f19bec1cb88adaeade2122ee13193287bbab77b2a334ffd9ba26210d170b6e7c7edd2fb6808d03acac8fe38b87a7523926e04028bc8df5753ced4b1bdf0b8a8d2315d210433417509dc685c660bd97678ef2d470c5845497d7d726e5ffbcc6e4db02d5d1a6a8b4629580ca3fae7392b33a992e73afd9e07c1cdc3576aedf6d2330f8dcf5d148873799e76cd4b8dda4eba698178d5edd6b260ef84af4517fbb68810571cfa7df52e241d8c43d3e035e23684e386c5d57cd0c4755614a6b8fb3529ee54ae2b2393dba3638795ec5c30c84dd3651ad75f96a4675df0b2d7e67329c5eefd62c2b4c378e2212ffcd788690201ba9eadb62395e8b3ca8bdacddc3a96f36456d0f95edb90dd975dd685348ada9658dc0d452cc99288460e12749a433770560281e6cc60a0cc654c664e53ebfa5bbc71c67993418c32ce1eb2643f64e9357b1ecca7805de5b75a3b8b918d719a229662fa8f0100bf6d717823b41b72fb9e91edcc75eaaabb066f7a714bcb0dfecf8d6944295cd40e3e53b041ac373e4dfe54336c9ce2417f297b64b1a5f45223a3b135c2faa6dda33a8128a545fca0dafeec4c1e6d187b336712802abdc8f658e5b3cbfc8961c0324e138cfafe635e3d47537890ccb5557d71e77419da3f7f513d22c6300cd51397c2e9f07d4584cd54081ec5339a43d64d6607159b9227f32e112b48e6644c7543be64d2015bddb8e50093143761a48cacb3376f16a34b86f82f066c9087d74663082549784b44444ee744cf5b62a5efc25aea49238df87dbecb07d9977901b70db24f8d851cb78b50d814be55d91c0775b5b7acb6a5651b260427057f6c86cd9fa709205f1d40431cfef5e3f90bc124637bcf35cdf343e3f52ce4236db25ac29aba0a8c69a9b0f1ae180f5be6e9c2a79fa522078604855624d5ec85a113738ee0a81f2d6ca9be1caa26ba628b0b1429c06b371f5e2801fec0599588417c02d21b6412ba01b904cae803c61e83ddf17e083a9b42564255c49ca91c2f079f8fc9439fe93ae322de7480df086018 + 10 100 e1cb88ba2c498ade2091ab06cefa24fd; + 0c4ac91a4cbcd2ddec777ed12feaee3b + 0411d5def6fe88aff66611870f7b83d8 + 5ba282f23cef22af42c21616603b8f25 + 1d630211fdf32fb5c4432d71b34bfd6daa6aac7ef09bd1656e7b710a70166f0e926d61116e29486f407724d581acf140d70b1352195971ed43c0857c2fe1018cb570317da3f5eae2b83e795e3375106bad876f2fd2cec8b972681cd3b0f4f75442694294f3659b0d452c8e39728e73bf402783d9c9ba3bc913473c747f212aa29d5303614869abce329d8c872a0da70f7d3e0d48a99f076117e32f313584d8531de33d8f99fc5379cc4f0dc2fb785bbdc372868d7598d7c2773ec7c6717f45c45a0d1db3f9fe342c809c407b56af8bc5896ecbd27c7c2517884018142a37cc50ad0a754dc5f2fbbcfdc45a8a0f4815c1ab75ea15fb1585e475c6ecd452a75fe4fad2719e5915b044833b67174cd6eaf4106d6e3512c9986e34442da5d46700c68b391f6dd0beb025f0ec3b719cfb643313223dc859c824025dddfb3cef7084c5ae0b139930c51f8f925d4c5cf7e9c8b6af2e43eed9fb2bbead8999e9d629d1e346244f5ef6034a018517954508ea83a1f32a682b927b5814a67bf581980d620e6f2102344121c74f774721d2e24a7fe426fd216ffcd1d7a3abeec214e21e6db6d17b5237ece8ad09e19643e0cbdc1a25e72d1e58c85810900e965e2f42ee1a5d947ea20815c46019cafefbf808264cd151a551607d57295b08b130dcc936c32b9d9c586ecf2d2ae62f353b0fb9c0a9b2eb4dc50a7ac12cb1c782415adfe6a2ef7d58f4c893d2cb501785e5d5fc4439847018ffa0142707474dcb40736a379a408f92bf83e719e9599e35969f0f3ec47c1a1a78b2663468609f818f1b3fe267f2acd2ddcda6068f9b3d2e021f9d63b9c6d922f6a879f763f17c9fa4fc64c18ba9fee1ad1a383e9384b6978595bfddd54a08ec4a1b56fb7dbf7fe9de9b60cf4d8d3082b238864ef23f0336756f4b0e00e30872600f87b76112d6c4b2ec1cd5c234cd4e3062f886040b8e4a6d267edecad7966ee43e92428c44ef294c18434f4ee0317ab1856e8b01acc9a821b7594462da20b7cbca013b9e6ef07434dfba97cf4b41fdd36aadc8ffdfc95bec624b227af1ea06af10cae4fea72ada570f2df547c3e9fd8ddd84283cebb8de338d5e83e454faa25394dad66457f66a3813993888606876d2bec156e29755d251e88af6c1603c3ae0427db9de04bb2e83a85eab1d6180553f35d9d0ee1dca7a3083f47ea6e1c7686fd89910b4f0bb5a768ea7ee97d55ce24c0bb733239074f79f7ccb25ccdcddd0c2d2693e37546cf8237451828b4147a688f2a2a891e2e23108bafd413e512c4a50dfd101c44ec1b27acbf4d68b566a6a544c81d5f67ab7ed5d7b42dcd389d367539b360fbc70eee289699a9000cddff14bc212ec8a6e18ab58c19727c403b99a7b982fc660f3d8f54e096dad2a281308d4f5d50fc6e1 + 100 1000 f7064b7217e8a6b74b381c58175d9ff2; + + ## And the ones which take a long time... + e67ee956b65e3e7a221eafc7f0ea01eb + 0ffbe8b89a03187d5f7214b8d288b467 + cce953773f0420b28340a8faf43dcebe + 97cba51fcc9a9854abe0d283981461d28bcbd2864cb1ffbcfee66dbf40e7a45824f0b7557bb3e2b42792aea3a8faecba712922c05fecc559b6ac2297ab5b1d2e308199c205b5a2e5ebcfdc27843e03d391110e79470308d67b03cdfd023493cc946bd635876f42935c89076a577557af4bdba7f1cd397c8b05f159d8207d7a5a8435f1ce996eeef6dd059bac6ea521125a8b972fae3be55d35a7536c441cba26013d9a21fab4c9c94241c7d862146386951d4955fac4916178dff83af45252341659aa2714b710adc9d7953dc06584fb9f34480b6b834e1ab10b138db634130d3cd607ce93722070b16b83aa71559e2d515c3b582b7a630d5b825c5df4faecefd9cd905043a85c4caec7502d238b25c06b4231b8af96aae7f58446c0b21b37b2a8c7af43406d4002eef6e0311358f3dd27d8123032a9bfed32aa916f59e286739760e58bf9e6675a3353b2678c0aa858596199b5116071b0f7f8b6e8683f46cd78f0e69c5879da781dddfa1b92ef3dd187add8deb65c22ffc302866738cb62e38c4c0320cc55a9533067ec6fcdf5d432bbae9d79e1b1c7fe84a52f603ecb4352f5d99bdecbfde786ff8e98b1b4997b28fd5515ce14198a8678595c0b457b174a816989f2df949a21a5b70adef9c04c6b0371c80b2b50d70c7b8fbf122abde9d815927050734b29973038c1413d23a1d2cd282ee6a6c701595f6900728aad1f0a1ff9e00a2e5412c7da0bc20b86e7e5fbe4137838c0dcc8d32b02359b45a85f98fe069b4a32061110d0dc0f6a15eadd409e292b26b9f250b6c6f4e9315320d8f36601fbcbe01163f59a3641e0ccd29f037b93fa6242696f68aaace3fc2882270a7e90dad52a265ca9b26071feafa347fbb79e1cd64141c7eca85e53c4c0b8b51f228596de801f181860bdf0ae48f025152611d92b7f440ca3efd072ddb1daf16bab4ed3b3be38a2b898a0c7e2c691f48ef88ee31711df2e2a2b739367412e44eb21cd56daba191f4b92500e2cc60e9ed77dfe8eacd89b35804070879326a3d732a73850c6ce90d68652be417fd37200888ee0b6f946050d4057613c9dd1e0c4b6923e01bde22735505ffa967311d72931a58be47157a09404ff67116b44c11efb021c39987f1ab9bbba3ae7b20cff80b058de38335f338bf8b2b3abd08d8b21a83c9713e8187ef98a43f811142d2b16c1c9919e242662350069c7dc59f70f499544f4eedea9ce55f8a6cd637d983191960eac0510f6013b21342a76571fcba18c9374d6554009fcbaee3db509f404edd4bc9d6fbaf9cad1b8007beadbe864a7c58eddd25c039dbcf43ed3eb03295d144f9f2186ea704fb065a5e3594364354f5c6720ee4c6f8731235d1f98851f586fee0952ed0e1d201f673e25377111a5212537fb4ff2f69dfcc7 + 1000 10000 526fbba33c160146160ab9abc3514d70; + de4b1444f9b3e24182a8f893069985ad + 662f8dcf37c2537fa4acda7f8588afca + 0d03c71cccec848e7b613408b41c1ac8 + 37b9a507c7e12a61a6e9248ba6b2c9b09373551a15bf70dee6a24059a9f32cd24519fba4fccb04d91dad2ac4b3f58850d6fc3e3fe7ecc43d2e6a61c4e7f31c3ec0bf91e272c7500e7697695893cdff766e1acfb858d7e78d899c50eb73ed4d7c27e54c1e3f26316d943cbb70ead8605b7fb1f9314f910cd058671069cda3e83085c9e145d1f7834cbf6d3688d9bd485e165567c39b45f70d865be3730b46f4673348250aa0984ba91ae35b911e0ddcb6536c94abc68ce1d040808cda931f0e327ec06face8eff8a84b587b57cead5be8082571cdf60c26c79b6004113659de81e6e59815c0ccbbe4fbff6f451851f406135806292a21ea803c1a64ebaf4274649f07533e6835c4aa8831c30410d05f398f2c6fbe1eff8795cd517bf5d2d96c1e752bf0f8372a4adf538fdcfce1da5d96ecb5aeb1475eead5207d12506f496c9f6c23b87d5dc8499a044a1c667123e1e041c0c821bbb851d049e5f30a37f9578576eb48a663566381793da0df295a127c6492b1eff9592274a38a24816cf6b15cc890465d11e8206dcf1d53bb467d788b277d855008699d8b49ea8019137838de83fed0cce28825e541a02020f426eeff1709370bcabbf99e341a038722dcfb8021914b20df5aae68c36137337ba2467ee5c8fb603c79eda88063215a59097a85eede7b8991aa7dde58c8046f62458cdfab00d648401c124ddd60ca160d8ba15ba9cc44d5077c73a73ace9ccf008e00a17b2d618ed4f935db3a452d37b6736900f017066f8797850c625ff6943ac5969030fe3c53d3126d8f0d8c43b2a229f077e57b4892378bb2aae0501d3c48be841d8daa6ac55a86e901ddc56fcb33c8dc06593719e60f3a54467ad502df248ebed9992bf85e6c61f50cbdd0a9100a9a09e11bf3f422318574dc655ba404650163e9a01f5b70fa789167656211b3a2c844d7ae13580f610b723e99f3efbd6c1c3646468101772d8f9a46e845bde3c4220c53051cdb7181459ced3e0f17091a4d6c3f9adef6f0d4d4bb219d37c6ef8821d1f5bceadffadb2f75d5b0ac1f7bee9be5974c84d6796a5d3f6ea4907808f562e53e9c83565c557eb9556f13955c905c3893c893db2e6ad4cadb79aff4355fb0ae54c80ae4a04820dda1b34d7afd9e324d8a9796ecc581e396f98c6c98188a358e9266b1a3781f9a863771288860c29426d0f17bce970fd39fa43d845f93b574a8b0b267d3c1589b79227e9b95ee0e78705e95ac5758db2c712de4a7b96d60e53b158e378a0f475a3e1c22974c4a90ac222e0f0098cbffcf35777813936764ef9c2574f5edeeeba642163ea08b83a14a1c8dfaaad46b93547c2486addcd7e51178b987b4d6ff365f12785bc7465aacb16d749a823ef81b259c2624addc7d863e42091fb76f5642167e27 + 10000 100000 1beef5f2e126cc3c549276415c17e51b; + 8404ffcc46b4d0898a8de08bf5eae9b6 + 7bd305dadd4f291f6700c7b48446988b + c18d7e08835beed76ae9e82396af49c6 + bacc8b39ade64cc59369ff39dfd2522c31a603edacdb057ead0d96e9d5ec1662e6d065bc2414852f9e8c67275ce0e82f12645fd458f4e5833461f69dfa92dc2fc77e488539617ec8857707e60fde8dd27364183373bb45b292495b89e74f730fc8bedd6b8e4ad23c464b069f4fbda078584ff1cd06acb21436dfbd3488cd7098981f1c7ef494da47f8367a4d0992a5f08a2d4193770d295cd5fce729c114ed16d7c68f5412b91189182ef8a43462637247e5c269cdf4e4e03334827157c72679617fbbf71bd5c59dc898c937f9a342c0c1a5fa69122b07f396b89ccbc51e17e7062278adb6c7f5b3a023cbfa5dbf076b6bd35cfd65cf9c2e6fa905a597e56fe81639966a0848e7cf1f9690342d5bd25e1cff78d510e407f49d42f9031921fa7757632a2ca344912b646376b67ddba85e47939fbc9f99cf148c57df560a4e84cdd86d7adef2a9d7c9f7862084ac393d92cae3ed1812b1596d4b594be1c7cad3fe7715484ff838285f9c78dfd08e41aac6820ddf4a443202a4ceb0c10225766dc4694ae3c97f9c37d96971c9fabde40d453a50f030ccd633596fea1326a6944952b5d1a7be5afe6c1b79be0a4737abb7771c1b5951fa8a4f2030560ad4756171d6dc91b0d052f39ffc58edb14e89bee46442ad6b0bae8b9a3e9be2af7a10b8136cf832d1335921ed325dca3fe10164888d0969a941206f3a2671186b3aa08ad120a040267548e2975bbd7aee77f9ea0eaed42f3e5f8b6051822fef1461db72a5bb3929719a2b1ca72d350c9986bd3aaef8219c260803258ad97f4cf69b435bf229ecfdda161ddf6efe1f37818301c1e06296555a87f51c340fe02b4eb24ff03f8d0a5d7699f99be34eeaa29d4b0a97971a0f8f5a3834b00031a58d94192b944d9ee460961086eb35d0de5a9dada27022e1c7dfcc5ed6c615c72bc5cdf02f9a98e6f49ed8558ca8cdaa9f610ce93f62568596c913c0f96d55ea593571a9b41e2bd4a48d0be558feaa1c8e30f49fb57be63fbe55c81723e1c4402c2ff085959318079d3c531cd69d2599d2cb321e02a0a850419932a3b522c33b8343653b30ea1a53909fe5384ed977ab3c2c20b60055d1eeb3bc47e7a79489ee69a5d42984f73864f048309c4f7ab0e02d5bba5e7c5fa6ead2f92f456e67878269bc8efd4c398791ffd2e005f9563e17a0872478326ea0a8c0f1277c345e19bee04d23fb1bc5af0f39d3a1b8c2ecc92324ae85e15cdc2315600c456efd341a4ceb1731b3963e3b8dc0d67cc486651915f40668c62b46b93d359d869a0daac341f1ce370372194d4c7636f1b370d3890f69a684bd43641e0541b451e89f780d442ec940c1b573211afaca11dd4294e310e341cd396e711afb00676b6afbebe67a3b5f4eac5b41afef4aed2c370e1ca0bd + 100000 1000000 df62013a9d388ea6e82cb7295fa706ec; +} diff --git a/symm/t/square b/symm/t/square index 9d0cbf9a..4adf5625 100644 --- a/symm/t/square +++ b/symm/t/square @@ -525,16 +525,16 @@ square { square-cmac { f260d7bc "" - 29984a33657f622352c1ee581eab804e; + a6c67fdb36ee9d8552f4112e08d64d76; da163547 d3 - cdc5e3485007695cdeb53ce1e8cec39e; + df74a9ab4bb73ba0904abebbc8aafd31; 48b75511 95e77022907dd1dff7dac5c9941d26d0c6eb14ad568f86edd1dc9268eeee533285a6ed810c9b689daaa9060d2d4b6003 - 7fdd1689e5a004d05b0bc92e3ecc8f16; + 93b8e5f20003a16bbc160e0010c09ad2; 062365b0 a54364c76c160f11896c4794846ecfa14a7130c9f137120634c9519848a877ff77bf79192a5b50ade5d9cd - 95f37b226b5f157fb68c9b235085799a; + fc6bc3e49f756681f782dabfaecabdab; 739a3d1f337f29549e6b0d27 "" c48de9e2768ea55c731b65c22052aebd; @@ -573,25 +573,25 @@ square-ccm { eb "" "" - bba9ea11; + 0b77d435; 14ad568f 86edd1dc9268eeee533285 "" a6 0a - 507bd8ea; + a1d8e1c8; ed810c9b 689daaa9060d2d4b60 03062365b0a54364c76c160f11896c4794846ecfa14a7130c9f137120634c9519848a877ff77bf79192a5b50ade5d9cd 739a3d1f337f29549e6b0d27a4ba234085406a6136512061f7080cc07df0591d8fa21f2dd88374d8cde8e160ad10997a - 2de5299d997f5a95d32115f84b8cbc072043580e27bb6ace6654173512e0d464ce4358899c43ac2befe5cbe68155797b - 217a8c66edf6aad2f17f555c8936028b; + 2de5299de77f5a9551211586c48cc2fb2043580e59bb6acee454174b9de0aa98ce435889e243ac2b6de5cb980e550787 + 890c514f7759ae037140eef723181c48; 21635c6d 62c9269029df3e6057 acc87638f508046733d9ff61cdbda3b3e9878731ebfedd4705e505da1435dceaa7b1cc49ae1d50c38201a8 94476b3f102b752eb9529533966f27043eb621b7f65b000961040ef2f9b2fc5fa450727a9b542cde52ebfda19d0ccc520f215eb57b - f88c7d34053cc7278c3e3b09715ab3dec3aa050aa70aa6dcd560a9ea58ddd10857b9bcffb98e2af8a3c851f60f540ab8872af0df1d - a65b843ead57238ecd6bf0eea46d6fbe; + f88c7d347b3cc7270e3e3b77fe5acd22c3aa050ad90aa6dc5760a994d7ddaff457b9bcffc78e2af821c8518880547444872af0df63 + 06f53d976392e9e86ac221394b5fb07c; b3a4f3ebbbb18ac6c95a97a4 8030370c33d090c54215ab "" @@ -660,37 +660,37 @@ square-eax { "" "" "" - 66848361a1d85500e3efc4889f3c2bd1; + 68950a6ce18037350d23d4b89f253b27; da163547 d3 "" "" "" - 3e14f9c0f9b208db9a52730d00525dc6; + aff75e00b2b5ec870ee51a74b87f658b; 48b75511 "" 95 "" "" - 1b81e7698ff5e258ea2e8f75b7bcfd88; + d62f0eac68b965fbb852442a082b390e; e7702290 "" "" 7d d5 - c256dd01d1ee4db1387ef0f0b3d43b57; + 3b4a842254e17ddd4906899551af908c; d1dff7da c5c9941d26d0c6eb14ad568f86edd1dc 9268eeee533285a6ed810c9b689daaa9060d2d4b6003062365b0a54364c76c160f11896c4794846ecfa14a7130c9f137 120634c9519848a877ff77bf79192a5b50ade5d9cd739a3d1f337f29549e6b0d27a4ba234085406a6136512061f7080c - cd9fbedf0befc513ec208493902b99f4e304654e0460d29920804d69dc150fa3e31d86a755ba930d0211fee9b7ca5f9f - f01b0b0089bf48fedc2c21556a1b644a; + 792f3471e561f7f3c2da13d0ea08d8b4299c8514c61fc51896804fe26089afebe792f88719672f75b3656f491e894635 + 0515fe199fc47f8f105e5ce90fa181ea; c07df059 1d8fa21f2dd88374d8cde8e160ad10 997a21635c6d62c9269029df3e6057acc87638f508046733d9ff61cdbda3b3e9878731ebfedd4705e505da 1435dceaa7b1cc49ae1d50c38201a894476b3f102b752eb9529533966f27043eb621b7f65b000961040ef2f9b2fc5fa450727a9b54 - b88f48021b69e18d9457120fc11ebdd467c0b1119b5ab0bd16a5c3aa70e263d3ab841cbe6bba4fd02c94690dd143746294f825c26f - e0efd3bd220277de49de30272939c21c; + 19a55efbd17a2244fe3c555e336fc60c40e085e44dde6f28ea6e3194127b46e45cb6245c82a396f1f29f784edcb0055d3f04e30eb0 + 074a5996cc3961dfceed8fe3d2863452; 2cde52ebfda19d0ccc520f21 "" "" @@ -771,43 +771,43 @@ square-gcm { "" "" "" - 66ac17c939ebd05ca617e77ba83201e9; + 66ac17c947ebd05c2417e70527327f15; da163547 d3 "" "" "" - 8361e8e723233ce477ef9132624bfb9e; + a793a26107dd3c18ba0431c52f2708af; 48b75511 "" 95 "" "" - 29a7e874d6054c42dcc1ada2e6f38cf8; + 19d3533dcde0922c2d3ffc53bb921da8; e7702290 "" "" 7d 7d - d439d2be5e01a0d767739abd80dbc293; + d51f111bc7c1b32ab76788e279cd208c; d1dff7da c5c9941d26d0c6eb14ad568f86edd1dc 9268eeee533285a6ed810c9b689daaa9060d2d4b6003062365b0a54364c76c160f11896c4794846ecfa14a7130c9f137 120634c9519848a877ff77bf79192a5b50ade5d9cd739a3d1f337f29549e6b0d27a4ba234085406a6136512061f7080c - f36d7d44b8637a2a560a8e89a7a5a5c754de68194a47bb2befdf2384e91eb38cc70807a42a687eb3113c9c223e1c8763 - e13b8f67d62247d887180b4cfb90eb6b; + 3c011823aab23337e5a5c1d3bc7422b6216ffbea8135a4e2ffb11dfb68c2a14e746ffc5b48f0551c9f22642faf9835d8 + 35aaf9095f76e809798699ffbe1c0b0a; c07df059 1d8fa21f2dd88374d8cde8e1 60ad10997a21635c6d62c9269029df3e6057ac c87638f508046733d9ff61cdbda3b3e9878731ebfedd4705e505da1435dceaa7b1cc49ae1d50c38201a894476b3f102b752eb9529533966f27 - 863f1602c0fb45b1a3264e8140b5addd0a82feb9b5e632fc539b3ed83667eecbf32c65dcacec865ea475c3506d61d00db1ba24288ba1a13ef1 - 58de876618ed18d0aab09ef9acd06bae; + 863f1602befb45b121264effcfb5d3210a82feb9cbe632fcd19b3ea6b9679037f32c65dcd2ec865e2675c32ee261aef1b1ba2428f5a1a13e73 + 80d044d5151c9bd4360850268cf9b6f6; 043eb621 b7f65b000961040ef2f9b2fc5fa450 727a9b542cde52ebfda19d0ccc520f215eb57bb3a4f3ebbbb18ac6c95a97a48030370c33d090c54215abd6 b3ad54efc9a38378c5b93bf4f2aad2605faee2b03fb648e27fff63102758fe2b69ac26afa3349829b94586306fed54154f8f28523c - 1e559d1bc26402ae885ebcb0946a573d44df5d59476510247197bc816467f5386887c5543f3a2256e26f44a91b31afcb7bcc85b2dc - 83783d53918eff6b310540ac8c1c5d85; + 8eade4d2955ca654b8aab9e25092f2ec8885f08960d1692a8c09bec546d8fd396dc75e52f6331a96391fa8e4a7d18b25bc86ae6aab + cca26d676181ec28242129be174ef96c; 03d4de1600157846b710ee72 "" "" @@ -900,43 +900,43 @@ square-ocb1 { "" "" "" - 4ec5c312526c730e41458aca324ae0a0; + 9517fb39f874f5eb9492ab804cac53a0; dff7dac5 c9941d26d0c6eb14ad568f86edd1dc92 68 "" "" - d0cc24f9084261cdaf4393e7b250701b; + f250f3dd82723801fae0678035528efe; eeee5332 85a6ed810c9b689daaa9060d2d4b6003 "" 06 - 72 - 1e1671f47f5bd8b32ebb09c019105281; + 7e + 9d05909f6a5484b677be057af1ea2a8a; 2365b0a5 4364c76c160f11896c4794846ecfa14a "" 7130c9f137120634c9519848a877ff77bf79192a5b50ade5d9cd739a3d1f337f29549e6b0d27a4ba234085406a613651 - 11e134a63441fc0951b37098764822d6eaa47187fe96a2f3658563cc612c857f0dd5d4e056c820a3670828dd7f5a9d1c - 86893621402b0a64fded1b34d47504fb; + 90236cc04b3ce7ab2c782d22fb93b02ae0583bb026f837c28a6e4ae22b533878255b39b158962faf8814c99423e5fa64 + 29a472eb0282c912127b000174003c50; 2061f708 0cc07df0591d8fa21f2dd88374d8cde8 e160ad10997a21635c6d62c9269029df3e6057acc87638f508046733d9ff61cdbda3b3e9878731ebfedd4705e505da14 35dceaa7b1cc49ae1d50c38201a894476b3f102b752eb9529533966f27043eb621b7f65b000961040ef2f9b2fc5fa450 - 1599b31480519c999bbc9fd4fb1a24676071040c16ff4a6834cdaf46c51ccbb9522cfde0e2df2867871cabd4aa73c33a - e8e411a4ac803610a4f2a8e1b6aabba9; + f16cf340bff7a524dd6b369039ef6eb7f1e0bae5814c79864964a2cb624b56041fe2c7f997b5d3760324b40197e72a0d + fab36fdf2f2c4ded8846e9d22e6ac9a8; 727a9b54 2cde52ebfda19d0ccc520f215eb57bb3 "" a4f3ebbbb18ac6c95a97a48030370c33d090c54215abd6b3ad54efc9a38378c5b93bf4f2aad2605faee2b03fb648e27fff63102758 - 384781d5a43f9e0d692787efd9cbbff3f8cc6e154544695653e495e63b3da9730b781b214f5c8f6d940542c76c24f0acacfcf099c0 - 5fa2cade7fa22e8df91b9f91593f9d3a; + 2469ec52037cd3e95b25de4c6ef7be38887e49d789f649a91cb0301fc2a94d80377a623f24afb58e9428b5c089cb6eecd6a7408678 + 357e06fe7770804cccb09d3f80cad777; fe2b69ac 26afa3349829b94586306fed54154f8f 28523c03d4de1600157846b710ee72807a2219bfb474fd71d891f24bb65d1563259f9eb53b571ea629c54d 57dd2d42f70800df9fcbaca48b77dba189196d1ebba10b0467cb9fc2712a199e533fa9156308cdec3f768281e040a9b9a222bd689a - 4c8c3874f73c8504b64bbe612589c103a462720d244c3092221b6c7b31a162aebefaea934d2ac8eb9e03f8f480aa1407d58a8a8e0f - 03fa1ede942984e0ae4f30946f7e0201; + d0a3845096db507a5f0fd48124160df0b71c43dcbcdb4383629901895b30f400b59087532c0357816645f6d972c8ec143524b1b628 + 6ef130cca6a884d3a8a04d06ae1ad5fc; ef66f5306ceb0c6b08ac8b0a 22260c571b4a42bb8fdb233bfa6a5cfb "" @@ -1026,16 +1026,16 @@ square-ocb1 { square-pmac1 { f260d7bc "" - aec43cacfa9e76ed9f6c0176974d1111; + aec43cac849e76ed1d6c0108184d6fed; da163547 d3 - 38e2ce3d69f8fb21ed28599d77607fe8; + 38e2ce3d17f8fb216f2859e3f8600114; 48b75511 95e77022907dd1dff7dac5c9941d26d0c6eb14ad568f86edd1dc9268eeee533285a6ed810c9b689daaa9060d2d4b6003 - 7c8785ab3dcb7d71ba3d6fcf7ba32376; + 65c61b1f4a19d8c1a15e7f7f77fed761; 062365b0 a54364c76c160f11896c4794846ecfa14a7130c9f137120634c9519848a877ff77bf79192a5b50ade5d9cd - b21eb39143554c29a104eb7ca8d10ca8; + 0df810432393db82d08f39204917e303; 739a3d1f337f29549e6b0d27 "" 8a5016e4834fa9fe8ef64746a89189ce; @@ -1068,43 +1068,43 @@ square-ocb3 { "" "" "" - b1669cbf9b59f73b4288eb0761332190; + 43ecf932f2a50f9a85f7dedbee4a09f2; 7dd1dff7 dac5c9941d26d0c6eb14ad568f86 ed "" "" - 780716f09513ecb8dd6f322f3901fd27; + 4e23472f7776db3c194a95e9523bfb69; d1dc9268 eeee533285a6ed810c9b689daaa9 "" 06 - b8 - 1fcf843450fa4d3da50d8de6525119eb; + 06 + 80f022fdba9a783af386696a7468a7d0; 0d2d4b60 03062365b0a54364c76c16 "" 0f11896c4794846ecfa14a7130c9f137120634c9519848a877ff77bf79192a5b50ade5d9cd739a3d1f337f29549e6b0d - 33f91dd536f3667b220ac4fa9ef6bec830fc052968f5c3c78141ad56254dbe7fff02e85ea5a278bce0c9a5037caa31f0 - 11621f557ad6c52a82e8d9495bb284e2; + ec00fcf09bc64fb96c5630b05426c0f3e11c4e5e278f64dd5391ee47cb3765af3407824e449c2f3ad2e53b1d7a754391 + 838c24cc6117a97999b82d8a6f49927f; 27a4ba23 4085406a6136512061f7080cc0 7df0591d8fa21f2dd88374d8cde8e160ad10997a21635c6d62c9269029df3e6057acc87638f508046733d9ff61cdbda3 b3e9878731ebfedd4705e505da1435dceaa7b1cc49ae1d50c38201a894476b3f102b752eb9529533966f27043eb621b7 - 1859d78e9ad0fbc8bf76a97f08e6de088994238974dce458f7dc145948b8ba9f8b8f52c82a1ff60046350eeec8936379 - 350f84d218392b1282daa7db5ebc4e5e; + 688100f96e36393c78db3753d70228a939eb45ddb3692356fad4b74ade71fea04546dccc6896115975a92e7d715e329b + a00f680ac9305870d77686138a56be0d; f65b0009 61040ef2f9b2fc5fa450727a9b54 "" 2cde52ebfda19d0ccc520f215eb57bb3a4f3ebbbb18ac6c95a97a48030370c33d090c54215abd6b3ad54efc9a38378c5b93bf4f2aa - 664cabfb69a25342a0bcc17049c316c78c412df3efbf75cc192b936dccf87b3c094d51468c20911a477cd6a214c403ff99abec313a - 09b989eae5e6973b675f06ab4b9ad1e7; + 47cba1883ecfcb1c960a4af93ca6a30d74cd55635368c2523b675910d02e63ec2f6255396d7a9325d8bce6913293860c952f3755c3 + 1c31d84f0f6360eb2dd47910f1375c61; d2605fae e2b03fb648e27fff63102758fe2b 69ac26afa3349829b94586306fed54154f8f28523c03d4de1600157846b710ee72807a2219bfb474fd71d8 91f24bb65d1563259f9eb53b571ea629c54d57dd2d42f70800df9fcbaca48b77dba189196d1ebba10b0467cb9fc2712a199e533fa9 - 9f53984003391daa1fe81c1e9385d1d902f330b930b225a8164f06adeaff820d08dfceeba9ae1a9639d556bba8ad6563186d022240 - 64d960c38db07c9ede67bd56b122544e; + d8a4afa64f98f4dc2a74ac259523c9201f96b07b7521a3c6a25b40b62c52fb1e65a993715cc737afc6e84b8ad7eb31386ee8bc8941 + 05c0bdead7d29c226d28e8d257555b3e; 156308cdec3f768281e040a9 b9a222bd689aef66f5306ceb0c6b "" @@ -1195,13 +1195,13 @@ square-ocb3-mct { 16 47b0a29f45c1b000f1a0c27eee7f42e7; 12 7a80e7d6e556778f77a327928ff4dc35; 8 886bfcb71cd24995911ed011b231d99e; - 4 d0b2597b73f284932af5a5db0fd1fb3d; + 4 6554bcd7716933559def79025d3c06ba; 16 5c3d0dfbee0d8977a7cee660; 12 6057212ee2684bb36520d44b; 8 5f0f721b8171461d242bb883; - 4 127b569653ace9af8aac7f47; + 4 bd7b3db643c64178da4896a5; 16 4184125b37fbd3fc; 12 258e75d36b5359b3; 8 bd8c4a18f64be7cf; - 4 55a5c5e40c4de0e9; + 4 4f39f80256368cc0; } diff --git a/symm/t/strobe b/symm/t/strobe new file mode 100644 index 00000000..1d056755 --- /dev/null +++ b/symm/t/strobe @@ -0,0 +1,1288 @@ +### Test vectors for STROBE. + +strobe { + ## This is the `Concurrence' test from Hamburg's reference implementation. + + ## Initial conversation. + 0 init *128 +; 1 init *128 +; + 0 ad/M =testing +; 1 ad/M =testing +; + 0 prf *10 !8a13a189683bf5678170; 1 prf *10 !8a13a189683bf5678170; + 0 ad =Hello +; 1 ad =Hello +; + 0 encout =World !123bfbee34; 1 encin !123bfbee34 =World; + 0 clrout =foo +; 1 clrin =foo +; + 0 clrin =bar +; 1 clrout =bar +; + 0 encin =baz !15e518; 1 encout !15e518 =baz; + + ## Lots of `X's. + 0 encout + = + !; + 1 encin + ! + =; + 0 encout + =X + !c6; + 1 encin + !c6 + =X; + 0 encout + =XX + !1016; + 1 encin + !1016 + =XX; + 0 encout + =XXX + !8ba0e8; + 1 encin + !8ba0e8 + =XXX; + 0 encout + =XXXX + !4990e91f; + 1 encin + !4990e91f + =XXXX; + 0 encout + =XXXXX + !5eec19edd5; + 1 encin + !5eec19edd5 + =XXXXX; + 0 encout + =XXXXXX + !dc06c47954a4; + 1 encin + !dc06c47954a4 + =XXXXXX; + 0 encout + =XXXXXXX + !9a4b7ade0aa89e; + 1 encin + !9a4b7ade0aa89e + =XXXXXXX; + 0 encout + =XXXXXXXX + !0d1b71cf776af3a9; + 1 encin + !0d1b71cf776af3a9 + =XXXXXXXX; + 0 encout + =XXXXXXXXX + !fa037bf1c020190bbc; + 1 encin + !fa037bf1c020190bbc + =XXXXXXXXX; + 0 encout + =XXXXXXXXXX + !a66e9933c1cdcf5fc084; + 1 encin + !a66e9933c1cdcf5fc084 + =XXXXXXXXXX; + 0 encout + =XXXXXXXXXXX + !561f708ef060b436728811; + 1 encin + !561f708ef060b436728811 + =XXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXX + !12fe9d8b24b91b434ec114ab; + 1 encin + !12fe9d8b24b91b434ec114ab + =XXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXX + !9a516cf12e38944e370864e0f8; + 1 encin + !9a516cf12e38944e370864e0f8 + =XXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXX + !9155393fc1155502250c839e3758; + 1 encin + !9155393fc1155502250c839e3758 + =XXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXX + !9731d438dd00d5de2a3bb21a6a14e3; + 1 encin + !9731d438dd00d5de2a3bb21a6a14e3 + =XXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXX + !41c74574fe9efce9225aa766a83d4f42; + 1 encin + !41c74574fe9efce9225aa766a83d4f42 + =XXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXX + !65d720eafbb3261f0d960d70c35dcaea20; + 1 encin + !65d720eafbb3261f0d960d70c35dcaea20 + =XXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXX + !703a1db79e5b68de3760c58f90fa6775f455; + 1 encin + !703a1db79e5b68de3760c58f90fa6775f455 + =XXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXX + !b47759d2263e7d97760a4461a0d04e7fd56f6e; + 1 encin + !b47759d2263e7d97760a4461a0d04e7fd56f6e + =XXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXX + !3869204c64230931d7c2e5647dd67e2800db2039; + 1 encin + !3869204c64230931d7c2e5647dd67e2800db2039 + =XXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXX + !4aa5d694e8006d83f87cfc62408b6127b77e47bdd7; + 1 encin + !4aa5d694e8006d83f87cfc62408b6127b77e47bdd7 + =XXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXX + !1f1e7cee02468524a853c1c1b53d561443d89b19d536; + 1 encin + !1f1e7cee02468524a853c1c1b53d561443d89b19d536 + =XXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXX + !7c3fc5d277be9e4525d80442d49d21536fc460a7317f0e; + 1 encin + !7c3fc5d277be9e4525d80442d49d21536fc460a7317f0e + =XXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXX + !fd2f4cf48128e3a4d20d1ffedb7e1629ba80e89e55b1d343; + 1 encin + !fd2f4cf48128e3a4d20d1ffedb7e1629ba80e89e55b1d343 + =XXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXX + !c85c1eb7626b48b96ebb245822a54dc95a6d96cb6a2af76c3c; + 1 encin + !c85c1eb7626b48b96ebb245822a54dc95a6d96cb6a2af76c3c + =XXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXX + !1b3a6f13c1741e91fad9a060ba3f144e356a7c8e5bf6ea982d84; + 1 encin + !1b3a6f13c1741e91fad9a060ba3f144e356a7c8e5bf6ea982d84 + =XXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXX + !a979bb3b94687d0d60c55dbc071668da261bfa6f37f91a2d194115; + 1 encin + !a979bb3b94687d0d60c55dbc071668da261bfa6f37f91a2d194115 + =XXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXX + !1e2a52e3220403c77bbb0e6e2b9456fcb918d5a0dfc04eef76fc9278; + 1 encin + !1e2a52e3220403c77bbb0e6e2b9456fcb918d5a0dfc04eef76fc9278 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !1f34cc349d503817d28fc1a67b8dd54a4082ae73d8d4f004279bd7ce63; + 1 encin + !1f34cc349d503817d28fc1a67b8dd54a4082ae73d8d4f004279bd7ce63 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !f7c6bdf8689f0cd920c8ff6745b3fc285decc5a73180841baec2008b72ae; + 1 encin + !f7c6bdf8689f0cd920c8ff6745b3fc285decc5a73180841baec2008b72ae + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !89bca3b3fe374d7776fca1568c67972791638e6004e37fc3b792388fbf4d94; + 1 encin + !89bca3b3fe374d7776fca1568c67972791638e6004e37fc3b792388fbf4d94 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !d752309a5e5ebe2c591c55b9b42c1aebc3a5d17bb696bd5e5fece6d70cd4a076; + 1 encin + !d752309a5e5ebe2c591c55b9b42c1aebc3a5d17bb696bd5e5fece6d70cd4a076 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !e6f0b8800dbee31e5a9b1487e186ae7e5839becfbe8572afe8a907803b6298ce47; + 1 encin + !e6f0b8800dbee31e5a9b1487e186ae7e5839becfbe8572afe8a907803b6298ce47 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !ea5e143b8a616211700347b46bda10ebd200ebad8cb747bee6ebbd2044bcbbe88767; + 1 encin + !ea5e143b8a616211700347b46bda10ebd200ebad8cb747bee6ebbd2044bcbbe88767 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !c88d7a83b618b4a3bbaf5cdcd9bfb77b8593163e29b656de06aad9dcba67f1090894a8; + 1 encin + !c88d7a83b618b4a3bbaf5cdcd9bfb77b8593163e29b656de06aad9dcba67f1090894a8 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !13343f85d5c33de49ebab48d9ea012ec6bee78fe997b9d465220b267ce527df63963920c; + 1 encin + !13343f85d5c33de49ebab48d9ea012ec6bee78fe997b9d465220b267ce527df63963920c + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !0772dfb3e97ffe9897e254e183ed6a339e14b6f6ce98579cbd74b0dd12705f0e8fc83a22f1; + 1 encin + !0772dfb3e97ffe9897e254e183ed6a339e14b6f6ce98579cbd74b0dd12705f0e8fc83a22f1 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !e666998f161d9d5cd8a4b84b8f69c7f242843a7d70a368e0012aebb31f3455c8db0a2762d56e; + 1 encin + !e666998f161d9d5cd8a4b84b8f69c7f242843a7d70a368e0012aebb31f3455c8db0a2762d56e + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !a617e710a24d8ec6e8d0643913558d03c979dff5adb4807a1bb676c54abc271e478e3cd26441f5; + 1 encin + !a617e710a24d8ec6e8d0643913558d03c979dff5adb4807a1bb676c54abc271e478e3cd26441f5 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !4ac10a6382eb48c30b8f3532c5d2bd6aa01258713c8048843f2f01aeb07ec9673ee23c12ebbc496c; + 1 encin + !4ac10a6382eb48c30b8f3532c5d2bd6aa01258713c8048843f2f01aeb07ec9673ee23c12ebbc496c + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !3a14815a68d3156dbb049088b1c3cac707e1fe98d6f6c2ef702507b73d36341539f5bf3ec179c459fd; + 1 encin + !3a14815a68d3156dbb049088b1c3cac707e1fe98d6f6c2ef702507b73d36341539f5bf3ec179c459fd + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !c5e4de01206e6d22e1b688ee16cf70722800c67acdf9b33ea0e4c4bb9abf57c29212a4b5037cead2d56b; + 1 encin + !c5e4de01206e6d22e1b688ee16cf70722800c67acdf9b33ea0e4c4bb9abf57c29212a4b5037cead2d56b + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !ef4c23d94151559c878ed774c21e343cb401494331bb90471146f6b9dfa754f29220fe979712c045f279cf; + 1 encin + !ef4c23d94151559c878ed774c21e343cb401494331bb90471146f6b9dfa754f29220fe979712c045f279cf + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !1d5e809ca195882a69bf0a3ceb7cab559cdaa31f10bbfb4c52350d921a6d00cc6432730fe329ba59fb40ee9b; + 1 encin + !1d5e809ca195882a69bf0a3ceb7cab559cdaa31f10bbfb4c52350d921a6d00cc6432730fe329ba59fb40ee9b + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !0ce3268ba95b6a6ff86e8c9f2258ac6464b6c1dd6ada24a3003cfd91f54c930c08a440fae376197d5b021cbe2e; + 1 encin + !0ce3268ba95b6a6ff86e8c9f2258ac6464b6c1dd6ada24a3003cfd91f54c930c08a440fae376197d5b021cbe2e + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !522fb018a04f385658fd390f4c2543454c872ce4ba8d6fed4aee361e896875ca34f393a9a5472464a3bcf01e9853; + 1 encin + !522fb018a04f385658fd390f4c2543454c872ce4ba8d6fed4aee361e896875ca34f393a9a5472464a3bcf01e9853 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !7f25149deb31470c801e70671e08ed3a370342fb15bb99d17b58943c5bf9c1e4eeebf0b8aaa4f30679be9a06bb8923; + 1 encin + !7f25149deb31470c801e70671e08ed3a370342fb15bb99d17b58943c5bf9c1e4eeebf0b8aaa4f30679be9a06bb8923 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !3f4a8395e2d690bb34b1bdcfb36c2c52079d5d66b4fcb6548da98d492da04db0d90500a4ef5c4f9e9feb55ee42154ad9; + 1 encin + !3f4a8395e2d690bb34b1bdcfb36c2c52079d5d66b4fcb6548da98d492da04db0d90500a4ef5c4f9e9feb55ee42154ad9 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !b0f391bda49d977c52f80f8675e84e9b014f7503bb3825c25da1b5169af498e96dc9440cae6b516325efdee35689abbad9; + 1 encin + !b0f391bda49d977c52f80f8675e84e9b014f7503bb3825c25da1b5169af498e96dc9440cae6b516325efdee35689abbad9 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !2812c84a8d5c2fb938292308e711d64bff30ea0c6419e3433eb43be48646fe8b29f3864cb06cd0a3cc70b38cdea4fa6d099d; + 1 encin + !2812c84a8d5c2fb938292308e711d64bff30ea0c6419e3433eb43be48646fe8b29f3864cb06cd0a3cc70b38cdea4fa6d099d + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !9f2d627361db4aab97458fda8cdc703ed90dad1f3d6d524c6dae99729194effd0a1f394c30271d9ddf28c04cca1b391b0bb660; + 1 encin + !9f2d627361db4aab97458fda8cdc703ed90dad1f3d6d524c6dae99729194effd0a1f394c30271d9ddf28c04cca1b391b0bb660 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !d90fd5180555d5694fb8d37ae3b14a16522f0975dfedf194d7fb982f78739e5c18ec96683d36e7ea87e95f9b6032af286cfc87b9; + 1 encin + !d90fd5180555d5694fb8d37ae3b14a16522f0975dfedf194d7fb982f78739e5c18ec96683d36e7ea87e95f9b6032af286cfc87b9 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !9f68b8e0fbabaa3ba7e89f01ce4ee4a303a0d5b70a4cf86e248fd0818b197e2a11fa48eacbac441e555f9c334ad08d01826e832362; + 1 encin + !9f68b8e0fbabaa3ba7e89f01ce4ee4a303a0d5b70a4cf86e248fd0818b197e2a11fa48eacbac441e555f9c334ad08d01826e832362 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !5eda3e402fb41bcd18f967777257f3bc57df8a46899110accff5cd918faaa0e6d50879e32dd185978ff379120fccfda4d4a8fdd4af64; + 1 encin + !5eda3e402fb41bcd18f967777257f3bc57df8a46899110accff5cd918faaa0e6d50879e32dd185978ff379120fccfda4d4a8fdd4af64 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !217f1002e3606276bc24ef3441adba9b103aa61c009157ff65c88925d7bc6ffa76860af8ca8e809de0650f560f5fef195697086774a509; + 1 encin + !217f1002e3606276bc24ef3441adba9b103aa61c009157ff65c88925d7bc6ffa76860af8ca8e809de0650f560f5fef195697086774a509 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !1a27632ac0e6dc87d3d9b752e104fae2e97962129829c68d921a01880ddae68eaf1c4cf514cfc1be573acb9704483c8052f263daba49c5c1; + 1 encin + !1a27632ac0e6dc87d3d9b752e104fae2e97962129829c68d921a01880ddae68eaf1c4cf514cfc1be573acb9704483c8052f263daba49c5c1 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !63ee36be37b62458e10f5a1014bec7168ee9925e290f232484aedee47e99c55168eb25fd677e83b2dfd8b74fbea276c17efe71cef67932e7fa; + 1 encin + !63ee36be37b62458e10f5a1014bec7168ee9925e290f232484aedee47e99c55168eb25fd677e83b2dfd8b74fbea276c17efe71cef67932e7fa + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !aa17985c6558bbb41d8bdcfbcdbfedb0c495c2f1795be87902defeb3eaf329cca4210047084f9664f7bba72dfafc3ac277d465ccad618af29090; + 1 encin + !aa17985c6558bbb41d8bdcfbcdbfedb0c495c2f1795be87902defeb3eaf329cca4210047084f9664f7bba72dfafc3ac277d465ccad618af29090 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !cb6c34e2718c39a14da69b7edcb2b9eb341036cf37b74109b920b02426d216bd137387cf79c904233da683b0e2d95497e62b0e5456095de8b7adb1; + 1 encin + !cb6c34e2718c39a14da69b7edcb2b9eb341036cf37b74109b920b02426d216bd137387cf79c904233da683b0e2d95497e62b0e5456095de8b7adb1 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !8920633abb5e90a51bd6da58a196e932647695b2ac3fa83d5a4a156f95c1cd506aab3c655f2b2b11c9434292529a0dfb6c5f1b5db33de9e3019f98aa; + 1 encin + !8920633abb5e90a51bd6da58a196e932647695b2ac3fa83d5a4a156f95c1cd506aab3c655f2b2b11c9434292529a0dfb6c5f1b5db33de9e3019f98aa + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !87df0d097ff3bcb1c875d39bb45138c7bec2b033209dadbb205655b28bcce6353f802b15b81642919f40f7601c36703266fbd07163e7da933d4f2bb9d4; + 1 encin + !87df0d097ff3bcb1c875d39bb45138c7bec2b033209dadbb205655b28bcce6353f802b15b81642919f40f7601c36703266fbd07163e7da933d4f2bb9d4 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !dbbb5a6d715a0504eb85597f07a74c7df63a2f497a45f523ba37a01134993fcbd08b4c3ee88c540c640728864f79b3011fb8f07c20da16453d3d7bc0ad62; + 1 encin + !dbbb5a6d715a0504eb85597f07a74c7df63a2f497a45f523ba37a01134993fcbd08b4c3ee88c540c640728864f79b3011fb8f07c20da16453d3d7bc0ad62 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !bba81430187038b681b56b17148f96ac219a2b474c29ea1d362fb97fa3177c6b3aae2a8b35bdf16d4166f2ff35319e60b0770436ed42fd4c72caf90b2e4047; + 1 encin + !bba81430187038b681b56b17148f96ac219a2b474c29ea1d362fb97fa3177c6b3aae2a8b35bdf16d4166f2ff35319e60b0770436ed42fd4c72caf90b2e4047 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !3b341925aa4a4b5930a4280f5c7ac0d8e7aa047e1a55f5ba7803cc57d3685df6d460fc0ab526c5f776a0ebe100907080f9448cf473668c06a4d8724f5bf34aa5; + 1 encin + !3b341925aa4a4b5930a4280f5c7ac0d8e7aa047e1a55f5ba7803cc57d3685df6d460fc0ab526c5f776a0ebe100907080f9448cf473668c06a4d8724f5bf34aa5 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !572c5a300ac0105071ed9d80d268a7ff9e71df5e4b7bd4734089368500159222711532393589a776e48f3809e03fa0af9895909220d3c1e477605546bf82388742; + 1 encin + !572c5a300ac0105071ed9d80d268a7ff9e71df5e4b7bd4734089368500159222711532393589a776e48f3809e03fa0af9895909220d3c1e477605546bf82388742 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !ad6a3713688e3c8d05bb38ab2115819c2a7a6044e6b532bdb5f5afc199eeeec5530ed464014e82ebbc5ae8cb230438831dd90233cf679e3cd0aa621e82749e00bcbf; + 1 encin + !ad6a3713688e3c8d05bb38ab2115819c2a7a6044e6b532bdb5f5afc199eeeec5530ed464014e82ebbc5ae8cb230438831dd90233cf679e3cd0aa621e82749e00bcbf + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !9ad3396f7edfd8cb0c67701033b2941e32867e9a34288cfbf0b575f4981d96738d7d58f95131652da0947405e6210eca92d4787a9604cedb5113e7820bf2b1f60b338a; + 1 encin + !9ad3396f7edfd8cb0c67701033b2941e32867e9a34288cfbf0b575f4981d96738d7d58f95131652da0947405e6210eca92d4787a9604cedb5113e7820bf2b1f60b338a + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !fd7ab45351a569c2215e8645840b3f62cdd02d348d984f848a6aa1971854fe5feaf4253ace1f3a5cd8f9e5caf2ad65572a389f9e09fb09059fa66f37aad370ff6d068a88; + 1 encin + !fd7ab45351a569c2215e8645840b3f62cdd02d348d984f848a6aa1971854fe5feaf4253ace1f3a5cd8f9e5caf2ad65572a389f9e09fb09059fa66f37aad370ff6d068a88 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !38e597770fde2538e66147b517e204e827674c1fc47d51d835fc5f8e9c0de980f6995ab39272791d075ac26d3ef13a4e87ed8491e4610c64ae8102310623cde0ebcc78ba30; + 1 encin + !38e597770fde2538e66147b517e204e827674c1fc47d51d835fc5f8e9c0de980f6995ab39272791d075ac26d3ef13a4e87ed8491e4610c64ae8102310623cde0ebcc78ba30 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !86e78b7d6e0c059f0895d5cb7cfc130523a1738b2d0efb4dacac3a8a10ef768d39f46c18206496cbe0a07ae07bd9ce0e7439f2952ed2ff25ae7d7c9242b7de77515d3a6a0bb3; + 1 encin + !86e78b7d6e0c059f0895d5cb7cfc130523a1738b2d0efb4dacac3a8a10ef768d39f46c18206496cbe0a07ae07bd9ce0e7439f2952ed2ff25ae7d7c9242b7de77515d3a6a0bb3 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !49f36a3342f69941f0c2fbf927b19eb768e8fcbd4dc1050e70146db69e16155964b4da24ec9f5b677ced64cccdb2d50623a2b85bc7f9e2e40f340993ba4d238fe47d4401a345b6; + 1 encin + !49f36a3342f69941f0c2fbf927b19eb768e8fcbd4dc1050e70146db69e16155964b4da24ec9f5b677ced64cccdb2d50623a2b85bc7f9e2e40f340993ba4d238fe47d4401a345b6 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !37256a8d6113eba18fa7e67a2eb2a7d9007099c0f8dad252a7450b2199a9031ff2318140668f623b161877eedd5342e324edb960a9584a680d45ca633adf08b90430c531b132478b; + 1 encin + !37256a8d6113eba18fa7e67a2eb2a7d9007099c0f8dad252a7450b2199a9031ff2318140668f623b161877eedd5342e324edb960a9584a680d45ca633adf08b90430c531b132478b + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !32dca3f2af35c92213a9af62b8c9206742f6c66a3df5148d7b72b967e9ae90d1ba3dc5b5743fce415a57deb51c46f5a133ffe2f83b8acb0f1495f16a702bc644c15377255a75a44039; + 1 encin + !32dca3f2af35c92213a9af62b8c9206742f6c66a3df5148d7b72b967e9ae90d1ba3dc5b5743fce415a57deb51c46f5a133ffe2f83b8acb0f1495f16a702bc644c15377255a75a44039 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !5bc91312769a0b0f43b13813583b56041d376cc7c059f0ba9693287df303d2271f9a81f8467f1b9d2a929ad508b0c08568d2988aa330395660c5a3e10828cb6c06b0a300a3a4789ff756; + 1 encin + !5bc91312769a0b0f43b13813583b56041d376cc7c059f0ba9693287df303d2271f9a81f8467f1b9d2a929ad508b0c08568d2988aa330395660c5a3e10828cb6c06b0a300a3a4789ff756 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !cfcef59edf00d6721c558676ad694b2bc7cdda43564d29da7f58e399a47fe0ad544a2a5c85086a37239b5f2ccabd7472e8a7936d9de9f2252b67d5953b21144f329565b9a4770c8ff4a227; + 1 encin + !cfcef59edf00d6721c558676ad694b2bc7cdda43564d29da7f58e399a47fe0ad544a2a5c85086a37239b5f2ccabd7472e8a7936d9de9f2252b67d5953b21144f329565b9a4770c8ff4a227 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !ef849e65f395e85b4396f22602b1aa9fe3726161e6e1d582ebc585c2e6790fce1c0e5f0b616e35e875b9ed5eccef0c945cc8e68be2374d8859576405979f300a18931d9806e6a8a1e99eb66b; + 1 encin + !ef849e65f395e85b4396f22602b1aa9fe3726161e6e1d582ebc585c2e6790fce1c0e5f0b616e35e875b9ed5eccef0c945cc8e68be2374d8859576405979f300a18931d9806e6a8a1e99eb66b + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !bd964b885a27668afe2e0738502b2ba3f11a63181e5cc26fb55223b320bae68e2c04f069d076b0ff5209f6a5b453c059177af54d28ba29bb1dd62edfb8a77dbad75c5b19397062408dd42ea499; + 1 encin + !bd964b885a27668afe2e0738502b2ba3f11a63181e5cc26fb55223b320bae68e2c04f069d076b0ff5209f6a5b453c059177af54d28ba29bb1dd62edfb8a77dbad75c5b19397062408dd42ea499 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !463042317a2a27ba58aaea708b0bfe915a2a2433f89ef1193e04082f06ad3cd7a5bf8920adfc155fcbb8dc91d0473c28ff17b2202baccf8d7ba72c7d622a95a5eaf3a5de9e7b1a55e3d76c98a745; + 1 encin + !463042317a2a27ba58aaea708b0bfe915a2a2433f89ef1193e04082f06ad3cd7a5bf8920adfc155fcbb8dc91d0473c28ff17b2202baccf8d7ba72c7d622a95a5eaf3a5de9e7b1a55e3d76c98a745 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !d91972d9c34b73ca5adfdf28c276903d020d856a39569272483a1d333f7c6b72ae1fc94e00e42d62d0c6f93d5d2ded327e19ff8c07e586e5ed9eebd00c55239446500cea3a4ea22c38f4d0e32d8dec; + 1 encin + !d91972d9c34b73ca5adfdf28c276903d020d856a39569272483a1d333f7c6b72ae1fc94e00e42d62d0c6f93d5d2ded327e19ff8c07e586e5ed9eebd00c55239446500cea3a4ea22c38f4d0e32d8dec + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !4e9afeacf87b309b35d87b488162b56421f477ce928add35f0f31a6824e0a5f5c31f3bf762ac50e6aef8c7d0e98eb01d4950d0dd67261f8c06dee7e034788e686318884285b1ddce6f64dc9aae9c5479; + 1 encin + !4e9afeacf87b309b35d87b488162b56421f477ce928add35f0f31a6824e0a5f5c31f3bf762ac50e6aef8c7d0e98eb01d4950d0dd67261f8c06dee7e034788e686318884285b1ddce6f64dc9aae9c5479 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !e15bb7f491654b63e1ed476efda5b4ed6a86c7c5193cc79da0650b7848e325b392f8321ca8463539f1e1e60777192732c64507162ddcad9fb563b6a34a35af66f6bfad0f7c9d22828c70cb718c40ddfff6; + 1 encin + !e15bb7f491654b63e1ed476efda5b4ed6a86c7c5193cc79da0650b7848e325b392f8321ca8463539f1e1e60777192732c64507162ddcad9fb563b6a34a35af66f6bfad0f7c9d22828c70cb718c40ddfff6 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !12643264e4261ecb9bf62fb88fd3d23fb26e017c16455c888acc5ae6e3bde3689b15659b1ec5942408a90146fbecc56c33136c0e691784ecce9ed0b0181b654f08eea34646ff8db13dc89a2e653164b306be; + 1 encin + !12643264e4261ecb9bf62fb88fd3d23fb26e017c16455c888acc5ae6e3bde3689b15659b1ec5942408a90146fbecc56c33136c0e691784ecce9ed0b0181b654f08eea34646ff8db13dc89a2e653164b306be + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !a66947e171591261bc4b41aaf24dc588bcdcbbec645dc4ddfdf3857e799ca30e8e65ac1ff2081dae5f3bc859c3a2f89f644aec1c239f4d498a22be827ef23e48a4771da5f9972cca6378616a8960a5f7c72076; + 1 encin + !a66947e171591261bc4b41aaf24dc588bcdcbbec645dc4ddfdf3857e799ca30e8e65ac1ff2081dae5f3bc859c3a2f89f644aec1c239f4d498a22be827ef23e48a4771da5f9972cca6378616a8960a5f7c72076 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !6f4bacbcfacfd24c2e17bda9e259edb6c02f949f78029a6990b53a5c51e4c103b938556a0f0a67f372c459f04281e1390db713ba8434837de3631e290f0ae637f8423cacad173e7952a021dc0c380ace81f9be89; + 1 encin + !6f4bacbcfacfd24c2e17bda9e259edb6c02f949f78029a6990b53a5c51e4c103b938556a0f0a67f372c459f04281e1390db713ba8434837de3631e290f0ae637f8423cacad173e7952a021dc0c380ace81f9be89 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !a4b2340dec02cabb3531362a815759930e2f09b6a466d44afd33959bb753638ce63b8050b365c5e9e00290114c8e8847d909eaca13581366170e6adf31347a9946f7573538d8921239f16b7ea403313a1d0fa218a7; + 1 encin + !a4b2340dec02cabb3531362a815759930e2f09b6a466d44afd33959bb753638ce63b8050b365c5e9e00290114c8e8847d909eaca13581366170e6adf31347a9946f7573538d8921239f16b7ea403313a1d0fa218a7 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !9df08e36f23ceb212520f4461e8df919d09d50e6818fa7571c2139268129ce9310c79e671d4442c330fdc336e3c9ed30f7b0ce90127d2d9d288e4b10078f4f580c0b127791ee53a8a9876f05c64c884fd009dcf9d1ad; + 1 encin + !9df08e36f23ceb212520f4461e8df919d09d50e6818fa7571c2139268129ce9310c79e671d4442c330fdc336e3c9ed30f7b0ce90127d2d9d288e4b10078f4f580c0b127791ee53a8a9876f05c64c884fd009dcf9d1ad + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !dedfd0487fd61a2d9c54495f96a2b5f6c46682d7411c5d9a6e73cb1ab5296353d7c6f0c2fc7b759b233495fdaaf532c2b6c0a1a10252c1f34e1eec567650ccaca1e891c74744d60ca6e132240f56b775fa84618fb9a97e; + 1 encin + !dedfd0487fd61a2d9c54495f96a2b5f6c46682d7411c5d9a6e73cb1ab5296353d7c6f0c2fc7b759b233495fdaaf532c2b6c0a1a10252c1f34e1eec567650ccaca1e891c74744d60ca6e132240f56b775fa84618fb9a97e + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !c6e3226aa258e7c492e730f5c43e2e39f664a7c9a3a6a038a1ccc077d307c2d2a9e21091f222b9dbc66e9fd462e38e59546cf2706dc02301afa34beb1894c334ce44daddd002250eec388f2e4aa819ac4a943c297292626a; + 1 encin + !c6e3226aa258e7c492e730f5c43e2e39f664a7c9a3a6a038a1ccc077d307c2d2a9e21091f222b9dbc66e9fd462e38e59546cf2706dc02301afa34beb1894c334ce44daddd002250eec388f2e4aa819ac4a943c297292626a + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !95fa84145d9a295647c55aac1f55e67ad2b91f7048a066c8314cb1c1b1e5a090c8b4862da9e29e270009f73cf2d7d18206d6a732b46bf85a1a4e44e4188a02a7e26716453df7962c5604195c47d7cc2d5850e8387cc641781f; + 1 encin + !95fa84145d9a295647c55aac1f55e67ad2b91f7048a066c8314cb1c1b1e5a090c8b4862da9e29e270009f73cf2d7d18206d6a732b46bf85a1a4e44e4188a02a7e26716453df7962c5604195c47d7cc2d5850e8387cc641781f + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !960bc94f8c562c75ae7a863757295bda90586db18e675f5b8e93e5897a534a369cf60383f636a81af4a0d48bc9fa11a2d5ce6f56efe48d55fe0202d13a24b64b9b3c6ff8bb27e4eccc3d5260fe5f22bb173d57837d078f11a38d; + 1 encin + !960bc94f8c562c75ae7a863757295bda90586db18e675f5b8e93e5897a534a369cf60383f636a81af4a0d48bc9fa11a2d5ce6f56efe48d55fe0202d13a24b64b9b3c6ff8bb27e4eccc3d5260fe5f22bb173d57837d078f11a38d + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !7f437b3d6edcec8b85a6b5a755f0718a63a10d2dbbcf842362c5e32822de6da58deae222ada40efae07b9148b9db97680decfc9c134cacbefbb89a79d685cfd53a33f28a24012d9e6d277350bf5ab87d61d7ef9c322c0c292481e5; + 1 encin + !7f437b3d6edcec8b85a6b5a755f0718a63a10d2dbbcf842362c5e32822de6da58deae222ada40efae07b9148b9db97680decfc9c134cacbefbb89a79d685cfd53a33f28a24012d9e6d277350bf5ab87d61d7ef9c322c0c292481e5 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !d7078e462a43b0805ea87ad33842b2418bce472ec2a3e2391701e8bb637518aa71bb1dfe34d9f1cd7c5e17858b6614105460e27342493515b90a16a0beb7964fde79f3828f979bf58487cd505f9b56da1b35b2dbaf4a79fc6eea7104; + 1 encin + !d7078e462a43b0805ea87ad33842b2418bce472ec2a3e2391701e8bb637518aa71bb1dfe34d9f1cd7c5e17858b6614105460e27342493515b90a16a0beb7964fde79f3828f979bf58487cd505f9b56da1b35b2dbaf4a79fc6eea7104 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !2cb0637be1b04080b43b84fb5275269a25cfc672897886337978163d9e73da4be2bab5036eecc91d19b5c8b542bde6ed4514bb22f3e0a6ea0792ea062c60a96dd266d7e470cf4407463286c5cd0397a8ea0011b3bc6f99be9e78631c65; + 1 encin + !2cb0637be1b04080b43b84fb5275269a25cfc672897886337978163d9e73da4be2bab5036eecc91d19b5c8b542bde6ed4514bb22f3e0a6ea0792ea062c60a96dd266d7e470cf4407463286c5cd0397a8ea0011b3bc6f99be9e78631c65 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !5d7358063a8200c777e45389670e232d4188d628376180c5032dd7f2c11d372eaff574f40a1b58ee0e0af0242bf7c24d5d70b2f7b088c052111006fad011ec35456990459fd16b7db74439c5e9e5f53ad39055adc2786ac65b9c2faed47c; + 1 encin + !5d7358063a8200c777e45389670e232d4188d628376180c5032dd7f2c11d372eaff574f40a1b58ee0e0af0242bf7c24d5d70b2f7b088c052111006fad011ec35456990459fd16b7db74439c5e9e5f53ad39055adc2786ac65b9c2faed47c + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !63d76039f6f5138bd4b61472da6cb3493ae0928323e84244dfc79d832e1f3f93919b619a1526969365b2622ad4fa0f5d6d8302ecc2e5dddb15d22978f52b78f21bb79f6fd0c587cf36a6d38671ad474d4bcbdad048f79e34aeeafa936a4fde; + 1 encin + !63d76039f6f5138bd4b61472da6cb3493ae0928323e84244dfc79d832e1f3f93919b619a1526969365b2622ad4fa0f5d6d8302ecc2e5dddb15d22978f52b78f21bb79f6fd0c587cf36a6d38671ad474d4bcbdad048f79e34aeeafa936a4fde + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !4fbb7e5d6f71ab847942a0a0581532679ad7b9a544e42c1a337144c0cc1e779db8172d5fd172e1957d51b3e70fe4fb0930997384efb7f1b17d16b58075f866effa13b13e5104ab8c110c6b9a7c89ff8ad1bebb27a31c22e6e2b49aec205e04b9; + 1 encin + !4fbb7e5d6f71ab847942a0a0581532679ad7b9a544e42c1a337144c0cc1e779db8172d5fd172e1957d51b3e70fe4fb0930997384efb7f1b17d16b58075f866effa13b13e5104ab8c110c6b9a7c89ff8ad1bebb27a31c22e6e2b49aec205e04b9 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !805395735a1a2f301dcfbb22cb101078d52fbf686c8998a6c0b2c5f3dee68976dbeef15796df545f345725e72e4b5d48c4b9102932f5db2574d88b91da298421d3f0be76efaafefde2aee208b8ee2e336d33f55047e42f16f87a164f56f3a5f4a0; + 1 encin + !805395735a1a2f301dcfbb22cb101078d52fbf686c8998a6c0b2c5f3dee68976dbeef15796df545f345725e72e4b5d48c4b9102932f5db2574d88b91da298421d3f0be76efaafefde2aee208b8ee2e336d33f55047e42f16f87a164f56f3a5f4a0 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !8b53700554352c4e5b1f7907cb52e061b48cf3e459f7db87fc14df1935072944bdf81f088624fe2b7bbd2a4a0e15ffd69017d685b06f138d8bd5f5999e9a1a9592a7774de43014b98cecea6e798eec14b2737239028d70aed2a6e6806bca6f691eaa; + 1 encin + !8b53700554352c4e5b1f7907cb52e061b48cf3e459f7db87fc14df1935072944bdf81f088624fe2b7bbd2a4a0e15ffd69017d685b06f138d8bd5f5999e9a1a9592a7774de43014b98cecea6e798eec14b2737239028d70aed2a6e6806bca6f691eaa + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !d6d4465829dfd8dc49547f835882ada15a406833a83ca2f6fbb4bb7c0db0d64ed12884e2d32abc05f0a429287746399e9262e2f304c0ad6ae36c1a5cd530fd929dfb0163017ed471b52fe10c1062c22f046a7fd375cd113d26b38b35504a8f32aefb5a; + 1 encin + !d6d4465829dfd8dc49547f835882ada15a406833a83ca2f6fbb4bb7c0db0d64ed12884e2d32abc05f0a429287746399e9262e2f304c0ad6ae36c1a5cd530fd929dfb0163017ed471b52fe10c1062c22f046a7fd375cd113d26b38b35504a8f32aefb5a + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !a6b1200dd06a7e2a036e971949474aaa154f524b4215ff691706a0e12498ff335792866ae19a48d8e4335bfb6d85d96126ed7b8b581b7f7042a0c7e5d6901ad8f32b7f29c04463f70aa8d5de527d19df1ad6d48a192109afc58eaa22c515ae0a10fde60c; + 1 encin + !a6b1200dd06a7e2a036e971949474aaa154f524b4215ff691706a0e12498ff335792866ae19a48d8e4335bfb6d85d96126ed7b8b581b7f7042a0c7e5d6901ad8f32b7f29c04463f70aa8d5de527d19df1ad6d48a192109afc58eaa22c515ae0a10fde60c + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !27be9a888de11c9b544d1c8e65b9453737464138e4a25b81ba946034da90d61dbd46cb298f1fecf60b6f9c6351ca9193f1db7f340e876a0ff812a0e4064332f91ac684d31b242229fd19d58c0626a6a43a5d9e8641cf6d9fd072f6e2a15ee1ecfa508e54dd; + 1 encin + !27be9a888de11c9b544d1c8e65b9453737464138e4a25b81ba946034da90d61dbd46cb298f1fecf60b6f9c6351ca9193f1db7f340e876a0ff812a0e4064332f91ac684d31b242229fd19d58c0626a6a43a5d9e8641cf6d9fd072f6e2a15ee1ecfa508e54dd + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !578538e9c435f10544df84cadc023d8513a0e51c73a512764d817d4ceab7f7ceb522031a9f94f0d73aae136f1e0d8468f99ba24f40803ead8beed12fac2385235887cb680ce3aa93f882f62061ff7a6411d4fa0ae395172ccceb4b4ee66c821e6d6b2d979bf2; + 1 encin + !578538e9c435f10544df84cadc023d8513a0e51c73a512764d817d4ceab7f7ceb522031a9f94f0d73aae136f1e0d8468f99ba24f40803ead8beed12fac2385235887cb680ce3aa93f882f62061ff7a6411d4fa0ae395172ccceb4b4ee66c821e6d6b2d979bf2 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !2a564c70ffaf1f6c9b4bde77e13b803a92603b1d853555a334d41eb38fca095fee9cf4d47b6dc34226f735c0471bb4ee20ab690a72d4bf6d9e9057fc44082334d00440e6264184e1f2140eea5f795b7b688959f00028d2b2620a1b1e085286ad8d9bce2cdccae7; + 1 encin + !2a564c70ffaf1f6c9b4bde77e13b803a92603b1d853555a334d41eb38fca095fee9cf4d47b6dc34226f735c0471bb4ee20ab690a72d4bf6d9e9057fc44082334d00440e6264184e1f2140eea5f795b7b688959f00028d2b2620a1b1e085286ad8d9bce2cdccae7 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !c3a497949629704ca9b40657503c581ba1cebb20b4f7bd3dd1e71f4a7e3cf67e5609cd60b35b2c6fa9df996d7c4609b90dc8195e55b3fcbc90184538c18b1307e5161f85005b1f41b043577548ca9104912ac7aab9c3993169bd66af217efd52d59c0b2217a9595f; + 1 encin + !c3a497949629704ca9b40657503c581ba1cebb20b4f7bd3dd1e71f4a7e3cf67e5609cd60b35b2c6fa9df996d7c4609b90dc8195e55b3fcbc90184538c18b1307e5161f85005b1f41b043577548ca9104912ac7aab9c3993169bd66af217efd52d59c0b2217a9595f + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !fffa26f4dbd0fa61db80afadaec090c5a55a4c3a1394d7aa64b7d144705c3864c5f296ced71fe5e75f7ad7805abc8113417681659a53dff7b6209db2de89412fb3ad47d6eaf6bf4a6b3b10d8f1272dafa114b2ad3f3f1ff7bd72dcb282027879d42d128f197dbf67b2; + 1 encin + !fffa26f4dbd0fa61db80afadaec090c5a55a4c3a1394d7aa64b7d144705c3864c5f296ced71fe5e75f7ad7805abc8113417681659a53dff7b6209db2de89412fb3ad47d6eaf6bf4a6b3b10d8f1272dafa114b2ad3f3f1ff7bd72dcb282027879d42d128f197dbf67b2 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !86cba2f603aae0dcbfbb3ac33c846452e4d0ba8891b11cee14f2117e97b48fc780e19fddd82131f310138a8d806525714a34b1ad02ce1ed1745572c031c64ddfe16b6f90b45242c4522fa8c050a4e392881de0d7d5c36d11774d5d0e83a6614a80bad1e7968d402b8e64; + 1 encin + !86cba2f603aae0dcbfbb3ac33c846452e4d0ba8891b11cee14f2117e97b48fc780e19fddd82131f310138a8d806525714a34b1ad02ce1ed1745572c031c64ddfe16b6f90b45242c4522fa8c050a4e392881de0d7d5c36d11774d5d0e83a6614a80bad1e7968d402b8e64 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !f0334f7e4a7acda9077684729f1740afdd50931e8814f3308a736faeb67d7c70432d076df140034313bece815f7060eca9911359681b6647feda952b8701ecc1996426eb7951df47bb2550700dac2da57643865ab6a3e59598094f72d646d39103d3251f6b2da9663a2ef5; + 1 encin + !f0334f7e4a7acda9077684729f1740afdd50931e8814f3308a736faeb67d7c70432d076df140034313bece815f7060eca9911359681b6647feda952b8701ecc1996426eb7951df47bb2550700dac2da57643865ab6a3e59598094f72d646d39103d3251f6b2da9663a2ef5 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !087b6a424f1c2a083940fb4d7c9772eb2fbd9dbbba59054f76c3c0687a78dab3bd6124da5960c9b9618c5b5ac00b6899dbc1d91a75a74d4824c6ab5647b3783ca0da89f2cbcbf2274b1e9529fede6439d7ecaf2ffc08e9ad1903af418590810abf8618668b5dad8ee1b21b18; + 1 encin + !087b6a424f1c2a083940fb4d7c9772eb2fbd9dbbba59054f76c3c0687a78dab3bd6124da5960c9b9618c5b5ac00b6899dbc1d91a75a74d4824c6ab5647b3783ca0da89f2cbcbf2274b1e9529fede6439d7ecaf2ffc08e9ad1903af418590810abf8618668b5dad8ee1b21b18 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !75bd4f8ff0d109a3425322a55977727a53cd81735fe798dea31a1e308bbe6d19cdd4fd309b63c5f0f3891374715ad385f57e780974aaf64c73cf3b183d50755e3c753dd7a080da9da863233619defebe67a3059dc125c707f873ec8acc1334d0226240edd4aab093480598ec21; + 1 encin + !75bd4f8ff0d109a3425322a55977727a53cd81735fe798dea31a1e308bbe6d19cdd4fd309b63c5f0f3891374715ad385f57e780974aaf64c73cf3b183d50755e3c753dd7a080da9da863233619defebe67a3059dc125c707f873ec8acc1334d0226240edd4aab093480598ec21 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !5e86f82dc7b83b01a9d4362e7ad0871c53e8f8c96ff7b78746d0162257fa81771bb8bae3a6f34bccc42951fbc1c06fb1dd518ac733ecc253357c93892946944f815cba634f1f85178f744ac95ce478b2661a740b45a422bf6cec31cfe20d78a3ee978dd1d7e79910a5903bd1c782; + 1 encin + !5e86f82dc7b83b01a9d4362e7ad0871c53e8f8c96ff7b78746d0162257fa81771bb8bae3a6f34bccc42951fbc1c06fb1dd518ac733ecc253357c93892946944f815cba634f1f85178f744ac95ce478b2661a740b45a422bf6cec31cfe20d78a3ee978dd1d7e79910a5903bd1c782 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !3d906019d05fe688c8b215aa6a765e4ca03745195109c46c53f84b3b54dc8e4c2ded2a13de8e8678d94b8dcf9a936783ce726344199e73f82f182459e4bd88fdb6047e7e3bd16d2ff501f62e991e0bda2026be06e661313c532e84396cf6f56afd2ca9f3cc79cf1cc3e52a1d893eb6; + 1 encin + !3d906019d05fe688c8b215aa6a765e4ca03745195109c46c53f84b3b54dc8e4c2ded2a13de8e8678d94b8dcf9a936783ce726344199e73f82f182459e4bd88fdb6047e7e3bd16d2ff501f62e991e0bda2026be06e661313c532e84396cf6f56afd2ca9f3cc79cf1cc3e52a1d893eb6 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !6a65bc3cf9bcad98c0a733fafd599fbc9553584eba486ef3fb197ed4a12ff5da33d7b4bddb9ad153b890f870801b45c1e0115ffa220c24d8d60c5529c81a9141808a4d8ab91f2f93356247bb1c951aaf7d23328562150265130f607d54289500020099a247034a75ee58fd73af740e16; + 1 encin + !6a65bc3cf9bcad98c0a733fafd599fbc9553584eba486ef3fb197ed4a12ff5da33d7b4bddb9ad153b890f870801b45c1e0115ffa220c24d8d60c5529c81a9141808a4d8ab91f2f93356247bb1c951aaf7d23328562150265130f607d54289500020099a247034a75ee58fd73af740e16 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !a0648c74ee2872b422b64290354ce4f38de2fb62fbbb99ae2a46a7233b9f4a9ea08ec253aa377042855099edca949834b90091dcf7d946baa658f0ef2906c906f70c151a19a1a73d9ae83176364533cd9b4de078329dd07ea96cdf2f6264446d5b9386a3d4a3f49d6db97e05ec518950aa; + 1 encin + !a0648c74ee2872b422b64290354ce4f38de2fb62fbbb99ae2a46a7233b9f4a9ea08ec253aa377042855099edca949834b90091dcf7d946baa658f0ef2906c906f70c151a19a1a73d9ae83176364533cd9b4de078329dd07ea96cdf2f6264446d5b9386a3d4a3f49d6db97e05ec518950aa + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !2c887d8300686768cac139577901999d57371ff152ec09e5cb979cf360a9704669b79a9e9252c6c90ba9f9e44bd5e77224081c3e3aebdc5d8f59a10794dfa167f8c2f6a25b3386eae4dd580ad86ebaa1df9bf5169f09698277f52f6877d3724c05bc07d3975865bc61b19a13174c2a750c96; + 1 encin + !2c887d8300686768cac139577901999d57371ff152ec09e5cb979cf360a9704669b79a9e9252c6c90ba9f9e44bd5e77224081c3e3aebdc5d8f59a10794dfa167f8c2f6a25b3386eae4dd580ad86ebaa1df9bf5169f09698277f52f6877d3724c05bc07d3975865bc61b19a13174c2a750c96 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !63c6df75230c4afab23cf2dc6f92138b9fde21cf5cd48865f3cb44642fb0f36a8212d07e4374351fee51566be0a0a70bfe1fb03b87cac717a3c667fc9190ba053a471428b2cc1329f49976a026150952bd8438220edfc60d9c3cd07dcbb6bff3ff0ea3353afeea85ab194db96161146f5e9e89; + 1 encin + !63c6df75230c4afab23cf2dc6f92138b9fde21cf5cd48865f3cb44642fb0f36a8212d07e4374351fee51566be0a0a70bfe1fb03b87cac717a3c667fc9190ba053a471428b2cc1329f49976a026150952bd8438220edfc60d9c3cd07dcbb6bff3ff0ea3353afeea85ab194db96161146f5e9e89 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !f980b6dd51a09fe70b93e752ec22a082e7a8fc5ffacaa9d53dd68c5688400355e52588087e91e4ee2185d54f998ab1fc1d263bdd5ae9cc1223414113bbc31f4e5eeeeab7764c60319b55beae12c1e02f92d5aa938c10b706027066cebde0269b572ba23f6e4972b127905faad5eaef8c5bc12115; + 1 encin + !f980b6dd51a09fe70b93e752ec22a082e7a8fc5ffacaa9d53dd68c5688400355e52588087e91e4ee2185d54f998ab1fc1d263bdd5ae9cc1223414113bbc31f4e5eeeeab7764c60319b55beae12c1e02f92d5aa938c10b706027066cebde0269b572ba23f6e4972b127905faad5eaef8c5bc12115 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !dee0d26ce7b9d85fefcef72ed7e3c995c06f60de485ce04b0531279d22eb6553bb58fc807e119f98e71220ca51f4242b8ae59228afa4756fd98e3c1f12efea0cd44f1bcdc6ccd6e77e0fff4375e65f0dd1c1a0c79aa17f3c79b09cde2ec364ee122e37a54bd1fdf4d97fa50a40b7eef07f781f9d1a; + 1 encin + !dee0d26ce7b9d85fefcef72ed7e3c995c06f60de485ce04b0531279d22eb6553bb58fc807e119f98e71220ca51f4242b8ae59228afa4756fd98e3c1f12efea0cd44f1bcdc6ccd6e77e0fff4375e65f0dd1c1a0c79aa17f3c79b09cde2ec364ee122e37a54bd1fdf4d97fa50a40b7eef07f781f9d1a + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !5fe15102695168abca87356479efd9a734ccbd1ebcc2742f470aec0931a24379d225c9f5617b02415e3db7fd8e741d359a4059830dd9c83c2152370b5b89fae3d6c89ec9445bd339c689741bc08c1ba7392ee4f9cb5d06612dc766919144add4a7659222c8bc3c63b08353e7f24ecfc4b459a27e9bf4; + 1 encin + !5fe15102695168abca87356479efd9a734ccbd1ebcc2742f470aec0931a24379d225c9f5617b02415e3db7fd8e741d359a4059830dd9c83c2152370b5b89fae3d6c89ec9445bd339c689741bc08c1ba7392ee4f9cb5d06612dc766919144add4a7659222c8bc3c63b08353e7f24ecfc4b459a27e9bf4 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !4a9ab68772ead5aaa6c1e40e7ce22e1647070b5185a4260cebddb7c7bbf2ab88bf8c81e361cccfac2e96e971cef18e0f16607f04f01fc5131542f3de46ab6192bfcce55fdd37c32fe86423222b6dfa1c443b6e4a1b7039fe07e80f390f6bc99ca5ced16148c39f25f0644ed58fad44bc44fe6fe8ff87c9; + 1 encin + !4a9ab68772ead5aaa6c1e40e7ce22e1647070b5185a4260cebddb7c7bbf2ab88bf8c81e361cccfac2e96e971cef18e0f16607f04f01fc5131542f3de46ab6192bfcce55fdd37c32fe86423222b6dfa1c443b6e4a1b7039fe07e80f390f6bc99ca5ced16148c39f25f0644ed58fad44bc44fe6fe8ff87c9 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !eaf08edd13b54042c0b2513d87f8fbe40eeeaba7395f20bfba998f23d19a88753f86588a21d1f47ad76733423bdf27ac253040608d12d7a931495a0572dd025230dc8992423e87639b5948cf792bf205bfc010d6e1edeb7a0244af19e38fe696771a1ba7f50fa7df7e20e2d514b73fa4db9668749f53ae91; + 1 encin + !eaf08edd13b54042c0b2513d87f8fbe40eeeaba7395f20bfba998f23d19a88753f86588a21d1f47ad76733423bdf27ac253040608d12d7a931495a0572dd025230dc8992423e87639b5948cf792bf205bfc010d6e1edeb7a0244af19e38fe696771a1ba7f50fa7df7e20e2d514b73fa4db9668749f53ae91 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !b741f0d5ddf4fd12c35b263c809648be6696576310c2982b2dd2ebf259df191cdbc4792fde17eec1ab0d470c9814ef62248ea816f8cb3a0be431179c0d44762f94014aaefb3227d70eda04f2ab460dd59bff4adced323d673b91d2128fbaed95fc9ccbd68195bf3dc50f382ce3987f7bef692647e8dfbee4d5; + 1 encin + !b741f0d5ddf4fd12c35b263c809648be6696576310c2982b2dd2ebf259df191cdbc4792fde17eec1ab0d470c9814ef62248ea816f8cb3a0be431179c0d44762f94014aaefb3227d70eda04f2ab460dd59bff4adced323d673b91d2128fbaed95fc9ccbd68195bf3dc50f382ce3987f7bef692647e8dfbee4d5 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !03b0931db86e38f25ad44cfaafeedfed18b06a97f92eff0d64011cb0733b4e514e59531569fd97b4309801b8671405068159bc1a5ffaf2cb177235ed52fa285d24309f7ab065eee2b69bb7c2c7c20211829883bb2250bce0d233c203748661cdbf9007dacf93746176e2bf4f67fd90a01bfc55723e783729b887; + 1 encin + !03b0931db86e38f25ad44cfaafeedfed18b06a97f92eff0d64011cb0733b4e514e59531569fd97b4309801b8671405068159bc1a5ffaf2cb177235ed52fa285d24309f7ab065eee2b69bb7c2c7c20211829883bb2250bce0d233c203748661cdbf9007dacf93746176e2bf4f67fd90a01bfc55723e783729b887 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !989906fd9f5f75949d1a37e67787ee1ef6e9a363200079e05becff5d47ad954753e5573aa490a193560dc32bdfeacad0e1ba94762967d31a87c3a5ba4ca89eb00db5f3ed51c5f6aa987bd68b8d7d6faccb4c3ee81a9d2d2f1ad36af8717765c076632448b4dea1c7e9cff8ebcb4a56cf6232aff243cdc94d5e3cb1; + 1 encin + !989906fd9f5f75949d1a37e67787ee1ef6e9a363200079e05becff5d47ad954753e5573aa490a193560dc32bdfeacad0e1ba94762967d31a87c3a5ba4ca89eb00db5f3ed51c5f6aa987bd68b8d7d6faccb4c3ee81a9d2d2f1ad36af8717765c076632448b4dea1c7e9cff8ebcb4a56cf6232aff243cdc94d5e3cb1 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !4a372dac2c861d6198141bbe14c36939fe30c5766fe2aa1c6c97c236e131f0fc77255636552d9a201c598c69d72482886db1ce216c50cfb870e622ad866518f6ce0d69d4bb6af0d53105a024ac3af6a684ce751dd2ce22b4aedf869f4c4ac8153e4c6c11188a1a7efa9ad48f0c219447bb0b2db7863495224cfb92f9; + 1 encin + !4a372dac2c861d6198141bbe14c36939fe30c5766fe2aa1c6c97c236e131f0fc77255636552d9a201c598c69d72482886db1ce216c50cfb870e622ad866518f6ce0d69d4bb6af0d53105a024ac3af6a684ce751dd2ce22b4aedf869f4c4ac8153e4c6c11188a1a7efa9ad48f0c219447bb0b2db7863495224cfb92f9 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !49cdb3599b611bcb646931ea2cd3c1060ee0a9929da09814766ec5a2b21c40b45f538d495a5222dedcc132cbb1ec9fac86413e51968c986a461376b32d9ef7e46d6eb78d30c1de2cdbe962406299f415b71bf161b2b281f86bc78d4c607d1998ddd54605a5c320da45733d83f6025cbfd8040f238be9e599d2c75b8966; + 1 encin + !49cdb3599b611bcb646931ea2cd3c1060ee0a9929da09814766ec5a2b21c40b45f538d495a5222dedcc132cbb1ec9fac86413e51968c986a461376b32d9ef7e46d6eb78d30c1de2cdbe962406299f415b71bf161b2b281f86bc78d4c607d1998ddd54605a5c320da45733d83f6025cbfd8040f238be9e599d2c75b8966 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !ea38bad18ac44276bd7a3f160166311eb27e5290267e2f9532d3780bc31d47ff1a3b14f4dc4ca025f5f62735205fdbe6c26f96cbcdef43b6a00f22ea30cf76068decad0d9bea2c943f112cc0233d862f9f729e1a8563c3aaa8d9dd7ea6060c4d080be791865b38d54517e01a7bc06b8172347dfcfa58a4af0cb726ec0615; + 1 encin + !ea38bad18ac44276bd7a3f160166311eb27e5290267e2f9532d3780bc31d47ff1a3b14f4dc4ca025f5f62735205fdbe6c26f96cbcdef43b6a00f22ea30cf76068decad0d9bea2c943f112cc0233d862f9f729e1a8563c3aaa8d9dd7ea6060c4d080be791865b38d54517e01a7bc06b8172347dfcfa58a4af0cb726ec0615 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !cb799d41fdeca9a8edf3be872ef7ae761b03f63503d4d9e7296c386dbc4032396844e9949a77dff11174a74d0f0d90a6cd54f8f32188756639949478dc73a386d79dff19238a2ea6d5595e5015ac55f5bba55390a8d1c0eeaf908068167c7b86b4633b632cb0e271c35506baad8f79a63e04face56d157101c7bfa2f6aaadb; + 1 encin + !cb799d41fdeca9a8edf3be872ef7ae761b03f63503d4d9e7296c386dbc4032396844e9949a77dff11174a74d0f0d90a6cd54f8f32188756639949478dc73a386d79dff19238a2ea6d5595e5015ac55f5bba55390a8d1c0eeaf908068167c7b86b4633b632cb0e271c35506baad8f79a63e04face56d157101c7bfa2f6aaadb + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !1730946f2b7514c84c2bb6ac6e268d42627d50a468c466a6fb8e7b6a27062df5b9bd67c858bd2b174a8ae43e506177fceaafde77296c5febf48fb91b6fbd38bd476bc5f4b1b246516ac7cb536887dad9e7a058ccff5733c443fdcb4ec7fccec491f204e07e801ada05af9df8b267ec7669b1d213d74fdb4c996ac67ff7b14d79; + 1 encin + !1730946f2b7514c84c2bb6ac6e268d42627d50a468c466a6fb8e7b6a27062df5b9bd67c858bd2b174a8ae43e506177fceaafde77296c5febf48fb91b6fbd38bd476bc5f4b1b246516ac7cb536887dad9e7a058ccff5733c443fdcb4ec7fccec491f204e07e801ada05af9df8b267ec7669b1d213d74fdb4c996ac67ff7b14d79 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !9e8b99a3af0f1e8b6316b1c0efcd812160803e80b9652d059bf2f85ec2e91581c6128e2d2afbfae23accfda5acfab80baad8466e97f3f73899ccbd9de6048dd85aeb5ab78a39fdca62af5a4c556feddbf97a50f7fc376c3a41c46bb599d6a44334dcb17a03fa93262aae1041322dcc17d7e08a655985fe6574db79f951f336bda7; + 1 encin + !9e8b99a3af0f1e8b6316b1c0efcd812160803e80b9652d059bf2f85ec2e91581c6128e2d2afbfae23accfda5acfab80baad8466e97f3f73899ccbd9de6048dd85aeb5ab78a39fdca62af5a4c556feddbf97a50f7fc376c3a41c46bb599d6a44334dcb17a03fa93262aae1041322dcc17d7e08a655985fe6574db79f951f336bda7 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !a21883c5d37094d553045637551fd4993ee73471e6fd2152111a3c5336622130199e981dce4ec30a8f151d6b1b3b070a343196c8624146f6247bc79bee489e883d2a6e720995b684c5b30f9aa734b6f1891ccaf05f464dcd9bcfbdcbac372e28845de339f8920716ba6efe914c166817c74f2df2e5331997426548a92be3a2dd5679; + 1 encin + !a21883c5d37094d553045637551fd4993ee73471e6fd2152111a3c5336622130199e981dce4ec30a8f151d6b1b3b070a343196c8624146f6247bc79bee489e883d2a6e720995b684c5b30f9aa734b6f1891ccaf05f464dcd9bcfbdcbac372e28845de339f8920716ba6efe914c166817c74f2df2e5331997426548a92be3a2dd5679 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !8d5a18f2b27043ec61d95990211be55d3901870f9ef30b2403e59bd09b30617f5e5043089ebd916a3f13591d72d7394ad4b2dc66ebebb7c1c5e6329525d8bf1c2cfe5f657363c180bd37f39f8e0fe67c16918d02a7dbc30d4436441d8383b7b50e8ce8d414506c20917b29d4b0f249fefef11cac8af387d27d0d45149860685143cbc0; + 1 encin + !8d5a18f2b27043ec61d95990211be55d3901870f9ef30b2403e59bd09b30617f5e5043089ebd916a3f13591d72d7394ad4b2dc66ebebb7c1c5e6329525d8bf1c2cfe5f657363c180bd37f39f8e0fe67c16918d02a7dbc30d4436441d8383b7b50e8ce8d414506c20917b29d4b0f249fefef11cac8af387d27d0d45149860685143cbc0 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !c512f89967db78a64284f9e8975327fe142cce566d2adad60dc3ec81d462877fea4fe06a32428b9a148dd9a8d4a661be6c56582e30a65d6e3289c08dcadcc717e4e9f785a7a887d652174f586286d9d2104171f938b084ed61dc5df1158c328e361c75b9901807dd2563d1be9c1507b4dbd1bafefb837ff8a2538c131425a27d29d59938; + 1 encin + !c512f89967db78a64284f9e8975327fe142cce566d2adad60dc3ec81d462877fea4fe06a32428b9a148dd9a8d4a661be6c56582e30a65d6e3289c08dcadcc717e4e9f785a7a887d652174f586286d9d2104171f938b084ed61dc5df1158c328e361c75b9901807dd2563d1be9c1507b4dbd1bafefb837ff8a2538c131425a27d29d59938 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !467c5e146445701168ce494b307cd12c729189b1c1176ff06ef8f93ff1b0690f311e284b68380395224cdea9e382a69b8d5fe027a5f79ccab42f523a1749362873aed701acbede76da4614cc538310027a37bcb6518e19c8ce3ab8b7c98f486217e3263887566cc8be7447cd5b14bfd27ed2336496ee9b4542603456d0511bbcebe3d1a2f8; + 1 encin + !467c5e146445701168ce494b307cd12c729189b1c1176ff06ef8f93ff1b0690f311e284b68380395224cdea9e382a69b8d5fe027a5f79ccab42f523a1749362873aed701acbede76da4614cc538310027a37bcb6518e19c8ce3ab8b7c98f486217e3263887566cc8be7447cd5b14bfd27ed2336496ee9b4542603456d0511bbcebe3d1a2f8 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !0fb913af1109a3ba37d5085ea5540b1ee92c5aed16f00fad7aad185e67c6071be4d496df368c7bf8e4acabd275754a6f1e8f59dd5c590dd4f43e86f4f150dd5a6f2d8f79852ab4df1274b660720409993effaff1da293fdf035acee7a60ad46a5f8d308aeeb97124f2429c4f19354faa8a2ef7dbfc7a44685601dbff5561dcd7d69e8d6b9e87; + 1 encin + !0fb913af1109a3ba37d5085ea5540b1ee92c5aed16f00fad7aad185e67c6071be4d496df368c7bf8e4acabd275754a6f1e8f59dd5c590dd4f43e86f4f150dd5a6f2d8f79852ab4df1274b660720409993effaff1da293fdf035acee7a60ad46a5f8d308aeeb97124f2429c4f19354faa8a2ef7dbfc7a44685601dbff5561dcd7d69e8d6b9e87 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !26d41ee387956931db354efe7fb8bc6a517e5418806986d8f758934f40f61d71788296cd7e3fee5f8c2ccd32375527c39c4de49b53c6c71726145fb173bd091230842edfc5f54c9ec2012c06decd95881d9da61d4c93ba6c7fff662073969fd74eb12a3b8ed255b01fc2c138f45033c03a77b127d0a17d4a00c02599fea636e1afbb257b9afb92; + 1 encin + !26d41ee387956931db354efe7fb8bc6a517e5418806986d8f758934f40f61d71788296cd7e3fee5f8c2ccd32375527c39c4de49b53c6c71726145fb173bd091230842edfc5f54c9ec2012c06decd95881d9da61d4c93ba6c7fff662073969fd74eb12a3b8ed255b01fc2c138f45033c03a77b127d0a17d4a00c02599fea636e1afbb257b9afb92 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !67f8a44aaff6a6854b93913b91acf1ff4279196ead3237bb06ec6186ed2ce0d0b89adaed048d0fd098e2192fd3ea2a61657f05d488a9ed67078d93ae8b90d45b2498a368cdb1da1c90aca04f6d1109bbbe219b6dc9f8a9c74aaf30ad127d50286409d9ee829a20c25b365bef91582c0261d6b34adbe0cdc638b90c17edf99f8b1464986012542dba; + 1 encin + !67f8a44aaff6a6854b93913b91acf1ff4279196ead3237bb06ec6186ed2ce0d0b89adaed048d0fd098e2192fd3ea2a61657f05d488a9ed67078d93ae8b90d45b2498a368cdb1da1c90aca04f6d1109bbbe219b6dc9f8a9c74aaf30ad127d50286409d9ee829a20c25b365bef91582c0261d6b34adbe0cdc638b90c17edf99f8b1464986012542dba + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !cdda0d5dc2e67d6068e0489111a8a7b04beb7acd74f69fc6ee867ad5da4fdf93267b2085186a8c243b61bf93b9ecaa3f4288597cf18ced9dab52efa3cb52b01211527a90872755862c765d659baa936a5eb74d2c5de82206ae1b170183ea10f49de534928879d813b4f450c3c1b8f5e80de7282b010a3c1d20214b0127fd0ab1fa34818275cc959540; + 1 encin + !cdda0d5dc2e67d6068e0489111a8a7b04beb7acd74f69fc6ee867ad5da4fdf93267b2085186a8c243b61bf93b9ecaa3f4288597cf18ced9dab52efa3cb52b01211527a90872755862c765d659baa936a5eb74d2c5de82206ae1b170183ea10f49de534928879d813b4f450c3c1b8f5e80de7282b010a3c1d20214b0127fd0ab1fa34818275cc959540 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !ff7cf9c75c7604cbb436d1b77819520b3fba90750073a66ed6df1533aa012bae59b17d5dc60832695394f3f9893857169ae23d961c96c6ec35054e55d600a62c9f0313780a7b8b3e6c55b3d268fdc25bcd379971fd82639c375ff0ca93b116bb992b137758c8ccfb1379406d50f64d58095b90076561706ab302db92d47c4ebf54ca9b2ba5594c1cd67f; + 1 encin + !ff7cf9c75c7604cbb436d1b77819520b3fba90750073a66ed6df1533aa012bae59b17d5dc60832695394f3f9893857169ae23d961c96c6ec35054e55d600a62c9f0313780a7b8b3e6c55b3d268fdc25bcd379971fd82639c375ff0ca93b116bb992b137758c8ccfb1379406d50f64d58095b90076561706ab302db92d47c4ebf54ca9b2ba5594c1cd67f + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !57b2bfaec0af01a35c50b5b0d8d6dd4ca5d9cb51ecaa601e31f845cc6772b6522112c913ef6f3b875ff364f91d60b340b687e1d54163e05086b69518c6697faa995a4cbbe7b467ec7d49b6cdd074d3458de3bf45eb468b3b6e4949725bb6d67e2e69788d35bb36817791f2d55b1289281f614d48f6d74f20856be25523e285df3febfb775289b4491635b2; + 1 encin + !57b2bfaec0af01a35c50b5b0d8d6dd4ca5d9cb51ecaa601e31f845cc6772b6522112c913ef6f3b875ff364f91d60b340b687e1d54163e05086b69518c6697faa995a4cbbe7b467ec7d49b6cdd074d3458de3bf45eb468b3b6e4949725bb6d67e2e69788d35bb36817791f2d55b1289281f614d48f6d74f20856be25523e285df3febfb775289b4491635b2 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !55af1f6bbdc6cb39b760e9f2b0bb10078cac8178deed373a5593730cf99c67356a352dcd3c6cbbd046cbb0c4f1e0c4658e982f4052a908c95e53d18704eb50d1d53a4ac371a96eecbca72a017ad9e91a34842233e48450b191ac8bd18d0e55d8ce893f690d50ebba44c7c85f1cce6e99b6eb7f626ba32729c4871222e893592df5904ac3a102d139ebb8ad6a; + 1 encin + !55af1f6bbdc6cb39b760e9f2b0bb10078cac8178deed373a5593730cf99c67356a352dcd3c6cbbd046cbb0c4f1e0c4658e982f4052a908c95e53d18704eb50d1d53a4ac371a96eecbca72a017ad9e91a34842233e48450b191ac8bd18d0e55d8ce893f690d50ebba44c7c85f1cce6e99b6eb7f626ba32729c4871222e893592df5904ac3a102d139ebb8ad6a + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !817f9d166e2324ca704127c3235a0201526f6cae694aa8a8a8cfab625d28f75a639c816562ac0f520b8ce5f075be45cd939a1978224d9b7a5cc703a623cf7c1c255825f246b6ff608bd90a859d6e4119b27e48018c8acf6fb82ea093d2c926a3f4a5f0d7f91953c0610858d063f2d28fb13c12d8f02a0c078707408c323ba8be17ce2a8df6862095adce0ead83; + 1 encin + !817f9d166e2324ca704127c3235a0201526f6cae694aa8a8a8cfab625d28f75a639c816562ac0f520b8ce5f075be45cd939a1978224d9b7a5cc703a623cf7c1c255825f246b6ff608bd90a859d6e4119b27e48018c8acf6fb82ea093d2c926a3f4a5f0d7f91953c0610858d063f2d28fb13c12d8f02a0c078707408c323ba8be17ce2a8df6862095adce0ead83 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !7298ea337f59a018915318a4d0db523f1df3d0be7c3da956e040361f40e48eb7e0bc2dfa76e840cd47e3d62c6345be06a0b05cbb17633c1892f0c4d5d9929bde4ddf116b6114e10ed97c625702f60dfcf2c9bbf89f4602490c35f9098d6f345fcd2c4988ff1a67033ed4eea7949432ff1e01ac43a0f134d221e6aea1b3c8d48c137a28da8b1f27b87fa698732cb0; + 1 encin + !7298ea337f59a018915318a4d0db523f1df3d0be7c3da956e040361f40e48eb7e0bc2dfa76e840cd47e3d62c6345be06a0b05cbb17633c1892f0c4d5d9929bde4ddf116b6114e10ed97c625702f60dfcf2c9bbf89f4602490c35f9098d6f345fcd2c4988ff1a67033ed4eea7949432ff1e01ac43a0f134d221e6aea1b3c8d48c137a28da8b1f27b87fa698732cb0 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !7cdf8bf7ca3c72b62bbf780fb64e525aed5941c5b427df53214e667aad02e66214082074bf5289412f065e28b5519d9e72990e7da6b821939b7003ed9d14be5d8e40e5b108163143032cf3ec8de24223ba39a7b31d3758a26992c0282bc8750768b9d7dc4873ea35a09dbfe21e87bf8dac2864cd7a6f9f233945a6d90a34b82c75b9cf3041d026a56f67c00b78dcc4; + 1 encin + !7cdf8bf7ca3c72b62bbf780fb64e525aed5941c5b427df53214e667aad02e66214082074bf5289412f065e28b5519d9e72990e7da6b821939b7003ed9d14be5d8e40e5b108163143032cf3ec8de24223ba39a7b31d3758a26992c0282bc8750768b9d7dc4873ea35a09dbfe21e87bf8dac2864cd7a6f9f233945a6d90a34b82c75b9cf3041d026a56f67c00b78dcc4 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !bfd7ed1fb1d60722deed37eee14269fa78711a348e141f74f1dc099369785bdf7ca057e1a9b8b8c312d780c714d0573a2d3f470c095f860eaa12f69178ea50e69c0c328cdcbd5e224c303f58ed28063c2bfb6ee0744de16664d3fbf3a865e5a4d24535d2ba07033f20ffdb85b3c7877aff32b8d221108b3ae453df3d83741b4d78b55b2541eae332e38083d56e9bacf9; + 1 encin + !bfd7ed1fb1d60722deed37eee14269fa78711a348e141f74f1dc099369785bdf7ca057e1a9b8b8c312d780c714d0573a2d3f470c095f860eaa12f69178ea50e69c0c328cdcbd5e224c303f58ed28063c2bfb6ee0744de16664d3fbf3a865e5a4d24535d2ba07033f20ffdb85b3c7877aff32b8d221108b3ae453df3d83741b4d78b55b2541eae332e38083d56e9bacf9 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !e7c484b50b855361c92113e093248397d429ad660b492d7cee162225be9be63c73a97d70202e511222075a6d456af4a1db02e9db54a120cc34088813c8c9ff466382147470a64c9376508d5198051dc5956a1ecca55da430a897922cf9feb2a02dd1230b015bc7693ba20a159dc2bb33df14b9a07d84815b51aa87b2a95fd3c6e641de935958e8bd39c0d4fb9f1bd8e090; + 1 encin + !e7c484b50b855361c92113e093248397d429ad660b492d7cee162225be9be63c73a97d70202e511222075a6d456af4a1db02e9db54a120cc34088813c8c9ff466382147470a64c9376508d5198051dc5956a1ecca55da430a897922cf9feb2a02dd1230b015bc7693ba20a159dc2bb33df14b9a07d84815b51aa87b2a95fd3c6e641de935958e8bd39c0d4fb9f1bd8e090 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !d074a37adcf01819fc6a0d9147bf55b3c4ef2f14f900ae750241648c134428dad5138117bc92b9ea4c0bb8838c8e578e909effb483d63b59500bce9e6ead001d8d21aef7cad4bf92ce2b7ae99885ea1444d065fc58026682d4e7d6f25fbb4952e2ce5fc50796a0332fabaa3a7cc3521d3a1d3994ef0cc3324ee0c04c928b3054326fff80bdbbe2c0ca251cefa84ff86e3e6e; + 1 encin + !d074a37adcf01819fc6a0d9147bf55b3c4ef2f14f900ae750241648c134428dad5138117bc92b9ea4c0bb8838c8e578e909effb483d63b59500bce9e6ead001d8d21aef7cad4bf92ce2b7ae99885ea1444d065fc58026682d4e7d6f25fbb4952e2ce5fc50796a0332fabaa3a7cc3521d3a1d3994ef0cc3324ee0c04c928b3054326fff80bdbbe2c0ca251cefa84ff86e3e6e + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !990e4ae01929cd998c3568ec423b4bd62a07a635453bb8a75c68c7db0ed42661294b8f7b0ba120885bcde030fadd1f8ef4f72650789da415ca81dc28b215d981909d6f67d3c03742028538a679b3f20ddae154b4d050748da40c93c2a6dd38287838118245ce3005f653aaf3af86e4b52159fd43984bca37f5c16b880c64154330134903dae9cddc6606f67acb5efb98eaf9ee; + 1 encin + !990e4ae01929cd998c3568ec423b4bd62a07a635453bb8a75c68c7db0ed42661294b8f7b0ba120885bcde030fadd1f8ef4f72650789da415ca81dc28b215d981909d6f67d3c03742028538a679b3f20ddae154b4d050748da40c93c2a6dd38287838118245ce3005f653aaf3af86e4b52159fd43984bca37f5c16b880c64154330134903dae9cddc6606f67acb5efb98eaf9ee + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !ad097be738311d2e3d2a39e3ee1b4bfe04bec56469d598b4b4e8834fc676df55cc9c84dbfaef232c595d15a89aabd037e9dc42227222e73aba94237615e296bc0f74e5cb363f16ddfb688c03fdfa10de2a24b5363241830d90dabfcd2fcd5759728ed2a8e65698780dc4851868791d86984ea5950f84e23e02ead6128cd93e68139b87f4ff50b53aef621ffc0bb6f8c645081765; + 1 encin + !ad097be738311d2e3d2a39e3ee1b4bfe04bec56469d598b4b4e8834fc676df55cc9c84dbfaef232c595d15a89aabd037e9dc42227222e73aba94237615e296bc0f74e5cb363f16ddfb688c03fdfa10de2a24b5363241830d90dabfcd2fcd5759728ed2a8e65698780dc4851868791d86984ea5950f84e23e02ead6128cd93e68139b87f4ff50b53aef621ffc0bb6f8c645081765 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !a292c951bf6914dc7215b8cc0643713e537adad6c5277ae3ffbab54cd6b6ba66b26e09b793af29e1b076e4c42a604385a1cb10e7e62b1b80a5b629305d343ef63a259c2b80e08b3c9f81dd5f9d81cce72ebd43b819efb12538f6a4d2ecfd20cb792106d4f830f47e17b1868514ce494c5394701bc4f0c7e6f151631953527b78b21f5127e128984d2b97cdb8e36e8c38fb084f9e86; + 1 encin + !a292c951bf6914dc7215b8cc0643713e537adad6c5277ae3ffbab54cd6b6ba66b26e09b793af29e1b076e4c42a604385a1cb10e7e62b1b80a5b629305d343ef63a259c2b80e08b3c9f81dd5f9d81cce72ebd43b819efb12538f6a4d2ecfd20cb792106d4f830f47e17b1868514ce494c5394701bc4f0c7e6f151631953527b78b21f5127e128984d2b97cdb8e36e8c38fb084f9e86 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !d78adbbbaf2c814d5ece2c7d946606951b1801c95be9594398aa6c1121bd48892f5e53b27a364a4d4d60a5ffdbf430b9e0058594e9a18bd0f875e0d3525b326e629f5a2a8552eef0c09188364ed7a1c27b59cd5b29155d77db49051eee69b6f01f49d23dcb5fb00e4c649a2cab73b34ca2ce7f7e307e9ae50ce9ff6b718a6165a5b0053d1fa94ee35ee2052d19215d6f92801c6263c0; + 1 encin + !d78adbbbaf2c814d5ece2c7d946606951b1801c95be9594398aa6c1121bd48892f5e53b27a364a4d4d60a5ffdbf430b9e0058594e9a18bd0f875e0d3525b326e629f5a2a8552eef0c09188364ed7a1c27b59cd5b29155d77db49051eee69b6f01f49d23dcb5fb00e4c649a2cab73b34ca2ce7f7e307e9ae50ce9ff6b718a6165a5b0053d1fa94ee35ee2052d19215d6f92801c6263c0 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !91553d2f6c335608f38b20fef4372d877d851d9f15f3088ea772a77880a9761784912d9863ab94984e7a047241b048d4fbf9dd3ae6f69be65b3a9fac8da0a3fc83968d9cde57f062eb67404b86eee957263b1bbd6d73d76bef122c5262fd33d2a84bccb5f39743b47f23d73850e68796a603ecf1957025ee90608b66c6137f08d56c4e2d5866265943dd1d5057a2495add3db8093d9359; + 1 encin + !91553d2f6c335608f38b20fef4372d877d851d9f15f3088ea772a77880a9761784912d9863ab94984e7a047241b048d4fbf9dd3ae6f69be65b3a9fac8da0a3fc83968d9cde57f062eb67404b86eee957263b1bbd6d73d76bef122c5262fd33d2a84bccb5f39743b47f23d73850e68796a603ecf1957025ee90608b66c6137f08d56c4e2d5866265943dd1d5057a2495add3db8093d9359 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !38a6a59d061f04f11ba59e1a39bdeaf5394895f788738eff0def07797d99371f02ad9f1f5e624308c1356730f1561008e0ae46ee68439715107ab75bdf960f5d2970c3899735fe9ae934afeb67a862c54eef175cbeaf856e84ca623f1e33084c21f9e5d7e68808b26edff9b08584537453f60ddbd52affd973423a3e671f5010f84908a9d76ee8e3a63b7d70f79e26c90f865a265184ba64; + 1 encin + !38a6a59d061f04f11ba59e1a39bdeaf5394895f788738eff0def07797d99371f02ad9f1f5e624308c1356730f1561008e0ae46ee68439715107ab75bdf960f5d2970c3899735fe9ae934afeb67a862c54eef175cbeaf856e84ca623f1e33084c21f9e5d7e68808b26edff9b08584537453f60ddbd52affd973423a3e671f5010f84908a9d76ee8e3a63b7d70f79e26c90f865a265184ba64 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !88368e2008e4625020bdc44e0ce3a7931a4e8ae20d109ff1ea15dadc5f5f20a20c397c08defe92c08bb3c9616af246a1081e79b3fab37030580c1184cc7b0e10ec37c7e0ca79e8cb2dc356fbdc920162407dbf70f16e44cddd14d96159e8ea3b90231f35356976c508ee854085bca1cd2601cdc4aeff90aa1e341a64f164acd6e75fdb4344bee0950c4c93a66cd73b58c9ad4969d704e4bef8; + 1 encin + !88368e2008e4625020bdc44e0ce3a7931a4e8ae20d109ff1ea15dadc5f5f20a20c397c08defe92c08bb3c9616af246a1081e79b3fab37030580c1184cc7b0e10ec37c7e0ca79e8cb2dc356fbdc920162407dbf70f16e44cddd14d96159e8ea3b90231f35356976c508ee854085bca1cd2601cdc4aeff90aa1e341a64f164acd6e75fdb4344bee0950c4c93a66cd73b58c9ad4969d704e4bef8 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !61bfce8ba6e1b8c43b1cb6d42068b9a05732db6d7ab6c6bf0eb5646952dfdedaaab8c098b68ff62bd5afefcdb8b9efa817db9a2c73287c80770cccaa904f978608cd084d64dfcff4694cba64d197f4d51d7bf727cb55c6218470ac2c4cf0097dc5af9e1aba3853f935c90627e8520c6c2dab7f49f8614c6aa2d732ae04483446925d6a24ae292ec5a5b5c614f3570f2cd4b2e7b43e3895708926; + 1 encin + !61bfce8ba6e1b8c43b1cb6d42068b9a05732db6d7ab6c6bf0eb5646952dfdedaaab8c098b68ff62bd5afefcdb8b9efa817db9a2c73287c80770cccaa904f978608cd084d64dfcff4694cba64d197f4d51d7bf727cb55c6218470ac2c4cf0097dc5af9e1aba3853f935c90627e8520c6c2dab7f49f8614c6aa2d732ae04483446925d6a24ae292ec5a5b5c614f3570f2cd4b2e7b43e3895708926 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !7a2ede61e2fde8e1745851495017a161de04c347153f7ec572b4c60b22e1c10509a5767dd664f2df7d1d8e86f2262396d0fcb7e7339fe53f4c80882b4d2b43add0736694a7829bda043cc744f236f1f5b9f61f7fd77b38819257f1e7c632d84e23d40e921b65b04439afa64c053cc9a3dc42e95c089495b571bc7493ded7c9c6be32e1a5288d3ca20861c9bbaf1261909acfa6a66a1ee3dce159de; + 1 encin + !7a2ede61e2fde8e1745851495017a161de04c347153f7ec572b4c60b22e1c10509a5767dd664f2df7d1d8e86f2262396d0fcb7e7339fe53f4c80882b4d2b43add0736694a7829bda043cc744f236f1f5b9f61f7fd77b38819257f1e7c632d84e23d40e921b65b04439afa64c053cc9a3dc42e95c089495b571bc7493ded7c9c6be32e1a5288d3ca20861c9bbaf1261909acfa6a66a1ee3dce159de + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !d7caceec9406ce456325d8c2d72a56fc3c648e949eb2e69f815f6a4607a3dcc2d980d15672325f3aa714f7c3560aaa7076912cb9a9073d382730a4d2f08636f5c8a75252a4517355845f2bf956c1768694aa523b94440e01d526ce4f1d289c413f02ad16b5de0de49602ec3469edd588c06a8f40eb25ef5440ec4ea5d9ff3c35aa75468b05026b6850895e64fc6ff718ef4f6988972d978a679c7550; + 1 encin + !d7caceec9406ce456325d8c2d72a56fc3c648e949eb2e69f815f6a4607a3dcc2d980d15672325f3aa714f7c3560aaa7076912cb9a9073d382730a4d2f08636f5c8a75252a4517355845f2bf956c1768694aa523b94440e01d526ce4f1d289c413f02ad16b5de0de49602ec3469edd588c06a8f40eb25ef5440ec4ea5d9ff3c35aa75468b05026b6850895e64fc6ff718ef4f6988972d978a679c7550 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !2d651c94a7ed4d02d256b4039661e0dc59e6a15b582e2aaa4931ad644d9765ffea394266410efa7502bd4a86a9f45f1c337c18510bbfa4f5bd40d9d1d0ba37f216d0980647266f382e448f8911c02f4840f9aa5c7dfc1a60bfbe90822358d46b7ef3bce139112101dc85ad5153233429cee3cfb9399c028400bdf7010083ab5defc0a2e3be79f95f76995e5e466641743d410533a5ca6e2b398da11bb5; + 1 encin + !2d651c94a7ed4d02d256b4039661e0dc59e6a15b582e2aaa4931ad644d9765ffea394266410efa7502bd4a86a9f45f1c337c18510bbfa4f5bd40d9d1d0ba37f216d0980647266f382e448f8911c02f4840f9aa5c7dfc1a60bfbe90822358d46b7ef3bce139112101dc85ad5153233429cee3cfb9399c028400bdf7010083ab5defc0a2e3be79f95f76995e5e466641743d410533a5ca6e2b398da11bb5 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !0976f8129dd12c3f8dea983af67ae3aadfbdafd1fc4070454e5f48a69491e6fb34abb1aba463547b8ac75b036c8acbf71760d560453da42b4d6eb9d648a9e3305329fcf206291d3ce91da0a00f3d7d9f56f5e828b5403c1ea451d731c8751a07943cccade4d76bc4ad467a08a35f3d5f2412ea96ff4affad059e5b13d8aac4160e40b13406cdf643df1a70d9f9a564c3c104f66b1e341cb70b0e9768c903; + 1 encin + !0976f8129dd12c3f8dea983af67ae3aadfbdafd1fc4070454e5f48a69491e6fb34abb1aba463547b8ac75b036c8acbf71760d560453da42b4d6eb9d648a9e3305329fcf206291d3ce91da0a00f3d7d9f56f5e828b5403c1ea451d731c8751a07943cccade4d76bc4ad467a08a35f3d5f2412ea96ff4affad059e5b13d8aac4160e40b13406cdf643df1a70d9f9a564c3c104f66b1e341cb70b0e9768c903 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !976ef493928a00c12152f71ee7f0471090b83d2443131f0f5906e825dd28a42601dee4a590eb773e9accb1c9651fe70d12ebad1ed6e48534a2c532dbb8da858ad46612cec9a3a1189643f3321b7cb1b450b1a2b536d4b80be7809693ebbd3a621e13d3fa245fe32cb36b38e42f83db93b5318caadc6ca85f74f4a9f15b01e1524a2f63e713b28fa8fd9f42ae5b46c1734f6d93b3bf7845e44275af3051d8d4; + 1 encin + !976ef493928a00c12152f71ee7f0471090b83d2443131f0f5906e825dd28a42601dee4a590eb773e9accb1c9651fe70d12ebad1ed6e48534a2c532dbb8da858ad46612cec9a3a1189643f3321b7cb1b450b1a2b536d4b80be7809693ebbd3a621e13d3fa245fe32cb36b38e42f83db93b5318caadc6ca85f74f4a9f15b01e1524a2f63e713b28fa8fd9f42ae5b46c1734f6d93b3bf7845e44275af3051d8d4 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !8ac2ef579459419d2d55a05cc2b1b38f2c214f917b6803c414487e168415010103ec052e2d34110a9400993c3db3ae2811153428a4bd102f7002d253db77da3bb8f19dd64cb2c9889e777c0e91d6935815f46f69ca20c7ce9ce987f1a8f53d2d827dd7966c7e3f678e6ae080db3b3139c6ac83f821d17c45e4a385e47b5257387f2bdcc8d95cc465b240fb0149499035a5dd93f428eb8a42793fbb9e4ae88d67; + 1 encin + !8ac2ef579459419d2d55a05cc2b1b38f2c214f917b6803c414487e168415010103ec052e2d34110a9400993c3db3ae2811153428a4bd102f7002d253db77da3bb8f19dd64cb2c9889e777c0e91d6935815f46f69ca20c7ce9ce987f1a8f53d2d827dd7966c7e3f678e6ae080db3b3139c6ac83f821d17c45e4a385e47b5257387f2bdcc8d95cc465b240fb0149499035a5dd93f428eb8a42793fbb9e4ae88d67 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !930bc40db6824175fec1a6ac155183afb11bdbb2ff1c32a4425f7667171e1271ac4d436c10c839bc85a1172a4095501e62a53a0c5933a7fee45b5415dc26bb68fbb4768b4bee89b14bce1459a8f46d84d2ac9f5fa4b43f66a719a1d74837da85de979ea0ff975441f8ec34225a17a4a326fc6273614d4643c672da8d8a22a8cac265adaf916b7bd11f30e8282a268e4abb16fed4677dc963bcf5e360b03d862adb; + 1 encin + !930bc40db6824175fec1a6ac155183afb11bdbb2ff1c32a4425f7667171e1271ac4d436c10c839bc85a1172a4095501e62a53a0c5933a7fee45b5415dc26bb68fbb4768b4bee89b14bce1459a8f46d84d2ac9f5fa4b43f66a719a1d74837da85de979ea0ff975441f8ec34225a17a4a326fc6273614d4643c672da8d8a22a8cac265adaf916b7bd11f30e8282a268e4abb16fed4677dc963bcf5e360b03d862adb + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !d18d19dc4a66a1994dd58caa14ae538160d710af67597889f5c5d7d1a6c2a7d076ac4fb070200e0e1deecd1d0f1189dea5f2d542b8375317fe734a52cda4b40b414816201de095841a89db465cbbc198b4e3d2239afd4b07add7cf5e4ba5c934c65a485e8c2d682546d28f435234b6208ad0fe9f02192bb9b2c1b73e20716e39525f586f12f5f9fe8e0c58381b8f26651794a3156261b94795e98cb494b8402f0034; + 1 encin + !d18d19dc4a66a1994dd58caa14ae538160d710af67597889f5c5d7d1a6c2a7d076ac4fb070200e0e1deecd1d0f1189dea5f2d542b8375317fe734a52cda4b40b414816201de095841a89db465cbbc198b4e3d2239afd4b07add7cf5e4ba5c934c65a485e8c2d682546d28f435234b6208ad0fe9f02192bb9b2c1b73e20716e39525f586f12f5f9fe8e0c58381b8f26651794a3156261b94795e98cb494b8402f0034 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !8f6ed81d1b3df9d227a6c0d1d7fcbd0dac459e92a6c8e7ede2f3f58767722026d6308129bb753818e0ec585a5e3e56dab714ae1aae8b7bc2e0625e0ef8294d1d7a048bd25bef0dfe581c08356ea0198430698d2499f0b833f6e3d22d8cdf7abfb3178365c082d274014fe6afcdd0e8e024c89f75f9ee6a12357684612d557ed1544931a04ab1ebedc73450f9126f805f62660e46298a930af945d5e737365b8eed349e; + 1 encin + !8f6ed81d1b3df9d227a6c0d1d7fcbd0dac459e92a6c8e7ede2f3f58767722026d6308129bb753818e0ec585a5e3e56dab714ae1aae8b7bc2e0625e0ef8294d1d7a048bd25bef0dfe581c08356ea0198430698d2499f0b833f6e3d22d8cdf7abfb3178365c082d274014fe6afcdd0e8e024c89f75f9ee6a12357684612d557ed1544931a04ab1ebedc73450f9126f805f62660e46298a930af945d5e737365b8eed349e + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !394cc6c775f2c9d0f178ccab1f32a9beb6f19f33bc82aaf734f30b4499a70ea6cad8e3c074766a466c7f541a460453b99bde9a5119a83f6360aeced4dfea527178008c38ea08a27352f731e8e5f2698e1fc030e26378bb597a6cb1153e2b2c6c2ed827efa4d941e3caecbbbbc16036cf614f0f8ebb1d355003f854c90b8e1ad325ba6269bbfc1807a8e067a546523c6e0b0a034affc0a4c8f8d10bd9d7f926e6c383c6d8; + 1 encin + !394cc6c775f2c9d0f178ccab1f32a9beb6f19f33bc82aaf734f30b4499a70ea6cad8e3c074766a466c7f541a460453b99bde9a5119a83f6360aeced4dfea527178008c38ea08a27352f731e8e5f2698e1fc030e26378bb597a6cb1153e2b2c6c2ed827efa4d941e3caecbbbbc16036cf614f0f8ebb1d355003f854c90b8e1ad325ba6269bbfc1807a8e067a546523c6e0b0a034affc0a4c8f8d10bd9d7f926e6c383c6d8 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !503e331b0acc00f2fc35dd9d8f145b2eb5c2e1ed3204dfdcc0a624eabd913ffac3dd87cd30a972df89ec6ccf43cfaadf5deca3c0448d510600068033201099f1c8e3f08eadb73ee63bc06e63e7df525458abf9b54113aebf3825127b1a0bdc12c96665e1cdd1ef08a66d147e727dadf9172076215a53f21b1201112cf2b57b33e10c8bed75afa629afe4e9f6510c77b853d64c218af57edcb68281594afc61f926a659bafd; + 1 encin + !503e331b0acc00f2fc35dd9d8f145b2eb5c2e1ed3204dfdcc0a624eabd913ffac3dd87cd30a972df89ec6ccf43cfaadf5deca3c0448d510600068033201099f1c8e3f08eadb73ee63bc06e63e7df525458abf9b54113aebf3825127b1a0bdc12c96665e1cdd1ef08a66d147e727dadf9172076215a53f21b1201112cf2b57b33e10c8bed75afa629afe4e9f6510c77b853d64c218af57edcb68281594afc61f926a659bafd + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !794a8b86dcef1e3409d82b9e460403e67a7719c399dbab43e23610347bb0c275bf7ca6babe885231398c04b6492b6bec7ec678f6ca6ab5b23e1466dfd253bf270f08cc6a467cc4bedec91bce70e95b87be5509501260ab8edd3c1a2ccac8722e3cfc6687c962aa9ccb1f7fca535d38b0c930ebe9a63407a0308aa6198b90fa0ed3d4918f962be7391c7ab9444beb642f6fb06f3a2df0bba77fdb29bd7ac2420aeeb9bcfecc0a; + 1 encin + !794a8b86dcef1e3409d82b9e460403e67a7719c399dbab43e23610347bb0c275bf7ca6babe885231398c04b6492b6bec7ec678f6ca6ab5b23e1466dfd253bf270f08cc6a467cc4bedec91bce70e95b87be5509501260ab8edd3c1a2ccac8722e3cfc6687c962aa9ccb1f7fca535d38b0c930ebe9a63407a0308aa6198b90fa0ed3d4918f962be7391c7ab9444beb642f6fb06f3a2df0bba77fdb29bd7ac2420aeeb9bcfecc0a + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !7e3f2d928ed2d2e4e1cf6c534b57d52235d437d9377b00760467bba709f113b0b562e52c1a077ffeff2622ea38f7f787ac7dbbc4b29aa7fc5d9e8d58977a2bf94172e00b3fc7f63875f08f1e5c67e2737259faf012e4362177affa06f24859b476a8ab55798bba9702980f57d78b0f1306b4dd7be7118ace1bbbe1a23340226fb91499d938f10573d720c4c72e3d116dd6d72fd7920a7af69b1f600b5cfbba5538645ce50aae1e; + 1 encin + !7e3f2d928ed2d2e4e1cf6c534b57d52235d437d9377b00760467bba709f113b0b562e52c1a077ffeff2622ea38f7f787ac7dbbc4b29aa7fc5d9e8d58977a2bf94172e00b3fc7f63875f08f1e5c67e2737259faf012e4362177affa06f24859b476a8ab55798bba9702980f57d78b0f1306b4dd7be7118ace1bbbe1a23340226fb91499d938f10573d720c4c72e3d116dd6d72fd7920a7af69b1f600b5cfbba5538645ce50aae1e + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !30ad0da493ebd63c8fc042db11b23a2d203bae61cffd6194337d4ad838589b693211b89a25f845d15c8dcbe61442b1a2f44fd4ec5a6bfdb43ecd0bcc5fb8da34d68f2c20ef1433422d658c917a25fc440b4c2b151a62b949e184f58f501ff0c6d77335ac8db3127e38313fe96c4b109e9c05233e300fd9c9fdf45b700e8ee0553c55bdaedc0d43856cbf8679f53525e722f3e3435eb4f154137195c27590c8e6eb7455f64d11c546; + 1 encin + !30ad0da493ebd63c8fc042db11b23a2d203bae61cffd6194337d4ad838589b693211b89a25f845d15c8dcbe61442b1a2f44fd4ec5a6bfdb43ecd0bcc5fb8da34d68f2c20ef1433422d658c917a25fc440b4c2b151a62b949e184f58f501ff0c6d77335ac8db3127e38313fe96c4b109e9c05233e300fd9c9fdf45b700e8ee0553c55bdaedc0d43856cbf8679f53525e722f3e3435eb4f154137195c27590c8e6eb7455f64d11c546 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !e3177d315779299d808bd2c34eca11dfb2efd5f0c11c354d59b9bc2d29eab2767cce65797e2d2cd94328dda9e700eaadd66dd23a7bcce40c978e5fa0081056d393cd1612ae248ad340dd33e0605cfba4c899dd7f78e4fdeec95d53b0fdac26217fe1a6f0104ddc0f2929feb54db9b07a6b8801c99978a6db91cbefb9f3001bef853424018d5911c2c22fa4d2939b5ead3457274332a2b223f2c8eccaec8a632ada3bb746e55adbf77f; + 1 encin + !e3177d315779299d808bd2c34eca11dfb2efd5f0c11c354d59b9bc2d29eab2767cce65797e2d2cd94328dda9e700eaadd66dd23a7bcce40c978e5fa0081056d393cd1612ae248ad340dd33e0605cfba4c899dd7f78e4fdeec95d53b0fdac26217fe1a6f0104ddc0f2929feb54db9b07a6b8801c99978a6db91cbefb9f3001bef853424018d5911c2c22fa4d2939b5ead3457274332a2b223f2c8eccaec8a632ada3bb746e55adbf77f + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !584cee6d2cbdcde833d8d786bc1939a8a3da9ee634703bf690532eff66665478b7c2478a2df42e0326cca1fd6f891c81a261489e8c37471f031874970bd556c6747626b6fdd05d4c4f4531da9a938030d38626f661ccec2faea96affc1273fc48245cd1828b346bb6ea7e107026171c06db53e08a66286a9b945a7142236ea16133e217aa104a2920eb401f4404d6dcb9589d694b0d6ef8c61e9a2a9f584a4a3af56449f2aaea8d06534; + 1 encin + !584cee6d2cbdcde833d8d786bc1939a8a3da9ee634703bf690532eff66665478b7c2478a2df42e0326cca1fd6f891c81a261489e8c37471f031874970bd556c6747626b6fdd05d4c4f4531da9a938030d38626f661ccec2faea96affc1273fc48245cd1828b346bb6ea7e107026171c06db53e08a66286a9b945a7142236ea16133e217aa104a2920eb401f4404d6dcb9589d694b0d6ef8c61e9a2a9f584a4a3af56449f2aaea8d06534 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !1e11e7edfa768149f9969b5863f1a2240c90f528593d0bd47ca6636a564aa7218cefe194a1d57c557b1429779336b8bd2cacfe63e15b4e8161f584ab860069ff5d3bccfd6b3c8be24789f2988fe8c1bd23fdf2a870fc502e8e25d2fd90ab4c95dcd929a050d99d3e2ef0a4e0fd8e42b5a73300becd80d6571ba8ebfcef4aa5a152843ac09f6c102b765c7e422f619563b18c5fc18bbdb279c6463b84116373adaf8c776c68b85221fe0abd; + 1 encin + !1e11e7edfa768149f9969b5863f1a2240c90f528593d0bd47ca6636a564aa7218cefe194a1d57c557b1429779336b8bd2cacfe63e15b4e8161f584ab860069ff5d3bccfd6b3c8be24789f2988fe8c1bd23fdf2a870fc502e8e25d2fd90ab4c95dcd929a050d99d3e2ef0a4e0fd8e42b5a73300becd80d6571ba8ebfcef4aa5a152843ac09f6c102b765c7e422f619563b18c5fc18bbdb279c6463b84116373adaf8c776c68b85221fe0abd + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !2219a18f102a95f8e186531b43cfc32c33b502b82f8327224423866a8f107e14dbac95eaffdac32deb461ca94008c4285478d2e294972cedc61af9a02c6a650261b3fe05d96e6e5de9f88c996a9fdba504b762e5e662f59b918a054bd44dcda3c093b49726d361a7c9efa020f7c5548ee1f671e17d1add2a5f802b2b661e8b4ac6ff9c4463776a8cc1d0db851dee9f53ac32c54f4f93641be604bb671db1be856fa228a41c6cd3bbb947cfb0; + 1 encin + !2219a18f102a95f8e186531b43cfc32c33b502b82f8327224423866a8f107e14dbac95eaffdac32deb461ca94008c4285478d2e294972cedc61af9a02c6a650261b3fe05d96e6e5de9f88c996a9fdba504b762e5e662f59b918a054bd44dcda3c093b49726d361a7c9efa020f7c5548ee1f671e17d1add2a5f802b2b661e8b4ac6ff9c4463776a8cc1d0db851dee9f53ac32c54f4f93641be604bb671db1be856fa228a41c6cd3bbb947cfb0 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !ae249337097bb506049440d2c68004517c467292742e4153c5f76bbfaf608b092e681b8eb012163166ef2be88b704d859ec57405bb3a3b15bcd9167d9c007f3865250cc614b8dda4864868243303c31749f2a4f32f967f95c28b4b80ff9f089d670af32b62641dd213d6c3d9553d16a1e398985a304c0ce4b535b8d2ec7b8328039883d55afe35cfb3cde5b59d21b5c85d14de7498404b25470f931e77bfa9658d92171cc3ea7b31406b1d2b03; + 1 encin + !ae249337097bb506049440d2c68004517c467292742e4153c5f76bbfaf608b092e681b8eb012163166ef2be88b704d859ec57405bb3a3b15bcd9167d9c007f3865250cc614b8dda4864868243303c31749f2a4f32f967f95c28b4b80ff9f089d670af32b62641dd213d6c3d9553d16a1e398985a304c0ce4b535b8d2ec7b8328039883d55afe35cfb3cde5b59d21b5c85d14de7498404b25470f931e77bfa9658d92171cc3ea7b31406b1d2b03 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !d3001684fb506a9e086187174ac74f28c7d78412658329bde0ffb2c70b47fbb071ff32f79b1386e88f6050969da71e1f44524a09c601d52dac9884e21869c7321a3a1a70e714723024f368889a7eb52ec19b4356962a9e38f7b8a03d092b218e2b175d691555b300a02ec585f5504b8d68a908ff6c62e38c5dd590fc783e751fe425e9af00b0c743cb48be38de4300b42609d2bbb3956ddd771865ed365790d5455253799a2df6a08e0bfee50595; + 1 encin + !d3001684fb506a9e086187174ac74f28c7d78412658329bde0ffb2c70b47fbb071ff32f79b1386e88f6050969da71e1f44524a09c601d52dac9884e21869c7321a3a1a70e714723024f368889a7eb52ec19b4356962a9e38f7b8a03d092b218e2b175d691555b300a02ec585f5504b8d68a908ff6c62e38c5dd590fc783e751fe425e9af00b0c743cb48be38de4300b42609d2bbb3956ddd771865ed365790d5455253799a2df6a08e0bfee50595 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !4f78ed9d5c890216da2099901998bf64bd90a0426a3d31dd0e8789a37659d9ddfd1154f7407c3cb6a2000a87fe1fb050f84715bb5dcd02e61e51cfbe67d95504b9c83a3e988cb77408d468ffb869aec42291d414be67049af20b9e10c59ef5ef39f3945c146c4ab941cec0043afb9460878a2c513080df8c757fbccb2269512c08136dcda6cfe8b0a6dbd3fe2754cf4551cf8a5a95a6215e677bded62ea4487ac6304b028154eba7e98e024112f28a; + 1 encin + !4f78ed9d5c890216da2099901998bf64bd90a0426a3d31dd0e8789a37659d9ddfd1154f7407c3cb6a2000a87fe1fb050f84715bb5dcd02e61e51cfbe67d95504b9c83a3e988cb77408d468ffb869aec42291d414be67049af20b9e10c59ef5ef39f3945c146c4ab941cec0043afb9460878a2c513080df8c757fbccb2269512c08136dcda6cfe8b0a6dbd3fe2754cf4551cf8a5a95a6215e677bded62ea4487ac6304b028154eba7e98e024112f28a + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !49a411020d58068cf8d35e23b4155598b348a9f91b92dfd0e831f7c040b7d4b696fbc5b4ca0e40cf9c57843dd440aa6c85e84e7c0091443baa205bfc668883380124d84fb042b92fd982099567b2f6048a0ec215975114990b3c72a09d684a21c006bae1dff5010d84330203c64f2afe2127ecac142760f3732f382658bb6b3ee3e58afbe7f231851ffbbc5220906a8acbb7ff61bb797996b590aeaeea7f1cd5769703fc182959c6082fbf1678f314ec; + 1 encin + !49a411020d58068cf8d35e23b4155598b348a9f91b92dfd0e831f7c040b7d4b696fbc5b4ca0e40cf9c57843dd440aa6c85e84e7c0091443baa205bfc668883380124d84fb042b92fd982099567b2f6048a0ec215975114990b3c72a09d684a21c006bae1dff5010d84330203c64f2afe2127ecac142760f3732f382658bb6b3ee3e58afbe7f231851ffbbc5220906a8acbb7ff61bb797996b590aeaeea7f1cd5769703fc182959c6082fbf1678f314ec + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !e439fc3b2bc7687dbf83956f3b2174e9c3b137e33d42661129c9fd00b7b6514608b03b68361d950b8c49f493eba04fa2c3f1b66bc4484ad07b81f65f7755609755adbeec725db22c9115cd1d2010eacb784a5b88b06a1e0e7c0ad63fdbbedbbd407d94df8740b014706acd1a27effd43581e9b218837512feb0f60141371ab6910098ff0e457c5df82779947197e46213e8b5973d4cef2e41b326a203f4bba820c18127e7aabe7abf06a12b8f31660c223; + 1 encin + !e439fc3b2bc7687dbf83956f3b2174e9c3b137e33d42661129c9fd00b7b6514608b03b68361d950b8c49f493eba04fa2c3f1b66bc4484ad07b81f65f7755609755adbeec725db22c9115cd1d2010eacb784a5b88b06a1e0e7c0ad63fdbbedbbd407d94df8740b014706acd1a27effd43581e9b218837512feb0f60141371ab6910098ff0e457c5df82779947197e46213e8b5973d4cef2e41b326a203f4bba820c18127e7aabe7abf06a12b8f31660c223 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !13e0220a51a95659729e38e45e6f189923e230e75ba60282ee7c1064bcf5fdabc446a44f16e36a1d3d0dee3933359f8c2ec41595494918e8ac5a69b131775febb8d2d0510e2a78932f084eb875ba8a22da5033123debd857a16811a2fe87997d59eebd6832371e7f893bc182b019fb750bca23ee7c80335e90f72ce6bdf1bdaf2b4597684bc22c43be120452f4cd8f8f1c9e5ca0a8fbd434ed889ba8fea8ec5a3e101b0185a661e261620166b534b59dd3b9; + 1 encin + !13e0220a51a95659729e38e45e6f189923e230e75ba60282ee7c1064bcf5fdabc446a44f16e36a1d3d0dee3933359f8c2ec41595494918e8ac5a69b131775febb8d2d0510e2a78932f084eb875ba8a22da5033123debd857a16811a2fe87997d59eebd6832371e7f893bc182b019fb750bca23ee7c80335e90f72ce6bdf1bdaf2b4597684bc22c43be120452f4cd8f8f1c9e5ca0a8fbd434ed889ba8fea8ec5a3e101b0185a661e261620166b534b59dd3b9 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !1bb9929153b43eba74014f0a3e9232b6b09c3264f7b51321dfdef0d31a83ac116f742fd8af086514605e91e04eac8a19463f156863ba14440c4c85aedfc741a1d6208bf6fb8c40e63da89216a7548b53def99de221ed4cb5bf0a9bc450763c21ed9345acc1a4f64b73a2ae652508f5ce7b8dd3f6e9df8674e6c987f8436a2c2418425008b1b75995cba10d2e0a871c694de76bbb0e21c1c24d1503fbb46e1e693ddb21c5876fbaca82e479f660fe67a7686a7a; + 1 encin + !1bb9929153b43eba74014f0a3e9232b6b09c3264f7b51321dfdef0d31a83ac116f742fd8af086514605e91e04eac8a19463f156863ba14440c4c85aedfc741a1d6208bf6fb8c40e63da89216a7548b53def99de221ed4cb5bf0a9bc450763c21ed9345acc1a4f64b73a2ae652508f5ce7b8dd3f6e9df8674e6c987f8436a2c2418425008b1b75995cba10d2e0a871c694de76bbb0e21c1c24d1503fbb46e1e693ddb21c5876fbaca82e479f660fe67a7686a7a + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !d0302df57b17f2f3188b63b200d2e9ec3915ce60514c9b6941f9a29031cd1f68e2c07af6400bda66945b1753a33b26ff6c2dabe6ffdc1fa915b3422ef0c1444aa71522d656dc225981f345771dec1dfd304733be682194b89d3c90ae3a490a309709909998a1edb6dd6f65f9e7ee49bb80b0d5ed399dab5a18573fa6df6e986ea4a53fd062638472f9162e2abe43de24fbb1ec1461ff86a498ee0877100c907b0158638ea75fae34ddaa6ce9963636af55f7e0c0; + 1 encin + !d0302df57b17f2f3188b63b200d2e9ec3915ce60514c9b6941f9a29031cd1f68e2c07af6400bda66945b1753a33b26ff6c2dabe6ffdc1fa915b3422ef0c1444aa71522d656dc225981f345771dec1dfd304733be682194b89d3c90ae3a490a309709909998a1edb6dd6f65f9e7ee49bb80b0d5ed399dab5a18573fa6df6e986ea4a53fd062638472f9162e2abe43de24fbb1ec1461ff86a498ee0877100c907b0158638ea75fae34ddaa6ce9963636af55f7e0c0 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !35b23065c41551d21bca567870fcac995aabf14c1f9f32d6638cb75964c5924e03902a5ca638abe78af8af70b448c113498a000289efd5e0a9c5b5ede89445e42dd3140fe0d73089f3d8223d9d662111e67809a539f034a07971ab886121a8bddcd45bdafdc249b15b1556ea2bdd0221a75fb575a76a56ff2557bc6b1773b4ac0317f141f35dbd032e32236039a3d881e15951e3e54c4bcfd364a9b9696f281fc750f1153c3fa2e3b87fe0cc3a73249daab30e15c6; + 1 encin + !35b23065c41551d21bca567870fcac995aabf14c1f9f32d6638cb75964c5924e03902a5ca638abe78af8af70b448c113498a000289efd5e0a9c5b5ede89445e42dd3140fe0d73089f3d8223d9d662111e67809a539f034a07971ab886121a8bddcd45bdafdc249b15b1556ea2bdd0221a75fb575a76a56ff2557bc6b1773b4ac0317f141f35dbd032e32236039a3d881e15951e3e54c4bcfd364a9b9696f281fc750f1153c3fa2e3b87fe0cc3a73249daab30e15c6 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !6aa7cbfd617d2dc854b2e65e4fe5077fb991987bbe6a2e274f8dfe06bc62b2c9539bddb7db1a6a76adde9b78d6b73776700e30dfa42cd4d96dfa359d8857ae3a952ceab3bc7b266d33dff9a75da46aa4ade9031eac27428f49dfd30dfe4894f1b7d474e50dbdb06987aa29b280eb0bca726e1246e8fbaa0d623cc4df1bd9d551e67d0aea9337c63446124ddafe3f942f5b05897f49c902a8f67c31a0a51455c6b2b7e845c68f2f82f6c86e6f5bbc37f78622bb067693; + 1 encin + !6aa7cbfd617d2dc854b2e65e4fe5077fb991987bbe6a2e274f8dfe06bc62b2c9539bddb7db1a6a76adde9b78d6b73776700e30dfa42cd4d96dfa359d8857ae3a952ceab3bc7b266d33dff9a75da46aa4ade9031eac27428f49dfd30dfe4894f1b7d474e50dbdb06987aa29b280eb0bca726e1246e8fbaa0d623cc4df1bd9d551e67d0aea9337c63446124ddafe3f942f5b05897f49c902a8f67c31a0a51455c6b2b7e845c68f2f82f6c86e6f5bbc37f78622bb067693 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !3d4f01c3f52db6eadd975fb99c579dd8ed921c67d577e3ad51f2eee94c643d31cd4e27636234478d2aa718b85656c9ffa3f8a4874e465f3b8007776b0124949eaa7a2f4199d9203cfc46e154bf62c36fa8f31d07efd9fe2333d42fcba6438a2022ee8e37b67489c9595efc02c7488eb05c9d691d2da641043610d800ffcf497ba5dfb1be9263fa7b05dd27ded3bdf7a463443c5cc11d2506f2e221cc13446a04e5e8a42941e55a52c135fb139b97cdd9dab1b22f9f4c40; + 1 encin + !3d4f01c3f52db6eadd975fb99c579dd8ed921c67d577e3ad51f2eee94c643d31cd4e27636234478d2aa718b85656c9ffa3f8a4874e465f3b8007776b0124949eaa7a2f4199d9203cfc46e154bf62c36fa8f31d07efd9fe2333d42fcba6438a2022ee8e37b67489c9595efc02c7488eb05c9d691d2da641043610d800ffcf497ba5dfb1be9263fa7b05dd27ded3bdf7a463443c5cc11d2506f2e221cc13446a04e5e8a42941e55a52c135fb139b97cdd9dab1b22f9f4c40 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !82ba5683a6b7b686523481e90adce794dbe49e5e80217d798272ae66f5827fe7bd0900d6bd0cb3101916a4284420298e8826afd5ec0e9a9c78ead1f48c8007f8cb4e20364adde10ec5f843e92e717106498cbc7ed7741f5e0db2a9ef48f5c07db71d4f31be38bcb1532c16fdbf3001300861ee0d9b2b06be8779cf13d8436b28af7e87ba69634bbdbccbee94201c93273089dd9fb8a6acb7fa6f1238effce72ee5130ac3930d947ba7f8bc4cd91e129a7cbafbeb4c32d01a; + 1 encin + !82ba5683a6b7b686523481e90adce794dbe49e5e80217d798272ae66f5827fe7bd0900d6bd0cb3101916a4284420298e8826afd5ec0e9a9c78ead1f48c8007f8cb4e20364adde10ec5f843e92e717106498cbc7ed7741f5e0db2a9ef48f5c07db71d4f31be38bcb1532c16fdbf3001300861ee0d9b2b06be8779cf13d8436b28af7e87ba69634bbdbccbee94201c93273089dd9fb8a6acb7fa6f1238effce72ee5130ac3930d947ba7f8bc4cd91e129a7cbafbeb4c32d01a + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !11b58ac73a99abf28873aaa1f820ba419659265dd34df84986f5437b6a9a353438e4f4d0134bad123e648ee670ee7eccb75c4a47f768006645020483a8e4e3f538b761a427763a2389d6b00b4b75226b21ee8b22d799c15df53fc786000a2e42d687b84419d4cd709b82c1b79313c642686a221194cdbdce50f52a678273182309dc50ff1b71d73c398937a090ac47e36d1abbe0ab5ee526a19e64c34ca1b54d67b23b002ba33ae1538f7a4cfdeb897bd930fc0cae0bf99470; + 1 encin + !11b58ac73a99abf28873aaa1f820ba419659265dd34df84986f5437b6a9a353438e4f4d0134bad123e648ee670ee7eccb75c4a47f768006645020483a8e4e3f538b761a427763a2389d6b00b4b75226b21ee8b22d799c15df53fc786000a2e42d687b84419d4cd709b82c1b79313c642686a221194cdbdce50f52a678273182309dc50ff1b71d73c398937a090ac47e36d1abbe0ab5ee526a19e64c34ca1b54d67b23b002ba33ae1538f7a4cfdeb897bd930fc0cae0bf99470 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !a6e6e7cd064b56599f350078aa8009f5c496f976525b4543ce8b5aed07b97b2d9ed2a6ec53b175740bc0a709b21b90a775ac216c925c4d2c8662bbf6839bafda30657c11e688cafd61779fbf345bd662f0253cd292a65bfa69d235c2ddd31f82bd2b72c354ba2345848e417cbdcb15b8bd18dac3f490c2bbca39a5fe839a22a5bc9d588971b7c6765b6367bae62fcad0e88246e515d47f3dfd706f5d2b2ad2fd678b3ba13c589ba7a9be4d140f77553ba09dc01e7b6eab91bbe8; + 1 encin + !a6e6e7cd064b56599f350078aa8009f5c496f976525b4543ce8b5aed07b97b2d9ed2a6ec53b175740bc0a709b21b90a775ac216c925c4d2c8662bbf6839bafda30657c11e688cafd61779fbf345bd662f0253cd292a65bfa69d235c2ddd31f82bd2b72c354ba2345848e417cbdcb15b8bd18dac3f490c2bbca39a5fe839a22a5bc9d588971b7c6765b6367bae62fcad0e88246e515d47f3dfd706f5d2b2ad2fd678b3ba13c589ba7a9be4d140f77553ba09dc01e7b6eab91bbe8 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !46ae65281aa759e1e8a46a5041503ebdcea654a9e36e593f37feed335172d4bc6b6774fd4c3a0965f04a69ee35dc96eb3cd4d16f62a5996b1c9040f4b5b5bc0230286c1c9cdb9ceeb96f8297466d1bbfc8ced321e11af6a1582672c8e82f54d8474c05310f67313bbb074d78fc938e5dbf18eac026370010e20d968a15b1acc9d7d4f83c8608b84570d999c151b1ee55fbc15c7cbf8235d95bbb710cbf3d606133dfe910824606f2a1c55accb3de74b67c963cf0077d778bd68d8a; + 1 encin + !46ae65281aa759e1e8a46a5041503ebdcea654a9e36e593f37feed335172d4bc6b6774fd4c3a0965f04a69ee35dc96eb3cd4d16f62a5996b1c9040f4b5b5bc0230286c1c9cdb9ceeb96f8297466d1bbfc8ced321e11af6a1582672c8e82f54d8474c05310f67313bbb074d78fc938e5dbf18eac026370010e20d968a15b1acc9d7d4f83c8608b84570d999c151b1ee55fbc15c7cbf8235d95bbb710cbf3d606133dfe910824606f2a1c55accb3de74b67c963cf0077d778bd68d8a + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !beccfb6865d93fc79df6863c229892181813ad92f2b7ae1069a0d359cab9ceb4b2bd78b5a77cd3557b3100709aee138c152663f8f01d2872c38133a1c2ca375379eeaadf41c5291e73e580a6b43d763a6d88b14be8f6898e636f9b57f3020ab4f83c6d36397d9e1fe63f62bc37bf5449eba8621febd9679b6d314e2992d2e02580b471c12deaadcf674c05db4a7e4ae2b29827f7744e2f08d6c4a9e1732b2cf24b036bc5e9b6d39be223117c2f6a4581868cfe14bd6ff5aeb583b848; + 1 encin + !beccfb6865d93fc79df6863c229892181813ad92f2b7ae1069a0d359cab9ceb4b2bd78b5a77cd3557b3100709aee138c152663f8f01d2872c38133a1c2ca375379eeaadf41c5291e73e580a6b43d763a6d88b14be8f6898e636f9b57f3020ab4f83c6d36397d9e1fe63f62bc37bf5449eba8621febd9679b6d314e2992d2e02580b471c12deaadcf674c05db4a7e4ae2b29827f7744e2f08d6c4a9e1732b2cf24b036bc5e9b6d39be223117c2f6a4581868cfe14bd6ff5aeb583b848 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !95ca5c74d1ee38d0fb0c75d8890ab98f8b9219aaccb500ee6257efbf26412c6f8909069a43f66ed0a4452ff21a7b8b995f2bce9b636a1363f582397133900410714fec452c95f109ef21a5ac227ae925c2e78baf70ff404f2447d802fa680820fa1686f8dd8dd9c163a2451ee9587cdccb288b4cc4bbb2f9e8950f5d8744ec5d5d9f1074e33ae98359c68d0ec1fc665c7e55efd68e97283257a4f3adf46276d480b66ae8cf4dc43c1152ea087717250d888ced63f6aed38f220c86116f; + 1 encin + !95ca5c74d1ee38d0fb0c75d8890ab98f8b9219aaccb500ee6257efbf26412c6f8909069a43f66ed0a4452ff21a7b8b995f2bce9b636a1363f582397133900410714fec452c95f109ef21a5ac227ae925c2e78baf70ff404f2447d802fa680820fa1686f8dd8dd9c163a2451ee9587cdccb288b4cc4bbb2f9e8950f5d8744ec5d5d9f1074e33ae98359c68d0ec1fc665c7e55efd68e97283257a4f3adf46276d480b66ae8cf4dc43c1152ea087717250d888ced63f6aed38f220c86116f + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !0f7e862cc4b8330d51d29e038100afc60afee12133bc2005549a46bb45fab3bcdfb9bb25039a066029f4b293b6bfbe128dc9ad86e365b67cd7f6a368620a0d20f23d3fe368f5d75bc96949706674272bab848e63ce53e0d18fa1f5945a396a67af66f9399c01103fc768471f6b146ae82c1624f6aaa9a41edb27ef3feade42034c7d7278c1e3dc2fdcc9ded319dd0548a0d14dc6bc94a821c6cfa351e8b105f65208afc56fc0fdc6c1307767a3ebe6df1533b8ee53be66f39be14f311b57; + 1 encin + !0f7e862cc4b8330d51d29e038100afc60afee12133bc2005549a46bb45fab3bcdfb9bb25039a066029f4b293b6bfbe128dc9ad86e365b67cd7f6a368620a0d20f23d3fe368f5d75bc96949706674272bab848e63ce53e0d18fa1f5945a396a67af66f9399c01103fc768471f6b146ae82c1624f6aaa9a41edb27ef3feade42034c7d7278c1e3dc2fdcc9ded319dd0548a0d14dc6bc94a821c6cfa351e8b105f65208afc56fc0fdc6c1307767a3ebe6df1533b8ee53be66f39be14f311b57 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !99e949b44e949bcbaed6b35d39f94327764bcee5268f6634e4fb0c8a7b8e5c5cdfa9524a7f1fe38c59c65a281093b6ccbfe5e87b3661f5757aa50465b70d65090a1c16563ea62af36e20d1885dcbd100a9bce0f96158b0dc4d0fcbc2ab65658c6d9d2fe7b3abe31f25eba6179e6da61e82418ebd0094eb2890723671d8a43eaf3a53e5cbaadea5ba3eaa065d5b3c60939a7feb4993b0e6000cca8e125f5e40961681cd5cd12fef3c53bff1ac2872f6d835faa92de1729a1f6c9ca5ba887a10; + 1 encin + !99e949b44e949bcbaed6b35d39f94327764bcee5268f6634e4fb0c8a7b8e5c5cdfa9524a7f1fe38c59c65a281093b6ccbfe5e87b3661f5757aa50465b70d65090a1c16563ea62af36e20d1885dcbd100a9bce0f96158b0dc4d0fcbc2ab65658c6d9d2fe7b3abe31f25eba6179e6da61e82418ebd0094eb2890723671d8a43eaf3a53e5cbaadea5ba3eaa065d5b3c60939a7feb4993b0e6000cca8e125f5e40961681cd5cd12fef3c53bff1ac2872f6d835faa92de1729a1f6c9ca5ba887a10 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !28919843d9fd8f617aa87186155b9542fde5014d857c172fb3562e448780d1004f98f5cbbbf3f8d0605f7dec2e73eb48fa3c23d3f191cf82af45363dc364c7dbba5304601a91663171b46e73fb0e48f9a4ea3092091f581d642fb40fa4469227bf7956ad90363e4550df4d73945386c0278a3433d10f4f0381bab8fdc62dd911e3130ef1c68e85d551c9c51be9d8a9ab15237b4ee914e7c04bfa95a0db8db7a9947ab7244d1d371c16d0d31ec354012ae4fd6485fcb590622e2e7e6e49383f92; + 1 encin + !28919843d9fd8f617aa87186155b9542fde5014d857c172fb3562e448780d1004f98f5cbbbf3f8d0605f7dec2e73eb48fa3c23d3f191cf82af45363dc364c7dbba5304601a91663171b46e73fb0e48f9a4ea3092091f581d642fb40fa4469227bf7956ad90363e4550df4d73945386c0278a3433d10f4f0381bab8fdc62dd911e3130ef1c68e85d551c9c51be9d8a9ab15237b4ee914e7c04bfa95a0db8db7a9947ab7244d1d371c16d0d31ec354012ae4fd6485fcb590622e2e7e6e49383f92 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !f521bf3504ddd72a7deb4fbbed98dc1b2793c9eb42593f4ba38724da05396801e219f45c7ce3f98f108ee002166df1206b3625e68c0997cca92c6b46f1f4966fe4be41db01594f936680d9e28f287363969730c9d1f6bff9782a6e5a3d05f3bb756032bb668698b934b50f94b8937602dad9617aaab85799647f96ab6069b06376350b34842a4aa8a874400f61c867cfc49945e964a1fc3da8d924ec800a65b0f83dac752f23eed134fa325586a0780b0988ed827805097fb993af5f25aedfefde; + 1 encin + !f521bf3504ddd72a7deb4fbbed98dc1b2793c9eb42593f4ba38724da05396801e219f45c7ce3f98f108ee002166df1206b3625e68c0997cca92c6b46f1f4966fe4be41db01594f936680d9e28f287363969730c9d1f6bff9782a6e5a3d05f3bb756032bb668698b934b50f94b8937602dad9617aaab85799647f96ab6069b06376350b34842a4aa8a874400f61c867cfc49945e964a1fc3da8d924ec800a65b0f83dac752f23eed134fa325586a0780b0988ed827805097fb993af5f25aedfefde + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !ee153185d4b6438313761f9dd042f1b16af08876cd2ee02a6fa3a89a8910d74b8fe53bf1877483476754556e2b0b2e9f13c483c3daeb2bbb294fb00816eba4a70e93a67aea8b3297684940b5e2ef22724f427334aaa77fd32516b9b888d775178f754e6cd3228ca9190675287abe9e98061868a2ba491b97c3c561f02f1988632be69ace7c37f01f7ef74399d9ba0c9dc8679f8c82823155553c2678a64bba1433b29e6476b2e480873fa512fcfcf3d04b94901cb3e8086671c76cd4168727f0b3fe; + 1 encin + !ee153185d4b6438313761f9dd042f1b16af08876cd2ee02a6fa3a89a8910d74b8fe53bf1877483476754556e2b0b2e9f13c483c3daeb2bbb294fb00816eba4a70e93a67aea8b3297684940b5e2ef22724f427334aaa77fd32516b9b888d775178f754e6cd3228ca9190675287abe9e98061868a2ba491b97c3c561f02f1988632be69ace7c37f01f7ef74399d9ba0c9dc8679f8c82823155553c2678a64bba1433b29e6476b2e480873fa512fcfcf3d04b94901cb3e8086671c76cd4168727f0b3fe + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !188778ec99fa6b7cdbb54ba457d08ec97b0afbccfac8f3adfa0f9d16e1332ebc8a7f826f7f017f5daeff368ec657208ce487b8f42d5e0c002476d71ad4119847082770f95815b07bd779af99cb8c3ad9384fc5513688d0636481bb28306385e63b29576622c017313a7d4e5914c50494b7ebcb1ae9185900437993bf6d0a7777beabc71245301bffa5c99031c67146904c9bb1d8e674562b4cccb6e12f9671cf06efbaaa32e5cc9249ecdefbe8804b88ee86437da83d4e16facccde46a3ce04f5209c1; + 1 encin + !188778ec99fa6b7cdbb54ba457d08ec97b0afbccfac8f3adfa0f9d16e1332ebc8a7f826f7f017f5daeff368ec657208ce487b8f42d5e0c002476d71ad4119847082770f95815b07bd779af99cb8c3ad9384fc5513688d0636481bb28306385e63b29576622c017313a7d4e5914c50494b7ebcb1ae9185900437993bf6d0a7777beabc71245301bffa5c99031c67146904c9bb1d8e674562b4cccb6e12f9671cf06efbaaa32e5cc9249ecdefbe8804b88ee86437da83d4e16facccde46a3ce04f5209c1 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !544bd24c1e8c7658f1c0c577ac0f06bac973fdf62c03ea6e8d83d811a2a91f0fea27f9a3bc62dc7fb8db7f03e37e7d35ace19024207a9ebfe3e5e083fc8b0a992e779db4a3c9844f5f7494bec07450cce78224f69c87ad0dde624a286831f3d4485c397cfc8217a517c8705ebd9d758358bc100b90ab18829bd8cec8a1a49b5b91167cafc7eedeac90cd74610c2f281692ade9f3f442b1f618f8c5ba32cd0380478c05281965301c9b3185094aeeb6bfe071d177fcbaef27812aa978b4b21ae66ee546de; + 1 encin + !544bd24c1e8c7658f1c0c577ac0f06bac973fdf62c03ea6e8d83d811a2a91f0fea27f9a3bc62dc7fb8db7f03e37e7d35ace19024207a9ebfe3e5e083fc8b0a992e779db4a3c9844f5f7494bec07450cce78224f69c87ad0dde624a286831f3d4485c397cfc8217a517c8705ebd9d758358bc100b90ab18829bd8cec8a1a49b5b91167cafc7eedeac90cd74610c2f281692ade9f3f442b1f618f8c5ba32cd0380478c05281965301c9b3185094aeeb6bfe071d177fcbaef27812aa978b4b21ae66ee546de + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !956b768925fd4ddf7a44aac550783d04b77bdc179a635c40e818bf2a5c3a450846ea931ec6ed539e50a586fb52ba11611e44a44c224bcbc757d6828757101788c093dfb0cca10d451de2c87fa50662b2e5272e96f89c6d06ec967f61265d6a06381d5b7b14d669336d28b74bc41b56c03a994901cb80a47470b2fcd2e2ea883c7f83271f3f92ed0e9614d90818440cf44c13ebb84df180e7c281ed6660e6d15d12a9776052b802c3ffaa55ec456bc7fc24130ee00ca84fc140f26a47450475467271fe7270; + 1 encin + !956b768925fd4ddf7a44aac550783d04b77bdc179a635c40e818bf2a5c3a450846ea931ec6ed539e50a586fb52ba11611e44a44c224bcbc757d6828757101788c093dfb0cca10d451de2c87fa50662b2e5272e96f89c6d06ec967f61265d6a06381d5b7b14d669336d28b74bc41b56c03a994901cb80a47470b2fcd2e2ea883c7f83271f3f92ed0e9614d90818440cf44c13ebb84df180e7c281ed6660e6d15d12a9776052b802c3ffaa55ec456bc7fc24130ee00ca84fc140f26a47450475467271fe7270 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !c17edf88f543f4f8f358ef7bc02f696d4afca3b561c9fe4b0110aadcd7d4f63cda8269fd39a69f5d4237644608391df0a336ddbb1f58d7e06d6f9e104287ec366cd3a36eeecafbaf56d565cc7b637f3841db2131d3b386094b8acde4cce3a321474134440781b7c3be99cbf38dbbb149521b510034567ef4f555ca5b4d0ab5f3e9370971b785f863547f600edf2031efaa6e31d4dcafa182a122e63d42e4375bb485fefa4faf9f3796529ad44f77e901f56cfb4f9bc960fb4e210f2425d891cf468a3b297fc5; + 1 encin + !c17edf88f543f4f8f358ef7bc02f696d4afca3b561c9fe4b0110aadcd7d4f63cda8269fd39a69f5d4237644608391df0a336ddbb1f58d7e06d6f9e104287ec366cd3a36eeecafbaf56d565cc7b637f3841db2131d3b386094b8acde4cce3a321474134440781b7c3be99cbf38dbbb149521b510034567ef4f555ca5b4d0ab5f3e9370971b785f863547f600edf2031efaa6e31d4dcafa182a122e63d42e4375bb485fefa4faf9f3796529ad44f77e901f56cfb4f9bc960fb4e210f2425d891cf468a3b297fc5 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 2 copy *0 +; + 0 encout + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !a9174d658536fdd35fe59babd9680f137e003592e3f53e75019ef8147c4ed9e8a68fb125bdfe4b0fb2b583a0dd3d7523b84cc6010b7f694687a51b2aa2d2d48968ce6e07d2c8e831718892a06216a4c2e4038c1b33d1ed75ad212398846dc0119543e8ebaf236725adbef7b9444cc98af397536cd494b60d75d4fe93463f0676e8e116cd5d20d6fca42d2e1bb3e524bf50cb44fe7d141147b9239223717e2fcc7baae339cd0e23502bef19345782cffaa5b6d4cad1936f2dbee4257c07e8456a8efc84e58171e2; + 3 copy *1 +; + 1 encin + !a9174d658536fdd35fe59babd9680f137e003592e3f53e75019ef8147c4ed9e8a68fb125bdfe4b0fb2b583a0dd3d7523b84cc6010b7f694687a51b2aa2d2d48968ce6e07d2c8e831718892a06216a4c2e4038c1b33d1ed75ad212398846dc0119543e8ebaf236725adbef7b9444cc98af397536cd494b60d75d4fe93463f0676e8e116cd5d20d6fca42d2e1bb3e524bf50cb44fe7d141147b9239223717e2fcc7baae339cd0e23502bef19345782cffaa5b6d4cad1936f2dbee4257c07e8456a8efc84e58171e2 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + + ## Some buffering tests. + 2 begin/ACT *0 +; + 2 process + =XXXXXXXXXXXXXXXXX + !a9174d658536fdd35fe59babd9680f137e; + 2 process + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !003592e3f53e75019ef8147c4ed9e8a68fb125bdfe4b0fb2b583a0dd3d7523b84cc6010b7f694687a51b2aa2d2d48968ce6e07d2c8e831718892a06216a4c2e4038c1b33d1ed75ad212398846dc0119543e8ebaf23; + 2 process + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + !6725adbef7b9444cc98af397536cd494b60d75d4fe93463f0676e8e116cd5d20d6fca42d2e1bb3e524bf50cb44fe7d141147b9239223717e2fcc7baae339cd0e23502bef19345782cffaa5b6d4cad1936f2dbee4257c07e8456a8efc84e58171; + 2 process + =X + !e2; + 2 done *0 +; + + 3 begin/IACT *0 +; + 3 process + !a9174d658536fdd35fe59babd9680f137e + =XXXXXXXXXXXXXXXXX; + 3 process + !003592e3f53e75019ef8147c4ed9e8a68fb125bdfe4b0fb2b583a0dd3d7523b84cc6010b7f694687a51b2aa2d2d48968ce6e07d2c8e831718892a06216a4c2e4038c1b33d1ed75ad212398846dc0119543e8ebaf23 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 3 process + !6725adbef7b9444cc98af397536cd494b60d75d4fe93463f0676e8e116cd5d20d6fca42d2e1bb3e524bf50cb44fe7d141147b9239223717e2fcc7baae339cd0e23502bef19345782cffaa5b6d4cad1936f2dbee4257c07e8456a8efc84e58171 + =XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; + 3 process + !e2 + =X; + 3 done *0 +; + + ## Back to legible tests. + 0 prf *123 !45476fc0806aee35e864c4f18e6ba62bd3eb1b1e8bef9042b30b0f15d00c3e9f5d5904ab789d4c67eaed582473c15aa4424f11d52b21a296b36db3392e2ecbb2dc6963bafba3b23882d061f1d335e86e470e8d819591bf0c223e24b925751d04f789fc73bc55f7d2b3ed4881c625aa6321d31511b13f6d5e4ce54a; + 1 prf *123 !45476fc0806aee35e864c4f18e6ba62bd3eb1b1e8bef9042b30b0f15d00c3e9f5d5904ab789d4c67eaed582473c15aa4424f11d52b21a296b36db3392e2ecbb2dc6963bafba3b23882d061f1d335e86e470e8d819591bf0c223e24b925751d04f789fc73bc55f7d2b3ed4881c625aa6321d31511b13f6d5e4ce54a; + + ## MAC verification tests, positive and negative. + 2 copy *1 +; + 2 macin !171419608e11e7c907d493209e17f26b +; + + 2 copy *1 +; + 2 begin/ICT *0 +; + 2 process !171419608e11e7c907d493209e17f26b +; + 2 done *0 +; + + 2 copy *1 +; + 2 begin/ICT *0 +; + 2 process !171419608e11e7c970d493209e17f26b +; + 2 done *0 -; + + 2 copy *1 +; + 2 ad =spong +; + 2 macin !171419608e11e7c907d493209e17f26b -; + + ## Resume Hamburg's `Concurrence' test. + 0 macout *16 !171419608e11e7c907d493209e17f26b; + 1 macin !171419608e11e7c907d493209e17f26b +; + + ## Additional home-grown tests (see `utils/strobe'). + 0 key "=this is my super-secret key" +; + 1 key "=this is my super-secret key" +; + + 0 prf *32 !5418e0f0ee7f982bbbdc2b4bbf49425d0088abfa98ee21d8ad8a3610d15ebb68; + 1 prf *32 !5418e0f0ee7f982bbbdc2b4bbf49425d0088abfa98ee21d8ad8a3610d15ebb68; + + 0 ratchet *32 +; 1 ratchet *32 +; + + 0 macin !b2084ebdfabd50768c91eebc190132cc +; + 1 macout *16 !b2084ebdfabd50768c91eebc190132cc; +} diff --git a/utils/advmodes b/utils/advmodes index 834771ce..c4344b30 100755 --- a/utils/advmodes +++ b/utils/advmodes @@ -34,7 +34,7 @@ def poly(nbits): base = C.GF(0).setbit(nbits).setbit(0) for k in xrange(1, nbits, 2): for cc in combs(range(1, nbits), k): - p = base + sum(C.GF(0).setbit(c) for c in cc) + p = base + sum((C.GF(0).setbit(c) for c in cc), C.GF(0)) if p.irreduciblep(): POLYMAP[nbits] = p; return p raise ValueError, nbits @@ -242,8 +242,8 @@ def omac_masks(E): p = poly(8*blksz) z = Z(blksz) L = E.encrypt(z) - m0 = mul_blk_gf(L, 2, p) - m1 = mul_blk_gf(m0, 2, p) + m0 = mul_blk_gf(L, C.GF(2), p) + m1 = mul_blk_gf(m0, C.GF(2), p) return m0, m1 def dump_omac(E): @@ -586,14 +586,14 @@ def pmac2(E, m): blksz = E.__class__.blksz p = prim(8*blksz) L = E.encrypt(Z(blksz)) - o = mul_blk_gf(L, 10, p) + o = mul_blk_gf(L, C.GF(10), p) a = Z(blksz) v, tl = blocks(m, blksz) for x in v: a ^= E.encrypt(x ^ o) - o = mul_blk_gf(o, 2, p) - if len(tl) == blksz: a ^= tl ^ mul_blk_gf(o, 3, p) - else: a ^= pad10star(tl, blksz) ^ mul_blk_gf(o, 5, p) + o = mul_blk_gf(o, C.GF(2), p) + if len(tl) == blksz: a ^= tl ^ mul_blk_gf(o, C.GF(3), p) + else: a ^= pad10star(tl, blksz) ^ mul_blk_gf(o, C.GF(5), p) return E.encrypt(a) def ocb3_masks(E): @@ -740,18 +740,18 @@ def ocb2enc(E, n, h, m, tsz = None): if tsz is None: tsz = blksz p = prim(8*blksz) L = E.encrypt(n) - o = mul_blk_gf(L, 2, p) + o = mul_blk_gf(L, C.GF(2), p) a = Z(blksz) v, tl = blocks(m, blksz) y = C.WriteBuffer() for x in v: a ^= x y.put(E.encrypt(x ^ o) ^ o) - o = mul_blk_gf(o, 2, p) + o = mul_blk_gf(o, C.GF(2), p) n = len(tl) yfinal = E.encrypt(C.MP(8*n).storeb(blksz) ^ o) cfinal = tl ^ yfinal[:n] - a ^= (tl + yfinal[n:]) ^ mul_blk_gf(o, 3, p) + a ^= (tl + yfinal[n:]) ^ mul_blk_gf(o, C.GF(3), p) y.put(cfinal) t = E.encrypt(a) if h: t ^= pmac2(E, h) @@ -761,7 +761,7 @@ def ocb2dec(E, n, h, y, t): blksz = E.__class__.blksz p = prim(8*blksz) L = E.encrypt(n) - o = mul_blk_gf(L, 2, p) + o = mul_blk_gf(L, C.GF(2), p) a = Z(blksz) v, tl = blocks(y, blksz) m = C.WriteBuffer() @@ -769,11 +769,11 @@ def ocb2dec(E, n, h, y, t): u = E.encrypt(x ^ o) ^ o y.put(u) a ^= u - o = mul_blk_gf(o, 2, p) + o = mul_blk_gf(o, C.GF(2), p) n = len(tl) yfinal = E.encrypt(C.MP(8*n).storeb(blksz) ^ o) mfinal = tl ^ yfinal[:n] - a ^= (mfinal + yfinal[n:]) ^ mul_blk_gf(o, 3, p) + a ^= (mfinal + yfinal[n:]) ^ mul_blk_gf(o, C.GF(3), p) m.put(mfinal) u = E.encrypt(a) if h: u ^= pmac2(E, h) diff --git a/utils/ecptdecompress.c b/utils/ecptdecompress.c index e4386964..61fb79f2 100644 --- a/utils/ecptdecompress.c +++ b/utils/ecptdecompress.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "ec.h" #include "mp.h" @@ -129,7 +130,7 @@ int main(int argc, char *argv[]) if ((err = ec_checkinfo(&ei, &rand_global)) != 0) fprintf(stderr, "bad curve: %s\n", err); puthex("p", ei.c->f->m, 0); - if (strcmp(F_NAME(ei.c->f), "binnorm") == 0) { + if (STRCMP(F_NAME(ei.c->f), ==, "binnorm")) { fctx_binnorm *fc = (fctx_binnorm *)ei.c->f; puthex("beta", fc->ntop.r[fc->ntop.n - 1], c->f->noctets); } diff --git a/utils/gcm-ref b/utils/gcm-ref index ccbf4321..4a53737b 100755 --- a/utils/gcm-ref +++ b/utils/gcm-ref @@ -54,7 +54,7 @@ def poly(nbits): base = C.GF(0).setbit(nbits).setbit(0) for k in xrange(1, nbits, 2): for cc in combs(range(1, nbits), k): - p = base + sum(C.GF(0).setbit(c) for c in cc) + p = base + sum((C.GF(0).setbit(c) for c in cc), C.GF(0)) if p.irreduciblep(): POLYMAP[nbits] = p; return p raise ValueError, nbits diff --git a/utils/strobe b/utils/strobe new file mode 100644 index 00000000..fd0fe577 --- /dev/null +++ b/utils/strobe @@ -0,0 +1,147 @@ +#! /usr/bin/python + +import catacomb as CAT + +I = 1 +A = 2 +C = 4 +T = 8 +M = 16 +INIT = 256 + +def dump_keccak_state(what, state): + buf = CAT.ReadBuffer(state + CAT.ByteString.zero(200 - len(state))) + print ";; %s [round 0]" % what + print ";; uncomplemented state..." + for i in xrange(5): + print ";;\t%s" % " ".join(["%016x" % buf.getu64l() for j in xrange(5)]) + print + +class Strobe (object): + def __init__(me, sec = 128, _copy = None): + if _copy is None: + me.k = CAT.Keccak1600() + me.r = (1600 - 2*sec)/8 + me.f = 0 + me.p0 = 0 + buf = CAT.WriteBuffer() + buf.putu8(1).putu8(me.r) + buf.putu8(1).putu8(0) + buf.putu8(1).putu8(12*8).put('STROBEv1.0.2') + ##dump_keccak_state("init", buf.contents) + me.k.mix(buf.contents).step() + me.buf = CAT.WriteBuffer() + me.mask = me.k.extract(me.r - 2) + ##dump_keccak_state("final", me.mask) + else: + me.k = _copy.k.dup() + me.r = _copy.r + me.f = _copy.f + me.p0 = _copy.p0 + me.buf = CAT.WriteBuffer().put(_copy.buf.contents) + me.mask = me._copy.mask + + def _crank(me): + me.buf.putu8(me.p0); me.p0 = 0 + if me.buf.size == me.r - 1: me.buf.putu8(0x84) + else: me.buf.putu8(0x04).zero(me.r - me.buf.size - 1).putu8(0x80) + ##dump_keccak_state("buffer", me.buf.contents) + ##dump_keccak_state("init", me.buf.contents ^ me.k.extract(me.r)) + me.k.mix(me.buf.contents).step() + me.mask = me.k.extract(me.r - 2) + ##dump_keccak_state("final", me.mask) + me.buf = CAT.WriteBuffer() + + def process(me, op, input): + if not op&T: + opf = op + else: + if not me.f&INIT: me.f |= INIT | (op&I) + opf = op ^ (me.f&255) + me.buf.putu8(me.p0); me.p0 = me.buf.size + if me.buf.size >= me.r - 2: me._crank() + me.buf.putu8(opf) + if op&C or me.buf.size >= me.r - 2: me._crank() + + if op&(I | T) == I or op&(I | A) == 0: buf = CAT.ByteString.zero(input) + else: buf = CAT.ByteString(input) + out = CAT.WriteBuffer() + i, j, sz = 0, me.buf.size, len(buf) + + while i < sz: + spare = me.r - j - 2 + n = min(spare, sz - i); x = buf[i:i + n]; i += n + if not op&C: out.put(x); me.buf.put(x) + else: + y = x ^ me.mask[j:j + n]; out.put(y) + if op&I or not op&T: me.buf.put(y) + else: me.buf.put(x) + if n == spare: me._crank(); j = 0 + + if op&(I | A | C | T) != (I | C | T): return out.contents + elif out.contents == CAT.ByteString.zero(out.size): return me + else: raise ValueError, 'verify failed' + +st0 = Strobe() +st1 = Strobe() + +st0.process(A | M, 'testing') +st1.process(A | M, 'testing') + +print 'prf(10) -> %s' % hex(st0.process(I | A | C, 10)) +st1.process(I | A | C, 10) + +print 'ad("Hello")' +st0.process(A, 'Hello') +st1.process(A, 'Hello') + +c0 = st0.process(A | C | T, 'World'); +st1.process(I | A | C | T, c0) +print 'encout("World") -> %s' % hex(c0) + +print 'clrout("foo")' +st0.process(A | T, 'foo'); +st1.process(I | A | T, 'foo') + +print 'clrin("bar")' +st1.process(A | T, 'bar') +st0.process(I | A | T, 'bar') + +m1 = st0.process(I | A | C | T, 'baz') +st1.process(A | C | T, m1) # backwards in time +print 'encin("baz") -> %s' % hex(m1) + +for i in xrange(200): + c = st0.process(A | C | T, i*'X') + m = st1.process(I | A | C | T, c) + print " 0 encout\n\t=%s\n\t!%s;" % (i*'X', hex(c)) + print " 1 encin\n\t!%s\n\t=%s;" % (hex(c), i*'X') + +r0 = st0.process(I | A | C, 123) +r1 = st1.process(I | A | C, 123) +print 'prf(123) -> %s' % hex(r0) +assert r0 == r1 + +t = st0.process(C | T, 16) +print 'macout(16) -> %s' % hex(t) +st1.process(I | C | T, t) + +k = 'this is my super-secret key' +print 'key("%s")' % k +st0.process(A | C, k) +st1.process(A | C, k) + +r0 = st0.process(I | A | C, 32) +r1 = st1.process(I | A | C, 32) +print 'prf(32) -> %s' % hex(r0) +assert r0 == r1 + +print 'ratchet(32)' +st0.process(C, 32) +st1.process(C, 32) + +t0 = CAT.bytes('b2084ebdfabd50768c91eebc190132cc') +st0.process(I | C | T, t0) +t1 = st1.process(C | T, 16) +assert t1 == t0 +print 'macin(%s)' % hex(t0)