import/impl static ffi infrastructure for crypto

This commit is contained in:
xenia 2020-11-08 02:33:02 -05:00
parent 5a94dc3751
commit 05736756ec
9 changed files with 326 additions and 2 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
export LD_LIBRARY_PATH="$PWD/lib:$LD_LIBRARY_PATH"

1
.gitignore vendored
View File

@ -3,5 +3,6 @@
.\#*
*.zo
*.dep
*.so
/crossfire/compiled/
/crossfire/doc/

View File

@ -15,7 +15,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
.PHONY: all clean
.PHONY: all check clean monocypher
all:
raco setup ./crossfire/
@ -24,4 +24,10 @@ check:
raco check-requires -- crossfire/*.rkt
clean:
$(RM) -r crossfire/doc crossfire/compiled crossfire/scribblings/compiled
$(RM) -r crossfire/doc crossfire/compiled crossfire/scribblings/compiled lib/
monocypher: lib/monocypher.so
lib/monocypher.so: /usr/include/monocypher/monocypher.c /usr/include/monocypher/monocypher-ed25519.c
[ -d lib ] || mkdir lib
$(CC) -o $@ -O3 -pipe -shared $^

48
crossfire/not-crypto.rkt Normal file
View File

@ -0,0 +1,48 @@
#lang racket/base
;; crossfire: distributed brute force infrastructure
;;
;; Copyright (C) 2020 haskal
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU Affero General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
(require ffi/vector ffi/unsafe racket/random
"static-support.rkt"
(for-syntax racket/base racket/string racket/syntax)
(only-in racket/contract provide/contract)
;; MATTHIAS
;; WHY
(rename-in racket/contract [-> contract:->]))
;; why not use the crypto package?
;; this needs to work in a static binary, and potentially without any crypto libraries available to
;; link for the agent
;; therefore we support monocypher via statically exported functions or by dynamic linking
;; thanks iitalics uwu
(define ((bytes-len/c len) bs)
(and bytes? bs) (= len (bytes-length bs)))
(define monocypher (ffi-lib/runtime "monocypher"))
(define-syntax (define/ffi stx)
(syntax-case stx ()
[(_ name type)
(with-syntax ([c-name
(datum->syntax
stx (string-replace (symbol->string (syntax->datum #'name)) "-" "_"))])
#'(define name (get-ffi-obj/runtime c-name monocypher type)))]))
(define/ffi crypto-sign-public-key
(_fun (pubkey : (_u8vector o 32)) (seckey : _u8vector) -> _void -> pubkey))
(provide/contract [crypto-sign-public-key (contract:-> (bytes-len/c 32) (bytes-len/c 32))])

View File

@ -0,0 +1,80 @@
#lang racket/base
;; crossfire: distributed brute force infrastructure
;;
;; Copyright (C) 2020 haskal
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU Affero General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
(require ffi/unsafe racket/bool racket/string)
(provide get-ffi-obj/static static-ffi-available?
ffi-lib/runtime get-ffi-obj/runtime)
;; this module provides utility functions for interacting with static ffi tables provided by an
;; embedding runtime, and for deferring either to the regular dynamic linking ffi or the static ffi
;; as the current runtime allows
;;
;; a static ffi table is a C array of structs of string and uintptr_t that define symbol exports
;; from an embedding runtime that is statically compiled (and therefore has no other usual dynamic
;; symbol table)
;;
;; applications compiled with static glibc libdl can actually still link in dynamic libraries, but
;; only if the same glibc version is available as a dynamic library during runtime. to avoid making
;; any runtime assumptions, i have created static ffi infrastructure that allows ffi calls into
;; statically compiled code exported by the embedding runtime
;; the embedding runtime provides a table in this format
(define _fun-opaque (_cpointer 'static-ffi:fun-opaque))
(define-cstruct _static-table ([name _string] [fun _fun-opaque]))
;; checks if the current runtime provides a static ffi table
(define (static-ffi-available?)
(with-handlers ([exn? (lambda (ex) #f)])
(dynamic-require ''#%static-ffi 'table)
#t))
;; try to load a library dynamically, but if dynamic linking is not available return #f
;; the result can be used in get-ffi-obj/runtime
(define (ffi-lib/runtime path [version #f])
(define fail (if (static-ffi-available?) (lambda () #f) #f))
(ffi-lib path version #:fail fail))
;; try to load an ffi object dynamically, but if lib is #f and static ffi is available, retrieve
;; the object from the static ffi table
(define (get-ffi-obj/runtime name lib type)
(when (and (false? lib) (not (static-ffi-available?)))
(error "no lib provided and static ffi not available"))
(if (false? lib)
(get-ffi-obj/static name type)
(get-ffi-obj name lib type)))
;; retrieve an object of a given name and type from the static ffi table, if it exists
;; otherwise raise error
(define (get-ffi-obj/static name type)
;; magic primitives
(define table-addr (dynamic-require ''#%static-ffi 'table))
(define table-size (dynamic-require ''#%static-ffi 'table-size))
(define table (cast table-addr _uint64 _static-table-pointer))
(define ent
(for/fold ([res #f]) ([i (in-range table-size)])
(define ent (ptr-ref table _static-table i))
(if (string=? (static-table-name ent) name)
(static-table-fun ent)
res)))
(when (false? ent)
(error "static ffi entry not found" name))
(cast ent _fun-opaque type))

3
static/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.o
*.zo
/run

52
static/Makefile Normal file
View File

@ -0,0 +1,52 @@
# crossfire: distributed brute force infrastructure
#
# Copyright (C) 2020 haskal
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
.PHONY: all clean
APP_NAME=run
RKT_NAME=$(APP_NAME).rkt
MONOCYPHER_INC=/usr/include/monocypher
####################################################################################################
LIBS=-ldl -lm
# for musl
#LIBS=-lffi -lucontext
all: $(APP_NAME)
# main build
$(APP_NAME): main_bc.c app.o monocypher.o monocypher-ed25519.o
$(CC) -o $@ -pipe -fPIC -O3 -DAPP_NAME='"$(APP_NAME)"' -I$(MONOCYPHER_INC) -static $^ \
-lracket3m -lrktio $(LIBS)
monocypher.o: $(MONOCYPHER_INC)/monocypher.c
$(CC) -o $@ -pipe -fPIC -O3 -I$(MONOCYPHER_INC) -c $^
monocypher-ed25519.o: $(MONOCYPHER_INC)/monocypher-ed25519.c
$(CC) -o $@ -pipe -fPIC -O3 -I$(MONOCYPHER_INC) -c $^
# this is faster than --c-mods by a lot
# it's less portable but like, we're containerized already so it'll work
app.o: app.zo
$(LD) -r -b binary $< -o $@
app.zo: $(RKT_NAME)
raco ctool --mods $@ $<
clean:
$(RM) $(APP_NAME) *.zo *.o

112
static/main_bc.c Normal file
View File

@ -0,0 +1,112 @@
// crossfire: distributed brute force infrastructure
//
// Copyright (C) 2020 haskal
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#define MZ_PRECISE_GC
#include <racket/scheme.h>
#include <stdint.h>
#include <stdio.h>
#include <monocypher.h>
extern const char _binary_app_zo_start;
extern const char _binary_app_zo_end;
// ffi defs
typedef struct {
const char* name;
uintptr_t ptr;
} ffi_ent;
#define STR(x) #x
#define FFI_ENT(name) {STR(name), (uintptr_t) name}
static const ffi_ent ffi_table[] = {
FFI_ENT(crypto_sign_public_key)
};
static const size_t ffi_table_size = sizeof(ffi_table)/sizeof(ffi_ent);
// setup ffi table to be passed to racket
static void bc_setup_ffi_table(Scheme_Env* parent) {
Scheme_Env* mod = NULL;
Scheme_Object* table = NULL;
Scheme_Object* table_size = NULL;
MZ_GC_DECL_REG(4);
MZ_GC_VAR_IN_REG(0, parent);
MZ_GC_VAR_IN_REG(1, mod);
MZ_GC_VAR_IN_REG(2, table);
MZ_GC_VAR_IN_REG(3, table_size);
MZ_GC_REG();
mod = scheme_primitive_module(scheme_intern_symbol("#%static-ffi"), parent);
table = scheme_make_integer(&ffi_table[0]);
scheme_add_global("table", table, mod);
table_size = scheme_make_integer(ffi_table_size);
scheme_add_global("table-size", table_size, mod);
scheme_finish_primitive_module(mod);
MZ_GC_UNREG();
}
static int run_bc(Scheme_Env* e, int argc, char* argv[]) {
(void)argc;
(void)argv;
volatile mz_jmp_buf* save;
mz_jmp_buf fresh;
int rv = 0;
size_t load_size = ((uintptr_t) &_binary_app_zo_end) - ((uintptr_t) &_binary_app_zo_start);
Scheme_Object* l = NULL;
Scheme_Object* a[2] = { NULL, NULL };
// gc setup
MZ_GC_DECL_REG(5);
MZ_GC_VAR_IN_REG(0, e);
MZ_GC_VAR_IN_REG(1, l);
MZ_GC_ARRAY_VAR_IN_REG(2, a, 2);
MZ_GC_REG();
bc_setup_ffi_table(e);
scheme_register_embedded_load(load_size, &_binary_app_zo_start);
scheme_embedded_load(load_size, &_binary_app_zo_start, 1);
l = scheme_make_null();
l = scheme_make_pair(scheme_intern_symbol(APP_NAME), l);
l = scheme_make_pair(scheme_intern_symbol("quote"), l);
a[0] = l;
a[1] = scheme_false;
save = scheme_current_thread->error_buf;
scheme_current_thread->error_buf = &fresh;
if (scheme_setjmp(scheme_error_buf)) {
fprintf(stderr, "[WRAPPER] encountered top-level racket error. aborting\n");
rv = -1;
} else {
scheme_dynamic_require(2, a);
}
scheme_current_thread->error_buf = (mz_jmp_buf*) save;
MZ_GC_UNREG();
return rv;
}
int main(int argc, char *argv[]) {
return scheme_main_setup(1, run_bc, argc, argv);
}

21
static/run.rkt Normal file
View File

@ -0,0 +1,21 @@
#lang racket/base
;; crossfire: distributed brute force infrastructure
;;
;; Copyright (C) 2020 haskal
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU Affero General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
(require "../crossfire/not-crypto.rkt")
(crypto-sign-public-key #"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")