/* * Key Derivation Function (KDF) Module * * Provides commands to derive keys. * * Copyright (C) 2023 Brian O'Hagan * */ #include "tlsInt.h" #include "tclOpts.h" #include /*******************************************************************/ static const char *command_opts [] = { "-cipher", "-digest", "-hash", "-iterations", "-password", "-salt", "-size", NULL}; enum _command_opts { _opt_cipher, _opt_digest, _opt_hash, _opt_iter, _opt_password, _opt_salt, _opt_size }; /* *------------------------------------------------------------------- * * 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, fn; int iklen, ivlen, iter = PKCS5_DEFAULT_ITER; unsigned char *passwd = NULL, *salt = NULL; Tcl_Obj *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]; 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, EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH); /* 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 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_password: GET_OPT_BYTE_ARRAY(objv[idx], passwd, &pass_len); break; case _opt_salt: GET_OPT_BYTE_ARRAY(objv[idx], salt, &salt_len); break; case _opt_size: GET_OPT_INT(objv[idx], &size); break; } } /* Validate options */ if (cipherName != NULL) { #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; } } if (digestName != NULL) { #if OPENSSL_VERSION_NUMBER < 0x30000000L md = EVP_get_digestbyname(digestName); #else md = EVP_MD_fetch(NULL, digestName, NULL); #endif if (md == NULL) { Tcl_AppendResult(interp, "Invalid digest: \"", digestName, "\"", NULL); return TCL_ERROR; } } 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; }