From 8eaf1a460901645345e960223f9e1de7f9652376 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 1 Nov 2018 17:01:52 +1100 Subject: src/openssl.c: Add kdf.derive --- doc/luaossl.tex | 31 +++ regress/95-kdf.lua | 45 ++++ regress/regress.lua | 1 + src/GNUmakefile | 1 + src/openssl.c | 689 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/openssl.kdf.lua | 3 + 6 files changed, 770 insertions(+) create mode 100755 regress/95-kdf.lua create mode 100644 src/openssl.kdf.lua 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 +#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; @@ -11268,6 +11734,229 @@ EXPORT int luaopen__openssl_cipher(lua_State *L) { } /* luaopen__openssl_cipher() */ +/* + * 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 -- cgit v1.2.3-59-g8ed1b