Index: doc/tls.html ================================================================== --- doc/tls.html +++ doc/tls.html @@ -35,11 +35,11 @@
tls::digests
tls::macs
tls::protocols
tls::version
 
-
tls::digest type ?-bin|-hex? ?-key hmac_key? [-file filename | -chan channel | ?-data? data]
+
tls::digest type ?options?
tls::md4 data
tls::md5 data
tls::sha1 data
tls::sha256 data
tls::sha512 data
@@ -77,11 +77,11 @@ tls::digests
tls::macs
tls::protocols
tls::version

-tls::digest type ?-bin|-hex? ?-key hmac_key? [-file filename | -chan channel | ?-data? data]
+tls::digest type ?options?
tls::md4 data
tls::md5 data
tls::sha1 data
tls::sha256 data
tls::sha512 data
@@ -459,22 +459,30 @@
tls::version
Returns the OpenSSL version string.

tls::digest type ?-bin|-hex? - ?-key hmac_key? [-file filename | -chan channel | ?-data? data]
+ ?-key hmac_key? [-file filename | -command cmdName | -chan channelId | ?-data? data]
Calculate the message digest for data or file filename using type hash algorithm. Returns value as a hex string - (default) or as a binary value with -bin option. Using - -chan option, a stacked channel is created and data read - from the channel is used to calculate a message digest with the result - returned with the last read operation before EOF. Use -key to - specify the key and return a Hashed Message Authentication Code (HMAC). - To salt a password, append or prepend the salt text to the password. - Type can be any OpenSSL supported hash algorithm including: md4, - md5, sha1, sha256, sha512, sha3-256, - etc. See tls::digests command for a full list.
+ (default) or as a binary value with -bin option. Use -key to + set the key and return a Hashed Message Authentication Code (HMAC). + To salt a password, append or prepend the salt data to the password. + Argument type can be any OpenSSL supported hash algorithm including: + md4, md5, sha1, sha256, sha512, + sha3-256, etc. See tls::digests command for a full list. +
+ Using the -chan option, a stacked channel is created and data + read from the channel is used to calculate a message digest with the + result returned with the last read operation before EOF. +
+ Using the -command option, a new command cmdName is + created and returned. To add data to the hash, call "cmdName + update data", where data is the data to add. When done, + call "cmdName finalize" to return the + message digest. +
tls::md4 data
Returns the MD4 message-digest for data as a hex string.
tls::md5 data
Index: generic/tlsDigest.c ================================================================== --- generic/tlsDigest.c +++ generic/tlsDigest.c @@ -38,28 +38,63 @@ int format; /* Output format */ Tcl_Interp *interp; /* Current interpreter */ EVP_MD_CTX *ctx; /* MD Context */ HMAC_CTX *hctx; /* HMAC Context */ + Tcl_Command token; /* Command token */ } DigestState; /* *------------------------------------------------------------------- * - * DigestFree -- + * Tls_DigestNew -- * - * This procedure removes a digest state structure + * This function creates a digest state structure + * + * Returns: + * Digest structure pointer + * + * Side effects: + * Creates structure + * + *------------------------------------------------------------------- + */ +DigestState *Tls_DigestNew(Tcl_Interp *interp, int format) { + DigestState *statePtr = NULL; + + statePtr = (DigestState *) ckalloc((unsigned) sizeof(DigestState)); + if (statePtr != NULL) { + memset(statePtr, 0, sizeof(DigestState)); + statePtr->self = NULL; /* This socket channel */ + statePtr->timer = NULL; /* Timer to flush data */ + statePtr->flags = 0; /* Chan config flags */ + statePtr->watchMask = 0; /* Current WatchProc mask */ + statePtr->mode = 0; /* Current mode of parent channel */ + statePtr->format = format; /* Output format */ + statePtr->interp = interp; /* Current interpreter */ + statePtr->ctx = NULL; /* MD Context */ + statePtr->hctx = NULL; /* HMAC Context */ + } + return statePtr; +} + +/* + *------------------------------------------------------------------- + * + * Tls_DigestFree -- + * + * This function removes a digest state structure * * Returns: * Nothing * * Side effects: * Removes structure * *------------------------------------------------------------------- */ -void DigestFree (DigestState *statePtr) { +void Tls_DigestFree(DigestState *statePtr) { if (statePtr == (DigestState *) NULL) return; if (statePtr->ctx != (EVP_MD_CTX *) NULL) { EVP_MD_CTX_free(statePtr->ctx); } @@ -72,111 +107,116 @@ /*******************************************************************/ /* *------------------------------------------------------------------- * - * DigestFile -- + * Tls_DigestInit -- * - * Return message digest for file using user specified hash function. + * Initialize a hash function * * Returns: - * TCL_OK or TCL_ERROR + * TCL_OK if successful or TCL_ERROR for failure with result set + * to error message. * * Side effects: - * Result is message digest or error message + * No result or error message * *------------------------------------------------------------------- */ -int DigestFile(Tcl_Interp *interp, Tcl_Obj *filename, const EVP_MD *md, int format, +int Tls_DigestInit(Tcl_Interp *interp, DigestState *statePtr, const EVP_MD *md, Tcl_Obj *keyObj) { - EVP_MD_CTX *ctx = (EVP_MD_CTX *) NULL; - HMAC_CTX *hctx = (HMAC_CTX *) NULL; - Tcl_Channel chan; - unsigned char buf[BUFFER_SIZE]; - unsigned char md_buf[EVP_MAX_MD_SIZE]; - unsigned int md_len; - unsigned char *key; int key_len, res; - - /* Open file channel */ - chan = Tcl_FSOpenFileChannel(interp, filename, "rb", 0444); - if (chan == (Tcl_Channel) NULL) { - return TCL_ERROR; - } - - /* Configure channel */ - if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") == TCL_ERROR) { - goto error; - } - Tcl_SetChannelBufferSize(chan, BUFFER_SIZE); + const unsigned char *key; /* Create message digest context */ if (keyObj == NULL) { - ctx = EVP_MD_CTX_new(); - res = (ctx != NULL); + statePtr->ctx = EVP_MD_CTX_new(); + res = (statePtr->ctx != NULL); } else { - hctx = HMAC_CTX_new(); - res = (hctx != NULL); + statePtr->hctx = HMAC_CTX_new(); + res = (statePtr->hctx != NULL); } if (!res) { Tcl_AppendResult(interp, "Create digest context failed: ", REASON(), NULL); - goto error; + return TCL_ERROR; } /* Initialize hash function */ if (keyObj == NULL) { - res = EVP_DigestInit_ex(ctx, md, NULL); + res = EVP_DigestInit_ex(statePtr->ctx, md, NULL); } else { key = Tcl_GetByteArrayFromObj(keyObj, &key_len); - res = HMAC_Init_ex(hctx, (const void *) key, key_len, md, NULL); + res = HMAC_Init_ex(statePtr->hctx, (const void *) key, key_len, md, NULL); } if (!res) { Tcl_AppendResult(interp, "Initialize digest failed: ", REASON(), NULL); - goto error; - } - - /* Read file data and update hash function */ - while (!Tcl_Eof(chan)) { - int len = Tcl_ReadRaw(chan, (char *) buf, BUFFER_SIZE); - 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) { - chan = (Tcl_Channel) NULL; - goto error; - } - chan = (Tcl_Channel) NULL; + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *------------------------------------------------------------------- + * + * Tls_DigestUpdate -- + * + * Update a hash function + * + * Returns: + * 1 if successful or 0 for failure + * + * Side effects: + * Adds buf to hash function + * + *------------------------------------------------------------------- + */ +int Tls_DigestUpdate(DigestState *statePtr, char *buf, size_t read) { + int res; + + if (statePtr->ctx != NULL) { + res = EVP_DigestUpdate(statePtr->ctx, buf, read); + } else { + res = HMAC_Update(statePtr->hctx, buf, read); + } + return res; +} + +/* + *------------------------------------------------------------------- + * + * Tls_DigestFinialize -- + * + * Finalize a hash function and generate a message digest + * + * Returns: + * TCL_OK if successful or TCL_ERROR for failure with result set + * to error message. + * + * Side effects: + * Sets result to message digest for hash function or an error message. + * + *------------------------------------------------------------------- + */ + +int Tls_DigestFinialize(Tcl_Interp *interp, DigestState *statePtr) { + unsigned char md_buf[EVP_MAX_MD_SIZE]; + unsigned int md_len; + int res; /* Finalize hash function and calculate message digest */ - if (keyObj == NULL) { - res = EVP_DigestFinal_ex(ctx, md_buf, &md_len); + if (statePtr->ctx != NULL) { + res = EVP_DigestFinal_ex(statePtr->ctx, md_buf, &md_len); } else { - res = HMAC_Final(hctx, md_buf, &md_len); + res = HMAC_Final(statePtr->hctx, md_buf, &md_len); } if (!res) { Tcl_AppendResult(interp, "Finalize digest failed: ", REASON(), NULL); - goto error; + return TCL_ERROR; } - /* Done with struct */ - EVP_MD_CTX_free(ctx); - ctx = NULL; - /* Return message digest as either a binary or hex string */ - if (format == BIN_FORMAT) { + if (statePtr->format == BIN_FORMAT) { Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(md_buf, md_len)); } else { Tcl_Obj *resultObj = Tcl_NewObj(); unsigned char *ptr = Tcl_SetByteArrayLength(resultObj, md_len*2); @@ -186,32 +226,97 @@ *ptr++ = hex[md_buf[i] & 0x0F]; } Tcl_SetObjResult(interp, resultObj); } return TCL_OK; - -error: - if (chan != (Tcl_Channel) NULL) { - Tcl_Close(interp, chan); - } - if (ctx != (EVP_MD_CTX *) NULL) { - EVP_MD_CTX_free(ctx); - } - if (hctx != (HMAC_CTX *) NULL) { - HMAC_CTX_free(hctx); - } - return TCL_ERROR; +} + +/*******************************************************************/ + + +/* + *------------------------------------------------------------------- + * + * Tls_DigestFile -- + * + * Return message digest for file using user specified hash function. + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * Result is message digest or error message + * + *------------------------------------------------------------------- + */ +int Tls_DigestFile(Tcl_Interp *interp, Tcl_Obj *filename, const EVP_MD *md, int format, + Tcl_Obj *keyObj) { + DigestState *statePtr; + Tcl_Channel chan = NULL; + unsigned char buf[BUFFER_SIZE]; + int res = TCL_OK, len; + + /* Open file channel */ + chan = Tcl_FSOpenFileChannel(interp, filename, "rb", 0444); + if (chan == (Tcl_Channel) NULL) { + return TCL_ERROR; + } + + /* Configure channel */ + if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") == TCL_ERROR) { + res = TCL_ERROR; + goto done; + } + Tcl_SetChannelBufferSize(chan, BUFFER_SIZE); + + /* Create struct */ + if ((statePtr = Tls_DigestNew(interp, format)) == NULL) { + res = TCL_ERROR; + goto done; + } + + /* Initialize hash function */ + if ((res = Tls_DigestInit(interp, statePtr, md, keyObj)) != TCL_OK) { + goto done; + } + + /* Read file data and update hash function */ + while (!Tcl_Eof(chan)) { + len = Tcl_ReadRaw(chan, (char *) buf, BUFFER_SIZE); + if (len > 0) { + if (!Tls_DigestUpdate(statePtr, &buf[0], (size_t) len)) { + Tcl_AppendResult(interp, "Update digest failed: ", REASON(), NULL); + res = TCL_ERROR; + goto done; + } + } + } + + /* Finalize hash function and calculate message digest */ + 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; } /*******************************************************************/ /* *------------------------------------------------------------------- * * DigestBlockModeProc -- * - * This procedure is invoked by the generic IO level + * This function is invoked by the generic IO level * to set blocking and nonblocking modes. * * Returns: * 0 if successful or POSIX error code if failed. * @@ -235,11 +340,11 @@ /* *------------------------------------------------------------------- * * DigestCloseProc -- * - * This procedure is invoked by the generic IO level to perform + * This function is invoked by the generic IO level to perform * channel-type-specific cleanup when channel is closed. All * queued output is flushed prior to calling this function. * * Returns: * 0 if successful or POSIX error code if failed. @@ -258,11 +363,11 @@ Tcl_DeleteTimerHandler(statePtr->timer); statePtr->timer = (Tcl_TimerToken) NULL; } /* Clean-up */ - DigestFree(statePtr); + Tls_DigestFree(statePtr); return 0; } /* * Same as DigestCloseProc but with individual read and write close control @@ -304,18 +409,13 @@ /* Get bytes from underlying channel */ parent = Tcl_GetStackedChannel(statePtr->self); read = Tcl_ReadRaw(parent, buf, toRead); - /* Add to message digest */ + /* Update hash function */ if (read > 0) { - 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) { + if (!Tls_DigestUpdate(statePtr, buf, (size_t) read)) { Tcl_SetChannelError(statePtr->self, Tcl_ObjPrintf("Digest update failed: %s", REASON())); *errorCodePtr = EINVAL; return -1; } /* This is correct */ @@ -341,15 +441,15 @@ 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) { + if (statePtr->format == BIN_FORMAT && toRead >= (int) md_len) { read = md_len; memcpy(buf, md_buf, read); - } else { + } else if(statePtr->format == HEX_FORMAT && toRead >= (int) (md_len*2)) { unsigned char hex_buf[EVP_MAX_MD_SIZE*2]; unsigned char *ptr = hex_buf; for (unsigned int i = 0; i < md_len; i++) { *ptr++ = hex[(md_buf[i] >> 4) & 0x0F]; @@ -381,23 +481,17 @@ *---------------------------------------------------------------------- */ int DigestOutputProc(ClientData clientData, const char *buf, int toWrite, int *errorCodePtr) { DigestState *statePtr = (DigestState *) clientData; *errorCodePtr = 0; - int res; if (toWrite <= 0 || statePtr->self == (Tcl_Channel) NULL) { return 0; } - /* Add to message digest */ - if (statePtr->ctx != NULL) { - res = EVP_DigestUpdate(statePtr->ctx, buf, (size_t) toWrite); - } else { - res = HMAC_Update(statePtr->hctx, buf, (size_t) toWrite); - } - if (!res) { + /* Update hash function */ + if (toWrite > 0 && !Tls_DigestUpdate(statePtr, buf, (size_t) toWrite)) { Tcl_SetChannelError(statePtr->self, Tcl_ObjPrintf("Digest update failed: %s", REASON())); *errorCodePtr = EINVAL; return -1; } return toWrite; @@ -635,11 +729,11 @@ }; /* *---------------------------------------------------------------------- * - * DigestChannel -- + * Tls_DigestChannel -- * * Create a stacked channel for a message digest transformation. * * Returns: * TCL_OK or TCL_ERROR @@ -648,13 +742,13 @@ * 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, +Tls_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 */ + 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; @@ -669,55 +763,20 @@ } /* Make sure to operate on the topmost channel */ chan = Tcl_GetTopChannel(chan); - /* Create internal storage structure */ - statePtr = (DigestState *) ckalloc((unsigned) sizeof(DigestState)); - 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; /* MD Context */ - statePtr->hctx = hctx; /* HMAC Context */ - statePtr->mac = NULL; /* MAC Context */ - } else { + /* Create struct */ + if ((statePtr = Tls_DigestNew(interp, format)) == NULL) { Tcl_AppendResult(interp, "Initialize digest error: memory allocation failure", (char *) NULL); 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; - } + statePtr->self = chan; + statePtr->mode = mode; /* 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); + if (Tls_DigestInit(interp, statePtr, md, keyObj) != TCL_OK) { return TCL_ERROR; } /* Configure channel */ Tcl_SetChannelOption(interp, chan, "-translation", "binary"); @@ -726,11 +785,11 @@ } /* Stack channel */ statePtr->self = Tcl_StackChannel(interp, &digestChannelType, (ClientData) statePtr, mode, chan); if (statePtr->self == (Tcl_Channel) NULL) { - DigestFree(statePtr); + Tls_DigestFree(statePtr); return TCL_ERROR; } Tcl_SetResult(interp, (char *) Tcl_GetChannelName(chan), TCL_VOLATILE); return TCL_OK; @@ -739,11 +798,11 @@ /* *---------------------------------------------------------------------- * * Unstack Channel -- * - * This procedure is invoked to process the "unstack" TCL command. + * This function is invoked to process the "unstack" TCL command. * See the user documentation for details on what it does. * * Returns: * TCL_OK or TCL_ERROR * @@ -787,14 +846,138 @@ clientData = clientData; } /*******************************************************************/ +static const char *instance_fns [] = { "finalize", "update", NULL }; + +/* + *------------------------------------------------------------------- + * + * InstanceObjCmd -- + * + * Handler for digest accumulator command instances. + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * Adds data to hash or returns message digest + * + *------------------------------------------------------------------- + */ +int InstanceObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + DigestState *statePtr = (DigestState *) clientData; + int fn, len = 0; + char *buf = NULL; + + if (objc < 2 || objc > 3) { + Tcl_WrongNumArgs(interp, 1, objv, "function ?data?"); + return TCL_ERROR; + } + + /* Function */ + if (Tcl_GetIndexFromObj(interp, objv[1], instance_fns, "function", 0, &fn) != TCL_OK) { + return TCL_ERROR; + } + + /* Do function */ + if (fn) { + /* Update hash function */ + if (objc == 3) { + buf = Tcl_GetByteArrayFromObj(objv[2], &len); + } else { + Tcl_WrongNumArgs(interp, 1, objv, "update data"); + return TCL_ERROR; + } + + if (!Tls_DigestUpdate(statePtr, buf, (size_t) len)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("Digest update failed: %s", REASON())); + return TCL_ERROR; + } + + } else { + /* Finalize hash function and calculate message digest */ + if (Tls_DigestFinialize(interp, statePtr) != TCL_OK) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("Digest finalize failed: %s", REASON())); + return TCL_ERROR; + } + + Tcl_DeleteCommandFromToken(interp, statePtr->token); + } + return TCL_OK; +} + +/* + *------------------------------------------------------------------- + * + * InstanceDelCallback -- + * + * Callback to clean-up when digest instance command is deleted. + * + * Returns: + * Nothing + * + * Side effects: + * Destroys struct + * + *------------------------------------------------------------------- + */ +void InstanceDelCallback(ClientData clientData) { + DigestState *statePtr = (DigestState *) clientData; + + /* Clean-up */ + Tls_DigestFree(statePtr); +} + +/* + *------------------------------------------------------------------- + * + * Tls_DigestInstance -- + * + * Create command to allow user to add data to hash function. + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * Creates command or error message + * + *------------------------------------------------------------------- + */ +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 */ + if ((statePtr = Tls_DigestNew(interp, format)) == NULL) { + Tcl_AppendResult(interp, "Initialize digest error: memory allocation failure", (char *) NULL); + return TCL_ERROR; + } + + /* Initialize hash function */ + if (Tls_DigestInit(interp, statePtr, md, keyObj) != TCL_OK) { + return TCL_ERROR; + } + + /* Create instance command */ + statePtr->token = Tcl_CreateObjCommand(interp, cmdName, InstanceObjCmd, + (ClientData) statePtr, InstanceDelCallback); + + /* Return command name */ + Tcl_SetObjResult(interp, cmdObj); + return TCL_OK; +} + + +/*******************************************************************/ + /* *------------------------------------------------------------------- * - * DigestHashFunction -- + * Tls_DigestData -- * * Calculate message digest using hash function. * * Returns: * TCL_OK or TCL_ERROR @@ -803,11 +986,11 @@ * Sets result to message digest or error message * *------------------------------------------------------------------- */ int -DigestHashFunction(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], +Tls_DigestData(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], const EVP_MD *md, int format, Tcl_Obj *keyObj) { char *data; int len, res; unsigned int md_len; unsigned char md_buf[EVP_MAX_MD_SIZE]; @@ -858,10 +1041,13 @@ return TCL_ERROR; } return TCL_OK; } + +/*******************************************************************/ + /* *------------------------------------------------------------------- * * DigestObjCmd -- * @@ -875,20 +1061,20 @@ * *------------------------------------------------------------------- */ 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_OK; + int idx, len, format = HEX_FORMAT, key_len = 0, data_len = 0, res = TCL_ERROR; const char *digestname, *channel = NULL; - Tcl_Obj *dataObj = NULL, *fileObj = NULL, *keyObj = NULL; + Tcl_Obj *cmdObj = NULL, *dataObj = NULL, *fileObj = NULL, *keyObj = NULL; unsigned char *key = NULL; const EVP_MD *md; Tcl_ResetResult(interp); if (objc < 3 || objc > 7) { - Tcl_WrongNumArgs(interp, 1, objv, "type ?-bin|-hex? ?-key hmac_key? [-channel chan | -file filename | ?-data? data]"); + Tcl_WrongNumArgs(interp, 1, objv, "type ?-bin|-hex? ?-key hmac_key? [-channel chan | -command cmdName | -file filename | ?-data? data]"); return TCL_ERROR; } /* Get digest */ digestname = Tcl_GetStringFromObj(objv[1], &len); @@ -897,11 +1083,11 @@ return TCL_ERROR; } /* Optimal case for blob of data */ if (objc == 3) { - return DigestHashFunction(interp, --objc, ++objv, md, format, NULL); + return Tls_DigestData(interp, --objc, ++objv, md, format, NULL); } /* Get options */ for (idx = 2; idx < objc-1; idx++) { char *opt = Tcl_GetStringFromObj(objv[idx], NULL); @@ -914,15 +1100,16 @@ OPTFLAG("-hex", format, HEX_FORMAT); OPTFLAG("-hexadecimal", format, HEX_FORMAT); OPTOBJ("-data", dataObj); OPTSTR("-chan", channel); OPTSTR("-channel", channel); + OPTOBJ("-command", cmdObj); OPTOBJ("-file", fileObj); OPTOBJ("-filename", fileObj); OPTOBJ("-key", keyObj); - OPTBAD("option", "-bin, -data, -file, -filename, -hex, or -key"); + OPTBAD("option", "-bin, -channel, -command, -data, -file, -filename, -hex, or -key"); return TCL_ERROR; } /* If no option for last arg, then its the data */ if (idx < objc) { @@ -929,18 +1116,20 @@ dataObj = objv[idx]; } /* Calc digest on file, stacked channel, or data blob */ if (fileObj != NULL) { - res = DigestFile(interp, fileObj, md, format, keyObj); + res = Tls_DigestFile(interp, fileObj, md, format, keyObj); } else if (channel != NULL) { - res = DigestChannel(interp, channel, md, format, keyObj); + res = Tls_DigestChannel(interp, channel, md, format, keyObj); + } else if (cmdObj != NULL) { + res = Tls_DigestInstance(interp, cmdObj, md, format, keyObj); } else if (dataObj != NULL) { Tcl_Obj *objs[2]; objs[0] = NULL; objs[1] = dataObj; - res = DigestHashFunction(interp, 2, objs, md, format, keyObj); + res = Tls_DigestData(interp, 2, objs, md, format, keyObj); } return res; } /* @@ -957,27 +1146,27 @@ * Sets result to message digest or error message * *------------------------------------------------------------------- */ int DigestMD4Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - return DigestHashFunction(interp, objc, objv, EVP_md4(), HEX_FORMAT, NULL); + return Tls_DigestData(interp, objc, objv, EVP_md4(), HEX_FORMAT, NULL); } int DigestMD5Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - return DigestHashFunction(interp, objc, objv, EVP_md5(), HEX_FORMAT, NULL); + return Tls_DigestData(interp, objc, objv, EVP_md5(), HEX_FORMAT, NULL); } int DigestSHA1Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - return DigestHashFunction(interp, objc, objv, EVP_sha1(), HEX_FORMAT, NULL); + return Tls_DigestData(interp, objc, objv, EVP_sha1(), HEX_FORMAT, NULL); } int DigestSHA256Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - return DigestHashFunction(interp, objc, objv, EVP_sha256(), HEX_FORMAT, NULL); + return Tls_DigestData(interp, objc, objv, EVP_sha256(), HEX_FORMAT, NULL); } int DigestSHA512Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - return DigestHashFunction(interp, objc, objv, EVP_sha512(), HEX_FORMAT, NULL); + return Tls_DigestData(interp, objc, objv, EVP_sha512(), HEX_FORMAT, NULL); } /* *------------------------------------------------------------------- * Index: tests/ciphers.csv ================================================================== --- tests/ciphers.csv +++ tests/ciphers.csv @@ -16,10 +16,11 @@ command,"proc exec_get_ciphers {} {set list [list];set data [exec openssl list -cipher-algorithms];foreach line [split $data ""\n""] {foreach {cipher null alias} [split [string trim $line]] {lappend list [string tolower $cipher]}};return [lsort -unique $list]}",,,,,,,,, command,"proc exec_get_digests {} {set list [list];set data [exec openssl dgst -list];foreach line [split $data ""\n""] {foreach digest $line {if {[string match ""-*"" $digest]} {lappend list [string trimleft $digest ""-""]}}};return [lsort $list]}",,,,,,,,, command,proc exec_get_macs {} {return [list cmac hmac]},,,,,,,,, command,proc list_tolower {list} {set result [list];foreach element $list {lappend result [string tolower $element]};return $result},,,,,,,,, command,proc read_chan {md filename args} {set ch [open $filename rb];fconfigure $ch -translation binary;set bsize [fconfigure $ch -buffersize];set new [tls::digest $md {*}$args -chan $ch];while {![eof $new]} {set result [read $new $bsize]};close $new;return $result},,,,,,,,, +command,proc accumulate {md string args} {set cmd [tls::digest $md {*}$args -command dcmd];$cmd update [string range $string 0 20];$cmd update [string range $string 21 end];return [$cmd finalize]},,,,,,,,, ,,,,,,,,,, command,# Test list ciphers,,,,,,,,, Ciphers List,All,,,lcompare [lsort [exec_get_ciphers]] [list_tolower [lsort [::tls::ciphers]]],,,missing {rc5 rc5-cbc rc5-cfb rc5-ecb rc5-ofb} unexpected {aes-128-ccm aes-128-gcm aes-192-ccm aes-192-gcm aes-256-ccm aes-256-gcm},,, ,,,,,,,,,, command,# Test list ciphers for protocols,,,,,,,,, @@ -82,10 +83,19 @@ Digest Chan,sha256,,,read_chan sha256 md_data.dat,,,B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491,,, Digest Chan,sha512,,,read_chan sha512 md_data.dat,,,B56EC55E33193E17B61D669FB7B04AD2483DE93FE847C411BBEAE6440ECEA6C7CFDD2E6F35A06CB189FC62D799E785CDB7A23178323789D001BC8E44A0B5907F,,, Digest Chan,md5 bin,,,string toupper [binary encode hex [read_chan md5 md_data.dat -bin]],,,CCB1BE2E11D8183E843FF73DA8C6D206,,, Digest Chan,md5 hex,,,read_chan md5 md_data.dat -hex,,,CCB1BE2E11D8183E843FF73DA8C6D206,,, ,,,,,,,,,, +command,# Test digest command for accumulator command,,,,,,,,, +Digest Command,md4,,,"accumulate md4 ""Example string for message digest tests.""",,,181CDCF9DB9B6FA8FC0A3BF9C34E29D9,,, +Digest Command,md5,,,"accumulate md5 ""Example string for message digest tests.""",,,CCB1BE2E11D8183E843FF73DA8C6D206,,, +Digest Command,sha1,,,"accumulate sha1 ""Example string for message digest tests.""",,,3AEFE840CA492C387E903F15ED6019E7AD833B47,,, +Digest Command,sha256,,,"accumulate sha256 ""Example string for message digest tests.""",,,B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491,,, +Digest Command,sha512,,,"accumulate sha512 ""Example string for message digest tests.""",,,B56EC55E33193E17B61D669FB7B04AD2483DE93FE847C411BBEAE6440ECEA6C7CFDD2E6F35A06CB189FC62D799E785CDB7A23178323789D001BC8E44A0B5907F,,, +Digest Command,md5 bin,,,"string toupper [binary encode hex [accumulate md5 ""Example string for message digest tests."" -bin]]",,,CCB1BE2E11D8183E843FF73DA8C6D206,,, +Digest Command,md5 hex,,,"accumulate md5 ""Example string for message digest tests."" -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,,,"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,,, Index: tests/ciphers.test ================================================================== --- tests/ciphers.test +++ tests/ciphers.test @@ -23,10 +23,11 @@ proc exec_get {delim args} {return [split [exec openssl {*}$args] $delim]} proc exec_get_ciphers {} {set list [list];set data [exec openssl list -cipher-algorithms];foreach line [split $data "\n"] {foreach {cipher null alias} [split [string trim $line]] {lappend list [string tolower $cipher]}};return [lsort -unique $list]} proc exec_get_digests {} {set list [list];set data [exec openssl dgst -list];foreach line [split $data "\n"] {foreach digest $line {if {[string match "-*" $digest]} {lappend list [string trimleft $digest "-"]}}};return [lsort $list]} command,proc exec_get_macs {} {return [list cmac hmac]},,,,,,,,, proc read_chan {md filename args} {set ch [open $filename rb];fconfigure $ch -translation binary;set bsize [fconfigure $ch -buffersize];set new [tls::digest $md {*}$args -chan $ch];while {![eof $new]} {set result [read $new $bsize]};close $new;return $result} +proc accumulate {md string args} {set cmd [tls::digest $md {*}$args -command dcmd];$cmd update [string range $string 0 20];$cmd update [string range $string 21 end];return [$cmd finalize]} # Test list ciphers test Ciphers_List-1.1 {All} -body { lcompare [lsort [exec_get_ciphers]] [list_tolower [lsort [::tls::ciphers]]] @@ -229,49 +230,79 @@ } -result {CCB1BE2E11D8183E843FF73DA8C6D206} test Digest_Chan-9.7 {md5 hex} -body { read_chan md5 md_data.dat -hex } -result {CCB1BE2E11D8183E843FF73DA8C6D206} +# Test digest command for accumulator command + + +test Digest_Command-10.1 {md4} -body { + accumulate md4 "Example string for message digest tests." + } -result {181CDCF9DB9B6FA8FC0A3BF9C34E29D9} + +test Digest_Command-10.2 {md5} -body { + accumulate md5 "Example string for message digest tests." + } -result {CCB1BE2E11D8183E843FF73DA8C6D206} + +test Digest_Command-10.3 {sha1} -body { + accumulate sha1 "Example string for message digest tests." + } -result {3AEFE840CA492C387E903F15ED6019E7AD833B47} + +test Digest_Command-10.4 {sha256} -body { + accumulate sha256 "Example string for message digest tests." + } -result {B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491} + +test Digest_Command-10.5 {sha512} -body { + accumulate sha512 "Example string for message digest tests." + } -result {B56EC55E33193E17B61D669FB7B04AD2483DE93FE847C411BBEAE6440ECEA6C7CFDD2E6F35A06CB189FC62D799E785CDB7A23178323789D001BC8E44A0B5907F} + +test Digest_Command-10.6 {md5 bin} -body { + string toupper [binary encode hex [accumulate md5 "Example string for message digest tests." -bin]] + } -result {CCB1BE2E11D8183E843FF73DA8C6D206} + +test Digest_Command-10.7 {md5 hex} -body { + accumulate md5 "Example string for message digest tests." -hex + } -result {CCB1BE2E11D8183E843FF73DA8C6D206} # Test HMAC -test Digest_HMAC-10.1 {data} -body { +test Digest_HMAC-11.1 {data} -body { tls::digest md5 -key "Example key" -data "Example string for message digest tests." } -result {901DA6E6976A71650C77443C37FF9C7F} -test Digest_HMAC-10.2 {file} -body { +test Digest_HMAC-11.2 {file} -body { tls::digest md5 -key "Example key" -file md_data.dat } -result {901DA6E6976A71650C77443C37FF9C7F} -test Digest_HMAC-10.3 {channel} -body { +test Digest_HMAC-11.3 {channel} -body { read_chan md5 md_data.dat -key "Example key" } -result {901DA6E6976A71650C77443C37FF9C7F} -test Digest_HMAC-10.4 {data bin} -body { +test Digest_HMAC-11.4 {data bin} -body { string toupper [binary encode hex [tls::digest md5 -bin -key "Example key" -data "Example string for message digest tests."]] } -result {901DA6E6976A71650C77443C37FF9C7F} # Test list MACs -test MAC_List-11.1 {All} -body { +test MAC_List-12.1 {All} -body { lcompare [exec_get_macs] [tls::macs] } -result {missing {} unexpected {}} # Test list protocols -test Protocols-12.1 {All} -body { +test Protocols-13.1 {All} -body { lcompare $protocols [::tls::protocols] } -result {missing {ssl2 ssl3} unexpected {}} # Test show version -test Version-13.1 {All} -body { +test Version-14.1 {All} -body { ::tls::version } -match {glob} -result {*} -test Version-13.2 {OpenSSL} -constraints {OpenSSL} -body { +test Version-14.2 {OpenSSL} -constraints {OpenSSL} -body { ::tls::version } -match {glob} -result {OpenSSL*} # Cleanup ::tcltest::cleanupTests return