Index: generic/tlsDigest.c
==================================================================
--- generic/tlsDigest.c
+++ generic/tlsDigest.c
@@ -1,9 +1,10 @@
 /*
- * Message Digests Module
+ * Message Digests (MD) and Message Authentication Code (MAC) Module
  *
- * Provides commands to calculate a message digest using a specified hash function.
+ * Provides commands to calculate a message digest (MD) or message
+ * authentication code (MAC) using a specified hash function and/or cipher.
  *
  * Copyright (C) 2023 Brian O'Hagan
  *
  */
 
@@ -18,19 +19,20 @@
 
 /* Constants */
 const char *hex = "0123456789abcdef";
 
 /* Macros */
-#define BUFFER_SIZE 65536
-#define CHAN_EOF 0x10
+#define BUFFER_SIZE	65536
+#define CHAN_EOF	0x10
+#define READ_DELAY	5
 
 /* Digest format and operation */
-#define BIN_FORMAT 0x01
-#define HEX_FORMAT 0x02
-#define TYPE_MD    0x10
-#define TYPE_HMAC  0x20
-#define TYPE_CMAC  0x40
+#define BIN_FORMAT	0x01
+#define HEX_FORMAT	0x02
+#define TYPE_MD		0x10
+#define TYPE_HMAC	0x20
+#define TYPE_CMAC	0x40
 
 /*
  * This structure defines the per-instance state of a digest operation.
  */
 typedef struct DigestState {
@@ -186,21 +188,21 @@
  * Side effects:
  *	Adds buf to hash function
  *
  *-------------------------------------------------------------------
  */
-int Tls_DigestUpdate(DigestState *statePtr, char *buf, size_t read, int show) {
+int Tls_DigestUpdate(DigestState *statePtr, char *buf, size_t read, int do_result) {
     int res = 0;
 
     if (statePtr->format & TYPE_MD) {
 	res = EVP_DigestUpdate(statePtr->ctx, buf, read);
     } else if (statePtr->format & TYPE_HMAC) {
 	res = HMAC_Update(statePtr->hctx, buf, read);
     } else if (statePtr->format & TYPE_CMAC) {
 	res = CMAC_Update(statePtr->cctx, buf, read);
     }
-    if (!res && show) {
+    if (!res && do_result) {
 	Tcl_AppendResult(statePtr->interp, "Update failed: ", REASON(), NULL);
 	return TCL_ERROR;
     }
     return res;
 }
@@ -578,11 +580,10 @@
  * Side effects:
  *	Configure notifier so future events on the channel will be seen by Tcl.
  *
  *----------------------------------------------------------------------
  */
-#define READ_DELAY	5
 void DigestWatchProc(ClientData clientData, int mask) {
     DigestState *statePtr = (DigestState *) clientData;
     Tcl_Channel parent;
     Tcl_DriverWatchProc *watchProc;
 
@@ -950,80 +951,37 @@
  *	Sets result to message digest or error message
  *
  *-------------------------------------------------------------------
  */
 int
-Tls_DigestData(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[],
-	const EVP_MD *md, const EVP_CIPHER *cipher, int format, Tcl_Obj *keyObj) {
+Tls_DigestData(Tcl_Interp *interp, Tcl_Obj *dataObj, const EVP_MD *md,
+	const EVP_CIPHER *cipher, int format, Tcl_Obj *keyObj) {
     char *data;
-    int data_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;
-    }
+    int data_len;
+    DigestState *statePtr;
 
     /* Get data */
-    data = Tcl_GetByteArrayFromObj(objv[1], &data_len);
+    data = Tcl_GetByteArrayFromObj(dataObj, &data_len);
     if (data == NULL || data_len == 0) {
 	Tcl_SetResult(interp, "No data", NULL);
 	return TCL_ERROR;
     }
 
-    /* Calculate digest based on hash function */
-    if (format & TYPE_MD) {
-	res = EVP_Digest(data, (size_t) data_len, md_buf, &md_len, md, NULL);
-
-    } else if (format & TYPE_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) data_len, md_buf, &md_len);
-	res = (hmac != NULL);
-
-    } else if (format & TYPE_CMAC) {
-	DigestState *statePtr;
-
-	if ((statePtr = Tls_DigestNew(interp, format)) == NULL) {
-	    Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL);
-	    return TCL_ERROR;
-	}
-	if (Tls_DigestInit(interp, statePtr, md, cipher, keyObj) != TCL_OK ||
-	    Tls_DigestUpdate(statePtr, data, (size_t) len, 1) == 0 ||
-	    Tls_DigestFinialize(interp, statePtr) != TCL_OK) {
-	    Tls_DigestFree(statePtr);
-	    return TCL_ERROR;
-	}
-	Tls_DigestFree(statePtr);
-	return TCL_OK;
-    }
-
-    /* Output digest to result per format (bin or hex) */
-    if (res) {
-	if (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);
-
-	    for (unsigned int i = 0; i < md_len; i++) {
-		*ptr++ = hex[(md_buf[i] >> 4) & 0x0F];
-		*ptr++ = hex[md_buf[i] & 0x0F];
-	    }
-	    Tcl_SetObjResult(interp, resultObj);
-	}
-
-    } else {
-	Tcl_AppendResult(interp, "Hash calculation error:", REASON(), (char *) NULL);
-	return TCL_ERROR;
-    }
+    /* Create state struct */
+    if ((statePtr = Tls_DigestNew(interp, format)) == NULL) {
+	Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL);
+	return TCL_ERROR;
+    }
+
+    /* Calc Digest */
+    if (Tls_DigestInit(interp, statePtr, md, cipher, keyObj) != TCL_OK ||
+	Tls_DigestUpdate(statePtr, data, (size_t) len, 1) == 0 ||
+	Tls_DigestFinialize(interp, statePtr) != TCL_OK) {
+	Tls_DigestFree(statePtr);
+	return TCL_ERROR;
+    }
+    Tls_DigestFree(statePtr);
     return TCL_OK;
 }
 
 /*******************************************************************/
 
