Index: generic/tlsDigest.c ================================================================== --- generic/tlsDigest.c +++ generic/tlsDigest.c @@ -1,9 +1,9 @@ /* * Message Digests Module * - * Provides commands to calculate a message digest using a specified hash algorithm. + * Provides commands to calculate a message digest using a specified hash function. * * Copyright (C) 2023 Brian O'Hagan * */ @@ -22,13 +22,11 @@ #define BIN_FORMAT 0 #define HEX_FORMAT 1 #define CHAN_EOF 0x10 /* - * This structure describes the per-instance state of an SSL channel. - * - * The SSL processing context is maintained here, in the ClientData + * This structure defines the per-instance state of a digest operation. */ typedef struct DigestState { Tcl_Channel self; /* This socket channel */ Tcl_TimerToken timer; /* Timer for read events */ @@ -57,11 +55,11 @@ * Creates structure * *------------------------------------------------------------------- */ DigestState *Tls_DigestNew(Tcl_Interp *interp, int format) { - DigestState *statePtr = NULL; + DigestState *statePtr; statePtr = (DigestState *) ckalloc((unsigned) sizeof(DigestState)); if (statePtr != NULL) { memset(statePtr, 0, sizeof(DigestState)); statePtr->self = NULL; /* This socket channel */ @@ -91,11 +89,13 @@ * Removes structure * *------------------------------------------------------------------- */ void Tls_DigestFree(DigestState *statePtr) { - if (statePtr == (DigestState *) NULL) return; + if (statePtr == (DigestState *) NULL) { + return; + } if (statePtr->ctx != (EVP_MD_CTX *) NULL) { EVP_MD_CTX_free(statePtr->ctx); } if (statePtr->hctx != (HMAC_CTX *) NULL) { @@ -260,17 +260,16 @@ if (chan == (Tcl_Channel) NULL) { return TCL_ERROR; } /* Configure channel */ - if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") == TCL_ERROR) { - res = TCL_ERROR; + if ((res = Tcl_SetChannelOption(interp, chan, "-translation", "binary")) == TCL_ERROR) { goto done; } Tcl_SetChannelBufferSize(chan, BUFFER_SIZE); - /* Create struct */ + /* Create state data struct */ if ((statePtr = Tls_DigestNew(interp, format)) == NULL) { res = TCL_ERROR; goto done; } @@ -295,13 +294,11 @@ res = Tls_DigestFinialize(interp, statePtr); done: /* Close channel */ if (Tcl_Close(interp, chan) == TCL_ERROR) { - chan = (Tcl_Channel) NULL; res = TCL_ERROR; - goto done; } /* Clean-up */ Tls_DigestFree(statePtr); return res; @@ -349,11 +346,11 @@ * Returns: * 0 if successful or POSIX error code if failed. * * Side effects: * Writes digest to output and closes the channel. Stores error - * messages in interp result. + * messages in interp result using Tcl_GetChannelErrorInterp. * *------------------------------------------------------------------- */ int DigestCloseProc(ClientData clientData, Tcl_Interp *interp) { DigestState *statePtr = (DigestState *) clientData; @@ -401,10 +398,11 @@ DigestState *statePtr = (DigestState *) clientData; Tcl_Channel parent; int read, res; *errorCodePtr = 0; + /* Abort if nothing to process */ if (toRead <= 0 || statePtr->self == (Tcl_Channel) NULL) { return 0; } /* Get bytes from underlying channel */ @@ -426,11 +424,10 @@ /* Error */ *errorCodePtr = Tcl_GetErrno(); } else if (!(statePtr->flags & CHAN_EOF)) { /* EOF */ - *errorCodePtr = 0; unsigned char md_buf[EVP_MAX_MD_SIZE]; unsigned int md_len = 0; /* Finalize hash function and calculate message digest */ if (statePtr->ctx != NULL) { @@ -437,10 +434,11 @@ res = EVP_DigestFinal_ex(statePtr->ctx, md_buf, &md_len); } else { res = HMAC_Final(statePtr->hctx, md_buf, &md_len); } if (!res) { + Tcl_SetChannelError(statePtr->self, Tcl_ObjPrintf("Digest finalize failed: %s", REASON())); *errorCodePtr = EINVAL; /* Write message digest to output channel as byte array or hex string */ } else if (md_len > 0) { if (statePtr->format == BIN_FORMAT && toRead >= (int) md_len) { @@ -482,10 +480,11 @@ */ int DigestOutputProc(ClientData clientData, const char *buf, int toWrite, int *errorCodePtr) { DigestState *statePtr = (DigestState *) clientData; *errorCodePtr = 0; + /* Abort if nothing to process */ if (toWrite <= 0 || statePtr->self == (Tcl_Channel) NULL) { return 0; } /* Update hash function */ @@ -517,10 +516,11 @@ const char *optionValue) { DigestState *statePtr = (DigestState *) clientData; Tcl_Channel parent; Tcl_DriverSetOptionProc *setOptionProc; + /* Abort if no channel */ if (statePtr->self == (Tcl_Channel) NULL) { return TCL_ERROR; } /* Delegate options downstream */ @@ -554,10 +554,11 @@ Tcl_DString *optionValue) { DigestState *statePtr = (DigestState *) clientData; Tcl_Channel parent; Tcl_DriverGetOptionProc *getOptionProc; + /* Abort if no channel */ if (statePtr->self == (Tcl_Channel) NULL) { return TCL_ERROR; } /* Delegate options downstream */ @@ -590,10 +591,11 @@ *---------------------------------------------------------------------- */ static void DigestTimerHandler(ClientData clientData) { DigestState *statePtr = (DigestState *) clientData; + /* Abort if no channel */ if (statePtr->self == (Tcl_Channel) NULL) { return; } /* Clear timer token */ @@ -611,11 +613,11 @@ * DigestWatchProc -- * * Initialize the notifier to watch for events from this channel. * * Returns: - * Nothing + * Nothing (can't return error messages) * * Side effects: * Configure notifier so future events on the channel will be seen by Tcl. * *---------------------------------------------------------------------- @@ -624,10 +626,11 @@ void DigestWatchProc(ClientData clientData, int mask) { DigestState *statePtr = (DigestState *) clientData; Tcl_Channel parent; Tcl_DriverWatchProc *watchProc; + /* Abort if no channel */ if (statePtr->self == (Tcl_Channel) NULL) { return; } /* Store OR-ed combination of TCL_READABLE, TCL_WRITABLE and TCL_EXCEPTION */ @@ -668,15 +671,19 @@ * *---------------------------------------------------------------------- */ int DigestGetHandleProc(ClientData clientData, int direction, ClientData *handlePtr) { DigestState *statePtr = (DigestState *) clientData; + Tcl_Channel parent; + /* Abort if no channel */ if (statePtr->self == (Tcl_Channel) NULL) { return TCL_ERROR; } - return Tcl_GetChannelHandle(Tcl_GetStackedChannel(statePtr->self), direction, handlePtr); + + parent = Tcl_GetStackedChannel(statePtr->self); + return Tcl_GetChannelHandle(parent, direction, handlePtr); } /* *---------------------------------------------------------------------- * @@ -737,37 +744,36 @@ * * Returns: * TCL_OK or TCL_ERROR * * Side effects: - * Adds transform to channel and sets result to channel name or error message. + * Adds transform to channel and sets result to channel id or error message. * *---------------------------------------------------------------------- */ static int Tls_DigestChannel(Tcl_Interp *interp, const char *channel, const EVP_MD *md, int format, Tcl_Obj *keyObj) { int mode; /* OR-ed combination of TCL_READABLE and TCL_WRITABLE */ Tcl_Channel chan; DigestState *statePtr; - EVP_MD_CTX *ctx = (EVP_MD_CTX *) NULL; - HMAC_CTX *hctx = (HMAC_CTX *) NULL; /* Validate args */ if (channel == (const char *) NULL || md == (const EVP_MD *) NULL) { return TCL_ERROR; } + /* Get channel Id */ chan = Tcl_GetChannel(interp, channel, &mode); if (chan == (Tcl_Channel) NULL) { return TCL_ERROR; } /* Make sure to operate on the topmost channel */ chan = Tcl_GetTopChannel(chan); - /* Create struct */ + /* Create state data struct */ if ((statePtr = Tls_DigestNew(interp, format)) == NULL) { Tcl_AppendResult(interp, "Initialize digest error: memory allocation failure", (char *) NULL); return TCL_ERROR; } statePtr->self = chan; @@ -789,21 +795,22 @@ if (statePtr->self == (Tcl_Channel) NULL) { Tls_DigestFree(statePtr); return TCL_ERROR; } + /* Set result to channel Id */ Tcl_SetResult(interp, (char *) Tcl_GetChannelName(chan), TCL_VOLATILE); return TCL_OK; } /* *---------------------------------------------------------------------- * * Unstack Channel -- * - * This function is invoked to process the "unstack" TCL command. - * See the user documentation for details on what it does. + * This function removes the stacked channel from the top of the + * channel stack if it is a digest channel. * * Returns: * TCL_OK or TCL_ERROR * * Side effects: @@ -814,10 +821,11 @@ static int UnstackObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Channel chan; int mode; /* OR-ed combination of TCL_READABLE and TCL_WRITABLE */ + /* Validate arg count */ if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "channel"); return TCL_ERROR; } @@ -836,15 +844,12 @@ "\": not a digest channel", NULL); Tcl_SetErrorCode(interp, "TLS", "UNSTACK", "CHANNEL", "INVALID", (char *) NULL); return TCL_ERROR; } - /* Pop transform from channel, leaves error info in interp result */ - if (Tcl_UnstackChannel(interp, chan) == TCL_ERROR) { - return TCL_ERROR; - } - return TCL_OK; + /* Pop transform from channel */ + return Tcl_UnstackChannel(interp, chan); clientData = clientData; } /*******************************************************************/ @@ -853,11 +858,12 @@ /* *------------------------------------------------------------------- * * InstanceObjCmd -- * - * Handler for digest accumulator command instances. + * Handler for digest command instances. Used to add data to hash + * function or retrieve message digest. * * Returns: * TCL_OK or TCL_ERROR * * Side effects: @@ -868,30 +874,32 @@ int InstanceObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { DigestState *statePtr = (DigestState *) clientData; int fn, len = 0; char *buf = NULL; + /* Validate arg count */ if (objc < 2 || objc > 3) { Tcl_WrongNumArgs(interp, 1, objv, "function ?data?"); return TCL_ERROR; } - /* Function */ + /* Get function */ if (Tcl_GetIndexFromObj(interp, objv[1], instance_fns, "function", 0, &fn) != TCL_OK) { return TCL_ERROR; } /* Do function */ if (fn) { - /* Update hash function */ + /* Get data or return error if none */ if (objc == 3) { buf = Tcl_GetByteArrayFromObj(objv[2], &len); } else { Tcl_WrongNumArgs(interp, 1, objv, "update data"); return TCL_ERROR; } + /* Update hash function */ if (!Tls_DigestUpdate(statePtr, buf, (size_t) len)) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("Digest update failed: %s", REASON())); return TCL_ERROR; } @@ -947,11 +955,11 @@ int Tls_DigestInstance(Tcl_Interp *interp, Tcl_Obj *cmdObj, const EVP_MD *md, int format, Tcl_Obj *keyObj) { DigestState *statePtr; char *cmdName = Tcl_GetStringFromObj(cmdObj, NULL); - /* Create struct */ + /* Create state data struct */ if ((statePtr = Tls_DigestNew(interp, format)) == NULL) { Tcl_AppendResult(interp, "Initialize digest error: memory allocation failure", (char *) NULL); return TCL_ERROR; } @@ -975,11 +983,11 @@ /* *------------------------------------------------------------------- * * Tls_DigestData -- * - * Calculate message digest using hash function. + * Return message digest for data using user specified hash function. * * Returns: * TCL_OK or TCL_ERROR * * Side effects: @@ -993,10 +1001,11 @@ char *data; int len, res; unsigned int md_len; unsigned char md_buf[EVP_MAX_MD_SIZE]; + /* Validate arg count */ if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "data"); return TCL_ERROR; } @@ -1061,18 +1070,19 @@ * *------------------------------------------------------------------- */ static int DigestObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - int idx, len, format = HEX_FORMAT, key_len = 0, data_len = 0, res = TCL_ERROR; + int idx, len, format = HEX_FORMAT, res = TCL_OK; const char *digestname, *channel = NULL; Tcl_Obj *cmdObj = NULL, *dataObj = NULL, *fileObj = NULL, *keyObj = NULL; - unsigned char *key = NULL; const EVP_MD *md; + /* Clear interp result */ Tcl_ResetResult(interp); + /* Validate arg count */ if (objc < 3 || objc > 7) { Tcl_WrongNumArgs(interp, 1, objv, "type ?-bin|-hex? ?-key hmac_key? [-channel chan | -command cmdName | -file filename | ?-data? data]"); return TCL_ERROR; } @@ -1090,21 +1100,22 @@ /* Get options */ for (idx = 2; idx < objc-1; idx++) { char *opt = Tcl_GetStringFromObj(objv[idx], NULL); - if (opt[0] != '-') + if (opt[0] != '-') { break; + } OPTFLAG("-bin", format, BIN_FORMAT); OPTFLAG("-binary", format, BIN_FORMAT); OPTFLAG("-hex", format, HEX_FORMAT); OPTFLAG("-hexadecimal", format, HEX_FORMAT); - OPTOBJ("-data", dataObj); OPTSTR("-chan", channel); OPTSTR("-channel", channel); OPTOBJ("-command", cmdObj); + OPTOBJ("-data", dataObj); OPTOBJ("-file", fileObj); OPTOBJ("-filename", fileObj); OPTOBJ("-key", keyObj); OPTBAD("option", "-bin, -channel, -command, -data, -file, -filename, -hex, or -key"); @@ -1114,11 +1125,11 @@ /* If no option for last arg, then its the data */ if (idx < objc) { dataObj = objv[idx]; } - /* Calc digest on file, stacked channel, or data blob */ + /* Calc digest on file, stacked channel, using instance command, or data blob */ if (fileObj != NULL) { res = Tls_DigestFile(interp, fileObj, md, format, keyObj); } else if (channel != NULL) { res = Tls_DigestChannel(interp, channel, md, format, keyObj); } else if (cmdObj != NULL) { Index: generic/tlsInfo.c ================================================================== --- generic/tlsInfo.c +++ generic/tlsInfo.c @@ -39,10 +39,12 @@ * *------------------------------------------------------------------- */ void NamesCallback(const OBJ_NAME *obj, void *arg) { Tcl_Obj *objPtr = (Tcl_Obj *) arg; + + /* Fields: (int) type and alias, (const char*) name and data */ if (1 || !obj->alias) { /* Filter out signed digests (a.k.a signature algorithms) */ if (strstr(obj->name, "rsa") == NULL && strstr(obj->name, "RSA") == NULL) { Tcl_ListObjAppendElement(NULL, objPtr, Tcl_NewStringObj(obj->name,-1)); }