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 tlsEncrypt.c tlsInfo.c tlsIO.c tlsKey.c tlsX509.c" + vars="tls.c tlsBIO.c tlsDigest.c tlsEncrypt.c tlsInfo.c tlsIO.c tlsKDF.c tlsUtil.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 tlsEncrypt.c tlsInfo.c tlsIO.c tlsKey.c tlsX509.c]) +TEA_ADD_SOURCES([tls.c tlsBIO.c tlsDigest.c tlsEncrypt.c tlsInfo.c tlsIO.c tlsKDF.c tlsUtil.c tlsX509.c]) TEA_ADD_HEADERS([generic/tls.h]) TEA_ADD_INCLUDES([]) TEA_ADD_LIBS([]) TEA_ADD_CFLAGS([]) TEA_ADD_STUB_SOURCES([]) Index: generic/tlsDigest.c ================================================================== --- generic/tlsDigest.c +++ generic/tlsDigest.c @@ -147,17 +147,35 @@ * *------------------------------------------------------------------- */ int DigestInitialize(Tcl_Interp *interp, DigestState *statePtr, Tcl_Obj *digestObj, Tcl_Obj *cipherObj, Tcl_Obj *keyObj, Tcl_Obj *macObj) { - int key_len = 0, type = statePtr->format & 0xFF0; - const char *digestName = NULL, *cipherName = NULL, *macName = NULL; + int res = 0, type = statePtr->format & 0xFF0; const EVP_MD *md = NULL; const EVP_CIPHER *cipher = NULL; - const unsigned char *key = NULL; + const void *key = NULL, *iv = NULL, *salt = NULL; + int key_len = 0; dprintf("Called"); + + /* Get digest */ + md = Util_GetDigest(interp, digestObj, type != TYPE_CMAC); + if (md == NULL && type != TYPE_CMAC) { + return TCL_ERROR; + } + + /* Get cipher */ + cipher = Util_GetCipher(interp, cipherObj, type == TYPE_CMAC); + if (cipher == NULL && type == TYPE_CMAC) { + 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; + } /* Create contexts */ switch(type) { case TYPE_MD: statePtr->ctx = EVP_MD_CTX_new(); @@ -176,72 +194,20 @@ if (!res) { Tcl_AppendResult(interp, "Create context failed", NULL); return TCL_ERROR; } - /* Get MAC */ - if (macObj != NULL) { - macName = Tcl_GetStringFromObj(macObj, 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; - } - - /* Get digest */ - if (digestObj != NULL) { - digestName = Tcl_GetStringFromObj(digestObj, NULL); - md = EVP_get_digestbyname(digestName); - if (md == NULL) { - Tcl_AppendResult(interp, "Invalid digest \"", digestName, "\"", NULL); - return TCL_ERROR; - } else if (md == EVP_shake128() || md == EVP_shake256()) { - statePtr->format |= IS_XOF; - } - } else if (type != TYPE_CMAC) { - Tcl_AppendResult(interp, "No digest specified", NULL); - return TCL_ERROR; - } - - /* Get cipher */ - if (cipherObj != NULL) { - cipherName = Tcl_GetStringFromObj(cipherObj, NULL); - cipher = EVP_get_cipherbyname(cipherName); - if (cipher == NULL) { - Tcl_AppendResult(interp, "Invalid cipher \"", cipherName, "\"", NULL); - return TCL_ERROR; - } - } else if (type == TYPE_CMAC) { - Tcl_AppendResult(interp, "No cipher specified", NULL); - return TCL_ERROR; - } - - /* Get key */ - if (keyObj != NULL) { - key = Tcl_GetByteArrayFromObj(keyObj, &key_len); - } else if (type != TYPE_MD) { - Tcl_AppendResult(interp, "No key specified", NULL); - return TCL_ERROR; - } - /* Initialize cryptography function */ switch(type) { case TYPE_MD: res = EVP_DigestInit_ex(statePtr->ctx, md, NULL); break; case TYPE_HMAC: - res = HMAC_Init_ex(statePtr->hctx, (const void *) key, key_len, md, NULL); + res = HMAC_Init_ex(statePtr->hctx, key, key_len, md, NULL); break; case TYPE_CMAC: - res = CMAC_Init(statePtr->cctx, (const void *) key, key_len, cipher, NULL); + res = CMAC_Init(statePtr->cctx, key, key_len, cipher, NULL); break; } if (!res) { Tcl_AppendResult(interp, "Initialize failed: ", REASON(), NULL); @@ -1313,15 +1279,15 @@ 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); + Tcl_AppendResult(interp, "invalid MAC \"", macName, "\"", NULL); return TCL_ERROR; } } else { - Tcl_AppendResult(interp, "No MAC specified", NULL); + Tcl_AppendResult(interp, "no MAC", NULL); return TCL_ERROR; } } /* Calc digest on file, stacked channel, using instance command, or data blob */ @@ -1332,11 +1298,11 @@ } else if (cmdObj != NULL) { res = DigestCommandHandler(interp, cmdObj, digestObj, cipherObj, format | type, keyObj, macObj); } else if (dataObj != NULL) { res = DigestDataHandler(interp, dataObj, digestObj, cipherObj, format | type, keyObj, macObj); } else { - Tcl_AppendResult(interp, "No operation specified: Use -channel, -command, -data, or -file option", NULL); + Tcl_AppendResult(interp, "No operation: Use -channel, -command, -data, or -file option", NULL); res = TCL_ERROR; } return res; } Index: generic/tlsEncrypt.c ================================================================== --- generic/tlsEncrypt.c +++ generic/tlsEncrypt.c @@ -136,105 +136,66 @@ *------------------------------------------------------------------- */ int EncryptInitialize(Tcl_Interp *interp, int type, EVP_CIPHER_CTX **ctx, Tcl_Obj *cipherObj, Tcl_Obj *keyObj, Tcl_Obj *ivObj) { const EVP_CIPHER *cipher; - char *cipherName = NULL, *keyString = NULL, *ivString = NULL; + char *keyString = NULL, *ivString = NULL; int cipher_len = 0, key_len = 0, iv_len = 0, res, max; unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH]; dprintf("Called"); /* Init buffers */ memset(key, 0, EVP_MAX_KEY_LENGTH); memset(iv, 0, EVP_MAX_IV_LENGTH); - /* Get encryption parameters */ - if (cipherObj != NULL) { - cipherName = Tcl_GetStringFromObj(cipherObj, &cipher_len); - } - if (keyObj != NULL) { - keyString = Tcl_GetByteArrayFromObj(keyObj, &key_len); - } - if (ivObj != NULL) { - ivString = Tcl_GetByteArrayFromObj(ivObj, &iv_len); - } - - /* Get cipher name */ -#if OPENSSL_VERSION_NUMBER < 0x30000000L - cipher = EVP_get_cipherbyname(cipherName); -#else - cipher = EVP_CIPHER_fetch(NULL, cipherName, NULL); -#endif + /* Get cipher */ + cipher = Util_GetCipher(interp, cipherObj, 1); if (cipher == NULL) { - Tcl_AppendResult(interp, "Invalid cipher: \"", cipherName, "\"", NULL); - return TCL_ERROR; - } - - if (key_len > 0) { -#if OPENSSL_VERSION_NUMBER < 0x30000000L - max = EVP_CIPHER_key_length(cipher); -#else - max = EVP_CIPHER_get_key_length(cipher); -#endif - if (max == 0) { - } else if (key_len <= max) { - memcpy((void *) key, (const void *) keyString, (size_t) key_len); - } else { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("Key too long. Must be <= %d bytes", max)); - return TCL_ERROR; - } - } - - if (iv_len > 0) { -#if OPENSSL_VERSION_NUMBER < 0x30000000L - max = EVP_CIPHER_iv_length(cipher); -#else - max = EVP_CIPHER_get_iv_length(cipher); -#endif - if (max == 0) { - } else if (iv_len <= max) { - memcpy((void *) iv, (const void *) ivString, (size_t) iv_len); - } else { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("IV too long. Must be <= %d bytes", max)); - return TCL_ERROR; - } - } - - /* Create and initialize the context */ + return TCL_ERROR; + } + + /* Get key - Only support internally defined cipher lengths. + Custom ciphers can be up to size_t bytes. */ + max = EVP_CIPHER_key_length(cipher); + keyString = (const void *) Util_GetKey(interp, keyObj, &key_len, "key", max, FALSE); + if (keyString != NULL) { + memcpy((void *) key, (const void *) keyString, (size_t) key_len); + } else if (keyObj != NULL) { + return TCL_ERROR; + } + + /* Get IV */ + max = EVP_CIPHER_iv_length(cipher); + ivString = (const void *) Util_GetIV(interp, ivObj, &iv_len, max, FALSE); + if (ivString != NULL) { + memcpy((void *) iv, (const void *) ivString, (size_t) iv_len); + } else if (ivObj != NULL) { + return TCL_ERROR; + } + + /* Create context */ if((*ctx = EVP_CIPHER_CTX_new()) == NULL) { Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); 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); return TCL_ERROR; } + + /* Erase buffers */ + memset(key, 0, EVP_MAX_KEY_LENGTH); + memset(iv, 0, EVP_MAX_IV_LENGTH); return TCL_OK; } /* *------------------------------------------------------------------- Index: generic/tlsInt.h ================================================================== --- generic/tlsInt.h +++ generic/tlsInt.h @@ -198,9 +198,19 @@ int Tls_EncryptCommands(Tcl_Interp *interp); int Tls_InfoCommands(Tcl_Interp *interp); int Tls_KeyCommands(Tcl_Interp *interp); BIO *BIO_new_tcl(State* statePtr, int flags); + +EVP_CIPHER *Util_GetCipher(Tcl_Interp *interp, Tcl_Obj *cipherObj, int no_null); +EVP_MD *Util_GetDigest(Tcl_Interp *interp, Tcl_Obj *digestObj, int no_null); +unsigned char *Util_GetIV(Tcl_Interp *interp, Tcl_Obj *ivObj, int *len, int max, int no_null); +unsigned char *Util_GetKey(Tcl_Interp *interp, Tcl_Obj *keyObj, int *len, char *name, int max, int no_null); +unsigned char *Util_GetSalt(Tcl_Interp *interp, Tcl_Obj *saltObj, int *len, int max, int no_null); +int Util_GetInt(Tcl_Interp *interp, Tcl_Obj *dataObj, int *value, char *name, int min, int max); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +EVP_MAC *Util_GetMAC(Tcl_Interp *interp, Tcl_Obj *MacObj, int no_null); +#endif #define PTR2INT(x) ((int) ((intptr_t) (x))) #endif /* _TLSINT_H */ ADDED generic/tlsKDF.c Index: generic/tlsKDF.c ================================================================== --- /dev/null +++ generic/tlsKDF.c @@ -0,0 +1,476 @@ +/* + * Key Derivation Function (KDF) Module + * + * Provides commands to derive keys. + * + * Copyright (C) 2023 Brian O'Hagan + * + */ + +#include "tlsInt.h" +#include "tclOpts.h" +#include +#include + +/*******************************************************************/ + +/* Options for KDF commands */ + +static const char *command_opts [] = { + "-cipher", "-digest", "-hash", "-info", "-iterations", "-key", "-length", "-password", + "-salt", "-size", "-N", "-n", "-r", "-p", NULL}; + +enum _command_opts { + _opt_cipher, _opt_digest, _opt_hash, _opt_info, _opt_iter, _opt_key, _opt_length, + _opt_password, _opt_salt, _opt_size, _opt_N, _opt_n, _opt_r, _opt_p +}; + +/* + *------------------------------------------------------------------- + * + * KDF_PBKDF2 -- + * + * PKCS5_PBKDF2_HMAC key derivation function (KDF) specified by PKCS #5. + * KDFs include PBKDF2 from RFC 2898/8018 and Scrypt from RFC 7914. + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * Sets result to a list of key and iv values, or an error message + * + *------------------------------------------------------------------- + */ +static int KDF_PBKDF2(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + int pass_len = 0, salt_len = 0, fn; + int iklen, ivlen, iter = 1; + unsigned char *pass = NULL, *salt = NULL; + const EVP_MD *md = NULL; + const EVP_CIPHER *cipher = NULL; + int buf_len = (EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH)*4, dk_len = buf_len; + unsigned char tmpkeyiv[(EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH)*4]; + + dprintf("Called"); + + /* Clear errors */ + Tcl_ResetResult(interp); + ERR_clear_error(); + + /* Validate arg count */ + if (objc < 3 || objc > 11) { + Tcl_WrongNumArgs(interp, 1, objv, "[-cipher cipher | -size length] -digest digest ?-iterations count? ?-password string? ?-salt string?"); + return TCL_ERROR; + } + + /* Init buffers */ + memset(tmpkeyiv, 0, buf_len); + + /* Get options */ + for (int idx = 1; idx < objc; idx++) { + /* Get option */ + if (Tcl_GetIndexFromObj(interp, objv[idx], command_opts, "option", 0, &fn) != TCL_OK) { + return TCL_ERROR; + } + + /* Validate arg has a value */ + if (++idx >= objc) { + Tcl_AppendResult(interp, "No value for option \"", command_opts[fn], "\"", NULL); + return TCL_ERROR; + } + + switch(fn) { + case _opt_cipher: + if ((cipher = Util_GetCipher(interp, objv[idx], TRUE)) == NULL) { + return TCL_ERROR; + } + break; + case _opt_digest: + case _opt_hash: + if ((md = Util_GetDigest(interp, objv[idx], TRUE)) == NULL) { + return TCL_ERROR; + } + break; + case _opt_iter: + if (Util_GetInt(interp, objv[idx], &iter, "iterations", 1, -1) != TCL_OK) { + return TCL_ERROR; + } + break; + case _opt_key: + case _opt_password: + pass = Util_GetKey(interp, objv[idx], &pass_len, command_opts[fn], 0, FALSE); + break; + case _opt_salt: + GET_OPT_BYTE_ARRAY(objv[idx], salt, &salt_len); + break; + case _opt_length: + case _opt_size: + if (Util_GetInt(interp, objv[idx], &dk_len, command_opts[fn], 1, buf_len) != TCL_OK) { + return TCL_ERROR; + } + break; + } + } + + /* Validate options */ + if (md == NULL) { + Tcl_AppendResult(interp, "no digest", NULL); + return TCL_ERROR; + } + + /* Set output type sizes */ + if (cipher == NULL) { + if (dk_len > buf_len) dk_len = buf_len; + iklen = dk_len; + ivlen = 0; + } else { + iklen = EVP_CIPHER_key_length(cipher); + ivlen = EVP_CIPHER_iv_length(cipher); + dk_len = iklen+ivlen; + } + + /* Derive key */ + if (!PKCS5_PBKDF2_HMAC(pass, pass_len, salt, salt_len, iter, md, dk_len, tmpkeyiv)) { + Tcl_AppendResult(interp, "Key derivation failed: ", REASON(), NULL); + return TCL_ERROR; + } + + /* Set result to key and iv */ + if (cipher == NULL) { + Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(tmpkeyiv, dk_len)); + } else { + Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL); + LAPPEND_BARRAY(interp, resultObj, "key", tmpkeyiv, iklen); + LAPPEND_BARRAY(interp, resultObj, "iv", tmpkeyiv+iklen, ivlen); + Tcl_SetObjResult(interp, resultObj); + } + + /* Clear data */ + memset(tmpkeyiv, 0, buf_len); + return TCL_OK; + clientData = clientData; +} + +/* + *------------------------------------------------------------------- + * + * KDF_HKDF -- + * + * HMAC-based Extract-and-Expand Key Derivation Function (HKDF). + * See RFC 5869. + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * Sets result to a key of specified length, or an error message + * + *------------------------------------------------------------------- + */ +static int KDF_HKDF(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + EVP_PKEY_CTX *pctx = NULL; + const EVP_MD *md = NULL; + unsigned char *salt = NULL, *key = NULL, *info = NULL, *out = NULL; + int salt_len = 0, key_len = 0, info_len = 0, res = TCL_OK, fn; + int dk_len = EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH; + size_t out_len; + Tcl_Obj *resultObj; + + dprintf("Called"); + + /* Clear errors */ + Tcl_ResetResult(interp); + ERR_clear_error(); + + /* Validate arg count */ + if (objc < 5 || objc > 11) { + Tcl_WrongNumArgs(interp, 1, objv, "-digest digest -key string ?-info string? ?-salt string? ?-size derived_length?"); + return TCL_ERROR; + } + + /* Get options */ + for (int idx = 1; idx < objc; idx++) { + /* Get option */ + if (Tcl_GetIndexFromObj(interp, objv[idx], command_opts, "option", 0, &fn) != TCL_OK) { + return TCL_ERROR; + } + + /* Validate arg has a value */ + if (++idx >= objc) { + Tcl_AppendResult(interp, "No value for option \"", command_opts[fn], "\"", NULL); + return TCL_ERROR; + } + + switch(fn) { + case _opt_digest: + case _opt_hash: + if ((md = Util_GetDigest(interp, objv[idx], TRUE)) == NULL) { + goto error; + } + break; + case _opt_info: + /* Max 1024/2048 */ + GET_OPT_BYTE_ARRAY(objv[idx], info, &info_len); + break; + case _opt_key: + case _opt_password: + if ((key = Util_GetKey(interp, objv[idx], &key_len, command_opts[fn], 0, 1)) == NULL) { + goto error; + } + break; + case _opt_salt: + GET_OPT_BYTE_ARRAY(objv[idx], salt, &salt_len); + break; + case _opt_length: + case _opt_size: + if (Util_GetInt(interp, objv[idx], &dk_len, command_opts[fn], 1, 0) != TCL_OK) { + goto error; + } + break; + } + } + + if (md == NULL) { + Tcl_AppendResult(interp, "no digest", NULL); + goto error; + } + + if (key == NULL) { + Tcl_AppendResult(interp, "no key", NULL); + goto error; + } + + /* Create context */ + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (pctx == NULL) { + Tcl_AppendResult(interp, "Memory allocation error", NULL); + goto error; + } + + if (EVP_PKEY_derive_init(pctx) < 1) { + Tcl_AppendResult(interp, "Initialize failed: ", REASON(), NULL); + goto error; + } + + /* Set config parameters */ + if (EVP_PKEY_CTX_set_hkdf_md(pctx, md) < 1) { + Tcl_AppendResult(interp, "Set digest failed: ", REASON(), NULL); + goto error; + } + if (EVP_PKEY_CTX_set1_hkdf_key(pctx, key, key_len) < 1) { + Tcl_AppendResult(interp, "Set key failed: ", REASON(), NULL); + goto error; + } + if (salt != NULL && EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, salt_len) < 1) { + Tcl_AppendResult(interp, "Set salt failed: ", REASON(), NULL); + goto error; + } + if (info != NULL && EVP_PKEY_CTX_add1_hkdf_info(pctx, info, info_len) < 1) { + Tcl_AppendResult(interp, "Set info failed: ", REASON(), NULL); + goto error; + } + + /* Get buffer */ + resultObj = Tcl_NewObj(); + if ((out = Tcl_SetByteArrayLength(resultObj, dk_len)) == NULL) { + Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); + goto error; + } + out_len = (size_t) dk_len; + + /* Derive key */ + if (EVP_PKEY_derive(pctx, out, &out_len) > 0) { + /* Shrink buffer to actual size */ + Tcl_SetByteArrayLength(resultObj, (int) out_len); + Tcl_SetObjResult(interp, resultObj); + res = TCL_OK; + goto done; + } else { + Tcl_AppendResult(interp, "Key derivation failed: ", REASON(), NULL); + Tcl_DecrRefCount(resultObj); + } + +error: + res = TCL_ERROR; +done: + if (pctx != NULL) { + EVP_PKEY_CTX_free(pctx); + } + return res; +} + +/* + *------------------------------------------------------------------- + * + * KDF_Scrypt -- + * + * HMAC-based Extract-and-Expand Key Derivation Function (HKDF). + * See RFC 5869 and RFC 7914. + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * Sets result to a list of key and iv values, or an error message + * + *------------------------------------------------------------------- + */ +static int KDF_Scrypt(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + EVP_PKEY_CTX *pctx = NULL; + unsigned char *salt = NULL, *pass = NULL, *out = NULL; + int salt_len = 0, pass_len = 0, dk_len = 64, res = TCL_OK, fn; + uint64_t N = 0, p = 0, r = 0, maxmem = 0; + size_t out_len; + Tcl_Obj *resultObj; + + dprintf("Called"); + + /* Clear errors */ + Tcl_ResetResult(interp); + ERR_clear_error(); + + /* Validate arg count */ + if (objc < 5 || objc > 13) { + Tcl_WrongNumArgs(interp, 1, objv, "-password string -salt string ?-N costParameter? ?-r blockSize? ?-p parallelization? ?-size derived_length?"); + return TCL_ERROR; + } + + /* Get options */ + for (int idx = 1; idx < objc; idx++) { + /* Get option */ + if (Tcl_GetIndexFromObj(interp, objv[idx], command_opts, "option", 0, &fn) != TCL_OK) { + return TCL_ERROR; + } + + /* Validate arg has a value */ + if (++idx >= objc) { + Tcl_AppendResult(interp, "No value for option \"", command_opts[fn], "\"", (char *) NULL); + return TCL_ERROR; + } + + switch(fn) { + case _opt_key: + case _opt_password: + GET_OPT_BYTE_ARRAY(objv[idx], pass, &pass_len); + break; + case _opt_salt: + GET_OPT_BYTE_ARRAY(objv[idx], salt, &salt_len); + break; + case _opt_length: + case _opt_size: + if (Util_GetInt(interp, objv[idx], &dk_len, command_opts[fn], 1, 0) != TCL_OK) { + goto error; + } + break; + case _opt_N: + case _opt_n: + GET_OPT_WIDE(objv[idx], &N); + break; + case _opt_r: + GET_OPT_WIDE(objv[idx], &r); + break; + case _opt_p: + GET_OPT_WIDE(objv[idx], &p); + break; + } + } + + if (pass == NULL) { + Tcl_AppendResult(interp, "no password", (char *) NULL); + return TCL_ERROR; + } + + if (salt == NULL) { + Tcl_AppendResult(interp, "no salt", (char *) NULL); + return TCL_ERROR; + } + + /* Create context */ + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_SCRYPT, NULL); + if (pctx == NULL) { + Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); + goto error; + } + + if (EVP_PKEY_derive_init(pctx) < 1) { + Tcl_AppendResult(interp, "Initialize failed: ", REASON(), NULL); + goto error; + } + + /* Set config parameters */ + if (EVP_PKEY_CTX_set1_pbe_pass(pctx, pass, pass_len) < 1) { + Tcl_AppendResult(interp, "Set key failed: ", REASON(), NULL); + goto error; + } + if (EVP_PKEY_CTX_set1_scrypt_salt(pctx, salt, salt_len) < 1) { + Tcl_AppendResult(interp, "Set salt failed: ", REASON(), NULL); + goto error; + } + if (N != 0 && EVP_PKEY_CTX_set_scrypt_N(pctx, N) < 1) { + Tcl_AppendResult(interp, "Set cost parameter (N) failed: ", REASON(), NULL); + goto error; + } + if (r != 0 && EVP_PKEY_CTX_set_scrypt_r(pctx, r) < 1) { + Tcl_AppendResult(interp, "Set lock size parameter (r) failed: ", REASON(), NULL); + goto error; + } + if (p != 0 && EVP_PKEY_CTX_set_scrypt_p(pctx, p) < 1) { + Tcl_AppendResult(interp, "Set Parallelization parameter (p) failed: ", REASON(), NULL); + goto error; + } + if (maxmem != 0 && EVP_PKEY_CTX_set_scrypt_maxmem_bytes(pctx, maxmem) < 1) { + Tcl_AppendResult(interp, "Set max memory failed: ", REASON(), NULL); + goto error; + } + + /* Get buffer */ + resultObj = Tcl_NewObj(); + if ((out = Tcl_SetByteArrayLength(resultObj, dk_len)) == NULL) { + Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); + goto error; + } + out_len = (size_t) dk_len; + + /* Derive key */ + if (EVP_PKEY_derive(pctx, out, &out_len) > 0) { + /* Shrink buffer to actual size */ + Tcl_SetByteArrayLength(resultObj, (int) out_len); + Tcl_SetObjResult(interp, resultObj); + goto done; + + } else { + Tcl_AppendResult(interp, "Key derivation failed: ", REASON(), NULL); + Tcl_DecrRefCount(resultObj); + } + +error: + res = TCL_ERROR; + +done: + if (pctx != NULL) { + EVP_PKEY_CTX_free(pctx); + } + return res; +} + +/* + *------------------------------------------------------------------- + * + * Tls_KeyCommands -- + * + * Create key commands + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * Creates commands + * + *------------------------------------------------------------------- + */ +int Tls_KeyCommands(Tcl_Interp *interp) { + Tcl_CreateObjCommand(interp, "tls::hkdf", KDF_HKDF, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateObjCommand(interp, "tls::pbkdf2", KDF_PBKDF2, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateObjCommand(interp, "tls::scrypt", KDF_Scrypt, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + return TCL_OK; +} + DELETED generic/tlsKey.c Index: generic/tlsKey.c ================================================================== --- generic/tlsKey.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - * Key Derivation Function (KDF) Module - * - * Provides commands to derive keys. - * - * Copyright (C) 2023 Brian O'Hagan - * - */ - -#include "tlsInt.h" -#include "tclOpts.h" -#include -#include - -/*******************************************************************/ - -/* - * Get cipher - */ -EVP_CIPHER *Util_GetCipher(Tcl_Interp *interp, char *name, int exist) { - EVP_CIPHER *cipher = NULL; - - if (name != NULL) { - cipher = EVP_get_cipherbyname(name); - if (cipher == NULL) { - Tcl_AppendResult(interp, "Invalid cipher: \"", name, "\"", NULL); - } - } else if (exist) { - Tcl_AppendResult(interp, "No cipher specified", NULL); - } - return cipher; -} - -/* - * Get message digest - */ -EVP_MD *Util_GetDigest(Tcl_Interp *interp, char *name, int exist) { - EVP_MD *md = NULL; - - if (name != NULL) { - md = EVP_get_digestbyname(name); - if (md == NULL) { - Tcl_AppendResult(interp, "Invalid digest: \"", name, "\"", NULL); - } - } else if (exist) { - Tcl_AppendResult(interp, "No digest specified", NULL); - } - return md; -} - -/*******************************************************************/ - -/* Options for KDF commands */ - -static const char *command_opts [] = { - "-cipher", "-digest", "-hash", "-info", "-iterations", "-key", "-length", "-password", - "-salt", "-size", "-N", "-n", "-r", "-p", NULL}; - -enum _command_opts { - _opt_cipher, _opt_digest, _opt_hash, _opt_info, _opt_iter, _opt_key, _opt_length, - _opt_password, _opt_salt, _opt_size, _opt_N, _opt_n, _opt_r, _opt_p -}; - -/* - *------------------------------------------------------------------- - * - * KDF_PBKDF2 -- - * - * PKCS5_PBKDF2_HMAC key derivation function (KDF) specified by PKCS #5. - * KDFs include PBKDF2 from RFC 2898/8018 and Scrypt from RFC 7914. - * - * Returns: - * TCL_OK or TCL_ERROR - * - * Side effects: - * Sets result to a list of key and iv values, or an error message - * - *------------------------------------------------------------------- - */ -static int KDF_PBKDF2(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - int pass_len = 0, salt_len = 0, fn; - int iklen, ivlen, iter = 1; - unsigned char *password = NULL, *salt = NULL; - const EVP_MD *md = NULL; - const EVP_CIPHER *cipher = NULL; - int buf_len = (EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH)*4, dk_len = buf_len; - unsigned char tmpkeyiv[(EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH)*4]; - char *cipherName = NULL, *digestName = NULL; - - dprintf("Called"); - - /* Clear errors */ - Tcl_ResetResult(interp); - ERR_clear_error(); - - /* Validate arg count */ - if (objc < 3 || objc > 11) { - Tcl_WrongNumArgs(interp, 1, objv, "[-cipher cipher | -size length] -digest digest ?-iterations count? ?-password string? ?-salt string?"); - return TCL_ERROR; - } - - /* Init buffers */ - memset(tmpkeyiv, 0, buf_len); - - /* Get options */ - for (int idx = 1; idx < objc; idx++) { - /* Get option */ - if (Tcl_GetIndexFromObj(interp, objv[idx], command_opts, "option", 0, &fn) != TCL_OK) { - return TCL_ERROR; - } - - /* Validate arg has a value */ - if (++idx >= objc) { - Tcl_AppendResult(interp, "No value for option \"", command_opts[fn], "\"", (char *) NULL); - return TCL_ERROR; - } - - switch(fn) { - case _opt_cipher: - GET_OPT_STRING(objv[idx], cipherName, NULL); - break; - case _opt_digest: - case _opt_hash: - GET_OPT_STRING(objv[idx], digestName, NULL); - break; - case _opt_iter: - GET_OPT_INT(objv[idx], &iter); - break; - case _opt_key: - case _opt_password: - GET_OPT_BYTE_ARRAY(objv[idx], password, &pass_len); - break; - case _opt_salt: - GET_OPT_BYTE_ARRAY(objv[idx], salt, &salt_len); - break; - case _opt_length: - case _opt_size: - GET_OPT_INT(objv[idx], &dk_len); - break; - } - } - - /* Validate options */ - if (cipherName != NULL && (cipher = Util_GetCipher(interp, cipherName, 0)) == NULL) { - return TCL_ERROR; - } - - if ((md = Util_GetDigest(interp, digestName, TRUE)) == NULL) { - return TCL_ERROR; - } - - if (iter < 1) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("Invalid iterations count %d: must be > 0", iter)); - return TCL_ERROR; - } - - if (dk_len < 1 || dk_len > buf_len) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("Invalid derived key length %d: must be 0 < size <= %d", dk_len, buf_len)); - return TCL_ERROR; - } - - /* Set output type sizes */ - if (cipher == NULL) { - if (dk_len > buf_len) dk_len = buf_len; - iklen = dk_len; - ivlen = 0; - } else { - iklen = EVP_CIPHER_key_length(cipher); - ivlen = EVP_CIPHER_iv_length(cipher); - dk_len = iklen+ivlen; - } - - /* Derive key */ - if (!PKCS5_PBKDF2_HMAC(password, pass_len, salt, salt_len, iter, md, dk_len, tmpkeyiv)) { - Tcl_AppendResult(interp, "Key derivation failed: ", REASON(), NULL); - return TCL_ERROR; - } - - /* Set result to key and iv */ - if (cipher == NULL) { - Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(tmpkeyiv, dk_len)); - } else { - Tcl_Obj *resultObj = Tcl_NewListObj(0, NULL); - LAPPEND_BARRAY(interp, resultObj, "key", tmpkeyiv, iklen); - LAPPEND_BARRAY(interp, resultObj, "iv", tmpkeyiv+iklen, ivlen); - Tcl_SetObjResult(interp, resultObj); - } - - /* Clear data */ - memset(tmpkeyiv, 0, buf_len); - return TCL_OK; -} - -/* - *------------------------------------------------------------------- - * - * KDF_HKDF -- - * - * HMAC-based Extract-and-Expand Key Derivation Function (HKDF). - * See RFC 5869. - * - * Returns: - * TCL_OK or TCL_ERROR - * - * Side effects: - * Sets result to a key of specified length, or an error message - * - *------------------------------------------------------------------- - */ -static int KDF_HKDF(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - EVP_PKEY_CTX *pctx; - const EVP_MD *md = NULL; - unsigned char *salt = NULL, *key = NULL, *info = NULL, *out = NULL; - int salt_len = 0, key_len = 0, info_len = 0, dk_len = 1024, res = TCL_OK, fn; - char *digestName; - size_t out_len; - Tcl_Obj *resultObj; - - dprintf("Called"); - - /* Clear errors */ - Tcl_ResetResult(interp); - ERR_clear_error(); - - /* Validate arg count */ - if (objc < 3 || objc > 11) { - Tcl_WrongNumArgs(interp, 1, objv, "-digest digest -key string ?-info string? ?-salt string? ?-size derived_length?"); - return TCL_ERROR; - } - - /* Get options */ - for (int idx = 1; idx < objc; idx++) { - /* Get option */ - if (Tcl_GetIndexFromObj(interp, objv[idx], command_opts, "option", 0, &fn) != TCL_OK) { - return TCL_ERROR; - } - - /* Validate arg has a value */ - if (++idx >= objc) { - Tcl_AppendResult(interp, "No value for option \"", command_opts[fn], "\"", (char *) NULL); - return TCL_ERROR; - } - - switch(fn) { - case _opt_digest: - case _opt_hash: - GET_OPT_STRING(objv[idx], digestName, NULL); - break; - case _opt_info: - /* Max 1024/2048 */ - GET_OPT_BYTE_ARRAY(objv[idx], info, &info_len); - break; - case _opt_key: - case _opt_password: - GET_OPT_BYTE_ARRAY(objv[idx], key, &key_len); - break; - case _opt_salt: - GET_OPT_BYTE_ARRAY(objv[idx], salt, &salt_len); - break; - case _opt_length: - case _opt_size: - GET_OPT_INT(objv[idx], &dk_len); - break; - } - } - - /* Get digest */ - if ((md = Util_GetDigest(interp, digestName, TRUE)) == NULL) { - goto error; - } - - /* Create context */ - pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); - if (pctx == NULL) { - Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); - goto error; - } - - if (EVP_PKEY_derive_init(pctx) < 1) { - Tcl_AppendResult(interp, "Initialize failed: ", REASON(), NULL); - goto error; - } - - /* Set config parameters */ - if (EVP_PKEY_CTX_set_hkdf_md(pctx, md) < 1) { - Tcl_AppendResult(interp, "Set digest failed: ", REASON(), NULL); - goto error; - } - if (EVP_PKEY_CTX_set1_hkdf_key(pctx, key, key_len) < 1) { - Tcl_AppendResult(interp, "Set key failed: ", REASON(), NULL); - goto error; - } - if (salt != NULL && EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, salt_len) < 1) { - Tcl_AppendResult(interp, "Set salt failed: ", REASON(), NULL); - goto error; - } - if (info != NULL && EVP_PKEY_CTX_add1_hkdf_info(pctx, info, info_len) < 1) { - Tcl_AppendResult(interp, "Set info failed: ", REASON(), NULL); - goto error; - } - - /* Get buffer */ - resultObj = Tcl_NewObj(); - if ((out = Tcl_SetByteArrayLength(resultObj, dk_len)) == NULL) { - Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); - goto error; - } - out_len = (size_t) dk_len; - - /* Derive key */ - if (EVP_PKEY_derive(pctx, out, &out_len) > 0) { - /* Shrink buffer to actual size */ - Tcl_SetByteArrayLength(resultObj, (int) out_len); - Tcl_SetObjResult(interp, resultObj); - goto done; - } else { - Tcl_AppendResult(interp, "Derive key failed: ", REASON(), NULL); - Tcl_DecrRefCount(resultObj); - } - -error: - res = TCL_ERROR; -done: - if (pctx != NULL) { - EVP_PKEY_CTX_free(pctx); - } - return TCL_OK; -} - -/* - *------------------------------------------------------------------- - * - * KDF_Scrypt -- - * - * HMAC-based Extract-and-Expand Key Derivation Function (HKDF). - * See RFC 5869 and RFC 7914. - * - * Returns: - * TCL_OK or TCL_ERROR - * - * Side effects: - * Sets result to a list of key and iv values, or an error message - * - *------------------------------------------------------------------- - */ -static int KDF_Scrypt(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { - EVP_PKEY_CTX *pctx; - unsigned char *salt = NULL, *pass = NULL, *out = NULL; - int salt_len = 0, pass_len = 0, dk_len = 64, res = TCL_OK, fn; - uint64_t N = 0, p = 0, r = 0, maxmem = 0; - size_t out_len; - Tcl_Obj *resultObj; - - dprintf("Called"); - - /* Clear errors */ - Tcl_ResetResult(interp); - ERR_clear_error(); - - /* Validate arg count */ - if (objc < 5 || objc > 13) { - Tcl_WrongNumArgs(interp, 1, objv, "-password string -salt string ?-N costParameter? ?-r blockSize? ?-p parallelization? ?-size derived_length?"); - return TCL_ERROR; - } - - /* Get options */ - for (int idx = 1; idx < objc; idx++) { - /* Get option */ - if (Tcl_GetIndexFromObj(interp, objv[idx], command_opts, "option", 0, &fn) != TCL_OK) { - return TCL_ERROR; - } - - /* Validate arg has a value */ - if (++idx >= objc) { - Tcl_AppendResult(interp, "No value for option \"", command_opts[fn], "\"", (char *) NULL); - return TCL_ERROR; - } - - switch(fn) { - case _opt_key: - case _opt_password: - GET_OPT_BYTE_ARRAY(objv[idx], pass, &pass_len); - break; - case _opt_salt: - GET_OPT_BYTE_ARRAY(objv[idx], salt, &salt_len); - break; - case _opt_length: - case _opt_size: - GET_OPT_INT(objv[idx], &dk_len); - break; - case _opt_N: - case _opt_n: - GET_OPT_WIDE(objv[idx], &N); - break; - case _opt_r: - GET_OPT_WIDE(objv[idx], &r); - break; - case _opt_p: - GET_OPT_WIDE(objv[idx], &p); - break; - } - } - - /* Create context */ - pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_SCRYPT, NULL); - if (pctx == NULL) { - Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); - goto error; - } - - if (EVP_PKEY_derive_init(pctx) < 1) { - Tcl_AppendResult(interp, "Initialize failed: ", REASON(), NULL); - goto error; - } - - /* Set config parameters */ - if (EVP_PKEY_CTX_set1_pbe_pass(pctx, pass, pass_len) < 1) { - Tcl_AppendResult(interp, "Set key failed: ", REASON(), NULL); - goto error; - } - if (EVP_PKEY_CTX_set1_scrypt_salt(pctx, salt, salt_len) < 1) { - Tcl_AppendResult(interp, "Set salt failed: ", REASON(), NULL); - goto error; - } - if (N != 0 && EVP_PKEY_CTX_set_scrypt_N(pctx, N) < 1) { - Tcl_AppendResult(interp, "Set cost parameter (N) failed: ", REASON(), NULL); - goto error; - } - if (r != 0 && EVP_PKEY_CTX_set_scrypt_r(pctx, r) < 1) { - Tcl_AppendResult(interp, "Set lock size parameter (r) failed: ", REASON(), NULL); - goto error; - } - if (p != 0 && EVP_PKEY_CTX_set_scrypt_p(pctx, p) < 1) { - Tcl_AppendResult(interp, "Set Parallelization parameter (p) failed: ", REASON(), NULL); - goto error; - } - if (maxmem != 0 && EVP_PKEY_CTX_set_scrypt_maxmem_bytes(pctx, maxmem) < 1) { - Tcl_AppendResult(interp, "Set max memory failed: ", REASON(), NULL); - goto error; - } - - /* Get buffer */ - resultObj = Tcl_NewObj(); - if ((out = Tcl_SetByteArrayLength(resultObj, dk_len)) == NULL) { - Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL); - goto error; - } - out_len = (size_t) dk_len; - - /* Derive key */ - if (EVP_PKEY_derive(pctx, out, &out_len) > 0) { - /* Shrink buffer to actual size */ - Tcl_SetByteArrayLength(resultObj, (int) out_len); - Tcl_SetObjResult(interp, resultObj); - goto done; - } else { - Tcl_AppendResult(interp, "Derive key failed: ", REASON(), NULL); - Tcl_DecrRefCount(resultObj); - } - -error: - res = TCL_ERROR; -done: - if (pctx != NULL) { - EVP_PKEY_CTX_free(pctx); - } - return res; -} - -/* - *------------------------------------------------------------------- - * - * Tls_KeyCommands -- - * - * Create key commands - * - * Returns: - * TCL_OK or TCL_ERROR - * - * Side effects: - * Creates commands - * - *------------------------------------------------------------------- - */ -int Tls_KeyCommands(Tcl_Interp *interp) { - Tcl_CreateObjCommand(interp, "tls::hkdf", KDF_HKDF, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); - Tcl_CreateObjCommand(interp, "tls::pbkdf2", KDF_PBKDF2, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); - Tcl_CreateObjCommand(interp, "tls::scrypt", KDF_Scrypt, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); - return TCL_OK; -} - ADDED generic/tlsUtil.c Index: generic/tlsUtil.c ================================================================== --- /dev/null +++ generic/tlsUtil.c @@ -0,0 +1,254 @@ +/* + * Cryptographic Utility Functions + * + * Provides commands to derive keys. + * + * Copyright (C) 2023 Brian O'Hagan + * + */ + +#include "tlsInt.h" +#include "tclOpts.h" +#include + + +/* + *------------------------------------------------------------------- + * + * Util_GetCipher -- + * + * Get symmetric cipher from TclObj + * + * Returns: + * Pointer to type or NULL + * + * Side effects: + * None + * + *------------------------------------------------------------------- + */ +EVP_CIPHER *Util_GetCipher(Tcl_Interp *interp, Tcl_Obj *cipherObj, int no_null) { + EVP_CIPHER *cipher = NULL; + char *name = NULL; + + if (cipherObj != NULL) { + name = Tcl_GetStringFromObj(cipherObj, NULL); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + cipher = EVP_get_cipherbyname(name); +#else + cipher = EVP_CIPHER_fetch(NULL, name, NULL); +#endif + if (cipher == NULL) { + Tcl_AppendResult(interp, "invalid cipher \"", name, "\"", (char *) NULL); + } + } else if (no_null) { + Tcl_AppendResult(interp, "no cipher", (char *) NULL); + } + return cipher; +} + +/* + *------------------------------------------------------------------- + * + * Util_GetDigest -- + * + * Get message digest (MD) or hash from TclObj + * + * Returns: + * Pointer to type or NULL + * + * Side effects: + * None + * + *------------------------------------------------------------------- + */ +EVP_MD *Util_GetDigest(Tcl_Interp *interp, Tcl_Obj *digestObj, int no_null) { + EVP_MD *md = NULL; + char *name = NULL; + + if (digestObj != NULL) { + name = Tcl_GetStringFromObj(digestObj, NULL); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + md = EVP_get_digestbyname(name); +#else + md = EVP_MD_fetch(NULL, name, NULL); +#endif + if (md == NULL) { + Tcl_AppendResult(interp, "invalid digest \"", name, "\"", (char *) NULL); + } + } else if (no_null) { + Tcl_AppendResult(interp, "no digest", (char *) NULL); + } + return md; +} + +/* + *------------------------------------------------------------------- + * + * Util_GetIV -- + * + * Get encryption initialization vector or seed from TclObj + * + * Returns: + * Pointer to type or NULL, and size + * + * Side effects: + * None + * + *------------------------------------------------------------------- + */ +unsigned char *Util_GetIV(Tcl_Interp *interp, Tcl_Obj *ivObj, int *len, int max, int no_null) { + unsigned char *iv = NULL; + *len = 0; + + if (ivObj != NULL) { + iv = Tcl_GetByteArrayFromObj(ivObj, len); + } else if (no_null) { + Tcl_AppendResult(interp, "no initialization vector (IV)", (char *) NULL); + return NULL; + } + + if (max > 0 && *len > max) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("IV too long. Must be <= %d bytes", max)); + return NULL; + } + return iv; +} + +/* + *------------------------------------------------------------------- + * + * Util_GetKey -- + * + * Get encryption key or password from TclObj + * + * Returns: + * Pointer to type or NULL, and size + * + * Side effects: + * None + * + *------------------------------------------------------------------- + */ +unsigned char *Util_GetKey(Tcl_Interp *interp, Tcl_Obj *keyObj, int *len, char *name, int max, int no_null) { + unsigned char *key = NULL; + *len = 0; + + if (keyObj != NULL) { + key = Tcl_GetByteArrayFromObj(keyObj, len); + } else if (no_null) { + Tcl_AppendResult(interp, "no ", name, (char *) NULL); + return NULL; + } + + if (max > 0 && *len > max) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("Invalid %s length. Must be <= %d bytes", name, max)); + return NULL; + } + return key; +} + +/* + *------------------------------------------------------------------- + * + * Util_GetMAC -- + * + * Get Message Authentication Code (MAC) from TclObj + * + * Returns: + * Pointer to type or NULL + * + * Side effects: + * None + * + *------------------------------------------------------------------- + */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +EVP_MAC *Util_GetMAC(Tcl_Interp *interp, Tcl_Obj *MacObj, int no_null) { + EVP_MAC *mac = NULL; + char *name = NULL; + + if (MacObj != NULL) { + name = Tcl_GetStringFromObj(MacObj, NULL); + mac = EVP_MAC_fetch(NULL, name, NULL); + if (mac == NULL) { + Tcl_AppendResult(interp, "invalid MAC \"", name, "\"", (char *) NULL); + return NULL; + } + } else if (no_null) { + Tcl_AppendResult(interp, "no MAC", (char *) NULL); + } + return mac; +} +#endif + +/* + *------------------------------------------------------------------- + * + * Util_GetSalt -- + * + * Get encryption salt from TclObj + * + * Returns: + * Pointer to type or NULL, and size + * + * Side effects: + * None + * + *------------------------------------------------------------------- + */ +unsigned char *Util_GetSalt(Tcl_Interp *interp, Tcl_Obj *saltObj, int *len, int max, int no_null) { + unsigned char *salt = NULL; + *len = 0; + + if (saltObj != NULL) { + salt = Tcl_GetByteArrayFromObj(saltObj, len); + } else if (no_null) { + Tcl_AppendResult(interp, "no salt", (char *) NULL); + return NULL; + } + + if (max > 0 && *len > max) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("Salt too long. Must be <= %d bytes", max)); + return NULL; + } + return salt; +} + +/*******************************************************************/ + +/* + *------------------------------------------------------------------- + * + * Util_GetInt -- + * + * Get integer value from TclObj + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * None + * + *------------------------------------------------------------------- + */ + +int Util_GetInt(Tcl_Interp *interp, Tcl_Obj *dataObj, int *value, char *name, int min, int max) { + + if (dataObj != NULL) { + if (Tcl_GetIntFromObj(interp, dataObj, value) != TCL_OK) { + return TCL_ERROR; + } + } + + /* Validate range */ + if (*value < min) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("invalid value \"%d\" for option \"%s\": must be >= %d", *value, name, min)); + return TCL_ERROR; + } else if (max > 0 && *value > max) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("invalid value \"%d\" for option \"%s\": must be <= %d", *value, name, max)); + return TCL_ERROR; + } + return TCL_OK; +} + Index: win/makefile.vc ================================================================== --- win/makefile.vc +++ win/makefile.vc @@ -29,11 +29,12 @@ $(TMP_DIR)\tlsBIO.obj \ $(TMP_DIR)\tlsDigest.obj \ $(TMP_DIR)\tlsEncrypt.obj \ $(TMP_DIR)\tlsInfo.obj \ $(TMP_DIR)\tlsIO.obj \ - $(TMP_DIR)\tlsKey.obj \ + $(TMP_DIR)\tlsKDF.obj \ + $(TMP_DIR)\tlsUtil.obj \ $(TMP_DIR)\tlsX509.obj # Define any additional project include flags # SSL_INSTALL_FOLDER = with the OpenSSL installation folder following. PRJ_INCLUDES = -I"$(SSL_INSTALL_FOLDER)\include" -I"$(OPENSSL_INSTALL_DIR)\include"