@@ -1046,30 +1004,30 @@
 	const EVP_CIPHER *cipher, int format, Tcl_Obj *keyObj) {
     DigestState *statePtr;
     Tcl_Channel chan = NULL;
     unsigned char buf[BUFFER_SIZE];
     int res = TCL_OK, len;
+
+    /* Create state data struct */
+    if ((statePtr = Tls_DigestNew(interp, format)) == NULL) {
+	Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL);
+	return TCL_ERROR;
+    }
 
     /* Open file channel */
     chan = Tcl_FSOpenFileChannel(interp, filename, "rb", 0444);
     if (chan == (Tcl_Channel) NULL) {
+	Tls_DigestFree(statePtr);
 	return TCL_ERROR;
     }
 
     /* Configure channel */
     if ((res = Tcl_SetChannelOption(interp, chan, "-translation", "binary")) == TCL_ERROR) {
 	goto done;
     }
     Tcl_SetChannelBufferSize(chan, BUFFER_SIZE);
 
-    /* Create state data struct */
-    if ((statePtr = Tls_DigestNew(interp, format)) == NULL) {
-	Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL);
-	res = TCL_ERROR;
-	goto done;
-    }
-
     /* Initialize hash function */
     if ((res = Tls_DigestInit(interp, statePtr, md, cipher, keyObj)) != TCL_OK) {
 	goto done;
     }
 
