From b8db4141bc66d354899e775bbd3bbdc338b9a6c4 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Mon, 27 Jul 2020 19:19:17 -0700 Subject: [PATCH 01/75] urcrypt pkg with one function, building --- nix/deps-env.nix | 2 +- nix/pkgs/default.nix | 9 +++++++-- nix/pkgs/urbit/default.nix | 4 ++-- nix/pkgs/urbit/release.nix | 2 +- nix/pkgs/urbit/shell.nix | 2 +- nix/pkgs/urcrypt/builder.sh | 7 +++++++ nix/pkgs/urcrypt/cross.nix | 12 ++++++++++++ nix/pkgs/urcrypt/default.nix | 9 +++++++++ nix/pkgs/urcrypt/release.sh | 13 +++++++++++++ nix/release.nix | 3 +++ pkg/urbit/configure | 2 +- pkg/urbit/jets/e/ed_scalarmult.c | 28 ++++++++++++++++++++++++++++ pkg/urcrypt/Makefile | 20 ++++++++++++++++++++ pkg/urcrypt/urcrypt.c | 19 +++++++++++++++++++ pkg/urcrypt/urcrypt.h | 10 ++++++++++ 15 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 nix/pkgs/urcrypt/builder.sh create mode 100644 nix/pkgs/urcrypt/cross.nix create mode 100644 nix/pkgs/urcrypt/default.nix create mode 100644 nix/pkgs/urcrypt/release.sh create mode 100644 pkg/urcrypt/Makefile create mode 100644 pkg/urcrypt/urcrypt.c create mode 100644 pkg/urcrypt/urcrypt.h diff --git a/nix/deps-env.nix b/nix/deps-env.nix index 6f7357b4f..7935da6ba 100644 --- a/nix/deps-env.nix +++ b/nix/deps-env.nix @@ -20,7 +20,7 @@ let vendor = with deps; - [ argon2 ed25519 h2o murmur3 scrypt secp256k1 softfloat3 uv ent ge-additions ivory-header ca-header ]; + [ argon2 ed25519 h2o murmur3 scrypt secp256k1 softfloat3 uv ent ge-additions urcrypt ivory-header ca-header ]; in diff --git a/nix/pkgs/default.nix b/nix/pkgs/default.nix index ba1129dd3..9404cbd9e 100644 --- a/nix/pkgs/default.nix +++ b/nix/pkgs/default.nix @@ -14,13 +14,18 @@ let inherit (deps) ed25519; }; + urcrypt = import ./urcrypt { + inherit pkgs ge-additions; + inherit (deps) ed25519; + }; + libaes_siv = import ./libaes_siv { inherit pkgs; }; mkUrbit = { debug }: import ./urbit { - inherit pkgs ent debug ge-additions libaes_siv; + inherit pkgs ent debug ge-additions urcrypt libaes_siv; inherit (deps) argon2 murmur3 uv ed25519 scrypt softfloat3; inherit (deps) secp256k1 h2o ivory-header ca-header; }; @@ -30,4 +35,4 @@ let in -{ inherit ent ge-additions libaes_siv arvo arvo-ropsten herb urbit urbit-debug; } +{ inherit ent ge-additions urcrypt libaes_siv arvo arvo-ropsten herb urbit urbit-debug; } diff --git a/nix/pkgs/urbit/default.nix b/nix/pkgs/urbit/default.nix index 19befa1ef..b808d0afb 100644 --- a/nix/pkgs/urbit/default.nix +++ b/nix/pkgs/urbit/default.nix @@ -1,7 +1,7 @@ { pkgs, debug, - argon2, ed25519, ent, ge-additions, libaes_siv, h2o, murmur3, scrypt, secp256k1, softfloat3, uv, ivory-header, ca-header + argon2, ed25519, ent, ge-additions, urcrypt, libaes_siv, h2o, murmur3, scrypt, secp256k1, softfloat3, uv, ivory-header, ca-header }: let @@ -26,7 +26,7 @@ let [ curl gmp sigseg openssl zlib lmdb ]; vendor = - [ argon2 softfloat3 ed25519 ent ge-additions libaes_siv h2o scrypt uv murmur3 secp256k1 ivory-header ca-header ]; + [ argon2 softfloat3 ed25519 ent ge-additions urcrypt libaes_siv h2o scrypt uv murmur3 secp256k1 ivory-header ca-header ]; urbit = pkgs.stdenv.mkDerivation { inherit name meta; diff --git a/nix/pkgs/urbit/release.nix b/nix/pkgs/urbit/release.nix index 9d486f3df..411be8868 100644 --- a/nix/pkgs/urbit/release.nix +++ b/nix/pkgs/urbit/release.nix @@ -16,7 +16,7 @@ let vendor = with deps; - [ argon2 softfloat3 ed25519 ge-additions libaes_siv h2o scrypt uv murmur3 secp256k1 ivory-header ca-header ]; + [ argon2 softfloat3 ed25519 ge-additions urcrypt libaes_siv h2o scrypt uv murmur3 secp256k1 ivory-header ca-header ]; in diff --git a/nix/pkgs/urbit/shell.nix b/nix/pkgs/urbit/shell.nix index dee99d962..9e5d781af 100644 --- a/nix/pkgs/urbit/shell.nix +++ b/nix/pkgs/urbit/shell.nix @@ -10,7 +10,7 @@ import ./default.nix { inherit pkgs; debug = false; inherit (tlon) - ent ge-additions libaes_siv; + ent ge-additions urcrypt libaes_siv; inherit (deps) argon2 ed25519 h2o murmur3 scrypt secp256k1 softfloat3 uv ivory-header ca-header; } diff --git a/nix/pkgs/urcrypt/builder.sh b/nix/pkgs/urcrypt/builder.sh new file mode 100644 index 000000000..5a0404377 --- /dev/null +++ b/nix/pkgs/urcrypt/builder.sh @@ -0,0 +1,7 @@ +source $stdenv/setup + +cp -r $src ./src +chmod -R u+w ./src +cd ./src + +PREFIX=$out make install diff --git a/nix/pkgs/urcrypt/cross.nix b/nix/pkgs/urcrypt/cross.nix new file mode 100644 index 000000000..48ef204bf --- /dev/null +++ b/nix/pkgs/urcrypt/cross.nix @@ -0,0 +1,12 @@ +{ env_name, env, deps }: + +env.make_derivation rec { + name = "urcrypt"; + builder = ./release.sh; + src = ../../../pkg/urcrypt; + + cross_inputs = [ deps.ed25519 deps.ge-additions ]; + + CC = "${env.host}-gcc"; + AR = "${env.host}-ar"; +} diff --git a/nix/pkgs/urcrypt/default.nix b/nix/pkgs/urcrypt/default.nix new file mode 100644 index 000000000..bb99897c4 --- /dev/null +++ b/nix/pkgs/urcrypt/default.nix @@ -0,0 +1,9 @@ +{ pkgs, ge-additions, ed25519 }: + +pkgs.stdenv.mkDerivation rec { + name = "urcrypt"; + builder = ./builder.sh; + src = ../../../pkg/urcrypt; + + nativeBuildInputs = [ ed25519 ge-additions ]; +} diff --git a/nix/pkgs/urcrypt/release.sh b/nix/pkgs/urcrypt/release.sh new file mode 100644 index 000000000..aaa54b5e1 --- /dev/null +++ b/nix/pkgs/urcrypt/release.sh @@ -0,0 +1,13 @@ +source $setup + +cp -r $src ./src +chmod -R u+w ./src +cd ./src + +for dep in $cross_inputs; do + export CFLAGS="${CFLAGS-} -I$dep/include" + export LDFLAGS="${LDFLAGS-} -L$dep/lib" +done + +PREFIX=$out make install + diff --git a/nix/release.nix b/nix/release.nix index 2ccd1098b..dc1ce41e7 100644 --- a/nix/release.nix +++ b/nix/release.nix @@ -19,6 +19,9 @@ let ge-additions = env: import ./pkgs/ge-additions/cross.nix env; + urcrypt = env: + import ./pkgs/urcrypt/cross.nix env; + libaes_siv = env: import ./pkgs/libaes_siv/cross.nix env; diff --git a/pkg/urbit/configure b/pkg/urbit/configure index 6157fb544..d496a86c0 100755 --- a/pkg/urbit/configure +++ b/pkg/urbit/configure @@ -6,7 +6,7 @@ URBIT_VERSION="0.10.8" deps=" \ curl gmp sigsegv argon2 ed25519 ent h2o scrypt uv murmur3 secp256k1 \ - softfloat3 ssl crypto z lmdb ge-additions aes_siv pthread \ + softfloat3 ssl crypto z lmdb ge-additions aes_siv urcrypt pthread \ " headers=" \ diff --git a/pkg/urbit/jets/e/ed_scalarmult.c b/pkg/urbit/jets/e/ed_scalarmult.c index aca547e39..607a40caf 100644 --- a/pkg/urbit/jets/e/ed_scalarmult.c +++ b/pkg/urbit/jets/e/ed_scalarmult.c @@ -6,9 +6,37 @@ #include #include "ge-additions.h" +#include "urcrypt.h" /* functions */ + // TODO: should these exits be u3_none or bail: fail instead? + u3_noun + u3qc_scalarmult_new(u3_atom a, u3_atom b) + { + c3_w ate_w, bet_w; + c3_y a_y[32], b_y[32], out_y[32]; + + if ( (ate_w = u3r_met(3, a)) > 32 ) { + return u3m_bail(c3__exit); + } + + if ( (bet_w = u3r_met(3, b)) > 32 ) { + return u3m_bail(c3__exit); + } + + memset(a_y, 0, 32); + memset(b_y, 0, 32); + u3r_bytes(0, ate_w, a_y, a); + u3r_bytes(0, bet_w, b_y, b); + + if ( 0 != urcrypt_ed_scalarmult(a_y, b_y, out_y) ) { + return u3m_bail(c3__exit); + } + + return u3i_bytes(32, out_y); + } + u3_noun u3qc_scalarmult(u3_atom a, u3_atom b) diff --git a/pkg/urcrypt/Makefile b/pkg/urcrypt/Makefile new file mode 100644 index 000000000..b1c6e51dd --- /dev/null +++ b/pkg/urcrypt/Makefile @@ -0,0 +1,20 @@ +CC ?= cc +AR ?= ar +PREFIX ?= ./out + +################################################################################ + +.PHONY: all test install clean + +all: urcrypt.c urcrypt.h + $(CC) $(CFLAGS) -O3 -Wall -Werror -pedantic -std=gnu99 -c urcrypt.c + $(AR) rcs liburcrypt.a urcrypt.o + +install: all + @mkdir -p $(PREFIX)/lib/ + @mkdir -p $(PREFIX)/include/ + cp liburcrypt.a $(PREFIX)/lib/ + cp urcrypt.h $(PREFIX)/include/ + +clean: + rm -rf ./out diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c new file mode 100644 index 000000000..41f5c7c73 --- /dev/null +++ b/pkg/urcrypt/urcrypt.c @@ -0,0 +1,19 @@ +#include "urcrypt.h" + +int +urcrypt_ed_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t out[32]) +{ + ge_p3 B, result; + + if ( ge_frombytes_negate_vartime(&B, b) != 0 ) { + return -1; + } + + // Undo the negation from above. See add_scalar.c in the ed25519 distro. + fe_neg(B.X, B.X); + fe_neg(B.T, B.T); + + ge_scalarmult(&result, a, &B); + ge_p3_tobytes(out, &result); + return 0; +} diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h new file mode 100644 index 000000000..9a0360469 --- /dev/null +++ b/pkg/urcrypt/urcrypt.h @@ -0,0 +1,10 @@ +#ifndef URCRYPT_H +#define URCRYPT_H + +#include +#include +#include + +int urcrypt_ed_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t out[32]); + +#endif From d7287d2114a91dd9321f325487b9421f52239fed Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Mon, 27 Jul 2020 19:30:20 -0700 Subject: [PATCH 02/75] ed_scalarmult jet only uses urcrypt --- pkg/urbit/jets/e/ed_scalarmult.c | 47 ++------------------------------ 1 file changed, 2 insertions(+), 45 deletions(-) diff --git a/pkg/urbit/jets/e/ed_scalarmult.c b/pkg/urbit/jets/e/ed_scalarmult.c index 607a40caf..0e7136f66 100644 --- a/pkg/urbit/jets/e/ed_scalarmult.c +++ b/pkg/urbit/jets/e/ed_scalarmult.c @@ -2,17 +2,14 @@ ** */ #include "all.h" - -#include - -#include "ge-additions.h" #include "urcrypt.h" /* functions */ // TODO: should these exits be u3_none or bail: fail instead? u3_noun - u3qc_scalarmult_new(u3_atom a, u3_atom b) + u3qc_scalarmult(u3_atom a, + u3_atom b) { c3_w ate_w, bet_w; c3_y a_y[32], b_y[32], out_y[32]; @@ -37,46 +34,6 @@ return u3i_bytes(32, out_y); } - u3_noun - u3qc_scalarmult(u3_atom a, - u3_atom b) - { - c3_y met_w; - - met_w = u3r_met(3, a); - if (met_w > 32) { - return u3m_bail(c3__exit); - } - c3_y a_y[32]; - memset(a_y, 0, 32); - u3r_bytes(0, met_w, a_y, a); - - met_w = u3r_met(3, b); - if (met_w > 32) { - return u3m_bail(c3__exit); - } - c3_y b_y[32]; - memset(b_y, 0, 32); - u3r_bytes(0, met_w, b_y, b); - - ge_p3 B; - if (ge_frombytes_negate_vartime(&B, b_y) != 0) { - return u3m_bail(c3__exit); - } - - // Undo the negation from above. See add_scalar.c in the ed25519 distro. - fe_neg(B.X, B.X); - fe_neg(B.T, B.T); - - ge_p3 result; - ge_scalarmult(&result, a_y, &B); - - c3_y output_y[32]; - ge_p3_tobytes(output_y, &result); - - return u3i_bytes(32, output_y); - } - u3_noun u3wee_scalarmult(u3_noun cor) { From 74d06deb1b0ca2f2e3359694b841195510c5cd9a Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Thu, 30 Jul 2020 13:07:13 -0700 Subject: [PATCH 03/75] one working function in so --- nix/pkgs/urcrypt/cross.nix | 12 ------------ nix/pkgs/urcrypt/default.nix | 2 +- nix/pkgs/urcrypt/release.sh | 13 ------------- nix/release.nix | 3 --- pkg/urcrypt/Makefile | 18 +++++++++++++++--- 5 files changed, 16 insertions(+), 32 deletions(-) delete mode 100644 nix/pkgs/urcrypt/cross.nix delete mode 100644 nix/pkgs/urcrypt/release.sh diff --git a/nix/pkgs/urcrypt/cross.nix b/nix/pkgs/urcrypt/cross.nix deleted file mode 100644 index 48ef204bf..000000000 --- a/nix/pkgs/urcrypt/cross.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ env_name, env, deps }: - -env.make_derivation rec { - name = "urcrypt"; - builder = ./release.sh; - src = ../../../pkg/urcrypt; - - cross_inputs = [ deps.ed25519 deps.ge-additions ]; - - CC = "${env.host}-gcc"; - AR = "${env.host}-ar"; -} diff --git a/nix/pkgs/urcrypt/default.nix b/nix/pkgs/urcrypt/default.nix index bb99897c4..4367c6597 100644 --- a/nix/pkgs/urcrypt/default.nix +++ b/nix/pkgs/urcrypt/default.nix @@ -5,5 +5,5 @@ pkgs.stdenv.mkDerivation rec { builder = ./builder.sh; src = ../../../pkg/urcrypt; - nativeBuildInputs = [ ed25519 ge-additions ]; + buildInputs = [ ed25519 ge-additions ]; } diff --git a/nix/pkgs/urcrypt/release.sh b/nix/pkgs/urcrypt/release.sh deleted file mode 100644 index aaa54b5e1..000000000 --- a/nix/pkgs/urcrypt/release.sh +++ /dev/null @@ -1,13 +0,0 @@ -source $setup - -cp -r $src ./src -chmod -R u+w ./src -cd ./src - -for dep in $cross_inputs; do - export CFLAGS="${CFLAGS-} -I$dep/include" - export LDFLAGS="${LDFLAGS-} -L$dep/lib" -done - -PREFIX=$out make install - diff --git a/nix/release.nix b/nix/release.nix index dc1ce41e7..2ccd1098b 100644 --- a/nix/release.nix +++ b/nix/release.nix @@ -19,9 +19,6 @@ let ge-additions = env: import ./pkgs/ge-additions/cross.nix env; - urcrypt = env: - import ./pkgs/urcrypt/cross.nix env; - libaes_siv = env: import ./pkgs/libaes_siv/cross.nix env; diff --git a/pkg/urcrypt/Makefile b/pkg/urcrypt/Makefile index b1c6e51dd..d67fe974d 100644 --- a/pkg/urcrypt/Makefile +++ b/pkg/urcrypt/Makefile @@ -6,14 +6,26 @@ PREFIX ?= ./out .PHONY: all test install clean -all: urcrypt.c urcrypt.h - $(CC) $(CFLAGS) -O3 -Wall -Werror -pedantic -std=gnu99 -c urcrypt.c - $(AR) rcs liburcrypt.a urcrypt.o +CFLAGS := $(CFLAGS) -O3 -Wall -Werror -pedantic -std=gnu99 +SOURCES = urcrypt.c urcrypt.h + +liburcrypt.a: $(SOURCES) + $(CC) $(CFLAGS) -c urcrypt.c -o urcrypt-static.o + $(AR) rcs liburcrypt.a urcrypt-static.o + +liburcrypt.so: $(SOURCES) + $(CC) $(CFLAGS) -fPIC -c urcrypt.c -o urcrypt-shared.o + $(CC) -shared urcrypt-shared.o -o liburcrypt.so \ + -led25519 -lge-additions \ + -Wl,--no-undefined + +all: liburcrypt.a liburcrypt.so install: all @mkdir -p $(PREFIX)/lib/ @mkdir -p $(PREFIX)/include/ cp liburcrypt.a $(PREFIX)/lib/ + cp liburcrypt.so $(PREFIX)/lib/ cp urcrypt.h $(PREFIX)/include/ clean: From 6cb81fe08461f6517d2e5c3cb48a27c8a6558efc Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Thu, 30 Jul 2020 14:40:47 -0700 Subject: [PATCH 04/75] ed_sign moved to urcrypt --- pkg/urbit/jets/e/ed_scalarmult.c | 9 +++---- pkg/urbit/jets/e/ed_sign.c | 40 ++++++++++++++------------------ pkg/urcrypt/urcrypt.c | 16 +++++++++++++ pkg/urcrypt/urcrypt.h | 5 ++++ 4 files changed, 43 insertions(+), 27 deletions(-) diff --git a/pkg/urbit/jets/e/ed_scalarmult.c b/pkg/urbit/jets/e/ed_scalarmult.c index 0e7136f66..727ba9f7e 100644 --- a/pkg/urbit/jets/e/ed_scalarmult.c +++ b/pkg/urbit/jets/e/ed_scalarmult.c @@ -6,7 +6,6 @@ /* functions */ - // TODO: should these exits be u3_none or bail: fail instead? u3_noun u3qc_scalarmult(u3_atom a, u3_atom b) @@ -15,11 +14,12 @@ c3_y a_y[32], b_y[32], out_y[32]; if ( (ate_w = u3r_met(3, a)) > 32 ) { - return u3m_bail(c3__exit); + // hoon does not check size of inputs + return u3_none; } if ( (bet_w = u3r_met(3, b)) > 32 ) { - return u3m_bail(c3__exit); + return u3_none; } memset(a_y, 0, 32); @@ -28,7 +28,8 @@ u3r_bytes(0, bet_w, b_y, b); if ( 0 != urcrypt_ed_scalarmult(a_y, b_y, out_y) ) { - return u3m_bail(c3__exit); + // this is unlikely to happen, but there is a return code. + return u3_none; } return u3i_bytes(32, out_y); diff --git a/pkg/urbit/jets/e/ed_sign.c b/pkg/urbit/jets/e/ed_sign.c index cef88afc6..db59b1252 100644 --- a/pkg/urbit/jets/e/ed_sign.c +++ b/pkg/urbit/jets/e/ed_sign.c @@ -2,9 +2,7 @@ ** */ #include "all.h" - - -#include +#include /* functions */ @@ -12,30 +10,26 @@ _cqee_sign(u3_noun a, u3_noun b) { - c3_y sig_y[64]; - c3_y sed_y[32]; - c3_y pub_y[64]; - c3_y sec_y[64]; + c3_w b_w = u3r_met(3, b); - c3_w mesm_w = u3r_met(3, a); - c3_w mess_w = u3r_met(3, b); + if ( b_w > 32 ) { + // hoon calls suck, which calls puck, which crashes + return u3m_bail(c3__exit); + } + else { + c3_w a_w = u3r_met(3, a); + c3_y* mes_y = u3a_malloc(a_w); + c3_y sed_y[32], sig_y[64]; - c3_y* mes_y = 0; + memset(sed_y, 0, 32); + u3r_bytes(0, a_w, mes_y, a); + u3r_bytes(0, b_w, sed_y, b); - memset(sig_y, 0, 64); - memset(sed_y, 0, 32); - memset(pub_y, 0, 64); - memset(sec_y, 0, 64); + urcrypt_ed_sign(mes_y, a_w, sed_y, sig_y); - mes_y = u3a_malloc(mesm_w); - - u3r_bytes(0, mesm_w, mes_y, a); - u3r_bytes(0, mess_w, sed_y, b); - - ed25519_create_keypair(pub_y, sec_y, sed_y); - ed25519_sign(sig_y, mes_y, mesm_w, pub_y, sec_y); - u3a_free(mes_y); - return u3i_bytes(64, sig_y); + u3a_free(mes_y); + return u3i_bytes(64, sig_y); + } } u3_noun diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 41f5c7c73..9e06b3d43 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -17,3 +17,19 @@ urcrypt_ed_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t out[32]) ge_p3_tobytes(out, &result); return 0; } + +void +urcrypt_ed_sign(uint8_t *message, + size_t length, + uint8_t seed[32], + uint8_t out[64]) +{ + uint8_t public[64], secret[64]; + + memset(public, 0, 64); + memset(secret, 0, 64); + memset(out, 0, 64); + + ed25519_create_keypair(public, secret, seed); + ed25519_sign(out, message, length, public, secret); +} diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 9a0360469..277d04bca 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -2,9 +2,14 @@ #define URCRYPT_H #include +#include #include #include int urcrypt_ed_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t out[32]); +void urcrypt_ed_sign(uint8_t *message, + size_t length, + uint8_t seed[32], + uint8_t signature[64]); #endif From cc5998ae07a2b373ae33d33a1f44a14d794fa6f2 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 31 Jul 2020 10:50:46 -0700 Subject: [PATCH 05/75] move ed_point_add to urcrypt --- pkg/urbit/jets/e/ed_point_add.c | 63 +++++++++------------------------ pkg/urcrypt/urcrypt.c | 31 ++++++++++++++++ pkg/urcrypt/urcrypt.h | 1 + 3 files changed, 48 insertions(+), 47 deletions(-) diff --git a/pkg/urbit/jets/e/ed_point_add.c b/pkg/urbit/jets/e/ed_point_add.c index 9d57b7dea..a1eff2e58 100644 --- a/pkg/urbit/jets/e/ed_point_add.c +++ b/pkg/urbit/jets/e/ed_point_add.c @@ -2,64 +2,33 @@ ** */ #include "all.h" - - -#include -#include +#include /* functions */ + u3_noun u3qc_point_add(u3_atom a, u3_atom b) { - c3_y met_w; + c3_w ate_w, bet_w; - met_w = u3r_met(3, a); - if (met_w > 32) { - return u3m_bail(c3__fail); + if ( ((ate_w = u3r_met(3, a)) > 32) || + ((bet_w = u3r_met(3, b)) > 32) ) { + return u3_none; } - c3_y a_y[32]; - memset(a_y, 0, 32); - u3r_bytes(0, met_w, a_y, a); + else { + c3_y a_y[32], b_y[32], out_y[32]; - met_w = u3r_met(3, b); - if (met_w > 32) { - return u3m_bail(c3__fail); + memset(a_y, 0, 32); + memset(b_y, 0, 32); + u3r_bytes(0, ate_w, a_y, a); + u3r_bytes(0, bet_w, b_y, b); + + return ( 0 == urcrypt_ed_point_add(a_y, b_y, out_y) ) + ? u3i_bytes(32, out_y) + : u3_none; } - c3_y b_y[32]; - memset(b_y, 0, 32); - u3r_bytes(0, met_w, b_y, b); - - ge_p3 A; - if (ge_frombytes_negate_vartime(&A, a_y) != 0) { - return u3m_bail(c3__exit); - } - - ge_p3 B; - if (ge_frombytes_negate_vartime(&B, b_y) != 0) { - return u3m_bail(c3__exit); - } - - // Undo the negation from above. See add_scalar.c in the ed25519 distro. - fe_neg(A.X, A.X); - fe_neg(A.T, A.T); - fe_neg(B.X, B.X); - fe_neg(B.T, B.T); - - ge_cached b_cached; - ge_p3_to_cached(&b_cached, &B); - - ge_p1p1 sum; - ge_add(&sum, &A, &b_cached); - - ge_p3 result; - ge_p1p1_to_p3(&result, &sum); - - c3_y output_y[32]; - ge_p3_tobytes(output_y, &result); - - return u3i_bytes(32, output_y); } u3_noun diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 9e06b3d43..96b455ce4 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -1,5 +1,36 @@ #include "urcrypt.h" +int +urcrypt_ed_point_add(uint8_t a[32], uint8_t b[32], uint8_t out[32]) +{ + ge_p3 A, B; + ge_cached b_cached; + ge_p1p1 sum; + ge_p3 result; + + if ( ge_frombytes_negate_vartime(&A, a) != 0 ) { + return -1; + } + + if ( ge_frombytes_negate_vartime(&B, b) != 0 ) { + return -1; + } + + // Undo the negation from above. See add_scalar.c in the ed25519 distro. + fe_neg(A.X, A.X); + fe_neg(A.T, A.T); + fe_neg(B.X, B.X); + fe_neg(B.T, B.T); + + ge_p3_to_cached(&b_cached, &B); + ge_add(&sum, &A, &b_cached); + ge_p1p1_to_p3(&result, &sum); + + ge_p3_tobytes(out, &result); + + return 0; +} + int urcrypt_ed_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t out[32]) { diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 277d04bca..1204cda38 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -6,6 +6,7 @@ #include #include +int urcrypt_ed_point_add(uint8_t a[32], uint8_t b[32], uint8_t out[32]); int urcrypt_ed_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t out[32]); void urcrypt_ed_sign(uint8_t *message, size_t length, From cfb9b21018a21e683571b3fdb37cd9c6a4a709f0 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 31 Jul 2020 11:55:02 -0700 Subject: [PATCH 06/75] ed_scalarmult_base -> urcrypt --- pkg/urbit/jets/e/ed_scalarmult_base.c | 45 ++++++++++++++------------- pkg/urcrypt/urcrypt.c | 8 +++++ pkg/urcrypt/urcrypt.h | 1 + 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/pkg/urbit/jets/e/ed_scalarmult_base.c b/pkg/urbit/jets/e/ed_scalarmult_base.c index bc3cda7f6..3bdbedaa1 100644 --- a/pkg/urbit/jets/e/ed_scalarmult_base.c +++ b/pkg/urbit/jets/e/ed_scalarmult_base.c @@ -2,35 +2,38 @@ ** */ #include "all.h" - -#include -#include +#include /* functions */ + u3_noun + u3qc_scalarmult_base(u3_atom a) + { + c3_w met_w = u3r_met(3, a); + + if ( met_w > 32 ) { + return u3_none; + } + else { + c3_y a_y[32], out_y[32]; + + memset(a_y, 0, 32); + u3r_bytes(0, met_w, a_y, a); + + urcrypt_ed_scalarmult_base(a_y, out_y); + return u3i_bytes(32, out_y); + } + } + u3_noun u3wee_scalarmult_base(u3_noun cor) { - u3_noun scalar = u3r_at(u3x_sam, cor); + u3_noun a = u3r_at(u3x_sam, cor); - if ( (u3_none == scalar) || (c3n == u3ud(scalar)) ) { + if ( (u3_none == a) || (c3n == u3ud(a)) ) { return u3m_bail(c3__exit); } - - c3_w met_w = u3r_met(3, scalar); - if ( met_w > 32 ) { - return u3m_bail(c3__fail); + else { + return u3qc_scalarmult_base(a); } - - c3_y scalar_y[32]; - memset(scalar_y, 0, 32); - u3r_bytes(0, met_w, scalar_y, scalar); - - ge_p3 R; - ge_scalarmult_base(&R, scalar_y); - - c3_y output_y[32]; - ge_p3_tobytes(output_y, &R); - - return u3i_bytes(32, output_y); } diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 96b455ce4..2539e7ee9 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -49,6 +49,14 @@ urcrypt_ed_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t out[32]) return 0; } +void +urcrypt_ed_scalarmult_base(uint8_t a[32], uint8_t out[32]) +{ + ge_p3 R; + ge_scalarmult_base(&R, a); + ge_p3_tobytes(out, &R); +} + void urcrypt_ed_sign(uint8_t *message, size_t length, diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 1204cda38..d6019d684 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -8,6 +8,7 @@ int urcrypt_ed_point_add(uint8_t a[32], uint8_t b[32], uint8_t out[32]); int urcrypt_ed_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t out[32]); +void urcrypt_ed_scalarmult_base(uint8_t a[32], uint8_t out[32]); void urcrypt_ed_sign(uint8_t *message, size_t length, uint8_t seed[32], From 5b9f60fd2fd005ab2b8a8cf442a0738e3c4e61e0 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 31 Jul 2020 14:21:02 -0700 Subject: [PATCH 07/75] urcrypt_ed_add_scalarmult_scalarmult_base --- .../e/ed_add_scalarmult_scalarmult_base.c | 66 ++++++------------- pkg/urcrypt/Makefile | 4 +- pkg/urcrypt/urcrypt.c | 23 +++++++ pkg/urcrypt/urcrypt.h | 4 ++ 4 files changed, 50 insertions(+), 47 deletions(-) diff --git a/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c b/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c index aea3c92af..8596ea536 100644 --- a/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c +++ b/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c @@ -2,61 +2,37 @@ ** */ #include "all.h" - - -#include -#include +#include /* functions */ - u3_noun u3qc_add_scalarmult_scalarmult_base(u3_atom a, - u3_atom a_point, - u3_atom b) + u3_atom b, + u3_atom c) { - c3_y met_w; + c3_w ate_w, bet_w, get_w; - met_w = u3r_met(3, a); - if (met_w > 32) { - return u3m_bail(c3__fail); + if ( ((ate_w = u3r_met(3, a)) > 32) || + ((bet_w = u3r_met(3, b)) > 32) || + ((get_w = u3r_met(3, c)) > 32) ) { + return u3_none; } - c3_y a_y[32]; - memset(a_y, 0, 32); - u3r_bytes(0, met_w, a_y, a); + else { + c3_y a_y[32], b_y[32], c_y[32], out_y[32]; - met_w = u3r_met(3, a_point); - if (met_w > 32) { - return u3m_bail(c3__fail); + memset(a_y, 0, 32); + memset(b_y, 0, 32); + memset(c_y, 0, 32); + u3r_bytes(0, ate_w, a_y, a); + u3r_bytes(0, bet_w, b_y, b); + u3r_bytes(0, get_w, c_y, c); + + return + ( 0 == urcrypt_ed_add_scalarmult_scalarmult_base(a_y, b_y, c_y, out_y) ) + ? u3i_bytes(32, out_y) + : u3_none; } - c3_y a_point_y[32]; - memset(a_point_y, 0, 32); - u3r_bytes(0, met_w, a_point_y, a_point); - - met_w = u3r_met(3, b); - if (met_w > 32) { - return u3m_bail(c3__fail); - } - c3_y b_y[32]; - memset(b_y, 0, 32); - u3r_bytes(0, met_w, b_y, b); - - ge_p3 A; - if (ge_frombytes_negate_vartime(&A, a_point_y) != 0) { - return u3m_bail(c3__exit); - } - - // Undo the negation from above. See add_scalar.c in the ed25519 distro. - fe_neg(A.X, A.X); - fe_neg(A.T, A.T); - - ge_p2 r; - ge_double_scalarmult_vartime(&r, a_y, &A, b_y); - - c3_y output_y[32]; - ge_tobytes(output_y, &r); - - return u3i_bytes(32, output_y); } u3_noun diff --git a/pkg/urcrypt/Makefile b/pkg/urcrypt/Makefile index d67fe974d..6c283fcc3 100644 --- a/pkg/urcrypt/Makefile +++ b/pkg/urcrypt/Makefile @@ -4,7 +4,7 @@ PREFIX ?= ./out ################################################################################ -.PHONY: all test install clean +.PHONY: all install clean CFLAGS := $(CFLAGS) -O3 -Wall -Werror -pedantic -std=gnu99 SOURCES = urcrypt.c urcrypt.h @@ -29,4 +29,4 @@ install: all cp urcrypt.h $(PREFIX)/include/ clean: - rm -rf ./out + rm urcrypt-static.o urcrypt-shared.o liburcrypt.a liburcrypt.so diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 2539e7ee9..28796aa32 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -57,6 +57,29 @@ urcrypt_ed_scalarmult_base(uint8_t a[32], uint8_t out[32]) ge_p3_tobytes(out, &R); } +int +urcrypt_ed_add_scalarmult_scalarmult_base(uint8_t a[32], + uint8_t a_point[32], + uint8_t b[32], + uint8_t out[32]) +{ + ge_p2 r; + ge_p3 A; + + if (ge_frombytes_negate_vartime(&A, a_point) != 0) { + return -1; + } + + // Undo the negation from above. See add_scalar.c in the ed25519 distro. + fe_neg(A.X, A.X); + fe_neg(A.T, A.T); + + ge_double_scalarmult_vartime(&r, a, &A, b); + ge_tobytes(out, &r); + + return 0; +} + void urcrypt_ed_sign(uint8_t *message, size_t length, diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index d6019d684..d9bb411f5 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -9,6 +9,10 @@ int urcrypt_ed_point_add(uint8_t a[32], uint8_t b[32], uint8_t out[32]); int urcrypt_ed_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t out[32]); void urcrypt_ed_scalarmult_base(uint8_t a[32], uint8_t out[32]); +int urcrypt_ed_add_scalarmult_scalarmult_base(uint8_t a[32], + uint8_t a_point[32], + uint8_t b[32], + uint8_t out[32]); void urcrypt_ed_sign(uint8_t *message, size_t length, uint8_t seed[32], From 2369b4eae26ac4069b458435631ced2b3c2dfbad Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 31 Jul 2020 14:46:19 -0700 Subject: [PATCH 08/75] urcrypt_ed_add_double_scalarmult --- pkg/urbit/jets/e/ed_add_double_scalarmult.c | 94 +++++---------------- pkg/urcrypt/urcrypt.c | 39 +++++++++ pkg/urcrypt/urcrypt.h | 5 ++ 3 files changed, 67 insertions(+), 71 deletions(-) diff --git a/pkg/urbit/jets/e/ed_add_double_scalarmult.c b/pkg/urbit/jets/e/ed_add_double_scalarmult.c index cee7af3e5..46b9c9515 100644 --- a/pkg/urbit/jets/e/ed_add_double_scalarmult.c +++ b/pkg/urbit/jets/e/ed_add_double_scalarmult.c @@ -2,88 +2,40 @@ ** */ #include "all.h" - -#include -#include - -#include "ge-additions.h" +#include /* functions */ u3_noun u3qc_add_double_scalarmult(u3_atom a, - u3_atom a_point, u3_atom b, - u3_atom b_point) + u3_atom c, + u3_atom d) { - c3_y met_w; + c3_w ate_w, bet_w, get_w, det_w; - met_w = u3r_met(3, a); - if (met_w > 32) { - return u3m_bail(c3__fail); + if ( ((ate_w = u3r_met(3, a)) > 32) || + ((bet_w = u3r_met(3, b)) > 32) || + ((get_w = u3r_met(3, c)) > 32) || + ((det_w = u3r_met(3, d)) > 32) ) { + return u3_none; } - c3_y a_y[32]; - memset(a_y, 0, 32); - u3r_bytes(0, met_w, a_y, a); + else { + c3_y a_y[32], b_y[32], c_y[32], d_y[32], out_y[32]; - met_w = u3r_met(3, a_point); - if (met_w > 32) { - return u3m_bail(c3__fail); + memset(a_y, 0, 32); + memset(b_y, 0, 32); + memset(c_y, 0, 32); + memset(d_y, 0, 32); + u3r_bytes(0, ate_w, a_y, a); + u3r_bytes(0, bet_w, b_y, b); + u3r_bytes(0, get_w, c_y, c); + u3r_bytes(0, det_w, d_y, d); + + return ( 0 == urcrypt_ed_add_double_scalarmult(a_y, b_y, c_y, d_y, out_y) ) + ? u3i_bytes(32, out_y) + : u3_none; } - c3_y a_point_y[32]; - memset(a_point_y, 0, 32); - u3r_bytes(0, met_w, a_point_y, a_point); - - met_w = u3r_met(3, b); - if (met_w > 32) { - return u3m_bail(c3__fail); - } - c3_y b_y[32]; - memset(b_y, 0, 32); - u3r_bytes(0, met_w, b_y, b); - - met_w = u3r_met(3, b_point); - if (met_w > 32) { - return u3m_bail(c3__fail); - } - c3_y b_point_y[32]; - memset(b_point_y, 0, 32); - u3r_bytes(0, met_w, b_point_y, b_point); - - ge_p3 A; - if (ge_frombytes_negate_vartime(&A, a_point_y) != 0) { - return u3m_bail(c3__exit); - } - - ge_p3 B; - if (ge_frombytes_negate_vartime(&B, b_point_y) != 0) { - return u3m_bail(c3__exit); - } - - // Undo the negation from above. See add_scalar.c in the ed25519 distro. - fe_neg(A.X, A.X); - fe_neg(A.T, A.T); - fe_neg(B.X, B.X); - fe_neg(B.T, B.T); - - // Perform the multiplications of a*A and b*B - ge_p3 a_result, b_result; - ge_scalarmult(&a_result, a_y, &A); - ge_scalarmult(&b_result, b_y, &B); - - // Sum those two points - ge_cached b_result_cached; - ge_p3_to_cached(&b_result_cached, &b_result); - ge_p1p1 sum; - ge_add(&sum, &a_result, &b_result_cached); - - ge_p3 final_result; - ge_p1p1_to_p3(&final_result, &sum); - - c3_y output_y[32]; - ge_p3_tobytes(output_y, &final_result); - - return u3i_bytes(32, output_y); } u3_noun diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 28796aa32..7cc214aaa 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -80,6 +80,45 @@ urcrypt_ed_add_scalarmult_scalarmult_base(uint8_t a[32], return 0; } +int +urcrypt_ed_add_double_scalarmult(uint8_t a[32], + uint8_t a_point[32], + uint8_t b[32], + uint8_t b_point[32], + uint8_t out[32]) +{ + ge_p3 A, B, a_result, b_result, final_result; + ge_cached b_result_cached; + ge_p1p1 sum; + + if ( ge_frombytes_negate_vartime(&A, a_point) != 0 ) { + return -1; + } + + if ( ge_frombytes_negate_vartime(&B, b_point) != 0 ) { + return -1; + } + + // Undo the negation from above. See add_scalar.c in the ed25519 distro. + fe_neg(A.X, A.X); + fe_neg(A.T, A.T); + fe_neg(B.X, B.X); + fe_neg(B.T, B.T); + + // Perform the multiplications of a*A and b*B + ge_scalarmult(&a_result, a, &A); + ge_scalarmult(&b_result, b, &B); + + // Sum those two points + ge_p3_to_cached(&b_result_cached, &b_result); + ge_add(&sum, &a_result, &b_result_cached); + + ge_p1p1_to_p3(&final_result, &sum); + ge_p3_tobytes(out, &final_result); + + return 0; +} + void urcrypt_ed_sign(uint8_t *message, size_t length, diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index d9bb411f5..62e0c279a 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -13,6 +13,11 @@ int urcrypt_ed_add_scalarmult_scalarmult_base(uint8_t a[32], uint8_t a_point[32], uint8_t b[32], uint8_t out[32]); +int urcrypt_ed_add_double_scalarmult(uint8_t a[32], + uint8_t a_point[32], + uint8_t b[32], + uint8_t b_point[32], + uint8_t out[32]); void urcrypt_ed_sign(uint8_t *message, size_t length, uint8_t seed[32], From bd17aa70ca3a6ce690c373fcfd52a9b11d49afe8 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 31 Jul 2020 16:31:56 -0700 Subject: [PATCH 09/75] urcrypt_ed_puck --- pkg/urbit/jets/e/ed_add_double_scalarmult.c | 6 +-- .../e/ed_add_scalarmult_scalarmult_base.c | 6 +-- pkg/urbit/jets/e/ed_point_add.c | 6 +-- pkg/urbit/jets/e/ed_puck.c | 37 +++++++++++-------- pkg/urbit/jets/e/ed_scalarmult.c | 6 +-- pkg/urbit/jets/e/ed_scalarmult_base.c | 6 +-- pkg/urbit/jets/e/ed_sign.c | 2 +- pkg/urcrypt/urcrypt.c | 7 ++++ pkg/urcrypt/urcrypt.h | 3 +- 9 files changed, 46 insertions(+), 33 deletions(-) diff --git a/pkg/urbit/jets/e/ed_add_double_scalarmult.c b/pkg/urbit/jets/e/ed_add_double_scalarmult.c index 46b9c9515..b177d826d 100644 --- a/pkg/urbit/jets/e/ed_add_double_scalarmult.c +++ b/pkg/urbit/jets/e/ed_add_double_scalarmult.c @@ -6,8 +6,8 @@ /* functions */ - u3_noun - u3qc_add_double_scalarmult(u3_atom a, + static u3_atom + _cqee_add_double_scalarmult(u3_atom a, u3_atom b, u3_atom c, u3_atom d) @@ -53,6 +53,6 @@ { return u3m_bail(c3__exit); } else { - return u3qc_add_double_scalarmult(a, b, c, d); + return _cqee_add_double_scalarmult(a, b, c, d); } } diff --git a/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c b/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c index 8596ea536..4ac8fc1ec 100644 --- a/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c +++ b/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c @@ -6,8 +6,8 @@ /* functions */ - u3_noun - u3qc_add_scalarmult_scalarmult_base(u3_atom a, + static u3_atom + _cqee_add_scalarmult_scalarmult_base(u3_atom a, u3_atom b, u3_atom c) { @@ -49,6 +49,6 @@ { return u3m_bail(c3__exit); } else { - return u3qc_add_scalarmult_scalarmult_base(a, b, c); + return _cqee_add_scalarmult_scalarmult_base(a, b, c); } } diff --git a/pkg/urbit/jets/e/ed_point_add.c b/pkg/urbit/jets/e/ed_point_add.c index a1eff2e58..fe105ac6e 100644 --- a/pkg/urbit/jets/e/ed_point_add.c +++ b/pkg/urbit/jets/e/ed_point_add.c @@ -7,8 +7,8 @@ /* functions */ - u3_noun - u3qc_point_add(u3_atom a, + static u3_atom + _cqee_point_add(u3_atom a, u3_atom b) { c3_w ate_w, bet_w; @@ -43,6 +43,6 @@ { return u3m_bail(c3__exit); } else { - return u3qc_point_add(a, b); + return _cqee_point_add(a, b); } } diff --git a/pkg/urbit/jets/e/ed_puck.c b/pkg/urbit/jets/e/ed_puck.c index 781899784..8c6f3760f 100644 --- a/pkg/urbit/jets/e/ed_puck.c +++ b/pkg/urbit/jets/e/ed_puck.c @@ -2,32 +2,37 @@ ** */ #include "all.h" - - -#include +#include /* functions */ + static u3_atom + _cqee_puck(u3_atom sed) + { + c3_y sed_y[32], pub_y[32]; + c3_w met_w; + + if ( (met_w = u3r_met(3, sed)) > 32 ) { + // hoon explicitly crashes on mis-size + return u3m_bail(c3__exit); + } + + memset(sed_y, 0, 32); + u3r_bytes(0, met_w, sed_y, sed); + + urcrypt_ed_puck(sed_y, pub_y); + return u3i_bytes(32, pub_y); + } + u3_noun u3wee_puck(u3_noun cor) { - c3_y pub_y[32]; - c3_y sec_y[64]; - c3_y sed_y[32]; - c3_w met_w; u3_noun a = u3r_at(u3x_sam, cor); if ( (u3_none == a) || (c3n == u3ud(a)) ) { return u3m_bail(c3__exit); } - - met_w = u3r_met(3, a); - if ( met_w > 32 ) { - return u3m_bail(c3__exit); + else { + return _cqee_puck(a); } - - memset(sed_y, 0, 32); - u3r_bytes(0, met_w, sed_y, a); - ed25519_create_keypair(pub_y, sec_y, sed_y); - return u3i_bytes(32, pub_y); } diff --git a/pkg/urbit/jets/e/ed_scalarmult.c b/pkg/urbit/jets/e/ed_scalarmult.c index 727ba9f7e..bee52d958 100644 --- a/pkg/urbit/jets/e/ed_scalarmult.c +++ b/pkg/urbit/jets/e/ed_scalarmult.c @@ -6,8 +6,8 @@ /* functions */ - u3_noun - u3qc_scalarmult(u3_atom a, + static u3_atom + _cqee_scalarmult(u3_atom a, u3_atom b) { c3_w ate_w, bet_w; @@ -47,6 +47,6 @@ { return u3m_bail(c3__exit); } else { - return u3qc_scalarmult(a, b); + return _cqee_scalarmult(a, b); } } diff --git a/pkg/urbit/jets/e/ed_scalarmult_base.c b/pkg/urbit/jets/e/ed_scalarmult_base.c index 3bdbedaa1..fb0d2f813 100644 --- a/pkg/urbit/jets/e/ed_scalarmult_base.c +++ b/pkg/urbit/jets/e/ed_scalarmult_base.c @@ -6,8 +6,8 @@ /* functions */ - u3_noun - u3qc_scalarmult_base(u3_atom a) + static u3_atom + _cqee_scalarmult_base(u3_atom a) { c3_w met_w = u3r_met(3, a); @@ -34,6 +34,6 @@ return u3m_bail(c3__exit); } else { - return u3qc_scalarmult_base(a); + return _cqee_scalarmult_base(a); } } diff --git a/pkg/urbit/jets/e/ed_sign.c b/pkg/urbit/jets/e/ed_sign.c index db59b1252..f26c04afd 100644 --- a/pkg/urbit/jets/e/ed_sign.c +++ b/pkg/urbit/jets/e/ed_sign.c @@ -6,7 +6,7 @@ /* functions */ - static u3_noun + static u3_atom _cqee_sign(u3_noun a, u3_noun b) { diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 7cc214aaa..a6f9a64e1 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -119,6 +119,13 @@ urcrypt_ed_add_double_scalarmult(uint8_t a[32], return 0; } +void +urcrypt_ed_puck(uint8_t seed[32], uint8_t out[32]) +{ + uint8_t secret[64]; + ed25519_create_keypair(out, secret, seed); +} + void urcrypt_ed_sign(uint8_t *message, size_t length, diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 62e0c279a..9ca52dd59 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -18,9 +18,10 @@ int urcrypt_ed_add_double_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t b_point[32], uint8_t out[32]); +void urcrypt_ed_puck(uint8_t seed[32], uint8_t out[32]); void urcrypt_ed_sign(uint8_t *message, size_t length, uint8_t seed[32], - uint8_t signature[64]); + uint8_t out[64]); #endif From 98d2ad9f08d4da54ad407889b1e1617be86e85f6 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 31 Jul 2020 16:56:39 -0700 Subject: [PATCH 10/75] urcrypt_ed_shar --- pkg/urbit/jets/e/ed_shar.c | 39 ++++++++++++++++++-------------------- pkg/urcrypt/urcrypt.c | 13 +++++++++++++ pkg/urcrypt/urcrypt.h | 1 + 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/pkg/urbit/jets/e/ed_shar.c b/pkg/urbit/jets/e/ed_shar.c index d67110699..0c57bc97b 100644 --- a/pkg/urbit/jets/e/ed_shar.c +++ b/pkg/urbit/jets/e/ed_shar.c @@ -2,33 +2,30 @@ ** */ #include "all.h" +#include -#include - - u3_noun - u3qee_shar(u3_atom pub, u3_atom sek) + static u3_atom + _cqee_shar(u3_atom pub, u3_atom sek) { - c3_y pub_y[32], sek_y[32], self_y[32], exp_y[64], shr_y[32]; - c3_w met_pub_w, met_sek_w; + c3_w pet_w, set_w; - met_pub_w = u3r_met(3, pub); - met_sek_w = u3r_met(3, sek); - - if ( (met_pub_w > 32) || (met_sek_w > 32) ) { + if ( (pet_w = u3r_met(3, pub)) > 32 ) { + // pub is not size checked in the hoon + return u3_none; + } + else if ( (set_w = u3r_met(3, sek)) > 32 ) { + // sek explicitly bails through suck return u3m_bail(c3__exit); } + else { + c3_y pub_y[32], sek_y[32], shr_y[32]; - u3r_bytes(0, 32, pub_y, pub); - u3r_bytes(0, 32, sek_y, sek); + u3r_bytes(0, 32, pub_y, pub); + u3r_bytes(0, 32, sek_y, sek); - memset(self_y, 0, 32); - memset(exp_y, 0, 64); - memset(shr_y, 0, 32); - - ed25519_create_keypair(self_y, exp_y, sek_y); - ed25519_key_exchange(shr_y, pub_y, exp_y); - - return u3i_bytes(32, shr_y); + urcrypt_ed_shar(pub_y, sek_y, shr_y); + return u3i_bytes(32, shr_y); + } } u3_noun @@ -42,6 +39,6 @@ { return u3m_bail(c3__exit); } else { - return u3qee_shar(pub, sek); + return _cqee_shar(pub, sek); } } diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index a6f9a64e1..4d7f9f875 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -126,6 +126,19 @@ urcrypt_ed_puck(uint8_t seed[32], uint8_t out[32]) ed25519_create_keypair(out, secret, seed); } +void +urcrypt_ed_shar(uint8_t public[32], uint8_t seed[32], uint8_t out[32]) +{ + uint8_t self[32], exp[64]; + + memset(self, 0, 32); + memset(exp, 0, 64); + memset(out, 0, 32); + + ed25519_create_keypair(self, exp, seed); + ed25519_key_exchange(out, public, exp); +} + void urcrypt_ed_sign(uint8_t *message, size_t length, diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 9ca52dd59..2d21bd74e 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -19,6 +19,7 @@ int urcrypt_ed_add_double_scalarmult(uint8_t a[32], uint8_t b_point[32], uint8_t out[32]); void urcrypt_ed_puck(uint8_t seed[32], uint8_t out[32]); +void urcrypt_ed_shar(uint8_t public[32], uint8_t seed[32], uint8_t out[32]); void urcrypt_ed_sign(uint8_t *message, size_t length, uint8_t seed[32], From 542bd55dc26a73200fbe77d8c15a157273bcee2a Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 31 Jul 2020 17:30:09 -0700 Subject: [PATCH 11/75] urcrypt_ed_veri --- pkg/urbit/jets/e/ed_shar.c | 2 ++ pkg/urbit/jets/e/ed_veri.c | 42 ++++++++++++++++++++------------------ pkg/urcrypt/urcrypt.c | 9 ++++++++ pkg/urcrypt/urcrypt.h | 3 +++ 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/pkg/urbit/jets/e/ed_shar.c b/pkg/urbit/jets/e/ed_shar.c index 0c57bc97b..5bf93a645 100644 --- a/pkg/urbit/jets/e/ed_shar.c +++ b/pkg/urbit/jets/e/ed_shar.c @@ -20,6 +20,8 @@ else { c3_y pub_y[32], sek_y[32], shr_y[32]; + memset(pub_y, 0, 32); + memset(sek_y, 0, 32); u3r_bytes(0, 32, pub_y, pub); u3r_bytes(0, 32, sek_y, sek); diff --git a/pkg/urbit/jets/e/ed_veri.c b/pkg/urbit/jets/e/ed_veri.c index 146ec54c6..634c95509 100644 --- a/pkg/urbit/jets/e/ed_veri.c +++ b/pkg/urbit/jets/e/ed_veri.c @@ -2,36 +2,38 @@ ** */ #include "all.h" - - -#include +#include /* functions */ - static u3_noun + static u3_atom _cqee_veri(u3_noun s, u3_noun m, u3_noun pk) { - c3_y sig_y[64]; - c3_y pub_y[32]; - c3_w ret; - c3_y* mes_y; + c3_w set_w, pek_w; - c3_w mesm_w = u3r_met(3, m); + if ( ((set_w = u3r_met(3, s)) > 64) || + ((pek_w = u3r_met(3, pk)) > 32) ) { + // hoon checks sizes, but weirdly and without crashes + return u3_none; + } + else { + c3_y sig_y[64], pub_y[32]; + c3_w met_w = u3r_met(3, m); + c3_y* mes_y = u3a_malloc(met_w); + c3_o ret_o; - memset(sig_y, 0, 64); - memset(pub_y, 0, 32); + memset(sig_y, 0, 64); + memset(pub_y, 0, 32); + u3r_bytes(0, 64, sig_y, s); + u3r_bytes(0, 32, pub_y, pk); + u3r_bytes(0, met_w, mes_y, m); - mes_y = u3a_malloc(mesm_w); - - u3r_bytes(0, 64, sig_y, s); - u3r_bytes(0, 32, pub_y, pk); - u3r_bytes(0, mesm_w, mes_y, m); - - ret = ed25519_verify(sig_y, mes_y, mesm_w, pub_y) == 1 ? c3y : c3n; - u3a_free(mes_y); - return ret; + ret_o = urcrypt_ed_veri(mes_y, met_w, pub_y, sig_y) ? c3y : c3n; + u3a_free(mes_y); + return ret_o; + } } u3_noun diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 4d7f9f875..c44cae36f 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -154,3 +154,12 @@ urcrypt_ed_sign(uint8_t *message, ed25519_create_keypair(public, secret, seed); ed25519_sign(out, message, length, public, secret); } + +bool +urcrypt_ed_veri(uint8_t *message, size_t length, + uint8_t public[32], uint8_t signature[64]) +{ + return ( ed25519_verify(signature, message, length, public) == 1 ) + ? true + : false; +} diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 2d21bd74e..3a15c0412 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -2,6 +2,7 @@ #define URCRYPT_H #include +#include #include #include #include @@ -25,4 +26,6 @@ void urcrypt_ed_sign(uint8_t *message, uint8_t seed[32], uint8_t out[64]); +bool urcrypt_ed_veri(uint8_t *message, size_t length, + uint8_t signature[64], uint8_t public[32]); #endif From 84c959313d35eecceb2cd4b745a103433a78e0f9 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 4 Aug 2020 13:10:30 -0700 Subject: [PATCH 12/75] begin move to u3r_unpack() (new function) for consistency in crypto jets combining the padding and length checking into one function helps make the crypto jets really boring and easy to scan, improving auditability. --- pkg/urbit/include/noun/retrieve.h | 9 +++++++ pkg/urbit/jets/e/ed_add_double_scalarmult.c | 29 +++++++-------------- pkg/urbit/noun/retrieve.c | 17 ++++++++++++ 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/pkg/urbit/include/noun/retrieve.h b/pkg/urbit/include/noun/retrieve.h index eed2bcc91..c17877d9d 100644 --- a/pkg/urbit/include/noun/retrieve.h +++ b/pkg/urbit/include/noun/retrieve.h @@ -346,6 +346,15 @@ c3_y* c_y, u3_atom d); + /* u3r_unpack(): + ** + ** Copy (len_w) bytes of (a) into (buf_y) if it fits, returning overage + */ + c3_w + u3r_unpack(c3_w len_w, + c3_y *buf_y, + u3_atom a); + /* u3r_chop(): ** ** Into the bloq space of `met`, from position `fum` for a diff --git a/pkg/urbit/jets/e/ed_add_double_scalarmult.c b/pkg/urbit/jets/e/ed_add_double_scalarmult.c index b177d826d..a89ed34d1 100644 --- a/pkg/urbit/jets/e/ed_add_double_scalarmult.c +++ b/pkg/urbit/jets/e/ed_add_double_scalarmult.c @@ -12,29 +12,18 @@ u3_atom c, u3_atom d) { - c3_w ate_w, bet_w, get_w, det_w; + c3_y a_y[32], b_y[32], c_y[32], d_y[32], out_y[32]; - if ( ((ate_w = u3r_met(3, a)) > 32) || - ((bet_w = u3r_met(3, b)) > 32) || - ((get_w = u3r_met(3, c)) > 32) || - ((det_w = u3r_met(3, d)) > 32) ) { - return u3_none; + if ( (0 == u3r_unpack(32, a_y, a)) && + (0 == u3r_unpack(32, b_y, b)) && + (0 == u3r_unpack(32, c_y, c)) && + (0 == u3r_unpack(32, d_y, d)) && + (0 == urcrypt_ed_add_double_scalarmult(a_y, b_y, c_y, d_y, out_y)) ) + { + return u3i_bytes(32, out_y); } else { - c3_y a_y[32], b_y[32], c_y[32], d_y[32], out_y[32]; - - memset(a_y, 0, 32); - memset(b_y, 0, 32); - memset(c_y, 0, 32); - memset(d_y, 0, 32); - u3r_bytes(0, ate_w, a_y, a); - u3r_bytes(0, bet_w, b_y, b); - u3r_bytes(0, get_w, c_y, c); - u3r_bytes(0, det_w, d_y, d); - - return ( 0 == urcrypt_ed_add_double_scalarmult(a_y, b_y, c_y, d_y, out_y) ) - ? u3i_bytes(32, out_y) - : u3_none; + return u3_none; } } diff --git a/pkg/urbit/noun/retrieve.c b/pkg/urbit/noun/retrieve.c index 63589ab1e..36fed5073 100644 --- a/pkg/urbit/noun/retrieve.c +++ b/pkg/urbit/noun/retrieve.c @@ -1076,6 +1076,23 @@ u3r_bytes(c3_w a_w, } } +/* u3r_unpack(): +** +** Copy (len_w) bytes of (a) into (buf_y) if it fits, returning overage +*/ +c3_w +u3r_unpack(c3_w len_w, c3_y *buf_y, u3_atom a) +{ + c3_w met_w = u3r_met(3, a); + if ( met_w <= len_w ) { + u3r_bytes(0, len_w, buf_y, a); + return 0; + } + else { + return len_w - met_w; + } +} + /* u3r_mp(): ** ** Copy (b) into (a_mp). From 4218308d040793021b277717c569a2bc9633f618 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 4 Aug 2020 13:42:10 -0700 Subject: [PATCH 13/75] more unpacks --- pkg/urbit/jets/e/ed_add_double_scalarmult.c | 3 +-- .../e/ed_add_scalarmult_scalarmult_base.c | 25 ++++++------------- pkg/urbit/jets/e/ed_point_add.c | 20 +++++---------- pkg/urbit/jets/e/ed_puck.c | 12 ++++----- pkg/urbit/jets/e/ed_scalarmult.c | 24 +++++------------- 5 files changed, 25 insertions(+), 59 deletions(-) diff --git a/pkg/urbit/jets/e/ed_add_double_scalarmult.c b/pkg/urbit/jets/e/ed_add_double_scalarmult.c index a89ed34d1..58b2a0781 100644 --- a/pkg/urbit/jets/e/ed_add_double_scalarmult.c +++ b/pkg/urbit/jets/e/ed_add_double_scalarmult.c @@ -18,8 +18,7 @@ (0 == u3r_unpack(32, b_y, b)) && (0 == u3r_unpack(32, c_y, c)) && (0 == u3r_unpack(32, d_y, d)) && - (0 == urcrypt_ed_add_double_scalarmult(a_y, b_y, c_y, d_y, out_y)) ) - { + (0 == urcrypt_ed_add_double_scalarmult(a_y, b_y, c_y, d_y, out_y)) ) { return u3i_bytes(32, out_y); } else { diff --git a/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c b/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c index 4ac8fc1ec..395fccd8f 100644 --- a/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c +++ b/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c @@ -11,27 +11,16 @@ u3_atom b, u3_atom c) { - c3_w ate_w, bet_w, get_w; + c3_y a_y[32], b_y[32], c_y[32], out_y[32]; - if ( ((ate_w = u3r_met(3, a)) > 32) || - ((bet_w = u3r_met(3, b)) > 32) || - ((get_w = u3r_met(3, c)) > 32) ) { - return u3_none; + if ( (0 == u3r_unpack(32, a_y, a)) && + (0 == u3r_unpack(32, b_y, b)) && + (0 == u3r_unpack(32, c_y, c)) && + (0 == urcrypt_ed_add_scalarmult_scalarmult_base(a_y, b_y, c_y, out_y)) ) { + return u3i_bytes(32, out_y); } else { - c3_y a_y[32], b_y[32], c_y[32], out_y[32]; - - memset(a_y, 0, 32); - memset(b_y, 0, 32); - memset(c_y, 0, 32); - u3r_bytes(0, ate_w, a_y, a); - u3r_bytes(0, bet_w, b_y, b); - u3r_bytes(0, get_w, c_y, c); - - return - ( 0 == urcrypt_ed_add_scalarmult_scalarmult_base(a_y, b_y, c_y, out_y) ) - ? u3i_bytes(32, out_y) - : u3_none; + return u3_none; } } diff --git a/pkg/urbit/jets/e/ed_point_add.c b/pkg/urbit/jets/e/ed_point_add.c index fe105ac6e..c21bdab0e 100644 --- a/pkg/urbit/jets/e/ed_point_add.c +++ b/pkg/urbit/jets/e/ed_point_add.c @@ -11,23 +11,15 @@ _cqee_point_add(u3_atom a, u3_atom b) { - c3_w ate_w, bet_w; + c3_y a_y[32], b_y[32], out_y[32]; - if ( ((ate_w = u3r_met(3, a)) > 32) || - ((bet_w = u3r_met(3, b)) > 32) ) { - return u3_none; + if ( (0 == u3r_unpack(32, a_y, a)) && + (0 == u3r_unpack(32, b_y, b)) && + (0 == urcrypt_ed_point_add(a_y, b_y, out_y)) ) { + return u3i_bytes(32, out_y); } else { - c3_y a_y[32], b_y[32], out_y[32]; - - memset(a_y, 0, 32); - memset(b_y, 0, 32); - u3r_bytes(0, ate_w, a_y, a); - u3r_bytes(0, bet_w, b_y, b); - - return ( 0 == urcrypt_ed_point_add(a_y, b_y, out_y) ) - ? u3i_bytes(32, out_y) - : u3_none; + return u3_none; } } diff --git a/pkg/urbit/jets/e/ed_puck.c b/pkg/urbit/jets/e/ed_puck.c index 8c6f3760f..5154df4e5 100644 --- a/pkg/urbit/jets/e/ed_puck.c +++ b/pkg/urbit/jets/e/ed_puck.c @@ -12,16 +12,14 @@ c3_y sed_y[32], pub_y[32]; c3_w met_w; - if ( (met_w = u3r_met(3, sed)) > 32 ) { + if ( 0 == u3r_unpack(32, sed_y, sed) ) { + urcrypt_ed_puck(sed_y, pub_y); + return u3i_bytes(32, pub_y); + } + else { // hoon explicitly crashes on mis-size return u3m_bail(c3__exit); } - - memset(sed_y, 0, 32); - u3r_bytes(0, met_w, sed_y, sed); - - urcrypt_ed_puck(sed_y, pub_y); - return u3i_bytes(32, pub_y); } u3_noun diff --git a/pkg/urbit/jets/e/ed_scalarmult.c b/pkg/urbit/jets/e/ed_scalarmult.c index bee52d958..96e0e757b 100644 --- a/pkg/urbit/jets/e/ed_scalarmult.c +++ b/pkg/urbit/jets/e/ed_scalarmult.c @@ -10,29 +10,17 @@ _cqee_scalarmult(u3_atom a, u3_atom b) { - c3_w ate_w, bet_w; c3_y a_y[32], b_y[32], out_y[32]; - if ( (ate_w = u3r_met(3, a)) > 32 ) { + if ( (0 == u3r_unpack(32, a_y, a)) && + (0 == u3r_unpack(32, b_y, b)) && + (0 == urcrypt_ed_scalarmult(a_y, b_y, out_y)) ) { + return u3i_bytes(32, out_y); + } + else { // hoon does not check size of inputs return u3_none; } - - if ( (bet_w = u3r_met(3, b)) > 32 ) { - return u3_none; - } - - memset(a_y, 0, 32); - memset(b_y, 0, 32); - u3r_bytes(0, ate_w, a_y, a); - u3r_bytes(0, bet_w, b_y, b); - - if ( 0 != urcrypt_ed_scalarmult(a_y, b_y, out_y) ) { - // this is unlikely to happen, but there is a return code. - return u3_none; - } - - return u3i_bytes(32, out_y); } u3_noun From c8714ebc54fa61fa8ad089baa7fc009ea6511568 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 4 Aug 2020 16:58:38 -0700 Subject: [PATCH 14/75] rest of the unpacks --- pkg/urbit/jets/e/ed_add_double_scalarmult.c | 14 ++++++------ .../e/ed_add_scalarmult_scalarmult_base.c | 12 +++++----- pkg/urbit/jets/e/ed_point_add.c | 10 ++++----- pkg/urbit/jets/e/ed_puck.c | 14 ++++++------ pkg/urbit/jets/e/ed_scalarmult.c | 12 +++++----- pkg/urbit/jets/e/ed_scalarmult_base.c | 10 +++------ pkg/urbit/jets/e/ed_shar.c | 14 ++++-------- pkg/urbit/jets/e/ed_sign.c | 20 ++++++++--------- pkg/urbit/jets/e/ed_veri.c | 22 ++++++++----------- 9 files changed, 56 insertions(+), 72 deletions(-) diff --git a/pkg/urbit/jets/e/ed_add_double_scalarmult.c b/pkg/urbit/jets/e/ed_add_double_scalarmult.c index 58b2a0781..e49a0bf16 100644 --- a/pkg/urbit/jets/e/ed_add_double_scalarmult.c +++ b/pkg/urbit/jets/e/ed_add_double_scalarmult.c @@ -14,15 +14,15 @@ { c3_y a_y[32], b_y[32], c_y[32], d_y[32], out_y[32]; - if ( (0 == u3r_unpack(32, a_y, a)) && - (0 == u3r_unpack(32, b_y, b)) && - (0 == u3r_unpack(32, c_y, c)) && - (0 == u3r_unpack(32, d_y, d)) && - (0 == urcrypt_ed_add_double_scalarmult(a_y, b_y, c_y, d_y, out_y)) ) { - return u3i_bytes(32, out_y); + if ( (0 != u3r_unpack(32, a_y, a)) || + (0 != u3r_unpack(32, b_y, b)) || + (0 != u3r_unpack(32, c_y, c)) || + (0 != u3r_unpack(32, d_y, d)) || + (0 != urcrypt_ed_add_double_scalarmult(a_y, b_y, c_y, d_y, out_y)) ) { + return u3_none; } else { - return u3_none; + return u3i_bytes(32, out_y); } } diff --git a/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c b/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c index 395fccd8f..0a0e2d2d4 100644 --- a/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c +++ b/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c @@ -13,14 +13,14 @@ { c3_y a_y[32], b_y[32], c_y[32], out_y[32]; - if ( (0 == u3r_unpack(32, a_y, a)) && - (0 == u3r_unpack(32, b_y, b)) && - (0 == u3r_unpack(32, c_y, c)) && - (0 == urcrypt_ed_add_scalarmult_scalarmult_base(a_y, b_y, c_y, out_y)) ) { - return u3i_bytes(32, out_y); + if ( (0 != u3r_unpack(32, a_y, a)) || + (0 != u3r_unpack(32, b_y, b)) || + (0 != u3r_unpack(32, c_y, c)) || + (0 != urcrypt_ed_add_scalarmult_scalarmult_base(a_y, b_y, c_y, out_y)) ) { + return u3_none; } else { - return u3_none; + return u3i_bytes(32, out_y); } } diff --git a/pkg/urbit/jets/e/ed_point_add.c b/pkg/urbit/jets/e/ed_point_add.c index c21bdab0e..a93f798b3 100644 --- a/pkg/urbit/jets/e/ed_point_add.c +++ b/pkg/urbit/jets/e/ed_point_add.c @@ -13,13 +13,13 @@ { c3_y a_y[32], b_y[32], out_y[32]; - if ( (0 == u3r_unpack(32, a_y, a)) && - (0 == u3r_unpack(32, b_y, b)) && - (0 == urcrypt_ed_point_add(a_y, b_y, out_y)) ) { - return u3i_bytes(32, out_y); + if ( (0 != u3r_unpack(32, a_y, a)) || + (0 != u3r_unpack(32, b_y, b)) || + (0 != urcrypt_ed_point_add(a_y, b_y, out_y)) ) { + return u3_none; } else { - return u3_none; + return u3i_bytes(32, out_y); } } diff --git a/pkg/urbit/jets/e/ed_puck.c b/pkg/urbit/jets/e/ed_puck.c index 5154df4e5..8257b937e 100644 --- a/pkg/urbit/jets/e/ed_puck.c +++ b/pkg/urbit/jets/e/ed_puck.c @@ -9,17 +9,17 @@ static u3_atom _cqee_puck(u3_atom sed) { - c3_y sed_y[32], pub_y[32]; - c3_w met_w; + c3_y sed_y[32]; - if ( 0 == u3r_unpack(32, sed_y, sed) ) { - urcrypt_ed_puck(sed_y, pub_y); - return u3i_bytes(32, pub_y); - } - else { + if ( 0 != u3r_unpack(32, sed_y, sed) ) { // hoon explicitly crashes on mis-size return u3m_bail(c3__exit); } + else { + c3_y pub_y[32]; + urcrypt_ed_puck(sed_y, pub_y); + return u3i_bytes(32, pub_y); + } } u3_noun diff --git a/pkg/urbit/jets/e/ed_scalarmult.c b/pkg/urbit/jets/e/ed_scalarmult.c index 96e0e757b..224fba3f6 100644 --- a/pkg/urbit/jets/e/ed_scalarmult.c +++ b/pkg/urbit/jets/e/ed_scalarmult.c @@ -12,15 +12,15 @@ { c3_y a_y[32], b_y[32], out_y[32]; - if ( (0 == u3r_unpack(32, a_y, a)) && - (0 == u3r_unpack(32, b_y, b)) && - (0 == urcrypt_ed_scalarmult(a_y, b_y, out_y)) ) { - return u3i_bytes(32, out_y); - } - else { + if ( (0 != u3r_unpack(32, a_y, a)) || + (0 != u3r_unpack(32, b_y, b)) || + (0 != urcrypt_ed_scalarmult(a_y, b_y, out_y)) ) { // hoon does not check size of inputs return u3_none; } + else { + return u3i_bytes(32, out_y); + } } u3_noun diff --git a/pkg/urbit/jets/e/ed_scalarmult_base.c b/pkg/urbit/jets/e/ed_scalarmult_base.c index fb0d2f813..c1b641bd9 100644 --- a/pkg/urbit/jets/e/ed_scalarmult_base.c +++ b/pkg/urbit/jets/e/ed_scalarmult_base.c @@ -9,17 +9,13 @@ static u3_atom _cqee_scalarmult_base(u3_atom a) { - c3_w met_w = u3r_met(3, a); + c3_y a_y[32]; - if ( met_w > 32 ) { + if ( 0 != u3r_unpack(32, a_y, a) ) { return u3_none; } else { - c3_y a_y[32], out_y[32]; - - memset(a_y, 0, 32); - u3r_bytes(0, met_w, a_y, a); - + c3_y out_y[32]; urcrypt_ed_scalarmult_base(a_y, out_y); return u3i_bytes(32, out_y); } diff --git a/pkg/urbit/jets/e/ed_shar.c b/pkg/urbit/jets/e/ed_shar.c index 5bf93a645..5ed28df37 100644 --- a/pkg/urbit/jets/e/ed_shar.c +++ b/pkg/urbit/jets/e/ed_shar.c @@ -7,24 +7,18 @@ static u3_atom _cqee_shar(u3_atom pub, u3_atom sek) { - c3_w pet_w, set_w; + c3_y pub_y[32], sek_y[32]; - if ( (pet_w = u3r_met(3, pub)) > 32 ) { + if ( 0 != u3r_unpack(32, pub_y, pub) ) { // pub is not size checked in the hoon return u3_none; } - else if ( (set_w = u3r_met(3, sek)) > 32 ) { + else if ( 0 != u3r_unpack(32, sek_y, sek) ) { // sek explicitly bails through suck return u3m_bail(c3__exit); } else { - c3_y pub_y[32], sek_y[32], shr_y[32]; - - memset(pub_y, 0, 32); - memset(sek_y, 0, 32); - u3r_bytes(0, 32, pub_y, pub); - u3r_bytes(0, 32, sek_y, sek); - + c3_y shr_y[32]; urcrypt_ed_shar(pub_y, sek_y, shr_y); return u3i_bytes(32, shr_y); } diff --git a/pkg/urbit/jets/e/ed_sign.c b/pkg/urbit/jets/e/ed_sign.c index f26c04afd..606282947 100644 --- a/pkg/urbit/jets/e/ed_sign.c +++ b/pkg/urbit/jets/e/ed_sign.c @@ -10,24 +10,22 @@ _cqee_sign(u3_noun a, u3_noun b) { - c3_w b_w = u3r_met(3, b); + c3_y sed_y[32]; - if ( b_w > 32 ) { + if ( 0 != u3r_unpack(32, sed_y, b) ) { // hoon calls suck, which calls puck, which crashes return u3m_bail(c3__exit); } else { - c3_w a_w = u3r_met(3, a); - c3_y* mes_y = u3a_malloc(a_w); - c3_y sed_y[32], sig_y[64]; - - memset(sed_y, 0, 32); - u3r_bytes(0, a_w, mes_y, a); - u3r_bytes(0, b_w, sed_y, b); - - urcrypt_ed_sign(mes_y, a_w, sed_y, sig_y); + c3_y sig_y[64]; + c3_y* mes_y; + c3_w met_w = u3r_met(3, a); + mes_y = u3a_malloc(met_w); + u3r_bytes(0, met_w, mes_y, a); + urcrypt_ed_sign(mes_y, met_w, sed_y, sig_y); u3a_free(mes_y); + return u3i_bytes(64, sig_y); } } diff --git a/pkg/urbit/jets/e/ed_veri.c b/pkg/urbit/jets/e/ed_veri.c index 634c95509..646dd89ca 100644 --- a/pkg/urbit/jets/e/ed_veri.c +++ b/pkg/urbit/jets/e/ed_veri.c @@ -11,28 +11,24 @@ u3_noun m, u3_noun pk) { - c3_w set_w, pek_w; + c3_y sig_y[64], pub_y[32]; - if ( ((set_w = u3r_met(3, s)) > 64) || - ((pek_w = u3r_met(3, pk)) > 32) ) { + if ( (0 != u3r_unpack(64, sig_y, s)) || + (0 != u3r_unpack(32, pub_y, pk)) ) { // hoon checks sizes, but weirdly and without crashes return u3_none; } else { - c3_y sig_y[64], pub_y[32]; + c3_t val_t; + c3_y* mes_y; c3_w met_w = u3r_met(3, m); - c3_y* mes_y = u3a_malloc(met_w); - c3_o ret_o; - memset(sig_y, 0, 64); - memset(pub_y, 0, 32); - u3r_bytes(0, 64, sig_y, s); - u3r_bytes(0, 32, pub_y, pk); + mes_y = u3a_malloc(met_w); u3r_bytes(0, met_w, mes_y, m); - - ret_o = urcrypt_ed_veri(mes_y, met_w, pub_y, sig_y) ? c3y : c3n; + val_t = urcrypt_ed_veri(mes_y, met_w, pub_y, sig_y); u3a_free(mes_y); - return ret_o; + + return val_t ? c3y : c3n; } } From cd4b8cb9b05af84cc5649f12fc3c634046c02437 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Wed, 5 Aug 2020 16:06:04 -0700 Subject: [PATCH 15/75] aes_ecb -> urcrypt --- nix/pkgs/urcrypt/default.nix | 2 +- pkg/urbit/jets/e/aes_ecb.c | 327 ++++++----------------------------- pkg/urcrypt/Makefile | 2 +- pkg/urcrypt/urcrypt.c | 122 +++++++++++++ pkg/urcrypt/urcrypt.h | 11 ++ 5 files changed, 191 insertions(+), 273 deletions(-) diff --git a/nix/pkgs/urcrypt/default.nix b/nix/pkgs/urcrypt/default.nix index 4367c6597..592e46cfe 100644 --- a/nix/pkgs/urcrypt/default.nix +++ b/nix/pkgs/urcrypt/default.nix @@ -5,5 +5,5 @@ pkgs.stdenv.mkDerivation rec { builder = ./builder.sh; src = ../../../pkg/urcrypt; - buildInputs = [ ed25519 ge-additions ]; + buildInputs = [ pkgs.openssl ed25519 ge-additions ]; } diff --git a/pkg/urbit/jets/e/aes_ecb.c b/pkg/urbit/jets/e/aes_ecb.c index be7d12008..3aae07d54 100644 --- a/pkg/urbit/jets/e/aes_ecb.c +++ b/pkg/urbit/jets/e/aes_ecb.c @@ -2,64 +2,26 @@ ** */ #include "all.h" - -#include - -#include "aes_siv.h" +#include /* functions */ - u3_noun - u3qea_ecba_en(u3_atom key, + + static u3_atom + _cqea_ecba_en(u3_atom key, u3_atom blk) { - c3_y key_y[16]; - c3_y blk_y[16]; - AES_KEY key_u; + c3_y key_y[16], blk_y[16], out_y[16]; - c3_assert(u3r_met(3, key) <= 16); - c3_assert(u3r_met(3, blk) <= 16); + u3r_bytes(0, 16, key_y, key); + u3r_bytes(0, 16, blk_y, blk); - { - int i = 15; - - do { - key_y[i] = u3r_byte(15-i, key); - i--; - } while (i >= 0); - } - { - int i = 15; - - do { - blk_y[i] = u3r_byte(15-i, blk); - i--; - } while (i >= 0); - } - - if ( 0 != AES_set_encrypt_key(key_y, 128, &key_u) ) { - return u3m_bail(c3__exit); + if ( 0 != urcrypt_aes_ecba_en(key_y, blk_y, out_y) ) { + return u3_none; } else { - AES_ecb_encrypt(blk_y, blk_y, &key_u, AES_ENCRYPT); + return u3i_bytes(16, out_y); } - - /* array reverse - we can write backwards u3i_bytes * - * in the unlikely event that this becomes a problem */ - { - int i = 15; - int j = 0; - c3_y tmp; - - do { - tmp = blk_y[i]; - blk_y[i] = blk_y[j]; - blk_y[j] = tmp; - i--; j++; - } while (i > j); - } - - return u3i_bytes(16, blk_y); } u3_noun @@ -72,60 +34,25 @@ c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return u3qea_ecba_en(a, b); + return _cqea_ecba_en(a, b); } } - u3_noun - u3qea_ecba_de(u3_atom key, + static u3_atom + _cqea_ecba_de(u3_atom key, u3_atom blk) { - c3_y key_y[16]; - c3_y blk_y[16]; - AES_KEY key_u; + c3_y key_y[16], blk_y[16], out_y[16]; - c3_assert(u3r_met(3, key) <= 16); - c3_assert(u3r_met(3, blk) <= 16); + u3r_bytes(0, 16, key_y, key); + u3r_bytes(0, 16, blk_y, blk); - { - int i = 15; - - do { - key_y[i] = u3r_byte(15-i, key); - i--; - } while (i >= 0); - } - { - int i = 15; - - do { - blk_y[i] = u3r_byte(15-i, blk); - i--; - } while (i >= 0); - } - - if ( 0 != AES_set_decrypt_key(key_y, 128, &key_u) ) { - return u3m_bail(c3__exit); + if ( 0 != urcrypt_aes_ecba_de(key_y, blk_y, out_y) ) { + return u3_none; } else { - AES_ecb_encrypt(blk_y, blk_y, &key_u, AES_DECRYPT); + return u3i_bytes(16, out_y); } - - /* array reverse - we can write backwards u3i_bytes * - * in the unlikely event that this becomes a problem */ - { - int i = 15; - int j = 0; - c3_y tmp; - - do { - tmp = blk_y[i]; - blk_y[i] = blk_y[j]; - blk_y[j] = tmp; - i--; j++; - } while (i > j); - } - return u3i_bytes(16, blk_y); } u3_noun @@ -138,61 +65,25 @@ c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return u3qea_ecba_de(a, b); + return _cqea_ecba_de(a, b); } } - u3_noun - u3qea_ecbb_en(u3_atom key, + static u3_atom + _cqea_ecbb_en(u3_atom key, u3_atom blk) { - c3_y key_y[24]; - c3_y blk_y[16]; - AES_KEY key_u; + c3_y key_y[24], blk_y[16], out_y[16]; - c3_assert(u3r_met(3, key) <= 24); - c3_assert(u3r_met(3, blk) <= 16); + u3r_bytes(0, 24, key_y, key); + u3r_bytes(0, 16, blk_y, blk); - { - int i = 23; - - do { - key_y[i] = u3r_byte(23-i, key); - i--; - } while (i >= 0); - } - { - int i = 15; - - do { - blk_y[i] = u3r_byte(15-i, blk); - i--; - } while (i >= 0); - } - - if ( 0 != AES_set_encrypt_key(key_y, 192, &key_u) ) { - return u3m_bail(c3__exit); + if ( 0 != urcrypt_aes_ecbb_en(key_y, blk_y, out_y) ) { + return u3_none; } else { - AES_ecb_encrypt(blk_y, blk_y, &key_u, AES_ENCRYPT); + return u3i_bytes(16, out_y); } - - /* array reverse - we can write backwards u3i_bytes * - * in the unlikely event that this becomes a problem */ - { - int i = 15; - int j = 0; - c3_y tmp; - - do { - tmp = blk_y[i]; - blk_y[i] = blk_y[j]; - blk_y[j] = tmp; - i--; j++; - } while (i > j); - } - - return u3i_bytes(16, blk_y); } u3_noun @@ -205,60 +96,25 @@ c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return u3qea_ecbb_en(a, b); + return _cqea_ecbb_en(a, b); } } - u3_noun - u3qea_ecbb_de(u3_atom key, + static u3_atom + _cqea_ecbb_de(u3_atom key, u3_atom blk) { - c3_y key_y[24]; - c3_y blk_y[16]; - AES_KEY key_u; + c3_y key_y[24], blk_y[16], out_y[16]; - c3_assert(u3r_met(3, key) <= 24); - c3_assert(u3r_met(3, blk) <= 16); + u3r_bytes(0, 24, key_y, key); + u3r_bytes(0, 16, blk_y, blk); - { - int i = 23; - - do { - key_y[i] = u3r_byte(23-i, key); - i--; - } while (i >= 0); - } - { - int i = 15; - - do { - blk_y[i] = u3r_byte(15-i, blk); - i--; - } while (i >= 0); - } - - if ( 0 != AES_set_decrypt_key(key_y, 192, &key_u) ) { - return u3m_bail(c3__exit); + if ( 0 != urcrypt_aes_ecbb_de(key_y, blk_y, out_y) ) { + return u3_none; } else { - AES_ecb_encrypt(blk_y, blk_y, &key_u, AES_DECRYPT); + return u3i_bytes(16, out_y); } - - /* array reverse - we can write backwards u3i_bytes * - * in the unlikely event that this becomes a problem */ - { - int i = 15; - int j = 0; - c3_y tmp; - - do { - tmp = blk_y[i]; - blk_y[i] = blk_y[j]; - blk_y[j] = tmp; - i--; j++; - } while (i > j); - } - return u3i_bytes(16, blk_y); } u3_noun @@ -271,61 +127,25 @@ c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return u3qea_ecbb_de(a, b); + return _cqea_ecbb_de(a, b); } } - u3_noun - u3qea_ecbc_en(u3_atom key, + static u3_atom + _cqea_ecbc_en(u3_atom key, u3_atom blk) { - c3_y key_y[32]; - c3_y blk_y[16]; - AES_KEY key_u; + c3_y key_y[32], blk_y[16], out_y[16]; - c3_assert(u3r_met(3, key) <= 32); - c3_assert(u3r_met(3, blk) <= 16); + u3r_bytes(0, 32, key_y, key); + u3r_bytes(0, 16, blk_y, blk); - { - int i = 31; - - do { - key_y[i] = u3r_byte(31-i, key); - i--; - } while (i >= 0); - } - { - int i = 15; - - do { - blk_y[i] = u3r_byte(15-i, blk); - i--; - } while (i >= 0); - } - - if ( 0 != AES_set_encrypt_key(key_y, 256, &key_u) ) { - return u3m_bail(c3__exit); + if ( 0 != urcrypt_aes_ecbc_en(key_y, blk_y, out_y) ) { + return u3_none; } else { - AES_ecb_encrypt(blk_y, blk_y, &key_u, AES_ENCRYPT); + return u3i_bytes(16, out_y); } - - /* array reverse - we can write backwards u3i_bytes * - * in the unlikely event that this becomes a problem */ - { - int i = 15; - int j = 0; - c3_y tmp; - - do { - tmp = blk_y[i]; - blk_y[i] = blk_y[j]; - blk_y[j] = tmp; - i--; j++; - } while (i > j); - } - - return u3i_bytes(16, blk_y); } u3_noun @@ -338,60 +158,25 @@ c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return u3qea_ecbc_en(a, b); + return _cqea_ecbc_en(a, b); } } - u3_noun - u3qea_ecbc_de(u3_atom key, + static u3_atom + _cqea_ecbc_de(u3_atom key, u3_atom blk) { - c3_y key_y[32]; - c3_y blk_y[16]; - AES_KEY key_u; + c3_y key_y[32], blk_y[16], out_y[16]; - c3_assert(u3r_met(3, key) <= 32); - c3_assert(u3r_met(3, blk) <= 16); + u3r_bytes(0, 32, key_y, key); + u3r_bytes(0, 16, blk_y, blk); - { - int i = 31; - - do { - key_y[i] = u3r_byte(31-i, key); - i--; - } while (i >= 0); - } - { - int i = 15; - - do { - blk_y[i] = u3r_byte(15-i, blk); - i--; - } while (i >= 0); - } - - if ( 0 != AES_set_decrypt_key(key_y, 256, &key_u) ) { - return u3m_bail(c3__exit); + if ( 0 != urcrypt_aes_ecbc_de(key_y, blk_y, out_y) ) { + return u3_none; } else { - AES_ecb_encrypt(blk_y, blk_y, &key_u, AES_DECRYPT); + return u3i_bytes(16, out_y); } - - /* array reverse - we can write backwards u3i_bytes * - * in the unlikely event that this becomes a problem */ - { - int i = 15; - int j = 0; - c3_y tmp; - - do { - tmp = blk_y[i]; - blk_y[i] = blk_y[j]; - blk_y[j] = tmp; - i--; j++; - } while (i > j); - } - return u3i_bytes(16, blk_y); } u3_noun @@ -404,6 +189,6 @@ c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return u3qea_ecbc_de(a, b); + return _cqea_ecbc_de(a, b); } } diff --git a/pkg/urcrypt/Makefile b/pkg/urcrypt/Makefile index 6c283fcc3..7b2a0ecc3 100644 --- a/pkg/urcrypt/Makefile +++ b/pkg/urcrypt/Makefile @@ -16,7 +16,7 @@ liburcrypt.a: $(SOURCES) liburcrypt.so: $(SOURCES) $(CC) $(CFLAGS) -fPIC -c urcrypt.c -o urcrypt-shared.o $(CC) -shared urcrypt-shared.o -o liburcrypt.so \ - -led25519 -lge-additions \ + -led25519 -lge-additions -lssl \ -Wl,--no-undefined all: liburcrypt.a liburcrypt.so diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index c44cae36f..5da4383f4 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -163,3 +163,125 @@ urcrypt_ed_veri(uint8_t *message, size_t length, ? true : false; } + +static void +reverse_bytes(size_t size, uint8_t *in, uint8_t *out) { + size_t i, j; + for ( i = 0, j = size - 1; i < size; i++, j-- ) { + out[i] = in[j]; + } +} + +int +urcrypt_aes_ecba_en(uint8_t key[16], uint8_t block[16], uint8_t out[16]) +{ + AES_KEY aes_key; + uint8_t rkey[16], rblock[16], rout[16]; + + reverse_bytes(16, key, rkey); + reverse_bytes(16, block, rblock); + + if ( 0 != AES_set_encrypt_key(rkey, 128, &aes_key) ) { + return -1; + } + else { + AES_ecb_encrypt(rblock, rout, &aes_key, AES_ENCRYPT); + reverse_bytes(16, rout, out); + return 0; + } +} + +int +urcrypt_aes_ecba_de(uint8_t key[16], uint8_t block[16], uint8_t out[16]) +{ + AES_KEY aes_key; + uint8_t rkey[16], rblock[16], rout[16]; + + reverse_bytes(16, key, rkey); + reverse_bytes(16, block, rblock); + + if ( 0 != AES_set_decrypt_key(rkey, 128, &aes_key) ) { + return -1; + } + else { + AES_ecb_encrypt(rblock, rout, &aes_key, AES_DECRYPT); + reverse_bytes(16, rout, out); + return 0; + } +} + +int +urcrypt_aes_ecbb_en(uint8_t key[24], uint8_t block[16], uint8_t out[16]) +{ + AES_KEY aes_key; + uint8_t rkey[24], rblock[16], rout[16]; + + reverse_bytes(24, key, rkey); + reverse_bytes(16, block, rblock); + + if ( 0 != AES_set_encrypt_key(rkey, 192, &aes_key) ) { + return -1; + } + else { + AES_ecb_encrypt(rblock, rout, &aes_key, AES_ENCRYPT); + reverse_bytes(16, rout, out); + return 0; + } +} + +int +urcrypt_aes_ecbb_de(uint8_t key[24], uint8_t block[16], uint8_t out[16]) +{ + AES_KEY aes_key; + uint8_t rkey[24], rblock[16], rout[16]; + + reverse_bytes(24, key, rkey); + reverse_bytes(16, block, rblock); + + if ( 0 != AES_set_decrypt_key(rkey, 192, &aes_key) ) { + return -1; + } + else { + AES_ecb_encrypt(rblock, rout, &aes_key, AES_DECRYPT); + reverse_bytes(16, rout, out); + return 0; + } +} + +int +urcrypt_aes_ecbc_en(uint8_t key[32], uint8_t block[16], uint8_t out[16]) +{ + AES_KEY aes_key; + uint8_t rkey[32], rblock[16], rout[16]; + + reverse_bytes(32, key, rkey); + reverse_bytes(16, block, rblock); + + if ( 0 != AES_set_encrypt_key(rkey, 256, &aes_key) ) { + return -1; + } + else { + AES_ecb_encrypt(rblock, rout, &aes_key, AES_ENCRYPT); + reverse_bytes(16, rout, out); + return 0; + } +} + +int +urcrypt_aes_ecbc_de(uint8_t key[32], uint8_t block[16], uint8_t out[16]) +{ + AES_KEY aes_key; + uint8_t rkey[32], rblock[16], rout[16]; + + reverse_bytes(32, key, rkey); + reverse_bytes(16, block, rblock); + + if ( 0 != AES_set_decrypt_key(rkey, 256, &aes_key) ) { + return -1; + } + else { + AES_ecb_encrypt(rblock, rout, &aes_key, AES_DECRYPT); + reverse_bytes(16, rout, out); + return 0; + } +} diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 3a15c0412..ee4f68732 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -4,9 +4,12 @@ #include #include #include + #include #include +#include + int urcrypt_ed_point_add(uint8_t a[32], uint8_t b[32], uint8_t out[32]); int urcrypt_ed_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t out[32]); void urcrypt_ed_scalarmult_base(uint8_t a[32], uint8_t out[32]); @@ -19,6 +22,7 @@ int urcrypt_ed_add_double_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t b_point[32], uint8_t out[32]); + void urcrypt_ed_puck(uint8_t seed[32], uint8_t out[32]); void urcrypt_ed_shar(uint8_t public[32], uint8_t seed[32], uint8_t out[32]); void urcrypt_ed_sign(uint8_t *message, @@ -28,4 +32,11 @@ void urcrypt_ed_sign(uint8_t *message, bool urcrypt_ed_veri(uint8_t *message, size_t length, uint8_t signature[64], uint8_t public[32]); + +int urcrypt_aes_ecba_en(uint8_t key[16], uint8_t block[16], uint8_t out[16]); +int urcrypt_aes_ecba_de(uint8_t key[16], uint8_t block[16], uint8_t out[16]); +int urcrypt_aes_ecbb_en(uint8_t key[24], uint8_t block[16], uint8_t out[16]); +int urcrypt_aes_ecbb_de(uint8_t key[24], uint8_t block[16], uint8_t out[16]); +int urcrypt_aes_ecbc_en(uint8_t key[32], uint8_t block[16], uint8_t out[16]); +int urcrypt_aes_ecbc_de(uint8_t key[32], uint8_t block[16], uint8_t out[16]); #endif From 6e065b6979c618a0ce8e689a33e695f1f01cb35a Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 7 Aug 2020 12:28:50 -0700 Subject: [PATCH 16/75] const qualifiers, inplace reverse, aes_cbca --- pkg/urbit/jets/e/aes_cbc.c | 89 ++++---------- pkg/urbit/jets/e/aes_ecb.c | 3 + pkg/urbit/noun/manage.c | 7 +- pkg/urcrypt/urcrypt.c | 242 +++++++++++++++++++++++++++++-------- pkg/urcrypt/urcrypt.h | 85 +++++++++---- 5 files changed, 281 insertions(+), 145 deletions(-) diff --git a/pkg/urbit/jets/e/aes_cbc.c b/pkg/urbit/jets/e/aes_cbc.c index 81233e506..e1604188a 100644 --- a/pkg/urbit/jets/e/aes_cbc.c +++ b/pkg/urbit/jets/e/aes_cbc.c @@ -2,85 +2,42 @@ ** */ #include "all.h" +#include #include +/* All of the CBC hoon truncates its key and prv inputs by passing them to + * the ECB functions, which truncate them, hence the raw u3r_bytes unpacking. + */ + /* functions */ - u3_noun - u3qea_cbca_en(u3_atom key, + static u3_atom + _cqea_cbca_en(u3_atom key, u3_atom iv, u3_atom msg) { - c3_y key_y[16]; - c3_y iv_y[16]; - c3_w len_msg_w; - c3_w len_out_w; - c3_y *msg_y; - c3_y *out_y; - u3_atom out; - AES_KEY key_u; + c3_y key_y[16], iv_y[16], *msg_y, *out_y; + c3_w met_w; + size_t len; - c3_assert(u3r_met(3, key) <= 16); - c3_assert(u3r_met(3, iv) <= 16); - len_msg_w = u3r_met(3, msg); - len_out_w = (len_msg_w % 16) == 0 ? len_msg_w : len_msg_w + (16 - (len_msg_w % 16)); - len_msg_w = len_out_w; + u3r_bytes(0, 16, key_y, key); + u3r_bytes(0, 16, iv_y, iv); - msg_y = u3a_malloc(len_msg_w); - out_y = u3a_malloc(len_out_w); + met_w = u3r_met(3, msg); + msg_y = u3a_malloc(met_w); + u3r_bytes(0, met_w, msg_y, msg); + out_y = urcrypt_aes_cbca_en(msg_y, met_w, key_y, iv_y, &len); + u3a_free(msg_y); - { - int i = 15; - - do { - key_y[i] = u3r_byte(15-i, key); - i--; - } while (i >= 0); - } - { - int i = 15; - - do { - iv_y[i] = u3r_byte(15-i, iv); - i--; - } while (i >= 0); - } - { - int i = len_msg_w - 1; - - do { - msg_y[i] = u3r_byte((len_msg_w - 1)-i, msg); - i--; - } while (i >= 0); - } - - if ( 0 != AES_set_encrypt_key(key_y, 128, &key_u) ) { - return u3m_bail(c3__exit); + if ( NULL == out_y ) { + return u3_none; } else { - AES_cbc_encrypt(msg_y, out_y, len_msg_w, &key_u, iv_y, AES_ENCRYPT); + u3_atom ret = u3i_bytes(len, out_y); + urcrypt_free(out_y); + return ret; } - - /* array reverse - we can write backwards u3i_bytes * - * in the unlikely event that this becomes a problem */ - { - int i = len_out_w - 1; - int j = 0; - c3_y tmp; - - do { - tmp = out_y[i]; - out_y[i] = out_y[j]; - out_y[j] = tmp; - i--; j++; - } while (i > j); - } - - out = u3i_bytes(len_out_w, out_y); - u3a_free(msg_y); - u3a_free(out_y); - return out; } u3_noun @@ -93,7 +50,7 @@ c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return u3qea_cbca_en(a, b, c); + return _cqea_cbca_en(a, b, c); } } diff --git a/pkg/urbit/jets/e/aes_ecb.c b/pkg/urbit/jets/e/aes_ecb.c index 3aae07d54..8ca616d4a 100644 --- a/pkg/urbit/jets/e/aes_ecb.c +++ b/pkg/urbit/jets/e/aes_ecb.c @@ -7,6 +7,9 @@ /* functions */ + /* All of the ECB hoon truncates its key and blk inputs with +fe, in these + * jets we unpack with an unconditional u3r_bytes */ + static u3_atom _cqea_ecba_en(u3_atom key, u3_atom blk) diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index c83d00605..86601968e 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include "all.h" #include "vere/vere.h" @@ -1656,10 +1656,9 @@ u3m_boot(c3_c* dir_c) */ u3m_init(); - /* In the worker, set the openssl memory allocation functions to always - ** work on the loom. + /* Initialize cryptography suite with loom allocation functions. */ - CRYPTO_set_mem_functions(u3a_malloc_ssl, u3a_realloc_ssl, u3a_free_ssl); + urcrypt_init(&u3a_malloc, &u3a_realloc, &u3a_free); /* Activate the storage system. */ diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 5da4383f4..9c973a228 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -1,7 +1,76 @@ #include "urcrypt.h" +static bool initialized = false; +static urcrypt_malloc_t urcrypt_malloc_ptr; +static urcrypt_realloc_t urcrypt_realloc_ptr; +static urcrypt_free_t urcrypt_free_ptr; + +void* urcrypt_malloc(size_t len) +{ + return (*urcrypt_malloc_ptr)(len); +} + +void* urcrypt_realloc(void *ptr, size_t len) +{ + return (*urcrypt_realloc_ptr)(ptr, len); +} + +void urcrypt_free(void *ptr) +{ + (*urcrypt_free_ptr)(ptr); +} + +static void* +urcrypt_malloc_ssl(size_t len +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + , const char* file, int line +#endif +) { return urcrypt_malloc(len); } + +static void* +urcrypt_realloc_ssl(void* ptr, size_t len +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + , const char* file, int line +#endif +) { return urcrypt_realloc(ptr, len); } + +static void +urcrypt_free_ssl(void* ptr +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + , const char* file, int line +#endif +) { urcrypt_free(ptr); } + +/* IMPORTANT: it is an error (undefined behavior) to call functions in + * this library without first calling urcrypt_init() exactly once. + */ int -urcrypt_ed_point_add(uint8_t a[32], uint8_t b[32], uint8_t out[32]) +urcrypt_init(urcrypt_malloc_t m, urcrypt_realloc_t r, urcrypt_free_t f) +{ + if ( initialized ) { + return -1; + } + else { + initialized = true; + urcrypt_malloc_ptr = ( NULL == m ) ? &malloc : m; + urcrypt_realloc_ptr = ( NULL == r ) ? &realloc : r; + urcrypt_free_ptr = ( NULL == f ) ? &free : f; + + if ( CRYPTO_set_mem_functions(&urcrypt_malloc_ssl, + &urcrypt_realloc_ssl, + &urcrypt_free_ssl) ) { + return 0; + } + else { + return -2; + } + } +} + +int +urcrypt_ed_point_add(const uint8_t a[32], + const uint8_t b[32], + uint8_t out[32]) { ge_p3 A, B; ge_cached b_cached; @@ -32,7 +101,9 @@ urcrypt_ed_point_add(uint8_t a[32], uint8_t b[32], uint8_t out[32]) } int -urcrypt_ed_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t out[32]) +urcrypt_ed_scalarmult(const uint8_t a[32], + const uint8_t b[32], + uint8_t out[32]) { ge_p3 B, result; @@ -50,7 +121,8 @@ urcrypt_ed_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t out[32]) } void -urcrypt_ed_scalarmult_base(uint8_t a[32], uint8_t out[32]) +urcrypt_ed_scalarmult_base(const uint8_t a[32], + uint8_t out[32]) { ge_p3 R; ge_scalarmult_base(&R, a); @@ -58,9 +130,9 @@ urcrypt_ed_scalarmult_base(uint8_t a[32], uint8_t out[32]) } int -urcrypt_ed_add_scalarmult_scalarmult_base(uint8_t a[32], - uint8_t a_point[32], - uint8_t b[32], +urcrypt_ed_add_scalarmult_scalarmult_base(const uint8_t a[32], + const uint8_t a_point[32], + const uint8_t b[32], uint8_t out[32]) { ge_p2 r; @@ -81,10 +153,10 @@ urcrypt_ed_add_scalarmult_scalarmult_base(uint8_t a[32], } int -urcrypt_ed_add_double_scalarmult(uint8_t a[32], - uint8_t a_point[32], - uint8_t b[32], - uint8_t b_point[32], +urcrypt_ed_add_double_scalarmult(const uint8_t a[32], + const uint8_t a_point[32], + const uint8_t b[32], + const uint8_t b_point[32], uint8_t out[32]) { ge_p3 A, B, a_result, b_result, final_result; @@ -120,14 +192,17 @@ urcrypt_ed_add_double_scalarmult(uint8_t a[32], } void -urcrypt_ed_puck(uint8_t seed[32], uint8_t out[32]) +urcrypt_ed_puck(const uint8_t seed[32], + uint8_t out[32]) { uint8_t secret[64]; ed25519_create_keypair(out, secret, seed); } void -urcrypt_ed_shar(uint8_t public[32], uint8_t seed[32], uint8_t out[32]) +urcrypt_ed_shar(const uint8_t public[32], + const uint8_t seed[32], + uint8_t out[32]) { uint8_t self[32], exp[64]; @@ -140,9 +215,9 @@ urcrypt_ed_shar(uint8_t public[32], uint8_t seed[32], uint8_t out[32]) } void -urcrypt_ed_sign(uint8_t *message, +urcrypt_ed_sign(const uint8_t *message, size_t length, - uint8_t seed[32], + const uint8_t seed[32], uint8_t out[64]) { uint8_t public[64], secret[64]; @@ -156,8 +231,10 @@ urcrypt_ed_sign(uint8_t *message, } bool -urcrypt_ed_veri(uint8_t *message, size_t length, - uint8_t public[32], uint8_t signature[64]) +urcrypt_ed_veri(const uint8_t *message, + size_t length, + const uint8_t public[32], + const uint8_t signature[64]) { return ( ed25519_verify(signature, message, length, public) == 1 ) ? true @@ -165,123 +242,182 @@ urcrypt_ed_veri(uint8_t *message, size_t length, } static void -reverse_bytes(size_t size, uint8_t *in, uint8_t *out) { +reverse_copy(size_t size, const uint8_t *in, uint8_t *out) { size_t i, j; for ( i = 0, j = size - 1; i < size; i++, j-- ) { out[i] = in[j]; } } +static void +reverse_inplace(size_t size, uint8_t *ptr) { + size_t i, j; + uint8_t tmp; + for ( i = 0, j = size - 1; i < j; i++, j-- ) { + tmp = ptr[i]; + ptr[i] = ptr[j]; + ptr[j] = tmp; + } +} + int -urcrypt_aes_ecba_en(uint8_t key[16], uint8_t block[16], uint8_t out[16]) +urcrypt_aes_ecba_en(const uint8_t key[16], + const uint8_t block[16], + uint8_t out[16]) { AES_KEY aes_key; - uint8_t rkey[16], rblock[16], rout[16]; + uint8_t rkey[16], rblock[16]; - reverse_bytes(16, key, rkey); - reverse_bytes(16, block, rblock); + reverse_copy(16, key, rkey); + reverse_copy(16, block, rblock); if ( 0 != AES_set_encrypt_key(rkey, 128, &aes_key) ) { return -1; } else { - AES_ecb_encrypt(rblock, rout, &aes_key, AES_ENCRYPT); - reverse_bytes(16, rout, out); + AES_ecb_encrypt(rblock, out, &aes_key, AES_ENCRYPT); + reverse_inplace(16, out); return 0; } } int -urcrypt_aes_ecba_de(uint8_t key[16], uint8_t block[16], uint8_t out[16]) +urcrypt_aes_ecba_de(const uint8_t key[16], + const uint8_t block[16], + uint8_t out[16]) { AES_KEY aes_key; - uint8_t rkey[16], rblock[16], rout[16]; + uint8_t rkey[16], rblock[16]; - reverse_bytes(16, key, rkey); - reverse_bytes(16, block, rblock); + reverse_copy(16, key, rkey); + reverse_copy(16, block, rblock); if ( 0 != AES_set_decrypt_key(rkey, 128, &aes_key) ) { return -1; } else { - AES_ecb_encrypt(rblock, rout, &aes_key, AES_DECRYPT); - reverse_bytes(16, rout, out); + AES_ecb_encrypt(rblock, out, &aes_key, AES_DECRYPT); + reverse_inplace(16, out); return 0; } } int -urcrypt_aes_ecbb_en(uint8_t key[24], uint8_t block[16], uint8_t out[16]) +urcrypt_aes_ecbb_en(const uint8_t key[24], + const uint8_t block[16], + uint8_t out[16]) { AES_KEY aes_key; - uint8_t rkey[24], rblock[16], rout[16]; + uint8_t rkey[24], rblock[16]; - reverse_bytes(24, key, rkey); - reverse_bytes(16, block, rblock); + reverse_copy(24, key, rkey); + reverse_copy(16, block, rblock); if ( 0 != AES_set_encrypt_key(rkey, 192, &aes_key) ) { return -1; } else { - AES_ecb_encrypt(rblock, rout, &aes_key, AES_ENCRYPT); - reverse_bytes(16, rout, out); + AES_ecb_encrypt(rblock, out, &aes_key, AES_ENCRYPT); + reverse_inplace(16, out); return 0; } } int -urcrypt_aes_ecbb_de(uint8_t key[24], uint8_t block[16], uint8_t out[16]) +urcrypt_aes_ecbb_de(const uint8_t key[24], + const uint8_t block[16], + uint8_t out[16]) { AES_KEY aes_key; - uint8_t rkey[24], rblock[16], rout[16]; + uint8_t rkey[24], rblock[16]; - reverse_bytes(24, key, rkey); - reverse_bytes(16, block, rblock); + reverse_copy(24, key, rkey); + reverse_copy(16, block, rblock); if ( 0 != AES_set_decrypt_key(rkey, 192, &aes_key) ) { return -1; } else { - AES_ecb_encrypt(rblock, rout, &aes_key, AES_DECRYPT); - reverse_bytes(16, rout, out); + AES_ecb_encrypt(rblock, out, &aes_key, AES_DECRYPT); + reverse_inplace(16, out); return 0; } } int -urcrypt_aes_ecbc_en(uint8_t key[32], uint8_t block[16], uint8_t out[16]) +urcrypt_aes_ecbc_en(const uint8_t key[32], + const uint8_t block[16], + uint8_t out[16]) { AES_KEY aes_key; - uint8_t rkey[32], rblock[16], rout[16]; + uint8_t rkey[32], rblock[16]; - reverse_bytes(32, key, rkey); - reverse_bytes(16, block, rblock); + reverse_copy(32, key, rkey); + reverse_copy(16, block, rblock); if ( 0 != AES_set_encrypt_key(rkey, 256, &aes_key) ) { return -1; } else { - AES_ecb_encrypt(rblock, rout, &aes_key, AES_ENCRYPT); - reverse_bytes(16, rout, out); + AES_ecb_encrypt(rblock, out, &aes_key, AES_ENCRYPT); + reverse_inplace(16, out); return 0; } } int -urcrypt_aes_ecbc_de(uint8_t key[32], uint8_t block[16], uint8_t out[16]) +urcrypt_aes_ecbc_de(const uint8_t key[32], + const uint8_t block[16], + uint8_t out[16]) { AES_KEY aes_key; - uint8_t rkey[32], rblock[16], rout[16]; + uint8_t rkey[32], rblock[16]; - reverse_bytes(32, key, rkey); - reverse_bytes(16, block, rblock); + reverse_copy(32, key, rkey); + reverse_copy(16, block, rblock); if ( 0 != AES_set_decrypt_key(rkey, 256, &aes_key) ) { return -1; } else { - AES_ecb_encrypt(rblock, rout, &aes_key, AES_DECRYPT); - reverse_bytes(16, rout, out); + AES_ecb_encrypt(rblock, out, &aes_key, AES_DECRYPT); + reverse_inplace(16, out); return 0; } } + +uint8_t* +urcrypt_aes_cbca_en(const uint8_t *message, + size_t length, + const uint8_t key[16], + const uint8_t ivec[16], + size_t *out_length) +{ + AES_KEY aes_key; + uint8_t rkey[16]; + + reverse_copy(16, key, rkey); + + if ( 0 != AES_set_encrypt_key(rkey, 128, &aes_key) ) { + return NULL; + } + else { + uint8_t riv[16], *in, *out; + size_t padding = 16 - (length % 16), + padded = length + padding; + + reverse_copy(16, ivec, riv); + + in = urcrypt_malloc(padded); + memset(in, 0, padding); + reverse_copy(length, message, in + padding); + + out = urcrypt_malloc(padded); + AES_cbc_encrypt(in, out, padded, &aes_key, riv, AES_ENCRYPT); + urcrypt_free(in); + + reverse_inplace(padded, out); + *out_length = padded; + return out; + } +} diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index ee4f68732..ca2d52cf7 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -3,40 +3,81 @@ #include #include +#include #include #include #include +#include #include -int urcrypt_ed_point_add(uint8_t a[32], uint8_t b[32], uint8_t out[32]); -int urcrypt_ed_scalarmult(uint8_t a[32], uint8_t b[32], uint8_t out[32]); -void urcrypt_ed_scalarmult_base(uint8_t a[32], uint8_t out[32]); -int urcrypt_ed_add_scalarmult_scalarmult_base(uint8_t a[32], - uint8_t a_point[32], - uint8_t b[32], +typedef void *(*urcrypt_malloc_t)(size_t); +typedef void *(*urcrypt_realloc_t)(void*, size_t); +typedef void (*urcrypt_free_t)(void*); + +int urcrypt_init(urcrypt_malloc_t, urcrypt_realloc_t, urcrypt_free_t); + +void *urcrypt_malloc(size_t); +void *urcrypt_realloc(void*, size_t); +void urcrypt_free(void*); + +int urcrypt_ed_point_add(const uint8_t a[32], + const uint8_t b[32], + uint8_t out[32]); +int urcrypt_ed_scalarmult(const uint8_t a[32], + const uint8_t b[32], + uint8_t out[32]); +void urcrypt_ed_scalarmult_base(const uint8_t a[32], + uint8_t out[32]); +int urcrypt_ed_add_scalarmult_scalarmult_base(const uint8_t a[32], + const uint8_t a_point[32], + const uint8_t b[32], uint8_t out[32]); -int urcrypt_ed_add_double_scalarmult(uint8_t a[32], - uint8_t a_point[32], - uint8_t b[32], - uint8_t b_point[32], +int urcrypt_ed_add_double_scalarmult(const uint8_t a[32], + const uint8_t a_point[32], + const uint8_t b[32], + const uint8_t b_point[32], uint8_t out[32]); -void urcrypt_ed_puck(uint8_t seed[32], uint8_t out[32]); -void urcrypt_ed_shar(uint8_t public[32], uint8_t seed[32], uint8_t out[32]); -void urcrypt_ed_sign(uint8_t *message, +void urcrypt_ed_puck(const uint8_t seed[32], + uint8_t out[32]); +void urcrypt_ed_shar(const uint8_t public[32], + const uint8_t seed[32], + uint8_t out[32]); +void urcrypt_ed_sign(const uint8_t *message, size_t length, - uint8_t seed[32], + const uint8_t seed[32], uint8_t out[64]); +bool urcrypt_ed_veri(const uint8_t *message, + size_t length, + const uint8_t signature[64], + const uint8_t public[32]); + +int urcrypt_aes_ecba_en(const uint8_t key[16], + const uint8_t block[16], + uint8_t out[16]); +int urcrypt_aes_ecba_de(const uint8_t key[16], + const uint8_t block[16], + uint8_t out[16]); +int urcrypt_aes_ecbb_en(const uint8_t key[24], + const uint8_t block[16], + uint8_t out[16]); +int urcrypt_aes_ecbb_de(const uint8_t key[24], + const uint8_t block[16], + uint8_t out[16]); +int urcrypt_aes_ecbc_en(const uint8_t key[32], + const uint8_t block[16], + uint8_t out[16]); +int urcrypt_aes_ecbc_de(const uint8_t key[32], + const uint8_t block[16], + uint8_t out[16]); + +uint8_t* urcrypt_aes_cbca_en(const uint8_t *message, + size_t length, + const uint8_t key[16], + const uint8_t ivec[16], + size_t *out_length); -bool urcrypt_ed_veri(uint8_t *message, size_t length, - uint8_t signature[64], uint8_t public[32]); -int urcrypt_aes_ecba_en(uint8_t key[16], uint8_t block[16], uint8_t out[16]); -int urcrypt_aes_ecba_de(uint8_t key[16], uint8_t block[16], uint8_t out[16]); -int urcrypt_aes_ecbb_en(uint8_t key[24], uint8_t block[16], uint8_t out[16]); -int urcrypt_aes_ecbb_de(uint8_t key[24], uint8_t block[16], uint8_t out[16]); -int urcrypt_aes_ecbc_en(uint8_t key[32], uint8_t block[16], uint8_t out[16]); -int urcrypt_aes_ecbc_de(uint8_t key[32], uint8_t block[16], uint8_t out[16]); #endif From e863afd30eb0c5feccb1bfb999f4a1826174da2c Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 7 Aug 2020 12:47:43 -0700 Subject: [PATCH 17/75] add u3r_unpack_alloc() --- pkg/urbit/include/noun/retrieve.h | 7 +++++++ pkg/urbit/jets/e/aes_cbc.c | 4 +--- pkg/urbit/jets/e/ed_sign.c | 6 ++---- pkg/urbit/jets/e/ed_veri.c | 10 +++------- pkg/urbit/noun/retrieve.c | 14 ++++++++++++++ 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/pkg/urbit/include/noun/retrieve.h b/pkg/urbit/include/noun/retrieve.h index c17877d9d..295ad622a 100644 --- a/pkg/urbit/include/noun/retrieve.h +++ b/pkg/urbit/include/noun/retrieve.h @@ -355,6 +355,13 @@ c3_y *buf_y, u3_atom a); + /* u3r_unpack_alloc(): + ** + ** Allocate a new byte array with all the bytes of (a) + */ + c3_y* + u3r_unpack_alloc(c3_w* len_w, u3_atom a); + /* u3r_chop(): ** ** Into the bloq space of `met`, from position `fum` for a diff --git a/pkg/urbit/jets/e/aes_cbc.c b/pkg/urbit/jets/e/aes_cbc.c index e1604188a..af8325751 100644 --- a/pkg/urbit/jets/e/aes_cbc.c +++ b/pkg/urbit/jets/e/aes_cbc.c @@ -24,9 +24,7 @@ u3r_bytes(0, 16, key_y, key); u3r_bytes(0, 16, iv_y, iv); - met_w = u3r_met(3, msg); - msg_y = u3a_malloc(met_w); - u3r_bytes(0, met_w, msg_y, msg); + msg_y = u3r_unpack_alloc(&met_w, msg); out_y = urcrypt_aes_cbca_en(msg_y, met_w, key_y, iv_y, &len); u3a_free(msg_y); diff --git a/pkg/urbit/jets/e/ed_sign.c b/pkg/urbit/jets/e/ed_sign.c index 606282947..e6a153ba1 100644 --- a/pkg/urbit/jets/e/ed_sign.c +++ b/pkg/urbit/jets/e/ed_sign.c @@ -18,11 +18,9 @@ } else { c3_y sig_y[64]; - c3_y* mes_y; - c3_w met_w = u3r_met(3, a); + c3_w met_w; + c3_y* mes_y = u3r_unpack_alloc(&met_w, a); - mes_y = u3a_malloc(met_w); - u3r_bytes(0, met_w, mes_y, a); urcrypt_ed_sign(mes_y, met_w, sed_y, sig_y); u3a_free(mes_y); diff --git a/pkg/urbit/jets/e/ed_veri.c b/pkg/urbit/jets/e/ed_veri.c index 646dd89ca..f4bf30ac3 100644 --- a/pkg/urbit/jets/e/ed_veri.c +++ b/pkg/urbit/jets/e/ed_veri.c @@ -19,13 +19,9 @@ return u3_none; } else { - c3_t val_t; - c3_y* mes_y; - c3_w met_w = u3r_met(3, m); - - mes_y = u3a_malloc(met_w); - u3r_bytes(0, met_w, mes_y, m); - val_t = urcrypt_ed_veri(mes_y, met_w, pub_y, sig_y); + c3_w met_w; + c3_y* mes_y = u3r_unpack_alloc(&met_w, m); + c3_t val_t = urcrypt_ed_veri(mes_y, met_w, pub_y, sig_y); u3a_free(mes_y); return val_t ? c3y : c3n; diff --git a/pkg/urbit/noun/retrieve.c b/pkg/urbit/noun/retrieve.c index 36fed5073..b8f875b86 100644 --- a/pkg/urbit/noun/retrieve.c +++ b/pkg/urbit/noun/retrieve.c @@ -1093,6 +1093,20 @@ u3r_unpack(c3_w len_w, c3_y *buf_y, u3_atom a) } } +/* u3r_unpack_alloc(): +** +** Allocate a new byte array with all the bytes of (a) +*/ +c3_y* +u3r_unpack_alloc(c3_w* len_w, u3_atom a) +{ + c3_w met_w = u3r_met(3, a); + c3_y* a_y = u3a_malloc(met_w); + u3r_bytes(0, met_w, a_y, a); + *len_w = met_w; + return a_y; +} + /* u3r_mp(): ** ** Copy (b) into (a_mp). From a1c1f3b9eed5b49b206ecca8e4dcdaf51fcb9bd6 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 7 Aug 2020 13:06:44 -0700 Subject: [PATCH 18/75] making the bodies of the cbc functions call helpers to be more boring --- pkg/urbit/jets/e/aes_cbc.c | 4 +- pkg/urcrypt/urcrypt.c | 75 +++++++++++++++++++++----------------- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/pkg/urbit/jets/e/aes_cbc.c b/pkg/urbit/jets/e/aes_cbc.c index af8325751..e0c2c1056 100644 --- a/pkg/urbit/jets/e/aes_cbc.c +++ b/pkg/urbit/jets/e/aes_cbc.c @@ -17,9 +17,9 @@ u3_atom iv, u3_atom msg) { - c3_y key_y[16], iv_y[16], *msg_y, *out_y; - c3_w met_w; size_t len; + c3_w met_w; + c3_y key_y[16], iv_y[16], *msg_y, *out_y; u3r_bytes(0, 16, key_y, key); u3r_bytes(0, 16, iv_y, iv); diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 9c973a228..36f603af2 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -242,7 +242,7 @@ urcrypt_ed_veri(const uint8_t *message, } static void -reverse_copy(size_t size, const uint8_t *in, uint8_t *out) { +_urcrypt_reverse_copy(size_t size, const uint8_t *in, uint8_t *out) { size_t i, j; for ( i = 0, j = size - 1; i < size; i++, j-- ) { out[i] = in[j]; @@ -250,7 +250,7 @@ reverse_copy(size_t size, const uint8_t *in, uint8_t *out) { } static void -reverse_inplace(size_t size, uint8_t *ptr) { +_urcrypt_reverse_inplace(size_t size, uint8_t *ptr) { size_t i, j; uint8_t tmp; for ( i = 0, j = size - 1; i < j; i++, j-- ) { @@ -268,15 +268,15 @@ urcrypt_aes_ecba_en(const uint8_t key[16], AES_KEY aes_key; uint8_t rkey[16], rblock[16]; - reverse_copy(16, key, rkey); - reverse_copy(16, block, rblock); + _urcrypt_reverse_copy(16, key, rkey); + _urcrypt_reverse_copy(16, block, rblock); if ( 0 != AES_set_encrypt_key(rkey, 128, &aes_key) ) { return -1; } else { AES_ecb_encrypt(rblock, out, &aes_key, AES_ENCRYPT); - reverse_inplace(16, out); + _urcrypt_reverse_inplace(16, out); return 0; } } @@ -289,15 +289,15 @@ urcrypt_aes_ecba_de(const uint8_t key[16], AES_KEY aes_key; uint8_t rkey[16], rblock[16]; - reverse_copy(16, key, rkey); - reverse_copy(16, block, rblock); + _urcrypt_reverse_copy(16, key, rkey); + _urcrypt_reverse_copy(16, block, rblock); if ( 0 != AES_set_decrypt_key(rkey, 128, &aes_key) ) { return -1; } else { AES_ecb_encrypt(rblock, out, &aes_key, AES_DECRYPT); - reverse_inplace(16, out); + _urcrypt_reverse_inplace(16, out); return 0; } } @@ -310,15 +310,15 @@ urcrypt_aes_ecbb_en(const uint8_t key[24], AES_KEY aes_key; uint8_t rkey[24], rblock[16]; - reverse_copy(24, key, rkey); - reverse_copy(16, block, rblock); + _urcrypt_reverse_copy(24, key, rkey); + _urcrypt_reverse_copy(16, block, rblock); if ( 0 != AES_set_encrypt_key(rkey, 192, &aes_key) ) { return -1; } else { AES_ecb_encrypt(rblock, out, &aes_key, AES_ENCRYPT); - reverse_inplace(16, out); + _urcrypt_reverse_inplace(16, out); return 0; } } @@ -331,15 +331,15 @@ urcrypt_aes_ecbb_de(const uint8_t key[24], AES_KEY aes_key; uint8_t rkey[24], rblock[16]; - reverse_copy(24, key, rkey); - reverse_copy(16, block, rblock); + _urcrypt_reverse_copy(24, key, rkey); + _urcrypt_reverse_copy(16, block, rblock); if ( 0 != AES_set_decrypt_key(rkey, 192, &aes_key) ) { return -1; } else { AES_ecb_encrypt(rblock, out, &aes_key, AES_DECRYPT); - reverse_inplace(16, out); + _urcrypt_reverse_inplace(16, out); return 0; } } @@ -352,15 +352,15 @@ urcrypt_aes_ecbc_en(const uint8_t key[32], AES_KEY aes_key; uint8_t rkey[32], rblock[16]; - reverse_copy(32, key, rkey); - reverse_copy(16, block, rblock); + _urcrypt_reverse_copy(32, key, rkey); + _urcrypt_reverse_copy(16, block, rblock); if ( 0 != AES_set_encrypt_key(rkey, 256, &aes_key) ) { return -1; } else { AES_ecb_encrypt(rblock, out, &aes_key, AES_ENCRYPT); - reverse_inplace(16, out); + _urcrypt_reverse_inplace(16, out); return 0; } } @@ -373,19 +373,34 @@ urcrypt_aes_ecbc_de(const uint8_t key[32], AES_KEY aes_key; uint8_t rkey[32], rblock[16]; - reverse_copy(32, key, rkey); - reverse_copy(16, block, rblock); + _urcrypt_reverse_copy(32, key, rkey); + _urcrypt_reverse_copy(16, block, rblock); if ( 0 != AES_set_decrypt_key(rkey, 256, &aes_key) ) { return -1; } else { AES_ecb_encrypt(rblock, out, &aes_key, AES_DECRYPT); - reverse_inplace(16, out); + _urcrypt_reverse_inplace(16, out); return 0; } } +static uint8_t* +_urcrypt_cbc_pad(size_t *length_ptr, const uint8_t *message) +{ + size_t length = *length_ptr, + padding = 16 - (length % 16), + padded = length + padding; + uint8_t *buf = urcrypt_malloc(padded); + + memset(buf, 0, padding); + _urcrypt_reverse_copy(length, message, buf + padding); + + *length_ptr = padded; + return buf; +} + uint8_t* urcrypt_aes_cbca_en(const uint8_t *message, size_t length, @@ -396,28 +411,22 @@ urcrypt_aes_cbca_en(const uint8_t *message, AES_KEY aes_key; uint8_t rkey[16]; - reverse_copy(16, key, rkey); + _urcrypt_reverse_copy(16, key, rkey); if ( 0 != AES_set_encrypt_key(rkey, 128, &aes_key) ) { return NULL; } else { uint8_t riv[16], *in, *out; - size_t padding = 16 - (length % 16), - padded = length + padding; - reverse_copy(16, ivec, riv); - - in = urcrypt_malloc(padded); - memset(in, 0, padding); - reverse_copy(length, message, in + padding); - - out = urcrypt_malloc(padded); - AES_cbc_encrypt(in, out, padded, &aes_key, riv, AES_ENCRYPT); + _urcrypt_reverse_copy(16, ivec, riv); + in = _urcrypt_cbc_pad(&length, message); + out = urcrypt_malloc(length); + AES_cbc_encrypt(in, out, length, &aes_key, riv, AES_ENCRYPT); urcrypt_free(in); - reverse_inplace(padded, out); - *out_length = padded; + _urcrypt_reverse_inplace(length, out); + *out_length = length; return out; } } From a9bbac948ec477282240c0011afbdce3bd184a3a Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 7 Aug 2020 14:40:27 -0700 Subject: [PATCH 19/75] more helper functions for shorter, more boring code in aes ecb, cbc --- pkg/urbit/jets/e/aes_cbc.c | 101 +++++++++---------------------------- pkg/urbit/jets/e/aes_ecb.c | 79 +++++++++-------------------- pkg/urcrypt/urcrypt.c | 70 ++++++++++++++++++++----- pkg/urcrypt/urcrypt.h | 6 ++- 4 files changed, 111 insertions(+), 145 deletions(-) diff --git a/pkg/urbit/jets/e/aes_cbc.c b/pkg/urbit/jets/e/aes_cbc.c index e0c2c1056..6f40c8aa8 100644 --- a/pkg/urbit/jets/e/aes_cbc.c +++ b/pkg/urbit/jets/e/aes_cbc.c @@ -10,22 +10,25 @@ * the ECB functions, which truncate them, hence the raw u3r_bytes unpacking. */ +typedef c3_y* (*urcrypt_cbc)(const c3_y*, + size_t, + const c3_y*, + const c3_y*, + size_t*); + /* functions */ static u3_atom - _cqea_cbca_en(u3_atom key, - u3_atom iv, - u3_atom msg) + _cqea_cbc_help(c3_y* key_y, u3_atom iv, u3_atom msg, urcrypt_cbc low_f) { size_t len; c3_w met_w; - c3_y key_y[16], iv_y[16], *msg_y, *out_y; + c3_y iv_y[16], *msg_y, *out_y; - u3r_bytes(0, 16, key_y, key); u3r_bytes(0, 16, iv_y, iv); msg_y = u3r_unpack_alloc(&met_w, msg); - out_y = urcrypt_aes_cbca_en(msg_y, met_w, key_y, iv_y, &len); + out_y = (*low_f)(msg_y, met_w, key_y, iv_y, &len); u3a_free(msg_y); if ( NULL == out_y ) { @@ -38,6 +41,16 @@ } } + static u3_atom + _cqea_cbca_en(u3_atom key, + u3_atom iv, + u3_atom msg) + { + c3_y key_y[16]; + u3r_bytes(0, 16, key_y, key); + return _cqea_cbc_help(key_y, iv, msg, &urcrypt_aes_cbca_en); + } + u3_noun u3wea_cbca_en(u3_noun cor) { @@ -52,80 +65,14 @@ } } - u3_noun - u3qea_cbca_de(u3_atom key, + static u3_atom + _cqea_cbca_de(u3_atom key, u3_atom iv, u3_atom msg) { c3_y key_y[16]; - c3_y iv_y[16]; - c3_w len_msg_w; - c3_w len_out_w; - c3_y *msg_y; - c3_y *out_y; - u3_atom out; - AES_KEY key_u; - - c3_assert(u3r_met(3, key) <= 16); - c3_assert(u3r_met(3, iv) <= 16); - len_msg_w = u3r_met(3, msg); - len_out_w = (len_msg_w % 16) == 0 ? len_msg_w : len_msg_w + (16 - (len_msg_w % 16)); - len_msg_w = len_out_w; - - msg_y = u3a_malloc(len_msg_w); - out_y = u3a_malloc(len_out_w); - - { - int i = 15; - - do { - key_y[i] = u3r_byte(15-i, key); - i--; - } while (i >= 0); - } - { - int i = 15; - - do { - iv_y[i] = u3r_byte(15-i, iv); - i--; - } while (i >= 0); - } - { - int i = len_msg_w - 1; - - do { - msg_y[i] = u3r_byte((len_msg_w - 1)-i, msg); - i--; - } while (i >= 0); - } - - if ( 0 != AES_set_decrypt_key(key_y, 128, &key_u) ) { - return u3m_bail(c3__exit); - } - else { - AES_cbc_encrypt(msg_y, out_y, len_msg_w, &key_u, iv_y, AES_DECRYPT); - } - - /* array reverse - we can write backwards u3i_bytes * - * in the unlikely event that this becomes a problem */ - { - int i = len_out_w - 1; - int j = 0; - c3_y tmp; - - do { - tmp = out_y[i]; - out_y[i] = out_y[j]; - out_y[j] = tmp; - i--; j++; - } while (i > j); - } - - out = u3i_bytes(len_out_w, out_y); - u3a_free(msg_y); - u3a_free(out_y); - return out; + u3r_bytes(0, 16, key_y, key); + return _cqea_cbc_help(key_y, iv, msg, &urcrypt_aes_cbca_de); } u3_noun @@ -138,7 +85,7 @@ c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return u3qea_cbca_de(a, b, c); + return _cqea_cbca_de(a, b, c); } } diff --git a/pkg/urbit/jets/e/aes_ecb.c b/pkg/urbit/jets/e/aes_ecb.c index 8ca616d4a..b95a36a73 100644 --- a/pkg/urbit/jets/e/aes_ecb.c +++ b/pkg/urbit/jets/e/aes_ecb.c @@ -4,6 +4,8 @@ #include "all.h" #include +typedef int (*urcrypt_ecb)(const c3_y*, const c3_y[16], c3_y[16]); + /* functions */ @@ -11,15 +13,13 @@ * jets we unpack with an unconditional u3r_bytes */ static u3_atom - _cqea_ecba_en(u3_atom key, - u3_atom blk) + _cqea_ecba_help(c3_y* key_y, u3_atom blk, urcrypt_ecb low_f) { - c3_y key_y[16], blk_y[16], out_y[16]; + c3_y blk_y[16], out_y[16]; - u3r_bytes(0, 16, key_y, key); u3r_bytes(0, 16, blk_y, blk); - if ( 0 != urcrypt_aes_ecba_en(key_y, blk_y, out_y) ) { + if ( 0 != (*low_f)(key_y, blk_y, out_y) ) { return u3_none; } else { @@ -27,6 +27,15 @@ } } + static u3_atom + _cqea_ecba_en(u3_atom key, + u3_atom blk) + { + c3_y key_y[16]; + u3r_bytes(0, 16, key_y, key); + return _cqea_ecba_help(key_y, blk, &urcrypt_aes_ecba_en); + } + u3_noun u3wea_ecba_en(u3_noun cor) { @@ -45,17 +54,9 @@ _cqea_ecba_de(u3_atom key, u3_atom blk) { - c3_y key_y[16], blk_y[16], out_y[16]; - + c3_y key_y[16]; u3r_bytes(0, 16, key_y, key); - u3r_bytes(0, 16, blk_y, blk); - - if ( 0 != urcrypt_aes_ecba_de(key_y, blk_y, out_y) ) { - return u3_none; - } - else { - return u3i_bytes(16, out_y); - } + return _cqea_ecba_help(key_y, blk, &urcrypt_aes_ecba_de); } u3_noun @@ -76,17 +77,9 @@ _cqea_ecbb_en(u3_atom key, u3_atom blk) { - c3_y key_y[24], blk_y[16], out_y[16]; - + c3_y key_y[24]; u3r_bytes(0, 24, key_y, key); - u3r_bytes(0, 16, blk_y, blk); - - if ( 0 != urcrypt_aes_ecbb_en(key_y, blk_y, out_y) ) { - return u3_none; - } - else { - return u3i_bytes(16, out_y); - } + return _cqea_ecba_help(key_y, blk, &urcrypt_aes_ecbb_en); } u3_noun @@ -107,17 +100,9 @@ _cqea_ecbb_de(u3_atom key, u3_atom blk) { - c3_y key_y[24], blk_y[16], out_y[16]; - + c3_y key_y[24]; u3r_bytes(0, 24, key_y, key); - u3r_bytes(0, 16, blk_y, blk); - - if ( 0 != urcrypt_aes_ecbb_de(key_y, blk_y, out_y) ) { - return u3_none; - } - else { - return u3i_bytes(16, out_y); - } + return _cqea_ecba_help(key_y, blk, &urcrypt_aes_ecbb_de); } u3_noun @@ -138,17 +123,9 @@ _cqea_ecbc_en(u3_atom key, u3_atom blk) { - c3_y key_y[32], blk_y[16], out_y[16]; - + c3_y key_y[32]; u3r_bytes(0, 32, key_y, key); - u3r_bytes(0, 16, blk_y, blk); - - if ( 0 != urcrypt_aes_ecbc_en(key_y, blk_y, out_y) ) { - return u3_none; - } - else { - return u3i_bytes(16, out_y); - } + return _cqea_ecba_help(key_y, blk, &urcrypt_aes_ecbc_en); } u3_noun @@ -169,17 +146,9 @@ _cqea_ecbc_de(u3_atom key, u3_atom blk) { - c3_y key_y[32], blk_y[16], out_y[16]; - + c3_y key_y[32]; u3r_bytes(0, 32, key_y, key); - u3r_bytes(0, 16, blk_y, blk); - - if ( 0 != urcrypt_aes_ecbc_de(key_y, blk_y, out_y) ) { - return u3_none; - } - else { - return u3i_bytes(16, out_y); - } + return _cqea_ecba_help(key_y, blk, &urcrypt_aes_ecbc_de); } u3_noun diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 36f603af2..f7604e2b1 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -390,7 +390,8 @@ static uint8_t* _urcrypt_cbc_pad(size_t *length_ptr, const uint8_t *message) { size_t length = *length_ptr, - padding = 16 - (length % 16), + rem = length % 16, + padding = rem ? 16 - rem : 0, padded = length + padding; uint8_t *buf = urcrypt_malloc(padded); @@ -401,6 +402,31 @@ _urcrypt_cbc_pad(size_t *length_ptr, const uint8_t *message) return buf; } +uint8_t* +_urcrypt_cbc_help(const uint8_t *message, + size_t length, + const AES_KEY *key, + const uint8_t ivec[16], + const int enc, + size_t *out_length) +{ + uint8_t riv[16], *in, *out; + + _urcrypt_reverse_copy(16, ivec, riv); + FILE* nukes = fopen("/tmp/urcrypt.txt", "w"); + fprintf(nukes, "length before: %d\r\n", (int) length); + in = _urcrypt_cbc_pad(&length, message); + fprintf(nukes, "length after: %d\r\n", (int) length); + fclose(nukes); + out = urcrypt_malloc(length); + AES_cbc_encrypt(in, out, length, key, riv, enc); + urcrypt_free(in); + + _urcrypt_reverse_inplace(length, out); + *out_length = length; + return out; +} + uint8_t* urcrypt_aes_cbca_en(const uint8_t *message, size_t length, @@ -417,16 +443,36 @@ urcrypt_aes_cbca_en(const uint8_t *message, return NULL; } else { - uint8_t riv[16], *in, *out; - - _urcrypt_reverse_copy(16, ivec, riv); - in = _urcrypt_cbc_pad(&length, message); - out = urcrypt_malloc(length); - AES_cbc_encrypt(in, out, length, &aes_key, riv, AES_ENCRYPT); - urcrypt_free(in); - - _urcrypt_reverse_inplace(length, out); - *out_length = length; - return out; + return _urcrypt_cbc_help(message, + length, + &aes_key, + ivec, + AES_ENCRYPT, + out_length); + } +} + +uint8_t* +urcrypt_aes_cbca_de(const uint8_t *message, + size_t length, + const uint8_t key[16], + const uint8_t ivec[16], + size_t *out_length) +{ + AES_KEY aes_key; + uint8_t rkey[16]; + + _urcrypt_reverse_copy(16, key, rkey); + + if ( 0 != AES_set_decrypt_key(rkey, 128, &aes_key) ) { + return NULL; + } + else { + return _urcrypt_cbc_help(message, + length, + &aes_key, + ivec, + AES_DECRYPT, + out_length); } } diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index ca2d52cf7..3471c25d1 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -78,6 +78,10 @@ uint8_t* urcrypt_aes_cbca_en(const uint8_t *message, const uint8_t key[16], const uint8_t ivec[16], size_t *out_length); - +uint8_t* urcrypt_aes_cbca_de(const uint8_t *message, + size_t length, + const uint8_t key[16], + const uint8_t ivec[16], + size_t *out_length); #endif From fe1375ef6b0060acd72055a4025f28884e2ade6b Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 7 Aug 2020 16:28:00 -0700 Subject: [PATCH 20/75] cbc:aes -> urcrypt --- pkg/urbit/jets/e/aes_cbc.c | 306 +++---------------------------------- pkg/urcrypt/urcrypt.c | 104 ++++++++++++- pkg/urcrypt/urcrypt.h | 20 +++ 3 files changed, 140 insertions(+), 290 deletions(-) diff --git a/pkg/urbit/jets/e/aes_cbc.c b/pkg/urbit/jets/e/aes_cbc.c index 6f40c8aa8..19e129132 100644 --- a/pkg/urbit/jets/e/aes_cbc.c +++ b/pkg/urbit/jets/e/aes_cbc.c @@ -4,8 +4,6 @@ #include "all.h" #include -#include - /* All of the CBC hoon truncates its key and prv inputs by passing them to * the ECB functions, which truncate them, hence the raw u3r_bytes unpacking. */ @@ -89,80 +87,14 @@ typedef c3_y* (*urcrypt_cbc)(const c3_y*, } } - u3_noun - u3qea_cbcb_en(u3_atom key, + static u3_atom + _cqea_cbcb_en(u3_atom key, u3_atom iv, u3_atom msg) { c3_y key_y[24]; - c3_y iv_y[16]; - c3_w len_msg_w; - c3_w len_out_w; - c3_y *msg_y; - c3_y *out_y; - u3_atom out; - AES_KEY key_u; - - c3_assert(u3r_met(3, key) <= 24); - c3_assert(u3r_met(3, iv) <= 16); - len_msg_w = u3r_met(3, msg); - len_out_w = (len_msg_w % 16) == 0 ? len_msg_w : len_msg_w + (16 - (len_msg_w % 16)); - len_msg_w = len_out_w; - - msg_y = u3a_malloc(len_msg_w); - out_y = u3a_malloc(len_out_w); - - { - int i = 23; - - do { - key_y[i] = u3r_byte(23-i, key); - i--; - } while (i >= 0); - } - { - int i = 15; - - do { - iv_y[i] = u3r_byte(15-i, iv); - i--; - } while (i >= 0); - } - { - int i = len_msg_w - 1; - - do { - msg_y[i] = u3r_byte((len_msg_w - 1)-i, msg); - i--; - } while (i >= 0); - } - - if ( 0 != AES_set_encrypt_key(key_y, 192, &key_u) ) { - return u3m_bail(c3__exit); - } - else { - AES_cbc_encrypt(msg_y, out_y, len_msg_w, &key_u, iv_y, AES_ENCRYPT); - } - - /* array reverse - we can write backwards u3i_bytes * - * in the unlikely event that this becomes a problem */ - { - int i = len_out_w - 1; - int j = 0; - c3_y tmp; - - do { - tmp = out_y[i]; - out_y[i] = out_y[j]; - out_y[j] = tmp; - i--; j++; - } while (i > j); - } - - out = u3i_bytes(len_out_w, out_y); - u3a_free(msg_y); - u3a_free(out_y); - return out; + u3r_bytes(0, 24, key_y, key); + return _cqea_cbc_help(key_y, iv, msg, &urcrypt_aes_cbcb_en); } u3_noun @@ -175,84 +107,18 @@ typedef c3_y* (*urcrypt_cbc)(const c3_y*, c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return u3qea_cbcb_en(a, b, c); + return _cqea_cbcb_en(a, b, c); } } - u3_noun - u3qea_cbcb_de(u3_atom key, + static u3_atom + _cqea_cbcb_de(u3_atom key, u3_atom iv, u3_atom msg) { c3_y key_y[24]; - c3_y iv_y[16]; - c3_w len_msg_w; - c3_w len_out_w; - c3_y *msg_y; - c3_y *out_y; - u3_atom out; - AES_KEY key_u; - - c3_assert(u3r_met(3, key) <= 24); - c3_assert(u3r_met(3, iv) <= 16); - len_msg_w = u3r_met(3, msg); - len_out_w = (len_msg_w % 16) == 0 ? len_msg_w : len_msg_w + (16 - (len_msg_w % 16)); - len_msg_w = len_out_w; - - msg_y = u3a_malloc(len_msg_w); - out_y = u3a_malloc(len_out_w); - - { - int i = 23; - - do { - key_y[i] = u3r_byte(23-i, key); - i--; - } while (i >= 0); - } - { - int i = 15; - - do { - iv_y[i] = u3r_byte(15-i, iv); - i--; - } while (i >= 0); - } - { - int i = len_msg_w - 1; - - do { - msg_y[i] = u3r_byte((len_msg_w - 1)-i, msg); - i--; - } while (i >= 0); - } - - if ( 0 != AES_set_decrypt_key(key_y, 192, &key_u) ) { - return u3m_bail(c3__exit); - } - else { - AES_cbc_encrypt(msg_y, out_y, len_msg_w, &key_u, iv_y, AES_DECRYPT); - } - - /* array reverse - we can write backwards u3i_bytes * - * in the unlikely event that this becomes a problem */ - { - int i = len_out_w - 1; - int j = 0; - c3_y tmp; - - do { - tmp = out_y[i]; - out_y[i] = out_y[j]; - out_y[j] = tmp; - i--; j++; - } while (i > j); - } - - out = u3i_bytes(len_out_w, out_y); - u3a_free(msg_y); - u3a_free(out_y); - return out; + u3r_bytes(0, 24, key_y, key); + return _cqea_cbc_help(key_y, iv, msg, &urcrypt_aes_cbcb_de); } u3_noun @@ -265,84 +131,18 @@ typedef c3_y* (*urcrypt_cbc)(const c3_y*, c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return u3qea_cbcb_de(a, b, c); + return _cqea_cbcb_de(a, b, c); } } - u3_noun - u3qea_cbcc_en(u3_atom key, + static u3_atom + _cqea_cbcc_en(u3_atom key, u3_atom iv, u3_atom msg) { c3_y key_y[32]; - c3_y iv_y[16]; - c3_w len_msg_w; - c3_w len_out_w; - c3_y *msg_y; - c3_y *out_y; - u3_atom out; - AES_KEY key_u; - - c3_assert(u3r_met(3, key) <= 32); - c3_assert(u3r_met(3, iv) <= 16); - len_msg_w = u3r_met(3, msg); - len_out_w = (len_msg_w % 16) == 0 ? len_msg_w : len_msg_w + (16 - (len_msg_w % 16)); - len_msg_w = len_out_w; - - msg_y = u3a_malloc(len_msg_w); - out_y = u3a_malloc(len_out_w); - - { - int i = 31; - - do { - key_y[i] = u3r_byte(31-i, key); - i--; - } while (i >= 0); - } - { - int i = 15; - - do { - iv_y[i] = u3r_byte(15-i, iv); - i--; - } while (i >= 0); - } - { - int i = len_msg_w - 1; - - do { - msg_y[i] = u3r_byte((len_msg_w - 1)-i, msg); - i--; - } while (i >= 0); - } - - if ( 0 != AES_set_encrypt_key(key_y, 256, &key_u) ) { - return u3m_bail(c3__exit); - } - else { - AES_cbc_encrypt(msg_y, out_y, len_msg_w, &key_u, iv_y, AES_ENCRYPT); - } - - /* array reverse - we can write backwards u3i_bytes * - * in the unlikely event that this becomes a problem */ - { - int i = len_out_w - 1; - int j = 0; - c3_y tmp; - - do { - tmp = out_y[i]; - out_y[i] = out_y[j]; - out_y[j] = tmp; - i--; j++; - } while (i > j); - } - - out = u3i_bytes(len_out_w, out_y); - u3a_free(msg_y); - u3a_free(out_y); - return out; + u3r_bytes(0, 32, key_y, key); + return _cqea_cbc_help(key_y, iv, msg, &urcrypt_aes_cbcc_en); } u3_noun @@ -355,84 +155,18 @@ typedef c3_y* (*urcrypt_cbc)(const c3_y*, c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return u3qea_cbcc_en(a, b, c); + return _cqea_cbcc_en(a, b, c); } } - u3_noun - u3qea_cbcc_de(u3_atom key, + static u3_atom + _cqea_cbcc_de(u3_atom key, u3_atom iv, u3_atom msg) { c3_y key_y[32]; - c3_y iv_y[16]; - c3_w len_msg_w; - c3_w len_out_w; - c3_y *msg_y; - c3_y *out_y; - u3_atom out; - AES_KEY key_u; - - c3_assert(u3r_met(3, key) <= 32); - c3_assert(u3r_met(3, iv) <= 16); - len_msg_w = u3r_met(3, msg); - len_out_w = (len_msg_w % 16) == 0 ? len_msg_w : len_msg_w + (16 - (len_msg_w % 16)); - len_msg_w = len_out_w; - - msg_y = u3a_malloc(len_msg_w); - out_y = u3a_malloc(len_out_w); - - { - int i = 31; - - do { - key_y[i] = u3r_byte(31-i, key); - i--; - } while (i >= 0); - } - { - int i = 15; - - do { - iv_y[i] = u3r_byte(15-i, iv); - i--; - } while (i >= 0); - } - { - int i = len_msg_w - 1; - - do { - msg_y[i] = u3r_byte((len_msg_w - 1)-i, msg); - i--; - } while (i >= 0); - } - - if ( 0 != AES_set_decrypt_key(key_y, 256, &key_u) ) { - return u3m_bail(c3__exit); - } - else { - AES_cbc_encrypt(msg_y, out_y, len_msg_w, &key_u, iv_y, AES_DECRYPT); - } - - /* array reverse - we can write backwards u3i_bytes * - * in the unlikely event that this becomes a problem */ - { - int i = len_out_w - 1; - int j = 0; - c3_y tmp; - - do { - tmp = out_y[i]; - out_y[i] = out_y[j]; - out_y[j] = tmp; - i--; j++; - } while (i > j); - } - - out = u3i_bytes(len_out_w, out_y); - u3a_free(msg_y); - u3a_free(out_y); - return out; + u3r_bytes(0, 32, key_y, key); + return _cqea_cbc_help(key_y, iv, msg, &urcrypt_aes_cbcc_de); } u3_noun @@ -445,6 +179,6 @@ typedef c3_y* (*urcrypt_cbc)(const c3_y*, c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return u3qea_cbcc_de(a, b, c); + return _cqea_cbcc_de(a, b, c); } } diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index f7604e2b1..ff1c72135 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -413,11 +413,7 @@ _urcrypt_cbc_help(const uint8_t *message, uint8_t riv[16], *in, *out; _urcrypt_reverse_copy(16, ivec, riv); - FILE* nukes = fopen("/tmp/urcrypt.txt", "w"); - fprintf(nukes, "length before: %d\r\n", (int) length); in = _urcrypt_cbc_pad(&length, message); - fprintf(nukes, "length after: %d\r\n", (int) length); - fclose(nukes); out = urcrypt_malloc(length); AES_cbc_encrypt(in, out, length, key, riv, enc); urcrypt_free(in); @@ -476,3 +472,103 @@ urcrypt_aes_cbca_de(const uint8_t *message, out_length); } } + +uint8_t* +urcrypt_aes_cbcb_en(const uint8_t *message, + size_t length, + const uint8_t key[24], + const uint8_t ivec[16], + size_t *out_length) +{ + AES_KEY aes_key; + uint8_t rkey[24]; + + _urcrypt_reverse_copy(24, key, rkey); + + if ( 0 != AES_set_encrypt_key(rkey, 192, &aes_key) ) { + return NULL; + } + else { + return _urcrypt_cbc_help(message, + length, + &aes_key, + ivec, + AES_ENCRYPT, + out_length); + } +} + +uint8_t* +urcrypt_aes_cbcb_de(const uint8_t *message, + size_t length, + const uint8_t key[24], + const uint8_t ivec[16], + size_t *out_length) +{ + AES_KEY aes_key; + uint8_t rkey[24]; + + _urcrypt_reverse_copy(24, key, rkey); + + if ( 0 != AES_set_decrypt_key(rkey, 192, &aes_key) ) { + return NULL; + } + else { + return _urcrypt_cbc_help(message, + length, + &aes_key, + ivec, + AES_DECRYPT, + out_length); + } +} + +uint8_t* +urcrypt_aes_cbcc_en(const uint8_t *message, + size_t length, + const uint8_t key[32], + const uint8_t ivec[16], + size_t *out_length) +{ + AES_KEY aes_key; + uint8_t rkey[32]; + + _urcrypt_reverse_copy(32, key, rkey); + + if ( 0 != AES_set_encrypt_key(rkey, 256, &aes_key) ) { + return NULL; + } + else { + return _urcrypt_cbc_help(message, + length, + &aes_key, + ivec, + AES_ENCRYPT, + out_length); + } +} + +uint8_t* +urcrypt_aes_cbcc_de(const uint8_t *message, + size_t length, + const uint8_t key[32], + const uint8_t ivec[16], + size_t *out_length) +{ + AES_KEY aes_key; + uint8_t rkey[32]; + + _urcrypt_reverse_copy(32, key, rkey); + + if ( 0 != AES_set_decrypt_key(rkey, 256, &aes_key) ) { + return NULL; + } + else { + return _urcrypt_cbc_help(message, + length, + &aes_key, + ivec, + AES_DECRYPT, + out_length); + } +} diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 3471c25d1..89699d6b0 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -83,5 +83,25 @@ uint8_t* urcrypt_aes_cbca_de(const uint8_t *message, const uint8_t key[16], const uint8_t ivec[16], size_t *out_length); +uint8_t* urcrypt_aes_cbcb_en(const uint8_t *message, + size_t length, + const uint8_t key[24], + const uint8_t ivec[16], + size_t *out_length); +uint8_t* urcrypt_aes_cbcb_de(const uint8_t *message, + size_t length, + const uint8_t key[24], + const uint8_t ivec[16], + size_t *out_length); +uint8_t* urcrypt_aes_cbcc_en(const uint8_t *message, + size_t length, + const uint8_t key[32], + const uint8_t ivec[16], + size_t *out_length); +uint8_t* urcrypt_aes_cbcc_de(const uint8_t *message, + size_t length, + const uint8_t key[32], + const uint8_t ivec[16], + size_t *out_length); #endif From 6fdc65dea61edb79ba3c05b7bcebc679b51731e1 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Mon, 10 Aug 2020 15:19:32 -0700 Subject: [PATCH 21/75] argon2 --- nix/pkgs/default.nix | 2 +- nix/pkgs/urcrypt/default.nix | 4 +- pkg/urbit/jets/e/argon2.c | 172 +++++++++++++++++++---------------- pkg/urcrypt/Makefile | 2 +- pkg/urcrypt/urcrypt.c | 114 +++++++++++++++++++++++ pkg/urcrypt/urcrypt.h | 25 +++++ 6 files changed, 239 insertions(+), 80 deletions(-) diff --git a/nix/pkgs/default.nix b/nix/pkgs/default.nix index 9404cbd9e..db5120376 100644 --- a/nix/pkgs/default.nix +++ b/nix/pkgs/default.nix @@ -16,7 +16,7 @@ let urcrypt = import ./urcrypt { inherit pkgs ge-additions; - inherit (deps) ed25519; + inherit (deps) ed25519 argon2; }; libaes_siv = import ./libaes_siv { diff --git a/nix/pkgs/urcrypt/default.nix b/nix/pkgs/urcrypt/default.nix index 592e46cfe..e81413ca0 100644 --- a/nix/pkgs/urcrypt/default.nix +++ b/nix/pkgs/urcrypt/default.nix @@ -1,9 +1,9 @@ -{ pkgs, ge-additions, ed25519 }: +{ pkgs, ed25519, ge-additions, argon2 }: pkgs.stdenv.mkDerivation rec { name = "urcrypt"; builder = ./builder.sh; src = ../../../pkg/urcrypt; - buildInputs = [ pkgs.openssl ed25519 ge-additions ]; + buildInputs = [ pkgs.openssl ed25519 ge-additions argon2 ]; } diff --git a/pkg/urbit/jets/e/argon2.c b/pkg/urbit/jets/e/argon2.c index 0663304b7..55764b9f6 100644 --- a/pkg/urbit/jets/e/argon2.c +++ b/pkg/urbit/jets/e/argon2.c @@ -2,104 +2,124 @@ ** */ #include "all.h" - -#include +#include /* helpers */ - int argon2_alloc(uint8_t** output, size_t bytes) + static c3_t + _cqear_unpack_word(c3_w *out, u3_atom in) { - *output = u3a_malloc(bytes); - return (NULL != output); + if ( u3r_met(5, in) > 1 ) { + return 0; + } + else { + *out = u3r_word(0, in); + return 1; + } } - void argon2_free(uint8_t* memory, size_t bytes) + static c3_t + _cqear_unpack_size(size_t *out, u3_atom in) { - u3a_free(memory); + c3_w out_w; + if ( _cqear_unpack_word(&out_w, in) ) { + *out = out_w; + return 1; + } + else { + return 0; + } + } + + static c3_t + _cqear_unpack_type(urcrypt_argon2_type *out, u3_atom in) + { + switch ( in ) { + default: + return 0; + case c3__d: + *out = urcrypt_argon2_d; + return 1; + case c3__i: + *out = urcrypt_argon2_i; + return 1; + case c3__id: + *out = urcrypt_argon2_id; + return 1; + case c3__u: + *out = urcrypt_argon2_u; + return 1; + } + } + + static c3_y* + _cqear_unpack_bytes(size_t size, u3_atom in) + { + c3_y* out = u3a_malloc(size); + u3r_bytes(0, (c3_w) size, out, in); + return out; } /* functions */ - u3_noun - u3qe_argon2( // configuration params, + static u3_atom + _cqe_argon2( // configuration params, u3_atom out, u3_atom type, u3_atom version, u3_atom threads, u3_atom mem_cost, u3_atom time_cost, u3_atom wik, u3_atom key, u3_atom wix, u3_atom extra, // input params u3_atom wid, u3_atom dat, u3_atom wis, u3_atom sat ) { - c3_assert( _(u3a_is_cat(out)) && _(u3a_is_cat(type)) && - _(u3a_is_cat(version)) && _(u3a_is_cat(threads)) && - _(u3a_is_cat(mem_cost)) && _(u3a_is_cat(time_cost)) && - _(u3a_is_cat(wik)) && _(u3a_is_cat(wix)) && - _(u3a_is_cat(wid)) && _(u3a_is_cat(wis)) ); + size_t out_sz, key_sz, ex_sz, dat_sz, sat_sz; + c3_w ver_w, ted_w, mem_w, tim_w; + urcrypt_argon2_type typ_u; - // flip endianness for argon2 - key = u3qc_rev(3, wik, key); - extra = u3qc_rev(3, wix, extra); - dat = u3qc_rev(3, wid, dat); - sat = u3qc_rev(3, wis, sat); - - // atoms to byte arrays - c3_y bytes_key[wik]; - u3r_bytes(0, wik, bytes_key, key); - c3_y bytes_extra[wix]; - u3r_bytes(0, wix, bytes_extra, extra); - c3_y bytes_dat[wid]; - u3r_bytes(0, wid, bytes_dat, dat); - c3_y bytes_sat[wis]; - u3r_bytes(0, wis, bytes_sat, sat); - - c3_y outhash[out]; - argon2_context context = { - outhash, // output array, at least [digest length] in size - out, // digest length - bytes_dat, // password array - wid, // password length - bytes_sat, // salt array - wis, // salt length - bytes_key, wik, // optional secret data - bytes_extra, wix, // optional associated data - time_cost, mem_cost, threads, threads, // performance cost configuration - version, // algorithm version - argon2_alloc, // custom memory allocation function - argon2_free, // custom memory deallocation function - ARGON2_DEFAULT_FLAGS // by default only internal memory is cleared - }; - - int argon_res; - switch ( type ) { - default: - u3l_log("\nunjetted argon2 variant %i\n", type); - u3m_bail(c3__exit); - break; - // - case c3__d: - argon_res = argon2d_ctx(&context); - break; - // - case c3__i: - argon_res = argon2i_ctx(&context); - break; - // - case c3__id: - argon_res = argon2id_ctx(&context); - break; - // - case c3__u: - argon_res = argon2u_ctx(&context); - break; + if ( !(_cqear_unpack_size(&out_sz, out) && + _cqear_unpack_type(&typ_u, type) && + _cqear_unpack_word(&ver_w, version) && + _cqear_unpack_word(&ted_w, threads) && + _cqear_unpack_word(&mem_w, mem_cost) && + _cqear_unpack_word(&tim_w, time_cost) && + _cqear_unpack_size(&key_sz, wik) && + _cqear_unpack_size(&ex_sz, wix) && + _cqear_unpack_size(&dat_sz, wid) && + _cqear_unpack_size(&sat_sz, wis)) ) { + u3l_log("%s\r\n", "argon2-punt"); + return u3_none; } + else { + u3_atom ret; + c3_y *key_y = _cqear_unpack_bytes(key_sz, key), + *ex_y = _cqear_unpack_bytes(ex_sz, extra), + *dat_y = _cqear_unpack_bytes(dat_sz, dat), + *sat_y = _cqear_unpack_bytes(sat_sz, sat), + *out_y = u3a_malloc(out_sz); + const c3_c* err_c = urcrypt_argon2( + typ_u, ver_w, ted_w, mem_w, tim_w, + key_sz, key_y, + ex_sz, ex_y, + dat_sz, dat_y, + sat_sz, sat_y, + out_sz, out_y); - if ( ARGON2_OK != argon_res ) { - u3l_log("\nargon2 error: %s\n", argon2_error_message(argon_res)); - u3m_bail(c3__exit); + u3a_free(key_y); + u3a_free(ex_y); + u3a_free(dat_y); + u3a_free(sat_y); + + if ( NULL == err_c ) { + ret = u3i_bytes(out_sz, out_y); + } + else { + ret = u3_none; + u3l_log("argon2-punt: %s\r\n", err_c); + } + + u3a_free(out_y); + return ret; } - - u3z(key); u3z(extra); u3z(dat); u3z(sat); - return u3kc_rev(3, out, u3i_bytes(out, outhash)); } u3_noun @@ -138,7 +158,7 @@ return u3m_bail(c3__exit); } else { - return u3qe_argon2(out, type, version, + return _cqe_argon2(out, type, version, threads, mem_cost, time_cost, wik, key, wix, extra, wid, dat, wis, sat); diff --git a/pkg/urcrypt/Makefile b/pkg/urcrypt/Makefile index 7b2a0ecc3..045b85416 100644 --- a/pkg/urcrypt/Makefile +++ b/pkg/urcrypt/Makefile @@ -16,7 +16,7 @@ liburcrypt.a: $(SOURCES) liburcrypt.so: $(SOURCES) $(CC) $(CFLAGS) -fPIC -c urcrypt.c -o urcrypt-shared.o $(CC) -shared urcrypt-shared.o -o liburcrypt.so \ - -led25519 -lge-additions -lssl \ + -led25519 -lge-additions -lssl -largon2 \ -Wl,--no-undefined all: liburcrypt.a liburcrypt.so diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index ff1c72135..5f1b12d81 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -249,6 +249,14 @@ _urcrypt_reverse_copy(size_t size, const uint8_t *in, uint8_t *out) { } } +static uint8_t* +_urcrypt_reverse_alloc(size_t size, const uint8_t *in) +{ + uint8_t *out = urcrypt_malloc(size); + _urcrypt_reverse_copy(size, in, out); + return out; +} + static void _urcrypt_reverse_inplace(size_t size, uint8_t *ptr) { size_t i, j; @@ -572,3 +580,109 @@ urcrypt_aes_cbcc_de(const uint8_t *message, out_length); } } + +static int +_urcrypt_argon2_alloc(uint8_t** output, size_t bytes) +{ + *output = urcrypt_malloc(bytes); + return (NULL != output); +} + +static void +_urcrypt_argon2_free(uint8_t* memory, size_t bytes) +{ + urcrypt_free(memory); +} + +// library convention is to have sizes in size_t, but argon2 wants them +// in uint32_t, so here's a helper macro for ensuring equivalence. +#define SZ_32(s) ( sizeof(size_t) <= sizeof(uint32_t) || s <= 0xFFFFFFFF ) + +// returns a constant error message string or NULL for success +// (so user doesn't have to call argon2_error_message) +const char* +urcrypt_argon2(urcrypt_argon2_type type, + uint32_t version, + uint32_t threads, + uint32_t memory_cost, + uint32_t time_cost, + size_t secret_length, + const uint8_t *secret, + size_t associated_length, + const uint8_t *associated, + size_t password_length, + const uint8_t *password, + size_t salt_length, + const uint8_t *salt, + size_t out_length, + uint8_t *out) +{ + if ( !( SZ_32(secret_length) && + SZ_32(associated_length) && + SZ_32(password_length) && + SZ_32(salt_length) && + SZ_32(out_length) ) ) { + return "length > 32 bits"; + } + else { + int (*f)(argon2_context*); + int result; + uint8_t *rsecret, *rassoc, *rpassword, *rsalt; + + switch ( type ) { + default: + return "unknown type"; + case urcrypt_argon2_d: + f = &argon2d_ctx; + break; + case urcrypt_argon2_i: + f = &argon2i_ctx; + break; + case urcrypt_argon2_id: + f = &argon2id_ctx; + break; + case urcrypt_argon2_u: + f = &argon2u_ctx; + break; + } + + rsecret = _urcrypt_reverse_alloc(secret_length, secret); + rassoc = _urcrypt_reverse_alloc(associated_length, associated); + rpassword = _urcrypt_reverse_alloc(password_length, password); + rsalt = _urcrypt_reverse_alloc(salt_length, salt); + argon2_context context = { + out, // output array, at least [digest length] in size + out_length, // digest length + rpassword, // password array + password_length, // password length + rsalt, // salt array + salt_length, // salt length + rsecret, // optional secret data + secret_length, + rassoc, // optional associated data + associated_length, + time_cost, // performance cost configuration + memory_cost, + threads, + threads, + version, // algorithm version + &_urcrypt_argon2_alloc,// custom memory allocation function + &_urcrypt_argon2_free, // custom memory deallocation function + ARGON2_DEFAULT_FLAGS // by default only internal memory is cleared + }; + + result = (*f)(&context); + urcrypt_free(rsecret); + urcrypt_free(rassoc); + urcrypt_free(rpassword); + urcrypt_free(rsalt); + + if ( ARGON2_OK != result ) { + return argon2_error_message(result); + } + else { + _urcrypt_reverse_inplace(out_length, out); + return NULL; + } + } +} diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 89699d6b0..da0b2beb3 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -12,6 +12,8 @@ #include #include +#include + typedef void *(*urcrypt_malloc_t)(size_t); typedef void *(*urcrypt_realloc_t)(void*, size_t); typedef void (*urcrypt_free_t)(void*); @@ -104,4 +106,27 @@ uint8_t* urcrypt_aes_cbcc_de(const uint8_t *message, const uint8_t ivec[16], size_t *out_length); +typedef enum urcrypt_argon2_type { + urcrypt_argon2_d = 0, + urcrypt_argon2_i = 1, + urcrypt_argon2_id = 2, + urcrypt_argon2_u = 10, +} urcrypt_argon2_type; + +const char* urcrypt_argon2(urcrypt_argon2_type type, + uint32_t version, + uint32_t threads, + uint32_t memory_cost, + uint32_t time_cost, + size_t secret_length, + const uint8_t *secret, + size_t associated_length, + const uint8_t *associated, + size_t password_length, + const uint8_t *password, + size_t salt_length, + const uint8_t *salt, + size_t out_length, + uint8_t *out); + #endif From 6deec6229288d5dccd7cc7ae30437db84f4b3961 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 11 Aug 2020 11:12:25 -0700 Subject: [PATCH 22/75] rename u3r_unpack->u3r_bytes_fit, u3r_unpack_alloc->u3r_bytes_all --- pkg/urbit/include/noun/retrieve.h | 13 +++++++------ pkg/urbit/jets/e/aes_cbc.c | 2 +- pkg/urbit/jets/e/ed_add_double_scalarmult.c | 8 ++++---- .../jets/e/ed_add_scalarmult_scalarmult_base.c | 6 +++--- pkg/urbit/jets/e/ed_point_add.c | 4 ++-- pkg/urbit/jets/e/ed_puck.c | 2 +- pkg/urbit/jets/e/ed_scalarmult.c | 4 ++-- pkg/urbit/jets/e/ed_scalarmult_base.c | 2 +- pkg/urbit/jets/e/ed_shar.c | 4 ++-- pkg/urbit/jets/e/ed_sign.c | 4 ++-- pkg/urbit/jets/e/ed_veri.c | 6 +++--- pkg/urbit/noun/retrieve.c | 8 ++++---- 12 files changed, 32 insertions(+), 31 deletions(-) diff --git a/pkg/urbit/include/noun/retrieve.h b/pkg/urbit/include/noun/retrieve.h index 295ad622a..8eb6ed880 100644 --- a/pkg/urbit/include/noun/retrieve.h +++ b/pkg/urbit/include/noun/retrieve.h @@ -346,21 +346,22 @@ c3_y* c_y, u3_atom d); - /* u3r_unpack(): + /* u3r_bytes_fit(): ** ** Copy (len_w) bytes of (a) into (buf_y) if it fits, returning overage */ c3_w - u3r_unpack(c3_w len_w, - c3_y *buf_y, - u3_atom a); + u3r_bytes_fit(c3_w len_w, + c3_y* buf_y, + u3_atom a); - /* u3r_unpack_alloc(): + /* u3r_bytes_all(): ** ** Allocate a new byte array with all the bytes of (a) */ c3_y* - u3r_unpack_alloc(c3_w* len_w, u3_atom a); + u3r_bytes_all(c3_w* len_w, + u3_atom a); /* u3r_chop(): ** diff --git a/pkg/urbit/jets/e/aes_cbc.c b/pkg/urbit/jets/e/aes_cbc.c index 19e129132..06d9df381 100644 --- a/pkg/urbit/jets/e/aes_cbc.c +++ b/pkg/urbit/jets/e/aes_cbc.c @@ -25,7 +25,7 @@ typedef c3_y* (*urcrypt_cbc)(const c3_y*, u3r_bytes(0, 16, iv_y, iv); - msg_y = u3r_unpack_alloc(&met_w, msg); + msg_y = u3r_bytes_all(&met_w, msg); out_y = (*low_f)(msg_y, met_w, key_y, iv_y, &len); u3a_free(msg_y); diff --git a/pkg/urbit/jets/e/ed_add_double_scalarmult.c b/pkg/urbit/jets/e/ed_add_double_scalarmult.c index e49a0bf16..af73d5964 100644 --- a/pkg/urbit/jets/e/ed_add_double_scalarmult.c +++ b/pkg/urbit/jets/e/ed_add_double_scalarmult.c @@ -14,10 +14,10 @@ { c3_y a_y[32], b_y[32], c_y[32], d_y[32], out_y[32]; - if ( (0 != u3r_unpack(32, a_y, a)) || - (0 != u3r_unpack(32, b_y, b)) || - (0 != u3r_unpack(32, c_y, c)) || - (0 != u3r_unpack(32, d_y, d)) || + if ( (0 != u3r_bytes_fit(32, a_y, a)) || + (0 != u3r_bytes_fit(32, b_y, b)) || + (0 != u3r_bytes_fit(32, c_y, c)) || + (0 != u3r_bytes_fit(32, d_y, d)) || (0 != urcrypt_ed_add_double_scalarmult(a_y, b_y, c_y, d_y, out_y)) ) { return u3_none; } diff --git a/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c b/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c index 0a0e2d2d4..2db437aec 100644 --- a/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c +++ b/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c @@ -13,9 +13,9 @@ { c3_y a_y[32], b_y[32], c_y[32], out_y[32]; - if ( (0 != u3r_unpack(32, a_y, a)) || - (0 != u3r_unpack(32, b_y, b)) || - (0 != u3r_unpack(32, c_y, c)) || + if ( (0 != u3r_bytes_fit(32, a_y, a)) || + (0 != u3r_bytes_fit(32, b_y, b)) || + (0 != u3r_bytes_fit(32, c_y, c)) || (0 != urcrypt_ed_add_scalarmult_scalarmult_base(a_y, b_y, c_y, out_y)) ) { return u3_none; } diff --git a/pkg/urbit/jets/e/ed_point_add.c b/pkg/urbit/jets/e/ed_point_add.c index a93f798b3..2a5da2938 100644 --- a/pkg/urbit/jets/e/ed_point_add.c +++ b/pkg/urbit/jets/e/ed_point_add.c @@ -13,8 +13,8 @@ { c3_y a_y[32], b_y[32], out_y[32]; - if ( (0 != u3r_unpack(32, a_y, a)) || - (0 != u3r_unpack(32, b_y, b)) || + if ( (0 != u3r_bytes_fit(32, a_y, a)) || + (0 != u3r_bytes_fit(32, b_y, b)) || (0 != urcrypt_ed_point_add(a_y, b_y, out_y)) ) { return u3_none; } diff --git a/pkg/urbit/jets/e/ed_puck.c b/pkg/urbit/jets/e/ed_puck.c index 8257b937e..436650e1a 100644 --- a/pkg/urbit/jets/e/ed_puck.c +++ b/pkg/urbit/jets/e/ed_puck.c @@ -11,7 +11,7 @@ { c3_y sed_y[32]; - if ( 0 != u3r_unpack(32, sed_y, sed) ) { + if ( 0 != u3r_bytes_fit(32, sed_y, sed) ) { // hoon explicitly crashes on mis-size return u3m_bail(c3__exit); } diff --git a/pkg/urbit/jets/e/ed_scalarmult.c b/pkg/urbit/jets/e/ed_scalarmult.c index 224fba3f6..97c88c4f6 100644 --- a/pkg/urbit/jets/e/ed_scalarmult.c +++ b/pkg/urbit/jets/e/ed_scalarmult.c @@ -12,8 +12,8 @@ { c3_y a_y[32], b_y[32], out_y[32]; - if ( (0 != u3r_unpack(32, a_y, a)) || - (0 != u3r_unpack(32, b_y, b)) || + if ( (0 != u3r_bytes_fit(32, a_y, a)) || + (0 != u3r_bytes_fit(32, b_y, b)) || (0 != urcrypt_ed_scalarmult(a_y, b_y, out_y)) ) { // hoon does not check size of inputs return u3_none; diff --git a/pkg/urbit/jets/e/ed_scalarmult_base.c b/pkg/urbit/jets/e/ed_scalarmult_base.c index c1b641bd9..9756e65a5 100644 --- a/pkg/urbit/jets/e/ed_scalarmult_base.c +++ b/pkg/urbit/jets/e/ed_scalarmult_base.c @@ -11,7 +11,7 @@ { c3_y a_y[32]; - if ( 0 != u3r_unpack(32, a_y, a) ) { + if ( 0 != u3r_bytes_fit(32, a_y, a) ) { return u3_none; } else { diff --git a/pkg/urbit/jets/e/ed_shar.c b/pkg/urbit/jets/e/ed_shar.c index 5ed28df37..7db4e6175 100644 --- a/pkg/urbit/jets/e/ed_shar.c +++ b/pkg/urbit/jets/e/ed_shar.c @@ -9,11 +9,11 @@ { c3_y pub_y[32], sek_y[32]; - if ( 0 != u3r_unpack(32, pub_y, pub) ) { + if ( 0 != u3r_bytes_fit(32, pub_y, pub) ) { // pub is not size checked in the hoon return u3_none; } - else if ( 0 != u3r_unpack(32, sek_y, sek) ) { + else if ( 0 != u3r_bytes_fit(32, sek_y, sek) ) { // sek explicitly bails through suck return u3m_bail(c3__exit); } diff --git a/pkg/urbit/jets/e/ed_sign.c b/pkg/urbit/jets/e/ed_sign.c index e6a153ba1..09fd317d0 100644 --- a/pkg/urbit/jets/e/ed_sign.c +++ b/pkg/urbit/jets/e/ed_sign.c @@ -12,14 +12,14 @@ { c3_y sed_y[32]; - if ( 0 != u3r_unpack(32, sed_y, b) ) { + if ( 0 != u3r_bytes_fit(32, sed_y, b) ) { // hoon calls suck, which calls puck, which crashes return u3m_bail(c3__exit); } else { c3_y sig_y[64]; c3_w met_w; - c3_y* mes_y = u3r_unpack_alloc(&met_w, a); + c3_y* mes_y = u3r_bytes_all(&met_w, a); urcrypt_ed_sign(mes_y, met_w, sed_y, sig_y); u3a_free(mes_y); diff --git a/pkg/urbit/jets/e/ed_veri.c b/pkg/urbit/jets/e/ed_veri.c index f4bf30ac3..c4a4b6f2d 100644 --- a/pkg/urbit/jets/e/ed_veri.c +++ b/pkg/urbit/jets/e/ed_veri.c @@ -13,14 +13,14 @@ { c3_y sig_y[64], pub_y[32]; - if ( (0 != u3r_unpack(64, sig_y, s)) || - (0 != u3r_unpack(32, pub_y, pk)) ) { + if ( (0 != u3r_bytes_fit(64, sig_y, s)) || + (0 != u3r_bytes_fit(32, pub_y, pk)) ) { // hoon checks sizes, but weirdly and without crashes return u3_none; } else { c3_w met_w; - c3_y* mes_y = u3r_unpack_alloc(&met_w, m); + c3_y* mes_y = u3r_bytes_all(&met_w, m); c3_t val_t = urcrypt_ed_veri(mes_y, met_w, pub_y, sig_y); u3a_free(mes_y); diff --git a/pkg/urbit/noun/retrieve.c b/pkg/urbit/noun/retrieve.c index b8f875b86..8354d4243 100644 --- a/pkg/urbit/noun/retrieve.c +++ b/pkg/urbit/noun/retrieve.c @@ -1076,12 +1076,12 @@ u3r_bytes(c3_w a_w, } } -/* u3r_unpack(): +/* u3r_bytes_fit(): ** ** Copy (len_w) bytes of (a) into (buf_y) if it fits, returning overage */ c3_w -u3r_unpack(c3_w len_w, c3_y *buf_y, u3_atom a) +u3r_bytes_fit(c3_w len_w, c3_y *buf_y, u3_atom a) { c3_w met_w = u3r_met(3, a); if ( met_w <= len_w ) { @@ -1093,12 +1093,12 @@ u3r_unpack(c3_w len_w, c3_y *buf_y, u3_atom a) } } -/* u3r_unpack_alloc(): +/* u3r_bytes_all(): ** ** Allocate a new byte array with all the bytes of (a) */ c3_y* -u3r_unpack_alloc(c3_w* len_w, u3_atom a) +u3r_bytes_all(c3_w* len_w, u3_atom a) { c3_w met_w = u3r_met(3, a); c3_y* a_y = u3a_malloc(met_w); From 3f917d784817efaa7dea3c3933e522fc6dcb1b2b Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 11 Aug 2020 13:04:04 -0700 Subject: [PATCH 23/75] move argon unpacking helpers to u3r --- pkg/urbit/include/noun/retrieve.h | 31 ++++++++++++- pkg/urbit/jets/e/argon2.c | 50 ++++++--------------- pkg/urbit/noun/retrieve.c | 74 ++++++++++++++++++++++++++++--- 3 files changed, 110 insertions(+), 45 deletions(-) diff --git a/pkg/urbit/include/noun/retrieve.h b/pkg/urbit/include/noun/retrieve.h index 8eb6ed880..33a5f6c3c 100644 --- a/pkg/urbit/include/noun/retrieve.h +++ b/pkg/urbit/include/noun/retrieve.h @@ -348,16 +348,26 @@ /* u3r_bytes_fit(): ** - ** Copy (len_w) bytes of (a) into (buf_y) if it fits, returning overage + ** Copy (len_w) bytes of (a) into (buf_y) if it fits, returning overage. */ c3_w u3r_bytes_fit(c3_w len_w, c3_y* buf_y, u3_atom a); + /* u3r_bytes_alloc(): + ** + ** Copy (len_w) bytes starting at (a_w) from (b) into a fresh allocation. + */ + c3_y* + u3r_bytes_alloc(c3_w a_w, + c3_w len_w, + u3_atom b); + /* u3r_bytes_all(): ** - ** Allocate a new byte array with all the bytes of (a) + ** Allocate and return a new byte array with all the bytes of (a), + ** storing the length in (len_w). */ c3_y* u3r_bytes_all(c3_w* len_w, @@ -393,6 +403,23 @@ u3r_word(c3_w a_w, u3_atom b); + + /* u3r_word_fit(): + ** + ** Fill (out_w) with (a) if it fits, returning success. + */ + c3_t + u3r_word_fit(c3_w* out_w, + u3_atom a); + + /* u3r_size_fit(): + ** + ** Fill (out) with (a) if it fits, returning success. + */ + c3_t + u3r_size_fit(size_t *out_p, + u3_atom a); + /* u3r_chub(): ** ** Return double-word (a_w) of (b). diff --git a/pkg/urbit/jets/e/argon2.c b/pkg/urbit/jets/e/argon2.c index 55764b9f6..f2c04fbd0 100644 --- a/pkg/urbit/jets/e/argon2.c +++ b/pkg/urbit/jets/e/argon2.c @@ -7,31 +7,6 @@ /* helpers */ - static c3_t - _cqear_unpack_word(c3_w *out, u3_atom in) - { - if ( u3r_met(5, in) > 1 ) { - return 0; - } - else { - *out = u3r_word(0, in); - return 1; - } - } - - static c3_t - _cqear_unpack_size(size_t *out, u3_atom in) - { - c3_w out_w; - if ( _cqear_unpack_word(&out_w, in) ) { - *out = out_w; - return 1; - } - else { - return 0; - } - } - static c3_t _cqear_unpack_type(urcrypt_argon2_type *out, u3_atom in) { @@ -56,9 +31,9 @@ static c3_y* _cqear_unpack_bytes(size_t size, u3_atom in) { - c3_y* out = u3a_malloc(size); - u3r_bytes(0, (c3_w) size, out, in); - return out; + c3_w len_w = size; + c3_assert(size == len_w); + return u3r_bytes_alloc(0, len_w, in); } /* functions @@ -76,16 +51,16 @@ c3_w ver_w, ted_w, mem_w, tim_w; urcrypt_argon2_type typ_u; - if ( !(_cqear_unpack_size(&out_sz, out) && + if ( !(u3r_size_fit(&out_sz, out) && _cqear_unpack_type(&typ_u, type) && - _cqear_unpack_word(&ver_w, version) && - _cqear_unpack_word(&ted_w, threads) && - _cqear_unpack_word(&mem_w, mem_cost) && - _cqear_unpack_word(&tim_w, time_cost) && - _cqear_unpack_size(&key_sz, wik) && - _cqear_unpack_size(&ex_sz, wix) && - _cqear_unpack_size(&dat_sz, wid) && - _cqear_unpack_size(&sat_sz, wis)) ) { + u3r_word_fit(&ver_w, version) && + u3r_word_fit(&ted_w, threads) && + u3r_word_fit(&mem_w, mem_cost) && + u3r_word_fit(&tim_w, time_cost) && + u3r_size_fit(&key_sz, wik) && + u3r_size_fit(&ex_sz, wix) && + u3r_size_fit(&dat_sz, wid) && + u3r_size_fit(&sat_sz, wis)) ) { u3l_log("%s\r\n", "argon2-punt"); return u3_none; } @@ -96,6 +71,7 @@ *dat_y = _cqear_unpack_bytes(dat_sz, dat), *sat_y = _cqear_unpack_bytes(sat_sz, sat), *out_y = u3a_malloc(out_sz); + const c3_c* err_c = urcrypt_argon2( typ_u, ver_w, ted_w, mem_w, tim_w, key_sz, key_y, diff --git a/pkg/urbit/noun/retrieve.c b/pkg/urbit/noun/retrieve.c index 8354d4243..345c3add7 100644 --- a/pkg/urbit/noun/retrieve.c +++ b/pkg/urbit/noun/retrieve.c @@ -1093,18 +1093,30 @@ u3r_bytes_fit(c3_w len_w, c3_y *buf_y, u3_atom a) } } +/* u3r_bytes_alloc(): +** +** Copy (len_w) bytes starting at (a_w) from (b) into a fresh allocation. +*/ +c3_y* +u3r_bytes_alloc(c3_w a_w, + c3_w len_w, + u3_atom b) +{ + c3_y* b_y = u3a_malloc(len_w); + u3r_bytes(a_w, a_w + len_w, b_y, b); + return b_y; +} + /* u3r_bytes_all(): ** -** Allocate a new byte array with all the bytes of (a) +** Allocate and return a new byte array with all the bytes of (a), +** storing the length in (len_w). */ c3_y* u3r_bytes_all(c3_w* len_w, u3_atom a) { - c3_w met_w = u3r_met(3, a); - c3_y* a_y = u3a_malloc(met_w); - u3r_bytes(0, met_w, a_y, a); - *len_w = met_w; - return a_y; + c3_w met_w = *len_w = u3r_met(3, a); + return u3r_bytes_alloc(0, met_w, a); } /* u3r_mp(): @@ -1165,6 +1177,56 @@ u3r_word(c3_w a_w, } } +/* u3r_word_fit(): +** +** Fill (out_w) with (a) if it fits, returning success. +*/ +c3_t +u3r_word_fit(c3_w *out_w, u3_atom a) +{ + if ( u3r_met(5, a) > 1 ) { + return 0; + } + else { + *out_w = u3r_word(0, a); + return 1; + } +} + +/* u3r_size_fit(): +** +** Fill (out) with (a) if it fits, returning success. +*/ +c3_t +u3r_size_fit(size_t *out_p, u3_atom a) +{ + if ( 0 == a ) { + *out_p = 0; + return 1; + } + else { + c3_w met_w = u3r_met(3, a); + if ( met_w > sizeof(size_t) ) { + return 0; + } + else { + u3r_bytes(0, sizeof(size_t), (c3_y*) out_p, a); +#if c3_endian == c3_endian_big + { // reverse those bytes + c3_w i_w, j_w; + c3_y tmp, *s_y = (c3_y*) out_p; + for ( i_w = 0, j_w = sizeof(size_t) - 1; i_w < j_w; ++i_w, --j_w ) { + tmp = s_y[i_w]; + s_y[i_w] = s_y[j_w]; + s_y[j_w] = tmp; + } + } +#endif + return 1; + } + } +} + /* u3r_chub(): ** ** Return double-word (a_w) of (b). From ab8d0dd7efb3ccd3571f365184fa513c3b6cbbe5 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 11 Aug 2020 15:01:24 -0700 Subject: [PATCH 24/75] blake2b --- pkg/urbit/include/noun/retrieve.h | 8 ---- pkg/urbit/jets/e/argon2.c | 43 ++++++++------------ pkg/urbit/jets/e/blake.c | 67 +++++++++++++------------------ pkg/urbit/noun/retrieve.c | 34 ---------------- pkg/urcrypt/urcrypt.c | 33 +++++++++++++++ pkg/urcrypt/urcrypt.h | 8 ++++ 6 files changed, 86 insertions(+), 107 deletions(-) diff --git a/pkg/urbit/include/noun/retrieve.h b/pkg/urbit/include/noun/retrieve.h index 33a5f6c3c..3fc20a988 100644 --- a/pkg/urbit/include/noun/retrieve.h +++ b/pkg/urbit/include/noun/retrieve.h @@ -412,14 +412,6 @@ u3r_word_fit(c3_w* out_w, u3_atom a); - /* u3r_size_fit(): - ** - ** Fill (out) with (a) if it fits, returning success. - */ - c3_t - u3r_size_fit(size_t *out_p, - u3_atom a); - /* u3r_chub(): ** ** Return double-word (a_w) of (b). diff --git a/pkg/urbit/jets/e/argon2.c b/pkg/urbit/jets/e/argon2.c index f2c04fbd0..9c145e00f 100644 --- a/pkg/urbit/jets/e/argon2.c +++ b/pkg/urbit/jets/e/argon2.c @@ -28,14 +28,6 @@ } } - static c3_y* - _cqear_unpack_bytes(size_t size, u3_atom in) - { - c3_w len_w = size; - c3_assert(size == len_w); - return u3r_bytes_alloc(0, len_w, in); - } - /* functions */ @@ -47,38 +39,37 @@ // input params u3_atom wid, u3_atom dat, u3_atom wis, u3_atom sat ) { - size_t out_sz, key_sz, ex_sz, dat_sz, sat_sz; - c3_w ver_w, ted_w, mem_w, tim_w; urcrypt_argon2_type typ_u; + c3_w out_w, wik_w, wix_w, wid_w, wis_w, ver_w, ted_w, mem_w, tim_w; - if ( !(u3r_size_fit(&out_sz, out) && + if ( !(u3r_word_fit(&out_w, out) && _cqear_unpack_type(&typ_u, type) && u3r_word_fit(&ver_w, version) && u3r_word_fit(&ted_w, threads) && u3r_word_fit(&mem_w, mem_cost) && u3r_word_fit(&tim_w, time_cost) && - u3r_size_fit(&key_sz, wik) && - u3r_size_fit(&ex_sz, wix) && - u3r_size_fit(&dat_sz, wid) && - u3r_size_fit(&sat_sz, wis)) ) { + u3r_word_fit(&wik_w, wik) && + u3r_word_fit(&wix_w, wix) && + u3r_word_fit(&wid_w, wid) && + u3r_word_fit(&wis_w, wis)) ) { u3l_log("%s\r\n", "argon2-punt"); return u3_none; } else { u3_atom ret; - c3_y *key_y = _cqear_unpack_bytes(key_sz, key), - *ex_y = _cqear_unpack_bytes(ex_sz, extra), - *dat_y = _cqear_unpack_bytes(dat_sz, dat), - *sat_y = _cqear_unpack_bytes(sat_sz, sat), - *out_y = u3a_malloc(out_sz); + c3_y *key_y = u3r_bytes_alloc(0, wik_w, key), + *ex_y = u3r_bytes_alloc(0, wix_w, extra), + *dat_y = u3r_bytes_alloc(0, wid_w, dat), + *sat_y = u3r_bytes_alloc(0, wis_w, sat), + *out_y = u3a_malloc(out_w); const c3_c* err_c = urcrypt_argon2( typ_u, ver_w, ted_w, mem_w, tim_w, - key_sz, key_y, - ex_sz, ex_y, - dat_sz, dat_y, - sat_sz, sat_y, - out_sz, out_y); + wik_w, key_y, + wix_w, ex_y, + wid_w, dat_y, + wis_w, sat_y, + out_w, out_y); u3a_free(key_y); u3a_free(ex_y); @@ -86,7 +77,7 @@ u3a_free(sat_y); if ( NULL == err_c ) { - ret = u3i_bytes(out_sz, out_y); + ret = u3i_bytes(out_w, out_y); } else { ret = u3_none; diff --git a/pkg/urbit/jets/e/blake.c b/pkg/urbit/jets/e/blake.c index eece09ab5..eb36ba87d 100644 --- a/pkg/urbit/jets/e/blake.c +++ b/pkg/urbit/jets/e/blake.c @@ -2,52 +2,42 @@ ** */ #include "all.h" - -#include -#include +#include /* functions */ - u3_noun - u3qe_blake(u3_atom wid, u3_atom dat, + static u3_atom + _cqe_blake(u3_atom wid, u3_atom dat, u3_atom wik, u3_atom dak, u3_atom out) { - c3_assert(_(u3a_is_cat(wid)) && _(u3a_is_cat(wik)) && _(u3a_is_cat(out))); - - // flip endianness for the internal blake2b function - dat = u3qc_rev(3, wid, dat); - dak = u3qc_rev(3, wik, dak); - - c3_y* dat_y = (c3_y*)u3a_malloc(wid); - u3r_bytes(0, wid, (void*)dat_y, dat); - - c3_y* dak_y = (c3_y*)u3a_malloc(wik); - u3r_bytes(0, wik, (void*)dak_y, dak); - - int ret; - c3_y out_y[64]; - ret = blake2b(out_y, // OUT: output - out, // IN: max output size - dat_y, // IN: msg body - wid, // IN: msg len - dak_y, // IN: key body - wik); // IN: key len - - /* free() BEFORE checking error code; - we don't want to leak memory if we return early - */ - u3a_free(dat_y); - u3a_free(dak_y); - - if ( 0 != ret ) - { - u3l_log("\rblake jet: cryto lib error\n"); - return u3m_bail(c3__exit); + c3_w wid_w; + if ( !u3r_word_fit(&wid_w, wid) ) { + // impossible to represent an atom this large + return u3m_bail(c3__fail); } + else { + // the hoon adjusts these widths to its liking + int err; + u3_atom ret; + c3_y out_y[64], dak_y[64]; + c3_w wik_w = c3_min(wik, 64), + out_w = c3_max(1, c3_min(out, 64)); + c3_y *dat_y = u3r_bytes_alloc(0, wid_w, dat); - return u3kc_rev(3, out, u3i_bytes(out, out_y)); + u3r_bytes(0, wik_w, dak_y, dak); + err = urcrypt_blake2(wid_w, dat_y, wik_w, dak_y, out_w, out_y); + u3a_free(dat_y); + + if ( 0 == err ) { + return u3i_bytes(out_w, out_y); + } + else { + u3l_log("%s\r\n", "blake2-punt: library error"); + return u3_none; + } + } } u3_noun @@ -64,9 +54,8 @@ u3r_cell(key, &wik, &dak) || u3ud(wik) || u3ud(dak) || u3ud(out) ) { - u3l_log("\rblake jet: arguments error\n"); return u3m_bail(c3__exit); } else { - return u3qe_blake(wid, dat, wik, dak, out); + return _cqe_blake(wid, dat, wik, dak, out); } } diff --git a/pkg/urbit/noun/retrieve.c b/pkg/urbit/noun/retrieve.c index 345c3add7..df3802a78 100644 --- a/pkg/urbit/noun/retrieve.c +++ b/pkg/urbit/noun/retrieve.c @@ -1193,40 +1193,6 @@ u3r_word_fit(c3_w *out_w, u3_atom a) } } -/* u3r_size_fit(): -** -** Fill (out) with (a) if it fits, returning success. -*/ -c3_t -u3r_size_fit(size_t *out_p, u3_atom a) -{ - if ( 0 == a ) { - *out_p = 0; - return 1; - } - else { - c3_w met_w = u3r_met(3, a); - if ( met_w > sizeof(size_t) ) { - return 0; - } - else { - u3r_bytes(0, sizeof(size_t), (c3_y*) out_p, a); -#if c3_endian == c3_endian_big - { // reverse those bytes - c3_w i_w, j_w; - c3_y tmp, *s_y = (c3_y*) out_p; - for ( i_w = 0, j_w = sizeof(size_t) - 1; i_w < j_w; ++i_w, --j_w ) { - tmp = s_y[i_w]; - s_y[i_w] = s_y[j_w]; - s_y[j_w] = tmp; - } - } -#endif - return 1; - } - } -} - /* u3r_chub(): ** ** Return double-word (a_w) of (b). diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 5f1b12d81..f878f6a9c 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -686,3 +686,36 @@ urcrypt_argon2(urcrypt_argon2_type type, } } } + +int +urcrypt_blake2(size_t message_length, + const uint8_t *message, + size_t key_length, + const uint8_t key[64], + size_t out_length, + uint8_t *out) +{ + int ret; + uint8_t rkey[64]; + uint8_t *rmessage; + + if ( key_length > 64 ) { + return -1; + } + + rmessage = _urcrypt_reverse_alloc(message_length, message); + _urcrypt_reverse_copy(key_length, key, rkey); + ret = blake2b(out, out_length, + rmessage, message_length, + rkey, key_length); + + urcrypt_free(rmessage); + + if ( 0 != ret ) { + return -1; + } + else { + _urcrypt_reverse_inplace(out_length, out); + return 0; + } +} diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index da0b2beb3..faa474cbf 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -13,6 +13,7 @@ #include #include +#include typedef void *(*urcrypt_malloc_t)(size_t); typedef void *(*urcrypt_realloc_t)(void*, size_t); @@ -129,4 +130,11 @@ const char* urcrypt_argon2(urcrypt_argon2_type type, size_t out_length, uint8_t *out); +int urcrypt_blake2(size_t message_length, + const uint8_t *message, + size_t key_length, + const uint8_t key[64], + size_t out_length, + uint8_t *out); + #endif From ca14af11a7334dc40d25804b78832b55d8285524 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 18 Aug 2020 10:31:50 -0700 Subject: [PATCH 25/75] cleaning up a bit, rethinking things --- pkg/urbit/jets/e/aes_siv.c | 2 -- pkg/urcrypt/urcrypt.c | 47 ++++++++++++++++++-------------------- pkg/urcrypt/urcrypt.h | 31 +++++++++++++++++++++++-- 3 files changed, 51 insertions(+), 29 deletions(-) diff --git a/pkg/urbit/jets/e/aes_siv.c b/pkg/urbit/jets/e/aes_siv.c index b31ab84cf..80ad62ab6 100644 --- a/pkg/urbit/jets/e/aes_siv.c +++ b/pkg/urbit/jets/e/aes_siv.c @@ -3,8 +3,6 @@ */ #include "all.h" -#include - #include "aes_siv.h" /* functions diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index f878f6a9c..d15ad2ae2 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -5,12 +5,12 @@ static urcrypt_malloc_t urcrypt_malloc_ptr; static urcrypt_realloc_t urcrypt_realloc_ptr; static urcrypt_free_t urcrypt_free_ptr; -void* urcrypt_malloc(size_t len) +static void* _urcrypt_malloc(size_t len) { return (*urcrypt_malloc_ptr)(len); } -void* urcrypt_realloc(void *ptr, size_t len) +static void* _urcrypt_realloc(void *ptr, size_t len) { return (*urcrypt_realloc_ptr)(ptr, len); } @@ -21,48 +21,47 @@ void urcrypt_free(void *ptr) } static void* -urcrypt_malloc_ssl(size_t len +_urcrypt_malloc_ssl(size_t len #if OPENSSL_VERSION_NUMBER >= 0x10100000L , const char* file, int line #endif -) { return urcrypt_malloc(len); } +) { return _urcrypt_malloc(len); } static void* -urcrypt_realloc_ssl(void* ptr, size_t len +_urcrypt_realloc_ssl(void* ptr, size_t len #if OPENSSL_VERSION_NUMBER >= 0x10100000L , const char* file, int line #endif -) { return urcrypt_realloc(ptr, len); } +) { return _urcrypt_realloc(ptr, len); } static void -urcrypt_free_ssl(void* ptr +_urcrypt_free_ssl(void* ptr #if OPENSSL_VERSION_NUMBER >= 0x10100000L , const char* file, int line #endif ) { urcrypt_free(ptr); } -/* IMPORTANT: it is an error (undefined behavior) to call functions in - * this library without first calling urcrypt_init() exactly once. - */ int -urcrypt_init(urcrypt_malloc_t m, urcrypt_realloc_t r, urcrypt_free_t f) +urcrypt_init(_urcrypt_malloc_t m, _urcrypt_realloc_t r, urcrypt_free_t f) { if ( initialized ) { return -1; } + else if ( (NULL == m) || (NULL == r) || (NULL == f) ) { + return -2; + } else { initialized = true; - urcrypt_malloc_ptr = ( NULL == m ) ? &malloc : m; - urcrypt_realloc_ptr = ( NULL == r ) ? &realloc : r; - urcrypt_free_ptr = ( NULL == f ) ? &free : f; - - if ( CRYPTO_set_mem_functions(&urcrypt_malloc_ssl, - &urcrypt_realloc_ssl, - &urcrypt_free_ssl) ) { + urcrypt_malloc_ptr = m; + urcrypt_realloc_ptr = r; + urcrypt_free_ptr = f; + if ( CRYPTO_set_mem_functions(&_urcrypt_malloc_ssl, + &_urcrypt_realloc_ssl, + &_urcrypt_free_ssl) ) { return 0; } else { - return -2; + return -3; } } } @@ -252,7 +251,7 @@ _urcrypt_reverse_copy(size_t size, const uint8_t *in, uint8_t *out) { static uint8_t* _urcrypt_reverse_alloc(size_t size, const uint8_t *in) { - uint8_t *out = urcrypt_malloc(size); + uint8_t *out = _urcrypt_malloc(size); _urcrypt_reverse_copy(size, in, out); return out; } @@ -401,7 +400,7 @@ _urcrypt_cbc_pad(size_t *length_ptr, const uint8_t *message) rem = length % 16, padding = rem ? 16 - rem : 0, padded = length + padding; - uint8_t *buf = urcrypt_malloc(padded); + uint8_t *buf = _urcrypt_malloc(padded); memset(buf, 0, padding); _urcrypt_reverse_copy(length, message, buf + padding); @@ -422,7 +421,7 @@ _urcrypt_cbc_help(const uint8_t *message, _urcrypt_reverse_copy(16, ivec, riv); in = _urcrypt_cbc_pad(&length, message); - out = urcrypt_malloc(length); + out = _urcrypt_malloc(length); AES_cbc_encrypt(in, out, length, key, riv, enc); urcrypt_free(in); @@ -584,7 +583,7 @@ urcrypt_aes_cbcc_de(const uint8_t *message, static int _urcrypt_argon2_alloc(uint8_t** output, size_t bytes) { - *output = urcrypt_malloc(bytes); + *output = _urcrypt_malloc(bytes); return (NULL != output); } @@ -598,8 +597,6 @@ _urcrypt_argon2_free(uint8_t* memory, size_t bytes) // in uint32_t, so here's a helper macro for ensuring equivalence. #define SZ_32(s) ( sizeof(size_t) <= sizeof(uint32_t) || s <= 0xFFFFFFFF ) -// returns a constant error message string or NULL for success -// (so user doesn't have to call argon2_error_message) const char* urcrypt_argon2(urcrypt_argon2_type type, uint32_t version, diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index faa474cbf..a57278f79 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -1,6 +1,8 @@ #ifndef URCRYPT_H #define URCRYPT_H +// XX most of these should be moved to urcrypt.c + #include #include #include @@ -19,10 +21,26 @@ typedef void *(*urcrypt_malloc_t)(size_t); typedef void *(*urcrypt_realloc_t)(void*, size_t); typedef void (*urcrypt_free_t)(void*); +/* We depend on OpenSSL for various reasons, which doesn't promise not to + * allocate memory and has the annoying CRYPTO_set_mem_functions api. We + * are therefore forced to use it, so we adopt a similar approach with + * urcrypt_init(). + * + * This creates an issue for certain link configurations when the client of + * urcrypt also uses the CRYPTO_set_mem_functions() api. The simplest thing + * that is guaranteed to work is to call first CRYPTO_set_mem_functions() and + * then urcrypt_init() with the same arguments during process initialization. + * + * You should call this function once. It will return 0 on success. Calling + * any other library functions without exactly one successful call to + * urcrypt_init() will result in undefined behavior. + */ int urcrypt_init(urcrypt_malloc_t, urcrypt_realloc_t, urcrypt_free_t); -void *urcrypt_malloc(size_t); -void *urcrypt_realloc(void*, size_t); +/* We can transparently deal with padding since we already use memory + * allocation; in cases where we return allocated memory, it should + * be freed with urcrypt_free() by the caller. + */ void urcrypt_free(void*); int urcrypt_ed_point_add(const uint8_t a[32], @@ -57,6 +75,10 @@ bool urcrypt_ed_veri(const uint8_t *message, const uint8_t signature[64], const uint8_t public[32]); +// XX let's de-const the reversed arrays and promise to trash them instead, +// it will save us a malloc in some cases and it's a better API since +// it puts the decision to copy in the caller's hands. + int urcrypt_aes_ecba_en(const uint8_t key[16], const uint8_t block[16], uint8_t out[16]); @@ -76,6 +98,10 @@ int urcrypt_aes_ecbc_de(const uint8_t key[32], const uint8_t block[16], uint8_t out[16]); +/* return an alloc'd output pointer and write its length to out_length + * caller should urcrypt_free() the returned pointer. The return value + * is NULL on an error. */ + uint8_t* urcrypt_aes_cbca_en(const uint8_t *message, size_t length, const uint8_t key[16], @@ -114,6 +140,7 @@ typedef enum urcrypt_argon2_type { urcrypt_argon2_u = 10, } urcrypt_argon2_type; +/* returns a constant error message string or NULL for success */ const char* urcrypt_argon2(urcrypt_argon2_type type, uint32_t version, uint32_t threads, From 2fdffe1e8261405ac7b21f1114e8ce5ec2382849 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 18 Aug 2020 12:35:07 -0700 Subject: [PATCH 26/75] overwrite inputs instead of copying in urcrypt --- pkg/urcrypt/urcrypt.c | 495 ++++++++++++++++++------------------------ pkg/urcrypt/urcrypt.h | 137 +++++------- 2 files changed, 272 insertions(+), 360 deletions(-) diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index d15ad2ae2..be47a99ad 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -1,69 +1,45 @@ #include "urcrypt.h" -static bool initialized = false; -static urcrypt_malloc_t urcrypt_malloc_ptr; -static urcrypt_realloc_t urcrypt_realloc_ptr; -static urcrypt_free_t urcrypt_free_ptr; - -static void* _urcrypt_malloc(size_t len) -{ - return (*urcrypt_malloc_ptr)(len); -} - -static void* _urcrypt_realloc(void *ptr, size_t len) -{ - return (*urcrypt_realloc_ptr)(ptr, len); -} - -void urcrypt_free(void *ptr) -{ - (*urcrypt_free_ptr)(ptr); -} +static urcrypt_malloc_t _urcrypt_ssl_malloc_ptr; +static urcrypt_realloc_t _urcrypt_ssl_realloc_ptr; +static urcrypt_free_t _urcrypt_ssl_free_ptr; static void* _urcrypt_malloc_ssl(size_t len #if OPENSSL_VERSION_NUMBER >= 0x10100000L , const char* file, int line #endif -) { return _urcrypt_malloc(len); } +) { return (*_urcrypt_ssl_malloc_ptr)(len); } static void* _urcrypt_realloc_ssl(void* ptr, size_t len #if OPENSSL_VERSION_NUMBER >= 0x10100000L , const char* file, int line #endif -) { return _urcrypt_realloc(ptr, len); } +) { return (*_urcrypt_ssl_realloc_ptr)(ptr, len); } static void _urcrypt_free_ssl(void* ptr #if OPENSSL_VERSION_NUMBER >= 0x10100000L , const char* file, int line #endif -) { urcrypt_free(ptr); } +) { (*_urcrypt_ssl_free_ptr)(ptr); } int -urcrypt_init(_urcrypt_malloc_t m, _urcrypt_realloc_t r, urcrypt_free_t f) +urcrypt_set_openssl_functions(urcrypt_malloc_t malloc_ptr, + urcrypt_realloc_t realloc_ptr, + urcrypt_free_t free_ptr) { - if ( initialized ) { - return -1; - } - else if ( (NULL == m) || (NULL == r) || (NULL == f) ) { - return -2; - } - else { - initialized = true; - urcrypt_malloc_ptr = m; - urcrypt_realloc_ptr = r; - urcrypt_free_ptr = f; - if ( CRYPTO_set_mem_functions(&_urcrypt_malloc_ssl, - &_urcrypt_realloc_ssl, - &_urcrypt_free_ssl) ) { - return 0; - } - else { - return -3; - } + int ret = CRYPTO_set_mem_functions(&_urcrypt_malloc_ssl, + &_urcrypt_realloc_ssl, + &_urcrypt_free_ssl); + if ( 0 == ret ) { + _urcrypt_ssl_malloc_ptr = malloc_ptr; + _urcrypt_ssl_realloc_ptr = realloc_ptr; + _urcrypt_ssl_free_ptr = free_ptr; } + + return ret; } int @@ -241,23 +217,7 @@ urcrypt_ed_veri(const uint8_t *message, } static void -_urcrypt_reverse_copy(size_t size, const uint8_t *in, uint8_t *out) { - size_t i, j; - for ( i = 0, j = size - 1; i < size; i++, j-- ) { - out[i] = in[j]; - } -} - -static uint8_t* -_urcrypt_reverse_alloc(size_t size, const uint8_t *in) -{ - uint8_t *out = _urcrypt_malloc(size); - _urcrypt_reverse_copy(size, in, out); - return out; -} - -static void -_urcrypt_reverse_inplace(size_t size, uint8_t *ptr) { +_urcrypt_reverse(size_t size, uint8_t *ptr) { size_t i, j; uint8_t tmp; for ( i = 0, j = size - 1; i < j; i++, j-- ) { @@ -268,329 +228,306 @@ _urcrypt_reverse_inplace(size_t size, uint8_t *ptr) { } int -urcrypt_aes_ecba_en(const uint8_t key[16], - const uint8_t block[16], - uint8_t out[16]) +urcrypt_aes_ecba_en(uint8_t key[16], uint8_t block[16], uint8_t out[16]) { AES_KEY aes_key; - uint8_t rkey[16], rblock[16]; - _urcrypt_reverse_copy(16, key, rkey); - _urcrypt_reverse_copy(16, block, rblock); + _urcrypt_reverse(16, key); + _urcrypt_reverse(16, block); - if ( 0 != AES_set_encrypt_key(rkey, 128, &aes_key) ) { + if ( 0 != AES_set_encrypt_key(key, 128, &aes_key) ) { return -1; } else { - AES_ecb_encrypt(rblock, out, &aes_key, AES_ENCRYPT); - _urcrypt_reverse_inplace(16, out); + AES_ecb_encrypt(block, out, &aes_key, AES_ENCRYPT); + _urcrypt_reverse(16, out); return 0; } } int -urcrypt_aes_ecba_de(const uint8_t key[16], - const uint8_t block[16], - uint8_t out[16]) +urcrypt_aes_ecba_de(uint8_t key[16], uint8_t block[16], uint8_t out[16]) { AES_KEY aes_key; - uint8_t rkey[16], rblock[16]; - _urcrypt_reverse_copy(16, key, rkey); - _urcrypt_reverse_copy(16, block, rblock); + _urcrypt_reverse(16, key); + _urcrypt_reverse(16, block); - if ( 0 != AES_set_decrypt_key(rkey, 128, &aes_key) ) { + if ( 0 != AES_set_decrypt_key(key, 128, &aes_key) ) { return -1; } else { - AES_ecb_encrypt(rblock, out, &aes_key, AES_DECRYPT); - _urcrypt_reverse_inplace(16, out); + AES_ecb_encrypt(block, out, &aes_key, AES_DECRYPT); + _urcrypt_reverse(16, out); return 0; } } int -urcrypt_aes_ecbb_en(const uint8_t key[24], - const uint8_t block[16], - uint8_t out[16]) +urcrypt_aes_ecbb_en(uint8_t key[24], uint8_t block[16], uint8_t out[16]) { AES_KEY aes_key; - uint8_t rkey[24], rblock[16]; - _urcrypt_reverse_copy(24, key, rkey); - _urcrypt_reverse_copy(16, block, rblock); + _urcrypt_reverse(24, key); + _urcrypt_reverse(16, block); - if ( 0 != AES_set_encrypt_key(rkey, 192, &aes_key) ) { + if ( 0 != AES_set_encrypt_key(key, 192, &aes_key) ) { return -1; } else { - AES_ecb_encrypt(rblock, out, &aes_key, AES_ENCRYPT); - _urcrypt_reverse_inplace(16, out); + AES_ecb_encrypt(block, out, &aes_key, AES_ENCRYPT); + _urcrypt_reverse(16, out); return 0; } } int -urcrypt_aes_ecbb_de(const uint8_t key[24], - const uint8_t block[16], - uint8_t out[16]) +urcrypt_aes_ecbb_de(uint8_t key[24], uint8_t block[16], uint8_t out[16]) { AES_KEY aes_key; - uint8_t rkey[24], rblock[16]; - _urcrypt_reverse_copy(24, key, rkey); - _urcrypt_reverse_copy(16, block, rblock); + _urcrypt_reverse(24, key); + _urcrypt_reverse(16, block); - if ( 0 != AES_set_decrypt_key(rkey, 192, &aes_key) ) { + if ( 0 != AES_set_decrypt_key(key, 192, &aes_key) ) { return -1; } else { - AES_ecb_encrypt(rblock, out, &aes_key, AES_DECRYPT); - _urcrypt_reverse_inplace(16, out); + AES_ecb_encrypt(block, out, &aes_key, AES_DECRYPT); + _urcrypt_reverse(16, out); return 0; } } int -urcrypt_aes_ecbc_en(const uint8_t key[32], - const uint8_t block[16], - uint8_t out[16]) +urcrypt_aes_ecbc_en(uint8_t key[32], uint8_t block[16], uint8_t out[16]) { AES_KEY aes_key; - uint8_t rkey[32], rblock[16]; - _urcrypt_reverse_copy(32, key, rkey); - _urcrypt_reverse_copy(16, block, rblock); + _urcrypt_reverse(32, key); + _urcrypt_reverse(16, block); - if ( 0 != AES_set_encrypt_key(rkey, 256, &aes_key) ) { + if ( 0 != AES_set_encrypt_key(key, 256, &aes_key) ) { return -1; } else { - AES_ecb_encrypt(rblock, out, &aes_key, AES_ENCRYPT); - _urcrypt_reverse_inplace(16, out); + AES_ecb_encrypt(block, out, &aes_key, AES_ENCRYPT); + _urcrypt_reverse(16, out); return 0; } } int -urcrypt_aes_ecbc_de(const uint8_t key[32], - const uint8_t block[16], - uint8_t out[16]) +urcrypt_aes_ecbc_de(uint8_t key[32], uint8_t block[16], uint8_t out[16]) { AES_KEY aes_key; - uint8_t rkey[32], rblock[16]; - _urcrypt_reverse_copy(32, key, rkey); - _urcrypt_reverse_copy(16, block, rblock); + _urcrypt_reverse(32, key); + _urcrypt_reverse(16, block); - if ( 0 != AES_set_decrypt_key(rkey, 256, &aes_key) ) { + if ( 0 != AES_set_decrypt_key(key, 256, &aes_key) ) { return -1; } else { - AES_ecb_encrypt(rblock, out, &aes_key, AES_DECRYPT); - _urcrypt_reverse_inplace(16, out); + AES_ecb_encrypt(block, out, &aes_key, AES_DECRYPT); + _urcrypt_reverse(16, out); return 0; } } -static uint8_t* -_urcrypt_cbc_pad(size_t *length_ptr, const uint8_t *message) +int +_urcrypt_cbc_pad(uint8_t **message_ptr, + size_t *length_ptr, + urcrypt_realloc_t realloc_ptr) { size_t length = *length_ptr, - rem = length % 16, - padding = rem ? 16 - rem : 0, - padded = length + padding; - uint8_t *buf = _urcrypt_malloc(padded); + rem = length % 16; - memset(buf, 0, padding); - _urcrypt_reverse_copy(length, message, buf + padding); + if ( 0 == rem ) { + // no padding necessary + return 0; + } + else { + size_t padding = 16 - rem, + padded = length + padding; - *length_ptr = padded; - return buf; + if ( padded < length ) { + return -1; + } + else { + uint8_t *out = (*realloc_ptr)(*message_ptr, padded); + if ( NULL == out ) { + return -2; + } + else { + *message_ptr = out; + *length_ptr = padded; + memset(out + length, 0, padding); + _urcrypt_reverse(padded, out); + return 0; + } + } + } } -uint8_t* -_urcrypt_cbc_help(const uint8_t *message, - size_t length, +int +_urcrypt_cbc_help(uint8_t **message_ptr, + size_t *length_ptr, const AES_KEY *key, - const uint8_t ivec[16], + uint8_t ivec[16], const int enc, - size_t *out_length) + urcrypt_realloc_t realloc_ptr) { - uint8_t riv[16], *in, *out; - - _urcrypt_reverse_copy(16, ivec, riv); - in = _urcrypt_cbc_pad(&length, message); - out = _urcrypt_malloc(length); - AES_cbc_encrypt(in, out, length, key, riv, enc); - urcrypt_free(in); - - _urcrypt_reverse_inplace(length, out); - *out_length = length; - return out; -} - -uint8_t* -urcrypt_aes_cbca_en(const uint8_t *message, - size_t length, - const uint8_t key[16], - const uint8_t ivec[16], - size_t *out_length) -{ - AES_KEY aes_key; - uint8_t rkey[16]; - - _urcrypt_reverse_copy(16, key, rkey); - - if ( 0 != AES_set_encrypt_key(rkey, 128, &aes_key) ) { - return NULL; + if ( 0 != _urcrypt_cbc_pad(message_ptr, length_ptr, realloc_ptr) ) { + return -1; } else { - return _urcrypt_cbc_help(message, - length, - &aes_key, - ivec, - AES_ENCRYPT, - out_length); + uint8_t *out = *message_ptr; + size_t length = *length_ptr; + _urcrypt_reverse(16, ivec); + AES_cbc_encrypt(out, out, length, key, ivec, enc); + _urcrypt_reverse(length, out); + return 0; } } -uint8_t* -urcrypt_aes_cbca_de(const uint8_t *message, - size_t length, - const uint8_t key[16], - const uint8_t ivec[16], - size_t *out_length) +int +urcrypt_aes_cbca_en(uint8_t **message_ptr, + size_t *length_ptr, + uint8_t key[16], + uint8_t ivec[16], + urcrypt_realloc_t realloc_ptr) { AES_KEY aes_key; - uint8_t rkey[16]; - _urcrypt_reverse_copy(16, key, rkey); + _urcrypt_reverse(16, key); - if ( 0 != AES_set_decrypt_key(rkey, 128, &aes_key) ) { - return NULL; + if ( 0 != AES_set_encrypt_key(key, 128, &aes_key) ) { + return -1; } else { - return _urcrypt_cbc_help(message, - length, - &aes_key, - ivec, - AES_DECRYPT, - out_length); + return _urcrypt_cbc_help(message_ptr, length_ptr, + &aes_key, ivec, AES_ENCRYPT, realloc_ptr); } } -uint8_t* -urcrypt_aes_cbcb_en(const uint8_t *message, - size_t length, - const uint8_t key[24], - const uint8_t ivec[16], - size_t *out_length) +int +urcrypt_aes_cbca_de(uint8_t **message_ptr, + size_t *length_ptr, + uint8_t key[16], + uint8_t ivec[16], + urcrypt_realloc_t realloc_ptr) { AES_KEY aes_key; - uint8_t rkey[24]; - _urcrypt_reverse_copy(24, key, rkey); + _urcrypt_reverse(16, key); - if ( 0 != AES_set_encrypt_key(rkey, 192, &aes_key) ) { - return NULL; + if ( 0 != AES_set_decrypt_key(key, 128, &aes_key) ) { + return -1; } else { - return _urcrypt_cbc_help(message, - length, - &aes_key, - ivec, - AES_ENCRYPT, - out_length); + return _urcrypt_cbc_help(message_ptr, length_ptr, + &aes_key, ivec, AES_DECRYPT, realloc_ptr); } } -uint8_t* -urcrypt_aes_cbcb_de(const uint8_t *message, - size_t length, - const uint8_t key[24], - const uint8_t ivec[16], - size_t *out_length) +int +urcrypt_aes_cbcb_en(uint8_t **message_ptr, + size_t *length_ptr, + uint8_t key[24], + uint8_t ivec[16], + urcrypt_realloc_t realloc_ptr) { AES_KEY aes_key; - uint8_t rkey[24]; - _urcrypt_reverse_copy(24, key, rkey); + _urcrypt_reverse(24, key); - if ( 0 != AES_set_decrypt_key(rkey, 192, &aes_key) ) { - return NULL; + if ( 0 != AES_set_encrypt_key(key, 192, &aes_key) ) { + return -1; } else { - return _urcrypt_cbc_help(message, - length, - &aes_key, - ivec, - AES_DECRYPT, - out_length); + return _urcrypt_cbc_help(message_ptr, length_ptr, + &aes_key, ivec, AES_ENCRYPT, realloc_ptr); } } -uint8_t* -urcrypt_aes_cbcc_en(const uint8_t *message, - size_t length, - const uint8_t key[32], - const uint8_t ivec[16], - size_t *out_length) +int +urcrypt_aes_cbcb_de(uint8_t **message_ptr, + size_t *length_ptr, + uint8_t key[24], + uint8_t ivec[16], + urcrypt_realloc_t realloc_ptr) { AES_KEY aes_key; - uint8_t rkey[32]; - _urcrypt_reverse_copy(32, key, rkey); + _urcrypt_reverse(24, key); - if ( 0 != AES_set_encrypt_key(rkey, 256, &aes_key) ) { - return NULL; + if ( 0 != AES_set_decrypt_key(key, 192, &aes_key) ) { + return -1; } else { - return _urcrypt_cbc_help(message, - length, - &aes_key, - ivec, - AES_ENCRYPT, - out_length); + return _urcrypt_cbc_help(message_ptr, length_ptr, + &aes_key, ivec, AES_DECRYPT, realloc_ptr); } } -uint8_t* -urcrypt_aes_cbcc_de(const uint8_t *message, - size_t length, - const uint8_t key[32], - const uint8_t ivec[16], - size_t *out_length) +int +urcrypt_aes_cbcc_en(uint8_t **message_ptr, + size_t *length_ptr, + uint8_t key[32], + uint8_t ivec[16], + urcrypt_realloc_t realloc_ptr) { AES_KEY aes_key; - uint8_t rkey[32]; - _urcrypt_reverse_copy(32, key, rkey); + _urcrypt_reverse(32, key); - if ( 0 != AES_set_decrypt_key(rkey, 256, &aes_key) ) { - return NULL; + if ( 0 != AES_set_encrypt_key(key, 256, &aes_key) ) { + return -1; } else { - return _urcrypt_cbc_help(message, - length, - &aes_key, - ivec, - AES_DECRYPT, - out_length); + return _urcrypt_cbc_help(message_ptr, length_ptr, + &aes_key, ivec, AES_ENCRYPT, realloc_ptr); } } +int +urcrypt_aes_cbcc_de(uint8_t **message_ptr, + size_t *length_ptr, + uint8_t key[32], + uint8_t ivec[16], + urcrypt_realloc_t realloc_ptr) +{ + AES_KEY aes_key; + + _urcrypt_reverse(32, key); + + if ( 0 != AES_set_decrypt_key(key, 256, &aes_key) ) { + return -1; + } + else { + return _urcrypt_cbc_help(message_ptr, length_ptr, + &aes_key, ivec, AES_DECRYPT, realloc_ptr); + } +} + +/* argon2 does memory allocation, but takes function pointers in the context. + * the signatures don't match, so we need these wrappers. + */ +static urcrypt_malloc_t _urcrypt_argon2_malloc_ptr; +static urcrypt_free_t _urcrypt_argon2_free_ptr; + static int _urcrypt_argon2_alloc(uint8_t** output, size_t bytes) { - *output = _urcrypt_malloc(bytes); + *output = (*_urcrypt_argon2_malloc_ptr)(bytes); return (NULL != output); } static void _urcrypt_argon2_free(uint8_t* memory, size_t bytes) { - urcrypt_free(memory); + (*_urcrypt_argon2_free_ptr)(memory); } // library convention is to have sizes in size_t, but argon2 wants them @@ -604,15 +541,17 @@ urcrypt_argon2(urcrypt_argon2_type type, uint32_t memory_cost, uint32_t time_cost, size_t secret_length, - const uint8_t *secret, + uint8_t *secret, size_t associated_length, - const uint8_t *associated, + uint8_t *associated, size_t password_length, - const uint8_t *password, + uint8_t *password, size_t salt_length, - const uint8_t *salt, + uint8_t *salt, size_t out_length, - uint8_t *out) + uint8_t *out, + urcrypt_malloc_t malloc_ptr, + urcrypt_free_t free_ptr) { if ( !( SZ_32(secret_length) && SZ_32(associated_length) && @@ -624,7 +563,6 @@ urcrypt_argon2(urcrypt_argon2_type type, else { int (*f)(argon2_context*); int result; - uint8_t *rsecret, *rassoc, *rpassword, *rsalt; switch ( type ) { default: @@ -643,20 +581,21 @@ urcrypt_argon2(urcrypt_argon2_type type, break; } - rsecret = _urcrypt_reverse_alloc(secret_length, secret); - rassoc = _urcrypt_reverse_alloc(associated_length, associated); - rpassword = _urcrypt_reverse_alloc(password_length, password); - rsalt = _urcrypt_reverse_alloc(salt_length, salt); + _urcrypt_reverse(secret_length, secret); + _urcrypt_reverse(associated_length, associated); + _urcrypt_reverse(password_length, password); + _urcrypt_reverse(salt_length, salt); + argon2_context context = { out, // output array, at least [digest length] in size out_length, // digest length - rpassword, // password array + password, // password array password_length, // password length - rsalt, // salt array + salt, // salt array salt_length, // salt length - rsecret, // optional secret data + secret, // optional secret data secret_length, - rassoc, // optional associated data + associated, // optional associated data associated_length, time_cost, // performance cost configuration memory_cost, @@ -668,17 +607,15 @@ urcrypt_argon2(urcrypt_argon2_type type, ARGON2_DEFAULT_FLAGS // by default only internal memory is cleared }; + _urcrypt_argon2_malloc_ptr = malloc_ptr; + _urcrypt_argon2_free_ptr = free_ptr; result = (*f)(&context); - urcrypt_free(rsecret); - urcrypt_free(rassoc); - urcrypt_free(rpassword); - urcrypt_free(rsalt); if ( ARGON2_OK != result ) { return argon2_error_message(result); } else { - _urcrypt_reverse_inplace(out_length, out); + _urcrypt_reverse(out_length, out); return NULL; } } @@ -686,33 +623,27 @@ urcrypt_argon2(urcrypt_argon2_type type, int urcrypt_blake2(size_t message_length, - const uint8_t *message, + uint8_t *message, size_t key_length, - const uint8_t key[64], + uint8_t key[64], size_t out_length, uint8_t *out) { - int ret; - uint8_t rkey[64]; - uint8_t *rmessage; - if ( key_length > 64 ) { return -1; } - - rmessage = _urcrypt_reverse_alloc(message_length, message); - _urcrypt_reverse_copy(key_length, key, rkey); - ret = blake2b(out, out_length, - rmessage, message_length, - rkey, key_length); - - urcrypt_free(rmessage); - - if ( 0 != ret ) { - return -1; - } else { - _urcrypt_reverse_inplace(out_length, out); - return 0; + _urcrypt_reverse(message_length, message); + _urcrypt_reverse(key_length, key); + + if ( 0 != blake2b(out, out_length, + message, message_length, + key, key_length)) { + return -1; + } + else { + _urcrypt_reverse(out_length, out); + return 0; + } } } diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index a57278f79..130a9b229 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -23,32 +23,28 @@ typedef void (*urcrypt_free_t)(void*); /* We depend on OpenSSL for various reasons, which doesn't promise not to * allocate memory and has the annoying CRYPTO_set_mem_functions api. We - * are therefore forced to use it, so we adopt a similar approach with - * urcrypt_init(). + * are therefore forced to support it in some fashion. * - * This creates an issue for certain link configurations when the client of - * urcrypt also uses the CRYPTO_set_mem_functions() api. The simplest thing - * that is guaranteed to work is to call first CRYPTO_set_mem_functions() and - * then urcrypt_init() with the same arguments during process initialization. + * If you need to control urcrypt's internal OpenSSL allocation, you can call + * this function. It just wraps the OpenSSL function. * - * You should call this function once. It will return 0 on success. Calling - * any other library functions without exactly one successful call to - * urcrypt_init() will result in undefined behavior. + * urcrypt will not use these functions directly. */ -int urcrypt_init(urcrypt_malloc_t, urcrypt_realloc_t, urcrypt_free_t); +int urcrypt_set_openssl_functions(urcrypt_malloc_t malloc_ptr, + urcrypt_realloc_t realloc_ptr, + urcrypt_free_t free_ptr); -/* We can transparently deal with padding since we already use memory - * allocation; in cases where we return allocated memory, it should - * be freed with urcrypt_free() by the caller. - */ -void urcrypt_free(void*); +// const arguments are not written to, non-const arguments may be +// array sizes[64] are purely documentary +// 0 on success, result in out int urcrypt_ed_point_add(const uint8_t a[32], const uint8_t b[32], uint8_t out[32]); int urcrypt_ed_scalarmult(const uint8_t a[32], const uint8_t b[32], uint8_t out[32]); +// void functions have no failure mode void urcrypt_ed_scalarmult_base(const uint8_t a[32], uint8_t out[32]); int urcrypt_ed_add_scalarmult_scalarmult_base(const uint8_t a[32], @@ -70,68 +66,51 @@ void urcrypt_ed_sign(const uint8_t *message, size_t length, const uint8_t seed[32], uint8_t out[64]); +// return value means the signature was (not) verified bool urcrypt_ed_veri(const uint8_t *message, size_t length, const uint8_t signature[64], const uint8_t public[32]); -// XX let's de-const the reversed arrays and promise to trash them instead, -// it will save us a malloc in some cases and it's a better API since -// it puts the decision to copy in the caller's hands. +int urcrypt_aes_ecba_en(uint8_t key[16], uint8_t block[16], uint8_t out[16]); +int urcrypt_aes_ecba_de(uint8_t key[16], uint8_t block[16], uint8_t out[16]); +int urcrypt_aes_ecbb_en(uint8_t key[24], uint8_t block[16], uint8_t out[16]); +int urcrypt_aes_ecbb_de(uint8_t key[24], uint8_t block[16], uint8_t out[16]); +int urcrypt_aes_ecbc_en(uint8_t key[32], uint8_t block[16], uint8_t out[16]); +int urcrypt_aes_ecbc_de(uint8_t key[32], uint8_t block[16], uint8_t out[16]); -int urcrypt_aes_ecba_en(const uint8_t key[16], - const uint8_t block[16], - uint8_t out[16]); -int urcrypt_aes_ecba_de(const uint8_t key[16], - const uint8_t block[16], - uint8_t out[16]); -int urcrypt_aes_ecbb_en(const uint8_t key[24], - const uint8_t block[16], - uint8_t out[16]); -int urcrypt_aes_ecbb_de(const uint8_t key[24], - const uint8_t block[16], - uint8_t out[16]); -int urcrypt_aes_ecbc_en(const uint8_t key[32], - const uint8_t block[16], - uint8_t out[16]); -int urcrypt_aes_ecbc_de(const uint8_t key[32], - const uint8_t block[16], - uint8_t out[16]); - -/* return an alloc'd output pointer and write its length to out_length - * caller should urcrypt_free() the returned pointer. The return value - * is NULL on an error. */ - -uint8_t* urcrypt_aes_cbca_en(const uint8_t *message, - size_t length, - const uint8_t key[16], - const uint8_t ivec[16], - size_t *out_length); -uint8_t* urcrypt_aes_cbca_de(const uint8_t *message, - size_t length, - const uint8_t key[16], - const uint8_t ivec[16], - size_t *out_length); -uint8_t* urcrypt_aes_cbcb_en(const uint8_t *message, - size_t length, - const uint8_t key[24], - const uint8_t ivec[16], - size_t *out_length); -uint8_t* urcrypt_aes_cbcb_de(const uint8_t *message, - size_t length, - const uint8_t key[24], - const uint8_t ivec[16], - size_t *out_length); -uint8_t* urcrypt_aes_cbcc_en(const uint8_t *message, - size_t length, - const uint8_t key[32], - const uint8_t ivec[16], - size_t *out_length); -uint8_t* urcrypt_aes_cbcc_de(const uint8_t *message, - size_t length, - const uint8_t key[32], - const uint8_t ivec[16], - size_t *out_length); +// message and length are read/write so +// realloc_ptr can be used as realloc to pad message +int urcrypt_aes_cbca_en(uint8_t **message_ptr, + size_t *length_ptr, + uint8_t key[16], + uint8_t ivec[16], + urcrypt_realloc_t realloc_ptr); +int urcrypt_aes_cbca_de(uint8_t **message_ptr, + size_t *length_ptr, + uint8_t key[16], + uint8_t ivec[16], + urcrypt_realloc_t realloc_ptr); +int urcrypt_aes_cbcb_en(uint8_t **message_ptr, + size_t *length_ptr, + uint8_t key[24], + uint8_t ivec[16], + urcrypt_realloc_t realloc_ptr); +int urcrypt_aes_cbcb_de(uint8_t **message_ptr, + size_t *length_ptr, + uint8_t key[24], + uint8_t ivec[16], + urcrypt_realloc_t realloc_ptr); +int urcrypt_aes_cbcc_en(uint8_t **message_ptr, + size_t *length_ptr, + uint8_t key[32], + uint8_t ivec[16], + urcrypt_realloc_t realloc_ptr); +int urcrypt_aes_cbcc_de(uint8_t **message_ptr, + size_t *length_ptr, + uint8_t key[32], + uint8_t ivec[16], + urcrypt_realloc_t realloc_ptr); typedef enum urcrypt_argon2_type { urcrypt_argon2_d = 0, @@ -147,20 +126,22 @@ const char* urcrypt_argon2(urcrypt_argon2_type type, uint32_t memory_cost, uint32_t time_cost, size_t secret_length, - const uint8_t *secret, + uint8_t *secret, size_t associated_length, - const uint8_t *associated, + uint8_t *associated, size_t password_length, - const uint8_t *password, + uint8_t *password, size_t salt_length, - const uint8_t *salt, + uint8_t *salt, size_t out_length, - uint8_t *out); + uint8_t *out, + urcrypt_malloc_t malloc_ptr, + urcrypt_free_t free_ptr); int urcrypt_blake2(size_t message_length, - const uint8_t *message, + uint8_t *message, size_t key_length, - const uint8_t key[64], + uint8_t key[64], size_t out_length, uint8_t *out); From bdfa1a4852651d835b28416388af6a09d6f6e418 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 18 Aug 2020 12:44:10 -0700 Subject: [PATCH 27/75] cleaning up _cbc_pad a bit --- pkg/urcrypt/urcrypt.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index be47a99ad..7215dcb59 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -340,18 +340,19 @@ _urcrypt_cbc_pad(uint8_t **message_ptr, size_t *length_ptr, urcrypt_realloc_t realloc_ptr) { - size_t length = *length_ptr, - rem = length % 16; + size_t length = *length_ptr, + remain = length % 16; - if ( 0 == rem ) { - // no padding necessary + if ( 0 == remain ) { + // no padding needed return 0; } else { - size_t padding = 16 - rem, + size_t padding = 16 - remain, padded = length + padding; if ( padded < length ) { + // size_t overflow return -1; } else { @@ -360,10 +361,9 @@ _urcrypt_cbc_pad(uint8_t **message_ptr, return -2; } else { - *message_ptr = out; - *length_ptr = padded; memset(out + length, 0, padding); - _urcrypt_reverse(padded, out); + *message_ptr = out; + *length_ptr = padded; return 0; } } @@ -385,6 +385,7 @@ _urcrypt_cbc_help(uint8_t **message_ptr, uint8_t *out = *message_ptr; size_t length = *length_ptr; _urcrypt_reverse(16, ivec); + _urcrypt_reverse(length, out); AES_cbc_encrypt(out, out, length, key, ivec, enc); _urcrypt_reverse(length, out); return 0; From e846b297542da60463014f9a5001b858c7c7999f Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 18 Aug 2020 15:42:22 -0700 Subject: [PATCH 28/75] integrate jets with urcrypt changes --- pkg/urbit/jets/e/aes_cbc.c | 34 ++++++++++++++++------------------ pkg/urbit/jets/e/aes_ecb.c | 16 ++++++++-------- pkg/urbit/jets/e/argon2.c | 4 +++- pkg/urbit/noun/manage.c | 20 +++++++++++++++++++- pkg/urcrypt/urcrypt.c | 36 ++++++++++++++++++++---------------- pkg/urcrypt/urcrypt.h | 8 ++++---- 6 files changed, 70 insertions(+), 48 deletions(-) diff --git a/pkg/urbit/jets/e/aes_cbc.c b/pkg/urbit/jets/e/aes_cbc.c index 06d9df381..793304ad4 100644 --- a/pkg/urbit/jets/e/aes_cbc.c +++ b/pkg/urbit/jets/e/aes_cbc.c @@ -8,35 +8,33 @@ * the ECB functions, which truncate them, hence the raw u3r_bytes unpacking. */ -typedef c3_y* (*urcrypt_cbc)(const c3_y*, - size_t, - const c3_y*, - const c3_y*, - size_t*); +typedef int (*urcrypt_cbc)(c3_y**, + size_t*, + c3_y*, + c3_y*, + urcrypt_realloc_t); /* functions */ static u3_atom _cqea_cbc_help(c3_y* key_y, u3_atom iv, u3_atom msg, urcrypt_cbc low_f) { - size_t len; - c3_w met_w; - c3_y iv_y[16], *msg_y, *out_y; + u3_atom ret; + c3_w met_w; + c3_y iv_y[16]; + c3_y* msg_y = u3r_bytes_all(&met_w, msg); + size_t len = met_w; u3r_bytes(0, 16, iv_y, iv); - - msg_y = u3r_bytes_all(&met_w, msg); - out_y = (*low_f)(msg_y, met_w, key_y, iv_y, &len); - u3a_free(msg_y); - - if ( NULL == out_y ) { - return u3_none; + if ( 0 != (*low_f)(&msg_y, &len, key_y, iv_y, &u3a_realloc) ) { + ret = u3_none; } else { - u3_atom ret = u3i_bytes(len, out_y); - urcrypt_free(out_y); - return ret; + ret = u3i_bytes(len, msg_y); } + u3a_free(msg_y); + + return ret; } static u3_atom diff --git a/pkg/urbit/jets/e/aes_ecb.c b/pkg/urbit/jets/e/aes_ecb.c index b95a36a73..9ea9c59ed 100644 --- a/pkg/urbit/jets/e/aes_ecb.c +++ b/pkg/urbit/jets/e/aes_ecb.c @@ -4,7 +4,7 @@ #include "all.h" #include -typedef int (*urcrypt_ecb)(const c3_y*, const c3_y[16], c3_y[16]); +typedef int (*urcrypt_ecb)(c3_y*, c3_y[16], c3_y[16]); /* functions */ @@ -13,7 +13,7 @@ typedef int (*urcrypt_ecb)(const c3_y*, const c3_y[16], c3_y[16]); * jets we unpack with an unconditional u3r_bytes */ static u3_atom - _cqea_ecba_help(c3_y* key_y, u3_atom blk, urcrypt_ecb low_f) + _cqea_ecb_help(c3_y* key_y, u3_atom blk, urcrypt_ecb low_f) { c3_y blk_y[16], out_y[16]; @@ -33,7 +33,7 @@ typedef int (*urcrypt_ecb)(const c3_y*, const c3_y[16], c3_y[16]); { c3_y key_y[16]; u3r_bytes(0, 16, key_y, key); - return _cqea_ecba_help(key_y, blk, &urcrypt_aes_ecba_en); + return _cqea_ecb_help(key_y, blk, &urcrypt_aes_ecba_en); } u3_noun @@ -56,7 +56,7 @@ typedef int (*urcrypt_ecb)(const c3_y*, const c3_y[16], c3_y[16]); { c3_y key_y[16]; u3r_bytes(0, 16, key_y, key); - return _cqea_ecba_help(key_y, blk, &urcrypt_aes_ecba_de); + return _cqea_ecb_help(key_y, blk, &urcrypt_aes_ecba_de); } u3_noun @@ -79,7 +79,7 @@ typedef int (*urcrypt_ecb)(const c3_y*, const c3_y[16], c3_y[16]); { c3_y key_y[24]; u3r_bytes(0, 24, key_y, key); - return _cqea_ecba_help(key_y, blk, &urcrypt_aes_ecbb_en); + return _cqea_ecb_help(key_y, blk, &urcrypt_aes_ecbb_en); } u3_noun @@ -102,7 +102,7 @@ typedef int (*urcrypt_ecb)(const c3_y*, const c3_y[16], c3_y[16]); { c3_y key_y[24]; u3r_bytes(0, 24, key_y, key); - return _cqea_ecba_help(key_y, blk, &urcrypt_aes_ecbb_de); + return _cqea_ecb_help(key_y, blk, &urcrypt_aes_ecbb_de); } u3_noun @@ -125,7 +125,7 @@ typedef int (*urcrypt_ecb)(const c3_y*, const c3_y[16], c3_y[16]); { c3_y key_y[32]; u3r_bytes(0, 32, key_y, key); - return _cqea_ecba_help(key_y, blk, &urcrypt_aes_ecbc_en); + return _cqea_ecb_help(key_y, blk, &urcrypt_aes_ecbc_en); } u3_noun @@ -148,7 +148,7 @@ typedef int (*urcrypt_ecb)(const c3_y*, const c3_y[16], c3_y[16]); { c3_y key_y[32]; u3r_bytes(0, 32, key_y, key); - return _cqea_ecba_help(key_y, blk, &urcrypt_aes_ecbc_de); + return _cqea_ecb_help(key_y, blk, &urcrypt_aes_ecbc_de); } u3_noun diff --git a/pkg/urbit/jets/e/argon2.c b/pkg/urbit/jets/e/argon2.c index 9c145e00f..7b47628d0 100644 --- a/pkg/urbit/jets/e/argon2.c +++ b/pkg/urbit/jets/e/argon2.c @@ -69,7 +69,9 @@ wix_w, ex_y, wid_w, dat_y, wis_w, sat_y, - out_w, out_y); + out_w, out_y, + &u3a_malloc, + &u3a_free); u3a_free(key_y); u3a_free(ex_y); diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 86601968e..852c2d777 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -1656,9 +1656,27 @@ u3m_boot(c3_c* dir_c) */ u3m_init(); + /* Initialize OpenSSL with loom allocation functions. + * IMPORTANT: this should come before the urcrypt_set_openssl [...] + * call, because in usual circumstances that will override the functions + * we set here, but weird linking situations we don't currently encounter + * could mean we need to set both, and it doesn't hurt. + */ + if ( 0 == CRYPTO_set_mem_functions(&u3a_malloc_ssl, + &u3a_realloc_ssl, + &u3a_free_ssl) ) { + u3l_log("%s\r\n", "openssl initialization failed"); + exit(1); + } + /* Initialize cryptography suite with loom allocation functions. */ - urcrypt_init(&u3a_malloc, &u3a_realloc, &u3a_free); + if ( 0 != urcrypt_set_openssl_mem_functions(&u3a_malloc, + &u3a_realloc, + &u3a_free) ) { + u3l_log("%s\r\n", "urcrypt initialization failed"); + exit(1); + } /* Activate the storage system. */ diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 7215dcb59..e9810b79e 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -26,20 +26,21 @@ _urcrypt_free_ssl(void* ptr ) { (*_urcrypt_ssl_free_ptr)(ptr); } int -urcrypt_set_openssl_functions(urcrypt_malloc_t malloc_ptr, - urcrypt_realloc_t realloc_ptr, - urcrypt_free_t free_ptr) +urcrypt_set_openssl_mem_functions(urcrypt_malloc_t malloc_ptr, + urcrypt_realloc_t realloc_ptr, + urcrypt_free_t free_ptr) { - int ret = CRYPTO_set_mem_functions(&_urcrypt_malloc_ssl, - &_urcrypt_realloc_ssl, - &_urcrypt_free_ssl); - if ( 0 == ret ) { + if ( CRYPTO_set_mem_functions(&_urcrypt_malloc_ssl, + &_urcrypt_realloc_ssl, + &_urcrypt_free_ssl) ) { _urcrypt_ssl_malloc_ptr = malloc_ptr; _urcrypt_ssl_realloc_ptr = realloc_ptr; _urcrypt_ssl_free_ptr = free_ptr; + return 0; + } + else { + return -1; } - - return ret; } int @@ -218,12 +219,14 @@ urcrypt_ed_veri(const uint8_t *message, static void _urcrypt_reverse(size_t size, uint8_t *ptr) { - size_t i, j; - uint8_t tmp; - for ( i = 0, j = size - 1; i < j; i++, j-- ) { - tmp = ptr[i]; - ptr[i] = ptr[j]; - ptr[j] = tmp; + if ( size > 0 ) { + size_t i, j; + uint8_t tmp; + for ( i = 0, j = size - 1; i < j; i++, j-- ) { + tmp = ptr[i]; + ptr[i] = ptr[j]; + ptr[j] = tmp; + } } } @@ -522,7 +525,7 @@ static int _urcrypt_argon2_alloc(uint8_t** output, size_t bytes) { *output = (*_urcrypt_argon2_malloc_ptr)(bytes); - return (NULL != output); + return (NULL != *output); } static void @@ -565,6 +568,7 @@ urcrypt_argon2(urcrypt_argon2_type type, int (*f)(argon2_context*); int result; + switch ( type ) { default: return "unknown type"; diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 130a9b229..a0a26fb7c 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -26,13 +26,13 @@ typedef void (*urcrypt_free_t)(void*); * are therefore forced to support it in some fashion. * * If you need to control urcrypt's internal OpenSSL allocation, you can call - * this function. It just wraps the OpenSSL function. + * this function. It wraps the OpenSSL function, returning 0 on success. * * urcrypt will not use these functions directly. */ -int urcrypt_set_openssl_functions(urcrypt_malloc_t malloc_ptr, - urcrypt_realloc_t realloc_ptr, - urcrypt_free_t free_ptr); +int urcrypt_set_openssl_mem_functions(urcrypt_malloc_t malloc_ptr, + urcrypt_realloc_t realloc_ptr, + urcrypt_free_t free_ptr); // const arguments are not written to, non-const arguments may be // array sizes[64] are purely documentary From e9c400424d351729f3baae5cd89ada6908421632 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 18 Aug 2020 15:47:19 -0700 Subject: [PATCH 29/75] move some includes into urcrypt.c --- pkg/urcrypt/urcrypt.c | 10 ++++++++++ pkg/urcrypt/urcrypt.h | 12 ------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index e9810b79e..4566ff693 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -1,5 +1,15 @@ #include "urcrypt.h" +#include +#include +#include + +#include +#include + +#include +#include + static urcrypt_malloc_t _urcrypt_ssl_malloc_ptr; static urcrypt_realloc_t _urcrypt_ssl_realloc_ptr; static urcrypt_free_t _urcrypt_ssl_free_ptr; diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index a0a26fb7c..4d326d4a1 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -1,21 +1,9 @@ #ifndef URCRYPT_H #define URCRYPT_H -// XX most of these should be moved to urcrypt.c - #include #include #include -#include - -#include -#include - -#include -#include - -#include -#include typedef void *(*urcrypt_malloc_t)(size_t); typedef void *(*urcrypt_realloc_t)(void*, size_t); From aea45ee03797b5a6514263d52f1f067d37f6e495 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Thu, 20 Aug 2020 14:43:22 -0700 Subject: [PATCH 30/75] ripemd-160->urcrypt --- pkg/urbit/include/jets/q.h | 10 ------ pkg/urbit/jets/e/argon2.c | 13 ++++--- pkg/urbit/jets/e/ripe.c | 69 +++++++++++--------------------------- pkg/urbit/noun/manage.c | 1 + pkg/urcrypt/urcrypt.c | 17 ++++++++++ pkg/urcrypt/urcrypt.h | 2 ++ 6 files changed, 48 insertions(+), 64 deletions(-) diff --git a/pkg/urbit/include/jets/q.h b/pkg/urbit/include/jets/q.h index 68f946603..de2f95257 100644 --- a/pkg/urbit/include/jets/q.h +++ b/pkg/urbit/include/jets/q.h @@ -138,16 +138,6 @@ u3_atom, u3_atom, u3_atom, u3_atom); - u3_noun u3qe_argon2(u3_atom, u3_atom, u3_atom, - u3_atom, u3_atom, u3_atom, - u3_atom, u3_atom, u3_atom, u3_atom, - u3_atom, u3_atom, u3_atom, u3_atom); - - u3_noun u3qe_blake(u3_atom wid, u3_atom dat, - u3_atom wik, u3_atom dak, u3_atom out); - - u3_noun u3qe_ripe(u3_atom wid, u3_atom dat); - u3_noun u3qe_make(u3_atom has, u3_atom prv); u3_noun u3qe_reco(u3_atom has, u3_atom sig_v, u3_atom sig_r, u3_atom sig_s); u3_noun u3qe_sign(u3_atom has, u3_atom prv); diff --git a/pkg/urbit/jets/e/argon2.c b/pkg/urbit/jets/e/argon2.c index 7b47628d0..b9c9415ac 100644 --- a/pkg/urbit/jets/e/argon2.c +++ b/pkg/urbit/jets/e/argon2.c @@ -43,15 +43,18 @@ c3_w out_w, wik_w, wix_w, wid_w, wis_w, ver_w, ted_w, mem_w, tim_w; if ( !(u3r_word_fit(&out_w, out) && - _cqear_unpack_type(&typ_u, type) && - u3r_word_fit(&ver_w, version) && - u3r_word_fit(&ted_w, threads) && - u3r_word_fit(&mem_w, mem_cost) && - u3r_word_fit(&tim_w, time_cost) && u3r_word_fit(&wik_w, wik) && u3r_word_fit(&wix_w, wix) && u3r_word_fit(&wid_w, wid) && u3r_word_fit(&wis_w, wis)) ) { + // too big to allocate + return u3m_bail(c3__fail); + } + else if ( !(_cqear_unpack_type(&typ_u, type) && + u3r_word_fit(&ver_w, version) && + u3r_word_fit(&ted_w, threads) && + u3r_word_fit(&mem_w, mem_cost) && + u3r_word_fit(&tim_w, time_cost)) ) { u3l_log("%s\r\n", "argon2-punt"); return u3_none; } diff --git a/pkg/urbit/jets/e/ripe.c b/pkg/urbit/jets/e/ripe.c index 908e6aa1c..971a04b2c 100644 --- a/pkg/urbit/jets/e/ripe.c +++ b/pkg/urbit/jets/e/ripe.c @@ -2,62 +2,34 @@ ** */ #include "all.h" -#include +#include /* functions */ - - u3_noun - u3qe_ripe(u3_atom wid, u3_atom dat) + static u3_atom + _cqe_ripe(u3_atom wid, u3_atom dat) { - c3_assert(_(u3a_is_cat(wid))); - dat = u3qc_rev(3, wid, dat); + c3_w len_w; + if ( !u3r_word_fit(&len_w, wid) ) { + return u3m_bail(c3__fail); + } + else { + u3_atom ret; + c3_y out_y[20]; + c3_y *dat_y = u3r_bytes_alloc(0, len_w, dat); - c3_y* dat_y = (c3_y*)u3a_malloc(wid); // msg body - u3r_bytes(0, wid, (void*)dat_y, dat); - - const EVP_MD* rip_u = EVP_ripemd160(); // ripem algorithm - EVP_MD_CTX* con_u = EVP_MD_CTX_create(); - - /* perform signature - */ - - c3_y sib_y[20]; // signature body - c3_w sil_w; // signature length - c3_w ret_w; // return code - - ret_w = EVP_DigestInit_ex(con_u, rip_u, NULL); - if ( 1 != ret_w ) { + if ( 0 == urcrypt_ripemd160(dat_y, len_w, out_y) ) { + ret = u3i_bytes(20, out_y); + } + else { + u3l_log("%s\r\n", "ripemd160-punt"); + ret = u3_none; + } u3a_free(dat_y); - EVP_MD_CTX_destroy(con_u); - u3l_log("\rripe jet: crypto library fail 1\n"); - return u3m_bail(c3__fail); + return ret; } - - ret_w = EVP_DigestUpdate(con_u, (void*)dat_y, wid); - u3a_free(dat_y); - if (1 != ret_w) { - EVP_MD_CTX_destroy(con_u); - u3l_log("\rripe jet: crypto library fail 2\n"); - return u3m_bail(c3__fail); - } - - ret_w = EVP_DigestFinal_ex(con_u, sib_y, &sil_w); - if ( 1 != ret_w ) { - EVP_MD_CTX_destroy(con_u); - u3l_log("\rripe jet: crypto library fail 3\n"); - return u3m_bail(c3__fail); - } - - EVP_MD_CTX_destroy(con_u); - - /* endian conversion; - turn into noun for return - */ - return u3kc_rev(3, sil_w, u3i_bytes(sil_w, sib_y)); } - u3_noun u3we_ripe(u3_noun cor) { @@ -68,10 +40,9 @@ u3ud(wid) || u3ud(dat)) ) { - u3l_log("\rripe jet: argument error\n"); return u3m_bail(c3__exit); } else { - return u3qe_ripe(wid, dat); + return _cqe_ripe(wid, dat); } } diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 852c2d777..873cc3fee 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "all.h" diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 4566ff693..efdd4ad55 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -525,6 +526,22 @@ urcrypt_aes_cbcc_de(uint8_t **message_ptr, } } +int +urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]) +{ + unsigned long n = length; + + if ( length != n ) { + return -1; + } + else { + _urcrypt_reverse(length, message); + RIPEMD160(message, n, out); + _urcrypt_reverse(20, out); + return 0; + } +} + /* argon2 does memory allocation, but takes function pointers in the context. * the signatures don't match, so we need these wrappers. */ diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 4d326d4a1..397fde87d 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -100,6 +100,8 @@ int urcrypt_aes_cbcc_de(uint8_t **message_ptr, uint8_t ivec[16], urcrypt_realloc_t realloc_ptr); +int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]); + typedef enum urcrypt_argon2_type { urcrypt_argon2_d = 0, urcrypt_argon2_i = 1, From 04e8e851453eb193cf92893fafc226e9c042ab63 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Thu, 20 Aug 2020 15:04:53 -0700 Subject: [PATCH 31/75] sha1->urcrypt --- pkg/urbit/include/jets/q.h | 1 - pkg/urbit/jets/e/sha1.c | 47 ++++++++++++-------------------------- pkg/urcrypt/urcrypt.c | 9 ++++++++ pkg/urcrypt/urcrypt.h | 1 + 4 files changed, 24 insertions(+), 34 deletions(-) diff --git a/pkg/urbit/include/jets/q.h b/pkg/urbit/include/jets/q.h index de2f95257..9be9280b3 100644 --- a/pkg/urbit/include/jets/q.h +++ b/pkg/urbit/include/jets/q.h @@ -132,7 +132,6 @@ u3_noun u3qe_shay(u3_atom, u3_atom); u3_noun u3qe_shas(u3_atom, u3_atom); u3_noun u3qe_shal(u3_atom, u3_atom); - u3_noun u3qe_sha1(u3_atom, u3_atom); u3_noun u3qe_hmac(u3_noun, u3_atom, u3_atom, u3_atom, u3_atom, u3_atom, u3_atom); diff --git a/pkg/urbit/jets/e/sha1.c b/pkg/urbit/jets/e/sha1.c index 049ab0da1..1423e5299 100644 --- a/pkg/urbit/jets/e/sha1.c +++ b/pkg/urbit/jets/e/sha1.c @@ -2,42 +2,24 @@ ** */ #include "all.h" - -#if defined(U3_OS_osx) -#include -#else -#include -#endif +#include /* functions */ - - u3_noun - u3qe_sha1(u3_atom wid, u3_atom dat) + static u3_noun + _cqe_sha1(u3_atom wid, u3_atom dat) { - c3_assert(_(u3a_is_cat(wid))); - dat = u3qc_rev(3, wid, dat); + c3_w len_w; + if ( !u3r_word_fit(&len_w, wid) ) { + return u3m_bail(c3__fail); + } + else { + c3_y out_y[20]; + c3_y *dat_y = u3r_bytes_alloc(0, len_w, dat); - c3_y* fat_y = u3a_malloc(wid + 1); - u3r_bytes(0, wid, fat_y, dat); - { - c3_y dig_y[32]; -#if defined(U3_OS_osx) - CC_SHA1_CTX ctx_h; - - CC_SHA1_Init(&ctx_h); - CC_SHA1_Update(&ctx_h, fat_y, wid); - CC_SHA1_Final(dig_y, &ctx_h); -#else - SHA_CTX ctx_h; - - SHA1_Init(&ctx_h); - SHA1_Update(&ctx_h, fat_y, wid); - SHA1_Final(dig_y, &ctx_h); -#endif - u3a_free(fat_y); - u3z(dat); - return u3kc_rev(3, 20, u3i_bytes(20, dig_y)); + urcrypt_sha1(dat_y, len_w, out_y); + u3a_free(dat_y); + return u3i_bytes(20, out_y); } } @@ -48,12 +30,11 @@ if ( (c3n == u3r_mean(cor, u3x_sam_2, &wid, u3x_sam_3, &dat, 0)) || (c3n == u3ud(wid)) || - (c3n == u3a_is_cat(wid)) || (c3n == u3ud(dat)) ) { return u3m_bail(c3__exit); } else { - return u3qe_sha1(wid, dat); + return _cqe_sha1(wid, dat); } } diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index efdd4ad55..20d32f8f3 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -526,6 +527,14 @@ urcrypt_aes_cbcc_de(uint8_t **message_ptr, } } +void +urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]) +{ + _urcrypt_reverse(length, message); + SHA1(message, length, out); + _urcrypt_reverse(20, out); +} + int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]) { diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 397fde87d..85aa29233 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -100,6 +100,7 @@ int urcrypt_aes_cbcc_de(uint8_t **message_ptr, uint8_t ivec[16], urcrypt_realloc_t realloc_ptr); +void urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]); int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]); typedef enum urcrypt_argon2_type { From b742eb729fb02d8e026ead08d453120a43a064e4 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Thu, 20 Aug 2020 15:25:11 -0700 Subject: [PATCH 32/75] shax->urcrypt --- pkg/urbit/include/jets/q.h | 1 - pkg/urbit/jets/e/shax.c | 50 +++++++++----------------------------- pkg/urcrypt/urcrypt.c | 22 +++++++++++------ pkg/urcrypt/urcrypt.h | 4 ++- 4 files changed, 29 insertions(+), 48 deletions(-) diff --git a/pkg/urbit/include/jets/q.h b/pkg/urbit/include/jets/q.h index 9be9280b3..9c6b2e491 100644 --- a/pkg/urbit/include/jets/q.h +++ b/pkg/urbit/include/jets/q.h @@ -128,7 +128,6 @@ u3_noun u3qes_pbk(u3_atom, u3_atom, u3_atom, u3_atom); u3_noun u3qes_pbl(u3_atom, u3_atom, u3_atom, u3_atom, u3_atom, u3_atom); - u3_noun u3qe_shax(u3_atom); u3_noun u3qe_shay(u3_atom, u3_atom); u3_noun u3qe_shas(u3_atom, u3_atom); u3_noun u3qe_shal(u3_atom, u3_atom); diff --git a/pkg/urbit/jets/e/shax.c b/pkg/urbit/jets/e/shax.c index 4df51183b..bd979833c 100644 --- a/pkg/urbit/jets/e/shax.c +++ b/pkg/urbit/jets/e/shax.c @@ -2,7 +2,7 @@ ** */ #include "all.h" - +#include #if defined(U3_OS_osx) #include @@ -41,43 +41,17 @@ } } -// u3_noun -// u3qe_shax( -// u3_atom a) -// { -// c3_w met_w = u3r_met(3, a); -// return u3qe_shay(met_w, a); -// } -// XX preformance -u3_noun - u3qe_shax(u3_atom a) + static u3_atom + _cqe_shax(u3_atom a) { - c3_w met_w = u3r_met(3, a); - c3_y* fat_y = u3a_malloc(met_w + 1); - - u3r_bytes(0, met_w, fat_y, a); - { - c3_y dig_y[32]; -#if defined(U3_OS_osx) - CC_SHA256_CTX ctx_h; - - CC_SHA256_Init(&ctx_h); - CC_SHA256_Update(&ctx_h, fat_y, met_w); - CC_SHA256_Final(dig_y, &ctx_h); -#else - SHA256_CTX ctx_h; - - SHA256_Init(&ctx_h); - SHA256_Update(&ctx_h, fat_y, met_w); - SHA256_Final(dig_y, &ctx_h); -#endif - u3a_free(fat_y); - return u3i_bytes(32, dig_y); - } + c3_w len_w; + c3_y out_y[32]; + c3_y* dat_y = u3r_bytes_all(&len_w, a); + urcrypt_sha256(dat_y, len_w, out_y); + u3a_free(dat_y); + return u3i_bytes(32, out_y); } -// XX end preformance - u3_noun u3qe_shal(u3_atom a, u3_atom b) @@ -110,9 +84,9 @@ u3_noun u3qe_shas(u3_atom sal, u3_atom ruz) { - u3_noun one = u3qe_shax(ruz); + u3_noun one = _cqe_shax(ruz); u3_noun two = u3qc_mix(sal, one); - u3_noun tri = u3qe_shax(two); + u3_noun tri = _cqe_shax(two); u3z(one); u3z(two); return tri; } @@ -127,7 +101,7 @@ u3_noun { return u3m_bail(c3__exit); } else { - return u3qe_shax(a); + return _cqe_shax(a); } } diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 20d32f8f3..2fc42dfda 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -527,14 +527,6 @@ urcrypt_aes_cbcc_de(uint8_t **message_ptr, } } -void -urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]) -{ - _urcrypt_reverse(length, message); - SHA1(message, length, out); - _urcrypt_reverse(20, out); -} - int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]) { @@ -551,6 +543,20 @@ urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]) } } +void +urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]) +{ + _urcrypt_reverse(length, message); + SHA1(message, length, out); + _urcrypt_reverse(20, out); +} + +void +urcrypt_sha256(uint8_t *message, size_t length, uint8_t out[32]) +{ + SHA256(message, length, out); +} + /* argon2 does memory allocation, but takes function pointers in the context. * the signatures don't match, so we need these wrappers. */ diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 85aa29233..55df668b0 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -100,9 +100,11 @@ int urcrypt_aes_cbcc_de(uint8_t **message_ptr, uint8_t ivec[16], urcrypt_realloc_t realloc_ptr); -void urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]); int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]); +void urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]); +void urcrypt_sha256(uint8_t *message, size_t length, uint8_t out[32]); + typedef enum urcrypt_argon2_type { urcrypt_argon2_d = 0, urcrypt_argon2_i = 1, From 975672a4f14f9316774ee164e39a90152fe0facf Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Thu, 20 Aug 2020 15:34:01 -0700 Subject: [PATCH 33/75] shay->urcrypt --- pkg/urbit/include/jets/q.h | 1 - pkg/urbit/jets/e/shax.c | 45 ++++++++++++-------------------------- 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/pkg/urbit/include/jets/q.h b/pkg/urbit/include/jets/q.h index 9c6b2e491..9a7cbe732 100644 --- a/pkg/urbit/include/jets/q.h +++ b/pkg/urbit/include/jets/q.h @@ -128,7 +128,6 @@ u3_noun u3qes_pbk(u3_atom, u3_atom, u3_atom, u3_atom); u3_noun u3qes_pbl(u3_atom, u3_atom, u3_atom, u3_atom, u3_atom, u3_atom); - u3_noun u3qe_shay(u3_atom, u3_atom); u3_noun u3qe_shas(u3_atom, u3_atom); u3_noun u3qe_shal(u3_atom, u3_atom); diff --git a/pkg/urbit/jets/e/shax.c b/pkg/urbit/jets/e/shax.c index bd979833c..fe3cdd81d 100644 --- a/pkg/urbit/jets/e/shax.c +++ b/pkg/urbit/jets/e/shax.c @@ -13,31 +13,20 @@ /* functions */ - u3_noun - u3qe_shay(u3_atom a, - u3_atom b) + static u3_atom + _cqe_shay(u3_atom wid, + u3_atom dat) { - c3_assert(_(u3a_is_cat(a))); - c3_y* fat_y = u3a_malloc(a + 1); - - u3r_bytes(0, a, fat_y, b); - { - c3_y dig_y[32]; -#if defined(U3_OS_osx) - CC_SHA256_CTX ctx_h; - - CC_SHA256_Init(&ctx_h); - CC_SHA256_Update(&ctx_h, fat_y, a); - CC_SHA256_Final(dig_y, &ctx_h); -#else - SHA256_CTX ctx_h; - - SHA256_Init(&ctx_h); - SHA256_Update(&ctx_h, fat_y, a); - SHA256_Final(dig_y, &ctx_h); -#endif - u3a_free(fat_y); - return u3i_bytes(32, dig_y); + c3_w len_w; + if ( !u3r_word_fit(&len_w, wid) ) { + return u3m_bail(c3__fail); + } + else { + c3_y out_y[32]; + c3_y* dat_y = u3r_bytes_alloc(0, len_w, dat); + urcrypt_sha256(dat_y, len_w, out_y); + u3a_free(dat_y); + return u3i_bytes(32, out_y); } } @@ -110,20 +99,14 @@ { u3_noun a, b; -// static int few = 0; -// if(few == 0) printf("foo\r\n"); -// few++; few %= 1000; - - if ( (u3_none == (a = u3r_at(u3x_sam_2, cor))) || (u3_none == (b = u3r_at(u3x_sam_3, cor))) || (c3n == u3ud(a)) || - (c3n == u3a_is_cat(a)) || (c3n == u3ud(b)) ) { return u3m_bail(c3__exit); } else { - return u3qe_shay(a, b); + return _cqe_shay(a, b); } } From a06b087f37bb451f7ac3282629f1dd67672b938a Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Thu, 20 Aug 2020 16:01:16 -0700 Subject: [PATCH 34/75] shas->urcrypt --- pkg/urbit/include/jets/q.h | 1 - pkg/urbit/jets/e/shax.c | 22 +++++++++++++--------- pkg/urcrypt/urcrypt.c | 27 ++++++++++++++++++++++++++- pkg/urcrypt/urcrypt.h | 5 ++++- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/pkg/urbit/include/jets/q.h b/pkg/urbit/include/jets/q.h index 9a7cbe732..f78025f73 100644 --- a/pkg/urbit/include/jets/q.h +++ b/pkg/urbit/include/jets/q.h @@ -128,7 +128,6 @@ u3_noun u3qes_pbk(u3_atom, u3_atom, u3_atom, u3_atom); u3_noun u3qes_pbl(u3_atom, u3_atom, u3_atom, u3_atom, u3_atom, u3_atom); - u3_noun u3qe_shas(u3_atom, u3_atom); u3_noun u3qe_shal(u3_atom, u3_atom); u3_noun u3qe_hmac(u3_noun, u3_atom, u3_atom, diff --git a/pkg/urbit/jets/e/shax.c b/pkg/urbit/jets/e/shax.c index fe3cdd81d..5687e6ede 100644 --- a/pkg/urbit/jets/e/shax.c +++ b/pkg/urbit/jets/e/shax.c @@ -69,15 +69,19 @@ } } - u3_noun - u3qe_shas(u3_atom sal, + static u3_atom + _cqe_shas(u3_atom sal, u3_atom ruz) { - u3_noun one = _cqe_shax(ruz); - u3_noun two = u3qc_mix(sal, one); - u3_noun tri = _cqe_shax(two); + c3_w sal_w, ruz_w; + c3_y *sal_y, *ruz_y, out_y[32]; - u3z(one); u3z(two); return tri; + sal_y = u3r_bytes_all(&sal_w, sal); + ruz_y = u3r_bytes_all(&ruz_w, ruz); + urcrypt_shas(sal_y, sal_w, ruz_y, ruz_w, out_y); + u3a_free(sal_y); + u3a_free(ruz_y); + return u3i_bytes(32, out_y); } u3_noun @@ -139,7 +143,7 @@ { return u3m_bail(c3__exit); } else { - return u3qe_shas(sal, ruz); + return _cqe_shas(sal, ruz); } } @@ -156,7 +160,7 @@ while ( 0 != b ) { u3_noun x = u3qc_mix(a, c); u3_noun y = u3qc_mix(b, x); - u3_noun d = u3qe_shas(c3_s4('o','g','-','b'), y); + u3_noun d = _cqe_shas(c3_s4('o','g','-','b'), y); u3_noun m; u3z(x); u3z(y); @@ -183,7 +187,7 @@ u3_atom b) { u3_noun x = u3qc_mix(b, a); - u3_noun c = u3qe_shas(c3_s4('o','g','-','a'), x); + u3_noun c = _cqe_shas(c3_s4('o','g','-','a'), x); u3_noun l = _og_list(a, b, c); u3_noun r = u3qc_can(0, l); diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 2fc42dfda..c595e0b6c 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -552,11 +552,36 @@ urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]) } void -urcrypt_sha256(uint8_t *message, size_t length, uint8_t out[32]) +urcrypt_sha256(const uint8_t *message, size_t length, uint8_t out[32]) { SHA256(message, length, out); } +void +urcrypt_shas(uint8_t *salt, size_t salt_length, + const uint8_t *message, size_t message_length, + uint8_t out[32]) +{ + size_t i; + uint8_t mid[32]; + + // docs don't say what happens if msg overlaps with out + urcrypt_sha256(message, message_length, mid); + + if ( salt_length > 32 ) { + for ( i = 0; i < 32; i++ ) { + salt[i] ^= mid[i]; + } + urcrypt_sha256(salt, salt_length, out); + } + else { + for ( i = 0; i < salt_length; i++ ) { + mid[i] ^= salt[i]; + } + urcrypt_sha256(mid, 32, out); + } +} + /* argon2 does memory allocation, but takes function pointers in the context. * the signatures don't match, so we need these wrappers. */ diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 55df668b0..9c7e1f304 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -103,7 +103,10 @@ int urcrypt_aes_cbcc_de(uint8_t **message_ptr, int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]); void urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]); -void urcrypt_sha256(uint8_t *message, size_t length, uint8_t out[32]); +void urcrypt_sha256(const uint8_t *message, size_t length, uint8_t out[32]); +void urcrypt_shas(uint8_t *salt, size_t salt_length, + const uint8_t *message, size_t message_length, + uint8_t out[32]); typedef enum urcrypt_argon2_type { urcrypt_argon2_d = 0, From 1277ea7a65a598c94a6863c0759e7e5b30fb3b72 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 21 Aug 2020 10:38:48 -0700 Subject: [PATCH 35/75] shal->urcrypt --- pkg/urbit/include/jets/q.h | 2 -- pkg/urbit/jets/e/shax.c | 46 ++++++++++++-------------------------- pkg/urcrypt/urcrypt.c | 6 +++++ pkg/urcrypt/urcrypt.h | 1 + 4 files changed, 21 insertions(+), 34 deletions(-) diff --git a/pkg/urbit/include/jets/q.h b/pkg/urbit/include/jets/q.h index f78025f73..0259f6acd 100644 --- a/pkg/urbit/include/jets/q.h +++ b/pkg/urbit/include/jets/q.h @@ -128,8 +128,6 @@ u3_noun u3qes_pbk(u3_atom, u3_atom, u3_atom, u3_atom); u3_noun u3qes_pbl(u3_atom, u3_atom, u3_atom, u3_atom, u3_atom, u3_atom); - u3_noun u3qe_shal(u3_atom, u3_atom); - u3_noun u3qe_hmac(u3_noun, u3_atom, u3_atom, u3_atom, u3_atom, u3_atom, u3_atom); diff --git a/pkg/urbit/jets/e/shax.c b/pkg/urbit/jets/e/shax.c index 5687e6ede..b4668f9c7 100644 --- a/pkg/urbit/jets/e/shax.c +++ b/pkg/urbit/jets/e/shax.c @@ -4,12 +4,6 @@ #include "all.h" #include -#if defined(U3_OS_osx) -#include -#else -#include -#endif - /* functions */ @@ -41,31 +35,20 @@ return u3i_bytes(32, out_y); } - u3_noun - u3qe_shal(u3_atom a, - u3_atom b) + static u3_atom + _cqe_shal(u3_atom wid, + u3_atom dat) { - c3_assert(_(u3a_is_cat(a))); - c3_y* fat_y = u3a_malloc(a + 1); - - u3r_bytes(0, a, fat_y, b); - { - c3_y dig_y[64]; -#if defined(U3_OS_osx) - CC_SHA512_CTX ctx_h; - - CC_SHA512_Init(&ctx_h); - CC_SHA512_Update(&ctx_h, fat_y, a); - CC_SHA512_Final(dig_y, &ctx_h); -#else - SHA512_CTX ctx_h; - - SHA512_Init(&ctx_h); - SHA512_Update(&ctx_h, fat_y, a); - SHA512_Final(dig_y, &ctx_h); -#endif - u3a_free(fat_y); - return u3i_bytes(64, dig_y); + c3_w len_w; + if ( !u3r_word_fit(&len_w, wid) ) { + return u3m_bail(c3__fail); + } + else { + c3_y out_y[64]; + c3_y* dat_y = u3r_bytes_alloc(0, len_w, dat); + urcrypt_sha512(dat_y, len_w, out_y); + u3a_free(dat_y); + return u3i_bytes(64, out_y); } } @@ -122,12 +105,11 @@ if ( (u3_none == (a = u3r_at(u3x_sam_2, cor))) || (u3_none == (b = u3r_at(u3x_sam_3, cor))) || (c3n == u3ud(a)) || - (c3n == u3a_is_cat(a)) || (c3n == u3ud(b)) ) { return u3m_bail(c3__exit); } else { - return u3qe_shal(a, b); + return _cqe_shal(a, b); } } diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index c595e0b6c..967c65b86 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -557,6 +557,12 @@ urcrypt_sha256(const uint8_t *message, size_t length, uint8_t out[32]) SHA256(message, length, out); } +void +urcrypt_sha512(const uint8_t *message, size_t length, uint8_t out[64]) +{ + SHA512(message, length, out); +} + void urcrypt_shas(uint8_t *salt, size_t salt_length, const uint8_t *message, size_t message_length, diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 9c7e1f304..40dff7cc2 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -104,6 +104,7 @@ int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]); void urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]); void urcrypt_sha256(const uint8_t *message, size_t length, uint8_t out[32]); +void urcrypt_sha512(const uint8_t *message, size_t length, uint8_t out[64]); void urcrypt_shas(uint8_t *salt, size_t salt_length, const uint8_t *message, size_t message_length, uint8_t out[32]); From 9bd589f6df13f62140fa5528079f21aa4b0e3c05 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 21 Aug 2020 10:45:12 -0700 Subject: [PATCH 36/75] remove unused old aes c file --- pkg/urbit/jets/e/aesc.c | 121 ---------------------------------------- 1 file changed, 121 deletions(-) delete mode 100644 pkg/urbit/jets/e/aesc.c diff --git a/pkg/urbit/jets/e/aesc.c b/pkg/urbit/jets/e/aesc.c deleted file mode 100644 index f5f233970..000000000 --- a/pkg/urbit/jets/e/aesc.c +++ /dev/null @@ -1,121 +0,0 @@ -/* j/5/aes.c -** -*/ -#include "all.h" - - -#if defined(U3_OS_osx) -#include -#else -#include -#endif - -/* functions -*/ - u3_noun - u3qea_en(u3_atom a, - u3_atom b) - { - c3_y a_y[32]; - c3_y b_y[16]; -#if defined(U3_OS_osx) - size_t siz_i = 0; -#else - AES_KEY key_u; -#endif - - c3_assert(u3r_met(3, a) <= 32); - c3_assert(u3r_met(3, b) <= 16); - - u3r_bytes(0, 32, a_y, a); - u3r_bytes(0, 16, b_y, b); - -#if defined(U3_OS_osx) - if ( kCCSuccess != CCCrypt(kCCEncrypt, kCCAlgorithmAES128, - kCCOptionECBMode, a_y, kCCKeySizeAES256, 0, b_y, - 16, b_y, 16, &siz_i) ) - { - return u3m_bail(c3__exit); - } - else c3_assert(16 == siz_i); -#else - if ( 0 != AES_set_encrypt_key(a_y, 256, &key_u) ) { - return u3m_bail(c3__exit); - } - else { - AES_encrypt(b_y, b_y, &key_u); - } -#endif - - return u3i_bytes(16, b_y); - } - - u3_noun - u3wea_en(u3_noun cor) - { - u3_noun a, b; - - if ( c3n == u3r_mean(cor, u3x_sam_2, &a, u3x_sam_3, &b, 0) || - c3n == u3ud(a) || - c3n == u3ud(b) ) - { - return u3m_bail(c3__exit); - } - else { - return u3qea_en(a, b); - } - } - - u3_noun - u3qea_de(u3_atom a, - u3_atom b) - { - c3_y a_y[32]; - c3_y b_y[16]; -#if defined(U3_OS_osx) - size_t siz_i = 0; -#else - AES_KEY key_u; -#endif - - c3_assert(u3r_met(3, a) <= 32); - c3_assert(u3r_met(3, b) <= 16); - - u3r_bytes(0, 32, a_y, a); - u3r_bytes(0, 16, b_y, b); - -#if defined(U3_OS_osx) - if ( kCCSuccess != CCCrypt(kCCDecrypt, kCCAlgorithmAES128, - kCCOptionECBMode, a_y, kCCKeySizeAES256, 0, b_y, - 16, b_y, 16, &siz_i) ) - { - return u3m_bail(c3__exit); - } - else c3_assert(16 == siz_i); -#else - if ( 0 != AES_set_decrypt_key(a_y, 256, &key_u) ) { - return u3m_bail(c3__exit); - } - else { - AES_decrypt(b_y, b_y, &key_u); - } -#endif - - return u3i_bytes(16, b_y); - } - - u3_noun - u3wea_de(u3_noun cor) - { - u3_noun a, b; - - if ( c3n == u3r_mean(cor, u3x_sam_2, &a, u3x_sam_3, &b, 0) || - c3n == u3ud(a) || - c3n == u3ud(b) ) - { - return u3m_bail(c3__exit); - } - else { - return u3qea_de(a, b); - } - } From 6f7b5acd205b3b5f6315ccc8ffe363d6acd3ad94 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 21 Aug 2020 10:55:32 -0700 Subject: [PATCH 37/75] use urcrypt instead of openssl for bash calculation --- pkg/urbit/noun/jets.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/pkg/urbit/noun/jets.c b/pkg/urbit/noun/jets.c index 8a8682f61..e490aaac4 100644 --- a/pkg/urbit/noun/jets.c +++ b/pkg/urbit/noun/jets.c @@ -2,12 +2,7 @@ ** */ #include "all.h" - -#if defined(U3_OS_osx) -#include -#else -#include -#endif +#include /** Data structures. **/ @@ -133,19 +128,7 @@ _cj_bash(u3_noun bat) } // assume little-endian fat_y = (c3_y*) wor_w; -#if defined(U3_OS_osx) - CC_SHA256_CTX ctx_h; - - CC_SHA256_Init(&ctx_h); - CC_SHA256_Update(&ctx_h, fat_y, met_w); - CC_SHA256_Final(dig_y, &ctx_h); -#else - SHA256_CTX ctx_h; - - SHA256_Init(&ctx_h); - SHA256_Update(&ctx_h, fat_y, met_w); - SHA256_Final(dig_y, &ctx_h); -#endif + urcrypt_sha256(fat_y, met_w, dig_y); pro = u3i_bytes(32, dig_y); u3h_put(u3R->jed.bas_p, bat, u3k(pro)); u3a_wfree(wor_w); From 856de7e415133c4c68e9e3581eb1ab7a2738363c Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 21 Aug 2020 12:32:48 -0700 Subject: [PATCH 38/75] name the sha functions consistently --- pkg/urbit/jets/e/shax.c | 6 +++--- pkg/urbit/noun/jets.c | 2 +- pkg/urcrypt/urcrypt.c | 10 +++++----- pkg/urcrypt/urcrypt.h | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/urbit/jets/e/shax.c b/pkg/urbit/jets/e/shax.c index b4668f9c7..29945ee82 100644 --- a/pkg/urbit/jets/e/shax.c +++ b/pkg/urbit/jets/e/shax.c @@ -18,7 +18,7 @@ else { c3_y out_y[32]; c3_y* dat_y = u3r_bytes_alloc(0, len_w, dat); - urcrypt_sha256(dat_y, len_w, out_y); + urcrypt_shay(dat_y, len_w, out_y); u3a_free(dat_y); return u3i_bytes(32, out_y); } @@ -30,7 +30,7 @@ c3_w len_w; c3_y out_y[32]; c3_y* dat_y = u3r_bytes_all(&len_w, a); - urcrypt_sha256(dat_y, len_w, out_y); + urcrypt_shay(dat_y, len_w, out_y); u3a_free(dat_y); return u3i_bytes(32, out_y); } @@ -46,7 +46,7 @@ else { c3_y out_y[64]; c3_y* dat_y = u3r_bytes_alloc(0, len_w, dat); - urcrypt_sha512(dat_y, len_w, out_y); + urcrypt_shal(dat_y, len_w, out_y); u3a_free(dat_y); return u3i_bytes(64, out_y); } diff --git a/pkg/urbit/noun/jets.c b/pkg/urbit/noun/jets.c index e490aaac4..56edeeaf0 100644 --- a/pkg/urbit/noun/jets.c +++ b/pkg/urbit/noun/jets.c @@ -128,7 +128,7 @@ _cj_bash(u3_noun bat) } // assume little-endian fat_y = (c3_y*) wor_w; - urcrypt_sha256(fat_y, met_w, dig_y); + urcrypt_shay(fat_y, met_w, dig_y); pro = u3i_bytes(32, dig_y); u3h_put(u3R->jed.bas_p, bat, u3k(pro)); u3a_wfree(wor_w); diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 967c65b86..251e5cec1 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -552,13 +552,13 @@ urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]) } void -urcrypt_sha256(const uint8_t *message, size_t length, uint8_t out[32]) +urcrypt_shay(const uint8_t *message, size_t length, uint8_t out[32]) { SHA256(message, length, out); } void -urcrypt_sha512(const uint8_t *message, size_t length, uint8_t out[64]) +urcrypt_shal(const uint8_t *message, size_t length, uint8_t out[64]) { SHA512(message, length, out); } @@ -572,19 +572,19 @@ urcrypt_shas(uint8_t *salt, size_t salt_length, uint8_t mid[32]; // docs don't say what happens if msg overlaps with out - urcrypt_sha256(message, message_length, mid); + urcrypt_shay(message, message_length, mid); if ( salt_length > 32 ) { for ( i = 0; i < 32; i++ ) { salt[i] ^= mid[i]; } - urcrypt_sha256(salt, salt_length, out); + urcrypt_shay(salt, salt_length, out); } else { for ( i = 0; i < salt_length; i++ ) { mid[i] ^= salt[i]; } - urcrypt_sha256(mid, 32, out); + urcrypt_shay(mid, 32, out); } } diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 40dff7cc2..63e1e50f0 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -103,8 +103,8 @@ int urcrypt_aes_cbcc_de(uint8_t **message_ptr, int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]); void urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]); -void urcrypt_sha256(const uint8_t *message, size_t length, uint8_t out[32]); -void urcrypt_sha512(const uint8_t *message, size_t length, uint8_t out[64]); +void urcrypt_shay(const uint8_t *message, size_t length, uint8_t out[32]); +void urcrypt_shal(const uint8_t *message, size_t length, uint8_t out[64]); void urcrypt_shas(uint8_t *salt, size_t salt_length, const uint8_t *message, size_t message_length, uint8_t out[32]); From 8d657e12c13e3001505ba67635300b025f43c4a9 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Mon, 24 Aug 2020 14:33:57 -0700 Subject: [PATCH 39/75] aes-siva-en->urcrypt --- nix/pkgs/default.nix | 10 +-- nix/pkgs/urcrypt/default.nix | 4 +- pkg/urbit/jets/e/aes_siv.c | 141 +++++++++++++++++++++++++++++++---- pkg/urcrypt/Makefile | 2 +- pkg/urcrypt/urcrypt.c | 67 ++++++++++++++++- pkg/urcrypt/urcrypt.h | 13 ++++ 6 files changed, 211 insertions(+), 26 deletions(-) diff --git a/nix/pkgs/default.nix b/nix/pkgs/default.nix index db5120376..3c6d2019b 100644 --- a/nix/pkgs/default.nix +++ b/nix/pkgs/default.nix @@ -14,15 +14,15 @@ let inherit (deps) ed25519; }; - urcrypt = import ./urcrypt { - inherit pkgs ge-additions; - inherit (deps) ed25519 argon2; - }; - libaes_siv = import ./libaes_siv { inherit pkgs; }; + urcrypt = import ./urcrypt { + inherit pkgs ge-additions libaes_siv; + inherit (deps) ed25519 argon2; + }; + mkUrbit = { debug }: import ./urbit { inherit pkgs ent debug ge-additions urcrypt libaes_siv; diff --git a/nix/pkgs/urcrypt/default.nix b/nix/pkgs/urcrypt/default.nix index e81413ca0..4e0cea360 100644 --- a/nix/pkgs/urcrypt/default.nix +++ b/nix/pkgs/urcrypt/default.nix @@ -1,9 +1,9 @@ -{ pkgs, ed25519, ge-additions, argon2 }: +{ pkgs, ed25519, ge-additions, argon2, libaes_siv }: pkgs.stdenv.mkDerivation rec { name = "urcrypt"; builder = ./builder.sh; src = ../../../pkg/urcrypt; - buildInputs = [ pkgs.openssl ed25519 ge-additions argon2 ]; + buildInputs = [ pkgs.openssl ed25519 ge-additions argon2 libaes_siv ]; } diff --git a/pkg/urbit/jets/e/aes_siv.c b/pkg/urbit/jets/e/aes_siv.c index 80ad62ab6..a298e7b00 100644 --- a/pkg/urbit/jets/e/aes_siv.c +++ b/pkg/urbit/jets/e/aes_siv.c @@ -2,11 +2,135 @@ ** */ #include "all.h" +#include #include "aes_siv.h" +typedef int (*urcrypt_siv)(c3_y*, size_t, + urcrypt_aes_siv_data*, size_t, + c3_y*, c3_y[16], c3_y*); + /* functions */ + // soc_w = number of items + // mat_w = size in bytes of assoc array + // dat_w = size of allocation (array + atom storage) + static void + _cqea_measure_ads(u3_noun ads, c3_w* soc_w, c3_w *mat_w, c3_w *dat_w) + { + u3_noun i, t; + c3_w a_w, b_w, tmp_w, met_w; + + for ( a_w = b_w = 0, t = ads; u3_nul != t; ++a_w ) { + u3x_cell(t, &i, &t); + if ( c3n == u3ud(i) ) { + u3m_bail(c3__exit); + return; + } + else { + tmp_w = b_w; + b_w += u3r_met(3, i); + if ( b_w < tmp_w ) { + u3m_bail(c3__fail); + return; + } + } + } + + // check for size overflows + tmp_w = a_w * sizeof(urcrypt_aes_siv_data); + if ( (tmp_w / a_w) != sizeof(urcrypt_aes_siv_data) ) { + u3m_bail(c3__fail); + } + else if ( (*dat_w = tmp_w + b_w) < tmp_w ) { + u3m_bail(c3__fail); + } + else { + *soc_w = a_w; + *mat_w = tmp_w; + } + } + + // assumes ads is a valid (list @) because it's already been measured + static void + _cqea_encode_ads(u3_noun ads, + c3_w mat_w, + urcrypt_aes_siv_data *dat_u) + { + c3_w met_w; + u3_noun i, t; + urcrypt_aes_siv_data *cur_u; + c3_y *dat_y = ((c3_y*) dat_u) + mat_w; + + for ( cur_u = dat_u, t = ads; u3_nul != t; t = u3t(t), ++cur_u ) { + i = u3h(t); + met_w = u3r_met(3, i); + u3r_bytes(0, met_w, dat_y, i); + cur_u->length = met_w; + cur_u->bytes = dat_y; + dat_y += met_w; + } + } + + static u3_noun + _cqea_siv_en(c3_y* key_y, + c3_w key_w, + u3_noun ads, + u3_atom txt, + urcrypt_siv low_f) + { + u3_noun ret; + c3_w txt_w, soc_w; + c3_y *txt_y, *out_y, iv_y[16]; + c3_t ads_t = ( u3_nul != ads ); + urcrypt_aes_siv_data *dat_u; + + if ( !ads_t ) { + soc_w = 0; + dat_u = NULL; + } + else { + c3_w mat_w, dat_w; + + _cqea_measure_ads(ads, &soc_w, &mat_w, &dat_w); + dat_u = u3a_malloc(dat_w); + _cqea_encode_ads(ads, mat_w, dat_u); + } + + txt_y = u3r_bytes_all(&txt_w, txt); + out_y = u3a_malloc(txt_w); + + ret = ( 0 != (*low_f)(txt_y, txt_w, dat_u, soc_w, key_y, iv_y, out_y) ) + ? u3_none + : u3nt(u3i_bytes(16, iv_y), + u3i_words(1, &txt_w), + u3i_bytes(txt_w, out_y)); + + u3a_free(txt_y); + u3a_free(out_y); + if ( ads_t ) { + u3a_free(dat_u); + } + + return ret; + } + + static u3_noun + _cqea_siva_en(u3_atom key, + u3_noun ads, + u3_atom txt) + { + if ( u3r_met(3, key) > 32 ) { + // hoon doesn't explicitly check size, but we need 32. + return u3_none; + } + else { + c3_y key_y[32]; + u3r_bytes(0, 32, key_y, key); + return _cqea_siv_en(key_y, 32, ads, txt, &urcrypt_aes_siva_en); + } + } + static void u3r_bytes_reverse(c3_w a_w, c3_w b_w, c3_y* c_y, /* out */ @@ -152,21 +276,6 @@ static u3_noun _siv_de(c3_y* key_y, return u3nc(0, rev_msg); } - -u3_noun -u3qea_siva_en(u3_atom key, - u3_noun ads, - u3_atom txt) -{ - c3_y key_y[32]; - if (u3r_met(3, key) > 32) { - return u3_none; - } - - u3r_bytes_reverse(0, 32, key_y, key); - return _siv_en(key_y, 32, ads, txt); -} - u3_noun u3wea_siva_en(u3_noun cor) { @@ -179,7 +288,7 @@ u3wea_siva_en(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return u3qea_siva_en(key, ads, txt); + return _cqea_siva_en(key, ads, txt); } } diff --git a/pkg/urcrypt/Makefile b/pkg/urcrypt/Makefile index 045b85416..2f1a6abb0 100644 --- a/pkg/urcrypt/Makefile +++ b/pkg/urcrypt/Makefile @@ -16,7 +16,7 @@ liburcrypt.a: $(SOURCES) liburcrypt.so: $(SOURCES) $(CC) $(CFLAGS) -fPIC -c urcrypt.c -o urcrypt-shared.o $(CC) -shared urcrypt-shared.o -o liburcrypt.so \ - -led25519 -lge-additions -lssl -largon2 \ + -led25519 -lge-additions -lssl -largon2 -laes_siv \ -Wl,--no-undefined all: liburcrypt.a liburcrypt.so diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 251e5cec1..da21b1b74 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -5,9 +5,10 @@ #include #include -#include #include #include +#include +#include #include #include @@ -527,6 +528,69 @@ urcrypt_aes_cbcc_de(uint8_t **message_ptr, } } +static int +_urcrypt_aes_siv_en(uint8_t *key, + size_t key_length, + uint8_t *message, + size_t message_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t iv[16], + uint8_t *out) +{ + AES_SIV_CTX *ctx = AES_SIV_CTX_new(); + + if ( NULL == ctx ) { + return -1; + } + else { + int code; + _urcrypt_reverse(key_length, key); + if ( 0 == AES_SIV_Init(ctx, key, key_length) ) { + code = -2; + } + else { + uint8_t *bytes; + size_t i, blen; + + for ( i = 0; i < data_length; ++i ) { + blen = data[i].length; + bytes = data[i].bytes; + _urcrypt_reverse(blen, bytes); + if ( 0 == AES_SIV_AssociateData(ctx, bytes, blen) ) { + code = -3; + goto finish; + } + } + + _urcrypt_reverse(message_length, message); + if ( 0 == AES_SIV_EncryptFinal(ctx, iv, out, message, message_length) ) { + code = -4; + } + else { + _urcrypt_reverse(16, iv); + _urcrypt_reverse(message_length, out); + code = 0; + } + } +finish: + AES_SIV_CTX_free(ctx); + return code; + } +} + +int +urcrypt_aes_siva_en(uint8_t *message, + size_t message_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t key[32], + uint8_t iv[16], + uint8_t *out) +{ + return _urcrypt_aes_siv_en(key, 32, message, message_length, data, data_length, iv, out); +} + int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]) { @@ -641,7 +705,6 @@ urcrypt_argon2(urcrypt_argon2_type type, int (*f)(argon2_context*); int result; - switch ( type ) { default: return "unknown type"; diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 63e1e50f0..e5d4906d6 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -100,6 +100,19 @@ int urcrypt_aes_cbcc_de(uint8_t **message_ptr, uint8_t ivec[16], urcrypt_realloc_t realloc_ptr); +typedef struct { + size_t length; + uint8_t *bytes; +} urcrypt_aes_siv_data; + +int urcrypt_aes_siva_en(uint8_t *message, + size_t message_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t key[32], + uint8_t iv[16], + uint8_t *out); + int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]); void urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]); From 400c5187e696aa7864af25e1b3e2966988ab51e5 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 25 Aug 2020 12:14:13 -0700 Subject: [PATCH 40/75] siva_de --- pkg/urbit/jets/e/aes_siv.c | 317 +++++++++++++++++++++---------------- pkg/urcrypt/urcrypt.c | 123 ++++++++++---- pkg/urcrypt/urcrypt.h | 7 + 3 files changed, 284 insertions(+), 163 deletions(-) diff --git a/pkg/urbit/jets/e/aes_siv.c b/pkg/urbit/jets/e/aes_siv.c index a298e7b00..f4b2c796d 100644 --- a/pkg/urbit/jets/e/aes_siv.c +++ b/pkg/urbit/jets/e/aes_siv.c @@ -6,131 +6,7 @@ #include "aes_siv.h" -typedef int (*urcrypt_siv)(c3_y*, size_t, - urcrypt_aes_siv_data*, size_t, - c3_y*, c3_y[16], c3_y*); - -/* functions - */ - // soc_w = number of items - // mat_w = size in bytes of assoc array - // dat_w = size of allocation (array + atom storage) - static void - _cqea_measure_ads(u3_noun ads, c3_w* soc_w, c3_w *mat_w, c3_w *dat_w) - { - u3_noun i, t; - c3_w a_w, b_w, tmp_w, met_w; - - for ( a_w = b_w = 0, t = ads; u3_nul != t; ++a_w ) { - u3x_cell(t, &i, &t); - if ( c3n == u3ud(i) ) { - u3m_bail(c3__exit); - return; - } - else { - tmp_w = b_w; - b_w += u3r_met(3, i); - if ( b_w < tmp_w ) { - u3m_bail(c3__fail); - return; - } - } - } - - // check for size overflows - tmp_w = a_w * sizeof(urcrypt_aes_siv_data); - if ( (tmp_w / a_w) != sizeof(urcrypt_aes_siv_data) ) { - u3m_bail(c3__fail); - } - else if ( (*dat_w = tmp_w + b_w) < tmp_w ) { - u3m_bail(c3__fail); - } - else { - *soc_w = a_w; - *mat_w = tmp_w; - } - } - - // assumes ads is a valid (list @) because it's already been measured - static void - _cqea_encode_ads(u3_noun ads, - c3_w mat_w, - urcrypt_aes_siv_data *dat_u) - { - c3_w met_w; - u3_noun i, t; - urcrypt_aes_siv_data *cur_u; - c3_y *dat_y = ((c3_y*) dat_u) + mat_w; - - for ( cur_u = dat_u, t = ads; u3_nul != t; t = u3t(t), ++cur_u ) { - i = u3h(t); - met_w = u3r_met(3, i); - u3r_bytes(0, met_w, dat_y, i); - cur_u->length = met_w; - cur_u->bytes = dat_y; - dat_y += met_w; - } - } - - static u3_noun - _cqea_siv_en(c3_y* key_y, - c3_w key_w, - u3_noun ads, - u3_atom txt, - urcrypt_siv low_f) - { - u3_noun ret; - c3_w txt_w, soc_w; - c3_y *txt_y, *out_y, iv_y[16]; - c3_t ads_t = ( u3_nul != ads ); - urcrypt_aes_siv_data *dat_u; - - if ( !ads_t ) { - soc_w = 0; - dat_u = NULL; - } - else { - c3_w mat_w, dat_w; - - _cqea_measure_ads(ads, &soc_w, &mat_w, &dat_w); - dat_u = u3a_malloc(dat_w); - _cqea_encode_ads(ads, mat_w, dat_u); - } - - txt_y = u3r_bytes_all(&txt_w, txt); - out_y = u3a_malloc(txt_w); - - ret = ( 0 != (*low_f)(txt_y, txt_w, dat_u, soc_w, key_y, iv_y, out_y) ) - ? u3_none - : u3nt(u3i_bytes(16, iv_y), - u3i_words(1, &txt_w), - u3i_bytes(txt_w, out_y)); - - u3a_free(txt_y); - u3a_free(out_y); - if ( ads_t ) { - u3a_free(dat_u); - } - - return ret; - } - - static u3_noun - _cqea_siva_en(u3_atom key, - u3_noun ads, - u3_atom txt) - { - if ( u3r_met(3, key) > 32 ) { - // hoon doesn't explicitly check size, but we need 32. - return u3_none; - } - else { - c3_y key_y[32]; - u3r_bytes(0, 32, key_y, key); - return _cqea_siv_en(key_y, 32, ads, txt, &urcrypt_aes_siva_en); - } - } - +// REMOVE ONCE WORKING static void u3r_bytes_reverse(c3_w a_w, c3_w b_w, c3_y* c_y, /* out */ @@ -276,6 +152,180 @@ static u3_noun _siv_de(c3_y* key_y, return u3nc(0, rev_msg); } + +// END REMOVE + +typedef int (*urcrypt_siv)(c3_y*, size_t, + urcrypt_aes_siv_data*, size_t, + c3_y*, c3_y[16], c3_y*); + +/* functions +*/ +// soc_w = number of items +// mat_w = size in bytes of assoc array +// dat_w = size of allocation (array + atom storage) +static void +_cqea_measure_ads(u3_noun ads, c3_w* soc_w, c3_w *mat_w, c3_w *dat_w) +{ + u3_noun i, t; + c3_w a_w, b_w, tmp_w, met_w; + + for ( a_w = b_w = 0, t = ads; u3_nul != t; ++a_w ) { + u3x_cell(t, &i, &t); + if ( c3n == u3ud(i) ) { + u3m_bail(c3__exit); + return; + } + else { + tmp_w = b_w; + b_w += u3r_met(3, i); + if ( b_w < tmp_w ) { + u3m_bail(c3__fail); + return; + } + } + } + + // check for size overflows + tmp_w = a_w * sizeof(urcrypt_aes_siv_data); + if ( (tmp_w / a_w) != sizeof(urcrypt_aes_siv_data) ) { + u3m_bail(c3__fail); + } + else if ( (*dat_w = tmp_w + b_w) < tmp_w ) { + u3m_bail(c3__fail); + } + else { + *soc_w = a_w; + *mat_w = tmp_w; + } +} + +// assumes ads is a valid (list @) because it's already been measured +static void +_cqea_encode_ads(u3_noun ads, + c3_w mat_w, + urcrypt_aes_siv_data *dat_u) +{ + c3_w met_w; + u3_noun i, t; + urcrypt_aes_siv_data *cur_u; + c3_y *dat_y = ((c3_y*) dat_u) + mat_w; + + for ( cur_u = dat_u, t = ads; u3_nul != t; t = u3t(t), ++cur_u ) { + i = u3h(t); + met_w = u3r_met(3, i); + u3r_bytes(0, met_w, dat_y, i); + cur_u->length = met_w; + cur_u->bytes = dat_y; + dat_y += met_w; + } +} + +static void +_cqea_ads_free(urcrypt_aes_siv_data *dat_u) +{ + if ( NULL != dat_u ) { + u3a_free(dat_u); + } +} + +static urcrypt_aes_siv_data* +_cqea_ads_alloc(u3_noun ads, c3_w *soc_w) +{ + if ( !ads ) { + *soc_w = 0; + return NULL; + } + else { + c3_w mat_w, dat_w; + urcrypt_aes_siv_data *dat_u; + + _cqea_measure_ads(ads, soc_w, &mat_w, &dat_w); + dat_u = u3a_malloc(dat_w); + _cqea_encode_ads(ads, mat_w, dat_u); + return dat_u; + } +} + +static u3_noun +_cqea_siv_en(c3_y* key_y, + c3_w key_w, + u3_noun ads, + u3_atom txt, + urcrypt_siv low_f) +{ + u3_noun ret; + c3_w txt_w, soc_w; + c3_y *txt_y, *out_y, iv_y[16]; + urcrypt_aes_siv_data *dat_u; + + dat_u = _cqea_ads_alloc(ads, &soc_w); + txt_y = u3r_bytes_all(&txt_w, txt); + out_y = u3a_malloc(txt_w); + + ret = ( 0 != (*low_f)(txt_y, txt_w, dat_u, soc_w, key_y, iv_y, out_y) ) + ? u3_none + : u3nt(u3i_bytes(16, iv_y), + u3i_words(1, &txt_w), + u3i_bytes(txt_w, out_y)); + + u3a_free(txt_y); + u3a_free(out_y); + _cqea_ads_free(dat_u); + return ret; +} + +static u3_noun +_cqea_siv_de(c3_y* key_y, + c3_w key_w, + u3_noun ads, + u3_atom iv, + u3_atom len, + u3_atom txt, + urcrypt_siv low_f) +{ + c3_w txt_w; + if ( !u3r_word_fit(&txt_w, len) ) { + return u3m_bail(c3__fail); + } + else { + u3_noun ret; + c3_w soc_w; + c3_y *txt_y, *out_y, iv_y[16]; + urcrypt_aes_siv_data *dat_u; + + dat_u = _cqea_ads_alloc(ads, &soc_w); + txt_y = u3r_bytes_alloc(0, txt_w, txt); + out_y = u3a_malloc(txt_w); + + ret = ( 0 != (*low_f)(txt_y, txt_w, dat_u, soc_w, key_y, iv_y, out_y) ) + ? u3_none + : u3nc(0, u3i_bytes(txt_w, out_y)); + + u3a_free(txt_y); + u3a_free(out_y); + _cqea_ads_free(dat_u); + + return ret; + } +} + +static u3_noun +_cqea_siva_en(u3_atom key, + u3_noun ads, + u3_atom txt) +{ + if ( u3r_met(3, key) > 32 ) { + // hoon doesn't explicitly check size, but we need 32. + return u3_none; + } + else { + c3_y key_y[32]; + u3r_bytes(0, 32, key_y, key); + return _cqea_siv_en(key_y, 32, ads, txt, &urcrypt_aes_siva_en); + } +} + u3_noun u3wea_siva_en(u3_noun cor) { @@ -292,20 +342,21 @@ u3wea_siva_en(u3_noun cor) } } -u3_noun -u3qea_siva_de(u3_atom key, +static u3_noun +_cqea_siva_de(u3_atom key, u3_noun ads, u3_atom iv, u3_atom len, u3_atom txt) { - c3_y key_y[32]; - if (u3r_met(3, key) > 32) { + if ( u3r_met(3, key) > 32 ) { return u3_none; } - - u3r_bytes_reverse(0, 32, key_y, key); - return _siv_de(key_y, 32, ads, iv, len, txt); + else { + c3_y key_y[32]; + u3r_bytes(0, 32, key_y, key); + return _cqea_siv_de(key_y, 32, ads, iv, len, txt, &urcrypt_aes_siva_de); + } } u3_noun @@ -323,7 +374,7 @@ u3wea_siva_de(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return u3qea_siva_de(key, ads, iv, len, txt); + return _cqea_siva_de(key, ads, iv, len, txt); } } diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index da21b1b74..896d5dde3 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -528,6 +528,41 @@ urcrypt_aes_cbcc_de(uint8_t **message_ptr, } } +static AES_SIV_CTX* +_urcrypt_aes_siv_init(uint8_t *key, + size_t key_length, + urcrypt_aes_siv_data *data, + size_t data_length) +{ + AES_SIV_CTX *ctx = AES_SIV_CTX_new(); + if ( NULL == ctx ) { + return NULL; + } + else { + _urcrypt_reverse(key_length, key); + if ( 0 == AES_SIV_Init(ctx, key, key_length) ) { + AES_SIV_CTX_free(ctx); + return NULL; + } + else { + size_t i, len; + uint8_t *dat; + + for ( i = 0; i < data_length; ++i ) { + len = data[i].length; + dat = data[i].bytes; + _urcrypt_reverse(len, dat); + if ( 0 == AES_SIV_AssociateData(ctx, dat, len) ) { + AES_SIV_CTX_free(ctx); + return NULL; + } + } + + return ctx; + } + } +} + static int _urcrypt_aes_siv_en(uint8_t *key, size_t key_length, @@ -538,44 +573,58 @@ _urcrypt_aes_siv_en(uint8_t *key, uint8_t iv[16], uint8_t *out) { - AES_SIV_CTX *ctx = AES_SIV_CTX_new(); + AES_SIV_CTX *ctx = _urcrypt_aes_siv_init(key, key_length, data, data_length); if ( NULL == ctx ) { return -1; } else { - int code; - _urcrypt_reverse(key_length, key); - if ( 0 == AES_SIV_Init(ctx, key, key_length) ) { - code = -2; + int ret; + _urcrypt_reverse(message_length, message); + ret = AES_SIV_EncryptFinal(ctx, iv, out, message, message_length); + AES_SIV_CTX_free(ctx); + + if ( 0 == ret ) { + return -2; } else { - uint8_t *bytes; - size_t i, blen; - - for ( i = 0; i < data_length; ++i ) { - blen = data[i].length; - bytes = data[i].bytes; - _urcrypt_reverse(blen, bytes); - if ( 0 == AES_SIV_AssociateData(ctx, bytes, blen) ) { - code = -3; - goto finish; - } - } - - _urcrypt_reverse(message_length, message); - if ( 0 == AES_SIV_EncryptFinal(ctx, iv, out, message, message_length) ) { - code = -4; - } - else { - _urcrypt_reverse(16, iv); - _urcrypt_reverse(message_length, out); - code = 0; - } + _urcrypt_reverse(16, iv); + _urcrypt_reverse(message_length, out); + return 0; } -finish: + } +} + +int +_urcrypt_aes_siv_de(uint8_t *message, + size_t message_length, + uint8_t *key, + size_t key_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t iv[16], + uint8_t *out) +{ + AES_SIV_CTX *ctx = _urcrypt_aes_siv_init(key, key_length, data, data_length); + + if ( NULL == ctx ) { + return -1; + } + else { + int ret; + + _urcrypt_reverse(message_length, message); + _urcrypt_reverse(16, iv); + ret = AES_SIV_DecryptFinal(ctx, out, iv, message, message_length); AES_SIV_CTX_free(ctx); - return code; + + if ( 0 == ret ) { + return -2; + } + else { + _urcrypt_reverse(message_length, out); + return 0; + } } } @@ -588,7 +637,21 @@ urcrypt_aes_siva_en(uint8_t *message, uint8_t iv[16], uint8_t *out) { - return _urcrypt_aes_siv_en(key, 32, message, message_length, data, data_length, iv, out); + return _urcrypt_aes_siv_en(key, 32, + message, message_length, data, data_length, iv, out); +} + +int +urcrypt_aes_siva_de(uint8_t *message, + size_t message_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t key[32], + uint8_t iv[16], + uint8_t *out) +{ + return _urcrypt_aes_siv_de(key, 32, + message, message_length, data, data_length, iv, out); } int diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index e5d4906d6..9d210ad29 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -112,6 +112,13 @@ int urcrypt_aes_siva_en(uint8_t *message, uint8_t key[32], uint8_t iv[16], uint8_t *out); +int urcrypt_aes_siva_de(uint8_t *message, + size_t message_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t key[32], + uint8_t iv[16], + uint8_t *out); int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]); From fd0eaad162caefa3b5ccbaff8ed13521574c49b8 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 25 Aug 2020 15:04:21 -0700 Subject: [PATCH 41/75] aes-siv jets all using urcrypt --- pkg/urbit/jets/e/aes_siv.c | 237 ++++++++----------------------------- pkg/urcrypt/urcrypt.c | 71 ++++++++++- pkg/urcrypt/urcrypt.h | 28 +++++ 3 files changed, 145 insertions(+), 191 deletions(-) diff --git a/pkg/urbit/jets/e/aes_siv.c b/pkg/urbit/jets/e/aes_siv.c index f4b2c796d..97b05414e 100644 --- a/pkg/urbit/jets/e/aes_siv.c +++ b/pkg/urbit/jets/e/aes_siv.c @@ -4,163 +4,13 @@ #include "all.h" #include -#include "aes_siv.h" - -// REMOVE ONCE WORKING -static void u3r_bytes_reverse(c3_w a_w, - c3_w b_w, - c3_y* c_y, /* out */ - u3_atom d) /* in */ -{ - u3r_bytes(a_w, b_w, c_y, d); - c3_w i_w; - for (i_w = 0; i_w < ((b_w - a_w) / 2) ; i_w++) { - c3_y lo = c_y[i_w]; - c3_y hi = c_y[b_w - i_w - 1]; - c_y[i_w] = hi; - c_y[b_w - i_w - 1] = lo; - } - - return; -} - -static u3_noun _siv_en(c3_y* key_y, - c3_w keysize, - u3_noun ads, - u3_atom txt) -{ - AES_SIV_CTX* ctx = AES_SIV_CTX_new(); - if ( 0 == ctx ) { - return u3_none; - } - - if ( 0 == AES_SIV_Init(ctx, key_y, keysize) ) { - AES_SIV_CTX_free(ctx); - return u3_none; - } - - while (u3_nul != ads) { - c3_w ad_w = u3r_met(3, u3h(ads)); - c3_y* ad_y = u3a_malloc(ad_w); - u3r_bytes_reverse(0, ad_w, ad_y, u3h(ads)); - - c3_w ret = AES_SIV_AssociateData(ctx, ad_y, ad_w); - u3a_free(ad_y); - - if ( 0 == ret ) { - AES_SIV_CTX_free(ctx); - return u3_none; - } - - ads = u3t(ads); - } - - c3_w txt_w = u3r_met(3, txt); - c3_y* txt_y = u3a_malloc(txt_w); - u3r_bytes_reverse(0, txt_w, txt_y, txt); - - const c3_w iv_w = 16; - c3_y iv_y[iv_w]; - c3_y* out_y = u3a_malloc(txt_w); - if ( 0 == AES_SIV_EncryptFinal(ctx, iv_y, out_y, txt_y, txt_w) ) { - u3a_free(out_y); - u3a_free(txt_y); - AES_SIV_CTX_free(ctx); - return u3_none; - } - - u3a_free(txt_y); - AES_SIV_CTX_free(ctx); - - // Read the first 16 bytes as the "iv" - u3_noun iv = u3i_bytes(16, iv_y); - u3_noun msg = u3i_bytes(txt_w, out_y); - - // Reverse byte order for output - u3_noun rev_iv = u3kc_rev(3, iv_w, iv); - u3_noun rev_msg = u3kc_rev(3, txt_w, msg); - - u3a_free(out_y); - - return u3nt(rev_iv, u3i_words(1, &txt_w), rev_msg); -} - -static u3_noun _siv_de(c3_y* key_y, - c3_w keysize, - u3_noun ads, - u3_atom iv, - u3_atom len, - u3_atom txt) -{ - AES_SIV_CTX* ctx = AES_SIV_CTX_new(); - if ( 0 == ctx ) { - return u3_none; - } - - if ( 0 == AES_SIV_Init(ctx, key_y, keysize) ) { - AES_SIV_CTX_free(ctx); - return u3_none; - } - - while (u3_nul != ads) { - c3_w ad_w = u3r_met(3, u3h(ads)); - c3_y* ad_y = u3a_malloc(ad_w); - u3r_bytes_reverse(0, ad_w, ad_y, u3h(ads)); - - c3_w ret = AES_SIV_AssociateData(ctx, ad_y, ad_w); - u3a_free(ad_y); - - if ( 0 == ret ) { - AES_SIV_CTX_free(ctx); - return u3_none; - } - - ads = u3t(ads); - } - - c3_w txt_w = u3r_word(0, len); - c3_y* txt_y = u3a_malloc(txt_w); - u3r_bytes_reverse(0, txt_w, txt_y, txt); - - const c3_w iv_w = 16; - c3_y iv_y[iv_w]; - u3r_bytes_reverse(0, 16, iv_y, iv); - - c3_y* out_y = u3a_malloc(txt_w); - if ( 0 == AES_SIV_DecryptFinal(ctx, out_y, iv_y, txt_y, txt_w) ) { - u3a_free(out_y); - u3a_free(txt_y); - AES_SIV_CTX_free(ctx); - - // Either decryption failed or signature bad or there was a memory - // error. Some of these are deterministic and some are not. return u3_none - // to fallback to the Nock implementation. - return u3_none; - } - - u3a_free(txt_y); - AES_SIV_CTX_free(ctx); - - // Read the first 16 bytes as the "iv" - u3_noun msg = u3i_bytes(txt_w, out_y); - - // Reverse byte order for output - u3_noun rev_msg = u3kc_rev(3, txt_w, msg); - - u3a_free(out_y); - - return u3nc(0, rev_msg); -} - - -// END REMOVE - typedef int (*urcrypt_siv)(c3_y*, size_t, urcrypt_aes_siv_data*, size_t, c3_y*, c3_y[16], c3_y*); /* functions */ + // soc_w = number of items // mat_w = size in bytes of assoc array // dat_w = size of allocation (array + atom storage) @@ -294,6 +144,7 @@ _cqea_siv_de(c3_y* key_y, c3_y *txt_y, *out_y, iv_y[16]; urcrypt_aes_siv_data *dat_u; + u3r_bytes(0, 16, iv_y, iv); dat_u = _cqea_ads_alloc(ads, &soc_w); txt_y = u3r_bytes_alloc(0, txt_w, txt); out_y = u3a_malloc(txt_w); @@ -310,13 +161,17 @@ _cqea_siv_de(c3_y* key_y, } } +// the siv* hoon doesn't explicitly check keysizes, but all of these functions +// have fixed maximum keysizes, so we will punt if we get a key that is too +// large. + static u3_noun _cqea_siva_en(u3_atom key, u3_noun ads, u3_atom txt) { if ( u3r_met(3, key) > 32 ) { - // hoon doesn't explicitly check size, but we need 32. + u3l_log("%s\r\n", "siva-en-punt"); return u3_none; } else { @@ -350,6 +205,7 @@ _cqea_siva_de(u3_atom key, u3_atom txt) { if ( u3r_met(3, key) > 32 ) { + u3l_log("%s\r\n", "siva-de-punt"); return u3_none; } else { @@ -378,21 +234,23 @@ u3wea_siva_de(u3_noun cor) } } - -u3_noun -u3qea_sivb_en(u3_atom key, +static u3_noun +_cqea_sivb_en(u3_atom key, u3_noun ads, u3_atom txt) { - c3_y key_y[48]; - if (u3r_met(3, key) > 48) { + if ( u3r_met(3, key) > 48 ) { + u3l_log("%s\r\n", "sivb-en-punt"); return u3_none; } - - u3r_bytes_reverse(0, 48, key_y, key); - return _siv_en(key_y, 48, ads, txt); + else { + c3_y key_y[48]; + u3r_bytes(0, 48, key_y, key); + return _cqea_siv_en(key_y, 48, ads, txt, &urcrypt_aes_sivb_en); + } } + u3_noun u3wea_sivb_en(u3_noun cor) { @@ -405,24 +263,26 @@ u3wea_sivb_en(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return u3qea_sivb_en(key, ads, txt); + return _cqea_sivb_en(key, ads, txt); } } -u3_noun -u3qea_sivb_de(u3_atom key, +static u3_noun +_cqea_sivb_de(u3_atom key, u3_noun ads, u3_atom iv, u3_atom len, u3_atom txt) { - c3_y key_y[48]; - if (u3r_met(3, key) > 48) { + if ( u3r_met(3, key) > 48 ) { + u3l_log("%s\r\n", "sivb-de-punt"); return u3_none; } - - u3r_bytes_reverse(0, 48, key_y, key); - return _siv_de(key_y, 48, ads, iv, len, txt); + else { + c3_y key_y[48]; + u3r_bytes(0, 48, key_y, key); + return _cqea_siv_de(key_y, 48, ads, iv, len, txt, &urcrypt_aes_sivb_de); + } } u3_noun @@ -440,24 +300,24 @@ u3wea_sivb_de(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return u3qea_sivb_de(key, ads, iv, len, txt); + return _cqea_sivb_de(key, ads, iv, len, txt); } } - - -u3_noun -u3qea_sivc_en(u3_atom key, +static u3_noun +_cqea_sivc_en(u3_atom key, u3_noun ads, u3_atom txt) { - c3_y key_y[64]; - if (u3r_met(3, key) > 64) { + if ( u3r_met(3, key) > 64 ) { + u3l_log("%s\r\n", "sivc-en-punt"); return u3_none; } - - u3r_bytes_reverse(0, 64, key_y, key); - return _siv_en(key_y, 64, ads, txt); + else { + c3_y key_y[64]; + u3r_bytes(0, 64, key_y, key); + return _cqea_siv_en(key_y, 64, ads, txt, &urcrypt_aes_sivc_en); + } } u3_noun @@ -472,25 +332,26 @@ u3wea_sivc_en(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return u3qea_sivc_en(key, ads, txt); + return _cqea_sivc_en(key, ads, txt); } } - -u3_noun -u3qea_sivc_de(u3_atom key, +static u3_noun +_cqea_sivc_de(u3_atom key, u3_noun ads, u3_atom iv, u3_atom len, u3_atom txt) { - c3_y key_y[64]; - if (u3r_met(3, key) > 64) { + if ( u3r_met(3, key) > 64 ) { + u3l_log("%s\r\n", "sivc-de-punt"); return u3_none; } - - u3r_bytes_reverse(0, 64, key_y, key); - return _siv_de(key_y, 64, ads, iv, len, txt); + else { + c3_y key_y[64]; + u3r_bytes(0, 64, key_y, key); + return _cqea_siv_de(key_y, 64, ads, iv, len, txt, &urcrypt_aes_sivc_de); + } } u3_noun @@ -508,6 +369,6 @@ u3wea_sivc_de(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return u3qea_sivc_de(key, ads, iv, len, txt); + return _cqea_sivc_de(key, ads, iv, len, txt); } } diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 896d5dde3..dba322396 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -528,6 +528,19 @@ urcrypt_aes_cbcc_de(uint8_t **message_ptr, } } +/* FIXME TODO remove +#include +void dbg(const char* fmt, ...) +{ + va_list ap; + FILE *nukes = fopen("/tmp/urcrypt.txt", "a"); + va_start(ap, fmt); + vfprintf(nukes, fmt, ap); + va_end(ap); + fclose(nukes); +} +*/ + static AES_SIV_CTX* _urcrypt_aes_siv_init(uint8_t *key, size_t key_length, @@ -596,10 +609,10 @@ _urcrypt_aes_siv_en(uint8_t *key, } int -_urcrypt_aes_siv_de(uint8_t *message, - size_t message_length, - uint8_t *key, +_urcrypt_aes_siv_de(uint8_t *key, size_t key_length, + uint8_t *message, + size_t message_length, urcrypt_aes_siv_data *data, size_t data_length, uint8_t iv[16], @@ -654,6 +667,58 @@ urcrypt_aes_siva_de(uint8_t *message, message, message_length, data, data_length, iv, out); } +int +urcrypt_aes_sivb_en(uint8_t *message, + size_t message_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t key[48], + uint8_t iv[16], + uint8_t *out) +{ + return _urcrypt_aes_siv_en(key, 48, + message, message_length, data, data_length, iv, out); +} + +int +urcrypt_aes_sivb_de(uint8_t *message, + size_t message_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t key[48], + uint8_t iv[16], + uint8_t *out) +{ + return _urcrypt_aes_siv_de(key, 48, + message, message_length, data, data_length, iv, out); +} + +int +urcrypt_aes_sivc_en(uint8_t *message, + size_t message_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t key[64], + uint8_t iv[16], + uint8_t *out) +{ + return _urcrypt_aes_siv_en(key, 64, + message, message_length, data, data_length, iv, out); +} + +int +urcrypt_aes_sivc_de(uint8_t *message, + size_t message_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t key[64], + uint8_t iv[16], + uint8_t *out) +{ + return _urcrypt_aes_siv_de(key, 64, + message, message_length, data, data_length, iv, out); +} + int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]) { diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 9d210ad29..00e4e615f 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -119,6 +119,34 @@ int urcrypt_aes_siva_de(uint8_t *message, uint8_t key[32], uint8_t iv[16], uint8_t *out); +int urcrypt_aes_sivb_en(uint8_t *message, + size_t message_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t key[32], + uint8_t iv[16], + uint8_t *out); +int urcrypt_aes_sivb_de(uint8_t *message, + size_t message_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t key[48], + uint8_t iv[16], + uint8_t *out); +int urcrypt_aes_sivc_en(uint8_t *message, + size_t message_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t key[64], + uint8_t iv[16], + uint8_t *out); +int urcrypt_aes_sivc_de(uint8_t *message, + size_t message_length, + urcrypt_aes_siv_data *data, + size_t data_length, + uint8_t key[64], + uint8_t iv[16], + uint8_t *out); int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]); From 367db569542125bb6ad47e4c73bda86938463fd5 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 25 Aug 2020 15:18:43 -0700 Subject: [PATCH 42/75] add punt logging for aes-siv --- pkg/urbit/jets/e/aes_siv.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/pkg/urbit/jets/e/aes_siv.c b/pkg/urbit/jets/e/aes_siv.c index 97b05414e..326c2311b 100644 --- a/pkg/urbit/jets/e/aes_siv.c +++ b/pkg/urbit/jets/e/aes_siv.c @@ -161,6 +161,15 @@ _cqea_siv_de(c3_y* key_y, } } +static u3_noun +_cqea_siv_punt(c3_y* nam_y, u3_noun val) +{ + if ( u3_none == val ) { + u3l_log("%s-punt\r\n", nam_y); + } + return val; +} + // the siv* hoon doesn't explicitly check keysizes, but all of these functions // have fixed maximum keysizes, so we will punt if we get a key that is too // large. @@ -171,7 +180,6 @@ _cqea_siva_en(u3_atom key, u3_atom txt) { if ( u3r_met(3, key) > 32 ) { - u3l_log("%s\r\n", "siva-en-punt"); return u3_none; } else { @@ -193,7 +201,7 @@ u3wea_siva_en(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return _cqea_siva_en(key, ads, txt); + return _cqea_siv_punt("siva-en", _cqea_siva_en(key, ads, txt)); } } @@ -205,7 +213,6 @@ _cqea_siva_de(u3_atom key, u3_atom txt) { if ( u3r_met(3, key) > 32 ) { - u3l_log("%s\r\n", "siva-de-punt"); return u3_none; } else { @@ -230,7 +237,7 @@ u3wea_siva_de(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return _cqea_siva_de(key, ads, iv, len, txt); + return _cqea_siv_punt("siva-de", _cqea_siva_de(key, ads, iv, len, txt)); } } @@ -240,7 +247,6 @@ _cqea_sivb_en(u3_atom key, u3_atom txt) { if ( u3r_met(3, key) > 48 ) { - u3l_log("%s\r\n", "sivb-en-punt"); return u3_none; } else { @@ -263,7 +269,7 @@ u3wea_sivb_en(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return _cqea_sivb_en(key, ads, txt); + return _cqea_siv_punt("sivb-en", _cqea_sivb_en(key, ads, txt)); } } @@ -275,7 +281,6 @@ _cqea_sivb_de(u3_atom key, u3_atom txt) { if ( u3r_met(3, key) > 48 ) { - u3l_log("%s\r\n", "sivb-de-punt"); return u3_none; } else { @@ -300,7 +305,7 @@ u3wea_sivb_de(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return _cqea_sivb_de(key, ads, iv, len, txt); + return _cqea_siv_punt("sivb-de", _cqea_sivb_de(key, ads, iv, len, txt)); } } @@ -310,7 +315,6 @@ _cqea_sivc_en(u3_atom key, u3_atom txt) { if ( u3r_met(3, key) > 64 ) { - u3l_log("%s\r\n", "sivc-en-punt"); return u3_none; } else { @@ -332,7 +336,7 @@ u3wea_sivc_en(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return _cqea_sivc_en(key, ads, txt); + return _cqea_siv_punt("sivc-en", _cqea_sivc_en(key, ads, txt)); } } @@ -344,7 +348,6 @@ _cqea_sivc_de(u3_atom key, u3_atom txt) { if ( u3r_met(3, key) > 64 ) { - u3l_log("%s\r\n", "sivc-de-punt"); return u3_none; } else { @@ -369,6 +372,6 @@ u3wea_sivc_de(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return _cqea_sivc_de(key, ads, iv, len, txt); + return _cqea_siv_punt("sivc-de", _cqea_sivc_de(key, ads, iv, len, txt)); } } From 9f251af61d2f90bed92b6d4e0f8d9b316b783f26 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Wed, 26 Aug 2020 10:51:42 -0700 Subject: [PATCH 43/75] normalize punt logging with u3l_punt --- pkg/urbit/include/noun/log.h | 12 ++++++++++- pkg/urbit/jets/e/aes_cbc.c | 12 +++++------ pkg/urbit/jets/e/aes_ecb.c | 12 +++++------ pkg/urbit/jets/e/aes_siv.c | 21 ++++++------------- pkg/urbit/jets/e/argon2.c | 12 +++++------ pkg/urbit/jets/e/blake.c | 3 +-- pkg/urbit/jets/e/ed_add_double_scalarmult.c | 3 ++- .../e/ed_add_scalarmult_scalarmult_base.c | 3 ++- pkg/urbit/jets/e/ed_point_add.c | 2 +- pkg/urbit/jets/e/ed_scalarmult.c | 2 +- pkg/urbit/jets/e/ed_scalarmult_base.c | 2 +- pkg/urbit/jets/e/ed_shar.c | 2 +- pkg/urbit/jets/e/ed_veri.c | 2 +- pkg/urbit/jets/e/ripe.c | 13 +++++------- pkg/urbit/noun/log.c | 9 ++++++++ 15 files changed, 59 insertions(+), 51 deletions(-) diff --git a/pkg/urbit/include/noun/log.h b/pkg/urbit/include/noun/log.h index 204cb8505..07ab2296c 100644 --- a/pkg/urbit/include/noun/log.h +++ b/pkg/urbit/include/noun/log.h @@ -2,8 +2,18 @@ ** */ -/* u3_log(): logs to stderr or redirects to configured function. +/* u3l_log(): logs to stderr or redirects to configured function. */ void u3l_log(const char* format, ...) __attribute__ ((format (printf, 1, 2))); + +/* u3l_punt(): condtionally logs a named punt + * (e.g. "mint-punt" for the `name` "mint") + * when `pro` is u3_none, and returns pro. + * For use when a jet driver declines to handle + * a core, when the user should be somehow notified + * (e.g. in a cryptographic jet). + */ + u3_weak + u3l_punt(const char* name, u3_weak pro); diff --git a/pkg/urbit/jets/e/aes_cbc.c b/pkg/urbit/jets/e/aes_cbc.c index 793304ad4..b561efa44 100644 --- a/pkg/urbit/jets/e/aes_cbc.c +++ b/pkg/urbit/jets/e/aes_cbc.c @@ -57,7 +57,7 @@ typedef int (*urcrypt_cbc)(c3_y**, c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return _cqea_cbca_en(a, b, c); + return u3l_punt("cbca-en", _cqea_cbca_en(a, b, c)); } } @@ -81,7 +81,7 @@ typedef int (*urcrypt_cbc)(c3_y**, c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return _cqea_cbca_de(a, b, c); + return u3l_punt("cbca-de", _cqea_cbca_de(a, b, c)); } } @@ -105,7 +105,7 @@ typedef int (*urcrypt_cbc)(c3_y**, c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return _cqea_cbcb_en(a, b, c); + return u3l_punt("cbcb-en", _cqea_cbcb_en(a, b, c)); } } @@ -129,7 +129,7 @@ typedef int (*urcrypt_cbc)(c3_y**, c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return _cqea_cbcb_de(a, b, c); + return u3l_punt("cbcb-de", _cqea_cbcb_de(a, b, c)); } } @@ -153,7 +153,7 @@ typedef int (*urcrypt_cbc)(c3_y**, c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return _cqea_cbcc_en(a, b, c); + return u3l_punt("cbcc-en", _cqea_cbcc_en(a, b, c)); } } @@ -177,6 +177,6 @@ typedef int (*urcrypt_cbc)(c3_y**, c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return _cqea_cbcc_de(a, b, c); + return u3l_punt("cbcc-de", _cqea_cbcc_de(a, b, c)); } } diff --git a/pkg/urbit/jets/e/aes_ecb.c b/pkg/urbit/jets/e/aes_ecb.c index 9ea9c59ed..d350bc808 100644 --- a/pkg/urbit/jets/e/aes_ecb.c +++ b/pkg/urbit/jets/e/aes_ecb.c @@ -46,7 +46,7 @@ typedef int (*urcrypt_ecb)(c3_y*, c3_y[16], c3_y[16]); c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return _cqea_ecba_en(a, b); + return u3l_punt("ecba-en", _cqea_ecba_en(a, b)); } } @@ -69,7 +69,7 @@ typedef int (*urcrypt_ecb)(c3_y*, c3_y[16], c3_y[16]); c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return _cqea_ecba_de(a, b); + return u3l_punt("ecba-de", _cqea_ecba_de(a, b)); } } @@ -92,7 +92,7 @@ typedef int (*urcrypt_ecb)(c3_y*, c3_y[16], c3_y[16]); c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return _cqea_ecbb_en(a, b); + return u3l_punt("ecbb-en", _cqea_ecbb_en(a, b)); } } @@ -115,7 +115,7 @@ typedef int (*urcrypt_ecb)(c3_y*, c3_y[16], c3_y[16]); c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return _cqea_ecbb_de(a, b); + return u3l_punt("ecbb-de", _cqea_ecbb_de(a, b)); } } @@ -138,7 +138,7 @@ typedef int (*urcrypt_ecb)(c3_y*, c3_y[16], c3_y[16]); c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return _cqea_ecbc_en(a, b); + return u3l_punt("ecbc-en", _cqea_ecbc_en(a, b)); } } @@ -161,6 +161,6 @@ typedef int (*urcrypt_ecb)(c3_y*, c3_y[16], c3_y[16]); c3n == u3ud(b) ) { return u3m_bail(c3__exit); } else { - return _cqea_ecbc_de(a, b); + return u3l_punt("ecbc-de", _cqea_ecbc_de(a, b)); } } diff --git a/pkg/urbit/jets/e/aes_siv.c b/pkg/urbit/jets/e/aes_siv.c index 326c2311b..a891eb431 100644 --- a/pkg/urbit/jets/e/aes_siv.c +++ b/pkg/urbit/jets/e/aes_siv.c @@ -161,15 +161,6 @@ _cqea_siv_de(c3_y* key_y, } } -static u3_noun -_cqea_siv_punt(c3_y* nam_y, u3_noun val) -{ - if ( u3_none == val ) { - u3l_log("%s-punt\r\n", nam_y); - } - return val; -} - // the siv* hoon doesn't explicitly check keysizes, but all of these functions // have fixed maximum keysizes, so we will punt if we get a key that is too // large. @@ -201,7 +192,7 @@ u3wea_siva_en(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return _cqea_siv_punt("siva-en", _cqea_siva_en(key, ads, txt)); + return u3l_punt("siva-en", _cqea_siva_en(key, ads, txt)); } } @@ -237,7 +228,7 @@ u3wea_siva_de(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return _cqea_siv_punt("siva-de", _cqea_siva_de(key, ads, iv, len, txt)); + return u3l_punt("siva-de", _cqea_siva_de(key, ads, iv, len, txt)); } } @@ -269,7 +260,7 @@ u3wea_sivb_en(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return _cqea_siv_punt("sivb-en", _cqea_sivb_en(key, ads, txt)); + return u3l_punt("sivb-en", _cqea_sivb_en(key, ads, txt)); } } @@ -305,7 +296,7 @@ u3wea_sivb_de(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return _cqea_siv_punt("sivb-de", _cqea_sivb_de(key, ads, iv, len, txt)); + return u3l_punt("sivb-de", _cqea_sivb_de(key, ads, iv, len, txt)); } } @@ -336,7 +327,7 @@ u3wea_sivc_en(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return _cqea_siv_punt("sivc-en", _cqea_sivc_en(key, ads, txt)); + return u3l_punt("sivc-en", _cqea_sivc_en(key, ads, txt)); } } @@ -372,6 +363,6 @@ u3wea_sivc_de(u3_noun cor) c3n == u3ud(txt) ) { return u3m_bail(c3__exit); } else { - return _cqea_siv_punt("sivc-de", _cqea_sivc_de(key, ads, iv, len, txt)); + return u3l_punt("sivc-de", _cqea_sivc_de(key, ads, iv, len, txt)); } } diff --git a/pkg/urbit/jets/e/argon2.c b/pkg/urbit/jets/e/argon2.c index b9c9415ac..e2f2e9bf3 100644 --- a/pkg/urbit/jets/e/argon2.c +++ b/pkg/urbit/jets/e/argon2.c @@ -55,7 +55,6 @@ u3r_word_fit(&ted_w, threads) && u3r_word_fit(&mem_w, mem_cost) && u3r_word_fit(&tim_w, time_cost)) ) { - u3l_log("%s\r\n", "argon2-punt"); return u3_none; } else { @@ -86,7 +85,7 @@ } else { ret = u3_none; - u3l_log("argon2-punt: %s\r\n", err_c); + u3l_log("argon2-error: %s\r\n", err_c); } u3a_free(out_y); @@ -130,9 +129,10 @@ return u3m_bail(c3__exit); } else { - return _cqe_argon2(out, type, version, - threads, mem_cost, time_cost, - wik, key, wix, extra, - wid, dat, wis, sat); + return u3l_punt("argon2", + _cqe_argon2(out, type, version, + threads, mem_cost, time_cost, + wik, key, wix, extra, + wid, dat, wis, sat)); } } diff --git a/pkg/urbit/jets/e/blake.c b/pkg/urbit/jets/e/blake.c index eb36ba87d..1770a3f89 100644 --- a/pkg/urbit/jets/e/blake.c +++ b/pkg/urbit/jets/e/blake.c @@ -34,7 +34,6 @@ return u3i_bytes(out_w, out_y); } else { - u3l_log("%s\r\n", "blake2-punt: library error"); return u3_none; } } @@ -56,6 +55,6 @@ { return u3m_bail(c3__exit); } else { - return _cqe_blake(wid, dat, wik, dak, out); + return u3l_punt("blake", _cqe_blake(wid, dat, wik, dak, out)); } } diff --git a/pkg/urbit/jets/e/ed_add_double_scalarmult.c b/pkg/urbit/jets/e/ed_add_double_scalarmult.c index af73d5964..e3f01950d 100644 --- a/pkg/urbit/jets/e/ed_add_double_scalarmult.c +++ b/pkg/urbit/jets/e/ed_add_double_scalarmult.c @@ -41,6 +41,7 @@ { return u3m_bail(c3__exit); } else { - return _cqee_add_double_scalarmult(a, b, c, d); + return u3l_punt("add-double-scalarmult", + _cqee_add_double_scalarmult(a, b, c, d)); } } diff --git a/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c b/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c index 2db437aec..26dd69994 100644 --- a/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c +++ b/pkg/urbit/jets/e/ed_add_scalarmult_scalarmult_base.c @@ -38,6 +38,7 @@ { return u3m_bail(c3__exit); } else { - return _cqee_add_scalarmult_scalarmult_base(a, b, c); + return u3l_punt("add-scalarmult-scalarmult-base", + _cqee_add_scalarmult_scalarmult_base(a, b, c)); } } diff --git a/pkg/urbit/jets/e/ed_point_add.c b/pkg/urbit/jets/e/ed_point_add.c index 2a5da2938..dfa52502e 100644 --- a/pkg/urbit/jets/e/ed_point_add.c +++ b/pkg/urbit/jets/e/ed_point_add.c @@ -35,6 +35,6 @@ { return u3m_bail(c3__exit); } else { - return _cqee_point_add(a, b); + return u3l_punt("point-add", _cqee_point_add(a, b)); } } diff --git a/pkg/urbit/jets/e/ed_scalarmult.c b/pkg/urbit/jets/e/ed_scalarmult.c index 97c88c4f6..136d51af2 100644 --- a/pkg/urbit/jets/e/ed_scalarmult.c +++ b/pkg/urbit/jets/e/ed_scalarmult.c @@ -35,6 +35,6 @@ { return u3m_bail(c3__exit); } else { - return _cqee_scalarmult(a, b); + return u3l_punt("scalarmult", _cqee_scalarmult(a, b)); } } diff --git a/pkg/urbit/jets/e/ed_scalarmult_base.c b/pkg/urbit/jets/e/ed_scalarmult_base.c index 9756e65a5..965ba8b22 100644 --- a/pkg/urbit/jets/e/ed_scalarmult_base.c +++ b/pkg/urbit/jets/e/ed_scalarmult_base.c @@ -30,6 +30,6 @@ return u3m_bail(c3__exit); } else { - return _cqee_scalarmult_base(a); + return u3l_punt("scalarmult-base", _cqee_scalarmult_base(a)); } } diff --git a/pkg/urbit/jets/e/ed_shar.c b/pkg/urbit/jets/e/ed_shar.c index 7db4e6175..e26b0627c 100644 --- a/pkg/urbit/jets/e/ed_shar.c +++ b/pkg/urbit/jets/e/ed_shar.c @@ -35,6 +35,6 @@ { return u3m_bail(c3__exit); } else { - return _cqee_shar(pub, sek); + return u3l_punt("shar", _cqee_shar(pub, sek)); } } diff --git a/pkg/urbit/jets/e/ed_veri.c b/pkg/urbit/jets/e/ed_veri.c index c4a4b6f2d..51ac20c13 100644 --- a/pkg/urbit/jets/e/ed_veri.c +++ b/pkg/urbit/jets/e/ed_veri.c @@ -37,6 +37,6 @@ u3x_sam_7, &c, 0) ) { return u3m_bail(c3__fail); } else { - return _cqee_veri(a, b, c); + return u3l_punt("veri", _cqee_veri(a, b, c)); } } diff --git a/pkg/urbit/jets/e/ripe.c b/pkg/urbit/jets/e/ripe.c index 971a04b2c..457068d47 100644 --- a/pkg/urbit/jets/e/ripe.c +++ b/pkg/urbit/jets/e/ripe.c @@ -18,13 +18,10 @@ c3_y out_y[20]; c3_y *dat_y = u3r_bytes_alloc(0, len_w, dat); - if ( 0 == urcrypt_ripemd160(dat_y, len_w, out_y) ) { - ret = u3i_bytes(20, out_y); - } - else { - u3l_log("%s\r\n", "ripemd160-punt"); - ret = u3_none; - } + ret = ( 0 == urcrypt_ripemd160(dat_y, len_w, out_y) ) + ? u3i_bytes(20, out_y) + : u3_none; + u3a_free(dat_y); return ret; } @@ -43,6 +40,6 @@ return u3m_bail(c3__exit); } else { - return _cqe_ripe(wid, dat); + return u3l_punt("ripe", _cqe_ripe(wid, dat)); } } diff --git a/pkg/urbit/noun/log.c b/pkg/urbit/noun/log.c index 8d58fef29..866a7430e 100644 --- a/pkg/urbit/noun/log.c +++ b/pkg/urbit/noun/log.c @@ -26,3 +26,12 @@ u3l_log(const char* format, ...) va_end(myargs); } + +u3_weak +u3l_punt(const char* name, u3_weak pro) +{ + if ( u3_none == pro ) { + u3l_log("%s-punt\r\n", name); + } + return pro; +} From f5449204e18325c0b448d8f696d313450a15da06 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Sat, 29 Aug 2020 11:55:58 -0700 Subject: [PATCH 44/75] secp_make->urcrypt --- nix/pkgs/default.nix | 5 +- nix/pkgs/urcrypt/default.nix | 8 ++- pkg/urbit/include/jets/q.h | 1 - pkg/urbit/jets/e/secp.c | 125 +++++++++++++++++++++++------------ pkg/urcrypt/Makefile | 2 +- pkg/urcrypt/urcrypt.c | 24 +++++++ pkg/urcrypt/urcrypt.h | 3 + 7 files changed, 118 insertions(+), 50 deletions(-) diff --git a/nix/pkgs/default.nix b/nix/pkgs/default.nix index 3c6d2019b..7a51f3187 100644 --- a/nix/pkgs/default.nix +++ b/nix/pkgs/default.nix @@ -19,8 +19,9 @@ let }; urcrypt = import ./urcrypt { - inherit pkgs ge-additions libaes_siv; - inherit (deps) ed25519 argon2; + inherit ge-additions libaes_siv; + inherit (pkgs) stdenv openssl gmp; + inherit (deps) ed25519 argon2 secp256k1; }; mkUrbit = { debug }: diff --git a/nix/pkgs/urcrypt/default.nix b/nix/pkgs/urcrypt/default.nix index 4e0cea360..739d05005 100644 --- a/nix/pkgs/urcrypt/default.nix +++ b/nix/pkgs/urcrypt/default.nix @@ -1,9 +1,11 @@ -{ pkgs, ed25519, ge-additions, argon2, libaes_siv }: +{ stdenv, openssl, gmp, ed25519, secp256k1, ge-additions, argon2, libaes_siv }: -pkgs.stdenv.mkDerivation rec { +stdenv.mkDerivation rec { name = "urcrypt"; builder = ./builder.sh; src = ../../../pkg/urcrypt; - buildInputs = [ pkgs.openssl ed25519 ge-additions argon2 libaes_siv ]; + buildInputs = [ + openssl gmp ed25519 secp256k1 argon2 ge-additions libaes_siv + ]; } diff --git a/pkg/urbit/include/jets/q.h b/pkg/urbit/include/jets/q.h index 0259f6acd..cbd673138 100644 --- a/pkg/urbit/include/jets/q.h +++ b/pkg/urbit/include/jets/q.h @@ -132,7 +132,6 @@ u3_atom, u3_atom, u3_atom, u3_atom); - u3_noun u3qe_make(u3_atom has, u3_atom prv); u3_noun u3qe_reco(u3_atom has, u3_atom sig_v, u3_atom sig_r, u3_atom sig_s); u3_noun u3qe_sign(u3_atom has, u3_atom prv); diff --git a/pkg/urbit/jets/e/secp.c b/pkg/urbit/jets/e/secp.c index 746d3b255..b85fcd174 100644 --- a/pkg/urbit/jets/e/secp.c +++ b/pkg/urbit/jets/e/secp.c @@ -4,10 +4,65 @@ #include "all.h" #include "../include/secp256k1.h" #include "../include/secp256k1_recovery.h" +#include "urcrypt.h" /* util funcs */ +static c3_t +_cqe_is_8(const c3_w words[8], u3_noun non) +{ + if ( (c3n == u3ud(non)) || + (8 != u3r_met(5, non)) ) { + return 0; + } + else { + c3_w i; + for ( i = 0; i < 8; ++i ) { + if ( words[i] != u3r_word(i, non) ) { + return 0; + } + } + return 1; + } +} + +/* FIXME!!! + * The hoon contains a generic secp core which is actually jetted, and then + * +secp256k1 passes these constants to it. This should be restructured so + * that the constants are matched as part of the batteries in the core stack + * of the jetted gates. +*/ +static c3_t +_cqe_256k1_veri(u3_noun cor) +{ + static const c3_w pow_w[8] = { + 0xfffffc2f, 0xfffffffe, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; + static const c3_w xow_w[8] = { + 0x16f81798, 0x59f2815b, 0x2dce28d9, 0x29bfcdb, + 0xce870b07, 0x55a06295, 0xf9dcbbac, 0x79be667e }; + static const c3_w yow_w[8] = { + 0xfb10d4b8, 0x9c47d08f, 0xa6855419, 0xfd17b448, + 0xe1108a8, 0x5da4fbfc, 0x26a3c465, 0x483ada77 }; + static const c3_w now_w[8] = { + 0xd0364141, 0xbfd25e8c, 0xaf48a03b, 0xbaaedce6, + 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff }; + + u3_noun w, p, b, mor, a, g, x, y, n, sam = u3x_at(254, cor); + u3x_qual(sam, &w, &p, &b, &mor); + u3x_trel(mor, &a, &g, &n); + u3x_cell(g, &x, &y); + + return ( 32 == w && + 0 == b && + 7 == a && + _cqe_is_8(pow_w, p) && + _cqe_is_8(xow_w, x) && + _cqe_is_8(yow_w, y) && + _cqe_is_8(now_w, n) ); +} + /* no guarantees if 'in' and 'out' overlap / are the same */ static void byte_reverse(c3_y *i_y, /* in */ c3_y *o_y, /* out */ @@ -257,6 +312,26 @@ u3qe_reco(u3_atom has, return(u3nc(x, y)); } +static u3_atom +_cqe_make(u3_atom has, + u3_atom prv) +{ + c3_y has_y[32], prv_y[32], out_y[32]; + + if ( ( u3r_met(3, has) > 32 ) || + ( u3r_met(3, prv) > 32 ) ) { + // hoon doesn't check size + return u3_none; + } + else { + u3r_bytes(0, 32, has_y, has); + u3r_bytes(0, 32, prv_y, prv); + + return ( 0 == urcrypt_secp_make(has_y, prv_y, out_y) ) + ? u3i_bytes(32, out_y) + : u3_none; + } +} u3_noun u3we_make(u3_noun cor) @@ -267,49 +342,13 @@ u3we_make(u3_noun cor) u3x_sam_3, &prv, 0)) || (c3n == u3ud(has)) || - (c3n == u3ud(prv)) ) - { - u3l_log("\rsecp jet: crypto package error\n"); - return u3m_bail(c3__exit); - } else { - return u3qe_make(has, prv); - } -} - - - -u3_noun -u3qe_make(u3_atom has, - u3_atom prv) -{ - - c3_y hel_y[32]; /* hash, little endian */ - c3_y heb_y[32]; /* hash, big endian */ - u3r_bytes(0, 32, hel_y, has); - byte_reverse(hel_y, heb_y, 32); - - c3_y pel_y[32]; /* priv key, little endian */ - c3_y peb_y[32]; /* priv key, big endian */ - u3r_bytes(0, 32, pel_y, prv); - byte_reverse(pel_y, peb_y, 32); - - - c3_ws ret_ws; - c3_y neb_y[32]; /* nonce */ - ret_ws = secp256k1_nonce_function_rfc6979(neb_y, /* OUT: return arg for nonce */ - (const c3_y *) heb_y, /* IN: message / hash */ - (const c3_y *) peb_y, /* IN: key32 */ - NULL, /* IN: algorithm (NULL == ECDSA) */ - (void *) NULL, /* IN: arbitrary data pointer (unused) */ - 0); /* IN: attempt number (0 == normal) */ - if (1 != ret_ws) { - u3l_log("\rsecp jet: crypto package error\n"); + (c3n == u3ud(prv)) ) { return u3m_bail(c3__exit); } - - c3_y nel_y[32]; - byte_reverse(neb_y, nel_y, 32); - u3_noun non = u3i_words(8, (const c3_w*) nel_y); - return(non); + else { + return u3l_punt("secp-make", + _cqe_256k1_veri(cor) + ? _cqe_make(has, prv) + : u3_none); + } } - diff --git a/pkg/urcrypt/Makefile b/pkg/urcrypt/Makefile index 2f1a6abb0..7c131b0f0 100644 --- a/pkg/urcrypt/Makefile +++ b/pkg/urcrypt/Makefile @@ -16,7 +16,7 @@ liburcrypt.a: $(SOURCES) liburcrypt.so: $(SOURCES) $(CC) $(CFLAGS) -fPIC -c urcrypt.c -o urcrypt-shared.o $(CC) -shared urcrypt-shared.o -o liburcrypt.so \ - -led25519 -lge-additions -lssl -largon2 -laes_siv \ + -led25519 -lge-additions -lssl -largon2 -laes_siv -lgmp -lsecp256k1 \ -Wl,--no-undefined all: liburcrypt.a liburcrypt.so diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index dba322396..4aa52eafd 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -13,6 +13,9 @@ #include #include +#include +#include + static urcrypt_malloc_t _urcrypt_ssl_malloc_ptr; static urcrypt_realloc_t _urcrypt_ssl_realloc_ptr; static urcrypt_free_t _urcrypt_ssl_free_ptr; @@ -916,3 +919,24 @@ urcrypt_blake2(size_t message_length, } } } + +int +urcrypt_secp_make(uint8_t hash[32], uint8_t key[32], uint8_t out[32]) +{ + _urcrypt_reverse(32, hash); + _urcrypt_reverse(32, key); + + if ( 1 != secp256k1_nonce_function_rfc6979( + out, // OUT: return arg for nonce + hash, // IN: message / hash */ + key, // IN: key32 + NULL, // IN: algorithm (NULL == ECDSA) + NULL, // IN: arbitrary data pointer (unused) + 0) ) { // IN: attempt number (0 == normal) + return -1; + } + else { + _urcrypt_reverse(32, out); + return 0; + } +} diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 00e4e615f..324fab73e 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -23,6 +23,7 @@ int urcrypt_set_openssl_mem_functions(urcrypt_malloc_t malloc_ptr, urcrypt_free_t free_ptr); // const arguments are not written to, non-const arguments may be +// all arrays are in little-endian byte order. // array sizes[64] are purely documentary // 0 on success, result in out @@ -190,4 +191,6 @@ int urcrypt_blake2(size_t message_length, size_t out_length, uint8_t *out); +int urcrypt_secp_make(uint8_t hash[32], uint8_t key[32], uint8_t out[32]); + #endif From 7870add409e09d435cdbc4e4ff99799ab8266745 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Thu, 24 Sep 2020 10:57:04 -0700 Subject: [PATCH 45/75] WIP: returning from secp rectification on master and preparing for rebase and merge. --- pkg/urbit/daemon/main.c | 6 +- pkg/urbit/include/noun/manage.h | 4 ++ pkg/urbit/jets/e/argon2.c | 4 +- pkg/urbit/jets/e/secp.c | 33 +++++++-- pkg/urbit/noun/manage.c | 66 ++++++++++++------ pkg/urbit/vere/king.c | 6 ++ pkg/urbit/worker/main.c | 6 ++ pkg/urcrypt/urcrypt.c | 118 +++++++++++++++++++++++++++++++- pkg/urcrypt/urcrypt.h | 37 ++++++++-- 9 files changed, 241 insertions(+), 39 deletions(-) diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index 95d097b31..e811d4c8b 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -619,7 +619,6 @@ main(c3_i argc, if ( c3y == u3_Host.ops_u.tex ) { u3_Host.bot_f = _stop_on_boot_completed_cb; } - #if 0 if ( 0 == getuid() ) { chroot(u3_Host.dir_c); @@ -748,12 +747,17 @@ main(c3_i argc, } } + /* FIXME // Initialize OpenSSL for client and server // { SSL_library_init(); SSL_load_error_strings(); } + */ + + // openssl won't init after this libcurl call, probably because it links + // against openssl and does allocations with it. // initialize curl // diff --git a/pkg/urbit/include/noun/manage.h b/pkg/urbit/include/noun/manage.h index a05ce7871..33a75078d 100644 --- a/pkg/urbit/include/noun/manage.h +++ b/pkg/urbit/include/noun/manage.h @@ -14,6 +14,10 @@ c3_d u3m_boot_lite(void); + /* u3m_stop(): graceful shutdown cleanup. */ + void + u3m_stop(void); + /* u3m_bail(): bail out. Does not return. ** ** Bail motes: diff --git a/pkg/urbit/jets/e/argon2.c b/pkg/urbit/jets/e/argon2.c index e2f2e9bf3..69901baad 100644 --- a/pkg/urbit/jets/e/argon2.c +++ b/pkg/urbit/jets/e/argon2.c @@ -8,7 +8,7 @@ */ static c3_t - _cqear_unpack_type(urcrypt_argon2_type *out, u3_atom in) + _cqear_unpack_type(c3_y* out, u3_atom in) { switch ( in ) { default: @@ -39,7 +39,7 @@ // input params u3_atom wid, u3_atom dat, u3_atom wis, u3_atom sat ) { - urcrypt_argon2_type typ_u; + c3_y typ_u; c3_w out_w, wik_w, wix_w, wid_w, wis_w, ver_w, ted_w, mem_w, tim_w; if ( !(u3r_word_fit(&out_w, out) && diff --git a/pkg/urbit/jets/e/secp.c b/pkg/urbit/jets/e/secp.c index b85fcd174..5052a956a 100644 --- a/pkg/urbit/jets/e/secp.c +++ b/pkg/urbit/jets/e/secp.c @@ -32,6 +32,9 @@ _cqe_is_8(const c3_w words[8], u3_noun non) * +secp256k1 passes these constants to it. This should be restructured so * that the constants are matched as part of the batteries in the core stack * of the jetted gates. + * + * The hoon also doesn't, in general, check the size of any of its arguments; + * we return u3_none when they are mis-sized. */ static c3_t _cqe_256k1_veri(u3_noun cor) @@ -211,6 +214,28 @@ u3we_reco(u3_noun cor) } } +static u3_noun +_cqe_reco(u3_atom has, + u3_atom siv, /* signature: v */ + u3_atom sir, /* signature: r */ + u3_atom sis) /* signature: s */ +{ + c3_y has_y[32], sir_y[32], sis_y[32], x_y[32], y_y[32]; + + if ( !((siv < 4) && + u3r_bytes_fit(32, has_y, has) && + u3r_bytes_fit(32, sir_y, sir) && + u3r_bytes_fit(32, sis_y, sis) && + (0 == urcrypt_secp_reco(has_y, (c3_y) siv, sir_y, sis_y, x_y, y_y))) ) + { + return u3_none; + } + else { + return u3nc(u3i_bytes(32, x_y), + u3i_bytes(32, y_y)); + } +} + u3_noun u3qe_reco(u3_atom has, u3_atom siv, /* signature: v */ @@ -318,15 +343,11 @@ _cqe_make(u3_atom has, { c3_y has_y[32], prv_y[32], out_y[32]; - if ( ( u3r_met(3, has) > 32 ) || - ( u3r_met(3, prv) > 32 ) ) { - // hoon doesn't check size + if ( !(u3r_bytes_fit(32, has_y, has) && + u3r_bytes_fit(32, prv_y, prv)) ) { return u3_none; } else { - u3r_bytes(0, 32, has_y, has); - u3r_bytes(0, 32, prv_y, prv); - return ( 0 == urcrypt_secp_make(has_y, prv_y, out_y) ) ? u3i_bytes(32, out_y) : u3_none; diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 873cc3fee..83445ab0a 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -1,6 +1,7 @@ /* n/m.c ** */ +#include #include #include #include @@ -1601,6 +1602,41 @@ _cm_signals(void) } } +static void +_cm_crypto_init() +{ + /* Initialize OpenSSL with loom allocation functions. + * IMPORTANT: this should come before the urcrypt_set_openssl [...] + * call, because in usual circumstances that will override the functions + * we set here, but weird linking situations we don't currently encounter + * could mean we need to set both, and it doesn't hurt. + */ + if ( 0 == CRYPTO_set_mem_functions(&u3a_malloc_ssl, + &u3a_realloc_ssl, + &u3a_free_ssl) ) { + u3l_log("%s\r\n", "openssl initialization failed"); + exit(1); + } + + /* Initialize cryptography suite */ + if ( 0 != urcrypt_set_openssl_mem_functions(&u3a_malloc, + &u3a_realloc, + &u3a_free) ) { + u3l_log("%s\r\n", "urcrypt-openssl initialization failed"); + exit(1); + } + + { + c3_y ent_y[32]; + ent_getentropy(ent_y, 32); + + if ( 0 != urcrypt_secp_init(ent_y) ) { + u3l_log("%s\r\n", "urcrypt-secp initialization failed"); + exit(1); + } + } +} + /* u3m_init(): start the environment. */ void @@ -1608,6 +1644,7 @@ u3m_init(void) { _cm_limits(); _cm_signals(); + _cm_crypto_init(); /* Make sure GMP uses our malloc. */ @@ -1646,6 +1683,13 @@ u3m_init(void) } } +/* u3m_stop(): graceful shutdown cleanup. */ +void +u3m_stop() +{ + urcrypt_secp_cleanup(); +} + /* u3m_boot(): start the u3 system. return next event, starting from 1. */ c3_d @@ -1657,28 +1701,6 @@ u3m_boot(c3_c* dir_c) */ u3m_init(); - /* Initialize OpenSSL with loom allocation functions. - * IMPORTANT: this should come before the urcrypt_set_openssl [...] - * call, because in usual circumstances that will override the functions - * we set here, but weird linking situations we don't currently encounter - * could mean we need to set both, and it doesn't hurt. - */ - if ( 0 == CRYPTO_set_mem_functions(&u3a_malloc_ssl, - &u3a_realloc_ssl, - &u3a_free_ssl) ) { - u3l_log("%s\r\n", "openssl initialization failed"); - exit(1); - } - - /* Initialize cryptography suite with loom allocation functions. - */ - if ( 0 != urcrypt_set_openssl_mem_functions(&u3a_malloc, - &u3a_realloc, - &u3a_free) ) { - u3l_log("%s\r\n", "urcrypt initialization failed"); - exit(1); - } - /* Activate the storage system. */ nuu_o = u3e_live(c3n, dir_c); diff --git a/pkg/urbit/vere/king.c b/pkg/urbit/vere/king.c index c0df14592..b1fa69de8 100644 --- a/pkg/urbit/vere/king.c +++ b/pkg/urbit/vere/king.c @@ -710,6 +710,11 @@ u3_king_commence() u3C.wag_w |= u3o_hashless; u3m_boot_lite(); + { +#include + SSL_library_init(); + SSL_load_error_strings(); + } // wire up signal controls // @@ -761,6 +766,7 @@ u3_king_commence() _king_loop_init(); uv_run(u3L, UV_RUN_DEFAULT); _king_loop_exit(); + u3m_stop(); } /* u3_king_stub(): get the One Pier for unreconstructed code. diff --git a/pkg/urbit/worker/main.c b/pkg/urbit/worker/main.c index 7bc494e26..9d621a3aa 100644 --- a/pkg/urbit/worker/main.c +++ b/pkg/urbit/worker/main.c @@ -213,6 +213,7 @@ _cw_serf_commence(c3_i argc, c3_c* argv[]) // enter loop // uv_run(lup_u, UV_RUN_DEFAULT); + u3m_stop(); } /* _cw_info(); print pier info @@ -226,6 +227,7 @@ _cw_info(c3_i argc, c3_c* argv[]) c3_d eve_d = u3m_boot(dir_c); fprintf(stderr, "urbit-worker: %s at event %" PRIu64 "\r\n", dir_c, eve_d); + u3m_stop(); } /* _cw_grab(); gc pier. @@ -238,6 +240,7 @@ _cw_grab(c3_i argc, c3_c* argv[]) c3_c* dir_c = argv[2]; u3m_boot(dir_c); u3_serf_grab(); + u3m_stop(); } /* _cw_cram(); jam persistent state (rock), and exit. @@ -258,6 +261,7 @@ _cw_cram(c3_i argc, c3_c* argv[]) } fprintf(stderr, "urbit-worker: cram: rock saved at event %" PRIu64 "\r\n", eve_d); + u3m_stop(); } /* _cw_queu(); cue rock, save, and exit. @@ -285,6 +289,7 @@ _cw_queu(c3_i argc, c3_c* argv[]) u3e_save(); fprintf(stderr, "urbit-worker: queu: rock loaded at event %" PRIu64 "\r\n", eve_d); + u3m_stop(); } } @@ -301,6 +306,7 @@ _cw_pack(c3_i argc, c3_c* argv[]) u3a_print_memory(stderr, "urbit-worker: pack: gained", u3m_pack()); u3e_save(); + u3m_stop(); } /* _cw_usage(): print urbit-worker usage. diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index 4aa52eafd..a042ebe96 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -807,7 +807,7 @@ _urcrypt_argon2_free(uint8_t* memory, size_t bytes) #define SZ_32(s) ( sizeof(size_t) <= sizeof(uint32_t) || s <= 0xFFFFFFFF ) const char* -urcrypt_argon2(urcrypt_argon2_type type, +urcrypt_argon2(uint8_t type, uint32_t version, uint32_t threads, uint32_t memory_cost, @@ -920,6 +920,37 @@ urcrypt_blake2(size_t message_length, } } +static secp256k1_context* _urcrypt_secp_ctx = NULL; + +int +urcrypt_secp_init(uint8_t entropy[32]) +{ + if ( NULL != _urcrypt_secp_ctx ) { + return -1; + } + else { + _urcrypt_secp_ctx = secp256k1_context_create( + SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); + if ( 1 == secp256k1_context_randomize(_urcrypt_secp_ctx, entropy) ) { + return 0; + } + else { + secp256k1_context_destroy(_urcrypt_secp_ctx); + _urcrypt_secp_ctx = NULL; + return -2; + } + } +} + +void +urcrypt_secp_cleanup(void) +{ + if ( NULL != _urcrypt_secp_ctx) { + secp256k1_context_destroy(_urcrypt_secp_ctx); + _urcrypt_secp_ctx = NULL; + } +} + int urcrypt_secp_make(uint8_t hash[32], uint8_t key[32], uint8_t out[32]) { @@ -940,3 +971,88 @@ urcrypt_secp_make(uint8_t hash[32], uint8_t key[32], uint8_t out[32]) return 0; } } + +int +urcrypt_secp_reco(uint8_t hash[32], + uint8_t key_v, + const uint8_t key_r[32], + const uint8_t key_s[32], + uint8_t out_x[32], + uint8_t out_y[32]) +{ + if ( (NULL == _urcrypt_secp_ctx) || + (NULL == hash) || + (NULL == key_r) || + (NULL == key_s) ) { + return -1; + } + else if ( key_v > 3 ) { + return -2; + } + else { + secp256k1_ecdsa_recoverable_signature signature; + uint8_t private[64]; + size_t i, j; + // make big private key out of two smaller parts, reversing endianness + for ( j = 31, i = 0; i < 32; ++i, --j) { + private[i] = key_r[j]; + } + for ( j = 31; i < 64; ++i, --j ) { + private[i] = key_s[j]; + } + memset(&signature, 0, sizeof(secp256k1_ecdsa_recoverable_signature)); + if ( 1 != secp256k1_ecdsa_recoverable_signature_parse_compact( + _urcrypt_secp_ctx, /* IN: context */ + &signature, /* OUT: sig */ + private, /* IN: r/s */ + key_v) ) { /* IN: v */ + return -3; + } + else { + secp256k1_pubkey public; + memset(&public, 0, sizeof(secp256k1_pubkey)); + // the code we ported from looks like it intended to reverse hash + // at some point, but doesn't actually. + if ( 1 != secp256k1_ecdsa_recover( + _urcrypt_secp_ctx, /* IN: context */ + &public, /* OUT: pub key */ + &signature, /* IN: signature */ + hash) ) { /* IN: message hash */ + return -4; + } + else { + /* convert pub into serialized form that we can get x, y out of */ + uint8_t serialized[65]; + size_t outputlen = 65; + memset(serialized, 0, outputlen); + if ( 1 != secp256k1_ec_pubkey_serialize( + _urcrypt_secp_ctx, /* IN: context */ + serialized, /* OUT: output */ + &outputlen, /* IN/OUT: outputlen */ + &public, /* IN: pubkey*/ + SECP256K1_EC_UNCOMPRESSED) ) { /* IN: flags */ + return -5; + } + else { + /* in file + subprojects/secp256k1/src/eckey_impl.h + func + secp256k1_eckey_pubkey_parse() + we can see + byte 0: signal bits (???) + bytes 1-32: x + bytes 33-64: y + + convert endianness while we're at it */ + for (j = 32, i = 0; i < 32; ++i, --j) { + out_x[i] = serialized[j]; + } + for (j = 64, i = 0; i < 32; ++i, --j) { + out_y[i] = serialized[j]; + } + return 0; + } + } + } + } +} diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 324fab73e..65c00a711 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -158,15 +158,13 @@ void urcrypt_shas(uint8_t *salt, size_t salt_length, const uint8_t *message, size_t message_length, uint8_t out[32]); -typedef enum urcrypt_argon2_type { - urcrypt_argon2_d = 0, - urcrypt_argon2_i = 1, - urcrypt_argon2_id = 2, - urcrypt_argon2_u = 10, -} urcrypt_argon2_type; +#define urcrypt_argon2_d 0 +#define urcrypt_argon2_i 1 +#define urcrypt_argon2_id 2 +#define urcrypt_argon2_u 10 /* returns a constant error message string or NULL for success */ -const char* urcrypt_argon2(urcrypt_argon2_type type, +const char* urcrypt_argon2(uint8_t type, // one of the urcrpyt_argon2_* uint32_t version, uint32_t threads, uint32_t memory_cost, @@ -191,6 +189,31 @@ int urcrypt_blake2(size_t message_length, size_t out_length, uint8_t *out); +/* there is some long-term context associated with the secp library + * (precomputed tables, etc). Because the context itself is thread-safe, + * we have opted to manage it statically and present a simple calling + * interface that doesn't mention it. + */ + +/* initialize static secp context (not thread-safe). Recommmendation: + * call this once at main thread startup before calling other secp functions. + * Use a high quality source of entropy. + */ +int urcrypt_secp_init(uint8_t entropy[32]); + +/* restore initial secp context conditons (not thread-safe). Recommendation: + * call this just before that main thread exits to make valgrind etc. happy + */ +void urcrypt_secp_cleanup(void); + +// technically usable without the secp context int urcrypt_secp_make(uint8_t hash[32], uint8_t key[32], uint8_t out[32]); +int urcrypt_secp_reco(uint8_t hash[32], + uint8_t key_v, // 0, 1, 2, 3 + const uint8_t key_r[32], + const uint8_t key_s[32], + uint8_t out_x[32], + uint8_t out_y[32]); + #endif From 58e4915a8d40c5e4254ae663ae8391616f787f69 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Tue, 29 Sep 2020 11:39:43 -0700 Subject: [PATCH 46/75] stops trying to hide pointer shapes, cleans up secp api, updates secp dependency --- nix/deps/secp256k1/default.nix | 4 +- pkg/urbit/daemon/main.c | 8 +- pkg/urbit/jets/e/argon2.c | 17 +++- pkg/urbit/jets/e/secp.c | 80 ++++++------------ pkg/urbit/noun/manage.c | 37 +++------ pkg/urbit/vere/king.c | 7 -- pkg/urcrypt/Makefile | 4 +- pkg/urcrypt/README.md | 51 ++++++++++++ pkg/urcrypt/urcrypt.c | 143 +++++++++++---------------------- pkg/urcrypt/urcrypt.h | 54 +++++++++---- 10 files changed, 191 insertions(+), 214 deletions(-) create mode 100644 pkg/urcrypt/README.md diff --git a/nix/deps/secp256k1/default.nix b/nix/deps/secp256k1/default.nix index 438b2dc25..828baf7d4 100644 --- a/nix/deps/secp256k1/default.nix +++ b/nix/deps/secp256k1/default.nix @@ -19,7 +19,7 @@ pkgs.stdenv.mkDerivation rec { src = pkgs.fetchFromGitHub { owner = "bitcoin-core"; repo = "secp256k1"; - rev = "e34ceb333b1c0e6f4115ecbb80c632ac1042fa49"; - sha256 = "0as78s179hcr3ysk3fw98k5wzabgnwri7vkkc17wg31lyz6ids6c"; + rev = "63150ab4da1ef13ebfb4396064e1ff501dbd015e"; + sha256 = "0s128jgb5r9v92hk95na44kgpwi0dr3bjkqx8k5yxqpwxlhrmzmx"; }; } diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index e811d4c8b..56ac40189 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -619,6 +619,7 @@ main(c3_i argc, if ( c3y == u3_Host.ops_u.tex ) { u3_Host.bot_f = _stop_on_boot_completed_cb; } + #if 0 if ( 0 == getuid() ) { chroot(u3_Host.dir_c); @@ -747,17 +748,16 @@ main(c3_i argc, } } - /* FIXME // Initialize OpenSSL for client and server // { SSL_library_init(); SSL_load_error_strings(); } - */ - // openssl won't init after this libcurl call, probably because it links - // against openssl and does allocations with it. + // must come before curl initialization because curl will use + // openssl malloc and prevent us from installing our custom allocator + u3m_boot_lite(); // initialize curl // diff --git a/pkg/urbit/jets/e/argon2.c b/pkg/urbit/jets/e/argon2.c index 69901baad..c01c46b06 100644 --- a/pkg/urbit/jets/e/argon2.c +++ b/pkg/urbit/jets/e/argon2.c @@ -7,6 +7,19 @@ /* helpers */ + static int + argon2_alloc(uint8_t** output, size_t bytes) + { + *output = u3a_malloc(bytes); + return (NULL != *output); + } + + static void + argon2_free(uint8_t* memory, size_t bytes) + { + u3a_free(memory); + } + static c3_t _cqear_unpack_type(c3_y* out, u3_atom in) { @@ -72,8 +85,8 @@ wid_w, dat_y, wis_w, sat_y, out_w, out_y, - &u3a_malloc, - &u3a_free); + &argon2_alloc, + &argon2_free); u3a_free(key_y); u3a_free(ex_y); diff --git a/pkg/urbit/jets/e/secp.c b/pkg/urbit/jets/e/secp.c index 5052a956a..4f0c70b98 100644 --- a/pkg/urbit/jets/e/secp.c +++ b/pkg/urbit/jets/e/secp.c @@ -5,67 +5,33 @@ #include "../include/secp256k1.h" #include "../include/secp256k1_recovery.h" #include "urcrypt.h" +#include + +static void* pre_u; +static urcrypt_secp_context sec_u; + +void u3e_secp_init() +{ + c3_y ent_y[32]; + ent_getentropy(ent_y, 32); + pre_u = malloc(urcrypt_secp_prealloc_size()); + + if ( 0 != urcrypt_secp_init(&sec_u, pre_u, ent_y) ) { + u3l_log("%s\r\n", "u3e_secp_init failed"); + abort(); + } +} + +void u3e_secp_stop() +{ + urcrypt_secp_destroy(&sec_u); + free(pre_u); + pre_u = NULL; +} /* util funcs */ -static c3_t -_cqe_is_8(const c3_w words[8], u3_noun non) -{ - if ( (c3n == u3ud(non)) || - (8 != u3r_met(5, non)) ) { - return 0; - } - else { - c3_w i; - for ( i = 0; i < 8; ++i ) { - if ( words[i] != u3r_word(i, non) ) { - return 0; - } - } - return 1; - } -} - -/* FIXME!!! - * The hoon contains a generic secp core which is actually jetted, and then - * +secp256k1 passes these constants to it. This should be restructured so - * that the constants are matched as part of the batteries in the core stack - * of the jetted gates. - * - * The hoon also doesn't, in general, check the size of any of its arguments; - * we return u3_none when they are mis-sized. -*/ -static c3_t -_cqe_256k1_veri(u3_noun cor) -{ - static const c3_w pow_w[8] = { - 0xfffffc2f, 0xfffffffe, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; - static const c3_w xow_w[8] = { - 0x16f81798, 0x59f2815b, 0x2dce28d9, 0x29bfcdb, - 0xce870b07, 0x55a06295, 0xf9dcbbac, 0x79be667e }; - static const c3_w yow_w[8] = { - 0xfb10d4b8, 0x9c47d08f, 0xa6855419, 0xfd17b448, - 0xe1108a8, 0x5da4fbfc, 0x26a3c465, 0x483ada77 }; - static const c3_w now_w[8] = { - 0xd0364141, 0xbfd25e8c, 0xaf48a03b, 0xbaaedce6, - 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff }; - - u3_noun w, p, b, mor, a, g, x, y, n, sam = u3x_at(254, cor); - u3x_qual(sam, &w, &p, &b, &mor); - u3x_trel(mor, &a, &g, &n); - u3x_cell(g, &x, &y); - - return ( 32 == w && - 0 == b && - 7 == a && - _cqe_is_8(pow_w, p) && - _cqe_is_8(xow_w, x) && - _cqe_is_8(yow_w, y) && - _cqe_is_8(now_w, n) ); -} - /* no guarantees if 'in' and 'out' overlap / are the same */ static void byte_reverse(c3_y *i_y, /* in */ c3_y *o_y, /* out */ diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index 83445ab0a..56e292d70 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -1,7 +1,6 @@ /* n/m.c ** */ -#include #include #include #include @@ -1602,39 +1601,21 @@ _cm_signals(void) } } +extern void u3e_secp_init(void); +extern void u3e_secp_stop(void); + static void -_cm_crypto_init() +_cm_crypto() { - /* Initialize OpenSSL with loom allocation functions. - * IMPORTANT: this should come before the urcrypt_set_openssl [...] - * call, because in usual circumstances that will override the functions - * we set here, but weird linking situations we don't currently encounter - * could mean we need to set both, and it doesn't hurt. - */ + /* Initialize OpenSSL with loom allocation functions. */ if ( 0 == CRYPTO_set_mem_functions(&u3a_malloc_ssl, &u3a_realloc_ssl, &u3a_free_ssl) ) { u3l_log("%s\r\n", "openssl initialization failed"); - exit(1); + abort(); } - /* Initialize cryptography suite */ - if ( 0 != urcrypt_set_openssl_mem_functions(&u3a_malloc, - &u3a_realloc, - &u3a_free) ) { - u3l_log("%s\r\n", "urcrypt-openssl initialization failed"); - exit(1); - } - - { - c3_y ent_y[32]; - ent_getentropy(ent_y, 32); - - if ( 0 != urcrypt_secp_init(ent_y) ) { - u3l_log("%s\r\n", "urcrypt-secp initialization failed"); - exit(1); - } - } + u3e_secp_init(); } /* u3m_init(): start the environment. @@ -1644,7 +1625,7 @@ u3m_init(void) { _cm_limits(); _cm_signals(); - _cm_crypto_init(); + _cm_crypto(); /* Make sure GMP uses our malloc. */ @@ -1687,7 +1668,7 @@ u3m_init(void) void u3m_stop() { - urcrypt_secp_cleanup(); + u3e_secp_stop(); } /* u3m_boot(): start the u3 system. return next event, starting from 1. diff --git a/pkg/urbit/vere/king.c b/pkg/urbit/vere/king.c index b1fa69de8..12f98f063 100644 --- a/pkg/urbit/vere/king.c +++ b/pkg/urbit/vere/king.c @@ -709,13 +709,6 @@ u3_king_commence() sag_w = u3C.wag_w; u3C.wag_w |= u3o_hashless; - u3m_boot_lite(); - { -#include - SSL_library_init(); - SSL_load_error_strings(); - } - // wire up signal controls // u3C.sign_hold_f = _king_sign_hold; diff --git a/pkg/urcrypt/Makefile b/pkg/urcrypt/Makefile index 7c131b0f0..2c3d294ad 100644 --- a/pkg/urcrypt/Makefile +++ b/pkg/urcrypt/Makefile @@ -6,11 +6,11 @@ PREFIX ?= ./out .PHONY: all install clean -CFLAGS := $(CFLAGS) -O3 -Wall -Werror -pedantic -std=gnu99 +CFLAGS := $(CFLAGS) -g -O3 -Wall -Werror -pedantic -std=gnu99 SOURCES = urcrypt.c urcrypt.h liburcrypt.a: $(SOURCES) - $(CC) $(CFLAGS) -c urcrypt.c -o urcrypt-static.o + $(CC) $(CFLAGS) -D URCRYPT_STATIC -c urcrypt.c -o urcrypt-static.o $(AR) rcs liburcrypt.a urcrypt-static.o liburcrypt.so: $(SOURCES) diff --git a/pkg/urcrypt/README.md b/pkg/urcrypt/README.md new file mode 100644 index 000000000..7b7f5de7d --- /dev/null +++ b/pkg/urcrypt/README.md @@ -0,0 +1,51 @@ +What is urcrypt? +---------------- +urcrypt is a library of cryptography routines used by urbit jets. + +Why is urcrypt? +--------------- +Urbit's C runtime (long the only urbit runtime) has accumulated a collection of +cryptography dependencies, some with custom additions or patches. These +libraries have different conventions and have been managed by u3 in an ad-hoc +manner. Reproducing that arrangement in other runtimes is tricky and +error-prone. The (sometimes inconsistent) logic must be reproduced and suitable +cryptography primitives must be found (or worse, written) for the new +environment. + +To ease these burdens, urcrypt isolates the quirks behind a consistent calling +convention. Everything is a little-endian byte array, and each jetted operation +has a corresponding function in the library. Jets simply unpack their nouns, +call urcrypt, and pack the results. + +What is a cryptography routine? +------------------------------- +This is more of a subjective question than it might appear. Any of the following +conditions are sufficient, but not necessary, for a function to be included in +urcrypt: + + * The routine is sensitive to side-channel attacks (encryption, etc) + * Some property of the routine is cryptographically useful (SHA, RIPE, etc) + * The routine typically lives in a crypto library, for whatever reason. + +Shared or static? +----------------- +Urcrypt has a number of dependencies, and there is something of a matrix of +combinations of static and shared versions of those dependencies. To keep things +simple, two modes are currently supported: + + * A static library archive (urcrypt.a) + * A shared object with internal, statically linked dependencies + (urcrypt.{so, dll, dylib, etc}) + +pkg-config can be used in the normal way to obtain the link parameters. + +A word on OpenSSL +----------------- +Urcrypt depends on OpenSSL's libcrypto, which has custom malloc functions. +Urcrypt tries to avoid dealing with global/static state, and mostly succeeds, +but users who need to control memory management will want to use +`CRYPTO_set_mem_functions` from OpenSSL. This is no problem for a statically +linked urcrypt, but the shared object doesn't expose that symbol to users. +`urcrypt_set_openssl_mem_functions` is a trivial wrapper that is exposed, +and can be used to configure shared urcrypt's internal libcrypto malloc. +The function always fails in statically linked urcrypt. diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index a042ebe96..beac1f219 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -16,47 +16,16 @@ #include #include -static urcrypt_malloc_t _urcrypt_ssl_malloc_ptr; -static urcrypt_realloc_t _urcrypt_ssl_realloc_ptr; -static urcrypt_free_t _urcrypt_ssl_free_ptr; - -static void* -_urcrypt_malloc_ssl(size_t len -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - , const char* file, int line -#endif -) { return (*_urcrypt_ssl_malloc_ptr)(len); } - -static void* -_urcrypt_realloc_ssl(void* ptr, size_t len -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - , const char* file, int line -#endif -) { return (*_urcrypt_ssl_realloc_ptr)(ptr, len); } - -static void -_urcrypt_free_ssl(void* ptr -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - , const char* file, int line -#endif -) { (*_urcrypt_ssl_free_ptr)(ptr); } - int -urcrypt_set_openssl_mem_functions(urcrypt_malloc_t malloc_ptr, - urcrypt_realloc_t realloc_ptr, - urcrypt_free_t free_ptr) +urcrypt_set_openssl_mem_functions(urcrypt_openssl_malloc_t m, + urcrypt_openssl_realloc_t r, + urcrypt_openssl_free_t f) { - if ( CRYPTO_set_mem_functions(&_urcrypt_malloc_ssl, - &_urcrypt_realloc_ssl, - &_urcrypt_free_ssl) ) { - _urcrypt_ssl_malloc_ptr = malloc_ptr; - _urcrypt_ssl_realloc_ptr = realloc_ptr; - _urcrypt_ssl_free_ptr = free_ptr; - return 0; - } - else { - return -1; - } +#ifdef URCRYPT_STATIC + return -2; +#else + return ( CRYPTO_set_mem_functions(m, r, f) ) ? 0 : -1; +#endif } int @@ -783,25 +752,6 @@ urcrypt_shas(uint8_t *salt, size_t salt_length, } } -/* argon2 does memory allocation, but takes function pointers in the context. - * the signatures don't match, so we need these wrappers. - */ -static urcrypt_malloc_t _urcrypt_argon2_malloc_ptr; -static urcrypt_free_t _urcrypt_argon2_free_ptr; - -static int -_urcrypt_argon2_alloc(uint8_t** output, size_t bytes) -{ - *output = (*_urcrypt_argon2_malloc_ptr)(bytes); - return (NULL != *output); -} - -static void -_urcrypt_argon2_free(uint8_t* memory, size_t bytes) -{ - (*_urcrypt_argon2_free_ptr)(memory); -} - // library convention is to have sizes in size_t, but argon2 wants them // in uint32_t, so here's a helper macro for ensuring equivalence. #define SZ_32(s) ( sizeof(size_t) <= sizeof(uint32_t) || s <= 0xFFFFFFFF ) @@ -822,8 +772,8 @@ urcrypt_argon2(uint8_t type, uint8_t *salt, size_t out_length, uint8_t *out, - urcrypt_malloc_t malloc_ptr, - urcrypt_free_t free_ptr) + urcrypt_argon2_malloc_t malloc_ptr, + urcrypt_argon2_free_t free_ptr) { if ( !( SZ_32(secret_length) && SZ_32(associated_length) && @@ -874,8 +824,8 @@ urcrypt_argon2(uint8_t type, threads, threads, version, // algorithm version - &_urcrypt_argon2_alloc,// custom memory allocation function - &_urcrypt_argon2_free, // custom memory deallocation function + malloc_ptr, // custom memory allocation function + free_ptr, // custom memory deallocation function ARGON2_DEFAULT_FLAGS // by default only internal memory is cleared }; @@ -920,35 +870,39 @@ urcrypt_blake2(size_t message_length, } } -static secp256k1_context* _urcrypt_secp_ctx = NULL; +#define SECP_FLAGS SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN; + +struct urcrypt_secp_context_struct { + secp256k1_context* secp; +}; + +size_t +urcrypt_secp_prealloc_size() +{ + return secp256k1_context_preallocated_size(SECP_FLAGS); +} int -urcrypt_secp_init(uint8_t entropy[32]) +urcrypt_secp_init(urcrypt_secp_context *context, + void *prealloc, + uint8_t entropy[32]) { - if ( NULL != _urcrypt_secp_ctx ) { - return -1; + secp256k1_context* secp = + secp256k1_context_preallocated_create(prealloc, SECP_FLAGS); + if ( 1 == secp256k1_context_randomize(secp, entropy) ) { + context->secp = secp; + return 0; } else { - _urcrypt_secp_ctx = secp256k1_context_create( - SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); - if ( 1 == secp256k1_context_randomize(_urcrypt_secp_ctx, entropy) ) { - return 0; - } - else { - secp256k1_context_destroy(_urcrypt_secp_ctx); - _urcrypt_secp_ctx = NULL; - return -2; - } + secp256k1_context_preallocated_destroy(secp); + return -1; } } void -urcrypt_secp_cleanup(void) +urcrypt_secp_destroy(urcrypt_secp_context *context) { - if ( NULL != _urcrypt_secp_ctx) { - secp256k1_context_destroy(_urcrypt_secp_ctx); - _urcrypt_secp_ctx = NULL; - } + secp256k1_context_preallocated_destroy(context->secp); } int @@ -973,15 +927,15 @@ urcrypt_secp_make(uint8_t hash[32], uint8_t key[32], uint8_t out[32]) } int -urcrypt_secp_reco(uint8_t hash[32], +urcrypt_secp_reco(urcrypt_secp_context* context, + uint8_t hash[32], uint8_t key_v, const uint8_t key_r[32], const uint8_t key_s[32], uint8_t out_x[32], uint8_t out_y[32]) { - if ( (NULL == _urcrypt_secp_ctx) || - (NULL == hash) || + if ( (NULL == hash) || (NULL == key_r) || (NULL == key_s) ) { return -1; @@ -1002,22 +956,21 @@ urcrypt_secp_reco(uint8_t hash[32], } memset(&signature, 0, sizeof(secp256k1_ecdsa_recoverable_signature)); if ( 1 != secp256k1_ecdsa_recoverable_signature_parse_compact( - _urcrypt_secp_ctx, /* IN: context */ - &signature, /* OUT: sig */ - private, /* IN: r/s */ - key_v) ) { /* IN: v */ + context->secp, /* IN: context */ + &signature, /* OUT: sig */ + private, /* IN: r/s */ + key_v) ) { /* IN: v */ return -3; } else { secp256k1_pubkey public; memset(&public, 0, sizeof(secp256k1_pubkey)); - // the code we ported from looks like it intended to reverse hash - // at some point, but doesn't actually. + _urcrypt_reverse(32, hash); if ( 1 != secp256k1_ecdsa_recover( - _urcrypt_secp_ctx, /* IN: context */ - &public, /* OUT: pub key */ - &signature, /* IN: signature */ - hash) ) { /* IN: message hash */ + context->secp, /* IN: context */ + &public, /* OUT: pub key */ + &signature, /* IN: signature */ + hash) ) { /* IN: message hash */ return -4; } else { @@ -1026,7 +979,7 @@ urcrypt_secp_reco(uint8_t hash[32], size_t outputlen = 65; memset(serialized, 0, outputlen); if ( 1 != secp256k1_ec_pubkey_serialize( - _urcrypt_secp_ctx, /* IN: context */ + context->secp, /* IN: context */ serialized, /* OUT: output */ &outputlen, /* IN/OUT: outputlen */ &public, /* IN: pubkey*/ diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 65c00a711..7c88a0cc3 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -5,10 +5,6 @@ #include #include -typedef void *(*urcrypt_malloc_t)(size_t); -typedef void *(*urcrypt_realloc_t)(void*, size_t); -typedef void (*urcrypt_free_t)(void*); - /* We depend on OpenSSL for various reasons, which doesn't promise not to * allocate memory and has the annoying CRYPTO_set_mem_functions api. We * are therefore forced to support it in some fashion. @@ -18,9 +14,31 @@ typedef void (*urcrypt_free_t)(void*); * * urcrypt will not use these functions directly. */ -int urcrypt_set_openssl_mem_functions(urcrypt_malloc_t malloc_ptr, - urcrypt_realloc_t realloc_ptr, - urcrypt_free_t free_ptr); + +typedef void *(*urcrypt_openssl_malloc_t)(size_t +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + , const char*, int +#endif + ); + +typedef void *(*urcrypt_openssl_realloc_t)(void*, size_t +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + , const char*, int +#endif + ); + +typedef void (*urcrypt_openssl_free_t)(void*, +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + , const char*, int +#endif + ); + +typedef int (*urcrypt_argon2_alloc_t)(uint8_t**, size_t); +typedef void (*urcrypt_argon2_free)(uint8_t*, size_t); + +int urcrypt_set_openssl_mem_functions(urcrypt_openssl_malloc_t, + urcrypt_openssl_realloc_t, + urcrypt_openssl_free_t); // const arguments are not written to, non-const arguments may be // all arrays are in little-endian byte order. @@ -179,8 +197,8 @@ const char* urcrypt_argon2(uint8_t type, // one of the urcrpyt_argon2_* uint8_t *salt, size_t out_length, uint8_t *out, - urcrypt_malloc_t malloc_ptr, - urcrypt_free_t free_ptr); + urcrypt_argon2_malloc_t malloc_ptr, + urcrypt_argon2_free_t free_ptr); int urcrypt_blake2(size_t message_length, uint8_t *message, @@ -190,16 +208,18 @@ int urcrypt_blake2(size_t message_length, uint8_t *out); /* there is some long-term context associated with the secp library - * (precomputed tables, etc). Because the context itself is thread-safe, - * we have opted to manage it statically and present a simple calling - * interface that doesn't mention it. + * (precomputed tables, etc), so secp functions require a context object */ +typedef struct urcrypt_secp_context_struct urcrypt_secp_context; -/* initialize static secp context (not thread-safe). Recommmendation: - * call this once at main thread startup before calling other secp functions. - * Use a high quality source of entropy. - */ -int urcrypt_secp_init(uint8_t entropy[32]); +// malloc a pointer of this size and pass it to init +size_t urcrypt_secp_prealloc_size(void); +// call this once at per context with high quality entropy +int urcrypt_secp_init(urcrypt_secp_context *context, + void* prealloc, + uint8_t entropy[32]); +// call just before freeing prealloc'd pointer +void urcrypt_secp_destroy(urcrypt_secp_context *context); /* restore initial secp context conditons (not thread-safe). Recommendation: * call this just before that main thread exits to make valgrind etc. happy From ecd4b2531ae81ab4f592e4bc985679bf60216e6c Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Wed, 30 Sep 2020 10:14:22 -0700 Subject: [PATCH 47/75] compiles and starts --- pkg/urbit/daemon/main.c | 8 ++++---- pkg/urbit/jets/e/secp.c | 21 +++++++++------------ pkg/urcrypt/urcrypt.c | 16 ++++++++-------- pkg/urcrypt/urcrypt.h | 16 +++++++++------- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/pkg/urbit/daemon/main.c b/pkg/urbit/daemon/main.c index 56ac40189..17a959b7c 100644 --- a/pkg/urbit/daemon/main.c +++ b/pkg/urbit/daemon/main.c @@ -748,6 +748,10 @@ main(c3_i argc, } } + // starting u3m configures OpenSSL memory functions, so we must do it + // before any OpenSSL allocations + u3m_boot_lite(); + // Initialize OpenSSL for client and server // { @@ -755,10 +759,6 @@ main(c3_i argc, SSL_load_error_strings(); } - // must come before curl initialization because curl will use - // openssl malloc and prevent us from installing our custom allocator - u3m_boot_lite(); - // initialize curl // if ( 0 != curl_global_init(CURL_GLOBAL_DEFAULT) ) { diff --git a/pkg/urbit/jets/e/secp.c b/pkg/urbit/jets/e/secp.c index 4f0c70b98..11d910ecf 100644 --- a/pkg/urbit/jets/e/secp.c +++ b/pkg/urbit/jets/e/secp.c @@ -7,16 +7,15 @@ #include "urcrypt.h" #include -static void* pre_u; -static urcrypt_secp_context sec_u; +static urcrypt_secp_context* sec_u; void u3e_secp_init() { c3_y ent_y[32]; ent_getentropy(ent_y, 32); - pre_u = malloc(urcrypt_secp_prealloc_size()); + sec_u = malloc(urcrypt_secp_prealloc_size()); - if ( 0 != urcrypt_secp_init(&sec_u, pre_u, ent_y) ) { + if ( 0 != urcrypt_secp_init(sec_u, ent_y) ) { u3l_log("%s\r\n", "u3e_secp_init failed"); abort(); } @@ -24,9 +23,9 @@ void u3e_secp_init() void u3e_secp_stop() { - urcrypt_secp_destroy(&sec_u); - free(pre_u); - pre_u = NULL; + urcrypt_secp_destroy(sec_u); + free(sec_u); + sec_u = NULL; } /* util funcs @@ -192,7 +191,8 @@ _cqe_reco(u3_atom has, u3r_bytes_fit(32, has_y, has) && u3r_bytes_fit(32, sir_y, sir) && u3r_bytes_fit(32, sis_y, sis) && - (0 == urcrypt_secp_reco(has_y, (c3_y) siv, sir_y, sis_y, x_y, y_y))) ) + (0 == urcrypt_secp_reco(sec_u, has_y, + (c3_y) siv, sir_y, sis_y, x_y, y_y))) ) { return u3_none; } @@ -333,9 +333,6 @@ u3we_make(u3_noun cor) return u3m_bail(c3__exit); } else { - return u3l_punt("secp-make", - _cqe_256k1_veri(cor) - ? _cqe_make(has, prv) - : u3_none); + return u3l_punt("secp-make", _cqe_make(has, prv)); } } diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index beac1f219..a908b5f6c 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -15,6 +15,7 @@ #include #include +#include int urcrypt_set_openssl_mem_functions(urcrypt_openssl_malloc_t m, @@ -772,7 +773,7 @@ urcrypt_argon2(uint8_t type, uint8_t *salt, size_t out_length, uint8_t *out, - urcrypt_argon2_malloc_t malloc_ptr, + urcrypt_argon2_alloc_t alloc_ptr, urcrypt_argon2_free_t free_ptr) { if ( !( SZ_32(secret_length) && @@ -824,13 +825,11 @@ urcrypt_argon2(uint8_t type, threads, threads, version, // algorithm version - malloc_ptr, // custom memory allocation function + alloc_ptr, // custom memory allocation function free_ptr, // custom memory deallocation function ARGON2_DEFAULT_FLAGS // by default only internal memory is cleared }; - _urcrypt_argon2_malloc_ptr = malloc_ptr; - _urcrypt_argon2_free_ptr = free_ptr; result = (*f)(&context); if ( ARGON2_OK != result ) { @@ -870,25 +869,26 @@ urcrypt_blake2(size_t message_length, } } -#define SECP_FLAGS SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN; +#define SECP_FLAGS SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN struct urcrypt_secp_context_struct { secp256k1_context* secp; + uint8_t prealloc[]; }; size_t urcrypt_secp_prealloc_size() { - return secp256k1_context_preallocated_size(SECP_FLAGS); + return sizeof(urcrypt_secp_context) + + secp256k1_context_preallocated_size(SECP_FLAGS); } int urcrypt_secp_init(urcrypt_secp_context *context, - void *prealloc, uint8_t entropy[32]) { secp256k1_context* secp = - secp256k1_context_preallocated_create(prealloc, SECP_FLAGS); + secp256k1_context_preallocated_create(context->prealloc, SECP_FLAGS); if ( 1 == secp256k1_context_randomize(secp, entropy) ) { context->secp = secp; return 0; diff --git a/pkg/urcrypt/urcrypt.h b/pkg/urcrypt/urcrypt.h index 7c88a0cc3..19ba66bdc 100644 --- a/pkg/urcrypt/urcrypt.h +++ b/pkg/urcrypt/urcrypt.h @@ -27,14 +27,14 @@ typedef void *(*urcrypt_openssl_realloc_t)(void*, size_t #endif ); -typedef void (*urcrypt_openssl_free_t)(void*, +typedef void (*urcrypt_openssl_free_t)(void* #if OPENSSL_VERSION_NUMBER >= 0x10100000L , const char*, int #endif ); typedef int (*urcrypt_argon2_alloc_t)(uint8_t**, size_t); -typedef void (*urcrypt_argon2_free)(uint8_t*, size_t); +typedef void (*urcrypt_argon2_free_t)(uint8_t*, size_t); int urcrypt_set_openssl_mem_functions(urcrypt_openssl_malloc_t, urcrypt_openssl_realloc_t, @@ -86,6 +86,8 @@ int urcrypt_aes_ecbb_de(uint8_t key[24], uint8_t block[16], uint8_t out[16]); int urcrypt_aes_ecbc_en(uint8_t key[32], uint8_t block[16], uint8_t out[16]); int urcrypt_aes_ecbc_de(uint8_t key[32], uint8_t block[16], uint8_t out[16]); +typedef void* (*urcrypt_realloc_t)(void*, size_t); + // message and length are read/write so // realloc_ptr can be used as realloc to pad message int urcrypt_aes_cbca_en(uint8_t **message_ptr, @@ -197,7 +199,7 @@ const char* urcrypt_argon2(uint8_t type, // one of the urcrpyt_argon2_* uint8_t *salt, size_t out_length, uint8_t *out, - urcrypt_argon2_malloc_t malloc_ptr, + urcrypt_argon2_alloc_t alloc_ptr, urcrypt_argon2_free_t free_ptr); int urcrypt_blake2(size_t message_length, @@ -212,13 +214,12 @@ int urcrypt_blake2(size_t message_length, */ typedef struct urcrypt_secp_context_struct urcrypt_secp_context; -// malloc a pointer of this size and pass it to init +// size of opaque secp handle, malloc and pass to init size_t urcrypt_secp_prealloc_size(void); // call this once at per context with high quality entropy int urcrypt_secp_init(urcrypt_secp_context *context, - void* prealloc, uint8_t entropy[32]); -// call just before freeing prealloc'd pointer +// call before freeing opaque secp handle void urcrypt_secp_destroy(urcrypt_secp_context *context); /* restore initial secp context conditons (not thread-safe). Recommendation: @@ -229,7 +230,8 @@ void urcrypt_secp_cleanup(void); // technically usable without the secp context int urcrypt_secp_make(uint8_t hash[32], uint8_t key[32], uint8_t out[32]); -int urcrypt_secp_reco(uint8_t hash[32], +int urcrypt_secp_reco(urcrypt_secp_context* context, + uint8_t hash[32], uint8_t key_v, // 0, 1, 2, 3 const uint8_t key_r[32], const uint8_t key_s[32], From 42e1180b0d93edf8e661e0ed31e29525318747b5 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Wed, 30 Sep 2020 10:56:20 -0700 Subject: [PATCH 48/75] updating solid pill after merge --- bin/solid.pill | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/solid.pill b/bin/solid.pill index 5add4da3d..30adcdd6c 100644 --- a/bin/solid.pill +++ b/bin/solid.pill @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6def8b55a977e3ced47d6042dba819450655421623efd4ed5db0852b0bce8723 -size 6229538 +oid sha256:d9343cf6ddb6ecb4277d8ea6026119f74c5cec7e5940eb8949ed3259d2fd31d2 +size 6238109 From 7bca3a86cf294886733847f5411aea0693f8f926 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 2 Oct 2020 09:08:56 -0700 Subject: [PATCH 49/75] obliterate ge-additions package, move to urcrypt --- nix/deps-env.nix | 2 +- nix/pkgs/default.nix | 11 +++------- nix/pkgs/ge-additions/builder.sh | 7 ------- nix/pkgs/ge-additions/cross.nix | 12 ----------- nix/pkgs/ge-additions/default.nix | 9 -------- nix/pkgs/ge-additions/release.sh | 13 ------------ nix/pkgs/urbit/default.nix | 4 ++-- nix/pkgs/urbit/release.nix | 3 +-- nix/pkgs/urbit/shell.nix | 2 +- nix/pkgs/urcrypt/default.nix | 4 ++-- nix/release.nix | 5 ----- pkg/ge-additions/Makefile | 20 ------------------ pkg/urbit/configure | 2 +- pkg/urcrypt/Makefile | 21 +++++++++++++------ pkg/{ => urcrypt}/ge-additions/LICENSE | 0 pkg/{ => urcrypt}/ge-additions/README.md | 0 pkg/{ => urcrypt}/ge-additions/ge-additions.c | 0 pkg/{ => urcrypt}/ge-additions/ge-additions.h | 0 pkg/urcrypt/urcrypt.c | 2 +- 19 files changed, 27 insertions(+), 90 deletions(-) delete mode 100644 nix/pkgs/ge-additions/builder.sh delete mode 100644 nix/pkgs/ge-additions/cross.nix delete mode 100644 nix/pkgs/ge-additions/default.nix delete mode 100644 nix/pkgs/ge-additions/release.sh delete mode 100644 pkg/ge-additions/Makefile rename pkg/{ => urcrypt}/ge-additions/LICENSE (100%) rename pkg/{ => urcrypt}/ge-additions/README.md (100%) rename pkg/{ => urcrypt}/ge-additions/ge-additions.c (100%) rename pkg/{ => urcrypt}/ge-additions/ge-additions.h (100%) diff --git a/nix/deps-env.nix b/nix/deps-env.nix index 7935da6ba..12cb9b8f4 100644 --- a/nix/deps-env.nix +++ b/nix/deps-env.nix @@ -20,7 +20,7 @@ let vendor = with deps; - [ argon2 ed25519 h2o murmur3 scrypt secp256k1 softfloat3 uv ent ge-additions urcrypt ivory-header ca-header ]; + [ argon2 ed25519 h2o murmur3 scrypt secp256k1 softfloat3 uv ent urcrypt ivory-header ca-header ]; in diff --git a/nix/pkgs/default.nix b/nix/pkgs/default.nix index 7a51f3187..35b8053d8 100644 --- a/nix/pkgs/default.nix +++ b/nix/pkgs/default.nix @@ -9,24 +9,19 @@ let arvo-ropsten = import ./arvo-ropsten { inherit pkgs; }; herb = import ../../pkg/herb { inherit pkgs; }; - ge-additions = import ./ge-additions { - inherit pkgs; - inherit (deps) ed25519; - }; - libaes_siv = import ./libaes_siv { inherit pkgs; }; urcrypt = import ./urcrypt { - inherit ge-additions libaes_siv; + inherit libaes_siv; inherit (pkgs) stdenv openssl gmp; inherit (deps) ed25519 argon2 secp256k1; }; mkUrbit = { debug }: import ./urbit { - inherit pkgs ent debug ge-additions urcrypt libaes_siv; + inherit pkgs ent debug urcrypt libaes_siv; inherit (deps) argon2 murmur3 uv ed25519 scrypt softfloat3; inherit (deps) secp256k1 h2o ivory-header ca-header; }; @@ -36,4 +31,4 @@ let in -{ inherit ent ge-additions urcrypt libaes_siv arvo arvo-ropsten herb urbit urbit-debug; } +{ inherit ent urcrypt libaes_siv arvo arvo-ropsten herb urbit urbit-debug; } diff --git a/nix/pkgs/ge-additions/builder.sh b/nix/pkgs/ge-additions/builder.sh deleted file mode 100644 index 5a0404377..000000000 --- a/nix/pkgs/ge-additions/builder.sh +++ /dev/null @@ -1,7 +0,0 @@ -source $stdenv/setup - -cp -r $src ./src -chmod -R u+w ./src -cd ./src - -PREFIX=$out make install diff --git a/nix/pkgs/ge-additions/cross.nix b/nix/pkgs/ge-additions/cross.nix deleted file mode 100644 index f16afad0b..000000000 --- a/nix/pkgs/ge-additions/cross.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ env_name, env, deps }: - -env.make_derivation rec { - name = "ge-additions"; - builder = ./release.sh; - src = ../../../pkg/ge-additions; - - cross_inputs = [ deps.ed25519 ]; - - CC = "${env.host}-gcc"; - AR = "${env.host}-ar"; -} diff --git a/nix/pkgs/ge-additions/default.nix b/nix/pkgs/ge-additions/default.nix deleted file mode 100644 index e77098cff..000000000 --- a/nix/pkgs/ge-additions/default.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ pkgs, ed25519 }: - -pkgs.stdenv.mkDerivation rec { - name = "ge-additions"; - builder = ./builder.sh; - src = ../../../pkg/ge-additions; - - nativeBuildInputs = [ ed25519 ]; -} diff --git a/nix/pkgs/ge-additions/release.sh b/nix/pkgs/ge-additions/release.sh deleted file mode 100644 index aaa54b5e1..000000000 --- a/nix/pkgs/ge-additions/release.sh +++ /dev/null @@ -1,13 +0,0 @@ -source $setup - -cp -r $src ./src -chmod -R u+w ./src -cd ./src - -for dep in $cross_inputs; do - export CFLAGS="${CFLAGS-} -I$dep/include" - export LDFLAGS="${LDFLAGS-} -L$dep/lib" -done - -PREFIX=$out make install - diff --git a/nix/pkgs/urbit/default.nix b/nix/pkgs/urbit/default.nix index b808d0afb..2aee0514c 100644 --- a/nix/pkgs/urbit/default.nix +++ b/nix/pkgs/urbit/default.nix @@ -1,7 +1,7 @@ { pkgs, debug, - argon2, ed25519, ent, ge-additions, urcrypt, libaes_siv, h2o, murmur3, scrypt, secp256k1, softfloat3, uv, ivory-header, ca-header + argon2, ed25519, ent, urcrypt, libaes_siv, h2o, murmur3, scrypt, secp256k1, softfloat3, uv, ivory-header, ca-header }: let @@ -26,7 +26,7 @@ let [ curl gmp sigseg openssl zlib lmdb ]; vendor = - [ argon2 softfloat3 ed25519 ent ge-additions urcrypt libaes_siv h2o scrypt uv murmur3 secp256k1 ivory-header ca-header ]; + [ argon2 softfloat3 ed25519 ent urcrypt libaes_siv h2o scrypt uv murmur3 secp256k1 ivory-header ca-header ]; urbit = pkgs.stdenv.mkDerivation { inherit name meta; diff --git a/nix/pkgs/urbit/release.nix b/nix/pkgs/urbit/release.nix index 411be8868..b1b823104 100644 --- a/nix/pkgs/urbit/release.nix +++ b/nix/pkgs/urbit/release.nix @@ -4,7 +4,6 @@ ent, name ? "urbit", debug ? false, - ge-additions, libaes_siv }: @@ -16,7 +15,7 @@ let vendor = with deps; - [ argon2 softfloat3 ed25519 ge-additions urcrypt libaes_siv h2o scrypt uv murmur3 secp256k1 ivory-header ca-header ]; + [ argon2 softfloat3 ed25519 urcrypt libaes_siv h2o scrypt uv murmur3 secp256k1 ivory-header ca-header ]; in diff --git a/nix/pkgs/urbit/shell.nix b/nix/pkgs/urbit/shell.nix index 9e5d781af..81392a9f3 100644 --- a/nix/pkgs/urbit/shell.nix +++ b/nix/pkgs/urbit/shell.nix @@ -10,7 +10,7 @@ import ./default.nix { inherit pkgs; debug = false; inherit (tlon) - ent ge-additions urcrypt libaes_siv; + ent urcrypt libaes_siv; inherit (deps) argon2 ed25519 h2o murmur3 scrypt secp256k1 softfloat3 uv ivory-header ca-header; } diff --git a/nix/pkgs/urcrypt/default.nix b/nix/pkgs/urcrypt/default.nix index 739d05005..f2e390274 100644 --- a/nix/pkgs/urcrypt/default.nix +++ b/nix/pkgs/urcrypt/default.nix @@ -1,4 +1,4 @@ -{ stdenv, openssl, gmp, ed25519, secp256k1, ge-additions, argon2, libaes_siv }: +{ stdenv, openssl, gmp, ed25519, secp256k1, argon2, libaes_siv }: stdenv.mkDerivation rec { name = "urcrypt"; @@ -6,6 +6,6 @@ stdenv.mkDerivation rec { src = ../../../pkg/urcrypt; buildInputs = [ - openssl gmp ed25519 secp256k1 argon2 ge-additions libaes_siv + openssl gmp ed25519 secp256k1 argon2 libaes_siv ]; } diff --git a/nix/release.nix b/nix/release.nix index 2ccd1098b..083e3b2e7 100644 --- a/nix/release.nix +++ b/nix/release.nix @@ -16,9 +16,6 @@ let ent = env: import ./pkgs/ent/cross.nix env; - ge-additions = env: - import ./pkgs/ge-additions/cross.nix env; - libaes_siv = env: import ./pkgs/libaes_siv/cross.nix env; @@ -27,7 +24,6 @@ let inherit debug; name = if debug then "urbit-debug" else "urbit"; ent = ent env; - ge-additions = ge-additions env; libaes_siv = libaes_siv env; }; @@ -36,7 +32,6 @@ let inherit (plat.env) curl libgmp libsigsegv openssl zlib lmdb; inherit (plat.env) cmake_toolchain; ent = ent plat; - ge-additions = ge-additions plat; libaes_siv = libaes_siv plat; urbit = urbit { env = plat; debug = false; }; urbit-debug = urbit { env = plat; debug = true; }; diff --git a/pkg/ge-additions/Makefile b/pkg/ge-additions/Makefile deleted file mode 100644 index 719e76a2c..000000000 --- a/pkg/ge-additions/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -CC ?= cc -AR ?= ar -PREFIX ?= ./out - -################################################################################ - -.PHONY: all test install clean - -all: ge-additions.c ge-additions.h - $(CC) $(CFLAGS) -O3 -Wall -Werror -pedantic -std=gnu99 -c ge-additions.c - $(AR) rcs libge-additions.a ge-additions.o - -install: all - @mkdir -p $(PREFIX)/lib/ - @mkdir -p $(PREFIX)/include/ - cp libge-additions.a $(PREFIX)/lib/ - cp ge-additions.h $(PREFIX)/include/ - -clean: - rm -rf ./out diff --git a/pkg/urbit/configure b/pkg/urbit/configure index d496a86c0..a0758fcf0 100755 --- a/pkg/urbit/configure +++ b/pkg/urbit/configure @@ -6,7 +6,7 @@ URBIT_VERSION="0.10.8" deps=" \ curl gmp sigsegv argon2 ed25519 ent h2o scrypt uv murmur3 secp256k1 \ - softfloat3 ssl crypto z lmdb ge-additions aes_siv urcrypt pthread \ + softfloat3 ssl crypto z lmdb aes_siv urcrypt pthread \ " headers=" \ diff --git a/pkg/urcrypt/Makefile b/pkg/urcrypt/Makefile index 2c3d294ad..c9c9fa2cf 100644 --- a/pkg/urcrypt/Makefile +++ b/pkg/urcrypt/Makefile @@ -9,14 +9,23 @@ PREFIX ?= ./out CFLAGS := $(CFLAGS) -g -O3 -Wall -Werror -pedantic -std=gnu99 SOURCES = urcrypt.c urcrypt.h -liburcrypt.a: $(SOURCES) - $(CC) $(CFLAGS) -D URCRYPT_STATIC -c urcrypt.c -o urcrypt-static.o - $(AR) rcs liburcrypt.a urcrypt-static.o +GEO = ge-additions/ge-additions.o -liburcrypt.so: $(SOURCES) +$(GEO): ge-additions/ge-additions.c ge-additions/ge-additions.h + $(CC) $(CFLAGS) -c ge-additions/ge-additions.c -o $(GEO) + +urcrypt-static.o: $(SOURCES) $(GEO) + $(CC) $(CFLAGS) -D URCRYPT_STATIC -c urcrypt.c -o urcrypt-static.o + +liburcrypt.a: urcrypt-static.o $(GEO) + $(AR) rcs liburcrypt.a urcrypt-static.o $(GEO) + +urcrypt-shared.o: $(SOURCES) $(GEO) $(CC) $(CFLAGS) -fPIC -c urcrypt.c -o urcrypt-shared.o - $(CC) -shared urcrypt-shared.o -o liburcrypt.so \ - -led25519 -lge-additions -lssl -largon2 -laes_siv -lgmp -lsecp256k1 \ + +liburcrypt.so: urcrypt-shared.o + $(CC) -shared urcrypt-shared.o $(GEO) -o liburcrypt.so \ + -led25519 -lssl -largon2 -laes_siv -lgmp -lsecp256k1 \ -Wl,--no-undefined all: liburcrypt.a liburcrypt.so diff --git a/pkg/ge-additions/LICENSE b/pkg/urcrypt/ge-additions/LICENSE similarity index 100% rename from pkg/ge-additions/LICENSE rename to pkg/urcrypt/ge-additions/LICENSE diff --git a/pkg/ge-additions/README.md b/pkg/urcrypt/ge-additions/README.md similarity index 100% rename from pkg/ge-additions/README.md rename to pkg/urcrypt/ge-additions/README.md diff --git a/pkg/ge-additions/ge-additions.c b/pkg/urcrypt/ge-additions/ge-additions.c similarity index 100% rename from pkg/ge-additions/ge-additions.c rename to pkg/urcrypt/ge-additions/ge-additions.c diff --git a/pkg/ge-additions/ge-additions.h b/pkg/urcrypt/ge-additions/ge-additions.h similarity index 100% rename from pkg/ge-additions/ge-additions.h rename to pkg/urcrypt/ge-additions/ge-additions.h diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index a908b5f6c..bae6239cc 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -1,8 +1,8 @@ #include "urcrypt.h" +#include "ge-additions/ge-additions.h" #include #include -#include #include #include From 9ccdead8d8cdd30d2229bd11ed8d4a40861b4c96 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Fri, 2 Oct 2020 15:41:21 -0700 Subject: [PATCH 50/75] Squashed 'pkg/urcrypt/ed25519/' content from commit 7fa6712ef git-subtree-dir: pkg/urcrypt/ed25519 git-subtree-split: 7fa6712ef5d581a6981ec2b08ee623314cd1d1c4 --- ed25519_32.dll | Bin 0 -> 167526 bytes ed25519_64.dll | Bin 0 -> 174710 bytes license.txt | 16 + readme.md | 166 +++++ src/add_scalar.c | 69 ++ src/ed25519.h | 38 ++ src/fe.c | 1491 ++++++++++++++++++++++++++++++++++++++++++++ src/fe.h | 41 ++ src/fixedint.h | 72 +++ src/ge.c | 467 ++++++++++++++ src/ge.h | 74 +++ src/key_exchange.c | 79 +++ src/keypair.c | 16 + src/precomp_data.h | 1391 +++++++++++++++++++++++++++++++++++++++++ src/sc.c | 809 ++++++++++++++++++++++++ src/sc.h | 12 + src/seed.c | 40 ++ src/sha512.c | 275 ++++++++ src/sha512.h | 21 + src/sign.c | 31 + src/verify.c | 77 +++ test.c | 150 +++++ 22 files changed, 5335 insertions(+) create mode 100644 ed25519_32.dll create mode 100644 ed25519_64.dll create mode 100644 license.txt create mode 100644 readme.md create mode 100644 src/add_scalar.c create mode 100644 src/ed25519.h create mode 100644 src/fe.c create mode 100644 src/fe.h create mode 100644 src/fixedint.h create mode 100644 src/ge.c create mode 100644 src/ge.h create mode 100644 src/key_exchange.c create mode 100644 src/keypair.c create mode 100644 src/precomp_data.h create mode 100644 src/sc.c create mode 100644 src/sc.h create mode 100644 src/seed.c create mode 100644 src/sha512.c create mode 100644 src/sha512.h create mode 100644 src/sign.c create mode 100644 src/verify.c create mode 100644 test.c diff --git a/ed25519_32.dll b/ed25519_32.dll new file mode 100644 index 0000000000000000000000000000000000000000..80a61c3048747a72fbfc2973fc567056a7dacb60 GIT binary patch literal 167526 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 ^A^opK5x~$>Uo>yZJBrayv})N%{zDA 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 M&eb_$*k^J72PG=`*8l(j literal 0 HcmV?d00001 diff --git a/ed25519_64.dll b/ed25519_64.dll new file mode 100644 index 0000000000000000000000000000000000000000..6964679294e2facb0f28993d37f45996545cfaa7 GIT binary patch literal 174710 zcmeFadwdi{)<4{x>kI@YKq3JHBsywF5Hnm>P>?jtXg73Y5K!>8U{Hxyz(j%dk{F_C z$6oQay6WzO>upzE*RXyI(4e*)Tyd&?T)2FtRM(+_*s@9ti+T4<*>j1`=25t_Z;+XPvJ}FkC`h) z^~cO*am?Fe1~sh#i-q?3GxUW}JK{#x&7hFJa*9zmr49?xOFf;ZgD@ZL+sBNs$3 zvGEy{|H%`L%NvxdY041flGPb^R;;AP z=RwyXL7Fa2n|}LkL^i9mPSH1LdS&6_K>X|DM4>e3`u!kamzbg)7*L)f3SLW(UyPJh zj7H#^L$gJpDe)T<k= z%|X_k_x$#5=`QK+iu;GU1}#A{%4!!`a^c?lRegqUt zgR)*(aI+e?=AR6%3FZ!h>s4~eafdwm^pkz$Kmrwaf&f+xKu;W#^+t0hrV6FnkMijm z=ccDOSj{Fo`Gwg3l%i`;4sV&p_$WVU{1;*y-O)G{BcIi<$E|o&ljH<*5K3x17Pc(j zMrjn?gD8;GIdVK6X6w@ea*#k8*CUnHH>y1ua$w}kn21X2WPOvqS=B4`OA2QP;wOzI zMJgC));)@9=%yFxZRSXjlJwQ3y9(k<&cznGk)ZFSgd`+HB0wZA_Zv6WL2pREzK*gGNlfLotmPY@vv84FuUojndgQ{Mk8a{WfT)R+giYiKhoJc^XKot;0*NT)_sVoIW21U7~bx`4P*NO>)N*bP(OV5<~+>g1I zyaiO*_{?2{fk!r`xw93$Mb=x)p%UWgI`{vU;J0N+D>DcakY5M_6*KN~&+CBhv7tbS zokbikrLxPWB|u)R?2N+Wa;a4?NH*SeH$!}~ep1%!&HIONYG~74gE3Fb#jB~tG@exu z{t3dT*<04zsRgr$*=)w_m+o9fr{1i+!X^TG$p{*g7RvfM^9~AAXJ{lM{{a8y$5-

@U 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%0AxHE%GZ#{zg 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 RFlp0DGve6UOB?rt{{;i+ocaI& literal 0 HcmV?d00001 diff --git a/license.txt b/license.txt new file mode 100644 index 000000000..c1503f912 --- /dev/null +++ b/license.txt @@ -0,0 +1,16 @@ +Copyright (c) 2015 Orson Peters + +This software is provided 'as-is', without any express or implied warranty. In no event will the +authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial +applications, and to alter it and redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the + original software. If you use this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be misrepresented as + being the original software. + +3. This notice may not be removed or altered from any source distribution. diff --git a/readme.md b/readme.md new file mode 100644 index 000000000..89329bbe4 --- /dev/null +++ b/readme.md @@ -0,0 +1,166 @@ +Ed25519 +======= + +This is a portable implementation of [Ed25519](http://ed25519.cr.yp.to/) based +on the SUPERCOP "ref10" implementation. Additionally there is key exchanging +and scalar addition included to further aid building a PKI using Ed25519. All +code is licensed under the permissive zlib license. + +All code is pure ANSI C without any dependencies, except for the random seed +generation which uses standard OS cryptography APIs (`CryptGenRandom` on +Windows, `/dev/urandom` on nix). If you wish to be entirely portable define +`ED25519_NO_SEED`. This disables the `ed25519_create_seed` function, so if your +application requires key generation you must supply your own seeding function +(which is simply a 256 bit (32 byte) cryptographic random number generator). + + +Performance +----------- + +On a Windows machine with an Intel Pentium B970 @ 2.3GHz I got the following +speeds (running on only one a single core): + + Seed generation: 64us (15625 per second) + Key generation: 88us (11364 per second) + Message signing (short message): 87us (11494 per second) + Message verifying (short message): 228us (4386 per second) + Scalar addition: 100us (10000 per second) + Key exchange: 220us (4545 per second) + +The speeds on other machines may vary. Sign/verify times will be higher with +longer messages. The implementation significantly benefits from 64 bit +architectures, if possible compile as 64 bit. + + +Usage +----- + +Simply add all .c and .h files in the `src/` folder to your project and include +`ed25519.h` in any file you want to use the API. If you prefer to use a shared +library, only copy `ed25519.h` and define `ED25519_DLL` before importing. A +windows DLL is pre-built. + +There are no defined types for seeds, private keys, public keys, shared secrets +or signatures. Instead simple `unsigned char` buffers are used with the +following sizes: + +```c +unsigned char seed[32]; +unsigned char signature[64]; +unsigned char public_key[32]; +unsigned char private_key[64]; +unsigned char scalar[32]; +unsigned char shared_secret[32]; +``` + +API +--- + +```c +int ed25519_create_seed(unsigned char *seed); +``` + +Creates a 32 byte random seed in `seed` for key generation. `seed` must be a +writable 32 byte buffer. Returns 0 on success, and nonzero on failure. + +```c +void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, + const unsigned char *seed); +``` + +Creates a new key pair from the given seed. `public_key` must be a writable 32 +byte buffer, `private_key` must be a writable 64 byte buffer and `seed` must be +a 32 byte buffer. + +```c +void ed25519_sign(unsigned char *signature, + const unsigned char *message, size_t message_len, + const unsigned char *public_key, const unsigned char *private_key); +``` + +Creates a signature of the given message with the given key pair. `signature` +must be a writable 64 byte buffer. `message` must have at least `message_len` +bytes to be read. + +```c +int ed25519_verify(const unsigned char *signature, + const unsigned char *message, size_t message_len, + const unsigned char *public_key); +``` + +Verifies the signature on the given message using `public_key`. `signature` +must be a readable 64 byte buffer. `message` must have at least `message_len` +bytes to be read. Returns 1 if the signature matches, 0 otherwise. + +```c +void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, + const unsigned char *scalar); +``` + +Adds `scalar` to the given key pair where scalar is a 32 byte buffer (possibly +generated with `ed25519_create_seed`), generating a new key pair. You can +calculate the public key sum without knowing the private key and vice versa by +passing in `NULL` for the key you don't know. This is useful for enforcing +randomness on a key pair by a third party while only knowing the public key, +among other things. Warning: the last bit of the scalar is ignored - if +comparing scalars make sure to clear it with `scalar[31] &= 127`. + + +```c +void ed25519_key_exchange(unsigned char *shared_secret, + const unsigned char *public_key, const unsigned char *private_key); +``` + +Performs a key exchange on the given public key and private key, producing a +shared secret. It is recommended to hash the shared secret before using it. +`shared_secret` must be a 32 byte writable buffer where the shared secret will +be stored. + +Example +------- + +```c +unsigned char seed[32], public_key[32], private_key[64], signature[64]; +unsigned char other_public_key[32], other_private_key[64], shared_secret[32]; +const unsigned char message[] = "TEST MESSAGE"; + +/* create a random seed, and a key pair out of that seed */ +if (ed25519_create_seed(seed)) { + printf("error while generating seed\n"); + exit(1); +} + +ed25519_create_keypair(public_key, private_key, seed); + +/* create signature on the message with the key pair */ +ed25519_sign(signature, message, strlen(message), public_key, private_key); + +/* verify the signature */ +if (ed25519_verify(signature, message, strlen(message), public_key)) { + printf("valid signature\n"); +} else { + printf("invalid signature\n"); +} + +/* create a dummy keypair to use for a key exchange, normally you'd only have +the public key and receive it through some communication channel */ +if (ed25519_create_seed(seed)) { + printf("error while generating seed\n"); + exit(1); +} + +ed25519_create_keypair(other_public_key, other_private_key, seed); + +/* do a key exchange with other_public_key */ +ed25519_key_exchange(shared_secret, other_public_key, private_key); + +/* + the magic here is that ed25519_key_exchange(shared_secret, public_key, + other_private_key); would result in the same shared_secret +*/ + +``` + +License +------- +All code is released under the zlib license. See license.txt for details. diff --git a/src/add_scalar.c b/src/add_scalar.c new file mode 100644 index 000000000..7528a7a4a --- /dev/null +++ b/src/add_scalar.c @@ -0,0 +1,69 @@ +#include "ed25519.h" +#include "ge.h" +#include "sc.h" +#include "sha512.h" + + +/* see http://crypto.stackexchange.com/a/6215/4697 */ +void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar) { + const unsigned char SC_1[32] = {1}; /* scalar with value 1 */ + + unsigned char n[32]; + ge_p3 nB; + ge_p1p1 A_p1p1; + ge_p3 A; + ge_p3 public_key_unpacked; + ge_cached T; + + sha512_context hash; + unsigned char hashbuf[64]; + + int i; + + /* copy the scalar and clear highest bit */ + for (i = 0; i < 31; ++i) { + n[i] = scalar[i]; + } + n[31] = scalar[31] & 127; + + /* private key: a = n + t */ + if (private_key) { + sc_muladd(private_key, SC_1, n, private_key); + + // https://github.com/orlp/ed25519/issues/3 + sha512_init(&hash); + sha512_update(&hash, private_key + 32, 32); + sha512_update(&hash, scalar, 32); + sha512_final(&hash, hashbuf); + for (i = 0; i < 32; ++i) { + private_key[32 + i] = hashbuf[i]; + } + } + + /* public key: A = nB + T */ + if (public_key) { + /* if we know the private key we don't need a point addition, which is faster */ + /* using a "timing attack" you could find out wether or not we know the private + key, but this information seems rather useless - if this is important pass + public_key and private_key seperately in 2 function calls */ + if (private_key) { + ge_scalarmult_base(&A, private_key); + } else { + /* unpack public key into T */ + ge_frombytes_negate_vartime(&public_key_unpacked, public_key); + fe_neg(public_key_unpacked.X, public_key_unpacked.X); /* undo negate */ + fe_neg(public_key_unpacked.T, public_key_unpacked.T); /* undo negate */ + ge_p3_to_cached(&T, &public_key_unpacked); + + /* calculate n*B */ + ge_scalarmult_base(&nB, n); + + /* A = n*B + T */ + ge_add(&A_p1p1, &nB, &T); + ge_p1p1_to_p3(&A, &A_p1p1); + } + + /* pack public key */ + ge_p3_tobytes(public_key, &A); + } +} diff --git a/src/ed25519.h b/src/ed25519.h new file mode 100644 index 000000000..8924659fa --- /dev/null +++ b/src/ed25519.h @@ -0,0 +1,38 @@ +#ifndef ED25519_H +#define ED25519_H + +#include + +#if defined(_WIN32) + #if defined(ED25519_BUILD_DLL) + #define ED25519_DECLSPEC __declspec(dllexport) + #elif defined(ED25519_DLL) + #define ED25519_DECLSPEC __declspec(dllimport) + #else + #define ED25519_DECLSPEC + #endif +#else + #define ED25519_DECLSPEC +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ED25519_NO_SEED +int ED25519_DECLSPEC ed25519_create_seed(unsigned char *seed); +#endif + +void ED25519_DECLSPEC ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed); +void ED25519_DECLSPEC ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key); +int ED25519_DECLSPEC ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key); +void ED25519_DECLSPEC ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar); +void ED25519_DECLSPEC ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/fe.c b/src/fe.c new file mode 100644 index 000000000..2105eb7b2 --- /dev/null +++ b/src/fe.c @@ -0,0 +1,1491 @@ +#include "fixedint.h" +#include "fe.h" + + +/* + helper functions +*/ +static uint64_t load_3(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + + return result; +} + +static uint64_t load_4(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + result |= ((uint64_t) in[3]) << 24; + + return result; +} + + + +/* + h = 0 +*/ + +void fe_0(fe h) { + h[0] = 0; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} + + + +/* + h = 1 +*/ + +void fe_1(fe h) { + h[0] = 1; + h[1] = 0; + h[2] = 0; + h[3] = 0; + h[4] = 0; + h[5] = 0; + h[6] = 0; + h[7] = 0; + h[8] = 0; + h[9] = 0; +} + + + +/* + h = f + g + Can overlap h with f or g. + + Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + + Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +void fe_add(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t h0 = f0 + g0; + int32_t h1 = f1 + g1; + int32_t h2 = f2 + g2; + int32_t h3 = f3 + g3; + int32_t h4 = f4 + g4; + int32_t h5 = f5 + g5; + int32_t h6 = f6 + g6; + int32_t h7 = f7 + g7; + int32_t h8 = f8 + g8; + int32_t h9 = f9 + g9; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + + +/* + Replace (f,g) with (g,g) if b == 1; + replace (f,g) with (f,g) if b == 0. + + Preconditions: b in {0,1}. +*/ + +void fe_cmov(fe f, const fe g, unsigned int b) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t x0 = f0 ^ g0; + int32_t x1 = f1 ^ g1; + int32_t x2 = f2 ^ g2; + int32_t x3 = f3 ^ g3; + int32_t x4 = f4 ^ g4; + int32_t x5 = f5 ^ g5; + int32_t x6 = f6 ^ g6; + int32_t x7 = f7 ^ g7; + int32_t x8 = f8 ^ g8; + int32_t x9 = f9 ^ g9; + + b = (unsigned int) (- (int) b); /* silence warning */ + x0 &= b; + x1 &= b; + x2 &= b; + x3 &= b; + x4 &= b; + x5 &= b; + x6 &= b; + x7 &= b; + x8 &= b; + x9 &= b; + + f[0] = f0 ^ x0; + f[1] = f1 ^ x1; + f[2] = f2 ^ x2; + f[3] = f3 ^ x3; + f[4] = f4 ^ x4; + f[5] = f5 ^ x5; + f[6] = f6 ^ x6; + f[7] = f7 ^ x7; + f[8] = f8 ^ x8; + f[9] = f9 ^ x9; +} + +/* + Replace (f,g) with (g,f) if b == 1; + replace (f,g) with (f,g) if b == 0. + + Preconditions: b in {0,1}. +*/ + +void fe_cswap(fe f,fe g,unsigned int b) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t x0 = f0 ^ g0; + int32_t x1 = f1 ^ g1; + int32_t x2 = f2 ^ g2; + int32_t x3 = f3 ^ g3; + int32_t x4 = f4 ^ g4; + int32_t x5 = f5 ^ g5; + int32_t x6 = f6 ^ g6; + int32_t x7 = f7 ^ g7; + int32_t x8 = f8 ^ g8; + int32_t x9 = f9 ^ g9; + b = (unsigned int) (- (int) b); /* silence warning */ + x0 &= b; + x1 &= b; + x2 &= b; + x3 &= b; + x4 &= b; + x5 &= b; + x6 &= b; + x7 &= b; + x8 &= b; + x9 &= b; + f[0] = f0 ^ x0; + f[1] = f1 ^ x1; + f[2] = f2 ^ x2; + f[3] = f3 ^ x3; + f[4] = f4 ^ x4; + f[5] = f5 ^ x5; + f[6] = f6 ^ x6; + f[7] = f7 ^ x7; + f[8] = f8 ^ x8; + f[9] = f9 ^ x9; + g[0] = g0 ^ x0; + g[1] = g1 ^ x1; + g[2] = g2 ^ x2; + g[3] = g3 ^ x3; + g[4] = g4 ^ x4; + g[5] = g5 ^ x5; + g[6] = g6 ^ x6; + g[7] = g7 ^ x7; + g[8] = g8 ^ x8; + g[9] = g9 ^ x9; +} + + + +/* + h = f +*/ + +void fe_copy(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + + h[0] = f0; + h[1] = f1; + h[2] = f2; + h[3] = f3; + h[4] = f4; + h[5] = f5; + h[6] = f6; + h[7] = f7; + h[8] = f8; + h[9] = f9; +} + + + +/* + Ignores top bit of h. +*/ + +void fe_frombytes(fe h, const unsigned char *s) { + int64_t h0 = load_4(s); + int64_t h1 = load_3(s + 4) << 6; + int64_t h2 = load_3(s + 7) << 5; + int64_t h3 = load_3(s + 10) << 3; + int64_t h4 = load_3(s + 13) << 2; + int64_t h5 = load_4(s + 16); + int64_t h6 = load_3(s + 20) << 7; + int64_t h7 = load_3(s + 23) << 5; + int64_t h8 = load_3(s + 26) << 4; + int64_t h9 = (load_3(s + 29) & 8388607) << 2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + + +void fe_invert(fe out, const fe z) { + fe t0; + fe t1; + fe t2; + fe t3; + int i; + + fe_sq(t0, z); + + for (i = 1; i < 1; ++i) { + fe_sq(t0, t0); + } + + fe_sq(t1, t0); + + for (i = 1; i < 2; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, z, t1); + fe_mul(t0, t0, t1); + fe_sq(t2, t0); + + for (i = 1; i < 1; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t1, t2); + fe_sq(t2, t1); + + for (i = 1; i < 5; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + + for (i = 1; i < 10; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + + for (i = 1; i < 20; ++i) { + fe_sq(t3, t3); + } + + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + + for (i = 1; i < 10; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + + for (i = 1; i < 50; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + + for (i = 1; i < 100; ++i) { + fe_sq(t3, t3); + } + + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + + for (i = 1; i < 50; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + + for (i = 1; i < 5; ++i) { + fe_sq(t1, t1); + } + + fe_mul(out, t1, t0); +} + + + +/* + return 1 if f is in {1,3,5,...,q-2} + return 0 if f is in {0,2,4,...,q-1} + + Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +int fe_isnegative(const fe f) { + unsigned char s[32]; + + fe_tobytes(s, f); + + return s[0] & 1; +} + + + +/* + return 1 if f == 0 + return 0 if f != 0 + + Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +int fe_isnonzero(const fe f) { + unsigned char s[32]; + unsigned char r; + + fe_tobytes(s, f); + + r = s[0]; + #define F(i) r |= s[i] + F(1); + F(2); + F(3); + F(4); + F(5); + F(6); + F(7); + F(8); + F(9); + F(10); + F(11); + F(12); + F(13); + F(14); + F(15); + F(16); + F(17); + F(18); + F(19); + F(20); + F(21); + F(22); + F(23); + F(24); + F(25); + F(26); + F(27); + F(28); + F(29); + F(30); + F(31); + #undef F + + return r != 0; +} + + + +/* + h = f * g + Can overlap h with f or g. + + Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + |g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + + Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. + */ + + /* + Notes on implementation strategy: + + Using schoolbook multiplication. + Karatsuba would save a little in some cost models. + + Most multiplications by 2 and 19 are 32-bit precomputations; + cheaper than 64-bit postcomputations. + + There is one remaining multiplication by 19 in the carry chain; + one *19 precomputation can be merged into this, + but the resulting data flow is considerably less clean. + + There are 12 carries below. + 10 of them are 2-way parallelizable and vectorizable. + Can get away with 11 carries, but then data flow is much deeper. + + With tighter constraints on inputs can squeeze carries into int32. +*/ + +void fe_mul(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t g1_19 = 19 * g1; /* 1.959375*2^29 */ + int32_t g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ + int32_t g3_19 = 19 * g3; + int32_t g4_19 = 19 * g4; + int32_t g5_19 = 19 * g5; + int32_t g6_19 = 19 * g6; + int32_t g7_19 = 19 * g7; + int32_t g8_19 = 19 * g8; + int32_t g9_19 = 19 * g9; + int32_t f1_2 = 2 * f1; + int32_t f3_2 = 2 * f3; + int32_t f5_2 = 2 * f5; + int32_t f7_2 = 2 * f7; + int32_t f9_2 = 2 * f9; + int64_t f0g0 = f0 * (int64_t) g0; + int64_t f0g1 = f0 * (int64_t) g1; + int64_t f0g2 = f0 * (int64_t) g2; + int64_t f0g3 = f0 * (int64_t) g3; + int64_t f0g4 = f0 * (int64_t) g4; + int64_t f0g5 = f0 * (int64_t) g5; + int64_t f0g6 = f0 * (int64_t) g6; + int64_t f0g7 = f0 * (int64_t) g7; + int64_t f0g8 = f0 * (int64_t) g8; + int64_t f0g9 = f0 * (int64_t) g9; + int64_t f1g0 = f1 * (int64_t) g0; + int64_t f1g1_2 = f1_2 * (int64_t) g1; + int64_t f1g2 = f1 * (int64_t) g2; + int64_t f1g3_2 = f1_2 * (int64_t) g3; + int64_t f1g4 = f1 * (int64_t) g4; + int64_t f1g5_2 = f1_2 * (int64_t) g5; + int64_t f1g6 = f1 * (int64_t) g6; + int64_t f1g7_2 = f1_2 * (int64_t) g7; + int64_t f1g8 = f1 * (int64_t) g8; + int64_t f1g9_38 = f1_2 * (int64_t) g9_19; + int64_t f2g0 = f2 * (int64_t) g0; + int64_t f2g1 = f2 * (int64_t) g1; + int64_t f2g2 = f2 * (int64_t) g2; + int64_t f2g3 = f2 * (int64_t) g3; + int64_t f2g4 = f2 * (int64_t) g4; + int64_t f2g5 = f2 * (int64_t) g5; + int64_t f2g6 = f2 * (int64_t) g6; + int64_t f2g7 = f2 * (int64_t) g7; + int64_t f2g8_19 = f2 * (int64_t) g8_19; + int64_t f2g9_19 = f2 * (int64_t) g9_19; + int64_t f3g0 = f3 * (int64_t) g0; + int64_t f3g1_2 = f3_2 * (int64_t) g1; + int64_t f3g2 = f3 * (int64_t) g2; + int64_t f3g3_2 = f3_2 * (int64_t) g3; + int64_t f3g4 = f3 * (int64_t) g4; + int64_t f3g5_2 = f3_2 * (int64_t) g5; + int64_t f3g6 = f3 * (int64_t) g6; + int64_t f3g7_38 = f3_2 * (int64_t) g7_19; + int64_t f3g8_19 = f3 * (int64_t) g8_19; + int64_t f3g9_38 = f3_2 * (int64_t) g9_19; + int64_t f4g0 = f4 * (int64_t) g0; + int64_t f4g1 = f4 * (int64_t) g1; + int64_t f4g2 = f4 * (int64_t) g2; + int64_t f4g3 = f4 * (int64_t) g3; + int64_t f4g4 = f4 * (int64_t) g4; + int64_t f4g5 = f4 * (int64_t) g5; + int64_t f4g6_19 = f4 * (int64_t) g6_19; + int64_t f4g7_19 = f4 * (int64_t) g7_19; + int64_t f4g8_19 = f4 * (int64_t) g8_19; + int64_t f4g9_19 = f4 * (int64_t) g9_19; + int64_t f5g0 = f5 * (int64_t) g0; + int64_t f5g1_2 = f5_2 * (int64_t) g1; + int64_t f5g2 = f5 * (int64_t) g2; + int64_t f5g3_2 = f5_2 * (int64_t) g3; + int64_t f5g4 = f5 * (int64_t) g4; + int64_t f5g5_38 = f5_2 * (int64_t) g5_19; + int64_t f5g6_19 = f5 * (int64_t) g6_19; + int64_t f5g7_38 = f5_2 * (int64_t) g7_19; + int64_t f5g8_19 = f5 * (int64_t) g8_19; + int64_t f5g9_38 = f5_2 * (int64_t) g9_19; + int64_t f6g0 = f6 * (int64_t) g0; + int64_t f6g1 = f6 * (int64_t) g1; + int64_t f6g2 = f6 * (int64_t) g2; + int64_t f6g3 = f6 * (int64_t) g3; + int64_t f6g4_19 = f6 * (int64_t) g4_19; + int64_t f6g5_19 = f6 * (int64_t) g5_19; + int64_t f6g6_19 = f6 * (int64_t) g6_19; + int64_t f6g7_19 = f6 * (int64_t) g7_19; + int64_t f6g8_19 = f6 * (int64_t) g8_19; + int64_t f6g9_19 = f6 * (int64_t) g9_19; + int64_t f7g0 = f7 * (int64_t) g0; + int64_t f7g1_2 = f7_2 * (int64_t) g1; + int64_t f7g2 = f7 * (int64_t) g2; + int64_t f7g3_38 = f7_2 * (int64_t) g3_19; + int64_t f7g4_19 = f7 * (int64_t) g4_19; + int64_t f7g5_38 = f7_2 * (int64_t) g5_19; + int64_t f7g6_19 = f7 * (int64_t) g6_19; + int64_t f7g7_38 = f7_2 * (int64_t) g7_19; + int64_t f7g8_19 = f7 * (int64_t) g8_19; + int64_t f7g9_38 = f7_2 * (int64_t) g9_19; + int64_t f8g0 = f8 * (int64_t) g0; + int64_t f8g1 = f8 * (int64_t) g1; + int64_t f8g2_19 = f8 * (int64_t) g2_19; + int64_t f8g3_19 = f8 * (int64_t) g3_19; + int64_t f8g4_19 = f8 * (int64_t) g4_19; + int64_t f8g5_19 = f8 * (int64_t) g5_19; + int64_t f8g6_19 = f8 * (int64_t) g6_19; + int64_t f8g7_19 = f8 * (int64_t) g7_19; + int64_t f8g8_19 = f8 * (int64_t) g8_19; + int64_t f8g9_19 = f8 * (int64_t) g9_19; + int64_t f9g0 = f9 * (int64_t) g0; + int64_t f9g1_38 = f9_2 * (int64_t) g1_19; + int64_t f9g2_19 = f9 * (int64_t) g2_19; + int64_t f9g3_38 = f9_2 * (int64_t) g3_19; + int64_t f9g4_19 = f9 * (int64_t) g4_19; + int64_t f9g5_38 = f9_2 * (int64_t) g5_19; + int64_t f9g6_19 = f9 * (int64_t) g6_19; + int64_t f9g7_38 = f9_2 * (int64_t) g7_19; + int64_t f9g8_19 = f9 * (int64_t) g8_19; + int64_t f9g9_38 = f9_2 * (int64_t) g9_19; + int64_t h0 = f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38; + int64_t h1 = f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19; + int64_t h2 = f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38; + int64_t h3 = f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19; + int64_t h4 = f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38; + int64_t h5 = f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19; + int64_t h6 = f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38; + int64_t h7 = f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19; + int64_t h8 = f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38; + int64_t h9 = f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 ; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = f * 121666 +Can overlap h with f. + +Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + +Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +*/ + +void fe_mul121666(fe h, fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int64_t h0 = f0 * (int64_t) 121666; + int64_t h1 = f1 * (int64_t) 121666; + int64_t h2 = f2 * (int64_t) 121666; + int64_t h3 = f3 * (int64_t) 121666; + int64_t h4 = f4 * (int64_t) 121666; + int64_t h5 = f5 * (int64_t) 121666; + int64_t h6 = f6 * (int64_t) 121666; + int64_t h7 = f7 * (int64_t) 121666; + int64_t h8 = f8 * (int64_t) 121666; + int64_t h9 = f9 * (int64_t) 121666; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry9 = (h9 + (int64_t) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (int64_t) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (int64_t) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (int64_t) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (int64_t) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (int64_t) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (int64_t) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = -f + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +*/ + +void fe_neg(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t h0 = -f0; + int32_t h1 = -f1; + int32_t h2 = -f2; + int32_t h3 = -f3; + int32_t h4 = -f4; + int32_t h5 = -f5; + int32_t h6 = -f6; + int32_t h7 = -f7; + int32_t h8 = -f8; + int32_t h9 = -f9; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + +void fe_pow22523(fe out, const fe z) { + fe t0; + fe t1; + fe t2; + int i; + fe_sq(t0, z); + + for (i = 1; i < 1; ++i) { + fe_sq(t0, t0); + } + + fe_sq(t1, t0); + + for (i = 1; i < 2; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, z, t1); + fe_mul(t0, t0, t1); + fe_sq(t0, t0); + + for (i = 1; i < 1; ++i) { + fe_sq(t0, t0); + } + + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + + for (i = 1; i < 5; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + + for (i = 1; i < 10; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, t1, t0); + fe_sq(t2, t1); + + for (i = 1; i < 20; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + + for (i = 1; i < 10; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t0, t1, t0); + fe_sq(t1, t0); + + for (i = 1; i < 50; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t1, t1, t0); + fe_sq(t2, t1); + + for (i = 1; i < 100; ++i) { + fe_sq(t2, t2); + } + + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + + for (i = 1; i < 50; ++i) { + fe_sq(t1, t1); + } + + fe_mul(t0, t1, t0); + fe_sq(t0, t0); + + for (i = 1; i < 2; ++i) { + fe_sq(t0, t0); + } + + fe_mul(out, t0, z); + return; +} + + +/* +h = f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +void fe_sq(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t f0_2 = 2 * f0; + int32_t f1_2 = 2 * f1; + int32_t f2_2 = 2 * f2; + int32_t f3_2 = 2 * f3; + int32_t f4_2 = 2 * f4; + int32_t f5_2 = 2 * f5; + int32_t f6_2 = 2 * f6; + int32_t f7_2 = 2 * f7; + int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ + int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ + int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ + int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ + int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ + int64_t f0f0 = f0 * (int64_t) f0; + int64_t f0f1_2 = f0_2 * (int64_t) f1; + int64_t f0f2_2 = f0_2 * (int64_t) f2; + int64_t f0f3_2 = f0_2 * (int64_t) f3; + int64_t f0f4_2 = f0_2 * (int64_t) f4; + int64_t f0f5_2 = f0_2 * (int64_t) f5; + int64_t f0f6_2 = f0_2 * (int64_t) f6; + int64_t f0f7_2 = f0_2 * (int64_t) f7; + int64_t f0f8_2 = f0_2 * (int64_t) f8; + int64_t f0f9_2 = f0_2 * (int64_t) f9; + int64_t f1f1_2 = f1_2 * (int64_t) f1; + int64_t f1f2_2 = f1_2 * (int64_t) f2; + int64_t f1f3_4 = f1_2 * (int64_t) f3_2; + int64_t f1f4_2 = f1_2 * (int64_t) f4; + int64_t f1f5_4 = f1_2 * (int64_t) f5_2; + int64_t f1f6_2 = f1_2 * (int64_t) f6; + int64_t f1f7_4 = f1_2 * (int64_t) f7_2; + int64_t f1f8_2 = f1_2 * (int64_t) f8; + int64_t f1f9_76 = f1_2 * (int64_t) f9_38; + int64_t f2f2 = f2 * (int64_t) f2; + int64_t f2f3_2 = f2_2 * (int64_t) f3; + int64_t f2f4_2 = f2_2 * (int64_t) f4; + int64_t f2f5_2 = f2_2 * (int64_t) f5; + int64_t f2f6_2 = f2_2 * (int64_t) f6; + int64_t f2f7_2 = f2_2 * (int64_t) f7; + int64_t f2f8_38 = f2_2 * (int64_t) f8_19; + int64_t f2f9_38 = f2 * (int64_t) f9_38; + int64_t f3f3_2 = f3_2 * (int64_t) f3; + int64_t f3f4_2 = f3_2 * (int64_t) f4; + int64_t f3f5_4 = f3_2 * (int64_t) f5_2; + int64_t f3f6_2 = f3_2 * (int64_t) f6; + int64_t f3f7_76 = f3_2 * (int64_t) f7_38; + int64_t f3f8_38 = f3_2 * (int64_t) f8_19; + int64_t f3f9_76 = f3_2 * (int64_t) f9_38; + int64_t f4f4 = f4 * (int64_t) f4; + int64_t f4f5_2 = f4_2 * (int64_t) f5; + int64_t f4f6_38 = f4_2 * (int64_t) f6_19; + int64_t f4f7_38 = f4 * (int64_t) f7_38; + int64_t f4f8_38 = f4_2 * (int64_t) f8_19; + int64_t f4f9_38 = f4 * (int64_t) f9_38; + int64_t f5f5_38 = f5 * (int64_t) f5_38; + int64_t f5f6_38 = f5_2 * (int64_t) f6_19; + int64_t f5f7_76 = f5_2 * (int64_t) f7_38; + int64_t f5f8_38 = f5_2 * (int64_t) f8_19; + int64_t f5f9_76 = f5_2 * (int64_t) f9_38; + int64_t f6f6_19 = f6 * (int64_t) f6_19; + int64_t f6f7_38 = f6 * (int64_t) f7_38; + int64_t f6f8_38 = f6_2 * (int64_t) f8_19; + int64_t f6f9_38 = f6 * (int64_t) f9_38; + int64_t f7f7_38 = f7 * (int64_t) f7_38; + int64_t f7f8_38 = f7_2 * (int64_t) f8_19; + int64_t f7f9_76 = f7_2 * (int64_t) f9_38; + int64_t f8f8_19 = f8 * (int64_t) f8_19; + int64_t f8f9_38 = f8 * (int64_t) f9_38; + int64_t f9f9_38 = f9 * (int64_t) f9_38; + int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; + int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; + int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; + int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; + int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; + int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; + int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; + int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; + int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; + int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = 2 * f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + +/* +See fe_mul.c for discussion of implementation strategy. +*/ + +void fe_sq2(fe h, const fe f) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t f0_2 = 2 * f0; + int32_t f1_2 = 2 * f1; + int32_t f2_2 = 2 * f2; + int32_t f3_2 = 2 * f3; + int32_t f4_2 = 2 * f4; + int32_t f5_2 = 2 * f5; + int32_t f6_2 = 2 * f6; + int32_t f7_2 = 2 * f7; + int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ + int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ + int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ + int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ + int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ + int64_t f0f0 = f0 * (int64_t) f0; + int64_t f0f1_2 = f0_2 * (int64_t) f1; + int64_t f0f2_2 = f0_2 * (int64_t) f2; + int64_t f0f3_2 = f0_2 * (int64_t) f3; + int64_t f0f4_2 = f0_2 * (int64_t) f4; + int64_t f0f5_2 = f0_2 * (int64_t) f5; + int64_t f0f6_2 = f0_2 * (int64_t) f6; + int64_t f0f7_2 = f0_2 * (int64_t) f7; + int64_t f0f8_2 = f0_2 * (int64_t) f8; + int64_t f0f9_2 = f0_2 * (int64_t) f9; + int64_t f1f1_2 = f1_2 * (int64_t) f1; + int64_t f1f2_2 = f1_2 * (int64_t) f2; + int64_t f1f3_4 = f1_2 * (int64_t) f3_2; + int64_t f1f4_2 = f1_2 * (int64_t) f4; + int64_t f1f5_4 = f1_2 * (int64_t) f5_2; + int64_t f1f6_2 = f1_2 * (int64_t) f6; + int64_t f1f7_4 = f1_2 * (int64_t) f7_2; + int64_t f1f8_2 = f1_2 * (int64_t) f8; + int64_t f1f9_76 = f1_2 * (int64_t) f9_38; + int64_t f2f2 = f2 * (int64_t) f2; + int64_t f2f3_2 = f2_2 * (int64_t) f3; + int64_t f2f4_2 = f2_2 * (int64_t) f4; + int64_t f2f5_2 = f2_2 * (int64_t) f5; + int64_t f2f6_2 = f2_2 * (int64_t) f6; + int64_t f2f7_2 = f2_2 * (int64_t) f7; + int64_t f2f8_38 = f2_2 * (int64_t) f8_19; + int64_t f2f9_38 = f2 * (int64_t) f9_38; + int64_t f3f3_2 = f3_2 * (int64_t) f3; + int64_t f3f4_2 = f3_2 * (int64_t) f4; + int64_t f3f5_4 = f3_2 * (int64_t) f5_2; + int64_t f3f6_2 = f3_2 * (int64_t) f6; + int64_t f3f7_76 = f3_2 * (int64_t) f7_38; + int64_t f3f8_38 = f3_2 * (int64_t) f8_19; + int64_t f3f9_76 = f3_2 * (int64_t) f9_38; + int64_t f4f4 = f4 * (int64_t) f4; + int64_t f4f5_2 = f4_2 * (int64_t) f5; + int64_t f4f6_38 = f4_2 * (int64_t) f6_19; + int64_t f4f7_38 = f4 * (int64_t) f7_38; + int64_t f4f8_38 = f4_2 * (int64_t) f8_19; + int64_t f4f9_38 = f4 * (int64_t) f9_38; + int64_t f5f5_38 = f5 * (int64_t) f5_38; + int64_t f5f6_38 = f5_2 * (int64_t) f6_19; + int64_t f5f7_76 = f5_2 * (int64_t) f7_38; + int64_t f5f8_38 = f5_2 * (int64_t) f8_19; + int64_t f5f9_76 = f5_2 * (int64_t) f9_38; + int64_t f6f6_19 = f6 * (int64_t) f6_19; + int64_t f6f7_38 = f6 * (int64_t) f7_38; + int64_t f6f8_38 = f6_2 * (int64_t) f8_19; + int64_t f6f9_38 = f6 * (int64_t) f9_38; + int64_t f7f7_38 = f7 * (int64_t) f7_38; + int64_t f7f8_38 = f7_2 * (int64_t) f8_19; + int64_t f7f9_76 = f7_2 * (int64_t) f9_38; + int64_t f8f8_19 = f8 * (int64_t) f8_19; + int64_t f8f9_38 = f8 * (int64_t) f9_38; + int64_t f9f9_38 = f9 * (int64_t) f9_38; + int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; + int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; + int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; + int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; + int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; + int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; + int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; + int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; + int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; + int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + h0 += h0; + h1 += h1; + h2 += h2; + h3 += h3; + h4 += h4; + h5 += h5; + h6 += h6; + h7 += h7; + h8 += h8; + h9 += h9; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry1 = (h1 + (int64_t) (1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry5 = (h5 + (int64_t) (1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry2 = (h2 + (int64_t) (1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry6 = (h6 + (int64_t) (1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry3 = (h3 + (int64_t) (1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry7 = (h7 + (int64_t) (1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry4 = (h4 + (int64_t) (1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry8 = (h8 + (int64_t) (1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 << 26; + carry9 = (h9 + (int64_t) (1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 << 25; + carry0 = (h0 + (int64_t) (1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 << 26; + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + + +/* +h = f - g +Can overlap h with f or g. + +Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + +Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +*/ + +void fe_sub(fe h, const fe f, const fe g) { + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + int32_t h0 = f0 - g0; + int32_t h1 = f1 - g1; + int32_t h2 = f2 - g2; + int32_t h3 = f3 - g3; + int32_t h4 = f4 - g4; + int32_t h5 = f5 - g5; + int32_t h6 = f6 - g6; + int32_t h7 = f7 - g7; + int32_t h8 = f8 - g8; + int32_t h9 = f9 - g9; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + + + +/* +Preconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + +Write p=2^255-19; q=floor(h/p). +Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). + +Proof: + Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. + Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4. + + Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). + Then 0> 25; + q = (h0 + q) >> 26; + q = (h1 + q) >> 25; + q = (h2 + q) >> 26; + q = (h3 + q) >> 25; + q = (h4 + q) >> 26; + q = (h5 + q) >> 25; + q = (h6 + q) >> 26; + q = (h7 + q) >> 25; + q = (h8 + q) >> 26; + q = (h9 + q) >> 25; + /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */ + h0 += 19 * q; + /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ + carry0 = h0 >> 26; + h1 += carry0; + h0 -= carry0 << 26; + carry1 = h1 >> 25; + h2 += carry1; + h1 -= carry1 << 25; + carry2 = h2 >> 26; + h3 += carry2; + h2 -= carry2 << 26; + carry3 = h3 >> 25; + h4 += carry3; + h3 -= carry3 << 25; + carry4 = h4 >> 26; + h5 += carry4; + h4 -= carry4 << 26; + carry5 = h5 >> 25; + h6 += carry5; + h5 -= carry5 << 25; + carry6 = h6 >> 26; + h7 += carry6; + h6 -= carry6 << 26; + carry7 = h7 >> 25; + h8 += carry7; + h7 -= carry7 << 25; + carry8 = h8 >> 26; + h9 += carry8; + h8 -= carry8 << 26; + carry9 = h9 >> 25; + h9 -= carry9 << 25; + + /* h10 = carry9 */ + /* + Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. + Have h0+...+2^230 h9 between 0 and 2^255-1; + evidently 2^255 h10-2^255 q = 0. + Goal: Output h0+...+2^230 h9. + */ + s[0] = (unsigned char) (h0 >> 0); + s[1] = (unsigned char) (h0 >> 8); + s[2] = (unsigned char) (h0 >> 16); + s[3] = (unsigned char) ((h0 >> 24) | (h1 << 2)); + s[4] = (unsigned char) (h1 >> 6); + s[5] = (unsigned char) (h1 >> 14); + s[6] = (unsigned char) ((h1 >> 22) | (h2 << 3)); + s[7] = (unsigned char) (h2 >> 5); + s[8] = (unsigned char) (h2 >> 13); + s[9] = (unsigned char) ((h2 >> 21) | (h3 << 5)); + s[10] = (unsigned char) (h3 >> 3); + s[11] = (unsigned char) (h3 >> 11); + s[12] = (unsigned char) ((h3 >> 19) | (h4 << 6)); + s[13] = (unsigned char) (h4 >> 2); + s[14] = (unsigned char) (h4 >> 10); + s[15] = (unsigned char) (h4 >> 18); + s[16] = (unsigned char) (h5 >> 0); + s[17] = (unsigned char) (h5 >> 8); + s[18] = (unsigned char) (h5 >> 16); + s[19] = (unsigned char) ((h5 >> 24) | (h6 << 1)); + s[20] = (unsigned char) (h6 >> 7); + s[21] = (unsigned char) (h6 >> 15); + s[22] = (unsigned char) ((h6 >> 23) | (h7 << 3)); + s[23] = (unsigned char) (h7 >> 5); + s[24] = (unsigned char) (h7 >> 13); + s[25] = (unsigned char) ((h7 >> 21) | (h8 << 4)); + s[26] = (unsigned char) (h8 >> 4); + s[27] = (unsigned char) (h8 >> 12); + s[28] = (unsigned char) ((h8 >> 20) | (h9 << 6)); + s[29] = (unsigned char) (h9 >> 2); + s[30] = (unsigned char) (h9 >> 10); + s[31] = (unsigned char) (h9 >> 18); +} diff --git a/src/fe.h b/src/fe.h new file mode 100644 index 000000000..b4b62d282 --- /dev/null +++ b/src/fe.h @@ -0,0 +1,41 @@ +#ifndef FE_H +#define FE_H + +#include "fixedint.h" + + +/* + fe means field element. + Here the field is \Z/(2^255-19). + An element t, entries t[0]...t[9], represents the integer + t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9]. + Bounds on each t[i] vary depending on context. +*/ + + +typedef int32_t fe[10]; + + +void fe_0(fe h); +void fe_1(fe h); + +void fe_frombytes(fe h, const unsigned char *s); +void fe_tobytes(unsigned char *s, const fe h); + +void fe_copy(fe h, const fe f); +int fe_isnegative(const fe f); +int fe_isnonzero(const fe f); +void fe_cmov(fe f, const fe g, unsigned int b); +void fe_cswap(fe f, fe g, unsigned int b); + +void fe_neg(fe h, const fe f); +void fe_add(fe h, const fe f, const fe g); +void fe_invert(fe out, const fe z); +void fe_sq(fe h, const fe f); +void fe_sq2(fe h, const fe f); +void fe_mul(fe h, const fe f, const fe g); +void fe_mul121666(fe h, fe f); +void fe_pow22523(fe out, const fe z); +void fe_sub(fe h, const fe f, const fe g); + +#endif diff --git a/src/fixedint.h b/src/fixedint.h new file mode 100644 index 000000000..1a8745b1e --- /dev/null +++ b/src/fixedint.h @@ -0,0 +1,72 @@ +/* + Portable header to provide the 32 and 64 bits type. + + Not a compatible replacement for , do not blindly use it as such. +*/ + +#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__WATCOMC__) && (defined(_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_) || defined(__UINT_FAST64_TYPE__)) )) && !defined(FIXEDINT_H_INCLUDED) + #include + #define FIXEDINT_H_INCLUDED + + #if defined(__WATCOMC__) && __WATCOMC__ >= 1250 && !defined(UINT64_C) + #include + #define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) + #endif +#endif + + +#ifndef FIXEDINT_H_INCLUDED + #define FIXEDINT_H_INCLUDED + + #include + + /* (u)int32_t */ + #ifndef uint32_t + #if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long uint32_t; + #elif (UINT_MAX == 0xffffffffUL) + typedef unsigned int uint32_t; + #elif (USHRT_MAX == 0xffffffffUL) + typedef unsigned short uint32_t; + #endif + #endif + + + #ifndef int32_t + #if (LONG_MAX == 0x7fffffffL) + typedef signed long int32_t; + #elif (INT_MAX == 0x7fffffffL) + typedef signed int int32_t; + #elif (SHRT_MAX == 0x7fffffffL) + typedef signed short int32_t; + #endif + #endif + + + /* (u)int64_t */ + #if (defined(__STDC__) && defined(__STDC_VERSION__) && __STDC__ && __STDC_VERSION__ >= 199901L) + typedef long long int64_t; + typedef unsigned long long uint64_t; + + #define UINT64_C(v) v ##ULL + #define INT64_C(v) v ##LL + #elif defined(__GNUC__) + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; + + #define UINT64_C(v) v ##ULL + #define INT64_C(v) v ##LL + #elif defined(__MWERKS__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__APPLE_CC__) || defined(_LONG_LONG) || defined(_CRAYC) + typedef long long int64_t; + typedef unsigned long long uint64_t; + + #define UINT64_C(v) v ##ULL + #define INT64_C(v) v ##LL + #elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined(__BORLANDC__) && __BORLANDC__ > 0x460) || defined(__alpha) || defined(__DECC) + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + + #define UINT64_C(v) v ##UI64 + #define INT64_C(v) v ##I64 + #endif +#endif diff --git a/src/ge.c b/src/ge.c new file mode 100644 index 000000000..87c691bff --- /dev/null +++ b/src/ge.c @@ -0,0 +1,467 @@ +#include "ge.h" +#include "precomp_data.h" + + +/* +r = p + q +*/ + +void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { + fe t0; + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->YplusX); + fe_mul(r->Y, r->Y, q->YminusX); + fe_mul(r->T, q->T2d, p->T); + fe_mul(r->X, p->Z, q->Z); + fe_add(t0, r->X, r->X); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_add(r->Z, t0, r->T); + fe_sub(r->T, t0, r->T); +} + + +static void slide(signed char *r, const unsigned char *a) { + int i; + int b; + int k; + + for (i = 0; i < 256; ++i) { + r[i] = 1 & (a[i >> 3] >> (i & 7)); + } + + for (i = 0; i < 256; ++i) + if (r[i]) { + for (b = 1; b <= 6 && i + b < 256; ++b) { + if (r[i + b]) { + if (r[i] + (r[i + b] << b) <= 15) { + r[i] += r[i + b] << b; + r[i + b] = 0; + } else if (r[i] - (r[i + b] << b) >= -15) { + r[i] -= r[i + b] << b; + + for (k = i + b; k < 256; ++k) { + if (!r[k]) { + r[k] = 1; + break; + } + + r[k] = 0; + } + } else { + break; + } + } + } + } +} + +/* +r = a * A + b * B +where a = a[0]+256*a[1]+...+256^31 a[31]. +and b = b[0]+256*b[1]+...+256^31 b[31]. +B is the Ed25519 base point (x,4/5) with x positive. +*/ + +void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) { + signed char aslide[256]; + signed char bslide[256]; + ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ + ge_p1p1 t; + ge_p3 u; + ge_p3 A2; + int i; + slide(aslide, a); + slide(bslide, b); + ge_p3_to_cached(&Ai[0], A); + ge_p3_dbl(&t, A); + ge_p1p1_to_p3(&A2, &t); + ge_add(&t, &A2, &Ai[0]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[1], &u); + ge_add(&t, &A2, &Ai[1]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[2], &u); + ge_add(&t, &A2, &Ai[2]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[3], &u); + ge_add(&t, &A2, &Ai[3]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[4], &u); + ge_add(&t, &A2, &Ai[4]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[5], &u); + ge_add(&t, &A2, &Ai[5]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[6], &u); + ge_add(&t, &A2, &Ai[6]); + ge_p1p1_to_p3(&u, &t); + ge_p3_to_cached(&Ai[7], &u); + ge_p2_0(r); + + for (i = 255; i >= 0; --i) { + if (aslide[i] || bslide[i]) { + break; + } + } + + for (; i >= 0; --i) { + ge_p2_dbl(&t, r); + + if (aslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_add(&t, &u, &Ai[aslide[i] / 2]); + } else if (aslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]); + } + + if (bslide[i] > 0) { + ge_p1p1_to_p3(&u, &t); + ge_madd(&t, &u, &Bi[bslide[i] / 2]); + } else if (bslide[i] < 0) { + ge_p1p1_to_p3(&u, &t); + ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]); + } + + ge_p1p1_to_p2(r, &t); + } +} + + +static const fe d = { + -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116 +}; + +static const fe sqrtm1 = { + -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482 +}; + +int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s) { + fe u; + fe v; + fe v3; + fe vxx; + fe check; + fe_frombytes(h->Y, s); + fe_1(h->Z); + fe_sq(u, h->Y); + fe_mul(v, u, d); + fe_sub(u, u, h->Z); /* u = y^2-1 */ + fe_add(v, v, h->Z); /* v = dy^2+1 */ + fe_sq(v3, v); + fe_mul(v3, v3, v); /* v3 = v^3 */ + fe_sq(h->X, v3); + fe_mul(h->X, h->X, v); + fe_mul(h->X, h->X, u); /* x = uv^7 */ + fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */ + fe_mul(h->X, h->X, v3); + fe_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */ + fe_sq(vxx, h->X); + fe_mul(vxx, vxx, v); + fe_sub(check, vxx, u); /* vx^2-u */ + + if (fe_isnonzero(check)) { + fe_add(check, vxx, u); /* vx^2+u */ + + if (fe_isnonzero(check)) { + return -1; + } + + fe_mul(h->X, h->X, sqrtm1); + } + + if (fe_isnegative(h->X) == (s[31] >> 7)) { + fe_neg(h->X, h->X); + } + + fe_mul(h->T, h->X, h->Y); + return 0; +} + + +/* +r = p + q +*/ + +void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { + fe t0; + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->yplusx); + fe_mul(r->Y, r->Y, q->yminusx); + fe_mul(r->T, q->xy2d, p->T); + fe_add(t0, p->Z, p->Z); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_add(r->Z, t0, r->T); + fe_sub(r->T, t0, r->T); +} + + +/* +r = p - q +*/ + +void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) { + fe t0; + + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->yminusx); + fe_mul(r->Y, r->Y, q->yplusx); + fe_mul(r->T, q->xy2d, p->T); + fe_add(t0, p->Z, p->Z); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_sub(r->Z, t0, r->T); + fe_add(r->T, t0, r->T); +} + + +/* +r = p +*/ + +void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) { + fe_mul(r->X, p->X, p->T); + fe_mul(r->Y, p->Y, p->Z); + fe_mul(r->Z, p->Z, p->T); +} + + + +/* +r = p +*/ + +void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) { + fe_mul(r->X, p->X, p->T); + fe_mul(r->Y, p->Y, p->Z); + fe_mul(r->Z, p->Z, p->T); + fe_mul(r->T, p->X, p->Y); +} + + +void ge_p2_0(ge_p2 *h) { + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); +} + + + +/* +r = 2 * p +*/ + +void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) { + fe t0; + + fe_sq(r->X, p->X); + fe_sq(r->Z, p->Y); + fe_sq2(r->T, p->Z); + fe_add(r->Y, p->X, p->Y); + fe_sq(t0, r->Y); + fe_add(r->Y, r->Z, r->X); + fe_sub(r->Z, r->Z, r->X); + fe_sub(r->X, t0, r->Y); + fe_sub(r->T, r->T, r->Z); +} + + +void ge_p3_0(ge_p3 *h) { + fe_0(h->X); + fe_1(h->Y); + fe_1(h->Z); + fe_0(h->T); +} + + +/* +r = 2 * p +*/ + +void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) { + ge_p2 q; + ge_p3_to_p2(&q, p); + ge_p2_dbl(r, &q); +} + + + +/* +r = p +*/ + +static const fe d2 = { + -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199 +}; + +void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) { + fe_add(r->YplusX, p->Y, p->X); + fe_sub(r->YminusX, p->Y, p->X); + fe_copy(r->Z, p->Z); + fe_mul(r->T2d, p->T, d2); +} + + +/* +r = p +*/ + +void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) { + fe_copy(r->X, p->X); + fe_copy(r->Y, p->Y); + fe_copy(r->Z, p->Z); +} + + +void ge_p3_tobytes(unsigned char *s, const ge_p3 *h) { + fe recip; + fe x; + fe y; + fe_invert(recip, h->Z); + fe_mul(x, h->X, recip); + fe_mul(y, h->Y, recip); + fe_tobytes(s, y); + s[31] ^= fe_isnegative(x) << 7; +} + + +static unsigned char equal(signed char b, signed char c) { + unsigned char ub = b; + unsigned char uc = c; + unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ + uint64_t y = x; /* 0: yes; 1..255: no */ + y -= 1; /* large: yes; 0..254: no */ + y >>= 63; /* 1: yes; 0: no */ + return (unsigned char) y; +} + +static unsigned char negative(signed char b) { + uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ + x >>= 63; /* 1: yes; 0: no */ + return (unsigned char) x; +} + +static void cmov(ge_precomp *t, const ge_precomp *u, unsigned char b) { + fe_cmov(t->yplusx, u->yplusx, b); + fe_cmov(t->yminusx, u->yminusx, b); + fe_cmov(t->xy2d, u->xy2d, b); +} + + +static void select(ge_precomp *t, int pos, signed char b) { + ge_precomp minust; + unsigned char bnegative = negative(b); + unsigned char babs = b - (((-bnegative) & b) << 1); + fe_1(t->yplusx); + fe_1(t->yminusx); + fe_0(t->xy2d); + cmov(t, &base[pos][0], equal(babs, 1)); + cmov(t, &base[pos][1], equal(babs, 2)); + cmov(t, &base[pos][2], equal(babs, 3)); + cmov(t, &base[pos][3], equal(babs, 4)); + cmov(t, &base[pos][4], equal(babs, 5)); + cmov(t, &base[pos][5], equal(babs, 6)); + cmov(t, &base[pos][6], equal(babs, 7)); + cmov(t, &base[pos][7], equal(babs, 8)); + fe_copy(minust.yplusx, t->yminusx); + fe_copy(minust.yminusx, t->yplusx); + fe_neg(minust.xy2d, t->xy2d); + cmov(t, &minust, bnegative); +} + +/* +h = a * B +where a = a[0]+256*a[1]+...+256^31 a[31] +B is the Ed25519 base point (x,4/5) with x positive. + +Preconditions: + a[31] <= 127 +*/ + +void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { + signed char e[64]; + signed char carry; + ge_p1p1 r; + ge_p2 s; + ge_precomp t; + int i; + + for (i = 0; i < 32; ++i) { + e[2 * i + 0] = (a[i] >> 0) & 15; + e[2 * i + 1] = (a[i] >> 4) & 15; + } + + /* each e[i] is between 0 and 15 */ + /* e[63] is between 0 and 7 */ + carry = 0; + + for (i = 0; i < 63; ++i) { + e[i] += carry; + carry = e[i] + 8; + carry >>= 4; + e[i] -= carry << 4; + } + + e[63] += carry; + /* each e[i] is between -8 and 8 */ + ge_p3_0(h); + + for (i = 1; i < 64; i += 2) { + select(&t, i / 2, e[i]); + ge_madd(&r, h, &t); + ge_p1p1_to_p3(h, &r); + } + + ge_p3_dbl(&r, h); + ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); + ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); + ge_p1p1_to_p2(&s, &r); + ge_p2_dbl(&r, &s); + ge_p1p1_to_p3(h, &r); + + for (i = 0; i < 64; i += 2) { + select(&t, i / 2, e[i]); + ge_madd(&r, h, &t); + ge_p1p1_to_p3(h, &r); + } +} + + +/* +r = p - q +*/ + +void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { + fe t0; + + fe_add(r->X, p->Y, p->X); + fe_sub(r->Y, p->Y, p->X); + fe_mul(r->Z, r->X, q->YminusX); + fe_mul(r->Y, r->Y, q->YplusX); + fe_mul(r->T, q->T2d, p->T); + fe_mul(r->X, p->Z, q->Z); + fe_add(t0, r->X, r->X); + fe_sub(r->X, r->Z, r->Y); + fe_add(r->Y, r->Z, r->Y); + fe_sub(r->Z, t0, r->T); + fe_add(r->T, t0, r->T); +} + + +void ge_tobytes(unsigned char *s, const ge_p2 *h) { + fe recip; + fe x; + fe y; + fe_invert(recip, h->Z); + fe_mul(x, h->X, recip); + fe_mul(y, h->Y, recip); + fe_tobytes(s, y); + s[31] ^= fe_isnegative(x) << 7; +} diff --git a/src/ge.h b/src/ge.h new file mode 100644 index 000000000..17fde2df1 --- /dev/null +++ b/src/ge.h @@ -0,0 +1,74 @@ +#ifndef GE_H +#define GE_H + +#include "fe.h" + + +/* +ge means group element. + +Here the group is the set of pairs (x,y) of field elements (see fe.h) +satisfying -x^2 + y^2 = 1 + d x^2y^2 +where d = -121665/121666. + +Representations: + ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z + ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT + ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T + ge_precomp (Duif): (y+x,y-x,2dxy) +*/ + +typedef struct { + fe X; + fe Y; + fe Z; +} ge_p2; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p3; + +typedef struct { + fe X; + fe Y; + fe Z; + fe T; +} ge_p1p1; + +typedef struct { + fe yplusx; + fe yminusx; + fe xy2d; +} ge_precomp; + +typedef struct { + fe YplusX; + fe YminusX; + fe Z; + fe T2d; +} ge_cached; + +void ge_p3_tobytes(unsigned char *s, const ge_p3 *h); +void ge_tobytes(unsigned char *s, const ge_p2 *h); +int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s); + +void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); +void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q); +void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b); +void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); +void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q); +void ge_scalarmult_base(ge_p3 *h, const unsigned char *a); + +void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p); +void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p); +void ge_p2_0(ge_p2 *h); +void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p); +void ge_p3_0(ge_p3 *h); +void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p); +void ge_p3_to_cached(ge_cached *r, const ge_p3 *p); +void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p); + +#endif diff --git a/src/key_exchange.c b/src/key_exchange.c new file mode 100644 index 000000000..abd75da2c --- /dev/null +++ b/src/key_exchange.c @@ -0,0 +1,79 @@ +#include "ed25519.h" +#include "fe.h" + +void ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key) { + unsigned char e[32]; + unsigned int i; + + fe x1; + fe x2; + fe z2; + fe x3; + fe z3; + fe tmp0; + fe tmp1; + + int pos; + unsigned int swap; + unsigned int b; + + /* copy the private key and make sure it's valid */ + for (i = 0; i < 32; ++i) { + e[i] = private_key[i]; + } + + e[0] &= 248; + e[31] &= 63; + e[31] |= 64; + + /* unpack the public key and convert edwards to montgomery */ + /* due to CodesInChaos: montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p */ + fe_frombytes(x1, public_key); + fe_1(tmp1); + fe_add(tmp0, x1, tmp1); + fe_sub(tmp1, tmp1, x1); + fe_invert(tmp1, tmp1); + fe_mul(x1, tmp0, tmp1); + + fe_1(x2); + fe_0(z2); + fe_copy(x3, x1); + fe_1(z3); + + swap = 0; + for (pos = 254; pos >= 0; --pos) { + b = e[pos / 8] >> (pos & 7); + b &= 1; + swap ^= b; + fe_cswap(x2, x3, swap); + fe_cswap(z2, z3, swap); + swap = b; + + /* from montgomery.h */ + fe_sub(tmp0, x3, z3); + fe_sub(tmp1, x2, z2); + fe_add(x2, x2, z2); + fe_add(z2, x3, z3); + fe_mul(z3, tmp0, x2); + fe_mul(z2, z2, tmp1); + fe_sq(tmp0, tmp1); + fe_sq(tmp1, x2); + fe_add(x3, z3, z2); + fe_sub(z2, z3, z2); + fe_mul(x2, tmp1, tmp0); + fe_sub(tmp1, tmp1, tmp0); + fe_sq(z2, z2); + fe_mul121666(z3, tmp1); + fe_sq(x3, x3); + fe_add(tmp0, tmp0, z3); + fe_mul(z3, x1, z2); + fe_mul(z2, tmp1, tmp0); + } + + fe_cswap(x2, x3, swap); + fe_cswap(z2, z3, swap); + + fe_invert(z2, z2); + fe_mul(x2, x2, z2); + fe_tobytes(shared_secret, x2); +} diff --git a/src/keypair.c b/src/keypair.c new file mode 100644 index 000000000..dc1b8eccc --- /dev/null +++ b/src/keypair.c @@ -0,0 +1,16 @@ +#include "ed25519.h" +#include "sha512.h" +#include "ge.h" + + +void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed) { + ge_p3 A; + + sha512(seed, 32, private_key); + private_key[0] &= 248; + private_key[31] &= 63; + private_key[31] |= 64; + + ge_scalarmult_base(&A, private_key); + ge_p3_tobytes(public_key, &A); +} diff --git a/src/precomp_data.h b/src/precomp_data.h new file mode 100644 index 000000000..ff23986c3 --- /dev/null +++ b/src/precomp_data.h @@ -0,0 +1,1391 @@ +static const ge_precomp Bi[8] = { + { + { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, + { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, + { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 }, + }, + { + { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, + { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, + { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 }, + }, + { + { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, + { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, + { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 }, + }, + { + { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, + { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, + { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 }, + }, + { + { -22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877 }, + { -6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951 }, + { 4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784 }, + }, + { + { -25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436 }, + { 25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918 }, + { 23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877 }, + }, + { + { -33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800 }, + { -25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305 }, + { -13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300 }, + }, + { + { -3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876 }, + { -24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619 }, + { -3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683 }, + }, +}; + + +/* base[i][j] = (j+1)*256^i*B */ +static const ge_precomp base[32][8] = { + { + { + { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, + { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, + { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 }, + }, + { + { -12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303 }, + { -21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081 }, + { 26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697 }, + }, + { + { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, + { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, + { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 }, + }, + { + { -17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540 }, + { 23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397 }, + { 7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325 }, + }, + { + { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, + { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, + { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 }, + }, + { + { -15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777 }, + { -8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737 }, + { -18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652 }, + }, + { + { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, + { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, + { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 }, + }, + { + { 14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726 }, + { -7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955 }, + { 27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425 }, + }, + }, + { + { + { -13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171 }, + { 27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510 }, + { 17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660 }, + }, + { + { -10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639 }, + { 29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963 }, + { 5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950 }, + }, + { + { -27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568 }, + { 12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335 }, + { 25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628 }, + }, + { + { -26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007 }, + { -2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772 }, + { -22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653 }, + }, + { + { 2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567 }, + { 13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686 }, + { 21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372 }, + }, + { + { -13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887 }, + { -23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954 }, + { -29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953 }, + }, + { + { 24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833 }, + { -16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532 }, + { -22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876 }, + }, + { + { 2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268 }, + { 33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214 }, + { 1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038 }, + }, + }, + { + { + { 6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800 }, + { 4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645 }, + { -4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664 }, + }, + { + { 1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933 }, + { -25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182 }, + { -17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222 }, + }, + { + { -18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991 }, + { 20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880 }, + { 9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092 }, + }, + { + { -16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295 }, + { 19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788 }, + { 8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553 }, + }, + { + { -15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026 }, + { 11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347 }, + { -18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033 }, + }, + { + { -23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395 }, + { -27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278 }, + { 1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890 }, + }, + { + { 32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995 }, + { -30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596 }, + { -11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891 }, + }, + { + { 31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060 }, + { 11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608 }, + { -20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606 }, + }, + }, + { + { + { 7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389 }, + { -19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016 }, + { -11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341 }, + }, + { + { -22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505 }, + { 14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553 }, + { -28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655 }, + }, + { + { 15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220 }, + { 12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631 }, + { -4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099 }, + }, + { + { 26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556 }, + { 14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749 }, + { 236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930 }, + }, + { + { 1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391 }, + { 5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253 }, + { 20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066 }, + }, + { + { 24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958 }, + { -11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082 }, + { -28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383 }, + }, + { + { -30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521 }, + { -11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807 }, + { 23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948 }, + }, + { + { 9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134 }, + { -32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455 }, + { 27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629 }, + }, + }, + { + { + { -8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069 }, + { -32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746 }, + { 24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919 }, + }, + { + { 11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837 }, + { 8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906 }, + { -28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771 }, + }, + { + { -25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817 }, + { 10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098 }, + { 10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409 }, + }, + { + { -12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504 }, + { -26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727 }, + { 28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420 }, + }, + { + { -32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003 }, + { -1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605 }, + { -30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384 }, + }, + { + { -26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701 }, + { -23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683 }, + { 29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708 }, + }, + { + { -3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563 }, + { -19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260 }, + { -5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387 }, + }, + { + { -19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672 }, + { 23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686 }, + { -24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665 }, + }, + }, + { + { + { 11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182 }, + { -31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277 }, + { 14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628 }, + }, + { + { -4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474 }, + { -26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539 }, + { -25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822 }, + }, + { + { -10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970 }, + { 19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756 }, + { -24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508 }, + }, + { + { -26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683 }, + { -10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655 }, + { -20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158 }, + }, + { + { -4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125 }, + { -15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839 }, + { -20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664 }, + }, + { + { 27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294 }, + { -18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899 }, + { -11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070 }, + }, + { + { 3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294 }, + { -15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949 }, + { -21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083 }, + }, + { + { 31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420 }, + { -5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940 }, + { 29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396 }, + }, + }, + { + { + { -12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567 }, + { 20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127 }, + { -16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294 }, + }, + { + { -12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887 }, + { 22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964 }, + { 16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195 }, + }, + { + { 9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244 }, + { 24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999 }, + { -1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762 }, + }, + { + { -18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274 }, + { -33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236 }, + { -16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605 }, + }, + { + { -13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761 }, + { -22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884 }, + { -6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482 }, + }, + { + { -24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638 }, + { -11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490 }, + { -32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170 }, + }, + { + { 5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736 }, + { 10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124 }, + { -17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392 }, + }, + { + { 8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029 }, + { 6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048 }, + { 28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958 }, + }, + }, + { + { + { 24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593 }, + { 26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071 }, + { -11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692 }, + }, + { + { 11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687 }, + { -160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441 }, + { -20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001 }, + }, + { + { -938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460 }, + { -19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007 }, + { -21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762 }, + }, + { + { 15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005 }, + { -9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674 }, + { 4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035 }, + }, + { + { 7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590 }, + { -2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957 }, + { -30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812 }, + }, + { + { 33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740 }, + { -18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122 }, + { -27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158 }, + }, + { + { 8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885 }, + { 26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140 }, + { 19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857 }, + }, + { + { 801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155 }, + { 19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260 }, + { 19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483 }, + }, + }, + { + { + { -3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677 }, + { 32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815 }, + { 22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751 }, + }, + { + { -16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203 }, + { -11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208 }, + { 1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230 }, + }, + { + { 16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850 }, + { -21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389 }, + { -9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968 }, + }, + { + { -11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689 }, + { 14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880 }, + { 5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304 }, + }, + { + { 30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632 }, + { -3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412 }, + { 20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566 }, + }, + { + { -20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038 }, + { -26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232 }, + { -1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943 }, + }, + { + { 17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856 }, + { 23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738 }, + { 15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971 }, + }, + { + { -27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718 }, + { -13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697 }, + { -11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883 }, + }, + }, + { + { + { 5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912 }, + { -26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358 }, + { 3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849 }, + }, + { + { 29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307 }, + { -14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977 }, + { -6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335 }, + }, + { + { -29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644 }, + { -22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616 }, + { -27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735 }, + }, + { + { -21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099 }, + { 29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341 }, + { -936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336 }, + }, + { + { -23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646 }, + { 31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425 }, + { -17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388 }, + }, + { + { -31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743 }, + { -16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822 }, + { -8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462 }, + }, + { + { 18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985 }, + { 9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702 }, + { -22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797 }, + }, + { + { 21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293 }, + { 27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100 }, + { 19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688 }, + }, + }, + { + { + { 12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186 }, + { 2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610 }, + { -2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707 }, + }, + { + { 7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220 }, + { 915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025 }, + { 32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044 }, + }, + { + { 32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992 }, + { -4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027 }, + { 21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197 }, + }, + { + { 8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901 }, + { 31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952 }, + { 19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878 }, + }, + { + { -28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390 }, + { 32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730 }, + { 2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730 }, + }, + { + { -19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180 }, + { -30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272 }, + { -15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715 }, + }, + { + { -22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970 }, + { -31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772 }, + { -17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865 }, + }, + { + { 15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750 }, + { 20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373 }, + { 32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348 }, + }, + }, + { + { + { 9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144 }, + { -22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195 }, + { 5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086 }, + }, + { + { -13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684 }, + { -8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518 }, + { -2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233 }, + }, + { + { -5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793 }, + { -2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794 }, + { 580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435 }, + }, + { + { 23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921 }, + { 13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518 }, + { 2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563 }, + }, + { + { 14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278 }, + { -27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024 }, + { 4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030 }, + }, + { + { 10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783 }, + { 27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717 }, + { 6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844 }, + }, + { + { 14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333 }, + { 16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048 }, + { 22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760 }, + }, + { + { -4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760 }, + { -15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757 }, + { -2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112 }, + }, + }, + { + { + { -19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468 }, + { 3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184 }, + { 10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289 }, + }, + { + { 15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066 }, + { 24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882 }, + { 13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226 }, + }, + { + { 16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101 }, + { 29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279 }, + { -6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811 }, + }, + { + { 27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709 }, + { 20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714 }, + { -2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121 }, + }, + { + { 9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464 }, + { 12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847 }, + { 13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400 }, + }, + { + { 4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414 }, + { -15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158 }, + { 17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045 }, + }, + { + { -461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415 }, + { -5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459 }, + { -31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079 }, + }, + { + { 21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412 }, + { -20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743 }, + { -14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836 }, + }, + }, + { + { + { 12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022 }, + { 18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429 }, + { -6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065 }, + }, + { + { 30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861 }, + { 10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000 }, + { -33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101 }, + }, + { + { 32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815 }, + { 29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642 }, + { 10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966 }, + }, + { + { 25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574 }, + { -21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742 }, + { -18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689 }, + }, + { + { 12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020 }, + { -10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772 }, + { 3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982 }, + }, + { + { -14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953 }, + { -16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218 }, + { -17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265 }, + }, + { + { 29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073 }, + { -3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325 }, + { -11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798 }, + }, + { + { -4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870 }, + { -7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863 }, + { -13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927 }, + }, + }, + { + { + { -2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267 }, + { -9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663 }, + { 22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862 }, + }, + { + { -25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673 }, + { 15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943 }, + { 15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020 }, + }, + { + { -4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238 }, + { 11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064 }, + { 14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795 }, + }, + { + { 15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052 }, + { -10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904 }, + { 29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531 }, + }, + { + { -13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979 }, + { -5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841 }, + { 10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431 }, + }, + { + { 10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324 }, + { -31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940 }, + { 10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320 }, + }, + { + { -15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184 }, + { 14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114 }, + { 30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878 }, + }, + { + { 12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784 }, + { -2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091 }, + { -16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585 }, + }, + }, + { + { + { -8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208 }, + { 10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864 }, + { 17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661 }, + }, + { + { 7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233 }, + { 26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212 }, + { -12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525 }, + }, + { + { -24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068 }, + { 9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397 }, + { -8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988 }, + }, + { + { 5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889 }, + { 32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038 }, + { 14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697 }, + }, + { + { 20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875 }, + { -25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905 }, + { -25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656 }, + }, + { + { 11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818 }, + { 27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714 }, + { 10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203 }, + }, + { + { 20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931 }, + { -30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024 }, + { -23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084 }, + }, + { + { -1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204 }, + { 20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817 }, + { 27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667 }, + }, + }, + { + { + { 11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504 }, + { -12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768 }, + { -19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255 }, + }, + { + { 6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790 }, + { 1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438 }, + { -22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333 }, + }, + { + { 17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971 }, + { 31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905 }, + { 29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409 }, + }, + { + { 12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409 }, + { 6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499 }, + { -8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363 }, + }, + { + { 28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664 }, + { -11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324 }, + { -21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940 }, + }, + { + { 13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990 }, + { -17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914 }, + { -25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290 }, + }, + { + { 24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257 }, + { -6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433 }, + { -16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236 }, + }, + { + { -12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045 }, + { 11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093 }, + { -1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347 }, + }, + }, + { + { + { -28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191 }, + { -15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507 }, + { -12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906 }, + }, + { + { 3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018 }, + { -16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109 }, + { -23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926 }, + }, + { + { -24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528 }, + { 8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625 }, + { -32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286 }, + }, + { + { 2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033 }, + { 27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866 }, + { 21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896 }, + }, + { + { 30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075 }, + { 26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347 }, + { -22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437 }, + }, + { + { -5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165 }, + { -18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588 }, + { -32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193 }, + }, + { + { -19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017 }, + { -28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883 }, + { 21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961 }, + }, + { + { 8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043 }, + { 29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663 }, + { -20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362 }, + }, + }, + { + { + { -33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860 }, + { 2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466 }, + { -24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063 }, + }, + { + { -26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997 }, + { -1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295 }, + { -13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369 }, + }, + { + { 9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385 }, + { 18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109 }, + { 2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906 }, + }, + { + { 4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424 }, + { -19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185 }, + { 7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962 }, + }, + { + { -7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325 }, + { 10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593 }, + { 696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404 }, + }, + { + { -11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644 }, + { 17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801 }, + { 26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804 }, + }, + { + { -31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884 }, + { -586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577 }, + { -9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849 }, + }, + { + { 32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473 }, + { -8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644 }, + { -2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319 }, + }, + }, + { + { + { -11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599 }, + { -9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768 }, + { -27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084 }, + }, + { + { -27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328 }, + { -15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369 }, + { 20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920 }, + }, + { + { 12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815 }, + { -32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025 }, + { -21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397 }, + }, + { + { -20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448 }, + { 6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981 }, + { 30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165 }, + }, + { + { 32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501 }, + { 17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073 }, + { -1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861 }, + }, + { + { 14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845 }, + { -1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211 }, + { 18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870 }, + }, + { + { 10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096 }, + { 33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803 }, + { -32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168 }, + }, + { + { 30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965 }, + { -14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505 }, + { 18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598 }, + }, + }, + { + { + { 5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782 }, + { 5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900 }, + { -31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479 }, + }, + { + { -12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208 }, + { 8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232 }, + { 17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719 }, + }, + { + { 16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271 }, + { -4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326 }, + { -8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132 }, + }, + { + { 14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300 }, + { 8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570 }, + { 15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670 }, + }, + { + { -2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994 }, + { -12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913 }, + { 31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317 }, + }, + { + { -25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730 }, + { 842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096 }, + { -4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078 }, + }, + { + { -15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411 }, + { -19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905 }, + { -9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654 }, + }, + { + { -28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870 }, + { -23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498 }, + { 12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579 }, + }, + }, + { + { + { 14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677 }, + { 10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647 }, + { -2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743 }, + }, + { + { -25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468 }, + { 21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375 }, + { -25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155 }, + }, + { + { 6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725 }, + { -12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612 }, + { -10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943 }, + }, + { + { -30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944 }, + { 30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928 }, + { 9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406 }, + }, + { + { 22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139 }, + { -8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963 }, + { -31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693 }, + }, + { + { 1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734 }, + { -448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680 }, + { -24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410 }, + }, + { + { -9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931 }, + { -16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654 }, + { 22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710 }, + }, + { + { 29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180 }, + { -26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684 }, + { -10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895 }, + }, + }, + { + { + { 22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501 }, + { -11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413 }, + { 6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880 }, + }, + { + { -8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874 }, + { 22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962 }, + { -7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899 }, + }, + { + { 21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152 }, + { 9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063 }, + { 7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080 }, + }, + { + { -9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146 }, + { -17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183 }, + { -19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133 }, + }, + { + { -32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421 }, + { -3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622 }, + { -4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197 }, + }, + { + { 2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663 }, + { 31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753 }, + { 4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755 }, + }, + { + { -9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862 }, + { -26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118 }, + { 26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171 }, + }, + { + { 15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380 }, + { 16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824 }, + { 28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270 }, + }, + }, + { + { + { -817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438 }, + { -31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584 }, + { -594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562 }, + }, + { + { 30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471 }, + { 18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610 }, + { 19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269 }, + }, + { + { -30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650 }, + { 14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369 }, + { 19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461 }, + }, + { + { 30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462 }, + { -5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793 }, + { -2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218 }, + }, + { + { -24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226 }, + { 18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019 }, + { -15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037 }, + }, + { + { 31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171 }, + { -17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132 }, + { -28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841 }, + }, + { + { 21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181 }, + { -33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210 }, + { -1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040 }, + }, + { + { 3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935 }, + { 24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105 }, + { -28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814 }, + }, + }, + { + { + { 793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852 }, + { 5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581 }, + { -4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646 }, + }, + { + { 10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844 }, + { 10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025 }, + { 27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453 }, + }, + { + { -23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068 }, + { 4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192 }, + { -17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921 }, + }, + { + { -9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259 }, + { -12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426 }, + { -5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072 }, + }, + { + { -17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305 }, + { 13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832 }, + { 28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943 }, + }, + { + { -16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011 }, + { 24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447 }, + { 17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494 }, + }, + { + { -28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245 }, + { -20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859 }, + { 28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915 }, + }, + { + { 16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707 }, + { 10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848 }, + { -11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224 }, + }, + }, + { + { + { -25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391 }, + { 15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215 }, + { -23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101 }, + }, + { + { 23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713 }, + { 21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849 }, + { -7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930 }, + }, + { + { -29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940 }, + { -21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031 }, + { -17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404 }, + }, + { + { -25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243 }, + { -23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116 }, + { -24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525 }, + }, + { + { -23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509 }, + { -10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883 }, + { 15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865 }, + }, + { + { -3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660 }, + { 4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273 }, + { -28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138 }, + }, + { + { -25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560 }, + { -10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135 }, + { 2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941 }, + }, + { + { -4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739 }, + { 18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756 }, + { -30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819 }, + }, + }, + { + { + { -6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347 }, + { -27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028 }, + { 21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075 }, + }, + { + { 16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799 }, + { -2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609 }, + { -25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817 }, + }, + { + { -23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989 }, + { -30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523 }, + { 4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278 }, + }, + { + { 31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045 }, + { 19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377 }, + { 24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480 }, + }, + { + { 17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016 }, + { 510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426 }, + { 18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525 }, + }, + { + { 13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396 }, + { 9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080 }, + { 12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892 }, + }, + { + { 15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275 }, + { 11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074 }, + { 20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140 }, + }, + { + { -16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717 }, + { -1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101 }, + { 24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127 }, + }, + }, + { + { + { -12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632 }, + { -26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415 }, + { -31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160 }, + }, + { + { 31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876 }, + { 22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625 }, + { -15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478 }, + }, + { + { 27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164 }, + { 26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595 }, + { -7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248 }, + }, + { + { -16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858 }, + { 15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193 }, + { 8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184 }, + }, + { + { -18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942 }, + { -1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635 }, + { 21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948 }, + }, + { + { 11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935 }, + { -25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415 }, + { -15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416 }, + }, + { + { -7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018 }, + { 4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778 }, + { 366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659 }, + }, + { + { -24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385 }, + { 18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503 }, + { 476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329 }, + }, + }, + { + { + { 20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056 }, + { -13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838 }, + { 24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948 }, + }, + { + { -3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691 }, + { -15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118 }, + { -23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517 }, + }, + { + { -20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269 }, + { -6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904 }, + { -23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589 }, + }, + { + { -28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193 }, + { -7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910 }, + { -30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930 }, + }, + { + { -7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667 }, + { 25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481 }, + { -9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876 }, + }, + { + { 22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640 }, + { -8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278 }, + { -21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112 }, + }, + { + { 26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272 }, + { 17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012 }, + { -10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221 }, + }, + { + { 30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046 }, + { 13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345 }, + { -19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310 }, + }, + }, + { + { + { 19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937 }, + { 31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636 }, + { -9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008 }, + }, + { + { -2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429 }, + { -15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576 }, + { 31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066 }, + }, + { + { -9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490 }, + { -12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104 }, + { 33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053 }, + }, + { + { 31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275 }, + { -20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511 }, + { 22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095 }, + }, + { + { -28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439 }, + { 23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939 }, + { -23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424 }, + }, + { + { 2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310 }, + { 3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608 }, + { -32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079 }, + }, + { + { -23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101 }, + { 21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418 }, + { 18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576 }, + }, + { + { 30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356 }, + { 9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996 }, + { -26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099 }, + }, + }, + { + { + { -26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728 }, + { -13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658 }, + { -10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242 }, + }, + { + { -21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001 }, + { -4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766 }, + { 18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373 }, + }, + { + { 26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458 }, + { -17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628 }, + { -13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657 }, + }, + { + { -23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062 }, + { 25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616 }, + { 31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014 }, + }, + { + { 24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383 }, + { -25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814 }, + { -20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718 }, + }, + { + { 30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417 }, + { 2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222 }, + { 33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444 }, + }, + { + { -20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597 }, + { 23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970 }, + { 1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799 }, + }, + { + { -5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647 }, + { 13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511 }, + { -29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032 }, + }, + }, + { + { + { 9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834 }, + { -23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461 }, + { 29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062 }, + }, + { + { -25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516 }, + { -20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547 }, + { -24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240 }, + }, + { + { -17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038 }, + { -33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741 }, + { 16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103 }, + }, + { + { -19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747 }, + { -1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323 }, + { 31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016 }, + }, + { + { -14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373 }, + { 15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228 }, + { -2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141 }, + }, + { + { 16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399 }, + { 11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831 }, + { -185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376 }, + }, + { + { -32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313 }, + { -18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958 }, + { -6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577 }, + }, + { + { -22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743 }, + { 29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684 }, + { -20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476 }, + }, + }, +}; diff --git a/src/sc.c b/src/sc.c new file mode 100644 index 000000000..ca5bad2ca --- /dev/null +++ b/src/sc.c @@ -0,0 +1,809 @@ +#include "fixedint.h" +#include "sc.h" + +static uint64_t load_3(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + + return result; +} + +static uint64_t load_4(const unsigned char *in) { + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + result |= ((uint64_t) in[3]) << 24; + + return result; +} + +/* +Input: + s[0]+256*s[1]+...+256^63*s[63] = s + +Output: + s[0]+256*s[1]+...+256^31*s[31] = s mod l + where l = 2^252 + 27742317777372353535851937790883648493. + Overwrites s in place. +*/ + +void sc_reduce(unsigned char *s) { + int64_t s0 = 2097151 & load_3(s); + int64_t s1 = 2097151 & (load_4(s + 2) >> 5); + int64_t s2 = 2097151 & (load_3(s + 5) >> 2); + int64_t s3 = 2097151 & (load_4(s + 7) >> 7); + int64_t s4 = 2097151 & (load_4(s + 10) >> 4); + int64_t s5 = 2097151 & (load_3(s + 13) >> 1); + int64_t s6 = 2097151 & (load_4(s + 15) >> 6); + int64_t s7 = 2097151 & (load_3(s + 18) >> 3); + int64_t s8 = 2097151 & load_3(s + 21); + int64_t s9 = 2097151 & (load_4(s + 23) >> 5); + int64_t s10 = 2097151 & (load_3(s + 26) >> 2); + int64_t s11 = 2097151 & (load_4(s + 28) >> 7); + int64_t s12 = 2097151 & (load_4(s + 31) >> 4); + int64_t s13 = 2097151 & (load_3(s + 34) >> 1); + int64_t s14 = 2097151 & (load_4(s + 36) >> 6); + int64_t s15 = 2097151 & (load_3(s + 39) >> 3); + int64_t s16 = 2097151 & load_3(s + 42); + int64_t s17 = 2097151 & (load_4(s + 44) >> 5); + int64_t s18 = 2097151 & (load_3(s + 47) >> 2); + int64_t s19 = 2097151 & (load_4(s + 49) >> 7); + int64_t s20 = 2097151 & (load_4(s + 52) >> 4); + int64_t s21 = 2097151 & (load_3(s + 55) >> 1); + int64_t s22 = 2097151 & (load_4(s + 57) >> 6); + int64_t s23 = (load_4(s + 60) >> 3); + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; + s13 += carry12; + s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; + s15 += carry14; + s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; + s17 += carry16; + s16 -= carry16 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; + s14 += carry13; + s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; + s16 += carry15; + s15 -= carry15 << 21; + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = (s0 + (1 << 20)) >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry1 = (s1 + (1 << 20)) >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry11 = s11 >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + + s[0] = (unsigned char) (s0 >> 0); + s[1] = (unsigned char) (s0 >> 8); + s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + s[3] = (unsigned char) (s1 >> 3); + s[4] = (unsigned char) (s1 >> 11); + s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + s[6] = (unsigned char) (s2 >> 6); + s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + s[8] = (unsigned char) (s3 >> 1); + s[9] = (unsigned char) (s3 >> 9); + s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + s[11] = (unsigned char) (s4 >> 4); + s[12] = (unsigned char) (s4 >> 12); + s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + s[14] = (unsigned char) (s5 >> 7); + s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + s[16] = (unsigned char) (s6 >> 2); + s[17] = (unsigned char) (s6 >> 10); + s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + s[19] = (unsigned char) (s7 >> 5); + s[20] = (unsigned char) (s7 >> 13); + s[21] = (unsigned char) (s8 >> 0); + s[22] = (unsigned char) (s8 >> 8); + s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + s[24] = (unsigned char) (s9 >> 3); + s[25] = (unsigned char) (s9 >> 11); + s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + s[27] = (unsigned char) (s10 >> 6); + s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + s[29] = (unsigned char) (s11 >> 1); + s[30] = (unsigned char) (s11 >> 9); + s[31] = (unsigned char) (s11 >> 17); +} + + + +/* +Input: + a[0]+256*a[1]+...+256^31*a[31] = a + b[0]+256*b[1]+...+256^31*b[31] = b + c[0]+256*c[1]+...+256^31*c[31] = c + +Output: + s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l + where l = 2^252 + 27742317777372353535851937790883648493. +*/ + +void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) { + int64_t a0 = 2097151 & load_3(a); + int64_t a1 = 2097151 & (load_4(a + 2) >> 5); + int64_t a2 = 2097151 & (load_3(a + 5) >> 2); + int64_t a3 = 2097151 & (load_4(a + 7) >> 7); + int64_t a4 = 2097151 & (load_4(a + 10) >> 4); + int64_t a5 = 2097151 & (load_3(a + 13) >> 1); + int64_t a6 = 2097151 & (load_4(a + 15) >> 6); + int64_t a7 = 2097151 & (load_3(a + 18) >> 3); + int64_t a8 = 2097151 & load_3(a + 21); + int64_t a9 = 2097151 & (load_4(a + 23) >> 5); + int64_t a10 = 2097151 & (load_3(a + 26) >> 2); + int64_t a11 = (load_4(a + 28) >> 7); + int64_t b0 = 2097151 & load_3(b); + int64_t b1 = 2097151 & (load_4(b + 2) >> 5); + int64_t b2 = 2097151 & (load_3(b + 5) >> 2); + int64_t b3 = 2097151 & (load_4(b + 7) >> 7); + int64_t b4 = 2097151 & (load_4(b + 10) >> 4); + int64_t b5 = 2097151 & (load_3(b + 13) >> 1); + int64_t b6 = 2097151 & (load_4(b + 15) >> 6); + int64_t b7 = 2097151 & (load_3(b + 18) >> 3); + int64_t b8 = 2097151 & load_3(b + 21); + int64_t b9 = 2097151 & (load_4(b + 23) >> 5); + int64_t b10 = 2097151 & (load_3(b + 26) >> 2); + int64_t b11 = (load_4(b + 28) >> 7); + int64_t c0 = 2097151 & load_3(c); + int64_t c1 = 2097151 & (load_4(c + 2) >> 5); + int64_t c2 = 2097151 & (load_3(c + 5) >> 2); + int64_t c3 = 2097151 & (load_4(c + 7) >> 7); + int64_t c4 = 2097151 & (load_4(c + 10) >> 4); + int64_t c5 = 2097151 & (load_3(c + 13) >> 1); + int64_t c6 = 2097151 & (load_4(c + 15) >> 6); + int64_t c7 = 2097151 & (load_3(c + 18) >> 3); + int64_t c8 = 2097151 & load_3(c + 21); + int64_t c9 = 2097151 & (load_4(c + 23) >> 5); + int64_t c10 = 2097151 & (load_3(c + 26) >> 2); + int64_t c11 = (load_4(c + 28) >> 7); + int64_t s0; + int64_t s1; + int64_t s2; + int64_t s3; + int64_t s4; + int64_t s5; + int64_t s6; + int64_t s7; + int64_t s8; + int64_t s9; + int64_t s10; + int64_t s11; + int64_t s12; + int64_t s13; + int64_t s14; + int64_t s15; + int64_t s16; + int64_t s17; + int64_t s18; + int64_t s19; + int64_t s20; + int64_t s21; + int64_t s22; + int64_t s23; + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + int64_t carry17; + int64_t carry18; + int64_t carry19; + int64_t carry20; + int64_t carry21; + int64_t carry22; + + s0 = c0 + a0 * b0; + s1 = c1 + a0 * b1 + a1 * b0; + s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0; + s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; + s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0; + s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0; + s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0; + s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0; + s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1 + a8 * b0; + s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0; + s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0; + s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0; + s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1; + s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2; + s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3; + s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4; + s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5; + s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6; + s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7; + s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8; + s20 = a9 * b11 + a10 * b10 + a11 * b9; + s21 = a10 * b11 + a11 * b10; + s22 = a11 * b11; + s23 = 0; + carry0 = (s0 + (1 << 20)) >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; + s13 += carry12; + s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; + s15 += carry14; + s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; + s17 += carry16; + s16 -= carry16 << 21; + carry18 = (s18 + (1 << 20)) >> 21; + s19 += carry18; + s18 -= carry18 << 21; + carry20 = (s20 + (1 << 20)) >> 21; + s21 += carry20; + s20 -= carry20 << 21; + carry22 = (s22 + (1 << 20)) >> 21; + s23 += carry22; + s22 -= carry22 << 21; + carry1 = (s1 + (1 << 20)) >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; + s14 += carry13; + s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; + s16 += carry15; + s15 -= carry15 << 21; + carry17 = (s17 + (1 << 20)) >> 21; + s18 += carry17; + s17 -= carry17 << 21; + carry19 = (s19 + (1 << 20)) >> 21; + s20 += carry19; + s19 -= carry19 << 21; + carry21 = (s21 + (1 << 20)) >> 21; + s22 += carry21; + s21 -= carry21 << 21; + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; + s13 += carry12; + s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; + s15 += carry14; + s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; + s17 += carry16; + s16 -= carry16 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; + s14 += carry13; + s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; + s16 += carry15; + s15 -= carry15 << 21; + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = (s0 + (1 << 20)) >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry1 = (s1 + (1 << 20)) >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + carry11 = s11 >> 21; + s12 += carry11; + s11 -= carry11 << 21; + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 << 21; + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 << 21; + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 << 21; + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 << 21; + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 << 21; + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 << 21; + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 << 21; + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 << 21; + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 << 21; + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 << 21; + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 << 21; + + s[0] = (unsigned char) (s0 >> 0); + s[1] = (unsigned char) (s0 >> 8); + s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + s[3] = (unsigned char) (s1 >> 3); + s[4] = (unsigned char) (s1 >> 11); + s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + s[6] = (unsigned char) (s2 >> 6); + s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + s[8] = (unsigned char) (s3 >> 1); + s[9] = (unsigned char) (s3 >> 9); + s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + s[11] = (unsigned char) (s4 >> 4); + s[12] = (unsigned char) (s4 >> 12); + s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + s[14] = (unsigned char) (s5 >> 7); + s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + s[16] = (unsigned char) (s6 >> 2); + s[17] = (unsigned char) (s6 >> 10); + s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + s[19] = (unsigned char) (s7 >> 5); + s[20] = (unsigned char) (s7 >> 13); + s[21] = (unsigned char) (s8 >> 0); + s[22] = (unsigned char) (s8 >> 8); + s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + s[24] = (unsigned char) (s9 >> 3); + s[25] = (unsigned char) (s9 >> 11); + s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + s[27] = (unsigned char) (s10 >> 6); + s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + s[29] = (unsigned char) (s11 >> 1); + s[30] = (unsigned char) (s11 >> 9); + s[31] = (unsigned char) (s11 >> 17); +} diff --git a/src/sc.h b/src/sc.h new file mode 100644 index 000000000..e29e7fa5a --- /dev/null +++ b/src/sc.h @@ -0,0 +1,12 @@ +#ifndef SC_H +#define SC_H + +/* +The set of scalars is \Z/l +where l = 2^252 + 27742317777372353535851937790883648493. +*/ + +void sc_reduce(unsigned char *s); +void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c); + +#endif diff --git a/src/seed.c b/src/seed.c new file mode 100644 index 000000000..11a2e3ec4 --- /dev/null +++ b/src/seed.c @@ -0,0 +1,40 @@ +#include "ed25519.h" + +#ifndef ED25519_NO_SEED + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +int ed25519_create_seed(unsigned char *seed) { +#ifdef _WIN32 + HCRYPTPROV prov; + + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + return 1; + } + + if (!CryptGenRandom(prov, 32, seed)) { + CryptReleaseContext(prov, 0); + return 1; + } + + CryptReleaseContext(prov, 0); +#else + FILE *f = fopen("/dev/urandom", "rb"); + + if (f == NULL) { + return 1; + } + + fread(seed, 1, 32, f); + fclose(f); +#endif + + return 0; +} + +#endif diff --git a/src/sha512.c b/src/sha512.c new file mode 100644 index 000000000..cb8ae7175 --- /dev/null +++ b/src/sha512.c @@ -0,0 +1,275 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#include "fixedint.h" +#include "sha512.h" + +/* the K array */ +static const uint64_t K[80] = { + UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), + UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), + UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), + UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), + UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), + UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), + UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), + UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), + UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), + UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), + UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), + UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), + UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), + UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), + UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), + UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), + UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), + UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), + UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), + UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), + UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), + UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), + UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), + UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), + UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), + UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), + UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), + UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), + UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), + UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), + UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), + UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), + UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), + UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), + UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), + UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), + UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), + UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), + UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), + UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) +}; + +/* Various logical functions */ + +#define ROR64c(x, y) \ + ( ((((x)&UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)(y)&UINT64_C(63))) | \ + ((x)<<((uint64_t)(64-((y)&UINT64_C(63)))))) & UINT64_C(0xFFFFFFFFFFFFFFFF)) + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y) \ + { x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \ + (((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \ + (((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \ + (((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); } + + +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR64c(x, n) +#define R(x, n) (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)n)) +#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) +#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) +#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) +#ifndef MIN + #define MIN(x, y) ( ((x)<(y))?(x):(y) ) +#endif + +/* compress 1024-bits */ +static int sha512_compress(sha512_context *md, unsigned char *buf) +{ + uint64_t S[8], W[80], t0, t1; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + + /* copy the state into 1024-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD64H(W[i], buf + (8*i)); + } + + /* fill W[16..79] */ + for (i = 16; i < 80; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + +/* Compress */ + #define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c);\ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 80; i += 8) { + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); + } + + #undef RND + + + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + + return 0; +} + + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return 0 if successful +*/ +int sha512_init(sha512_context * md) { + if (md == NULL) return 1; + + md->curlen = 0; + md->length = 0; + md->state[0] = UINT64_C(0x6a09e667f3bcc908); + md->state[1] = UINT64_C(0xbb67ae8584caa73b); + md->state[2] = UINT64_C(0x3c6ef372fe94f82b); + md->state[3] = UINT64_C(0xa54ff53a5f1d36f1); + md->state[4] = UINT64_C(0x510e527fade682d1); + md->state[5] = UINT64_C(0x9b05688c2b3e6c1f); + md->state[6] = UINT64_C(0x1f83d9abfb41bd6b); + md->state[7] = UINT64_C(0x5be0cd19137e2179); + + return 0; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return 0 if successful +*/ +int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen) +{ + size_t n; + size_t i; + int err; + if (md == NULL) return 1; + if (in == NULL) return 1; + if (md->curlen > sizeof(md->buf)) { + return 1; + } + while (inlen > 0) { + if (md->curlen == 0 && inlen >= 128) { + if ((err = sha512_compress (md, (unsigned char *)in)) != 0) { + return err; + } + md->length += 128 * 8; + in += 128; + inlen -= 128; + } else { + n = MIN(inlen, (128 - md->curlen)); + + for (i = 0; i < n; i++) { + md->buf[i + md->curlen] = in[i]; + } + + + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == 128) { + if ((err = sha512_compress (md, md->buf)) != 0) { + return err; + } + md->length += 8*128; + md->curlen = 0; + } + } + } + return 0; +} + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (64 bytes) + @return 0 if successful +*/ + int sha512_final(sha512_context * md, unsigned char *out) + { + int i; + + if (md == NULL) return 1; + if (out == NULL) return 1; + + if (md->curlen >= sizeof(md->buf)) { + return 1; + } + + /* increase the length of the message */ + md->length += md->curlen * UINT64_C(8); + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char)0x80; + + /* if the length is currently above 112 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 112) { + while (md->curlen < 128) { + md->buf[md->curlen++] = (unsigned char)0; + } + sha512_compress(md, md->buf); + md->curlen = 0; + } + + /* pad upto 120 bytes of zeroes + * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash + * > 2^64 bits of data... :-) + */ +while (md->curlen < 120) { + md->buf[md->curlen++] = (unsigned char)0; +} + + /* store length */ +STORE64H(md->length, md->buf+120); +sha512_compress(md, md->buf); + + /* copy output */ +for (i = 0; i < 8; i++) { + STORE64H(md->state[i], out+(8*i)); +} + +return 0; +} + +int sha512(const unsigned char *message, size_t message_len, unsigned char *out) +{ + sha512_context ctx; + int ret; + if ((ret = sha512_init(&ctx))) return ret; + if ((ret = sha512_update(&ctx, message, message_len))) return ret; + if ((ret = sha512_final(&ctx, out))) return ret; + return 0; +} diff --git a/src/sha512.h b/src/sha512.h new file mode 100644 index 000000000..a34dd5e42 --- /dev/null +++ b/src/sha512.h @@ -0,0 +1,21 @@ +#ifndef SHA512_H +#define SHA512_H + +#include + +#include "fixedint.h" + +/* state */ +typedef struct sha512_context_ { + uint64_t length, state[8]; + size_t curlen; + unsigned char buf[128]; +} sha512_context; + + +int sha512_init(sha512_context * md); +int sha512_final(sha512_context * md, unsigned char *out); +int sha512_update(sha512_context * md, const unsigned char *in, size_t inlen); +int sha512(const unsigned char *message, size_t message_len, unsigned char *out); + +#endif diff --git a/src/sign.c b/src/sign.c new file mode 100644 index 000000000..199a8393b --- /dev/null +++ b/src/sign.c @@ -0,0 +1,31 @@ +#include "ed25519.h" +#include "sha512.h" +#include "ge.h" +#include "sc.h" + + +void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key) { + sha512_context hash; + unsigned char hram[64]; + unsigned char r[64]; + ge_p3 R; + + + sha512_init(&hash); + sha512_update(&hash, private_key + 32, 32); + sha512_update(&hash, message, message_len); + sha512_final(&hash, r); + + sc_reduce(r); + ge_scalarmult_base(&R, r); + ge_p3_tobytes(signature, &R); + + sha512_init(&hash); + sha512_update(&hash, signature, 32); + sha512_update(&hash, public_key, 32); + sha512_update(&hash, message, message_len); + sha512_final(&hash, hram); + + sc_reduce(hram); + sc_muladd(signature + 32, hram, private_key, r); +} diff --git a/src/verify.c b/src/verify.c new file mode 100644 index 000000000..32f988edc --- /dev/null +++ b/src/verify.c @@ -0,0 +1,77 @@ +#include "ed25519.h" +#include "sha512.h" +#include "ge.h" +#include "sc.h" + +static int consttime_equal(const unsigned char *x, const unsigned char *y) { + unsigned char r = 0; + + r = x[0] ^ y[0]; + #define F(i) r |= x[i] ^ y[i] + F(1); + F(2); + F(3); + F(4); + F(5); + F(6); + F(7); + F(8); + F(9); + F(10); + F(11); + F(12); + F(13); + F(14); + F(15); + F(16); + F(17); + F(18); + F(19); + F(20); + F(21); + F(22); + F(23); + F(24); + F(25); + F(26); + F(27); + F(28); + F(29); + F(30); + F(31); + #undef F + + return !r; +} + +int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key) { + unsigned char h[64]; + unsigned char checker[32]; + sha512_context hash; + ge_p3 A; + ge_p2 R; + + if (signature[63] & 224) { + return 0; + } + + if (ge_frombytes_negate_vartime(&A, public_key) != 0) { + return 0; + } + + sha512_init(&hash); + sha512_update(&hash, signature, 32); + sha512_update(&hash, public_key, 32); + sha512_update(&hash, message, message_len); + sha512_final(&hash, h); + + sc_reduce(h); + ge_double_scalarmult_vartime(&R, h, &A, signature + 32); + ge_tobytes(checker, &R); + + if (!consttime_equal(checker, signature)) { + return 0; + } + + return 1; +} diff --git a/test.c b/test.c new file mode 100644 index 000000000..e2159a9af --- /dev/null +++ b/test.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include + +/* #define ED25519_DLL */ +#include "src/ed25519.h" + +#include "src/ge.h" +#include "src/sc.h" + + +int main() { + unsigned char public_key[32], private_key[64], seed[32], scalar[32]; + unsigned char other_public_key[32], other_private_key[64]; + unsigned char shared_secret[32], other_shared_secret[32]; + unsigned char signature[64]; + + clock_t start; + clock_t end; + int i; + + const unsigned char message[] = "Hello, world!"; + const int message_len = strlen((char*) message); + + /* create a random seed, and a keypair out of that seed */ + ed25519_create_seed(seed); + ed25519_create_keypair(public_key, private_key, seed); + + /* create signature on the message with the keypair */ + ed25519_sign(signature, message, message_len, public_key, private_key); + + /* verify the signature */ + if (ed25519_verify(signature, message, message_len, public_key)) { + printf("valid signature\n"); + } else { + printf("invalid signature\n"); + } + + /* create scalar and add it to the keypair */ + ed25519_create_seed(scalar); + ed25519_add_scalar(public_key, private_key, scalar); + + /* create signature with the new keypair */ + ed25519_sign(signature, message, message_len, public_key, private_key); + + /* verify the signature with the new keypair */ + if (ed25519_verify(signature, message, message_len, public_key)) { + printf("valid signature\n"); + } else { + printf("invalid signature\n"); + } + + /* make a slight adjustment and verify again */ + signature[44] ^= 0x10; + if (ed25519_verify(signature, message, message_len, public_key)) { + printf("did not detect signature change\n"); + } else { + printf("correctly detected signature change\n"); + } + + /* generate two keypairs for testing key exchange */ + ed25519_create_seed(seed); + ed25519_create_keypair(public_key, private_key, seed); + ed25519_create_seed(seed); + ed25519_create_keypair(other_public_key, other_private_key, seed); + + /* create two shared secrets - from both perspectives - and check if they're equal */ + ed25519_key_exchange(shared_secret, other_public_key, private_key); + ed25519_key_exchange(other_shared_secret, public_key, other_private_key); + + for (i = 0; i < 32; ++i) { + if (shared_secret[i] != other_shared_secret[i]) { + printf("key exchange was incorrect\n"); + break; + } + } + + if (i == 32) { + printf("key exchange was correct\n"); + } + + /* test performance */ + printf("testing seed generation performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_create_seed(seed); + } + end = clock(); + + printf("%fus per seed\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + + printf("testing key generation performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_create_keypair(public_key, private_key, seed); + } + end = clock(); + + printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing sign performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_sign(signature, message, message_len, public_key, private_key); + } + end = clock(); + + printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing verify performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_verify(signature, message, message_len, public_key); + } + end = clock(); + + printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + + printf("testing keypair scalar addition performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_add_scalar(public_key, private_key, scalar); + } + end = clock(); + + printf("%fus per keypair\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing public key scalar addition performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_add_scalar(public_key, NULL, scalar); + } + end = clock(); + + printf("%fus per key\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing key exchange performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_key_exchange(shared_secret, other_public_key, private_key); + } + end = clock(); + + printf("%fus per shared secret\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + return 0; +} From 5fbd0b2a90f7adb60c98b9187ccc794928daea87 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Sat, 3 Oct 2020 15:15:14 -0700 Subject: [PATCH 51/75] mid-autoconfiscation of urcrypt --- nix/deps/default.nix | 1 + nix/deps/libaes_siv/builder.sh | 10 ++++++ nix/deps/libaes_siv/default.nix | 13 ++++++++ nix/pkgs/urcrypt/default.nix | 4 +-- nix/pkgs/urcrypt/shell.nix | 11 +++++++ pkg/urcrypt/.gitignore | 6 ++++ pkg/urcrypt/Makefile.am | 22 +++++++++++++ pkg/urcrypt/{Makefile => Makefile.old} | 0 pkg/urcrypt/build-aux/m4/.gitkeep | 0 pkg/urcrypt/configure.ac | 45 ++++++++++++++++++++++++++ pkg/urcrypt/shell.nix | 1 + pkg/urcrypt/urcrypt.c | 4 +-- 12 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 nix/deps/libaes_siv/builder.sh create mode 100644 nix/deps/libaes_siv/default.nix create mode 100644 nix/pkgs/urcrypt/shell.nix create mode 100644 pkg/urcrypt/.gitignore create mode 100644 pkg/urcrypt/Makefile.am rename pkg/urcrypt/{Makefile => Makefile.old} (100%) create mode 100644 pkg/urcrypt/build-aux/m4/.gitkeep create mode 100644 pkg/urcrypt/configure.ac create mode 100644 pkg/urcrypt/shell.nix diff --git a/nix/deps/default.nix b/nix/deps/default.nix index 8f44e7501..6a07a8b79 100644 --- a/nix/deps/default.nix +++ b/nix/deps/default.nix @@ -4,6 +4,7 @@ rec { argon2 = import ./argon2 { inherit pkgs; }; murmur3 = import ./murmur3 { inherit pkgs; }; uv = import ./uv { inherit pkgs; }; + libaes_siv = import ./libaes_siv { inherit pkgs; }; ed25519 = import ./ed25519 { inherit pkgs; }; scrypt = import ./scrypt { inherit pkgs; }; softfloat3 = import ./softfloat3 { inherit pkgs; }; diff --git a/nix/deps/libaes_siv/builder.sh b/nix/deps/libaes_siv/builder.sh new file mode 100644 index 000000000..4fc6381a4 --- /dev/null +++ b/nix/deps/libaes_siv/builder.sh @@ -0,0 +1,10 @@ +source $stdenv/setup + +cp -r $src ./src +chmod -R u+w ./src +cd ./src + +cmake -DCMAKE_INSTALL_PREFIX=$out . && \ +make && \ +make test && \ +make install diff --git a/nix/deps/libaes_siv/default.nix b/nix/deps/libaes_siv/default.nix new file mode 100644 index 000000000..576bd64e4 --- /dev/null +++ b/nix/deps/libaes_siv/default.nix @@ -0,0 +1,13 @@ +{ pkgs }: + +pkgs.stdenv.mkDerivation rec { + name = "libaes_siv-50955"; + buildInputs = [ pkgs.cmake pkgs.openssl ]; + builder = ./builder.sh; + src = pkgs.fetchFromGitHub { + owner = "frodwith"; + repo = "libaes_siv"; + rev = "509550e92a416172b9b8255e275f3a04d5fd4545"; + sha256 = "11clbvasyyc7ml2x9g5z3il6hs9gzsa10fcnj4grmijzm7gkb3qq"; + }; +} diff --git a/nix/pkgs/urcrypt/default.nix b/nix/pkgs/urcrypt/default.nix index f2e390274..73aac0e56 100644 --- a/nix/pkgs/urcrypt/default.nix +++ b/nix/pkgs/urcrypt/default.nix @@ -1,4 +1,4 @@ -{ stdenv, openssl, gmp, ed25519, secp256k1, argon2, libaes_siv }: +{ stdenv, openssl, gmp, secp256k1, argon2, scrypt, libaes_siv }: stdenv.mkDerivation rec { name = "urcrypt"; @@ -6,6 +6,6 @@ stdenv.mkDerivation rec { src = ../../../pkg/urcrypt; buildInputs = [ - openssl gmp ed25519 secp256k1 argon2 libaes_siv + openssl gmp secp256k1 argon2 scrypt libaes_siv ]; } diff --git a/nix/pkgs/urcrypt/shell.nix b/nix/pkgs/urcrypt/shell.nix new file mode 100644 index 000000000..00a40f405 --- /dev/null +++ b/nix/pkgs/urcrypt/shell.nix @@ -0,0 +1,11 @@ +let + pkgs = import ../../nixpkgs.nix; + deps = import ../../deps { inherit pkgs; }; +in + +import ./default.nix { + inherit (pkgs) + stdenv openssl gmp; + inherit (deps) + libaes_siv argon2 scrypt secp256k1; +} diff --git a/pkg/urcrypt/.gitignore b/pkg/urcrypt/.gitignore new file mode 100644 index 000000000..509a0c428 --- /dev/null +++ b/pkg/urcrypt/.gitignore @@ -0,0 +1,6 @@ +build-aux +Makefile.in +aclocal.m4 +config.h.* +configure +.deps diff --git a/pkg/urcrypt/Makefile.am b/pkg/urcrypt/Makefile.am new file mode 100644 index 000000000..a401e816f --- /dev/null +++ b/pkg/urcrypt/Makefile.am @@ -0,0 +1,22 @@ +ACLOCAL_AMFLAGS = -I build-aux/m4 + +lib_LTLIBRARIES = liburcrypt.la +include_HEADERS = urcrypt.h +noinst_HEADERS = $(ed25519_includes) $(ge_additions_includes) +liburcrypt_la_SOURCES = urcrypt.c $(ed25519_sources) $(ge_additions_sources) +liburcrypt_la_CPPFLAGS = -Ied25519/src -Ige-additions + +### ed25519 +ed25519_sources = ed25519/src/add_scalar.c ed25519/src/keypair.c \ + ed25519/src/sc.c ed25519/src/seed.c \ + ed25519/src/verify.c ed25519/src/ge.c \ + ed25519/src/key_exchange.c ed25519/src/sha512.c \ + ed25519/src/sign.c +ed25519_includes = ed25519/src/fixedint.h ed25519/src/ge.h \ + ed25519/src/sha512.h ed25519/src/ed25519.h \ + ed25519/src/fe.h ed25519/src/precomp_data.h \ + ed25519/src/sc.h + +### ge-additions +ge_additions_sources = ge-additions/ge-additions.c +ge_additions_includes = ge-additions/ge-additions.h diff --git a/pkg/urcrypt/Makefile b/pkg/urcrypt/Makefile.old similarity index 100% rename from pkg/urcrypt/Makefile rename to pkg/urcrypt/Makefile.old diff --git a/pkg/urcrypt/build-aux/m4/.gitkeep b/pkg/urcrypt/build-aux/m4/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/urcrypt/configure.ac b/pkg/urcrypt/configure.ac new file mode 100644 index 000000000..20b2c09ae --- /dev/null +++ b/pkg/urcrypt/configure.ac @@ -0,0 +1,45 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.69]) +AC_INIT([urcrypt], [1.0]) +AC_CONFIG_SRCDIR([urcrypt.c]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([build-aux/m4]) +AM_INIT_AUTOMAKE([foreign subdir-objects -Wall -Werror]) +AM_PROG_AR +LT_INIT +AC_PROG_CC +AC_CHECK_LIB([aes_siv], [AES_SIV_CTX_new]) + +## Checks for libraries. +## FIXME: Replace `main' with a function in `-laes_siv': +## FIXME: Replace `main' with a function in `-largon2': +#AC_CHECK_LIB([argon2], [main]) +## FIXME: Replace `main' with a function in `-led25519': +#AC_CHECK_LIB([ed25519], [main]) +## FIXME: Replace `main' with a function in `-lgmp': +#AC_CHECK_LIB([gmp], [main]) +## FIXME: Replace `main' with a function in `-lsecp256k1': +#AC_CHECK_LIB([secp256k1], [main]) +## FIXME: Replace `main' with a function in `-lssl': +#AC_CHECK_LIB([ssl], [main]) + +# Checks for header files. +AC_CHECK_HEADERS([limits.h stddef.h stdint.h stdlib.h string.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_CHECK_HEADER_STDBOOL +AC_TYPE_INT32_T +AC_TYPE_INT64_T +AC_TYPE_SIZE_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_UINT8_T + +# Checks for library functions. +AC_CHECK_FUNCS([memset]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/pkg/urcrypt/shell.nix b/pkg/urcrypt/shell.nix new file mode 100644 index 000000000..17cea416e --- /dev/null +++ b/pkg/urcrypt/shell.nix @@ -0,0 +1 @@ +import ../../nix/pkgs/urcrypt/shell.nix diff --git a/pkg/urcrypt/urcrypt.c b/pkg/urcrypt/urcrypt.c index bae6239cc..5238c4c83 100644 --- a/pkg/urcrypt/urcrypt.c +++ b/pkg/urcrypt/urcrypt.c @@ -1,8 +1,8 @@ #include "urcrypt.h" -#include "ge-additions/ge-additions.h" - #include + #include +#include #include #include From aa02d49a228c62fe13299a3e2bd38b4f7316f927 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Sat, 3 Oct 2020 15:18:53 -0700 Subject: [PATCH 52/75] Squashed 'pkg/urcrypt/argon2/' content from commit 4da94a611 git-subtree-dir: pkg/urcrypt/argon2 git-subtree-split: 4da94a611ee62bad87ab2b131ffda3bcc0723d9c --- .gitattributes | 10 + .gitignore | 21 + .travis.yml | 25 + Argon2.sln | 160 + CHANGELOG.md | 32 + LICENSE | 314 + Makefile | 187 + README.md | 30 + appveyor.yml | 25 + argon2-specs.pdf | Bin 0 -> 459608 bytes export.sh | 7 + include/argon2.h | 475 + kats/argon2d | 12304 ++++++++++++++++ kats/argon2d.shasum | 1 + kats/argon2d_v16 | 12304 ++++++++++++++++ kats/argon2d_v16.shasum | 1 + kats/argon2i | 12304 ++++++++++++++++ kats/argon2i.shasum | 1 + kats/argon2i_v16 | 12304 ++++++++++++++++ kats/argon2i_v16.shasum | 1 + kats/argon2id | 12304 ++++++++++++++++ kats/argon2id.shasum | 1 + kats/argon2id_v16 | 12304 ++++++++++++++++ kats/argon2id_v16.shasum | 1 + kats/check-sums.ps1 | 42 + kats/check-sums.sh | 13 + kats/test.ps1 | 50 + kats/test.sh | 49 + latex/IEEEtran.cls | 6347 ++++++++ latex/Makefile | 18 + latex/argon2-specs.tex | 920 ++ latex/pics/argon2-par.pdf | Bin 0 -> 76165 bytes latex/pics/compression.pdf | Bin 0 -> 40828 bytes latex/pics/generic.pdf | Bin 0 -> 30736 bytes latex/pics/power-distribution.jpg | Bin 0 -> 25673 bytes latex/tradeoff.bib | 822 ++ libargon2.pc | 16 + man/argon2.1 | 60 + meson.build | 33 + src/argon2.c | 490 + src/bench.c | 111 + src/blake2/blake2-impl.h | 156 + src/blake2/blake2.h | 89 + src/blake2/blake2b.c | 390 + src/blake2/blamka-round-opt.h | 471 + src/blake2/blamka-round-ref.h | 56 + src/core.c | 670 + src/core.h | 228 + src/encoding.c | 493 + src/encoding.h | 57 + src/genkat.c | 209 + src/genkat.h | 51 + src/opt.c | 285 + src/ref.c | 196 + src/run.c | 341 + src/test.c | 253 + src/thread.c | 57 + src/thread.h | 67 + vs2015/Argon2Opt/Argon2Opt.vcxproj | 226 + vs2015/Argon2Opt/Argon2Opt.vcxproj.filters | 69 + vs2015/Argon2OptBench/Argon2OptBench.vcxproj | 226 + .../Argon2OptBench.vcxproj.filters | 69 + vs2015/Argon2OptDll/Argon2OptDll.vcxproj | 225 + .../Argon2OptDll/Argon2OptDll.vcxproj.filters | 66 + .../Argon2OptGenKAT/Argon2OptGenKAT.vcxproj | 239 + .../Argon2OptGenKAT.vcxproj.filters | 72 + .../Argon2OptTestCI/Argon2OptTestCI.vcxproj | 227 + .../Argon2OptTestCI.vcxproj.filters | 69 + vs2015/Argon2Ref/Argon2Ref.vcxproj | 226 + vs2015/Argon2Ref/Argon2Ref.vcxproj.filters | 69 + vs2015/Argon2RefBench/Argon2RefBench.vcxproj | 226 + .../Argon2RefBench.vcxproj.filters | 69 + vs2015/Argon2RefDll/Argon2RefDll.vcxproj | 225 + .../Argon2RefDll/Argon2RefDll.vcxproj.filters | 66 + .../Argon2RefGenKAT/Argon2RefGenKAT.vcxproj | 227 + .../Argon2RefGenKAT.vcxproj.filters | 72 + .../Argon2RefTestCI/Argon2RefTestCI.vcxproj | 226 + .../Argon2RefTestCI.vcxproj.filters | 69 + 78 files changed, 91119 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 Argon2.sln create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 appveyor.yml create mode 100644 argon2-specs.pdf create mode 100755 export.sh create mode 100644 include/argon2.h create mode 100644 kats/argon2d create mode 100644 kats/argon2d.shasum create mode 100644 kats/argon2d_v16 create mode 100644 kats/argon2d_v16.shasum create mode 100644 kats/argon2i create mode 100644 kats/argon2i.shasum create mode 100644 kats/argon2i_v16 create mode 100644 kats/argon2i_v16.shasum create mode 100644 kats/argon2id create mode 100644 kats/argon2id.shasum create mode 100644 kats/argon2id_v16 create mode 100644 kats/argon2id_v16.shasum create mode 100644 kats/check-sums.ps1 create mode 100755 kats/check-sums.sh create mode 100644 kats/test.ps1 create mode 100755 kats/test.sh create mode 100644 latex/IEEEtran.cls create mode 100644 latex/Makefile create mode 100644 latex/argon2-specs.tex create mode 100644 latex/pics/argon2-par.pdf create mode 100644 latex/pics/compression.pdf create mode 100644 latex/pics/generic.pdf create mode 100644 latex/pics/power-distribution.jpg create mode 100644 latex/tradeoff.bib create mode 100644 libargon2.pc create mode 100644 man/argon2.1 create mode 100644 meson.build create mode 100644 src/argon2.c create mode 100644 src/bench.c create mode 100644 src/blake2/blake2-impl.h create mode 100644 src/blake2/blake2.h create mode 100644 src/blake2/blake2b.c create mode 100644 src/blake2/blamka-round-opt.h create mode 100644 src/blake2/blamka-round-ref.h create mode 100644 src/core.c create mode 100644 src/core.h create mode 100644 src/encoding.c create mode 100644 src/encoding.h create mode 100644 src/genkat.c create mode 100644 src/genkat.h create mode 100644 src/opt.c create mode 100644 src/ref.c create mode 100644 src/run.c create mode 100644 src/test.c create mode 100644 src/thread.c create mode 100644 src/thread.h create mode 100644 vs2015/Argon2Opt/Argon2Opt.vcxproj create mode 100644 vs2015/Argon2Opt/Argon2Opt.vcxproj.filters create mode 100644 vs2015/Argon2OptBench/Argon2OptBench.vcxproj create mode 100644 vs2015/Argon2OptBench/Argon2OptBench.vcxproj.filters create mode 100644 vs2015/Argon2OptDll/Argon2OptDll.vcxproj create mode 100644 vs2015/Argon2OptDll/Argon2OptDll.vcxproj.filters create mode 100644 vs2015/Argon2OptGenKAT/Argon2OptGenKAT.vcxproj create mode 100644 vs2015/Argon2OptGenKAT/Argon2OptGenKAT.vcxproj.filters create mode 100644 vs2015/Argon2OptTestCI/Argon2OptTestCI.vcxproj create mode 100644 vs2015/Argon2OptTestCI/Argon2OptTestCI.vcxproj.filters create mode 100644 vs2015/Argon2Ref/Argon2Ref.vcxproj create mode 100644 vs2015/Argon2Ref/Argon2Ref.vcxproj.filters create mode 100644 vs2015/Argon2RefBench/Argon2RefBench.vcxproj create mode 100644 vs2015/Argon2RefBench/Argon2RefBench.vcxproj.filters create mode 100644 vs2015/Argon2RefDll/Argon2RefDll.vcxproj create mode 100644 vs2015/Argon2RefDll/Argon2RefDll.vcxproj.filters create mode 100644 vs2015/Argon2RefGenKAT/Argon2RefGenKAT.vcxproj create mode 100644 vs2015/Argon2RefGenKAT/Argon2RefGenKAT.vcxproj.filters create mode 100644 vs2015/Argon2RefTestCI/Argon2RefTestCI.vcxproj create mode 100644 vs2015/Argon2RefTestCI/Argon2RefTestCI.vcxproj.filters diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..177bed95d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +# Export ignore +.gitattributes export-ignore +.gitignore export-ignore +.travis.yml export-ignore +appveyor.yml export-ignore +export.sh export-ignore +latex/* export-ignore + +# Linguist documentation +latex/* linguist-documentation diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..4cfff979b --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +argon2 +libargon2.a +libargon2.so* +libargon2.dylib +.DS_Store +src/*.o +src/blake2/*.o +genkat +.idea +*.pyc +testcase +*.gcda +*.gcno +*.gcov +bench +vs2015/build +Argon2.sdf +Argon2.VC.opendb +*.zip +*.tar.gz +tags diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..265fc48c0 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +language: c + +compiler: + - clang + - gcc + +os: + - linux + - osx + +# Clang on Linux needs to run in a VM to use ASAN. +# See: https://github.com/travis-ci/travis-ci/issues/9033 +matrix: + exclude: + - compiler: clang + os: linux + include: + - compiler: clang + os: linux + sudo: true + +script: make && make testci + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/Argon2.sln b/Argon2.sln new file mode 100644 index 000000000..8c23fdc58 --- /dev/null +++ b/Argon2.sln @@ -0,0 +1,160 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2OptTestCI", "vs2015\Argon2OptTestCI\Argon2OptTestCI.vcxproj", "{12956597-5E42-433A-93F3-D4EFF50AA207}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2RefTestCI", "vs2015\Argon2RefTestCI\Argon2RefTestCI.vcxproj", "{8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2OptGenKAT", "vs2015\Argon2OptGenKAT\Argon2OptGenKAT.vcxproj", "{DBBAAAE6-4560-4D11-8280-30A6650A82EF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2RefGenKAT", "vs2015\Argon2RefGenKAT\Argon2RefGenKAT.vcxproj", "{71921B4C-A795-4A37-95A3-99D600E01211}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2Opt", "vs2015\Argon2Opt\Argon2Opt.vcxproj", "{CAA75C57-998C-494E-B8A5-5894EF0FC528}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2Ref", "vs2015\Argon2Ref\Argon2Ref.vcxproj", "{B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2OptBench", "vs2015\Argon2OptBench\Argon2OptBench.vcxproj", "{B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2RefBench", "vs2015\Argon2RefBench\Argon2RefBench.vcxproj", "{99203F6A-6E8C-42FC-8C7C-C07E8913D539}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2OptDll", "vs2015\Argon2OptDll\Argon2OptDll.vcxproj", "{3A898DD8-ACAE-4269-ADFE-EB7260D71583}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Argon2RefDll", "vs2015\Argon2RefDll\Argon2RefDll.vcxproj", "{19D911A1-533C-4475-B313-F372481A35D4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + ReleaseStatic|x64 = ReleaseStatic|x64 + ReleaseStatic|x86 = ReleaseStatic|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {12956597-5E42-433A-93F3-D4EFF50AA207}.Debug|x64.ActiveCfg = Debug|x64 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Debug|x64.Build.0 = Debug|x64 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Debug|x86.ActiveCfg = Debug|Win32 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Debug|x86.Build.0 = Debug|Win32 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Release|x64.ActiveCfg = Release|x64 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Release|x64.Build.0 = Release|x64 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Release|x86.ActiveCfg = Release|Win32 + {12956597-5E42-433A-93F3-D4EFF50AA207}.Release|x86.Build.0 = Release|Win32 + {12956597-5E42-433A-93F3-D4EFF50AA207}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {12956597-5E42-433A-93F3-D4EFF50AA207}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {12956597-5E42-433A-93F3-D4EFF50AA207}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {12956597-5E42-433A-93F3-D4EFF50AA207}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Debug|x64.ActiveCfg = Debug|x64 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Debug|x64.Build.0 = Debug|x64 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Debug|x86.ActiveCfg = Debug|Win32 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Debug|x86.Build.0 = Debug|Win32 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Release|x64.ActiveCfg = Release|x64 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Release|x64.Build.0 = Release|x64 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Release|x86.ActiveCfg = Release|Win32 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.Release|x86.Build.0 = Release|Win32 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {8A1F7F84-34AF-4DB2-9D58-D4823DFE79E9}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Debug|x64.ActiveCfg = Debug|x64 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Debug|x64.Build.0 = Debug|x64 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Debug|x86.ActiveCfg = Debug|Win32 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Debug|x86.Build.0 = Debug|Win32 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Release|x64.ActiveCfg = Release|x64 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Release|x64.Build.0 = Release|x64 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Release|x86.ActiveCfg = Release|Win32 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.Release|x86.Build.0 = Release|Win32 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {DBBAAAE6-4560-4D11-8280-30A6650A82EF}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {71921B4C-A795-4A37-95A3-99D600E01211}.Debug|x64.ActiveCfg = Debug|x64 + {71921B4C-A795-4A37-95A3-99D600E01211}.Debug|x64.Build.0 = Debug|x64 + {71921B4C-A795-4A37-95A3-99D600E01211}.Debug|x86.ActiveCfg = Debug|Win32 + {71921B4C-A795-4A37-95A3-99D600E01211}.Debug|x86.Build.0 = Debug|Win32 + {71921B4C-A795-4A37-95A3-99D600E01211}.Release|x64.ActiveCfg = Release|x64 + {71921B4C-A795-4A37-95A3-99D600E01211}.Release|x64.Build.0 = Release|x64 + {71921B4C-A795-4A37-95A3-99D600E01211}.Release|x86.ActiveCfg = Release|Win32 + {71921B4C-A795-4A37-95A3-99D600E01211}.Release|x86.Build.0 = Release|Win32 + {71921B4C-A795-4A37-95A3-99D600E01211}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {71921B4C-A795-4A37-95A3-99D600E01211}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {71921B4C-A795-4A37-95A3-99D600E01211}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {71921B4C-A795-4A37-95A3-99D600E01211}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Debug|x64.ActiveCfg = Debug|x64 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Debug|x64.Build.0 = Debug|x64 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Debug|x86.ActiveCfg = Debug|Win32 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Debug|x86.Build.0 = Debug|Win32 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Release|x64.ActiveCfg = Release|x64 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Release|x64.Build.0 = Release|x64 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Release|x86.ActiveCfg = Release|Win32 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.Release|x86.Build.0 = Release|Win32 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {CAA75C57-998C-494E-B8A5-5894EF0FC528}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Debug|x64.ActiveCfg = Debug|x64 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Debug|x64.Build.0 = Debug|x64 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Debug|x86.ActiveCfg = Debug|Win32 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Debug|x86.Build.0 = Debug|Win32 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Release|x64.ActiveCfg = Release|x64 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Release|x64.Build.0 = Release|x64 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Release|x86.ActiveCfg = Release|Win32 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.Release|x86.Build.0 = Release|Win32 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {B9CAC9CE-9F0D-4F52-8D67-FDBBAFCD0DE2}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Debug|x64.ActiveCfg = Debug|x64 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Debug|x64.Build.0 = Debug|x64 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Debug|x86.ActiveCfg = Debug|Win32 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Debug|x86.Build.0 = Debug|Win32 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Release|x64.ActiveCfg = Release|x64 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Release|x64.Build.0 = Release|x64 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Release|x86.ActiveCfg = Release|Win32 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.Release|x86.Build.0 = Release|Win32 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {B3A0FB44-0C1C-4EC3-B155-8B39371F8EE4}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Debug|x64.ActiveCfg = Debug|x64 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Debug|x64.Build.0 = Debug|x64 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Debug|x86.ActiveCfg = Debug|Win32 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Debug|x86.Build.0 = Debug|Win32 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Release|x64.ActiveCfg = Release|x64 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Release|x64.Build.0 = Release|x64 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Release|x86.ActiveCfg = Release|Win32 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.Release|x86.Build.0 = Release|Win32 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {99203F6A-6E8C-42FC-8C7C-C07E8913D539}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Debug|x64.ActiveCfg = Debug|x64 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Debug|x64.Build.0 = Debug|x64 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Debug|x86.ActiveCfg = Debug|Win32 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Debug|x86.Build.0 = Debug|Win32 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Release|x64.ActiveCfg = Release|x64 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Release|x64.Build.0 = Release|x64 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Release|x86.ActiveCfg = Release|Win32 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.Release|x86.Build.0 = Release|Win32 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {3A898DD8-ACAE-4269-ADFE-EB7260D71583}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + {19D911A1-533C-4475-B313-F372481A35D4}.Debug|x64.ActiveCfg = Debug|x64 + {19D911A1-533C-4475-B313-F372481A35D4}.Debug|x64.Build.0 = Debug|x64 + {19D911A1-533C-4475-B313-F372481A35D4}.Debug|x86.ActiveCfg = Debug|Win32 + {19D911A1-533C-4475-B313-F372481A35D4}.Debug|x86.Build.0 = Debug|Win32 + {19D911A1-533C-4475-B313-F372481A35D4}.Release|x64.ActiveCfg = Release|x64 + {19D911A1-533C-4475-B313-F372481A35D4}.Release|x64.Build.0 = Release|x64 + {19D911A1-533C-4475-B313-F372481A35D4}.Release|x86.ActiveCfg = Release|Win32 + {19D911A1-533C-4475-B313-F372481A35D4}.Release|x86.Build.0 = Release|Win32 + {19D911A1-533C-4475-B313-F372481A35D4}.ReleaseStatic|x64.ActiveCfg = ReleaseStatic|x64 + {19D911A1-533C-4475-B313-F372481A35D4}.ReleaseStatic|x64.Build.0 = ReleaseStatic|x64 + {19D911A1-533C-4475-B313-F372481A35D4}.ReleaseStatic|x86.ActiveCfg = ReleaseStatic|Win32 + {19D911A1-533C-4475-B313-F372481A35D4}.ReleaseStatic|x86.Build.0 = ReleaseStatic|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..0578fde27 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,32 @@ +# 20171227 +* Added ABI version number +* AVX2/AVX-512F optimizations of BLAMKA +* Set Argon2 version number from the command line +* New bindings +* Minor bug and warning fixes (no security issue) + +# 20161029 + +* Argon2id added +* Better documentation +* Dual licensing CC0 / Apache 2.0 +* Minor bug fixes (no security issue) + +# 20160406 + +* Version 1.3 of Argon2 +* Version number in encoded hash +* Refactored low-level API +* Visibility control for library symbols +* Microsoft Visual Studio solution +* New bindings +* Minor bug and warning fixes (no security issue) + + +# 20151206 + +* Python bindings +* Password read from stdin, instead of being an argument +* Compatibility FreeBSD, NetBSD, OpenBSD +* Constant-time verification +* Minor bug and warning fixes (no security issue) diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..fa611f7ac --- /dev/null +++ b/LICENSE @@ -0,0 +1,314 @@ +Argon2 reference source code package - reference C implementations + +Copyright 2015 +Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves + +You may use this work under the terms of a Creative Commons CC0 1.0 +License/Waiver or the Apache Public License 2.0, at your option. The terms of +these licenses can be found at: + +- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 +- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + +The terms of the licenses are reproduced below. + +-------------------------------------------------------------------------------- + +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. + +-------------------------------------------------------------------------------- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..1e1129fd6 --- /dev/null +++ b/Makefile @@ -0,0 +1,187 @@ +# +# Argon2 reference source code package - reference C implementations +# +# Copyright 2015 +# Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves +# +# You may use this work under the terms of a Creative Commons CC0 1.0 +# License/Waiver or the Apache Public License 2.0, at your option. The terms of +# these licenses can be found at: +# +# - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 +# - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 +# +# You should have received a copy of both of these licenses along with this +# software. If not, they may be obtained at the above URLs. +# + +RUN = argon2 +BENCH = bench +GENKAT = genkat + +# Increment on an ABI breaking change +ABI_VERSION = 1 + +DIST = phc-winner-argon2 + +SRC = src/argon2.c src/core.c src/blake2/blake2b.c src/thread.c src/encoding.c +SRC_RUN = src/run.c +SRC_BENCH = src/bench.c +SRC_GENKAT = src/genkat.c +OBJ = $(SRC:.c=.o) + +CFLAGS += -std=c89 -O3 -Wall -g -Iinclude -Isrc + +ifeq ($(NO_THREADS), 1) +CFLAGS += -DARGON2_NO_THREADS +else +CFLAGS += -pthread +endif + +CI_CFLAGS := $(CFLAGS) -Werror=declaration-after-statement -D_FORTIFY_SOURCE=2 \ + -Wextra -Wno-type-limits -Werror -coverage -DTEST_LARGE_RAM + +OPTTARGET ?= native +OPTTEST := $(shell $(CC) -Iinclude -Isrc -march=$(OPTTARGET) src/opt.c -c \ + -o /dev/null 2>/dev/null; echo $$?) +# Detect compatible platform +ifneq ($(OPTTEST), 0) +$(info Building without optimizations) + SRC += src/ref.c +else +$(info Building with optimizations for $(OPTTARGET)) + CFLAGS += -march=$(OPTTARGET) + SRC += src/opt.c +endif + +BUILD_PATH := $(shell pwd) +KERNEL_NAME := $(shell uname -s) + +LIB_NAME=argon2 +ifeq ($(KERNEL_NAME), Linux) + LIB_EXT := so.$(ABI_VERSION) + LIB_CFLAGS := -shared -fPIC -fvisibility=hidden -DA2_VISCTL=1 + SO_LDFLAGS := -Wl,-soname,lib$(LIB_NAME).$(LIB_EXT) + LINKED_LIB_EXT := so +endif +ifeq ($(KERNEL_NAME), $(filter $(KERNEL_NAME),FreeBSD NetBSD OpenBSD)) + LIB_EXT := so + LIB_CFLAGS := -shared -fPIC +endif +ifeq ($(KERNEL_NAME), Darwin) + LIB_EXT := $(ABI_VERSION).dylib + LIB_CFLAGS := -dynamiclib -install_name @rpath/lib$(LIB_NAME).$(LIB_EXT) + LINKED_LIB_EXT := dylib +endif +ifeq ($(findstring CYGWIN, $(KERNEL_NAME)), CYGWIN) + LIB_EXT := dll + LIB_CFLAGS := -shared -Wl,--out-implib,lib$(LIB_NAME).$(LIB_EXT).a +endif +ifeq ($(findstring MINGW, $(KERNEL_NAME)), MINGW) + LIB_EXT := dll + LIB_CFLAGS := -shared -Wl,--out-implib,lib$(LIB_NAME).$(LIB_EXT).a +endif +ifeq ($(findstring MSYS, $(KERNEL_NAME)), MSYS) + LIB_EXT := dll + LIB_CFLAGS := -shared -Wl,--out-implib,lib$(LIB_NAME).$(LIB_EXT).a +endif +ifeq ($(KERNEL_NAME), SunOS) + CC := gcc + CFLAGS += -D_REENTRANT + LIB_EXT := so + LIB_CFLAGS := -shared -fPIC +endif + +ifeq ($(KERNEL_NAME), Linux) +ifeq ($(CC), clang) + CI_CFLAGS += -fsanitize=address -fsanitize=undefined +endif +endif + +LIB_SH := lib$(LIB_NAME).$(LIB_EXT) +LIB_ST := lib$(LIB_NAME).a + +ifdef LINKED_LIB_EXT +LINKED_LIB_SH := lib$(LIB_NAME).$(LINKED_LIB_EXT) +endif + + +LIBRARIES = $(LIB_SH) $(LIB_ST) +HEADERS = include/argon2.h + +INSTALL = install + +DESTDIR = +PREFIX = /usr +INCLUDE_REL = include +LIBRARY_REL = lib +BINARY_REL = bin + +INST_INCLUDE = $(DESTDIR)$(PREFIX)/$(INCLUDE_REL) +INST_LIBRARY = $(DESTDIR)$(PREFIX)/$(LIBRARY_REL) +INST_BINARY = $(DESTDIR)$(PREFIX)/$(BINARY_REL) + +.PHONY: clean dist format $(GENKAT) all install + +all: $(RUN) libs +libs: $(LIBRARIES) + +$(RUN): $(SRC) $(SRC_RUN) + $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ + +$(BENCH): $(SRC) $(SRC_BENCH) + $(CC) $(CFLAGS) $^ -o $@ + +$(GENKAT): $(SRC) $(SRC_GENKAT) + $(CC) $(CFLAGS) $^ -o $@ -DGENKAT + +$(LIB_SH): $(SRC) + $(CC) $(CFLAGS) $(LIB_CFLAGS) $(LDFLAGS) $(SO_LDFLAGS) $^ -o $@ + +$(LIB_ST): $(OBJ) + ar rcs $@ $^ + +clean: + rm -f $(RUN) $(BENCH) $(GENKAT) + rm -f $(LIB_SH) $(LIB_ST) kat-argon2* + rm -f testcase + rm -rf *.dSYM + cd src/ && rm -f *.o + cd src/blake2/ && rm -f *.o + cd kats/ && rm -f kat-* diff* run_* make_* + +dist: + cd ..; \ + tar -c --exclude='.??*' -z -f $(DIST)-`date "+%Y%m%d"`.tgz $(DIST)/* + +test: $(SRC) src/test.c + $(CC) $(CFLAGS) -Wextra -Wno-type-limits $^ -o testcase + @sh kats/test.sh + ./testcase + +testci: $(SRC) src/test.c + $(CC) $(CI_CFLAGS) $^ -o testcase + @sh kats/test.sh + ./testcase + +.PHONY: test + +format: + clang-format -style="{BasedOnStyle: llvm, IndentWidth: 4}" \ + -i include/*.h src/*.c src/*.h src/blake2/*.c src/blake2/*.h + +install: $(RUN) libs + $(INSTALL) -d $(INST_INCLUDE) + $(INSTALL) -m 0644 $(HEADERS) $(INST_INCLUDE) + $(INSTALL) -d $(INST_LIBRARY) + $(INSTALL) $(LIBRARIES) $(INST_LIBRARY) +ifdef LINKED_LIB_SH + cd $(INST_LIBRARY) && ln -s $(notdir $(LIB_SH) $(LINKED_LIB_SH)) +endif + $(INSTALL) -d $(INST_BINARY) + $(INSTALL) $(RUN) $(INST_BINARY) + +uninstall: + cd $(INST_INCLUDE) && rm -f $(notdir $(HEADERS)) + cd $(INST_LIBRARY) && rm -f $(notdir $(LIBRARIES) $(LINKED_LIB_SH)) + cd $(INST_BINARY) && rm -f $(notdir $(RUN)) diff --git a/README.md b/README.md new file mode 100644 index 000000000..74cf17086 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# Argon2 + +This is a fork of [the reference C implementation of Argon2](https://github.com/P-H-C/phc-winner-argon2), the password-hashing function that won the [Password Hashing Competition (PHC)](https://password-hashing.net). + +## About Argon2u + +In addition to the official three variants (Argon2i, Argon2d, and Argon2id), this fork also implements a fourth variant, Argon2u. It operates similarly to Argon2id, in that it is a hybrid of Argon2i and Argon2d. Where Argon2id uses Argon2i's algorithm for the first two processed segments, Argon2u does this for the first three. + +## More about Argon2 + +Please see the [original repository](https://github.com/P-H-C/phc-winner-argon2) for information about Argon2. + +## Intellectual property + +Except for the components listed below, the Argon2 code in this +repository is copyright (c) 2015 Daniel Dinu, Dmitry Khovratovich (main +authors), Jean-Philippe Aumasson and Samuel Neves, and dual licensed under the +[CC0 License](https://creativecommons.org/about/cc0) and the +[Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0). For more info +see the LICENSE file. + +The string encoding routines in [`src/encoding.c`](src/encoding.c) are +copyright (c) 2015 Thomas Pornin, and under +[CC0 License](https://creativecommons.org/about/cc0). + +The BLAKE2 code in [`src/blake2/`](src/blake2) is copyright (c) Samuel +Neves, 2013-2015, and under +[CC0 License](https://creativecommons.org/about/cc0). + +All licenses are therefore GPL-compatible. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..fb1f048ef --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,25 @@ +os: Visual Studio 2015 + +environment: + matrix: + - platform: x86 + configuration: Debug + - platform: x86 + configuration: Release + - platform: x64 + configuration: Debug + - platform: x64 + configuration: Release + +matrix: + fast_finish: false + +build: + parallel: true + project: Argon2.sln + verbosity: minimal + +test_script: + - ps: kats\test.ps1 + - ps: if ("Release" -eq $env:configuration) { vs2015\build\Argon2OptTestCI.exe } + - ps: if ("Release" -eq $env:configuration) { vs2015\build\Argon2RefTestCI.exe } diff --git a/argon2-specs.pdf b/argon2-specs.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d916af6415c93ade373b61c9179f9928b2b1daa9 GIT binary patch literal 459608 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