Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -23,10 +23,13 @@ $(MAKE) -C tweetnacl tweetnacl.o blake2b/blake2b.o: $(MAKE) -C blake2b blake2b.o +test: @EXTENSION_TARGET@ + cd build/test && ./test.tcl + clean: rm -f @EXTENSION_TARGET@ nano.o $(MAKE) -C tweetnacl clean $(MAKE) -C blake2b clean @@ -37,6 +40,6 @@ $(MAKE) -C blake2b distclean mrproper: distclean rm -f configure aclocal.m4 config.guess config.sub install-sh -.PHONY: all clean distclean mrproper +.PHONY: all test clean distclean mrproper Index: build/test/test.tcl ================================================================== --- build/test/test.tcl +++ build/test/test.tcl @@ -2,10 +2,24 @@ lappend auto_path [file join [file dirname [info script]] .. ..] package require nano -set key [binary decode hex 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000] -puts "Key Length: [string length $key]" -set signed [binary encode hex [::nano::internal::sign "" $key]] +proc test1 {} { + set data [binary decode hex 0000000000000000000000000000000000000000000000000000000000000000] + set key [binary decode hex C4D214F19E706E9C7487CEF00DE8059200C32414F0ED82E5E33B523AEDF719BA] + set signed [string toupper [binary encode hex [::nano::internal::signDetached $data $key]]] + set signed_expected 1C2DE9B8A71215F949A11BBEA7EFA4ECD67A8C2B5A9AD98AE6B1AB7F7A3D2CFD715F570309148C7B39C346FB9B91B321D7E75BD598F271AF31AB60A99D086709 + + if {$signed ne $signed_expected} { + puts "\[FAIL\] Got: $signed" + puts "\[FAIL\] Exp: $signed_expected" + return false + } + + set pubKey [::nano::internal::publicKey $key] + set pubKey [binary encode hex $pubKey] + + puts "Pub: $pubKey" +} -puts $signed +test1 Index: nano.c ================================================================== --- nano.c +++ nano.c @@ -58,11 +58,78 @@ } return; } -static int nano_sign(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { +#define NANO_SECRET_KEY_LENGTH (crypto_sign_SECRETKEYBYTES - crypto_sign_PUBLICKEYBYTES) +#define NANO_PUBLIC_KEY_LENGTH (crypto_sign_PUBLICKEYBYTES) + +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_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; @@ -71,39 +138,105 @@ return(TCL_ERROR); } data = Tcl_GetByteArrayFromObj(objv[1], &data_length); - secret_key = Tcl_GetByteArrayFromObj(objv[2], &secret_key_length); - if (secret_key_length != crypto_sign_SECRETKEYBYTES) { - Tcl_SetResult(interp, "Secret key is not the right size", NULL); - - return(TCL_ERROR); - } - 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, signature_length)); + 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; + + 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); + if (cso_ret != 0) { + Tcl_Free((char *) signed_data); + + Tcl_SetResult(interp, "crypto_sign_open failed", NULL); + + return(TCL_ERROR); + } + + Tcl_Free((char *) signed_data); + + /* XXX:TODO: Validate */ return(TCL_OK); /* NOTREACH */ clientData = clientData; @@ -118,9 +251,11 @@ if (!tclInitStubs_ret) { return(TCL_ERROR); } #endif - Tcl_CreateObjCommand(interp, "::nano::internal::sign", nano_sign, 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); return(TCL_OK); }