@@ -1101,13 +1059,14 @@
 /*******************************************************************/
 
 /*
  *-------------------------------------------------------------------
  *
- * DigestObjCmd --
+ * DigestMain --
  *
- *	Return message digest using user specified hash function.
+ *	Return message digest or Message Authentication Code (MAC) of
+ *	data using user specified hash function.
  *
  * Returns:
  *	TCL_OK or TCL_ERROR
  *
  * Side effects:
@@ -1134,11 +1093,11 @@
 
     /* Optimal case for a digest and blob of data */
     if (objc == 3 && type == TYPE_MD) {
 	digestName = Tcl_GetStringFromObj(objv[1],NULL);
 	if ((md = EVP_get_digestbyname(digestName)) != NULL) {
-	    return Tls_DigestData(interp, --objc, ++objv, md, NULL, HEX_FORMAT | TYPE_MD, NULL);
+	    return Tls_DigestData(interp, objv[2], md, NULL, HEX_FORMAT | TYPE_MD, NULL);
 	} else {
 	    Tcl_AppendResult(interp, "Invalid digest \"", digestName, "\"", NULL);
 	    return TCL_ERROR;
 	}
     }
@@ -1216,14 +1175,11 @@
     } else if (channel != NULL) {
 	res = Tls_DigestChannel(interp, channel, md, cipher, format | type, keyObj);
     } else if (cmdObj != NULL) {
 	res = Tls_DigestInstance(interp, cmdObj, md, cipher, format | type, keyObj);
     } else if (dataObj != NULL) {
-	Tcl_Obj *objs[2];
-	objs[0] = NULL;
-	objs[1] = dataObj;
-	res = Tls_DigestData(interp, 2, objs, md, cipher, format | type, keyObj);
+	res = Tls_DigestData(interp, dataObj, md, cipher, format | type, keyObj);
     }
     return res;
 }
 
 /*
@@ -1267,27 +1223,47 @@
  *	Sets result to message digest or error message
  *
  *-------------------------------------------------------------------
  */
 int DigestMD4Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
-    return Tls_DigestData(interp, objc, objv, EVP_md4(), NULL, HEX_FORMAT | TYPE_MD, NULL);
+    if (objc != 2) {
+	Tcl_WrongNumArgs(interp, 1, objv, "data");
+	return TCL_ERROR;
+    }
+    return Tls_DigestData(interp, objv[1], EVP_md4(), NULL, HEX_FORMAT | TYPE_MD, NULL);
 }
 
 int DigestMD5Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
-    return Tls_DigestData(interp, objc, objv, EVP_md5(), NULL, HEX_FORMAT | TYPE_MD, NULL);
+    if (objc != 2) {
+	Tcl_WrongNumArgs(interp, 1, objv, "data");
+	return TCL_ERROR;
+    }
+    return Tls_DigestData(interp, objv[1], EVP_md5(), NULL, HEX_FORMAT | TYPE_MD, NULL);
 }
 
 int DigestSHA1Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
-    return Tls_DigestData(interp, objc, objv, EVP_sha1(), NULL, HEX_FORMAT | TYPE_MD, NULL);
+    if (objc != 2) {
+	Tcl_WrongNumArgs(interp, 1, objv, "data");
+	return TCL_ERROR;
+    }
+    return Tls_DigestData(interp, objv[1], EVP_sha1(), NULL, HEX_FORMAT | TYPE_MD, NULL);
 }
 
 int DigestSHA256Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
-    return Tls_DigestData(interp, objc, objv, EVP_sha256(), NULL, HEX_FORMAT | TYPE_MD, NULL);
+    if (objc != 2) {
+	Tcl_WrongNumArgs(interp, 1, objv, "data");
+	return TCL_ERROR;
+    }
+    return Tls_DigestData(interp, objv[1], EVP_sha256(), NULL, HEX_FORMAT | TYPE_MD, NULL);
 }
 
 int DigestSHA512Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
-    return Tls_DigestData(interp, objc, objv, EVP_sha512(), NULL, HEX_FORMAT | TYPE_MD, NULL);
+    if (objc != 2) {
+	Tcl_WrongNumArgs(interp, 1, objv, "data");
+	return TCL_ERROR;
+    }
+    return Tls_DigestData(interp, objv[1], EVP_sha512(), NULL, HEX_FORMAT | TYPE_MD, NULL);
 }
 
 /*
  *-------------------------------------------------------------------
  *