Index: generic/tlsDigest.c ================================================================== --- generic/tlsDigest.c +++ generic/tlsDigest.c @@ -36,11 +36,39 @@ int mode; /* Current mode of parent channel */ int format; /* Output format */ Tcl_Interp *interp; /* Current interpreter */ EVP_MD_CTX *ctx; /* MD Context */ + HMAC_CTX *hctx; /* HMAC Context */ } DigestState; + +/* + *------------------------------------------------------------------- + * + * DigestFree -- + * + * This procedure removes a digest state structure + * + * Returns: + * Nothing + * + * Side effects: + * Removes structure + * + *------------------------------------------------------------------- + */ +void DigestFree (DigestState *statePtr) { + if (statePtr == (DigestState *) NULL) return; + + if (statePtr->ctx != (EVP_MD_CTX *) NULL) { + EVP_MD_CTX_free(statePtr->ctx); + } + if (statePtr->hctx != (HMAC_CTX *) NULL) { + HMAC_CTX_free(statePtr->hctx); + } + ckfree(statePtr); +} /*******************************************************************/ /* *------------------------------------------------------------------- @@ -106,18 +134,21 @@ } /* Read file data and update hash function */ while (!Tcl_Eof(chan)) { int len = Tcl_ReadRaw(chan, (char *) buf, BUFFER_SIZE); - if (keyObj == NULL) { - res = EVP_DigestUpdate(ctx, &buf, (size_t) len); - } else { - res = HMAC_Update(hctx, &buf[0], (size_t) len); - } - if (len > 0 && !res) { - Tcl_AppendResult(interp, "Update digest failed: ", REASON(), NULL); - goto error; + if (len > 0) { + if (keyObj == NULL) { + res = EVP_DigestUpdate(ctx, &buf, (size_t) len); + } else { + res = HMAC_Update(hctx, &buf[0], (size_t) len); + } + if (!res) { + Tcl_AppendResult(interp, "Update digest failed: ", REASON(), NULL); + res = TCL_ERROR; + goto error; + } } } /* Close channel */ if (Tcl_Close(interp, chan) == TCL_ERROR) { @@ -201,34 +232,10 @@ } /* *------------------------------------------------------------------- * - * DigestFree -- - * - * This procedure removes a digest state structure - * - * Returns: - * Nothing - * - * Side effects: - * Removes structure - * - *------------------------------------------------------------------- - */ -void DigestFree (DigestState *statePtr) { - if (statePtr == (DigestState *) NULL) return; - - if (statePtr->ctx != NULL) { - EVP_MD_CTX_free(statePtr->ctx); - } - ckfree(statePtr); -} - -/* - *------------------------------------------------------------------- - * * DigestCloseProc -- * * This procedure is invoked by the generic IO level to perform * channel-type-specific cleanup when digest channel is closed. * @@ -282,11 +289,11 @@ *---------------------------------------------------------------------- */ int DigestInputProc(ClientData clientData, char *buf, int toRead, int *errorCodePtr) { DigestState *statePtr = (DigestState *) clientData; Tcl_Channel parent; - int read; + int read, res; *errorCodePtr = 0; if (toRead <= 0 || statePtr->self == (Tcl_Channel) NULL) { return 0; } @@ -295,12 +302,16 @@ parent = Tcl_GetStackedChannel(statePtr->self); read = Tcl_ReadRaw(parent, buf, toRead); /* Add to message digest */ if (read > 0) { - /* OK */ - if (!EVP_DigestUpdate(statePtr->ctx, buf, read)) { + if (statePtr->ctx != NULL) { + res = EVP_DigestUpdate(statePtr->ctx, buf, (size_t) read); + } else { + res = HMAC_Update(statePtr->hctx, buf, (size_t) read); + } + if (!res) { Tcl_SetChannelError(statePtr->self, Tcl_ObjPrintf("Digest update failed: %s", REASON())); *errorCodePtr = EINVAL; return -1; } *errorCodePtr = EAGAIN; @@ -314,12 +325,17 @@ /* EOF */ *errorCodePtr = 0; unsigned char md_buf[EVP_MAX_MD_SIZE]; unsigned int md_len = 0; - /* Get message digest */ - if (!EVP_DigestFinal_ex(statePtr->ctx, md_buf, &md_len)) { + /* Finalize hash function and calculate message digest */ + if (statePtr->ctx != NULL) { + res = EVP_DigestFinal_ex(statePtr->ctx, md_buf, &md_len); + } else { + res = HMAC_Final(statePtr->hctx, md_buf, &md_len); + } + if (!res) { *errorCodePtr = EINVAL; /* Write message digest to output channel as byte array or hex string */ } else if (md_len > 0) { if (statePtr->format == BIN_FORMAT) { @@ -609,15 +625,17 @@ * Adds transform to channel and sets result to channel name or error message. * *---------------------------------------------------------------------- */ static int -DigestChannel(Tcl_Interp *interp, const char *channel, const EVP_MD *md, int format) { - int mode; /* OR-ed combination of TCL_READABLE and TCL_WRITABLE */ +DigestChannel(Tcl_Interp *interp, const char *channel, const EVP_MD *md, int format, + Tcl_Obj *keyObj) { + int res, mode; /* OR-ed combination of TCL_READABLE and TCL_WRITABLE */ Tcl_Channel chan; - EVP_MD_CTX *ctx; 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; } @@ -628,39 +646,54 @@ } /* Make sure to operate on the topmost channel */ chan = Tcl_GetTopChannel(chan); - /* Create internal storage structures */ - ctx = EVP_MD_CTX_new(); + /* Create internal storage structure */ statePtr = (DigestState *) ckalloc((unsigned) sizeof(DigestState)); - if (ctx != NULL && statePtr != NULL) { + if (statePtr != NULL) { memset(statePtr, 0, sizeof(DigestState)); statePtr->self = chan; /* This socket channel */ statePtr->timer = (Tcl_TimerToken) NULL; /* Timer to flush data */ statePtr->flags = 0; /* Chan config flags */ statePtr->watchMask = 0; /* Current WatchProc mask */ statePtr->mode = mode; /* Current mode of parent channel */ statePtr->format = format; /* Output format */ statePtr->interp = interp; /* Current interpreter */ - statePtr->ctx = ctx; /* SSL Context */ + statePtr->ctx = ctx; /* MD Context */ + statePtr->hctx = hctx; /* HMAC Context */ + statePtr->mac = NULL; /* MAC Context */ } else { Tcl_AppendResult(interp, "Initialize digest error: memory allocation failure", (char *) NULL); - EVP_MD_CTX_free(ctx); + return TCL_ERROR; + } + + /* Create message digest context */ + if (keyObj == NULL) { + ctx = EVP_MD_CTX_new(); + } else { + hctx = HMAC_CTX_new(); + } + if (ctx != NULL || hctx != NULL) { + statePtr->ctx = ctx; + statePtr->hctx = hctx; + } else { + Tcl_AppendResult(interp, "Create digest context failed: ", REASON(), NULL); DigestFree(statePtr); return TCL_ERROR; } - /* Initialize digest */ -#if OPENSSL_VERSION_NUMBER < 0x30000000L - if (!EVP_DigestInit_ex(ctx, md, NULL)) -#else - if (!EVP_DigestInit_ex2(ctx, md, NULL)) -#endif - { - Tcl_AppendResult(interp, "Initialize digest error: ", REASON(), (char *) NULL); - EVP_MD_CTX_free(ctx); + /* Initialize hash function */ + if (keyObj == NULL) { + res = EVP_DigestInit_ex(ctx, md, NULL); + } else { + int key_len; + unsigned char *key = Tcl_GetByteArrayFromObj(keyObj, &key_len); + res = HMAC_Init_ex(hctx, (const void *) key, key_len, md, NULL); + } + if (!res) { + Tcl_AppendResult(interp, "Initialize digest failed: ", REASON(), (char *) NULL); DigestFree(statePtr); return TCL_ERROR; } /* Configure channel */ @@ -670,11 +703,10 @@ } /* Stack channel */ statePtr->self = Tcl_StackChannel(interp, &digestChannelType, (ClientData) statePtr, mode, chan); if (statePtr->self == (Tcl_Channel) NULL) { - EVP_MD_CTX_free(ctx); DigestFree(statePtr); return TCL_ERROR; } Tcl_SetResult(interp, (char *) Tcl_GetChannelName(chan), TCL_VOLATILE); @@ -768,14 +800,14 @@ Tcl_SetResult(interp, "No data", NULL); return TCL_ERROR; } /* Calculate digest based on hash function */ - if (keyObj == NULL) { + if (keyObj == (Tcl_Obj *) NULL) { res = EVP_Digest(data, (size_t) len, md_buf, &md_len, md, NULL); } else { - unsigned char *key, *hmac; + unsigned char *key, *hmac = NULL; int key_len; key = Tcl_GetByteArrayFromObj(keyObj, &key_len); hmac = HMAC(md, (const void *) key, key_len, (const unsigned char *) data, (size_t) len, md_buf, &md_len); @@ -820,11 +852,11 @@ * *------------------------------------------------------------------- */ 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; + int idx, len, format = HEX_FORMAT, key_len = 0, data_len = 0, res = TCL_OK; const char *digestname, *channel = NULL; Tcl_Obj *dataObj = NULL, *fileObj = NULL, *keyObj = NULL; unsigned char *key = NULL; const EVP_MD *md; @@ -874,22 +906,20 @@ dataObj = objv[idx]; } /* Calc digest on file, stacked channel, or data blob */ if (fileObj != NULL) { - return DigestFile(interp, fileObj, md, format, keyObj); + res = DigestFile(interp, fileObj, md, format, keyObj); } else if (channel != NULL) { - return DigestChannel(interp, channel, md, format); + res = DigestChannel(interp, channel, md, format, keyObj); } else if (dataObj != NULL) { Tcl_Obj *objs[2]; objs[0] = NULL; objs[1] = dataObj; - return DigestHashFunction(interp, 2, objs, md, format, keyObj); + res = DigestHashFunction(interp, 2, objs, md, format, keyObj); } - - Tcl_AppendResult(interp, "No data specified.", NULL); - return TCL_ERROR; + return res; } /* *------------------------------------------------------------------- * Index: tests/ciphers.csv ================================================================== --- tests/ciphers.csv +++ tests/ciphers.csv @@ -81,11 +81,11 @@ Digest Chan,md5 hex,,,read_chan md5 md_data.dat -hex,,,CCB1BE2E11D8183E843FF73DA8C6D206,,, ,,,,,,,,,, command,# Test HMAC,,,,,,,,, Digest HMAC,data,,,"tls::digest md5 -key ""Example key"" -data ""Example string for message digest tests.""",,,901DA6E6976A71650C77443C37FF9C7F,,, Digest HMAC,file,,,"tls::digest md5 -key ""Example key"" -file md_data.dat",,,901DA6E6976A71650C77443C37FF9C7F,,, -Digest HMAC,channel,knownBug,,"read_chan md5 md_data.dat -key ""Example key""",,,901DA6E6976A71650C77443C37FF9C7F,,, +Digest HMAC,channel,,,"read_chan md5 md_data.dat -key ""Example key""",,,901DA6E6976A71650C77443C37FF9C7F,,, Digest HMAC,data bin,,,"string toupper [binary encode hex [tls::digest md5 -bin -key ""Example key"" -data ""Example string for message digest tests.""]]",,,901DA6E6976A71650C77443C37FF9C7F,,, ,,,,,,,,,, command,# Test list MACs,,,,,,,,, MAC List,All,,,lcompare [exec_get_macs] [tls::macs],,,missing {} unexpected {},,, ,,,,,,,,,, Index: tests/ciphers.test ================================================================== --- tests/ciphers.test +++ tests/ciphers.test @@ -224,11 +224,11 @@ test Digest_HMAC-10.2 {file} -body { tls::digest md5 -key "Example key" -file md_data.dat } -result {901DA6E6976A71650C77443C37FF9C7F} -test Digest_HMAC-10.3 {channel} -constraints {knownBug} -body { +test Digest_HMAC-10.3 {channel} -body { read_chan md5 md_data.dat -key "Example key" } -result {901DA6E6976A71650C77443C37FF9C7F} test Digest_HMAC-10.4 {data bin} -body { string toupper [binary encode hex [tls::digest md5 -bin -key "Example key" -data "Example string for message digest tests."]]