Skip to content

Commit 6dc8161

Browse files
authored
PYTHON-4181 Use libmongocrypt native crypto when available for 10-50x better performance (#756)
Bundle crypto-enabled libmongocrypt on Mac and Windows. Add crypto enabled libmongocrypt to test matrix. Always set AES-CTR crypto callbacks on macOS < 10.15. Update readme to prefer the crypto-enabled libmongocrypt builds.
1 parent ae4c445 commit 6dc8161

File tree

6 files changed

+81
-30
lines changed

6 files changed

+81
-30
lines changed

bindings/python/.evergreen/test.sh

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ git clone https://github.com/mongodb-labs/drivers-evergreen-tools.git
1414

1515
if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin
1616
PYMONGOCRYPT_LIB=${MONGOCRYPT_DIR}/nocrypto/bin/mongocrypt.dll
17+
PYMONGOCRYPT_LIB_CRYPTO=$(cygpath -m ${MONGOCRYPT_DIR}/bin/mongocrypt.dll)
1718
export PYMONGOCRYPT_LIB=$(cygpath -m $PYMONGOCRYPT_LIB)
1819
PYTHONS=("C:/python/Python37/python.exe"
1920
"C:/python/Python38/python.exe"
@@ -26,6 +27,7 @@ if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin
2627
--version latest --out ../crypt_shared/
2728
elif [ "Darwin" = "$(uname -s)" ]; then
2829
export PYMONGOCRYPT_LIB=${MONGOCRYPT_DIR}/nocrypto/lib/libmongocrypt.dylib
30+
PYMONGOCRYPT_LIB_CRYPTO=${MONGOCRYPT_DIR}/lib/libmongocrypt.dylib
2931
MACOS_VER=$(sw_vers -productVersion)
3032
if [[ $MACOS_VER =~ ^10.14 ]]; then
3133
PYTHONS=("/Library/Frameworks/Python.framework/Versions/3.7/bin/python3"
@@ -44,7 +46,13 @@ elif [ "Darwin" = "$(uname -s)" ]; then
4446
python3 drivers-evergreen-tools/.evergreen/mongodl.py --component crypt_shared \
4547
--version latest --out ../crypt_shared/
4648
else
47-
export PYMONGOCRYPT_LIB=${MONGOCRYPT_DIR}/nocrypto/lib64/libmongocrypt.so
49+
if [ -e "${MONGOCRYPT_DIR}/lib64/" ]; then
50+
export PYMONGOCRYPT_LIB=${MONGOCRYPT_DIR}/nocrypto/lib64/libmongocrypt.so
51+
PYMONGOCRYPT_LIB_CRYPTO=${MONGOCRYPT_DIR}/lib64/libmongocrypt.so
52+
else
53+
export PYMONGOCRYPT_LIB=${MONGOCRYPT_DIR}/nocrypto/lib/libmongocrypt.so
54+
PYMONGOCRYPT_LIB_CRYPTO=${MONGOCRYPT_DIR}/lib/libmongocrypt.so
55+
fi
4856

4957
export CRYPT_SHARED_PATH="../crypt_shared/lib/mongo_crypt_v1.so"
5058
MACHINE=$(uname -m)
@@ -74,8 +82,10 @@ for PYTHON_BINARY in "${PYTHONS[@]}"; do
7482
createvirtualenv $PYTHON_BINARY .venv
7583
python -m pip install --prefer-binary -r test-requirements.txt
7684
python -m pip install -v -e .
77-
python -m pytest -v --ignore=test/performance .
78-
echo "Running tests with CSFLE on dynamic library path..."
85+
echo "Running tests with crypto enabled libmongocrypt..."
86+
PYMONGOCRYPT_LIB=$PYMONGOCRYPT_LIB_CRYPTO python -c 'from pymongocrypt.binding import lib;assert lib.mongocrypt_is_crypto_available(), "mongocrypt_is_crypto_available() returned False"'
87+
PYMONGOCRYPT_LIB=$PYMONGOCRYPT_LIB_CRYPTO python -m pytest -v --ignore=test/performance .
88+
echo "Running tests with crypt_shared on dynamic library path..."
7989
TEST_CRYPT_SHARED=1 DYLD_FALLBACK_LIBRARY_PATH=../crypt_shared/lib/:$DYLD_FALLBACK_LIBRARY_PATH \
8090
LD_LIBRARY_PATH=../crypt_shared/lib:$LD_LIBRARY_PATH \
8191
PATH=../crypt_shared/bin:$PATH \

bindings/python/CHANGELOG.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Changes in Version 1.9.0
66

77
- Add support for named KMS providers like "local:name1".
88
This feature requires libmongocrypt >= 1.9.0.
9+
- Use libmongocrypt native crypto when available which results in 10-50x better performance.
10+
On Linux, it is recommended to download the platform specific build and
11+
set PYMONGOCRYPT_LIB to the crypto-enabled libmongocrypt.so.
12+
- Bundle the crypto-enabled libmongocrypt builds in macOS and Windows wheels for better performance.
913
- Bundle libmongocrypt 1.9.0 in release wheels.
1014

1115
Changes in Version 1.8.0

bindings/python/README.rst

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -114,33 +114,43 @@ For example::
114114
$ curl -O https://s3.amazonaws.com/mciuploads/libmongocrypt/all/master/latest/libmongocrypt-all.tar.gz
115115
$ mkdir libmongocrypt-all && tar xzf libmongocrypt-all.tar.gz -C libmongocrypt-all
116116
$ ls libmongocrypt-all
117-
amazon2 rhel-62-64-bit rhel72-zseries-test ubuntu1604-arm64
118-
debian10 rhel-67-s390x suse12-64 ubuntu1804-64
119-
debian92 rhel-70-64-bit suse12-s390x ubuntu1804-arm64
120-
linux-64-amazon-ami rhel-71-ppc64el suse15-64 windows-test
121-
macos rhel-80-64-bit ubuntu1604
117+
amazon2 debian92 rhel-80-64-bit rhel72-zseries-test ubuntu1804-arm64
118+
amazon2-arm64 linux-64-amazon-ami rhel-81-ppc64el suse12-64 ubuntu2004-64
119+
amazon2023 macos rhel-82-arm64 suse15-64 ubuntu2004-arm64
120+
amazon2023-arm64 rhel-62-64-bit rhel-83-zseries ubuntu1604 ubuntu2204-64
121+
debian10 rhel-70-64-bit rhel-91-64-bit ubuntu1604-arm64 ubuntu2204-arm64
122+
debian11 rhel-71-ppc64el rhel-91-arm64 ubuntu1804-64 windows-test
122123

123124
macOS::
124125

125126
$ # Set PYMONGOCRYPT_LIB for macOS:
126-
$ export PYMONGOCRYPT_LIB=$(pwd)/libmongocrypt-all/macos/nocrypto/lib/libmongocrypt.dylib
127+
$ export PYMONGOCRYPT_LIB=$(pwd)/libmongocrypt-all/macos/lib/libmongocrypt.dylib
127128
$ python -c "import pymongocrypt; print(pymongocrypt.libmongocrypt_version())"
128-
1.2.1
129+
1.9.0
129130

130131
Windows::
131132

132133
$ # Set PYMONGOCRYPT_LIB for Windows:
133-
$ chmod +x $(pwd)/libmongocrypt-all/windows-test/nocrypto/bin/mongocrypt.dll
134-
$ export PYMONGOCRYPT_LIB=$(pwd)/libmongocrypt-all/windows-test/nocrypto/bin/mongocrypt.dll
134+
$ chmod +x $(pwd)/libmongocrypt-all/windows-test/bin/mongocrypt.dll
135+
$ export PYMONGOCRYPT_LIB=$(pwd)/libmongocrypt-all/windows-test/bin/mongocrypt.dll
135136
$ python -c "import pymongocrypt; print(pymongocrypt.libmongocrypt_version())"
136-
1.2.1
137+
1.9.0
137138

138-
Linux::
139+
Linux: set the libmongocrypt build for your platform, for example for Ubuntu 22.04 x86_64::
140+
141+
$ # Set PYMONGOCRYPT_LIB for Ubuntu 22.04 x86_64:
142+
$ export PYMONGOCRYPT_LIB=$(pwd)/libmongocrypt-all/ubuntu2204-64/lib/libmongocrypt.so
143+
$ python -c "import pymongocrypt; print(pymongocrypt.libmongocrypt_version())"
144+
1.9.0
145+
146+
Note if your Linux platform is not available, the generic RHEL 6.2 x86_64 "nocrypto" build
147+
should still be compatible however the "nocrypto" build will result in lower performance
148+
for encryption and decryption::
139149

140150
$ # Set PYMONGOCRYPT_LIB for RHEL 6.2 x86_64:
141151
$ export PYMONGOCRYPT_LIB=$(pwd)/libmongocrypt-all/rhel-62-64-bit/nocrypto/lib64/libmongocrypt.so
142152
$ python -c "import pymongocrypt; print(pymongocrypt.libmongocrypt_version())"
143-
1.2.1
153+
1.9.0
144154

145155
Dependencies
146156
============
@@ -178,7 +188,7 @@ variables, like ``LD_LIBRARY_PATH``. For example::
178188

179189
$ export PYMONGOCRYPT_LIB='/path/to/libmongocrypt.so'
180190
$ python -c "import pymongocrypt; print(pymongocrypt.libmongocrypt_version())"
181-
1.2.1
191+
1.9.0
182192

183193
Testing
184194
=======

bindings/python/pymongocrypt/binding.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ def _parse_version(version):
6363
*/
6464
const char *mongocrypt_version(uint32_t *len);
6565
66+
/**
67+
* Returns true if libmongocrypt was built with native crypto support.
68+
*
69+
* If libmongocrypt was not built with native crypto support, setting crypto
70+
* hooks is required.
71+
*
72+
* @returns True if libmongocrypt was built with native crypto support.
73+
*/
74+
bool mongocrypt_is_crypto_available(void);
75+
6676
/**
6777
* A non-owning view of a byte buffer.
6878
*

bindings/python/pymongocrypt/mongocrypt.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
import copy
15+
import sys
16+
import platform
1617

18+
from packaging.version import Version
1719

1820
from pymongocrypt.binary import (MongoCryptBinaryIn,
1921
MongoCryptBinaryOut)
@@ -205,18 +207,31 @@ def __init(self):
205207
if self.__opts.bypass_query_analysis:
206208
lib.mongocrypt_setopt_bypass_query_analysis(self.__crypt)
207209

208-
if not lib.mongocrypt_setopt_crypto_hooks(
209-
self.__crypt, aes_256_cbc_encrypt, aes_256_cbc_decrypt,
210-
secure_random, hmac_sha_512, hmac_sha_256, sha_256, ffi.NULL):
211-
self.__raise_from_status()
210+
# Prefer using the native crypto binding when we know it's available.
211+
try:
212+
crypto_available = lib.mongocrypt_is_crypto_available()
213+
except AttributeError:
214+
# libmongocrypt < 1.9
215+
crypto_available = False
216+
217+
if not crypto_available:
218+
if not lib.mongocrypt_setopt_crypto_hooks(
219+
self.__crypt, aes_256_cbc_encrypt, aes_256_cbc_decrypt,
220+
secure_random, hmac_sha_512, hmac_sha_256, sha_256, ffi.NULL):
221+
self.__raise_from_status()
212222

213-
if not lib.mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5(
214-
self.__crypt, sign_rsaes_pkcs1_v1_5, ffi.NULL):
215-
self.__raise_from_status()
223+
if not lib.mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5(
224+
self.__crypt, sign_rsaes_pkcs1_v1_5, ffi.NULL):
225+
self.__raise_from_status()
216226

217-
if not lib.mongocrypt_setopt_aes_256_ctr(
218-
self.__crypt, aes_256_ctr_encrypt, aes_256_ctr_decrypt, ffi.NULL):
219-
self.__raise_from_status()
227+
if not lib.mongocrypt_setopt_aes_256_ctr(
228+
self.__crypt, aes_256_ctr_encrypt, aes_256_ctr_decrypt, ffi.NULL):
229+
self.__raise_from_status()
230+
elif sys.platform == 'darwin' and Version(platform.mac_ver()[0]) < Version("10.15"):
231+
# MONGOCRYPT-440 libmongocrypt does not support AES-CTR on macOS < 10.15.
232+
if not lib.mongocrypt_setopt_aes_256_ctr(
233+
self.__crypt, aes_256_ctr_encrypt, aes_256_ctr_decrypt, ffi.NULL):
234+
self.__raise_from_status()
220235

221236
if self.__opts.crypt_shared_lib_path is not None:
222237
lib.mongocrypt_setopt_set_crypt_shared_lib_path_override(

bindings/python/release.sh

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin
6666
dos2unix .venv/Scripts/activate || true
6767
. ./.venv/Scripts/activate
6868

69-
get_libmongocrypt windows-test libmongocrypt/nocrypto/bin/mongocrypt.dll
69+
# Use crypto-enabled libmongocrypt.
70+
get_libmongocrypt windows-test libmongocrypt/bin/mongocrypt.dll
7071
build_wheel
7172
test_dist dist/*.whl
7273
fi
@@ -76,8 +77,9 @@ if [ "Darwin" = "$(uname -s)" ]; then
7677
$PYTHON -m venv .venv
7778
. .venv/bin/activate
7879

80+
# Use crypto-enabled libmongocrypt.
7981
# Build intel wheel for Python 3.7.
80-
get_libmongocrypt macos_x86_64 libmongocrypt/nocrypto/lib/libmongocrypt.dylib
82+
get_libmongocrypt macos_x86_64 libmongocrypt/lib/libmongocrypt.dylib
8183
# See https://github.com/pypa/cibuildwheel/blob/a3e5b541dc3111166a3abdbbc90ecb195c8cb9e2/cibuildwheel/macos.py#L247
8284
# for information on these environment variables.
8385
export MACOSX_DEPLOYMENT_TARGET=10.14
@@ -88,7 +90,7 @@ if [ "Darwin" = "$(uname -s)" ]; then
8890
fi
8991

9092
# Build universal2 wheel.
91-
get_libmongocrypt macos libmongocrypt/nocrypto/lib/libmongocrypt.dylib
93+
get_libmongocrypt macos libmongocrypt/lib/libmongocrypt.dylib
9294
export MACOSX_DEPLOYMENT_TARGET=11.0
9395
export _PYTHON_HOST_PLATFORM=macosx-11.0-universal2
9496
build_wheel

0 commit comments

Comments
 (0)