nostril

A C cli tool for creating nostr events
git clone git://jb55.com/nostril
Log | Files | Refs | README

commit 009af9ed5b873d8d5c702d5e013105d26e655d8b
Author: William Casarin <jb55@jb55.com>
Date:   Thu, 14 Apr 2022 07:52:29 -0700

initial commit

Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
A.envrc | 1+
A.gitignore | 8++++++++
AMakefile | 22++++++++++++++++++++++
Acompiler.h | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfigurator.c | 1110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acursor.h | 320+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aendian.h | 364+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahex.h | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anostril.c | 426+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arandom.h | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asha256.c | 302++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asha256.h | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ashell.nix | 5+++++
13 files changed, 2940 insertions(+), 0 deletions(-)

diff --git a/.envrc b/.envrc @@ -0,0 +1 @@ +use nix diff --git a/.gitignore b/.gitignore @@ -0,0 +1,8 @@ +*.o +nostril +configurator.out* +configurator +.build-result +.buildcmd +config.h +tags diff --git a/Makefile b/Makefile @@ -0,0 +1,22 @@ + +OBJS = sha256.o nostril.o +HEADERS = hex.h random.h config.h sha256.h + +all: nostril + +nostril: $(OBJS) $(HEADERS) + $(CC) $(OBJS) -lsecp256k1 -o $@ + +config.h: configurator + ./configurator > $@ + +configurator: configurator.c + $(CC) $< -o $@ + +clean: + rm -f nostril *.o + +tags: fake + ctags *.c *.h + +.PHONY: fake diff --git a/compiler.h b/compiler.h @@ -0,0 +1,85 @@ + +#ifndef COMPILER_H +#define COMPILER_H + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include "config.h" + +#if HAVE_UNALIGNED_ACCESS +#define alignment_ok(p, n) 1 +#else +#define alignment_ok(p, n) ((size_t)(p) % (n) == 0) +#endif + +#define UNUSED __attribute__((__unused__)) + +/** + * BUILD_ASSERT - assert a build-time dependency. + * @cond: the compile-time condition which must be true. + * + * Your compile will fail if the condition isn't true, or can't be evaluated + * by the compiler. This can only be used within a function. + * + * Example: + * #include <stddef.h> + * ... + * static char *foo_to_char(struct foo *foo) + * { + * // This code needs string to be at start of foo. + * BUILD_ASSERT(offsetof(struct foo, string) == 0); + * return (char *)foo; + * } + */ +#define BUILD_ASSERT(cond) \ + do { (void) sizeof(char [1 - 2*!(cond)]); } while(0) + +/** + * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression. + * @cond: the compile-time condition which must be true. + * + * Your compile will fail if the condition isn't true, or can't be evaluated + * by the compiler. This can be used in an expression: its value is "0". + * + * Example: + * #define foo_to_char(foo) \ + * ((char *)(foo) \ + * + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0)) + */ +#define BUILD_ASSERT_OR_ZERO(cond) \ + (sizeof(char [1 - 2*!(cond)]) - 1) + +#define memclear(mem, size) memset(mem, 0, size) +#define memclear_2(m1, s1, m2, s2) { memclear(m1, s1); memclear(m2, s2); } +#define memclear_3(m1, s1, m2, s2, m3, s3) { memclear(m1, s1); memclear(m2, s2); memclear(m3, s3); } + +static inline void *memcheck_(const void *data, size_t len) +{ + (void)len; + return (void *)data; +} + +#if HAVE_TYPEOF +/** + * memcheck - check that a memory region is initialized + * @data: start of region + * @len: length in bytes + * + * When running under valgrind, this causes an error to be printed + * if the entire region is not defined. Otherwise valgrind only + * reports an error when an undefined value is used for a branch, or + * written out. + * + * Example: + * // Search for space, but make sure it's all initialized. + * if (memchr(memcheck(somebytes, bytes_len), ' ', bytes_len)) { + * printf("space was found!\n"); + * } + */ +#define memcheck(data, len) ((__typeof__((data)+0))memcheck_((data), (len))) +#else +#define memcheck(data, len) memcheck_((data), (len)) +#endif + +#endif /* COMPILER_H */ diff --git a/configurator.c b/configurator.c @@ -0,0 +1,1110 @@ +/* Simple tool to create config.h. + * Would be much easier with ccan modules, but deliberately standalone. + * + * Copyright 2011 Rusty Russell <rusty@rustcorp.com.au>. MIT license. + * + * c12r_err, c12r_errx functions copied from ccan/err/err.c + * Copyright Rusty Russell <rusty@rustcorp.com.au>. CC0 (Public domain) License. + * + * 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. + */ +#define _POSIX_C_SOURCE 200809L /* For pclose, popen, strdup */ + +#define EXIT_BAD_USAGE 1 +#define EXIT_TROUBLE_RUNNING 2 +#define EXIT_BAD_TEST 3 +#define EXIT_BAD_INPUT 4 + +#include <errno.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef _MSC_VER +#define popen _popen +#define pclose _pclose +#endif + +#ifdef _MSC_VER +#define DEFAULT_COMPILER "cl" +/* Note: Dash options avoid POSIX path conversion when used under msys bash + * and are therefore preferred to slash (e.g. -nologo over /nologo) + * Note: Disable Warning 4200 "nonstandard extension used : zero-sized array + * in struct/union" for flexible array members. + */ +#define DEFAULT_FLAGS "-nologo -Zi -W4 -wd4200 " \ + "-D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS" +#define DEFAULT_OUTPUT_EXE_FLAG "-Fe:" +#else +#define DEFAULT_COMPILER "cc" +#define DEFAULT_FLAGS "-g3 -ggdb -Wall -Wundef -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -Wold-style-definition" +#define DEFAULT_OUTPUT_EXE_FLAG "-o" +#endif + +#define OUTPUT_FILE "configurator.out" +#define INPUT_FILE "configuratortest.c" + +#ifdef _WIN32 +#define DIR_SEP "\\" +#else +#define DIR_SEP "/" +#endif + +static const char *progname = ""; +static int verbose; +static bool like_a_libtool = false; + +struct test { + const char *name; + const char *desc; + /* + * Template style flags (pick one): + * OUTSIDE_MAIN: + * - put a simple boilerplate main below it. + * DEFINES_FUNC: + * - defines a static function called func; adds ref to avoid warnings + * INSIDE_MAIN: + * - put this inside main(). + * DEFINES_EVERYTHING: + * - don't add any boilerplate at all. + * + * Execution flags: + * EXECUTE: + * - a runtime test; must compile, exit 0 means flag is set. + * MAY_NOT_COMPILE: + * - Only useful with EXECUTE: don't get upset if it doesn't compile. + * <nothing>: + * - a compile test, if it compiles must run and exit 0. + */ + const char *style; + const char *depends; + const char *link; + const char *fragment; + const char *flags; + const char *overrides; /* On success, force this to '1' */ + bool done; + bool answer; +}; + +/* Terminated by a NULL name */ +static struct test *tests; + +static const struct test base_tests[] = { + { "HAVE_UNALIGNED_ACCESS", "unaligned access to int", + "DEFINES_EVERYTHING|EXECUTE", NULL, NULL, + "#include <string.h>\n" + "int main(int argc, char *argv[]) {\n" + " (void)argc;\n" + " char pad[sizeof(int *) * 1];\n" + " memcpy(pad, argv[0], sizeof(pad));\n" + " int *x = (int *)pad, *y = (int *)(pad + 1);\n" + " return *x == *y;\n" + "}\n" }, + { "HAVE_TYPEOF", "__typeof__ support", + "INSIDE_MAIN", NULL, NULL, + "__typeof__(argc) i; i = argc; return i == argc ? 0 : 1;" }, + { "HAVE_BIG_ENDIAN", "big endian", + "INSIDE_MAIN|EXECUTE", NULL, NULL, + "union { int i; char c[sizeof(int)]; } u;\n" + "u.i = 0x01020304;\n" + "return u.c[0] == 0x01 && u.c[1] == 0x02 && u.c[2] == 0x03 && u.c[3] == 0x04 ? 0 : 1;" }, + { "HAVE_BYTESWAP_H", "<byteswap.h>", + "OUTSIDE_MAIN", NULL, NULL, + "#include <byteswap.h>\n" }, + { "HAVE_BSWAP_64", "bswap64 in byteswap.h", + "DEFINES_FUNC", "HAVE_BYTESWAP_H", NULL, + "#include <byteswap.h>\n" + "static int func(int x) { return bswap_64(x); }" }, + { "HAVE_LITTLE_ENDIAN", "little endian", + "INSIDE_MAIN|EXECUTE", NULL, NULL, + "union { int i; char c[sizeof(int)]; } u;\n" + "u.i = 0x01020304;\n" + "return u.c[0] == 0x04 && u.c[1] == 0x03 && u.c[2] == 0x02 && u.c[3] == 0x01 ? 0 : 1;" }, + /* + { "HAVE_32BIT_OFF_T", "off_t is 32 bits", + "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE", NULL, NULL, + "#include <sys/types.h>\n" + "int main(void) {\n" + " return sizeof(off_t) == 4 ? 0 : 1;\n" + "}\n" }, + { "HAVE_ALIGNOF", "__alignof__ support", + "INSIDE_MAIN", NULL, NULL, + "return __alignof__(double) > 0 ? 0 : 1;" }, + { "HAVE_ASPRINTF", "asprintf() declaration", + "DEFINES_FUNC", NULL, NULL, + "#ifndef _GNU_SOURCE\n" + "#define _GNU_SOURCE\n" + "#endif\n" + "#include <stdio.h>\n" + "static char *func(int x) {" + " char *p;\n" + " if (asprintf(&p, \"%u\", x) == -1) \n" + " p = NULL;\n" + " return p;\n" + "}" }, + { "HAVE_ATTRIBUTE_COLD", "__attribute__((cold)) support", + "DEFINES_FUNC", NULL, NULL, + "static int __attribute__((cold)) func(int x) { return x; }" }, + { "HAVE_ATTRIBUTE_CONST", "__attribute__((const)) support", + "DEFINES_FUNC", NULL, NULL, + "static int __attribute__((const)) func(int x) { return x; }" }, + { "HAVE_ATTRIBUTE_DEPRECATED", "__attribute__((deprecated)) support", + "DEFINES_FUNC", NULL, NULL, + "static int __attribute__((deprecated)) func(int x) { return x; }" }, + { "HAVE_ATTRIBUTE_NONNULL", "__attribute__((nonnull)) support", + "DEFINES_FUNC", NULL, NULL, + "static char *__attribute__((nonnull)) func(char *p) { return p; }" }, + { "HAVE_ATTRIBUTE_RETURNS_NONNULL", "__attribute__((returns_nonnull)) support", + "DEFINES_FUNC", NULL, NULL, + "static const char *__attribute__((returns_nonnull)) func(void) { return \"hi\"; }" }, + { "HAVE_ATTRIBUTE_SENTINEL", "__attribute__((sentinel)) support", + "DEFINES_FUNC", NULL, NULL, + "static int __attribute__((sentinel)) func(int i, ...) { return i; }" }, + { "HAVE_ATTRIBUTE_PURE", "__attribute__((pure)) support", + "DEFINES_FUNC", NULL, NULL, + "static int __attribute__((pure)) func(int x) { return x; }" }, + { "HAVE_ATTRIBUTE_MAY_ALIAS", "__attribute__((may_alias)) support", + "OUTSIDE_MAIN", NULL, NULL, + "typedef short __attribute__((__may_alias__)) short_a;" }, + { "HAVE_ATTRIBUTE_NORETURN", "__attribute__((noreturn)) support", + "DEFINES_FUNC", NULL, NULL, + "#include <stdlib.h>\n" + "static void __attribute__((noreturn)) func(int x) { exit(x); }" }, + { "HAVE_ATTRIBUTE_PRINTF", "__attribute__ format printf support", + "DEFINES_FUNC", NULL, NULL, + "static void __attribute__((format(__printf__, 1, 2))) func(const char *fmt, ...) { (void)fmt; }" }, + { "HAVE_ATTRIBUTE_UNUSED", "__attribute__((unused)) support", + "OUTSIDE_MAIN", NULL, NULL, + "static int __attribute__((unused)) func(int x) { return x; }" }, + { "HAVE_ATTRIBUTE_USED", "__attribute__((used)) support", + "OUTSIDE_MAIN", NULL, NULL, + "static int __attribute__((used)) func(int x) { return x; }" }, + { "HAVE_BACKTRACE", "backtrace() in <execinfo.h>", + "DEFINES_FUNC", NULL, NULL, + "#include <execinfo.h>\n" + "static int func(int x) {" + " void *bt[10];\n" + " return backtrace(bt, 10) < x;\n" + "}" }, + { "HAVE_BUILTIN_CHOOSE_EXPR", "__builtin_choose_expr support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_choose_expr(1, 0, \"garbage\");" }, + { "HAVE_BUILTIN_CLZ", "__builtin_clz support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_clz(1) == (sizeof(int)*8 - 1) ? 0 : 1;" }, + { "HAVE_BUILTIN_CLZL", "__builtin_clzl support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_clzl(1) == (sizeof(long)*8 - 1) ? 0 : 1;" }, + { "HAVE_BUILTIN_CLZLL", "__builtin_clzll support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_clzll(1) == (sizeof(long long)*8 - 1) ? 0 : 1;" }, + { "HAVE_BUILTIN_CTZ", "__builtin_ctz support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_ctz(1 << (sizeof(int)*8 - 1)) == (sizeof(int)*8 - 1) ? 0 : 1;" }, + { "HAVE_BUILTIN_CTZL", "__builtin_ctzl support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_ctzl(1UL << (sizeof(long)*8 - 1)) == (sizeof(long)*8 - 1) ? 0 : 1;" }, + { "HAVE_BUILTIN_CTZLL", "__builtin_ctzll support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_ctzll(1ULL << (sizeof(long long)*8 - 1)) == (sizeof(long long)*8 - 1) ? 0 : 1;" }, + { "HAVE_BUILTIN_CONSTANT_P", "__builtin_constant_p support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_constant_p(1) ? 0 : 1;" }, + { "HAVE_BUILTIN_EXPECT", "__builtin_expect support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_expect(argc == 1, 1) ? 0 : 1;" }, + { "HAVE_BUILTIN_FFS", "__builtin_ffs support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_ffs(0) == 0 ? 0 : 1;" }, + { "HAVE_BUILTIN_FFSL", "__builtin_ffsl support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_ffsl(0L) == 0 ? 0 : 1;" }, + { "HAVE_BUILTIN_FFSLL", "__builtin_ffsll support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_ffsll(0LL) == 0 ? 0 : 1;" }, + { "HAVE_BUILTIN_POPCOUNT", "__builtin_popcount support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_popcount(255) == 8 ? 0 : 1;" }, + { "HAVE_BUILTIN_POPCOUNTL", "__builtin_popcountl support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_popcountl(255L) == 8 ? 0 : 1;" }, + { "HAVE_BUILTIN_POPCOUNTLL", "__builtin_popcountll support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_popcountll(255LL) == 8 ? 0 : 1;" }, + { "HAVE_BUILTIN_TYPES_COMPATIBLE_P", "__builtin_types_compatible_p support", + "INSIDE_MAIN", NULL, NULL, + "return __builtin_types_compatible_p(char *, int) ? 1 : 0;" }, + { "HAVE_ICCARM_INTRINSICS", "<intrinsics.h>", + "DEFINES_FUNC", NULL, NULL, + "#include <intrinsics.h>\n" + "int func(int v) {\n" + " return __CLZ(__RBIT(v));\n" + "}" }, + { "HAVE_CLOCK_GETTIME", "clock_gettime() declaration", + "DEFINES_FUNC", "HAVE_STRUCT_TIMESPEC", NULL, + "#include <time.h>\n" + "static struct timespec func(void) {\n" + " struct timespec ts;\n" + " clock_gettime(CLOCK_REALTIME, &ts);\n" + " return ts;\n" + "}\n" }, + { "HAVE_CLOCK_GETTIME_IN_LIBRT", "clock_gettime() in librt", + "DEFINES_FUNC", + "HAVE_STRUCT_TIMESPEC !HAVE_CLOCK_GETTIME", + "-lrt", + "#include <time.h>\n" + "static struct timespec func(void) {\n" + " struct timespec ts;\n" + " clock_gettime(CLOCK_REALTIME, &ts);\n" + " return ts;\n" + "}\n", + "HAVE_CLOCK_GETTIME" }, + { "HAVE_COMPOUND_LITERALS", "compound literal support", + "INSIDE_MAIN", NULL, NULL, + "int *foo = (int[]) { 1, 2, 3, 4 };\n" + "return foo[0] ? 0 : 1;" }, + { "HAVE_FCHDIR", "fchdir support", + "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE", NULL, NULL, + "#include <sys/types.h>\n" + "#include <sys/stat.h>\n" + "#include <fcntl.h>\n" + "#include <unistd.h>\n" + "int main(void) {\n" + " int fd = open(\"..\", O_RDONLY);\n" + " return fchdir(fd) == 0 ? 0 : 1;\n" + "}\n" }, + { "HAVE_ERR_H", "<err.h>", + "DEFINES_FUNC", NULL, NULL, + "#include <err.h>\n" + "static void func(int arg) {\n" + " if (arg == 0)\n" + " err(1, \"err %u\", arg);\n" + " if (arg == 1)\n" + " errx(1, \"err %u\", arg);\n" + " if (arg == 3)\n" + " warn(\"warn %u\", arg);\n" + " if (arg == 4)\n" + " warnx(\"warn %u\", arg);\n" + "}\n" }, + { "HAVE_FILE_OFFSET_BITS", "_FILE_OFFSET_BITS to get 64-bit offsets", + "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE", + "HAVE_32BIT_OFF_T", NULL, + "#define _FILE_OFFSET_BITS 64\n" + "#include <sys/types.h>\n" + "int main(void) {\n" + " return sizeof(off_t) == 8 ? 0 : 1;\n" + "}\n" }, + { "HAVE_FOR_LOOP_DECLARATION", "for loop declaration support", + "INSIDE_MAIN", NULL, NULL, + "int ret = 1;\n" + "for (int i = 0; i < argc; i++) { ret = 0; };\n" + "return ret;" }, + { "HAVE_FLEXIBLE_ARRAY_MEMBER", "flexible array member support", + "OUTSIDE_MAIN", NULL, NULL, + "struct foo { unsigned int x; int arr[]; };" }, + { "HAVE_GETPAGESIZE", "getpagesize() in <unistd.h>", + "DEFINES_FUNC", NULL, NULL, + "#include <unistd.h>\n" + "static int func(void) { return getpagesize(); }" }, + { "HAVE_ISBLANK", "isblank() in <ctype.h>", + "DEFINES_FUNC", NULL, NULL, + "#ifndef _GNU_SOURCE\n" + "#define _GNU_SOURCE\n" + "#endif\n" + "#include <ctype.h>\n" + "static int func(void) { return isblank(' '); }" }, + { "HAVE_MEMMEM", "memmem in <string.h>", + "DEFINES_FUNC", NULL, NULL, + "#ifndef _GNU_SOURCE\n" + "#define _GNU_SOURCE\n" + "#endif\n" + "#include <string.h>\n" + "static void *func(void *h, size_t hl, void *n, size_t nl) {\n" + "return memmem(h, hl, n, nl);" + "}\n", }, + { "HAVE_MEMRCHR", "memrchr in <string.h>", + "DEFINES_FUNC", NULL, NULL, + "#ifndef _GNU_SOURCE\n" + "#define _GNU_SOURCE\n" + "#endif\n" + "#include <string.h>\n" + "static void *func(void *s, int c, size_t n) {\n" + "return memrchr(s, c, n);" + "}\n", }, + { "HAVE_MMAP", "mmap() declaration", + "DEFINES_FUNC", NULL, NULL, + "#include <sys/mman.h>\n" + "static void *func(int fd) {\n" + " return mmap(0, 65536, PROT_READ, MAP_SHARED, fd, 0);\n" + "}" }, + { "HAVE_PROC_SELF_MAPS", "/proc/self/maps exists", + "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE", NULL, NULL, + "#include <sys/types.h>\n" + "#include <sys/stat.h>\n" + "#include <fcntl.h>\n" + "int main(void) {\n" + " return open(\"/proc/self/maps\", O_RDONLY) != -1 ? 0 : 1;\n" + "}\n" }, + { "HAVE_QSORT_R_PRIVATE_LAST", "qsort_r cmp takes trailing arg", + "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE", NULL, NULL, + "#ifndef _GNU_SOURCE\n" + "#define _GNU_SOURCE\n" + "#endif\n" + "#include <stdlib.h>\n" + "static int cmp(const void *lp, const void *rp, void *priv) {\n" + " *(unsigned int *)priv = 1;\n" + " return *(const int *)lp - *(const int *)rp; }\n" + "int main(void) {\n" + " int array[] = { 9, 2, 5 };\n" + " unsigned int called = 0;\n" + " qsort_r(array, 3, sizeof(int), cmp, &called);\n" + " return called && array[0] == 2 && array[1] == 5 && array[2] == 9 ? 0 : 1;\n" + "}\n" }, + { "HAVE_STRUCT_TIMESPEC", "struct timespec declaration", + "DEFINES_FUNC", NULL, NULL, + "#include <time.h>\n" + "static void func(void) {\n" + " struct timespec ts;\n" + " ts.tv_sec = ts.tv_nsec = 1;\n" + "}\n" }, + { "HAVE_SECTION_START_STOP", "__attribute__((section)) and __start/__stop", + "DEFINES_FUNC", NULL, NULL, + "static void *__attribute__((__section__(\"mysec\"))) p = &p;\n" + "static int func(void) {\n" + " extern void *__start_mysec[], *__stop_mysec[];\n" + " return __stop_mysec - __start_mysec;\n" + "}\n" }, + { "HAVE_STACK_GROWS_UPWARDS", "stack grows upwards", + "DEFINES_EVERYTHING|EXECUTE", NULL, NULL, + "#include <stddef.h>\n" + "static ptrdiff_t nest(const void *base, unsigned int i)\n" + "{\n" + " if (i == 0)\n" + " return (const char *)&i - (const char *)base;\n" + " return nest(base, i-1);\n" + "}\n" + "int main(int argc, char *argv[]) {\n" + " (void)argv;\n" + " return (nest(&argc, argc) > 0) ? 0 : 1;\n" + "}\n" }, + { "HAVE_STATEMENT_EXPR", "statement expression support", + "INSIDE_MAIN", NULL, NULL, + "return ({ int x = argc; x == argc ? 0 : 1; });" }, + { "HAVE_SYS_FILIO_H", "<sys/filio.h>", + "OUTSIDE_MAIN", NULL, NULL, + "#include <sys/filio.h>\n" }, + { "HAVE_SYS_TERMIOS_H", "<sys/termios.h>", + "OUTSIDE_MAIN", NULL, NULL, + "#include <sys/termios.h>\n" }, + { "HAVE_SYS_UNISTD_H", "<sys/unistd.h>", + "OUTSIDE_MAIN", NULL, NULL, + "#include <sys/unistd.h>\n" }, + { "HAVE_UTIME", "utime() declaration", + "DEFINES_FUNC", NULL, NULL, + "#include <sys/types.h>\n" + "#include <utime.h>\n" + "static int func(const char *filename) {\n" + " struct utimbuf times = { 0 };\n" + " return utime(filename, &times);\n" + "}" }, + { "HAVE_WARN_UNUSED_RESULT", "__attribute__((warn_unused_result))", + "DEFINES_FUNC", NULL, NULL, + "#include <sys/types.h>\n" + "#include <utime.h>\n" + "static __attribute__((warn_unused_result)) int func(int i) {\n" + " return i + 1;\n" + "}" }, + { "HAVE_OPENMP", "#pragma omp and -fopenmp support", + "INSIDE_MAIN|EXECUTE|MAY_NOT_COMPILE", NULL, NULL, + "int i;\n" + "#pragma omp parallel for\n" + "for(i = 0; i < 0; i++) {};\n" + "return 0;\n", + "-Werror -fopenmp" }, + { "HAVE_VALGRIND_MEMCHECK_H", "<valgrind/memcheck.h>", + "OUTSIDE_MAIN", NULL, NULL, + "#include <valgrind/memcheck.h>\n" }, + { "HAVE_UCONTEXT", "working <ucontext.h", + "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE", + NULL, NULL, + "#include <ucontext.h>\n" + "static int x = 0;\n" + "static char stack[2048];\n" + "static ucontext_t a, b;\n" + "static void fn(void) {\n" + " x |= 2;\n" + " setcontext(&b);\n" + " x |= 4;\n" + "}\n" + "int main(void) {\n" + " x |= 1;\n" + " getcontext(&a);\n" + " a.uc_stack.ss_sp = stack;\n" + " a.uc_stack.ss_size = sizeof(stack);\n" + " makecontext(&a, fn, 0);\n" + " swapcontext(&b, &a);\n" + " return (x == 3) ? 0 : 1;\n" + "}\n" + }, + { "HAVE_POINTER_SAFE_MAKECONTEXT", "passing pointers via makecontext()", + "DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE", + "HAVE_UCONTEXT", NULL, + "#include <stddef.h>\n" + "#include <ucontext.h>\n" + "static int worked = 0;\n" + "static char stack[1024];\n" + "static ucontext_t a, b;\n" + "static void fn(void *p, void *q) {\n" + " void *cp = &worked;\n" + " void *cq = (void *)(~((ptrdiff_t)cp));\n" + " if ((p == cp) && (q == cq))\n" + " worked = 1;\n" + " setcontext(&b);\n" + "}\n" + "int main(void) {\n" + " void *ap = &worked;\n" + " void *aq = (void *)(~((ptrdiff_t)ap));\n" + " getcontext(&a);\n" + " a.uc_stack.ss_sp = stack;\n" + " a.uc_stack.ss_size = sizeof(stack);\n" + " makecontext(&a, (void (*)(void))fn, 2, ap, aq);\n" + " swapcontext(&b, &a);\n" + " return worked ? 0 : 1;\n" + "}\n" + }, + { "HAVE_BUILTIN_CPU_SUPPORTS", "__builtin_cpu_supports()", + "DEFINES_FUNC", NULL, NULL, + "#include <stdbool.h>\n" + "static bool func(void) {\n" + " return __builtin_cpu_supports(\"mmx\");\n" + "}" + }, + { "HAVE_CLOSEFROM", "closefrom() offered by system", + "DEFINES_EVERYTHING", NULL, NULL, + "#include <stdlib.h>\n" + "#include <unistd.h>\n" + "int main(void) {\n" + " closefrom(STDERR_FILENO + 1);\n" + " return 0;\n" + "}\n" + }, + { "HAVE_F_CLOSEM", "F_CLOSEM defined for fctnl.", + "DEFINES_EVERYTHING", NULL, NULL, + "#include <fcntl.h>\n" + "#include <unistd.h>\n" + "int main(void) {\n" + " int res = fcntl(STDERR_FILENO + 1, F_CLOSEM, 0);\n" + " return res < 0;\n" + "}\n" + }, + { "HAVE_NR_CLOSE_RANGE", "close_range syscall available as __NR_close_range.", + "DEFINES_EVERYTHING", NULL, NULL, + "#include <limits.h>\n" + "#include <sys/syscall.h>\n" + "#include <unistd.h>\n" + "int main(void) {\n" + " int res = syscall(__NR_close_range, STDERR_FILENO + 1, INT_MAX, 0);\n" + " return res < 0;\n" + "}\n" + }, + { "HAVE_F_MAXFD", "F_MAXFD defined for fcntl.", + "DEFINES_EVERYTHING", NULL, NULL, + "#include <fcntl.h>\n" + "#include <unistd.h>\n" + "int main(void) {\n" + " int res = fcntl(0, F_MAXFD);\n" + " return res < 0;\n" + "}\n" + }, + */ +}; + +static void c12r_err(int eval, const char *fmt, ...) +{ + int err_errno = errno; + va_list ap; + + fprintf(stderr, "%s: ", progname); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, ": %s\n", strerror(err_errno)); + exit(eval); +} + +static void c12r_errx(int eval, const char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "%s: ", progname); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(eval); +} + +static void start_test(const char *what, const char *why) +{ + if (like_a_libtool) { + printf("%s%s... ", what, why); + fflush(stdout); + } +} + +static void end_test(bool result) +{ + if (like_a_libtool) + printf("%s\n", result ? "yes" : "no"); +} + +static size_t fcopy(FILE *fsrc, FILE *fdst) +{ + char buffer[BUFSIZ]; + size_t rsize, wsize; + size_t copied = 0; + + while ((rsize = fread(buffer, 1, BUFSIZ, fsrc)) > 0) { + wsize = fwrite(buffer, 1, rsize, fdst); + copied += wsize; + if (wsize != rsize) + break; + } + + return copied; +} + +static char *grab_stream(FILE *file) +{ + size_t max, ret, size = 0; + char *buffer; + + max = BUFSIZ; + buffer = malloc(max); + while ((ret = fread(buffer+size, 1, max - size, file)) == max - size) { + size += ret; + buffer = realloc(buffer, max *= 2); + } + size += ret; + if (ferror(file)) + c12r_err(EXIT_TROUBLE_RUNNING, "reading from command"); + buffer[size] = '\0'; + return buffer; +} + +static char *run(const char *cmd, int *exitstatus) +{ + static const char redir[] = " 2>&1"; + size_t cmdlen; + char *cmdredir; + FILE *cmdout; + char *ret; + + cmdlen = strlen(cmd); + cmdredir = malloc(cmdlen + sizeof(redir)); + memcpy(cmdredir, cmd, cmdlen); + memcpy(cmdredir + cmdlen, redir, sizeof(redir)); + + cmdout = popen(cmdredir, "r"); + if (!cmdout) + c12r_err(EXIT_TROUBLE_RUNNING, "popen \"%s\"", cmdredir); + + free(cmdredir); + + ret = grab_stream(cmdout); + *exitstatus = pclose(cmdout); + return ret; +} + +static char *connect_args(const char *argv[], const char *outflag, + const char *files) +{ + unsigned int i; + char *ret; + size_t len = strlen(outflag) + strlen(files) + 1; + + for (i = 1; argv[i]; i++) + len += 1 + strlen(argv[i]); + + ret = malloc(len); + len = 0; + for (i = 1; argv[i]; i++) { + strcpy(ret + len, argv[i]); + len += strlen(argv[i]); + if (argv[i+1] || *outflag) + ret[len++] = ' '; + } + strcpy(ret + len, outflag); + len += strlen(outflag); + strcpy(ret + len, files); + return ret; +} + +static struct test *find_test(const char *name) +{ + unsigned int i; + + for (i = 0; tests[i].name; i++) { + if (strcmp(tests[i].name, name) == 0) + return &tests[i]; + } + c12r_errx(EXIT_BAD_TEST, "Unknown test %s", name); + abort(); +} + +#define PRE_BOILERPLATE "/* Test program generated by configurator. */\n" +#define MAIN_START_BOILERPLATE \ + "int main(int argc, char *argv[]) {\n" \ + " (void)argc;\n" \ + " (void)argv;\n" +#define USE_FUNC_BOILERPLATE "(void)func;\n" +#define MAIN_BODY_BOILERPLATE "return 0;\n" +#define MAIN_END_BOILERPLATE "}\n" + +static bool run_test(const char *cmd, const char *wrapper, struct test *test) +{ + char *output, *newcmd; + FILE *outf; + int status; + + if (test->done) + return test->answer; + + if (test->depends) { + size_t len; + const char *deps = test->depends; + char *dep; + + /* Space-separated dependencies, could be ! for inverse. */ + while ((len = strcspn(deps, " ")) != 0) { + bool positive = true; + if (deps[len]) { + dep = strdup(deps); + dep[len] = '\0'; + } else { + dep = (char *)deps; + } + + if (dep[0] == '!') { + dep++; + positive = false; + } + if (run_test(cmd, wrapper, find_test(dep)) != positive) { + test->answer = false; + test->done = true; + return test->answer; + } + if (deps[len]) + free(dep); + + deps += len; + deps += strspn(deps, " "); + } + } + + outf = fopen(INPUT_FILE, verbose > 1 ? "w+" : "w"); + if (!outf) + c12r_err(EXIT_TROUBLE_RUNNING, "creating %s", INPUT_FILE); + + fprintf(outf, "%s", PRE_BOILERPLATE); + + if (strstr(test->style, "INSIDE_MAIN")) { + fprintf(outf, "%s", MAIN_START_BOILERPLATE); + fprintf(outf, "%s", test->fragment); + fprintf(outf, "%s", MAIN_END_BOILERPLATE); + } else if (strstr(test->style, "OUTSIDE_MAIN")) { + fprintf(outf, "%s", test->fragment); + fprintf(outf, "%s", MAIN_START_BOILERPLATE); + fprintf(outf, "%s", MAIN_BODY_BOILERPLATE); + fprintf(outf, "%s", MAIN_END_BOILERPLATE); + } else if (strstr(test->style, "DEFINES_FUNC")) { + fprintf(outf, "%s", test->fragment); + fprintf(outf, "%s", MAIN_START_BOILERPLATE); + fprintf(outf, "%s", USE_FUNC_BOILERPLATE); + fprintf(outf, "%s", MAIN_BODY_BOILERPLATE); + fprintf(outf, "%s", MAIN_END_BOILERPLATE); + } else if (strstr(test->style, "DEFINES_EVERYTHING")) { + fprintf(outf, "%s", test->fragment); + } else + c12r_errx(EXIT_BAD_TEST, "Unknown style for test %s: %s", + test->name, test->style); + + if (verbose > 1) { + fseek(outf, 0, SEEK_SET); + fcopy(outf, stdout); + } + + fclose(outf); + + newcmd = strdup(cmd); + + if (test->flags) { + newcmd = realloc(newcmd, strlen(newcmd) + strlen(" ") + + strlen(test->flags) + 1); + strcat(newcmd, " "); + strcat(newcmd, test->flags); + if (verbose > 1) + printf("Extra flags line: %s", newcmd); + } + + if (test->link) { + newcmd = realloc(newcmd, strlen(newcmd) + strlen(" ") + + strlen(test->link) + 1); + strcat(newcmd, " "); + strcat(newcmd, test->link); + if (verbose > 1) + printf("Extra link line: %s", newcmd); + } + + start_test("checking for ", test->desc); + output = run(newcmd, &status); + + free(newcmd); + + if (status != 0 || strstr(output, "warning")) { + if (verbose) + printf("Compile %s for %s, status %i: %s\n", + status ? "fail" : "warning", + test->name, status, output); + if (strstr(test->style, "EXECUTE") + && !strstr(test->style, "MAY_NOT_COMPILE")) + c12r_errx(EXIT_BAD_TEST, + "Test for %s did not compile:\n%s", + test->name, output); + test->answer = false; + free(output); + } else { + /* Compile succeeded. */ + free(output); + /* We run INSIDE_MAIN tests for sanity checking. */ + if (strstr(test->style, "EXECUTE") + || strstr(test->style, "INSIDE_MAIN")) { + char *cmd = malloc(strlen(wrapper) + strlen(" ." DIR_SEP OUTPUT_FILE) + 1); + + strcpy(cmd, wrapper); + strcat(cmd, " ." DIR_SEP OUTPUT_FILE); + output = run(cmd, &status); + free(cmd); + if (!strstr(test->style, "EXECUTE") && status != 0) + c12r_errx(EXIT_BAD_TEST, + "Test for %s failed with %i:\n%s", + test->name, status, output); + if (verbose && status) + printf("%s exited %i\n", test->name, status); + free(output); + } + test->answer = (status == 0); + } + test->done = true; + end_test(test->answer); + + if (test->answer && test->overrides) { + struct test *override = find_test(test->overrides); + override->done = true; + override->answer = true; + } + return test->answer; +} + +static char *any_field(char **fieldname) +{ + char buf[1000]; + for (;;) { + char *p, *eq; + + if (!fgets(buf, sizeof(buf), stdin)) + return NULL; + + p = buf; + /* Ignore whitespace, lines starting with # */ + while (*p == ' ' || *p == '\t') + p++; + if (*p == '#' || *p == '\n') + continue; + + eq = strchr(p, '='); + if (!eq) + c12r_errx(EXIT_BAD_INPUT, "no = in line: %s", p); + *eq = '\0'; + *fieldname = strdup(p); + p = eq + 1; + if (strlen(p) && p[strlen(p)-1] == '\n') + p[strlen(p)-1] = '\0'; + return strdup(p); + } +} + +static char *read_field(const char *name, bool compulsory) +{ + char *fieldname, *value; + + value = any_field(&fieldname); + if (!value) { + if (!compulsory) + return NULL; + c12r_errx(EXIT_BAD_INPUT, "Could not read field %s", name); + } + if (strcmp(fieldname, name) != 0) + c12r_errx(EXIT_BAD_INPUT, + "Expected field %s not %s", name, fieldname); + return value; +} + +/* Test descriptions from stdin: + * Lines starting with # or whitespace-only are ignored. + * + * First three non-ignored lines must be: + * var=<varname> + * desc=<description-for-autotools-style> + * style=OUTSIDE_MAIN DEFINES_FUNC INSIDE_MAIN DEFINES_EVERYTHING EXECUTE MAY_NOT_COMPILE + * + * Followed by optional lines: + * depends=<space-separated-testnames, ! to invert> + * link=<extra args for link line> + * flags=<extra args for compile line> + * overrides=<testname-to-force> + * + * Finally a code line, either: + * code=<oneline> OR + * code= + * <lines of code> + * <end-comment> + * + * And <end-comment> looks like this next comment: */ +/*END*/ +static bool read_test(struct test *test) +{ + char *field, *value; + char buf[1000]; + + memset(test, 0, sizeof(*test)); + test->name = read_field("var", false); + if (!test->name) + return false; + test->desc = read_field("desc", true); + test->style = read_field("style", true); + /* Read any optional fields. */ + while ((value = any_field(&field)) != NULL) { + if (strcmp(field, "depends") == 0) + test->depends = value; + else if (strcmp(field, "link") == 0) + test->link = value; + else if (strcmp(field, "flags") == 0) + test->flags = value; + else if (strcmp(field, "overrides") == 0) + test->overrides = value; + else if (strcmp(field, "code") == 0) + break; + else + c12r_errx(EXIT_BAD_INPUT, "Unknown field %s in %s", + field, test->name); + } + if (!value) + c12r_errx(EXIT_BAD_INPUT, "Missing code in %s", test->name); + + if (strlen(value) == 0) { + /* Multiline program, read to END comment */ + while (fgets(buf, sizeof(buf), stdin) != 0) { + size_t n; + if (strncmp(buf, "/*END*/", 7) == 0) + break; + n = strlen(value); + value = realloc(value, n + strlen(buf) + 1); + strcpy(value + n, buf); + n += strlen(buf); + } + } + test->fragment = value; + return true; +} + +static void read_tests(size_t num_tests) +{ + while (read_test(tests + num_tests)) { + num_tests++; + tests = realloc(tests, (num_tests + 1) * sizeof(tests[0])); + tests[num_tests].name = NULL; + } +} + +int main(int argc, const char *argv[]) +{ + char *cmd; + unsigned int i; + const char *default_args[] + = { "", DEFAULT_COMPILER, DEFAULT_FLAGS, NULL }; + const char *outflag = DEFAULT_OUTPUT_EXE_FLAG; + const char *configurator_cc = NULL; + const char *wrapper = ""; + const char *orig_cc; + const char *varfile = NULL; + const char *headerfile = NULL; + bool extra_tests = false; + FILE *outf; + + if (argc > 0) + progname = argv[0]; + + while (argc > 1) { + if (strcmp(argv[1], "--help") == 0) { + printf("Usage: configurator [-v] [--var-file=<filename>] [-O<outflag>] [--configurator-cc=<compiler-for-tests>] [--wrapper=<wrapper-for-tests>] [--autotools-style] [--extra-tests] [<compiler> <flags>...]\n" + " <compiler> <flags> will have \"<outflag> <outfile> <infile.c>\" appended\n" + "Default: %s %s %s\n", + DEFAULT_COMPILER, DEFAULT_FLAGS, + DEFAULT_OUTPUT_EXE_FLAG); + exit(0); + } + if (strncmp(argv[1], "-O", 2) == 0) { + argc--; + argv++; + outflag = argv[1] + 2; + if (!*outflag) { + fprintf(stderr, + "%s: option requires an argument -- O\n", + argv[0]); + exit(EXIT_BAD_USAGE); + } + } else if (strcmp(argv[1], "-v") == 0) { + argc--; + argv++; + verbose++; + } else if (strcmp(argv[1], "-vv") == 0) { + argc--; + argv++; + verbose += 2; + } else if (strncmp(argv[1], "--configurator-cc=", 18) == 0) { + configurator_cc = argv[1] + 18; + argc--; + argv++; + } else if (strncmp(argv[1], "--wrapper=", 10) == 0) { + wrapper = argv[1] + 10; + argc--; + argv++; + } else if (strncmp(argv[1], "--var-file=", 11) == 0) { + varfile = argv[1] + 11; + argc--; + argv++; + } else if (strcmp(argv[1], "--autotools-style") == 0) { + like_a_libtool = true; + argc--; + argv++; + } else if (strncmp(argv[1], "--header-file=", 14) == 0) { + headerfile = argv[1] + 14; + argc--; + argv++; + } else if (strcmp(argv[1], "--extra-tests") == 0) { + extra_tests = true; + argc--; + argv++; + } else if (strcmp(argv[1], "--") == 0) { + break; + } else if (argv[1][0] == '-') { + c12r_errx(EXIT_BAD_USAGE, "Unknown option %s", argv[1]); + } else { + break; + } + } + + if (argc == 1) + argv = default_args; + + /* Copy with NULL entry at end */ + tests = calloc(sizeof(base_tests)/sizeof(base_tests[0]) + 1, + sizeof(base_tests[0])); + memcpy(tests, base_tests, sizeof(base_tests)); + + if (extra_tests) + read_tests(sizeof(base_tests)/sizeof(base_tests[0])); + + orig_cc = argv[1]; + if (configurator_cc) + argv[1] = configurator_cc; + + cmd = connect_args(argv, outflag, OUTPUT_FILE " " INPUT_FILE); + if (like_a_libtool) { + start_test("Making autoconf users comfortable", ""); + sleep(1); + end_test(1); + } + for (i = 0; tests[i].name; i++) + run_test(cmd, wrapper, &tests[i]); + free(cmd); + + remove(OUTPUT_FILE); + remove(INPUT_FILE); + + if (varfile) { + FILE *vars; + + if (strcmp(varfile, "-") == 0) + vars = stdout; + else { + start_test("Writing variables to ", varfile); + vars = fopen(varfile, "a"); + if (!vars) + c12r_err(EXIT_TROUBLE_RUNNING, + "Could not open %s", varfile); + } + for (i = 0; tests[i].name; i++) + fprintf(vars, "%s=%u\n", tests[i].name, tests[i].answer); + if (vars != stdout) { + if (fclose(vars) != 0) + c12r_err(EXIT_TROUBLE_RUNNING, + "Closing %s", varfile); + end_test(1); + } + } + + if (headerfile) { + start_test("Writing header to ", headerfile); + outf = fopen(headerfile, "w"); + if (!outf) + c12r_err(EXIT_TROUBLE_RUNNING, + "Could not open %s", headerfile); + } else + outf = stdout; + + fprintf(outf, "/* Generated by CCAN configurator */\n" + "#ifndef CCAN_CONFIG_H\n" + "#define CCAN_CONFIG_H\n"); + fprintf(outf, "#ifndef _GNU_SOURCE\n"); + fprintf(outf, "#define _GNU_SOURCE /* Always use GNU extensions. */\n"); + fprintf(outf, "#endif\n"); + fprintf(outf, "#define CCAN_COMPILER \"%s\"\n", orig_cc); + cmd = connect_args(argv + 1, "", ""); + fprintf(outf, "#define CCAN_CFLAGS \"%s\"\n", cmd); + free(cmd); + fprintf(outf, "#define CCAN_OUTPUT_EXE_CFLAG \"%s\"\n\n", outflag); + /* This one implies "#include <ccan/..." works, eg. for tdb2.h */ + fprintf(outf, "#define HAVE_CCAN 1\n"); + for (i = 0; tests[i].name; i++) + fprintf(outf, "#define %s %u\n", tests[i].name, tests[i].answer); + fprintf(outf, "#endif /* CCAN_CONFIG_H */\n"); + + if (headerfile) { + if (fclose(outf) != 0) + c12r_err(EXIT_TROUBLE_RUNNING, "Closing %s", headerfile); + end_test(1); + } + + return 0; +} diff --git a/cursor.h b/cursor.h @@ -0,0 +1,320 @@ + +#ifndef CURSOR_H +#define CURSOR_H + +#include <stdio.h> +#include <assert.h> +#include <string.h> + +#define unlikely(x) __builtin_expect((x),0) +#define likely(x) __builtin_expect((x),1) + +struct cursor { + unsigned char *start; + unsigned char *p; + unsigned char *end; +}; + +struct array { + struct cursor cur; + unsigned int elem_size; +}; + +static inline void reset_cursor(struct cursor *cursor) +{ + cursor->p = cursor->start; +} + +static inline void wipe_cursor(struct cursor *cursor) +{ + reset_cursor(cursor); + memset(cursor->start, 0, cursor->end - cursor->start); +} + +static inline void make_cursor(unsigned char *start, unsigned char *end, struct cursor *cursor) +{ + cursor->start = start; + cursor->p = start; + cursor->end = end; +} + +static inline void make_array(struct array *a, unsigned char* start, unsigned char *end, unsigned int elem_size) +{ + make_cursor(start, end, &a->cur); + a->elem_size = elem_size; +} + +static inline int cursor_eof(struct cursor *c) +{ + return c->p == c->end; +} + +static inline void *cursor_malloc(struct cursor *mem, unsigned long size) +{ + void *ret; + + if (mem->p + size > mem->end) { + return NULL; + } + + ret = mem->p; + mem->p += size; + + return ret; +} + +static inline void *cursor_alloc(struct cursor *mem, unsigned long size) +{ + void *ret; + if (!(ret = cursor_malloc(mem, size))) { + return 0; + } + + memset(ret, 0, size); + return ret; +} + +static inline int cursor_slice(struct cursor *mem, struct cursor *slice, size_t size) +{ + unsigned char *p; + if (!(p = cursor_alloc(mem, size))) { + return 0; + } + make_cursor(p, mem->p, slice); + return 1; +} + + +static inline void copy_cursor(struct cursor *src, struct cursor *dest) +{ + dest->start = src->start; + dest->p = src->p; + dest->end = src->end; +} + +static inline int pull_byte(struct cursor *cursor, unsigned char *c) +{ + if (unlikely(cursor->p + 1 > cursor->end)) + return 0; + + *c = *cursor->p; + cursor->p++; + + return 1; +} + +static inline int cursor_pull_c_str(struct cursor *cursor, const char **str) +{ + *str = (const char*)cursor->p; + + for (; cursor->p < cursor->end; cursor->p++) { + if (*cursor->p == 0) { + cursor->p++; + return 1; + } + } + + return 0; +} + + +static inline int cursor_push_byte(struct cursor *cursor, unsigned char c) +{ + if (unlikely(cursor->p + 1 > cursor->end)) { + return 0; + } + + *cursor->p = c; + cursor->p++; + + return 1; +} + +static inline int cursor_pull(struct cursor *cursor, unsigned char *data, int len) +{ + if (unlikely(cursor->p + len > cursor->end)) { + return 0; + } + + memcpy(data, cursor->p, len); + cursor->p += len; + + return 1; +} + +static inline int pull_data_into_cursor(struct cursor *cursor, + struct cursor *dest, + unsigned char **data, + int len) +{ + int ok; + + if (unlikely(dest->p + len > dest->end)) { + printf("not enough room in dest buffer\n"); + return 0; + } + + ok = cursor_pull(cursor, dest->p, len); + if (!ok) return 0; + + *data = dest->p; + dest->p += len; + + return 1; +} + +static inline int cursor_dropn(struct cursor *cur, int size, int n) +{ + if (n == 0) + return 1; + + if (unlikely(cur->p - size*n < cur->start)) { + return 0; + } + + cur->p -= size*n; + return 1; +} + +static inline int cursor_drop(struct cursor *cur, int size) +{ + return cursor_dropn(cur, size, 1); +} + +static inline unsigned char *cursor_topn(struct cursor *cur, int len, int n) +{ + n += 1; + if (unlikely(cur->p - len*n < cur->start)) { + return NULL; + } + return cur->p - len*n; +} + +static inline unsigned char *cursor_top(struct cursor *cur, int len) +{ + if (unlikely(cur->p - len < cur->start)) { + return NULL; + } + return cur->p - len; +} + +static inline int cursor_top_int(struct cursor *cur, int *i) +{ + unsigned char *p; + if (unlikely(!(p = cursor_top(cur, sizeof(*i))))) { + return 0; + } + *i = *((int*)p); + return 1; +} + +static inline int cursor_pop(struct cursor *cur, unsigned char *data, int len) +{ + if (unlikely(cur->p - len < cur->start)) { + return 0; + } + + cur->p -= len; + memcpy(data, cur->p, len); + + return 1; +} + +static inline int cursor_push(struct cursor *cursor, unsigned char *data, int len) +{ + if (unlikely(cursor->p + len >= cursor->end)) { + return 0; + } + + if (cursor->p != data) + memcpy(cursor->p, data, len); + + cursor->p += len; + + return 1; +} + +static inline int cursor_push_int(struct cursor *cursor, int i) +{ + return cursor_push(cursor, (unsigned char*)&i, sizeof(i)); +} + +static inline int cursor_len(struct cursor *cursor) +{ + return cursor->p - cursor->start; +} + +static inline size_t cursor_count(struct cursor *cursor, size_t elem_size) +{ + return cursor_len(cursor)/elem_size; +} + +static inline int cursor_pull_int(struct cursor *cursor, int *i) +{ + return cursor_pull(cursor, (unsigned char*)i, sizeof(*i)); +} + +static inline int cursor_push_u16(struct cursor *cursor, unsigned short i) +{ + return cursor_push(cursor, (unsigned char*)&i, sizeof(i)); +} + +static inline void *index_cursor(struct cursor *cursor, unsigned int index, int elem_size) +{ + unsigned char *p; + p = &cursor->start[elem_size * index]; + + if (unlikely(p >= cursor->end)) + return NULL; + + return (void*)p; +} + + +static inline int push_sized_str(struct cursor *cursor, const char *str, int len) +{ + return cursor_push(cursor, (unsigned char*)str, len); +} + +static inline int cursor_push_str(struct cursor *cursor, const char *str) +{ + return cursor_push(cursor, (unsigned char*)str, strlen(str)); +} + +static inline int cursor_push_c_str(struct cursor *cursor, const char *str) +{ + return cursor_push_str(cursor, str) && cursor_push_byte(cursor, 0); +} + +static inline int cursor_remaining_capacity(struct cursor *cursor) +{ + return cursor->end - cursor->p; +} + + +#define max(a,b) ((a) > (b) ? (a) : (b)) +static inline void cursor_print_around(struct cursor *cur, int range) +{ + unsigned char *c; + + printf("[%ld/%ld]\n", cur->p - cur->start, cur->end - cur->start); + + c = max(cur->p - range, cur->start); + for (; c < cur->end && c < (cur->p + range); c++) { + printf("%02x", *c); + } + printf("\n"); + + c = max(cur->p - range, cur->start); + for (; c < cur->end && c < (cur->p + range); c++) { + if (c == cur->p) { + printf("^"); + continue; + } + printf(" "); + } + printf("\n"); +} +#undef max + +#endif diff --git a/endian.h b/endian.h @@ -0,0 +1,364 @@ +/* CC0 (Public domain) */ +#ifndef CCAN_ENDIAN_H +#define CCAN_ENDIAN_H +#include <stdint.h> + +#include "config.h" +#include "cursor.h" + +/** + * BSWAP_16 - reverse bytes in a constant uint16_t value. + * @val: constant value whose bytes to swap. + * + * Designed to be usable in constant-requiring initializers. + * + * Example: + * struct mystruct { + * char buf[BSWAP_16(0x1234)]; + * }; + */ +#define BSWAP_16(val) \ + ((((uint16_t)(val) & 0x00ff) << 8) \ + | (((uint16_t)(val) & 0xff00) >> 8)) + +/** + * BSWAP_32 - reverse bytes in a constant uint32_t value. + * @val: constant value whose bytes to swap. + * + * Designed to be usable in constant-requiring initializers. + * + * Example: + * struct mystruct { + * char buf[BSWAP_32(0xff000000)]; + * }; + */ +#define BSWAP_32(val) \ + ((((uint32_t)(val) & 0x000000ff) << 24) \ + | (((uint32_t)(val) & 0x0000ff00) << 8) \ + | (((uint32_t)(val) & 0x00ff0000) >> 8) \ + | (((uint32_t)(val) & 0xff000000) >> 24)) + +/** + * BSWAP_64 - reverse bytes in a constant uint64_t value. + * @val: constantvalue whose bytes to swap. + * + * Designed to be usable in constant-requiring initializers. + * + * Example: + * struct mystruct { + * char buf[BSWAP_64(0xff00000000000000ULL)]; + * }; + */ +#define BSWAP_64(val) \ + ((((uint64_t)(val) & 0x00000000000000ffULL) << 56) \ + | (((uint64_t)(val) & 0x000000000000ff00ULL) << 40) \ + | (((uint64_t)(val) & 0x0000000000ff0000ULL) << 24) \ + | (((uint64_t)(val) & 0x00000000ff000000ULL) << 8) \ + | (((uint64_t)(val) & 0x000000ff00000000ULL) >> 8) \ + | (((uint64_t)(val) & 0x0000ff0000000000ULL) >> 24) \ + | (((uint64_t)(val) & 0x00ff000000000000ULL) >> 40) \ + | (((uint64_t)(val) & 0xff00000000000000ULL) >> 56)) + +#if HAVE_BYTESWAP_H +#include <byteswap.h> +#else +/** + * bswap_16 - reverse bytes in a uint16_t value. + * @val: value whose bytes to swap. + * + * Example: + * // Output contains "1024 is 4 as two bytes reversed" + * printf("1024 is %u as two bytes reversed\n", bswap_16(1024)); + */ +static inline uint16_t bswap_16(uint16_t val) +{ + return BSWAP_16(val); +} + +/** + * bswap_32 - reverse bytes in a uint32_t value. + * @val: value whose bytes to swap. + * + * Example: + * // Output contains "1024 is 262144 as four bytes reversed" + * printf("1024 is %u as four bytes reversed\n", bswap_32(1024)); + */ +static inline uint32_t bswap_32(uint32_t val) +{ + return BSWAP_32(val); +} +#endif /* !HAVE_BYTESWAP_H */ + +#if !HAVE_BSWAP_64 +/** + * bswap_64 - reverse bytes in a uint64_t value. + * @val: value whose bytes to swap. + * + * Example: + * // Output contains "1024 is 1125899906842624 as eight bytes reversed" + * printf("1024 is %llu as eight bytes reversed\n", + * (unsigned long long)bswap_64(1024)); + */ +static inline uint64_t bswap_64(uint64_t val) +{ + return BSWAP_64(val); +} +#endif + +/* Needed for Glibc like endiness check */ +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 + +/* Sanity check the defines. We don't handle weird endianness. */ +#if !HAVE_LITTLE_ENDIAN && !HAVE_BIG_ENDIAN +#error "Unknown endian" +#elif HAVE_LITTLE_ENDIAN && HAVE_BIG_ENDIAN +#error "Can't compile for both big and little endian." +#elif HAVE_LITTLE_ENDIAN +#ifndef __BYTE_ORDER +#define __BYTE_ORDER __LITTLE_ENDIAN +#elif __BYTE_ORDER != __LITTLE_ENDIAN +#error "__BYTE_ORDER already defined, but not equal to __LITTLE_ENDIAN" +#endif +#elif HAVE_BIG_ENDIAN +#ifndef __BYTE_ORDER +#define __BYTE_ORDER __BIG_ENDIAN +#elif __BYTE_ORDER != __BIG_ENDIAN +#error "__BYTE_ORDER already defined, but not equal to __BIG_ENDIAN" +#endif +#endif + + +#ifdef __CHECKER__ +/* sparse needs forcing to remove bitwise attribute from ccan/short_types */ +#define ENDIAN_CAST __attribute__((force)) +#define ENDIAN_TYPE __attribute__((bitwise)) +#else +#define ENDIAN_CAST +#define ENDIAN_TYPE +#endif + +typedef uint64_t ENDIAN_TYPE leint64_t; +typedef uint64_t ENDIAN_TYPE beint64_t; +typedef uint32_t ENDIAN_TYPE leint32_t; +typedef uint32_t ENDIAN_TYPE beint32_t; +typedef uint16_t ENDIAN_TYPE leint16_t; +typedef uint16_t ENDIAN_TYPE beint16_t; + +#if HAVE_LITTLE_ENDIAN +/** + * CPU_TO_LE64 - convert a constant uint64_t value to little-endian + * @native: constant to convert + */ +#define CPU_TO_LE64(native) ((ENDIAN_CAST leint64_t)(native)) + +/** + * CPU_TO_LE32 - convert a constant uint32_t value to little-endian + * @native: constant to convert + */ +#define CPU_TO_LE32(native) ((ENDIAN_CAST leint32_t)(native)) + +/** + * CPU_TO_LE16 - convert a constant uint16_t value to little-endian + * @native: constant to convert + */ +#define CPU_TO_LE16(native) ((ENDIAN_CAST leint16_t)(native)) + +/** + * LE64_TO_CPU - convert a little-endian uint64_t constant + * @le_val: little-endian constant to convert + */ +#define LE64_TO_CPU(le_val) ((ENDIAN_CAST uint64_t)(le_val)) + +/** + * LE32_TO_CPU - convert a little-endian uint32_t constant + * @le_val: little-endian constant to convert + */ +#define LE32_TO_CPU(le_val) ((ENDIAN_CAST uint32_t)(le_val)) + +/** + * LE16_TO_CPU - convert a little-endian uint16_t constant + * @le_val: little-endian constant to convert + */ +#define LE16_TO_CPU(le_val) ((ENDIAN_CAST uint16_t)(le_val)) + +#else /* ... HAVE_BIG_ENDIAN */ +#define CPU_TO_LE64(native) ((ENDIAN_CAST leint64_t)BSWAP_64(native)) +#define CPU_TO_LE32(native) ((ENDIAN_CAST leint32_t)BSWAP_32(native)) +#define CPU_TO_LE16(native) ((ENDIAN_CAST leint16_t)BSWAP_16(native)) +#define LE64_TO_CPU(le_val) BSWAP_64((ENDIAN_CAST uint64_t)le_val) +#define LE32_TO_CPU(le_val) BSWAP_32((ENDIAN_CAST uint32_t)le_val) +#define LE16_TO_CPU(le_val) BSWAP_16((ENDIAN_CAST uint16_t)le_val) +#endif /* HAVE_BIG_ENDIAN */ + +#if HAVE_BIG_ENDIAN +/** + * CPU_TO_BE64 - convert a constant uint64_t value to big-endian + * @native: constant to convert + */ +#define CPU_TO_BE64(native) ((ENDIAN_CAST beint64_t)(native)) + +/** + * CPU_TO_BE32 - convert a constant uint32_t value to big-endian + * @native: constant to convert + */ +#define CPU_TO_BE32(native) ((ENDIAN_CAST beint32_t)(native)) + +/** + * CPU_TO_BE16 - convert a constant uint16_t value to big-endian + * @native: constant to convert + */ +#define CPU_TO_BE16(native) ((ENDIAN_CAST beint16_t)(native)) + +/** + * BE64_TO_CPU - convert a big-endian uint64_t constant + * @le_val: big-endian constant to convert + */ +#define BE64_TO_CPU(le_val) ((ENDIAN_CAST uint64_t)(le_val)) + +/** + * BE32_TO_CPU - convert a big-endian uint32_t constant + * @le_val: big-endian constant to convert + */ +#define BE32_TO_CPU(le_val) ((ENDIAN_CAST uint32_t)(le_val)) + +/** + * BE16_TO_CPU - convert a big-endian uint16_t constant + * @le_val: big-endian constant to convert + */ +#define BE16_TO_CPU(le_val) ((ENDIAN_CAST uint16_t)(le_val)) + +#else /* ... HAVE_LITTLE_ENDIAN */ +#define CPU_TO_BE64(native) ((ENDIAN_CAST beint64_t)BSWAP_64(native)) +#define CPU_TO_BE32(native) ((ENDIAN_CAST beint32_t)BSWAP_32(native)) +#define CPU_TO_BE16(native) ((ENDIAN_CAST beint16_t)BSWAP_16(native)) +#define BE64_TO_CPU(le_val) BSWAP_64((ENDIAN_CAST uint64_t)le_val) +#define BE32_TO_CPU(le_val) BSWAP_32((ENDIAN_CAST uint32_t)le_val) +#define BE16_TO_CPU(le_val) BSWAP_16((ENDIAN_CAST uint16_t)le_val) +#endif /* HAVE_LITTE_ENDIAN */ + + +/** + * cpu_to_le64 - convert a uint64_t value to little-endian + * @native: value to convert + */ +static inline leint64_t cpu_to_le64(uint64_t native) +{ + return CPU_TO_LE64(native); +} + +/** + * cpu_to_le32 - convert a uint32_t value to little-endian + * @native: value to convert + */ +static inline leint32_t cpu_to_le32(uint32_t native) +{ + return CPU_TO_LE32(native); +} + +/** + * cpu_to_le16 - convert a uint16_t value to little-endian + * @native: value to convert + */ +static inline leint16_t cpu_to_le16(uint16_t native) +{ + return CPU_TO_LE16(native); +} + +/** + * le64_to_cpu - convert a little-endian uint64_t value + * @le_val: little-endian value to convert + */ +static inline uint64_t le64_to_cpu(leint64_t le_val) +{ + return LE64_TO_CPU(le_val); +} + +/** + * le32_to_cpu - convert a little-endian uint32_t value + * @le_val: little-endian value to convert + */ +static inline uint32_t le32_to_cpu(leint32_t le_val) +{ + return LE32_TO_CPU(le_val); +} + +/** + * le16_to_cpu - convert a little-endian uint16_t value + * @le_val: little-endian value to convert + */ +static inline uint16_t le16_to_cpu(leint16_t le_val) +{ + return LE16_TO_CPU(le_val); +} + +/** + * cpu_to_be64 - convert a uint64_t value to big endian. + * @native: value to convert + */ +static inline beint64_t cpu_to_be64(uint64_t native) +{ + return CPU_TO_BE64(native); +} + +/** + * cpu_to_be32 - convert a uint32_t value to big endian. + * @native: value to convert + */ +static inline beint32_t cpu_to_be32(uint32_t native) +{ + return CPU_TO_BE32(native); +} + +/** + * cpu_to_be16 - convert a uint16_t value to big endian. + * @native: value to convert + */ +static inline beint16_t cpu_to_be16(uint16_t native) +{ + return CPU_TO_BE16(native); +} + +/** + * be64_to_cpu - convert a big-endian uint64_t value + * @be_val: big-endian value to convert + */ +static inline uint64_t be64_to_cpu(beint64_t be_val) +{ + return BE64_TO_CPU(be_val); +} + +/** + * be32_to_cpu - convert a big-endian uint32_t value + * @be_val: big-endian value to convert + */ +static inline uint32_t be32_to_cpu(beint32_t be_val) +{ + return BE32_TO_CPU(be_val); +} + +/** + * be16_to_cpu - convert a big-endian uint16_t value + * @be_val: big-endian value to convert + */ +static inline uint16_t be16_to_cpu(beint16_t be_val) +{ + return BE16_TO_CPU(be_val); +} + +/** + * be64/be32/be16 - 64/32/16 bit big-endian representation. + */ +typedef beint64_t be64; +typedef beint32_t be32; +typedef beint16_t be16; + +/** + * le64/le32/le16 - 64/32/16 bit little-endian representation. + */ +typedef leint64_t le64; +typedef leint32_t le32; +typedef leint16_t le16; + + +#endif /* CCAN_ENDIAN_H */ diff --git a/hex.h b/hex.h @@ -0,0 +1,69 @@ + +static inline int char_to_hex(unsigned char *val, char c) +{ + if (c >= '0' && c <= '9') { + *val = c - '0'; + return 1; + } + if (c >= 'a' && c <= 'f') { + *val = c - 'a' + 10; + return 1; + } + if (c >= 'A' && c <= 'F') { + *val = c - 'A' + 10; + return 1; + } + return 0; +} + +static inline int hex_decode(const char *str, size_t slen, void *buf, size_t bufsize) +{ + unsigned char v1, v2; + unsigned char *p = buf; + + while (slen > 1) { + if (!char_to_hex(&v1, str[0]) || !char_to_hex(&v2, str[1])) + return 0; + if (!bufsize) + return 0; + *(p++) = (v1 << 4) | v2; + str += 2; + slen -= 2; + bufsize--; + } + return slen == 0 && bufsize == 0; +} + +static inline size_t hex_str_size(size_t bytes) +{ + return 2 * bytes + 1; +} + +static inline char hexchar(unsigned int val) +{ + if (val < 10) + return '0' + val; + if (val < 16) + return 'a' + val - 10; + abort(); +} + +static inline int hex_encode(const void *buf, size_t bufsize, char *dest, size_t destsize) +{ + size_t i; + + if (destsize < hex_str_size(bufsize)) { + fprintf(stderr, "hexencode: destsize(%zu) < hex_str_size(%zu)\n", destsize, hex_str_size(bufsize)); + return 0; + } + + for (i = 0; i < bufsize; i++) { + unsigned int c = ((const unsigned char *)buf)[i]; + *(dest++) = hexchar(c >> 4); + *(dest++) = hexchar(c & 0xF); + } + *dest = '\0'; + + return 1; +} + diff --git a/nostril.c b/nostril.c @@ -0,0 +1,426 @@ + +#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <assert.h> +#include <errno.h> +#include <inttypes.h> + +#include <secp256k1.h> +#include <secp256k1_schnorrsig.h> + +#include "cursor.h" +#include "hex.h" +#include "sha256.h" +#include "random.h" + +#define MAX_TAGS 32 +#define MAX_TAG_ELEMS 16 + +#define HAS_CREATED_AT (1<<1) +#define HAS_KIND (1<<2) +#define HAS_ENVELOPE (1<<2) + +struct key { + secp256k1_keypair pair; + unsigned char pubkey[32]; +}; + +struct args { + unsigned int flags; + int kind; + + const char *sec; + const char *content; + + uint64_t created_at; +}; + +struct nostr_tag { + const char *strs[MAX_TAG_ELEMS]; + int num_elems; +}; + +struct nostr_event { + unsigned char id[32]; + unsigned char pubkey[32]; + unsigned char sig[64]; + + const char *content; + + uint64_t created_at; + int kind; + + struct nostr_tag tags[MAX_TAGS]; + int num_tags; +}; + +void usage() +{ + printf("usage: nostril <content>\n"); + exit(1); +} + + +inline static int cursor_push_escaped_char(struct cursor *cur, char c) +{ + switch (c) { + case '"': return cursor_push_str(cur, "\\\""); + case '\\': return cursor_push_str(cur, "\\\\"); + case '\b': return cursor_push_str(cur, "\\b"); + case '\f': return cursor_push_str(cur, "\\f"); + case '\n': return cursor_push_str(cur, "\\n"); + case '\r': return cursor_push_str(cur, "\\r"); + case '\t': return cursor_push_str(cur, "\\t"); + // TODO: \u hex hex hex hex + } + return cursor_push_byte(cur, c); +} + +static int cursor_push_jsonstr(struct cursor *cur, const char *str) +{ + int i; + int len; + + len = strlen(str); + + if (!cursor_push_byte(cur, '"')) + return 0; + + for (i = 0; i < len; i++) { + if (!cursor_push_escaped_char(cur, str[i])) + return 0; + } + + if (!cursor_push_byte(cur, '"')) + return 0; + + return 1; +} + +static int cursor_push_tag(struct cursor *cur, struct nostr_tag *tag) +{ + int i; + + if (!cursor_push_byte(cur, '[')) + return 0; + + for (i = 0; i < tag->num_elems; i++) { + if (!cursor_push_jsonstr(cur, tag->strs[i])) + return 0; + if (i != tag->num_elems-1) { + if (!cursor_push_byte(cur, ',')) + return 0; + } + } + + return cursor_push_byte(cur, ']'); +} + +static int cursor_push_tags(struct cursor *cur, struct nostr_event *ev) +{ + int i; + + if (!cursor_push_byte(cur, '[')) + return 0; + + for (i = 0; i < ev->num_tags; i++) { + if (!cursor_push_tag(cur, &ev->tags[i])) + return 0; + if (i != ev->num_tags-1) { + if (!cursor_push_str(cur, ",")) + return 0; + } + } + + return cursor_push_byte(cur, ']'); +} + + +int event_commitment(struct nostr_event *ev, unsigned char *buf, int buflen) +{ + char timebuf[16] = {0}; + char kindbuf[16] = {0}; + char pubkey[65]; + struct cursor cur; + int ok; + + ok = hex_encode(ev->pubkey, sizeof(ev->pubkey), pubkey, sizeof(pubkey)); + assert(ok); + + make_cursor(buf, buf + buflen, &cur); + + snprintf(timebuf, sizeof(timebuf), "%" PRIu64 "", ev->created_at); + snprintf(kindbuf, sizeof(kindbuf), "%d", ev->kind); + + ok = + cursor_push_str(&cur, "[0,\"") && + cursor_push_str(&cur, pubkey) && + cursor_push_str(&cur, "\",") && + cursor_push_str(&cur, timebuf) && + cursor_push_str(&cur, ",") && + cursor_push_str(&cur, kindbuf) && + cursor_push_str(&cur, ",") && + cursor_push_tags(&cur, ev) && + cursor_push_str(&cur, ",") && + cursor_push_jsonstr(&cur, ev->content) && + cursor_push_str(&cur, "]"); + + if (!ok) + return 0; + + return cur.p - cur.start; +} + +static int make_sig(secp256k1_context *ctx, struct key *key, + unsigned char *id, unsigned char sig[64]) +{ + unsigned char aux[32]; + + if (!fill_random(aux, sizeof(aux))) { + return 0; + } + + return secp256k1_schnorrsig_sign(ctx, sig, id, &key->pair, aux); +} + +static int create_key(secp256k1_context *ctx, struct key *key, unsigned char seckey[32]) +{ + secp256k1_xonly_pubkey pubkey; + + /* Try to create a keypair with a valid context, it should only + * fail if the secret key is zero or out of range. */ + if (!secp256k1_keypair_create(ctx, &key->pair, seckey)) + return 0; + + if (!secp256k1_keypair_xonly_pub(ctx, &pubkey, NULL, &key->pair)) + return 0; + + /* Serialize the public key. Should always return 1 for a valid public key. */ + return secp256k1_xonly_pubkey_serialize(ctx, key->pubkey, &pubkey); +} + +static int decode_key(secp256k1_context *ctx, const char *secstr, struct key *key) +{ + unsigned char seckey[32]; + int ok; + + if (!hex_decode(secstr, strlen(secstr), seckey, 32)) { + fprintf(stderr, "could not hex decode secret key\n"); + return 0; + } + + return create_key(ctx, key, seckey); +} + +static int generate_key(secp256k1_context *ctx, struct key *key) +{ + unsigned char seckey[32]; + + /* If the secret key is zero or out of range (bigger than secp256k1's + * order), we try to sample a new key. Note that the probability of this + * happening is negligible. */ + if (!fill_random(seckey, sizeof(seckey))) { + return 0; + } + + return create_key(ctx, key, seckey); +} + + +static int init_secp_context(secp256k1_context **ctx) +{ + unsigned char randomize[32]; + + *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if (!fill_random(randomize, sizeof(randomize))) { + return 0; + } + + /* Randomizing the context is recommended to protect against side-channel + * leakage See `secp256k1_context_randomize` in secp256k1.h for more + * information about it. This should never fail. */ + return secp256k1_context_randomize(*ctx, randomize); +} + +static int generate_event_id(struct nostr_event *ev) +{ + static unsigned char buf[32000]; + + int len; + + if (!(len = event_commitment(ev, buf, sizeof(buf)))) { + fprintf(stderr, "event_commitment: buffer out of space\n"); + return 0; + } + + fprintf(stderr, "commitment: '%.*s'\n", len, buf); + + sha256((struct sha256*)ev->id, buf, len); + + return 1; +} + +static int sign_event(secp256k1_context *ctx, struct key *key, struct nostr_event *ev) +{ + if (!make_sig(ctx, key, ev->id, ev->sig)) { + fprintf(stderr, "Signature generation failed\n"); + return 0; + } + + return 1; +} + +static int print_event(struct nostr_event *ev, int envelope) +{ + unsigned char buf[32000]; + char pubkey[65]; + char id[65]; + char sig[129]; + struct cursor cur; + int ok; + + ok = hex_encode(ev->id, sizeof(ev->id), id, sizeof(id)) && + hex_encode(ev->pubkey, sizeof(ev->pubkey), pubkey, sizeof(pubkey)) && + hex_encode(ev->sig, sizeof(ev->sig), sig, sizeof(sig)); + + assert(ok); + + make_cursor(buf, buf+sizeof(buf), &cur); + if (!cursor_push_tags(&cur, ev)) + return 0; + + if (envelope) + printf("[\"EVENT\","); + + printf("{\"id\": \"%s\",", id); + printf("\"pubkey\": \"%s\",", pubkey); + printf("\"created_at\": %" PRIu64 ",", ev->created_at); + printf("\"kind\": %d,", ev->kind); + printf("\"tags\": %.*s,", (int)cursor_len(&cur), cur.start); + + reset_cursor(&cur); + if (!cursor_push_jsonstr(&cur, ev->content)) + return 0; + + printf("\"content\": %.*s,", (int)cursor_len(&cur), cur.start); + printf("\"sig\": \"%s\"}", sig); + + if (envelope) + printf("]"); + + printf("\n"); + + return 1; +} + +static void make_event_from_args(struct nostr_event *ev, struct args *args) +{ + ev->tags[0].strs[0] = "tag"; + ev->tags[0].strs[1] = "a"; + ev->tags[0].num_elems = 2; + ev->num_tags = 0; + + ev->created_at = args->flags & HAS_CREATED_AT? args->created_at : time(NULL); + ev->content = args->content; + ev->kind = 1; +} + +static int parse_num(const char *arg, uint64_t *t) +{ + *t = strtol(arg, NULL, 10); + return errno != EINVAL; +} + +static int parse_args(int argc, const char *argv[], struct args *args) +{ + const char *arg; + uint64_t n; + + argv++; argc--; + for (; argc; ) { + arg = *argv++; argc--; + if (!argc) { + args->content = arg; + return 1; + } + + if (!strcmp(arg, "--sec")) { + args->sec = *argv++; argc--; + } else if (!strcmp(arg, "--created-at")) { + arg = *argv++; argc--; + if (!parse_num(arg, &args->created_at)) { + fprintf(stderr, "created-at must be a unix timestamp\n"); + return 0; + } else { + args->flags |= HAS_CREATED_AT; + } + } else if (!strcmp(arg, "--kind")) { + if (!parse_num(arg, &n)) { + fprintf(stderr, "kind should be a number, got '%s'\n", arg); + return 0; + } + args->kind = (int)n; + args->flags |= HAS_KIND; + } else if (!strcmp(arg, "--envelope")) { + args->flags |= HAS_ENVELOPE; + } else if (!strncmp(arg, "--", 2)) { + fprintf(stderr, "unknown argument: %s\n", arg); + return 0; + } + } + + return 1; +} + +int main(int argc, const char *argv[]) +{ + struct args args = {0}; + struct nostr_event ev = {0}; + struct key key; + secp256k1_context *ctx; + int ok; + + if (argc < 2) + usage(); + + if (!init_secp_context(&ctx)) + return 2; + + if (!parse_args(argc, argv, &args)) + return 10; + + make_event_from_args(&ev, &args); + + if (args.sec) { + if (!decode_key(ctx, args.sec, &key)) { + return 8; + } + } else { + if (!generate_key(ctx, &key)) { + fprintf(stderr, "could not generate key"); + return 4; + } + } + + // set the event's pubkey + memcpy(ev.pubkey, key.pubkey, 32); + + if (!generate_event_id(&ev)) { + fprintf(stderr, "could not generate event id\n"); + return 5; + } + + if (!sign_event(ctx, &key, &ev)) { + fprintf(stderr, "could not sign event\n"); + return 6; + } + + if (!print_event(&ev, args.flags & HAS_ENVELOPE)) { + fprintf(stderr, "buffer too small\n"); + return 88; + } + + return 0; +} + diff --git a/random.h b/random.h @@ -0,0 +1,73 @@ +/************************************************************************* + * Copyright (c) 2020-2021 Elichai Turkel * + * Distributed under the CC0 software license, see the accompanying file * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ + +/* + * This file is an attempt at collecting best practice methods for obtaining randomness with different operating systems. + * It may be out-of-date. Consult the documentation of the operating system before considering to use the methods below. + * + * Platform randomness sources: + * Linux -> `getrandom(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. http://man7.org/linux/man-pages/man2/getrandom.2.html, https://linux.die.net/man/4/urandom + * macOS -> `getentropy(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. https://www.unix.com/man-page/mojave/2/getentropy, https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man4/random.4.auto.html + * FreeBSD -> `getrandom(2)`(`sys/random.h`), if not available `kern.arandom` should be used. https://www.freebsd.org/cgi/man.cgi?query=getrandom, https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4 + * OpenBSD -> `getentropy(2)`(`unistd.h`), if not available `/dev/urandom` should be used. https://man.openbsd.org/getentropy, https://man.openbsd.org/urandom + * Windows -> `BCryptGenRandom`(`bcrypt.h`). https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom + */ + +#if defined(_WIN32) +#include <windows.h> +#include <ntstatus.h> +#include <bcrypt.h> +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) +#include <sys/random.h> +#elif defined(__OpenBSD__) +#include <unistd.h> +#else +#error "Couldn't identify the OS" +#endif + +#include <stddef.h> +#include <limits.h> +#include <stdio.h> + + +/* Returns 1 on success, and 0 on failure. */ +static int fill_random(unsigned char* data, size_t size) { +#if defined(_WIN32) + NTSTATUS res = BCryptGenRandom(NULL, data, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG); + if (res != STATUS_SUCCESS || size > ULONG_MAX) { + return 0; + } else { + return 1; + } +#elif defined(__linux__) || defined(__FreeBSD__) + /* If `getrandom(2)` is not available you should fallback to /dev/urandom */ + ssize_t res = getrandom(data, size, 0); + if (res < 0 || (size_t)res != size ) { + return 0; + } else { + return 1; + } +#elif defined(__APPLE__) || defined(__OpenBSD__) + /* If `getentropy(2)` is not available you should fallback to either + * `SecRandomCopyBytes` or /dev/urandom */ + int res = getentropy(data, size); + if (res == 0) { + return 1; + } else { + return 0; + } +#endif + return 0; +} + +static void print_hex(unsigned char* data, size_t size) { + size_t i; + printf("0x"); + for (i = 0; i < size; i++) { + printf("%02x", data[i]); + } + printf("\n"); +} diff --git a/sha256.c b/sha256.c @@ -0,0 +1,302 @@ +/* MIT (BSD) license - see LICENSE file for details */ +/* SHA256 core code translated from the Bitcoin project's C++: + * + * src/crypto/sha256.cpp commit 417532c8acb93c36c2b6fd052b7c11b6a2906aa2 + * Copyright (c) 2014 The Bitcoin Core developers + * Distributed under the MIT software license, see the accompanying + * file COPYING or http://www.opensource.org/licenses/mit-license.php. + */ +#include "sha256.h" +#include "endian.h" +#include "compiler.h" +#include <stdbool.h> +#include <assert.h> +#include <string.h> + +static void invalidate_sha256(struct sha256_ctx *ctx) +{ +#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL + ctx->c.md_len = 0; +#else + ctx->bytes = (size_t)-1; +#endif +} + +static void check_sha256(struct sha256_ctx *ctx) +{ +#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL + assert(ctx->c.md_len != 0); +#else + assert(ctx->bytes != (size_t)-1); +#endif +} + +#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL +void sha256_init(struct sha256_ctx *ctx) +{ + SHA256_Init(&ctx->c); +} + +void sha256_update(struct sha256_ctx *ctx, const void *p, size_t size) +{ + check_sha256(ctx); + SHA256_Update(&ctx->c, p, size); +} + +void sha256_done(struct sha256_ctx *ctx, struct sha256 *res) +{ + SHA256_Final(res->u.u8, &ctx->c); + invalidate_sha256(ctx); +} +#else +static uint32_t Ch(uint32_t x, uint32_t y, uint32_t z) +{ + return z ^ (x & (y ^ z)); +} +static uint32_t Maj(uint32_t x, uint32_t y, uint32_t z) +{ + return (x & y) | (z & (x | y)); +} +static uint32_t Sigma0(uint32_t x) +{ + return (x >> 2 | x << 30) ^ (x >> 13 | x << 19) ^ (x >> 22 | x << 10); +} +static uint32_t Sigma1(uint32_t x) +{ + return (x >> 6 | x << 26) ^ (x >> 11 | x << 21) ^ (x >> 25 | x << 7); +} +static uint32_t sigma0(uint32_t x) +{ + return (x >> 7 | x << 25) ^ (x >> 18 | x << 14) ^ (x >> 3); +} +static uint32_t sigma1(uint32_t x) +{ + return (x >> 17 | x << 15) ^ (x >> 19 | x << 13) ^ (x >> 10); +} + +/** One round of SHA-256. */ +static void Round(uint32_t a, uint32_t b, uint32_t c, uint32_t *d, uint32_t e, uint32_t f, uint32_t g, uint32_t *h, uint32_t k, uint32_t w) +{ + uint32_t t1 = *h + Sigma1(e) + Ch(e, f, g) + k + w; + uint32_t t2 = Sigma0(a) + Maj(a, b, c); + *d += t1; + *h = t1 + t2; +} + +/** Perform one SHA-256 transformation, processing a 64-byte chunk. */ +static void Transform(uint32_t *s, const uint32_t *chunk) +{ + uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7]; + uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; + + Round(a, b, c, &d, e, f, g, &h, 0x428a2f98, w0 = be32_to_cpu(chunk[0])); + Round(h, a, b, &c, d, e, f, &g, 0x71374491, w1 = be32_to_cpu(chunk[1])); + Round(g, h, a, &b, c, d, e, &f, 0xb5c0fbcf, w2 = be32_to_cpu(chunk[2])); + Round(f, g, h, &a, b, c, d, &e, 0xe9b5dba5, w3 = be32_to_cpu(chunk[3])); + Round(e, f, g, &h, a, b, c, &d, 0x3956c25b, w4 = be32_to_cpu(chunk[4])); + Round(d, e, f, &g, h, a, b, &c, 0x59f111f1, w5 = be32_to_cpu(chunk[5])); + Round(c, d, e, &f, g, h, a, &b, 0x923f82a4, w6 = be32_to_cpu(chunk[6])); + Round(b, c, d, &e, f, g, h, &a, 0xab1c5ed5, w7 = be32_to_cpu(chunk[7])); + Round(a, b, c, &d, e, f, g, &h, 0xd807aa98, w8 = be32_to_cpu(chunk[8])); + Round(h, a, b, &c, d, e, f, &g, 0x12835b01, w9 = be32_to_cpu(chunk[9])); + Round(g, h, a, &b, c, d, e, &f, 0x243185be, w10 = be32_to_cpu(chunk[10])); + Round(f, g, h, &a, b, c, d, &e, 0x550c7dc3, w11 = be32_to_cpu(chunk[11])); + Round(e, f, g, &h, a, b, c, &d, 0x72be5d74, w12 = be32_to_cpu(chunk[12])); + Round(d, e, f, &g, h, a, b, &c, 0x80deb1fe, w13 = be32_to_cpu(chunk[13])); + Round(c, d, e, &f, g, h, a, &b, 0x9bdc06a7, w14 = be32_to_cpu(chunk[14])); + Round(b, c, d, &e, f, g, h, &a, 0xc19bf174, w15 = be32_to_cpu(chunk[15])); + + Round(a, b, c, &d, e, f, g, &h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, &c, d, e, f, &g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, &b, c, d, e, &f, 0x0fc19dc6, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, &a, b, c, d, &e, 0x240ca1cc, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, &h, a, b, c, &d, 0x2de92c6f, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, &g, h, a, b, &c, 0x4a7484aa, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, &f, g, h, a, &b, 0x5cb0a9dc, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, &e, f, g, h, &a, 0x76f988da, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, &d, e, f, g, &h, 0x983e5152, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, &c, d, e, f, &g, 0xa831c66d, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, &b, c, d, e, &f, 0xb00327c8, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, &a, b, c, d, &e, 0xbf597fc7, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, &h, a, b, c, &d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, &g, h, a, b, &c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, &f, g, h, a, &b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, &e, f, g, h, &a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, &d, e, f, g, &h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, &c, d, e, f, &g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, &b, c, d, e, &f, 0x4d2c6dfc, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, &a, b, c, d, &e, 0x53380d13, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, &h, a, b, c, &d, 0x650a7354, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, &g, h, a, b, &c, 0x766a0abb, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, &f, g, h, a, &b, 0x81c2c92e, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, &e, f, g, h, &a, 0x92722c85, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, &d, e, f, g, &h, 0xa2bfe8a1, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, &c, d, e, f, &g, 0xa81a664b, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, &b, c, d, e, &f, 0xc24b8b70, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, &a, b, c, d, &e, 0xc76c51a3, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, &h, a, b, c, &d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, &g, h, a, b, &c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, &f, g, h, a, &b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, &e, f, g, h, &a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, &d, e, f, g, &h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, &c, d, e, f, &g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, &b, c, d, e, &f, 0x2748774c, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, &a, b, c, d, &e, 0x34b0bcb5, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, &h, a, b, c, &d, 0x391c0cb3, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, &g, h, a, b, &c, 0x4ed8aa4a, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, &f, g, h, a, &b, 0x5b9cca4f, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, &e, f, g, h, &a, 0x682e6ff3, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, &d, e, f, g, &h, 0x748f82ee, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, &c, d, e, f, &g, 0x78a5636f, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, &b, c, d, e, &f, 0x84c87814, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, &a, b, c, d, &e, 0x8cc70208, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, &h, a, b, c, &d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, &g, h, a, b, &c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, &f, g, h, a, &b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, &e, f, g, h, &a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0)); + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; + s[5] += f; + s[6] += g; + s[7] += h; +} + + +static void add(struct sha256_ctx *ctx, const void *p, size_t len) +{ + const unsigned char *data = p; + size_t bufsize = ctx->bytes % 64; + + if (bufsize + len >= 64) { + /* Fill the buffer, and process it. */ + memcpy(ctx->buf.u8 + bufsize, data, 64 - bufsize); + ctx->bytes += 64 - bufsize; + data += 64 - bufsize; + len -= 64 - bufsize; + Transform(ctx->s, ctx->buf.u32); + bufsize = 0; + } + + while (len >= 64) { + /* Process full chunks directly from the source. */ + if (alignment_ok(data, sizeof(uint32_t))) + Transform(ctx->s, (const uint32_t *)data); + else { + memcpy(ctx->buf.u8, data, sizeof(ctx->buf)); + Transform(ctx->s, ctx->buf.u32); + } + ctx->bytes += 64; + data += 64; + len -= 64; + } + + if (len) { + /* Fill the buffer with what remains. */ + memcpy(ctx->buf.u8 + bufsize, data, len); + ctx->bytes += len; + } +} + +void sha256_init(struct sha256_ctx *ctx) +{ + struct sha256_ctx init = SHA256_INIT; + *ctx = init; +} + +void sha256_update(struct sha256_ctx *ctx, const void *p, size_t size) +{ + check_sha256(ctx); + add(ctx, p, size); +} + +void sha256_done(struct sha256_ctx *ctx, struct sha256 *res) +{ + static const unsigned char pad[64] = {0x80}; + uint64_t sizedesc; + size_t i; + + sizedesc = cpu_to_be64((uint64_t)ctx->bytes << 3); + /* Add '1' bit to terminate, then all 0 bits, up to next block - 8. */ + add(ctx, pad, 1 + ((128 - 8 - (ctx->bytes % 64) - 1) % 64)); + /* Add number of bits of data (big endian) */ + add(ctx, &sizedesc, 8); + for (i = 0; i < sizeof(ctx->s) / sizeof(ctx->s[0]); i++) + res->u.u32[i] = cpu_to_be32(ctx->s[i]); + invalidate_sha256(ctx); +} +#endif + +void sha256(struct sha256 *sha, const void *p, size_t size) +{ + struct sha256_ctx ctx; + + sha256_init(&ctx); + sha256_update(&ctx, p, size); + sha256_done(&ctx, sha); +} + +void sha256_u8(struct sha256_ctx *ctx, uint8_t v) +{ + sha256_update(ctx, &v, sizeof(v)); +} + +void sha256_u16(struct sha256_ctx *ctx, uint16_t v) +{ + sha256_update(ctx, &v, sizeof(v)); +} + +void sha256_u32(struct sha256_ctx *ctx, uint32_t v) +{ + sha256_update(ctx, &v, sizeof(v)); +} + +void sha256_u64(struct sha256_ctx *ctx, uint64_t v) +{ + sha256_update(ctx, &v, sizeof(v)); +} + +/* Add as little-endian */ +void sha256_le16(struct sha256_ctx *ctx, uint16_t v) +{ + leint16_t lev = cpu_to_le16(v); + sha256_update(ctx, &lev, sizeof(lev)); +} + +void sha256_le32(struct sha256_ctx *ctx, uint32_t v) +{ + leint32_t lev = cpu_to_le32(v); + sha256_update(ctx, &lev, sizeof(lev)); +} + +void sha256_le64(struct sha256_ctx *ctx, uint64_t v) +{ + leint64_t lev = cpu_to_le64(v); + sha256_update(ctx, &lev, sizeof(lev)); +} + +/* Add as big-endian */ +void sha256_be16(struct sha256_ctx *ctx, uint16_t v) +{ + beint16_t bev = cpu_to_be16(v); + sha256_update(ctx, &bev, sizeof(bev)); +} + +void sha256_be32(struct sha256_ctx *ctx, uint32_t v) +{ + beint32_t bev = cpu_to_be32(v); + sha256_update(ctx, &bev, sizeof(bev)); +} + +void sha256_be64(struct sha256_ctx *ctx, uint64_t v) +{ + beint64_t bev = cpu_to_be64(v); + sha256_update(ctx, &bev, sizeof(bev)); +} + + diff --git a/sha256.h b/sha256.h @@ -0,0 +1,155 @@ + +#ifndef CCAN_CRYPTO_SHA256_H +#define CCAN_CRYPTO_SHA256_H + + +/** Output length for `wally_sha256` */ +#define SHA256_LEN 32 + + +/* BSD-MIT - see LICENSE file for details */ +/* #include "config.h" */ +#include <stdint.h> +#include <stdlib.h> + +/* Uncomment this to use openssl's SHA256 routines (and link with -lcrypto) */ +/*#define CCAN_CRYPTO_SHA256_USE_OPENSSL 1*/ + +#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL +#include <openssl/sha.h> +#endif + +/** + * struct sha256 - structure representing a completed SHA256. + * @u.u8: an unsigned char array. + * @u.u32: a 32-bit integer array. + * + * Other fields may be added to the union in future. + */ +struct sha256 { + union { + uint32_t u32[8]; + unsigned char u8[32]; + } u; +}; + +/** + * sha256 - return sha256 of an object. + * @sha256: the sha256 to fill in + * @p: pointer to memory, + * @size: the number of bytes pointed to by @p + * + * The bytes pointed to by @p is SHA256 hashed into @sha256. This is + * equivalent to sha256_init(), sha256_update() then sha256_done(). + */ +void sha256(struct sha256 *sha, const void *p, size_t size); + +/** + * struct sha256_ctx - structure to store running context for sha256 + */ +struct sha256_ctx { +#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL + SHA256_CTX c; +#else + uint32_t s[8]; + union { + uint32_t u32[16]; + unsigned char u8[64]; + } buf; + size_t bytes; +#endif +}; + +/** + * sha256_init - initialize an SHA256 context. + * @ctx: the sha256_ctx to initialize + * + * This must be called before sha256_update or sha256_done, or + * alternately you can assign SHA256_INIT. + * + * If it was already initialized, this forgets anything which was + * hashed before. + * + * Example: + * static void hash_all(const char **arr, struct sha256 *hash) + * { + * size_t i; + * struct sha256_ctx ctx; + * + * sha256_init(&ctx); + * for (i = 0; arr[i]; i++) + * sha256_update(&ctx, arr[i], strlen(arr[i])); + * sha256_done(&ctx, hash); + * } + */ +void sha256_init(struct sha256_ctx *ctx); + +/** + * SHA256_INIT - initializer for an SHA256 context. + * + * This can be used to statically initialize an SHA256 context (instead + * of sha256_init()). + * + * Example: + * static void hash_all(const char **arr, struct sha256 *hash) + * { + * size_t i; + * struct sha256_ctx ctx = SHA256_INIT; + * + * for (i = 0; arr[i]; i++) + * sha256_update(&ctx, arr[i], strlen(arr[i])); + * sha256_done(&ctx, hash); + * } + */ +#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL +#define SHA256_INIT \ + { { { 0x6a09e667ul, 0xbb67ae85ul, 0x3c6ef372ul, 0xa54ff53aul, \ + 0x510e527ful, 0x9b05688cul, 0x1f83d9abul, 0x5be0cd19ul }, \ + 0x0, 0x0, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + 0x0, 0x20 } } +#else +#define SHA256_INIT \ + { { 0x6a09e667ul, 0xbb67ae85ul, 0x3c6ef372ul, 0xa54ff53aul, \ + 0x510e527ful, 0x9b05688cul, 0x1f83d9abul, 0x5be0cd19ul }, \ + { { 0 } }, 0 } +#endif + +/** + * sha256_update - include some memory in the hash. + * @ctx: the sha256_ctx to use + * @p: pointer to memory, + * @size: the number of bytes pointed to by @p + * + * You can call this multiple times to hash more data, before calling + * sha256_done(). + */ +void sha256_update(struct sha256_ctx *ctx, const void *p, size_t size); + +/** + * sha256_done - finish SHA256 and return the hash + * @ctx: the sha256_ctx to complete + * @res: the hash to return. + * + * Note that @ctx is *destroyed* by this, and must be reinitialized. + * To avoid that, pass a copy instead. + */ +void sha256_done(struct sha256_ctx *sha256, struct sha256 *res); + +/* Add various types to an SHA256 hash */ +void sha256_u8(struct sha256_ctx *ctx, uint8_t v); +void sha256_u16(struct sha256_ctx *ctx, uint16_t v); +void sha256_u32(struct sha256_ctx *ctx, uint32_t v); +void sha256_u64(struct sha256_ctx *ctx, uint64_t v); + +/* Add as little-endian */ +void sha256_le16(struct sha256_ctx *ctx, uint16_t v); +void sha256_le32(struct sha256_ctx *ctx, uint32_t v); +void sha256_le64(struct sha256_ctx *ctx, uint64_t v); + +/* Add as big-endian */ +void sha256_be16(struct sha256_ctx *ctx, uint16_t v); +void sha256_be32(struct sha256_ctx *ctx, uint32_t v); +void sha256_be64(struct sha256_ctx *ctx, uint64_t v); + +#endif /* CCAN_CRYPTO_SHA256_H */ diff --git a/shell.nix b/shell.nix @@ -0,0 +1,5 @@ +{ pkgs ? import <nixpkgs> {} }: +with pkgs; +mkShell { + buildInputs = [ secp256k1 ]; +}