Index: generic/tlsDigest.c ================================================================== --- generic/tlsDigest.c +++ generic/tlsDigest.c @@ -151,10 +151,12 @@ */ int DigestInitialize(Tcl_Interp *interp, DigestState *statePtr, const EVP_MD *md, const EVP_CIPHER *cipher, Tcl_Obj *keyObj, EVP_MAC *mac) { int key_len = 0, res = 0; const unsigned char *key = NULL; + + dprintf("Called"); /* Create contexts */ switch(statePtr->format & 0xFF0) { case TYPE_MD: statePtr->ctx = EVP_MD_CTX_new(); @@ -206,19 +208,22 @@ * DigestUpdate -- * * Update a hash function with data * * Returns: - * 1 if successful or 0 for failure + * TCL_OK if successful or TCL_ERROR for failure with result set + * to error message if do_result is true. * * Side effects: * Adds buf data to hash function or sets result to error message * *------------------------------------------------------------------- */ int DigestUpdate(DigestState *statePtr, char *buf, size_t read, int do_result) { int res = 0; + + dprintf("Called"); switch(statePtr->format & 0xFF0) { case TYPE_MD: res = EVP_DigestUpdate(statePtr->ctx, buf, read); break; @@ -232,11 +237,11 @@ if (!res && do_result) { Tcl_AppendResult(statePtr->interp, "Update failed: ", REASON(), NULL); return TCL_ERROR; } - return res; + return TCL_OK; } /* *------------------------------------------------------------------- * @@ -255,10 +260,12 @@ */ int DigestFinalize(Tcl_Interp *interp, DigestState *statePtr, Tcl_Obj **resultObj) { unsigned char md_buf[EVP_MAX_MD_SIZE]; unsigned int ulen; int res = 0, md_len = 0; + + dprintf("Called"); /* Finalize cryptography function and get result */ switch(statePtr->format & 0xFF0) { case TYPE_MD: if (!(statePtr->format & IS_XOF)) { @@ -434,11 +441,11 @@ read = Tcl_ReadRaw(parent, buf, toRead); /* Update hash function */ if (read > 0) { /* Have data */ - if (!DigestUpdate(statePtr, buf, (size_t) read, 0)) { + if (DigestUpdate(statePtr, buf, (size_t) read, 0) != TCL_OK) { Tcl_SetChannelError(statePtr->self, Tcl_ObjPrintf("Update failed: %s", REASON())); *errorCodePtr = EINVAL; return 0; } /* This is correct */ @@ -492,11 +499,11 @@ if (toWrite <= 0 || statePtr->self == (Tcl_Channel) NULL) { return 0; } /* Update hash function */ - if (toWrite > 0 && !DigestUpdate(statePtr, buf, (size_t) toWrite, 0)) { + if (toWrite > 0 && DigestUpdate(statePtr, buf, (size_t) toWrite, 0) != TCL_OK) { Tcl_SetChannelError(statePtr->self, Tcl_ObjPrintf("Update failed: %s", REASON())); *errorCodePtr = EINVAL; return 0; } return toWrite; @@ -753,16 +760,17 @@ * Side effects: * Adds transform to channel and sets result to channel id or error message. * *---------------------------------------------------------------------- */ -static int -DigestChannelHandler(Tcl_Interp *interp, const char *channel, const EVP_MD *md, +static int DigestChannelHandler(Tcl_Interp *interp, const char *channel, const EVP_MD *md, const EVP_CIPHER *cipher, int format, Tcl_Obj *keyObj, EVP_MAC *mac) { int mode; /* OR-ed combination of TCL_READABLE and TCL_WRITABLE */ Tcl_Channel chan; DigestState *statePtr; + + dprintf("Called"); /* Validate args */ if (channel == (const char *) NULL) { return TCL_ERROR; } @@ -821,14 +829,15 @@ * Side effects: * Removes transform from channel or sets result to error message. * *---------------------------------------------------------------------- */ -static int -DigestUnstackObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { +static int DigestUnstackObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tcl_Channel chan; int mode; /* OR-ed combination of TCL_READABLE and TCL_WRITABLE */ + + dprintf("Called"); /* Validate arg count */ if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "channelId"); return TCL_ERROR; @@ -877,10 +886,12 @@ int DigestInstanceObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { DigestState *statePtr = (DigestState *) clientData; int fn, len = 0; char *buf = NULL; static const char *instance_fns [] = { "finalize", "update", NULL }; + + dprintf("Called"); /* Validate arg count */ if (objc < 2 || objc > 3) { Tcl_WrongNumArgs(interp, 1, objv, "function ?data?"); return TCL_ERROR; @@ -900,11 +911,11 @@ Tcl_WrongNumArgs(interp, 1, objv, "update data"); return TCL_ERROR; } /* Update hash function */ - if (!DigestUpdate(statePtr, buf, (size_t) len, 1)) { + if (DigestUpdate(statePtr, buf, (size_t) len, 1) != TCL_OK) { return TCL_ERROR; } } else { /* Finalize hash function and calculate message digest */ @@ -956,10 +967,12 @@ */ int DigestCommandHandler(Tcl_Interp *interp, Tcl_Obj *cmdObj, const EVP_MD *md, const EVP_CIPHER *cipher, int format, Tcl_Obj *keyObj, EVP_MAC *mac) { DigestState *statePtr; char *cmdName = Tcl_GetStringFromObj(cmdObj, NULL); + + dprintf("Called"); /* Create state data structure */ if ((statePtr = DigestStateNew(interp, format)) == NULL) { Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); return TCL_ERROR; @@ -995,16 +1008,17 @@ * Side effects: * Sets result to message digest or error message * *------------------------------------------------------------------- */ -int -DigestDataHandler(Tcl_Interp *interp, Tcl_Obj *dataObj, const EVP_MD *md, +int DigestDataHandler(Tcl_Interp *interp, Tcl_Obj *dataObj, const EVP_MD *md, const EVP_CIPHER *cipher, int format, Tcl_Obj *keyObj, EVP_MAC *mac) { char *data; int data_len; DigestState *statePtr; + + dprintf("Called"); /* Get data */ data = Tcl_GetByteArrayFromObj(dataObj, &data_len); if (data == NULL) { Tcl_SetResult(interp, "No data", NULL); @@ -1017,11 +1031,11 @@ return TCL_ERROR; } /* Calc Digest, abort for error */ if (DigestInitialize(interp, statePtr, md, cipher, keyObj, mac) != TCL_OK || - DigestUpdate(statePtr, data, (size_t) data_len, 1) == 0 || + DigestUpdate(statePtr, data, (size_t) data_len, 1) != TCL_OK || DigestFinalize(interp, statePtr, NULL) != TCL_OK) { DigestStateFree(statePtr); return TCL_ERROR; } @@ -1045,32 +1059,34 @@ * Side effects: * Result is message digest or error message * *------------------------------------------------------------------- */ -int DigestFileHandler(Tcl_Interp *interp, Tcl_Obj *filename, const EVP_MD *md, +int DigestFileHandler(Tcl_Interp *interp, Tcl_Obj *inFileObj, const EVP_MD *md, const EVP_CIPHER *cipher, int format, Tcl_Obj *keyObj, EVP_MAC *mac) { DigestState *statePtr; Tcl_Channel chan = NULL; unsigned char buf[BUFFER_SIZE]; int res = TCL_OK, len; + + dprintf("Called"); /* Create state data structure */ if ((statePtr = DigestStateNew(interp, format)) == NULL) { Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); return TCL_ERROR; } /* Open file channel, abort for error */ - chan = Tcl_FSOpenFileChannel(interp, filename, "rb", 0444); + chan = Tcl_FSOpenFileChannel(interp, inFileObj, "rb", 0444); if (chan == (Tcl_Channel) NULL) { DigestStateFree(statePtr); return TCL_ERROR; } /* Configure channel */ - if ((res = Tcl_SetChannelOption(interp, chan, "-translation", "binary")) == TCL_ERROR) { + if ((res = Tcl_SetChannelOption(interp, chan, "-translation", "binary")) != TCL_OK) { goto done; } Tcl_SetChannelBufferSize(chan, BUFFER_SIZE); /* Initialize hash function */ @@ -1080,11 +1096,11 @@ /* Read file data and update hash function */ while (!Tcl_Eof(chan)) { len = Tcl_ReadRaw(chan, (char *) buf, BUFFER_SIZE); if (len > 0) { - if (!DigestUpdate(statePtr, &buf[0], (size_t) len, 1)) { + if (DigestUpdate(statePtr, &buf[0], (size_t) len, 1) != TCL_OK) { res = TCL_ERROR; goto done; } } } @@ -1240,10 +1256,12 @@ Tcl_Obj *fileObj = NULL, *keyObj = NULL, *macObj = NULL; const char *channel = NULL, *opt; const EVP_MD *md = NULL; const EVP_CIPHER *cipher = NULL; EVP_MAC *mac = NULL; + + dprintf("Called"); /* Clear interp result */ Tcl_ResetResult(interp); /* Validate arg count */ Index: generic/tlsEncrypt.c ================================================================== --- generic/tlsEncrypt.c +++ generic/tlsEncrypt.c @@ -15,10 +15,13 @@ #include #if OPENSSL_VERSION_NUMBER >= 0x30000000L #include #endif +/* Macros */ +#define BUFFER_SIZE 32768 + /* Encryption functions */ #define TYPE_MD 0x010 #define TYPE_HMAC 0x020 #define TYPE_CMAC 0x040 #define TYPE_MAC 0x080 @@ -139,11 +142,10 @@ } else { res = EVP_DecryptUpdate(ctx, outbuf, out_len, data, data_len); } if (res) { - *out_len += len; return TCL_OK; } else { Tcl_AppendResult(interp, "Update failed: ", REASON(), NULL); return TCL_ERROR; } @@ -177,11 +179,10 @@ } else { res = EVP_DecryptFinal_ex(ctx, outbuf, out_len); } if (res) { - *out_len += len; return TCL_OK; } else { Tcl_AppendResult(interp, "Finalize failed: ", REASON(), NULL); return TCL_ERROR; } @@ -205,11 +206,11 @@ *------------------------------------------------------------------- */ int EncryptDataHandler(Tcl_Interp *interp, int type, Tcl_Obj *dataObj, Tcl_Obj *cipherObj, Tcl_Obj *digestObj, Tcl_Obj *keyObj, Tcl_Obj *ivObj) { EVP_CIPHER_CTX *ctx = NULL; - int data_len = 0, out_len = 0, res; + int data_len = 0, out_len = 0, len = 0, res = TCL_OK; unsigned char *data, *outbuf; Tcl_Obj *resultObj; dprintf("Called"); @@ -230,14 +231,15 @@ } /* Perform operation */ if (EncryptInitialize(interp, type, &ctx, cipherObj, keyObj, ivObj) != TCL_OK || EncryptUpdate(interp, type, ctx, outbuf, &out_len, data, data_len) != TCL_OK || - EncryptFinalize(interp, type, ctx, outbuf+out_len, &out_len) != TCL_OK) { + EncryptFinalize(interp, type, ctx, outbuf+out_len, &len) != TCL_OK) { res = TCL_ERROR; goto done; } + out_len += len; done: /* Set output result */ if (res == TCL_OK) { outbuf = Tcl_SetByteArrayLength(resultObj, out_len); @@ -246,10 +248,110 @@ Tcl_DecrRefCount(resultObj); /* Result is error message */ } /* Clean up */ + if (ctx != NULL) { + EVP_CIPHER_CTX_free(ctx); + } + return res; +} + +/*******************************************************************/ + +/* + *------------------------------------------------------------------- + * + * EncryptFileHandler -- + * + * Perform encryption function on a block of data and return result. + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * Encrypts or decrypts inFile data to outFile and sets result to + * size of outFile, or an error message. + * + *------------------------------------------------------------------- + */ +int EncryptFileHandler(Tcl_Interp *interp, int type, Tcl_Obj *inFileObj, Tcl_Obj *outFileObj, + Tcl_Obj *cipherObj, Tcl_Obj *digestObj, Tcl_Obj *keyObj, Tcl_Obj *ivObj) { + EVP_CIPHER_CTX *ctx = NULL; + int total = 0, res, out_len = 0, len; + Tcl_Channel in = NULL, out = NULL; + unsigned char in_buf[BUFFER_SIZE]; + unsigned char out_buf[BUFFER_SIZE+1024]; + + dprintf("Called"); + + /* Open input file */ + if ((in = Tcl_FSOpenFileChannel(interp, inFileObj, "rb", 0444)) == (Tcl_Channel) NULL) { + return TCL_ERROR; + } + + /* Open output file */ + if ((out = Tcl_FSOpenFileChannel(interp, outFileObj, "wb", 0644)) == (Tcl_Channel) NULL) { + Tcl_Close(interp, in); + return TCL_ERROR; + } + + /* Initialize operation */ + if ((res = EncryptInitialize(interp, type, &ctx, cipherObj, keyObj, ivObj)) != TCL_OK) { + goto done; + } + + /* Read file data from inFile, encrypt/decrypt it, then output to outFile */ + while (!Tcl_Eof(in)) { + int read = Tcl_ReadRaw(in, (char *) in_buf, BUFFER_SIZE); + if (read > 0) { + if ((res = EncryptUpdate(interp, type, ctx, out_buf, &out_len, in_buf, read)) == TCL_OK) { + if (out_len > 0) { + len = Tcl_WriteRaw(out, (const char *) out_buf, out_len); + if (len >= 0) { + total += len; + } else { + Tcl_AppendResult(interp, "Write error: ", Tcl_ErrnoMsg(Tcl_GetErrno()), (char *) NULL); + res = TCL_ERROR; + goto done; + } + } + } else { + goto done; + } + } else if (read < 0) { + Tcl_AppendResult(interp, "Read error: ", Tcl_ErrnoMsg(Tcl_GetErrno()), (char *) NULL); + res = TCL_ERROR; + goto done; + } + } + + /* Finalize data and write any remaining data in block */ + if ((res = EncryptFinalize(interp, type, ctx, out_buf, &out_len)) == TCL_OK) { + if (out_len > 0) { + len = Tcl_WriteRaw(out, (const char *) out_buf, out_len); + if (len >= 0) { + total += len; + } else { + Tcl_AppendResult(interp, "Write error: ", Tcl_ErrnoMsg(Tcl_GetErrno()), (char *) NULL); + res = TCL_ERROR; + goto done; + } + } + Tcl_SetObjResult(interp, Tcl_NewIntObj(total)); + } else { + goto done; + } + +done: + /* Clean up */ + if (in != NULL) { + Tcl_Close(interp, in); + } + if (out != NULL) { + Tcl_Close(interp, out); + } if (ctx != NULL) { EVP_CIPHER_CTX_free(ctx); } return res; } @@ -284,11 +386,11 @@ /* Clear interp result */ Tcl_ResetResult(interp); /* Validate arg count */ if (objc < 3 || objc > 12) { - Tcl_WrongNumArgs(interp, 1, objv, "-cipher name ?-digest name? ?-key key? ?-iv string? [-data data]"); + Tcl_WrongNumArgs(interp, 1, objv, "-cipher name ?-digest name? -key key ?-iv string? [-infile filename -outfile filename | -data data]"); return TCL_ERROR; } /* Get options */ for (int idx = 1; idx < objc; idx++) { @@ -299,14 +401,16 @@ } OPTOBJ("-cipher", cipherObj); OPTOBJ("-data", dataObj); OPTOBJ("-digest", digestObj); + OPTOBJ("-infile", inFileObj); + OPTOBJ("-outfile", outFileObj); OPTOBJ("-key", keyObj); OPTOBJ("-iv", ivObj); - OPTBAD("option", "-cipher, -data, -digest, -key, or -iv"); + OPTBAD("option", "-cipher, -data, -digest, -infile, -key, -iv, -outfile"); return TCL_ERROR; } /* Check for required options */ if (cipherObj == NULL) { @@ -315,14 +419,16 @@ Tcl_AppendResult(interp, "No key", NULL); return TCL_ERROR; } /* Perform encryption function on file, stacked channel, using instance command, or data blob */ - if (dataObj != NULL) { + if (inFileObj != NULL && outFileObj != NULL) { + res = EncryptFileHandler(interp, type, inFileObj, outFileObj, cipherObj, digestObj, keyObj, ivObj); + } else if (dataObj != NULL) { res = EncryptDataHandler(interp, type, dataObj, cipherObj, digestObj, keyObj, ivObj); } else { - Tcl_AppendResult(interp, "No operation specified: Use -data option", NULL); + Tcl_AppendResult(interp, "No operation specified: Use -data, -infile, or -outfile option", NULL); res = TCL_ERROR; } return res; }