diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index 1bc11df8f..a7ed457f6 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -96,6 +96,14 @@ macro anycast(any v, $Type) @builtin return ($Type*)v.ptr; } +macro @addr(#val) @builtin +{ + $if $defined(&#val): + return &#val; + $else + return &&#val; + $endif +} fn bool print_backtrace(String message, int backtraces_to_ignore) @if (env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem) { void*[256] buffer; diff --git a/lib/std/crypto/ed25519.c3 b/lib/std/crypto/ed25519.c3 new file mode 100644 index 000000000..28ebcdf4b --- /dev/null +++ b/lib/std/crypto/ed25519.c3 @@ -0,0 +1,737 @@ +/* + Ed25519 Digital Signature Algorithm +*/ + +module std::crypto::ed25519; +import std::hash::sha512; + +alias PrivateKey = char[32]; +alias PublicKey = char[PrivateKey.len]; +alias Signature = char[2 * PublicKey.len]; + +<* + Generate a public key from a private key. + + @param [in] private_key : "32 bytes of cryptographically secure random data" + @require private_key.len == PrivateKey.len +*> +fn PublicKey public_keygen(char[] private_key) +{ + return pack(&&unproject(&&(BASE * expand_private_key(private_key)[:FBaseInt.len]))); +} + +<* + Sign a message. + + @param [in] message + @param [in] private_key + @param [in] public_key + @require private_key.len == PrivateKey.len + @require public_key.len == PublicKey.len +*> +fn Signature sign(char[] message, char[] private_key, char[] public_key) +{ + Signature r @noinit; + + char[*] exp = expand_private_key(private_key); + + Sha512 sha @noinit; + sha.init(); + + sha.update(exp[FBaseInt.len..]); + sha.update(message); + + FBaseInt k = from_bytes(&&sha.final()); + + r[:F25519Int.len] = pack(&&unproject(&&(BASE * k[..])))[..]; + + sha.init(); + + sha.update(r[:F25519Int.len]); + sha.update(public_key); + sha.update(message); + + FBaseInt z = from_bytes(&&sha.final()); + FBaseInt e = from_bytes(exp[:FBaseInt.len]); + + r[F25519Int.len..] = (z * e + k)[..]; + + return r; +} + +<* + Verify the signature of a message. + + @param [in] message + @param [in] signature + @param [in] public_key + @require signature.len == Signature.len + @require public_key.len == PublicKey.len +*> +fn bool verify(char[] message, char[] signature, char[] public_key) +{ + char ok = 1; + + F25519Int lhs = pack(&&unproject(&&(BASE * signature[F25519Int.len..]))); + + Unpacking unp_p = unpack_on_curve((F25519Int*)public_key); + Projection p = project(&unp_p.point); + ok &= unp_p.on_curve; + + Sha512 sha @noinit; + sha.init(); + + sha.update(signature[:F25519Int.len]); + sha.update(public_key); + sha.update(message); + + FBaseInt z = from_bytes(&&sha.final()); + + p = p * z[..]; + + Unpacking unp_q = unpack_on_curve((F25519Int*)signature[:F25519Int.len]); + Projection q = project(&unp_q.point); + ok &= unp_q.on_curve; + + p = p + q; + + F25519Int rhs = pack(&&unproject(&p)); + + return (bool)(ok & eq(&lhs, &rhs)); +} + +// Base point for Ed25519. Generate a subgroup of order 2^252+0x14def9dea2f79cd65812631a5cf5d3ed +const Projection BASE @private = +{ + x"1ad5258f602d56c9 b2a7259560c72c69 5cdcd6fd31e2a4c0 fe536ecdd3366921", + x"5866666666666666 6666666666666666 6666666666666666 6666666666666666", + x"a3ddb7a5b38ade6d f5525177809ff020 7de3ab648e4eea66 65768bd70f5f8767", + ONE +}; + +<* + Compute the pruned SHA-512 hash of a private key. + + @param [in] private_key + @require private_key.len == PrivateKey.len +*> +fn char[sha512::HASH_SIZE] expand_private_key(char[] private_key) @local +{ + char[*] r = sha512::hash(private_key); + + r[0] &= 0b11111000; + r[FBaseInt.len - 1] &= 0b01111111; + r[FBaseInt.len - 1] |= 0b01000000; + + return r; +} + + + +/* + Operations on the twisted Edwards curve -x^2+y^2=1-121665/121666*x^2*y^2 over the prime field F_(2^255-19) (edwards25519) + The set of F_(2^255-19)-rational curve points is a group of order 2^3*(2^252+0x14def9dea2f79cd65812631a5cf5d3ed) +*/ + +module std::crypto::ed25519 @private; + +// Affine coordinates. +struct Point +{ + F25519Int x; + F25519Int y; +} + +// Projective coordinates. +struct Projection +{ + F25519Int x; + F25519Int y; + F25519Int t; + F25519Int z; +} + +// Neutral. +const Projection NEUTRAL = +{ + ZERO, + ONE, + ZERO, + ONE +}; + +<* + Convert affine to projective coordinates. + + @param [&in] p +*> +fn Projection project(Point* p) => { p.x, p.y, p.x * p.y, ONE }; + +<* + Convert projective to affine coordinates. + + @param [&in] p +*> +fn Point unproject(Projection* p) +{ + Point r @noinit; + + F25519Int inv = p.z.inv(); + r.x = p.x * inv; + r.y = p.y * inv; + + r.x.normalize(); + r.y.normalize(); + + return r; +} + +// d parameter for edwards25519 : -121665/121666 +const F25519Int D = x"a3785913ca4deb75 abd841414d0a7000 98e879777940c78c 73fe6f2bee6c0352"; + +// 2*d +const F25519Int DD = x"59f1b226949bd6eb 56b183829a14e000 30d1f3eef2808e19 e7fcdf56dcd90624"; + +<* + Compress a point. + + @param [&in] p +*> +fn F25519Int pack(Point* p) +{ + Point r = *p; + + r.x.normalize(); + r.y.normalize(); + + r.y[^1] |= (r.x[0] & 1) << 7; + + return r.y; +} + +struct Unpacking +{ + Point point; + char on_curve; // Non-zero if true. +} + +<* + Uncompress a point. Check if it is on the curve. + + @param [&in] encoding +*> +fn Unpacking unpack_on_curve(F25519Int* encoding) +{ + Point p @noinit; + + char parity = (*encoding)[^1] >> 7; + + p.y = *encoding; + p.y[^1] &= 0b01111111; + + F25519Int y2 = p.y * p.y; + F25519Int x2 = (D * y2 + ONE).inv() * (y2 - ONE); + + F25519Int x = x2.sqrt(); + + p.x = f25519_select(&x, &&-x, (x[0] ^ parity) & 1); + + F25519Int _x2 = p.x * p.x; + + x2.normalize(); + _x2.normalize(); + + return {p, eq(&x2, &_x2)}; +} + +macro Projection Projection.@add(&s, Projection #p) @operator(+) => s.add(@addr(#p)); +<* + Addition. + + @param [&in] s +*> +fn Projection Projection.add(&s, Projection* p) @operator(+) +{ + Projection r @noinit; + + F25519Int a = (s.y - s.x) * (p.y - p.x); + F25519Int b = (s.y + s.x) * (p.y + p.x); + F25519Int c = s.t * DD * p.t; + F25519Int d = (s.z * p.z).mul_s(2); + F25519Int e = b - a; + F25519Int f = d - c; + F25519Int g = d + c; + F25519Int h = b + a; + + r.x = e * f; + r.y = g * h; + r.t = e * h; + r.z = f * g; + + return r; +} + +<* + Double a point. + + @param [&in] s +*> +fn Projection Projection.twice(&s) +{ + Projection r @noinit; + + F25519Int a = s.x * s.x; + F25519Int b = s.y * s.y; + F25519Int c = (s.z * s.z).mul_s(2); + F25519Int d = s.x + s.y; + F25519Int e = d * d - a - b; + F25519Int g = b - a; + F25519Int f = g - c; + F25519Int h = -b - a; + + r.x = e * f; + r.y = g * h; + r.t = e * h; + r.z = f * g; + + return r; +} + +<* + Variable base scalar multiplication. + + @param [&in] s + @param [in] n +*> +fn Projection Projection.mul(&s, char[] n) @operator(*) +{ + Projection r = NEUTRAL; + + for (isz i = n.len << 3 - 1; i >= 0; i--) + { + r = r.twice(); + + Projection t = r + s; + + char bit = n[i >> 3] >> (i & 7) & 1; + r.x = f25519_select(&r.x, &t.x, bit); + r.y = f25519_select(&r.y, &t.y, bit); + r.z = f25519_select(&r.z, &t.z, bit); + r.t = f25519_select(&r.t, &t.t, bit); + } + + return r; +} + + + +/* + Modular arithmetic over the prime field F_(2^255-19) +*/ + +module std::crypto::ed25519 @private; + +typedef F25519Int = inline char[32]; + +const F25519Int ZERO = {}; +const F25519Int ONE = {[0] = 1}; + +<* + Reduce an element with carry to at most 2^255+18 (32 bytes) + + @param [&inout] s +*> +fn void F25519Int.reduce_carry(&s, uint carry) +{ + // Reduce using 2^255 = 19 mod p + (*s)[^1] &= 0b01111111; + + carry *= 19; + + for (usz i; i < F25519Int.len; i++) + { + carry += (*s)[i]; + (*s)[i] = (char)carry; + carry >>= 8; + } +} + +<* + Reduce an element to at most 2^255-19 + + @param [&inout] s +*> +fn void F25519Int.normalize(&s) +{ + s.reduce_carry((*s)[^1] >> 7); + + // Substract p + F25519Int sub @noinit; + ushort c = 19; + for (usz i; i + 1 < F25519Int.len; i++) + { + c += (*s)[i]; + sub[i] = (char)c; + c >>= 8; + } + c += (*s)[^1] - 0b10000000; + sub[^1] = (char)c; + + *s = f25519_select(&sub, s, (char)(c >> 15)); +} + +<* + Constant-time equality comparison. Return is non-zero if true. + + @param [&in] a + @param [&in] b +*> +fn char eq(F25519Int* a, F25519Int* b) +{ + char e; + for (usz i; i < F25519Int.len; i++) e |= (*a)[i] ^ (*b)[i]; + + e |= (e >> 4); + e |= (e >> 2); + e |= (e >> 1); + + return e ^ 1; +} + +<* + Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1. + + @param [&in] zero : "selected if condition is 0" + @param [&in] one : "selected if condition is 1" +*> +fn F25519Int f25519_select(F25519Int* zero, F25519Int* one, char condition) +{ + F25519Int r @noinit; + + for (usz i; i < F25519Int.len; i++) r[i] = (*zero)[i] ^ (-condition & ((*one)[i] ^ (*zero)[i])); + + return r; +} + +macro F25519Int F25519Int.@add(&s, F25519Int #n) @operator(+) => s.add(@addr(#n)); + +<* + Addition. + + @param [&in] s + @param [&in] n +*> +fn F25519Int F25519Int.add(&s, F25519Int* n) @operator(+) +{ + F25519Int r @noinit; + + ushort c; + foreach (i, v : *s) + { + c >>= 8; + c += v + (*n)[i]; + r[i] = (char)c; + } + + r.reduce_carry(c >> 7); + + return r; +} + +macro F25519Int F25519Int.@sub(&s, F25519Int #n) @operator(-) => s.sub(@addr(#n)); + +<* + Substraction. + + @param [&in] s + @param [&in] n +*> +fn F25519Int F25519Int.sub(&s, F25519Int* n) @operator(-) +{ + // Compute s+2*p-n instead of s-n to avoid underflow. + F25519Int r @noinit; + + uint c = (char)~(2 * 19 - 1); + for (usz i; i + 1 < F25519Int.len; i++) + { + c += 0b11111111_00000000 + (*s)[i] - (*n)[i]; + r[i] = (char)c; + c >>= 8; + } + c += (*s)[^1] - (*n)[^1]; + r[^1] = (char)c; + + r.reduce_carry(c >> 7); + + return r; +} + +<* + Negation. + + @param [&in] s +*> +fn F25519Int F25519Int.neg(&s) @operator(-) +{ + // Compute 2*p-s instead of -s to avoid underflow. + F25519Int r @noinit; + + uint c = (char)~(2 * 19 - 1); + foreach (i, v : ((char*)s)[:F25519Int.len - 1]) + { + c += 0b11111111_00000000 - v; + r[i] = (char)c; + c >>= 8; + } + c -= (*s)[^1]; + r[^1] = (char)c; + + r.reduce_carry(c >> 7); + + return r; +} + + +macro F25519Int F25519Int.@mul(&s, F25519Int #n) @operator(*) => s.mul(@addr(#n)); + +<* + Multiplication. + + @param [&in] s + @param [&in] n +*> +fn F25519Int F25519Int.mul(&s, F25519Int* n) @operator(*) +{ + F25519Int r @noinit; + + uint c; + for (usz i = 0; i < F25519Int.len; i++) + { + c >>= 8; + for (usz j; j <= i; j++) c += (*s)[j] * (*n)[i - j]; + // Reduce using 2^256 = 2*19 mod p + for (usz j = i + 1; j < F25519Int.len; j++) c += (*s)[j] * (*n)[^j - i] * 2 * 19; + r[i] = (char)c; + } + + r.reduce_carry(c >> 7); + + return r; +} + +<* + Multiplication by a small element. + + @param [&in] s +*> +fn F25519Int F25519Int.mul_s(&s, uint n) +{ + F25519Int r @noinit; + + uint c; + foreach (i, v : *s) + { + c >>= 8; + c += v * n; + r[i] = (char)c; + } + + r.reduce_carry(c >> 7); + + return r; +} + +<* + Inverse an element. + + @param [&in] s +*> +fn F25519Int F25519Int.inv(&s) +{ + //Compute s^(p-2) + F25519Int r = *s; + + for (usz i; i < 255 - 1 - 5; i++) r = r * r * *s; + + r *= r; + r = r * r * s; + r *= r; + r = r * r * s; + r = r * r * s; + + return r; +} + +<* + Raise an element to the power of 2^252-3 + + @param [&in] s +*> +fn F25519Int F25519Int.pow_2523(&s) @local +{ + F25519Int r = *s; + + for (usz i; i < 252 - 1 - 2; i++) r = r * r * *s; + + r = r * r; + r = r * r * s; + + return r; +} + +<* + Compute the square root of an element. + + @param [&in] s +*> +fn F25519Int F25519Int.sqrt(&s) +{ + F25519Int twice = s.mul_s(2); + F25519Int pow = twice.pow_2523(); + + return ((twice * pow * pow) - ONE) * s * pow; +} + + + +/* + Modular arithmetic over the prime field F_(2^252+0x14def9dea2f79cd65812631a5cf5d3ed) +*/ + +module std::crypto::ed25519 @private; +import std::math; + +typedef FBaseInt = inline char[32]; + +// Order of the field : 2^252+0x14def9dea2f79cd65812631a5cf5d3ed +const FBaseInt ORDER = x"edd3f55c1a631258 d69cf7a2def9de14 0000000000000000 0000000000000010"; + +<* + FBaseInterpret bytes as a normalized element. + + @param [in] bytes +*> +fn FBaseInt from_bytes(char[] bytes) +{ + FBaseInt r; + + usz bitc = min(252 - 1, bytes.len << 3); + usz bytec = bitc >> 3; + usz mod = bitc & 7; + usz rem = bytes.len << 3 - bitc; + + r[:bytec] = bytes[^bytec..]; + + if (mod) + { + r <<= mod; + r[0] |= bytes[^bytec + 1] >> (8 - mod); + } + + for (isz i = rem - 1; i >= 0; i--) + { + r <<= 1; + r[0] |= bytes[i >> 3] >> (i & 7) & 1; + r = r.sub_l(&ORDER); + } + + return r; +} + +<* + Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1. + + @param [&in] zero : "selected if condition is 0" + @param [&in] one : "selected if condition is 1" +*> +fn FBaseInt fbase_select(FBaseInt* zero, FBaseInt* one, char condition) +{ + FBaseInt r @noinit; + + for (usz i; i < FBaseInt.len; i++) r[i] = (*zero)[i] ^ (-condition & ((*one)[i] ^ (*zero)[i])); + + return r; +} + +macro FBaseInt FBaseInt.@add(&s, FBaseInt #n) @operator(+) => s.add(@addr(#n)); + +<* + Addition. + + @param [&in] s + @param [&in] n +*> +fn FBaseInt FBaseInt.add(&s, FBaseInt* n) @operator(+) +{ + FBaseInt r @noinit; + + ushort c; + for (usz i; i < FBaseInt.len; i++) + { + c += (*s)[i] + (*n)[i]; + r[i] = (char)c; + c >>= 8; + } + + return r.sub_l(&ORDER); +} + +<* + Substraction if RHS is less than LHS else identity. + + @param [&in] s + @param [&in] n +*> +fn FBaseInt FBaseInt.sub_l(&s, FBaseInt* n) +{ + FBaseInt sub @noinit; + ushort c; + for (usz i; i < FBaseInt.len; i++) + { + c = (*s)[i] - (*n)[i] - c; + sub[i] = (char)c; + c = (c >> 8) & 1; + } + + return fbase_select(&sub, s, (char)c); +} + +<* + Left shift. + + @param [&in] s +*> +fn FBaseInt FBaseInt.shl(&s, usz n) @operator(<<) +{ + FBaseInt r @noinit; + + ushort c; + for (usz i; i < FBaseInt.len; i++) + { + c |= (*s)[i] << n; + r[i] = (char)c; + c >>= 8; + } + + return r; +} + +macro FBaseInt FBaseInt.@mul(&s, FBaseInt #n) @operator(*) => s.mul(@addr(#n)); +<* + Multiplication. + + @param [&in] s + @param [&in] n +*> +fn FBaseInt FBaseInt.mul(&s, FBaseInt* n) @operator(*) +{ + FBaseInt r; + + for (isz i = 252; i >= 0; i--) + { + r = (r << 1).sub_l(&ORDER); + r = fbase_select(&r, &&(r + s), (*n)[i >> 3] >> (i & 7) & 1); + } + + return r; +} diff --git a/releasenotes.md b/releasenotes.md index f144962f5..d3a007ede 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -63,14 +63,17 @@ - `@links` on macros would not be added to calling functions. - Fix `Formatter.print` returning incorrect size. - A distinct type based on an array would yield .len == 0 +- Overloading addition with a pointer would not work. ### Stdlib changes - Improve contract for readline. #2280 - Added Whirlpool hash. +- Added Ed25519. - Added string::bformat. - Virtual memory library. - New virtual emory arena allocator. - Added `WString.len`. +- Added `@addr` macro. ## 0.7.3 Change list diff --git a/src/compiler/enums.h b/src/compiler/enums.h index b50051ddb..2fa391b9a 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -964,6 +964,7 @@ typedef enum OVERLOADS_COUNT = OVERLOAD_SHR_ASSIGN } OperatorOverload; +#define OVERLOAD_NONE ((OperatorOverload)0) typedef enum { OS_TYPE_UNKNOWN, diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 3887facf0..f187ad390 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -208,8 +208,10 @@ static inline bool sema_analyse_maybe_dead_expr(SemaContext *, Expr *expr, bool static inline bool sema_insert_binary_overload(SemaContext *context, Expr *expr, Decl *overload, Expr *lhs, Expr *rhs, bool reverse); static bool sema_replace_with_overload(SemaContext *context, Expr *expr, Expr *left, Expr *right, Type *left_type, OperatorOverload* operator_overload_ref); -// -- implementations +INLINE bool sema_expr_analyse_ptr_add(SemaContext *context, Expr *expr, Expr *left, Expr *right, CanonicalType *left_type, CanonicalType *right_type, Type *cast_to_iptr, bool *failed_ref); +static Type *defer_iptr_cast(Expr *maybe_pointer); +// -- implementations typedef struct { @@ -7188,12 +7190,31 @@ static bool sema_binary_arithmetic_promotion(SemaContext *context, Expr *left, E OperatorOverload *operator_overload_ref, bool *failed_ref) { + OperatorOverload overload = *operator_overload_ref; if (type_is_user_defined(left_type) || type_is_user_defined(right_type)) { if (!sema_replace_with_overload(context, parent, left, right, left_type, operator_overload_ref)) return false; if (!*operator_overload_ref) return true; } + if (overload == OVERLOAD_PLUS) + { + Type *cast_to_iptr = defer_iptr_cast(left); + if (!cast_to_iptr) cast_to_iptr = defer_iptr_cast(right); + left_type = type_no_optional(left->type)->canonical; + right_type = type_no_optional(right->type)->canonical; + if (type_is_pointer_like(left_type)) + { + *operator_overload_ref = OVERLOAD_NONE; + return sema_expr_analyse_ptr_add(context, parent, left, right, left_type, right_type, cast_to_iptr, failed_ref); + } + if (type_is_pointer_like(right_type)) + { + *operator_overload_ref = OVERLOAD_NONE; + return sema_expr_analyse_ptr_add(context, parent, right, left, right_type, left_type, cast_to_iptr, failed_ref); + } + } + Type *max = cast_numeric_arithmetic_promotion(type_find_max_type(left_type, right_type)); if (!max || (!type_underlying_is_numeric(max) && !(allow_bool_vec && type_flat_is_bool_vector(max)))) { @@ -7525,46 +7546,15 @@ static bool sema_expr_analyse_add(SemaContext *context, Expr *expr, Expr *left, // this is safe in the pointer case actually. if (!sema_binary_analyse_subexpr(context, left, right)) return false; - Type *cast_to_iptr = defer_iptr_cast(left); - if (!cast_to_iptr) cast_to_iptr = defer_iptr_cast(right); Type *left_type = type_no_optional(left->type)->canonical; Type *right_type = type_no_optional(right->type)->canonical; - bool right_is_pointer = type_is_pointer_like(right_type); - bool left_is_pointer = type_is_pointer_like(left_type); - // 2. To detect pointer additions, reorder if needed - if (right_is_pointer && !left_is_pointer) - { - Expr *temp = right; - right = left; - left = temp; - right_type = left_type; - left_type = left->type->canonical; - left_is_pointer = true; - right_is_pointer = false; - (void)right_is_pointer; - expr->binary_expr.left = exprid(left); - expr->binary_expr.right = exprid(right); - } - - // 3. The "left" will now always be the pointer. - // so check if we want to do the normal pointer add special handling. - if (left_is_pointer) - { - return sema_expr_analyse_ptr_add(context, expr, left, right, left_type, right_type, cast_to_iptr, failed_ref); - } - if (left_type->type_kind == TYPE_ENUM || right_type->type_kind == TYPE_ENUM) { return sema_expr_analyse_enum_add_sub(context, expr, left, right, failed_ref); } - left_type = type_no_optional(left->type)->canonical; - right_type = type_no_optional(right->type)->canonical; - - ASSERT_SPAN(expr, !cast_to_iptr); - // 4. Do a binary arithmetic promotion OperatorOverload overload = OVERLOAD_PLUS; if (!sema_binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, diff --git a/test/unit/stdlib/crypto/ed25519.c3 b/test/unit/stdlib/crypto/ed25519.c3 new file mode 100644 index 000000000..31f09bec0 --- /dev/null +++ b/test/unit/stdlib/crypto/ed25519.c3 @@ -0,0 +1,15 @@ +module std::crypto::ed25519_test @test; +import std::crypto::ed25519; + +fn void ed25519_keygen_sign_verify() @test { + PrivateKey private_key = x"97b0d0435be3a583df0b77aff277e5fc8f857cae1a827723241bb9bbf7e96018"; + + PublicKey public_key = ed25519::public_keygen(&private_key); + assert(public_key == x"fd462c5c7044b224f4a6cc0f0e5e16511be881be639b3fbc600f7b5d6da54e2f"); + + String message = "abcdefghijklmnopqrstuvwxyz0123456789"; + + Signature signature = ed25519::sign(message, &private_key, &public_key); + assert(signature == x"c03cf7b181583f6353a9434be910a484e1720247a0dc384d601e495294d0202728dda5f31bc62b00158b1588bc2a65f48ccfaa1b1431aff3d8da9271b7de9403"); + assert(ed25519::verify(message, &signature, &public_key)); +}