Merge commit 'a2800598825bb5a03b577cca2874ff1cfae863f4' as 'src/deps/src/lua-resty-openssl'
This commit is contained in:
commit
38fdd39d00
|
@ -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 -}}
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
|
@ -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
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
t/servroot
|
||||
__pycache__
|
||||
.idea/
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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.
|
|
@ -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
|
@ -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
|
|
@ -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)
|
||||
|
|
@ -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")
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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")
|
|
@ -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
|
|
@ -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")
|
||||
|
|
@ -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
|
|
@ -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,
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
||||
}
|
|
@ -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
|
|
@ -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,
|
||||
}
|
|
@ -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);
|
||||
]]
|
|
@ -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
|
|
@ -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);
|
||||
]]
|
|
@ -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,
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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);
|
||||
]]
|
|
@ -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);
|
||||
]]
|
|
@ -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);
|
||||
]]
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
]]
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
]]
|
|
@ -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;
|
||||
]])
|
||||
|
|
@ -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);
|
||||
]]
|
|
@ -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);
|
||||
|
||||
]]
|
|
@ -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
|
|
@ -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);
|
||||
]]
|
|
@ -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
|
|
@ -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,
|
||||
},
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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
|
|
@ -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,
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
]]
|
|
@ -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
|
|
@ -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);
|
||||
]]
|
|
@ -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);
|
||||
]]
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
||||
})
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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
Loading…
Reference in New Issue