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 tlsX509.c" + vars="tls.c tlsBIO.c tlsDigest.c tlsEncrypt.c tlsInfo.c tlsIO.c tlsKey.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 tlsX509.c]) +TEA_ADD_SOURCES([tls.c tlsBIO.c tlsDigest.c tlsEncrypt.c tlsInfo.c tlsIO.c tlsKey.c tlsX509.c]) TEA_ADD_HEADERS([generic/tls.h]) TEA_ADD_INCLUDES([]) TEA_ADD_LIBS([]) TEA_ADD_CFLAGS([]) TEA_ADD_STUB_SOURCES([]) Index: doc/cryptography.html ================================================================== --- doc/cryptography.html +++ doc/cryptography.html @@ -38,10 +38,12 @@
tls::sha512 data
tls::unstack channelId
 
tls::encrypt -cipher name -key key ?options?
tls::decrypt -cipher name -key key ?options?
+
 
+
tls::derive_key key ?options?
OPTIONS
COMMANDS
GLOSSARY
@@ -85,10 +87,12 @@ tls::sha512 data
tls::unstack channelId

tls::encrypt -cipher name -key key ?options?
tls::decrypt -cipher name -key key ?options?
+
+tls::derive_key ?options?


OPTIONS

@@ -111,31 +115,31 @@ See tls::digests for the valid values.
-iterations count
-
Number (integer) of iterations on the password to use in deriving the - encryption key. Default is 10000. Some KDF implementations require an - iteration count.
+
Number (integer > 0) of iterations to use in deriving the encryption + key. Default is 2048. Some KDF implementations + require an iteration count.
-iv string
Initialization vector (IV) to use. Required for some ciphers and GMAC. - Cipher modes CBC, CFB, OFB and CTR all need an IV while ECB mode does not. + Cipher modes CBC, CFB, and OFB all need an IV while ECB and CTR modes do not. A new, random IV should be created for each use. Think of the IV as a nonce (number used once), it's public but random and unpredictable. See the tls::cipher for iv_length and - when required (length > 0). If not set, it will default to \x00 fill data.
+ when required (length > 0). Max is 16 bytes. If not set, it will default to \x00 fill data.
-key string
Encryption key to use for cryptography function. Can be a binary or text string. Longer keys provide better protection. Used by ciphers, HMAC, some CMAC, and some KDF implementations. If the length of the key is < - key_length it will be padded. If > key_length, it will be rejected. + key_length it will be padded. Max is 64 bytes. If > key_length, it will be rejected. See the tls::cipher for key_length.
-mac name
@@ -143,23 +147,25 @@ See tls::macs for the valid values.
-password string
-
Password to use for some KDF functions.
+
Password to use for some KDF functions. If not specified, the default + value is used. Can be a binary or text string.
-properties list
-
List of additional properties to pass to cryptography function.
+
List of additional properties to pass to cryptographic function.
-salt string
-
Specifies salt value to use when encrypting data. Default is to use a - randomly generated value. This option is used by BLAKE2 MAC and some KDF - implementations use a non-secret unique cryptographic salt.
+
Specifies salt value to use when encrypting data. Can be a binary or + text string. Default is to use a randomly generated value. This option is + used by BLAKE2 MAC and some KDF implementations use a non-secret unique + cryptographic salt.
-size number
Set the output hash size in bytes. Used by KMAC128 or KMAC256 to specify @@ -282,11 +288,11 @@

The following commands provide access to the OpenSSL cryptography functions.

-

Info Commands

+

Info Commands

tls::cipher name
Return a list of property names and values describing cipher name. Properties include name, description, block_size, key_length, iv_length, type, and mode list. If block-size is 1, @@ -327,11 +333,11 @@
tls::version
Returns the OpenSSL version string.

-

Message Digest (MD) and Message Authentication Code (MAC) Commands

+

Message Digest (MD) and Message Authentication Code (MAC) Commands

