#include <stdint.h> #include <limits.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <tcl.h> #include "tweetnacl.h" #include "blake2.h" #define NANO_SECRET_KEY_LENGTH (crypto_sign_SECRETKEYBYTES - crypto_sign_PUBLICKEYBYTES) #define NANO_PUBLIC_KEY_LENGTH (crypto_sign_PUBLICKEYBYTES) #define TclNano_AttemptAlloc(x) ((void *) Tcl_AttemptAlloc(x)) #define TclNano_Free(x) Tcl_Free((char *) x) #if defined(HAVE_GETRANDOM) # ifdef HAVE_SYS_RANDOM_H # include <sys/random.h> # endif 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; } #elif defined(HAVE_GETENTROPY) 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; } #else # ifdef HAVE_SYS_TYPES_H # include <sys/types.h> # endif # ifdef HAVE_SYS_STAT_H # include <sys/stat.h> # endif # ifdef HAVE_FCNTL_H # include <fcntl.h> # endif void randombytes(uint8_t *buffer, uint64_t length) { ssize_t read_ret; int fd = -1; while (fd < 0) { fd = open("/dev/urandom", O_RDONLY); } while (length > 0) { read_ret = read(fd, buffer, length); if (read_ret <= 0) { continue; } buffer += read_ret; length -= read_ret; } close(fd); return; } #endif 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 = TclNano_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 = TclNano_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)); TclNano_Free(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) { Tcl_SetResult(interp, "Secret key is not the right size", NULL); return(TCL_ERROR); } signature = TclNano_AttemptAlloc(signature_length); if (!signature) { TclNano_Free(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) { TclNano_Free(secret_key); TclNano_Free(signature); Tcl_SetResult(interp, "crypto_sign failed", NULL); return(TCL_ERROR); } Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(signature, crypto_sign_BYTES)); TclNano_Free(signature); TclNano_Free(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, *verify_data, *public_key; int signature_length, data_length, signed_data_length, verify_data_length, public_key_length; unsigned long long verify_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 = TclNano_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); verify_data_length = signed_data_length; verify_data = TclNano_AttemptAlloc(verify_data_length); if (!verify_data) { TclNano_Free(verify_data); Tcl_SetResult(interp, "Internal error", NULL); return(TCL_ERROR); } verify_data_length_nacl = verify_data_length; cso_ret = crypto_sign_open(verify_data, &verify_data_length_nacl, signed_data, signed_data_length, public_key); result = 0; if (cso_ret == 0) { result = 1; } TclNano_Free(signed_data); TclNano_Free(verify_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 = crypto_sign_BYTES; Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(result, result_length)); return(TCL_OK); /* NOTREACH */ clientData = clientData; } static int nano_tcl_self_test(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, ""); return(TCL_ERROR); } return(TCL_OK); /* NOTREACH */ clientData = clientData; } int Nano_Init(Tcl_Interp *interp) { const char nanoInitScript[] = { #include "nano.tcl.h" 0x00 }; #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::selfTest", nano_tcl_self_test, NULL, NULL); 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); if (interp) { Tcl_Eval(interp, nanoInitScript); } return(TCL_OK); }