aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/luaossl.tex31
-rwxr-xr-xregress/95-kdf.lua45
-rw-r--r--regress/regress.lua1
-rw-r--r--src/GNUmakefile1
-rw-r--r--src/openssl.c689
-rw-r--r--src/openssl.kdf.lua3
6 files changed, 770 insertions, 0 deletions
diff --git a/doc/luaossl.tex b/doc/luaossl.tex
index c63df57..8561957 100644
--- a/doc/luaossl.tex
+++ b/doc/luaossl.tex
@@ -1457,6 +1457,37 @@ Applies DES\_set\_odd\_parity to the string $key$. Only the first 8 bytes of $ke
\end{Module}
+\begin{Module}{openssl.kdf}
+
+Binds OpenSSL's Key Derivation Function interfaces.
+
+\subsubsection[\fn{kdf.derive}]{\fn{kdf.derive($options$)}}
+
+Derive a key given the table of $options$, different KDF types require different options. Accepted options are:
+
+\begin{ctabular}{ c | c | p{5in}}
+field & type & description\\\hline
+.type & string & key derivation algorithm---``PBKDF2'', ``id-scrypt'', ``hkdf'', ``TLS-PRF1'' or other depending on your version of OpenSSL \\
+.outlen & number & the desired output size \\
+.pass & string & password \\
+.salt & string & salt \\
+.iter & number & iteration count \\
+.md & string & digest to use \\
+.key & string & key \\
+.maxmem\_bytes & number & amount of RAM key derivation may maximally use (in bytes) \\
+.secret & string & TLS1-PRF secret \\
+.seed & string & TLS1-PRF seed \\
+.hkdf\_mode & string & the HKDF mode to use, one of ``extract\_and\_expand'', ``extract\_only'' or ``expand\_only'' \\
+.info & string & HKDF info value \\
+.N & number & scrypt ``N'' parameter to use \\
+.r & number & scrypt ``r'' parameter to use \\
+.p & number & scrypt ``p'' parameter to use
+\end{ctabular}
+
+
+\end{Module}
+
+
\chapter{Examples}
These examples and others are made available under examples/ in the source tree.
diff --git a/regress/95-kdf.lua b/regress/95-kdf.lua
new file mode 100755
index 0000000..dd7cff4
--- /dev/null
+++ b/regress/95-kdf.lua
@@ -0,0 +1,45 @@
+#!/usr/bin/env lua
+
+local regress = require "regress"
+local kdf = require "openssl.kdf"
+
+local function hexstring(str)
+ return (str:gsub("..", function(b) return string.char(tonumber(b, 16)) end))
+end
+
+-- Scrypt Example
+regress.check(kdf.derive{
+ type = "id-scrypt"; -- the nid short-name is id-scrypt
+ pass = "";
+ salt = "";
+ N = 16;
+ r = 1;
+ p = 1;
+ outlen = 64;
+} == hexstring"77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906",
+ "scrypt output doesn't match test vector")
+
+-- PBKDF2 Example
+regress.check(kdf.derive{
+ type = "PBKDF2";
+ pass = "password";
+ salt = "salt";
+ iter = 1;
+ md = "sha1";
+ outlen = 20;
+} == hexstring"0c60c80f961f0e71f3a9b524af6012062fe037a6",
+ "PBKDF2 output doesn't match test vector")
+
+-- TLS1-PRF Example
+regress.check(kdf.derive{
+ type = "TLS1-PRF";
+ md = "md5-sha1";
+ secret = hexstring"bded7fa5c1699c010be23dd06ada3a48349f21e5f86263d512c0c5cc379f0e780ec55d9844b2f1db02a96453513568d0";
+ seed = "master secret"
+ .. hexstring"e5acaf549cd25c22d964c0d930fa4b5261d2507fad84c33715b7b9a864020693"
+ .. hexstring"135e4d557fdf3aa6406d82975d5c606a9734c9334b42136e96990fbd5358cdb2";
+ outlen = 48;
+} == hexstring"2f6962dfbc744c4b2138bb6b3d33054c5ecc14f24851d9896395a44ab3964efc2090c5bf51a0891209f46c1e1e998f62",
+ "TLS1-PRF output doesn't match test vector")
+
+regress.say "OK"
diff --git a/regress/regress.lua b/regress/regress.lua
index b89e996..19ee065 100644
--- a/regress/regress.lua
+++ b/regress/regress.lua
@@ -3,6 +3,7 @@ local require = require -- may be overloaded by regress.require
local regress = {
openssl = require"openssl",
bignum = require"openssl.bignum",
+ kdf = require"openssl.kdf",
pkey = require"openssl.pkey",
x509 = require"openssl.x509",
name = require"openssl.x509.name",
diff --git a/src/GNUmakefile b/src/GNUmakefile
index f5c7c55..9a568a1 100644
--- a/src/GNUmakefile
+++ b/src/GNUmakefile
@@ -80,6 +80,7 @@ MODS$(1)_$(d) = \
$$(DESTDIR)$(3)/openssl.lua \
$$(DESTDIR)$(3)/openssl/auxlib.lua \
$$(DESTDIR)$(3)/openssl/bignum.lua \
+ $$(DESTDIR)$(3)/openssl/kdf.lua \
$$(DESTDIR)$(3)/openssl/ocsp/basic.lua \
$$(DESTDIR)$(3)/openssl/ocsp/response.lua \
$$(DESTDIR)$(3)/openssl/pkey.lua \
diff --git a/src/openssl.c b/src/openssl.c
index 9248b0d..082e450 100644
--- a/src/openssl.c
+++ b/src/openssl.c
@@ -127,6 +127,18 @@
#define HAVE_C___DECLSPEC_NORETURN MSC_PREREQ(8,0,0)
#endif
+#ifndef HAVE_OPENSSL_ZALLOC
+#define HAVE_OPENSSL_ZALLOC OPENSSL_PREREQ(1,1,0)
+#endif
+
+#ifndef HAVE_OPENSSL_CLEAR_FREE
+#define HAVE_OPENSSL_CLEAR_FREE OPENSSL_PREREQ(1,1,0)
+#endif
+
+#ifndef HAVE_OPENSSL_MEMDUP
+#define HAVE_OPENSSL_MEMDUP OPENSSL_PREREQ(1,1,0)
+#endif
+
#ifndef HAVE_ASN1_STRING_GET0_DATA
#define HAVE_ASN1_STRING_GET0_DATA (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
#endif
@@ -207,6 +219,18 @@
#define HAVE_EVP_CIPHER_CTX_NEW (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
#endif
+#ifndef HAVE_EVP_KDF_CTX
+#define HAVE_EVP_KDF_CTX OPENSSL_PREREQ(1,2,0)
+#endif
+
+#ifndef HAVE_PKCS5_PBKDF2_HMAC
+#define HAVE_PKCS5_PBKDF2_HMAC (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
+#endif
+
+#ifndef HAVE_SCRYPT
+#define HAVE_SCRYPT OPENSSL_PREREQ(1,1,0)
+#endif
+
#ifndef HAVE_EVP_MD_CTX_FREE
#define HAVE_EVP_MD_CTX_FREE (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
#endif
@@ -227,6 +251,14 @@
#define HAVE_EVP_PKEY_CTX_NEW (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
#endif
+#ifndef HAVE_EVP_PKEY_CTX_KDF
+#define HAVE_EVP_PKEY_CTX_KDF OPENSSL_PREREQ(1,1,0)
+#endif
+
+#ifndef HAVE_EVP_PKEY_CTX_HKDF_MODE
+#define HAVE_EVP_PKEY_CTX_HKDF_MODE (HAVE_EVP_PKEY_CTX_KDF && OPENSSL_PREREQ(1,1,1))
+#endif
+
#ifndef HAVE_EVP_PKEY_GET0
#define HAVE_EVP_PKEY_GET0 (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
#endif
@@ -511,6 +543,10 @@
#define HMAC_INIT_EX_INT OPENSSL_PREREQ(1,0,0)
#endif
+#if HAVE_EVP_PKEY_CTX_KDF || HAVE_EVP_KDF_CTX
+#include <openssl/kdf.h>
+#endif
+
#ifndef STRERROR_R_CHAR_P
#ifdef __GLIBC__
#define STRERROR_R_CHAR_P (_GNU_SOURCE || !(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600))
@@ -1497,6 +1533,32 @@ dlerr:
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+#if !HAVE_OPENSSL_ZALLOC
+static void *OPENSSL_zalloc(size_t num) {
+ void *ret = OPENSSL_malloc(num);
+
+ if (ret != NULL)
+ memset(ret, 0, num);
+ return ret;
+}
+#endif
+
+#if !HAVE_OPENSSL_CLEAR_FREE
+static void OPENSSL_clear_free(void *str, size_t num) {
+ if (str == NULL)
+ return;
+ if (num)
+ OPENSSL_cleanse(str, num);
+ CRYPTO_free(str);
+} /* CRYPTO_clear_free() */
+#endif
+
+#if !HAVE_OPENSSL_MEMDUP
+static void *OPENSSL_memdup(const void *data, size_t siz) {
+ return BUF_memdup(data, siz);
+} /* OPENSSL_memdup() */
+#endif
+
#define COMPAT_X509_STORE_FREE_BUG 0x01
static struct {
@@ -2080,6 +2142,410 @@ static int compat_X509_up_ref(X509 *crt) {
*/
#endif
+
+#if !HAVE_EVP_KDF_CTX
+/*
+ * Emulate EVP_KDF_CTX API (introduced in OpenSSL 1.2.0)
+ */
+
+#ifndef ERR_LIB_KDF
+#define ERR_LIB_KDF 0
+#endif
+
+#ifndef KDFerr
+#define KDFerr(f,r) ERR_PUT_error(ERR_LIB_KDF,f,(r),__FILE__,__LINE__)
+#endif
+
+#ifndef EVP_F_EVP_KDF_CTRL
+#define EVP_F_EVP_KDF_CTRL 0
+#endif
+
+#ifndef EVP_F_EVP_KDF_CTX_NEW_ID
+#define EVP_F_EVP_KDF_CTX_NEW_ID 0
+#endif
+
+typedef struct {
+ int nid;
+ union {
+#if HAVE_PKCS5_PBKDF2_HMAC
+ /* Arguments for PKCS5_PBKDF2_HMAC */
+ struct {
+ unsigned char *pass;
+ size_t passlen;
+ unsigned char *salt;
+ size_t saltlen;
+ int iter;
+ const EVP_MD *md;
+ } pbkdf2;
+#endif
+
+#if HAVE_SCRYPT
+ /* Arguments for EVP_PBE_scrypt */
+ struct {
+ unsigned char *pass;
+ size_t passlen;
+ unsigned char *salt;
+ size_t saltlen;
+ uint64_t N;
+ uint32_t r;
+ uint32_t p;
+ uint64_t maxmem_bytes;
+ } scrypt;
+#endif
+
+#if HAVE_EVP_PKEY_CTX_KDF
+ EVP_PKEY_CTX *pctx;
+#endif
+ };
+} EVP_KDF_CTX;
+
+static void EVP_KDF_CTX_free(EVP_KDF_CTX *kctx) {
+ if (kctx == NULL)
+ return;
+
+ switch(kctx->nid) {
+#if HAVE_PKCS5_PBKDF2_HMAC
+ case NID_id_pbkdf2:
+ OPENSSL_clear_free(kctx->pbkdf2.pass, kctx->pbkdf2.passlen);
+ OPENSSL_clear_free(kctx->pbkdf2.salt, kctx->pbkdf2.saltlen);
+ break;
+#endif
+#if HAVE_SCRYPT
+ case NID_id_scrypt:
+ OPENSSL_clear_free(kctx->scrypt.pass, kctx->scrypt.passlen);
+ OPENSSL_clear_free(kctx->scrypt.salt, kctx->scrypt.saltlen);
+ break;
+#endif
+#if HAVE_EVP_PKEY_CTX_KDF
+ case NID_tls1_prf:
+ case NID_hkdf:
+ EVP_PKEY_CTX_free(kctx->pctx);
+ break;
+#endif
+ }
+
+ OPENSSL_free(kctx);
+} /* EVP_KDF_CTX_free() */
+
+static EVP_KDF_CTX *EVP_KDF_CTX_new_id(int id) {
+ EVP_KDF_CTX *ret;
+
+ ret = OPENSSL_zalloc(sizeof(*ret));
+ if (ret == NULL) {
+ EVPerr(EVP_F_EVP_KDF_CTX_NEW_ID, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+
+ ret->nid = id;
+
+ switch(id) {
+#if HAVE_PKCS5_PBKDF2_HMAC
+ case NID_id_pbkdf2:
+ break;
+#endif
+#if HAVE_SCRYPT
+ case NID_id_scrypt:
+ break;
+#endif
+#if HAVE_EVP_PKEY_CTX_KDF
+ case NID_tls1_prf:
+ case NID_hkdf: {
+ ret->pctx = EVP_PKEY_CTX_new_id(id, NULL);
+ if (!ret->pctx) {
+ OPENSSL_free(ret);
+ return NULL;
+ }
+ if (EVP_PKEY_derive_init(ret->pctx) <= 0) {
+ EVP_KDF_CTX_free(ret);
+ return NULL;
+ }
+ break;
+ }
+ break;
+#endif
+ default:
+ OPENSSL_free(ret);
+ EVPerr(EVP_F_EVP_KDF_CTX_NEW_ID, EVP_R_UNSUPPORTED_ALGORITHM);
+ return NULL;
+ }
+
+ return ret;
+} /* EVP_KDF_CTX_new_id() */
+
+static int set_membuf(unsigned char **buffer, size_t *buflen, const unsigned char *new_buffer, size_t new_buflen) {
+ if (new_buffer == NULL)
+ return 1;
+ OPENSSL_clear_free(*buffer, *buflen);
+ if (new_buflen > 0) {
+ *buffer = OPENSSL_memdup(new_buffer, new_buflen);
+ } else {
+ *buffer = OPENSSL_malloc(1);
+ }
+ if (*buffer == NULL) {
+ KDFerr(EVP_F_EVP_KDF_CTRL, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+ *buflen = new_buflen;
+ return 1;
+}
+
+#define EVP_KDF_CTRL_SET_PASS 0x01 /* unsigned char *, size_t */
+#define EVP_KDF_CTRL_SET_SALT 0x02 /* unsigned char *, size_t */
+#define EVP_KDF_CTRL_SET_ITER 0x03 /* int */
+#define EVP_KDF_CTRL_SET_MD 0x04 /* EVP_MD * */
+#define EVP_KDF_CTRL_SET_KEY 0x05 /* unsigned char *, size_t */
+#define EVP_KDF_CTRL_SET_MAXMEM_BYTES 0x06 /* uint64_t */
+#define EVP_KDF_CTRL_SET_TLS_SECRET 0x07 /* unsigned char *, size_t */
+#define EVP_KDF_CTRL_RESET_TLS_SEED 0x08
+#define EVP_KDF_CTRL_ADD_TLS_SEED 0x09 /* unsigned char *, size_t */
+#define EVP_KDF_CTRL_RESET_HKDF_INFO 0x0a
+#define EVP_KDF_CTRL_ADD_HKDF_INFO 0x0b /* unsigned char *, size_t */
+#define EVP_KDF_CTRL_SET_HKDF_MODE 0x0c /* int */
+#define EVP_KDF_CTRL_SET_SCRYPT_N 0x0d /* uint64_t */
+#define EVP_KDF_CTRL_SET_SCRYPT_R 0x0e /* uint32_t */
+#define EVP_KDF_CTRL_SET_SCRYPT_P 0x0f /* uint32_t */
+
+#ifdef EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND
+#define EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND
+#elif HAVE_EVP_PKEY_CTX_KDF
+#define EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND 0
+#endif
+
+#ifdef EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY
+#define EVP_KDF_HKDF_MODE_EXTRACT_ONLY EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY
+#endif
+
+#ifdef EVP_PKEY_HKDEF_MODE_EXPAND_ONLY
+#define EVP_KDF_HKDF_MODE_EXPAND_ONLY EVP_PKEY_HKDEF_MODE_EXPAND_ONLY
+#endif
+
+static int EVP_KDF_vctrl(EVP_KDF_CTX *kctx, int cmd, va_list args) {
+ const EVP_MD *md;
+ const unsigned char *p;
+ size_t len;
+ uint64_t u64_value;
+ uint32_t value;
+ int iter, mode;
+
+ if (kctx == NULL)
+ return 0;
+
+ switch (kctx->nid) {
+#if HAVE_PKCS5_PBKDF2_HMAC
+ case NID_id_pbkdf2: {
+ switch (cmd) {
+ case EVP_KDF_CTRL_SET_PASS:
+ p = va_arg(args, const unsigned char *);
+ len = va_arg(args, size_t);
+ return set_membuf(&kctx->pbkdf2.pass, &(kctx->pbkdf2.passlen), p, len);
+
+ case EVP_KDF_CTRL_SET_SALT:
+ p = va_arg(args, const unsigned char *);
+ len = va_arg(args, size_t);
+ return set_membuf(&kctx->pbkdf2.salt, &kctx->pbkdf2.saltlen, p, len);
+
+ case EVP_KDF_CTRL_SET_ITER:
+ iter = va_arg(args, int);
+ if (iter < 1)
+ return 0;
+ kctx->pbkdf2.iter = iter;
+ return 1;
+
+ case EVP_KDF_CTRL_SET_MD:
+ md = va_arg(args, const EVP_MD *);
+ if (md == NULL)
+ return 0;
+ kctx->pbkdf2.md = md;
+ return 1;
+
+ default:
+ EVPerr(EVP_F_EVP_KDF_CTRL, EVP_R_COMMAND_NOT_SUPPORTED);
+ return -2;
+ }
+ }
+#endif
+#if HAVE_SCRYPT
+ case NID_id_scrypt: {
+ switch (cmd) {
+ case EVP_KDF_CTRL_SET_PASS:
+ p = va_arg(args, const unsigned char *);
+ len = va_arg(args, size_t);
+ return set_membuf(&kctx->scrypt.pass, &kctx->scrypt.passlen, p, len);
+
+ case EVP_KDF_CTRL_SET_SALT:
+ p = va_arg(args, const unsigned char *);
+ len = va_arg(args, size_t);
+ return set_membuf(&kctx->scrypt.salt, &kctx->scrypt.saltlen, p, len);
+
+ case EVP_KDF_CTRL_SET_SCRYPT_N:
+ u64_value = va_arg(args, uint64_t);
+ if ((u64_value <= 1) || ((u64_value & (u64_value - 1)) != 0)) /* is_power_of_two check */
+ return 0;
+ kctx->scrypt.N = u64_value;
+ return 1;
+
+ case EVP_KDF_CTRL_SET_SCRYPT_R:
+ value = va_arg(args, uint32_t);
+ if (value < 1)
+ return 0;
+ kctx->scrypt.r = value;
+ return 1;
+
+ case EVP_KDF_CTRL_SET_SCRYPT_P:
+ value = va_arg(args, uint32_t);
+ if (value < 1)
+ return 0;
+ kctx->scrypt.p = value;
+ return 1;
+
+ case EVP_KDF_CTRL_SET_MAXMEM_BYTES:
+ u64_value = va_arg(args, uint64_t);
+ if (u64_value < 1)
+ return 0;
+ kctx->scrypt.maxmem_bytes = u64_value;
+ return 1;
+
+ default:
+ EVPerr(EVP_F_EVP_KDF_CTRL, EVP_R_COMMAND_NOT_SUPPORTED);
+ return -2;
+ }
+ }
+#endif
+#if HAVE_EVP_PKEY_CTX_KDF
+ case NID_tls1_prf: {
+ switch (cmd) {
+ case EVP_KDF_CTRL_SET_MD:
+ md = va_arg(args, const EVP_MD *);
+ return EVP_PKEY_CTX_set_tls1_prf_md(kctx->pctx, md);
+
+ case EVP_KDF_CTRL_SET_TLS_SECRET:
+ p = va_arg(args, const unsigned char *);
+ len = va_arg(args, size_t);
+ /* XXX: the old api resets the seed when you set the secret. When
+ * using this compat routine, make sure you set the secret before
+ * the seed.
+ * https://github.com/openssl/openssl/issues/7728
+ */
+ return EVP_PKEY_CTX_set1_tls1_prf_secret(kctx->pctx, p, len);
+
+ case EVP_KDF_CTRL_ADD_TLS_SEED:
+ p = va_arg(args, const unsigned char *);
+ len = va_arg(args, size_t);
+ return EVP_PKEY_CTX_add1_tls1_prf_seed(kctx->pctx, p, len);
+
+ default:
+ EVPerr(EVP_F_EVP_KDF_CTRL, EVP_R_COMMAND_NOT_SUPPORTED);
+ return -2;
+ }
+ }
+ case NID_hkdf: {
+ switch (cmd) {
+ case EVP_KDF_CTRL_SET_SALT:
+ p = va_arg(args, const unsigned char *);
+ len = va_arg(args, size_t);
+ return EVP_PKEY_CTX_set1_hkdf_salt(kctx->pctx, p, len);
+
+ case EVP_KDF_CTRL_SET_MD:
+ md = va_arg(args, const EVP_MD *);
+ return EVP_PKEY_CTX_set_hkdf_md(kctx->pctx, md);
+
+ case EVP_KDF_CTRL_SET_KEY:
+ p = va_arg(args, const unsigned char *);
+ len = va_arg(args, size_t);
+ return EVP_PKEY_CTX_set1_hkdf_key(kctx->pctx, p, len);
+
+ case EVP_KDF_CTRL_ADD_HKDF_INFO:
+ p = va_arg(args, const unsigned char *);
+ len = va_arg(args, size_t);
+ return EVP_PKEY_CTX_add1_hkdf_info(kctx->pctx, p, len);
+
+ case EVP_KDF_CTRL_SET_HKDF_MODE:
+ mode = va_arg(args, int);
+#if HAVE_EVP_PKEY_CTX_HKDF_MODE
+ return EVP_PKEY_CTX_hkdf_mode(kctx->pctx, mode);
+#else
+ if (mode == EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND)
+ return 1;
+ else
+ /* XXX: OpenSSL doesn't set an error here */
+ return 0;
+
+#endif
+ default:
+ EVPerr(EVP_F_EVP_KDF_CTRL, EVP_R_COMMAND_NOT_SUPPORTED);
+ return -2;
+ }
+ }
+#endif
+ default:
+ (void)cmd;
+ (void)args;
+ return 0;
+ }
+} /* EVP_KDF_vctrl() */
+
+static int EVP_KDF_ctrl(EVP_KDF_CTX *kctx, int cmd, ...) {
+ int ret;
+ va_list args;
+ va_start(args, cmd);
+ ret = EVP_KDF_vctrl(kctx, cmd, args);
+ va_end(args);
+ if (ret == -2)
+ EVPerr(EVP_F_EVP_KDF_CTRL, EVP_R_COMMAND_NOT_SUPPORTED);
+ return ret;
+} /* EVP_KDF_ctrl() */
+
+static size_t EVP_KDF_size(EVP_KDF_CTX *kctx) {
+ if (kctx == NULL)
+ return 0;
+
+ switch(kctx->nid) {
+#if HAVE_EVP_PKEY_CTX_KDF
+ case NID_tls1_prf:
+ case NID_hkdf: {
+ size_t outlen = 0;
+ EVP_PKEY_derive(kctx->pctx, NULL, &outlen);
+ return outlen;
+ }
+#endif
+ default:
+ return SIZE_MAX;
+ }
+} /* EVP_KDF_size() */
+
+static int EVP_KDF_derive(EVP_KDF_CTX *kctx, unsigned char *out, size_t *outlen) {
+ switch(kctx->nid) {
+#if HAVE_PKCS5_PBKDF2_HMAC
+ case NID_id_pbkdf2:
+ return PKCS5_PBKDF2_HMAC((const char*)kctx->pbkdf2.pass, kctx->pbkdf2.passlen,
+ kctx->pbkdf2.salt, kctx->pbkdf2.saltlen,
+ kctx->pbkdf2.iter,
+ kctx->pbkdf2.md,
+ *outlen, out);
+#endif
+#if HAVE_SCRYPT
+ case NID_id_scrypt:
+ return EVP_PBE_scrypt((const char*)kctx->scrypt.pass, kctx->scrypt.passlen,
+ kctx->scrypt.salt, kctx->scrypt.saltlen,
+ kctx->scrypt.N, kctx->scrypt.r, kctx->scrypt.p,
+ kctx->scrypt.maxmem_bytes,
+ out, *outlen);
+#endif
+#if HAVE_EVP_PKEY_CTX_KDF
+ case NID_tls1_prf:
+ case NID_hkdf:
+ return EVP_PKEY_derive(kctx->pctx, out, outlen);
+#endif
+ default:
+ (void)out;
+ (void)outlen;
+ return 0;
+ }
+} /* EVP_KDF_derive() */
+
+#endif
+
+
/* compat_init must not be called from multiple threads at once */
static int compat_init(void) {
static int store_index = -1, ssl_ctx_index = -1, done;
@@ -11269,6 +11735,229 @@ EXPORT int luaopen__openssl_cipher(lua_State *L) {
/*
+ * openssl.kdf
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int EVP_KDF__gc(lua_State *L) {
+ EVP_KDF_CTX **res = lua_touserdata(L, 1);
+
+ if (*res) {
+ EVP_KDF_CTX_free(*res);
+ *res = NULL;
+ }
+
+ return 0;
+} /* EVP_KDF__gc() */
+
+
+static int kdf_derive(lua_State *L) {
+ int nid;
+ luaL_Buffer b;
+ EVP_KDF_CTX *kctx, **kctxp;
+ unsigned char* out;
+ size_t outlen = 0;
+ const char *str = NULL;
+ size_t len;
+ _Bool seed = 0;
+ int mode;
+
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ {
+ const char* type;
+ if (!loadfield(L, 1, "type", LUA_TSTRING, &type))
+ return luaL_argerror(L, 1, "missing 'type' field");
+ if (!auxS_txt2nid(&nid, type))
+ return luaL_argerror(L, 1, "unknown 'type'");
+ }
+
+ /* ensure EVP_KDF_CTX is collected on error */
+ kctxp = prepudata(L, sizeof(EVP_KDF_CTX*), NULL, &EVP_KDF__gc);
+ if (!(kctx = EVP_KDF_CTX_new_id(nid)))
+ return auxL_error(L, auxL_EOPENSSL, "kdf.derive");
+ *kctxp = kctx;
+
+
+ lua_pushnil(L);
+ while (lua_next(L, 1)) {
+ switch (auxL_testoption(L, -2, 0, (const char *[]){
+ /* special fields */
+ "type",
+ "outlen",
+ /* general options */
+ "pass",
+ "salt",
+ "iter",
+ "md",
+ "key",
+ "maxmem_bytes",
+ /* KDF specific */
+ "secret",
+ "seed",
+ "info",
+ "hkdf_mode",
+ "N",
+ "r",
+ "p",
+ NULL }, 0)) {
+ case 0: /* skip 'type' */
+ break;
+
+ case 1:
+ outlen = auxL_checkunsigned(L, -1, 1, SIZE_MAX-1);
+ break;
+
+ case 2:
+ str = luaL_checklstring(L, -1, &len);
+ if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_PASS, (const unsigned char*)str, len) <= 0)
+ goto error;
+ break;
+
+ case 3:
+ str = luaL_checklstring(L, -1, &len);
+ if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SALT, (const unsigned char*)str, len) <= 0)
+ goto error;
+ break;
+
+ case 4:
+ if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_ITER, auxL_checkunsigned(L, -1, 1, (int)-1)) <= 0)
+ goto error;
+ break;
+
+ case 5:
+ if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MD, md_checkdigest(L, -1)) <= 0)
+ goto error;
+ break;
+
+ case 6:
+ str = luaL_checklstring(L, -1, &len);
+ if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_KEY, (const unsigned char*)str, len) <= 0)
+ goto error;
+ break;
+
+ case 7:
+ if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MAXMEM_BYTES, auxL_checkunsigned(L, -1, 0, UINT64_MAX)) <= 0)
+ goto error;
+ break;
+
+ case 8:
+ str = luaL_checklstring(L, -1, &len);
+ if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_TLS_SECRET, (const unsigned char*)str, len) <= 0)
+ goto error;
+ break;
+
+ case 9:
+ seed = 1;
+ break;
+
+ case 10:
+ str = luaL_checklstring(L, -1, &len);
+ if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_ADD_HKDF_INFO, (const unsigned char*)str, len) <= 0)
+ goto error;
+ break;
+
+ case 11:
+ mode = ((int[]){
+#ifdef EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND
+ EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND,
+#endif
+#ifdef EVP_KDF_HKDF_MODE_EXTRACT_ONLY
+ EVP_KDF_HKDF_MODE_EXTRACT_ONLY,
+#endif
+#ifdef EVP_KDF_HKDF_MODE_EXPAND_ONLY
+ EVP_KDF_HKDF_MODE_EXPAND_ONLY,
+#endif
+ 0 })[auxL_checkoption(L, -1, 0, (const char *[]){
+#ifdef EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND
+ "extract_and_expand",
+#endif
+#ifdef EVP_KDF_HKDF_MODE_EXTRACT_ONLY
+ "extract_only",
+#endif
+#ifdef EVP_KDF_HKDF_MODE_EXPAND_ONLY
+ "expand_only",
+#endif
+ NULL }, 0)];
+ if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_HKDF_MODE, mode) <= 0)
+ goto error;
+ break;
+
+ case 12:
+ if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SCRYPT_N, auxL_checkunsigned(L, -1, 0, UINT64_MAX)) <= 0)
+ goto error;
+ break;
+
+ case 13:
+ if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SCRYPT_R, auxL_checkunsigned(L, -1, 0, UINT32_MAX)) <= 0)
+ goto error;
+ break;
+
+ case 14:
+ if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SCRYPT_P, auxL_checkunsigned(L, -1, 0, UINT32_MAX)) <= 0)
+ goto error;
+ break;
+
+ default:
+ return luaL_argerror(L, 1, lua_pushfstring(L, "unknown field '%s'", lua_tostring(L, -2)));
+ }
+ lua_pop(L, 1);
+ }
+
+ /* XXX: seed must be set *after* secret
+ * https://github.com/openssl/openssl/issues/7728 */
+ if (seed) {
+ lua_getfield(L, 1, "seed");
+ str = luaL_checklstring(L, -1, &len);
+ if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_ADD_TLS_SEED, (const unsigned char*)str, len) <= 0)
+ goto error;
+ lua_pop(L, 1);
+ }
+
+ if (outlen == 0) {
+ outlen = EVP_KDF_size(kctx);
+ if (outlen == 0)
+ goto error;
+ if (outlen == SIZE_MAX)
+ return luaL_argerror(L, 1, "missing 'outlen' field");
+ }
+
+ out = (unsigned char *)luaL_buffinitsize(L, &b, outlen);
+
+ if (EVP_KDF_derive(kctx, out, &outlen) <= 0)
+ goto error;
+
+ EVP_KDF_CTX_free(kctx);
+ *kctxp = NULL;
+
+ luaL_pushresultsize(&b, outlen);
+
+ return 1;
+
+error:
+ if (*kctxp) {
+ EVP_KDF_CTX_free(kctx);
+ *kctxp = NULL;
+ }
+ return auxL_error(L, auxL_EOPENSSL, "kdf.derive");
+} /* kdf_derive */
+
+
+static const auxL_Reg kdf_globals[] = {
+ { "derive", &kdf_derive },
+ { NULL, NULL },
+};
+
+int luaopen__openssl_kdf(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, kdf_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_kdf() */
+
+
+/*
* OCSP_RESPONSE - openssl.ocsp.response
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
diff --git a/src/openssl.kdf.lua b/src/openssl.kdf.lua
new file mode 100644
index 0000000..da203b0
--- /dev/null
+++ b/src/openssl.kdf.lua
@@ -0,0 +1,3 @@
+local ctx = require"_openssl.kdf"
+
+return ctx