From 6a5717a48f957ed1a0ef9abd560a1e62592efdb2 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 6 Aug 2022 17:18:40 -0700 Subject: [PATCH] Make more libc improvements - Make memmem() faster - Make readdir() thread safe - Remove 64kb limit from mkdeps.com - Add old crypt() function from Musl - Improve new fix-third-party.py tool - Improve libc/isystem/ headers and fix bugs --- build/bootstrap/mkdeps.com | Bin 73728 -> 147456 bytes libc/assert.h | 3 +- libc/calls/struct/dirent.h | 1 + libc/calls/sysparam.h | 3 +- libc/fmt/fmt.h | 1 + libc/isystem/crypt.h | 4 + libc/isystem/dlfcn.h | 2 +- libc/isystem/err.h | 2 +- libc/isystem/float.h | 1 + libc/isystem/langinfo.h | 1 + libc/isystem/nl_types.h | 4 + libc/isystem/stdio.h | 1 - libc/isystem/sys/file.h | 8 +- libc/isystem/sys/ipc.h | 1 - libc/isystem/sys/prctl.h | 5 + libc/log/backtrace2.c | 6 +- libc/log/log.h | 1 - libc/nexgen32e/ksha512.S | 64 ++ libc/nexgen32e/nexgen32e.h | 2 + libc/nexgen32e/sha512.S | 51 +- libc/stdio/dirstream.c | 82 +- libc/stdio/gcvt.c | 25 + libc/stdio/stdio.h | 3 + libc/str/memmem.c | 56 +- libc/sysv/consts/l.h | 9 + libc/unicode/langinfo.c | 176 +++-- libc/unicode/locale.h | 16 +- libc/unicode/nltypes.h | 19 + test/libc/stdio/crypt_test.c | 33 + test/libc/stdio/test.mk | 1 + test/libc/str/memmem_test.c | 66 +- test/libc/str/strstr_test.c | 13 + third_party/mbedtls/sha256.c | 2 +- third_party/mbedtls/sha512.c | 70 +- third_party/musl/crypt.c | 61 ++ third_party/musl/crypt.h | 16 + third_party/musl/crypt.internal.h | 15 + third_party/musl/crypt_blowfish.c | 846 ++++++++++++++++++++ third_party/musl/crypt_des.c | 1051 +++++++++++++++++++++++++ third_party/musl/crypt_des.internal.h | 16 + third_party/musl/crypt_md5.c | 324 ++++++++ third_party/musl/crypt_r.c | 69 ++ third_party/musl/crypt_sha256.c | 358 +++++++++ third_party/musl/crypt_sha512.c | 395 ++++++++++ third_party/musl/encrypt.c | 97 +++ third_party/musl/musl.mk | 1 + third_party/unzip/unix.c | 4 +- tool/build/mkdeps.c | 60 +- tool/emacs/cosmo-stuff.el | 2 +- tool/scripts/fix-third-party.py | 16 +- 50 files changed, 3783 insertions(+), 280 deletions(-) create mode 100644 libc/isystem/crypt.h create mode 100644 libc/isystem/nl_types.h create mode 100644 libc/isystem/sys/prctl.h create mode 100644 libc/nexgen32e/ksha512.S create mode 100644 libc/stdio/gcvt.c create mode 100644 libc/sysv/consts/l.h create mode 100644 libc/unicode/nltypes.h create mode 100644 test/libc/stdio/crypt_test.c create mode 100644 third_party/musl/crypt.c create mode 100644 third_party/musl/crypt.h create mode 100644 third_party/musl/crypt.internal.h create mode 100644 third_party/musl/crypt_blowfish.c create mode 100644 third_party/musl/crypt_des.c create mode 100644 third_party/musl/crypt_des.internal.h create mode 100644 third_party/musl/crypt_md5.c create mode 100644 third_party/musl/crypt_r.c create mode 100644 third_party/musl/crypt_sha256.c create mode 100644 third_party/musl/crypt_sha512.c create mode 100644 third_party/musl/encrypt.c diff --git a/build/bootstrap/mkdeps.com b/build/bootstrap/mkdeps.com index e3c05a272ee5beea0bb074fc3fed616e966ad6d4..d5f61fc2772686499ad71a8c3dbcec54e8d63d63 100755 GIT binary patch literal 147456 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%0AGS;4zE=%8l!u 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 X848@Cz!?ghp}-jmoT0$~YZUlD{dZ=5 literal 73728 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 UTo{21BXD5^E{wqcw@~ diff --git a/libc/assert.h b/libc/assert.h index e7c8d22e4..ce6933b2a 100644 --- a/libc/assert.h +++ b/libc/assert.h @@ -1,6 +1,5 @@ #ifndef COSMOPOLITAN_LIBC_ASSERT_H_ #define COSMOPOLITAN_LIBC_ASSERT_H_ -#include "libc/bits/likely.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -11,7 +10,7 @@ void __assert_fail(const char *, const char *, int) hidden relegated; #define assert(EXPR) ((void)0) #else #define assert(EXPR) \ - ((void)(LIKELY(EXPR) || (__assert_fail(#EXPR, __FILE__, __LINE__), 0))) + ((void)((EXPR) || (__assert_fail(#EXPR, __FILE__, __LINE__), 0))) #endif #ifndef __cplusplus diff --git a/libc/calls/struct/dirent.h b/libc/calls/struct/dirent.h index cb94f580d..4385bd4ea 100644 --- a/libc/calls/struct/dirent.h +++ b/libc/calls/struct/dirent.h @@ -20,6 +20,7 @@ int dirfd(DIR *); long telldir(DIR *); struct dirent *readdir(DIR *); void rewinddir(DIR *); +void seekdir(DIR *, long); #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_DIRENT_H_ */ diff --git a/libc/calls/sysparam.h b/libc/calls/sysparam.h index 64d36a7c1..57c5c6834 100644 --- a/libc/calls/sysparam.h +++ b/libc/calls/sysparam.h @@ -27,9 +27,10 @@ COSMOPOLITAN_C_START_ #define isset(x, i) __bitop(x, i, &) #define isclr(x, i) !isset(x, i) -#define howmany(n, d) (((n) + ((d)-1)) / (d)) +#undef roundup #define roundup(n, d) (howmany(n, d) * (d)) #define powerof2(n) !(((n)-1) & (n)) +#define howmany(n, d) (((n) + ((d)-1)) / (d)) COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/fmt/fmt.h b/libc/fmt/fmt.h index edc011bac..e872b062b 100644 --- a/libc/fmt/fmt.h +++ b/libc/fmt/fmt.h @@ -30,6 +30,7 @@ int __fmt(void *, void *, const char *, va_list) hidden; char *itoa(int, char *, int) compatfn; char *fcvt(double, int, int *, int *); char *ecvt(double, int, int *, int *); +char *gcvt(double, int, char *); /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § string formatting » optimizations ─╬─│┼ diff --git a/libc/isystem/crypt.h b/libc/isystem/crypt.h new file mode 100644 index 000000000..64b206eb6 --- /dev/null +++ b/libc/isystem/crypt.h @@ -0,0 +1,4 @@ +#ifndef COSMOPOLITAN_LIBC_ISYSTEM_CRYPT_H_ +#define COSMOPOLITAN_LIBC_ISYSTEM_CRYPT_H_ +#include "third_party/musl/crypt.h" +#endif /* COSMOPOLITAN_LIBC_ISYSTEM_CRYPT_H_ */ diff --git a/libc/isystem/dlfcn.h b/libc/isystem/dlfcn.h index b7f807af5..3d124aa22 100644 --- a/libc/isystem/dlfcn.h +++ b/libc/isystem/dlfcn.h @@ -1,4 +1,4 @@ #ifndef LIBC_ISYSTEM_SYS_DLFCN_H_ #define LIBC_ISYSTEM_SYS_DLFCN_H_ -#include "libc/calls/calls.h" +#include "libc/runtime/dlfcn.h" #endif diff --git a/libc/isystem/err.h b/libc/isystem/err.h index f75781cf0..0bba6cfdc 100644 --- a/libc/isystem/err.h +++ b/libc/isystem/err.h @@ -1,4 +1,4 @@ #ifndef LIBC_ISYSTEM_ERR_H_ #define LIBC_ISYSTEM_ERR_H_ -#include "libc/log/log.h" +#include "libc/log/bsd.h" #endif diff --git a/libc/isystem/float.h b/libc/isystem/float.h index ea1e0cf74..3b76ad848 100644 --- a/libc/isystem/float.h +++ b/libc/isystem/float.h @@ -1,4 +1,5 @@ #ifndef LIBC_ISYSTEM_FLOAT_H_ #define LIBC_ISYSTEM_FLOAT_H_ #include "libc/math.h" +#include "libc/runtime/fenv.h" #endif diff --git a/libc/isystem/langinfo.h b/libc/isystem/langinfo.h index 217a859d8..58829e26f 100644 --- a/libc/isystem/langinfo.h +++ b/libc/isystem/langinfo.h @@ -1,4 +1,5 @@ #ifndef COSMOPOLITAN_LIBC_ISYSTEM_LANGINFO_H_ #define COSMOPOLITAN_LIBC_ISYSTEM_LANGINFO_H_ +#include "libc/unicode/langinfo.h" #endif /* COSMOPOLITAN_LIBC_ISYSTEM_LANGINFO_H_ */ diff --git a/libc/isystem/nl_types.h b/libc/isystem/nl_types.h new file mode 100644 index 000000000..eb7a3d22a --- /dev/null +++ b/libc/isystem/nl_types.h @@ -0,0 +1,4 @@ +#ifndef COSMOPOLITAN_LIBC_ISYSTEM_NL_TYPES_H_ +#define COSMOPOLITAN_LIBC_ISYSTEM_NL_TYPES_H_ +#include "libc/unicode/nltypes.h" +#endif /* COSMOPOLITAN_LIBC_ISYSTEM_NL_TYPES_H_ */ diff --git a/libc/isystem/stdio.h b/libc/isystem/stdio.h index e227eda4d..5781a1259 100644 --- a/libc/isystem/stdio.h +++ b/libc/isystem/stdio.h @@ -2,7 +2,6 @@ #define LIBC_ISYSTEM_STDIO_H_ #include "libc/calls/calls.h" #include "libc/fmt/fmt.h" -#include "libc/log/log.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" #endif diff --git a/libc/isystem/sys/file.h b/libc/isystem/sys/file.h index 82ec41632..f343d0a22 100644 --- a/libc/isystem/sys/file.h +++ b/libc/isystem/sys/file.h @@ -1,10 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_ISYSTEM_SYS_FILE_H_ #define COSMOPOLITAN_LIBC_ISYSTEM_SYS_FILE_H_ #include "libc/calls/calls.h" +#include "libc/calls/struct/flock.h" +#include "libc/sysv/consts/l.h" #include "libc/sysv/consts/lock.h" - -#define L_SET SEEK_SET -#define L_INCR SEEK_CUR -#define L_XTND SEEK_END - +#include "libc/sysv/consts/ok.h" #endif /* COSMOPOLITAN_LIBC_ISYSTEM_SYS_FILE_H_ */ diff --git a/libc/isystem/sys/ipc.h b/libc/isystem/sys/ipc.h index 6b69674f1..693c25145 100644 --- a/libc/isystem/sys/ipc.h +++ b/libc/isystem/sys/ipc.h @@ -2,5 +2,4 @@ #define COSMOPOLITAN_LIBC_ISYSTEM_SYS_IPC_H_ #include "libc/calls/ipc.h" #include "libc/calls/weirdtypes.h" -#include "libc/sysv/consts/ipc.h" #endif /* COSMOPOLITAN_LIBC_ISYSTEM_SYS_IPC_H_ */ diff --git a/libc/isystem/sys/prctl.h b/libc/isystem/sys/prctl.h new file mode 100644 index 000000000..cd18af431 --- /dev/null +++ b/libc/isystem/sys/prctl.h @@ -0,0 +1,5 @@ +#ifndef COSMOPOLITAN_LIBC_ISYSTEM_SYS_PRCTL_H_ +#define COSMOPOLITAN_LIBC_ISYSTEM_SYS_PRCTL_H_ +#include "libc/calls/calls.h" +#include "libc/sysv/consts/pr.h" +#endif /* COSMOPOLITAN_LIBC_ISYSTEM_SYS_PRCTL_H_ */ diff --git a/libc/log/backtrace2.c b/libc/log/backtrace2.c index f0a029b45..1239c010f 100644 --- a/libc/log/backtrace2.c +++ b/libc/log/backtrace2.c @@ -120,18 +120,16 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { j += uint64toarray_radix16(addr - 1, buf + j) + 1; } argv[i++] = NULL; - if (sys_pipe(pipefds) == -1) { + if (sys_pipe2(pipefds, O_CLOEXEC) == -1) { return -1; } - if ((pid = vfork()) == -1) { + if ((pid = fork()) == -1) { sys_close(pipefds[0]); sys_close(pipefds[1]); return -1; } if (!pid) { sys_dup2(pipefds[1], 1); - if (pipefds[0] != 1) sys_close(pipefds[0]); - if (pipefds[1] != 1) sys_close(pipefds[1]); sys_execve(addr2line, argv, environ); _Exit(127); } diff --git a/libc/log/log.h b/libc/log/log.h index 50c81adde..b284a0e62 100644 --- a/libc/log/log.h +++ b/libc/log/log.h @@ -41,7 +41,6 @@ COSMOPOLITAN_C_START_ extern FILE *__log_file; -void perror(const char *) relegated; /* print the last system error */ void __die(void) relegated wontreturn; /* print backtrace and abort() */ void meminfo(int); /* shows malloc statistics &c. */ void memsummary(int); /* light version of same thing */ diff --git a/libc/nexgen32e/ksha512.S b/libc/nexgen32e/ksha512.S new file mode 100644 index 000000000..1b28a1040 --- /dev/null +++ b/libc/nexgen32e/ksha512.S @@ -0,0 +1,64 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macros.internal.h" + + .rodata + .align 64 +kSha512: + .quad 0x428a2f98d728ae22,0x7137449123ef65cd + .quad 0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc + .quad 0x3956c25bf348b538,0x59f111f1b605d019 + .quad 0x923f82a4af194f9b,0xab1c5ed5da6d8118 + .quad 0xd807aa98a3030242,0x12835b0145706fbe + .quad 0x243185be4ee4b28c,0x550c7dc3d5ffb4e2 + .quad 0x72be5d74f27b896f,0x80deb1fe3b1696b1 + .quad 0x9bdc06a725c71235,0xc19bf174cf692694 + .quad 0xe49b69c19ef14ad2,0xefbe4786384f25e3 + .quad 0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65 + .quad 0x2de92c6f592b0275,0x4a7484aa6ea6e483 + .quad 0x5cb0a9dcbd41fbd4,0x76f988da831153b5 + .quad 0x983e5152ee66dfab,0xa831c66d2db43210 + .quad 0xb00327c898fb213f,0xbf597fc7beef0ee4 + .quad 0xc6e00bf33da88fc2,0xd5a79147930aa725 + .quad 0x06ca6351e003826f,0x142929670a0e6e70 + .quad 0x27b70a8546d22ffc,0x2e1b21385c26c926 + .quad 0x4d2c6dfc5ac42aed,0x53380d139d95b3df + .quad 0x650a73548baf63de,0x766a0abb3c77b2a8 + .quad 0x81c2c92e47edaee6,0x92722c851482353b + .quad 0xa2bfe8a14cf10364,0xa81a664bbc423001 + .quad 0xc24b8b70d0f89791,0xc76c51a30654be30 + .quad 0xd192e819d6ef5218,0xd69906245565a910 + .quad 0xf40e35855771202a,0x106aa07032bbd1b8 + .quad 0x19a4c116b8d2d0c8,0x1e376c085141ab53 + .quad 0x2748774cdf8eeb99,0x34b0bcb5e19b48a8 + .quad 0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb + .quad 0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3 + .quad 0x748f82ee5defb2fc,0x78a5636f43172f60 + .quad 0x84c87814a1f0ab72,0x8cc702081a6439ec + .quad 0x90befffa23631e28,0xa4506cebde82bde9 + .quad 0xbef9a3f7b2c67915,0xc67178f2e372532b + .quad 0xca273eceea26619c,0xd186b8c721c0c207 + .quad 0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178 + .quad 0x06f067aa72176fba,0x0a637dc5a2c898a6 + .quad 0x113f9804bef90dae,0x1b710b35131c471b + .quad 0x28db77f523047d84,0x32caab7b40c72493 + .quad 0x3c9ebe0a15c9bebc,0x431d67c49c100d4c + .quad 0x4cc5d4becb3e42b6,0x597f299cfc657e2a + .quad 0x5fcb6fab3ad6faec,0x6c44198c4a475817 + .endobj kSha512,globl diff --git a/libc/nexgen32e/nexgen32e.h b/libc/nexgen32e/nexgen32e.h index 5d9afc801..ea8efa135 100644 --- a/libc/nexgen32e/nexgen32e.h +++ b/libc/nexgen32e/nexgen32e.h @@ -5,6 +5,8 @@ COSMOPOLITAN_C_START_ extern long kHalfCache3; extern const uint64_t kTens[20]; +extern const uint32_t kSha256[64]; +extern const uint64_t kSha512[80]; extern const unsigned char kTensIndex[64]; void imapxlatab(void *); diff --git a/libc/nexgen32e/sha512.S b/libc/nexgen32e/sha512.S index 34b8f21aa..1da108f44 100644 --- a/libc/nexgen32e/sha512.S +++ b/libc/nexgen32e/sha512.S @@ -609,7 +609,7 @@ sha512_transform_rorx: vmovdqa PSHUFFLE_BYTE_FLIP_MASK(%rip), BYTE_FLIP_MASK .Loop0: - lea K512(%rip), TBL + lea kSha512(%rip), TBL ## byte swap first 16 dwords COPY_YMM_AND_BSWAP Y_0, (INP), BYTE_FLIP_MASK @@ -693,55 +693,6 @@ sha512_transform_rorx: ######################################################################## ### Binary Data - -# Mergeable 640-byte rodata section. This allows linker to merge the table -# with other, exactly the same 640-byte fragment of another rodata section -# (if such section exists). -.section .rodata.cst640.K512, "aM", @progbits, 640 -.align 64 -# K[t] used in SHA512 hashing -K512: - .quad 0x428a2f98d728ae22,0x7137449123ef65cd - .quad 0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc - .quad 0x3956c25bf348b538,0x59f111f1b605d019 - .quad 0x923f82a4af194f9b,0xab1c5ed5da6d8118 - .quad 0xd807aa98a3030242,0x12835b0145706fbe - .quad 0x243185be4ee4b28c,0x550c7dc3d5ffb4e2 - .quad 0x72be5d74f27b896f,0x80deb1fe3b1696b1 - .quad 0x9bdc06a725c71235,0xc19bf174cf692694 - .quad 0xe49b69c19ef14ad2,0xefbe4786384f25e3 - .quad 0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65 - .quad 0x2de92c6f592b0275,0x4a7484aa6ea6e483 - .quad 0x5cb0a9dcbd41fbd4,0x76f988da831153b5 - .quad 0x983e5152ee66dfab,0xa831c66d2db43210 - .quad 0xb00327c898fb213f,0xbf597fc7beef0ee4 - .quad 0xc6e00bf33da88fc2,0xd5a79147930aa725 - .quad 0x06ca6351e003826f,0x142929670a0e6e70 - .quad 0x27b70a8546d22ffc,0x2e1b21385c26c926 - .quad 0x4d2c6dfc5ac42aed,0x53380d139d95b3df - .quad 0x650a73548baf63de,0x766a0abb3c77b2a8 - .quad 0x81c2c92e47edaee6,0x92722c851482353b - .quad 0xa2bfe8a14cf10364,0xa81a664bbc423001 - .quad 0xc24b8b70d0f89791,0xc76c51a30654be30 - .quad 0xd192e819d6ef5218,0xd69906245565a910 - .quad 0xf40e35855771202a,0x106aa07032bbd1b8 - .quad 0x19a4c116b8d2d0c8,0x1e376c085141ab53 - .quad 0x2748774cdf8eeb99,0x34b0bcb5e19b48a8 - .quad 0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb - .quad 0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3 - .quad 0x748f82ee5defb2fc,0x78a5636f43172f60 - .quad 0x84c87814a1f0ab72,0x8cc702081a6439ec - .quad 0x90befffa23631e28,0xa4506cebde82bde9 - .quad 0xbef9a3f7b2c67915,0xc67178f2e372532b - .quad 0xca273eceea26619c,0xd186b8c721c0c207 - .quad 0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178 - .quad 0x06f067aa72176fba,0x0a637dc5a2c898a6 - .quad 0x113f9804bef90dae,0x1b710b35131c471b - .quad 0x28db77f523047d84,0x32caab7b40c72493 - .quad 0x3c9ebe0a15c9bebc,0x431d67c49c100d4c - .quad 0x4cc5d4becb3e42b6,0x597f299cfc657e2a - .quad 0x5fcb6fab3ad6faec,0x6c44198c4a475817 - .rodata.cst32 # Mask for byte-swapping a couple of qwords in an XMM register using (v)pshufb. PSHUFFLE_BYTE_FLIP_MASK: diff --git a/libc/stdio/dirstream.c b/libc/stdio/dirstream.c index 168fef969..c9d235d7d 100644 --- a/libc/stdio/dirstream.c +++ b/libc/stdio/dirstream.c @@ -18,11 +18,14 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/bits/weaken.h" +#include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/dirent.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/nopl.h" +#include "libc/intrin/pthread.h" #include "libc/mem/mem.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/enum/filetype.h" @@ -53,6 +56,7 @@ struct dirstream { bool iszip; int64_t fd; int64_t tell; + pthread_mutex_t lock; struct { uint64_t offset; uint64_t records; @@ -109,6 +113,22 @@ struct dirent_netbsd { char d_name[512]; }; +void _lockdir(DIR *dir) { + pthread_mutex_lock(&dir->lock); +} + +void _unlockdir(DIR *dir) { + pthread_mutex_unlock(&dir->lock); +} + +#ifdef _NOPL1 +#define _lockdir(d) _NOPL1("__threadcalls", _lockdir, d) +#define _unlockdir(d) _NOPL1("__threadcalls", _unlockdir, d) +#else +#define _lockdir(d) (__threaded ? _lockdir(d) : 0) +#define _unlockdir(d) (__threaded ? _unlockdir(d) : 0) +#endif + static textwindows DIR *opendir_nt_impl(char16_t *name, size_t len) { DIR *res; if (len + 2 + 1 <= PATH_MAX) { @@ -299,16 +319,7 @@ DIR *fdopendir(int fd) { return dir; } -/** - * Reads next entry from directory stream. - * - * This API doesn't define any particular ordering. - * - * @param dir is the object opendir() or fdopendir() returned - * @return next entry or NULL on end or error, which can be - * differentiated by setting errno to 0 beforehand - */ -struct dirent *readdir(DIR *dir) { +static struct dirent *readdir_impl(DIR *dir) { size_t n; long basep; int rc, mode; @@ -393,6 +404,23 @@ struct dirent *readdir(DIR *dir) { } } +/** + * Reads next entry from directory stream. + * + * This API doesn't define any particular ordering. + * + * @param dir is the object opendir() or fdopendir() returned + * @return next entry or NULL on end or error, which can be + * differentiated by setting errno to 0 beforehand + */ +struct dirent *readdir(DIR *dir) { + struct dirent *e; + _lockdir(dir); + e = readdir_impl(dir); + _unlockdir(dir); + return e; +} + /** * Closes directory object returned by opendir(). * @return 0 on success or -1 w/ errno @@ -421,8 +449,12 @@ int closedir(DIR *dir) { * Returns offset into directory data. */ long telldir(DIR *dir) { - STRACE("telldir(%p) → %d", dir, dir->tell); - return dir->tell; + long rc; + _lockdir(dir); + rc = dir->tell; + STRACE("telldir(%p) → %ld", dir, rc); + _unlockdir(dir); + return rc; } /** @@ -430,6 +462,7 @@ long telldir(DIR *dir) { */ int dirfd(DIR *dir) { int rc; + _lockdir(dir); if (dir->iszip) { rc = eopnotsupp(); } else if (IsWindows()) { @@ -438,6 +471,7 @@ int dirfd(DIR *dir) { rc = dir->fd; } STRACE("dirfd(%p) → %d% m", dir, rc); + _unlockdir(dir); return rc; } @@ -445,6 +479,7 @@ int dirfd(DIR *dir) { * Seeks to beginning of directory stream. */ void rewinddir(DIR *dir) { + _lockdir(dir); if (dir->iszip) { dir->tell = 0; dir->zip.offset = GetZipCdirOffset(weaken(__zipos_get)()->cdir); @@ -463,4 +498,27 @@ void rewinddir(DIR *dir) { } } STRACE("rewinddir(%p)", dir); + _unlockdir(dir); +} + +/** + * Seeks in directory stream. + */ +void seekdir(DIR *dir, long off) { + long i; + struct Zipos *zip; + _lockdir(dir); + zip = weaken(__zipos_get)(); + if (dir->iszip) { + dir->zip.offset = GetZipCdirOffset(weaken(__zipos_get)()->cdir); + for (i = 0; i < off && i < dir->zip.records; ++i) { + dir->zip.offset += ZIP_CFILE_HDRSIZE(zip->map + dir->zip.offset); + } + } else { + i = lseek(dir->fd, off, SEEK_SET); + dir->buf_pos = dir->buf_end = 0; + } + dir->tell = i; + STRACE("seekdir(%p, %ld) → %ld", dir, off, i); + _unlockdir(dir); } diff --git a/libc/stdio/gcvt.c b/libc/stdio/gcvt.c new file mode 100644 index 000000000..462959915 --- /dev/null +++ b/libc/stdio/gcvt.c @@ -0,0 +1,25 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/fmt.h" +#include "libc/stdio/stdio.h" + +char *gcvt(double x, int n, char *b) { + sprintf(b, "%.*g", n, x); + return b; +} diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h index 40555bd9d..8059adc80 100644 --- a/libc/stdio/stdio.h +++ b/libc/stdio/stdio.h @@ -6,6 +6,7 @@ #include "libc/nexgen32e/threaded.h" #include "libc/runtime/symbolic.h" +#define _STDIO_H #define L_ctermid 20 #define FILENAME_MAX PATH_MAX @@ -78,12 +79,14 @@ int fseeko(FILE *, int64_t, int) paramsnonnull(); int64_t ftello(FILE *) paramsnonnull(); void rewind(FILE *) paramsnonnull(); int fopenflags(const char *) paramsnonnull(); +void setlinebuf(FILE *); void setbuf(FILE *, char *); void setbuffer(FILE *, char *, size_t); int setvbuf(FILE *, char *, int, size_t); FILE *popen(const char *, const char *); int pclose(FILE *); char *ctermid(char *); +void perror(const char *) relegated; typedef uint64_t fpos_t; compatfn char *gets(char *) paramsnonnull(); diff --git a/libc/str/memmem.c b/libc/str/memmem.c index 071d5ea3f..499042aaa 100644 --- a/libc/str/memmem.c +++ b/libc/str/memmem.c @@ -16,8 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/likely.h" +#include "libc/dce.h" +#include "libc/intrin/asan.internal.h" #include "libc/str/str.h" +typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16))); + /** * Searches for fixed-length substring in memory region. * @@ -27,26 +32,41 @@ * @param needlelen is its character count * @return pointer to first result or NULL if not found */ -void *memmem(const void *haystack, size_t haystacklen, const void *needle, - size_t needlelen) { - size_t i, j; - const char *p; +noasan void *memmem(const void *haystack, size_t haystacklen, + const void *needle, size_t needlelen) { + char c; + xmm_t n, *v; + unsigned i, k, m; + const char *p, *q, *e; + if (IsAsan()) __asan_verify(needle, needlelen); + if (IsAsan()) __asan_verify(haystack, haystacklen); if (!needlelen) return haystack; - if (needlelen <= haystacklen) { - p = memchr(haystack, *(const char *)needle, haystacklen); - if (needlelen == 1) return p; - if (p) { - haystacklen -= p - (const char *)haystack; - haystack = p; + if (UNLIKELY(needlelen > haystacklen)) return 0; + q = needle; + c = *q; + n = (xmm_t){c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c}; + p = haystack; + e = p + haystacklen; + k = (uintptr_t)p & 15; + v = (const xmm_t *)((uintptr_t)p & -16); + m = __builtin_ia32_pmovmskb128(*v == n); + m >>= k; + m <<= k; + for (;;) { + while (!m) { + ++v; + if ((const char *)v >= e) return 0; + m = __builtin_ia32_pmovmskb128(*v == n); } - /* TODO: make not quadratic */ - for (i = 0; i < haystacklen; ++i) { - for (j = 0;; ++j) { - if (j == needlelen) return (/*unconst*/ char *)haystack + i; - if (i + j == haystacklen) break; - if (((char *)needle)[j] != ((char *)haystack)[i + j]) break; + do { + k = __builtin_ctzl(m); + p = (const char *)v + k; + if (UNLIKELY(p + needlelen > e)) return 0; + for (i = 1;; ++i) { + if (i == needlelen) return (/*unconst*/ char *)p; + if (p[i] != q[i]) break; } - } + m &= ~(1 << k); + } while (m); } - return NULL; } diff --git a/libc/sysv/consts/l.h b/libc/sysv/consts/l.h new file mode 100644 index 000000000..8fb2c7e36 --- /dev/null +++ b/libc/sysv/consts/l.h @@ -0,0 +1,9 @@ +#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_L_H_ +#define COSMOPOLITAN_LIBC_SYSV_CONSTS_L_H_ +#include "libc/calls/calls.h" + +#define L_SET SEEK_SET +#define L_INCR SEEK_CUR +#define L_XTND SEEK_END + +#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_L_H_ */ diff --git a/libc/unicode/langinfo.c b/libc/unicode/langinfo.c index 7166edb9a..154b0d9f2 100644 --- a/libc/unicode/langinfo.c +++ b/libc/unicode/langinfo.c @@ -1,89 +1,105 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2021 Justine Alexandra Roberts Tunney │ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ │ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/nexgen32e/gettls.h" +#include "libc/thread/thread.h" #include "libc/unicode/langinfo.h" +#include "libc/unicode/locale.h" +#include "libc/unicode/nltypes.h" -static const char c_time[] = "\ -Sun\0\n\ -Mon\0\n\ -Tue\0\n\ -Wed\0\n\ -Thu\0\n\ -Fri\0\n\ -Sat\0\n\ -Sunday\0\n\ -Monday\0\n\ -Tuesday\0\n\ -Wednesday\0\n\ -Thursday\0\n\ -Friday\0\n\ -Saturday\0\n\ -Jan\0\n\ -Feb\0\n\ -Mar\0\n\ -Apr\0\n\ -May\0\n\ -Jun\0\n\ -Jul\0\n\ -Aug\0\n\ -Sep\0\n\ -Oct\0\n\ -Nov\0\n\ -Dec\0\n\ -January\0\n\ -February\0\n\ -March\0\n\ -April\0\n\ -May\0\n\ -June\0\n\ -July\0\n\ -August\0\n\ -September\0\n\ -October\0\n\ -November\0\n\ -December\0\n\ -AM\0\n\ -PM\0\n\ -a %b %e %T %Y\0\n\ -m/%d/%y\0\n\ -H:%M:%S\0\n\ -I:%M:%S %p\0\n\ -0\n\ -0\n\ -m/%d/%y\0\n\ -0123456789\0\n\ -a %b %e %T %Y\0\n\ -H:%M:%S"; +asm(".ident\t\"\\n\\n\ +Musl libc (MIT License)\\n\ +Copyright 2005-2014 Rich Felker, et. al.\""); +asm(".include \"libc/disclaimer.inc\""); +// clang-format off -static const char c_messages[] = "\ -^[yY]\0\ -^[nN]\0\ -yes\0\ -no"; +static const char c_time[] = + "Sun\0" "Mon\0" "Tue\0" "Wed\0" "Thu\0" "Fri\0" "Sat\0" + "Sunday\0" "Monday\0" "Tuesday\0" "Wednesday\0" + "Thursday\0" "Friday\0" "Saturday\0" + "Jan\0" "Feb\0" "Mar\0" "Apr\0" "May\0" "Jun\0" + "Jul\0" "Aug\0" "Sep\0" "Oct\0" "Nov\0" "Dec\0" + "January\0" "February\0" "March\0" "April\0" + "May\0" "June\0" "July\0" "August\0" + "September\0" "October\0" "November\0" "December\0" + "AM\0" "PM\0" + "%a %b %e %T %Y\0" + "%m/%d/%y\0" + "%H:%M:%S\0" + "%I:%M:%S %p\0" + "\0" + "\0" + "%m/%d/%y\0" + "0123456789\0" + "%a %b %e %T %Y\0" + "%H:%M:%S"; -static const char c_numeric[] = "\ -.\0\ -"; +static const char c_messages[] = "^[yY]\0" "^[nN]\0" "yes\0" "no"; +static const char c_numeric[] = ".\0" ""; -char *nl_langinfo(int item) { - if (item == CODESET) { - return "UTF-8"; - } else { - return 0; - } +char *nl_langinfo_l(nl_item item, locale_t loc) +{ + int cat = item >> 16; + int idx = item & 65535; + const char *str; + + if (item == CODESET) return loc->cat[LC_CTYPE] ? "UTF-8" : "ASCII"; + + /* _NL_LOCALE_NAME extension */ + if (idx == 65535 && cat < LC_ALL) + return loc->cat[cat] ? (char *)loc->cat[cat]->name : "C"; + + switch (cat) { + case LC_NUMERIC: + if (idx > 1) return ""; + str = c_numeric; + break; + case LC_TIME: + if (idx > 0x31) return ""; + str = c_time; + break; + case LC_MONETARY: + if (idx > 0) return ""; + str = ""; + break; + case LC_MESSAGES: + if (idx > 3) return ""; + str = c_messages; + break; + default: + return ""; + } + + for (; idx; idx--, str++) for (; *str; str++); + // if (cat != LC_NUMERIC && *str) str = LCTRANS(str, cat, loc); + return (char *)str; +} + +char *nl_langinfo(nl_item item) +{ + return nl_langinfo_l(item, ((cthread_t)__get_tls())->locale); } diff --git a/libc/unicode/locale.h b/libc/unicode/locale.h index 891531612..41a46a1bb 100644 --- a/libc/unicode/locale.h +++ b/libc/unicode/locale.h @@ -17,13 +17,27 @@ #define LC_MONETARY_MASK 16 #define LC_MESSAGES_MASK 32 #define LC_ALL_MASK 0x1fbf +#define LOCALE_NAME_MAX 23 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -struct __locale_struct; +#define LC_GLOBAL_LOCALE ((locale_t)-1) + +struct __locale_map { + const void *map; + size_t map_size; + char name[LOCALE_NAME_MAX + 1]; + const struct __locale_map *next; +}; + +struct __locale_struct { + const struct __locale_map *cat[6]; +}; + typedef struct __locale_struct *locale_t; +char *nl_langinfo_l(int, locale_t); char *setlocale(int, const char *); double strtod_l(const char *, char **, locale_t); double wcstod_l(const wchar_t *, wchar_t **, locale_t); diff --git a/libc/unicode/nltypes.h b/libc/unicode/nltypes.h new file mode 100644 index 000000000..c356265fe --- /dev/null +++ b/libc/unicode/nltypes.h @@ -0,0 +1,19 @@ +#ifndef COSMOPOLITAN_LIBC_UNICODE_NLTYPES_H_ +#define COSMOPOLITAN_LIBC_UNICODE_NLTYPES_H_ + +#define NL_SETD 1 +#define NL_CAT_LOCALE 1 + +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +typedef int nl_item; +typedef void *nl_catd; + +nl_catd catopen(const char *, int); +char *catgets(nl_catd, int, int, const char *); +int catclose(nl_catd); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_UNICODE_NLTYPES_H_ */ diff --git a/test/libc/stdio/crypt_test.c b/test/libc/stdio/crypt_test.c new file mode 100644 index 000000000..4dc60cb00 --- /dev/null +++ b/test/libc/stdio/crypt_test.c @@ -0,0 +1,33 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/testlib/testlib.h" +#include "third_party/musl/crypt.h" + +TEST(crypt, test) { + // consistent with python crypt.crypt() + EXPECT_STREQ("thXpd0YFlQG2w", crypt("hello", "there")); + EXPECT_STREQ("$1$there$3B/lVCUlX8R18HDBuijby1", crypt("hello", "$1$there")); + EXPECT_STREQ("$2aVOhEz8P7i6", crypt("hello", "$2$there")); + EXPECT_STREQ("$5$there$.u5mdR0jvLs0jEf7qHTG98t8la1KVhEBH3eOFZ7ztL0", + crypt("hello", "$5$there")); + EXPECT_STREQ( + "$6$there$y0TGuPcNSR23fFWCwYUj6VUfhnc9nlnkm6Y8waSLnANwCUcxK6esd7xm7.Jl." + "jjr1/sPTaRK7igDNSxC.BhgX/", + crypt("hello", "$6$there")); +} diff --git a/test/libc/stdio/test.mk b/test/libc/stdio/test.mk index c6bd57baf..d88c6c47d 100644 --- a/test/libc/stdio/test.mk +++ b/test/libc/stdio/test.mk @@ -44,6 +44,7 @@ TEST_LIBC_STDIO_DIRECTDEPS = \ LIBC_X \ LIBC_ZIPOS \ THIRD_PARTY_GDTOA \ + THIRD_PARTY_MUSL \ THIRD_PARTY_ZLIB \ THIRD_PARTY_ZLIB_GZ diff --git a/test/libc/str/memmem_test.c b/test/libc/str/memmem_test.c index 87f56ce7c..1a586b924 100644 --- a/test/libc/str/memmem_test.c +++ b/test/libc/str/memmem_test.c @@ -18,7 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/alg/alg.h" #include "libc/bits/bits.h" +#include "libc/bits/likely.h" #include "libc/mem/mem.h" +#include "libc/rand/rand.h" #include "libc/str/internal.h" #include "libc/str/str.h" #include "libc/testlib/ezbench.h" @@ -27,6 +29,27 @@ #define MakeMemory(SL) memcpy(malloc(sizeof(SL) - 1), SL, sizeof(SL) - 1) +void *memmem_naive(const void *haystk, size_t haystklen, // + const void *needle, size_t needlelen) { + size_t i, j; + if (!needlelen) return haystk; + if (needlelen > haystklen) return 0; + for (i = 0; i < haystklen; ++i) { + for (j = 0;; ++j) { + if (j == needlelen) return (/*unconst*/ char *)haystk + i; + if (i + j == haystklen) break; + if (((char *)haystk)[i + j] != ((char *)needle)[j]) break; + } + } + return 0; +} + +TEST(memmem, special) { + EXPECT_EQ(0, memmem(0, 0, 0, 0)); + EXPECT_EQ(0, memmem(0, 0, "", 1)); + EXPECT_EQ("", memmem("", 1, 0, 0)); +} + TEST(memmem, test) { char *needle = MakeMemory("abcdefgh"); char *haystk = MakeMemory("acccccccbbbbbbbbabcdefghdddddddd"); @@ -55,8 +78,8 @@ TEST(memmem, testStartOfMemory) { } TEST(memmem, testEndOfMemory) { - char *needle = MakeMemory("123"); char *haystk = MakeMemory("abc123"); + char *needle = MakeMemory("123"); EXPECT_EQ(&haystk[3], memmem(haystk, 6, needle, 3)); free(haystk); free(needle); @@ -79,8 +102,8 @@ TEST(memmem, testOneYes) { } TEST(memmem, testCrossesSseRegister) { - char *needle = MakeMemory("eeeeeeeeeeeeefffffffffffff"); char *haystk = MakeMemory("eeeeeeeeeeeeeeeeffffffffffffffffrrrrrrrrrrrrrrrr"); + char *needle = MakeMemory("eeeeeeeeeeeeefffffffffffff"); EXPECT_EQ(&haystk[3], memmem(haystk, 16 * 3, needle, 26)); free(haystk); free(needle); @@ -132,7 +155,46 @@ TEST(memmem, testWut) { ASSERT_STREQ("x", memmem("x", 1, "x", 1)); } +TEST(memmem, fuzz) { + int i, j, k, n, m; + char a[128], b[128], *p, *q; + for (i = 0; i < 10000; ++i) { + rngset(a, sizeof(a), lemur64, -1); + rngset(b, sizeof(b), lemur64, -1); + p = a + lemur64() % sizeof(a) / 2; + q = b + lemur64() % sizeof(b) / 2; + n = lemur64() % sizeof(a) / 2; + m = lemur64() % sizeof(b) / 2; + ASSERT_EQ(memmem_naive(p, n, q, m), memmem(p, n, q, m)); + } +} + +/* + * memmem naive l: 43,783c 14,142ns m: 31,285c 10,105ns + * memmem l: 2,597c 839ns m: 2,612c 844ns + * memmem l: 509c 164ns m: 599c 193ns + * + * strstr naive l: 103,057c 33,287ns m: 47,035c 15,192ns + * strstr l: 3,186c 1,029ns m: 3,218c 1,039ns + * strstr torture 1 l: 27c 9ns m: 61c 20ns + * strstr torture 2 l: 2,322c 750ns m: 2,362c 763ns + * strstr torture 4 l: 2,407c 777ns m: 2,448c 791ns + * strstr torture 8 l: 2,803c 905ns m: 2,862c 924ns + * strstr torture 16 l: 4,559c 1,473ns m: 3,614c 1,167ns + * strstr torture 32 l: 5,324c 1,720ns m: 5,577c 1,801ns + * + * strcasestr naive l: 129,908c 41,959ns m: 155,420c 50,200ns + * strcasestr l: 33,464c 10,809ns m: 31,636c 10,218ns + * strcasestr tort 1 l: 38c 12ns m: 69c 22ns + * strcasestr tort 2 l: 2,544c 822ns m: 2,580c 833ns + * strcasestr tort 4 l: 2,745c 887ns m: 2,767c 894ns + * strcasestr tort 8 l: 4,198c 1,356ns m: 4,216c 1,362ns + * strcasestr tort 16 l: 7,402c 2,391ns m: 7,487c 2,418ns + * strcasestr tort 32 l: 13,772c 4,448ns m: 12,945c 4,181ns + */ BENCH(memmem, bench) { + EZBENCH2("memmem naive", donothing, + EXPROPRIATE(memmem_naive(kHyperion, kHyperionSize, "THE END", 7))); EZBENCH2("memmem", donothing, EXPROPRIATE(memmem(kHyperion, kHyperionSize, "THE END", 7))); EZBENCH2("memmem", donothing, diff --git a/test/libc/str/strstr_test.c b/test/libc/str/strstr_test.c index 405594c2b..31bbcc797 100644 --- a/test/libc/str/strstr_test.c +++ b/test/libc/str/strstr_test.c @@ -95,7 +95,20 @@ TEST(strstr, test) { ASSERT_STREQ("x", strstr("x", "x")); } +TEST(strstr, breakit) { + char *p; + p = gc(calloc(1, 32)); + p[0] = 'c'; + p[1] = 'c'; + p[10] = 'b'; + ASSERT_EQ(NULL, strstr(p, "b")); +} + /* + * memmem naive l: 43,783c 14,142ns m: 31,285c 10,105ns + * memmem l: 2,597c 839ns m: 2,612c 844ns + * memmem l: 509c 164ns m: 599c 193ns + * * strstr naive l: 103,057c 33,287ns m: 47,035c 15,192ns * strstr l: 3,186c 1,029ns m: 3,218c 1,039ns * strstr torture 1 l: 27c 9ns m: 61c 20ns diff --git a/third_party/mbedtls/sha256.c b/third_party/mbedtls/sha256.c index a623517e1..c7543a87a 100644 --- a/third_party/mbedtls/sha256.c +++ b/third_party/mbedtls/sha256.c @@ -18,6 +18,7 @@ #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/macros.internal.h" +#include "libc/nexgen32e/nexgen32e.h" #include "libc/nexgen32e/sha.h" #include "libc/nexgen32e/x86feature.h" #include "libc/str/str.h" @@ -119,7 +120,6 @@ int mbedtls_sha256_starts_ret( mbedtls_sha256_context *ctx, int is224 ) } #if !defined(MBEDTLS_SHA256_PROCESS_ALT) -extern const uint32_t kSha256[64]; #define K kSha256 #define SHR(x,n) (((x) & 0xFFFFFFFF) >> (n)) diff --git a/third_party/mbedtls/sha512.c b/third_party/mbedtls/sha512.c index 635bfe481..90da99e3d 100644 --- a/third_party/mbedtls/sha512.c +++ b/third_party/mbedtls/sha512.c @@ -18,6 +18,7 @@ #include "libc/intrin/asan.internal.h" #include "libc/literal.h" #include "libc/macros.internal.h" +#include "libc/nexgen32e/nexgen32e.h" #include "libc/nexgen32e/x86feature.h" #include "libc/str/str.h" #include "third_party/mbedtls/chk.h" @@ -130,53 +131,6 @@ int mbedtls_sha512_starts_ret( mbedtls_sha512_context *ctx, int is384 ) #if !defined(MBEDTLS_SHA512_PROCESS_ALT) -/* - * Round constants - */ -static const uint64_t K[80] = -{ - 0x428A2F98D728AE22, 0x7137449123EF65CD, - 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC, - 0x3956C25BF348B538, 0x59F111F1B605D019, - 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118, - 0xD807AA98A3030242, 0x12835B0145706FBE, - 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2, - 0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, - 0x9BDC06A725C71235, 0xC19BF174CF692694, - 0xE49B69C19EF14AD2, 0xEFBE4786384F25E3, - 0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65, - 0x2DE92C6F592B0275, 0x4A7484AA6EA6E483, - 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5, - 0x983E5152EE66DFAB, 0xA831C66D2DB43210, - 0xB00327C898FB213F, 0xBF597FC7BEEF0EE4, - 0xC6E00BF33DA88FC2, 0xD5A79147930AA725, - 0x06CA6351E003826F, 0x142929670A0E6E70, - 0x27B70A8546D22FFC, 0x2E1B21385C26C926, - 0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF, - 0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, - 0x81C2C92E47EDAEE6, 0x92722C851482353B, - 0xA2BFE8A14CF10364, 0xA81A664BBC423001, - 0xC24B8B70D0F89791, 0xC76C51A30654BE30, - 0xD192E819D6EF5218, 0xD69906245565A910, - 0xF40E35855771202A, 0x106AA07032BBD1B8, - 0x19A4C116B8D2D0C8, 0x1E376C085141AB53, - 0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8, - 0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB, - 0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3, - 0x748F82EE5DEFB2FC, 0x78A5636F43172F60, - 0x84C87814A1F0AB72, 0x8CC702081A6439EC, - 0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, - 0xBEF9A3F7B2C67915, 0xC67178F2E372532B, - 0xCA273ECEEA26619C, 0xD186B8C721C0C207, - 0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178, - 0x06F067AA72176FBA, 0x0A637DC5A2C898A6, - 0x113F9804BEF90DAE, 0x1B710B35131C471B, - 0x28DB77F523047D84, 0x32CAAB7B40C72493, - 0x3C9EBE0A15C9BEBC, 0x431D67C49C100D4C, - 0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, - 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817, -}; - #define SHR(x,n) ((x) >> (n)) #define ROR(x,n) (SHR((x),(n)) | ((x) << (64 - (n)))) #define S0(x) (ROR(x, 1) ^ ROR(x, 8) ^ SHR(x, 7)) @@ -185,10 +139,10 @@ static const uint64_t K[80] = #define S3(x) (ROR(x,14) ^ ROR(x,18) ^ ROR(x,41)) #define F0(x,y,z) (((x) & (y)) | ((z) & ((x) | (y)))) #define F1(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) -#define P(a,b,c,d,e,f,g,h,x,K) \ +#define P(a,b,c,d,e,f,g,h,x,k) \ do \ { \ - local.temp1 = (h) + S3(e) + F1((e),(f),(g)) + (K) + (x); \ + local.temp1 = (h) + S3(e) + F1((e),(f),(g)) + (k) + (x); \ local.temp2 = S2(a) + F0((a),(b),(c)); \ (d) += local.temp1; (h) = local.temp1 + local.temp2; \ } while( 0 ) @@ -240,7 +194,7 @@ int mbedtls_internal_sha512_process( mbedtls_sha512_context *ctx, S0(local.W[i - 15]) + local.W[i - 16]; } P( local.A[0], local.A[1], local.A[2], local.A[3], local.A[4], - local.A[5], local.A[6], local.A[7], local.W[i], K[i] ); + local.A[5], local.A[6], local.A[7], local.W[i], kSha512[i] ); local.temp1 = local.A[7]; local.A[7] = local.A[6]; local.A[6] = local.A[5]; @@ -265,21 +219,21 @@ int mbedtls_internal_sha512_process( mbedtls_sha512_context *ctx, do { P( local.A[0], local.A[1], local.A[2], local.A[3], local.A[4], - local.A[5], local.A[6], local.A[7], local.W[i], K[i] ); i++; + local.A[5], local.A[6], local.A[7], local.W[i], kSha512[i] ); i++; P( local.A[7], local.A[0], local.A[1], local.A[2], local.A[3], - local.A[4], local.A[5], local.A[6], local.W[i], K[i] ); i++; + local.A[4], local.A[5], local.A[6], local.W[i], kSha512[i] ); i++; P( local.A[6], local.A[7], local.A[0], local.A[1], local.A[2], - local.A[3], local.A[4], local.A[5], local.W[i], K[i] ); i++; + local.A[3], local.A[4], local.A[5], local.W[i], kSha512[i] ); i++; P( local.A[5], local.A[6], local.A[7], local.A[0], local.A[1], - local.A[2], local.A[3], local.A[4], local.W[i], K[i] ); i++; + local.A[2], local.A[3], local.A[4], local.W[i], kSha512[i] ); i++; P( local.A[4], local.A[5], local.A[6], local.A[7], local.A[0], - local.A[1], local.A[2], local.A[3], local.W[i], K[i] ); i++; + local.A[1], local.A[2], local.A[3], local.W[i], kSha512[i] ); i++; P( local.A[3], local.A[4], local.A[5], local.A[6], local.A[7], - local.A[0], local.A[1], local.A[2], local.W[i], K[i] ); i++; + local.A[0], local.A[1], local.A[2], local.W[i], kSha512[i] ); i++; P( local.A[2], local.A[3], local.A[4], local.A[5], local.A[6], - local.A[7], local.A[0], local.A[1], local.W[i], K[i] ); i++; + local.A[7], local.A[0], local.A[1], local.W[i], kSha512[i] ); i++; P( local.A[1], local.A[2], local.A[3], local.A[4], local.A[5], - local.A[6], local.A[7], local.A[0], local.W[i], K[i] ); i++; + local.A[6], local.A[7], local.A[0], local.W[i], kSha512[i] ); i++; } while( i < 80 ); #endif /* MBEDTLS_SHA512_SMALLER */ diff --git a/third_party/musl/crypt.c b/third_party/musl/crypt.c new file mode 100644 index 000000000..570a77d58 --- /dev/null +++ b/third_party/musl/crypt.c @@ -0,0 +1,61 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "third_party/musl/crypt.h" +#include "third_party/musl/crypt.internal.h" + +asm(".ident\t\"\\n\\n\ +Musl libc (MIT License)\\n\ +Copyright 2005-2014 Rich Felker, et. al.\""); +asm(".include \"libc/disclaimer.inc\""); +// clang-format off + +/** + * Encrypts password the old fashioned way. + * + * The method of encryption depends on the first three chars of salt: + * + * - `$1$` is MD5 + * - `$2$` is Blowfish + * - `$5$` is SHA-256 + * - `$6$` is SHA-512 + * - Otherwise DES + * + * @return static memory with encrypted password + * @see third_party/argon2/ + */ +char *crypt(const char *key, const char *salt) +{ + /* This buffer is sufficiently large for all + * currently-supported hash types. It needs to be updated if + * longer hashes are added. The cast to struct crypt_data * is + * purely to meet the public API requirements of the crypt_r + * function; the implementation of crypt_r uses the object + * purely as a char buffer. */ + static char buf[128]; + return crypt_r(key, salt, (struct crypt_data *)buf); +} diff --git a/third_party/musl/crypt.h b/third_party/musl/crypt.h new file mode 100644 index 000000000..087400dc5 --- /dev/null +++ b/third_party/musl/crypt.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_MUSL_CRYPT_H_ +#define COSMOPOLITAN_THIRD_PARTY_MUSL_CRYPT_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct crypt_data { + int initialized; + char __buf[256]; +}; + +char *crypt(const char *, const char *); +char *crypt_r(const char *, const char *, struct crypt_data *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_THIRD_PARTY_MUSL_CRYPT_H_ */ diff --git a/third_party/musl/crypt.internal.h b/third_party/musl/crypt.internal.h new file mode 100644 index 000000000..931e373d1 --- /dev/null +++ b/third_party/musl/crypt.internal.h @@ -0,0 +1,15 @@ +#ifndef COSMOPOLITAN_THIRD_PARTY_MUSL_CRYPT_INTERNAL_H_ +#define COSMOPOLITAN_THIRD_PARTY_MUSL_CRYPT_INTERNAL_H_ +#include "third_party/musl/crypt.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +hidden char *__crypt_des(const char *, const char *, char *); +hidden char *__crypt_md5(const char *, const char *, char *); +hidden char *__crypt_blowfish(const char *, const char *, char *); +hidden char *__crypt_sha256(const char *, const char *, char *); +hidden char *__crypt_sha512(const char *, const char *, char *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_THIRD_PARTY_MUSL_CRYPT_INTERNAL_H_ */ diff --git a/third_party/musl/crypt_blowfish.c b/third_party/musl/crypt_blowfish.c new file mode 100644 index 000000000..db6b9a394 --- /dev/null +++ b/third_party/musl/crypt_blowfish.c @@ -0,0 +1,846 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/alg/alg.h" +#include "libc/limits.h" +#include "libc/literal.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.internal.h" +#include "libc/str/str.h" +#include "third_party/musl/crypt.internal.h" + +asm(".ident\t\"\\n\\n\ +Musl libc (MIT License)\\n\ +Copyright 2005-2014 Rich Felker, et. al.\""); +asm(".include \"libc/disclaimer.inc\""); +// clang-format off + +/* Modified by Rich Felker in for inclusion in musl libc, based on + * Solar Designer's second size-optimized version sent to the musl + * mailing list. */ + +/* + * The crypt_blowfish homepage is: + * + * http://www.openwall.com/crypt/ + * + * This code comes from John the Ripper password cracker, with reentrant + * and crypt(3) interfaces added, but optimizations specific to password + * cracking removed. + * + * Written by Solar Designer in 1998-2012. + * No copyright is claimed, and the software is hereby placed in the public + * domain. In case this attempt to disclaim copyright and place the software + * in the public domain is deemed null and void, then the software is + * Copyright (c) 1998-2014 Solar Designer and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * It is my intent that you should be able to use this on your system, + * as part of a software package, or anywhere else to improve security, + * ensure compatibility, or for any other purpose. I would appreciate + * it if you give credit where it is due and keep your modifications in + * the public domain as well, but I don't require that in order to let + * you place this code and any modifications you make under a license + * of your choice. + * + * This implementation is fully compatible with OpenBSD's bcrypt.c for prefix + * "$2b$", originally by Niels Provos , and it uses + * some of his ideas. The password hashing algorithm was designed by David + * Mazieres . For information on the level of + * compatibility for bcrypt hash prefixes other than "$2b$", please refer to + * the comments in BF_set_key() below and to the included crypt(3) man page. + * + * There's a paper on the algorithm that explains its design decisions: + * + * http://www.usenix.org/events/usenix99/provos.html + * + * Some of the tricks in BF_ROUND might be inspired by Eric Young's + * Blowfish library (I can't be sure if I would think of something if I + * hadn't seen his code). + */ + +typedef uint32_t BF_word; +typedef int32_t BF_word_signed; + +/* Number of Blowfish rounds, this is also hardcoded into a few places */ +#define BF_N 16 + +typedef BF_word BF_key[BF_N + 2]; + +typedef union { + struct { + BF_key P; + BF_word S[4][0x100]; + } s; + BF_word PS[BF_N + 2 + 4 * 0x100]; +} BF_ctx; + +/* + * Magic IV for 64 Blowfish encryptions that we do at the end. + * The string is "OrpheanBeholderScryDoubt" on big-endian. + */ +static const BF_word BF_magic_w[6] = { + 0x4F727068, 0x65616E42, 0x65686F6C, + 0x64657253, 0x63727944, 0x6F756274 +}; + +/* + * P-box and S-box tables initialized with digits of Pi. + */ +static const BF_ctx BF_init_state = {{ + { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b + }, { + { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a + }, { + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 + }, { + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 + }, { + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 + } + } +}}; + +static const unsigned char BF_itoa64[64 + 1] = + "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +static const unsigned char BF_atoi64[0x60] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 64, 64, + 64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 64, 64, 64, 64, 64, + 64, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 64, 64, 64, 64, 64 +}; + +#define BF_safe_atoi64(dst, src) \ +{ \ + tmp = (unsigned char)(src); \ + if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \ + tmp = BF_atoi64[tmp]; \ + if (tmp > 63) return -1; \ + (dst) = tmp; \ +} + +static int BF_decode(BF_word *dst, const char *src, int size) +{ + unsigned char *dptr = (unsigned char *)dst; + unsigned char *end = dptr + size; + const unsigned char *sptr = (const unsigned char *)src; + unsigned int tmp, c1, c2, c3, c4; + + do { + BF_safe_atoi64(c1, *sptr++); + BF_safe_atoi64(c2, *sptr++); + *dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4); + if (dptr >= end) break; + + BF_safe_atoi64(c3, *sptr++); + *dptr++ = ((c2 & 0x0F) << 4) | ((c3 & 0x3C) >> 2); + if (dptr >= end) break; + + BF_safe_atoi64(c4, *sptr++); + *dptr++ = ((c3 & 0x03) << 6) | c4; + } while (dptr < end); + + return 0; +} + +static void BF_encode(char *dst, const BF_word *src, int size) +{ + const unsigned char *sptr = (const unsigned char *)src; + const unsigned char *end = sptr + size; + unsigned char *dptr = (unsigned char *)dst; + unsigned int c1, c2; + + do { + c1 = *sptr++; + *dptr++ = BF_itoa64[c1 >> 2]; + c1 = (c1 & 0x03) << 4; + if (sptr >= end) { + *dptr++ = BF_itoa64[c1]; + break; + } + + c2 = *sptr++; + c1 |= c2 >> 4; + *dptr++ = BF_itoa64[c1]; + c1 = (c2 & 0x0f) << 2; + if (sptr >= end) { + *dptr++ = BF_itoa64[c1]; + break; + } + + c2 = *sptr++; + c1 |= c2 >> 6; + *dptr++ = BF_itoa64[c1]; + *dptr++ = BF_itoa64[c2 & 0x3f]; + } while (sptr < end); +} + +static void BF_swap(BF_word *x, int count) +{ + if ((union { int i; char c; }){1}.c) + do { + BF_word tmp = *x; + tmp = (tmp << 16) | (tmp >> 16); + *x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF); + } while (--count); +} + +#define BF_ROUND(L, R, N) \ + tmp1 = L & 0xFF; \ + tmp2 = L >> 8; \ + tmp2 &= 0xFF; \ + tmp3 = L >> 16; \ + tmp3 &= 0xFF; \ + tmp4 = L >> 24; \ + tmp1 = ctx->s.S[3][tmp1]; \ + tmp2 = ctx->s.S[2][tmp2]; \ + tmp3 = ctx->s.S[1][tmp3]; \ + tmp3 += ctx->s.S[0][tmp4]; \ + tmp3 ^= tmp2; \ + R ^= ctx->s.P[N + 1]; \ + tmp3 += tmp1; \ + R ^= tmp3; + +static BF_word BF_encrypt(BF_ctx *ctx, + BF_word L, BF_word R, + BF_word *start, BF_word *end) +{ + BF_word tmp1, tmp2, tmp3, tmp4; + BF_word *ptr = start; + + do { + L ^= ctx->s.P[0]; +#if 0 + BF_ROUND(L, R, 0); + BF_ROUND(R, L, 1); + BF_ROUND(L, R, 2); + BF_ROUND(R, L, 3); + BF_ROUND(L, R, 4); + BF_ROUND(R, L, 5); + BF_ROUND(L, R, 6); + BF_ROUND(R, L, 7); + BF_ROUND(L, R, 8); + BF_ROUND(R, L, 9); + BF_ROUND(L, R, 10); + BF_ROUND(R, L, 11); + BF_ROUND(L, R, 12); + BF_ROUND(R, L, 13); + BF_ROUND(L, R, 14); + BF_ROUND(R, L, 15); +#else + for (int i=0; i<16; i+=2) { + BF_ROUND(L, R, i); + BF_ROUND(R, L, i+1); + } +#endif + tmp4 = R; + R = L; + L = tmp4 ^ ctx->s.P[BF_N + 1]; + *ptr++ = L; + *ptr++ = R; + } while (ptr < end); + + return L; +} + +static void BF_set_key(const char *key, BF_key expanded, BF_key initial, + unsigned char flags) +{ + const char *ptr = key; + unsigned int bug, i, j; + BF_word safety, sign, diff, tmp[2]; + +/* + * There was a sign extension bug in older revisions of this function. While + * we would have liked to simply fix the bug and move on, we have to provide + * a backwards compatibility feature (essentially the bug) for some systems and + * a safety measure for some others. The latter is needed because for certain + * multiple inputs to the buggy algorithm there exist easily found inputs to + * the correct algorithm that produce the same hash. Thus, we optionally + * deviate from the correct algorithm just enough to avoid such collisions. + * While the bug itself affected the majority of passwords containing + * characters with the 8th bit set (although only a percentage of those in a + * collision-producing way), the anti-collision safety measure affects + * only a subset of passwords containing the '\xff' character (not even all of + * those passwords, just some of them). This character is not found in valid + * UTF-8 sequences and is rarely used in popular 8-bit character encodings. + * Thus, the safety measure is unlikely to cause much annoyance, and is a + * reasonable tradeoff to use when authenticating against existing hashes that + * are not reliably known to have been computed with the correct algorithm. + * + * We use an approach that tries to minimize side-channel leaks of password + * information - that is, we mostly use fixed-cost bitwise operations instead + * of branches or table lookups. (One conditional branch based on password + * length remains. It is not part of the bug aftermath, though, and is + * difficult and possibly unreasonable to avoid given the use of C strings by + * the caller, which results in similar timing leaks anyway.) + * + * For actual implementation, we set an array index in the variable "bug" + * (0 means no bug, 1 means sign extension bug emulation) and a flag in the + * variable "safety" (bit 16 is set when the safety measure is requested). + * Valid combinations of settings are: + * + * Prefix "$2a$": bug = 0, safety = 0x10000 + * Prefix "$2b$": bug = 0, safety = 0 + * Prefix "$2x$": bug = 1, safety = 0 + * Prefix "$2y$": bug = 0, safety = 0 + */ + bug = flags & 1; + safety = ((BF_word)flags & 2) << 15; + + sign = diff = 0; + + for (i = 0; i < BF_N + 2; i++) { + tmp[0] = tmp[1] = 0; + for (j = 0; j < 4; j++) { + tmp[0] <<= 8; + tmp[0] |= (unsigned char)*ptr; /* correct */ + tmp[1] <<= 8; + tmp[1] |= (signed char)*ptr; /* bug */ +/* + * Sign extension in the first char has no effect - nothing to overwrite yet, + * and those extra 24 bits will be fully shifted out of the 32-bit word. For + * chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign + * extension in tmp[1] occurs. Once this flag is set, it remains set. + */ + if (j) + sign |= tmp[1] & 0x80; + if (!*ptr) + ptr = key; + else + ptr++; + } + diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */ + + expanded[i] = tmp[bug]; + initial[i] = BF_init_state.s.P[i] ^ tmp[bug]; + } + +/* + * At this point, "diff" is zero iff the correct and buggy algorithms produced + * exactly the same result. If so and if "sign" is non-zero, which indicates + * that there was a non-benign sign extension, this means that we have a + * collision between the correctly computed hash for this password and a set of + * passwords that could be supplied to the buggy algorithm. Our safety measure + * is meant to protect from such many-buggy to one-correct collisions, by + * deviating from the correct algorithm in such cases. Let's check for this. + */ + diff |= diff >> 16; /* still zero iff exact match */ + diff &= 0xffff; /* ditto */ + diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */ + sign <<= 9; /* move the non-benign sign extension flag to bit 16 */ + sign &= ~diff & safety; /* action needed? */ + +/* + * If we have determined that we need to deviate from the correct algorithm, + * flip bit 16 in initial expanded key. (The choice of 16 is arbitrary, but + * let's stick to it now. It came out of the approach we used above, and it's + * not any worse than any other choice we could make.) + * + * It is crucial that we don't do the same to the expanded key used in the main + * Eksblowfish loop. By doing it to only one of these two, we deviate from a + * state that could be directly specified by a password to the buggy algorithm + * (and to the fully correct one as well, but that's a side-effect). + */ + initial[0] ^= sign; +} + +static const unsigned char flags_by_subtype[26] = { + 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0 +}; + +static char *BF_crypt(const char *key, const char *setting, + char *output, BF_word min) +{ + struct { + BF_ctx ctx; + BF_key expanded_key; + union { + BF_word salt[4]; + BF_word output[6]; + } binary; + } *data; + BF_word count; + int i; + + if (!(data = gc(malloc(sizeof(*data))))) return 0; + + if (setting[0] != '$' || + setting[1] != '2' || + setting[2] - 'a' > 25U || + !flags_by_subtype[setting[2] - 'a'] || + setting[3] != '$' || + setting[4] - '0' > 1U || + setting[5] - '0' > 9U || + setting[6] != '$') { + return NULL; + } + + count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0')); + if (count < min || BF_decode(data->binary.salt, &setting[7], 16)) { + return NULL; + } + BF_swap(data->binary.salt, 4); + + BF_set_key(key, data->expanded_key, data->ctx.s.P, + flags_by_subtype[setting[2] - 'a']); + + memcpy(data->ctx.s.S, BF_init_state.s.S, sizeof(data->ctx.s.S)); + + { + BF_word L = 0, R = 0; + BF_word *ptr = &data->ctx.PS[0]; + do { + L = BF_encrypt(&data->ctx, + L ^ data->binary.salt[0], R ^ data->binary.salt[1], + ptr, ptr); + R = *(ptr + 1); + ptr += 2; + + if (ptr >= &data->ctx.PS[BF_N + 2 + 4 * 0x100]) + break; + + L = BF_encrypt(&data->ctx, + L ^ data->binary.salt[2], R ^ data->binary.salt[3], + ptr, ptr); + R = *(ptr + 1); + ptr += 2; + } while (1); + } + + do { + int done; + + for (i = 0; i < BF_N + 2; i += 2) { + data->ctx.s.P[i] ^= data->expanded_key[i]; + data->ctx.s.P[i + 1] ^= data->expanded_key[i + 1]; + } + + done = 0; + do { + BF_encrypt(&data->ctx, 0, 0, + &data->ctx.PS[0], + &data->ctx.PS[BF_N + 2 + 4 * 0x100]); + + if (done) + break; + done = 1; + + { + BF_word tmp1, tmp2, tmp3, tmp4; + + tmp1 = data->binary.salt[0]; + tmp2 = data->binary.salt[1]; + tmp3 = data->binary.salt[2]; + tmp4 = data->binary.salt[3]; + for (i = 0; i < BF_N; i += 4) { + data->ctx.s.P[i] ^= tmp1; + data->ctx.s.P[i + 1] ^= tmp2; + data->ctx.s.P[i + 2] ^= tmp3; + data->ctx.s.P[i + 3] ^= tmp4; + } + data->ctx.s.P[16] ^= tmp1; + data->ctx.s.P[17] ^= tmp2; + } + } while (1); + } while (--count); + + for (i = 0; i < 6; i += 2) { + BF_word L, LR[2]; + + L = BF_magic_w[i]; + LR[1] = BF_magic_w[i + 1]; + + count = 64; + do { + L = BF_encrypt(&data->ctx, L, LR[1], + &LR[0], &LR[0]); + } while (--count); + + data->binary.output[i] = L; + data->binary.output[i + 1] = LR[1]; + } + + memcpy(output, setting, 7 + 22 - 1); + output[7 + 22 - 1] = BF_itoa64[ + BF_atoi64[setting[7 + 22 - 1] - 0x20] & 0x30]; + +/* This has to be bug-compatible with the original implementation, so + * only encode 23 of the 24 bytes. :-) */ + BF_swap(data->binary.output, 6); + BF_encode(&output[7 + 22], data->binary.output, 23); + output[7 + 22 + 31] = '\0'; + + return output; +} + +/* + * Please preserve the runtime self-test. It serves two purposes at once: + * + * 1. We really can't afford the risk of producing incompatible hashes e.g. + * when there's something like gcc bug 26587 again, whereas an application or + * library integrating this code might not also integrate our external tests or + * it might not run them after every build. Even if it does, the miscompile + * might only occur on the production build, but not on a testing build (such + * as because of different optimization settings). It is painful to recover + * from incorrectly-computed hashes - merely fixing whatever broke is not + * enough. Thus, a proactive measure like this self-test is needed. + * + * 2. We don't want to leave sensitive data from our actual password hash + * computation on the stack or in registers. Previous revisions of the code + * would do explicit cleanups, but simply running the self-test after hash + * computation is more reliable. + * + * The performance cost of this quick self-test is around 0.6% at the "$2a$08" + * setting. + */ +char *__crypt_blowfish(const char *key, const char *setting, char *output) +{ + const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8"; + const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu"; + static const char test_hashes[2][34] = { + "i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55", /* 'a', 'b', 'y' */ + "VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55", /* 'x' */ + }; + const char *test_hash = test_hashes[0]; + char *retval; + const char *p; + int ok; + struct { + char s[7 + 22 + 1]; + char o[7 + 22 + 31 + 1 + 1 + 1]; + } buf; + +/* Hash the supplied password */ + retval = BF_crypt(key, setting, output, 16); + +/* + * Do a quick self-test. It is important that we make both calls to BF_crypt() + * from the same scope such that they likely use the same stack locations, + * which makes the second call overwrite the first call's sensitive data on the + * stack and makes it more likely that any alignment related issues would be + * detected by the self-test. + */ + memcpy(buf.s, test_setting, sizeof(buf.s)); + if (retval) { + unsigned int flags = flags_by_subtype[setting[2] - 'a']; + test_hash = test_hashes[flags & 1]; + buf.s[2] = setting[2]; + } + memset(buf.o, 0x55, sizeof(buf.o)); + buf.o[sizeof(buf.o) - 1] = 0; + p = BF_crypt(test_key, buf.s, buf.o, 1); + + ok = (p == buf.o && + !memcmp(p, buf.s, 7 + 22) && + !memcmp(p + (7 + 22), + test_hash, + 31 + 1 + 1 + 1)); + + { + const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345"; + BF_key ae, ai, ye, yi; + BF_set_key(k, ae, ai, 2); /* $2a$ */ + BF_set_key(k, ye, yi, 4); /* $2y$ */ + ai[0] ^= 0x10000; /* undo the safety (for comparison) */ + ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 && + !memcmp(ae, ye, sizeof(ae)) && + !memcmp(ai, yi, sizeof(ai)); + } + + if (ok && retval) + return retval; + + return "*"; +} diff --git a/third_party/musl/crypt_des.c b/third_party/musl/crypt_des.c new file mode 100644 index 000000000..75d2f178f --- /dev/null +++ b/third_party/musl/crypt_des.c @@ -0,0 +1,1051 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/alg/alg.h" +#include "libc/limits.h" +#include "libc/literal.h" +#include "libc/str/str.h" +#include "third_party/musl/crypt.internal.h" +#include "third_party/musl/crypt_des.internal.h" + +asm(".ident\t\"\\n\\n\ +Musl libc (MIT License)\\n\ +Copyright 2005-2014 Rich Felker, et. al.\""); +asm(".include \"libc/disclaimer.inc\""); +// clang-format off + +/* + * This version has been further modified by Rich Felker, primary author + * and maintainer of musl libc, to remove table generation code and + * replaced all runtime-generated constant tables with static-initialized + * tables in the binary, in the interest of minimizing non-shareable + * memory usage and stack size requirements. + */ +/* + * This version is derived from the original implementation of FreeSec + * (release 1.1) by David Burren. I've made it reentrant, reduced its memory + * usage from about 70 KB to about 7 KB (with only minimal performance impact + * and keeping code size about the same), made the handling of invalid salts + * mostly UFC-crypt compatible, added a quick runtime self-test (which also + * serves to zeroize the stack from sensitive data), and added optional tests. + * - Solar Designer + */ + +/* + * FreeSec: libcrypt for NetBSD + * + * Copyright (c) 1994 David Burren + * Copyright (c) 2000,2002,2010,2012 Solar Designer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of other contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Owl: Owl/packages/glibc/crypt_freesec.c,v 1.6 2010/02/20 14:45:06 solar Exp $ + * $Id: crypt.c,v 1.15 1994/09/13 04:58:49 davidb Exp $ + * + * This is an original implementation of the DES and the crypt(3) interfaces + * by David Burren. It has been heavily re-worked by Solar Designer. + */ + +#define _PASSWORD_EFMT1 '_' + +static const unsigned char key_shifts[16] = { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 +}; + +static const uint32_t psbox[8][64] = { + { + 0x00808200,0x00000000,0x00008000,0x00808202, + 0x00808002,0x00008202,0x00000002,0x00008000, + 0x00000200,0x00808200,0x00808202,0x00000200, + 0x00800202,0x00808002,0x00800000,0x00000002, + 0x00000202,0x00800200,0x00800200,0x00008200, + 0x00008200,0x00808000,0x00808000,0x00800202, + 0x00008002,0x00800002,0x00800002,0x00008002, + 0x00000000,0x00000202,0x00008202,0x00800000, + 0x00008000,0x00808202,0x00000002,0x00808000, + 0x00808200,0x00800000,0x00800000,0x00000200, + 0x00808002,0x00008000,0x00008200,0x00800002, + 0x00000200,0x00000002,0x00800202,0x00008202, + 0x00808202,0x00008002,0x00808000,0x00800202, + 0x00800002,0x00000202,0x00008202,0x00808200, + 0x00000202,0x00800200,0x00800200,0x00000000, + 0x00008002,0x00008200,0x00000000,0x00808002, + },{ + 0x40084010,0x40004000,0x00004000,0x00084010, + 0x00080000,0x00000010,0x40080010,0x40004010, + 0x40000010,0x40084010,0x40084000,0x40000000, + 0x40004000,0x00080000,0x00000010,0x40080010, + 0x00084000,0x00080010,0x40004010,0x00000000, + 0x40000000,0x00004000,0x00084010,0x40080000, + 0x00080010,0x40000010,0x00000000,0x00084000, + 0x00004010,0x40084000,0x40080000,0x00004010, + 0x00000000,0x00084010,0x40080010,0x00080000, + 0x40004010,0x40080000,0x40084000,0x00004000, + 0x40080000,0x40004000,0x00000010,0x40084010, + 0x00084010,0x00000010,0x00004000,0x40000000, + 0x00004010,0x40084000,0x00080000,0x40000010, + 0x00080010,0x40004010,0x40000010,0x00080010, + 0x00084000,0x00000000,0x40004000,0x00004010, + 0x40000000,0x40080010,0x40084010,0x00084000, + },{ + 0x00000104,0x04010100,0x00000000,0x04010004, + 0x04000100,0x00000000,0x00010104,0x04000100, + 0x00010004,0x04000004,0x04000004,0x00010000, + 0x04010104,0x00010004,0x04010000,0x00000104, + 0x04000000,0x00000004,0x04010100,0x00000100, + 0x00010100,0x04010000,0x04010004,0x00010104, + 0x04000104,0x00010100,0x00010000,0x04000104, + 0x00000004,0x04010104,0x00000100,0x04000000, + 0x04010100,0x04000000,0x00010004,0x00000104, + 0x00010000,0x04010100,0x04000100,0x00000000, + 0x00000100,0x00010004,0x04010104,0x04000100, + 0x04000004,0x00000100,0x00000000,0x04010004, + 0x04000104,0x00010000,0x04000000,0x04010104, + 0x00000004,0x00010104,0x00010100,0x04000004, + 0x04010000,0x04000104,0x00000104,0x04010000, + 0x00010104,0x00000004,0x04010004,0x00010100, + },{ + 0x80401000,0x80001040,0x80001040,0x00000040, + 0x00401040,0x80400040,0x80400000,0x80001000, + 0x00000000,0x00401000,0x00401000,0x80401040, + 0x80000040,0x00000000,0x00400040,0x80400000, + 0x80000000,0x00001000,0x00400000,0x80401000, + 0x00000040,0x00400000,0x80001000,0x00001040, + 0x80400040,0x80000000,0x00001040,0x00400040, + 0x00001000,0x00401040,0x80401040,0x80000040, + 0x00400040,0x80400000,0x00401000,0x80401040, + 0x80000040,0x00000000,0x00000000,0x00401000, + 0x00001040,0x00400040,0x80400040,0x80000000, + 0x80401000,0x80001040,0x80001040,0x00000040, + 0x80401040,0x80000040,0x80000000,0x00001000, + 0x80400000,0x80001000,0x00401040,0x80400040, + 0x80001000,0x00001040,0x00400000,0x80401000, + 0x00000040,0x00400000,0x00001000,0x00401040, + },{ + 0x00000080,0x01040080,0x01040000,0x21000080, + 0x00040000,0x00000080,0x20000000,0x01040000, + 0x20040080,0x00040000,0x01000080,0x20040080, + 0x21000080,0x21040000,0x00040080,0x20000000, + 0x01000000,0x20040000,0x20040000,0x00000000, + 0x20000080,0x21040080,0x21040080,0x01000080, + 0x21040000,0x20000080,0x00000000,0x21000000, + 0x01040080,0x01000000,0x21000000,0x00040080, + 0x00040000,0x21000080,0x00000080,0x01000000, + 0x20000000,0x01040000,0x21000080,0x20040080, + 0x01000080,0x20000000,0x21040000,0x01040080, + 0x20040080,0x00000080,0x01000000,0x21040000, + 0x21040080,0x00040080,0x21000000,0x21040080, + 0x01040000,0x00000000,0x20040000,0x21000000, + 0x00040080,0x01000080,0x20000080,0x00040000, + 0x00000000,0x20040000,0x01040080,0x20000080, + },{ + 0x10000008,0x10200000,0x00002000,0x10202008, + 0x10200000,0x00000008,0x10202008,0x00200000, + 0x10002000,0x00202008,0x00200000,0x10000008, + 0x00200008,0x10002000,0x10000000,0x00002008, + 0x00000000,0x00200008,0x10002008,0x00002000, + 0x00202000,0x10002008,0x00000008,0x10200008, + 0x10200008,0x00000000,0x00202008,0x10202000, + 0x00002008,0x00202000,0x10202000,0x10000000, + 0x10002000,0x00000008,0x10200008,0x00202000, + 0x10202008,0x00200000,0x00002008,0x10000008, + 0x00200000,0x10002000,0x10000000,0x00002008, + 0x10000008,0x10202008,0x00202000,0x10200000, + 0x00202008,0x10202000,0x00000000,0x10200008, + 0x00000008,0x00002000,0x10200000,0x00202008, + 0x00002000,0x00200008,0x10002008,0x00000000, + 0x10202000,0x10000000,0x00200008,0x10002008, + },{ + 0x00100000,0x02100001,0x02000401,0x00000000, + 0x00000400,0x02000401,0x00100401,0x02100400, + 0x02100401,0x00100000,0x00000000,0x02000001, + 0x00000001,0x02000000,0x02100001,0x00000401, + 0x02000400,0x00100401,0x00100001,0x02000400, + 0x02000001,0x02100000,0x02100400,0x00100001, + 0x02100000,0x00000400,0x00000401,0x02100401, + 0x00100400,0x00000001,0x02000000,0x00100400, + 0x02000000,0x00100400,0x00100000,0x02000401, + 0x02000401,0x02100001,0x02100001,0x00000001, + 0x00100001,0x02000000,0x02000400,0x00100000, + 0x02100400,0x00000401,0x00100401,0x02100400, + 0x00000401,0x02000001,0x02100401,0x02100000, + 0x00100400,0x00000000,0x00000001,0x02100401, + 0x00000000,0x00100401,0x02100000,0x00000400, + 0x02000001,0x02000400,0x00000400,0x00100001, + },{ + 0x08000820,0x00000800,0x00020000,0x08020820, + 0x08000000,0x08000820,0x00000020,0x08000000, + 0x00020020,0x08020000,0x08020820,0x00020800, + 0x08020800,0x00020820,0x00000800,0x00000020, + 0x08020000,0x08000020,0x08000800,0x00000820, + 0x00020800,0x00020020,0x08020020,0x08020800, + 0x00000820,0x00000000,0x00000000,0x08020020, + 0x08000020,0x08000800,0x00020820,0x00020000, + 0x00020820,0x00020000,0x08020800,0x00000800, + 0x00000020,0x08020020,0x00000800,0x00020820, + 0x08000800,0x00000020,0x08000020,0x08020000, + 0x08020020,0x08000000,0x00020000,0x08000820, + 0x00000000,0x08020820,0x00020020,0x08000020, + 0x08020000,0x08000800,0x08000820,0x00000000, + 0x08020820,0x00020800,0x00020800,0x00000820, + 0x00000820,0x00020020,0x08000000,0x08020800, + }, +}; +static const uint32_t ip_maskl[16][16] = { + { + 0x00000000,0x00010000,0x00000000,0x00010000, + 0x01000000,0x01010000,0x01000000,0x01010000, + 0x00000000,0x00010000,0x00000000,0x00010000, + 0x01000000,0x01010000,0x01000000,0x01010000, + },{ + 0x00000000,0x00000001,0x00000000,0x00000001, + 0x00000100,0x00000101,0x00000100,0x00000101, + 0x00000000,0x00000001,0x00000000,0x00000001, + 0x00000100,0x00000101,0x00000100,0x00000101, + },{ + 0x00000000,0x00020000,0x00000000,0x00020000, + 0x02000000,0x02020000,0x02000000,0x02020000, + 0x00000000,0x00020000,0x00000000,0x00020000, + 0x02000000,0x02020000,0x02000000,0x02020000, + },{ + 0x00000000,0x00000002,0x00000000,0x00000002, + 0x00000200,0x00000202,0x00000200,0x00000202, + 0x00000000,0x00000002,0x00000000,0x00000002, + 0x00000200,0x00000202,0x00000200,0x00000202, + },{ + 0x00000000,0x00040000,0x00000000,0x00040000, + 0x04000000,0x04040000,0x04000000,0x04040000, + 0x00000000,0x00040000,0x00000000,0x00040000, + 0x04000000,0x04040000,0x04000000,0x04040000, + },{ + 0x00000000,0x00000004,0x00000000,0x00000004, + 0x00000400,0x00000404,0x00000400,0x00000404, + 0x00000000,0x00000004,0x00000000,0x00000004, + 0x00000400,0x00000404,0x00000400,0x00000404, + },{ + 0x00000000,0x00080000,0x00000000,0x00080000, + 0x08000000,0x08080000,0x08000000,0x08080000, + 0x00000000,0x00080000,0x00000000,0x00080000, + 0x08000000,0x08080000,0x08000000,0x08080000, + },{ + 0x00000000,0x00000008,0x00000000,0x00000008, + 0x00000800,0x00000808,0x00000800,0x00000808, + 0x00000000,0x00000008,0x00000000,0x00000008, + 0x00000800,0x00000808,0x00000800,0x00000808, + },{ + 0x00000000,0x00100000,0x00000000,0x00100000, + 0x10000000,0x10100000,0x10000000,0x10100000, + 0x00000000,0x00100000,0x00000000,0x00100000, + 0x10000000,0x10100000,0x10000000,0x10100000, + },{ + 0x00000000,0x00000010,0x00000000,0x00000010, + 0x00001000,0x00001010,0x00001000,0x00001010, + 0x00000000,0x00000010,0x00000000,0x00000010, + 0x00001000,0x00001010,0x00001000,0x00001010, + },{ + 0x00000000,0x00200000,0x00000000,0x00200000, + 0x20000000,0x20200000,0x20000000,0x20200000, + 0x00000000,0x00200000,0x00000000,0x00200000, + 0x20000000,0x20200000,0x20000000,0x20200000, + },{ + 0x00000000,0x00000020,0x00000000,0x00000020, + 0x00002000,0x00002020,0x00002000,0x00002020, + 0x00000000,0x00000020,0x00000000,0x00000020, + 0x00002000,0x00002020,0x00002000,0x00002020, + },{ + 0x00000000,0x00400000,0x00000000,0x00400000, + 0x40000000,0x40400000,0x40000000,0x40400000, + 0x00000000,0x00400000,0x00000000,0x00400000, + 0x40000000,0x40400000,0x40000000,0x40400000, + },{ + 0x00000000,0x00000040,0x00000000,0x00000040, + 0x00004000,0x00004040,0x00004000,0x00004040, + 0x00000000,0x00000040,0x00000000,0x00000040, + 0x00004000,0x00004040,0x00004000,0x00004040, + },{ + 0x00000000,0x00800000,0x00000000,0x00800000, + 0x80000000,0x80800000,0x80000000,0x80800000, + 0x00000000,0x00800000,0x00000000,0x00800000, + 0x80000000,0x80800000,0x80000000,0x80800000, + },{ + 0x00000000,0x00000080,0x00000000,0x00000080, + 0x00008000,0x00008080,0x00008000,0x00008080, + 0x00000000,0x00000080,0x00000000,0x00000080, + 0x00008000,0x00008080,0x00008000,0x00008080, + }, +}; +static const uint32_t ip_maskr[16][16] = { + { + 0x00000000,0x00000000,0x00010000,0x00010000, + 0x00000000,0x00000000,0x00010000,0x00010000, + 0x01000000,0x01000000,0x01010000,0x01010000, + 0x01000000,0x01000000,0x01010000,0x01010000, + },{ + 0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000100,0x00000100,0x00000101,0x00000101, + 0x00000100,0x00000100,0x00000101,0x00000101, + },{ + 0x00000000,0x00000000,0x00020000,0x00020000, + 0x00000000,0x00000000,0x00020000,0x00020000, + 0x02000000,0x02000000,0x02020000,0x02020000, + 0x02000000,0x02000000,0x02020000,0x02020000, + },{ + 0x00000000,0x00000000,0x00000002,0x00000002, + 0x00000000,0x00000000,0x00000002,0x00000002, + 0x00000200,0x00000200,0x00000202,0x00000202, + 0x00000200,0x00000200,0x00000202,0x00000202, + },{ + 0x00000000,0x00000000,0x00040000,0x00040000, + 0x00000000,0x00000000,0x00040000,0x00040000, + 0x04000000,0x04000000,0x04040000,0x04040000, + 0x04000000,0x04000000,0x04040000,0x04040000, + },{ + 0x00000000,0x00000000,0x00000004,0x00000004, + 0x00000000,0x00000000,0x00000004,0x00000004, + 0x00000400,0x00000400,0x00000404,0x00000404, + 0x00000400,0x00000400,0x00000404,0x00000404, + },{ + 0x00000000,0x00000000,0x00080000,0x00080000, + 0x00000000,0x00000000,0x00080000,0x00080000, + 0x08000000,0x08000000,0x08080000,0x08080000, + 0x08000000,0x08000000,0x08080000,0x08080000, + },{ + 0x00000000,0x00000000,0x00000008,0x00000008, + 0x00000000,0x00000000,0x00000008,0x00000008, + 0x00000800,0x00000800,0x00000808,0x00000808, + 0x00000800,0x00000800,0x00000808,0x00000808, + },{ + 0x00000000,0x00000000,0x00100000,0x00100000, + 0x00000000,0x00000000,0x00100000,0x00100000, + 0x10000000,0x10000000,0x10100000,0x10100000, + 0x10000000,0x10000000,0x10100000,0x10100000, + },{ + 0x00000000,0x00000000,0x00000010,0x00000010, + 0x00000000,0x00000000,0x00000010,0x00000010, + 0x00001000,0x00001000,0x00001010,0x00001010, + 0x00001000,0x00001000,0x00001010,0x00001010, + },{ + 0x00000000,0x00000000,0x00200000,0x00200000, + 0x00000000,0x00000000,0x00200000,0x00200000, + 0x20000000,0x20000000,0x20200000,0x20200000, + 0x20000000,0x20000000,0x20200000,0x20200000, + },{ + 0x00000000,0x00000000,0x00000020,0x00000020, + 0x00000000,0x00000000,0x00000020,0x00000020, + 0x00002000,0x00002000,0x00002020,0x00002020, + 0x00002000,0x00002000,0x00002020,0x00002020, + },{ + 0x00000000,0x00000000,0x00400000,0x00400000, + 0x00000000,0x00000000,0x00400000,0x00400000, + 0x40000000,0x40000000,0x40400000,0x40400000, + 0x40000000,0x40000000,0x40400000,0x40400000, + },{ + 0x00000000,0x00000000,0x00000040,0x00000040, + 0x00000000,0x00000000,0x00000040,0x00000040, + 0x00004000,0x00004000,0x00004040,0x00004040, + 0x00004000,0x00004000,0x00004040,0x00004040, + },{ + 0x00000000,0x00000000,0x00800000,0x00800000, + 0x00000000,0x00000000,0x00800000,0x00800000, + 0x80000000,0x80000000,0x80800000,0x80800000, + 0x80000000,0x80000000,0x80800000,0x80800000, + },{ + 0x00000000,0x00000000,0x00000080,0x00000080, + 0x00000000,0x00000000,0x00000080,0x00000080, + 0x00008000,0x00008000,0x00008080,0x00008080, + 0x00008000,0x00008000,0x00008080,0x00008080, + }, +}; +static const uint32_t fp_maskl[8][16] = { + { + 0x00000000,0x40000000,0x00400000,0x40400000, + 0x00004000,0x40004000,0x00404000,0x40404000, + 0x00000040,0x40000040,0x00400040,0x40400040, + 0x00004040,0x40004040,0x00404040,0x40404040, + },{ + 0x00000000,0x10000000,0x00100000,0x10100000, + 0x00001000,0x10001000,0x00101000,0x10101000, + 0x00000010,0x10000010,0x00100010,0x10100010, + 0x00001010,0x10001010,0x00101010,0x10101010, + },{ + 0x00000000,0x04000000,0x00040000,0x04040000, + 0x00000400,0x04000400,0x00040400,0x04040400, + 0x00000004,0x04000004,0x00040004,0x04040004, + 0x00000404,0x04000404,0x00040404,0x04040404, + },{ + 0x00000000,0x01000000,0x00010000,0x01010000, + 0x00000100,0x01000100,0x00010100,0x01010100, + 0x00000001,0x01000001,0x00010001,0x01010001, + 0x00000101,0x01000101,0x00010101,0x01010101, + },{ + 0x00000000,0x80000000,0x00800000,0x80800000, + 0x00008000,0x80008000,0x00808000,0x80808000, + 0x00000080,0x80000080,0x00800080,0x80800080, + 0x00008080,0x80008080,0x00808080,0x80808080, + },{ + 0x00000000,0x20000000,0x00200000,0x20200000, + 0x00002000,0x20002000,0x00202000,0x20202000, + 0x00000020,0x20000020,0x00200020,0x20200020, + 0x00002020,0x20002020,0x00202020,0x20202020, + },{ + 0x00000000,0x08000000,0x00080000,0x08080000, + 0x00000800,0x08000800,0x00080800,0x08080800, + 0x00000008,0x08000008,0x00080008,0x08080008, + 0x00000808,0x08000808,0x00080808,0x08080808, + },{ + 0x00000000,0x02000000,0x00020000,0x02020000, + 0x00000200,0x02000200,0x00020200,0x02020200, + 0x00000002,0x02000002,0x00020002,0x02020002, + 0x00000202,0x02000202,0x00020202,0x02020202, + }, +}; +static const uint32_t fp_maskr[8][16] = { + { + 0x00000000,0x40000000,0x00400000,0x40400000, + 0x00004000,0x40004000,0x00404000,0x40404000, + 0x00000040,0x40000040,0x00400040,0x40400040, + 0x00004040,0x40004040,0x00404040,0x40404040, + },{ + 0x00000000,0x10000000,0x00100000,0x10100000, + 0x00001000,0x10001000,0x00101000,0x10101000, + 0x00000010,0x10000010,0x00100010,0x10100010, + 0x00001010,0x10001010,0x00101010,0x10101010, + },{ + 0x00000000,0x04000000,0x00040000,0x04040000, + 0x00000400,0x04000400,0x00040400,0x04040400, + 0x00000004,0x04000004,0x00040004,0x04040004, + 0x00000404,0x04000404,0x00040404,0x04040404, + },{ + 0x00000000,0x01000000,0x00010000,0x01010000, + 0x00000100,0x01000100,0x00010100,0x01010100, + 0x00000001,0x01000001,0x00010001,0x01010001, + 0x00000101,0x01000101,0x00010101,0x01010101, + },{ + 0x00000000,0x80000000,0x00800000,0x80800000, + 0x00008000,0x80008000,0x00808000,0x80808000, + 0x00000080,0x80000080,0x00800080,0x80800080, + 0x00008080,0x80008080,0x00808080,0x80808080, + },{ + 0x00000000,0x20000000,0x00200000,0x20200000, + 0x00002000,0x20002000,0x00202000,0x20202000, + 0x00000020,0x20000020,0x00200020,0x20200020, + 0x00002020,0x20002020,0x00202020,0x20202020, + },{ + 0x00000000,0x08000000,0x00080000,0x08080000, + 0x00000800,0x08000800,0x00080800,0x08080800, + 0x00000008,0x08000008,0x00080008,0x08080008, + 0x00000808,0x08000808,0x00080808,0x08080808, + },{ + 0x00000000,0x02000000,0x00020000,0x02020000, + 0x00000200,0x02000200,0x00020200,0x02020200, + 0x00000002,0x02000002,0x00020002,0x02020002, + 0x00000202,0x02000202,0x00020202,0x02020202, + }, +}; +static const uint32_t key_perm_maskl[8][16] = { + { + 0x00000000,0x00000000,0x00000010,0x00000010, + 0x00001000,0x00001000,0x00001010,0x00001010, + 0x00100000,0x00100000,0x00100010,0x00100010, + 0x00101000,0x00101000,0x00101010,0x00101010, + },{ + 0x00000000,0x00000000,0x00000020,0x00000020, + 0x00002000,0x00002000,0x00002020,0x00002020, + 0x00200000,0x00200000,0x00200020,0x00200020, + 0x00202000,0x00202000,0x00202020,0x00202020, + },{ + 0x00000000,0x00000000,0x00000040,0x00000040, + 0x00004000,0x00004000,0x00004040,0x00004040, + 0x00400000,0x00400000,0x00400040,0x00400040, + 0x00404000,0x00404000,0x00404040,0x00404040, + },{ + 0x00000000,0x00000000,0x00000080,0x00000080, + 0x00008000,0x00008000,0x00008080,0x00008080, + 0x00800000,0x00800000,0x00800080,0x00800080, + 0x00808000,0x00808000,0x00808080,0x00808080, + },{ + 0x00000000,0x00000001,0x00000100,0x00000101, + 0x00010000,0x00010001,0x00010100,0x00010101, + 0x01000000,0x01000001,0x01000100,0x01000101, + 0x01010000,0x01010001,0x01010100,0x01010101, + },{ + 0x00000000,0x00000002,0x00000200,0x00000202, + 0x00020000,0x00020002,0x00020200,0x00020202, + 0x02000000,0x02000002,0x02000200,0x02000202, + 0x02020000,0x02020002,0x02020200,0x02020202, + },{ + 0x00000000,0x00000004,0x00000400,0x00000404, + 0x00040000,0x00040004,0x00040400,0x00040404, + 0x04000000,0x04000004,0x04000400,0x04000404, + 0x04040000,0x04040004,0x04040400,0x04040404, + },{ + 0x00000000,0x00000008,0x00000800,0x00000808, + 0x00080000,0x00080008,0x00080800,0x00080808, + 0x08000000,0x08000008,0x08000800,0x08000808, + 0x08080000,0x08080008,0x08080800,0x08080808, + }, +}; +static const uint32_t key_perm_maskr[12][16] = { + { + 0x00000000,0x00000001,0x00000000,0x00000001, + 0x00000000,0x00000001,0x00000000,0x00000001, + 0x00000000,0x00000001,0x00000000,0x00000001, + 0x00000000,0x00000001,0x00000000,0x00000001, + },{ + 0x00000000,0x00000000,0x00100000,0x00100000, + 0x00001000,0x00001000,0x00101000,0x00101000, + 0x00000010,0x00000010,0x00100010,0x00100010, + 0x00001010,0x00001010,0x00101010,0x00101010, + },{ + 0x00000000,0x00000002,0x00000000,0x00000002, + 0x00000000,0x00000002,0x00000000,0x00000002, + 0x00000000,0x00000002,0x00000000,0x00000002, + 0x00000000,0x00000002,0x00000000,0x00000002, + },{ + 0x00000000,0x00000000,0x00200000,0x00200000, + 0x00002000,0x00002000,0x00202000,0x00202000, + 0x00000020,0x00000020,0x00200020,0x00200020, + 0x00002020,0x00002020,0x00202020,0x00202020, + },{ + 0x00000000,0x00000004,0x00000000,0x00000004, + 0x00000000,0x00000004,0x00000000,0x00000004, + 0x00000000,0x00000004,0x00000000,0x00000004, + 0x00000000,0x00000004,0x00000000,0x00000004, + },{ + 0x00000000,0x00000000,0x00400000,0x00400000, + 0x00004000,0x00004000,0x00404000,0x00404000, + 0x00000040,0x00000040,0x00400040,0x00400040, + 0x00004040,0x00004040,0x00404040,0x00404040, + },{ + 0x00000000,0x00000008,0x00000000,0x00000008, + 0x00000000,0x00000008,0x00000000,0x00000008, + 0x00000000,0x00000008,0x00000000,0x00000008, + 0x00000000,0x00000008,0x00000000,0x00000008, + },{ + 0x00000000,0x00000000,0x00800000,0x00800000, + 0x00008000,0x00008000,0x00808000,0x00808000, + 0x00000080,0x00000080,0x00800080,0x00800080, + 0x00008080,0x00008080,0x00808080,0x00808080, + },{ + 0x00000000,0x00000000,0x01000000,0x01000000, + 0x00010000,0x00010000,0x01010000,0x01010000, + 0x00000100,0x00000100,0x01000100,0x01000100, + 0x00010100,0x00010100,0x01010100,0x01010100, + },{ + 0x00000000,0x00000000,0x02000000,0x02000000, + 0x00020000,0x00020000,0x02020000,0x02020000, + 0x00000200,0x00000200,0x02000200,0x02000200, + 0x00020200,0x00020200,0x02020200,0x02020200, + },{ + 0x00000000,0x00000000,0x04000000,0x04000000, + 0x00040000,0x00040000,0x04040000,0x04040000, + 0x00000400,0x00000400,0x04000400,0x04000400, + 0x00040400,0x00040400,0x04040400,0x04040400, + },{ + 0x00000000,0x00000000,0x08000000,0x08000000, + 0x00080000,0x00080000,0x08080000,0x08080000, + 0x00000800,0x00000800,0x08000800,0x08000800, + 0x00080800,0x00080800,0x08080800,0x08080800, + }, +}; +static const uint32_t comp_maskl0[4][8] = { + { + 0x00000000,0x00020000,0x00000001,0x00020001, + 0x00080000,0x000a0000,0x00080001,0x000a0001, + },{ + 0x00000000,0x00001000,0x00000000,0x00001000, + 0x00000040,0x00001040,0x00000040,0x00001040, + },{ + 0x00000000,0x00400000,0x00000020,0x00400020, + 0x00008000,0x00408000,0x00008020,0x00408020, + },{ + 0x00000000,0x00100000,0x00000800,0x00100800, + 0x00000000,0x00100000,0x00000800,0x00100800, + }, +}; +static const uint32_t comp_maskr0[4][8] = { + { + 0x00000000,0x00200000,0x00020000,0x00220000, + 0x00000002,0x00200002,0x00020002,0x00220002, + },{ + 0x00000000,0x00000000,0x00100000,0x00100000, + 0x00000004,0x00000004,0x00100004,0x00100004, + },{ + 0x00000000,0x00004000,0x00000800,0x00004800, + 0x00000000,0x00004000,0x00000800,0x00004800, + },{ + 0x00000000,0x00400000,0x00008000,0x00408000, + 0x00000008,0x00400008,0x00008008,0x00408008, + }, +}; +static const uint32_t comp_maskl1[4][16] = { + { + 0x00000000,0x00000010,0x00004000,0x00004010, + 0x00040000,0x00040010,0x00044000,0x00044010, + 0x00000100,0x00000110,0x00004100,0x00004110, + 0x00040100,0x00040110,0x00044100,0x00044110, + },{ + 0x00000000,0x00800000,0x00000002,0x00800002, + 0x00000200,0x00800200,0x00000202,0x00800202, + 0x00200000,0x00a00000,0x00200002,0x00a00002, + 0x00200200,0x00a00200,0x00200202,0x00a00202, + },{ + 0x00000000,0x00002000,0x00000004,0x00002004, + 0x00000400,0x00002400,0x00000404,0x00002404, + 0x00000000,0x00002000,0x00000004,0x00002004, + 0x00000400,0x00002400,0x00000404,0x00002404, + },{ + 0x00000000,0x00010000,0x00000008,0x00010008, + 0x00000080,0x00010080,0x00000088,0x00010088, + 0x00000000,0x00010000,0x00000008,0x00010008, + 0x00000080,0x00010080,0x00000088,0x00010088, + }, +}; +static const uint32_t comp_maskr1[4][16] = { + { + 0x00000000,0x00000000,0x00000080,0x00000080, + 0x00002000,0x00002000,0x00002080,0x00002080, + 0x00000001,0x00000001,0x00000081,0x00000081, + 0x00002001,0x00002001,0x00002081,0x00002081, + },{ + 0x00000000,0x00000010,0x00800000,0x00800010, + 0x00010000,0x00010010,0x00810000,0x00810010, + 0x00000200,0x00000210,0x00800200,0x00800210, + 0x00010200,0x00010210,0x00810200,0x00810210, + },{ + 0x00000000,0x00000400,0x00001000,0x00001400, + 0x00080000,0x00080400,0x00081000,0x00081400, + 0x00000020,0x00000420,0x00001020,0x00001420, + 0x00080020,0x00080420,0x00081020,0x00081420, + },{ + 0x00000000,0x00000100,0x00040000,0x00040100, + 0x00000000,0x00000100,0x00040000,0x00040100, + 0x00000040,0x00000140,0x00040040,0x00040140, + 0x00000040,0x00000140,0x00040040,0x00040140, + }, +}; + +static const unsigned char ascii64[] = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +/* 0000000000111111111122222222223333333333444444444455555555556666 */ +/* 0123456789012345678901234567890123456789012345678901234567890123 */ + +/* + * We match the behavior of UFC-crypt on systems where "char" is signed by + * default (the majority), regardless of char's signedness on our system. + */ +static uint32_t ascii_to_bin(int ch) +{ + int sch = (ch < 0x80) ? ch : -(0x100 - ch); + int retval; + + retval = sch - '.'; + if (sch >= 'A') { + retval = sch - ('A' - 12); + if (sch >= 'a') + retval = sch - ('a' - 38); + } + retval &= 0x3f; + + return retval; +} + +/* + * When we choose to "support" invalid salts, nevertheless disallow those + * containing characters that would violate the passwd file format. + */ +static inline int ascii_is_unsafe(unsigned char ch) +{ + return !ch || ch == '\n' || ch == ':'; +} + +static uint32_t setup_salt(uint32_t salt) +{ + uint32_t obit, saltbit, saltbits; + unsigned int i; + + saltbits = 0; + saltbit = 1; + obit = 0x800000; + for (i = 0; i < 24; i++) { + if (salt & saltbit) + saltbits |= obit; + saltbit <<= 1; + obit >>= 1; + } + + return saltbits; +} + +void __des_setkey(const unsigned char *key, struct expanded_key *ekey) +{ + uint32_t k0, k1, rawkey0, rawkey1; + unsigned int shifts, round, i, ibit; + + rawkey0 = + (uint32_t)key[3] | + ((uint32_t)key[2] << 8) | + ((uint32_t)key[1] << 16) | + ((uint32_t)key[0] << 24); + rawkey1 = + (uint32_t)key[7] | + ((uint32_t)key[6] << 8) | + ((uint32_t)key[5] << 16) | + ((uint32_t)key[4] << 24); + + /* + * Do key permutation and split into two 28-bit subkeys. + */ + k0 = k1 = 0; + for (i = 0, ibit = 28; i < 4; i++, ibit -= 4) { + unsigned int j = i << 1; + k0 |= key_perm_maskl[i][(rawkey0 >> ibit) & 0xf] | + key_perm_maskl[i + 4][(rawkey1 >> ibit) & 0xf]; + k1 |= key_perm_maskr[j][(rawkey0 >> ibit) & 0xf]; + ibit -= 4; + k1 |= key_perm_maskr[j + 1][(rawkey0 >> ibit) & 0xf] | + key_perm_maskr[i + 8][(rawkey1 >> ibit) & 0xf]; + } + + /* + * Rotate subkeys and do compression permutation. + */ + shifts = 0; + for (round = 0; round < 16; round++) { + uint32_t t0, t1; + uint32_t kl, kr; + + shifts += key_shifts[round]; + + t0 = (k0 << shifts) | (k0 >> (28 - shifts)); + t1 = (k1 << shifts) | (k1 >> (28 - shifts)); + + kl = kr = 0; + ibit = 25; + for (i = 0; i < 4; i++) { + kl |= comp_maskl0[i][(t0 >> ibit) & 7]; + kr |= comp_maskr0[i][(t1 >> ibit) & 7]; + ibit -= 4; + kl |= comp_maskl1[i][(t0 >> ibit) & 0xf]; + kr |= comp_maskr1[i][(t1 >> ibit) & 0xf]; + ibit -= 3; + } + ekey->l[round] = kl; + ekey->r[round] = kr; + } +} + +/* + * l_in, r_in, l_out, and r_out are in pseudo-"big-endian" format. + */ +void __do_des(uint32_t l_in, uint32_t r_in, + uint32_t *l_out, uint32_t *r_out, + uint32_t count, uint32_t saltbits, const struct expanded_key *ekey) +{ + uint32_t l, r; + + /* + * Do initial permutation (IP). + */ + l = r = 0; + if (l_in | r_in) { + unsigned int i, ibit; + for (i = 0, ibit = 28; i < 8; i++, ibit -= 4) { + l |= ip_maskl[i][(l_in >> ibit) & 0xf] | + ip_maskl[i + 8][(r_in >> ibit) & 0xf]; + r |= ip_maskr[i][(l_in >> ibit) & 0xf] | + ip_maskr[i + 8][(r_in >> ibit) & 0xf]; + } + } + + while (count--) { + /* + * Do each round. + */ + unsigned int round = 16; + const uint32_t *kl = ekey->l; + const uint32_t *kr = ekey->r; + uint32_t f; + while (round--) { + uint32_t r48l, r48r; + /* + * Expand R to 48 bits (simulate the E-box). + */ + r48l = ((r & 0x00000001) << 23) + | ((r & 0xf8000000) >> 9) + | ((r & 0x1f800000) >> 11) + | ((r & 0x01f80000) >> 13) + | ((r & 0x001f8000) >> 15); + + r48r = ((r & 0x0001f800) << 7) + | ((r & 0x00001f80) << 5) + | ((r & 0x000001f8) << 3) + | ((r & 0x0000001f) << 1) + | ((r & 0x80000000) >> 31); + /* + * Do salting for crypt() and friends, and + * XOR with the permuted key. + */ + f = (r48l ^ r48r) & saltbits; + r48l ^= f ^ *kl++; + r48r ^= f ^ *kr++; + /* + * Do S-box lookups (which shrink it back to 32 bits) + * and do the P-box permutation at the same time. + */ + f = psbox[0][r48l >> 18] + | psbox[1][(r48l >> 12) & 0x3f] + | psbox[2][(r48l >> 6) & 0x3f] + | psbox[3][r48l & 0x3f] + | psbox[4][r48r >> 18] + | psbox[5][(r48r >> 12) & 0x3f] + | psbox[6][(r48r >> 6) & 0x3f] + | psbox[7][r48r & 0x3f]; + /* + * Now that we've permuted things, complete f(). + */ + f ^= l; + l = r; + r = f; + } + r = l; + l = f; + } + + /* + * Do final permutation (inverse of IP). + */ + { + unsigned int i, ibit; + uint32_t lo, ro; + lo = ro = 0; + for (i = 0, ibit = 28; i < 4; i++, ibit -= 4) { + ro |= fp_maskr[i][(l >> ibit) & 0xf] | + fp_maskr[i + 4][(r >> ibit) & 0xf]; + ibit -= 4; + lo |= fp_maskl[i][(l >> ibit) & 0xf] | + fp_maskl[i + 4][(r >> ibit) & 0xf]; + } + *l_out = lo; + *r_out = ro; + } +} + +static void des_cipher(const unsigned char *in, unsigned char *out, + uint32_t count, uint32_t saltbits, const struct expanded_key *ekey) +{ + uint32_t l_out, r_out, rawl, rawr; + + rawl = + (uint32_t)in[3] | + ((uint32_t)in[2] << 8) | + ((uint32_t)in[1] << 16) | + ((uint32_t)in[0] << 24); + rawr = + (uint32_t)in[7] | + ((uint32_t)in[6] << 8) | + ((uint32_t)in[5] << 16) | + ((uint32_t)in[4] << 24); + + __do_des(rawl, rawr, &l_out, &r_out, count, saltbits, ekey); + + out[0] = l_out >> 24; + out[1] = l_out >> 16; + out[2] = l_out >> 8; + out[3] = l_out; + out[4] = r_out >> 24; + out[5] = r_out >> 16; + out[6] = r_out >> 8; + out[7] = r_out; +} + +static char *_crypt_extended_r_uut(const char *_key, const char *_setting, char *output) +{ + const unsigned char *key = (const unsigned char *)_key; + const unsigned char *setting = (const unsigned char *)_setting; + struct expanded_key ekey; + unsigned char keybuf[8]; + unsigned char *p, *q; + uint32_t count, salt, l, r0, r1; + unsigned int i; + + /* + * Copy the key, shifting each character left by one bit and padding + * with zeroes. + */ + q = keybuf; + while (q <= &keybuf[sizeof(keybuf) - 1]) { + *q++ = *key << 1; + if (*key) + key++; + } + __des_setkey(keybuf, &ekey); + + if (*setting == _PASSWORD_EFMT1) { + /* + * "new"-style: + * setting - underscore, 4 chars of count, 4 chars of salt + * key - unlimited characters + */ + for (i = 1, count = 0; i < 5; i++) { + uint32_t value = ascii_to_bin(setting[i]); + if (ascii64[value] != setting[i]) + return NULL; + count |= value << (i - 1) * 6; + } + if (!count) + return NULL; + + for (i = 5, salt = 0; i < 9; i++) { + uint32_t value = ascii_to_bin(setting[i]); + if (ascii64[value] != setting[i]) + return NULL; + salt |= value << (i - 5) * 6; + } + + while (*key) { + /* + * Encrypt the key with itself. + */ + des_cipher(keybuf, keybuf, 1, 0, &ekey); + /* + * And XOR with the next 8 characters of the key. + */ + q = keybuf; + while (q <= &keybuf[sizeof(keybuf) - 1] && *key) + *q++ ^= *key++ << 1; + __des_setkey(keybuf, &ekey); + } + + memcpy(output, setting, 9); + output[9] = '\0'; + p = (unsigned char *)output + 9; + } else { + /* + * "old"-style: + * setting - 2 chars of salt + * key - up to 8 characters + */ + count = 25; + + if (ascii_is_unsafe(setting[0]) || ascii_is_unsafe(setting[1])) + return NULL; + + salt = (ascii_to_bin(setting[1]) << 6) + | ascii_to_bin(setting[0]); + + output[0] = setting[0]; + output[1] = setting[1]; + p = (unsigned char *)output + 2; + } + + /* + * Do it. + */ + __do_des(0, 0, &r0, &r1, count, setup_salt(salt), &ekey); + + /* + * Now encode the result... + */ + l = (r0 >> 8); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = (r0 << 16) | ((r1 >> 16) & 0xffff); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = r1 << 2; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + *p = 0; + + return output; +} + +char *__crypt_des(const char *key, const char *setting, char *output) +{ + const char *test_key = "\x80\xff\x80\x01 " + "\x7f\x81\x80\x80\x0d\x0a\xff\x7f \x81 test"; + const char *test_setting = "_0.../9Zz"; + const char *test_hash = "_0.../9ZzX7iSJNd21sU"; + char test_buf[21]; + char *retval; + const char *p; + + if (*setting != _PASSWORD_EFMT1) { + test_setting = "\x80x"; + test_hash = "\x80x22/wK52ZKGA"; + } + + /* + * Hash the supplied password. + */ + retval = _crypt_extended_r_uut(key, setting, output); + + /* + * Perform a quick self-test. It is important that we make both calls + * to _crypt_extended_r_uut() from the same scope such that they likely + * use the same stack locations, which makes the second call overwrite + * the first call's sensitive data on the stack and makes it more + * likely that any alignment related issues would be detected. + */ + p = _crypt_extended_r_uut(test_key, test_setting, test_buf); + if (p && !strcmp(p, test_hash) && retval) + return retval; + + return (setting[0]=='*') ? "x" : "*"; +} diff --git a/third_party/musl/crypt_des.internal.h b/third_party/musl/crypt_des.internal.h new file mode 100644 index 000000000..d4365baff --- /dev/null +++ b/third_party/musl/crypt_des.internal.h @@ -0,0 +1,16 @@ +// clang-format off +#ifndef CRYPT_DES_H +#define CRYPT_DES_H + +#include "libc/limits.h" +#include "libc/literal.h" + +struct expanded_key { + uint32_t l[16], r[16]; +}; + +hidden void __des_setkey(const unsigned char *, struct expanded_key *); +hidden void __do_des(uint32_t, uint32_t, uint32_t *, uint32_t *, + uint32_t, uint32_t, const struct expanded_key *); + +#endif diff --git a/third_party/musl/crypt_md5.c b/third_party/musl/crypt_md5.c new file mode 100644 index 000000000..37ac97385 --- /dev/null +++ b/third_party/musl/crypt_md5.c @@ -0,0 +1,324 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/alg/alg.h" +#include "libc/limits.h" +#include "libc/literal.h" +#include "libc/str/str.h" +#include "third_party/musl/crypt.internal.h" + +#pragma GCC diagnostic ignored "-Wmissing-braces" + +asm(".ident\t\"\\n\\n\ +Musl libc (MIT License)\\n\ +Copyright 2005-2014 Rich Felker, et. al.\""); +asm(".include \"libc/disclaimer.inc\""); +// clang-format off + +/* + * md5 crypt implementation + * + * original md5 crypt design is from Poul-Henning Kamp + * this implementation was created based on the code in freebsd + * at least 32bit int is assumed, key is limited and $1$ prefix is mandatory, + * on error "*" is returned + */ + +/* public domain md5 implementation based on rfc1321 and libtomcrypt */ + +struct md5 { + uint64_t len; /* processed message length */ + uint32_t h[4]; /* hash state */ + uint8_t buf[64]; /* message block buffer */ +}; + +static uint32_t rol(uint32_t n, int k) { return (n << k) | (n >> (32-k)); } +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define G(x,y,z) (y ^ (z & (y ^ x))) +#define H(x,y,z) (x ^ y ^ z) +#define I(x,y,z) (y ^ (x | ~z)) +#define FF(a,b,c,d,w,s,t) a += F(b,c,d) + w + t; a = rol(a,s) + b +#define GG(a,b,c,d,w,s,t) a += G(b,c,d) + w + t; a = rol(a,s) + b +#define HH(a,b,c,d,w,s,t) a += H(b,c,d) + w + t; a = rol(a,s) + b +#define II(a,b,c,d,w,s,t) a += I(b,c,d) + w + t; a = rol(a,s) + b + +static const uint32_t tab[64] = { +0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, +0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, +0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, +0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, +0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, +0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, +0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, +0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 +}; + +static void processblock(struct md5 *s, const uint8_t *buf) +{ + uint32_t i, W[16], a, b, c, d; + + for (i = 0; i < 16; i++) { + W[i] = buf[4*i]; + W[i] |= (uint32_t)buf[4*i+1]<<8; + W[i] |= (uint32_t)buf[4*i+2]<<16; + W[i] |= (uint32_t)buf[4*i+3]<<24; + } + + a = s->h[0]; + b = s->h[1]; + c = s->h[2]; + d = s->h[3]; + + i = 0; + while (i < 16) { + FF(a,b,c,d, W[i], 7, tab[i]); i++; + FF(d,a,b,c, W[i], 12, tab[i]); i++; + FF(c,d,a,b, W[i], 17, tab[i]); i++; + FF(b,c,d,a, W[i], 22, tab[i]); i++; + } + while (i < 32) { + GG(a,b,c,d, W[(5*i+1)%16], 5, tab[i]); i++; + GG(d,a,b,c, W[(5*i+1)%16], 9, tab[i]); i++; + GG(c,d,a,b, W[(5*i+1)%16], 14, tab[i]); i++; + GG(b,c,d,a, W[(5*i+1)%16], 20, tab[i]); i++; + } + while (i < 48) { + HH(a,b,c,d, W[(3*i+5)%16], 4, tab[i]); i++; + HH(d,a,b,c, W[(3*i+5)%16], 11, tab[i]); i++; + HH(c,d,a,b, W[(3*i+5)%16], 16, tab[i]); i++; + HH(b,c,d,a, W[(3*i+5)%16], 23, tab[i]); i++; + } + while (i < 64) { + II(a,b,c,d, W[7*i%16], 6, tab[i]); i++; + II(d,a,b,c, W[7*i%16], 10, tab[i]); i++; + II(c,d,a,b, W[7*i%16], 15, tab[i]); i++; + II(b,c,d,a, W[7*i%16], 21, tab[i]); i++; + } + + s->h[0] += a; + s->h[1] += b; + s->h[2] += c; + s->h[3] += d; +} + +static void pad(struct md5 *s) +{ + unsigned r = s->len % 64; + + s->buf[r++] = 0x80; + if (r > 56) { + memset(s->buf + r, 0, 64 - r); + r = 0; + processblock(s, s->buf); + } + memset(s->buf + r, 0, 56 - r); + s->len *= 8; + s->buf[56] = s->len; + s->buf[57] = s->len >> 8; + s->buf[58] = s->len >> 16; + s->buf[59] = s->len >> 24; + s->buf[60] = s->len >> 32; + s->buf[61] = s->len >> 40; + s->buf[62] = s->len >> 48; + s->buf[63] = s->len >> 56; + processblock(s, s->buf); +} + +static void md5_init(struct md5 *s) +{ + s->len = 0; + s->h[0] = 0x67452301; + s->h[1] = 0xefcdab89; + s->h[2] = 0x98badcfe; + s->h[3] = 0x10325476; +} + +static void md5_sum(struct md5 *s, uint8_t *md) +{ + int i; + + pad(s); + for (i = 0; i < 4; i++) { + md[4*i] = s->h[i]; + md[4*i+1] = s->h[i] >> 8; + md[4*i+2] = s->h[i] >> 16; + md[4*i+3] = s->h[i] >> 24; + } +} + +static void md5_update(struct md5 *s, const void *m, unsigned long len) +{ + const uint8_t *p = m; + unsigned r = s->len % 64; + + s->len += len; + if (r) { + if (len < 64 - r) { + memcpy(s->buf + r, p, len); + return; + } + memcpy(s->buf + r, p, 64 - r); + len -= 64 - r; + p += 64 - r; + processblock(s, s->buf); + } + for (; len >= 64; len -= 64, p += 64) + processblock(s, p); + memcpy(s->buf, p, len); +} + +/*- + * Copyright (c) 2003 Poul-Henning Kamp + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* key limit is not part of the original design, added for DoS protection */ +#define KEY_MAX 30000 +#define SALT_MAX 8 + +static const unsigned char b64[] = +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static char *to64(char *s, unsigned int u, int n) +{ + while (--n >= 0) { + *s++ = b64[u % 64]; + u /= 64; + } + return s; +} + +static char *md5crypt(const char *key, const char *setting, char *output) +{ + struct md5 ctx; + unsigned char md[16]; + unsigned int i, klen, slen; + const char *salt; + char *p; + + /* reject large keys */ + klen = strnlen(key, KEY_MAX+1); + if (klen > KEY_MAX) + return 0; + + /* setting: $1$salt$ (closing $ is optional) */ + if (strncmp(setting, "$1$", 3) != 0) + return 0; + salt = setting + 3; + for (i = 0; i < SALT_MAX && salt[i] && salt[i] != '$'; i++); + slen = i; + + /* md5(key salt key) */ + md5_init(&ctx); + md5_update(&ctx, key, klen); + md5_update(&ctx, salt, slen); + md5_update(&ctx, key, klen); + md5_sum(&ctx, md); + + /* md5(key $1$ salt repeated-md weird-key[0]-0) */ + md5_init(&ctx); + md5_update(&ctx, key, klen); + md5_update(&ctx, setting, 3 + slen); + for (i = klen; i > sizeof md; i -= sizeof md) + md5_update(&ctx, md, sizeof md); + md5_update(&ctx, md, i); + md[0] = 0; + for (i = klen; i; i >>= 1) + if (i & 1) + md5_update(&ctx, md, 1); + else + md5_update(&ctx, key, 1); + md5_sum(&ctx, md); + + /* md = f(md, key, salt) iteration */ + for (i = 0; i < 1000; i++) { + md5_init(&ctx); + if (i % 2) + md5_update(&ctx, key, klen); + else + md5_update(&ctx, md, sizeof md); + if (i % 3) + md5_update(&ctx, salt, slen); + if (i % 7) + md5_update(&ctx, key, klen); + if (i % 2) + md5_update(&ctx, md, sizeof md); + else + md5_update(&ctx, key, klen); + md5_sum(&ctx, md); + } + + /* output is $1$salt$hash */ + memcpy(output, setting, 3 + slen); + p = output + 3 + slen; + *p++ = '$'; + static const unsigned char perm[][3] = { + 0,6,12,1,7,13,2,8,14,3,9,15,4,10,5 }; + for (i=0; i<5; i++) p = to64(p, + (md[perm[i][0]]<<16)|(md[perm[i][1]]<<8)|md[perm[i][2]], 4); + p = to64(p, md[11], 2); + *p = 0; + + return output; +} + +char *__crypt_md5(const char *key, const char *setting, char *output) +{ + static const char testkey[] = "Xy01@#\x01\x02\x80\x7f\xff\r\n\x81\t !"; + static const char testsetting[] = "$1$abcd0123$"; + static const char testhash[] = "$1$abcd0123$9Qcg8DyviekV3tDGMZynJ1"; + char testbuf[64]; + char *p, *q; + + p = md5crypt(key, setting, output); + /* self test and stack cleanup */ + q = md5crypt(testkey, testsetting, testbuf); + if (!p || q != testbuf || memcmp(testbuf, testhash, sizeof testhash)) + return "*"; + return p; +} diff --git a/third_party/musl/crypt_r.c b/third_party/musl/crypt_r.c new file mode 100644 index 000000000..b272d0fb8 --- /dev/null +++ b/third_party/musl/crypt_r.c @@ -0,0 +1,69 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "third_party/musl/crypt.h" +#include "third_party/musl/crypt.internal.h" + +asm(".ident\t\"\\n\\n\ +Musl libc (MIT License)\\n\ +Copyright 2005-2014 Rich Felker, et. al.\""); +asm(".include \"libc/disclaimer.inc\""); +// clang-format off + +/** + * Encrypts password the old fashioned way. + * + * The method of encryption depends on the first three chars of salt: + * + * - `$1$` is MD5 + * - `$2$` is Blowfish + * - `$5$` is SHA-256 + * - `$6$` is SHA-512 + * - Otherwise DES + * + * @return static memory with encrypted password + * @see third_party/argon2/ + */ +char *crypt_r(const char *key, const char *salt, struct crypt_data *data) +{ + /* Per the crypt_r API, the caller has provided a pointer to + * struct crypt_data; however, this implementation does not + * use the structure to store any internal state, and treats + * it purely as a char buffer for storing the result. */ + char *output = (char *)data; + if (salt[0] == '$' && salt[1] && salt[2]) { + if (salt[1] == '1' && salt[2] == '$') + return __crypt_md5(key, salt, output); + if (salt[1] == '2' && salt[3] == '$') + return __crypt_blowfish(key, salt, output); + if (salt[1] == '5' && salt[2] == '$') + return __crypt_sha256(key, salt, output); + if (salt[1] == '6' && salt[2] == '$') + return __crypt_sha512(key, salt, output); + } + return __crypt_des(key, salt, output); +} diff --git a/third_party/musl/crypt_sha256.c b/third_party/musl/crypt_sha256.c new file mode 100644 index 000000000..ec2e38a7d --- /dev/null +++ b/third_party/musl/crypt_sha256.c @@ -0,0 +1,358 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/alg/alg.h" +#include "libc/calls/calls.h" +#include "libc/fmt/conv.h" +#include "libc/fmt/fmt.h" +#include "libc/limits.h" +#include "libc/literal.h" +#include "libc/mem/mem.h" +#include "libc/nexgen32e/nexgen32e.h" +#include "libc/rand/rand.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/stdio/temp.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/exit.h" +#include "third_party/gdtoa/gdtoa.h" +#include "third_party/musl/crypt.internal.h" + +#pragma GCC diagnostic ignored "-Wmissing-braces" + +asm(".ident\t\"\\n\\n\ +Musl libc (MIT License)\\n\ +Copyright 2005-2014 Rich Felker, et. al.\""); +asm(".include \"libc/disclaimer.inc\""); +// clang-format off + +/* + * public domain sha256 crypt implementation + * + * original sha crypt design: http://people.redhat.com/drepper/SHA-crypt.txt + * in this implementation at least 32bit int is assumed, + * key length is limited, the $5$ prefix is mandatory, '\n' and ':' is rejected + * in the salt and rounds= setting must contain a valid iteration count, + * on error "*" is returned. + */ + +/* public domain sha256 implementation based on fips180-3 */ + +struct sha256 { + uint64_t len; /* processed message length */ + uint32_t h[8]; /* hash state */ + uint8_t buf[64]; /* message block buffer */ +}; + +static uint32_t ror(uint32_t n, int k) { return (n >> k) | (n << (32-k)); } +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) ((x & y) | (z & (x | y))) +#define S0(x) (ror(x,2) ^ ror(x,13) ^ ror(x,22)) +#define S1(x) (ror(x,6) ^ ror(x,11) ^ ror(x,25)) +#define R0(x) (ror(x,7) ^ ror(x,18) ^ (x>>3)) +#define R1(x) (ror(x,17) ^ ror(x,19) ^ (x>>10)) + +static void processblock(struct sha256 *s, const uint8_t *buf) +{ + uint32_t W[64], t1, t2, a, b, c, d, e, f, g, h; + int i; + + for (i = 0; i < 16; i++) { + W[i] = (uint32_t)buf[4*i]<<24; + W[i] |= (uint32_t)buf[4*i+1]<<16; + W[i] |= (uint32_t)buf[4*i+2]<<8; + W[i] |= buf[4*i+3]; + } + for (; i < 64; i++) + W[i] = R1(W[i-2]) + W[i-7] + R0(W[i-15]) + W[i-16]; + a = s->h[0]; + b = s->h[1]; + c = s->h[2]; + d = s->h[3]; + e = s->h[4]; + f = s->h[5]; + g = s->h[6]; + h = s->h[7]; + for (i = 0; i < 64; i++) { + t1 = h + S1(e) + Ch(e,f,g) + kSha256[i] + W[i]; + t2 = S0(a) + Maj(a,b,c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + s->h[0] += a; + s->h[1] += b; + s->h[2] += c; + s->h[3] += d; + s->h[4] += e; + s->h[5] += f; + s->h[6] += g; + s->h[7] += h; +} + +static void pad(struct sha256 *s) +{ + unsigned r = s->len % 64; + + s->buf[r++] = 0x80; + if (r > 56) { + memset(s->buf + r, 0, 64 - r); + r = 0; + processblock(s, s->buf); + } + memset(s->buf + r, 0, 56 - r); + s->len *= 8; + s->buf[56] = s->len >> 56; + s->buf[57] = s->len >> 48; + s->buf[58] = s->len >> 40; + s->buf[59] = s->len >> 32; + s->buf[60] = s->len >> 24; + s->buf[61] = s->len >> 16; + s->buf[62] = s->len >> 8; + s->buf[63] = s->len; + processblock(s, s->buf); +} + +static void sha256_init(struct sha256 *s) +{ + s->len = 0; + s->h[0] = 0x6a09e667; + s->h[1] = 0xbb67ae85; + s->h[2] = 0x3c6ef372; + s->h[3] = 0xa54ff53a; + s->h[4] = 0x510e527f; + s->h[5] = 0x9b05688c; + s->h[6] = 0x1f83d9ab; + s->h[7] = 0x5be0cd19; +} + +static void sha256_sum(struct sha256 *s, uint8_t *md) +{ + int i; + + pad(s); + for (i = 0; i < 8; i++) { + md[4*i] = s->h[i] >> 24; + md[4*i+1] = s->h[i] >> 16; + md[4*i+2] = s->h[i] >> 8; + md[4*i+3] = s->h[i]; + } +} + +static void sha256_update(struct sha256 *s, const void *m, unsigned long len) +{ + const uint8_t *p = m; + unsigned r = s->len % 64; + + s->len += len; + if (r) { + if (len < 64 - r) { + memcpy(s->buf + r, p, len); + return; + } + memcpy(s->buf + r, p, 64 - r); + len -= 64 - r; + p += 64 - r; + processblock(s, s->buf); + } + for (; len >= 64; len -= 64, p += 64) + processblock(s, p); + memcpy(s->buf, p, len); +} + +static const unsigned char b64[] = +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static char *to64(char *s, unsigned int u, int n) +{ + while (--n >= 0) { + *s++ = b64[u % 64]; + u /= 64; + } + return s; +} + +/* key limit is not part of the original design, added for DoS protection. + * rounds limit has been lowered (versus the reference/spec), also for DoS + * protection. runtime is O(klen^2 + klen*rounds) */ +#define KEY_MAX 256 +#define SALT_MAX 16 +#define ROUNDS_DEFAULT 5000 +#define ROUNDS_MIN 1000 +#define ROUNDS_MAX 9999999 + +/* hash n bytes of the repeated md message digest */ +static void hashmd(struct sha256 *s, unsigned int n, const void *md) +{ + unsigned int i; + + for (i = n; i > 32; i -= 32) + sha256_update(s, md, 32); + sha256_update(s, md, i); +} + +static char *sha256crypt(const char *key, const char *setting, char *output) +{ + struct sha256 ctx; + unsigned char md[32], kmd[32], smd[32]; + unsigned int i, r, klen, slen; + char rounds[20] = ""; + const char *salt; + char *p; + + /* reject large keys */ + klen = strnlen(key, KEY_MAX+1); + if (klen > KEY_MAX) + return 0; + + /* setting: $5$rounds=n$salt$ (rounds=n$ and closing $ are optional) */ + if (strncmp(setting, "$5$", 3) != 0) + return 0; + salt = setting + 3; + + r = ROUNDS_DEFAULT; + if (strncmp(salt, "rounds=", sizeof "rounds=" - 1) == 0) { + unsigned long u; + char *end; + + /* + * this is a deviation from the reference: + * bad rounds setting is rejected if it is + * - empty + * - unterminated (missing '$') + * - begins with anything but a decimal digit + * the reference implementation treats these bad + * rounds as part of the salt or parse them with + * strtoul semantics which may cause problems + * including non-portable hashes that depend on + * the host's value of ULONG_MAX. + */ + salt += sizeof "rounds=" - 1; + if (!isdigit(*salt)) + return 0; + u = strtoul(salt, &end, 10); + if (*end != '$') + return 0; + salt = end+1; + if (u < ROUNDS_MIN) + r = ROUNDS_MIN; + else if (u > ROUNDS_MAX) + return 0; + else + r = u; + /* needed when rounds is zero prefixed or out of bounds */ + (sprintf)(rounds, "rounds=%u$", r); + } + + for (i = 0; i < SALT_MAX && salt[i] && salt[i] != '$'; i++) + /* reject characters that interfere with /etc/shadow parsing */ + if (salt[i] == '\n' || salt[i] == ':') + return 0; + slen = i; + + /* B = sha(key salt key) */ + sha256_init(&ctx); + sha256_update(&ctx, key, klen); + sha256_update(&ctx, salt, slen); + sha256_update(&ctx, key, klen); + sha256_sum(&ctx, md); + + /* A = sha(key salt repeat-B alternate-B-key) */ + sha256_init(&ctx); + sha256_update(&ctx, key, klen); + sha256_update(&ctx, salt, slen); + hashmd(&ctx, klen, md); + for (i = klen; i > 0; i >>= 1) + if (i & 1) + sha256_update(&ctx, md, sizeof md); + else + sha256_update(&ctx, key, klen); + sha256_sum(&ctx, md); + + /* DP = sha(repeat-key), this step takes O(klen^2) time */ + sha256_init(&ctx); + for (i = 0; i < klen; i++) + sha256_update(&ctx, key, klen); + sha256_sum(&ctx, kmd); + + /* DS = sha(repeat-salt) */ + sha256_init(&ctx); + for (i = 0; i < 16 + md[0]; i++) + sha256_update(&ctx, salt, slen); + sha256_sum(&ctx, smd); + + /* iterate A = f(A,DP,DS), this step takes O(rounds*klen) time */ + for (i = 0; i < r; i++) { + sha256_init(&ctx); + if (i % 2) + hashmd(&ctx, klen, kmd); + else + sha256_update(&ctx, md, sizeof md); + if (i % 3) + sha256_update(&ctx, smd, slen); + if (i % 7) + hashmd(&ctx, klen, kmd); + if (i % 2) + sha256_update(&ctx, md, sizeof md); + else + hashmd(&ctx, klen, kmd); + sha256_sum(&ctx, md); + } + + /* output is $5$rounds=n$salt$hash */ + p = output; + p += (sprintf)(p, "$5$%s%.*s$", rounds, slen, salt); + static const unsigned char perm[][3] = { + 0,10,20,21,1,11,12,22,2,3,13,23,24,4,14, + 15,25,5,6,16,26,27,7,17,18,28,8,9,19,29 }; + for (i=0; i<10; i++) p = to64(p, + (md[perm[i][0]]<<16)|(md[perm[i][1]]<<8)|md[perm[i][2]], 4); + p = to64(p, (md[31]<<8)|md[30], 3); + *p = 0; + return output; +} + +char *__crypt_sha256(const char *key, const char *setting, char *output) +{ + static const char testkey[] = "Xy01@#\x01\x02\x80\x7f\xff\r\n\x81\t !"; + static const char testsetting[] = "$5$rounds=1234$abc0123456789$"; + static const char testhash[] = "$5$rounds=1234$abc0123456789$3VfDjPt05VHFn47C/ojFZ6KRPYrOjj1lLbH.dkF3bZ6"; + char testbuf[128]; + char *p, *q; + + p = sha256crypt(key, setting, output); + /* self test and stack cleanup */ + q = sha256crypt(testkey, testsetting, testbuf); + if (!p || q != testbuf || memcmp(testbuf, testhash, sizeof testhash)) + return "*"; + return p; +} diff --git a/third_party/musl/crypt_sha512.c b/third_party/musl/crypt_sha512.c new file mode 100644 index 000000000..48a4d1b12 --- /dev/null +++ b/third_party/musl/crypt_sha512.c @@ -0,0 +1,395 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/alg/alg.h" +#include "libc/calls/calls.h" +#include "libc/fmt/conv.h" +#include "libc/fmt/fmt.h" +#include "libc/limits.h" +#include "libc/literal.h" +#include "libc/mem/mem.h" +#include "libc/nexgen32e/nexgen32e.h" +#include "libc/rand/rand.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/stdio/temp.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/exit.h" +#include "third_party/gdtoa/gdtoa.h" +#include "third_party/musl/crypt.internal.h" + +#pragma GCC diagnostic ignored "-Wmissing-braces" + +asm(".ident\t\"\\n\\n\ +Musl libc (MIT License)\\n\ +Copyright 2005-2014 Rich Felker, et. al.\""); +asm(".include \"libc/disclaimer.inc\""); +// clang-format off + +/* + * public domain sha512 crypt implementation + * + * original sha crypt design: http://people.redhat.com/drepper/SHA-crypt.txt + * in this implementation at least 32bit int is assumed, + * key length is limited, the $6$ prefix is mandatory, '\n' and ':' is rejected + * in the salt and rounds= setting must contain a valid iteration count, + * on error "*" is returned. + */ + +/* public domain sha512 implementation based on fips180-3 */ +/* >=2^64 bits messages are not supported (about 2000 peta bytes) */ + +struct sha512 { + uint64_t len; /* processed message length */ + uint64_t h[8]; /* hash state */ + uint8_t buf[128]; /* message block buffer */ +}; + +static uint64_t ror(uint64_t n, int k) { return (n >> k) | (n << (64-k)); } +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) ((x & y) | (z & (x | y))) +#define S0(x) (ror(x,28) ^ ror(x,34) ^ ror(x,39)) +#define S1(x) (ror(x,14) ^ ror(x,18) ^ ror(x,41)) +#define R0(x) (ror(x,1) ^ ror(x,8) ^ (x>>7)) +#define R1(x) (ror(x,19) ^ ror(x,61) ^ (x>>6)) + +static void processblock(struct sha512 *s, const uint8_t *buf) +{ + uint64_t W[80], t1, t2, a, b, c, d, e, f, g, h; + int i; + + for (i = 0; i < 16; i++) { + W[i] = (uint64_t)buf[8*i]<<56; + W[i] |= (uint64_t)buf[8*i+1]<<48; + W[i] |= (uint64_t)buf[8*i+2]<<40; + W[i] |= (uint64_t)buf[8*i+3]<<32; + W[i] |= (uint64_t)buf[8*i+4]<<24; + W[i] |= (uint64_t)buf[8*i+5]<<16; + W[i] |= (uint64_t)buf[8*i+6]<<8; + W[i] |= buf[8*i+7]; + } + for (; i < 80; i++) + W[i] = R1(W[i-2]) + W[i-7] + R0(W[i-15]) + W[i-16]; + a = s->h[0]; + b = s->h[1]; + c = s->h[2]; + d = s->h[3]; + e = s->h[4]; + f = s->h[5]; + g = s->h[6]; + h = s->h[7]; + for (i = 0; i < 80; i++) { + t1 = h + S1(e) + Ch(e,f,g) + kSha512[i] + W[i]; + t2 = S0(a) + Maj(a,b,c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + s->h[0] += a; + s->h[1] += b; + s->h[2] += c; + s->h[3] += d; + s->h[4] += e; + s->h[5] += f; + s->h[6] += g; + s->h[7] += h; +} + +static void pad(struct sha512 *s) +{ + unsigned r = s->len % 128; + + s->buf[r++] = 0x80; + if (r > 112) { + memset(s->buf + r, 0, 128 - r); + r = 0; + processblock(s, s->buf); + } + memset(s->buf + r, 0, 120 - r); + s->len *= 8; + s->buf[120] = s->len >> 56; + s->buf[121] = s->len >> 48; + s->buf[122] = s->len >> 40; + s->buf[123] = s->len >> 32; + s->buf[124] = s->len >> 24; + s->buf[125] = s->len >> 16; + s->buf[126] = s->len >> 8; + s->buf[127] = s->len; + processblock(s, s->buf); +} + +static void sha512_init(struct sha512 *s) +{ + s->len = 0; + s->h[0] = 0x6a09e667f3bcc908ULL; + s->h[1] = 0xbb67ae8584caa73bULL; + s->h[2] = 0x3c6ef372fe94f82bULL; + s->h[3] = 0xa54ff53a5f1d36f1ULL; + s->h[4] = 0x510e527fade682d1ULL; + s->h[5] = 0x9b05688c2b3e6c1fULL; + s->h[6] = 0x1f83d9abfb41bd6bULL; + s->h[7] = 0x5be0cd19137e2179ULL; +} + +static void sha512_sum(struct sha512 *s, uint8_t *md) +{ + int i; + + pad(s); + for (i = 0; i < 8; i++) { + md[8*i] = s->h[i] >> 56; + md[8*i+1] = s->h[i] >> 48; + md[8*i+2] = s->h[i] >> 40; + md[8*i+3] = s->h[i] >> 32; + md[8*i+4] = s->h[i] >> 24; + md[8*i+5] = s->h[i] >> 16; + md[8*i+6] = s->h[i] >> 8; + md[8*i+7] = s->h[i]; + } +} + +static void sha512_update(struct sha512 *s, const void *m, unsigned long len) +{ + const uint8_t *p = m; + unsigned r = s->len % 128; + + s->len += len; + if (r) { + if (len < 128 - r) { + memcpy(s->buf + r, p, len); + return; + } + memcpy(s->buf + r, p, 128 - r); + len -= 128 - r; + p += 128 - r; + processblock(s, s->buf); + } + for (; len >= 128; len -= 128, p += 128) + processblock(s, p); + memcpy(s->buf, p, len); +} + +static const unsigned char b64[] = +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static char *to64(char *s, unsigned int u, int n) +{ + while (--n >= 0) { + *s++ = b64[u % 64]; + u /= 64; + } + return s; +} + +/* key limit is not part of the original design, added for DoS protection. + * rounds limit has been lowered (versus the reference/spec), also for DoS + * protection. runtime is O(klen^2 + klen*rounds) */ +#define KEY_MAX 256 +#define SALT_MAX 16 +#define ROUNDS_DEFAULT 5000 +#define ROUNDS_MIN 1000 +#define ROUNDS_MAX 9999999 + +/* hash n bytes of the repeated md message digest */ +static void hashmd(struct sha512 *s, unsigned int n, const void *md) +{ + unsigned int i; + + for (i = n; i > 64; i -= 64) + sha512_update(s, md, 64); + sha512_update(s, md, i); +} + +static char *sha512crypt(const char *key, const char *setting, char *output) +{ + struct sha512 ctx; + unsigned char md[64], kmd[64], smd[64]; + unsigned int i, r, klen, slen; + char rounds[20] = ""; + const char *salt; + char *p; + + /* reject large keys */ + for (i = 0; i <= KEY_MAX && key[i]; i++); + if (i > KEY_MAX) + return 0; + klen = i; + + /* setting: $6$rounds=n$salt$ (rounds=n$ and closing $ are optional) */ + if (strncmp(setting, "$6$", 3) != 0) + return 0; + salt = setting + 3; + + r = ROUNDS_DEFAULT; + if (strncmp(salt, "rounds=", sizeof "rounds=" - 1) == 0) { + unsigned long u; + char *end; + + /* + * this is a deviation from the reference: + * bad rounds setting is rejected if it is + * - empty + * - unterminated (missing '$') + * - begins with anything but a decimal digit + * the reference implementation treats these bad + * rounds as part of the salt or parse them with + * strtoul semantics which may cause problems + * including non-portable hashes that depend on + * the host's value of ULONG_MAX. + */ + salt += sizeof "rounds=" - 1; + if (!isdigit(*salt)) + return 0; + u = strtoul(salt, &end, 10); + if (*end != '$') + return 0; + salt = end+1; + if (u < ROUNDS_MIN) + r = ROUNDS_MIN; + else if (u > ROUNDS_MAX) + return 0; + else + r = u; + /* needed when rounds is zero prefixed or out of bounds */ + (sprintf)(rounds, "rounds=%u$", r); + } + + for (i = 0; i < SALT_MAX && salt[i] && salt[i] != '$'; i++) + /* reject characters that interfere with /etc/shadow parsing */ + if (salt[i] == '\n' || salt[i] == ':') + return 0; + slen = i; + + /* B = sha(key salt key) */ + sha512_init(&ctx); + sha512_update(&ctx, key, klen); + sha512_update(&ctx, salt, slen); + sha512_update(&ctx, key, klen); + sha512_sum(&ctx, md); + + /* A = sha(key salt repeat-B alternate-B-key) */ + sha512_init(&ctx); + sha512_update(&ctx, key, klen); + sha512_update(&ctx, salt, slen); + hashmd(&ctx, klen, md); + for (i = klen; i > 0; i >>= 1) + if (i & 1) + sha512_update(&ctx, md, sizeof md); + else + sha512_update(&ctx, key, klen); + sha512_sum(&ctx, md); + + /* DP = sha(repeat-key), this step takes O(klen^2) time */ + sha512_init(&ctx); + for (i = 0; i < klen; i++) + sha512_update(&ctx, key, klen); + sha512_sum(&ctx, kmd); + + /* DS = sha(repeat-salt) */ + sha512_init(&ctx); + for (i = 0; i < 16 + md[0]; i++) + sha512_update(&ctx, salt, slen); + sha512_sum(&ctx, smd); + + /* iterate A = f(A,DP,DS), this step takes O(rounds*klen) time */ + for (i = 0; i < r; i++) { + sha512_init(&ctx); + if (i % 2) + hashmd(&ctx, klen, kmd); + else + sha512_update(&ctx, md, sizeof md); + if (i % 3) + sha512_update(&ctx, smd, slen); + if (i % 7) + hashmd(&ctx, klen, kmd); + if (i % 2) + sha512_update(&ctx, md, sizeof md); + else + hashmd(&ctx, klen, kmd); + sha512_sum(&ctx, md); + } + + /* output is $6$rounds=n$salt$hash */ + p = output; + p += (sprintf)(p, "$6$%s%.*s$", rounds, slen, salt); +#if 1 + static const unsigned char perm[][3] = { + 0,21,42,22,43,1,44,2,23,3,24,45,25,46,4, + 47,5,26,6,27,48,28,49,7,50,8,29,9,30,51, + 31,52,10,53,11,32,12,33,54,34,55,13,56,14,35, + 15,36,57,37,58,16,59,17,38,18,39,60,40,61,19, + 62,20,41 }; + for (i=0; i<21; i++) p = to64(p, + (md[perm[i][0]]<<16)|(md[perm[i][1]]<<8)|md[perm[i][2]], 4); +#else + p = to64(p, (md[0]<<16)|(md[21]<<8)|md[42], 4); + p = to64(p, (md[22]<<16)|(md[43]<<8)|md[1], 4); + p = to64(p, (md[44]<<16)|(md[2]<<8)|md[23], 4); + p = to64(p, (md[3]<<16)|(md[24]<<8)|md[45], 4); + p = to64(p, (md[25]<<16)|(md[46]<<8)|md[4], 4); + p = to64(p, (md[47]<<16)|(md[5]<<8)|md[26], 4); + p = to64(p, (md[6]<<16)|(md[27]<<8)|md[48], 4); + p = to64(p, (md[28]<<16)|(md[49]<<8)|md[7], 4); + p = to64(p, (md[50]<<16)|(md[8]<<8)|md[29], 4); + p = to64(p, (md[9]<<16)|(md[30]<<8)|md[51], 4); + p = to64(p, (md[31]<<16)|(md[52]<<8)|md[10], 4); + p = to64(p, (md[53]<<16)|(md[11]<<8)|md[32], 4); + p = to64(p, (md[12]<<16)|(md[33]<<8)|md[54], 4); + p = to64(p, (md[34]<<16)|(md[55]<<8)|md[13], 4); + p = to64(p, (md[56]<<16)|(md[14]<<8)|md[35], 4); + p = to64(p, (md[15]<<16)|(md[36]<<8)|md[57], 4); + p = to64(p, (md[37]<<16)|(md[58]<<8)|md[16], 4); + p = to64(p, (md[59]<<16)|(md[17]<<8)|md[38], 4); + p = to64(p, (md[18]<<16)|(md[39]<<8)|md[60], 4); + p = to64(p, (md[40]<<16)|(md[61]<<8)|md[19], 4); + p = to64(p, (md[62]<<16)|(md[20]<<8)|md[41], 4); +#endif + p = to64(p, md[63], 2); + *p = 0; + return output; +} + +char *__crypt_sha512(const char *key, const char *setting, char *output) +{ + static const char testkey[] = "Xy01@#\x01\x02\x80\x7f\xff\r\n\x81\t !"; + static const char testsetting[] = "$6$rounds=1234$abc0123456789$"; + static const char testhash[] = "$6$rounds=1234$abc0123456789$BCpt8zLrc/RcyuXmCDOE1ALqMXB2MH6n1g891HhFj8.w7LxGv.FTkqq6Vxc/km3Y0jE0j24jY5PIv/oOu6reg1"; + char testbuf[128]; + char *p, *q; + + p = sha512crypt(key, setting, output); + /* self test and stack cleanup */ + q = sha512crypt(testkey, testsetting, testbuf); + if (!p || q != testbuf || memcmp(testbuf, testhash, sizeof testhash)) + return "*"; + return p; +} diff --git a/third_party/musl/encrypt.c b/third_party/musl/encrypt.c new file mode 100644 index 000000000..5e25ed62c --- /dev/null +++ b/third_party/musl/encrypt.c @@ -0,0 +1,97 @@ +/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ +│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ │ +│ Musl Libc │ +│ Copyright © 2005-2014 Rich Felker, et al. │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining │ +│ a copy of this software and associated documentation files (the │ +│ "Software"), to deal in the Software without restriction, including │ +│ without limitation the rights to use, copy, modify, merge, publish, │ +│ distribute, sublicense, and/or sell copies of the Software, and to │ +│ permit persons to whom the Software is furnished to do so, subject to │ +│ the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be │ +│ included in all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ +│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ +│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ +│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │ +│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │ +│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │ +│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ +│ │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/alg/alg.h" +#include "libc/calls/calls.h" +#include "libc/calls/weirdtypes.h" +#include "libc/fmt/conv.h" +#include "libc/limits.h" +#include "libc/literal.h" +#include "libc/mem/mem.h" +#include "libc/rand/rand.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/temp.h" +#include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/fileno.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/ok.h" +#include "third_party/gdtoa/gdtoa.h" +#include "third_party/getopt/getopt.h" +#include "third_party/musl/crypt_des.internal.h" + +asm(".ident\t\"\\n\\n\ +Musl libc (MIT License)\\n\ +Copyright 2005-2014 Rich Felker, et. al.\""); +asm(".include \"libc/disclaimer.inc\""); +// clang-format off + +static struct expanded_key __encrypt_key; + +void setkey(const char *key) +{ + unsigned char bkey[8]; + int i, j; + + for (i = 0; i < 8; i++) { + bkey[i] = 0; + for (j = 7; j >= 0; j--, key++) + bkey[i] |= (uint32_t)(*key & 1) << j; + } + + __des_setkey(bkey, &__encrypt_key); +} + +void encrypt(char *block, int edflag) +{ + struct expanded_key decrypt_key, *key; + uint32_t b[2]; + int i, j; + char *p; + + p = block; + for (i = 0; i < 2; i++) { + b[i] = 0; + for (j = 31; j >= 0; j--, p++) + b[i] |= (uint32_t)(*p & 1) << j; + } + + key = &__encrypt_key; + if (edflag) { + key = &decrypt_key; + for (i = 0; i < 16; i++) { + decrypt_key.l[i] = __encrypt_key.l[15-i]; + decrypt_key.r[i] = __encrypt_key.r[15-i]; + } + } + + __do_des(b[0], b[1], b, b + 1, 1, 0, key); + + p = block; + for (i = 0; i < 2; i++) + for (j = 31; j >= 0; j--) + *p++ = b[i]>>j & 1; +} diff --git a/third_party/musl/musl.mk b/third_party/musl/musl.mk index 261e38ba1..446d5283a 100644 --- a/third_party/musl/musl.mk +++ b/third_party/musl/musl.mk @@ -20,6 +20,7 @@ THIRD_PARTY_MUSL_A_DIRECTDEPS = \ LIBC_ALG \ LIBC_CALLS \ LIBC_INTRIN \ + LIBC_FMT \ LIBC_MEM \ LIBC_NEXGEN32E \ LIBC_RUNTIME \ diff --git a/third_party/unzip/unix.c b/third_party/unzip/unix.c index b14d9fd2b..66a572241 100644 --- a/third_party/unzip/unix.c +++ b/third_party/unzip/unix.c @@ -134,8 +134,8 @@ static ZCONST char CannotSetTimestamps[] = #ifndef SFX #ifdef NO_DIR /* for AT&T 3B1 */ -#define opendir(path) fopen(path,"r") -#define closedir(dir) fclose(dir) +#define _opendir(path) fopen(path,"r") +#define _closedir(dir) fclose(dir) typedef FILE DIR; typedef struct zdir { FILE *dirhandle; diff --git a/tool/build/mkdeps.c b/tool/build/mkdeps.c index 8a85b3fb3..00e733bdb 100644 --- a/tool/build/mkdeps.c +++ b/tool/build/mkdeps.c @@ -25,6 +25,7 @@ #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" +#include "libc/calls/sysparam.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" @@ -58,8 +59,6 @@ #include "third_party/getopt/getopt.h" #include "tool/build/lib/getargs.h" -#define MAX_READ FRAMESIZE - /** * @fileoverview Make dependency generator. * @@ -76,10 +75,9 @@ * - # include "foo.h" * - #include "foo.h" * - * Only the first 64kb of each source file is considered. */ -_Alignas(16) const char kIncludePrefix[] = "include \""; +#define kIncludePrefix "include \"" const char kSourceExts[][5] = {".s", ".S", ".c", ".cc", ".cpp"}; @@ -252,16 +250,13 @@ int LoadRelationshipsWorker(void *arg, int tid) { int fd; ssize_t rc; bool skipme; + struct stat st; struct Edge edge; - char *buf, *freeme; - char srcbuf[PATH_MAX]; - size_t i, n, inclen, size; + size_t i, n, size, inclen; unsigned srcid, dependency; + char *buf, srcbuf[PATH_MAX]; const char *p, *pe, *src, *path, *pathend; inclen = strlen(kIncludePrefix); - freeme = buf = memalign(PAGESIZE, PAGESIZE + MAX_READ + 16); - buf += PAGESIZE; - buf[-1] = '\n'; for (;;) { pthread_mutex_lock(&galock); if ((src = getargs_next(&ga))) strcpy(srcbuf, src); @@ -277,29 +272,31 @@ int LoadRelationshipsWorker(void *arg, int tid) { pthread_mutex_lock(&reportlock); OnMissingFile(ga.path, src); } - CHECK_NE(-1, (rc = read(fd, buf, MAX_READ))); - close(fd); - size = rc; - bzero(buf + size, 16); - for (p = buf, pe = p + size; p < pe; ++p) { - p = strstr(p, kIncludePrefix); - if (!p) break; - path = p + inclen; - pathend = memchr(path, '"', pe - path); - if (pathend && (p[-1] == '#' || p[-1] == '.') && p[-2] == '\n') { - pthread_mutex_lock(&readlock); - dependency = GetSourceId(path, pathend - path); - pthread_mutex_unlock(&readlock); - edge.from = srcid; - edge.to = dependency; - pthread_mutex_lock(&writelock); - append(&edges, &edge); - pthread_mutex_unlock(&writelock); - p = pathend; + CHECK_NE(-1, fstat(fd, &st)); + if ((size = st.st_size)) { + CHECK_NE(MAP_FAILED, (buf = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0))); + for (p = buf + 1, pe = buf + size; p < pe; ++p) { + if (!(p = memmem(p, pe - p, kIncludePrefix, inclen))) break; + path = p + inclen; + pathend = memchr(path, '"', pe - path); + if (pathend && // + (p[-1] == '#' || p[-1] == '.') && // + (p - buf == 1 || p[-2] == '\n')) { // + pthread_mutex_lock(&readlock); + dependency = GetSourceId(path, pathend - path); + pthread_mutex_unlock(&readlock); + edge.from = srcid; + edge.to = dependency; + pthread_mutex_lock(&writelock); + append(&edges, &edge); + pthread_mutex_unlock(&writelock); + p = pathend; + } } + munmap(buf, size); } + close(fd); } - free(freeme); return 0; } @@ -429,9 +426,10 @@ void Explore(void) { int main(int argc, char *argv[]) { int i, fd; char path[PATH_MAX]; + ShowCrashReports(); if (argc == 2 && !strcmp(argv[1], "-n")) exit(0); GetOpts(argc, argv); - threads = GetCpuCount(); + threads = 1; // GetCpuCount(); th = calloc(threads, sizeof(*th)); bouts = calloc(threads, sizeof(*bouts)); LoadRelationships(argc, argv); diff --git a/tool/emacs/cosmo-stuff.el b/tool/emacs/cosmo-stuff.el index 456651ee4..b072d6cc6 100644 --- a/tool/emacs/cosmo-stuff.el +++ b/tool/emacs/cosmo-stuff.el @@ -465,7 +465,7 @@ (error "don't know how to show assembly for non c/c++ source file")) (let* ((default-directory root) (compile-command - (format "o//third_party/make/make.com %s -j12 -O MODE=%s %s %s" + (format "/usr/bin/make %s -j12 -O MODE=%s %s %s" (or extra-make-flags "") mode asm-gcc asm-clang))) (save-buffer) (set-visited-file-modtime (current-time)) diff --git a/tool/scripts/fix-third-party.py b/tool/scripts/fix-third-party.py index 2873f8b38..a262c460f 100755 --- a/tool/scripts/fix-third-party.py +++ b/tool/scripts/fix-third-party.py @@ -21,7 +21,7 @@ import re import sys LIBC_ISYSTEM = 'libc/isystem/' -EXTENSIONS = ('.c', '.cc', '.cpp', '.h', '.hh', '.hpp', '.inc', 'h.in') +EXTENSIONS = ('.c', '.cc', '.cpp', '.h', '.hh', '.hpp', '.inc', '.tab', 'h.in') isystem = {} for dirpath, dirs, files in os.walk(LIBC_ISYSTEM): @@ -52,24 +52,28 @@ def FixThirdParty(path): res = [] if not code.startswith('// clang-format off\n'): res.append('// clang-format off\n') - for m in re.finditer(r'# *include *"([^"]+)"', code): + for m in re.finditer(r'(?:/[/*] MISSING )?#\s*include\s*"([^"]+)"(?: \*/)?', code): end, newstart = m.span() res.append(code[start:end]) - res.append('#include "%s"' % (FixQuotedPath(path, m.group(1)))) + inc = FixQuotedPath(path, m.group(1)) + if os.path.exists(inc): + res.append('#include "%s"' % (inc)) + else: + res.append('// MISSING #include "%s"' % (inc)) start = newstart res.append(code[start:]) code = ''.join(res) res = [] start = 0 - for m in re.finditer(r'# *include *<([^>]+)>', code): + for m in re.finditer(r'(?:/[/*] MISSING )?#\s*include\s*<([^>]+)>(?: \*/)?', code): end, newstart = m.span() res.append(code[start:end]) inc = m.group(1) if inc in isystem: res.append(isystem[inc]) else: - res.append('/* MISSING #include <%s> */' % (m.group(1))) + res.append('// MISSING #include <%s>' % (m.group(1))) start = newstart res.append(code[start:]) @@ -78,8 +82,6 @@ def FixThirdParty(path): with open(path, 'wb') as f: f.write(code.encode('utf-8')) -sys.argv = ['', 'third_party/bash'] - for arg in sys.argv[1:]: if os.path.isdir(arg): for dirpath, dirs, files in os.walk(arg):