Merge commit 'a2800598825bb5a03b577cca2874ff1cfae863f4' as 'src/deps/src/lua-resty-openssl'

This commit is contained in:
Théophile Diot 2023-06-30 15:38:47 -04:00
commit 38fdd39d00
174 changed files with 30614 additions and 0 deletions

View File

@ -0,0 +1,42 @@
{{ if .Versions -}}
<a name="unreleased"></a>
## [Unreleased]
{{ if .Unreleased.CommitGroups -}}
{{ range .Unreleased.CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ replace .Scope "*" "\\*" -1 }}:** {{ end }}{{ .Subject }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{ range .Versions }}
<a name="{{ .Tag.Name }}"></a>
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
{{ range .CommitGroups -}}
### {{ lower .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ replace .Scope "*" "\\*" -1 }}:** {{ end }}{{ .Subject }} [{{ .Hash.Short }}]({{ $.Info.RepositoryURL }}/commit/{{ .Hash.Long }})
{{ end }}
{{ end -}}
{{- if .NoteGroups -}}
{{ range .NoteGroups -}}
### {{ .Title }}
{{ range .Notes }}
{{ .Body }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{- if .Versions }}
[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
{{ range .Versions -}}
{{ if .Tag.Previous -}}
[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
{{ end -}}
{{ end -}}
{{ end -}}

View File

@ -0,0 +1,29 @@
style: github
template: CHANGELOG.tpl.md
info:
title: CHANGELOG
repository_url: https://github.com/fffonion/lua-resty-openssl
options:
sort: "semver"
commits:
filters:
Type:
- feat
- fix
- perf
- refactor
commit_groups:
title_maps:
feat: Features
fix: Bug Fixes
perf: Performance Improvements
refactor: Code Refactoring
header:
pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s\\/]*)\\))?\\s(.*)$"
pattern_maps:
- Type
- Scope
- Subject
notes:
keywords:
- BREAKING CHANGE

View File

@ -0,0 +1,14 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.lua]
indent_style = space
indent_size = 2
[Makefile]
indent_style = tab

View File

@ -0,0 +1,32 @@
name: Lint
on:
push:
paths:
- lib/**.lua
pull_request:
paths:
- lib/**.lua
jobs:
tests:
name: Lint
runs-on: ubuntu-22.04
steps:
- name: Checkout source code
uses: actions/checkout@v2
- uses: Jayrgo/luacheck-action@v1
name: luacheck
with:
# List of files, directories and rockspecs to check.
# Default: .
files: 'lib'
# Path to configuration file.
# Default: .luacheckrc
config: '.luacheckrc'
# Arguments passed to luacheck.
# Default: -q
args: '-q'

View File

@ -0,0 +1,314 @@
name: Tests
on:
pull_request:
paths-ignore:
- '*.md'
push:
branches:
- master
- release/*
paths-ignore:
- '*.md'
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
tests:
name: Tests
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
include:
# TODO: arm64
# latest and one version older for valgrind and perf test
- nginx: "1.19.9"
openssl: "1.0.2u"
extras: "valgrind"
lua_nginx_module: "v0.10.20"
lua_resty_core: "v0.1.22"
- nginx: "1.19.9"
openssl: "1.1.1t"
extras: "valgrind"
lua_nginx_module: "v0.10.20"
lua_resty_core: "v0.1.22"
- nginx: "1.19.9"
openssl: "3.0.8"
extras: "valgrind"
openssl_opts: "enable-fips"
lua_nginx_module: "v0.10.20"
lua_resty_core: "v0.1.22"
nginx_cc_opts: "-Wno-error"
- nginx: "1.21.4"
openssl: "1.0.2u"
extras: "valgrind"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
- nginx: "1.21.4"
openssl: "1.1.1t"
extras: "valgrind perf"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
- nginx: "1.21.4"
openssl: "3.0.8"
extras: "valgrind perf"
openssl_opts: "enable-fips"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
nginx_cc_opts: "-Wno-error"
- nginx: "1.21.4"
openssl: "3.1.0"
extras: "valgrind perf"
openssl_opts: "enable-fips"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
nginx_cc_opts: "-Wno-error"
# latest version with EOL 1.1.0
- nginx: "1.21.4"
openssl: "1.1.0l"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
# version that kong uses, for fips
- nginx: "1.21.4"
openssl: "1.0.2u"
fips2: "2.0.16"
openssl_opts: "fips --with-fipsdir=/home/runner/work/cache/ssl/fips"
extras: "valgrind"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
- nginx: "1.21.4"
boringssl: "ae223d6138807a13006342edfeef32e813246b39" # fips-20190808
extras: "valgrind perf"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
- nginx: "1.21.4"
boringssl: "853ca1ea1168dff08011e5d42d94609cc0ca2e27" # fips-20210429, not active yet
extras: "valgrind perf"
lua_nginx_module: "v0.10.21"
lua_resty_core: "v0.1.23"
env:
JOBS: 3
SH: bash
NGX_BUILD_JOBS: 3
BASE_PATH: /home/runner/work/cache
LUAJIT_PREFIX: /home/runner/work/cache/luajit21
LUAJIT_LIB: /home/runner/work/cache/luajit21/lib
LUAJIT_INC: /home/runner/work/cache/luajit21/include/luajit-2.1
LUA_INCLUDE_DIR: /home/runner/work/cache/luajit21/include/luajit-2.1
OPENSSL_PREFIX: /home/runner/work/cache/ssl
# lib64 since openssl 3.0
OPENSSL_LIB: /home/runner/work/cache/ssl/lib64
OPENSSL_INC: /home/runner/work/cache/ssl/include
TEST_NGINX_SLEEP: 0.005
TEST_NGINX_RANDOMIZE: 1
LUACHECK_VER: 0.21.1
CC: gcc
NGX_BUILD_CC: gcc
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Setup cache
uses: actions/cache@v3
with:
path: |
/home/runner/work/cache
key: ${{ runner.os }}-${{ hashFiles('**/tests.yml') }}-nginx-${{ matrix.nginx }}-openssl-${{ matrix.openssl }}-${{ matrix.fips2 }}-boringssl-${{ matrix.boringssl }}
- name: Setup tools
run: |
sudo apt-get update
sudo apt-get install -qq -y cpanminus axel ca-certificates valgrind haveged
mkdir -p $OPENSSL_PREFIX $LUAJIT_PREFIX
# perl cache
pushd /home/runner/work/cache
if [ ! -e perl ]; then sudo cpanm --notest Test::Nginx > build.log 2>&1 || (cat build.log && exit 1); cp -r /usr/local/share/perl/ .; else sudo cp -r perl /usr/local/share; fi
# build tools at parent directory of cache
cd ..
git clone https://github.com/openresty/openresty.git ./openresty
git clone https://github.com/openresty/nginx-devel-utils.git
git clone https://github.com/simpl/ngx_devel_kit.git ./ndk-nginx-module
git clone https://github.com/openresty/lua-nginx-module.git ./lua-nginx-module -b ${{ matrix.lua_nginx_module }}
git clone https://github.com/openresty/no-pool-nginx.git ./no-pool-nginx
git clone https://github.com/fffonion/lua-resty-openssl-aux-module ./lua-resty-openssl-aux-module
# lua libraries at parent directory of current repository
popd
git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core -b ${{ matrix.lua_resty_core }}
git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache
git clone https://github.com/jkeys089/lua-resty-hmac ../lua-resty-hmac && pushd ../lua-resty-hmac && git checkout 79a4929 && popd
git clone https://github.com/openresty/lua-resty-string ../lua-resty-string
- name: Build OpenSSL
if: matrix.boringssl == ''
run: |
mkdir -p $OPENSSL_PREFIX
# fips doesn't seem to support to build parallelly
if [ "X${{ matrix.fips2 }}" != "X" ]; then wget https://www.openssl.org/source/old/fips/openssl-fips-${{ matrix.fips2 }}.tar.gz -qO - | tar zxf - ; pushd openssl-fips-${{ matrix.fips2 }}/; FIPSDIR=$OPENSSL_PREFIX/fips ./config; make; make install; popd; fi
if [ "X$OPENSSL_HASH" != "X" ]; then wget https://github.com/openssl/openssl/archive/$OPENSSL_HASH.tar.gz -qO - | tar zxf ; pushd openssl-$OPENSSL_HASH/; fi
if [ "X$OPENSSL_HASH" = "X" ] ; then wget https://www.openssl.org/source/openssl-${{ matrix.openssl }}.tar.gz -qO - | tar zxf -; pushd openssl-${{ matrix.openssl }}/; fi
if [ ! -e $OPENSSL_PREFIX/include ]; then ./config shared -d --prefix=$OPENSSL_PREFIX -DPURIFY ${{ matrix.openssl_opts }} > build.log 2>&1 || (cat build.log && exit 1); fi
if [ ! -e $OPENSSL_PREFIX/include ]; then make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1); fi
if [ ! -e $OPENSSL_PREFIX/include ]; then sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1); fi
if [ -e $OPENSSL_LIB/libcrypto.so.3 ] && [ ! -e $OPENSSL_LIB/ossl-modules/fips.so ]; then mkdir -p $OPENSSL_PREFIX/ssl; sudo make PATH=$PATH install_fips > build.log 2>&1 || (cat build.log && exit 1); fi
if [ ! -e $OPENSSL_PREFIX/lib64 ]; then sudo cp -r $OPENSSL_PREFIX/lib $OPENSSL_PREFIX/lib64; fi
mkdir -p $OPENSSL_PREFIX/certs/ && sudo cp -r /etc/ssl/certs/* $OPENSSL_PREFIX/certs/
- name: Build BoringSSL
if: matrix.boringssl != ''
run: |
mkdir -p $OPENSSL_PREFIX
if [ ! -e $OPENSSL_PREFIX/include ]; then
# libtinfo5 is a dependency of clang7 on ubuntu20.04
sudo apt-get install -qq -y cmake libtinfo5 unzip libunwind-dev libgcc-9-dev libstdc++-9-dev
wget https://releases.llvm.org/7.0.1/clang+llvm-7.0.1-x86_64-linux-gnu-ubuntu-18.04.tar.xz -qO - |tar Jxf -
export HOME="$PWD"
printf "set(CMAKE_C_COMPILER \"clang\")\nset(CMAKE_CXX_COMPILER \"clang++\")\n" > ${HOME}/toolchain
export PATH="$PWD/clang+llvm-7.0.1-x86_64-linux-gnu-ubuntu-18.04/bin:$PATH"
clang --version
wget https://dl.google.com/go/go1.12.7.linux-amd64.tar.gz -qO - |tar zxf -
export GOPATH="$PWD/gopath"
export GOROOT="$PWD/go"
export PATH="$GOPATH/bin:$GOROOT/bin:$PATH"
go version
wget https://github.com/ninja-build/ninja/releases/download/v1.9.0/ninja-linux.zip -q
unzip -o ninja-linux.zip
export PATH="$PWD:$PATH"
ninja --version
wget https://commondatastorage.googleapis.com/chromium-boringssl-fips/boringssl-${{ matrix.boringssl }}.tar.xz -qO - | tar Jxf -; pushd boringssl
if [ "${{ matrix.boringssl }}" == "ae223d6138807a13006342edfeef32e813246b39" ]; then
patch -p1 < ../t/fixtures/boringssl_fips.patch
fi
rm -rf build; mkdir build; pushd build
cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=${HOME}/toolchain -DFIPS=1 -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=1 .. > build.log 2>&1 || (cat build.log && exit 1)
ninja > build.log 2>&1 || (cat build.log && exit 1)
./tool/bssl isfips
popd; rm -rf $OPENSSL_INC; cp -r include $OPENSSL_INC
mkdir -p $OPENSSL_LIB; cp -r build/*/*.so $OPENSSL_LIB
fi
mkdir -p $OPENSSL_PREFIX/certs/ && sudo cp -r /etc/ssl/certs/* $OPENSSL_PREFIX/certs/
- name: Build LuaJIT
env:
LUAJIT_CC_OPTS: ${{ matrix.luajit_cc_opts }}
run: |
if [[ "${{ matrix.extras }}" == *valgrind* ]]; then LUAJIT_CC_OPTS="$LUAJIT_CC_OPTS -DLUAJIT_NUMMODE=2 -DLUAJIT_USE_SYSMALLOC -O0"; fi
export
cd $LUAJIT_PREFIX
if [ ! -e luajit2 ]; then git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git; fi
cd luajit2
make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS="-DLUA_USE_APICHECK -DLUA_USE_ASSERT -DLUAJIT_ENABLE_LUA52COMPAT ${{ matrix.luajit_cc_opts }}" > build.log 2>&1 || (cat build.log && exit 1)
make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1)
- name: Build lua-cjson
run: |
if [ ! -e lua-cjson ]; then git clone https://github.com/openresty/lua-cjson.git ./lua-cjson; fi
pushd ./lua-cjson && make && sudo PATH=$PATH make install && popd
- name: Build Nginx
env:
NGINX_CC_OPTS: ${{ matrix.nginx_cc_opts }}
run: |
if [[ "${{ matrix.extras }}" == *valgrind* ]]; then NGINX_CC_OPTS="$NGINX_CC_OPTS -O0"; fi
export PATH=$BASE_PATH/work/nginx/sbin:$BASE_PATH/../nginx-devel-utils:$PATH
export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
export NGX_LUA_LOC=$BASE_PATH/../lua-nginx-module
export NGX_STREAM_LUA_LOC=$BASE_PATH/../stream-lua-nginx-module
export
cd $BASE_PATH
if [ ! -e work ]; then ngx-build ${{ matrix.nginx }} --add-module=../ndk-nginx-module --add-module=../lua-nginx-module --add-module=../lua-resty-openssl-aux-module --with-http_ssl_module --with-cc-opt="-I$OPENSSL_INC $NGINX_CC_OPTS" --with-ld-opt="-L$OPENSSL_LIB -Wl,-rpath,$OPENSSL_LIB" --with-debug > build.log 2>&1 || (cat build.log && exit 1); fi
nginx -V
ldd `which nginx`|grep -E 'luajit|ssl|pcre'
- name: Run performance test
if: contains(matrix.extras, 'perf')
run: |
wget https://github.com/openresty/resty-cli/raw/master/bin/resty
chmod +x resty
export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
export PATH=$BASE_PATH/work/nginx/sbin:$PATH
for f in $(find examples/perf -type f -name "test_*" | sort); do
./resty --no-stream -I lib $f
echo '================================================================'
done
- name: Run Test
run: |
export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
export PATH=$BASE_PATH/work/nginx/sbin:$PATH
TEST_NGINX_TIMEOUT=20 prove -j$JOBS -r t/ 2>&1
echo "Nginx SSL plain FFI"
export CI_SKIP_NGINX_C=1
TEST_NGINX_TIMEOUT=10 prove -j$JOBS t/openssl/ssl/ 2>&1
- name: Run Valgrind
if: contains(matrix.extras, 'valgrind')
run: |
export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
export TEST_NGINX_VALGRIND='--num-callers=100 -q --tool=memcheck --leak-check=full --show-possibly-lost=no --gen-suppressions=all --suppressions=valgrind.suppress --track-origins=yes' TEST_NGINX_TIMEOUT=60 TEST_NGINX_SLEEP=1
export PATH=$BASE_PATH/work/nginx/sbin:$PATH
stdbuf -o 0 -e 0 prove -j$JOBS -r t/ 2>&1 | grep -v "Connection refused" | grep -v "Retry connecting after" | tee output.log
if grep -q 'insert_a_suppression_name_here' output.log; then echo "Valgrind found problems"; exit 1; fi
echo "Nginx SSL plain FFI"
export CI_SKIP_NGINX_C=1
stdbuf -o 0 -e 0 prove -j$JOBS t/openssl/ssl/ 2>&1 | grep -v "Connection refused" | grep -v "Retry connecting after" | tee output.log
if grep -q 'insert_a_suppression_name_here' output.log; then echo "Valgrind found problems"; exit 1; fi
- name: Run FIPS Test
run: |
# openssl 3.0
if [ -e $OPENSSL_LIB/libcrypto.so.3 ]; then
echo "FIPS for OpenSSL 3.0"
cp t/fixtures/openssl_fips.cnf $OPENSSL_PREFIX/openssl-fips.cnf
pushd openssl-${{ matrix.openssl }}/;
# LD_LIBRARY_PATH=$OPENSSL_LIB $OPENSSL_PREFIX/bin/openssl fipsinstall -out $OPENSSL_PREFIX/fipsmodule.cnf -module $OPENSSL_LIB/ossl-modules/fips.so
# don't activate by default
sed -i "/activate = 1/d" $OPENSSL_PREFIX/ssl/fipsmodule.cnf
cat $OPENSSL_PREFIX/ssl/fipsmodule.cnf >> $OPENSSL_PREFIX/openssl-fips.cnf
export OPENSSL_CONF=$OPENSSL_PREFIX/openssl-fips.cnf
popd
export TEST_NGINX_FIPS=1
fi
# openssl 1.0.2 with fips module
if [ "X${{ matrix.fips2 }}" != "X" ]; then
echo "FIPS for OpenSSL 1.0.2"
export TEST_NGINX_FIPS=1
fi
# BoringSSL
if [ "X${{ matrix.boringssl }}" != "X" ]; then
echo "FIPS for BoringSSL ${{ matrix.boringssl }}"
export TEST_NGINX_FIPS=1
fi
if [ "X$TEST_NGINX_FIPS" != "X" ]; then
echo "Running FIPS tests"
export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
export PATH=$BASE_PATH/work/nginx/sbin:$PATH
TEST_NGINX_FIPS=1 TEST_NGINX_TIMEOUT=10 prove -j$JOBS -r t/ 2>&1
TEST_NGINX_TIMEOUT=20 prove -j$JOBS -r t/ 2>&1
fi

View File

@ -0,0 +1,3 @@
t/servroot
__pycache__
.idea/

View File

@ -0,0 +1,15 @@
std = "ngx_lua"
unused_args = false
redefined = false
max_line_length = false
not_globals = {
"string.len",
"table.getn",
}
ignore = {
"6.", -- ignore whitespace warnings
}

View File

@ -0,0 +1,563 @@
<a name="unreleased"></a>
## [Unreleased]
<a name="0.8.23"></a>
## [0.8.23] - 2023-06-20
### bug fixes
- **\*:** fix typos and add error check for new_of/dup_of ([#2](https://github.com/fffonion/lua-resty-openssl/issues/2)) [aa6ad47](https://github.com/fffonion/lua-resty-openssl/commit/aa6ad4707845cca9c46282a1550bb9fee7d48698)
### features
- **tests:** add performance test ([#112](https://github.com/fffonion/lua-resty-openssl/issues/112)) [100b4e4](https://github.com/fffonion/lua-resty-openssl/commit/100b4e43843a597327be6e5356c64b5ce621fa56)
<a name="0.8.22"></a>
## [0.8.22] - 2023-04-26
### bug fixes
- **crypto:** use OPENSSL_free in BoringSSL ([#107](https://github.com/fffonion/lua-resty-openssl/issues/107)) [7830212](https://github.com/fffonion/lua-resty-openssl/commit/78302123ac744f2d0b6de1156e459e9ea72b7edb)
<a name="0.8.21"></a>
## [0.8.21] - 2023-03-24
### features
- **x509.store:** extend verify to support setting flags ([#104](https://github.com/fffonion/lua-resty-openssl/issues/104)) [fa45b6c](https://github.com/fffonion/lua-resty-openssl/commit/fa45b6ce197dee7e2a55601bd4833f415c6cbaa2)
<a name="0.8.20"></a>
## [0.8.20] - 2023-03-10
### bug fixes
- **pkey:** use group bits instead of ECDSA_sig to get parameter size in ECDSA signature ([#102](https://github.com/fffonion/lua-resty-openssl/issues/102)) [f12cbfc](https://github.com/fffonion/lua-resty-openssl/commit/f12cbfc123490c666e2cbd7bec90948910a02336)
<a name="0.8.19"></a>
## [0.8.19] - 2023-03-10
### bug fixes
- **pkey:** fix signature length for secp521r1 ecdsa signature length ([#100](https://github.com/fffonion/lua-resty-openssl/issues/100)) [b7303d4](https://github.com/fffonion/lua-resty-openssl/commit/b7303d49cf738fe134f3e5efbf6157c96ff85237)
<a name="0.8.18"></a>
## [0.8.18] - 2023-03-04
### features
- **bn:** to_binary supports left padding of zeros [d59cac9](https://github.com/fffonion/lua-resty-openssl/commit/d59cac9d7e019e1bcdeaa6714f61294c354cf141)
- **pkey:** allow to convert to and from binary format of ecdsa signature [9a20323](https://github.com/fffonion/lua-resty-openssl/commit/9a203233a23dd08d8f7eeaaff0599921d752a2e2)
<a name="0.8.17"></a>
## [0.8.17] - 2023-01-20
### bug fixes
- **\*:** support OpenSSL 3.1 [dc932f3](https://github.com/fffonion/lua-resty-openssl/commit/dc932f394e5c2b94129b406480897535ec561355)
- **pkey:** allow one shot sign/verify in BoringSSL [32e5df3](https://github.com/fffonion/lua-resty-openssl/commit/32e5df37ac1aaa060c2c1f9b599bd194247d5ecb)
<a name="0.8.16"></a>
## [0.8.16] - 2022-12-20
### features
- **pkey:** load PKCS[#1](https://github.com/fffonion/lua-resty-openssl/issues/1) PEM encoded RSAPublicKey and RSAPrivateKey [3246ec0](https://github.com/fffonion/lua-resty-openssl/commit/3246ec0e51252bfa2812d49f9c6385dcaf0af10b)
<a name="0.8.15"></a>
## [0.8.15] - 2022-10-28
### bug fixes
- **pkey:** check private key existence before doing sign ([#83](https://github.com/fffonion/lua-resty-openssl/issues/83)) [eefcd2a](https://github.com/fffonion/lua-resty-openssl/commit/eefcd2a80b240f44be0bdadd1c2ccc28612004c0)
<a name="0.8.14"></a>
## [0.8.14] - 2022-10-21
### bug fixes
- **x509.crl:** fix metamethods when revoked is empty ([#79](https://github.com/fffonion/lua-resty-openssl/issues/79)) [e65adc7](https://github.com/fffonion/lua-resty-openssl/commit/e65adc7f132628c97e4db69cb5c4b13ff9cf0abf)
<a name="0.8.13"></a>
## [0.8.13] - 2022-10-14
### bug fixes
- **x509.\*:** fix set_extension will fail when a extension with same NID is not exist yet ([#75](https://github.com/fffonion/lua-resty-openssl/issues/75)) [b2f57b8](https://github.com/fffonion/lua-resty-openssl/commit/b2f57b860509a371ab1df71bbbc9e176e5a4d004)
### features
- **x509.altname:** support set and get IP addresses ([#74](https://github.com/fffonion/lua-resty-openssl/issues/74)) [363c80d](https://github.com/fffonion/lua-resty-openssl/commit/363c80d1f2c7ba29dce268e213a9a16c9eae2953)
- **x509.store:** add set_flags ([#77](https://github.com/fffonion/lua-resty-openssl/issues/77)) [8f3f16a](https://github.com/fffonion/lua-resty-openssl/commit/8f3f16a2b6d6c0f680c781f20a9e84a631da9aa5)
<a name="0.8.11"></a>
## [0.8.11] - 2022-10-12
### performance improvements
- **\*:** reuse cdata to improve performance [fc9cecd](https://github.com/fffonion/lua-resty-openssl/commit/fc9cecd785fc0193290cc3398d1ebbe7ae66fe15)
<a name="0.8.10"></a>
## [0.8.10] - 2022-06-24
### features
- **x509:** add get_signature_digest_name [d54b5d6](https://github.com/fffonion/lua-resty-openssl/commit/d54b5d61bc14813121f4a6bda2e1d7eab215094a)
<a name="0.8.9"></a>
## [0.8.9] - 2022-06-23
### bug fixes
- **aux/nginx:** add nginx 1.21.4 and ngx_lua 0.10.21 to support matrix [028da56](https://github.com/fffonion/lua-resty-openssl/commit/028da56d7de606d4b4b323fb3686ad4d93f69c7d)
<a name="0.8.8"></a>
## [0.8.8] - 2022-04-14
### bug fixes
- **ctx:** use global ctx where request is unavailable [e3590cf](https://github.com/fffonion/lua-resty-openssl/commit/e3590cfcbeb6f0d5f110c3c4e1b6cdc63b88e001)
- **x509.extension:** correct X509V3_CTX size for OpenSSL 3.0 [0946c59](https://github.com/fffonion/lua-resty-openssl/commit/0946c5937fa9fa4bb41a70267a67fcc87307b6a6)
### features
- **x509.extension:** add X509V3_set_issuer_pkey in OpenSSL 3.0 [dbd3f74](https://github.com/fffonion/lua-resty-openssl/commit/dbd3f7418a665ae797e6ffc71ba1d7f0660c95f0)
- **x509.store:** add set_purpose and verify_method parameter [b7500fe](https://github.com/fffonion/lua-resty-openssl/commit/b7500fe7212c26070363afeab4a8acfe44c3cfc8)
<a name="0.8.7"></a>
## [0.8.7] - 2022-03-18
### features
- **x509.crl:** add functions to find and inspect revoked list in CRL [37c1661](https://github.com/fffonion/lua-resty-openssl/commit/37c1661fbebebad3b804f602f631e4ba65b80e07)
<a name="0.8.6"></a>
## [0.8.6] - 2022-03-16
### bug fixes
- **obj:** clean up stale error occured from OBJ_txt2* [219a2f0](https://github.com/fffonion/lua-resty-openssl/commit/219a2f0cace8480800394d6e88b188138f2650a1)
- **pkey:** clear_error in passphrase type mismatch [8577422](https://github.com/fffonion/lua-resty-openssl/commit/857742273629d4e801a2d862644213fe5fdbf02a)
- **x509.\*:** move clear_error to last when loading [369eea1](https://github.com/fffonion/lua-resty-openssl/commit/369eea1e4a1a185055296e07f272a3e470442916)
### features
- **openssl:** add function to list SSL ciphers [9861af1](https://github.com/fffonion/lua-resty-openssl/commit/9861af1a074f74f529e341049ada29cbf7d57a48)
- **ssl:** refine various handshake controlling functions [30bf41e](https://github.com/fffonion/lua-resty-openssl/commit/30bf41e958775f60afff1976fe731978c816dd25)
<a name="0.8.5"></a>
## [0.8.5] - 2022-02-02
### bug fixes
- **\*:** correct size type in cipher, hmac and rand in BoringSSL [54ce5f0](https://github.com/fffonion/lua-resty-openssl/commit/54ce5f0dd1861f2af15eacea154c805a237c03d8)
- **bn:** use BN_check_prime in OpenSSL 3.0 [8c107e3](https://github.com/fffonion/lua-resty-openssl/commit/8c107e3dcf2006d6c453234278ab0a45109042d6)
- **kdf:** correct FFI definition for BoringSSL [30ba7cf](https://github.com/fffonion/lua-resty-openssl/commit/30ba7cf9d90d8bc611cbccdca83e69c308739b60)
- **stack:** correct indices to use size_t in BoringSSL [526ecb8](https://github.com/fffonion/lua-resty-openssl/commit/526ecb89c81b0e477b749a2424231329e468ce02)
### features
- **\*:** add more modules for OSSL_LIB_CTX support [35f4bcb](https://github.com/fffonion/lua-resty-openssl/commit/35f4bcb796bc2fbe4ab066b8f78047bf30118986)
<a name="0.8.4"></a>
## [0.8.4] - 2021-12-20
### bug fixes
- **x509.\*:** use SHA256 as default sign digest in BoringSSL [355681a](https://github.com/fffonion/lua-resty-openssl/commit/355681a33d88d85de0faae3e8eb6685e0e3b9f34)
### features
- **pkey:** add pkey:get_default_digest_type [0572e57](https://github.com/fffonion/lua-resty-openssl/commit/0572e57e0ab418f2dd749dbc5042b0c680e346a7)
<a name="0.8.3"></a>
## [0.8.3] - 2021-12-16
### bug fixes
- **hmac:** include evp.md headers [125ea05](https://github.com/fffonion/lua-resty-openssl/commit/125ea059da6b1effef7a187c434ebd6022dc3b82)
<a name="0.8.2"></a>
## [0.8.2] - 2021-11-22
### bug fixes
- **jwk:** fix typo of secp521r1 [81d2a64](https://github.com/fffonion/lua-resty-openssl/commit/81d2a646bde7a66ab87e127eace0d40aa714be58)
<a name="0.8.1"></a>
## [0.8.1] - 2021-11-05
### bug fixes
- **ssl_ctx:** fix typo when getting SSL_CTX from request [7b9e90f](https://github.com/fffonion/lua-resty-openssl/commit/7b9e90faef8337c759c281172be8c1f599be704d)
### features
- **ctx:** add ctx module to provide OSSL_LIB_CTX context [65750bf](https://github.com/fffonion/lua-resty-openssl/commit/65750bfd800b2eebeb9bf653a03518f3ad235fba)
<a name="0.8.0"></a>
## [0.8.0] - 2021-10-29
### bug fixes
- **\*:** move EVP_* definition into seperate files [e0c3d61](https://github.com/fffonion/lua-resty-openssl/commit/e0c3d6178e8b0baab5c53d331dedf8ffb1b1b0c7)
- **auxiliary/nginx:** set off_t to 64bit per nginx config ([#32](https://github.com/fffonion/lua-resty-openssl/issues/32)) [8c209fa](https://github.com/fffonion/lua-resty-openssl/commit/8c209fabbd4ba2f1d6f3a267059c758b4697a433)
- **pkey:** allow sign/verify without md_alg for EdDSA on BoringSSL [ab83fd4](https://github.com/fffonion/lua-resty-openssl/commit/ab83fd4fc053f496699d5dcc77dbb551e2389e77)
- **x509:** compatibility for BoringSSL 1.1.0 (fips-20190808) [84244af](https://github.com/fffonion/lua-resty-openssl/commit/84244af7d91e3421dfccdf1940beb70adbd66adb)
### features
- **evp:** add geneirc function to get and set params [c724e1d](https://github.com/fffonion/lua-resty-openssl/commit/c724e1d41010fab7fb112ca3674eef1aab0b06be)
- **kdf:** add new API with EVP_KDF interfaces [2336ae3](https://github.com/fffonion/lua-resty-openssl/commit/2336ae3b9a7a05473e251a10523a7357afb6f2f2)
- **mac:** add EVP_MAC [0625be9](https://github.com/fffonion/lua-resty-openssl/commit/0625be92e0eaf6a9ee61b3499690d6079aaf933d)
- **openssl:** add function list mac and kdf algorithms and set properties for EVP algorithm fetches [0ed8316](https://github.com/fffonion/lua-resty-openssl/commit/0ed83167dbb1b7d8171bcb59cb749187220572e2)
- **openssl:** support FIPS in OpenSSL 3.0 [beb3ad3](https://github.com/fffonion/lua-resty-openssl/commit/beb3ad3ec8f162aeb11d3f89ea8211c2f3e38c1e)
- **param:** add new function to use OSSL_PARAM [5ffbbcc](https://github.com/fffonion/lua-resty-openssl/commit/5ffbbcce386d98127c84b7f24bb019cff76c05e3)
- **provider:** cipher, digest, kdf, pkey and x509 can now fetch by provider and has new get_provider_name function [52938ca](https://github.com/fffonion/lua-resty-openssl/commit/52938ca5b66f48186815975d685227836bd92cef)
<a name="0.7.5"></a>
## [0.7.5] - 2021-09-18
### bug fixes
- **\*:** rename some EVP_ API to use get in openssl3.0 [8fbdb39](https://github.com/fffonion/lua-resty-openssl/commit/8fbdb396d0a4988a24ff2e0404c1866a416d9cff)
- **aux/nginx:** add 1.19.9 [eb73691](https://github.com/fffonion/lua-resty-openssl/commit/eb73691c058c9d55a1b57405f889f5bc3ecd0420)
<a name="0.7.4"></a>
## [0.7.4] - 2021-08-02
### bug fixes
- **extension:** fallback to ASN1_STRING_print in extension:text where X509V3_EXT_print is not available [f0268f5](https://github.com/fffonion/lua-resty-openssl/commit/f0268f55b124eb4ff65b472899e241af850f9d35)
<a name="0.7.3"></a>
## [0.7.3] - 2021-06-29
### bug fixes
- **pkey:** only pass in passphrase/passphrase_cb to PEM_* functions [6a56494](https://github.com/fffonion/lua-resty-openssl/commit/6a564949e08a6dbe87a44d82e694d862b77c8b68)
- **pkey:** avoid callbacks overflow when setting passphrase_cb [e8aec4e](https://github.com/fffonion/lua-resty-openssl/commit/e8aec4e3ceb4419e373938f9ad4b592efa43acfc)
### features
- **pkey:** allow to specify digest type and padding scheme in sign/verify [ff982ba](https://github.com/fffonion/lua-resty-openssl/commit/ff982ba374ab543c440ccab597d71cdbf4560cdb)
<a name="0.7.2"></a>
## [0.7.2] - 2021-03-25
### bug fixes
- **\*:** redefine callback functions to a style FFI will not overflow [f91202c](https://github.com/fffonion/lua-resty-openssl/commit/f91202c57b826d935d831ec452d2b90fc33277fa)
<a name="0.7.1"></a>
## [0.7.1] - 2021-03-18
### bug fixes
- **altname:** return unsupported as value in not implemented types [ef5e1ed](https://github.com/fffonion/lua-resty-openssl/commit/ef5e1eda9eaea1fd4c8d7d65e438275fed10cdc6)
- **auxiliary/nginx:** typo in error message [4bd22d8](https://github.com/fffonion/lua-resty-openssl/commit/4bd22d81419ed160af1dcea16f42fd284f8f2ad5)
<a name="0.7.0"></a>
## [0.7.0] - 2021-02-19
### bug fixes
- **csr:** count extension count in openssl 3.0 [5af0f4b](https://github.com/fffonion/lua-resty-openssl/commit/5af0f4b02edd0fb8c461a1e08b04eb4eb781f744)
- **csr:** BREAKING: remove csr:set_subject_alt function [513fd8a](https://github.com/fffonion/lua-resty-openssl/commit/513fd8ac61b6f7775465eabc5a3d6a454ccebc54)
- **openssl:** include crypto header in openssl.lua [ef54bf7](https://github.com/fffonion/lua-resty-openssl/commit/ef54bf72710f2613ac6d6e5e8ebb712fa7135939)
- **openssl:** BREAKING: not load sub modules by default [a402f05](https://github.com/fffonion/lua-resty-openssl/commit/a402f05f3ea4b85589c1de6b4347cdfc4c397ea7)
### features
- **\*:** support BoringSSL [9c4e5dc](https://github.com/fffonion/lua-resty-openssl/commit/9c4e5dccefb7fa2e08c489e2922ea05e043e28f2)
- **bn:** add generate_prime [2cc77a4](https://github.com/fffonion/lua-resty-openssl/commit/2cc77a4513dad2f4d684535d1230484e8e91bfbd)
- **openssl:** add function to list supported cipher and digest algorithms [5bdc2a4](https://github.com/fffonion/lua-resty-openssl/commit/5bdc2a406c974f471331636de670915df9386f82)
- **openssl:** add function to get and set fips mode [f6de183](https://github.com/fffonion/lua-resty-openssl/commit/f6de183b19e57616ded39e73518acd198c730056)
<a name="0.6.11"></a>
## [0.6.11] - 2021-01-21
### bug fixes
- **aux/nginx:** only show warning message when function is being called [9964a6d](https://github.com/fffonion/lua-resty-openssl/commit/9964a6d29aded1c0d06c1a8700ee313e08506c2f)
- **openssl:** not load ssl modules by default [390ad79](https://github.com/fffonion/lua-resty-openssl/commit/390ad79c413ec779ff7a1ad2b86ff0fe389c085d)
- **ssl:** add function to free the verify callback function [62dc81a](https://github.com/fffonion/lua-resty-openssl/commit/62dc81a4c7be1c745e7e3ab728f3e060c981f446)
<a name="0.6.10"></a>
## [0.6.10] - 2021-01-12
### bug fixes
- **ecx:** return nil, err in set_parameters [98acaee](https://github.com/fffonion/lua-resty-openssl/commit/98acaeeeaa60dffd93a934f4fbf7ddfd8e9e9652)
- **pkey:** use named_curve encoding for EC group [1e65d9d](https://github.com/fffonion/lua-resty-openssl/commit/1e65d9d4b71c0e9c5f4d404e640a96e03902fd30)
### features
- **pkcs12:** allow to define algorithm to encrypt key and cert [b9678ce](https://github.com/fffonion/lua-resty-openssl/commit/b9678ce4ee4a233fb0bd8ed61d41c6d45a6fbb9d)
- **pkcs12:** check on cert and key mismatch [5953cc2](https://github.com/fffonion/lua-resty-openssl/commit/5953cc281cff06027f3b2bba23402e2915fd3ae1)
- **pkcs12:** encode and decode for pkcs12 [1467579](https://github.com/fffonion/lua-resty-openssl/commit/1467579fbe253996570dd188f580b98b8eb1db98)
- **pkey:** add is_private function to check if it's a private key [eb6cc1c](https://github.com/fffonion/lua-resty-openssl/commit/eb6cc1c2d5f7698c2641950d745a78da7baa6225)
- **ssl:** add the ssl and ssl_ctx module [40f3999](https://github.com/fffonion/lua-resty-openssl/commit/40f39994446a4cb954fc516f7047194cbf1141f8)
<a name="0.6.9"></a>
## [0.6.9] - 2020-11-09
### bug fixes
- **\*:** not mutating tables when doing pairs to avoid missing of iterration [836d5c9](https://github.com/fffonion/lua-resty-openssl/commit/836d5c915b27c0e63782c47effae16515ba71fed)
- **pkey:** fix typo in paramgen error message [d341246](https://github.com/fffonion/lua-resty-openssl/commit/d341246b5db5f912a3bcb06b7be1d08ffee093b3)
- **tests:** openssl3.0 alpha7 [5caa0e6](https://github.com/fffonion/lua-resty-openssl/commit/5caa0e60193ea535d0c0f1fe8491bc6779c9e720)
- **x509.altname:** organize GC handling better [f5a138c](https://github.com/fffonion/lua-resty-openssl/commit/f5a138c8b10dd285d9cacb6f2b3877b7831d0fba)
### features
- **provider:** add the provider module [dff92af](https://github.com/fffonion/lua-resty-openssl/commit/dff92af37102b094f1187914a0c76b6635130626)
- **x509.\*:** add get_signature_nid and get_signature_name [a35ae0a](https://github.com/fffonion/lua-resty-openssl/commit/a35ae0af6ad98251d4226e0daceab07c2832fc17)
<a name="0.6.8"></a>
## [0.6.8] - 2020-10-15
### bug fixes
- **pkey:** correctly free parameter after new parameters are set for RSA and DH keys on OpenSSL 1.0.2 [32d8c12](https://github.com/fffonion/lua-resty-openssl/commit/32d8c127f29e4ee0f13a8191f05f85ec74c2d8d4)
- **tests:** sort json in tests [aeeb7c3](https://github.com/fffonion/lua-resty-openssl/commit/aeeb7c3c2c7899b1b9c36b620476cca81b8eefdc)
### features
- **pkey:** allow to pass params for EC and DH keygen [e9aa7c7](https://github.com/fffonion/lua-resty-openssl/commit/e9aa7c751458134d03dfcda1318186cf3a691c1d)
- **pkey:** get and set DH parameters [ebaad8d](https://github.com/fffonion/lua-resty-openssl/commit/ebaad8d1e6533c9ad4980f557ead986104b947d0)
- **pkey:** support DH key and paramgen [f4661c6](https://github.com/fffonion/lua-resty-openssl/commit/f4661c6eb1d57d36daa93e8c86105b77ba8fe0cb)
- **pkey:** support one shot signing for all key types [79ca0d4](https://github.com/fffonion/lua-resty-openssl/commit/79ca0d43feda10894bfe5f0e72c4460dd4778c66)
<a name="0.6.7"></a>
## [0.6.7] - 2020-10-08
### features
- **pkey:** sign_raw and verify_recover [90ed1b6](https://github.com/fffonion/lua-resty-openssl/commit/90ed1b637729bfde33a94c6467327419186bdd38)
<a name="0.6.6"></a>
## [0.6.6] - 2020-09-29
### bug fixes
- **\*:** export tostring for x509.name and x509.altname [6143659](https://github.com/fffonion/lua-resty-openssl/commit/6143659706ea5b8c42a418b7fac1eae4179a6280)
- **kdf:** fix HKDF potential buffer overflow [da6f420](https://github.com/fffonion/lua-resty-openssl/commit/da6f42025c657f610f1ebee95f0489afd3628d9f)
- **x509.name:** potential memory leak in x509.name:find() [ac51fb1](https://github.com/fffonion/lua-resty-openssl/commit/ac51fb10581ec31e639c1298c080a899466fd57d)
- **x509.store:** return all error on load_file or add failure [a4ee237](https://github.com/fffonion/lua-resty-openssl/commit/a4ee2379802e41f5b5566ac11e59598d1f338ca5)
### features
- **x509.extension:** support create by ASN.1 octet string and nconf [7d8e81f](https://github.com/fffonion/lua-resty-openssl/commit/7d8e81f6789abd951f6e6b3aeb96607f8682c1d5)
<a name="0.6.5"></a>
## [0.6.5] - 2020-09-16
### bug fixes
- **\*:** x509.* set should return true on success [2a09575](https://github.com/fffonion/lua-resty-openssl/commit/2a09575425133e92c990513c7ea7445cf2ca47f4)
<a name="0.6.4"></a>
## [0.6.4] - 2020-08-27
### features
- **x509.csr:** finish {set,add}_extension functions [d34b702](https://github.com/fffonion/lua-resty-openssl/commit/d34b702a17b4f491e2a97e971da1d6125d482066)
- **x509.extension:** add ability to convert to other data type [15a5c7f](https://github.com/fffonion/lua-resty-openssl/commit/15a5c7ff38452a7bd04919b4a7e9c9dc1dfa931d)
<a name="0.6.3"></a>
## [0.6.3] - 2020-08-10
### bug fixes
- **\*:** cleanup and centralize ffi.typeof [5cbc247](https://github.com/fffonion/lua-resty-openssl/commit/5cbc2475bc5926fb0e4aa1b3e5b592144518d013)
- **\*:** remove hack for openssl 3.0 around broken EVP_PKEY_base_id [33181c3](https://github.com/fffonion/lua-resty-openssl/commit/33181c34210fb16c4190e88e1892fb19952420b2)
- **cipher:** use CipherFinal_ex and make test more robust [61fa022](https://github.com/fffonion/lua-resty-openssl/commit/61fa0224fc8dca8a13f9c3ae6904e6cb71c00c6b)
- **openssl:** correctly check error for getting version num ([#6](https://github.com/fffonion/lua-resty-openssl/issues/6)) [6a4b9e6](https://github.com/fffonion/lua-resty-openssl/commit/6a4b9e636714e81d405b934868ef347b3c803674)
- **tests:** pin lua-nginx-module and lua-resty-core [010b37e](https://github.com/fffonion/lua-resty-openssl/commit/010b37eb273da7b96ef39f95a6990357ecf49e49)
- **tests:** make pkey parameter test less flaky [d023edc](https://github.com/fffonion/lua-resty-openssl/commit/d023edcba56e5832b04e2ee0d84195c69a6258d4)
- **x509.\*:** pass correct digest parameter to sign [982ad48](https://github.com/fffonion/lua-resty-openssl/commit/982ad48594444994d5c5b98ba9ca3d139ce96f8c)
### features
- **\*:** support reset for hmac and digest [37ba4b0](https://github.com/fffonion/lua-resty-openssl/commit/37ba4b0f63c60898ee25cfeeeab8b5651c62296e)
- **\*:** initial support for OpenSSL 3.0 [be5dc10](https://github.com/fffonion/lua-resty-openssl/commit/be5dc10c24aabb6697ecb9fe2bd75c8a11e2b2d7)
- **x509.csr:** add get_extension and get_extensions function [638ca46](https://github.com/fffonion/lua-resty-openssl/commit/638ca46ecf1a4fdacac6e24abaea7d19db93c98b)
- **x509.extensions:** finish the stack implementation [f4cf725](https://github.com/fffonion/lua-resty-openssl/commit/f4cf7256e9cce0a280fab46d356ca5fcf3a48b4f)
- **x509.revoked:** add the x509.revoked module [58f0ce1](https://github.com/fffonion/lua-resty-openssl/commit/58f0ce11f2a39cdaabf1c9ba38ea7587adf8f25a)
<a name="0.6.2"></a>
## [0.6.2] - 2020-05-13
### bug fixes
- **\*:** add prefix to all error messages [8f52c25](https://github.com/fffonion/lua-resty-openssl/commit/8f52c2583b87ae0e66e9546f5db03d8fe667cbd4)
### features
- **cipher:** AEAD modes with authentication [fd7471e](https://github.com/fffonion/lua-resty-openssl/commit/fd7471e3a011519df0250681ee1bf82d61b1f154)
- **pkey:** support one shot sign/verify for Ed25519 and Ed448 keys [2565e85](https://github.com/fffonion/lua-resty-openssl/commit/2565e85337325f9cee7d601220120b185a22c430)
- **pkey:** support key derivation for EC, X25519 and X448 keys [0c0d941](https://github.com/fffonion/lua-resty-openssl/commit/0c0d9417711f4c9b513ae02382ea6f9f68f750fd)
- **pkey:** output pkey to DER and JWK format [8da24a5](https://github.com/fffonion/lua-resty-openssl/commit/8da24a5cd9241c09f51c610164dee5daffdd9129)
- **pkey:** load EC key from JWK format [df0c06f](https://github.com/fffonion/lua-resty-openssl/commit/df0c06f1e07be3c6e46d9d2a86005361ad386f83)
- **pkey:** set/get_parameters for EC key [67d54c8](https://github.com/fffonion/lua-resty-openssl/commit/67d54c8dc8555870bbf3fb216b3c636f3d9b220d)
- **pkey:** load RSA key from JWK format [dc118b3](https://github.com/fffonion/lua-resty-openssl/commit/dc118b3aec2a9ff26fc3f615a1569525cbc13dd4)
- **pkey:** add function to set rsa parameter [867fa10](https://github.com/fffonion/lua-resty-openssl/commit/867fa109863a2fd770f26a44b15cbea9d422b5cb)
<a name="0.6.1"></a>
## [0.6.1] - 2020-05-08
### bug fixes
- **x509:** fail soft when CRL is not set [2f2eb5e](https://github.com/fffonion/lua-resty-openssl/commit/2f2eb5edc78e3aa892eb36bd1b091c42ddc64480)
<a name="0.6.0"></a>
## [0.6.0] - 2020-03-11
### features
- **bn:** mathematics, bit shift and comparasion operations [87bf557](https://github.com/fffonion/lua-resty-openssl/commit/87bf5575a3643e11814b9c7be68ec78ce05011fe)
- **kdf:** use give id as type parameter [0e767d0](https://github.com/fffonion/lua-resty-openssl/commit/0e767d006f4561788d826eef82b753093f06ef9e)
- **kdf:** kdf.derive in luaossl compat mode [45788b6](https://github.com/fffonion/lua-resty-openssl/commit/45788b6ea742755b31d6b361950f3ea5d5d24bdf)
<a name="0.6.0-rc.0"></a>
## [0.6.0-rc.0] - 2020-03-02
### features
- **altname:** RFC822 alias to email [37467fc](https://github.com/fffonion/lua-resty-openssl/commit/37467fcf83093d0c99251f43a4cc916d5c934eda)
- **kdf:** add key derivation functions support [d78835e](https://github.com/fffonion/lua-resty-openssl/commit/d78835e861df4b7f79bb0fe5e17a2f19be1e0d3f)
<a name="0.5.4"></a>
## [0.5.4] - 2020-02-27
### bug fixes
- **store:** set X509_V_FLAG_CRL_CHECK flag if a crl is added [88574d5](https://github.com/fffonion/lua-resty-openssl/commit/88574d5ecef0f75a293cd7d23b764d629905e3df)
- **x509.\*:** returns soft error if extension is not found [a0a75aa](https://github.com/fffonion/lua-resty-openssl/commit/a0a75aa2644203e22461aa1dd09ef8672e2ba576)
<a name="0.5.3"></a>
## [0.5.3] - 2020-02-22
### features
- **openssl:** lua-resty-hmac compat [fad844f](https://github.com/fffonion/lua-resty-openssl/commit/fad844f804abe8d73b7d4b7655d562fdb3d84ebf)
<a name="0.5.2"></a>
## [0.5.2] - 2020-02-09
### bug fixes
- **pkey:** decrease copy by 1 when generating key [bcc38e9](https://github.com/fffonion/lua-resty-openssl/commit/bcc38e9fc5e733a8f3f9d09e5eef1e2eb3c15d4d)
### features
- **x509.extension:** allow to create an extension by NID [6d66a2d](https://github.com/fffonion/lua-resty-openssl/commit/6d66a2d9fa7cc36cc2e6c85a78ad2236e525f3b0)
<a name="0.5.1"></a>
## [0.5.1] - 2020-02-04
### bug fixes
- **x509.crl:** fix creating empty crl instance [046ca36](https://github.com/fffonion/lua-resty-openssl/commit/046ca36228f639c191c81a7b84dfedfc523d0340)
### features
- **pkey:** load encrypted PEM key [7fa7a29](https://github.com/fffonion/lua-resty-openssl/commit/7fa7a29882bbcef294f83cd1f66b9960344a0e07)
- **x509.extension:** add tostring() as synonym to text() [87c162d](https://github.com/fffonion/lua-resty-openssl/commit/87c162de9fa7bb3e3930bd760ff7dfece30f1b49)
<a name="0.5.0"></a>
## [0.5.0] - 2020-02-03
### bug fixes
- **\*:** add missing crl.dup function, organize store:add gc handler [6815e5d](https://github.com/fffonion/lua-resty-openssl/commit/6815e5df04fdb77c83b0345f166664759a573962)
- **asn1:** support GENERALIZEDTIME string format [8c7e2d6](https://github.com/fffonion/lua-resty-openssl/commit/8c7e2d67857cb6875cf52fadf43cadf05d8c5c40)
- **error:** return latest error string not earliest in some cases [0b5955d](https://github.com/fffonion/lua-resty-openssl/commit/0b5955d4cb73f3c7d3321ed7384ae862640a6a7f)
- **stack:** protective over first argument [bf455ff](https://github.com/fffonion/lua-resty-openssl/commit/bf455ff310b94b26a3bed513ffc9f308f65691ed)
- **x509:** guard around oscp stack index [1b59b85](https://github.com/fffonion/lua-resty-openssl/commit/1b59b8565b5dee4cb1dd14d22bc24ec04dfbf3d6)
- **x509.store:** correctly save x509 instance references [d8d755f](https://github.com/fffonion/lua-resty-openssl/commit/d8d755f7a281ad09d896a1d78ad9e53f6c028bdc)
### features
- **\*:** add iterater and helpers for stack-like objects [46bb723](https://github.com/fffonion/lua-resty-openssl/commit/46bb7237028a16e67878d8310c25e908ceece009)
- **autogen:** generate tests for x509, csr and crl [1392428](https://github.com/fffonion/lua-resty-openssl/commit/1392428352164d2a1a6e0c03075ff65b55aecdee)
- **objects:** add helper function for ASN1_OBJECT [d037706](https://github.com/fffonion/lua-resty-openssl/commit/d037706c11d716afe3616bdaf4658afc1763081d)
- **pkey:** asymmetric encryption and decryption [6d60451](https://github.com/fffonion/lua-resty-openssl/commit/6d60451157edbf9cefb634f888dfa3e6d9be302f)
- **x509:** getter/setters for extensions [243f40d](https://github.com/fffonion/lua-resty-openssl/commit/243f40d35562a516f404188a5c7eb8f5134d9b30)
- **x509:** add get_ocsp_url and get_crl_url [6141b6f](https://github.com/fffonion/lua-resty-openssl/commit/6141b6f5aed38706b477a71d8c4383bf55da7eee)
- **x509.altname:** support iterate and decode over the stack [083a201](https://github.com/fffonion/lua-resty-openssl/commit/083a201746e02d51f6c5c640ad9bf8c6730ebe0b)
- **x509.crl:** add crl module [242f8cb](https://github.com/fffonion/lua-resty-openssl/commit/242f8cb45d6c2df5918f26540c92a430d42feb5d)
- **x509.csr:** autogen some csr functions as well [9800e36](https://github.com/fffonion/lua-resty-openssl/commit/9800e36c2ff8a299b88f24091cc722940a8652bb)
- **x509.extension:** decode object, set/get critical flag and get text representation [8cb585f](https://github.com/fffonion/lua-resty-openssl/commit/8cb585fc51de04065cd7eeeea06e6240e7251614)
- **x509.extension:** add x509.extension.dist_points and x509.extension.info_access [63d3992](https://github.com/fffonion/lua-resty-openssl/commit/63d3992163144ed75474a8046398d605570c30b7)
<a name="0.4.4"></a>
## [0.4.4] - 2020-02-27
### bug fixes
- **pkey:** clean up errors when trying loading key types [7b3d351](https://github.com/fffonion/lua-resty-openssl/commit/7b3d3513cfb7a8800f49dbdd3ca521b4dadefbad)
<a name="0.4.3"></a>
## [0.4.3] - 2020-01-15
### bug fixes
- **asn1:** support GENERALIZEDTIME string format [cc6326f](https://github.com/fffonion/lua-resty-openssl/commit/cc6326fed1bc53e64042d4742208ed68d7bb42ac)
<a name="0.4.2"></a>
## [0.4.2] - 2020-01-06
### bug fixes
- **bn:** memory leak in bn:to_hex [6718e9e](https://github.com/fffonion/lua-resty-openssl/commit/6718e9e76a8410c78b32e5abf6d06a628fe8dc8b)
- **compat:** refine luaossl compat mode [0d86eb5](https://github.com/fffonion/lua-resty-openssl/commit/0d86eb58848e970408bec7ee9d77102c241c3a5c)
- **openssl:** typo in luaossl_compat [#1](https://github.com/fffonion/lua-resty-openssl/issues/1) [1c3ea60](https://github.com/fffonion/lua-resty-openssl/commit/1c3ea60877d1532eaaddc13ab3be1550c4c5a7f1)
- **x509:** memory leak in x509:set_not_(before|after) [b4a32f8](https://github.com/fffonion/lua-resty-openssl/commit/b4a32f82c33107d2db729caa06aee141b7f9a016)
- **x509:** and missing x509.get_serial_number code [e7d0fb6](https://github.com/fffonion/lua-resty-openssl/commit/e7d0fb6eace77d357d19043b88bb765ec29a5193)
- **x509.csr:** correctly gc extension [ece5be3](https://github.com/fffonion/lua-resty-openssl/commit/ece5be3f517b69563150973e7da6063d5826a9ad)
- **x509.store:** memory leak in store:add [57815dd](https://github.com/fffonion/lua-resty-openssl/commit/57815dd38bbb2e260e8cdf3e8ddac48d6254b8fc)
<a name="0.4.1"></a>
## [0.4.1] - 2019-12-24
### bug fixes
- **x509:** correct X509_add1_ext_i2d include path [b08b312](https://github.com/fffonion/lua-resty-openssl/commit/b08b3123a0fb2770296f04c830414bd38588e8eb)
### features
- **x509:** getters for basic constraints and basic constraints critical [82f5725](https://github.com/fffonion/lua-resty-openssl/commit/82f5725d4738b3bf83fcbf3154fe5979fe8d1af4)
<a name="0.4.0"></a>
## [0.4.0] - 2019-12-20
### bug fixes
- **\*:** always return ok, err if there's no explict return value [3e68167](https://github.com/fffonion/lua-resty-openssl/commit/3e681676f85e26c8c7af6f72a2c4afcb98952cd6)
- **evp:** correct ptr naming [72f8765](https://github.com/fffonion/lua-resty-openssl/commit/72f8765250861d6504a767da81afe19c2d2896a4)
### features
- **\*:** add x509.digest and bn.to_hex [11ea9ae](https://github.com/fffonion/lua-resty-openssl/commit/11ea9aebca6bb5c354ad94525bd2e264debfebbd)
- **version:** add function to print human readable version [7687573](https://github.com/fffonion/lua-resty-openssl/commit/76875731011e5641eef9881ace2becf1bf057cfd)
- **x509:** add x509 stack (chain) support [72154fc](https://github.com/fffonion/lua-resty-openssl/commit/72154fcb7686ce5a754d4fe4f121f07507a1513e)
- **x509.chain:** allow to duplicate a stack [3fa19b7](https://github.com/fffonion/lua-resty-openssl/commit/3fa19b79509c73cf5dce6e3445cbf90a9466d656)
- **x509.name:** allow to iterate over objects and find objects [714a1e5](https://github.com/fffonion/lua-resty-openssl/commit/714a1e541e0ce3ffb33d257d9af50ae628094fb2)
- **x509.store:** support certificate verification [c9dd4bf](https://github.com/fffonion/lua-resty-openssl/commit/c9dd4bf8065a51a97fcf940c33ba046b73ac2049)
<a name="0.3.0"></a>
## [0.3.0] - 2019-12-12
### bug fixes
- **\*:** move cdef and macros to seperate file [28c3390](https://github.com/fffonion/lua-resty-openssl/commit/28c339085383bfbcb72e192701b96e08fb4344f0)
- **\*:** normalize error handling [ff18d54](https://github.com/fffonion/lua-resty-openssl/commit/ff18d54d2b4402de3bc02731f99c32a9953f8784)
### features
- **cipher:** add symmetric cryptography support [9b89e8d](https://github.com/fffonion/lua-resty-openssl/commit/9b89e8dcc1489832c893373150bfeef6a838da34)
- **hmac:** add hmac support [5cc2a15](https://github.com/fffonion/lua-resty-openssl/commit/5cc2a15ce43a9c1c73dccf1a15232ee2a9108460)
<a name="0.2.1"></a>
## [0.2.1] - 2019-10-22
### bug fixes
- **x509:** decrease by set_version by 1 per standard [b6ea5b9](https://github.com/fffonion/lua-resty-openssl/commit/b6ea5b933aadfb8284f7486eb33d8a4c21b7a6de)
<a name="0.2.0"></a>
## 0.2.0 - 2019-10-18
### bug fixes
- **\*:** fix working and name test [f6db7ef](https://github.com/fffonion/lua-resty-openssl/commit/f6db7ef3c1ce5f9a75f01dc24f904fd8942c7897)
- **\*:** normalize naming, explictly control cdef for different openssl versions [c626b53](https://github.com/fffonion/lua-resty-openssl/commit/c626b538c2dc33272b130503ddd21f21bd9d995f)
- **\*:** cleanup cdef [3c02d02](https://github.com/fffonion/lua-resty-openssl/commit/3c02d020822a30fdf7dae0aa7c6aa47c4660aea8)
- **\*:** test cdata type before passing in ffi [de99069](https://github.com/fffonion/lua-resty-openssl/commit/de99069e40c075844a15b91720e2d5c9c9a68dd7)
### features
- **\*:** add more x509 API, and rand bytes generator [6630fde](https://github.com/fffonion/lua-resty-openssl/commit/6630fde2e5e9f367e4652dc390678d4eeb57ad5d)
- **error:** add ability to pull error description [d19ece9](https://github.com/fffonion/lua-resty-openssl/commit/d19ece993ac797fdf6708400cf83e3f4ed0bb9f4)
- **x509:** generate certificate [9b4f59b](https://github.com/fffonion/lua-resty-openssl/commit/9b4f59bf94647aab37da6b8076ee99e155ba8023)
- **x509:** export pubkey [ede4f81](https://github.com/fffonion/lua-resty-openssl/commit/ede4f817cb0fe092ad6f9ab5d6ecdcde864a9fd8)
[Unreleased]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.23...HEAD
[0.8.23]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.22...0.8.23
[0.8.22]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.21...0.8.22
[0.8.21]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.20...0.8.21
[0.8.20]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.19...0.8.20
[0.8.19]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.18...0.8.19
[0.8.18]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.17...0.8.18
[0.8.17]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.16...0.8.17
[0.8.16]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.15...0.8.16
[0.8.15]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.14...0.8.15
[0.8.14]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.13...0.8.14
[0.8.13]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.11...0.8.13
[0.8.11]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.10...0.8.11
[0.8.10]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.9...0.8.10
[0.8.9]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.8...0.8.9
[0.8.8]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.7...0.8.8
[0.8.7]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.6...0.8.7
[0.8.6]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.5...0.8.6
[0.8.5]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.4...0.8.5
[0.8.4]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.3...0.8.4
[0.8.3]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.2...0.8.3
[0.8.2]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.1...0.8.2
[0.8.1]: https://github.com/fffonion/lua-resty-openssl/compare/0.8.0...0.8.1
[0.8.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.7.5...0.8.0
[0.7.5]: https://github.com/fffonion/lua-resty-openssl/compare/0.7.4...0.7.5
[0.7.4]: https://github.com/fffonion/lua-resty-openssl/compare/0.7.3...0.7.4
[0.7.3]: https://github.com/fffonion/lua-resty-openssl/compare/0.7.2...0.7.3
[0.7.2]: https://github.com/fffonion/lua-resty-openssl/compare/0.7.1...0.7.2
[0.7.1]: https://github.com/fffonion/lua-resty-openssl/compare/0.7.0...0.7.1
[0.7.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.11...0.7.0
[0.6.11]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.10...0.6.11
[0.6.10]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.9...0.6.10
[0.6.9]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.8...0.6.9
[0.6.8]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.7...0.6.8
[0.6.7]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.6...0.6.7
[0.6.6]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.5...0.6.6
[0.6.5]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.4...0.6.5
[0.6.4]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.3...0.6.4
[0.6.3]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.2...0.6.3
[0.6.2]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.1...0.6.2
[0.6.1]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.0...0.6.1
[0.6.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.6.0-rc.0...0.6.0
[0.6.0-rc.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.5.4...0.6.0-rc.0
[0.5.4]: https://github.com/fffonion/lua-resty-openssl/compare/0.5.3...0.5.4
[0.5.3]: https://github.com/fffonion/lua-resty-openssl/compare/0.5.2...0.5.3
[0.5.2]: https://github.com/fffonion/lua-resty-openssl/compare/0.5.1...0.5.2
[0.5.1]: https://github.com/fffonion/lua-resty-openssl/compare/0.5.0...0.5.1
[0.5.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.4.4...0.5.0
[0.4.4]: https://github.com/fffonion/lua-resty-openssl/compare/0.4.3...0.4.4
[0.4.3]: https://github.com/fffonion/lua-resty-openssl/compare/0.4.2...0.4.3
[0.4.2]: https://github.com/fffonion/lua-resty-openssl/compare/0.4.1...0.4.2
[0.4.1]: https://github.com/fffonion/lua-resty-openssl/compare/0.4.0...0.4.1
[0.4.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.3.0...0.4.0
[0.3.0]: https://github.com/fffonion/lua-resty-openssl/compare/0.2.1...0.3.0
[0.2.1]: https://github.com/fffonion/lua-resty-openssl/compare/0.2.0...0.2.1

View File

@ -0,0 +1,25 @@
BSD 2-Clause License
Copyright (c) 2020, Wangchong Zhou
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,19 @@
OPENRESTY_PREFIX=/usr/local/openresty
#LUA_VERSION := 5.1
PREFIX ?= /usr/local
LUA_INCLUDE_DIR ?= $(PREFIX)/include
LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION)
.PHONY: all test install
all: ;
install: all
cp -rpv lib/resty/openssl/. $(DESTDIR)$(LUA_LIB_DIR)/resty/openssl
cp -pv lib/resty/openssl.lua $(DESTDIR)$(LUA_LIB_DIR)/resty/
test: all
PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r t

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
name = lua-resty-openssl
abstract = FFI-based OpenSSL binding for LuaJIT
author = fffonion
is_original = yes
license = 3bsd
lib_dir = lib
doc_dir = lib
repo_link = https://github.com/fffonion/lua-resty-openssl
main_module = lib/resty/openssl.lua
requires = luajit
exclude_files=*.rock, *.rockspec

View File

@ -0,0 +1,110 @@
local key = string.rep("0", 32)
local iv = string.rep("0", 12)
local to_be_encrypted = "secret"
local aad = "aead aad"
local mode = "aes-256-gcm"
-- local mode = "chacha20-poly1305"
ngx.say("use cipher ", mode)
-- using one shot interface
local cipher = assert(require("resty.openssl.cipher").new(mode))
local encrypted = assert(cipher:encrypt(key, iv, to_be_encrypted, false, aad))
-- OR using streaming interface
assert(cipher:init(key, iv, {
is_encrypt = true,
}))
assert(cipher:update_aead_aad(aad))
encrypted = assert(cipher:final(to_be_encrypted))
ngx.say("encryption result: ", ngx.encode_base64(encrypted))
local tag = assert(cipher:get_aead_tag())
ngx.say("tag is: ", ngx.encode_base64(tag), " ", #tag)
local _, err = cipher:decrypt(key, iv, encrypted, false, nil, tag)
if err then
ngx.say("no AAD, decryption failed")
end
local _, err = cipher:decrypt(key, iv, encrypted, false, "wrong", tag)
if err then
ngx.say("wrong AAD, decryption failed")
end
-- this seems not working for chacha20-poly1305
local _, err = cipher:decrypt(key, iv, encrypted, false, aad, nil)
if err then
ngx.say("no tag, decryption failed")
end
local _, err = cipher:decrypt(key, iv, encrypted, false, aad, "wrong")
if err then
ngx.say("wrong tag, decryption failed")
end
-- using one shot interface
local decrypted = assert(cipher:decrypt(key, iv, encrypted, false, aad, tag))
-- OR using streaming interface
assert(cipher:init(key, iv, {
is_encrypt = false,
}))
assert(cipher:update_aead_aad(aad))
assert(cipher:set_aead_tag(tag))
decrypted = assert(cipher:final(encrypted))
ngx.say("decryption result: ", decrypted)
--[[
Note in some implementations like `libsodium` or Java, AEAD ciphers append the `tag` (or `MAC`)
at the end of encrypted ciphertext. In such case, user will need to manually cut off the `tag`
with correct size(usually 16 bytes) and pass in the ciphertext and `tag` seperately.
-- encrypt with libsodium and decrypt in lua-resty-openssl
<? php
$encrypted_with_tag = sodium_crypto_aead_aes256gcm_encrypt(
$to_be_encrypted,
$aad,
$iv,
$key
);
?>
local tag = string.sub(encrypted_with_tag, #encrypted_with_tag-16, #encrypted_with_tag)
local encrypted = string.sub(encrypted_with_tag, 1, #encrypted_with_tag-16)
local decrypted = assert(cipher:decrypt(key, iv, encrypted, false, aad, tag))
-- encrypt with lua-resty-openssl and decrypt in libsodium
local encrypted = assert(cipher:encrypt(key, iv, to_be_encrypted, false, aad))
local tag = assert(cipher:get_aead_tag())
<? php
$decrypted = sodium_crypto_aead_aes256gcm_decrypt(
$encrypted . $tag,
$aad,
$iv,
$key
);
?>
]]--
--[[
If the encryption is not done properly, it's possible that no tag is provided after all.
In such case, use the streaming interface and call update() instead of final()
]]
assert(cipher:init(key, iv, {
is_encrypt = false,
}))
assert(cipher:update_aead_aad(aad))
decrypted = assert(cipher:update(encrypted))
ngx.say("decryption result (without checking MAC): ", decrypted)

View File

@ -0,0 +1,74 @@
-- sample function to generate a DER CSR from given pkey and domains
local function create_csr(domain_pkey, ...)
local domains = {...}
local subject = require("resty.openssl.x509.name").new()
local _, err = subject:add("CN", domains[1])
if err then
return nil, err
end
local alt, err
if #{...} > 1 then
alt, err = require("resty.openssl.x509.altname").new()
if err then
return nil, err
end
for _, domain in pairs(domains) do
_, err = alt:add("DNS", domain)
if err then
return nil, err
end
end
end
local csr = require("resty.openssl.x509.csr").new()
local _
_, err = csr:set_subject_name(subject)
if err then
return nil, err
end
if alt then
_, err = csr:set_subject_alt_name(alt)
if err then
return nil, err
end
end
_, err = csr:set_pubkey(domain_pkey)
if err then
return nil, err
end
_, err = csr:sign(domain_pkey)
if err then
return nil, err
end
return csr:tostring("DER"), nil
end
-- create a EC key
local pkey, err = require("resty.openssl.pkey").new({
type = 'EC',
curve = 'prime256v1',
})
if err then
error(err)
end
-- create a CSR using the key
local der, err = create_csr(pkey, "example.com", "*.example.com")
if err then
error(err)
end
-- use openssl cli to see csr we just generated
local f = io.open("example.csr", "w")
f:write(der)
f:close()
os.execute("openssl req -in example.csr -inform der -noout -text")
os.remove("example.csr")

View File

@ -0,0 +1,52 @@
local version=require "resty.openssl.version"
print("VERSION:")
local version_table = {
"VERSION",
"CFLAGS",
"BUILT_ON",
"PLATFORM",
"DIR",
"ENGINES_DIR",
"VERSION_STRING",
"FULL_VERSION_STRING",
"MODULES_DIR",
"CPU_INFO",
}
for _, k in ipairs(version_table) do
print(string.format("%20s: %s", k, version.version(version[k])))
end
print(string.rep("-", 64))
if version.OPENSSL_3X then
print("INFO:")
local info_table = {
"INFO_CONFIG_DIR",
"INFO_ENGINES_DIR",
"INFO_MODULES_DIR",
"INFO_DSO_EXTENSION",
"INFO_DIR_FILENAME_SEPARATOR",
"INFO_LIST_SEPARATOR",
"INFO_SEED_SOURCE",
"INFO_CPU_SETTINGS",
}
for _, k in ipairs(info_table) do
print(string.format("%20s: %s", k, version.info(version[k])))
end
print(string.rep("-", 64))
print("PROVIDER:")
local pro = require "resty.openssl.provider"
for _, n in ipairs({"default", "legacy", "fips", "null"}) do
local ok, err = pro.load(n)
print(string.format("%10s load: %s", n, ok or err))
end
end

View File

@ -0,0 +1,93 @@
local ffi = require "ffi"
local C = ffi.C
local ITER = 2000
local get_duration
do
ffi.cdef [[
typedef long time_t;
typedef int clockid_t;
typedef struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
} nanotime;
int clock_gettime(clockid_t clk_id, struct timespec *tp);
]]
local time_ns
do
local nanop = ffi.new("nanotime[1]")
function time_ns()
-- CLOCK_REALTIME -> 0
C.clock_gettime(0, nanop)
local t = nanop[0]
return tonumber(t.tv_sec) * 1e9 + tonumber(t.tv_nsec)
end
end
local last = 0
get_duration = function()
local n = time_ns()
local d = n - last
last = n
return d
end
end
local function hmt(t)
if t > 1e9 * 0.01 then
return string.format("%.3f s", t/1e9)
elseif t > 1e6 * 0.01 then
return string.format("%.3f ms", t/1e6)
else
return string.format("%d ns", t)
end
end
-- return sum, avg, max
local function stat(t)
if not t then
return 0, 0, 0
end
local v = 0
local max = 0
for _, i in ipairs(t) do
v = v + i
if i > max then
max = i
end
end
return v, v/#t, max
end
local function test(desc, r, iter)
print("RUNNING " .. ITER .. " ITERATIONS FOR " .. desc)
local data = table.new(ITER, 0)
for i=1, ITER do
get_duration()
local ok, err = r()
data[i] = get_duration()
assert(ok, err)
end
local sum, avg, max = stat(data)
print(string.format("FINISHED in\t%s (%d op/s)\nAVG\t%s\tMAX\t%s", hmt(sum), 1e9/avg, hmt(avg), hmt(max)))
print(string.rep("-", 64))
end
local function set_iteration(i)
ITER = i
end
print("LOADING TEST FROM " .. arg[0])
print(string.rep("=", 64))
return {
test = test,
set_iteration = set_iteration,
}

View File

@ -0,0 +1,49 @@
local path = debug.getinfo(1, "S").source:sub(2):match("(.*/)")
package.path = path .. "/?.lua;" .. package.path
local test = require "framework".test
local set_iteration = require "framework".set_iteration
local cipher = require "resty.openssl.cipher"
local version = require("resty.openssl.version")
local key = string.rep("0", 32)
local iv = string.rep("0", 16)
local data = string.rep("1", 4096)
local aad = string.rep("2", 10)
set_iteration(100000)
for _, t in ipairs({"aes-256-cbc", "aes-256-gcm", "chacha20-poly1305"}) do
for _, op in ipairs({"encrypt", "decrypt"}) do
-- the fips version of boringssl we used seems don't have chacha20
if t == "chacha20-poly1305" and (not version.OPENSSL_111_OR_LATER or version.BORINGSSL) then
goto continue
end
local c = assert(cipher.new(t))
local _iv = iv
local _aad
if t == "aes-256-gcm" or t == "chacha20-poly1305" then
_iv = string.rep("0", 12)
_aad = aad
end
if op == "encrypt" then
test("encrypt with " .. t .. " on " .. #data .. " bytes", function()
return c:encrypt(key, _iv, data, false, _aad)
end)
else
local ciphertext = assert(c:encrypt(key, _iv, data, false, _aad))
local tag
if t == "aes-256-gcm" or t == "chacha20-poly1305" then
tag = assert(c:get_aead_tag())
end
test("decrypt with " .. t .. " on " .. #ciphertext .. " bytes", function()
return c:decrypt(key, _iv, ciphertext, false, _aad, tag)
end)
end
::continue::
end
end

View File

@ -0,0 +1,64 @@
local path = debug.getinfo(1, "S").source:sub(2):match("(.*/)")
package.path = path .. "/?.lua;" .. package.path
local test = require "framework".test
local set_iteration = require "framework".set_iteration
local pkey = require "resty.openssl.pkey"
local version = require("resty.openssl.version")
local data = string.rep("=", 200)
set_iteration(1000)
local rsa = pkey.new({ type = "RSA", bits = 4096 })
for _, op in ipairs({"encrypt", "decrypt"}) do
if op == "encrypt" then
test("encrypt with RSA on " .. #data .. " bytes", function()
return rsa:encrypt(data)
end)
else
local ciphertext = assert(rsa:encrypt(data))
test("decrypt with RSA on " .. #ciphertext .. " bytes", function()
return rsa:decrypt(ciphertext)
end)
end
end
for _, t in ipairs({"RSA", "EC", "Ed25519", "Ed448"}) do
for _, op in ipairs({"sign", "verify"}) do
-- the fips version of boringssl we used seems don't have ed448
if (t == "Ed25519" and not version.OPENSSL_111_OR_LATER) or (t == "Ed448" and version.BORINGSSL) then
goto continue
end
local opts = { type = t }
if t == "EC" then
opts.curve = "prime256v1"
elseif t == "RSA" then
opts.bits = 4096
end
local c = assert(pkey.new(opts))
local md_alg
if t == "RSA" or t == "EC" then
md_alg = "SHA256"
end
if op == "sign" then
test("sign with " .. t .. (md_alg and ("-" .. md_alg) or "") .. " on " .. #data .. " bytes", function()
return c:sign(data, md_alg)
end)
else
local sig = assert(c:sign(data, md_alg))
test("verify with " .. t .. (md_alg and ("-" .. md_alg) or "") .. " on " .. #data .. " bytes", function()
return c:verify(sig, data, md_alg)
end)
end
::continue::
end
end

View File

@ -0,0 +1,33 @@
local path = debug.getinfo(1, "S").source:sub(2):match("(.*/)")
package.path = path .. "/?.lua;" .. package.path
local test = require "framework".test
local pkey = require "resty.openssl.pkey"
local example_pkey = assert(pkey.new())
for _, op in ipairs({"load", "export"}) do
for _, t in ipairs({"PEM", "DER", "JWK"}) do
for _, p in ipairs({"public", "private"}) do
if op == "load" then
local txt = assert(example_pkey:tostring(p, t))
local opts = {
format = t,
}
if t ~= "JWK" then
opts.type = p == "public" and "pu" or "pr"
end
test("load " .. t .. " " .. p .. " key", function()
return pkey.new(txt, opts)
end)
else
test("export " .. t .. " " .. p .. " key", function()
return example_pkey:tostring(p, t)
end)
end
end
end
end

View File

@ -0,0 +1,23 @@
local path = debug.getinfo(1, "S").source:sub(2):match("(.*/)")
package.path = path .. "/?.lua;" .. package.path
local test = require "framework".test
local x509 = require "resty.openssl.x509"
local cert = assert(io.open(path .. "../../t/fixtures/Github.pem")):read("*a")
local example_x509 = assert(x509.new(cert))
for _, op in ipairs({"load", "export"}) do
for _, t in ipairs({"PEM", "DER"}) do
if op == "load" then
local txt = assert(example_x509:tostring(t))
test("load " .. t .. " x509", function()
return x509.new(txt, t)
end)
else
test("export " .. t .. " x509", function()
return example_x509:tostring(t)
end)
end
end
end

View File

@ -0,0 +1,17 @@
local pkey = require("resty.openssl.pkey")
local priv = assert(pkey.new())
local pub = assert(pkey.new(priv:to_PEM("public")))
local original = "original text"
-- same as nodejs: crypto.privateEncrypt
-- php: openssl_private_encrypt
local digested = assert(priv:sign_raw(original))
print("Digested message: " .. ngx.encode_base64(digested))
-- same as nodejs: crypto.publicDecrypt
-- php: openssl_public_decrypt
local recovered = assert(pub:verify_recover(digested))
print("Recovered message: " .. recovered)

View File

@ -0,0 +1,76 @@
local openssl_bignum = require "resty.openssl.bn"
local openssl_rand = require "resty.openssl.rand"
local openssl_pkey = require "resty.openssl.pkey"
local x509 = require "resty.openssl.x509"
local x509_extension = require "resty.openssl.x509.extension"
local x509_name = require "resty.openssl.x509.name"
-- taken from https://github.com/Kong/kong/blob/master/kong/cmd/utils/prefix_handler.lua
local function generate_self_signed()
local key = openssl_pkey.new { bits = 2048 }
local crt = x509.new()
assert(crt:set_pubkey(key))
assert(crt:set_version(3))
assert(crt:set_serial_number(openssl_bignum.from_binary(openssl_rand.bytes(16))))
-- last for 20 years
local now = os.time()
assert(crt:set_not_before(now))
assert(crt:set_not_after(now + 86400 * 20 * 365))
local name = assert(x509_name.new()
:add("C", "US")
:add("ST", "California")
:add("L", "San Francisco")
:add("O", "Kong")
:add("OU", "IT Department")
:add("CN", "localhost"))
assert(crt:set_subject_name(name))
assert(crt:set_issuer_name(name))
-- Not a CA
assert(crt:set_basic_constraints { CA = false })
assert(crt:set_basic_constraints_critical(true))
-- Only allowed to be used for TLS connections (client or server)
assert(crt:add_extension(x509_extension.new("extendedKeyUsage",
"serverAuth,clientAuth")))
-- RFC-3280 4.2.1.2
assert(crt:add_extension(x509_extension.new("subjectKeyIdentifier", "hash", {
subject = crt
})))
-- All done; sign
assert(crt:sign(key))
return crt, key
end
local crt, key = generate_self_signed()
do -- write key out
local fd = assert(io.open("key.pem", "w+b"))
local pem = assert(key:to_PEM("private"))
assert(fd:write(pem))
fd:close()
end
print("================== private key =================")
os.execute("openssl pkey -in key.pem -noout -text")
os.remove("key.pem")
do -- write cert out
local fd = assert(io.open("cert.pem", "w+b"))
local pem = assert(crt:to_PEM("private"))
assert(fd:write(pem))
fd:close()
end
print("\n\n")
print("================== certificate =================")
os.execute("openssl x509 -in cert.pem -noout -text")
os.remove("cert.pem")

View File

@ -0,0 +1,58 @@
local pkey = require("resty.openssl.pkey")
local digest = require("resty.openssl.digest")
local x509 = require("resty.openssl.x509")
local altname = require("resty.openssl.x509.altname")
local extension = require("resty.openssl.x509.extension")
local objects = require("resty.openssl.objects")
-- creates the ACME Identifier NID into openssl's internal lookup table
-- if it doesn't exist
local id_pe_acmeIdentifier = "1.3.6.1.5.5.7.1.31"
local nid = objects.txt2nid(id_pe_acmeIdentifier)
if not nid or nid == 0 then
nid = objects.create(
id_pe_acmeIdentifier, -- nid
"pe-acmeIdentifier", -- sn
"ACME Identifier" -- ln
)
end
-- generate the tls-alpn-01 challenge certificate/key per
-- https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-07
-- with given domain name and challenge token
local function serve_challenge_cert(domain, challenge)
local dgst = assert(digest.new("sha256"):final(challenge))
-- There're two ways to set ASN.1 octect string to the extension
-- The recommanded way is to pass the string directly to extension.from_der()
local _, err = extension.from_der(dgst, nid, true)
if err then
return nil, nil, err
end
-- OR we put the ASN.1 signature for this string by ourselves
-- 0x04: OCTET STRING
-- 0x20: length
local dgst_hex = "DER:0420" .. dgst:gsub("(.)", function(s) return string.format("%02x", string.byte(s)) end)
local ext, err = extension.new(nid, dgst_hex)
if err then
return nil, nil, err
end
ext:set_critical(true)
local key = pkey.new()
local cert = x509.new()
cert:set_pubkey(key)
cert:add_extension(ext)
local alt = assert(altname.new():add(
"DNS", domain
))
local _, err = cert:set_subject_alt_name(alt)
if err then
return nil, nil, err
end
cert:sign(key)
return key, cert
end

View File

@ -0,0 +1,31 @@
local pkey = require("resty.openssl.pkey")
-- alice's private key
local alice_priv = assert(pkey.new({
type = "X25519"
}))
-- alice's public key, shared with bob
local alice_pub = assert(pkey.new(alice_priv:to_PEM("public")))
-- bob's private key
local bob_priv = assert(pkey.new({
type = "X25519"
}))
-- bobs' public key, shared with alice
local bob_pub = assert(pkey.new(bob_priv:to_PEM("public")))
ngx.say("alice and bob hold ",
alice_priv:to_PEM() == bob_priv:to_PEM() and "same keys" or "different keys")
ngx.say("")
local k1 = assert(alice_priv:derive(bob_pub))
ngx.say("alice use this key to talk with bob: ", ngx.encode_base64(k1))
local k2 = assert(bob_priv:derive(alice_pub))
ngx.say("bob use this key to talk with alice: ", ngx.encode_base64(k2))
ngx.say("")
ngx.say(k1 == k2 and "key exchange is correct" or "key exchange is not correct")

View File

@ -0,0 +1,476 @@
local ffi = require("ffi")
local C = ffi.C
local ffi_cast = ffi.cast
local ffi_str = ffi.string
local format_error = require("resty.openssl.err").format_error
local OPENSSL_3X, BORINGSSL
local function try_require_modules()
package.loaded["resty.openssl.version"] = nil
local pok, lib = pcall(require, "resty.openssl.version")
if pok then
OPENSSL_3X = lib.OPENSSL_3X
BORINGSSL = lib.BORINGSSL
require "resty.openssl.include.crypto"
require "resty.openssl.include.objects"
else
package.loaded["resty.openssl.version"] = nil
end
end
try_require_modules()
local _M = {
_VERSION = '0.8.23',
}
local libcrypto_name
local lib_patterns = {
"%s", "%s.so.3", "%s.so.1.1", "%s.so.1.0"
}
function _M.load_library()
for _, pattern in ipairs(lib_patterns) do
-- true: load to global namespae
local pok, _ = pcall(ffi.load, string.format(pattern, "crypto"), true)
if pok then
libcrypto_name = string.format(pattern, "crypto")
ffi.load(string.format(pattern, "ssl"), true)
try_require_modules()
return libcrypto_name
end
end
return false, "unable to load crypto library"
end
function _M.load_modules()
_M.bn = require("resty.openssl.bn")
_M.cipher = require("resty.openssl.cipher")
_M.digest = require("resty.openssl.digest")
_M.hmac = require("resty.openssl.hmac")
_M.kdf = require("resty.openssl.kdf")
_M.pkey = require("resty.openssl.pkey")
_M.objects = require("resty.openssl.objects")
_M.rand = require("resty.openssl.rand")
_M.version = require("resty.openssl.version")
_M.x509 = require("resty.openssl.x509")
_M.altname = require("resty.openssl.x509.altname")
_M.chain = require("resty.openssl.x509.chain")
_M.csr = require("resty.openssl.x509.csr")
_M.crl = require("resty.openssl.x509.crl")
_M.extension = require("resty.openssl.x509.extension")
_M.extensions = require("resty.openssl.x509.extensions")
_M.name = require("resty.openssl.x509.name")
_M.revoked = require("resty.openssl.x509.revoked")
_M.store = require("resty.openssl.x509.store")
_M.pkcs12 = require("resty.openssl.pkcs12")
_M.ssl = require("resty.openssl.ssl")
_M.ssl_ctx = require("resty.openssl.ssl_ctx")
if OPENSSL_3X then
_M.provider = require("resty.openssl.provider")
_M.mac = require("resty.openssl.mac")
_M.ctx = require("resty.openssl.ctx")
end
_M.bignum = _M.bn
end
function _M.luaossl_compat()
_M.load_modules()
_M.csr.setSubject = _M.csr.set_subject_name
_M.csr.setPublicKey = _M.csr.set_pubkey
_M.x509.setPublicKey = _M.x509.set_pubkey
_M.x509.getPublicKey = _M.x509.get_pubkey
_M.x509.setSerial = _M.x509.set_serial_number
_M.x509.getSerial = _M.x509.get_serial_number
_M.x509.setSubject = _M.x509.set_subject_name
_M.x509.getSubject = _M.x509.get_subject_name
_M.x509.setIssuer = _M.x509.set_issuer_name
_M.x509.getIssuer = _M.x509.get_issuer_name
_M.x509.getOCSP = _M.x509.get_ocsp_url
local pkey_new = _M.pkey.new
_M.pkey.new = function(a, b)
if type(a) == "string" then
return pkey_new(a, b and unpack(b))
else
return pkey_new(a, b)
end
end
_M.cipher.encrypt = function(self, key, iv, padding)
return self, _M.cipher.init(self, key, iv, true, not padding)
end
_M.cipher.decrypt = function(self, key, iv, padding)
return self, _M.cipher.init(self, key, iv, false, not padding)
end
local digest_update = _M.digest.update
_M.digest.update = function(self, ...)
local ok, err = digest_update(self, ...)
if ok then
return self
else
return nil, err
end
end
local store_verify = _M.store.verify
_M.store.verify = function(...)
local ok, err = store_verify(...)
if err then
return false, err
else
return true, ok
end
end
local kdf_derive = _M.kdf.derive
local kdf_keys_mappings = {
iter = "pbkdf2_iter",
key = "hkdf_key",
info = "hkdf_info",
secret = "tls1_prf_secret",
seed = "tls1_prf_seed",
maxmem_bytes = "scrypt_maxmem",
N = "scrypt_N",
r = "scrypt_r",
p = "scrypt_p",
}
_M.kdf.derive = function(o)
for k1, k2 in pairs(kdf_keys_mappings) do
o[k1] = o[k2]
o[k2] = nil
end
local hkdf_mode = o.hkdf_mode
if hkdf_mode == "extract_and_expand" then
o.hkdf_mode = _M.kdf.HKDEF_MODE_EXTRACT_AND_EXPAND
elseif hkdf_mode == "extract_only" then
o.hkdf_mode = _M.kdf.HKDEF_MODE_EXTRACT_ONLY
elseif hkdf_mode == "expand_only" then
o.hkdf_mode = _M.kdf.HKDEF_MODE_EXPAND_ONLY
end
return kdf_derive(o)
end
_M.pkcs12.new = function(tbl)
local certs = {}
local passphrase = tbl.passphrase
if not tbl.key then
return nil, "key must be set"
end
for _, cert in ipairs(tbl.certs) do
if not _M.x509.istype(cert) then
return nil, "certs must contains only x509 instance"
end
if cert:check_private_key(tbl.key) then
tbl.cert = cert
else
certs[#certs+1] = cert
end
end
tbl.cacerts = certs
return _M.pkcs12.encode(tbl, passphrase)
end
_M.crl.add = _M.crl.add_revoked
_M.crl.lookupSerial = _M.crl.get_by_serial
for mod, tbl in pairs(_M) do
if type(tbl) == 'table' then
-- avoid using a same table as the iterrator will change
local new_tbl = {}
-- luaossl always error() out
for k, f in pairs(tbl) do
if type(f) == 'function' then
local of = f
new_tbl[k] = function(...)
local ret = { of(...) }
if ret and #ret > 1 and ret[#ret] then
error(mod .. "." .. k .. "(): " .. ret[#ret])
end
return unpack(ret)
end
end
end
for k, f in pairs(new_tbl) do
tbl[k] = f
end
setmetatable(tbl, {
__index = function(t, k)
local tok
-- handle special case
if k == 'toPEM' then
tok = 'to_PEM'
else
tok = k:gsub("(%l)(%u)", function(a, b) return a .. "_" .. b:lower() end)
if tok == k then
return
end
end
if type(tbl[tok]) == 'function' then
return tbl[tok]
end
end
})
end
end
-- skip error() conversion
_M.pkcs12.parse = function(p12, passphrase)
local r, err = _M.pkcs12.decode(p12, passphrase)
if err then error(err) end
return r.key, r.cert, r.cacerts
end
end
if OPENSSL_3X then
require "resty.openssl.include.evp"
local provider = require "resty.openssl.provider"
local ctx_lib = require "resty.openssl.ctx"
local fips_provider_ctx
function _M.set_fips_mode(enable, self_test)
if (not not enable) == _M.get_fips_mode() then
return true
end
if enable then
local p, err = provider.load("fips")
if not p then
return false, err
end
fips_provider_ctx = p
if self_test then
local ok, err = p:self_test()
if not ok then
return false, err
end
end
elseif fips_provider_ctx then -- disable
local p = fips_provider_ctx
fips_provider_ctx = nil
return p:unload()
end
-- set algorithm in fips mode in default ctx
-- this deny/allow non-FIPS compliant algorithms to be used from EVP interface
-- and redirect/remove redirect implementation to fips provider
if C.EVP_default_properties_enable_fips(ctx_lib.get_libctx(), enable and 1 or 0) == 0 then
return false, format_error("openssl.set_fips_mode: EVP_default_properties_enable_fips")
end
return true
end
function _M.get_fips_mode()
local pok = provider.is_available("fips")
if not pok then
return false
end
return C.EVP_default_properties_is_fips_enabled(ctx_lib.get_libctx()) == 1
end
else
function _M.set_fips_mode(enable)
if (not not enable) == _M.get_fips_mode() then
return true
end
if C.FIPS_mode_set(enable and 1 or 0) == 0 then
return false, format_error("openssl.set_fips_mode")
end
return true
end
function _M.get_fips_mode()
return C.FIPS_mode() == 1
end
end
function _M.set_default_properties(props)
if not OPENSSL_3X then
return nil, "openssl.set_default_properties is only not supported from OpenSSL 3.0"
end
local ctx_lib = require "resty.openssl.ctx"
if C.EVP_set_default_properties(ctx_lib.get_libctx(), props) == 0 then
return false, format_error("openssl.EVP_set_default_properties")
end
return true
end
local function list_legacy(typ, get_nid_cf)
local typ_lower = string.lower(typ:sub(5)) -- cut off EVP_
require ("resty.openssl.include.evp." .. typ_lower)
local ret = {}
local fn = ffi_cast("fake_openssl_" .. typ_lower .. "_list_fn*",
function(elem, from, to, arg)
if elem ~= nil then
local nid = get_nid_cf(elem)
table.insert(ret, ffi_str(C.OBJ_nid2sn(nid)))
end
-- from/to (renamings) are ignored
end)
C[typ .. "_do_all_sorted"](fn, nil)
fn:free()
return ret
end
local function list_provided(typ)
local typ_lower = string.lower(typ:sub(5)) -- cut off EVP_
local typ_ptr = typ .. "*"
require ("resty.openssl.include.evp." .. typ_lower)
local ctx_lib = require "resty.openssl.ctx"
local ret = {}
local fn = ffi_cast("fake_openssl_" .. typ_lower .. "_provided_list_fn*",
function(elem, _)
elem = ffi_cast(typ_ptr, elem)
local name = ffi_str(C[typ .. "_get0_name"](elem))
-- alternate names are ignored, retrieve use TYPE_names_do_all
local prov = ffi_str(C.OSSL_PROVIDER_get0_name(C[typ .. "_get0_provider"](elem)))
table.insert(ret, name .. " @ " .. prov)
end)
C[typ .. "_do_all_provided"](ctx_lib.get_libctx(), fn, nil)
fn:free()
table.sort(ret)
return ret
end
function _M.list_cipher_algorithms()
if BORINGSSL then
return nil, "openssl.list_cipher_algorithms is not supported on BoringSSL"
end
require "resty.openssl.include.evp.cipher"
local ret = list_legacy("EVP_CIPHER",
OPENSSL_3X and C.EVP_CIPHER_get_nid or C.EVP_CIPHER_nid)
if OPENSSL_3X then
local ret_provided = list_provided("EVP_CIPHER")
for _, r in ipairs(ret_provided) do
table.insert(ret, r)
end
end
return ret
end
function _M.list_digest_algorithms()
if BORINGSSL then
return nil, "openssl.list_digest_algorithms is not supported on BoringSSL"
end
require "resty.openssl.include.evp.md"
local ret = list_legacy("EVP_MD",
OPENSSL_3X and C.EVP_MD_get_type or C.EVP_MD_type)
if OPENSSL_3X then
local ret_provided = list_provided("EVP_MD")
for _, r in ipairs(ret_provided) do
table.insert(ret, r)
end
end
return ret
end
function _M.list_mac_algorithms()
if not OPENSSL_3X then
return nil, "openssl.list_mac_algorithms is only supported from OpenSSL 3.0"
end
return list_provided("EVP_MAC")
end
function _M.list_kdf_algorithms()
if not OPENSSL_3X then
return nil, "openssl.list_kdf_algorithms is only supported from OpenSSL 3.0"
end
return list_provided("EVP_KDF")
end
local valid_ssl_protocols = {
["SSLv3"] = 0x0300,
["TLSv1"] = 0x0301,
["TLSv1.1"] = 0x0302,
["TLSv1.2"] = 0x0303,
["TLSv1.3"] = 0x0304,
}
function _M.list_ssl_ciphers(cipher_list, ciphersuites, protocol)
local ssl_lib = require("resty.openssl.ssl")
local ssl_macro = require("resty.openssl.include.ssl")
if protocol then
if not valid_ssl_protocols[protocol] then
return nil, "unknown protocol \"" .. protocol .. "\""
end
protocol = valid_ssl_protocols[protocol]
end
local ssl_ctx = C.SSL_CTX_new(C.TLS_server_method())
if ssl_ctx == nil then
return nil, format_error("SSL_CTX_new")
end
ffi.gc(ssl_ctx, C.SSL_CTX_free)
local ssl = C.SSL_new(ssl_ctx)
if ssl == nil then
return nil, format_error("SSL_new")
end
ffi.gc(ssl, C.SSL_free)
if protocol then
if ssl_macro.SSL_set_min_proto_version(ssl, protocol) == 0 or
ssl_macro.SSL_set_max_proto_version(ssl, protocol) == 0 then
return nil, format_error("SSL_set_min/max_proto_version")
end
end
ssl = { ctx = ssl }
local ok, err
if cipher_list then
ok, err = ssl_lib.set_cipher_list(ssl, cipher_list)
if not ok then
return nil, err
end
end
if ciphersuites then
ok, err = ssl_lib.set_ciphersuites(ssl, ciphersuites)
if not ok then
return nil, err
end
end
return ssl_lib.get_ciphers(ssl)
end
return _M

View File

@ -0,0 +1,91 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_str = ffi.string
local floor = math.floor
local asn1_macro = require("resty.openssl.include.asn1")
-- https://github.com/wahern/luaossl/blob/master/src/openssl.c
local function isleap(year)
return (year % 4) == 0 and ((year % 100) > 0 or (year % 400) == 0)
end
local past = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }
local function yday(year, mon, mday)
local d = past[mon] + mday - 1
if mon > 2 and isleap(year) then
d = d + 1
end
return d
end
local function leaps(year)
return floor(year / 400) + floor(year / 4) - floor(year / 100)
end
local function asn1_to_unix(asn1)
if asn1 == nil then
return nil, "except an ASN1 instance at #1, got nil"
end
local s = asn1_macro.ASN1_STRING_get0_data(asn1)
s = ffi_str(s)
-- V_ASN1_UTCTIME 190303223958Z
-- V_ASN1_GENERALIZEDTIME 21190822162753Z
local yyoffset = 2
local year
-- # define V_ASN1_GENERALIZEDTIME 24
if C.ASN1_STRING_type(asn1) == 24 then
yyoffset = 4
year = tonumber(s:sub(1, yyoffset))
else
year = tonumber(s:sub(1, yyoffset))
year = year + (year < 50 and 2000 or 1900)
end
local month = tonumber(s:sub(yyoffset+1, yyoffset+2))
if month > 12 or month < 1 then
return nil, "asn1.asn1_to_unix: bad format " .. s
end
local day = tonumber(s:sub(yyoffset+3, yyoffset+4))
if day > 31 or day < 1 then
return nil, "asn1.asn1_to_unix: bad format " .. s
end
local hour = tonumber(s:sub(yyoffset+5, yyoffset+6))
if hour > 23 or hour < 0 then
return nil, "asn1.asn1_to_unix: bad format " .. s
end
local minute = tonumber(s:sub(yyoffset+7, yyoffset+8))
if minute > 59 or hour < 0 then
return nil, "asn1.asn1_to_unix: bad format " .. s
end
local second = tonumber(s:sub(yyoffset+9, yyoffset+10))
if second > 59 or second < 0 then
return nil, "asn1.asn1_to_unix: bad format " .. s
end
local tm
tm = (year - 1970) * 365
tm = tm + leaps(year - 1) - leaps(1969)
tm = (tm + yday(year, month, day)) * 24
tm = (tm + hour) * 60
tm = (tm + minute) * 60
tm = tm + second
-- offset?
local sign = s:sub(yyoffset+11, yyoffset+11)
if sign == "+" or sign == "-" then
local sgn = sign == "+" and 1 or -1
local hh = tonumber(s:sub(yyoffset+12, yyoffset+13) or 'no')
local mm = tonumber(s:sub(yyoffset+14, yyoffset+15) or 'no')
if not hh or not mm then
return nil, "asn1.asn1_to_unix: bad format " .. s
end
tm = tm + sgn * (hh * 3600 + mm * 60)
end
return tm
end
return {
asn1_to_unix = asn1_to_unix,
}

View File

@ -0,0 +1,43 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_new = ffi.new
local ffi_str = ffi.string
require "resty.openssl.include.bio"
local format_error = require("resty.openssl.err").format_error
local function read_wrap(f, ...)
if type(f) ~= "cdata" then -- should be explictly a function
return nil, "bio_util.read_wrap: expect a function at #1"
end
local bio_method = C.BIO_s_mem()
if bio_method == nil then
return nil, "bio_util.read_wrap: BIO_s_mem() failed"
end
local bio = C.BIO_new(bio_method)
ffi_gc(bio, C.BIO_free)
-- BIO_reset; #define BIO_CTRL_RESET 1
local code = C.BIO_ctrl(bio, 1, 0, nil)
if code ~= 1 then
return nil, "bio_util.read_wrap: BIO_ctrl() failed: " .. code
end
local code = f(bio, ...)
if code ~= 1 then
return nil, format_error(f, code)
end
local buf = ffi_new("char *[1]")
-- BIO_get_mem_data; #define BIO_CTRL_INFO 3
local length = C.BIO_ctrl(bio, 3, 0, buf)
return ffi_str(buf[0], length)
end
return {
read_wrap = read_wrap,
}

View File

@ -0,0 +1,28 @@
-- Put common type definition at the same place for convenience
-- and standarlization
local ffi = require "ffi"
--[[
TYPE_ptr: usually used to define a pointer (to cast or something)
char* var_name; // <- we use char_ptr
ptr_of_TYPE: usually used to pass the pointer of an object that
is already allocated. so that we can also set value of it as well
int p = 2; // ptr_of_int(); ptr_of_int[0] = 2;
plus_one(&p); // <- we use ptr_of_int
]]
return {
void_ptr = ffi.typeof("void *"),
ptr_of_uint64 = ffi.typeof("uint64_t[1]"),
ptr_of_uint = ffi.typeof("unsigned int[1]"),
ptr_of_size_t = ffi.typeof("size_t[1]"),
ptr_of_int = ffi.typeof("int[1]"),
null = ffi.new("void *"), -- hack wher ngx.null is not available
uchar_array = ffi.typeof("unsigned char[?]"),
uchar_ptr = ffi.typeof("unsigned char*"),
SIZE_MAX = math.pow(2, 64), -- nginx set _FILE_OFFSET_BITS to 64
}

View File

@ -0,0 +1,143 @@
local ffi = require "ffi"
local ffi_new = ffi.new
local ffi_gc = ffi.gc
local ffi_str = ffi.string
local C = ffi.C
require "resty.openssl.include.ecdsa"
local bn_lib = require "resty.openssl.bn"
local format_error = require("resty.openssl.err").format_error
local ceil = math.ceil
local _M = {}
--[[ A DER formatted ECDSA signature looks like
SEQUENCE {
INTEGER
4B 5F CF E8 A7 BD 6A C2 1D 25 0D F8 DE 9C EF DC
C4 DF 33 F3 AF 2F 3D 5B 83 2C 1F BD 98 C8 61 34
INTEGER
7E F9 E9 60 B1 E6 7F 59 9E 2C 38 22 39 B2 C4 B1
71 3E FA AE 24 A4 B7 D2 03 5A 60 8D F3 34 3D E8
}
It has ASN.1 headers on both the SEQUENCE and INTEGERs, so
the total length is typically 70 bytes (3 headers, 2 bytes each).
The binary form is typically 64 bytes.
]]
local function group_size(ec_key)
local group = C.EC_KEY_get0_group(ec_key)
if group == nil then
assert("failed to get EC group", 2)
end
local sz = C.EC_GROUP_order_bits(group)
if sz <= 0 then
assert("failed to get EC group order bits", 2)
end
return ceil(sz / 8)
end
_M.sig_der2raw = function(der, ec_key)
if ec_key == nil then
error("ec_key is required", 2)
end
local psize = group_size(ec_key)
local buf = ffi.new("const unsigned char*", der)
local buf_ptr = ffi.new("const unsigned char*[1]", buf)
local sig = C.d2i_ECDSA_SIG(nil, buf_ptr, #der)
if sig == nil then
return nil, format_error("failed to parse ECDSA signature: d2i_ECDSA_SIG")
end
ffi_gc(sig, C.ECDSA_SIG_free)
local bn_r_ptr = ffi_new("const BIGNUM*[1]")
local bn_s_ptr = ffi_new("const BIGNUM*[1]")
C.ECDSA_SIG_get0(sig, bn_r_ptr, bn_s_ptr)
if bn_r_ptr[0] == nil or bn_s_ptr[0] == nil then
return nil, format_error("failed to get r or s from sig")
end
local bn_r = bn_lib.dup(bn_r_ptr[0])
local bn_s = bn_lib.dup(bn_s_ptr[0])
if bn_r == nil or bn_s == nil then
return nil, format_error("failed to dup r or s")
end
local rbin, err = bn_r:to_binary(psize)
if err then
return nil, "failed to parse r to binary: " .. err
end
local sbin, err = bn_s:to_binary(psize)
if err then
return nil, "failed to parse s to binary: " .. err
end
return rbin .. sbin
end
_M.sig_raw2der = function(bin, ec_key)
if ec_key == nil then
error("ec_key is required", 2)
end
local psize = group_size(ec_key)
if #bin ~= psize * 2 then
return nil, "invalid signature length, expect " .. (psize * 2) .. " but got " .. #bin
end
local rbin = string.sub(bin, 1, psize)
local sbin = string.sub(bin, psize + 1)
local bn_r, err = bn_lib.from_binary(rbin)
if err then
return nil, "failed to parse r from binary: " .. err
end
local bn_s, err = bn_lib.from_binary(sbin)
if err then
return nil, "failed to parse s from binary: " .. err
end
local sig = C.ECDSA_SIG_new()
if sig == nil then
return nil, format_error("ECDSA_SIG_new")
end
ffi_gc(sig, C.ECDSA_SIG_free)
local bn_r0 = C.BN_dup(bn_r.ctx)
local bn_s0 = C.BN_dup(bn_s.ctx)
if not bn_r0 or not bn_s0 then
return nil, format_error("failed to BN_dup r or s")
end
local ok = C.ECDSA_SIG_set0(sig, bn_r0, bn_s0)
if ok ~= 1 then
return nil, format_error("failed to set r and s to sig")
end
local der_len = C.i2d_ECDSA_SIG(sig, nil)
if der_len <= 0 then
return nil, format_error("failed to get ECDSA signature size")
end
local buf = ffi_new("unsigned char[?]", der_len)
local buf_ptr = ffi.new("unsigned char*[1]", buf)
der_len = C.i2d_ECDSA_SIG(sig, buf_ptr)
if der_len <= 0 then
return nil, format_error("failed to encode ECDSA signature to DER")
end
return ffi_str(buf, der_len)
end
return _M

View File

@ -0,0 +1,261 @@
local ffi = require "ffi"
local C = ffi.C
local cjson = require("cjson.safe")
local b64 = require("ngx.base64")
local evp_macro = require "resty.openssl.include.evp"
local rsa_lib = require "resty.openssl.rsa"
local ec_lib = require "resty.openssl.ec"
local ecx_lib = require "resty.openssl.ecx"
local bn_lib = require "resty.openssl.bn"
local digest_lib = require "resty.openssl.digest"
local _M = {}
local rsa_jwk_params = {"n", "e", "d", "p", "q", "dp", "dq", "qi"}
local rsa_openssl_params = rsa_lib.params
local function load_jwk_rsa(tbl)
if not tbl["n"] or not tbl["e"] then
return nil, "at least \"n\" and \"e\" parameter is required"
end
local params = {}
local err
for i, k in ipairs(rsa_jwk_params) do
local v = tbl[k]
if v then
v = b64.decode_base64url(v)
if not v then
return nil, "cannot decode parameter \"" .. k .. "\" from base64 " .. tbl[k]
end
params[rsa_openssl_params[i]], err = bn_lib.from_binary(v)
if err then
return nil, "cannot use parameter \"" .. k .. "\": " .. err
end
end
end
local key = C.RSA_new()
if key == nil then
return nil, "RSA_new() failed"
end
local _, err = rsa_lib.set_parameters(key, params)
if err ~= nil then
C.RSA_free(key)
return nil, err
end
return key
end
local ec_curves = {
["P-256"] = C.OBJ_ln2nid("prime256v1"),
["P-384"] = C.OBJ_ln2nid("secp384r1"),
["P-521"] = C.OBJ_ln2nid("secp521r1"),
}
local ec_curves_reverse = {}
for k, v in pairs(ec_curves) do
ec_curves_reverse[v] = k
end
local ec_jwk_params = {"x", "y", "d"}
local function load_jwk_ec(tbl)
local curve = tbl['crv']
if not curve then
return nil, "\"crv\" not defined for EC key"
end
if not tbl["x"] or not tbl["y"] then
return nil, "at least \"x\" and \"y\" parameter is required"
end
local curve_nid = ec_curves[curve]
if not curve_nid then
return nil, "curve \"" .. curve .. "\" is not supported by this library"
elseif curve_nid == 0 then
return nil, "curve \"" .. curve .. "\" is not supported by linked OpenSSL"
end
local params = {}
local err
for _, k in ipairs(ec_jwk_params) do
local v = tbl[k]
if v then
v = b64.decode_base64url(v)
if not v then
return nil, "cannot decode parameter \"" .. k .. "\" from base64 " .. tbl[k]
end
params[k], err = bn_lib.from_binary(v)
if err then
return nil, "cannot use parameter \"" .. k .. "\": " .. err
end
end
end
-- map to the name we expect
if params["d"] then
params["private"] = params["d"]
params["d"] = nil
end
params["group"] = curve_nid
local key = C.EC_KEY_new()
if key == nil then
return nil, "EC_KEY_new() failed"
end
local _, err = ec_lib.set_parameters(key, params)
if err ~= nil then
C.EC_KEY_free(key)
return nil, err
end
return key
end
local function load_jwk_okp(key_type, tbl)
local params = {}
if tbl["d"] then
params.private = b64.decode_base64url(tbl["d"])
elseif tbl["x"] then
params.public = b64.decode_base64url(tbl["x"])
else
return nil, "at least \"x\" or \"d\" parameter is required"
end
local key, err = ecx_lib.set_parameters(key_type, nil, params)
if err ~= nil then
return nil, err
end
return key
end
local ecx_curves_reverse = {}
for k, v in pairs(evp_macro.ecx_curves) do
ecx_curves_reverse[v] = k
end
function _M.load_jwk(txt)
local tbl, err = cjson.decode(txt)
if err then
return nil, "error decoding JSON from JWK: " .. err
elseif type(tbl) ~= "table" then
return nil, "except input to be decoded as a table, got " .. type(tbl)
end
local key, key_free, key_type, err
if tbl["kty"] == "RSA" then
key_type = evp_macro.EVP_PKEY_RSA
if key_type == 0 then
return nil, "the linked OpenSSL library doesn't support RSA key"
end
key, err = load_jwk_rsa(tbl)
key_free = C.RSA_free
elseif tbl["kty"] == "EC" then
key_type = evp_macro.EVP_PKEY_EC
if key_type == 0 then
return nil, "the linked OpenSSL library doesn't support EC key"
end
key, err = load_jwk_ec(tbl)
key_free = C.EC_KEY_free
elseif tbl["kty"] == "OKP" then
local curve = tbl["crv"]
key_type = evp_macro.ecx_curves[curve]
if not key_type then
return nil, "unknown curve \"" .. tostring(curve)
elseif key_type == 0 then
return nil, "the linked OpenSSL library doesn't support \"" .. curve .. "\" key"
end
key, err = load_jwk_okp(key_type, tbl)
if key ~= nil then
return key
end
else
return nil, "not yet supported jwk type \"" .. (tbl["kty"] or "nil") .. "\""
end
if err then
return nil, "failed to construct " .. tbl["kty"] .. " key from JWK: " .. err
end
local ctx = C.EVP_PKEY_new()
if ctx == nil then
key_free(key)
return nil, "EVP_PKEY_new() failed"
end
local code = C.EVP_PKEY_assign(ctx, key_type, key)
if code ~= 1 then
key_free(key)
C.EVP_PKEY_free(ctx)
return nil, "EVP_PKEY_assign() failed"
end
return ctx
end
function _M.dump_jwk(pkey, is_priv)
local jwk
if pkey.key_type == evp_macro.EVP_PKEY_RSA then
local param_keys = { "n" , "e" }
if is_priv then
param_keys = rsa_jwk_params
end
local params, err = pkey:get_parameters()
if err then
return nil, "jwk.dump_jwk: " .. err
end
jwk = {
kty = "RSA",
}
for i, p in ipairs(param_keys) do
local v = params[rsa_openssl_params[i]]:to_binary()
jwk[p] = b64.encode_base64url(v)
end
elseif pkey.key_type == evp_macro.EVP_PKEY_EC then
local params, err = pkey:get_parameters()
if err then
return nil, "jwk.dump_jwk: " .. err
end
jwk = {
kty = "EC",
crv = ec_curves_reverse[params.group],
x = b64.encode_base64url(params.x:to_binary()),
y = b64.encode_base64url(params.x:to_binary()),
}
if is_priv then
jwk.d = b64.encode_base64url(params.private:to_binary())
end
elseif ecx_curves_reverse[pkey.key_type] then
local params, err = pkey:get_parameters()
if err then
return nil, "jwk.dump_jwk: " .. err
end
jwk = {
kty = "OKP",
crv = ecx_curves_reverse[pkey.key_type],
d = b64.encode_base64url(params.private),
x = b64.encode_base64url(params.public),
}
else
return nil, "jwk.dump_jwk: not implemented for this key type"
end
local der = pkey:tostring(is_priv and "private" or "public", "DER")
local dgst = digest_lib.new("sha256")
local d, err = dgst:final(der)
if err then
return nil, "jwk.dump_jwk: failed to calculate digest for key"
end
jwk.kid = b64.encode_base64url(d)
return cjson.encode(jwk)
end
return _M

View File

@ -0,0 +1,318 @@
local get_req_ssl, get_req_ssl_ctx
local get_socket_ssl, get_socket_ssl_ctx
local pok, nginx_c = pcall(require, "resty.openssl.auxiliary.nginx_c")
if pok and not os.getenv("CI_SKIP_NGINX_C") then
get_req_ssl = nginx_c.get_req_ssl
get_req_ssl_ctx = nginx_c.get_req_ssl_ctx
get_socket_ssl = nginx_c.get_socket_ssl
get_socket_ssl_ctx = nginx_c.get_socket_ssl
else
local ffi = require "ffi"
ffi.cdef [[
// Nginx seems to always config _FILE_OFFSET_BITS=64, this should always be 8 byte
typedef long long off_t;
typedef unsigned int socklen_t; // windows uses int, same size
typedef unsigned short in_port_t;
typedef struct ssl_st SSL;
typedef struct ssl_ctx_st SSL_CTX;
typedef long (*ngx_recv_pt)(void *c, void *buf, size_t size);
typedef long (*ngx_recv_chain_pt)(void *c, void *in,
off_t limit);
typedef long (*ngx_send_pt)(void *c, void *buf, size_t size);
typedef void *(*ngx_send_chain_pt)(void *c, void *in,
off_t limit);
typedef struct {
size_t len;
void *data;
} ngx_str_t;
typedef struct {
SSL *connection;
SSL_CTX *session_ctx;
// trimmed
} ngx_ssl_connection_s;
]]
local ngx_version = ngx.config.nginx_version
if ngx_version == 1017008 or ngx_version == 1019003 or ngx_version == 1019009
or ngx_version == 1021004 then
-- 1.17.8, 1.19.3, 1.19.9, 1.21.4
-- https://github.com/nginx/nginx/blob/master/src/core/ngx_connection.h
ffi.cdef [[
typedef struct {
ngx_str_t src_addr;
ngx_str_t dst_addr;
in_port_t src_port;
in_port_t dst_port;
} ngx_proxy_protocol_t;
typedef struct {
void *data;
void *read;
void *write;
int fd;
ngx_recv_pt recv;
ngx_send_pt send;
ngx_recv_chain_pt recv_chain;
ngx_send_chain_pt send_chain;
void *listening;
off_t sent;
void *log;
void *pool;
int type;
void *sockaddr;
socklen_t socklen;
ngx_str_t addr_text;
// https://github.com/nginx/nginx/commit/be932e81a1531a3ba032febad968fc2006c4fa48
ngx_proxy_protocol_t *proxy_protocol;
ngx_ssl_connection_s *ssl;
// trimmed
} ngx_connection_s;
]]
else
error("resty.openssl.auxiliary.nginx doesn't support Nginx version " .. ngx_version, 2)
end
ffi.cdef [[
typedef struct {
ngx_connection_s *connection;
// trimmed
} ngx_stream_lua_request_s;
typedef struct {
unsigned int signature; /* "HTTP" */
ngx_connection_s *connection;
// trimmed
} ngx_http_request_s;
]]
local get_request
do
local ok, exdata = pcall(require, "thread.exdata")
if ok and exdata then
function get_request()
local r = exdata()
if r ~= nil then
return r
end
end
else
local getfenv = getfenv
function get_request()
return getfenv(0).__ngx_req
end
end
end
local SOCKET_CTX_INDEX = 1
local NO_C_MODULE_WARNING_MSG_SHOWN = false
local NO_C_MODULE_WARNING_MSG = "note resty.openssl.auxiliary.nginx is using plain FFI " ..
"and it's only intended to be used in development, " ..
"consider using lua-resty-openssl.aux-module in production."
local function get_ngx_ssl_from_req()
if not NO_C_MODULE_WARNING_MSG_SHOWN then
ngx.log(ngx.WARN, NO_C_MODULE_WARNING_MSG)
NO_C_MODULE_WARNING_MSG_SHOWN = true
end
local c = get_request()
if ngx.config.subsystem == "stream" then
c = ffi.cast("ngx_stream_lua_request_s*", c)
else -- http
c = ffi.cast("ngx_http_request_s*", c)
end
local ngx_ssl = c.connection.ssl
if ngx_ssl == nil then
return nil, "c.connection.ssl is nil"
end
return ngx_ssl
end
get_req_ssl = function()
local ssl, err = get_ngx_ssl_from_req()
if err then
return nil, err
end
return ssl.connection
end
get_req_ssl_ctx = function()
local ssl, err = get_ngx_ssl_from_req()
if err then
return nil, err
end
return ssl.session_ctx
end
ffi.cdef[[
typedef struct ngx_http_lua_socket_tcp_upstream_s
ngx_http_lua_socket_tcp_upstream_t;
typedef struct {
ngx_connection_s *connection;
// trimmed
} ngx_peer_connection_s;
typedef
int (*ngx_http_lua_socket_tcp_retval_handler_masked)(void *r,
void *u, void *L);
typedef void (*ngx_http_lua_socket_tcp_upstream_handler_pt_masked)
(void *r, void *u);
typedef
int (*ngx_stream_lua_socket_tcp_retval_handler)(void *r,
void *u, void *L);
typedef void (*ngx_stream_lua_socket_tcp_upstream_handler_pt)
(void *r, void *u);
typedef struct {
ngx_stream_lua_socket_tcp_retval_handler read_prepare_retvals;
ngx_stream_lua_socket_tcp_retval_handler write_prepare_retvals;
ngx_stream_lua_socket_tcp_upstream_handler_pt read_event_handler;
ngx_stream_lua_socket_tcp_upstream_handler_pt write_event_handler;
void *socket_pool;
void *conf;
void *cleanup;
void *request;
ngx_peer_connection_s peer;
// trimmed
} ngx_stream_lua_socket_tcp_upstream_s;
]]
local ngx_lua_version = ngx.config and
ngx.config.ngx_lua_version and
ngx.config.ngx_lua_version
if ngx_lua_version >= 10019 and ngx_lua_version <= 10021 then
-- https://github.com/openresty/lua-nginx-module/blob/master/src/ngx_http_lua_socket_tcp.h
ffi.cdef[[
typedef struct {
ngx_http_lua_socket_tcp_retval_handler_masked read_prepare_retvals;
ngx_http_lua_socket_tcp_retval_handler_masked write_prepare_retvals;
ngx_http_lua_socket_tcp_upstream_handler_pt_masked read_event_handler;
ngx_http_lua_socket_tcp_upstream_handler_pt_masked write_event_handler;
void *udata_queue; // 0.10.19
void *socket_pool;
void *conf;
void *cleanup;
void *request;
ngx_peer_connection_s peer;
// trimmed
} ngx_http_lua_socket_tcp_upstream_s;
]]
elseif ngx_lua_version < 10019 then
-- the struct doesn't seem to get changed a long time since birth
ffi.cdef[[
typedef struct {
ngx_http_lua_socket_tcp_retval_handler_masked read_prepare_retvals;
ngx_http_lua_socket_tcp_retval_handler_masked write_prepare_retvals;
ngx_http_lua_socket_tcp_upstream_handler_pt_masked read_event_handler;
ngx_http_lua_socket_tcp_upstream_handler_pt_masked write_event_handler;
void *socket_pool;
void *conf;
void *cleanup;
void *request;
ngx_peer_connection_s peer;
// trimmed
} ngx_http_lua_socket_tcp_upstream_s;
]]
else
error("resty.openssl.auxiliary.nginx doesn't support lua-nginx-module version " .. (ngx_lua_version or "nil"), 2)
end
local function get_ngx_ssl_from_socket_ctx(sock)
if not NO_C_MODULE_WARNING_MSG_SHOWN then
ngx.log(ngx.WARN, NO_C_MODULE_WARNING_MSG)
NO_C_MODULE_WARNING_MSG_SHOWN = true
end
local u = sock[SOCKET_CTX_INDEX]
if u == nil then
return nil, "lua_socket_tcp_upstream_t not found"
end
if ngx.config.subsystem == "stream" then
u = ffi.cast("ngx_stream_lua_socket_tcp_upstream_s*", u)
else -- http
u = ffi.cast("ngx_http_lua_socket_tcp_upstream_s*", u)
end
local p = u.peer
if p == nil then
return nil, "u.peer is nil"
end
local uc = p.connection
if uc == nil then
return nil, "u.peer.connection is nil"
end
local ngx_ssl = uc.ssl
if ngx_ssl == nil then
return nil, "u.peer.connection.ssl is nil"
end
return ngx_ssl
end
get_socket_ssl = function(sock)
local ssl, err = get_ngx_ssl_from_socket_ctx(sock)
if err then
return nil, err
end
return ssl.connection
end
get_socket_ssl_ctx = function(sock)
local ssl, err = get_ngx_ssl_from_socket_ctx(sock)
if err then
return nil, err
end
return ssl.session_ctx
end
end
return {
get_req_ssl = get_req_ssl,
get_req_ssl_ctx = get_req_ssl_ctx,
get_socket_ssl = get_socket_ssl,
get_socket_ssl_ctx = get_socket_ssl_ctx,
}

View File

@ -0,0 +1,154 @@
local ffi = require "ffi"
local C = ffi.C
local SOCKET_CTX_INDEX = 1
local NGX_OK = ngx.OK
local get_req_ssl, get_req_ssl_ctx
local get_socket_ssl, get_socket_ssl_ctx
local get_request
do
local ok, exdata = pcall(require, "thread.exdata")
if ok and exdata then
function get_request()
local r = exdata()
if r ~= nil then
return r
end
end
else
local getfenv = getfenv
function get_request()
return getfenv(0).__ngx_req
end
end
end
local stream_subsystem = false
if ngx.config.subsystem == "stream" then
stream_subsystem = true
ffi.cdef [[
typedef struct ngx_stream_lua_request_s ngx_stream_lua_request_t;
typedef struct ngx_stream_lua_socket_tcp_upstream_s ngx_stream_lua_socket_tcp_upstream_t;
int ngx_stream_lua_resty_openssl_aux_get_request_ssl(ngx_stream_lua_request_t *r,
void **_ssl_conn);
int ngx_stream_lua_resty_openssl_aux_get_request_ssl_ctx(ngx_stream_lua_request_t *r,
void **_sess);
int ngx_stream_lua_resty_openssl_aux_get_socket_ssl(ngx_stream_lua_socket_tcp_upstream_t *u,
void **_ssl_conn);
int ngx_stream_lua_resty_openssl_aux_get_socket_ssl_ctx(ngx_stream_lua_socket_tcp_upstream_t *u,
void **_sess);
]]
-- sanity test
local _ = C.ngx_stream_lua_resty_openssl_aux_get_request_ssl
else
ffi.cdef [[
typedef struct ngx_http_request_s ngx_http_request_t;
typedef struct ngx_http_lua_socket_tcp_upstream_s ngx_http_lua_socket_tcp_upstream_t;
int ngx_http_lua_resty_openssl_aux_get_request_ssl(ngx_http_request_t *r,
void **_ssl_conn);
int ngx_http_lua_resty_openssl_aux_get_request_ssl_ctx(ngx_http_request_t *r,
void **_sess);
int ngx_http_lua_resty_openssl_aux_get_socket_ssl(ngx_http_lua_socket_tcp_upstream_t *u,
void **_ssl_conn);
int ngx_http_lua_resty_openssl_aux_get_socket_ssl_ctx(ngx_http_lua_socket_tcp_upstream_t *u,
void **_sess);
]]
-- sanity test
local _ = C.ngx_http_lua_resty_openssl_aux_get_request_ssl
end
local void_pp = ffi.new("void *[1]")
local ssl_type = ffi.typeof("SSL*")
local ssl_ctx_type = ffi.typeof("SSL_CTX*")
get_req_ssl = function()
local c = get_request()
local ret
if stream_subsystem then
ret = C.ngx_stream_lua_resty_openssl_aux_get_request_ssl(c, void_pp)
else
ret = C.ngx_http_lua_resty_openssl_aux_get_request_ssl(c, void_pp)
end
if ret ~= NGX_OK then
return nil, "cannot read r->connection->ssl->connection"
end
return ffi.cast(ssl_type, void_pp[0])
end
get_req_ssl_ctx = function()
local c = get_request()
local ret
if stream_subsystem then
ret = C.ngx_stream_lua_resty_openssl_aux_get_request_ssl_ctx(c, void_pp)
else
ret = C.ngx_http_lua_resty_openssl_aux_get_request_ssl_ctx(c, void_pp)
end
if ret ~= NGX_OK then
return nil, "cannot read r->connection->ssl->session_ctx"
end
return ffi.cast(ssl_ctx_type, void_pp[0])
end
get_socket_ssl = function(sock)
local u = sock[SOCKET_CTX_INDEX]
local ret
if stream_subsystem then
ret = C.ngx_stream_lua_resty_openssl_aux_get_socket_ssl(u, void_pp)
else
ret = C.ngx_http_lua_resty_openssl_aux_get_socket_ssl(u, void_pp)
end
if ret ~= NGX_OK then
return nil, "cannot read u->peer.connection->ssl->connection"
end
return ffi.cast(ssl_type, void_pp[0])
end
get_socket_ssl_ctx = function(sock)
local u = sock[SOCKET_CTX_INDEX]
local ret
if stream_subsystem then
ret = C.ngx_stream_lua_resty_openssl_aux_get_socket_ssl_ctx(u, void_pp)
else
ret = C.ngx_http_lua_resty_openssl_aux_get_socket_ssl_ctx(u, void_pp)
end
if ret ~= NGX_OK then
return nil, "cannot read u->peer.connection->ssl->session_ctx"
end
return ffi.cast(ssl_ctx_type, void_pp[0])
end
return {
get_req_ssl = get_req_ssl,
get_req_ssl_ctx = get_req_ssl_ctx,
get_socket_ssl = get_socket_ssl,
get_socket_ssl_ctx = get_socket_ssl_ctx,
}

View File

@ -0,0 +1,435 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_new = ffi.new
local ffi_str = ffi.string
local floor = math.floor
require "resty.openssl.include.bn"
local crypto_macro = require("resty.openssl.include.crypto")
local ctypes = require "resty.openssl.auxiliary.ctypes"
local format_error = require("resty.openssl.err").format_error
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local _M = {}
local mt = {__index = _M}
local bn_ptr_ct = ffi.typeof('BIGNUM*')
local bn_ptrptr_ct = ffi.typeof('BIGNUM*[1]')
function _M.new(bn)
local ctx = C.BN_new()
ffi_gc(ctx, C.BN_free)
if type(bn) == 'number' then
if C.BN_set_word(ctx, bn) ~= 1 then
return nil, format_error("bn.new")
end
elseif bn then
return nil, "bn.new: expect nil or a number at #1"
end
return setmetatable( { ctx = ctx }, mt), nil
end
function _M.istype(l)
return l and l.ctx and ffi.istype(bn_ptr_ct, l.ctx)
end
function _M.dup(ctx)
if not ffi.istype(bn_ptr_ct, ctx) then
return nil, "bn.dup: expect a bn ctx at #1"
end
local ctx = C.BN_dup(ctx)
ffi_gc(ctx, C.BN_free)
local self = setmetatable({
ctx = ctx,
}, mt)
return self
end
function _M:to_binary(pad)
if pad then
if type(pad) ~= "number" then
return nil, "bn:to_binary: expect a number at #1"
elseif OPENSSL_10 then
return nil, "bn:to_binary: padding is only supported on OpenSSL 1.1.0 or later"
end
end
local length
if not pad then
length = (C.BN_num_bits(self.ctx)+7)/8
-- align to bytes
length = floor(length)
else
length = pad
end
local buf = ctypes.uchar_array(length)
local sz
if not pad then
sz = C.BN_bn2bin(self.ctx, buf)
else
sz = C.BN_bn2binpad(self.ctx, buf, pad)
end
if sz <= 0 then
return nil, format_error("bn:to_binary")
end
return ffi_str(buf, sz)
end
function _M.from_binary(s)
if type(s) ~= "string" then
return nil, "bn.from_binary: expect a string at #1"
end
local ctx = C.BN_bin2bn(s, #s, nil)
if ctx == nil then
return nil, format_error("bn.from_binary")
end
ffi_gc(ctx, C.BN_free)
return setmetatable( { ctx = ctx }, mt), nil
end
function _M:to_hex()
local buf = C.BN_bn2hex(self.ctx)
if buf == nil then
return nil, format_error("bn:to_hex")
end
ffi_gc(buf, crypto_macro.OPENSSL_free)
local s = ffi_str(buf)
return s
end
function _M.from_hex(s)
if type(s) ~= "string" then
return nil, "bn.from_hex: expect a string at #1"
end
local p = ffi_new(bn_ptrptr_ct)
if C.BN_hex2bn(p, s) == 0 then
return nil, format_error("bn.from_hex")
end
local ctx = p[0]
ffi_gc(ctx, C.BN_free)
return setmetatable( { ctx = ctx }, mt), nil
end
function _M:to_dec()
local buf = C.BN_bn2dec(self.ctx)
if buf == nil then
return nil, format_error("bn:to_dec")
end
ffi_gc(buf, crypto_macro.OPENSSL_free)
local s = ffi_str(buf)
return s
end
mt.__tostring = _M.to_dec
function _M.from_dec(s)
if type(s) ~= "string" then
return nil, "bn.from_dec: expect a string at #1"
end
local p = ffi_new(bn_ptrptr_ct)
if C.BN_dec2bn(p, s) == 0 then
return nil, format_error("bn.from_dec")
end
local ctx = p[0]
ffi_gc(ctx, C.BN_free)
return setmetatable( { ctx = ctx }, mt), nil
end
function _M:to_number()
return tonumber(C.BN_get_word(self.ctx))
end
_M.tonumber = _M.to_number
function _M.generate_prime(bits, safe)
local ctx = C.BN_new()
ffi_gc(ctx, C.BN_free)
if C.BN_generate_prime_ex(ctx, bits, safe and 1 or 0, nil, nil, nil) == 0 then
return nil, format_error("bn.BN_generate_prime_ex")
end
return setmetatable( { ctx = ctx }, mt), nil
end
-- BN_CTX is used to store temporary variable
-- we only need one per worker
local bn_ctx_tmp = C.BN_CTX_new()
assert(bn_ctx_tmp ~= nil)
if OPENSSL_10 then
C.BN_CTX_init(bn_ctx_tmp)
end
ffi_gc(bn_ctx_tmp, C.BN_CTX_free)
_M.bn_ctx_tmp = bn_ctx_tmp
-- mathematics
local is_negative
if OPENSSL_10 then
local bn_zero = assert(_M.new(0)).ctx
is_negative = function(ctx)
return C.BN_cmp(ctx, bn_zero) < 0 and 1 or 0
end
else
is_negative = C.BN_is_negative
end
function mt.__unm(a)
local b = _M.dup(a.ctx)
if b == nil then
error("BN_dup() failed")
end
local sign = is_negative(b.ctx)
C.BN_set_negative(b.ctx, 1-sign)
return b
end
local function check_args(op, ...)
local args = {...}
for i, arg in ipairs(args) do
if type(arg) == 'number' then
local b = C.BN_new()
if b == nil then
error("BN_new() failed")
end
ffi_gc(b, C.BN_free)
if C.BN_set_word(b, arg) ~= 1 then
error("BN_set_word() failed")
end
args[i] = b
elseif _M.istype(arg) then
args[i] = arg.ctx
else
error("cannot " .. op .. " a " .. type(arg) .. " to bignum")
end
end
local ctx = C.BN_new()
if ctx == nil then
error("BN_new() failed")
end
ffi_gc(ctx, C.BN_free)
local r = setmetatable( { ctx = ctx }, mt)
return r, unpack(args)
end
function mt.__add(...)
local r, a, b = check_args("add", ...)
if C.BN_add(r.ctx, a, b) == 0 then
error("BN_add() failed")
end
return r
end
_M.add = mt.__add
function mt.__sub(...)
local r, a, b = check_args("substract", ...)
if C.BN_sub(r.ctx, a, b) == 0 then
error("BN_sub() failed")
end
return r
end
_M.sub = mt.__sub
function mt.__mul(...)
local r, a, b = check_args("multiply", ...)
if C.BN_mul(r.ctx, a, b, bn_ctx_tmp) == 0 then
error("BN_mul() failed")
end
return r
end
_M.mul = mt.__mul
-- lua 5.3 only
function mt.__idiv(...)
local r, a, b = check_args("divide", ...)
if C.BN_div(r.ctx, nil, a, b, bn_ctx_tmp) == 0 then
error("BN_div() failed")
end
return r
end
mt.__div = mt.__idiv
_M.idiv = mt.__idiv
_M.div = mt.__div
function mt.__mod(...)
local r, a, b = check_args("mod", ...)
if C.BN_div(nil, r.ctx, a, b, bn_ctx_tmp) == 0 then
error("BN_div() failed")
end
return r
end
_M.mod = mt.__mod
-- __concat doesn't make sense at all?
function _M.sqr(...)
local r, a = check_args("square", ...)
if C.BN_sqr(r.ctx, a, bn_ctx_tmp) == 0 then
error("BN_sqr() failed")
end
return r
end
function _M.gcd(...)
local r, a, b = check_args("extract greatest common divisor", ...)
if C.BN_gcd(r.ctx, a, b, bn_ctx_tmp) == 0 then
error("BN_gcd() failed")
end
return r
end
function _M.exp(...)
local r, a, b = check_args("power", ...)
if C.BN_exp(r.ctx, a, b, bn_ctx_tmp) == 0 then
error("BN_exp() failed")
end
return r
end
_M.pow = _M.exp
for _, op in ipairs({ "add", "sub" , "mul", "exp" }) do
local f = "BN_mod_" .. op
local cf = C[f]
_M["mod_" .. op] = function(...)
local r, a, b, m = check_args(op, ...)
if cf(r.ctx, a, b, m, bn_ctx_tmp) == 0 then
error(f .. " failed")
end
return r
end
end
function _M.mod_sqr(...)
local r, a, m = check_args("mod_sub", ...)
if C.BN_mod_sqr(r.ctx, a, m, bn_ctx_tmp) == 0 then
error("BN_mod_sqr() failed")
end
return r
end
local function nyi()
error("NYI")
end
-- bit operations, lua 5.3
mt.__band = nyi
mt.__bor = nyi
mt.__bxor = nyi
mt.__bnot = nyi
function mt.__shl(a, b)
local r, a = check_args("lshift", a)
if C.BN_lshift(r.ctx, a, b) == 0 then
error("BN_lshift() failed")
end
return r
end
_M.lshift = mt.__shl
function mt.__shr(a, b)
local r, a = check_args("rshift", a)
if C.BN_rshift(r.ctx, a, b) == 0 then
error("BN_lshift() failed")
end
return r
end
_M.rshift = mt.__shr
-- comparaions
-- those functions are only called when the table
-- has exact same metamethods, i.e. they are all BN
-- so we don't need to check args
function mt.__eq(a, b)
return C.BN_cmp(a.ctx, b.ctx) == 0
end
function mt.__lt(a, b)
return C.BN_cmp(a.ctx, b.ctx) < 0
end
function mt.__le(a, b)
return C.BN_cmp(a.ctx, b.ctx) <= 0
end
if OPENSSL_10 then
-- in openssl 1.0.x those functions are implemented as macros
-- don't want to copy paste all structs here
-- the followings are definitely slower, but works
local bn_zero = assert(_M.new(0)).ctx
local bn_one = assert(_M.new(1)).ctx
function _M:is_zero()
return C.BN_cmp(self.ctx, bn_zero) == 0
end
function _M:is_one()
return C.BN_cmp(self.ctx, bn_one) == 0
end
function _M:is_word(n)
local ctx = C.BN_new()
ffi_gc(ctx, C.BN_free)
if ctx == nil then
return nil, "bn:is_word: BN_new() failed"
end
if C.BN_set_word(ctx, n) ~= 1 then
return nil, "bn:is_word: BN_set_word() failed"
end
return C.BN_cmp(self.ctx, ctx) == 0
end
function _M:is_odd()
return self:to_number() % 2 == 1
end
else
function _M:is_zero()
return C.BN_is_zero(self.ctx) == 1
end
function _M:is_one()
return C.BN_is_one(self.ctx) == 1
end
function _M:is_word(n)
return C.BN_is_word(self.ctx, n) == 1
end
function _M:is_odd()
return C.BN_is_odd(self.ctx) == 1
end
end
function _M:is_prime(nchecks)
if nchecks and type(nchecks) ~= "number" then
return nil, "bn:is_prime: expect a number at #1"
end
-- if nchecks is not defined, set to BN_prime_checks:
-- select number of iterations based on the size of the number
local code
if OPENSSL_3X then
code = C.BN_check_prime(self.ctx, bn_ctx_tmp, nil)
else
code = C.BN_is_prime_ex(self.ctx, nchecks or 0, bn_ctx_tmp, nil)
end
if code == -1 then
return nil, format_error("bn.is_prime")
end
return code == 1
end
return _M

View File

@ -0,0 +1,300 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_str = ffi.string
local ffi_cast = ffi.cast
require "resty.openssl.include.evp.cipher"
local evp_macro = require "resty.openssl.include.evp"
local ctypes = require "resty.openssl.auxiliary.ctypes"
local ctx_lib = require "resty.openssl.ctx"
local format_error = require("resty.openssl.err").format_error
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local uchar_array = ctypes.uchar_array
local void_ptr = ctypes.void_ptr
local ptr_of_int = ctypes.ptr_of_int
local uchar_ptr = ctypes.uchar_ptr
local _M = {}
local mt = {__index = _M}
local cipher_ctx_ptr_ct = ffi.typeof('EVP_CIPHER_CTX*')
local out_length = ptr_of_int()
-- EVP_MAX_BLOCK_LENGTH is 32, we give it a 64 to be future proof
local out_buffer = ctypes.uchar_array(1024 + 64)
function _M.new(typ, properties)
if not typ then
return nil, "cipher.new: expect type to be defined"
end
local ctx
if OPENSSL_11_OR_LATER then
ctx = C.EVP_CIPHER_CTX_new()
ffi_gc(ctx, C.EVP_CIPHER_CTX_free)
elseif OPENSSL_10 then
ctx = ffi.new('EVP_CIPHER_CTX')
C.EVP_CIPHER_CTX_init(ctx)
ffi_gc(ctx, C.EVP_CIPHER_CTX_cleanup)
end
if ctx == nil then
return nil, "cipher.new: failed to create EVP_CIPHER_CTX"
end
local ctyp
if OPENSSL_3X then
ctyp = C.EVP_CIPHER_fetch(ctx_lib.get_libctx(), typ, properties)
else
ctyp = C.EVP_get_cipherbyname(typ)
end
local err_new = string.format("cipher.new: invalid cipher type \"%s\"", typ)
if ctyp == nil then
return nil, format_error(err_new)
end
local code = C.EVP_CipherInit_ex(ctx, ctyp, nil, "", nil, -1)
if code ~= 1 then
return nil, format_error(err_new)
end
return setmetatable({
ctx = ctx,
algo = ctyp,
initialized = false,
block_size = tonumber(OPENSSL_3X and C.EVP_CIPHER_CTX_get_block_size(ctx)
or C.EVP_CIPHER_CTX_block_size(ctx)),
key_size = tonumber(OPENSSL_3X and C.EVP_CIPHER_CTX_get_key_length(ctx)
or C.EVP_CIPHER_CTX_key_length(ctx)),
iv_size = tonumber(OPENSSL_3X and C.EVP_CIPHER_CTX_get_iv_length(ctx)
or C.EVP_CIPHER_CTX_iv_length(ctx)),
}, mt), nil
end
function _M.istype(l)
return l and l.ctx and ffi.istype(cipher_ctx_ptr_ct, l.ctx)
end
function _M:get_provider_name()
if not OPENSSL_3X then
return false, "cipher:get_provider_name is not supported"
end
local p = C.EVP_CIPHER_get0_provider(self.algo)
if p == nil then
return nil
end
return ffi_str(C.OSSL_PROVIDER_get0_name(p))
end
if OPENSSL_3X then
local param_lib = require "resty.openssl.param"
_M.settable_params, _M.set_params, _M.gettable_params, _M.get_param = param_lib.get_params_func("EVP_CIPHER_CTX")
end
function _M:init(key, iv, opts)
opts = opts or {}
if not key or #key ~= self.key_size then
return false, string.format("cipher:init: incorrect key size, expect %d", self.key_size)
end
if not iv or #iv ~= self.iv_size then
return false, string.format("cipher:init: incorrect iv size, expect %d", self.iv_size)
end
-- always passed in the `EVP_CIPHER` parameter to reinitialized the cipher
-- it will have a same effect as EVP_CIPHER_CTX_cleanup/EVP_CIPHER_CTX_reset then Init_ex with
-- empty algo
if C.EVP_CipherInit_ex(self.ctx, self.algo, nil, key, iv, opts.is_encrypt and 1 or 0) == 0 then
return false, format_error("cipher:init EVP_CipherInit_ex")
end
if opts.no_padding then
-- EVP_CIPHER_CTX_set_padding() always returns 1.
C.EVP_CIPHER_CTX_set_padding(self.ctx, 0)
end
self.initialized = true
return true
end
function _M:encrypt(key, iv, s, no_padding, aead_aad)
local _, err = self:init(key, iv, {
is_encrypt = true,
no_padding = no_padding,
})
if err then
return nil, err
end
if aead_aad then
local _, err = self:update_aead_aad(aead_aad)
if err then
return nil, err
end
end
return self:final(s)
end
function _M:decrypt(key, iv, s, no_padding, aead_aad, aead_tag)
local _, err = self:init(key, iv, {
is_encrypt = false,
no_padding = no_padding,
})
if err then
return nil, err
end
if aead_aad then
local _, err = self:update_aead_aad(aead_aad)
if err then
return nil, err
end
end
if aead_tag then
local _, err = self:set_aead_tag(aead_tag)
if err then
return nil, err
end
end
return self:final(s)
end
-- https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
function _M:update_aead_aad(aad)
if not self.initialized then
return nil, "cipher:update_aead_aad: cipher not initalized, call cipher:init first"
end
if C.EVP_CipherUpdate(self.ctx, nil, out_length, aad, #aad) ~= 1 then
return false, format_error("cipher:update_aead_aad")
end
return true
end
function _M:get_aead_tag(size)
if not self.initialized then
return nil, "cipher:get_aead_tag: cipher not initalized, call cipher:init first"
end
size = size or self.key_size / 2
if size > self.key_size then
return nil, string.format("tag size %d is too large", size)
end
if C.EVP_CIPHER_CTX_ctrl(self.ctx, evp_macro.EVP_CTRL_AEAD_GET_TAG, size, out_buffer) ~= 1 then
return nil, format_error("cipher:get_aead_tag")
end
return ffi_str(out_buffer, size)
end
function _M:set_aead_tag(tag)
if not self.initialized then
return nil, "cipher:set_aead_tag: cipher not initalized, call cipher:init first"
end
if type(tag) ~= "string" then
return false, "cipher:set_aead_tag expect a string at #1"
end
local tag_void_ptr = ffi_cast(void_ptr, tag)
if C.EVP_CIPHER_CTX_ctrl(self.ctx, evp_macro.EVP_CTRL_AEAD_SET_TAG, #tag, tag_void_ptr) ~= 1 then
return false, format_error("cipher:set_aead_tag")
end
return true
end
function _M:update(...)
if not self.initialized then
return nil, "cipher:update: cipher not initalized, call cipher:init first"
end
local ret = {}
for i, s in ipairs({...}) do
local inl = #s
if inl > 1024 then
s = ffi_cast(uchar_ptr, s)
for i=0, inl-1, 1024 do
local chunk_size = 1024
if inl - i < 1024 then
chunk_size = inl - i
end
if C.EVP_CipherUpdate(self.ctx, out_buffer, out_length, s+i, chunk_size) ~= 1 then
return nil, format_error("cipher:update")
end
table.insert(ret, ffi_str(out_buffer, out_length[0]))
end
else
if C.EVP_CipherUpdate(self.ctx, out_buffer, out_length, s, inl) ~= 1 then
return nil, format_error("cipher:update")
end
table.insert(ret, ffi_str(out_buffer, out_length[0]))
end
end
return table.concat(ret, "")
end
function _M:final(s)
local ret, err
if s then
ret, err = self:update(s)
if err then
return nil, err
end
end
if C.EVP_CipherFinal_ex(self.ctx, out_buffer, out_length) ~= 1 then
return nil, format_error("cipher:final: EVP_CipherFinal_ex")
end
local final_ret = ffi_str(out_buffer, out_length[0])
return ret and (ret .. final_ret) or final_ret
end
function _M:derive(key, salt, count, md, md_properties)
if type(key) ~= "string" then
return nil, nil, "cipher:derive: expect a string at #1"
elseif salt and type(salt) ~= "string" then
return nil, nil, "cipher:derive: expect a string at #2"
elseif count then
count = tonumber(count)
if not count then
return nil, nil, "cipher:derive: expect a number at #3"
end
elseif md and type(md) ~= "string" then
return nil, nil, "cipher:derive: expect a string or nil at #4"
end
if salt then
if #salt > 8 then
ngx.log(ngx.WARN, "cipher:derive: salt is too long, truncate salt to 8 bytes")
salt = salt:sub(0, 8)
elseif #salt < 8 then
ngx.log(ngx.WARN, "cipher:derive: salt is too short, padding with zero bytes to length")
salt = salt .. string.rep('\000', 8 - #salt)
end
end
local mdt
if OPENSSL_3X then
mdt = C.EVP_MD_fetch(ctx_lib.get_libctx(), md or 'sha1', md_properties)
else
mdt = C.EVP_get_digestbyname(md or 'sha1')
end
if mdt == nil then
return nil, nil, string.format("cipher:derive: invalid digest type \"%s\"", md)
end
local cipt = C.EVP_CIPHER_CTX_cipher(self.ctx)
local keyb = uchar_array(self.key_size)
local ivb = uchar_array(self.iv_size)
local size = C.EVP_BytesToKey(cipt, mdt, salt,
key, #key, count or 1,
keyb, ivb)
if size == 0 then
return nil, nil, format_error("cipher:derive: EVP_BytesToKey")
end
return ffi_str(keyb, size), ffi_str(ivb, self.iv_size)
end
return _M

View File

@ -0,0 +1,78 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
require "resty.openssl.include.ossl_typ"
local format_error = require("resty.openssl.err").format_error
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
ffi.cdef [[
OSSL_LIB_CTX *OSSL_LIB_CTX_new(void);
int OSSL_LIB_CTX_load_config(OSSL_LIB_CTX *ctx, const char *config_file);
void OSSL_LIB_CTX_free(OSSL_LIB_CTX *ctx);
]]
local ossl_lib_ctx
local function new(request_context_only, conf_file)
if not OPENSSL_3X then
return false, "ctx is only supported from OpenSSL 3.0"
end
local ctx = C.OSSL_LIB_CTX_new()
ffi_gc(ctx, C.OSSL_LIB_CTX_free)
if conf_file and C.OSSL_LIB_CTX_load_config(ctx, conf_file) ~= 1 then
return false, format_error("ctx.new")
end
if request_context_only then
ngx.ctx.ossl_lib_ctx = ctx
else
ossl_lib_ctx = ctx
end
return true
end
local function free(request_context_only)
if not OPENSSL_3X then
return false, "ctx is only supported from OpenSSL 3.0"
end
if request_context_only then
ngx.ctx.ossl_lib_ctx = nil
else
ossl_lib_ctx = nil
end
return true
end
local test_request
do
local ok, exdata = pcall(require, "thread.exdata")
if ok and exdata then
test_request = function()
local r = exdata()
if r ~= nil then
return not not r
end
end
else
local getfenv = getfenv
function test_request()
return not not getfenv(0).__ngx_req
end
end
end
return {
new = new,
free = free,
get_libctx = function() return test_request() and ngx.ctx.ossl_lib_ctx or ossl_lib_ctx end,
}

View File

@ -0,0 +1,142 @@
local ffi = require "ffi"
local C = ffi.C
require "resty.openssl.include.dh"
local bn_lib = require "resty.openssl.bn"
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local format_error = require("resty.openssl.err").format_error
local _M = {}
_M.params = {"public", "private", "p", "q", "g"}
local empty_table = {}
local bn_ptrptr_ct = ffi.typeof("const BIGNUM *[1]")
function _M.get_parameters(dh_st)
return setmetatable(empty_table, {
__index = function(_, k)
local ptr, ret
if OPENSSL_11_OR_LATER then
ptr = bn_ptrptr_ct()
end
if OPENSSL_11_OR_LATER then
ptr = bn_ptrptr_ct()
end
if k == 'p' then
if OPENSSL_11_OR_LATER then
C.DH_get0_pqg(dh_st, ptr, nil, nil)
end
elseif k == 'q' then
if OPENSSL_11_OR_LATER then
C.DH_get0_pqg(dh_st, nil, ptr, nil)
end
elseif k == 'g' then
if OPENSSL_11_OR_LATER then
C.DH_get0_pqg(dh_st, nil, nil, ptr)
end
elseif k == 'public' then
if OPENSSL_11_OR_LATER then
C.DH_get0_key(dh_st, ptr, nil)
end
k = "pub_key"
elseif k == 'private' then
if OPENSSL_11_OR_LATER then
C.DH_get0_key(dh_st, nil, ptr)
end
k = "priv_key"
else
return nil, "rsa.get_parameters: unknown parameter \"" .. k .. "\" for RSA key"
end
if OPENSSL_11_OR_LATER then
ret = ptr[0]
elseif OPENSSL_10 then
ret = dh_st[k]
end
if ret == nil then
return nil
end
return bn_lib.dup(ret)
end
}), nil
end
local function dup_bn_value(v)
if not bn_lib.istype(v) then
return nil, "expect value to be a bn instance"
end
local bn = C.BN_dup(v.ctx)
if bn == nil then
return nil, "BN_dup() failed"
end
return bn
end
function _M.set_parameters(dh_st, opts)
local err
local opts_bn = {}
-- remember which parts of BNs has been added to dh_st, they should be freed
-- by DH_free and we don't cleanup them on failure
local cleanup_from_idx = 1
-- dup input
local do_set_key, do_set_pqg
for k, v in pairs(opts) do
opts_bn[k], err = dup_bn_value(v)
if err then
err = "dh.set_parameters: cannot process parameter \"" .. k .. "\":" .. err
goto cleanup_with_error
end
if k == "private" or k == "public" then
do_set_key = true
elseif k == "p" or k == "q" or k == "g" then
do_set_pqg = true
end
end
if OPENSSL_11_OR_LATER then
local code
if do_set_key then
code = C.DH_set0_key(dh_st, opts_bn["public"], opts_bn["private"])
if code == 0 then
err = format_error("dh.set_parameters: DH_set0_key")
goto cleanup_with_error
end
end
cleanup_from_idx = cleanup_from_idx + 2
if do_set_pqg then
code = C.DH_set0_pqg(dh_st, opts_bn["p"], opts_bn["q"], opts_bn["g"])
if code == 0 then
err = format_error("dh.set_parameters: DH_set0_pqg")
goto cleanup_with_error
end
end
return true
elseif OPENSSL_10 then
for k, v in pairs(opts_bn) do
if k == "public" then
k = "pub_key"
elseif k == "private" then
k = "priv_key"
end
if dh_st[k] ~= nil then
C.BN_free(dh_st[k])
end
dh_st[k]= v
end
return true
end
::cleanup_with_error::
for i, k in pairs(_M.params) do
if i >= cleanup_from_idx then
C.BN_free(opts_bn[k])
end
end
return false, err
end
return _M

View File

@ -0,0 +1,116 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_str = ffi.string
require "resty.openssl.include.evp.md"
local ctypes = require "resty.openssl.auxiliary.ctypes"
local ctx_lib = require "resty.openssl.ctx"
local format_error = require("resty.openssl.err").format_error
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local _M = {}
local mt = {__index = _M}
local md_ctx_ptr_ct = ffi.typeof('EVP_MD_CTX*')
function _M.new(typ, properties)
local ctx
if OPENSSL_11_OR_LATER then
ctx = C.EVP_MD_CTX_new()
ffi_gc(ctx, C.EVP_MD_CTX_free)
elseif OPENSSL_10 then
ctx = C.EVP_MD_CTX_create()
ffi_gc(ctx, C.EVP_MD_CTX_destroy)
end
if ctx == nil then
return nil, "digest.new: failed to create EVP_MD_CTX"
end
local err_new = string.format("digest.new: invalid digest type \"%s\"", typ)
local algo
if typ == "null" then
algo = C.EVP_md_null()
else
if OPENSSL_3X then
algo = C.EVP_MD_fetch(ctx_lib.get_libctx(), typ or 'sha1', properties)
else
algo = C.EVP_get_digestbyname(typ or 'sha1')
end
if algo == nil then
return nil, format_error(err_new)
end
end
local code = C.EVP_DigestInit_ex(ctx, algo, nil)
if code ~= 1 then
return nil, format_error(err_new)
end
return setmetatable({
ctx = ctx,
algo = algo,
buf = ctypes.uchar_array(OPENSSL_3X and C.EVP_MD_get_size(algo) or C.EVP_MD_size(algo)),
}, mt), nil
end
function _M.istype(l)
return l and l.ctx and ffi.istype(md_ctx_ptr_ct, l.ctx)
end
function _M:get_provider_name()
if not OPENSSL_3X then
return false, "digest:get_provider_name is not supported"
end
local p = C.EVP_MD_get0_provider(self.algo)
if p == nil then
return nil
end
return ffi_str(C.OSSL_PROVIDER_get0_name(p))
end
if OPENSSL_3X then
local param_lib = require "resty.openssl.param"
_M.settable_params, _M.set_params, _M.gettable_params, _M.get_param = param_lib.get_params_func("EVP_MD_CTX")
end
function _M:update(...)
for _, s in ipairs({...}) do
if C.EVP_DigestUpdate(self.ctx, s, #s) ~= 1 then
return false, format_error("digest:update")
end
end
return true, nil
end
local result_length = ctypes.ptr_of_uint()
function _M:final(s)
if s then
if C.EVP_DigestUpdate(self.ctx, s, #s) ~= 1 then
return false, format_error("digest:final")
end
end
-- no return value of EVP_DigestFinal_ex
C.EVP_DigestFinal_ex(self.ctx, self.buf, result_length)
if result_length[0] == nil or result_length[0] <= 0 then
return nil, format_error("digest:final: EVP_DigestFinal_ex")
end
return ffi_str(self.buf, result_length[0])
end
function _M:reset()
local code = C.EVP_DigestInit_ex(self.ctx, self.algo, nil)
if code ~= 1 then
return false, format_error("digest:reset")
end
return true
end
return _M

View File

@ -0,0 +1,186 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
require "resty.openssl.include.ec"
local bn_lib = require "resty.openssl.bn"
local objects_lib = require "resty.openssl.objects"
local ctypes = require "resty.openssl.auxiliary.ctypes"
local version_num = require("resty.openssl.version").version_num
local format_error = require("resty.openssl.err").format_error
local BORINGSSL = require("resty.openssl.version").BORINGSSL
local _M = {}
_M.params = {"group", "public", "private", "x", "y"}
local empty_table = {}
function _M.get_parameters(ec_key_st)
return setmetatable(empty_table, {
__index = function(_, k)
local group = C.EC_KEY_get0_group(ec_key_st)
local bn
if k == 'group' then
local nid = C.EC_GROUP_get_curve_name(group)
if nid == 0 then
return nil, "ec.get_parameters: EC_GROUP_get_curve_name() failed"
end
return nid
elseif k == 'public' or k == "pub_key" then
local pub_point = C.EC_KEY_get0_public_key(ec_key_st)
if pub_point == nil then
return nil, format_error("ec.get_parameters: EC_KEY_get0_public_key")
end
local point_form = C.EC_KEY_get_conv_form(ec_key_st)
if point_form == nil then
return nil, format_error("ec.get_parameters: EC_KEY_get_conv_form")
end
if BORINGSSL then
local sz = tonumber(C.EC_POINT_point2oct(group, pub_point, point_form, nil, 0, bn_lib.bn_ctx_tmp))
if sz <= 0 then
return nil, format_error("ec.get_parameters: EC_POINT_point2oct")
end
local buf = ctypes.uchar_array(sz)
C.EC_POINT_point2oct(group, pub_point, point_form, buf, sz, bn_lib.bn_ctx_tmp)
buf = ffi.string(buf, sz)
local err
bn, err = bn_lib.from_binary(buf)
if bn == nil then
return nil, "ec.get_parameters: bn_lib.from_binary: " .. err
end
return bn
else
bn = C.EC_POINT_point2bn(group, pub_point, point_form, nil, bn_lib.bn_ctx_tmp)
if bn == nil then
return nil, format_error("ec.get_parameters: EC_POINT_point2bn")
end
ffi_gc(bn, C.BN_free)
end
elseif k == 'private' or k == "priv_key" then
-- get0, don't GC
bn = C.EC_KEY_get0_private_key(ec_key_st)
elseif k == 'x' or k == 'y' then
local pub_point = C.EC_KEY_get0_public_key(ec_key_st)
if pub_point == nil then
return nil, format_error("ec.get_parameters: EC_KEY_get0_public_key")
end
bn = C.BN_new()
if bn == nil then
return nil, "ec.get_parameters: BN_new() failed"
end
ffi_gc(bn, C.BN_free)
local f
if version_num >= 0x10101000 then
f = C.EC_POINT_get_affine_coordinates
else
f = C.EC_POINT_get_affine_coordinates_GFp
end
local code
if k == 'x' then
code = f(group, pub_point, bn, nil, bn_lib.bn_ctx_tmp)
else
code = f(group, pub_point, nil, bn, bn_lib.bn_ctx_tmp)
end
if code ~= 1 then
return nil, format_error("ec.get_parameters: EC_POINT_get_affine_coordinates")
end
else
return nil, "ec.get_parameters: unknown parameter \"" .. k .. "\" for EC key"
end
if bn == nil then
return nil
end
return bn_lib.dup(bn)
end
}), nil
end
function _M.set_parameters(ec_key_st, opts)
for _, k in ipairs(_M.params) do
if k ~= "group" then
if opts[k] and not bn_lib.istype(opts[k]) then
return nil, "expect parameter \"" .. k .. "\" to be a bn instance"
end
end
end
local group_nid = opts["group"]
local group
if group_nid then
local nid, err = objects_lib.txtnid2nid(group_nid)
if err then
return nil, "ec.set_parameters: cannot use parameter \"group\":" .. err
end
group = C.EC_GROUP_new_by_curve_name(nid)
if group == nil then
return nil, "ec.set_parameters: EC_GROUP_new_by_curve_name() failed"
end
ffi_gc(group, C.EC_GROUP_free)
-- # define OPENSSL_EC_NAMED_CURVE 0x001
C.EC_GROUP_set_asn1_flag(group, 1)
C.EC_GROUP_set_point_conversion_form(group, C.POINT_CONVERSION_UNCOMPRESSED)
if C.EC_KEY_set_group(ec_key_st, group) ~= 1 then
return nil, format_error("ec.set_parameters: EC_KEY_set_group")
end
end
local x = opts["x"]
local y = opts["y"]
local pub = opts["public"]
if (x and not y) or (y and not x) then
return nil, "ec.set_parameters: \"x\" and \"y\" parameter must be defined at same time or both undefined"
end
if x and y then
if pub then
return nil, "ec.set_parameters: cannot set \"x\" and \"y\" with \"public\" at same time to set public key"
end
-- double check if we have set group already
if group == nil then
group = C.EC_KEY_get0_group(ec_key_st)
if group == nil then
return nil, "ec.set_parameters: cannot set public key without setting \"group\""
end
end
if C.EC_KEY_set_public_key_affine_coordinates(ec_key_st, x.ctx, y.ctx) ~= 1 then
return nil, format_error("ec.set_parameters: EC_KEY_set_public_key_affine_coordinates")
end
end
if pub then
if group == nil then
group = C.EC_KEY_get0_group(ec_key_st)
if group == nil then
return nil, "ec.set_parameters: cannot set public key without setting \"group\""
end
end
local point = C.EC_POINT_bn2point(group, pub.ctx, nil, bn_lib.bn_ctx_tmp)
if point == nil then
return nil, format_error("ec.set_parameters: EC_POINT_bn2point")
end
ffi_gc(point, C.EC_POINT_free)
if C.EC_KEY_set_public_key(ec_key_st, point) ~= 1 then
return nil, format_error("ec.set_parameters: EC_KEY_set_public_key")
end
end
local priv = opts["private"]
if priv then
-- openssl duplicates it inside
if C.EC_KEY_set_private_key(ec_key_st, priv.ctx) ~= 1 then
return nil, format_error("ec.set_parameters: EC_KEY_set_private_key")
end
end
end
return _M

View File

@ -0,0 +1,67 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_str = ffi.string
require "resty.openssl.include.ec"
require "resty.openssl.include.evp"
local ctypes = require "resty.openssl.auxiliary.ctypes"
local format_error = require("resty.openssl.err").format_error
local _M = {}
_M.params = {"public", "private"}
local empty_table = {}
local MAX_ECX_KEY_SIZE = 114 -- ed448 uses 114 bytes
function _M.get_parameters(evp_pkey_st)
return setmetatable(empty_table, {
__index = function(_, k)
local buf = ctypes.uchar_array(MAX_ECX_KEY_SIZE)
local length = ctypes.ptr_of_size_t(MAX_ECX_KEY_SIZE)
if k == 'public' or k == "pub_key" then
if C.EVP_PKEY_get_raw_public_key(evp_pkey_st, buf, length) ~= 1 then
error(format_error("ecx.get_parameters: EVP_PKEY_get_raw_private_key"))
end
elseif k == 'private' or k == "priv ~=_key" then
if C.EVP_PKEY_get_raw_private_key(evp_pkey_st, buf, length) ~= 1 then
return nil, format_error("ecx.get_parameters: EVP_PKEY_get_raw_private_key")
end
else
return nil, "ecx.get_parameters: unknown parameter \"" .. k .. "\" for EC key"
end
return ffi_str(buf, length[0])
end
}), nil
end
function _M.set_parameters(key_type, evp_pkey_st, opts)
-- for ecx keys we always create a new EVP_PKEY and release the old one
-- Note: we allow to pass a nil as evp_pkey_st to create a new EVP_PKEY
local key
if opts.private then
local priv = opts.private
key = C.EVP_PKEY_new_raw_private_key(key_type, nil, priv, #priv)
if key == nil then
return nil, format_error("ecx.set_parameters: EVP_PKEY_new_raw_private_key")
end
elseif opts.public then
local pub = opts.public
key = C.EVP_PKEY_new_raw_public_key(key_type, nil, pub, #pub)
if key == nil then
return nil, format_error("ecx.set_parameters: EVP_PKEY_new_raw_public_key")
end
else
return nil, "no parameter is specified"
end
if evp_pkey_st ~= nil then
C.EVP_PKEY_free(evp_pkey_st)
end
return key
end
return _M

View File

@ -0,0 +1,62 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_str = ffi.string
local ffi_sizeof = ffi.sizeof
local ctypes = require "resty.openssl.auxiliary.ctypes"
require "resty.openssl.include.err"
local constchar_ptrptr = ffi.typeof("const char*[1]")
local buf = ffi.new('char[256]')
local function format_error(ctx, code, all_errors)
local errors = {}
if code then
table.insert(errors, string.format("code: %d", code or 0))
end
-- get the OpenSSL errors
while C.ERR_peek_error() ~= 0 do
local line = ctypes.ptr_of_int()
local path = constchar_ptrptr()
local code
if all_errors then
code = C.ERR_get_error_line(path, line)
else
code = C.ERR_peek_last_error_line(path, line)
end
local abs_path = ffi_str(path[0])
-- ../crypto/asn1/a_d2i_fp.c => crypto/asn1/a_d2i_fp.c
local start = abs_path:find("/")
if start then
abs_path = abs_path:sub(start+1)
end
C.ERR_error_string_n(code, buf, ffi_sizeof(buf))
table.insert(errors, string.format("%s:%d:%s",
abs_path, line[0], ffi_str(buf))
)
if not all_errors then
break
end
end
C.ERR_clear_error()
if #errors > 0 then
return string.format("%s%s%s", (ctx or ""), (ctx and ": " or ""), table.concat(errors, " "))
else
return string.format("%s failed", ctx)
end
end
local function format_all_error(ctx, code)
return format_error(ctx, code, true)
end
return {
format_error = format_error,
format_all_error = format_all_error,
}

View File

@ -0,0 +1,90 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_str = ffi.string
require "resty.openssl.include.hmac"
require "resty.openssl.include.evp.md"
local ctypes = require "resty.openssl.auxiliary.ctypes"
local format_error = require("resty.openssl.err").format_error
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local _M = {}
local mt = {__index = _M}
local hmac_ctx_ptr_ct = ffi.typeof('HMAC_CTX*')
-- Note: https://www.openssl.org/docs/manmaster/man3/HMAC_Init.html
-- Replace with EVP_MAC_* functions for OpenSSL 3.0
function _M.new(key, typ)
local ctx
if OPENSSL_11_OR_LATER then
ctx = C.HMAC_CTX_new()
ffi_gc(ctx, C.HMAC_CTX_free)
elseif OPENSSL_10 then
ctx = ffi.new('HMAC_CTX')
C.HMAC_CTX_init(ctx)
ffi_gc(ctx, C.HMAC_CTX_cleanup)
end
if ctx == nil then
return nil, "hmac.new: failed to create HMAC_CTX"
end
local algo = C.EVP_get_digestbyname(typ or 'sha1')
if algo == nil then
return nil, string.format("hmac.new: invalid digest type \"%s\"", typ)
end
local code = C.HMAC_Init_ex(ctx, key, #key, algo, nil)
if code ~= 1 then
return nil, format_error("hmac.new")
end
return setmetatable({
ctx = ctx,
algo = algo,
buf = ctypes.uchar_array(OPENSSL_3X and C.EVP_MD_get_size(algo) or C.EVP_MD_size(algo)),
}, mt), nil
end
function _M.istype(l)
return l and l.ctx and ffi.istype(hmac_ctx_ptr_ct, l.ctx)
end
function _M:update(...)
for _, s in ipairs({...}) do
if C.HMAC_Update(self.ctx, s, #s) ~= 1 then
return false, format_error("hmac:update")
end
end
return true, nil
end
local result_length = ctypes.ptr_of_uint()
function _M:final(s)
if s then
if C.HMAC_Update(self.ctx, s, #s) ~= 1 then
return false, format_error("hmac:final")
end
end
if C.HMAC_Final(self.ctx, self.buf, result_length) ~= 1 then
return nil, format_error("hmac:final: HMAC_Final")
end
return ffi_str(self.buf, result_length[0])
end
function _M:reset()
local code = C.HMAC_Init_ex(self.ctx, nil, 0, nil, nil)
if code ~= 1 then
return false, format_error("hmac:reset")
end
return true
end
return _M

View File

@ -0,0 +1,94 @@
local ffi = require "ffi"
local C = ffi.C
require "resty.openssl.include.ossl_typ"
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
ffi.cdef [[
typedef struct ASN1_VALUE_st ASN1_VALUE;
typedef struct asn1_type_st ASN1_TYPE;
ASN1_IA5STRING *ASN1_IA5STRING_new();
int ASN1_STRING_type(const ASN1_STRING *x);
ASN1_STRING *ASN1_STRING_type_new(int type);
int ASN1_STRING_set(ASN1_STRING *str, const void *data, int len);
ASN1_INTEGER *BN_to_ASN1_INTEGER(const BIGNUM *bn, ASN1_INTEGER *ai);
BIGNUM *ASN1_INTEGER_to_BN(const ASN1_INTEGER *ai, BIGNUM *bn);
typedef int time_t;
ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t t);
int ASN1_INTEGER_set(ASN1_INTEGER *a, long v);
long ASN1_INTEGER_get(const ASN1_INTEGER *a);
int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v);
int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v);
int ASN1_STRING_length(const ASN1_STRING *x);
]]
local function declare_asn1_functions(typ, has_ex)
local t = {}
for i=1, 7 do
t[i] = typ
end
ffi.cdef(string.format([[
%s *%s_new(void);
void %s_free(%s *a);
%s *%s_dup(%s *a);
]], unpack(t)))
if OPENSSL_3X and has_ex then
ffi.cdef(string.format([[
%s *%s_new_ex(OSSL_LIB_CTX *libctx, const char *propq);
]], typ, typ))
end
end
declare_asn1_functions("ASN1_INTEGER")
declare_asn1_functions("ASN1_OBJECT")
declare_asn1_functions("ASN1_STRING")
declare_asn1_functions("ASN1_ENUMERATED")
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local BORINGSSL_110 = require("resty.openssl.version").BORINGSSL_110
local ASN1_STRING_get0_data
if OPENSSL_11_OR_LATER then
ffi.cdef[[
const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x);
]]
ASN1_STRING_get0_data = C.ASN1_STRING_get0_data
elseif OPENSSL_10 then
ffi.cdef[[
unsigned char *ASN1_STRING_data(ASN1_STRING *x);
typedef struct ASN1_ENCODING_st {
unsigned char *enc; /* DER encoding */
long len; /* Length of encoding */
int modified; /* set to 1 if 'enc' is invalid */
} ASN1_ENCODING;
]]
ASN1_STRING_get0_data = C.ASN1_STRING_data
end
if BORINGSSL_110 then
ffi.cdef [[
// required by resty/openssl/include/x509/crl.lua
typedef struct ASN1_ENCODING_st {
unsigned char *enc; /* DER encoding */
long len; /* Length of encoding */
int modified; /* set to 1 if 'enc' is invalid */
} ASN1_ENCODING;
]]
end
return {
ASN1_STRING_get0_data = ASN1_STRING_get0_data,
declare_asn1_functions = declare_asn1_functions,
has_new_ex = true,
}

View File

@ -0,0 +1,13 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
ffi.cdef [[
typedef struct bio_method_st BIO_METHOD;
long BIO_ctrl(BIO *bp, int cmd, long larg, void *parg);
BIO *BIO_new_mem_buf(const void *buf, int len);
BIO *BIO_new(const BIO_METHOD *type);
int BIO_free(BIO *a);
const BIO_METHOD *BIO_s_mem(void);
int BIO_read(BIO *b, void *data, int dlen);
]]

View File

@ -0,0 +1,79 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local BN_ULONG
if ffi.abi('64bit') then
BN_ULONG = 'unsigned long long'
else -- 32bit
BN_ULONG = 'unsigned int'
end
ffi.cdef(
[[
BIGNUM *BN_new(void);
void BN_free(BIGNUM *a);
BN_CTX *BN_CTX_new(void);
void BN_CTX_init(BN_CTX *c);
void BN_CTX_free(BN_CTX *c);
BIGNUM *BN_dup(const BIGNUM *a);
int BN_add_word(BIGNUM *a, ]] .. BN_ULONG ..[[ w);
int BN_set_word(BIGNUM *a, ]] .. BN_ULONG ..[[ w);
]] .. BN_ULONG ..[[ BN_get_word(BIGNUM *a);
int BN_num_bits(const BIGNUM *a);
BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen);
int BN_hex2bn(BIGNUM **a, const char *str);
int BN_dec2bn(BIGNUM **a, const char *str);
int BN_bn2bin(const BIGNUM *a, unsigned char *to);
char *BN_bn2hex(const BIGNUM *a);
char *BN_bn2dec(const BIGNUM *a);
void BN_set_negative(BIGNUM *a, int n);
int BN_is_negative(const BIGNUM *a);
int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
int BN_mul(BIGNUM *r, BIGNUM *a, BIGNUM *b, BN_CTX *ctx);
int BN_sqr(BIGNUM *r, BIGNUM *a, BN_CTX *ctx);
int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *a, const BIGNUM *d,
BN_CTX *ctx);
int BN_mod_add(BIGNUM *ret, BIGNUM *a, BIGNUM *b, const BIGNUM *m,
BN_CTX *ctx);
int BN_mod_sub(BIGNUM *ret, BIGNUM *a, BIGNUM *b, const BIGNUM *m,
BN_CTX *ctx);
int BN_mod_mul(BIGNUM *ret, BIGNUM *a, BIGNUM *b, const BIGNUM *m,
BN_CTX *ctx);
int BN_mod_sqr(BIGNUM *ret, BIGNUM *a, const BIGNUM *m, BN_CTX *ctx);
int BN_exp(BIGNUM *r, BIGNUM *a, BIGNUM *p, BN_CTX *ctx);
int BN_mod_exp(BIGNUM *r, BIGNUM *a, const BIGNUM *p,
const BIGNUM *m, BN_CTX *ctx);
int BN_gcd(BIGNUM *r, BIGNUM *a, BIGNUM *b, BN_CTX *ctx);
int BN_lshift(BIGNUM *r, const BIGNUM *a, int n);
int BN_rshift(BIGNUM *r, BIGNUM *a, int n);
int BN_cmp(BIGNUM *a, BIGNUM *b);
int BN_ucmp(BIGNUM *a, BIGNUM *b);
// openssl >= 1.1 only
int BN_is_zero(BIGNUM *a);
int BN_is_one(BIGNUM *a);
int BN_is_word(BIGNUM *a, ]] .. BN_ULONG ..[[ w);
int BN_is_odd(BIGNUM *a);
int BN_is_prime_ex(const BIGNUM *p,int nchecks, BN_CTX *ctx, BN_GENCB *cb);
int BN_generate_prime_ex(BIGNUM *ret,int bits,int safe, const BIGNUM *add,
const BIGNUM *rem, BN_GENCB *cb);
]]
)
if OPENSSL_3X then
ffi.cdef [[
int BN_check_prime(const BIGNUM *p, BN_CTX *ctx, BN_GENCB *cb);
]]
end

View File

@ -0,0 +1,9 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
ffi.cdef [[
CONF *NCONF_new(CONF_METHOD *meth);
void NCONF_free(CONF *conf);
int NCONF_load_bio(CONF *conf, BIO *bp, long *eline);
]]

View File

@ -0,0 +1,37 @@
local ffi = require "ffi"
local C = ffi.C
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local BORINGSSL = require("resty.openssl.version").BORINGSSL
local OPENSSL_free
if OPENSSL_10 then
ffi.cdef [[
void CRYPTO_free(void *ptr);
]]
OPENSSL_free = C.CRYPTO_free
elseif BORINGSSL then
ffi.cdef [[
void OPENSSL_free(void *ptr);
]]
OPENSSL_free = C.OPENSSL_free
elseif OPENSSL_11_OR_LATER then
ffi.cdef [[
void CRYPTO_free(void *ptr, const char *file, int line);
]]
OPENSSL_free = function(ptr)
-- file and line is for debuggin only, since we can't know the c file info
-- the macro is expanded, just ignore this
C.CRYPTO_free(ptr, "", 0)
end
end
ffi.cdef [[
int FIPS_mode(void);
int FIPS_mode_set(int ONOFF);
]]
return {
OPENSSL_free = OPENSSL_free,
}

View File

@ -0,0 +1,80 @@
local ffi = require "ffi"
local C = ffi.C
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.objects"
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
if OPENSSL_11_OR_LATER then
ffi.cdef [[
void DH_get0_pqg(const DH *dh,
const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g);
void DH_get0_key(const DH *dh,
const BIGNUM **pub_key, const BIGNUM **priv_key);
int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key);
]]
elseif OPENSSL_10 then
ffi.cdef [[
struct dh_st {
/*
* This first argument is used to pick up errors when a DH is passed
* instead of a EVP_PKEY
*/
int pad;
int version;
BIGNUM *p;
BIGNUM *g;
long length; /* optional */
BIGNUM *pub_key; /* g^x */
BIGNUM *priv_key; /* x */
int flags;
/*BN_MONT_CTX*/ void *method_mont_p;
/* Place holders if we want to do X9.42 DH */
BIGNUM *q;
BIGNUM *j;
unsigned char *seed;
int seedlen;
BIGNUM *counter;
int references;
/* trimmer */
// CRYPTO_EX_DATA ex_data;
// const DH_METHOD *meth;
// ENGINE *engine;
};
]]
end
ffi.cdef [[
DH *DH_get_1024_160(void);
DH *DH_get_2048_224(void);
DH *DH_get_2048_256(void);
DH *DH_new_by_nid(int nid);
]];
local dh_groups = {
-- per https://tools.ietf.org/html/rfc5114
dh_1024_160 = function() return C.DH_get_1024_160() end,
dh_2048_224 = function() return C.DH_get_2048_224() end,
dh_2048_256 = function() return C.DH_get_2048_256() end,
}
local groups = {
"ffdhe2048", "ffdhe3072", "ffdhe4096", "ffdhe6144", "ffdhe8192",
"modp_2048", "modp_3072", "modp_4096", "modp_6144", "modp_8192",
-- following cannot be used with FIPS provider
"modp_1536", -- and the RFC5114 ones
}
for _, group in ipairs(groups) do
local nid = C.OBJ_sn2nid(group)
if nid ~= 0 then
dh_groups[group] = function() return C.DH_new_by_nid(nid) end
end
end
return {
dh_groups = dh_groups,
}

View File

@ -0,0 +1,59 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
ffi.cdef [[
/** Enum for the point conversion form as defined in X9.62 (ECDSA)
* for the encoding of a elliptic curve point (x,y) */
typedef enum {
/** the point is encoded as z||x, where the octet z specifies
* which solution of the quadratic equation y is */
POINT_CONVERSION_COMPRESSED = 2,
/** the point is encoded as z||x||y, where z is the octet 0x04 */
POINT_CONVERSION_UNCOMPRESSED = 4,
/** the point is encoded as z||x||y, where the octet z specifies
* which solution of the quadratic equation y is */
POINT_CONVERSION_HYBRID = 6
} point_conversion_form_t;
EC_KEY *EC_KEY_new(void);
void EC_KEY_free(EC_KEY *key);
EC_GROUP *EC_GROUP_new_by_curve_name(int nid);
void EC_GROUP_set_asn1_flag(EC_GROUP *group, int flag);
void EC_GROUP_set_point_conversion_form(EC_GROUP *group,
point_conversion_form_t form);
void EC_GROUP_set_curve_name(EC_GROUP *group, int nid);
int EC_GROUP_get_curve_name(const EC_GROUP *group);
void EC_GROUP_free(EC_GROUP *group);
BIGNUM *EC_POINT_point2bn(const EC_GROUP *, const EC_POINT *,
point_conversion_form_t form, BIGNUM *, BN_CTX *);
// for BoringSSL
size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *p,
point_conversion_form_t form,
unsigned char *buf, size_t len, BN_CTX *ctx);
// OpenSSL < 1.1.1
int EC_POINT_get_affine_coordinates_GFp(const EC_GROUP *group,
const EC_POINT *p,
BIGNUM *x, BIGNUM *y, BN_CTX *ctx);
// OpenSSL >= 1.1.1
int EC_POINT_get_affine_coordinates(const EC_GROUP *group, const EC_POINT *p,
BIGNUM *x, BIGNUM *y, BN_CTX *ctx);
EC_POINT *EC_POINT_bn2point(const EC_GROUP *group, const BIGNUM *bn,
EC_POINT *p, BN_CTX *ctx);
point_conversion_form_t EC_KEY_get_conv_form(const EC_KEY *key);
const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *key);
int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *prv);
const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key);
int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub);
int EC_KEY_set_public_key_affine_coordinates(EC_KEY *key, BIGNUM *x, BIGNUM *y);
const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key);
int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group);
]]

View File

@ -0,0 +1,16 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
ffi.cdef [[
ECDSA_SIG *ECDSA_SIG_new(void);
void ECDSA_SIG_free(ECDSA_SIG *sig);
void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps);
int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s);
int i2d_ECDSA_SIG(const ECDSA_SIG *sig, unsigned char **pp);
ECDSA_SIG *d2i_ECDSA_SIG(ECDSA_SIG **sig, const unsigned char **pp, long len);
int EC_GROUP_order_bits(const EC_GROUP *group);
]]

View File

@ -0,0 +1,9 @@
local ffi = require "ffi"
ffi.cdef [[
unsigned long ERR_peek_error(void);
unsigned long ERR_peek_last_error_line(const char **file, int *line);
unsigned long ERR_get_error_line(const char **file, int *line);
void ERR_clear_error(void);
void ERR_error_string_n(unsigned long e, char *buf, size_t len);
]]

View File

@ -0,0 +1,109 @@
local ffi = require "ffi"
local C = ffi.C
local bit = require("bit")
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.err"
require "resty.openssl.include.objects"
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local BORINGSSL = require("resty.openssl.version").BORINGSSL
if BORINGSSL then
ffi.cdef [[
int PKCS5_PBKDF2_HMAC(const char *password, size_t password_len,
const uint8_t *salt, size_t salt_len,
unsigned iterations, const EVP_MD *digest,
size_t key_len, uint8_t *out_key);
int EVP_PBE_scrypt(const char *password, size_t password_len,
const uint8_t *salt, size_t salt_len,
uint64_t N, uint64_t r, uint64_t p,
size_t max_mem, uint8_t *out_key,
size_t key_len);
]]
else
ffi.cdef [[
/* KDF */
int PKCS5_PBKDF2_HMAC(const char *pass, int passlen,
const unsigned char *salt, int saltlen, int iter,
const EVP_MD *digest, int keylen, unsigned char *out);
int EVP_PBE_scrypt(const char *pass, size_t passlen,
const unsigned char *salt, size_t saltlen,
uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem,
unsigned char *key, size_t keylen);
]]
end
if OPENSSL_3X then
require "resty.openssl.include.provider"
ffi.cdef [[
int EVP_set_default_properties(OSSL_LIB_CTX *libctx, const char *propq);
int EVP_default_properties_enable_fips(OSSL_LIB_CTX *libctx, int enable);
int EVP_default_properties_is_fips_enabled(OSSL_LIB_CTX *libctx);
// const OSSL_PROVIDER *EVP_RAND_get0_provider(const EVP_RAND *rand);
// EVP_RAND *EVP_RAND_fetch(OSSL_LIB_CTX *libctx, const char *algorithm,
// const char *properties);
]]
end
local EVP_PKEY_ALG_CTRL = 0x1000
local _M = {
EVP_PKEY_RSA = C.OBJ_txt2nid("rsaEncryption"),
EVP_PKEY_DH = C.OBJ_txt2nid("dhKeyAgreement"),
EVP_PKEY_EC = C.OBJ_txt2nid("id-ecPublicKey"),
EVP_PKEY_X25519 = C.OBJ_txt2nid("X25519"),
EVP_PKEY_ED25519 = C.OBJ_txt2nid("ED25519"),
EVP_PKEY_X448 = C.OBJ_txt2nid("X448"),
EVP_PKEY_ED448 = C.OBJ_txt2nid("ED448"),
EVP_PKEY_OP_PARAMGEN = bit.lshift(1, 1),
EVP_PKEY_OP_KEYGEN = bit.lshift(1, 2),
EVP_PKEY_OP_SIGN = bit.lshift(1, 3),
EVP_PKEY_OP_VERIFY = bit.lshift(1, 4),
EVP_PKEY_OP_DERIVE = OPENSSL_3X and bit.lshift(1, 12) or bit.lshift(1, 10),
EVP_PKEY_ALG_CTRL = EVP_PKEY_ALG_CTRL,
EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN = EVP_PKEY_ALG_CTRL + 1,
EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID = EVP_PKEY_ALG_CTRL + 1,
EVP_PKEY_CTRL_EC_PARAM_ENC = EVP_PKEY_ALG_CTRL + 2,
EVP_PKEY_CTRL_RSA_KEYGEN_BITS = EVP_PKEY_ALG_CTRL + 3,
EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP = EVP_PKEY_ALG_CTRL + 4,
EVP_PKEY_CTRL_RSA_PADDING = EVP_PKEY_ALG_CTRL + 1,
EVP_PKEY_CTRL_RSA_PSS_SALTLEN = EVP_PKEY_ALG_CTRL + 2,
EVP_CTRL_AEAD_SET_IVLEN = 0x9,
EVP_CTRL_AEAD_GET_TAG = 0x10,
EVP_CTRL_AEAD_SET_TAG = 0x11,
EVP_PKEY_CTRL_TLS_MD = EVP_PKEY_ALG_CTRL,
EVP_PKEY_CTRL_TLS_SECRET = EVP_PKEY_ALG_CTRL + 1,
EVP_PKEY_CTRL_TLS_SEED = EVP_PKEY_ALG_CTRL + 2,
EVP_PKEY_CTRL_HKDF_MD = EVP_PKEY_ALG_CTRL + 3,
EVP_PKEY_CTRL_HKDF_SALT = EVP_PKEY_ALG_CTRL + 4,
EVP_PKEY_CTRL_HKDF_KEY = EVP_PKEY_ALG_CTRL + 5,
EVP_PKEY_CTRL_HKDF_INFO = EVP_PKEY_ALG_CTRL + 6,
EVP_PKEY_CTRL_HKDF_MODE = EVP_PKEY_ALG_CTRL + 7,
EVP_PKEY_CTRL_PASS = EVP_PKEY_ALG_CTRL + 8,
EVP_PKEY_CTRL_SCRYPT_SALT = EVP_PKEY_ALG_CTRL + 9,
EVP_PKEY_CTRL_SCRYPT_N = EVP_PKEY_ALG_CTRL + 10,
EVP_PKEY_CTRL_SCRYPT_R = EVP_PKEY_ALG_CTRL + 11,
EVP_PKEY_CTRL_SCRYPT_P = EVP_PKEY_ALG_CTRL + 12,
EVP_PKEY_CTRL_SCRYPT_MAXMEM_BYTES = EVP_PKEY_ALG_CTRL + 13,
}
-- clean up error occurs during OBJ_txt2*
C.ERR_clear_error()
_M.ecx_curves = {
Ed25519 = _M.EVP_PKEY_ED25519,
X25519 = _M.EVP_PKEY_X25519,
Ed448 = _M.EVP_PKEY_ED448,
X448 = _M.EVP_PKEY_X448,
}
return _M

View File

@ -0,0 +1,123 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local BORINGSSL = require("resty.openssl.version").BORINGSSL
ffi.cdef [[
// openssl < 3.0
int EVP_CIPHER_CTX_block_size(const EVP_CIPHER_CTX *ctx);
int EVP_CIPHER_CTX_key_length(const EVP_CIPHER_CTX *ctx);
int EVP_CIPHER_CTX_iv_length(const EVP_CIPHER_CTX *ctx);
int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *c, int pad);
const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *ctx);
const EVP_CIPHER *EVP_get_cipherbyname(const char *name);
int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr);
int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
int *outl, const unsigned char *in, int inl);
int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
int *outl, const unsigned char *in, int inl);
int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx,
const EVP_CIPHER *cipher, ENGINE *impl,
const unsigned char *key,
const unsigned char *iv, int enc);
int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
int *outl, const unsigned char *in, int inl);
int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm,
int *outl);
// list functions
typedef void* fake_openssl_cipher_list_fn(const EVP_CIPHER *ciph, const char *from,
const char *to, void *x);
//void EVP_CIPHER_do_all_sorted(fake_openssl_cipher_list_fn*, void *arg);
void EVP_CIPHER_do_all_sorted(void (*fn)
(const EVP_CIPHER *ciph, const char *from,
const char *to, void *x), void *arg);
int EVP_CIPHER_nid(const EVP_CIPHER *cipher);
]]
if BORINGSSL then
ffi.cdef [[
int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md,
const uint8_t *salt, const uint8_t *data,
size_t data_len, unsigned count, uint8_t *key,
uint8_t *iv);
]]
else
ffi.cdef [[
int EVP_BytesToKey(const EVP_CIPHER *type, const EVP_MD *md,
const unsigned char *salt,
const unsigned char *data, int datal, int count,
unsigned char *key, unsigned char *iv);
]]
end
if OPENSSL_3X then
require "resty.openssl.include.provider"
ffi.cdef [[
int EVP_CIPHER_CTX_get_block_size(const EVP_CIPHER_CTX *ctx);
int EVP_CIPHER_CTX_get_key_length(const EVP_CIPHER_CTX *ctx);
int EVP_CIPHER_CTX_get_iv_length(const EVP_CIPHER_CTX *ctx);
int EVP_CIPHER_get_nid(const EVP_CIPHER *cipher);
const OSSL_PROVIDER *EVP_CIPHER_get0_provider(const EVP_CIPHER *cipher);
EVP_CIPHER *EVP_CIPHER_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
const char *properties);
typedef void* fake_openssl_cipher_provided_list_fn(EVP_CIPHER *cipher, void *arg);
void EVP_CIPHER_do_all_provided(OSSL_LIB_CTX *libctx,
fake_openssl_cipher_provided_list_fn*,
void *arg);
int EVP_CIPHER_up_ref(EVP_CIPHER *cipher);
void EVP_CIPHER_free(EVP_CIPHER *cipher);
const char *EVP_CIPHER_get0_name(const EVP_CIPHER *cipher);
int EVP_CIPHER_CTX_set_params(EVP_CIPHER_CTX *ctx, const OSSL_PARAM params[]);
const OSSL_PARAM *EVP_CIPHER_CTX_settable_params(EVP_CIPHER_CTX *ctx);
int EVP_CIPHER_CTX_get_params(EVP_CIPHER_CTX *ctx, OSSL_PARAM params[]);
const OSSL_PARAM *EVP_CIPHER_CTX_gettable_params(EVP_CIPHER_CTX *ctx);
]]
end
if OPENSSL_11_OR_LATER then
ffi.cdef [[
EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void);
int EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c);
void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *c);
]]
elseif OPENSSL_10 then
ffi.cdef [[
void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *a);
int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a);
// # define EVP_MAX_IV_LENGTH 16
// # define EVP_MAX_BLOCK_LENGTH 32
struct evp_cipher_ctx_st {
const EVP_CIPHER *cipher;
ENGINE *engine; /* functional reference if 'cipher' is
* ENGINE-provided */
int encrypt; /* encrypt or decrypt */
int buf_len; /* number we have left */
unsigned char oiv[16]; /* original iv EVP_MAX_IV_LENGTH */
unsigned char iv[16]; /* working iv EVP_MAX_IV_LENGTH */
unsigned char buf[32]; /* saved partial block EVP_MAX_BLOCK_LENGTH */
int num; /* used by cfb/ofb/ctr mode */
void *app_data; /* application stuff */
int key_len; /* May change for variable length cipher */
unsigned long flags; /* Various flags */
void *cipher_data; /* per EVP data */
int final_used;
int block_mask;
unsigned char final[32]; /* possible final block EVP_MAX_BLOCK_LENGTH */
} /* EVP_CIPHER_CTX */ ;
]]
end

View File

@ -0,0 +1,148 @@
local ffi = require "ffi"
local ffi_cast = ffi.cast
local C = ffi.C
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.evp.md"
local evp = require("resty.openssl.include.evp")
local ctypes = require "resty.openssl.auxiliary.ctypes"
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local BORINGSSL = require("resty.openssl.version").BORINGSSL
local void_ptr = ctypes.void_ptr
local _M = {
EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND = 0,
EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY = 1,
EVP_PKEY_HKDEF_MODE_EXPAND_ONLY = 2,
}
if OPENSSL_3X then
require "resty.openssl.include.provider"
ffi.cdef [[
const OSSL_PROVIDER *EVP_KDF_get0_provider(const EVP_KDF *kdf);
typedef void* fake_openssl_kdf_provided_list_fn(EVP_KDF *kdf, void *arg);
void EVP_KDF_do_all_provided(OSSL_LIB_CTX *libctx,
fake_openssl_kdf_provided_list_fn*,
void *arg);
int EVP_KDF_up_ref(EVP_KDF *kdf);
void EVP_KDF_free(EVP_KDF *kdf);
const char *EVP_KDF_get0_name(const EVP_KDF *kdf);
EVP_KDF *EVP_KDF_fetch(OSSL_LIB_CTX *libctx, const char *algorithm,
const char *properties);
EVP_KDF_CTX *EVP_KDF_CTX_new(const EVP_KDF *kdf);
void EVP_KDF_CTX_free(EVP_KDF_CTX *ctx);
void EVP_KDF_CTX_reset(EVP_KDF_CTX *ctx);
size_t EVP_KDF_CTX_get_kdf_size(EVP_KDF_CTX *ctx);
int EVP_KDF_derive(EVP_KDF_CTX *ctx, unsigned char *key, size_t keylen,
const OSSL_PARAM params[]);
int EVP_KDF_CTX_get_params(EVP_KDF_CTX *ctx, OSSL_PARAM params[]);
int EVP_KDF_CTX_set_params(EVP_KDF_CTX *ctx, const OSSL_PARAM params[]);
const OSSL_PARAM *EVP_KDF_CTX_gettable_params(const EVP_KDF_CTX *ctx);
const OSSL_PARAM *EVP_KDF_CTX_settable_params(const EVP_KDF_CTX *ctx);
]]
end
if OPENSSL_3X or BORINGSSL then
ffi.cdef [[
int EVP_PKEY_CTX_set_tls1_prf_md(EVP_PKEY_CTX *ctx, const EVP_MD *md);
int EVP_PKEY_CTX_set1_tls1_prf_secret(EVP_PKEY_CTX *pctx,
const unsigned char *sec, int seclen);
int EVP_PKEY_CTX_add1_tls1_prf_seed(EVP_PKEY_CTX *pctx,
const unsigned char *seed, int seedlen);
int EVP_PKEY_CTX_set_hkdf_md(EVP_PKEY_CTX *ctx, const EVP_MD *md);
int EVP_PKEY_CTX_set1_hkdf_salt(EVP_PKEY_CTX *ctx,
const unsigned char *salt, int saltlen);
int EVP_PKEY_CTX_set1_hkdf_key(EVP_PKEY_CTX *ctx,
const unsigned char *key, int keylen);
int EVP_PKEY_CTX_set_hkdf_mode(EVP_PKEY_CTX *ctx, int mode);
int EVP_PKEY_CTX_add1_hkdf_info(EVP_PKEY_CTX *ctx,
const unsigned char *info, int infolen);
]]
_M.EVP_PKEY_CTX_set_tls1_prf_md = function(pctx, md)
return C.EVP_PKEY_CTX_set_tls1_prf_md(pctx, md)
end
_M.EVP_PKEY_CTX_set1_tls1_prf_secret = function(pctx, sec)
return C.EVP_PKEY_CTX_set1_tls1_prf_secret(pctx, sec, #sec)
end
_M.EVP_PKEY_CTX_add1_tls1_prf_seed = function(pctx, seed)
return C.EVP_PKEY_CTX_add1_tls1_prf_seed(pctx, seed, #seed)
end
_M.EVP_PKEY_CTX_set_hkdf_md = function(pctx, md)
return C.EVP_PKEY_CTX_set_hkdf_md(pctx, md)
end
_M.EVP_PKEY_CTX_set1_hkdf_salt = function(pctx, salt)
return C.EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, #salt)
end
_M.EVP_PKEY_CTX_set1_hkdf_key = function(pctx, key)
return C.EVP_PKEY_CTX_set1_hkdf_key(pctx, key, #key)
end
_M.EVP_PKEY_CTX_set_hkdf_mode = function(pctx, mode)
return C.EVP_PKEY_CTX_set_hkdf_mode(pctx, mode)
end
_M.EVP_PKEY_CTX_add1_hkdf_info = function(pctx, info)
return C.EVP_PKEY_CTX_add1_hkdf_info(pctx, info, #info)
end
else
_M.EVP_PKEY_CTX_set_tls1_prf_md = function(pctx, md)
return C.EVP_PKEY_CTX_ctrl(pctx, -1,
evp.EVP_PKEY_OP_DERIVE,
evp.EVP_PKEY_CTRL_TLS_MD,
0, ffi_cast(void_ptr, md))
end
_M.EVP_PKEY_CTX_set1_tls1_prf_secret = function(pctx, sec)
return C.EVP_PKEY_CTX_ctrl(pctx, -1,
evp.EVP_PKEY_OP_DERIVE,
evp.EVP_PKEY_CTRL_TLS_SECRET,
#sec, ffi_cast(void_ptr, sec))
end
_M.EVP_PKEY_CTX_add1_tls1_prf_seed = function(pctx, seed)
return C.EVP_PKEY_CTX_ctrl(pctx, -1,
evp.EVP_PKEY_OP_DERIVE,
evp.EVP_PKEY_CTRL_TLS_SEED,
#seed, ffi_cast(void_ptr, seed))
end
_M.EVP_PKEY_CTX_set_hkdf_md = function(pctx, md)
return C.EVP_PKEY_CTX_ctrl(pctx, -1,
evp.EVP_PKEY_OP_DERIVE,
evp.EVP_PKEY_CTRL_HKDF_MD,
0, ffi_cast(void_ptr, md))
end
_M.EVP_PKEY_CTX_set1_hkdf_salt = function(pctx, salt)
return C.EVP_PKEY_CTX_ctrl(pctx, -1,
evp.EVP_PKEY_OP_DERIVE,
evp.EVP_PKEY_CTRL_HKDF_SALT,
#salt, ffi_cast(void_ptr, salt))
end
_M.EVP_PKEY_CTX_set1_hkdf_key = function(pctx, key)
return C.EVP_PKEY_CTX_ctrl(pctx, -1,
evp.EVP_PKEY_OP_DERIVE,
evp.EVP_PKEY_CTRL_HKDF_KEY,
#key, ffi_cast(void_ptr, key))
end
_M.EVP_PKEY_CTX_set_hkdf_mode = function(pctx, mode)
return C.EVP_PKEY_CTX_ctrl(pctx, -1,
evp.EVP_PKEY_OP_DERIVE,
evp.EVP_PKEY_CTRL_HKDF_MODE,
mode, nil)
end
_M.EVP_PKEY_CTX_add1_hkdf_info = function(pctx, info)
return C.EVP_PKEY_CTX_ctrl(pctx, -1,
evp.EVP_PKEY_OP_DERIVE,
evp.EVP_PKEY_CTRL_HKDF_INFO,
#info, ffi_cast(void_ptr, info))
end
end
return _M

View File

@ -0,0 +1,38 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.provider"
ffi.cdef [[
typedef struct evp_mac_st EVP_MAC;
typedef struct evp_mac_ctx_st EVP_MAC_CTX;
EVP_MAC_CTX *EVP_MAC_CTX_new(EVP_MAC *mac);
void EVP_MAC_CTX_free(EVP_MAC_CTX *ctx);
const OSSL_PROVIDER *EVP_MAC_get0_provider(const EVP_MAC *mac);
EVP_MAC *EVP_MAC_fetch(OSSL_LIB_CTX *libctx, const char *algorithm,
const char *properties);
int EVP_MAC_init(EVP_MAC_CTX *ctx, const unsigned char *key, size_t keylen,
const OSSL_PARAM params[]);
int EVP_MAC_update(EVP_MAC_CTX *ctx, const unsigned char *data, size_t datalen);
int EVP_MAC_final(EVP_MAC_CTX *ctx,
unsigned char *out, size_t *outl, size_t outsize);
size_t EVP_MAC_CTX_get_mac_size(EVP_MAC_CTX *ctx);
typedef void* fake_openssl_mac_provided_list_fn(EVP_MAC *mac, void *arg);
void EVP_MAC_do_all_provided(OSSL_LIB_CTX *libctx,
fake_openssl_mac_provided_list_fn*,
void *arg);
int EVP_MAC_up_ref(EVP_MAC *mac);
void EVP_MAC_free(EVP_MAC *mac);
const char *EVP_MAC_get0_name(const EVP_MAC *mac);
int EVP_MAC_CTX_set_params(EVP_MAC_CTX *ctx, const OSSL_PARAM params[]);
const OSSL_PARAM *EVP_MAC_CTX_settable_params(EVP_MAC_CTX *ctx);
int EVP_MAC_CTX_get_params(EVP_MAC_CTX *ctx, OSSL_PARAM params[]);
const OSSL_PARAM *EVP_MAC_CTX_gettable_params(EVP_MAC_CTX *ctx);
]]

View File

@ -0,0 +1,86 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
ffi.cdef [[
int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type,
ENGINE *impl);
int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d,
size_t cnt);
int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md,
unsigned int *s);
const EVP_MD *EVP_get_digestbyname(const char *name);
int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d,
size_t cnt);
int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md,
unsigned int *s);
const EVP_MD *EVP_md_null(void);
// openssl < 3.0
int EVP_MD_size(const EVP_MD *md);
int EVP_MD_type(const EVP_MD *md);
typedef void* fake_openssl_md_list_fn(const EVP_MD *ciph, const char *from,
const char *to, void *x);
void EVP_MD_do_all_sorted(fake_openssl_md_list_fn*, void *arg);
const EVP_MD *EVP_get_digestbyname(const char *name);
]]
if OPENSSL_3X then
require "resty.openssl.include.provider"
ffi.cdef [[
int EVP_MD_get_size(const EVP_MD *md);
int EVP_MD_get_type(const EVP_MD *md);
const OSSL_PROVIDER *EVP_MD_get0_provider(const EVP_MD *md);
EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
const char *properties);
typedef void* fake_openssl_md_provided_list_fn(EVP_MD *md, void *arg);
void EVP_MD_do_all_provided(OSSL_LIB_CTX *libctx,
fake_openssl_md_provided_list_fn*,
void *arg);
int EVP_MD_up_ref(EVP_MD *md);
void EVP_MD_free(EVP_MD *md);
const char *EVP_MD_get0_name(const EVP_MD *md);
int EVP_MD_CTX_set_params(EVP_MD_CTX *ctx, const OSSL_PARAM params[]);
const OSSL_PARAM *EVP_MD_CTX_settable_params(EVP_MD_CTX *ctx);
int EVP_MD_CTX_get_params(EVP_MD_CTX *ctx, OSSL_PARAM params[]);
const OSSL_PARAM *EVP_MD_CTX_gettable_params(EVP_MD_CTX *ctx);
]]
end
if OPENSSL_11_OR_LATER then
ffi.cdef [[
EVP_MD_CTX *EVP_MD_CTX_new(void);
void EVP_MD_CTX_free(EVP_MD_CTX *ctx);
]]
elseif OPENSSL_10 then
ffi.cdef [[
EVP_MD_CTX *EVP_MD_CTX_create(void);
void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx);
// crypto/evp/evp.h
// only needed for openssl 1.0.x where initializer for HMAC_CTX is not avaiable
// HACK: renamed from env_md_ctx_st to evp_md_ctx_st to match typedef (lazily)
// it's an internal struct thus name is not exported so we will be fine
struct evp_md_ctx_st {
const EVP_MD *digest;
ENGINE *engine; /* functional reference if 'digest' is
* ENGINE-provided */
unsigned long flags;
void *md_data;
/* Public key context for sign/verify */
EVP_PKEY_CTX *pctx;
/* Update function: usually copied from EVP_MD */
int (*update) (EVP_MD_CTX *ctx, const void *data, size_t count);
} /* EVP_MD_CTX */ ;
]]
end

View File

@ -0,0 +1,234 @@
local ffi = require "ffi"
local C = ffi.C
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.evp.md"
local evp = require("resty.openssl.include.evp")
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local BORINGSSL = require("resty.openssl.version").BORINGSSL
ffi.cdef [[
EVP_PKEY *EVP_PKEY_new(void);
void EVP_PKEY_free(EVP_PKEY *pkey);
RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey);
EC_KEY *EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey);
DH *EVP_PKEY_get0_DH(EVP_PKEY *pkey);
int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key);
// openssl < 3.0
int EVP_PKEY_base_id(const EVP_PKEY *pkey);
int EVP_PKEY_size(const EVP_PKEY *pkey);
EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e);
EVP_PKEY_CTX *EVP_PKEY_CTX_new_id(int id, ENGINE *e);
void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx);
int EVP_PKEY_CTX_ctrl(EVP_PKEY_CTX *ctx, int keytype, int optype,
int cmd, int p1, void *p2);
// TODO replace EVP_PKEY_CTX_ctrl with EVP_PKEY_CTX_ctrl_str to reduce
// some hardcoded macros
int EVP_PKEY_CTX_ctrl_str(EVP_PKEY_CTX *ctx, const char *type,
const char *value);
int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx,
unsigned char *out, size_t *outlen,
const unsigned char *in, size_t inlen);
int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx,
unsigned char *out, size_t *outlen,
const unsigned char *in, size_t inlen);
int EVP_PKEY_sign_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_sign(EVP_PKEY_CTX *ctx,
unsigned char *sig, size_t *siglen,
const unsigned char *tbs, size_t tbslen);
int EVP_PKEY_verify_recover_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_verify_recover(EVP_PKEY_CTX *ctx,
unsigned char *rout, size_t *routlen,
const unsigned char *sig, size_t siglen);
EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *e,
const unsigned char *key, size_t keylen);
EVP_PKEY *EVP_PKEY_new_raw_public_key(int type, ENGINE *e,
const unsigned char *key, size_t keylen);
int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey, unsigned char *priv,
size_t *len);
int EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub,
size_t *len);
int EVP_SignFinal(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s,
EVP_PKEY *pkey);
int EVP_VerifyFinal(EVP_MD_CTX *ctx, const unsigned char *sigbuf,
unsigned int siglen, EVP_PKEY *pkey);
int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey);
int EVP_DigestSign(EVP_MD_CTX *ctx, unsigned char *sigret,
size_t *siglen, const unsigned char *tbs,
size_t tbslen);
int EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey);
int EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret,
size_t siglen, const unsigned char *tbs, size_t tbslen);
int EVP_PKEY_get_default_digest_nid(EVP_PKEY *pkey, int *pnid);
int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer);
int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen);
int EVP_PKEY_keygen_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey);
int EVP_PKEY_paramgen_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey);
]]
if OPENSSL_3X then
require "resty.openssl.include.provider"
ffi.cdef [[
int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad_mode);
int EVP_PKEY_get_base_id(const EVP_PKEY *pkey);
int EVP_PKEY_get_size(const EVP_PKEY *pkey);
const OSSL_PROVIDER *EVP_PKEY_get0_provider(const EVP_PKEY *key);
const OSSL_PROVIDER *EVP_PKEY_CTX_get0_provider(const EVP_PKEY_CTX *ctx);
const OSSL_PARAM *EVP_PKEY_settable_params(const EVP_PKEY *pkey);
int EVP_PKEY_set_params(EVP_PKEY *pkey, OSSL_PARAM params[]);
int EVP_PKEY_get_params(EVP_PKEY *ctx, OSSL_PARAM params[]);
const OSSL_PARAM *EVP_PKEY_gettable_params(EVP_PKEY *ctx);
]]
end
if OPENSSL_10 then
ffi.cdef [[
// crypto/evp/evp.h
// only needed for openssl 1.0.x where getters are not available
// needed to get key to extract parameters
// Note: this struct is trimmed
struct evp_pkey_st {
int type;
int save_type;
const EVP_PKEY_ASN1_METHOD *ameth;
ENGINE *engine;
ENGINE *pmeth_engine;
union {
void *ptr;
struct rsa_st *rsa;
struct dsa_st *dsa;
struct dh_st *dh;
struct ec_key_st *ec;
} pkey;
// trimmed
// CRYPTO_REF_COUNT references;
// CRYPTO_RWLOCK *lock;
// STACK_OF(X509_ATTRIBUTE) *attributes;
// int save_parameters;
// struct {
// EVP_KEYMGMT *keymgmt;
// void *provkey;
// } pkeys[10];
// size_t dirty_cnt_copy;
};
]]
end
local _M = {}
if OPENSSL_3X or BORINGSSL then
ffi.cdef [[
int EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid);
int EVP_PKEY_CTX_set_ec_param_enc(EVP_PKEY_CTX *ctx, int param_enc);
int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX *ctx, int mbits);
int EVP_PKEY_CTX_set_rsa_keygen_pubexp(EVP_PKEY_CTX *ctx, BIGNUM *pubexp);
int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad);
int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int len);
int EVP_PKEY_CTX_set_dh_paramgen_prime_len(EVP_PKEY_CTX *ctx, int pbits);
]]
_M.EVP_PKEY_CTX_set_ec_paramgen_curve_nid = function(pctx, nid)
return C.EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid)
end
_M.EVP_PKEY_CTX_set_ec_param_enc = function(pctx, param_enc)
return C.EVP_PKEY_CTX_set_ec_param_enc(pctx, param_enc)
end
_M.EVP_PKEY_CTX_set_rsa_keygen_bits = function(pctx, mbits)
return C.EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, mbits)
end
_M.EVP_PKEY_CTX_set_rsa_keygen_pubexp = function(pctx, pubexp)
return C.EVP_PKEY_CTX_set_rsa_keygen_pubexp(pctx, pubexp)
end
_M.EVP_PKEY_CTX_set_rsa_padding = function(pctx, pad)
return C.EVP_PKEY_CTX_set_rsa_padding(pctx, pad)
end
_M.EVP_PKEY_CTX_set_rsa_pss_saltlen = function(pctx, len)
return C.EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, len)
end
_M.EVP_PKEY_CTX_set_dh_paramgen_prime_len = function(pctx, pbits)
return C.EVP_PKEY_CTX_set_dh_paramgen_prime_len(pctx, pbits)
end
else
_M.EVP_PKEY_CTX_set_ec_paramgen_curve_nid = function(pctx, nid)
return C.EVP_PKEY_CTX_ctrl(pctx,
evp.EVP_PKEY_EC,
evp.EVP_PKEY_OP_PARAMGEN + evp.EVP_PKEY_OP_KEYGEN,
evp.EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID,
nid, nil)
end
_M.EVP_PKEY_CTX_set_ec_param_enc = function(pctx, param_enc)
return C.EVP_PKEY_CTX_ctrl(pctx,
evp.EVP_PKEY_EC,
evp.EVP_PKEY_OP_PARAMGEN + evp.EVP_PKEY_OP_KEYGEN,
evp.EVP_PKEY_CTRL_EC_PARAM_ENC,
param_enc, nil)
end
_M.EVP_PKEY_CTX_set_rsa_keygen_bits = function(pctx, mbits)
return C.EVP_PKEY_CTX_ctrl(pctx,
evp.EVP_PKEY_RSA,
evp.EVP_PKEY_OP_KEYGEN,
evp.EVP_PKEY_CTRL_RSA_KEYGEN_BITS,
mbits, nil)
end
_M.EVP_PKEY_CTX_set_rsa_keygen_pubexp = function(pctx, pubexp)
return C.EVP_PKEY_CTX_ctrl(pctx,
evp.EVP_PKEY_RSA, evp.EVP_PKEY_OP_KEYGEN,
evp.EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP,
0, pubexp)
end
_M.EVP_PKEY_CTX_set_rsa_padding = function(pctx, pad)
return C.EVP_PKEY_CTX_ctrl(pctx,
evp.EVP_PKEY_RSA,
-1,
evp.EVP_PKEY_CTRL_RSA_PADDING,
pad, nil)
end
_M.EVP_PKEY_CTX_set_rsa_pss_saltlen = function(pctx, len)
return C.EVP_PKEY_CTX_ctrl(pctx,
evp.EVP_PKEY_RSA,
evp.EVP_PKEY_OP_SIGN + evp.EVP_PKEY_OP_VERIFY,
evp.EVP_PKEY_CTRL_RSA_PSS_SALTLEN,
len, nil)
end
_M.EVP_PKEY_CTX_set_dh_paramgen_prime_len = function(pctx, pbits)
return C.EVP_PKEY_CTX_ctrl(pctx,
evp.EVP_PKEY_DH, evp.EVP_PKEY_OP_PARAMGEN,
evp.EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN,
pbits, nil)
end
end
return _M

View File

@ -0,0 +1,48 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.evp"
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local BORINGSSL = require("resty.openssl.version").BORINGSSL
if BORINGSSL then
ffi.cdef [[
int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, size_t key_len,
const EVP_MD *md, ENGINE *impl);
]]
else
ffi.cdef [[
int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int len,
const EVP_MD *md, ENGINE *impl);
]]
end
ffi.cdef [[
int HMAC_Update(HMAC_CTX *ctx, const unsigned char *data,
size_t len);
int HMAC_Final(HMAC_CTX *ctx, unsigned char *md,
unsigned int *len);
]]
if OPENSSL_11_OR_LATER then
ffi.cdef [[
HMAC_CTX *HMAC_CTX_new(void);
void HMAC_CTX_free(HMAC_CTX *ctx);
]]
elseif OPENSSL_10 then
ffi.cdef [[
// # define HMAC_MAX_MD_CBLOCK 128/* largest known is SHA512 */
struct hmac_ctx_st {
const EVP_MD *md;
EVP_MD_CTX md_ctx;
EVP_MD_CTX i_ctx;
EVP_MD_CTX o_ctx;
unsigned int key_length;
unsigned char key[128];
};
void HMAC_CTX_init(HMAC_CTX *ctx);
void HMAC_CTX_cleanup(HMAC_CTX *ctx);
]]
end

View File

@ -0,0 +1,19 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
ffi.cdef [[
int OBJ_obj2txt(char *buf, int buf_len, const ASN1_OBJECT *a, int no_name);
ASN1_OBJECT *OBJ_txt2obj(const char *s, int no_name);
int OBJ_txt2nid(const char *s);
const char *OBJ_nid2sn(int n);
int OBJ_ln2nid(const char *s);
int OBJ_sn2nid(const char *s);
const char *OBJ_nid2ln(int n);
const char *OBJ_nid2sn(int n);
int OBJ_obj2nid(const ASN1_OBJECT *o);
const ASN1_OBJECT *OBJ_nid2obj(int n);
int OBJ_create(const char *oid, const char *sn, const char *ln);
int OBJ_find_sigid_algs(int signid, int *pdig_nid, int *ppkey_nid);
]]

View File

@ -0,0 +1,72 @@
local ffi = require "ffi"
ffi.cdef(
[[
typedef struct rsa_st RSA;
typedef struct evp_pkey_st EVP_PKEY;
typedef struct bignum_st BIGNUM;
typedef struct bn_gencb_st BN_GENCB;
typedef struct bignum_ctx BN_CTX;
typedef struct bio_st BIO;
typedef struct evp_cipher_st EVP_CIPHER;
typedef struct evp_md_ctx_st EVP_MD_CTX;
typedef struct evp_pkey_ctx_st EVP_PKEY_CTX;
typedef struct evp_md_st EVP_MD;
typedef struct evp_pkey_asn1_method_st EVP_PKEY_ASN1_METHOD;
typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
typedef struct engine_st ENGINE;
typedef struct x509_st X509;
typedef struct x509_attributes_st X509_ATTRIBUTE;
typedef struct X509_extension_st X509_EXTENSION;
typedef struct X509_name_st X509_NAME;
typedef struct X509_name_entry_st X509_NAME_ENTRY;
typedef struct X509_req_st X509_REQ;
typedef struct X509_crl_st X509_CRL;
typedef struct x509_store_st X509_STORE;
typedef struct x509_store_ctx_st X509_STORE_CTX;
typedef struct x509_purpose_st X509_PURPOSE;
typedef struct v3_ext_ctx X509V3_CTX;
typedef struct asn1_string_st ASN1_INTEGER;
typedef struct asn1_string_st ASN1_ENUMERATED;
typedef struct asn1_string_st ASN1_BIT_STRING;
typedef struct asn1_string_st ASN1_OCTET_STRING;
typedef struct asn1_string_st ASN1_PRINTABLESTRING;
typedef struct asn1_string_st ASN1_T61STRING;
typedef struct asn1_string_st ASN1_IA5STRING;
typedef struct asn1_string_st ASN1_GENERALSTRING;
typedef struct asn1_string_st ASN1_UNIVERSALSTRING;
typedef struct asn1_string_st ASN1_BMPSTRING;
typedef struct asn1_string_st ASN1_UTCTIME;
typedef struct asn1_string_st ASN1_TIME;
typedef struct asn1_string_st ASN1_GENERALIZEDTIME;
typedef struct asn1_string_st ASN1_VISIBLESTRING;
typedef struct asn1_string_st ASN1_UTF8STRING;
typedef struct asn1_string_st ASN1_STRING;
typedef struct asn1_object_st ASN1_OBJECT;
typedef struct conf_st CONF;
typedef struct conf_method_st CONF_METHOD;
typedef int ASN1_BOOLEAN;
typedef int ASN1_NULL;
typedef struct ec_key_st EC_KEY;
typedef struct ec_method_st EC_METHOD;
typedef struct ec_point_st EC_POINT;
typedef struct ec_group_st EC_GROUP;
typedef struct rsa_meth_st RSA_METHOD;
// typedef struct evp_keymgmt_st EVP_KEYMGMT;
// typedef struct crypto_ex_data_st CRYPTO_EX_DATA;
// typedef struct bn_mont_ctx_st BN_MONT_CTX;
// typedef struct bn_blinding_st BN_BLINDING;
// crypto.h
// typedef void CRYPTO_RWLOCK;
typedef struct hmac_ctx_st HMAC_CTX;
typedef struct x509_revoked_st X509_REVOKED;
typedef struct dh_st DH;
typedef struct PKCS12_st PKCS12;
typedef struct ssl_st SSL;
typedef struct ssl_ctx_st SSL_CTX;
typedef struct evp_kdf_st EVP_KDF;
typedef struct evp_kdf_ctx_st EVP_KDF_CTX;
typedef struct ossl_lib_ctx_st OSSL_LIB_CTX;
typedef struct ECDSA_SIG_st ECDSA_SIG;
]])

View File

@ -0,0 +1,71 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
ffi.cdef [[
typedef struct ossl_param_st {
const char *key; /* the name of the parameter */
unsigned int data_type; /* declare what kind of content is in buffer */
void *data; /* value being passed in or out */
size_t data_size; /* data size */
size_t return_size; /* returned content size */
} OSSL_PARAM;
OSSL_PARAM OSSL_PARAM_construct_int(const char *key, int *buf);
OSSL_PARAM OSSL_PARAM_construct_uint(const char *key, unsigned int *buf);
OSSL_PARAM OSSL_PARAM_construct_BN(const char *key, unsigned char *buf,
size_t bsize);
OSSL_PARAM OSSL_PARAM_construct_double(const char *key, double *buf);
OSSL_PARAM OSSL_PARAM_construct_utf8_string(const char *key, char *buf,
size_t bsize);
OSSL_PARAM OSSL_PARAM_construct_octet_string(const char *key, void *buf,
size_t bsize);
OSSL_PARAM OSSL_PARAM_construct_utf8_ptr(const char *key, char **buf,
size_t bsize);
OSSL_PARAM OSSL_PARAM_construct_octet_ptr(const char *key, void **buf,
size_t bsize);
OSSL_PARAM OSSL_PARAM_construct_end(void);
int OSSL_PARAM_get_int32(const OSSL_PARAM *p, int32_t *val);
int OSSL_PARAM_get_uint32(const OSSL_PARAM *p, uint32_t *val);
int OSSL_PARAM_get_int64(const OSSL_PARAM *p, int64_t *val);
int OSSL_PARAM_get_uint64(const OSSL_PARAM *p, uint64_t *val);
// int OSSL_PARAM_get_size_t(const OSSL_PARAM *p, size_t *val);
// int OSSL_PARAM_get_time_t(const OSSL_PARAM *p, time_t *val);
int OSSL_PARAM_set_int(OSSL_PARAM *p, int val);
int OSSL_PARAM_set_uint(OSSL_PARAM *p, unsigned int val);
int OSSL_PARAM_set_long(OSSL_PARAM *p, long int val);
int OSSL_PARAM_set_ulong(OSSL_PARAM *p, unsigned long int val);
int OSSL_PARAM_set_int32(OSSL_PARAM *p, int32_t val);
int OSSL_PARAM_set_uint32(OSSL_PARAM *p, uint32_t val);
int OSSL_PARAM_set_int64(OSSL_PARAM *p, int64_t val);
int OSSL_PARAM_set_uint64(OSSL_PARAM *p, uint64_t val);
// int OSSL_PARAM_set_size_t(OSSL_PARAM *p, size_t val);
// int OSSL_PARAM_set_time_t(OSSL_PARAM *p, time_t val);
int OSSL_PARAM_get_double(const OSSL_PARAM *p, double *val);
int OSSL_PARAM_set_double(OSSL_PARAM *p, double val);
int OSSL_PARAM_get_BN(const OSSL_PARAM *p, BIGNUM **val);
int OSSL_PARAM_set_BN(OSSL_PARAM *p, const BIGNUM *val);
int OSSL_PARAM_get_utf8_string(const OSSL_PARAM *p, char **val, size_t max_len);
int OSSL_PARAM_set_utf8_string(OSSL_PARAM *p, const char *val);
int OSSL_PARAM_get_octet_string(const OSSL_PARAM *p, void **val, size_t max_len,
size_t *used_len);
int OSSL_PARAM_set_octet_string(OSSL_PARAM *p, const void *val, size_t len);
int OSSL_PARAM_get_utf8_ptr(const OSSL_PARAM *p, const char **val);
int OSSL_PARAM_set_utf8_ptr(OSSL_PARAM *p, const char *val);
int OSSL_PARAM_get_octet_ptr(const OSSL_PARAM *p, const void **val,
size_t *used_len);
int OSSL_PARAM_set_octet_ptr(OSSL_PARAM *p, const void *val,
size_t used_len);
int OSSL_PARAM_get_utf8_string_ptr(const OSSL_PARAM *p, const char **val);
int OSSL_PARAM_get_octet_string_ptr(const OSSL_PARAM *p, const void **val,
size_t *used_len);
]]

View File

@ -0,0 +1,50 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
ffi.cdef [[
// all pem_password_cb* has been modified to pem_password_cb to avoid a table overflow issue
typedef int (*pem_password_cb)(char *buf, int size, int rwflag, void *userdata);
EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x,
// the following signature has been modified to avoid ffi.cast
pem_password_cb cb, const char *u);
// pem_password_cb *cb, void *u);
EVP_PKEY *PEM_read_bio_PUBKEY(BIO *bp, EVP_PKEY **x,
// the following signature has been modified to avoid ffi.cast
pem_password_cb cb, const char *u);
// pem_password_cb *cb, void *u);
int PEM_write_bio_PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
unsigned char *kstr, int klen,
pem_password_cb *cb, void *u);
int PEM_write_bio_PUBKEY(BIO *bp, EVP_PKEY *x);
RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **x,
// the following signature has been modified to avoid ffi.cast
pem_password_cb cb, const char *u);
// pem_password_cb *cb, void *u);
RSA *PEM_read_bio_RSAPublicKey(BIO *bp, RSA **x,
// the following signature has been modified to avoid ffi.cast
pem_password_cb cb, const char *u);
// pem_password_cb *cb, void *u);
int PEM_write_bio_RSAPrivateKey(BIO *bp, RSA *x, const EVP_CIPHER *enc,
unsigned char *kstr, int klen,
pem_password_cb *cb, void *u);
int PEM_write_bio_RSAPublicKey(BIO *bp, RSA *x);
X509_REQ *PEM_read_bio_X509_REQ(BIO *bp, X509_REQ **x, pem_password_cb cb, void *u);
int PEM_write_bio_X509_REQ(BIO *bp, X509_REQ *x);
X509_CRL *PEM_read_bio_X509_CRL(BIO *bp, X509_CRL **x, pem_password_cb cb, void *u);
int PEM_write_bio_X509_CRL(BIO *bp, X509_CRL *x);
X509 *PEM_read_bio_X509(BIO *bp, X509 **x, pem_password_cb cb, void *u);
int PEM_write_bio_X509(BIO *bp, X509 *x);
DH *PEM_read_bio_DHparams(BIO *bp, DH **x, pem_password_cb cb, void *u);
int PEM_write_bio_DHparams(BIO *bp, DH *x);
EC_GROUP *PEM_read_bio_ECPKParameters(BIO *bp, EC_GROUP **x, pem_password_cb cb, void *u);
int PEM_write_bio_ECPKParameters(BIO *bp, const EC_GROUP *x);
]]

View File

@ -0,0 +1,31 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.stack"
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
ffi.cdef [[
// hack by changing char* to const char* here
PKCS12 *PKCS12_create(const char *pass, const char *name, EVP_PKEY *pkey, X509 *cert,
OPENSSL_STACK *ca, // STACK_OF(X509)
int nid_key, int nid_cert, int iter, int mac_iter, int keytype);
int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert,
OPENSSL_STACK **ca); // STACK_OF(X509) **ca);
void PKCS12_free(PKCS12 *p12);
int i2d_PKCS12_bio(BIO *bp, PKCS12 *a);
PKCS12 *d2i_PKCS12_bio(BIO *bp, PKCS12 **a);
]]
if OPENSSL_3X then
ffi.cdef [[
PKCS12 *PKCS12_create_ex(const char *pass, const char *name, EVP_PKEY *pkey,
X509 *cert,
OPENSSL_STACK *ca, // STACK_OF(X509)
int nid_key, int nid_cert,
int iter, int mac_iter, int keytype,
OSSL_LIB_CTX *ctx, const char *propq);
]]
end

View File

@ -0,0 +1,27 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.param"
ffi.cdef [[
typedef struct ossl_provider_st OSSL_PROVIDER;
typedef struct ossl_lib_ctx_st OSSL_LIB_CTX;
void OSSL_PROVIDER_set_default_search_path(OSSL_LIB_CTX *libctx,
const char *path);
OSSL_PROVIDER *OSSL_PROVIDER_load(OSSL_LIB_CTX *libctx, const char *name);
OSSL_PROVIDER *OSSL_PROVIDER_try_load(OSSL_LIB_CTX *libctx, const char *name);
int OSSL_PROVIDER_unload(OSSL_PROVIDER *prov);
int OSSL_PROVIDER_available(OSSL_LIB_CTX *libctx, const char *name);
const OSSL_PARAM *OSSL_PROVIDER_gettable_params(OSSL_PROVIDER *prov);
int OSSL_PROVIDER_get_params(OSSL_PROVIDER *prov, OSSL_PARAM params[]);
// int OSSL_PROVIDER_add_builtin(OSSL_LIB_CTX *libctx, const char *name,
// ossl_provider_init_fn *init_fn);
const char *OSSL_PROVIDER_get0_name(const OSSL_PROVIDER *prov);
int OSSL_PROVIDER_self_test(const OSSL_PROVIDER *prov);
]]

View File

@ -0,0 +1,24 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local BORINGSSL = require("resty.openssl.version").BORINGSSL
if BORINGSSL then
ffi.cdef [[
int RAND_bytes(uint8_t *buf, size_t num);
int RAND_priv_bytes(uint8_t *buf, size_t num);
]]
elseif OPENSSL_3X then
ffi.cdef [[
int RAND_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
unsigned int strength);
int RAND_priv_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
unsigned int strength);
]]
else
ffi.cdef [[
int RAND_bytes(unsigned char *buf, int num);
int RAND_priv_bytes(unsigned char *buf, int num);
]]
end

View File

@ -0,0 +1,70 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
ffi.cdef [[
RSA *RSA_new(void);
void RSA_free(RSA *r);
]]
if OPENSSL_11_OR_LATER then
ffi.cdef [[
void RSA_get0_key(const RSA *r,
const BIGNUM **n, const BIGNUM **e, const BIGNUM **d);
void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q);
void RSA_get0_crt_params(const RSA *r,
const BIGNUM **dmp1, const BIGNUM **dmq1,
const BIGNUM **iqmp);
int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d);
int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q);
int RSA_set0_crt_params(RSA *r,BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp);
struct rsa_st;
]]
elseif OPENSSL_10 then
ffi.cdef [[
// crypto/rsa/rsa_locl.h
// needed to extract parameters
// Note: this struct is trimmed
struct rsa_st {
int pad;
// the following has been changed in OpenSSL 1.1.x to int32_t
long version;
const RSA_METHOD *meth;
ENGINE *engine;
BIGNUM *n;
BIGNUM *e;
BIGNUM *d;
BIGNUM *p;
BIGNUM *q;
BIGNUM *dmp1;
BIGNUM *dmq1;
BIGNUM *iqmp;
// trimmed
// CRYPTO_EX_DATA ex_data;
// int references;
// int flags;
// BN_MONT_CTX *_method_mod_n;
// BN_MONT_CTX *_method_mod_p;
// BN_MONT_CTX *_method_mod_q;
// char *bignum_data;
// BN_BLINDING *blinding;
// BN_BLINDING *mt_blinding;
};
]]
end
return {
paddings = {
RSA_PKCS1_PADDING = 1,
RSA_SSLV23_PADDING = 2,
RSA_NO_PADDING = 3,
RSA_PKCS1_OAEP_PADDING = 4,
RSA_X931_PADDING = 5,
RSA_PKCS1_PSS_PADDING = 6,
},
}

View File

@ -0,0 +1,113 @@
local ffi = require "ffi"
local C = ffi.C
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.stack"
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local BORINGSSL = require("resty.openssl.version").BORINGSSL
ffi.cdef [[
// SSL_METHOD
typedef struct ssl_method_st SSL_METHOD;
const SSL_METHOD *TLS_method(void);
const SSL_METHOD *TLS_server_method(void);
// SSL_CIPHER
typedef struct ssl_cipher_st SSL_CIPHER;
const char *SSL_CIPHER_get_name(const SSL_CIPHER *cipher);
SSL_CIPHER *SSL_get_current_cipher(const SSL *ssl);
SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth);
void SSL_CTX_free(SSL_CTX *a);
// SSL_SESSION
typedef struct ssl_session_st SSL_SESSION;
SSL_SESSION *SSL_get_session(const SSL *ssl);
long SSL_SESSION_set_timeout(SSL_SESSION *s, long t);
long SSL_SESSION_get_timeout(const SSL_SESSION *s);
typedef int (*SSL_CTX_alpn_select_cb_func)(SSL *ssl,
const unsigned char **out,
unsigned char *outlen,
const unsigned char *in,
unsigned int inlen,
void *arg);
void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
SSL_CTX_alpn_select_cb_func cb,
void *arg);
int SSL_select_next_proto(unsigned char **out, unsigned char *outlen,
const unsigned char *server,
unsigned int server_len,
const unsigned char *client,
unsigned int client_len);
SSL *SSL_new(SSL_CTX *ctx);
void SSL_free(SSL *ssl);
int SSL_set_cipher_list(SSL *ssl, const char *str);
int SSL_set_ciphersuites(SSL *s, const char *str);
long SSL_set_options(SSL *ssl, long options);
long SSL_clear_options(SSL *ssl, long options);
long SSL_get_options(SSL *ssl);
/*STACK_OF(SSL_CIPHER)*/ OPENSSL_STACK *SSL_get_ciphers(const SSL *ssl);
/*STACK_OF(SSL_CIPHER)*/ OPENSSL_STACK *SSL_CTX_get_ciphers(const SSL_CTX *ctx);
OPENSSL_STACK *SSL_get_peer_cert_chain(const SSL *ssl);
typedef int (*verify_callback)(int preverify_ok, X509_STORE_CTX *x509_ctx);
void SSL_set_verify(SSL *s, int mode,
int (*verify_callback)(int, X509_STORE_CTX *));
int SSL_add_client_CA(SSL *ssl, X509 *cacert);
long SSL_ctrl(SSL *ssl, int cmd, long larg, void *parg);
]]
if OPENSSL_3X then
ffi.cdef [[
X509 *SSL_get1_peer_certificate(const SSL *ssl);
]]
else
ffi.cdef [[
X509 *SSL_get_peer_certificate(const SSL *ssl);
]]
end
if BORINGSSL then
ffi.cdef [[
int SSL_set_min_proto_version(SSL *ssl, int version);
int SSL_set_max_proto_version(SSL *ssl, int version);
]]
end
local SSL_CTRL_SET_MIN_PROTO_VERSION = 123
local SSL_CTRL_SET_MAX_PROTO_VERSION = 124
local SSL_set_min_proto_version
if BORINGSSL then
SSL_set_min_proto_version = function(ctx, version)
return C.SSL_set_min_proto_version(ctx, version)
end
else
SSL_set_min_proto_version = function(ctx, version)
return C.SSL_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, nil)
end
end
local SSL_set_max_proto_version
if BORINGSSL then
SSL_set_max_proto_version = function(ctx, version)
return C.SSL_set_max_proto_version(ctx, version)
end
else
SSL_set_max_proto_version = function(ctx, version)
return C.SSL_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, nil)
end
end
return {
SSL_set_min_proto_version = SSL_set_min_proto_version,
SSL_set_max_proto_version = SSL_set_max_proto_version,
}

View File

@ -0,0 +1,95 @@
--[[
The OpenSSL stack library. Note `safestack` is not usable here in ffi because
those symbols are eaten after preprocessing.
Instead, we should do a Lua land type checking by having a nested field indicating
which type of cdata its ctx holds.
]]
local ffi = require "ffi"
local C = ffi.C
require "resty.openssl.include.ossl_typ"
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local BORINGSSL = require("resty.openssl.version").BORINGSSL
local _M = {}
ffi.cdef [[
typedef char *OPENSSL_STRING;
]]
if OPENSSL_11_OR_LATER and not BORINGSSL then
ffi.cdef [[
typedef struct stack_st OPENSSL_STACK;
OPENSSL_STACK *OPENSSL_sk_new_null(void);
int OPENSSL_sk_push(OPENSSL_STACK *st, const void *data);
void OPENSSL_sk_pop_free(OPENSSL_STACK *st, void (*func) (void *));
int OPENSSL_sk_num(const OPENSSL_STACK *);
void *OPENSSL_sk_value(const OPENSSL_STACK *, int);
OPENSSL_STACK *OPENSSL_sk_dup(const OPENSSL_STACK *st);
void OPENSSL_sk_free(OPENSSL_STACK *);
void *OPENSSL_sk_delete(OPENSSL_STACK *st, int loc);
typedef void (*OPENSSL_sk_freefunc)(void *);
typedef void *(*OPENSSL_sk_copyfunc)(const void *);
OPENSSL_STACK *OPENSSL_sk_deep_copy(const OPENSSL_STACK *,
OPENSSL_sk_copyfunc c,
OPENSSL_sk_freefunc f);
]]
_M.OPENSSL_sk_pop_free = C.OPENSSL_sk_pop_free
_M.OPENSSL_sk_new_null = C.OPENSSL_sk_new_null
_M.OPENSSL_sk_push = C.OPENSSL_sk_push
_M.OPENSSL_sk_pop_free = C.OPENSSL_sk_pop_free
_M.OPENSSL_sk_num = C.OPENSSL_sk_num
_M.OPENSSL_sk_value = C.OPENSSL_sk_value
_M.OPENSSL_sk_dup = C.OPENSSL_sk_dup
_M.OPENSSL_sk_delete = C.OPENSSL_sk_delete
_M.OPENSSL_sk_free = C.OPENSSL_sk_free
_M.OPENSSL_sk_deep_copy = C.OPENSSL_sk_deep_copy
elseif OPENSSL_10 or BORINGSSL then
ffi.cdef [[
typedef struct stack_st _STACK;
// i made this up
typedef struct stack_st OPENSSL_STACK;
_STACK *sk_new_null(void);
void sk_pop_free(_STACK *st, void (*func) (void *));
_STACK *sk_dup(_STACK *st);
void sk_free(_STACK *st);
_STACK *sk_deep_copy(_STACK *, void *(*)(void *), void (*)(void *));
]]
if BORINGSSL then -- indices are using size_t instead of int
ffi.cdef [[
size_t sk_push(_STACK *st, void *data);
size_t sk_num(const _STACK *);
void *sk_value(const _STACK *, size_t);
void *sk_delete(_STACK *st, size_t loc);
]]
else -- normal OpenSSL 1.0
ffi.cdef [[
int sk_push(_STACK *st, void *data);
int sk_num(const _STACK *);
void *sk_value(const _STACK *, int);
void *sk_delete(_STACK *st, int loc);
]]
end
_M.OPENSSL_sk_pop_free = C.sk_pop_free
_M.OPENSSL_sk_new_null = C.sk_new_null
_M.OPENSSL_sk_push = function(...) return tonumber(C.sk_push(...)) end
_M.OPENSSL_sk_pop_free = C.sk_pop_free
_M.OPENSSL_sk_num = function(...) return tonumber(C.sk_num(...)) end
_M.OPENSSL_sk_value = C.sk_value
_M.OPENSSL_sk_delete = C.sk_delete
_M.OPENSSL_sk_dup = C.sk_dup
_M.OPENSSL_sk_free = C.sk_free
_M.OPENSSL_sk_deep_copy = C.sk_deep_copy
end
return _M

View File

@ -0,0 +1,49 @@
local GEN_OTHERNAME = 0
local GEN_EMAIL = 1
local GEN_DNS = 2
local GEN_X400 = 3
local GEN_DIRNAME = 4
local GEN_EDIPARTY = 5
local GEN_URI = 6
local GEN_IPADD = 7
local GEN_RID = 8
local default_types = {
OtherName = GEN_OTHERNAME, -- otherName
RFC822Name = GEN_EMAIL, -- email
RFC822 = GEN_EMAIL,
Email = GEN_EMAIL,
DNSName = GEN_DNS, -- dns
DNS = GEN_DNS,
X400 = GEN_X400, -- x400
DirName = GEN_DIRNAME, -- dirName
EdiParty = GEN_EDIPARTY, -- EdiParty
UniformResourceIdentifier = GEN_URI, -- uri
URI = GEN_URI,
IPAddress = GEN_IPADD, -- ipaddr
IP = GEN_IPADD,
RID = GEN_RID, -- rid
}
local literals = {
[GEN_OTHERNAME] = "OtherName",
[GEN_EMAIL] = "email",
[GEN_DNS] = "DNS",
[GEN_X400] = "X400",
[GEN_DIRNAME] = "DirName",
[GEN_EDIPARTY] = "EdiParty",
[GEN_URI] = "URI",
[GEN_IPADD] = "IP",
[GEN_RID] = "RID",
}
local types = {}
for t, gid in pairs(default_types) do
types[t:lower()] = gid
types[t] = gid
end
return {
types = types,
literals = literals,
}

View File

@ -0,0 +1,86 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.evp"
require "resty.openssl.include.objects"
require "resty.openssl.include.x509"
require "resty.openssl.include.stack"
local asn1_macro = require "resty.openssl.include.asn1"
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local BORINGSSL_110 = require("resty.openssl.version").BORINGSSL_110
asn1_macro.declare_asn1_functions("X509_CRL", asn1_macro.has_new_ex)
ffi.cdef [[
X509_NAME *X509_CRL_get_issuer(const X509_CRL *crl);
int X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name);
int X509_CRL_set_version(X509_CRL *x, long version);
int X509_CRL_add_ext(X509_CRL *x, X509_EXTENSION *ex, int loc);
X509_EXTENSION *X509_CRL_get_ext(const X509_CRL *x, int loc);
int X509_CRL_get_ext_by_NID(const X509_CRL *x, int nid, int lastpos);
void *X509_CRL_get_ext_d2i(const X509_CRL *x, int nid, int *crit, int *idx);
int X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md);
int X509_CRL_verify(X509_CRL *a, EVP_PKEY *r);
int i2d_X509_CRL_bio(BIO *bp, X509_CRL *crl);
X509_CRL *d2i_X509_CRL_bio(BIO *bp, X509_CRL **crl);
int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev);
int X509_CRL_print(BIO *bio, X509_CRL *crl);
int X509_CRL_get0_by_serial(X509_CRL *crl,
X509_REVOKED **ret, ASN1_INTEGER *serial);
int X509_CRL_get0_by_cert(X509_CRL *crl, X509_REVOKED **ret, X509 *x);
//STACK_OF(X509_REVOKED)
OPENSSL_STACK *X509_CRL_get_REVOKED(X509_CRL *crl);
int X509_CRL_get0_by_serial(X509_CRL *crl,
X509_REVOKED **ret, ASN1_INTEGER *serial);
]]
if OPENSSL_11_OR_LATER then
ffi.cdef [[
int X509_CRL_set1_lastUpdate(X509_CRL *x, const ASN1_TIME *tm);
int X509_CRL_set1_nextUpdate(X509_CRL *x, const ASN1_TIME *tm);
/*const*/ ASN1_TIME *X509_CRL_get0_lastUpdate(const X509_CRL *crl);
/*const*/ ASN1_TIME *X509_CRL_get0_nextUpdate(const X509_CRL *crl);
long X509_CRL_get_version(const X509_CRL *crl);
X509_EXTENSION *X509_CRL_delete_ext(X509_CRL *x, int loc);
int X509_CRL_get_signature_nid(const X509_CRL *crl);
]]
end
if OPENSSL_10 or BORINGSSL_110 then
-- in openssl 1.0.x some getters are direct accessor to struct members (defiend by macros)
ffi.cdef [[
typedef struct X509_crl_info_st {
ASN1_INTEGER *version;
X509_ALGOR *sig_alg;
X509_NAME *issuer;
ASN1_TIME *lastUpdate;
ASN1_TIME *nextUpdate;
// STACK_OF(X509_REVOKED)
OPENSSL_STACK *revoked;
// STACK_OF(X509_EXTENSION)
OPENSSL_STACK /* [0] */ *extensions;
ASN1_ENCODING enc;
} X509_CRL_INFO;
// Note: this struct is trimmed
struct X509_crl_st {
/* actual signature */
X509_CRL_INFO *crl;
// trimmed
} /* X509_CRL */ ;
int X509_CRL_set_lastUpdate(X509_CRL *x, const ASN1_TIME *tm);
int X509_CRL_set_nextUpdate(X509_CRL *x, const ASN1_TIME *tm);
]]
end

View File

@ -0,0 +1,88 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.evp"
require "resty.openssl.include.objects"
require "resty.openssl.include.x509"
require "resty.openssl.include.stack"
local asn1_macro = require "resty.openssl.include.asn1"
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local BORINGSSL_110 = require("resty.openssl.version").BORINGSSL_110
asn1_macro.declare_asn1_functions("X509_REQ", asn1_macro.has_new_ex)
ffi.cdef [[
int X509_REQ_set_subject_name(X509_REQ *req, X509_NAME *name);
EVP_PKEY *X509_REQ_get_pubkey(X509_REQ *req);
int X509_REQ_set_pubkey(X509_REQ *x, EVP_PKEY *pkey);
int X509_REQ_set_version(X509_REQ *x, long version);
int X509_REQ_get_attr_count(const X509_REQ *req);
int X509_CRL_add_ext(X509_CRL *x, X509_EXTENSION *ex, int loc);
X509_EXTENSION *X509_CRL_get_ext(const X509_CRL *x, int loc);
int X509_CRL_get_ext_by_NID(const X509_CRL *x, int nid, int lastpos);
int i2d_re_X509_REQ_tbs(X509_REQ *req, unsigned char **pp);
void X509_ATTRIBUTE_free(X509_ATTRIBUTE *a);
int X509_REQ_get_attr_by_NID(const X509_REQ *req, int nid, int lastpos);
X509_ATTRIBUTE *X509_REQ_delete_attr(X509_REQ *req, int loc);
int *X509_REQ_get_extension_nids(void);
int X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md);
int X509_REQ_verify(X509_REQ *a, EVP_PKEY *r);
int i2d_X509_REQ_bio(BIO *bp, X509_REQ *req);
X509_REQ *d2i_X509_REQ_bio(BIO *bp, X509_REQ **req);
// STACK_OF(X509_EXTENSION)
OPENSSL_STACK *X509_REQ_get_extensions(X509_REQ *req);
// STACK_OF(X509_EXTENSION)
int X509_REQ_add_extensions(X509_REQ *req, OPENSSL_STACK *exts);
int X509_REQ_check_private_key(X509_REQ *x, EVP_PKEY *k);
]]
if OPENSSL_11_OR_LATER then
ffi.cdef [[
X509_NAME *X509_REQ_get_subject_name(const X509_REQ *req);
long X509_REQ_get_version(const X509_REQ *req);
int X509_REQ_get_signature_nid(const X509_REQ *crl);
]]
end
if OPENSSL_10 or BORINGSSL_110 then
ffi.cdef [[
typedef struct X509_req_info_st {
ASN1_ENCODING enc;
ASN1_INTEGER *version;
X509_NAME *subject;
/*X509_PUBKEY*/ void *pubkey;
/* d=2 hl=2 l= 0 cons: cont: 00 */
/*STACK_OF(X509_ATTRIBUTE)*/ OPENSSL_STACK *attributes; /* [ 0 ] */
} X509_REQ_INFO;
// Note: this struct is trimmed
typedef struct X509_req_st {
X509_REQ_INFO *req_info;
X509_ALGOR *sig_alg;
// trimmed
//ASN1_BIT_STRING *signature;
//int references;
} X509_REQ;
]]
end
if OPENSSL_3X then
ffi.cdef [[
int X509_REQ_verify_ex(X509_REQ *a, EVP_PKEY *pkey, OSSL_LIB_CTX *libctx,
const char *propq);
]]
end

View File

@ -0,0 +1,44 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.x509v3"
require "resty.openssl.include.x509"
local asn1_macro = require "resty.openssl.include.asn1"
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
asn1_macro.declare_asn1_functions("X509_EXTENSION")
if OPENSSL_3X then
ffi.cdef [[
struct v3_ext_ctx {
int flags;
X509 *issuer_cert;
X509 *subject_cert;
X509_REQ *subject_req;
X509_CRL *crl;
/*X509V3_CONF_METHOD*/ void *db_meth;
void *db;
EVP_PKEY *issuer_pkey;
};
int X509V3_set_issuer_pkey(X509V3_CTX *ctx, EVP_PKEY *pkey);
]]
else
ffi.cdef [[
struct v3_ext_ctx {
int flags;
X509 *issuer_cert;
X509 *subject_cert;
X509_REQ *subject_req;
X509_CRL *crl;
/*X509V3_CONF_METHOD*/ void *db_meth;
void *db;
};
]]
end
ffi.cdef [[
int X509_EXTENSION_set_data(X509_EXTENSION *ex, ASN1_OCTET_STRING *data);
int X509_EXTENSION_set_object(X509_EXTENSION *ex, const ASN1_OBJECT *obj);
]]

View File

@ -0,0 +1,138 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.bio"
require "resty.openssl.include.pem"
require "resty.openssl.include.stack"
local asn1_macro = require "resty.openssl.include.asn1"
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local BORINGSSL_110 = require("resty.openssl.version").BORINGSSL_110
asn1_macro.declare_asn1_functions("X509", asn1_macro.has_new_ex)
ffi.cdef [[
int i2d_X509_bio(BIO *bp, X509 *x509);
X509 *d2i_X509_bio(BIO *bp, X509 **x509);
// STACK_OF(X509)
OPENSSL_STACK *X509_chain_up_ref(OPENSSL_STACK *chain);
int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md);
int X509_verify(X509 *a, EVP_PKEY *r);
ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long adj);
int X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc);
X509_EXTENSION *X509_get_ext(const X509 *x, int loc);
int X509_get_ext_by_NID(const X509 *x, int nid, int lastpos);
void *X509_get_ext_d2i(const X509 *x, int nid, int *crit, int *idx);
int X509_EXTENSION_set_critical(X509_EXTENSION *ex, int crit);
int X509_EXTENSION_get_critical(const X509_EXTENSION *ex);
ASN1_OBJECT *X509_EXTENSION_get_object(X509_EXTENSION *ex);
ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ne);
X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, void *ext_struc);
X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex,
int nid, int crit,
ASN1_OCTET_STRING *data);
// needed by pkey
EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a);
EVP_PKEY *d2i_PUBKEY_bio(BIO *bp, EVP_PKEY **a);
int i2d_PrivateKey_bio(BIO *bp, EVP_PKEY *pkey);
int i2d_PUBKEY_bio(BIO *bp, EVP_PKEY *pkey);
EVP_PKEY *X509_get_pubkey(X509 *x);
int X509_set_pubkey(X509 *x, EVP_PKEY *pkey);
int X509_set_version(X509 *x, long version);
int X509_set_serialNumber(X509 *x, ASN1_INTEGER *serial);
X509_NAME *X509_get_subject_name(const X509 *a);
int X509_set_subject_name(X509 *x, X509_NAME *name);
X509_NAME *X509_get_issuer_name(const X509 *a);
int X509_set_issuer_name(X509 *x, X509_NAME *name);
int X509_pubkey_digest(const X509 *data, const EVP_MD *type,
unsigned char *md, unsigned int *len);
int X509_digest(const X509 *data, const EVP_MD *type,
unsigned char *md, unsigned int *len);
const char *X509_verify_cert_error_string(long n);
int X509_verify_cert(X509_STORE_CTX *ctx);
int X509_get_signature_nid(const X509 *x);
unsigned char *X509_alias_get0(X509 *x, int *len);
unsigned char *X509_keyid_get0(X509 *x, int *len);
int X509_check_private_key(X509 *x, EVP_PKEY *k);
]]
if OPENSSL_11_OR_LATER then
ffi.cdef [[
int X509_up_ref(X509 *a);
int X509_set1_notBefore(X509 *x, const ASN1_TIME *tm);
int X509_set1_notAfter(X509 *x, const ASN1_TIME *tm);
/*const*/ ASN1_TIME *X509_get0_notBefore(const X509 *x);
/*const*/ ASN1_TIME *X509_get0_notAfter(const X509 *x);
long X509_get_version(const X509 *x);
const ASN1_INTEGER *X509_get0_serialNumber(X509 *x);
X509_EXTENSION *X509_delete_ext(X509 *x, int loc);
]]
elseif OPENSSL_10 then
ffi.cdef [[
// STACK_OF(X509_EXTENSION)
X509_EXTENSION *X509v3_delete_ext(OPENSSL_STACK *x, int loc);
]]
end
if OPENSSL_10 or BORINGSSL_110 then
-- in openssl 1.0.x some getters are direct accessor to struct members (defiend by macros)
ffi.cdef [[
// crypto/x509/x509.h
typedef struct X509_val_st {
ASN1_TIME *notBefore;
ASN1_TIME *notAfter;
} X509_VAL;
typedef struct X509_algor_st {
ASN1_OBJECT *algorithm;
ASN1_TYPE *parameter;
} X509_ALGOR;
// Note: this struct is trimmed
typedef struct x509_cinf_st {
/*ASN1_INTEGER*/ void *version;
/*ASN1_INTEGER*/ void *serialNumber;
X509_ALGOR *signature;
X509_NAME *issuer;
X509_VAL *validity;
X509_NAME *subject;
/*X509_PUBKEY*/ void *key;
/*ASN1_BIT_STRING*/ void *issuerUID; /* [ 1 ] optional in v2 */
/*ASN1_BIT_STRING*/ void *subjectUID; /* [ 2 ] optional in v2 */
/*STACK_OF(X509_EXTENSION)*/ OPENSSL_STACK *extensions; /* [ 3 ] optional in v3 */
// trimmed
// ASN1_ENCODING enc;
} X509_CINF;
// Note: this struct is trimmed
struct x509_st {
X509_CINF *cert_info;
// trimmed
} X509;
int X509_set_notBefore(X509 *x, const ASN1_TIME *tm);
int X509_set_notAfter(X509 *x, const ASN1_TIME *tm);
ASN1_INTEGER *X509_get_serialNumber(X509 *x);
]]
end
if BORINGSSL_110 then
ffi.cdef [[
ASN1_TIME *X509_get_notBefore(const X509 *x);
ASN1_TIME *X509_get_notAfter(const X509 *x);
]]
end

View File

@ -0,0 +1,21 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.asn1"
require "resty.openssl.include.objects"
local asn1_macro = require "resty.openssl.include.asn1"
asn1_macro.declare_asn1_functions("X509_NAME")
ffi.cdef [[
int X509_NAME_add_entry_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, int type,
const unsigned char *bytes, int len, int loc,
int set);
int X509_NAME_entry_count(const X509_NAME *name);
X509_NAME_ENTRY *X509_NAME_get_entry(X509_NAME *name, int loc);
ASN1_OBJECT *X509_NAME_ENTRY_get_object(const X509_NAME_ENTRY *ne);
ASN1_STRING * X509_NAME_ENTRY_get_data(const X509_NAME_ENTRY *ne);
int X509_NAME_get_index_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj,
int lastpos);
]]

View File

@ -0,0 +1,17 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.asn1"
require "resty.openssl.include.objects"
local asn1_macro = require "resty.openssl.include.asn1"
asn1_macro.declare_asn1_functions("X509_REVOKED")
ffi.cdef [[
int X509_REVOKED_set_serialNumber(X509_REVOKED *x, ASN1_INTEGER *serial);
int X509_REVOKED_set_revocationDate(X509_REVOKED *r, ASN1_TIME *tm);
int X509_REVOKED_add_ext(X509_REVOKED *x, X509_EXTENSION *ex, int loc);
const ASN1_INTEGER *X509_REVOKED_get0_serialNumber(const X509_REVOKED *r);
const ASN1_TIME *X509_REVOKED_get0_revocationDate(const X509_REVOKED *r);
]]

View File

@ -0,0 +1,131 @@
local ffi = require "ffi"
local C = ffi.C
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.stack"
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local BORINGSSL = require("resty.openssl.version").BORINGSSL
local BORINGSSL_110 = require("resty.openssl.version").BORINGSSL_110
ffi.cdef [[
X509_STORE *X509_STORE_new(void);
void X509_STORE_free(X509_STORE *v);
/* int X509_STORE_lock(X509_STORE *ctx);
int X509_STORE_unlock(X509_STORE *ctx);
int X509_STORE_up_ref(X509_STORE *v);
// STACK_OF(X509_OBJECT)
OPENSSL_STACK *X509_STORE_get0_objects(X509_STORE *v);*/
int X509_STORE_add_cert(X509_STORE *ctx, X509 *x);
int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x);
int X509_STORE_load_locations(X509_STORE *ctx,
const char *file, const char *dir);
int X509_STORE_set_default_paths(X509_STORE *ctx);
int X509_STORE_set_flags(X509_STORE *ctx, unsigned long flags);
int X509_STORE_set_depth(X509_STORE *store, int depth);
int X509_STORE_set_purpose(X509_STORE *ctx, int purpose);
X509_STORE_CTX *X509_STORE_CTX_new(void);
void X509_STORE_CTX_free(X509_STORE_CTX *ctx);
// STACK_OF(X509)
int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store,
X509 *x509, OPENSSL_STACK *chain);
int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx);
int X509_STORE_CTX_set_default(X509_STORE_CTX *ctx, const char *name);
void X509_STORE_CTX_set_flags(X509_STORE_CTX *ctx, unsigned long flags);
int X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose);
// STACK_OF(X509_CRL)
void X509_STORE_CTX_set0_crls(X509_STORE_CTX *c, OPENSSL_STACK *sk);
int X509_PURPOSE_get_by_sname(char *sname);
X509_PURPOSE *X509_PURPOSE_get0(int idx);
int X509_PURPOSE_get_id(const X509_PURPOSE *xp);
]]
local _M = {
verify_flags = {
X509_V_FLAG_CB_ISSUER_CHECK = 0x0, -- Deprecated
X509_V_FLAG_USE_CHECK_TIME = 0x2,
X509_V_FLAG_CRL_CHECK = 0x4,
X509_V_FLAG_CRL_CHECK_ALL = 0x8,
X509_V_FLAG_IGNORE_CRITICAL = 0x10,
X509_V_FLAG_X509_STRICT = 0x20,
X509_V_FLAG_ALLOW_PROXY_CERTS = 0x40,
X509_V_FLAG_POLICY_CHECK = 0x80,
X509_V_FLAG_EXPLICIT_POLICY = 0x100,
X509_V_FLAG_INHIBIT_ANY = 0x200,
X509_V_FLAG_INHIBIT_MAP = 0x400,
X509_V_FLAG_NOTIFY_POLICY = 0x800,
X509_V_FLAG_EXTENDED_CRL_SUPPORT = 0x1000,
X509_V_FLAG_USE_DELTAS = 0x2000,
X509_V_FLAG_CHECK_SS_SIGNATURE = 0x4000,
X509_V_FLAG_TRUSTED_FIRST = 0x8000,
X509_V_FLAG_SUITEB_128_LOS_ONLY = 0x10000,
X509_V_FLAG_SUITEB_192_LOS = 0x20000,
X509_V_FLAG_SUITEB_128_LOS = 0x30000,
X509_V_FLAG_PARTIAL_CHAIN = 0x80000,
X509_V_FLAG_NO_ALT_CHAINS = 0x100000,
X509_V_FLAG_NO_CHECK_TIME = 0x200000,
},
}
if OPENSSL_10 or BORINGSSL_110 then
ffi.cdef [[
// STACK_OF(X509)
OPENSSL_STACK *X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx);
]];
_M.X509_STORE_CTX_get0_chain = C.X509_STORE_CTX_get_chain
elseif OPENSSL_11_OR_LATER then
ffi.cdef [[
// STACK_OF(X509)
OPENSSL_STACK *X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx);
typedef int (*X509_STORE_CTX_check_revocation_fn)(X509_STORE_CTX *ctx);
// STACK_OF(X509)
void X509_STORE_CTX_set0_verified_chain(X509_STORE_CTX *ctx, OPENSSL_STACK *sk);
]];
_M.X509_STORE_CTX_get0_chain = C.X509_STORE_CTX_get0_chain
end
-- these two apis are supported from 1.1.0 but not supported by boringssl
if not BORINGSSL then
if OPENSSL_11_OR_LATER then
ffi.cdef [[
typedef int (*X509_STORE_CTX_check_revocation_fn)(X509_STORE_CTX *ctx);
X509_STORE_CTX_check_revocation_fn X509_STORE_CTX_get_check_revocation(const X509_STORE_CTX *ctx);
// STACK_OF(X509)
void X509_STORE_CTX_set0_verified_chain(X509_STORE_CTX *ctx, OPENSSL_STACK *sk);
]];
end
end
if OPENSSL_3X then
ffi.cdef [[
X509_STORE_CTX *X509_STORE_CTX_new_ex(OSSL_LIB_CTX *libctx, const char *propq);
int X509_STORE_set_default_paths_ex(X509_STORE *ctx, OSSL_LIB_CTX *libctx,
const char *propq);
/* int X509_STORE_load_file_ex(X509_STORE *ctx, const char *file,
OSSL_LIB_CTX *libctx, const char *propq);
int X509_STORE_load_store_ex(X509_STORE *ctx, const char *uri,
OSSL_LIB_CTX *libctx, const char *propq); */
int X509_STORE_load_locations_ex(X509_STORE *ctx, const char *file,
const char *dir, OSSL_LIB_CTX *libctx,
const char *propq);
]]
_M.X509_STORE_set_default_paths = function(...) return C.X509_STORE_set_default_paths_ex(...) end
_M.X509_STORE_load_locations = function(...) return C.X509_STORE_load_locations_ex(...) end
else
_M.X509_STORE_set_default_paths = function(s) return C.X509_STORE_set_default_paths(s) end
_M.X509_STORE_load_locations = function(s, file, dir) return C.X509_STORE_load_locations(s, file, dir) end
end
return _M

View File

@ -0,0 +1,108 @@
local ffi = require "ffi"
require "resty.openssl.include.ossl_typ"
require "resty.openssl.include.stack"
local asn1_macro = require "resty.openssl.include.asn1"
ffi.cdef [[
// STACK_OF(OPENSSL_STRING)
OPENSSL_STACK *X509_get1_ocsp(X509 *x);
void X509_email_free(OPENSSL_STACK *sk);
void X509V3_set_nconf(X509V3_CTX *ctx, CONF *conf);
typedef struct EDIPartyName_st EDIPARTYNAME;
typedef struct otherName_st OTHERNAME;
typedef struct GENERAL_NAME_st {
int type;
union {
char *ptr;
OTHERNAME *otherName; /* otherName */
ASN1_IA5STRING *rfc822Name;
ASN1_IA5STRING *dNSName;
ASN1_TYPE *x400Address;
X509_NAME *directoryName;
EDIPARTYNAME *ediPartyName;
ASN1_IA5STRING *uniformResourceIdentifier;
ASN1_OCTET_STRING *iPAddress;
ASN1_OBJECT *registeredID;
/* Old names */
ASN1_OCTET_STRING *ip; /* iPAddress */
X509_NAME *dirn; /* dirn */
ASN1_IA5STRING *ia5; /* rfc822Name, dNSName,
* uniformResourceIdentifier */
ASN1_OBJECT *rid; /* registeredID */
ASN1_TYPE *other; /* x400Address */
} d;
} GENERAL_NAME;
// STACK_OF(GENERAL_NAME)
typedef struct stack_st GENERAL_NAMES;
// STACK_OF(X509_EXTENSION)
int X509V3_add1_i2d(OPENSSL_STACK **x, int nid, void *value,
int crit, unsigned long flags);
void *X509V3_EXT_d2i(X509_EXTENSION *ext);
X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, void *ext_struc);
int X509V3_EXT_print(BIO *out, X509_EXTENSION *ext, unsigned long flag,
int indent);
int X509_add1_ext_i2d(X509 *x, int nid, void *value, int crit,
unsigned long flags);
// although the struct has plural form, it's not a stack
typedef struct BASIC_CONSTRAINTS_st {
int ca;
ASN1_INTEGER *pathlen;
} BASIC_CONSTRAINTS;
void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subject,
X509_REQ *req, X509_CRL *crl, int flags);
X509_EXTENSION *X509V3_EXT_nconf_nid(CONF *conf, X509V3_CTX *ctx, int ext_nid,
const char *value);
X509_EXTENSION *X509V3_EXT_nconf(CONF *conf, X509V3_CTX *ctx, const char *name,
const char *value);
int X509V3_EXT_print(BIO *out, X509_EXTENSION *ext, unsigned long flag,
int indent);
void *X509V3_get_d2i(const OPENSSL_STACK *x, int nid, int *crit, int *idx);
int X509v3_get_ext_by_NID(const OPENSSL_STACK *x,
int nid, int lastpos);
X509_EXTENSION *X509v3_get_ext(const OPENSSL_STACK *x, int loc);
// STACK_OF(ACCESS_DESCRIPTION)
typedef struct stack_st AUTHORITY_INFO_ACCESS;
typedef struct ACCESS_DESCRIPTION_st {
ASN1_OBJECT *method;
GENERAL_NAME *location;
} ACCESS_DESCRIPTION;
typedef struct DIST_POINT_NAME_st {
int type;
union {
GENERAL_NAMES *fullname;
// STACK_OF(X509_NAME_ENTRY)
OPENSSL_STACK *relativename;
} name;
/* If relativename then this contains the full distribution point name */
X509_NAME *dpname;
} DIST_POINT_NAME;
typedef struct DIST_POINT_st {
DIST_POINT_NAME *distpoint;
ASN1_BIT_STRING *reasons;
GENERAL_NAMES *CRLissuer;
int dp_reasons;
} DIST_POINT;
]]
asn1_macro.declare_asn1_functions("GENERAL_NAME")
asn1_macro.declare_asn1_functions("BASIC_CONSTRAINTS")
asn1_macro.declare_asn1_functions("AUTHORITY_INFO_ACCESS") -- OCSP responder and CA
asn1_macro.declare_asn1_functions("ACCESS_DESCRIPTION")
asn1_macro.declare_asn1_functions("DIST_POINT") -- CRL distribution points

View File

@ -0,0 +1,388 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_str = ffi.string
require("resty.openssl.objects")
require("resty.openssl.include.evp.md")
-- used by legacy EVP_PKEY_derive interface
require("resty.openssl.include.evp.pkey")
local kdf_macro = require "resty.openssl.include.evp.kdf"
local ctx_lib = require "resty.openssl.ctx"
local format_error = require("resty.openssl.err").format_error
local version_num = require("resty.openssl.version").version_num
local version_text = require("resty.openssl.version").version_text
local BORINGSSL = require("resty.openssl.version").BORINGSSL
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local ctypes = require "resty.openssl.auxiliary.ctypes"
--[[
https://wiki.openssl.org/index.php/EVP_Key_Derivation
OpenSSL 1.0.2 and above provides PBKDF2 by way of PKCS5_PBKDF2_HMAC and PKCS5_PBKDF2_HMAC_SHA1.
OpenSSL 1.1.0 and above additionally provides HKDF and TLS1 PRF KDF by way of EVP_PKEY_derive and Scrypt by way of EVP_PBE_scrypt
OpenSSL 1.1.1 and above additionally provides Scrypt by way of EVP_PKEY_derive.
OpenSSL 3.0 additionally provides Single Step KDF, SSH KDF, PBKDF2, Scrypt, HKDF, ANSI X9.42 KDF, ANSI X9.63 KDF and TLS1 PRF KDF by way of EVP_KDF.
From OpenSSL 3.0 the recommended way of performing key derivation is to use the EVP_KDF functions. If compatibility with OpenSSL 1.1.1 is required then a limited set of KDFs can be used via EVP_PKEY_derive.
]]
local NID_id_pbkdf2 = -1
local NID_id_scrypt = -2
local NID_tls1_prf = -3
local NID_hkdf = -4
if version_num >= 0x10002000 then
NID_id_pbkdf2 = C.OBJ_txt2nid("PBKDF2")
assert(NID_id_pbkdf2 > 0)
end
if version_num >= 0x10100000 and not BORINGSSL then
NID_hkdf = C.OBJ_txt2nid("HKDF")
assert(NID_hkdf > 0)
NID_tls1_prf = C.OBJ_txt2nid("TLS1-PRF")
assert(NID_tls1_prf > 0)
-- we use EVP_PBE_scrypt to do scrypt, so this is supported >= 1.1.0
NID_id_scrypt = C.OBJ_txt2nid("id-scrypt")
assert(NID_id_scrypt > 0)
end
local _M = {
HKDEF_MODE_EXTRACT_AND_EXPAND = kdf_macro.EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND,
HKDEF_MODE_EXTRACT_ONLY = kdf_macro.EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY,
HKDEF_MODE_EXPAND_ONLY = kdf_macro.EVP_PKEY_HKDEF_MODE_EXPAND_ONLY,
PBKDF2 = NID_id_pbkdf2,
SCRYPT = NID_id_scrypt,
TLS1_PRF = NID_tls1_prf,
HKDF = NID_hkdf,
}
local type_literals = {
[NID_id_pbkdf2] = "PBKDF2",
[NID_id_scrypt] = "scrypt",
[NID_tls1_prf] = "TLS-1PRF",
[NID_hkdf] = "HKDF",
}
local TYPE_NUMBER = 0x1
local TYPE_STRING = 0x2
local function check_options(opt, nid, field, typ, is_optional, required_only_if_nid)
local v = opt[field]
if not v then
if is_optional or (required_only_if_nid and required_only_if_nid ~= nid) then
return typ == TYPE_NUMBER and 0 or nil
else
return nil, "\"" .. field .. "\" must be set"
end
end
if typ == TYPE_NUMBER then
v = tonumber(v)
if not typ then
return nil, "except a number as \"" .. field .. "\""
end
elseif typ == TYPE_STRING then
if type(v) ~= "string" then
return nil, "except a string as \"" .. field .. "\""
end
else
error("don't known how to check " .. typ, 2)
end
return v
end
local function check_hkdf_options(opt)
local mode = opt.hkdf_mode
if not mode or version_num < 0x10101000 then
mode = _M.HKDEF_MODE_EXTRACT_AND_EXPAND
end
if mode == _M.HKDEF_MODE_EXTRACT_AND_EXPAND and (
not opt.salt or not opt.hkdf_info) then
return '""salt" and "hkdf_info" are required for EXTRACT_AND_EXPAND mode'
elseif mode == _M.HKDEF_MODE_EXTRACT_ONLY and not opt.salt then
return '"salt" is required for EXTRACT_ONLY mode'
elseif mode == _M.EVP_PKEY_HKDEF_MODE_EXPAND_ONLY and not opt.hkdf_info then
return '"hkdf_info" is required for EXPAND_ONLY mode'
end
return nil
end
local options_schema = {
outlen = { TYPE_NUMBER },
pass = { TYPE_STRING, true },
salt = { TYPE_STRING, true },
md = { TYPE_STRING, true },
-- pbkdf2 only
pbkdf2_iter = { TYPE_NUMBER, true },
-- hkdf only
hkdf_key = { TYPE_STRING, nil, NID_hkdf },
hkdf_mode = { TYPE_NUMBER, true },
hkdf_info = { TYPE_STRING, true },
-- tls1-prf
tls1_prf_secret = { TYPE_STRING, nil, NID_tls1_prf },
tls1_prf_seed = { TYPE_STRING, nil, NID_tls1_prf },
-- scrypt only
scrypt_maxmem = { TYPE_NUMBER, true },
scrypt_N = { TYPE_NUMBER, nil, NID_id_scrypt },
scrypt_r = { TYPE_NUMBER, nil, NID_id_scrypt },
scrypt_p = { TYPE_NUMBER, nil, NID_id_scrypt },
}
local outlen = ctypes.ptr_of_uint64()
function _M.derive(options)
local typ = options.type
if not typ then
return nil, "kdf.derive: \"type\" must be set"
elseif type(typ) ~= "number" then
return nil, "kdf.derive: expect a number as \"type\""
end
if typ <= 0 then
return nil, "kdf.derive: kdf type " .. (type_literals[typ] or tostring(typ)) ..
" not supported in " .. version_text
end
for k, v in pairs(options_schema) do
local v, err = check_options(options, typ, k, unpack(v))
if err then
return nil, "kdf.derive: " .. err
end
options[k] = v
end
if typ == NID_hkdf then
local err = check_hkdf_options(options)
if err then
return nil, "kdf.derive: " .. err
end
end
local salt_len = 0
if options.salt then
salt_len = #options.salt
end
local pass_len = 0
if options.pass then
pass_len = #options.pass
end
local md
if OPENSSL_3X then
md = C.EVP_MD_fetch(ctx_lib.get_libctx(), options.md or 'sha1', options.properties)
else
md = C.EVP_get_digestbyname(options.md or 'sha1')
end
if md == nil then
return nil, string.format("kdf.derive: invalid digest type \"%s\"", md)
end
local buf = ctypes.uchar_array(options.outlen)
-- begin legacay low level routines
local code
if typ == NID_id_pbkdf2 then
-- make openssl 1.0.2 happy
if version_num < 0x10100000 and not options.pass then
options.pass = ""
pass_len = 0
end
-- https://www.openssl.org/docs/man1.1.0/man3/PKCS5_PBKDF2_HMAC.html
local iter = options.pbkdf2_iter
if iter < 1 then
iter = 1
end
code = C.PKCS5_PBKDF2_HMAC(
options.pass, pass_len,
options.salt, salt_len, iter,
md, options.outlen, buf
)
elseif typ == NID_id_scrypt then
code = C.EVP_PBE_scrypt(
options.pass, pass_len,
options.salt, salt_len,
options.scrypt_N, options.scrypt_r, options.scrypt_p, options.scrypt_maxmem,
buf, options.outlen
)
elseif typ ~= NID_tls1_prf and typ ~= NID_hkdf then
return nil, string.format("kdf.derive: unknown type %d", typ)
end
if code then
if code ~= 1 then
return nil, format_error("kdf.derive")
else
return ffi_str(buf, options.outlen)
end
end
-- end legacay low level routines
-- begin EVP_PKEY_derive routines
outlen[0] = options.outlen
local ctx = C.EVP_PKEY_CTX_new_id(typ, nil)
if ctx == nil then
return nil, format_error("kdf.derive: EVP_PKEY_CTX_new_id")
end
ffi_gc(ctx, C.EVP_PKEY_CTX_free)
if C.EVP_PKEY_derive_init(ctx) ~= 1 then
return nil, format_error("kdf.derive: EVP_PKEY_derive_init")
end
if typ == NID_tls1_prf then
if kdf_macro.EVP_PKEY_CTX_set_tls1_prf_md(ctx, md) ~= 1 then
return nil, format_error("kdf.derive: EVP_PKEY_CTX_set_tls1_prf_md")
end
if kdf_macro.EVP_PKEY_CTX_set1_tls1_prf_secret(ctx, options.tls1_prf_secret) ~= 1 then
return nil, format_error("kdf.derive: EVP_PKEY_CTX_set1_tls1_prf_secret")
end
if kdf_macro.EVP_PKEY_CTX_add1_tls1_prf_seed(ctx, options.tls1_prf_seed) ~= 1 then
return nil, format_error("kdf.derive: EVP_PKEY_CTX_add1_tls1_prf_seed")
end
elseif typ == NID_hkdf then
if kdf_macro.EVP_PKEY_CTX_set_hkdf_md(ctx, md) ~= 1 then
return nil, format_error("kdf.derive: EVP_PKEY_CTX_set_hkdf_md")
end
if options.salt and
kdf_macro.EVP_PKEY_CTX_set1_hkdf_salt(ctx, options.salt) ~= 1 then
return nil, format_error("kdf.derive: EVP_PKEY_CTX_set1_hkdf_salt")
end
if options.hkdf_key and
kdf_macro.EVP_PKEY_CTX_set1_hkdf_key(ctx, options.hkdf_key) ~= 1 then
return nil, format_error("kdf.derive: EVP_PKEY_CTX_set1_hkdf_key")
end
if options.hkdf_info and
kdf_macro.EVP_PKEY_CTX_add1_hkdf_info(ctx, options.hkdf_info) ~= 1 then
return nil, format_error("kdf.derive: EVP_PKEY_CTX_add1_hkdf_info")
end
if options.hkdf_mode then
if version_num >= 0x10101000 then
if kdf_macro.EVP_PKEY_CTX_set_hkdf_mode(ctx, options.hkdf_mode) ~= 1 then
return nil, format_error("kdf.derive: EVP_PKEY_CTX_set_hkdf_mode")
end
if options.hkdf_mode == _M.HKDEF_MODE_EXTRACT_ONLY then
local md_size = OPENSSL_3X and C.EVP_MD_get_size(md) or C.EVP_MD_size(md)
if options.outlen ~= md_size then
options.outlen = md_size
ngx.log(ngx.WARN, "hkdf_mode EXTRACT_ONLY outputs fixed length of ", md_size,
" key, ignoring options.outlen")
end
outlen[0] = md_size
buf = ctypes.uchar_array(md_size)
end
else
ngx.log(ngx.WARN, "hkdf_mode is not effective in ", version_text)
end
end
else
return nil, string.format("kdf.derive: unknown type %d", typ)
end
code = C.EVP_PKEY_derive(ctx, buf, outlen)
if code == -2 then
return nil, "kdf.derive: operation is not supported by the public key algorithm"
end
-- end EVP_PKEY_derive routines
return ffi_str(buf, options.outlen)
end
if not OPENSSL_3X then
return _M
end
_M.derive_legacy = _M.derive
_M.derive = nil
-- OPENSSL 3.0 style API
local param_lib = require "resty.openssl.param"
local SIZE_MAX = ctypes.SIZE_MAX
local mt = {__index = _M}
local kdf_ctx_ptr_ct = ffi.typeof('EVP_KDF_CTX*')
function _M.new(typ, properties)
local algo = C.EVP_KDF_fetch(ctx_lib.get_libctx(), typ, properties)
if algo == nil then
return nil, format_error(string.format("mac.new: invalid mac type \"%s\"", typ))
end
local ctx = C.EVP_KDF_CTX_new(algo)
if ctx == nil then
return nil, "mac.new: failed to create EVP_MAC_CTX"
end
ffi_gc(ctx, C.EVP_KDF_CTX_free)
local buf
local buf_size = tonumber(C.EVP_KDF_CTX_get_kdf_size(ctx))
if buf_size == SIZE_MAX then -- no fixed size
buf_size = nil
else
buf = ctypes.uchar_array(buf_size)
end
return setmetatable({
ctx = ctx,
algo = algo,
buf = buf,
buf_size = buf_size,
}, mt), nil
end
function _M.istype(l)
return l and l.ctx and ffi.istype(kdf_ctx_ptr_ct, l.ctx)
end
function _M:get_provider_name()
local p = C.EVP_KDF_get0_provider(self.algo)
if p == nil then
return nil
end
return ffi_str(C.OSSL_PROVIDER_get0_name(p))
end
_M.settable_params, _M.set_params, _M.gettable_params, _M.get_param = param_lib.get_params_func("EVP_KDF_CTX")
function _M:derive(outlen, options, options_count)
if not _M.istype(self) then
return _M.derive_legacy(self)
end
if self.buf_size and outlen then
return nil, string.format("kdf:derive: this KDF has fixed output size %d, "..
"it can't be set manually", self.buf_size)
end
outlen = self.buf_size or outlen
local buf = self.buf or ctypes.uchar_array(outlen)
if options_count then
options_count = options_count - 1
else
options_count = 0
for k, v in pairs(options) do options_count = options_count + 1 end
end
local param, err
if options_count > 0 then
local schema = self:settable_params(true) -- raw schema
param, err = param_lib.construct(options, nil, schema)
if err then
return nil, "kdf:derive: " .. err
end
end
if C.EVP_KDF_derive(self.ctx, buf, outlen, param) ~= 1 then
return nil, format_error("kdf:derive")
end
return ffi_str(buf, outlen)
end
function _M:reset()
C.EVP_KDF_CTX_reset(self.ctx)
return true
end
return _M

View File

@ -0,0 +1,96 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_str = ffi.string
require "resty.openssl.include.evp.mac"
local param_lib = require "resty.openssl.param"
local ctx_lib = require "resty.openssl.ctx"
local ctypes = require "resty.openssl.auxiliary.ctypes"
local format_error = require("resty.openssl.err").format_error
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local _M = {}
local mt = {__index = _M}
local mac_ctx_ptr_ct = ffi.typeof('EVP_MAC_CTX*')
local param_types = {
cipher = param_lib.OSSL_PARAM_UTF8_STRING,
digest = param_lib.OSSL_PARAM_UTF8_STRING,
}
local params = {}
function _M.new(key, typ, cipher, digest, properties)
if not OPENSSL_3X then
return false, "EVP_MAC is only supported from OpenSSL 3.0"
end
local algo = C.EVP_MAC_fetch(ctx_lib.get_libctx(), typ, properties)
if algo == nil then
return nil, format_error(string.format("mac.new: invalid mac type \"%s\"", typ))
end
local ctx = C.EVP_MAC_CTX_new(algo)
if ctx == nil then
return nil, "mac.new: failed to create EVP_MAC_CTX"
end
ffi_gc(ctx, C.EVP_MAC_CTX_free)
params.digest = digest
params.cipher = cipher
local p = param_lib.construct(params, 2, param_types)
local code = C.EVP_MAC_init(ctx, key, #key, p)
if code ~= 1 then
return nil, format_error(string.format("mac.new: invalid cipher or digest type"))
end
local md_size = C.EVP_MAC_CTX_get_mac_size(ctx)
return setmetatable({
ctx = ctx,
algo = algo,
buf = ctypes.uchar_array(md_size),
buf_size = md_size,
}, mt), nil
end
function _M.istype(l)
return l and l.ctx and ffi.istype(mac_ctx_ptr_ct, l.ctx)
end
function _M:get_provider_name()
local p = C.EVP_MAC_get0_provider(self.algo)
if p == nil then
return nil
end
return ffi_str(C.OSSL_PROVIDER_get0_name(p))
end
_M.settable_params, _M.set_params, _M.gettable_params, _M.get_param = param_lib.get_params_func("EVP_MAC_CTX")
function _M:update(...)
for _, s in ipairs({...}) do
if C.EVP_MAC_update(self.ctx, s, #s) ~= 1 then
return false, format_error("digest:update")
end
end
return true, nil
end
function _M:final(s)
if s then
local _, err = self:update(s)
if err then
return nil, err
end
end
local length = ctypes.ptr_of_size_t()
if C.EVP_MAC_final(self.ctx, self.buf, length, self.buf_size) ~= 1 then
return nil, format_error("digest:final: EVP_MAC_final")
end
return ffi_str(self.buf, length[0])
end
return _M

View File

@ -0,0 +1,74 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_str = ffi.string
local ffi_sizeof = ffi.sizeof
require "resty.openssl.include.objects"
require "resty.openssl.include.err"
local buf = ffi.new('char[?]', 100)
local function obj2table(obj)
local nid = C.OBJ_obj2nid(obj)
local len = C.OBJ_obj2txt(buf, ffi_sizeof(buf), obj, 1)
local oid = ffi_str(buf, len)
return {
id = oid,
nid = nid,
sn = ffi_str(C.OBJ_nid2sn(nid)),
ln = ffi_str(C.OBJ_nid2ln(nid)),
}
end
local function nid2table(nid)
return obj2table(C.OBJ_nid2obj(nid))
end
local function txt2nid(txt)
if type(txt) ~= "string" then
return nil, "objects.txt2nid: expect a string at #1"
end
local nid = C.OBJ_txt2nid(txt)
if nid == 0 then
-- clean up error occurs during OBJ_txt2nid
C.ERR_clear_error()
return nil, "objects.txt2nid: invalid NID text " .. txt
end
return nid
end
local function txtnid2nid(txt_nid)
local nid
if type(txt_nid) == "string" then
nid = C.OBJ_txt2nid(txt_nid)
if nid == 0 then
-- clean up error occurs during OBJ_txt2nid
C.ERR_clear_error()
return nil, "objects.txtnid2nid: invalid NID text " .. txt_nid
end
elseif type(txt_nid) == "number" then
nid = txt_nid
else
return nil, "objects.txtnid2nid: expect string or number at #1"
end
return nid
end
local function find_sigid_algs(nid)
local out = ffi.new("int[0]")
if C.OBJ_find_sigid_algs(nid, out, nil) == 0 then
return 0, "objects.find_sigid_algs: invalid sigid " .. nid
end
return tonumber(out[0])
end
return {
obj2table = obj2table,
nid2table = nid2table,
txt2nid = txt2nid,
txtnid2nid = txtnid2nid,
find_sigid_algs = find_sigid_algs,
create = C.OBJ_create,
}

View File

@ -0,0 +1,322 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_new = ffi.new
local ffi_str = ffi.string
local ffi_cast = ffi.cast
require "resty.openssl.include.param"
local format_error = require("resty.openssl.err").format_error
local bn_lib = require("resty.openssl.bn")
local null = require("resty.openssl.auxiliary.ctypes").null
local OSSL_PARAM_INTEGER = 1
local OSSL_PARAM_UNSIGNED_INTEGER = 2
local OSSL_PARAM_REAL = 3
local OSSL_PARAM_UTF8_STRING = 4
local OSSL_PARAM_OCTET_STRING = 5
local OSSL_PARAM_UTF8_PTR = 6
local OSSL_PARAM_OCTET_PTR = 7
local alter_type_key = {}
local buf_param_key = {}
local function construct(buf_t, length, types_map, types_size)
if not length then
length = 0
for k, v in pairs(buf_t) do length = length + 1 end
end
local params = ffi_new("OSSL_PARAM[?]", length + 1)
local i = 0
local buf_param
for key, value in pairs(buf_t) do
local typ = types_map[key]
if not typ then
return nil, "param:construct: unknown key \"" .. key .. "\""
end
local param, buf, size
if value == null then -- out
value = nil
size = types_size and types_size[key] or 100
if typ == OSSL_PARAM_UTF8_STRING or typ == OSSL_PARAM_OCTET_STRING then
buf = ffi_new("char[?]", size)
end
else
local numeric = type(value) == "number"
if (numeric and typ >= OSSL_PARAM_UTF8_STRING) or
(not numeric and typ <= OSSL_PARAM_UNSIGNED_INTEGER) then
local alter_typ = types_map[alter_type_key] and types_map[alter_type_key][key]
if alter_typ and ((numeric and alter_typ <= OSSL_PARAM_UNSIGNED_INTEGER) or
(not numeric and alter_typ >= OSSL_PARAM_UTF8_STRING)) then
typ = alter_typ
else
return nil, "param:construct: key \"" .. key .. "\" can't be a " .. type(value)
end
end
end
if typ == "bn" then -- out only
buf = ffi_new("char[?]", size)
param = C.OSSL_PARAM_construct_BN(key, buf, size)
buf_param = buf_param or {}
buf_param[key] = param
elseif typ == OSSL_PARAM_INTEGER then
buf = value and ffi_new("int[1]", value) or ffi_new("int[1]")
param = C.OSSL_PARAM_construct_int(key, buf)
elseif typ == OSSL_PARAM_UNSIGNED_INTEGER then
buf = value and ffi_new("unsigned int[1]", value) or
ffi_new("unsigned int[1]")
param = C.OSSL_PARAM_construct_uint(key, buf)
elseif typ == OSSL_PARAM_UTF8_STRING then
buf = value and ffi_cast("char *", value) or buf
param = C.OSSL_PARAM_construct_utf8_string(key, buf, value and #value or size)
elseif typ == OSSL_PARAM_OCTET_STRING then
buf = value and ffi_cast("char *", value) or buf
param = C.OSSL_PARAM_construct_octet_string(key, ffi_cast("void*", buf),
value and #value or size)
elseif typ == OSSL_PARAM_UTF8_PTR then
buf = ffi_new("char*[1]")
param = C.OSSL_PARAM_construct_utf8_ptr(key, buf, 0)
elseif typ == OSSL_PARAM_OCTET_PTR then
buf = ffi_new("char*[1]")
param = C.OSSL_PARAM_construct_octet_ptr(key, ffi_cast("void**", buf), 0)
else
error("type " .. typ .. " is not yet implemented")
end
if not value then -- out
buf_t[key] = buf
end
params[i] = param
i = i + 1
end
buf_t[buf_param_key] = buf_param
params[length] = C.OSSL_PARAM_construct_end()
return params
end
local function parse(buf_t, length, types_map, types_size)
for key, buf in pairs(buf_t) do
local typ = types_map[key]
local sz = types_size and types_size[key]
if key == buf_param_key then -- luacheck: ignore
-- ignore
elseif buf == nil or buf[0] == nil then
buf_t[key] = nil
elseif typ == "bn" then
local bn_t = ffi_new("BIGNUM*[1]")
local param = buf_t[buf_param_key][key]
if C.OSSL_PARAM_get_BN(param, bn_t) ~= 1 then
return nil, format_error("param:parse: OSSL_PARAM_get_BN")
end
buf_t[key] = bn_lib.dup(bn_t[0])
elseif typ == OSSL_PARAM_INTEGER or
typ == OSSL_PARAM_UNSIGNED_INTEGER then
buf_t[key] = tonumber(buf[0])
elseif typ == OSSL_PARAM_UTF8_STRING or
typ == OSSL_PARAM_OCTET_STRING then
buf_t[key] = sz and ffi_str(buf, sz) or ffi_str(buf)
elseif typ == OSSL_PARAM_UTF8_PTR or
typ == OSSL_PARAM_OCTET_PTR then
buf_t[key] = sz and ffi_str(buf[0], sz) or ffi_str(buf[0])
elseif not typ then
return nil, "param:parse: unknown key type \"" .. key .. "\""
else
error("type " .. typ .. " is not yet implemented")
end
end
-- for GC
buf_t[buf_param_key] = nil
return buf_t
end
local param_type_readable = {
[OSSL_PARAM_UNSIGNED_INTEGER] = "unsigned integer",
[OSSL_PARAM_INTEGER] = "integer",
[OSSL_PARAM_REAL] = "real number",
[OSSL_PARAM_UTF8_PTR] = "pointer to a UTF8 encoded string",
[OSSL_PARAM_UTF8_STRING] = "UTF8 encoded string",
[OSSL_PARAM_OCTET_PTR] = "pointer to an octet string",
[OSSL_PARAM_OCTET_STRING] = "octet string",
}
local function readable_data_type(p)
local typ = p.data_type
local literal = param_type_readable[typ]
if not literal then
literal = string.format("unknown type [%d]", typ)
end
local sz = tonumber(p.data_size)
if sz == 0 then
literal = literal .. " (arbitrary size)"
else
literal = literal .. string.format(" (max %d bytes large)", sz)
end
return literal
end
local function parse_params_schema(params, schema, schema_readable)
if params == nil then
return nil, format_error("parse_params_schema")
end
local i = 0
while true do
local p = params[i]
if p.key == nil then
break
end
local key = ffi_str(p.key)
if schema then
-- TODO: don't support same key with different types for now
-- prefer string type over integer types
local typ = tonumber(p.data_type)
if schema[key] then
schema[alter_type_key] = schema[alter_type_key] or {}
schema[alter_type_key][key] = typ
else
schema[key] = typ
end
end
-- if schema_return_size then -- only non-ptr string types are needed actually
-- schema_return_size[key] = tonumber(p.return_size)
-- end
if schema_readable then
table.insert(schema_readable, { key, readable_data_type(p) })
end
i = i + 1
end
return schema
end
local param_maps_set, param_maps_get = {}, {}
local function get_params_func(typ, field)
local typ_lower = typ:sub(5):lower()
if typ_lower:sub(-4) == "_ctx" then
typ_lower = typ_lower:sub(0, -5)
end
-- field name for indexing schema, usually the (const) one created by
-- EVP_TYP_fetch or EVP_get_typebynam,e
field = field or "algo"
local cf_settable = C[typ .. "_settable_params"]
local settable = function(self, raw)
local k = self[field]
if raw and param_maps_set[k] then
return param_maps_set[k]
end
local param = cf_settable(self.ctx)
-- no params, this is fine, shouldn't be regarded as an error
if param == nil then
param_maps_set[k] = {}
return {}
end
local schema, schema_reabale = {}, raw and nil or {}
parse_params_schema(param, schema, schema_reabale)
param_maps_set[k] = schema
return raw and schema or schema_reabale
end
local cf_set = C[typ .. "_set_params"]
local set = function(self, params)
if not param_maps_set[self[field]] then
local ok, err = self:settable_params()
if not ok then
return false, typ_lower .. ":set_params: " .. err
end
end
local oparams, err = construct(params, nil, param_maps_set[self[field]])
if err then
return false, typ_lower .. ":set_params: " .. err
end
if cf_set(self.ctx, oparams) ~= 1 then
return false, format_error(typ_lower .. ":set_params: " .. typ .. "_set_params")
end
return true
end
local cf_gettable = C[typ .. "_gettable_params"]
local gettable = function(self, raw)
local k = self[field]
if raw and param_maps_set[k] then
return param_maps_set[k]
end
local param = cf_gettable(self.ctx)
-- no params, this is fine, shouldn't be regarded as an error
if param == nil then
param_maps_get[k] = {}
return {}
end
local schema, schema_reabale = {}, raw and nil or {}
parse_params_schema(param, schema, schema_reabale)
param_maps_set[k] = schema
return raw and schema or schema_reabale
end
local cf_get = C[typ .. "_get_params"]
local get_buffer, get_size_map = {}, {}
local get = function(self, key, want_size, want_type)
if not param_maps_get[self[field]] then
local ok, err = self:gettable_params()
if not ok then
return false, typ_lower .. ":set_params: " .. err
end
end
local schema = param_maps_set[self[field]]
if schema == nil or not schema[key] then -- nil or null
return nil, typ_lower .. ":get_param: unknown key \"" .. key .. "\""
end
table.clear(get_buffer)
table.clear(get_size_map)
get_buffer[key] = null
get_size_map[key] = want_size
schema = want_type and { [key] = want_type } or schema
local req, err = construct(get_buffer, 1, schema, get_size_map)
if not req then
return nil, typ_lower .. ":get_param: failed to construct params: " .. err
end
if cf_get(self.ctx, req) ~= 1 then
return nil, format_error(typ_lower .. ":get_param:get")
end
get_buffer, err = parse(get_buffer, 1, schema, get_size_map)
if err then
return nil, typ_lower .. ":get_param: failed to parse params: " .. err
end
return get_buffer[key]
end
return settable, set, gettable, get
end
return {
OSSL_PARAM_INTEGER = OSSL_PARAM_INTEGER,
OSSL_PARAM_UNSIGNED_INTEGER = OSSL_PARAM_INTEGER,
OSSL_PARAM_REAL = OSSL_PARAM_REAL,
OSSL_PARAM_UTF8_STRING = OSSL_PARAM_UTF8_STRING,
OSSL_PARAM_OCTET_STRING = OSSL_PARAM_OCTET_STRING,
OSSL_PARAM_UTF8_PTR = OSSL_PARAM_UTF8_PTR,
OSSL_PARAM_OCTET_PTR = OSSL_PARAM_OCTET_PTR,
construct = construct,
parse = parse,
parse_params_schema = parse_params_schema,
get_params_func = get_params_func,
}

View File

@ -0,0 +1,172 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_str = ffi.string
require "resty.openssl.include.pkcs12"
require "resty.openssl.include.bio"
local bio_util = require "resty.openssl.auxiliary.bio"
local format_error = require("resty.openssl.err").format_error
local pkey_lib = require "resty.openssl.pkey"
local x509_lib = require "resty.openssl.x509"
local stack_macro = require "resty.openssl.include.stack"
local stack_lib = require "resty.openssl.stack"
local objects_lib = require "resty.openssl.objects"
local ctx_lib = require "resty.openssl.ctx"
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local stack_of_x509_new = stack_lib.new_of("X509")
local stack_of_x509_add = stack_lib.add_of("X509")
local stack_of_x509_iter = stack_lib.mt_of("X509", x509_lib.dup, {}).__ipairs
local ptr_ptr_of_pkey = ffi.typeof("EVP_PKEY*[1]")
local ptr_ptr_of_x509 = ffi.typeof("X509*[1]")
local ptr_ptr_of_stack = ffi.typeof("OPENSSL_STACK*[1]")
local function decode(p12, passphrase)
local bio = C.BIO_new_mem_buf(p12, #p12)
if bio == nil then
return nil, "pkcs12.decode: BIO_new_mem_buf() failed"
end
ffi_gc(bio, C.BIO_free)
local p12 = C.d2i_PKCS12_bio(bio, nil)
if p12 == nil then
return nil, format_error("pkcs12.decode: d2i_PKCS12_bio")
end
ffi_gc(p12, C.PKCS12_free)
local ppkey = ptr_ptr_of_pkey()
local px509 = ptr_ptr_of_x509()
local pstack = ptr_ptr_of_stack()
local stack = stack_of_x509_new()
if stack == nil then
return nil, "pkcs12.decode: OPENSSL_sk_new_null() failed"
end
-- assign a valid OPENSSL_STACK so gc is taken care of
pstack[0] = stack
local code = C.PKCS12_parse(p12, passphrase or "", ppkey, px509, pstack)
if code ~= 1 then
return nil, format_error("pkcs12.decode: PKCS12_parse")
end
local cacerts
local n = stack_macro.OPENSSL_sk_num(stack)
if n > 0 then
cacerts = {}
local iter = stack_of_x509_iter({ ctx = stack })
for i=1, n do
local _, c = iter()
cacerts[i] = c
end
end
local friendly_name = C.X509_alias_get0(px509[0], nil)
if friendly_name ~= nil then
friendly_name = ffi_str(friendly_name)
end
return {
key = pkey_lib.new(ppkey[0]),
cert = x509_lib.new(px509[0]),
friendly_name = friendly_name,
cacerts = cacerts,
-- store reference to the stack, so it's not GC'ed unexpectedly
_stack = stack,
}
end
local function encode(opts, passphrase, properties)
if passphrase and type(passphrase) ~= "string" then
return nil, "pkcs12.encode: expect passphrase to be a string"
end
local pkey = opts.key
if not pkey_lib.istype(pkey) then
return nil, "pkcs12.encode: expect key to be a pkey instance"
end
local cert = opts.cert
if not x509_lib.istype(cert) then
return nil, "pkcs12.encode: expect cert to be a x509 instance"
end
local ok, err = cert:check_private_key(pkey)
if not ok then
return nil, "pkcs12.encode: key doesn't match cert: " .. err
end
local nid_key = opts.nid_key
if nid_key then
nid_key, err = objects_lib.txtnid2nid(nid_key)
if err then
return nil, "pkcs12.encode: invalid nid_key"
end
end
local nid_cert = opts.nid_cert
if nid_cert then
nid_cert, err = objects_lib.txtnid2nid(nid_cert)
if err then
return nil, "pkcs12.encode: invalid nid_cert"
end
end
local x509stack
local cacerts = opts.cacerts
if cacerts then
if type(cacerts) ~= "table" then
return nil, "pkcs12.encode: expect cacerts to be a table"
end
if #cacerts > 0 then
-- stack lib handles gc
x509stack = stack_of_x509_new()
for _, c in ipairs(cacerts) do
if not OPENSSL_10 then
if C.X509_up_ref(c.ctx) ~= 1 then
return nil, "pkcs12.encode: failed to add cacerts: X509_up_ref failed"
end
end
local ok, err = stack_of_x509_add(x509stack, c.ctx)
if not ok then
return nil, "pkcs12.encode: failed to add cacerts: " .. err
end
end
if OPENSSL_10 then
-- OpenSSL 1.0.2 doesn't have X509_up_ref
-- shallow copy the stack, up_ref for each element
x509stack = C.X509_chain_up_ref(x509stack)
-- use the shallow gc
ffi_gc(x509stack, stack_macro.OPENSSL_sk_free)
end
end
end
local p12
if OPENSSL_3X then
p12 = C.PKCS12_create_ex(passphrase or "", opts.friendly_name,
pkey.ctx, cert.ctx, x509stack,
nid_key or 0, nid_cert or 0,
opts.iter or 0, opts.mac_iter or 0, 0,
ctx_lib.get_libctx(), properties)
else
p12 = C.PKCS12_create(passphrase or "", opts.friendly_name,
pkey.ctx, cert.ctx, x509stack,
nid_key or 0, nid_cert or 0,
opts.iter or 0, opts.mac_iter or 0, 0)
end
if p12 == nil then
return nil, format_error("pkcs12.encode: PKCS12_create")
end
ffi_gc(p12, C.PKCS12_free)
return bio_util.read_wrap(C.i2d_PKCS12_bio, p12)
end
return {
decode = decode,
loads = decode,
encode = encode,
dumps = encode,
}

View File

@ -0,0 +1,974 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_new = ffi.new
local ffi_str = ffi.string
local ffi_cast = ffi.cast
local ffi_copy = ffi.copy
local rsa_macro = require "resty.openssl.include.rsa"
local dh_macro = require "resty.openssl.include.dh"
require "resty.openssl.include.bio"
require "resty.openssl.include.pem"
require "resty.openssl.include.x509"
require "resty.openssl.include.evp.pkey"
local evp_macro = require "resty.openssl.include.evp"
local pkey_macro = require "resty.openssl.include.evp.pkey"
local bio_util = require "resty.openssl.auxiliary.bio"
local digest_lib = require "resty.openssl.digest"
local rsa_lib = require "resty.openssl.rsa"
local dh_lib = require "resty.openssl.dh"
local ec_lib = require "resty.openssl.ec"
local ecx_lib = require "resty.openssl.ecx"
local objects_lib = require "resty.openssl.objects"
local jwk_lib = require "resty.openssl.auxiliary.jwk"
local ctx_lib = require "resty.openssl.ctx"
local ctypes = require "resty.openssl.auxiliary.ctypes"
local ecdsa_util = require "resty.openssl.auxiliary.ecdsa"
local format_error = require("resty.openssl.err").format_error
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local OPENSSL_111_OR_LATER = require("resty.openssl.version").OPENSSL_111_OR_LATER
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local BORINGSSL = require("resty.openssl.version").BORINGSSL
local ptr_of_uint = ctypes.ptr_of_uint
local ptr_of_size_t = ctypes.ptr_of_size_t
local ptr_of_int = ctypes.ptr_of_int
local null = ctypes.null
local load_pem_args = { null, null, null }
local load_der_args = { null }
local get_pkey_key
if OPENSSL_11_OR_LATER then
get_pkey_key = {
[evp_macro.EVP_PKEY_RSA] = function(ctx) return C.EVP_PKEY_get0_RSA(ctx) end,
[evp_macro.EVP_PKEY_EC] = function(ctx) return C.EVP_PKEY_get0_EC_KEY(ctx) end,
[evp_macro.EVP_PKEY_DH] = function(ctx) return C.EVP_PKEY_get0_DH(ctx) end
}
else
get_pkey_key = {
[evp_macro.EVP_PKEY_RSA] = function(ctx) return ctx.pkey and ctx.pkey.rsa end,
[evp_macro.EVP_PKEY_EC] = function(ctx) return ctx.pkey and ctx.pkey.ec end,
[evp_macro.EVP_PKEY_DH] = function(ctx) return ctx.pkey and ctx.pkey.dh end,
}
end
local load_rsa_key_funcs
if not OPENSSL_3X then
load_rsa_key_funcs= {
['PEM_read_bio_RSAPrivateKey'] = true,
['PEM_read_bio_RSAPublicKey'] = true,
} -- those functions return RSA* instead of EVP_PKEY*
end
local function load_pem_der(txt, opts, funcs)
local fmt = opts.format or '*'
if fmt ~= 'PEM' and fmt ~= 'DER' and fmt ~= "JWK" and fmt ~= '*' then
return nil, "expecting 'DER', 'PEM', 'JWK' or '*' as \"format\""
end
local typ = opts.type or '*'
if typ ~= 'pu' and typ ~= 'pr' and typ ~= '*' then
return nil, "expecting 'pr', 'pu' or '*' as \"type\""
end
if fmt == "JWK" and (typ == "pu" or type == "pr") then
return nil, "explictly load private or public key from JWK format is not supported"
end
ngx.log(ngx.DEBUG, "load key using fmt: ", fmt, ", type: ", typ)
local bio = C.BIO_new_mem_buf(txt, #txt)
if bio == nil then
return nil, "BIO_new_mem_buf() failed"
end
ffi_gc(bio, C.BIO_free)
local ctx
local fs = funcs[fmt][typ]
local passphrase_cb
for f, arg in pairs(fs) do
-- don't need BIO when loading JWK key: we parse it in Lua land
if f == "load_jwk" then
local err
ctx, err = jwk_lib[f](txt)
if ctx == nil then
-- if fmt is explictly set to JWK, we should return an error now
if fmt == "JWK" then
return nil, err
end
ngx.log(ngx.DEBUG, "jwk decode failed: ", err, ", continuing")
end
else
-- #define BIO_CTRL_RESET 1
local code = C.BIO_ctrl(bio, 1, 0, nil)
if code ~= 1 then
return nil, "BIO_ctrl() failed"
end
-- only pass in passphrase/passphrase_cb to PEM_* functions
if fmt == "PEM" or (fmt == "*" and arg == load_pem_args) then
if opts.passphrase then
local passphrase = opts.passphrase
if type(passphrase) ~= "string" then
-- clear errors occur when trying
C.ERR_clear_error()
return nil, "passphrase must be a string"
end
arg = { null, nil, passphrase }
elseif opts.passphrase_cb then
passphrase_cb = passphrase_cb or ffi_cast("pem_password_cb", function(buf, size)
local p = opts.passphrase_cb()
local len = #p -- 1 byte for \0
if len > size then
ngx.log(ngx.WARN, "pkey:load_pem_der: passphrase truncated from ", len, " to ", size)
len = size
end
ffi_copy(buf, p, len)
return len
end)
arg = { null, passphrase_cb, null }
end
end
ctx = C[f](bio, unpack(arg))
end
if ctx ~= nil then
ngx.log(ngx.DEBUG, "pkey:load_pem_der: loaded pkey using function ", f)
-- pkcs1 functions create a rsa rather than evp_pkey
-- disable the checking in openssl 3.0 for sail safe
if not OPENSSL_3X and load_rsa_key_funcs[f] then
local rsa = ctx
ctx = C.EVP_PKEY_new()
if ctx == null then
return nil, format_error("pkey:load_pem_der: EVP_PKEY_new")
end
if C.EVP_PKEY_assign(ctx, evp_macro.EVP_PKEY_RSA, rsa) ~= 1 then
C.RSA_free(rsa)
C.EVP_PKEY_free(ctx)
return nil, "pkey:load_pem_der: EVP_PKEY_assign() failed"
end
end
break
end
end
if passphrase_cb ~= nil then
passphrase_cb:free()
end
if ctx == nil then
return nil, format_error()
end
-- clear errors occur when trying
C.ERR_clear_error()
return ctx, nil
end
local function generate_param(key_type, config)
if key_type == evp_macro.EVP_PKEY_DH then
local dh_group = config.group
if dh_group then
local get_group_func = dh_macro.dh_groups[dh_group]
if not get_group_func then
return nil, "unknown pre-defined group " .. dh_group
end
local ctx = get_group_func()
if ctx == nil then
return nil, format_error("DH_get_x")
end
local params = C.EVP_PKEY_new()
if not params then
return nil, format_error("EVP_PKEY_new")
end
ffi_gc(params, C.EVP_PKEY_free)
if C.EVP_PKEY_assign(params, key_type, ctx) ~= 1 then
return nil, format_error("EVP_PKEY_assign")
end
return params
end
end
local pctx = C.EVP_PKEY_CTX_new_id(key_type, nil)
if pctx == nil then
return nil, format_error("EVP_PKEY_CTX_new_id")
end
ffi_gc(pctx, C.EVP_PKEY_CTX_free)
if C.EVP_PKEY_paramgen_init(pctx) ~= 1 then
return nil, format_error("EVP_PKEY_paramgen_init")
end
if key_type == evp_macro.EVP_PKEY_EC then
local curve = config.curve or 'prime192v1'
local nid = C.OBJ_ln2nid(curve)
if nid == 0 then
return nil, "unknown curve " .. curve
end
if pkey_macro.EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) <= 0 then
return nil, format_error("EVP_PKEY_CTX_ctrl: EC: curve_nid")
end
if not BORINGSSL then
-- use the named-curve encoding for best backward-compatibilty
-- and for playing well with go:crypto/x509
-- # define OPENSSL_EC_NAMED_CURVE 0x001
if pkey_macro.EVP_PKEY_CTX_set_ec_param_enc(pctx, 1) <= 0 then
return nil, format_error("EVP_PKEY_CTX_ctrl: EC: param_enc")
end
end
elseif key_type == evp_macro.EVP_PKEY_DH then
local bits = config.bits
if not config.param and not bits then
bits = 2048
end
if bits and pkey_macro.EVP_PKEY_CTX_set_dh_paramgen_prime_len(pctx, bits) <= 0 then
return nil, format_error("EVP_PKEY_CTX_ctrl: DH: bits")
end
end
local ctx_ptr = ffi_new("EVP_PKEY*[1]")
if C.EVP_PKEY_paramgen(pctx, ctx_ptr) ~= 1 then
return nil, format_error("EVP_PKEY_paramgen")
end
local params = ctx_ptr[0]
ffi_gc(params, C.EVP_PKEY_free)
return params
end
local load_param_funcs = {
[evp_macro.EVP_PKEY_EC] = {
["*"] = {
["*"] = {
['PEM_read_bio_ECPKParameters'] = load_pem_args,
-- ['d2i_ECPKParameters_bio'] = load_der_args,
}
},
},
[evp_macro.EVP_PKEY_DH] = {
["*"] = {
["*"] = {
['PEM_read_bio_DHparams'] = load_pem_args,
-- ['d2i_DHparams_bio'] = load_der_args,
}
},
},
}
local function generate_key(config)
local typ = config.type or 'RSA'
local key_type
if typ == "RSA" then
key_type = evp_macro.EVP_PKEY_RSA
elseif typ == "EC" then
key_type = evp_macro.EVP_PKEY_EC
elseif typ == "DH" then
key_type = evp_macro.EVP_PKEY_DH
elseif evp_macro.ecx_curves[typ] then
key_type = evp_macro.ecx_curves[typ]
else
return nil, "unsupported type " .. typ
end
if key_type == 0 then
return nil, "the linked OpenSSL library doesn't support " .. typ .. " key"
end
local pctx
if key_type == evp_macro.EVP_PKEY_EC or key_type == evp_macro.EVP_PKEY_DH then
local params, err
if config.param then
-- HACK
config.type = nil
local ctx, err = load_pem_der(config.param, config, load_param_funcs[key_type])
if err then
return nil, "load_pem_der: " .. err
end
if key_type == evp_macro.EVP_PKEY_EC then
local ec_group = ctx
ffi_gc(ec_group, C.EC_GROUP_free)
ctx = C.EC_KEY_new()
if ctx == nil then
return nil, "EC_KEY_new() failed"
end
if C.EC_KEY_set_group(ctx, ec_group) ~= 1 then
return nil, format_error("EC_KEY_set_group")
end
end
params = C.EVP_PKEY_new()
if not params then
return nil, format_error("EVP_PKEY_new")
end
ffi_gc(params, C.EVP_PKEY_free)
if C.EVP_PKEY_assign(params, key_type, ctx) ~= 1 then
return nil, format_error("EVP_PKEY_assign")
end
else
params, err = generate_param(key_type, config)
if err then
return nil, "generate_param: " .. err
end
end
pctx = C.EVP_PKEY_CTX_new(params, nil)
if pctx == nil then
return nil, format_error("EVP_PKEY_CTX_new")
end
else
pctx = C.EVP_PKEY_CTX_new_id(key_type, nil)
if pctx == nil then
return nil, format_error("EVP_PKEY_CTX_new_id")
end
end
ffi_gc(pctx, C.EVP_PKEY_CTX_free)
if C.EVP_PKEY_keygen_init(pctx) ~= 1 then
return nil, format_error("EVP_PKEY_keygen_init")
end
-- RSA key parameters are set for keygen ctx not paramgen
if key_type == evp_macro.EVP_PKEY_RSA then
local bits = config.bits or 2048
if bits > 4294967295 then
return nil, "bits out of range"
end
if pkey_macro.EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, bits) <= 0 then
return nil, format_error("EVP_PKEY_CTX_ctrl: RSA: bits")
end
if config.exp then
-- don't free exp as it's used internally in key
local exp = C.BN_new()
if exp == nil then
return nil, "BN_new() failed"
end
C.BN_set_word(exp, config.exp)
if pkey_macro.EVP_PKEY_CTX_set_rsa_keygen_pubexp(pctx, exp) <= 0 then
return nil, format_error("EVP_PKEY_CTX_ctrl: RSA: exp")
end
end
end
local ctx_ptr = ffi_new("EVP_PKEY*[1]")
-- TODO: move to use EVP_PKEY_gen after drop support for <1.1.1
if C.EVP_PKEY_keygen(pctx, ctx_ptr) ~= 1 then
return nil, format_error("EVP_PKEY_gen")
end
return ctx_ptr[0]
end
local load_key_try_funcs = {} do
-- TODO: pkcs1 load functions are not required in openssl 3.0
local _load_key_try_funcs = {
PEM = {
-- Note: make sure we always try load priv key first
pr = {
['PEM_read_bio_PrivateKey'] = load_pem_args,
-- disable in openssl3.0, PEM_read_bio_PrivateKey can read pkcs1 in 3.0
['PEM_read_bio_RSAPrivateKey'] = not OPENSSL_3X and load_pem_args or nil,
},
pu = {
['PEM_read_bio_PUBKEY'] = load_pem_args,
-- disable in openssl3.0, PEM_read_bio_PrivateKey can read pkcs1 in 3.0
['PEM_read_bio_RSAPublicKey'] = not OPENSSL_3X and load_pem_args or nil,
},
},
DER = {
pr = { ['d2i_PrivateKey_bio'] = load_der_args, },
pu = { ['d2i_PUBKEY_bio'] = load_der_args, },
},
JWK = {
pr = { ['load_jwk'] = {}, },
}
}
-- populate * funcs
local all_funcs = {}
local typ_funcs = {}
for fmt, ffs in pairs(_load_key_try_funcs) do
load_key_try_funcs[fmt] = ffs
local funcs = {}
for typ, fs in pairs(ffs) do
for f, arg in pairs(fs) do
funcs[f] = arg
all_funcs[f] = arg
if not typ_funcs[typ] then
typ_funcs[typ] = {}
end
typ_funcs[typ] = arg
end
end
load_key_try_funcs[fmt]["*"] = funcs
end
load_key_try_funcs["*"] = {}
load_key_try_funcs["*"]["*"] = all_funcs
for typ, fs in pairs(typ_funcs) do
load_key_try_funcs[typ] = fs
end
end
local function __tostring(self, is_priv, fmt, is_pkcs1)
if fmt == "JWK" then
return jwk_lib.dump_jwk(self, is_priv)
elseif is_pkcs1 then
if fmt ~= "PEM" or self.key_type ~= evp_macro.EVP_PKEY_RSA then
return nil, "PKCS#1 format is only supported to encode RSA key in \"PEM\" format"
elseif OPENSSL_3X then -- maybe possible with OSSL_ENCODER_CTX_new_for_pkey though
return nil, "writing out RSA key in PKCS#1 format is not supported in OpenSSL 3.0"
end
end
if is_priv then
if fmt == "DER" then
return bio_util.read_wrap(C.i2d_PrivateKey_bio, self.ctx)
end
-- PEM
if is_pkcs1 then
local rsa = get_pkey_key[evp_macro.EVP_PKEY_RSA](self.ctx)
if rsa == nil then
return nil, "unable to read RSA key for writing"
end
return bio_util.read_wrap(C.PEM_write_bio_RSAPrivateKey,
rsa,
nil, nil, 0, nil, nil)
end
return bio_util.read_wrap(C.PEM_write_bio_PrivateKey,
self.ctx,
nil, nil, 0, nil, nil)
else
if fmt == "DER" then
return bio_util.read_wrap(C.i2d_PUBKEY_bio, self.ctx)
end
-- PEM
if is_pkcs1 then
local rsa = get_pkey_key[evp_macro.EVP_PKEY_RSA](self.ctx)
if rsa == nil then
return nil, "unable to read RSA key for writing"
end
return bio_util.read_wrap(C.PEM_write_bio_RSAPublicKey, rsa)
end
return bio_util.read_wrap(C.PEM_write_bio_PUBKEY, self.ctx)
end
end
local _M = {}
local mt = { __index = _M, __tostring = __tostring }
local empty_table = {}
local evp_pkey_ptr_ct = ffi.typeof('EVP_PKEY*')
function _M.new(s, opts)
local ctx, err
s = s or {}
if type(s) == 'table' then
ctx, err = generate_key(s)
if err then
err = "pkey.new:generate_key: " .. err
end
elseif type(s) == 'string' then
ctx, err = load_pem_der(s, opts or empty_table, load_key_try_funcs)
if err then
err = "pkey.new:load_key: " .. err
end
elseif type(s) == 'cdata' then
if ffi.istype(evp_pkey_ptr_ct, s) then
ctx = s
else
return nil, "pkey.new: expect a EVP_PKEY* cdata at #1"
end
else
return nil, "pkey.new: unexpected type " .. type(s) .. " at #1"
end
if err then
return nil, err
end
ffi_gc(ctx, C.EVP_PKEY_free)
local key_type = OPENSSL_3X and C.EVP_PKEY_get_base_id(ctx) or C.EVP_PKEY_base_id(ctx)
if key_type == 0 then
return nil, "pkey.new: cannot get key_type"
end
local key_type_is_ecx = (key_type == evp_macro.EVP_PKEY_ED25519) or
(key_type == evp_macro.EVP_PKEY_X25519) or
(key_type == evp_macro.EVP_PKEY_ED448) or
(key_type == evp_macro.EVP_PKEY_X448)
-- although OpenSSL discourages to use this size for digest/verify
-- but this is good enough for now
local buf_size = OPENSSL_3X and C.EVP_PKEY_get_size(ctx) or C.EVP_PKEY_size(ctx)
local self = setmetatable({
ctx = ctx,
pkey_ctx = nil,
rsa_padding = nil,
key_type = key_type,
key_type_is_ecx = key_type_is_ecx,
buf = ctypes.uchar_array(buf_size),
buf_size = buf_size,
}, mt)
return self, nil
end
function _M.istype(l)
return l and l.ctx and ffi.istype(evp_pkey_ptr_ct, l.ctx)
end
function _M:get_key_type()
return objects_lib.nid2table(self.key_type)
end
function _M:get_default_digest_type()
if BORINGSSL then
return nil, "BoringSSL doesn't have default digest for pkey"
end
local nid = ptr_of_int()
local code = C.EVP_PKEY_get_default_digest_nid(self.ctx, nid)
if code == -2 then
return nil, "operation is not supported by the public key algorithm"
elseif code <= 0 then
return nil, format_error("get_default_digest", code)
end
local ret = objects_lib.nid2table(nid[0])
ret.mandatory = code == 2
return ret
end
function _M:get_provider_name()
if not OPENSSL_3X then
return false, "pkey:get_provider_name is not supported"
end
local p = C.EVP_PKEY_get0_provider(self.ctx)
if p == nil then
return nil
end
return ffi_str(C.OSSL_PROVIDER_get0_name(p))
end
if OPENSSL_3X then
local param_lib = require "resty.openssl.param"
_M.settable_params, _M.set_params, _M.gettable_params, _M.get_param = param_lib.get_params_func("EVP_PKEY", "key_type")
end
function _M:get_parameters()
if not self.key_type_is_ecx then
local getter = get_pkey_key[self.key_type]
if not getter then
return nil, "key getter not defined"
end
local key = getter(self.ctx)
if key == nil then
return nil, format_error("EVP_PKEY_get0_{key}")
end
if self.key_type == evp_macro.EVP_PKEY_RSA then
return rsa_lib.get_parameters(key)
elseif self.key_type == evp_macro.EVP_PKEY_EC then
return ec_lib.get_parameters(key)
elseif self.key_type == evp_macro.EVP_PKEY_DH then
return dh_lib.get_parameters(key)
end
else
return ecx_lib.get_parameters(self.ctx)
end
end
function _M:set_parameters(opts)
if not self.key_type_is_ecx then
local getter = get_pkey_key[self.key_type]
if not getter then
return nil, "key getter not defined"
end
local key = getter(self.ctx)
if key == nil then
return nil, format_error("EVP_PKEY_get0_{key}")
end
if self.key_type == evp_macro.EVP_PKEY_RSA then
return rsa_lib.set_parameters(key, opts)
elseif self.key_type == evp_macro.EVP_PKEY_EC then
return ec_lib.set_parameters(key, opts)
elseif self.key_type == evp_macro.EVP_PKEY_DH then
return dh_lib.set_parameters(key, opts)
end
else
-- for ecx keys we always create a new EVP_PKEY and release the old one
local ctx, err = ecx_lib.set_parameters(self.key_type, self.ctx, opts)
if err then
return false, err
end
self.ctx = ctx
end
end
function _M:is_private()
local params = self:get_parameters()
if self.key_type == evp_macro.EVP_PKEY_RSA then
return params.d ~= nil
else
return params.private ~= nil
end
end
local ASYMMETRIC_OP_ENCRYPT = 0x1
local ASYMMETRIC_OP_DECRYPT = 0x2
local ASYMMETRIC_OP_SIGN_RAW = 0x4
local ASYMMETRIC_OP_VERIFY_RECOVER = 0x8
local function asymmetric_routine(self, s, op, padding)
local pkey_ctx
if self.key_type == evp_macro.EVP_PKEY_RSA then
if padding then
padding = tonumber(padding)
if not padding then
return nil, "invalid padding: " .. __tostring(padding)
end
else
padding = rsa_macro.paddings.RSA_PKCS1_PADDING
end
end
if self.pkey_ctx ~= nil and
(self.key_type ~= evp_macro.EVP_PKEY_RSA or self.rsa_padding == padding) then
pkey_ctx = self.pkey_ctx
else
pkey_ctx = C.EVP_PKEY_CTX_new(self.ctx, nil)
if pkey_ctx == nil then
return nil, format_error("pkey:asymmetric_routine EVP_PKEY_CTX_new()")
end
ffi_gc(pkey_ctx, C.EVP_PKEY_CTX_free)
self.pkey_ctx = pkey_ctx
end
local f, fint, op_name
if op == ASYMMETRIC_OP_ENCRYPT then
fint = C.EVP_PKEY_encrypt_init
f = C.EVP_PKEY_encrypt
op_name = "encrypt"
elseif op == ASYMMETRIC_OP_DECRYPT then
fint = C.EVP_PKEY_decrypt_init
f = C.EVP_PKEY_decrypt
op_name = "decrypt"
elseif op == ASYMMETRIC_OP_SIGN_RAW then
fint = C.EVP_PKEY_sign_init
f = C.EVP_PKEY_sign
op_name = "sign"
elseif op == ASYMMETRIC_OP_VERIFY_RECOVER then
fint = C.EVP_PKEY_verify_recover_init
f = C.EVP_PKEY_verify_recover
op_name = "verify_recover"
else
error("bad \"op\", got " .. op, 2)
end
local code = fint(pkey_ctx)
if code < 1 then
return nil, format_error("pkey:asymmetric_routine EVP_PKEY_" .. op_name .. "_init", code)
end
-- EVP_PKEY_CTX_ctrl must be called after *_init
if self.key_type == evp_macro.EVP_PKEY_RSA and padding then
if pkey_macro.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding) ~= 1 then
return nil, format_error("pkey:asymmetric_routine EVP_PKEY_CTX_set_rsa_padding")
end
self.rsa_padding = padding
end
local length = ptr_of_size_t(self.buf_size)
if f(pkey_ctx, self.buf, length, s, #s) <= 0 then
return nil, format_error("pkey:asymmetric_routine EVP_PKEY_" .. op_name)
end
return ffi_str(self.buf, length[0]), nil
end
_M.PADDINGS = rsa_macro.paddings
function _M:encrypt(s, padding)
return asymmetric_routine(self, s, ASYMMETRIC_OP_ENCRYPT, padding)
end
function _M:decrypt(s, padding)
return asymmetric_routine(self, s, ASYMMETRIC_OP_DECRYPT, padding)
end
function _M:sign_raw(s, padding)
-- TODO: temporary hack before OpenSSL has proper check for existence of private key
if self.key_type_is_ecx and not self:is_private() then
return nil, "pkey:sign_raw: missing private key"
end
return asymmetric_routine(self, s, ASYMMETRIC_OP_SIGN_RAW, padding)
end
function _M:verify_recover(s, padding)
return asymmetric_routine(self, s, ASYMMETRIC_OP_VERIFY_RECOVER, padding)
end
local evp_pkey_ctx_ptr_ptr_ct = ffi.typeof('EVP_PKEY_CTX*[1]')
local function sign_verify_prepare(self, fint, md_alg, padding, opts)
local pkey_ctx
if self.key_type == evp_macro.EVP_PKEY_RSA and padding then
pkey_ctx = C.EVP_PKEY_CTX_new(self.ctx, nil)
if pkey_ctx == nil then
return nil, format_error("pkey:sign_verify_prepare EVP_PKEY_CTX_new()")
end
ffi_gc(pkey_ctx, C.EVP_PKEY_CTX_free)
end
local md_ctx = C.EVP_MD_CTX_new()
if md_ctx == nil then
return nil, "pkey:sign_verify_prepare: EVP_MD_CTX_new() failed"
end
ffi_gc(md_ctx, C.EVP_MD_CTX_free)
local algo
if md_alg then
if OPENSSL_3X then
algo = C.EVP_MD_fetch(ctx_lib.get_libctx(), md_alg, nil)
else
algo = C.EVP_get_digestbyname(md_alg)
end
if algo == nil then
return nil, string.format("pkey:sign_verify_prepare: invalid digest type \"%s\"", md_alg)
end
end
local ppkey_ctx = evp_pkey_ctx_ptr_ptr_ct()
ppkey_ctx[0] = pkey_ctx
if fint(md_ctx, ppkey_ctx, algo, nil, self.ctx) ~= 1 then
return nil, format_error("pkey:sign_verify_prepare: Init failed")
end
if self.key_type == evp_macro.EVP_PKEY_RSA then
if padding then
if pkey_macro.EVP_PKEY_CTX_set_rsa_padding(ppkey_ctx[0], padding) ~= 1 then
return nil, format_error("pkey:sign_verify_prepare EVP_PKEY_CTX_set_rsa_padding")
end
end
if opts and opts.pss_saltlen and padding ~= rsa_macro.paddings.RSA_PKCS1_PSS_PADDING then
if pkey_macro.EVP_PKEY_CTX_set_rsa_pss_saltlen(ppkey_ctx[0], opts.pss_saltlen) ~= 1 then
return nil, format_error("pkey:sign_verify_prepare EVP_PKEY_CTX_set_rsa_pss_saltlen")
end
end
end
return md_ctx
end
function _M:sign(digest, md_alg, padding, opts)
-- TODO: temporary hack before OpenSSL has proper check for existence of private key
if self.key_type_is_ecx and not self:is_private() then
return nil, "pkey:sign: missing private key"
end
local ret, err
if digest_lib.istype(digest) then
local length = ptr_of_uint()
if C.EVP_SignFinal(digest.ctx, self.buf, length, self.ctx) ~= 1 then
return nil, format_error("pkey:sign: EVP_SignFinal")
end
ret = ffi_str(self.buf, length[0])
elseif type(digest) == "string" then
if not OPENSSL_111_OR_LATER and not BORINGSSL then
-- we can still support earilier version with *Update and *Final
-- but we choose to not relying on the legacy interface for simplicity
return nil, "pkey:sign: new-style sign only available in OpenSSL 1.1.1 (or BoringSSL 1.1.0) or later"
elseif BORINGSSL and not md_alg and not self.key_type_is_ecx then
return nil, "pkey:sign: BoringSSL doesn't provide default digest, md_alg must be specified"
end
local md_ctx, err = sign_verify_prepare(self, C.EVP_DigestSignInit, md_alg, padding, opts)
if err then
return nil, err
end
local length = ptr_of_size_t(self.buf_size)
if C.EVP_DigestSign(md_ctx, self.buf, length, digest, #digest) ~= 1 then
return nil, format_error("pkey:sign: EVP_DigestSign")
end
ret = ffi_str(self.buf, length[0])
else
return nil, "pkey:sign: expect a digest instance or a string at #1"
end
if self.key_type == evp_macro.EVP_PKEY_EC and opts and opts.ecdsa_use_raw then
if not OPENSSL_11_OR_LATER then
return nil, "pkey:sign: opts.ecdsa_use_raw is only supported on OpenSSL 1.1.0 or later"
end
local ec_key = get_pkey_key[evp_macro.EVP_PKEY_EC](self.ctx)
ret, err = ecdsa_util.sig_der2raw(ret, ec_key)
if err then
return nil, "pkey:sign: ecdsa.sig_der2raw: " .. err
end
end
return ret
end
function _M:verify(signature, digest, md_alg, padding, opts)
if type(signature) ~= "string" then
return nil, "pkey:verify: expect a string at #1"
end
local err
if self.key_type == evp_macro.EVP_PKEY_EC and opts and opts.ecdsa_use_raw then
if not OPENSSL_11_OR_LATER then
return nil, "pkey:sign: opts.ecdsa_use_raw is only supported on OpenSSL 1.1.0 or later"
end
local ec_key = get_pkey_key[evp_macro.EVP_PKEY_EC](self.ctx)
signature, err = ecdsa_util.sig_raw2der(signature, ec_key)
if err then
return nil, "pkey:sign: ecdsa.sig_raw2der: " .. err
end
end
local code
if digest_lib.istype(digest) then
code = C.EVP_VerifyFinal(digest.ctx, signature, #signature, self.ctx)
elseif type(digest) == "string" then
if not OPENSSL_111_OR_LATER and not BORINGSSL then
-- we can still support earilier version with *Update and *Final
-- but we choose to not relying on the legacy interface for simplicity
return nil, "pkey:verify: new-style verify only available in OpenSSL 1.1.1 (or BoringSSL 1.1.0) or later"
elseif BORINGSSL and not md_alg and not self.key_type_is_ecx then
return nil, "pkey:verify: BoringSSL doesn't provide default digest, md_alg must be specified"
end
local md_ctx, err = sign_verify_prepare(self, C.EVP_DigestVerifyInit, md_alg, padding, opts)
if err then
return nil, err
end
code = C.EVP_DigestVerify(md_ctx, signature, #signature, digest, #digest)
else
return nil, "pkey:verify: expect a digest instance or a string at #2"
end
if code == 0 then
return false, nil
elseif code == 1 then
return true, nil
end
return false, format_error("pkey:verify")
end
function _M:derive(peerkey)
if not self.istype(peerkey) then
return nil, "pkey:derive: expect a pkey instance at #1"
end
local pctx = C.EVP_PKEY_CTX_new(self.ctx, nil)
if pctx == nil then
return nil, "pkey:derive: EVP_PKEY_CTX_new() failed"
end
ffi_gc(pctx, C.EVP_PKEY_CTX_free)
local code = C.EVP_PKEY_derive_init(pctx)
if code <= 0 then
return nil, format_error("pkey:derive: EVP_PKEY_derive_init", code)
end
code = C.EVP_PKEY_derive_set_peer(pctx, peerkey.ctx)
if code <= 0 then
return nil, format_error("pkey:derive: EVP_PKEY_derive_set_peer", code)
end
local buflen = ptr_of_size_t()
code = C.EVP_PKEY_derive(pctx, nil, buflen)
if code <= 0 then
return nil, format_error("pkey:derive: EVP_PKEY_derive check buffer size", code)
end
local buf = ctypes.uchar_array(buflen[0])
code = C.EVP_PKEY_derive(pctx, buf, buflen)
if code <= 0 then
return nil, format_error("pkey:derive: EVP_PKEY_derive", code)
end
return ffi_str(buf, buflen[0])
end
local function pub_or_priv_is_pri(pub_or_priv)
if pub_or_priv == 'private' or pub_or_priv == 'PrivateKey' then
return true
elseif not pub_or_priv or pub_or_priv == 'public' or pub_or_priv == 'PublicKey' then
return false
else
return nil, string.format("can only export private or public key, not %s", pub_or_priv)
end
end
function _M:tostring(pub_or_priv, fmt, pkcs1)
local is_priv, err = pub_or_priv_is_pri(pub_or_priv)
if err then
return nil, "pkey:tostring: " .. err
end
return __tostring(self, is_priv, fmt, pkcs1)
end
function _M:to_PEM(pub_or_priv, pkcs1)
return self:tostring(pub_or_priv, "PEM", pkcs1)
end
function _M.paramgen(config)
local typ = config.type
local key_type, write_func, get_ctx_func
if typ == "EC" then
key_type = evp_macro.EVP_PKEY_EC
if key_type == 0 then
return nil, "pkey.paramgen: the linked OpenSSL library doesn't support EC key"
end
write_func = C.PEM_write_bio_ECPKParameters
get_ctx_func = function(ctx)
local ctx = get_pkey_key[key_type](ctx)
if ctx == nil then
error(format_error("pkey.paramgen: EVP_PKEY_get0_{key}"))
end
return C.EC_KEY_get0_group(ctx)
end
elseif typ == "DH" then
key_type = evp_macro.EVP_PKEY_DH
if key_type == 0 then
return nil, "pkey.paramgen: the linked OpenSSL library doesn't support DH key"
end
write_func = C.PEM_write_bio_DHparams
get_ctx_func = get_pkey_key[key_type]
else
return nil, "pkey.paramgen: unsupported type " .. type
end
local params, err = generate_param(key_type, config)
if err then
return nil, "pkey.paramgen: generate_param: " .. err
end
local ctx = get_ctx_func(params)
if ctx == nil then
return nil, format_error("pkey.paramgen: EVP_PKEY_get0_{key}")
end
return bio_util.read_wrap(write_func, ctx)
end
return _M

View File

@ -0,0 +1,136 @@
local ffi = require "ffi"
local C = ffi.C
require "resty.openssl.include.provider"
local param_lib = require "resty.openssl.param"
local ctx_lib = require "resty.openssl.ctx"
local null = require("resty.openssl.auxiliary.ctypes").null
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local format_error = require("resty.openssl.err").format_error
if not OPENSSL_3X then
error("provider is only supported since OpenSSL 3.0")
end
local _M = {}
local mt = {__index = _M}
local ossl_provider_ctx_ct = ffi.typeof('OSSL_PROVIDER*')
function _M.load(name, try)
local ctx
local libctx = ctx_lib.get_libctx()
if try then
ctx = C.OSSL_PROVIDER_try_load(libctx, name)
if ctx == nil then
return nil, format_error("provider.try_load")
end
else
ctx = C.OSSL_PROVIDER_load(libctx, name)
if ctx == nil then
return nil, format_error("provider.load")
end
end
return setmetatable({
ctx = ctx,
param_types = nil,
}, mt), nil
end
function _M.set_default_search_path(path)
C.OSSL_PROVIDER_set_default_search_path(ctx_lib.get_libctx(), path)
end
function _M.is_available(name)
return C.OSSL_PROVIDER_available(ctx_lib.get_libctx(), name) == 1
end
function _M.istype(l)
return l and l.ctx and ffi.istype(ossl_provider_ctx_ct, l.ctx)
end
function _M:unload()
if C.OSSL_PROVIDER_unload(self.ctx) == nil then
return false, format_error("provider:unload")
end
return true
end
function _M:self_test()
if C.OSSL_PROVIDER_self_test(self.ctx) == nil then
return false, format_error("provider:self_test")
end
return true
end
local params_well_known = {
-- Well known parameter names that core passes to providers
["openssl-version"] = param_lib.OSSL_PARAM_UTF8_PTR,
["provider-name"] = param_lib.OSSL_PARAM_UTF8_PTR,
["module-filename"] = param_lib.OSSL_PARAM_UTF8_PTR,
-- Well known parameter names that Providers can define
["name"] = param_lib.OSSL_PARAM_UTF8_PTR,
["version"] = param_lib.OSSL_PARAM_UTF8_PTR,
["buildinfo"] = param_lib.OSSL_PARAM_UTF8_PTR,
["status"] = param_lib.OSSL_PARAM_INTEGER,
["security-checks"] = param_lib.OSSL_PARAM_INTEGER,
}
local function load_gettable_names(ctx)
local schema = {}
for k, v in pairs(params_well_known) do
schema[k] = v
end
local err
schema, err = param_lib.parse_params_schema(
C.OSSL_PROVIDER_gettable_params(ctx), schema)
if err then
return nil, err
end
return schema
end
function _M:get_params(...)
local keys = {...}
local key_length = #keys
if key_length == 0 then
return nil, "provider:get_params: at least one key is required"
end
if not self.param_types then
local param_types, err = load_gettable_names(self.ctx)
if err then
return nil, "provider:get_params: " .. err
end
self.param_types = param_types
end
local buffers = {}
for _, key in ipairs(keys) do
buffers[key] = null
end
local req, err = param_lib.construct(buffers, key_length, self.param_types)
if not req then
return nil, "provider:get_params: failed to construct params: " .. err
end
if C.OSSL_PROVIDER_get_params(self.ctx, req) ~= 1 then
return nil, format_error("provider:get_params")
end
buffers, err = param_lib.parse(buffers, key_length, self.param_types)
if err then
return nil, "provider:get_params: failed to parse params: " .. err
end
if key_length == 1 then
return buffers[keys[1]]
end
return buffers
end
return _M

View File

@ -0,0 +1,51 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_str = ffi.string
require "resty.openssl.include.rand"
local ctx_lib = require "resty.openssl.ctx"
local ctypes = require "resty.openssl.auxiliary.ctypes"
local format_error = require("resty.openssl.err").format_error
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local buf
local buf_size = 0
local function bytes(length, private, strength)
if type(length) ~= "number" then
return nil, "rand.bytes: expect a number at #1"
elseif strength and type(strength) ~= "number" then
return nil, "rand.bytes: expect a number at #3"
end
-- generally we don't need manually reseed rng
-- https://www.openssl.org/docs/man1.1.1/man3/RAND_seed.html
-- initialize or resize buffer
if not buf or buf_size < length then
buf = ctypes.uchar_array(length)
buf_size = length
end
local code
if OPENSSL_3X then
if private then
code = C.RAND_priv_bytes_ex(ctx_lib.get_libctx(), buf, length, strength or 0)
else
code = C.RAND_bytes_ex(ctx_lib.get_libctx(), buf, length, strength or 0)
end
else
if private then
code = C.RAND_priv_bytes(buf, length)
else
code = C.RAND_bytes(buf, length)
end
end
if code ~= 1 then
return nil, format_error("rand.bytes", code)
end
return ffi_str(buf, length)
end
return {
bytes = bytes,
}

View File

@ -0,0 +1,155 @@
local ffi = require "ffi"
local C = ffi.C
local bn_lib = require "resty.openssl.bn"
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local OPENSSL_11_OR_LATER = require("resty.openssl.version").OPENSSL_11_OR_LATER
local format_error = require("resty.openssl.err").format_error
local _M = {}
_M.params = {"n", "e", "d", "p", "q", "dmp1", "dmq1", "iqmp"}
local empty_table = {}
local bn_ptrptr_ct = ffi.typeof("const BIGNUM *[1]")
function _M.get_parameters(rsa_st)
-- {"n", "e", "d", "p", "q", "dmp1", "dmq1", "iqmp"}
return setmetatable(empty_table, {
__index = function(_, k)
local ptr, ret
if OPENSSL_11_OR_LATER then
ptr = bn_ptrptr_ct()
end
if k == 'n' then
if OPENSSL_11_OR_LATER then
C.RSA_get0_key(rsa_st, ptr, nil, nil)
end
elseif k == 'e' then
if OPENSSL_11_OR_LATER then
C.RSA_get0_key(rsa_st, nil, ptr, nil)
end
elseif k == 'd' then
if OPENSSL_11_OR_LATER then
C.RSA_get0_key(rsa_st, nil, nil, ptr)
end
elseif k == 'p' then
if OPENSSL_11_OR_LATER then
C.RSA_get0_factors(rsa_st, ptr, nil)
end
elseif k == 'q' then
if OPENSSL_11_OR_LATER then
C.RSA_get0_factors(rsa_st, nil, ptr)
end
elseif k == 'dmp1' then
if OPENSSL_11_OR_LATER then
C.RSA_get0_crt_params(rsa_st, ptr, nil, nil)
end
elseif k == 'dmq1' then
if OPENSSL_11_OR_LATER then
C.RSA_get0_crt_params(rsa_st, nil, ptr, nil)
end
elseif k == 'iqmp' then
if OPENSSL_11_OR_LATER then
C.RSA_get0_crt_params(rsa_st, nil, nil, ptr)
end
else
return nil, "rsa.get_parameters: unknown parameter \"" .. k .. "\" for RSA key"
end
if OPENSSL_11_OR_LATER then
ret = ptr[0]
elseif OPENSSL_10 then
ret = rsa_st[k]
end
if ret == nil then
return nil
end
return bn_lib.dup(ret)
end
}), nil
end
local function dup_bn_value(v)
if not bn_lib.istype(v) then
return nil, "expect value to be a bn instance"
end
local bn = C.BN_dup(v.ctx)
if bn == nil then
return nil, "BN_dup() failed"
end
return bn
end
function _M.set_parameters(rsa_st, opts)
local err
local opts_bn = {}
-- remember which parts of BNs has been added to rsa_st, they should be freed
-- by RSA_free and we don't cleanup them on failure
local cleanup_from_idx = 1
-- dup input
local do_set_key, do_set_factors, do_set_crt_params
for k, v in pairs(opts) do
opts_bn[k], err = dup_bn_value(v)
if err then
err = "rsa.set_parameters: cannot process parameter \"" .. k .. "\":" .. err
goto cleanup_with_error
end
if k == "n" or k == "e" or k == "d" then
do_set_key = true
elseif k == "p" or k == "q" then
do_set_factors = true
elseif k == "dmp1" or k == "dmq1" or k == "iqmp" then
do_set_crt_params = true
end
end
if OPENSSL_11_OR_LATER then
-- "The values n and e must be non-NULL the first time this function is called on a given RSA object."
-- thus we force to set them together
local code
if do_set_key then
code = C.RSA_set0_key(rsa_st, opts_bn["n"], opts_bn["e"], opts_bn["d"])
if code == 0 then
err = format_error("rsa.set_parameters: RSA_set0_key")
goto cleanup_with_error
end
end
cleanup_from_idx = cleanup_from_idx + 3
if do_set_factors then
code = C.RSA_set0_factors(rsa_st, opts_bn["p"], opts_bn["q"])
if code == 0 then
err = format_error("rsa.set_parameters: RSA_set0_factors")
goto cleanup_with_error
end
end
cleanup_from_idx = cleanup_from_idx + 2
if do_set_crt_params then
code = C.RSA_set0_crt_params(rsa_st, opts_bn["dmp1"], opts_bn["dmq1"], opts_bn["iqmp"])
if code == 0 then
err = format_error("rsa.set_parameters: RSA_set0_crt_params")
goto cleanup_with_error
end
end
return true
elseif OPENSSL_10 then
for k, v in pairs(opts_bn) do
if rsa_st[k] ~= nil then
C.BN_free(rsa_st[k])
end
rsa_st[k]= v
end
return true
end
::cleanup_with_error::
for i, k in pairs(_M.params) do
if i >= cleanup_from_idx then
C.BN_free(opts_bn[k])
end
end
return false, err
end
return _M

View File

@ -0,0 +1,353 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_str = ffi.string
local ffi_cast = ffi.cast
require "resty.openssl.include.ssl"
local nginx_aux = require("resty.openssl.auxiliary.nginx")
local x509_lib = require("resty.openssl.x509")
local chain_lib = require("resty.openssl.x509.chain")
local stack_lib = require("resty.openssl.stack")
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local OPENSSL_10 = require("resty.openssl.version").OPENSSL_10
local format_error = require("resty.openssl.err").format_error
local _M = {
SSL_VERIFY_NONE = 0x00,
SSL_VERIFY_PEER = 0x01,
SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02,
SSL_VERIFY_CLIENT_ONCE = 0x04,
SSL_VERIFY_POST_HANDSHAKE = 0x08,
}
local ops = {
SSL_OP_NO_EXTENDED_MASTER_SECRET = 0x00000001,
SSL_OP_CLEANSE_PLAINTEXT = 0x00000002,
SSL_OP_LEGACY_SERVER_CONNECT = 0x00000004,
SSL_OP_TLSEXT_PADDING = 0x00000010,
SSL_OP_SAFARI_ECDHE_ECDSA_BUG = 0x00000040,
SSL_OP_IGNORE_UNEXPECTED_EOF = 0x00000080,
SSL_OP_DISABLE_TLSEXT_CA_NAMES = 0x00000200,
SSL_OP_ALLOW_NO_DHE_KEX = 0x00000400,
SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = 0x00000800,
SSL_OP_NO_QUERY_MTU = 0x00001000,
SSL_OP_COOKIE_EXCHANGE = 0x00002000,
SSL_OP_NO_TICKET = 0x00004000,
SSL_OP_CISCO_ANYCONNECT = 0x00008000,
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000,
SSL_OP_NO_COMPRESSION = 0x00020000,
SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 0x00040000,
SSL_OP_NO_ENCRYPT_THEN_MAC = 0x00080000,
SSL_OP_ENABLE_MIDDLEBOX_COMPAT = 0x00100000,
SSL_OP_PRIORITIZE_CHACHA = 0x00200000,
SSL_OP_CIPHER_SERVER_PREFERENCE = 0x00400000,
SSL_OP_TLS_ROLLBACK_BUG = 0x00800000,
SSL_OP_NO_ANTI_REPLAY = 0x01000000,
SSL_OP_NO_SSLv3 = 0x02000000,
SSL_OP_NO_TLSv1 = 0x04000000,
SSL_OP_NO_TLSv1_2 = 0x08000000,
SSL_OP_NO_TLSv1_1 = 0x10000000,
SSL_OP_NO_TLSv1_3 = 0x20000000,
SSL_OP_NO_DTLSv1 = 0x04000000,
SSL_OP_NO_DTLSv1_2 = 0x08000000,
SSL_OP_NO_RENEGOTIATION = 0x40000000,
SSL_OP_CRYPTOPRO_TLSEXT_BUG = 0x80000000,
}
ops.SSL_OP_NO_SSL_MASK = ops.SSL_OP_NO_SSLv3 + ops.SSL_OP_NO_TLSv1 + ops.SSL_OP_NO_TLSv1_1
+ ops.SSL_OP_NO_TLSv1_2 + ops.SSL_OP_NO_TLSv1_3
ops.SSL_OP_NO_DTLS_MASK = ops.SSL_OP_NO_DTLSv1 + ops.SSL_OP_NO_DTLSv1_2
for k, v in pairs(ops) do
_M[k] = v
end
local mt = {__index = _M}
local ssl_ptr_ct = ffi.typeof('SSL*')
local stack_of_ssl_cipher_iter = function(ctx)
return stack_lib.mt_of("SSL_CIPHER", function(x) return x end, {}, true).__ipairs({ctx = ctx})
end
function _M.from_request()
-- don't GC this
local ctx, err = nginx_aux.get_req_ssl()
if err ~= nil then
return nil, err
end
return setmetatable({
ctx = ctx,
-- the cdata is not manage by Lua, don't GC on Lua side
_managed = false,
-- this is the client SSL session
_server = true,
}, mt)
end
function _M.from_socket(socket)
if not socket then
return nil, "expect a ngx.socket.tcp instance at #1"
end
-- don't GC this
local ctx, err = nginx_aux.get_socket_ssl(socket)
if err ~= nil then
return nil, err
end
return setmetatable({
ctx = ctx,
-- the cdata is not manage by Lua, don't GC on Lua side
_managed = false,
-- this is the client SSL session
_server = false,
}, mt)
end
function _M.istype(l)
return l and l.ctx and ffi.istype(ssl_ptr_ct, l.ctx)
end
function _M:get_peer_certificate()
local x509
if OPENSSL_3X then
x509 = C.SSL_get1_peer_certificate(self.ctx)
else
x509 = C.SSL_get_peer_certificate(self.ctx)
end
if x509 == nil then
return nil
end
ffi.gc(x509, C.X509_free)
local err
-- always copy, although the ref counter of returned x509 is
-- already increased by one.
x509, err = x509_lib.dup(x509)
if err then
return nil, err
end
return x509
end
function _M:get_peer_cert_chain()
local stack = C.SSL_get_peer_cert_chain(self.ctx)
if stack == nil then
return nil
end
return chain_lib.dup(stack)
end
-- TLSv1.3
function _M:set_ciphersuites(ciphers)
if C.SSL_set_ciphersuites(self.ctx, ciphers) ~= 1 then
return false, format_error("ssl:set_ciphers: SSL_set_ciphersuites")
end
return true
end
-- TLSv1.2 and lower
function _M:set_cipher_list(ciphers)
if C.SSL_set_cipher_list(self.ctx, ciphers) ~= 1 then
return false, format_error("ssl:set_ciphers: SSL_set_cipher_list")
end
return true
end
function _M:get_ciphers()
local ciphers = C.SSL_get_ciphers(self.ctx)
if ciphers == nil then
return nil
end
local ret = {}
for i, cipher in stack_of_ssl_cipher_iter(ciphers) do
cipher = C.SSL_CIPHER_get_name(cipher)
if cipher == nil then
return nil, format_error("ssl:get_ciphers: SSL_CIPHER_get_name")
end
ret[i] = ffi_str(cipher)
end
return table.concat(ret, ":")
end
function _M:get_cipher_name()
local cipher = C.SSL_get_current_cipher(self.ctx)
if cipher == nil then
return nil
end
cipher = C.SSL_CIPHER_get_name(cipher)
if cipher == nil then
return nil, format_error("ssl:get_cipher_name: SSL_CIPHER_get_name")
end
return ffi_str(cipher)
end
function _M:set_timeout(tm)
local session = C.SSL_get_session(self.ctx)
if session == nil then
return false, format_error("ssl:set_timeout: SSL_get_session")
end
if C.SSL_SESSION_set_timeout(session, tm) ~= 1 then
return false, format_error("ssl:set_timeout: SSL_SESSION_set_timeout")
end
return true
end
function _M:get_timeout()
local session = C.SSL_get_session(self.ctx)
if session == nil then
return false, format_error("ssl:get_timeout: SSL_get_session")
end
return tonumber(C.SSL_SESSION_get_timeout(session))
end
local ssl_verify_default_cb = ffi_cast("verify_callback", function()
return 1
end)
function _M:set_verify(mode, cb)
if self._verify_cb then
self._verify_cb:free()
end
if cb then
cb = ffi_cast("verify_callback", cb)
self._verify_cb = cb
end
C.SSL_set_verify(self.ctx, mode, cb or ssl_verify_default_cb)
return true
end
function _M:free_verify_cb()
if self._verify_cb then
self._verify_cb:free()
self._verify_cb = nil
end
end
function _M:add_client_ca(x509)
if not self._server then
return false, "ssl:add_client_ca is only supported on server side"
end
if not x509_lib.istype(x509) then
return false, "expect a x509 instance at #1"
end
if C.SSL_add_client_CA(self.ctx, x509.ctx) ~= 1 then
return false, format_error("ssl:add_client_ca: SSL_add_client_CA")
end
return true
end
function _M:set_options(...)
local bitmask = 0
for _, opt in ipairs({...}) do
bitmask = bit.bor(bitmask, opt)
end
if OPENSSL_10 then
bitmask = C.SSL_ctrl(self.ctx, 32, bitmask, nil) -- SSL_CTRL_OPTIONS
else
bitmask = C.SSL_set_options(self.ctx, bitmask)
end
return tonumber(bitmask)
end
function _M:get_options(readable)
local bitmask
if OPENSSL_10 then
bitmask = C.SSL_ctrl(self.ctx, 32, 0, nil) -- SSL_CTRL_OPTIONS
else
bitmask = C.SSL_get_options(self.ctx)
end
if not readable then
return tonumber(bitmask)
end
local ret = {}
for k, v in pairs(ops) do
if bit.band(v, bitmask) > 0 then
table.insert(ret, k)
end
end
table.sort(ret)
return ret
end
function _M:clear_options(...)
local bitmask = 0
for _, opt in ipairs({...}) do
bitmask = bit.bor(bitmask, opt)
end
if OPENSSL_10 then
bitmask = C.SSL_ctrl(self.ctx, 77, bitmask, nil) -- SSL_CTRL_CLEAR_OPTIONS
else
bitmask = C.SSL_clear_options(self.ctx, bitmask)
end
return tonumber(bitmask)
end
local valid_protocols = {
["SSLv3"] = ops.SSL_OP_NO_SSLv3,
["TLSv1"] = ops.SSL_OP_NO_TLSv1,
["TLSv1.1"] = ops.SSL_OP_NO_TLSv1_1,
["TLSv1.2"] = ops.SSL_OP_NO_TLSv1_2,
["TLSv1.3"] = ops.SSL_OP_NO_TLSv1_3,
}
local any_tlsv1 = ops.SSL_OP_NO_TLSv1_1 + ops.SSL_OP_NO_TLSv1_2 + ops.SSL_OP_NO_TLSv1_3
function _M:set_protocols(...)
local bitmask = 0
for _, prot in ipairs({...}) do
local b = valid_protocols[prot]
if not b then
return nil, "\"" .. prot .. "\" is not a valid protocol"
end
bitmask = bit.bor(bitmask, b)
end
if bit.band(bitmask, any_tlsv1) > 0 then
bitmask = bit.bor(bitmask, ops.SSL_OP_NO_TLSv1)
end
-- first disable all protocols
if OPENSSL_10 then
C.SSL_ctrl(self.ctx, 32, ops.SSL_OP_NO_SSL_MASK, nil) -- SSL_CTRL_OPTIONS
else
C.SSL_set_options(self.ctx, ops.SSL_OP_NO_SSL_MASK)
end
-- then enable selected protocols
if OPENSSL_10 then
return tonumber(C.SSL_clear_options(self.ctx, bitmask))
else
return tonumber(C.SSL_ctrl(self.ctx, 77, bitmask, nil)) -- SSL_CTRL_CLEAR_OPTIONS)
end
end
return _M

View File

@ -0,0 +1,95 @@
local ffi = require "ffi"
local C = ffi.C
local new_tab = table.new
local char = string.char
local concat = table.concat
require "resty.openssl.include.ssl"
local nginx_aux = require("resty.openssl.auxiliary.nginx")
local _M = {}
local mt = {__index = _M}
local ssl_ctx_ptr_ct = ffi.typeof('SSL_CTX*')
function _M.from_request()
-- don't GC this
local ctx, err = nginx_aux.get_req_ssl_ctx()
if err ~= nil then
return nil, err
end
return setmetatable({
ctx = ctx,
-- the cdata is not manage by Lua, don't GC on Lua side
_managed = false,
-- this is the Server SSL session
_server = true,
}, mt)
end
function _M.from_socket(socket)
if not socket then
return nil, "expect a ngx.socket.tcp instance at #1"
end
-- don't GC this
local ctx, err = nginx_aux.get_socket_ssl_ctx(socket)
if err ~= nil then
return nil, err
end
return setmetatable({
ctx = ctx,
-- the cdata is not manage by Lua, don't GC on Lua side
_managed = false,
-- this is the client SSL session
_server = false,
}, mt)
end
function _M.istype(l)
return l and l.ctx and ffi.istype(ssl_ctx_ptr_ct, l.ctx)
end
local function encode_alpn_wire(alpns)
local ret = new_tab(#alpns*2, 0)
for i, alpn in ipairs(alpns) do
ret[i*2-1] = char(#alpn)
ret[i*2] = alpn
end
return concat(ret, "")
end
function _M:set_alpns(alpns)
if not self._server then
return nil, "ssl_ctx:set_alpns is only supported on server side"
end
alpns = encode_alpn_wire(alpns)
if self._alpn_select_cb then
self._alpn_select_cb:free()
end
local alpn_select_cb = ffi.cast("SSL_CTX_alpn_select_cb_func", function(_, out, outlen, client, client_len)
local code = ffi.C.SSL_select_next_proto(
ffi.cast("unsigned char **", out), outlen,
alpns, #alpns,
client, client_len)
if code ~= 1 then -- OPENSSL_NPN_NEGOTIATED
return 3 -- SSL_TLSEXT_ERR_NOACK
end
return 0 -- SSL_TLSEXT_ERR_OK
end)
C.SSL_CTX_set_alpn_select_cb(self.ctx, alpn_select_cb, nil)
-- store the reference to avoid it being GC'ed
self._alpn_select_cb = alpn_select_cb
return true
end
return _M

View File

@ -0,0 +1,159 @@
--[[
The OpenSSL stack library. Note `safestack` is not usable here in ffi because
those symbols are eaten after preprocessing.
Instead, we should do a Lua land type checking by having a nested field indicating
which type of cdata its ctx holds.
]]
local ffi = require "ffi"
local C = ffi.C
local ffi_cast = ffi.cast
local ffi_gc = ffi.gc
local stack_macro = require "resty.openssl.include.stack"
local format_error = require("resty.openssl.err").format_error
local _M = {}
local function gc_of(typ)
local f = C[typ .. "_free"]
return function (st)
stack_macro.OPENSSL_sk_pop_free(st, f)
end
end
_M.gc_of = gc_of
_M.mt_of = function(typ, convert, index_tbl, no_gc)
if type(typ) ~= "string" then
error("expect a string at #1")
elseif type(convert) ~= "function" then
error("expect a function at #2")
end
local typ_ptr = typ .. "*"
-- starts from 0
local function value_at(ctx, i)
local elem = stack_macro.OPENSSL_sk_value(ctx, i)
if elem == nil then
error(format_error("OPENSSL_sk_value"))
end
local dup, err = convert(ffi_cast(typ_ptr, elem))
if err then
error(err)
end
return dup
end
local function iter(tbl)
if not tbl then error("instance is nil") end
local i = 0
local n = tonumber(stack_macro.OPENSSL_sk_num(tbl.ctx))
return function()
i = i + 1
if i <= n then
return i, value_at(tbl.ctx, i-1)
end
end
end
local ret = {
__pairs = iter,
__ipairs = iter,
__len = function(tbl)
if not tbl then error("instance is nil") end
return tonumber(stack_macro.OPENSSL_sk_num(tbl.ctx))
end,
__index = function(tbl, k)
if not tbl then error("instance is nil") end
local i = tonumber(k)
if not i then
return index_tbl[k]
end
local n = stack_macro.OPENSSL_sk_num(tbl.ctx)
if i <= 0 or i > n then
return nil
end
return value_at(tbl.ctx, i-1)
end,
}
if not no_gc then
ret.__gc = gc_of(typ)
end
return ret
end
_M.new_of = function(typ)
local gc = gc_of(typ)
return function()
local raw = stack_macro.OPENSSL_sk_new_null()
if raw == nil then
return nil, "stack.new_of: OPENSSL_sk_new_null() failed"
end
ffi_gc(raw, gc)
return raw
end
end
_M.add_of = function(typ)
local ptr = ffi.typeof(typ .. "*")
return function(stack, ctx)
if not stack then error("instance is nil") end
if ctx == nil or not ffi.istype(ptr, ctx) then
return false, "stack.add_of: expect a " .. typ .. "* at #1"
end
local code = stack_macro.OPENSSL_sk_push(stack, ctx)
if code == 0 then
return false, "stack.add_of: OPENSSL_sk_push() failed"
end
return true
end
end
local stack_ptr_ct = ffi.typeof("OPENSSL_STACK*")
_M.dup_of = function(_)
return function(ctx)
if ctx == nil or not ffi.istype(stack_ptr_ct, ctx) then
return nil, "stack.dup_of: expect a stack ctx at #1"
end
local ctx = stack_macro.OPENSSL_sk_dup(ctx)
if ctx == nil then
return nil, "stack.dup_of: OPENSSL_sk_dup() failed"
end
-- if the stack is duplicated: since we don't copy the elements
-- then we only control gc of the stack itself here
ffi_gc(ctx, stack_macro.OPENSSL_sk_free)
return ctx
end
end
-- fallback function to iterate if LUAJIT_ENABLE_LUA52COMPAT not enabled
_M.all_func = function(mt)
return function(stack)
if not stack then error("stack is nil") end
local ret = {}
local _next = mt.__pairs(stack)
while true do
local i, elem = _next()
if elem then
ret[i] = elem
else
break
end
end
return ret
end
end
_M.deep_copy_of = function(typ)
local dup = C[typ .. "_dup"]
local free = C[typ .. "_free"]
return function(ctx)
return stack_macro.OPENSSL_sk_deep_copy(ctx, dup, free)
end
end
return _M

View File

@ -0,0 +1,117 @@
-- https://github.com/GUI/lua-openssl-ffi/blob/master/lib/openssl-ffi/version.lua
local ffi = require "ffi"
local C = ffi.C
local ffi_str = ffi.string
ffi.cdef[[
// 1.0
unsigned long SSLeay(void);
const char *SSLeay_version(int t);
// >= 1.1
unsigned long OpenSSL_version_num();
const char *OpenSSL_version(int t);
// >= 3.0
const char *OPENSSL_info(int t);
// BoringSSL
int BORINGSSL_self_test(void);
]]
local version_func, info_func
local types_table
-- >= 1.1
local ok, version_num = pcall(function()
local num = C.OpenSSL_version_num()
version_func = C.OpenSSL_version
types_table = {
VERSION = 0,
CFLAGS = 1,
BUILT_ON = 2,
PLATFORM = 3,
DIR = 4,
ENGINES_DIR = 5,
VERSION_STRING = 6,
FULL_VERSION_STRING = 7,
MODULES_DIR = 8,
CPU_INFO = 9,
}
return num
end)
if not ok then
-- 1.0.x
ok, version_num = pcall(function()
local num = C.SSLeay()
version_func = C.SSLeay_version
types_table = {
VERSION = 0,
CFLAGS = 2,
BUILT_ON = 3,
PLATFORM = 4,
DIR = 5,
}
return num
end)
end
if not ok then
error(string.format("OpenSSL has encountered an error: %s; is OpenSSL library loaded?",
tostring(version_num)))
elseif type(version_num) == 'number' and version_num < 0x10000000 then
error(string.format("OpenSSL version %s is not supported", tostring(version_num or 0)))
elseif not version_num then
error("Can not get OpenSSL version")
end
if version_num >= 0x30000000 then
local info_table = {
INFO_CONFIG_DIR = 1001,
INFO_ENGINES_DIR = 1002,
INFO_MODULES_DIR = 1003,
INFO_DSO_EXTENSION = 1004,
INFO_DIR_FILENAME_SEPARATOR = 1005,
INFO_LIST_SEPARATOR = 1006,
INFO_SEED_SOURCE = 1007,
INFO_CPU_SETTINGS = 1008,
}
for k, v in pairs(info_table) do
types_table[k] = v
end
info_func = C.OPENSSL_info
else
info_func = function(_)
error(string.format("OPENSSL_info is not supported on %s", ffi_str(version_func(0))))
end
end
local BORINGSSL = false
pcall(function()
local _ = C.BORINGSSL_self_test
BORINGSSL = true
end)
return setmetatable({
version_num = tonumber(version_num),
version_text = ffi_str(version_func(0)),
version = function(t)
return ffi_str(version_func(t))
end,
info = function(t)
return ffi_str(info_func(t))
end,
OPENSSL_3X = version_num >= 0x30000000 and version_num < 0x30200000,
OPENSSL_30 = version_num >= 0x30000000 and version_num < 0x30100000, -- for backward compat, deprecated
OPENSSL_11 = version_num >= 0x10100000 and version_num < 0x10200000,
OPENSSL_111 = version_num >= 0x10101000 and version_num < 0x10200000,
OPENSSL_11_OR_LATER = version_num >= 0x10100000 and version_num < 0x30200000,
OPENSSL_111_OR_LATER = version_num >= 0x10101000 and version_num < 0x30200000,
OPENSSL_10 = version_num < 0x10100000 and version_num > 0x10000000,
BORINGSSL = BORINGSSL,
BORINGSSL_110 = BORINGSSL and version_num >= 0x10100000 and version_num < 0x10101000
}, {
__index = types_table,
})

View File

@ -0,0 +1,251 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_cast = ffi.cast
local ffi_str = ffi.string
require "resty.openssl.include.x509"
require "resty.openssl.include.x509v3"
local asn1_macro = require "resty.openssl.include.asn1"
local stack_lib = require "resty.openssl.stack"
local name_lib = require "resty.openssl.x509.name"
local altname_macro = require "resty.openssl.include.x509.altname"
local _M = {}
local general_names_ptr_ct = ffi.typeof("GENERAL_NAMES*")
local STACK = "GENERAL_NAME"
local new = stack_lib.new_of(STACK)
local add = stack_lib.add_of(STACK)
local dup = stack_lib.dup_of(STACK)
local types = altname_macro.types
local AF_INET = 2
local AF_INET6 = 10
if ffi.os == "OSX" then
AF_INET6 = 30
elseif ffi.os == "BSD" then
AF_INET6 = 28
elseif ffi.os == "Windows" then
AF_INET6 = 23
end
ffi.cdef [[
typedef int socklen_t;
int inet_pton(int af, const char *restrict src, void *restrict dst);
const char *inet_ntop(int af, const void *restrict src,
char *restrict dst, socklen_t size);
]]
local ip_buffer = ffi.new("unsigned char [46]") -- 46 bytes enough for both string ipv6 and binary ipv6
-- similar to GENERAL_NAME_print, but returns value instead of print
local gn_decode = function(ctx)
local typ = ctx.type
local k = altname_macro.literals[typ]
local v
if typ == types.OtherName then
v = "OtherName:<unsupported>"
elseif typ == types.RFC822Name then
v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.rfc822Name))
elseif typ == types.DNS then
v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.dNSName))
elseif typ == types.X400 then
v = "X400:<unsupported>"
elseif typ == types.DirName then
v = name_lib.dup(ctx.d.directoryName)
elseif typ == types.EdiParty then
v = "EdiParty:<unsupported>"
elseif typ == types.URI then
v = ffi_str(asn1_macro.ASN1_STRING_get0_data(ctx.d.uniformResourceIdentifier))
elseif typ == types.IP then
v = asn1_macro.ASN1_STRING_get0_data(ctx.d.iPAddress)
local l = tonumber(C.ASN1_STRING_length(ctx.d.iPAddress))
if l ~= 4 and l ~= 16 then
error("Unknown IP address type")
end
v = C.inet_ntop(l == 4 and AF_INET or AF_INET6, v, ip_buffer, 46)
v = ffi_str(v)
elseif typ == types.RID then
v = "RID:<unsupported>"
else
error("unknown type" .. typ .. "-> " .. types.OtherName)
end
return { k, v }
end
-- shared with info_access
_M.gn_decode = gn_decode
local mt = stack_lib.mt_of(STACK, gn_decode, _M)
local mt__pairs = mt.__pairs
mt.__pairs = function(tbl)
local f = mt__pairs(tbl)
return function()
local _, e = f()
if not e then return end
return unpack(e)
end
end
function _M.new()
local ctx = new()
if ctx == nil then
return nil, "x509.altname.new: OPENSSL_sk_new_null() failed"
end
local cast = ffi_cast("GENERAL_NAMES*", ctx)
local self = setmetatable({
ctx = ctx,
cast = cast,
_is_shallow_copy = false,
}, mt)
return self, nil
end
function _M.istype(l)
return l and l.cast and ffi.istype(general_names_ptr_ct, l.cast)
end
function _M.dup(ctx)
if ctx == nil or not ffi.istype(general_names_ptr_ct, ctx) then
return nil, "x509.altname.dup: expect a GENERAL_NAMES* ctx at #1"
end
local dup_ctx, err = dup(ctx)
if dup_ctx == nil then
return nil, err
end
return setmetatable({
cast = ffi_cast("GENERAL_NAMES*", dup_ctx),
ctx = dup_ctx,
-- don't let lua gc the original stack to keep its elements
_dupped_from = ctx,
_is_shallow_copy = true,
_elem_refs = {},
_elem_refs_idx = 1,
}, mt), nil
end
local function gn_set(gn, typ, value)
if type(typ) ~= 'string' then
return "x509.altname:gn_set: expect a string at #1"
end
local typ_lower = typ:lower()
if type(value) ~= 'string' then
return "x509.altname:gn_set: except a string at #2"
end
local txt = value
local gn_type = types[typ_lower]
if not gn_type then
return "x509.altname:gn_set: unknown type " .. typ
end
if gn_type == types.IP then
if C.inet_pton(AF_INET, txt, ip_buffer) == 1 then
txt = ffi_str(ip_buffer, 4)
elseif C.inet_pton(AF_INET6, txt, ip_buffer) == 1 then
txt = ffi_str(ip_buffer, 16)
else
return "x509.altname:gn_set: invalid IP address " .. txt
end
elseif gn_type ~= types.Email and
gn_type ~= types.URI and
gn_type ~= types.DNS then
return "x509.altname:gn_set: setting type " .. typ .. " is currently not supported"
end
gn.type = gn_type
local asn1_string = C.ASN1_IA5STRING_new()
if asn1_string == nil then
return "x509.altname:gn_set: ASN1_STRING_type_new() failed"
end
local code = C.ASN1_STRING_set(asn1_string, txt, #txt)
if code ~= 1 then
C.ASN1_STRING_free(asn1_string)
return "x509.altname:gn_set: ASN1_STRING_set() failed: " .. code
end
gn.d.ia5 = asn1_string
end
-- shared with info_access
_M.gn_set = gn_set
function _M:add(typ, value)
-- the stack element stays with stack
-- we shouldn't add gc handler if it's already been
-- pushed to stack. instead, rely on the gc handler
-- of the stack to release all memories
local gn = C.GENERAL_NAME_new()
if gn == nil then
return nil, "x509.altname:add: GENERAL_NAME_new() failed"
end
local err = gn_set(gn, typ, value)
if err then
C.GENERAL_NAME_free(gn)
return nil, err
end
local _, err = add(self.ctx, gn)
if err then
C.GENERAL_NAME_free(gn)
return nil, err
end
-- if the stack is duplicated, the gc handler is not pop_free
-- handle the gc by ourselves
if self._is_shallow_copy then
ffi_gc(gn, C.GENERAL_NAME_free)
self._elem_refs[self._elem_refs_idx] = gn
self._elem_refs_idx = self._elem_refs_idx + 1
end
return self
end
_M.all = function(self)
local ret = {}
local _next = mt.__pairs(self)
while true do
local k, v = _next()
if k then
ret[k] = v
else
break
end
end
return ret
end
_M.each = mt.__pairs
_M.index = mt.__index
_M.count = mt.__len
mt.__tostring = function(self)
local values = {}
local _next = mt.__pairs(self)
while true do
local k, v = _next()
if k then
table.insert(values, k .. "=" .. v)
else
break
end
end
table.sort(values)
return table.concat(values, "/")
end
_M.tostring = mt.__tostring
return _M

View File

@ -0,0 +1,79 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local stack_lib = require "resty.openssl.stack"
local x509_lib = require "resty.openssl.x509"
local format_error = require("resty.openssl.err").format_error
local _M = {}
local stack_ptr_ct = ffi.typeof("OPENSSL_STACK*")
local STACK = "X509"
local gc = stack_lib.gc_of(STACK)
local new = stack_lib.new_of(STACK)
local add = stack_lib.add_of(STACK)
local mt = stack_lib.mt_of(STACK, x509_lib.dup, _M)
function _M.new()
local raw, err = new()
if raw == nil then
return nil, err
end
local self = setmetatable({
stack_of = STACK,
ctx = raw,
}, mt)
return self, nil
end
function _M.istype(l)
return l and l.ctx and ffi.istype(stack_ptr_ct, l.ctx)
and l.stack_of and l.stack_of == STACK
end
function _M.dup(ctx)
if ctx == nil or not ffi.istype(stack_ptr_ct, ctx) then
return nil, "x509.chain.dup: expect a stack ctx at #1, got " .. type(ctx)
end
-- sk_X509_dup plus up ref for each X509 element
local ctx = C.X509_chain_up_ref(ctx)
if ctx == nil then
return nil, "x509.chain.dup: X509_chain_up_ref() failed"
end
ffi_gc(ctx, gc)
return setmetatable({
stack_of = STACK,
ctx = ctx,
}, mt)
end
function _M:add(x509)
if not x509_lib.istype(x509) then
return nil, "x509.chain:add: expect a x509 instance at #1"
end
local dup = C.X509_dup(x509.ctx)
if dup == nil then
return nil, format_error("x509.chain:add: X509_dup")
end
local _, err = add(self.ctx, dup)
if err then
C.X509_free(dup)
return nil, err
end
return true
end
_M.all = stack_lib.all_func(mt)
_M.each = mt.__ipairs
_M.index = mt.__index
_M.count = mt.__len
return _M

View File

@ -0,0 +1,607 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
require "resty.openssl.include.x509.crl"
require "resty.openssl.include.pem"
require "resty.openssl.include.x509v3"
local asn1_lib = require("resty.openssl.asn1")
local bn_lib = require("resty.openssl.bn")
local revoked_lib = require("resty.openssl.x509.revoked")
local digest_lib = require("resty.openssl.digest")
local extension_lib = require("resty.openssl.x509.extension")
local pkey_lib = require("resty.openssl.pkey")
local bio_util = require "resty.openssl.auxiliary.bio"
local ctx_lib = require "resty.openssl.ctx"
local stack_lib = require "resty.openssl.stack"
local txtnid2nid = require("resty.openssl.objects").txtnid2nid
local find_sigid_algs = require("resty.openssl.objects").find_sigid_algs
local format_error = require("resty.openssl.err").format_error
local version = require("resty.openssl.version")
local OPENSSL_10 = version.OPENSSL_10
local OPENSSL_11_OR_LATER = version.OPENSSL_11_OR_LATER
local OPENSSL_3X = version.OPENSSL_3X
local BORINGSSL = version.BORINGSSL
local BORINGSSL_110 = version.BORINGSSL_110 -- used in boringssl-fips-20190808
local accessors = {}
accessors.set_issuer_name = C.X509_CRL_set_issuer_name
accessors.set_version = C.X509_CRL_set_version
if OPENSSL_11_OR_LATER and not BORINGSSL_110 then
accessors.get_last_update = C.X509_CRL_get0_lastUpdate
accessors.set_last_update = C.X509_CRL_set1_lastUpdate
accessors.get_next_update = C.X509_CRL_get0_nextUpdate
accessors.set_next_update = C.X509_CRL_set1_nextUpdate
accessors.get_version = C.X509_CRL_get_version
accessors.get_issuer_name = C.X509_CRL_get_issuer -- returns internal ptr
accessors.get_signature_nid = C.X509_CRL_get_signature_nid
-- BORINGSSL_110 exports X509_CRL_get_signature_nid, but just ignored for simplicity
accessors.get_revoked = C.X509_CRL_get_REVOKED
elseif OPENSSL_10 or BORINGSSL_110 then
accessors.get_last_update = function(crl)
if crl == nil or crl.crl == nil then
return nil
end
return crl.crl.lastUpdate
end
accessors.set_last_update = C.X509_CRL_set_lastUpdate
accessors.get_next_update = function(crl)
if crl == nil or crl.crl == nil then
return nil
end
return crl.crl.nextUpdate
end
accessors.set_next_update = C.X509_CRL_set_nextUpdate
accessors.get_version = function(crl)
if crl == nil or crl.crl == nil then
return nil
end
return C.ASN1_INTEGER_get(crl.crl.version)
end
accessors.get_issuer_name = function(crl)
if crl == nil or crl.crl == nil then
return nil
end
return crl.crl.issuer
end
accessors.get_signature_nid = function(crl)
if crl == nil or crl.crl == nil or crl.crl.sig_alg == nil then
return nil
end
return C.OBJ_obj2nid(crl.crl.sig_alg.algorithm)
end
accessors.get_revoked = function(crl)
return crl.crl.revoked
end
end
local function __tostring(self, fmt)
if not fmt or fmt == 'PEM' then
return bio_util.read_wrap(C.PEM_write_bio_X509_CRL, self.ctx)
elseif fmt == 'DER' then
return bio_util.read_wrap(C.i2d_X509_CRL_bio, self.ctx)
else
return nil, "x509.crl:tostring: can only write PEM or DER format, not " .. fmt
end
end
local _M = {}
local mt = { __index = _M, __tostring = __tostring }
local x509_crl_ptr_ct = ffi.typeof("X509_CRL*")
function _M.new(crl, fmt, properties)
local ctx
if not crl then
if OPENSSL_3X then
ctx = C.X509_CRL_new_ex(ctx_lib.get_libctx(), properties)
else
ctx = C.X509_CRL_new()
end
if ctx == nil then
return nil, "x509.crl.new: X509_CRL_new() failed"
end
elseif type(crl) == "string" then
-- routine for load an existing csr
local bio = C.BIO_new_mem_buf(crl, #crl)
if bio == nil then
return nil, format_error("x509.crl.new: BIO_new_mem_buf")
end
fmt = fmt or "*"
while true do -- luacheck: ignore 512 -- loop is executed at most once
if fmt == "PEM" or fmt == "*" then
ctx = C.PEM_read_bio_X509_CRL(bio, nil, nil, nil)
if ctx ~= nil then
break
elseif fmt == "*" then
-- BIO_reset; #define BIO_CTRL_RESET 1
local code = C.BIO_ctrl(bio, 1, 0, nil)
if code ~= 1 then
return nil, "x509.crl.new: BIO_ctrl() failed: " .. code
end
end
end
if fmt == "DER" or fmt == "*" then
ctx = C.d2i_X509_CRL_bio(bio, nil)
end
break
end
C.BIO_free(bio)
if ctx == nil then
return nil, format_error("x509.crl.new")
end
-- clear errors occur when trying
C.ERR_clear_error()
else
return nil, "x509.crl.new: expect nil or a string at #1"
end
ffi_gc(ctx, C.X509_CRL_free)
local self = setmetatable({
ctx = ctx,
}, mt)
return self, nil
end
function _M.istype(l)
return l and l and l.ctx and ffi.istype(x509_crl_ptr_ct, l.ctx)
end
function _M.dup(ctx)
if not ffi.istype(x509_crl_ptr_ct, ctx) then
return nil, "x509.crl.dup: expect a x509.crl ctx at #1"
end
local ctx = C.X509_CRL_dup(ctx)
if ctx == nil then
return nil, "x509.crl.dup: X509_CRL_dup() failed"
end
ffi_gc(ctx, C.X509_CRL_free)
local self = setmetatable({
ctx = ctx,
}, mt)
return self, nil
end
function _M:tostring(fmt)
return __tostring(self, fmt)
end
function _M:to_PEM()
return __tostring(self, "PEM")
end
function _M:text()
return bio_util.read_wrap(C.X509_CRL_print, self.ctx)
end
local function revoked_decode(ctx)
if OPENSSL_10 then
error("x509.crl:revoked_decode: not supported on OpenSSL 1.0")
end
local ret = {}
local serial = C.X509_REVOKED_get0_serialNumber(ctx)
if serial ~= nil then
serial = C.ASN1_INTEGER_to_BN(serial, nil)
if serial == nil then
error("x509.crl:revoked_decode: ASN1_INTEGER_to_BN() failed")
end
ffi_gc(serial, C.BN_free)
ret["serial_number"] = bn_lib.to_hex({ctx = serial})
end
local date = C.X509_REVOKED_get0_revocationDate(ctx)
if date ~= nil then
date = asn1_lib.asn1_to_unix(date)
ret["revocation_date"] = date
end
return ret
end
local revoked_mt = stack_lib.mt_of("X509_REVOKED", revoked_decode, _M)
local function nil_iter() return nil end
local function revoked_iter(self)
local stack = accessors.get_revoked(self.ctx)
if stack == nil then
return nil_iter
end
return revoked_mt.__ipairs({ctx = stack})
end
mt.__pairs = revoked_iter
mt.__ipairs = revoked_iter
mt.__index = function(self, k)
local i = tonumber(k)
if not i then
return _M[k]
end
local stack = accessors.get_revoked(self.ctx)
if stack == nil then
return nil
end
return revoked_mt.__index({ctx = stack}, i)
end
mt.__len = function(self)
local stack = accessors.get_revoked(self.ctx)
if stack == nil then
return 0
end
return revoked_mt.__len({ctx = stack})
end
_M.all = function(self)
local ret = {}
local _next = mt.__pairs(self)
while true do
local k, v = _next()
if k then
ret[k] = v
else
break
end
end
return ret
end
_M.each = mt.__pairs
_M.index = mt.__index
_M.count = mt.__len
--- Adds revoked item to stack of revoked certificates of crl
-- @tparam table Instance of crl module
-- @tparam table Instance of revoked module
-- @treturn boolean true if revoked item was successfully added or false otherwise
-- @treturn[opt] string Returns optional error message in case of error
function _M:add_revoked(revoked)
if not revoked_lib.istype(revoked) then
return false, "x509.crl:add_revoked: expect a revoked instance at #1"
end
local ctx = C.X509_REVOKED_dup(revoked.ctx)
if ctx == nil then
return nil, "x509.crl:add_revoked: X509_REVOKED_dup() failed"
end
if C.X509_CRL_add0_revoked(self.ctx, ctx) == 0 then
return false, format_error("x509.crl:add_revoked")
end
return true
end
local ptr_ptr_of_x509_revoked = ffi.typeof("X509_REVOKED*[1]")
function _M:get_by_serial(sn)
local bn, err
if bn_lib.istype(sn) then
bn = sn
elseif type(sn) == "string" then
bn, err = bn_lib.from_hex(sn)
if err then
return nil, "x509.crl:find: can't decode bn: " .. err
end
else
return nil, "x509.crl:find: expect a bn instance at #1"
end
local sn_asn1 = C.BN_to_ASN1_INTEGER(bn.ctx, nil)
if sn_asn1 == nil then
return nil, "x509.crl:find: BN_to_ASN1_INTEGER() failed"
end
ffi_gc(sn_asn1, C.ASN1_INTEGER_free)
local pp = ptr_ptr_of_x509_revoked()
local code = C.X509_CRL_get0_by_serial(self.ctx, pp, sn_asn1)
if code == 1 then
return revoked_decode(pp[0])
elseif code == 2 then
return nil, "not revoked (removeFromCRL)"
end
-- 0 or other
return nil
end
-- START AUTO GENERATED CODE
-- AUTO GENERATED
function _M:sign(pkey, digest)
if not pkey_lib.istype(pkey) then
return false, "x509.crl:sign: expect a pkey instance at #1"
end
local digest_algo
if digest then
if not digest_lib.istype(digest) then
return false, "x509.crl:sign: expect a digest instance at #2"
elseif not digest.algo then
return false, "x509.crl:sign: expect a digest instance to have algo member"
end
digest_algo = digest.algo
elseif BORINGSSL then
digest_algo = C.EVP_get_digestbyname('sha256')
end
-- returns size of signature if success
if C.X509_CRL_sign(self.ctx, pkey.ctx, digest_algo) == 0 then
return false, format_error("x509.crl:sign")
end
return true
end
-- AUTO GENERATED
function _M:verify(pkey)
if not pkey_lib.istype(pkey) then
return false, "x509.crl:verify: expect a pkey instance at #1"
end
local code = C.X509_CRL_verify(self.ctx, pkey.ctx)
if code == 1 then
return true
elseif code == 0 then
return false
else -- typically -1
return false, format_error("x509.crl:verify", code)
end
end
-- AUTO GENERATED
local function get_extension(ctx, nid_txt, last_pos)
last_pos = (last_pos or 0) - 1
local nid, err = txtnid2nid(nid_txt)
if err then
return nil, nil, err
end
local pos = C.X509_CRL_get_ext_by_NID(ctx, nid, last_pos)
if pos == -1 then
return nil
end
local ctx = C.X509_CRL_get_ext(ctx, pos)
if ctx == nil then
return nil, nil, format_error()
end
return ctx, pos
end
-- AUTO GENERATED
function _M:add_extension(extension)
if not extension_lib.istype(extension) then
return false, "x509.crl:add_extension: expect a x509.extension instance at #1"
end
-- X509_CRL_add_ext returnes the stack on success, and NULL on error
-- the X509_EXTENSION ctx is dupped internally
if C.X509_CRL_add_ext(self.ctx, extension.ctx, -1) == nil then
return false, format_error("x509.crl:add_extension")
end
return true
end
-- AUTO GENERATED
function _M:get_extension(nid_txt, last_pos)
local ctx, pos, err = get_extension(self.ctx, nid_txt, last_pos)
if err then
return nil, nil, "x509.crl:get_extension: " .. err
end
local ext, err = extension_lib.dup(ctx)
if err then
return nil, nil, "x509.crl:get_extension: " .. err
end
return ext, pos+1
end
local X509_CRL_delete_ext
if OPENSSL_11_OR_LATER then
X509_CRL_delete_ext = C.X509_CRL_delete_ext
elseif OPENSSL_10 then
X509_CRL_delete_ext = function(ctx, pos)
return C.X509v3_delete_ext(ctx.crl.extensions, pos)
end
else
X509_CRL_delete_ext = function(...)
error("X509_CRL_delete_ext undefined")
end
end
-- AUTO GENERATED
function _M:set_extension(extension, last_pos)
if not extension_lib.istype(extension) then
return false, "x509.crl:set_extension: expect a x509.extension instance at #1"
end
last_pos = (last_pos or 0) - 1
local nid = extension:get_object().nid
local pos = C.X509_CRL_get_ext_by_NID(self.ctx, nid, last_pos)
-- pos may be -1, which means not found, it's fine, we will add new one instead of replace
local removed = X509_CRL_delete_ext(self.ctx, pos)
C.X509_EXTENSION_free(removed)
if C.X509_CRL_add_ext(self.ctx, extension.ctx, pos) == nil then
return false, format_error("x509.crl:set_extension")
end
return true
end
-- AUTO GENERATED
function _M:set_extension_critical(nid_txt, crit, last_pos)
local ctx, _, err = get_extension(self.ctx, nid_txt, last_pos)
if err then
return nil, "x509.crl:set_extension_critical: " .. err
end
if C.X509_EXTENSION_set_critical(ctx, crit and 1 or 0) ~= 1 then
return false, format_error("x509.crl:set_extension_critical")
end
return true
end
-- AUTO GENERATED
function _M:get_extension_critical(nid_txt, last_pos)
local ctx, _, err = get_extension(self.ctx, nid_txt, last_pos)
if err then
return nil, "x509.crl:get_extension_critical: " .. err
end
return C.X509_EXTENSION_get_critical(ctx) == 1
end
-- AUTO GENERATED
function _M:get_issuer_name()
local got = accessors.get_issuer_name(self.ctx)
if got == nil then
return nil
end
local lib = require("resty.openssl.x509.name")
-- the internal ptr is returned, ie we need to copy it
return lib.dup(got)
end
-- AUTO GENERATED
function _M:set_issuer_name(toset)
local lib = require("resty.openssl.x509.name")
if lib.istype and not lib.istype(toset) then
return false, "x509.crl:set_issuer_name: expect a x509.name instance at #1"
end
toset = toset.ctx
if accessors.set_issuer_name(self.ctx, toset) == 0 then
return false, format_error("x509.crl:set_issuer_name")
end
return true
end
-- AUTO GENERATED
function _M:get_last_update()
local got = accessors.get_last_update(self.ctx)
if got == nil then
return nil
end
got = asn1_lib.asn1_to_unix(got)
return got
end
-- AUTO GENERATED
function _M:set_last_update(toset)
if type(toset) ~= "number" then
return false, "x509.crl:set_last_update: expect a number at #1"
end
toset = C.ASN1_TIME_set(nil, toset)
ffi_gc(toset, C.ASN1_STRING_free)
if accessors.set_last_update(self.ctx, toset) == 0 then
return false, format_error("x509.crl:set_last_update")
end
return true
end
-- AUTO GENERATED
function _M:get_next_update()
local got = accessors.get_next_update(self.ctx)
if got == nil then
return nil
end
got = asn1_lib.asn1_to_unix(got)
return got
end
-- AUTO GENERATED
function _M:set_next_update(toset)
if type(toset) ~= "number" then
return false, "x509.crl:set_next_update: expect a number at #1"
end
toset = C.ASN1_TIME_set(nil, toset)
ffi_gc(toset, C.ASN1_STRING_free)
if accessors.set_next_update(self.ctx, toset) == 0 then
return false, format_error("x509.crl:set_next_update")
end
return true
end
-- AUTO GENERATED
function _M:get_version()
local got = accessors.get_version(self.ctx)
if got == nil then
return nil
end
got = tonumber(got) + 1
return got
end
-- AUTO GENERATED
function _M:set_version(toset)
if type(toset) ~= "number" then
return false, "x509.crl:set_version: expect a number at #1"
end
-- Note: this is defined by standards (X.509 et al) to be one less than the certificate version.
-- So a version 3 certificate will return 2 and a version 1 certificate will return 0.
toset = toset - 1
if accessors.set_version(self.ctx, toset) == 0 then
return false, format_error("x509.crl:set_version")
end
return true
end
-- AUTO GENERATED
function _M:get_signature_nid()
local nid = accessors.get_signature_nid(self.ctx)
if nid <= 0 then
return nil, format_error("x509.crl:get_signature_nid")
end
return nid
end
-- AUTO GENERATED
function _M:get_signature_name()
local nid = accessors.get_signature_nid(self.ctx)
if nid <= 0 then
return nil, format_error("x509.crl:get_signature_name")
end
return ffi.string(C.OBJ_nid2sn(nid))
end
-- AUTO GENERATED
function _M:get_signature_digest_name()
local nid = accessors.get_signature_nid(self.ctx)
if nid <= 0 then
return nil, format_error("x509.crl:get_signature_digest_name")
end
local nid = find_sigid_algs(nid)
return ffi.string(C.OBJ_nid2sn(nid))
end
-- END AUTO GENERATED CODE
return _M

View File

@ -0,0 +1,531 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_cast = ffi.cast
require "resty.openssl.include.pem"
require "resty.openssl.include.x509v3"
require "resty.openssl.include.x509.csr"
require "resty.openssl.include.asn1"
local stack_macro = require "resty.openssl.include.stack"
local stack_lib = require "resty.openssl.stack"
local pkey_lib = require "resty.openssl.pkey"
local digest_lib = require("resty.openssl.digest")
local extension_lib = require("resty.openssl.x509.extension")
local extensions_lib = require("resty.openssl.x509.extensions")
local bio_util = require "resty.openssl.auxiliary.bio"
local ctypes = require "resty.openssl.auxiliary.ctypes"
local ctx_lib = require "resty.openssl.ctx"
local txtnid2nid = require("resty.openssl.objects").txtnid2nid
local find_sigid_algs = require("resty.openssl.objects").find_sigid_algs
local format_error = require("resty.openssl.err").format_error
local version = require("resty.openssl.version")
local OPENSSL_10 = version.OPENSSL_10
local OPENSSL_11_OR_LATER = version.OPENSSL_11_OR_LATER
local OPENSSL_3X = version.OPENSSL_3X
local BORINGSSL = version.BORINGSSL
local BORINGSSL_110 = version.BORINGSSL_110 -- used in boringssl-fips-20190808
local accessors = {}
accessors.set_subject_name = C.X509_REQ_set_subject_name
accessors.get_pubkey = C.X509_REQ_get_pubkey
accessors.set_pubkey = C.X509_REQ_set_pubkey
accessors.set_version = C.X509_REQ_set_version
if OPENSSL_11_OR_LATER or BORINGSSL_110 then
accessors.get_signature_nid = C.X509_REQ_get_signature_nid
elseif OPENSSL_10 then
accessors.get_signature_nid = function(csr)
if csr == nil or csr.sig_alg == nil then
return nil
end
return C.OBJ_obj2nid(csr.sig_alg.algorithm)
end
end
if OPENSSL_11_OR_LATER and not BORINGSSL_110 then
accessors.get_subject_name = C.X509_REQ_get_subject_name -- returns internal ptr
accessors.get_version = C.X509_REQ_get_version
elseif OPENSSL_10 or BORINGSSL_110 then
accessors.get_subject_name = function(csr)
if csr == nil or csr.req_info == nil then
return nil
end
return csr.req_info.subject
end
accessors.get_version = function(csr)
if csr == nil or csr.req_info == nil then
return nil
end
return C.ASN1_INTEGER_get(csr.req_info.version)
end
end
local function __tostring(self, fmt)
if not fmt or fmt == 'PEM' then
return bio_util.read_wrap(C.PEM_write_bio_X509_REQ, self.ctx)
elseif fmt == 'DER' then
return bio_util.read_wrap(C.i2d_X509_REQ_bio, self.ctx)
else
return nil, "x509.csr:tostring: can only write PEM or DER format, not " .. fmt
end
end
local _M = {}
local mt = { __index = _M, __tostring = __tostring }
local x509_req_ptr_ct = ffi.typeof("X509_REQ*")
local stack_ptr_type = ffi.typeof("struct stack_st *[1]")
local x509_extensions_gc = stack_lib.gc_of("X509_EXTENSION")
function _M.new(csr, fmt, properties)
local ctx
if not csr then
if OPENSSL_3X then
ctx = C.X509_REQ_new_ex(ctx_lib.get_libctx(), properties)
else
ctx = C.X509_REQ_new()
end
if ctx == nil then
return nil, "x509.csr.new: X509_REQ_new() failed"
end
elseif type(csr) == "string" then
-- routine for load an existing csr
local bio = C.BIO_new_mem_buf(csr, #csr)
if bio == nil then
return nil, format_error("x509.csr.new: BIO_new_mem_buf")
end
fmt = fmt or "*"
while true do -- luacheck: ignore 512 -- loop is executed at most once
if fmt == "PEM" or fmt == "*" then
ctx = C.PEM_read_bio_X509_REQ(bio, nil, nil, nil)
if ctx ~= nil then
break
elseif fmt == "*" then
-- BIO_reset; #define BIO_CTRL_RESET 1
local code = C.BIO_ctrl(bio, 1, 0, nil)
if code ~= 1 then
return nil, "x509.csr.new: BIO_ctrl() failed: " .. code
end
end
end
if fmt == "DER" or fmt == "*" then
ctx = C.d2i_X509_REQ_bio(bio, nil)
end
break
end
C.BIO_free(bio)
if ctx == nil then
return nil, format_error("x509.csr.new")
end
-- clear errors occur when trying
C.ERR_clear_error()
else
return nil, "x509.csr.new: expect nil or a string at #1"
end
ffi_gc(ctx, C.X509_REQ_free)
local self = setmetatable({
ctx = ctx,
}, mt)
return self, nil
end
function _M.istype(l)
return l and l and l.ctx and ffi.istype(x509_req_ptr_ct, l.ctx)
end
function _M:tostring(fmt)
return __tostring(self, fmt)
end
function _M:to_PEM()
return __tostring(self, "PEM")
end
function _M:check_private_key(key)
if not pkey_lib.istype(key) then
return false, "x509.csr:check_private_key: except a pkey instance at #1"
end
if not key:is_private() then
return false, "x509.csr:check_private_key: not a private key"
end
if C.X509_REQ_check_private_key(self.ctx, key.ctx) == 1 then
return true
end
return false, format_error("x509.csr:check_private_key")
end
--- Get all csr extensions
-- @tparam table self Instance of csr
-- @treturn Extensions object
function _M:get_extensions()
local extensions = C.X509_REQ_get_extensions(self.ctx)
-- GC handler is sk_X509_EXTENSION_pop_free
ffi_gc(extensions, x509_extensions_gc)
return extensions_lib.dup(extensions)
end
local function get_extension(ctx, nid_txt, last_pos)
local nid, err = txtnid2nid(nid_txt)
if err then
return nil, nil, err
end
local extensions = C.X509_REQ_get_extensions(ctx)
if extensions == nil then
return nil, nil, format_error("csr.get_extension: X509_REQ_get_extensions")
end
ffi_gc(extensions, x509_extensions_gc)
-- make 1-index array to 0-index
last_pos = (last_pos or 0) -1
local ext_idx = C.X509v3_get_ext_by_NID(extensions, nid, last_pos)
if ext_idx == -1 then
err = ("X509v3_get_ext_by_NID extension for %d not found"):format(nid)
return nil, -1, format_error(err)
end
local ctx = C.X509v3_get_ext(extensions, ext_idx)
if ctx == nil then
return nil, nil, format_error("X509v3_get_ext")
end
return ctx, ext_idx, nil
end
--- Get a csr extension
-- @tparam table self Instance of csr
-- @tparam string|number Nid number or name of the extension
-- @tparam number Position to start looking for the extension; default to look from start if omitted
-- @treturn Parsed extension object or nil if not found
function _M:get_extension(nid_txt, last_pos)
local ctx, pos, err = get_extension(self.ctx, nid_txt, last_pos)
if err then
return nil, nil, "x509.csr:get_extension: " .. err
end
local ext, err = extension_lib.dup(ctx)
if err then
return nil, nil, "x509.csr:get_extension: " .. err
end
return ext, pos+1
end
local function modify_extension(replace, ctx, nid, toset, crit)
local extensions_ptr = stack_ptr_type()
extensions_ptr[0] = C.X509_REQ_get_extensions(ctx)
local need_cleanup = extensions_ptr[0] ~= nil and
-- extensions_ptr being nil is fine: it may just because there's no extension yet
-- https://github.com/openssl/openssl/commit/2039ac07b401932fa30a05ade80b3626e189d78a
-- introduces a change that a empty stack instead of NULL will be returned in no extension
-- is found. so we need to double check the number if it's not NULL.
stack_macro.OPENSSL_sk_num(extensions_ptr[0]) > 0
local flag
if replace then
-- x509v3.h: # define X509V3_ADD_REPLACE 2L
flag = 0x2
else
-- x509v3.h: # define X509V3_ADD_APPEND 1L
flag = 0x1
end
local code = C.X509V3_add1_i2d(extensions_ptr, nid, toset, crit and 1 or 0, flag)
-- when the stack is newly allocated, we want to cleanup the newly created stack as well
-- setting the gc handler here as it's mutated in X509V3_add1_i2d if it's pointing to NULL
ffi_gc(extensions_ptr[0], x509_extensions_gc)
if code ~= 1 then
return false, format_error("X509V3_add1_i2d", code)
end
code = C.X509_REQ_add_extensions(ctx, extensions_ptr[0])
if code ~= 1 then
return false, format_error("X509_REQ_add_extensions", code)
end
if need_cleanup then
-- cleanup old attributes
-- delete the first only, why?
local attr = C.X509_REQ_delete_attr(ctx, 0)
if attr ~= nil then
C.X509_ATTRIBUTE_free(attr)
end
end
-- mark encoded form as invalid so next time it will be re-encoded
if OPENSSL_11_OR_LATER then
C.i2d_re_X509_REQ_tbs(ctx, nil)
else
ctx.req_info.enc.modified = 1
end
return true
end
local function add_extension(...)
return modify_extension(false, ...)
end
local function replace_extension(...)
return modify_extension(true, ...)
end
function _M:add_extension(extension)
if not extension_lib.istype(extension) then
return false, "x509:set_extension: expect a x509.extension instance at #1"
end
local nid = extension:get_object().nid
local toset = extension_lib.to_data(extension, nid)
return add_extension(self.ctx, nid, toset.ctx, extension:get_critical())
end
function _M:set_extension(extension)
if not extension_lib.istype(extension) then
return false, "x509:set_extension: expect a x509.extension instance at #1"
end
local nid = extension:get_object().nid
local toset = extension_lib.to_data(extension, nid)
return replace_extension(self.ctx, nid, toset.ctx, extension:get_critical())
end
function _M:set_extension_critical(nid_txt, crit, last_pos)
local nid, err = txtnid2nid(nid_txt)
if err then
return nil, "x509.csr:set_extension_critical: " .. err
end
local extension, _, err = get_extension(self.ctx, nid, last_pos)
if err then
return nil, "x509.csr:set_extension_critical: " .. err
end
local toset = extension_lib.to_data({
ctx = extension
}, nid)
return replace_extension(self.ctx, nid, toset.ctx, crit and 1 or 0)
end
function _M:get_extension_critical(nid_txt, last_pos)
local ctx, _, err = get_extension(self.ctx, nid_txt, last_pos)
if err then
return nil, "x509.csr:get_extension_critical: " .. err
end
return C.X509_EXTENSION_get_critical(ctx) == 1
end
-- START AUTO GENERATED CODE
-- AUTO GENERATED
function _M:sign(pkey, digest)
if not pkey_lib.istype(pkey) then
return false, "x509.csr:sign: expect a pkey instance at #1"
end
local digest_algo
if digest then
if not digest_lib.istype(digest) then
return false, "x509.csr:sign: expect a digest instance at #2"
elseif not digest.algo then
return false, "x509.csr:sign: expect a digest instance to have algo member"
end
digest_algo = digest.algo
elseif BORINGSSL then
digest_algo = C.EVP_get_digestbyname('sha256')
end
-- returns size of signature if success
if C.X509_REQ_sign(self.ctx, pkey.ctx, digest_algo) == 0 then
return false, format_error("x509.csr:sign")
end
return true
end
-- AUTO GENERATED
function _M:verify(pkey)
if not pkey_lib.istype(pkey) then
return false, "x509.csr:verify: expect a pkey instance at #1"
end
local code = C.X509_REQ_verify(self.ctx, pkey.ctx)
if code == 1 then
return true
elseif code == 0 then
return false
else -- typically -1
return false, format_error("x509.csr:verify", code)
end
end
-- AUTO GENERATED
function _M:get_subject_name()
local got = accessors.get_subject_name(self.ctx)
if got == nil then
return nil
end
local lib = require("resty.openssl.x509.name")
-- the internal ptr is returned, ie we need to copy it
return lib.dup(got)
end
-- AUTO GENERATED
function _M:set_subject_name(toset)
local lib = require("resty.openssl.x509.name")
if lib.istype and not lib.istype(toset) then
return false, "x509.csr:set_subject_name: expect a x509.name instance at #1"
end
toset = toset.ctx
if accessors.set_subject_name(self.ctx, toset) == 0 then
return false, format_error("x509.csr:set_subject_name")
end
return true
end
-- AUTO GENERATED
function _M:get_pubkey()
local got = accessors.get_pubkey(self.ctx)
if got == nil then
return nil
end
local lib = require("resty.openssl.pkey")
-- returned a copied instance directly
return lib.new(got)
end
-- AUTO GENERATED
function _M:set_pubkey(toset)
local lib = require("resty.openssl.pkey")
if lib.istype and not lib.istype(toset) then
return false, "x509.csr:set_pubkey: expect a pkey instance at #1"
end
toset = toset.ctx
if accessors.set_pubkey(self.ctx, toset) == 0 then
return false, format_error("x509.csr:set_pubkey")
end
return true
end
-- AUTO GENERATED
function _M:get_version()
local got = accessors.get_version(self.ctx)
if got == nil then
return nil
end
got = tonumber(got) + 1
return got
end
-- AUTO GENERATED
function _M:set_version(toset)
if type(toset) ~= "number" then
return false, "x509.csr:set_version: expect a number at #1"
end
-- Note: this is defined by standards (X.509 et al) to be one less than the certificate version.
-- So a version 3 certificate will return 2 and a version 1 certificate will return 0.
toset = toset - 1
if accessors.set_version(self.ctx, toset) == 0 then
return false, format_error("x509.csr:set_version")
end
return true
end
local NID_subject_alt_name = C.OBJ_sn2nid("subjectAltName")
assert(NID_subject_alt_name ~= 0)
-- AUTO GENERATED: EXTENSIONS
function _M:get_subject_alt_name()
local crit = ctypes.ptr_of_int()
local extensions = C.X509_REQ_get_extensions(self.ctx)
-- GC handler is sk_X509_EXTENSION_pop_free
ffi_gc(extensions, x509_extensions_gc)
local got = C.X509V3_get_d2i(extensions, NID_subject_alt_name, crit, nil)
crit = tonumber(crit[0])
if crit == -1 then -- not found
return nil
elseif crit == -2 then
return nil, "x509.csr:get_subject_alt_name: extension of subject_alt_name occurs more than one times, " ..
"this is not yet implemented. Please use get_extension instead."
elseif got == nil then
return nil, format_error("x509.csr:get_subject_alt_name")
end
-- Note: here we only free the stack itself not elements
-- since there seems no way to increase ref count for a GENERAL_NAME
-- we left the elements referenced by the new-dup'ed stack
local got_ref = got
ffi_gc(got_ref, stack_lib.gc_of("GENERAL_NAME"))
got = ffi_cast("GENERAL_NAMES*", got_ref)
local lib = require("resty.openssl.x509.altname")
-- the internal ptr is returned, ie we need to copy it
return lib.dup(got)
end
-- AUTO GENERATED: EXTENSIONS
function _M:set_subject_alt_name(toset)
local lib = require("resty.openssl.x509.altname")
if lib.istype and not lib.istype(toset) then
return false, "x509.csr:set_subject_alt_name: expect a x509.altname instance at #1"
end
toset = toset.ctx
return replace_extension(self.ctx, NID_subject_alt_name, toset)
end
-- AUTO GENERATED: EXTENSIONS
function _M:set_subject_alt_name_critical(crit)
return _M.set_extension_critical(self, NID_subject_alt_name, crit)
end
-- AUTO GENERATED: EXTENSIONS
function _M:get_subject_alt_name_critical()
return _M.get_extension_critical(self, NID_subject_alt_name)
end
-- AUTO GENERATED
function _M:get_signature_nid()
local nid = accessors.get_signature_nid(self.ctx)
if nid <= 0 then
return nil, format_error("x509.csr:get_signature_nid")
end
return nid
end
-- AUTO GENERATED
function _M:get_signature_name()
local nid = accessors.get_signature_nid(self.ctx)
if nid <= 0 then
return nil, format_error("x509.csr:get_signature_name")
end
return ffi.string(C.OBJ_nid2sn(nid))
end
-- AUTO GENERATED
function _M:get_signature_digest_name()
local nid = accessors.get_signature_nid(self.ctx)
if nid <= 0 then
return nil, format_error("x509.csr:get_signature_digest_name")
end
local nid = find_sigid_algs(nid)
return ffi.string(C.OBJ_nid2sn(nid))
end
-- END AUTO GENERATED CODE
return _M

View File

@ -0,0 +1,281 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_new = ffi.new
local ffi_cast = ffi.cast
local ffi_str = ffi.string
require "resty.openssl.include.x509"
require "resty.openssl.include.x509.extension"
require "resty.openssl.include.x509v3"
require "resty.openssl.include.bio"
require "resty.openssl.include.conf"
local asn1_macro = require("resty.openssl.include.asn1")
local objects_lib = require "resty.openssl.objects"
local stack_lib = require("resty.openssl.stack")
local bio_util = require "resty.openssl.auxiliary.bio"
local format_error = require("resty.openssl.err").format_error
local OPENSSL_3X = require("resty.openssl.version").OPENSSL_3X
local BORINGSSL = require("resty.openssl.version").BORINGSSL
local _M = {}
local mt = { __index = _M }
local x509_extension_ptr_ct = ffi.typeof("X509_EXTENSION*")
local extension_types = {
issuer = "resty.openssl.x509",
subject = "resty.openssl.x509",
request = "resty.openssl.x509.csr",
crl = "resty.openssl.x509.crl",
}
if OPENSSL_3X then
extension_types["issuer_pkey"] = "resty.openssl.pkey"
end
local nconf_load
if BORINGSSL then
nconf_load = function()
return nil, "NCONF_load_bio not exported in BoringSSL"
end
else
nconf_load = function(conf, str)
local bio = C.BIO_new_mem_buf(str, #str)
if bio == nil then
return format_error("BIO_new_mem_buf")
end
ffi_gc(bio, C.BIO_free)
if C.NCONF_load_bio(conf, bio, nil) ~= 1 then
return format_error("NCONF_load_bio")
end
end
end
function _M.new(txtnid, value, data)
local nid, err = objects_lib.txtnid2nid(txtnid)
if err then
return nil, "x509.extension.new: " .. err
end
if type(value) ~= 'string' then
return nil, "x509.extension.new: expect string at #2"
end
-- get a ptr and also zerofill the struct
local x509_ctx_ptr = ffi_new('X509V3_CTX[1]')
local conf = C.NCONF_new(nil)
if conf == nil then
return nil, format_error("NCONF_new")
end
ffi_gc(conf, C.NCONF_free)
if type(data) == 'table' then
local args = {}
if data.db then
if type(data.db) ~= 'string' then
return nil, "x509.extension.new: expect data.db must be a string"
end
err = nconf_load(conf, data)
if err then
return nil, "x509.extension.new: " .. err
end
end
for k, t in pairs(extension_types) do
if data[k] then
local lib = require(t)
if not lib.istype(data[k]) then
return nil, "x509.extension.new: expect data." .. k .. " to be a " .. t .. " instance"
end
args[k] = data[k].ctx
end
end
C.X509V3_set_ctx(x509_ctx_ptr[0], args.issuer, args.subject, args.request, args.crl, 0)
if OPENSSL_3X and args.issuer_pkey then
if C.X509V3_set_issuer_pkey(x509_ctx_ptr[0], args.issuer_pkey) ~= 1 then
return nil, format_error("x509.extension.new: X509V3_set_issuer_pkey")
end
end
elseif type(data) == 'string' then
err = nconf_load(conf, data)
if err then
return nil, "x509.extension.new: " .. err
end
elseif data then
return nil, "x509.extension.new: expect nil, string a table at #3"
end
-- setting conf is required for some extensions to load
-- crypto/x509/v3_conf.c:do_ext_conf "else if (method->r2i) {" branch
C.X509V3_set_nconf(x509_ctx_ptr[0], conf)
local ctx = C.X509V3_EXT_nconf_nid(conf, x509_ctx_ptr[0], nid, value)
if ctx == nil then
return nil, format_error("x509.extension.new")
end
ffi_gc(ctx, C.X509_EXTENSION_free)
local self = setmetatable({
ctx = ctx,
}, mt)
return self, nil
end
function _M.istype(l)
return l and l.ctx and ffi.istype(x509_extension_ptr_ct, l.ctx)
end
function _M.dup(ctx)
if not ffi.istype(x509_extension_ptr_ct, ctx) then
return nil, "x509.extension.dup: expect a x509.extension ctx at #1"
end
local ctx = C.X509_EXTENSION_dup(ctx)
if ctx == nil then
return nil, "x509.extension.dup: X509_EXTENSION_dup() failed"
end
ffi_gc(ctx, C.X509_EXTENSION_free)
local self = setmetatable({
ctx = ctx,
}, mt)
return self, nil
end
function _M.from_der(value, txtnid, crit)
local nid, err = objects_lib.txtnid2nid(txtnid)
if err then
return nil, "x509.extension.from_der: " .. err
end
if type(value) ~= 'string' then
return nil, "x509.extension.from_der: expect string at #1"
end
local asn1 = C.ASN1_STRING_new()
if asn1 == nil then
return nil, format_error("x509.extension.from_der: ASN1_STRING_new")
end
ffi_gc(asn1, C.ASN1_STRING_free)
if C.ASN1_STRING_set(asn1, value, #value) ~= 1 then
return nil, format_error("x509.extension.from_der: ASN1_STRING_set")
end
local ctx = C.X509_EXTENSION_create_by_NID(nil, nid, crit and 1 or 0, asn1)
if ctx == nil then
return nil, format_error("x509.extension.from_der: X509_EXTENSION_create_by_NID")
end
ffi_gc(ctx, C.X509_EXTENSION_free)
local self = setmetatable({
ctx = ctx,
}, mt)
return self, nil
end
function _M:to_der()
local asn1 = C.X509_EXTENSION_get_data(self.ctx)
return ffi_str(asn1_macro.ASN1_STRING_get0_data(asn1))
end
function _M.from_data(any, txtnid, crit)
local nid, err = objects_lib.txtnid2nid(txtnid)
if err then
return nil, "x509.extension.from_der: " .. err
end
if type(any) ~= "table" or type(any.ctx) ~= "cdata" then
return nil, "x509.extension.from_data: expect a table with ctx at #1"
elseif type(nid) ~= "number" then
return nil, "x509.extension.from_data: expect a table at #2"
end
local ctx = C.X509V3_EXT_i2d(nid, crit and 1 or 0, any.ctx)
if ctx == nil then
return nil, format_error("x509.extension.from_data: X509V3_EXT_i2d")
end
ffi_gc(ctx, C.X509_EXTENSION_free)
local self = setmetatable({
ctx = ctx,
}, mt)
return self, nil
end
local NID_subject_alt_name = C.OBJ_sn2nid("subjectAltName")
assert(NID_subject_alt_name ~= 0)
function _M.to_data(extension, nid)
if not _M.istype(extension) then
return nil, "x509.extension.dup: expect a x509.extension ctx at #1"
elseif type(nid) ~= "number" then
return nil, "x509.extension.to_data: expect a table at #2"
end
local void_ptr = C.X509V3_EXT_d2i(extension.ctx)
if void_ptr == nil then
return nil, format_error("x509.extension:to_data: X509V3_EXT_d2i")
end
if nid == NID_subject_alt_name then
-- Note: here we only free the stack itself not elements
-- since there seems no way to increase ref count for a GENERAL_NAME
-- we left the elements referenced by the new-dup'ed stack
ffi_gc(void_ptr, stack_lib.gc_of("GENERAL_NAME"))
local got = ffi_cast("GENERAL_NAMES*", void_ptr)
local lib = require("resty.openssl.x509.altname")
-- the internal ptr is returned, ie we need to copy it
return lib.dup(got)
end
return nil, string.format("x509.extension:to_data: don't know how to convert to NID %d", nid)
end
function _M:get_object()
-- retruns the internal pointer
local asn1 = C.X509_EXTENSION_get_object(self.ctx)
return objects_lib.obj2table(asn1)
end
function _M:get_critical()
return C.X509_EXTENSION_get_critical(self.ctx) == 1
end
function _M:set_critical(crit)
if C.X509_EXTENSION_set_critical(self.ctx, crit and 1 or 0) ~= 1 then
return false, format_error("x509.extension:set_critical")
end
return true
end
function _M:tostring()
local ret, err = bio_util.read_wrap(C.X509V3_EXT_print, self.ctx, 0, 0)
if not err then
return ret
end
-- fallback to ASN.1 print
local asn1 = C.X509_EXTENSION_get_data(self.ctx)
return bio_util.read_wrap(C.ASN1_STRING_print, asn1)
end
_M.text = _M.tostring
mt.__tostring = function(tbl)
local txt, err = _M.text(tbl)
if err then
error(err)
end
return txt
end
return _M

View File

@ -0,0 +1,81 @@
local ffi = require "ffi"
require "resty.openssl.include.x509"
require "resty.openssl.include.x509v3"
local altname_lib = require "resty.openssl.x509.altname"
local stack_lib = require "resty.openssl.stack"
local _M = {}
local stack_ptr_ct = ffi.typeof("OPENSSL_STACK*")
local STACK = "DIST_POINT"
local new = stack_lib.new_of(STACK)
local dup = stack_lib.dup_of(STACK)
-- TODO: return other attributes?
local cdp_decode_fullname = function(ctx)
return altname_lib.dup(ctx.distpoint.name.fullname)
end
local mt = stack_lib.mt_of(STACK, cdp_decode_fullname, _M)
function _M.new()
local ctx = new()
if ctx == nil then
return nil, "OPENSSL_sk_new_null() failed"
end
local self = setmetatable({
stack_of = STACK,
ctx = ctx,
_is_shallow_copy = false,
}, mt)
return self, nil
end
function _M.istype(l)
return l and l.ctx and ffi.istype(stack_ptr_ct, l.ctx)
and l.stack_of and l.stack_of == STACK
end
function _M.dup(ctx)
if ctx == nil or not ffi.istype(stack_ptr_ct, ctx) then
return nil, "expect a stack ctx at #1"
end
local dup_ctx, err = dup(ctx)
if dup_ctx == nil then
return nil, err
end
return setmetatable({
stack_of = STACK,
ctx = dup_ctx,
-- don't let lua gc the original stack to keep its elements
_dupped_from = ctx,
_is_shallow_copy = true,
_elem_refs = {},
_elem_refs_idx = 1,
}, mt), nil
end
_M.all = function(stack)
local ret = {}
local _next = mt.__ipairs(stack)
while true do
local i, e = _next()
if i then
ret[i] = e
else
break
end
end
return ret
end
_M.each = mt.__ipairs
_M.index = mt.__index
_M.count = mt.__len
return _M

View File

@ -0,0 +1,140 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_cast = ffi.cast
require "resty.openssl.include.x509"
require "resty.openssl.include.x509v3"
require "resty.openssl.include.err"
local altname_lib = require "resty.openssl.x509.altname"
local stack_lib = require "resty.openssl.stack"
local _M = {}
local authority_info_access_ptr_ct = ffi.typeof("AUTHORITY_INFO_ACCESS*")
local STACK = "ACCESS_DESCRIPTION"
local new = stack_lib.new_of(STACK)
local add = stack_lib.add_of(STACK)
local dup = stack_lib.dup_of(STACK)
local aia_decode = function(ctx)
local nid = C.OBJ_obj2nid(ctx.method)
local gn = altname_lib.gn_decode(ctx.location)
return { nid, unpack(gn) }
end
local mt = stack_lib.mt_of(STACK, aia_decode, _M)
local mt__pairs = mt.__pairs
mt.__pairs = function(tbl)
local f = mt__pairs(tbl)
return function()
local _, e = f()
if not e then return end
return unpack(e)
end
end
function _M.new()
local ctx = new()
if ctx == nil then
return nil, "OPENSSL_sk_new_null() failed"
end
local cast = ffi_cast("AUTHORITY_INFO_ACCESS*", ctx)
local self = setmetatable({
ctx = ctx,
cast = cast,
_is_shallow_copy = false,
}, mt)
return self, nil
end
function _M.istype(l)
return l and l.cast and ffi.istype(authority_info_access_ptr_ct, l.cast)
end
function _M.dup(ctx)
if ctx == nil or not ffi.istype(authority_info_access_ptr_ct, ctx) then
return nil, "expect a AUTHORITY_INFO_ACCESS* ctx at #1"
end
local dup_ctx, err = dup(ctx)
if dup_ctx == nil then
return nil, err
end
return setmetatable({
ctx = dup_ctx,
cast = ffi_cast("AUTHORITY_INFO_ACCESS*", dup_ctx),
-- don't let lua gc the original stack to keep its elements
_dupped_from = ctx,
_is_shallow_copy = true,
_elem_refs = {},
_elem_refs_idx = 1,
}, mt), nil
end
function _M:add(nid, typ, value)
-- the stack element stays with stack
-- we shouldn't add gc handler if it's already been
-- pushed to stack. instead, rely on the gc handler
-- of the stack to release all memories
local ad = C.ACCESS_DESCRIPTION_new()
if ad == nil then
return nil, "ACCESS_DESCRIPTION_new() failed"
end
-- C.ASN1_OBJECT_free(ad.method)
local asn1 = C.OBJ_txt2obj(nid, 0)
if asn1 == nil then
C.ACCESS_DESCRIPTION_free(ad)
-- clean up error occurs during OBJ_txt2*
C.ERR_clear_error()
return nil, "invalid NID text " .. (nid or "nil")
end
ad.method = asn1
local err = altname_lib.gn_set(ad.location, typ, value)
if err then
C.ACCESS_DESCRIPTION_free(ad)
return nil, err
end
local _, err = add(self.ctx, ad)
if err then
C.ACCESS_DESCRIPTION_free(ad)
return nil, err
end
-- if the stack is duplicated, the gc handler is not pop_free
-- handle the gc by ourselves
if self._is_shallow_copy then
ffi_gc(ad, C.ACCESS_DESCRIPTION_free)
self._elem_refs[self._elem_refs_idx] = ad
self._elem_refs_idx = self._elem_refs_idx + 1
end
return self
end
_M.all = function(stack)
local ret = {}
local _next = mt.__ipairs(stack)
while true do
local i, e = _next()
if i then
ret[i] = e
else
break
end
end
return ret
end
_M.each = mt.__ipairs
_M.index = mt.__index
_M.count = mt.__len
return _M

View File

@ -0,0 +1,91 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local stack_lib = require "resty.openssl.stack"
local extension_lib = require "resty.openssl.x509.extension"
local format_error = require("resty.openssl.err").format_error
local _M = {}
local stack_ptr_ct = ffi.typeof("OPENSSL_STACK*")
local STACK = "X509_EXTENSION"
local new = stack_lib.new_of(STACK)
local add = stack_lib.add_of(STACK)
local dup = stack_lib.dup_of(STACK)
local mt = stack_lib.mt_of(STACK, extension_lib.dup, _M)
function _M.new()
local raw, err = new()
if raw == nil then
return nil, err
end
local self = setmetatable({
stack_of = STACK,
ctx = raw,
}, mt)
return self, nil
end
function _M.istype(l)
return l and l.ctx and ffi.istype(stack_ptr_ct, l.ctx)
and l.stack_of and l.stack_of == STACK
end
function _M.dup(ctx)
if ctx == nil or not ffi.istype(stack_ptr_ct, ctx) then
return nil, "x509.extensions.dup: expect a stack ctx at #1, got " .. type(ctx)
end
local dup_ctx, err = dup(ctx)
if dup_ctx == nil then
return nil, err
end
return setmetatable({
stack_of = STACK,
ctx = dup_ctx,
-- don't let lua gc the original stack to keep its elements
_dupped_from = ctx,
_is_shallow_copy = true,
_elem_refs = {},
_elem_refs_idx = 1,
}, mt), nil
end
function _M:add(extension)
if not extension_lib.istype(extension) then
return nil, "expect a x509.extension instance at #1"
end
local dup = C.X509_EXTENSION_dup(extension.ctx)
if dup == nil then
return nil, format_error("extensions:add: X509_EXTENSION_dup")
end
local _, err = add(self.ctx, dup)
if err then
C.X509_EXTENSION_free(dup)
return nil, err
end
-- if the stack is duplicated, the gc handler is not pop_free
-- handle the gc by ourselves
if self._is_shallow_copy then
ffi_gc(dup, C.X509_EXTENSION_free)
self._elem_refs[self._elem_refs_idx] = dup
self._elem_refs_idx = self._elem_refs_idx + 1
end
return true
end
_M.all = stack_lib.all_func(mt)
_M.each = mt.__ipairs
_M.index = mt.__index
_M.count = mt.__len
return _M

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,156 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
local ffi_str = ffi.string
require "resty.openssl.include.x509.name"
require "resty.openssl.include.err"
local objects_lib = require "resty.openssl.objects"
local asn1_macro = require "resty.openssl.include.asn1"
-- local MBSTRING_FLAG = 0x1000
local MBSTRING_ASC = 0x1001 -- (MBSTRING_FLAG|1)
local _M = {}
local x509_name_ptr_ct = ffi.typeof("X509_NAME*")
-- starts from 0
local function value_at(ctx, i)
local entry = C.X509_NAME_get_entry(ctx, i)
local obj = C.X509_NAME_ENTRY_get_object(entry)
local ret = objects_lib.obj2table(obj)
local str = C.X509_NAME_ENTRY_get_data(entry)
if str ~= nil then
ret.blob = ffi_str(asn1_macro.ASN1_STRING_get0_data(str))
end
return ret
end
local function iter(tbl)
local i = 0
local n = tonumber(C.X509_NAME_entry_count(tbl.ctx))
return function()
i = i + 1
if i <= n then
local obj = value_at(tbl.ctx, i-1)
return obj.sn or obj.ln or obj.id, obj
end
end
end
local mt = {
__index = _M,
__pairs = iter,
__len = function(tbl) return tonumber(C.X509_NAME_entry_count(tbl.ctx)) end,
}
function _M.new()
local ctx = C.X509_NAME_new()
if ctx == nil then
return nil, "x509.name.new: X509_NAME_new() failed"
end
ffi_gc(ctx, C.X509_NAME_free)
local self = setmetatable({
ctx = ctx,
}, mt)
return self, nil
end
function _M.istype(l)
return l and l.ctx and ffi.istype(x509_name_ptr_ct, l.ctx)
end
function _M.dup(ctx)
if not ffi.istype(x509_name_ptr_ct, ctx) then
return nil, "x509.name.dup: expect a x509.name ctx at #1, got " .. type(ctx)
end
local ctx = C.X509_NAME_dup(ctx)
ffi_gc(ctx, C.X509_NAME_free)
local self = setmetatable({
ctx = ctx,
}, mt)
return self, nil
end
function _M:add(nid, txt)
local asn1 = C.OBJ_txt2obj(nid, 0)
if asn1 == nil then
-- clean up error occurs during OBJ_txt2*
C.ERR_clear_error()
return nil, "x509.name:add: invalid NID text " .. (nid or "nil")
end
local code = C.X509_NAME_add_entry_by_OBJ(self.ctx, asn1, MBSTRING_ASC, txt, #txt, -1, 0)
C.ASN1_OBJECT_free(asn1)
if code ~= 1 then
return nil, "x509.name:add: X509_NAME_add_entry_by_OBJ() failed"
end
return self
end
function _M:find(nid, last_pos)
local asn1 = C.OBJ_txt2obj(nid, 0)
if asn1 == nil then
-- clean up error occurs during OBJ_txt2*
C.ERR_clear_error()
return nil, nil, "x509.name:find: invalid NID text " .. (nid or "nil")
end
-- make 1-index array to 0-index
last_pos = (last_pos or 0) - 1
local pos = C.X509_NAME_get_index_by_OBJ(self.ctx, asn1, last_pos)
if pos == -1 then
return nil
end
C.ASN1_OBJECT_free(asn1)
return value_at(self.ctx, pos), pos+1
end
-- fallback function to iterate if LUAJIT_ENABLE_LUA52COMPAT not enabled
function _M:all()
local ret = {}
local _next = iter(self)
while true do
local k, obj = _next()
if obj then
ret[k] = obj
else
break
end
end
return ret
end
function _M:each()
return iter(self)
end
mt.__tostring = function(self)
local values = {}
local _next = iter(self)
while true do
local k, v = _next()
if k then
table.insert(values, k .. "=" .. v.blob)
else
break
end
end
table.sort(values)
return table.concat(values, "/")
end
_M.tostring = mt.__tostring
return _M

View File

@ -0,0 +1,108 @@
local ffi = require "ffi"
local C = ffi.C
local ffi_gc = ffi.gc
require "resty.openssl.include.x509.crl"
require "resty.openssl.include.x509.revoked"
local bn_lib = require("resty.openssl.bn")
local format_error = require("resty.openssl.err").format_error
local _M = {}
local mt = { __index = _M }
local x509_revoked_ptr_ct = ffi.typeof('X509_REVOKED*')
local NID_crl_reason = C.OBJ_txt2nid("CRLReason")
assert(NID_crl_reason > 0)
--- Creates new instance of X509_REVOKED data
-- @tparam bn|number sn Serial number as number or bn instance
-- @tparam number time Revocation time
-- @tparam number reason Revocation reason
-- @treturn table instance of the module or nil
-- @treturn[opt] string Returns optional error message in case of error
function _M.new(sn, time, reason)
--- only convert to bn if it is number
if type(sn) == "number"then
sn = bn_lib.new(sn)
end
if not bn_lib.istype(sn) then
return nil, "x509.revoked.new: sn should be number or a bn instance"
end
if type(time) ~= "number" then
return nil, "x509.revoked.new: expect a number at #2"
end
if type(reason) ~= "number" then
return nil, "x509.revoked.new: expect a number at #3"
end
local ctx = C.X509_REVOKED_new()
ffi_gc(ctx, C.X509_REVOKED_free)
-- serial number
local sn_asn1 = C.BN_to_ASN1_INTEGER(sn.ctx, nil)
if sn_asn1 == nil then
return nil, "x509.revoked.new: BN_to_ASN1_INTEGER() failed"
end
ffi_gc(sn_asn1, C.ASN1_INTEGER_free)
if C.X509_REVOKED_set_serialNumber(ctx, sn_asn1) == 0 then
return nil, format_error("x509.revoked.new: X509_REVOKED_set_serialNumber()")
end
-- time
time = C.ASN1_TIME_set(nil, time)
if time == nil then
return nil, format_error("x509.revoked.new: ASN1_TIME_set()")
end
ffi_gc(time, C.ASN1_STRING_free)
if C.X509_REVOKED_set_revocationDate(ctx, time) == 0 then
return nil, format_error("x509.revoked.new: X509_REVOKED_set_revocationDate()")
end
-- reason
local reason_asn1 = C.ASN1_ENUMERATED_new()
if reason_asn1 == nil then
return nil, "x509.revoked.new: ASN1_ENUMERATED_new() failed"
end
ffi_gc(reason_asn1, C.ASN1_ENUMERATED_free)
local reason_ext = C.X509_EXTENSION_new()
if reason_ext == nil then
return nil, "x509.revoked.new: X509_EXTENSION_new() failed"
end
ffi_gc(reason_ext, C.X509_EXTENSION_free)
if C.ASN1_ENUMERATED_set(reason_asn1, reason) == 0 then
return nil, format_error("x509.revoked.new: ASN1_ENUMERATED_set()")
end
if C.X509_EXTENSION_set_data(reason_ext, reason_asn1) == 0 then
return nil, format_error("x509.revoked.new: X509_EXTENSION_set_data()")
end
if C.X509_EXTENSION_set_object(reason_ext, C.OBJ_nid2obj(NID_crl_reason)) == 0 then
return nil, format_error("x509.revoked.new: X509_EXTENSION_set_object()")
end
if C.X509_REVOKED_add_ext(ctx, reason_ext, 0) == 0 then
return nil, format_error("x509.revoked.new: X509_EXTENSION_set_object()")
end
local self = setmetatable({
ctx = ctx,
}, mt)
return self, nil
end
--- Type check
-- @tparam table Instance of revoked module
-- @treturn boolean true if instance is instance of revoked module false otherwise
function _M.istype(l)
return l and l.ctx and ffi.istype(x509_revoked_ptr_ct, l.ctx)
end
return _M

Some files were not shown because too many files have changed in this diff Show More