#include <tcl.h>
#include <stdint.h>
#include <limits.h>
#include <string.h>
#include "tweetnacl.h"
#include "blake2-supercop.h"
#define NANO_SECRET_KEY_LENGTH (crypto_sign_SECRETKEYBYTES - crypto_sign_PUBLICKEYBYTES)
#define NANO_PUBLIC_KEY_LENGTH (crypto_sign_PUBLICKEYBYTES)
#if 0
#include <sys/random.h>
void randombytes(uint8_t *buffer, uint64_t length) {
ssize_t gr_ret;
while (length > 0) {
gr_ret = getrandom(buffer, length, 0);
if (gr_ret <= 0) {
continue;
}
buffer += gr_ret;
length -= gr_ret;
}
return;
}
#endif
#if 0
#include <unistd.h>
void randombytes(uint8_t *buffer, uint64_t length) {
int ge_ret;
int current_length;
while (length > 0) {
current_length = length;
if (current_length > 256) {
current_length = 256;
}
ge_ret = getentropy(buffer, current_length);
if (ge_ret != 0) {
continue;
}
buffer += current_length;
length -= current_length;
}
return;
}
#endif
/*
* XXX:TODO: NOT RANDOM: For testing only
*/
void randombytes(uint8_t *buffer, uint64_t length) {
while (length > 0) {
buffer[length - 1] = (length % 256);
length--;
}
return;
}
static unsigned char *nano_parse_secret_key(Tcl_Obj *secret_key_only_obj, int *out_key_length) {
unsigned char *secret_key, *public_key, *secret_key_only;
int secret_key_length, secret_key_only_length;
secret_key_only = Tcl_GetByteArrayFromObj(secret_key_only_obj, &secret_key_only_length);
if (secret_key_only_length != NANO_SECRET_KEY_LENGTH) {
return(NULL);
}
if ((NANO_SECRET_KEY_LENGTH + NANO_PUBLIC_KEY_LENGTH) != crypto_sign_SECRETKEYBYTES) {
return(NULL);
}
secret_key_length = crypto_sign_SECRETKEYBYTES;
secret_key = (unsigned char *) Tcl_AttemptAlloc(secret_key_length);
if (!secret_key) {
return(NULL);
}
memcpy(secret_key, secret_key_only, secret_key_only_length);
public_key = secret_key + secret_key_only_length;
crypto_sign_keypair(public_key, secret_key, 0);
*out_key_length = secret_key_length;
return(secret_key);
}
static int nano_tcl_generate_keypair(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
int csk_ret;
unsigned char secret_key[crypto_sign_SECRETKEYBYTES], public_key[crypto_sign_PUBLICKEYBYTES];
if (objc != 1) {
Tcl_WrongNumArgs(interp, 1, objv, "");
return(TCL_ERROR);
}
csk_ret = crypto_sign_keypair(public_key, secret_key, 1);
if (csk_ret != 0) {
Tcl_SetResult(interp, "Internal error", NULL);
return(TCL_ERROR);
}
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(secret_key, NANO_SECRET_KEY_LENGTH));
return(TCL_OK);
/* NOTREACH */
clientData = clientData;
}
static int nano_tcl_secret_key_to_public_key(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
unsigned char *secret_key, *public_key;
int secret_key_length, public_key_length;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "secretKey");
return(TCL_ERROR);
}
secret_key = Tcl_GetByteArrayFromObj(objv[1], &secret_key_length);
if (secret_key_length != NANO_SECRET_KEY_LENGTH) {
Tcl_SetResult(interp, "Secret key is not the right size", NULL);
return(TCL_ERROR);
}
public_key_length = NANO_PUBLIC_KEY_LENGTH;
public_key = (unsigned char *) Tcl_AttemptAlloc(public_key_length);
if (!public_key) {
Tcl_SetResult(interp, "Internal error", NULL);
return(TCL_ERROR);
}
crypto_sign_keypair(public_key, secret_key, 0);
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(public_key, public_key_length));
Tcl_Free((char *) public_key);
return(TCL_OK);
/* NOTREACH */
clientData = clientData;
}
static int nano_tcl_sign_detached(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
int cs_ret;
unsigned char *signature, *data, *secret_key;
unsigned long long signature_length;
int data_length, secret_key_length;
if (objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "data secretKey");
return(TCL_ERROR);
}
data = Tcl_GetByteArrayFromObj(objv[1], &data_length);
signature_length = data_length + crypto_sign_BYTES;
if (signature_length >= UINT_MAX) {
Tcl_SetResult(interp, "Input message too long", NULL);
return(TCL_ERROR);
}
secret_key = nano_parse_secret_key(objv[2], &secret_key_length);
if (secret_key == NULL) {
Tcl_SetResult(interp, "Secret key is not the right size", NULL);
return(TCL_ERROR);
}
signature = (unsigned char *) Tcl_AttemptAlloc(signature_length);
if (!signature) {
Tcl_Free((char *) secret_key);
Tcl_SetResult(interp, "Unable to allocate memory", NULL);
return(TCL_ERROR);
}
cs_ret = crypto_sign(signature, &signature_length, data, data_length, secret_key);
if (cs_ret != 0) {
Tcl_Free((char *) secret_key);
Tcl_Free((char *) signature);
Tcl_SetResult(interp, "crypto_sign failed", NULL);
return(TCL_ERROR);
}
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(signature, crypto_sign_BYTES));
Tcl_Free((char *) signature);
Tcl_Free((char *) secret_key);
return(TCL_OK);
/* NOTREACH */
clientData = clientData;
}
static int nano_tcl_verify_detached(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
int cso_ret;
unsigned char *signature, *data, *signed_data, *public_key;
int signature_length, data_length, signed_data_length, public_key_length;
unsigned long long data_length_nacl;
int result;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 1, objv, "data signature publicKey");
return(TCL_ERROR);
}
data = Tcl_GetByteArrayFromObj(objv[1], &data_length);
signature = Tcl_GetByteArrayFromObj(objv[2], &signature_length);
if (signature_length != crypto_sign_BYTES) {
Tcl_SetResult(interp, "Signature is not the right size", NULL);
return(TCL_ERROR);
}
public_key = Tcl_GetByteArrayFromObj(objv[3], &public_key_length);
if (public_key_length != NANO_PUBLIC_KEY_LENGTH) {
Tcl_SetResult(interp, "Public key is not the right size", NULL);
return(TCL_ERROR);
}
signed_data_length = data_length + signature_length;
signed_data = (unsigned char *) Tcl_AttemptAlloc(signed_data_length);
if (!signed_data) {
Tcl_SetResult(interp, "Internal error", NULL);
return(TCL_ERROR);
}
memcpy(signed_data, signature, signature_length);
memcpy(signed_data + signature_length, data, data_length);
data_length_nacl = data_length;
cso_ret = crypto_sign_open(data, &data_length_nacl, signed_data, signed_data_length, public_key);
result = 0;
if (cso_ret == 0) {
result = 1;
}
Tcl_Free((char *) signed_data);
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result));
return(TCL_OK);
/* NOTREACH */
clientData = clientData;
}
static int nano_tcl_hash_data(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
unsigned char *data, result[crypto_sign_BYTES];
int data_length, result_length;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "data");
return(TCL_ERROR);
}
data = Tcl_GetByteArrayFromObj(objv[1], &data_length);
crypto_hash(result, data, data_length);
result_length = sizeof(result);
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(result, result_length));
return(TCL_OK);
/* NOTREACH */
clientData = clientData;
}
int Nano_Init(Tcl_Interp *interp) {
#ifdef USE_TCL_STUBS
const char *tclInitStubs_ret;
/* Initialize Stubs */
tclInitStubs_ret = Tcl_InitStubs(interp, TCL_PATCH_LEVEL, 0);
if (!tclInitStubs_ret) {
return(TCL_ERROR);
}
#endif
Tcl_CreateObjCommand(interp, "::nano::internal::generateKey", nano_tcl_generate_keypair, NULL, NULL);
Tcl_CreateObjCommand(interp, "::nano::internal::signDetached", nano_tcl_sign_detached, NULL, NULL);
Tcl_CreateObjCommand(interp, "::nano::internal::publicKey", nano_tcl_secret_key_to_public_key, NULL, NULL);
Tcl_CreateObjCommand(interp, "::nano::internal::verifyDetached", nano_tcl_verify_detached, NULL, NULL);
Tcl_CreateObjCommand(interp, "::nano::internal::hashData", nano_tcl_hash_data, NULL, NULL);
return(TCL_OK);
}