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