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?
@@ -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
+
- 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
+
- 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
+
- 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).
+
+
+
+
+ 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.
+
+
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"