Index: configure ================================================================== --- configure +++ configure @@ -5394,11 +5394,11 @@ # This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS # and PKG_TCL_SOURCES. #----------------------------------------------------------------------- - vars="tls.c tlsBIO.c tlsDigest.c tlsInfo.c tlsIO.c tlsX509.c" + vars="tls.c tlsBIO.c tlsDigest.c tlsEncrypt.c tlsInfo.c tlsIO.c tlsX509.c" for i in $vars; do case $i in \$*) # allow $-var names PKG_SOURCES="$PKG_SOURCES $i" Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -69,11 +69,11 @@ # and runtime Tcl library files in TEA_ADD_TCL_SOURCES. # This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS # and PKG_TCL_SOURCES. #----------------------------------------------------------------------- -TEA_ADD_SOURCES([tls.c tlsBIO.c tlsDigest.c tlsInfo.c tlsIO.c tlsX509.c]) +TEA_ADD_SOURCES([tls.c tlsBIO.c tlsDigest.c tlsEncrypt.c tlsInfo.c tlsIO.c tlsX509.c]) TEA_ADD_HEADERS([generic/tls.h]) TEA_ADD_INCLUDES([]) TEA_ADD_LIBS([]) TEA_ADD_CFLAGS([]) TEA_ADD_STUB_SOURCES([]) Index: generic/tls.c ================================================================== --- generic/tls.c +++ generic/tls.c @@ -2547,10 +2547,11 @@ Tcl_CreateObjCommand(interp, "tls::misc", MiscObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "tls::unimport", UnimportObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "tls::status", StatusObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); Tls_DigestCommands(interp); + Tls_EncryptCommands(interp); Tls_InfoCommands(interp); if (interp) { Tcl_Eval(interp, tlsTclInitScript); } ADDED generic/tlsEncrypt.c Index: generic/tlsEncrypt.c ================================================================== --- /dev/null +++ generic/tlsEncrypt.c @@ -0,0 +1,284 @@ +/* + * Encryption Functions Module + * + * This module provides commands that can be used to encrypt or decrypt data. + * + * Copyright (C) 2023 Brian O'Hagan + * + */ + +#include "tlsInt.h" +#include "tclOpts.h" +#include +#include +#include +#include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#endif + +/* Encryption functions */ +#define TYPE_MD 0x010 +#define TYPE_HMAC 0x020 +#define TYPE_CMAC 0x040 +#define TYPE_MAC 0x080 +#define TYPE_ENCRYPT 0x100 +#define TYPE_DECRYPT 0x200 +#define TYPE_SIGN 0x400 +#define TYPE_VERIFY 0x800 + + +/*******************************************************************/ + +/* + *------------------------------------------------------------------- + * + * CryptoDataHandler -- + * + * Perform encryption function on a block of data and return result. + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * Sets result or error message + * + *------------------------------------------------------------------- + */ +int +Encrypt_DataHandler(Tcl_Interp *interp, int type, Tcl_Obj *dataObj, Tcl_Obj *cipherObj, + Tcl_Obj *digestObj, Tcl_Obj *keyObj, Tcl_Obj *ivObj) { + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *cipher; + char *cipherName = NULL, *data = NULL, *key = NULL, *iv = NULL; + int cipher_len = 0, data_len = 0, key_len = 0, iv_len = 0, out_len = 0, tmplen, res; + unsigned char *outbuf; + Tcl_Obj *resultObj; + + dprintf("Called"); + + if (cipherObj != NULL) { + cipherName = Tcl_GetStringFromObj(cipherObj, &cipher_len); + } + if (keyObj != NULL) { + key = Tcl_GetStringFromObj(keyObj, &key_len); + } + if (ivObj != NULL) { + iv = Tcl_GetStringFromObj(ivObj, &iv_len); + } + if (dataObj != NULL) { + data = Tcl_GetByteArrayFromObj(dataObj, &data_len); + } else { + Tcl_AppendResult(interp, "No data", NULL); + } + + /* Get cipher name */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L + cipher = EVP_get_cipherbyname(cipherName); +#else + cipher = EVP_CIPHER_fetch(NULL, cipherName, NULL); +#endif + if (cipher == NULL) { + Tcl_AppendResult(interp, "Invalid cipher: ", cipherName, NULL); + return TCL_ERROR; + } + + /* Allocate storage for encrypted data. Size should be data size + block size. */ + resultObj = Tcl_NewObj(); + outbuf = Tcl_SetByteArrayLength(resultObj, data_len+1024); + if (resultObj == NULL || outbuf == NULL) { + Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); + return TCL_ERROR; + } + + /* Create and initialize the context */ + if((ctx = EVP_CIPHER_CTX_new()) == NULL) { + Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); + Tcl_DecrRefCount(resultObj); + return TCL_ERROR; + } + + /* Initialize the operation. Need appropriate key and iv size. */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if (type == TYPE_ENCRYPT) { + res = EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv); + } else { + res = EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv); + } +#else + OSSL_PARAM params[2]; + int index = 0; + + if (iv != NULL) { + params[index++] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_IV, (void *) iv, (size_t) iv_len); + } + params[index] = OSSL_PARAM_construct_end(); + + if (type == TYPE_ENCRYPT) { + res = EVP_EncryptInit_ex2(ctx, cipher, key, iv, params); + } else { + res = EVP_DecryptInit_ex2(ctx, cipher, key, iv, params); + } +#endif + + if(!res) { + Tcl_AppendResult(interp, "Initialize failed: ", REASON(), NULL); + Tcl_DecrRefCount(resultObj); + EVP_CIPHER_CTX_free(ctx); + return TCL_ERROR; + } + + /* Encrypt/decrypt data */ + if (type == TYPE_ENCRYPT) { + res = EVP_EncryptUpdate(ctx, outbuf, &out_len, data, data_len); + } else { + res = EVP_DecryptUpdate(ctx, outbuf, &out_len, data, data_len); + } + + if (!res) { + Tcl_AppendResult(interp, "Update failed: ", REASON(), NULL); + Tcl_DecrRefCount(resultObj); + EVP_CIPHER_CTX_free(ctx); + return TCL_ERROR; + } + + /* Finalize data */ + if (type == TYPE_ENCRYPT) { + res = EVP_EncryptFinal_ex(ctx, outbuf+out_len, &tmplen); + } else { + res = EVP_DecryptFinal_ex(ctx, outbuf+out_len, &tmplen); + } + + if (!res) { + Tcl_AppendResult(interp, "Finalize failed: ", REASON(), NULL); + Tcl_DecrRefCount(resultObj); + EVP_CIPHER_CTX_free(ctx); + return TCL_ERROR; + } + + out_len += tmplen; + outbuf = Tcl_SetByteArrayLength(resultObj, out_len); + + /* Set return result */ + Tcl_SetObjResult(interp, resultObj); + + /* Clean up */ + EVP_CIPHER_CTX_free(ctx); + return TCL_OK; +} + +/*******************************************************************/ + +/* + *------------------------------------------------------------------- + * + * EncryptionMain -- + * + * Perform encryption function and return result. + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * Sets result or error message + * + *------------------------------------------------------------------- + */ +static int EncryptionMain(int type, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + int res = TCL_OK; + Tcl_Obj *cipherObj = NULL, *cmdObj = NULL, *dataObj = NULL, *digestObj = NULL; + Tcl_Obj *inFileObj = NULL, *outFileObj = NULL, *keyObj = NULL, *ivObj = NULL; + const char *channel = NULL, *opt; + const EVP_MD *md = NULL; + const EVP_CIPHER *cipher = NULL; + + dprintf("Called"); + + /* 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]"); + return TCL_ERROR; + } + + /* Get options */ + for (int idx = 1; idx < objc; idx++) { + opt = Tcl_GetStringFromObj(objv[idx], NULL); + + if (opt[0] != '-') { + break; + } + + OPTOBJ("-cipher", cipherObj); + OPTOBJ("-data", dataObj); + OPTOBJ("-digest", digestObj); + OPTOBJ("-key", keyObj); + OPTOBJ("-iv", ivObj); + + OPTBAD("option", "-cipher, -data, -digest, -key, or -iv"); + return TCL_ERROR; + } + + /* Check for required options */ + if (cipherObj == NULL) { + Tcl_AppendResult(interp, "No cipher", NULL); + } else if (keyObj == NULL) { + 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) { + res = Encrypt_DataHandler(interp, type, dataObj, cipherObj, digestObj, keyObj, ivObj); + } else { + Tcl_AppendResult(interp, "No operation specified: Use -data option", NULL); + res = TCL_ERROR; + } + return res; +} + +/* + *------------------------------------------------------------------- + * + * Encryption Commands -- + * + * Perform encryption function and return results + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * Command dependent + * + *------------------------------------------------------------------- + */ +static int EncryptObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + return EncryptionMain(TYPE_ENCRYPT, interp, objc, objv); +} + +static int DecryptObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + return EncryptionMain(TYPE_DECRYPT, interp, objc, objv); +} + +/* + *------------------------------------------------------------------- + * + * Encrypt_Initialize -- + * + * Create namespace, commands, and register package version + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * Creates commands + * + *------------------------------------------------------------------- + */ +int Tls_EncryptCommands(Tcl_Interp *interp) { + Tcl_CreateObjCommand(interp, "tls::encrypt", EncryptObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateObjCommand(interp, "tls::decrypt", DecryptObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); +} + Index: generic/tlsInt.h ================================================================== --- generic/tlsInt.h +++ generic/tlsInt.h @@ -194,10 +194,11 @@ void Tls_Error(State *statePtr, char *msg); void Tls_Free(char *blockPtr); void Tls_Clean(State *statePtr); int Tls_WaitForConnect(State *statePtr, int *errorCodePtr, int handshakeFailureIsPermanent); int Tls_DigestCommands(Tcl_Interp *interp); +int Tls_EncryptCommands(Tcl_Interp *interp); int Tls_InfoCommands(Tcl_Interp *interp); BIO *BIO_new_tcl(State* statePtr, int flags); #define PTR2INT(x) ((int) ((intptr_t) (x))) Index: win/makefile.vc ================================================================== --- win/makefile.vc +++ win/makefile.vc @@ -26,10 +26,11 @@ # hence it is under that condition. TMP_DIR is the output directory # defined by rules for object files. PRJ_OBJS = $(TMP_DIR)\tls.obj \ $(TMP_DIR)\tlsBIO.obj \ $(TMP_DIR)\tlsDigest.obj \ + $(TMP_DIR)\tlsEncrypt.obj \ $(TMP_DIR)\tlsInfo.obj \ $(TMP_DIR)\tlsIO.obj \ $(TMP_DIR)\tlsX509.obj # Define any additional project include flags