Index: doc/tls.html
==================================================================
--- doc/tls.html
+++ doc/tls.html
@@ -545,10 +545,20 @@
shared secret key. The cryptographic strength depends upon the size of
the key and the security of the hash function used. It uses the same
options as tls::digest, plus additional option -key to
specify the key to use. To salt a password, append or prepend the salt
data to the password.
+
+
tls::mac -cipher name
+ -digest name -key key ?-bin|-hex?
+ [-file filename | -command cmdName |
+ -chan channelId | -data data]
+ (OpenSSL 3.0+) Calculate the Message Authentication Code (MAC). MACs
+ are used to ensure authenticity and the integrity of data. It uses the
+ same options as tls::digest, plus the additional options
+ -cipher to specify the cipher to use, -digest to specify
+ the digest, and for certain ciphers, -key to specify the key.
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
@@ -1099,52 +1099,41 @@
*-------------------------------------------------------------------
*/
static int DigestMain(int type, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
int idx, start = 1, format = HEX_FORMAT, res = TCL_OK;
Tcl_Obj *cmdObj = NULL, *dataObj = NULL, *fileObj = NULL, *keyObj = NULL;
- const char *digestName = NULL, *cipherName = NULL, *channel = NULL, *opt;
+ const char *digestName = NULL, *cipherName = NULL, *macName = NULL, *channel = NULL, *opt;
const EVP_MD *md = NULL;
const EVP_CIPHER *cipher = NULL;
/* Clear interp result */
Tcl_ResetResult(interp);
/* Validate arg count */
if (objc < 3 || objc > 12) {
- Tcl_WrongNumArgs(interp, 1, objv, "?-bin|-hex? ?-cipher name? ?-digest name? ?-key key? [-channel chan | -command cmdName | -file filename | ?-data? data]");
- return TCL_ERROR;
- }
-
- /* 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 DigestDataHandler(interp, objv[2], md, NULL, HEX_FORMAT | TYPE_MD, NULL);
- } else {
- Tcl_AppendResult(interp, "Invalid digest \"", digestName, "\"", NULL);
- return TCL_ERROR;
- }
- } else {
- /* Special case if first arg is digest, cipher, or mac */
- opt = Tcl_GetStringFromObj(objv[start], NULL);
- if (opt[0] != '-') {
- if (type == TYPE_MD || type == TYPE_HMAC) {
- digestName = opt;
- start++;
- } else if (type == TYPE_CMAC) {
- cipherName = opt;
- start++;
- } else if (type == TYPE_MAC) {
- macName = opt;
- start++;
- }
+ Tcl_WrongNumArgs(interp, 1, objv, "?-bin|-hex? ?-cipher name? ?-digest name? ?-key key? ?-mac name? [-channel chan | -command cmdName | -file filename | ?-data? data]");
+ return TCL_ERROR;
+ }
+
+ /* Special case of first arg is digest, cipher, or mac */
+ opt = Tcl_GetStringFromObj(objv[start], NULL);
+ if (opt[0] != '-') {
+ if (type == TYPE_MD || type == TYPE_HMAC) {
+ digestName = opt;
+ start++;
+ } else if (type == TYPE_CMAC) {
+ cipherName = opt;
+ start++;
+ } else if (type == TYPE_MAC) {
+ macName = opt;
+ start++;
}
}
/* Get options */
for (idx = start; idx < objc; idx++) {
- *opt = Tcl_GetStringFromObj(objv[idx], NULL);
+ opt = Tcl_GetStringFromObj(objv[idx], NULL);
if (opt[0] != '-') {
break;
}
@@ -1159,14 +1148,20 @@
OPTOBJ("-data", dataObj);
OPTSTR("-digest", digestName);
OPTOBJ("-file", fileObj);
OPTOBJ("-filename", fileObj);
OPTOBJ("-key", keyObj);
+ OPTSTR("-mac", macName);
- OPTBAD("option", "-bin, -channel, -cipher, -command, -data, -digest, -file, -filename, -hex, or -key");
+ OPTBAD("option", "-bin, -channel, -cipher, -command, -data, -digest, -file, -filename, -hex, -key, or -mac");
return TCL_ERROR;
}
+
+ /* If only 1 arg left, it's the data */
+ if (idx < objc && dataObj == NULL) {
+ dataObj = objv[idx];
+ }
/* Get cipher */
if (cipherName != NULL) {
cipher = EVP_get_cipherbyname(cipherName);
type = TYPE_CMAC;
@@ -1178,11 +1173,11 @@
} else if (type == TYPE_CMAC) {
Tcl_AppendResult(interp, "No cipher specified", NULL);
return TCL_ERROR;
}
- /* Get digest */
+ /* Get message digest */
if (digestName != NULL) {
md = EVP_get_digestbyname(digestName);
if (md == NULL) {
Tcl_AppendResult(interp, "Invalid digest \"", digestName, "\"", NULL);
return TCL_ERROR;
@@ -1197,14 +1192,30 @@
/* Get key */
if (keyObj != NULL) {
if (type == TYPE_MD) {
type = TYPE_HMAC;
}
+
} else if (type != TYPE_MD) {
Tcl_AppendResult(interp, "No key specified", NULL);
return TCL_ERROR;
}
+
+ /* Get MAC */
+ if (macName != NULL) {
+ if (strcmp(macName, "cmac") == 0) {
+ type = TYPE_CMAC;
+ } else if (strcmp(macName, "hmac") == 0) {
+ type = TYPE_HMAC;
+ } else {
+ Tcl_AppendResult(interp, "Invalid MAC \"", macName, "\"", NULL);
+ return TCL_ERROR;
+ }
+ } else if (type == TYPE_MAC) {
+ Tcl_AppendResult(interp, "No MAC specified", NULL);
+ return TCL_ERROR;
+ }
/* Calc digest on file, stacked channel, using instance command, or data blob */
if (fileObj != NULL) {
res = DigestFileHandler(interp, fileObj, md, cipher, format | type, keyObj);
} else if (channel != NULL) {
@@ -1244,10 +1255,14 @@
}
static int HMACObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
return DigestMain(TYPE_HMAC, interp, objc, objv);
}
+
+static int MACObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
+ return DigestMain(TYPE_MAC, interp, objc, objv);
+}
/*
*-------------------------------------------------------------------
*
* Message Digest Convenience Commands --
@@ -1312,14 +1327,15 @@
int Tls_DigestCommands(Tcl_Interp *interp) {
Tcl_CreateObjCommand(interp, "tls::digest", MdObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::md", MdObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::cmac", CMACObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::hmac", HMACObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+ Tcl_CreateObjCommand(interp, "tls::mac", MACObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::md4", MD4ObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::md5", MD5ObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::sha1", SHA1ObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::sha256", SHA256ObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::sha512", SHA512ObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::unstack", DigestUnstackObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
return TCL_OK;
}