Index: generic/tlsDigest.c ================================================================== --- generic/tlsDigest.c +++ generic/tlsDigest.c @@ -23,13 +23,14 @@ /* Macros */ #define BUFFER_SIZE 65536 #define CHAN_EOF 0x10 #define READ_DELAY 5 -/* Digest format and operation */ +/* Digest format (bits 0-3) and operation (bits 4-7) */ #define BIN_FORMAT 0x01 #define HEX_FORMAT 0x02 +/*#define B64_FORMAT 0x04*/ #define IS_XOF 0x08 #define TYPE_MD 0x10 #define TYPE_HMAC 0x20 #define TYPE_CMAC 0x40 #define TYPE_MAC 0x80 @@ -45,10 +46,11 @@ int flags; /* Chan config flags */ int watchMask; /* Current WatchProc mask */ int mode; /* Current mode of parent channel */ int format; /* Digest format and operation */ + int length; /* Digest length in bytes */ Tcl_Interp *interp; /* Current interpreter */ EVP_MD_CTX *ctx; /* MD Context */ HMAC_CTX *hctx; /* HMAC context */ CMAC_CTX *cctx; /* CMAC context */ @@ -68,11 +70,11 @@ * Side effects: * Creates structure * *------------------------------------------------------------------- */ -DigestState *DigestStateNew(Tcl_Interp *interp, int format) { +DigestState *DigestStateNew(Tcl_Interp *interp, int format, int length) { DigestState *statePtr; statePtr = (DigestState *) ckalloc((unsigned) sizeof(DigestState)); if (statePtr != NULL) { memset(statePtr, 0, sizeof(DigestState)); @@ -80,10 +82,11 @@ 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; /* Digest format and operation */ + statePtr->length = length; /* Digest length in bytes */ statePtr->interp = interp; /* Current interpreter */ statePtr->ctx = NULL; /* MD Context */ statePtr->hctx = NULL; /* HMAC Context */ statePtr->cctx = NULL; /* CMAC Context */ statePtr->token = NULL; /* Command token */ @@ -156,25 +159,36 @@ Tcl_Size key_len = 0; dprintf("Called"); /* Get digest */ - md = Util_GetDigest(interp, digestObj, type != TYPE_CMAC); - if (md == NULL && type != TYPE_CMAC) { - return TCL_ERROR; + if (type != TYPE_CMAC) { + md = Util_GetDigest(interp, digestObj, type != TYPE_CMAC); + if (md != NULL) { + /* Is XOF */ + if (EVP_MD_flags(md) & EVP_MD_FLAG_XOF) { + statePtr->format = statePtr->format | IS_XOF; + } + } else { + return TCL_ERROR; + } } /* Get cipher */ - cipher = Util_GetCipher(interp, cipherObj, type == TYPE_CMAC); - if (cipher == NULL && type == TYPE_CMAC) { - return TCL_ERROR; + if (type == TYPE_CMAC) { + cipher = Util_GetCipher(interp, cipherObj, type == TYPE_CMAC); + if (cipher == NULL) { + return TCL_ERROR; + } } /* Get key */ - key = (const void *) Util_GetKey(interp, keyObj, &key_len, "key", 0, type != TYPE_MD); - if (key == NULL && type != TYPE_MD) { - return TCL_ERROR; + if (type != TYPE_MD) { + key = (const void *) Util_GetKey(interp, keyObj, &key_len, "key", 0, type != TYPE_MD); + if (key == NULL) { + return TCL_ERROR; + } } /* Create contexts */ switch(type) { case TYPE_MD: @@ -194,11 +208,11 @@ if (!res) { Tcl_AppendResult(interp, "Create context failed", (char *) NULL); return TCL_ERROR; } - /* Initialize cryptography function */ + /* Initialize hash function */ switch(type) { case TYPE_MD: res = EVP_DigestInit_ex(statePtr->ctx, md, NULL); break; case TYPE_HMAC: @@ -235,10 +249,11 @@ int DigestUpdate(DigestState *statePtr, char *buf, Tcl_Size read, int do_result) { int res = 0; dprintf("Called"); + /* Update hash function */ switch(statePtr->format & 0xFF0) { case TYPE_MD: res = EVP_DigestUpdate(statePtr->ctx, buf, (size_t) read); break; case TYPE_HMAC: @@ -277,30 +292,32 @@ unsigned int ulen; int res = 0, md_len = 0, type = statePtr->format & 0xFF0; dprintf("Called"); - /* Finalize cryptography function and get result */ + /* Finalize hash function and get result */ switch(type) { case TYPE_MD: - if (!(statePtr->format & IS_XOF)) { + if (!(statePtr->format & IS_XOF) || statePtr->length == 0) { + /* Non XOF or XOF with default length */ res = EVP_DigestFinal_ex(statePtr->ctx, md_buf, &ulen); md_len = (int) ulen; } else { - res = EVP_DigestFinalXOF(statePtr->ctx, md_buf, (size_t) EVP_MAX_MD_SIZE); - md_len = EVP_MAX_MD_SIZE; + /* XOF with custom length */ + md_len = statePtr->length < EVP_MAX_MD_SIZE ? statePtr->length : EVP_MAX_MD_SIZE; + res = EVP_DigestFinalXOF(statePtr->ctx, md_buf, (size_t) md_len); } break; case TYPE_HMAC: res = HMAC_Final(statePtr->hctx, md_buf, &ulen); md_len = (int) ulen; break; case TYPE_CMAC: { - size_t size; - res = CMAC_Final(statePtr->cctx, md_buf, &size); - md_len = (int) size; + size_t length; + res = CMAC_Final(statePtr->cctx, md_buf, &length); + md_len = (int) length; break; } } if (!res) { @@ -780,11 +797,11 @@ * Adds transform to channel and sets result to channel id or error message. * *---------------------------------------------------------------------- */ static int DigestChannelHandler(Tcl_Interp *interp, const char *channel, Tcl_Obj *digestObj, - Tcl_Obj *cipherObj, int format, Tcl_Obj *keyObj, Tcl_Obj *macObj) { + Tcl_Obj *cipherObj, int format, Tcl_Obj *keyObj, Tcl_Obj *macObj, int length) { int mode; /* OR-ed combination of TCL_READABLE and TCL_WRITABLE */ Tcl_Channel chan; DigestState *statePtr; dprintf("Called"); @@ -809,11 +826,11 @@ if (Tcl_GetChannelBufferSize(chan) < EVP_MAX_MD_SIZE * 2) { Tcl_SetChannelBufferSize(chan, EVP_MAX_MD_SIZE * 2); } /* Create state data structure */ - if ((statePtr = DigestStateNew(interp, format)) == NULL) { + if ((statePtr = DigestStateNew(interp, format, length)) == NULL) { Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); return TCL_ERROR; } statePtr->self = chan; statePtr->mode = mode; @@ -986,18 +1003,18 @@ * Creates command or error message * *------------------------------------------------------------------- */ int DigestCommandHandler(Tcl_Interp *interp, Tcl_Obj *cmdObj, Tcl_Obj *digestObj, - Tcl_Obj *cipherObj, int format, Tcl_Obj *keyObj, Tcl_Obj *macObj) { + Tcl_Obj *cipherObj, int format, Tcl_Obj *keyObj, Tcl_Obj *macObj, int length) { DigestState *statePtr; char *cmdName = Tcl_GetString(cmdObj); dprintf("Called"); /* Create state data structure */ - if ((statePtr = DigestStateNew(interp, format)) == NULL) { + if ((statePtr = DigestStateNew(interp, format, length)) == NULL) { Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); return TCL_ERROR; } /* Initialize hash function */ @@ -1035,11 +1052,11 @@ * Sets result to message digest or error message * *------------------------------------------------------------------- */ int DigestDataHandler(Tcl_Interp *interp, Tcl_Obj *dataObj, Tcl_Obj *digestObj, - Tcl_Obj *cipherObj, int format, Tcl_Obj *keyObj, Tcl_Obj *macObj) { + Tcl_Obj *cipherObj, int format, Tcl_Obj *keyObj, Tcl_Obj *macObj, int length) { unsigned char *data; Tcl_Size data_len; DigestState *statePtr; dprintf("Called"); @@ -1050,11 +1067,11 @@ Tcl_SetResult(interp, "No data", NULL); return TCL_ERROR; } /* Create state data structure */ - if ((statePtr = DigestStateNew(interp, format)) == NULL) { + if ((statePtr = DigestStateNew(interp, format, length)) == NULL) { Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); return TCL_ERROR; } /* Calc Digest */ @@ -1086,20 +1103,20 @@ * Result is message digest or error message * *------------------------------------------------------------------- */ int DigestFileHandler(Tcl_Interp *interp, Tcl_Obj *inFileObj, Tcl_Obj *digestObj, - Tcl_Obj *cipherObj, int format, Tcl_Obj *keyObj, Tcl_Obj *macObj) { + Tcl_Obj *cipherObj, int format, Tcl_Obj *keyObj, Tcl_Obj *macObj, int length) { DigestState *statePtr; Tcl_Channel chan = NULL; unsigned char buf[BUFFER_SIZE]; int res = TCL_OK; dprintf("Called"); /* Create state data structure */ - if ((statePtr = DigestStateNew(interp, format)) == NULL) { + if ((statePtr = DigestStateNew(interp, format, length)) == NULL) { Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); return TCL_ERROR; } /* Open file channel */ @@ -1146,15 +1163,16 @@ /*******************************************************************/ static const char *command_opts [] = { "-bin", "-binary", "-hex", "-hexadecimal", "-chan", "-channel", "-cipher", "-command", "-data", "-digest", "-file", "-filename", - "-hash", "-key", "-mac", NULL}; + "-hash", "-key", "-length", "-mac", "-size", NULL}; enum _command_opts { _opt_bin, _opt_binary, _opt_hex, _opt_hexadecimal, _opt_chan, _opt_channel, _opt_cipher, - _opt_command, _opt_data, _opt_digest, _opt_file, _opt_filename, _opt_hash, _opt_key, _opt_mac + _opt_command, _opt_data, _opt_digest, _opt_file, _opt_filename, _opt_hash, _opt_key, + _opt_length, _opt_mac, _opt_size }; /* *------------------------------------------------------------------- * @@ -1170,11 +1188,13 @@ * Sets result to message digest or error message * *------------------------------------------------------------------- */ static int DigestMain(int type, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - int start = 1, format = HEX_FORMAT, res = TCL_OK; + int format = HEX_FORMAT; /* Output format */ + int length = 0; /* MD length, where 0=default size */ + int start = 1, res = TCL_OK; Tcl_Size fn; Tcl_Obj *cipherObj = NULL, *cmdObj = NULL, *dataObj = NULL, *digestObj = NULL; Tcl_Obj *fileObj = NULL, *keyObj = NULL, *macObj = NULL; const char *channel = NULL, *opt; @@ -1260,10 +1280,18 @@ case _opt_filename: fileObj = objv[idx]; break; case _opt_key: keyObj = objv[idx]; + break; + case _opt_length: + case _opt_size: + GET_OPT_INT(objv[idx], &length); + if (length < 1 || length > EVP_MAX_MD_SIZE) { + Tcl_AppendResult(interp, "Invalid length", (char *) NULL); + return TCL_ERROR; + } break; case _opt_mac: macObj = objv[idx]; break; } @@ -1278,10 +1306,11 @@ } else if (keyObj != NULL) { type = TYPE_HMAC; } } + /* Convert MAC to CMAC or HMAC type */ if (type == TYPE_MAC) { if (macObj != NULL) { char *macName = Tcl_GetString(macObj); if (strcmp(macName,"cmac") == 0) { type = TYPE_CMAC; @@ -1297,19 +1326,24 @@ } } /* Calc digest on file, stacked channel, using instance command, or data blob */ if (fileObj != NULL) { - res = DigestFileHandler(interp, fileObj, digestObj, cipherObj, format | type, keyObj, macObj); + res = DigestFileHandler(interp, fileObj, digestObj, cipherObj, format | type, keyObj, + macObj, length); } else if (channel != NULL) { - res = DigestChannelHandler(interp, channel, digestObj, cipherObj, format | type, keyObj, macObj); + res = DigestChannelHandler(interp, channel, digestObj, cipherObj, format | type, keyObj, + macObj, length); } else if (cmdObj != NULL) { - res = DigestCommandHandler(interp, cmdObj, digestObj, cipherObj, format | type, keyObj, macObj); + res = DigestCommandHandler(interp, cmdObj, digestObj, cipherObj, format | type, keyObj, + macObj, length); } else if (dataObj != NULL) { - res = DigestDataHandler(interp, dataObj, digestObj, cipherObj, format | type, keyObj, macObj); + res = DigestDataHandler(interp, dataObj, digestObj, cipherObj, format | type, keyObj, + macObj, length); } else { - Tcl_AppendResult(interp, "No operation: Use -channel, -command, -data, or -file option", (char *) NULL); + Tcl_AppendResult(interp, "No operation: Use -channel, -command, -data, or -file option", + (char *) NULL); res = TCL_ERROR; } return res; } @@ -1374,11 +1408,11 @@ return TCL_ERROR; } digestObj = Tcl_NewStringObj(digestName, -1); Tcl_IncrRefCount(digestObj); - res = DigestDataHandler(interp, dataObj, digestObj, NULL, format, NULL, NULL); + res = DigestDataHandler(interp, dataObj, digestObj, NULL, format, NULL, NULL, EVP_MAX_MD_SIZE); Tcl_DecrRefCount(digestObj); return res; } int MD4ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { @@ -1421,10 +1455,11 @@ * *------------------------------------------------------------------- */ int Tls_DigestCommands(Tcl_Interp *interp) { Tcl_CreateObjCommand(interp, "::tls::digest", MdObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateObjCommand(interp, "::tls::hash", MdObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "::tls::md", MdObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "::tls::cmac", CMACObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "::tls::hmac", HMACObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "::tls::mac", MACObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "::tls::md4", MD4ObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);