tls::cmac ?-cipher? name -key key ?-bin|-hex? [-chan channelId | -command cmdName | @@ -392,11 +398,11 @@
tls::unstack channelId
Removes the top level cryptographic transform from channel channelId.

-

Encryption and Decryption Commands

+

Encryption and Decryption Commands

tls::encrypt ?-cipher? name -key key ?-iv string? [-chan channelId | -command cmdName | -infile filename -outfile filename | @@ -419,10 +425,27 @@ info. Option -iv is only used for some ciphers. See the "tls::cipher cipher" command for key and iv sizes and when the iv is used (iv_length > 0).
+
+ +

Key Derivation Function (KDF) Commands

+ +
tls::derive_key + [-cipher cipher | -size size] + -digest digest ?-iterations count? + ?-password string? ?-salt string?
+
Derive a key and initialization vector (iv) from a password and salt + value using PKCS5_PBKDF2_HMAC. This is a more secure way to generate + keys and ivs for use by tls::encrypt. + See options for usage info. If -cipher + is specified, then the derived key and iv sized for that cipher are + returned as a key-value list. If not or if -size is specified, + then the derived key (dk) of size bytes is returned.
+ +

GLOSSARY

The following is a list of the terminology used in this package along with brief definitions. For more details, please consult with the OpenSSL documentation.

Index: generic/tls.c ================================================================== --- generic/tls.c +++ generic/tls.c @@ -2550,10 +2550,11 @@ Tcl_CreateObjCommand(interp, "tls::status", StatusObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); Tls_DigestCommands(interp); Tls_EncryptCommands(interp); Tls_InfoCommands(interp); + Tls_KeyCommands(interp); if (interp) { Tcl_Eval(interp, tlsTclInitScript); } Index: generic/tlsDigest.c ================================================================== --- generic/tlsDigest.c +++ generic/tlsDigest.c @@ -1239,10 +1239,11 @@ OPTOBJ("-command", cmdObj); OPTOBJ("-data", dataObj); OPTOBJ("-digest", digestObj); OPTOBJ("-file", fileObj); OPTOBJ("-filename", fileObj); + OPTOBJ("-hash", digestObj); OPTOBJ("-key", keyObj); OPTOBJ("-mac", macObj); OPTBAD("option", "-bin, -channel, -cipher, -command, -data, -digest, -file, -filename, -hex, -key, or -mac"); return TCL_ERROR; Index: generic/tlsEncrypt.c ================================================================== --- generic/tlsEncrypt.c +++ generic/tlsEncrypt.c @@ -1248,10 +1248,11 @@ OPTSTR("-channel", channel); OPTOBJ("-cipher", cipherObj); OPTOBJ("-command", cmdObj); OPTOBJ("-data", dataObj); OPTOBJ("-digest", digestObj); + OPTOBJ("-hash", digestObj); OPTOBJ("-infile", inFileObj); OPTOBJ("-outfile", outFileObj); OPTOBJ("-key", keyObj); OPTOBJ("-iv", ivObj); OPTOBJ("-mac", macObj); Index: generic/tlsInt.h ================================================================== --- generic/tlsInt.h +++ generic/tlsInt.h @@ -35,11 +35,10 @@ # define CONST86 const # else # define CONST86 # endif #endif - /* * Backwards compatibility for size type change */ #if TCL_MAJOR_VERSION < 9 && TCL_MINOR_VERSION < 7 # define Tcl_Size int @@ -196,11 +195,12 @@ 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); +int Tls_KeyCommands(Tcl_Interp *interp); BIO *BIO_new_tcl(State* statePtr, int flags); #define PTR2INT(x) ((int) ((intptr_t) (x))) #endif /* _TLSINT_H */ ADDED generic/tlsKey.c Index: generic/tlsKey.c ================================================================== --- /dev/null +++ generic/tlsKey.c @@ -0,0 +1,152 @@ +/* + * Key Derivation Function (KDF) Module + * + * Provides commands to derive keys. + * + * Copyright (C) 2023 Brian O'Hagan + * + */ + +#include "tlsInt.h" +#include "tclOpts.h" +#include + +/*******************************************************************/ + +/* + *------------------------------------------------------------------- + * + * DeriveKey -- + * + * PKCS5_PBKDF2_HMAC key derivation function (KDF) specified by PKCS #5. + * See RFC 6070. + * + * Returns: + * TCL_OK or TCL_ERROR + * + * Side effects: + * Sets result to a list of key and iv values, or an error message + * + *------------------------------------------------------------------- + */ +static int DeriveKey(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + int key_len = 0, md_len = 0, pass_len = 0, salt_len = 0; + int iklen, ivlen, iter = PKCS5_DEFAULT_ITER; + unsigned char *passwd = NULL, *salt = NULL; + Tcl_Obj *cipherObj = NULL, *digestObj = NULL, *passwdObj = NULL, *saltObj = NULL, *resultObj; + const EVP_MD *md = NULL; + const EVP_CIPHER *cipher = NULL; + int max = EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH, size = max; + unsigned char tmpkeyiv[EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH]; + + 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, EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH); + + /* Get options */ + for (int idx = 1; idx < objc; idx++) { + char *opt = Tcl_GetStringFromObj(objv[idx], NULL); + + if (opt[0] != '-') { + break; + } + + OPTOBJ("-cipher", cipherObj); + OPTOBJ("-digest", digestObj); + OPTOBJ("-hash", digestObj); + OPTINT("-iterations", iter); + OPTOBJ("-password", passwdObj); + OPTOBJ("-salt", saltObj); + OPTINT("-size", size); + + OPTBAD("option", "-cipher, -digest, -iterations, -password, -salt, or -size option"); + return TCL_ERROR; + } + + /* Validate options */ + if (cipherObj != NULL) { + char *name = Tcl_GetByteArrayFromObj(cipherObj, NULL); + cipher = EVP_get_cipherbyname(name); + } + if (digestObj != NULL) { + char *name = Tcl_GetStringFromObj(digestObj, &md_len); + md = EVP_get_digestbyname(name); + } else { + Tcl_AppendResult(interp, "No digest specified", NULL); + return TCL_ERROR; + } + if (passwdObj != NULL) { + passwd = Tcl_GetByteArrayFromObj(passwdObj, &pass_len); + } + if (saltObj != NULL) { + salt = Tcl_GetByteArrayFromObj(saltObj, &salt_len); + } + if (iter < 1) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("Invalid iterations count %d: must be > 0", iter)); + return TCL_ERROR; + } + if (size < 1 || size > max) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("Invalid derived key length %d: must be 0 < size <= %d", size, max)); + return TCL_ERROR; + } + + if (cipher == NULL) { + if (size > max) size = max; + iklen = size; + ivlen = 0; + } else { + iklen = EVP_CIPHER_key_length(cipher); + ivlen = EVP_CIPHER_iv_length(cipher); + size = iklen+ivlen; + } + + /* Perform password derivation */ + if (!PKCS5_PBKDF2_HMAC(passwd, pass_len, salt, salt_len, iter, md, size, tmpkeyiv)) { + Tcl_AppendResult(interp, "Key derivation failed: ", REASON(), NULL); + return TCL_ERROR; + } + + /* Return key and iv */ + if (cipher == NULL) { + resultObj = Tcl_NewByteArrayObj(tmpkeyiv, size); + } else { + resultObj = Tcl_NewListObj(0, NULL); + LAPPEND_BARRAY(interp, resultObj, "key", tmpkeyiv, iklen); + LAPPEND_BARRAY(interp, resultObj, "iv", tmpkeyiv+iklen, ivlen); + } + Tcl_SetObjResult(interp, resultObj); + return TCL_OK; + clientData = clientData; +} + +/* + *------------------------------------------------------------------- + * + * 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::derive_key", DeriveKey, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + return TCL_OK; +} + Index: win/makefile.vc ================================================================== --- win/makefile.vc +++ win/makefile.vc @@ -29,10 +29,11 @@ $(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)\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"