pax_global_header00006660000000000000000000000064142147627470014530gustar00rootroot0000000000000052 comment=c91c8642e712e7801bf40403dd72165b607f4930 oscrypto-1.3.0/000077500000000000000000000000001421476274700134135ustar00rootroot00000000000000oscrypto-1.3.0/.circleci/000077500000000000000000000000001421476274700152465ustar00rootroot00000000000000oscrypto-1.3.0/.circleci/config.yml000066400000000000000000000007331421476274700172410ustar00rootroot00000000000000version: 2 jobs: python2.7: machine: image: ubuntu-2004:202101-01 resource_class: arm.medium steps: - checkout - run: python run.py deps - run: python run.py ci-driver python3.9: machine: image: ubuntu-2004:202101-01 resource_class: arm.medium steps: - checkout - run: python run.py deps - run: python3 run.py ci-driver workflows: version: 2 arm64: jobs: - python2.7 - python3.9 oscrypto-1.3.0/.github/000077500000000000000000000000001421476274700147535ustar00rootroot00000000000000oscrypto-1.3.0/.github/FUNDING.yml000066400000000000000000000000751421476274700165720ustar00rootroot00000000000000# These are supported funding model platforms github: wbond oscrypto-1.3.0/.github/workflows/000077500000000000000000000000001421476274700170105ustar00rootroot00000000000000oscrypto-1.3.0/.github/workflows/ci.yml000066400000000000000000000163751421476274700201420ustar00rootroot00000000000000name: CI on: [push, pull_request] jobs: build-windows: name: Python ${{ matrix.python }} on windows-2019 ${{ matrix.arch }} runs-on: windows-2019 strategy: matrix: python: - '2.7' - '3.9' # - 'pypy-3.7-v7.3.5' arch: - 'x86' - 'x64' exclude: - python: 'pypy-3.7-v7.3.5' arch: x86 steps: - uses: actions/checkout@master - uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} architecture: ${{ matrix.arch }} - name: Install dependencies run: python run.py deps - name: Run test suite run: python run.py ci-driver - name: Run test suite (Windows legacy API) run: python run.py ci-driver winlegacy build-windows-old: name: Python ${{ matrix.python }} on windows-2019 ${{ matrix.arch }} runs-on: windows-2019 strategy: matrix: python: - '2.6' - '3.3' arch: - 'x86' - 'x64' steps: - uses: actions/checkout@master - name: Cache Python id: cache-python uses: actions/cache@v2 with: path: ~/AppData/Local/Python${{ matrix.python }}-${{ matrix.arch }} key: windows-2019-python-${{ matrix.python }}-${{ matrix.arch }} - name: Install Python ${{ matrix.python }} run: python run.py python-install ${{ matrix.python }} ${{ matrix.arch }} | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Install dependencies run: python run.py deps - name: Run test suite run: python run.py ci-driver - name: Run test suite (Windows legacy API) run: python run.py ci-driver winlegacy build-mac: name: Python ${{ matrix.python }} on macos-10.15 runs-on: macos-10.15 strategy: matrix: python: - '2.7' - '3.9' # - 'pypy-3.7-v7.3.5' steps: - uses: actions/checkout@master - uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} architecture: x64 - name: Install dependencies run: python run.py deps - name: Run test suite run: python run.py ci-driver - name: Run test suite (Mac cffi) run: python run.py ci-driver cffi - name: Run test suite (Mac OpenSSL) run: python run.py ci-driver openssl if: ${{ matrix.python }} != 'pypy-3.7-v7.3.5' - name: Run test suite (Mac OpenSSL/cffi) run: python run.py ci-driver cffi openssl if: ${{ matrix.python }} != 'pypy-3.7-v7.3.5' build-mac-old: name: Python ${{ matrix.python }} on macos-10.15 runs-on: macos-10.15 strategy: matrix: python: - '2.6' - '3.3' steps: - uses: actions/checkout@master - name: Check pyenv id: check-pyenv uses: actions/cache@v2 with: path: ~/.pyenv key: macos-10.15-${{ matrix.python }}-pyenv - name: Install Python ${{ matrix.python }} run: python run.py pyenv-install ${{ matrix.python }} >> $GITHUB_PATH - name: Install dependencies run: python run.py deps - name: Run test suite run: python run.py ci-driver - name: Run test suite (Mac cffi) run: python run.py ci-driver cffi - name: Run test suite (Mac OpenSSL) run: python run.py ci-driver openssl - name: Run test suite (Mac OpenSSL/cffi) run: python run.py ci-driver cffi openssl build-mac-openssl3: name: Python ${{ matrix.python }} on macos-10.15 with OpenSSL 3.0 runs-on: macos-10.15 strategy: matrix: python: - '3.6' - '3.10' steps: - uses: actions/checkout@master - uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} architecture: x64 - name: Install OpenSSL 3.0 run: brew install openssl@3 - name: Install dependencies run: python run.py deps - name: Run test suite (Mac OpenSSL 3.0) run: python run.py ci-driver openssl3 - name: Run test suite (Mac OpenSSL 3.0/cffi) run: python run.py ci-driver cffi openssl3 build-ubuntu: name: Python ${{ matrix.python }} on ubuntu-18.04 x64 runs-on: ubuntu-18.04 strategy: matrix: python: - '2.7' - '3.6' - '3.9' - '3.10' - 'pypy-3.7-v7.3.5' steps: - uses: actions/checkout@master - uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} architecture: x64 - name: Install dependencies run: python run.py deps - name: Run test suite run: python run.py ci-driver build-ubuntu-openssl3: name: Python ${{ matrix.python }} on (Docker) ubuntu-22.04 x64 runs-on: ubuntu-latest container: ubuntu:22.04 steps: - uses: actions/checkout@master - name: Install Python and OpenSSL run: DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends python3 python3-setuptools python-is-python3 openssl curl ca-certificates git - name: Install dependencies run: python run.py deps - name: Run test suite run: python run.py ci-driver - name: Run test suite (cffi) run: python run.py ci-driver cffi build-ubuntu-old: name: Python ${{ matrix.python }} on ubuntu-18.04 x64 runs-on: ubuntu-18.04 strategy: matrix: python: - '2.6' - '3.2' - '3.3' steps: - uses: actions/checkout@master - name: Setup deadsnakes/ppa run: sudo apt-add-repository ppa:deadsnakes/ppa - name: Update apt run: sudo apt-get update - name: Install Python ${{matrix.python}} run: sudo apt-get install python${{matrix.python}} - name: Install dependencies run: python${{matrix.python}} run.py deps - name: Run test suite run: python${{matrix.python}} run.py ci-driver build-arm: name: Python 2.7/3.8 on arm runs-on: [self-hosted, linux, ARM] steps: - uses: actions/checkout@master - name: Install dependencies (2.7) run: python2 run.py deps - name: Run test suite (2.7) run: python2 run.py ci-driver - name: Cleanup deps (2.7) if: always() run: python2 run.py ci-cleanup - name: Install dependencies (3.8) run: python3 run.py deps - name: Run test suite (3.8) run: python3 run.py ci-driver - name: Cleanup deps (3.8) if: always() run: python3 run.py ci-cleanup build-arm64: name: Python 2.7/3.8 on arm64 runs-on: [self-hosted, linux, ARM64] steps: - uses: actions/checkout@master - name: Install dependencies (2.7) run: python2 run.py deps - name: Run test suite (2.7) run: python2 run.py ci-driver - name: Cleanup deps (2.7) if: always() run: python2 run.py ci-cleanup - name: Install dependencies (3.8) run: python3 run.py deps - name: Run test suite (3.8) run: python3 run.py ci-driver - name: Cleanup deps (3.8) if: always() run: python3 run.py ci-cleanup oscrypto-1.3.0/.gitignore000066400000000000000000000002451421476274700154040ustar00rootroot00000000000000*.egg-info/ .tox/ __pycache__/ build/ dist/ tests/output/ *.pyc .coverage .DS_Store .python-version coverage.xml oscrypto.sublime-project oscrypto.sublime-workspace oscrypto-1.3.0/LICENSE000066400000000000000000000020631421476274700144210ustar00rootroot00000000000000Copyright (c) 2015-2022 Will Bond Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. oscrypto-1.3.0/changelog.md000066400000000000000000000300061421476274700156630ustar00rootroot00000000000000# changelog ## 1.3.0 - Add support for OpenSSL 3.0 - Add first-class support for RSASSA-PSS certificates - Add user-friendly handling of the error message with TLS on macOS when a ceritificate has a lifetime that is longer than the CAB forum guidelines - Fix AES 192/256 encryption on OpenSSL and Windows to allow no padding when plaintext is an exact multiple of 16 bytes long. Previously AES192 would require plaintext with a length that was a multiple of 24 AND 16, and AES256 would require plaintext with a length that was a multiple of 32. - Add the ability to skip tests that require internet connectivity *via @jnahmias* - Fix a bug throwing an exception when passing an invalid type to `asymmetric.load_public_key()` *via @Arbitrage0* - Fix a number of typos in doc strings *via @frennkie and @kianmeng* ## 1.2.1 - Fix running in an environment with a custom OpenSSL install on macOS 10.15 - Fix compatibility with macOS 11, where `ctype.find_library()` no longer works due to system `.dylib`s no longer being present on the filesystem - Handle the Mac `EPROTOTYPE` error that may be returned when a TLS connection is terminated - Fixed the `oscrypto-tests` sdist on PyPi to work properly to generate a .whl ## 1.2.0 - Allow `oscrypto.use_ctypes()`, `oscrypto.use_openssl()` and `oscrypto.use_winlegacy()` to be called after initialization as long as the configuration does not change ## 1.1.1 - Use versioned libcrypto.dylib and libssl.dylib on macOS Catalina to prevent segfaults - Fix warnings reported when using with cffi 1.13.2 ## 1.1.0 - Added `oscrypto.load_order()`, which returns a `list` of unicode strings of the names of the fully-qualified module names for all of submodules of the package. The module names are listed in their dependency load order. This is primarily intended for the sake of implementing hot reloading. ## 1.0.0 - Backwards Compatibility Breaks - `oscrypto.backend()` will now return `"mac"` instead of `"osx"` when running on a Mac and not explicitly configured to use OpenSSL - Enhancements - Added functionality to calculate public keys from private keys since that was removed from asn1crypto: - `asn1crypto.keys.PrivateKeyInfo().unwrap()` is now `asymmetric.PrivateKey().unwrap()` - `asn1crypto.keys.PrivateKeyInfo().public_key` is now `asymmetric.PrivateKey().public_key.unwrap()` - `asn1crypto.keys.PrivateKeyInfo().public_key_info` is now `asymmetric.PrivateKey().public_key.asn1` - `asn1crypto.keys.PrivateKeyInfo().fingerprint` is now `asymmetric.PrivateKey().fingerprint` - `asn1crypto.keys.PublicKeyInfo().unwrap()` is now `asymmetric.PublicKey().unwrap()` - `asn1crypto.keys.PublicKeyInfo().fingerprint` is now `asymmetric.PublicKey().fingerprint` - Added `oscrypto.use_ctypes()` to avoid CFFI if desired - Added `tls.TLSSocket().port` property - Improved handling of disconnects with `tls.TLSSocket()` - Improved error messages when dealing with failures originating in OpenSSL - Allow PEM-encoded files to have leading whitespace when loaded via `keys.parse_private()`, `keys.parse_public()` and `keys.parse_certificate()` - Restructured internal imports of asn1crypto to make vendoring easier - No longer touch the user keychain on Macs when generating keys, instead use a temporary one - Bug Fixes - Fixed compatibility with Python 3.7+ - Fixed compatibility with LibreSSL version 2.2.x+ - Fixed a bug where `tls.TLSSocket().read_until()` that would sometimes read more data from the socket than necessary - Fixed a buffer overflow when reading data from an OpenSSL memory bio - Fixed a bug in `util.pbkdf2()` that would cause incorrect output in some situations when run on Windows XP or with OpenSSL 0.9.8 - Fixed `aes_cbc_no_padding_encrypt()` so it can be executed when the backend is OpenSSL - A `SecTrustRef` obtained from `SSLCopyPeerTrust()` on Mac is now properly released - Packaging - `wheel`, `sdist` and `bdist_egg` releases now all include LICENSE, `sdist` includes docs - Added `oscrypto_tests` package to PyPi ## 0.19.1 - Fixed a bug where `trust_list.get_path()` would not call the `cert_callback` when a certificate was exported - Fixed an issue on OS X/macOS where a certificate with an explicit any purpose trust OID would not be exported since it didn't contain the OID for SSL ## 0.19.0 - Backwards compatibility break: `trust_list.get_path()` not longer accepts the parameter `map_vendor_oids`, and only includes CA certificates that the OS marks as trusted for TLS server authentication. This change was made due to (at least some versions of) OpenSSL not verifying a server certificate if the CA bundle includes a `TRUSTED CERTIFICATE` entry, which is how the trust information was exported. Since trust information can no longer be exported to disk, the list of certificates must be filtered, and since the intent of this function was always to provide a list of CA certs for use by OpenSSL when creating TLS connection, this change in functionality is in line with the original intent. - `asymmetric.rsa_pkcs1v15_verify()` and `asymmetric.rsa_rss_verify()` will now raise a `SignatureError` when there is a key size mismatch. ## 0.18.0 - `trust_list.get_path()` and `trust_list.get_list()` now accept a parameter `cert_callback`, which is a callback that will be called once for each certificate in the trust store. If the certificate will not be exported, a reason will be provided. - Added `oscrypto.version` for version introspection without side-effects - Now uses `asn1crypto.algos.DSASignature` instead of self-contained ASN.1 definition ## 0.17.3 - Work around an issue on OS X where SecureTransport would try to read non-TLS data as TLS records, causing hangs with `tls.TLSSocket()` - Handle an alternate way the Windows SChannel API can fail when the DH params for a TLS handshake are too small - Fix a bug with cffi on OS X and converting a CFString to a UTF-8 byte string ## 0.17.2 - Handle `errSecInvalidTrustSettings` errors on macOS exporting trust roots - Prevent a `KeyError` on macOS when exporting trust roots and trust settings are present for certificates not in the list ## 0.17.1 - Expose `LibraryNotFoundError` via `errors.LibraryNotFoundError` ## 0.17.0 - Added support for OpenSSL 1.1.0 - Allow using OpenSSL on OS X and Windows - Prevent FFI library references from being garbage collected before parent `asymmetric.PublicKey`, `asymmetric.PrivateKey` and `asymmetric.Certificate` objects - Improved handling of `errSecAuthFailed` error that occurs when calling `asymmetric.generate_*()` functions on OS X in some virtualenvs ## 0.16.2 - Allow `cffi` files to be removed from source tree when embedding ## 0.16.1 - Updated [asn1crypto](https://github.com/wbond/asn1crypto) dependency to `0.18.1`. ## 0.16.0 - Backwards compatibility break: `trust_list.get_list()` now returns a list of 3-element tuples containing the certificate byte string, a set of trust OIDs and a set of reject OIDs. Previously it returned a list of certificate byte strings. - `trust_list` now makes OS trust information OIDs available via the `trust_list.get_list()` function, and writes OpenSSL-compatible trust information to the CA certs file when calling `trust_info.get_path()` on Windows and OS X. - Removed reliance on opaque OpenSSL struct information for compatibility with upcoming OpenSSL 1.1.0 release - Improved handling of client authentication and socket read errors when using OpenSSL - Added Windows XP support ## 0.15.0 - Added `asymmetric.generate_dh_parameters()` and `asymmetric.dump_dh_parameters()` - Improve disconnection handling of `tls.TLSSocket` on Windows - Ensure that certificates signed using MD5 and MD2 are rejected on Windows when using the `extra_trust_roots` parameter of `tls.TLSSession` ## 0.14.2 - Fixed `trust_list` to work with new Security.framework behavior on OS X 10.11 El Capitan - Fixed an occasional bug with `tls.TLSSocket()` on Windows when using TLSv1.2 and the server negotiated using a `DHE_RSA` key exchange - Fixed a bug on Windows 10 where a TLS handshake would fail if the TLS record was not completely received within one call to `socket.recv()` - Fixed a bug where a private key would not be encoded with PEM encoding when requested, if no passphrase was provided to `asymmetric.dump_private_key()` ## 0.14.1 - Fixed a bug where `asymmetric.generate_pair()` would raise an exception on OS X when the system Python was used to create a virtualenv ## 0.14.0 - `tls.TLSSocket()` now has a default connect, read and write timeout of `10` seconds - Fixed bugs with `manual_validation` keyword param for `tls.TLSSession()` on all three platforms - Fixed a bug in `asymmetric.PublicKey.self_signed` that would always force signature verification - Improved parsing of TLS records during handshakes to improve error messaging - `tls.TLSSocket()` on OS X now respects `KeyboardInterrupt` while in a read or write callback - TLS connections on Windows will fallback to TLSv1.1 if TLSv1.2 is negotiated but a trust root with an MD2 or MD5 certificate is part of the certificate path. Previously the connection would fail. - TLS connections with optional client authentication no longer fail on Windows - `trust_list.get_list()` on Windows now returns a de-duplicated list ## 0.13.1 - Improved handling of signature errors to always raise `errors.SignatureError` - Fixed a bug with `trust_list.get_list()` on Windows not returning certificates that were valid for all uses ## 0.13.0 - Backwards compatibility break: `trust_list.get_list()` now returns a list of `asn1crypto.x509.Certificate` objects instead of a list of byte strings - `trust_list.get_list()` now returns a copy of the list to prevent accidental modification of the list - Added `tls.TLSSocket.hostname` ## 0.12.0 - Fixed Python 2.6 support on Windows and Linux - Fixed handling of some TLS error conditions with Python 2 on Windows - Corrected handling of incomplete DSA keys on Windows - Fixed a bug converting a `FILETIME` struct with Python 2 on Windows to a `datetime` object - Fixed a cast/free bug with cffi and CPython on Windows that incorrectly reported some TLS certificates as invalid - Fixed a bug with exporting the trust list from Windows on Python 2 x64 - Fixed detection of weak DH params in a TLS connection on OS X 10.7-10.9 - OS X 10.7-10.9 no longer use CRL/OCSP to check for revocation, making the functionality consistent with Linux, Window and OS X 10.10 and newer - Fixed OS X 10.7 TLS validation when using `extra_trust_roots` in a `tls.TLSSession` ## 0.11.1 - Handles specific weak DH keys error code in newer versions of OpenSSL - Added `__str__()` and `__unicode__()` to TLS exceptions ## 0.11.0 - Added TLS functionality - Added Python 2.6 support - Added `asymmetric.Certificate.self_signed` - Added "raw" RSA signing/verification to `asymmetric.rsa_pkcs1v15_sign()` and `asymmetric.rsa_pkcs1v15_verify()` functions - Fixes for compatibility bugs with OS X 10.7 - Fixes for compatibility bugs with pypy3 - Fixes for compatibility bugs with cffi 0.8.6 ## 0.10.0 - `oscrypto.public_key` renamed to `oscrypto.asymmetric` - `.algo` attribute of `asymmetric.PublicKey`, `asymmetric.PrivateKey` and `asymmetric.Certificate` classes renamed to `.algorithm` - `parse_public()`, `parse_private()`, `parse_certificate()` and `parse_pkcs12()` all now return just an asn1crypto object instead of a 2-element tuple with the algorithm name - Added the `asymmetric.generate_pair()` function - Added the functions: - `asymmetric.dump_certificate()` - `asymmetric.dump_public_key()` - `asymmetric.dump_private_key()` - `asymmetric.dump_openssl_private_key()` - Added the `kdf.pbkdf2_iteration_calculator()` function - Added the `setup.py clean` command ## 0.9.0 - Initial release oscrypto-1.3.0/dev/000077500000000000000000000000001421476274700141715ustar00rootroot00000000000000oscrypto-1.3.0/dev/__init__.py000066400000000000000000000032031421476274700163000ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os package_name = "oscrypto" other_packages = [ "certbuilder", "certvalidator", "crlbuilder", "csrbuilder", "ocspbuilder" ] task_keyword_args = [ { 'name': 'use_openssl', 'placeholder': '/path/to/libcrypto,/path/to/libssl', 'env_var': 'OSCRYPTO_USE_OPENSSL', }, { 'name': 'use_winlegacy', 'placeholder': 'true', 'env_var': 'OSCRYPTO_USE_WINLEGACY', }, { 'name': 'use_ctypes', 'placeholder': 'true', 'env_var': 'OSCRYPTO_USE_CTYPES', }, { 'name': 'skip_internet', 'placeholder': 'true', 'env_var': 'OSCRYPTO_SKIP_INTERNET_TESTS', }, ] requires_oscrypto = True has_tests_package = True package_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) build_root = os.path.abspath(os.path.join(package_root, '..')) md_source_map = { 'docs/oscrypto.md': ['oscrypto/__init__.py'], 'docs/asymmetric.md': ['oscrypto/asymmetric.py', 'oscrypto/_openssl/asymmetric.py'], 'docs/kdf.md': ['oscrypto/kdf.py', 'oscrypto/_openssl/util.py'], 'docs/keys.md': ['oscrypto/keys.py', 'oscrypto/_asymmetric.py', 'oscrypto/_openssl/asymmetric.py'], 'docs/symmetric.md': ['oscrypto/_openssl/symmetric.py'], 'docs/tls.md': ['oscrypto/tls.py', 'oscrypto/_openssl/tls.py'], 'docs/trust_list.md': ['oscrypto/trust_list.py'], 'docs/util.md': ['oscrypto/util.py', 'oscrypto/_rand.py'], } definition_replacements = { ' is returned by OpenSSL': ' is returned by the OS crypto library' } oscrypto-1.3.0/dev/_import.py000066400000000000000000000066411421476274700162230ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import imp import sys import os from . import build_root, package_name, package_root if sys.version_info < (3,): getcwd = os.getcwdu else: getcwd = os.getcwd def _import_from(mod, path, mod_dir=None, allow_error=False): """ Imports a module from a specific path :param mod: A unicode string of the module name :param path: A unicode string to the directory containing the module :param mod_dir: If the sub directory of "path" is different than the "mod" name, pass the sub directory as a unicode string :param allow_error: If an ImportError should be raised when the module can't be imported :return: None if not loaded, otherwise the module """ if mod_dir is None: mod_dir = mod.replace('.', os.sep) if not os.path.exists(path): return None if not os.path.exists(os.path.join(path, mod_dir)) \ and not os.path.exists(os.path.join(path, mod_dir + '.py')): return None if os.sep in mod_dir: append, mod_dir = mod_dir.rsplit(os.sep, 1) path = os.path.join(path, append) try: mod_info = imp.find_module(mod_dir, [path]) return imp.load_module(mod, *mod_info) except ImportError: if allow_error: raise return None def _preload(require_oscrypto, print_info): """ Preloads asn1crypto and optionally oscrypto from a local source checkout, or from a normal install :param require_oscrypto: A bool if oscrypto needs to be preloaded :param print_info: A bool if info about asn1crypto and oscrypto should be printed """ if print_info: print('Working dir: ' + getcwd()) print('Python ' + sys.version.replace('\n', '')) asn1crypto = None oscrypto = None if require_oscrypto: # Some CI services don't use the package name for the dir if package_name == 'oscrypto': oscrypto_dir = package_root else: oscrypto_dir = os.path.join(build_root, 'oscrypto') oscrypto_tests = None if os.path.exists(oscrypto_dir): oscrypto_tests = _import_from('oscrypto_tests', oscrypto_dir, 'tests') if oscrypto_tests is None: import oscrypto_tests asn1crypto, oscrypto = oscrypto_tests.local_oscrypto() else: if package_name == 'asn1crypto': asn1crypto_dir = package_root else: asn1crypto_dir = os.path.join(build_root, 'asn1crypto') if os.path.exists(asn1crypto_dir): asn1crypto = _import_from('asn1crypto', asn1crypto_dir) if asn1crypto is None: import asn1crypto if print_info: print( '\nasn1crypto: %s, %s' % ( asn1crypto.__version__, os.path.dirname(asn1crypto.__file__) ) ) if require_oscrypto: backend = oscrypto.backend() if backend == 'openssl': from oscrypto._openssl._libcrypto import libcrypto_version backend = '%s (%s)' % (backend, libcrypto_version) print( 'oscrypto: %s, %s backend, %s' % ( oscrypto.__version__, backend, os.path.dirname(oscrypto.__file__) ) ) oscrypto-1.3.0/dev/_pep425.py000066400000000000000000000141361421476274700157260ustar00rootroot00000000000000# coding: utf-8 """ This file was originally derived from https://github.com/pypa/pip/blob/3e713708088aedb1cde32f3c94333d6e29aaf86e/src/pip/_internal/pep425tags.py The following license covers that code: Copyright (c) 2008-2018 The pip developers (see AUTHORS.txt file) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import unicode_literals, division, absolute_import, print_function import sys import os import ctypes import re import platform if sys.version_info >= (2, 7): import sysconfig if sys.version_info < (3,): str_cls = unicode # noqa else: str_cls = str def _pep425_implementation(): """ :return: A 2 character unicode string of the implementation - 'cp' for cpython or 'pp' for PyPy """ return 'pp' if hasattr(sys, 'pypy_version_info') else 'cp' def _pep425_version(): """ :return: A tuple of integers representing the Python version number """ if hasattr(sys, 'pypy_version_info'): return (sys.version_info[0], sys.pypy_version_info.major, sys.pypy_version_info.minor) else: return (sys.version_info[0], sys.version_info[1]) def _pep425_supports_manylinux(): """ :return: A boolean indicating if the machine can use manylinux1 packages """ try: import _manylinux return bool(_manylinux.manylinux1_compatible) except (ImportError, AttributeError): pass # Check for glibc 2.5 try: proc = ctypes.CDLL(None) gnu_get_libc_version = proc.gnu_get_libc_version gnu_get_libc_version.restype = ctypes.c_char_p ver = gnu_get_libc_version() if not isinstance(ver, str_cls): ver = ver.decode('ascii') match = re.match(r'(\d+)\.(\d+)', ver) return match and match.group(1) == '2' and int(match.group(2)) >= 5 except (AttributeError): return False def _pep425_get_abi(): """ :return: A unicode string of the system abi. Will be something like: "cp27m", "cp33m", etc. """ try: soabi = sysconfig.get_config_var('SOABI') if soabi: if soabi.startswith('cpython-'): return 'cp%s' % soabi.split('-')[1] return soabi.replace('.', '_').replace('-', '_') except (IOError, NameError): pass impl = _pep425_implementation() suffix = '' if impl == 'cp': suffix += 'm' if sys.maxunicode == 0x10ffff and sys.version_info < (3, 3): suffix += 'u' return '%s%s%s' % (impl, ''.join(map(str_cls, _pep425_version())), suffix) def _pep425tags(): """ :return: A list of 3-element tuples with unicode strings or None: [0] implementation tag - cp33, pp27, cp26, py2, py2.py3 [1] abi tag - cp26m, None [2] arch tag - linux_x86_64, macosx_10_10_x85_64, etc """ tags = [] versions = [] version_info = _pep425_version() major = version_info[:-1] for minor in range(version_info[-1], -1, -1): versions.append(''.join(map(str, major + (minor,)))) impl = _pep425_implementation() abis = [] abi = _pep425_get_abi() if abi: abis.append(abi) abi3 = _pep425_implementation() == 'cp' and sys.version_info >= (3,) if abi3: abis.append('abi3') abis.append('none') if sys.platform == 'darwin': plat_ver = platform.mac_ver() ver_parts = plat_ver[0].split('.') minor = int(ver_parts[1]) arch = plat_ver[2] if sys.maxsize == 2147483647: arch = 'i386' arches = [] while minor > 5: arches.append('macosx_10_%s_%s' % (minor, arch)) arches.append('macosx_10_%s_intel' % (minor,)) arches.append('macosx_10_%s_universal' % (minor,)) minor -= 1 else: if sys.platform == 'win32': if 'amd64' in sys.version.lower(): arches = ['win_amd64'] arches = [sys.platform] elif hasattr(os, 'uname'): (plat, _, _, _, machine) = os.uname() plat = plat.lower().replace('/', '') machine.replace(' ', '_').replace('/', '_') if plat == 'linux' and sys.maxsize == 2147483647: machine = 'i686' arch = '%s_%s' % (plat, machine) if _pep425_supports_manylinux(): arches = [arch.replace('linux', 'manylinux1'), arch] else: arches = [arch] for abi in abis: for arch in arches: tags.append(('%s%s' % (impl, versions[0]), abi, arch)) if abi3: for version in versions[1:]: for arch in arches: tags.append(('%s%s' % (impl, version), 'abi3', arch)) for arch in arches: tags.append(('py%s' % (versions[0][0]), 'none', arch)) tags.append(('%s%s' % (impl, versions[0]), 'none', 'any')) tags.append(('%s%s' % (impl, versions[0][0]), 'none', 'any')) for i, version in enumerate(versions): tags.append(('py%s' % (version,), 'none', 'any')) if i == 0: tags.append(('py%s' % (version[0]), 'none', 'any')) tags.append(('py2.py3', 'none', 'any')) return tags oscrypto-1.3.0/dev/_task.py000066400000000000000000000101441421476274700156440ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import ast import _ast import os import sys from . import package_root, task_keyword_args from ._import import _import_from if sys.version_info < (3,): byte_cls = str else: byte_cls = bytes def _list_tasks(): """ Fetches a list of all valid tasks that may be run, and the args they accept. Does not actually import the task module to prevent errors if a user does not have the dependencies installed for every task. :return: A list of 2-element tuples: 0: a unicode string of the task name 1: a list of dicts containing the parameter definitions """ out = [] dev_path = os.path.join(package_root, 'dev') for fname in sorted(os.listdir(dev_path)): if fname.startswith('.') or fname.startswith('_'): continue if not fname.endswith('.py'): continue name = fname[:-3] args = () full_path = os.path.join(package_root, 'dev', fname) with open(full_path, 'rb') as f: full_code = f.read() if sys.version_info >= (3,): full_code = full_code.decode('utf-8') task_node = ast.parse(full_code, filename=full_path) for node in ast.iter_child_nodes(task_node): if isinstance(node, _ast.Assign): if len(node.targets) == 1 \ and isinstance(node.targets[0], _ast.Name) \ and node.targets[0].id == 'run_args': args = ast.literal_eval(node.value) break out.append((name, args)) return out def show_usage(): """ Prints to stderr the valid options for invoking tasks """ valid_tasks = [] for task in _list_tasks(): usage = task[0] for run_arg in task[1]: usage += ' ' name = run_arg.get('name', '') if run_arg.get('required', False): usage += '{%s}' % name else: usage += '[%s]' % name valid_tasks.append(usage) out = 'Usage: run.py' for karg in task_keyword_args: out += ' [%s=%s]' % (karg['name'], karg['placeholder']) out += ' (%s)' % ' | '.join(valid_tasks) print(out, file=sys.stderr) sys.exit(1) def _get_arg(num): """ :return: A unicode string of the requested command line arg """ if len(sys.argv) < num + 1: return None arg = sys.argv[num] if isinstance(arg, byte_cls): arg = arg.decode('utf-8') return arg def run_task(): """ Parses the command line args, invoking the requested task """ arg_num = 1 task = None args = [] kwargs = {} # We look for the task name, processing any global task keyword args # by setting the appropriate env var while True: val = _get_arg(arg_num) if val is None: break next_arg = False for karg in task_keyword_args: if val.startswith(karg['name'] + '='): os.environ[karg['env_var']] = val[len(karg['name']) + 1:] next_arg = True break if next_arg: arg_num += 1 continue task = val break if task is None: show_usage() task_mod = _import_from('dev.%s' % task, package_root, allow_error=True) if task_mod is None: show_usage() run_args = task_mod.__dict__.get('run_args', []) max_args = arg_num + 1 + len(run_args) if len(sys.argv) > max_args: show_usage() for i, run_arg in enumerate(run_args): val = _get_arg(arg_num + 1 + i) if val is None: if run_arg.get('required', False): show_usage() break if run_arg.get('cast') == 'int' and val.isdigit(): val = int(val) kwarg = run_arg.get('kwarg') if kwarg: kwargs[kwarg] = val else: args.append(val) run = task_mod.__dict__.get('run') result = run(*args, **kwargs) sys.exit(int(not result)) oscrypto-1.3.0/dev/api_docs.py000066400000000000000000000345261421476274700163360ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import ast import _ast import os import re import sys import textwrap import commonmark from collections import OrderedDict from . import package_name, package_root, md_source_map, definition_replacements if hasattr(commonmark, 'DocParser'): raise EnvironmentError("commonmark must be version 0.6.0 or newer") def _get_func_info(docstring, def_lineno, code_lines, prefix): """ Extracts the function signature and description of a Python function :param docstring: A unicode string of the docstring for the function :param def_lineno: An integer line number that function was defined on :param code_lines: A list of unicode string lines from the source file the function was defined in :param prefix: A prefix to prepend to all output lines :return: A 2-element tuple: - [0] A unicode string of the function signature with a docstring of parameter info - [1] A markdown snippet of the function description """ def_index = def_lineno - 1 definition = code_lines[def_index] definition = definition.rstrip() while not definition.endswith(':'): def_index += 1 definition += '\n' + code_lines[def_index].rstrip() definition = textwrap.dedent(definition).rstrip(':') definition = definition.replace('\n', '\n' + prefix) description = '' found_colon = False params = '' for line in docstring.splitlines(): if line and line[0] == ':': found_colon = True if not found_colon: if description: description += '\n' description += line else: if params: params += '\n' params += line description = description.strip() description_md = '' if description: description_md = "%s%s" % (prefix, description.replace('\n', '\n' + prefix)) description_md = re.sub('\n>(\\s+)\n', '\n>\n', description_md) params = params.strip() if params: definition += (':\n%s """\n%s ' % (prefix, prefix)) definition += params.replace('\n', '\n%s ' % prefix) definition += ('\n%s """' % prefix) definition = re.sub('\n>(\\s+)\n', '\n>\n', definition) for search, replace in definition_replacements.items(): definition = definition.replace(search, replace) return (definition, description_md) def _find_sections(md_ast, sections, last, last_class, total_lines=None): """ Walks through a commonmark AST to find section headers that delineate content that should be updated by this script :param md_ast: The AST of the markdown document :param sections: A dict to store the start and end lines of a section. The key will be a two-element tuple of the section type ("class", "function", "method" or "attribute") and identifier. The values are a two-element tuple of the start and end line number in the markdown document of the section. :param last: A dict containing information about the last section header seen. Includes the keys "type_name", "identifier", "start_line". :param last_class: A unicode string of the name of the last class found - used when processing methods and attributes. :param total_lines: An integer of the total number of lines in the markdown document - used to work around a bug in the API of the Python port of commonmark """ def child_walker(node): for child, entering in node.walker(): if child == node: continue yield child, entering for child, entering in child_walker(md_ast): if child.t == 'heading': start_line = child.sourcepos[0][0] if child.level == 2: if last: sections[(last['type_name'], last['identifier'])] = (last['start_line'], start_line - 1) last.clear() if child.level in set([3, 5]): heading_elements = [] for heading_child, _ in child_walker(child): heading_elements.append(heading_child) if len(heading_elements) != 2: continue first = heading_elements[0] second = heading_elements[1] if first.t != 'code': continue if second.t != 'text': continue type_name = second.literal.strip() identifier = first.literal.strip().replace('()', '').lstrip('.') if last: sections[(last['type_name'], last['identifier'])] = (last['start_line'], start_line - 1) last.clear() if type_name == 'function': if child.level != 3: continue if type_name == 'class': if child.level != 3: continue last_class.append(identifier) if type_name in set(['method', 'attribute']): if child.level != 5: continue identifier = last_class[-1] + '.' + identifier last.update({ 'type_name': type_name, 'identifier': identifier, 'start_line': start_line, }) elif child.t == 'block_quote': find_sections(child, sections, last, last_class) if last: sections[(last['type_name'], last['identifier'])] = (last['start_line'], total_lines) find_sections = _find_sections def walk_ast(node, code_lines, sections, md_chunks): """ A callback used to walk the Python AST looking for classes, functions, methods and attributes. Generates chunks of markdown markup to replace the existing content. :param node: An _ast module node object :param code_lines: A list of unicode strings - the source lines of the Python file :param sections: A dict of markdown document sections that need to be updated. The key will be a two-element tuple of the section type ("class", "function", "method" or "attribute") and identifier. The values are a two-element tuple of the start and end line number in the markdown document of the section. :param md_chunks: A dict with keys from the sections param and the values being a unicode string containing a chunk of markdown markup. """ if isinstance(node, _ast.FunctionDef): key = ('function', node.name) if key not in sections: return docstring = ast.get_docstring(node) def_lineno = node.lineno + len(node.decorator_list) definition, description_md = _get_func_info(docstring, def_lineno, code_lines, '> ') md_chunk = textwrap.dedent(""" ### `%s()` function > ```python > %s > ``` > %s """).strip() % ( node.name, definition, description_md ) + "\n" md_chunks[key] = md_chunk.replace('>\n\n', '') elif isinstance(node, _ast.ClassDef): if ('class', node.name) not in sections: return for subnode in node.body: if isinstance(subnode, _ast.FunctionDef): node_id = node.name + '.' + subnode.name method_key = ('method', node_id) is_method = method_key in sections attribute_key = ('attribute', node_id) is_attribute = attribute_key in sections is_constructor = subnode.name == '__init__' if not is_constructor and not is_attribute and not is_method: continue docstring = ast.get_docstring(subnode) def_lineno = subnode.lineno if sys.version_info < (3, 8): def_lineno += len(subnode.decorator_list) if not docstring: continue if is_method or is_constructor: definition, description_md = _get_func_info(docstring, def_lineno, code_lines, '> > ') if is_constructor: key = ('class', node.name) class_docstring = ast.get_docstring(node) or '' class_description = textwrap.dedent(class_docstring).strip() if class_description: class_description_md = "> %s\n>" % (class_description.replace("\n", "\n> ")) else: class_description_md = '' md_chunk = textwrap.dedent(""" ### `%s()` class %s > ##### constructor > > > ```python > > %s > > ``` > > %s """).strip() % ( node.name, class_description_md, definition, description_md ) md_chunk = md_chunk.replace('\n\n\n', '\n\n') else: key = method_key md_chunk = textwrap.dedent(""" > > ##### `.%s()` method > > > ```python > > %s > > ``` > > %s """).strip() % ( subnode.name, definition, description_md ) if md_chunk[-5:] == '\n> >\n': md_chunk = md_chunk[0:-5] else: key = attribute_key description = textwrap.dedent(docstring).strip() description_md = "> > %s" % (description.replace("\n", "\n> > ")) md_chunk = textwrap.dedent(""" > > ##### `.%s` attribute > %s """).strip() % ( subnode.name, description_md ) md_chunks[key] = re.sub('[ \\t]+\n', '\n', md_chunk.rstrip()) elif isinstance(node, _ast.If): for subast in node.body: walk_ast(subast, code_lines, sections, md_chunks) for subast in node.orelse: walk_ast(subast, code_lines, sections, md_chunks) def run(): """ Looks through the docs/ dir and parses each markdown document, looking for sections to update from Python docstrings. Looks for section headers in the format: - ### `ClassName()` class - ##### `.method_name()` method - ##### `.attribute_name` attribute - ### `function_name()` function The markdown content following these section headers up until the next section header will be replaced by new markdown generated from the Python docstrings of the associated source files. By default maps docs/{name}.md to {modulename}/{name}.py. Allows for custom mapping via the md_source_map variable. """ print('Updating API docs...') md_files = [] for root, _, filenames in os.walk(os.path.join(package_root, 'docs')): for filename in filenames: if not filename.endswith('.md'): continue md_files.append(os.path.join(root, filename)) parser = commonmark.Parser() for md_file in md_files: md_file_relative = md_file[len(package_root) + 1:] if md_file_relative in md_source_map: py_files = md_source_map[md_file_relative] py_paths = [os.path.join(package_root, py_file) for py_file in py_files] else: py_files = [os.path.basename(md_file).replace('.md', '.py')] py_paths = [os.path.join(package_root, package_name, py_files[0])] if not os.path.exists(py_paths[0]): continue with open(md_file, 'rb') as f: markdown = f.read().decode('utf-8') original_markdown = markdown md_lines = list(markdown.splitlines()) md_ast = parser.parse(markdown) last_class = [] last = {} sections = OrderedDict() find_sections(md_ast, sections, last, last_class, markdown.count("\n") + 1) md_chunks = {} for index, py_file in enumerate(py_files): py_path = py_paths[index] with open(os.path.join(py_path), 'rb') as f: code = f.read().decode('utf-8') module_ast = ast.parse(code, filename=py_file) code_lines = list(code.splitlines()) for node in ast.iter_child_nodes(module_ast): walk_ast(node, code_lines, sections, md_chunks) added_lines = 0 def _replace_md(key, sections, md_chunk, md_lines, added_lines): start, end = sections[key] start -= 1 start += added_lines end += added_lines new_lines = md_chunk.split('\n') added_lines += len(new_lines) - (end - start) # Ensure a newline above each class header if start > 0 and md_lines[start][0:4] == '### ' and md_lines[start - 1][0:1] == '>': added_lines += 1 new_lines.insert(0, '') md_lines[start:end] = new_lines return added_lines for key in sections: if key not in md_chunks: raise ValueError('No documentation found for %s' % key[1]) added_lines = _replace_md(key, sections, md_chunks[key], md_lines, added_lines) markdown = '\n'.join(md_lines).strip() + '\n' if original_markdown != markdown: with open(md_file, 'wb') as f: f.write(markdown.encode('utf-8')) if __name__ == '__main__': run() oscrypto-1.3.0/dev/build.py000066400000000000000000000055361421476274700156530ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import imp import os import tarfile import zipfile import setuptools.sandbox from . import package_root, package_name, has_tests_package def _list_zip(filename): """ Prints all of the files in a .zip file """ zf = zipfile.ZipFile(filename, 'r') for name in zf.namelist(): print(' %s' % name) def _list_tgz(filename): """ Prints all of the files in a .tar.gz file """ tf = tarfile.open(filename, 'r:gz') for name in tf.getnames(): print(' %s' % name) def run(): """ Creates a sdist .tar.gz and a bdist_wheel --univeral .whl :return: A bool - if the packaging process was successful """ setup = os.path.join(package_root, 'setup.py') tests_root = os.path.join(package_root, 'tests') tests_setup = os.path.join(tests_root, 'setup.py') # Trying to call setuptools.sandbox.run_setup(setup, ['--version']) # resulted in a segfault, so we do this instead module_info = imp.find_module('version', [os.path.join(package_root, package_name)]) version_mod = imp.load_module('%s.version' % package_name, *module_info) pkg_name_info = (package_name, version_mod.__version__) print('Building %s-%s' % pkg_name_info) sdist = '%s-%s.tar.gz' % pkg_name_info whl = '%s-%s-py2.py3-none-any.whl' % pkg_name_info setuptools.sandbox.run_setup(setup, ['-q', 'sdist']) print(' - created %s' % sdist) _list_tgz(os.path.join(package_root, 'dist', sdist)) setuptools.sandbox.run_setup(setup, ['-q', 'bdist_wheel', '--universal']) print(' - created %s' % whl) _list_zip(os.path.join(package_root, 'dist', whl)) setuptools.sandbox.run_setup(setup, ['-q', 'clean']) if has_tests_package: print('Building %s_tests-%s' % (package_name, version_mod.__version__)) tests_sdist = '%s_tests-%s.tar.gz' % pkg_name_info tests_whl = '%s_tests-%s-py2.py3-none-any.whl' % pkg_name_info setuptools.sandbox.run_setup(tests_setup, ['-q', 'sdist']) print(' - created %s' % tests_sdist) _list_tgz(os.path.join(tests_root, 'dist', tests_sdist)) setuptools.sandbox.run_setup(tests_setup, ['-q', 'bdist_wheel', '--universal']) print(' - created %s' % tests_whl) _list_zip(os.path.join(tests_root, 'dist', tests_whl)) setuptools.sandbox.run_setup(tests_setup, ['-q', 'clean']) dist_dir = os.path.join(package_root, 'dist') tests_dist_dir = os.path.join(tests_root, 'dist') os.rename( os.path.join(tests_dist_dir, tests_sdist), os.path.join(dist_dir, tests_sdist) ) os.rename( os.path.join(tests_dist_dir, tests_whl), os.path.join(dist_dir, tests_whl) ) os.rmdir(tests_dist_dir) return True oscrypto-1.3.0/dev/ci-cleanup.py000066400000000000000000000013731421476274700165670ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os import shutil from . import build_root, other_packages def run(): """ Cleans up CI dependencies - used for persistent GitHub Actions Runners since they don't clean themselves up. """ print("Removing ci dependencies") deps_dir = os.path.join(build_root, 'modularcrypto-deps') if os.path.exists(deps_dir): shutil.rmtree(deps_dir, ignore_errors=True) print("Removing modularcrypto packages") for other_package in other_packages: pkg_dir = os.path.join(build_root, other_package) if os.path.exists(pkg_dir): shutil.rmtree(pkg_dir, ignore_errors=True) print() return True oscrypto-1.3.0/dev/ci-driver.py000066400000000000000000000036331421476274700164340ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os import platform import sys import subprocess run_args = [ { 'name': 'cffi', 'kwarg': 'cffi', }, { 'name': 'openssl', 'kwarg': 'openssl', }, { 'name': 'winlegacy', 'kwarg': 'winlegacy', }, ] def _write_env(env, key, value): sys.stdout.write("%s: %s\n" % (key, value)) sys.stdout.flush() if sys.version_info < (3,): env[key.encode('utf-8')] = value.encode('utf-8') else: env[key] = value def run(**_): """ Runs CI, setting various env vars :return: A bool - if the CI ran successfully """ env = os.environ.copy() options = set(sys.argv[2:]) newline = False if 'cffi' not in options: _write_env(env, 'OSCRYPTO_USE_CTYPES', 'true') newline = True if 'openssl' in options and sys.platform == 'darwin': mac_version_info = tuple(map(int, platform.mac_ver()[0].split('.')[:2])) if mac_version_info < (10, 15): _write_env(env, 'OSCRYPTO_USE_OPENSSL', '/usr/lib/libcrypto.dylib,/usr/lib/libssl.dylib') else: _write_env(env, 'OSCRYPTO_USE_OPENSSL', '/usr/lib/libcrypto.35.dylib,/usr/lib/libssl.35.dylib') newline = True if 'openssl3' in options and sys.platform == 'darwin': _write_env( env, 'OSCRYPTO_USE_OPENSSL', '/usr/local/opt/openssl@3/lib/libcrypto.dylib,/usr/local/opt/openssl@3/lib/libssl.dylib' ) if 'winlegacy' in options: _write_env(env, 'OSCRYPTO_USE_WINLEGACY', 'true') newline = True if newline: sys.stdout.write("\n") proc = subprocess.Popen( [ sys.executable, 'run.py', 'ci', ], env=env ) proc.communicate() return proc.returncode == 0 oscrypto-1.3.0/dev/ci.py000066400000000000000000000025241421476274700151410ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os import site import sys from . import build_root, requires_oscrypto from ._import import _preload deps_dir = os.path.join(build_root, 'modularcrypto-deps') if os.path.exists(deps_dir): site.addsitedir(deps_dir) # In case any of the deps are installed system-wide sys.path.insert(0, deps_dir) if sys.version_info[0:2] not in [(2, 6), (3, 2)]: from .lint import run as run_lint else: run_lint = None if sys.version_info[0:2] != (3, 2): from .coverage import run as run_coverage from .coverage import coverage run_tests = None else: from .tests import run as run_tests run_coverage = None def run(): """ Runs the linter and tests :return: A bool - if the linter and tests ran successfully """ _preload(requires_oscrypto, True) if run_lint: print('') lint_result = run_lint() else: lint_result = True if run_coverage: print('\nRunning tests (via coverage.py %s)' % coverage.__version__) sys.stdout.flush() tests_result = run_coverage(ci=True) else: print('\nRunning tests') sys.stdout.flush() tests_result = run_tests(ci=True) sys.stdout.flush() return lint_result and tests_result oscrypto-1.3.0/dev/codecov.json000066400000000000000000000001461421476274700165070ustar00rootroot00000000000000{ "slug": "wbond/oscrypto", "token": "020411b6-d436-4c7e-8866-640c263cb774", "disabled": true } oscrypto-1.3.0/dev/coverage.py000066400000000000000000000530451421476274700163450ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import cgi import codecs import coverage import imp import json import os import unittest import re import sys import tempfile import time import platform as _plat import subprocess from fnmatch import fnmatch from . import package_name, package_root, other_packages if sys.version_info < (3,): str_cls = unicode # noqa from urllib2 import URLError from urllib import urlencode from io import open else: str_cls = str from urllib.error import URLError from urllib.parse import urlencode if sys.version_info < (3, 7): Pattern = re._pattern_type else: Pattern = re.Pattern def run(ci=False): """ Runs the tests while measuring coverage :param ci: If coverage is being run in a CI environment - this triggers trying to run the tests for the rest of modularcrypto and uploading coverage data :return: A bool - if the tests ran successfully """ xml_report_path = os.path.join(package_root, 'coverage.xml') if os.path.exists(xml_report_path): os.unlink(xml_report_path) cov = coverage.Coverage(include='%s/*.py' % package_name) cov.start() from .tests import run as run_tests result = run_tests(ci=ci) print() if ci: suite = unittest.TestSuite() loader = unittest.TestLoader() for other_package in other_packages: for test_class in _load_package_tests(other_package): suite.addTest(loader.loadTestsFromTestCase(test_class)) if suite.countTestCases() > 0: print('Running tests from other modularcrypto packages') sys.stdout.flush() runner_result = unittest.TextTestRunner(stream=sys.stdout, verbosity=1).run(suite) result = runner_result.wasSuccessful() and result print() sys.stdout.flush() cov.stop() cov.save() cov.report(show_missing=False) print() sys.stdout.flush() if ci: cov.xml_report() if ci and result and os.path.exists(xml_report_path): _codecov_submit() print() return result def _load_package_tests(name): """ Load the test classes from another modularcrypto package :param name: A unicode string of the other package name :return: A list of unittest.TestCase classes of the tests for the package """ package_dir = os.path.join('..', name) if not os.path.exists(package_dir): return [] tests_module_info = imp.find_module('tests', [package_dir]) tests_module = imp.load_module('%s.tests' % name, *tests_module_info) return tests_module.test_classes() def _env_info(): """ :return: A two-element tuple of unicode strings. The first is the name of the environment, the second the root of the repo. The environment name will be one of: "ci-travis", "ci-circle", "ci-appveyor", "ci-github-actions", "local" """ if os.getenv('CI') == 'true' and os.getenv('TRAVIS') == 'true': return ('ci-travis', os.getenv('TRAVIS_BUILD_DIR')) if os.getenv('CI') == 'True' and os.getenv('APPVEYOR') == 'True': return ('ci-appveyor', os.getenv('APPVEYOR_BUILD_FOLDER')) if os.getenv('CI') == 'true' and os.getenv('CIRCLECI') == 'true': return ('ci-circle', os.getcwdu() if sys.version_info < (3,) else os.getcwd()) if os.getenv('GITHUB_ACTIONS') == 'true': return ('ci-github-actions', os.getenv('GITHUB_WORKSPACE')) return ('local', package_root) def _codecov_submit(): env_name, root = _env_info() try: with open(os.path.join(root, 'dev/codecov.json'), 'rb') as f: json_data = json.loads(f.read().decode('utf-8')) except (OSError, ValueError, UnicodeDecodeError, KeyError): print('error reading codecov.json') return if json_data.get('disabled'): return if env_name == 'ci-travis': # http://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables build_url = 'https://travis-ci.org/%s/jobs/%s' % (os.getenv('TRAVIS_REPO_SLUG'), os.getenv('TRAVIS_JOB_ID')) query = { 'service': 'travis', 'branch': os.getenv('TRAVIS_BRANCH'), 'build': os.getenv('TRAVIS_JOB_NUMBER'), 'pr': os.getenv('TRAVIS_PULL_REQUEST'), 'job': os.getenv('TRAVIS_JOB_ID'), 'tag': os.getenv('TRAVIS_TAG'), 'slug': os.getenv('TRAVIS_REPO_SLUG'), 'commit': os.getenv('TRAVIS_COMMIT'), 'build_url': build_url, } elif env_name == 'ci-appveyor': # http://www.appveyor.com/docs/environment-variables build_url = 'https://ci.appveyor.com/project/%s/build/%s' % ( os.getenv('APPVEYOR_REPO_NAME'), os.getenv('APPVEYOR_BUILD_VERSION') ) query = { 'service': "appveyor", 'branch': os.getenv('APPVEYOR_REPO_BRANCH'), 'build': os.getenv('APPVEYOR_JOB_ID'), 'pr': os.getenv('APPVEYOR_PULL_REQUEST_NUMBER'), 'job': '/'.join(( os.getenv('APPVEYOR_ACCOUNT_NAME'), os.getenv('APPVEYOR_PROJECT_SLUG'), os.getenv('APPVEYOR_BUILD_VERSION') )), 'tag': os.getenv('APPVEYOR_REPO_TAG_NAME'), 'slug': os.getenv('APPVEYOR_REPO_NAME'), 'commit': os.getenv('APPVEYOR_REPO_COMMIT'), 'build_url': build_url, } elif env_name == 'ci-circle': # https://circleci.com/docs/environment-variables query = { 'service': 'circleci', 'branch': os.getenv('CIRCLE_BRANCH'), 'build': os.getenv('CIRCLE_BUILD_NUM'), 'pr': os.getenv('CIRCLE_PR_NUMBER'), 'job': os.getenv('CIRCLE_BUILD_NUM') + "." + os.getenv('CIRCLE_NODE_INDEX'), 'tag': os.getenv('CIRCLE_TAG'), 'slug': os.getenv('CIRCLE_PROJECT_USERNAME') + "/" + os.getenv('CIRCLE_PROJECT_REPONAME'), 'commit': os.getenv('CIRCLE_SHA1'), 'build_url': os.getenv('CIRCLE_BUILD_URL'), } elif env_name == 'ci-github-actions': branch = '' tag = '' ref = os.getenv('GITHUB_REF', '') if ref.startswith('refs/tags/'): tag = ref[10:] elif ref.startswith('refs/heads/'): branch = ref[11:] impl = _plat.python_implementation() major, minor = _plat.python_version_tuple()[0:2] build_name = '%s %s %s.%s' % (_platform_name(), impl, major, minor) query = { 'service': 'custom', 'token': json_data['token'], 'branch': branch, 'tag': tag, 'slug': os.getenv('GITHUB_REPOSITORY'), 'commit': os.getenv('GITHUB_SHA'), 'build_url': 'https://github.com/wbond/oscrypto/commit/%s/checks' % os.getenv('GITHUB_SHA'), 'name': 'GitHub Actions %s on %s' % (build_name, os.getenv('RUNNER_OS')) } else: if not os.path.exists(os.path.join(root, '.git')): print('git repository not found, not submitting coverage data') return git_status = _git_command(['status', '--porcelain'], root) if git_status != '': print('git repository has uncommitted changes, not submitting coverage data') return branch = _git_command(['rev-parse', '--abbrev-ref', 'HEAD'], root) commit = _git_command(['rev-parse', '--verify', 'HEAD'], root) tag = _git_command(['name-rev', '--tags', '--name-only', commit], root) impl = _plat.python_implementation() major, minor = _plat.python_version_tuple()[0:2] build_name = '%s %s %s.%s' % (_platform_name(), impl, major, minor) query = { 'branch': branch, 'commit': commit, 'slug': json_data['slug'], 'token': json_data['token'], 'build': build_name, } if tag != 'undefined': query['tag'] = tag payload = 'PLATFORM=%s\n' % _platform_name() payload += 'PYTHON_VERSION=%s %s\n' % (_plat.python_version(), _plat.python_implementation()) if 'oscrypto' in sys.modules: payload += 'OSCRYPTO_BACKEND=%s\n' % sys.modules['oscrypto'].backend() payload += '<<<<<< ENV\n' for path in _list_files(root): payload += path + '\n' payload += '<<<<<< network\n' payload += '# path=coverage.xml\n' with open(os.path.join(root, 'coverage.xml'), 'r', encoding='utf-8') as f: payload += f.read() + '\n' payload += '<<<<<< EOF\n' url = 'https://codecov.io/upload/v4' headers = { 'Accept': 'text/plain' } filtered_query = {} for key in query: value = query[key] if value == '' or value is None: continue filtered_query[key] = value print('Submitting coverage info to codecov.io') info = _do_request( 'POST', url, headers, query_params=filtered_query ) encoding = info[1] or 'utf-8' text = info[2].decode(encoding).strip() parts = text.split() upload_url = parts[1] headers = { 'Content-Type': 'text/plain', 'x-amz-acl': 'public-read', 'x-amz-storage-class': 'REDUCED_REDUNDANCY' } print('Uploading coverage data to codecov.io S3 bucket') _do_request( 'PUT', upload_url, headers, data=payload.encode('utf-8') ) def _git_command(params, cwd): """ Executes a git command, returning the output :param params: A list of the parameters to pass to git :param cwd: The working directory to execute git in :return: A 2-element tuple of (stdout, stderr) """ proc = subprocess.Popen( ['git'] + params, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=cwd ) stdout, stderr = proc.communicate() code = proc.wait() if code != 0: e = OSError('git exit code was non-zero') e.stdout = stdout raise e return stdout.decode('utf-8').strip() def _parse_env_var_file(data): """ Parses a basic VAR="value data" file contents into a dict :param data: A unicode string of the file data :return: A dict of parsed name/value data """ output = {} for line in data.splitlines(): line = line.strip() if not line or '=' not in line: continue parts = line.split('=') if len(parts) != 2: continue name = parts[0] value = parts[1] if len(value) > 1: if value[0] == '"' and value[-1] == '"': value = value[1:-1] output[name] = value return output def _platform_name(): """ Returns information about the current operating system and version :return: A unicode string containing the OS name and version """ if sys.platform == 'darwin': version = _plat.mac_ver()[0] _plat_ver_info = tuple(map(int, version.split('.'))) if _plat_ver_info < (10, 12): name = 'OS X' else: name = 'macOS' return '%s %s' % (name, version) elif sys.platform == 'win32': _win_ver = sys.getwindowsversion() _plat_ver_info = (_win_ver[0], _win_ver[1]) return 'Windows %s' % _plat.win32_ver()[0] elif sys.platform in ['linux', 'linux2']: if os.path.exists('/etc/os-release'): with open('/etc/os-release', 'r', encoding='utf-8') as f: pairs = _parse_env_var_file(f.read()) if 'NAME' in pairs and 'VERSION_ID' in pairs: return '%s %s' % (pairs['NAME'], pairs['VERSION_ID']) version = pairs['VERSION_ID'] elif 'PRETTY_NAME' in pairs: return pairs['PRETTY_NAME'] elif 'NAME' in pairs: return pairs['NAME'] else: raise ValueError('No suitable version info found in /etc/os-release') elif os.path.exists('/etc/lsb-release'): with open('/etc/lsb-release', 'r', encoding='utf-8') as f: pairs = _parse_env_var_file(f.read()) if 'DISTRIB_DESCRIPTION' in pairs: return pairs['DISTRIB_DESCRIPTION'] else: raise ValueError('No suitable version info found in /etc/lsb-release') else: return 'Linux' else: return '%s %s' % (_plat.system(), _plat.release()) def _list_files(root): """ Lists all of the files in a directory, taking into account any .gitignore file that is present :param root: A unicode filesystem path :return: A list of unicode strings, containing paths of all files not ignored by .gitignore with root, using relative paths """ dir_patterns, file_patterns = _gitignore(root) paths = [] prefix = os.path.abspath(root) + os.sep for base, dirs, files in os.walk(root): for d in dirs: for dir_pattern in dir_patterns: if fnmatch(d, dir_pattern): dirs.remove(d) break for f in files: skip = False for file_pattern in file_patterns: if fnmatch(f, file_pattern): skip = True break if skip: continue full_path = os.path.join(base, f) if full_path[:len(prefix)] == prefix: full_path = full_path[len(prefix):] paths.append(full_path) return sorted(paths) def _gitignore(root): """ Parses a .gitignore file and returns patterns to match dirs and files. Only basic gitignore patterns are supported. Pattern negation, ** wildcards and anchored patterns are not currently implemented. :param root: A unicode string of the path to the git repository :return: A 2-element tuple: - 0: a list of unicode strings to match against dirs - 1: a list of unicode strings to match against dirs and files """ gitignore_path = os.path.join(root, '.gitignore') dir_patterns = ['.git'] file_patterns = [] if not os.path.exists(gitignore_path): return (dir_patterns, file_patterns) with open(gitignore_path, 'r', encoding='utf-8') as f: for line in f.readlines(): line = line.strip() if not line: continue if line.startswith('#'): continue if '**' in line: raise NotImplementedError('gitignore ** wildcards are not implemented') if line.startswith('!'): raise NotImplementedError('gitignore pattern negation is not implemented') if line.startswith('/'): raise NotImplementedError('gitignore anchored patterns are not implemented') if line.startswith('\\#'): line = '#' + line[2:] if line.startswith('\\!'): line = '!' + line[2:] if line.endswith('/'): dir_patterns.append(line[:-1]) else: file_patterns.append(line) return (dir_patterns, file_patterns) def _do_request(method, url, headers, data=None, query_params=None, timeout=20): """ Performs an HTTP request :param method: A unicode string of 'POST' or 'PUT' :param url; A unicode string of the URL to request :param headers: A dict of unicode strings, where keys are header names and values are the header values. :param data: A dict of unicode strings (to be encoded as application/x-www-form-urlencoded), or a byte string of data. :param query_params: A dict of unicode keys and values to pass as query params :param timeout: An integer number of seconds to use as the timeout :return: A 3-element tuple: - 0: A unicode string of the response content-type - 1: A unicode string of the response encoding, or None - 2: A byte string of the response body """ if query_params: url += '?' + urlencode(query_params).replace('+', '%20') if isinstance(data, dict): data_bytes = {} for key in data: data_bytes[key.encode('utf-8')] = data[key].encode('utf-8') data = urlencode(data_bytes) headers['Content-Type'] = 'application/x-www-form-urlencoded' if isinstance(data, str_cls): raise TypeError('data must be a byte string') try: tempfd, tempf_path = tempfile.mkstemp('-coverage') os.write(tempfd, data or b'') os.close(tempfd) if sys.platform == 'win32': powershell_exe = os.path.join('system32\\WindowsPowerShell\\v1.0\\powershell.exe') code = "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12;" code += "$wc = New-Object Net.WebClient;" for key in headers: code += "$wc.Headers.add('%s','%s');" % (key, headers[key]) code += "$out = $wc.UploadFile('%s', '%s', '%s');" % (url, method, tempf_path) code += "[System.Text.Encoding]::GetEncoding('ISO-8859-1').GetString($wc.ResponseHeaders.ToByteArray())" # To properly obtain bytes, we use BitConverter to get hex dash # encoding (e.g. AE-09-3F) and they decode in python code += " + [System.BitConverter]::ToString($out);" stdout, stderr = _execute( [powershell_exe, '-Command', code], os.getcwd(), re.compile(r'Unable to connect to|TLS|Internal Server Error'), 6 ) if stdout[-2:] == b'\r\n' and b'\r\n\r\n' in stdout: # An extra trailing crlf is added at the end by powershell stdout = stdout[0:-2] parts = stdout.split(b'\r\n\r\n', 1) if len(parts) == 2: stdout = parts[0] + b'\r\n\r\n' + codecs.decode(parts[1].replace(b'-', b''), 'hex_codec') else: args = [ 'curl', '--http1.1', '--connect-timeout', '5', '--request', method, '--location', '--silent', '--show-error', '--include', # Prevent curl from asking for an HTTP "100 Continue" response '--header', 'Expect:' ] for key in headers: args.append('--header') args.append("%s: %s" % (key, headers[key])) args.append('--data-binary') args.append('@%s' % tempf_path) args.append(url) stdout, stderr = _execute( args, os.getcwd(), re.compile(r'Failed to connect to|TLS|SSLRead|outstanding|cleanly|timed out'), 6 ) finally: if tempf_path and os.path.exists(tempf_path): os.remove(tempf_path) if len(stderr) > 0: raise URLError("Error %sing %s:\n%s" % (method, url, stderr)) parts = stdout.split(b'\r\n\r\n', 1) if len(parts) != 2: raise URLError("Error %sing %s, response data malformed:\n%s" % (method, url, stdout)) header_block, body = parts content_type_header = None content_len_header = None for hline in header_block.decode('iso-8859-1').splitlines(): hline_parts = hline.split(':', 1) if len(hline_parts) != 2: continue name, val = hline_parts name = name.strip().lower() val = val.strip() if name == 'content-type': content_type_header = val if name == 'content-length': content_len_header = val if content_type_header is None and content_len_header != '0': raise URLError("Error %sing %s, no content-type header:\n%s" % (method, url, stdout)) if content_type_header is None: content_type = 'text/plain' encoding = 'utf-8' else: content_type, params = cgi.parse_header(content_type_header) encoding = params.get('charset') return (content_type, encoding, body) def _execute(params, cwd, retry=None, retries=0, backoff=2): """ Executes a subprocess :param params: A list of the executable and arguments to pass to it :param cwd: The working directory to execute the command in :param retry: If this string is present in stderr, or regex pattern matches stderr, retry the operation :param retries: An integer number of times to retry :return: A 2-element tuple of (stdout, stderr) """ proc = subprocess.Popen( params, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd ) stdout, stderr = proc.communicate() code = proc.wait() if code != 0: if retry and retries > 0: stderr_str = stderr.decode('utf-8') if isinstance(retry, Pattern): if retry.search(stderr_str) is not None: time.sleep(backoff) return _execute(params, cwd, retry, retries - 1, backoff * 2) elif retry in stderr_str: time.sleep(backoff) return _execute(params, cwd, retry, retries - 1, backoff * 2) e = OSError('subprocess exit code for "%s" was %d: %s' % (' '.join(params), code, stderr)) e.stdout = stdout e.stderr = stderr raise e return (stdout, stderr) if __name__ == '__main__': _codecov_submit() oscrypto-1.3.0/dev/deps.py000066400000000000000000000525021421476274700155020ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os import subprocess import sys import shutil import re import json import tarfile import zipfile from . import package_root, build_root, other_packages from ._pep425 import _pep425tags, _pep425_implementation if sys.version_info < (3,): str_cls = unicode # noqa else: str_cls = str def run(): """ Installs required development dependencies. Uses git to checkout other modularcrypto repos for more accurate coverage data. """ deps_dir = os.path.join(build_root, 'modularcrypto-deps') if os.path.exists(deps_dir): shutil.rmtree(deps_dir, ignore_errors=True) os.mkdir(deps_dir) try: print("Staging ci dependencies") _stage_requirements(deps_dir, os.path.join(package_root, 'requires', 'ci')) print("Checking out modularcrypto packages for coverage") for other_package in other_packages: pkg_url = 'https://github.com/wbond/%s.git' % other_package pkg_dir = os.path.join(build_root, other_package) if os.path.exists(pkg_dir): print("%s is already present" % other_package) continue print("Cloning %s" % pkg_url) _execute(['git', 'clone', pkg_url], build_root) print() except (Exception): if os.path.exists(deps_dir): shutil.rmtree(deps_dir, ignore_errors=True) raise return True def _download(url, dest): """ Downloads a URL to a directory :param url: The URL to download :param dest: The path to the directory to save the file in :return: The filesystem path to the saved file """ print('Downloading %s' % url) filename = os.path.basename(url) dest_path = os.path.join(dest, filename) if sys.platform == 'win32': powershell_exe = os.path.join('system32\\WindowsPowerShell\\v1.0\\powershell.exe') code = "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12;" code += "(New-Object Net.WebClient).DownloadFile('%s', '%s');" % (url, dest_path) _execute([powershell_exe, '-Command', code], dest, 'Unable to connect to') else: _execute( ['curl', '-L', '--silent', '--show-error', '-O', url], dest, 'Failed to connect to' ) return dest_path def _tuple_from_ver(version_string): """ :param version_string: A unicode dotted version string :return: A tuple of integers """ match = re.search( r'(\d+(?:\.\d+)*)' r'([-._]?(?:alpha|a|beta|b|preview|pre|c|rc)\.?\d*)?' r'(-\d+|(?:[-._]?(?:rev|r|post)\.?\d*))?' r'([-._]?dev\.?\d*)?', version_string ) if not match: return tuple() nums = tuple(map(int, match.group(1).split('.'))) pre = match.group(2) if pre: pre = pre.replace('alpha', 'a') pre = pre.replace('beta', 'b') pre = pre.replace('preview', 'rc') pre = pre.replace('pre', 'rc') pre = re.sub(r'(?= (3,): env['PYTHONPATH'] = deps_dir else: env[b'PYTHONPATH'] = deps_dir.encode('utf-8') _execute( [ sys.executable, 'setup.py', 'install', '--root=%s' % root, '--install-lib=%s' % install_lib, '--no-compile' ], setup_dir, env=env ) finally: if ar: ar.close() if staging_dir: shutil.rmtree(staging_dir) def _sort_pep440_versions(releases, include_prerelease): """ :param releases: A list of unicode string PEP 440 version numbers :param include_prerelease: A boolean indicating if prerelease versions should be included :return: A sorted generator of 2-element tuples: 0: A unicode string containing a PEP 440 version number 1: A tuple of tuples containing integers - this is the output of _tuple_from_ver() for the PEP 440 version number and is intended for comparing versions """ parsed_versions = [] for v in releases: t = _tuple_from_ver(v) if not include_prerelease and t[1][0] < 0: continue parsed_versions.append((v, t)) return sorted(parsed_versions, key=lambda v: v[1]) def _is_valid_python_version(python_version, requires_python): """ Verifies the "python_version" and "requires_python" keys from a PyPi download record are applicable to the current version of Python :param python_version: The "python_version" value from a PyPi download JSON structure. This should be one of: "py2", "py3", "py2.py3" or "source". :param requires_python: The "requires_python" value from a PyPi download JSON structure. This will be None, or a comma-separated list of conditions that must be true. Ex: ">=3.5", "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" """ if python_version == "py2" and sys.version_info >= (3,): return False if python_version == "py3" and sys.version_info < (3,): return False if requires_python is not None: def _ver_tuples(ver_str): ver_str = ver_str.strip() if ver_str.endswith('.*'): ver_str = ver_str[:-2] cond_tup = tuple(map(int, ver_str.split('.'))) return (sys.version_info[:len(cond_tup)], cond_tup) for part in map(str_cls.strip, requires_python.split(',')): if part.startswith('!='): sys_tup, cond_tup = _ver_tuples(part[2:]) if sys_tup == cond_tup: return False elif part.startswith('>='): sys_tup, cond_tup = _ver_tuples(part[2:]) if sys_tup < cond_tup: return False elif part.startswith('>'): sys_tup, cond_tup = _ver_tuples(part[1:]) if sys_tup <= cond_tup: return False elif part.startswith('<='): sys_tup, cond_tup = _ver_tuples(part[2:]) if sys_tup > cond_tup: return False elif part.startswith('<'): sys_tup, cond_tup = _ver_tuples(part[1:]) if sys_tup >= cond_tup: return False elif part.startswith('=='): sys_tup, cond_tup = _ver_tuples(part[2:]) if sys_tup != cond_tup: return False return True def _locate_suitable_download(downloads): """ :param downloads: A list of dicts containing a key "url", "python_version" and "requires_python" :return: A unicode string URL, or None if not a valid release for the current version of Python """ valid_tags = _pep425tags() exe_suffix = None if sys.platform == 'win32' and _pep425_implementation() == 'cp': win_arch = 'win32' if sys.maxsize == 2147483647 else 'win-amd64' version_info = sys.version_info exe_suffix = '.%s-py%d.%d.exe' % (win_arch, version_info[0], version_info[1]) wheels = {} whl = None tar_bz2 = None tar_gz = None exe = None for download in downloads: if not _is_valid_python_version(download.get('python_version'), download.get('requires_python')): continue if exe_suffix and download['url'].endswith(exe_suffix): exe = download['url'] if download['url'].endswith('.whl'): parts = os.path.basename(download['url']).split('-') tag_impl = parts[-3] tag_abi = parts[-2] tag_arch = parts[-1].split('.')[0] wheels[(tag_impl, tag_abi, tag_arch)] = download['url'] if download['url'].endswith('.tar.bz2'): tar_bz2 = download['url'] if download['url'].endswith('.tar.gz'): tar_gz = download['url'] # Find the most-specific wheel possible for tag in valid_tags: if tag in wheels: whl = wheels[tag] break if exe_suffix and exe: url = exe elif whl: url = whl elif tar_bz2: url = tar_bz2 elif tar_gz: url = tar_gz else: return None return url def _stage_requirements(deps_dir, path): """ Installs requirements without using Python to download, since different services are limiting to TLS 1.2, and older version of Python do not support that :param deps_dir: A unicode path to a temporary directory to use for downloads :param path: A unicode filesystem path to a requirements file """ packages = _parse_requires(path) for p in packages: url = None pkg = p['pkg'] pkg_sub_dir = None if p['type'] == 'url': anchor = None if '#' in pkg: pkg, anchor = pkg.split('#', 1) if '&' in anchor: parts = anchor.split('&') else: parts = [anchor] for part in parts: param, value = part.split('=') if param == 'subdirectory': pkg_sub_dir = value if pkg.endswith('.zip') or pkg.endswith('.tar.gz') or pkg.endswith('.tar.bz2') or pkg.endswith('.whl'): url = pkg else: raise Exception('Unable to install package from URL that is not an archive') else: pypi_json_url = 'https://pypi.org/pypi/%s/json' % pkg json_dest = _download(pypi_json_url, deps_dir) with open(json_dest, 'rb') as f: pkg_info = json.loads(f.read().decode('utf-8')) if os.path.exists(json_dest): os.remove(json_dest) if p['type'] == '==': if p['ver'] not in pkg_info['releases']: raise Exception('Unable to find version %s of %s' % (p['ver'], pkg)) url = _locate_suitable_download(pkg_info['releases'][p['ver']]) if not url: raise Exception('Unable to find a compatible download of %s == %s' % (pkg, p['ver'])) else: p_ver_tup = _tuple_from_ver(p['ver']) for ver_str, ver_tup in reversed(_sort_pep440_versions(pkg_info['releases'], False)): if p['type'] == '>=' and ver_tup < p_ver_tup: break url = _locate_suitable_download(pkg_info['releases'][ver_str]) if url: break if not url: if p['type'] == '>=': raise Exception('Unable to find a compatible download of %s >= %s' % (pkg, p['ver'])) else: raise Exception('Unable to find a compatible download of %s' % pkg) local_path = _download(url, deps_dir) _extract_package(deps_dir, local_path, pkg_sub_dir) os.remove(local_path) def _parse_requires(path): """ Does basic parsing of pip requirements files, to allow for using something other than Python to do actual TLS requests :param path: A path to a requirements file :return: A list of dict objects containing the keys: - 'type' ('any', 'url', '==', '>=') - 'pkg' - 'ver' (if 'type' == '==' or 'type' == '>=') """ python_version = '.'.join(map(str_cls, sys.version_info[0:2])) sys_platform = sys.platform packages = [] with open(path, 'rb') as f: contents = f.read().decode('utf-8') for line in re.split(r'\r?\n', contents): line = line.strip() if not len(line): continue if re.match(r'^\s*#', line): continue if ';' in line: package, cond = line.split(';', 1) package = package.strip() cond = cond.strip() cond = cond.replace('sys_platform', repr(sys_platform)) cond = re.sub( r'[\'"]' r'(\d+(?:\.\d+)*)' r'([-._]?(?:alpha|a|beta|b|preview|pre|c|rc)\.?\d*)?' r'(-\d+|(?:[-._]?(?:rev|r|post)\.?\d*))?' r'([-._]?dev\.?\d*)?' r'[\'"]', r'_tuple_from_ver(\g<0>)', cond ) cond = cond.replace('python_version', '_tuple_from_ver(%r)' % python_version) if not eval(cond): continue else: package = line.strip() if re.match(r'^\s*-r\s*', package): sub_req_file = re.sub(r'^\s*-r\s*', '', package) sub_req_file = os.path.abspath(os.path.join(os.path.dirname(path), sub_req_file)) packages.extend(_parse_requires(sub_req_file)) continue if re.match(r'https?://', package): packages.append({'type': 'url', 'pkg': package}) continue if '>=' in package: parts = package.split('>=') package = parts[0].strip() ver = parts[1].strip() packages.append({'type': '>=', 'pkg': package, 'ver': ver}) continue if '==' in package: parts = package.split('==') package = parts[0].strip() ver = parts[1].strip() packages.append({'type': '==', 'pkg': package, 'ver': ver}) continue if re.search(r'[^ a-zA-Z0-9\-]', package): raise Exception('Unsupported requirements format version constraint: %s' % package) packages.append({'type': 'any', 'pkg': package}) return packages def _execute(params, cwd, retry=None, env=None): """ Executes a subprocess :param params: A list of the executable and arguments to pass to it :param cwd: The working directory to execute the command in :param retry: If this string is present in stderr, retry the operation :return: A 2-element tuple of (stdout, stderr) """ proc = subprocess.Popen( params, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd, env=env ) stdout, stderr = proc.communicate() code = proc.wait() if code != 0: if retry and retry in stderr.decode('utf-8'): return _execute(params, cwd) e = OSError('subprocess exit code for "%s" was %d: %s' % (' '.join(params), code, stderr)) e.stdout = stdout e.stderr = stderr raise e return (stdout, stderr) oscrypto-1.3.0/dev/lint.py000066400000000000000000000020061421476274700155070ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os from . import package_name, package_root import flake8 if not hasattr(flake8, '__version_info__') or flake8.__version_info__ < (3,): from flake8.engine import get_style_guide else: from flake8.api.legacy import get_style_guide def run(): """ Runs flake8 lint :return: A bool - if flake8 did not find any errors """ print('Running flake8 %s' % flake8.__version__) flake8_style = get_style_guide(config_file=os.path.join(package_root, 'tox.ini')) paths = [] for _dir in [package_name, 'dev', 'tests']: for root, _, filenames in os.walk(_dir): for filename in filenames: if not filename.endswith('.py'): continue paths.append(os.path.join(root, filename)) report = flake8_style.check_files(paths) success = report.total_errors == 0 if success: print('OK') return success oscrypto-1.3.0/dev/pyenv-install.py000066400000000000000000000123471421476274700173570ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os import subprocess import sys run_args = [ { 'name': 'version', 'kwarg': 'version', }, ] def _write_env(env, key, value): sys.stdout.write("%s: %s\n" % (key, value)) sys.stdout.flush() if sys.version_info < (3,): env[key.encode('utf-8')] = value.encode('utf-8') else: env[key] = value def run(version=None): """ Installs a version of Python on Mac using pyenv :return: A bool - if Python was installed successfully """ if sys.platform == 'win32': raise ValueError('pyenv-install is not designed for Windows') if version not in set(['2.6', '3.3']): raise ValueError('Invalid version: %r' % version) python_path = os.path.expanduser('~/.pyenv/versions/%s/bin' % version) if os.path.exists(os.path.join(python_path, 'python')): print(python_path) return True stdout = "" stderr = "" proc = subprocess.Popen( 'command -v pyenv', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) proc.communicate() if proc.returncode != 0: proc = subprocess.Popen( ['brew', 'install', 'pyenv'], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) so, se = proc.communicate() stdout += so.decode('utf-8') stderr += se.decode('utf-8') if proc.returncode != 0: print(stdout) print(stderr, file=sys.stderr) return False pyenv_script = './%s' % version try: with open(pyenv_script, 'wb') as f: if version == '2.6': contents = '#require_gcc\n' \ 'install_package "openssl-1.0.2k" "https://www.openssl.org/source/old/1.0.2/openssl-1.0.2k.tar.gz' \ '#6b3977c61f2aedf0f96367dcfb5c6e578cf37e7b8d913b4ecb6643c3cb88d8c0" mac_openssl\n' \ 'install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz' \ '#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline' \ ' --if has_broken_mac_readline\n' \ 'install_package "Python-2.6.9" "https://www.python.org/ftp/python/2.6.9/Python-2.6.9.tgz' \ '#7277b1285d8a82f374ef6ebaac85b003266f7939b3f2a24a3af52f9523ac94db" standard verify_py26' elif version == '3.3': contents = '#require_gcc\n' \ 'install_package "openssl-1.0.2k" "https://www.openssl.org/source/old/1.0.2/openssl-1.0.2k.tar.gz' \ '#6b3977c61f2aedf0f96367dcfb5c6e578cf37e7b8d913b4ecb6643c3cb88d8c0" mac_openssl\n' \ 'install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz' \ '#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline' \ ' --if has_broken_mac_readline\n' \ 'install_package "Python-3.3.7" "https://www.python.org/ftp/python/3.3.7/Python-3.3.7.tar.xz' \ '#85f60c327501c36bc18c33370c14d472801e6af2f901dafbba056f61685429fe" standard verify_py33' f.write(contents.encode('utf-8')) args = ['pyenv', 'install', pyenv_script] stdin = None stdin_contents = None env = os.environ.copy() if version == '2.6': _write_env(env, 'PYTHON_CONFIGURE_OPTS', '--enable-ipv6') stdin = subprocess.PIPE stdin_contents = '--- configure 2021-08-05 20:17:26.000000000 -0400\n' \ '+++ configure 2021-08-05 20:21:30.000000000 -0400\n' \ '@@ -10300,17 +10300,8 @@\n' \ ' rm -f core conftest.err conftest.$ac_objext \\\n' \ ' conftest$ac_exeext conftest.$ac_ext\n' \ ' \n' \ '-if test "$buggygetaddrinfo" = "yes"; then\n' \ '-\tif test "$ipv6" = "yes"; then\n' \ '-\t\techo \'Fatal: You must get working getaddrinfo() function.\'\n' \ '-\t\techo \' or you can specify "--disable-ipv6"\'.\n' \ '-\t\texit 1\n' \ '-\tfi\n' \ '-else\n' \ '-\n' \ ' $as_echo "#define HAVE_GETADDRINFO 1" >>confdefs.h\n' \ ' \n' \ '-fi\n' \ ' for ac_func in getnameinfo\n' \ ' do :\n' \ ' ac_fn_c_check_func "$LINENO" "getnameinfo" "ac_cv_func_getnameinfo"' stdin_contents = stdin_contents.encode('ascii') args.append('--patch') proc = subprocess.Popen( args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=stdin, env=env ) so, se = proc.communicate(stdin_contents) stdout += so.decode('utf-8') stderr += se.decode('utf-8') if proc.returncode != 0: print(stdout) print(stderr, file=sys.stderr) return False finally: if os.path.exists(pyenv_script): os.unlink(pyenv_script) print(python_path) return True oscrypto-1.3.0/dev/python-install.py000066400000000000000000000040061421476274700175300ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os import shutil import subprocess import sys from urllib.parse import urlparse from urllib.request import urlopen run_args = [ { 'name': 'version', 'kwarg': 'version', }, { 'name': 'arch', 'kwarg': 'arch', }, ] def run(version=None, arch=None): """ Installs a version of Python on Windows :return: A bool - if Python was installed successfully """ if sys.platform != 'win32': raise ValueError('python-install is only designed for Windows') if version not in set(['2.6', '3.3']): raise ValueError('Invalid version: %r' % version) if arch not in set(['x86', 'x64']): raise ValueError('Invalid arch: %r' % arch) if version == '2.6': if arch == 'x64': url = 'https://www.python.org/ftp/python/2.6.6/python-2.6.6.amd64.msi' else: url = 'https://www.python.org/ftp/python/2.6.6/python-2.6.6.msi' else: if arch == 'x64': url = 'https://www.python.org/ftp/python/3.3.5/python-3.3.5.amd64.msi' else: url = 'https://www.python.org/ftp/python/3.3.5/python-3.3.5.msi' home = os.environ.get('USERPROFILE') msi_filename = os.path.basename(urlparse(url).path) msi_path = os.path.join(home, msi_filename) install_path = os.path.join(os.environ.get('LOCALAPPDATA'), 'Python%s-%s' % (version, arch)) if os.path.exists(os.path.join(install_path, 'python.exe')): print(install_path) return True try: with urlopen(url) as r, open(msi_path, 'wb') as f: shutil.copyfileobj(r, f) proc = subprocess.Popen( 'msiexec /passive /a %s TARGETDIR=%s' % (msi_filename, install_path), shell=True, cwd=home ) proc.communicate() finally: if os.path.exists(msi_path): os.unlink(msi_path) print(install_path) return True oscrypto-1.3.0/dev/release.py000066400000000000000000000031751421476274700161710ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import subprocess import sys import twine.cli from . import package_name, package_root, has_tests_package from .build import run as build def run(): """ Creates a sdist .tar.gz and a bdist_wheel --univeral .whl and uploads them to pypi :return: A bool - if the packaging and upload process was successful """ git_wc_proc = subprocess.Popen( ['git', 'status', '--porcelain', '-uno'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=package_root ) git_wc_status, _ = git_wc_proc.communicate() if len(git_wc_status) > 0: print(git_wc_status.decode('utf-8').rstrip(), file=sys.stderr) print('Unable to perform release since working copy is not clean', file=sys.stderr) return False git_tag_proc = subprocess.Popen( ['git', 'tag', '-l', '--contains', 'HEAD'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=package_root ) tag, tag_error = git_tag_proc.communicate() if len(tag_error) > 0: print(tag_error.decode('utf-8').rstrip(), file=sys.stderr) print('Error looking for current git tag', file=sys.stderr) return False if len(tag) == 0: print('No git tag found on HEAD', file=sys.stderr) return False tag = tag.decode('ascii').strip() build() twine.cli.dispatch(['upload', 'dist/%s-%s*' % (package_name, tag)]) if has_tests_package: twine.cli.dispatch(['upload', 'dist/%s_tests-%s*' % (package_name, tag)]) return True oscrypto-1.3.0/dev/tests.py000066400000000000000000000043661421476274700157160ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import unittest import re import sys import warnings from . import requires_oscrypto from ._import import _preload from tests import test_classes if sys.version_info < (3,): range = xrange # noqa from cStringIO import StringIO else: from io import StringIO run_args = [ { 'name': 'regex', 'kwarg': 'matcher', }, { 'name': 'repeat_count', 'kwarg': 'repeat', 'cast': 'int', }, ] def run(matcher=None, repeat=1, ci=False): """ Runs the tests :param matcher: A unicode string containing a regular expression to use to filter test names by. A value of None will cause no filtering. :param repeat: An integer - the number of times to run the tests :param ci: A bool, indicating if the tests are being run as part of CI :return: A bool - if the tests succeeded """ _preload(requires_oscrypto, not ci) warnings.filterwarnings("error") loader = unittest.TestLoader() # We have to manually track the list of applicable tests because for # some reason with Python 3.4 on Windows, the tests in a suite are replaced # with None after being executed. This breaks the repeat functionality. test_list = [] for test_class in test_classes(): if matcher: names = loader.getTestCaseNames(test_class) for name in names: if re.search(matcher, name): test_list.append(test_class(name)) else: test_list.append(loader.loadTestsFromTestCase(test_class)) stream = sys.stdout verbosity = 1 if matcher and repeat == 1: verbosity = 2 elif repeat > 1: stream = StringIO() for _ in range(0, repeat): suite = unittest.TestSuite() for test in test_list: suite.addTest(test) result = unittest.TextTestRunner(stream=stream, verbosity=verbosity).run(suite) if len(result.errors) > 0 or len(result.failures) > 0: if repeat > 1: print(stream.getvalue()) return False if repeat > 1: stream.truncate(0) return True oscrypto-1.3.0/dev/version.py000066400000000000000000000053351421476274700162360ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import codecs import os import re from . import package_root, package_name, has_tests_package run_args = [ { 'name': 'pep440_version', 'required': True }, ] def run(new_version): """ Updates the package version in the various locations :param new_version: A unicode string of the new library version as a PEP 440 version :return: A bool - if the version number was successfully bumped """ # We use a restricted form of PEP 440 versions version_match = re.match( r'(\d+)\.(\d+)\.(\d)+(?:\.((?:dev|a|b|rc)\d+))?$', new_version ) if not version_match: raise ValueError('Invalid PEP 440 version: %s' % new_version) new_version_info = ( int(version_match.group(1)), int(version_match.group(2)), int(version_match.group(3)), ) if version_match.group(4): new_version_info += (version_match.group(4),) version_path = os.path.join(package_root, package_name, 'version.py') setup_path = os.path.join(package_root, 'setup.py') setup_tests_path = os.path.join(package_root, 'tests', 'setup.py') tests_path = os.path.join(package_root, 'tests', '__init__.py') file_paths = [version_path, setup_path] if has_tests_package: file_paths.extend([setup_tests_path, tests_path]) for file_path in file_paths: orig_source = '' with codecs.open(file_path, 'r', encoding='utf-8') as f: orig_source = f.read() found = 0 new_source = '' for line in orig_source.splitlines(True): if line.startswith('__version__ = '): found += 1 new_source += '__version__ = %r\n' % new_version elif line.startswith('__version_info__ = '): found += 1 new_source += '__version_info__ = %r\n' % (new_version_info,) elif line.startswith('PACKAGE_VERSION = '): found += 1 new_source += 'PACKAGE_VERSION = %r\n' % new_version else: new_source += line if found == 0: raise ValueError('Did not find any versions in %s' % file_path) s = 's' if found > 1 else '' rel_path = file_path[len(package_root) + 1:] was_were = 'was' if found == 1 else 'were' if new_source != orig_source: print('Updated %d version%s in %s' % (found, s, rel_path)) with codecs.open(file_path, 'w', encoding='utf-8') as f: f.write(new_source) else: print('%d version%s in %s %s up-to-date' % (found, s, rel_path, was_were)) return True oscrypto-1.3.0/docs/000077500000000000000000000000001421476274700143435ustar00rootroot00000000000000oscrypto-1.3.0/docs/asymmetric.md000066400000000000000000000501301421476274700170410ustar00rootroot00000000000000# oscrypto.asymmetric API Documentation The *oscrypto.asymmetric* submodule implements public key signing, verification, encryption and decryption. Additionally, it can generate, load and dump keys of various types and DH parameters. The following functions comprise the public API: - [Keys/Certificates](#keys-certificates) - [`generate_pair()`](#generate_pair-function) - [`load_certificate()`](#load_certificate-function) - [`load_public_key()`](#load_public_key-function) - [`load_private_key()`](#load_private_key-function) - [`load_pkcs12()`](#load_pkcs12-function) - [`dump_public_key()`](#dump_public_key-function) - [`dump_certificate()`](#dump_certificate-function) - [`dump_private_key()`](#dump_private_key-function) - [`dump_openssl_private_key()`](#dump_openssl_private_key-function) - [DH](#dh) - [`generate_dh_parameters()`](#generate_dh_parameters-function) - [`dump_dh_parameters()`](#dump_dh_parameters-function) - [RSA](#rsa) - [`rsa_pkcs1v15_sign()`](#rsa_pkcs1v15_sign-function) - [`rsa_pkcs1v15_verify()`](#rsa_pkcs1v15_verify-function) - [`rsa_pss_sign()`](#rsa_pss_sign-function) - [`rsa_pss_verify()`](#rsa_pss_verify-function) - [`rsa_pkcs1v15_encrypt()`](#rsa_pkcs1v15_encrypt-function) - [`rsa_pkcs1v15_decrypt()`](#rsa_pkcs1v15_decrypt-function) - [`rsa_oaep_encrypt()`](#rsa_oaep_encrypt-function) - [`rsa_oaep_decrypt()`](#rsa_oaep_decrypt-function) - [DSA](#dsa) - [`dsa_sign()`](#dsa_sign-function) - [`dsa_verify()`](#dsa_verify-function) - [ECDSA](#ecdsa) - [`ecdsa_sign()`](#ecdsa_sign-function) - [`ecdsa_verify()`](#ecdsa_verify-function) ## Keys/Certificates ### `generate_pair()` function > ```python > def generate_pair(algorithm, bit_size=None, curve=None): > """ > :param algorithm: > The key algorithm - "rsa", "dsa" or "ec" > > :param bit_size: > An integer - used for "rsa" and "dsa". For "rsa" the value maye be 1024, > 2048, 3072 or 4096. For "dsa" the value may be 1024, plus 2048 or 3072 > if OpenSSL 1.0.0 or newer is available. > > :param curve: > A unicode string - used for "ec" keys. Valid values include "secp256r1", > "secp384r1" and "secp521r1". > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A 2-element tuple of (PublicKey, PrivateKey). The contents of each key > may be saved by calling .asn1.dump(). > """ > ``` > > Generates a public/private key pair ### `load_certificate()` function > ```python > def load_certificate(source): > """ > :param source: > A byte string of file contents, a unicode string filename or an > asn1crypto.x509.Certificate object > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A Certificate object > """ > ``` > > Loads an x509 certificate into a Certificate object ### `load_public_key()` function > ```python > def load_public_key(source): > """ > :param source: > A byte string of file contents, a unicode string filename or an > asn1crypto.keys.PublicKeyInfo object > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > oscrypto.errors.AsymmetricKeyError - when the public key is incompatible with the OS crypto library > OSError - when an error is returned by the OS crypto library > > :return: > A PublicKey object > """ > ``` > > Loads a public key into a PublicKey object ### `load_private_key()` function > ```python > def load_private_key(source, password=None): > """ > :param source: > A byte string of file contents, a unicode string filename or an > asn1crypto.keys.PrivateKeyInfo object > > :param password: > A byte or unicode string to decrypt the private key file. Unicode > strings will be encoded using UTF-8. Not used is the source is a > PrivateKeyInfo object. > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > oscrypto.errors.AsymmetricKeyError - when the private key is incompatible with the OS crypto library > OSError - when an error is returned by the OS crypto library > > :return: > A PrivateKey object > """ > ``` > > Loads a private key into a PrivateKey object ### `load_pkcs12()` function > ```python > def load_pkcs12(source, password=None): > """ > :param source: > A byte string of file contents or a unicode string filename > > :param password: > A byte or unicode string to decrypt the PKCS12 file. Unicode strings > will be encoded using UTF-8. > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > oscrypto.errors.AsymmetricKeyError - when a contained key is incompatible with the OS crypto library > OSError - when an error is returned by the OS crypto library > > :return: > A three-element tuple containing (PrivateKey, Certificate, [Certificate, ...]) > """ > ``` > > Loads a .p12 or .pfx file into a PrivateKey object and one or more > Certificates objects ### `dump_public_key()` function > ```python > def dump_public_key(public_key, encoding='pem'): > """ > :param public_key: > An oscrypto.asymmetric.PublicKey or asn1crypto.keys.PublicKeyInfo object > > :param encoding: > A unicode string of "pem" or "der" > > :return: > A byte string of the encoded public key > """ > ``` > > Serializes a public key object into a byte string ### `dump_certificate()` function > ```python > def dump_certificate(certificate, encoding='pem'): > """ > :param certificate: > An oscrypto.asymmetric.Certificate or asn1crypto.x509.Certificate object > > :param encoding: > A unicode string of "pem" or "der" > > :return: > A byte string of the encoded certificate > """ > ``` > > Serializes a certificate object into a byte string ### `dump_private_key()` function > ```python > def dump_private_key(private_key, passphrase, encoding='pem', target_ms=200): > """ > :param private_key: > An oscrypto.asymmetric.PrivateKey or asn1crypto.keys.PrivateKeyInfo > object > > :param passphrase: > A unicode string of the passphrase to encrypt the private key with. > A passphrase of None will result in no encryption. A blank string will > result in a ValueError to help ensure that the lack of passphrase is > intentional. > > :param encoding: > A unicode string of "pem" or "der" > > :param target_ms: > Use PBKDF2 with the number of iterations that takes about this many > milliseconds on the current machine. > > :raises: > ValueError - when a blank string is provided for the passphrase > > :return: > A byte string of the encoded and encrypted private key > """ > ``` > > Serializes a private key object into a byte string of the PKCS#8 format ### `dump_openssl_private_key()` function > ```python > def dump_openssl_private_key(private_key, passphrase): > """ > :param private_key: > An oscrypto.asymmetric.PrivateKey or asn1crypto.keys.PrivateKeyInfo > object > > :param passphrase: > A unicode string of the passphrase to encrypt the private key with. > A passphrase of None will result in no encryption. A blank string will > result in a ValueError to help ensure that the lack of passphrase is > intentional. > > :raises: > ValueError - when a blank string is provided for the passphrase > > :return: > A byte string of the encoded and encrypted private key > """ > ``` > > Serializes a private key object into a byte string of the PEM formats used > by OpenSSL. The format chosen will depend on the type of private key - RSA, > DSA or EC. > > Do not use this method unless you really must interact with a system that > does not support PKCS#8 private keys. The encryption provided by PKCS#8 is > far superior to the OpenSSL formats. This is due to the fact that the > OpenSSL formats don't stretch the passphrase, making it very easy to > brute-force. ## DH ### `generate_dh_parameters()` function > ```python > def generate_dh_parameters(bit_size): > """ > :param bit_size: > The integer bit size of the parameters to generate. Must be between 512 > and 4096, and divisible by 64. Recommended secure value as of early 2016 > is 2048, with an absolute minimum of 1024. > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > An asn1crypto.algos.DHParameters object. Use > oscrypto.asymmetric.dump_dh_parameters() to save to disk for usage with > web servers. > """ > ``` > > Generates DH parameters for use with Diffie-Hellman key exchange. Returns > a structure in the format of DHParameter defined in PKCS#3, which is also > used by the OpenSSL dhparam tool. > > THIS CAN BE VERY TIME CONSUMING! ### `dump_dh_parameters()` function > ```python > def dump_dh_parameters(dh_parameters, encoding='pem'): > """ > :param dh_parameters: > An asn1crypto.algos.DHParameters object > > :param encoding: > A unicode string of "pem" or "der" > > :return: > A byte string of the encoded DH parameters > """ > ``` > > Serializes an asn1crypto.algos.DHParameters object into a byte string ## RSA ### `rsa_pkcs1v15_sign()` function > ```python > def rsa_pkcs1v15_sign(private_key, data, hash_algorithm): > """ > :param private_key: > The PrivateKey to generate the signature with > > :param data: > A byte string of the data the signature is for > > :param hash_algorithm: > A unicode string of "md5", "sha1", "sha224", "sha256", "sha384", > "sha512" or "raw" > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the signature > """ > ``` > > Generates an RSASSA-PKCS-v1.5 signature. > > When the hash_algorithm is "raw", the operation is identical to RSA > private key encryption. That is: the data is not hashed and no ASN.1 > structure with an algorithm identifier of the hash algorithm is placed in > the encrypted byte string. ### `rsa_pkcs1v15_verify()` function > ```python > def rsa_pkcs1v15_verify(certificate_or_public_key, signature, data, hash_algorithm): > """ > :param certificate_or_public_key: > A Certificate or PublicKey instance to verify the signature with > > :param signature: > A byte string of the signature to verify > > :param data: > A byte string of the data the signature is for > > :param hash_algorithm: > A unicode string of "md5", "sha1", "sha224", "sha256", "sha384", > "sha512" or "raw" > > :raises: > oscrypto.errors.SignatureError - when the signature is determined to be invalid > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > """ > ``` > > Verifies an RSASSA-PKCS-v1.5 signature. > > When the hash_algorithm is "raw", the operation is identical to RSA > public key decryption. That is: the data is not hashed and no ASN.1 > structure with an algorithm identifier of the hash algorithm is placed in > the encrypted byte string. ### `rsa_pss_sign()` function > ```python > def rsa_pss_sign(private_key, data, hash_algorithm): > """ > :param private_key: > The PrivateKey to generate the signature with > > :param data: > A byte string of the data the signature is for > > :param hash_algorithm: > A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the signature > """ > ``` > > Generates an RSASSA-PSS signature. For the PSS padding the mask gen > algorithm will be mgf1 using the same hash algorithm as the signature. The > salt length with be the length of the hash algorithm, and the trailer field > with be the standard 0xBC byte. ### `rsa_pss_verify()` function > ```python > def rsa_pss_verify(certificate_or_public_key, signature, data, hash_algorithm): > """ > :param certificate_or_public_key: > A Certificate or PublicKey instance to verify the signature with > > :param signature: > A byte string of the signature to verify > > :param data: > A byte string of the data the signature is for > > :param hash_algorithm: > A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" > > :raises: > oscrypto.errors.SignatureError - when the signature is determined to be invalid > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > """ > ``` > > Verifies an RSASSA-PSS signature. For the PSS padding the mask gen algorithm > will be mgf1 using the same hash algorithm as the signature. The salt length > with be the length of the hash algorithm, and the trailer field with be the > standard 0xBC byte. ### `rsa_pkcs1v15_encrypt()` function > ```python > def rsa_pkcs1v15_encrypt(certificate_or_public_key, data): > """ > :param certificate_or_public_key: > A PublicKey or Certificate object > > :param data: > A byte string, with a maximum length 11 bytes less than the key length > (in bytes) > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the encrypted data > """ > ``` > > Encrypts a byte string using an RSA public key or certificate. Uses PKCS#1 > v1.5 padding. ### `rsa_pkcs1v15_decrypt()` function > ```python > def rsa_pkcs1v15_decrypt(private_key, ciphertext): > """ > :param private_key: > A PrivateKey object > > :param ciphertext: > A byte string of the encrypted data > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the original plaintext > """ > ``` > > Decrypts a byte string using an RSA private key. Uses PKCS#1 v1.5 padding. ### `rsa_oaep_encrypt()` function > ```python > def rsa_oaep_encrypt(certificate_or_public_key, data): > """ > :param certificate_or_public_key: > A PublicKey or Certificate object > > :param data: > A byte string, with a maximum length 41 bytes (or more) less than the > key length (in bytes) > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the encrypted data > """ > ``` > > Encrypts a byte string using an RSA public key or certificate. Uses PKCS#1 > OAEP padding with SHA1. ### `rsa_oaep_decrypt()` function > ```python > def rsa_oaep_decrypt(private_key, ciphertext): > """ > :param private_key: > A PrivateKey object > > :param ciphertext: > A byte string of the encrypted data > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the original plaintext > """ > ``` > > Decrypts a byte string using an RSA private key. Uses PKCS#1 OAEP padding > with SHA1. ## DSA ### `dsa_sign()` function > ```python > def dsa_sign(private_key, data, hash_algorithm): > """ > :param private_key: > The PrivateKey to generate the signature with > > :param data: > A byte string of the data the signature is for > > :param hash_algorithm: > A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the signature > """ > ``` > > Generates a DSA signature ### `dsa_verify()` function > ```python > def dsa_verify(certificate_or_public_key, signature, data, hash_algorithm): > """ > :param certificate_or_public_key: > A Certificate or PublicKey instance to verify the signature with > > :param signature: > A byte string of the signature to verify > > :param data: > A byte string of the data the signature is for > > :param hash_algorithm: > A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" > > :raises: > oscrypto.errors.SignatureError - when the signature is determined to be invalid > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > """ > ``` > > Verifies a DSA signature ## ECDSA ### `ecdsa_sign()` function > ```python > def ecdsa_sign(private_key, data, hash_algorithm): > """ > :param private_key: > The PrivateKey to generate the signature with > > :param data: > A byte string of the data the signature is for > > :param hash_algorithm: > A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the signature > """ > ``` > > Generates an ECDSA signature ### `ecdsa_verify()` function > ```python > def ecdsa_verify(certificate_or_public_key, signature, data, hash_algorithm): > """ > :param certificate_or_public_key: > A Certificate or PublicKey instance to verify the signature with > > :param signature: > A byte string of the signature to verify > > :param data: > A byte string of the data the signature is for > > :param hash_algorithm: > A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" > > :raises: > oscrypto.errors.SignatureError - when the signature is determined to be invalid > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > """ > ``` > > Verifies an ECDSA signature oscrypto-1.3.0/docs/kdf.md000066400000000000000000000073761421476274700154460ustar00rootroot00000000000000# oscrypto.kdf API Documentation The *oscrypto.kdf* submodule implements key derivation functions. The following functions comprise the public API: - [`pbkdf2()`](#pbkdf2-function) - [`pbkdf2_iteration_calculator()`](#pbkdf2_iteration_calculator-function) - [`pbkdf1()`](#pbkdf1-function) - [`pkcs12_kdf()`](#pkcs12_kdf-function) ### `pbkdf2()` function > ```python > def pbkdf2(hash_algorithm, password, salt, iterations, key_length): > """ > :param hash_algorithm: > The string name of the hash algorithm to use: "sha1", "sha224", "sha256", "sha384", "sha512" > > :param password: > A byte string of the password to use an input to the KDF > > :param salt: > A cryptographic random byte string > > :param iterations: > The numbers of iterations to use when deriving the key > > :param key_length: > The length of the desired key in bytes > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > > :return: > The derived key as a byte string > """ > ``` > > PBKDF2 from PKCS#5 ### `pbkdf2_iteration_calculator()` function > ```python > def pbkdf2_iteration_calculator(hash_algorithm, key_length, target_ms=100, quiet=False): > """ > :param hash_algorithm: > The string name of the hash algorithm to use: "md5", "sha1", "sha224", > "sha256", "sha384", "sha512" > > :param key_length: > The length of the desired key in bytes > > :param target_ms: > The number of milliseconds the derivation should take > > :param quiet: > If no output should be printed as attempts are made > > :return: > An integer number of iterations of PBKDF2 using the specified hash > that will take at least target_ms > """ > ``` > > Runs pbkdf2() twice to determine the approximate number of iterations to > use to hit a desired time per run. Use this on a production machine to > dynamically adjust the number of iterations as high as you can. ### `pbkdf1()` function > ```python > def pbkdf1(hash_algorithm, password, salt, iterations, key_length): > """ > :param hash_algorithm: > The string name of the hash algorithm to use: "md2", "md5", "sha1" > > :param password: > A byte string of the password to use an input to the KDF > > :param salt: > A cryptographic random byte string > > :param iterations: > The numbers of iterations to use when deriving the key > > :param key_length: > The length of the desired key in bytes > > :return: > The derived key as a byte string > """ > ``` > > An implementation of PBKDF1 - should only be used for interop with legacy > systems, not new architectures ### `pkcs12_kdf()` function > ```python > def pkcs12_kdf(hash_algorithm, password, salt, iterations, key_length, id_): > """ > :param hash_algorithm: > The string name of the hash algorithm to use: "md5", "sha1", "sha224", "sha256", "sha384", "sha512" > > :param password: > A byte string of the password to use an input to the KDF > > :param salt: > A cryptographic random byte string > > :param iterations: > The numbers of iterations to use when deriving the key > > :param key_length: > The length of the desired key in bytes > > :param id_: > The ID of the usage - 1 for key, 2 for iv, 3 for mac > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > > :return: > The derived key as a byte string > """ > ``` > > KDF from RFC7292 appendix B.2 - https://tools.ietf.org/html/rfc7292#page-19 oscrypto-1.3.0/docs/keys.md000066400000000000000000000061741421476274700156500ustar00rootroot00000000000000# oscrypto.keys API Documentation The *oscrypto.keys* submodule implements functions to parse certificates, public keys, private keys and PKCS#12 (`.p12`/`.pfx`) files. The following functions comprise the public API: - [`parse_certificate()`](#parse_certificate-function) - [`parse_public()`](#parse_public-function) - [`parse_private()`](#parse_private-function) - [`parse_pkcs12()`](#parse_pkcs12-function) ### `parse_certificate()` function > ```python > def parse_certificate(data): > """ > :param data: > A byte string to load the certificate from > > :raises: > ValueError - when the data does not appear to contain a certificate > > :return: > An asn1crypto.x509.Certificate object > """ > ``` > > Loads a certificate from a DER or PEM-formatted file. Supports X.509 > certificates only. ### `parse_public()` function > ```python > def parse_public(data): > """ > :param data: > A byte string to load the public key from > > :raises: > ValueError - when the data does not appear to contain a public key > > :return: > An asn1crypto.keys.PublicKeyInfo object > """ > ``` > > Loads a public key from a DER or PEM-formatted file. Supports RSA, DSA and > EC public keys. For RSA keys, both the old RSAPublicKey and > SubjectPublicKeyInfo structures are supported. Also allows extracting a > public key from an X.509 certificate. ### `parse_private()` function > ```python > def parse_private(data, password=None): > """ > :param data: > A byte string to load the private key from > > :param password: > The password to unencrypt the private key > > :raises: > ValueError - when the data does not appear to contain a private key, or the password is invalid > > :return: > An asn1crypto.keys.PrivateKeyInfo object > """ > ``` > > Loads a private key from a DER or PEM-formatted file. Supports RSA, DSA and > EC private keys. Works with the follow formats: > > - RSAPrivateKey (PKCS#1) > - ECPrivateKey (SECG SEC1 V2) > - DSAPrivateKey (OpenSSL) > - PrivateKeyInfo (RSA/DSA/EC - PKCS#8) > - EncryptedPrivateKeyInfo (RSA/DSA/EC - PKCS#8) > - Encrypted RSAPrivateKey (PEM only, OpenSSL) > - Encrypted DSAPrivateKey (PEM only, OpenSSL) > - Encrypted ECPrivateKey (PEM only, OpenSSL) ### `parse_pkcs12()` function > ```python > def parse_pkcs12(data, password=None): > """ > :param data: > A byte string of a DER-encoded PKCS#12 file > > :param password: > A byte string of the password to any encrypted data > > :raises: > ValueError - when any of the parameters are of the wrong type or value > OSError - when an error is returned by one of the OS decryption functions > > :return: > A three-element tuple of: > 1. An asn1crypto.keys.PrivateKeyInfo object > 2. An asn1crypto.x509.Certificate object > 3. A list of zero or more asn1crypto.x509.Certificate objects that are > "extra" certificates, possibly intermediates from the cert chain > """ > ``` > > Parses a PKCS#12 ANS.1 DER-encoded structure and extracts certs and keys oscrypto-1.3.0/docs/oscrypto.md000066400000000000000000000037201421476274700165510ustar00rootroot00000000000000# oscrypto API Documentation The *oscrypto* module provides functions to obtain information about the backend being used, and allows a custom version of OpenSSL to be used on any platform. *These functions are rarely necessary. Using the `backend()` function for non-debugging purposes is likely a sign tight-coupling.* - [`backend()`](#backend-function) - [`use_openssl()`](#use_openssl-function) ### `backend()` function > ```python > def backend(): > """ > :return: > A unicode string of the backend being used: "openssl", "mac", "win", > "winlegacy" > """ > ``` ### `use_openssl()` function > ```python > def use_openssl(libcrypto_path, libssl_path, trust_list_path=None): > """ > :param libcrypto_path: > A unicode string of the file path to the OpenSSL/LibreSSL libcrypto > dynamic library. > > :param libssl_path: > A unicode string of the file path to the OpenSSL/LibreSSL libssl > dynamic library. > > :param trust_list_path: > An optional unicode string of the path to a file containing > OpenSSL-compatible CA certificates in PEM format. If this is not > provided and the platform is OS X or Windows, the system trust roots > will be exported from the OS and used for all TLS connections. > > :raises: > ValueError - when one of the paths is not a unicode string > OSError - when the trust_list_path does not exist on the filesystem > oscrypto.errors.LibraryNotFoundError - when one of the path does not exist on the filesystem > RuntimeError - when this function is called after another part of oscrypto has been imported > """ > ``` > > Forces using OpenSSL dynamic libraries on OS X (.dylib) or Windows (.dll), > or using a specific dynamic library on Linux/BSD (.so). > > This can also be used to configure oscrypto to use LibreSSL dynamic > libraries. > > This method must be called before any oscrypto submodules are imported. oscrypto-1.3.0/docs/readme.md000066400000000000000000000110731421476274700161240ustar00rootroot00000000000000# oscrypto Documentation *oscrypto* is a library that exposes cryptography primitives from the host operating system. It is broken down into a few different submodules: | Submodule | Functionality | | ---------------------------------------- | --------------------------------------------------------------------------------------------- | | [`oscrypto`](oscrypto.md) | Configuration and information about backend | | [`oscrypto.symmetric`](symmetric.md) | AES, Triple DES, DES, RC2 and RC4 encryption | | [`oscrypto.asymmetric`](asymmetric.md) | RSA, DSA and EC-key signing and verification, RSA encryption | | [`oscrypto.kdf`](kdf.md) | PBKDF2, PBKDF1 and PKCS#12 key derivation functions | | [`oscrypto.keys`](keys.md) | Certificate, public key and private key loading, parsing and normalization | | [`oscrypto.tls`](tls.md) | TLSv1.x socket wrappers utilizing OS trust store and modern cipher suites | | [`oscrypto.trust_list`](trust_list.md) | CA certificate list export from the OS trust store | | [`oscrypto.util`](util.md) | Random byte generation, constant time string comparison | Many of the supported ciphers and hashes are not necessarily modern, and should primarily be used for integration with legacy systems. For modern cryptography, please see [Modern Cryptography](#modern-cryptography). ## Modern Cryptography A good place to get an overview of the correct tools to use for modern cryptography is [(Updated) Cryptographic Right Answers](https://gist.github.com/tqbf/be58d2d39690c3b366ad) by Thomas Ptacek. In short, you probably want to be using [NaCl](http://nacl.cr.yp.to/) by Daniel J. Bernstein (DJB) - he is a very accomplished cryptographer. Using [scrypt](http://www.tarsnap.com/scrypt.html) by Colin Percival for password hashing is a good idea. Here are some libraries for Python that may be useful: - https://github.com/pyca/pynacl - https://pypi.python.org/pypi/scrypt/ Thomas‘s recommendations are an alternative, slightly-updated version of [Cryptographic Right Answers](http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html) by Colin Percival. Colin‘s contain recommendations that may be a little more accessible, using things like RSA PSS for signing, RSA OAEP for encryption, scrypt or PBKDF2 for password hashing, and AES CTR with HMAC for symmetric encryption. ## Learning Before using *oscrypto*, you should know a bit about cryptography, and how to safely use the primitives. If you don‘t, you could very likely utilize them in an unsafe way, resulting in exposure of confidential information, including secret keys, encrypted data, and more. Here are some topics worth learning about: - Block ciphers (AES, Triple DES (2-key and 3-key), DES, RC2) - Weak block ciphers (Triple DES 2-key, DES, RC2) - Block cipher padding (PKCS#7 and PKCS#5) - Block cipher padding oracle attacks - Block cipher modes of operation (CBC, ECB, CFB, OFB, CTR) - Block cipher modes to avoid (ECB) - Nonce reuse in CTR-mode - Authenticated encryption (AEAD, EtM, MtE, E&M) - Authenticated block cipher modes (GCM, CCM) - Stream ciphers (RC4) - Hashing (MD5, SHA1, SHA-2 (SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA512/256)) - Weak hashes (MD5, SHA1) - Length extension attacks (MD5, SHA1, SHA-256, SHA-512) - HMAC - Cryptographically random numbers - RSA key sizes (1024, 2048, 3072, 4096) - DSA key sizes and hash algorithms - SHA1/1024 - SHA1/2048 (non-standard) - SHA-2/2048 - SHA-2/3072 - Elliptic curve (EC) keys and named curves - P-192 / secp192r1 / prime192v1 - P-224 / secp224r1 - P-256 / secp256r1 / prime256v1 - P-384 / secp384r1 - P-521 / secp521r1 - RSA signature padding (PKCS#1 v1.5 and PSS) - RSA encryption padding (PKCS#1 v1.5 and OAEP) - Weak RSA signature/encryption padding (PKCS#1 v1.5) - Timing attacks Some sources to learn more about cryptography: - [Crypto101](https://www.crypto101.io/) - [(Updated) Cryptographic Right Answers](https://gist.github.com/tqbf/be58d2d39690c3b366ad) - [How To Safely Generate a Random Number](http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/) - http://crypto.stackexchange.com/ oscrypto-1.3.0/docs/symmetric.md000066400000000000000000000235321421476274700167060ustar00rootroot00000000000000# oscrypto.symmetric API Documentation The *oscrypto.symmetric* submodule implements symmetric/secret key encryption and decryption. The following functions comprise the public API: - AES - [`aes_cbc_pkcs7_encrypt()`](#aes_cbc_pkcs7_encrypt-function) - [`aes_cbc_pkcs7_decrypt()`](#aes_cbc_pkcs7_decrypt-function) - [`aes_cbc_no_padding_encrypt()`](#aes_cbc_no_padding_encrypt-function) - [`aes_cbc_no_padding_decrypt()`](#aes_cbc_no_padding_decrypt-function) - Triple DES - [`tripledes_cbc_pkcs5_encrypt()`](#tripledes_cbc_pkcs5_encrypt-function) - [`tripledes_cbc_pkcs5_decrypt()`](#tripledes_cbc_pkcs5_decrypt-function) - DES - [`des_cbc_pkcs5_encrypt()`](#des_cbc_pkcs5_encrypt-function) - [`des_cbc_pkcs5_decrypt()`](#des_cbc_pkcs5_decrypt-function) - RC4 - [`rc4_encrypt()`](#rc4_encrypt-function) - [`rc4_decrypt()`](#rc4_decrypt-function) - RC2 - [`rc2_cbc_pkcs5_encrypt()`](#rc2_cbc_pkcs5_encrypt-function) - [`rc2_cbc_pkcs5_decrypt()`](#rc2_cbc_pkcs5_decrypt-function) ### `aes_cbc_pkcs7_encrypt()` function > ```python > def aes_cbc_pkcs7_encrypt(key, data, iv): > """ > :param key: > The encryption key - a byte string either 16, 24 or 32 bytes long > > :param data: > The plaintext - a byte string > > :param iv: > The initialization vector - either a byte string 16-bytes long or None > to generate an IV > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A tuple of two byte strings (iv, ciphertext) > """ > ``` > > Encrypts plaintext using AES in CBC mode with a 128, 192 or 256 bit key and > PKCS#7 padding. ### `aes_cbc_pkcs7_decrypt()` function > ```python > def aes_cbc_pkcs7_decrypt(key, data, iv): > """ > :param key: > The encryption key - a byte string either 16, 24 or 32 bytes long > > :param data: > The ciphertext - a byte string > > :param iv: > The initialization vector - a byte string 16-bytes long > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the plaintext > """ > ``` > > Decrypts AES ciphertext in CBC mode using a 128, 192 or 256 bit key ### `aes_cbc_no_padding_encrypt()` function > ```python > def aes_cbc_no_padding_encrypt(key, data, iv): > """ > :param key: > The encryption key - a byte string either 16, 24 or 32 bytes long > > :param data: > The plaintext - a byte string > > :param iv: > The initialization vector - either a byte string 16-bytes long or None > to generate an IV > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A tuple of two byte strings (iv, ciphertext) > """ > ``` > > Encrypts plaintext using AES in CBC mode with a 128, 192 or 256 bit key and > no padding. This means the ciphertext must be an exact multiple of 16 bytes > long. ### `aes_cbc_no_padding_decrypt()` function > ```python > def aes_cbc_no_padding_decrypt(key, data, iv): > """ > :param key: > The encryption key - a byte string either 16, 24 or 32 bytes long > > :param data: > The ciphertext - a byte string > > :param iv: > The initialization vector - a byte string 16-bytes long > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the plaintext > """ > ``` > > Decrypts AES ciphertext in CBC mode using a 128, 192 or 256 bit key and no > padding. ### `tripledes_cbc_pkcs5_encrypt()` function > ```python > def tripledes_cbc_pkcs5_encrypt(key, data, iv): > """ > :param key: > The encryption key - a byte string 16 or 24 bytes long (2 or 3 key mode) > > :param data: > The plaintext - a byte string > > :param iv: > The initialization vector - a byte string 8-bytes long or None > to generate an IV > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A tuple of two byte strings (iv, ciphertext) > """ > ``` > > Encrypts plaintext using 3DES in CBC mode using either the 2 or 3 key > variant (16 or 24 byte long key) and PKCS#5 padding. ### `tripledes_cbc_pkcs5_decrypt()` function > ```python > def tripledes_cbc_pkcs5_decrypt(key, data, iv): > """ > :param key: > The encryption key - a byte string 16 or 24 bytes long (2 or 3 key mode) > > :param data: > The ciphertext - a byte string > > :param iv: > The initialization vector - a byte string 8-bytes long > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the plaintext > """ > ``` > > Decrypts 3DES ciphertext in CBC mode using either the 2 or 3 key variant > (16 or 24 byte long key) and PKCS#5 padding. ### `des_cbc_pkcs5_encrypt()` function > ```python > def des_cbc_pkcs5_encrypt(key, data, iv): > """ > :param key: > The encryption key - a byte string 8 bytes long (includes error correction bits) > > :param data: > The plaintext - a byte string > > :param iv: > The initialization vector - a byte string 8-bytes long or None > to generate an IV > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A tuple of two byte strings (iv, ciphertext) > """ > ``` > > Encrypts plaintext using DES in CBC mode with a 56 bit key and PKCS#5 > padding. ### `des_cbc_pkcs5_decrypt()` function > ```python > def des_cbc_pkcs5_decrypt(key, data, iv): > """ > :param key: > The encryption key - a byte string 8 bytes long (includes error correction bits) > > :param data: > The ciphertext - a byte string > > :param iv: > The initialization vector - a byte string 8-bytes long > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the plaintext > """ > ``` > > Decrypts DES ciphertext in CBC mode using a 56 bit key and PKCS#5 padding. ### `rc4_encrypt()` function > ```python > def rc4_encrypt(key, data): > """ > :param key: > The encryption key - a byte string 5-16 bytes long > > :param data: > The plaintext - a byte string > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the ciphertext > """ > ``` > > Encrypts plaintext using RC4 with a 40-128 bit key ### `rc4_decrypt()` function > ```python > def rc4_decrypt(key, data): > """ > :param key: > The encryption key - a byte string 5-16 bytes long > > :param data: > The ciphertext - a byte string > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the plaintext > """ > ``` > > Decrypts RC4 ciphertext using a 40-128 bit key ### `rc2_cbc_pkcs5_encrypt()` function > ```python > def rc2_cbc_pkcs5_encrypt(key, data, iv): > """ > :param key: > The encryption key - a byte string 8 bytes long > > :param data: > The plaintext - a byte string > > :param iv: > The initialization vector - a byte string 8-bytes long or None > to generate an IV > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A tuple of two byte strings (iv, ciphertext) > """ > ``` > > Encrypts plaintext using RC2 in CBC mode with a 40-128 bit key and PKCS#5 > padding. ### `rc2_cbc_pkcs5_decrypt()` function > ```python > def rc2_cbc_pkcs5_decrypt(key, data, iv): > """ > :param key: > The encryption key - a byte string 8 bytes long > > :param data: > The ciphertext - a byte string > > :param iv: > The initialization vector - a byte string 8 bytes long > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string of the plaintext > """ > ``` > > Decrypts RC2 ciphertext ib CBC mode using a 40-128 bit key and PKCS#5 > padding. oscrypto-1.3.0/docs/tls.md000066400000000000000000000216171421476274700154760ustar00rootroot00000000000000# oscrypto.tls API Documentation The *oscrypto.tls* submodule implements a TLSv1.x wrapper for sockets. The features include: - Certificate verification performed by OS trust roots - Custom CA certificate support - SNI support - Session reuse via IDs/tickets - Modern cipher suites (RC4, DES, anon and NULL ciphers disabled) - Weak DH parameters and certificate signatures rejected - SSLv3 disabled by default, SSLv2 unimplemented The API consists of: - [`TLSSocket()`](#tlssocket-class) - [`.hostname`](#hostname-attribute) - [`.port`](#port-attribute) - [`.certificate`](#certificate-attribute) - [`.intermediates`](#intermediates-attribute) - [`.protocol`](#protocol-attribute) - [`.cipher_suite`](#cipher_suite-attribute) - [`.compression`](#compression-attribute) - [`.session_id`](#session_id-attribute) - [`.session_ticket`](#session_ticket-attribute) - [`.session`](#session-attribute) - [`.socket`](#socket-attribute) - [`.wrap()`](#wrap-method) - [`.read()`](#read-method) - [`.read_line()`](#read_line-method) - [`.read_until()`](#read_until-method) - [`.read_exactly()`](#read_exactly-method) - [`.select_read()`](#select_read-method) - [`.write()`](#write-method) - [`.select_write()`](#select_write-method) - [`.shutdown()`](#shutdown-method) - [`.close()`](#close-method) - [`TLSSession()`](#tlssession-class) ### `TLSSocket()` class > A wrapper around a socket.socket that adds TLS > > ##### constructor > > > ```python > > def __init__(self, address, port, timeout=10, session=None): > > """ > > :param address: > > A unicode string of the domain name or IP address to connect to > > > > :param port: > > An integer of the port number to connect to > > > > :param timeout: > > An integer timeout to use for the socket > > > > :param session: > > An oscrypto.tls.TLSSession object to allow for session reuse and > > controlling the protocols and validation performed > > """ > > ``` > > ##### `.hostname` attribute > > > A unicode string of the TLS server domain name or IP address > > ##### `.port` attribute > > > An integer of the port number the socket is connected to > > ##### `.certificate` attribute > > > An asn1crypto.x509.Certificate object of the end-entity certificate > > presented by the server > > ##### `.intermediates` attribute > > > A list of asn1crypto.x509.Certificate objects that were presented as > > intermediates by the server > > ##### `.protocol` attribute > > > A unicode string of: "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3" > > ##### `.cipher_suite` attribute > > > A unicode string of the IANA cipher suite name of the negotiated > > cipher suite > > ##### `.compression` attribute > > > A boolean if compression is enabled > > ##### `.session_id` attribute > > > A unicode string of "new" or "reused" or None for no ticket > > ##### `.session_ticket` attribute > > > A unicode string of "new" or "reused" or None for no ticket > > ##### `.session` attribute > > > The oscrypto.tls.TLSSession object used for this connection > > ##### `.socket` attribute > > > The underlying socket.socket connection > > ##### `.wrap()` method > > > ```python > > def wrap(cls, socket, hostname, session=None): > > """ > > :param socket: > > A socket.socket object to wrap with TLS > > > > :param hostname: > > A unicode string of the hostname or IP the socket is connected to > > > > :param session: > > An existing TLSSession object to allow for session reuse, specific > > protocol or manual certificate validation > > > > :raises: > > ValueError - when any of the parameters contain an invalid value > > TypeError - when any of the parameters are of the wrong type > > OSError - when an error is returned by the OS crypto library > > """ > > ``` > > > > Takes an existing socket and adds TLS > > ##### `.read()` method > > > ```python > > def read(self, max_length): > > """ > > :param max_length: > > The number of bytes to read - output may be less than this > > > > :raises: > > socket.socket - when a non-TLS socket error occurs > > oscrypto.errors.TLSError - when a TLS-related error occurs > > ValueError - when any of the parameters contain an invalid value > > TypeError - when any of the parameters are of the wrong type > > OSError - when an error is returned by the OS crypto library > > > > :return: > > A byte string of the data read > > """ > > ``` > > > > Reads data from the TLS-wrapped socket > > ##### `.read_line()` method > > > ```python > > def read_line(self): > > """ > > :return: > > A byte string of the next line from the socket > > """ > > ``` > > > > Reads a line from the socket, including the line ending of "\r\n", "\r", > > or "\n" > > ##### `.read_until()` method > > > ```python > > def read_until(self, marker): > > """ > > :param marker: > > A byte string or regex object from re.compile(). Used to determine > > when to stop reading. Regex objects are more inefficient since > > they must scan the entire byte string of read data each time data > > is read off the socket. > > > > :return: > > A byte string of the data read, including the marker > > """ > > ``` > > > > Reads data from the socket until a marker is found. Data read includes > > the marker. > > ##### `.read_exactly()` method > > > ```python > > def read_exactly(self, num_bytes): > > """ > > :param num_bytes: > > An integer - the exact number of bytes to read > > > > :return: > > A byte string of the data that was read > > """ > > ``` > > > > Reads exactly the specified number of bytes from the socket > > ##### `.select_read()` method > > > ```python > > def select_read(self, timeout=None): > > """ > > :param timeout: > > A float - the period of time to wait for data to be read. None for > > no time limit. > > > > :return: > > A boolean - if data is ready to be read. Will only be False if > > timeout is not None. > > """ > > ``` > > > > Blocks until the socket is ready to be read from, or the timeout is hit > > ##### `.write()` method > > > ```python > > def write(self, data): > > """ > > :param data: > > A byte string to write to the socket > > > > :raises: > > socket.socket - when a non-TLS socket error occurs > > oscrypto.errors.TLSError - when a TLS-related error occurs > > ValueError - when any of the parameters contain an invalid value > > TypeError - when any of the parameters are of the wrong type > > OSError - when an error is returned by the OS crypto library > > """ > > ``` > > > > Writes data to the TLS-wrapped socket > > ##### `.select_write()` method > > > ```python > > def select_write(self, timeout=None): > > """ > > :param timeout: > > A float - the period of time to wait for the socket to be ready to > > written to. None for no time limit. > > > > :return: > > A boolean - if the socket is ready for writing. Will only be False > > if timeout is not None. > > """ > > ``` > > > > Blocks until the socket is ready to be written to, or the timeout is hit > > ##### `.shutdown()` method > > > ```python > > def shutdown(self) > > ``` > > > > Shuts down the TLS session and then shuts down the underlying socket > > ##### `.close()` method > > > ```python > > def close(self) > > ``` > > > > Shuts down the TLS session and socket and forcibly closes it ### `TLSSession()` class > A TLS session object that multiple TLSSocket objects can share for the > sake of session reuse > > ##### constructor > > > ```python > > def __init__(self, protocol=None, manual_validation=False, extra_trust_roots=None): > > """ > > :param protocol: > > A unicode string or set of unicode strings representing allowable > > protocols to negotiate with the server: > > > > - "TLSv1.2" > > - "TLSv1.1" > > - "TLSv1" > > - "SSLv3" > > > > Default is: {"TLSv1", "TLSv1.1", "TLSv1.2"} > > > > :param manual_validation: > > If certificate and certificate path validation should be skipped > > and left to the developer to implement > > > > :param extra_trust_roots: > > A list containing one or more certificates to be treated as trust > > roots, in one of the following formats: > > - A byte string of the DER encoded certificate > > - A unicode string of the certificate filename > > - An asn1crypto.x509.Certificate object > > - An oscrypto.asymmetric.Certificate object > > > > :raises: > > ValueError - when any of the parameters contain an invalid value > > TypeError - when any of the parameters are of the wrong type > > OSError - when an error is returned by the OS crypto library > > """ > > ``` oscrypto-1.3.0/docs/trust_list.md000066400000000000000000000112221421476274700170770ustar00rootroot00000000000000# oscrypto.trust_list API Documentation The *oscrypto.trust_list* submodule implements functions to extract CA certificates/trust roots from the operating system trust store. The following functions comprise the public API: - [`get_list()`](#get_list-function) - [`get_path()`](#get_path-function) ### `get_list()` function > ```python > def get_list(cache_length=24, map_vendor_oids=True, cert_callback=None): > """ > :param cache_length: > The number of hours to cache the CA certs in memory before they are > refreshed > > :param map_vendor_oids: > A bool indicating if the following mapping of OIDs should happen for > trust information from the OS trust list: > - 1.2.840.113635.100.1.3 (apple_ssl) -> 1.3.6.1.5.5.7.3.1 (server_auth) > - 1.2.840.113635.100.1.3 (apple_ssl) -> 1.3.6.1.5.5.7.3.2 (client_auth) > - 1.2.840.113635.100.1.8 (apple_smime) -> 1.3.6.1.5.5.7.3.4 (email_protection) > - 1.2.840.113635.100.1.9 (apple_eap) -> 1.3.6.1.5.5.7.3.13 (eap_over_ppp) > - 1.2.840.113635.100.1.9 (apple_eap) -> 1.3.6.1.5.5.7.3.14 (eap_over_lan) > - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.5 (ipsec_end_system) > - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.6 (ipsec_tunnel) > - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.7 (ipsec_user) > - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.17 (ipsec_ike) > - 1.2.840.113635.100.1.16 (apple_code_signing) -> 1.3.6.1.5.5.7.3.3 (code_signing) > - 1.2.840.113635.100.1.20 (apple_time_stamping) -> 1.3.6.1.5.5.7.3.8 (time_stamping) > - 1.3.6.1.4.1.311.10.3.2 (microsoft_time_stamp_signing) -> 1.3.6.1.5.5.7.3.8 (time_stamping) > > :param cert_callback: > A callback that is called once for each certificate in the trust store. > It should accept two parameters: an asn1crypto.x509.Certificate object, > and a reason. The reason will be None if the certificate is being > exported, otherwise it will be a unicode string of the reason it won't. > > :raises: > oscrypto.errors.CACertsError - when an error occurs exporting/locating certs > > :return: > A (copied) list of 3-element tuples containing CA certs from the OS > trust ilst: > - 0: an asn1crypto.x509.Certificate object > - 1: a set of unicode strings of OIDs of trusted purposes > - 2: a set of unicode strings of OIDs of rejected purposes > """ > ``` > > Retrieves (and caches in memory) the list of CA certs from the OS. Includes > trust information from the OS - purposes the certificate should be trusted > or rejected for. > > Trust information is encoded via object identifiers (OIDs) that are sourced > from various RFCs and vendors (Apple and Microsoft). This trust information > augments what is in the certificate itself. Any OID that is in the set of > trusted purposes indicates the certificate has been explicitly trusted for > a purpose beyond the extended key purpose extension. Any OID in the reject > set is a purpose that the certificate should not be trusted for, even if > present in the extended key purpose extension. > > *A list of common trust OIDs can be found as part of the `KeyPurposeId()` > class in the `asn1crypto.x509` module of the `asn1crypto` package.* ### `get_path()` function > ```python > def get_path(temp_dir=None, cache_length=24, cert_callback=None): > """ > :param temp_dir: > The temporary directory to cache the CA certs in on OS X and Windows. > Needs to have secure permissions so other users can not modify the > contents. > > :param cache_length: > The number of hours to cache the CA certs on OS X and Windows > > :param cert_callback: > A callback that is called once for each certificate in the trust store. > It should accept two parameters: an asn1crypto.x509.Certificate object, > and a reason. The reason will be None if the certificate is being > exported, otherwise it will be a unicode string of the reason it won't. > This is only called on Windows and OS X when passed to this function. > > :raises: > oscrypto.errors.CACertsError - when an error occurs exporting/locating certs > > :return: > The full filesystem path to a CA certs file > """ > ``` > > Get the filesystem path to a file that contains OpenSSL-compatible CA certs. > > On OS X and Windows, there are extracted from the system certificate store > and cached in a file on the filesystem. This path should not be writable > by other users, otherwise they could inject CA certs into the trust list. oscrypto-1.3.0/docs/util.md000066400000000000000000000021251421476274700156420ustar00rootroot00000000000000# oscrypto.util API Documentation The *oscrypto.util* submodule implements supporting cryptographic functionality. The following functions comprise the public API: - [`rand_bytes()`](#rand_bytes-function) - [`constant_compare()`](#constant_compare-function) ### `rand_bytes()` function > ```python > def rand_bytes(length): > """ > :param length: > The desired number of bytes > > :raises: > ValueError - when any of the parameters contain an invalid value > TypeError - when any of the parameters are of the wrong type > OSError - when an error is returned by the OS crypto library > > :return: > A byte string > """ > ``` > > Returns a number of random bytes suitable for cryptographic purposes ### `constant_compare()` function > ```python > def constant_compare(a, b): > """ > :param a: > The first byte string > > :param b: > The second byte string > > :return: > A boolean if the two byte strings are equal > """ > ``` > > Compares two byte strings in constant time to see if they are equal oscrypto-1.3.0/oscrypto/000077500000000000000000000000001421476274700152755ustar00rootroot00000000000000oscrypto-1.3.0/oscrypto/__init__.py000066400000000000000000000230321421476274700174060ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os import platform import sys import threading from ._types import str_cls, type_name from .errors import LibraryNotFoundError from .version import __version__, __version_info__ __all__ = [ '__version__', '__version_info__', 'backend', 'ffi', 'load_order', 'use_ctypes', 'use_openssl', 'use_winlegacy', ] _backend_lock = threading.Lock() _module_values = { 'backend': None, 'backend_config': None, 'ffi': None } def backend(): """ :return: A unicode string of the backend being used: "openssl", "mac", "win", "winlegacy" """ if _module_values['backend'] is not None: return _module_values['backend'] with _backend_lock: if _module_values['backend'] is not None: return _module_values['backend'] if sys.platform == 'win32': # Windows XP was major version 5, Vista was 6 if sys.getwindowsversion()[0] < 6: _module_values['backend'] = 'winlegacy' else: _module_values['backend'] = 'win' elif sys.platform == 'darwin': _module_values['backend'] = 'mac' else: _module_values['backend'] = 'openssl' return _module_values['backend'] def _backend_config(): """ :return: A dict of config info for the backend. Only currently used by "openssl", it may contains zero or more of the following keys: - "libcrypto_path" - "libssl_path" """ if backend() != 'openssl': return {} if _module_values['backend_config'] is not None: return _module_values['backend_config'] with _backend_lock: if _module_values['backend_config'] is not None: return _module_values['backend_config'] _module_values['backend_config'] = {} return _module_values['backend_config'] def use_openssl(libcrypto_path, libssl_path, trust_list_path=None): """ Forces using OpenSSL dynamic libraries on OS X (.dylib) or Windows (.dll), or using a specific dynamic library on Linux/BSD (.so). This can also be used to configure oscrypto to use LibreSSL dynamic libraries. This method must be called before any oscrypto submodules are imported. :param libcrypto_path: A unicode string of the file path to the OpenSSL/LibreSSL libcrypto dynamic library. :param libssl_path: A unicode string of the file path to the OpenSSL/LibreSSL libssl dynamic library. :param trust_list_path: An optional unicode string of the path to a file containing OpenSSL-compatible CA certificates in PEM format. If this is not provided and the platform is OS X or Windows, the system trust roots will be exported from the OS and used for all TLS connections. :raises: ValueError - when one of the paths is not a unicode string OSError - when the trust_list_path does not exist on the filesystem oscrypto.errors.LibraryNotFoundError - when one of the path does not exist on the filesystem RuntimeError - when this function is called after another part of oscrypto has been imported """ if not isinstance(libcrypto_path, str_cls): raise ValueError('libcrypto_path must be a unicode string, not %s' % type_name(libcrypto_path)) if not isinstance(libssl_path, str_cls): raise ValueError('libssl_path must be a unicode string, not %s' % type_name(libssl_path)) if not os.path.exists(libcrypto_path): raise LibraryNotFoundError('libcrypto does not exist at %s' % libcrypto_path) if not os.path.exists(libssl_path): raise LibraryNotFoundError('libssl does not exist at %s' % libssl_path) if trust_list_path is not None: if not isinstance(trust_list_path, str_cls): raise ValueError('trust_list_path must be a unicode string, not %s' % type_name(trust_list_path)) if not os.path.exists(trust_list_path): raise OSError('trust_list_path does not exist at %s' % trust_list_path) with _backend_lock: new_config = { 'libcrypto_path': libcrypto_path, 'libssl_path': libssl_path, 'trust_list_path': trust_list_path, } if _module_values['backend'] == 'openssl' and _module_values['backend_config'] == new_config: return if _module_values['backend'] is not None: raise RuntimeError('Another part of oscrypto has already been imported, unable to force use of OpenSSL') _module_values['backend'] = 'openssl' _module_values['backend_config'] = new_config def use_winlegacy(): """ Forces use of the legacy Windows CryptoAPI. This should only be used on Windows XP or for testing. It is less full-featured than the Cryptography Next Generation (CNG) API, and as a result the elliptic curve and PSS padding features are implemented in pure Python. This isn't ideal, but it a shim for end-user client code. No one is going to run a server on Windows XP anyway, right?! :raises: EnvironmentError - when this function is called on an operating system other than Windows RuntimeError - when this function is called after another part of oscrypto has been imported """ if sys.platform != 'win32': plat = platform.system() or sys.platform if plat == 'Darwin': plat = 'OS X' raise EnvironmentError('The winlegacy backend can only be used on Windows, not %s' % plat) with _backend_lock: if _module_values['backend'] == 'winlegacy': return if _module_values['backend'] is not None: raise RuntimeError( 'Another part of oscrypto has already been imported, unable to force use of Windows legacy CryptoAPI' ) _module_values['backend'] = 'winlegacy' def use_ctypes(): """ Forces use of ctypes instead of cffi for the FFI layer :raises: RuntimeError - when this function is called after another part of oscrypto has been imported """ with _backend_lock: if _module_values['ffi'] == 'ctypes': return if _module_values['backend'] is not None: raise RuntimeError( 'Another part of oscrypto has already been imported, unable to force use of ctypes' ) _module_values['ffi'] = 'ctypes' def ffi(): """ Returns the FFI module being used :return: A unicode string of "cffi" or "ctypes" """ if _module_values['ffi'] is not None: return _module_values['ffi'] with _backend_lock: try: import cffi # noqa: F401 _module_values['ffi'] = 'cffi' except (ImportError): _module_values['ffi'] = 'ctypes' return _module_values['ffi'] def load_order(): """ Returns a list of the module and sub-module names for oscrypto in dependency load order, for the sake of live reloading code :return: A list of unicode strings of module names, as they would appear in sys.modules, ordered by which module should be reloaded first """ return [ 'oscrypto._asn1', 'oscrypto._cipher_suites', 'oscrypto._errors', 'oscrypto._int', 'oscrypto._types', 'oscrypto.errors', 'oscrypto.version', 'oscrypto', 'oscrypto._ffi', 'oscrypto._pkcs12', 'oscrypto._pkcs5', 'oscrypto._rand', 'oscrypto._tls', 'oscrypto._linux_bsd.trust_list', 'oscrypto._mac._common_crypto_cffi', 'oscrypto._mac._common_crypto_ctypes', 'oscrypto._mac._common_crypto', 'oscrypto._mac._core_foundation_cffi', 'oscrypto._mac._core_foundation_ctypes', 'oscrypto._mac._core_foundation', 'oscrypto._mac._security_cffi', 'oscrypto._mac._security_ctypes', 'oscrypto._mac._security', 'oscrypto._mac.trust_list', 'oscrypto._mac.util', 'oscrypto._openssl._libcrypto_cffi', 'oscrypto._openssl._libcrypto_ctypes', 'oscrypto._openssl._libcrypto', 'oscrypto._openssl._libssl_cffi', 'oscrypto._openssl._libssl_ctypes', 'oscrypto._openssl._libssl', 'oscrypto._openssl.util', 'oscrypto._win._cng_cffi', 'oscrypto._win._cng_ctypes', 'oscrypto._win._cng', 'oscrypto._win._decode', 'oscrypto._win._advapi32_cffi', 'oscrypto._win._advapi32_ctypes', 'oscrypto._win._advapi32', 'oscrypto._win._kernel32_cffi', 'oscrypto._win._kernel32_ctypes', 'oscrypto._win._kernel32', 'oscrypto._win._secur32_cffi', 'oscrypto._win._secur32_ctypes', 'oscrypto._win._secur32', 'oscrypto._win._crypt32_cffi', 'oscrypto._win._crypt32_ctypes', 'oscrypto._win._crypt32', 'oscrypto._win.trust_list', 'oscrypto._win.util', 'oscrypto.trust_list', 'oscrypto.util', 'oscrypto.kdf', 'oscrypto._mac.symmetric', 'oscrypto._openssl.symmetric', 'oscrypto._win.symmetric', 'oscrypto.symmetric', 'oscrypto._asymmetric', 'oscrypto._ecdsa', 'oscrypto._pkcs1', 'oscrypto._mac.asymmetric', 'oscrypto._openssl.asymmetric', 'oscrypto._win.asymmetric', 'oscrypto.asymmetric', 'oscrypto.keys', 'oscrypto._mac.tls', 'oscrypto._openssl.tls', 'oscrypto._win.tls', 'oscrypto.tls', ] oscrypto-1.3.0/oscrypto/_asn1.py000066400000000000000000000036471421476274700166620ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function # This file exists strictly to make it easier to vendor a combination of # oscrypto and asn1crypto from asn1crypto import algos, cms, core, keys, pem, pkcs12, util, x509 DHParameters = algos.DHParameters DSASignature = algos.DSASignature KeyExchangeAlgorithm = algos.KeyExchangeAlgorithm Pbkdf2Salt = algos.Pbkdf2Salt EncryptedData = cms.EncryptedData Integer = core.Integer Null = core.Null OctetString = core.OctetString DSAParams = keys.DSAParams DSAPrivateKey = keys.DSAPrivateKey ECDomainParameters = keys.ECDomainParameters ECPointBitString = keys.ECPointBitString ECPrivateKey = keys.ECPrivateKey EncryptedPrivateKeyInfo = keys.EncryptedPrivateKeyInfo PrivateKeyAlgorithm = keys.PrivateKeyAlgorithm PrivateKeyInfo = keys.PrivateKeyInfo PublicKeyAlgorithm = keys.PublicKeyAlgorithm PublicKeyInfo = keys.PublicKeyInfo RSAPrivateKey = keys.RSAPrivateKey RSAPublicKey = keys.RSAPublicKey int_from_bytes = util.int_from_bytes int_to_bytes = util.int_to_bytes OrderedDict = util.OrderedDict timezone = util.timezone armor = pem.armor unarmor = pem.unarmor CertBag = pkcs12.CertBag Pfx = pkcs12.Pfx SafeContents = pkcs12.SafeContents Certificate = x509.Certificate TrustedCertificate = x509.TrustedCertificate __all__ = [ 'armor', 'CertBag', 'Certificate', 'DHParameters', 'DSAParams', 'DSAPrivateKey', 'DSASignature', 'ECDomainParameters', 'ECPointBitString', 'ECPrivateKey', 'EncryptedData', 'EncryptedPrivateKeyInfo', 'int_from_bytes', 'int_to_bytes', 'Integer', 'KeyExchangeAlgorithm', 'Null', 'OctetString', 'OrderedDict', 'Pbkdf2Salt', 'Pfx', 'PrivateKeyAlgorithm', 'PrivateKeyInfo', 'PublicKeyAlgorithm', 'PublicKeyInfo', 'RSAPrivateKey', 'RSAPublicKey', 'SafeContents', 'timezone', 'TrustedCertificate', 'unarmor', ] oscrypto-1.3.0/oscrypto/_asymmetric.py000066400000000000000000000743001421476274700201670ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import hashlib import hmac import re import binascii from ._asn1 import ( CertBag, Certificate, DSAPrivateKey, ECPrivateKey, EncryptedData, EncryptedPrivateKeyInfo, Integer, OctetString, Pfx, PrivateKeyInfo, PublicKeyInfo, RSAPrivateKey, RSAPublicKey, SafeContents, unarmor, ) from .kdf import pbkdf1, pbkdf2, pkcs12_kdf from .symmetric import ( aes_cbc_pkcs7_decrypt, des_cbc_pkcs5_decrypt, rc2_cbc_pkcs5_decrypt, rc4_decrypt, tripledes_cbc_pkcs5_decrypt, ) from .util import constant_compare from ._errors import pretty_message from ._types import byte_cls, str_cls, type_name class _PrivateKeyBase(): asn1 = None _fingerprint = None def unwrap(self): """ Unwraps the private key into an asn1crypto.keys.RSAPrivateKey, asn1crypto.keys.DSAPrivateKey or asn1crypto.keys.ECPrivateKey object :return: An asn1crypto.keys.RSAPrivateKey, asn1crypto.keys.DSAPrivateKey or asn1crypto.keys.ECPrivateKey object """ if self.algorithm == 'rsa': return self.asn1['private_key'].parsed if self.algorithm == 'dsa': params = self.asn1['private_key_algorithm']['parameters'] return DSAPrivateKey({ 'version': 0, 'p': params['p'], 'q': params['q'], 'g': params['g'], 'public_key': self.public_key.unwrap(), 'private_key': self.asn1['private_key'].parsed, }) if self.algorithm == 'ec': output = self.asn1['private_key'].parsed output['parameters'] = self.asn1['private_key_algorithm']['parameters'] output['public_key'] = self.public_key.unwrap() return output @property def algorithm(self): """ :return: A unicode string of "rsa", "dsa" or "ec" """ return self.asn1.algorithm @property def curve(self): """ :return: A unicode string of EC curve name """ return self.asn1.curve[1] @property def bit_size(self): """ :return: The number of bits in the key, as an integer """ return self.asn1.bit_size @property def byte_size(self): """ :return: The number of bytes in the key, as an integer """ return self.asn1.byte_size class _PublicKeyBase(): asn1 = None _fingerprint = None def unwrap(self): """ Unwraps a public key into an asn1crypto.keys.RSAPublicKey, asn1crypto.core.Integer (for DSA) or asn1crypto.keys.ECPointBitString object :return: An asn1crypto.keys.RSAPublicKey, asn1crypto.core.Integer or asn1crypto.keys.ECPointBitString object """ if self.algorithm == 'ec': return self.asn1['public_key'] return self.asn1['public_key'].parsed @property def fingerprint(self): """ Creates a fingerprint that can be compared with a private key to see if the two form a pair. This fingerprint is not compatible with fingerprints generated by any other software. :return: A byte string that is a sha256 hash of selected components (based on the key type) """ if self._fingerprint is None: self._fingerprint = _fingerprint(self.asn1, None) return self._fingerprint @property def algorithm(self): """ :return: A unicode string of "rsa", "dsa" or "ec" """ return self.asn1.algorithm @property def curve(self): """ :return: A unicode string of EC curve name """ return self.asn1.curve[1] @property def bit_size(self): """ :return: The number of bits in the key, as an integer """ return self.asn1.bit_size @property def byte_size(self): """ :return: The number of bytes in the key, as an integer """ return self.asn1.byte_size class _CertificateBase(): asn1 = None @property def algorithm(self): """ :return: A unicode string of "rsa", "dsa" or "ec" """ return self.public_key.algorithm @property def curve(self): """ :return: A unicode string of EC curve name """ return self.public_key.curve @property def bit_size(self): """ :return: The number of bits in the public key, as an integer """ return self.public_key.bit_size @property def byte_size(self): """ :return: The number of bytes in the public key, as an integer """ return self.public_key.byte_size def _unwrap_private_key_info(key_info): """ Unwraps an asn1crypto.keys.PrivateKeyInfo object into an asn1crypto.keys.RSAPrivateKey, asn1crypto.keys.DSAPrivateKey or asn1crypto.keys.ECPrivateKey. :param key_info: An asn1crypto.keys.PrivateKeyInfo object :return: One of: - asn1crypto.keys.RSAPrivateKey - asn1crypto.keys.DSAPrivateKey - asn1crypto.keys.ECPrivateKey """ key_alg = key_info.algorithm if key_alg == 'rsa' or key_alg == 'rsassa_pss': return key_info['private_key'].parsed if key_alg == 'dsa': params = key_info['private_key_algorithm']['parameters'] parsed = key_info['private_key'].parsed return DSAPrivateKey({ 'version': 0, 'p': params['p'], 'q': params['q'], 'g': params['g'], 'public_key': Integer(pow( params['g'].native, parsed.native, params['p'].native )), 'private_key': parsed, }) if key_alg == 'ec': parsed = key_info['private_key'].parsed parsed['parameters'] = key_info['private_key_algorithm']['parameters'] return parsed raise ValueError('Unsupported key_info.algorithm "%s"' % key_info.algorithm) def _fingerprint(key_object, load_private_key): """ Returns a fingerprint used for correlating public keys and private keys :param key_object: An asn1crypto.keys.PrivateKeyInfo or asn1crypto.keys.PublicKeyInfo :raises: ValueError - when the key_object is not of the proper type ;return: A byte string fingerprint """ if isinstance(key_object, PrivateKeyInfo): key = key_object['private_key'].parsed if key_object.algorithm == 'rsa': to_hash = '%d:%d' % ( key['modulus'].native, key['public_exponent'].native, ) elif key_object.algorithm == 'dsa': params = key_object['private_key_algorithm']['parameters'] public_key = Integer(pow( params['g'].native, key_object['private_key'].parsed.native, params['p'].native )) to_hash = '%d:%d:%d:%d' % ( params['p'].native, params['q'].native, params['g'].native, public_key.native, ) elif key_object.algorithm == 'ec': public_key = key['public_key'].native if public_key is None: # This is gross, but since the EC public key is optional, # and we need to load the private key and use the crypto lib # to get the public key, we have to import the platform-specific # asymmetric implementation. This is the reason a bunch of the # imports are module imports, so we don't get an import cycle. public_key_object = load_private_key(key_object).public_key public_key = public_key_object.asn1['public_key'].parsed.native to_hash = '%s:' % key_object.curve[1] to_hash = to_hash.encode('utf-8') to_hash += public_key if isinstance(to_hash, str_cls): to_hash = to_hash.encode('utf-8') return hashlib.sha256(to_hash).digest() if isinstance(key_object, PublicKeyInfo): if key_object.algorithm == 'rsa': key = key_object['public_key'].parsed to_hash = '%d:%d' % ( key['modulus'].native, key['public_exponent'].native, ) elif key_object.algorithm == 'dsa': key = key_object['public_key'].parsed params = key_object['algorithm']['parameters'] to_hash = '%d:%d:%d:%d' % ( params['p'].native, params['q'].native, params['g'].native, key.native, ) elif key_object.algorithm == 'ec': public_key = key_object['public_key'].native to_hash = '%s:' % key_object.curve[1] to_hash = to_hash.encode('utf-8') to_hash += public_key if isinstance(to_hash, str_cls): to_hash = to_hash.encode('utf-8') return hashlib.sha256(to_hash).digest() raise ValueError(pretty_message( ''' key_object must be an instance of the asn1crypto.keys.PrivateKeyInfo or asn1crypto.keys.PublicKeyInfo classes, not %s ''', type_name(key_object) )) crypto_funcs = { 'rc2': rc2_cbc_pkcs5_decrypt, 'rc4': rc4_decrypt, 'des': des_cbc_pkcs5_decrypt, 'tripledes': tripledes_cbc_pkcs5_decrypt, 'aes': aes_cbc_pkcs7_decrypt, } def parse_public(data): """ Loads a public key from a DER or PEM-formatted file. Supports RSA, DSA and EC public keys. For RSA keys, both the old RSAPublicKey and SubjectPublicKeyInfo structures are supported. Also allows extracting a public key from an X.509 certificate. :param data: A byte string to load the public key from :raises: ValueError - when the data does not appear to contain a public key :return: An asn1crypto.keys.PublicKeyInfo object """ if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) key_type = None # Appears to be PEM formatted if re.match(b'\\s*-----', data) is not None: key_type, algo, data = _unarmor_pem(data) if key_type == 'private key': raise ValueError(pretty_message( ''' The data specified does not appear to be a public key or certificate, but rather a private key ''' )) # When a public key returning from _unarmor_pem has a known algorithm # of RSA, that means the DER structure is of the type RSAPublicKey, so # we need to wrap it in the PublicKeyInfo structure. if algo == 'rsa': return PublicKeyInfo.wrap(data, 'rsa') if key_type is None or key_type == 'public key': try: pki = PublicKeyInfo.load(data) # Call .native to fully parse since asn1crypto is lazy pki.native return pki except (ValueError): pass # Data was not PublicKeyInfo try: rpk = RSAPublicKey.load(data) # Call .native to fully parse since asn1crypto is lazy rpk.native return PublicKeyInfo.wrap(rpk, 'rsa') except (ValueError): pass # Data was not an RSAPublicKey if key_type is None or key_type == 'certificate': try: parsed_cert = Certificate.load(data) key_info = parsed_cert['tbs_certificate']['subject_public_key_info'] return key_info except (ValueError): pass # Data was not a cert raise ValueError('The data specified does not appear to be a known public key or certificate format') def parse_certificate(data): """ Loads a certificate from a DER or PEM-formatted file. Supports X.509 certificates only. :param data: A byte string to load the certificate from :raises: ValueError - when the data does not appear to contain a certificate :return: An asn1crypto.x509.Certificate object """ if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) key_type = None # Appears to be PEM formatted if re.match(b'\\s*-----', data) is not None: key_type, _, data = _unarmor_pem(data) if key_type == 'private key': raise ValueError(pretty_message( ''' The data specified does not appear to be a certificate, but rather a private key ''' )) if key_type == 'public key': raise ValueError(pretty_message( ''' The data specified does not appear to be a certificate, but rather a public key ''' )) if key_type is None or key_type == 'certificate': try: return Certificate.load(data) except (ValueError): pass # Data was not a Certificate raise ValueError(pretty_message( ''' The data specified does not appear to be a known certificate format ''' )) def parse_private(data, password=None): """ Loads a private key from a DER or PEM-formatted file. Supports RSA, DSA and EC private keys. Works with the follow formats: - RSAPrivateKey (PKCS#1) - ECPrivateKey (SECG SEC1 V2) - DSAPrivateKey (OpenSSL) - PrivateKeyInfo (RSA/DSA/EC - PKCS#8) - EncryptedPrivateKeyInfo (RSA/DSA/EC - PKCS#8) - Encrypted RSAPrivateKey (PEM only, OpenSSL) - Encrypted DSAPrivateKey (PEM only, OpenSSL) - Encrypted ECPrivateKey (PEM only, OpenSSL) :param data: A byte string to load the private key from :param password: The password to unencrypt the private key :raises: ValueError - when the data does not appear to contain a private key, or the password is invalid :return: An asn1crypto.keys.PrivateKeyInfo object """ if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if password is not None: if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) else: password = b'' # Appears to be PEM formatted if re.match(b'\\s*-----', data) is not None: key_type, _, data = _unarmor_pem(data, password) if key_type == 'public key': raise ValueError(pretty_message( ''' The data specified does not appear to be a private key, but rather a public key ''' )) if key_type == 'certificate': raise ValueError(pretty_message( ''' The data specified does not appear to be a private key, but rather a certificate ''' )) try: pki = PrivateKeyInfo.load(data) # Call .native to fully parse since asn1crypto is lazy pki.native return pki except (ValueError): pass # Data was not PrivateKeyInfo try: parsed_wrapper = EncryptedPrivateKeyInfo.load(data) encryption_algorithm_info = parsed_wrapper['encryption_algorithm'] encrypted_data = parsed_wrapper['encrypted_data'].native decrypted_data = _decrypt_encrypted_data(encryption_algorithm_info, encrypted_data, password) pki = PrivateKeyInfo.load(decrypted_data) # Call .native to fully parse since asn1crypto is lazy pki.native return pki except (ValueError): pass # Data was not EncryptedPrivateKeyInfo try: parsed = RSAPrivateKey.load(data) # Call .native to fully parse since asn1crypto is lazy parsed.native return PrivateKeyInfo.wrap(parsed, 'rsa') except (ValueError): pass # Data was not an RSAPrivateKey try: parsed = DSAPrivateKey.load(data) # Call .native to fully parse since asn1crypto is lazy parsed.native return PrivateKeyInfo.wrap(parsed, 'dsa') except (ValueError): pass # Data was not a DSAPrivateKey try: parsed = ECPrivateKey.load(data) # Call .native to fully parse since asn1crypto is lazy parsed.native return PrivateKeyInfo.wrap(parsed, 'ec') except (ValueError): pass # Data was not an ECPrivateKey raise ValueError(pretty_message( ''' The data specified does not appear to be a known private key format ''' )) def _unarmor_pem(data, password=None): """ Removes PEM-encoding from a public key, private key or certificate. If the private key is encrypted, the password will be used to decrypt it. :param data: A byte string of the PEM-encoded data :param password: A byte string of the encryption password, or None :return: A 3-element tuple in the format: (key_type, algorithm, der_bytes). The key_type will be a unicode string of "public key", "private key" or "certificate". The algorithm will be a unicode string of "rsa", "dsa" or "ec". """ object_type, headers, der_bytes = unarmor(data) type_regex = '^((DSA|EC|RSA) PRIVATE KEY|ENCRYPTED PRIVATE KEY|PRIVATE KEY|PUBLIC KEY|RSA PUBLIC KEY|CERTIFICATE)' armor_type = re.match(type_regex, object_type) if not armor_type: raise ValueError(pretty_message( ''' data does not seem to contain a PEM-encoded certificate, private key or public key ''' )) pem_header = armor_type.group(1) data = data.strip() # RSA private keys are encrypted after being DER-encoded, but before base64 # encoding, so they need to be handled specially if pem_header in set(['RSA PRIVATE KEY', 'DSA PRIVATE KEY', 'EC PRIVATE KEY']): algo = armor_type.group(2).lower() return ('private key', algo, _unarmor_pem_openssl_private(headers, der_bytes, password)) key_type = pem_header.lower() algo = None if key_type == 'encrypted private key': key_type = 'private key' elif key_type == 'rsa public key': key_type = 'public key' algo = 'rsa' return (key_type, algo, der_bytes) def _unarmor_pem_openssl_private(headers, data, password): """ Parses a PKCS#1 private key, or encrypted private key :param headers: A dict of "Name: Value" lines from right after the PEM header :param data: A byte string of the DER-encoded PKCS#1 private key :param password: A byte string of the password to use if the private key is encrypted :return: A byte string of the DER-encoded private key """ enc_algo = None enc_iv_hex = None enc_iv = None if 'DEK-Info' in headers: params = headers['DEK-Info'] if params.find(',') != -1: enc_algo, enc_iv_hex = params.strip().split(',') else: enc_algo = 'RC4' if not enc_algo: return data if enc_iv_hex: enc_iv = binascii.unhexlify(enc_iv_hex.encode('ascii')) enc_algo = enc_algo.lower() enc_key_length = { 'aes-128-cbc': 16, 'aes-128': 16, 'aes-192-cbc': 24, 'aes-192': 24, 'aes-256-cbc': 32, 'aes-256': 32, 'rc4': 16, 'rc4-64': 8, 'rc4-40': 5, 'rc2-64-cbc': 8, 'rc2-40-cbc': 5, 'rc2-cbc': 16, 'rc2': 16, 'des-ede3-cbc': 24, 'des-ede3': 24, 'des3': 24, 'des-ede-cbc': 16, 'des-cbc': 8, 'des': 8, }[enc_algo] enc_key = hashlib.md5(password + enc_iv[0:8]).digest() while enc_key_length > len(enc_key): enc_key += hashlib.md5(enc_key + password + enc_iv[0:8]).digest() enc_key = enc_key[0:enc_key_length] enc_algo_name = { 'aes-128-cbc': 'aes', 'aes-128': 'aes', 'aes-192-cbc': 'aes', 'aes-192': 'aes', 'aes-256-cbc': 'aes', 'aes-256': 'aes', 'rc4': 'rc4', 'rc4-64': 'rc4', 'rc4-40': 'rc4', 'rc2-64-cbc': 'rc2', 'rc2-40-cbc': 'rc2', 'rc2-cbc': 'rc2', 'rc2': 'rc2', 'des-ede3-cbc': 'tripledes', 'des-ede3': 'tripledes', 'des3': 'tripledes', 'des-ede-cbc': 'tripledes', 'des-cbc': 'des', 'des': 'des', }[enc_algo] decrypt_func = crypto_funcs[enc_algo_name] if enc_algo_name == 'rc4': return decrypt_func(enc_key, data) return decrypt_func(enc_key, data, enc_iv) def _parse_pkcs12(data, password, load_private_key): """ Parses a PKCS#12 ANS.1 DER-encoded structure and extracts certs and keys :param data: A byte string of a DER-encoded PKCS#12 file :param password: A byte string of the password to any encrypted data :param load_private_key: A callable that will accept a byte string and return an oscrypto.asymmetric.PrivateKey object :raises: ValueError - when any of the parameters are of the wrong type or value OSError - when an error is returned by one of the OS decryption functions :return: A three-element tuple of: 1. An asn1crypto.keys.PrivateKeyInfo object 2. An asn1crypto.x509.Certificate object 3. A list of zero or more asn1crypto.x509.Certificate objects that are "extra" certificates, possibly intermediates from the cert chain """ if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if password is not None: if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) else: password = b'' certs = {} private_keys = {} pfx = Pfx.load(data) auth_safe = pfx['auth_safe'] if auth_safe['content_type'].native != 'data': raise ValueError(pretty_message( ''' Only password-protected PKCS12 files are currently supported ''' )) authenticated_safe = pfx.authenticated_safe mac_data = pfx['mac_data'] if mac_data: mac_algo = mac_data['mac']['digest_algorithm']['algorithm'].native key_length = { 'sha1': 20, 'sha224': 28, 'sha256': 32, 'sha384': 48, 'sha512': 64, 'sha512_224': 28, 'sha512_256': 32, }[mac_algo] mac_key = pkcs12_kdf( mac_algo, password, mac_data['mac_salt'].native, mac_data['iterations'].native, key_length, 3 # ID 3 is for generating an HMAC key ) hash_mod = getattr(hashlib, mac_algo) computed_hmac = hmac.new(mac_key, auth_safe['content'].contents, hash_mod).digest() stored_hmac = mac_data['mac']['digest'].native if not constant_compare(computed_hmac, stored_hmac): raise ValueError('Password provided is invalid') for content_info in authenticated_safe: content = content_info['content'] if isinstance(content, OctetString): _parse_safe_contents(content.native, certs, private_keys, password, load_private_key) elif isinstance(content, EncryptedData): encrypted_content_info = content['encrypted_content_info'] encryption_algorithm_info = encrypted_content_info['content_encryption_algorithm'] encrypted_content = encrypted_content_info['encrypted_content'].native decrypted_content = _decrypt_encrypted_data(encryption_algorithm_info, encrypted_content, password) _parse_safe_contents(decrypted_content, certs, private_keys, password, load_private_key) else: raise ValueError(pretty_message( ''' Public-key-based PKCS12 files are not currently supported ''' )) key_fingerprints = set(private_keys.keys()) cert_fingerprints = set(certs.keys()) common_fingerprints = sorted(list(key_fingerprints & cert_fingerprints)) key = None cert = None other_certs = [] if len(common_fingerprints) >= 1: fingerprint = common_fingerprints[0] key = private_keys[fingerprint] cert = certs[fingerprint] other_certs = [certs[f] for f in certs if f != fingerprint] return (key, cert, other_certs) if len(private_keys) > 0: first_key = sorted(list(private_keys.keys()))[0] key = private_keys[first_key] if len(certs) > 0: first_key = sorted(list(certs.keys()))[0] cert = certs[first_key] del certs[first_key] if len(certs) > 0: other_certs = sorted(list(certs.values()), key=lambda c: c.subject.human_friendly) return (key, cert, other_certs) def _parse_safe_contents(safe_contents, certs, private_keys, password, load_private_key): """ Parses a SafeContents PKCS#12 ANS.1 structure and extracts certs and keys :param safe_contents: A byte string of ber-encoded SafeContents, or a asn1crypto.pkcs12.SafeContents parsed object :param certs: A dict to store certificates in :param keys: A dict to store keys in :param password: A byte string of the password to any encrypted data :param load_private_key: A callable that will accept a byte string and return an oscrypto.asymmetric.PrivateKey object """ if isinstance(safe_contents, byte_cls): safe_contents = SafeContents.load(safe_contents) for safe_bag in safe_contents: bag_value = safe_bag['bag_value'] if isinstance(bag_value, CertBag): if bag_value['cert_id'].native == 'x509': cert = bag_value['cert_value'].parsed public_key_info = cert['tbs_certificate']['subject_public_key_info'] certs[_fingerprint(public_key_info, None)] = bag_value['cert_value'].parsed elif isinstance(bag_value, PrivateKeyInfo): private_keys[_fingerprint(bag_value, load_private_key)] = bag_value elif isinstance(bag_value, EncryptedPrivateKeyInfo): encryption_algorithm_info = bag_value['encryption_algorithm'] encrypted_key_bytes = bag_value['encrypted_data'].native decrypted_key_bytes = _decrypt_encrypted_data(encryption_algorithm_info, encrypted_key_bytes, password) private_key = PrivateKeyInfo.load(decrypted_key_bytes) private_keys[_fingerprint(private_key, load_private_key)] = private_key elif isinstance(bag_value, SafeContents): _parse_safe_contents(bag_value, certs, private_keys, password, load_private_key) else: # We don't care about CRL bags or secret bags pass def _decrypt_encrypted_data(encryption_algorithm_info, encrypted_content, password): """ Decrypts encrypted ASN.1 data :param encryption_algorithm_info: An instance of asn1crypto.pkcs5.Pkcs5EncryptionAlgorithm :param encrypted_content: A byte string of the encrypted content :param password: A byte string of the encrypted content's password :return: A byte string of the decrypted plaintext """ decrypt_func = crypto_funcs[encryption_algorithm_info.encryption_cipher] # Modern, PKCS#5 PBES2-based encryption if encryption_algorithm_info.kdf == 'pbkdf2': if encryption_algorithm_info.encryption_cipher == 'rc5': raise ValueError(pretty_message( ''' PBES2 encryption scheme utilizing RC5 encryption is not supported ''' )) enc_key = pbkdf2( encryption_algorithm_info.kdf_hmac, password, encryption_algorithm_info.kdf_salt, encryption_algorithm_info.kdf_iterations, encryption_algorithm_info.key_length ) enc_iv = encryption_algorithm_info.encryption_iv plaintext = decrypt_func(enc_key, encrypted_content, enc_iv) elif encryption_algorithm_info.kdf == 'pbkdf1': derived_output = pbkdf1( encryption_algorithm_info.kdf_hmac, password, encryption_algorithm_info.kdf_salt, encryption_algorithm_info.kdf_iterations, encryption_algorithm_info.key_length + 8 ) enc_key = derived_output[0:8] enc_iv = derived_output[8:16] plaintext = decrypt_func(enc_key, encrypted_content, enc_iv) elif encryption_algorithm_info.kdf == 'pkcs12_kdf': enc_key = pkcs12_kdf( encryption_algorithm_info.kdf_hmac, password, encryption_algorithm_info.kdf_salt, encryption_algorithm_info.kdf_iterations, encryption_algorithm_info.key_length, 1 # ID 1 is for generating a key ) # Since RC4 is a stream cipher, we don't use an IV if encryption_algorithm_info.encryption_cipher == 'rc4': plaintext = decrypt_func(enc_key, encrypted_content) else: enc_iv = pkcs12_kdf( encryption_algorithm_info.kdf_hmac, password, encryption_algorithm_info.kdf_salt, encryption_algorithm_info.kdf_iterations, encryption_algorithm_info.encryption_block_size, 2 # ID 2 is for generating an IV ) plaintext = decrypt_func(enc_key, encrypted_content, enc_iv) return plaintext oscrypto-1.3.0/oscrypto/_cipher_suites.py000066400000000000000000000432251421476274700206620ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function __all__ = [ 'CIPHER_SUITE_MAP', ] CIPHER_SUITE_MAP = { b'\x00\x00': 'TLS_NULL_WITH_NULL_NULL', b'\x00\x01': 'TLS_RSA_WITH_NULL_MD5', b'\x00\x02': 'TLS_RSA_WITH_NULL_SHA', b'\x00\x03': 'TLS_RSA_EXPORT_WITH_RC4_40_MD5', b'\x00\x04': 'TLS_RSA_WITH_RC4_128_MD5', b'\x00\x05': 'TLS_RSA_WITH_RC4_128_SHA', b'\x00\x06': 'TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5', b'\x00\x07': 'TLS_RSA_WITH_IDEA_CBC_SHA', b'\x00\x08': 'TLS_RSA_EXPORT_WITH_DES40_CBC_SHA', b'\x00\x09': 'TLS_RSA_WITH_DES_CBC_SHA', b'\x00\x0A': 'TLS_RSA_WITH_3DES_EDE_CBC_SHA', b'\x00\x0B': 'TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA', b'\x00\x0C': 'TLS_DH_DSS_WITH_DES_CBC_SHA', b'\x00\x0D': 'TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA', b'\x00\x0E': 'TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA', b'\x00\x0F': 'TLS_DH_RSA_WITH_DES_CBC_SHA', b'\x00\x10': 'TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA', b'\x00\x11': 'TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA', b'\x00\x12': 'TLS_DHE_DSS_WITH_DES_CBC_SHA', b'\x00\x13': 'TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA', b'\x00\x14': 'TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA', b'\x00\x15': 'TLS_DHE_RSA_WITH_DES_CBC_SHA', b'\x00\x16': 'TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA', b'\x00\x17': 'TLS_DH_anon_EXPORT_WITH_RC4_40_MD5', b'\x00\x18': 'TLS_DH_anon_WITH_RC4_128_MD5', b'\x00\x19': 'TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA', b'\x00\x1A': 'TLS_DH_anon_WITH_DES_CBC_SHA', b'\x00\x1B': 'TLS_DH_anon_WITH_3DES_EDE_CBC_SHA', b'\x00\x1E': 'TLS_KRB5_WITH_DES_CBC_SHA', b'\x00\x1F': 'TLS_KRB5_WITH_3DES_EDE_CBC_SHA', b'\x00\x20': 'TLS_KRB5_WITH_RC4_128_SHA', b'\x00\x21': 'TLS_KRB5_WITH_IDEA_CBC_SHA', b'\x00\x22': 'TLS_KRB5_WITH_DES_CBC_MD5', b'\x00\x23': 'TLS_KRB5_WITH_3DES_EDE_CBC_MD5', b'\x00\x24': 'TLS_KRB5_WITH_RC4_128_MD5', b'\x00\x25': 'TLS_KRB5_WITH_IDEA_CBC_MD5', b'\x00\x26': 'TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA', b'\x00\x27': 'TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA', b'\x00\x28': 'TLS_KRB5_EXPORT_WITH_RC4_40_SHA', b'\x00\x29': 'TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5', b'\x00\x2A': 'TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5', b'\x00\x2B': 'TLS_KRB5_EXPORT_WITH_RC4_40_MD5', b'\x00\x2C': 'TLS_PSK_WITH_NULL_SHA', b'\x00\x2D': 'TLS_DHE_PSK_WITH_NULL_SHA', b'\x00\x2E': 'TLS_RSA_PSK_WITH_NULL_SHA', b'\x00\x2F': 'TLS_RSA_WITH_AES_128_CBC_SHA', b'\x00\x30': 'TLS_DH_DSS_WITH_AES_128_CBC_SHA', b'\x00\x31': 'TLS_DH_RSA_WITH_AES_128_CBC_SHA', b'\x00\x32': 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA', b'\x00\x33': 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA', b'\x00\x34': 'TLS_DH_anon_WITH_AES_128_CBC_SHA', b'\x00\x35': 'TLS_RSA_WITH_AES_256_CBC_SHA', b'\x00\x36': 'TLS_DH_DSS_WITH_AES_256_CBC_SHA', b'\x00\x37': 'TLS_DH_RSA_WITH_AES_256_CBC_SHA', b'\x00\x38': 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA', b'\x00\x39': 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA', b'\x00\x3A': 'TLS_DH_anon_WITH_AES_256_CBC_SHA', b'\x00\x3B': 'TLS_RSA_WITH_NULL_SHA256', b'\x00\x3C': 'TLS_RSA_WITH_AES_128_CBC_SHA256', b'\x00\x3D': 'TLS_RSA_WITH_AES_256_CBC_SHA256', b'\x00\x3E': 'TLS_DH_DSS_WITH_AES_128_CBC_SHA256', b'\x00\x3F': 'TLS_DH_RSA_WITH_AES_128_CBC_SHA256', b'\x00\x40': 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA256', b'\x00\x41': 'TLS_RSA_WITH_CAMELLIA_128_CBC_SHA', b'\x00\x42': 'TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA', b'\x00\x43': 'TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA', b'\x00\x44': 'TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA', b'\x00\x45': 'TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA', b'\x00\x46': 'TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA', b'\x00\x67': 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA256', b'\x00\x68': 'TLS_DH_DSS_WITH_AES_256_CBC_SHA256', b'\x00\x69': 'TLS_DH_RSA_WITH_AES_256_CBC_SHA256', b'\x00\x6A': 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA256', b'\x00\x6B': 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA256', b'\x00\x6C': 'TLS_DH_anon_WITH_AES_128_CBC_SHA256', b'\x00\x6D': 'TLS_DH_anon_WITH_AES_256_CBC_SHA256', b'\x00\x84': 'TLS_RSA_WITH_CAMELLIA_256_CBC_SHA', b'\x00\x85': 'TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA', b'\x00\x86': 'TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA', b'\x00\x87': 'TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA', b'\x00\x88': 'TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA', b'\x00\x89': 'TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA', b'\x00\x8A': 'TLS_PSK_WITH_RC4_128_SHA', b'\x00\x8B': 'TLS_PSK_WITH_3DES_EDE_CBC_SHA', b'\x00\x8C': 'TLS_PSK_WITH_AES_128_CBC_SHA', b'\x00\x8D': 'TLS_PSK_WITH_AES_256_CBC_SHA', b'\x00\x8E': 'TLS_DHE_PSK_WITH_RC4_128_SHA', b'\x00\x8F': 'TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA', b'\x00\x90': 'TLS_DHE_PSK_WITH_AES_128_CBC_SHA', b'\x00\x91': 'TLS_DHE_PSK_WITH_AES_256_CBC_SHA', b'\x00\x92': 'TLS_RSA_PSK_WITH_RC4_128_SHA', b'\x00\x93': 'TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA', b'\x00\x94': 'TLS_RSA_PSK_WITH_AES_128_CBC_SHA', b'\x00\x95': 'TLS_RSA_PSK_WITH_AES_256_CBC_SHA', b'\x00\x96': 'TLS_RSA_WITH_SEED_CBC_SHA', b'\x00\x97': 'TLS_DH_DSS_WITH_SEED_CBC_SHA', b'\x00\x98': 'TLS_DH_RSA_WITH_SEED_CBC_SHA', b'\x00\x99': 'TLS_DHE_DSS_WITH_SEED_CBC_SHA', b'\x00\x9A': 'TLS_DHE_RSA_WITH_SEED_CBC_SHA', b'\x00\x9B': 'TLS_DH_anon_WITH_SEED_CBC_SHA', b'\x00\x9C': 'TLS_RSA_WITH_AES_128_GCM_SHA256', b'\x00\x9D': 'TLS_RSA_WITH_AES_256_GCM_SHA384', b'\x00\x9E': 'TLS_DHE_RSA_WITH_AES_128_GCM_SHA256', b'\x00\x9F': 'TLS_DHE_RSA_WITH_AES_256_GCM_SHA384', b'\x00\xA0': 'TLS_DH_RSA_WITH_AES_128_GCM_SHA256', b'\x00\xA1': 'TLS_DH_RSA_WITH_AES_256_GCM_SHA384', b'\x00\xA2': 'TLS_DHE_DSS_WITH_AES_128_GCM_SHA256', b'\x00\xA3': 'TLS_DHE_DSS_WITH_AES_256_GCM_SHA384', b'\x00\xA4': 'TLS_DH_DSS_WITH_AES_128_GCM_SHA256', b'\x00\xA5': 'TLS_DH_DSS_WITH_AES_256_GCM_SHA384', b'\x00\xA6': 'TLS_DH_anon_WITH_AES_128_GCM_SHA256', b'\x00\xA7': 'TLS_DH_anon_WITH_AES_256_GCM_SHA384', b'\x00\xA8': 'TLS_PSK_WITH_AES_128_GCM_SHA256', b'\x00\xA9': 'TLS_PSK_WITH_AES_256_GCM_SHA384', b'\x00\xAA': 'TLS_DHE_PSK_WITH_AES_128_GCM_SHA256', b'\x00\xAB': 'TLS_DHE_PSK_WITH_AES_256_GCM_SHA384', b'\x00\xAC': 'TLS_RSA_PSK_WITH_AES_128_GCM_SHA256', b'\x00\xAD': 'TLS_RSA_PSK_WITH_AES_256_GCM_SHA384', b'\x00\xAE': 'TLS_PSK_WITH_AES_128_CBC_SHA256', b'\x00\xAF': 'TLS_PSK_WITH_AES_256_CBC_SHA384', b'\x00\xB0': 'TLS_PSK_WITH_NULL_SHA256', b'\x00\xB1': 'TLS_PSK_WITH_NULL_SHA384', b'\x00\xB2': 'TLS_DHE_PSK_WITH_AES_128_CBC_SHA256', b'\x00\xB3': 'TLS_DHE_PSK_WITH_AES_256_CBC_SHA384', b'\x00\xB4': 'TLS_DHE_PSK_WITH_NULL_SHA256', b'\x00\xB5': 'TLS_DHE_PSK_WITH_NULL_SHA384', b'\x00\xB6': 'TLS_RSA_PSK_WITH_AES_128_CBC_SHA256', b'\x00\xB7': 'TLS_RSA_PSK_WITH_AES_256_CBC_SHA384', b'\x00\xB8': 'TLS_RSA_PSK_WITH_NULL_SHA256', b'\x00\xB9': 'TLS_RSA_PSK_WITH_NULL_SHA384', b'\x00\xBA': 'TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256', b'\x00\xBB': 'TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256', b'\x00\xBC': 'TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256', b'\x00\xBD': 'TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256', b'\x00\xBE': 'TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256', b'\x00\xBF': 'TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256', b'\x00\xC0': 'TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256', b'\x00\xC1': 'TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256', b'\x00\xC2': 'TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256', b'\x00\xC3': 'TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256', b'\x00\xC4': 'TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256', b'\x00\xC5': 'TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256', b'\x00\xFF': 'TLS_EMPTY_RENEGOTIATION_INFO_SCSV', b'\x13\x01': 'TLS_AES_128_GCM_SHA256', b'\x13\x02': 'TLS_AES_256_GCM_SHA384', b'\x13\x03': 'TLS_CHACHA20_POLY1305_SHA256', b'\x13\x04': 'TLS_AES_128_CCM_SHA256', b'\x13\x05': 'TLS_AES_128_CCM_8_SHA256', b'\xC0\x01': 'TLS_ECDH_ECDSA_WITH_NULL_SHA', b'\xC0\x02': 'TLS_ECDH_ECDSA_WITH_RC4_128_SHA', b'\xC0\x03': 'TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA', b'\xC0\x04': 'TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA', b'\xC0\x05': 'TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA', b'\xC0\x06': 'TLS_ECDHE_ECDSA_WITH_NULL_SHA', b'\xC0\x07': 'TLS_ECDHE_ECDSA_WITH_RC4_128_SHA', b'\xC0\x08': 'TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA', b'\xC0\x09': 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA', b'\xC0\x0A': 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA', b'\xC0\x0B': 'TLS_ECDH_RSA_WITH_NULL_SHA', b'\xC0\x0C': 'TLS_ECDH_RSA_WITH_RC4_128_SHA', b'\xC0\x0D': 'TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA', b'\xC0\x0E': 'TLS_ECDH_RSA_WITH_AES_128_CBC_SHA', b'\xC0\x0F': 'TLS_ECDH_RSA_WITH_AES_256_CBC_SHA', b'\xC0\x10': 'TLS_ECDHE_RSA_WITH_NULL_SHA', b'\xC0\x11': 'TLS_ECDHE_RSA_WITH_RC4_128_SHA', b'\xC0\x12': 'TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA', b'\xC0\x13': 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA', b'\xC0\x14': 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA', b'\xC0\x15': 'TLS_ECDH_anon_WITH_NULL_SHA', b'\xC0\x16': 'TLS_ECDH_anon_WITH_RC4_128_SHA', b'\xC0\x17': 'TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA', b'\xC0\x18': 'TLS_ECDH_anon_WITH_AES_128_CBC_SHA', b'\xC0\x19': 'TLS_ECDH_anon_WITH_AES_256_CBC_SHA', b'\xC0\x1A': 'TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA', b'\xC0\x1B': 'TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA', b'\xC0\x1C': 'TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA', b'\xC0\x1D': 'TLS_SRP_SHA_WITH_AES_128_CBC_SHA', b'\xC0\x1E': 'TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA', b'\xC0\x1F': 'TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA', b'\xC0\x20': 'TLS_SRP_SHA_WITH_AES_256_CBC_SHA', b'\xC0\x21': 'TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA', b'\xC0\x22': 'TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA', b'\xC0\x23': 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256', b'\xC0\x24': 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384', b'\xC0\x25': 'TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256', b'\xC0\x26': 'TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384', b'\xC0\x27': 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256', b'\xC0\x28': 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384', b'\xC0\x29': 'TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256', b'\xC0\x2A': 'TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384', b'\xC0\x2B': 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256', b'\xC0\x2C': 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384', b'\xC0\x2D': 'TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256', b'\xC0\x2E': 'TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384', b'\xC0\x2F': 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256', b'\xC0\x30': 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384', b'\xC0\x31': 'TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256', b'\xC0\x32': 'TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384', b'\xC0\x33': 'TLS_ECDHE_PSK_WITH_RC4_128_SHA', b'\xC0\x34': 'TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA', b'\xC0\x35': 'TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA', b'\xC0\x36': 'TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA', b'\xC0\x37': 'TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256', b'\xC0\x38': 'TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384', b'\xC0\x39': 'TLS_ECDHE_PSK_WITH_NULL_SHA', b'\xC0\x3A': 'TLS_ECDHE_PSK_WITH_NULL_SHA256', b'\xC0\x3B': 'TLS_ECDHE_PSK_WITH_NULL_SHA384', b'\xC0\x3C': 'TLS_RSA_WITH_ARIA_128_CBC_SHA256', b'\xC0\x3D': 'TLS_RSA_WITH_ARIA_256_CBC_SHA384', b'\xC0\x3E': 'TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256', b'\xC0\x3F': 'TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384', b'\xC0\x40': 'TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256', b'\xC0\x41': 'TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384', b'\xC0\x42': 'TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256', b'\xC0\x43': 'TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384', b'\xC0\x44': 'TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256', b'\xC0\x45': 'TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384', b'\xC0\x46': 'TLS_DH_anon_WITH_ARIA_128_CBC_SHA256', b'\xC0\x47': 'TLS_DH_anon_WITH_ARIA_256_CBC_SHA384', b'\xC0\x48': 'TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256', b'\xC0\x49': 'TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384', b'\xC0\x4A': 'TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256', b'\xC0\x4B': 'TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384', b'\xC0\x4C': 'TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256', b'\xC0\x4D': 'TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384', b'\xC0\x4E': 'TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256', b'\xC0\x4F': 'TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384', b'\xC0\x50': 'TLS_RSA_WITH_ARIA_128_GCM_SHA256', b'\xC0\x51': 'TLS_RSA_WITH_ARIA_256_GCM_SHA384', b'\xC0\x52': 'TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256', b'\xC0\x53': 'TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384', b'\xC0\x54': 'TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256', b'\xC0\x55': 'TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384', b'\xC0\x56': 'TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256', b'\xC0\x57': 'TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384', b'\xC0\x58': 'TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256', b'\xC0\x59': 'TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384', b'\xC0\x5A': 'TLS_DH_anon_WITH_ARIA_128_GCM_SHA256', b'\xC0\x5B': 'TLS_DH_anon_WITH_ARIA_256_GCM_SHA384', b'\xC0\x5C': 'TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256', b'\xC0\x5D': 'TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384', b'\xC0\x5E': 'TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256', b'\xC0\x5F': 'TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384', b'\xC0\x60': 'TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256', b'\xC0\x61': 'TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384', b'\xC0\x62': 'TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256', b'\xC0\x63': 'TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384', b'\xC0\x64': 'TLS_PSK_WITH_ARIA_128_CBC_SHA256', b'\xC0\x65': 'TLS_PSK_WITH_ARIA_256_CBC_SHA384', b'\xC0\x66': 'TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256', b'\xC0\x67': 'TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384', b'\xC0\x68': 'TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256', b'\xC0\x69': 'TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384', b'\xC0\x6A': 'TLS_PSK_WITH_ARIA_128_GCM_SHA256', b'\xC0\x6B': 'TLS_PSK_WITH_ARIA_256_GCM_SHA384', b'\xC0\x6C': 'TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256', b'\xC0\x6D': 'TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384', b'\xC0\x6E': 'TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256', b'\xC0\x6F': 'TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384', b'\xC0\x70': 'TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256', b'\xC0\x71': 'TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384', b'\xC0\x72': 'TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256', b'\xC0\x73': 'TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384', b'\xC0\x74': 'TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256', b'\xC0\x75': 'TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384', b'\xC0\x76': 'TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256', b'\xC0\x77': 'TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384', b'\xC0\x78': 'TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256', b'\xC0\x79': 'TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384', b'\xC0\x7A': 'TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256', b'\xC0\x7B': 'TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384', b'\xC0\x7C': 'TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256', b'\xC0\x7D': 'TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384', b'\xC0\x7E': 'TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256', b'\xC0\x7F': 'TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384', b'\xC0\x80': 'TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256', b'\xC0\x81': 'TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384', b'\xC0\x82': 'TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256', b'\xC0\x83': 'TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384', b'\xC0\x84': 'TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256', b'\xC0\x85': 'TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384', b'\xC0\x86': 'TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256', b'\xC0\x87': 'TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384', b'\xC0\x88': 'TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256', b'\xC0\x89': 'TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384', b'\xC0\x8A': 'TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256', b'\xC0\x8B': 'TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384', b'\xC0\x8C': 'TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256', b'\xC0\x8D': 'TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384', b'\xC0\x8E': 'TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256', b'\xC0\x8F': 'TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384', b'\xC0\x90': 'TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256', b'\xC0\x91': 'TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384', b'\xC0\x92': 'TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256', b'\xC0\x93': 'TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384', b'\xC0\x94': 'TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256', b'\xC0\x95': 'TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384', b'\xC0\x96': 'TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256', b'\xC0\x97': 'TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384', b'\xC0\x98': 'TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256', b'\xC0\x99': 'TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384', b'\xC0\x9A': 'TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256', b'\xC0\x9B': 'TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384', b'\xC0\x9C': 'TLS_RSA_WITH_AES_128_CCM', b'\xC0\x9D': 'TLS_RSA_WITH_AES_256_CCM', b'\xC0\x9E': 'TLS_DHE_RSA_WITH_AES_128_CCM', b'\xC0\x9F': 'TLS_DHE_RSA_WITH_AES_256_CCM', b'\xC0\xA0': 'TLS_RSA_WITH_AES_128_CCM_8', b'\xC0\xA1': 'TLS_RSA_WITH_AES_256_CCM_8', b'\xC0\xA2': 'TLS_DHE_RSA_WITH_AES_128_CCM_8', b'\xC0\xA3': 'TLS_DHE_RSA_WITH_AES_256_CCM_8', b'\xC0\xA4': 'TLS_PSK_WITH_AES_128_CCM', b'\xC0\xA5': 'TLS_PSK_WITH_AES_256_CCM', b'\xC0\xA6': 'TLS_DHE_PSK_WITH_AES_128_CCM', b'\xC0\xA7': 'TLS_DHE_PSK_WITH_AES_256_CCM', b'\xC0\xA8': 'TLS_PSK_WITH_AES_128_CCM_8', b'\xC0\xA9': 'TLS_PSK_WITH_AES_256_CCM_8', b'\xC0\xAA': 'TLS_PSK_DHE_WITH_AES_128_CCM_8', b'\xC0\xAB': 'TLS_PSK_DHE_WITH_AES_256_CCM_8', b'\xCC\xA8': 'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256', b'\xCC\xA9': 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256', b'\xCC\xAA': 'TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256', b'\xCC\xAB': 'TLS_PSK_WITH_CHACHA20_POLY1305_SHA256', b'\xCC\xAC': 'TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256', b'\xCC\xAD': 'TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256', b'\xCC\xAE': 'TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256', } oscrypto-1.3.0/oscrypto/_ecdsa.py000066400000000000000000000544421421476274700170760ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import hashlib import hmac import sys from . import backend from ._asn1 import ( Certificate, DSASignature, ECDomainParameters, ECPointBitString, ECPrivateKey, int_from_bytes, PrivateKeyAlgorithm, PrivateKeyInfo, PublicKeyAlgorithm, PublicKeyInfo, ) from ._errors import pretty_message from ._types import type_name, byte_cls from .util import rand_bytes from .errors import SignatureError if sys.version_info < (3,): chr_cls = chr range = xrange # noqa else: def chr_cls(num): return bytes([num]) _backend = backend() if _backend != 'winlegacy': # This pure-Python ECDSA code is only suitable for use on client machines, # and is only needed on Windows 5.x (XP/2003). For testing sake it is # possible to force use of it on newer versions of Windows. raise SystemError('Pure-python ECDSA code is only for Windows XP/2003') __all__ = [ 'ec_generate_pair', 'ec_compute_public_key_point', 'ec_public_key_info', 'ecdsa_sign', 'ecdsa_verify', ] CURVE_BYTES = { 'secp256r1': 32, 'secp384r1': 48, 'secp521r1': 66, } CURVE_EXTRA_BITS = { 'secp256r1': 0, 'secp384r1': 0, 'secp521r1': 7, } def ec_generate_pair(curve): """ Generates a EC public/private key pair :param curve: A unicode string. Valid values include "secp256r1", "secp384r1" and "secp521r1". :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type :return: A 2-element tuple of (asn1crypto.keys.PublicKeyInfo, asn1crypto.keys.PrivateKeyInfo) """ if curve not in set(['secp256r1', 'secp384r1', 'secp521r1']): raise ValueError(pretty_message( ''' curve must be one of "secp256r1", "secp384r1", "secp521r1", not %s ''', repr(curve) )) curve_num_bytes = CURVE_BYTES[curve] curve_base_point = { 'secp256r1': SECP256R1_BASE_POINT, 'secp384r1': SECP384R1_BASE_POINT, 'secp521r1': SECP521R1_BASE_POINT, }[curve] while True: private_key_bytes = rand_bytes(curve_num_bytes) private_key_int = int_from_bytes(private_key_bytes, signed=False) if private_key_int > 0 and private_key_int < curve_base_point.order: break private_key_info = PrivateKeyInfo({ 'version': 0, 'private_key_algorithm': PrivateKeyAlgorithm({ 'algorithm': 'ec', 'parameters': ECDomainParameters( name='named', value=curve ) }), 'private_key': ECPrivateKey({ 'version': 'ecPrivkeyVer1', 'private_key': private_key_int }), }) ec_point = ec_compute_public_key_point(private_key_info) private_key_info['private_key'].parsed['public_key'] = ec_point.copy() return (ec_public_key_info(ec_point, curve), private_key_info) def ec_compute_public_key_point(private_key): """ Constructs the PublicKeyInfo for a PrivateKeyInfo :param private_key: An asn1crypto.keys.PrivateKeyInfo object :raises: ValueError - when any of the parameters contain an invalid value :return: An asn1crypto.keys.ECPointBitString object """ if not isinstance(private_key, PrivateKeyInfo): raise TypeError(pretty_message( ''' private_key must be an instance of the asn1crypto.keys.PrivateKeyInfo class, not %s ''', type_name(private_key) )) curve_type, details = private_key.curve if curve_type == 'implicit_ca': raise ValueError(pretty_message( ''' Unable to compute public key for EC key using Implicit CA parameters ''' )) if curve_type == 'specified': raise ValueError(pretty_message( ''' Unable to compute public key for EC key over a specified field ''' )) elif curve_type == 'named': if details not in set(['secp256r1', 'secp384r1', 'secp521r1']): raise ValueError(pretty_message( ''' Named curve must be one of "secp256r1", "secp384r1", "secp521r1", not %s ''', repr(details) )) base_point = { 'secp256r1': SECP256R1_BASE_POINT, 'secp384r1': SECP384R1_BASE_POINT, 'secp521r1': SECP521R1_BASE_POINT, }[details] public_point = base_point * private_key['private_key'].parsed['private_key'].native return ECPointBitString.from_coords(public_point.x, public_point.y) def ec_public_key_info(public_key_point, curve): """ Constructs the PublicKeyInfo for an ECPointBitString :param private_key: An asn1crypto.keys.ECPointBitString object :param curve: A unicode string of the curve name - one of secp256r1, secp384r1 or secp521r1 :raises: ValueError - when any of the parameters contain an invalid value :return: An asn1crypto.keys.PublicKeyInfo object """ if curve not in set(['secp256r1', 'secp384r1', 'secp521r1']): raise ValueError(pretty_message( ''' curve must be one of "secp256r1", "secp384r1", "secp521r1", not %s ''', repr(curve) )) return PublicKeyInfo({ 'algorithm': PublicKeyAlgorithm({ 'algorithm': 'ec', 'parameters': ECDomainParameters( name='named', value=curve ) }), 'public_key': public_key_point, }) def ecdsa_sign(private_key, data, hash_algorithm): """ Generates an ECDSA signature in pure Python (thus slow) :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "sha1", "sha256", "sha384" or "sha512" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if not hasattr(private_key, 'asn1') or not isinstance(private_key.asn1, PrivateKeyInfo): raise TypeError(pretty_message( ''' private_key must be an instance of the oscrypto.asymmetric.PrivateKey class, not %s ''', type_name(private_key) )) curve_name = private_key.curve if curve_name not in set(['secp256r1', 'secp384r1', 'secp521r1']): raise ValueError(pretty_message( ''' private_key does not use one of the named curves secp256r1, secp384r1 or secp521r1 ''' )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if hash_algorithm not in set(['sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) hash_func = getattr(hashlib, hash_algorithm) ec_private_key = private_key.asn1['private_key'].parsed private_key_bytes = ec_private_key['private_key'].contents private_key_int = ec_private_key['private_key'].native curve_num_bytes = CURVE_BYTES[curve_name] curve_base_point = { 'secp256r1': SECP256R1_BASE_POINT, 'secp384r1': SECP384R1_BASE_POINT, 'secp521r1': SECP521R1_BASE_POINT, }[curve_name] n = curve_base_point.order # RFC 6979 section 3.2 # a. digest = hash_func(data).digest() hash_length = len(digest) h = int_from_bytes(digest, signed=False) % n # b. V = b'\x01' * hash_length # c. K = b'\x00' * hash_length # d. K = hmac.new(K, V + b'\x00' + private_key_bytes + digest, hash_func).digest() # e. V = hmac.new(K, V, hash_func).digest() # f. K = hmac.new(K, V + b'\x01' + private_key_bytes + digest, hash_func).digest() # g. V = hmac.new(K, V, hash_func).digest() # h. r = 0 s = 0 while True: # h. 1 T = b'' # h. 2 while len(T) < curve_num_bytes: V = hmac.new(K, V, hash_func).digest() T += V # h. 3 k = int_from_bytes(T[0:curve_num_bytes], signed=False) if k == 0 or k >= n: continue # Calculate the signature in the loop in case we need a new k r = (curve_base_point * k).x % n if r == 0: continue s = (inverse_mod(k, n) * (h + (private_key_int * r) % n)) % n if s == 0: continue break return DSASignature({'r': r, 's': s}).dump() def ecdsa_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies an ECDSA signature in pure Python (thus slow) :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ has_asn1 = hasattr(certificate_or_public_key, 'asn1') if not has_asn1 or not isinstance(certificate_or_public_key.asn1, (PublicKeyInfo, Certificate)): raise TypeError(pretty_message( ''' certificate_or_public_key must be an instance of the oscrypto.asymmetric.PublicKey or oscrypto.asymmetric.Certificate classes, not %s ''', type_name(certificate_or_public_key) )) curve_name = certificate_or_public_key.curve if curve_name not in set(['secp256r1', 'secp384r1', 'secp521r1']): raise ValueError(pretty_message( ''' certificate_or_public_key does not use one of the named curves secp256r1, secp384r1 or secp521r1 ''' )) if not isinstance(signature, byte_cls): raise TypeError(pretty_message( ''' signature must be a byte string, not %s ''', type_name(signature) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if hash_algorithm not in set(['sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) asn1 = certificate_or_public_key.asn1 if isinstance(asn1, Certificate): asn1 = asn1.public_key curve_base_point = { 'secp256r1': SECP256R1_BASE_POINT, 'secp384r1': SECP384R1_BASE_POINT, 'secp521r1': SECP521R1_BASE_POINT, }[curve_name] x, y = asn1['public_key'].to_coords() n = curve_base_point.order # Validates that the point is valid public_key_point = PrimePoint(curve_base_point.curve, x, y, n) try: signature = DSASignature.load(signature) r = signature['r'].native s = signature['s'].native except (ValueError): raise SignatureError('Signature is invalid') invalid = 0 # Check r is valid invalid |= r < 1 invalid |= r >= n # Check s is valid invalid |= s < 1 invalid |= s >= n if invalid: raise SignatureError('Signature is invalid') hash_func = getattr(hashlib, hash_algorithm) digest = hash_func(data).digest() z = int_from_bytes(digest, signed=False) % n w = inverse_mod(s, n) u1 = (z * w) % n u2 = (r * w) % n hash_point = (curve_base_point * u1) + (public_key_point * u2) if r != (hash_point.x % n): raise SignatureError('Signature is invalid') """ Classes and objects to represent prime-field elliptic curves and points on them. Exports the following items: - PrimeCurve() - PrimePoint() - SECP192R1_CURVE - SECP192R1_BASE_POINT - SECP224R1_CURVE - SECP224R1_BASE_POINT - SECP256R1_CURVE - SECP256R1_BASE_POINT - SECP384R1_CURVE - SECP384R1_BASE_POINT - SECP521R1_CURVE - SECP521R1_BASE_POINT The curve constants are all PrimeCurve() objects and the base point constants are all PrimePoint() objects. Some of the following source code is derived from http://webpages.charter.net/curryfans/peter/downloads.html, but has been heavily modified to fit into this projects lint settings. The original project license is listed below: Copyright (c) 2014 Peter Pearson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ def inverse_mod(a, p): """ Compute the modular inverse of a (mod p) :param a: An integer :param p: An integer :return: An integer """ if a < 0 or p <= a: a = a % p # From Ferguson and Schneier, roughly: c, d = a, p uc, vc, ud, vd = 1, 0, 0, 1 while c != 0: q, c, d = divmod(d, c) + (c,) uc, vc, ud, vd = ud - q * uc, vd - q * vc, uc, vc # At this point, d is the GCD, and ud*a+vd*p = d. # If d == 1, this means that ud is a inverse. assert d == 1 if ud > 0: return ud else: return ud + p class PrimeCurve(): """ Elliptic curve over a prime field. Characteristic two field curves are not supported. """ def __init__(self, p, a, b): """ The curve of points satisfying y^2 = x^3 + a*x + b (mod p) :param p: The prime number as an integer :param a: The component a as an integer :param b: The component b as an integer """ self.p = p self.a = a self.b = b def contains(self, point): """ :param point: A Point object :return: Boolean if the point is on this curve """ y2 = point.y * point.y x3 = point.x * point.x * point.x return (y2 - (x3 + self.a * point.x + self.b)) % self.p == 0 class PrimePoint(): """ A point on a prime-field elliptic curve """ def __init__(self, curve, x, y, order=None): """ :param curve: A PrimeCurve object :param x: The x coordinate of the point as an integer :param y: The y coordinate of the point as an integer :param order: The order of the point, as an integer - optional """ self.curve = curve self.x = x self.y = y self.order = order # self.curve is allowed to be None only for INFINITY: if self.curve: if not self.curve.contains(self): raise ValueError('Invalid EC point') if self.order: if self * self.order != INFINITY: raise ValueError('Invalid EC point') def __cmp__(self, other): """ :param other: A PrimePoint object :return: 0 if identical, 1 otherwise """ if self.curve == other.curve and self.x == other.x and self.y == other.y: return 0 else: return 1 def __add__(self, other): """ :param other: A PrimePoint object :return: A PrimePoint object """ # X9.62 B.3: if other == INFINITY: return self if self == INFINITY: return other assert self.curve == other.curve if self.x == other.x: if (self.y + other.y) % self.curve.p == 0: return INFINITY else: return self.double() p = self.curve.p l_ = ((other.y - self.y) * inverse_mod(other.x - self.x, p)) % p x3 = (l_ * l_ - self.x - other.x) % p y3 = (l_ * (self.x - x3) - self.y) % p return PrimePoint(self.curve, x3, y3) def __mul__(self, other): """ :param other: An integer to multiple the Point by :return: A PrimePoint object """ def leftmost_bit(x): assert x > 0 result = 1 while result <= x: result = 2 * result return result // 2 e = other if self.order: e = e % self.order if e == 0: return INFINITY if self == INFINITY: return INFINITY assert e > 0 # From X9.62 D.3.2: e3 = 3 * e negative_self = PrimePoint(self.curve, self.x, -self.y, self.order) i = leftmost_bit(e3) // 2 result = self # print "Multiplying %s by %d (e3 = %d):" % ( self, other, e3 ) while i > 1: result = result.double() if (e3 & i) != 0 and (e & i) == 0: result = result + self if (e3 & i) == 0 and (e & i) != 0: result = result + negative_self # print ". . . i = %d, result = %s" % ( i, result ) i = i // 2 return result def __rmul__(self, other): """ :param other: An integer to multiple the Point by :return: A PrimePoint object """ return self * other def double(self): """ :return: A PrimePoint object that is twice this point """ # X9.62 B.3: p = self.curve.p a = self.curve.a l_ = ((3 * self.x * self.x + a) * inverse_mod(2 * self.y, p)) % p x3 = (l_ * l_ - 2 * self.x) % p y3 = (l_ * (self.x - x3) - self.y) % p return PrimePoint(self.curve, x3, y3) # This one point is the Point At Infinity for all purposes: INFINITY = PrimePoint(None, None, None) # NIST Curve P-192: SECP192R1_CURVE = PrimeCurve( 6277101735386680763835789423207666416083908700390324961279, -3, 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 ) SECP192R1_BASE_POINT = PrimePoint( SECP192R1_CURVE, 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012, 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811, 6277101735386680763835789423176059013767194773182842284081 ) # NIST Curve P-224: SECP224R1_CURVE = PrimeCurve( 26959946667150639794667015087019630673557916260026308143510066298881, -3, 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4 ) SECP224R1_BASE_POINT = PrimePoint( SECP224R1_CURVE, 0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21, 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34, 26959946667150639794667015087019625940457807714424391721682722368061 ) # NIST Curve P-256: SECP256R1_CURVE = PrimeCurve( 115792089210356248762697446949407573530086143415290314195533631308867097853951, -3, 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b ) SECP256R1_BASE_POINT = PrimePoint( SECP256R1_CURVE, 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5, 115792089210356248762697446949407573529996955224135760342422259061068512044369 ) # NIST Curve P-384: SECP384R1_CURVE = PrimeCurve( 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319, # noqa -3, 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef ) SECP384R1_BASE_POINT = PrimePoint( SECP384R1_CURVE, 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7, 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f, 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643 ) # NIST Curve P-521: SECP521R1_CURVE = PrimeCurve( 6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151, # noqa -3, 0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00 # noqa ) SECP521R1_BASE_POINT = PrimePoint( SECP521R1_CURVE, 0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66, # noqa 0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650, # noqa 6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449 # noqa ) oscrypto-1.3.0/oscrypto/_errors.py000066400000000000000000000017751421476274700173340ustar00rootroot00000000000000# coding: utf-8 """ Helper for formatting exception messages. Exports the following items: - pretty_message() """ from __future__ import unicode_literals, division, absolute_import, print_function import re import textwrap __all__ = [ 'pretty_message', ] def pretty_message(string, *params): """ Takes a multi-line string and does the following: - dedents - converts newlines with text before and after into a single line - strips leading and trailing whitespace :param string: The string to format :param *params: Params to interpolate into the string :return: The formatted string """ output = textwrap.dedent(string) # Unwrap lines, taking into account bulleted lists, ordered lists and # underlines consisting of = signs if output.find('\n') != -1: output = re.sub('(?<=\\S)\n(?=[^ \n\t\\d\\*\\-=])', ' ', output) if params: output = output % params output = output.strip() return output oscrypto-1.3.0/oscrypto/_ffi.py000066400000000000000000000275061421476274700165640ustar00rootroot00000000000000# coding: utf-8 """ Exceptions and compatibility shims for consistently using ctypes and cffi """ from __future__ import unicode_literals, division, absolute_import, print_function import sys import platform from ctypes.util import find_library from . import ffi from ._types import str_cls, byte_cls, int_types, bytes_to_list __all__ = [ 'array_from_pointer', 'array_set', 'buffer_from_bytes', 'buffer_from_unicode', 'buffer_pointer', 'byte_array', 'byte_string_from_buffer', 'bytes_from_buffer', 'callback', 'cast', 'deref', 'errno', 'FFIEngineError', 'get_library', 'is_null', 'native', 'new', 'null', 'pointer_set', 'ref', 'register_ffi', 'sizeof', 'struct', 'struct_bytes', 'struct_from_buffer', 'unwrap', 'write_to_buffer', ] if ffi() == 'cffi': from cffi import FFI _ffi_registry = {} ffi = FFI() def register_ffi(library, ffi_obj): _ffi_registry[library] = ffi_obj def _get_ffi(library): if library in _ffi_registry: return _ffi_registry[library] return ffi def buffer_from_bytes(initializer): if sys.platform == 'win32': return ffi.new('unsigned char[]', initializer) return ffi.new('char[]', initializer) def buffer_from_unicode(initializer): return ffi.new('wchar_t []', initializer) def write_to_buffer(buffer, data, offset=0): buffer[offset:offset + len(data)] = data def buffer_pointer(buffer): return ffi.new('char *[]', [buffer]) def cast(library, type_, value): ffi_obj = _get_ffi(library) return ffi_obj.cast(type_, value) def sizeof(library, value): ffi_obj = _get_ffi(library) return ffi_obj.sizeof(value) def bytes_from_buffer(buffer, maxlen=None): if maxlen is not None: return ffi.buffer(buffer, maxlen)[:] return ffi.buffer(buffer)[:] def byte_string_from_buffer(buffer): return ffi.string(buffer) def byte_array(byte_string): return byte_string def pointer_set(pointer_, value): pointer_[0] = value def array_set(array, value): for index, val in enumerate(value): array[index] = val def null(): return ffi.NULL def is_null(point): if point is None: return True if point == ffi.NULL: return True if ffi.getctype(ffi.typeof(point)) == 'void *': return False if point[0] == ffi.NULL: return True return False def errno(): return ffi.errno def new(library, type_, value=None): ffi_obj = _get_ffi(library) params = [] if value is not None: params.append(value) if type_ in set(['BCRYPT_KEY_HANDLE', 'BCRYPT_ALG_HANDLE']): return ffi_obj.cast(type_, 0) return ffi_obj.new(type_, *params) def ref(value, offset=0): return value + offset def native(type_, value): if type_ == str_cls: return ffi.string(value) if type_ == byte_cls: return ffi.buffer(value)[:] return type_(value) def deref(point): return point[0] def unwrap(point): return point[0] def struct(library, name): ffi_obj = _get_ffi(library) return ffi_obj.new('%s *' % name) def struct_bytes(struct_): return ffi.buffer(struct_)[:] def struct_from_buffer(library, name, buffer): ffi_obj = _get_ffi(library) new_struct_pointer = ffi_obj.new('%s *' % name) new_struct = new_struct_pointer[0] struct_size = sizeof(library, new_struct) struct_buffer = ffi_obj.buffer(new_struct_pointer) struct_buffer[:] = ffi_obj.buffer(buffer, struct_size)[:] return new_struct_pointer def array_from_pointer(library, name, point, size): ffi_obj = _get_ffi(library) array = ffi_obj.cast('%s[%s]' % (name, size), point) total_bytes = ffi_obj.sizeof(array) if total_bytes == 0: return [] output = [] string_types = { 'LPSTR': True, 'LPCSTR': True, 'LPWSTR': True, 'LPCWSTR': True, 'char *': True, 'wchar_t *': True, } string_type = name in string_types for i in range(0, size): value = array[i] if string_type: value = ffi_obj.string(value) output.append(value) return output def callback(library, signature_name, func): ffi_obj = _get_ffi(library) return ffi_obj.callback(signature_name, func) engine = 'cffi' else: import ctypes from ctypes import pointer, c_int, c_char_p, c_uint, c_void_p, c_wchar_p _pointer_int_types = int_types + (c_char_p, ctypes.POINTER(ctypes.c_byte)) _pointer_types = { 'void *': True, 'wchar_t *': True, 'char *': True, 'char **': True, } _type_map = { 'void *': c_void_p, 'wchar_t *': c_wchar_p, 'char *': c_char_p, 'char **': ctypes.POINTER(c_char_p), 'int': c_int, 'unsigned int': c_uint, 'size_t': ctypes.c_size_t, 'uint32_t': ctypes.c_uint32, } if sys.platform == 'win32': from ctypes import wintypes _pointer_types.update({ 'LPSTR': True, 'LPWSTR': True, 'LPCSTR': True, 'LPCWSTR': True, }) _type_map.update({ 'BYTE': ctypes.c_byte, 'LPSTR': c_char_p, 'LPWSTR': c_wchar_p, 'LPCSTR': c_char_p, 'LPCWSTR': c_wchar_p, 'ULONG': wintypes.ULONG, 'DWORD': wintypes.DWORD, 'char *': ctypes.POINTER(ctypes.c_byte), 'char **': ctypes.POINTER(ctypes.POINTER(ctypes.c_byte)), }) def _type_info(library, type_): is_double_pointer = type_[-3:] == ' **' if is_double_pointer: type_ = type_[:-1] is_pointer = type_[-2:] == ' *' and type_ not in _pointer_types if is_pointer: type_ = type_[:-2] is_array = type_.find('[') != -1 if is_array: is_array = type_[type_.find('[') + 1:type_.find(']')] if is_array == '': is_array = True else: is_array = int(is_array) type_ = type_[0:type_.find('[')] if type_ in _type_map: type_ = _type_map[type_] else: type_ = getattr(library, type_) if is_double_pointer: type_ = ctypes.POINTER(type_) return (is_pointer, is_array, type_) def register_ffi(library, ffi_obj): pass def buffer_from_bytes(initializer): return ctypes.create_string_buffer(initializer) def buffer_from_unicode(initializer): return ctypes.create_unicode_buffer(initializer) def write_to_buffer(buffer, data, offset=0): if isinstance(buffer, ctypes.POINTER(ctypes.c_byte)): ctypes.memmove(buffer, data, len(data)) return if offset == 0: buffer.value = data else: buffer.value = buffer.raw[0:offset] + data def buffer_pointer(buffer): return pointer(ctypes.cast(buffer, c_char_p)) def cast(library, type_, value): is_pointer, is_array, type_ = _type_info(library, type_) if is_pointer: type_ = ctypes.POINTER(type_) elif is_array: type_ = type_ * is_array return ctypes.cast(value, type_) def sizeof(library, value): return ctypes.sizeof(value) def bytes_from_buffer(buffer, maxlen=None): if isinstance(buffer, _pointer_int_types): return ctypes.string_at(buffer, maxlen) if maxlen is not None: return buffer.raw[0:maxlen] return buffer.raw def byte_string_from_buffer(buffer): return buffer.value def byte_array(byte_string): return (ctypes.c_byte * len(byte_string))(*bytes_to_list(byte_string)) def pointer_set(pointer_, value): pointer_.contents.value = value def array_set(array, value): for index, val in enumerate(value): array[index] = val def null(): return None def is_null(point): return not bool(point) def errno(): return ctypes.get_errno() def new(library, type_, value=None): is_pointer, is_array, type_ = _type_info(library, type_) if is_array: if is_array is True: type_ = type_ * value value = None else: type_ = type_ * is_array params = [] if value is not None: params.append(value) output = type_(*params) if is_pointer: output = pointer(output) return output def ref(value, offset=0): if offset == 0: return ctypes.byref(value) return ctypes.cast(ctypes.addressof(value) + offset, ctypes.POINTER(ctypes.c_byte)) def native(type_, value): if isinstance(value, type_): return value if sys.version_info < (3,) and type_ == int and isinstance(value, int_types): return value if isinstance(value, ctypes.Array) and value._type_ == ctypes.c_byte: return ctypes.string_at(ctypes.addressof(value), value._length_) return type_(value.value) def deref(point): return point[0] def unwrap(point): return point.contents def struct(library, name): return pointer(getattr(library, name)()) def struct_bytes(struct_): return ctypes.string_at(struct_, ctypes.sizeof(struct_.contents)) def struct_from_buffer(library, type_, buffer): class_ = getattr(library, type_) value = class_() ctypes.memmove(ctypes.addressof(value), buffer, ctypes.sizeof(class_)) return ctypes.pointer(value) def array_from_pointer(library, type_, point, size): _, _, type_ = _type_info(library, type_) array = ctypes.cast(point, ctypes.POINTER(type_)) output = [] for i in range(0, size): output.append(array[i]) return output def callback(library, signature_type, func): return getattr(library, signature_type)(func) engine = 'ctypes' def get_library(name, dylib_name, version): """ Retrieve the C library path with special handling for Mac :param name: A unicode string of the library to search the system for :param dylib_name: Mac only - a unicode string of the unversioned dylib name :param version: Mac only - a unicode string of the dylib version to use. Used on macOS 10.15+ when the unversioned dylib is found, since unversioned OpenSSL/LibreSSL are just placeholders, and a versioned dylib must be imported. Used on macOS 10.16+ when find_library() doesn't return a result, due to system dylibs not being present on the filesystem any longer. :return: A unicode string of the path to the library """ library = find_library(name) if sys.platform == 'darwin': unversioned = '/usr/lib/%s' % dylib_name versioned = unversioned.replace('.dylib', '.%s.dylib' % version) mac_ver = tuple(map(int, platform.mac_ver()[0].split('.'))) if not library and mac_ver >= (10, 16): # On macOS 10.16+, find_library doesn't work, so we set a static path library = versioned elif mac_ver >= (10, 15) and library == unversioned: # On macOS 10.15+, we want to strongly version since unversioned libcrypto has a non-stable ABI library = versioned return library class FFIEngineError(Exception): """ An exception when trying to instantiate ctypes or cffi """ pass oscrypto-1.3.0/oscrypto/_int.py000066400000000000000000000012321421476274700165760ustar00rootroot00000000000000# coding: utf-8 """ Function to fill ensure integers converted to a byte string are a specific width. Exports the following items: - fill_width() """ from __future__ import unicode_literals, division, absolute_import, print_function __all__ = [ 'fill_width', ] def fill_width(bytes_, width): """ Ensure a byte string representing a positive integer is a specific width (in bytes) :param bytes_: The integer byte string :param width: The desired width as an integer :return: A byte string of the width specified """ while len(bytes_) < width: bytes_ = b'\x00' + bytes_ return bytes_ oscrypto-1.3.0/oscrypto/_linux_bsd/000077500000000000000000000000001421476274700174235ustar00rootroot00000000000000oscrypto-1.3.0/oscrypto/_linux_bsd/__init__.py000066400000000000000000000000001421476274700215220ustar00rootroot00000000000000oscrypto-1.3.0/oscrypto/_linux_bsd/trust_list.py000066400000000000000000000076711421476274700222240ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os from .._asn1 import Certificate, TrustedCertificate, unarmor from .._errors import pretty_message __all__ = [ 'extract_from_system', 'system_path', ] def system_path(): """ Tries to find a CA certs bundle in common locations :raises: OSError - when no valid CA certs bundle was found on the filesystem :return: The full filesystem path to a CA certs bundle file """ ca_path = None # Common CA cert paths paths = [ '/usr/lib/ssl/certs/ca-certificates.crt', '/etc/ssl/certs/ca-certificates.crt', '/etc/ssl/certs/ca-bundle.crt', '/etc/pki/tls/certs/ca-bundle.crt', '/etc/ssl/ca-bundle.pem', '/usr/local/share/certs/ca-root-nss.crt', '/etc/ssl/cert.pem' ] # First try SSL_CERT_FILE if 'SSL_CERT_FILE' in os.environ: paths.insert(0, os.environ['SSL_CERT_FILE']) for path in paths: if os.path.exists(path) and os.path.getsize(path) > 0: ca_path = path break if not ca_path: raise OSError(pretty_message( ''' Unable to find a CA certs bundle in common locations - try setting the SSL_CERT_FILE environmental variable ''' )) return ca_path def extract_from_system(cert_callback=None, callback_only_on_failure=False): """ Extracts trusted CA certs from the system CA cert bundle :param cert_callback: A callback that is called once for each certificate in the trust store. It should accept two parameters: an asn1crypto.x509.Certificate object, and a reason. The reason will be None if the certificate is being exported, otherwise it will be a unicode string of the reason it won't. :param callback_only_on_failure: A boolean - if the callback should only be called when a certificate is not exported. :return: A list of 3-element tuples: - 0: a byte string of a DER-encoded certificate - 1: a set of unicode strings that are OIDs of purposes to trust the certificate for - 2: a set of unicode strings that are OIDs of purposes to reject the certificate for """ all_purposes = '2.5.29.37.0' ca_path = system_path() output = [] with open(ca_path, 'rb') as f: for armor_type, _, cert_bytes in unarmor(f.read(), multiple=True): # Without more info, a certificate is trusted for all purposes if armor_type == 'CERTIFICATE': if cert_callback: cert_callback(Certificate.load(cert_bytes), None) output.append((cert_bytes, set(), set())) # The OpenSSL TRUSTED CERTIFICATE construct adds OIDs for trusted # and rejected purposes, so we extract that info. elif armor_type == 'TRUSTED CERTIFICATE': cert, aux = TrustedCertificate.load(cert_bytes) reject_all = False trust_oids = set() reject_oids = set() for purpose in aux['trust']: if purpose.dotted == all_purposes: trust_oids = set([purpose.dotted]) break trust_oids.add(purpose.dotted) for purpose in aux['reject']: if purpose.dotted == all_purposes: reject_all = True break reject_oids.add(purpose.dotted) if reject_all: if cert_callback: cert_callback(cert, 'explicitly distrusted') continue if cert_callback and not callback_only_on_failure: cert_callback(cert, None) output.append((cert.dump(), trust_oids, reject_oids)) return output oscrypto-1.3.0/oscrypto/_mac/000077500000000000000000000000001421476274700161745ustar00rootroot00000000000000oscrypto-1.3.0/oscrypto/_mac/__init__.py000066400000000000000000000000001421476274700202730ustar00rootroot00000000000000oscrypto-1.3.0/oscrypto/_mac/_common_crypto.py000066400000000000000000000007561421476274700216050ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .. import ffi if ffi() == 'cffi': from ._common_crypto_cffi import CommonCrypto else: from ._common_crypto_ctypes import CommonCrypto __all__ = [ 'CommonCrypto', 'CommonCryptoConst', ] class CommonCryptoConst(): kCCPBKDF2 = 2 kCCPRFHmacAlgSHA1 = 1 kCCPRFHmacAlgSHA224 = 2 kCCPRFHmacAlgSHA256 = 3 kCCPRFHmacAlgSHA384 = 4 kCCPRFHmacAlgSHA512 = 5 oscrypto-1.3.0/oscrypto/_mac/_common_crypto_cffi.py000066400000000000000000000013411421476274700225630ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .._ffi import register_ffi from cffi import FFI __all__ = [ 'CommonCrypto', ] ffi = FFI() ffi.cdef(""" typedef uint32_t CCPBKDFAlgorithm; typedef uint32_t CCPseudoRandomAlgorithm; typedef unsigned int uint; int CCKeyDerivationPBKDF(CCPBKDFAlgorithm algorithm, const char *password, size_t passwordLen, const char *salt, size_t saltLen, CCPseudoRandomAlgorithm prf, uint rounds, char *derivedKey, size_t derivedKeyLen); """) common_crypto_path = '/usr/lib/system/libcommonCrypto.dylib' CommonCrypto = ffi.dlopen(common_crypto_path) register_ffi(CommonCrypto, ffi) oscrypto-1.3.0/oscrypto/_mac/_common_crypto_ctypes.py000066400000000000000000000013131421476274700231620ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from ctypes import CDLL, c_uint32, c_char_p, c_size_t, c_int, c_uint from .._ffi import FFIEngineError __all__ = [ 'CommonCrypto', ] common_crypto_path = '/usr/lib/system/libcommonCrypto.dylib' CommonCrypto = CDLL(common_crypto_path, use_errno=True) try: CommonCrypto.CCKeyDerivationPBKDF.argtypes = [ c_uint32, c_char_p, c_size_t, c_char_p, c_size_t, c_uint32, c_uint, c_char_p, c_size_t ] CommonCrypto.CCKeyDerivationPBKDF.restype = c_int except (AttributeError): raise FFIEngineError('Error initializing ctypes') oscrypto-1.3.0/oscrypto/_mac/_core_foundation.py000066400000000000000000000237151421476274700220730ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .. import ffi from .._ffi import is_null, unwrap if ffi() == 'cffi': from ._core_foundation_cffi import CoreFoundation, CFHelpers else: from ._core_foundation_ctypes import CoreFoundation, CFHelpers _all__ = [ 'CFHelpers', 'CoreFoundation', 'handle_cf_error', ] def handle_cf_error(error_pointer): """ Checks a CFErrorRef and throws an exception if there is an error to report :param error_pointer: A CFErrorRef :raises: OSError - when the CFErrorRef contains an error """ if is_null(error_pointer): return error = unwrap(error_pointer) if is_null(error): return cf_string_domain = CoreFoundation.CFErrorGetDomain(error) domain = CFHelpers.cf_string_to_unicode(cf_string_domain) CoreFoundation.CFRelease(cf_string_domain) num = CoreFoundation.CFErrorGetCode(error) cf_string_ref = CoreFoundation.CFErrorCopyDescription(error) output = CFHelpers.cf_string_to_unicode(cf_string_ref) CoreFoundation.CFRelease(cf_string_ref) if output is None: if domain == 'NSOSStatusErrorDomain': code_map = { -2147416010: 'ACL add failed', -2147416025: 'ACL base certs not supported', -2147416019: 'ACL challenge callback failed', -2147416015: 'ACL change failed', -2147416012: 'ACL delete failed', -2147416017: 'ACL entry tag not found', -2147416011: 'ACL replace failed', -2147416021: 'ACL subject type not supported', -2147415789: 'Algid mismatch', -2147415726: 'Already logged in', -2147415040: 'Apple add application ACL subject', -2147415036: 'Apple invalid key end date', -2147415037: 'Apple invalid key start date', -2147415039: 'Apple public key incomplete', -2147415038: 'Apple signature mismatch', -2147415034: 'Apple SSLv2 rollback', -2147415802: 'Attach handle busy', -2147415731: 'Block size mismatch', -2147415722: 'Crypto data callback failed', -2147415804: 'Device error', -2147415835: 'Device failed', -2147415803: 'Device memory error', -2147415836: 'Device reset', -2147415728: 'Device verify failed', -2147416054: 'Function failed', -2147416057: 'Function not implemented', -2147415807: 'Input length error', -2147415837: 'Insufficient client identification', -2147416063: 'Internal error', -2147416027: 'Invalid access credentials', -2147416026: 'Invalid ACL base certs', -2147416020: 'Invalid ACL challenge callback', -2147416016: 'Invalid ACL edit mode', -2147416018: 'Invalid ACL entry tag', -2147416022: 'Invalid ACL subject value', -2147415759: 'Invalid algorithm', -2147415678: 'Invalid attr access credentials', -2147415704: 'Invalid attr alg params', -2147415686: 'Invalid attr base', -2147415738: 'Invalid attr block size', -2147415680: 'Invalid attr dl db handle', -2147415696: 'Invalid attr effective bits', -2147415692: 'Invalid attr end date', -2147415752: 'Invalid attr init vector', -2147415682: 'Invalid attr iteration count', -2147415754: 'Invalid attr key', -2147415740: 'Invalid attr key length', -2147415700: 'Invalid attr key type', -2147415702: 'Invalid attr label', -2147415698: 'Invalid attr mode', -2147415708: 'Invalid attr output size', -2147415748: 'Invalid attr padding', -2147415742: 'Invalid attr passphrase', -2147415688: 'Invalid attr prime', -2147415674: 'Invalid attr private key format', -2147415676: 'Invalid attr public key format', -2147415746: 'Invalid attr random', -2147415706: 'Invalid attr rounds', -2147415750: 'Invalid attr salt', -2147415744: 'Invalid attr seed', -2147415694: 'Invalid attr start date', -2147415684: 'Invalid attr subprime', -2147415672: 'Invalid attr symmetric key format', -2147415690: 'Invalid attr version', -2147415670: 'Invalid attr wrapped key format', -2147415760: 'Invalid context', -2147416000: 'Invalid context handle', -2147415976: 'Invalid crypto data', -2147415994: 'Invalid data', -2147415768: 'Invalid data count', -2147415723: 'Invalid digest algorithm', -2147416059: 'Invalid input pointer', -2147415766: 'Invalid input vector', -2147415792: 'Invalid key', -2147415780: 'Invalid keyattr mask', -2147415782: 'Invalid keyusage mask', -2147415790: 'Invalid key class', -2147415776: 'Invalid key format', -2147415778: 'Invalid key label', -2147415783: 'Invalid key pointer', -2147415791: 'Invalid key reference', -2147415727: 'Invalid login name', -2147416014: 'Invalid new ACL entry', -2147416013: 'Invalid new ACL owner', -2147416058: 'Invalid output pointer', -2147415765: 'Invalid output vector', -2147415978: 'Invalid passthrough id', -2147416060: 'Invalid pointer', -2147416024: 'Invalid sample value', -2147415733: 'Invalid signature', -2147415787: 'Key blob type incorrect', -2147415786: 'Key header inconsistent', -2147415724: 'Key label already exists', -2147415788: 'Key usage incorrect', -2147416061: 'Mds error', -2147416062: 'Memory error', -2147415677: 'Missing attr access credentials', -2147415703: 'Missing attr alg params', -2147415685: 'Missing attr base', -2147415737: 'Missing attr block size', -2147415679: 'Missing attr dl db handle', -2147415695: 'Missing attr effective bits', -2147415691: 'Missing attr end date', -2147415751: 'Missing attr init vector', -2147415681: 'Missing attr iteration count', -2147415753: 'Missing attr key', -2147415739: 'Missing attr key length', -2147415699: 'Missing attr key type', -2147415701: 'Missing attr label', -2147415697: 'Missing attr mode', -2147415707: 'Missing attr output size', -2147415747: 'Missing attr padding', -2147415741: 'Missing attr passphrase', -2147415687: 'Missing attr prime', -2147415673: 'Missing attr private key format', -2147415675: 'Missing attr public key format', -2147415745: 'Missing attr random', -2147415705: 'Missing attr rounds', -2147415749: 'Missing attr salt', -2147415743: 'Missing attr seed', -2147415693: 'Missing attr start date', -2147415683: 'Missing attr subprime', -2147415671: 'Missing attr symmetric key format', -2147415689: 'Missing attr version', -2147415669: 'Missing attr wrapped key format', -2147415801: 'Not logged in', -2147415840: 'No user interaction', -2147416029: 'Object ACL not supported', -2147416028: 'Object ACL required', -2147416030: 'Object manip auth denied', -2147416031: 'Object use auth denied', -2147416032: 'Operation auth denied', -2147416055: 'OS access denied', -2147415806: 'Output length error', -2147415725: 'Private key already exists', -2147415730: 'Private key not found', -2147415989: 'Privilege not granted', -2147415805: 'Privilege not supported', -2147415729: 'Public key inconsistent', -2147415732: 'Query size unknown', -2147416023: 'Sample value not supported', -2147416056: 'Self check failed', -2147415838: 'Service not available', -2147415736: 'Staged operation in progress', -2147415735: 'Staged operation not started', -2147415779: 'Unsupported keyattr mask', -2147415781: 'Unsupported keyusage mask', -2147415785: 'Unsupported key format', -2147415777: 'Unsupported key label', -2147415784: 'Unsupported key size', -2147415839: 'User canceled', -2147415767: 'Vector of bufs unsupported', -2147415734: 'Verify failed', } if num in code_map: output = code_map[num] if not output: output = '%s %s' % (domain, num) raise OSError(output) CFHelpers.register_native_mapping( CoreFoundation.CFStringGetTypeID(), CFHelpers.cf_string_to_unicode ) CFHelpers.register_native_mapping( CoreFoundation.CFNumberGetTypeID(), CFHelpers.cf_number_to_number ) CFHelpers.register_native_mapping( CoreFoundation.CFDataGetTypeID(), CFHelpers.cf_data_to_bytes ) CFHelpers.register_native_mapping( CoreFoundation.CFDictionaryGetTypeID(), CFHelpers.cf_dictionary_to_dict ) oscrypto-1.3.0/oscrypto/_mac/_core_foundation_cffi.py000066400000000000000000000252551421476274700230630ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .._ffi import ( buffer_from_bytes, byte_string_from_buffer, deref, is_null, new, register_ffi, ) from cffi import FFI __all__ = [ 'CFHelpers', 'CoreFoundation', ] ffi = FFI() ffi.cdef(""" typedef bool Boolean; typedef long CFIndex; typedef unsigned long CFStringEncoding; typedef unsigned long CFNumberType; typedef unsigned long CFTypeID; typedef void *CFTypeRef; typedef CFTypeRef CFArrayRef; typedef CFTypeRef CFDataRef; typedef CFTypeRef CFStringRef; typedef CFTypeRef CFNumberRef; typedef CFTypeRef CFBooleanRef; typedef CFTypeRef CFDictionaryRef; typedef CFTypeRef CFErrorRef; typedef CFTypeRef CFAllocatorRef; typedef struct { CFIndex version; void *retain; void *release; void *copyDescription; void *equal; void *hash; } CFDictionaryKeyCallBacks; typedef struct { CFIndex version; void *retain; void *release; void *copyDescription; void *equal; } CFDictionaryValueCallBacks; typedef struct { CFIndex version; void *retain; void *release; void *copyDescription; void *equal; } CFArrayCallBacks; CFIndex CFDataGetLength(CFDataRef theData); const char *CFDataGetBytePtr(CFDataRef theData); CFDataRef CFDataCreate(CFAllocatorRef allocator, const char *bytes, CFIndex length); CFDictionaryRef CFDictionaryCreate(CFAllocatorRef allocator, const void **keys, const void **values, CFIndex numValues, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks); CFIndex CFDictionaryGetCount(CFDictionaryRef theDict); const char *CFStringGetCStringPtr(CFStringRef theString, CFStringEncoding encoding); Boolean CFStringGetCString(CFStringRef theString, char *buffer, CFIndex bufferSize, CFStringEncoding encoding); CFStringRef CFStringCreateWithCString(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding); CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType theType, const void *valuePtr); CFStringRef CFCopyTypeIDDescription(CFTypeID type_id); void CFRelease(CFTypeRef cf); void CFRetain(CFTypeRef cf); CFStringRef CFErrorCopyDescription(CFErrorRef err); CFStringRef CFErrorGetDomain(CFErrorRef err); CFIndex CFErrorGetCode(CFErrorRef err); Boolean CFBooleanGetValue(CFBooleanRef boolean); CFTypeID CFDictionaryGetTypeID(void); CFTypeID CFNumberGetTypeID(void); CFTypeID CFStringGetTypeID(void); CFTypeID CFDataGetTypeID(void); CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFArrayCallBacks *callBacks); CFIndex CFArrayGetCount(CFArrayRef theArray); CFTypeRef CFArrayGetValueAtIndex(CFArrayRef theArray, CFIndex idx); CFNumberType CFNumberGetType(CFNumberRef number); Boolean CFNumberGetValue(CFNumberRef number, CFNumberType theType, void *valuePtr); CFIndex CFDictionaryGetKeysAndValues(CFDictionaryRef theDict, const void **keys, const void **values); CFTypeID CFGetTypeID(CFTypeRef cf); extern CFAllocatorRef kCFAllocatorDefault; extern CFArrayCallBacks kCFTypeArrayCallBacks; extern CFBooleanRef kCFBooleanTrue; extern CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks; extern CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks; """) core_foundation_path = '/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation' CoreFoundation = ffi.dlopen(core_foundation_path) register_ffi(CoreFoundation, ffi) kCFNumberCFIndexType = 14 kCFStringEncodingUTF8 = 0x08000100 class CFHelpers(): """ Namespace for core foundation helpers """ _native_map = {} @classmethod def register_native_mapping(cls, type_id, callback): """ Register a function to convert a core foundation data type into its equivalent in python :param type_id: The CFTypeId for the type :param callback: A callback to pass the CFType object to """ cls._native_map[int(type_id)] = callback @staticmethod def cf_number_to_number(value): """ Converts a CFNumber object to a python float or integer :param value: The CFNumber object :return: A python number (float or integer) """ type_ = CoreFoundation.CFNumberGetType(value) type_name_ = { 1: 'int8_t', # kCFNumberSInt8Type 2: 'in16_t', # kCFNumberSInt16Type 3: 'int32_t', # kCFNumberSInt32Type 4: 'int64_t', # kCFNumberSInt64Type 5: 'float', # kCFNumberFloat32Type 6: 'double', # kCFNumberFloat64Type 7: 'char', # kCFNumberCharType 8: 'short', # kCFNumberShortType 9: 'int', # kCFNumberIntType 10: 'long', # kCFNumberLongType 11: 'long long', # kCFNumberLongLongType 12: 'float', # kCFNumberFloatType 13: 'double', # kCFNumberDoubleType 14: 'long', # kCFNumberCFIndexType 15: 'int', # kCFNumberNSIntegerType 16: 'double', # kCFNumberCGFloatType }[type_] output = new(CoreFoundation, type_name_ + ' *') CoreFoundation.CFNumberGetValue(value, type_, output) return deref(output) @staticmethod def cf_dictionary_to_dict(dictionary): """ Converts a CFDictionary object into a python dictionary :param dictionary: The CFDictionary to convert :return: A python dict """ dict_length = CoreFoundation.CFDictionaryGetCount(dictionary) keys = new(CoreFoundation, 'CFTypeRef[%s]' % dict_length) values = new(CoreFoundation, 'CFTypeRef[%s]' % dict_length) CoreFoundation.CFDictionaryGetKeysAndValues( dictionary, keys, values ) output = {} for index in range(0, dict_length): output[CFHelpers.native(keys[index])] = CFHelpers.native(values[index]) return output @classmethod def native(cls, value): """ Converts a CF* object into its python equivalent :param value: The CF* object to convert :return: The native python object """ type_id = CoreFoundation.CFGetTypeID(value) if type_id in cls._native_map: return cls._native_map[type_id](value) else: return value @staticmethod def cf_string_to_unicode(value): """ Creates a python unicode string from a CFString object :param value: The CFString to convert :return: A python unicode string """ string_ptr = CoreFoundation.CFStringGetCStringPtr( value, kCFStringEncodingUTF8 ) string = None if is_null(string_ptr) else ffi.string(string_ptr) if string is None: buffer = buffer_from_bytes(1024) result = CoreFoundation.CFStringGetCString( value, buffer, 1024, kCFStringEncodingUTF8 ) if not result: raise OSError('Error copying C string from CFStringRef') string = byte_string_from_buffer(buffer) if string is not None: string = string.decode('utf-8') return string @staticmethod def cf_string_from_unicode(string): """ Creates a CFStringRef object from a unicode string :param string: The unicode string to create the CFString object from :return: A CFStringRef """ return CoreFoundation.CFStringCreateWithCString( CoreFoundation.kCFAllocatorDefault, string.encode('utf-8'), kCFStringEncodingUTF8 ) @staticmethod def cf_data_to_bytes(value): """ Extracts a bytestring from a CFData object :param value: A CFData object :return: A byte string """ start = CoreFoundation.CFDataGetBytePtr(value) num_bytes = CoreFoundation.CFDataGetLength(value) return ffi.buffer(start, num_bytes)[:] @staticmethod def cf_data_from_bytes(bytes_): """ Creates a CFDataRef object from a byte string :param bytes_: The data to create the CFData object from :return: A CFDataRef """ return CoreFoundation.CFDataCreate( CoreFoundation.kCFAllocatorDefault, bytes_, len(bytes_) ) @staticmethod def cf_dictionary_from_pairs(pairs): """ Creates a CFDictionaryRef object from a list of 2-element tuples representing the key and value. Each key should be a CFStringRef and each value some sort of CF* type. :param pairs: A list of 2-element tuples :return: A CFDictionaryRef """ length = len(pairs) keys = [] values = [] for pair in pairs: key, value = pair keys.append(key) values.append(value) return CoreFoundation.CFDictionaryCreate( CoreFoundation.kCFAllocatorDefault, keys, values, length, ffi.addressof(CoreFoundation.kCFTypeDictionaryKeyCallBacks), ffi.addressof(CoreFoundation.kCFTypeDictionaryValueCallBacks) ) @staticmethod def cf_array_from_list(values): """ Creates a CFArrayRef object from a list of CF* type objects. :param values: A list of CF* type object :return: A CFArrayRef """ length = len(values) return CoreFoundation.CFArrayCreate( CoreFoundation.kCFAllocatorDefault, values, length, ffi.addressof(CoreFoundation.kCFTypeArrayCallBacks) ) @staticmethod def cf_number_from_integer(integer): """ Creates a CFNumber object from an integer :param integer: The integer to create the CFNumber for :return: A CFNumber """ integer_as_long = ffi.new('long *', integer) return CoreFoundation.CFNumberCreate( CoreFoundation.kCFAllocatorDefault, kCFNumberCFIndexType, integer_as_long ) oscrypto-1.3.0/oscrypto/_mac/_core_foundation_ctypes.py000066400000000000000000000322471421476274700234620ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from ctypes import c_void_p, c_long, c_uint32, c_char_p, c_byte, c_ulong, c_bool from ctypes import CDLL, string_at, cast, POINTER, byref import ctypes from .._ffi import FFIEngineError, buffer_from_bytes, byte_string_from_buffer __all__ = [ 'CFHelpers', 'CoreFoundation', ] core_foundation_path = '/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation' CoreFoundation = CDLL(core_foundation_path, use_errno=True) CFIndex = c_long CFStringEncoding = c_uint32 CFArray = c_void_p CFData = c_void_p CFString = c_void_p CFNumber = c_void_p CFDictionary = c_void_p CFError = c_void_p CFType = c_void_p CFTypeID = c_ulong CFBoolean = c_void_p CFNumberType = c_uint32 CFTypeRef = POINTER(CFType) CFArrayRef = POINTER(CFArray) CFDataRef = POINTER(CFData) CFStringRef = POINTER(CFString) CFNumberRef = POINTER(CFNumber) CFBooleanRef = POINTER(CFBoolean) CFDictionaryRef = POINTER(CFDictionary) CFErrorRef = POINTER(CFError) CFAllocatorRef = c_void_p CFDictionaryKeyCallBacks = c_void_p CFDictionaryValueCallBacks = c_void_p CFArrayCallBacks = c_void_p pointer_p = POINTER(c_void_p) try: CoreFoundation.CFDataGetLength.argtypes = [ CFDataRef ] CoreFoundation.CFDataGetLength.restype = CFIndex CoreFoundation.CFDataGetBytePtr.argtypes = [ CFDataRef ] CoreFoundation.CFDataGetBytePtr.restype = c_void_p CoreFoundation.CFDataCreate.argtypes = [ CFAllocatorRef, c_char_p, CFIndex ] CoreFoundation.CFDataCreate.restype = CFDataRef CoreFoundation.CFDictionaryCreate.argtypes = [ CFAllocatorRef, CFStringRef, CFTypeRef, CFIndex, CFDictionaryKeyCallBacks, CFDictionaryValueCallBacks ] CoreFoundation.CFDictionaryCreate.restype = CFDictionaryRef CoreFoundation.CFDictionaryGetCount.argtypes = [ CFDictionaryRef ] CoreFoundation.CFDictionaryGetCount.restype = CFIndex CoreFoundation.CFStringGetCStringPtr.argtypes = [ CFStringRef, CFStringEncoding ] CoreFoundation.CFStringGetCStringPtr.restype = c_char_p CoreFoundation.CFStringGetCString.argtypes = [ CFStringRef, c_char_p, CFIndex, CFStringEncoding ] CoreFoundation.CFStringGetCString.restype = c_bool CoreFoundation.CFStringCreateWithCString.argtypes = [ CFAllocatorRef, c_char_p, CFStringEncoding ] CoreFoundation.CFStringCreateWithCString.restype = CFStringRef CoreFoundation.CFNumberCreate.argtypes = [ CFAllocatorRef, CFNumberType, c_void_p ] CoreFoundation.CFNumberCreate.restype = CFNumberRef CoreFoundation.CFCopyTypeIDDescription.argtypes = [ CFTypeID ] CoreFoundation.CFCopyTypeIDDescription.restype = CFStringRef CoreFoundation.CFRelease.argtypes = [ CFTypeRef ] CoreFoundation.CFRelease.restype = None CoreFoundation.CFRetain.argtypes = [ CFTypeRef ] CoreFoundation.CFRetain.restype = None CoreFoundation.CFErrorCopyDescription.argtypes = [ CFErrorRef ] CoreFoundation.CFErrorCopyDescription.restype = CFStringRef CoreFoundation.CFErrorGetDomain.argtypes = [ CFErrorRef ] CoreFoundation.CFErrorGetDomain.restype = CFStringRef CoreFoundation.CFErrorGetCode.argtypes = [ CFErrorRef ] CoreFoundation.CFErrorGetCode.restype = CFIndex CoreFoundation.CFBooleanGetValue.argtypes = [ CFBooleanRef ] CoreFoundation.CFBooleanGetValue.restype = c_byte CoreFoundation.CFDictionaryGetTypeID.argtypes = [] CoreFoundation.CFDictionaryGetTypeID.restype = CFTypeID CoreFoundation.CFNumberGetTypeID.argtypes = [] CoreFoundation.CFNumberGetTypeID.restype = CFTypeID CoreFoundation.CFStringGetTypeID.argtypes = [] CoreFoundation.CFStringGetTypeID.restype = CFTypeID CoreFoundation.CFDataGetTypeID.argtypes = [] CoreFoundation.CFDataGetTypeID.restype = CFTypeID CoreFoundation.CFArrayCreate.argtypes = [ CFAllocatorRef, POINTER(c_void_p), CFIndex, CFArrayCallBacks ] CoreFoundation.CFArrayCreate.restype = CFArrayRef CoreFoundation.CFArrayGetCount.argtypes = [ CFArrayRef ] CoreFoundation.CFArrayGetCount.restype = CFIndex CoreFoundation.CFArrayGetValueAtIndex.argtypes = [ CFArrayRef, CFIndex ] CoreFoundation.CFArrayGetValueAtIndex.restype = CFTypeRef CoreFoundation.CFNumberGetType.argtypes = [ CFNumberRef ] CoreFoundation.CFNumberGetType.restype = CFNumberType CoreFoundation.CFNumberGetValue.argtypes = [ CFNumberRef, CFNumberType, c_void_p ] CoreFoundation.CFNumberGetValue.restype = c_bool CoreFoundation.CFDictionaryGetKeysAndValues.argtypes = [ CFDictionaryRef, pointer_p, pointer_p ] CoreFoundation.CFDictionaryGetKeysAndValues.restype = CFIndex CoreFoundation.CFGetTypeID.argtypes = [ CFTypeRef ] CoreFoundation.CFGetTypeID.restype = CFTypeID setattr(CoreFoundation, 'kCFAllocatorDefault', CFAllocatorRef.in_dll(CoreFoundation, 'kCFAllocatorDefault')) setattr(CoreFoundation, 'kCFBooleanTrue', CFTypeRef.in_dll(CoreFoundation, 'kCFBooleanTrue')) kCFTypeDictionaryKeyCallBacks = c_void_p.in_dll(CoreFoundation, 'kCFTypeDictionaryKeyCallBacks') kCFTypeDictionaryValueCallBacks = c_void_p.in_dll(CoreFoundation, 'kCFTypeDictionaryValueCallBacks') kCFTypeArrayCallBacks = c_void_p.in_dll(CoreFoundation, 'kCFTypeArrayCallBacks') except (AttributeError): raise FFIEngineError('Error initializing ctypes') setattr(CoreFoundation, 'CFDataRef', CFDataRef) setattr(CoreFoundation, 'CFErrorRef', CFErrorRef) setattr(CoreFoundation, 'CFArrayRef', CFArrayRef) kCFNumberCFIndexType = CFNumberType(14) kCFStringEncodingUTF8 = CFStringEncoding(0x08000100) def _cast_pointer_p(value): """ Casts a value to a pointer of a pointer :param value: A ctypes object :return: A POINTER(c_void_p) object """ return cast(value, pointer_p) class CFHelpers(): """ Namespace for core foundation helpers """ _native_map = {} @classmethod def register_native_mapping(cls, type_id, callback): """ Register a function to convert a core foundation data type into its equivalent in python :param type_id: The CFTypeId for the type :param callback: A callback to pass the CFType object to """ cls._native_map[int(type_id)] = callback @staticmethod def cf_number_to_number(value): """ Converts a CFNumber object to a python float or integer :param value: The CFNumber object :return: A python number (float or integer) """ type_ = CoreFoundation.CFNumberGetType(_cast_pointer_p(value)) c_type = { 1: c_byte, # kCFNumberSInt8Type 2: ctypes.c_short, # kCFNumberSInt16Type 3: ctypes.c_int32, # kCFNumberSInt32Type 4: ctypes.c_int64, # kCFNumberSInt64Type 5: ctypes.c_float, # kCFNumberFloat32Type 6: ctypes.c_double, # kCFNumberFloat64Type 7: c_byte, # kCFNumberCharType 8: ctypes.c_short, # kCFNumberShortType 9: ctypes.c_int, # kCFNumberIntType 10: c_long, # kCFNumberLongType 11: ctypes.c_longlong, # kCFNumberLongLongType 12: ctypes.c_float, # kCFNumberFloatType 13: ctypes.c_double, # kCFNumberDoubleType 14: c_long, # kCFNumberCFIndexType 15: ctypes.c_int, # kCFNumberNSIntegerType 16: ctypes.c_double, # kCFNumberCGFloatType }[type_] output = c_type(0) CoreFoundation.CFNumberGetValue(_cast_pointer_p(value), type_, byref(output)) return output.value @staticmethod def cf_dictionary_to_dict(dictionary): """ Converts a CFDictionary object into a python dictionary :param dictionary: The CFDictionary to convert :return: A python dict """ dict_length = CoreFoundation.CFDictionaryGetCount(dictionary) keys = (CFTypeRef * dict_length)() values = (CFTypeRef * dict_length)() CoreFoundation.CFDictionaryGetKeysAndValues( dictionary, _cast_pointer_p(keys), _cast_pointer_p(values) ) output = {} for index in range(0, dict_length): output[CFHelpers.native(keys[index])] = CFHelpers.native(values[index]) return output @classmethod def native(cls, value): """ Converts a CF* object into its python equivalent :param value: The CF* object to convert :return: The native python object """ type_id = CoreFoundation.CFGetTypeID(value) if type_id in cls._native_map: return cls._native_map[type_id](value) else: return value @staticmethod def cf_string_to_unicode(value): """ Creates a python unicode string from a CFString object :param value: The CFString to convert :return: A python unicode string """ string = CoreFoundation.CFStringGetCStringPtr( _cast_pointer_p(value), kCFStringEncodingUTF8 ) if string is None: buffer = buffer_from_bytes(1024) result = CoreFoundation.CFStringGetCString( _cast_pointer_p(value), buffer, 1024, kCFStringEncodingUTF8 ) if not result: raise OSError('Error copying C string from CFStringRef') string = byte_string_from_buffer(buffer) if string is not None: string = string.decode('utf-8') return string @staticmethod def cf_string_from_unicode(string): """ Creates a CFStringRef object from a unicode string :param string: The unicode string to create the CFString object from :return: A CFStringRef """ return CoreFoundation.CFStringCreateWithCString( CoreFoundation.kCFAllocatorDefault, string.encode('utf-8'), kCFStringEncodingUTF8 ) @staticmethod def cf_data_to_bytes(value): """ Extracts a bytestring from a CFData object :param value: A CFData object :return: A byte string """ start = CoreFoundation.CFDataGetBytePtr(value) num_bytes = CoreFoundation.CFDataGetLength(value) return string_at(start, num_bytes) @staticmethod def cf_data_from_bytes(bytes_): """ Creates a CFDataRef object from a byte string :param bytes_: The data to create the CFData object from :return: A CFDataRef """ return CoreFoundation.CFDataCreate( CoreFoundation.kCFAllocatorDefault, bytes_, len(bytes_) ) @staticmethod def cf_dictionary_from_pairs(pairs): """ Creates a CFDictionaryRef object from a list of 2-element tuples representing the key and value. Each key should be a CFStringRef and each value some sort of CF* type. :param pairs: A list of 2-element tuples :return: A CFDictionaryRef """ length = len(pairs) keys = [] values = [] for pair in pairs: key, value = pair keys.append(key) values.append(value) keys = (CFStringRef * length)(*keys) values = (CFTypeRef * length)(*values) return CoreFoundation.CFDictionaryCreate( CoreFoundation.kCFAllocatorDefault, _cast_pointer_p(byref(keys)), _cast_pointer_p(byref(values)), length, kCFTypeDictionaryKeyCallBacks, kCFTypeDictionaryValueCallBacks ) @staticmethod def cf_array_from_list(values): """ Creates a CFArrayRef object from a list of CF* type objects. :param values: A list of CF* type object :return: A CFArrayRef """ length = len(values) values = (CFTypeRef * length)(*values) return CoreFoundation.CFArrayCreate( CoreFoundation.kCFAllocatorDefault, _cast_pointer_p(byref(values)), length, kCFTypeArrayCallBacks ) @staticmethod def cf_number_from_integer(integer): """ Creates a CFNumber object from an integer :param integer: The integer to create the CFNumber for :return: A CFNumber """ integer_as_long = c_long(integer) return CoreFoundation.CFNumberCreate( CoreFoundation.kCFAllocatorDefault, kCFNumberCFIndexType, byref(integer_as_long) ) oscrypto-1.3.0/oscrypto/_mac/_security.py000066400000000000000000000101321421476274700205510ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .. import ffi from .._ffi import null from ..errors import TLSDisconnectError, TLSGracefulDisconnectError if ffi() == 'cffi': from ._security_cffi import Security, version_info as osx_version_info from ._core_foundation_cffi import CoreFoundation, CFHelpers else: from ._security_ctypes import Security, version_info as osx_version_info from ._core_foundation_ctypes import CoreFoundation, CFHelpers __all__ = [ 'handle_sec_error', 'osx_version_info', 'Security', 'SecurityConst', ] def handle_sec_error(error, exception_class=None): """ Checks a Security OSStatus error code and throws an exception if there is an error to report :param error: An OSStatus :param exception_class: The exception class to use for the exception if an error occurred :raises: OSError - when the OSStatus contains an error """ if error == 0: return if error in set([SecurityConst.errSSLClosedNoNotify, SecurityConst.errSSLClosedAbort]): raise TLSDisconnectError('The remote end closed the connection') if error == SecurityConst.errSSLClosedGraceful: raise TLSGracefulDisconnectError('The remote end closed the connection') cf_error_string = Security.SecCopyErrorMessageString(error, null()) output = CFHelpers.cf_string_to_unicode(cf_error_string) CoreFoundation.CFRelease(cf_error_string) if output is None or output == '': output = 'OSStatus %s' % error if exception_class is None: exception_class = OSError raise exception_class(output) def _extract_policy_properties(value): properties_dict = Security.SecPolicyCopyProperties(value) return CFHelpers.cf_dictionary_to_dict(properties_dict) CFHelpers.register_native_mapping( Security.SecPolicyGetTypeID(), _extract_policy_properties ) class SecurityConst(): kSecTrustSettingsDomainUser = 0 kSecTrustSettingsDomainAdmin = 1 kSecTrustSettingsDomainSystem = 2 kSecTrustResultProceed = 1 kSecTrustResultUnspecified = 4 kSecTrustOptionImplicitAnchors = 0x00000040 kSecFormatOpenSSL = 1 kSecItemTypePrivateKey = 1 kSecItemTypePublicKey = 2 kSSLSessionOptionBreakOnServerAuth = 0 kSSLProtocol2 = 1 kSSLProtocol3 = 2 kTLSProtocol1 = 4 kTLSProtocol11 = 7 kTLSProtocol12 = 8 kSSLClientSide = 1 kSSLStreamType = 0 errSSLProtocol = -9800 errSSLWouldBlock = -9803 errSSLClosedGraceful = -9805 errSSLClosedNoNotify = -9816 errSSLClosedAbort = -9806 errSSLXCertChainInvalid = -9807 errSSLCrypto = -9809 errSSLInternal = -9810 errSSLCertExpired = -9814 errSSLCertNotYetValid = -9815 errSSLUnknownRootCert = -9812 errSSLNoRootCert = -9813 errSSLHostNameMismatch = -9843 errSSLPeerHandshakeFail = -9824 errSSLPeerProtocolVersion = -9836 errSSLPeerUserCancelled = -9839 errSSLWeakPeerEphemeralDHKey = -9850 errSSLServerAuthCompleted = -9841 errSSLRecordOverflow = -9847 CSSMERR_APPLETP_HOSTNAME_MISMATCH = -2147408896 CSSMERR_TP_CERT_EXPIRED = -2147409654 CSSMERR_TP_CERT_NOT_VALID_YET = -2147409653 CSSMERR_TP_CERT_REVOKED = -2147409652 CSSMERR_TP_NOT_TRUSTED = -2147409622 CSSMERR_TP_CERT_SUSPENDED = -2147409651 CSSM_CERT_X_509v3 = 0x00000004 APPLE_TP_REVOCATION_CRL = b'*\x86H\x86\xf7cd\x01\x06' APPLE_TP_REVOCATION_OCSP = b'*\x86H\x86\xf7cd\x01\x07' CSSM_APPLE_TP_OCSP_OPTS_VERSION = 0 CSSM_TP_ACTION_OCSP_DISABLE_NET = 0x00000004 CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE = 0x00000008 CSSM_APPLE_TP_CRL_OPTS_VERSION = 0 errSecVerifyFailed = -67808 errSecNoTrustSettings = -25263 errSecItemNotFound = -25300 errSecInvalidTrustSettings = -25262 kSecPaddingNone = 0 kSecPaddingPKCS1 = 1 CSSM_KEYUSE_SIGN = 0x00000004 CSSM_KEYUSE_VERIFY = 0x00000008 CSSM_ALGID_DH = 2 CSSM_ALGID_RSA = 42 CSSM_ALGID_DSA = 43 CSSM_ALGID_ECDSA = 73 CSSM_KEYATTR_PERMANENT = 0x00000001 CSSM_KEYATTR_EXTRACTABLE = 0x00000020 oscrypto-1.3.0/oscrypto/_mac/_security_cffi.py000066400000000000000000000252721421476274700215530ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import platform from .._ffi import register_ffi from cffi import FFI __all__ = [ 'Security', 'version', 'version_info', ] version = platform.mac_ver()[0] version_info = tuple(map(int, version.split('.'))) if version_info < (10, 7): raise OSError('Only OS X 10.7 and newer are supported, not %s.%s' % (version_info[0], version_info[1])) ffi = FFI() ffi.cdef(""" typedef bool Boolean; typedef long CFIndex; typedef int32_t OSStatus; typedef unsigned long CFTypeID; typedef uint32_t SecTrustSettingsDomain; typedef uint32_t SecPadding; typedef uint32_t SecItemImportExportFlags; typedef uint32_t SecKeyImportExportFlags; typedef uint32_t SecExternalFormat; typedef uint32_t SecExternalItemType; typedef uint32_t CSSM_ALGORITHMS; typedef uint64_t CSSM_CC_HANDLE; typedef uint32_t CSSM_KEYUSE; typedef uint32_t CSSM_CERT_TYPE; typedef uint32_t SSLProtocol; typedef uint32_t SSLCipherSuite; typedef uint32_t SecTrustResultType; typedef void *CFTypeRef; typedef CFTypeRef CFArrayRef; typedef CFTypeRef CFDataRef; typedef CFTypeRef CFStringRef; typedef CFTypeRef CFDictionaryRef; typedef CFTypeRef CFErrorRef; typedef CFTypeRef CFAllocatorRef; typedef ... *SecKeyRef; typedef ... *SecCertificateRef; typedef ... *SecTransformRef; typedef ... *SecRandomRef; typedef ... *SecPolicyRef; typedef ... *SecPolicySearchRef; typedef ... *SecAccessRef; typedef struct { uint32_t version; SecKeyImportExportFlags flags; CFTypeRef passphrase; CFStringRef alertTitle; CFStringRef alertPrompt; SecAccessRef accessRef; CFArrayRef keyUsage; CFArrayRef keyAttributes; } SecItemImportExportKeyParameters; typedef ... *SecKeychainRef; typedef ... *SSLContextRef; typedef ... *SecTrustRef; typedef uint32_t SSLConnectionRef; typedef struct { uint32_t Length; char *Data; } CSSM_DATA, CSSM_OID; typedef struct { uint32_t Version; uint32_t Flags; CSSM_DATA *LocalResponder; CSSM_DATA *LocalResponderCert; } CSSM_APPLE_TP_OCSP_OPTIONS; typedef struct { uint32_t Version; uint32_t CrlFlags; void *crlStore; } CSSM_APPLE_TP_CRL_OPTIONS; OSStatus SecKeychainCreate(char *path, uint32_t pass_len, void *pass, Boolean prompt, SecAccessRef initialAccess, SecKeychainRef *keychain); OSStatus SecKeychainDelete(SecKeychainRef keychain); int SecRandomCopyBytes(SecRandomRef rnd, size_t count, char *bytes); SecKeyRef SecKeyCreateFromData(CFDictionaryRef parameters, CFDataRef keyData, CFErrorRef *error); SecTransformRef SecEncryptTransformCreate(SecKeyRef keyRef, CFErrorRef *error); SecTransformRef SecDecryptTransformCreate(SecKeyRef keyRef, CFErrorRef *error); Boolean SecTransformSetAttribute(SecTransformRef transformRef, CFStringRef key, CFTypeRef value, CFErrorRef *error); CFTypeRef SecTransformExecute(SecTransformRef transformRef, CFErrorRef *errorRef); SecTransformRef SecVerifyTransformCreate(SecKeyRef key, CFDataRef signature, CFErrorRef *error); SecTransformRef SecSignTransformCreate(SecKeyRef key, CFErrorRef *error); SecCertificateRef SecCertificateCreateWithData(CFAllocatorRef allocator, CFDataRef data); OSStatus SecCertificateCopyPublicKey(SecCertificateRef certificate, SecKeyRef *key); CFStringRef SecCopyErrorMessageString(OSStatus status, void *reserved); OSStatus SecTrustCopyAnchorCertificates(CFArrayRef *anchors); CFDataRef SecCertificateCopyData(SecCertificateRef certificate); OSStatus SecTrustSettingsCopyCertificates(SecTrustSettingsDomain domain, CFArrayRef *certArray); OSStatus SecTrustSettingsCopyTrustSettings(SecCertificateRef certRef, SecTrustSettingsDomain domain, CFArrayRef *trustSettings); CFDictionaryRef SecPolicyCopyProperties(SecPolicyRef policyRef); CFTypeID SecPolicyGetTypeID(void); OSStatus SecKeyEncrypt(SecKeyRef key, SecPadding padding, const char *plainText, size_t plainTextLen, char *cipherText, size_t *cipherTextLen); OSStatus SecKeyDecrypt(SecKeyRef key, SecPadding padding, const char *cipherText, size_t cipherTextLen, char *plainText, size_t *plainTextLen); OSStatus SecKeyRawSign(SecKeyRef key, SecPadding padding, const char *dataToSign, size_t dataToSignLen, char *sig, size_t * sigLen); OSStatus SecKeyRawVerify(SecKeyRef key, SecPadding padding, const char *signedData, size_t signedDataLen, const char *sig, size_t sigLen); OSStatus SecItemImport(CFDataRef importedData, CFStringRef fileNameOrExtension, SecExternalFormat *inputFormat, SecExternalItemType *itemType, SecItemImportExportFlags flags, const SecItemImportExportKeyParameters *keyParams, SecKeychainRef importKeychain, CFArrayRef *outItems); OSStatus SecItemExport(CFTypeRef secItemOrArray, SecExternalFormat outputFormat, SecItemImportExportFlags flags, const SecItemImportExportKeyParameters *keyParams, CFDataRef *exportedData); OSStatus SecAccessCreate(CFStringRef descriptor, CFArrayRef trustedlist, SecAccessRef *accessRef); OSStatus SecKeyCreatePair(SecKeychainRef keychainRef, CSSM_ALGORITHMS algorithm, uint32_t keySizeInBits, CSSM_CC_HANDLE contextHandle, CSSM_KEYUSE publicKeyUsage, uint32_t publicKeyAttr, CSSM_KEYUSE privateKeyUsage, uint32_t privateKeyAttr, SecAccessRef initialAccess, SecKeyRef* publicKeyRef, SecKeyRef* privateKeyRef); OSStatus SecKeychainItemDelete(SecKeyRef itemRef); typedef OSStatus (*SSLReadFunc)(SSLConnectionRef connection, char *data, size_t *dataLength); typedef OSStatus (*SSLWriteFunc)(SSLConnectionRef connection, const char *data, size_t *dataLength); OSStatus SSLSetIOFuncs(SSLContextRef context, SSLReadFunc readFunc, SSLWriteFunc writeFunc); OSStatus SSLSetPeerID(SSLContextRef context, const char *peerID, size_t peerIDLen); OSStatus SSLSetConnection(SSLContextRef context, SSLConnectionRef connection); OSStatus SSLSetPeerDomainName(SSLContextRef context, const char *peerName, size_t peerNameLen); OSStatus SSLHandshake(SSLContextRef context); OSStatus SSLGetBufferedReadSize(SSLContextRef context, size_t *bufSize); OSStatus SSLRead(SSLContextRef context, char *data, size_t dataLength, size_t *processed); OSStatus SSLWrite(SSLContextRef context, const char *data, size_t dataLength, size_t *processed); OSStatus SSLClose(SSLContextRef context); OSStatus SSLGetNumberSupportedCiphers(SSLContextRef context, size_t *numCiphers); OSStatus SSLGetSupportedCiphers(SSLContextRef context, SSLCipherSuite *ciphers, size_t *numCiphers); OSStatus SSLSetEnabledCiphers(SSLContextRef context, const SSLCipherSuite *ciphers, size_t numCiphers); OSStatus SSLGetNumberEnabledCiphers(SSLContextRef context, size_t *numCiphers); OSStatus SSLGetEnabledCiphers(SSLContextRef context, SSLCipherSuite *ciphers, size_t *numCiphers); OSStatus SSLGetNegotiatedCipher(SSLContextRef context, SSLCipherSuite *cipherSuite); OSStatus SSLGetNegotiatedProtocolVersion(SSLContextRef context, SSLProtocol *protocol); OSStatus SSLCopyPeerTrust(SSLContextRef context, SecTrustRef *trust); OSStatus SecTrustGetCssmResultCode(SecTrustRef trust, OSStatus *resultCode); CFIndex SecTrustGetCertificateCount(SecTrustRef trust); SecCertificateRef SecTrustGetCertificateAtIndex(SecTrustRef trust, CFIndex ix); OSStatus SecTrustSetAnchorCertificates(SecTrustRef trust, CFArrayRef anchorCertificates); OSStatus SecTrustSetAnchorCertificatesOnly(SecTrustRef trust, Boolean anchorCertificatesOnly); OSStatus SecTrustSetPolicies(SecTrustRef trust, CFArrayRef policies); SecPolicyRef SecPolicyCreateSSL(Boolean server, CFStringRef hostname); OSStatus SecPolicySearchCreate(CSSM_CERT_TYPE certType, const CSSM_OID *policyOID, const CSSM_DATA *value, SecPolicySearchRef *searchRef); OSStatus SecPolicySearchCopyNext(SecPolicySearchRef searchRef, SecPolicyRef *policyRef); OSStatus SecPolicySetValue(SecPolicyRef policyRef, const CSSM_DATA *value); OSStatus SecTrustEvaluate(SecTrustRef trust, SecTrustResultType *result); extern SecRandomRef kSecRandomDefault; extern CFStringRef kSecPaddingKey; extern CFStringRef kSecPaddingPKCS7Key; extern CFStringRef kSecPaddingPKCS5Key; extern CFStringRef kSecPaddingPKCS1Key; extern CFStringRef kSecPaddingOAEPKey; extern CFStringRef kSecPaddingNoneKey; extern CFStringRef kSecModeCBCKey; extern CFStringRef kSecTransformInputAttributeName; extern CFStringRef kSecDigestTypeAttribute; extern CFStringRef kSecDigestLengthAttribute; extern CFStringRef kSecIVKey; extern CFStringRef kSecAttrIsExtractable; extern CFStringRef kSecDigestSHA1; extern CFStringRef kSecDigestSHA2; extern CFStringRef kSecDigestMD5; extern CFStringRef kSecAttrKeyType; extern CFTypeRef kSecAttrKeyTypeRSA; extern CFTypeRef kSecAttrKeyTypeDSA; extern CFTypeRef kSecAttrKeyTypeECDSA; extern CFStringRef kSecAttrKeySizeInBits; extern CFStringRef kSecAttrLabel; extern CFTypeRef kSecAttrCanSign; extern CFTypeRef kSecAttrCanVerify; extern CFTypeRef kSecAttrKeyTypeAES; extern CFTypeRef kSecAttrKeyTypeRC4; extern CFTypeRef kSecAttrKeyTypeRC2; extern CFTypeRef kSecAttrKeyType3DES; extern CFTypeRef kSecAttrKeyTypeDES; """) if version_info < (10, 8): ffi.cdef(""" OSStatus SSLNewContext(Boolean isServer, SSLContextRef *contextPtr); OSStatus SSLDisposeContext(SSLContextRef context); OSStatus SSLSetEnableCertVerify(SSLContextRef context, Boolean enableVerify); OSStatus SSLSetProtocolVersionEnabled(SSLContextRef context, SSLProtocol protocol, Boolean enable); """) else: ffi.cdef(""" typedef uint32_t SSLProtocolSide; typedef uint32_t SSLConnectionType; typedef uint32_t SSLSessionOption; SSLContextRef SSLCreateContext(CFAllocatorRef alloc, SSLProtocolSide protocolSide, SSLConnectionType connectionType); OSStatus SSLSetSessionOption(SSLContextRef context, SSLSessionOption option, Boolean value); OSStatus SSLSetProtocolVersionMin(SSLContextRef context, SSLProtocol minVersion); OSStatus SSLSetProtocolVersionMax(SSLContextRef context, SSLProtocol maxVersion); """) security_path = '/System/Library/Frameworks/Security.framework/Security' Security = ffi.dlopen(security_path) register_ffi(Security, ffi) oscrypto-1.3.0/oscrypto/_mac/_security_ctypes.py000066400000000000000000000440601421476274700221470ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import platform from ctypes import c_void_p, c_int32, c_char_p, c_size_t, c_byte, c_int, c_uint32, c_uint64, c_ulong, c_long, c_bool from ctypes import CDLL, POINTER, CFUNCTYPE, Structure from .._ffi import FFIEngineError __all__ = [ 'Security', 'version', 'version_info', ] version = platform.mac_ver()[0] version_info = tuple(map(int, platform.mac_ver()[0].split('.'))) if version_info < (10, 7): raise OSError('Only OS X 10.7 and newer are supported, not %s.%s' % (version_info[0], version_info[1])) security_path = '/System/Library/Frameworks/Security.framework/Security' Security = CDLL(security_path, use_errno=True) Boolean = c_bool CFIndex = c_long CFData = c_void_p CFString = c_void_p CFArray = c_void_p CFDictionary = c_void_p CFError = c_void_p CFType = c_void_p CFTypeID = c_ulong CFTypeRef = POINTER(CFType) CFAllocatorRef = c_void_p OSStatus = c_int32 CFDataRef = POINTER(CFData) CFStringRef = POINTER(CFString) CFArrayRef = POINTER(CFArray) CFDictionaryRef = POINTER(CFDictionary) CFErrorRef = POINTER(CFError) SecKeyRef = POINTER(c_void_p) SecCertificateRef = POINTER(c_void_p) SecTransformRef = POINTER(c_void_p) SecRandomRef = c_void_p SecTrustSettingsDomain = c_uint32 SecItemImportExportFlags = c_uint32 SecKeyImportExportFlags = c_uint32 SecExternalFormat = c_uint32 SecExternalItemType = c_uint32 SecPadding = c_uint32 SSLProtocol = c_uint32 SSLCipherSuite = c_uint32 SecPolicyRef = POINTER(c_void_p) CSSM_CC_HANDLE = c_uint64 CSSM_ALGORITHMS = c_uint32 CSSM_KEYUSE = c_uint32 SecAccessRef = POINTER(c_void_p) SecKeychainRef = POINTER(c_void_p) SSLContextRef = POINTER(c_void_p) SecTrustRef = POINTER(c_void_p) SSLConnectionRef = c_uint32 SecTrustResultType = c_uint32 SecTrustOptionFlags = c_uint32 SecPolicySearchRef = c_void_p CSSM_CERT_TYPE = c_uint32 class CSSM_DATA(Structure): # noqa _fields_ = [ ('Length', c_uint32), ('Data', c_char_p) ] CSSM_OID = CSSM_DATA class CSSM_APPLE_TP_OCSP_OPTIONS(Structure): # noqa _fields_ = [ ('Version', c_uint32), ('Flags', c_uint32), ('LocalResponder', POINTER(CSSM_DATA)), ('LocalResponderCert', POINTER(CSSM_DATA)), ] class CSSM_APPLE_TP_CRL_OPTIONS(Structure): # noqa _fields_ = [ ('Version', c_uint32), ('CrlFlags', c_uint32), ('crlStore', c_void_p), ] class SecItemImportExportKeyParameters(Structure): _fields_ = [ ('version', c_uint32), ('flags', SecKeyImportExportFlags), ('passphrase', CFTypeRef), ('alertTitle', CFStringRef), ('alertPrompt', CFStringRef), ('accessRef', SecAccessRef), ('keyUsage', CFArrayRef), ('keyAttributes', CFArrayRef), ] try: Security.SecKeychainCreate.argtypes = [ c_char_p, c_uint32, c_void_p, Boolean, SecAccessRef, POINTER(SecKeychainRef) ] Security.SecKeychainCreate.restype = OSStatus Security.SecKeychainDelete.argtypes = [SecKeychainRef] Security.SecKeychainDelete.restype = OSStatus Security.SecRandomCopyBytes.argtypes = [ SecRandomRef, c_size_t, c_char_p ] Security.SecRandomCopyBytes.restype = c_int Security.SecKeyCreateFromData.argtypes = [ CFDictionaryRef, CFDataRef, POINTER(CFErrorRef) ] Security.SecKeyCreateFromData.restype = SecKeyRef Security.SecEncryptTransformCreate.argtypes = [ SecKeyRef, POINTER(CFErrorRef) ] Security.SecEncryptTransformCreate.restype = SecTransformRef Security.SecDecryptTransformCreate.argtypes = [ SecKeyRef, POINTER(CFErrorRef) ] Security.SecDecryptTransformCreate.restype = SecTransformRef Security.SecTransformSetAttribute.argtypes = [ SecTransformRef, CFStringRef, CFTypeRef, POINTER(CFErrorRef) ] Security.SecTransformSetAttribute.restype = Boolean Security.SecTransformExecute.argtypes = [ SecTransformRef, POINTER(CFErrorRef) ] Security.SecTransformExecute.restype = CFTypeRef Security.SecVerifyTransformCreate.argtypes = [ SecKeyRef, CFDataRef, POINTER(CFErrorRef) ] Security.SecVerifyTransformCreate.restype = SecTransformRef Security.SecSignTransformCreate.argtypes = [ SecKeyRef, POINTER(CFErrorRef) ] Security.SecSignTransformCreate.restype = SecTransformRef Security.SecCertificateCreateWithData.argtypes = [ CFAllocatorRef, CFDataRef ] Security.SecCertificateCreateWithData.restype = SecCertificateRef Security.SecCertificateCopyPublicKey.argtypes = [ SecCertificateRef, POINTER(SecKeyRef) ] Security.SecCertificateCopyPublicKey.restype = OSStatus Security.SecCopyErrorMessageString.argtypes = [ OSStatus, c_void_p ] Security.SecCopyErrorMessageString.restype = CFStringRef Security.SecTrustCopyAnchorCertificates.argtypes = [ POINTER(CFArrayRef) ] Security.SecTrustCopyAnchorCertificates.restype = OSStatus Security.SecCertificateCopyData.argtypes = [ SecCertificateRef ] Security.SecCertificateCopyData.restype = CFDataRef Security.SecTrustSettingsCopyCertificates.argtypes = [ SecTrustSettingsDomain, POINTER(CFArrayRef) ] Security.SecTrustSettingsCopyCertificates.restype = OSStatus Security.SecTrustSettingsCopyTrustSettings.argtypes = [ SecCertificateRef, SecTrustSettingsDomain, POINTER(CFArrayRef) ] Security.SecTrustSettingsCopyTrustSettings.restype = OSStatus Security.SecPolicyCopyProperties.argtypes = [ SecPolicyRef ] Security.SecPolicyCopyProperties.restype = CFDictionaryRef Security.SecPolicyGetTypeID.argtypes = [] Security.SecPolicyGetTypeID.restype = CFTypeID Security.SecKeyEncrypt.argtypes = [ SecKeyRef, SecPadding, c_char_p, c_size_t, c_char_p, POINTER(c_size_t) ] Security.SecKeyEncrypt.restype = OSStatus Security.SecKeyDecrypt.argtypes = [ SecKeyRef, SecPadding, c_char_p, c_size_t, c_char_p, POINTER(c_size_t) ] Security.SecKeyDecrypt.restype = OSStatus Security.SecKeyRawSign.argtypes = [ SecKeyRef, SecPadding, c_char_p, c_size_t, c_char_p, POINTER(c_size_t) ] Security.SecKeyRawSign.restype = OSStatus Security.SecKeyRawVerify.argtypes = [ SecKeyRef, SecPadding, c_char_p, c_size_t, c_char_p, c_size_t ] Security.SecKeyRawVerify.restype = OSStatus Security.SecAccessCreate.argtypes = [ CFStringRef, CFArrayRef, POINTER(SecAccessRef) ] Security.SecAccessCreate.restype = OSStatus Security.SecKeyCreatePair.argtypes = [ SecKeychainRef, CSSM_ALGORITHMS, c_uint32, CSSM_CC_HANDLE, CSSM_KEYUSE, c_uint32, CSSM_KEYUSE, c_uint32, SecAccessRef, POINTER(SecKeyRef), POINTER(SecKeyRef) ] Security.SecKeyCreatePair.restype = OSStatus Security.SecItemImport.argtypes = [ CFDataRef, CFStringRef, POINTER(SecExternalFormat), POINTER(SecExternalItemType), SecItemImportExportFlags, POINTER(SecItemImportExportKeyParameters), SecKeychainRef, POINTER(CFArrayRef) ] Security.SecItemImport.restype = OSStatus Security.SecItemExport.argtypes = [ CFTypeRef, SecExternalFormat, SecItemImportExportFlags, POINTER(SecItemImportExportKeyParameters), POINTER(CFDataRef) ] Security.SecItemExport.restype = OSStatus Security.SecKeychainItemDelete.argtypes = [ SecKeyRef ] Security.SecKeychainItemDelete.restype = OSStatus SSLReadFunc = CFUNCTYPE(OSStatus, SSLConnectionRef, POINTER(c_byte), POINTER(c_size_t)) SSLWriteFunc = CFUNCTYPE(OSStatus, SSLConnectionRef, POINTER(c_byte), POINTER(c_size_t)) Security.SSLSetIOFuncs.argtypes = [ SSLContextRef, SSLReadFunc, SSLWriteFunc ] Security.SSLSetIOFuncs.restype = OSStatus Security.SSLSetPeerID.argtypes = [ SSLContextRef, c_char_p, c_size_t ] Security.SSLSetPeerID.restype = OSStatus Security.SSLSetCertificateAuthorities.argtypes = [ SSLContextRef, CFTypeRef, Boolean ] Security.SSLSetCertificateAuthorities.restype = OSStatus Security.SecTrustSetPolicies.argtypes = [ SecTrustRef, CFArrayRef ] Security.SecTrustSetPolicies.restype = OSStatus Security.SecPolicyCreateSSL.argtypes = [ Boolean, CFStringRef ] Security.SecPolicyCreateSSL.restype = SecPolicyRef Security.SecPolicySearchCreate.argtypes = [ CSSM_CERT_TYPE, POINTER(CSSM_OID), POINTER(CSSM_DATA), POINTER(SecPolicySearchRef) ] Security.SecPolicySearchCreate.restype = OSStatus Security.SecPolicySearchCopyNext.argtypes = [ SecPolicySearchRef, POINTER(SecPolicyRef) ] Security.SecPolicySearchCopyNext.restype = OSStatus Security.SecPolicySetValue.argtypes = [ SecPolicyRef, POINTER(CSSM_DATA) ] Security.SecPolicySetValue.restype = OSStatus Security.SSLSetConnection.argtypes = [ SSLContextRef, SSLConnectionRef ] Security.SSLSetConnection.restype = OSStatus Security.SSLSetPeerDomainName.argtypes = [ SSLContextRef, c_char_p, c_size_t ] Security.SSLSetPeerDomainName.restype = OSStatus Security.SSLHandshake.argtypes = [ SSLContextRef ] Security.SSLHandshake.restype = OSStatus Security.SSLGetBufferedReadSize.argtypes = [ SSLContextRef, POINTER(c_size_t) ] Security.SSLGetBufferedReadSize.restype = OSStatus Security.SSLRead.argtypes = [ SSLContextRef, c_char_p, c_size_t, POINTER(c_size_t) ] Security.SSLRead.restype = OSStatus Security.SSLWrite.argtypes = [ SSLContextRef, c_char_p, c_size_t, POINTER(c_size_t) ] Security.SSLWrite.restype = OSStatus Security.SSLClose.argtypes = [ SSLContextRef ] Security.SSLClose.restype = OSStatus Security.SSLGetNumberSupportedCiphers.argtypes = [ SSLContextRef, POINTER(c_size_t) ] Security.SSLGetNumberSupportedCiphers.restype = OSStatus Security.SSLGetSupportedCiphers.argtypes = [ SSLContextRef, POINTER(SSLCipherSuite), POINTER(c_size_t) ] Security.SSLGetSupportedCiphers.restype = OSStatus Security.SSLSetEnabledCiphers.argtypes = [ SSLContextRef, POINTER(SSLCipherSuite), c_size_t ] Security.SSLSetEnabledCiphers.restype = OSStatus Security.SSLGetNumberEnabledCiphers.argtype = [ SSLContextRef, POINTER(c_size_t) ] Security.SSLGetNumberEnabledCiphers.restype = OSStatus Security.SSLGetEnabledCiphers.argtypes = [ SSLContextRef, POINTER(SSLCipherSuite), POINTER(c_size_t) ] Security.SSLGetEnabledCiphers.restype = OSStatus Security.SSLGetNegotiatedCipher.argtypes = [ SSLContextRef, POINTER(SSLCipherSuite) ] Security.SSLGetNegotiatedCipher.restype = OSStatus Security.SSLGetNegotiatedProtocolVersion.argtypes = [ SSLContextRef, POINTER(SSLProtocol) ] Security.SSLGetNegotiatedProtocolVersion.restype = OSStatus Security.SSLCopyPeerTrust.argtypes = [ SSLContextRef, POINTER(SecTrustRef) ] Security.SSLCopyPeerTrust.restype = OSStatus Security.SecTrustGetCssmResultCode.argtypes = [ SecTrustRef, POINTER(OSStatus) ] Security.SecTrustGetCssmResultCode.restype = OSStatus Security.SecTrustGetCertificateCount.argtypes = [ SecTrustRef ] Security.SecTrustGetCertificateCount.restype = CFIndex Security.SecTrustGetCertificateAtIndex.argtypes = [ SecTrustRef, CFIndex ] Security.SecTrustGetCertificateAtIndex.restype = SecCertificateRef Security.SecTrustSetAnchorCertificates.argtypes = [ SecTrustRef, CFArrayRef ] Security.SecTrustSetAnchorCertificates.restype = OSStatus Security.SecTrustSetAnchorCertificatesOnly.argstypes = [ SecTrustRef, Boolean ] Security.SecTrustSetAnchorCertificatesOnly.restype = OSStatus Security.SecTrustEvaluate.argtypes = [ SecTrustRef, POINTER(SecTrustResultType) ] Security.SecTrustEvaluate.restype = OSStatus if version_info < (10, 8): Security.SSLNewContext.argtypes = [ Boolean, POINTER(SSLContextRef) ] Security.SSLNewContext.restype = OSStatus Security.SSLDisposeContext.argtypes = [ SSLContextRef ] Security.SSLDisposeContext.restype = OSStatus Security.SSLSetEnableCertVerify.argtypes = [ SSLContextRef, Boolean ] Security.SSLSetEnableCertVerify.restype = OSStatus Security.SSLSetProtocolVersionEnabled.argtypes = [ SSLContextRef, SSLProtocol, Boolean ] Security.SSLSetProtocolVersionEnabled.restype = OSStatus else: SSLProtocolSide = c_uint32 SSLConnectionType = c_uint32 SSLSessionOption = c_uint32 Security.SSLCreateContext.argtypes = [ CFAllocatorRef, SSLProtocolSide, SSLConnectionType ] Security.SSLCreateContext.restype = SSLContextRef Security.SSLSetSessionOption.argtypes = [ SSLContextRef, SSLSessionOption, Boolean ] Security.SSLSetSessionOption.restype = OSStatus Security.SSLSetProtocolVersionMin.argtypes = [ SSLContextRef, SSLProtocol ] Security.SSLSetProtocolVersionMin.restype = OSStatus Security.SSLSetProtocolVersionMax.argtypes = [ SSLContextRef, SSLProtocol ] Security.SSLSetProtocolVersionMax.restype = OSStatus setattr(Security, 'SSLReadFunc', SSLReadFunc) setattr(Security, 'SSLWriteFunc', SSLWriteFunc) setattr(Security, 'SSLContextRef', SSLContextRef) setattr(Security, 'SSLProtocol', SSLProtocol) setattr(Security, 'SSLCipherSuite', SSLCipherSuite) setattr(Security, 'SecTrustRef', SecTrustRef) setattr(Security, 'SecTrustResultType', SecTrustResultType) setattr(Security, 'OSStatus', OSStatus) setattr(Security, 'SecAccessRef', SecAccessRef) setattr(Security, 'SecKeychainRef', SecKeychainRef) setattr(Security, 'SecKeyRef', SecKeyRef) setattr(Security, 'SecPolicySearchRef', SecPolicySearchRef) setattr(Security, 'SecPolicyRef', SecPolicyRef) setattr(Security, 'CSSM_DATA', CSSM_DATA) setattr(Security, 'CSSM_OID', CSSM_OID) setattr(Security, 'CSSM_APPLE_TP_OCSP_OPTIONS', CSSM_APPLE_TP_OCSP_OPTIONS) setattr(Security, 'CSSM_APPLE_TP_CRL_OPTIONS', CSSM_APPLE_TP_CRL_OPTIONS) setattr(Security, 'SecItemImportExportKeyParameters', SecItemImportExportKeyParameters) setattr(Security, 'kSecRandomDefault', SecRandomRef.in_dll(Security, 'kSecRandomDefault')) setattr(Security, 'kSecPaddingKey', CFStringRef.in_dll(Security, 'kSecPaddingKey')) setattr(Security, 'kSecPaddingPKCS7Key', CFStringRef.in_dll(Security, 'kSecPaddingPKCS7Key')) setattr(Security, 'kSecPaddingPKCS5Key', CFStringRef.in_dll(Security, 'kSecPaddingPKCS5Key')) setattr(Security, 'kSecPaddingPKCS1Key', CFStringRef.in_dll(Security, 'kSecPaddingPKCS1Key')) setattr(Security, 'kSecPaddingOAEPKey', CFStringRef.in_dll(Security, 'kSecPaddingOAEPKey')) setattr(Security, 'kSecPaddingNoneKey', CFStringRef.in_dll(Security, 'kSecPaddingNoneKey')) setattr(Security, 'kSecModeCBCKey', CFStringRef.in_dll(Security, 'kSecModeCBCKey')) setattr( Security, 'kSecTransformInputAttributeName', CFStringRef.in_dll(Security, 'kSecTransformInputAttributeName') ) setattr(Security, 'kSecDigestTypeAttribute', CFStringRef.in_dll(Security, 'kSecDigestTypeAttribute')) setattr(Security, 'kSecDigestLengthAttribute', CFStringRef.in_dll(Security, 'kSecDigestLengthAttribute')) setattr(Security, 'kSecIVKey', CFStringRef.in_dll(Security, 'kSecIVKey')) setattr(Security, 'kSecAttrIsExtractable', CFStringRef.in_dll(Security, 'kSecAttrIsExtractable')) setattr(Security, 'kSecDigestSHA1', CFStringRef.in_dll(Security, 'kSecDigestSHA1')) setattr(Security, 'kSecDigestSHA2', CFStringRef.in_dll(Security, 'kSecDigestSHA2')) setattr(Security, 'kSecDigestMD5', CFStringRef.in_dll(Security, 'kSecDigestMD5')) setattr(Security, 'kSecAttrKeyType', CFStringRef.in_dll(Security, 'kSecAttrKeyType')) setattr(Security, 'kSecAttrKeyTypeRSA', CFTypeRef.in_dll(Security, 'kSecAttrKeyTypeRSA')) setattr(Security, 'kSecAttrKeyTypeDSA', CFTypeRef.in_dll(Security, 'kSecAttrKeyTypeDSA')) setattr(Security, 'kSecAttrKeyTypeECDSA', CFTypeRef.in_dll(Security, 'kSecAttrKeyTypeECDSA')) setattr(Security, 'kSecAttrKeySizeInBits', CFStringRef.in_dll(Security, 'kSecAttrKeySizeInBits')) setattr(Security, 'kSecAttrLabel', CFStringRef.in_dll(Security, 'kSecAttrLabel')) setattr(Security, 'kSecAttrCanSign', CFTypeRef.in_dll(Security, 'kSecAttrCanSign')) setattr(Security, 'kSecAttrCanVerify', CFTypeRef.in_dll(Security, 'kSecAttrCanVerify')) setattr(Security, 'kSecAttrKeyTypeAES', CFTypeRef.in_dll(Security, 'kSecAttrKeyTypeAES')) setattr(Security, 'kSecAttrKeyTypeRC4', CFTypeRef.in_dll(Security, 'kSecAttrKeyTypeRC4')) setattr(Security, 'kSecAttrKeyTypeRC2', CFTypeRef.in_dll(Security, 'kSecAttrKeyTypeRC2')) setattr(Security, 'kSecAttrKeyType3DES', CFTypeRef.in_dll(Security, 'kSecAttrKeyType3DES')) setattr(Security, 'kSecAttrKeyTypeDES', CFTypeRef.in_dll(Security, 'kSecAttrKeyTypeDES')) except (AttributeError): raise FFIEngineError('Error initializing ctypes') oscrypto-1.3.0/oscrypto/_mac/asymmetric.py000066400000000000000000001757341421476274700207440ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from base64 import b32encode import os import shutil import tempfile from .._asn1 import ( Certificate as Asn1Certificate, ECDomainParameters, Integer, KeyExchangeAlgorithm, Null, PrivateKeyInfo, PublicKeyAlgorithm, PublicKeyInfo, RSAPublicKey, ) from .._asymmetric import ( _CertificateBase, _fingerprint, _parse_pkcs12, _PrivateKeyBase, _PublicKeyBase, _unwrap_private_key_info, parse_certificate, parse_private, parse_public, ) from .._errors import pretty_message from .._ffi import new, unwrap, bytes_from_buffer, buffer_from_bytes, deref, null, is_null, pointer_set from ._security import Security, SecurityConst, handle_sec_error, osx_version_info from ._core_foundation import CoreFoundation, CFHelpers, handle_cf_error from .util import rand_bytes from ..errors import AsymmetricKeyError, IncompleteAsymmetricKeyError, SignatureError from .._pkcs1 import add_pss_padding, verify_pss_padding, remove_pkcs1v15_encryption_padding from .._types import type_name, str_cls, byte_cls, int_types __all__ = [ 'Certificate', 'dsa_sign', 'dsa_verify', 'ecdsa_sign', 'ecdsa_verify', 'generate_pair', 'load_certificate', 'load_pkcs12', 'load_private_key', 'load_public_key', 'parse_pkcs12', 'PrivateKey', 'PublicKey', 'rsa_oaep_decrypt', 'rsa_oaep_encrypt', 'rsa_pkcs1v15_decrypt', 'rsa_pkcs1v15_encrypt', 'rsa_pkcs1v15_sign', 'rsa_pkcs1v15_verify', 'rsa_pss_sign', 'rsa_pss_verify', ] class PrivateKey(_PrivateKeyBase): """ Container for the OS crypto library representation of a private key """ sec_key_ref = None _public_key = None # A reference to the library used in the destructor to make sure it hasn't # been garbage collected by the time this object is garbage collected _lib = None def __init__(self, sec_key_ref, asn1): """ :param sec_key_ref: A Security framework SecKeyRef value from loading/importing the key :param asn1: An asn1crypto.keys.PrivateKeyInfo object """ self.sec_key_ref = sec_key_ref self.asn1 = asn1 self._lib = CoreFoundation @property def public_key(self): """ :return: A PublicKey object corresponding to this private key. """ if self._public_key is None: cf_data_private = None try: # We export here so that Security.framework will fill in the EC # public key for us, instead of us having to compute it cf_data_private_pointer = new(CoreFoundation, 'CFDataRef *') result = Security.SecItemExport(self.sec_key_ref, 0, 0, null(), cf_data_private_pointer) handle_sec_error(result) cf_data_private = unwrap(cf_data_private_pointer) private_key_bytes = CFHelpers.cf_data_to_bytes(cf_data_private) key = parse_private(private_key_bytes) if key.algorithm == 'rsa': public_asn1 = PublicKeyInfo({ 'algorithm': PublicKeyAlgorithm({ 'algorithm': 'rsa', 'parameters': Null() }), 'public_key': RSAPublicKey({ 'modulus': key['private_key'].parsed['modulus'], 'public_exponent': key['private_key'].parsed['public_exponent'], }) }) elif key.algorithm == 'dsa': params = key['private_key_algorithm']['parameters'] public_asn1 = PublicKeyInfo({ 'algorithm': PublicKeyAlgorithm({ 'algorithm': 'dsa', 'parameters': params.copy() }), 'public_key': Integer(pow( params['g'].native, key['private_key'].parsed.native, params['p'].native )) }) elif key.algorithm == 'ec': public_asn1 = PublicKeyInfo({ 'algorithm': PublicKeyAlgorithm({ 'algorithm': 'ec', 'parameters': ECDomainParameters( name='named', value=self.curve ) }), 'public_key': key['private_key'].parsed['public_key'], }) finally: if cf_data_private: CoreFoundation.CFRelease(cf_data_private) self._public_key = _load_key(public_asn1) return self._public_key @property def fingerprint(self): """ Creates a fingerprint that can be compared with a public key to see if the two form a pair. This fingerprint is not compatible with fingerprints generated by any other software. :return: A byte string that is a sha256 hash of selected components (based on the key type) """ if self._fingerprint is None: self._fingerprint = _fingerprint(self.asn1, load_private_key) return self._fingerprint def __del__(self): if self.sec_key_ref: self._lib.CFRelease(self.sec_key_ref) self._lib = None self.sec_key_ref = None class PublicKey(_PublicKeyBase): """ Container for the OS crypto library representation of a public key """ sec_key_ref = None # A reference to the library used in the destructor to make sure it hasn't # been garbage collected by the time this object is garbage collected _lib = None def __init__(self, sec_key_ref, asn1): """ :param sec_key_ref: A Security framework SecKeyRef value from loading/importing the key :param asn1: An asn1crypto.keys.PublicKeyInfo object """ self.sec_key_ref = sec_key_ref self.asn1 = asn1 self._lib = CoreFoundation def __del__(self): if self.sec_key_ref: self._lib.CFRelease(self.sec_key_ref) self._lib = None self.sec_key_ref = None class Certificate(_CertificateBase): """ Container for the OS crypto library representation of a certificate """ sec_certificate_ref = None _public_key = None _self_signed = None def __init__(self, sec_certificate_ref, asn1): """ :param sec_certificate_ref: A Security framework SecCertificateRef value from loading/importing the certificate :param asn1: An asn1crypto.x509.Certificate object """ self.sec_certificate_ref = sec_certificate_ref self.asn1 = asn1 @property def sec_key_ref(self): """ :return: The SecKeyRef of the public key """ return self.public_key.sec_key_ref @property def public_key(self): """ :return: The PublicKey object for the public key this certificate contains """ if not self._public_key and self.sec_certificate_ref: if self.asn1.signature_algo == "rsassa_pss": # macOS doesn't like importing RSA PSS certs, so we treat it like a # traditional RSA cert asn1 = self.asn1.copy() asn1['tbs_certificate']['subject_public_key_info']['algorithm']['algorithm'] = 'rsa' temp_cert = _load_x509(asn1) sec_cert_ref = temp_cert.sec_certificate_ref else: sec_cert_ref = self.sec_certificate_ref sec_public_key_ref_pointer = new(Security, 'SecKeyRef *') res = Security.SecCertificateCopyPublicKey(sec_cert_ref, sec_public_key_ref_pointer) handle_sec_error(res) sec_public_key_ref = unwrap(sec_public_key_ref_pointer) self._public_key = PublicKey(sec_public_key_ref, self.asn1['tbs_certificate']['subject_public_key_info']) return self._public_key @property def self_signed(self): """ :return: A boolean - if the certificate is self-signed """ if self._self_signed is None: self._self_signed = False if self.asn1.self_signed in set(['yes', 'maybe']): signature_algo = self.asn1['signature_algorithm'].signature_algo hash_algo = self.asn1['signature_algorithm'].hash_algo if signature_algo == 'rsassa_pkcs1v15': verify_func = rsa_pkcs1v15_verify elif signature_algo == 'rsassa_pss': verify_func = rsa_pss_verify elif signature_algo == 'dsa': verify_func = dsa_verify elif signature_algo == 'ecdsa': verify_func = ecdsa_verify else: raise OSError(pretty_message( ''' Unable to verify the signature of the certificate since it uses the unsupported algorithm %s ''', signature_algo )) try: verify_func( self.public_key, self.asn1['signature_value'].native, self.asn1['tbs_certificate'].dump(), hash_algo ) self._self_signed = True except (SignatureError): pass return self._self_signed def __del__(self): if self._public_key: self._public_key.__del__() self._public_key = None if self.sec_certificate_ref: CoreFoundation.CFRelease(self.sec_certificate_ref) self.sec_certificate_ref = None def generate_pair(algorithm, bit_size=None, curve=None): """ Generates a public/private key pair :param algorithm: The key algorithm - "rsa", "dsa" or "ec" :param bit_size: An integer - used for "rsa" and "dsa". For "rsa" the value maye be 1024, 2048, 3072 or 4096. For "dsa" the value may be 1024. :param curve: A unicode string - used for "ec" keys. Valid values include "secp256r1", "secp384r1" and "secp521r1". :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A 2-element tuple of (PublicKey, PrivateKey). The contents of each key may be saved by calling .asn1.dump(). """ if algorithm not in set(['rsa', 'dsa', 'ec']): raise ValueError(pretty_message( ''' algorithm must be one of "rsa", "dsa", "ec", not %s ''', repr(algorithm) )) if algorithm == 'rsa': if bit_size not in set([1024, 2048, 3072, 4096]): raise ValueError(pretty_message( ''' bit_size must be one of 1024, 2048, 3072, 4096, not %s ''', repr(bit_size) )) elif algorithm == 'dsa': if bit_size not in set([1024]): raise ValueError(pretty_message( ''' bit_size must be 1024, not %s ''', repr(bit_size) )) elif algorithm == 'ec': if curve not in set(['secp256r1', 'secp384r1', 'secp521r1']): raise ValueError(pretty_message( ''' curve must be one of "secp256r1", "secp384r1", "secp521r1", not %s ''', repr(curve) )) cf_dict = None public_key_ref = None private_key_ref = None cf_data_public = None cf_data_private = None cf_string = None sec_access_ref = None sec_keychain_ref = None temp_dir = None try: alg_id = { 'dsa': SecurityConst.CSSM_ALGID_DSA, 'ec': SecurityConst.CSSM_ALGID_ECDSA, 'rsa': SecurityConst.CSSM_ALGID_RSA, }[algorithm] if algorithm == 'ec': key_size = { 'secp256r1': 256, 'secp384r1': 384, 'secp521r1': 521, }[curve] else: key_size = bit_size private_key_pointer = new(Security, 'SecKeyRef *') public_key_pointer = new(Security, 'SecKeyRef *') cf_string = CFHelpers.cf_string_from_unicode("Temporary oscrypto key") # We used to use SecKeyGeneratePair() for everything but DSA keys, but due to changes # in macOS security, we can't reliably access the default keychain, and instead # get an "OSError: User interaction is not allowed." result. Because of this we now # use SecKeyCreatePair() for everything, but we even use a throw-away keychain. passphrase_len = 16 rand_data = rand_bytes(10 + passphrase_len) passphrase = rand_data[10:] temp_filename = b32encode(rand_data[:10]).decode('utf-8') temp_dir = tempfile.mkdtemp() temp_path = os.path.join(temp_dir, temp_filename).encode('utf-8') sec_keychain_ref_pointer = new(Security, 'SecKeychainRef *') result = Security.SecKeychainCreate( temp_path, passphrase_len, passphrase, False, null(), sec_keychain_ref_pointer ) handle_sec_error(result) sec_keychain_ref = unwrap(sec_keychain_ref_pointer) sec_access_ref_pointer = new(Security, 'SecAccessRef *') result = Security.SecAccessCreate(cf_string, null(), sec_access_ref_pointer) handle_sec_error(result) sec_access_ref = unwrap(sec_access_ref_pointer) result = Security.SecKeyCreatePair( sec_keychain_ref, alg_id, key_size, 0, SecurityConst.CSSM_KEYUSE_VERIFY, SecurityConst.CSSM_KEYATTR_EXTRACTABLE | SecurityConst.CSSM_KEYATTR_PERMANENT, SecurityConst.CSSM_KEYUSE_SIGN, SecurityConst.CSSM_KEYATTR_EXTRACTABLE | SecurityConst.CSSM_KEYATTR_PERMANENT, sec_access_ref, public_key_pointer, private_key_pointer ) handle_sec_error(result) public_key_ref = unwrap(public_key_pointer) private_key_ref = unwrap(private_key_pointer) cf_data_public_pointer = new(CoreFoundation, 'CFDataRef *') result = Security.SecItemExport(public_key_ref, 0, 0, null(), cf_data_public_pointer) handle_sec_error(result) cf_data_public = unwrap(cf_data_public_pointer) public_key_bytes = CFHelpers.cf_data_to_bytes(cf_data_public) cf_data_private_pointer = new(CoreFoundation, 'CFDataRef *') result = Security.SecItemExport(private_key_ref, 0, 0, null(), cf_data_private_pointer) handle_sec_error(result) cf_data_private = unwrap(cf_data_private_pointer) private_key_bytes = CFHelpers.cf_data_to_bytes(cf_data_private) # Clean the new keys out of the keychain result = Security.SecKeychainItemDelete(public_key_ref) handle_sec_error(result) result = Security.SecKeychainItemDelete(private_key_ref) handle_sec_error(result) finally: if cf_dict: CoreFoundation.CFRelease(cf_dict) if public_key_ref: CoreFoundation.CFRelease(public_key_ref) if private_key_ref: CoreFoundation.CFRelease(private_key_ref) if cf_data_public: CoreFoundation.CFRelease(cf_data_public) if cf_data_private: CoreFoundation.CFRelease(cf_data_private) if cf_string: CoreFoundation.CFRelease(cf_string) if sec_keychain_ref: Security.SecKeychainDelete(sec_keychain_ref) CoreFoundation.CFRelease(sec_keychain_ref) if temp_dir: shutil.rmtree(temp_dir) if sec_access_ref: CoreFoundation.CFRelease(sec_access_ref) return (load_public_key(public_key_bytes), load_private_key(private_key_bytes)) def generate_dh_parameters(bit_size): """ Generates DH parameters for use with Diffie-Hellman key exchange. Returns a structure in the format of DHParameter defined in PKCS#3, which is also used by the OpenSSL dhparam tool. THIS CAN BE VERY TIME CONSUMING! :param bit_size: The integer bit size of the parameters to generate. Must be between 512 and 4096, and divisible by 64. Recommended secure value as of early 2016 is 2048, with an absolute minimum of 1024. :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: An asn1crypto.algos.DHParameters object. Use oscrypto.asymmetric.dump_dh_parameters() to save to disk for usage with web servers. """ if not isinstance(bit_size, int_types): raise TypeError(pretty_message( ''' bit_size must be an integer, not %s ''', type_name(bit_size) )) if bit_size < 512: raise ValueError('bit_size must be greater than or equal to 512') if bit_size > 4096: raise ValueError('bit_size must be less than or equal to 4096') if bit_size % 64 != 0: raise ValueError('bit_size must be a multiple of 64') public_key_ref = None private_key_ref = None cf_data_public = None cf_data_private = None cf_string = None sec_keychain_ref = None sec_access_ref = None temp_dir = None try: public_key_pointer = new(Security, 'SecKeyRef *') private_key_pointer = new(Security, 'SecKeyRef *') cf_string = CFHelpers.cf_string_from_unicode("Temporary oscrypto key") passphrase_len = 16 rand_data = rand_bytes(10 + passphrase_len) passphrase = rand_data[10:] temp_filename = b32encode(rand_data[:10]).decode('utf-8') temp_dir = tempfile.mkdtemp() temp_path = os.path.join(temp_dir, temp_filename).encode('utf-8') sec_keychain_ref_pointer = new(Security, 'SecKeychainRef *') result = Security.SecKeychainCreate( temp_path, passphrase_len, passphrase, False, null(), sec_keychain_ref_pointer ) handle_sec_error(result) sec_keychain_ref = unwrap(sec_keychain_ref_pointer) sec_access_ref_pointer = new(Security, 'SecAccessRef *') result = Security.SecAccessCreate(cf_string, null(), sec_access_ref_pointer) handle_sec_error(result) sec_access_ref = unwrap(sec_access_ref_pointer) result = Security.SecKeyCreatePair( sec_keychain_ref, SecurityConst.CSSM_ALGID_DH, bit_size, 0, 0, SecurityConst.CSSM_KEYATTR_EXTRACTABLE | SecurityConst.CSSM_KEYATTR_PERMANENT, 0, SecurityConst.CSSM_KEYATTR_EXTRACTABLE | SecurityConst.CSSM_KEYATTR_PERMANENT, sec_access_ref, public_key_pointer, private_key_pointer ) handle_sec_error(result) public_key_ref = unwrap(public_key_pointer) private_key_ref = unwrap(private_key_pointer) cf_data_private_pointer = new(CoreFoundation, 'CFDataRef *') result = Security.SecItemExport(private_key_ref, 0, 0, null(), cf_data_private_pointer) handle_sec_error(result) cf_data_private = unwrap(cf_data_private_pointer) private_key_bytes = CFHelpers.cf_data_to_bytes(cf_data_private) # Clean the new keys out of the keychain result = Security.SecKeychainItemDelete(public_key_ref) handle_sec_error(result) result = Security.SecKeychainItemDelete(private_key_ref) handle_sec_error(result) return KeyExchangeAlgorithm.load(private_key_bytes)['parameters'] finally: if public_key_ref: CoreFoundation.CFRelease(public_key_ref) if private_key_ref: CoreFoundation.CFRelease(private_key_ref) if cf_data_public: CoreFoundation.CFRelease(cf_data_public) if cf_data_private: CoreFoundation.CFRelease(cf_data_private) if cf_string: CoreFoundation.CFRelease(cf_string) if sec_keychain_ref: Security.SecKeychainDelete(sec_keychain_ref) CoreFoundation.CFRelease(sec_keychain_ref) if temp_dir: shutil.rmtree(temp_dir) if sec_access_ref: CoreFoundation.CFRelease(sec_access_ref) def load_certificate(source): """ Loads an x509 certificate into a Certificate object :param source: A byte string of file contents, a unicode string filename or an asn1crypto.x509.Certificate object :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A Certificate object """ if isinstance(source, Asn1Certificate): certificate = source elif isinstance(source, byte_cls): certificate = parse_certificate(source) elif isinstance(source, str_cls): with open(source, 'rb') as f: certificate = parse_certificate(f.read()) else: raise TypeError(pretty_message( ''' source must be a byte string, unicode string or asn1crypto.x509.Certificate object, not %s ''', type_name(source) )) return _load_x509(certificate) def _load_x509(certificate): """ Loads an ASN.1 object of an x509 certificate into a Certificate object :param certificate: An asn1crypto.x509.Certificate object :return: A Certificate object """ source = certificate.dump() cf_source = None try: cf_source = CFHelpers.cf_data_from_bytes(source) sec_key_ref = Security.SecCertificateCreateWithData(CoreFoundation.kCFAllocatorDefault, cf_source) return Certificate(sec_key_ref, certificate) finally: if cf_source: CoreFoundation.CFRelease(cf_source) def load_private_key(source, password=None): """ Loads a private key into a PrivateKey object :param source: A byte string of file contents, a unicode string filename or an asn1crypto.keys.PrivateKeyInfo object :param password: A byte or unicode string to decrypt the private key file. Unicode strings will be encoded using UTF-8. Not used is the source is a PrivateKeyInfo object. :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type oscrypto.errors.AsymmetricKeyError - when the private key is incompatible with the OS crypto library OSError - when an error is returned by the OS crypto library :return: A PrivateKey object """ if isinstance(source, PrivateKeyInfo): private_object = source else: if password is not None: if isinstance(password, str_cls): password = password.encode('utf-8') if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) if isinstance(source, str_cls): with open(source, 'rb') as f: source = f.read() elif not isinstance(source, byte_cls): raise TypeError(pretty_message( ''' source must be a byte string, unicode string or asn1crypto.keys.PrivateKeyInfo object, not %s ''', type_name(source) )) private_object = parse_private(source, password) return _load_key(private_object) def load_public_key(source): """ Loads a public key into a PublicKey object :param source: A byte string of file contents, a unicode string filename or an asn1crypto.keys.PublicKeyInfo object :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type oscrypto.errors.AsymmetricKeyError - when the public key is incompatible with the OS crypto library OSError - when an error is returned by the OS crypto library :return: A PublicKey object """ if isinstance(source, PublicKeyInfo): public_key = source elif isinstance(source, byte_cls): public_key = parse_public(source) elif isinstance(source, str_cls): with open(source, 'rb') as f: public_key = parse_public(f.read()) else: raise TypeError(pretty_message( ''' source must be a byte string, unicode string or asn1crypto.keys.PublicKeyInfo object, not %s ''', type_name(source) )) return _load_key(public_key) def _load_key(key_object): """ Common code to load public and private keys into PublicKey and PrivateKey objects :param key_object: An asn1crypto.keys.PublicKeyInfo or asn1crypto.keys.PrivateKeyInfo object :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type oscrypto.errors.AsymmetricKeyError - when the key is incompatible with the OS crypto library OSError - when an error is returned by the OS crypto library :return: A PublicKey or PrivateKey object """ if key_object.algorithm == 'ec': curve_type, details = key_object.curve if curve_type != 'named': raise AsymmetricKeyError('OS X only supports EC keys using named curves') if details not in set(['secp256r1', 'secp384r1', 'secp521r1']): raise AsymmetricKeyError(pretty_message( ''' OS X only supports EC keys using the named curves secp256r1, secp384r1 and secp521r1 ''' )) elif key_object.algorithm == 'dsa' and key_object.hash_algo == 'sha2': raise AsymmetricKeyError(pretty_message( ''' OS X only supports DSA keys based on SHA1 (2048 bits or less) - this key is based on SHA2 and is %s bits ''', key_object.bit_size )) elif key_object.algorithm == 'dsa' and key_object.hash_algo is None: raise IncompleteAsymmetricKeyError(pretty_message( ''' The DSA key does not contain the necessary p, q and g parameters and can not be used ''' )) if isinstance(key_object, PublicKeyInfo): if key_object.algorithm == 'rsassa_pss': # We have to masquerade an RSA PSS key as plain RSA or it won't # import properly temp_key_object = key_object.copy() temp_key_object['algorithm']['algorithm'] = 'rsa' source = temp_key_object.dump() else: source = key_object.dump() item_type = SecurityConst.kSecItemTypePublicKey else: source = _unwrap_private_key_info(key_object).dump() item_type = SecurityConst.kSecItemTypePrivateKey cf_source = None keys_array = None attr_array = None try: cf_source = CFHelpers.cf_data_from_bytes(source) format_pointer = new(Security, 'uint32_t *') pointer_set(format_pointer, SecurityConst.kSecFormatOpenSSL) type_pointer = new(Security, 'uint32_t *') pointer_set(type_pointer, item_type) keys_pointer = new(CoreFoundation, 'CFArrayRef *') attr_array = CFHelpers.cf_array_from_list([ Security.kSecAttrIsExtractable ]) import_export_params_pointer = new(Security, 'SecItemImportExportKeyParameters *') import_export_params = unwrap(import_export_params_pointer) import_export_params.version = 0 import_export_params.flags = 0 import_export_params.passphrase = null() import_export_params.alertTitle = null() import_export_params.alertPrompt = null() import_export_params.accessRef = null() import_export_params.keyUsage = null() import_export_params.keyAttributes = attr_array res = Security.SecItemImport( cf_source, null(), format_pointer, type_pointer, 0, import_export_params_pointer, null(), keys_pointer ) handle_sec_error(res) keys_array = unwrap(keys_pointer) length = CoreFoundation.CFArrayGetCount(keys_array) if length > 0: sec_key_ref = CoreFoundation.CFArrayGetValueAtIndex(keys_array, 0) CoreFoundation.CFRetain(sec_key_ref) if item_type == SecurityConst.kSecItemTypePublicKey: return PublicKey(sec_key_ref, key_object) if item_type == SecurityConst.kSecItemTypePrivateKey: return PrivateKey(sec_key_ref, key_object) finally: if attr_array: CoreFoundation.CFRelease(attr_array) if keys_array: CoreFoundation.CFRelease(keys_array) if cf_source: CoreFoundation.CFRelease(cf_source) def parse_pkcs12(data, password=None): """ Parses a PKCS#12 ANS.1 DER-encoded structure and extracts certs and keys :param data: A byte string of a DER-encoded PKCS#12 file :param password: A byte string of the password to any encrypted data :raises: ValueError - when any of the parameters are of the wrong type or value OSError - when an error is returned by one of the OS decryption functions :return: A three-element tuple of: 1. An asn1crypto.keys.PrivateKeyInfo object 2. An asn1crypto.x509.Certificate object 3. A list of zero or more asn1crypto.x509.Certificate objects that are "extra" certificates, possibly intermediates from the cert chain """ return _parse_pkcs12(data, password, load_private_key) def load_pkcs12(source, password=None): """ Loads a .p12 or .pfx file into a PrivateKey object and one or more Certificates objects :param source: A byte string of file contents or a unicode string filename :param password: A byte or unicode string to decrypt the PKCS12 file. Unicode strings will be encoded using UTF-8. :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type oscrypto.errors.AsymmetricKeyError - when a contained key is incompatible with the OS crypto library OSError - when an error is returned by the OS crypto library :return: A three-element tuple containing (PrivateKey, Certificate, [Certificate, ...]) """ if password is not None: if isinstance(password, str_cls): password = password.encode('utf-8') if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) if isinstance(source, str_cls): with open(source, 'rb') as f: source = f.read() elif not isinstance(source, byte_cls): raise TypeError(pretty_message( ''' source must be a byte string or a unicode string, not %s ''', type_name(source) )) key_info, cert_info, extra_certs_info = parse_pkcs12(source, password) key = None cert = None if key_info: key = _load_key(key_info) if cert_info: cert = _load_x509(cert_info) extra_certs = [_load_x509(info) for info in extra_certs_info] return (key, cert, extra_certs) def rsa_pkcs1v15_encrypt(certificate_or_public_key, data): """ Encrypts a byte string using an RSA public key or certificate. Uses PKCS#1 v1.5 padding. :param certificate_or_public_key: A PublicKey or Certificate object :param data: A byte string, with a maximum length 11 bytes less than the key length (in bytes) :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the encrypted data """ if not isinstance(certificate_or_public_key, (Certificate, PublicKey)): raise TypeError(pretty_message( ''' certificate_or_public_key must be an instance of the Certificate or PublicKey class, not %s ''', type_name(certificate_or_public_key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) key_length = certificate_or_public_key.byte_size buffer = buffer_from_bytes(key_length) output_length = new(Security, 'size_t *', key_length) result = Security.SecKeyEncrypt( certificate_or_public_key.sec_key_ref, SecurityConst.kSecPaddingPKCS1, data, len(data), buffer, output_length ) handle_sec_error(result) return bytes_from_buffer(buffer, deref(output_length)) def rsa_pkcs1v15_decrypt(private_key, ciphertext): """ Decrypts a byte string using an RSA private key. Uses PKCS#1 v1.5 padding. :param private_key: A PrivateKey object :param ciphertext: A byte string of the encrypted data :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the original plaintext """ if not isinstance(private_key, PrivateKey): raise TypeError(pretty_message( ''' private_key must an instance of the PrivateKey class, not %s ''', type_name(private_key) )) if not isinstance(ciphertext, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(ciphertext) )) key_length = private_key.byte_size buffer = buffer_from_bytes(key_length) output_length = new(Security, 'size_t *', key_length) if osx_version_info < (10, 8): padding = SecurityConst.kSecPaddingNone else: padding = SecurityConst.kSecPaddingPKCS1 result = Security.SecKeyDecrypt( private_key.sec_key_ref, padding, ciphertext, len(ciphertext), buffer, output_length ) handle_sec_error(result) output = bytes_from_buffer(buffer, deref(output_length)) if osx_version_info < (10, 8): output = remove_pkcs1v15_encryption_padding(key_length, output) return output def rsa_oaep_encrypt(certificate_or_public_key, data): """ Encrypts a byte string using an RSA public key or certificate. Uses PKCS#1 OAEP padding with SHA1. :param certificate_or_public_key: A PublicKey or Certificate object :param data: A byte string, with a maximum length 41 bytes (or more) less than the key length (in bytes) :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the encrypted data """ return _encrypt(certificate_or_public_key, data, Security.kSecPaddingOAEPKey) def rsa_oaep_decrypt(private_key, ciphertext): """ Decrypts a byte string using an RSA private key. Uses PKCS#1 OAEP padding with SHA1. :param private_key: A PrivateKey object :param ciphertext: A byte string of the encrypted data :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the original plaintext """ return _decrypt(private_key, ciphertext, Security.kSecPaddingOAEPKey) def _encrypt(certificate_or_public_key, data, padding): """ Encrypts plaintext using an RSA public key or certificate :param certificate_or_public_key: A Certificate or PublicKey object :param data: The plaintext - a byte string :param padding: The padding mode to use, specified as a kSecPadding*Key value :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the ciphertext """ if not isinstance(certificate_or_public_key, (Certificate, PublicKey)): raise TypeError(pretty_message( ''' certificate_or_public_key must be an instance of the Certificate or PublicKey class, not %s ''', type_name(certificate_or_public_key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if not padding: raise ValueError('padding must be specified') cf_data = None sec_transform = None try: cf_data = CFHelpers.cf_data_from_bytes(data) error_pointer = new(CoreFoundation, 'CFErrorRef *') sec_transform = Security.SecEncryptTransformCreate( certificate_or_public_key.sec_key_ref, error_pointer ) handle_cf_error(error_pointer) if padding: Security.SecTransformSetAttribute( sec_transform, Security.kSecPaddingKey, padding, error_pointer ) handle_cf_error(error_pointer) Security.SecTransformSetAttribute( sec_transform, Security.kSecTransformInputAttributeName, cf_data, error_pointer ) handle_cf_error(error_pointer) ciphertext = Security.SecTransformExecute(sec_transform, error_pointer) handle_cf_error(error_pointer) return CFHelpers.cf_data_to_bytes(ciphertext) finally: if cf_data: CoreFoundation.CFRelease(cf_data) if sec_transform: CoreFoundation.CFRelease(sec_transform) def _decrypt(private_key, ciphertext, padding): """ Decrypts RSA ciphertext using a private key :param private_key: A PrivateKey object :param ciphertext: The ciphertext - a byte string :param padding: The padding mode to use, specified as a kSecPadding*Key value :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if not isinstance(private_key, PrivateKey): raise TypeError(pretty_message( ''' private_key must be an instance of the PrivateKey class, not %s ''', type_name(private_key) )) if not isinstance(ciphertext, byte_cls): raise TypeError(pretty_message( ''' ciphertext must be a byte string, not %s ''', type_name(ciphertext) )) if not padding: raise ValueError('padding must be specified') cf_data = None sec_transform = None try: cf_data = CFHelpers.cf_data_from_bytes(ciphertext) error_pointer = new(CoreFoundation, 'CFErrorRef *') sec_transform = Security.SecDecryptTransformCreate( private_key.sec_key_ref, error_pointer ) handle_cf_error(error_pointer) Security.SecTransformSetAttribute( sec_transform, Security.kSecPaddingKey, padding, error_pointer ) handle_cf_error(error_pointer) Security.SecTransformSetAttribute( sec_transform, Security.kSecTransformInputAttributeName, cf_data, error_pointer ) handle_cf_error(error_pointer) plaintext = Security.SecTransformExecute(sec_transform, error_pointer) handle_cf_error(error_pointer) return CFHelpers.cf_data_to_bytes(plaintext) finally: if cf_data: CoreFoundation.CFRelease(cf_data) if sec_transform: CoreFoundation.CFRelease(sec_transform) def rsa_pkcs1v15_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies an RSASSA-PKCS-v1.5 signature. When the hash_algorithm is "raw", the operation is identical to RSA public key decryption. That is: the data is not hashed and no ASN.1 structure with an algorithm identifier of the hash algorithm is placed in the encrypted byte string. :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384", "sha512" or "raw" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if certificate_or_public_key.algorithm != 'rsa': raise ValueError('The key specified is not an RSA public key') return _verify(certificate_or_public_key, signature, data, hash_algorithm) def rsa_pss_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies an RSASSA-PSS signature. For the PSS padding the mask gen algorithm will be mgf1 using the same hash algorithm as the signature. The salt length with be the length of the hash algorithm, and the trailer field with be the standard 0xBC byte. :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if not isinstance(certificate_or_public_key, (Certificate, PublicKey)): raise TypeError(pretty_message( ''' certificate_or_public_key must be an instance of the Certificate or PublicKey class, not %s ''', type_name(certificate_or_public_key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) cp_algo = certificate_or_public_key.algorithm if cp_algo != 'rsa' and cp_algo != 'rsassa_pss': raise ValueError('The key specified is not an RSA public key') hash_length = { 'sha1': 20, 'sha224': 28, 'sha256': 32, 'sha384': 48, 'sha512': 64 }.get(hash_algorithm, 0) key_length = certificate_or_public_key.byte_size buffer = buffer_from_bytes(key_length) output_length = new(Security, 'size_t *', key_length) result = Security.SecKeyEncrypt( certificate_or_public_key.sec_key_ref, SecurityConst.kSecPaddingNone, signature, len(signature), buffer, output_length ) handle_sec_error(result) plaintext = bytes_from_buffer(buffer, deref(output_length)) if not verify_pss_padding(hash_algorithm, hash_length, certificate_or_public_key.bit_size, data, plaintext): raise SignatureError('Signature is invalid') def dsa_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies a DSA signature :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if certificate_or_public_key.algorithm != 'dsa': raise ValueError('The key specified is not a DSA public key') return _verify(certificate_or_public_key, signature, data, hash_algorithm) def ecdsa_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies an ECDSA signature :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if certificate_or_public_key.algorithm != 'ec': raise ValueError('The key specified is not an EC public key') return _verify(certificate_or_public_key, signature, data, hash_algorithm) def _verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies an RSA, DSA or ECDSA signature :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if not isinstance(certificate_or_public_key, (Certificate, PublicKey)): raise TypeError(pretty_message( ''' certificate_or_public_key must be an instance of the Certificate or PublicKey class, not %s ''', type_name(certificate_or_public_key) )) if not isinstance(signature, byte_cls): raise TypeError(pretty_message( ''' signature must be a byte string, not %s ''', type_name(signature) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) valid_hash_algorithms = set(['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512']) if certificate_or_public_key.algorithm == 'rsa': valid_hash_algorithms |= set(['raw']) if hash_algorithm not in valid_hash_algorithms: valid_hash_algorithms_error = '"md5", "sha1", "sha224", "sha256", "sha384", "sha512"' if certificate_or_public_key.algorithm == 'rsa': valid_hash_algorithms_error += ', "raw"' raise ValueError(pretty_message( ''' hash_algorithm must be one of %s, not %s ''', valid_hash_algorithms_error, repr(hash_algorithm) )) if certificate_or_public_key.algorithm == 'rsa' and hash_algorithm == 'raw': if len(data) > certificate_or_public_key.byte_size - 11: raise ValueError(pretty_message( ''' data must be 11 bytes shorter than the key size when hash_algorithm is "raw" - key size is %s bytes, but data is %s bytes long ''', certificate_or_public_key.byte_size, len(data) )) result = Security.SecKeyRawVerify( certificate_or_public_key.sec_key_ref, SecurityConst.kSecPaddingPKCS1, data, len(data), signature, len(signature) ) # errSSLCrypto is returned in some situations on macOS 10.12 if result == SecurityConst.errSecVerifyFailed or result == SecurityConst.errSSLCrypto: raise SignatureError('Signature is invalid') handle_sec_error(result) return cf_signature = None cf_data = None cf_hash_length = None sec_transform = None try: error_pointer = new(CoreFoundation, 'CFErrorRef *') cf_signature = CFHelpers.cf_data_from_bytes(signature) sec_transform = Security.SecVerifyTransformCreate( certificate_or_public_key.sec_key_ref, cf_signature, error_pointer ) handle_cf_error(error_pointer) hash_constant = { 'md5': Security.kSecDigestMD5, 'sha1': Security.kSecDigestSHA1, 'sha224': Security.kSecDigestSHA2, 'sha256': Security.kSecDigestSHA2, 'sha384': Security.kSecDigestSHA2, 'sha512': Security.kSecDigestSHA2 }[hash_algorithm] Security.SecTransformSetAttribute( sec_transform, Security.kSecDigestTypeAttribute, hash_constant, error_pointer ) handle_cf_error(error_pointer) if hash_algorithm in set(['sha224', 'sha256', 'sha384', 'sha512']): hash_length = { 'sha224': 224, 'sha256': 256, 'sha384': 384, 'sha512': 512 }[hash_algorithm] cf_hash_length = CFHelpers.cf_number_from_integer(hash_length) Security.SecTransformSetAttribute( sec_transform, Security.kSecDigestLengthAttribute, cf_hash_length, error_pointer ) handle_cf_error(error_pointer) if certificate_or_public_key.algorithm == 'rsa': Security.SecTransformSetAttribute( sec_transform, Security.kSecPaddingKey, Security.kSecPaddingPKCS1Key, error_pointer ) handle_cf_error(error_pointer) cf_data = CFHelpers.cf_data_from_bytes(data) Security.SecTransformSetAttribute( sec_transform, Security.kSecTransformInputAttributeName, cf_data, error_pointer ) handle_cf_error(error_pointer) res = Security.SecTransformExecute(sec_transform, error_pointer) if not is_null(error_pointer): error = unwrap(error_pointer) if not is_null(error): raise SignatureError('Signature is invalid') res = bool(CoreFoundation.CFBooleanGetValue(res)) if not res: raise SignatureError('Signature is invalid') finally: if sec_transform: CoreFoundation.CFRelease(sec_transform) if cf_signature: CoreFoundation.CFRelease(cf_signature) if cf_data: CoreFoundation.CFRelease(cf_data) if cf_hash_length: CoreFoundation.CFRelease(cf_hash_length) def rsa_pkcs1v15_sign(private_key, data, hash_algorithm): """ Generates an RSASSA-PKCS-v1.5 signature. When the hash_algorithm is "raw", the operation is identical to RSA private key encryption. That is: the data is not hashed and no ASN.1 structure with an algorithm identifier of the hash algorithm is placed in the encrypted byte string. :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384", "sha512" or "raw" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if private_key.algorithm != 'rsa': raise ValueError('The key specified is not an RSA private key') return _sign(private_key, data, hash_algorithm) def rsa_pss_sign(private_key, data, hash_algorithm): """ Generates an RSASSA-PSS signature. For the PSS padding the mask gen algorithm will be mgf1 using the same hash algorithm as the signature. The salt length with be the length of the hash algorithm, and the trailer field with be the standard 0xBC byte. :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if not isinstance(private_key, PrivateKey): raise TypeError(pretty_message( ''' private_key must be an instance of the PrivateKey class, not %s ''', type_name(private_key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) pk_algo = private_key.algorithm if pk_algo != 'rsa' and pk_algo != 'rsassa_pss': raise ValueError('The key specified is not an RSA private key') hash_length = { 'sha1': 20, 'sha224': 28, 'sha256': 32, 'sha384': 48, 'sha512': 64 }.get(hash_algorithm, 0) encoded_data = add_pss_padding(hash_algorithm, hash_length, private_key.bit_size, data) key_length = private_key.byte_size buffer = buffer_from_bytes(key_length) output_length = new(Security, 'size_t *', key_length) result = Security.SecKeyDecrypt( private_key.sec_key_ref, SecurityConst.kSecPaddingNone, encoded_data, len(encoded_data), buffer, output_length ) handle_sec_error(result) return bytes_from_buffer(buffer, deref(output_length)) def dsa_sign(private_key, data, hash_algorithm): """ Generates a DSA signature :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if private_key.algorithm != 'dsa': raise ValueError('The key specified is not a DSA private key') return _sign(private_key, data, hash_algorithm) def ecdsa_sign(private_key, data, hash_algorithm): """ Generates an ECDSA signature :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if private_key.algorithm != 'ec': raise ValueError('The key specified is not an EC private key') return _sign(private_key, data, hash_algorithm) def _sign(private_key, data, hash_algorithm): """ Generates an RSA, DSA or ECDSA signature :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if not isinstance(private_key, PrivateKey): raise TypeError(pretty_message( ''' private_key must be an instance of PrivateKey, not %s ''', type_name(private_key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) valid_hash_algorithms = set(['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512']) if private_key.algorithm == 'rsa': valid_hash_algorithms |= set(['raw']) if hash_algorithm not in valid_hash_algorithms: valid_hash_algorithms_error = '"md5", "sha1", "sha224", "sha256", "sha384", "sha512"' if private_key.algorithm == 'rsa': valid_hash_algorithms_error += ', "raw"' raise ValueError(pretty_message( ''' hash_algorithm must be one of %s, not %s ''', valid_hash_algorithms_error, repr(hash_algorithm) )) if private_key.algorithm == 'rsa' and hash_algorithm == 'raw': if len(data) > private_key.byte_size - 11: raise ValueError(pretty_message( ''' data must be 11 bytes shorter than the key size when hash_algorithm is "raw" - key size is %s bytes, but data is %s bytes long ''', private_key.byte_size, len(data) )) key_length = private_key.byte_size buffer = buffer_from_bytes(key_length) output_length = new(Security, 'size_t *', key_length) result = Security.SecKeyRawSign( private_key.sec_key_ref, SecurityConst.kSecPaddingPKCS1, data, len(data), buffer, output_length ) handle_sec_error(result) return bytes_from_buffer(buffer, deref(output_length)) cf_signature = None cf_data = None cf_hash_length = None sec_transform = None try: error_pointer = new(CoreFoundation, 'CFErrorRef *') sec_transform = Security.SecSignTransformCreate(private_key.sec_key_ref, error_pointer) handle_cf_error(error_pointer) hash_constant = { 'md5': Security.kSecDigestMD5, 'sha1': Security.kSecDigestSHA1, 'sha224': Security.kSecDigestSHA2, 'sha256': Security.kSecDigestSHA2, 'sha384': Security.kSecDigestSHA2, 'sha512': Security.kSecDigestSHA2 }[hash_algorithm] Security.SecTransformSetAttribute( sec_transform, Security.kSecDigestTypeAttribute, hash_constant, error_pointer ) handle_cf_error(error_pointer) if hash_algorithm in set(['sha224', 'sha256', 'sha384', 'sha512']): hash_length = { 'sha224': 224, 'sha256': 256, 'sha384': 384, 'sha512': 512 }[hash_algorithm] cf_hash_length = CFHelpers.cf_number_from_integer(hash_length) Security.SecTransformSetAttribute( sec_transform, Security.kSecDigestLengthAttribute, cf_hash_length, error_pointer ) handle_cf_error(error_pointer) if private_key.algorithm == 'rsa': Security.SecTransformSetAttribute( sec_transform, Security.kSecPaddingKey, Security.kSecPaddingPKCS1Key, error_pointer ) handle_cf_error(error_pointer) cf_data = CFHelpers.cf_data_from_bytes(data) Security.SecTransformSetAttribute( sec_transform, Security.kSecTransformInputAttributeName, cf_data, error_pointer ) handle_cf_error(error_pointer) cf_signature = Security.SecTransformExecute(sec_transform, error_pointer) handle_cf_error(error_pointer) return CFHelpers.cf_data_to_bytes(cf_signature) finally: if sec_transform: CoreFoundation.CFRelease(sec_transform) if cf_signature: CoreFoundation.CFRelease(cf_signature) if cf_data: CoreFoundation.CFRelease(cf_data) if cf_hash_length: CoreFoundation.CFRelease(cf_hash_length) oscrypto-1.3.0/oscrypto/_mac/symmetric.py000066400000000000000000000522351421476274700205710ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .._errors import pretty_message from .._ffi import new, null from ._core_foundation import CoreFoundation, CFHelpers, handle_cf_error from ._security import Security from .util import rand_bytes from .._types import type_name, byte_cls __all__ = [ 'aes_cbc_no_padding_decrypt', 'aes_cbc_no_padding_encrypt', 'aes_cbc_pkcs7_decrypt', 'aes_cbc_pkcs7_encrypt', 'des_cbc_pkcs5_decrypt', 'des_cbc_pkcs5_encrypt', 'rc2_cbc_pkcs5_decrypt', 'rc2_cbc_pkcs5_encrypt', 'rc4_decrypt', 'rc4_encrypt', 'tripledes_cbc_pkcs5_decrypt', 'tripledes_cbc_pkcs5_encrypt', ] def aes_cbc_no_padding_encrypt(key, data, iv): """ Encrypts plaintext using AES in CBC mode with a 128, 192 or 256 bit key and no padding. This means the ciphertext must be an exact multiple of 16 bytes long. :param key: The encryption key - a byte string either 16, 24 or 32 bytes long :param data: The plaintext - a byte string :param iv: The initialization vector - either a byte string 16-bytes long or None to generate an IV :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A tuple of two byte strings (iv, ciphertext) """ if len(key) not in [16, 24, 32]: raise ValueError(pretty_message( ''' key must be either 16, 24 or 32 bytes (128, 192 or 256 bits) long - is %s ''', len(key) )) if not iv: iv = rand_bytes(16) elif len(iv) != 16: raise ValueError(pretty_message( ''' iv must be 16 bytes long - is %s ''', len(iv) )) if len(data) % 16 != 0: raise ValueError(pretty_message( ''' data must be a multiple of 16 bytes long - is %s ''', len(data) )) return (iv, _encrypt(Security.kSecAttrKeyTypeAES, key, data, iv, Security.kSecPaddingNoneKey)) def aes_cbc_no_padding_decrypt(key, data, iv): """ Decrypts AES ciphertext in CBC mode using a 128, 192 or 256 bit key and no padding. :param key: The encryption key - a byte string either 16, 24 or 32 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string 16-bytes long :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if len(key) not in [16, 24, 32]: raise ValueError(pretty_message( ''' key must be either 16, 24 or 32 bytes (128, 192 or 256 bits) long - is %s ''', len(key) )) if len(iv) != 16: raise ValueError(pretty_message( ''' iv must be 16 bytes long - is %s ''', len(iv) )) return _decrypt(Security.kSecAttrKeyTypeAES, key, data, iv, Security.kSecPaddingNoneKey) def aes_cbc_pkcs7_encrypt(key, data, iv): """ Encrypts plaintext using AES in CBC mode with a 128, 192 or 256 bit key and PKCS#7 padding. :param key: The encryption key - a byte string either 16, 24 or 32 bytes long :param data: The plaintext - a byte string :param iv: The initialization vector - either a byte string 16-bytes long or None to generate an IV :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A tuple of two byte strings (iv, ciphertext) """ if len(key) not in [16, 24, 32]: raise ValueError(pretty_message( ''' key must be either 16, 24 or 32 bytes (128, 192 or 256 bits) long - is %s ''', len(key) )) if not iv: iv = rand_bytes(16) elif len(iv) != 16: raise ValueError(pretty_message( ''' iv must be 16 bytes long - is %s ''', len(iv) )) return (iv, _encrypt(Security.kSecAttrKeyTypeAES, key, data, iv, Security.kSecPaddingPKCS7Key)) def aes_cbc_pkcs7_decrypt(key, data, iv): """ Decrypts AES ciphertext in CBC mode using a 128, 192 or 256 bit key :param key: The encryption key - a byte string either 16, 24 or 32 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string 16-bytes long :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if len(key) not in [16, 24, 32]: raise ValueError(pretty_message( ''' key must be either 16, 24 or 32 bytes (128, 192 or 256 bits) long - is %s ''', len(key) )) if len(iv) != 16: raise ValueError(pretty_message( ''' iv must be 16 bytes long - is %s ''', len(iv) )) return _decrypt(Security.kSecAttrKeyTypeAES, key, data, iv, Security.kSecPaddingPKCS7Key) def rc4_encrypt(key, data): """ Encrypts plaintext using RC4 with a 40-128 bit key :param key: The encryption key - a byte string 5-16 bytes long :param data: The plaintext - a byte string :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the ciphertext """ if len(key) < 5 or len(key) > 16: raise ValueError(pretty_message( ''' key must be 5 to 16 bytes (40 to 128 bits) long - is %s ''', len(key) )) return _encrypt(Security.kSecAttrKeyTypeRC4, key, data, None, None) def rc4_decrypt(key, data): """ Decrypts RC4 ciphertext using a 40-128 bit key :param key: The encryption key - a byte string 5-16 bytes long :param data: The ciphertext - a byte string :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if len(key) < 5 or len(key) > 16: raise ValueError(pretty_message( ''' key must be 5 to 16 bytes (40 to 128 bits) long - is %s ''', len(key) )) return _decrypt(Security.kSecAttrKeyTypeRC4, key, data, None, None) def rc2_cbc_pkcs5_encrypt(key, data, iv): """ Encrypts plaintext using RC2 with a 64 bit key :param key: The encryption key - a byte string 8 bytes long :param data: The plaintext - a byte string :param iv: The 8-byte initialization vector to use - a byte string - set as None to generate an appropriate one :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A tuple of two byte strings (iv, ciphertext) """ if len(key) < 5 or len(key) > 16: raise ValueError(pretty_message( ''' key must be 5 to 16 bytes (40 to 128 bits) long - is %s ''', len(key) )) if not iv: iv = rand_bytes(8) elif len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) return (iv, _encrypt(Security.kSecAttrKeyTypeRC2, key, data, iv, Security.kSecPaddingPKCS5Key)) def rc2_cbc_pkcs5_decrypt(key, data, iv): """ Decrypts RC2 ciphertext using a 64 bit key :param key: The encryption key - a byte string 8 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector used for encryption - a byte string :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if len(key) < 5 or len(key) > 16: raise ValueError(pretty_message( ''' key must be 5 to 16 bytes (40 to 128 bits) long - is %s ''', len(key) )) if len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) return _decrypt(Security.kSecAttrKeyTypeRC2, key, data, iv, Security.kSecPaddingPKCS5Key) def tripledes_cbc_pkcs5_encrypt(key, data, iv): """ Encrypts plaintext using 3DES in either 2 or 3 key mode :param key: The encryption key - a byte string 16 or 24 bytes long (2 or 3 key mode) :param data: The plaintext - a byte string :param iv: The 8-byte initialization vector to use - a byte string - set as None to generate an appropriate one :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A tuple of two byte strings (iv, ciphertext) """ if len(key) != 16 and len(key) != 24: raise ValueError(pretty_message( ''' key must be 16 bytes (2 key) or 24 bytes (3 key) long - %s ''', len(key) )) if not iv: iv = rand_bytes(8) elif len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - %s ''', len(iv) )) # Expand 2-key to actual 24 byte byte string used by cipher if len(key) == 16: key = key + key[0:8] return (iv, _encrypt(Security.kSecAttrKeyType3DES, key, data, iv, Security.kSecPaddingPKCS5Key)) def tripledes_cbc_pkcs5_decrypt(key, data, iv): """ Decrypts 3DES ciphertext in either 2 or 3 key mode :param key: The encryption key - a byte string 16 or 24 bytes long (2 or 3 key mode) :param data: The ciphertext - a byte string :param iv: The initialization vector used for encryption - a byte string :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if len(key) != 16 and len(key) != 24: raise ValueError(pretty_message( ''' key must be 16 bytes (2 key) or 24 bytes (3 key) long - is %s ''', len(key) )) if len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) # Expand 2-key to actual 24 byte byte string used by cipher if len(key) == 16: key = key + key[0:8] return _decrypt(Security.kSecAttrKeyType3DES, key, data, iv, Security.kSecPaddingPKCS5Key) def des_cbc_pkcs5_encrypt(key, data, iv): """ Encrypts plaintext using DES with a 56 bit key :param key: The encryption key - a byte string 8 bytes long (includes error correction bits) :param data: The plaintext - a byte string :param iv: The 8-byte initialization vector to use - a byte string - set as None to generate an appropriate one :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A tuple of two byte strings (iv, ciphertext) """ if len(key) != 8: raise ValueError(pretty_message( ''' key must be 8 bytes (56 bits + 8 parity bits) long - is %s ''', len(key) )) if not iv: iv = rand_bytes(8) elif len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) return (iv, _encrypt(Security.kSecAttrKeyTypeDES, key, data, iv, Security.kSecPaddingPKCS5Key)) def des_cbc_pkcs5_decrypt(key, data, iv): """ Decrypts DES ciphertext using a 56 bit key :param key: The encryption key - a byte string 8 bytes long (includes error correction bits) :param data: The ciphertext - a byte string :param iv: The initialization vector used for encryption - a byte string :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if len(key) != 8: raise ValueError(pretty_message( ''' key must be 8 bytes (56 bits + 8 parity bits) long - is %s ''', len(key) )) if len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) return _decrypt(Security.kSecAttrKeyTypeDES, key, data, iv, Security.kSecPaddingPKCS5Key) def _encrypt(cipher, key, data, iv, padding): """ Encrypts plaintext :param cipher: A kSecAttrKeyType* value that specifies the cipher to use :param key: The encryption key - a byte string 5-16 bytes long :param data: The plaintext - a byte string :param iv: The initialization vector - a byte string - unused for RC4 :param padding: The padding mode to use, specified as a kSecPadding*Key value - unused for RC4 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the ciphertext """ if not isinstance(key, byte_cls): raise TypeError(pretty_message( ''' key must be a byte string, not %s ''', type_name(key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if cipher != Security.kSecAttrKeyTypeRC4 and not isinstance(iv, byte_cls): raise TypeError(pretty_message( ''' iv must be a byte string, not %s ''', type_name(iv) )) if cipher != Security.kSecAttrKeyTypeRC4 and not padding: raise ValueError('padding must be specified') cf_dict = None cf_key = None cf_data = None cf_iv = None sec_key = None sec_transform = None try: cf_dict = CFHelpers.cf_dictionary_from_pairs([(Security.kSecAttrKeyType, cipher)]) cf_key = CFHelpers.cf_data_from_bytes(key) cf_data = CFHelpers.cf_data_from_bytes(data) error_pointer = new(CoreFoundation, 'CFErrorRef *') sec_key = Security.SecKeyCreateFromData(cf_dict, cf_key, error_pointer) handle_cf_error(error_pointer) sec_transform = Security.SecEncryptTransformCreate(sec_key, error_pointer) handle_cf_error(error_pointer) if cipher != Security.kSecAttrKeyTypeRC4: Security.SecTransformSetAttribute(sec_transform, Security.kSecModeCBCKey, null(), error_pointer) handle_cf_error(error_pointer) Security.SecTransformSetAttribute(sec_transform, Security.kSecPaddingKey, padding, error_pointer) handle_cf_error(error_pointer) cf_iv = CFHelpers.cf_data_from_bytes(iv) Security.SecTransformSetAttribute(sec_transform, Security.kSecIVKey, cf_iv, error_pointer) handle_cf_error(error_pointer) Security.SecTransformSetAttribute( sec_transform, Security.kSecTransformInputAttributeName, cf_data, error_pointer ) handle_cf_error(error_pointer) ciphertext = Security.SecTransformExecute(sec_transform, error_pointer) handle_cf_error(error_pointer) return CFHelpers.cf_data_to_bytes(ciphertext) finally: if cf_dict: CoreFoundation.CFRelease(cf_dict) if cf_key: CoreFoundation.CFRelease(cf_key) if cf_data: CoreFoundation.CFRelease(cf_data) if cf_iv: CoreFoundation.CFRelease(cf_iv) if sec_key: CoreFoundation.CFRelease(sec_key) if sec_transform: CoreFoundation.CFRelease(sec_transform) def _decrypt(cipher, key, data, iv, padding): """ Decrypts AES/RC4/RC2/3DES/DES ciphertext :param cipher: A kSecAttrKeyType* value that specifies the cipher to use :param key: The encryption key - a byte string 5-16 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string - unused for RC4 :param padding: The padding mode to use, specified as a kSecPadding*Key value - unused for RC4 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if not isinstance(key, byte_cls): raise TypeError(pretty_message( ''' key must be a byte string, not %s ''', type_name(key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if cipher != Security.kSecAttrKeyTypeRC4 and not isinstance(iv, byte_cls): raise TypeError(pretty_message( ''' iv must be a byte string, not %s ''', type_name(iv) )) if cipher != Security.kSecAttrKeyTypeRC4 and not padding: raise ValueError('padding must be specified') cf_dict = None cf_key = None cf_data = None cf_iv = None sec_key = None sec_transform = None try: cf_dict = CFHelpers.cf_dictionary_from_pairs([(Security.kSecAttrKeyType, cipher)]) cf_key = CFHelpers.cf_data_from_bytes(key) cf_data = CFHelpers.cf_data_from_bytes(data) error_pointer = new(CoreFoundation, 'CFErrorRef *') sec_key = Security.SecKeyCreateFromData(cf_dict, cf_key, error_pointer) handle_cf_error(error_pointer) sec_transform = Security.SecDecryptTransformCreate(sec_key, error_pointer) handle_cf_error(error_pointer) if cipher != Security.kSecAttrKeyTypeRC4: Security.SecTransformSetAttribute(sec_transform, Security.kSecModeCBCKey, null(), error_pointer) handle_cf_error(error_pointer) Security.SecTransformSetAttribute(sec_transform, Security.kSecPaddingKey, padding, error_pointer) handle_cf_error(error_pointer) cf_iv = CFHelpers.cf_data_from_bytes(iv) Security.SecTransformSetAttribute(sec_transform, Security.kSecIVKey, cf_iv, error_pointer) handle_cf_error(error_pointer) Security.SecTransformSetAttribute( sec_transform, Security.kSecTransformInputAttributeName, cf_data, error_pointer ) handle_cf_error(error_pointer) plaintext = Security.SecTransformExecute(sec_transform, error_pointer) handle_cf_error(error_pointer) return CFHelpers.cf_data_to_bytes(plaintext) finally: if cf_dict: CoreFoundation.CFRelease(cf_dict) if cf_key: CoreFoundation.CFRelease(cf_key) if cf_data: CoreFoundation.CFRelease(cf_data) if cf_iv: CoreFoundation.CFRelease(cf_iv) if sec_key: CoreFoundation.CFRelease(sec_key) if sec_transform: CoreFoundation.CFRelease(sec_transform) oscrypto-1.3.0/oscrypto/_mac/tls.py000066400000000000000000001476251421476274700173670ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import datetime import sys import re import socket as socket_ import select import numbers import errno import weakref from ._security import Security, osx_version_info, handle_sec_error, SecurityConst from ._core_foundation import CoreFoundation, handle_cf_error, CFHelpers from .._asn1 import ( Certificate as Asn1Certificate, int_to_bytes, timezone, ) from .._errors import pretty_message from .._ffi import ( array_from_pointer, array_set, buffer_from_bytes, bytes_from_buffer, callback, cast, deref, new, null, pointer_set, struct, struct_bytes, unwrap, write_to_buffer, ) from .._types import type_name, str_cls, byte_cls, int_types from .._cipher_suites import CIPHER_SUITE_MAP from .util import rand_bytes from ..errors import TLSError, TLSDisconnectError, TLSGracefulDisconnectError from .._tls import ( detect_client_auth_request, detect_other_protocol, extract_chain, get_dh_params_length, parse_session_info, raise_client_auth, raise_dh_params, raise_disconnection, raise_expired_not_yet_valid, raise_handshake, raise_hostname, raise_lifetime_too_long, raise_no_issuer, raise_protocol_error, raise_protocol_version, raise_revoked, raise_self_signed, raise_verification, raise_weak_signature, ) from .asymmetric import load_certificate, Certificate from ..keys import parse_certificate if sys.version_info < (3,): range = xrange # noqa if sys.version_info < (3, 7): Pattern = re._pattern_type else: Pattern = re.Pattern __all__ = [ 'TLSSession', 'TLSSocket', ] _PROTOCOL_STRING_CONST_MAP = { 'SSLv2': SecurityConst.kSSLProtocol2, 'SSLv3': SecurityConst.kSSLProtocol3, 'TLSv1': SecurityConst.kTLSProtocol1, 'TLSv1.1': SecurityConst.kTLSProtocol11, 'TLSv1.2': SecurityConst.kTLSProtocol12, } _PROTOCOL_CONST_STRING_MAP = { SecurityConst.kSSLProtocol2: 'SSLv2', SecurityConst.kSSLProtocol3: 'SSLv3', SecurityConst.kTLSProtocol1: 'TLSv1', SecurityConst.kTLSProtocol11: 'TLSv1.1', SecurityConst.kTLSProtocol12: 'TLSv1.2', } _line_regex = re.compile(b'(\r\n|\r|\n)') _cipher_blacklist_regex = re.compile('anon|PSK|SEED|RC4|MD5|NULL|CAMELLIA|ARIA|SRP|KRB5|EXPORT|(? 0.0: read_ready, _, _ = select.select([socket], [], [], timeout) if len(read_ready) == 0: raise socket_.error(errno.EAGAIN, 'timed out') chunk = socket.recv(bytes_requested - len(data)) data += chunk if chunk == b'': if len(data) == 0: if timeout is None: return SecurityConst.errSSLClosedNoNotify return SecurityConst.errSSLClosedAbort break except (socket_.error) as e: error = e.errno if error is not None and error != errno.EAGAIN: if error == errno.ECONNRESET or error == errno.EPIPE: return SecurityConst.errSSLClosedNoNotify return SecurityConst.errSSLClosedAbort if self and not self._done_handshake: # SecureTransport doesn't bother to check if the TLS record header # is valid before asking to read more data, which can result in # connection hangs. Here we do basic checks to get around the issue. if len(data) >= 3 and len(self._server_hello) == 0: # Check to ensure it is an alert or handshake first valid_record_type = data[0:1] in set([b'\x15', b'\x16']) # Check if the protocol version is SSL 3.0 or TLS 1.0-1.3 valid_protocol_version = data[1:3] in set([ b'\x03\x00', b'\x03\x01', b'\x03\x02', b'\x03\x03', b'\x03\x04' ]) if not valid_record_type or not valid_protocol_version: self._server_hello += data + _read_remaining(socket) return SecurityConst.errSSLProtocol self._server_hello += data write_to_buffer(data_buffer, data) pointer_set(data_length_pointer, len(data)) if len(data) != bytes_requested: return SecurityConst.errSSLWouldBlock return 0 except (KeyboardInterrupt) as e: if self: self._exception = e return SecurityConst.errSSLClosedAbort def _read_remaining(socket): """ Reads everything available from the socket - used for debugging when there is a protocol error :param socket: The socket to read from :return: A byte string of the remaining data """ output = b'' old_timeout = socket.gettimeout() try: socket.settimeout(0.0) output += socket.recv(8192) except (socket_.error): pass finally: socket.settimeout(old_timeout) return output def _write_callback(connection_id, data_buffer, data_length_pointer): """ Callback called by Secure Transport to actually write to the socket :param connection_id: An integer identifying the connection :param data_buffer: A char pointer FFI type containing the data to write :param data_length_pointer: A size_t pointer FFI type of the amount of data to write. Will be overwritten with the amount of data actually written on return. :return: An integer status code of the result - 0 for success """ try: self = _connection_refs.get(connection_id) if not self: socket = _socket_refs.get(connection_id) else: socket = self._socket if not self and not socket: return 0 data_length = deref(data_length_pointer) data = bytes_from_buffer(data_buffer, data_length) if self and not self._done_handshake: self._client_hello += data error = None try: sent = socket.send(data) except (socket_.error) as e: error = e.errno if error is not None and error != errno.EAGAIN: if error == errno.ECONNRESET or error == errno.EPIPE: return SecurityConst.errSSLClosedNoNotify return SecurityConst.errSSLClosedAbort if sent != data_length: pointer_set(data_length_pointer, sent) return SecurityConst.errSSLWouldBlock return 0 except (KeyboardInterrupt) as e: self._exception = e return SecurityConst.errSSLPeerUserCancelled _read_callback_pointer = callback(Security, 'SSLReadFunc', _read_callback) _write_callback_pointer = callback(Security, 'SSLWriteFunc', _write_callback) class TLSSession(object): """ A TLS session object that multiple TLSSocket objects can share for the sake of session reuse """ _protocols = None _ciphers = None _manual_validation = None _extra_trust_roots = None _peer_id = None def __init__(self, protocol=None, manual_validation=False, extra_trust_roots=None): """ :param protocol: A unicode string or set of unicode strings representing allowable protocols to negotiate with the server: - "TLSv1.2" - "TLSv1.1" - "TLSv1" - "SSLv3" Default is: {"TLSv1", "TLSv1.1", "TLSv1.2"} :param manual_validation: If certificate and certificate path validation should be skipped and left to the developer to implement :param extra_trust_roots: A list containing one or more certificates to be treated as trust roots, in one of the following formats: - A byte string of the DER encoded certificate - A unicode string of the certificate filename - An asn1crypto.x509.Certificate object - An oscrypto.asymmetric.Certificate object :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if not isinstance(manual_validation, bool): raise TypeError(pretty_message( ''' manual_validation must be a boolean, not %s ''', type_name(manual_validation) )) self._manual_validation = manual_validation if protocol is None: protocol = set(['TLSv1', 'TLSv1.1', 'TLSv1.2']) if isinstance(protocol, str_cls): protocol = set([protocol]) elif not isinstance(protocol, set): raise TypeError(pretty_message( ''' protocol must be a unicode string or set of unicode strings, not %s ''', type_name(protocol) )) unsupported_protocols = protocol - set(['SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2']) if unsupported_protocols: raise ValueError(pretty_message( ''' protocol must contain only the unicode strings "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", not %s ''', repr(unsupported_protocols) )) self._protocols = protocol self._extra_trust_roots = [] if extra_trust_roots: for extra_trust_root in extra_trust_roots: if isinstance(extra_trust_root, Certificate): extra_trust_root = extra_trust_root.asn1 elif isinstance(extra_trust_root, byte_cls): extra_trust_root = parse_certificate(extra_trust_root) elif isinstance(extra_trust_root, str_cls): with open(extra_trust_root, 'rb') as f: extra_trust_root = parse_certificate(f.read()) elif not isinstance(extra_trust_root, Asn1Certificate): raise TypeError(pretty_message( ''' extra_trust_roots must be a list of byte strings, unicode strings, asn1crypto.x509.Certificate objects or oscrypto.asymmetric.Certificate objects, not %s ''', type_name(extra_trust_root) )) self._extra_trust_roots.append(extra_trust_root) self._peer_id = rand_bytes(8) class TLSSocket(object): """ A wrapper around a socket.socket that adds TLS """ _socket = None _session = None _exception = None _session_context = None _decrypted_bytes = None _hostname = None _certificate = None _intermediates = None _protocol = None _cipher_suite = None _compression = None _session_id = None _session_ticket = None _done_handshake = None _server_hello = None _client_hello = None _local_closed = False _gracefully_closed = False _connection_id = None @classmethod def wrap(cls, socket, hostname, session=None): """ Takes an existing socket and adds TLS :param socket: A socket.socket object to wrap with TLS :param hostname: A unicode string of the hostname or IP the socket is connected to :param session: An existing TLSSession object to allow for session reuse, specific protocol or manual certificate validation :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if not isinstance(socket, socket_.socket): raise TypeError(pretty_message( ''' socket must be an instance of socket.socket, not %s ''', type_name(socket) )) if not isinstance(hostname, str_cls): raise TypeError(pretty_message( ''' hostname must be a unicode string, not %s ''', type_name(hostname) )) if session is not None and not isinstance(session, TLSSession): raise TypeError(pretty_message( ''' session must be an instance of oscrypto.tls.TLSSession, not %s ''', type_name(session) )) new_socket = cls(None, None, session=session) new_socket._socket = socket new_socket._hostname = hostname new_socket._handshake() return new_socket def __init__(self, address, port, timeout=10, session=None): """ :param address: A unicode string of the domain name or IP address to connect to :param port: An integer of the port number to connect to :param timeout: An integer timeout to use for the socket :param session: An oscrypto.tls.TLSSession object to allow for session reuse and controlling the protocols and validation performed """ self._done_handshake = False self._server_hello = b'' self._client_hello = b'' self._decrypted_bytes = b'' if address is None and port is None: self._socket = None else: if not isinstance(address, str_cls): raise TypeError(pretty_message( ''' address must be a unicode string, not %s ''', type_name(address) )) if not isinstance(port, int_types): raise TypeError(pretty_message( ''' port must be an integer, not %s ''', type_name(port) )) if timeout is not None and not isinstance(timeout, numbers.Number): raise TypeError(pretty_message( ''' timeout must be a number, not %s ''', type_name(timeout) )) self._socket = socket_.create_connection((address, port), timeout) self._socket.settimeout(timeout) if session is None: session = TLSSession() elif not isinstance(session, TLSSession): raise TypeError(pretty_message( ''' session must be an instance of oscrypto.tls.TLSSession, not %s ''', type_name(session) )) self._session = session if self._socket: self._hostname = address self._handshake() def _handshake(self): """ Perform an initial TLS handshake """ session_context = None ssl_policy_ref = None crl_search_ref = None crl_policy_ref = None ocsp_search_ref = None ocsp_policy_ref = None policy_array_ref = None trust_ref = None try: if osx_version_info < (10, 8): session_context_pointer = new(Security, 'SSLContextRef *') result = Security.SSLNewContext(False, session_context_pointer) handle_sec_error(result) session_context = unwrap(session_context_pointer) else: session_context = Security.SSLCreateContext( null(), SecurityConst.kSSLClientSide, SecurityConst.kSSLStreamType ) result = Security.SSLSetIOFuncs( session_context, _read_callback_pointer, _write_callback_pointer ) handle_sec_error(result) self._connection_id = id(self) % 2147483647 _connection_refs[self._connection_id] = self _socket_refs[self._connection_id] = self._socket result = Security.SSLSetConnection(session_context, self._connection_id) handle_sec_error(result) utf8_domain = self._hostname.encode('utf-8') result = Security.SSLSetPeerDomainName( session_context, utf8_domain, len(utf8_domain) ) handle_sec_error(result) if osx_version_info >= (10, 10): disable_auto_validation = self._session._manual_validation or self._session._extra_trust_roots explicit_validation = (not self._session._manual_validation) and self._session._extra_trust_roots else: disable_auto_validation = True explicit_validation = not self._session._manual_validation # Ensure requested protocol support is set for the session if osx_version_info < (10, 8): for protocol in ['SSLv2', 'SSLv3', 'TLSv1']: protocol_const = _PROTOCOL_STRING_CONST_MAP[protocol] enabled = protocol in self._session._protocols result = Security.SSLSetProtocolVersionEnabled( session_context, protocol_const, enabled ) handle_sec_error(result) if disable_auto_validation: result = Security.SSLSetEnableCertVerify(session_context, False) handle_sec_error(result) else: protocol_consts = [_PROTOCOL_STRING_CONST_MAP[protocol] for protocol in self._session._protocols] min_protocol = min(protocol_consts) max_protocol = max(protocol_consts) result = Security.SSLSetProtocolVersionMin( session_context, min_protocol ) handle_sec_error(result) result = Security.SSLSetProtocolVersionMax( session_context, max_protocol ) handle_sec_error(result) if disable_auto_validation: result = Security.SSLSetSessionOption( session_context, SecurityConst.kSSLSessionOptionBreakOnServerAuth, True ) handle_sec_error(result) # Disable all sorts of bad cipher suites supported_ciphers_pointer = new(Security, 'size_t *') result = Security.SSLGetNumberSupportedCiphers(session_context, supported_ciphers_pointer) handle_sec_error(result) supported_ciphers = deref(supported_ciphers_pointer) cipher_buffer = buffer_from_bytes(supported_ciphers * 4) supported_cipher_suites_pointer = cast(Security, 'uint32_t *', cipher_buffer) result = Security.SSLGetSupportedCiphers( session_context, supported_cipher_suites_pointer, supported_ciphers_pointer ) handle_sec_error(result) supported_ciphers = deref(supported_ciphers_pointer) supported_cipher_suites = array_from_pointer( Security, 'uint32_t', supported_cipher_suites_pointer, supported_ciphers ) good_ciphers = [] for supported_cipher_suite in supported_cipher_suites: cipher_suite = int_to_bytes(supported_cipher_suite, width=2) cipher_suite_name = CIPHER_SUITE_MAP.get(cipher_suite, cipher_suite) good_cipher = _cipher_blacklist_regex.search(cipher_suite_name) is None if good_cipher: good_ciphers.append(supported_cipher_suite) num_good_ciphers = len(good_ciphers) good_ciphers_array = new(Security, 'uint32_t[]', num_good_ciphers) array_set(good_ciphers_array, good_ciphers) good_ciphers_pointer = cast(Security, 'uint32_t *', good_ciphers_array) result = Security.SSLSetEnabledCiphers( session_context, good_ciphers_pointer, num_good_ciphers ) handle_sec_error(result) # Set a peer id from the session to allow for session reuse, the hostname # is appended to prevent a bug on OS X 10.7 where it tries to reuse a # connection even if the hostnames are different. peer_id = self._session._peer_id + self._hostname.encode('utf-8') result = Security.SSLSetPeerID(session_context, peer_id, len(peer_id)) handle_sec_error(result) handshake_result = Security.SSLHandshake(session_context) if self._exception is not None: exception = self._exception self._exception = None raise exception while handshake_result == SecurityConst.errSSLWouldBlock: handshake_result = Security.SSLHandshake(session_context) if self._exception is not None: exception = self._exception self._exception = None raise exception if osx_version_info < (10, 8) and osx_version_info >= (10, 7): do_validation = explicit_validation and handshake_result == 0 else: do_validation = explicit_validation and handshake_result == SecurityConst.errSSLServerAuthCompleted if do_validation: trust_ref_pointer = new(Security, 'SecTrustRef *') result = Security.SSLCopyPeerTrust( session_context, trust_ref_pointer ) handle_sec_error(result) trust_ref = unwrap(trust_ref_pointer) cf_string_hostname = CFHelpers.cf_string_from_unicode(self._hostname) ssl_policy_ref = Security.SecPolicyCreateSSL(True, cf_string_hostname) result = CoreFoundation.CFRelease(cf_string_hostname) handle_cf_error(result) # Create a new policy for OCSP checking to disable it ocsp_oid_pointer = struct(Security, 'CSSM_OID') ocsp_oid = unwrap(ocsp_oid_pointer) ocsp_oid.Length = len(SecurityConst.APPLE_TP_REVOCATION_OCSP) ocsp_oid_buffer = buffer_from_bytes(SecurityConst.APPLE_TP_REVOCATION_OCSP) ocsp_oid.Data = cast(Security, 'char *', ocsp_oid_buffer) ocsp_search_ref_pointer = new(Security, 'SecPolicySearchRef *') result = Security.SecPolicySearchCreate( SecurityConst.CSSM_CERT_X_509v3, ocsp_oid_pointer, null(), ocsp_search_ref_pointer ) handle_sec_error(result) ocsp_search_ref = unwrap(ocsp_search_ref_pointer) ocsp_policy_ref_pointer = new(Security, 'SecPolicyRef *') result = Security.SecPolicySearchCopyNext(ocsp_search_ref, ocsp_policy_ref_pointer) handle_sec_error(result) ocsp_policy_ref = unwrap(ocsp_policy_ref_pointer) ocsp_struct_pointer = struct(Security, 'CSSM_APPLE_TP_OCSP_OPTIONS') ocsp_struct = unwrap(ocsp_struct_pointer) ocsp_struct.Version = SecurityConst.CSSM_APPLE_TP_OCSP_OPTS_VERSION ocsp_struct.Flags = ( SecurityConst.CSSM_TP_ACTION_OCSP_DISABLE_NET | SecurityConst.CSSM_TP_ACTION_OCSP_CACHE_READ_DISABLE ) ocsp_struct_bytes = struct_bytes(ocsp_struct_pointer) cssm_data_pointer = struct(Security, 'CSSM_DATA') cssm_data = unwrap(cssm_data_pointer) cssm_data.Length = len(ocsp_struct_bytes) ocsp_struct_buffer = buffer_from_bytes(ocsp_struct_bytes) cssm_data.Data = cast(Security, 'char *', ocsp_struct_buffer) result = Security.SecPolicySetValue(ocsp_policy_ref, cssm_data_pointer) handle_sec_error(result) # Create a new policy for CRL checking to disable it crl_oid_pointer = struct(Security, 'CSSM_OID') crl_oid = unwrap(crl_oid_pointer) crl_oid.Length = len(SecurityConst.APPLE_TP_REVOCATION_CRL) crl_oid_buffer = buffer_from_bytes(SecurityConst.APPLE_TP_REVOCATION_CRL) crl_oid.Data = cast(Security, 'char *', crl_oid_buffer) crl_search_ref_pointer = new(Security, 'SecPolicySearchRef *') result = Security.SecPolicySearchCreate( SecurityConst.CSSM_CERT_X_509v3, crl_oid_pointer, null(), crl_search_ref_pointer ) handle_sec_error(result) crl_search_ref = unwrap(crl_search_ref_pointer) crl_policy_ref_pointer = new(Security, 'SecPolicyRef *') result = Security.SecPolicySearchCopyNext(crl_search_ref, crl_policy_ref_pointer) handle_sec_error(result) crl_policy_ref = unwrap(crl_policy_ref_pointer) crl_struct_pointer = struct(Security, 'CSSM_APPLE_TP_CRL_OPTIONS') crl_struct = unwrap(crl_struct_pointer) crl_struct.Version = SecurityConst.CSSM_APPLE_TP_CRL_OPTS_VERSION crl_struct.CrlFlags = 0 crl_struct_bytes = struct_bytes(crl_struct_pointer) cssm_data_pointer = struct(Security, 'CSSM_DATA') cssm_data = unwrap(cssm_data_pointer) cssm_data.Length = len(crl_struct_bytes) crl_struct_buffer = buffer_from_bytes(crl_struct_bytes) cssm_data.Data = cast(Security, 'char *', crl_struct_buffer) result = Security.SecPolicySetValue(crl_policy_ref, cssm_data_pointer) handle_sec_error(result) policy_array_ref = CFHelpers.cf_array_from_list([ ssl_policy_ref, crl_policy_ref, ocsp_policy_ref ]) result = Security.SecTrustSetPolicies(trust_ref, policy_array_ref) handle_sec_error(result) if self._session._extra_trust_roots: ca_cert_refs = [] ca_certs = [] for cert in self._session._extra_trust_roots: ca_cert = load_certificate(cert) ca_certs.append(ca_cert) ca_cert_refs.append(ca_cert.sec_certificate_ref) result = Security.SecTrustSetAnchorCertificatesOnly(trust_ref, False) handle_sec_error(result) array_ref = CFHelpers.cf_array_from_list(ca_cert_refs) result = Security.SecTrustSetAnchorCertificates(trust_ref, array_ref) handle_sec_error(result) result_pointer = new(Security, 'SecTrustResultType *') result = Security.SecTrustEvaluate(trust_ref, result_pointer) handle_sec_error(result) trust_result_code = deref(result_pointer) invalid_chain_error_codes = set([ SecurityConst.kSecTrustResultProceed, SecurityConst.kSecTrustResultUnspecified ]) if trust_result_code not in invalid_chain_error_codes: handshake_result = SecurityConst.errSSLXCertChainInvalid else: handshake_result = Security.SSLHandshake(session_context) while handshake_result == SecurityConst.errSSLWouldBlock: handshake_result = Security.SSLHandshake(session_context) self._done_handshake = True handshake_error_codes = set([ SecurityConst.errSSLXCertChainInvalid, SecurityConst.errSSLCertExpired, SecurityConst.errSSLCertNotYetValid, SecurityConst.errSSLUnknownRootCert, SecurityConst.errSSLNoRootCert, SecurityConst.errSSLHostNameMismatch, SecurityConst.errSSLInternal, ]) # In testing, only errSSLXCertChainInvalid was ever returned for # all of these different situations, however we include the others # for completeness. To get the real reason we have to use the # certificate from the handshake and use the deprecated function # SecTrustGetCssmResultCode(). if handshake_result in handshake_error_codes: if trust_ref: CoreFoundation.CFRelease(trust_ref) trust_ref = None trust_ref_pointer = new(Security, 'SecTrustRef *') result = Security.SSLCopyPeerTrust( session_context, trust_ref_pointer ) handle_sec_error(result) trust_ref = unwrap(trust_ref_pointer) result_code_pointer = new(Security, 'OSStatus *') result = Security.SecTrustGetCssmResultCode(trust_ref, result_code_pointer) result_code = deref(result_code_pointer) chain = extract_chain(self._server_hello) self_signed = False revoked = False expired = False not_yet_valid = False no_issuer = False cert = None bad_hostname = False if chain: cert = chain[0] oscrypto_cert = load_certificate(cert) self_signed = oscrypto_cert.self_signed revoked = result_code == SecurityConst.CSSMERR_TP_CERT_REVOKED no_issuer = not self_signed and result_code == SecurityConst.CSSMERR_TP_NOT_TRUSTED expired = result_code == SecurityConst.CSSMERR_TP_CERT_EXPIRED not_yet_valid = result_code == SecurityConst.CSSMERR_TP_CERT_NOT_VALID_YET bad_hostname = result_code == SecurityConst.CSSMERR_APPLETP_HOSTNAME_MISMATCH validity_too_long = result_code == SecurityConst.CSSMERR_TP_CERT_SUSPENDED # On macOS 10.12, some expired certificates return errSSLInternal if osx_version_info >= (10, 12): validity = cert['tbs_certificate']['validity'] not_before = validity['not_before'].chosen.native not_after = validity['not_after'].chosen.native utcnow = datetime.datetime.now(timezone.utc) expired = not_after < utcnow not_yet_valid = not_before > utcnow if chain and chain[0].hash_algo in set(['md5', 'md2']): raise_weak_signature(chain[0]) if revoked: raise_revoked(cert) if bad_hostname: raise_hostname(cert, self._hostname) elif expired or not_yet_valid: raise_expired_not_yet_valid(cert) elif no_issuer: raise_no_issuer(cert) elif self_signed: raise_self_signed(cert) elif validity_too_long: raise_lifetime_too_long(cert) if detect_client_auth_request(self._server_hello): raise_client_auth() raise_verification(cert) if handshake_result == SecurityConst.errSSLPeerHandshakeFail: if detect_client_auth_request(self._server_hello): raise_client_auth() raise_handshake() if handshake_result == SecurityConst.errSSLWeakPeerEphemeralDHKey: raise_dh_params() if handshake_result == SecurityConst.errSSLPeerProtocolVersion: raise_protocol_version() if handshake_result in set([SecurityConst.errSSLRecordOverflow, SecurityConst.errSSLProtocol]): self._server_hello += _read_remaining(self._socket) raise_protocol_error(self._server_hello) if handshake_result in set([SecurityConst.errSSLClosedNoNotify, SecurityConst.errSSLClosedAbort]): if not self._done_handshake: self._server_hello += _read_remaining(self._socket) if detect_other_protocol(self._server_hello): raise_protocol_error(self._server_hello) raise_disconnection() if osx_version_info < (10, 10): dh_params_length = get_dh_params_length(self._server_hello) if dh_params_length is not None and dh_params_length < 1024: raise_dh_params() would_block = handshake_result == SecurityConst.errSSLWouldBlock server_auth_complete = handshake_result == SecurityConst.errSSLServerAuthCompleted manual_validation = self._session._manual_validation and server_auth_complete if not would_block and not manual_validation: handle_sec_error(handshake_result, TLSError) self._session_context = session_context protocol_const_pointer = new(Security, 'SSLProtocol *') result = Security.SSLGetNegotiatedProtocolVersion( session_context, protocol_const_pointer ) handle_sec_error(result) protocol_const = deref(protocol_const_pointer) self._protocol = _PROTOCOL_CONST_STRING_MAP[protocol_const] cipher_int_pointer = new(Security, 'SSLCipherSuite *') result = Security.SSLGetNegotiatedCipher( session_context, cipher_int_pointer ) handle_sec_error(result) cipher_int = deref(cipher_int_pointer) cipher_bytes = int_to_bytes(cipher_int, width=2) self._cipher_suite = CIPHER_SUITE_MAP.get(cipher_bytes, cipher_bytes) session_info = parse_session_info( self._server_hello, self._client_hello ) self._compression = session_info['compression'] self._session_id = session_info['session_id'] self._session_ticket = session_info['session_ticket'] except (OSError, socket_.error): if session_context: if osx_version_info < (10, 8): result = Security.SSLDisposeContext(session_context) handle_sec_error(result) else: result = CoreFoundation.CFRelease(session_context) handle_cf_error(result) self._session_context = None self.close() raise finally: # Trying to release crl_search_ref or ocsp_search_ref results in # a segmentation fault, so we do not do that if ssl_policy_ref: result = CoreFoundation.CFRelease(ssl_policy_ref) handle_cf_error(result) ssl_policy_ref = None if crl_policy_ref: result = CoreFoundation.CFRelease(crl_policy_ref) handle_cf_error(result) crl_policy_ref = None if ocsp_policy_ref: result = CoreFoundation.CFRelease(ocsp_policy_ref) handle_cf_error(result) ocsp_policy_ref = None if policy_array_ref: result = CoreFoundation.CFRelease(policy_array_ref) handle_cf_error(result) policy_array_ref = None if trust_ref: CoreFoundation.CFRelease(trust_ref) trust_ref = None def read(self, max_length): """ Reads data from the TLS-wrapped socket :param max_length: The number of bytes to read - output may be less than this :raises: socket.socket - when a non-TLS socket error occurs oscrypto.errors.TLSError - when a TLS-related error occurs oscrypto.errors.TLSDisconnectError - when the connection disconnects oscrypto.errors.TLSGracefulDisconnectError - when the remote end gracefully closed the connection ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the data read """ if not isinstance(max_length, int_types): raise TypeError(pretty_message( ''' max_length must be an integer, not %s ''', type_name(max_length) )) if self._session_context is None: # Even if the session is closed, we can use # buffered data to respond to read requests if self._decrypted_bytes != b'': output = self._decrypted_bytes self._decrypted_bytes = b'' return output self._raise_closed() buffered_length = len(self._decrypted_bytes) # If we already have enough buffered data, just use that if buffered_length >= max_length: output = self._decrypted_bytes[0:max_length] self._decrypted_bytes = self._decrypted_bytes[max_length:] return output # Don't block if we have buffered data available, since it is ok to # return less than the max_length if buffered_length > 0 and not self.select_read(0): output = self._decrypted_bytes self._decrypted_bytes = b'' return output # Only read enough to get the requested amount when # combined with buffered data to_read = max_length - len(self._decrypted_bytes) read_buffer = buffer_from_bytes(to_read) processed_pointer = new(Security, 'size_t *') result = Security.SSLRead( self._session_context, read_buffer, to_read, processed_pointer ) if self._exception is not None: exception = self._exception self._exception = None raise exception if result and result not in set([SecurityConst.errSSLWouldBlock, SecurityConst.errSSLClosedGraceful]): handle_sec_error(result, TLSError) if result and result == SecurityConst.errSSLClosedGraceful: self._gracefully_closed = True self._shutdown(False) self._raise_closed() bytes_read = deref(processed_pointer) output = self._decrypted_bytes + bytes_from_buffer(read_buffer, bytes_read) self._decrypted_bytes = output[max_length:] return output[0:max_length] def select_read(self, timeout=None): """ Blocks until the socket is ready to be read from, or the timeout is hit :param timeout: A float - the period of time to wait for data to be read. None for no time limit. :return: A boolean - if data is ready to be read. Will only be False if timeout is not None. """ # If we have buffered data, we consider a read possible if len(self._decrypted_bytes) > 0: return True read_ready, _, _ = select.select([self._socket], [], [], timeout) return len(read_ready) > 0 def read_until(self, marker): """ Reads data from the socket until a marker is found. Data read includes the marker. :param marker: A byte string or regex object from re.compile(). Used to determine when to stop reading. Regex objects are more inefficient since they must scan the entire byte string of read data each time data is read off the socket. :return: A byte string of the data read, including the marker """ if not isinstance(marker, byte_cls) and not isinstance(marker, Pattern): raise TypeError(pretty_message( ''' marker must be a byte string or compiled regex object, not %s ''', type_name(marker) )) output = b'' is_regex = isinstance(marker, Pattern) while True: if len(self._decrypted_bytes) > 0: chunk = self._decrypted_bytes self._decrypted_bytes = b'' else: to_read = self._os_buffered_size() or 8192 chunk = self.read(to_read) offset = len(output) output += chunk if is_regex: match = marker.search(output) if match is not None: end = match.end() break else: # If the marker was not found last time, we have to start # at a position where the marker would have its final char # in the newly read chunk start = max(0, offset - len(marker) - 1) match = output.find(marker, start) if match != -1: end = match + len(marker) break self._decrypted_bytes = output[end:] + self._decrypted_bytes return output[0:end] def _os_buffered_size(self): """ Returns the number of bytes of decrypted data stored in the Secure Transport read buffer. This amount of data can be read from SSLRead() without calling self._socket.recv(). :return: An integer - the number of available bytes """ num_bytes_pointer = new(Security, 'size_t *') result = Security.SSLGetBufferedReadSize( self._session_context, num_bytes_pointer ) handle_sec_error(result) return deref(num_bytes_pointer) def read_line(self): r""" Reads a line from the socket, including the line ending of "\r\n", "\r", or "\n" :return: A byte string of the next line from the socket """ return self.read_until(_line_regex) def read_exactly(self, num_bytes): """ Reads exactly the specified number of bytes from the socket :param num_bytes: An integer - the exact number of bytes to read :return: A byte string of the data that was read """ output = b'' remaining = num_bytes while remaining > 0: output += self.read(remaining) remaining = num_bytes - len(output) return output def write(self, data): """ Writes data to the TLS-wrapped socket :param data: A byte string to write to the socket :raises: socket.socket - when a non-TLS socket error occurs oscrypto.errors.TLSError - when a TLS-related error occurs oscrypto.errors.TLSDisconnectError - when the connection disconnects oscrypto.errors.TLSGracefulDisconnectError - when the remote end gracefully closed the connection ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if self._session_context is None: self._raise_closed() processed_pointer = new(Security, 'size_t *') data_len = len(data) while data_len: write_buffer = buffer_from_bytes(data) result = Security.SSLWrite( self._session_context, write_buffer, data_len, processed_pointer ) if self._exception is not None: exception = self._exception self._exception = None raise exception handle_sec_error(result, TLSError) bytes_written = deref(processed_pointer) data = data[bytes_written:] data_len = len(data) if data_len > 0: self.select_write() def select_write(self, timeout=None): """ Blocks until the socket is ready to be written to, or the timeout is hit :param timeout: A float - the period of time to wait for the socket to be ready to written to. None for no time limit. :return: A boolean - if the socket is ready for writing. Will only be False if timeout is not None. """ _, write_ready, _ = select.select([], [self._socket], [], timeout) return len(write_ready) > 0 def _shutdown(self, manual): """ Shuts down the TLS session and then shuts down the underlying socket :param manual: A boolean if the connection was manually shutdown """ if self._session_context is None: return # Ignore error during close in case other end closed already result = Security.SSLClose(self._session_context) if osx_version_info < (10, 8): result = Security.SSLDisposeContext(self._session_context) handle_sec_error(result) else: result = CoreFoundation.CFRelease(self._session_context) handle_cf_error(result) self._session_context = None if manual: self._local_closed = True try: self._socket.shutdown(socket_.SHUT_RDWR) except (socket_.error): pass def shutdown(self): """ Shuts down the TLS session and then shuts down the underlying socket """ self._shutdown(True) def close(self): """ Shuts down the TLS session and socket and forcibly closes it """ try: self.shutdown() finally: if self._socket: try: self._socket.close() except (socket_.error): pass self._socket = None if self._connection_id in _socket_refs: del _socket_refs[self._connection_id] def _read_certificates(self): """ Reads end-entity and intermediate certificate information from the TLS session """ trust_ref = None cf_data_ref = None result = None try: trust_ref_pointer = new(Security, 'SecTrustRef *') result = Security.SSLCopyPeerTrust( self._session_context, trust_ref_pointer ) handle_sec_error(result) trust_ref = unwrap(trust_ref_pointer) number_certs = Security.SecTrustGetCertificateCount(trust_ref) self._intermediates = [] for index in range(0, number_certs): sec_certificate_ref = Security.SecTrustGetCertificateAtIndex( trust_ref, index ) cf_data_ref = Security.SecCertificateCopyData(sec_certificate_ref) cert_data = CFHelpers.cf_data_to_bytes(cf_data_ref) result = CoreFoundation.CFRelease(cf_data_ref) handle_cf_error(result) cf_data_ref = None cert = Asn1Certificate.load(cert_data) if index == 0: self._certificate = cert else: self._intermediates.append(cert) finally: if trust_ref: result = CoreFoundation.CFRelease(trust_ref) handle_cf_error(result) if cf_data_ref: result = CoreFoundation.CFRelease(cf_data_ref) handle_cf_error(result) def _raise_closed(self): """ Raises an exception describing if the local or remote end closed the connection """ if self._local_closed: raise TLSDisconnectError('The connection was already closed') elif self._gracefully_closed: raise TLSGracefulDisconnectError('The remote end closed the connection') else: raise TLSDisconnectError('The connection was closed') @property def certificate(self): """ An asn1crypto.x509.Certificate object of the end-entity certificate presented by the server """ if self._session_context is None: self._raise_closed() if self._certificate is None: self._read_certificates() return self._certificate @property def intermediates(self): """ A list of asn1crypto.x509.Certificate objects that were presented as intermediates by the server """ if self._session_context is None: self._raise_closed() if self._certificate is None: self._read_certificates() return self._intermediates @property def cipher_suite(self): """ A unicode string of the IANA cipher suite name of the negotiated cipher suite """ return self._cipher_suite @property def protocol(self): """ A unicode string of: "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3" """ return self._protocol @property def compression(self): """ A boolean if compression is enabled """ return self._compression @property def session_id(self): """ A unicode string of "new" or "reused" or None for no ticket """ return self._session_id @property def session_ticket(self): """ A unicode string of "new" or "reused" or None for no ticket """ return self._session_ticket @property def session(self): """ The oscrypto.tls.TLSSession object used for this connection """ return self._session @property def hostname(self): """ A unicode string of the TLS server domain name or IP address """ return self._hostname @property def port(self): """ An integer of the port number the socket is connected to """ return self.socket.getpeername()[1] @property def socket(self): """ The underlying socket.socket connection """ if self._session_context is None: self._raise_closed() return self._socket def __del__(self): self.close() oscrypto-1.3.0/oscrypto/_mac/trust_list.py000066400000000000000000000165421421476274700207720ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import hashlib import sys from .._asn1 import Certificate from .._ffi import new, unwrap from ._core_foundation import CoreFoundation, CFHelpers from ._security import Security, SecurityConst, handle_sec_error if sys.version_info < (3,): range = xrange # noqa __all__ = [ 'extract_from_system', 'system_path', ] def system_path(): return None def extract_from_system(cert_callback=None, callback_only_on_failure=False): """ Extracts trusted CA certificates from the OS X trusted root keychain. :param cert_callback: A callback that is called once for each certificate in the trust store. It should accept two parameters: an asn1crypto.x509.Certificate object, and a reason. The reason will be None if the certificate is being exported, otherwise it will be a unicode string of the reason it won't. :param callback_only_on_failure: A boolean - if the callback should only be called when a certificate is not exported. :raises: OSError - when an error is returned by the OS crypto library :return: A list of 3-element tuples: - 0: a byte string of a DER-encoded certificate - 1: a set of unicode strings that are OIDs of purposes to trust the certificate for - 2: a set of unicode strings that are OIDs of purposes to reject the certificate for """ certs_pointer_pointer = new(CoreFoundation, 'CFArrayRef *') res = Security.SecTrustCopyAnchorCertificates(certs_pointer_pointer) handle_sec_error(res) certs_pointer = unwrap(certs_pointer_pointer) certificates = {} trust_info = {} all_purposes = '2.5.29.37.0' default_trust = (set(), set()) length = CoreFoundation.CFArrayGetCount(certs_pointer) for index in range(0, length): cert_pointer = CoreFoundation.CFArrayGetValueAtIndex(certs_pointer, index) der_cert, cert_hash = _cert_details(cert_pointer) certificates[cert_hash] = der_cert CoreFoundation.CFRelease(certs_pointer) for domain in [SecurityConst.kSecTrustSettingsDomainUser, SecurityConst.kSecTrustSettingsDomainAdmin]: cert_trust_settings_pointer_pointer = new(CoreFoundation, 'CFArrayRef *') res = Security.SecTrustSettingsCopyCertificates(domain, cert_trust_settings_pointer_pointer) if res == SecurityConst.errSecNoTrustSettings: continue handle_sec_error(res) cert_trust_settings_pointer = unwrap(cert_trust_settings_pointer_pointer) length = CoreFoundation.CFArrayGetCount(cert_trust_settings_pointer) for index in range(0, length): cert_pointer = CoreFoundation.CFArrayGetValueAtIndex(cert_trust_settings_pointer, index) trust_settings_pointer_pointer = new(CoreFoundation, 'CFArrayRef *') res = Security.SecTrustSettingsCopyTrustSettings(cert_pointer, domain, trust_settings_pointer_pointer) # In OS X 10.11, this value started being seen. From the comments in # the Security Framework Reference, the lack of any settings should # indicate "always trust this certificate" if res == SecurityConst.errSecItemNotFound: continue # If the trust settings for a certificate are invalid, we need to # assume the certificate should not be trusted if res == SecurityConst.errSecInvalidTrustSettings: der_cert, cert_hash = _cert_details(cert_pointer) if cert_hash in certificates: _cert_callback( cert_callback, certificates[cert_hash], 'invalid trust settings' ) del certificates[cert_hash] continue handle_sec_error(res) trust_settings_pointer = unwrap(trust_settings_pointer_pointer) trust_oids = set() reject_oids = set() settings_length = CoreFoundation.CFArrayGetCount(trust_settings_pointer) for settings_index in range(0, settings_length): settings_dict_entry = CoreFoundation.CFArrayGetValueAtIndex(trust_settings_pointer, settings_index) settings_dict = CFHelpers.cf_dictionary_to_dict(settings_dict_entry) # No policy OID means the trust result is for all purposes policy_oid = settings_dict.get('kSecTrustSettingsPolicy', {}).get('SecPolicyOid', all_purposes) # 0 = kSecTrustSettingsResultInvalid # 1 = kSecTrustSettingsResultTrustRoot # 2 = kSecTrustSettingsResultTrustAsRoot # 3 = kSecTrustSettingsResultDeny # 4 = kSecTrustSettingsResultUnspecified trust_result = settings_dict.get('kSecTrustSettingsResult', 1) should_trust = trust_result != 0 and trust_result != 3 if should_trust: trust_oids.add(policy_oid) else: reject_oids.add(policy_oid) der_cert, cert_hash = _cert_details(cert_pointer) # If rejected for all purposes, we don't export the certificate if all_purposes in reject_oids: if cert_hash in certificates: _cert_callback( cert_callback, certificates[cert_hash], 'explicitly distrusted' ) del certificates[cert_hash] else: if all_purposes in trust_oids: trust_oids = set([all_purposes]) trust_info[cert_hash] = (trust_oids, reject_oids) CoreFoundation.CFRelease(trust_settings_pointer) CoreFoundation.CFRelease(cert_trust_settings_pointer) output = [] for cert_hash in certificates: if not callback_only_on_failure: _cert_callback(cert_callback, certificates[cert_hash], None) cert_trust_info = trust_info.get(cert_hash, default_trust) output.append((certificates[cert_hash], cert_trust_info[0], cert_trust_info[1])) return output def _cert_callback(callback, der_cert, reason): """ Constructs an asn1crypto.x509.Certificate object and calls the export callback :param callback: The callback to call :param der_cert: A byte string of the DER-encoded certificate :param reason: None if cert is being exported, or a unicode string of the reason it is not being exported """ if not callback: return callback(Certificate.load(der_cert), reason) def _cert_details(cert_pointer): """ Return the certificate and a hash of it :param cert_pointer: A SecCertificateRef :return: A 2-element tuple: - [0]: A byte string of the SHA1 hash of the cert - [1]: A byte string of the DER-encoded contents of the cert """ data_pointer = None try: data_pointer = Security.SecCertificateCopyData(cert_pointer) der_cert = CFHelpers.cf_data_to_bytes(data_pointer) cert_hash = hashlib.sha1(der_cert).digest() return (der_cert, cert_hash) finally: if data_pointer is not None: CoreFoundation.CFRelease(data_pointer) oscrypto-1.3.0/oscrypto/_mac/util.py000066400000000000000000000234541421476274700175330ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os from .._errors import pretty_message from .._ffi import buffer_from_bytes, bytes_from_buffer, errno, byte_string_from_buffer from .._types import type_name, str_cls, byte_cls, int_types from ..errors import LibraryNotFoundError from ._common_crypto import CommonCrypto, CommonCryptoConst from ._security import Security __all__ = [ 'pbkdf2', 'pkcs12_kdf', 'rand_bytes', ] _encoding = 'utf-8' _fallback_encodings = ['utf-8', 'cp1252'] def _try_decode(value): try: return str_cls(value, _encoding) # If the "correct" encoding did not work, try some defaults, and then just # obliterate characters that we can't seen to decode properly except (UnicodeDecodeError): for encoding in _fallback_encodings: try: return str_cls(value, encoding, errors='strict') except (UnicodeDecodeError): pass return str_cls(value, errors='replace') def _extract_error(): """ Extracts the last OS error message into a python unicode string :return: A unicode string error message """ error_num = errno() try: error_string = os.strerror(error_num) except (ValueError): return str_cls(error_num) if isinstance(error_string, str_cls): return error_string return _try_decode(error_string) def pbkdf2(hash_algorithm, password, salt, iterations, key_length): """ PBKDF2 from PKCS#5 :param hash_algorithm: The string name of the hash algorithm to use: "sha1", "sha224", "sha256", "sha384", "sha512" :param password: A byte string of the password to use an input to the KDF :param salt: A cryptographic random byte string :param iterations: The numbers of iterations to use when deriving the key :param key_length: The length of the desired key in bytes :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: The derived key as a byte string """ if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) if not isinstance(salt, byte_cls): raise TypeError(pretty_message( ''' salt must be a byte string, not %s ''', type_name(salt) )) if not isinstance(iterations, int_types): raise TypeError(pretty_message( ''' iterations must be an integer, not %s ''', type_name(iterations) )) if iterations < 1: raise ValueError('iterations must be greater than 0') if not isinstance(key_length, int_types): raise TypeError(pretty_message( ''' key_length must be an integer, not %s ''', type_name(key_length) )) if key_length < 1: raise ValueError('key_length must be greater than 0') if hash_algorithm not in set(['sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) algo = { 'sha1': CommonCryptoConst.kCCPRFHmacAlgSHA1, 'sha224': CommonCryptoConst.kCCPRFHmacAlgSHA224, 'sha256': CommonCryptoConst.kCCPRFHmacAlgSHA256, 'sha384': CommonCryptoConst.kCCPRFHmacAlgSHA384, 'sha512': CommonCryptoConst.kCCPRFHmacAlgSHA512 }[hash_algorithm] output_buffer = buffer_from_bytes(key_length) result = CommonCrypto.CCKeyDerivationPBKDF( CommonCryptoConst.kCCPBKDF2, password, len(password), salt, len(salt), algo, iterations, output_buffer, key_length ) if result != 0: raise OSError(_extract_error()) return bytes_from_buffer(output_buffer) pbkdf2.pure_python = False def rand_bytes(length): """ Returns a number of random bytes suitable for cryptographic purposes :param length: The desired number of bytes :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string """ if not isinstance(length, int_types): raise TypeError(pretty_message( ''' length must be an integer, not %s ''', type_name(length) )) if length < 1: raise ValueError('length must be greater than 0') if length > 1024: raise ValueError('length must not be greater than 1024') buffer = buffer_from_bytes(length) result = Security.SecRandomCopyBytes(Security.kSecRandomDefault, length, buffer) if result != 0: raise OSError(_extract_error()) return bytes_from_buffer(buffer) # If in a future version of OS X they remove OpenSSL, this try/except block # will fall back to the pure Python implementation, which is just slower try: from .._openssl._libcrypto import libcrypto def _extract_openssl_error(): """ Extracts the last OpenSSL error message into a python unicode string :return: A unicode string error message """ error_num = libcrypto.ERR_get_error() buffer = buffer_from_bytes(120) libcrypto.ERR_error_string(error_num, buffer) # Since we are dealing with a string, it is NULL terminated error_string = byte_string_from_buffer(buffer) return _try_decode(error_string) def pkcs12_kdf(hash_algorithm, password, salt, iterations, key_length, id_): """ KDF from RFC7292 appendix B.2 - https://tools.ietf.org/html/rfc7292#page-19 :param hash_algorithm: The string name of the hash algorithm to use: "md5", "sha1", "sha224", "sha256", "sha384", "sha512" :param password: A byte string of the password to use an input to the KDF :param salt: A cryptographic random byte string :param iterations: The numbers of iterations to use when deriving the key :param key_length: The length of the desired key in bytes :param id_: The ID of the usage - 1 for key, 2 for iv, 3 for mac :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: The derived key as a byte string """ if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) if not isinstance(salt, byte_cls): raise TypeError(pretty_message( ''' salt must be a byte string, not %s ''', type_name(salt) )) if not isinstance(iterations, int_types): raise TypeError(pretty_message( ''' iterations must be an integer, not %s ''', type_name(iterations) )) if iterations < 1: raise ValueError(pretty_message( ''' iterations must be greater than 0 - is %s ''', repr(iterations) )) if not isinstance(key_length, int_types): raise TypeError(pretty_message( ''' key_length must be an integer, not %s ''', type_name(key_length) )) if key_length < 1: raise ValueError(pretty_message( ''' key_length must be greater than 0 - is %s ''', repr(key_length) )) if hash_algorithm not in set(['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "md5", "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) if id_ not in set([1, 2, 3]): raise ValueError(pretty_message( ''' id_ must be one of 1, 2, 3, not %s ''', repr(id_) )) utf16_password = password.decode('utf-8').encode('utf-16be') + b'\x00\x00' digest_type = { 'md5': libcrypto.EVP_md5, 'sha1': libcrypto.EVP_sha1, 'sha224': libcrypto.EVP_sha224, 'sha256': libcrypto.EVP_sha256, 'sha384': libcrypto.EVP_sha384, 'sha512': libcrypto.EVP_sha512, }[hash_algorithm]() output_buffer = buffer_from_bytes(key_length) result = libcrypto.PKCS12_key_gen_uni( utf16_password, len(utf16_password), salt, len(salt), id_, iterations, key_length, output_buffer, digest_type ) if result != 1: raise OSError(_extract_openssl_error()) return bytes_from_buffer(output_buffer) except (LibraryNotFoundError): from .._pkcs12 import pkcs12_kdf oscrypto-1.3.0/oscrypto/_openssl/000077500000000000000000000000001421476274700171175ustar00rootroot00000000000000oscrypto-1.3.0/oscrypto/_openssl/__init__.py000066400000000000000000000000001421476274700212160ustar00rootroot00000000000000oscrypto-1.3.0/oscrypto/_openssl/_libcrypto.py000066400000000000000000000073121421476274700216420ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .. import ffi from .._ffi import buffer_from_bytes, byte_string_from_buffer, null from .._types import str_cls if ffi() == 'cffi': from ._libcrypto_cffi import ( libcrypto, version as libcrypto_version, version_info as libcrypto_version_info ) else: from ._libcrypto_ctypes import ( libcrypto, version as libcrypto_version, version_info as libcrypto_version_info ) __all__ = [ 'handle_openssl_error', 'libcrypto', 'libcrypto_legacy_support', 'libcrypto_version', 'libcrypto_version_info', 'LibcryptoConst', 'peek_openssl_error', ] _encoding = 'utf-8' _fallback_encodings = ['utf-8', 'cp1252'] if libcrypto_version_info < (1, 1): libcrypto.ERR_load_crypto_strings() libcrypto.OPENSSL_config(null()) # This enables legacy algorithms in OpenSSL 3.0, such as RC2, etc # which are used by various tests and some old protocols and things # like PKCS12 libcrypto_legacy_support = True if libcrypto_version_info >= (3, ): if libcrypto.OSSL_PROVIDER_available(null(), "legacy".encode("ascii")): libcrypto.OSSL_PROVIDER_load(null(), "legacy".encode("ascii")) else: libcrypto_legacy_support = False def _try_decode(value): try: return str_cls(value, _encoding) # If the "correct" encoding did not work, try some defaults, and then just # obliterate characters that we can't seen to decode properly except (UnicodeDecodeError): for encoding in _fallback_encodings: try: return str_cls(value, encoding, errors='strict') except (UnicodeDecodeError): pass return str_cls(value, errors='replace') def handle_openssl_error(result, exception_class=None): """ Checks if an error occurred, and if so throws an OSError containing the last OpenSSL error message :param result: An integer result code - 1 or greater indicates success :param exception_class: The exception class to use for the exception if an error occurred :raises: OSError - when an OpenSSL error occurs """ if result > 0: return if exception_class is None: exception_class = OSError error_num = libcrypto.ERR_get_error() buffer = buffer_from_bytes(120) libcrypto.ERR_error_string(error_num, buffer) # Since we are dealing with a string, it is NULL terminated error_string = byte_string_from_buffer(buffer) raise exception_class(_try_decode(error_string)) def peek_openssl_error(): """ Peeks into the error stack and pulls out the lib, func and reason :return: A three-element tuple of integers (lib, func, reason) """ error = libcrypto.ERR_peek_error() if libcrypto_version_info < (3, 0): lib = int((error >> 24) & 0xff) func = int((error >> 12) & 0xfff) reason = int(error & 0xfff) else: lib = int((error >> 23) & 0xff) # OpenSSL 3.0 removed ERR_GET_FUNC() func = 0 reason = int(error & 0x7fffff) return (lib, func, reason) class LibcryptoConst(): EVP_CTRL_SET_RC2_KEY_BITS = 3 SSLEAY_VERSION = 0 RSA_PKCS1_PADDING = 1 RSA_NO_PADDING = 3 RSA_PKCS1_OAEP_PADDING = 4 # OpenSSL 0.9.x EVP_MD_CTX_FLAG_PSS_MDLEN = -1 # OpenSSL 1.x.x EVP_PKEY_CTRL_RSA_PADDING = 0x1001 RSA_PKCS1_PSS_PADDING = 6 EVP_PKEY_CTRL_RSA_PSS_SALTLEN = 0x1002 EVP_PKEY_RSA = 6 EVP_PKEY_OP_SIGN = 1 << 3 EVP_PKEY_OP_VERIFY = 1 << 4 NID_X9_62_prime256v1 = 415 NID_secp384r1 = 715 NID_secp521r1 = 716 OPENSSL_EC_NAMED_CURVE = 1 DH_GENERATOR_2 = 2 oscrypto-1.3.0/oscrypto/_openssl/_libcrypto_cffi.py000066400000000000000000000233021421476274700226260ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import re from .. import _backend_config from .._errors import pretty_message from .._ffi import get_library, register_ffi from ..errors import LibraryNotFoundError from cffi import FFI __all__ = [ 'is_libressl', 'libcrypto', 'libressl_version', 'libressl_version_info', 'version', 'version_info', ] libcrypto_path = _backend_config().get('libcrypto_path') if libcrypto_path is None: libcrypto_path = get_library('crypto', 'libcrypto.dylib', '42') if not libcrypto_path: raise LibraryNotFoundError('The library libcrypto could not be found') try: vffi = FFI() vffi.cdef("const char *SSLeay_version(int type);") version_string = vffi.string(vffi.dlopen(libcrypto_path).SSLeay_version(0)).decode('utf-8') except (AttributeError): vffi = FFI() vffi.cdef("const char *OpenSSL_version(int type);") version_string = vffi.string(vffi.dlopen(libcrypto_path).OpenSSL_version(0)).decode('utf-8') is_libressl = 'LibreSSL' in version_string version_match = re.search('\\b(\\d\\.\\d\\.\\d[a-z]*)\\b', version_string) if not version_match: version_match = re.search('(?<=LibreSSL )(\\d\\.\\d(\\.\\d)?)\\b', version_string) if not version_match: raise LibraryNotFoundError('Error detecting the version of libcrypto') version = version_match.group(1) version_parts = re.sub('(\\d)([a-z]+)', '\\1.\\2', version).split('.') version_info = tuple(int(part) if part.isdigit() else part for part in version_parts) # LibreSSL is compatible with libcrypto from OpenSSL 1.0.1 libressl_version = '' libressl_version_info = tuple() if is_libressl: libressl_version = version libressl_version_info = version_info version = '1.0.1' version_info = (1, 0, 1) ffi = FFI() libcrypto = ffi.dlopen(libcrypto_path) register_ffi(libcrypto, ffi) if version_info < (0, 9, 8): raise LibraryNotFoundError(pretty_message( ''' OpenSSL versions older than 0.9.8 are not supported - found version %s ''', version )) if version_info < (1, 1): ffi.cdef(""" void ERR_load_crypto_strings(void); void ERR_free_strings(void); """) if version_info >= (3, ): ffi.cdef(""" typedef ... OSSL_LIB_CTX; typedef ... OSSL_PROVIDER; int OSSL_PROVIDER_available(OSSL_LIB_CTX *libctx, const char *name); OSSL_PROVIDER *OSSL_PROVIDER_load(OSSL_LIB_CTX *libctx, const char *name); """) # The typedef uintptr_t lines here allow us to check for a NULL pointer, # without having to redefine the structs in our code. This is kind of a hack, # but it should cause problems since we treat these as opaque. ffi.cdef(""" typedef ... EVP_MD; typedef uintptr_t EVP_CIPHER_CTX; typedef ... EVP_CIPHER; typedef ... ENGINE; typedef uintptr_t EVP_PKEY; typedef uintptr_t X509; typedef uintptr_t DH; typedef uintptr_t RSA; typedef uintptr_t DSA; typedef uintptr_t EC_KEY; typedef ... EVP_MD_CTX; typedef ... EVP_PKEY_CTX; typedef ... BN_GENCB; typedef ... BIGNUM; unsigned long ERR_get_error(void); char *ERR_error_string(unsigned long e, char *buf); unsigned long ERR_peek_error(void); void OPENSSL_config(const char *config_name); EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void); void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx); int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen); int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *x, int padding); int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr); const EVP_CIPHER *EVP_aes_128_cbc(void); const EVP_CIPHER *EVP_aes_192_cbc(void); const EVP_CIPHER *EVP_aes_256_cbc(void); const EVP_CIPHER *EVP_des_cbc(void); const EVP_CIPHER *EVP_des_ede_cbc(void); const EVP_CIPHER *EVP_des_ede3_cbc(void); const EVP_CIPHER *EVP_rc4(void); const EVP_CIPHER *EVP_rc2_cbc(void); int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const char *key, const char *iv); int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, char *out, int *outl, const char *in, int inl); int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, char *out, int *outl); int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const char *key, const char *iv); int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, char *out, int *outl, const char *in, int inl); int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, char *out, int *outl); EVP_PKEY *d2i_AutoPrivateKey(EVP_PKEY **a, const char **pp, long length); EVP_PKEY *d2i_PUBKEY(EVP_PKEY **a, const char **pp, long length); int i2d_PUBKEY(EVP_PKEY *a, char **pp); void EVP_PKEY_free(EVP_PKEY *key); X509 *d2i_X509(X509 **px, const char **in, int len); int i2d_X509(X509 *x, char **out); EVP_PKEY *X509_get_pubkey(X509 *x); void X509_free(X509 *a); RSA *EVP_PKEY_get1_RSA(EVP_PKEY *pkey); void RSA_free(RSA *r); int RSA_public_encrypt(int flen, const char *from, char *to, RSA *rsa, int padding); int RSA_private_encrypt(int flen, const char *from, char *to, RSA *rsa, int padding); int RSA_public_decrypt(int flen, const char *from, char *to, RSA *rsa, int padding); int RSA_private_decrypt(int flen, const char *from, char *to, RSA *rsa, int padding); int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, unsigned int cnt); const EVP_MD *EVP_md5(void); const EVP_MD *EVP_sha1(void); const EVP_MD *EVP_sha224(void); const EVP_MD *EVP_sha256(void); const EVP_MD *EVP_sha384(void); const EVP_MD *EVP_sha512(void); int PKCS12_key_gen_uni(char *pass, int passlen, char *salt, int saltlen, int id, int iter, int n, char *out, const EVP_MD *md_type); void BN_free(BIGNUM *a); int BN_dec2bn(BIGNUM **a, const char *str); DH *DH_new(void); int DH_generate_parameters_ex(DH *dh, int prime_len, int generator, BN_GENCB *cb); int i2d_DHparams(const DH *a, char **pp); void DH_free(DH *dh); RSA *RSA_new(void); int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb); int i2d_RSAPublicKey(RSA *a, char **pp); int i2d_RSAPrivateKey(RSA *a, char **pp); DSA *DSA_new(void); int DSA_generate_parameters_ex(DSA *dsa, int bits, const char *seed, int seed_len, int *counter_ret, unsigned long *h_ret, BN_GENCB *cb); int DSA_generate_key(DSA *a); int i2d_DSA_PUBKEY(const DSA *a, char **pp); int i2d_DSAPrivateKey(const DSA *a, char **pp); void DSA_free(DSA *dsa); EC_KEY *EC_KEY_new_by_curve_name(int nid); int EC_KEY_generate_key(EC_KEY *key); void EC_KEY_set_asn1_flag(EC_KEY *, int); int i2d_ECPrivateKey(EC_KEY *key, char **out); int i2o_ECPublicKey(EC_KEY *key, char **out); void EC_KEY_free(EC_KEY *key); """) if version_info < (3, ): ffi.cdef(""" int EVP_PKEY_size(EVP_PKEY *pkey); """) else: ffi.cdef(""" int EVP_PKEY_get_size(EVP_PKEY *pkey); """) if version_info < (1, 1): ffi.cdef(""" EVP_MD_CTX *EVP_MD_CTX_create(void); void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx); """) else: ffi.cdef(""" EVP_MD_CTX *EVP_MD_CTX_new(void); void EVP_MD_CTX_free(EVP_MD_CTX *ctx); """) if version_info < (1,): ffi.cdef(""" typedef ... *DSA_SIG; typedef ... *ECDSA_SIG; DSA_SIG *DSA_do_sign(const char *dgst, int dlen, DSA *dsa); ECDSA_SIG *ECDSA_do_sign(const char *dgst, int dgst_len, EC_KEY *eckey); DSA_SIG *d2i_DSA_SIG(DSA_SIG **v, const char **pp, long length); ECDSA_SIG *d2i_ECDSA_SIG(ECDSA_SIG **v, const char **pp, long len); int i2d_DSA_SIG(const DSA_SIG *a, char **pp); int i2d_ECDSA_SIG(const ECDSA_SIG *a, char **pp); int DSA_do_verify(const char *dgst, int dgst_len, DSA_SIG *sig, DSA *dsa); int ECDSA_do_verify(const char *dgst, int dgst_len, const ECDSA_SIG *sig, EC_KEY *eckey); void DSA_SIG_free(DSA_SIG *a); void ECDSA_SIG_free(ECDSA_SIG *a); DSA *EVP_PKEY_get1_DSA(EVP_PKEY *pkey); EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *pkey); int RSA_verify_PKCS1_PSS(RSA *rsa, const char *mHash, const EVP_MD *Hash, const char *EM, int sLen); int RSA_padding_add_PKCS1_PSS(RSA *rsa, char *EM, const char *mHash, const EVP_MD *Hash, int sLen); int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl); int EVP_SignFinal(EVP_MD_CTX *ctx, char *sig, unsigned int *s, EVP_PKEY *pkey); int EVP_VerifyFinal(EVP_MD_CTX *ctx, char *sigbuf, unsigned int siglen, EVP_PKEY *pkey); void EVP_MD_CTX_set_flags(EVP_MD_CTX *ctx, int flags); """) else: ffi.cdef(""" int PKCS5_PBKDF2_HMAC(const char *pass, int passlen, const char *salt, int saltlen, int iter, const EVP_MD *digest, int keylen, char *out); int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey); int EVP_DigestSignFinal(EVP_MD_CTX *ctx, char *sig, size_t *siglen); int EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey); int EVP_DigestVerifyFinal(EVP_MD_CTX *ctx, const char *sig, size_t siglen); int EVP_PKEY_CTX_ctrl(EVP_PKEY_CTX *ctx, int keytype, int optype, int cmd, int p1, void *p2); """) oscrypto-1.3.0/oscrypto/_openssl/_libcrypto_ctypes.py000066400000000000000000000424061421476274700232340ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import re from ctypes import CDLL, c_void_p, c_char_p, c_int, c_ulong, c_uint, c_long, c_size_t, POINTER from .. import _backend_config from .._errors import pretty_message from .._ffi import FFIEngineError, get_library from ..errors import LibraryNotFoundError __all__ = [ 'is_libressl', 'libcrypto', 'libressl_version', 'libressl_version_info', 'version', 'version_info', ] libcrypto_path = _backend_config().get('libcrypto_path') if libcrypto_path is None: libcrypto_path = get_library('crypto', 'libcrypto.dylib', '42') if not libcrypto_path: raise LibraryNotFoundError('The library libcrypto could not be found') libcrypto = CDLL(libcrypto_path, use_errno=True) try: libcrypto.SSLeay_version.argtypes = [c_int] libcrypto.SSLeay_version.restype = c_char_p version_string = libcrypto.SSLeay_version(0).decode('utf-8') except (AttributeError): libcrypto.OpenSSL_version.argtypes = [c_int] libcrypto.OpenSSL_version.restype = c_char_p version_string = libcrypto.OpenSSL_version(0).decode('utf-8') is_libressl = 'LibreSSL' in version_string version_match = re.search('\\b(\\d\\.\\d\\.\\d[a-z]*)\\b', version_string) if not version_match: version_match = re.search('(?<=LibreSSL )(\\d\\.\\d(\\.\\d)?)\\b', version_string) if not version_match: raise LibraryNotFoundError('Error detecting the version of libcrypto') version = version_match.group(1) version_parts = re.sub('(\\d)([a-z]+)', '\\1.\\2', version).split('.') version_info = tuple(int(part) if part.isdigit() else part for part in version_parts) # LibreSSL is compatible with libcrypto from OpenSSL 1.0.1 libressl_version = '' libressl_version_info = tuple() if is_libressl: libressl_version = version libressl_version_info = version_info version = '1.0.1' version_info = (1, 0, 1) if version_info < (0, 9, 8): raise LibraryNotFoundError(pretty_message( ''' OpenSSL versions older than 0.9.8 are not supported - found version %s ''', version )) P_EVP_CIPHER_CTX = c_void_p P_EVP_CIPHER = c_void_p P_EVP_MD_CTX = c_void_p P_EVP_MD = c_void_p P_ENGINE = c_void_p OSSL_PROVIDER = c_void_p OSSL_LIB_CTX = c_void_p P_EVP_PKEY = c_void_p EVP_PKEY_CTX = c_void_p P_EVP_PKEY_CTX = POINTER(c_void_p) P_X509 = POINTER(c_void_p) P_DH = c_void_p P_RSA = c_void_p P_DSA = c_void_p P_EC_KEY = c_void_p P_BN_GENCB = c_void_p BIGNUM = c_void_p P_BIGNUM = POINTER(BIGNUM) p_int = POINTER(c_int) p_uint = POINTER(c_uint) try: if version_info < (1, 1): libcrypto.ERR_load_crypto_strings.argtypes = [] libcrypto.ERR_load_crypto_strings.restype = None libcrypto.ERR_free_strings.argtypes = [] libcrypto.ERR_free_strings.restype = None if version_info >= (3, ): libcrypto.OSSL_PROVIDER_available.argtypes = [OSSL_LIB_CTX, c_char_p] libcrypto.OSSL_PROVIDER_available.restype = c_int libcrypto.OSSL_PROVIDER_load.argtypes = [OSSL_LIB_CTX, c_char_p] libcrypto.OSSL_PROVIDER_load.restype = POINTER(OSSL_PROVIDER) libcrypto.ERR_get_error.argtypes = [] libcrypto.ERR_get_error.restype = c_ulong libcrypto.ERR_peek_error.argtypes = [] libcrypto.ERR_peek_error.restype = c_ulong libcrypto.ERR_error_string.argtypes = [ c_ulong, c_char_p ] libcrypto.ERR_error_string.restype = c_char_p libcrypto.OPENSSL_config.argtypes = [ c_char_p ] libcrypto.OPENSSL_config.restype = None # This allocates the memory and inits libcrypto.EVP_CIPHER_CTX_new.argtype = [] libcrypto.EVP_CIPHER_CTX_new.restype = P_EVP_CIPHER_CTX libcrypto.EVP_CIPHER_CTX_set_key_length.argtypes = [ P_EVP_CIPHER_CTX, c_int ] libcrypto.EVP_CIPHER_CTX_set_key_length.restype = c_int libcrypto.EVP_CIPHER_CTX_set_padding.argtypes = [ P_EVP_CIPHER_CTX, c_int ] libcrypto.EVP_CIPHER_CTX_set_padding.restype = c_int libcrypto.EVP_CIPHER_CTX_ctrl.argtypes = [ P_EVP_CIPHER_CTX, c_int, c_int, c_void_p ] libcrypto.EVP_CIPHER_CTX_ctrl.restype = c_int # This cleans up and frees libcrypto.EVP_CIPHER_CTX_free.argtypes = [ P_EVP_CIPHER_CTX ] libcrypto.EVP_CIPHER_CTX_free.restype = None libcrypto.EVP_aes_128_cbc.argtypes = [] libcrypto.EVP_aes_128_cbc.restype = P_EVP_CIPHER libcrypto.EVP_aes_192_cbc.argtypes = [] libcrypto.EVP_aes_192_cbc.restype = P_EVP_CIPHER libcrypto.EVP_aes_256_cbc.argtypes = [] libcrypto.EVP_aes_256_cbc.restype = P_EVP_CIPHER libcrypto.EVP_des_cbc.argtypes = [] libcrypto.EVP_des_cbc.restype = P_EVP_CIPHER libcrypto.EVP_des_ede_cbc.argtypes = [] libcrypto.EVP_des_ede_cbc.restype = P_EVP_CIPHER libcrypto.EVP_des_ede3_cbc.argtypes = [] libcrypto.EVP_des_ede3_cbc.restype = P_EVP_CIPHER libcrypto.EVP_rc4.argtypes = [] libcrypto.EVP_rc4.restype = P_EVP_CIPHER libcrypto.EVP_rc2_cbc.argtypes = [] libcrypto.EVP_rc2_cbc.restype = P_EVP_CIPHER libcrypto.EVP_EncryptInit_ex.argtypes = [ P_EVP_CIPHER_CTX, P_EVP_CIPHER, P_ENGINE, c_char_p, c_char_p ] libcrypto.EVP_EncryptInit_ex.restype = c_int libcrypto.EVP_EncryptUpdate.argtypes = [ P_EVP_CIPHER_CTX, c_char_p, p_int, c_char_p, c_int ] libcrypto.EVP_EncryptUpdate.restype = c_int libcrypto.EVP_EncryptFinal_ex.argtypes = [ P_EVP_CIPHER_CTX, c_char_p, p_int ] libcrypto.EVP_EncryptFinal_ex.restype = c_int libcrypto.EVP_DecryptInit_ex.argtypes = [ P_EVP_CIPHER_CTX, P_EVP_CIPHER, P_ENGINE, c_char_p, c_char_p ] libcrypto.EVP_DecryptInit_ex.restype = c_int libcrypto.EVP_DecryptUpdate.argtypes = [ P_EVP_CIPHER_CTX, c_char_p, p_int, c_char_p, c_int ] libcrypto.EVP_DecryptUpdate.restype = c_int libcrypto.EVP_DecryptFinal_ex.argtypes = [ P_EVP_CIPHER_CTX, c_char_p, p_int ] libcrypto.EVP_DecryptFinal_ex.restype = c_int libcrypto.d2i_AutoPrivateKey.argtypes = [ POINTER(P_EVP_PKEY), POINTER(c_char_p), c_int ] libcrypto.d2i_AutoPrivateKey.restype = P_EVP_PKEY libcrypto.d2i_PUBKEY.argtypes = [ POINTER(P_EVP_PKEY), POINTER(c_char_p), c_int ] libcrypto.d2i_PUBKEY.restype = P_EVP_PKEY libcrypto.i2d_PUBKEY.argtypes = [ P_EVP_PKEY, POINTER(c_char_p) ] libcrypto.i2d_PUBKEY.restype = c_int libcrypto.d2i_X509.argtypes = [ POINTER(P_X509), POINTER(c_char_p), c_int ] libcrypto.d2i_X509.restype = P_X509 libcrypto.i2d_X509.argtypes = [ P_X509, POINTER(c_char_p) ] libcrypto.i2d_X509.restype = c_int libcrypto.X509_get_pubkey.argtypes = [ P_X509 ] libcrypto.X509_get_pubkey.restype = P_EVP_PKEY libcrypto.X509_free.argtypes = [ P_X509 ] libcrypto.X509_free.restype = None libcrypto.EVP_PKEY_free.argtypes = [ P_EVP_PKEY ] libcrypto.EVP_PKEY_free.restype = None if version_info < (1, 1): libcrypto.EVP_MD_CTX_create.argtypes = [] libcrypto.EVP_MD_CTX_create.restype = P_EVP_MD_CTX libcrypto.EVP_MD_CTX_destroy.argtypes = [ P_EVP_MD_CTX ] libcrypto.EVP_MD_CTX_destroy.restype = None else: libcrypto.EVP_MD_CTX_new.argtypes = [] libcrypto.EVP_MD_CTX_new.restype = P_EVP_MD_CTX libcrypto.EVP_MD_CTX_free.argtypes = [ P_EVP_MD_CTX ] libcrypto.EVP_MD_CTX_free.restype = None libcrypto.EVP_md5.argtypes = [] libcrypto.EVP_md5.restype = P_EVP_MD libcrypto.EVP_sha1.argtypes = [] libcrypto.EVP_sha1.restype = P_EVP_MD libcrypto.EVP_sha224.argtypes = [] libcrypto.EVP_sha224.restype = P_EVP_MD libcrypto.EVP_sha256.argtypes = [] libcrypto.EVP_sha256.restype = P_EVP_MD libcrypto.EVP_sha384.argtypes = [] libcrypto.EVP_sha384.restype = P_EVP_MD libcrypto.EVP_sha512.argtypes = [] libcrypto.EVP_sha512.restype = P_EVP_MD if version_info < (3, 0): libcrypto.EVP_PKEY_size.argtypes = [ P_EVP_PKEY ] libcrypto.EVP_PKEY_size.restype = c_int else: libcrypto.EVP_PKEY_get_size.argtypes = [ P_EVP_PKEY ] libcrypto.EVP_PKEY_get_size.restype = c_int libcrypto.EVP_PKEY_get1_RSA.argtypes = [ P_EVP_PKEY ] libcrypto.EVP_PKEY_get1_RSA.restype = P_RSA libcrypto.RSA_free.argtypes = [ P_RSA ] libcrypto.RSA_free.restype = None libcrypto.RSA_public_encrypt.argtypes = [ c_int, c_char_p, c_char_p, P_RSA, c_int ] libcrypto.RSA_public_encrypt.restype = c_int libcrypto.RSA_private_encrypt.argtypes = [ c_int, c_char_p, c_char_p, P_RSA, c_int ] libcrypto.RSA_private_encrypt.restype = c_int libcrypto.RSA_public_decrypt.argtypes = [ c_int, c_char_p, c_char_p, P_RSA, c_int ] libcrypto.RSA_public_decrypt.restype = c_int libcrypto.RSA_private_decrypt.argtypes = [ c_int, c_char_p, c_char_p, P_RSA, c_int ] libcrypto.RSA_private_decrypt.restype = c_int libcrypto.EVP_DigestUpdate.argtypes = [ P_EVP_MD_CTX, c_char_p, c_uint ] libcrypto.EVP_DigestUpdate.restype = c_int libcrypto.PKCS12_key_gen_uni.argtypes = [ c_char_p, c_int, c_char_p, c_int, c_int, c_int, c_int, c_char_p, c_void_p ] libcrypto.PKCS12_key_gen_uni.restype = c_int libcrypto.BN_free.argtypes = [ P_BIGNUM ] libcrypto.BN_free.restype = None libcrypto.BN_dec2bn.argtypes = [ POINTER(P_BIGNUM), c_char_p ] libcrypto.BN_dec2bn.restype = c_int libcrypto.DH_new.argtypes = [] libcrypto.DH_new.restype = P_DH libcrypto.DH_generate_parameters_ex.argtypes = [ P_DH, c_int, c_int, P_BN_GENCB ] libcrypto.DH_generate_parameters_ex.restype = c_int libcrypto.i2d_DHparams.argtypes = [ P_DH, POINTER(c_char_p) ] libcrypto.i2d_DHparams.restype = c_int libcrypto.DH_free.argtypes = [ P_DH ] libcrypto.DH_free.restype = None libcrypto.RSA_new.argtypes = [] libcrypto.RSA_new.restype = P_RSA libcrypto.RSA_generate_key_ex.argtypes = [ P_RSA, c_int, P_BIGNUM, P_BN_GENCB ] libcrypto.RSA_generate_key_ex.restype = c_int libcrypto.i2d_RSAPublicKey.argtypes = [ P_RSA, POINTER(c_char_p) ] libcrypto.i2d_RSAPublicKey.restype = c_int libcrypto.i2d_RSAPrivateKey.argtypes = [ P_RSA, POINTER(c_char_p) ] libcrypto.i2d_RSAPrivateKey.restype = c_int libcrypto.RSA_free.argtypes = [ P_RSA ] libcrypto.RSA_free.restype = None libcrypto.DSA_new.argtypes = [] libcrypto.DSA_new.restype = P_DSA libcrypto.DSA_generate_parameters_ex.argtypes = [ P_DSA, c_int, c_char_p, c_int, POINTER(c_int), POINTER(c_ulong), P_BN_GENCB ] libcrypto.DSA_generate_parameters_ex.restype = c_int libcrypto.DSA_generate_key.argtypes = [ P_DSA ] libcrypto.DSA_generate_key.restype = c_int libcrypto.i2d_DSA_PUBKEY.argtypes = [ P_DSA, POINTER(c_char_p) ] libcrypto.i2d_DSA_PUBKEY.restype = c_int libcrypto.i2d_DSAPrivateKey.argtypes = [ P_DSA, POINTER(c_char_p) ] libcrypto.i2d_DSAPrivateKey.restype = c_int libcrypto.DSA_free.argtypes = [ P_DSA ] libcrypto.DSA_free.restype = None libcrypto.EC_KEY_new_by_curve_name.argtypes = [ c_int ] libcrypto.EC_KEY_new_by_curve_name.restype = P_EC_KEY libcrypto.EC_KEY_generate_key.argtypes = [ P_EC_KEY ] libcrypto.EC_KEY_generate_key.restype = c_int libcrypto.EC_KEY_set_asn1_flag.argtypes = [ P_EC_KEY, c_int ] libcrypto.EC_KEY_set_asn1_flag.restype = None libcrypto.i2d_ECPrivateKey.argtypes = [ P_EC_KEY, POINTER(c_char_p) ] libcrypto.i2d_ECPrivateKey.restype = c_int libcrypto.i2o_ECPublicKey.argtypes = [ P_EC_KEY, POINTER(c_char_p) ] libcrypto.i2o_ECPublicKey.restype = c_int libcrypto.EC_KEY_free.argtypes = [ P_EC_KEY ] libcrypto.EC_KEY_free.restype = None if version_info < (1,): P_DSA_SIG = c_void_p P_ECDSA_SIG = c_void_p libcrypto.DSA_do_sign.argtypes = [ c_char_p, c_int, P_DSA ] libcrypto.DSA_do_sign.restype = P_DSA_SIG libcrypto.ECDSA_do_sign.argtypes = [ c_char_p, c_int, P_EC_KEY ] libcrypto.ECDSA_do_sign.restype = P_ECDSA_SIG libcrypto.d2i_DSA_SIG.argtypes = [ POINTER(P_DSA_SIG), POINTER(c_char_p), c_long ] libcrypto.d2i_DSA_SIG.restype = P_DSA_SIG libcrypto.d2i_ECDSA_SIG.argtypes = [ POINTER(P_ECDSA_SIG), POINTER(c_char_p), c_long ] libcrypto.d2i_ECDSA_SIG.restype = P_ECDSA_SIG libcrypto.i2d_DSA_SIG.argtypes = [ P_DSA_SIG, POINTER(c_char_p) ] libcrypto.i2d_DSA_SIG.restype = c_int libcrypto.i2d_ECDSA_SIG.argtypes = [ P_ECDSA_SIG, POINTER(c_char_p) ] libcrypto.i2d_ECDSA_SIG.restype = c_int libcrypto.DSA_do_verify.argtypes = [ c_char_p, c_int, P_DSA_SIG, P_DSA ] libcrypto.DSA_do_verify.restype = c_int libcrypto.ECDSA_do_verify.argtypes = [ c_char_p, c_int, P_ECDSA_SIG, P_EC_KEY ] libcrypto.ECDSA_do_verify.restype = c_int libcrypto.DSA_SIG_free.argtypes = [ P_DSA_SIG ] libcrypto.DSA_SIG_free.restype = None libcrypto.ECDSA_SIG_free.argtypes = [ P_ECDSA_SIG ] libcrypto.ECDSA_SIG_free.restype = None libcrypto.EVP_PKEY_get1_DSA.argtypes = [ P_EVP_PKEY ] libcrypto.EVP_PKEY_get1_DSA.restype = P_DSA libcrypto.EVP_PKEY_get1_EC_KEY.argtypes = [ P_EVP_PKEY ] libcrypto.EVP_PKEY_get1_EC_KEY.restype = P_EC_KEY libcrypto.RSA_verify_PKCS1_PSS.argtypes = [ P_RSA, c_char_p, P_EVP_MD, c_char_p, c_int ] libcrypto.RSA_verify_PKCS1_PSS.restype = c_int libcrypto.RSA_padding_add_PKCS1_PSS.argtypes = [ P_RSA, c_char_p, c_char_p, P_EVP_MD, c_int ] libcrypto.RSA_padding_add_PKCS1_PSS.restype = c_int libcrypto.EVP_DigestInit_ex.argtypes = [ P_EVP_MD_CTX, P_EVP_MD, P_ENGINE ] libcrypto.EVP_DigestInit_ex.restype = c_int libcrypto.EVP_SignFinal.argtypes = [ P_EVP_MD_CTX, c_char_p, p_uint, P_EVP_PKEY ] libcrypto.EVP_SignFinal.restype = c_int libcrypto.EVP_VerifyFinal.argtypes = [ P_EVP_MD_CTX, c_char_p, c_uint, P_EVP_PKEY ] libcrypto.EVP_VerifyFinal.restype = c_int libcrypto.EVP_MD_CTX_set_flags.argtypes = [ P_EVP_MD_CTX, c_int ] libcrypto.EVP_MD_CTX_set_flags.restype = None else: libcrypto.PKCS5_PBKDF2_HMAC.argtypes = [ c_char_p, c_int, c_char_p, c_int, c_int, P_EVP_MD, c_int, c_char_p ] libcrypto.PKCS5_PBKDF2_HMAC.restype = c_int libcrypto.EVP_DigestSignInit.argtypes = [ P_EVP_MD_CTX, POINTER(P_EVP_PKEY_CTX), P_EVP_MD, P_ENGINE, P_EVP_PKEY ] libcrypto.EVP_DigestSignInit.restype = c_int libcrypto.EVP_DigestSignFinal.argtypes = [ P_EVP_MD_CTX, c_char_p, POINTER(c_size_t) ] libcrypto.EVP_DigestSignFinal.restype = c_int libcrypto.EVP_DigestVerifyInit.argtypes = [ P_EVP_MD_CTX, POINTER(P_EVP_PKEY_CTX), P_EVP_MD, P_ENGINE, P_EVP_PKEY ] libcrypto.EVP_DigestVerifyInit.restype = c_int libcrypto.EVP_DigestVerifyFinal.argtypes = [ P_EVP_MD_CTX, c_char_p, c_size_t ] libcrypto.EVP_DigestVerifyFinal.restype = c_int libcrypto.EVP_PKEY_CTX_ctrl.argtypes = [ P_EVP_PKEY_CTX, c_int, c_int, c_int, c_int, c_void_p ] libcrypto.EVP_PKEY_CTX_ctrl.restype = c_int except (AttributeError): raise FFIEngineError('Error initializing ctypes') setattr(libcrypto, 'EVP_PKEY_CTX', EVP_PKEY_CTX) setattr(libcrypto, 'BIGNUM', BIGNUM) oscrypto-1.3.0/oscrypto/_openssl/_libssl.py000066400000000000000000000041631421476274700211240ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .. import ffi # Initialize OpenSSL from ._libcrypto import libcrypto_version_info if ffi() == 'cffi': from ._libssl_cffi import libssl else: from ._libssl_ctypes import libssl __all__ = [ 'libssl', 'LibsslConst', ] if libcrypto_version_info < (1, 1): libssl.SSL_library_init() # Enables SHA2 algorithms on 0.9.8n and older if libcrypto_version_info < (1, 0): libssl.OPENSSL_add_all_algorithms_noconf() class LibsslConst(): ERR_LIB_ASN1 = 13 ERR_LIB_SSL = 20 SSL_CTRL_OPTIONS = 32 SSL_CTRL_SET_SESS_CACHE_MODE = 44 SSL_VERIFY_NONE = 0 SSL_VERIFY_PEER = 1 SSL_ST_OK = 3 SSL_ERROR_WANT_READ = 2 SSL_ERROR_WANT_WRITE = 3 SSL_ERROR_ZERO_RETURN = 6 SSL_OP_NO_SSLv2 = 0x01000000 SSL_OP_NO_SSLv3 = 0x02000000 SSL_OP_NO_TLSv1 = 0x04000000 SSL_OP_NO_TLSv1_2 = 0x08000000 SSL_OP_NO_TLSv1_1 = 0x10000000 SSL_SESS_CACHE_CLIENT = 0x0001 SSL_R_NO_SHARED_CIPHER = 193 SSL_F_SSL3_CHECK_CERT_AND_ALGORITHM = 130 SSL_F_SSL3_GET_KEY_EXCHANGE = 141 SSL_F_SSL3_GET_SERVER_CERTIFICATE = 144 SSL_R_BAD_DH_P_LENGTH = 110 SSL_R_CERTIFICATE_VERIFY_FAILED = 134 SSL_R_UNKNOWN_PROTOCOL = 252 SSL_R_DH_KEY_TOO_SMALL = 372 # OpenSSL 1.1.0 SSL_F_TLS_PROCESS_SKE_DHE = 419 SSL_F_SSL3_GET_RECORD = 143 SSL_R_WRONG_VERSION_NUMBER = 267 SSL_F_TLS_PROCESS_SERVER_CERTIFICATE = 367 # OpenSSL < 1.1.0 SSL_F_SSL23_GET_SERVER_HELLO = 119 SSL_F_SSL3_READ_BYTES = 148 SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE = 1040 SSL_R_TLSV1_ALERT_PROTOCOL_VERSION = 1070 SSL_CTRL_SET_TLSEXT_HOSTNAME = 55 TLSEXT_NAMETYPE_host_name = 0 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY = 20 X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN = 19 X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT = 18 X509_V_ERR_CERT_NOT_YET_VALID = 9 X509_V_ERR_CERT_HAS_EXPIRED = 10 ASN1_F_ASN1_ITEM_VERIFY = 197 ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM = 161 if libcrypto_version_info >= (1, 1, 0): LibsslConst.SSL_R_DH_KEY_TOO_SMALL = 394 oscrypto-1.3.0/oscrypto/_openssl/_libssl_cffi.py000066400000000000000000000057611421476274700221200ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .. import _backend_config from .._ffi import get_library, register_ffi from ..errors import LibraryNotFoundError from ._libcrypto import libcrypto_version_info from cffi import FFI __all__ = [ 'libssl', ] ffi = FFI() libssl_path = _backend_config().get('libssl_path') if libssl_path is None: libssl_path = get_library('ssl', 'libssl', '44') if not libssl_path: raise LibraryNotFoundError('The library libssl could not be found') libssl = ffi.dlopen(libssl_path) register_ffi(libssl, ffi) ffi.cdef(""" typedef ... SSL_METHOD; typedef uintptr_t SSL_CTX; typedef ... SSL_SESSION; typedef uintptr_t SSL; typedef ... BIO_METHOD; typedef uintptr_t BIO; typedef uintptr_t X509; typedef ... X509_STORE; typedef ... X509_STORE_CTX; typedef uintptr_t _STACK; BIO_METHOD *BIO_s_mem(void); BIO *BIO_new(BIO_METHOD *type); int BIO_free(BIO *a); int BIO_read(BIO *b, void *buf, int len); int BIO_write(BIO *b, const void *buf, int len); size_t BIO_ctrl_pending(BIO *b); SSL_CTX *SSL_CTX_new(const SSL_METHOD *method); long SSL_CTX_set_timeout(SSL_CTX *ctx, long t); void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)); int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx); int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath); long SSL_get_verify_result(const SSL *ssl); X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *ctx); int X509_STORE_add_cert(X509_STORE *ctx, X509 *x); int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str); long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg); void SSL_CTX_free(SSL_CTX *a); SSL *SSL_new(SSL_CTX *ctx); void SSL_free(SSL *ssl); void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio); long SSL_ctrl(SSL *ssl, int cmd, long larg, void *parg); _STACK *SSL_get_peer_cert_chain(const SSL *s); SSL_SESSION *SSL_get1_session(const SSL *ssl); int SSL_set_session(SSL *ssl, SSL_SESSION *session); void SSL_SESSION_free(SSL_SESSION *session); void SSL_set_connect_state(SSL *ssl); int SSL_do_handshake(SSL *ssl); int SSL_get_error(const SSL *ssl, int ret); const char *SSL_get_version(const SSL *ssl); int SSL_read(SSL *ssl, void *buf, int num); int SSL_write(SSL *ssl, const void *buf, int num); int SSL_pending(const SSL *ssl); int SSL_shutdown(SSL *ssl); """) if libcrypto_version_info < (1, 1): ffi.cdef(""" int sk_num(const _STACK *); X509 *sk_value(const _STACK *, int); int SSL_library_init(void); void OPENSSL_add_all_algorithms_noconf(void); SSL_METHOD *SSLv23_method(void); """) else: ffi.cdef(""" int OPENSSL_sk_num(const _STACK *); X509 *OPENSSL_sk_value(const _STACK *, int); SSL_METHOD *TLS_method(void); """) oscrypto-1.3.0/oscrypto/_openssl/_libssl_ctypes.py000066400000000000000000000136121421476274700225120ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from ctypes import CDLL, CFUNCTYPE, POINTER, c_void_p, c_char_p, c_int, c_size_t, c_long from .. import _backend_config from .._ffi import FFIEngineError, get_library from ..errors import LibraryNotFoundError from ._libcrypto import libcrypto_version_info __all__ = [ 'libssl', ] libssl_path = _backend_config().get('libssl_path') if libssl_path is None: libssl_path = get_library('ssl', 'libssl', '44') if not libssl_path: raise LibraryNotFoundError('The library libssl could not be found') libssl = CDLL(libssl_path, use_errno=True) P_SSL_METHOD = POINTER(c_void_p) P_SSL_CTX = POINTER(c_void_p) P_SSL_SESSION = POINTER(c_void_p) P_SSL = POINTER(c_void_p) P_BIO_METHOD = POINTER(c_void_p) P_BIO = POINTER(c_void_p) X509 = c_void_p P_X509 = POINTER(X509) P_X509_STORE = POINTER(c_void_p) P_X509_STORE_CTX = POINTER(c_void_p) _STACK = c_void_p P_STACK = POINTER(_STACK) try: if libcrypto_version_info < (1, 1): libssl.sk_num.argtypes = [P_STACK] libssl.sk_num.restype = c_int libssl.sk_value.argtypes = [P_STACK, c_int] libssl.sk_value.restype = P_X509 libssl.SSL_library_init.argtypes = [] libssl.SSL_library_init.restype = c_int libssl.OPENSSL_add_all_algorithms_noconf.argtypes = [] libssl.OPENSSL_add_all_algorithms_noconf.restype = None libssl.SSLv23_method.argtypes = [] libssl.SSLv23_method.restype = P_SSL_METHOD else: libssl.OPENSSL_sk_num.argtypes = [P_STACK] libssl.OPENSSL_sk_num.restype = c_int libssl.OPENSSL_sk_value.argtypes = [P_STACK, c_int] libssl.OPENSSL_sk_value.restype = P_X509 libssl.TLS_method.argtypes = [] libssl.TLS_method.restype = P_SSL_METHOD libssl.BIO_s_mem.argtypes = [] libssl.BIO_s_mem.restype = P_BIO_METHOD libssl.BIO_new.argtypes = [ P_BIO_METHOD ] libssl.BIO_new.restype = P_BIO libssl.BIO_free.argtypes = [ P_BIO ] libssl.BIO_free.restype = c_int libssl.BIO_read.argtypes = [ P_BIO, c_char_p, c_int ] libssl.BIO_read.restype = c_int libssl.BIO_write.argtypes = [ P_BIO, c_char_p, c_int ] libssl.BIO_write.restype = c_int libssl.BIO_ctrl_pending.argtypes = [ P_BIO ] libssl.BIO_ctrl_pending.restype = c_size_t libssl.SSL_CTX_new.argtypes = [ P_SSL_METHOD ] libssl.SSL_CTX_new.restype = P_SSL_CTX libssl.SSL_CTX_set_timeout.argtypes = [ P_SSL_CTX, c_long ] libssl.SSL_CTX_set_timeout.restype = c_long verify_callback = CFUNCTYPE(c_int, c_int, P_X509_STORE_CTX) setattr(libssl, 'verify_callback', verify_callback) libssl.SSL_CTX_set_verify.argtypes = [ P_SSL_CTX, c_int, POINTER(verify_callback) ] libssl.SSL_CTX_set_verify.restype = None libssl.SSL_CTX_set_default_verify_paths.argtypes = [ P_SSL_CTX ] libssl.SSL_CTX_set_default_verify_paths.restype = c_int libssl.SSL_CTX_load_verify_locations.argtypes = [ P_SSL_CTX, c_char_p, c_char_p ] libssl.SSL_CTX_load_verify_locations.restype = c_int libssl.SSL_get_verify_result.argtypes = [ P_SSL ] libssl.SSL_get_verify_result.restype = c_long libssl.SSL_CTX_get_cert_store.argtypes = [ P_SSL_CTX ] libssl.SSL_CTX_get_cert_store.restype = P_X509_STORE libssl.X509_STORE_add_cert.argtypes = [ P_X509_STORE, P_X509 ] libssl.X509_STORE_add_cert.restype = c_int libssl.SSL_CTX_set_cipher_list.argtypes = [ P_SSL_CTX, c_char_p ] libssl.SSL_CTX_set_cipher_list.restype = c_int libssl.SSL_CTX_ctrl.arg_types = [ P_SSL_CTX, c_int, c_long, c_void_p ] libssl.SSL_CTX_ctrl.restype = c_long libssl.SSL_CTX_free.argtypes = [ P_SSL_CTX ] libssl.SSL_CTX_free.restype = None libssl.SSL_new.argtypes = [ P_SSL_CTX ] libssl.SSL_new.restype = P_SSL libssl.SSL_free.argtypes = [ P_SSL ] libssl.SSL_free.restype = None libssl.SSL_set_bio.argtypes = [ P_SSL, P_BIO, P_BIO ] libssl.SSL_set_bio.restype = None libssl.SSL_ctrl.arg_types = [ P_SSL, c_int, c_long, c_void_p ] libssl.SSL_ctrl.restype = c_long libssl.SSL_get_peer_cert_chain.argtypes = [ P_SSL ] libssl.SSL_get_peer_cert_chain.restype = P_STACK libssl.SSL_get1_session.argtypes = [ P_SSL ] libssl.SSL_get1_session.restype = P_SSL_SESSION libssl.SSL_set_session.argtypes = [ P_SSL, P_SSL_SESSION ] libssl.SSL_set_session.restype = c_int libssl.SSL_SESSION_free.argtypes = [ P_SSL_SESSION ] libssl.SSL_SESSION_free.restype = None libssl.SSL_set_connect_state.argtypes = [ P_SSL ] libssl.SSL_set_connect_state.restype = None libssl.SSL_do_handshake.argtypes = [ P_SSL ] libssl.SSL_do_handshake.restype = c_int libssl.SSL_get_error.argtypes = [ P_SSL, c_int ] libssl.SSL_get_error.restype = c_int libssl.SSL_get_version.argtypes = [ P_SSL ] libssl.SSL_get_version.restype = c_char_p libssl.SSL_read.argtypes = [ P_SSL, c_char_p, c_int ] libssl.SSL_read.restype = c_int libssl.SSL_write.argtypes = [ P_SSL, c_char_p, c_int ] libssl.SSL_write.restype = c_int libssl.SSL_pending.argtypes = [ P_SSL ] libssl.SSL_pending.restype = c_int libssl.SSL_shutdown.argtypes = [ P_SSL ] libssl.SSL_shutdown.restype = c_int except (AttributeError): raise FFIEngineError('Error initializing ctypes') setattr(libssl, '_STACK', _STACK) setattr(libssl, 'X509', X509) oscrypto-1.3.0/oscrypto/_openssl/asymmetric.py000066400000000000000000001730731421476274700216610ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import hashlib from .._asn1 import ( Certificate as Asn1Certificate, DHParameters, ECDomainParameters, PrivateKeyInfo, PublicKeyAlgorithm, PublicKeyInfo, ) from .._asymmetric import ( _CertificateBase, _fingerprint, _parse_pkcs12, _PrivateKeyBase, _PublicKeyBase, _unwrap_private_key_info, parse_certificate, parse_private, parse_public, ) from .._errors import pretty_message from .._ffi import ( buffer_from_bytes, buffer_pointer, bytes_from_buffer, deref, is_null, new, null, unwrap, write_to_buffer, ) from ._libcrypto import libcrypto, LibcryptoConst, libcrypto_version_info, handle_openssl_error from ..errors import AsymmetricKeyError, IncompleteAsymmetricKeyError, SignatureError from .._types import type_name, str_cls, byte_cls, int_types from ..util import constant_compare __all__ = [ 'Certificate', 'dsa_sign', 'dsa_verify', 'ecdsa_sign', 'ecdsa_verify', 'generate_pair', 'load_certificate', 'load_pkcs12', 'load_private_key', 'load_public_key', 'parse_pkcs12', 'PrivateKey', 'PublicKey', 'rsa_oaep_decrypt', 'rsa_oaep_encrypt', 'rsa_pkcs1v15_decrypt', 'rsa_pkcs1v15_encrypt', 'rsa_pkcs1v15_sign', 'rsa_pkcs1v15_verify', 'rsa_pss_sign', 'rsa_pss_verify', ] class PrivateKey(_PrivateKeyBase): """ Container for the OpenSSL representation of a private key """ evp_pkey = None _public_key = None # A reference to the library used in the destructor to make sure it hasn't # been garbage collected by the time this object is garbage collected _lib = None def __init__(self, evp_pkey, asn1): """ :param evp_pkey: An OpenSSL EVP_PKEY value from loading/importing the key :param asn1: An asn1crypto.keys.PrivateKeyInfo object """ self.evp_pkey = evp_pkey self.asn1 = asn1 self._lib = libcrypto @property def public_key(self): """ :return: A PublicKey object corresponding to this private key. """ if self._public_key is None: buffer_size = libcrypto.i2d_PUBKEY(self.evp_pkey, null()) pubkey_buffer = buffer_from_bytes(buffer_size) pubkey_pointer = buffer_pointer(pubkey_buffer) pubkey_length = libcrypto.i2d_PUBKEY(self.evp_pkey, pubkey_pointer) handle_openssl_error(pubkey_length) pubkey_data = bytes_from_buffer(pubkey_buffer, pubkey_length) asn1 = PublicKeyInfo.load(pubkey_data) # OpenSSL 1.x suffers from issues trying to use RSASSA-PSS keys, so we # masquerade it as a normal RSA key so the OID checks work if libcrypto_version_info < (3,) and asn1.algorithm == 'rsassa_pss': temp_asn1 = asn1.copy() temp_asn1['algorithm']['algorithm'] = 'rsa' temp_data = temp_asn1.dump() write_to_buffer(pubkey_buffer, temp_data) pubkey_length = len(temp_data) pub_evp_pkey = libcrypto.d2i_PUBKEY(null(), buffer_pointer(pubkey_buffer), pubkey_length) if is_null(pub_evp_pkey): handle_openssl_error(0) self._public_key = PublicKey(pub_evp_pkey, asn1) return self._public_key @property def fingerprint(self): """ Creates a fingerprint that can be compared with a public key to see if the two form a pair. This fingerprint is not compatible with fingerprints generated by any other software. :return: A byte string that is a sha256 hash of selected components (based on the key type) """ if self._fingerprint is None: self._fingerprint = _fingerprint(self.asn1, load_private_key) return self._fingerprint def __del__(self): if self.evp_pkey: self._lib.EVP_PKEY_free(self.evp_pkey) self._lib = None self.evp_pkey = None class PublicKey(_PublicKeyBase): """ Container for the OpenSSL representation of a public key """ evp_pkey = None # A reference to the library used in the destructor to make sure it hasn't # been garbage collected by the time this object is garbage collected _lib = None def __init__(self, evp_pkey, asn1): """ :param evp_pkey: An OpenSSL EVP_PKEY value from loading/importing the key :param asn1: An asn1crypto.keys.PublicKeyInfo object """ self.evp_pkey = evp_pkey self.asn1 = asn1 self._lib = libcrypto def __del__(self): if self.evp_pkey: self._lib.EVP_PKEY_free(self.evp_pkey) self._lib = None self.evp_pkey = None class Certificate(_CertificateBase): """ Container for the OpenSSL representation of a certificate """ x509 = None _public_key = None _self_signed = None # A reference to the library used in the destructor to make sure it hasn't # been garbage collected by the time this object is garbage collected _lib = None def __init__(self, x509, asn1): """ :param x509: An OpenSSL X509 value from loading/importing the certificate :param asn1: An asn1crypto.x509.Certificate object """ self.x509 = x509 self.asn1 = asn1 self._lib = libcrypto @property def evp_pkey(self): """ :return: The EVP_PKEY of the public key this certificate contains """ return self.public_key.evp_pkey @property def public_key(self): """ :return: The PublicKey object for the public key this certificate contains """ if not self._public_key and self.x509: # OpenSSL 1.x suffers from issues trying to use RSASSA-PSS keys, so we # masquerade it as a normal RSA key so the OID checks work if libcrypto_version_info < (3,) and self.asn1.public_key.algorithm == 'rsassa_pss': self._public_key = load_public_key(self.asn1.public_key) else: evp_pkey = libcrypto.X509_get_pubkey(self.x509) self._public_key = PublicKey(evp_pkey, self.asn1.public_key) return self._public_key @property def self_signed(self): """ :return: A boolean - if the certificate is self-signed """ if self._self_signed is None: self._self_signed = False if self.asn1.self_signed in set(['yes', 'maybe']): signature_algo = self.asn1['signature_algorithm'].signature_algo hash_algo = self.asn1['signature_algorithm'].hash_algo if signature_algo == 'rsassa_pkcs1v15': verify_func = rsa_pkcs1v15_verify elif signature_algo == 'rsassa_pss': verify_func = rsa_pss_verify elif signature_algo == 'dsa': verify_func = dsa_verify elif signature_algo == 'ecdsa': verify_func = ecdsa_verify else: raise OSError(pretty_message( ''' Unable to verify the signature of the certificate since it uses the unsupported algorithm %s ''', signature_algo )) try: verify_func( self.public_key, self.asn1['signature_value'].native, self.asn1['tbs_certificate'].dump(), hash_algo ) self._self_signed = True except (SignatureError): pass return self._self_signed def __del__(self): if self._public_key: self._public_key.__del__() self._public_key = None if self.x509: self._lib.X509_free(self.x509) self._lib = None self.x509 = None def generate_pair(algorithm, bit_size=None, curve=None): """ Generates a public/private key pair :param algorithm: The key algorithm - "rsa", "dsa" or "ec" :param bit_size: An integer - used for "rsa" and "dsa". For "rsa" the value maye be 1024, 2048, 3072 or 4096. For "dsa" the value may be 1024, plus 2048 or 3072 if OpenSSL 1.0.0 or newer is available. :param curve: A unicode string - used for "ec" keys. Valid values include "secp256r1", "secp384r1" and "secp521r1". :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A 2-element tuple of (PublicKey, PrivateKey). The contents of each key may be saved by calling .asn1.dump(). """ if algorithm not in set(['rsa', 'dsa', 'ec']): raise ValueError(pretty_message( ''' algorithm must be one of "rsa", "dsa", "ec", not %s ''', repr(algorithm) )) if algorithm == 'rsa': if bit_size not in set([1024, 2048, 3072, 4096]): raise ValueError(pretty_message( ''' bit_size must be one of 1024, 2048, 3072, 4096, not %s ''', repr(bit_size) )) elif algorithm == 'dsa': if libcrypto_version_info < (1,): if bit_size != 1024: raise ValueError(pretty_message( ''' bit_size must be 1024, not %s ''', repr(bit_size) )) else: if bit_size not in set([1024, 2048, 3072]): raise ValueError(pretty_message( ''' bit_size must be one of 1024, 2048, 3072, not %s ''', repr(bit_size) )) elif algorithm == 'ec': if curve not in set(['secp256r1', 'secp384r1', 'secp521r1']): raise ValueError(pretty_message( ''' curve must be one of "secp256r1", "secp384r1", "secp521r1", not %s ''', repr(curve) )) if algorithm == 'rsa': rsa = None exponent = None try: rsa = libcrypto.RSA_new() if is_null(rsa): handle_openssl_error(0) exponent_pointer = new(libcrypto, 'BIGNUM **') result = libcrypto.BN_dec2bn(exponent_pointer, b'65537') handle_openssl_error(result) exponent = unwrap(exponent_pointer) result = libcrypto.RSA_generate_key_ex(rsa, bit_size, exponent, null()) handle_openssl_error(result) buffer_length = libcrypto.i2d_RSAPublicKey(rsa, null()) if buffer_length < 0: handle_openssl_error(buffer_length) buffer = buffer_from_bytes(buffer_length) result = libcrypto.i2d_RSAPublicKey(rsa, buffer_pointer(buffer)) if result < 0: handle_openssl_error(result) public_key_bytes = bytes_from_buffer(buffer, buffer_length) buffer_length = libcrypto.i2d_RSAPrivateKey(rsa, null()) if buffer_length < 0: handle_openssl_error(buffer_length) buffer = buffer_from_bytes(buffer_length) result = libcrypto.i2d_RSAPrivateKey(rsa, buffer_pointer(buffer)) if result < 0: handle_openssl_error(result) private_key_bytes = bytes_from_buffer(buffer, buffer_length) finally: if rsa: libcrypto.RSA_free(rsa) if exponent: libcrypto.BN_free(exponent) elif algorithm == 'dsa': dsa = None try: dsa = libcrypto.DSA_new() if is_null(dsa): handle_openssl_error(0) result = libcrypto.DSA_generate_parameters_ex(dsa, bit_size, null(), 0, null(), null(), null()) handle_openssl_error(result) result = libcrypto.DSA_generate_key(dsa) handle_openssl_error(result) buffer_length = libcrypto.i2d_DSA_PUBKEY(dsa, null()) if buffer_length < 0: handle_openssl_error(buffer_length) buffer = buffer_from_bytes(buffer_length) result = libcrypto.i2d_DSA_PUBKEY(dsa, buffer_pointer(buffer)) if result < 0: handle_openssl_error(result) public_key_bytes = bytes_from_buffer(buffer, buffer_length) buffer_length = libcrypto.i2d_DSAPrivateKey(dsa, null()) if buffer_length < 0: handle_openssl_error(buffer_length) buffer = buffer_from_bytes(buffer_length) result = libcrypto.i2d_DSAPrivateKey(dsa, buffer_pointer(buffer)) if result < 0: handle_openssl_error(result) private_key_bytes = bytes_from_buffer(buffer, buffer_length) finally: if dsa: libcrypto.DSA_free(dsa) elif algorithm == 'ec': ec_key = None try: curve_id = { 'secp256r1': LibcryptoConst.NID_X9_62_prime256v1, 'secp384r1': LibcryptoConst.NID_secp384r1, 'secp521r1': LibcryptoConst.NID_secp521r1, }[curve] ec_key = libcrypto.EC_KEY_new_by_curve_name(curve_id) if is_null(ec_key): handle_openssl_error(0) result = libcrypto.EC_KEY_generate_key(ec_key) handle_openssl_error(result) libcrypto.EC_KEY_set_asn1_flag(ec_key, LibcryptoConst.OPENSSL_EC_NAMED_CURVE) buffer_length = libcrypto.i2o_ECPublicKey(ec_key, null()) if buffer_length < 0: handle_openssl_error(buffer_length) buffer = buffer_from_bytes(buffer_length) result = libcrypto.i2o_ECPublicKey(ec_key, buffer_pointer(buffer)) if result < 0: handle_openssl_error(result) public_key_point_bytes = bytes_from_buffer(buffer, buffer_length) # i2o_ECPublicKey only returns the ECPoint bytes, so we have to # manually wrap it in a PublicKeyInfo structure to get it to parse public_key = PublicKeyInfo({ 'algorithm': PublicKeyAlgorithm({ 'algorithm': 'ec', 'parameters': ECDomainParameters( name='named', value=curve ) }), 'public_key': public_key_point_bytes }) public_key_bytes = public_key.dump() buffer_length = libcrypto.i2d_ECPrivateKey(ec_key, null()) if buffer_length < 0: handle_openssl_error(buffer_length) buffer = buffer_from_bytes(buffer_length) result = libcrypto.i2d_ECPrivateKey(ec_key, buffer_pointer(buffer)) if result < 0: handle_openssl_error(result) private_key_bytes = bytes_from_buffer(buffer, buffer_length) finally: if ec_key: libcrypto.EC_KEY_free(ec_key) return (load_public_key(public_key_bytes), load_private_key(private_key_bytes)) def generate_dh_parameters(bit_size): """ Generates DH parameters for use with Diffie-Hellman key exchange. Returns a structure in the format of DHParameter defined in PKCS#3, which is also used by the OpenSSL dhparam tool. THIS CAN BE VERY TIME CONSUMING! :param bit_size: The integer bit size of the parameters to generate. Must be between 512 and 4096, and divisible by 64. Recommended secure value as of early 2016 is 2048, with an absolute minimum of 1024. :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: An asn1crypto.algos.DHParameters object. Use oscrypto.asymmetric.dump_dh_parameters() to save to disk for usage with web servers. """ if not isinstance(bit_size, int_types): raise TypeError(pretty_message( ''' bit_size must be an integer, not %s ''', type_name(bit_size) )) if bit_size < 512: raise ValueError('bit_size must be greater than or equal to 512') if bit_size > 4096: raise ValueError('bit_size must be less than or equal to 4096') if bit_size % 64 != 0: raise ValueError('bit_size must be a multiple of 64') dh = None try: dh = libcrypto.DH_new() if is_null(dh): handle_openssl_error(0) result = libcrypto.DH_generate_parameters_ex(dh, bit_size, LibcryptoConst.DH_GENERATOR_2, null()) handle_openssl_error(result) buffer_length = libcrypto.i2d_DHparams(dh, null()) if buffer_length < 0: handle_openssl_error(buffer_length) buffer = buffer_from_bytes(buffer_length) result = libcrypto.i2d_DHparams(dh, buffer_pointer(buffer)) if result < 0: handle_openssl_error(result) dh_params_bytes = bytes_from_buffer(buffer, buffer_length) return DHParameters.load(dh_params_bytes) finally: if dh: libcrypto.DH_free(dh) def load_certificate(source): """ Loads an x509 certificate into a Certificate object :param source: A byte string of file contents, a unicode string filename or an asn1crypto.x509.Certificate object :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A Certificate object """ if isinstance(source, Asn1Certificate): certificate = source elif isinstance(source, byte_cls): certificate = parse_certificate(source) elif isinstance(source, str_cls): with open(source, 'rb') as f: certificate = parse_certificate(f.read()) else: raise TypeError(pretty_message( ''' source must be a byte string, unicode string or asn1crypto.x509.Certificate object, not %s ''', type_name(source) )) return _load_x509(certificate) def _load_x509(certificate): """ Loads an ASN.1 object of an x509 certificate into a Certificate object :param certificate: An asn1crypto.x509.Certificate object :return: A Certificate object """ source = certificate.dump() buffer = buffer_from_bytes(source) evp_pkey = libcrypto.d2i_X509(null(), buffer_pointer(buffer), len(source)) if is_null(evp_pkey): handle_openssl_error(0) return Certificate(evp_pkey, certificate) def load_private_key(source, password=None): """ Loads a private key into a PrivateKey object :param source: A byte string of file contents, a unicode string filename or an asn1crypto.keys.PrivateKeyInfo object :param password: A byte or unicode string to decrypt the private key file. Unicode strings will be encoded using UTF-8. Not used is the source is a PrivateKeyInfo object. :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type oscrypto.errors.AsymmetricKeyError - when the private key is incompatible with the OS crypto library OSError - when an error is returned by the OS crypto library :return: A PrivateKey object """ if isinstance(source, PrivateKeyInfo): private_object = source else: if password is not None: if isinstance(password, str_cls): password = password.encode('utf-8') if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) if isinstance(source, str_cls): with open(source, 'rb') as f: source = f.read() elif not isinstance(source, byte_cls): raise TypeError(pretty_message( ''' source must be a byte string, unicode string or asn1crypto.keys.PrivateKeyInfo object, not %s ''', type_name(source) )) private_object = parse_private(source, password) return _load_key(private_object) def load_public_key(source): """ Loads a public key into a PublicKey object :param source: A byte string of file contents, a unicode string filename or an asn1crypto.keys.PublicKeyInfo object :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type oscrypto.errors.AsymmetricKeyError - when the public key is incompatible with the OS crypto library OSError - when an error is returned by the OS crypto library :return: A PublicKey object """ if isinstance(source, PublicKeyInfo): public_key = source elif isinstance(source, byte_cls): public_key = parse_public(source) elif isinstance(source, str_cls): with open(source, 'rb') as f: public_key = parse_public(f.read()) else: raise TypeError(pretty_message( ''' source must be a byte string, unicode string or asn1crypto.keys.PublicKeyInfo object, not %s ''', type_name(source) )) if public_key.algorithm == 'dsa': if libcrypto_version_info < (1,) and public_key.hash_algo == 'sha2': raise AsymmetricKeyError(pretty_message( ''' OpenSSL 0.9.8 only supports DSA keys based on SHA1 (2048 bits or less) - this key is based on SHA2 and is %s bits ''', public_key.bit_size )) elif public_key.hash_algo is None: raise IncompleteAsymmetricKeyError(pretty_message( ''' The DSA key does not contain the necessary p, q and g parameters and can not be used ''' )) # OpenSSL 1.x suffers from issues trying to use RSASSA-PSS keys, so we # masquerade it as a normal RSA key so the OID checks work if libcrypto_version_info < (3,) and public_key.algorithm == 'rsassa_pss': temp_key = public_key.copy() temp_key['algorithm']['algorithm'] = 'rsa' data = temp_key.dump() else: data = public_key.dump() buffer = buffer_from_bytes(data) evp_pkey = libcrypto.d2i_PUBKEY(null(), buffer_pointer(buffer), len(data)) if is_null(evp_pkey): handle_openssl_error(0) return PublicKey(evp_pkey, public_key) def _load_key(private_object): """ Loads a private key into a PrivateKey object :param private_object: An asn1crypto.keys.PrivateKeyInfo object :return: A PrivateKey object """ if libcrypto_version_info < (1,) and private_object.algorithm == 'dsa' and private_object.hash_algo == 'sha2': raise AsymmetricKeyError(pretty_message( ''' OpenSSL 0.9.8 only supports DSA keys based on SHA1 (2048 bits or less) - this key is based on SHA2 and is %s bits ''', private_object.bit_size )) source = _unwrap_private_key_info(private_object).dump() buffer = buffer_from_bytes(source) evp_pkey = libcrypto.d2i_AutoPrivateKey(null(), buffer_pointer(buffer), len(source)) if is_null(evp_pkey): handle_openssl_error(0) return PrivateKey(evp_pkey, private_object) def parse_pkcs12(data, password=None): """ Parses a PKCS#12 ANS.1 DER-encoded structure and extracts certs and keys :param data: A byte string of a DER-encoded PKCS#12 file :param password: A byte string of the password to any encrypted data :raises: ValueError - when any of the parameters are of the wrong type or value OSError - when an error is returned by one of the OS decryption functions :return: A three-element tuple of: 1. An asn1crypto.keys.PrivateKeyInfo object 2. An asn1crypto.x509.Certificate object 3. A list of zero or more asn1crypto.x509.Certificate objects that are "extra" certificates, possibly intermediates from the cert chain """ return _parse_pkcs12(data, password, load_private_key) def load_pkcs12(source, password=None): """ Loads a .p12 or .pfx file into a PrivateKey object and one or more Certificates objects :param source: A byte string of file contents or a unicode string filename :param password: A byte or unicode string to decrypt the PKCS12 file. Unicode strings will be encoded using UTF-8. :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type oscrypto.errors.AsymmetricKeyError - when a contained key is incompatible with the OS crypto library OSError - when an error is returned by the OS crypto library :return: A three-element tuple containing (PrivateKey, Certificate, [Certificate, ...]) """ if password is not None: if isinstance(password, str_cls): password = password.encode('utf-8') if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) if isinstance(source, str_cls): with open(source, 'rb') as f: source = f.read() elif not isinstance(source, byte_cls): raise TypeError(pretty_message( ''' source must be a byte string or a unicode string, not %s ''', type_name(source) )) key_info, cert_info, extra_certs_info = parse_pkcs12(source, password) key = None cert = None if key_info: key = _load_key(key_info) if cert_info: cert = _load_x509(cert_info) extra_certs = [_load_x509(info) for info in extra_certs_info] return (key, cert, extra_certs) def rsa_pkcs1v15_encrypt(certificate_or_public_key, data): """ Encrypts a byte string using an RSA public key or certificate. Uses PKCS#1 v1.5 padding. :param certificate_or_public_key: A PublicKey or Certificate object :param data: A byte string, with a maximum length 11 bytes less than the key length (in bytes) :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the encrypted data """ return _encrypt(certificate_or_public_key, data, LibcryptoConst.RSA_PKCS1_PADDING) def rsa_pkcs1v15_decrypt(private_key, ciphertext): """ Decrypts a byte string using an RSA private key. Uses PKCS#1 v1.5 padding. :param private_key: A PrivateKey object :param ciphertext: A byte string of the encrypted data :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the original plaintext """ return _decrypt(private_key, ciphertext, LibcryptoConst.RSA_PKCS1_PADDING) def rsa_oaep_encrypt(certificate_or_public_key, data): """ Encrypts a byte string using an RSA public key or certificate. Uses PKCS#1 OAEP padding with SHA1. :param certificate_or_public_key: A PublicKey or Certificate object :param data: A byte string, with a maximum length 41 bytes (or more) less than the key length (in bytes) :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the encrypted data """ return _encrypt(certificate_or_public_key, data, LibcryptoConst.RSA_PKCS1_OAEP_PADDING) def rsa_oaep_decrypt(private_key, ciphertext): """ Decrypts a byte string using an RSA private key. Uses PKCS#1 OAEP padding with SHA1. :param private_key: A PrivateKey object :param ciphertext: A byte string of the encrypted data :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the original plaintext """ return _decrypt(private_key, ciphertext, LibcryptoConst.RSA_PKCS1_OAEP_PADDING) def _evp_pkey_get_size(evp_pkey): """ Handles the function name change from OpenSSL 1.1 -> 3.0 :param evp_pkey: The EVP_PKEY of the Certificte or PublicKey to get the size of :return: An int of the number of bytes necessary for the key """ if libcrypto_version_info < (3, ): return libcrypto.EVP_PKEY_size(evp_pkey) return libcrypto.EVP_PKEY_get_size(evp_pkey) def _encrypt(certificate_or_public_key, data, padding): """ Encrypts plaintext using an RSA public key or certificate :param certificate_or_public_key: A PublicKey, Certificate or PrivateKey object :param data: The byte string to encrypt :param padding: The padding mode to use :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the encrypted data """ if not isinstance(certificate_or_public_key, (Certificate, PublicKey)): raise TypeError(pretty_message( ''' certificate_or_public_key must be an instance of the Certificate or PublicKey class, not %s ''', type_name(certificate_or_public_key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) rsa = None try: buffer_size = _evp_pkey_get_size(certificate_or_public_key.evp_pkey) buffer = buffer_from_bytes(buffer_size) rsa = libcrypto.EVP_PKEY_get1_RSA(certificate_or_public_key.evp_pkey) res = libcrypto.RSA_public_encrypt(len(data), data, buffer, rsa, padding) handle_openssl_error(res) return bytes_from_buffer(buffer, res) finally: if rsa: libcrypto.RSA_free(rsa) def _decrypt(private_key, ciphertext, padding): """ Decrypts RSA ciphertext using a private key :param private_key: A PrivateKey object :param ciphertext: The ciphertext - a byte string :param padding: The padding mode to use :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if not isinstance(private_key, PrivateKey): raise TypeError(pretty_message( ''' private_key must be an instance of the PrivateKey class, not %s ''', type_name(private_key) )) if not isinstance(ciphertext, byte_cls): raise TypeError(pretty_message( ''' ciphertext must be a byte string, not %s ''', type_name(ciphertext) )) rsa = None try: buffer_size = _evp_pkey_get_size(private_key.evp_pkey) buffer = buffer_from_bytes(buffer_size) rsa = libcrypto.EVP_PKEY_get1_RSA(private_key.evp_pkey) res = libcrypto.RSA_private_decrypt(len(ciphertext), ciphertext, buffer, rsa, padding) handle_openssl_error(res) return bytes_from_buffer(buffer, res) finally: if rsa: libcrypto.RSA_free(rsa) def rsa_pkcs1v15_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies an RSASSA-PKCS-v1.5 signature. When the hash_algorithm is "raw", the operation is identical to RSA public key decryption. That is: the data is not hashed and no ASN.1 structure with an algorithm identifier of the hash algorithm is placed in the encrypted byte string. :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384", "sha512" or "raw" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if certificate_or_public_key.algorithm != 'rsa': raise ValueError(pretty_message( ''' The key specified is not an RSA public key, but %s ''', certificate_or_public_key.algorithm.upper() )) return _verify(certificate_or_public_key, signature, data, hash_algorithm) def rsa_pss_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies an RSASSA-PSS signature. For the PSS padding the mask gen algorithm will be mgf1 using the same hash algorithm as the signature. The salt length with be the length of the hash algorithm, and the trailer field with be the standard 0xBC byte. :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ cp_alg = certificate_or_public_key.algorithm if cp_alg != 'rsa' and cp_alg != 'rsassa_pss': raise ValueError(pretty_message( ''' The key specified is not an RSA public key, but %s ''', certificate_or_public_key.algorithm.upper() )) return _verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding=True) def dsa_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies a DSA signature :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if certificate_or_public_key.algorithm != 'dsa': raise ValueError(pretty_message( ''' The key specified is not a DSA public key, but %s ''', certificate_or_public_key.algorithm.upper() )) return _verify(certificate_or_public_key, signature, data, hash_algorithm) def ecdsa_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies an ECDSA signature :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if certificate_or_public_key.algorithm != 'ec': raise ValueError(pretty_message( ''' The key specified is not an EC public key, but %s ''', certificate_or_public_key.algorithm.upper() )) return _verify(certificate_or_public_key, signature, data, hash_algorithm) def _verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding=False): """ Verifies an RSA, DSA or ECDSA signature :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :param rsa_pss_padding: If the certificate_or_public_key is an RSA key, this enables PSS padding :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if not isinstance(certificate_or_public_key, (Certificate, PublicKey)): raise TypeError(pretty_message( ''' certificate_or_public_key must be an instance of the Certificate or PublicKey class, not %s ''', type_name(certificate_or_public_key) )) if not isinstance(signature, byte_cls): raise TypeError(pretty_message( ''' signature must be a byte string, not %s ''', type_name(signature) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) cp_alg = certificate_or_public_key.algorithm cp_is_rsa = cp_alg == 'rsa' or cp_alg == 'rsassa_pss' valid_hash_algorithms = set(['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512']) if cp_is_rsa and not rsa_pss_padding: valid_hash_algorithms |= set(['raw']) if hash_algorithm not in valid_hash_algorithms: valid_hash_algorithms_error = '"md5", "sha1", "sha224", "sha256", "sha384", "sha512"' if cp_is_rsa and not rsa_pss_padding: valid_hash_algorithms_error += ', "raw"' raise ValueError(pretty_message( ''' hash_algorithm must be one of %s, not %s ''', valid_hash_algorithms_error, repr(hash_algorithm) )) if not cp_is_rsa and rsa_pss_padding: raise ValueError(pretty_message( ''' PSS padding can only be used with RSA keys - the key provided is a %s key ''', cp_alg.upper() )) if cp_is_rsa and hash_algorithm == 'raw': if len(data) > certificate_or_public_key.byte_size - 11: raise ValueError(pretty_message( ''' data must be 11 bytes shorter than the key size when hash_algorithm is "raw" - key size is %s bytes, but data is %s bytes long ''', certificate_or_public_key.byte_size, len(data) )) rsa = None try: rsa = libcrypto.EVP_PKEY_get1_RSA(certificate_or_public_key.evp_pkey) if is_null(rsa): handle_openssl_error(0) buffer_size = _evp_pkey_get_size(certificate_or_public_key.evp_pkey) decrypted_buffer = buffer_from_bytes(buffer_size) decrypted_length = libcrypto.RSA_public_decrypt( len(signature), signature, decrypted_buffer, rsa, LibcryptoConst.RSA_PKCS1_PADDING ) handle_openssl_error(decrypted_length) decrypted_bytes = bytes_from_buffer(decrypted_buffer, decrypted_length) if not constant_compare(data, decrypted_bytes): raise SignatureError('Signature is invalid') return finally: if rsa: libcrypto.RSA_free(rsa) evp_md_ctx = None rsa = None dsa = None dsa_sig = None ec_key = None ecdsa_sig = None try: if libcrypto_version_info < (1, 1): evp_md_ctx = libcrypto.EVP_MD_CTX_create() else: evp_md_ctx = libcrypto.EVP_MD_CTX_new() evp_md = { 'md5': libcrypto.EVP_md5, 'sha1': libcrypto.EVP_sha1, 'sha224': libcrypto.EVP_sha224, 'sha256': libcrypto.EVP_sha256, 'sha384': libcrypto.EVP_sha384, 'sha512': libcrypto.EVP_sha512 }[hash_algorithm]() if libcrypto_version_info < (1,): if cp_is_rsa and rsa_pss_padding: digest = getattr(hashlib, hash_algorithm)(data).digest() rsa = libcrypto.EVP_PKEY_get1_RSA(certificate_or_public_key.evp_pkey) if is_null(rsa): handle_openssl_error(0) buffer_size = _evp_pkey_get_size(certificate_or_public_key.evp_pkey) decoded_buffer = buffer_from_bytes(buffer_size) decoded_length = libcrypto.RSA_public_decrypt( len(signature), signature, decoded_buffer, rsa, LibcryptoConst.RSA_NO_PADDING ) handle_openssl_error(decoded_length) res = libcrypto.RSA_verify_PKCS1_PSS( rsa, digest, evp_md, decoded_buffer, LibcryptoConst.EVP_MD_CTX_FLAG_PSS_MDLEN ) elif cp_is_rsa: res = libcrypto.EVP_DigestInit_ex(evp_md_ctx, evp_md, null()) handle_openssl_error(res) res = libcrypto.EVP_DigestUpdate(evp_md_ctx, data, len(data)) handle_openssl_error(res) res = libcrypto.EVP_VerifyFinal( evp_md_ctx, signature, len(signature), certificate_or_public_key.evp_pkey ) elif cp_alg == 'dsa': digest = getattr(hashlib, hash_algorithm)(data).digest() signature_buffer = buffer_from_bytes(signature) signature_pointer = buffer_pointer(signature_buffer) dsa_sig = libcrypto.d2i_DSA_SIG(null(), signature_pointer, len(signature)) if is_null(dsa_sig): raise SignatureError('Signature is invalid') dsa = libcrypto.EVP_PKEY_get1_DSA(certificate_or_public_key.evp_pkey) if is_null(dsa): handle_openssl_error(0) res = libcrypto.DSA_do_verify(digest, len(digest), dsa_sig, dsa) elif cp_alg == 'ec': digest = getattr(hashlib, hash_algorithm)(data).digest() signature_buffer = buffer_from_bytes(signature) signature_pointer = buffer_pointer(signature_buffer) ecdsa_sig = libcrypto.d2i_ECDSA_SIG(null(), signature_pointer, len(signature)) if is_null(ecdsa_sig): raise SignatureError('Signature is invalid') ec_key = libcrypto.EVP_PKEY_get1_EC_KEY(certificate_or_public_key.evp_pkey) if is_null(ec_key): handle_openssl_error(0) res = libcrypto.ECDSA_do_verify(digest, len(digest), ecdsa_sig, ec_key) else: evp_pkey_ctx_pointer_pointer = new(libcrypto, 'EVP_PKEY_CTX **') res = libcrypto.EVP_DigestVerifyInit( evp_md_ctx, evp_pkey_ctx_pointer_pointer, evp_md, null(), certificate_or_public_key.evp_pkey ) handle_openssl_error(res) evp_pkey_ctx_pointer = unwrap(evp_pkey_ctx_pointer_pointer) if rsa_pss_padding: # Enable PSS padding res = libcrypto.EVP_PKEY_CTX_ctrl( evp_pkey_ctx_pointer, LibcryptoConst.EVP_PKEY_RSA, -1, # All operations LibcryptoConst.EVP_PKEY_CTRL_RSA_PADDING, LibcryptoConst.RSA_PKCS1_PSS_PADDING, null() ) handle_openssl_error(res) # Use the hash algorithm output length as the salt length if libcrypto_version_info < (3, 0): res = libcrypto.EVP_PKEY_CTX_ctrl( evp_pkey_ctx_pointer, LibcryptoConst.EVP_PKEY_RSA, LibcryptoConst.EVP_PKEY_OP_SIGN | LibcryptoConst.EVP_PKEY_OP_VERIFY, LibcryptoConst.EVP_PKEY_CTRL_RSA_PSS_SALTLEN, -1, null() ) handle_openssl_error(res) res = libcrypto.EVP_DigestUpdate(evp_md_ctx, data, len(data)) handle_openssl_error(res) res = libcrypto.EVP_DigestVerifyFinal(evp_md_ctx, signature, len(signature)) if res < 1: raise SignatureError('Signature is invalid') handle_openssl_error(res) finally: if evp_md_ctx: if libcrypto_version_info < (1, 1): libcrypto.EVP_MD_CTX_destroy(evp_md_ctx) else: libcrypto.EVP_MD_CTX_free(evp_md_ctx) if rsa: libcrypto.RSA_free(rsa) if dsa: libcrypto.DSA_free(dsa) if dsa_sig: libcrypto.DSA_SIG_free(dsa_sig) if ec_key: libcrypto.EC_KEY_free(ec_key) if ecdsa_sig: libcrypto.ECDSA_SIG_free(ecdsa_sig) def rsa_pkcs1v15_sign(private_key, data, hash_algorithm): """ Generates an RSASSA-PKCS-v1.5 signature. When the hash_algorithm is "raw", the operation is identical to RSA private key encryption. That is: the data is not hashed and no ASN.1 structure with an algorithm identifier of the hash algorithm is placed in the encrypted byte string. :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384", "sha512" or "raw" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if private_key.algorithm != 'rsa': raise ValueError(pretty_message( ''' The key specified is not an RSA private key, but %s ''', private_key.algorithm.upper() )) return _sign(private_key, data, hash_algorithm) def rsa_pss_sign(private_key, data, hash_algorithm): """ Generates an RSASSA-PSS signature. For the PSS padding the mask gen algorithm will be mgf1 using the same hash algorithm as the signature. The salt length with be the length of the hash algorithm, and the trailer field with be the standard 0xBC byte. :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ pkey_alg = private_key.algorithm if pkey_alg != 'rsa' and pkey_alg != 'rsassa_pss': raise ValueError(pretty_message( ''' The key specified is not an RSA private key, but %s ''', pkey_alg.upper() )) return _sign(private_key, data, hash_algorithm, rsa_pss_padding=True) def dsa_sign(private_key, data, hash_algorithm): """ Generates a DSA signature :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if private_key.algorithm != 'dsa': raise ValueError(pretty_message( ''' The key specified is not a DSA private key, but %s ''', private_key.algorithm.upper() )) return _sign(private_key, data, hash_algorithm) def ecdsa_sign(private_key, data, hash_algorithm): """ Generates an ECDSA signature :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if private_key.algorithm != 'ec': raise ValueError(pretty_message( ''' The key specified is not an EC private key, but %s ''', private_key.algorithm.upper() )) return _sign(private_key, data, hash_algorithm) def _sign(private_key, data, hash_algorithm, rsa_pss_padding=False): """ Generates an RSA, DSA or ECDSA signature :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha224", "sha256", "sha384" or "sha512" :param rsa_pss_padding: If the private_key is an RSA key, this enables PSS padding :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if not isinstance(private_key, PrivateKey): raise TypeError(pretty_message( ''' private_key must be an instance of PrivateKey, not %s ''', type_name(private_key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) pkey_alg = private_key.algorithm pkey_is_rsa = pkey_alg == 'rsa' or pkey_alg == 'rsassa_pss' valid_hash_algorithms = set(['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512']) if pkey_alg == 'rsa' and not rsa_pss_padding: valid_hash_algorithms |= set(['raw']) if hash_algorithm not in valid_hash_algorithms: valid_hash_algorithms_error = '"md5", "sha1", "sha224", "sha256", "sha384", "sha512"' if pkey_is_rsa and not rsa_pss_padding: valid_hash_algorithms_error += ', "raw"' raise ValueError(pretty_message( ''' hash_algorithm must be one of %s, not %s ''', valid_hash_algorithms_error, repr(hash_algorithm) )) if not pkey_is_rsa and rsa_pss_padding: raise ValueError(pretty_message( ''' PSS padding can only be used with RSA keys - the key provided is a %s key ''', pkey_alg.upper() )) if pkey_is_rsa and hash_algorithm == 'raw': if len(data) > private_key.byte_size - 11: raise ValueError(pretty_message( ''' data must be 11 bytes shorter than the key size when hash_algorithm is "raw" - key size is %s bytes, but data is %s bytes long ''', private_key.byte_size, len(data) )) rsa = None try: rsa = libcrypto.EVP_PKEY_get1_RSA(private_key.evp_pkey) if is_null(rsa): handle_openssl_error(0) buffer_size = _evp_pkey_get_size(private_key.evp_pkey) signature_buffer = buffer_from_bytes(buffer_size) signature_length = libcrypto.RSA_private_encrypt( len(data), data, signature_buffer, rsa, LibcryptoConst.RSA_PKCS1_PADDING ) handle_openssl_error(signature_length) return bytes_from_buffer(signature_buffer, signature_length) finally: if rsa: libcrypto.RSA_free(rsa) evp_md_ctx = None rsa = None dsa = None dsa_sig = None ec_key = None ecdsa_sig = None try: if libcrypto_version_info < (1, 1): evp_md_ctx = libcrypto.EVP_MD_CTX_create() else: evp_md_ctx = libcrypto.EVP_MD_CTX_new() evp_md = { 'md5': libcrypto.EVP_md5, 'sha1': libcrypto.EVP_sha1, 'sha224': libcrypto.EVP_sha224, 'sha256': libcrypto.EVP_sha256, 'sha384': libcrypto.EVP_sha384, 'sha512': libcrypto.EVP_sha512 }[hash_algorithm]() if libcrypto_version_info < (1,): if pkey_is_rsa and rsa_pss_padding: digest = getattr(hashlib, hash_algorithm)(data).digest() rsa = libcrypto.EVP_PKEY_get1_RSA(private_key.evp_pkey) if is_null(rsa): handle_openssl_error(0) buffer_size = _evp_pkey_get_size(private_key.evp_pkey) em_buffer = buffer_from_bytes(buffer_size) res = libcrypto.RSA_padding_add_PKCS1_PSS( rsa, em_buffer, digest, evp_md, LibcryptoConst.EVP_MD_CTX_FLAG_PSS_MDLEN ) handle_openssl_error(res) signature_buffer = buffer_from_bytes(buffer_size) signature_length = libcrypto.RSA_private_encrypt( buffer_size, em_buffer, signature_buffer, rsa, LibcryptoConst.RSA_NO_PADDING ) handle_openssl_error(signature_length) elif pkey_is_rsa: buffer_size = _evp_pkey_get_size(private_key.evp_pkey) signature_buffer = buffer_from_bytes(buffer_size) signature_length = new(libcrypto, 'unsigned int *') res = libcrypto.EVP_DigestInit_ex(evp_md_ctx, evp_md, null()) handle_openssl_error(res) res = libcrypto.EVP_DigestUpdate(evp_md_ctx, data, len(data)) handle_openssl_error(res) res = libcrypto.EVP_SignFinal( evp_md_ctx, signature_buffer, signature_length, private_key.evp_pkey ) handle_openssl_error(res) signature_length = deref(signature_length) elif pkey_alg == 'dsa': digest = getattr(hashlib, hash_algorithm)(data).digest() dsa = libcrypto.EVP_PKEY_get1_DSA(private_key.evp_pkey) if is_null(dsa): handle_openssl_error(0) dsa_sig = libcrypto.DSA_do_sign(digest, len(digest), dsa) if is_null(dsa_sig): handle_openssl_error(0) buffer_size = libcrypto.i2d_DSA_SIG(dsa_sig, null()) signature_buffer = buffer_from_bytes(buffer_size) signature_pointer = buffer_pointer(signature_buffer) signature_length = libcrypto.i2d_DSA_SIG(dsa_sig, signature_pointer) handle_openssl_error(signature_length) elif pkey_alg == 'ec': digest = getattr(hashlib, hash_algorithm)(data).digest() ec_key = libcrypto.EVP_PKEY_get1_EC_KEY(private_key.evp_pkey) if is_null(ec_key): handle_openssl_error(0) ecdsa_sig = libcrypto.ECDSA_do_sign(digest, len(digest), ec_key) if is_null(ecdsa_sig): handle_openssl_error(0) buffer_size = libcrypto.i2d_ECDSA_SIG(ecdsa_sig, null()) signature_buffer = buffer_from_bytes(buffer_size) signature_pointer = buffer_pointer(signature_buffer) signature_length = libcrypto.i2d_ECDSA_SIG(ecdsa_sig, signature_pointer) handle_openssl_error(signature_length) else: buffer_size = _evp_pkey_get_size(private_key.evp_pkey) signature_buffer = buffer_from_bytes(buffer_size) signature_length = new(libcrypto, 'size_t *', buffer_size) evp_pkey_ctx_pointer_pointer = new(libcrypto, 'EVP_PKEY_CTX **') res = libcrypto.EVP_DigestSignInit( evp_md_ctx, evp_pkey_ctx_pointer_pointer, evp_md, null(), private_key.evp_pkey ) handle_openssl_error(res) evp_pkey_ctx_pointer = unwrap(evp_pkey_ctx_pointer_pointer) if rsa_pss_padding: # Enable PSS padding res = libcrypto.EVP_PKEY_CTX_ctrl( evp_pkey_ctx_pointer, LibcryptoConst.EVP_PKEY_RSA, -1, # All operations LibcryptoConst.EVP_PKEY_CTRL_RSA_PADDING, LibcryptoConst.RSA_PKCS1_PSS_PADDING, null() ) handle_openssl_error(res) # Use the hash algorithm output length as the salt length if libcrypto_version_info < (3, 0): res = libcrypto.EVP_PKEY_CTX_ctrl( evp_pkey_ctx_pointer, LibcryptoConst.EVP_PKEY_RSA, LibcryptoConst.EVP_PKEY_OP_SIGN | LibcryptoConst.EVP_PKEY_OP_VERIFY, LibcryptoConst.EVP_PKEY_CTRL_RSA_PSS_SALTLEN, -1, null() ) handle_openssl_error(res) res = libcrypto.EVP_DigestUpdate(evp_md_ctx, data, len(data)) handle_openssl_error(res) res = libcrypto.EVP_DigestSignFinal(evp_md_ctx, signature_buffer, signature_length) handle_openssl_error(res) signature_length = deref(signature_length) return bytes_from_buffer(signature_buffer, signature_length) finally: if evp_md_ctx: if libcrypto_version_info < (1, 1): libcrypto.EVP_MD_CTX_destroy(evp_md_ctx) else: libcrypto.EVP_MD_CTX_free(evp_md_ctx) if rsa: libcrypto.RSA_free(rsa) if dsa: libcrypto.DSA_free(dsa) if dsa_sig: libcrypto.DSA_SIG_free(dsa_sig) if ec_key: libcrypto.EC_KEY_free(ec_key) if ecdsa_sig: libcrypto.ECDSA_SIG_free(ecdsa_sig) oscrypto-1.3.0/oscrypto/_openssl/symmetric.py000066400000000000000000000556301421476274700215160ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import math from .._errors import pretty_message from .._ffi import new, null, is_null, buffer_from_bytes, bytes_from_buffer, deref from ._libcrypto import libcrypto, libcrypto_legacy_support, LibcryptoConst, handle_openssl_error from ..util import rand_bytes from .._types import type_name, byte_cls __all__ = [ 'aes_cbc_no_padding_decrypt', 'aes_cbc_no_padding_encrypt', 'aes_cbc_pkcs7_decrypt', 'aes_cbc_pkcs7_encrypt', 'des_cbc_pkcs5_decrypt', 'des_cbc_pkcs5_encrypt', 'rc2_cbc_pkcs5_decrypt', 'rc2_cbc_pkcs5_encrypt', 'rc4_decrypt', 'rc4_encrypt', 'tripledes_cbc_pkcs5_decrypt', 'tripledes_cbc_pkcs5_encrypt', ] def aes_cbc_no_padding_encrypt(key, data, iv): """ Encrypts plaintext using AES in CBC mode with a 128, 192 or 256 bit key and no padding. This means the ciphertext must be an exact multiple of 16 bytes long. :param key: The encryption key - a byte string either 16, 24 or 32 bytes long :param data: The plaintext - a byte string :param iv: The initialization vector - either a byte string 16-bytes long or None to generate an IV :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A tuple of two byte strings (iv, ciphertext) """ cipher = _calculate_aes_cipher(key) if not iv: iv = rand_bytes(16) elif len(iv) != 16: raise ValueError(pretty_message( ''' iv must be 16 bytes long - is %s ''', len(iv) )) if len(data) % 16 != 0: raise ValueError(pretty_message( ''' data must be a multiple of 16 bytes long - is %s ''', len(data) )) return (iv, _encrypt(cipher, key, data, iv, False)) def aes_cbc_no_padding_decrypt(key, data, iv): """ Decrypts AES ciphertext in CBC mode using a 128, 192 or 256 bit key and no padding. :param key: The encryption key - a byte string either 16, 24 or 32 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string 16-bytes long :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A byte string of the plaintext """ cipher = _calculate_aes_cipher(key) if len(iv) != 16: raise ValueError(pretty_message( ''' iv must be 16 bytes long - is %s ''', len(iv) )) return _decrypt(cipher, key, data, iv, False) def aes_cbc_pkcs7_encrypt(key, data, iv): """ Encrypts plaintext using AES in CBC mode with a 128, 192 or 256 bit key and PKCS#7 padding. :param key: The encryption key - a byte string either 16, 24 or 32 bytes long :param data: The plaintext - a byte string :param iv: The initialization vector - either a byte string 16-bytes long or None to generate an IV :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A tuple of two byte strings (iv, ciphertext) """ cipher = _calculate_aes_cipher(key) if not iv: iv = rand_bytes(16) elif len(iv) != 16: raise ValueError(pretty_message( ''' iv must be 16 bytes long - is %s ''', len(iv) )) return (iv, _encrypt(cipher, key, data, iv, True)) def aes_cbc_pkcs7_decrypt(key, data, iv): """ Decrypts AES ciphertext in CBC mode using a 128, 192 or 256 bit key :param key: The encryption key - a byte string either 16, 24 or 32 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string 16-bytes long :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A byte string of the plaintext """ cipher = _calculate_aes_cipher(key) if len(iv) != 16: raise ValueError(pretty_message( ''' iv must be 16 bytes long - is %s ''', len(iv) )) return _decrypt(cipher, key, data, iv, True) def _calculate_aes_cipher(key): """ Determines if the key is a valid AES 128, 192 or 256 key :param key: A byte string of the key to use :raises: ValueError - when an invalid key is provided :return: A unicode string of the AES variation - "aes128", "aes192" or "aes256" """ if len(key) not in [16, 24, 32]: raise ValueError(pretty_message( ''' key must be either 16, 24 or 32 bytes (128, 192 or 256 bits) long - is %s ''', len(key) )) if len(key) == 16: cipher = 'aes128' elif len(key) == 24: cipher = 'aes192' elif len(key) == 32: cipher = 'aes256' return cipher def rc4_encrypt(key, data): """ Encrypts plaintext using RC4 with a 40-128 bit key :param key: The encryption key - a byte string 5-16 bytes long :param data: The plaintext - a byte string :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A byte string of the ciphertext """ if not libcrypto_legacy_support: raise EnvironmentError('OpenSSL has been compiled without RC4 support') if len(key) < 5 or len(key) > 16: raise ValueError(pretty_message( ''' key must be 5 to 16 bytes (40 to 128 bits) long - is %s ''', len(key) )) return _encrypt('rc4', key, data, None, None) def rc4_decrypt(key, data): """ Decrypts RC4 ciphertext using a 40-128 bit key :param key: The encryption key - a byte string 5-16 bytes long :param data: The ciphertext - a byte string :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A byte string of the plaintext """ if not libcrypto_legacy_support: raise EnvironmentError('OpenSSL has been compiled without RC4 support') if len(key) < 5 or len(key) > 16: raise ValueError(pretty_message( ''' key must be 5 to 16 bytes (40 to 128 bits) long - is %s ''', len(key) )) return _decrypt('rc4', key, data, None, None) def rc2_cbc_pkcs5_encrypt(key, data, iv): """ Encrypts plaintext using RC2 in CBC mode with a 40-128 bit key and PKCS#5 padding. :param key: The encryption key - a byte string 8 bytes long :param data: The plaintext - a byte string :param iv: The initialization vector - a byte string 8-bytes long or None to generate an IV :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A tuple of two byte strings (iv, ciphertext) """ if not libcrypto_legacy_support: raise EnvironmentError('OpenSSL has been compiled without RC2 support') if len(key) < 5 or len(key) > 16: raise ValueError(pretty_message( ''' key must be 5 to 16 bytes (40 to 128 bits) long - is %s ''', len(key) )) if not iv: iv = rand_bytes(8) elif len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) return (iv, _encrypt('rc2', key, data, iv, True)) def rc2_cbc_pkcs5_decrypt(key, data, iv): """ Decrypts RC2 ciphertext ib CBC mode using a 40-128 bit key and PKCS#5 padding. :param key: The encryption key - a byte string 8 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string 8 bytes long :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A byte string of the plaintext """ if not libcrypto_legacy_support: raise EnvironmentError('OpenSSL has been compiled without RC2 support') if len(key) < 5 or len(key) > 16: raise ValueError(pretty_message( ''' key must be 5 to 16 bytes (40 to 128 bits) long - is %s ''', len(key) )) if len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) return _decrypt('rc2', key, data, iv, True) def tripledes_cbc_pkcs5_encrypt(key, data, iv): """ Encrypts plaintext using 3DES in CBC mode using either the 2 or 3 key variant (16 or 24 byte long key) and PKCS#5 padding. :param key: The encryption key - a byte string 16 or 24 bytes long (2 or 3 key mode) :param data: The plaintext - a byte string :param iv: The initialization vector - a byte string 8-bytes long or None to generate an IV :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A tuple of two byte strings (iv, ciphertext) """ if len(key) != 16 and len(key) != 24: raise ValueError(pretty_message( ''' key must be 16 bytes (2 key) or 24 bytes (3 key) long - %s ''', len(key) )) if not iv: iv = rand_bytes(8) elif len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - %s ''', len(iv) )) cipher = 'tripledes_3key' # Expand 2-key to actual 24 byte byte string used by cipher if len(key) == 16: key = key + key[0:8] cipher = 'tripledes_2key' return (iv, _encrypt(cipher, key, data, iv, True)) def tripledes_cbc_pkcs5_decrypt(key, data, iv): """ Decrypts 3DES ciphertext in CBC mode using either the 2 or 3 key variant (16 or 24 byte long key) and PKCS#5 padding. :param key: The encryption key - a byte string 16 or 24 bytes long (2 or 3 key mode) :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string 8-bytes long :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A byte string of the plaintext """ if len(key) != 16 and len(key) != 24: raise ValueError(pretty_message( ''' key must be 16 bytes (2 key) or 24 bytes (3 key) long - is %s ''', len(key) )) if len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) cipher = 'tripledes_3key' # Expand 2-key to actual 24 byte byte string used by cipher if len(key) == 16: key = key + key[0:8] cipher = 'tripledes_2key' return _decrypt(cipher, key, data, iv, True) def des_cbc_pkcs5_encrypt(key, data, iv): """ Encrypts plaintext using DES in CBC mode with a 56 bit key and PKCS#5 padding. :param key: The encryption key - a byte string 8 bytes long (includes error correction bits) :param data: The plaintext - a byte string :param iv: The initialization vector - a byte string 8-bytes long or None to generate an IV :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A tuple of two byte strings (iv, ciphertext) """ if not libcrypto_legacy_support: raise EnvironmentError('OpenSSL has been compiled without DES support') if len(key) != 8: raise ValueError(pretty_message( ''' key must be 8 bytes (56 bits + 8 parity bits) long - is %s ''', len(key) )) if not iv: iv = rand_bytes(8) elif len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) return (iv, _encrypt('des', key, data, iv, True)) def des_cbc_pkcs5_decrypt(key, data, iv): """ Decrypts DES ciphertext in CBC mode using a 56 bit key and PKCS#5 padding. :param key: The encryption key - a byte string 8 bytes long (includes error correction bits) :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string 8-bytes long :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A byte string of the plaintext """ if not libcrypto_legacy_support: raise EnvironmentError('OpenSSL has been compiled without DES support') if len(key) != 8: raise ValueError(pretty_message( ''' key must be 8 bytes (56 bits + 8 parity bits) long - is %s ''', len(key) )) if len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) return _decrypt('des', key, data, iv, True) def _encrypt(cipher, key, data, iv, padding): """ Encrypts plaintext :param cipher: A unicode string of "aes128", "aes192", "aes256", "des", "tripledes_2key", "tripledes_3key", "rc2", "rc4" :param key: The encryption key - a byte string 5-32 bytes long :param data: The plaintext - a byte string :param iv: The initialization vector - a byte string - unused for RC4 :param padding: Boolean, if padding should be used - unused for RC4 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A byte string of the ciphertext """ if not isinstance(key, byte_cls): raise TypeError(pretty_message( ''' key must be a byte string, not %s ''', type_name(key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if cipher != 'rc4' and not isinstance(iv, byte_cls): raise TypeError(pretty_message( ''' iv must be a byte string, not %s ''', type_name(iv) )) if cipher != 'rc4' and not padding: # AES in CBC mode can be allowed with no padding if # the data is an exact multiple of the block size is_aes = cipher in set(['aes128', 'aes192', 'aes256']) if not is_aes or (is_aes and (len(data) % 16) != 0): raise ValueError('padding must be specified') evp_cipher_ctx = None try: evp_cipher_ctx = libcrypto.EVP_CIPHER_CTX_new() if is_null(evp_cipher_ctx): handle_openssl_error(0) evp_cipher, buffer_size = _setup_evp_encrypt_decrypt(cipher, data) if iv is None: iv = null() if cipher in set(['rc2', 'rc4']): res = libcrypto.EVP_EncryptInit_ex(evp_cipher_ctx, evp_cipher, null(), null(), null()) handle_openssl_error(res) res = libcrypto.EVP_CIPHER_CTX_set_key_length(evp_cipher_ctx, len(key)) handle_openssl_error(res) if cipher == 'rc2': res = libcrypto.EVP_CIPHER_CTX_ctrl( evp_cipher_ctx, LibcryptoConst.EVP_CTRL_SET_RC2_KEY_BITS, len(key) * 8, null() ) handle_openssl_error(res) evp_cipher = null() res = libcrypto.EVP_EncryptInit_ex(evp_cipher_ctx, evp_cipher, null(), key, iv) handle_openssl_error(res) if padding is not None: res = libcrypto.EVP_CIPHER_CTX_set_padding(evp_cipher_ctx, int(padding)) handle_openssl_error(res) buffer = buffer_from_bytes(buffer_size) output_length = new(libcrypto, 'int *') res = libcrypto.EVP_EncryptUpdate(evp_cipher_ctx, buffer, output_length, data, len(data)) handle_openssl_error(res) output = bytes_from_buffer(buffer, deref(output_length)) res = libcrypto.EVP_EncryptFinal_ex(evp_cipher_ctx, buffer, output_length) handle_openssl_error(res) output += bytes_from_buffer(buffer, deref(output_length)) return output finally: if evp_cipher_ctx: libcrypto.EVP_CIPHER_CTX_free(evp_cipher_ctx) def _decrypt(cipher, key, data, iv, padding): """ Decrypts AES/RC4/RC2/3DES/DES ciphertext :param cipher: A unicode string of "aes128", "aes192", "aes256", "des", "tripledes_2key", "tripledes_3key", "rc2", "rc4" :param key: The encryption key - a byte string 5-32 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string - unused for RC4 :param padding: Boolean, if padding should be used - unused for RC4 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A byte string of the plaintext """ if not isinstance(key, byte_cls): raise TypeError(pretty_message( ''' key must be a byte string, not %s ''', type_name(key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if cipher != 'rc4' and not isinstance(iv, byte_cls): raise TypeError(pretty_message( ''' iv must be a byte string, not %s ''', type_name(iv) )) if cipher not in set(['rc4', 'aes128', 'aes192', 'aes256']) and not padding: raise ValueError('padding must be specified') evp_cipher_ctx = None try: evp_cipher_ctx = libcrypto.EVP_CIPHER_CTX_new() if is_null(evp_cipher_ctx): handle_openssl_error(0) evp_cipher, buffer_size = _setup_evp_encrypt_decrypt(cipher, data) if iv is None: iv = null() if cipher in set(['rc2', 'rc4']): res = libcrypto.EVP_DecryptInit_ex(evp_cipher_ctx, evp_cipher, null(), null(), null()) handle_openssl_error(res) res = libcrypto.EVP_CIPHER_CTX_set_key_length(evp_cipher_ctx, len(key)) handle_openssl_error(res) if cipher == 'rc2': res = libcrypto.EVP_CIPHER_CTX_ctrl( evp_cipher_ctx, LibcryptoConst.EVP_CTRL_SET_RC2_KEY_BITS, len(key) * 8, null() ) handle_openssl_error(res) evp_cipher = null() res = libcrypto.EVP_DecryptInit_ex(evp_cipher_ctx, evp_cipher, null(), key, iv) handle_openssl_error(res) if padding is not None: res = libcrypto.EVP_CIPHER_CTX_set_padding(evp_cipher_ctx, int(padding)) handle_openssl_error(res) buffer = buffer_from_bytes(buffer_size) output_length = new(libcrypto, 'int *') res = libcrypto.EVP_DecryptUpdate(evp_cipher_ctx, buffer, output_length, data, len(data)) handle_openssl_error(res) output = bytes_from_buffer(buffer, deref(output_length)) res = libcrypto.EVP_DecryptFinal_ex(evp_cipher_ctx, buffer, output_length) handle_openssl_error(res) output += bytes_from_buffer(buffer, deref(output_length)) return output finally: if evp_cipher_ctx: libcrypto.EVP_CIPHER_CTX_free(evp_cipher_ctx) def _setup_evp_encrypt_decrypt(cipher, data): """ Creates an EVP_CIPHER pointer object and determines the buffer size necessary for the parameter specified. :param evp_cipher_ctx: An EVP_CIPHER_CTX pointer :param cipher: A unicode string of "aes128", "aes192", "aes256", "des", "tripledes_2key", "tripledes_3key", "rc2", "rc4" :param key: The key byte string :param data: The plaintext or ciphertext as a byte string :param padding: If padding is to be used :return: A 2-element tuple with the first element being an EVP_CIPHER pointer and the second being an integer that is the required buffer size """ evp_cipher = { 'aes128': libcrypto.EVP_aes_128_cbc, 'aes192': libcrypto.EVP_aes_192_cbc, 'aes256': libcrypto.EVP_aes_256_cbc, 'rc2': libcrypto.EVP_rc2_cbc, 'rc4': libcrypto.EVP_rc4, 'des': libcrypto.EVP_des_cbc, 'tripledes_2key': libcrypto.EVP_des_ede_cbc, 'tripledes_3key': libcrypto.EVP_des_ede3_cbc, }[cipher]() if cipher == 'rc4': buffer_size = len(data) else: block_size = { 'aes128': 16, 'aes192': 16, 'aes256': 16, 'rc2': 8, 'des': 8, 'tripledes_2key': 8, 'tripledes_3key': 8, }[cipher] buffer_size = block_size * int(math.ceil(len(data) / block_size)) return (evp_cipher, buffer_size) oscrypto-1.3.0/oscrypto/_openssl/tls.py000066400000000000000000001266011421476274700203010ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys import re import socket as socket_ import select import numbers from ._libssl import libssl, LibsslConst from ._libcrypto import libcrypto, libcrypto_version_info, handle_openssl_error, peek_openssl_error from .. import _backend_config from .._asn1 import Certificate as Asn1Certificate from .._errors import pretty_message from .._ffi import null, bytes_from_buffer, buffer_from_bytes, is_null, buffer_pointer from .._types import type_name, str_cls, byte_cls, int_types from ..errors import TLSError, TLSDisconnectError, TLSGracefulDisconnectError from .._tls import ( detect_client_auth_request, extract_chain, get_dh_params_length, parse_session_info, raise_client_auth, raise_dh_params, raise_disconnection, raise_expired_not_yet_valid, raise_handshake, raise_hostname, raise_no_issuer, raise_protocol_error, raise_protocol_version, raise_self_signed, raise_verification, raise_weak_signature, parse_tls_records, parse_handshake_messages, ) from .asymmetric import load_certificate, Certificate from ..keys import parse_certificate from ..trust_list import get_path if sys.version_info < (3,): range = xrange # noqa if sys.version_info < (3, 7): Pattern = re._pattern_type else: Pattern = re.Pattern __all__ = [ 'TLSSession', 'TLSSocket', ] _trust_list_path = _backend_config().get('trust_list_path') _line_regex = re.compile(b'(\r\n|\r|\n)') _PROTOCOL_MAP = { 'SSLv2': LibsslConst.SSL_OP_NO_SSLv2, 'SSLv3': LibsslConst.SSL_OP_NO_SSLv3, 'TLSv1': LibsslConst.SSL_OP_NO_TLSv1, 'TLSv1.1': LibsslConst.SSL_OP_NO_TLSv1_1, 'TLSv1.2': LibsslConst.SSL_OP_NO_TLSv1_2, } def _homogenize_openssl3_error(error_tuple): """ Takes a 3-element tuple from peek_openssl_error() and modifies it to handle the changes in OpenSSL 3.0. That release removed the concept of an error function, meaning the second item in the tuple will always be 0. :param error_tuple: A 3-element tuple of integers :return: A 3-element tuple of integers """ if libcrypto_version_info < (3,): return error_tuple return (error_tuple[0], 0, error_tuple[2]) class TLSSession(object): """ A TLS session object that multiple TLSSocket objects can share for the sake of session reuse """ _protocols = None _ciphers = None _manual_validation = None _extra_trust_roots = None _ssl_ctx = None _ssl_session = None def __init__(self, protocol=None, manual_validation=False, extra_trust_roots=None): """ :param protocol: A unicode string or set of unicode strings representing allowable protocols to negotiate with the server: - "TLSv1.2" - "TLSv1.1" - "TLSv1" - "SSLv3" Default is: {"TLSv1", "TLSv1.1", "TLSv1.2"} :param manual_validation: If certificate and certificate path validation should be skipped and left to the developer to implement :param extra_trust_roots: A list containing one or more certificates to be treated as trust roots, in one of the following formats: - A byte string of the DER encoded certificate - A unicode string of the certificate filename - An asn1crypto.x509.Certificate object - An oscrypto.asymmetric.Certificate object :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if not isinstance(manual_validation, bool): raise TypeError(pretty_message( ''' manual_validation must be a boolean, not %s ''', type_name(manual_validation) )) self._manual_validation = manual_validation if protocol is None: protocol = set(['TLSv1', 'TLSv1.1', 'TLSv1.2']) if isinstance(protocol, str_cls): protocol = set([protocol]) elif not isinstance(protocol, set): raise TypeError(pretty_message( ''' protocol must be a unicode string or set of unicode strings, not %s ''', type_name(protocol) )) valid_protocols = set(['SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2']) unsupported_protocols = protocol - valid_protocols if unsupported_protocols: raise ValueError(pretty_message( ''' protocol must contain only the unicode strings "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", not %s ''', repr(unsupported_protocols) )) self._protocols = protocol self._extra_trust_roots = [] if extra_trust_roots: for extra_trust_root in extra_trust_roots: if isinstance(extra_trust_root, Certificate): extra_trust_root = extra_trust_root.asn1 elif isinstance(extra_trust_root, byte_cls): extra_trust_root = parse_certificate(extra_trust_root) elif isinstance(extra_trust_root, str_cls): with open(extra_trust_root, 'rb') as f: extra_trust_root = parse_certificate(f.read()) elif not isinstance(extra_trust_root, Asn1Certificate): raise TypeError(pretty_message( ''' extra_trust_roots must be a list of byte strings, unicode strings, asn1crypto.x509.Certificate objects or oscrypto.asymmetric.Certificate objects, not %s ''', type_name(extra_trust_root) )) self._extra_trust_roots.append(extra_trust_root) ssl_ctx = None try: if libcrypto_version_info < (1, 1): method = libssl.SSLv23_method() else: method = libssl.TLS_method() ssl_ctx = libssl.SSL_CTX_new(method) if is_null(ssl_ctx): handle_openssl_error(0) self._ssl_ctx = ssl_ctx libssl.SSL_CTX_set_timeout(ssl_ctx, 600) # Allow caching SSL sessions libssl.SSL_CTX_ctrl( ssl_ctx, LibsslConst.SSL_CTRL_SET_SESS_CACHE_MODE, LibsslConst.SSL_SESS_CACHE_CLIENT, null() ) if sys.platform in set(['win32', 'darwin']): trust_list_path = _trust_list_path if trust_list_path is None: trust_list_path = get_path() if sys.platform == 'win32': path_encoding = 'mbcs' else: path_encoding = 'utf-8' result = libssl.SSL_CTX_load_verify_locations( ssl_ctx, trust_list_path.encode(path_encoding), null() ) else: result = libssl.SSL_CTX_set_default_verify_paths(ssl_ctx) handle_openssl_error(result) verify_mode = LibsslConst.SSL_VERIFY_NONE if manual_validation else LibsslConst.SSL_VERIFY_PEER libssl.SSL_CTX_set_verify(ssl_ctx, verify_mode, null()) # Modern cipher suite list from https://wiki.mozilla.org/Security/Server_Side_TLS late August 2015 result = libssl.SSL_CTX_set_cipher_list( ssl_ctx, ( b'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:' b'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:' b'DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:' b'kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:' b'ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:' b'ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:' b'DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:' b'DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:' b'AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:' b'AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:' b'!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:' b'!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA' ) ) handle_openssl_error(result) disabled_protocols = set(['SSLv2']) disabled_protocols |= (valid_protocols - self._protocols) for disabled_protocol in disabled_protocols: libssl.SSL_CTX_ctrl( ssl_ctx, LibsslConst.SSL_CTRL_OPTIONS, _PROTOCOL_MAP[disabled_protocol], null() ) if self._extra_trust_roots: x509_store = libssl.SSL_CTX_get_cert_store(ssl_ctx) for cert in self._extra_trust_roots: oscrypto_cert = load_certificate(cert) result = libssl.X509_STORE_add_cert( x509_store, oscrypto_cert.x509 ) handle_openssl_error(result) except (Exception): if ssl_ctx: libssl.SSL_CTX_free(ssl_ctx) self._ssl_ctx = None raise def __del__(self): if self._ssl_ctx: libssl.SSL_CTX_free(self._ssl_ctx) self._ssl_ctx = None if self._ssl_session: libssl.SSL_SESSION_free(self._ssl_session) self._ssl_session = None class TLSSocket(object): """ A wrapper around a socket.socket that adds TLS """ _socket = None # An oscrypto.tls.TLSSession object _session = None # An OpenSSL SSL struct pointer _ssl = None # OpenSSL memory bios used for reading/writing data to and # from the socket _rbio = None _wbio = None # Size of _bio_write_buffer and _read_buffer _buffer_size = 8192 # A buffer used to pull bytes out of the _wbio memory bio to # be written to the socket _bio_write_buffer = None # A buffer used to push bytes into the _rbio memory bio to # be decrypted by OpenSSL _read_buffer = None # Raw ciphertext from the socker that hasn't need fed to OpenSSL yet _raw_bytes = None # Plaintext that has been decrypted, but not asked for yet _decrypted_bytes = None _hostname = None _certificate = None _intermediates = None _protocol = None _cipher_suite = None _compression = None _session_id = None _session_ticket = None # If we explicitly asked for the connection to be closed _local_closed = False _gracefully_closed = False @classmethod def wrap(cls, socket, hostname, session=None): """ Takes an existing socket and adds TLS :param socket: A socket.socket object to wrap with TLS :param hostname: A unicode string of the hostname or IP the socket is connected to :param session: An existing TLSSession object to allow for session reuse, specific protocol or manual certificate validation :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if not isinstance(socket, socket_.socket): raise TypeError(pretty_message( ''' socket must be an instance of socket.socket, not %s ''', type_name(socket) )) if not isinstance(hostname, str_cls): raise TypeError(pretty_message( ''' hostname must be a unicode string, not %s ''', type_name(hostname) )) if session is not None and not isinstance(session, TLSSession): raise TypeError(pretty_message( ''' session must be an instance of oscrypto.tls.TLSSession, not %s ''', type_name(session) )) new_socket = cls(None, None, session=session) new_socket._socket = socket new_socket._hostname = hostname new_socket._handshake() return new_socket def __init__(self, address, port, timeout=10, session=None): """ :param address: A unicode string of the domain name or IP address to connect to :param port: An integer of the port number to connect to :param timeout: An integer timeout to use for the socket :param session: An oscrypto.tls.TLSSession object to allow for session reuse and controlling the protocols and validation performed """ self._raw_bytes = b'' self._decrypted_bytes = b'' if address is None and port is None: self._socket = None else: if not isinstance(address, str_cls): raise TypeError(pretty_message( ''' address must be a unicode string, not %s ''', type_name(address) )) if not isinstance(port, int_types): raise TypeError(pretty_message( ''' port must be an integer, not %s ''', type_name(port) )) if timeout is not None and not isinstance(timeout, numbers.Number): raise TypeError(pretty_message( ''' timeout must be a number, not %s ''', type_name(timeout) )) self._socket = socket_.create_connection((address, port), timeout) self._socket.settimeout(timeout) if session is None: session = TLSSession() elif not isinstance(session, TLSSession): raise TypeError(pretty_message( ''' session must be an instance of oscrypto.tls.TLSSession, not %s ''', type_name(session) )) self._session = session if self._socket: self._hostname = address self._handshake() def _handshake(self): """ Perform an initial TLS handshake """ self._ssl = None self._rbio = None self._wbio = None try: self._ssl = libssl.SSL_new(self._session._ssl_ctx) if is_null(self._ssl): self._ssl = None handle_openssl_error(0) mem_bio = libssl.BIO_s_mem() self._rbio = libssl.BIO_new(mem_bio) if is_null(self._rbio): handle_openssl_error(0) self._wbio = libssl.BIO_new(mem_bio) if is_null(self._wbio): handle_openssl_error(0) libssl.SSL_set_bio(self._ssl, self._rbio, self._wbio) utf8_domain = self._hostname.encode('utf-8') libssl.SSL_ctrl( self._ssl, LibsslConst.SSL_CTRL_SET_TLSEXT_HOSTNAME, LibsslConst.TLSEXT_NAMETYPE_host_name, utf8_domain ) libssl.SSL_set_connect_state(self._ssl) if self._session._ssl_session: libssl.SSL_set_session(self._ssl, self._session._ssl_session) self._bio_write_buffer = buffer_from_bytes(self._buffer_size) self._read_buffer = buffer_from_bytes(self._buffer_size) handshake_server_bytes = b'' handshake_client_bytes = b'' while True: result = libssl.SSL_do_handshake(self._ssl) handshake_client_bytes += self._raw_write() if result == 1: break error = libssl.SSL_get_error(self._ssl, result) if error == LibsslConst.SSL_ERROR_WANT_READ: chunk = self._raw_read() if chunk == b'': if handshake_server_bytes == b'': raise_disconnection() if detect_client_auth_request(handshake_server_bytes): raise_client_auth() raise_protocol_error(handshake_server_bytes) handshake_server_bytes += chunk elif error == LibsslConst.SSL_ERROR_WANT_WRITE: handshake_client_bytes += self._raw_write() elif error == LibsslConst.SSL_ERROR_ZERO_RETURN: self._gracefully_closed = True self._shutdown(False) self._raise_closed() else: info = peek_openssl_error() dh_key_info_1 = ( LibsslConst.ERR_LIB_SSL, LibsslConst.SSL_F_SSL3_CHECK_CERT_AND_ALGORITHM, LibsslConst.SSL_R_DH_KEY_TOO_SMALL ) dh_key_info_1 = _homogenize_openssl3_error(dh_key_info_1) dh_key_info_2 = ( LibsslConst.ERR_LIB_SSL, LibsslConst.SSL_F_TLS_PROCESS_SKE_DHE, LibsslConst.SSL_R_DH_KEY_TOO_SMALL ) dh_key_info_2 = _homogenize_openssl3_error(dh_key_info_2) dh_key_info_3 = ( LibsslConst.ERR_LIB_SSL, LibsslConst.SSL_F_SSL3_GET_KEY_EXCHANGE, LibsslConst.SSL_R_BAD_DH_P_LENGTH ) dh_key_info_3 = _homogenize_openssl3_error(dh_key_info_3) if info == dh_key_info_1 or info == dh_key_info_2 or info == dh_key_info_3: raise_dh_params() if libcrypto_version_info < (1, 1): unknown_protocol_info = ( LibsslConst.ERR_LIB_SSL, LibsslConst.SSL_F_SSL23_GET_SERVER_HELLO, LibsslConst.SSL_R_UNKNOWN_PROTOCOL ) else: unknown_protocol_info = ( LibsslConst.ERR_LIB_SSL, LibsslConst.SSL_F_SSL3_GET_RECORD, LibsslConst.SSL_R_WRONG_VERSION_NUMBER ) unknown_protocol_info = _homogenize_openssl3_error(unknown_protocol_info) if info == unknown_protocol_info: raise_protocol_error(handshake_server_bytes) tls_version_info_error = ( LibsslConst.ERR_LIB_SSL, LibsslConst.SSL_F_SSL23_GET_SERVER_HELLO, LibsslConst.SSL_R_TLSV1_ALERT_PROTOCOL_VERSION ) tls_version_info_error = _homogenize_openssl3_error(tls_version_info_error) if info == tls_version_info_error: raise_protocol_version() handshake_error_info = ( LibsslConst.ERR_LIB_SSL, LibsslConst.SSL_F_SSL23_GET_SERVER_HELLO, LibsslConst.SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE ) # OpenSSL 3.0 no longer has func codes, so this can be confused # with the following handler which needs to check for client auth if libcrypto_version_info < (3, ) and info == handshake_error_info: raise_handshake() handshake_failure_info = ( LibsslConst.ERR_LIB_SSL, LibsslConst.SSL_F_SSL3_READ_BYTES, LibsslConst.SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE ) handshake_failure_info = _homogenize_openssl3_error(handshake_failure_info) if info == handshake_failure_info: saw_client_auth = False for record_type, _, record_data in parse_tls_records(handshake_server_bytes): if record_type != b'\x16': continue for message_type, message_data in parse_handshake_messages(record_data): if message_type == b'\x0d': saw_client_auth = True break if saw_client_auth: raise_client_auth() raise_handshake() if libcrypto_version_info < (1, 1): cert_verify_failed_info = ( LibsslConst.ERR_LIB_SSL, LibsslConst.SSL_F_SSL3_GET_SERVER_CERTIFICATE, LibsslConst.SSL_R_CERTIFICATE_VERIFY_FAILED ) else: cert_verify_failed_info = ( LibsslConst.ERR_LIB_SSL, LibsslConst.SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, LibsslConst.SSL_R_CERTIFICATE_VERIFY_FAILED ) cert_verify_failed_info = _homogenize_openssl3_error(cert_verify_failed_info) # It would appear that some versions of OpenSSL (such as on Fedora 30) # don't even have the MD5 digest algorithm included any longer? To # give a more useful error message we handle this specifically. unknown_hash_algo_info = ( LibsslConst.ERR_LIB_ASN1, LibsslConst.ASN1_F_ASN1_ITEM_VERIFY, LibsslConst.ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM ) unknown_hash_algo_info = _homogenize_openssl3_error(unknown_hash_algo_info) if info == unknown_hash_algo_info: chain = extract_chain(handshake_server_bytes) if chain: cert = chain[0] oscrypto_cert = load_certificate(cert) if oscrypto_cert.asn1.hash_algo in set(['md5', 'md2']): raise_weak_signature(oscrypto_cert) if info == cert_verify_failed_info: verify_result = libssl.SSL_get_verify_result(self._ssl) chain = extract_chain(handshake_server_bytes) self_signed = False time_invalid = False no_issuer = False cert = None oscrypto_cert = None if chain: cert = chain[0] oscrypto_cert = load_certificate(cert) self_signed = oscrypto_cert.self_signed issuer_error_codes = set([ LibsslConst.X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, LibsslConst.X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN, LibsslConst.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY ]) if verify_result in issuer_error_codes: no_issuer = not self_signed time_error_codes = set([ LibsslConst.X509_V_ERR_CERT_HAS_EXPIRED, LibsslConst.X509_V_ERR_CERT_NOT_YET_VALID ]) time_invalid = verify_result in time_error_codes if time_invalid: raise_expired_not_yet_valid(cert) if no_issuer: raise_no_issuer(cert) if self_signed: raise_self_signed(cert) if oscrypto_cert and oscrypto_cert.asn1.hash_algo in set(['md5', 'md2']): raise_weak_signature(oscrypto_cert) raise_verification(cert) handle_openssl_error(0, TLSError) session_info = parse_session_info( handshake_server_bytes, handshake_client_bytes ) self._protocol = session_info['protocol'] self._cipher_suite = session_info['cipher_suite'] self._compression = session_info['compression'] self._session_id = session_info['session_id'] self._session_ticket = session_info['session_ticket'] if self._cipher_suite.find('_DHE_') != -1: dh_params_length = get_dh_params_length(handshake_server_bytes) if dh_params_length < 1024: self.close() raise_dh_params() # When saving the session for future requests, we use # SSL_get1_session() variant to increase the reference count. This # prevents the session from being freed when one connection closes # before another is opened. However, since we increase the ref # count, we also have to explicitly free any previous session. if self._session_id == 'new' or self._session_ticket == 'new': if self._session._ssl_session: libssl.SSL_SESSION_free(self._session._ssl_session) self._session._ssl_session = libssl.SSL_get1_session(self._ssl) if not self._session._manual_validation: if self.certificate.hash_algo in set(['md5', 'md2']): raise_weak_signature(self.certificate) # OpenSSL does not do hostname or IP address checking in the end # entity certificate, so we must perform that check if not self.certificate.is_valid_domain_ip(self._hostname): raise_hostname(self.certificate, self._hostname) except (OSError, socket_.error): if self._ssl: libssl.SSL_free(self._ssl) self._ssl = None self._rbio = None self._wbio = None # The BIOs are freed by SSL_free(), so we only need to free # them if for some reason SSL_free() was not called else: if self._rbio: libssl.BIO_free(self._rbio) self._rbio = None if self._wbio: libssl.BIO_free(self._wbio) self._wbio = None self.close() raise def _raw_read(self): """ Reads data from the socket and writes it to the memory bio used by libssl to decrypt the data. Returns the unencrypted data for the purpose of debugging handshakes. :return: A byte string of ciphertext from the socket. Used for debugging the handshake only. """ data = self._raw_bytes try: data += self._socket.recv(8192) except (socket_.error): pass output = data written = libssl.BIO_write(self._rbio, data, len(data)) self._raw_bytes = data[written:] return output def _raw_write(self): """ Takes ciphertext from the memory bio and writes it to the socket. :return: A byte string of ciphertext going to the socket. Used for debugging the handshake only. """ data_available = libssl.BIO_ctrl_pending(self._wbio) if data_available == 0: return b'' to_read = min(self._buffer_size, data_available) read = libssl.BIO_read(self._wbio, self._bio_write_buffer, to_read) to_write = bytes_from_buffer(self._bio_write_buffer, read) output = to_write while len(to_write): raise_disconnect = False try: sent = self._socket.send(to_write) except (socket_.error) as e: # Handle ECONNRESET and EPIPE if e.errno == 104 or e.errno == 32: raise_disconnect = True # Handle EPROTOTYPE. Newer versions of macOS will return this # if we try to call send() while the socket is being torn down elif sys.platform == 'darwin' and e.errno == 41: raise_disconnect = True else: raise if raise_disconnect: raise_disconnection() to_write = to_write[sent:] if len(to_write): self.select_write() return output def read(self, max_length): """ Reads data from the TLS-wrapped socket :param max_length: The number of bytes to read - output may be less than this :raises: socket.socket - when a non-TLS socket error occurs oscrypto.errors.TLSError - when a TLS-related error occurs ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the data read """ if not isinstance(max_length, int_types): raise TypeError(pretty_message( ''' max_length must be an integer, not %s ''', type_name(max_length) )) buffered_length = len(self._decrypted_bytes) # If we already have enough buffered data, just use that if buffered_length >= max_length: output = self._decrypted_bytes[0:max_length] self._decrypted_bytes = self._decrypted_bytes[max_length:] return output if self._ssl is None: self._raise_closed() # Don't block if we have buffered data available, since it is ok to # return less than the max_length if buffered_length > 0 and not self.select_read(0): output = self._decrypted_bytes self._decrypted_bytes = b'' return output # Only read enough to get the requested amount when # combined with buffered data to_read = min(self._buffer_size, max_length - buffered_length) output = self._decrypted_bytes # The SSL_read() loop handles renegotiations, so we need to handle # requests for both reads and writes again = True while again: again = False result = libssl.SSL_read(self._ssl, self._read_buffer, to_read) self._raw_write() if result <= 0: error = libssl.SSL_get_error(self._ssl, result) if error == LibsslConst.SSL_ERROR_WANT_READ: if self._raw_read() != b'': again = True continue raise_disconnection() elif error == LibsslConst.SSL_ERROR_WANT_WRITE: self._raw_write() again = True continue elif error == LibsslConst.SSL_ERROR_ZERO_RETURN: self._gracefully_closed = True self._shutdown(False) break else: handle_openssl_error(0, TLSError) output += bytes_from_buffer(self._read_buffer, result) if self._gracefully_closed and len(output) == 0: self._raise_closed() self._decrypted_bytes = output[max_length:] return output[0:max_length] def select_read(self, timeout=None): """ Blocks until the socket is ready to be read from, or the timeout is hit :param timeout: A float - the period of time to wait for data to be read. None for no time limit. :return: A boolean - if data is ready to be read. Will only be False if timeout is not None. """ # If we have buffered data, we consider a read possible if len(self._decrypted_bytes) > 0: return True read_ready, _, _ = select.select([self._socket], [], [], timeout) return len(read_ready) > 0 def read_until(self, marker): """ Reads data from the socket until a marker is found. Data read includes the marker. :param marker: A byte string or regex object from re.compile(). Used to determine when to stop reading. Regex objects are more inefficient since they must scan the entire byte string of read data each time data is read off the socket. :return: A byte string of the data read, including the marker """ if not isinstance(marker, byte_cls) and not isinstance(marker, Pattern): raise TypeError(pretty_message( ''' marker must be a byte string or compiled regex object, not %s ''', type_name(marker) )) output = b'' is_regex = isinstance(marker, Pattern) while True: if len(self._decrypted_bytes) > 0: chunk = self._decrypted_bytes self._decrypted_bytes = b'' else: if self._ssl is None: self._raise_closed() to_read = libssl.SSL_pending(self._ssl) or 8192 chunk = self.read(to_read) offset = len(output) output += chunk if is_regex: match = marker.search(output) if match is not None: end = match.end() break else: # If the marker was not found last time, we have to start # at a position where the marker would have its final char # in the newly read chunk start = max(0, offset - len(marker) - 1) match = output.find(marker, start) if match != -1: end = match + len(marker) break self._decrypted_bytes = output[end:] + self._decrypted_bytes return output[0:end] def read_line(self): r""" Reads a line from the socket, including the line ending of "\r\n", "\r", or "\n" :return: A byte string of the next line from the socket """ return self.read_until(_line_regex) def read_exactly(self, num_bytes): """ Reads exactly the specified number of bytes from the socket :param num_bytes: An integer - the exact number of bytes to read :return: A byte string of the data that was read """ output = b'' remaining = num_bytes while remaining > 0: output += self.read(remaining) remaining = num_bytes - len(output) return output def write(self, data): """ Writes data to the TLS-wrapped socket :param data: A byte string to write to the socket :raises: socket.socket - when a non-TLS socket error occurs oscrypto.errors.TLSError - when a TLS-related error occurs ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ data_len = len(data) while data_len: if self._ssl is None: self._raise_closed() result = libssl.SSL_write(self._ssl, data, data_len) self._raw_write() if result <= 0: error = libssl.SSL_get_error(self._ssl, result) if error == LibsslConst.SSL_ERROR_WANT_READ: if self._raw_read() != b'': continue raise_disconnection() elif error == LibsslConst.SSL_ERROR_WANT_WRITE: self._raw_write() continue elif error == LibsslConst.SSL_ERROR_ZERO_RETURN: self._gracefully_closed = True self._shutdown(False) self._raise_closed() else: handle_openssl_error(0, TLSError) data = data[result:] data_len = len(data) def select_write(self, timeout=None): """ Blocks until the socket is ready to be written to, or the timeout is hit :param timeout: A float - the period of time to wait for the socket to be ready to written to. None for no time limit. :return: A boolean - if the socket is ready for writing. Will only be False if timeout is not None. """ _, write_ready, _ = select.select([], [self._socket], [], timeout) return len(write_ready) > 0 def _shutdown(self, manual): """ Shuts down the TLS session and then shuts down the underlying socket :param manual: A boolean if the connection was manually shutdown """ if self._ssl is None: return while True: result = libssl.SSL_shutdown(self._ssl) # Don't be noisy if the socket is already closed try: self._raw_write() except (TLSDisconnectError): pass if result >= 0: break if result < 0: error = libssl.SSL_get_error(self._ssl, result) if error == LibsslConst.SSL_ERROR_WANT_READ: if self._raw_read() != b'': continue else: break elif error == LibsslConst.SSL_ERROR_WANT_WRITE: self._raw_write() continue else: handle_openssl_error(0, TLSError) if manual: self._local_closed = True libssl.SSL_free(self._ssl) self._ssl = None # BIOs are freed by SSL_free() self._rbio = None self._wbio = None try: self._socket.shutdown(socket_.SHUT_RDWR) except (socket_.error): pass def shutdown(self): """ Shuts down the TLS session and then shuts down the underlying socket """ self._shutdown(True) def close(self): """ Shuts down the TLS session and socket and forcibly closes it """ try: self.shutdown() finally: if self._socket: try: self._socket.close() except (socket_.error): pass self._socket = None def _read_certificates(self): """ Reads end-entity and intermediate certificate information from the TLS session """ stack_pointer = libssl.SSL_get_peer_cert_chain(self._ssl) if is_null(stack_pointer): handle_openssl_error(0, TLSError) if libcrypto_version_info < (1, 1): number_certs = libssl.sk_num(stack_pointer) else: number_certs = libssl.OPENSSL_sk_num(stack_pointer) self._intermediates = [] for index in range(0, number_certs): if libcrypto_version_info < (1, 1): x509_ = libssl.sk_value(stack_pointer, index) else: x509_ = libssl.OPENSSL_sk_value(stack_pointer, index) buffer_size = libcrypto.i2d_X509(x509_, null()) cert_buffer = buffer_from_bytes(buffer_size) cert_pointer = buffer_pointer(cert_buffer) cert_length = libcrypto.i2d_X509(x509_, cert_pointer) handle_openssl_error(cert_length) cert_data = bytes_from_buffer(cert_buffer, cert_length) cert = Asn1Certificate.load(cert_data) if index == 0: self._certificate = cert else: self._intermediates.append(cert) def _raise_closed(self): """ Raises an exception describing if the local or remote end closed the connection """ if self._local_closed: raise TLSDisconnectError('The connection was already closed') elif self._gracefully_closed: raise TLSGracefulDisconnectError('The remote end closed the connection') else: raise TLSDisconnectError('The connection was closed') @property def certificate(self): """ An asn1crypto.x509.Certificate object of the end-entity certificate presented by the server """ if self._ssl is None: self._raise_closed() if self._certificate is None: self._read_certificates() return self._certificate @property def intermediates(self): """ A list of asn1crypto.x509.Certificate objects that were presented as intermediates by the server """ if self._ssl is None: self._raise_closed() if self._certificate is None: self._read_certificates() return self._intermediates @property def cipher_suite(self): """ A unicode string of the IANA cipher suite name of the negotiated cipher suite """ return self._cipher_suite @property def protocol(self): """ A unicode string of: "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3" """ return self._protocol @property def compression(self): """ A boolean if compression is enabled """ return self._compression @property def session_id(self): """ A unicode string of "new" or "reused" or None for no ticket """ return self._session_id @property def session_ticket(self): """ A unicode string of "new" or "reused" or None for no ticket """ return self._session_ticket @property def session(self): """ The oscrypto.tls.TLSSession object used for this connection """ return self._session @property def hostname(self): """ A unicode string of the TLS server domain name or IP address """ return self._hostname @property def port(self): """ An integer of the port number the socket is connected to """ return self.socket.getpeername()[1] @property def socket(self): """ The underlying socket.socket connection """ if self._ssl is None: self._raise_closed() return self._socket def __del__(self): self.close() oscrypto-1.3.0/oscrypto/_openssl/util.py000066400000000000000000000153461421476274700204570ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .._errors import pretty_message from .._ffi import buffer_from_bytes, bytes_from_buffer from ._libcrypto import libcrypto, libcrypto_version_info, handle_openssl_error from .._rand import rand_bytes from .._types import type_name, byte_cls, int_types __all__ = [ 'pbkdf2', 'pkcs12_kdf', 'rand_bytes', ] # OpenSSL 0.9.8 does not include PBKDF2 if libcrypto_version_info < (1,): from .._pkcs5 import pbkdf2 else: def pbkdf2(hash_algorithm, password, salt, iterations, key_length): """ PBKDF2 from PKCS#5 :param hash_algorithm: The string name of the hash algorithm to use: "sha1", "sha224", "sha256", "sha384", "sha512" :param password: A byte string of the password to use an input to the KDF :param salt: A cryptographic random byte string :param iterations: The numbers of iterations to use when deriving the key :param key_length: The length of the desired key in bytes :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type :return: The derived key as a byte string """ if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) if not isinstance(salt, byte_cls): raise TypeError(pretty_message( ''' salt must be a byte string, not %s ''', type_name(salt) )) if not isinstance(iterations, int_types): raise TypeError(pretty_message( ''' iterations must be an integer, not %s ''', type_name(iterations) )) if iterations < 1: raise ValueError('iterations must be greater than 0') if not isinstance(key_length, int_types): raise TypeError(pretty_message( ''' key_length must be an integer, not %s ''', type_name(key_length) )) if key_length < 1: raise ValueError('key_length must be greater than 0') if hash_algorithm not in set(['sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) evp_md = { 'sha1': libcrypto.EVP_sha1, 'sha224': libcrypto.EVP_sha224, 'sha256': libcrypto.EVP_sha256, 'sha384': libcrypto.EVP_sha384, 'sha512': libcrypto.EVP_sha512 }[hash_algorithm]() output_buffer = buffer_from_bytes(key_length) result = libcrypto.PKCS5_PBKDF2_HMAC( password, len(password), salt, len(salt), iterations, evp_md, key_length, output_buffer ) handle_openssl_error(result) return bytes_from_buffer(output_buffer) pbkdf2.pure_python = False def pkcs12_kdf(hash_algorithm, password, salt, iterations, key_length, id_): """ KDF from RFC7292 appendix B.2 - https://tools.ietf.org/html/rfc7292#page-19 :param hash_algorithm: The string name of the hash algorithm to use: "md5", "sha1", "sha224", "sha256", "sha384", "sha512" :param password: A byte string of the password to use an input to the KDF :param salt: A cryptographic random byte string :param iterations: The numbers of iterations to use when deriving the key :param key_length: The length of the desired key in bytes :param id_: The ID of the usage - 1 for key, 2 for iv, 3 for mac :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type :return: The derived key as a byte string """ if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) if not isinstance(salt, byte_cls): raise TypeError(pretty_message( ''' salt must be a byte string, not %s ''', type_name(salt) )) if not isinstance(iterations, int_types): raise TypeError(pretty_message( ''' iterations must be an integer, not %s ''', type_name(iterations) )) if iterations < 1: raise ValueError(pretty_message( ''' iterations must be greater than 0 - is %s ''', repr(iterations) )) if not isinstance(key_length, int_types): raise TypeError(pretty_message( ''' key_length must be an integer, not %s ''', type_name(key_length) )) if key_length < 1: raise ValueError(pretty_message( ''' key_length must be greater than 0 - is %s ''', repr(key_length) )) if hash_algorithm not in set(['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "md5", "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) if id_ not in set([1, 2, 3]): raise ValueError(pretty_message( ''' id_ must be one of 1, 2, 3, not %s ''', repr(id_) )) utf16_password = password.decode('utf-8').encode('utf-16be') + b'\x00\x00' digest_type = { 'md5': libcrypto.EVP_md5, 'sha1': libcrypto.EVP_sha1, 'sha224': libcrypto.EVP_sha224, 'sha256': libcrypto.EVP_sha256, 'sha384': libcrypto.EVP_sha384, 'sha512': libcrypto.EVP_sha512, }[hash_algorithm]() output_buffer = buffer_from_bytes(key_length) result = libcrypto.PKCS12_key_gen_uni( utf16_password, len(utf16_password), salt, len(salt), id_, iterations, key_length, output_buffer, digest_type ) handle_openssl_error(result) return bytes_from_buffer(output_buffer) oscrypto-1.3.0/oscrypto/_pkcs1.py000066400000000000000000000475351421476274700170450ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys import hashlib import math import platform import struct import os from . import backend from .util import constant_compare, rand_bytes from ._asn1 import ( Certificate, int_from_bytes, int_to_bytes, PrivateKeyInfo, PublicKeyInfo, ) from ._errors import pretty_message from ._int import fill_width from ._types import type_name, byte_cls, int_types if sys.version_info < (3,): chr_cls = chr range = xrange # noqa else: def chr_cls(num): return bytes([num]) _backend = backend() __all__ = [ 'add_pss_padding', 'add_pkcs1v15_signature_padding', 'raw_rsa_private_crypt', 'raw_rsa_public_crypt', 'remove_pkcs1v15_encryption_padding', 'remove_pkcs1v15_signature_padding', 'verify_pss_padding', ] def _is_osx_107(): """ :return: A bool if the current machine is running OS X 10.7 """ if sys.platform != 'darwin': return False version = platform.mac_ver()[0] return tuple(map(int, version.split('.')))[0:2] == (10, 7) def add_pss_padding(hash_algorithm, salt_length, key_length, message): """ Pads a byte string using the EMSA-PSS-Encode operation described in PKCS#1 v2.2. :param hash_algorithm: The string name of the hash algorithm to use: "sha1", "sha224", "sha256", "sha384", "sha512" :param salt_length: The length of the salt as an integer - typically the same as the length of the output from the hash_algorithm :param key_length: The length of the RSA key, in bits :param message: A byte string of the message to pad :return: The encoded (passed) message """ if _backend != 'winlegacy' and sys.platform != 'darwin': raise SystemError(pretty_message( ''' Pure-python RSA PSS signature padding addition code is only for Windows XP/2003 and OS X ''' )) if not isinstance(message, byte_cls): raise TypeError(pretty_message( ''' message must be a byte string, not %s ''', type_name(message) )) if not isinstance(salt_length, int_types): raise TypeError(pretty_message( ''' salt_length must be an integer, not %s ''', type_name(salt_length) )) if salt_length < 0: raise ValueError(pretty_message( ''' salt_length must be 0 or more - is %s ''', repr(salt_length) )) if not isinstance(key_length, int_types): raise TypeError(pretty_message( ''' key_length must be an integer, not %s ''', type_name(key_length) )) if key_length < 512: raise ValueError(pretty_message( ''' key_length must be 512 or more - is %s ''', repr(key_length) )) if hash_algorithm not in set(['sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) hash_func = getattr(hashlib, hash_algorithm) # The maximal bit size of a non-negative integer is one less than the bit # size of the key since the first bit is used to store sign em_bits = key_length - 1 em_len = int(math.ceil(em_bits / 8)) message_digest = hash_func(message).digest() hash_length = len(message_digest) if em_len < hash_length + salt_length + 2: raise ValueError(pretty_message( ''' Key is not long enough to use with specified hash_algorithm and salt_length ''' )) if salt_length > 0: salt = os.urandom(salt_length) else: salt = b'' m_prime = (b'\x00' * 8) + message_digest + salt m_prime_digest = hash_func(m_prime).digest() padding = b'\x00' * (em_len - salt_length - hash_length - 2) db = padding + b'\x01' + salt db_mask = _mgf1(hash_algorithm, m_prime_digest, em_len - hash_length - 1) masked_db = int_to_bytes(int_from_bytes(db) ^ int_from_bytes(db_mask)) masked_db = fill_width(masked_db, len(db_mask)) zero_bits = (8 * em_len) - em_bits left_bit_mask = ('0' * zero_bits) + ('1' * (8 - zero_bits)) left_int_mask = int(left_bit_mask, 2) if left_int_mask != 255: masked_db = chr_cls(left_int_mask & ord(masked_db[0:1])) + masked_db[1:] return masked_db + m_prime_digest + b'\xBC' def verify_pss_padding(hash_algorithm, salt_length, key_length, message, signature): """ Verifies the PSS padding on an encoded message :param hash_algorithm: The string name of the hash algorithm to use: "sha1", "sha224", "sha256", "sha384", "sha512" :param salt_length: The length of the salt as an integer - typically the same as the length of the output from the hash_algorithm :param key_length: The length of the RSA key, in bits :param message: A byte string of the message to pad :param signature: The signature to verify :return: A boolean indicating if the signature is invalid """ if _backend != 'winlegacy' and sys.platform != 'darwin': raise SystemError(pretty_message( ''' Pure-python RSA PSS signature padding verification code is only for Windows XP/2003 and OS X ''' )) if not isinstance(message, byte_cls): raise TypeError(pretty_message( ''' message must be a byte string, not %s ''', type_name(message) )) if not isinstance(signature, byte_cls): raise TypeError(pretty_message( ''' signature must be a byte string, not %s ''', type_name(signature) )) if not isinstance(salt_length, int_types): raise TypeError(pretty_message( ''' salt_length must be an integer, not %s ''', type_name(salt_length) )) if salt_length < 0: raise ValueError(pretty_message( ''' salt_length must be 0 or more - is %s ''', repr(salt_length) )) if hash_algorithm not in set(['sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) hash_func = getattr(hashlib, hash_algorithm) em_bits = key_length - 1 em_len = int(math.ceil(em_bits / 8)) message_digest = hash_func(message).digest() hash_length = len(message_digest) if em_len < hash_length + salt_length + 2: return False if signature[-1:] != b'\xBC': return False zero_bits = (8 * em_len) - em_bits masked_db_length = em_len - hash_length - 1 masked_db = signature[0:masked_db_length] first_byte = ord(masked_db[0:1]) bits_that_should_be_zero = first_byte >> (8 - zero_bits) if bits_that_should_be_zero != 0: return False m_prime_digest = signature[masked_db_length:masked_db_length + hash_length] db_mask = _mgf1(hash_algorithm, m_prime_digest, em_len - hash_length - 1) left_bit_mask = ('0' * zero_bits) + ('1' * (8 - zero_bits)) left_int_mask = int(left_bit_mask, 2) if left_int_mask != 255: db_mask = chr_cls(left_int_mask & ord(db_mask[0:1])) + db_mask[1:] db = int_to_bytes(int_from_bytes(masked_db) ^ int_from_bytes(db_mask)) if len(db) < len(masked_db): db = (b'\x00' * (len(masked_db) - len(db))) + db zero_length = em_len - hash_length - salt_length - 2 zero_string = b'\x00' * zero_length if not constant_compare(db[0:zero_length], zero_string): return False if db[zero_length:zero_length + 1] != b'\x01': return False salt = db[0 - salt_length:] m_prime = (b'\x00' * 8) + message_digest + salt h_prime = hash_func(m_prime).digest() return constant_compare(m_prime_digest, h_prime) def _mgf1(hash_algorithm, seed, mask_length): """ The PKCS#1 MGF1 mask generation algorithm :param hash_algorithm: The string name of the hash algorithm to use: "sha1", "sha224", "sha256", "sha384", "sha512" :param seed: A byte string to use as the seed for the mask :param mask_length: The desired mask length, as an integer :return: A byte string of the mask """ if not isinstance(seed, byte_cls): raise TypeError(pretty_message( ''' seed must be a byte string, not %s ''', type_name(seed) )) if not isinstance(mask_length, int_types): raise TypeError(pretty_message( ''' mask_length must be an integer, not %s ''', type_name(mask_length) )) if mask_length < 1: raise ValueError(pretty_message( ''' mask_length must be greater than 0 - is %s ''', repr(mask_length) )) if hash_algorithm not in set(['sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) output = b'' hash_length = { 'sha1': 20, 'sha224': 28, 'sha256': 32, 'sha384': 48, 'sha512': 64 }[hash_algorithm] iterations = int(math.ceil(mask_length / hash_length)) pack = struct.Struct(b'>I').pack hash_func = getattr(hashlib, hash_algorithm) for counter in range(0, iterations): b = pack(counter) output += hash_func(seed + b).digest() return output[0:mask_length] def add_pkcs1v15_signature_padding(key_length, data): """ Adds PKCS#1 v1.5 padding to a message to be signed :param key_length: An integer of the number of bytes in the key :param data: A byte string to pad :return: The padded data as a byte string """ if _backend != 'winlegacy': raise SystemError(pretty_message( ''' Pure-python RSA PKCSv1.5 signature padding addition code is only for Windows XP/2003 ''' )) return _add_pkcs1v15_padding(key_length, data, 'signing') def remove_pkcs1v15_signature_padding(key_length, data): """ Removes PKCS#1 v1.5 padding from a signed message using constant time operations :param key_length: An integer of the number of bytes in the key :param data: A byte string to unpad :return: The unpadded data as a byte string """ if _backend != 'winlegacy': raise SystemError(pretty_message( ''' Pure-python RSA PKCSv1.5 signature padding removal code is only for Windows XP/2003 ''' )) return _remove_pkcs1v15_padding(key_length, data, 'verifying') def remove_pkcs1v15_encryption_padding(key_length, data): """ Removes PKCS#1 v1.5 padding from a decrypted message using constant time operations :param key_length: An integer of the number of bytes in the key :param data: A byte string to unpad :return: The unpadded data as a byte string """ if not _is_osx_107(): raise SystemError(pretty_message( ''' Pure-python RSA PKCSv1.5 encryption padding removal code is only for OS X 10.7 ''' )) return _remove_pkcs1v15_padding(key_length, data, 'decrypting') def _add_pkcs1v15_padding(key_length, data, operation): """ Adds PKCS#1 v1.5 padding to a message :param key_length: An integer of the number of bytes in the key :param data: A byte string to unpad :param operation: A unicode string of "encrypting" or "signing" :return: The padded data as a byte string """ if operation == 'encrypting': second_byte = b'\x02' else: second_byte = b'\x01' if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if not isinstance(key_length, int_types): raise TypeError(pretty_message( ''' key_length must be an integer, not %s ''', type_name(key_length) )) if key_length < 64: raise ValueError(pretty_message( ''' key_length must be 64 or more - is %s ''', repr(key_length) )) if len(data) > key_length - 11: raise ValueError(pretty_message( ''' data must be between 1 and %s bytes long - is %s ''', key_length - 11, len(data) )) required_bytes = key_length - 3 - len(data) padding = b'' while required_bytes > 0: temp_padding = rand_bytes(required_bytes) # Remove null bytes since they are markers in PKCS#1 v1.5 temp_padding = b''.join(temp_padding.split(b'\x00')) padding += temp_padding required_bytes -= len(temp_padding) return b'\x00' + second_byte + padding + b'\x00' + data def _remove_pkcs1v15_padding(key_length, data, operation): """ Removes PKCS#1 v1.5 padding from a message using constant time operations :param key_length: An integer of the number of bytes in the key :param data: A byte string to unpad :param operation: A unicode string of "decrypting" or "verifying" :return: The unpadded data as a byte string """ if operation == 'decrypting': second_byte = 2 else: second_byte = 1 if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if not isinstance(key_length, int_types): raise TypeError(pretty_message( ''' key_length must be an integer, not %s ''', type_name(key_length) )) if key_length < 64: raise ValueError(pretty_message( ''' key_length must be 64 or more - is %s ''', repr(key_length) )) if len(data) != key_length: raise ValueError('Error %s' % operation) error = 0 trash = 0 padding_end = 0 # Uses bitwise operations on an error variable and another trash variable # to perform constant time error checking/token scanning on the data for i in range(0, len(data)): byte = data[i:i + 1] byte_num = ord(byte) # First byte should be \x00 if i == 0: error |= byte_num # Second byte should be \x02 for decryption, \x01 for verification elif i == 1: error |= int((byte_num | second_byte) != second_byte) # Bytes 3-10 should not be \x00 elif i < 10: error |= int((byte_num ^ 0) == 0) # Byte 11 or after that is zero is end of padding else: non_zero = byte_num | 0 if padding_end == 0: if non_zero: trash |= i else: padding_end |= i else: if non_zero: trash |= i else: trash |= i if error != 0: raise ValueError('Error %s' % operation) return data[padding_end + 1:] def raw_rsa_private_crypt(private_key, data): """ Performs a raw RSA algorithm in a byte string using a private key. This is a low-level primitive and is prone to disastrous results if used incorrectly. :param private_key: An oscrypto.asymmetric.PrivateKey object :param data: A byte string of the plaintext to be signed or ciphertext to be decrypted. Must be less than or equal to the length of the private key. In the case of signing, padding must already be applied. In the case of decryption, padding must be removed afterward. :return: A byte string of the transformed data """ if _backend != 'winlegacy': raise SystemError('Pure-python RSA crypt is only for Windows XP/2003') if not hasattr(private_key, 'asn1') or not isinstance(private_key.asn1, PrivateKeyInfo): raise TypeError(pretty_message( ''' private_key must be an instance of the oscrypto.asymmetric.PrivateKey class, not %s ''', type_name(private_key) )) algo = private_key.asn1['private_key_algorithm']['algorithm'].native if algo != 'rsa' and algo != 'rsassa_pss': raise ValueError(pretty_message( ''' private_key must be an RSA key, not %s ''', algo.upper() )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) rsa_private_key = private_key.asn1['private_key'].parsed transformed_int = pow( int_from_bytes(data), rsa_private_key['private_exponent'].native, rsa_private_key['modulus'].native ) return int_to_bytes(transformed_int, width=private_key.asn1.byte_size) def raw_rsa_public_crypt(certificate_or_public_key, data): """ Performs a raw RSA algorithm in a byte string using a certificate or public key. This is a low-level primitive and is prone to disastrous results if used incorrectly. :param certificate_or_public_key: An oscrypto.asymmetric.PublicKey or oscrypto.asymmetric.Certificate object :param data: A byte string of the signature when verifying, or padded plaintext when encrypting. Must be less than or equal to the length of the public key. When verifying, padding will need to be removed afterwards. When encrypting, padding must be applied before. :return: A byte string of the transformed data """ if _backend != 'winlegacy': raise SystemError('Pure-python RSA crypt is only for Windows XP/2003') has_asn1 = hasattr(certificate_or_public_key, 'asn1') valid_types = (PublicKeyInfo, Certificate) if not has_asn1 or not isinstance(certificate_or_public_key.asn1, valid_types): raise TypeError(pretty_message( ''' certificate_or_public_key must be an instance of the oscrypto.asymmetric.PublicKey or oscrypto.asymmetric.Certificate classes, not %s ''', type_name(certificate_or_public_key) )) algo = certificate_or_public_key.asn1['algorithm']['algorithm'].native if algo != 'rsa' and algo != 'rsassa_pss': raise ValueError(pretty_message( ''' certificate_or_public_key must be an RSA key, not %s ''', algo.upper() )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) rsa_public_key = certificate_or_public_key.asn1['public_key'].parsed transformed_int = pow( int_from_bytes(data), rsa_public_key['public_exponent'].native, rsa_public_key['modulus'].native ) return int_to_bytes( transformed_int, width=certificate_or_public_key.asn1.byte_size ) oscrypto-1.3.0/oscrypto/_pkcs12.py000066400000000000000000000114141421476274700171120ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys import hashlib import math from ._asn1 import int_from_bytes, int_to_bytes from ._errors import pretty_message from ._types import type_name, byte_cls, int_types if sys.version_info < (3,): chr_cls = chr else: def chr_cls(num): return bytes([num]) __all__ = [ 'pkcs12_kdf', ] def pkcs12_kdf(hash_algorithm, password, salt, iterations, key_length, id_): """ KDF from RFC7292 appendix b.2 - https://tools.ietf.org/html/rfc7292#page-19 :param hash_algorithm: The string name of the hash algorithm to use: "md5", "sha1", "sha224", "sha256", "sha384", "sha512" :param password: A byte string of the password to use an input to the KDF :param salt: A cryptographic random byte string :param iterations: The numbers of iterations to use when deriving the key :param key_length: The length of the desired key in bytes :param id_: The ID of the usage - 1 for key, 2 for iv, 3 for mac :return: The derived key as a byte string """ if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) if not isinstance(salt, byte_cls): raise TypeError(pretty_message( ''' salt must be a byte string, not %s ''', type_name(salt) )) if not isinstance(iterations, int_types): raise TypeError(pretty_message( ''' iterations must be an integer, not %s ''', type_name(iterations) )) if iterations < 1: raise ValueError(pretty_message( ''' iterations must be greater than 0 - is %s ''', repr(iterations) )) if not isinstance(key_length, int_types): raise TypeError(pretty_message( ''' key_length must be an integer, not %s ''', type_name(key_length) )) if key_length < 1: raise ValueError(pretty_message( ''' key_length must be greater than 0 - is %s ''', repr(key_length) )) if hash_algorithm not in set(['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "md5", "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) if id_ not in set([1, 2, 3]): raise ValueError(pretty_message( ''' id_ must be one of 1, 2, 3, not %s ''', repr(id_) )) utf16_password = password.decode('utf-8').encode('utf-16be') + b'\x00\x00' algo = getattr(hashlib, hash_algorithm) # u and v values are bytes (not bits as in the RFC) u = { 'md5': 16, 'sha1': 20, 'sha224': 28, 'sha256': 32, 'sha384': 48, 'sha512': 64 }[hash_algorithm] if hash_algorithm in ['sha384', 'sha512']: v = 128 else: v = 64 # Step 1 d = chr_cls(id_) * v # Step 2 s = b'' if salt != b'': s_len = v * int(math.ceil(float(len(salt)) / v)) while len(s) < s_len: s += salt s = s[0:s_len] # Step 3 p = b'' if utf16_password != b'': p_len = v * int(math.ceil(float(len(utf16_password)) / v)) while len(p) < p_len: p += utf16_password p = p[0:p_len] # Step 4 i = s + p # Step 5 c = int(math.ceil(float(key_length) / u)) a = b'\x00' * (c * u) for num in range(1, c + 1): # Step 6A a2 = algo(d + i).digest() for _ in range(2, iterations + 1): a2 = algo(a2).digest() if num < c: # Step 6B b = b'' while len(b) < v: b += a2 b = int_from_bytes(b[0:v]) + 1 # Step 6C for num2 in range(0, len(i) // v): start = num2 * v end = (num2 + 1) * v i_num2 = i[start:end] i_num2 = int_to_bytes(int_from_bytes(i_num2) + b) # Ensure the new slice is the right size i_num2_l = len(i_num2) if i_num2_l > v: i_num2 = i_num2[i_num2_l - v:] i = i[0:start] + i_num2 + i[end:] # Step 7 (one piece at a time) begin = (num - 1) * u to_copy = min(key_length, u) a = a[0:begin] + a2[0:to_copy] + a[begin + to_copy:] return a[0:key_length] oscrypto-1.3.0/oscrypto/_pkcs5.py000066400000000000000000000065251421476274700170430ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys import hashlib import hmac import struct from ._asn1 import int_from_bytes, int_to_bytes from ._errors import pretty_message from ._types import type_name, byte_cls, int_types if sys.version_info < (3,): chr_cls = chr else: def chr_cls(num): return bytes([num]) __all__ = [ 'pbkdf2', ] def pbkdf2(hash_algorithm, password, salt, iterations, key_length): """ Implements PBKDF2 from PKCS#5 v2.2 in pure Python :param hash_algorithm: The string name of the hash algorithm to use: "md5", "sha1", "sha224", "sha256", "sha384", "sha512" :param password: A byte string of the password to use an input to the KDF :param salt: A cryptographic random byte string :param iterations: The numbers of iterations to use when deriving the key :param key_length: The length of the desired key in bytes :return: The derived key as a byte string """ if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) if not isinstance(salt, byte_cls): raise TypeError(pretty_message( ''' salt must be a byte string, not %s ''', type_name(salt) )) if not isinstance(iterations, int_types): raise TypeError(pretty_message( ''' iterations must be an integer, not %s ''', type_name(iterations) )) if iterations < 1: raise ValueError(pretty_message( ''' iterations must be greater than 0 - is %s ''', repr(iterations) )) if not isinstance(key_length, int_types): raise TypeError(pretty_message( ''' key_length must be an integer, not %s ''', type_name(key_length) )) if key_length < 1: raise ValueError(pretty_message( ''' key_length must be greater than 0 - is %s ''', repr(key_length) )) if hash_algorithm not in set(['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "md5", "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) algo = getattr(hashlib, hash_algorithm) hash_length = { 'md5': 16, 'sha1': 20, 'sha224': 28, 'sha256': 32, 'sha384': 48, 'sha512': 64 }[hash_algorithm] original_hmac = hmac.new(password, None, algo) block = 1 output = b'' while len(output) < key_length: prf = original_hmac.copy() prf.update(salt + struct.pack(b'>I', block)) last = prf.digest() u = int_from_bytes(last) for _ in range(iterations-1): prf = original_hmac.copy() prf.update(last) last = prf.digest() u ^= int_from_bytes(last) output += int_to_bytes(u, width=hash_length) block += 1 return output[0:key_length] pbkdf2.pure_python = True oscrypto-1.3.0/oscrypto/_rand.py000066400000000000000000000020201421476274700167240ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os from ._errors import pretty_message from ._types import type_name, int_types __all__ = [ 'rand_bytes', ] def rand_bytes(length): """ Returns a number of random bytes suitable for cryptographic purposes :param length: The desired number of bytes :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by OpenSSL :return: A byte string """ if not isinstance(length, int_types): raise TypeError(pretty_message( ''' length must be an integer, not %s ''', type_name(length) )) if length < 1: raise ValueError('length must be greater than 0') if length > 1024: raise ValueError('length must not be greater than 1024') return os.urandom(length) oscrypto-1.3.0/oscrypto/_tls.py000066400000000000000000000426651421476274700166250ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import re from datetime import datetime from ._asn1 import Certificate, int_from_bytes, timezone from ._cipher_suites import CIPHER_SUITE_MAP from .errors import TLSVerificationError, TLSDisconnectError, TLSError __all__ = [ 'detect_client_auth_request', 'extract_chain', 'get_dh_params_length', 'parse_alert', 'parse_handshake_messages', 'parse_session_info', 'parse_tls_records', 'raise_client_auth', 'raise_dh_params', 'raise_disconnection', 'raise_expired_not_yet_valid', 'raise_handshake', 'raise_hostname', 'raise_no_issuer', 'raise_protocol_error', 'raise_revoked', 'raise_self_signed', 'raise_verification', 'raise_weak_signature', ] def extract_chain(server_handshake_bytes): """ Extracts the X.509 certificates from the server handshake bytes for use when debugging :param server_handshake_bytes: A byte string of the handshake data received from the server :return: A list of asn1crypto.x509.Certificate objects """ output = [] chain_bytes = None for record_type, _, record_data in parse_tls_records(server_handshake_bytes): if record_type != b'\x16': continue for message_type, message_data in parse_handshake_messages(record_data): if message_type == b'\x0b': chain_bytes = message_data break if chain_bytes: break if chain_bytes: # The first 3 bytes are the cert chain length pointer = 3 while pointer < len(chain_bytes): cert_length = int_from_bytes(chain_bytes[pointer:pointer + 3]) cert_start = pointer + 3 cert_end = cert_start + cert_length pointer = cert_end cert_bytes = chain_bytes[cert_start:cert_end] output.append(Certificate.load(cert_bytes)) return output def detect_client_auth_request(server_handshake_bytes): """ Determines if a CertificateRequest message is sent from the server asking the client for a certificate :param server_handshake_bytes: A byte string of the handshake data received from the server :return: A boolean - if a client certificate request was found """ for record_type, _, record_data in parse_tls_records(server_handshake_bytes): if record_type != b'\x16': continue for message_type, message_data in parse_handshake_messages(record_data): if message_type == b'\x0d': return True return False def get_dh_params_length(server_handshake_bytes): """ Determines the length of the DH params from the ServerKeyExchange :param server_handshake_bytes: A byte string of the handshake data received from the server :return: None or an integer of the bit size of the DH parameters """ output = None dh_params_bytes = None for record_type, _, record_data in parse_tls_records(server_handshake_bytes): if record_type != b'\x16': continue for message_type, message_data in parse_handshake_messages(record_data): if message_type == b'\x0c': dh_params_bytes = message_data break if dh_params_bytes: break if dh_params_bytes: output = int_from_bytes(dh_params_bytes[0:2]) * 8 return output def parse_alert(server_handshake_bytes): """ Parses the handshake for protocol alerts :param server_handshake_bytes: A byte string of the handshake data received from the server :return: None or an 2-element tuple of integers: 0: 1 (warning) or 2 (fatal) 1: The alert description (see https://tools.ietf.org/html/rfc5246#section-7.2) """ for record_type, _, record_data in parse_tls_records(server_handshake_bytes): if record_type != b'\x15': continue if len(record_data) != 2: return None return (int_from_bytes(record_data[0:1]), int_from_bytes(record_data[1:2])) return None def parse_session_info(server_handshake_bytes, client_handshake_bytes): """ Parse the TLS handshake from the client to the server to extract information including the cipher suite selected, if compression is enabled, the session id and if a new or reused session ticket exists. :param server_handshake_bytes: A byte string of the handshake data received from the server :param client_handshake_bytes: A byte string of the handshake data sent to the server :return: A dict with the following keys: - "protocol": unicode string - "cipher_suite": unicode string - "compression": boolean - "session_id": "new", "reused" or None - "session_ticket: "new", "reused" or None """ protocol = None cipher_suite = None compression = False session_id = None session_ticket = None server_session_id = None client_session_id = None for record_type, _, record_data in parse_tls_records(server_handshake_bytes): if record_type != b'\x16': continue for message_type, message_data in parse_handshake_messages(record_data): # Ensure we are working with a ServerHello message if message_type != b'\x02': continue protocol = { b'\x03\x00': "SSLv3", b'\x03\x01': "TLSv1", b'\x03\x02': "TLSv1.1", b'\x03\x03': "TLSv1.2", b'\x03\x04': "TLSv1.3", }[message_data[0:2]] session_id_length = int_from_bytes(message_data[34:35]) if session_id_length > 0: server_session_id = message_data[35:35 + session_id_length] cipher_suite_start = 35 + session_id_length cipher_suite_bytes = message_data[cipher_suite_start:cipher_suite_start + 2] cipher_suite = CIPHER_SUITE_MAP[cipher_suite_bytes] compression_start = cipher_suite_start + 2 compression = message_data[compression_start:compression_start + 1] != b'\x00' extensions_length_start = compression_start + 1 extensions_data = message_data[extensions_length_start:] for extension_type, extension_data in _parse_hello_extensions(extensions_data): if extension_type == 35: session_ticket = "new" break break for record_type, _, record_data in parse_tls_records(client_handshake_bytes): if record_type != b'\x16': continue for message_type, message_data in parse_handshake_messages(record_data): # Ensure we are working with a ClientHello message if message_type != b'\x01': continue session_id_length = int_from_bytes(message_data[34:35]) if session_id_length > 0: client_session_id = message_data[35:35 + session_id_length] cipher_suite_start = 35 + session_id_length cipher_suite_length = int_from_bytes(message_data[cipher_suite_start:cipher_suite_start + 2]) compression_start = cipher_suite_start + 2 + cipher_suite_length compression_length = int_from_bytes(message_data[compression_start:compression_start + 1]) # On subsequent requests, the session ticket will only be seen # in the ClientHello message if server_session_id is None and session_ticket is None: extensions_length_start = compression_start + 1 + compression_length extensions_data = message_data[extensions_length_start:] for extension_type, extension_data in _parse_hello_extensions(extensions_data): if extension_type == 35: session_ticket = "reused" break break if server_session_id is not None: if client_session_id is None: session_id = "new" else: if client_session_id != server_session_id: session_id = "new" else: session_id = "reused" return { "protocol": protocol, "cipher_suite": cipher_suite, "compression": compression, "session_id": session_id, "session_ticket": session_ticket, } def parse_tls_records(data): """ Creates a generator returning tuples of information about each record in a byte string of data from a TLS client or server. Stops as soon as it find a ChangeCipherSpec message since all data from then on is encrypted. :param data: A byte string of TLS records :return: A generator that yields 3-element tuples: [0] Byte string of record type [1] Byte string of protocol version [2] Byte string of record data """ pointer = 0 data_len = len(data) while pointer < data_len: # Don't try to parse any more once the ChangeCipherSpec is found if data[pointer:pointer + 1] == b'\x14': break length = int_from_bytes(data[pointer + 3:pointer + 5]) yield ( data[pointer:pointer + 1], data[pointer + 1:pointer + 3], data[pointer + 5:pointer + 5 + length] ) pointer += 5 + length def parse_handshake_messages(data): """ Creates a generator returning tuples of information about each message in a byte string of data from a TLS handshake record :param data: A byte string of a TLS handshake record data :return: A generator that yields 2-element tuples: [0] Byte string of message type [1] Byte string of message data """ pointer = 0 data_len = len(data) while pointer < data_len: length = int_from_bytes(data[pointer + 1:pointer + 4]) yield ( data[pointer:pointer + 1], data[pointer + 4:pointer + 4 + length] ) pointer += 4 + length def _parse_hello_extensions(data): """ Creates a generator returning tuples of information about each extension from a byte string of extension data contained in a ServerHello ores ClientHello message :param data: A byte string of a extension data from a TLS ServerHello or ClientHello message :return: A generator that yields 2-element tuples: [0] Byte string of extension type [1] Byte string of extension data """ if data == b'': return extentions_length = int_from_bytes(data[0:2]) extensions_start = 2 extensions_end = 2 + extentions_length pointer = extensions_start while pointer < extensions_end: extension_type = int_from_bytes(data[pointer:pointer + 2]) extension_length = int_from_bytes(data[pointer + 2:pointer + 4]) yield ( extension_type, data[pointer + 4:pointer + 4 + extension_length] ) pointer += 4 + extension_length def raise_hostname(certificate, hostname): """ Raises a TLSVerificationError due to a hostname mismatch :param certificate: An asn1crypto.x509.Certificate object :raises: TLSVerificationError """ is_ip = re.match('^\\d+\\.\\d+\\.\\d+\\.\\d+$', hostname) or hostname.find(':') != -1 if is_ip: hostname_type = 'IP address %s' % hostname else: hostname_type = 'domain name %s' % hostname message = 'Server certificate verification failed - %s does not match' % hostname_type valid_ips = ', '.join(certificate.valid_ips) valid_domains = ', '.join(certificate.valid_domains) if valid_domains: message += ' valid domains: %s' % valid_domains if valid_domains and valid_ips: message += ' or' if valid_ips: message += ' valid IP addresses: %s' % valid_ips raise TLSVerificationError(message, certificate) def raise_verification(certificate): """ Raises a generic TLSVerificationError :param certificate: An asn1crypto.x509.Certificate object :raises: TLSVerificationError """ message = 'Server certificate verification failed' raise TLSVerificationError(message, certificate) def raise_weak_signature(certificate): """ Raises a TLSVerificationError when a certificate uses a weak signature algorithm :param certificate: An asn1crypto.x509.Certificate object :raises: TLSVerificationError """ message = 'Server certificate verification failed - weak certificate signature algorithm' raise TLSVerificationError(message, certificate) def raise_client_auth(): """ Raises a TLSError indicating client authentication is required :raises: TLSError """ message = 'TLS handshake failed - client authentication required' raise TLSError(message) def raise_revoked(certificate): """ Raises a TLSVerificationError due to the certificate being revoked :param certificate: An asn1crypto.x509.Certificate object :raises: TLSVerificationError """ message = 'Server certificate verification failed - certificate has been revoked' raise TLSVerificationError(message, certificate) def raise_no_issuer(certificate): """ Raises a TLSVerificationError due to no issuer certificate found in trust roots :param certificate: An asn1crypto.x509.Certificate object :raises: TLSVerificationError """ message = 'Server certificate verification failed - certificate issuer not found in trusted root certificate store' raise TLSVerificationError(message, certificate) def raise_self_signed(certificate): """ Raises a TLSVerificationError due to a self-signed certificate roots :param certificate: An asn1crypto.x509.Certificate object :raises: TLSVerificationError """ message = 'Server certificate verification failed - certificate is self-signed' raise TLSVerificationError(message, certificate) def raise_lifetime_too_long(certificate): """ Raises a TLSVerificationError due to a certificate lifetime exceeding the CAB forum certificate lifetime limit :param certificate: An asn1crypto.x509.Certificate object :raises: TLSVerificationError """ message = 'Server certificate verification failed - certificate lifetime is too long' raise TLSVerificationError(message, certificate) def raise_expired_not_yet_valid(certificate): """ Raises a TLSVerificationError due to certificate being expired, or not yet being valid :param certificate: An asn1crypto.x509.Certificate object :raises: TLSVerificationError """ validity = certificate['tbs_certificate']['validity'] not_after = validity['not_after'].native not_before = validity['not_before'].native now = datetime.now(timezone.utc) if not_before > now: formatted_before = not_before.strftime('%Y-%m-%d %H:%M:%SZ') message = 'Server certificate verification failed - certificate not valid until %s' % formatted_before elif not_after < now: formatted_after = not_after.strftime('%Y-%m-%d %H:%M:%SZ') message = 'Server certificate verification failed - certificate expired %s' % formatted_after raise TLSVerificationError(message, certificate) def raise_disconnection(): """ Raises a TLSDisconnectError due to a disconnection :raises: TLSDisconnectError """ raise TLSDisconnectError('The remote end closed the connection') def raise_protocol_error(server_handshake_bytes): """ Raises a TLSError due to a protocol error :param server_handshake_bytes: A byte string of the handshake data received from the server :raises: TLSError """ other_protocol = detect_other_protocol(server_handshake_bytes) if other_protocol: raise TLSError('TLS protocol error - server responded using %s' % other_protocol) raise TLSError('TLS protocol error - server responded using a different protocol') def raise_handshake(): """ Raises a TLSError due to a handshake error :raises: TLSError """ raise TLSError('TLS handshake failed') def raise_protocol_version(): """ Raises a TLSError due to a TLS version incompatibility :raises: TLSError """ raise TLSError('TLS handshake failed - protocol version error') def raise_dh_params(): """ Raises a TLSError due to weak DH params :raises: TLSError """ raise TLSError('TLS handshake failed - weak DH parameters') def detect_other_protocol(server_handshake_bytes): """ Looks at the server handshake bytes to try and detect a different protocol :param server_handshake_bytes: A byte string of the handshake data received from the server :return: None, or a unicode string of "ftp", "http", "imap", "pop3", "smtp" """ if server_handshake_bytes[0:5] == b'HTTP/': return 'HTTP' if server_handshake_bytes[0:4] == b'220 ': if re.match(b'^[^\r\n]*ftp', server_handshake_bytes, re.I): return 'FTP' else: return 'SMTP' if server_handshake_bytes[0:4] == b'220-': return 'FTP' if server_handshake_bytes[0:4] == b'+OK ': return 'POP3' if server_handshake_bytes[0:4] == b'* OK' or server_handshake_bytes[0:9] == b'* PREAUTH': return 'IMAP' return None oscrypto-1.3.0/oscrypto/_types.py000066400000000000000000000015501421476274700171530ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys import inspect if sys.version_info < (3,): str_cls = unicode # noqa byte_cls = str int_types = (int, long) # noqa def bytes_to_list(byte_string): return [ord(b) for b in byte_string] else: str_cls = str byte_cls = bytes int_types = (int,) bytes_to_list = list def type_name(value): """ Returns a user-readable name for the type of an object :param value: A value to get the type name of :return: A unicode string of the object's type name """ if inspect.isclass(value): cls = value else: cls = value.__class__ if cls.__module__ in set(['builtins', '__builtin__']): return cls.__name__ return '%s.%s' % (cls.__module__, cls.__name__) oscrypto-1.3.0/oscrypto/_win/000077500000000000000000000000001421476274700162315ustar00rootroot00000000000000oscrypto-1.3.0/oscrypto/_win/__init__.py000066400000000000000000000000001421476274700203300ustar00rootroot00000000000000oscrypto-1.3.0/oscrypto/_win/_advapi32.py000066400000000000000000000103761421476274700203620ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys from .. import ffi from ._decode import _try_decode from ..errors import SignatureError from .._ffi import new, unwrap, null from .._types import str_cls if ffi() == 'cffi': from ._advapi32_cffi import advapi32, get_error else: from ._advapi32_ctypes import advapi32, get_error __all__ = [ 'advapi32', 'Advapi32Const', 'handle_error', ] _gwv = sys.getwindowsversion() _win_version_info = (_gwv[0], _gwv[1]) def open_context_handle(provider, verify_only=True): if provider == Advapi32Const.MS_ENH_RSA_AES_PROV: provider_type = Advapi32Const.PROV_RSA_AES elif provider == Advapi32Const.MS_ENH_DSS_DH_PROV: provider_type = Advapi32Const.PROV_DSS_DH else: raise ValueError('Invalid provider specified: %s' % provider) # The DSS provider needs a container to allow importing and exporting # private keys, but all of the RSA stuff works fine with CRYPT_VERIFYCONTEXT if verify_only or provider != Advapi32Const.MS_ENH_DSS_DH_PROV: container_name = null() flags = Advapi32Const.CRYPT_VERIFYCONTEXT else: container_name = Advapi32Const.CONTAINER_NAME flags = Advapi32Const.CRYPT_NEWKEYSET context_handle_pointer = new(advapi32, 'HCRYPTPROV *') res = advapi32.CryptAcquireContextW( context_handle_pointer, container_name, provider, provider_type, flags ) # If using the DSS provider and the container exists, just open it if not res and get_error()[0] == Advapi32Const.NTE_EXISTS: res = advapi32.CryptAcquireContextW( context_handle_pointer, container_name, provider, provider_type, 0 ) handle_error(res) return unwrap(context_handle_pointer) def close_context_handle(handle): res = advapi32.CryptReleaseContext(handle, 0) handle_error(res) def handle_error(result): """ Extracts the last Windows error message into a python unicode string :param result: A function result, 0 or None indicates failure :return: A unicode string error message """ if result: return code, error_string = get_error() if code == Advapi32Const.NTE_BAD_SIGNATURE: raise SignatureError('Signature is invalid') if not isinstance(error_string, str_cls): error_string = _try_decode(error_string) raise OSError(error_string) class Advapi32Const(): # Name we give to a container used to make DSA private key import/export work CONTAINER_NAME = 'oscrypto temporary DSS keyset' PROV_RSA_AES = 24 PROV_DSS_DH = 13 X509_PUBLIC_KEY_INFO = 8 PKCS_PRIVATE_KEY_INFO = 44 X509_DSS_SIGNATURE = 40 CRYPT_NO_SALT = 0x00000010 MS_ENH_DSS_DH_PROV = "Microsoft Enhanced DSS and Diffie-Hellman Cryptographic Provider" # This is the name for Windows Server 2003 and newer and Windows Vista and newer MS_ENH_RSA_AES_PROV = "Microsoft Enhanced RSA and AES Cryptographic Provider" CRYPT_EXPORTABLE = 1 CRYPT_NEWKEYSET = 0x00000008 CRYPT_VERIFYCONTEXT = 0xF0000000 CALG_MD5 = 0x00008003 CALG_SHA1 = 0x00008004 CALG_SHA_256 = 0x0000800c CALG_SHA_384 = 0x0000800d CALG_SHA_512 = 0x0000800e CALG_RC2 = 0x00006602 CALG_RC4 = 0x00006801 CALG_DES = 0x00006601 CALG_3DES_112 = 0x00006609 CALG_3DES = 0x00006603 CALG_AES_128 = 0x0000660e CALG_AES_192 = 0x0000660f CALG_AES_256 = 0x00006610 CALG_DSS_SIGN = 0x00002200 CALG_RSA_SIGN = 0x00002400 CALG_RSA_KEYX = 0x0000a400 CRYPT_MODE_CBC = 1 PKCS5_PADDING = 1 CUR_BLOB_VERSION = 2 PUBLICKEYBLOB = 6 PRIVATEKEYBLOB = 7 PLAINTEXTKEYBLOB = 8 KP_IV = 1 KP_PADDING = 3 KP_MODE = 4 KP_EFFECTIVE_KEYLEN = 19 CRYPT_OAEP = 0x00000040 NTE_BAD_SIGNATURE = -2146893818 # 0x80090006 NTE_EXISTS = -2146893809 # 0x8009000F AT_SIGNATURE = 2 RSA1 = 0x31415352 RSA2 = 0x32415352 DSS1 = 0x31535344 DSS2 = 0x32535344 if _win_version_info == (5, 1): # This is the Windows XP name for the provider Advapi32Const.MS_ENH_RSA_AES_PROV = "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" oscrypto-1.3.0/oscrypto/_win/_advapi32_cffi.py000066400000000000000000000107701421476274700213470ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .._ffi import register_ffi from .._types import str_cls from ..errors import LibraryNotFoundError import cffi __all__ = [ 'advapi32', 'get_error', ] ffi = cffi.FFI() if cffi.__version_info__ >= (0, 9): ffi.set_unicode(True) ffi.cdef(""" typedef HANDLE HCRYPTPROV; typedef HANDLE HCRYPTKEY; typedef HANDLE HCRYPTHASH; typedef unsigned int ALG_ID; typedef struct _CRYPTOAPI_BLOB { DWORD cbData; BYTE *pbData; } CRYPT_INTEGER_BLOB, CRYPT_OBJID_BLOB, CRYPT_DER_BLOB, CRYPT_ATTR_BLOB; typedef struct _CRYPT_ALGORITHM_IDENTIFIER { LPSTR pszObjId; CRYPT_OBJID_BLOB Parameters; } CRYPT_ALGORITHM_IDENTIFIER; typedef struct _CRYPT_BIT_BLOB { DWORD cbData; BYTE *pbData; DWORD cUnusedBits; } CRYPT_BIT_BLOB; typedef struct _CERT_PUBLIC_KEY_INFO { CRYPT_ALGORITHM_IDENTIFIER Algorithm; CRYPT_BIT_BLOB PublicKey; } CERT_PUBLIC_KEY_INFO; typedef struct _CRYPT_ATTRIBUTE { LPSTR pszObjId; DWORD cValue; CRYPT_ATTR_BLOB *rgValue; } CRYPT_ATTRIBUTE; typedef struct _CRYPT_ATTRIBUTES { DWORD cAttr; CRYPT_ATTRIBUTE *rgAttr; } CRYPT_ATTRIBUTES; typedef struct _CRYPT_PRIVATE_KEY_INFO { DWORD Version; CRYPT_ALGORITHM_IDENTIFIER Algorithm; CRYPT_DER_BLOB PrivateKey; CRYPT_ATTRIBUTES *pAttributes; } CRYPT_PRIVATE_KEY_INFO; typedef struct _PUBLICKEYSTRUC { BYTE bType; BYTE bVersion; WORD reserved; ALG_ID aiKeyAlg; } BLOBHEADER, PUBLICKEYSTRUC; typedef struct _DSSPUBKEY { DWORD magic; DWORD bitlen; } DSSPUBKEY; typedef struct _DSSBLOBHEADER { PUBLICKEYSTRUC publickeystruc; DSSPUBKEY dsspubkey; } DSSBLOBHEADER; typedef struct _RSAPUBKEY { DWORD magic; DWORD bitlen; DWORD pubexp; } RSAPUBKEY; typedef struct _RSABLOBHEADER { PUBLICKEYSTRUC publickeystruc; RSAPUBKEY rsapubkey; } RSABLOBHEADER; typedef struct _PLAINTEXTKEYBLOB { BLOBHEADER hdr; DWORD dwKeySize; // rgbKeyData omitted since it is a flexible array member } PLAINTEXTKEYBLOB; typedef struct _DSSSEED { DWORD counter; BYTE seed[20]; } DSSSEED; BOOL CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pszContainer, LPCWSTR pszProvider, DWORD dwProvType, DWORD dwFlags); BOOL CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags); BOOL CryptImportKey(HCRYPTPROV hProv, BYTE *pbData, DWORD dwDataLen, HCRYPTKEY hPubKey, DWORD dwFlags, HCRYPTKEY *phKey); BOOL CryptGenKey(HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags, HCRYPTKEY *phKey); BOOL CryptGetKeyParam(HCRYPTKEY hKey, DWORD dwParam, BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags); BOOL CryptSetKeyParam(HCRYPTKEY hKey, DWORD dwParam, void *pbData, DWORD dwFlags); BOOL CryptExportKey(HCRYPTKEY hKey, HCRYPTKEY hExpKey, DWORD dwBlobType, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen); BOOL CryptDestroyKey(HCRYPTKEY hKey); BOOL CryptCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey, DWORD dwFlags, HCRYPTHASH *phHash); BOOL CryptHashData(HCRYPTHASH hHash, BYTE *pbData, DWORD dwDataLen, DWORD dwFlags); BOOL CryptSetHashParam(HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, DWORD dwFlags); BOOL CryptSignHashW(HCRYPTHASH hHash, DWORD dwKeySpec, LPCWSTR sDescription, DWORD dwFlags, BYTE *pbSignature, DWORD *pdwSigLen); BOOL CryptVerifySignatureW(HCRYPTHASH hHash, BYTE *pbSignature, DWORD dwSigLen, HCRYPTKEY hPubKey, LPCWSTR sDescription, DWORD dwFlags); BOOL CryptDestroyHash(HCRYPTHASH hHash); BOOL CryptEncrypt(HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen, DWORD dwBufLen); BOOL CryptDecrypt(HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen); """) try: advapi32 = ffi.dlopen('advapi32.dll') register_ffi(advapi32, ffi) except (OSError) as e: if str_cls(e).find('cannot load library') != -1: raise LibraryNotFoundError('advapi32.dll could not be found') raise def get_error(): return ffi.getwinerror() oscrypto-1.3.0/oscrypto/_win/_advapi32_ctypes.py000066400000000000000000000164761421476274700217600ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import ctypes from ctypes import windll, wintypes, POINTER, Structure, c_void_p, c_char_p, c_uint from ctypes.wintypes import BOOL, DWORD from .._ffi import FFIEngineError from .._types import str_cls from ..errors import LibraryNotFoundError __all__ = [ 'advapi32', 'get_error', ] try: advapi32 = windll.advapi32 except (OSError) as e: if str_cls(e).find('The specified module could not be found') != -1: raise LibraryNotFoundError('advapi32.dll could not be found') raise HCRYPTPROV = wintypes.HANDLE HCRYPTKEY = wintypes.HANDLE HCRYPTHASH = wintypes.HANDLE PBYTE = c_char_p ALG_ID = c_uint try: class CRYPTOAPI_BLOB(Structure): # noqa _fields_ = [ ("cbData", DWORD), ("pbData", POINTER(ctypes.c_byte)), ] CRYPT_INTEGER_BLOB = CRYPTOAPI_BLOB CRYPT_OBJID_BLOB = CRYPTOAPI_BLOB CRYPT_DER_BLOB = CRYPTOAPI_BLOB CRYPT_ATTR_BLOB = CRYPTOAPI_BLOB class CRYPT_ALGORITHM_IDENTIFIER(Structure): _fields = [ ('pszObjId', wintypes.LPSTR), ('Parameters', CRYPT_OBJID_BLOB), ] class CRYPT_BIT_BLOB(Structure): _fields_ = [ ('cbData', DWORD), ('pbData', PBYTE), ('cUnusedBits', DWORD), ] class CERT_PUBLIC_KEY_INFO(Structure): _fields_ = [ ('Algorithm', CRYPT_ALGORITHM_IDENTIFIER), ('PublicKey', CRYPT_BIT_BLOB), ] class CRYPT_ATTRIBUTE(Structure): _fields_ = [ ('pszObjId', wintypes.LPSTR), ('cValue', DWORD), ('rgValue', POINTER(CRYPT_ATTR_BLOB)), ] class CRYPT_ATTRIBUTES(Structure): _fields_ = [ ('cAttr', DWORD), ('rgAttr', POINTER(CRYPT_ATTRIBUTE)), ] class CRYPT_PRIVATE_KEY_INFO(Structure): _fields_ = [ ('Version', DWORD), ('Algorithm', CRYPT_ALGORITHM_IDENTIFIER), ('PrivateKey', CRYPT_DER_BLOB), ('pAttributes', POINTER(CRYPT_ATTRIBUTES)), ] class PUBLICKEYSTRUC(Structure): _fields_ = [ ('bType', wintypes.BYTE), ('bVersion', wintypes.BYTE), ('reserved', wintypes.WORD), ('aiKeyAlg', ALG_ID), ] BLOBHEADER = PUBLICKEYSTRUC class DSSPUBKEY(Structure): _fields_ = [ ('magic', DWORD), ('bitlen', DWORD), ] class DSSBLOBHEADER(Structure): _fields_ = [ ('publickeystruc', PUBLICKEYSTRUC), ('dsspubkey', DSSPUBKEY), ] class RSAPUBKEY(Structure): _fields_ = [ ('magic', DWORD), ('bitlen', DWORD), ('pubexp', DWORD), ] class RSABLOBHEADER(Structure): _fields_ = [ ('publickeystruc', PUBLICKEYSTRUC), ('rsapubkey', RSAPUBKEY), ] class PLAINTEXTKEYBLOB(Structure): _fields_ = [ ('hdr', BLOBHEADER), ('dwKeySize', DWORD), # rgbKeyData omitted since it is a flexible array member ] class DSSSEED(Structure): _fields_ = [ ('counter', DWORD), ('seed', wintypes.BYTE * 20), ] advapi32.CryptAcquireContextW.argtypes = [ POINTER(HCRYPTPROV), wintypes.LPCWSTR, wintypes.LPCWSTR, DWORD, DWORD ] advapi32.CryptAcquireContextW.restype = wintypes.BOOL advapi32.CryptReleaseContext.argtypes = [ HCRYPTPROV, DWORD ] advapi32.CryptReleaseContext.restype = wintypes.BOOL advapi32.CryptImportKey.argtypes = [ HCRYPTPROV, PBYTE, DWORD, HCRYPTKEY, DWORD, POINTER(HCRYPTKEY) ] advapi32.CryptImportKey.restype = BOOL advapi32.CryptGenKey.argtypes = [ HCRYPTPROV, ALG_ID, DWORD, POINTER(HCRYPTKEY) ] advapi32.CryptGenKey.restype = wintypes.BOOL advapi32.CryptGetKeyParam.argtypes = [ HCRYPTKEY, DWORD, PBYTE, POINTER(DWORD), DWORD ] advapi32.CryptGetKeyParam.restype = wintypes.BOOL advapi32.CryptSetKeyParam.argtypes = [ HCRYPTKEY, DWORD, c_void_p, DWORD ] advapi32.CryptSetKeyParam.restype = wintypes.BOOL advapi32.CryptExportKey.argtypes = [ HCRYPTKEY, HCRYPTKEY, DWORD, DWORD, PBYTE, POINTER(DWORD) ] advapi32.CryptExportKey.restype = BOOL advapi32.CryptDestroyKey.argtypes = [ HCRYPTKEY ] advapi32.CryptDestroyKey.restype = wintypes.BOOL advapi32.CryptCreateHash.argtypes = [ HCRYPTPROV, ALG_ID, HCRYPTKEY, DWORD, POINTER(HCRYPTHASH) ] advapi32.CryptCreateHash.restype = BOOL advapi32.CryptHashData.argtypes = [ HCRYPTHASH, PBYTE, DWORD, DWORD ] advapi32.CryptHashData.restype = BOOL advapi32.CryptSetHashParam.argtypes = [ HCRYPTHASH, DWORD, PBYTE, DWORD ] advapi32.CryptSetHashParam.restype = BOOL advapi32.CryptSignHashW.argtypes = [ HCRYPTHASH, DWORD, wintypes.LPCWSTR, DWORD, PBYTE, POINTER(DWORD) ] advapi32.CryptSignHashW.restype = BOOL advapi32.CryptVerifySignatureW.argtypes = [ HCRYPTHASH, PBYTE, DWORD, HCRYPTKEY, wintypes.LPCWSTR, DWORD ] advapi32.CryptVerifySignatureW.restype = BOOL advapi32.CryptDestroyHash.argtypes = [ HCRYPTHASH ] advapi32.CryptDestroyHash.restype = wintypes.BOOL advapi32.CryptEncrypt.argtypes = [ HCRYPTKEY, HCRYPTHASH, BOOL, DWORD, PBYTE, POINTER(DWORD), DWORD ] advapi32.CryptEncrypt.restype = BOOL advapi32.CryptDecrypt.argtypes = [ HCRYPTKEY, HCRYPTHASH, BOOL, DWORD, PBYTE, POINTER(DWORD) ] advapi32.CryptDecrypt.restype = BOOL except (AttributeError): raise FFIEngineError('Error initializing ctypes') setattr(advapi32, 'HCRYPTPROV', HCRYPTPROV) setattr(advapi32, 'HCRYPTKEY', HCRYPTKEY) setattr(advapi32, 'HCRYPTHASH', HCRYPTHASH) setattr(advapi32, 'CRYPT_INTEGER_BLOB', CRYPT_INTEGER_BLOB) setattr(advapi32, 'CRYPT_OBJID_BLOB', CRYPT_OBJID_BLOB) setattr(advapi32, 'CRYPT_DER_BLOB', CRYPT_DER_BLOB) setattr(advapi32, 'CRYPT_ATTR_BLOB', CRYPT_ATTR_BLOB) setattr(advapi32, 'CRYPT_ALGORITHM_IDENTIFIER', CRYPT_ALGORITHM_IDENTIFIER) setattr(advapi32, 'CRYPT_BIT_BLOB', CRYPT_BIT_BLOB) setattr(advapi32, 'CERT_PUBLIC_KEY_INFO', CERT_PUBLIC_KEY_INFO) setattr(advapi32, 'CRYPT_PRIVATE_KEY_INFO', CRYPT_PRIVATE_KEY_INFO) setattr(advapi32, 'CRYPT_ATTRIBUTE', CRYPT_ATTRIBUTE) setattr(advapi32, 'CRYPT_ATTRIBUTES', CRYPT_ATTRIBUTES) setattr(advapi32, 'PUBLICKEYSTRUC', PUBLICKEYSTRUC) setattr(advapi32, 'DSSPUBKEY', DSSPUBKEY) setattr(advapi32, 'DSSBLOBHEADER', DSSBLOBHEADER) setattr(advapi32, 'RSAPUBKEY', RSAPUBKEY) setattr(advapi32, 'RSABLOBHEADER', RSABLOBHEADER) setattr(advapi32, 'BLOBHEADER', BLOBHEADER) setattr(advapi32, 'PLAINTEXTKEYBLOB', PLAINTEXTKEYBLOB) setattr(advapi32, 'DSSSEED', DSSSEED) def get_error(): error = ctypes.GetLastError() return (error, ctypes.FormatError(error)) oscrypto-1.3.0/oscrypto/_win/_cng.py000066400000000000000000000106271421476274700175170ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .. import ffi from .._ffi import new, null, unwrap if ffi() == 'cffi': from ._cng_cffi import bcrypt else: from ._cng_ctypes import bcrypt __all__ = [ 'bcrypt', 'BcryptConst', 'close_alg_handle', 'handle_error', 'open_alg_handle', ] def open_alg_handle(constant, flags=0): handle_pointer = new(bcrypt, 'BCRYPT_ALG_HANDLE *') res = bcrypt.BCryptOpenAlgorithmProvider(handle_pointer, constant, null(), flags) handle_error(res) return unwrap(handle_pointer) def close_alg_handle(handle): res = bcrypt.BCryptCloseAlgorithmProvider(handle, 0) handle_error(res) def handle_error(error_num): """ Extracts the last Windows error message into a python unicode string :param error_num: The number to get the error string for :return: A unicode string error message """ if error_num == 0: return messages = { BcryptConst.STATUS_NOT_FOUND: 'The object was not found', BcryptConst.STATUS_INVALID_PARAMETER: 'An invalid parameter was passed to a service or function', BcryptConst.STATUS_NO_MEMORY: ( 'Not enough virtual memory or paging file quota is available to complete the specified operation' ), BcryptConst.STATUS_INVALID_HANDLE: 'An invalid HANDLE was specified', BcryptConst.STATUS_INVALID_SIGNATURE: 'The cryptographic signature is invalid', BcryptConst.STATUS_NOT_SUPPORTED: 'The request is not supported', BcryptConst.STATUS_BUFFER_TOO_SMALL: 'The buffer is too small to contain the entry', BcryptConst.STATUS_INVALID_BUFFER_SIZE: 'The size of the buffer is invalid for the specified operation', } output = 'NTSTATUS error 0x%0.2X' % error_num if error_num is not None and error_num in messages: output += ': ' + messages[error_num] raise OSError(output) class BcryptConst(): BCRYPT_RNG_ALGORITHM = 'RNG' BCRYPT_KEY_LENGTH = 'KeyLength' BCRYPT_EFFECTIVE_KEY_LENGTH = 'EffectiveKeyLength' BCRYPT_RSAPRIVATE_BLOB = 'RSAPRIVATEBLOB' BCRYPT_RSAFULLPRIVATE_BLOB = 'RSAFULLPRIVATEBLOB' BCRYPT_RSAPUBLIC_BLOB = 'RSAPUBLICBLOB' BCRYPT_DSA_PRIVATE_BLOB = 'DSAPRIVATEBLOB' BCRYPT_DSA_PUBLIC_BLOB = 'DSAPUBLICBLOB' BCRYPT_ECCPRIVATE_BLOB = 'ECCPRIVATEBLOB' BCRYPT_ECCPUBLIC_BLOB = 'ECCPUBLICBLOB' BCRYPT_RSAPUBLIC_MAGIC = 0x31415352 BCRYPT_RSAPRIVATE_MAGIC = 0x32415352 BCRYPT_RSAFULLPRIVATE_MAGIC = 0x33415352 BCRYPT_DSA_PUBLIC_MAGIC = 0x42505344 BCRYPT_DSA_PRIVATE_MAGIC = 0x56505344 BCRYPT_DSA_PUBLIC_MAGIC_V2 = 0x32425044 BCRYPT_DSA_PRIVATE_MAGIC_V2 = 0x32565044 DSA_HASH_ALGORITHM_SHA1 = 0 DSA_HASH_ALGORITHM_SHA256 = 1 DSA_HASH_ALGORITHM_SHA512 = 2 DSA_FIPS186_2 = 0 DSA_FIPS186_3 = 1 BCRYPT_NO_KEY_VALIDATION = 8 BCRYPT_ECDSA_PUBLIC_P256_MAGIC = 0x31534345 BCRYPT_ECDSA_PRIVATE_P256_MAGIC = 0x32534345 BCRYPT_ECDSA_PUBLIC_P384_MAGIC = 0x33534345 BCRYPT_ECDSA_PRIVATE_P384_MAGIC = 0x34534345 BCRYPT_ECDSA_PUBLIC_P521_MAGIC = 0x35534345 BCRYPT_ECDSA_PRIVATE_P521_MAGIC = 0x36534345 STATUS_SUCCESS = 0x00000000 STATUS_NOT_FOUND = 0xC0000225 STATUS_INVALID_PARAMETER = 0xC000000D STATUS_NO_MEMORY = 0xC0000017 STATUS_INVALID_HANDLE = 0xC0000008 STATUS_INVALID_SIGNATURE = 0xC000A000 STATUS_NOT_SUPPORTED = 0xC00000BB STATUS_BUFFER_TOO_SMALL = 0xC0000023 STATUS_INVALID_BUFFER_SIZE = 0xC0000206 BCRYPT_KEY_DATA_BLOB_MAGIC = 0x4d42444b BCRYPT_KEY_DATA_BLOB_VERSION1 = 0x00000001 BCRYPT_KEY_DATA_BLOB = 'KeyDataBlob' BCRYPT_PAD_PKCS1 = 0x00000002 BCRYPT_PAD_OAEP = 0x00000004 BCRYPT_PAD_PSS = 0x00000008 BCRYPT_3DES_ALGORITHM = '3DES' BCRYPT_3DES_112_ALGORITHM = '3DES_112' BCRYPT_AES_ALGORITHM = 'AES' BCRYPT_DES_ALGORITHM = 'DES' BCRYPT_RC2_ALGORITHM = 'RC2' BCRYPT_RC4_ALGORITHM = 'RC4' BCRYPT_DSA_ALGORITHM = 'DSA' BCRYPT_ECDSA_P256_ALGORITHM = 'ECDSA_P256' BCRYPT_ECDSA_P384_ALGORITHM = 'ECDSA_P384' BCRYPT_ECDSA_P521_ALGORITHM = 'ECDSA_P521' BCRYPT_RSA_ALGORITHM = 'RSA' BCRYPT_MD5_ALGORITHM = 'MD5' BCRYPT_SHA1_ALGORITHM = 'SHA1' BCRYPT_SHA256_ALGORITHM = 'SHA256' BCRYPT_SHA384_ALGORITHM = 'SHA384' BCRYPT_SHA512_ALGORITHM = 'SHA512' BCRYPT_ALG_HANDLE_HMAC_FLAG = 0x00000008 BCRYPT_BLOCK_PADDING = 0x00000001 oscrypto-1.3.0/oscrypto/_win/_cng_cffi.py000066400000000000000000000105761421476274700205110ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .._ffi import register_ffi from .._types import str_cls from ..errors import LibraryNotFoundError from cffi import FFI __all__ = [ 'bcrypt', ] ffi = FFI() ffi.cdef(""" typedef HANDLE BCRYPT_ALG_HANDLE; typedef HANDLE BCRYPT_KEY_HANDLE; typedef ULONG NTSTATUS; typedef unsigned char *PUCHAR; typedef unsigned char *PBYTE; typedef struct _BCRYPT_RSAKEY_BLOB { ULONG Magic; ULONG BitLength; ULONG cbPublicExp; ULONG cbModulus; ULONG cbPrime1; ULONG cbPrime2; } BCRYPT_RSAKEY_BLOB; typedef struct _BCRYPT_DSA_KEY_BLOB { ULONG dwMagic; ULONG cbKey; UCHAR Count[4]; UCHAR Seed[20]; UCHAR q[20]; } BCRYPT_DSA_KEY_BLOB; typedef struct _BCRYPT_DSA_KEY_BLOB_V2 { ULONG dwMagic; ULONG cbKey; INT hashAlgorithm; INT standardVersion; ULONG cbSeedLength; ULONG cbGroupSize; UCHAR Count[4]; } BCRYPT_DSA_KEY_BLOB_V2; typedef struct _BCRYPT_ECCKEY_BLOB { ULONG dwMagic; ULONG cbKey; } BCRYPT_ECCKEY_BLOB; typedef struct _BCRYPT_PKCS1_PADDING_INFO { LPCWSTR pszAlgId; } BCRYPT_PKCS1_PADDING_INFO; typedef struct _BCRYPT_PSS_PADDING_INFO { LPCWSTR pszAlgId; ULONG cbSalt; } BCRYPT_PSS_PADDING_INFO; typedef struct _BCRYPT_OAEP_PADDING_INFO { LPCWSTR pszAlgId; PUCHAR pbLabel; ULONG cbLabel; } BCRYPT_OAEP_PADDING_INFO; typedef struct _BCRYPT_KEY_DATA_BLOB_HEADER { ULONG dwMagic; ULONG dwVersion; ULONG cbKeyData; } BCRYPT_KEY_DATA_BLOB_HEADER; NTSTATUS BCryptOpenAlgorithmProvider(BCRYPT_ALG_HANDLE *phAlgorithm, LPCWSTR pszAlgId, LPCWSTR pszImplementation, DWORD dwFlags); NTSTATUS BCryptCloseAlgorithmProvider(BCRYPT_ALG_HANDLE hAlgorithm, DWORD dwFlags); NTSTATUS BCryptSetProperty(HANDLE hObject, LPCWSTR pszProperty, ULONG *pbInput, ULONG cbInput, ULONG dwFlags); NTSTATUS BCryptImportKeyPair(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_KEY_HANDLE hImportKey, LPCWSTR pszBlobType, BCRYPT_KEY_HANDLE *phKey, PUCHAR pbInput, ULONG cbInput, ULONG dwFlags); NTSTATUS BCryptImportKey(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_KEY_HANDLE hImportKey, LPCWSTR pszBlobType, BCRYPT_KEY_HANDLE *phKey, PUCHAR pbKeyObject, ULONG cbKeyObject, PUCHAR pbInput, ULONG cbInput, ULONG dwFlags); NTSTATUS BCryptDestroyKey(BCRYPT_KEY_HANDLE hKey); NTSTATUS BCryptVerifySignature(BCRYPT_KEY_HANDLE hKey, void *pPaddingInfo, PUCHAR pbHash, ULONG cbHash, PUCHAR pbSignature, ULONG cbSignature, ULONG dwFlags); NTSTATUS BCryptSignHash(BCRYPT_KEY_HANDLE hKey, void * pPaddingInfo, PBYTE pbInput, DWORD cbInput, PBYTE pbOutput, DWORD cbOutput, DWORD *pcbResult, ULONG dwFlags); NTSTATUS BCryptEncrypt(BCRYPT_KEY_HANDLE hKey, PUCHAR pbInput, ULONG cbInput, void *pPaddingInfo, PUCHAR pbIV, ULONG cbIV, PUCHAR pbOutput, ULONG cbOutput, ULONG *pcbResult, ULONG dwFlags); NTSTATUS BCryptDecrypt(BCRYPT_KEY_HANDLE hKey, PUCHAR pbInput, ULONG cbInput, void *pPaddingInfo, PUCHAR pbIV, ULONG cbIV, PUCHAR pbOutput, ULONG cbOutput, ULONG *pcbResult, ULONG dwFlags); NTSTATUS BCryptDeriveKeyPBKDF2(BCRYPT_ALG_HANDLE hPrf, PUCHAR pbPassword, ULONG cbPassword, PUCHAR pbSalt, ULONG cbSalt, ULONGLONG cIterations, PUCHAR pbDerivedKey, ULONG cbDerivedKey, ULONG dwFlags); NTSTATUS BCryptGenRandom(BCRYPT_ALG_HANDLE hAlgorithm, PUCHAR pbBuffer, ULONG cbBuffer, ULONG dwFlags); NTSTATUS BCryptGenerateKeyPair(BCRYPT_ALG_HANDLE hAlgorithm, BCRYPT_KEY_HANDLE *phKey, ULONG dwLength, ULONG dwFlags); NTSTATUS BCryptFinalizeKeyPair(BCRYPT_KEY_HANDLE hKey, ULONG dwFlags); NTSTATUS BCryptExportKey(BCRYPT_KEY_HANDLE hKey, BCRYPT_KEY_HANDLE hExportKey, LPCWSTR pszBlobType, PUCHAR pbOutput, ULONG cbOutput, ULONG *pcbResult, ULONG dwFlags); """) try: bcrypt = ffi.dlopen('bcrypt.dll') register_ffi(bcrypt, ffi) except (OSError) as e: if str_cls(e).find('cannot load library') != -1: raise LibraryNotFoundError('bcrypt.dll could not be found - Windows XP and Server 2003 are not supported') raise oscrypto-1.3.0/oscrypto/_win/_cng_ctypes.py000066400000000000000000000140131421476274700210770ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from ctypes import windll, wintypes, POINTER, Structure, c_void_p, c_ulonglong, c_char_p, c_byte from ctypes.wintypes import ULONG, DWORD, LPCWSTR from .._ffi import FFIEngineError from .._types import str_cls from ..errors import LibraryNotFoundError __all__ = [ 'bcrypt', ] try: bcrypt = windll.bcrypt except (OSError) as e: if str_cls(e).find('The specified module could not be found') != -1: raise LibraryNotFoundError('bcrypt.dll could not be found - Windows XP and Server 2003 are not supported') raise BCRYPT_ALG_HANDLE = wintypes.HANDLE BCRYPT_KEY_HANDLE = wintypes.HANDLE NTSTATUS = wintypes.ULONG PUCHAR = c_char_p PBYTE = c_char_p try: bcrypt.BCryptOpenAlgorithmProvider.argtypes = [ POINTER(BCRYPT_ALG_HANDLE), LPCWSTR, LPCWSTR, DWORD ] bcrypt.BCryptOpenAlgorithmProvider.restype = NTSTATUS bcrypt.BCryptCloseAlgorithmProvider.argtypes = [ BCRYPT_ALG_HANDLE, ULONG ] bcrypt.BCryptCloseAlgorithmProvider.restype = NTSTATUS bcrypt.BCryptImportKeyPair.argtypes = [ BCRYPT_ALG_HANDLE, BCRYPT_KEY_HANDLE, LPCWSTR, POINTER(BCRYPT_KEY_HANDLE), PUCHAR, ULONG, ULONG ] bcrypt.BCryptImportKeyPair.restype = NTSTATUS bcrypt.BCryptImportKey.argtypes = [ BCRYPT_ALG_HANDLE, BCRYPT_KEY_HANDLE, LPCWSTR, POINTER(BCRYPT_KEY_HANDLE), PUCHAR, ULONG, PUCHAR, ULONG, ULONG ] bcrypt.BCryptImportKey.restype = NTSTATUS bcrypt.BCryptDestroyKey.argtypes = [ BCRYPT_KEY_HANDLE ] bcrypt.BCryptDestroyKey.restype = NTSTATUS bcrypt.BCryptVerifySignature.argtypes = [ BCRYPT_KEY_HANDLE, c_void_p, PUCHAR, ULONG, PUCHAR, ULONG, ULONG ] bcrypt.BCryptVerifySignature.restype = NTSTATUS bcrypt.BCryptSignHash.argtypes = [ BCRYPT_KEY_HANDLE, c_void_p, PBYTE, DWORD, PBYTE, DWORD, POINTER(DWORD), ULONG ] bcrypt.BCryptSignHash.restype = NTSTATUS bcrypt.BCryptSetProperty.argtypes = [ BCRYPT_KEY_HANDLE, LPCWSTR, c_void_p, ULONG, ULONG ] bcrypt.BCryptSetProperty.restype = NTSTATUS bcrypt.BCryptEncrypt.argtypes = [ BCRYPT_KEY_HANDLE, PUCHAR, ULONG, c_void_p, PUCHAR, ULONG, PUCHAR, ULONG, POINTER(ULONG), ULONG ] bcrypt.BCryptEncrypt.restype = NTSTATUS bcrypt.BCryptDecrypt.argtypes = [ BCRYPT_KEY_HANDLE, PUCHAR, ULONG, c_void_p, PUCHAR, ULONG, PUCHAR, ULONG, POINTER(ULONG), ULONG ] bcrypt.BCryptDecrypt.restype = NTSTATUS bcrypt.BCryptDeriveKeyPBKDF2.argtypes = [ BCRYPT_ALG_HANDLE, PUCHAR, ULONG, PUCHAR, ULONG, c_ulonglong, PUCHAR, ULONG, ULONG ] bcrypt.BCryptDeriveKeyPBKDF2.restype = NTSTATUS bcrypt.BCryptGenRandom.argtypes = [ BCRYPT_ALG_HANDLE, PUCHAR, ULONG, ULONG ] bcrypt.BCryptGenRandom.restype = NTSTATUS bcrypt.BCryptGenerateKeyPair.argtypes = [ BCRYPT_ALG_HANDLE, POINTER(BCRYPT_KEY_HANDLE), ULONG, ULONG ] bcrypt.BCryptGenerateKeyPair.restype = NTSTATUS bcrypt.BCryptFinalizeKeyPair.argtypes = [ BCRYPT_KEY_HANDLE, ULONG ] bcrypt.BCryptFinalizeKeyPair.restype = NTSTATUS bcrypt.BCryptExportKey.argtypes = [ BCRYPT_KEY_HANDLE, BCRYPT_KEY_HANDLE, LPCWSTR, PUCHAR, ULONG, POINTER(ULONG), ULONG ] bcrypt.BCryptExportKey.restype = NTSTATUS except (AttributeError): raise FFIEngineError('Error initializing ctypes') class BCRYPT_RSAKEY_BLOB(Structure): # noqa _fields_ = [ ('Magic', ULONG), ('BitLength', ULONG), ('cbPublicExp', ULONG), ('cbModulus', ULONG), ('cbPrime1', ULONG), ('cbPrime2', ULONG), ] class BCRYPT_DSA_KEY_BLOB(Structure): # noqa _fields_ = [ ('dwMagic', ULONG), ('cbKey', ULONG), ('Count', c_byte * 4), ('Seed', c_byte * 20), ('q', c_byte * 20), ] class BCRYPT_DSA_KEY_BLOB_V2(Structure): # noqa _fields_ = [ ('dwMagic', ULONG), ('cbKey', ULONG), ('hashAlgorithm', wintypes.INT), ('standardVersion', wintypes.INT), ('cbSeedLength', ULONG), ('cbGroupSize', ULONG), ('Count', c_byte * 4), ] class BCRYPT_ECCKEY_BLOB(Structure): # noqa _fields_ = [ ('dwMagic', ULONG), ('cbKey', ULONG), ] class BCRYPT_PKCS1_PADDING_INFO(Structure): # noqa _fields_ = [ ('pszAlgId', LPCWSTR), ] class BCRYPT_PSS_PADDING_INFO(Structure): # noqa _fields_ = [ ('pszAlgId', LPCWSTR), ('cbSalt', ULONG), ] class BCRYPT_OAEP_PADDING_INFO(Structure): # noqa _fields_ = [ ('pszAlgId', LPCWSTR), ('pbLabel', PUCHAR), ('cbLabel', ULONG), ] class BCRYPT_KEY_DATA_BLOB_HEADER(Structure): # noqa _fields_ = [ ('dwMagic', ULONG), ('dwVersion', ULONG), ('cbKeyData', ULONG), ] setattr(bcrypt, 'BCRYPT_ALG_HANDLE', BCRYPT_ALG_HANDLE) setattr(bcrypt, 'BCRYPT_KEY_HANDLE', BCRYPT_KEY_HANDLE) setattr(bcrypt, 'BCRYPT_RSAKEY_BLOB', BCRYPT_RSAKEY_BLOB) setattr(bcrypt, 'BCRYPT_DSA_KEY_BLOB', BCRYPT_DSA_KEY_BLOB) setattr(bcrypt, 'BCRYPT_DSA_KEY_BLOB_V2', BCRYPT_DSA_KEY_BLOB_V2) setattr(bcrypt, 'BCRYPT_ECCKEY_BLOB', BCRYPT_ECCKEY_BLOB) setattr(bcrypt, 'BCRYPT_PKCS1_PADDING_INFO', BCRYPT_PKCS1_PADDING_INFO) setattr(bcrypt, 'BCRYPT_PSS_PADDING_INFO', BCRYPT_PSS_PADDING_INFO) setattr(bcrypt, 'BCRYPT_OAEP_PADDING_INFO', BCRYPT_OAEP_PADDING_INFO) setattr(bcrypt, 'BCRYPT_KEY_DATA_BLOB_HEADER', BCRYPT_KEY_DATA_BLOB_HEADER) oscrypto-1.3.0/oscrypto/_win/_crypt32.py000066400000000000000000000035511421476274700202540ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .. import ffi from ._decode import _try_decode from .._ffi import buffer_from_bytes from .._types import str_cls if ffi() == 'cffi': from ._crypt32_cffi import crypt32, get_error else: from ._crypt32_ctypes import crypt32, get_error __all__ = [ 'crypt32', 'Crypt32Const', 'handle_error', ] def handle_error(result): """ Extracts the last Windows error message into a python unicode string :param result: A function result, 0 or None indicates failure :return: A unicode string error message """ if result: return _, error_string = get_error() if not isinstance(error_string, str_cls): error_string = _try_decode(error_string) raise OSError(error_string) class Crypt32Const(): X509_ASN_ENCODING = 1 ERROR_INSUFFICIENT_BUFFER = 122 CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG = 0x4 CRYPT_E_NOT_FOUND = -2146885628 CERT_STORE_PROV_MEMORY = b'Memory' CERT_STORE_CREATE_NEW_FLAG = 0x00002000 CERT_STORE_ADD_USE_EXISTING = 2 USAGE_MATCH_TYPE_OR = 1 CERT_CHAIN_POLICY_SSL = 4 AUTHTYPE_SERVER = 2 CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG = 0x00000010 CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS = 0x00000F00 CERT_CHAIN_CACHE_END_CERT = 1 CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY = 0x80000000 TRUST_E_CERT_SIGNATURE = 0x80096004 CERT_E_EXPIRED = 0x800B0101 CERT_E_ROLE = 0x800B0103 CERT_E_PURPOSE = 0x800B0106 CERT_E_UNTRUSTEDROOT = 0x800B0109 CERT_E_CN_NO_MATCH = 0x800B010F CRYPT_E_REVOKED = 0x80092010 PKIX_KP_SERVER_AUTH = buffer_from_bytes(b"1.3.6.1.5.5.7.3.1\x00") SERVER_GATED_CRYPTO = buffer_from_bytes(b"1.3.6.1.4.1.311.10.3.3\x00") SGC_NETSCAPE = buffer_from_bytes(b"2.16.840.1.113730.4.1\x00") oscrypto-1.3.0/oscrypto/_win/_crypt32_cffi.py000066400000000000000000000134401421476274700212410ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys from .._ffi import register_ffi from .._types import str_cls from ..errors import LibraryNotFoundError import cffi __all__ = [ 'crypt32', 'get_error', ] ffi = cffi.FFI() if cffi.__version_info__ >= (0, 9): ffi.set_unicode(True) if sys.maxsize > 2 ** 32: ffi.cdef("typedef uint64_t ULONG_PTR;") else: ffi.cdef("typedef unsigned long ULONG_PTR;") ffi.cdef(""" typedef HANDLE HCERTSTORE; typedef unsigned char *PBYTE; typedef struct _CRYPTOAPI_BLOB { DWORD cbData; PBYTE pbData; } CRYPTOAPI_BLOB; typedef CRYPTOAPI_BLOB CRYPT_INTEGER_BLOB; typedef CRYPTOAPI_BLOB CERT_NAME_BLOB; typedef CRYPTOAPI_BLOB CRYPT_BIT_BLOB; typedef CRYPTOAPI_BLOB CRYPT_OBJID_BLOB; typedef struct _CRYPT_ALGORITHM_IDENTIFIER { LPSTR pszObjId; CRYPT_OBJID_BLOB Parameters; } CRYPT_ALGORITHM_IDENTIFIER; typedef struct _FILETIME { DWORD dwLowDateTime; DWORD dwHighDateTime; } FILETIME; typedef struct _CERT_PUBLIC_KEY_INFO { CRYPT_ALGORITHM_IDENTIFIER Algorithm; CRYPT_BIT_BLOB PublicKey; } CERT_PUBLIC_KEY_INFO; typedef struct _CERT_EXTENSION { LPSTR pszObjId; BOOL fCritical; CRYPT_OBJID_BLOB Value; } CERT_EXTENSION, *PCERT_EXTENSION; typedef struct _CERT_INFO { DWORD dwVersion; CRYPT_INTEGER_BLOB SerialNumber; CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm; CERT_NAME_BLOB Issuer; FILETIME NotBefore; FILETIME NotAfter; CERT_NAME_BLOB Subject; CERT_PUBLIC_KEY_INFO SubjectPublicKeyInfo; CRYPT_BIT_BLOB IssuerUniqueId; CRYPT_BIT_BLOB SubjectUniqueId; DWORD cExtension; PCERT_EXTENSION *rgExtension; } CERT_INFO, *PCERT_INFO; typedef struct _CERT_CONTEXT { DWORD dwCertEncodingType; PBYTE pbCertEncoded; DWORD cbCertEncoded; PCERT_INFO pCertInfo; HCERTSTORE hCertStore; } CERT_CONTEXT, *PCERT_CONTEXT; typedef struct _CERT_TRUST_STATUS { DWORD dwErrorStatus; DWORD dwInfoStatus; } CERT_TRUST_STATUS, *PCERT_TRUST_STATUS; typedef struct _CERT_ENHKEY_USAGE { DWORD cUsageIdentifier; LPSTR *rgpszUsageIdentifier; } CERT_ENHKEY_USAGE, *PCERT_ENHKEY_USAGE; typedef struct _CERT_CHAIN_ELEMENT { DWORD cbSize; PCERT_CONTEXT pCertContext; CERT_TRUST_STATUS TrustStatus; void *pRevocationInfo; PCERT_ENHKEY_USAGE pIssuanceUsage; PCERT_ENHKEY_USAGE pApplicationUsage; LPCWSTR pwszExtendedErrorInfo; } CERT_CHAIN_ELEMENT, *PCERT_CHAIN_ELEMENT; typedef struct _CERT_SIMPLE_CHAIN { DWORD cbSize; CERT_TRUST_STATUS TrustStatus; DWORD cElement; PCERT_CHAIN_ELEMENT *rgpElement; void *pTrustListInfo; BOOL fHasRevocationFreshnessTime; DWORD dwRevocationFreshnessTime; } CERT_SIMPLE_CHAIN, *PCERT_SIMPLE_CHAIN; typedef struct _CERT_CHAIN_CONTEXT { DWORD cbSize; CERT_TRUST_STATUS TrustStatus; DWORD cChain; PCERT_SIMPLE_CHAIN *rgpChain; DWORD cLowerQualityChainContext; void *rgpLowerQualityChainContext; BOOL fHasRevocationFreshnessTime; DWORD dwRevocationFreshnessTime; } CERT_CHAIN_CONTEXT, *PCERT_CHAIN_CONTEXT; typedef struct _CERT_USAGE_MATCH { DWORD dwType; CERT_ENHKEY_USAGE Usage; } CERT_USAGE_MATCH; typedef struct _CERT_CHAIN_PARA { DWORD cbSize; CERT_USAGE_MATCH RequestedUsage; } CERT_CHAIN_PARA; typedef struct _CERT_CHAIN_POLICY_PARA { DWORD cbSize; DWORD dwFlags; void *pvExtraPolicyPara; } CERT_CHAIN_POLICY_PARA; typedef struct _HTTPSPolicyCallbackData { DWORD cbSize; DWORD dwAuthType; DWORD fdwChecks; WCHAR *pwszServerName; } SSL_EXTRA_CERT_CHAIN_POLICY_PARA; typedef struct _CERT_CHAIN_POLICY_STATUS { DWORD cbSize; DWORD dwError; LONG lChainIndex; LONG lElementIndex; void *pvExtraPolicyStatus; } CERT_CHAIN_POLICY_STATUS; typedef HANDLE HCERTCHAINENGINE; typedef HANDLE HCRYPTPROV; HCERTSTORE CertOpenStore(LPCSTR lpszStoreProvider, DWORD dwMsgAndCertEncodingType, HCRYPTPROV hCryptProv, DWORD dwFlags, void *pvPara); BOOL CertAddEncodedCertificateToStore(HCERTSTORE hCertStore, DWORD dwCertEncodingType, BYTE *pbCertEncoded, DWORD cbCertEncoded, DWORD dwAddDisposition, PCERT_CONTEXT *ppCertContext); BOOL CertGetCertificateChain(HCERTCHAINENGINE hChainEngine, CERT_CONTEXT *pCertContext, FILETIME *pTime, HCERTSTORE hAdditionalStore, CERT_CHAIN_PARA *pChainPara, DWORD dwFlags, void *pvReserved, PCERT_CHAIN_CONTEXT *ppChainContext); BOOL CertVerifyCertificateChainPolicy(ULONG_PTR pszPolicyOID, PCERT_CHAIN_CONTEXT pChainContext, CERT_CHAIN_POLICY_PARA *pPolicyPara, CERT_CHAIN_POLICY_STATUS *pPolicyStatus); void CertFreeCertificateChain(PCERT_CHAIN_CONTEXT pChainContext); HCERTSTORE CertOpenSystemStoreW(HANDLE hprov, LPCWSTR szSubsystemProtocol); PCERT_CONTEXT CertEnumCertificatesInStore(HCERTSTORE hCertStore, CERT_CONTEXT *pPrevCertContext); BOOL CertCloseStore(HCERTSTORE hCertStore, DWORD dwFlags); BOOL CertGetEnhancedKeyUsage(CERT_CONTEXT *pCertContext, DWORD dwFlags, CERT_ENHKEY_USAGE *pUsage, DWORD *pcbUsage); """) try: crypt32 = ffi.dlopen('crypt32.dll') register_ffi(crypt32, ffi) except (OSError) as e: if str_cls(e).find('cannot load library') != -1: raise LibraryNotFoundError('crypt32.dll could not be found') raise def get_error(): return ffi.getwinerror() oscrypto-1.3.0/oscrypto/_win/_crypt32_ctypes.py000066400000000000000000000200341421476274700216360ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys import ctypes from ctypes import windll, wintypes, POINTER, Structure, c_void_p, c_char_p from ctypes.wintypes import DWORD from .._ffi import FFIEngineError from .._types import str_cls from ..errors import LibraryNotFoundError from ._kernel32 import kernel32 __all__ = [ 'crypt32', 'get_error', ] try: crypt32 = windll.crypt32 except (OSError) as e: if str_cls(e).find('The specified module could not be found') != -1: raise LibraryNotFoundError('crypt32.dll could not be found') raise HCERTSTORE = wintypes.HANDLE HCERTCHAINENGINE = wintypes.HANDLE HCRYPTPROV = wintypes.HANDLE HCRYPTKEY = wintypes.HANDLE PBYTE = c_char_p if sys.maxsize > 2 ** 32: ULONG_PTR = ctypes.c_uint64 else: ULONG_PTR = ctypes.c_ulong try: class CRYPTOAPI_BLOB(Structure): # noqa _fields_ = [ ("cbData", DWORD), ("pbData", c_void_p), ] CRYPT_INTEGER_BLOB = CRYPTOAPI_BLOB CERT_NAME_BLOB = CRYPTOAPI_BLOB CRYPT_BIT_BLOB = CRYPTOAPI_BLOB CRYPT_OBJID_BLOB = CRYPTOAPI_BLOB class CRYPT_ALGORITHM_IDENTIFIER(Structure): # noqa _fields_ = [ ("pszObjId", wintypes.LPSTR), ("Parameters", CRYPT_OBJID_BLOB), ] class CERT_PUBLIC_KEY_INFO(Structure): # noqa _fields_ = [ ("Algorithm", CRYPT_ALGORITHM_IDENTIFIER), ("PublicKey", CRYPT_BIT_BLOB), ] class CERT_EXTENSION(Structure): # noqa _fields_ = [ ("pszObjId", wintypes.LPSTR), ("fCritical", wintypes.BOOL), ("Value", CRYPT_OBJID_BLOB), ] PCERT_EXTENSION = POINTER(CERT_EXTENSION) class CERT_INFO(Structure): # noqa _fields_ = [ ("dwVersion", DWORD), ("SerialNumber", CRYPT_INTEGER_BLOB), ("SignatureAlgorithm", CRYPT_ALGORITHM_IDENTIFIER), ("Issuer", CERT_NAME_BLOB), ("NotBefore", kernel32.FILETIME), ("NotAfter", kernel32.FILETIME), ("Subject", CERT_NAME_BLOB), ("SubjectPublicKeyInfo", CERT_PUBLIC_KEY_INFO), ("IssuerUniqueId", CRYPT_BIT_BLOB), ("SubjectUniqueId", CRYPT_BIT_BLOB), ("cExtension", DWORD), ("rgExtension", POINTER(PCERT_EXTENSION)), ] PCERT_INFO = POINTER(CERT_INFO) class CERT_CONTEXT(Structure): # noqa _fields_ = [ ("dwCertEncodingType", DWORD), ("pbCertEncoded", c_void_p), ("cbCertEncoded", DWORD), ("pCertInfo", PCERT_INFO), ("hCertStore", HCERTSTORE) ] PCERT_CONTEXT = POINTER(CERT_CONTEXT) class CERT_ENHKEY_USAGE(Structure): # noqa _fields_ = [ ('cUsageIdentifier', DWORD), ('rgpszUsageIdentifier', POINTER(POINTER(wintypes.BYTE))), ] PCERT_ENHKEY_USAGE = POINTER(CERT_ENHKEY_USAGE) class CERT_TRUST_STATUS(Structure): # noqa _fields_ = [ ('dwErrorStatus', DWORD), ('dwInfoStatus', DWORD), ] class CERT_CHAIN_ELEMENT(Structure): # noqa _fields_ = [ ('cbSize', DWORD), ('pCertContext', PCERT_CONTEXT), ('TrustStatus', CERT_TRUST_STATUS), ('pRevocationInfo', c_void_p), ('pIssuanceUsage', PCERT_ENHKEY_USAGE), ('pApplicationUsage', PCERT_ENHKEY_USAGE), ('pwszExtendedErrorInfo', wintypes.LPCWSTR), ] PCERT_CHAIN_ELEMENT = POINTER(CERT_CHAIN_ELEMENT) class CERT_SIMPLE_CHAIN(Structure): # noqa _fields_ = [ ('cbSize', DWORD), ('TrustStatus', CERT_TRUST_STATUS), ('cElement', DWORD), ('rgpElement', POINTER(PCERT_CHAIN_ELEMENT)), ('pTrustListInfo', c_void_p), ('fHasRevocationFreshnessTime', wintypes.BOOL), ('dwRevocationFreshnessTime', DWORD), ] PCERT_SIMPLE_CHAIN = POINTER(CERT_SIMPLE_CHAIN) class CERT_CHAIN_CONTEXT(Structure): # noqa _fields_ = [ ('cbSize', DWORD), ('TrustStatus', CERT_TRUST_STATUS), ('cChain', DWORD), ('rgpChain', POINTER(PCERT_SIMPLE_CHAIN)), ('cLowerQualityChainContext', DWORD), ('rgpLowerQualityChainContext', c_void_p), ('fHasRevocationFreshnessTime', wintypes.BOOL), ('dwRevocationFreshnessTime', DWORD), ] PCERT_CHAIN_CONTEXT = POINTER(CERT_CHAIN_CONTEXT) class CERT_USAGE_MATCH(Structure): # noqa _fields_ = [ ('dwType', DWORD), ('Usage', CERT_ENHKEY_USAGE), ] class CERT_CHAIN_PARA(Structure): # noqa _fields_ = [ ('cbSize', DWORD), ('RequestedUsage', CERT_USAGE_MATCH), ] class CERT_CHAIN_POLICY_PARA(Structure): # noqa _fields_ = [ ('cbSize', DWORD), ('dwFlags', DWORD), ('pvExtraPolicyPara', c_void_p), ] class SSL_EXTRA_CERT_CHAIN_POLICY_PARA(Structure): # noqa _fields_ = [ ('cbSize', DWORD), ('dwAuthType', DWORD), ('fdwChecks', DWORD), ('pwszServerName', wintypes.LPCWSTR), ] class CERT_CHAIN_POLICY_STATUS(Structure): # noqa _fields_ = [ ('cbSize', DWORD), ('dwError', DWORD), ('lChainIndex', wintypes.LONG), ('lElementIndex', wintypes.LONG), ('pvExtraPolicyStatus', c_void_p), ] crypt32.CertOpenStore.argtypes = [ wintypes.LPCSTR, DWORD, HCRYPTPROV, DWORD, c_void_p ] crypt32.CertOpenStore.restype = HCERTSTORE crypt32.CertAddEncodedCertificateToStore.argtypes = [ HCERTSTORE, DWORD, PBYTE, DWORD, DWORD, POINTER(PCERT_CONTEXT) ] crypt32.CertAddEncodedCertificateToStore.restype = wintypes.BOOL crypt32.CertGetCertificateChain.argtypes = [ HCERTCHAINENGINE, PCERT_CONTEXT, POINTER(kernel32.FILETIME), HCERTSTORE, POINTER(CERT_CHAIN_PARA), DWORD, c_void_p, POINTER(PCERT_CHAIN_CONTEXT) ] crypt32.CertGetCertificateChain.restype = wintypes.BOOL crypt32.CertVerifyCertificateChainPolicy.argtypes = [ ULONG_PTR, PCERT_CHAIN_CONTEXT, POINTER(CERT_CHAIN_POLICY_PARA), POINTER(CERT_CHAIN_POLICY_STATUS) ] crypt32.CertVerifyCertificateChainPolicy.restype = wintypes.BOOL crypt32.CertFreeCertificateChain.argtypes = [ PCERT_CHAIN_CONTEXT ] crypt32.CertFreeCertificateChain.restype = None crypt32.CertOpenSystemStoreW.argtypes = [ wintypes.HANDLE, wintypes.LPCWSTR ] crypt32.CertOpenSystemStoreW.restype = HCERTSTORE crypt32.CertEnumCertificatesInStore.argtypes = [ HCERTSTORE, PCERT_CONTEXT ] crypt32.CertEnumCertificatesInStore.restype = PCERT_CONTEXT crypt32.CertCloseStore.argtypes = [ HCERTSTORE, DWORD ] crypt32.CertCloseStore.restype = wintypes.BOOL crypt32.CertGetEnhancedKeyUsage.argtypes = [ PCERT_CONTEXT, DWORD, c_void_p, POINTER(DWORD) ] crypt32.CertGetEnhancedKeyUsage.restype = wintypes.BOOL except (AttributeError): raise FFIEngineError('Error initializing ctypes') setattr(crypt32, 'FILETIME', kernel32.FILETIME) setattr(crypt32, 'CERT_ENHKEY_USAGE', CERT_ENHKEY_USAGE) setattr(crypt32, 'CERT_CONTEXT', CERT_CONTEXT) setattr(crypt32, 'PCERT_CONTEXT', PCERT_CONTEXT) setattr(crypt32, 'CERT_USAGE_MATCH', CERT_USAGE_MATCH) setattr(crypt32, 'CERT_CHAIN_PARA', CERT_CHAIN_PARA) setattr(crypt32, 'CERT_CHAIN_POLICY_PARA', CERT_CHAIN_POLICY_PARA) setattr(crypt32, 'SSL_EXTRA_CERT_CHAIN_POLICY_PARA', SSL_EXTRA_CERT_CHAIN_POLICY_PARA) setattr(crypt32, 'CERT_CHAIN_POLICY_STATUS', CERT_CHAIN_POLICY_STATUS) setattr(crypt32, 'PCERT_CHAIN_CONTEXT', PCERT_CHAIN_CONTEXT) def get_error(): error = ctypes.GetLastError() return (error, ctypes.FormatError(error)) oscrypto-1.3.0/oscrypto/_win/_decode.py000066400000000000000000000016211421476274700201650ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import locale from .._types import str_cls _encoding = locale.getpreferredencoding() _fallback_encodings = ['utf-8', 'cp1252'] def _try_decode(byte_string): """ Tries decoding a byte string from the OS into a unicode string :param byte_string: A byte string :return: A unicode string """ try: return str_cls(byte_string, _encoding) # If the "correct" encoding did not work, try some defaults, and then just # obliterate characters that we can't seen to decode properly except (UnicodeDecodeError): for encoding in _fallback_encodings: try: return str_cls(byte_string, encoding, errors='strict') except (UnicodeDecodeError): pass return str_cls(byte_string, errors='replace') oscrypto-1.3.0/oscrypto/_win/_kernel32.py000066400000000000000000000014461421476274700203740ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .. import ffi from ._decode import _try_decode from .._types import str_cls if ffi() == 'cffi': from ._kernel32_cffi import kernel32, get_error else: from ._kernel32_ctypes import kernel32, get_error __all__ = [ 'handle_error', 'kernel32', ] def handle_error(result): """ Extracts the last Windows error message into a python unicode string :param result: A function result, 0 or None indicates failure :return: A unicode string error message """ if result: return _, error_string = get_error() if not isinstance(error_string, str_cls): error_string = _try_decode(error_string) raise OSError(error_string) oscrypto-1.3.0/oscrypto/_win/_kernel32_cffi.py000066400000000000000000000016441421476274700213630ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .._ffi import register_ffi from .._types import str_cls from ..errors import LibraryNotFoundError import cffi __all__ = [ 'get_error', 'kernel32', ] ffi = cffi.FFI() if cffi.__version_info__ >= (0, 9): ffi.set_unicode(True) ffi.cdef(""" typedef long long LARGE_INTEGER; BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount); typedef struct _FILETIME { DWORD dwLowDateTime; DWORD dwHighDateTime; } FILETIME; void GetSystemTimeAsFileTime(FILETIME *lpSystemTimeAsFileTime); """) try: kernel32 = ffi.dlopen('kernel32.dll') register_ffi(kernel32, ffi) except (OSError) as e: if str_cls(e).find('cannot load library') != -1: raise LibraryNotFoundError('kernel32.dll could not be found') raise def get_error(): return ffi.getwinerror() oscrypto-1.3.0/oscrypto/_win/_kernel32_ctypes.py000066400000000000000000000023651421476274700217640ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import ctypes from ctypes import windll, wintypes, POINTER, c_longlong, Structure from .._ffi import FFIEngineError from .._types import str_cls from ..errors import LibraryNotFoundError __all__ = [ 'get_error', 'kernel32', ] try: kernel32 = windll.kernel32 except (OSError) as e: if str_cls(e).find('The specified module could not be found') != -1: raise LibraryNotFoundError('kernel32.dll could not be found') raise LARGE_INTEGER = c_longlong try: kernel32.QueryPerformanceCounter.argtypes = [POINTER(LARGE_INTEGER)] kernel32.QueryPerformanceCounter.restype = wintypes.BOOL class FILETIME(Structure): _fields_ = [ ("dwLowDateTime", wintypes.DWORD), ("dwHighDateTime", wintypes.DWORD), ] kernel32.GetSystemTimeAsFileTime.argtypes = [POINTER(FILETIME)] kernel32.GetSystemTimeAsFileTime.restype = None except (AttributeError): raise FFIEngineError('Error initializing ctypes') setattr(kernel32, 'LARGE_INTEGER', LARGE_INTEGER) setattr(kernel32, 'FILETIME', FILETIME) def get_error(): error = ctypes.GetLastError() return (error, ctypes.FormatError(error)) oscrypto-1.3.0/oscrypto/_win/_secur32.py000066400000000000000000000076011421476274700202340ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .. import ffi from ._decode import _try_decode from ..errors import TLSError from .._types import str_cls if ffi() == 'cffi': from ._secur32_cffi import secur32, get_error else: from ._secur32_ctypes import secur32, get_error __all__ = [ 'handle_error', 'secur32', 'Secur32Const', ] def handle_error(result, exception_class=None): """ Extracts the last Windows error message into a python unicode string :param result: A function result, 0 or None indicates failure :param exception_class: The exception class to use for the exception if an error occurred :return: A unicode string error message """ if result == 0: return if result == Secur32Const.SEC_E_OUT_OF_SEQUENCE: raise TLSError('A packet was received out of order') if result == Secur32Const.SEC_E_MESSAGE_ALTERED: raise TLSError('A packet was received altered') if result == Secur32Const.SEC_E_CONTEXT_EXPIRED: raise TLSError('The TLS session expired') _, error_string = get_error() if not isinstance(error_string, str_cls): error_string = _try_decode(error_string) if exception_class is None: exception_class = OSError raise exception_class(('SECURITY_STATUS error 0x%0.2X: ' % result) + error_string) class Secur32Const(): SCHANNEL_CRED_VERSION = 4 SECPKG_CRED_OUTBOUND = 0x00000002 UNISP_NAME = "Microsoft Unified Security Protocol Provider" SCH_CRED_MANUAL_CRED_VALIDATION = 0x00000008 SCH_CRED_AUTO_CRED_VALIDATION = 0x00000020 SCH_USE_STRONG_CRYPTO = 0x00400000 SCH_CRED_NO_DEFAULT_CREDS = 0x00000010 SECBUFFER_VERSION = 0 SEC_E_OK = 0x00000000 SEC_I_CONTINUE_NEEDED = 0x00090312 SEC_I_CONTEXT_EXPIRED = 0x00090317 SEC_I_RENEGOTIATE = 0x00090321 SEC_E_INCOMPLETE_MESSAGE = 0x80090318 SEC_E_INVALID_TOKEN = 0x80090308 SEC_E_OUT_OF_SEQUENCE = 0x8009031 SEC_E_MESSAGE_ALTERED = 0x8009030F SEC_E_CONTEXT_EXPIRED = 0x80090317 SEC_E_INVALID_PARAMETER = 0x8009035D SEC_E_WRONG_PRINCIPAL = 0x80090322 # Domain name mismatch SEC_E_UNTRUSTED_ROOT = 0x80090325 SEC_E_CERT_EXPIRED = 0x80090328 SEC_E_ILLEGAL_MESSAGE = 0x80090326 # Handshake error SEC_E_INTERNAL_ERROR = 0x80090304 # Occurs when DH params are too small SEC_E_BUFFER_TOO_SMALL = 0x80090321 SEC_I_INCOMPLETE_CREDENTIALS = 0x00090320 ISC_REQ_REPLAY_DETECT = 4 ISC_REQ_SEQUENCE_DETECT = 8 ISC_REQ_CONFIDENTIALITY = 16 ISC_REQ_ALLOCATE_MEMORY = 256 ISC_REQ_INTEGRITY = 65536 ISC_REQ_STREAM = 0x00008000 ISC_REQ_USE_SUPPLIED_CREDS = 0x00000080 ISC_RET_REPLAY_DETECT = 4 ISC_RET_SEQUENCE_DETECT = 8 ISC_RET_CONFIDENTIALITY = 16 ISC_RET_ALLOCATED_MEMORY = 256 ISC_RET_INTEGRITY = 65536 ISC_RET_STREAM = 0x00008000 SECBUFFER_ALERT = 17 SECBUFFER_STREAM_HEADER = 7 SECBUFFER_STREAM_TRAILER = 6 SECBUFFER_EXTRA = 5 SECBUFFER_TOKEN = 2 SECBUFFER_DATA = 1 SECBUFFER_EMPTY = 0 SECPKG_ATTR_STREAM_SIZES = 0x04 SECPKG_ATTR_CONNECTION_INFO = 0x5A SECPKG_ATTR_REMOTE_CERT_CONTEXT = 0x53 SP_PROT_TLS1_2_CLIENT = 0x800 SP_PROT_TLS1_1_CLIENT = 0x200 SP_PROT_TLS1_CLIENT = 0x80 SP_PROT_SSL3_CLIENT = 0x20 SP_PROT_SSL2_CLIENT = 0x8 CALG_AES_256 = 0x00006610 CALG_AES_128 = 0x0000660E CALG_3DES = 0x00006603 CALG_RC4 = 0x00006801 CALG_RC2 = 0x00006602 CALG_DES = 0x00006601 CALG_MD5 = 0x00008003 CALG_SHA1 = 0x00008004 CALG_SHA256 = 0x0000800C CALG_SHA384 = 0x0000800D CALG_SHA512 = 0x0000800E CALG_DH_SF = 0x0000AA01 CALG_DH_EPHEM = 0x0000AA02 CALG_ECDH = 0x0000AA05 CALG_ECDHE = 0x0000AE06 CALG_RSA_KEYX = 0x0000A400 CALG_RSA_SIGN = 0x00002400 CALG_ECDSA = 0x00002203 CALG_DSS_SIGN = 0x00002200 oscrypto-1.3.0/oscrypto/_win/_secur32_cffi.py000066400000000000000000000075771421476274700212370ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys from .._ffi import register_ffi from .._types import str_cls from ..errors import LibraryNotFoundError import cffi __all__ = [ 'get_error', 'secur32', ] ffi = cffi.FFI() if cffi.__version_info__ >= (0, 9): ffi.set_unicode(True) if sys.maxsize > 2 ** 32: ffi.cdef("typedef uint64_t ULONG_PTR;") else: ffi.cdef("typedef unsigned long ULONG_PTR;") ffi.cdef(""" typedef HANDLE HCERTSTORE; typedef unsigned int ALG_ID; typedef WCHAR SEC_WCHAR; typedef unsigned long SECURITY_STATUS; typedef void *LUID; typedef void *SEC_GET_KEY_FN; typedef struct _SecHandle { ULONG_PTR dwLower; ULONG_PTR dwUpper; } SecHandle; typedef SecHandle CredHandle; typedef SecHandle CtxtHandle; typedef struct _SCHANNEL_CRED { DWORD dwVersion; DWORD cCreds; void *paCred; HCERTSTORE hRootStore; DWORD cMappers; void **aphMappers; DWORD cSupportedAlgs; ALG_ID *palgSupportedAlgs; DWORD grbitEnabledProtocols; DWORD dwMinimumCipherStrength; DWORD dwMaximumCipherStrength; DWORD dwSessionLifespan; DWORD dwFlags; DWORD dwCredFormat; } SCHANNEL_CRED; typedef struct _TimeStamp { DWORD dwLowDateTime; DWORD dwHighDateTime; } TimeStamp; typedef struct _SecBuffer { ULONG cbBuffer; ULONG BufferType; BYTE *pvBuffer; } SecBuffer; typedef struct _SecBufferDesc { ULONG ulVersion; ULONG cBuffers; SecBuffer *pBuffers; } SecBufferDesc; typedef struct _SecPkgContext_StreamSizes { ULONG cbHeader; ULONG cbTrailer; ULONG cbMaximumMessage; ULONG cBuffers; ULONG cbBlockSize; } SecPkgContext_StreamSizes; typedef struct _CERT_CONTEXT { DWORD dwCertEncodingType; BYTE *pbCertEncoded; DWORD cbCertEncoded; void *pCertInfo; HCERTSTORE hCertStore; } CERT_CONTEXT; typedef struct _SecPkgContext_ConnectionInfo { DWORD dwProtocol; ALG_ID aiCipher; DWORD dwCipherStrength; ALG_ID aiHash; DWORD dwHashStrength; ALG_ID aiExch; DWORD dwExchStrength; } SecPkgContext_ConnectionInfo; SECURITY_STATUS AcquireCredentialsHandleW(SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse, LUID *pvLogonID, void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument, CredHandle *phCredential, TimeStamp *ptsExpiry); SECURITY_STATUS FreeCredentialsHandle(CredHandle *phCredential); SECURITY_STATUS InitializeSecurityContextW(CredHandle *phCredential, CtxtHandle *phContext, SEC_WCHAR *pszTargetName, ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, SecBufferDesc *pInput, ULONG Reserved2, CtxtHandle *phNewContext, SecBufferDesc *pOutput, ULONG *pfContextAttr, TimeStamp *ptsExpiry); SECURITY_STATUS FreeContextBuffer(void *pvContextBuffer); SECURITY_STATUS ApplyControlToken(CtxtHandle *phContext, SecBufferDesc *pInput); SECURITY_STATUS DeleteSecurityContext(CtxtHandle *phContext); SECURITY_STATUS QueryContextAttributesW(CtxtHandle *phContext, ULONG ulAttribute, void *pBuffer); SECURITY_STATUS EncryptMessage(CtxtHandle *phContext, ULONG fQOP, SecBufferDesc *pMessage, ULONG MessageSeqNo); SECURITY_STATUS DecryptMessage(CtxtHandle *phContext, SecBufferDesc *pMessage, ULONG MessageSeqNo, ULONG *pfQOP); """) try: secur32 = ffi.dlopen('secur32.dll') register_ffi(secur32, ffi) except (OSError) as e: if str_cls(e).find('cannot load library') != -1: raise LibraryNotFoundError('secur32.dll could not be found') raise def get_error(): return ffi.getwinerror() oscrypto-1.3.0/oscrypto/_win/_secur32_ctypes.py000066400000000000000000000122711421476274700216220ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys import ctypes from ctypes import windll, wintypes, POINTER, c_void_p, c_uint, Structure from ctypes.wintypes import DWORD, ULONG from .._ffi import FFIEngineError from .._types import str_cls from ..errors import LibraryNotFoundError __all__ = [ 'get_error', 'secur32', ] try: secur32 = windll.secur32 except (OSError) as e: if str_cls(e).find('The specified module could not be found') != -1: raise LibraryNotFoundError('secur32.dll could not be found') raise HCERTSTORE = wintypes.HANDLE ALG_ID = c_uint if sys.maxsize > 2 ** 32: ULONG_PTR = ctypes.c_uint64 else: ULONG_PTR = ctypes.c_ulong SEC_GET_KEY_FN = c_void_p LUID = c_void_p SECURITY_STATUS = ctypes.c_ulong SEC_WCHAR = wintypes.WCHAR try: class SecHandle(Structure): _fields_ = [ ('dwLower', ULONG_PTR), ('dwUpper', ULONG_PTR), ] CredHandle = SecHandle CtxtHandle = SecHandle class SCHANNEL_CRED(Structure): # noqa _fields_ = [ ('dwVersion', DWORD), ('cCreds', DWORD), ('paCred', c_void_p), ('hRootStore', HCERTSTORE), ('cMappers', DWORD), ('aphMappers', POINTER(c_void_p)), ('cSupportedAlgs', DWORD), ('palgSupportedAlgs', POINTER(ALG_ID)), ('grbitEnabledProtocols', DWORD), ('dwMinimumCipherStrength', DWORD), ('dwMaximumCipherStrength', DWORD), ('dwSessionLifespan', DWORD), ('dwFlags', DWORD), ('dwCredFormat', DWORD), ] class TimeStamp(Structure): _fields_ = [ ('dwLowDateTime', DWORD), ('dwHighDateTime', DWORD), ] class SecBuffer(Structure): _fields_ = [ ('cbBuffer', ULONG), ('BufferType', ULONG), ('pvBuffer', POINTER(ctypes.c_byte)), ] PSecBuffer = POINTER(SecBuffer) class SecBufferDesc(Structure): _fields_ = [ ('ulVersion', ULONG), ('cBuffers', ULONG), ('pBuffers', PSecBuffer), ] class SecPkgContext_StreamSizes(Structure): # noqa _fields_ = [ ('cbHeader', ULONG), ('cbTrailer', ULONG), ('cbMaximumMessage', ULONG), ('cBuffers', ULONG), ('cbBlockSize', ULONG), ] class SecPkgContext_ConnectionInfo(Structure): # noqa _fields_ = [ ('dwProtocol', DWORD), ('aiCipher', ALG_ID), ('dwCipherStrength', DWORD), ('aiHash', ALG_ID), ('dwHashStrength', DWORD), ('aiExch', ALG_ID), ('dwExchStrength', DWORD), ] secur32.AcquireCredentialsHandleW.argtypes = [ POINTER(SEC_WCHAR), POINTER(SEC_WCHAR), ULONG, POINTER(LUID), c_void_p, SEC_GET_KEY_FN, c_void_p, POINTER(CredHandle), POINTER(TimeStamp) ] secur32.AcquireCredentialsHandleW.restype = SECURITY_STATUS secur32.FreeCredentialsHandle.argtypes = [ POINTER(CredHandle) ] secur32.FreeCredentialsHandle.restype = SECURITY_STATUS secur32.InitializeSecurityContextW.argtypes = [ POINTER(CredHandle), POINTER(CtxtHandle), POINTER(SEC_WCHAR), ULONG, ULONG, ULONG, POINTER(SecBufferDesc), ULONG, POINTER(CtxtHandle), POINTER(SecBufferDesc), POINTER(ULONG), POINTER(TimeStamp) ] secur32.InitializeSecurityContextW.restype = SECURITY_STATUS secur32.FreeContextBuffer.argtypes = [ c_void_p ] secur32.FreeContextBuffer.restype = SECURITY_STATUS secur32.ApplyControlToken.argtypes = [ POINTER(CtxtHandle), POINTER(SecBufferDesc) ] secur32.ApplyControlToken.restype = SECURITY_STATUS secur32.DeleteSecurityContext.argtypes = [ POINTER(CtxtHandle) ] secur32.DeleteSecurityContext.restype = SECURITY_STATUS secur32.QueryContextAttributesW.argtypes = [ POINTER(CtxtHandle), ULONG, c_void_p ] secur32.QueryContextAttributesW.restype = SECURITY_STATUS secur32.EncryptMessage.argtypes = [ POINTER(CtxtHandle), ULONG, POINTER(SecBufferDesc), ULONG ] secur32.EncryptMessage.restype = SECURITY_STATUS secur32.DecryptMessage.argtypes = [ POINTER(CtxtHandle), POINTER(SecBufferDesc), ULONG, POINTER(ULONG) ] secur32.DecryptMessage.restype = SECURITY_STATUS except (AttributeError): raise FFIEngineError('Error initializing ctypes') setattr(secur32, 'ALG_ID', ALG_ID) setattr(secur32, 'CredHandle', CredHandle) setattr(secur32, 'CtxtHandle', CtxtHandle) setattr(secur32, 'SecBuffer', SecBuffer) setattr(secur32, 'SecBufferDesc', SecBufferDesc) setattr(secur32, 'SecPkgContext_StreamSizes', SecPkgContext_StreamSizes) setattr(secur32, 'SecPkgContext_ConnectionInfo', SecPkgContext_ConnectionInfo) setattr(secur32, 'SCHANNEL_CRED', SCHANNEL_CRED) def get_error(): error = ctypes.GetLastError() return (error, ctypes.FormatError(error)) oscrypto-1.3.0/oscrypto/_win/asymmetric.py000066400000000000000000003567131421476274700207770ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os import sys import hashlib import random from .._asn1 import ( Certificate as Asn1Certificate, DHParameters, DSAParams, DSASignature, ECDomainParameters, ECPrivateKey, Integer, int_from_bytes, int_to_bytes, PrivateKeyAlgorithm, PrivateKeyInfo, PublicKeyAlgorithm, PublicKeyInfo, RSAPrivateKey, RSAPublicKey, ) from .._asymmetric import ( _CertificateBase, _fingerprint, _parse_pkcs12, _PrivateKeyBase, _PublicKeyBase, _unwrap_private_key_info, parse_certificate, parse_private, parse_public, ) from .._errors import pretty_message from .._ffi import ( buffer_from_bytes, buffer_from_unicode, byte_array, bytes_from_buffer, cast, deref, native, new, null, pointer_set, sizeof, struct, struct_bytes, struct_from_buffer, unwrap, write_to_buffer, ) from .. import backend from .._int import fill_width from ..errors import AsymmetricKeyError, IncompleteAsymmetricKeyError, SignatureError from .._types import type_name, str_cls, byte_cls, int_types from .._pkcs1 import ( add_pkcs1v15_signature_padding, add_pss_padding, raw_rsa_private_crypt, raw_rsa_public_crypt, remove_pkcs1v15_signature_padding, verify_pss_padding, ) from ..util import constant_compare _gwv = sys.getwindowsversion() _win_version_info = (_gwv[0], _gwv[1]) _backend = backend() if _backend == 'winlegacy': from ._advapi32 import advapi32, Advapi32Const, handle_error, open_context_handle, close_context_handle from .._ecdsa import ( ec_generate_pair as _pure_python_ec_generate_pair, ec_compute_public_key_point as _pure_python_ec_compute_public_key_point, ec_public_key_info, ecdsa_sign as _pure_python_ecdsa_sign, ecdsa_verify as _pure_python_ecdsa_verify, ) else: from ._cng import bcrypt, BcryptConst, handle_error, open_alg_handle, close_alg_handle __all__ = [ 'Certificate', 'dsa_sign', 'dsa_verify', 'ecdsa_sign', 'ecdsa_verify', 'generate_pair', 'load_certificate', 'load_pkcs12', 'load_private_key', 'load_public_key', 'parse_pkcs12', 'PrivateKey', 'PublicKey', 'rsa_oaep_decrypt', 'rsa_oaep_encrypt', 'rsa_pkcs1v15_decrypt', 'rsa_pkcs1v15_encrypt', 'rsa_pkcs1v15_sign', 'rsa_pkcs1v15_verify', 'rsa_pss_sign', 'rsa_pss_verify', ] # A list of primes from OpenSSL's bn_prime.h to use when testing primality of a # large integer _SMALL_PRIMES = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919, 7927, 7933, 7937, 7949, 7951, 7963, 7993, 8009, 8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087, 8089, 8093, 8101, 8111, 8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209, 8219, 8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291, 8293, 8297, 8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387, 8389, 8419, 8423, 8429, 8431, 8443, 8447, 8461, 8467, 8501, 8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573, 8581, 8597, 8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677, 8681, 8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741, 8747, 8753, 8761, 8779, 8783, 8803, 8807, 8819, 8821, 8831, 8837, 8839, 8849, 8861, 8863, 8867, 8887, 8893, 8923, 8929, 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011, 9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, 9127, 9133, 9137, 9151, 9157, 9161, 9173, 9181, 9187, 9199, 9203, 9209, 9221, 9227, 9239, 9241, 9257, 9277, 9281, 9283, 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371, 9377, 9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439, 9461, 9463, 9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533, 9539, 9547, 9551, 9587, 9601, 9613, 9619, 9623, 9629, 9631, 9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719, 9721, 9733, 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811, 9817, 9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887, 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973, 10007, 10009, 10037, 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099, 10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 10169, 10177, 10181, 10193, 10211, 10223, 10243, 10247, 10253, 10259, 10267, 10271, 10273, 10289, 10301, 10303, 10313, 10321, 10331, 10333, 10337, 10343, 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459, 10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567, 10589, 10597, 10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657, 10663, 10667, 10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739, 10753, 10771, 10781, 10789, 10799, 10831, 10837, 10847, 10853, 10859, 10861, 10867, 10883, 10889, 10891, 10903, 10909, 10937, 10939, 10949, 10957, 10973, 10979, 10987, 10993, 11003, 11027, 11047, 11057, 11059, 11069, 11071, 11083, 11087, 11093, 11113, 11117, 11119, 11131, 11149, 11159, 11161, 11171, 11173, 11177, 11197, 11213, 11239, 11243, 11251, 11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317, 11321, 11329, 11351, 11353, 11369, 11383, 11393, 11399, 11411, 11423, 11437, 11443, 11447, 11467, 11471, 11483, 11489, 11491, 11497, 11503, 11519, 11527, 11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657, 11677, 11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777, 11779, 11783, 11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833, 11839, 11863, 11867, 11887, 11897, 11903, 11909, 11923, 11927, 11933, 11939, 11941, 11953, 11959, 11969, 11971, 11981, 11987, 12007, 12011, 12037, 12041, 12043, 12049, 12071, 12073, 12097, 12101, 12107, 12109, 12113, 12119, 12143, 12149, 12157, 12161, 12163, 12197, 12203, 12211, 12227, 12239, 12241, 12251, 12253, 12263, 12269, 12277, 12281, 12289, 12301, 12323, 12329, 12343, 12347, 12373, 12377, 12379, 12391, 12401, 12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 12479, 12487, 12491, 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553, 12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619, 12637, 12641, 12647, 12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739, 12743, 12757, 12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829, 12841, 12853, 12889, 12893, 12899, 12907, 12911, 12917, 12919, 12923, 12941, 12953, 12959, 12967, 12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033, 13037, 13043, 13049, 13063, 13093, 13099, 13103, 13109, 13121, 13127, 13147, 13151, 13159, 13163, 13171, 13177, 13183, 13187, 13217, 13219, 13229, 13241, 13249, 13259, 13267, 13291, 13297, 13309, 13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397, 13399, 13411, 13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487, 13499, 13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, 13613, 13619, 13627, 13633, 13649, 13669, 13679, 13681, 13687, 13691, 13693, 13697, 13709, 13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763, 13781, 13789, 13799, 13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879, 13883, 13901, 13903, 13907, 13913, 13921, 13931, 13933, 13963, 13967, 13997, 13999, 14009, 14011, 14029, 14033, 14051, 14057, 14071, 14081, 14083, 14087, 14107, 14143, 14149, 14153, 14159, 14173, 14177, 14197, 14207, 14221, 14243, 14249, 14251, 14281, 14293, 14303, 14321, 14323, 14327, 14341, 14347, 14369, 14387, 14389, 14401, 14407, 14411, 14419, 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489, 14503, 14519, 14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591, 14593, 14621, 14627, 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699, 14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 14759, 14767, 14771, 14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843, 14851, 14867, 14869, 14879, 14887, 14891, 14897, 14923, 14929, 14939, 14947, 14951, 14957, 14969, 14983, 15013, 15017, 15031, 15053, 15061, 15073, 15077, 15083, 15091, 15101, 15107, 15121, 15131, 15137, 15139, 15149, 15161, 15173, 15187, 15193, 15199, 15217, 15227, 15233, 15241, 15259, 15263, 15269, 15271, 15277, 15287, 15289, 15299, 15307, 15313, 15319, 15329, 15331, 15349, 15359, 15361, 15373, 15377, 15383, 15391, 15401, 15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473, 15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, 15601, 15607, 15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679, 15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, 15787, 15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881, 15887, 15889, 15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971, 15973, 15991, 16001, 16007, 16033, 16057, 16061, 16063, 16067, 16069, 16073, 16087, 16091, 16097, 16103, 16111, 16127, 16139, 16141, 16183, 16187, 16189, 16193, 16217, 16223, 16229, 16231, 16249, 16253, 16267, 16273, 16301, 16319, 16333, 16339, 16349, 16361, 16363, 16369, 16381, 16411, 16417, 16421, 16427, 16433, 16447, 16451, 16453, 16477, 16481, 16487, 16493, 16519, 16529, 16547, 16553, 16561, 16567, 16573, 16603, 16607, 16619, 16631, 16633, 16649, 16651, 16657, 16661, 16673, 16691, 16693, 16699, 16703, 16729, 16741, 16747, 16759, 16763, 16787, 16811, 16823, 16829, 16831, 16843, 16871, 16879, 16883, 16889, 16901, 16903, 16921, 16927, 16931, 16937, 16943, 16963, 16979, 16981, 16987, 16993, 17011, 17021, 17027, 17029, 17033, 17041, 17047, 17053, 17077, 17093, 17099, 17107, 17117, 17123, 17137, 17159, 17167, 17183, 17189, 17191, 17203, 17207, 17209, 17231, 17239, 17257, 17291, 17293, 17299, 17317, 17321, 17327, 17333, 17341, 17351, 17359, 17377, 17383, 17387, 17389, 17393, 17401, 17417, 17419, 17431, 17443, 17449, 17467, 17471, 17477, 17483, 17489, 17491, 17497, 17509, 17519, 17539, 17551, 17569, 17573, 17579, 17581, 17597, 17599, 17609, 17623, 17627, 17657, 17659, 17669, 17681, 17683, 17707, 17713, 17729, 17737, 17747, 17749, 17761, 17783, 17789, 17791, 17807, 17827, 17837, 17839, 17851, 17863, ] class _WinKey(): # A CNG BCRYPT_KEY_HANDLE on Vista and newer, an HCRYPTKEY on XP and 2003 key_handle = None # On XP and 2003, we have to carry around more info context_handle = None ex_key_handle = None # A reference to the library used in the destructor to make sure it hasn't # been garbage collected by the time this object is garbage collected _lib = None def __init__(self, key_handle, asn1): """ :param key_handle: A CNG BCRYPT_KEY_HANDLE value (Vista and newer) or an HCRYPTKEY (XP and 2003) from loading/importing the key :param asn1: An asn1crypto object for the concrete type """ self.key_handle = key_handle self.asn1 = asn1 if _backend == 'winlegacy': self._lib = advapi32 else: self._lib = bcrypt def __del__(self): if self.key_handle: if _backend == 'winlegacy': res = self._lib.CryptDestroyKey(self.key_handle) else: res = self._lib.BCryptDestroyKey(self.key_handle) handle_error(res) self.key_handle = None if self.context_handle and _backend == 'winlegacy': close_context_handle(self.context_handle) self.context_handle = None self._lib = None class PrivateKey(_WinKey, _PrivateKeyBase): """ Container for the OS crypto library representation of a private key """ _public_key = None def __init__(self, key_handle, asn1): """ :param key_handle: A CNG BCRYPT_KEY_HANDLE value (Vista and newer) or an HCRYPTKEY (XP and 2003) from loading/importing the key :param asn1: An asn1crypto.keys.PrivateKeyInfo object """ _WinKey.__init__(self, key_handle, asn1) @property def public_key(self): """ :return: A PublicKey object corresponding to this private key. """ if _backend == 'winlegacy': if self.algorithm == 'ec': pub_point = _pure_python_ec_compute_public_key_point(self.asn1) self._public_key = PublicKey(None, ec_public_key_info(pub_point, self.curve)) elif self.algorithm == 'dsa': # The DSA provider won't allow exporting the private key with # CryptoImportKey flags set to 0 and won't allow flags to be set # to CRYPT_EXPORTABLE, so we manually recreated the public key # ASN.1 params = self.asn1['private_key_algorithm']['parameters'] pub_asn1 = PublicKeyInfo({ 'algorithm': PublicKeyAlgorithm({ 'algorithm': 'dsa', 'parameters': params }), 'public_key': Integer(pow( params['g'].native, self.asn1['private_key'].parsed.native, params['p'].native )) }) self._public_key = load_public_key(pub_asn1) else: # This suffers from similar problems as above, although not # as insurmountable. This is just a simpler/faster solution # since the private key has all of the data we need anyway parsed = self.asn1['private_key'].parsed pub_asn1 = PublicKeyInfo({ 'algorithm': PublicKeyAlgorithm({ 'algorithm': 'rsa' }), 'public_key': RSAPublicKey({ 'modulus': parsed['modulus'], 'public_exponent': parsed['public_exponent'] }) }) self._public_key = load_public_key(pub_asn1) else: pub_asn1, _ = _bcrypt_key_handle_to_asn1(self.algorithm, self.bit_size, self.key_handle) self._public_key = load_public_key(pub_asn1) return self._public_key @property def fingerprint(self): """ Creates a fingerprint that can be compared with a public key to see if the two form a pair. This fingerprint is not compatible with fingerprints generated by any other software. :return: A byte string that is a sha256 hash of selected components (based on the key type) """ if self._fingerprint is None: self._fingerprint = _fingerprint(self.asn1, load_private_key) return self._fingerprint class PublicKey(_WinKey, _PublicKeyBase): """ Container for the OS crypto library representation of a public key """ def __init__(self, key_handle, asn1): """ :param key_handle: A CNG BCRYPT_KEY_HANDLE value (Vista and newer) or an HCRYPTKEY (XP and 2003) from loading/importing the key :param asn1: An asn1crypto.keys.PublicKeyInfo object """ _WinKey.__init__(self, key_handle, asn1) class Certificate(_WinKey, _CertificateBase): """ Container for the OS crypto library representation of a certificate """ _public_key = None _self_signed = None def __init__(self, key_handle, asn1): """ :param key_handle: A CNG BCRYPT_KEY_HANDLE value (Vista and newer) or an HCRYPTKEY (XP and 2003) from loading/importing the certificate :param asn1: An asn1crypto.x509.Certificate object """ _WinKey.__init__(self, key_handle, asn1) @property def public_key(self): """ :return: The PublicKey object for the public key this certificate contains """ if self._public_key is None: self._public_key = load_public_key(self.asn1['tbs_certificate']['subject_public_key_info']) return self._public_key @property def self_signed(self): """ :return: A boolean - if the certificate is self-signed """ if self._self_signed is None: self._self_signed = False if self.asn1.self_signed in set(['yes', 'maybe']): signature_algo = self.asn1['signature_algorithm'].signature_algo hash_algo = self.asn1['signature_algorithm'].hash_algo if signature_algo == 'rsassa_pkcs1v15': verify_func = rsa_pkcs1v15_verify elif signature_algo == 'rsassa_pss': verify_func = rsa_pss_verify elif signature_algo == 'dsa': verify_func = dsa_verify elif signature_algo == 'ecdsa': verify_func = ecdsa_verify else: raise OSError(pretty_message( ''' Unable to verify the signature of the certificate since it uses the unsupported algorithm %s ''', signature_algo )) try: verify_func( self, self.asn1['signature_value'].native, self.asn1['tbs_certificate'].dump(), hash_algo ) self._self_signed = True except (SignatureError): pass return self._self_signed def generate_pair(algorithm, bit_size=None, curve=None): """ Generates a public/private key pair :param algorithm: The key algorithm - "rsa", "dsa" or "ec" :param bit_size: An integer - used for "rsa" and "dsa". For "rsa" the value maye be 1024, 2048, 3072 or 4096. For "dsa" the value may be 1024, plus 2048 or 3072 if on Windows 8 or newer. :param curve: A unicode string - used for "ec" keys. Valid values include "secp256r1", "secp384r1" and "secp521r1". :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A 2-element tuple of (PublicKey, PrivateKey). The contents of each key may be saved by calling .asn1.dump(). """ if algorithm not in set(['rsa', 'dsa', 'ec']): raise ValueError(pretty_message( ''' algorithm must be one of "rsa", "dsa", "ec", not %s ''', repr(algorithm) )) if algorithm == 'rsa': if bit_size not in set([1024, 2048, 3072, 4096]): raise ValueError(pretty_message( ''' bit_size must be one of 1024, 2048, 3072, 4096, not %s ''', repr(bit_size) )) elif algorithm == 'dsa': # Windows Vista and 7 only support SHA1-based DSA keys if _win_version_info < (6, 2) or _backend == 'winlegacy': if bit_size != 1024: raise ValueError(pretty_message( ''' bit_size must be 1024, not %s ''', repr(bit_size) )) else: if bit_size not in set([1024, 2048, 3072]): raise ValueError(pretty_message( ''' bit_size must be one of 1024, 2048, 3072, not %s ''', repr(bit_size) )) elif algorithm == 'ec': if curve not in set(['secp256r1', 'secp384r1', 'secp521r1']): raise ValueError(pretty_message( ''' curve must be one of "secp256r1", "secp384r1", "secp521r1", not %s ''', repr(curve) )) if _backend == 'winlegacy': if algorithm == 'ec': pub_info, priv_info = _pure_python_ec_generate_pair(curve) return (PublicKey(None, pub_info), PrivateKey(None, priv_info)) return _advapi32_generate_pair(algorithm, bit_size) else: return _bcrypt_generate_pair(algorithm, bit_size, curve) def _advapi32_key_handle_to_asn1(algorithm, bit_size, key_handle): """ Accepts an key handle and exports it to ASN.1 :param algorithm: The key algorithm - "rsa" or "dsa" :param bit_size: An integer - only used when algorithm is "rsa" :param key_handle: The handle to export :return: A 2-element tuple of asn1crypto.keys.PrivateKeyInfo and asn1crypto.keys.PublicKeyInfo """ if algorithm == 'rsa': struct_type = 'RSABLOBHEADER' else: struct_type = 'DSSBLOBHEADER' out_len = new(advapi32, 'DWORD *') res = advapi32.CryptExportKey( key_handle, null(), Advapi32Const.PRIVATEKEYBLOB, 0, null(), out_len ) handle_error(res) buffer_length = deref(out_len) buffer_ = buffer_from_bytes(buffer_length) res = advapi32.CryptExportKey( key_handle, null(), Advapi32Const.PRIVATEKEYBLOB, 0, buffer_, out_len ) handle_error(res) blob_struct_pointer = struct_from_buffer(advapi32, struct_type, buffer_) blob_struct = unwrap(blob_struct_pointer) struct_size = sizeof(advapi32, blob_struct) private_blob = bytes_from_buffer(buffer_, buffer_length)[struct_size:] if algorithm == 'rsa': public_info, private_info = _advapi32_interpret_rsa_key_blob(bit_size, blob_struct, private_blob) else: # The public key for a DSA key is not available in from the private # key blob, so we have to separately export the public key public_out_len = new(advapi32, 'DWORD *') res = advapi32.CryptExportKey( key_handle, null(), Advapi32Const.PUBLICKEYBLOB, 0, null(), public_out_len ) handle_error(res) public_buffer_length = deref(public_out_len) public_buffer = buffer_from_bytes(public_buffer_length) res = advapi32.CryptExportKey( key_handle, null(), Advapi32Const.PUBLICKEYBLOB, 0, public_buffer, public_out_len ) handle_error(res) public_blob = bytes_from_buffer(public_buffer, public_buffer_length)[struct_size:] public_info, private_info = _advapi32_interpret_dsa_key_blob(bit_size, public_blob, private_blob) return (public_info, private_info) def _advapi32_generate_pair(algorithm, bit_size=None): """ Generates a public/private key pair using CryptoAPI :param algorithm: The key algorithm - "rsa" or "dsa" :param bit_size: An integer - used for "rsa" and "dsa". For "rsa" the value maye be 1024, 2048, 3072 or 4096. For "dsa" the value may be 1024. :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A 2-element tuple of (PublicKey, PrivateKey). The contents of each key may be saved by calling .asn1.dump(). """ if algorithm == 'rsa': provider = Advapi32Const.MS_ENH_RSA_AES_PROV algorithm_id = Advapi32Const.CALG_RSA_SIGN else: provider = Advapi32Const.MS_ENH_DSS_DH_PROV algorithm_id = Advapi32Const.CALG_DSS_SIGN context_handle = None key_handle = None try: context_handle = open_context_handle(provider, verify_only=False) key_handle_pointer = new(advapi32, 'HCRYPTKEY *') flags = (bit_size << 16) | Advapi32Const.CRYPT_EXPORTABLE res = advapi32.CryptGenKey(context_handle, algorithm_id, flags, key_handle_pointer) handle_error(res) key_handle = unwrap(key_handle_pointer) public_info, private_info = _advapi32_key_handle_to_asn1(algorithm, bit_size, key_handle) return (load_public_key(public_info), load_private_key(private_info)) finally: if context_handle: close_context_handle(context_handle) if key_handle: advapi32.CryptDestroyKey(key_handle) def _bcrypt_key_handle_to_asn1(algorithm, bit_size, key_handle): """ Accepts an key handle and exports it to ASN.1 :param algorithm: The key algorithm - "rsa", "dsa" or "ec" :param bit_size: An integer - only used when algorithm is "dsa" :param key_handle: The handle to export :return: A 2-element tuple of asn1crypto.keys.PrivateKeyInfo and asn1crypto.keys.PublicKeyInfo """ if algorithm == 'rsa': struct_type = 'BCRYPT_RSAKEY_BLOB' private_blob_type = BcryptConst.BCRYPT_RSAFULLPRIVATE_BLOB public_blob_type = BcryptConst.BCRYPT_RSAPUBLIC_BLOB elif algorithm == 'dsa': if bit_size > 1024: struct_type = 'BCRYPT_DSA_KEY_BLOB_V2' else: struct_type = 'BCRYPT_DSA_KEY_BLOB' private_blob_type = BcryptConst.BCRYPT_DSA_PRIVATE_BLOB public_blob_type = BcryptConst.BCRYPT_DSA_PUBLIC_BLOB else: struct_type = 'BCRYPT_ECCKEY_BLOB' private_blob_type = BcryptConst.BCRYPT_ECCPRIVATE_BLOB public_blob_type = BcryptConst.BCRYPT_ECCPUBLIC_BLOB private_out_len = new(bcrypt, 'ULONG *') res = bcrypt.BCryptExportKey(key_handle, null(), private_blob_type, null(), 0, private_out_len, 0) handle_error(res) private_buffer_length = deref(private_out_len) private_buffer = buffer_from_bytes(private_buffer_length) res = bcrypt.BCryptExportKey( key_handle, null(), private_blob_type, private_buffer, private_buffer_length, private_out_len, 0 ) handle_error(res) private_blob_struct_pointer = struct_from_buffer(bcrypt, struct_type, private_buffer) private_blob_struct = unwrap(private_blob_struct_pointer) struct_size = sizeof(bcrypt, private_blob_struct) private_blob = bytes_from_buffer(private_buffer, private_buffer_length)[struct_size:] if algorithm == 'rsa': private_key = _bcrypt_interpret_rsa_key_blob('private', private_blob_struct, private_blob) elif algorithm == 'dsa': if bit_size > 1024: private_key = _bcrypt_interpret_dsa_key_blob('private', 2, private_blob_struct, private_blob) else: private_key = _bcrypt_interpret_dsa_key_blob('private', 1, private_blob_struct, private_blob) else: private_key = _bcrypt_interpret_ec_key_blob('private', private_blob_struct, private_blob) public_out_len = new(bcrypt, 'ULONG *') res = bcrypt.BCryptExportKey(key_handle, null(), public_blob_type, null(), 0, public_out_len, 0) handle_error(res) public_buffer_length = deref(public_out_len) public_buffer = buffer_from_bytes(public_buffer_length) res = bcrypt.BCryptExportKey( key_handle, null(), public_blob_type, public_buffer, public_buffer_length, public_out_len, 0 ) handle_error(res) public_blob_struct_pointer = struct_from_buffer(bcrypt, struct_type, public_buffer) public_blob_struct = unwrap(public_blob_struct_pointer) struct_size = sizeof(bcrypt, public_blob_struct) public_blob = bytes_from_buffer(public_buffer, public_buffer_length)[struct_size:] if algorithm == 'rsa': public_key = _bcrypt_interpret_rsa_key_blob('public', public_blob_struct, public_blob) elif algorithm == 'dsa': if bit_size > 1024: public_key = _bcrypt_interpret_dsa_key_blob('public', 2, public_blob_struct, public_blob) else: public_key = _bcrypt_interpret_dsa_key_blob('public', 1, public_blob_struct, public_blob) else: public_key = _bcrypt_interpret_ec_key_blob('public', public_blob_struct, public_blob) return (public_key, private_key) def _bcrypt_generate_pair(algorithm, bit_size=None, curve=None): """ Generates a public/private key pair using CNG :param algorithm: The key algorithm - "rsa", "dsa" or "ec" :param bit_size: An integer - used for "rsa" and "dsa". For "rsa" the value maye be 1024, 2048, 3072 or 4096. For "dsa" the value may be 1024, plus 2048 or 3072 if on Windows 8 or newer. :param curve: A unicode string - used for "ec" keys. Valid values include "secp256r1", "secp384r1" and "secp521r1". :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A 2-element tuple of (PublicKey, PrivateKey). The contents of each key may be saved by calling .asn1.dump(). """ if algorithm == 'rsa': alg_constant = BcryptConst.BCRYPT_RSA_ALGORITHM elif algorithm == 'dsa': alg_constant = BcryptConst.BCRYPT_DSA_ALGORITHM else: alg_constant = { 'secp256r1': BcryptConst.BCRYPT_ECDSA_P256_ALGORITHM, 'secp384r1': BcryptConst.BCRYPT_ECDSA_P384_ALGORITHM, 'secp521r1': BcryptConst.BCRYPT_ECDSA_P521_ALGORITHM, }[curve] bit_size = { 'secp256r1': 256, 'secp384r1': 384, 'secp521r1': 521, }[curve] key_handle = None try: alg_handle = open_alg_handle(alg_constant) key_handle_pointer = new(bcrypt, 'BCRYPT_KEY_HANDLE *') res = bcrypt.BCryptGenerateKeyPair(alg_handle, key_handle_pointer, bit_size, 0) handle_error(res) key_handle = unwrap(key_handle_pointer) res = bcrypt.BCryptFinalizeKeyPair(key_handle, 0) handle_error(res) public_key, private_key = _bcrypt_key_handle_to_asn1(algorithm, bit_size, key_handle) finally: if key_handle: bcrypt.BCryptDestroyKey(key_handle) return (load_public_key(public_key), load_private_key(private_key)) def generate_dh_parameters(bit_size): """ Generates DH parameters for use with Diffie-Hellman key exchange. Returns a structure in the format of DHParameter defined in PKCS#3, which is also used by the OpenSSL dhparam tool. THIS CAN BE VERY TIME CONSUMING! :param bit_size: The integer bit size of the parameters to generate. Must be between 512 and 4096, and divisible by 64. Recommended secure value as of early 2016 is 2048, with an absolute minimum of 1024. :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: An asn1crypto.algos.DHParameters object. Use oscrypto.asymmetric.dump_dh_parameters() to save to disk for usage with web servers. """ if not isinstance(bit_size, int_types): raise TypeError(pretty_message( ''' bit_size must be an integer, not %s ''', type_name(bit_size) )) if bit_size < 512: raise ValueError('bit_size must be greater than or equal to 512') if bit_size > 4096: raise ValueError('bit_size must be less than or equal to 4096') if bit_size % 64 != 0: raise ValueError('bit_size must be a multiple of 64') alg_handle = None # The following algorithm has elements taken from OpenSSL. In short, it # generates random numbers and then ensures that they are valid for the # hardcoded generator of 2, and then ensures the number is a "safe" prime # by ensuring p//2 is prime also. # OpenSSL allows use of generator 2 or 5, but we hardcode 2 since it is # the default, and what is used by Security.framework on OS X also. g = 2 try: byte_size = bit_size // 8 if _backend == 'win': alg_handle = open_alg_handle(BcryptConst.BCRYPT_RNG_ALGORITHM) buffer = buffer_from_bytes(byte_size) while True: if _backend == 'winlegacy': rb = os.urandom(byte_size) else: res = bcrypt.BCryptGenRandom(alg_handle, buffer, byte_size, 0) handle_error(res) rb = bytes_from_buffer(buffer) p = int_from_bytes(rb) # If a number is even, it can't be prime if p % 2 == 0: continue # Perform the generator checks outlined in OpenSSL's # dh_builtin_genparams() located in dh_gen.c if g == 2: if p % 24 != 11: continue elif g == 5: rem = p % 10 if rem != 3 and rem != 7: continue divisible = False for prime in _SMALL_PRIMES: if p % prime == 0: divisible = True break # If the number is not divisible by any of the small primes, then # move on to the full Miller-Rabin test. if not divisible and _is_prime(bit_size, p): q = p // 2 if _is_prime(bit_size, q): return DHParameters({'p': p, 'g': g}) finally: if alg_handle: close_alg_handle(alg_handle) def _is_prime(bit_size, n): """ An implementation of Miller–Rabin for checking if a number is prime. :param bit_size: An integer of the number of bits in the prime number :param n: An integer, the prime number :return: A boolean """ r = 0 s = n - 1 while s % 2 == 0: r += 1 s //= 2 if bit_size >= 1300: k = 2 elif bit_size >= 850: k = 3 elif bit_size >= 650: k = 4 elif bit_size >= 550: k = 5 elif bit_size >= 450: k = 6 for _ in range(k): a = random.randrange(2, n - 1) x = pow(a, s, n) if x == 1 or x == n - 1: continue for _ in range(r - 1): x = pow(x, 2, n) if x == n - 1: break else: return False return True def _advapi32_interpret_rsa_key_blob(bit_size, blob_struct, blob): """ Takes a CryptoAPI RSA private key blob and converts it into the ASN.1 structures for the public and private keys :param bit_size: The integer bit size of the key :param blob_struct: An instance of the advapi32.RSAPUBKEY struct :param blob: A byte string of the binary data after the header :return: A 2-element tuple of (asn1crypto.keys.PublicKeyInfo, asn1crypto.keys.PrivateKeyInfo) """ len1 = bit_size // 8 len2 = bit_size // 16 prime1_offset = len1 prime2_offset = prime1_offset + len2 exponent1_offset = prime2_offset + len2 exponent2_offset = exponent1_offset + len2 coefficient_offset = exponent2_offset + len2 private_exponent_offset = coefficient_offset + len2 public_exponent = blob_struct.rsapubkey.pubexp modulus = int_from_bytes(blob[0:prime1_offset][::-1]) prime1 = int_from_bytes(blob[prime1_offset:prime2_offset][::-1]) prime2 = int_from_bytes(blob[prime2_offset:exponent1_offset][::-1]) exponent1 = int_from_bytes(blob[exponent1_offset:exponent2_offset][::-1]) exponent2 = int_from_bytes(blob[exponent2_offset:coefficient_offset][::-1]) coefficient = int_from_bytes(blob[coefficient_offset:private_exponent_offset][::-1]) private_exponent = int_from_bytes(blob[private_exponent_offset:private_exponent_offset + len1][::-1]) public_key_info = PublicKeyInfo({ 'algorithm': PublicKeyAlgorithm({ 'algorithm': 'rsa', }), 'public_key': RSAPublicKey({ 'modulus': modulus, 'public_exponent': public_exponent, }), }) rsa_private_key = RSAPrivateKey({ 'version': 'two-prime', 'modulus': modulus, 'public_exponent': public_exponent, 'private_exponent': private_exponent, 'prime1': prime1, 'prime2': prime2, 'exponent1': exponent1, 'exponent2': exponent2, 'coefficient': coefficient, }) private_key_info = PrivateKeyInfo({ 'version': 0, 'private_key_algorithm': PrivateKeyAlgorithm({ 'algorithm': 'rsa', }), 'private_key': rsa_private_key, }) return (public_key_info, private_key_info) def _advapi32_interpret_dsa_key_blob(bit_size, public_blob, private_blob): """ Takes a CryptoAPI DSS private key blob and converts it into the ASN.1 structures for the public and private keys :param bit_size: The integer bit size of the key :param public_blob: A byte string of the binary data after the public key header :param private_blob: A byte string of the binary data after the private key header :return: A 2-element tuple of (asn1crypto.keys.PublicKeyInfo, asn1crypto.keys.PrivateKeyInfo) """ len1 = 20 len2 = bit_size // 8 q_offset = len2 g_offset = q_offset + len1 x_offset = g_offset + len2 y_offset = x_offset p = int_from_bytes(private_blob[0:q_offset][::-1]) q = int_from_bytes(private_blob[q_offset:g_offset][::-1]) g = int_from_bytes(private_blob[g_offset:x_offset][::-1]) x = int_from_bytes(private_blob[x_offset:x_offset + len1][::-1]) y = int_from_bytes(public_blob[y_offset:y_offset + len2][::-1]) public_key_info = PublicKeyInfo({ 'algorithm': PublicKeyAlgorithm({ 'algorithm': 'dsa', 'parameters': DSAParams({ 'p': p, 'q': q, 'g': g, }) }), 'public_key': Integer(y), }) private_key_info = PrivateKeyInfo({ 'version': 0, 'private_key_algorithm': PrivateKeyAlgorithm({ 'algorithm': 'dsa', 'parameters': DSAParams({ 'p': p, 'q': q, 'g': g, }) }), 'private_key': Integer(x), }) return (public_key_info, private_key_info) def _bcrypt_interpret_rsa_key_blob(key_type, blob_struct, blob): """ Take a CNG BCRYPT_RSAFULLPRIVATE_BLOB and converts it into an ASN.1 structure :param key_type: A unicode string of "private" or "public" :param blob_struct: An instance of BCRYPT_RSAKEY_BLOB :param blob: A byte string of the binary data contained after the struct :return: An asn1crypto.keys.PrivateKeyInfo or asn1crypto.keys.PublicKeyInfo object, based on the key_type param """ public_exponent_byte_length = native(int, blob_struct.cbPublicExp) modulus_byte_length = native(int, blob_struct.cbModulus) modulus_offset = public_exponent_byte_length public_exponent = int_from_bytes(blob[0:modulus_offset]) modulus = int_from_bytes(blob[modulus_offset:modulus_offset + modulus_byte_length]) if key_type == 'public': return PublicKeyInfo({ 'algorithm': PublicKeyAlgorithm({ 'algorithm': 'rsa', }), 'public_key': RSAPublicKey({ 'modulus': modulus, 'public_exponent': public_exponent, }), }) elif key_type == 'private': prime1_byte_length = native(int, blob_struct.cbPrime1) prime2_byte_length = native(int, blob_struct.cbPrime2) prime1_offset = modulus_offset + modulus_byte_length prime2_offset = prime1_offset + prime1_byte_length exponent1_offset = prime2_offset + prime2_byte_length exponent2_offset = exponent1_offset + prime2_byte_length coefficient_offset = exponent2_offset + prime2_byte_length private_exponent_offset = coefficient_offset + prime1_byte_length prime1 = int_from_bytes(blob[prime1_offset:prime2_offset]) prime2 = int_from_bytes(blob[prime2_offset:exponent1_offset]) exponent1 = int_from_bytes(blob[exponent1_offset:exponent2_offset]) exponent2 = int_from_bytes(blob[exponent2_offset:coefficient_offset]) coefficient = int_from_bytes(blob[coefficient_offset:private_exponent_offset]) private_exponent = int_from_bytes(blob[private_exponent_offset:private_exponent_offset + modulus_byte_length]) rsa_private_key = RSAPrivateKey({ 'version': 'two-prime', 'modulus': modulus, 'public_exponent': public_exponent, 'private_exponent': private_exponent, 'prime1': prime1, 'prime2': prime2, 'exponent1': exponent1, 'exponent2': exponent2, 'coefficient': coefficient, }) return PrivateKeyInfo({ 'version': 0, 'private_key_algorithm': PrivateKeyAlgorithm({ 'algorithm': 'rsa', }), 'private_key': rsa_private_key, }) else: raise ValueError(pretty_message( ''' key_type must be one of "public", "private", not %s ''', repr(key_type) )) def _bcrypt_interpret_dsa_key_blob(key_type, version, blob_struct, blob): """ Take a CNG BCRYPT_DSA_KEY_BLOB or BCRYPT_DSA_KEY_BLOB_V2 and converts it into an ASN.1 structure :param key_type: A unicode string of "private" or "public" :param version: An integer - 1 or 2, indicating the blob is BCRYPT_DSA_KEY_BLOB or BCRYPT_DSA_KEY_BLOB_V2 :param blob_struct: An instance of BCRYPT_DSA_KEY_BLOB or BCRYPT_DSA_KEY_BLOB_V2 :param blob: A byte string of the binary data contained after the struct :return: An asn1crypto.keys.PrivateKeyInfo or asn1crypto.keys.PublicKeyInfo object, based on the key_type param """ key_byte_length = native(int, blob_struct.cbKey) if version == 1: q = int_from_bytes(native(byte_cls, blob_struct.q)) g_offset = key_byte_length public_offset = g_offset + key_byte_length private_offset = public_offset + key_byte_length p = int_from_bytes(blob[0:g_offset]) g = int_from_bytes(blob[g_offset:public_offset]) elif version == 2: seed_byte_length = native(int, blob_struct.cbSeedLength) group_byte_length = native(int, blob_struct.cbGroupSize) q_offset = seed_byte_length p_offset = q_offset + group_byte_length g_offset = p_offset + key_byte_length public_offset = g_offset + key_byte_length private_offset = public_offset + key_byte_length # The seed is skipped since it is not part of the ASN.1 structure q = int_from_bytes(blob[q_offset:p_offset]) p = int_from_bytes(blob[p_offset:g_offset]) g = int_from_bytes(blob[g_offset:public_offset]) else: raise ValueError('version must be 1 or 2, not %s' % repr(version)) if key_type == 'public': public = int_from_bytes(blob[public_offset:private_offset]) return PublicKeyInfo({ 'algorithm': PublicKeyAlgorithm({ 'algorithm': 'dsa', 'parameters': DSAParams({ 'p': p, 'q': q, 'g': g, }) }), 'public_key': Integer(public), }) elif key_type == 'private': private = int_from_bytes(blob[private_offset:private_offset + key_byte_length]) return PrivateKeyInfo({ 'version': 0, 'private_key_algorithm': PrivateKeyAlgorithm({ 'algorithm': 'dsa', 'parameters': DSAParams({ 'p': p, 'q': q, 'g': g, }) }), 'private_key': Integer(private), }) else: raise ValueError(pretty_message( ''' key_type must be one of "public", "private", not %s ''', repr(key_type) )) def _bcrypt_interpret_ec_key_blob(key_type, blob_struct, blob): """ Take a CNG BCRYPT_ECCKEY_BLOB and converts it into an ASN.1 structure :param key_type: A unicode string of "private" or "public" :param blob_struct: An instance of BCRYPT_ECCKEY_BLOB :param blob: A byte string of the binary data contained after the struct :return: An asn1crypto.keys.PrivateKeyInfo or asn1crypto.keys.PublicKeyInfo object, based on the key_type param """ magic = native(int, blob_struct.dwMagic) key_byte_length = native(int, blob_struct.cbKey) curve = { BcryptConst.BCRYPT_ECDSA_PRIVATE_P256_MAGIC: 'secp256r1', BcryptConst.BCRYPT_ECDSA_PRIVATE_P384_MAGIC: 'secp384r1', BcryptConst.BCRYPT_ECDSA_PRIVATE_P521_MAGIC: 'secp521r1', BcryptConst.BCRYPT_ECDSA_PUBLIC_P256_MAGIC: 'secp256r1', BcryptConst.BCRYPT_ECDSA_PUBLIC_P384_MAGIC: 'secp384r1', BcryptConst.BCRYPT_ECDSA_PUBLIC_P521_MAGIC: 'secp521r1', }[magic] public = b'\x04' + blob[0:key_byte_length * 2] if key_type == 'public': return PublicKeyInfo({ 'algorithm': PublicKeyAlgorithm({ 'algorithm': 'ec', 'parameters': ECDomainParameters( name='named', value=curve ) }), 'public_key': public, }) elif key_type == 'private': private = int_from_bytes(blob[key_byte_length * 2:key_byte_length * 3]) return PrivateKeyInfo({ 'version': 0, 'private_key_algorithm': PrivateKeyAlgorithm({ 'algorithm': 'ec', 'parameters': ECDomainParameters( name='named', value=curve ) }), 'private_key': ECPrivateKey({ 'version': 'ecPrivkeyVer1', 'private_key': private, 'public_key': public, }), }) else: raise ValueError(pretty_message( ''' key_type must be one of "public", "private", not %s ''', repr(key_type) )) def load_certificate(source): """ Loads an x509 certificate into a Certificate object :param source: A byte string of file contents or a unicode string filename :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A Certificate object """ if isinstance(source, Asn1Certificate): certificate = source elif isinstance(source, byte_cls): certificate = parse_certificate(source) elif isinstance(source, str_cls): with open(source, 'rb') as f: certificate = parse_certificate(f.read()) else: raise TypeError(pretty_message( ''' source must be a byte string, unicode string or asn1crypto.x509.Certificate object, not %s ''', type_name(source) )) return _load_key(certificate, Certificate) def _load_key(key_object, container): """ Loads a certificate, public key or private key into a Certificate, PublicKey or PrivateKey object :param key_object: An asn1crypto.x509.Certificate, asn1crypto.keys.PublicKeyInfo or asn1crypto.keys.PrivateKeyInfo object :param container: The class of the object to hold the key_handle :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type oscrypto.errors.AsymmetricKeyError - when the key is incompatible with the OS crypto library OSError - when an error is returned by the OS crypto library :return: A PrivateKey, PublicKey or Certificate object, based on container """ key_info = key_object if isinstance(key_object, Asn1Certificate): key_info = key_object['tbs_certificate']['subject_public_key_info'] algo = key_info.algorithm curve_name = None if algo == 'ec': curve_type, curve_name = key_info.curve if curve_type != 'named': raise AsymmetricKeyError(pretty_message( ''' Windows only supports EC keys using named curves ''' )) if curve_name not in set(['secp256r1', 'secp384r1', 'secp521r1']): raise AsymmetricKeyError(pretty_message( ''' Windows only supports EC keys using the named curves secp256r1, secp384r1 and secp521r1 ''' )) elif algo == 'dsa': if key_info.hash_algo is None: raise IncompleteAsymmetricKeyError(pretty_message( ''' The DSA key does not contain the necessary p, q and g parameters and can not be used ''' )) elif key_info.bit_size > 1024 and (_win_version_info < (6, 2) or _backend == 'winlegacy'): raise AsymmetricKeyError(pretty_message( ''' Windows XP, 2003, Vista, 7 and Server 2008 only support DSA keys based on SHA1 (1024 bits or less) - this key is based on %s and is %s bits ''', key_info.hash_algo.upper(), key_info.bit_size )) elif key_info.bit_size == 2048 and key_info.hash_algo == 'sha1': raise AsymmetricKeyError(pretty_message( ''' Windows only supports 2048 bit DSA keys based on SHA2 - this key is 2048 bits and based on SHA1, a non-standard combination that is usually generated by old versions of OpenSSL ''' )) if _backend == 'winlegacy': if algo == 'ec': return container(None, key_object) return _advapi32_load_key(key_object, key_info, container) return _bcrypt_load_key(key_object, key_info, container, curve_name) def _advapi32_load_key(key_object, key_info, container): """ Loads a certificate, public key or private key into a Certificate, PublicKey or PrivateKey object via CryptoAPI :param key_object: An asn1crypto.x509.Certificate, asn1crypto.keys.PublicKeyInfo or asn1crypto.keys.PrivateKeyInfo object :param key_info: An asn1crypto.keys.PublicKeyInfo or asn1crypto.keys.PrivateKeyInfo object :param container: The class of the object to hold the key_handle :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type oscrypto.errors.AsymmetricKeyError - when the key is incompatible with the OS crypto library OSError - when an error is returned by the OS crypto library :return: A PrivateKey, PublicKey or Certificate object, based on container """ key_type = 'public' if isinstance(key_info, PublicKeyInfo) else 'private' algo = key_info.algorithm if algo == 'rsassa_pss': algo = 'rsa' if algo == 'rsa' or algo == 'rsassa_pss': provider = Advapi32Const.MS_ENH_RSA_AES_PROV else: provider = Advapi32Const.MS_ENH_DSS_DH_PROV context_handle = None key_handle = None try: context_handle = open_context_handle(provider, verify_only=key_type == 'public') blob = _advapi32_create_blob(key_info, key_type, algo) buffer_ = buffer_from_bytes(blob) key_handle_pointer = new(advapi32, 'HCRYPTKEY *') res = advapi32.CryptImportKey( context_handle, buffer_, len(blob), null(), 0, key_handle_pointer ) handle_error(res) key_handle = unwrap(key_handle_pointer) output = container(key_handle, key_object) output.context_handle = context_handle if algo == 'rsa': ex_blob = _advapi32_create_blob(key_info, key_type, algo, signing=False) ex_buffer = buffer_from_bytes(ex_blob) ex_key_handle_pointer = new(advapi32, 'HCRYPTKEY *') res = advapi32.CryptImportKey( context_handle, ex_buffer, len(ex_blob), null(), 0, ex_key_handle_pointer ) handle_error(res) output.ex_key_handle = unwrap(ex_key_handle_pointer) return output except (Exception): if key_handle: advapi32.CryptDestroyKey(key_handle) if context_handle: close_context_handle(context_handle) raise def _advapi32_create_blob(key_info, key_type, algo, signing=True): """ Generates a blob for importing a key to CryptoAPI :param key_info: An asn1crypto.keys.PublicKeyInfo or asn1crypto.keys.PrivateKeyInfo object :param key_type: A unicode string of "public" or "private" :param algo: A unicode string of "rsa" or "dsa" :param signing: If the key handle is for signing - may only be False for rsa keys :return: A byte string of a blob to pass to advapi32.CryptImportKey() """ if key_type == 'public': blob_type = Advapi32Const.PUBLICKEYBLOB else: blob_type = Advapi32Const.PRIVATEKEYBLOB if algo == 'rsa': struct_type = 'RSABLOBHEADER' if signing: algorithm_id = Advapi32Const.CALG_RSA_SIGN else: algorithm_id = Advapi32Const.CALG_RSA_KEYX else: struct_type = 'DSSBLOBHEADER' algorithm_id = Advapi32Const.CALG_DSS_SIGN blob_header_pointer = struct(advapi32, 'BLOBHEADER') blob_header = unwrap(blob_header_pointer) blob_header.bType = blob_type blob_header.bVersion = Advapi32Const.CUR_BLOB_VERSION blob_header.reserved = 0 blob_header.aiKeyAlg = algorithm_id blob_struct_pointer = struct(advapi32, struct_type) blob_struct = unwrap(blob_struct_pointer) blob_struct.publickeystruc = blob_header bit_size = key_info.bit_size len1 = bit_size // 8 len2 = bit_size // 16 if algo == 'rsa': pubkey_pointer = struct(advapi32, 'RSAPUBKEY') pubkey = unwrap(pubkey_pointer) pubkey.bitlen = bit_size if key_type == 'public': parsed_key_info = key_info['public_key'].parsed pubkey.magic = Advapi32Const.RSA1 pubkey.pubexp = parsed_key_info['public_exponent'].native blob_data = int_to_bytes(parsed_key_info['modulus'].native, signed=False, width=len1)[::-1] else: parsed_key_info = key_info['private_key'].parsed pubkey.magic = Advapi32Const.RSA2 pubkey.pubexp = parsed_key_info['public_exponent'].native blob_data = int_to_bytes(parsed_key_info['modulus'].native, signed=False, width=len1)[::-1] blob_data += int_to_bytes(parsed_key_info['prime1'].native, signed=False, width=len2)[::-1] blob_data += int_to_bytes(parsed_key_info['prime2'].native, signed=False, width=len2)[::-1] blob_data += int_to_bytes(parsed_key_info['exponent1'].native, signed=False, width=len2)[::-1] blob_data += int_to_bytes(parsed_key_info['exponent2'].native, signed=False, width=len2)[::-1] blob_data += int_to_bytes(parsed_key_info['coefficient'].native, signed=False, width=len2)[::-1] blob_data += int_to_bytes(parsed_key_info['private_exponent'].native, signed=False, width=len1)[::-1] blob_struct.rsapubkey = pubkey else: pubkey_pointer = struct(advapi32, 'DSSPUBKEY') pubkey = unwrap(pubkey_pointer) pubkey.bitlen = bit_size if key_type == 'public': pubkey.magic = Advapi32Const.DSS1 params = key_info['algorithm']['parameters'].native key_data = int_to_bytes(key_info['public_key'].parsed.native, signed=False, width=len1)[::-1] else: pubkey.magic = Advapi32Const.DSS2 params = key_info['private_key_algorithm']['parameters'].native key_data = int_to_bytes(key_info['private_key'].parsed.native, signed=False, width=20)[::-1] blob_struct.dsspubkey = pubkey blob_data = int_to_bytes(params['p'], signed=False, width=len1)[::-1] blob_data += int_to_bytes(params['q'], signed=False, width=20)[::-1] blob_data += int_to_bytes(params['g'], signed=False, width=len1)[::-1] blob_data += key_data dssseed_pointer = struct(advapi32, 'DSSSEED') dssseed = unwrap(dssseed_pointer) # This indicates no counter or seed info is available dssseed.counter = 0xffffffff blob_data += struct_bytes(dssseed_pointer) return struct_bytes(blob_struct_pointer) + blob_data def _bcrypt_load_key(key_object, key_info, container, curve_name): """ Loads a certificate, public key or private key into a Certificate, PublicKey or PrivateKey object via CNG :param key_object: An asn1crypto.x509.Certificate, asn1crypto.keys.PublicKeyInfo or asn1crypto.keys.PrivateKeyInfo object :param key_info: An asn1crypto.keys.PublicKeyInfo or asn1crypto.keys.PrivateKeyInfo object :param container: The class of the object to hold the key_handle :param curve_name: None or a unicode string of the curve name for an EC key :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type oscrypto.errors.AsymmetricKeyError - when the key is incompatible with the OS crypto library OSError - when an error is returned by the OS crypto library :return: A PrivateKey, PublicKey or Certificate object, based on container """ alg_handle = None key_handle = None key_type = 'public' if isinstance(key_info, PublicKeyInfo) else 'private' algo = key_info.algorithm if algo == 'rsassa_pss': algo = 'rsa' try: alg_selector = key_info.curve[1] if algo == 'ec' else algo alg_constant = { 'rsa': BcryptConst.BCRYPT_RSA_ALGORITHM, 'dsa': BcryptConst.BCRYPT_DSA_ALGORITHM, 'secp256r1': BcryptConst.BCRYPT_ECDSA_P256_ALGORITHM, 'secp384r1': BcryptConst.BCRYPT_ECDSA_P384_ALGORITHM, 'secp521r1': BcryptConst.BCRYPT_ECDSA_P521_ALGORITHM, }[alg_selector] alg_handle = open_alg_handle(alg_constant) if algo == 'rsa': if key_type == 'public': blob_type = BcryptConst.BCRYPT_RSAPUBLIC_BLOB magic = BcryptConst.BCRYPT_RSAPUBLIC_MAGIC parsed_key = key_info['public_key'].parsed prime1_size = 0 prime2_size = 0 else: blob_type = BcryptConst.BCRYPT_RSAFULLPRIVATE_BLOB magic = BcryptConst.BCRYPT_RSAFULLPRIVATE_MAGIC parsed_key = key_info['private_key'].parsed prime1 = int_to_bytes(parsed_key['prime1'].native) prime2 = int_to_bytes(parsed_key['prime2'].native) exponent1 = int_to_bytes(parsed_key['exponent1'].native) exponent2 = int_to_bytes(parsed_key['exponent2'].native) coefficient = int_to_bytes(parsed_key['coefficient'].native) private_exponent = int_to_bytes(parsed_key['private_exponent'].native) prime1_size = len(prime1) prime2_size = len(prime2) public_exponent = int_to_bytes(parsed_key['public_exponent'].native) modulus = int_to_bytes(parsed_key['modulus'].native) blob_struct_pointer = struct(bcrypt, 'BCRYPT_RSAKEY_BLOB') blob_struct = unwrap(blob_struct_pointer) blob_struct.Magic = magic blob_struct.BitLength = key_info.bit_size blob_struct.cbPublicExp = len(public_exponent) blob_struct.cbModulus = len(modulus) blob_struct.cbPrime1 = prime1_size blob_struct.cbPrime2 = prime2_size blob = struct_bytes(blob_struct_pointer) + public_exponent + modulus if key_type == 'private': blob += prime1 + prime2 blob += fill_width(exponent1, prime1_size) blob += fill_width(exponent2, prime2_size) blob += fill_width(coefficient, prime1_size) blob += fill_width(private_exponent, len(modulus)) elif algo == 'dsa': if key_type == 'public': blob_type = BcryptConst.BCRYPT_DSA_PUBLIC_BLOB public_key = key_info['public_key'].parsed.native params = key_info['algorithm']['parameters'] else: blob_type = BcryptConst.BCRYPT_DSA_PRIVATE_BLOB public_key = _unwrap_private_key_info(key_info)['public_key'].native private_bytes = int_to_bytes(key_info['private_key'].parsed.native) params = key_info['private_key_algorithm']['parameters'] public_bytes = int_to_bytes(public_key) p = int_to_bytes(params['p'].native) g = int_to_bytes(params['g'].native) q = int_to_bytes(params['q'].native) if key_info.bit_size > 1024: q_len = len(q) else: q_len = 20 key_width = max(len(public_bytes), len(g), len(p)) public_bytes = fill_width(public_bytes, key_width) p = fill_width(p, key_width) g = fill_width(g, key_width) q = fill_width(q, q_len) # We don't know the count or seed, so we set them to the max value # since setting them to 0 results in a parameter error count = b'\xff' * 4 seed = b'\xff' * q_len if key_info.bit_size > 1024: if key_type == 'public': magic = BcryptConst.BCRYPT_DSA_PUBLIC_MAGIC_V2 else: magic = BcryptConst.BCRYPT_DSA_PRIVATE_MAGIC_V2 blob_struct_pointer = struct(bcrypt, 'BCRYPT_DSA_KEY_BLOB_V2') blob_struct = unwrap(blob_struct_pointer) blob_struct.dwMagic = magic blob_struct.cbKey = key_width # We don't know if SHA256 was used here, but the output is long # enough for the generation of q for the supported 2048/224, # 2048/256 and 3072/256 FIPS approved pairs blob_struct.hashAlgorithm = BcryptConst.DSA_HASH_ALGORITHM_SHA256 blob_struct.standardVersion = BcryptConst.DSA_FIPS186_3 blob_struct.cbSeedLength = q_len blob_struct.cbGroupSize = q_len blob_struct.Count = byte_array(count) blob = struct_bytes(blob_struct_pointer) blob += seed + q + p + g + public_bytes if key_type == 'private': blob += fill_width(private_bytes, q_len) else: if key_type == 'public': magic = BcryptConst.BCRYPT_DSA_PUBLIC_MAGIC else: magic = BcryptConst.BCRYPT_DSA_PRIVATE_MAGIC blob_struct_pointer = struct(bcrypt, 'BCRYPT_DSA_KEY_BLOB') blob_struct = unwrap(blob_struct_pointer) blob_struct.dwMagic = magic blob_struct.cbKey = key_width blob_struct.Count = byte_array(count) blob_struct.Seed = byte_array(seed) blob_struct.q = byte_array(q) blob = struct_bytes(blob_struct_pointer) + p + g + public_bytes if key_type == 'private': blob += fill_width(private_bytes, q_len) elif algo == 'ec': if key_type == 'public': blob_type = BcryptConst.BCRYPT_ECCPUBLIC_BLOB x, y = key_info['public_key'].to_coords() else: blob_type = BcryptConst.BCRYPT_ECCPRIVATE_BLOB public_key = key_info['private_key'].parsed['public_key'] # We aren't guaranteed to get the public key coords with the # key info structure, but BCrypt doesn't seem to have an issue # importing the private key with 0 values, which can only be # presumed that it is generating the x and y points from the # private key value and base point if public_key: x, y = public_key.to_coords() else: x = 0 y = 0 private_bytes = int_to_bytes(key_info['private_key'].parsed['private_key'].native) blob_struct_pointer = struct(bcrypt, 'BCRYPT_ECCKEY_BLOB') blob_struct = unwrap(blob_struct_pointer) magic = { ('public', 'secp256r1'): BcryptConst.BCRYPT_ECDSA_PUBLIC_P256_MAGIC, ('public', 'secp384r1'): BcryptConst.BCRYPT_ECDSA_PUBLIC_P384_MAGIC, ('public', 'secp521r1'): BcryptConst.BCRYPT_ECDSA_PUBLIC_P521_MAGIC, ('private', 'secp256r1'): BcryptConst.BCRYPT_ECDSA_PRIVATE_P256_MAGIC, ('private', 'secp384r1'): BcryptConst.BCRYPT_ECDSA_PRIVATE_P384_MAGIC, ('private', 'secp521r1'): BcryptConst.BCRYPT_ECDSA_PRIVATE_P521_MAGIC, }[(key_type, curve_name)] key_width = { 'secp256r1': 32, 'secp384r1': 48, 'secp521r1': 66 }[curve_name] x_bytes = int_to_bytes(x) y_bytes = int_to_bytes(y) x_bytes = fill_width(x_bytes, key_width) y_bytes = fill_width(y_bytes, key_width) blob_struct.dwMagic = magic blob_struct.cbKey = key_width blob = struct_bytes(blob_struct_pointer) + x_bytes + y_bytes if key_type == 'private': blob += fill_width(private_bytes, key_width) key_handle_pointer = new(bcrypt, 'BCRYPT_KEY_HANDLE *') res = bcrypt.BCryptImportKeyPair( alg_handle, null(), blob_type, key_handle_pointer, blob, len(blob), BcryptConst.BCRYPT_NO_KEY_VALIDATION ) handle_error(res) key_handle = unwrap(key_handle_pointer) return container(key_handle, key_object) finally: if alg_handle: close_alg_handle(alg_handle) def load_private_key(source, password=None): """ Loads a private key into a PrivateKey object :param source: A byte string of file contents, a unicode string filename or an asn1crypto.keys.PrivateKeyInfo object :param password: A byte or unicode string to decrypt the private key file. Unicode strings will be encoded using UTF-8. Not used is the source is a PrivateKeyInfo object. :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type oscrypto.errors.AsymmetricKeyError - when the private key is incompatible with the OS crypto library OSError - when an error is returned by the OS crypto library :return: A PrivateKey object """ if isinstance(source, PrivateKeyInfo): private_object = source else: if password is not None: if isinstance(password, str_cls): password = password.encode('utf-8') if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) if isinstance(source, str_cls): with open(source, 'rb') as f: source = f.read() elif not isinstance(source, byte_cls): raise TypeError(pretty_message( ''' source must be a byte string, unicode string or asn1crypto.keys.PrivateKeyInfo object, not %s ''', type_name(source) )) private_object = parse_private(source, password) return _load_key(private_object, PrivateKey) def load_public_key(source): """ Loads a public key into a PublicKey object :param source: A byte string of file contents, a unicode string filename or an asn1crypto.keys.PublicKeyInfo object :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type oscrypto.errors.AsymmetricKeyError - when the public key is incompatible with the OS crypto library OSError - when an error is returned by the OS crypto library :return: A PublicKey object """ if isinstance(source, PublicKeyInfo): public_key = source elif isinstance(source, byte_cls): public_key = parse_public(source) elif isinstance(source, str_cls): with open(source, 'rb') as f: public_key = parse_public(f.read()) else: raise TypeError(pretty_message( ''' source must be a byte string, unicode string or asn1crypto.keys.PublicKeyInfo object, not %s ''', type_name(public_key) )) return _load_key(public_key, PublicKey) def parse_pkcs12(data, password=None): """ Parses a PKCS#12 ANS.1 DER-encoded structure and extracts certs and keys :param data: A byte string of a DER-encoded PKCS#12 file :param password: A byte string of the password to any encrypted data :raises: ValueError - when any of the parameters are of the wrong type or value OSError - when an error is returned by one of the OS decryption functions :return: A three-element tuple of: 1. An asn1crypto.keys.PrivateKeyInfo object 2. An asn1crypto.x509.Certificate object 3. A list of zero or more asn1crypto.x509.Certificate objects that are "extra" certificates, possibly intermediates from the cert chain """ return _parse_pkcs12(data, password, load_private_key) def load_pkcs12(source, password=None): """ Loads a .p12 or .pfx file into a PrivateKey object and one or more Certificates objects :param source: A byte string of file contents or a unicode string filename :param password: A byte or unicode string to decrypt the PKCS12 file. Unicode strings will be encoded using UTF-8. :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type oscrypto.errors.AsymmetricKeyError - when a contained key is incompatible with the OS crypto library OSError - when an error is returned by the OS crypto library :return: A three-element tuple containing (PrivateKey, Certificate, [Certificate, ...]) """ if password is not None: if isinstance(password, str_cls): password = password.encode('utf-8') if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) if isinstance(source, str_cls): with open(source, 'rb') as f: source = f.read() elif not isinstance(source, byte_cls): raise TypeError(pretty_message( ''' source must be a byte string or a unicode string, not %s ''', type_name(source) )) key_info, cert_info, extra_certs_info = parse_pkcs12(source, password) key = None cert = None if key_info: key = _load_key(key_info, PrivateKey) if cert_info: cert = _load_key(cert_info.public_key, Certificate) extra_certs = [_load_key(info.public_key, Certificate) for info in extra_certs_info] return (key, cert, extra_certs) def rsa_pkcs1v15_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies an RSASSA-PKCS-v1.5 signature. When the hash_algorithm is "raw", the operation is identical to RSA public key decryption. That is: the data is not hashed and no ASN.1 structure with an algorithm identifier of the hash algorithm is placed in the encrypted byte string. :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if certificate_or_public_key.algorithm != 'rsa': raise ValueError('The key specified is not an RSA public key') return _verify(certificate_or_public_key, signature, data, hash_algorithm) def rsa_pss_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies an RSASSA-PSS signature. For the PSS padding the mask gen algorithm will be mgf1 using the same hash algorithm as the signature. The salt length with be the length of the hash algorithm, and the trailer field with be the standard 0xBC byte. :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ cp_alg = certificate_or_public_key.algorithm if cp_alg != 'rsa' and cp_alg != 'rsassa_pss': raise ValueError('The key specified is not an RSA public key') return _verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding=True) def dsa_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies a DSA signature :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if certificate_or_public_key.algorithm != 'dsa': raise ValueError('The key specified is not a DSA public key') return _verify(certificate_or_public_key, signature, data, hash_algorithm) def ecdsa_verify(certificate_or_public_key, signature, data, hash_algorithm): """ Verifies an ECDSA signature :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512" :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if certificate_or_public_key.algorithm != 'ec': raise ValueError('The key specified is not an EC public key') return _verify(certificate_or_public_key, signature, data, hash_algorithm) def _verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding=False): """ Verifies an RSA, DSA or ECDSA signature :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw" :param rsa_pss_padding: If PSS padding should be used for RSA keys :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if not isinstance(certificate_or_public_key, (Certificate, PublicKey)): raise TypeError(pretty_message( ''' certificate_or_public_key must be an instance of the Certificate or PublicKey class, not %s ''', type_name(certificate_or_public_key) )) if not isinstance(signature, byte_cls): raise TypeError(pretty_message( ''' signature must be a byte string, not %s ''', type_name(signature) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) cp_alg = certificate_or_public_key.algorithm cp_is_rsa = cp_alg == 'rsa' or cp_alg == 'rsassa_pss' valid_hash_algorithms = set(['md5', 'sha1', 'sha256', 'sha384', 'sha512']) if cp_is_rsa and not rsa_pss_padding: valid_hash_algorithms |= set(['raw']) if hash_algorithm not in valid_hash_algorithms: valid_hash_algorithms_error = '"md5", "sha1", "sha256", "sha384", "sha512"' if cp_is_rsa and not rsa_pss_padding: valid_hash_algorithms_error += ', "raw"' raise ValueError(pretty_message( ''' hash_algorithm must be one of %s, not %s ''', valid_hash_algorithms_error, repr(hash_algorithm) )) if not cp_is_rsa and rsa_pss_padding is not False: raise ValueError(pretty_message( ''' PSS padding may only be used with RSA keys - signing via a %s key was requested ''', cp_alg.upper() )) if hash_algorithm == 'raw': if len(data) > certificate_or_public_key.byte_size - 11: raise ValueError(pretty_message( ''' data must be 11 bytes shorter than the key size when hash_algorithm is "raw" - key size is %s bytes, but data is %s bytes long ''', certificate_or_public_key.byte_size, len(data) )) if _backend == 'winlegacy': if certificate_or_public_key.algorithm == 'ec': return _pure_python_ecdsa_verify(certificate_or_public_key, signature, data, hash_algorithm) return _advapi32_verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding) return _bcrypt_verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding) def _advapi32_verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding=False): """ Verifies an RSA, DSA or ECDSA signature via CryptoAPI :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw" :param rsa_pss_padding: If PSS padding should be used for RSA keys :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ algo = certificate_or_public_key.algorithm algo_is_rsa = algo == 'rsa' or algo == 'rsassa_pss' if algo_is_rsa and rsa_pss_padding: hash_length = { 'sha1': 20, 'sha224': 28, 'sha256': 32, 'sha384': 48, 'sha512': 64 }.get(hash_algorithm, 0) decrypted_signature = raw_rsa_public_crypt(certificate_or_public_key, signature) key_size = certificate_or_public_key.bit_size if not verify_pss_padding(hash_algorithm, hash_length, key_size, data, decrypted_signature): raise SignatureError('Signature is invalid') return if algo_is_rsa and hash_algorithm == 'raw': padded_plaintext = raw_rsa_public_crypt(certificate_or_public_key, signature) try: plaintext = remove_pkcs1v15_signature_padding(certificate_or_public_key.byte_size, padded_plaintext) if not constant_compare(plaintext, data): raise ValueError() except (ValueError): raise SignatureError('Signature is invalid') return hash_handle = None try: alg_id = { 'md5': Advapi32Const.CALG_MD5, 'sha1': Advapi32Const.CALG_SHA1, 'sha256': Advapi32Const.CALG_SHA_256, 'sha384': Advapi32Const.CALG_SHA_384, 'sha512': Advapi32Const.CALG_SHA_512, }[hash_algorithm] hash_handle_pointer = new(advapi32, 'HCRYPTHASH *') res = advapi32.CryptCreateHash( certificate_or_public_key.context_handle, alg_id, null(), 0, hash_handle_pointer ) handle_error(res) hash_handle = unwrap(hash_handle_pointer) res = advapi32.CryptHashData(hash_handle, data, len(data), 0) handle_error(res) if algo == 'dsa': # Windows doesn't use the ASN.1 Sequence for DSA signatures, # so we have to convert it here for the verification to work try: signature = DSASignature.load(signature).to_p1363() # Switch the two integers so that the reversal later will # result in the correct order half_len = len(signature) // 2 signature = signature[half_len:] + signature[:half_len] except (ValueError, OverflowError, TypeError): raise SignatureError('Signature is invalid') # The CryptoAPI expects signatures to be in little endian byte order, # which is the opposite of other systems, so we must reverse it reversed_signature = signature[::-1] res = advapi32.CryptVerifySignatureW( hash_handle, reversed_signature, len(signature), certificate_or_public_key.key_handle, null(), 0 ) handle_error(res) finally: if hash_handle: advapi32.CryptDestroyHash(hash_handle) def _bcrypt_verify(certificate_or_public_key, signature, data, hash_algorithm, rsa_pss_padding=False): """ Verifies an RSA, DSA or ECDSA signature via CNG :param certificate_or_public_key: A Certificate or PublicKey instance to verify the signature with :param signature: A byte string of the signature to verify :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw" :param rsa_pss_padding: If PSS padding should be used for RSA keys :raises: oscrypto.errors.SignatureError - when the signature is determined to be invalid ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if hash_algorithm == 'raw': digest = data else: hash_constant = { 'md5': BcryptConst.BCRYPT_MD5_ALGORITHM, 'sha1': BcryptConst.BCRYPT_SHA1_ALGORITHM, 'sha256': BcryptConst.BCRYPT_SHA256_ALGORITHM, 'sha384': BcryptConst.BCRYPT_SHA384_ALGORITHM, 'sha512': BcryptConst.BCRYPT_SHA512_ALGORITHM }[hash_algorithm] digest = getattr(hashlib, hash_algorithm)(data).digest() padding_info = null() flags = 0 cp_alg = certificate_or_public_key.algorithm cp_is_rsa = cp_alg == 'rsa' or cp_alg == 'rsassa_pss' if cp_is_rsa: if rsa_pss_padding: flags = BcryptConst.BCRYPT_PAD_PSS padding_info_struct_pointer = struct(bcrypt, 'BCRYPT_PSS_PADDING_INFO') padding_info_struct = unwrap(padding_info_struct_pointer) # This has to be assigned to a variable to prevent cffi from gc'ing it hash_buffer = buffer_from_unicode(hash_constant) padding_info_struct.pszAlgId = cast(bcrypt, 'wchar_t *', hash_buffer) padding_info_struct.cbSalt = len(digest) else: flags = BcryptConst.BCRYPT_PAD_PKCS1 padding_info_struct_pointer = struct(bcrypt, 'BCRYPT_PKCS1_PADDING_INFO') padding_info_struct = unwrap(padding_info_struct_pointer) # This has to be assigned to a variable to prevent cffi from gc'ing it if hash_algorithm == 'raw': padding_info_struct.pszAlgId = null() else: hash_buffer = buffer_from_unicode(hash_constant) padding_info_struct.pszAlgId = cast(bcrypt, 'wchar_t *', hash_buffer) padding_info = cast(bcrypt, 'void *', padding_info_struct_pointer) else: # Windows doesn't use the ASN.1 Sequence for DSA/ECDSA signatures, # so we have to convert it here for the verification to work try: signature = DSASignature.load(signature).to_p1363() except (ValueError, OverflowError, TypeError): raise SignatureError('Signature is invalid') res = bcrypt.BCryptVerifySignature( certificate_or_public_key.key_handle, padding_info, digest, len(digest), signature, len(signature), flags ) failure = res == BcryptConst.STATUS_INVALID_SIGNATURE failure = failure or res == BcryptConst.STATUS_INVALID_PARAMETER if failure: raise SignatureError('Signature is invalid') handle_error(res) def rsa_pkcs1v15_sign(private_key, data, hash_algorithm): """ Generates an RSASSA-PKCS-v1.5 signature. When the hash_algorithm is "raw", the operation is identical to RSA private key encryption. That is: the data is not hashed and no ASN.1 structure with an algorithm identifier of the hash algorithm is placed in the encrypted byte string. :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if private_key.algorithm != 'rsa': raise ValueError('The key specified is not an RSA private key') return _sign(private_key, data, hash_algorithm) def rsa_pss_sign(private_key, data, hash_algorithm): """ Generates an RSASSA-PSS signature. For the PSS padding the mask gen algorithm will be mgf1 using the same hash algorithm as the signature. The salt length with be the length of the hash algorithm, and the trailer field with be the standard 0xBC byte. :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ pkey_alg = private_key.algorithm if pkey_alg != 'rsa' and pkey_alg != 'rsassa_pss': raise ValueError('The key specified is not an RSA private key') return _sign(private_key, data, hash_algorithm, rsa_pss_padding=True) def dsa_sign(private_key, data, hash_algorithm): """ Generates a DSA signature :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if private_key.algorithm != 'dsa': raise ValueError('The key specified is not a DSA private key') return _sign(private_key, data, hash_algorithm) def ecdsa_sign(private_key, data, hash_algorithm): """ Generates an ECDSA signature :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384" or "sha512" :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if private_key.algorithm != 'ec': raise ValueError('The key specified is not an EC private key') return _sign(private_key, data, hash_algorithm) def _sign(private_key, data, hash_algorithm, rsa_pss_padding=False): """ Generates an RSA, DSA or ECDSA signature :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw" :param rsa_pss_padding: If PSS padding should be used for RSA keys :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if not isinstance(private_key, PrivateKey): raise TypeError(pretty_message( ''' private_key must be an instance of PrivateKey, not %s ''', type_name(private_key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) pkey_alg = private_key.algorithm pkey_is_rsa = pkey_alg == 'rsa' or pkey_alg == 'rsassa_pss' valid_hash_algorithms = set(['md5', 'sha1', 'sha256', 'sha384', 'sha512']) if private_key.algorithm == 'rsa' and not rsa_pss_padding: valid_hash_algorithms |= set(['raw']) if hash_algorithm not in valid_hash_algorithms: valid_hash_algorithms_error = '"md5", "sha1", "sha256", "sha384", "sha512"' if pkey_is_rsa and not rsa_pss_padding: valid_hash_algorithms_error += ', "raw"' raise ValueError(pretty_message( ''' hash_algorithm must be one of %s, not %s ''', valid_hash_algorithms_error, repr(hash_algorithm) )) if not pkey_is_rsa and rsa_pss_padding is not False: raise ValueError(pretty_message( ''' PSS padding may only be used with RSA keys - signing via a %s key was requested ''', pkey_alg.upper() )) if hash_algorithm == 'raw': if len(data) > private_key.byte_size - 11: raise ValueError(pretty_message( ''' data must be 11 bytes shorter than the key size when hash_algorithm is "raw" - key size is %s bytes, but data is %s bytes long ''', private_key.byte_size, len(data) )) if _backend == 'winlegacy': if private_key.algorithm == 'ec': return _pure_python_ecdsa_sign(private_key, data, hash_algorithm) return _advapi32_sign(private_key, data, hash_algorithm, rsa_pss_padding) return _bcrypt_sign(private_key, data, hash_algorithm, rsa_pss_padding) def _advapi32_sign(private_key, data, hash_algorithm, rsa_pss_padding=False): """ Generates an RSA, DSA or ECDSA signature via CryptoAPI :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw" :param rsa_pss_padding: If PSS padding should be used for RSA keys :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ algo = private_key.algorithm algo_is_rsa = algo == 'rsa' or algo == 'rsassa_pss' if algo_is_rsa and hash_algorithm == 'raw': padded_data = add_pkcs1v15_signature_padding(private_key.byte_size, data) return raw_rsa_private_crypt(private_key, padded_data) if algo_is_rsa and rsa_pss_padding: hash_length = { 'sha1': 20, 'sha224': 28, 'sha256': 32, 'sha384': 48, 'sha512': 64 }.get(hash_algorithm, 0) padded_data = add_pss_padding(hash_algorithm, hash_length, private_key.bit_size, data) return raw_rsa_private_crypt(private_key, padded_data) if private_key.algorithm == 'dsa' and hash_algorithm == 'md5': raise ValueError(pretty_message( ''' Windows does not support md5 signatures with DSA keys ''' )) hash_handle = None try: alg_id = { 'md5': Advapi32Const.CALG_MD5, 'sha1': Advapi32Const.CALG_SHA1, 'sha256': Advapi32Const.CALG_SHA_256, 'sha384': Advapi32Const.CALG_SHA_384, 'sha512': Advapi32Const.CALG_SHA_512, }[hash_algorithm] hash_handle_pointer = new(advapi32, 'HCRYPTHASH *') res = advapi32.CryptCreateHash( private_key.context_handle, alg_id, null(), 0, hash_handle_pointer ) handle_error(res) hash_handle = unwrap(hash_handle_pointer) res = advapi32.CryptHashData(hash_handle, data, len(data), 0) handle_error(res) out_len = new(advapi32, 'DWORD *') res = advapi32.CryptSignHashW( hash_handle, Advapi32Const.AT_SIGNATURE, null(), 0, null(), out_len ) handle_error(res) buffer_length = deref(out_len) buffer_ = buffer_from_bytes(buffer_length) res = advapi32.CryptSignHashW( hash_handle, Advapi32Const.AT_SIGNATURE, null(), 0, buffer_, out_len ) handle_error(res) output = bytes_from_buffer(buffer_, deref(out_len)) # CryptoAPI outputs the signature in little endian byte order, so we # must swap it for compatibility with other systems output = output[::-1] if algo == 'dsa': # Switch the two integers because the reversal just before switched # then half_len = len(output) // 2 output = output[half_len:] + output[:half_len] # Windows doesn't use the ASN.1 Sequence for DSA signatures, # so we have to convert it here for the verification to work output = DSASignature.from_p1363(output).dump() return output finally: if hash_handle: advapi32.CryptDestroyHash(hash_handle) def _bcrypt_sign(private_key, data, hash_algorithm, rsa_pss_padding=False): """ Generates an RSA, DSA or ECDSA signature via CNG :param private_key: The PrivateKey to generate the signature with :param data: A byte string of the data the signature is for :param hash_algorithm: A unicode string of "md5", "sha1", "sha256", "sha384", "sha512" or "raw" :param rsa_pss_padding: If PSS padding should be used for RSA keys :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the signature """ if hash_algorithm == 'raw': digest = data else: hash_constant = { 'md5': BcryptConst.BCRYPT_MD5_ALGORITHM, 'sha1': BcryptConst.BCRYPT_SHA1_ALGORITHM, 'sha256': BcryptConst.BCRYPT_SHA256_ALGORITHM, 'sha384': BcryptConst.BCRYPT_SHA384_ALGORITHM, 'sha512': BcryptConst.BCRYPT_SHA512_ALGORITHM }[hash_algorithm] digest = getattr(hashlib, hash_algorithm)(data).digest() padding_info = null() flags = 0 pkey_alg = private_key.algorithm pkey_is_rsa = pkey_alg == 'rsa' or pkey_alg == 'rsassa_pss' if pkey_is_rsa: if rsa_pss_padding: hash_length = { 'md5': 16, 'sha1': 20, 'sha256': 32, 'sha384': 48, 'sha512': 64 }[hash_algorithm] flags = BcryptConst.BCRYPT_PAD_PSS padding_info_struct_pointer = struct(bcrypt, 'BCRYPT_PSS_PADDING_INFO') padding_info_struct = unwrap(padding_info_struct_pointer) # This has to be assigned to a variable to prevent cffi from gc'ing it hash_buffer = buffer_from_unicode(hash_constant) padding_info_struct.pszAlgId = cast(bcrypt, 'wchar_t *', hash_buffer) padding_info_struct.cbSalt = hash_length else: flags = BcryptConst.BCRYPT_PAD_PKCS1 padding_info_struct_pointer = struct(bcrypt, 'BCRYPT_PKCS1_PADDING_INFO') padding_info_struct = unwrap(padding_info_struct_pointer) # This has to be assigned to a variable to prevent cffi from gc'ing it if hash_algorithm == 'raw': padding_info_struct.pszAlgId = null() else: hash_buffer = buffer_from_unicode(hash_constant) padding_info_struct.pszAlgId = cast(bcrypt, 'wchar_t *', hash_buffer) padding_info = cast(bcrypt, 'void *', padding_info_struct_pointer) if pkey_alg == 'dsa' and private_key.bit_size > 1024 and hash_algorithm in set(['md5', 'sha1']): raise ValueError(pretty_message( ''' Windows does not support sha1 signatures with DSA keys based on sha224, sha256 or sha512 ''' )) out_len = new(bcrypt, 'DWORD *') res = bcrypt.BCryptSignHash( private_key.key_handle, padding_info, digest, len(digest), null(), 0, out_len, flags ) handle_error(res) buffer_len = deref(out_len) buffer = buffer_from_bytes(buffer_len) if pkey_is_rsa: padding_info = cast(bcrypt, 'void *', padding_info_struct_pointer) res = bcrypt.BCryptSignHash( private_key.key_handle, padding_info, digest, len(digest), buffer, buffer_len, out_len, flags ) handle_error(res) signature = bytes_from_buffer(buffer, deref(out_len)) if not pkey_is_rsa: # Windows doesn't use the ASN.1 Sequence for DSA/ECDSA signatures, # so we have to convert it here for the verification to work signature = DSASignature.from_p1363(signature).dump() return signature def _encrypt(certificate_or_public_key, data, rsa_oaep_padding=False): """ Encrypts a value using an RSA public key :param certificate_or_public_key: A Certificate or PublicKey instance to encrypt with :param data: A byte string of the data to encrypt :param rsa_oaep_padding: If OAEP padding should be used instead of PKCS#1 v1.5 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the ciphertext """ if not isinstance(certificate_or_public_key, (Certificate, PublicKey)): raise TypeError(pretty_message( ''' certificate_or_public_key must be an instance of the Certificate or PublicKey class, not %s ''', type_name(certificate_or_public_key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if not isinstance(rsa_oaep_padding, bool): raise TypeError(pretty_message( ''' rsa_oaep_padding must be a bool, not %s ''', type_name(rsa_oaep_padding) )) if _backend == 'winlegacy': return _advapi32_encrypt(certificate_or_public_key, data, rsa_oaep_padding) return _bcrypt_encrypt(certificate_or_public_key, data, rsa_oaep_padding) def _advapi32_encrypt(certificate_or_public_key, data, rsa_oaep_padding=False): """ Encrypts a value using an RSA public key via CryptoAPI :param certificate_or_public_key: A Certificate or PublicKey instance to encrypt with :param data: A byte string of the data to encrypt :param rsa_oaep_padding: If OAEP padding should be used instead of PKCS#1 v1.5 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the ciphertext """ flags = 0 if rsa_oaep_padding: flags = Advapi32Const.CRYPT_OAEP out_len = new(advapi32, 'DWORD *', len(data)) res = advapi32.CryptEncrypt( certificate_or_public_key.ex_key_handle, null(), True, flags, null(), out_len, 0 ) handle_error(res) buffer_len = deref(out_len) buffer = buffer_from_bytes(buffer_len) write_to_buffer(buffer, data) pointer_set(out_len, len(data)) res = advapi32.CryptEncrypt( certificate_or_public_key.ex_key_handle, null(), True, flags, buffer, out_len, buffer_len ) handle_error(res) return bytes_from_buffer(buffer, deref(out_len))[::-1] def _bcrypt_encrypt(certificate_or_public_key, data, rsa_oaep_padding=False): """ Encrypts a value using an RSA public key via CNG :param certificate_or_public_key: A Certificate or PublicKey instance to encrypt with :param data: A byte string of the data to encrypt :param rsa_oaep_padding: If OAEP padding should be used instead of PKCS#1 v1.5 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the ciphertext """ flags = BcryptConst.BCRYPT_PAD_PKCS1 if rsa_oaep_padding is True: flags = BcryptConst.BCRYPT_PAD_OAEP padding_info_struct_pointer = struct(bcrypt, 'BCRYPT_OAEP_PADDING_INFO') padding_info_struct = unwrap(padding_info_struct_pointer) # This has to be assigned to a variable to prevent cffi from gc'ing it hash_buffer = buffer_from_unicode(BcryptConst.BCRYPT_SHA1_ALGORITHM) padding_info_struct.pszAlgId = cast(bcrypt, 'wchar_t *', hash_buffer) padding_info_struct.pbLabel = null() padding_info_struct.cbLabel = 0 padding_info = cast(bcrypt, 'void *', padding_info_struct_pointer) else: padding_info = null() out_len = new(bcrypt, 'ULONG *') res = bcrypt.BCryptEncrypt( certificate_or_public_key.key_handle, data, len(data), padding_info, null(), 0, null(), 0, out_len, flags ) handle_error(res) buffer_len = deref(out_len) buffer = buffer_from_bytes(buffer_len) res = bcrypt.BCryptEncrypt( certificate_or_public_key.key_handle, data, len(data), padding_info, null(), 0, buffer, buffer_len, out_len, flags ) handle_error(res) return bytes_from_buffer(buffer, deref(out_len)) def _decrypt(private_key, ciphertext, rsa_oaep_padding=False): """ Encrypts a value using an RSA private key :param private_key: A PrivateKey instance to decrypt with :param ciphertext: A byte string of the data to decrypt :param rsa_oaep_padding: If OAEP padding should be used instead of PKCS#1 v1.5 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if not isinstance(private_key, PrivateKey): raise TypeError(pretty_message( ''' private_key must be an instance of the PrivateKey class, not %s ''', type_name(private_key) )) if not isinstance(ciphertext, byte_cls): raise TypeError(pretty_message( ''' ciphertext must be a byte string, not %s ''', type_name(ciphertext) )) if not isinstance(rsa_oaep_padding, bool): raise TypeError(pretty_message( ''' rsa_oaep_padding must be a bool, not %s ''', type_name(rsa_oaep_padding) )) if _backend == 'winlegacy': return _advapi32_decrypt(private_key, ciphertext, rsa_oaep_padding) return _bcrypt_decrypt(private_key, ciphertext, rsa_oaep_padding) def _advapi32_decrypt(private_key, ciphertext, rsa_oaep_padding=False): """ Encrypts a value using an RSA private key via CryptoAPI :param private_key: A PrivateKey instance to decrypt with :param ciphertext: A byte string of the data to decrypt :param rsa_oaep_padding: If OAEP padding should be used instead of PKCS#1 v1.5 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ flags = 0 if rsa_oaep_padding: flags = Advapi32Const.CRYPT_OAEP ciphertext = ciphertext[::-1] buffer = buffer_from_bytes(ciphertext) out_len = new(advapi32, 'DWORD *', len(ciphertext)) res = advapi32.CryptDecrypt( private_key.ex_key_handle, null(), True, flags, buffer, out_len ) handle_error(res) return bytes_from_buffer(buffer, deref(out_len)) def _bcrypt_decrypt(private_key, ciphertext, rsa_oaep_padding=False): """ Encrypts a value using an RSA private key via CNG :param private_key: A PrivateKey instance to decrypt with :param ciphertext: A byte string of the data to decrypt :param rsa_oaep_padding: If OAEP padding should be used instead of PKCS#1 v1.5 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ flags = BcryptConst.BCRYPT_PAD_PKCS1 if rsa_oaep_padding is True: flags = BcryptConst.BCRYPT_PAD_OAEP padding_info_struct_pointer = struct(bcrypt, 'BCRYPT_OAEP_PADDING_INFO') padding_info_struct = unwrap(padding_info_struct_pointer) # This has to be assigned to a variable to prevent cffi from gc'ing it hash_buffer = buffer_from_unicode(BcryptConst.BCRYPT_SHA1_ALGORITHM) padding_info_struct.pszAlgId = cast(bcrypt, 'wchar_t *', hash_buffer) padding_info_struct.pbLabel = null() padding_info_struct.cbLabel = 0 padding_info = cast(bcrypt, 'void *', padding_info_struct_pointer) else: padding_info = null() out_len = new(bcrypt, 'ULONG *') res = bcrypt.BCryptDecrypt( private_key.key_handle, ciphertext, len(ciphertext), padding_info, null(), 0, null(), 0, out_len, flags ) handle_error(res) buffer_len = deref(out_len) buffer = buffer_from_bytes(buffer_len) res = bcrypt.BCryptDecrypt( private_key.key_handle, ciphertext, len(ciphertext), padding_info, null(), 0, buffer, buffer_len, out_len, flags ) handle_error(res) return bytes_from_buffer(buffer, deref(out_len)) def rsa_pkcs1v15_encrypt(certificate_or_public_key, data): """ Encrypts a byte string using an RSA public key or certificate. Uses PKCS#1 v1.5 padding. :param certificate_or_public_key: A PublicKey or Certificate object :param data: A byte string, with a maximum length 11 bytes less than the key length (in bytes) :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the encrypted data """ return _encrypt(certificate_or_public_key, data) def rsa_pkcs1v15_decrypt(private_key, ciphertext): """ Decrypts a byte string using an RSA private key. Uses PKCS#1 v1.5 padding. :param private_key: A PrivateKey object :param ciphertext: A byte string of the encrypted data :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the original plaintext """ return _decrypt(private_key, ciphertext) def rsa_oaep_encrypt(certificate_or_public_key, data): """ Encrypts a byte string using an RSA public key or certificate. Uses PKCS#1 OAEP padding with SHA1. :param certificate_or_public_key: A PublicKey or Certificate object :param data: A byte string, with a maximum length 41 bytes (or more) less than the key length (in bytes) :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the encrypted data """ return _encrypt(certificate_or_public_key, data, rsa_oaep_padding=True) def rsa_oaep_decrypt(private_key, ciphertext): """ Decrypts a byte string using an RSA private key. Uses PKCS#1 OAEP padding with SHA1. :param private_key: A PrivateKey object :param ciphertext: A byte string of the encrypted data :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the original plaintext """ return _decrypt(private_key, ciphertext, rsa_oaep_padding=True) oscrypto-1.3.0/oscrypto/_win/symmetric.py000066400000000000000000000742331421476274700206300ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .._errors import pretty_message from .._ffi import ( buffer_from_bytes, bytes_from_buffer, deref, new, null, pointer_set, struct, struct_bytes, unwrap, write_to_buffer, ) from .util import rand_bytes from .. import backend from .._types import type_name, byte_cls _backend = backend() if _backend == 'winlegacy': from ._advapi32 import advapi32, Advapi32Const, handle_error, open_context_handle, close_context_handle else: from ._cng import bcrypt, BcryptConst, handle_error, open_alg_handle, close_alg_handle __all__ = [ 'aes_cbc_no_padding_decrypt', 'aes_cbc_no_padding_encrypt', 'aes_cbc_pkcs7_decrypt', 'aes_cbc_pkcs7_encrypt', 'des_cbc_pkcs5_decrypt', 'des_cbc_pkcs5_encrypt', 'rc2_cbc_pkcs5_decrypt', 'rc2_cbc_pkcs5_encrypt', 'rc4_decrypt', 'rc4_encrypt', 'tripledes_cbc_pkcs5_decrypt', 'tripledes_cbc_pkcs5_encrypt', ] def aes_cbc_no_padding_encrypt(key, data, iv): """ Encrypts plaintext using AES in CBC mode with a 128, 192 or 256 bit key and no padding. This means the ciphertext must be an exact multiple of 16 bytes long. :param key: The encryption key - a byte string either 16, 24 or 32 bytes long :param data: The plaintext - a byte string :param iv: The initialization vector - either a byte string 16-bytes long or None to generate an IV :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A tuple of two byte strings (iv, ciphertext) """ if len(key) not in [16, 24, 32]: raise ValueError(pretty_message( ''' key must be either 16, 24 or 32 bytes (128, 192 or 256 bits) long - is %s ''', len(key) )) if not iv: iv = rand_bytes(16) elif len(iv) != 16: raise ValueError(pretty_message( ''' iv must be 16 bytes long - is %s ''', len(iv) )) if len(data) % 16 != 0: raise ValueError(pretty_message( ''' data must be a multiple of 16 bytes long - is %s ''', len(data) )) return (iv, _encrypt('aes', key, data, iv, False)) def aes_cbc_no_padding_decrypt(key, data, iv): """ Decrypts AES ciphertext in CBC mode using a 128, 192 or 256 bit key and no padding. :param key: The encryption key - a byte string either 16, 24 or 32 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string 16-bytes long :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if len(key) not in [16, 24, 32]: raise ValueError(pretty_message( ''' key must be either 16, 24 or 32 bytes (128, 192 or 256 bits) long - is %s ''', len(key) )) if len(iv) != 16: raise ValueError(pretty_message( ''' iv must be 16 bytes long - is %s ''', len(iv) )) return _decrypt('aes', key, data, iv, False) def aes_cbc_pkcs7_encrypt(key, data, iv): """ Encrypts plaintext using AES in CBC mode with a 128, 192 or 256 bit key and PKCS#7 padding. :param key: The encryption key - a byte string either 16, 24 or 32 bytes long :param data: The plaintext - a byte string :param iv: The initialization vector - either a byte string 16-bytes long or None to generate an IV :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A tuple of two byte strings (iv, ciphertext) """ if len(key) not in [16, 24, 32]: raise ValueError(pretty_message( ''' key must be either 16, 24 or 32 bytes (128, 192 or 256 bits) long - is %s ''', len(key) )) if not iv: iv = rand_bytes(16) elif len(iv) != 16: raise ValueError(pretty_message( ''' iv must be 16 bytes long - is %s ''', len(iv) )) return (iv, _encrypt('aes', key, data, iv, True)) def aes_cbc_pkcs7_decrypt(key, data, iv): """ Decrypts AES ciphertext in CBC mode using a 128, 192 or 256 bit key :param key: The encryption key - a byte string either 16, 24 or 32 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string 16-bytes long :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if len(key) not in [16, 24, 32]: raise ValueError(pretty_message( ''' key must be either 16, 24 or 32 bytes (128, 192 or 256 bits) long - is %s ''', len(key) )) if len(iv) != 16: raise ValueError(pretty_message( ''' iv must be 16 bytes long - is %s ''', len(iv) )) return _decrypt('aes', key, data, iv, True) def rc4_encrypt(key, data): """ Encrypts plaintext using RC4 with a 40-128 bit key :param key: The encryption key - a byte string 5-16 bytes long :param data: The plaintext - a byte string :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the ciphertext """ if len(key) < 5 or len(key) > 16: raise ValueError(pretty_message( ''' key must be 5 to 16 bytes (40 to 128 bits) long - is %s ''', len(key) )) return _encrypt('rc4', key, data, None, None) def rc4_decrypt(key, data): """ Decrypts RC4 ciphertext using a 40-128 bit key :param key: The encryption key - a byte string 5-16 bytes long :param data: The ciphertext - a byte string :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if len(key) < 5 or len(key) > 16: raise ValueError(pretty_message( ''' key must be 5 to 16 bytes (40 to 128 bits) long - is %s ''', len(key) )) return _decrypt('rc4', key, data, None, None) def rc2_cbc_pkcs5_encrypt(key, data, iv): """ Encrypts plaintext using RC2 with a 64 bit key :param key: The encryption key - a byte string 8 bytes long :param data: The plaintext - a byte string :param iv: The 8-byte initialization vector to use - a byte string - set as None to generate an appropriate one :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A tuple of two byte strings (iv, ciphertext) """ if len(key) < 5 or len(key) > 16: raise ValueError(pretty_message( ''' key must be 5 to 16 bytes (40 to 128 bits) long - is %s ''', len(key) )) if not iv: iv = rand_bytes(8) elif len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) return (iv, _encrypt('rc2', key, data, iv, True)) def rc2_cbc_pkcs5_decrypt(key, data, iv): """ Decrypts RC2 ciphertext using a 64 bit key :param key: The encryption key - a byte string 8 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector used for encryption - a byte string :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if len(key) < 5 or len(key) > 16: raise ValueError(pretty_message( ''' key must be 5 to 16 bytes (40 to 128 bits) long - is %s ''', len(key) )) if len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) return _decrypt('rc2', key, data, iv, True) def tripledes_cbc_pkcs5_encrypt(key, data, iv): """ Encrypts plaintext using 3DES in either 2 or 3 key mode :param key: The encryption key - a byte string 16 or 24 bytes long (2 or 3 key mode) :param data: The plaintext - a byte string :param iv: The 8-byte initialization vector to use - a byte string - set as None to generate an appropriate one :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A tuple of two byte strings (iv, ciphertext) """ if len(key) != 16 and len(key) != 24: raise ValueError(pretty_message( ''' key must be 16 bytes (2 key) or 24 bytes (3 key) long - is %s ''', len(key) )) if not iv: iv = rand_bytes(8) elif len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) cipher = 'tripledes_3key' if len(key) == 16: cipher = 'tripledes_2key' return (iv, _encrypt(cipher, key, data, iv, True)) def tripledes_cbc_pkcs5_decrypt(key, data, iv): """ Decrypts 3DES ciphertext in either 2 or 3 key mode :param key: The encryption key - a byte string 16 or 24 bytes long (2 or 3 key mode) :param data: The ciphertext - a byte string :param iv: The initialization vector used for encryption - a byte string :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if len(key) != 16 and len(key) != 24: raise ValueError(pretty_message( ''' key must be 16 bytes (2 key) or 24 bytes (3 key) long - is %s ''', len(key) )) if len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) cipher = 'tripledes_3key' if len(key) == 16: cipher = 'tripledes_2key' return _decrypt(cipher, key, data, iv, True) def des_cbc_pkcs5_encrypt(key, data, iv): """ Encrypts plaintext using DES with a 56 bit key :param key: The encryption key - a byte string 8 bytes long (includes error correction bits) :param data: The plaintext - a byte string :param iv: The 8-byte initialization vector to use - a byte string - set as None to generate an appropriate one :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A tuple of two byte strings (iv, ciphertext) """ if len(key) != 8: raise ValueError(pretty_message( ''' key must be 8 bytes (56 bits + 8 parity bits) long - is %s ''', len(key) )) if not iv: iv = rand_bytes(8) elif len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) return (iv, _encrypt('des', key, data, iv, True)) def des_cbc_pkcs5_decrypt(key, data, iv): """ Decrypts DES ciphertext using a 56 bit key :param key: The encryption key - a byte string 8 bytes long (includes error correction bits) :param data: The ciphertext - a byte string :param iv: The initialization vector used for encryption - a byte string :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if len(key) != 8: raise ValueError(pretty_message( ''' key must be 8 bytes (56 bits + 8 parity bits) long - is %s ''', len(key) )) if len(iv) != 8: raise ValueError(pretty_message( ''' iv must be 8 bytes long - is %s ''', len(iv) )) return _decrypt('des', key, data, iv, True) def _advapi32_create_handles(cipher, key, iv): """ Creates an HCRYPTPROV and HCRYPTKEY for symmetric encryption/decryption. The HCRYPTPROV must be released by close_context_handle() and the HCRYPTKEY must be released by advapi32.CryptDestroyKey() when done. :param cipher: A unicode string of "aes", "des", "tripledes_2key", "tripledes_3key", "rc2", "rc4" :param key: A byte string of the symmetric key :param iv: The initialization vector - a byte string - unused for RC4 :return: A tuple of (HCRYPTPROV, HCRYPTKEY) """ context_handle = None if cipher == 'aes': algorithm_id = { 16: Advapi32Const.CALG_AES_128, 24: Advapi32Const.CALG_AES_192, 32: Advapi32Const.CALG_AES_256, }[len(key)] else: algorithm_id = { 'des': Advapi32Const.CALG_DES, 'tripledes_2key': Advapi32Const.CALG_3DES_112, 'tripledes_3key': Advapi32Const.CALG_3DES, 'rc2': Advapi32Const.CALG_RC2, 'rc4': Advapi32Const.CALG_RC4, }[cipher] provider = Advapi32Const.MS_ENH_RSA_AES_PROV context_handle = open_context_handle(provider, verify_only=False) blob_header_pointer = struct(advapi32, 'BLOBHEADER') blob_header = unwrap(blob_header_pointer) blob_header.bType = Advapi32Const.PLAINTEXTKEYBLOB blob_header.bVersion = Advapi32Const.CUR_BLOB_VERSION blob_header.reserved = 0 blob_header.aiKeyAlg = algorithm_id blob_struct_pointer = struct(advapi32, 'PLAINTEXTKEYBLOB') blob_struct = unwrap(blob_struct_pointer) blob_struct.hdr = blob_header blob_struct.dwKeySize = len(key) blob = struct_bytes(blob_struct_pointer) + key flags = 0 if cipher in set(['rc2', 'rc4']) and len(key) == 5: flags = Advapi32Const.CRYPT_NO_SALT key_handle_pointer = new(advapi32, 'HCRYPTKEY *') res = advapi32.CryptImportKey( context_handle, blob, len(blob), null(), flags, key_handle_pointer ) handle_error(res) key_handle = unwrap(key_handle_pointer) if cipher == 'rc2': buf = new(advapi32, 'DWORD *', len(key) * 8) res = advapi32.CryptSetKeyParam( key_handle, Advapi32Const.KP_EFFECTIVE_KEYLEN, buf, 0 ) handle_error(res) if cipher != 'rc4': res = advapi32.CryptSetKeyParam( key_handle, Advapi32Const.KP_IV, iv, 0 ) handle_error(res) buf = new(advapi32, 'DWORD *', Advapi32Const.CRYPT_MODE_CBC) res = advapi32.CryptSetKeyParam( key_handle, Advapi32Const.KP_MODE, buf, 0 ) handle_error(res) buf = new(advapi32, 'DWORD *', Advapi32Const.PKCS5_PADDING) res = advapi32.CryptSetKeyParam( key_handle, Advapi32Const.KP_PADDING, buf, 0 ) handle_error(res) return (context_handle, key_handle) def _bcrypt_create_key_handle(cipher, key): """ Creates a BCRYPT_KEY_HANDLE for symmetric encryption/decryption. The handle must be released by bcrypt.BCryptDestroyKey() when done. :param cipher: A unicode string of "aes", "des", "tripledes_2key", "tripledes_3key", "rc2", "rc4" :param key: A byte string of the symmetric key :return: A BCRYPT_KEY_HANDLE """ alg_handle = None alg_constant = { 'aes': BcryptConst.BCRYPT_AES_ALGORITHM, 'des': BcryptConst.BCRYPT_DES_ALGORITHM, 'tripledes_2key': BcryptConst.BCRYPT_3DES_112_ALGORITHM, 'tripledes_3key': BcryptConst.BCRYPT_3DES_ALGORITHM, 'rc2': BcryptConst.BCRYPT_RC2_ALGORITHM, 'rc4': BcryptConst.BCRYPT_RC4_ALGORITHM, }[cipher] try: alg_handle = open_alg_handle(alg_constant) blob_type = BcryptConst.BCRYPT_KEY_DATA_BLOB blob_struct_pointer = struct(bcrypt, 'BCRYPT_KEY_DATA_BLOB_HEADER') blob_struct = unwrap(blob_struct_pointer) blob_struct.dwMagic = BcryptConst.BCRYPT_KEY_DATA_BLOB_MAGIC blob_struct.dwVersion = BcryptConst.BCRYPT_KEY_DATA_BLOB_VERSION1 blob_struct.cbKeyData = len(key) blob = struct_bytes(blob_struct_pointer) + key if cipher == 'rc2': buf = new(bcrypt, 'DWORD *', len(key) * 8) res = bcrypt.BCryptSetProperty( alg_handle, BcryptConst.BCRYPT_EFFECTIVE_KEY_LENGTH, buf, 4, 0 ) handle_error(res) key_handle_pointer = new(bcrypt, 'BCRYPT_KEY_HANDLE *') res = bcrypt.BCryptImportKey( alg_handle, null(), blob_type, key_handle_pointer, null(), 0, blob, len(blob), 0 ) handle_error(res) return unwrap(key_handle_pointer) finally: if alg_handle: close_alg_handle(alg_handle) def _encrypt(cipher, key, data, iv, padding): """ Encrypts plaintext :param cipher: A unicode string of "aes", "des", "tripledes_2key", "tripledes_3key", "rc2", "rc4" :param key: The encryption key - a byte string 5-16 bytes long :param data: The plaintext - a byte string :param iv: The initialization vector - a byte string - unused for RC4 :param padding: Boolean, if padding should be used - unused for RC4 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the ciphertext """ if not isinstance(key, byte_cls): raise TypeError(pretty_message( ''' key must be a byte string, not %s ''', type_name(key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if cipher != 'rc4' and not isinstance(iv, byte_cls): raise TypeError(pretty_message( ''' iv must be a byte string, not %s ''', type_name(iv) )) if cipher != 'rc4' and not padding: # AES in CBC mode can be allowed with no padding if # the data is an exact multiple of the block size if not (cipher == 'aes' and len(data) % 16 == 0): raise ValueError('padding must be specified') if _backend == 'winlegacy': return _advapi32_encrypt(cipher, key, data, iv, padding) return _bcrypt_encrypt(cipher, key, data, iv, padding) def _advapi32_encrypt(cipher, key, data, iv, padding): """ Encrypts plaintext via CryptoAPI :param cipher: A unicode string of "aes", "des", "tripledes_2key", "tripledes_3key", "rc2", "rc4" :param key: The encryption key - a byte string 5-16 bytes long :param data: The plaintext - a byte string :param iv: The initialization vector - a byte string - unused for RC4 :param padding: Boolean, if padding should be used - unused for RC4 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the ciphertext """ context_handle = None key_handle = None try: context_handle, key_handle = _advapi32_create_handles(cipher, key, iv) out_len = new(advapi32, 'DWORD *', len(data)) res = advapi32.CryptEncrypt( key_handle, null(), True, 0, null(), out_len, 0 ) handle_error(res) buffer_len = deref(out_len) buffer = buffer_from_bytes(buffer_len) write_to_buffer(buffer, data) pointer_set(out_len, len(data)) res = advapi32.CryptEncrypt( key_handle, null(), True, 0, buffer, out_len, buffer_len ) handle_error(res) output = bytes_from_buffer(buffer, deref(out_len)) # Remove padding when not required. CryptoAPI doesn't support this, so # we just manually remove it. if cipher == 'aes' and not padding and len(output) == len(data) + 16: output = output[:-16] return output finally: if key_handle: advapi32.CryptDestroyKey(key_handle) if context_handle: close_context_handle(context_handle) def _bcrypt_encrypt(cipher, key, data, iv, padding): """ Encrypts plaintext via CNG :param cipher: A unicode string of "aes", "des", "tripledes_2key", "tripledes_3key", "rc2", "rc4" :param key: The encryption key - a byte string 5-16 bytes long :param data: The plaintext - a byte string :param iv: The initialization vector - a byte string - unused for RC4 :param padding: Boolean, if padding should be used - unused for RC4 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the ciphertext """ key_handle = None try: key_handle = _bcrypt_create_key_handle(cipher, key) if iv is None: iv_len = 0 else: iv_len = len(iv) flags = 0 if padding is True: flags = BcryptConst.BCRYPT_BLOCK_PADDING out_len = new(bcrypt, 'ULONG *') res = bcrypt.BCryptEncrypt( key_handle, data, len(data), null(), null(), 0, null(), 0, out_len, flags ) handle_error(res) buffer_len = deref(out_len) buffer = buffer_from_bytes(buffer_len) iv_buffer = buffer_from_bytes(iv) if iv else null() res = bcrypt.BCryptEncrypt( key_handle, data, len(data), null(), iv_buffer, iv_len, buffer, buffer_len, out_len, flags ) handle_error(res) return bytes_from_buffer(buffer, deref(out_len)) finally: if key_handle: bcrypt.BCryptDestroyKey(key_handle) def _decrypt(cipher, key, data, iv, padding): """ Decrypts AES/RC4/RC2/3DES/DES ciphertext :param cipher: A unicode string of "aes", "des", "tripledes_2key", "tripledes_3key", "rc2", "rc4" :param key: The encryption key - a byte string 5-16 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string - unused for RC4 :param padding: Boolean, if padding should be used - unused for RC4 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ if not isinstance(key, byte_cls): raise TypeError(pretty_message( ''' key must be a byte string, not %s ''', type_name(key) )) if not isinstance(data, byte_cls): raise TypeError(pretty_message( ''' data must be a byte string, not %s ''', type_name(data) )) if cipher != 'rc4' and not isinstance(iv, byte_cls): raise TypeError(pretty_message( ''' iv must be a byte string, not %s ''', type_name(iv) )) if cipher not in set(['rc4', 'aes']) and not padding: raise ValueError('padding must be specified') if _backend == 'winlegacy': return _advapi32_decrypt(cipher, key, data, iv, padding) return _bcrypt_decrypt(cipher, key, data, iv, padding) def _advapi32_decrypt(cipher, key, data, iv, padding): """ Decrypts AES/RC4/RC2/3DES/DES ciphertext via CryptoAPI :param cipher: A unicode string of "aes", "des", "tripledes_2key", "tripledes_3key", "rc2", "rc4" :param key: The encryption key - a byte string 5-16 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string - unused for RC4 :param padding: Boolean, if padding should be used - unused for RC4 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ context_handle = None key_handle = None try: context_handle, key_handle = _advapi32_create_handles(cipher, key, iv) if cipher == 'aes' and not padding and len(data) % 16 != 0: raise ValueError('Invalid data - ciphertext length must be a multiple of 16') buffer = buffer_from_bytes(data) out_len = new(advapi32, 'DWORD *', len(data)) res = advapi32.CryptDecrypt( key_handle, null(), # To skip padding, we have to tell the API that this is not # the final block False if cipher == 'aes' and not padding else True, 0, buffer, out_len ) handle_error(res) return bytes_from_buffer(buffer, deref(out_len)) finally: if key_handle: advapi32.CryptDestroyKey(key_handle) if context_handle: close_context_handle(context_handle) def _bcrypt_decrypt(cipher, key, data, iv, padding): """ Decrypts AES/RC4/RC2/3DES/DES ciphertext via CNG :param cipher: A unicode string of "aes", "des", "tripledes_2key", "tripledes_3key", "rc2", "rc4" :param key: The encryption key - a byte string 5-16 bytes long :param data: The ciphertext - a byte string :param iv: The initialization vector - a byte string - unused for RC4 :param padding: Boolean, if padding should be used - unused for RC4 :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the plaintext """ key_handle = None try: key_handle = _bcrypt_create_key_handle(cipher, key) if iv is None: iv_len = 0 else: iv_len = len(iv) flags = 0 if padding is True: flags = BcryptConst.BCRYPT_BLOCK_PADDING out_len = new(bcrypt, 'ULONG *') res = bcrypt.BCryptDecrypt( key_handle, data, len(data), null(), null(), 0, null(), 0, out_len, flags ) handle_error(res) buffer_len = deref(out_len) buffer = buffer_from_bytes(buffer_len) iv_buffer = buffer_from_bytes(iv) if iv else null() res = bcrypt.BCryptDecrypt( key_handle, data, len(data), null(), iv_buffer, iv_len, buffer, buffer_len, out_len, flags ) handle_error(res) return bytes_from_buffer(buffer, deref(out_len)) finally: if key_handle: bcrypt.BCryptDestroyKey(key_handle) oscrypto-1.3.0/oscrypto/_win/tls.py000066400000000000000000001632421421476274700174150ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys import re import socket as socket_ import select import numbers from .._asn1 import Certificate as Asn1Certificate from .._errors import pretty_message from .._ffi import ( buffer_from_bytes, buffer_from_unicode, bytes_from_buffer, cast, deref, is_null, native, new, null, ref, sizeof, struct, unwrap, write_to_buffer, ) from ._secur32 import secur32, Secur32Const, handle_error from ._crypt32 import crypt32, Crypt32Const, handle_error as handle_crypt32_error from ._kernel32 import kernel32 from .._types import type_name, str_cls, byte_cls, int_types from ..errors import TLSError, TLSVerificationError, TLSDisconnectError, TLSGracefulDisconnectError from .._tls import ( detect_client_auth_request, detect_other_protocol, extract_chain, get_dh_params_length, parse_alert, parse_session_info, raise_client_auth, raise_dh_params, raise_disconnection, raise_expired_not_yet_valid, raise_handshake, raise_hostname, raise_no_issuer, raise_protocol_error, raise_protocol_version, raise_revoked, raise_self_signed, raise_verification, raise_weak_signature, ) from .asymmetric import load_certificate, Certificate from ..keys import parse_certificate if sys.version_info < (3,): range = xrange # noqa socket_error_cls = socket_.error else: socket_error_cls = WindowsError if sys.version_info < (3, 7): Pattern = re._pattern_type else: Pattern = re.Pattern __all__ = [ 'TLSSession', 'TLSSocket', ] _line_regex = re.compile(b'(\r\n|\r|\n)') _gwv = sys.getwindowsversion() _win_version_info = (_gwv[0], _gwv[1]) class _TLSDowngradeError(TLSVerificationError): pass class _TLSRetryError(TLSError): """ TLSv1.2 on Windows 7 and 8 seems to have isuses with some DHE_RSA ServerKeyExchange messages due to variable length integer encoding. This exception is used to trigger a reconnection to attempt the handshake again. """ pass class TLSSession(object): """ A TLS session object that multiple TLSSocket objects can share for the sake of session reuse """ _protocols = None _ciphers = None _manual_validation = None _extra_trust_roots = None _credentials_handle = None def __init__(self, protocol=None, manual_validation=False, extra_trust_roots=None): """ :param protocol: A unicode string or set of unicode strings representing allowable protocols to negotiate with the server: - "TLSv1.2" - "TLSv1.1" - "TLSv1" - "SSLv3" Default is: {"TLSv1", "TLSv1.1", "TLSv1.2"} :param manual_validation: If certificate and certificate path validation should be skipped and left to the developer to implement :param extra_trust_roots: A list containing one or more certificates to be treated as trust roots, in one of the following formats: - A byte string of the DER encoded certificate - A unicode string of the certificate filename - An asn1crypto.x509.Certificate object - An oscrypto.asymmetric.Certificate object :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if not isinstance(manual_validation, bool): raise TypeError(pretty_message( ''' manual_validation must be a boolean, not %s ''', type_name(manual_validation) )) self._manual_validation = manual_validation if protocol is None: protocol = set(['TLSv1', 'TLSv1.1', 'TLSv1.2']) if isinstance(protocol, str_cls): protocol = set([protocol]) elif not isinstance(protocol, set): raise TypeError(pretty_message( ''' protocol must be a unicode string or set of unicode strings, not %s ''', type_name(protocol) )) unsupported_protocols = protocol - set(['SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2']) if unsupported_protocols: raise ValueError(pretty_message( ''' protocol must contain only the unicode strings "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", not %s ''', repr(unsupported_protocols) )) self._protocols = protocol self._extra_trust_roots = [] if extra_trust_roots: for extra_trust_root in extra_trust_roots: if isinstance(extra_trust_root, Certificate): extra_trust_root = extra_trust_root.asn1 elif isinstance(extra_trust_root, byte_cls): extra_trust_root = parse_certificate(extra_trust_root) elif isinstance(extra_trust_root, str_cls): with open(extra_trust_root, 'rb') as f: extra_trust_root = parse_certificate(f.read()) elif not isinstance(extra_trust_root, Asn1Certificate): raise TypeError(pretty_message( ''' extra_trust_roots must be a list of byte strings, unicode strings, asn1crypto.x509.Certificate objects or oscrypto.asymmetric.Certificate objects, not %s ''', type_name(extra_trust_root) )) self._extra_trust_roots.append(extra_trust_root) self._obtain_credentials() def _obtain_credentials(self): """ Obtains a credentials handle from secur32.dll for use with SChannel """ protocol_values = { 'SSLv3': Secur32Const.SP_PROT_SSL3_CLIENT, 'TLSv1': Secur32Const.SP_PROT_TLS1_CLIENT, 'TLSv1.1': Secur32Const.SP_PROT_TLS1_1_CLIENT, 'TLSv1.2': Secur32Const.SP_PROT_TLS1_2_CLIENT, } protocol_bit_mask = 0 for key, value in protocol_values.items(): if key in self._protocols: protocol_bit_mask |= value algs = [ Secur32Const.CALG_AES_128, Secur32Const.CALG_AES_256, Secur32Const.CALG_3DES, Secur32Const.CALG_SHA1, Secur32Const.CALG_ECDHE, Secur32Const.CALG_DH_EPHEM, Secur32Const.CALG_RSA_KEYX, Secur32Const.CALG_RSA_SIGN, Secur32Const.CALG_ECDSA, Secur32Const.CALG_DSS_SIGN, ] if 'TLSv1.2' in self._protocols: algs.extend([ Secur32Const.CALG_SHA512, Secur32Const.CALG_SHA384, Secur32Const.CALG_SHA256, ]) alg_array = new(secur32, 'ALG_ID[%s]' % len(algs)) for index, alg in enumerate(algs): alg_array[index] = alg flags = Secur32Const.SCH_USE_STRONG_CRYPTO | Secur32Const.SCH_CRED_NO_DEFAULT_CREDS if not self._manual_validation and not self._extra_trust_roots: flags |= Secur32Const.SCH_CRED_AUTO_CRED_VALIDATION else: flags |= Secur32Const.SCH_CRED_MANUAL_CRED_VALIDATION schannel_cred_pointer = struct(secur32, 'SCHANNEL_CRED') schannel_cred = unwrap(schannel_cred_pointer) schannel_cred.dwVersion = Secur32Const.SCHANNEL_CRED_VERSION schannel_cred.cCreds = 0 schannel_cred.paCred = null() schannel_cred.hRootStore = null() schannel_cred.cMappers = 0 schannel_cred.aphMappers = null() schannel_cred.cSupportedAlgs = len(alg_array) schannel_cred.palgSupportedAlgs = alg_array schannel_cred.grbitEnabledProtocols = protocol_bit_mask schannel_cred.dwMinimumCipherStrength = 0 schannel_cred.dwMaximumCipherStrength = 0 # Default session lifetime is 10 hours schannel_cred.dwSessionLifespan = 0 schannel_cred.dwFlags = flags schannel_cred.dwCredFormat = 0 cred_handle_pointer = new(secur32, 'CredHandle *') result = secur32.AcquireCredentialsHandleW( null(), Secur32Const.UNISP_NAME, Secur32Const.SECPKG_CRED_OUTBOUND, null(), schannel_cred_pointer, null(), null(), cred_handle_pointer, null() ) handle_error(result) self._credentials_handle = cred_handle_pointer def __del__(self): if self._credentials_handle: result = secur32.FreeCredentialsHandle(self._credentials_handle) handle_error(result) self._credentials_handle = None class TLSSocket(object): """ A wrapper around a socket.socket that adds TLS """ _socket = None _session = None _context_handle_pointer = None _context_flags = None _hostname = None _header_size = None _message_size = None _trailer_size = None _received_bytes = None _decrypted_bytes = None _encrypt_desc = None _encrypt_buffers = None _encrypt_data_buffer = None _decrypt_desc = None _decrypt_buffers = None _decrypt_data_buffer = None _certificate = None _intermediates = None _protocol = None _cipher_suite = None _compression = None _session_id = None _session_ticket = None _remote_closed = False @classmethod def wrap(cls, socket, hostname, session=None): """ Takes an existing socket and adds TLS :param socket: A socket.socket object to wrap with TLS :param hostname: A unicode string of the hostname or IP the socket is connected to :param session: An existing TLSSession object to allow for session reuse, specific protocol or manual certificate validation :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if not isinstance(socket, socket_.socket): raise TypeError(pretty_message( ''' socket must be an instance of socket.socket, not %s ''', type_name(socket) )) if not isinstance(hostname, str_cls): raise TypeError(pretty_message( ''' hostname must be a unicode string, not %s ''', type_name(hostname) )) if session is not None and not isinstance(session, TLSSession): raise TypeError(pretty_message( ''' session must be an instance of oscrypto.tls.TLSSession, not %s ''', type_name(session) )) new_socket = cls(None, None, session=session) new_socket._socket = socket new_socket._hostname = hostname # Since we don't create the socket connection here, we can't try to # reconnect with a lower version of the TLS protocol, so we just # move the data to public exception type TLSVerificationError() try: new_socket._handshake() except (_TLSDowngradeError) as e: new_e = TLSVerificationError(e.message, e.certificate) raise new_e except (_TLSRetryError) as e: new_e = TLSError(e.message) raise new_e return new_socket def __init__(self, address, port, timeout=10, session=None): """ :param address: A unicode string of the domain name or IP address to connect to :param port: An integer of the port number to connect to :param timeout: An integer timeout to use for the socket :param session: An oscrypto.tls.TLSSession object to allow for session reuse and controlling the protocols and validation performed """ self._received_bytes = b'' self._decrypted_bytes = b'' if address is None and port is None: self._socket = None else: if not isinstance(address, str_cls): raise TypeError(pretty_message( ''' address must be a unicode string, not %s ''', type_name(address) )) if not isinstance(port, int_types): raise TypeError(pretty_message( ''' port must be an integer, not %s ''', type_name(port) )) if timeout is not None and not isinstance(timeout, numbers.Number): raise TypeError(pretty_message( ''' timeout must be a number, not %s ''', type_name(timeout) )) self._socket = socket_.create_connection((address, port), timeout) self._socket.settimeout(timeout) if session is None: session = TLSSession() elif not isinstance(session, TLSSession): raise TypeError(pretty_message( ''' session must be an instance of oscrypto.tls.TLSSession, not %s ''', type_name(session) )) self._session = session if self._socket: self._hostname = address try: self._handshake() except (_TLSDowngradeError): self.close() new_session = TLSSession( session._protocols - set(['TLSv1.2']), session._manual_validation, session._extra_trust_roots ) session.__del__() self._received_bytes = b'' self._session = new_session self._socket = socket_.create_connection((address, port), timeout) self._socket.settimeout(timeout) self._handshake() except (_TLSRetryError): self._received_bytes = b'' self._socket = socket_.create_connection((address, port), timeout) self._socket.settimeout(timeout) self._handshake() def _create_buffers(self, number): """ Creates a SecBufferDesc struct and contained SecBuffer structs :param number: The number of contains SecBuffer objects to create :return: A tuple of (SecBufferDesc pointer, SecBuffer array) """ buffers = new(secur32, 'SecBuffer[%d]' % number) for index in range(0, number): buffers[index].cbBuffer = 0 buffers[index].BufferType = Secur32Const.SECBUFFER_EMPTY buffers[index].pvBuffer = null() sec_buffer_desc_pointer = struct(secur32, 'SecBufferDesc') sec_buffer_desc = unwrap(sec_buffer_desc_pointer) sec_buffer_desc.ulVersion = Secur32Const.SECBUFFER_VERSION sec_buffer_desc.cBuffers = number sec_buffer_desc.pBuffers = buffers return (sec_buffer_desc_pointer, buffers) def _extra_trust_root_validation(self): """ Manually invoked windows certificate chain builder and verification step when there are extra trust roots to include in the search process """ store = None cert_chain_context_pointer = None try: # We set up an in-memory store to pass as an extra store to grab # certificates from when performing the verification store = crypt32.CertOpenStore( Crypt32Const.CERT_STORE_PROV_MEMORY, Crypt32Const.X509_ASN_ENCODING, null(), 0, null() ) if is_null(store): handle_crypt32_error(0) cert_hashes = set() for cert in self._session._extra_trust_roots: cert_data = cert.dump() result = crypt32.CertAddEncodedCertificateToStore( store, Crypt32Const.X509_ASN_ENCODING, cert_data, len(cert_data), Crypt32Const.CERT_STORE_ADD_USE_EXISTING, null() ) if not result: handle_crypt32_error(0) cert_hashes.add(cert.sha256) cert_context_pointer_pointer = new(crypt32, 'PCERT_CONTEXT *') result = secur32.QueryContextAttributesW( self._context_handle_pointer, Secur32Const.SECPKG_ATTR_REMOTE_CERT_CONTEXT, cert_context_pointer_pointer ) handle_error(result) cert_context_pointer = unwrap(cert_context_pointer_pointer) cert_context_pointer = cast(crypt32, 'PCERT_CONTEXT', cert_context_pointer) # We have to do a funky shuffle here because FILETIME from kernel32 # is different than FILETIME from crypt32 when using cffi. If we # overwrite the "now_pointer" variable, cffi releases the backing # memory and we end up getting a validation error about certificate # expiration time. orig_now_pointer = new(kernel32, 'FILETIME *') kernel32.GetSystemTimeAsFileTime(orig_now_pointer) now_pointer = cast(crypt32, 'FILETIME *', orig_now_pointer) usage_identifiers = new(crypt32, 'char *[3]') usage_identifiers[0] = cast(crypt32, 'char *', Crypt32Const.PKIX_KP_SERVER_AUTH) usage_identifiers[1] = cast(crypt32, 'char *', Crypt32Const.SERVER_GATED_CRYPTO) usage_identifiers[2] = cast(crypt32, 'char *', Crypt32Const.SGC_NETSCAPE) cert_enhkey_usage_pointer = struct(crypt32, 'CERT_ENHKEY_USAGE') cert_enhkey_usage = unwrap(cert_enhkey_usage_pointer) cert_enhkey_usage.cUsageIdentifier = 3 cert_enhkey_usage.rgpszUsageIdentifier = cast(crypt32, 'char **', usage_identifiers) cert_usage_match_pointer = struct(crypt32, 'CERT_USAGE_MATCH') cert_usage_match = unwrap(cert_usage_match_pointer) cert_usage_match.dwType = Crypt32Const.USAGE_MATCH_TYPE_OR cert_usage_match.Usage = cert_enhkey_usage cert_chain_para_pointer = struct(crypt32, 'CERT_CHAIN_PARA') cert_chain_para = unwrap(cert_chain_para_pointer) cert_chain_para.RequestedUsage = cert_usage_match cert_chain_para_size = sizeof(crypt32, cert_chain_para) cert_chain_para.cbSize = cert_chain_para_size cert_chain_context_pointer_pointer = new(crypt32, 'PCERT_CHAIN_CONTEXT *') result = crypt32.CertGetCertificateChain( null(), cert_context_pointer, now_pointer, store, cert_chain_para_pointer, Crypt32Const.CERT_CHAIN_CACHE_END_CERT | Crypt32Const.CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY, null(), cert_chain_context_pointer_pointer ) handle_crypt32_error(result) cert_chain_policy_para_flags = Crypt32Const.CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS cert_chain_context_pointer = unwrap(cert_chain_context_pointer_pointer) # Unwrap the chain and if the final element in the chain is one of # extra trust roots, set flags so that we trust the certificate even # though it is not in the Trusted Roots store cert_chain_context = unwrap(cert_chain_context_pointer) num_chains = native(int, cert_chain_context.cChain) if num_chains == 1: first_simple_chain_pointer = unwrap(cert_chain_context.rgpChain) first_simple_chain = unwrap(first_simple_chain_pointer) num_elements = native(int, first_simple_chain.cElement) last_element_pointer = first_simple_chain.rgpElement[num_elements - 1] last_element = unwrap(last_element_pointer) last_element_cert = unwrap(last_element.pCertContext) last_element_cert_data = bytes_from_buffer( last_element_cert.pbCertEncoded, native(int, last_element_cert.cbCertEncoded) ) last_cert = Asn1Certificate.load(last_element_cert_data) if last_cert.sha256 in cert_hashes: cert_chain_policy_para_flags |= Crypt32Const.CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG ssl_extra_cert_chain_policy_para_pointer = struct(crypt32, 'SSL_EXTRA_CERT_CHAIN_POLICY_PARA') ssl_extra_cert_chain_policy_para = unwrap(ssl_extra_cert_chain_policy_para_pointer) ssl_extra_cert_chain_policy_para.cbSize = sizeof(crypt32, ssl_extra_cert_chain_policy_para) ssl_extra_cert_chain_policy_para.dwAuthType = Crypt32Const.AUTHTYPE_SERVER ssl_extra_cert_chain_policy_para.fdwChecks = 0 ssl_extra_cert_chain_policy_para.pwszServerName = cast( crypt32, 'wchar_t *', buffer_from_unicode(self._hostname) ) cert_chain_policy_para_pointer = struct(crypt32, 'CERT_CHAIN_POLICY_PARA') cert_chain_policy_para = unwrap(cert_chain_policy_para_pointer) cert_chain_policy_para.cbSize = sizeof(crypt32, cert_chain_policy_para) cert_chain_policy_para.dwFlags = cert_chain_policy_para_flags cert_chain_policy_para.pvExtraPolicyPara = cast(crypt32, 'void *', ssl_extra_cert_chain_policy_para_pointer) cert_chain_policy_status_pointer = struct(crypt32, 'CERT_CHAIN_POLICY_STATUS') cert_chain_policy_status = unwrap(cert_chain_policy_status_pointer) cert_chain_policy_status.cbSize = sizeof(crypt32, cert_chain_policy_status) result = crypt32.CertVerifyCertificateChainPolicy( Crypt32Const.CERT_CHAIN_POLICY_SSL, cert_chain_context_pointer, cert_chain_policy_para_pointer, cert_chain_policy_status_pointer ) handle_crypt32_error(result) cert_context = unwrap(cert_context_pointer) cert_data = bytes_from_buffer(cert_context.pbCertEncoded, native(int, cert_context.cbCertEncoded)) cert = Asn1Certificate.load(cert_data) error = cert_chain_policy_status.dwError if error: if error == Crypt32Const.CERT_E_EXPIRED: raise_expired_not_yet_valid(cert) if error == Crypt32Const.CERT_E_UNTRUSTEDROOT: oscrypto_cert = load_certificate(cert) if oscrypto_cert.self_signed: raise_self_signed(cert) else: raise_no_issuer(cert) if error == Crypt32Const.CERT_E_CN_NO_MATCH: raise_hostname(cert, self._hostname) if error == Crypt32Const.TRUST_E_CERT_SIGNATURE: raise_weak_signature(cert) if error == Crypt32Const.CRYPT_E_REVOKED: raise_revoked(cert) raise_verification(cert) if cert.hash_algo in set(['md5', 'md2']): raise_weak_signature(cert) finally: if store: crypt32.CertCloseStore(store, 0) if cert_chain_context_pointer: crypt32.CertFreeCertificateChain(cert_chain_context_pointer) def _handshake(self, renegotiate=False): """ Perform an initial TLS handshake, or a renegotiation :param renegotiate: If the handshake is for a renegotiation """ in_buffers = None out_buffers = None new_context_handle_pointer = None try: if renegotiate: temp_context_handle_pointer = self._context_handle_pointer else: new_context_handle_pointer = new(secur32, 'CtxtHandle *') temp_context_handle_pointer = new_context_handle_pointer requested_flags = { Secur32Const.ISC_REQ_REPLAY_DETECT: 'replay detection', Secur32Const.ISC_REQ_SEQUENCE_DETECT: 'sequence detection', Secur32Const.ISC_REQ_CONFIDENTIALITY: 'confidentiality', Secur32Const.ISC_REQ_ALLOCATE_MEMORY: 'memory allocation', Secur32Const.ISC_REQ_INTEGRITY: 'integrity', Secur32Const.ISC_REQ_STREAM: 'stream orientation', Secur32Const.ISC_REQ_USE_SUPPLIED_CREDS: 'disable automatic client auth', } self._context_flags = 0 for flag in requested_flags: self._context_flags |= flag in_sec_buffer_desc_pointer, in_buffers = self._create_buffers(2) in_buffers[0].BufferType = Secur32Const.SECBUFFER_TOKEN out_sec_buffer_desc_pointer, out_buffers = self._create_buffers(2) out_buffers[0].BufferType = Secur32Const.SECBUFFER_TOKEN out_buffers[1].BufferType = Secur32Const.SECBUFFER_ALERT output_context_flags_pointer = new(secur32, 'ULONG *') if renegotiate: first_handle = temp_context_handle_pointer second_handle = null() else: first_handle = null() second_handle = temp_context_handle_pointer result = secur32.InitializeSecurityContextW( self._session._credentials_handle, first_handle, self._hostname, self._context_flags, 0, 0, null(), 0, second_handle, out_sec_buffer_desc_pointer, output_context_flags_pointer, null() ) if result not in set([Secur32Const.SEC_E_OK, Secur32Const.SEC_I_CONTINUE_NEEDED]): handle_error(result, TLSError) if not renegotiate: temp_context_handle_pointer = second_handle else: temp_context_handle_pointer = first_handle handshake_server_bytes = b'' handshake_client_bytes = b'' if out_buffers[0].cbBuffer > 0: token = bytes_from_buffer(out_buffers[0].pvBuffer, out_buffers[0].cbBuffer) handshake_client_bytes += token self._socket.send(token) out_buffers[0].cbBuffer = 0 secur32.FreeContextBuffer(out_buffers[0].pvBuffer) out_buffers[0].pvBuffer = null() in_data_buffer = buffer_from_bytes(32768) in_buffers[0].pvBuffer = cast(secur32, 'BYTE *', in_data_buffer) bytes_read = b'' while result != Secur32Const.SEC_E_OK: try: fail_late = False bytes_read = self._socket.recv(8192) if bytes_read == b'': raise_disconnection() except (socket_error_cls): fail_late = True handshake_server_bytes += bytes_read self._received_bytes += bytes_read in_buffers[0].cbBuffer = len(self._received_bytes) write_to_buffer(in_data_buffer, self._received_bytes) result = secur32.InitializeSecurityContextW( self._session._credentials_handle, temp_context_handle_pointer, self._hostname, self._context_flags, 0, 0, in_sec_buffer_desc_pointer, 0, null(), out_sec_buffer_desc_pointer, output_context_flags_pointer, null() ) if result == Secur32Const.SEC_E_INCOMPLETE_MESSAGE: in_buffers[0].BufferType = Secur32Const.SECBUFFER_TOKEN # Windows 10 seems to fill the second input buffer with # a BufferType of SECBUFFER_MISSING (4), which if not # cleared causes the handshake to fail. if in_buffers[1].BufferType != Secur32Const.SECBUFFER_EMPTY: in_buffers[1].BufferType = Secur32Const.SECBUFFER_EMPTY in_buffers[1].cbBuffer = 0 if not is_null(in_buffers[1].pvBuffer): secur32.FreeContextBuffer(in_buffers[1].pvBuffer) in_buffers[1].pvBuffer = null() if fail_late: raise_disconnection() continue if result == Secur32Const.SEC_E_ILLEGAL_MESSAGE: if detect_client_auth_request(handshake_server_bytes): raise_client_auth() alert_info = parse_alert(handshake_server_bytes) if alert_info and alert_info == (2, 70): raise_protocol_version() raise_handshake() if result == Secur32Const.SEC_E_WRONG_PRINCIPAL: chain = extract_chain(handshake_server_bytes) raise_hostname(chain[0], self._hostname) if result == Secur32Const.SEC_E_CERT_EXPIRED: chain = extract_chain(handshake_server_bytes) raise_expired_not_yet_valid(chain[0]) if result == Secur32Const.SEC_E_UNTRUSTED_ROOT: chain = extract_chain(handshake_server_bytes) cert = chain[0] oscrypto_cert = load_certificate(cert) if not oscrypto_cert.self_signed: raise_no_issuer(cert) raise_self_signed(cert) if result == Secur32Const.SEC_E_INTERNAL_ERROR: if get_dh_params_length(handshake_server_bytes) < 1024: raise_dh_params() if result == Secur32Const.SEC_I_INCOMPLETE_CREDENTIALS: raise_client_auth() if result == Crypt32Const.TRUST_E_CERT_SIGNATURE: raise_weak_signature(cert) if result == Secur32Const.SEC_E_INVALID_TOKEN: # If an alert it present, there may have been a handshake # error due to the server using a certificate path with a # trust root using MD2 or MD5 combined with TLS 1.2. To # work around this, if the user allows anything other than # TLS 1.2, we just remove it from the acceptable protocols # and try again. if out_buffers[1].cbBuffer > 0: alert_bytes = bytes_from_buffer(out_buffers[1].pvBuffer, out_buffers[1].cbBuffer) handshake_client_bytes += alert_bytes alert_number = alert_bytes[6:7] if alert_number == b'\x28' or alert_number == b'\x2b': if 'TLSv1.2' in self._session._protocols and len(self._session._protocols) > 1: chain = extract_chain(handshake_server_bytes) raise _TLSDowngradeError( 'Server certificate verification failed - weak certificate signature algorithm', chain[0] ) if detect_client_auth_request(handshake_server_bytes): raise_client_auth() if detect_other_protocol(handshake_server_bytes): raise_protocol_error(handshake_server_bytes) raise_handshake() # These are semi-common errors with TLSv1.2 on Windows 7 an 8 # that appears to be due to poor handling of the # ServerKeyExchange for DHE_RSA cipher suites. The solution # is to retry the handshake. if result == Secur32Const.SEC_E_BUFFER_TOO_SMALL or result == Secur32Const.SEC_E_MESSAGE_ALTERED: if 'TLSv1.2' in self._session._protocols: raise _TLSRetryError('TLS handshake failed') if fail_late: raise_disconnection() if result == Secur32Const.SEC_E_INVALID_PARAMETER: if get_dh_params_length(handshake_server_bytes) < 1024: raise_dh_params() if result not in set([Secur32Const.SEC_E_OK, Secur32Const.SEC_I_CONTINUE_NEEDED]): handle_error(result, TLSError) if out_buffers[0].cbBuffer > 0: token = bytes_from_buffer(out_buffers[0].pvBuffer, out_buffers[0].cbBuffer) handshake_client_bytes += token self._socket.send(token) out_buffers[0].cbBuffer = 0 secur32.FreeContextBuffer(out_buffers[0].pvBuffer) out_buffers[0].pvBuffer = null() if in_buffers[1].BufferType == Secur32Const.SECBUFFER_EXTRA: extra_amount = in_buffers[1].cbBuffer self._received_bytes = self._received_bytes[-extra_amount:] in_buffers[1].BufferType = Secur32Const.SECBUFFER_EMPTY in_buffers[1].cbBuffer = 0 secur32.FreeContextBuffer(in_buffers[1].pvBuffer) in_buffers[1].pvBuffer = null() # The handshake is complete, so discard any extra bytes if result == Secur32Const.SEC_E_OK: handshake_server_bytes = handshake_server_bytes[-extra_amount:] else: self._received_bytes = b'' connection_info_pointer = struct(secur32, 'SecPkgContext_ConnectionInfo') result = secur32.QueryContextAttributesW( temp_context_handle_pointer, Secur32Const.SECPKG_ATTR_CONNECTION_INFO, connection_info_pointer ) handle_error(result, TLSError) connection_info = unwrap(connection_info_pointer) self._protocol = { Secur32Const.SP_PROT_SSL2_CLIENT: 'SSLv2', Secur32Const.SP_PROT_SSL3_CLIENT: 'SSLv3', Secur32Const.SP_PROT_TLS1_CLIENT: 'TLSv1', Secur32Const.SP_PROT_TLS1_1_CLIENT: 'TLSv1.1', Secur32Const.SP_PROT_TLS1_2_CLIENT: 'TLSv1.2', }.get(native(int, connection_info.dwProtocol), str_cls(connection_info.dwProtocol)) if self._protocol in set(['SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2']): session_info = parse_session_info(handshake_server_bytes, handshake_client_bytes) self._cipher_suite = session_info['cipher_suite'] self._compression = session_info['compression'] self._session_id = session_info['session_id'] self._session_ticket = session_info['session_ticket'] output_context_flags = deref(output_context_flags_pointer) for flag in requested_flags: if (flag | output_context_flags) == 0: raise OSError(pretty_message( ''' Unable to obtain a credential context with the property %s ''', requested_flags[flag] )) if not renegotiate: self._context_handle_pointer = temp_context_handle_pointer new_context_handle_pointer = None stream_sizes_pointer = struct(secur32, 'SecPkgContext_StreamSizes') result = secur32.QueryContextAttributesW( self._context_handle_pointer, Secur32Const.SECPKG_ATTR_STREAM_SIZES, stream_sizes_pointer ) handle_error(result) stream_sizes = unwrap(stream_sizes_pointer) self._header_size = native(int, stream_sizes.cbHeader) self._message_size = native(int, stream_sizes.cbMaximumMessage) self._trailer_size = native(int, stream_sizes.cbTrailer) self._buffer_size = self._header_size + self._message_size + self._trailer_size if self._session._extra_trust_roots: self._extra_trust_root_validation() except (OSError, socket_.error): self.close() raise finally: if out_buffers: if not is_null(out_buffers[0].pvBuffer): secur32.FreeContextBuffer(out_buffers[0].pvBuffer) if not is_null(out_buffers[1].pvBuffer): secur32.FreeContextBuffer(out_buffers[1].pvBuffer) if new_context_handle_pointer: secur32.DeleteSecurityContext(new_context_handle_pointer) def read(self, max_length): """ Reads data from the TLS-wrapped socket :param max_length: The number of bytes to read :raises: socket.socket - when a non-TLS socket error occurs oscrypto.errors.TLSError - when a TLS-related error occurs ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string of the data read """ if not isinstance(max_length, int_types): raise TypeError(pretty_message( ''' max_length must be an integer, not %s ''', type_name(max_length) )) if self._context_handle_pointer is None: # Allow the user to read any remaining decrypted data if self._decrypted_bytes != b'': output = self._decrypted_bytes[0:max_length] self._decrypted_bytes = self._decrypted_bytes[max_length:] return output self._raise_closed() # The first time read is called, set up a single contiguous buffer that # it used by DecryptMessage() to populate the three output buffers. # Since we are creating the buffer, we do not need to free it other # than allowing Python to GC it once this object is GCed. if not self._decrypt_data_buffer: self._decrypt_data_buffer = buffer_from_bytes(self._buffer_size) self._decrypt_desc, self._decrypt_buffers = self._create_buffers(4) self._decrypt_buffers[0].BufferType = Secur32Const.SECBUFFER_DATA self._decrypt_buffers[0].pvBuffer = cast(secur32, 'BYTE *', self._decrypt_data_buffer) to_recv = max(max_length, self._buffer_size) # These variables are set to reduce dict access and function calls # in the read loop. Also makes the code easier to read. null_value = null() buf0 = self._decrypt_buffers[0] buf1 = self._decrypt_buffers[1] buf2 = self._decrypt_buffers[2] buf3 = self._decrypt_buffers[3] def _reset_buffers(): buf0.BufferType = Secur32Const.SECBUFFER_DATA buf0.pvBuffer = cast(secur32, 'BYTE *', self._decrypt_data_buffer) buf0.cbBuffer = 0 buf1.BufferType = Secur32Const.SECBUFFER_EMPTY buf1.pvBuffer = null_value buf1.cbBuffer = 0 buf2.BufferType = Secur32Const.SECBUFFER_EMPTY buf2.pvBuffer = null_value buf2.cbBuffer = 0 buf3.BufferType = Secur32Const.SECBUFFER_EMPTY buf3.pvBuffer = null_value buf3.cbBuffer = 0 output = self._decrypted_bytes output_len = len(output) self._decrypted_bytes = b'' # Don't block if we have buffered data available if output_len > 0 and not self.select_read(0): self._decrypted_bytes = b'' return output # This read loop will only be run if there wasn't enough # buffered data to fulfill the requested max_length do_read = len(self._received_bytes) == 0 while output_len < max_length: if do_read: self._received_bytes += self._socket.recv(to_recv) if len(self._received_bytes) == 0: raise_disconnection() data_len = min(len(self._received_bytes), self._buffer_size) if data_len == 0: break self._decrypt_buffers[0].cbBuffer = data_len write_to_buffer(self._decrypt_data_buffer, self._received_bytes[0:data_len]) result = secur32.DecryptMessage( self._context_handle_pointer, self._decrypt_desc, 0, null() ) do_read = False if result == Secur32Const.SEC_E_INCOMPLETE_MESSAGE: _reset_buffers() do_read = True continue elif result == Secur32Const.SEC_I_CONTEXT_EXPIRED: self._remote_closed = True self.shutdown() break elif result == Secur32Const.SEC_I_RENEGOTIATE: self._handshake(renegotiate=True) return self.read(max_length) elif result != Secur32Const.SEC_E_OK: handle_error(result, TLSError) valid_buffer_types = set([ Secur32Const.SECBUFFER_EMPTY, Secur32Const.SECBUFFER_STREAM_HEADER, Secur32Const.SECBUFFER_STREAM_TRAILER ]) extra_amount = None for buf in (buf0, buf1, buf2, buf3): buffer_type = buf.BufferType if buffer_type == Secur32Const.SECBUFFER_DATA: output += bytes_from_buffer(buf.pvBuffer, buf.cbBuffer) output_len = len(output) elif buffer_type == Secur32Const.SECBUFFER_EXTRA: extra_amount = native(int, buf.cbBuffer) elif buffer_type not in valid_buffer_types: raise OSError(pretty_message( ''' Unexpected decrypt output buffer of type %s ''', buffer_type )) if extra_amount: self._received_bytes = self._received_bytes[data_len - extra_amount:] else: self._received_bytes = self._received_bytes[data_len:] # Here we reset the structs for the next call to DecryptMessage() _reset_buffers() # If we have read something, but there is nothing left to read, we # break so that we don't block for longer than necessary if self.select_read(0): do_read = True if not do_read and len(self._received_bytes) == 0: break # If the output is more than we requested (because data is decrypted in # blocks), we save the extra in a buffer if len(output) > max_length: self._decrypted_bytes = output[max_length:] output = output[0:max_length] return output def select_read(self, timeout=None): """ Blocks until the socket is ready to be read from, or the timeout is hit :param timeout: A float - the period of time to wait for data to be read. None for no time limit. :return: A boolean - if data is ready to be read. Will only be False if timeout is not None. """ # If we have buffered data, we consider a read possible if len(self._decrypted_bytes) > 0: return True read_ready, _, _ = select.select([self._socket], [], [], timeout) return len(read_ready) > 0 def read_until(self, marker): """ Reads data from the socket until a marker is found. Data read may include data beyond the marker. :param marker: A byte string or regex object from re.compile(). Used to determine when to stop reading. Regex objects are more inefficient since they must scan the entire byte string of read data each time data is read off the socket. :return: A byte string of the data read """ if not isinstance(marker, byte_cls) and not isinstance(marker, Pattern): raise TypeError(pretty_message( ''' marker must be a byte string or compiled regex object, not %s ''', type_name(marker) )) output = b'' is_regex = isinstance(marker, Pattern) while True: if len(self._decrypted_bytes) > 0: chunk = self._decrypted_bytes self._decrypted_bytes = b'' else: chunk = self.read(8192) offset = len(output) output += chunk if is_regex: match = marker.search(output) if match is not None: end = match.end() break else: # If the marker was not found last time, we have to start # at a position where the marker would have its final char # in the newly read chunk start = max(0, offset - len(marker) - 1) match = output.find(marker, start) if match != -1: end = match + len(marker) break self._decrypted_bytes = output[end:] + self._decrypted_bytes return output[0:end] def read_line(self): r""" Reads a line from the socket, including the line ending of "\r\n", "\r", or "\n" :return: A byte string of the next line from the socket """ return self.read_until(_line_regex) def read_exactly(self, num_bytes): """ Reads exactly the specified number of bytes from the socket :param num_bytes: An integer - the exact number of bytes to read :return: A byte string of the data that was read """ output = b'' remaining = num_bytes while remaining > 0: output += self.read(remaining) remaining = num_bytes - len(output) return output def write(self, data): """ Writes data to the TLS-wrapped socket :param data: A byte string to write to the socket :raises: socket.socket - when a non-TLS socket error occurs oscrypto.errors.TLSError - when a TLS-related error occurs ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library """ if self._context_handle_pointer is None: self._raise_closed() if not self._encrypt_data_buffer: self._encrypt_data_buffer = buffer_from_bytes(self._header_size + self._message_size + self._trailer_size) self._encrypt_desc, self._encrypt_buffers = self._create_buffers(4) self._encrypt_buffers[0].BufferType = Secur32Const.SECBUFFER_STREAM_HEADER self._encrypt_buffers[0].cbBuffer = self._header_size self._encrypt_buffers[0].pvBuffer = cast(secur32, 'BYTE *', self._encrypt_data_buffer) self._encrypt_buffers[1].BufferType = Secur32Const.SECBUFFER_DATA self._encrypt_buffers[1].pvBuffer = ref(self._encrypt_data_buffer, self._header_size) self._encrypt_buffers[2].BufferType = Secur32Const.SECBUFFER_STREAM_TRAILER self._encrypt_buffers[2].cbBuffer = self._trailer_size self._encrypt_buffers[2].pvBuffer = ref(self._encrypt_data_buffer, self._header_size + self._message_size) while len(data) > 0: to_write = min(len(data), self._message_size) write_to_buffer(self._encrypt_data_buffer, data[0:to_write], self._header_size) self._encrypt_buffers[1].cbBuffer = to_write self._encrypt_buffers[2].pvBuffer = ref(self._encrypt_data_buffer, self._header_size + to_write) result = secur32.EncryptMessage( self._context_handle_pointer, 0, self._encrypt_desc, 0 ) if result != Secur32Const.SEC_E_OK: handle_error(result, TLSError) to_send = native(int, self._encrypt_buffers[0].cbBuffer) to_send += native(int, self._encrypt_buffers[1].cbBuffer) to_send += native(int, self._encrypt_buffers[2].cbBuffer) try: self._socket.send(bytes_from_buffer(self._encrypt_data_buffer, to_send)) except (socket_.error) as e: if e.errno == 10053: raise_disconnection() raise data = data[to_send:] def select_write(self, timeout=None): """ Blocks until the socket is ready to be written to, or the timeout is hit :param timeout: A float - the period of time to wait for the socket to be ready to written to. None for no time limit. :return: A boolean - if the socket is ready for writing. Will only be False if timeout is not None. """ _, write_ready, _ = select.select([], [self._socket], [], timeout) return len(write_ready) > 0 def shutdown(self): """ Shuts down the TLS session and then shuts down the underlying socket :raises: OSError - when an error is returned by the OS crypto library """ if self._context_handle_pointer is None: return out_buffers = None try: # ApplyControlToken fails with SEC_E_UNSUPPORTED_FUNCTION # when called on Windows 7 if _win_version_info >= (6, 2): buffers = new(secur32, 'SecBuffer[1]') # This is a SCHANNEL_SHUTDOWN token (DWORD of 1) buffers[0].cbBuffer = 4 buffers[0].BufferType = Secur32Const.SECBUFFER_TOKEN buffers[0].pvBuffer = cast(secur32, 'BYTE *', buffer_from_bytes(b'\x01\x00\x00\x00')) sec_buffer_desc_pointer = struct(secur32, 'SecBufferDesc') sec_buffer_desc = unwrap(sec_buffer_desc_pointer) sec_buffer_desc.ulVersion = Secur32Const.SECBUFFER_VERSION sec_buffer_desc.cBuffers = 1 sec_buffer_desc.pBuffers = buffers result = secur32.ApplyControlToken(self._context_handle_pointer, sec_buffer_desc_pointer) handle_error(result, TLSError) out_sec_buffer_desc_pointer, out_buffers = self._create_buffers(2) out_buffers[0].BufferType = Secur32Const.SECBUFFER_TOKEN out_buffers[1].BufferType = Secur32Const.SECBUFFER_ALERT output_context_flags_pointer = new(secur32, 'ULONG *') result = secur32.InitializeSecurityContextW( self._session._credentials_handle, self._context_handle_pointer, self._hostname, self._context_flags, 0, 0, null(), 0, null(), out_sec_buffer_desc_pointer, output_context_flags_pointer, null() ) acceptable_results = set([ Secur32Const.SEC_E_OK, Secur32Const.SEC_E_CONTEXT_EXPIRED, Secur32Const.SEC_I_CONTINUE_NEEDED ]) if result not in acceptable_results: handle_error(result, TLSError) token = bytes_from_buffer(out_buffers[0].pvBuffer, out_buffers[0].cbBuffer) try: # If there is an error sending the shutdown, ignore it since the # connection is likely gone at this point self._socket.send(token) except (socket_.error): pass finally: if out_buffers: if not is_null(out_buffers[0].pvBuffer): secur32.FreeContextBuffer(out_buffers[0].pvBuffer) if not is_null(out_buffers[1].pvBuffer): secur32.FreeContextBuffer(out_buffers[1].pvBuffer) secur32.DeleteSecurityContext(self._context_handle_pointer) self._context_handle_pointer = None try: self._socket.shutdown(socket_.SHUT_RDWR) except (socket_.error): pass def close(self): """ Shuts down the TLS session and socket and forcibly closes it """ try: self.shutdown() finally: if self._socket: try: self._socket.close() except (socket_.error): pass self._socket = None def _read_certificates(self): """ Reads end-entity and intermediate certificate information from the TLS session """ cert_context_pointer_pointer = new(crypt32, 'CERT_CONTEXT **') result = secur32.QueryContextAttributesW( self._context_handle_pointer, Secur32Const.SECPKG_ATTR_REMOTE_CERT_CONTEXT, cert_context_pointer_pointer ) handle_error(result, TLSError) cert_context_pointer = unwrap(cert_context_pointer_pointer) cert_context_pointer = cast(crypt32, 'CERT_CONTEXT *', cert_context_pointer) cert_context = unwrap(cert_context_pointer) cert_data = bytes_from_buffer(cert_context.pbCertEncoded, native(int, cert_context.cbCertEncoded)) self._certificate = Asn1Certificate.load(cert_data) self._intermediates = [] store_handle = None try: store_handle = cert_context.hCertStore context_pointer = crypt32.CertEnumCertificatesInStore(store_handle, null()) while not is_null(context_pointer): context = unwrap(context_pointer) data = bytes_from_buffer(context.pbCertEncoded, native(int, context.cbCertEncoded)) # The cert store seems to include the end-entity certificate as # the last entry, but we already have that from the struct. if data != cert_data: self._intermediates.append(Asn1Certificate.load(data)) context_pointer = crypt32.CertEnumCertificatesInStore(store_handle, context_pointer) finally: if store_handle: crypt32.CertCloseStore(store_handle, 0) def _raise_closed(self): """ Raises an exception describing if the local or remote end closed the connection """ if self._remote_closed: raise TLSGracefulDisconnectError('The remote end closed the connection') else: raise TLSDisconnectError('The connection was already closed') @property def certificate(self): """ An asn1crypto.x509.Certificate object of the end-entity certificate presented by the server """ if self._context_handle_pointer is None: self._raise_closed() if self._certificate is None: self._read_certificates() return self._certificate @property def intermediates(self): """ A list of asn1crypto.x509.Certificate objects that were presented as intermediates by the server """ if self._context_handle_pointer is None: self._raise_closed() if self._certificate is None: self._read_certificates() return self._intermediates @property def cipher_suite(self): """ A unicode string of the IANA cipher suite name of the negotiated cipher suite """ return self._cipher_suite @property def protocol(self): """ A unicode string of: "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3" """ return self._protocol @property def compression(self): """ A boolean if compression is enabled """ return self._compression @property def session_id(self): """ A unicode string of "new" or "reused" or None for no ticket """ return self._session_id @property def session_ticket(self): """ A unicode string of "new" or "reused" or None for no ticket """ return self._session_ticket @property def session(self): """ The oscrypto.tls.TLSSession object used for this connection """ return self._session @property def hostname(self): """ A unicode string of the TLS server domain name or IP address """ return self._hostname @property def port(self): """ An integer of the port number the socket is connected to """ return self.socket.getpeername()[1] @property def socket(self): """ The underlying socket.socket connection """ if self._context_handle_pointer is None: self._raise_closed() return self._socket def __del__(self): self.close() oscrypto-1.3.0/oscrypto/_win/trust_list.py000066400000000000000000000177201421476274700210260ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import datetime import hashlib import struct from .._asn1 import Certificate from .._ffi import ( array_from_pointer, buffer_from_bytes, bytes_from_buffer, cast, deref, is_null, new, null, struct_from_buffer, unwrap, ) from ._crypt32 import crypt32, Crypt32Const, get_error, handle_error from .._types import str_cls __all__ = [ 'extract_from_system', 'system_path', ] def system_path(): return None def extract_from_system(cert_callback=None, callback_only_on_failure=False): """ Extracts trusted CA certificates from the Windows certificate store :param cert_callback: A callback that is called once for each certificate in the trust store. It should accept two parameters: an asn1crypto.x509.Certificate object, and a reason. The reason will be None if the certificate is being exported, otherwise it will be a unicode string of the reason it won't. :param callback_only_on_failure: A boolean - if the callback should only be called when a certificate is not exported. :raises: OSError - when an error is returned by the OS crypto library :return: A list of 3-element tuples: - 0: a byte string of a DER-encoded certificate - 1: a set of unicode strings that are OIDs of purposes to trust the certificate for - 2: a set of unicode strings that are OIDs of purposes to reject the certificate for """ certificates = {} processed = {} now = datetime.datetime.utcnow() for store in ["ROOT", "CA"]: store_handle = crypt32.CertOpenSystemStoreW(null(), store) handle_error(store_handle) context_pointer = null() while True: context_pointer = crypt32.CertEnumCertificatesInStore(store_handle, context_pointer) if is_null(context_pointer): break context = unwrap(context_pointer) trust_all = False data = None digest = None if context.dwCertEncodingType != Crypt32Const.X509_ASN_ENCODING: continue data = bytes_from_buffer(context.pbCertEncoded, int(context.cbCertEncoded)) digest = hashlib.sha1(data).digest() if digest in processed: continue processed[digest] = True cert_info = unwrap(context.pCertInfo) not_before_seconds = _convert_filetime_to_timestamp(cert_info.NotBefore) try: not_before = datetime.datetime.fromtimestamp(not_before_seconds) if not_before > now: if cert_callback: cert_callback(Certificate.load(data), 'not yet valid') continue except (ValueError, OSError): # If there is an error converting the not before timestamp, # it is almost certainly because it is from too long ago, # which means the cert is definitely valid by now. pass not_after_seconds = _convert_filetime_to_timestamp(cert_info.NotAfter) try: not_after = datetime.datetime.fromtimestamp(not_after_seconds) if not_after < now: if cert_callback: cert_callback(Certificate.load(data), 'no longer valid') continue except (ValueError, OSError) as e: # The only reason we would get an exception here is if the # expiration time is so far in the future that it can't be # used as a timestamp, or it is before 0. If it is very far # in the future, the cert is still valid, so we only raise # an exception if the timestamp is less than zero. if not_after_seconds < 0: message = e.args[0] + ' - ' + str_cls(not_after_seconds) e.args = (message,) + e.args[1:] raise e trust_oids = set() reject_oids = set() # Here we grab the extended key usage properties that Windows # layers on top of the extended key usage extension that is # part of the certificate itself. For highest security, users # should only use certificates for the intersection of the two # lists of purposes. However, many seen to treat the OS trust # list as an override. to_read = new(crypt32, 'DWORD *', 0) res = crypt32.CertGetEnhancedKeyUsage( context_pointer, Crypt32Const.CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, null(), to_read ) # Per the Microsoft documentation, if CRYPT_E_NOT_FOUND is returned # from get_error(), it means the certificate is valid for all purposes error_code, _ = get_error() if not res and error_code != Crypt32Const.CRYPT_E_NOT_FOUND: handle_error(res) if error_code == Crypt32Const.CRYPT_E_NOT_FOUND: trust_all = True else: usage_buffer = buffer_from_bytes(deref(to_read)) res = crypt32.CertGetEnhancedKeyUsage( context_pointer, Crypt32Const.CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, cast(crypt32, 'CERT_ENHKEY_USAGE *', usage_buffer), to_read ) handle_error(res) key_usage_pointer = struct_from_buffer(crypt32, 'CERT_ENHKEY_USAGE', usage_buffer) key_usage = unwrap(key_usage_pointer) # Having no enhanced usage properties means a cert is distrusted if key_usage.cUsageIdentifier == 0: if cert_callback: cert_callback(Certificate.load(data), 'explicitly distrusted') continue oids = array_from_pointer( crypt32, 'LPCSTR', key_usage.rgpszUsageIdentifier, key_usage.cUsageIdentifier ) for oid in oids: trust_oids.add(oid.decode('ascii')) cert = None # If the certificate is not under blanket trust, we have to # determine what purposes it is rejected for by diffing the # set of OIDs from the certificate with the OIDs that are # trusted. if not trust_all: cert = Certificate.load(data) if cert.extended_key_usage_value: for cert_oid in cert.extended_key_usage_value: oid = cert_oid.dotted if oid not in trust_oids: reject_oids.add(oid) if cert_callback and not callback_only_on_failure: if cert is None: cert = Certificate.load(data) cert_callback(cert, None) certificates[digest] = (data, trust_oids, reject_oids) result = crypt32.CertCloseStore(store_handle, 0) handle_error(result) store_handle = None return certificates.values() def _convert_filetime_to_timestamp(filetime): """ Windows returns times as 64-bit unsigned longs that are the number of hundreds of nanoseconds since Jan 1 1601. This converts it to a datetime object. :param filetime: A FILETIME struct object :return: An integer unix timestamp """ hundreds_nano_seconds = struct.unpack( b'>Q', struct.pack( b'>LL', filetime.dwHighDateTime, filetime.dwLowDateTime ) )[0] seconds_since_1601 = hundreds_nano_seconds / 10000000 return seconds_since_1601 - 11644473600 # Seconds from Jan 1 1601 to Jan 1 1970 oscrypto-1.3.0/oscrypto/_win/util.py000066400000000000000000000121621421476274700175620ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from .. import backend from .._errors import pretty_message from .._ffi import buffer_from_bytes, bytes_from_buffer from .._pkcs12 import pkcs12_kdf from .._types import type_name, byte_cls, int_types __all__ = [ 'pbkdf2', 'pkcs12_kdf', 'rand_bytes', ] _backend = backend() if _backend == 'win': from ._cng import bcrypt, BcryptConst, handle_error, open_alg_handle, close_alg_handle def pbkdf2(hash_algorithm, password, salt, iterations, key_length): """ PBKDF2 from PKCS#5 :param hash_algorithm: The string name of the hash algorithm to use: "sha1", "sha256", "sha384", "sha512" :param password: A byte string of the password to use an input to the KDF :param salt: A cryptographic random byte string :param iterations: The numbers of iterations to use when deriving the key :param key_length: The length of the desired key in bytes :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: The derived key as a byte string """ if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', type_name(password) )) if not isinstance(salt, byte_cls): raise TypeError(pretty_message( ''' salt must be a byte string, not %s ''', type_name(salt) )) if not isinstance(iterations, int_types): raise TypeError(pretty_message( ''' iterations must be an integer, not %s ''', type_name(iterations) )) if iterations < 1: raise ValueError('iterations must be greater than 0') if not isinstance(key_length, int_types): raise TypeError(pretty_message( ''' key_length must be an integer, not %s ''', type_name(key_length) )) if key_length < 1: raise ValueError('key_length must be greater than 0') if hash_algorithm not in set(['sha1', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "sha1", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) alg_constant = { 'sha1': BcryptConst.BCRYPT_SHA1_ALGORITHM, 'sha256': BcryptConst.BCRYPT_SHA256_ALGORITHM, 'sha384': BcryptConst.BCRYPT_SHA384_ALGORITHM, 'sha512': BcryptConst.BCRYPT_SHA512_ALGORITHM }[hash_algorithm] alg_handle = None try: alg_handle = open_alg_handle(alg_constant, BcryptConst.BCRYPT_ALG_HANDLE_HMAC_FLAG) output_buffer = buffer_from_bytes(key_length) res = bcrypt.BCryptDeriveKeyPBKDF2( alg_handle, password, len(password), salt, len(salt), iterations, output_buffer, key_length, 0 ) handle_error(res) return bytes_from_buffer(output_buffer) finally: if alg_handle: close_alg_handle(alg_handle) pbkdf2.pure_python = False def rand_bytes(length): """ Returns a number of random bytes suitable for cryptographic purposes :param length: The desired number of bytes :raises: ValueError - when any of the parameters contain an invalid value TypeError - when any of the parameters are of the wrong type OSError - when an error is returned by the OS crypto library :return: A byte string """ if not isinstance(length, int_types): raise TypeError(pretty_message( ''' length must be an integer, not %s ''', type_name(length) )) if length < 1: raise ValueError('length must be greater than 0') if length > 1024: raise ValueError('length must not be greater than 1024') alg_handle = None try: alg_handle = open_alg_handle(BcryptConst.BCRYPT_RNG_ALGORITHM) buffer = buffer_from_bytes(length) res = bcrypt.BCryptGenRandom(alg_handle, buffer, length, 0) handle_error(res) return bytes_from_buffer(buffer) finally: if alg_handle: close_alg_handle(alg_handle) # winlegacy backend else: from .._pkcs5 import pbkdf2 from .._rand import rand_bytes oscrypto-1.3.0/oscrypto/asymmetric.py000066400000000000000000000315261421476274700200330ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import hashlib import binascii from . import backend from ._asn1 import ( armor, Certificate as Asn1Certificate, DHParameters, EncryptedPrivateKeyInfo, Null, OrderedDict, Pbkdf2Salt, PrivateKeyInfo, PublicKeyInfo, ) from ._asymmetric import _unwrap_private_key_info from ._errors import pretty_message from ._types import type_name, str_cls from .kdf import pbkdf2, pbkdf2_iteration_calculator from .symmetric import aes_cbc_pkcs7_encrypt from .util import rand_bytes _backend = backend() if _backend == 'mac': from ._mac.asymmetric import ( Certificate, dsa_sign, dsa_verify, ecdsa_sign, ecdsa_verify, generate_pair, generate_dh_parameters, load_certificate, load_pkcs12, load_private_key, load_public_key, PrivateKey, PublicKey, rsa_pkcs1v15_sign, rsa_pkcs1v15_verify, rsa_pss_sign, rsa_pss_verify, rsa_pkcs1v15_encrypt, rsa_pkcs1v15_decrypt, rsa_oaep_encrypt, rsa_oaep_decrypt, ) elif _backend == 'win' or _backend == 'winlegacy': from ._win.asymmetric import ( Certificate, dsa_sign, dsa_verify, ecdsa_sign, ecdsa_verify, generate_pair, generate_dh_parameters, load_certificate, load_pkcs12, load_private_key, load_public_key, PrivateKey, PublicKey, rsa_pkcs1v15_sign, rsa_pkcs1v15_verify, rsa_pss_sign, rsa_pss_verify, rsa_pkcs1v15_encrypt, rsa_pkcs1v15_decrypt, rsa_oaep_encrypt, rsa_oaep_decrypt, ) else: from ._openssl.asymmetric import ( Certificate, dsa_sign, dsa_verify, ecdsa_sign, ecdsa_verify, generate_pair, generate_dh_parameters, load_certificate, load_pkcs12, load_private_key, load_public_key, PrivateKey, PublicKey, rsa_pkcs1v15_sign, rsa_pkcs1v15_verify, rsa_pss_sign, rsa_pss_verify, rsa_pkcs1v15_encrypt, rsa_pkcs1v15_decrypt, rsa_oaep_encrypt, rsa_oaep_decrypt, ) __all__ = [ 'Certificate', 'dsa_sign', 'dsa_verify', 'dump_certificate', 'dump_dh_parameters', 'dump_openssl_private_key', 'dump_private_key', 'dump_public_key', 'ecdsa_sign', 'ecdsa_verify', 'generate_pair', 'generate_dh_parameters', 'load_certificate', 'load_pkcs12', 'load_private_key', 'load_public_key', 'PrivateKey', 'PublicKey', 'rsa_oaep_decrypt', 'rsa_oaep_encrypt', 'rsa_pkcs1v15_decrypt', 'rsa_pkcs1v15_encrypt', 'rsa_pkcs1v15_sign', 'rsa_pkcs1v15_verify', 'rsa_pss_sign', 'rsa_pss_verify', ] def dump_dh_parameters(dh_parameters, encoding='pem'): """ Serializes an asn1crypto.algos.DHParameters object into a byte string :param dh_parameters: An asn1crypto.algos.DHParameters object :param encoding: A unicode string of "pem" or "der" :return: A byte string of the encoded DH parameters """ if encoding not in set(['pem', 'der']): raise ValueError(pretty_message( ''' encoding must be one of "pem", "der", not %s ''', repr(encoding) )) if not isinstance(dh_parameters, DHParameters): raise TypeError(pretty_message( ''' dh_parameters must be an instance of asn1crypto.algos.DHParameters, not %s ''', type_name(dh_parameters) )) output = dh_parameters.dump() if encoding == 'pem': output = armor('DH PARAMETERS', output) return output def dump_public_key(public_key, encoding='pem'): """ Serializes a public key object into a byte string :param public_key: An oscrypto.asymmetric.PublicKey or asn1crypto.keys.PublicKeyInfo object :param encoding: A unicode string of "pem" or "der" :return: A byte string of the encoded public key """ if encoding not in set(['pem', 'der']): raise ValueError(pretty_message( ''' encoding must be one of "pem", "der", not %s ''', repr(encoding) )) is_oscrypto = isinstance(public_key, PublicKey) if not isinstance(public_key, PublicKeyInfo) and not is_oscrypto: raise TypeError(pretty_message( ''' public_key must be an instance of oscrypto.asymmetric.PublicKey or asn1crypto.keys.PublicKeyInfo, not %s ''', type_name(public_key) )) if is_oscrypto: public_key = public_key.asn1 output = public_key.dump() if encoding == 'pem': output = armor('PUBLIC KEY', output) return output def dump_certificate(certificate, encoding='pem'): """ Serializes a certificate object into a byte string :param certificate: An oscrypto.asymmetric.Certificate or asn1crypto.x509.Certificate object :param encoding: A unicode string of "pem" or "der" :return: A byte string of the encoded certificate """ if encoding not in set(['pem', 'der']): raise ValueError(pretty_message( ''' encoding must be one of "pem", "der", not %s ''', repr(encoding) )) is_oscrypto = isinstance(certificate, Certificate) if not isinstance(certificate, Asn1Certificate) and not is_oscrypto: raise TypeError(pretty_message( ''' certificate must be an instance of oscrypto.asymmetric.Certificate or asn1crypto.x509.Certificate, not %s ''', type_name(certificate) )) if is_oscrypto: certificate = certificate.asn1 output = certificate.dump() if encoding == 'pem': output = armor('CERTIFICATE', output) return output def dump_private_key(private_key, passphrase, encoding='pem', target_ms=200): """ Serializes a private key object into a byte string of the PKCS#8 format :param private_key: An oscrypto.asymmetric.PrivateKey or asn1crypto.keys.PrivateKeyInfo object :param passphrase: A unicode string of the passphrase to encrypt the private key with. A passphrase of None will result in no encryption. A blank string will result in a ValueError to help ensure that the lack of passphrase is intentional. :param encoding: A unicode string of "pem" or "der" :param target_ms: Use PBKDF2 with the number of iterations that takes about this many milliseconds on the current machine. :raises: ValueError - when a blank string is provided for the passphrase :return: A byte string of the encoded and encrypted public key """ if encoding not in set(['pem', 'der']): raise ValueError(pretty_message( ''' encoding must be one of "pem", "der", not %s ''', repr(encoding) )) if passphrase is not None: if not isinstance(passphrase, str_cls): raise TypeError(pretty_message( ''' passphrase must be a unicode string, not %s ''', type_name(passphrase) )) if passphrase == '': raise ValueError(pretty_message( ''' passphrase may not be a blank string - pass None to disable encryption ''' )) is_oscrypto = isinstance(private_key, PrivateKey) if not isinstance(private_key, PrivateKeyInfo) and not is_oscrypto: raise TypeError(pretty_message( ''' private_key must be an instance of oscrypto.asymmetric.PrivateKey or asn1crypto.keys.PrivateKeyInfo, not %s ''', type_name(private_key) )) if is_oscrypto: private_key = private_key.asn1 output = private_key.dump() if passphrase is not None: cipher = 'aes256_cbc' key_length = 32 kdf_hmac = 'sha256' kdf_salt = rand_bytes(key_length) iterations = pbkdf2_iteration_calculator(kdf_hmac, key_length, target_ms=target_ms, quiet=True) # Need a bare minimum of 10,000 iterations for PBKDF2 as of 2015 if iterations < 10000: iterations = 10000 passphrase_bytes = passphrase.encode('utf-8') key = pbkdf2(kdf_hmac, passphrase_bytes, kdf_salt, iterations, key_length) iv, ciphertext = aes_cbc_pkcs7_encrypt(key, output, None) output = EncryptedPrivateKeyInfo({ 'encryption_algorithm': { 'algorithm': 'pbes2', 'parameters': { 'key_derivation_func': { 'algorithm': 'pbkdf2', 'parameters': { 'salt': Pbkdf2Salt( name='specified', value=kdf_salt ), 'iteration_count': iterations, 'prf': { 'algorithm': kdf_hmac, 'parameters': Null() } } }, 'encryption_scheme': { 'algorithm': cipher, 'parameters': iv } } }, 'encrypted_data': ciphertext }).dump() if encoding == 'pem': if passphrase is None: object_type = 'PRIVATE KEY' else: object_type = 'ENCRYPTED PRIVATE KEY' output = armor(object_type, output) return output def dump_openssl_private_key(private_key, passphrase): """ Serializes a private key object into a byte string of the PEM formats used by OpenSSL. The format chosen will depend on the type of private key - RSA, DSA or EC. Do not use this method unless you really must interact with a system that does not support PKCS#8 private keys. The encryption provided by PKCS#8 is far superior to the OpenSSL formats. This is due to the fact that the OpenSSL formats don't stretch the passphrase, making it very easy to brute-force. :param private_key: An oscrypto.asymmetric.PrivateKey or asn1crypto.keys.PrivateKeyInfo object :param passphrase: A unicode string of the passphrase to encrypt the private key with. A passphrase of None will result in no encryption. A blank string will result in a ValueError to help ensure that the lack of passphrase is intentional. :raises: ValueError - when a blank string is provided for the passphrase :return: A byte string of the encoded and encrypted public key """ if passphrase is not None: if not isinstance(passphrase, str_cls): raise TypeError(pretty_message( ''' passphrase must be a unicode string, not %s ''', type_name(passphrase) )) if passphrase == '': raise ValueError(pretty_message( ''' passphrase may not be a blank string - pass None to disable encryption ''' )) is_oscrypto = isinstance(private_key, PrivateKey) if not isinstance(private_key, PrivateKeyInfo) and not is_oscrypto: raise TypeError(pretty_message( ''' private_key must be an instance of oscrypto.asymmetric.PrivateKey or asn1crypto.keys.PrivateKeyInfo, not %s ''', type_name(private_key) )) if is_oscrypto: private_key = private_key.asn1 output = _unwrap_private_key_info(private_key).dump() headers = None if passphrase is not None: iv = rand_bytes(16) headers = OrderedDict() headers['Proc-Type'] = '4,ENCRYPTED' headers['DEK-Info'] = 'AES-128-CBC,%s' % binascii.hexlify(iv).decode('ascii') key_length = 16 passphrase_bytes = passphrase.encode('utf-8') key = hashlib.md5(passphrase_bytes + iv[0:8]).digest() while key_length > len(key): key += hashlib.md5(key + passphrase_bytes + iv[0:8]).digest() key = key[0:key_length] iv, output = aes_cbc_pkcs7_encrypt(key, output, iv) if private_key.algorithm == 'ec': object_type = 'EC PRIVATE KEY' elif private_key.algorithm == 'rsa': object_type = 'RSA PRIVATE KEY' elif private_key.algorithm == 'dsa': object_type = 'DSA PRIVATE KEY' return armor(object_type, output, headers=headers) oscrypto-1.3.0/oscrypto/errors.py000066400000000000000000000035701421476274700171700ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys import socket __all__ = [ 'AsymmetricKeyError', 'CACertsError', 'LibraryNotFoundError', 'SignatureError', 'TLSError', 'TLSConnectionError', 'TLSDisconnectError', 'TLSGracefulDisconnectError', 'TLSVerificationError', ] class LibraryNotFoundError(Exception): """ An exception when trying to find a shared library """ pass class SignatureError(Exception): """ An exception when validating a signature """ pass class AsymmetricKeyError(Exception): """ An exception when a key is invalid or unsupported """ pass class IncompleteAsymmetricKeyError(AsymmetricKeyError): """ An exception when a key is missing necessary information """ pass class CACertsError(Exception): """ An exception when exporting CA certs from the OS trust store """ pass class TLSError(socket.error): """ An exception related to TLS functionality """ message = None def __init__(self, message): self.args = (message,) self.message = message def __str__(self): output = self.__unicode__() if sys.version_info < (3,): output = output.encode('utf-8') return output def __unicode__(self): return self.message class TLSConnectionError(TLSError): pass class TLSDisconnectError(TLSConnectionError): pass class TLSGracefulDisconnectError(TLSDisconnectError): pass class TLSVerificationError(TLSError): """ A server certificate verification error happened during a TLS handshake """ certificate = None def __init__(self, message, certificate): TLSError.__init__(self, message) self.certificate = certificate self.args = (message, certificate) oscrypto-1.3.0/oscrypto/kdf.py000066400000000000000000000162621421476274700164220ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys import hashlib from datetime import datetime from . import backend from .util import rand_bytes from ._types import type_name, byte_cls, int_types from ._errors import pretty_message from ._ffi import new, deref _backend = backend() if _backend == 'mac': from ._mac.util import pbkdf2, pkcs12_kdf elif _backend == 'win' or _backend == 'winlegacy': from ._win.util import pbkdf2, pkcs12_kdf from ._win._kernel32 import kernel32, handle_error else: from ._openssl.util import pbkdf2, pkcs12_kdf __all__ = [ 'pbkdf1', 'pbkdf2', 'pbkdf2_iteration_calculator', 'pkcs12_kdf', ] if sys.platform == 'win32': def _get_start(): number = new(kernel32, 'LARGE_INTEGER *') res = kernel32.QueryPerformanceCounter(number) handle_error(res) return deref(number) def _get_elapsed(start): length = _get_start() - start return int(length / 1000.0) else: def _get_start(): return datetime.now() def _get_elapsed(start): length = datetime.now() - start seconds = length.seconds + (length.days * 24 * 3600) milliseconds = (length.microseconds / 10 ** 3) return int(milliseconds + (seconds * 10 ** 3)) def pbkdf2_iteration_calculator(hash_algorithm, key_length, target_ms=100, quiet=False): """ Runs pbkdf2() twice to determine the approximate number of iterations to use to hit a desired time per run. Use this on a production machine to dynamically adjust the number of iterations as high as you can. :param hash_algorithm: The string name of the hash algorithm to use: "md5", "sha1", "sha224", "sha256", "sha384", "sha512" :param key_length: The length of the desired key in bytes :param target_ms: The number of milliseconds the derivation should take :param quiet: If no output should be printed as attempts are made :return: An integer number of iterations of PBKDF2 using the specified hash that will take at least target_ms """ if hash_algorithm not in set(['sha1', 'sha224', 'sha256', 'sha384', 'sha512']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "sha1", "sha224", "sha256", "sha384", "sha512", not %s ''', repr(hash_algorithm) )) if not isinstance(key_length, int_types): raise TypeError(pretty_message( ''' key_length must be an integer, not %s ''', type_name(key_length) )) if key_length < 1: raise ValueError(pretty_message( ''' key_length must be greater than 0 - is %s ''', repr(key_length) )) if not isinstance(target_ms, int_types): raise TypeError(pretty_message( ''' target_ms must be an integer, not %s ''', type_name(target_ms) )) if target_ms < 1: raise ValueError(pretty_message( ''' target_ms must be greater than 0 - is %s ''', repr(target_ms) )) if pbkdf2.pure_python: raise OSError(pretty_message( ''' Only a very slow, pure-python version of PBKDF2 is available, making this function useless ''' )) iterations = 10000 password = 'this is a test'.encode('utf-8') salt = rand_bytes(key_length) def _measure(): start = _get_start() pbkdf2(hash_algorithm, password, salt, iterations, key_length) observed_ms = _get_elapsed(start) if not quiet: print('%s iterations in %sms' % (iterations, observed_ms)) return 1.0 / target_ms * observed_ms # Measure the initial guess, then estimate how many iterations it would # take to reach 1/2 of the target ms and try it to get a good final number fraction = _measure() iterations = int(iterations / fraction / 2.0) fraction = _measure() iterations = iterations / fraction # < 20,000 round to 1000 # 20,000-100,000 round to 5,000 # > 100,000 round to 10,000 round_factor = -3 if iterations < 100000 else -4 result = int(round(iterations, round_factor)) if result > 20000: result = (result // 5000) * 5000 return result def pbkdf1(hash_algorithm, password, salt, iterations, key_length): """ An implementation of PBKDF1 - should only be used for interop with legacy systems, not new architectures :param hash_algorithm: The string name of the hash algorithm to use: "md2", "md5", "sha1" :param password: A byte string of the password to use an input to the KDF :param salt: A cryptographic random byte string :param iterations: The numbers of iterations to use when deriving the key :param key_length: The length of the desired key in bytes :return: The derived key as a byte string """ if not isinstance(password, byte_cls): raise TypeError(pretty_message( ''' password must be a byte string, not %s ''', (type_name(password)) )) if not isinstance(salt, byte_cls): raise TypeError(pretty_message( ''' salt must be a byte string, not %s ''', (type_name(salt)) )) if not isinstance(iterations, int_types): raise TypeError(pretty_message( ''' iterations must be an integer, not %s ''', (type_name(iterations)) )) if iterations < 1: raise ValueError(pretty_message( ''' iterations must be greater than 0 - is %s ''', repr(iterations) )) if not isinstance(key_length, int_types): raise TypeError(pretty_message( ''' key_length must be an integer, not %s ''', (type_name(key_length)) )) if key_length < 1: raise ValueError(pretty_message( ''' key_length must be greater than 0 - is %s ''', repr(key_length) )) if hash_algorithm not in set(['md2', 'md5', 'sha1']): raise ValueError(pretty_message( ''' hash_algorithm must be one of "md2", "md5", "sha1", not %s ''', repr(hash_algorithm) )) if key_length > 16 and hash_algorithm in set(['md2', 'md5']): raise ValueError(pretty_message( ''' key_length can not be longer than 16 for %s - is %s ''', (hash_algorithm, repr(key_length)) )) if key_length > 20 and hash_algorithm == 'sha1': raise ValueError(pretty_message( ''' key_length can not be longer than 20 for sha1 - is %s ''', repr(key_length) )) algo = getattr(hashlib, hash_algorithm) output = algo(password + salt).digest() for _ in range(2, iterations + 1): output = algo(output).digest() return output[:key_length] oscrypto-1.3.0/oscrypto/keys.py000066400000000000000000000010361421476274700166220ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from . import backend from ._asymmetric import parse_certificate, parse_private, parse_public _backend = backend() if _backend == 'mac': from ._mac.asymmetric import parse_pkcs12 elif _backend == 'win' or _backend == 'winlegacy': from ._win.asymmetric import parse_pkcs12 else: from ._openssl.asymmetric import parse_pkcs12 __all__ = [ 'parse_certificate', 'parse_pkcs12', 'parse_private', 'parse_public', ] oscrypto-1.3.0/oscrypto/symmetric.py000066400000000000000000000034601421476274700176660ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from . import backend _backend = backend() if _backend == 'mac': from ._mac.symmetric import ( aes_cbc_no_padding_decrypt, aes_cbc_no_padding_encrypt, aes_cbc_pkcs7_decrypt, aes_cbc_pkcs7_encrypt, des_cbc_pkcs5_decrypt, des_cbc_pkcs5_encrypt, rc2_cbc_pkcs5_decrypt, rc2_cbc_pkcs5_encrypt, rc4_decrypt, rc4_encrypt, tripledes_cbc_pkcs5_decrypt, tripledes_cbc_pkcs5_encrypt, ) elif _backend == 'win' or _backend == 'winlegacy': from ._win.symmetric import ( aes_cbc_no_padding_decrypt, aes_cbc_no_padding_encrypt, aes_cbc_pkcs7_decrypt, aes_cbc_pkcs7_encrypt, des_cbc_pkcs5_decrypt, des_cbc_pkcs5_encrypt, rc2_cbc_pkcs5_decrypt, rc2_cbc_pkcs5_encrypt, rc4_decrypt, rc4_encrypt, tripledes_cbc_pkcs5_decrypt, tripledes_cbc_pkcs5_encrypt, ) else: from ._openssl.symmetric import ( aes_cbc_no_padding_decrypt, aes_cbc_no_padding_encrypt, aes_cbc_pkcs7_decrypt, aes_cbc_pkcs7_encrypt, des_cbc_pkcs5_decrypt, des_cbc_pkcs5_encrypt, rc2_cbc_pkcs5_decrypt, rc2_cbc_pkcs5_encrypt, rc4_decrypt, rc4_encrypt, tripledes_cbc_pkcs5_decrypt, tripledes_cbc_pkcs5_encrypt, ) __all__ = [ 'aes_cbc_no_padding_decrypt', 'aes_cbc_no_padding_encrypt', 'aes_cbc_pkcs7_decrypt', 'aes_cbc_pkcs7_encrypt', 'des_cbc_pkcs5_decrypt', 'des_cbc_pkcs5_encrypt', 'rc2_cbc_pkcs5_decrypt', 'rc2_cbc_pkcs5_encrypt', 'rc4_decrypt', 'rc4_encrypt', 'tripledes_cbc_pkcs5_decrypt', 'tripledes_cbc_pkcs5_encrypt', ] oscrypto-1.3.0/oscrypto/tls.py000066400000000000000000000007661421476274700164620ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from . import backend _backend = backend() if _backend == 'mac': from ._mac.tls import ( TLSSession, TLSSocket, ) elif _backend == 'win' or _backend == 'winlegacy': from ._win.tls import ( TLSSession, TLSSocket, ) else: from ._openssl.tls import ( TLSSession, TLSSocket, ) __all__ = [ 'TLSSession', 'TLSSocket', ] oscrypto-1.3.0/oscrypto/trust_list.py000066400000000000000000000320011421476274700200570ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import os import time import sys import tempfile import threading from ._asn1 import armor, Certificate from ._errors import pretty_message from .errors import CACertsError if sys.platform == 'win32': from ._win.trust_list import extract_from_system, system_path elif sys.platform == 'darwin': from ._mac.trust_list import extract_from_system, system_path else: from ._linux_bsd.trust_list import extract_from_system, system_path __all__ = [ 'clear_cache', 'get_list', 'get_path', ] path_lock = threading.Lock() memory_lock = threading.Lock() _module_values = { 'last_update': None, 'certs': None } _oid_map = { # apple_smime -> email_protection '1.2.840.113635.100.1.8': set(['1.3.6.1.5.5.7.3.4']), # apple_code_signing -> code_signing '1.2.840.113635.100.1.16': set(['1.3.6.1.5.5.7.3.3']), # apple_time_stamping -> time_stamping '1.2.840.113635.100.1.20': set(['1.3.6.1.5.5.7.3.8']), # microsoft_time_stamp_signing -> time_stamping '1.3.6.1.4.1.311.10.3.2': set(['1.3.6.1.5.5.7.3.8']), # apple_ssl -> (server_auth, client_auth) '1.2.840.113635.100.1.3': set([ '1.3.6.1.5.5.7.3.1', '1.3.6.1.5.5.7.3.2', ]), # apple_eap -> (eap_over_ppp, eap_over_lan) '1.2.840.113635.100.1.9': set([ '1.3.6.1.5.5.7.3.13', '1.3.6.1.5.5.7.3.14', ]), # apple_ipsec -> (ipsec_end_system, ipsec_tunnel, ipsec_user, ipsec_ike) '1.2.840.113635.100.1.11': set([ '1.3.6.1.5.5.7.3.5', '1.3.6.1.5.5.7.3.6', '1.3.6.1.5.5.7.3.7', '1.3.6.1.5.5.7.3.17', ]) } def get_path(temp_dir=None, cache_length=24, cert_callback=None): """ Get the filesystem path to a file that contains OpenSSL-compatible CA certs. On OS X and Windows, there are extracted from the system certificate store and cached in a file on the filesystem. This path should not be writable by other users, otherwise they could inject CA certs into the trust list. :param temp_dir: The temporary directory to cache the CA certs in on OS X and Windows. Needs to have secure permissions so other users can not modify the contents. :param cache_length: The number of hours to cache the CA certs on OS X and Windows :param cert_callback: A callback that is called once for each certificate in the trust store. It should accept two parameters: an asn1crypto.x509.Certificate object, and a reason. The reason will be None if the certificate is being exported, otherwise it will be a unicode string of the reason it won't. This is only called on Windows and OS X when passed to this function. :raises: oscrypto.errors.CACertsError - when an error occurs exporting/locating certs :return: The full filesystem path to a CA certs file """ ca_path, temp = _ca_path(temp_dir) # Windows and OS X if temp and _cached_path_needs_update(ca_path, cache_length): empty_set = set() any_purpose = '2.5.29.37.0' apple_ssl = '1.2.840.113635.100.1.3' win_server_auth = '1.3.6.1.5.5.7.3.1' with path_lock: if _cached_path_needs_update(ca_path, cache_length): with open(ca_path, 'wb') as f: for cert, trust_oids, reject_oids in extract_from_system(cert_callback, True): if sys.platform == 'darwin': if trust_oids != empty_set and any_purpose not in trust_oids \ and apple_ssl not in trust_oids: if cert_callback: cert_callback(Certificate.load(cert), 'implicitly distrusted for TLS') continue if reject_oids != empty_set and (apple_ssl in reject_oids or any_purpose in reject_oids): if cert_callback: cert_callback(Certificate.load(cert), 'explicitly distrusted for TLS') continue elif sys.platform == 'win32': if trust_oids != empty_set and any_purpose not in trust_oids \ and win_server_auth not in trust_oids: if cert_callback: cert_callback(Certificate.load(cert), 'implicitly distrusted for TLS') continue if reject_oids != empty_set and (win_server_auth in reject_oids or any_purpose in reject_oids): if cert_callback: cert_callback(Certificate.load(cert), 'explicitly distrusted for TLS') continue if cert_callback: cert_callback(Certificate.load(cert), None) f.write(armor('CERTIFICATE', cert)) if not ca_path: raise CACertsError('No CA certs found') return ca_path def get_list(cache_length=24, map_vendor_oids=True, cert_callback=None): """ Retrieves (and caches in memory) the list of CA certs from the OS. Includes trust information from the OS - purposes the certificate should be trusted or rejected for. Trust information is encoded via object identifiers (OIDs) that are sourced from various RFCs and vendors (Apple and Microsoft). This trust information augments what is in the certificate itself. Any OID that is in the set of trusted purposes indicates the certificate has been explicitly trusted for a purpose beyond the extended key purpose extension. Any OID in the reject set is a purpose that the certificate should not be trusted for, even if present in the extended key purpose extension. *A list of common trust OIDs can be found as part of the `KeyPurposeId()` class in the `asn1crypto.x509` module of the `asn1crypto` package.* :param cache_length: The number of hours to cache the CA certs in memory before they are refreshed :param map_vendor_oids: A bool indicating if the following mapping of OIDs should happen for trust information from the OS trust list: - 1.2.840.113635.100.1.3 (apple_ssl) -> 1.3.6.1.5.5.7.3.1 (server_auth) - 1.2.840.113635.100.1.3 (apple_ssl) -> 1.3.6.1.5.5.7.3.2 (client_auth) - 1.2.840.113635.100.1.8 (apple_smime) -> 1.3.6.1.5.5.7.3.4 (email_protection) - 1.2.840.113635.100.1.9 (apple_eap) -> 1.3.6.1.5.5.7.3.13 (eap_over_ppp) - 1.2.840.113635.100.1.9 (apple_eap) -> 1.3.6.1.5.5.7.3.14 (eap_over_lan) - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.5 (ipsec_end_system) - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.6 (ipsec_tunnel) - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.7 (ipsec_user) - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.17 (ipsec_ike) - 1.2.840.113635.100.1.16 (apple_code_signing) -> 1.3.6.1.5.5.7.3.3 (code_signing) - 1.2.840.113635.100.1.20 (apple_time_stamping) -> 1.3.6.1.5.5.7.3.8 (time_stamping) - 1.3.6.1.4.1.311.10.3.2 (microsoft_time_stamp_signing) -> 1.3.6.1.5.5.7.3.8 (time_stamping) :param cert_callback: A callback that is called once for each certificate in the trust store. It should accept two parameters: an asn1crypto.x509.Certificate object, and a reason. The reason will be None if the certificate is being exported, otherwise it will be a unicode string of the reason it won't. :raises: oscrypto.errors.CACertsError - when an error occurs exporting/locating certs :return: A (copied) list of 3-element tuples containing CA certs from the OS trust ilst: - 0: an asn1crypto.x509.Certificate object - 1: a set of unicode strings of OIDs of trusted purposes - 2: a set of unicode strings of OIDs of rejected purposes """ if not _in_memory_up_to_date(cache_length): with memory_lock: if not _in_memory_up_to_date(cache_length): certs = [] for cert_bytes, trust_oids, reject_oids in extract_from_system(cert_callback): if map_vendor_oids: trust_oids = _map_oids(trust_oids) reject_oids = _map_oids(reject_oids) certs.append((Certificate.load(cert_bytes), trust_oids, reject_oids)) _module_values['certs'] = certs _module_values['last_update'] = time.time() return list(_module_values['certs']) def clear_cache(temp_dir=None): """ Clears any cached info that was exported from the OS trust store. This will ensure the latest changes are returned from calls to get_list() and get_path(), but at the expense of re-exporting and parsing all certificates. :param temp_dir: The temporary directory to cache the CA certs in on OS X and Windows. Needs to have secure permissions so other users can not modify the contents. Must be the same value passed to get_path(). """ with memory_lock: _module_values['last_update'] = None _module_values['certs'] = None ca_path, temp = _ca_path(temp_dir) if temp: with path_lock: if os.path.exists(ca_path): os.remove(ca_path) def _ca_path(temp_dir=None): """ Returns the file path to the CA certs file :param temp_dir: The temporary directory to cache the CA certs in on OS X and Windows. Needs to have secure permissions so other users can not modify the contents. :return: A 2-element tuple: - 0: A unicode string of the file path - 1: A bool if the file is a temporary file """ ca_path = system_path() # Windows and OS X if ca_path is None: if temp_dir is None: temp_dir = tempfile.gettempdir() if not os.path.isdir(temp_dir): raise CACertsError(pretty_message( ''' The temp dir specified, "%s", is not a directory ''', temp_dir )) ca_path = os.path.join(temp_dir, 'oscrypto-ca-bundle.crt') return (ca_path, True) return (ca_path, False) def _map_oids(oids): """ Takes a set of unicode string OIDs and converts vendor-specific OIDs into generics OIDs from RFCs. - 1.2.840.113635.100.1.3 (apple_ssl) -> 1.3.6.1.5.5.7.3.1 (server_auth) - 1.2.840.113635.100.1.3 (apple_ssl) -> 1.3.6.1.5.5.7.3.2 (client_auth) - 1.2.840.113635.100.1.8 (apple_smime) -> 1.3.6.1.5.5.7.3.4 (email_protection) - 1.2.840.113635.100.1.9 (apple_eap) -> 1.3.6.1.5.5.7.3.13 (eap_over_ppp) - 1.2.840.113635.100.1.9 (apple_eap) -> 1.3.6.1.5.5.7.3.14 (eap_over_lan) - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.5 (ipsec_end_system) - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.6 (ipsec_tunnel) - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.7 (ipsec_user) - 1.2.840.113635.100.1.11 (apple_ipsec) -> 1.3.6.1.5.5.7.3.17 (ipsec_ike) - 1.2.840.113635.100.1.16 (apple_code_signing) -> 1.3.6.1.5.5.7.3.3 (code_signing) - 1.2.840.113635.100.1.20 (apple_time_stamping) -> 1.3.6.1.5.5.7.3.8 (time_stamping) - 1.3.6.1.4.1.311.10.3.2 (microsoft_time_stamp_signing) -> 1.3.6.1.5.5.7.3.8 (time_stamping) :param oids: A set of unicode strings :return: The original set of OIDs with any mapped OIDs added """ new_oids = set() for oid in oids: if oid in _oid_map: new_oids |= _oid_map[oid] return oids | new_oids def _cached_path_needs_update(ca_path, cache_length): """ Checks to see if a cache file needs to be refreshed :param ca_path: A unicode string of the path to the cache file :param cache_length: An integer representing the number of hours the cache is valid for :return: A boolean - True if the cache needs to be updated, False if the file is up-to-date """ exists = os.path.exists(ca_path) if not exists: return True stats = os.stat(ca_path) if stats.st_mtime < time.time() - cache_length * 60 * 60: return True if stats.st_size == 0: return True return False def _in_memory_up_to_date(cache_length): """ Checks to see if the in-memory cache of certificates is fresh :param cache_length: An integer representing the number of hours the cache is valid for :return: A boolean - True if the cache is up-to-date, False if it needs to be refreshed """ return ( _module_values['certs'] and _module_values['last_update'] and _module_values['last_update'] > time.time() - (cache_length * 60 * 60) ) oscrypto-1.3.0/oscrypto/util.py000066400000000000000000000024631421476274700166310ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys from ._errors import pretty_message from ._types import type_name, byte_cls if sys.platform == 'darwin': from ._mac.util import rand_bytes elif sys.platform == 'win32': from ._win.util import rand_bytes else: from ._openssl.util import rand_bytes __all__ = [ 'constant_compare', 'rand_bytes', ] def constant_compare(a, b): """ Compares two byte strings in constant time to see if they are equal :param a: The first byte string :param b: The second byte string :return: A boolean if the two byte strings are equal """ if not isinstance(a, byte_cls): raise TypeError(pretty_message( ''' a must be a byte string, not %s ''', type_name(a) )) if not isinstance(b, byte_cls): raise TypeError(pretty_message( ''' b must be a byte string, not %s ''', type_name(b) )) if len(a) != len(b): return False if sys.version_info < (3,): a = [ord(char) for char in a] b = [ord(char) for char in b] result = 0 for x, y in zip(a, b): result |= x ^ y return result == 0 oscrypto-1.3.0/oscrypto/version.py000066400000000000000000000002301421476274700173270ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function __version__ = '1.3.0' __version_info__ = (1, 3, 0) oscrypto-1.3.0/readme.md000066400000000000000000000331311421476274700151730ustar00rootroot00000000000000# oscrypto A compilation-free, always up-to-date encryption library for Python that works on Windows, OS X, Linux and BSD. Supports the following versions of Python: 2.6, 2.7, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10 and pypy. - [Supported Operating Systems](#supported-operationg-systems) - [Features](#features) - [Why Another Python Crypto Library?](#why-another-python-crypto-library) - [Related Crypto Libraries](#related-crypto-libraries) - [Current Release](#current-release) - [Dependencies](#dependencies) - [Installation](#installation) - [License](#license) - [Documentation](#documentation) - [Continuous Integration](#continuous-integration) - [Testing](#testing) - [Development](#development) - [CI Tasks](#ci-tasks) [![GitHub Actions CI](https://github.com/wbond/oscrypto/workflows/CI/badge.svg)](https://github.com/wbond/oscrypto/actions?workflow=CI) [![CircleCI](https://circleci.com/gh/wbond/oscrypto.svg?style=shield)](https://circleci.com/gh/wbond/oscrypto) [![PyPI](https://img.shields.io/pypi/v/oscrypto.svg)](https://pypi.python.org/pypi/oscrypto) ## Supported Operating Systems The library integrates with the encryption library that is part of the operating system. This means that a compiler is never needed, and OS security updates take care of patching vulnerabilities. Supported operating systems include: - Windows XP or newer - Uses: - [Cryptography API: Next Generation (CNG)](https://msdn.microsoft.com/en-us/library/windows/desktop/aa376210(v=vs.85).aspx) - [Secure Channel](https://msdn.microsoft.com/en-us/library/windows/desktop/aa380123(v=vs.85).aspx) for TLS - [CryptoAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/aa380256(v=vs.85).aspx) for trust lists and XP support - Tested on: - Windows XP (no SNI) - Windows 7 - Windows 8.1 - Windows Server 2012 - Windows 10 - OS X 10.7 or newer - Uses: - [Security.framework](https://developer.apple.com/library/prerelease/mac/documentation/Security/Reference/SecurityFrameworkReference/index.html) - [Secure Transport](https://developer.apple.com/library/prerelease/mac/documentation/Security/Reference/secureTransportRef/index.html#//apple_ref/doc/uid/TP30000155) for TLS - [CommonCrypto](http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/CommonCrypto/) for PBKDF2 - OpenSSL (or LibreSSL on macOS 10.13) for the PKCS #12 KDF - Tested on: - OS X 10.7 - OS X 10.8 - OS X 10.9 - OS X 10.10 - OS X 10.11 - OS X 10.11 with OpenSSL 1.1.0 - macOS 10.12 - macOS 10.13 with LibreSSL 2.2.7 - macOS 10.14 - macOS 10.15 - macOS 10.15 with OpenSSL 3.0 - macOS 11 - macOS 12 - Linux or BSD - Uses one of: - [OpenSSL 0.9.8](https://www.openssl.org/docs/man0.9.8/) - [OpenSSL 1.0.x](https://www.openssl.org/docs/man1.0.0/) - [OpenSSL 1.1.0](https://www.openssl.org/docs/man1.1.0/) - [OpenSSL 3.0](https://www.openssl.org/docs/man3.0/) - [LibreSSL](http://www.libressl.org/) - Tested on: - Arch Linux with OpenSSL 1.0.2 - OpenBSD 5.7 with LibreSSL - Ubuntu 10.04 with OpenSSL 0.9.8 - Ubuntu 12.04 with OpenSSL 1.0.1 - Ubuntu 15.04 with OpenSSL 1.0.1 - Ubuntu 16.04 with OpenSSL 1.0.2 on Raspberry Pi 3 (armhf) - Ubuntu 18.04 with OpenSSL 1.1.x (amd64, arm64, ppc64el) - Ubuntu 22.04 with OpenSSL 3.0 (amd64) *OS X 10.6 will not be supported due to a lack of available cryptographic primitives and due to lack of vendor support.* ## Features Currently the following features are implemented. Many of these should only be used for integration with existing/legacy systems. If you don't know which you should, or should not use, please see [Learning](docs/readme.md#learning). - [TLSv1.x socket wrappers](docs/tls.md) - Certificate verification performed by OS trust roots - Custom CA certificate support - SNI support (except Windows XP) - Session reuse via IDs/tickets - Modern cipher suites (RC4, DES, anon and NULL ciphers disabled) - Weak DH parameters and certificate signatures rejected - SSLv3 disabled by default, SSLv2 unimplemented - CRL/OCSP revocation checks consistenty disabled - [Exporting OS trust roots](docs/trust_list.md) - PEM-formatted CA certs from the OS for OpenSSL-based code - [Encryption/decryption](docs/symmetric.md) - AES (128, 192, 256), CBC mode, PKCS7 padding - AES (128, 192, 256), CBC mode, no padding - TripleDES 3-key, CBC mode, PKCS5 padding - TripleDes 2-key, CBC mode, PKCS5 padding - DES, CBC mode, PKCS5 padding - RC2 (40-128), CBC mode, PKCS5 padding - RC4 (40-128) - RSA PKCSv1.5 - RSA OAEP (SHA1 only) - [Generating public/private key pairs](docs/asymmetric.md) - RSA (1024, 2048, 3072, 4096 bit) - DSA (1024 bit on all platforms - 2048, 3072 bit with OpenSSL 1.x or Windows 8) - EC (secp256r1, secp384r1, secp521r1 curves) - [Generating DH parameters](docs/asymmetric.md) - [Signing and verification](docs/asymmetric.md) - RSA PKCSv1.5 - RSA PSS - DSA - EC - [Loading and normalizing DER and PEM formatted keys](docs/keys.md) - RSA public and private keys - DSA public and private keys - EC public and private keys - X.509 Certificates - PKCS#12 archives (`.pfx`/`.p12`) - [Key derivation](docs/kdf.md) - PBKDF2 - PBKDF1 - PKCS#12 KDF - [Random byte generation](docs/util.md) The feature set was largely driven by the technologies used related to generating and validating X.509 certificates. The various CBC encryption schemes and KDFs are used to load encrypted private keys, and the various RSA padding schemes are part of X.509 signatures. For modern cryptography not tied to an existing system, please see the [Modern Cryptography](docs/readme.md#modern-cryptography) section of the docs. *Please note that this library does not include modern block modes such as CTR and GCM due to lack of support from both OS X and OpenSSL 0.9.8.* ## Why Another Python Crypto Library? In short, the existing cryptography libraries for Python didn't fit the needs of a couple of projects I was working on. Primarily these are applications distributed to end-users who aren't programmers, that need to handle TLS and various technologies related to X.509 certificates. If your system is not tied to AES, TLS, X.509, or related technologies, you probably want [more modern cryptography](docs/readme.md#modern-cryptography). Depending on your needs, the [cryptography](https://cryptography.io) package may be a good (or better) fit. Some things that make oscrypto unique: - No compiler needed, ever. No need to pre-compile shared libraries. Just distribute the Python source files, any way you want. - Uses the operating system's crypto library - does not require OpenSSL on Windows or OS X. - Relies on the operating system for security patching. You don't need to rebuild all of your apps every time there is a new TLS vulnerability. - Intentionally limited in scope to crypto primitives. Other libraries built upon it deal with certificate path validation, creating certificates and CSRs, constructing CMS structures. - Built on top of a fast, pure-Python ASN.1 parser, [asn1crypto](https://github.com/wbond/asn1crypto). - TLS functionality uses the operating system's trust list/CA certs and is pre-configured with sane defaults - Public APIs are simple and use strict type checks to avoid errors Some downsides include: - Does not currently implement: - standalone DH key exchange - various encryption modes such as GCM, CCM, CTR, CFB, OFB, ECB - key wrapping - CMAC - HKDF - Non-TLS functionality is architected for dealing with data that fits in memory and is available all at once - Developed by a single developer ## Related Crypto Libraries *oscrypto* is part of the modularcrypto family of Python packages: - [asn1crypto](https://github.com/wbond/asn1crypto) - [oscrypto](https://github.com/wbond/oscrypto) - [csrbuilder](https://github.com/wbond/csrbuilder) - [certbuilder](https://github.com/wbond/certbuilder) - [crlbuilder](https://github.com/wbond/crlbuilder) - [ocspbuilder](https://github.com/wbond/ocspbuilder) - [certvalidator](https://github.com/wbond/certvalidator) ## Current Release 1.2.1 - [changelog](changelog.md) ## Dependencies - [*asn1crypto*](https://github.com/wbond/asn1crypto) - Python 2.6, 2.7, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10 or pypy - OpenSSL/LibreSSL if on Linux¹ *¹ On Linux, `ctypes.util.find_library()` is used to located OpenSSL. Alpine Linux does not have an appropriate install by default for `find_library()` to work properly. Instead, `oscrypto.use_openssl()` must be called with the path to the OpenSSL shared libraries.* ## Installation ```bash pip install oscrypto ``` ## License *oscrypto* is licensed under the terms of the MIT license. See the [LICENSE](LICENSE) file for the exact license text. ## Documentation [*oscrypto* documentation](docs/readme.md) ## Continuous Integration Various combinations of platforms and versions of Python are tested via: - [macOS, Linux, Windows](https://github.com/wbond/oscrypto/actions/workflows/ci.yml) via GitHub Actions - [arm64](https://circleci.com/gh/wbond/oscrypto) via CircleCI ## Testing Tests are written using `unittest` and require no third-party packages. Depending on what type of source is available for the package, the following commands can be used to run the test suite. ### Git Repository When working within a Git working copy, or an archive of the Git repository, the full test suite is run via: ```bash python run.py tests ``` To run only some tests, pass a regular expression as a parameter to `tests`. ```bash python run.py tests aes ``` To run tests multiple times, in order to catch edge-case bugs, pass an integer to `tests`. If combined with a regular expression for filtering, pass the repeat count after the regular expression. ```bash python run.py tests 20 python run.py tests aes 20 ``` #### Backend Options To run tests using a custom build of OpenSSL, or to use OpenSSL on Windows or Mac, add `use_openssl` after `run.py`, like: ```bash python run.py use_openssl=/path/to/libcrypto.so,/path/to/libssl.so tests ``` To run tests forcing the use of ctypes, even if cffi is installed, add `use_ctypes` after `run.py`: ```bash python run.py use_ctypes=true tests ``` To run tests using the legacy Windows crypto functions on Windows 7+, add `use_winlegacy` after `run.py`: ```bash python run.py use_winlegacy=true tests ``` #### Internet Tests To skip tests that require an internet connection, add `skip_internet` after `run.py`: ```bash python run.py skip_internet=true tests ``` ### PyPi Source Distribution When working within an extracted source distribution (aka `.tar.gz`) from PyPi, the full test suite is run via: ```bash python setup.py test ``` #### Test Options The following env vars can control aspects of running tests: ##### Force OpenSSL Shared Library Paths Setting the env var `OSCRYPTO_USE_OPENSSL` to a string in the form: ``` /path/to/libcrypto.so,/path/to/libssl.so ``` will force use of specific OpenSSL shared libraries. This also works on Mac and Windows to force use of OpenSSL instead of using native crypto libraries. ##### Force Use of ctypes By default, oscrypto will use the `cffi` module for FFI if it is installed. To use the slightly slower, but more widely-tested, `ctypes` FFI layer, set the env var `OPENSSL_USE_CTYPES=true`. ##### Force Use of Legacy Windows Crypto APIs On Windows 7 and newer, oscrypto will use the CNG backend by default. To force use of the older CryptoAPI, set the env var `OPENSSL_USE_WINLEGACY=true`. ##### Skip Tests Requiring an Internet Connection Some of the TLS tests require an active internet connection to ensure that various "bad" server certificates are rejected. To skip tests requiring an internet connection, set the env var `OPENSSL_SKIP_INTERNET_TESTS=true`. ### Package When the package has been installed via pip (or another method), the package `oscrypto_tests` may be installed and invoked to run the full test suite: ```bash pip install oscrypto_tests python -m oscrypto_tests ``` ## Development To install the package used for linting, execute: ```bash pip install --user -r requires/lint ``` The following command will run the linter: ```bash python run.py lint ``` Support for code coverage can be installed via: ```bash pip install --user -r requires/coverage ``` Coverage is measured by running: ```bash python run.py coverage ``` To install the packages requires to generate the API documentation, run: ```bash pip install --user -r requires/api_docs ``` The documentation can then be generated by running: ```bash python run.py api_docs ``` To install the necessary packages for releasing a new version on PyPI, run: ```bash pip install --user -r requires/release ``` Releases are created by: - Making a git tag in [semver](http://semver.org/) format - Running the command: ```bash python run.py release ``` Existing releases can be found at https://pypi.python.org/pypi/oscrypto. ## CI Tasks A task named `deps` exists to download and stage all necessary testing dependencies. On posix platforms, `curl` is used for downloads and on Windows PowerShell with `Net.WebClient` is used. This configuration sidesteps issues related to getting pip to work properly and messing with `site-packages` for the version of Python being used. The `ci` task runs `lint` (if flake8 is available for the version of Python) and `coverage` (or `tests` if coverage is not available for the version of Python). If the current directory is a clean git working copy, the coverage data is submitted to codecov.io. ```bash python run.py deps python run.py ci ``` oscrypto-1.3.0/requires/000077500000000000000000000000001421476274700152525ustar00rootroot00000000000000oscrypto-1.3.0/requires/api_docs000066400000000000000000000000241421476274700167520ustar00rootroot00000000000000CommonMark >= 0.6.0 oscrypto-1.3.0/requires/ci000066400000000000000000000007151421476274700155730ustar00rootroot00000000000000setuptools == 36.8.0 ; python_version == '2.6' setuptools == 18.4 ; python_version == '3.2' setuptools == 39.2.0 ; python_version == '3.3' https://github.com/wbond/asn1crypto/archive/master.zip -r ./coverage -r ./lint # cffi 3.15.0 is required for Python 3.10 cffi == 1.15.0 ; (python_version == '2.7' or python_version >= '3.6') and sys_platform == 'darwin' pycparser == 2.19 ; (python_version == '2.7' or python_version >= '3.6') and sys_platform == 'darwin' oscrypto-1.3.0/requires/coverage000066400000000000000000000002701421476274700167670ustar00rootroot00000000000000coverage == 4.4.1 ; python_version == '2.6' coverage == 4.5.4 ; python_version == '3.3' or python_version == '3.4' coverage == 5.5 ; python_version == '2.7' or python_version >= '3.5' oscrypto-1.3.0/requires/lint000066400000000000000000000015111421476274700161410ustar00rootroot00000000000000setuptools >= 39.0.1 ; python_version == '2.7' or python_version >= '3.3' enum34 == 1.1.6 ; python_version == '2.7' or python_version == '3.3' configparser == 3.5.0 ; python_version == '2.7' mccabe == 0.6.1 ; python_version == '3.3' pycodestyle == 2.3.1 ; python_version == '3.3' pyflakes == 1.6.0 ; python_version == '3.3' flake8 == 3.5.0 ; python_version == '3.3' mccabe == 0.6.1 ; python_version == '2.7' or python_version >= '3.4' pycodestyle == 2.5.0 ; python_version == '2.7' or python_version >= '3.4' pyflakes == 2.1.1 ; python_version == '2.7' or python_version >= '3.4' functools32 == 3.2.3-2 ; python_version == '2.7' typing == 3.7.4.1 ; python_version == '2.7' or python_version == '3.4' entrypoints == 0.3 ; python_version == '2.7' or python_version >= '3.4' flake8 == 3.7.9 ; python_version == '2.7' or python_version >= '3.4' oscrypto-1.3.0/requires/release000066400000000000000000000000651421476274700166160ustar00rootroot00000000000000wheel >= 0.31.0 twine >= 1.11.0 setuptools >= 38.6.0 oscrypto-1.3.0/run.py000077500000000000000000000002461421476274700145760ustar00rootroot00000000000000#!/usr/bin/env python # coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function from dev._task import run_task run_task() oscrypto-1.3.0/setup.py000066400000000000000000000115411421476274700151270ustar00rootroot00000000000000import codecs import os import shutil import sys import warnings import setuptools from setuptools import find_packages, setup, Command from setuptools.command.egg_info import egg_info PACKAGE_NAME = 'oscrypto' PACKAGE_VERSION = '1.3.0' PACKAGE_ROOT = os.path.dirname(os.path.abspath(__file__)) # setuptools 38.6.0 and newer know about long_description_content_type, but # distutils still complains about it, so silence the warning sv = setuptools.__version__ svi = tuple(int(o) if o.isdigit() else o for o in sv.split('.')) if svi >= (38, 6): warnings.filterwarnings( 'ignore', "Unknown distribution option: 'long_description_content_type'", module='distutils.dist' ) # Try to load the tests first from the source repository layout. If that # doesn't work, we assume this file is in the release package, and the tests # are part of the package {PACKAGE_NAME}_tests. if os.path.exists(os.path.join(PACKAGE_ROOT, 'tests')): tests_require = [] test_suite = 'tests.make_suite' else: tests_require = ['%s_tests' % PACKAGE_NAME] test_suite = '%s_tests.make_suite' % PACKAGE_NAME # This allows us to send the LICENSE and docs when creating a sdist. Wheels # automatically include the LICENSE, and don't need the docs. For these # to be included, the command must be "python setup.py sdist". package_data = {} if sys.argv[1:] == ['sdist'] or sorted(sys.argv[1:]) == ['-q', 'sdist']: package_data[PACKAGE_NAME] = [ '../LICENSE', '../*.md', '../docs/*.md', ] # Ensures a copy of the LICENSE is included with the egg-info for # install and bdist_egg commands class EggInfoCommand(egg_info): def run(self): egg_info_path = os.path.join( PACKAGE_ROOT, '%s.egg-info' % PACKAGE_NAME ) if not os.path.exists(egg_info_path): os.mkdir(egg_info_path) shutil.copy2( os.path.join(PACKAGE_ROOT, 'LICENSE'), os.path.join(egg_info_path, 'LICENSE') ) egg_info.run(self) class CleanCommand(Command): user_options = [ ('all', 'a', '(Compatibility with original clean command)'), ] def initialize_options(self): self.all = False def finalize_options(self): pass def run(self): sub_folders = ['build', 'temp', '%s.egg-info' % PACKAGE_NAME] if self.all: sub_folders.append('dist') for sub_folder in sub_folders: full_path = os.path.join(PACKAGE_ROOT, sub_folder) if os.path.exists(full_path): shutil.rmtree(full_path) for root, dirs, files in os.walk(os.path.join(PACKAGE_ROOT, PACKAGE_NAME)): for filename in files: if filename[-4:] == '.pyc': os.unlink(os.path.join(root, filename)) for dirname in list(dirs): if dirname == '__pycache__': shutil.rmtree(os.path.join(root, dirname)) readme = '' with codecs.open(os.path.join(PACKAGE_ROOT, 'readme.md'), 'r', 'utf-8') as f: readme = f.read() setup( name=PACKAGE_NAME, version=PACKAGE_VERSION, description=( 'TLS (SSL) sockets, key generation, encryption, decryption, signing, ' 'verification and KDFs using the OS crypto libraries. Does not require ' 'a compiler, and relies on the OS for patching. Works on Windows, OS X ' 'and Linux/BSD.' ), long_description=readme, long_description_content_type='text/markdown', url='https://github.com/wbond/oscrypto', author='wbond', author_email='will@wbond.net', license='MIT', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Security :: Cryptography', ], keywords='crypto pki tls ssl x509 certificate encrypt decrypt sign verify rsa dsa ec dh', install_requires=['asn1crypto>=1.5.1'], packages=find_packages(exclude=['tests*', 'dev*']), package_data=package_data, tests_require=tests_require, test_suite=test_suite, cmdclass={ 'clean': CleanCommand, 'egg_info': EggInfoCommand, } ) oscrypto-1.3.0/tests/000077500000000000000000000000001421476274700145555ustar00rootroot00000000000000oscrypto-1.3.0/tests/LICENSE000066400000000000000000000020631421476274700155630ustar00rootroot00000000000000Copyright (c) 2015-2019 Will Bond Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. oscrypto-1.3.0/tests/__init__.py000066400000000000000000000101331421476274700166640ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import imp import os import unittest __version__ = '1.3.0' __version_info__ = (1, 3, 0) _asn1crypto_module = None _oscrypto_module = None def local_oscrypto(): """ Make sure oscrypto is initialized and the backend is selected via env vars :return: A 2-element tuple with the (asn1crypto, oscrypto) modules """ global _asn1crypto_module global _oscrypto_module if _oscrypto_module: return (_asn1crypto_module, _oscrypto_module) tests_dir = os.path.dirname(os.path.abspath(__file__)) # If we are in a source checkout, load the local oscrypto module, and # local asn1crypto module if possible. Otherwise do a normal import. in_source_checkout = os.path.basename(tests_dir) == 'tests' if in_source_checkout: _asn1crypto_module = _import_from( 'asn1crypto', os.path.abspath(os.path.join(tests_dir, '..', '..', 'asn1crypto')) ) if _asn1crypto_module is None: import asn1crypto as _asn1crypto_module if in_source_checkout: _oscrypto_module = _import_from( 'oscrypto', os.path.abspath(os.path.join(tests_dir, '..')) ) if _oscrypto_module is None: import oscrypto as _oscrypto_module if os.environ.get('OSCRYPTO_USE_CTYPES'): _oscrypto_module.use_ctypes() # Configuring via env vars so CI for other packages doesn't need to do # anything complicated to get the alternate backends if os.environ.get('OSCRYPTO_USE_OPENSSL'): paths = os.environ.get('OSCRYPTO_USE_OPENSSL').split(',') if len(paths) != 2: raise ValueError('Value for OSCRYPTO_USE_OPENSSL env var must be two paths separated by a comma') _oscrypto_module.use_openssl(*paths) elif os.environ.get('OSCRYPTO_USE_WINLEGACY'): _oscrypto_module.use_winlegacy() return (_asn1crypto_module, _oscrypto_module) def _import_from(mod, path, mod_dir=None): """ Imports a module from a specific path :param mod: A unicode string of the module name :param path: A unicode string to the directory containing the module :param mod_dir: If the sub directory of "path" is different than the "mod" name, pass the sub directory as a unicode string :return: None if not loaded, otherwise the module """ if mod_dir is None: mod_dir = mod if not os.path.exists(path): return None if not os.path.exists(os.path.join(path, mod_dir)): return None try: mod_info = imp.find_module(mod_dir, [path]) return imp.load_module(mod, *mod_info) except ImportError: return None def make_suite(): """ Constructs a unittest.TestSuite() of all tests for the package. For use with setuptools. :return: A unittest.TestSuite() object """ loader = unittest.TestLoader() suite = unittest.TestSuite() for test_class in test_classes(): tests = loader.loadTestsFromTestCase(test_class) suite.addTests(tests) return suite def test_classes(): """ Returns a list of unittest.TestCase classes for the package :return: A list of unittest.TestCase classes """ _, oscrypto = local_oscrypto() if oscrypto.__version__ != __version__: raise AssertionError( ('oscrypto_tests version %s can not be run with ' % __version__) + ('oscrypto version %s' % oscrypto.__version__) ) from .test_kdf import KDFTests from .test_keys import KeyTests from .test_asymmetric import AsymmetricTests from .test_symmetric import SymmetricTests from .test_trust_list import TrustListTests from .test_init import InitTests test_classes = [ KDFTests, KeyTests, AsymmetricTests, SymmetricTests, TrustListTests, InitTests, ] if not os.environ.get('OSCRYPTO_SKIP_INTERNET_TESTS'): from .test_tls import TLSTests test_classes.append(TLSTests) return test_classes oscrypto-1.3.0/tests/__main__.py000066400000000000000000000005721421476274700166530ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys import unittest from . import test_classes suite = unittest.TestSuite() loader = unittest.TestLoader() for test_class in test_classes(): suite.addTest(loader.loadTestsFromTestCase(test_class)) unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(suite) oscrypto-1.3.0/tests/_https_client.py000066400000000000000000000212341421476274700177700ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys import re import os from oscrypto import tls, errors as oscrypto_errors, version from asn1crypto.util import OrderedDict if sys.version_info < (3,): from urlparse import urlparse str_cls = unicode # noqa else: from urllib.parse import urlparse str_cls = str class HttpsClientException(Exception): pass class HttpsClientError(HttpsClientException): pass class HttpsClient(): def __init__(self, keep_alive=True, ignore_close=False): self.socket = None self.timeout = None self.url_info = None self.keep_alive = keep_alive self.ignore_close = ignore_close def close(self): """ Closes any open connection """ if not self.socket: return self.socket.close() self.socket = None def download(self, url, timeout): """ Downloads a URL and returns the contents :param url: The URL to download :param timeout: The int number of seconds to set the timeout to :return: The string contents of the URL """ self.setup_connection(url, timeout) tries = 0 while tries < 2: tries += 1 try: self.ensure_connected() req_headers = OrderedDict() req_headers['Host'] = self.url_info[0] if self.url_info[1] != 443: req_headers['Host'] += ':%d' % self.url_info[1] req_headers['Connection'] = 'Keep-Alive' if self.keep_alive else 'Close' req_headers["User-Agent"] = 'oscrypto %s TLS HTTP Client' % version.__version__ request = 'GET ' url_info = urlparse(url) path = '/' if not url_info.path else url_info.path if url_info.query: path += '?' + url_info.query request += path + ' HTTP/1.1' self.write_request(request, req_headers) response = self.read_headers() if not response: self.close() continue v, code, message, resp_headers = response data = self.read_body(code, resp_headers, timeout) if code == 301: location = resp_headers.get('location') if not isinstance(location, str_cls): raise HttpsClientError('Missing or duplicate Location HTTP header') if not re.match(r'https?://', location): if not location.startswith('/'): location = os.path.dirname(url_info.path) + location location = url_info.scheme + '://' + url_info.netloc + location return self.download(location, timeout) if code != 200: raise HttpsClientError('HTTP error %s downloading %s.' % (code, url)) else: return data except (oscrypto_errors.TLSGracefulDisconnectError): self.close() continue def setup_connection(self, url, timeout): """ :param url: The URL to download :param timeout: The int number of seconds to set the timeout to :return: A boolean indicating if the connection was reused """ url_info = urlparse(url) if url_info.scheme == 'http': raise HttpsClientException('Can not connect to a non-TLS server') hostname = url_info.hostname port = url_info.port if not port: port = 443 if self.socket and self.url_info != (hostname, port): self.close() self.timeout = timeout self.url_info = (hostname, port) return self.ensure_connected() def ensure_connected(self): """ Make sure a valid tls.TLSSocket() is open to the server :return: A boolean indicating if the connection was reused """ if self.socket: return True host, port = self.url_info session = tls.TLSSession() self.socket = tls.TLSSocket(host, port, timeout=self.timeout, session=session) return False def write_request(self, request, headers): """ :param request: A unicode string of the first line of the HTTP request :param headers: An OrderedDict of the request headers """ lines = [request] for header, value in headers.items(): lines.append('%s: %s' % (header, value)) lines.extend(['', '']) request = '\r\n'.join(lines).encode('iso-8859-1') self.socket.write(request) def read_headers(self): """ Reads the HTTP response headers from the socket :return: On error, None, otherwise a 4-element tuple: 0: A 2-element tuple of integers representing the HTTP version 1: An integer representing the HTTP response code 2: A unicode string of the HTTP response code name 3: An OrderedDict of HTTP headers with lowercase unicode key and unicode values """ version = None code = None text = None headers = OrderedDict() data = self.socket.read_until(b'\r\n\r\n') string = data.decode('iso-8859-1') first = False for line in string.split('\r\n'): line = line.strip() if first is False: if line == '': continue match = re.match(r'^HTTP/(1\.[01]) +(\d+) +(.*)$', line) if not match: return None version = tuple(map(int, match.group(1).split('.'))) code = int(match.group(2)) text = match.group(3) first = True else: if not len(line): continue parts = line.split(':', 1) if len(parts) == 2: name = parts[0].strip().lower() value = parts[1].strip() if name in headers: if isinstance(headers[name], tuple): headers[name] = headers[name] + (value,) else: headers[name] = (headers[name], value) else: headers[name] = value return (version, code, text, headers) def parse_content_length(self, headers): """ Returns the content-length from a dict of headers :return: An integer of the content length """ content_length = headers.get('content-length') if isinstance(content_length, str_cls) and len(content_length) > 0: content_length = int(content_length) return content_length def read_body(self, code, resp_headers, timeout): """ """ data = b'' transfer_encoding = resp_headers.get('transfer-encoding') if transfer_encoding and transfer_encoding.lower() == 'chunked': while True: line = self.socket.read_until(b'\r\n').decode('iso-8859-1').rstrip() if re.match(r'^[a-fA-F0-9]+$', line): chunk_length = int(line, 16) if chunk_length == 0: break data += self.socket.read_exactly(chunk_length) if self.socket.read_exactly(2) != b'\r\n': raise HttpsClientException('Unable to parse chunk newline') else: self.close() raise HttpsClientException('Unable to parse chunk length') else: content_length = self.parse_content_length(resp_headers) if content_length is not None: if content_length > 0: data = self.socket.read_exactly(content_length) elif code == 304 or code == 204 or (code >= 100 and code < 200): # Per https://tools.ietf.org/html/rfc7230#section-3.3.3 these have no body pass else: # This should only happen if the server is going to close the connection while self.socket.select_read(timeout=timeout): data += self.socket.read(8192) self.close() if not self.ignore_close and resp_headers.get('connection', '').lower() == 'close': self.close() return data oscrypto-1.3.0/tests/_socket_proxy.py000066400000000000000000000036651421476274700200310ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import socket import select import threading _sockets = {} _socket_lock = threading.Lock() def proxy(src, dst, callback=None): timeout = 10 try: read_ready, _, _ = select.select([src], [], [], timeout) while len(read_ready): if callback: callback(src, dst) else: dst.send(src.recv(8192)) read_ready, _, _ = select.select([src], [], [], timeout) except (socket.error, select.error, OSError, ValueError): pass try: src.shutdown(socket.SHUT_RDWR) except (socket.error, OSError, ValueError): pass src.close() try: dst.shutdown(socket.SHUT_RDWR) except (socket.error, OSError, ValueError): pass dst.close() def listen(server, ip, port, send_callback, recv_callback): lsock, laddr = server.accept() rsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) rsock.connect((ip, port)) with _socket_lock: _sockets[threading.current_thread().ident] = { 'lsock': lsock, 'rsock': rsock } t1 = threading.Thread(target=proxy, args=(rsock, lsock, recv_callback)) t2 = threading.Thread(target=proxy, args=(lsock, rsock, send_callback)) t1.start() t2.start() def make_socket_proxy(ip, port, send_callback=None, recv_callback=None): server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('', 8080)) server.listen(1) t = threading.Thread( target=listen, args=(server, ip, port, send_callback, recv_callback) ) t.start() sock = socket.create_connection(('localhost', 8080)) sock.settimeout(1) t.join() with _socket_lock: data = _sockets[t.ident] return (sock, data['lsock'], data['rsock'], server) oscrypto-1.3.0/tests/_socket_server.py000066400000000000000000000020521421476274700201430ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import socket import select import threading def listen(server, on_read): sock, addr = server.accept() timeout = 10 try: read_ready, _, _ = select.select([sock], [], [], timeout) while len(read_ready): data = sock.recv(8192) if on_read(sock, data): read_ready, _, _ = select.select([sock], [], [], 0) else: read_ready = [] except (socket.error, select.error, OSError, ValueError): pass try: sock.shutdown(socket.SHUT_RDWR) except (socket.error, OSError, ValueError): pass sock.close() def make_socket_server(port, on_read=None): server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('', port)) server.listen(1) t = threading.Thread( target=listen, args=(server, on_read) ) t.start() return server oscrypto-1.3.0/tests/_unittest_compat.py000066400000000000000000000112441421476274700205120ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import sys import unittest import re if sys.version_info < (3,): str_cls = unicode # noqa else: str_cls = str _non_local = {'patched': False} def patch(): if sys.version_info >= (3, 0): return if _non_local['patched']: return if sys.version_info < (2, 7): unittest.TestCase.assertIsInstance = _assert_is_instance unittest.TestCase.assertRegex = _assert_regex unittest.TestCase.assertRaises = _assert_raises unittest.TestCase.assertRaisesRegex = _assert_raises_regex unittest.TestCase.assertGreaterEqual = _assert_greater_equal unittest.TestCase.assertLess = _assert_less unittest.TestCase.assertLessEqual = _assert_less_equal unittest.TestCase.assertIn = _assert_in unittest.TestCase.assertNotIn = _assert_not_in else: unittest.TestCase.assertRegex = unittest.TestCase.assertRegexpMatches unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp _non_local['patched'] = True def _safe_repr(obj): try: return repr(obj) except Exception: return object.__repr__(obj) def _format_message(msg, standard_msg): return msg or standard_msg def _assert_greater_equal(self, a, b, msg=None): if not a >= b: standard_msg = '%s not greater than or equal to %s' % (_safe_repr(a), _safe_repr(b)) self.fail(_format_message(msg, standard_msg)) def _assert_less(self, a, b, msg=None): if not a < b: standard_msg = '%s not less than %s' % (_safe_repr(a), _safe_repr(b)) self.fail(_format_message(msg, standard_msg)) def _assert_less_equal(self, a, b, msg=None): if not a <= b: standard_msg = '%s not less than or equal to %s' % (_safe_repr(a), _safe_repr(b)) self.fail(_format_message(msg, standard_msg)) def _assert_is_instance(self, obj, cls, msg=None): if not isinstance(obj, cls): if not msg: msg = '%s is not an instance of %r' % (obj, cls) self.fail(msg) def _assert_in(self, member, container, msg=None): if member not in container: standard_msg = '%s not found in %s' % (_safe_repr(member), _safe_repr(container)) self.fail(_format_message(msg, standard_msg)) def _assert_not_in(self, member, container, msg=None): if member in container: standard_msg = '%s found in %s' % (_safe_repr(member), _safe_repr(container)) self.fail(_format_message(msg, standard_msg)) def _assert_regex(self, text, expected_regexp, msg=None): """Fail the test unless the text matches the regular expression.""" if isinstance(expected_regexp, str_cls): expected_regexp = re.compile(expected_regexp) if not expected_regexp.search(text): msg = msg or "Regexp didn't match" msg = '%s: %r not found in %r' % (msg, expected_regexp.pattern, text) self.fail(msg) def _assert_raises(self, excClass, callableObj=None, *args, **kwargs): # noqa context = _AssertRaisesContext(excClass, self) if callableObj is None: return context with context: callableObj(*args, **kwargs) def _assert_raises_regex(self, expected_exception, expected_regexp, callable_obj=None, *args, **kwargs): if expected_regexp is not None: expected_regexp = re.compile(expected_regexp) context = _AssertRaisesContext(expected_exception, self, expected_regexp) if callable_obj is None: return context with context: callable_obj(*args, **kwargs) class _AssertRaisesContext(object): def __init__(self, expected, test_case, expected_regexp=None): self.expected = expected self.failureException = test_case.failureException self.expected_regexp = expected_regexp def __enter__(self): return self def __exit__(self, exc_type, exc_value, tb): if exc_type is None: try: exc_name = self.expected.__name__ except AttributeError: exc_name = str(self.expected) raise self.failureException( "{0} not raised".format(exc_name)) if not issubclass(exc_type, self.expected): # let unexpected exceptions pass through return False self.exception = exc_value # store for later retrieval if self.expected_regexp is None: return True expected_regexp = self.expected_regexp if not expected_regexp.search(str(exc_value)): raise self.failureException( '"%s" does not match "%s"' % (expected_regexp.pattern, str(exc_value)) ) return True oscrypto-1.3.0/tests/exception_context.py000066400000000000000000000015611421476274700206740ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import re import sys from contextlib import contextmanager if sys.version_info < (3,): str_cls = unicode # noqa else: str_cls = str @contextmanager def assert_exception(test_case, expected_class, expected_msg): """ Look for a specific exception type and message, allowing the exception to be raised if it doesn't match """ expected_re = re.compile(expected_msg) try: yield except Exception as e: should_raise = True if isinstance(e, expected_class): test_case.assertIsInstance(e, expected_class) msg = str_cls(e) if expected_re.search(msg): test_case.assertRegex(msg, expected_re) should_raise = False if should_raise: raise oscrypto-1.3.0/tests/fixtures/000077500000000000000000000000001421476274700164265ustar00rootroot00000000000000oscrypto-1.3.0/tests/fixtures/DSAParametersInheritedCACert.crt000066400000000000000000000010421421476274700244460ustar00rootroot000000000000000‚0‚Ý 0 *†HÎ80?1 0 UUS10U Test Certificates 201110 UDSA CA0 100101083000Z 301231083000Z0T1 0 UUS10U Test Certificates 20111$0"UDSA Parameters Inherited CA0’0 *†HÎ8„€t/OuÐW#o-fcÑB ËCŸNâõ:ÒØÇêÝó²ša@ ²zQófù6#pTèéfz”¥±ô1V]¿ŠG‘`ªùE\Kg³öm¹ÍdÝ ˆÇÄKѸ¾2 Ÿ¹J„3ë¶BKK2í)=ø~¢÷Ø¢VSq*S{£|0z0U#0€ÆŒtè{ ÈYÇ}<[TY`% ±0UeŸp:Œ­öCÈçUŽèKÛ‡â0Uÿ0U 00  `†He00Uÿ0ÿ0 *†HÎ800-Gð˜É»©‚Å+z_É僷Ù“á ¾­%ˆpßÃYO"œêÔoscrypto-1.3.0/tests/fixtures/badtls.io_ca.crt000066400000000000000000000024261421476274700214660ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDlTCCAn+gAwIBAgIIVvpPzLyqk+0wCwYJKoZIhvcNAQELMGoxaDAJBgNVBAYT AlVTMBQGA1UECAwNTWFzc2FjaHVzZXR0czAOBgNVBAcMB05ld2J1cnkwFgYDVQQK DA9CYWQgVExTIExpbWl0ZWQwHQYDVQQDDBZCYWQgVExTIExpbWl0ZWQgUlNBIENB MB4XDTE2MDEwMTAwMDAwMFoXDTI2MDEwMTAwMDAwMFowajFoMAkGA1UEBhMCVVMw FAYDVQQIDA1NYXNzYWNodXNldHRzMA4GA1UEBwwHTmV3YnVyeTAWBgNVBAoMD0Jh ZCBUTFMgTGltaXRlZDAdBgNVBAMMFkJhZCBUTFMgTGltaXRlZCBSU0EgQ0EwggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSHu3OR1RS0D2xLKGK2Ts5eLoO /P+IXst5WPdaD9UwGI8edfAy3U8wcMFDoXNhBQM+ZW69Z5uOZVxs704+j5cgCEAT LbtyIrF2X8BixXFzrJFd+kpojURheyxML20GbZsznJgKzYvGqFqWa/1lYwy/v0SP RNGPEkjFXb/tItDwrDxcuDzY6zjNlW5MwqvS11P1H8eg0idUrANY2MzT8+oyH3Sn JLCsmulnmj1b6IZZDN4i8rKXEbH14jIsANHIgTqvS+kJf3Z1PqHAOUqVGlO3SDZd KIqZ8olS6ty9/pco6cxvX2Te9m1z5f1fSrdxAtx7lHM3pdvs9DhML+8FAewDAgMB AAGjQzBBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFGEbxkZbhgwiZRAMx7Vs VCRXl/tkMA8GA1UdDwEB/wQFAwMHBgAwCwYJKoZIhvcNAQELA4IBAQBKv0TJoRhd wg7dPOFDVuKaLtuVzXEeUWfsA86iW4wjXFO/npI+1exSBX92MhsWk5Gjn9dO/Hq4 EZ1pMJ8hFdrOXoEHlvhnZSavtoy25ZvEoxJ9XWYPqWCmwdfB3xhT4hoEaIlu5Azf Fw/QV5oFV8SYgwClQ+fTStxdW7CBKEX55KPUn4FOOXV5TfbLOJj3w/1V2pBTKn2f 2safgWyIpNw7OyvYVICdW5/NvD+VTBp+4PfWkTfRD5LEAxqvaGXupBaI2qGYVibJ WQ77yy6bOvcJh4heqtIJuYg5F3vhvSGo4i5Bkx+daRKFzFwsoiexgRNTdlPCEGsQ 15WBlk3X/9bt -----END CERTIFICATE----- oscrypto-1.3.0/tests/fixtures/digicert_ca.crt000066400000000000000000000043231421476274700213770ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5 BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf 1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d 32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E= -----END CERTIFICATE-----oscrypto-1.3.0/tests/fixtures/dsa_signature000066400000000000000000000000561421476274700212020ustar00rootroot000000000000000,Ml§ÏDÌ1„j¸£ÆÄÔ¤‹ÑDs烖rXè•Ë ±[V÷¸oscrypto-1.3.0/tests/fixtures/ecdsa_signature000066400000000000000000000001071421476274700215070ustar00rootroot000000000000000E b²«h,¸J¤ù‘€yG¯)œ}|[H~ãÁŽô*|!ùyòœª|Ú™Ó~JÊZ¬ˆ6''#ŠON½Ãݰ}oscrypto-1.3.0/tests/fixtures/keys/000077500000000000000000000000001421476274700174015ustar00rootroot00000000000000oscrypto-1.3.0/tests/fixtures/keys/test-4096.crt000066400000000000000000000037711421476274700215020ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFtzCCA5+gAwIBAgIIWjK8l6e+CwEwDQYJKoZIhvcNAQELBQAwbTELMAkGA1UE BhMCVVMxFjAUBgNVBAgMDU5ldyBIYW1wc2hpcmUxETAPBgNVBAcMCFBseW1vdXRo MR8wHQYDVQQKDBZDb2RleCBOb24gU3VmZmljaXQgTExDMRIwEAYDVQQDDAlXaWxs IEJvbmQwHhcNMTcxMjE0MTgwMTU5WhcNMTgxMjE0MTgwMTU5WjBtMQswCQYDVQQG EwJVUzEWMBQGA1UECAwNTmV3IEhhbXBzaGlyZTERMA8GA1UEBwwIUGx5bW91dGgx HzAdBgNVBAoMFkNvZGV4IE5vbiBTdWZmaWNpdCBMTEMxEjAQBgNVBAMMCVdpbGwg Qm9uZDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALiicomVU1jSfHdh TmNQUUczukg5Gz2Lo/CXZf5iQZ58JQITY/PFwPu6F3PYJD7vuARmwBF5JFA4qZ7B K7rRNWLl46f91YaSxDXyTWnbTbyQSbWZwn15os/tmHHf391RoRgcX0WFDeIoy/FH CI2HE64YTvmjDu8KNVWLWDvFTQuu48SmJwk394JkmTk3JLP5bEwQOXPrvPYLrfKT 42pyyzjZpO4xRHGownLxQSus0LGWrV3JjsHa/Plw3qUnOA9FOaC7qpzHbUZo9dXC 3e9rl9Vvr+r7KEJx7+M+uDQyS6NVYXbwtNtPqvcaWoAm+dmAOgZw2ysKw05TB4ou T22bp7VQZn6cZYRQadMNQ+NfcC3DA5u4bIhJCx6eRs8pDmMGZMLVgYug8Y8hAwxs mOmLtBZRlUurWfKQp4D31Grn932OGMfwycLndimgA2oQjMkbAQwDmB/tu6gOr1vO zlf3epwASTLX5rVDnPLkx2yt4+P0ur5C8KsEQtypRs08GcseALSNbAx0I0cjRS8a 8/pJJ9uxB6pf7/+sfpH/UBhUrY4MmHRumUnwPSsDSrv3vKQs7DuEf5WCFYhZpOh9 U14SuRXqgmB8351Z3W1nuSOg6PorqJQWZRUvxyg8hlSCAJ5rE56chm5pB7TLIqrH zTMAc0+GvhsupqZ2n2UVh6iRJIzTAgMBAAGjWzBZMAkGA1UdEwQCMAAwHQYDVR0l BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRWzMAIJOQMOWCaUJ0R gJFBGmAltjAOBgNVHQ8BAf8EBAMCBaAwDQYJKoZIhvcNAQELBQADggIBAFq3VWfT JQIoJ8bTiXW75+kvmyTg64OoF+QP6W+eik1hC8068WPKRmwG/3B8TRmeBCihkIPg OzSGt+2ClzwlNAfbkgTCoFWnkie2bTS5WbCXzUVde/QoKi1tPUfY5D2Ral5huzkh +hOW+GzsPnrlOMv7llesntFPxL/e48+U7ErOvPCB39Px+gz5vQ7XNSa+3wFX678c FCl+hP35aDOqRXWNIarsrQ5chfRNZwtXjRxdFYj0Fgip2vc0aB4uqU6roBXRzgwr uzxJnZ8S/lZE6pAvzkqA0EowXKbX+8oYm1St0rpeMS4cZq1St8F5s0YjxoYYopAL 0xk+kBTvPdeUg101edab0YyS1JUSYEkXl6R+KY1xb2oI5V+5MdqdJmunYw4KmtBy GvZuigdei+toh/uQy2GATipEHXJbVcluRDlPHG0xFgRBHhOaDwIA188OuoJvL2j6 6FsHdsN8NzqqkxD7sWWY0t08E2PiEdRt6WlM44yjcvOUVM6hcCK93BSSKbVvr4y6 ITrfg6pheZtALX61QT2cMZJ+jN4Y2WHuohWhz3SsCyzOiKBR9bU2EuvtcxGUlEsE cgcybPWtayTtHi/NZNz/SSQ6nzZLmDG6MVjwkLodC2mp5zacFxBTwHuJRHMuaN1H cvBhUyF0F7JZSdJfolKShNKTj+pkECUDUWyS -----END CERTIFICATE----- oscrypto-1.3.0/tests/fixtures/keys/test-4096.key000066400000000000000000000063101421476274700214720ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQC4onKJlVNY0nx3 YU5jUFFHM7pIORs9i6Pwl2X+YkGefCUCE2PzxcD7uhdz2CQ+77gEZsAReSRQOKme wSu60TVi5eOn/dWGksQ18k1p2028kEm1mcJ9eaLP7Zhx39/dUaEYHF9FhQ3iKMvx RwiNhxOuGE75ow7vCjVVi1g7xU0LruPEpicJN/eCZJk5NySz+WxMEDlz67z2C63y k+Nqcss42aTuMURxqMJy8UErrNCxlq1dyY7B2vz5cN6lJzgPRTmgu6qcx21GaPXV wt3va5fVb6/q+yhCce/jPrg0MkujVWF28LTbT6r3GlqAJvnZgDoGcNsrCsNOUweK Lk9tm6e1UGZ+nGWEUGnTDUPjX3AtwwObuGyISQsenkbPKQ5jBmTC1YGLoPGPIQMM bJjpi7QWUZVLq1nykKeA99Rq5/d9jhjH8MnC53YpoANqEIzJGwEMA5gf7buoDq9b zs5X93qcAEky1+a1Q5zy5MdsrePj9Lq+QvCrBELcqUbNPBnLHgC0jWwMdCNHI0Uv GvP6SSfbsQeqX+//rH6R/1AYVK2ODJh0bplJ8D0rA0q797ykLOw7hH+VghWIWaTo fVNeErkV6oJgfN+dWd1tZ7kjoOj6K6iUFmUVL8coPIZUggCeaxOenIZuaQe0yyKq x80zAHNPhr4bLqamdp9lFYeokSSM0wIDAQABAoICAGxWbazLYR+SSpRmFW/y9e44 rQqp75D6ehS3UZKlpulO4QxJwnGMzQMoG5DO992yHaD/hZ37HlKwHN9Za8Zv8cp5 KE8vg3Y2cklRRxlhJEkLxSR494VWtTEaskPHYH1l44fZRhHt08qrs5d83cjlxKd3 /gc/JYkfzVHHtNtFl0LOuTlvYSJoo6PwUXQgJjowldo//uZFIhihm8zl+CBDAXn3 Q268m/OxWX/Gjj4JZl9+EXYhydF4JM5c61evHpbfQMROufrKjhUti2qFsGJHGM2V fnz8KJBseJ039TAzfux1Mf5vmT/CjIf3/7iIpjx4oBOdbwzfgOBZmef76oFfP1UG lHn5d4TH/ZRWBvyEMBaVy5WfSrVVyWZqafpSuzNnadzvLtQeM85GnA3kE8qcWUo0 REh9NdZd+hIp+03r7/teUkr8GN9hATOjf+811HqWGS6XiMXfATcwJhodtXW9Ox8S sHjrYanA06rwOPdbFPPz1Q2vhKQBAfa3vwrstgp6pF30ogtv0G2MNvjiy5CzgSUW ZAvJLO0gIEbtHV7gE3F2oVqZqfWPQ6jmbhnjL+490srUBEeKkJIPyJW69ZGIqO02 gncEDx+2NXGWJlNfyEOrz5CwRK4sLky/84T6Rsbe/yBbY/lu5ogvzkuegKnDbdqK 7zHqs0RyIL2knOi+3YARAoIBAQDbdE3cKJQK04SsgnV9cXHJXSAAK/a0QIvE6Kmj nzGaqHyta3/I5Jj/V8Kq4T++u4mzdK7v1j9rWAnO10pSzy6+7PfCxIfTcCpttSCS I3QgC/HJ8lNpGONoQ4l1xYhfTaJC7A0kOQP075ai4ZHsps2RXElbcyXzgXmpGDGb POOalhDkO01tgY/ebyMQBU59aiVXLYUhkw+xFNDHWw7IS2PtJukPrXgl+h6zPmBg 7yDZOGQA6VLXoHcHWoxzevdRX1dHYpLPVTUKdaEkvv7U/w43wN5OsaRytfXF13YE Klc+7c96snXthehXk9xzcpYJG6yi2dhLv0zTjeywHQSASGWLAoIBAQDXYbhnV5J0 xT5RIQ/c6xV83i/4FB8aFQhE0WIdyuoDKddqNoY/mJ5Elxf8s6T631rinNsc+QIG /2EVr8dZTcdf4tlYExFaJVIvsUEB7Nc/94BGNQ0Pe6f+0AlPcb6KeeqpnXwBgoHD 92VaDqb3/B0WL6dQnhioCfikQxFGXAOs1OPJ/yuJwGBQ2UnUP2RkGOJq6dk2BgiB sQF1EVLSCO93Yq1xHjgaIEUoR0+rv1Ev5MEN2thaPPUKItv91yxWYXYNNTB1pzHF 0kzQRMeuuYIM9E8HQsJDQHhJ4PidkQlE8FmY142oAptdudQEG2UvelH0Rs7LFwVZ MVwHG6Nohq7ZAoIBAEKITzapAsEJgZa/cplWdHXlCteifIgHbZiEJTIbCHHoFOyK w9IHJC99zuH5qlbMCqJihMrnvJBxv397G+hhMb3mn/LnnHpI7kQWfspvfzxcArEE C6+4QPGSUDFpcr/aDJCXeiB6PK3cmWHGW3RQ2c/xF7hm/6GEpnrSf++dlS9/wZ1A MWg6uIBqV0n7IeG59ZopjcwxBrfcJLPb8m5dFO2IN25uXBe/beFPzu50cRQKGZ+B HYGL/6o2FJDKPgLokdBtnS7ImW0TXoiCc0XmEcxrZ7qZ9s5kRh/2A6ydnZfRh26e fkxFpbwvcs15du1J+5vfycilNciJpbF7CqtNx6MCggEBAKo59LnQr9MUlZUfcV8k ed4v6W/Dz3UYzGk/p7FuT5fiePvcTixzXhtN48p43VreLwHOASub7qtDKiMuw3rk i0BQlYETyPIbbyJ8kKmJ2/IVlk9m3ISxMeyl1cXxWywtAXe/HWRAFnmgzXzo+bQj kYWYcj+4RDqDbv28eCnIzarKs6JRx8JexoD/y2cyFxE5zbG8U01sZUrDcChrYa7Z GxKbbgNqY+LaQG5+NgzC79LPlHlzzu07Ilw5gu/UIZm/+8KimENUunTkKous4LlK quu8J73QWKM4kBmr8jLuj4nqAJZZPv9b2Bmypkv8rvNgVOehEw3WiwaciWSgxnz1 fbkCggEBAIQl8GNUihubkiGWuKrCVp6La5A3fJOjJEmKeay+4RFfhPKO69oEd4QO hc9fifbA8JWxcBArFvHP+AgNLVURbal7GJ1jX2ZB1K9+h7A7w+dYAEnToBRaKQKt +BB7Z9bGV1V2WQnfnta/1IrFSMuWsu9ec0lF2g7jquyfbkX7VXdJkZkOvOcuQ0bR ojXfmp2sGW5Ck9+QUIEWepABX6Tu3j5t91dRLNMUiMfNwXBZLq0LfA35PFIrRHG4 zO3k54IrF0YeabKQoHshpayldDRagoFnfWXqhl708owOVIerlV9ZKl5oBLh79Eoz 9fiCcEiyfVs35wXjsfHm9YjRWQlx6qo= -----END PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-aes128.key000066400000000000000000000033461421476274700221010ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-128-CBC,01F6EE04516C912788B11BD7377626C2 73aqVra5MCRJyYTjaqnm8d8OTBSdKvs8hTamsyIcHKe+hy8c/A5XaxQy6GZhGqze gpu1E8sRpvCHI5WpNflnzuiySamR5iC2OEoMtxHi4j9K0xw/jSdWn2H+uj4grPOo xIyMj8X9jyLHTa9N12Msvevdd6Zp6HCB6v8K9yi89Q6jl8B929sldQEftcHzLALi iI9QBs2uH47WyRY4PfZC3HHSynfZSE2Q5hm/Si3b7OGx14VZx78fPfVfBlwRGne6 7ZPQdpQm8HP1FR9XmfotZh6PjkcNEkfi6k8c3LPEcpOsRb0LdlHB3/H2+a6YAKOu ipA6HpDD3C6f8fq7n6S1pih681iTlVxaUMJkHEmTJPdKdc4tThAQ4V9rutqNIfhv y/FrmN/1klnulvwyGIbwaPatcrZyvPBbFt5hJYh55sGLzPq7pumaDSd+8jlTRr70 KC/ivwGXxFNRah76S52v/tSVuiBxCc5q5tYE2dKHdrqNZGUjE80PUGQQEunCNjY9 CFq84wDXZ4VZsPGMBQlhZmx/nyC9dQ8UGBwrLgfyFfp/W9j7o4xbAyremfgWVFvs ChYifyw5BErF0cfbfF1nGmMDH26mLcSLD18/dipzL1vUGLH5mJegPeD7KHScDUpy 4t/3wddEnbU4w9BUZhI4zqlSiAnxARO4maEFFgsuJNuwYwd7CSfIVQpYQS+2qVSZ Qp6pYzfmJFJ0CSqOtkK88clb7pKjOCBYHhfdcm84K8v0hhgDviVQ92HG9p7VmwDo gzNhUFwJ/mSkZQKz/CLy7dGoW0fD3xiseGTlsR658wcJcR7l8qkUBGF3D3hi4t83 fiTCR9j8MBhezgqO6ATSxgN3rqKQUBVphYMgNsVBlSKr2h8TwJAADo8xVGS5TRR4 OrY3TOminoG2Bn0KyavMiwIVQI/6U0Y5OrLDgl666NHVtjWMfK5NXD4pMTPRuLVF Snnsi1b0fxGMHtXLLA96iLNtyy5H91xqek1vFou2bsN9PW9TTihNMyTLO/BAi3oy kpRv99swSE6LaEBXKD1HvGJ3pyLarTRQvLE1YilBz8aHVM06Hh/MZIsQ1x6O9lZI 5mQSeFgcD/SAOrgxIE3tNnPSHHcBiRbUFxgfZQDlo4+WgYZl5WOTBuoqJKb0Zeeh ubH2I+EGW2kCQo87t4kt7fKEU+k+DfCMoLF+WoEo/s9jU/k5kuCC8iEnITUC8ew8 9ppGkcEf086WDVI4lG/Q7LNHyIZuzPNVf+ZpVDRYL8I/j6dgq5OWCv2uXFjZCE+q H664h31DV2TbaSZUsETzvVbzKmvYjzD1QayFrJIKtrgxlWxlw0HmucCm+K+Kbjk7 SeJE21KbKkxl4dxjhrtsxXp9uhL4IgebeXBV88UetkdqvYtDTRHUtyPCnf8VQjJp DelBSgLahhZuuHcXBBrk5KUKzKMZElsYkS+RdR+P5nESseFtXqJH2kjsAJKGLra5 3dDeOMPluHKQKHtwYUaO9QpWwAwJu3V2l+Tmo20JyQAZJujDfQ1WECCES+Kb8VIN khp52v9TVhFKSTSLKNFyt8ytEX4wCR2T56Vv3Gwe9/6e6lDljx3eRgmGFhw1Fa6h -----END RSA PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-aes128.p12000066400000000000000000000055361421476274700217160ustar00rootroot000000000000000‚ Z0‚  *†H†÷  ‚ ‚ 0‚ 0‚— *†H†÷  ‚ˆ0‚„0‚} *†H†÷ 0 *†H†÷  0(lÀÖ5ó€‚P!ã”BMÔì1Vw+ŽŽC´dª’%,'în,ƒƒþy½ë[xŒáVãÞ¿«m¥)*x­— p ë©qjy!÷À¢3<\=ƒÎ•1ƳkE¢1,h)½Z‘é [³DXkýU„š#æ¬QÕËñ?àÞÕ£ç]ˆ†Ûì>5k?ãåôî 7ûÙ$ ÁŠ‚Þ‹µÖ¡‰‹˜ßÈ1 tQa0"DFOßµþ‰›h≯’jþRÕr·ÇV33õã.`nƺQ§’G/~-hm3@ãÒ ª¹6u(Ùÿå²tî챂ÕÇAºœ…葤M#8³$UiØ´AÃÏ2g/¦G0 1K‚êøÿG%Ÿ€l¶¬”—fbðMõiA÷G–â˜TaQá®JøÈõ÷Õ<ö<çrqÆ.’…¾eB™ÊD?Èè)S`tè=b Ô¥Ž9!E†V`{ìù;.£l¬ Êv|ßÕèkÒþmÎÉxÑD D$Ö‹f®´Ÿ@Ñ“#ÔSt~š¦0×OŠ“ÐiUW溷,Â1§á‘

Lf¸ êâG [Áq¼n1x·ît Îqb¢˜ÖŒp×ûIñŠZšÇ©ïM–R¥l솦8½OÌ^¶ ¹îˆ•[ÞJDIGtzcÄ’‹dNÞº¦G!i&AQ)PƒJK½ºùF~ÁºÖÿ°|ÍÕû÷ò+-F_=rl¶D0 CЛ¦ØÔg†ÍÊöìß…ë§02 ¢Õí:(„&) õ%M6dBFufµmeÙVï ·…M)6.9÷oi|$ü'¢_ Ä,: ÚÒã3-´ÓKؔа@a ŠÆÂÅÈL³=m4$^„åeëΉô·tµ_C^'`cýÀדh îPð“01äIm¹TŸ.9 3×eSñÊ hfÞûe§ÿ®¶BQ•"ŽjH:à·–ºoá“ú·$4×îÎòŸd lûø-K,ãö øõZÓØÄÞ(¦2œ`B"BÁñÕ–ñ6>Íèqߺ#p0‡PÁƒøÙ¡Ø ŠR$:°"«•d쯥{H©Ü©×x45 »‚´šÉ—¶±®äªskPö=¤C†Ø¹¨ZôϽ$Åc›sBmȲÒl…tftcœH«ÁOIša PHà“ÉÂêÃk¡­…Áê/3M­àBlÔ°,sXSü)°„Ššu+PŽxM0Ÿ‚`zH`_®òÿø££Âå‹ UôN–/¦BŸ%¯296Dø°‰s“Z†ÓÊœD¡ýIϸ°Ú<¿gI@¼q±Z~?‰ß7U‹¥G:Šˆ»rºöÈ–h½0~Ãsóoi Ä%k¨< y?§Röÿ@Ep@o€%©}so¤ª­$`%sðÆG!©´æ‰w‘hÈñN~Éþ$ð»OX[É“·—tò¸®«ï0~5¹¥1èW=FS3ª=¾>µ5ü}€B¡‡¯;"º\ª´R2Cb­ÖaŠåQ ¤Ïóù&;vE%½Èók¡_-æ«BÔªÙûYó3ánjÅ«^Õ½p!ãÜɽ“®\v¦€þz Z,hlêQ†¶¢h"“Ž/6éÂ!¶L­ánw$µiT0‚j *†H†÷  ‚[‚W0‚S0‚O *†H†÷   ‚î0‚ê0 *†H†÷  0º:$[L S%‚È!¸gsó–g“W.ì§™¬V¶±s\ZÖ>m^Šöeô"—…’­gä1¦ÁÌ¡ÓïZ\ï¾DÇéùŒÐ ¬ªò}à‹Ñò¬!«ÿÏ”t­ÞW "=qrj¨%ñ K<6Òy‡’çN‡ßn/ù7]w½1¯$½ˆUçº?µ •Ñ&¬=cÞ¾Ådˆ¼Å÷×E[µÙK©‰[³< ¡#Áút|7 ýá®gï–]R›Ù ÿÇÿ²Û¢kÁœøù–ˆŠ«Ís&S bjÁÉe<#°­ †Lµau¨&&÷ãU»ÔÊŒ0™'°pN~6±ÿs\Y€Iº¢Yé–98°­èœoü½°lú:jS‚üýƒK75ÉvH–q5Áƾ༠ity»Êl+îy„Ä¿ ,±„zÍu™oM'¥æ)Úû] ´|¡r4éz Û#ÏoÙ/NˆpZªkÅ!K\§’à}š›ÞM“WB©ŒmkH2Wóƒ±Ì>'CwÏ(¤"”rÈÖ‘ô8úÅ'Îÿ£i˜à•*Ýú…0µ6è=à×kcÆ2{ÛÎ;ôœ§ë4„ÉJûÊÈpnQÙä ö×£°™{ó-rÞ…D)þÆÊD<”>Âöê=2_y¹ÛEê`»çÅ‹f•ŤéK=–[»U}fYh*X³,À“ÜÁRŒw$Ã…þ ú_Éz*Ɖ¢2Â`5"Ô óÁ2NµÜfg·ÕÇË«~¼ðÍ—ºœ4}1tò%)ÆÎ¯Æ¥æ`›×:ƒÛã2ÅàP³º(Á@šF½$¦¼|½1*M+j‡!±'3‡8Œa"ÝãåTwçâß mÓ‚hÛÃSy7E~–J¶ÉËÏψ6*ŽT‰ÍÈxz¢‡rb²Zwk8ˆ“3Ò8-ìW²É¯°£Ê>Dm·’R6Z†;ùN^{î¤[4ñªE2éeÒ.¨…Ûú{O^ÎŽèr”‡Ñ“KÒc<3ƒÄf%Èb§›Úu}Ê’xóÌUÉ“}ŠÃΆ‘†“XßDÚ"&O^—[…ËÌè‡\)~ÎèèzýîdXnøHgéVvÎzއ!À…W>ü÷ƒÓ>ä“tÈneSLÜ3Ø`hì‡Ø€ ÷ܨÚ)`¸ýl„ÉAÃ÷SÇqg–:‰`¹ƒ ÇnOöì# ©%›¨¼ëcÎNžMn÷þ¡ûø®¼¼Þn",áÇÙ“iY%”Iä (=òƒÕ­œlØw9wùZ¼_»BŸýˆÒéj«ó÷øå£YÒ‡RxÝHül—/„ye=ß,O ¼¢Ïã³aF(Òï}ûÏ~‹íÕ嬠¢Ú~ _»“¤üþß¹È/tŠ„•"ÞAH¿ÎVÂq½1nDÓ«XS,ËŸ~îõ”©±¤V…ÀØ_¿°ÝþÏñ'ø–xºäkÑ ÝpQ T~H‘2J±‹ênm”êsžgÏ;Z©{õ¶f.õGÚ²*ÜF'ÊBY±ªPãÙ<›GÝÈËv;)‡Ìþ'F1…æXÚxhøg ­[µyî¿®W¾ÂJm^ƒ}6Æìnañå£S+Æ@ +{¼éã ›M¸‚2E ?–Äów||´Ó{•]Ù­F1½x*Xž*w̩տì‡@ïå\Kæ7hÛ`÷áé„ña]2KQÜ1\åJ¯Q†WÑ ï¨û Fì{fu/P3(¹¿QúT–jrE ¥¾?(ZÒ±Ðj*I%ÄùVÃñàC:¡œ¦ÚÒPj¥ ÝFðüN^1pñߨ«kÀÙë‘ø§Xƒ”'E¼{t_‹U2sGCë–ö×ã!Óoö’6› FŸ¦A®Íܪ ÊJ÷:Ce†ªÌöaU3Z轋x#xp®t­`‰»,õÙí‡à“ÉäÝlGƒ"ycÆD(¥VCš_èX$oÍí5]Í ·í'£y ÿ'ø´W I@‡q<—Jø%‹IÂÞ‰#i@F¥mÒ·B]AÙ•¤qýeSv]ån+Æëêžf!¤t˳û°~]^â_4¶œI}wŸ4Ûß³•weJaBŸ!~dÉ«ßIZ šûfL’?_:‚ãÅ'-«×Þ%^…–|–•I ¹ØwŠº¿= LjrÏ;ëê”÷ЕÿF&ggVÃSÙJlZ¡ìÚ^q“y7I.¡ Á(˜…¶yZàØö£hŒ0 ]W[n_fôý{âï…h‡3üö€‰Kƒ$ÁMÀÝß¹pAÕPTÑÓ ”ƒÙÙˆ3e¶ ž¾Oc/p`¤¡bh¿!?ÐhHÿ]üÃE\\¨®ŒP»¬c5>.RDŒ%€ñm“ÝèkXÚÛ^¾€Ëƒ$ÁÛoJ¢ßÛ;9«…^ZÔ¨™)ÃTÑ€s-Eÿ/t$QYFJë ÷tÕ í´¢<”2¸lh ³ÙdŒßèÎÌzÎ#ëøCæ\Oaýäm±È>ŸÍ–E”φíeö§#èyŒ,^3U¤œ#޽u u MîKå0‚j *†H†÷  ‚[‚W0‚S0‚O *†H†÷   ‚î0‚ê0 *†H†÷  0«n+¯Æ‚Èsy:Ô:i•/ ¬¯*šI‚æÑ—×ȺMB¼q÷Ô©™}¨ŒˆÝIúýÒÍ•†£^x€¨°³¼=Ö /¿@‘lùÍš,ÉMÏqídZ€š+Ö1!#jÏ™ÃZeC—'ÜÏI¢1ldßc³¸ÂþØž Xöý·íŒö]±™!(”ÆkCî¯nCü C©'ªLP=Å(‹[&>P«[A2y¥}çúçXðZŒu1°ÃÝùÕÓ‚íw4oò LØZp'›»WëZ|HäoÌÄw/”Tö……9¾ò‰N*(ƒ{ÿiˆ(5“2•vÕ£ ,`/ðqv¡Ñ1XL¥ÅCyÂýUäh¹~ù|ö«‚W¬‡x©^2Ç×Êb¡ÃM÷Ûï{,/‹¿2"jýœFý¿v¾Z…h‚˜Ø$‚ôQ[Y6ôÖþNëŽ `ÉJ~vß¾V`ÑrnÕH,5 7cï”,Oó?´2NR•‚°Ý<»ÀuÅWŒèÌ«L]Ø.¥@³k믰JУÏ;Uóß4nÔOqðLŸôD>AMTvýÆÀŽP…[€€¿k»‰#ÊlK¯nËOô¿Ç‰màO}è{ ‰„Ký›OrÂt`™rpÖ¦Ïo^çDÃ8¹'Ï!è Žà+ˆb¯´"L#`zò#%eÒo°ñ U€¡@[ŽºA»é'*újJ.~qiéñâ ¯H›îGnê’ºtž·X–ÂÛ5r=Z™nŸY&² V[g1á&I¬Ã,×8­’“kxZ]˜ÖÚë¬i´3òí§ r§5®Á— fŠß€yI I…ïÏdKg¡çûÄ=ð‹½@Ö¸SÌ8òÎ+ JšÙ7Û×õ¸™Ø’Fók¤f‚¢L\åNÚêØª¾rU@bÏÐiuUärF’V÷`ñÞ^¶ g„Žu%1;:Õþj›£ëßE= È3WÕQŒ‘¦4ÑI tbU7‚D .¡Ð{ë"D{.ÞŠ£€T|%åbì0ºóC0¦ÕÓk6rQ¸(;qC±WLM‰)3딉·  -1N0# *†H†÷  1•×Ï×&€”QÂ}XîמiQÀP0' *†H†÷  1PKCS#12 Test010!0 +ÆeTNÏ¡êÏê-œ¬HÖ@f• $õY"ŸÇoscrypto-1.3.0/tests/fixtures/keys/test-der.crt000066400000000000000000000023131421476274700216410ustar00rootroot000000000000000‚Ç0‚¯  ½åÙ¤1\‚0  *†H†÷  01 0 UUS10U Massachusetts10UNewbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0 150506143716Z 250503143716Z01 0 UUS10U Massachusetts10UNewbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0‚"0  *†H†÷ ‚0‚ ‚½[(tâæJ,*½PÞxªìP6~”¹ÆEœ¨vÚ¯;Ã×ÿÄùB*ɯ}6žë#$\]Ž-ÚT4F?>Ylj<¾“kØ›K{™#ÿneFÍ:¡ûÃeCWRÝá,ˆœzîKT#ãå¿Ùú`b®v` ‘ ¶QÃÍì껩 SAÖVËéO7+§Áp½&…é# Z~QA’„÷H×~äÆìøt›€À}™ùŸšÿb›æ(@ä>F–Ö`-ò§¥á¿’P!òß/M'ïBäÞË Æž쬦(r ¾ú=û:–¸s“•Kά8‹ONÔö)Óñ5õýÁ ãzAøCkF•š*òºp˜Î¥ Ÿpâ *nòö®ãîWÕü-—„§ù»šˆä²:ƒ£åácG#‚ÅKoÿ×ú†Æ·ñMã‡Æ„MåøÈ£F5ªKÝq~ºÚpTW]™$TX kìZõeÌðœTÿì½0/òvÞ¼ñ+ÛZ©} œ’ZЛÉÇÝŠñ‚…MXöÇ“Ï jÍîBý¿$VÂ"ì*îGÝdwä½Ìvíþ Ü9Ɖ·\Í,=ço_eœ7oscrypto-1.3.0/tests/fixtures/keys/test-der.key000066400000000000000000000022501421476274700216410ustar00rootroot000000000000000‚¤‚½[(tâæJ,*½PÞxªìP6~”¹ÆEœ¨vÚ¯;Ã×ÿÄùB*ɯ}6žë#$\]Ž-ÚT4F?>Ylj<¾“kØ›K{™#ÿneFÍ:¡ûÃeCWRÝá,ˆœzîKT#ãå¿Ùú`b®v` ‘ ¶QÃÍì껩 SAÖVËéO7+§Áp½&…é# Z~QA’„÷H×~äÆìøt›€À}™ùŸšÿb›æ(@ä>F–Ö`-ò§¥á¿’P!òß/M'ïBäÞË Æž쬦(r µÖÉCiqW±®±%c¾¯™4žÄ‚”yÂõñ2 Ô’„Jññ”Ÿ‹Æk6®¶"½È×g€Ó¾–©IÒx¥\–Ÿèªîë£l(¶"Ã7†öŠ8è,çÑŽ“»h›pI!ï”»7e)D9÷ÌDEÌê–óÕmü½øŒgë·Ýa HÊþÕé?…§¸ßàø¾wA_PϺ–QªlSÑ –\èB"¹ñàÆÌ½Àgç–!þÉùÑ=¥ߘµ†ÕÛÕ8æuôu._NUœ'©@Q mHUZ,pԬ฾yO¨‘Î'.ٛϒ,H!®M´œÕ«¼¬À:ò>ìsÓF4’—¤ýljƒÍªW¯hLgÙ­È©'³…e/1¦×EŠ¿´w6†wd >ÔY4-=8â+s.´U¼—\´qÅã°Gæ•Ú@áÇ>°™WêxB ZšÀX7šzÕ¹1$ìöí÷s1/€8 y|<©ª–@ ¬_¥ö~tÌô¢?Ý¡4Ø•³«úÁKº#‚.L¾´þ­êVO©ölÆörŽ{ÿ3ÙpqüÀ×jcŒâ¨€ªÚüõC:P5QÖÄ'R,ëìöDïùp£ v_ˆRn  ¯¤ ¢R_€$º-¡#œ[ lÉÅ&°qW´ä/ü7”Ô(ûNRй„¢t®³B¡&þÛ‘"7|wD‘CV¹[}öþ Â‡J8¥ycû4°“áÝýœ›…«á;8¨—<ÆM{¦O9?Q+k†A+R¹FQæN!X5{Þqz³y;ììÂ2Âmµå¿‚½@oscrypto-1.3.0/tests/fixtures/keys/test-dsa-1024-der.crt000066400000000000000000000022051421476274700227720ustar00rootroot000000000000000‚0‚@  Ì¥äl—bŒ>0 *†HÎ801 0 UUS10U Massachusetts10UNewbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0 150617124016Z 150717124016Z01 0 UUS10U Massachusetts10UNewbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0‚·0‚+*†HÎ80‚Ô-^»[þè@‰«ó€dðj§ê~YeûZÅô+D)f‹=æm¡Ühê#¾W÷N Õ£Þ©Wñ…¤uÍr{úk—æzëR«l'Kû,D›É‘d"8«ü‡šPO¸Qêøës[Hn%'êQÀ,êB}´"aÌ5îŸÈAò7J|ûü(t°)€/4­YIéY+™DÌ–ú¬aË=ˆ¥Ë´¬¯­ö ¶¶G;5wFlS樠æKôÉq+ tvÞͬuë÷~˜jÅÞ}—Òëä¶½»ÒH@fùq8²“4&žíÎ$ÿ!FÝè sZìõïóä„Cbøa,{–(***nUW¦ÚÑ…®¹“ë|’“uarBÜfä3c—Ð'ÞcŸ3)]“ª«Ó£×çóþ³ªN™ñžÔª›UxƒÔÈLk44[êgZ®³ÚÂ#·QÃncj#¾èÕCuĘ r k4/Wgº¤RqÄ!P2Î9èI¦L óßö„'¨r)Œï£‚0‚0Uëñ?û;<2ZŽh-ìCîä0ÒU#Ê0Ç€ëñ?û;<2ZŽh-ìCîä¡£¤ 01 0 UUS10U Massachusetts10UNewbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io‚ Ì¥äl—bŒ>0 U0ÿ0 *†HÎ800-£á¢l,# 7ž={ƒ0ÖÍ0•edS‚ Áa`(fO¾bToscrypto-1.3.0/tests/fixtures/keys/test-dsa-1024-der.key000066400000000000000000000006771421476274700230050ustar00rootroot000000000000000‚»Ô-^»[þè@‰«ó€dðj§ê~YeûZÅô+D)f‹=æm¡Ühê#¾W÷N Õ£Þ©Wñ…¤uÍr{úk—æzëR«l'Kû,D›É‘d"8«ü‡šPO¸Qêøës[Hn%'êQÀ,êB}´"aÌ5îŸÈAò7J|ûü(t°)€/4­YIéY+™DÌ–ú¬aË=ˆ¥Ë´¬¯­ö ¶¶G;5wFlS樠æKôÉq+ tvÞͬuë÷~˜jÅÞ}—Òëä¶½»ÒH@fùq8²“4&žíÎ$ÿ!FÝè sZìõïóä„Cbøa,{–(***nUW¦ÚÑ®¹“ë|’“uarBÜfä3c—Ð'ÞcŸ3)]“ª«Ó£×çóþ³ªN™ñžÔª›UxƒÔÈLk44[êgZ®³ÚÂ#·QÃncj#¾èÕCuĘ r k4/Wgº¤RqÄ!P2Î9èI¦L óßö„'¨r)ŒïAÐá8nAbš¤.›X7w¯q•}oscrypto-1.3.0/tests/fixtures/keys/test-dsa-1024.crt000066400000000000000000000031271421476274700222260ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIEgTCCBECgAwIBAgIJAMyl5GyXYow+MAkGByqGSM44BAMwgZ0xCzAJBgNVBAYT AlVTMRYwFAYDVQQIEw1NYXNzYWNodXNldHRzMRAwDgYDVQQHEwdOZXdidXJ5MR4w HAYDVQQKExVDb2RleCBOb24gU3VmZmljaXQgTEMxEDAOBgNVBAsTB1Rlc3Rpbmcx EjAQBgNVBAMTCVdpbGwgQm9uZDEeMBwGCSqGSIb3DQEJARYPd2lsbEBjb2RleG5z LmlvMB4XDTE1MDYxNzEyNDAxNloXDTE1MDcxNzEyNDAxNlowgZ0xCzAJBgNVBAYT AlVTMRYwFAYDVQQIEw1NYXNzYWNodXNldHRzMRAwDgYDVQQHEwdOZXdidXJ5MR4w HAYDVQQKExVDb2RleCBOb24gU3VmZmljaXQgTEMxEDAOBgNVBAsTB1Rlc3Rpbmcx EjAQBgNVBAMTCVdpbGwgQm9uZDEeMBwGCSqGSIb3DQEJARYPd2lsbEBjb2RleG5z LmlvMIIBtzCCASsGByqGSM44BAEwggEeAoGBANQtXrtb/uhAiavzgGSQ8GqnkOp+ WWX7WsX0K0QFKWaLPeZtodxo6hYjvlf3Tn8N1aPeqVfxhRGkFnXNcnv6a5cWHBPm egAA61KrbJAnHUv7LESbyZESZCITOKv8h5pQFk+4Uer463NbSG4lJ43qUcCQLOpC FX20HCIdYcw17h+fAhUAyEEbj/IEAx43Snz7/Ch0FbADEykCgYAXLzStWUnpWQYr mRZEzA8YlvqsYcs9AYilGMu0DqwRr632Dba2Rzs1d0ZsU+aooOaBS/TJcY0rDXR2 3s0TrHXr936YasXefRqX0uvktr270khAZvlxOLKTNCae7c4k/wcSIUbd6ApzWuz1 7/MY5IRDYvhhLHuWKCoqKm5VV6ba0QOBhQACgYEArrmT63ySk3VhckLcZuQzY5fQ J94DY58zCCldk6qrHNOj1+fz/rOqTpnxgZ7UqhibVXiD1BHITBlrNDQXW+pnWq6z HtoXwiO3UcNuY2oeI74c6NVDgXXEmBIKcqBrHzQvVwNnuhqkUnHEIQIcUDLOOehJ pkwJ89/2hCeocgMpjO+jggEGMIIBAjAdBgNVHQ4EFgQU6/EGkD/7OzwyCFqOaA4t F+xD7uQwgdIGA1UdIwSByjCBx4AU6/EGkD/7OzwyCFqOaA4tF+xD7uShgaOkgaAw gZ0xCzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1NYXNzYWNodXNldHRzMRAwDgYDVQQH EwdOZXdidXJ5MR4wHAYDVQQKExVDb2RleCBOb24gU3VmZmljaXQgTEMxEDAOBgNV BAsTB1Rlc3RpbmcxEjAQBgNVBAMTCVdpbGwgQm9uZDEeMBwGCSqGSIb3DQEJARYP d2lsbEBjb2RleG5zLmlvggkAzKXkbJdijD4wDAYDVR0TBAUwAwEB/zAJBgcqhkjO OAQDAzAAMC0CFQCj4RcGomwsBiMNN549e4Mw1s0wlQIUA2UWE2RTggnBYWAdKGZP AL4GYlQ= -----END CERTIFICATE----- oscrypto-1.3.0/tests/fixtures/keys/test-dsa-1024.key000066400000000000000000000012341421476274700222230ustar00rootroot00000000000000-----BEGIN DSA PRIVATE KEY----- MIIBuwIBAAKBgQDULV67W/7oQImr84BkkPBqp5Dqflll+1rF9CtEBSlmiz3mbaHc aOoWI75X905/DdWj3qlX8YURpBZ1zXJ7+muXFhwT5noAAOtSq2yQJx1L+yxEm8mR EmQiEzir/IeaUBZPuFHq+OtzW0huJSeN6lHAkCzqQhV9tBwiHWHMNe4fnwIVAMhB G4/yBAMeN0p8+/wodBWwAxMpAoGAFy80rVlJ6VkGK5kWRMwPGJb6rGHLPQGIpRjL tA6sEa+t9g22tkc7NXdGbFPmqKDmgUv0yXGNKw10dt7NE6x16/d+mGrF3n0al9Lr 5La9u9JIQGb5cTiykzQmnu3OJP8HEiFG3egKc1rs9e/zGOSEQ2L4YSx7ligqKipu VVem2tECgYEArrmT63ySk3VhckLcZuQzY5fQJ94DY58zCCldk6qrHNOj1+fz/rOq TpnxgZ7UqhibVXiD1BHITBlrNDQXW+pnWq6zHtoXwiO3UcNuY2oeI74c6NVDgXXE mBIKcqBrHzQvVwNnuhqkUnHEIQIcUDLOOehJpkwJ89/2hCeocgMpjO8CFBJB0OE4 Bm5BYpqkLptYN3evcZV9 -----END DSA PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-dsa-1024.param000066400000000000000000000007071421476274700225370ustar00rootroot00000000000000-----BEGIN DSA PARAMETERS----- MIIBHgKBgQDULV67W/7oQImr84BkkPBqp5Dqflll+1rF9CtEBSlmiz3mbaHcaOoW I75X905/DdWj3qlX8YURpBZ1zXJ7+muXFhwT5noAAOtSq2yQJx1L+yxEm8mREmQi Ezir/IeaUBZPuFHq+OtzW0huJSeN6lHAkCzqQhV9tBwiHWHMNe4fnwIVAMhBG4/y BAMeN0p8+/wodBWwAxMpAoGAFy80rVlJ6VkGK5kWRMwPGJb6rGHLPQGIpRjLtA6s Ea+t9g22tkc7NXdGbFPmqKDmgUv0yXGNKw10dt7NE6x16/d+mGrF3n0al9Lr5La9 u9JIQGb5cTiykzQmnu3OJP8HEiFG3egKc1rs9e/zGOSEQ2L4YSx7ligqKipuVVem 2tE= -----END DSA PARAMETERS----- oscrypto-1.3.0/tests/fixtures/keys/test-dsa-2048-der.crt000066400000000000000000000030101421476274700227740ustar00rootroot000000000000000‚0‚Ä  ûöy’wœ«0 *†HÎ801 0 UUS10U Massachusetts10UNewbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0 150618210403Z 150718210403Z01 0 UUS10U Massachusetts10UNewbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0‚;0‚.*†HÎ80‚!‚çøÊÚ_]ÐÊŒ­ ¸äe £¤ÇZR4—ð‡—`Á±(jÆ…MKð#W,°ø~Ú<{úŸàï¦ÕëJÙÛ]0äýízœ/P‘ÂW¿Áéo@TZÇï'G¼™ -õD>—Âñüǯ¹Ânžmô“¬øX,öhX;wµŠê h'$–hTjWº4fðÔ‹¯· ò+Pø #YY Û£ò¿°g4#‚Þü±”§QYJ(‹Û&«‰!òׇæv½ãx¡iø`›oVQšñPÕõr‰ÈŒÕeCíþ3ŒË€³u¾jöÍsgƒ—¢&ÔÉ ) Zôá-²¦™P.Jî—«ºXµPã²6ÙÍ‚y:š2‚'vnÆ® p-kGT«Êîªæû›Êp/Ùñg ü´=±êƒ²¤¾ìQLô‡Øˆ§Õõ™­õi"«W\Î=§dá²géƒXº‡Jòžb`[†ø¼ lþ—ÊÄæ•«qGzªz¢VÐKý# %ìnNãV•{OàGI ä‹ÿ¹ ‚13åµöªé.Ï¿-s’È÷[m‡tááã…‚‚uö½Õ8ýtœºîœ3Ü4B¹¬ø à…ž·>gfL…0Áêûƒw£n3th¾0ÂL¬‘L7 ¶çæY^'wo»èÎqG($WQûyOÑX—wtþׂé:{h¬Ü½±Qó•™Ò? geøÅÓ æè@à¿sœDùáreÝÚU7Aa‚}„|tMØ;’y7èÄ߯ ê ™1J oÊDMg—w—§÷ Ê#©ªyy³ O2¸«ïÆÌ%iE‡-VÕŠ†¼øšO$Ñ –Ô«21sù1¬—ÇóM¼Ôܤ7J£Y: 2?ϧÅá2¿½‚9ËH[O—È™Öè¤b°諾5£‚0‚0U>陨ûÛdˆ…iwG°ë€,÷5ÿ0ÒU#Ê0Ç€>陨ûÛdˆ…iwG°ë€,÷5ÿ¡£¤ 01 0 UUS10U Massachusetts10UNewbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io‚ ûöy’wœ«0 U0ÿ0 *†HÎ8/0,z¤ ÎVÐÔ Ä»fÕ¼Um.d&…k•ksY± &¥?ö`÷“ˆoscrypto-1.3.0/tests/fixtures/keys/test-dsa-2048-der.key000066400000000000000000000015021421476274700230000ustar00rootroot000000000000000‚>‚çøÊÚ_]ÐÊŒ­ ¸äe £¤ÇZR4—ð‡—`Á±(jÆ…MKð#W,°ø~Ú<{úŸàï¦ÕëJÙÛ]0äýízœ/P‘ÂW¿Áéo@TZÇï'G¼™ -õD>—Âñüǯ¹Ânžmô“¬øX,öhX;wµŠê h'$–hTjWº4fðÔ‹¯· ò+Pø #YY Û£ò¿°g4#‚Þü±”§QYJ(‹Û&«‰!òׇæv½ãx¡iø`›oVQšñPÕõr‰ÈŒÕeCíþ3ŒË€³u¾jöÍsgƒ—¢&ÔÉ ) Zôá-²¦™P.Jî—«ºXµPã²6ÙÍ‚y:š2‚'vnÆ® p-kGT«Êîªæû›Êp/Ùñg ü´=±êƒ²¤¾ìQLô‡Øˆ§Õõ™­õi"«W\Î=§dá²géƒXº‡Jòžb`[†ø¼ lþ—ÊÄæ•«qGzªz¢VÐKý# %ìnNãV•{OàGI ä‹ÿ¹ ‚13åµöªé.Ï¿-s’È÷[m‡tááã…‚uö½Õ8ýtœºîœ3Ü4B¹¬ø à…ž·>gfL…0Áêûƒw£n3th¾0ÂL¬‘L7 ¶çæY^'wo»èÎqG($WQûyOÑX—wtþׂé:{h¬Ü½±Qó•™Ò? geøÅÓ æè@à¿sœDùáreÝÚU7Aa‚}„|tMØ;’y7èÄ߯ ê ™1J oÊDMg—w—§÷ Ê#©ªyy³ O2¸«ïÆÌ%iE‡-VÕŠ†¼øšO$Ñ –Ô«21sù1¬—ÇóM¼Ôܤ7J£Y: 2?ϧÅá2¿½‚9ËH[O—È™Öè¤b°諾52ï%¯ê`Ïù¿¥FɳãO©Þ¾oscrypto-1.3.0/tests/fixtures/keys/test-dsa-2048-sha2-der.crt000066400000000000000000000025711421476274700236420ustar00rootroot000000000000000‚u0‚  ËñúV¤+ÃÛ0  `†He01 0 UUS10U Massachusetts10U Newbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0 150627135600Z 150727135600Z01 0 UUS10U Massachusetts10U Newbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0‚F0‚9*†HÎ80‚,‚‡Þ <÷U‰áuTb†ØžLBîºþ€†äáÛ:)D²š-†|;Ï`p;¥ÑEùX[yŽÓkr¶¾bÆm2Fw¾40XNN«ç+ít->¡I²¢3ÝüëÏ_8ÿî˜]_¼‡H¬€ûëé»±¤ˆšê @7æÀp=Ç)¼eLz»@Œk[SàÐDš•ý Ûõ±ÎðãXó4oãÈ»"/¼Á˜ïxôZWFáæ6Q"<1lv C=úÓ ™›€3åLÕdVZ§çÁjAÞ$pØûØÀâü3JP·mÜÍNâX—‰ &»¥[n6‡ >ÚE’šL="p)!ñW!lYоC#=K7[@l[ÞßëG3toŒº]‚m¬¾iÐJbe»ÏòBÖ-A)„‘Wà3 HÅø[²\mæ;¥ã*ð8;.Mw$v5[ד*Â¥A9 h¤â2Ñx£-æTT±(J‘”¿Ù§$¥¶á¬š¯©¨·22fQCQ¯3ŽÍ U‘aXnN$Ìñ¿½^.Q ÖÒàýÄšq(i½Ä{„Kò]tGfiôºÍfaòx7Øu¡!^µr„´ÿ!+ü iòõ (¯6ÈV4kþ²B6äå3+ª¿5}ƒ|\L?îÿ½ë:yõýãZ&Mºó¹§‡u;ÃÄ ÜAjöÃ+uÂO©Õ‰Ó¦ÂèF¢Jö]uõŸæ‚‚G tù’3!¥L?ëzðÙÜíêFyGóLÆ´‹¸ƒÖÜ)`Ë â¶ù®¸z£« 4™ƒ;ã5ÅCð1.àH¿'ãÓý k…]ÈÐ#â4 …è«ý^Þ :+€aAƒÎÔ(g5ÑßÛЀæ¼ò,¯7i¬Âì®=Í¢üÖRM˜Ôœ@˜}×dô! Òts0”…‡·Qªõ2Wâ=wï´#éœÀuwŸdù*ÙìƒÑ2ãÒ½ãævŸtìN ó¹eɪK7 ȾԲ7À6™K%¤3¥¢JGä}úÊPÁ\l9=‡Dy^Æ_LLbŸaž¢ ¶_p9\£P0N0U—´·þ*í[Çúòm'ß):²0U#0€—´·þ*í[Çúòm'ß):²0 U0ÿ0  `†HeI0F!ëæŒ÷öwBÀ_éÅ\wûmþh“€)IÀR™ ºÜ!¶ODïßw!ɾþ‚ P8¾ ~§ ðÛ>¡~rXoscrypto-1.3.0/tests/fixtures/keys/test-dsa-2048-sha2-der.key000066400000000000000000000015311421476274700236350ustar00rootroot000000000000000‚U‚‡Þ <÷U‰áuTb†ØžLBîºþ€†äáÛ:)D²š-†|;Ï`p;¥ÑEùX[yŽÓkr¶¾bÆm2Fw¾40XNN«ç+ít->¡I²¢3ÝüëÏ_8ÿî˜]_¼‡H¬€ûëé»±¤ˆšê @7æÀp=Ç)¼eLz»@Œk[SàÐDš•ý Ûõ±ÎðãXó4oãÈ»"/¼Á˜ïxôZWFáæ6Q"<1lv C=úÓ ™›€3åLÕdVZ§çÁjAÞ$pØûØÀâü3JP·mÜÍNâX—‰ &»¥[n6‡ >ÚE’šL="p)!ñW!lYоC#=K7[@l[ÞßëG3toŒº]‚m¬¾iÐJbe»ÏòBÖ-A)„‘Wà3 HÅø[²\mæ;¥ã*ð8;.Mw$v5[ד*Â¥A9 h¤â2Ñx£-æTT±(J‘”¿Ù§$¥¶á¬š¯©¨·22fQCQ¯3ŽÍ U‘aXnN$Ìñ¿½^.Q ÖÒàýÄšq(i½Ä{„Kò]tGfiôºÍfaòx7Øu¡!^µr„´ÿ!+ü iòõ (¯6ÈV4kþ²B6äå3+ª¿5}ƒ|\L?îÿ½ë:yõýãZ&Mºó¹§‡u;ÃÄ ÜAjöÃ+uÂO©Õ‰Ó¦ÂèF¢Jö]uõŸæ‚G tù’3!¥L?ëzðÙÜíêFyGóLÆ´‹¸ƒÖÜ)`Ë â¶ù®¸z£« 4™ƒ;ã5ÅCð1.àH¿'ãÓý k…]ÈÐ#â4 …è«ý^Þ :+€aAƒÎÔ(g5ÑßÛЀæ¼ò,¯7i¬Âì®=Í¢üÖRM˜Ôœ@˜}×dô! Òts0”…‡·Qªõ2Wâ=wï´#éœÀuwŸdù*ÙìƒÑ2ãÒ½ãævŸtìN ó¹eɪK7 ȾԲ7À6™K%¤3¥¢JGä}úÊPÁ\l9=‡Dy^Æ_LLbŸaž¢ ¶_p9\ #øÖ𒬈•+„C1zéŠVqR!Õ€~ãÁ[âÇï®oscrypto-1.3.0/tests/fixtures/keys/test-dsa-2048-sha2.crt000066400000000000000000000036401421476274700230700ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFdTCCBRmgAwIBAgIJAMvx+lakK8PbMAsGCWCGSAFlAwQDAjCBnTELMAkGA1UE BhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxEDAOBgNVBAcMB05ld2J1cnkx HjAcBgNVBAoMFUNvZGV4IE5vbiBTdWZmaWNpdCBMQzEQMA4GA1UECwwHVGVzdGlu ZzESMBAGA1UEAwwJV2lsbCBCb25kMR4wHAYJKoZIhvcNAQkBFg93aWxsQGNvZGV4 bnMuaW8wHhcNMTUwNjI3MTM1NjAwWhcNMTUwNzI3MTM1NjAwWjCBnTELMAkGA1UE BhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxEDAOBgNVBAcMB05ld2J1cnkx HjAcBgNVBAoMFUNvZGV4IE5vbiBTdWZmaWNpdCBMQzEQMA4GA1UECwwHVGVzdGlu ZzESMBAGA1UEAwwJV2lsbCBCb25kMR4wHAYJKoZIhvcNAQkBFg93aWxsQGNvZGV4 bnMuaW8wggNGMIICOQYHKoZIzjgEATCCAiwCggEBAIfeIDz3VYkT4QN1VGIYHobY nkxCF+4Eup3+gIbk4ds6KUQfHxUcspothnwDO89gcDuPpdEURflYW3mO02tyD7a+ Yg7GbTJGd740MFhOTqvnjSvtdC0+DqFJnbKifzON3fzrz184/53umF1fvIdIFayA ++vpu7GkiJoX6g1AN+bAcD3HKbxlTHoAu0AfjGtbU+AY0ESalf0K2/WxzvAbEeNY 8zRv48i7Ii+8wZjvePRaV0YF4eY2HBZRIgg8MQBsdqBDPfrTC5mbgDPlTNVkVlqn 58FqQd4kcNj72MDi/DNKULdtE9zNTh3iWACXiSAmu6VbbjaHCz7aRZKaTD0icCkC IQDxVyFskFkOir5DIz1LN1tAbFsVBN7f60czCH90b4y6XQKCAQBtrL5pgdBKYmW7 z/JC1i0bQSkUhJFX4BozIEjF+FuyXG3mO6XjKvAWODsuTXckdh01W9eTKsKlQTkL aAGk4jLReKMt5lRUsShKkZS/2ackpbbhrJqvqai3MjJmUQAbQ1GvM47NDFWRYVhu ThoFJMzxv71eLlEL1gTS4P3EF5pxKGkXBL3Ee4QfS/JddEd/Zmn0us1mYfJ4N9h1 oQ4hXrVyhLT/IQgr/AlpjfL1CSivNshWNGsd/h6yF0I25OUzK6q/HDV9g3xcTD/u /73rOnn1/eNaEyZNErrzuaeHdTvDfw/EC9xBavbDGyt1wk+p1YnTpsIC6EaiSvZd G3X1nxPmA4IBBQACggEARyB0+ZIzIaVMPxDrevDZ3O3qRnlH80zGtIu4g9bcKWDL GyDitvmuuI0VeqOrDQyPBTSZCIM74zXFQxrwMS7gSL8n49P9DGuFXcjQI+IdNBIJ hRboEasUEf0FExBe3gk6K4BhQYPO1AUoZzXR39sZA9CA5rzyLK83aQOswuyuPc1/ ovzWBlJNmNQcnECYfdd/ZPQUIQzSdHMwlIWHt1Gq9TJX4j1377Qj6ZwAwHV3EJ9k +SrZ7IPRMuPSvePmdp907E4L87llyapLNwzIvtSynTfANpkbHEslGKQzpaJKR+R9 +spQwVxsOT2HG0QOeV7GX0xMYp8UYZ6iCbYfX3A5XKNQME4wHQYDVR0OBBYEFJe0 t/4q7VvDh/ryAG0nBN8dKTqyMB8GA1UdIwQYMBaAFJe0t/4q7VvDh/ryAG0nBN8d KTqyMAwGA1UdEwQFMAMBAf8wCwYJYIZIAWUDBAMCA0kAMEYCIQDr5oz39ndCE8Bf 6cVcd/tt/miTFxyAKUnAnVKZILoS3AIhALZPRO/fdyHJD77+nRmCClA4vg8gfqcg 8ALbPqF+clh/ -----END CERTIFICATE----- oscrypto-1.3.0/tests/fixtures/keys/test-dsa-2048-sha2.key000066400000000000000000000023101421476274700230610ustar00rootroot00000000000000-----BEGIN DSA PRIVATE KEY----- MIIDVQIBAAKCAQEAh94gPPdViRPhA3VUYhgehtieTEIX7gS6nf6AhuTh2zopRB8f FRyymi2GfAM7z2BwO4+l0RRF+VhbeY7Ta3IPtr5iDsZtMkZ3vjQwWE5Oq+eNK+10 LT4OoUmdsqJ/M43d/OvPXzj/ne6YXV+8h0gVrID76+m7saSImhfqDUA35sBwPccp vGVMegC7QB+Ma1tT4BjQRJqV/Qrb9bHO8BsR41jzNG/jyLsiL7zBmO949FpXRgXh 5jYcFlEiCDwxAGx2oEM9+tMLmZuAM+VM1WRWWqfnwWpB3iRw2PvYwOL8M0pQt20T 3M1OHeJYAJeJICa7pVtuNocLPtpFkppMPSJwKQIhAPFXIWyQWQ6KvkMjPUs3W0Bs WxUE3t/rRzMIf3RvjLpdAoIBAG2svmmB0EpiZbvP8kLWLRtBKRSEkVfgGjMgSMX4 W7JcbeY7peMq8BY4Oy5NdyR2HTVb15MqwqVBOQtoAaTiMtF4oy3mVFSxKEqRlL/Z pySltuGsmq+pqLcyMmZRABtDUa8zjs0MVZFhWG5OGgUkzPG/vV4uUQvWBNLg/cQX mnEoaRcEvcR7hB9L8l10R39mafS6zWZh8ng32HWhDiFetXKEtP8hCCv8CWmN8vUJ KK82yFY0ax3+HrIXQjbk5TMrqr8cNX2DfFxMP+7/ves6efX941oTJk0SuvO5p4d1 O8N/D8QL3EFq9sMbK3XCT6nVidOmwgLoRqJK9l0bdfWfE+YCggEARyB0+ZIzIaVM PxDrevDZ3O3qRnlH80zGtIu4g9bcKWDLGyDitvmuuI0VeqOrDQyPBTSZCIM74zXF QxrwMS7gSL8n49P9DGuFXcjQI+IdNBIJhRboEasUEf0FExBe3gk6K4BhQYPO1AUo ZzXR39sZA9CA5rzyLK83aQOswuyuPc1/ovzWBlJNmNQcnECYfdd/ZPQUIQzSdHMw lIWHt1Gq9TJX4j1377Qj6ZwAwHV3EJ9k+SrZ7IPRMuPSvePmdp907E4L87llyapL NwzIvtSynTfANpkbHEslGKQzpaJKR+R9+spQwVxsOT2HG0QOeV7GX0xMYp8UYZ6i CbYfX3A5XAIgI/jW8JIBrIiVK4RDMXrpFYpWcVIh1YAFfuPBW+LH764= -----END DSA PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-dsa-2048-sha2.param000066400000000000000000000014641421476274700234020ustar00rootroot00000000000000-----BEGIN DSA PARAMETERS----- MIICLAKCAQEAh94gPPdViRPhA3VUYhgehtieTEIX7gS6nf6AhuTh2zopRB8fFRyy mi2GfAM7z2BwO4+l0RRF+VhbeY7Ta3IPtr5iDsZtMkZ3vjQwWE5Oq+eNK+10LT4O oUmdsqJ/M43d/OvPXzj/ne6YXV+8h0gVrID76+m7saSImhfqDUA35sBwPccpvGVM egC7QB+Ma1tT4BjQRJqV/Qrb9bHO8BsR41jzNG/jyLsiL7zBmO949FpXRgXh5jYc FlEiCDwxAGx2oEM9+tMLmZuAM+VM1WRWWqfnwWpB3iRw2PvYwOL8M0pQt20T3M1O HeJYAJeJICa7pVtuNocLPtpFkppMPSJwKQIhAPFXIWyQWQ6KvkMjPUs3W0BsWxUE 3t/rRzMIf3RvjLpdAoIBAG2svmmB0EpiZbvP8kLWLRtBKRSEkVfgGjMgSMX4W7Jc beY7peMq8BY4Oy5NdyR2HTVb15MqwqVBOQtoAaTiMtF4oy3mVFSxKEqRlL/ZpySl tuGsmq+pqLcyMmZRABtDUa8zjs0MVZFhWG5OGgUkzPG/vV4uUQvWBNLg/cQXmnEo aRcEvcR7hB9L8l10R39mafS6zWZh8ng32HWhDiFetXKEtP8hCCv8CWmN8vUJKK82 yFY0ax3+HrIXQjbk5TMrqr8cNX2DfFxMP+7/ves6efX941oTJk0SuvO5p4d1O8N/ D8QL3EFq9sMbK3XCT6nVidOmwgLoRqJK9l0bdfWfE+Y= -----END DSA PARAMETERS----- oscrypto-1.3.0/tests/fixtures/keys/test-dsa-2048.crt000066400000000000000000000041431421476274700222340ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIGBDCCBcSgAwIBAgIJAPv2eZIPd5yrMAkGByqGSM44BAMwgZ0xCzAJBgNVBAYT AlVTMRYwFAYDVQQIEw1NYXNzYWNodXNldHRzMRAwDgYDVQQHEwdOZXdidXJ5MR4w HAYDVQQKExVDb2RleCBOb24gU3VmZmljaXQgTEMxEDAOBgNVBAsTB1Rlc3Rpbmcx EjAQBgNVBAMTCVdpbGwgQm9uZDEeMBwGCSqGSIb3DQEJARYPd2lsbEBjb2RleG5z LmlvMB4XDTE1MDYxODIxMDQwM1oXDTE1MDcxODIxMDQwM1owgZ0xCzAJBgNVBAYT AlVTMRYwFAYDVQQIEw1NYXNzYWNodXNldHRzMRAwDgYDVQQHEwdOZXdidXJ5MR4w HAYDVQQKExVDb2RleCBOb24gU3VmZmljaXQgTEMxEDAOBgNVBAsTB1Rlc3Rpbmcx EjAQBgNVBAMTCVdpbGwgQm9uZDEeMBwGCSqGSIb3DQEJARYPd2lsbEBjb2RleG5z LmlvMIIDOzCCAi4GByqGSM44BAEwggIhAoIBAQDn+MoB2l9d0MqBjK0JuORloKOk HcdaUjSXH/CHl2DBsR4oasaFTQhL8CNXLLAb+H7aPAZ7EBX6n+DvptXrStnbXTDk /e16nC9QAoE8aSXaWUzwNwyiAeu5BewP1cJG/O2iSWQTJKtkq2tu66zOkcydsrJx Lo7nORZJdY+0Y2NAeKu/lAgdN5rj8kBWnYFWyUrBtIteoKGvtSKmX37eaRCPx/yz riJUg24qoq4Ak2VvrPPpbuk1PpHCFFe/welvQFRax+8nR7yZDC31RAg+nZfC8Q/8 x6+5ncJuno1tEPQUk6z4WCz2aFgVO3e1iuoMaCcklmhUale6NBRm8A7Ui6+3AhUA j6DyK1D4oCNZWQzbo/K/sGc0IxMCggEBAN78sZSnFVFZSigPi9smq4kh8teH5na9 43ihafgFYJsRb1YGUZrxUNX1conIjNVlEEPt/jMfjMuAs3W+avbNc2eDl6Im1Mkg KRYGoFr04S2ypplQLkrul6sDuli1CFDjsjbZzYJ5Opoygid2bsauCXAta0dUq8ru qub7m8pwL9nxZw38tD2x6oOypL7sUUz0h9iIp9X1ma31aSKrnQ5XXM49p2ThA7Jn 6YNYuocOSvKeYmBbhvi8Bwls/pfKxOaVq3FHeqp6olbQS/0ZESMJJexuBk7jVpV7 T+BHSRQN5Iv/uaCCMTMB5bX2qukuzxgRFb8tBnOSyPdbbYd0E+Hh44UDggEFAAKC AQB19r3VOP10nLrunBYz3DRCuaz4CuCFnrc+Z2ZMhR4wwer7g3ejbjN0aAMWvjDC TKyRTDcJtufmWV4nd2+76M5xRygkV40PUft5T9FYl3d0/teC6QA6e2is3L2xURDz lZnSPwpnZfjF06AA5oHoQOC/cwecRPnhcmV/3dpVN0Fhgn2EfHRN2I07AJJ5N+jE At+vCeoLFh+ZMUoMnW/KRE1nE5d3l6f3oMojqap5ebMgf08yuKvvxswlaUWHLVbV ioYRvPiaTyTRIJbUqzIxc/kxrJCXxxTzTbwS1NykN0qjWTogMj/Pp8UT4QMyv72C OctIW0+XyJnW6KRisA/vqr01o4IBBjCCAQIwHQYDVR0OBBYEFD7pmdj722SIhWl3 R7DrgCyd9zX/MIHSBgNVHSMEgcowgceAFD7pmdj722SIhWl3R7DrgCyd9zX/oYGj pIGgMIGdMQswCQYDVQQGEwJVUzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czEQMA4G A1UEBxMHTmV3YnVyeTEeMBwGA1UEChMVQ29kZXggTm9uIFN1ZmZpY2l0IExDMRAw DgYDVQQLEwdUZXN0aW5nMRIwEAYDVQQDEwlXaWxsIEJvbmQxHjAcBgkqhkiG9w0B CQEWD3dpbGxAY29kZXhucy5pb4IJAPv2eZIPd5yrMAwGA1UdEwQFMAMBAf8wCQYH KoZIzjgEAwMvADAsAhR6pAMJzlbQ1CDEu2bVvFVtLmQmhQIUa5UUaw5zWbEMJqU/ A/ZgHB/3k4g= -----END CERTIFICATE----- oscrypto-1.3.0/tests/fixtures/keys/test-dsa-2048.key000066400000000000000000000022501421476274700222310ustar00rootroot00000000000000-----BEGIN DSA PRIVATE KEY----- MIIDPgIBAAKCAQEA5/jKAdpfXdDKgYytCbjkZaCjpB3HWlI0lx/wh5dgwbEeKGrG hU0IS/AjVyywG/h+2jwGexAV+p/g76bV60rZ210w5P3tepwvUAKBPGkl2llM8DcM ogHruQXsD9XCRvztoklkEySrZKtrbuuszpHMnbKycS6O5zkWSXWPtGNjQHirv5QI HTea4/JAVp2BVslKwbSLXqChr7Uipl9+3mkQj8f8s64iVINuKqKuAJNlb6zz6W7p NT6RwhRXv8Hpb0BUWsfvJ0e8mQwt9UQIPp2XwvEP/MevuZ3Cbp6NbRD0FJOs+Fgs 9mhYFTt3tYrqDGgnJJZoVGpXujQUZvAO1IuvtwIVAI+g8itQ+KAjWVkM26Pyv7Bn NCMTAoIBAQDe/LGUpxVRWUooD4vbJquJIfLXh+Z2veN4oWn4BWCbEW9WBlGa8VDV 9XKJyIzVZRBD7f4zH4zLgLN1vmr2zXNng5eiJtTJICkWBqBa9OEtsqaZUC5K7per A7pYtQhQ47I22c2CeTqaMoIndm7GrglwLWtHVKvK7qrm+5vKcC/Z8WcN/LQ9seqD sqS+7FFM9IfYiKfV9Zmt9Wkiq50OV1zOPadk4QOyZ+mDWLqHDkrynmJgW4b4vAcJ bP6XysTmlatxR3qqeqJW0Ev9GREjCSXsbgZO41aVe0/gR0kUDeSL/7mggjEzAeW1 9qrpLs8YERW/LQZzksj3W22HdBPh4eOFAoIBAHX2vdU4/XScuu6cFjPcNEK5rPgK 4IWetz5nZkyFHjDB6vuDd6NuM3RoAxa+MMJMrJFMNwm25+ZZXid3b7voznFHKCRX jQ9R+3lP0ViXd3T+14LpADp7aKzcvbFREPOVmdI/Cmdl+MXToADmgehA4L9zB5xE +eFyZX/d2lU3QWGCfYR8dE3YjTsAknk36MQC368J6gsWH5kxSgydb8pETWcTl3eX p/egyiOpqnl5syB/TzK4q+/GzCVpRYctVtWKhhG8+JpPJNEgltSrMjFz+TGskJfH FPNNvBLU3KQ3SqNZOiAyP8+nxRPhAzK/vYI5y0hbT5fImdbopGKwD++qvTUCFDIR 7yWv6mDP+b+lRsmz40+pAN6+ -----END DSA PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-dsa-2048.param000066400000000000000000000014441421476274700225450ustar00rootroot00000000000000-----BEGIN DSA PARAMETERS----- MIICIQKCAQEA5/jKAdpfXdDKgYytCbjkZaCjpB3HWlI0lx/wh5dgwbEeKGrGhU0I S/AjVyywG/h+2jwGexAV+p/g76bV60rZ210w5P3tepwvUAKBPGkl2llM8DcMogHr uQXsD9XCRvztoklkEySrZKtrbuuszpHMnbKycS6O5zkWSXWPtGNjQHirv5QIHTea 4/JAVp2BVslKwbSLXqChr7Uipl9+3mkQj8f8s64iVINuKqKuAJNlb6zz6W7pNT6R whRXv8Hpb0BUWsfvJ0e8mQwt9UQIPp2XwvEP/MevuZ3Cbp6NbRD0FJOs+Fgs9mhY FTt3tYrqDGgnJJZoVGpXujQUZvAO1IuvtwIVAI+g8itQ+KAjWVkM26Pyv7BnNCMT AoIBAQDe/LGUpxVRWUooD4vbJquJIfLXh+Z2veN4oWn4BWCbEW9WBlGa8VDV9XKJ yIzVZRBD7f4zH4zLgLN1vmr2zXNng5eiJtTJICkWBqBa9OEtsqaZUC5K7perA7pY tQhQ47I22c2CeTqaMoIndm7GrglwLWtHVKvK7qrm+5vKcC/Z8WcN/LQ9seqDsqS+ 7FFM9IfYiKfV9Zmt9Wkiq50OV1zOPadk4QOyZ+mDWLqHDkrynmJgW4b4vAcJbP6X ysTmlatxR3qqeqJW0Ev9GREjCSXsbgZO41aVe0/gR0kUDeSL/7mggjEzAeW19qrp Ls8YERW/LQZzksj3W22HdBPh4eOF -----END DSA PARAMETERS----- oscrypto-1.3.0/tests/fixtures/keys/test-dsa-512.crt000066400000000000000000000017251421476274700221510ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIICpzCCAhACAg4AMA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw ODIyMDcyNjQzWhcNMTcwODIxMDcyNjQzWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs ZS5jb20wgfAwgagGByqGSM44BAEwgZwCQQDKVt7ZYtFRCzrm2/NTjl45YtMgVctQ pLadAowFRydY13uhGw+JXyM+qmngfQkXImQpoYdIe+A8DWG2vaO3wKQ3AhUAxx6d eaDs+XNHcbsiVQ1osvxrG8sCQHQYZDlSy/A5AFXrWXUNlTJbNhWDnitiG/95qYCe FGnwYPp/WyhX+/lbDmQujkrbd4wYStudZM0cc4iDAWeOHQ0DQwACQDtK/S6POMQE 8aI+skBdNQn+Ch76kNDhztC/suOr9FbCSxnZ/CfhSgE1phOJyEkdR2jgErl3uh51 lo+7to76LLUwDQYJKoZIhvcNAQEFBQADgYEAnrmxZ3HB0LmVoFYdBJWxNBkRaFyn jBmRsSJp2xvFg2nyAF77AOqBuFOFqOxg04eDxH8TGLQOWjqdyCFCY79AQlmkdB+8 Z5SWqPEwLJHVLd91O9avQwwRQT5TAxGXFkHTlQxOoaGfTsVQFqSDnlYC4mFjspA7 W+K8+llxOFmtVzU= -----END CERTIFICATE----- oscrypto-1.3.0/tests/fixtures/keys/test-dsa-aes128.key000066400000000000000000000034471421476274700226500ustar00rootroot00000000000000-----BEGIN DSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-128-CBC,FD61FC37119474382557313D31F8E4C6 PSjB7ez+he0SUh2/u40OnSsVw6WhOnL0RlKkXRL24dXHdpdYiNq8ToqB9zsmVu7/ 9C8r4aL94zu+JlZnEaHMuJUuevvpCAnxOguFQil/cQIfCMyNmYjyfFhr95eTxxvf tp5qKIgAgpHhTYuLZGMeWpYECIZJwiQ3lWFzjiQu+/n1mEGV66xjwIh15fwCOUoF vUQpDVGlW4bbBXqScThlKe4SW2YBH3KNEhMU9CbOeJRW5SFKNUEb58NiwAlm4tTE FOD+WwyXqHueqHmCpnRfunOA1dChnllrhKLL8FGrWcz/6zx788NwaLBziBo5e39r BQZ6gzv3RqXlBRWFc7SDIwo+rpCEegEcg17tc6VqOeJFzIerPcU57tQnYvxxhfT7 Cnw9JeAaVOAtC3av66yvRFjN0l8A2CiD4Bcw8L9583CdW8YKpIUdRyXtt11Tb1HD oifCYEH1aPVPI0foZVLkrrQNbrDNkaXsEYPSBDp8S5XL5XuRpiJYdcrMwJl3qEho m+n+oo+s+7XpSE4MTOyAWqq8HbThyG9MXapS9JWsKWd0gbfUwMDPdb56ICBZSKVj SAVsFNfhbkhrHgQwRW0kyfB3uUV79jvXLHDvO3VoAm34zgcfxYNx/znE5/gvl7aY BbRBWS/YLlTPYHsNsyPJ+0TcwOPUPjdG73TMKln6h4rrdNylIAb1sBOGBdIQGVUq 1sQ/StzA7Og1wwm+KCLZJkxqNXW3vfuN3DzX9iSb/3k2Z6YlvVO/plEeO69Ie4SW 5Bv0HwhmODqql+0zkQ6JLQpvYMG/GZ9Y9bZwqrqTa07nV/kvZ+zDMdThO+ifVxUq ERmcWlass/VJndUloAeCK9D7jauqwoo18g0cg2/aEfBvzxoAuEKoiADt1QysxmG6 sNekp1yscepjubvHKV/oD8rFBVvHPcV55jPLU0CbfXO2MfrD87V+eoVrqC4pnR5v 6VYnY3GqZLA7AiOCailt3D9UVzsRtGO2jArBnyMTR0NoI5er2V8n6IIqlwC1OyU4 yJ0oSXunkq4+VkWGOr1CquLhyXSC//RyQSrHfra8ckEJtU/02hFeW374Fe2WaNr2 k79hh6VT063LIDaTXI8Ls1qCdqsM7BXkQAMVwgNR17FvAInSrCYo03YbB4IOdfH4 D+MC6tz1CJZB7mH5+uRCQ3vAg5047dm3ZcWwCCL2GqnUzHdVlvWEcV6R+ITXsovX vqTKoMrV2hSED9x4cLaAxrozxSPorkKaZqkd/N5E0pcgV4TuUX+Fm5NCChvSmsz8 qkW+HwDUCq7ffd6nITEbjCjYUSGWaxlfzL/6XOgKPw7S3C15nxuGb6jLeEP8dHvN wetL0g6WYQ/ZjhgBdbZ1NLQOUUAztkB4Jd5r+mVjDG9nobyEC/squHxoBtsM1jRG UvtRWiMgqkBjx0HKKI4DEtX3VrwJ8b/JExSS9cqLTJF8gRHadd8AM7sx4YRU1M7Y fKWZKCIBruPtJTkIk9oqv/5Lk1dq10DU22T+GggBZ+fIctdIBA1/7IDvln6vkLXi MTYJMP0i/AGH/Ag0f5aNNVRPYMnLf2Nq6pWVKUhRKOyvhHARvtvX0U5ik5lnXUhn oq+Jrj5I9zPKG5oAqujc+7OQK6jgP1CgHYDxHG4fmDDtjNxpKOADrShygCfJnLn0 -----END DSA PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-dsa-der.crt000066400000000000000000000033671421476274700224200ustar00rootroot000000000000000‚ó0‚™  Æý€Ë¾20  `†He01 0 UUS10U Massachusetts10U Newbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0 150520130902Z 250517130902Z01 0 UUS10U Massachusetts10U Newbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0‚Æ0‚9*†HÎ80‚,‚ÆÏM$”³ü *“mzÎO¥u†©¡ò‚üÇq¿”¯Uª¦¾Ô)ÿ“ÆŽ‚­o/&þÍÃ'f5ÿŸK‰˜QñOAŽïygÆø.1˜ÖRŒ[VwbW'Û2Œ¸µ"1[ƒk·]‰Bðᕊ'>3Xª5²jÁÈý¢·É#%VVõƒ màПŠÀ´$‡`ÍeÜçÏN3»#;sQ¬øA# ]9X¼YôL¤n£ØpøKù-³˺Ä+€JæÜ1ÑpLíRÌq&ŠT‘§åüòµÏ#XËZج5ƒŠŠ…ÀзýTúwïJ ¼„ñõOŽ»¿• R,Ë‚Rx`ž`u?îÔ&zÅ ÿ»$™c9Œ¾Šñ©Å*ÈîkqC'¼#Ãý²Ó›‡¯lÒˆ+}#! þ9rrÅ6óŒ½ØÃ¸9nšÑÜÚ›kÆ«sš˜ÍfX­¾{Ârä°îݼÚÜ¿¦‚jGи,¢ªcà é a)B4ž×COäR±Ó>¶Æ _¿ I!žEIó¡R)¡B¸Aú¨)•pQÄZø' WPi]»‚€!}°å,T&‘¹#+$¯Ã^ºšsÈõŒ®µñ¹@?]²Øižž./µî›„†×'=dÍvQoœfmF™ÿš‘Ü%@ «\çóh¬¾î¢Ôã+­Uí.OY癥õˆ–¸£\¯H¡þì®æ B(P‡õÝpº$ËfÎãÞa½ ýFÃ~ª.ãÜ¿b%„i@»C6+(Fᤠ<>q³_¦àó›^(Ú3¡µûä´ “Ìê¼n1ón¡9?À PòÝÖßuÈKê$LAämŽ„ÏŽ²`ÐÇø‹Ij¹me¥€ü7¦S* @*•+]Æ©“ì Ÿ#÷±:Òš$/=fÒ½â-ø8ñõ84ïÕ)/Ö €ˆ¸¿W D\ˆÞ.Í\ªa‹ÌÁjT°Ôl@Â}%þV}F(- Ô9ŸÕËñ^È!®w?ÎVûè]·åúT_¥¦IPŸsx: kõø$“&]ÊÃF‚Dÿ¬c•wàqÒAk8_ˆº4]j‚…‚€)*¶áUã•´ˆúëû#9Ê%Ø)p yù¹”'@y9âÁÓB経\‰ð:š€Ý™R]÷&%Åcb©» ÀÕW‘ÖåŠÄZõû[™`Ùíú—‘0_€¥vöõʉV¿„ƒË®K‘@>Óh}9Îd~ÿ^h ¦»òAÀõÏýD!,«Èˆ×*n¤HױϛÁÚ«ù¹û›Ë *"ôo>VÅüÆê®‰ UCiךփhžºQE™jȽ³MÞíÓ3Õ&íÀElJü)¯œ…üRi>…&J®ãJ•²¯(”ÍD¤3unê× ö\}‘ž£@ØQÞ&Š—®ÝWVâƄʲÝÊc½‚Áb’I+RA´3a–×þS£3Ï·%Ùv€§-§h>3Ç-SÄKC](l)VاÝLÖSˆ;l5“÷”h}½„ÖM¶çј½#½o±¦ 9­ŠŽ¶Ó~l_ ¿‹úë"E¶º<ò|Ëo™D£P0N0U£7†ù™(òtp`‡òÓ~a¨¾0U#0€£7†ù™(òtp`‡òÓ~a¨¾0 U0ÿ0  `†HeG0D y8!oCMUJ%°q l"ƒ!\$¾gôd@8I#T6 [2ƒêŽñKØôÏx •€g?㈉!ŽÓõmº8Íoscrypto-1.3.0/tests/fixtures/keys/test-dsa-der.key000066400000000000000000000023321421476274700224070ustar00rootroot000000000000000‚Ö‚ÆÏM$”³ü *“mzÎO¥u†©¡ò‚üÇq¿”¯Uª¦¾Ô)ÿ“ÆŽ‚­o/&þÍÃ'f5ÿŸK‰˜QñOAŽïygÆø.1˜ÖRŒ[VwbW'Û2Œ¸µ"1[ƒk·]‰Bðᕊ'>3Xª5²jÁÈý¢·É#%VVõƒ màПŠÀ´$‡`ÍeÜçÏN3»#;sQ¬øA# ]9X¼YôL¤n£ØpøKù-³˺Ä+€JæÜ1ÑpLíRÌq&ŠT‘§åüòµÏ#XËZج5ƒŠŠ…ÀзýTúwïJ ¼„ñõOŽ»¿• R,Ë‚Rx`ž`u?îÔ&zÅ ÿ»$™c9Œ¾Šñ©Å*ÈîkqC'¼#Ãý²Ó›‡¯lÒˆ+}#! þ9rrÅ6óŒ½ØÃ¸9nšÑÜÚ›kÆ«sš˜ÍfX­¾{Ârä°îݼÚÜ¿¦‚jGи,¢ªcà é a)B4ž×COäR±Ó>¶Æ _¿ I!žEIó¡R)¡B¸Aú¨)•pQÄZø' WPi]»‚€!}°å,T&‘¹#+$¯Ã^ºšsÈõŒ®µñ¹@?]²Øižž./µî›„†×'=dÍvQoœfmF™ÿš‘Ü%@ «\çóh¬¾î¢Ôã+­Uí.OY癥õˆ–¸£\¯H¡þì®æ B(P‡õÝpº$ËfÎãÞa½ ýFÃ~ª.ãÜ¿b%„i@»C6+(Fᤠ<>q³_¦àó›^(Ú3¡µûä´ “Ìê¼n1ón¡9?À PòÝÖßuÈKê$LAämŽ„ÏŽ²`ÐÇø‹Ij¹me¥€ü7¦S* @*•+]Æ©“ì Ÿ#÷±:Òš$/=fÒ½â-ø8ñõ84ïÕ)/Ö €ˆ¸¿W D\ˆÞ.Í\ªa‹ÌÁjT°Ôl@Â}%þV}F(- Ô9ŸÕËñ^È!®w?ÎVûè]·åúT_¥¦IPŸsx: kõø$“&]ÊÃF‚Dÿ¬c•wàqÒAk8_ˆº4]j‚€)*¶áUã•´ˆúëû#9Ê%Ø)p yù¹”'@y9âÁÓB経\‰ð:š€Ý™R]÷&%Åcb©» ÀÕW‘ÖåŠÄZõû[™`Ùíú—‘0_€¥vöõʉV¿„ƒË®K‘@>Óh}9Îd~ÿ^h ¦»òAÀõÏýD!,«Èˆ×*n¤HױϛÁÚ«ù¹û›Ë *"ôo>VÅüÆê®‰ UCiךփhžºQE™jȽ³MÞíÓ3Õ&íÀElJü)¯œ…üRi>…&J®ãJ•²¯(”ÍD¤3unê× ö\}‘ž£@ØQÞ&Š—®ÝWVâƄʲÝÊc½‚Áb’I+RA´3a–×þS£3Ï·%Ùv€§-§h>3Ç-SÄKC](l)VاÝLÖSˆ;l5“÷”h}½„ÖM¶çј½#½o±¦ 9­ŠŽ¶Ó~l_ ¿‹úë"E¶º<ò|Ëo™D!• ùÍ`áM£îÖÇbÔŠ&a|0Ÿ‡?"ÑeÔ¾·ÕJ oscrypto-1.3.0/tests/fixtures/keys/test-dsa.crt000066400000000000000000000046501421476274700216440ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIG8zCCBpmgAwIBAgIJAMaQ/YDLvjIbMAsGCWCGSAFlAwQDAjCBnTELMAkGA1UE BhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxEDAOBgNVBAcMB05ld2J1cnkx HjAcBgNVBAoMFUNvZGV4IE5vbiBTdWZmaWNpdCBMQzEQMA4GA1UECwwHVGVzdGlu ZzESMBAGA1UEAwwJV2lsbCBCb25kMR4wHAYJKoZIhvcNAQkBFg93aWxsQGNvZGV4 bnMuaW8wHhcNMTUwNTIwMTMwOTAyWhcNMjUwNTE3MTMwOTAyWjCBnTELMAkGA1UE BhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxEDAOBgNVBAcMB05ld2J1cnkx HjAcBgNVBAoMFUNvZGV4IE5vbiBTdWZmaWNpdCBMQzEQMA4GA1UECwwHVGVzdGlu ZzESMBAGA1UEAwwJV2lsbCBCb25kMR4wHAYJKoZIhvcNAQkBFg93aWxsQGNvZGV4 bnMuaW8wggTGMIIDOQYHKoZIzjgEATCCAywCggGBAMbPTSSUs/wLKhWTbXrOT6V1 hqmh8oL8x3G/lK9VAqqmvtQSEI8QKf+TAsaOgq1vLyb+zcMnZjX/n0uJmFHxFk8V QY7veWfG+C4xFpjWUoxbVndiVyfbBjKMD7i1IjFbg2u3XYlC8OGViic+M1iqNbJq wcj9orfJIyVWVvWDDW3g0J+KwLQkh2DNZdznz04zuxYjO3MQUaz4QSMJXQE5WLxZ 9EyNpG6j2HD4S/ktsxPLusQrgErmE9wx0XBM7VIUzHEmilSRp+X88rXPI1jLWtis NYOKio+FwNC3/VT6d+8ZSgq8GIQd8fVPjru/lQxSLMuCUngRYJ5gdT/u1CZ6ncUJ /xu7JJljOYy+B4rxqcUqyO6Qa3FDBye8I8P9stObh69s0ogrfSMhFwAgIP45cnIC xTbzjL3Yw7g5bprR3Nqba8arD3OamM1mWK2+e8Jy5LDu3bza3L+mgmpHEtCNuCyi qmPDBg4J6aBhKUI0ntdDT+RSsdM+tsYKXxi/HQsKSQIhAJ5FSY1/86FSKaFCuEGB +qgfKZVwH1HEWvgnCldQaV27AoIBgCGNfRiw5SxUJpG5IyskFx2vw166Ehmac8gY 9YyutfG5QD9dstgbaZ4eni4vte6bHRqEAobXJz1kzXZRb5xmbUaZ/5qBkdwlQAyr XH/Dp/NorL7uotTjK61V7S4ET1nnmaX1iJa4o39cr0gDof7srgLmC0IoUIf13XC6 JMsRZs4H495hvQ39RsN+qi7j3L9iJYQbaUC7BgZDNisoRuEWpAkTPD5xsxtfpuDz m14AKNozobUI++S0EgmTCMzqvG4bMY3zAW6hOT/AIFDy3dbfdchL6iRMQeRtjoTP jrJg0Mf4i0lquW1lpYD8jTemUyqPCUAqlStdxqmT7AqfI/exOtKaJC89ZhrSveIt +Djx9Tg079UpL9YNgIi4v51XBwxEXIjeLgbNXBCqYYvMwWpUArDUbEDCfSX+Vn0a RigDLaDUOZ8Y1cvxXsghGq53P85W++hdt+X6VF+lpklQn3N4Ogtr9fgkApMmXcrD RoKPRP+sY5V34HHSQWs4j1+IujRdagOCAYUAAoIBgCkqtuFV45W0iPrr+yMROcoZ JdgpcKB5Gfm5lCdAeR8TOeLB0x5C57WMXInwOn+aGYDdmRF/Ul33JiUGxWMaYhKp ux8HCsDVV5GQFNblisQOWvX7W5lgDtkVF+36GpeRMF+AFqV29vUDyokFVhK/hIPL rkuRQD7TaH05zmQWfv9eaAmmu/JBEMD1z/1EISyryIjXKm6kSNexz5vB2gKr+Qa5 +5vLDCoi9G8DPlbF/MbqrpAdiQ1VQ2nXmtaDaBOeulFFmWrIvbNN3u3THjPVJu0C wEVsHkr8Ka+chfxSaY0+FoUmHkofruNKlbKvKJTNRKQzdW7q16D2A1x9H5Geo0Ae 2FHeJhOKl67dV1biHsaEyrLdymO9BYLBYpJJK1JBtDNhltf+U6Mzz7cl2R92gKct p2gDPjPHBi1TB8RLQ10obClW2KfdTNYCUxyIO2w1kx/3lGh9vYTWTbbnDtGYvSO9 b7GmDTmtio62035sXw2/i/rrIkW2ujzyAxN8y2+ZRKNQME4wHQYDVR0OBBYEFIGj N4b5mSjydHBgh/LTfo0ZYai+MB8GA1UdIwQYMBaAFIGjN4b5mSjydHBgh/LTfo0Z Yai+MAwGA1UdEwQFMAMBAf8wCwYJYIZIAWUDBAMCA0cAMEQCIHk4IW8QkENNVUol sHGgbCKDIVwkBb5n9GRAOEkjVDYZAiBbMoPqjvFLDhYIBNj0z3gYDZWAZz/jiIkh jtP1bbo4zQ== -----END CERTIFICATE----- oscrypto-1.3.0/tests/fixtures/keys/test-dsa.key000066400000000000000000000033201421476274700216350ustar00rootroot00000000000000-----BEGIN DSA PRIVATE KEY----- MIIE1gIBAAKCAYEAxs9NJJSz/AsqFZNtes5PpXWGqaHygvzHcb+Ur1UCqqa+1BIQ jxAp/5MCxo6CrW8vJv7NwydmNf+fS4mYUfEWTxVBju95Z8b4LjEWmNZSjFtWd2JX J9sGMowPuLUiMVuDa7ddiULw4ZWKJz4zWKo1smrByP2it8kjJVZW9YMNbeDQn4rA tCSHYM1l3OfPTjO7FiM7cxBRrPhBIwldATlYvFn0TI2kbqPYcPhL+S2zE8u6xCuA SuYT3DHRcEztUhTMcSaKVJGn5fzytc8jWMta2Kw1g4qKj4XA0Lf9VPp37xlKCrwY hB3x9U+Ou7+VDFIsy4JSeBFgnmB1P+7UJnqdxQn/G7skmWM5jL4HivGpxSrI7pBr cUMHJ7wjw/2y05uHr2zSiCt9IyEXACAg/jlycgLFNvOMvdjDuDlumtHc2ptrxqsP c5qYzWZYrb57wnLksO7dvNrcv6aCakcS0I24LKKqY8MGDgnpoGEpQjSe10NP5FKx 0z62xgpfGL8dCwpJAiEAnkVJjX/zoVIpoUK4QYH6qB8plXAfUcRa+CcKV1BpXbsC ggGAIY19GLDlLFQmkbkjKyQXHa/DXroSGZpzyBj1jK618blAP12y2Btpnh6eLi+1 7psdGoQChtcnPWTNdlFvnGZtRpn/moGR3CVADKtcf8On82isvu6i1OMrrVXtLgRP WeeZpfWIlrijf1yvSAOh/uyuAuYLQihQh/XdcLokyxFmzgfj3mG9Df1Gw36qLuPc v2IlhBtpQLsGBkM2KyhG4RakCRM8PnGzG1+m4PObXgAo2jOhtQj75LQSCZMIzOq8 bhsxjfMBbqE5P8AgUPLd1t91yEvqJExB5G2OhM+OsmDQx/iLSWq5bWWlgPyNN6ZT Ko8JQCqVK13GqZPsCp8j97E60pokLz1mGtK94i34OPH1ODTv1Skv1g2AiLi/nVcH DERciN4uBs1cEKphi8zBalQCsNRsQMJ9Jf5WfRpGKAMtoNQ5nxjVy/FeyCEarnc/ zlb76F235fpUX6WmSVCfc3g6C2v1+CQCkyZdysNGgo9E/6xjlXfgcdJBaziPX4i6 NF1qAoIBgCkqtuFV45W0iPrr+yMROcoZJdgpcKB5Gfm5lCdAeR8TOeLB0x5C57WM XInwOn+aGYDdmRF/Ul33JiUGxWMaYhKpux8HCsDVV5GQFNblisQOWvX7W5lgDtkV F+36GpeRMF+AFqV29vUDyokFVhK/hIPLrkuRQD7TaH05zmQWfv9eaAmmu/JBEMD1 z/1EISyryIjXKm6kSNexz5vB2gKr+Qa5+5vLDCoi9G8DPlbF/MbqrpAdiQ1VQ2nX mtaDaBOeulFFmWrIvbNN3u3THjPVJu0CwEVsHkr8Ka+chfxSaY0+FoUmHkofruNK lbKvKJTNRKQzdW7q16D2A1x9H5Geo0Ae2FHeJhOKl67dV1biHsaEyrLdymO9BYLB YpJJK1JBtDNhltf+U6Mzz7cl2R92gKctp2gDPjPHBi1TB8RLQ10obClW2KfdTNYC UxyIO2w1kx/3lGh9vYTWTbbnDtGYvSO9b7GmDTmtio62035sXw2/i/rrIkW2ujzy AxN8y2+ZRAIhAJUN+c1g4U0Po+7Wx2LUiiZhEHwwn4c/ItFl1L631UoM -----END DSA PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-dsa.p12000066400000000000000000000057351421476274700214630ustar00rootroot000000000000000‚ Ù0‚ Ÿ *†H†÷  ‚ ‚ Œ0‚ ˆ0‚— *†H†÷  ‚ˆ0‚„0‚} *†H†÷ 0 *†H†÷  0} €¡J€‚P:|/ÉÔì°¡jæf°ƒŽç`fý­üÙˆqÿiÂ/$p"!pLÿpZG·:÷<5…uÉ¿×CMÆ!Ä®ÊÊôϺþ‰i.]]óú)XGsB@(:àA½%j6ݵŸX¼M ¥ ¶ø…ïü"Êö·l£PÚoÉÉì±%ýO2 î.ÿæsp‚9.£Øªí[@C èœÙÖåÔ³šEíð…ûç¸Ëw†x~ûëý„¾¾¬dXô/à,J.®šÂÁpÑNç¶îL÷©53Á|œ•-Œ½ä{áq‡|šVó´ôùmôýÚ‹ž'yh³† ç®av·óû‚vÉNw<xdª„κÒî£ÞŠ{uƬ/u8ŽÌS¥¹|КXG«‚­R°œ¸w¸,%åF³¯#æ†Iþ¿°FK”ÊŽ@ì^çïâ‘ö‘l÷VdVuœþ–i[“ÑýÖ·W,™ØIb80˜ñ´Éë«­ùbËϬL"ŒuÌšrC (!*rÒ*Ñ2gwÅà|ØÜ÷(.ÑEFN»E™*ƒ°[ùnã÷ƒT­ìœ[—ÇÕê‡OWEã:ì Ý—c0ìóZ7x%±«¬ai˜¾‰÷¯Úmâ—–4¥„c *hÚ™û-#oCm…^ýM‰¢EÀ²‚˜Úµ²à{ëŒ}²—رMØÞ.PX‚`äÞªRÀ‹fåÅÜæ£ x!€1Ž?4Φ¹ù6؃RIkL˜»[èîÑPηÃO§Þüx»Ý‘¶XN±é—& €q (cºé@P„š¿F*Úñ¾¤¸ê›S~ë¹à¿÷ôIuÙR¾5?ÇìnÁ þ ošZ…U‘¤ë„ U6O":Ú¾¤»ßA'œ*­N$I¥®[CÈå÷Œž…{üiÛu6ÆÙЂïOh¨PϘ–.7]¢ŸÔ¼%ðï«Fû³P‘8e©Â<ËtþUhNîÜPˆÃ¬ÍÎÑH^®¶ê.@-.ûÿl}«.Ýi!àÉÂH“GïøIêª1ZŸIÞ.˜}¹Œºf&&Z¼;Œì«[å}ɶÔ[5Ö,Çç}Ä]Ǭù¶r¶÷›jÖ‡Ÿk@=b^vÒNjä¦x:Š:*)A ݺÓÍ(•p±ØÉ0mÔq3xx™&|G—«‰‰)¸S=µ jçt~Ix !_áÓ÷E‡°l²sŽÍ%ˆnÔíHóÞ¶†¨÷àõÍÁ ~ uª?&þÝÇç?¼œ‰¿AŸ}`»CâÝà)§ÂЧª0jƒk5wŒËZKÅ’ºu[Íš$¡®»dÉ‹›@ÚßLÐN~’ç$%vY4"Ò‹êdø¿ð)äÛp‘‹€÷,b­²ÜG,˜0&‚oJƒ oJÈ`è ¿r¿äDžʆˆp%f¿‹…„£"ðó)mÎÊ*Þv1ÞõG¿q'5u³‡"ð֊Ö綺z‡ á›0Å…|:Àü)áL9ój´nûÇ&Ö¹““+ÿ&7›%XAA !ÅãíwaÔlj¶.UÏzÐÚÆN=ä¨i!Äg—#+Hr’%ôº·‡Ðža‡út„­ë¥ØÿS zÐæZ‘×áÆ›\‹õÞ´ 8%Múµ:ƒ©Ä¡iGm‹QÖ߬šŠ¼KJAÞšÏPã û®) åtŽèuoZî™In^PTã$,ÏW_¹â,‚„Ùa ‚¯hJ‹_ÿŽFª÷ÜËó¶/U@WnE¢Âæ“É^¼8Un³Ó`Â*A ½+žõnŠÿÇÚó}=øÐ B¯bPIr4óàJÍy#©Ç_‚ m_ÄÒ ,,©RyØò÷˜Ð¥&nCÏçÐR1MXŸró/P~Àø¾tÎ`[úpdgØhj ‚f: Št ýEJÿý‹ÂÈUc;íy`P4µ¢ û ½7!£­x%ÔŽ²^'‹´ZŠGùlZo+ Á2èrþsŽOÕ(KX›òà»(¶s#F«žíìZ2ÑŠæÂ,ävÿ¹ŠQ~”g¹'¹j™ƒlå@(JíÙ*:z,$wÍ:$üqb $ :­2I·(ð9·ûj}¢V;ø{få³XK5ñC$u_cÎn´¨¹ï¸ÃØi|ŒÜ |¼=9N(ÿˆGm…©¯ÉÖ¨—Kª%½eàÅÌ´VZÄfßÃG=Âô§Ô%JoYæS×@´yšR<µXú£+ þ—ÿkQYõ­ËjŒ­ÿi¿CvC›xV$yv>QCé.!l/¾;IcÊ—>5«èU\'¤¼û¬<$°'0 LR¸µ8+WFÂÚ:×Õ,c/K·Ñ;XÚˆ¤¿±µŸþÊ+fX:뾇Ø÷Y€#(‰»ái0‚é *†H†÷  ‚Ú‚Ö0‚Ò0‚Î *†H†÷   ‚–0‚’0 *†H†÷  0†¹–i?oæ‚p:Ì!×AÇî_\˜û¾cNY‘÷" ÙN®€°hë·„ù±§€Lš‡ºC!ðì7T¼dÛ®G /öíQ,˜ã}K&¥í¨¤’ÍH¦_>BUÁX“lô¿ÔJŒ0YƒOÆV§‚ÌsJ8Ѓ¶)? ½cÄýñÄî³Gäj<“a’ ìèŸq<æ^ ËQ~0¬Ž;…«¹í1F¶;Êb÷+5nšÐI9.@²+ÑοmTã~!À.% ‚ðî(P…¥ð_™ÅdÝ@UäYî“nÙXú—Ä|hgÙCnR€ª„w5¹+TÃâ @!F¢Úø5:—à“ï¸1è3j¿0ÓÔÕ^rÁü¨Í:˜øörñÌŽœQ[ ðÑV½Œ© H«³ÐO6(â꤂†õtµÌÜœaVÂæˆ;vTâcâi&p™À´LØÁ¤MóüÉÞÖIZ6)ñþ¥|`±iɫӱV _~ëšØYS'ìR™ÅBÁêU|rÐz˜ÕF¦V®Ëáà‰_ OÝ`asºû.Œ}%§ï9ÞŒ)7‡ÂEY§8‚߇šGVµ¤± ‡½<XЦÞã‚Qʇ†Ÿ:<ѱAJôŸ´ ¸*è’œ÷Þ4%å&4gm8ÔS5½æieeì8}øÎ<ØßMR™ë±rˆ«Þ\Ú]€WQøÔîêúf­Â NÊ* œ;ýç!ƒ†NÒ‡ åø°k¦p}ˆ~œ0Æ=ÿ“‡g¤V¨È87URƒù±°d9 ‰I¢3÷2B,u´XòûjXÁT7¿ôëÂ;Ú¿k¢QÀ 9¿Hö|úÒ hÜŽ1ÏîË0ÕFíIô §-í†ýõDQ&Ê“`bï ß‘®8þbþ©Ÿ!€”O([kV']uåÑ0Òаq æ¸,i|uÍ‘îŸcµpVíSg¨—{|¯…`Á:|ôáºO,‹GY¼œ äa~Ø„6‚Ͼþy0osRãY¾Šýt/ŸÛíPç×U·ùØÍs›]“¯_IÛ«=^¡T{ñ§cw¬ÛG;·þZ%f_€OBúÛ± NÅxù¦FÚMÀõž^J£ÃþìzS™d˜þ²‘«„w&¯ƒèÉó$ÂrËÏuZ‚VîˆC  ´ÕÛ9­v÷ûWD6öY²¦1%0# *†H†÷  1$öéDH1}æÄñ*_„®Öåš‹Í010!0 +`õÜÊ·ÆÖçÈ'ÅÙc ŸéqÊš` oscrypto-1.3.0/tests/fixtures/keys/test-dsa.param000066400000000000000000000022151421476274700221470ustar00rootroot00000000000000-----BEGIN DSA PARAMETERS----- MIIDLAKCAYEAxs9NJJSz/AsqFZNtes5PpXWGqaHygvzHcb+Ur1UCqqa+1BIQjxAp /5MCxo6CrW8vJv7NwydmNf+fS4mYUfEWTxVBju95Z8b4LjEWmNZSjFtWd2JXJ9sG MowPuLUiMVuDa7ddiULw4ZWKJz4zWKo1smrByP2it8kjJVZW9YMNbeDQn4rAtCSH YM1l3OfPTjO7FiM7cxBRrPhBIwldATlYvFn0TI2kbqPYcPhL+S2zE8u6xCuASuYT 3DHRcEztUhTMcSaKVJGn5fzytc8jWMta2Kw1g4qKj4XA0Lf9VPp37xlKCrwYhB3x 9U+Ou7+VDFIsy4JSeBFgnmB1P+7UJnqdxQn/G7skmWM5jL4HivGpxSrI7pBrcUMH J7wjw/2y05uHr2zSiCt9IyEXACAg/jlycgLFNvOMvdjDuDlumtHc2ptrxqsPc5qY zWZYrb57wnLksO7dvNrcv6aCakcS0I24LKKqY8MGDgnpoGEpQjSe10NP5FKx0z62 xgpfGL8dCwpJAiEAnkVJjX/zoVIpoUK4QYH6qB8plXAfUcRa+CcKV1BpXbsCggGA IY19GLDlLFQmkbkjKyQXHa/DXroSGZpzyBj1jK618blAP12y2Btpnh6eLi+17psd GoQChtcnPWTNdlFvnGZtRpn/moGR3CVADKtcf8On82isvu6i1OMrrVXtLgRPWeeZ pfWIlrijf1yvSAOh/uyuAuYLQihQh/XdcLokyxFmzgfj3mG9Df1Gw36qLuPcv2Il hBtpQLsGBkM2KyhG4RakCRM8PnGzG1+m4PObXgAo2jOhtQj75LQSCZMIzOq8bhsx jfMBbqE5P8AgUPLd1t91yEvqJExB5G2OhM+OsmDQx/iLSWq5bWWlgPyNN6ZTKo8J QCqVK13GqZPsCp8j97E60pokLz1mGtK94i34OPH1ODTv1Skv1g2AiLi/nVcHDERc iN4uBs1cEKphi8zBalQCsNRsQMJ9Jf5WfRpGKAMtoNQ5nxjVy/FeyCEarnc/zlb7 6F235fpUX6WmSVCfc3g6C2v1+CQCkyZdysNGgo9E/6xjlXfgcdJBaziPX4i6NF1q -----END DSA PARAMETERS----- oscrypto-1.3.0/tests/fixtures/keys/test-ec-aes128.key000066400000000000000000000011771421476274700224660ustar00rootroot00000000000000-----BEGIN EC PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-128-CBC,0B30DBF3A302FAACF03A6CC97FB03F8A 9EEoQMvYmw0gsVmR1mKCAcfvbnQ9WwLjz3+PZwW3WIP13tLUy2dYqp6MS0ALriJw 54WNaOQJZz6oF1ppMz/fTvAXVC9uTNgUhHJ9C2l6+BGpZFZVlW9q2LOw5IxZPfYB 0bLAXu82PVH4g9Mygr9773a+7y3mki2tA1K05L0HTVYKMpvRm4kV/a2ANKRbdAv9 77u/B+ftTUezpCkI9UApxXG318yQUA1fFhmCebGb2IRBtTls+NmvswBy76xNeJLw q+/N2KPkDyQTRpSbZ8+MMIGaY/ZB9tAR8H31uG+Qv9hgSMahXadWdwJuofoIiq9l N3dHLD9TwedfrnEWVtLA84ynum0DWhWxGiNb7vHqtTDoAYbSR+c/rM2rnUFMGJ3o 5vw9avcLkgEEW5wB5XRhdPZsuVQNgOF1BhLX/UqODkBNFSUnxwNhe4ofdFNRECIm fe56AXrE7uhBXIHMm+s234fFPMbh3tpUdif9AsTA7P8= -----END EC PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-ec-der.crt000066400000000000000000000015741421476274700222360ustar00rootroot000000000000000‚x0‚  Ü-Óß¡éÄ0 *†HÎ=01 0 UUS10U Massachusetts10U Newbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0 150520125646Z 250517125646Z01 0 UUS10U Massachusetts10U Newbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0‚K0‚*†HÎ=0÷0,*†HÎ=!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ0[ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü ZÆ5ت:“ç³ë½Uv˜†¼e°ÌS°ö;Î<>'Ò`KÄ6†ç“jfxá&·Ÿ~AkÑòá,BGø¼æåc¤@òw}-ë3 ô¡9Eؘ–OãBâþ›ŽçëJ|ž+Î3Wk1^Î˶@h7¿Qõ!ÿÿÿÿÿÿÿÿÿÿÿÿ¼æú­§ž„ó¹ÊÂüc%QB‹]Lq÷ÖÆ£IcB\GŸËs$ÉÝÑ-ñ:Ÿ·Þ ÐX“Tö‰Ç/‡+÷ù=;4íž{=WBßx Ì1Æן`£P0N0UTªTpl4më]—×üÕ$<Š×0U#0€TªTpl4më]—×üÕ$<Š×0 U0ÿ0 *†HÎ=I0F!ô>aA ;B¤ ¹ð%0«DVÊÔR„h)Ú ¹  BH!Ä逼¡öŬš>Ù¥Ý}S?àf4äCŒ‚Ä5±ªö‰õoscrypto-1.3.0/tests/fixtures/keys/test-ec-der.key000066400000000000000000000005541421476274700222330ustar00rootroot000000000000000‚h èå#ë/‰»º7/ØÌ²{äBž>Sù‰Ði…C] ú0÷0,*†HÎ=!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ0[ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü ZÆ5ت:“ç³ë½Uv˜†¼e°ÌS°ö;Î<>'Ò`KÄ6†ç“jfxá&·Ÿ~AkÑòá,BGø¼æåc¤@òw}-ë3 ô¡9Eؘ–OãBâþ›ŽçëJ|ž+Î3Wk1^Î˶@h7¿Qõ!ÿÿÿÿÿÿÿÿÿÿÿÿ¼æú­§ž„ó¹ÊÂüc%Q¡DB‹]Lq÷ÖÆ£IcB\GŸËs$ÉÝÑ-ñ:Ÿ·Þ ÐX“Tö‰Ç/‡+÷ù=;4íž{=WBßx Ì1Æן`oscrypto-1.3.0/tests/fixtures/keys/test-ec-named-der.crt000066400000000000000000000012071421476274700233110ustar00rootroot000000000000000‚ƒ0‚)  å¬ZÆâC 0 *†HÎ=01 0 UUS10U Massachusetts10U Newbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0 150617154903Z 150717154903Z01 0 UUS10U Massachusetts10U Newbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0Y0*†HÎ=*†HÎ=B…› Ï ü’”ôbÜŒIÿ÷¬ÖŒù!#e¸™) ÅFd¸c/¸ÑËszçZm4µM:µeá;_h)AFú£~û£P0N0U#îîGH*ä5T¸ýVh_âªÍ0U#0€#îîGH*ä5T¸ýVh_âªÍ0 U0ÿ0 *†HÎ=H0E!ÆÄaÁb•¦ Ìèa\µÕqÀJ<¢é›øËÿØoXÈÑÿn Tî*>ÅE¼N†…KêÕ—*%88?ìjòK0ÁF¨á 5oscrypto-1.3.0/tests/fixtures/keys/test-ec-named-der.key000066400000000000000000000001711421476274700233100ustar00rootroot000000000000000w „-²Å>=D€V…HÛðž›ÃšbH2ÖkjÏœOé÷  *†HÎ=¡DB…› Ï ü’”ôbÜŒIÿ÷¬ÖŒù!#e¸™) ÅFd¸c/¸ÑËszçZm4µM:µeá;_h)AFú£~ûoscrypto-1.3.0/tests/fixtures/keys/test-ec-named.crt000066400000000000000000000016441421476274700225460ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIICgzCCAimgAwIBAgIJAOWsWhjG4kOgMAoGCCqGSM49BAMCMIGdMQswCQYDVQQG EwJVUzEWMBQGA1UECAwNTWFzc2FjaHVzZXR0czEQMA4GA1UEBwwHTmV3YnVyeTEe MBwGA1UECgwVQ29kZXggTm9uIFN1ZmZpY2l0IExDMRAwDgYDVQQLDAdUZXN0aW5n MRIwEAYDVQQDDAlXaWxsIEJvbmQxHjAcBgkqhkiG9w0BCQEWD3dpbGxAY29kZXhu cy5pbzAeFw0xNTA2MTcxNTQ5MDNaFw0xNTA3MTcxNTQ5MDNaMIGdMQswCQYDVQQG EwJVUzEWMBQGA1UECAwNTWFzc2FjaHVzZXR0czEQMA4GA1UEBwwHTmV3YnVyeTEe MBwGA1UECgwVQ29kZXggTm9uIFN1ZmZpY2l0IExDMRAwDgYDVQQLDAdUZXN0aW5n MRIwEAYDVQQDDAlXaWxsIEJvbmQxHjAcBgkqhkiG9w0BCQEWD3dpbGxAY29kZXhu cy5pbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIWbAAvPCfySlPRi3IxJ//es 1oz5ISNlGbiZKSDFRmQUuGMvuNHLcxV65xlabTQCtU06tWXhO19oKUFGEfqjfvuj UDBOMB0GA1UdDgQWBBQjje7uR0gq5DVUuP1WaBZf4qrNgTAfBgNVHSMEGDAWgBQj je7uR0gq5DVUuP1WaBZf4qrNgTAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMCA0gA MEUCIQDGxGHBYh2VpgzM6GFctdVxwEo8oumb+Mv/2G9YyNH/bgIgVO4qPsVFvE6G hY9L6tWXKiU4OD8E7GrySzDBRqjhIDU= -----END CERTIFICATE----- oscrypto-1.3.0/tests/fixtures/keys/test-ec-named.key000066400000000000000000000003431421476274700225410ustar00rootroot00000000000000-----BEGIN EC PRIVATE KEY----- MHcCAQEEIIQtgbLFPj1EGIBWhUjb8MKem8OaYkgy1mtqzxacT+n3oAoGCCqGSM49 AwEHoUQDQgAEhZsAC88J/JKU9GLcjEn/96zWjPkhI2UZuJkpIMVGZBS4Yy+40ctz FXrnGVptNAK1TTq1ZeE7X2gpQUYR+qN++w== -----END EC PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-ec.crt000066400000000000000000000023611421476274700214610ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDeDCCAx2gAwIBAgIJANwFLdPfoenEMAoGCCqGSM49BAMCMIGdMQswCQYDVQQG EwJVUzEWMBQGA1UECAwNTWFzc2FjaHVzZXR0czEQMA4GA1UEBwwHTmV3YnVyeTEe MBwGA1UECgwVQ29kZXggTm9uIFN1ZmZpY2l0IExDMRAwDgYDVQQLDAdUZXN0aW5n MRIwEAYDVQQDDAlXaWxsIEJvbmQxHjAcBgkqhkiG9w0BCQEWD3dpbGxAY29kZXhu cy5pbzAeFw0xNTA1MjAxMjU2NDZaFw0yNTA1MTcxMjU2NDZaMIGdMQswCQYDVQQG EwJVUzEWMBQGA1UECAwNTWFzc2FjaHVzZXR0czEQMA4GA1UEBwwHTmV3YnVyeTEe MBwGA1UECgwVQ29kZXggTm9uIFN1ZmZpY2l0IExDMRAwDgYDVQQLDAdUZXN0aW5n MRIwEAYDVQQDDAlXaWxsIEJvbmQxHjAcBgkqhkiG9w0BCQEWD3dpbGxAY29kZXhu cy5pbzCCAUswggEDBgcqhkjOPQIBMIH3AgEBMCwGByqGSM49AQECIQD/////AAAA AQAAAAAAAAAAAAAAAP///////////////zBbBCD/////AAAAAQAAAAAAAAAAAAAA AP///////////////AQgWsY12Ko6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsD FQDEnTYIhucEk2pmeOETnSa3gZ9+kARBBGsX0fLhLEJH+Lzm5WOkQPJ3A32BLesz oPShOUXYmMKWT+NC4v4af5uO5+tKfA+eFivOM1drMV7Oy7ZAaDe/UfUCIQD///// AAAAAP//////////vOb6racXnoTzucrC/GMlUQIBAQNCAASLXUxx99bGo0ljQlxH n8tzJB3J3dEt8TqftwTeINBYAJNU9onHL4cr9/k9OzTtnnsOPVdC33gDC8wxxgPX n2ABo1AwTjAdBgNVHQ4EFgQUVKpUcGw0Gm3rXZfXHvzVJDyKDtcwHwYDVR0jBBgw FoAUVKpUcGw0Gm3rXZfXHvzVJDyKDtcwDAYDVR0TBAUwAwEB/zAKBggqhkjOPQQD AgNJADBGAiEA9D5hQQo7QqQNufAlMB4Dq0RWytRShGgp2gu5C6BCSJ0CIQDE6YAV vKH2xayaPtml3X1TP+BmNORDjILENbGqBPaJ9Q== -----END CERTIFICATE----- oscrypto-1.3.0/tests/fixtures/keys/test-ec.key000066400000000000000000000010541421476274700214570ustar00rootroot00000000000000-----BEGIN EC PRIVATE KEY----- MIIBaAIBAQQg6OWPI5AB6xIvibu6NwIv2Myye+QfQp4+U/mJ0GmFQ12ggfowgfcC AQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAAAAAAAAAAAAAA//////////////// MFsEIP////8AAAABAAAAAAAAAAAAAAAA///////////////8BCBaxjXYqjqT57Pr vVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSdNgiG5wSTamZ44ROdJreBn36QBEEE axfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP40Li/hp/m47n60p8D54W K84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8 YyVRAgEBoUQDQgAEi11McffWxqNJY0JcR5/LcyQdyd3RLfE6n7cE3iDQWACTVPaJ xy+HK/f5PTs07Z57Dj1XQt94AwvMMcYD159gAQ== -----END EC PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-inter-der.crt000066400000000000000000000020101421476274700227520ustar00rootroot000000000000000‚0‚ì Cy0  *†H†÷  01 0 UUS10U Massachusetts10UNewbury10U Codex Non Sufficit LC10U Testing10U Will Bond10 *†H†÷  will@codexns.io0 150509004139Z 250506004139Z0˜1 0 UUS10U Massachusetts10U Codex Non Sufficit LC10U Testing Intermediate10U Will Bond10 *†H†÷  will@codexns.io0‚"0  *†H†÷ ‚0‚ ‚¿=S¢ j{h*]´u`y4\c%~³è{Ìj“×SŸˆÙœŠ±1¼y:ëßÃ&eMmÉË÷{wÝÖþR»D"ÜN’QEloúN9ª°æó´ä&¡—ï5ìýžÃÕytÿ`‡«Ï<öâþEÒ§“Ø:|”-1ÆÛtèïîÿüeâHŠ9« O·è[¨“o>ä6ô`wPÒü .Û‹(oóÜVå $û7ëþ6¬NcĤÀbH½²A“û7,þ¼3T–ˆÊk^ýäbp!E`\aù P‡,Eì<,Ÿ×")çwø¯ÈäL”…\óp‡Î+ÊõàOˆík™£P0N0UÒ ý.%Ñ·!×P~»¤}¿4ïR^0U#0€¾B…=Ìÿãù(~XV´ý\êK0 U0ÿ0  *†H†÷  ‚ ºaÄ:3ކöª†í;”}“p küü³é*ñIÉ ö@ƒÜµ¿È:ë\QØ}^/ȲˆæŒ¡zœ2K°¾p•K0¿™I‚®, `çå*yë&ŬÂArò– Ù ½‘[ª÷ÿÈ÷d-VãŽ"%–{" Æ7C ÅEVº¶‡•§ éòI'å䈤·ý]VÄ9šÿ$±?à4”LŒIïåjÛ>%ΜË( “_!øšŒ`:M;º|™£¶xÏ—SJ½r_­o…2ÿ¹¶ý)&»£®þò ù‹âH,LJ8;ô,Лzuñ˜f,ÃëGö`±(ÏÇyboµÂoscrypto-1.3.0/tests/fixtures/keys/test-inter.crt000066400000000000000000000026541421476274700222200ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIEBDCCAuygAwIBAgIDGEN5MA0GCSqGSIb3DQEBCwUAMIGdMQswCQYDVQQGEwJV UzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czEQMA4GA1UEBxMHTmV3YnVyeTEeMBwG A1UEChMVQ29kZXggTm9uIFN1ZmZpY2l0IExDMRAwDgYDVQQLEwdUZXN0aW5nMRIw EAYDVQQDEwlXaWxsIEJvbmQxHjAcBgkqhkiG9w0BCQEWD3dpbGxAY29kZXhucy5p bzAeFw0xNTA1MDkwMDQxMzlaFw0yNTA1MDYwMDQxMzlaMIGYMQswCQYDVQQGEwJV UzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czEeMBwGA1UEChMVQ29kZXggTm9uIFN1 ZmZpY2l0IExDMR0wGwYDVQQLExRUZXN0aW5nIEludGVybWVkaWF0ZTESMBAGA1UE AxMJV2lsbCBCb25kMR4wHAYJKoZIhvcNAQkBFg93aWxsQGNvZGV4bnMuaW8wggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/PVOdogtqe2gqXZ20dWB5NFxj JX6z6A57zGqT11OfiNmckIqxMRK8eToR69/DJmVNbQTJy/cFexZ33db+UrtEItxO klFFbG/6TjmqsOYF87TkJqGX7zXs/Z7D1Xl0/2CNh6vPPPbi/o1FHtKnk4HYOnyU LRMxxtt06O/u//wdnWXiSAOKOasLT7eP6Fuok28+BeQ29GB3UNL8oC7biyhv89yN VhDlHAkk+zfrGP4dNqxOY8SkwI0GYki9skEckwf7Nyz+vA8zVJaIymte/eRicCFF YFxh+QtQhyxF7DwsB5/XIgEWKRXnd/ivyORMlIVc83CHCM4ryvXgG0+I7WuZAgMB AAGjUDBOMB0GA1UdDgQWBBTSCv0uJdG3IddQfrukfb8071JeAjAfBgNVHSMEGDAW gBS+QoU9zP/j+SgCj35YVrT9A1zqSzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB CwUAA4IBAQAKnbphxDozjob2qobtO5QRfZNwIANr/Pyz6SoD8UnJDfZAg9y1jb/I OutcURjYfV4vyLKI5oyhepyBMkuwvnB/lRpLERAwv5lJgq4sIGDn5Sp56yYcxazC QXLyG5YJ2Q29kVuq9//IE/dkLVaP444iJZZ7IgrGN4EFQwvFRVa6toeVpwzp8kkn 5eQaiKQYt/1dVsQ5mv8ksT/gNJRMD4xJ7xvlats+Jc6cyygJk18h+JqMYDoFTTu6 fJmjtnjPFg+XU0q9GnJfrW+FjxYGMv+5tv0pJrujEq7+8gz5A4viSCzHHoc4O/Qs 0Jt6dQ/xmGYswxjrR/ZgsSjPx3lib7XC -----END CERTIFICATE----- oscrypto-1.3.0/tests/fixtures/keys/test-inter.csr000066400000000000000000000021061421476274700222070ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIC8DCCAdgCAQAwgaoxCzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1NYXNzYWNodXNl dHRzMRAwDgYDVQQHEwdOZXdidXJ5MR4wHAYDVQQKExVDb2RleCBOb24gU3VmZmlj aXQgTEMxHTAbBgNVBAsTFFRlc3RpbmcgSW50ZXJtZWRpYXRlMRIwEAYDVQQDEwlX aWxsIEJvbmQxHjAcBgkqhkiG9w0BCQEWD3dpbGxAY29kZXhucy5pbzCCASIwDQYJ KoZIhvcNAQEBBQADggEPADCCAQoCggEBAL89U52iC2p7aCpdnbR1YHk0XGMlfrPo DnvMapPXU5+I2ZyQirExErx5OhHr38MmZU1tBMnL9wV7Fnfd1v5Su0Qi3E6SUUVs b/pOOaqw5gXztOQmoZfvNez9nsPVeXT/YI2Hq8889uL+jUUe0qeTgdg6fJQtEzHG 23To7+7//B2dZeJIA4o5qwtPt4/oW6iTbz4F5Db0YHdQ0vygLtuLKG/z3I1WEOUc CST7N+sY/h02rE5jxKTAjQZiSL2yQRyTB/s3LP68DzNUlojKa1795GJwIUVgXGH5 C1CHLEXsPCwHn9ciARYpFed3+K/I5EyUhVzzcIcIzivK9eAbT4jta5kCAwEAAaAA MA0GCSqGSIb3DQEBCwUAA4IBAQBMlRtzNp//q2kFXLYaQbWv6q87Z32HMp6Jq814 TP0lz2CYZQ2KXtk+AHc+cKU8XHvVRFfydd15xikWM8z4Sn4LgXLD9UvKswFeTnQV gHkFC41Cp5H5K5shB0dNtIoKh/Lip7EIH2EyH/6ocgqrymqd2VoO3nIE5ysyzGrW +BVaZtmXVbyWDhQK10TmPe4GJN22VPzkvlPlqDRQC70IfJoFkZAdd085FT+sq7ny X7D73jBfYgfd9Z9h0t+vKN5NWWN5OI3IZADkMOYhest7RgZTFX2NBqHbigCJaUk8 f/KDImyMWk+vaDGrkUoOfKKXvdRSUL1mQDgt/FmPq9EdZt4j -----END CERTIFICATE REQUEST----- oscrypto-1.3.0/tests/fixtures/keys/test-inter.key000066400000000000000000000032131421476274700222100ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAvz1TnaILantoKl2dtHVgeTRcYyV+s+gOe8xqk9dTn4jZnJCK sTESvHk6EevfwyZlTW0Eycv3BXsWd93W/lK7RCLcTpJRRWxv+k45qrDmBfO05Cah l+817P2ew9V5dP9gjYerzzz24v6NRR7Sp5OB2Dp8lC0TMcbbdOjv7v/8HZ1l4kgD ijmrC0+3j+hbqJNvPgXkNvRgd1DS/KAu24sob/PcjVYQ5RwJJPs36xj+HTasTmPE pMCNBmJIvbJBHJMH+zcs/rwPM1SWiMprXv3kYnAhRWBcYfkLUIcsRew8LAef1yIB FikV53f4r8jkTJSFXPNwhwjOK8r14BtPiO1rmQIDAQABAoIBAFkm5cwetuO1FCJr 8hEmhwr59ffiiuajz9y2txik0T8WNRxf5cgk8G8ctI2WQIJha+hkYk8Q3t+XOio3 KPr25oYlGj8mxuxuWOV/gjKotRMhQbr6p2qFAcaxlUoGQtDgHMrd7KJ38UBxGrqW W+3zPhznZxxMxx31bmlaDkCE5auH3WRBVj5tVd7L75It0BStFG/smzF45S+7/7nS k3hWgWLvdZNYrE46LShyZ0r/RktrgxHMRZV3fMEnMt1gqpHCyFXdt3jb2hs0ku1X OKFcCBUd918/oHWW/jXsMsemM0aORskJRHT6ZA4sT4fnP6qYEydY2+2Gh8KxLOur V2uZ14ECgYEA9u27PfPCW/XWNtmmiWTw0DqJ/V/HeqpXs01AEci1GxkG9Ej0PdI5 V+QPi9s8Kn/AVnMo0zkizKiExu2bHqTXKxtif76qZjL5AkIeAq4asIzOn5Pd6GBN lo0IT6HMLBklM1v/WIky/oExQ49fnLM583oWpuUUEX0Bg/Rze+HFic0CgYEAxkPc YuqyZXA49qOkz363H74YiEXQIocMKm27CIl37EezqBfArfJR8HGoTk3hX8JQvgxw LDGoMKRZ4xtW0RGqFNd3yn0bweDuISn3Gdrg2B+VvPlgq7wG240trcCsVsEGG5Pe xlP+IHG8XsChhjFSAiv2TMNij2zRfgNQ7QAZLP0CgYAQRO2XUV427OWmS4TbfJkZ PyIkXZEpFdwdZjOYeF7mYCnDIqNlB1rb3ZzgnBtEuZIUoBXKhPM0BM5qxPD5febn YjCFNizH+wfJDE4p4wrDIyPiA74VN3MlPjmT9ZccOLPoxGD4lmGTvzNdvi6QhoaE 8kBX0rQVyuS0qwCI4wE+6QKBgDP0mI47XqCCdgr7+zZ8yFRDJn6ffNlvdlqwArFM BOcgKw5DmzR6KcrOngJXGlZTv4DuFX8fTCnjB8Mh6PBEpsNra3LesiVuYjjCnIRj /UPlkwHAf/rA/TMPngbIVOEvGACmEVz7qb6qz2gtJqbZydnT12lqUiuvon5ZNB3H RtGpAoGAFaYxkr2+v3Bax3dJrpHb1NgY42vKajo2Tb9hHm65hBdCm0G2toOGDpui wq9k2Ed17kW1NPetGR1H5T2uto/gsz0Y9I6y5M5jYn9OWm4ZjOlj4Lr+VHlX9Q6C kUq8NRho40913BcYjR9LhKVP7rsBLhvtUGaet/IsP1AgIqX+Tjc= -----END RSA PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-pkcs8-aes128-der.key000066400000000000000000000024431421476274700236740ustar00rootroot000000000000000‚0I *†H†÷  0<0 *†H†÷  06$‹Ö(†Ò0 `†HeR(׃7Nº´ 7üG‘©‚ÐÏ£QK†ÙNÈ‚5¡rk‚*úÝŠ[‡Ú×¾Bm’Ê{Šã4ËÿÖꎉfA±¤!2Ò)ӽР°'."“1£‰ÿ8z—kÖ^èã¦d§âvNå¡nžèžä©õrhe±jm§o%¡¬ ª½[_tãÞtúö øa;nÉöèè¾ts«¸`5%ñõ=ÑÉ´' ëÖÐ(|Ï{ká“s±û3f‘qãÜþéÿšçCl2ZÖ '^¸7 l{Y¬_»VñUúº¬ 6¼Yv£wÉ]´sE³a1DA ì"žyðþÿrf/åʶðß¶µÕÿÆGŠ;´(ඃê%¾§ü›ÒLÒ]upõ¹‰’ßL1“ ½ZRZ7ËÖvF„‚ž§iêbóTr—ÓŸ)uÅ=ôÝû^ö:®Ë„P¨–íÆy­~NÂ<3 UØŽ!糫;Ž›ý¢<)1è—S/ÂÂÌj½Ol#tS2‰3k$(é—z£nÈ ñÛ‰ib,'æ$¼à떶ѦkwˆÞµ-‡mÂUøI›j@%ÅsïĘU{Æ®4¾šÀù ›ÇjÿÝ8`héÜLÍé^“±0û]•í®eb©ÂÚÉè–\cúØèÑ×(²Ö&rË:â´ÝèuCÄdmµ†Ô/MøÙ:ÚˆYä°mÒ¬h&¦†V¶bù{ˆ ؉·µÝl ]˜Žõï;²㨤üËNßµ¶%gæ)ÊMÌÉ?7Îãz9NBµ‰Nfé(¬ÂÛx·Lö"+=OÛ)šÜ7†4orâ"® u›9ÝX\ŽaÍ‚IEÈó°ËÙ[NScÕÛê©àߌÁ®KÁÊ `íwf˜9ˆ7ùpÌ«oUdb ’xl¿Z¯xÞŸÖâóÄ•N"$¨!¸‹ƒJ‡RNS¦(†$-ÿ•°åÿnù 3n§F;Ubß·-a½VÓÚ¨ï7.”®…MdêÌXìQèÜåßתÖNzÕܳܳú¨nQ¸•Up¯Ñ®O¦¸s÷Ê’ÅrH«ë˜×+Ðð1»qv«]póˆ€ú!g$ûx³ÊMÞj1{è{¿k A õßÎ?…~Àé.ç)ƒE¤1”ã“•_ïöþ ×ÿ 9º«í…o[y—òöF4Z ¾rl\sù!I²´á=ÍDüþ¥.Œµýðæ+¿Åd­„tÁzÚeÃa¡ÚkY¬÷¾éy¬Äãj¤¶pðËô÷ÁC£áðHÛÓ$sB\à5àë­Ýj¿JA†Á†f[Q©Ò@H ™ëÎoX¼ªóyA}kêì;£ÇÕ9tãUƒ~¨oscrypto-1.3.0/tests/fixtures/keys/test-pkcs8-aes256.key000066400000000000000000000035021421476274700231230ustar00rootroot00000000000000-----BEGIN ENCRYPTED PRIVATE KEY----- MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQI2WI31tR33JUCAggA MB0GCWCGSAFlAwQBKgQQz9HGmQ20dNiIH9WFukhAQwSCBNCa49F9s7/Vno5q1Ly1 gnbzrJfvOgSHy1P93MB1HeQq330iDr13fu8cGEMP6ngUBv8lFMClXBw8HB1eC+8f HT8X2CfGrorwDXpIJCI6j2rEmTc125np9VcYegQMQRFjKetFFO9zmfk+qIKl4hhg 98QYdx1DYr28En0vHbwqHoMkagiSLfR6/N+n4M237nUdjOuuB0o3+vX1OtYOiK0h WMS/IQqJ999pwsvPpwDV/LhtaIFdArRIs2+W+Cy1RwcGgFUxpbl/PnLFSFfIdYqF gC7g6pLtK3mRJq6uOHsPZjuEuhO6dgnDusvIkVKb4IsNVWld0VHcTyI5HKe+i/tW E/sQBR/zYzzd701t5DQcW/EXePEWiwY2vlyYHoGOEFFg0xgWc2w10GpBSir6WcV9 yjElu04Dic7gjoPs7oH9RmoRKzMnJ9KMnSPGnQ3GDlv9imROehe1iqnLNN1bpTy6 lHNVDDyOba+rRgra30iqeriZOmk4vBGnqA13c9tdWk3lLXrrYgst53xO7MIv6QMB 5gp/fCKcIAezMre4WfXJ/z6OxY+zaHo2B2oxXULtDTW4OfwmcuXPCj+NTtYTGlnC 4i2Q+BXiD+NQl4w6UrAipn5TlIWYXvxKvFKvcSONyEho+eEekiTBOxlNn9rxb9IB 3x2dVVd2gKCXkUlXBFAYIx0Q7NYmjX4jordXRuXRo8HDjtAx6wHvLNzvaSffMoYq D5eNtcSzuRm1xmErvDLXaxL5cs9mbKSDq86bQgm1xYd8QOqw4WYUh+zKuszK5XEw kTtc3CP5TTF0nkiEbQkwBW9Kaus2TjnhRrgVRtRBa21DVfI807+SlSdcpElWW8jS Zy+FJxWQNs8VOLcUMT5caxVJAHTEtlfP45smIF/jCvGLvHtaOJt0XIHWnY+uE/Lz GWXlzyKvwpq1wUu8bNaKCORzwKDLPsOh9qcfc65hYywC1l3OsndlKwMTvb4gfRIp r+gQ88NxP/9d/PICPBiJdCkPHe0EK4d0mmUIShP1OTAd7c9n+9rAB1KM2lCFlowP w6iPD8Swmjexd1NK/UTJysj3nsSgZKHiHH9AdPwDo/hTm5aO1ivlbXD4n35bUvQa Rabh1lSY/O/hda1M8IQNg9t59nEsqQBEz3v4ODx0fMUSrmeSy1UkoUOts3nGK0N/ ObpRBzF8fYqYOSfGG6zNt7ZXGhk9WmvURR4n34kjT+7cjGFC2DMbX8JuFmqfqd+F Ks9P1NfsUM1z12CH14X8PPWVFiwZw8b4QU8wyzZdYURym9NZ5ZZdcy06dI+g/yuN 2tr58XEEG2lXgEUvZWRR/KlQ3NOckxY3jSAXPyL2UpwEWvbRbGzt75JP91eWXayB 0gGcJOd9nXegxoR3OAFALEH8yRuDapuqm0PCb1FAG4lYqUjWcXsu6ivWN3g7PCQy LeubZBMTvf2B9nMeVeRXaBjXLguiithEz+vyaOE8T2hF4I7+ML4zc4EBYRRPK2nC MtjVkOo83Q46aOwOcXqLuf/4IOQYv9s66b8OY3YI9STtRm09mUoKtFazDwvx/bmK UuVMO9X1YBF8CVNHC0v9IkOmZaedAdqBZOFqsvvsaSlpoBO/Uc+OS2DkspEbvEze E/A4z5WSiMa8XHYnvgYebBkhew== -----END ENCRYPTED PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-pkcs8-blank-der.key000066400000000000000000000023551421476274700237620ustar00rootroot000000000000000‚é0 *†H†÷ 0ìÒ ¹@–f‚È:ž™%”#þo>“¥é«ÑoGε´bQ礃Š`SßrESzˆ¾æFÈ´$Ÿ¹¤œ±ïS擈-à,cÎ=RÑ6¡¶ ªúL |o¸íCý×ÓÞ%âÏuÙÙ7}5¦äŒðÞi™/-ÓøàÁÕ qÕôã’Í{ñïCþ{¨»QÕàÂg#¼%rïhaƒ©ÇkXŽVuÏÿ\cêÖ›r—Å—ÁºX^Î)æ Œ5ä\HÄQ‰vŽü‰"(ƒN.hÕ—/Xâî<­‰1\•˜ûÅÊ a;ºÇ¢D4NBû ¿JêUéLð>ÊwœqáS:¥#•R’¥‘@̶#9l‚— ßç³¼qÅ%/3ÆS)^w XcyNh·áEmóù+ëøjSG¹Ã ÷+O3;¦?Í'ª,ÿ“ëó÷Æ>b1U òw¦7'ØÌ̈×ÛWÊPÚkQMbzßÍwNÜcmã^; B`>1>³aóŒ*µýj{“WÆÕgÛökêU²XŒ0D4ti\ö„ß¾Hz7˜Aåxô”Y`zºÇ9y_§“ÉdOp¼LI˜ÞÓšL[Žðë’å¢IöH.Âà#Èä­l‰w;D`nT¿|oñ7â_îNS†©,;gtÝ»” tNˆd›j¬Ôô%,jü˜ý‘V[V–Íf„@&o¬—z¿C­½QÜ­ÜFöók2z#ÐÎEwJÀì4¬Í¢ÎúiÆ9­ölOMd¶{“,èUî¥B ôë¡· Å:>ý…¥m Œ*‹SƒÄ&…2q‡Áþ§E¾®í¥zUÉÓ42$ Ý1œß'PÊ¥ñòÃ»Û o ~~ ºÄLå,¯š,y Si¢üí¦GŽ@cPE·jVº‹<‡ g ”žL‘:W£¶â9srÙúè—o-gÆvc)ëu “óôfVtœóy–ì0ôÆ,a„4‘aPg,7 NóÆ$ VçØ÷4¨1ŽðušLþI“ƒ¢Q‚„´€ô¹RIè ¾àn¸ß–VÜŒã×Í‘ê¶óætKNoáBŶœ›‹]@òIûÒiÒJ’Iãy¶Ý„XœÌk³ˆ6Œ"%š¥÷oBtîåçé- …8ta*bAk (ß•åVNnE¶Ëô¬Èg‘mV~0Ôã¤ü?Ìt©wkÐë}ÂÔ:î\µTp6åºBIÝÔˆY8u^ô–‡}Æ éþ5ìh@Åæ›#Ö¤L¡¢.ŒmK>ǵ&NŽkõRSK¨~µ:¹Øhˆî¿ýoscrypto-1.3.0/tests/fixtures/keys/test-pkcs8-blank.key000066400000000000000000000033711421476274700232110ustar00rootroot00000000000000-----BEGIN ENCRYPTED PRIVATE KEY----- MIIE6TAbBgkqhkiG9w0BBQMwDgQIsSetDKkFJYICAggABIIEyOOe9inXgJ/uBVQl 0qKB7VA8DWNcpVvD6p9HzNloffwSn4TtzqIC05lV71ixDoP1kbRALw2pckbO0vsH dxTQb/YvFEXyIsZI9v9rMwr/OA80CCmXMuHZOxeYcinF9N9x1BxR2hc75wSxlxXd C8C3lfVTgyZLOl9A8mtGVnCs5wAqrNdj9H1JyglAoF81VcTgFroxLrraaEGRvhyb XBV3PxW1WQ0jLB1C7SllmeOXaQVkAlgf+oUtX20nwja3V5H7XfkO3PLf/mqZINFT Nu1LqjkX6/KVXQOFoYkDJOscEGitSrns+YzTKcu/ZlEZqXTJ686F7WT1t6CLFpC3 bAVYCejzv/FHL08WUZ1DODWK18cQTbKUAfMAPcgQ/i+HWlq7r3bPy8AlC7O1zeCN 3WN3Vd4Z5XeTpVGxj5kLqXg3HFgHnlvWd19kSZ4iYJ6QJ7e4UxGSybDfQ/vKveMy uD5o3kNep8JJ/AR4jrRgh9QLMHGXR9dGquK7X7AB8ubibnXkPMLsfprznGrh4r1F krgJbffEon1dNrpENL4lTpgWBq8UX0P9OYrli+Gh979s5WdcaKxgzRg0cFu29+eA 4efIZHodrgz0tClmwfBSj+JPvMLTRjksWoWB2oBr63RuarnDOmeNtgVWyNJU3pfO vRZ4vzUO8/3OIZX8BK0+mMXeHpKRBYW+MBlxlDczQwyOqSIfV5zsa+J9veRyJvBp a5fc9YTwotTS1YwHfgxdg9SPsju4AmfZMXPNFf9SMYmZGr+kLM6mOuBI8rwmxP7o mJ4ceuWxJsVHDzrcAv3ZbC/o+nIuEjvLy/n5Qi1fbNLjg0rEeWsnvqRYoP062u67 RH3Lhi6GgVBMvPDGV2CCA2A+B6xNI/RmKjDXTGpVo2emDBzroPKNoQCluYZkeDYg JPa70kuaKQaKCEx1JiDSfNpGxpa9MjRTQ2yuRr8VI4qHR5cCeouX0ykNgsH2iZxN zVXtSHXBs+lk+uz+5+Q4RxITPSeges+QlwvAT9cxOEAT6vRnru2FyjDoUaF/8Ppp afJEx1xk0sv49AdzGMu3IOc5S+aS1gVMOrVx40xo/rQ44N9xuQHCp3eNAN7OXBv3 CJgM15CJtE/VJLPunxOnnKeXud9hRfpPXkbKZ6nRnw8Qg094+VOJaptxmM+QvRtx HZr81AjSxK8PGcgY+RAGXy4LSuNLwGB04n7dArZ8b+6i9VA9kduhnvXg3dZpTV5b dIXHEfhVo0zI/gKCDgw+kXTFoC2iX1aun98b69T88+HTPceVEC/TG/uUWklphHpW hyWMQdnH7UAF2lxnoj0N2QdkPk9zM13UdSO8dgtr+it/FlcBKIhNgt0kqOpOSImV MXw+bGMWSyuBBJwo69QbJ8/WXalF61Z3hphffTcz4tn0Iv68I3jCDB5oNL9okQ/Z GbptFdMhP+DnvG8/vmC6neJ7TYa6//jOLysOTg7uuP/pMu9Ftxc0xeLkSxv/Q+hQ g3Wnak+A2bJ/cLaVXE4pgfWx0MW/QVMsc8OJXDcetcPOcVwub2JLPSNWkEFu33Op 4WbOSKAvZ9qElTlby6Z+6EUP/2uccHkwMawx7HRVhSwA4vbDA6wxQBVkEOeEShy0 7OJqMonBEYYaqBs49g== -----END ENCRYPTED PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-pkcs8-der.key000066400000000000000000000023021421476274700226650ustar00rootroot000000000000000‚¾0  *†H†÷ ‚¨0‚¤‚½[(tâæJ,*½PÞxªìP6~”¹ÆEœ¨vÚ¯;Ã×ÿÄùB*ɯ}6žë#$\]Ž-ÚT4F?>Ylj<¾“kØ›K{™#ÿneFÍ:¡ûÃeCWRÝá,ˆœzîKT#ãå¿Ùú`b®v` ‘ ¶QÃÍì껩 SAÖVËéO7+§Áp½&…é# Z~QA’„÷H×~äÆìøt›€À}™ùŸšÿb›æ(@ä>F–Ö`-ò§¥á¿’P!òß/M'ïBäÞË Æž쬦(r µÖÉCiqW±®±%c¾¯™4žÄ‚”yÂõñ2 Ô’„Jññ”Ÿ‹Æk6®¶"½È×g€Ó¾–©IÒx¥\–Ÿèªîë£l(¶"Ã7†öŠ8è,çÑŽ“»h›pI!ï”»7e)D9÷ÌDEÌê–óÕmü½øŒgë·Ýa HÊþÕé?…§¸ßàø¾wA_PϺ–QªlSÑ –\èB"¹ñàÆÌ½Àgç–!þÉùÑ=¥ߘµ†ÕÛÕ8æuôu._NUœ'©@Q mHUZ,pԬ฾yO¨‘Î'.ٛϒ,H!®M´œÕ«¼¬À:ò>ìsÓF4’—¤ýljƒÍªW¯hLgÙ­È©'³…e/1¦×EŠ¿´w6†wd >ÔY4-=8â+s.´U¼—\´qÅã°Gæ•Ú@áÇ>°™WêxB ZšÀX7šzÕ¹1$ìöí÷s1/€8 y|<©ª–@ ¬_¥ö~tÌô¢?Ý¡4Ø•³«úÁKº#‚.L¾´þ­êVO©ölÆörŽ{ÿ3ÙpqüÀ×jcŒâ¨€ªÚüõC:P5QÖÄ'R,ëìöDïùp£ v_ˆRn  ¯¤ ¢R_€$º-¡#œ[ lÉÅ&°qW´ä/ü7”Ô(ûNRй„¢t®³B¡&þÛ‘"7|wD‘CV¹[}öþ Â‡J8¥ycû4°“áÝýœ›…«á;8¨—<ÆM{¦O9?Q+k†A+R¹FQæN!X5{Þqz³y;ììÂ2Âmµå¿‚½@oscrypto-1.3.0/tests/fixtures/keys/test-pkcs8-des.key000066400000000000000000000033711421476274700226750ustar00rootroot00000000000000-----BEGIN ENCRYPTED PRIVATE KEY----- MIIE6TAbBgkqhkiG9w0BBQMwDgQIPIgi2uA9hbkCAggABIIEyNFrhiP9YxjTgadK ybooxFqa+Kq3TCwQRH3suoWdJZbaF3MKa86ghqXj7/tBzmqCmy5metQXHXnwQFEY 9noEcdauYjPQVstwPhsNZpcbR72PkndR8sGgqMtVtC2GPw2sQ3vMMZNbmqD/4zI1 GY0tIt+hEprZJEDDN7xDvDxnAmN0sxbnBMN8DttgX9Awyur59qW+NlJw+ZJF7tto zglefFLI4f6MrpksfVrV8JDhrAkj3LEQeJ79cqp4cg3l7zbPjg+A+ImZEYliDB8U HH3Z/h2uHhmVJeVGJokspWIQLgcLpPLZ7JdrndVyGhVpMt48N3pKOHrUjJLAu/jN PStvOaSlb7iJJsdv0WVoQf+W2sW/uBVCZwdf3BHNWeXvXSsqJGNMfzhzSuHpRv4s qpVlTnY6howfU0qZ+x/Kt9tSeOa+wbOdyp13aVAIhAtxZBfl/QRtYL70I6K9+kSs CXfSyQOYMhqv5fw7h/UZge+M1KF1gGhy8GElw8ADg9FJ/Rc3Yl4kAzvBLcRvXo1K QMR5rLw2h/10WC9JFZ46GEY8BmoZFML2hX3ZCD7nXc0u+Q1jPFH4rdOLwuWHg4ry WpaVgq+f+nBtxgNQTU+GoxR1g1tzUaXR/fSEXwzg/w7aU5k+iq0i/UHb/xCW5sgK tHmqqKvfSmhOuaswVihXpcPY8CsfCwMnm7myIiMxRIResziAB0yEHG8WLqtiwJc8 FRzJanKNdjXpD49Lk7KmncdhseupEIJXDZ6VrqxR7T8jDOAjOOUJsqoFvVFOGIBu Yuf0oyaETdJJzL7Sf+rOBc8q9lLLh7q8i8dNz04kYH0PoX9gBmLcbSYOIf2ID/sc skbLdEiZQQis/zkmkE6fzRhhBBWpqoitJ8G81QlPLWZYB51jL955G2SnQKxgWdk9 JduoF7dvKvIUs2e0led1wSWP/ShRn39V67gDp8geH6TWFUYTkZXWlfSYK/uGfiO0 bqv5BzyFZOaAbKBPlytyBhDkQw8pLEraztJKXrtwSj2wawFryb3w97/2lrVvdeJJ Jln1o8jBYhJnS/MgTh8DzzgaDOuwqWxNRASYfyqCIrddrgv8fUASvzThtqhu0w1U 7kJ9cjLV5a5v08Hd5txO3lWDQyiMx3cGyq7tZxDFJHpIIbuqf4VhV314UGLNnz64 Xex5x5LNhACCmqwc6uBrwWofq1hfNfHDeddvxMeX++klM+O44UJz9oApQwM+vIye BYwKfvX/fbesrh3SbKrk2MgeSwZ50jI2RSmSZlxyph2rkoYrv2lndaKkzZB3w37q XpicoDRpuDGOx6iQrH2daHcfoJOCsWe5nR/InX/V9MDwSs5oaGfdqQ7R2ocUNs4t ++JGdnkKAOfI/ElywnyWS1g0DnsUuGC7C35Xwkyimoi/due/jUYVFURw+tBa1oC1 C02+pPo75hvEF0zEPAZcUREAsalfLpFOj/N4Z3q1Uwi25YEI04q+ESSBlSSrcJEU VKeWyYi/5wyi8cOWUwgXh3NfuhJgHmtnhekTXkYWOoqpTv5FRxRlWxifNmRYYUMv EpzlHqj23Tr6V6dKjMr+Mi79LhI/9CQxkh/XsRRfURD+em/tuuiVzJZ7xzc2/mXa Rl+yyu7/lew7PS6rnQ== -----END ENCRYPTED PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-pkcs8-dsa-der.key000066400000000000000000000015511421476274700234370ustar00rootroot000000000000000‚e0‚9*†HÎ80‚,‚ÆÏM$”³ü *“mzÎO¥u†©¡ò‚üÇq¿”¯Uª¦¾Ô)ÿ“ÆŽ‚­o/&þÍÃ'f5ÿŸK‰˜QñOAŽïygÆø.1˜ÖRŒ[VwbW'Û2Œ¸µ"1[ƒk·]‰Bðᕊ'>3Xª5²jÁÈý¢·É#%VVõƒ màПŠÀ´$‡`ÍeÜçÏN3»#;sQ¬øA# ]9X¼YôL¤n£ØpøKù-³˺Ä+€JæÜ1ÑpLíRÌq&ŠT‘§åüòµÏ#XËZج5ƒŠŠ…ÀзýTúwïJ ¼„ñõOŽ»¿• R,Ë‚Rx`ž`u?îÔ&zÅ ÿ»$™c9Œ¾Šñ©Å*ÈîkqC'¼#Ãý²Ó›‡¯lÒˆ+}#! þ9rrÅ6óŒ½ØÃ¸9nšÑÜÚ›kÆ«sš˜ÍfX­¾{Ârä°îݼÚÜ¿¦‚jGи,¢ªcà é a)B4ž×COäR±Ó>¶Æ _¿ I!žEIó¡R)¡B¸Aú¨)•pQÄZø' WPi]»‚€!}°å,T&‘¹#+$¯Ã^ºšsÈõŒ®µñ¹@?]²Øižž./µî›„†×'=dÍvQoœfmF™ÿš‘Ü%@ «\çóh¬¾î¢Ôã+­Uí.OY癥õˆ–¸£\¯H¡þì®æ B(P‡õÝpº$ËfÎãÞa½ ýFÃ~ª.ãÜ¿b%„i@»C6+(Fᤠ<>q³_¦àó›^(Ú3¡µûä´ “Ìê¼n1ón¡9?À PòÝÖßuÈKê$LAämŽ„ÏŽ²`ÐÇø‹Ij¹me¥€ü7¦S* @*•+]Æ©“ì Ÿ#÷±:Òš$/=fÒ½â-ø8ñõ84ïÕ)/Ö €ˆ¸¿W D\ˆÞ.Í\ªa‹ÌÁjT°Ôl@Â}%þV}F(- Ô9ŸÕËñ^È!®w?ÎVûè]·åúT_¥¦IPŸsx: kõø$“&]ÊÃF‚Dÿ¬c•wàqÒAk8_ˆº4]j#!• ùÍ`áM£îÖÇbÔŠ&a|0Ÿ‡?"ÑeÔ¾·ÕJ oscrypto-1.3.0/tests/fixtures/keys/test-pkcs8-dsa.key000066400000000000000000000023251421476274700226670ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIDZQIBADCCAzkGByqGSM44BAEwggMsAoIBgQDGz00klLP8CyoVk216zk+ldYap ofKC/Mdxv5SvVQKqpr7UEhCPECn/kwLGjoKtby8m/s3DJ2Y1/59LiZhR8RZPFUGO 73lnxvguMRaY1lKMW1Z3Ylcn2wYyjA+4tSIxW4Nrt12JQvDhlYonPjNYqjWyasHI /aK3ySMlVlb1gw1t4NCfisC0JIdgzWXc589OM7sWIztzEFGs+EEjCV0BOVi8WfRM jaRuo9hw+Ev5LbMTy7rEK4BK5hPcMdFwTO1SFMxxJopUkafl/PK1zyNYy1rYrDWD ioqPhcDQt/1U+nfvGUoKvBiEHfH1T467v5UMUizLglJ4EWCeYHU/7tQmep3FCf8b uySZYzmMvgeK8anFKsjukGtxQwcnvCPD/bLTm4evbNKIK30jIRcAICD+OXJyAsU2 84y92MO4OW6a0dzam2vGqw9zmpjNZlitvnvCcuSw7t282ty/poJqRxLQjbgsoqpj wwYOCemgYSlCNJ7XQ0/kUrHTPrbGCl8Yvx0LCkkCIQCeRUmNf/OhUimhQrhBgfqo HymVcB9RxFr4JwpXUGlduwKCAYAhjX0YsOUsVCaRuSMrJBcdr8NeuhIZmnPIGPWM rrXxuUA/XbLYG2meHp4uL7Xumx0ahAKG1yc9ZM12UW+cZm1Gmf+agZHcJUAMq1x/ w6fzaKy+7qLU4yutVe0uBE9Z55ml9YiWuKN/XK9IA6H+7K4C5gtCKFCH9d1wuiTL EWbOB+PeYb0N/UbDfqou49y/YiWEG2lAuwYGQzYrKEbhFqQJEzw+cbMbX6bg85te ACjaM6G1CPvktBIJkwjM6rxuGzGN8wFuoTk/wCBQ8t3W33XIS+okTEHkbY6Ez46y YNDH+ItJarltZaWA/I03plMqjwlAKpUrXcapk+wKnyP3sTrSmiQvPWYa0r3iLfg4 8fU4NO/VKS/WDYCIuL+dVwcMRFyI3i4GzVwQqmGLzMFqVAKw1GxAwn0l/lZ9GkYo Ay2g1DmfGNXL8V7IIRqudz/OVvvoXbfl+lRfpaZJUJ9zeDoLa/X4JAKTJl3Kw0aC j0T/rGOVd+Bx0kFrOI9fiLo0XWoEIwIhAJUN+c1g4U0Po+7Wx2LUiiZhEHwwn4c/ ItFl1L631UoM -----END PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-pkcs8-ec-der.key000066400000000000000000000005751421476274700232640ustar00rootroot000000000000000‚y0‚*†HÎ=0÷0,*†HÎ=!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ0[ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü ZÆ5ت:“ç³ë½Uv˜†¼e°ÌS°ö;Î<>'Ò`KÄ6†ç“jfxá&·Ÿ~AkÑòá,BGø¼æåc¤@òw}-ë3 ô¡9Eؘ–OãBâþ›ŽçëJ|ž+Î3Wk1^Î˶@h7¿Qõ!ÿÿÿÿÿÿÿÿÿÿÿÿ¼æú­§ž„ó¹ÊÂüc%Qm0k èå#ë/‰»º7/ØÌ²{äBž>Sù‰Ði…C]¡DB‹]Lq÷ÖÆ£IcB\GŸËs$ÉÝÑ-ñ:Ÿ·Þ ÐX“Tö‰Ç/‡+÷ù=;4íž{=WBßx Ì1Æן`oscrypto-1.3.0/tests/fixtures/keys/test-pkcs8-ec-named-der.key000066400000000000000000000002121421476274700243320ustar00rootroot000000000000000‡0*†HÎ=*†HÎ=m0k „-²Å>=D€V…HÛðž›ÃšbH2ÖkjÏœOé÷¡DB…› Ï ü’”ôbÜŒIÿ÷¬ÖŒù!#e¸™) ÅFd¸c/¸ÑËszçZm4µM:µeá;_h)AFú£~ûoscrypto-1.3.0/tests/fixtures/keys/test-pkcs8-ec-named.key000066400000000000000000000003611421476274700235670ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQghC2BssU+PUQYgFaF SNvwwp6bw5piSDLWa2rPFpxP6fehRANCAASFmwALzwn8kpT0YtyMSf/3rNaM+SEj ZRm4mSkgxUZkFLhjL7jRy3MVeucZWm00ArVNOrVl4TtfaClBRhH6o377 -----END PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-pkcs8-ec.key000066400000000000000000000010721421476274700225050ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA ///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg 9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQg6OWPI5AB6xIv ibu6NwIv2Myye+QfQp4+U/mJ0GmFQ12hRANCAASLXUxx99bGo0ljQlxHn8tzJB3J 3dEt8TqftwTeINBYAJNU9onHL4cr9/k9OzTtnnsOPVdC33gDC8wxxgPXn2AB -----END PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-pkcs8-tripledes.key000066400000000000000000000034521421476274700241150ustar00rootroot00000000000000-----BEGIN ENCRYPTED PRIVATE KEY----- MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIn6e16heAU4cCAggA MBQGCCqGSIb3DQMHBAjnH1OY41T/NASCBMifNpJWcjAlthILQqFyZ9U6l0Cm5pD9 OpyFEdjEz6jG2JryGKAdpur9zwtYGAYaLBF94dlxLsVnbR00nxeYBfagImk9rTCC Xhdu1AFG24IQILwJJWMs/JX/Xz0gxLzVhaeikpm2gSaQytyldQAhaNWIJzfFU/t2 q4URGS05MHIrSfdli1D5r1UwvOKd18p89ZX2EHNGu8Z6+lfiigyNgVngmDJ8L0OC 7IVNOG9+CkYcjez56lQfexspU2z8CeY2bZ2gp+5SfyOxSUZ4592bLBm+8sFf3gnL 02KvYDRuzWRMBAGkeCN+vRrQB0verefo8OVkJ++VqO5mBpvn2HOOUqdezl0lQDOX gx8fi/9ogv7ryPmXgSVTJEJm6I9rg4rDllpYVDANEXO4pZ4Wz2k/1sGRU2cCYUSj +sQBGGTqSwLBOVLOCihTAyO2IwpJklLZO2RTSwaGVwBzH34kd3FpVPAUdZvGINlp 8+7aRK8VltWk9C7xg6JxpOj1ynRbfo05lgUIPWZdag1CHL+HpK+t0rD98g4h+6fw 7N0LNo47GlSPGZLp4bv1OCb6lmGh8nhlm01s4uVR/YN/eJgiy5VXowd1e8pOs8KI QOsZFph8jkABxOd+aGDsvWTYorHAYvx4zNU8NjlYUTjKoT75viSAS8B+3uZTpJ7N UJgpZCVrkHx/OPeteKkmUQ7IWIrGs0vvxwybs2NWhU4dckqeFmQlJc+UVhvecdwS 57+f8fGH6NOpz3olCCusxWO3CyzpRRASGChrMrGGXHpZHmKbuXj5a6WtOH6uUuEL 0iYVTVQ67HFWapBd3OuyCkQhmripRaJBPaoIzsww6A8Cupp7s9xcYmZeg0t8oxTM kJ6rpXO2AKL4GV2FhWL+cdOXRum4WMiUnZ5Ty2xG5rrceQjIk/yaJo+/EJ+4t74R wFOWQJzswrdUx4G2c6kcqC/80eNyYWtHXKlfAOGQ5Cd6C6vkGQWLsMYnufkWk3AV pNYbfme+um8C+nd0uyb/iiue0LWVa3YNdJfYqD5McOL12hxX7EpIhAyytIjV02Rm oanhQg11pOPQWQ80CEYz2jPPO+rabl9Q8tmwcgWy4dHASfZ95746cDyaks9QR3KF HdSi0DmcPhin+QW0TxNgNxhe1dadxrQbCjh3t5dCrS46dYcn5y7ikI0+WO7eafFE vBQOrcFsqmt73i1MHpXgxhhHa+PZZVk7LxdrpUPWJNvGbkEdKU4U4R8d7kd6+WJe CguO0Ga9G7qQpnhE7dqQnCCOoreuoymHTk0qZPfDsCBhmoUwlUTMLCDppdRBjlnd J4sl9ds/pMbGLGgr27oFMcBzk6hBLpXp46ORE9ZOMMC1HZcELh3sa2dqRweke9CW oxnN75KDbtLqfVoydoYymwp37Q3i0xv7XkBBuoa5nub8M9PMLrGtq+h4VKTB75Wh kMku6pzTcJ8kvBdmJPBhK4Q1uxmBgxLO75TIJ4ff+lTOp4eRGeYjO518UypJJFjY GxPRgQx6DKJo2GzW6ku2xRzbVncFXYvDPfoEQotstTQ61p7X8+Y6Hrx2JR726Fsj kjqVIxKqFnGVyInB7XgaM/7V+OiMJKSynej9Le6++rJoCXQ6jLaJQieF22fguoLB URE= -----END ENCRYPTED PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-pkcs8.key000066400000000000000000000032501421476274700221200ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC9WygHdOLmSiwC Kr1Q3ngXquxQNn6UucZFnKh22q87w9f/xBv5AkIqya99Np7rIyRcXY4t2lQ0Rj8d PllsBmo8vpNr2JtLe5kj/25lRgjNOqH7w2VDFldS3eEsiJx67ktUI+MR5Qe/2fpg FmKQrnZgBSCRILZRw83s6rupCxFTQdZWyx/pTzcrp8FwvRUmFoXpIwMgWn5RQZKE FfdI137kxuz4dJuAwH2Z+Z+a/2Kb5ihA5D5GltZgLfKnpeG/EZJQIfLfL00n70Lk 3ssNxhXCnuyspihyGgw8cMJwC3xljWt7e2KFWT/X1a4IZEe9wwQpxyMdtrgx1E5M AZiHVC9fAgMBAAECggEATw0fSP2jPED63my5XGmD+V2CCnq1naFxBN7B9dyWC31X T4+vneUzeml4ue1zqvag1263TK05OhmZf7vn2RFUiMeHBB8JthmDdWPN0rnKMuOn fFO2kqthCVdYCh9+NFQHXrkcsvvKoG1/+V1fCMfM44lAb1YYx0nXTnEwpwHX2cmK OBjX0SD/2y81CrrDB8MAIoBhe6yjPwCkajNPpvz6jacg1AdoKCZ/mo9uDjgl9ff6 TZ6vtsr6aTty6g4hY4Hki1J/KrgZ1ssz5db8YgaRyVdQ5ZEOaK252gsq2/la29iu xZ7WLOcciuUjLuAjWL1n5X2YRhv5pk8O2MHgq7L+AQKBgQDtUFKi/g6mHvzKIgjC YnpP7j611slDaXFXsa6xJWOPvq+ZkBI0nsSClHnCgfUA8TIeCdSShErx8ZQen4vG azautiK9yNdngNO+ln+pBUkE0nilXJaf6KruAuujbCi2IsMHN4b2ijjoLOfRAI6T u2h/FJtwSSGQ75S7BzdlKUQ59wKBgQDMRB8dA0XMEeqW89Vt/L34jGcb67fdYQpI yv7V6T+Fp7jf4Pi+d0FfUM+6FpZRqmxT0aADlghc6EIiufHgxsy9wGfnkJYh/sn5 0R09pQ4Y35i1htXb1TjmdfR1LhBfEE5VnCepQFEPCm1IVVoscNSs4Li+eRJPqJEe H84nkC4b2QKBgQCbz5ICLBZIIa5NtJzVq7yswDryPuxz00Y0kpek/WxqE4PNqlcZ r2hMZ9mtyI+pJ7OFH2UvMabXRYq/tHccNoZ3nWQgAT7UWTQtPTjiK3MutFW8FJdc tHGNxeMasEfmldpA4cc+FbCZV+p4QgpamsBYN5p61bkxJOwdA/bt93MxLwKBgDgN eXw8qaqWQAmsX6UO9hJ+dMz0oj/doTTYf5Wzq/rBS7ojwh6CGy5MvrQR/q3qVk+p 9n8FbMYR9hQRco57/zMS2XBx/MDXahVjjOKdqICq2vz1QzpQCI01UR/WxCdSEizr 7PZE7/lwowx2X4hSbgoCoK+kCaJSX4Akui2hIwYBAoGBAJxbCxNsycUmsHEdV7Tk Lxj8N5TUKPtOUoq5hKJ0BK6zQqEm/tuRIgQ3fHdEkUNWuQRbfQAf9v6gwodKOKV5 Y/s0sJPhj939nJuFq+E7OKiXjTwaxk17phVPOT9RK39rhkErUrlGUeZOIVg1e95x erMeeTvs7MIywm215b+CvUAF -----END PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-pss.crt000066400000000000000000000031271421476274700217000ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIEgTCCAzigAwIBAgIUHUhBPuha4XBidArC3PoG4Y2NU3IwPgYJKoZIhvcNAQEK MDGgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogQC AgDeMIGfMQswCQYDVQQGEwJVUzEWMBQGA1UECAwNTmV3IEhhbXBzaGlyZTERMA8G A1UEBwwIUGx5bW91dGgxHzAdBgNVBAoMFkNvZGV4IE5vbiBTdWZmaWNpdCBMTEMx EDAOBgNVBAsMB1Rlc3RpbmcxEjAQBgNVBAMMCVdpbGwgQm9uZDEeMBwGCSqGSIb3 DQEJARYPd2lsbEBjb2RleG5zLmlvMB4XDTIyMDMxNTAwMDAwMFoXDTMyMDMxNDAw MDAwMFowgZ8xCzAJBgNVBAYTAlVTMRYwFAYDVQQIDA1OZXcgSGFtcHNoaXJlMREw DwYDVQQHDAhQbHltb3V0aDEfMB0GA1UECgwWQ29kZXggTm9uIFN1ZmZpY2l0IExM QzEQMA4GA1UECwwHVGVzdGluZzESMBAGA1UEAwwJV2lsbCBCb25kMR4wHAYJKoZI hvcNAQkBFg93aWxsQGNvZGV4bnMuaW8wggEgMAsGCSqGSIb3DQEBCgOCAQ8AMIIB CgKCAQEAnDpE0BB8DGErRF2u7pSMx297L9j28C6G3WbKi9KUd2NI5l8qLIp8TrLN bgQQX+UzfRXaqdESycA5I0RRW+QtHITBzjXnPNWlJGUf5VNvNa+GEOPzNjo93QpM FYOB+QeX6O4BM+gkqWDt7R+2/VUHuisJIJsfE8vBZkxcq9pQyaMAdcUzgvQN3dB+ HxUdj6DNi4QufcBDNB7RFJM+4Sk36u+723cnBn0YhpkyYbgzRua/u5omsS7fZH3E y8+cadfGofWoZKLAeTzYgrM/2it5gXBYudD5OURK/RUOKBHzbmY3IwFDGtJBH3j0 Xc5JBVhUWOWK4owkS/RiPvZzY0MUrQIDAQABo1MwUTAdBgNVHQ4EFgQUNwXHVWt9 syO/IPs7hREMrYi5KYIwHwYDVR0jBBgwFoAUNwXHVWt9syO/IPs7hREMrYi5KYIw DwYDVR0TAQH/BAUwAwEB/zA+BgkqhkiG9w0BAQowMaANMAsGCWCGSAFlAwQCAaEa MBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgGiBAICAN4DggEBABGYo8yF9Q69i+pm mi3sj+SXpx5GoSDmQoRp7J/n3KedERftR/j5T9w4py04K2pL4bl+OHmE8WJCmXWr RuN9M12t0qwF86koRcZz1NlsVYCBVBJSYGnd3TOGyIKbq8M/WMXqAe2UiK+hYp16 MqfQEvj+pAAolWaxwC2/wbE8Isse4eXr/ax0SmyRMVncs6ivW6z5IGDgJdgolA2d pyfqQGFSABMu9AvQNnYX0vsxmSZH9NqU4Q/JJ2EO8ydvwH2TbQnqcIXFNccjV0L1 upXJ9oeT14Ia40PhzY9aJrSB7hMoFoRGRDSwFxKPjr81fM9312ZmZDJTMlMPet8d eLIy5aY= -----END CERTIFICATE----- oscrypto-1.3.0/tests/fixtures/keys/test-pss.key000066400000000000000000000032501421476274700216750ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvAIBADALBgkqhkiG9w0BAQoEggSoMIIEpAIBAAKCAQEAnDpE0BB8DGErRF2u 7pSMx297L9j28C6G3WbKi9KUd2NI5l8qLIp8TrLNbgQQX+UzfRXaqdESycA5I0RR W+QtHITBzjXnPNWlJGUf5VNvNa+GEOPzNjo93QpMFYOB+QeX6O4BM+gkqWDt7R+2 /VUHuisJIJsfE8vBZkxcq9pQyaMAdcUzgvQN3dB+HxUdj6DNi4QufcBDNB7RFJM+ 4Sk36u+723cnBn0YhpkyYbgzRua/u5omsS7fZH3Ey8+cadfGofWoZKLAeTzYgrM/ 2it5gXBYudD5OURK/RUOKBHzbmY3IwFDGtJBH3j0Xc5JBVhUWOWK4owkS/RiPvZz Y0MUrQIDAQABAoIBAE0bTT9MA42wn8Q8YMyMMPd2BFzXPvnIXPQFec6M/o32SNK7 AXu+ViAZi97Szqol28XO9X9/G0DMSOcpHua1tbYvp9/5JsoVwE44QwfSqD7dBNRt TWrkUAg6vtaXc5zPrKBgZ+OQnhejKYsxbgWW5Bt0hiq7pRkGJcwxwENpmxoJPgFW WFH1CpC+m/1C3FzMcnvgsFwog6u52x0tm40jvK4BEXFMk/KQ8EK14qs1gMe068ZY k7k0JFzmYw8mv95yIGkJaR3ILhA0Uf/robGL31cgRNgLZX4OOLLwBHKQQaF4cT4b dsH970sasTHnnpMzpcAVygF1LVdbB+BNZv5PQVkCgYEAzcUOavafJLdYZV1VwQms sTGAZpZ6A418PwCf4PwZjtEnTlSyXUpGqCWSn1KEHkLMXvSTWjvvUc6av2EY71HF fKjdlBYuFCnKtjB9S8763se8x+J/oURi5j9P9Y1jZjQRLrz/EGsKKOK6czYX9hK0 3tqFLwjBMZTAV8ZIlmVyzR8CgYEAwl062KC9eNSXz9mkcISN4LzTM0OIqvf3qO02 NMwSs3yKhoyHvfssPRinr3ao0ehvw1FeMG9MNCj6dslnPSik771HO3UlpA96eWjZ U29TnF0bEiT87paXGk4LUViXQARS07MrLAr3VWhWQNqGN2IejWnFWJye5+qIhosP 6aglWLMCgYEArSHOfcVAJf52S+N6Uc+4E+cYqDiYNQEIt2HpI746KuZkcg3lfYUP ak8izRb6vxNpLcQPb27acDSsE7a46jQdPERBG2aGIsT/IMgvxGdEwcnBV1WGGcSW ijBfMLZkGgxZIMA4xDGr/La4NlQfWRZoRNIMGYT+OJ92LuRREuoM6/UCgYEAlXFF i8abqEodp+y56OGijQfig+oLLRmXzMUHBrVaM7Mwd5+Zx1NWgmtwkxLzXuIO/OiV OTx6JSjU23yj6nxTxRmNqYO7O1fDXZJmdyaD7RgRdnQGHhSffdRAE33AXbJ1VNzP 9v8/iOW+XAMpa2//+c83s5hI/hxweLJ3u7pTbM0CgYAyw1rLSjUgBs/KPr55dod8 Yl2XCG3dxmx2JwIkqXbGzWsJ6gWhXTAUGqhqSdW7enKn5AGXc/5IcHaipQ6jmlZr JSErJ1fCQjpOpuqoncTv7uz12UiQuWPuglov/golFvhFr7fl6/THsUnxG0Kw77jn /FlTWhrj0N5RhKvaiSF9BA== -----END PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-public-dsa-1024-der.key000066400000000000000000000006731421476274700242550ustar00rootroot000000000000000‚·0‚+*†HÎ80‚Ô-^»[þè@‰«ó€dðj§ê~YeûZÅô+D)f‹=æm¡Ühê#¾W÷N Õ£Þ©Wñ…¤uÍr{úk—æzëR«l'Kû,D›É‘d"8«ü‡šPO¸Qêøës[Hn%'êQÀ,êB}´"aÌ5îŸÈAò7J|ûü(t°)€/4­YIéY+™DÌ–ú¬aË=ˆ¥Ë´¬¯­ö ¶¶G;5wFlS樠æKôÉq+ tvÞͬuë÷~˜jÅÞ}—Òëä¶½»ÒH@fùq8²“4&žíÎ$ÿ!FÝè sZìõïóä„Cbøa,{–(***nUW¦ÚÑ…®¹“ë|’“uarBÜfä3c—Ð'ÞcŸ3)]“ª«Ó£×çóþ³ªN™ñžÔª›UxƒÔÈLk44[êgZ®³ÚÂ#·QÃncj#¾èÕCuĘ r k4/Wgº¤RqÄ!P2Î9èI¦L óßö„'¨r)Œïoscrypto-1.3.0/tests/fixtures/keys/test-public-dsa-1024.key000066400000000000000000000012161421476274700234770ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIIBtzCCASsGByqGSM44BAEwggEeAoGBANQtXrtb/uhAiavzgGSQ8GqnkOp+WWX7 WsX0K0QFKWaLPeZtodxo6hYjvlf3Tn8N1aPeqVfxhRGkFnXNcnv6a5cWHBPmegAA 61KrbJAnHUv7LESbyZESZCITOKv8h5pQFk+4Uer463NbSG4lJ43qUcCQLOpCFX20 HCIdYcw17h+fAhUAyEEbj/IEAx43Snz7/Ch0FbADEykCgYAXLzStWUnpWQYrmRZE zA8YlvqsYcs9AYilGMu0DqwRr632Dba2Rzs1d0ZsU+aooOaBS/TJcY0rDXR23s0T rHXr936YasXefRqX0uvktr270khAZvlxOLKTNCae7c4k/wcSIUbd6ApzWuz17/MY 5IRDYvhhLHuWKCoqKm5VV6ba0QOBhQACgYEArrmT63ySk3VhckLcZuQzY5fQJ94D Y58zCCldk6qrHNOj1+fz/rOqTpnxgZ7UqhibVXiD1BHITBlrNDQXW+pnWq6zHtoX wiO3UcNuY2oeI74c6NVDgXXEmBIKcqBrHzQvVwNnuhqkUnHEIQIcUDLOOehJpkwJ 89/2hCeocgMpjO8= -----END PUBLIC KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-public-dsa-2048-der.key000066400000000000000000000014771421476274700242670ustar00rootroot000000000000000‚;0‚.*†HÎ80‚!‚çøÊÚ_]ÐÊŒ­ ¸äe £¤ÇZR4—ð‡—`Á±(jÆ…MKð#W,°ø~Ú<{úŸàï¦ÕëJÙÛ]0äýízœ/P‘ÂW¿Áéo@TZÇï'G¼™ -õD>—Âñüǯ¹Ânžmô“¬øX,öhX;wµŠê h'$–hTjWº4fðÔ‹¯· ò+Pø #YY Û£ò¿°g4#‚Þü±”§QYJ(‹Û&«‰!òׇæv½ãx¡iø`›oVQšñPÕõr‰ÈŒÕeCíþ3ŒË€³u¾jöÍsgƒ—¢&ÔÉ ) Zôá-²¦™P.Jî—«ºXµPã²6ÙÍ‚y:š2‚'vnÆ® p-kGT«Êîªæû›Êp/Ùñg ü´=±êƒ²¤¾ìQLô‡Øˆ§Õõ™­õi"«W\Î=§dá²géƒXº‡Jòžb`[†ø¼ lþ—ÊÄæ•«qGzªz¢VÐKý# %ìnNãV•{OàGI ä‹ÿ¹ ‚13åµöªé.Ï¿-s’È÷[m‡tááã…‚‚uö½Õ8ýtœºîœ3Ü4B¹¬ø à…ž·>gfL…0Áêûƒw£n3th¾0ÂL¬‘L7 ¶çæY^'wo»èÎqG($WQûyOÑX—wtþׂé:{h¬Ü½±Qó•™Ò? geøÅÓ æè@à¿sœDùáreÝÚU7Aa‚}„|tMØ;’y7èÄ߯ ê ™1J oÊDMg—w—§÷ Ê#©ªyy³ O2¸«ïÆÌ%iE‡-VÕŠ†¼øšO$Ñ –Ô«21sù1¬—ÇóM¼Ôܤ7J£Y: 2?ϧÅá2¿½‚9ËH[O—È™Öè¤b°諾5oscrypto-1.3.0/tests/fixtures/keys/test-public-dsa-2048-sha2-der.key000066400000000000000000000015121421476274700251100ustar00rootroot000000000000000‚F0‚9*†HÎ80‚,‚‡Þ <÷U‰áuTb†ØžLBîºþ€†äáÛ:)D²š-†|;Ï`p;¥ÑEùX[yŽÓkr¶¾bÆm2Fw¾40XNN«ç+ít->¡I²¢3ÝüëÏ_8ÿî˜]_¼‡H¬€ûëé»±¤ˆšê @7æÀp=Ç)¼eLz»@Œk[SàÐDš•ý Ûõ±ÎðãXó4oãÈ»"/¼Á˜ïxôZWFáæ6Q"<1lv C=úÓ ™›€3åLÕdVZ§çÁjAÞ$pØûØÀâü3JP·mÜÍNâX—‰ &»¥[n6‡ >ÚE’šL="p)!ñW!lYоC#=K7[@l[ÞßëG3toŒº]‚m¬¾iÐJbe»ÏòBÖ-A)„‘Wà3 HÅø[²\mæ;¥ã*ð8;.Mw$v5[ד*Â¥A9 h¤â2Ñx£-æTT±(J‘”¿Ù§$¥¶á¬š¯©¨·22fQCQ¯3ŽÍ U‘aXnN$Ìñ¿½^.Q ÖÒàýÄšq(i½Ä{„Kò]tGfiôºÍfaòx7Øu¡!^µr„´ÿ!+ü iòõ (¯6ÈV4kþ²B6äå3+ª¿5}ƒ|\L?îÿ½ë:yõýãZ&Mºó¹§‡u;ÃÄ ÜAjöÃ+uÂO©Õ‰Ó¦ÂèF¢Jö]uõŸæ‚‚G tù’3!¥L?ëzðÙÜíêFyGóLÆ´‹¸ƒÖÜ)`Ë â¶ù®¸z£« 4™ƒ;ã5ÅCð1.àH¿'ãÓý k…]ÈÐ#â4 …è«ý^Þ :+€aAƒÎÔ(g5ÑßÛЀæ¼ò,¯7i¬Âì®=Í¢üÖRM˜Ôœ@˜}×dô! Òts0”…‡·Qªõ2Wâ=wï´#éœÀuwŸdù*ÙìƒÑ2ãÒ½ãævŸtìN ó¹eɪK7 ȾԲ7À6™K%¤3¥¢JGä}úÊPÁ\l9=‡Dy^Æ_LLbŸaž¢ ¶_p9\oscrypto-1.3.0/tests/fixtures/keys/test-public-dsa-2048-sha2.key000066400000000000000000000022521421476274700243420ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIIDRjCCAjkGByqGSM44BAEwggIsAoIBAQCH3iA891WJE+EDdVRiGB6G2J5MQhfu BLqd/oCG5OHbOilEHx8VHLKaLYZ8AzvPYHA7j6XRFEX5WFt5jtNrcg+2vmIOxm0y Rne+NDBYTk6r540r7XQtPg6hSZ2yon8zjd38689fOP+d7phdX7yHSBWsgPvr6bux pIiaF+oNQDfmwHA9xym8ZUx6ALtAH4xrW1PgGNBEmpX9Ctv1sc7wGxHjWPM0b+PI uyIvvMGY73j0WldGBeHmNhwWUSIIPDEAbHagQz360wuZm4Az5UzVZFZap+fBakHe JHDY+9jA4vwzSlC3bRPczU4d4lgAl4kgJrulW242hws+2kWSmkw9InApAiEA8Vch bJBZDoq+QyM9SzdbQGxbFQTe3+tHMwh/dG+Mul0CggEAbay+aYHQSmJlu8/yQtYt G0EpFISRV+AaMyBIxfhbslxt5jul4yrwFjg7Lk13JHYdNVvXkyrCpUE5C2gBpOIy 0XijLeZUVLEoSpGUv9mnJKW24ayar6motzIyZlEAG0NRrzOOzQxVkWFYbk4aBSTM 8b+9Xi5RC9YE0uD9xBeacShpFwS9xHuEH0vyXXRHf2Zp9LrNZmHyeDfYdaEOIV61 coS0/yEIK/wJaY3y9QkorzbIVjRrHf4eshdCNuTlMyuqvxw1fYN8XEw/7v+96zp5 9f3jWhMmTRK687mnh3U7w38PxAvcQWr2wxsrdcJPqdWJ06bCAuhGokr2XRt19Z8T 5gOCAQUAAoIBAEcgdPmSMyGlTD8Q63rw2dzt6kZ5R/NMxrSLuIPW3Clgyxsg4rb5 rriNFXqjqw0MjwU0mQiDO+M1xUMa8DEu4Ei/J+PT/QxrhV3I0CPiHTQSCYUW6BGr FBH9BRMQXt4JOiuAYUGDztQFKGc10d/bGQPQgOa88iyvN2kDrMLsrj3Nf6L81gZS TZjUHJxAmH3Xf2T0FCEM0nRzMJSFh7dRqvUyV+I9d++0I+mcAMB1dxCfZPkq2eyD 0TLj0r3j5nafdOxOC/O5ZcmqSzcMyL7Usp03wDaZGxxLJRikM6WiSkfkffrKUMFc bDk9hxtEDnlexl9MTGKfFGGeogm2H19wOVw= -----END PUBLIC KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-public-dsa-2048.key000066400000000000000000000022321421476274700235050ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIIDOzCCAi4GByqGSM44BAEwggIhAoIBAQDn+MoB2l9d0MqBjK0JuORloKOkHcda UjSXH/CHl2DBsR4oasaFTQhL8CNXLLAb+H7aPAZ7EBX6n+DvptXrStnbXTDk/e16 nC9QAoE8aSXaWUzwNwyiAeu5BewP1cJG/O2iSWQTJKtkq2tu66zOkcydsrJxLo7n ORZJdY+0Y2NAeKu/lAgdN5rj8kBWnYFWyUrBtIteoKGvtSKmX37eaRCPx/yzriJU g24qoq4Ak2VvrPPpbuk1PpHCFFe/welvQFRax+8nR7yZDC31RAg+nZfC8Q/8x6+5 ncJuno1tEPQUk6z4WCz2aFgVO3e1iuoMaCcklmhUale6NBRm8A7Ui6+3AhUAj6Dy K1D4oCNZWQzbo/K/sGc0IxMCggEBAN78sZSnFVFZSigPi9smq4kh8teH5na943ih afgFYJsRb1YGUZrxUNX1conIjNVlEEPt/jMfjMuAs3W+avbNc2eDl6Im1MkgKRYG oFr04S2ypplQLkrul6sDuli1CFDjsjbZzYJ5Opoygid2bsauCXAta0dUq8ruqub7 m8pwL9nxZw38tD2x6oOypL7sUUz0h9iIp9X1ma31aSKrnQ5XXM49p2ThA7Jn6YNY uocOSvKeYmBbhvi8Bwls/pfKxOaVq3FHeqp6olbQS/0ZESMJJexuBk7jVpV7T+BH SRQN5Iv/uaCCMTMB5bX2qukuzxgRFb8tBnOSyPdbbYd0E+Hh44UDggEFAAKCAQB1 9r3VOP10nLrunBYz3DRCuaz4CuCFnrc+Z2ZMhR4wwer7g3ejbjN0aAMWvjDCTKyR TDcJtufmWV4nd2+76M5xRygkV40PUft5T9FYl3d0/teC6QA6e2is3L2xURDzlZnS PwpnZfjF06AA5oHoQOC/cwecRPnhcmV/3dpVN0Fhgn2EfHRN2I07AJJ5N+jEAt+v CeoLFh+ZMUoMnW/KRE1nE5d3l6f3oMojqap5ebMgf08yuKvvxswlaUWHLVbVioYR vPiaTyTRIJbUqzIxc/kxrJCXxxTzTbwS1NykN0qjWTogMj/Pp8UT4QMyv72COctI W0+XyJnW6KRisA/vqr01 -----END PUBLIC KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-public-dsa-der.key000066400000000000000000000023121421476274700236610ustar00rootroot000000000000000‚Æ0‚9*†HÎ80‚,‚ÆÏM$”³ü *“mzÎO¥u†©¡ò‚üÇq¿”¯Uª¦¾Ô)ÿ“ÆŽ‚­o/&þÍÃ'f5ÿŸK‰˜QñOAŽïygÆø.1˜ÖRŒ[VwbW'Û2Œ¸µ"1[ƒk·]‰Bðᕊ'>3Xª5²jÁÈý¢·É#%VVõƒ màПŠÀ´$‡`ÍeÜçÏN3»#;sQ¬øA# ]9X¼YôL¤n£ØpøKù-³˺Ä+€JæÜ1ÑpLíRÌq&ŠT‘§åüòµÏ#XËZج5ƒŠŠ…ÀзýTúwïJ ¼„ñõOŽ»¿• R,Ë‚Rx`ž`u?îÔ&zÅ ÿ»$™c9Œ¾Šñ©Å*ÈîkqC'¼#Ãý²Ó›‡¯lÒˆ+}#! þ9rrÅ6óŒ½ØÃ¸9nšÑÜÚ›kÆ«sš˜ÍfX­¾{Ârä°îݼÚÜ¿¦‚jGи,¢ªcà é a)B4ž×COäR±Ó>¶Æ _¿ I!žEIó¡R)¡B¸Aú¨)•pQÄZø' WPi]»‚€!}°å,T&‘¹#+$¯Ã^ºšsÈõŒ®µñ¹@?]²Øižž./µî›„†×'=dÍvQoœfmF™ÿš‘Ü%@ «\çóh¬¾î¢Ôã+­Uí.OY癥õˆ–¸£\¯H¡þì®æ B(P‡õÝpº$ËfÎãÞa½ ýFÃ~ª.ãÜ¿b%„i@»C6+(Fᤠ<>q³_¦àó›^(Ú3¡µûä´ “Ìê¼n1ón¡9?À PòÝÖßuÈKê$LAämŽ„ÏŽ²`ÐÇø‹Ij¹me¥€ü7¦S* @*•+]Æ©“ì Ÿ#÷±:Òš$/=fÒ½â-ø8ñõ84ïÕ)/Ö €ˆ¸¿W D\ˆÞ.Í\ªa‹ÌÁjT°Ôl@Â}%þV}F(- Ô9ŸÕËñ^È!®w?ÎVûè]·åúT_¥¦IPŸsx: kõø$“&]ÊÃF‚Dÿ¬c•wàqÒAk8_ˆº4]j‚…‚€)*¶áUã•´ˆúëû#9Ê%Ø)p yù¹”'@y9âÁÓB経\‰ð:š€Ý™R]÷&%Åcb©» ÀÕW‘ÖåŠÄZõû[™`Ùíú—‘0_€¥vöõʉV¿„ƒË®K‘@>Óh}9Îd~ÿ^h ¦»òAÀõÏýD!,«Èˆ×*n¤HױϛÁÚ«ù¹û›Ë *"ôo>VÅüÆê®‰ UCiךփhžºQE™jȽ³MÞíÓ3Õ&íÀElJü)¯œ…üRi>…&J®ãJ•²¯(”ÍD¤3unê× ö\}‘ž£@ØQÞ&Š—®ÝWVâƄʲÝÊc½‚Áb’I+RA´3a–×þS£3Ï·%Ùv€§-§h>3Ç-SÄKC](l)VاÝLÖSˆ;l5“÷”h}½„ÖM¶çј½#½o±¦ 9­ŠŽ¶Ó~l_ ¿‹úë"E¶º<ò|Ëo™Doscrypto-1.3.0/tests/fixtures/keys/test-public-dsa.key000066400000000000000000000032621421476274700231160ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIIExjCCAzkGByqGSM44BAEwggMsAoIBgQDGz00klLP8CyoVk216zk+ldYapofKC /Mdxv5SvVQKqpr7UEhCPECn/kwLGjoKtby8m/s3DJ2Y1/59LiZhR8RZPFUGO73ln xvguMRaY1lKMW1Z3Ylcn2wYyjA+4tSIxW4Nrt12JQvDhlYonPjNYqjWyasHI/aK3 ySMlVlb1gw1t4NCfisC0JIdgzWXc589OM7sWIztzEFGs+EEjCV0BOVi8WfRMjaRu o9hw+Ev5LbMTy7rEK4BK5hPcMdFwTO1SFMxxJopUkafl/PK1zyNYy1rYrDWDioqP hcDQt/1U+nfvGUoKvBiEHfH1T467v5UMUizLglJ4EWCeYHU/7tQmep3FCf8buySZ YzmMvgeK8anFKsjukGtxQwcnvCPD/bLTm4evbNKIK30jIRcAICD+OXJyAsU284y9 2MO4OW6a0dzam2vGqw9zmpjNZlitvnvCcuSw7t282ty/poJqRxLQjbgsoqpjwwYO CemgYSlCNJ7XQ0/kUrHTPrbGCl8Yvx0LCkkCIQCeRUmNf/OhUimhQrhBgfqoHymV cB9RxFr4JwpXUGlduwKCAYAhjX0YsOUsVCaRuSMrJBcdr8NeuhIZmnPIGPWMrrXx uUA/XbLYG2meHp4uL7Xumx0ahAKG1yc9ZM12UW+cZm1Gmf+agZHcJUAMq1x/w6fz aKy+7qLU4yutVe0uBE9Z55ml9YiWuKN/XK9IA6H+7K4C5gtCKFCH9d1wuiTLEWbO B+PeYb0N/UbDfqou49y/YiWEG2lAuwYGQzYrKEbhFqQJEzw+cbMbX6bg85teACja M6G1CPvktBIJkwjM6rxuGzGN8wFuoTk/wCBQ8t3W33XIS+okTEHkbY6Ez46yYNDH +ItJarltZaWA/I03plMqjwlAKpUrXcapk+wKnyP3sTrSmiQvPWYa0r3iLfg48fU4 NO/VKS/WDYCIuL+dVwcMRFyI3i4GzVwQqmGLzMFqVAKw1GxAwn0l/lZ9GkYoAy2g 1DmfGNXL8V7IIRqudz/OVvvoXbfl+lRfpaZJUJ9zeDoLa/X4JAKTJl3Kw0aCj0T/ rGOVd+Bx0kFrOI9fiLo0XWoDggGFAAKCAYApKrbhVeOVtIj66/sjETnKGSXYKXCg eRn5uZQnQHkfEzniwdMeQue1jFyJ8Dp/mhmA3ZkRf1Jd9yYlBsVjGmISqbsfBwrA 1VeRkBTW5YrEDlr1+1uZYA7ZFRft+hqXkTBfgBaldvb1A8qJBVYSv4SDy65LkUA+ 02h9Oc5kFn7/XmgJprvyQRDA9c/9RCEsq8iI1ypupEjXsc+bwdoCq/kGufubywwq IvRvAz5WxfzG6q6QHYkNVUNp15rWg2gTnrpRRZlqyL2zTd7t0x4z1SbtAsBFbB5K /CmvnIX8UmmNPhaFJh5KH67jSpWyryiUzUSkM3Vu6teg9gNcfR+RnqNAHthR3iYT ipeu3VdW4h7GhMqy3cpjvQWCwWKSSStSQbQzYZbX/lOjM8+3JdkfdoCnLadoAz4z xwYtUwfES0NdKGwpVtin3UzWAlMciDtsNZMf95Rofb2E1k225w7RmL0jvW+xpg05 rYqOttN+bF8Nv4v66yJFtro88gMTfMtvmUQ= -----END PUBLIC KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-public-ec-der.key000066400000000000000000000005171421476274700235060ustar00rootroot000000000000000‚K0‚*†HÎ=0÷0,*†HÎ=!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ0[ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü ZÆ5ت:“ç³ë½Uv˜†¼e°ÌS°ö;Î<>'Ò`KÄ6†ç“jfxá&·Ÿ~AkÑòá,BGø¼æåc¤@òw}-ë3 ô¡9Eؘ–OãBâþ›ŽçëJ|ž+Î3Wk1^Î˶@h7¿Qõ!ÿÿÿÿÿÿÿÿÿÿÿÿ¼æú­§ž„ó¹ÊÂüc%QB‹]Lq÷ÖÆ£IcB\GŸËs$ÉÝÑ-ñ:Ÿ·Þ ÐX“Tö‰Ç/‡+÷ù=;4íž{=WBßx Ì1Æן`oscrypto-1.3.0/tests/fixtures/keys/test-public-ec-named-der.key000066400000000000000000000001331421476274700245620ustar00rootroot000000000000000Y0*†HÎ=*†HÎ=B…› Ï ü’”ôbÜŒIÿ÷¬ÖŒù!#e¸™) ÅFd¸c/¸ÑËszçZm4µM:µeá;_h)AFú£~ûoscrypto-1.3.0/tests/fixtures/keys/test-public-ec-named.key000066400000000000000000000002621421476274700240150ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhZsAC88J/JKU9GLcjEn/96zWjPkh I2UZuJkpIMVGZBS4Yy+40ctzFXrnGVptNAK1TTq1ZeE7X2gpQUYR+qN++w== -----END PUBLIC KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-public-ec.key000066400000000000000000000007731421476274700227420ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA//// ///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5 RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA //////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABItdTHH31sajSWNCXEefy3Mk Hcnd0S3xOp+3BN4g0FgAk1T2iccvhyv3+T07NO2eew49V0LfeAMLzDHGA9efYAE= -----END PUBLIC KEY----- oscrypto-1.3.0/tests/fixtures/keys/test-public-rsa-der.key000066400000000000000000000004461421476274700237050ustar00rootroot000000000000000‚"0  *†H†÷ ‚0‚ ‚½[(tâæJ,*½PÞxªìP6~”¹ÆEœ¨vÚ¯;Ã×ÿÄùB*ɯ}6žë#$\]Ž-ÚT4F?>Ylj<¾“kØ›K{™#ÿneFÍ:¡ûÃeCWRÝá,ˆœzîKT#ãå¿Ùú`b®v` ‘ ¶QÃÍì껩 SAÖVËéO7+§Áp½&…é# Z~QA’„÷H×~äÆìøt›€À}™ùŸšÿb›æ(@ä>F–Ö`-ò§¥á¿’P!òß/M'ïBäÞË Æž쬦(r Ylj<¾“kØ›K{™#ÿneFÍ:¡ûÃeCWRÝá,ˆœzîKT#ãå¿Ùú`b®v` ‘ ¶QÃÍì껩 SAÖVËéO7+§Áp½&…é# Z~QA’„÷H×~äÆìøt›€À}™ùŸšÿb›æ(@ä>F–Ö`-ò§¥á¿’P!òß/M'ïBäÞË Æž쬦(r Ÿ– ß›QonuŠ7‡2ñe ViÏÍ8q•À$G4ਠO@øÿ°³5‘0 ^ì\r…ùUx¨… óîã?‹–âMÀRšŽVÙ~-ouñ§[‚´ä'§–Á4+Éjm¢u i©¢à QU×å„Å3ͪžèH,³á*í@d‚Æ%ÆÚÆËš½—2‹@€(DhÚ‹3 w*÷†Îë¨j0ÐÞvàz©®„:(]”¡VnAÔ$*¾Ãž1Ÿ3¥Tfâ×w7¦g[W‡ÿ*® !ëlü˜”KþÔLüuRÄbH ×÷!÷Iõ6Ë’oü-%Ã8MÍØ«aŸWÉ3‘@*¶Beo#édsòMÉU’j—ðÀ¥ÏkÍs![é•IÄ|ЖùÀm{"® k­øf•-W f|ÆL_Îw®8¼e©ºF±rp¢†zØ“ê=B‹õs¶>ΕdììlLS0 ZÑý6†oƒ“#ܬ¯¢Ä¨#vYõòE?ÇF°/ŠL†¿$3€$[÷õ‹j_¡µmFº•±Ú{-•–¦ÌÇ÷ Õ°IŒ™.Ð@+£üA¥WÞä'FÁ߬ö#º,€åwF±d9jø"_aŠ~ÿÙÌþ›m?Ñx‰ Ds7ôÀW2pIÌ/ß—^n‡6úú! £ Ü `?Ñæý¤a¯ŒÏ%‹Åíʲ³ÒÅ[ì}F o4}1"0”œqV@Ð!¼flE,Î"=QNwDæÀïah7Sy=(Ò==·¼c›í²Ü¾£b?°Hä:©wËÀ¥5æ¼{¨-)ôTbý ½Ÿœ Ájnž…°*[’¸1Ú£wÊØßÛn4^⑟~¿+æ Dr‹™7¬*OiëÆ¬»Æ›Wûí…9&­Œ]Œ"¹¿ßÊ.ïÚp6ìâ¿O¼fœ]‡ôQfzíòØlËÝýQ^”U›ç†öµdÊb÷\ÄBŸx6 ™Ï?q8¶«§†Ÿ’ð”<í°“%h?¥ <Ë‘Å)õ|±õâüNâjŽ™‰{ØjZ4ÃÝ œÔÅ%Ÿ™S¨7|v²òËúA§ßú´¬Üøo D‚üÔÜÍ9ýß:RvÖFÜ‹¬Éõððñü Œƒñ4"Šr]ñ‰H]±ÃöJI ÈÀxDÛÝȇ羜œ¢Æi ­Ìfìµñ€d»¢¶ù}•»@ÄiÙPcK))” ÅGøÝ•¹Å³ô2ù~ÅO-Šñâ–IÇ)ù¶Óp†HÝâàþu¬P”˾X…w–ߊۋKËTÌ—c03w¥Êþ·é-¸`Tvû;ó&éMÀ!ânº´‘H.´¼%™0‚j *†H†÷  ‚[‚W0‚S0‚O *†H†÷   ‚î0‚ê0 *†H†÷  0š–Q²f|Šå‚ȇg žë> î¤ûõ|CžŠ¡Ò µV–sâ²|>P+žᣜy>‚¼›éu™æ)Ï\~¤üOÓÜʨ“f€o7{ -ú¦HYÂÏÖ¼`*J7òxOÄ{²c]YW®-’$$îõ.é\ñsÒÞ˜ù6 [)O£›Q0ðBEåòÌŽ˜ƒKt›Ÿ. :ŒîËÁ™’ÿÉVìl^‘äªÎÇ—"MÞWî0äçò±žˆØlK¹jÜUHV(²U%ò‚ö‰äaÏY¸zè‘ë0Ê7G¯G\dcŠx&o^˜(£LÔº›¯d‡¿ZƒšD V!AÐŒ­«qËa¹-4xcÞApîövA"||$…Ðv**`Ëñ¸¾0×Z¸£¦ì)|c?‡uMôí’/tÆkѸ<_wv7H6ôŸ?Ô®»²&úõȤŠ:χ{Áºÿ…¾\½­÷ÕW“÷ž#›ÐÌç> '‹†ê`Ñ‡È 9aåç»nˆiØÖÊßN[€²Ã4n§¨ ù|Ì+T:òC‘\Tßð3|à¿#©ƒÛ”dX;ÓÙãO ?tõØPöªš›¹Í<>°—A§Ÿ1´ä þlwÚMÖN²4¹=çÆñÙ·ÞêØ[“ƒË÷jÜóÕ0Öfu÷«2QaDš!]š9´ŒìŒïo‡î[½N;ÏÍ…¶PXæ¡[ ª±SCÊ93§ÓÙÑ*S930”ýîá÷Õ­ú¢]²iòo+ˆ+u4^ÝʽðV}Œ¼ÚSŒ8Ó&…DRuÿ¾k“ß×J„VT2¯ñp$DB>[±ïÌ"K•-tOpéPJ ºû|%êh=zÜ-s1'ìÑ"4®¦qfSÎÆ™@)†¼÷ßW]¯šYØzž…§ð|úZ¥ÞüAëÄ’œ°~Zú¡9Ïìž\|¯X”$I.Oà¡‘tÆ%[ª9>›ÎÚÔhêáOäÚùO‘½w+}ÛCÃhۤṕˆ1*¤‚/ÁÃCqR*qR’"yƒ]8(èã•ÃpÍÆY2,Y ‘S{]tØ»¼+^Oû‚×{!úóò¬ã]•lR„0°ùãV'zĺChUv¢O´@£à¶-økµ¤\Wü}SxõMœÕ7—gÆT؇495}5¹»Ž#-îð ´Ö(â?|Ía5lˆ˜ùRíùHNp;¸ADÑš)8Ÿ"<‹žÖá÷Ùë\-Ó€nê6£OFÆ[!HÞ~ÅltÂFÀG˜Õ?èOzâ'Á@05k épâ<}ᇠ•ѨSJ!¾FÇÙ†¨>G‘N«÷•Ûºó Âu-HY¼{@%œd,BŸÀqH*„´ƒ.‘ü^‘ù$.¬ f¯‘wCæIc‡ª@Ê`Òì9È¢iP»zÌâ_`ÃAJY3 ªÛ@né| £ÒÙ‹~š 6#éП¦¢²:eå¯ÂµÚºI¿ø%2‹ûüÉz‡w(©)Î3BÑâÆQ7æøšÔ¾f½`o"éå„ ·³DèrmŤâå"7•¹¸Ï?1N0# *†H†÷  1•×Ï×&€”QÂ}XîמiQÀP0' *†H†÷  1PKCS#12 Test010!0 +\†(UG q¥Ã³Òåð+¿„øÂh•‰ª÷rmoscrypto-1.3.0/tests/fixtures/keys/test-third-chain.crt000066400000000000000000000110001421476274700232520ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIID+zCCAuOgAwIBAgIFAJOEAykwDQYJKoZIhvcNAQELBQAwgZgxCzAJBgNVBAYT AlVTMRYwFAYDVQQIEw1NYXNzYWNodXNldHRzMR4wHAYDVQQKExVDb2RleCBOb24g U3VmZmljaXQgTEMxHTAbBgNVBAsTFFRlc3RpbmcgSW50ZXJtZWRpYXRlMRIwEAYD VQQDEwlXaWxsIEJvbmQxHjAcBgkqhkiG9w0BCQEWD3dpbGxAY29kZXhucy5pbzAe Fw0xNTA1MTEyMTQyNTZaFw0yNTA1MDgyMTQyNTZaMIGgMQswCQYDVQQGEwJVUzEW MBQGA1UECBMNTWFzc2FjaHVzZXR0czEeMBwGA1UEChMVQ29kZXggTm9uIFN1ZmZp Y2l0IExDMSUwIwYDVQQLExxUZXN0IFRoaXJkLUxldmVsIENlcnRpZmljYXRlMRIw EAYDVQQDEwlXaWxsIEJvbmQxHjAcBgkqhkiG9w0BCQEWD3dpbGxAY29kZXhucy5p bzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMAKLNVdAlEUbBeAKfSz 3Ymd78tt6l2jjEdz4QMqFkYDSbl5pMB0RP+yRKHVSr+gL99F3wAXHoMb81bFVItL dyx3vK1qK2FhMvmuw8yNUy3o4aIwE/Neobp4eWLWNWwdqmPyNpvc8g8HdVsnk97u 9ZUGoiHEblvLZ8uijvbxK5hbCkKHjOhHCDT5egtPKOyFqOWJ1WzINICRfEeIjZM0 cuLUWVJmzb9QJ8CmN4O6R1rYK131eIZ6FWlJTpx1n7MvygTOmDiyVE0NMfJFTzdj a8kQxiwdyqN9I9OfHqRefh8TCQBuAguuwPUyAu9Ve45eDqzfeWrgoyinZ0KXiqrH 5DUCAwEAAaNCMEAwHQYDVR0OBBYEFEQ44OAmhb+Yhtwb4R31MjC+q6wNMB8GA1Ud IwQYMBaAFNIK/S4l0bch11B+u6R9vzTvUl4CMA0GCSqGSIb3DQEBCwUAA4IBAQAb Wq6ueTRDgY5hDAcVn3j5Eaco88rzhhzgfnH4GSES/QOQlOEFbj0/zzAk7LCgcXq3 7ud/cbA0tuoFmra9Q4D3YEcL0xHiDlXkvzcy2MJ6MysRiQftvHE9o1f8lxWqEfHK EtL3espMfVE8zisiA7kS07Jm4t7OSBte21JVwqC+dlqYca2T0coJ47Fw/yVvlaQU O5h6wvHuUS4L0myNdW+/V2yoUex8C9OK3qs8H61ULcgoVnvn/DJJerad53LBfJR3 jmmamq6Pu7COWU07N7MOGkrG8bwal64ncdzwvTtBj9NdoDataw9LvjyAGxYDSY6X KiqBZoOHm108hjAfW+XC -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEBDCCAuygAwIBAgIDGEN5MA0GCSqGSIb3DQEBCwUAMIGdMQswCQYDVQQGEwJV UzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czEQMA4GA1UEBxMHTmV3YnVyeTEeMBwG A1UEChMVQ29kZXggTm9uIFN1ZmZpY2l0IExDMRAwDgYDVQQLEwdUZXN0aW5nMRIw EAYDVQQDEwlXaWxsIEJvbmQxHjAcBgkqhkiG9w0BCQEWD3dpbGxAY29kZXhucy5p bzAeFw0xNTA1MDkwMDQxMzlaFw0yNTA1MDYwMDQxMzlaMIGYMQswCQYDVQQGEwJV UzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czEeMBwGA1UEChMVQ29kZXggTm9uIFN1 ZmZpY2l0IExDMR0wGwYDVQQLExRUZXN0aW5nIEludGVybWVkaWF0ZTESMBAGA1UE AxMJV2lsbCBCb25kMR4wHAYJKoZIhvcNAQkBFg93aWxsQGNvZGV4bnMuaW8wggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/PVOdogtqe2gqXZ20dWB5NFxj JX6z6A57zGqT11OfiNmckIqxMRK8eToR69/DJmVNbQTJy/cFexZ33db+UrtEItxO klFFbG/6TjmqsOYF87TkJqGX7zXs/Z7D1Xl0/2CNh6vPPPbi/o1FHtKnk4HYOnyU LRMxxtt06O/u//wdnWXiSAOKOasLT7eP6Fuok28+BeQ29GB3UNL8oC7biyhv89yN VhDlHAkk+zfrGP4dNqxOY8SkwI0GYki9skEckwf7Nyz+vA8zVJaIymte/eRicCFF YFxh+QtQhyxF7DwsB5/XIgEWKRXnd/ivyORMlIVc83CHCM4ryvXgG0+I7WuZAgMB AAGjUDBOMB0GA1UdDgQWBBTSCv0uJdG3IddQfrukfb8071JeAjAfBgNVHSMEGDAW gBS+QoU9zP/j+SgCj35YVrT9A1zqSzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB CwUAA4IBAQAKnbphxDozjob2qobtO5QRfZNwIANr/Pyz6SoD8UnJDfZAg9y1jb/I OutcURjYfV4vyLKI5oyhepyBMkuwvnB/lRpLERAwv5lJgq4sIGDn5Sp56yYcxazC QXLyG5YJ2Q29kVuq9//IE/dkLVaP444iJZZ7IgrGN4EFQwvFRVa6toeVpwzp8kkn 5eQaiKQYt/1dVsQ5mv8ksT/gNJRMD4xJ7xvlats+Jc6cyygJk18h+JqMYDoFTTu6 fJmjtnjPFg+XU0q9GnJfrW+FjxYGMv+5tv0pJrujEq7+8gz5A4viSCzHHoc4O/Qs 0Jt6dQ/xmGYswxjrR/ZgsSjPx3lib7XC -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIExzCCA6+gAwIBAgIJAL3l2aQQMVyCMA0GCSqGSIb3DQEBCwUAMIGdMQswCQYD VQQGEwJVUzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czEQMA4GA1UEBxMHTmV3YnVy eTEeMBwGA1UEChMVQ29kZXggTm9uIFN1ZmZpY2l0IExDMRAwDgYDVQQLEwdUZXN0 aW5nMRIwEAYDVQQDEwlXaWxsIEJvbmQxHjAcBgkqhkiG9w0BCQEWD3dpbGxAY29k ZXhucy5pbzAeFw0xNTA1MDYxNDM3MTZaFw0yNTA1MDMxNDM3MTZaMIGdMQswCQYD VQQGEwJVUzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czEQMA4GA1UEBxMHTmV3YnVy eTEeMBwGA1UEChMVQ29kZXggTm9uIFN1ZmZpY2l0IExDMRAwDgYDVQQLEwdUZXN0 aW5nMRIwEAYDVQQDEwlXaWxsIEJvbmQxHjAcBgkqhkiG9w0BCQEWD3dpbGxAY29k ZXhucy5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL1bKAd04uZK LAIqvVDeeBeq7FA2fpS5xkWcqHbarzvD1//EG/kCQirJr302nusjJFxdji3aVDRG Px0+WWwGajy+k2vYm0t7mSP/bmVGCM06ofvDZUMWV1Ld4SyInHruS1Qj4xHlB7/Z +mAWYpCudmAFIJEgtlHDzezqu6kLEVNB1lbLH+lPNyunwXC9FSYWhekjAyBaflFB koQV90jXfuTG7Ph0m4DAfZn5n5r/YpvmKEDkPkaW1mAt8qel4b8RklAh8t8vTSfv QuTeyw3GFcKe7KymKHIaDDxwwnALfGWNa3t7YoVZP9fVrghkR73DBCnHIx22uDHU TkwBmIdUL18CAwEAAaOCAQYwggECMB0GA1UdDgQWBBS+QoU9zP/j+SgCj35YVrT9 A1zqSzCB0gYDVR0jBIHKMIHHgBS+QoU9zP/j+SgCj35YVrT9A1zqS6GBo6SBoDCB nTELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEDAOBgNVBAcT B05ld2J1cnkxHjAcBgNVBAoTFUNvZGV4IE5vbiBTdWZmaWNpdCBMQzEQMA4GA1UE CxMHVGVzdGluZzESMBAGA1UEAxMJV2lsbCBCb25kMR4wHAYJKoZIhvcNAQkBFg93 aWxsQGNvZGV4bnMuaW+CCQC95dmkEDFcgjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 DQEBCwUAA4IBAQA/5fmvWSMRWqD6JalrNr3Saio5+c/LzxbQ7kZZX1EH5Bo+vhD6 Pfs6lrhzE5OVS86sOItPTtT2KQIE0wTxNfX9wQzjekH4Q2sZRpWaKvK6cH+YzqUK n3DiICpu8vau4wHDrlfV/C2XhKf5u5qIB+SyOhGDo+XhY0cjgh/FS2//1/qGxrfx TeOHxoRN5YH4yKNGNapL3XF+utpwVFddmSRUWCBr7Fof9RJlzPCcVP/svTAXDi/y dhHevPEr21oYqX0KnJJa0JvJx92K8YKFTVj2x5PPCn9qze5C/Re/JFbCIuwq7kcX 3WQRd+S9zHbtG/4g3DnGibdczSw9529fZZw3 -----END CERTIFICATE----- oscrypto-1.3.0/tests/fixtures/keys/test-third-der.crt000066400000000000000000000017771421476274700227660ustar00rootroot000000000000000‚û0‚ã “„)0  *†H†÷  0˜1 0 UUS10U Massachusetts10U Codex Non Sufficit LC10U Testing Intermediate10U Will Bond10 *†H†÷  will@codexns.io0 150511214256Z 250508214256Z0 1 0 UUS10U Massachusetts10U Codex Non Sufficit LC1%0#U Test Third-Level Certificate10U Will Bond10 *†H†÷  will@codexns.io0‚"0  *†H†÷ ‚0‚ ‚À ,Õ]Ql€)ô³Ý‰ïËmê]£ŒGsá*FI¹y¤ÀtDÿ²D¡ÕJ¿ /ßE߃óVÅT‹Kw,w¼­j+aa2ù®ÃÌS-èá¢0ó^¡ºxybÖ5lªcò6›Üòu['“Þîõ•¢!Än[ËgË¢Žöñ+˜[ B‡ŒèG4ùz O(ì…¨å‰ÕlÈ4€‘|Gˆ“4râÔYRfÍ¿P'À¦7ƒºGZØ+]õx†ziINœuŸ³/ÊΘ8²TM 1òEO7ckÉÆ,Ê£}#ÓŸ¤^~ n ®Àõ2ïU{Ž^¬ßyjà£(§gB—ŠªÇä5£B0@0UD8àà&…¿˜†Üáõ20¾«¬ 0U#0€Ò ý.%Ñ·!×P~»¤}¿4ïR^0  *†H†÷  ‚Z®®y4CŽa Ÿxù§(óÊó†à~qø!ý”án=?Ï0$ì° qz·îçq°4¶êš¶½C€÷`G ÓâUä¿72ØÂz3+‰í¼q=£Wü—ªñÊÒ÷zÊL}Q<Î+"¹Ó²fâÞÎH^ÛRU ¾vZ˜q­“ÑÊ ã±pÿ%o•¤;˜zÂñîQ. Òluo¿Wl¨Qì| ÓŠÞ«<­T-È(V{çü2Iz¶çrÁ|”wŽišš®»°ŽYM;7³JÆñ¼—®'qÜð½;AÓ] 6­kK¾<€IŽ—**fƒ‡›]<†0[åÂoscrypto-1.3.0/tests/fixtures/keys/test-third-der.key000066400000000000000000000022511421476274700227520ustar00rootroot000000000000000‚¥‚À ,Õ]Ql€)ô³Ý‰ïËmê]£ŒGsá*FI¹y¤ÀtDÿ²D¡ÕJ¿ /ßE߃óVÅT‹Kw,w¼­j+aa2ù®ÃÌS-èá¢0ó^¡ºxybÖ5lªcò6›Üòu['“Þîõ•¢!Än[ËgË¢Žöñ+˜[ B‡ŒèG4ùz O(ì…¨å‰ÕlÈ4€‘|Gˆ“4râÔYRfÍ¿P'À¦7ƒºGZØ+]õx†ziINœuŸ³/ÊΘ8²TM 1òEO7ckÉÆ,Ê£}#ÓŸ¤^~ n ®Àõ2ïU{Ž^¬ßyjà£(§gB—ŠªÇä5‚²ÆF*r¥yu ÐÂY',È,OóºÙ¡Ä­?vðŠժ \´F4)í¦*§¦@Ór°–å—·¨ì° Ó„ð#P3[i é×þÁTu™Y•¢”Ü«ñ›®š:ä÷umàa¡]Üø!.~b\ÓŠÉg»HÆ]Éý1be{lªê¦ÑŽ(c9Ñ­EüO³œmYÁ;VöhÛªÂHZÇ5ÄÔ¬Ç(¿eIÖZ{TÞ«ºø¬7tt™}Ó¯¾Šçp¼˜qéQî/^|ÍUÛ÷9¢ÅöŸ‹6Ç÷®C»ò2À<©šBO€ä¥šC>iI¡@òðB™b5J8‡æª$7þýæÔÚ¦½7rFÔ_žn::ï5Ðãì{òüõjÞ£o¬}nxc²v~Úˆë忏dF ýæZ`/Ç¿¨Þå"´ÔÜÂõE©ÒŒJ`À‡<¿Y‘¡N] @&\@ÅÐÓR€u£5\+{úùݦþ1«î›šÊ][­—|µBTÜT 4B3 3ނɼw4¼´’›i¥`»8t;1måsÈ–á@‘«£«{W2ŠšvNè ïÐÞA×,» a×:¡ ¼å³\IóÈ_¼maÊíb€†éðÜ%:TÌ"²ÂøHÐxxÊè² îò}t­ F0H4W3²õÇv‰e,¸~ãK!ûrcSs ѵ©ÊIb«/äïLâ’°Ä€…ÿÉ;Îþ¨Ü›r:4'ÌØ¢yä?Äã±6¦,ÝÏ¡Ë(;¯H‹Òu P›He(3c‘¨c>``o*š),ªˆ?vâIG²vÊï,Zˆ‰Çúš'ê:•x°^^ã0àq)¼ÅŽÌ®pG„Ì[Ø+ûn¾i-œÕ£‘†”åÆÌ5‘EèaY{“éõO5?ZÄr)Oâ“çl‘z—*ðÜžÏ4 8½ñµµ×4ª5½Òvíÿ¥Õ@$]í Ç–<q3Reh}«é#±h¨JÛS§dêk—ä:qõ3Skj rÙjaY5ýU:páfš´µ÷×sp¤f¯†nêož¦òœÀþj#Eqù´‹˜Óî!îïI¯W]‘k;?…-÷Æs“Søæ"¨ð™ÃÙ@ñ,&f Ôf71‹4‹¤ûe`Ï~ÈÑ÷~V€øWDšÞÌŠæ¨à=Î5öîX ÞùÂfyêQœož¦l»Mqt¹¾ZZ=ɯ3vÈÐÑçaI%“HI•FEþòÚ´ö 5‰¯0 ›«r“´o†ÎIE‡¥tÍ—¨—ˆœñÒ^tš q†\0økXIòÂw€0êörü×À¤Ãi€ãÛƒÄÎŒp§JÀß¾{H6åÚ„¿žÈÎ>*dEQ£¢=L½§=Ûj>bVë=§ŸA§·do)Ñ^CçTñ¤ä„™·Ì^×XßæÏ®|p ’Bv¾AOÏò¹Åœ»8ð 8 ?½æú-Ô[ ÀN†éh ½R 鉼eìèïN¦x)÷M)t\HyÍ7,gúš?œ"›§&.¼fµÿº,Œë¢èGÌÕÜ3B‡…ºÞbé.벜ÃLþ|R]ãFð0©w6§õ3<7áG[uVY.TÔØžW,ÂWpeÕ‰°–A+Ô ~Яº@K6¿Ù—|ÖÒŒ:BÉ£¨Â¯_­öµdBÃÂZ´çeóGÍ­O»ñøý›PÑj6Û}Kâ~ßvüö“Ñ2;€Õ6\·¶‘ÖKöç½5úÞYo+ÇílªÆ)ü÷àkTóÜ|âz^lqdIfìA`e¼·˜]Ej±§#ìôƳ ýíCÍ1AEbqýØvË.$tU%Ÿžeæ^RRŠˆ¨Ðº ž¬…õ%»Sn= ™÷DíÀ×j#üqó]"Ü=´UTå¹òÒ´G'Y'WTGÕy‰F¶nÒßÐOk<âqye…‚]Ô%#¥Ä*?‡m –¬£YÕq$ 8˜ è‡×¾-¬• —q¯×P8—›ØÓJ…ëÒ%–Éöò8M`d]•[]lë`¨ÿhLá:z¹p•{ÜÃÖ[·Kg51Í¥÷}ç½ òúú‹;%xœ“UlÄÍÉV¾s§.•iƒ"Gl ¸¶¸gMu¥à…¬ É`€¹“ž”^‚lWr}økæƒF*eŒOW!µDíášÛ¯¡ûÞμžmò³ ,+ú36u+7‘}G+©—%g$D´® Q:NôéMº)íšFA—¨ âZa“Ѳ¨ÍÏör ™dèœwD2\®=†ë6~áå5ï÷¾ç°/qôrY â´^š#ĦdDˆh›‘?zåêômߤwGugJ«—ÿjý•`ÒùAâÛÿu8,E?)´r¸¨ÞçI&mO·/Ð3°ÂäóF­]#et£ò]Böë3›‘ÉexÝJéxŸ£¡têÈ22ÀÒßX+ù‹Þ’aíš’Å,I!>–KÙÛTUçb1 ‚ ¥&! ôÔÌ(/¾Iˆå±7i‘Ûk‰›˜?ïõ´ýÓ±U5¹¶i÷;Ñy(ª$¤žj \Ê’Š`³Œæ©(^è©9¼“œkXj¸'Âf¿—µäÿKQGmš" ÿ®d·“@à‡š¤¨×.lƒÀð;W0àQ {¯ëc$ [üüGÆb¿&’&y[rÉ}?kö¦ V•œáb@îdz4\ È}••‘IÌoê–fe'á¼¹¶•šIîî–T~®aØæ H‡“pR“8…cËbµÅ.‰çDŠÐ5óHˆÇä[úg®ì+é}‰#Î/”/iòÕÍjÈ=î ‘X³‹o5 èø(lVµãk§¥`¬Ç5ä‰ÃÓ¹=n4ƒªøÓÉ` oJ ìþUì¢e]§Mã@þ\TÖf;ó¥J èÕm3Fßl‰J]–æ>ˆü€•!ktÝ¢yG€¢öåùét«Í;VÉÓÏ7wIè}«èRY±+½ÝÂ2Aõ¸¹)÷¬=Ï8Î 7›3þ¥Í ¢»ì„ø¢¾G}’ïM8~^]æŽ=_Bœô³ã“A¸'O=MîúD—¿¾ÚÇ "Ô~*¤(‹®HfÒϽ ûJÃiœTá!>ìBP3þ/¾ Õÿ1Ðkyx´ Î6O%¬Im÷´Ù·‘Äì¬?ËAÅ·T17N‚ÏÅKL šàª¤qùŒZ4þrÏ*7(»ˆÄgÄBBñ†ikî~-Ûž}pgr@Îê“qê.é$B œ¶RqÕ3wK@“˜ê¨D» 7E…bFÙU  Nø´ÄÂÆe3ÿÔ½¾ÿ@éêÍå§0©š#¿K@ÃÊ,7 ï­À¬j e§•«˜.¶¼+AÕíö8nWœâM‡7­µ½ãÿÏ,Ë:àÅ„,!6£ñÂÔVž}N㈹2¡øå¨’Øû,TÞ8–”Ü=¡¼…Êæ"o²mãw‹¦%‚§ ÙŽ?†î¾ÃýÀÌ—¥ujÃ¥_.)Ð¥¯å$ëž?è¡à)¦õq¶nLò…˜PjÓ}Ü;#](.5ÒUÜx´çˆTüdUó¶¿MèPÒ€+ºê'åW™F=oµÖ‡l¶1Ü‚5›¥öªWÒ~ÕÌíü@:|±™Àì6Ï´—‘ó{ÍhjÄš„†Ûh_ÐÉÏ;4øm~ ¡h_RM kϬb¹œ9Ž‘xÊh Ù2!Þºkÿâ!Ð,H´~ùÃð±ü"·g7§¨scÃr“¡ð-0‚A *†H†÷  ‚2‚.0‚*0‚& *†H†÷   ‚î0‚ê0 *†H†÷  0~!Œ‹öÁ°A‚È¢[ßÁt¦½æZ/ %~Óµ%BÌ_vˆ e£«ý=ˆ=CÚË©ûXð<êè;²ßÓÄ3‡)M  3Æxþ>(=3P1·bîØñÇÖx•³%^踰¿YÇâ>…êõ.XHÕ\[Í[hži€Iz7ÐEÕí2×ãPa‰Ñwª˜1aò¨Â‚Þ½½Ìoä}níWåñN‡˜¸#›Ù«§»Ã—µÈïfVÖƒY¨ö¥Ã~.ÞBXK9HuÂWL–‰:E™Ñ @”³¬}mYýôOZÿ@G7qWè+À@]["#Uƒó2DgÂàѦúc à $‰Qô”_K´ºdŒrT§‘ÿeeµ¶ïÕ6¦¤u%àK¿»òT|óxäIò Ñ2Ũðsø>UTˆA2û¹ ¸ì“PÇ/)*„b7ŸÐFKhêÕ5‘0Áù—ë÷’[*y¯s!‘%û0 ƒîV¤‹ú‡¼± Â#V&M°M²%–ÞÞ3–Ýi]02qòó%h'pú—JÌ­ô‹xûûô;C„EÍÃe ˜¼¬€ÄðKömür¨åÅ~¸æQgG#ê Ÿ‘äd ¶Y´š^ˆ-¬ˆç?¿Í WO:€+0˜ôòs[!U[|Ø‘©á@AGÑ4!ïCs´7ìZ3‰Ë\fóÛhZ»Ø‘ßCù6ðiÝ1½mÉFi,˜­”»0bs F9°N±RþB?síÒo ×o)¨'ŒÑ)kñW&¤fšØg½M(ž„I2üYf•@™÷ ?®÷äzîÖ^F@Ò“—úïH÷òÄmLÅXƒ+ʵ‡WLkm—™ƒ`HpðM=ƒrØéºÝžuª!¹”h4I‹Ñ3[Ç–éA•²#Y&‰'­=õЭvÙDäS‚*ýdi¢,)PІ}\;DžßÙŠ›õ^;äÖTÌŒ°Osu7U²]ÇòÉ»vR-ù\COÙ÷!ù nÚR€Âˆ{œ˜È³“UŒMr“WÕûhw³¤€nonœÅúƒÃA.è³¢²€»œ¨˜ ôÓ±(.£ßë‹k‰‡ý,Zç˜ÛK ª•Ò`G`#tÒ…è©ó3„i¨ñ¹æ|l |¼ÿæ’6qtÅêQ¡:ÊG#\§lÿQës¯ãsñÂŽ40;1#о LN­\¥À¾äGÀ†Üþiš«bÍZRQô…$ŒLò…@×2X¼D$ÖOXÉ$û¿Äå†ýª„•l¸ëäæœxÞMíÐɪJK®ÐØß%hïÛ-þƒZUW^Ú5¾%ºöo|ä‡qnŠànM‰u='¢6*äìe.1Γn‰œOz¿ô¨Ý©.âgmîJ ›ØˆIŽØÄHÏÁ|àT¶3%ËOç« }l"à)þþ&ú–4ôO\|†Ú4Ì—AJ¶5UaÑè¶PcK©6/íú@ ÿÛ4œèX <¢/f Oâžo?‚î¸Ö“ÑÉî%!¾r;t˜†Qv«{À€ Åð‘•F”Ïömv_¹)»ž‚YC‚ë¶™.¨s)È=Ö;$ÈHW@ š5«Z,U˜k´r¦cËaZ¾~—u)Ýiˆ¡LME ¶Hxß”4nâDå¢4Oƒ<Žå¦‡2 S˜Ê°é$¯û½ïcw•ÿ8ÿµPÆvï¾…˜ûÜÜ÷;ì5WV‡çÊߌ-àAû¿ Ûs1®ºœœ–8 ,W È4]¢Yz•ÐÝëñn™­§û ñòZ΄iÜÇÚ€Ü44¤š‡Åøõà5"Ô ùOá*2¤Üú1ò0.Á!Ûù%€h"ü•9  ÎhˆR¨ðøq{ñDU¸äçA‘¤Jõš8¬¨¹eûóžüÞï­x2o>.æoHÙȵš7Ÿ€°’!^¢ÛdòUËÈwï™ï¦²l¾L¬Ö\<ë­ á_ëfWM5Ú4„@Sž˜ÊQA_´Çø,pbÑ×n Ω!­ñT<ªê|¯XÂê— —Ž×_Tƒ½ÿú¢Û¤éb¡Ã3r?¡ÏbÙ…ëH’®ÝÞSÁ\X¿ÏP©Äæ¤(+`­åÆä"ì^ò³Î Ä5ë]ÿˆ;Ü'Ž Ñã)ú“óTÈÃØŒ€{!vÇ*qŠ„ Á6 ¼Èšø*te„å÷EœE;á–Ú{4.Y±i¨ ç+h{³}ô‰üyêllõêÎV÷`‡3ü"vê ˜ázÌ–îSq\û.+ؽlq^çÆN*4#tÙ˜;d%ºñFêìþ@©.ËóÎñ¢ûþn± –PòÅ Àýr9 ­êx¸‰ìçXX±™ÇƒÀeôѤ åÓÎô†§R6Ik‘®ùÂ"üz‘­/ÑwêÜœ?‹ÜÓ–Êå¢<’nBÒØýÄ}3D‚v¶ÛCR"æ;3Œº´íÕC_>븗ª¼NZq°.®f8¬\l*m$ý½zf¬ºõ'˜¼7È(ð&ñ!igàïåÖФ¤x|Ç+亇›ˆ©Q¹%)gœø…äÖG ÕÜòî«‚H ˆqa¸!ƒCÌ‹nQ©ÑŠÂÏL€JþT~mX×!¬ÎÒ>ªMym=;3!É~;K ç8eŽ{ûùɧé»[§Õ¨ØÎ6üH¬¼ÿ¯|Ýû¹0Á! 0‚j *†H†÷  ‚[‚W0‚S0‚O *†H†÷   ‚î0‚ê0 *†H†÷  0˜ ´¡_ɂȠ¹¾@ƒØzvì\Öž>,°w™sFFp áàÉÆéíª¹:IëtO)$„ãn•ŠEHiî¾ßÙ»¼‚I$¹_·¶±qª9^wNyÛ?®,Z'«©–ž…꣖ZÔš¾Yk2k X_‘OU/Ã¥^›iV’v£"ÒhQ)±µ'5ÑßÕà#ÕN¼½|ûªj½dé@Â×FÖ“œÔ}\RL1ÙGK€ÌK²6,:"?ü_è ìEYÁ¡þ ÛIÇ5ÅkËͽo Àé…´¦€ÏÌDÙm´°ºë·33Us¾Ó·³³LU£•5Ʋ X7Bä’¾$YYŠ ¥†Ëå`‡9ï?^]ìr»ª¢ÿ’þê¬þ¾ó9¼{ÌßᬢŽäøÓ¯­›ùaB±£í÷ ŒP4êžÎ˜¾¶–æ# PÐÁ²’*ã5Q®yy_.±ßƒìÃÏ^Œ©ò¾Þ}Í SŒÖ†éžK¹o!L»ppÌýÎzªEéžMQ%L8I¼½ù6Bc5#iê&Ùmƒl½!ey#êçê„ê”5êèÅ}óEŽð¤ŒÝ·°LFzÇS¡1ÎkZNÜ]~›*îa-ÁF‰ˆò!gä˜ö…ÿ“±¬ B+ ñ"] ³Âˆ½Ã‘NA#Ø‚ j‘pÙ¼¾_s“’YU“ùZZÒ^‡SKÑêŠ,Lí¼Ð ý6ë ozéö½:MRžHÂÕ=Æ×4¬ŸeX ôÛáf¦÷»»D.“yQsN‡Ôø*•vDE˜œ´t\Ř‹$= ¦—£UýðÛLÔ1 „¨f‚§ÑkDÊÍæ6[ó@¬¡¡ ‹¾Ý»¤vì3)û!!ZÒó¸ðñúàq¾9ä€ 'XÏeœ]ƒrÝsyb±Ÿa¸d»LîÛxó1„Mœ’’œêïÌ{÷¸”*›FHÃ+ÎÃÎ$¿ßálcyÂåˆætÀœÅ§¢,Šš R6÷É®q~ØÄWøs÷lž8É/-¯žÄ©pìé‹Ðôj¦Ì$Š2¹šøí2à½gpFȚ̨œ¸’×%<Æ¿ Á â? ŸGòª÷¯”âbãU®Y9ö à³À5zxŸ¢`QÓ„Ò7ñß{{!Zsux˜0ŠU&™µ$èÔ\0ÊkÍÓ§ˆ‰‡Ž<›à% 6#ê®û°Ü–íò*‘ñB`ÞGS§ G÷¨‡r ®16̂ʞ-;ËD‘_p0£jR¨qµ‚ä¦â¨)œbŠ+Ó‰—ÊÛcüA û3ûlBÿ­”6cgz½]NÓ×5»AÖžÃùOô¶VƒÊ&yfRr»7’ôì²*æ@H¥×ÚwÊSÁET‰rúu嘖áåÿØf‰y.-@˜ €›QDÔö³7WœÀI ðCä;®ÊÖÈÖ»íÃÈqžEX"¥2Ã^øæƒgó<îŸ5dÞnq‚1‹Õ›¹ 5‡—œLÎØ¢g0Kü!ßËT4Ÿhz.+[U<ÑË-ÚѳžÌ]§P¤ÚìP¹ `D‡>vÞêU3„”'.«»øezÀÅ"n0ÃúHiÌe^á-8E€AÿZ®#C¡ý(YbC‡R2ZÍOÒ£M¶yNa" óö\'ÒAà£pÞ­'Ëié ¶hO´}öùÑÛ740ó3ˆ¡!³6=dÔ²35Ü!'ò•gÕG«L¼¾î<’ï@AL¨²¨ $ÒhxŒ¼ÈÙÙñ_¸„øM@d¼¦¥-_IŸM¨¥©C”H2¾“ò¹ÈPÂ1ø~YÈT}£ýÎTïùˆpòX¿û‘¶¥ô¶Ëÿæ½XÌØ3Óèa$·ÓÆj÷êÝö„h#ОÁe´MÐ6ûSÜŽƒU×5'ŒæKgpÐõßóvM„Ød•Û·Y…—³À®1KäÛ¬ËHZ³Óu¦º80¾ÓV‡¬‹‰¾øX{šèÊ’ûÃöà™Îq¼|ú<‡³¹kã ²C©OST|ãØj·òîx¨²/ñ`´³66þ¬Ö[VEü¾á`ô\ª«èU™¤ X¤§XÊÂ9â[ÚÅÎY“|á£gG¿>Q“:vOh•|2¶¤úÄÊC¶[–7¤¶EèÊf™“KϬ´7;þ/­ÚeŽEímø0§ÈYAŽ2/$`×<¥Ð²Z^ ÞŒ'¾2 ¹as3¬[¡è¼ãÀ‚q1bŠ7úAÇrrØÚ|§-I—#)m̲&¸;I1P0Ö# â7´Š\+h™Í/–šAb*QÉ›&¨RGù+µC‘#¿F’D`€ Œ› Es‰ˆ- ó‡\7ö½ß p¼Ü# ±ÿç³ÖÓˆý*WÈfAÎçŠHÓRÖ=§1—Šæúµ!©SÍ*I-mâ?8fQM£ ¾ÛòÖÒ"ó[¶L†"¤6}­3îþ ³* M°ý*l1ºÒz½:·~¼J“q=…¬\ãÈN8à„ÞìCÖ¦)¦Á™B–Ÿn»™ÏAÛtà;úÌ ZŒŸ|@1Ùøqž¡ëÛ ,¬sVI’ò­ÞC”[¨ÜÃCHòÀ&<¬4pI»â¡šLÕnÎ5™ýa7M(ö¦1^Wf?Á.‹XÿÓiC\ešáÁNby€•É¥°×ŸÁ/ý Ô…/ZIúºÏÕÓQ"h^1é÷סŪPŒß Ìs|ƒiŠ?=ÔhT¼Ùɱª/µ¬¡©’~.ü¯}…^~ü-WbúKæÿÜ3>,Õ‰ûL4"'%& à)‚!u.Qçz)è;éi =Mm,\·e‹nµ@?Œ”+ÀÍ ’wYĈß ê¬ç­Ò©³¶¤ò¨N‘_n‘± ŸjÁ?µ”¡²òy½ccŒ0Ɇ®q‹5äÈð²täÜ®rñÐC{w £v¼ÜƪvzEóß:å8Œ'(§¼.¬Yï·¬ÃøÇf5B±Q3]ÙW/þ¯I>¬úgj*Ô˜2ÅÙ믅Qþ¤ .9¡Ïª¦€³VW ª½7]4Ñ¡ÐJõ5ãjˆGÙDŽ“ó²E8H47À©…iª'E@]Aùå9þžŽÆ¬V½”Èãm~Tñëa°/Ý]ÑbÓ½·¿ÔÿQ)ÙË2º %ñÀÞ˜Ïw~rÒÆj*8í­Dß„¨/C¦v Åîi64£ö#ˆšB»GöY›ßqUYy¯­$}§:Nß®%ï^ž7üN» ,VoõÄ]U.Q–ŽÀ/¹…qhg¸;á‹%Å!5“wLØkÁ͉·Â§ùÛ;oKñŸOIçâKdÅŽŠ¤ÕóŒ’=^SSGä¡3†‘…ˆ£+?ÝS9!@b$—' °Õø«‘m;á>·ÅT¥U/4ó)!8ЉY¨YÓlãHɶ¢e4Õ)Jüý³ 25˜q G· 'NÑïY?úM§¦[48ÿJ¯88 n"ôàqâ TFx7 E¡ì¶w þT'ÙÕ½|µ;g ¿ß e]ô¸1N0# *†H†÷  1•×Ï×&€”QÂ}XîמiQÀP0' *†H†÷  1PKCS#12 Test010!0 +— Í[ Tfw]^ZÆxä[KsVøoscrypto-1.3.0/tests/fixtures/keys/test.crt000066400000000000000000000032641421476274700210770ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIExzCCA6+gAwIBAgIJAL3l2aQQMVyCMA0GCSqGSIb3DQEBCwUAMIGdMQswCQYD VQQGEwJVUzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czEQMA4GA1UEBxMHTmV3YnVy eTEeMBwGA1UEChMVQ29kZXggTm9uIFN1ZmZpY2l0IExDMRAwDgYDVQQLEwdUZXN0 aW5nMRIwEAYDVQQDEwlXaWxsIEJvbmQxHjAcBgkqhkiG9w0BCQEWD3dpbGxAY29k ZXhucy5pbzAeFw0xNTA1MDYxNDM3MTZaFw0yNTA1MDMxNDM3MTZaMIGdMQswCQYD VQQGEwJVUzEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czEQMA4GA1UEBxMHTmV3YnVy eTEeMBwGA1UEChMVQ29kZXggTm9uIFN1ZmZpY2l0IExDMRAwDgYDVQQLEwdUZXN0 aW5nMRIwEAYDVQQDEwlXaWxsIEJvbmQxHjAcBgkqhkiG9w0BCQEWD3dpbGxAY29k ZXhucy5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL1bKAd04uZK LAIqvVDeeBeq7FA2fpS5xkWcqHbarzvD1//EG/kCQirJr302nusjJFxdji3aVDRG Px0+WWwGajy+k2vYm0t7mSP/bmVGCM06ofvDZUMWV1Ld4SyInHruS1Qj4xHlB7/Z +mAWYpCudmAFIJEgtlHDzezqu6kLEVNB1lbLH+lPNyunwXC9FSYWhekjAyBaflFB koQV90jXfuTG7Ph0m4DAfZn5n5r/YpvmKEDkPkaW1mAt8qel4b8RklAh8t8vTSfv QuTeyw3GFcKe7KymKHIaDDxwwnALfGWNa3t7YoVZP9fVrghkR73DBCnHIx22uDHU TkwBmIdUL18CAwEAAaOCAQYwggECMB0GA1UdDgQWBBS+QoU9zP/j+SgCj35YVrT9 A1zqSzCB0gYDVR0jBIHKMIHHgBS+QoU9zP/j+SgCj35YVrT9A1zqS6GBo6SBoDCB nTELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEDAOBgNVBAcT B05ld2J1cnkxHjAcBgNVBAoTFUNvZGV4IE5vbiBTdWZmaWNpdCBMQzEQMA4GA1UE CxMHVGVzdGluZzESMBAGA1UEAxMJV2lsbCBCb25kMR4wHAYJKoZIhvcNAQkBFg93 aWxsQGNvZGV4bnMuaW+CCQC95dmkEDFcgjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 DQEBCwUAA4IBAQA/5fmvWSMRWqD6JalrNr3Saio5+c/LzxbQ7kZZX1EH5Bo+vhD6 Pfs6lrhzE5OVS86sOItPTtT2KQIE0wTxNfX9wQzjekH4Q2sZRpWaKvK6cH+YzqUK n3DiICpu8vau4wHDrlfV/C2XhKf5u5qIB+SyOhGDo+XhY0cjgh/FS2//1/qGxrfx TeOHxoRN5YH4yKNGNapL3XF+utpwVFddmSRUWCBr7Fof9RJlzPCcVP/svTAXDi/y dhHevPEr21oYqX0KnJJa0JvJx92K8YKFTVj2x5PPCn9qze5C/Re/JFbCIuwq7kcX 3WQRd+S9zHbtG/4g3DnGibdczSw9529fZZw3 -----END CERTIFICATE----- oscrypto-1.3.0/tests/fixtures/keys/test.key000066400000000000000000000032171421476274700210750ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAvVsoB3Ti5kosAiq9UN54F6rsUDZ+lLnGRZyodtqvO8PX/8Qb +QJCKsmvfTae6yMkXF2OLdpUNEY/HT5ZbAZqPL6Ta9ibS3uZI/9uZUYIzTqh+8Nl QxZXUt3hLIiceu5LVCPjEeUHv9n6YBZikK52YAUgkSC2UcPN7Oq7qQsRU0HWVssf 6U83K6fBcL0VJhaF6SMDIFp+UUGShBX3SNd+5Mbs+HSbgMB9mfmfmv9im+YoQOQ+ RpbWYC3yp6XhvxGSUCHy3y9NJ+9C5N7LDcYVwp7srKYochoMPHDCcAt8ZY1re3ti hVk/19WuCGRHvcMEKccjHba4MdROTAGYh1QvXwIDAQABAoIBAE8NH0j9ozxA+t5s uVxpg/ldggp6tZ2hcQTewfXclgt9V0+Pr53lM3ppeLntc6r2oNdut0ytOToZmX+7 59kRVIjHhwQfCbYZg3VjzdK5yjLjp3xTtpKrYQlXWAoffjRUB165HLL7yqBtf/ld XwjHzOOJQG9WGMdJ105xMKcB19nJijgY19Eg/9svNQq6wwfDACKAYXusoz8ApGoz T6b8+o2nINQHaCgmf5qPbg44JfX3+k2er7bK+mk7cuoOIWOB5ItSfyq4GdbLM+XW /GIGkclXUOWRDmitudoLKtv5WtvYrsWe1iznHIrlIy7gI1i9Z+V9mEYb+aZPDtjB 4Kuy/gECgYEA7VBSov4Oph78yiIIwmJ6T+4+tdbJQ2lxV7GusSVjj76vmZASNJ7E gpR5woH1APEyHgnUkoRK8fGUHp+Lxms2rrYivcjXZ4DTvpZ/qQVJBNJ4pVyWn+iq 7gLro2wotiLDBzeG9oo46Czn0QCOk7tofxSbcEkhkO+Uuwc3ZSlEOfcCgYEAzEQf HQNFzBHqlvPVbfy9+IxnG+u33WEKSMr+1ek/hae43+D4vndBX1DPuhaWUapsU9Gg A5YIXOhCIrnx4MbMvcBn55CWIf7J+dEdPaUOGN+YtYbV29U45nX0dS4QXxBOVZwn qUBRDwptSFVaLHDUrOC4vnkST6iRHh/OJ5AuG9kCgYEAm8+SAiwWSCGuTbSc1au8 rMA68j7sc9NGNJKXpP1sahODzapXGa9oTGfZrciPqSezhR9lLzGm10WKv7R3HDaG d51kIAE+1Fk0LT044itzLrRVvBSXXLRxjcXjGrBH5pXaQOHHPhWwmVfqeEIKWprA WDeaetW5MSTsHQP27fdzMS8CgYA4DXl8PKmqlkAJrF+lDvYSfnTM9KI/3aE02H+V s6v6wUu6I8IeghsuTL60Ef6t6lZPqfZ/BWzGEfYUEXKOe/8zEtlwcfzA12oVY4zi naiAqtr89UM6UAiNNVEf1sQnUhIs6+z2RO/5cKMMdl+IUm4KAqCvpAmiUl+AJLot oSMGAQKBgQCcWwsTbMnFJrBxHVe05C8Y/DeU1Cj7TlKKuYSidASus0KhJv7bkSIE N3x3RJFDVrkEW30AH/b+oMKHSjileWP7NLCT4Y/d/ZybhavhOziol408GsZNe6YV Tzk/USt/a4ZBK1K5RlHmTiFYNXvecXqzHnk77OzCMsJtteW/gr1ABQ== -----END RSA PRIVATE KEY----- oscrypto-1.3.0/tests/fixtures/message.sha1000066400000000000000000000000241421476274700206240ustar00rootroot00000000000000zD“MMVTmZè'¾ð8Ü>oscrypto-1.3.0/tests/fixtures/message.sha256000066400000000000000000000000401421476274700207760ustar00rootroot00000000000000*~Ë¥aOpíõr,8<° HðI_{F­>²J Üoscrypto-1.3.0/tests/fixtures/message.txt000066400000000000000000000000331421476274700206070ustar00rootroot00000000000000This is the message to signoscrypto-1.3.0/tests/fixtures/rsa_pss_signature000066400000000000000000000004001421476274700220760ustar00rootroot00000000000000`D@“T*鼃е£P(%¶Š^GEèÂ{¯aؘ‡ö¤³SM¯þÏa@!_ '8ÀNz\Œß}H¾ž]wB²>²Ú˜€E¼õf >Æõæå8¤a£e«‚]ÑY0ñb㬂õ8ÜU–ŒÄOS]jìç¢éQâêóƒYä—¯\ÿºˆâÙÝ6ôó­™"Ĥ5òn6¥%¡ààN´`g,E©]“¨¢@ª¡qŸ>†+ ÔR›9ª:vF¬œ…¯KW»´€æ†bMJÐgéò—-[ƒäáö(™l˜‰±¦êBܲ’ gῬ…Ë2õ&þªoscrypto-1.3.0/tests/fixtures/rsa_pss_signature_pss_cert000066400000000000000000000004001421476274700240000ustar00rootroot00000000000000TÄF ý]5ÕPlñѨ9-°’ÞNÝyÌ=ñÚˆ˜)­Y«=¶~K>[ŽGG™)ÂÒpFÆï)Sq·‰“zjÔ É¶ºyY‘+ùz>¶`£ã_ç…6z¿2Iß$-±ë\‰ *~Ý16j/KÀF 6éœYÇ» úE¢šcÅÊ ÷?E>ëÔ4dŠ!§AWu–ÉWkæ>¹X>}û蟂¥„Ì_*­ˆwŠ7Æ".}xÐÎ…b4Óª•}ùSôº¶Ñp áž’r2'GYZZïç…ð ¢xUkÍ€ˆ¨ ü^Omûe‘Œ'9¨;ó—i5³®n7B T­ûÄ vkß“œ#Oj¸è=_H*E:vo¶à&O»mËEK†´øh×óŸB%ü“gúàPt Œ—\kªÀPñÜ‹EÂïlmUôÑ^<¾5›…ŠÀésJ/ÓßÝXÞ¿¨DÓá2¬§Y­ é[ùÁÿé¼fúºK¥¨-y÷&¸ú{ɰޖWž‘‡0·éŠhÍ­¬4£ü-«¾€’ú›¼oscrypto-1.3.0/tests/fixtures/rsa_public_encrypted_oaep000066400000000000000000000004001421476274700235470ustar00rootroot00000000000000·b;;{ÕeØ\õTqFêlïÊLï¿[­.abÿâ»SvwÈâèfBè.±1ùv’Yá¬@Ñ8èÊíÝ{næÞ]\¤îøT‹lúrÛ?dîdøllã²^N¢íŸÚ8ÄÉ®éEÂè)Û{kÊ6X»çTMز7Á¸Œ@iZú±Û›;EhÛäïXü³†¡âψœ”à ì¦AÜ­ÖÐ3ËIù@aöl†ônÀÈ?­®v›wŒ<Èi“Z…9Xhþùãåp'IklVµ¢Mhñd=°Àx£a!Q½²¦«’øñÌõ|nw‹I!TìÄß«ö×}ûHËoscrypto-1.3.0/tests/fixtures/rsa_signature000066400000000000000000000004001421476274700212110ustar00rootroot00000000000000BNù Òš½)¼­Z bÁr]PËh3mËçàô%>æ€@2‰—€{hÆJ%¹Ž™iuÉá›KÐ%›(¥òRO4.MªFSl‹q²ô×î9ŸY—À"0,GVÁ„æ!`äáAè’8sì€}òx¯²Ì©=Df,Pr•ý¢gwíRƒbǪi®°Ü "{2„*1Ét.%ÐÕXÛ*k²Ÿì2Ïú¹t†:ÅV –T[=0¾Á³7wÇj,/ë[©¨pœŸ4ûP˜åÚxZv9ø®©Ï_ƒå½\8 ¾>emà÷bÌ< èK„þaÈ îGÛyé•>»ùÆ:T„ oscrypto-1.3.0/tests/fixtures/rsa_signature_raw000066400000000000000000000004001421476274700220620ustar00rootroot00000000000000W«°•Ÿm-ðSVDã/ u/ãWáéJk­ë}1T\0ÃÛ†Â33sjѱ¯}’¼]Ûx¹c‚W‡½0/N•,ª+­‚¿óZÍ—·‹>2ÀmèÇ-›!ÕI :7×ÝQ–M®°˜­LŽ&âšU|­Cèðо‰~ló`<®h¸™1h/o÷NYÎ7gš.”pW$¾Ïêú³KQ¿œò¤£ÌYWû•6LäÛ\)CKNþn:6¿Š4i$¥üŠX¸÷$ã…¦Ê7˾Öoscrypto-1.3.0/tests/readme.md000066400000000000000000000002221421476274700163300ustar00rootroot00000000000000# oscrypto_tests Run the test suite via: ```bash python -m oscrypto_tests ``` Full documentation a . oscrypto-1.3.0/tests/setup.py000066400000000000000000000107321421476274700162720ustar00rootroot00000000000000import codecs import os import shutil import sys import warnings import setuptools from setuptools import setup, Command from setuptools.command.egg_info import egg_info PACKAGE_NAME = 'oscrypto' PACKAGE_VERSION = '1.3.0' TEST_PACKAGE_NAME = '%s_tests' % PACKAGE_NAME TESTS_ROOT = os.path.dirname(os.path.abspath(__file__)) # setuptools 38.6.0 and newer know about long_description_content_type, but # distutils still complains about it, so silence the warning sv = setuptools.__version__ svi = tuple(int(o) if o.isdigit() else o for o in sv.split('.')) if svi >= (38, 6): warnings.filterwarnings( 'ignore', "Unknown distribution option: 'long_description_content_type'", module='distutils.dist' ) # Older versions of distutils would take a glob pattern and return dirs # and then would complain that it couldn't copy a dir like a file, so we # have to build an explicit list of file names data_files = [] fixtures_dir = os.path.join(TESTS_ROOT, 'fixtures') for root, dirs, files in os.walk(fixtures_dir): for filename in files: data_files.append(os.path.join(root, filename)[len(TESTS_ROOT) + 1:]) package_data = { TEST_PACKAGE_NAME: data_files } # This allows us to send the LICENSE when creating a sdist. Wheels # automatically include the license, and don't need the docs. For these # to be included, the command must be "python setup.py sdist". if sys.argv[1:] == ['sdist'] or sorted(sys.argv[1:]) == ['-q', 'sdist']: package_data[TEST_PACKAGE_NAME].extend([ 'LICENSE', 'readme.md', ]) # Ensures a copy of the LICENSE is included with the egg-info for # install and bdist_egg commands class EggInfoCommand(egg_info): def run(self): egg_info_path = os.path.join( TESTS_ROOT, '%s.egg-info' % TEST_PACKAGE_NAME ) if not os.path.exists(egg_info_path): os.mkdir(egg_info_path) shutil.copy2( os.path.join(TESTS_ROOT, 'LICENSE'), os.path.join(egg_info_path, 'LICENSE') ) egg_info.run(self) class CleanCommand(Command): user_options = [ ('all', 'a', '(Compatibility with original clean command)'), ] def initialize_options(self): self.all = False def finalize_options(self): pass def run(self): sub_folders = ['build', 'temp', '%s.egg-info' % TEST_PACKAGE_NAME] if self.all: sub_folders.append('dist') for sub_folder in sub_folders: full_path = os.path.join(TESTS_ROOT, sub_folder) if os.path.exists(full_path): shutil.rmtree(full_path) for root, dirs, files in os.walk(TESTS_ROOT): for filename in files: if filename[-4:] == '.pyc': os.unlink(os.path.join(root, filename)) for dirname in list(dirs): if dirname == '__pycache__': shutil.rmtree(os.path.join(root, dirname)) readme = '' with codecs.open(os.path.join(TESTS_ROOT, 'readme.md'), 'r', 'utf-8') as f: readme = f.read() setup( name=TEST_PACKAGE_NAME, version=PACKAGE_VERSION, description=( 'Test suite for oscrypto, separated due to file size' ), long_description=readme, long_description_content_type='text/markdown', url='https://github.com/wbond/oscrypto', author='wbond', author_email='will@wbond.net', license='MIT', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Security :: Cryptography', ], keywords='crypto pki tls ssl x509 certificate encrypt decrypt sign verify rsa dsa ec dh', packages=[TEST_PACKAGE_NAME], package_dir={TEST_PACKAGE_NAME: '.'}, package_data=package_data, install_requires=[ '%s==%s' % (PACKAGE_NAME, PACKAGE_VERSION), ], cmdclass={ 'clean': CleanCommand, 'egg_info': EggInfoCommand, } ) oscrypto-1.3.0/tests/test_asymmetric.py000066400000000000000000000716511421476274700203550ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import unittest import sys import os from asn1crypto import pem, algos, keys, core from oscrypto import asymmetric, errors, backend from ._unittest_compat import patch patch() if sys.version_info < (3,): byte_cls = str int_types = (int, long) # noqa else: byte_cls = bytes int_types = (int,) _backend = backend() if _backend == 'openssl': from oscrypto._openssl._libcrypto import libcrypto_version_info openssl_098 = libcrypto_version_info < (1, 0, 0) else: openssl_098 = False tests_root = os.path.dirname(__file__) fixtures_dir = os.path.join(tests_root, 'fixtures') def _win_version_pair(): ver_info = sys.getwindowsversion() return (ver_info[0], ver_info[1]) def _should_support_sha2(): if _backend == 'mac': return False if _backend == 'winlegacy': return False if _backend == 'win' and _win_version_pair() < (6, 2): return False if openssl_098: return False return True class AsymmetricTests(unittest.TestCase): def test_load_incomplete_dsa_cert(self): with self.assertRaises(errors.IncompleteAsymmetricKeyError): asymmetric.load_public_key(os.path.join(fixtures_dir, 'DSAParametersInheritedCACert.crt')) def test_cert_attributes(self): cert = asymmetric.load_certificate(os.path.join(fixtures_dir, 'keys/test.crt')) self.assertEqual(2048, cert.bit_size) self.assertEqual(256, cert.byte_size) self.assertEqual('rsa', cert.algorithm) def test_public_key_attributes(self): pub_key = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-public-rsa.key')) self.assertEqual(2048, pub_key.bit_size) self.assertEqual(256, pub_key.byte_size) self.assertEqual('rsa', pub_key.algorithm) def test_private_key_attributes(self): private_key = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) self.assertEqual(2048, private_key.bit_size) self.assertEqual(256, private_key.byte_size) self.assertEqual('rsa', private_key.algorithm) def test_cert_ec_attributes(self): cert = asymmetric.load_certificate(os.path.join(fixtures_dir, 'keys/test-ec-named.crt')) self.assertEqual(256, cert.bit_size) self.assertEqual(32, cert.byte_size) self.assertEqual('secp256r1', cert.curve) self.assertEqual('ec', cert.algorithm) def test_public_key_ec_attributes(self): pub_key = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-public-ec-named.key')) self.assertEqual(256, pub_key.bit_size) self.assertEqual(32, pub_key.byte_size) self.assertEqual('secp256r1', pub_key.curve) self.assertEqual('ec', pub_key.algorithm) def test_private_key_ec_attributes(self): private_key = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-ec-named.key')) self.assertEqual(256, private_key.bit_size) self.assertEqual(32, private_key.byte_size) self.assertEqual('secp256r1', private_key.curve) self.assertEqual('ec', private_key.algorithm) def test_dump_public(self): public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) pem_serialized = asymmetric.dump_public_key(public) public_reloaded = asymmetric.load_public_key(pem_serialized) self.assertIsInstance(public_reloaded, asymmetric.PublicKey) self.assertEqual('rsa', public_reloaded.algorithm) def test_dump_certificate(self): cert = asymmetric.load_certificate(os.path.join(fixtures_dir, 'keys/test.crt')) pem_serialized = asymmetric.dump_certificate(cert) cert_reloaded = asymmetric.load_certificate(pem_serialized) self.assertIsInstance(cert_reloaded, asymmetric.Certificate) self.assertEqual('rsa', cert_reloaded.algorithm) def test_dump_private(self): def do_run(): private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) for password in [None, 'password123']: pem_serialized = asymmetric.dump_private_key(private, password, target_ms=20) private_reloaded = asymmetric.load_private_key(pem_serialized, password) self.assertTrue(pem.detect(pem_serialized)) self.assertIsInstance(private_reloaded, asymmetric.PrivateKey) self.assertEqual('rsa', private_reloaded.algorithm) # OpenSSL 0.9.8 and Windows CryptoAPI don't have PBKDF2 implemented in # C, thus the dump operation fails since there is no reasonable way to # ensure we are using a good number of iterations of PBKDF2 if openssl_098 or _backend == 'winlegacy': with self.assertRaises(OSError): do_run() else: do_run() def test_dump_private_openssl(self): private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) pem_serialized = asymmetric.dump_openssl_private_key(private, 'password123') private_reloaded = asymmetric.load_private_key(pem_serialized, 'password123') self.assertIsInstance(private_reloaded, asymmetric.PrivateKey) self.assertEqual('rsa', private_reloaded.algorithm) def test_load_rsa_pss_cert(self): cert = asymmetric.load_certificate(os.path.join(fixtures_dir, 'keys/test-pss.crt')) self.assertEqual('rsassa_pss', cert.algorithm) self.assertEqual(2048, cert.bit_size) def test_dh_generate(self): dh_parameters = asymmetric.generate_dh_parameters(512) self.assertIsInstance(dh_parameters, algos.DHParameters) self.assertIsInstance(dh_parameters['p'].native, int_types) self.assertIsInstance(dh_parameters['g'].native, int_types) self.assertEqual(2, dh_parameters['g'].native) def test_rsa_generate(self): public, private = asymmetric.generate_pair('rsa', bit_size=2048) self.assertEqual('rsa', public.algorithm) self.assertEqual(2048, public.bit_size) original_data = b'This is data to sign' signature = asymmetric.rsa_pkcs1v15_sign(private, original_data, 'sha1') self.assertIsInstance(signature, byte_cls) asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'sha1') raw_public = asymmetric.dump_public_key(public) asymmetric.load_public_key(raw_public) raw_private = asymmetric.dump_private_key(private, None) asymmetric.load_private_key(raw_private, None) self.assertIsInstance(private.fingerprint, byte_cls) self.assertIsInstance(public.fingerprint, byte_cls) self.assertEqual(private.fingerprint, public.fingerprint) def test_dsa_generate(self): public, private = asymmetric.generate_pair('dsa', bit_size=1024) self.assertEqual('dsa', public.algorithm) self.assertEqual(1024, public.bit_size) original_data = b'This is data to sign' signature = asymmetric.dsa_sign(private, original_data, 'sha1') self.assertIsInstance(signature, byte_cls) asymmetric.dsa_verify(public, signature, original_data, 'sha1') raw_public = asymmetric.dump_public_key(public) asymmetric.load_public_key(raw_public) raw_private = asymmetric.dump_private_key(private, None) asymmetric.load_private_key(raw_private, None) self.assertIsInstance(private.fingerprint, byte_cls) self.assertIsInstance(public.fingerprint, byte_cls) self.assertEqual(private.fingerprint, public.fingerprint) def test_ec_generate(self): public, private = asymmetric.generate_pair('ec', curve='secp256r1') self.assertEqual('ec', public.algorithm) self.assertEqual('secp256r1', public.asn1.curve[1]) original_data = b'This is data to sign' signature = asymmetric.ecdsa_sign(private, original_data, 'sha1') self.assertIsInstance(signature, byte_cls) asymmetric.ecdsa_verify(public, signature, original_data, 'sha1') raw_public = asymmetric.dump_public_key(public) asymmetric.load_public_key(raw_public) raw_private = asymmetric.dump_private_key(private, None) asymmetric.load_private_key(raw_private, None) self.assertIsInstance(private.fingerprint, byte_cls) self.assertIsInstance(public.fingerprint, byte_cls) self.assertEqual(private.fingerprint, public.fingerprint) def test_rsa_verify(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'rsa_signature'), 'rb') as f: signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'sha1') def test_rsa_verify_key_size_mismatch(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'rsa_signature'), 'rb') as f: signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-4096.crt')) with self.assertRaises(errors.SignatureError): asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'sha1') def test_rsa_verify_fail(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'rsa_signature'), 'rb') as f: signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) with self.assertRaises(errors.SignatureError): asymmetric.rsa_pkcs1v15_verify(public, signature, original_data + b'1', 'sha1') def test_rsa_verify_fail_each_byte(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'rsa_signature'), 'rb') as f: original_signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) for i in range(0, len(original_signature)): if i == 0: signature = b'\xab' + original_signature[1:] elif i == len(original_signature) - 1: signature = original_signature[0:-1] + b'\xab' else: signature = original_signature[0:i] + b'\xab' + original_signature[i + 1:] with self.assertRaises(errors.SignatureError): asymmetric.rsa_pkcs1v15_verify(public, signature, original_data + b'1', 'sha1') def test_rsa_pss_verify(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'rsa_pss_signature'), 'rb') as f: signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) asymmetric.rsa_pss_verify(public, signature, original_data, 'sha1') def test_rsa_pss_verify_pss_cert(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'rsa_pss_signature_pss_cert'), 'rb') as f: signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-pss.crt')) asymmetric.rsa_pss_verify(public, signature, original_data, 'sha256') def test_rsa_pss_verify_fail(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'rsa_pss_signature'), 'rb') as f: signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) with self.assertRaises(errors.SignatureError): asymmetric.rsa_pss_verify(public, signature, original_data + b'1', 'sha1') def test_rsa_pss_verify_pss_cert_fail(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'rsa_pss_signature_pss_cert'), 'rb') as f: signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-pss.crt')) with self.assertRaises(errors.SignatureError): asymmetric.rsa_pss_verify(public, signature, original_data + b'1', 'sha256') def test_rsa_raw_verify(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'rsa_signature_raw'), 'rb') as f: signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'raw') def test_rsa_raw_verify_fail(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'rsa_signature_raw'), 'rb') as f: signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) with self.assertRaises(errors.SignatureError): asymmetric.rsa_pkcs1v15_verify(public, signature, original_data + b'1', 'raw') def test_dsa_verify(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'dsa_signature'), 'rb') as f: signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-dsa-1024.crt')) asymmetric.dsa_verify(public, signature, original_data, 'sha1') def test_dsa_verify_key_size_mismatch(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'dsa_signature'), 'rb') as f: signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-dsa-512.crt')) with self.assertRaises(errors.SignatureError): asymmetric.dsa_verify(public, signature, original_data, 'sha1') def test_dsa_verify_fail(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'dsa_signature'), 'rb') as f: signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-dsa-1024.crt')) with self.assertRaises(errors.SignatureError): asymmetric.dsa_verify(public, signature, original_data + b'1', 'sha1') def test_dsa_verify_fail_each_byte(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'dsa_signature'), 'rb') as f: original_signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-dsa-1024.crt')) for i in range(0, len(original_signature)): if i == 0: signature = b'\xab' + original_signature[1:] elif i == len(original_signature) - 1: signature = original_signature[0:-1] + b'\xab' else: signature = original_signature[0:i] + b'\xab' + original_signature[i+1:] with self.assertRaises(errors.SignatureError): asymmetric.dsa_verify(public, signature, original_data + b'1', 'sha1') def test_ecdsa_verify(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'ecdsa_signature'), 'rb') as f: signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-public-ec-named.key')) asymmetric.ecdsa_verify(public, signature, original_data, 'sha1') def test_ecdsa_verify_fail_each_byte(self): with open(os.path.join(fixtures_dir, 'message.txt'), 'rb') as f: original_data = f.read() with open(os.path.join(fixtures_dir, 'ecdsa_signature'), 'rb') as f: original_signature = f.read() public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-public-ec-named.key')) for i in range(0, len(original_signature)): if i == 0: signature = b'\xab' + original_signature[1:] elif i == len(original_signature) - 1: signature = original_signature[0:-1] + b'\xab' else: signature = original_signature[0:i] + b'\xab' + original_signature[i+1:] with self.assertRaises(errors.SignatureError): asymmetric.ecdsa_verify(public, signature, original_data + b'1', 'sha1') def test_rsa_pkcs1v15_encrypt(self): original_data = b'This is data to encrypt' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) ciphertext = asymmetric.rsa_pkcs1v15_encrypt(public, original_data) self.assertIsInstance(ciphertext, byte_cls) plaintext = asymmetric.rsa_pkcs1v15_decrypt(private, ciphertext) self.assertEqual(original_data, plaintext) def test_rsa_oaep_encrypt(self): original_data = b'This is data to encrypt' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) ciphertext = asymmetric.rsa_oaep_encrypt(public, original_data) self.assertIsInstance(ciphertext, byte_cls) plaintext = asymmetric.rsa_oaep_decrypt(private, ciphertext) self.assertEqual(original_data, plaintext) def test_rsa_private_pkcs1v15_decrypt(self): original_data = b'This is the message to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) with open(os.path.join(fixtures_dir, 'rsa_public_encrypted'), 'rb') as f: plaintext = asymmetric.rsa_pkcs1v15_decrypt(private, f.read()) self.assertEqual(original_data, plaintext) def test_rsa_private_oaep_decrypt(self): original_data = b'This is the message to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) with open(os.path.join(fixtures_dir, 'rsa_public_encrypted_oaep'), 'rb') as f: plaintext = asymmetric.rsa_oaep_decrypt(private, f.read()) self.assertEqual(original_data, plaintext) def test_rsa_sign(self): original_data = b'This is data to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) signature = asymmetric.rsa_pkcs1v15_sign(private, original_data, 'sha1') self.assertIsInstance(signature, byte_cls) asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'sha1') def test_rsa_fingerprint(self): private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) self.assertIsInstance(private.fingerprint, byte_cls) self.assertIsInstance(public.fingerprint, byte_cls) self.assertEqual(private.fingerprint, public.fingerprint) def test_rsa_public_key_attr(self): private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) computed_public = private.public_key self.assertEqual(public.asn1.dump(), computed_public.asn1.dump()) def test_rsa_private_key_unwrap(self): private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) self.assertIsInstance(private.unwrap(), keys.RSAPrivateKey) def test_rsa_public_key_unwrap(self): public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) self.assertIsInstance(public.unwrap(), keys.RSAPublicKey) def test_rsa_pss_sign(self): original_data = b'This is data to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) signature = asymmetric.rsa_pss_sign(private, original_data, 'sha1') self.assertIsInstance(signature, byte_cls) asymmetric.rsa_pss_verify(public, signature, original_data, 'sha1') def test_rsa_pss_sign_pss_cert(self): original_data = b'This is data to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-pss.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-pss.crt')) signature = asymmetric.rsa_pss_sign(private, original_data, 'sha1') self.assertIsInstance(signature, byte_cls) asymmetric.rsa_pss_verify(public, signature, original_data, 'sha1') def test_rsa_pss_sha256_sign(self): original_data = b'This is data to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) signature = asymmetric.rsa_pss_sign(private, original_data, 'sha256') self.assertIsInstance(signature, byte_cls) asymmetric.rsa_pss_verify(public, signature, original_data, 'sha256') def test_rsa_pss_sha256_sign_pss_cert(self): original_data = b'This is data to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-pss.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-pss.crt')) signature = asymmetric.rsa_pss_sign(private, original_data, 'sha256') self.assertIsInstance(signature, byte_cls) asymmetric.rsa_pss_verify(public, signature, original_data, 'sha256') def test_rsa_raw_sign(self): original_data = b'This is data to sign!' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test.crt')) signature = asymmetric.rsa_pkcs1v15_sign(private, original_data, 'raw') self.assertIsInstance(signature, byte_cls) asymmetric.rsa_pkcs1v15_verify(public, signature, original_data, 'raw') def test_dsa_sign(self): original_data = b'This is data to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-dsa-1024.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-dsa-1024.crt')) signature = asymmetric.dsa_sign(private, original_data, 'sha1') self.assertIsInstance(signature, byte_cls) asymmetric.dsa_verify(public, signature, original_data, 'sha1') def test_dsa_fingerprint(self): private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-dsa-1024.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-dsa-1024.crt')) self.assertIsInstance(private.fingerprint, byte_cls) self.assertIsInstance(public.fingerprint, byte_cls) self.assertEqual(private.fingerprint, public.fingerprint) def test_dsa_public_key_attr(self): private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-dsa-1024.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-dsa-1024.crt')) computed_public = private.public_key self.assertEqual(public.asn1.dump(), computed_public.asn1.dump()) def test_dsa_private_key_unwrap(self): private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-dsa-1024.key')) self.assertIsInstance(private.unwrap(), keys.DSAPrivateKey) def test_dsa_public_key_unwrap(self): public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-dsa-1024.crt')) self.assertIsInstance(public.unwrap(), core.Integer) def test_dsa_2048_sha1_sign(self): def do_run(): original_data = b'This is data to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-dsa-2048.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-dsa-2048.crt')) signature = asymmetric.dsa_sign(private, original_data, 'sha1') self.assertIsInstance(signature, byte_cls) asymmetric.dsa_verify(public, signature, original_data, 'sha1') if sys.platform == 'win32': with self.assertRaises(errors.AsymmetricKeyError): do_run() else: do_run() def test_dsa_2048_sha2_sign(self): def do_run(): original_data = b'This is data to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-dsa-2048-sha2.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-dsa-2048-sha2.crt')) signature = asymmetric.dsa_sign(private, original_data, 'sha256') self.assertIsInstance(signature, byte_cls) asymmetric.dsa_verify(public, signature, original_data, 'sha256') if not _should_support_sha2(): with self.assertRaises(errors.AsymmetricKeyError): do_run() else: do_run() def test_dsa_3072_sign(self): def do_run(): original_data = b'This is data to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-dsa.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-dsa.crt')) signature = asymmetric.dsa_sign(private, original_data, 'sha256') self.assertIsInstance(signature, byte_cls) asymmetric.dsa_verify(public, signature, original_data, 'sha256') if not _should_support_sha2(): with self.assertRaises(errors.AsymmetricKeyError): do_run() else: do_run() def test_dsa_3072_sign_sha1(self): def do_run(): original_data = b'This is data to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-dsa.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-dsa.crt')) signature = asymmetric.dsa_sign(private, original_data, 'sha1') self.assertIsInstance(signature, byte_cls) asymmetric.dsa_verify(public, signature, original_data, 'sha1') if _backend == 'mac' or openssl_098 or _backend == 'winlegacy': with self.assertRaises(errors.AsymmetricKeyError): do_run() elif _backend == 'win': if _win_version_pair() < (6, 2): exception_class = errors.AsymmetricKeyError else: exception_class = ValueError with self.assertRaises(exception_class): do_run() else: do_run() def test_ecdsa_sign(self): original_data = b'This is data to sign' private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-ec-named.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-ec-named.crt')) signature = asymmetric.ecdsa_sign(private, original_data, 'sha1') self.assertIsInstance(signature, byte_cls) asymmetric.ecdsa_verify(public, signature, original_data, 'sha1') def test_ec_fingerprints(self): private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-ec-named.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-ec-named.crt')) self.assertIsInstance(private.fingerprint, byte_cls) self.assertIsInstance(public.fingerprint, byte_cls) self.assertEqual(private.fingerprint, public.fingerprint) def test_ec_public_key_attr(self): private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-ec-named.key')) public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-ec-named.crt')) computed_public = private.public_key self.assertEqual(public.asn1.dump(), computed_public.asn1.dump()) def test_ec_private_key_unwrap(self): private = asymmetric.load_private_key(os.path.join(fixtures_dir, 'keys/test-ec-named.key')) self.assertIsInstance(private.unwrap(), keys.ECPrivateKey) def test_ec_public_key_unwrap(self): public = asymmetric.load_public_key(os.path.join(fixtures_dir, 'keys/test-ec-named.crt')) self.assertIsInstance(public.unwrap(), keys.ECPointBitString) oscrypto-1.3.0/tests/test_init.py000066400000000000000000000111641421476274700171340ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import ast import _ast import unittest import os import sys import oscrypto as module # This handles situations where an import is importing a function from a # dotted path, e.g. "from . import ident", and ident is a function, not a # submodule MOD_MAP = { 'oscrypto._backend_config': 'oscrypto', 'oscrypto.backend': 'oscrypto', 'oscrypto.ffi': 'oscrypto' } def add_mod(mod_name, imports): """ Maps pre-defined module.function to module import names :param mod_name: A unicode string of a fully-qualified module name being imported :param imports: A set of unicode strings of the modules that are being imported """ imports.add(MOD_MAP.get(mod_name, mod_name)) def walk_ast(parent_node, modname, imports): """ Walks the AST for a module finding any imports and recording them :param parent_node: A node from the _ast module :param modname: A unicode string of the module we are walking the AST of :param imports: A set of unicode strings of the imports that have been found so far """ for node in ast.iter_child_nodes(parent_node): if isinstance(node, _ast.Import): if node.names[0].name.startswith(module.__name__): add_mod(node.names[0].name, imports) elif isinstance(node, _ast.ImportFrom): if node.level > 0: if modname == module.__name__: base_mod = module.__name__ else: base_mod = '.'.join(modname.split('.')[:-node.level]) if node.module: base_mod += '.' + node.module else: base_mod = node.module if not base_mod.startswith(module.__name__): continue if node.level > 0 and not node.module: for n in node.names: add_mod(base_mod + '.' + n.name, imports) else: add_mod(base_mod, imports) elif isinstance(node, _ast.If): for subast in node.body: walk_ast(subast, modname, imports) for subast in node.orelse: walk_ast(subast, modname, imports) elif sys.version_info >= (3, 3) and isinstance(node, _ast.Try): for subast in node.body: walk_ast(subast, modname, imports) for subast in node.orelse: walk_ast(subast, modname, imports) for subast in node.finalbody: walk_ast(subast, modname, imports) elif sys.version_info < (3, 3) and isinstance(node, _ast.TryFinally): for subast in node.body: walk_ast(subast, modname, imports) for subast in node.finalbody: walk_ast(subast, modname, imports) elif sys.version_info < (3, 3) and isinstance(node, _ast.TryExcept): for subast in node.body: walk_ast(subast, modname, imports) for subast in node.orelse: walk_ast(subast, modname, imports) class InitTests(unittest.TestCase): def test_load_order(self): deps = {} mod_root = os.path.abspath(os.path.dirname(module.__file__)) files = [] for root, dnames, fnames in os.walk(mod_root): for f in fnames: if f.endswith('.py'): full_path = os.path.join(root, f) rel_path = full_path.replace(mod_root + os.sep, '') files.append((full_path, rel_path)) for full_path, rel_path in sorted(files): with open(full_path, 'rb') as f: full_code = f.read() if sys.version_info >= (3,): full_code = full_code.decode('utf-8') modname = rel_path.replace('.py', '').replace(os.sep, '.') if modname == '__init__': modname = module.__name__ else: modname = '%s.%s' % (module.__name__, modname) if sys.version_info < (3,) and sys.platform == 'win32' and b'\r\n' in full_code: full_code = full_code.replace(b'\r\n', b'\n') imports = set([]) module_node = ast.parse(full_code, filename=full_path) walk_ast(module_node, modname, imports) deps[modname] = imports load_order = module.load_order() prev = set([]) for mod in load_order: self.assertEqual(True, mod in deps) self.assertEqual((mod, set([])), (mod, deps[mod] - prev)) prev.add(mod) oscrypto-1.3.0/tests/test_kdf.py000066400000000000000000000036211421476274700167340ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import unittest import sys from oscrypto import kdf, _pkcs5 from ._unittest_compat import patch patch() if sys.version_info < (3,): byte_cls = str else: byte_cls = bytes class KDFTests(unittest.TestCase): def test_pbkdf1(self): key = kdf.pbkdf1('sha1', b'password', b'\x78\x57\x8E\x5A\x5D\x63\xCB\x06', 1000, 16) self.assertEqual(b'\xDC\x19\x84\x7E\x05\xC6\x4D\x2F\xAF\x10\xEB\xFB\x4A\x3D\x2A\x20', key) def test_pbkdf2(self): key = kdf.pbkdf2('sha1', b'password', b'\x78\x57\x8E\x5A\x5D\x63\xCB\x06', 2048, 24) self.assertEqual( b'\xBF\xDE\x6B\xE9\x4D\xF7\xE1\x1D\xD4\x09\xBC\xE2\x0A\x02\x55\xEC\x32\x7C\xB9\x36\xFF\xE9\x36\x43', key ) key = kdf.pbkdf2('sha1', b'password', b'\x09\xb7\x8c\xf0\x0c\x7f\xf5\x4a', 2048, 24) self.assertEqual( b'\x00\x74\x9d\x47\x49\xa0\x88\x2b\xe9\x48\xb8\x65\x7b\xf2\x64\x97\xf1\x1e\xa2\xf9\xfc\xd4\x70\x46', key ) def test_python_pbkdf2(self): key = _pkcs5.pbkdf2('sha1', b'password', b'\x78\x57\x8E\x5A\x5D\x63\xCB\x06', 2048, 24) self.assertEqual( b'\xBF\xDE\x6B\xE9\x4D\xF7\xE1\x1D\xD4\x09\xBC\xE2\x0A\x02\x55\xEC\x32\x7C\xB9\x36\xFF\xE9\x36\x43', key ) key = _pkcs5.pbkdf2('sha1', b'password', b'\x09\xb7\x8c\xf0\x0c\x7f\xf5\x4a', 2048, 24) self.assertEqual( b'\x00\x74\x9d\x47\x49\xa0\x88\x2b\xe9\x48\xb8\x65\x7b\xf2\x64\x97\xf1\x1e\xa2\xf9\xfc\xd4\x70\x46', key ) def test_pkcs12_kdf(self): key = kdf.pkcs12_kdf('sha1', b'sesame', b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF', 2048, 24, 1) self.assertEqual( b'\x7C\xD9\xFD\x3E\x2B\x3B\xE7\x69\x1A\x44\xE3\xBE\xF0\xF9\xEA\x0F\xB9\xB8\x97\xD4\xE3\x25\xD9\xD1', key ) oscrypto-1.3.0/tests/test_keys.py000066400000000000000000000306731421476274700171520ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import unittest import sys import os import asn1crypto from oscrypto import keys, backend from .unittest_data import data_decorator, data from ._unittest_compat import patch patch() if sys.version_info < (3,): byte_cls = str else: byte_cls = bytes tests_root = os.path.dirname(__file__) fixtures_dir = os.path.join(tests_root, 'fixtures') _backend = backend() if _backend == 'openssl': from oscrypto._openssl._libcrypto import libcrypto_legacy_support supports_legacy = libcrypto_legacy_support else: supports_legacy = True @data_decorator class KeyTests(unittest.TestCase): @staticmethod def private_keys(): return ( ( 'keys/test-aes128.key', b'password123', 'rsa', False, ), ( 'keys/test-aes256.key', b'password123', 'rsa', False, ), ( 'keys/test-der.key', None, 'rsa', False, ), ( 'keys/test-dsa-aes128.key', b'password123', 'dsa', False, ), ( 'keys/test-dsa-der.key', None, 'dsa', False, ), ( 'keys/test-dsa.key', None, 'dsa', False, ), ( 'keys/test-ec-aes128.key', b'password123', 'ec', False, ), ( 'keys/test-ec-der.key', None, 'ec', False, ), ( 'keys/test-ec.key', None, 'ec', False, ), ( 'keys/test-inter.key', None, 'rsa', False, ), ( 'keys/test-pkcs8-aes128-der.key', b'password123', 'rsa', False, ), ( 'keys/test-pkcs8-aes256.key', b'password123', 'rsa', False, ), ( 'keys/test-pkcs8-blank-der.key', b'', 'rsa', True, ), ( 'keys/test-pkcs8-blank-der.key', None, 'rsa', True, ), ( 'keys/test-pkcs8-blank.key', b'', 'rsa', True, ), ( 'keys/test-pkcs8-blank.key', None, 'rsa', True, ), ( 'keys/test-pkcs8-der.key', None, 'rsa', False, ), ( 'keys/test-pkcs8-des.key', b'password123', 'rsa', True, ), ( 'keys/test-pkcs8-tripledes.key', b'password123', 'rsa', False, ), ( 'keys/test-pkcs8.key', None, 'rsa', False, ), ( 'keys/test-third-der.key', None, 'rsa', False, ), ( 'keys/test-third.key', None, 'rsa', False, ), ( 'keys/test-tripledes.key', b'password123', 'rsa', False, ), ( 'keys/test.key', None, 'rsa', False, ), ) @data('private_keys') def parse_private(self, input_filename, password, algo, uses_legacy): def do_run(): with open(os.path.join(fixtures_dir, input_filename), 'rb') as f: private_object = keys.parse_private(f.read(), password) self.assertEqual(algo, private_object['private_key_algorithm']['algorithm'].native) # Make sure we can parse the whole structure private_object.native if not supports_legacy and uses_legacy: with self.assertRaises(EnvironmentError): do_run() else: do_run() def test_parse_private_pem_leading_whitespace(self): with open(os.path.join(fixtures_dir, 'keys/test.key'), 'rb') as f: private_object = keys.parse_private(b' \n' + f.read(), None) # Make sure we can parse the whole structure private_object.native @staticmethod def public_keys(): return ( ( 'keys/test-public-dsa-der.key', 'dsa', ), ( 'keys/test-public-dsa.key', 'dsa', ), ( 'keys/test-public-ec-der.key', 'ec', ), ( 'keys/test-public-ec.key', 'ec', ), ( 'keys/test-public-rsa-der.key', 'rsa', ), ( 'keys/test-public-rsa.key', 'rsa', ), ( 'keys/test-public-rsapublickey-der.key', 'rsa', ), ( 'keys/test-public-rsapublickey.key', 'rsa', ), ) @data('public_keys') def parse_public(self, input_filename, algo): with open(os.path.join(fixtures_dir, input_filename), 'rb') as f: parsed = keys.parse_public(f.read()) self.assertEqual(algo, parsed['algorithm']['algorithm'].native) # Make sure we can parse the whole structure parsed.native def test_parse_public_pem_leading_whitespace(self): with open(os.path.join(fixtures_dir, 'keys/test-public-rsa.key'), 'rb') as f: parsed = keys.parse_public(b' \r\n' + f.read()) # Make sure we can parse the whole structure parsed.native @staticmethod def certificates(): return ( ( 'keys/test-der.crt', 'rsa' ), ( 'keys/test-dsa-der.crt', 'dsa' ), ( 'keys/test-dsa.crt', 'dsa' ), ( 'keys/test-ec-der.crt', 'ec' ), ( 'keys/test-ec.crt', 'ec' ), ( 'keys/test-inter-der.crt', 'rsa' ), ( 'keys/test-inter.crt', 'rsa' ), ( 'keys/test-third-der.crt', 'rsa' ), ( 'keys/test-third.crt', 'rsa' ), ( 'keys/test.crt', 'rsa' ), ) @data('certificates') def parse_certificate(self, input_filename, algo): with open(os.path.join(fixtures_dir, input_filename), 'rb') as f: parsed = keys.parse_certificate(f.read()) self.assertEqual(algo, parsed['tbs_certificate']['subject_public_key_info']['algorithm']['algorithm'].native) self.assertEqual('Codex Non Sufficit LC', parsed['tbs_certificate']['subject'].native['organization_name']) # Make sure we can parse the whole structure parsed.native def test_parse_certificate_pem_leading_whitespace(self): with open(os.path.join(fixtures_dir, 'keys/test.crt'), 'rb') as f: parsed = keys.parse_certificate(b'\n' + f.read()) # Make sure we can parse the whole structure parsed.native @staticmethod def pkcs12_files(): return ( ( 'aes128', 'keys/test-aes128.p12', b'password123', True, ), ( 'aes256', 'keys/test-aes256.p12', b'password123', True, ), ( 'rc2', 'keys/test-rc2.p12', b'password123', True, ), ( 'tripledes_blank', 'keys/test-tripledes-blank.p12', b'', True ), ( 'tripledes_blank_none', 'keys/test-tripledes-blank.p12', None, True ), ( 'tripledes', 'keys/test-tripledes.p12', b'password123', True ), ) @data('pkcs12_files', True) def parse_pkcs12(self, input_filename, password, uses_legacy): def do_run(): with open(os.path.join(fixtures_dir, input_filename), 'rb') as f: key_info, cert_info, extra_cert_infos = keys.parse_pkcs12(f.read(), password) with open(os.path.join(fixtures_dir, 'keys/test-pkcs8-der.key'), 'rb') as f: key_der = f.read() with open(os.path.join(fixtures_dir, 'keys/test-der.crt'), 'rb') as f: cert_der = f.read() self.assertEqual(key_der, key_info.dump()) self.assertEqual(cert_der, cert_info.dump()) self.assertEqual([], extra_cert_infos) # Make sure we can parse the DER key_info.native cert_info.native if not supports_legacy and uses_legacy: with self.assertRaises(EnvironmentError): do_run() else: do_run() def test_parse_pkcs12_dsa(self): def do_run(): with open(os.path.join(fixtures_dir, 'keys/test-dsa.p12'), 'rb') as f: key_info, cert_info, extra_cert_infos = keys.parse_pkcs12(f.read(), b'password123') with open(os.path.join(fixtures_dir, 'keys/test-pkcs8-dsa-der.key'), 'rb') as f: key_der = f.read() with open(os.path.join(fixtures_dir, 'keys/test-dsa-der.crt'), 'rb') as f: cert_der = f.read() self.assertEqual(key_der, key_info.dump()) self.assertEqual(cert_der, cert_info.dump()) self.assertEqual([], extra_cert_infos) # Make sure we can parse the DER key_info.native cert_info.native if not supports_legacy: with self.assertRaises(EnvironmentError): do_run() else: do_run() def test_parse_pkcs12_chain(self): def do_run(): with open(os.path.join(fixtures_dir, 'keys/test-third.p12'), 'rb') as f: key_info, cert_info, extra_cert_infos = keys.parse_pkcs12(f.read(), b'password123') with open(os.path.join(fixtures_dir, 'keys/test-third-der.key'), 'rb') as f: private_key = asn1crypto.keys.RSAPrivateKey.load(f.read()) key_der = asn1crypto.keys.PrivateKeyInfo.wrap(private_key, 'rsa').dump() with open(os.path.join(fixtures_dir, 'keys/test-third-der.crt'), 'rb') as f: cert_der = f.read() with open(os.path.join(fixtures_dir, 'keys/test-inter-der.crt'), 'rb') as f: intermediate_cert_der = f.read() with open(os.path.join(fixtures_dir, 'keys/test-der.crt'), 'rb') as f: root_cert_der = f.read() self.assertEqual(key_der, key_info.dump()) self.assertEqual(cert_der, cert_info.dump()) self.assertEqual( sorted([intermediate_cert_der, root_cert_der]), sorted([info.dump() for info in extra_cert_infos]) ) # Make sure we can parse the DER key_info.native cert_info.native for info in extra_cert_infos: info.native if not supports_legacy: with self.assertRaises(EnvironmentError): do_run() else: do_run() oscrypto-1.3.0/tests/test_symmetric.py000066400000000000000000000157341421476274700202140ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import unittest import sys from oscrypto import symmetric, util, backend from ._unittest_compat import patch from .exception_context import assert_exception from .unittest_data import data_decorator, data patch() if sys.version_info < (3,): byte_cls = str else: byte_cls = bytes _backend = backend() if _backend == 'openssl': from oscrypto._openssl._libcrypto import libcrypto_legacy_support supports_legacy = libcrypto_legacy_support else: supports_legacy = True @data_decorator class SymmetricTests(unittest.TestCase): @staticmethod def aes_key_lengths(): return ( ('128', 16), ('192', 24), ('256', 32), ) @data('aes_key_lengths', True) def aes_cbc_no_padding_encrypt_decrypt(self, key_byte_len): key = util.rand_bytes(key_byte_len) data = b'This is the data' iv, ciphertext = symmetric.aes_cbc_no_padding_encrypt(key, data, None) self.assertNotEqual(data, ciphertext) self.assertEqual(byte_cls, type(ciphertext)) plaintext = symmetric.aes_cbc_no_padding_decrypt(key, ciphertext, iv) self.assertEqual(data, plaintext) @data('aes_key_lengths', True) def aes_cbc_no_padding_encrypt_decrypt_two_block(self, key_byte_len): key = util.rand_bytes(key_byte_len) data = b'This is data to encrypt-32 bytes' iv, ciphertext = symmetric.aes_cbc_no_padding_encrypt(key, data, None) self.assertNotEqual(data, ciphertext) self.assertEqual(byte_cls, type(ciphertext)) plaintext = symmetric.aes_cbc_no_padding_decrypt(key, ciphertext, iv) self.assertEqual(data, plaintext) @data('aes_key_lengths', True) def aes_cbc_no_padding_wrong_length(self, key_byte_len): key = util.rand_bytes(key_byte_len) with assert_exception(self, ValueError, r'data must be a multiple of 16 bytes long - is 31'): data = b'31 bytes of data to encrypt now' iv, ciphertext = symmetric.aes_cbc_no_padding_encrypt(key, data, None) with assert_exception(self, ValueError, r'data must be a multiple of 16 bytes long - is 33'): data = b'Thirty three bytes to encrypt now' iv, ciphertext = symmetric.aes_cbc_no_padding_encrypt(key, data, None) with assert_exception(self, ValueError, r'data must be a multiple of 16 bytes long - is 15'): data = b'Fifteen bytes!!' iv, ciphertext = symmetric.aes_cbc_no_padding_encrypt(key, data, None) with assert_exception(self, ValueError, r'data must be a multiple of 16 bytes long - is 24'): data = b'Twenty four bytes long!!' iv, ciphertext = symmetric.aes_cbc_no_padding_encrypt(key, data, None) @data('aes_key_lengths', True) def aes_encrypt_decrypt(self, key_byte_len): key = util.rand_bytes(key_byte_len) data = b'This is data to encrypt' iv, ciphertext = symmetric.aes_cbc_pkcs7_encrypt(key, data, None) self.assertNotEqual(data, ciphertext) self.assertEqual(byte_cls, type(ciphertext)) plaintext = symmetric.aes_cbc_pkcs7_decrypt(key, ciphertext, iv) self.assertEqual(data, plaintext) def test_rc4_40_encrypt_decrypt(self): def do_run(): key = util.rand_bytes(5) data = b'This is data to encrypt' ciphertext = symmetric.rc4_encrypt(key, data) self.assertNotEqual(data, ciphertext) self.assertEqual(byte_cls, type(ciphertext)) plaintext = symmetric.rc4_decrypt(key, ciphertext) self.assertEqual(data, plaintext) if not supports_legacy: with self.assertRaises(EnvironmentError): do_run() else: do_run() def test_rc4_128_encrypt_decrypt(self): def do_run(): key = util.rand_bytes(16) data = b'This is data to encrypt' ciphertext = symmetric.rc4_encrypt(key, data) self.assertNotEqual(data, ciphertext) self.assertEqual(byte_cls, type(ciphertext)) plaintext = symmetric.rc4_decrypt(key, ciphertext) self.assertEqual(data, plaintext) if not supports_legacy: with self.assertRaises(EnvironmentError): do_run() else: do_run() def test_rc2_64_encrypt_decrypt(self): def do_run(): key = util.rand_bytes(8) data = b'This is data to encrypt' iv, ciphertext = symmetric.rc2_cbc_pkcs5_encrypt(key, data, None) self.assertNotEqual(data, ciphertext) self.assertEqual(byte_cls, type(ciphertext)) plaintext = symmetric.rc2_cbc_pkcs5_decrypt(key, ciphertext, iv) self.assertEqual(data, plaintext) if not supports_legacy: with self.assertRaises(EnvironmentError): do_run() else: do_run() def test_rc2_40_encrypt_decrypt(self): def do_run(): key = util.rand_bytes(5) data = b'This is data to encrypt' iv, ciphertext = symmetric.rc2_cbc_pkcs5_encrypt(key, data, None) self.assertNotEqual(data, ciphertext) self.assertEqual(byte_cls, type(ciphertext)) plaintext = symmetric.rc2_cbc_pkcs5_decrypt(key, ciphertext, iv) self.assertEqual(data, plaintext) if not supports_legacy: with self.assertRaises(EnvironmentError): do_run() else: do_run() def test_des_encrypt_decrypt(self): def do_run(): key = util.rand_bytes(8) data = b'This is data to encrypt' iv, ciphertext = symmetric.des_cbc_pkcs5_encrypt(key, data, None) self.assertNotEqual(data, ciphertext) self.assertEqual(byte_cls, type(ciphertext)) plaintext = symmetric.des_cbc_pkcs5_decrypt(key, ciphertext, iv) self.assertEqual(data, plaintext) if not supports_legacy: with self.assertRaises(EnvironmentError): do_run() else: do_run() def test_3des_2k_encrypt_decrypt(self): key = util.rand_bytes(16) data = b'This is data to encrypt' iv, ciphertext = symmetric.tripledes_cbc_pkcs5_encrypt(key, data, None) self.assertNotEqual(data, ciphertext) self.assertEqual(byte_cls, type(ciphertext)) plaintext = symmetric.tripledes_cbc_pkcs5_decrypt(key, ciphertext, iv) self.assertEqual(data, plaintext) def test_3des_3k_encrypt_decrypt(self): key = util.rand_bytes(24) data = b'This is data to encrypt' iv, ciphertext = symmetric.tripledes_cbc_pkcs5_encrypt(key, data, None) self.assertNotEqual(data, ciphertext) self.assertEqual(byte_cls, type(ciphertext)) plaintext = symmetric.tripledes_cbc_pkcs5_decrypt(key, ciphertext, iv) self.assertEqual(data, plaintext) oscrypto-1.3.0/tests/test_tls.py000066400000000000000000000555501421476274700170020ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import unittest import sys import os import re import threading import platform import time import socket from oscrypto import tls, errors, backend from oscrypto._tls import parse_tls_records, parse_handshake_messages from asn1crypto import x509 from .unittest_data import data_decorator, data from .exception_context import assert_exception from ._unittest_compat import patch from ._https_client import HttpsClient from ._socket_proxy import make_socket_proxy from ._socket_server import make_socket_server if sys.version_info < (3,): import thread else: import _thread as thread patch() if sys.version_info < (3,): str_cls = unicode # noqa byte_cls = str else: str_cls = str byte_cls = bytes _backend = backend() xp = sys.platform == 'win32' and sys.getwindowsversion()[0] < 6 tests_root = os.path.dirname(__file__) fixtures_dir = os.path.join(tests_root, 'fixtures') digicert_ca_path = os.path.join(fixtures_dir, 'digicert_ca.crt') badtls_ca_path = os.path.join(fixtures_dir, 'badtls.io_ca.crt') # PyPy <= 5.6.0 on OS X 10.11 has a bug with _get_clocktime osx_pypy_bug = platform.python_implementation() == 'PyPy' \ and sys.platform == 'darwin' \ and tuple(map(int, platform.mac_ver()[0].split('.'))) < (10, 12) raise_with = None if sys.version_info < (3,): exec(''' def raise_with(e, tb): raise e, None, tb ''') else: exec(''' def raise_with(e, tb): e.__traceback__ = tb raise e from None ''') def connection_timeout(timeout=30): def timeout_decorator(f): def wrapped(*args): try: if not osx_pypy_bug: t = threading.Timer(timeout, lambda: thread.interrupt_main()) t.start() f(*args) except (KeyboardInterrupt): raise_with(AssertionError("Timed out"), sys.exc_info()[2]) finally: if not osx_pypy_bug: t.cancel() return wrapped return timeout_decorator @data_decorator class TLSTests(unittest.TestCase): @staticmethod def tls_hosts(): return ( ('google', 'www.google.com', 443), ('package_control', 'packagecontrol.io', 443), ('dh1024', 'dh1024.badtls.io', 10005), ) @data('tls_hosts', True) @connection_timeout() def tls_connect(self, hostname, port): session = None if hostname == 'dh1024.badtls.io': session = tls.TLSSession(extra_trust_roots=[badtls_ca_path]) connection = tls.TLSSocket(hostname, port, session=session) self.assertEqual(hostname, connection.hostname) self.assertIsInstance(connection.hostname, str_cls) self.assertIsInstance(connection.cipher_suite, str_cls) self.assertIsInstance(connection.certificate, x509.Certificate) self.assertLess(10, len(connection.cipher_suite)) self.assertEqual(port, connection.port) connection.write(b'GET / HTTP/1.1\r\nHost: ' + hostname.encode('utf-8') + b'\r\n\r\n') html = connection.read_until(re.compile(b'', re.I)) self.assertNotEqual(None, re.search(b'', html, re.I)) @connection_timeout() def test_tls_connect_revoked(self): if _backend == 'mac': from oscrypto._mac._security import osx_version_info # macOS 10.12 will read the OCSP Staple response and raise a revoked # error, even though we have revocation checking disabled if osx_version_info >= (10, 12): with assert_exception(self, errors.TLSError, 'revoked'): tls.TLSSocket('global-root-ca-revoked.chain-demos.digicert.com', 443) return tls.TLSSocket('global-root-ca-revoked.chain-demos.digicert.com', 443) @connection_timeout() def test_tls_error_http(self): with assert_exception(self, errors.TLSError, 'server responded using HTTP'): tls.TLSSocket('www.google.com', 80) @connection_timeout() def test_tls_error_ftp(self): s = None try: def_timeout = socket.getdefaulttimeout() socket.setdefaulttimeout(5) s = make_socket_server(18021, lambda s, d: s.send(b'220 Welcome to FooFTP\n')) with assert_exception(self, errors.TLSError, 'remote end closed the connection|server responded using FTP'): tls.TLSSocket('localhost', 18021) finally: if s: s.close() socket.setdefaulttimeout(def_timeout) @connection_timeout() def test_tls_error_missing_issuer(self): expected = 'certificate issuer not found in trusted root certificate store' with assert_exception(self, errors.TLSVerificationError, expected): tls.TLSSocket('domain-match.badtls.io', 10000) @connection_timeout() def test_tls_error_domain_mismatch(self): session = tls.TLSSession(extra_trust_roots=[badtls_ca_path]) with assert_exception(self, errors.TLSVerificationError, 'does not match'): tls.TLSSocket('domain-mismatch.badtls.io', 11002, session=session) @connection_timeout() def test_tls_error_san_mismatch(self): session = tls.TLSSession(extra_trust_roots=[badtls_ca_path]) with assert_exception(self, errors.TLSVerificationError, 'does not match'): tls.TLSSocket('san-mismatch.badtls.io', 11003, session=session) @connection_timeout() def test_tls_wildcard_success(self): session = tls.TLSSession(extra_trust_roots=[badtls_ca_path]) tls.TLSSocket('wildcard-match.badtls.io', 10001, session=session) @connection_timeout() def test_tls_error_not_yet_valid(self): session = tls.TLSSession(extra_trust_roots=[badtls_ca_path]) with assert_exception(self, errors.TLSVerificationError, 'not valid until'): tls.TLSSocket('future.badtls.io', 11001, session=session) @connection_timeout() def test_tls_error_expired_2(self): session = tls.TLSSession(extra_trust_roots=[badtls_ca_path]) # This test allows past or future since cert is 1963, which some systems # will interpret as 2063 with assert_exception(self, errors.TLSVerificationError, 'certificate expired|not valid until'): tls.TLSSocket('expired-1963.badtls.io', 11000, session=session) @connection_timeout() def test_tls_error_client_cert_required(self): session = tls.TLSSession(extra_trust_roots=[badtls_ca_path]) with assert_exception(self, errors.TLSError, 'client authentication'): tls.TLSSocket('required-auth.badtls.io', 10003, session=session) @connection_timeout() def test_tls_error_handshake_error_3(self): session = tls.TLSSession(extra_trust_roots=[badtls_ca_path]) with assert_exception(self, errors.TLSError, 'weak certificate signature algorithm'): tls.TLSSocket('weak-sig.badtls.io', 11004, session=session) @connection_timeout() def test_tls_error_non_web(self): session = tls.TLSSession(extra_trust_roots=[badtls_ca_path]) with assert_exception(self, errors.TLSVerificationError, 'verification failed'): tls.TLSSocket('bad-key-usage.badtls.io', 11005, session=session) @connection_timeout() def test_tls_error_wildcard_mismatch(self): session = tls.TLSSession(extra_trust_roots=[badtls_ca_path]) with assert_exception(self, errors.TLSVerificationError, 'does not match'): tls.TLSSocket('wildcard.mismatch.badtls.io', 11007, session=session) @connection_timeout() def test_tls_error_expired(self): session = tls.TLSSession(extra_trust_roots=[badtls_ca_path]) with assert_exception(self, errors.TLSVerificationError, 'certificate expired'): tls.TLSSocket('expired.badtls.io', 11006, session=session) @connection_timeout() def test_tls_error_self_signed(self): with assert_exception(self, errors.TLSVerificationError, 'self-signed'): tls.TLSSocket('self-signed.badssl.com', 443) @connection_timeout() def test_tls_error_weak_dh_params(self): # badssl.com uses SNI, which Windows XP does not support regex = 'weak DH parameters' if not xp else 'self-signed' # ideally we would use badtls.io since that does not require SNI, however # it is not possible to force a good version of OpenSSL to use such a # small value for DH params, and I don't feel like the headache of trying # to get an old, staticly-linked socat set up just for this text on XP with assert_exception(self, errors.TLSError, regex): tls.TLSSocket('dh512.badssl.com', 443) @connection_timeout() def test_tls_error_handshake_error(self): session = tls.TLSSession(extra_trust_roots=[badtls_ca_path]) with assert_exception(self, errors.TLSError, 'TLS handshake failed'): tls.TLSSocket('rc4-md5.badtls.io', 11009, session=session) @connection_timeout() def test_tls_error_handshake_error_2(self): session = tls.TLSSession(extra_trust_roots=[badtls_ca_path]) with assert_exception(self, errors.TLSError, 'TLS handshake failed'): tls.TLSSocket('rc4.badtls.io', 11008, session=session) @connection_timeout() def test_tls_extra_trust_roots_no_match(self): expected = 'certificate issuer not found in trusted root certificate store' with assert_exception(self, errors.TLSVerificationError, expected): session = tls.TLSSession(extra_trust_roots=[digicert_ca_path]) tls.TLSSocket('domain-match.badtls.io', 10000, session=session) @connection_timeout() def test_tls_extra_trust_roots(self): session = tls.TLSSession(extra_trust_roots=[badtls_ca_path, digicert_ca_path]) tls.TLSSocket('domain-match.badtls.io', 10000, session=session) @connection_timeout(90) def test_tls_graceful_disconnect(self): # This connects to a bunch of pages on the same domains using # the Connection: Keep-Alive header, however the server is # configured to disconnect after 40 responses, so it requires # TLSSocket() to properly handle a graceful disconnect. urls = [ 'https://packagecontrol.io/browse/popular.json?page=1', 'https://packagecontrol.io/browse/popular.json?page=2', 'https://packagecontrol.io/browse/popular.json?page=3', 'https://packagecontrol.io/browse/popular.json?page=4', 'https://packagecontrol.io/browse/popular.json?page=5', 'https://packagecontrol.io/browse/popular.json?page=6', 'https://packagecontrol.io/browse/popular.json?page=7', 'https://packagecontrol.io/browse/popular.json?page=8', 'https://packagecontrol.io/browse/popular.json?page=9', 'https://packagecontrol.io/browse/popular.json?page=10', 'https://packagecontrol.io/browse/popular.json?page=11', 'https://packagecontrol.io/browse/popular.json?page=12', 'https://packagecontrol.io/browse/popular.json?page=13', 'https://packagecontrol.io/browse/popular.json?page=14', 'https://packagecontrol.io/browse/popular.json?page=15', 'https://packagecontrol.io/browse/popular.json?page=16', 'https://packagecontrol.io/browse/popular.json?page=17', 'https://packagecontrol.io/browse/popular.json?page=18', 'https://packagecontrol.io/browse/popular.json?page=19', 'https://packagecontrol.io/browse/popular.json?page=20', 'https://packagecontrol.io/browse/popular.json?page=21', 'https://packagecontrol.io/browse/popular.json?page=22', 'https://packagecontrol.io/browse/popular.json?page=23', 'https://packagecontrol.io/browse/popular.json?page=24', 'https://packagecontrol.io/browse/popular.json?page=25', 'https://packagecontrol.io/browse/popular.json?page=26', 'https://packagecontrol.io/browse/popular.json?page=27', 'https://packagecontrol.io/browse/popular.json?page=28', 'https://packagecontrol.io/browse/popular.json?page=29', 'https://packagecontrol.io/browse/popular.json?page=30', 'https://packagecontrol.io/browse/popular.json?page=31', 'https://packagecontrol.io/browse/popular.json?page=32', 'https://packagecontrol.io/browse/popular.json?page=33', 'https://packagecontrol.io/browse/popular.json?page=34', 'https://packagecontrol.io/browse/popular.json?page=35', 'https://packagecontrol.io/browse/popular.json?page=36', 'https://packagecontrol.io/browse/popular.json?page=37', 'https://packagecontrol.io/browse/popular.json?page=38', 'https://packagecontrol.io/browse/popular.json?page=39', 'https://packagecontrol.io/browse/popular.json?page=40', 'https://packagecontrol.io/browse/popular.json?page=41', 'https://packagecontrol.io/browse/popular.json?page=42', 'https://packagecontrol.io/browse/popular.json?page=43', 'https://packagecontrol.io/browse/popular.json?page=44', 'https://packagecontrol.io/browse/popular.json?page=45', 'https://packagecontrol.io/browse/popular.json?page=46', 'https://packagecontrol.io/browse/popular.json?page=47', 'https://packagecontrol.io/browse/popular.json?page=48', 'https://packagecontrol.io/browse/popular.json?page=49', 'https://packagecontrol.io/browse/popular.json?page=50', 'https://packagecontrol.io/browse/popular.json?page=51', 'https://packagecontrol.io/browse/popular.json?page=52', 'https://packagecontrol.io/browse/popular.json?page=53', 'https://packagecontrol.io/browse/popular.json?page=54', 'https://packagecontrol.io/browse/popular.json?page=55', 'https://packagecontrol.io/browse/popular.json?page=56', 'https://packagecontrol.io/browse/popular.json?page=57', 'https://packagecontrol.io/browse/popular.json?page=58', 'https://packagecontrol.io/browse/popular.json?page=59', 'https://packagecontrol.io/browse/popular.json?page=60', 'https://packagecontrol.io/browse/popular.json?page=61', 'https://packagecontrol.io/browse/popular.json?page=62', 'https://packagecontrol.io/browse/popular.json?page=63', 'https://packagecontrol.io/browse/popular.json?page=64', 'https://packagecontrol.io/browse/popular.json?page=65', 'https://packagecontrol.io/browse/popular.json?page=66', 'https://packagecontrol.io/browse/popular.json?page=67', 'https://packagecontrol.io/browse/popular.json?page=68', 'https://packagecontrol.io/browse/popular.json?page=69', 'https://packagecontrol.io/browse/popular.json?page=70', 'https://packagecontrol.io/browse/popular.json?page=71', 'https://packagecontrol.io/browse/popular.json?page=72', 'https://packagecontrol.io/browse/popular.json?page=73', 'https://packagecontrol.io/browse/popular.json?page=74', 'https://packagecontrol.io/browse/popular.json?page=75', 'https://packagecontrol.io/browse/popular.json?page=76', 'https://packagecontrol.io/browse/popular.json?page=77', 'https://packagecontrol.io/browse/popular.json?page=78', 'https://packagecontrol.io/browse/popular.json?page=79', 'https://packagecontrol.io/browse/popular.json?page=80', 'https://packagecontrol.io/browse/popular.json?page=81', 'https://packagecontrol.io/browse/popular.json?page=82', 'https://packagecontrol.io/browse/popular.json?page=83', 'https://packagecontrol.io/browse/popular.json?page=84', 'https://packagecontrol.io/browse/popular.json?page=85', 'https://packagecontrol.io/browse/popular.json?page=86', 'https://packagecontrol.io/browse/popular.json?page=87', 'https://packagecontrol.io/browse/popular.json?page=88', 'https://packagecontrol.io/browse/popular.json?page=89', 'https://packagecontrol.io/browse/popular.json?page=90', 'https://packagecontrol.io/browse/popular.json?page=91', 'https://packagecontrol.io/browse/popular.json?page=92', 'https://packagecontrol.io/browse/popular.json?page=93', 'https://packagecontrol.io/browse/popular.json?page=94', 'https://packagecontrol.io/browse/popular.json?page=95', 'https://packagecontrol.io/browse/popular.json?page=96', 'https://packagecontrol.io/browse/popular.json?page=97', 'https://packagecontrol.io/browse/popular.json?page=98', 'https://packagecontrol.io/browse/popular.json?page=99', 'https://packagecontrol.io/browse/popular.json?page=100', ] c = HttpsClient() for url in urls: c.download(url, 15) @connection_timeout(60) def test_tls_large_download(self): # This tests downloading a large (3MB+) file to ensure # there aren't buffer overflow issues in TLSSocket() c = HttpsClient() c.download('https://packagecontrol.io/channel_v3.json', 15) @connection_timeout() def test_tls_protocol_version(self): session = tls.TLSSession(set(['TLSv1', 'TLSv1.1'])) with assert_exception(self, errors.TLSError, 'TLS handshake failed - protocol version error'): tls.TLSSocket('github.com', 443, session=session) @connection_timeout() def test_tls_pause_timeout(self): c = HttpsClient(False, True) c.download('https://packagecontrol.io/browse/popular.json?page=1', 5) time.sleep(10) c.download('https://packagecontrol.io/browse/popular.json?page=2', 5) @connection_timeout() def test_tls_closed_connection_read(self): ip = socket.gethostbyname('badtls.io') with assert_exception(self, (errors.TLSDisconnectError, errors.TLSError), 'The remote end closed the connection|TLS handshake failed'): try: sock, send_sock, recv_sock, server = make_socket_proxy(ip, 443) tsock = tls.TLSSocket.wrap(sock, 'badtls.io') send_sock.close() tsock.read(8192) finally: recv_sock.close() server.close() @connection_timeout() def test_tls_closed_connection_write(self): ip = socket.gethostbyname('badtls.io') with assert_exception(self, (errors.TLSDisconnectError, errors.TLSError), 'The remote end closed the connection|TLS handshake failed'): try: sock, send_sock, recv_sock, server = make_socket_proxy(ip, 443) tsock = tls.TLSSocket.wrap(sock, 'badtls.io') send_sock.close() tsock.write(b'GET / HTTP/1.1\r\n') tsock.write(b'\r\n') tsock.write(b'\r\n') finally: recv_sock.close() server.close() @connection_timeout() def test_tls_closed_connection_read_handshake(self): ip = socket.gethostbyname('badtls.io') # Break the connection after the ServerHello is received def recv_callback(src, dest): data = src.recv(8192) for record_type, tls_version, message in parse_tls_records(data): if record_type == b'\x16': for message_type, payload in parse_handshake_messages(message): if message_type == b'\x02': dest.close() return dest.send(data) with assert_exception(self, (errors.TLSDisconnectError, errors.TLSError), 'The remote end closed the connection|TLS handshake failed'): try: sock, send_sock, recv_sock, server = make_socket_proxy(ip, 443, None, recv_callback) tls.TLSSocket.wrap(sock, 'badtls.io') finally: recv_sock.close() send_sock.close() server.close() @connection_timeout() def test_tls_closed_connection_write_handshake(self): ip = socket.gethostbyname('badtls.io') # Break the connection after the ClientHello is sent def send_callback(src, dest): data = src.recv(8192) for record_type, tls_version, message in parse_tls_records(data): if record_type == b'\x16': for message_type, payload in parse_handshake_messages(message): if message_type == b'\x01': src.close() return dest.send(data) with assert_exception(self, (errors.TLSDisconnectError, errors.TLSError), 'The remote end closed the connection|TLS handshake failed'): recv_sock = None send_sock = None server = None try: sock, send_sock, recv_sock, server = make_socket_proxy(ip, 443, send_callback) tls.TLSSocket.wrap(sock, 'badtls.io') finally: if recv_sock: recv_sock.close() if send_sock: send_sock.close() if server: server.close() @connection_timeout() def test_tls_closed_connection_read_shutdown(self): ip = socket.gethostbyname('badtls.io') try: sock, send_sock, recv_sock, server = make_socket_proxy(ip, 443) tsock = None try: tsock = tls.TLSSocket.wrap(sock, 'badtls.io') send_sock.close() tsock.read(8192) shutdown = False except (errors.TLSError): if tsock: tsock.shutdown() shutdown = True self.assertEqual(True, shutdown) finally: recv_sock.close() server.close() @connection_timeout() def test_tls_closed_connection_write_shutdown(self): ip = socket.gethostbyname('badtls.io') try: sock, send_sock, recv_sock, server = make_socket_proxy(ip, 443) tsock = None try: tsock = tls.TLSSocket.wrap(sock, 'badtls.io') send_sock.close() tsock.write(b'GET / HTTP/1.1\r\n') tsock.write(b'\r\n') tsock.write(b'\r\n') tsock.read(8192) shutdown = False except (errors.TLSError): if tsock: tsock.shutdown() shutdown = True self.assertEqual(True, shutdown) finally: recv_sock.close() server.close() oscrypto-1.3.0/tests/test_trust_list.py000066400000000000000000000056221421476274700204070ustar00rootroot00000000000000# coding: utf-8 from __future__ import unicode_literals, division, absolute_import, print_function import hashlib import os import unittest import sys from oscrypto import trust_list from asn1crypto import x509, pem from ._unittest_compat import patch patch() if sys.version_info < (3,): str_cls = unicode # noqa byte_cls = str else: str_cls = str byte_cls = bytes tests_root = os.path.dirname(__file__) fixtures_dir = os.path.join(tests_root, 'fixtures') digicert_ca_path = os.path.join(fixtures_dir, 'digicert_ca.crt') class TrustListTests(unittest.TestCase): def test_get_list(self): trust_list.clear_cache() certs = trust_list.get_list() self.assertIsInstance(certs, list) self.assertLess(10, len(certs)) for cert, trust_oids, reject_oids in certs: self.assertIsInstance(cert, x509.Certificate) self.assertIsInstance(trust_oids, set) self.assertIsInstance(reject_oids, set) cert.native def test_get_list_callback(self): trust_list.clear_cache() lambda_data = {'calls': 0, 'reasons': 0, 'certs': {}} def cb(cert, reason): if reason is not None: self.assertIsInstance(reason, str_cls) lambda_data['reasons'] += 1 self.assertIsInstance(cert, x509.Certificate) sha1 = hashlib.sha1(cert.dump()).digest() message = None if sha1 in lambda_data['certs']: message = 'Certificate (%s) already passed to callback' % cert.subject.human_friendly self.assertNotIn(sha1, lambda_data['certs'], message) lambda_data['certs'][sha1] = True lambda_data['calls'] += 1 certs = trust_list.get_list(cert_callback=cb) self.assertIsInstance(certs, list) self.assertLess(10, len(certs)) self.assertLessEqual(len(certs), lambda_data['calls']) self.assertEqual(lambda_data['calls'] - len(certs), lambda_data['reasons']) for cert, trust_oids, reject_oids in certs: self.assertIsInstance(cert, x509.Certificate) self.assertIsInstance(trust_oids, set) self.assertIsInstance(reject_oids, set) cert.native def test_get_list_mutate(self): trust_list.clear_cache() certs = trust_list.get_list() certs2 = trust_list.get_list() with open(digicert_ca_path, 'rb') as f: _, _, digicert_ca_bytes = pem.unarmor(f.read()) digicert_ca_cert = x509.Certificate.load(digicert_ca_bytes) certs.append(digicert_ca_cert) self.assertNotEqual(certs2, certs) def test_get_path(self): trust_list.clear_cache() certs = trust_list.get_path() with open(certs, 'rb') as f: cert_data = f.read() self.assertEqual(True, pem.detect(cert_data)) self.assertLess(10240, len(cert_data)) oscrypto-1.3.0/tests/unittest_data.py000066400000000000000000000042671421476274700200100ustar00rootroot00000000000000# Written by Will Bond # # The author or authors of this code dedicate any and all copyright interest in # this code to the public domain. We make this dedication for the benefit of the # public at large and to the detriment of our heirs and successors. We intend # this dedication to be an overt act of relinquishment in perpetuity of all # present and future rights to this code under copyright law. def data(provider_method, first_param_name_suffix=False): """ A method decorator for unittest.TestCase classes that configured a static method to be used to provide multiple sets of test data to a single test :param provider_method: The name of the staticmethod of the class to use as the data provider :param first_param_name_suffix: If the first parameter for each set should be appended to the method name to generate the name of the test. Otherwise integers are used. :return: The decorated function """ def test_func_decorator(test_func): test_func._provider_method = provider_method test_func._provider_name_suffix = first_param_name_suffix return test_func return test_func_decorator def data_decorator(cls): """ A class decorator that works with the @provider decorator to generate test method from a data provider """ def generate_test_func(name, original_function, num, params): if original_function._provider_name_suffix: data_name = params[0] params = params[1:] else: data_name = num expanded_name = 'test_%s_%s' % (name, data_name) # We used expanded variable names here since this line is present in # backtraces that are generated from test failures. def generated_test_function(self): original_function(self, *params) setattr(cls, expanded_name, generated_test_function) for name in dir(cls): func = getattr(cls, name) if hasattr(func, '_provider_method'): num = 1 for params in getattr(cls, func._provider_method)(): generate_test_func(name, func, num, params) num += 1 return cls oscrypto-1.3.0/tox.ini000066400000000000000000000003251421476274700147260ustar00rootroot00000000000000[tox] envlist = py26,py27,py32,py33,py34,py35,py36,py37,py38,py39,py310,pypy [testenv] deps = -rrequires/ci commands = {envpython} run.py ci [pep8] max-line-length = 120 [flake8] max-line-length = 120 jobs = 1