It turns out we probably don't need to be adding Curve25519 right at the moment for TLS. But how quick I could bridge to a short C implementation's function was really neat, so I thought I'd both save the code and show it off.
What happens is it takes a 32-byte input and an optional 32-byte basepoint, both as BINARY!. Then it returns another 32-byte result from calling the C implementation on the unsigned char*
buffers (REBYTE):
//
// export curve25519: native [
// return: "32-byte binary of public key data"
// [binary!]
// secret "32-byte binary of secret data"
// [binary!]
// /basepoint "optional 32-bytes, defaults to #{09000000...}"
// [binary!]
// ]
//
REBNATIVE(curve25519)
{
CRYPT_INCLUDE_PARAMS_OF_CURVE25519;
size_t secret_size;
REBYTE *secret = rebBytes(&secret_size,
"if 32 != length of", ARG(secret), "[",
"fail {SECRET must be a 32-byte BINARY! for curve25519}",
"]",
ARG(secret),
);
assert(secret_size == 32);
size_t basepoint_size;
REBYTE *opt_basepoint = rebBytes(&basepoint_size,
"if all [", REF(basepoint), "32 != length of", REF(basepoint), "] [",
"fail {/BASEPOINT must be a 32-byte BINARY! for curve25519}",
"]",
REF(basepoint),
);
assert(opt_basepoint ? basepoint_size == 32 : basepoint_size == 0);
REBYTE mypublic[32]; // "public" is a C++ keyword :-/
curve25519(mypublic, secret, opt_basepoint);
rebFree(secret);
rebFree(opt_basepoint); // null-tolerant, like C's free()
return rebSizedBinary(mypublic, 32);
}
Here you see the power of libRebol's hybridized calls. rebBytes isn't just a routine for extracting the binary bytes out of a Rebol value...it's able to do a size check first (which FAILs if it needs to). Then the last item in the evaluation is the argument itself. C's 0-for-null is leveraged simply, so that if an argument is null the extractors just give back null.
Being able to do this in plain ANSI C is really rather awesome!