Merge branch 'release/0.4.1'
This commit is contained in:
28
.travis.yml
28
.travis.yml
@@ -1,5 +1,9 @@
|
|||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
|
- "3.6"
|
||||||
- "3.5"
|
- "3.5"
|
||||||
- "3.4"
|
- "3.4"
|
||||||
- "3.3"
|
- "3.3"
|
||||||
@@ -9,38 +13,50 @@ python:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# add a pep8 test
|
# add a pep8 test
|
||||||
- python: 3.5
|
- python: 3.6
|
||||||
env: TOXENV=pep8
|
env: TOXENV=pep8
|
||||||
|
|
||||||
# test setup.py using each tested version
|
# test setup.py using each tested version
|
||||||
|
- python: 3.6
|
||||||
|
env: TOXENV=setup36
|
||||||
|
|
||||||
- python: 3.5
|
- python: 3.5
|
||||||
env: TOXENV=setup35
|
env: TOXENV=setup35
|
||||||
|
|
||||||
- python: 3.4
|
- python: 3.4
|
||||||
env: TOXENV=setup34
|
env: TOXENV=setup34
|
||||||
|
|
||||||
- python: 3.3
|
- python: 3.3
|
||||||
env: TOXENV=setup33
|
env: TOXENV=setup33
|
||||||
|
|
||||||
- python: 2.7
|
- python: 2.7
|
||||||
env: TOXENV=setup27
|
env: TOXENV=setup27
|
||||||
|
|
||||||
allow_failures:
|
allow_failures:
|
||||||
# pep8 failures shouldn't be considered fatal
|
# pep8 failures shouldn't be considered fatal
|
||||||
- env: TOXENV=pep8
|
- env: TOXENV=pep8
|
||||||
# pypy and pypy3 tests are just for fun
|
# pypy and pypy3 tests are just for fun
|
||||||
- python: "pypy"
|
- python: "pypy"
|
||||||
- python: "pypy3"
|
- python: "pypy3"
|
||||||
|
# osx, until it's working
|
||||||
|
- os: osx
|
||||||
|
|
||||||
# install requirements
|
# install requirements
|
||||||
install:
|
install:
|
||||||
# make sure libffi-dev, gnupg2, and pgpdump are installed
|
- ./install_dependencies.${TRAVIS_OS_NAME}.sh
|
||||||
- sudo apt-get update
|
|
||||||
- sudo apt-get install -y libffi-dev gnupg2 pgpdump
|
|
||||||
# ensure tox and coveralls are installed
|
# ensure tox and coveralls are installed
|
||||||
- pip install tox python-coveralls
|
- pip install tox python-coveralls
|
||||||
|
|
||||||
# set TOXENV if it isn't yet
|
# set TOXENV if it isn't yet
|
||||||
before_script:
|
before_script:
|
||||||
- if [[ -z "$TOXENV" ]]; then export TOXENV=py${TRAVIS_PYTHON_VERSION//.}; fi
|
- if [[ -z "$TOXENV" ]]; then export TOXENV=py${TRAVIS_PYTHON_VERSION//.}; fi
|
||||||
- if [[ "$TRAVIS_PYTHON_VERSION" == 'pypy' ]]; then export TOXENV=pypy; fi
|
- if [[ "${TRAVIS_PYTHON_VERSION}" == 'pypy' ]]; then export TOXENV=pypy; fi
|
||||||
- if [[ "$TRAVIS_PYTHON_VERSION" == 'pypy3' ]]; then export TOXENV=pypy3; fi
|
- if [[ "${TRAVIS_PYTHON_VERSION}" == 'pypy3' ]]; then export TOXENV=pypy3; fi
|
||||||
|
|
||||||
# run tox
|
# run tox
|
||||||
script:
|
script:
|
||||||
- ./tox.sh
|
- ./tox.sh
|
||||||
|
|
||||||
# and report coverage to coveralls, but only if this was a pytest run
|
# and report coverage to coveralls, but only if this was a pytest run
|
||||||
after_success:
|
after_success:
|
||||||
- if [[ "${TOXENV}" == "py"* ]]; then coveralls; fi
|
- if [[ "${TOXENV}" == "py"* ]]; then coveralls; fi
|
||||||
|
|||||||
10
README.rst
10
README.rst
@@ -38,12 +38,19 @@ Documentation
|
|||||||
|
|
||||||
`PGPy Documentation <https://pythonhosted.org/PGPy/>`_
|
`PGPy Documentation <https://pythonhosted.org/PGPy/>`_
|
||||||
|
|
||||||
|
Discussion
|
||||||
|
----------
|
||||||
|
|
||||||
|
Please report any bugs found on the `issue tracker <https://github.com/SecurityInnovation/PGPy/issues>`_
|
||||||
|
|
||||||
|
You can also join ``#pgpy`` on Freenode to ask questions or get involved
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
- Python 3 >= 3.3; Python 2 >= 2.7
|
- Python 3 >= 3.3; Python 2 >= 2.7
|
||||||
|
|
||||||
Tested with: 3.5, 3.4, 3.3, 2.7
|
Tested with: 3.6, 3.5, 3.4, 3.3, 2.7
|
||||||
|
|
||||||
- `Cryptography <https://pypi.python.org/pypi/cryptography>`_
|
- `Cryptography <https://pypi.python.org/pypi/cryptography>`_
|
||||||
|
|
||||||
@@ -59,3 +66,4 @@ License
|
|||||||
-------
|
-------
|
||||||
|
|
||||||
BSD 3-Clause licensed. See the bundled `LICENSE <https://github.com/SecurityInnovation/PGPy/blob/master/LICENSE>`_ file for more details.
|
BSD 3-Clause licensed. See the bundled `LICENSE <https://github.com/SecurityInnovation/PGPy/blob/master/LICENSE>`_ file for more details.
|
||||||
|
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
# $Id$
|
|
||||||
# Maintainer: Michael Greene <mgreene@securityinnovation.com>
|
|
||||||
# Contributor: Michael Greene <mgreene@securityinnovation.com>
|
|
||||||
|
|
||||||
pkgbase=python-pgpy
|
|
||||||
pkgname=('python-pgpy' 'python2-pgpy')
|
|
||||||
pkgver=0.4.0
|
|
||||||
pkgrel=1
|
|
||||||
pkgdesc="Pretty Good Privacy for Python - a pure Python OpenPGP implementation."
|
|
||||||
arch=('any')
|
|
||||||
license=('BSD')
|
|
||||||
url="https://github.com/SecurityInnovation/PGPy"
|
|
||||||
makedepends=('python-setuptools' 'python-cryptography' 'python-six' 'python-pyasn1'
|
|
||||||
'python2-setuptools' 'python2-cryptography' 'python2-enum34' 'python2-singledispatch' 'python2-six' 'python2-pyasn1')
|
|
||||||
source=("https://pypi.python.org/packages/0a/2c/bfe57ac97d31fcd7172df43770d68bab1fbd38d629448ec8013f4714e779/PGPy-0.4.0a.tar.gz")
|
|
||||||
sha256sums=('0025d65f2db2886868ac5af68a85322d255ed52211756c6141c9d46264091da2')
|
|
||||||
sha384sums=('4ea26e952e6004778661072dbe4644351716b53122f4e96b8d2c44e57c31e23dc5e62e74f5700f628db2c163e01317c8')
|
|
||||||
sha512sums=('527625a143e6fc221f7f5d84455546b9687287059c1b28f4e16db46f80a2227e69f0781b95752797c212d3ff788979622331285a55f32bd3bbe4f108328ae2ed')
|
|
||||||
|
|
||||||
prepare() {
|
|
||||||
cp -a PGPy-${pkgver}{,-python2}
|
|
||||||
}
|
|
||||||
|
|
||||||
build() {
|
|
||||||
# Build Python 3 module
|
|
||||||
cd ${srcdir}/PGPy-${pkgver}
|
|
||||||
python3 setup.py build
|
|
||||||
|
|
||||||
# Build python2 module
|
|
||||||
cd ${srcdir}/PGPy-${pkgver}-python2
|
|
||||||
python2 setup.py build
|
|
||||||
}
|
|
||||||
|
|
||||||
package_python-pgpy() {
|
|
||||||
depends=('python-cryptography>=1.1.0' 'python-six>=1.9.0' 'python-pyasn1')
|
|
||||||
|
|
||||||
cd PGPy-${pkgver}
|
|
||||||
python3 setup.py install --root="${pkgdir}" --optimize=1 --skip-build
|
|
||||||
install -D -m 644 LICENSE ${pkgdir}/usr/share/licenses/python-pgpy/LICENSE
|
|
||||||
}
|
|
||||||
|
|
||||||
package_python2-pgpy() {
|
|
||||||
depends=('python2-cryptography>=1.1.0' 'python2-six>=1.9.0' 'python2-enum34' 'python2-singledispatch' 'python2-pyasn1')
|
|
||||||
|
|
||||||
cd PGPy-${pkgver}-python2
|
|
||||||
python2 setup.py install --root="${pkgdir}" --optimize=1 --skip-build
|
|
||||||
install -D -m 644 LICENSE ${pkgdir}/usr/share/licenses/python2-pgpy/LICENSE
|
|
||||||
}
|
|
||||||
11
debian/changelog
vendored
11
debian/changelog
vendored
@@ -1,11 +0,0 @@
|
|||||||
pgpy (0.4.0a-1) unstable; urgency=low
|
|
||||||
|
|
||||||
* Updated to latest release
|
|
||||||
|
|
||||||
-- Michael <mgreene@securityinnovation.com> Thu, 21 Apr 2016 21:15:38 -0700
|
|
||||||
|
|
||||||
pgpy (0.3.0-1) unstable; urgency=low
|
|
||||||
|
|
||||||
* Initial release (Closes: #62)
|
|
||||||
|
|
||||||
-- Michael <mgreene@securityinnovation.com> Tue, 07 Oct 2014 16:37:53 -0700
|
|
||||||
1
debian/compat
vendored
1
debian/compat
vendored
@@ -1 +0,0 @@
|
|||||||
8
|
|
||||||
35
debian/control
vendored
35
debian/control
vendored
@@ -1,35 +0,0 @@
|
|||||||
Source: pgpy
|
|
||||||
Section: python
|
|
||||||
Priority: optional
|
|
||||||
Maintainer: Michael Greene <mgreene@securityinnovation.com>
|
|
||||||
Build-Depends: debhelper (>= 8.0.0), cdbs,
|
|
||||||
python3-all-dev (>= 3.2), python3-setuptools,
|
|
||||||
python-all-dev (>= 2.7), python-setuptools
|
|
||||||
Standards-Version: 3.9.2
|
|
||||||
X-Python-Version: >= 2.7
|
|
||||||
X-Python3-Version: >= 3.3
|
|
||||||
Vcs-Git: https://github.com/SecurityInnovation/PGPy.git
|
|
||||||
Vcs-Browser: https://github.com/SecurityInnovation/PGPy
|
|
||||||
|
|
||||||
Package: python-pgpy
|
|
||||||
Architecture: all
|
|
||||||
Depends: ${misc:Depends}, ${python:Depends}
|
|
||||||
Recommends: python-pgpy-doc
|
|
||||||
Description: Pretty Good Privacy for Python.
|
|
||||||
A Pure-Python implementation of OpenPGP.
|
|
||||||
|
|
||||||
Package: python3-pgpy
|
|
||||||
Architecture: all
|
|
||||||
Depends: ${misc:Depends}, ${python3:Depends}, python3-six
|
|
||||||
Recommends: python-pgpy-doc
|
|
||||||
Description: Pretty Good Privacy for Python.
|
|
||||||
A Pure-Python implementation of OpenPGP.
|
|
||||||
|
|
||||||
Package: python-pgpy-doc
|
|
||||||
Section: doc
|
|
||||||
Architecture: all
|
|
||||||
Recommends: python3-pgpy-doc, python-pgpy-doc
|
|
||||||
Description: Pretty Good Privacy for Python (documentation).
|
|
||||||
A Pure-Python implementation of OpenPGP.
|
|
||||||
.
|
|
||||||
This package contains the HTML documentation.
|
|
||||||
33
debian/copyright
vendored
33
debian/copyright
vendored
@@ -1,33 +0,0 @@
|
|||||||
Format: http://dep.debian.net/deps/dep5
|
|
||||||
Upstream-Name: PGPy
|
|
||||||
Source: https://github.com/SecurityInnovation/PGPy
|
|
||||||
|
|
||||||
Files: *
|
|
||||||
Copyright: 2014-2016 Michael Greene, 2014-2016 Security Innovation, Inc
|
|
||||||
License: BSD 3-Clause
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of the {organization} nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
1
debian/docs
vendored
1
debian/docs
vendored
@@ -1 +0,0 @@
|
|||||||
README.rst
|
|
||||||
3
debian/python-pgpy-doc.docs
vendored
3
debian/python-pgpy-doc.docs
vendored
@@ -1,3 +0,0 @@
|
|||||||
./docs/source/api
|
|
||||||
./docs/source/examples
|
|
||||||
./docs/source/*.rst
|
|
||||||
1
debian/python-pgpy.install
vendored
1
debian/python-pgpy.install
vendored
@@ -1 +0,0 @@
|
|||||||
usr/lib/python2*
|
|
||||||
5
debian/python-pgpy.pydist
vendored
5
debian/python-pgpy.pydist
vendored
@@ -1,5 +0,0 @@
|
|||||||
enum34 python-enum34
|
|
||||||
singledispatch python-singledispatch
|
|
||||||
six python-six (>= 1.9.0)
|
|
||||||
cryptography python-cryptography (>= 1.1.0)
|
|
||||||
pyasn1 python-pyasn1
|
|
||||||
1
debian/python3-pgpy.install
vendored
1
debian/python3-pgpy.install
vendored
@@ -1 +0,0 @@
|
|||||||
usr/lib/python3*
|
|
||||||
5
debian/python3-pgpy.pydist
vendored
5
debian/python3-pgpy.pydist
vendored
@@ -1,5 +0,0 @@
|
|||||||
enum34 -3.4 python3-enum34
|
|
||||||
singledispatch python3-singledispatch
|
|
||||||
six python3-six (>= 1.9.0)
|
|
||||||
cryptography python3-cryptography (>= 1.1.0)
|
|
||||||
pyasn1 python3-pyasn1
|
|
||||||
8
debian/rules
vendored
8
debian/rules
vendored
@@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/make -f
|
|
||||||
# -*- makefile -*-
|
|
||||||
SHELL=/bin/bash
|
|
||||||
|
|
||||||
DEB_PYTHON_PACKAGES_EXCLUDE = python-pgpy-doc
|
|
||||||
|
|
||||||
include /usr/share/cdbs/1/rules/debhelper.mk
|
|
||||||
include /usr/share/cdbs/1/class/python-distutils.mk
|
|
||||||
1
debian/source/format
vendored
1
debian/source/format
vendored
@@ -1 +0,0 @@
|
|||||||
3.0 (native)
|
|
||||||
@@ -8,16 +8,13 @@ Classes
|
|||||||
|
|
||||||
.. autoclass:: PGPKey
|
.. autoclass:: PGPKey
|
||||||
:members:
|
:members:
|
||||||
:noindex:
|
|
||||||
|
|
||||||
.. py:attribute:: ascii_header
|
.. py:attribute:: ascii_header
|
||||||
:noindex:
|
|
||||||
:annotation: = OrderedDict([('Version', 'PGPy v|version|')])
|
:annotation: = OrderedDict([('Version', 'PGPy v|version|')])
|
||||||
|
|
||||||
An :py:obj:`~collections.OrderedDict` of headers that appear, in order, in the ASCII-armored form of this object.
|
An :py:obj:`~collections.OrderedDict` of headers that appear, in order, in the ASCII-armored form of this object.
|
||||||
|
|
||||||
.. py:classmethod:: from_file(filename)
|
.. py:classmethod:: from_file(filename)
|
||||||
:noindex:
|
|
||||||
|
|
||||||
Create a new :py:obj:`PGPKey` object, with contents loaded from a file. May be binary or ASCII armored.
|
Create a new :py:obj:`PGPKey` object, with contents loaded from a file. May be binary or ASCII armored.
|
||||||
|
|
||||||
@@ -32,7 +29,6 @@ Classes
|
|||||||
# others: { (Fingerprint, bool(key.is_public): PGPKey }
|
# others: { (Fingerprint, bool(key.is_public): PGPKey }
|
||||||
|
|
||||||
.. py:classmethod:: from_blob(blob)
|
.. py:classmethod:: from_blob(blob)
|
||||||
:noindex:
|
|
||||||
|
|
||||||
Create a new :py:obj:`PGPKey` object, with contents loaded from a blob. May be binary or ASCII armored.
|
Create a new :py:obj:`PGPKey` object, with contents loaded from a blob. May be binary or ASCII armored.
|
||||||
|
|
||||||
@@ -55,7 +51,6 @@ Classes
|
|||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. py:attribute:: ascii_header
|
.. py:attribute:: ascii_header
|
||||||
:noindex:
|
|
||||||
|
|
||||||
An :py:obj:`~collections.OrderedDict` of headers that appear, in order, in the ASCII-armored form of this object.
|
An :py:obj:`~collections.OrderedDict` of headers that appear, in order, in the ASCII-armored form of this object.
|
||||||
|
|
||||||
@@ -67,12 +62,10 @@ Classes
|
|||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. py:attribute:: ascii_header
|
.. py:attribute:: ascii_header
|
||||||
:noindex:
|
|
||||||
|
|
||||||
An :py:obj:`~collections.OrderedDict` of headers that appear, in order, in the ASCII-armored form of this object.
|
An :py:obj:`~collections.OrderedDict` of headers that appear, in order, in the ASCII-armored form of this object.
|
||||||
|
|
||||||
.. py:classmethod:: from_file(filename)
|
.. py:classmethod:: from_file(filename)
|
||||||
:noindex:
|
|
||||||
|
|
||||||
Create a new :py:obj:`PGPMessage` object, with contents loaded from a file. May be binary or ASCII armored.
|
Create a new :py:obj:`PGPMessage` object, with contents loaded from a file. May be binary or ASCII armored.
|
||||||
|
|
||||||
@@ -83,7 +76,6 @@ Classes
|
|||||||
:returns: :py:obj:`PGPMessage`
|
:returns: :py:obj:`PGPMessage`
|
||||||
|
|
||||||
.. py:classmethod:: from_blob(blob)
|
.. py:classmethod:: from_blob(blob)
|
||||||
:noindex:
|
|
||||||
|
|
||||||
Create a new :py:obj:`PGPMessage` object, with contents loaded from a blob. May be binary or ASCII armored.
|
Create a new :py:obj:`PGPMessage` object, with contents loaded from a blob. May be binary or ASCII armored.
|
||||||
|
|
||||||
@@ -102,12 +94,10 @@ Classes
|
|||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. py:attribute:: ascii_header
|
.. py:attribute:: ascii_header
|
||||||
:noindex:
|
|
||||||
|
|
||||||
An :py:obj:`~collections.OrderedDict` of headers that appear, in order, in the ASCII-armored form of this object.
|
An :py:obj:`~collections.OrderedDict` of headers that appear, in order, in the ASCII-armored form of this object.
|
||||||
|
|
||||||
.. py:classmethod:: from_file(filename)
|
.. py:classmethod:: from_file(filename)
|
||||||
:noindex:
|
|
||||||
|
|
||||||
Create a new :py:obj:`PGPSignature` object, with contents loaded from a file. May be binary or ASCII armored.
|
Create a new :py:obj:`PGPSignature` object, with contents loaded from a file. May be binary or ASCII armored.
|
||||||
|
|
||||||
@@ -118,7 +108,6 @@ Classes
|
|||||||
:returns: :py:obj:`PGPSignature`
|
:returns: :py:obj:`PGPSignature`
|
||||||
|
|
||||||
.. py:classmethod:: from_blob(blob)
|
.. py:classmethod:: from_blob(blob)
|
||||||
:noindex:
|
|
||||||
|
|
||||||
Create a new :py:obj:`PGPSignature` object, with contents loaded from a blob. May be binary or ASCII armored.
|
Create a new :py:obj:`PGPSignature` object, with contents loaded from a blob. May be binary or ASCII armored.
|
||||||
|
|
||||||
@@ -8,68 +8,53 @@ Constants
|
|||||||
|
|
||||||
.. autoclass:: PubKeyAlgorithm
|
.. autoclass:: PubKeyAlgorithm
|
||||||
:no-members:
|
:no-members:
|
||||||
:noindex:
|
|
||||||
|
|
||||||
.. autoattribute:: RSAEncryptOrSign
|
.. autoattribute:: RSAEncryptOrSign
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: DSA
|
.. autoattribute:: DSA
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: ElGamal
|
.. autoattribute:: ElGamal
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: ECDH
|
.. autoattribute:: ECDH
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: ECDSA
|
.. autoattribute:: ECDSA
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
:py:class:`EllipticCurveOID`
|
:py:class:`EllipticCurveOID`
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
.. autoclass:: EllipticCurveOID
|
.. autoclass:: EllipticCurveOID
|
||||||
:noindex:
|
:no-members:
|
||||||
|
|
||||||
.. autoattribute:: Curve25519
|
.. autoattribute:: Curve25519
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Ed25519
|
.. autoattribute:: Ed25519
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: NIST_P256
|
.. autoattribute:: NIST_P256
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: NIST_P384
|
.. autoattribute:: NIST_P384
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: NIST_P521
|
.. autoattribute:: NIST_P521
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Brainpool_P256
|
.. autoattribute:: Brainpool_P256
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Brainpool_P384
|
.. autoattribute:: Brainpool_P384
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Brainpool_P512
|
.. autoattribute:: Brainpool_P512
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: SECP256K1
|
.. autoattribute:: SECP256K1
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
|
|
||||||
@@ -78,38 +63,35 @@ Constants
|
|||||||
|
|
||||||
.. autoclass:: SymmetricKeyAlgorithm
|
.. autoclass:: SymmetricKeyAlgorithm
|
||||||
:no-members:
|
:no-members:
|
||||||
:noindex:
|
|
||||||
|
|
||||||
.. autoattribute:: IDEA
|
.. autoattribute:: IDEA
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: TripleDES
|
.. autoattribute:: TripleDES
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: CAST5
|
.. autoattribute:: CAST5
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Blowfish
|
.. autoattribute:: Blowfish
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: AES128
|
.. autoattribute:: AES128
|
||||||
:noindex:
|
:annotation:
|
||||||
|
|
||||||
|
.. autoattribute:: AES192
|
||||||
|
:annotation:
|
||||||
|
|
||||||
|
.. autoattribute:: AES256
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Camellia128
|
.. autoattribute:: Camellia128
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Camellia192
|
.. autoattribute:: Camellia192
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Camellia256
|
.. autoattribute:: Camellia256
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
|
|
||||||
@@ -118,22 +100,17 @@ Constants
|
|||||||
|
|
||||||
.. autoclass:: CompressionAlgorithm
|
.. autoclass:: CompressionAlgorithm
|
||||||
:no-members:
|
:no-members:
|
||||||
:noindex:
|
|
||||||
|
|
||||||
.. autoattribute:: Uncompressed
|
.. autoattribute:: Uncompressed
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: ZIP
|
.. autoattribute:: ZIP
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: ZLIB
|
.. autoattribute:: ZLIB
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: BZ2
|
.. autoattribute:: BZ2
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
:py:class:`HashAlgorithm`
|
:py:class:`HashAlgorithm`
|
||||||
@@ -141,34 +118,26 @@ Constants
|
|||||||
|
|
||||||
.. autoclass:: HashAlgorithm
|
.. autoclass:: HashAlgorithm
|
||||||
:no-members:
|
:no-members:
|
||||||
:noindex:
|
|
||||||
|
|
||||||
.. autoattribute:: MD5
|
.. autoattribute:: MD5
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: SHA1
|
.. autoattribute:: SHA1
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: RIPEMD160
|
.. autoattribute:: RIPEMD160
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: SHA256
|
.. autoattribute:: SHA256
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: SHA384
|
.. autoattribute:: SHA384
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: SHA512
|
.. autoattribute:: SHA512
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: SHA224
|
.. autoattribute:: SHA224
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
|
|
||||||
@@ -177,62 +146,47 @@ Constants
|
|||||||
|
|
||||||
.. autoclass:: SignatureType
|
.. autoclass:: SignatureType
|
||||||
:no-members:
|
:no-members:
|
||||||
:noindex:
|
|
||||||
|
|
||||||
.. autoattribute:: BinaryDocument
|
.. autoattribute:: BinaryDocument
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: CanonicalDocument
|
.. autoattribute:: CanonicalDocument
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Standalone
|
.. autoattribute:: Standalone
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Generic_Cert
|
.. autoattribute:: Generic_Cert
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Persona_Cert
|
.. autoattribute:: Persona_Cert
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Positive_Cert
|
.. autoattribute:: Positive_Cert
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Subkey_Binding
|
.. autoattribute:: Subkey_Binding
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: PrimaryKey_Binding
|
.. autoattribute:: PrimaryKey_Binding
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: DirectlyOnKey
|
.. autoattribute:: DirectlyOnKey
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: KeyRevocation
|
.. autoattribute:: KeyRevocation
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: SubkeyRevocation
|
.. autoattribute:: SubkeyRevocation
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: CertRevocation
|
.. autoattribute:: CertRevocation
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Timestamp
|
.. autoattribute:: Timestamp
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: ThirdParty_Confirmation
|
.. autoattribute:: ThirdParty_Confirmation
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
|
|
||||||
@@ -241,34 +195,26 @@ Constants
|
|||||||
|
|
||||||
.. autoclass:: KeyFlags
|
.. autoclass:: KeyFlags
|
||||||
:no-members:
|
:no-members:
|
||||||
:noindex:
|
|
||||||
|
|
||||||
.. autoattribute:: Certify
|
.. autoattribute:: Certify
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Sign
|
.. autoattribute:: Sign
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: EncryptCommunications
|
.. autoattribute:: EncryptCommunications
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: EncryptStorage
|
.. autoattribute:: EncryptStorage
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Split
|
.. autoattribute:: Split
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Authentication
|
.. autoattribute:: Authentication
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: MultiPerson
|
.. autoattribute:: MultiPerson
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
|
|
||||||
@@ -277,25 +223,19 @@ Constants
|
|||||||
|
|
||||||
.. autoclass:: RevocationReason
|
.. autoclass:: RevocationReason
|
||||||
:no-members:
|
:no-members:
|
||||||
:noindex:
|
|
||||||
|
|
||||||
.. autoattribute:: NotSpecified
|
.. autoattribute:: NotSpecified
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Superseded
|
.. autoattribute:: Superseded
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Compromised
|
.. autoattribute:: Compromised
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: Retired
|
.. autoattribute:: Retired
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
.. autoattribute:: UserID
|
.. autoattribute:: UserID
|
||||||
:noindex:
|
|
||||||
:annotation:
|
:annotation:
|
||||||
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
Exceptions
|
|
||||||
==========
|
|
||||||
|
|
||||||
.. py:currentmodule:: pgpy.errors
|
|
||||||
|
|
||||||
.. automodule:: pgpy.errors
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
:noindex:
|
|
||||||
36
docs/source/api/exceptions.rst.inc
Normal file
36
docs/source/api/exceptions.rst.inc
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
Exceptions
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. py:currentmodule:: pgpy.errors
|
||||||
|
|
||||||
|
|
||||||
|
:py:class:`PGPError`
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
.. autoexception:: PGPError
|
||||||
|
|
||||||
|
|
||||||
|
:py:class:`PGPEncryptionError`
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
.. autoexception:: PGPEncryptionError
|
||||||
|
|
||||||
|
:py:class:`PGPDecryptionError`
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
.. autoexception:: PGPDecryptionError
|
||||||
|
|
||||||
|
:py:class:`PGPOpenSSLCipherNotSupported`
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
.. autoexception:: PGPOpenSSLCipherNotSupported
|
||||||
|
|
||||||
|
:py:class:`PGPInsecureCipher`
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
.. autoexception:: PGPInsecureCipher
|
||||||
|
|
||||||
|
:py:class:`WontImplementError`
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
.. autoexception:: WontImplementError
|
||||||
@@ -2,15 +2,8 @@
|
|||||||
PGPy API
|
PGPy API
|
||||||
********
|
********
|
||||||
|
|
||||||
.. toctree::
|
.. include:: exceptions.rst.inc
|
||||||
:hidden:
|
|
||||||
|
|
||||||
exceptions
|
.. include:: constants.rst.inc
|
||||||
constants
|
|
||||||
classes
|
|
||||||
|
|
||||||
.. include:: exceptions.rst
|
.. include:: classes.rst.inc
|
||||||
|
|
||||||
.. include:: constants.rst
|
|
||||||
|
|
||||||
.. include:: classes.rst
|
|
||||||
|
|||||||
@@ -4,10 +4,26 @@
|
|||||||
Changelog
|
Changelog
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
v0.4.1
|
||||||
|
======
|
||||||
|
|
||||||
|
Released: April 13, 2017
|
||||||
|
|
||||||
|
Bugs Fixed
|
||||||
|
----------
|
||||||
|
* Fixed an issue with dearmoring ASCII-armored PGP blocks with windows-style newlines (#156)
|
||||||
|
* Improved the robustness of the code that tunes the hash count for deriving symmetric encryption keys (#157)
|
||||||
|
* Fixed an issue with how public keys are created from private keys that was causing exports to become malformed (#168)
|
||||||
|
* Added explicit support for Python 3.6 (#166)
|
||||||
|
|
||||||
|
New Features
|
||||||
|
------------
|
||||||
|
* Added support for Brainpool Standard curves for users who have OpenSSL 1.0.2 available
|
||||||
|
|
||||||
v0.4.0
|
v0.4.0
|
||||||
======
|
======
|
||||||
|
|
||||||
Released: |today|
|
Released: April 21, 2016
|
||||||
|
|
||||||
Bugs Fixed
|
Bugs Fixed
|
||||||
----------
|
----------
|
||||||
|
|||||||
@@ -27,6 +27,21 @@ It is possible to generate most types of keys with PGPy now. The process is most
|
|||||||
ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.AES192, SymmetricKeyAlgorithm.AES128],
|
ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.AES192, SymmetricKeyAlgorithm.AES128],
|
||||||
compression=[CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2, CompressionAlgorithm.ZIP, CompressionAlgorithm.Uncompressed])
|
compression=[CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2, CompressionAlgorithm.ZIP, CompressionAlgorithm.Uncompressed])
|
||||||
|
|
||||||
|
Specifying key expiration can be done using the ``key_expires`` keyword when adding the user id. Expiration can be specified
|
||||||
|
using a :py:obj:`datetime.datetime` or a :py:obj:`datetime.timedelta` object::
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
# in this example, we'll use fewer preferences for the sake of brevity, and set the key to expire in 10 years
|
||||||
|
key = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 4096)
|
||||||
|
uid = pgpy.PGPUID.new('Nikola Tesla') # comment and email are optional
|
||||||
|
|
||||||
|
# the key_expires keyword accepts a :py:obj:`datetime.datetime`
|
||||||
|
key.add_uid(uid, usage={KeyFlags.Sign}, hashes=[HashAlgorithm.SHA512, HashAlgorithm.SHA256],
|
||||||
|
ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.Camellia256],
|
||||||
|
compression=[CompressionAlgorithm.BZ2, CompressionAlgorithm.Uncompressed],
|
||||||
|
key_expires=timedelta(days=365))
|
||||||
|
|
||||||
Generating Sub Keys
|
Generating Sub Keys
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@@ -35,7 +50,9 @@ Generating a subkey is similar to the process above, except that it requires an
|
|||||||
# assuming we already have a primary key, we can generate a new key and add it as a subkey thusly:
|
# assuming we already have a primary key, we can generate a new key and add it as a subkey thusly:
|
||||||
subkey = pgpy.PGPKey.new(PubKeyAlgorithm.RSA, 4096)
|
subkey = pgpy.PGPKey.new(PubKeyAlgorithm.RSA, 4096)
|
||||||
|
|
||||||
# preferences that are specific to the subkey can be chosen here, otherwise the key will use the primary key's preferences.
|
# preferences that are specific to the subkey can be chosen here
|
||||||
|
# any preference(s) needed for actions by this subkey that not specified here
|
||||||
|
# will seamlessly "inherit" from those specified on the selected User ID
|
||||||
key.add_subkey(subkey, usage={KeyFlags.Authentication})
|
key.add_subkey(subkey, usage={KeyFlags.Authentication})
|
||||||
|
|
||||||
Loading Keys
|
Loading Keys
|
||||||
@@ -86,7 +103,7 @@ It is usually recommended to passphrase-protect private keys. Adding a passphras
|
|||||||
|
|
||||||
# key.is_public is False
|
# key.is_public is False
|
||||||
# key.is_protected is False
|
# key.is_protected is False
|
||||||
key.protect("C0rrectPassphr@se")
|
key.protect("C0rrectPassphr@se", SymmetricKeyAlgorithm.AES256, HashAlgorithm.SHA256)
|
||||||
# key.is_protected is now True
|
# key.is_protected is now True
|
||||||
|
|
||||||
Unlocking Protected Secret Keys
|
Unlocking Protected Secret Keys
|
||||||
@@ -106,6 +123,11 @@ Key unlocking is quite simple::
|
|||||||
# enc_key.is_unlocked is now True
|
# enc_key.is_unlocked is now True
|
||||||
...
|
...
|
||||||
|
|
||||||
|
# This form works equivalently, but may be more semantically clear in some cases:
|
||||||
|
with enc_key.unlock("C0rrectPassphr@se") as ukey:
|
||||||
|
# ukey is just a reference to enc_key in this case
|
||||||
|
...
|
||||||
|
|
||||||
Exporting Keys
|
Exporting Keys
|
||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@@ -126,4 +148,3 @@ in Python 2::
|
|||||||
|
|
||||||
# ASCII armored
|
# ASCII armored
|
||||||
keystr = str(key)
|
keystr = str(key)
|
||||||
|
|
||||||
|
|||||||
@@ -57,4 +57,3 @@ in Python 2::
|
|||||||
# if message is cleartext, this will also properly canonicalize and dash-escape
|
# if message is cleartext, this will also properly canonicalize and dash-escape
|
||||||
# the message text
|
# the message text
|
||||||
msgstr = str(message)
|
msgstr = str(message)
|
||||||
|
|
||||||
|
|||||||
@@ -2,29 +2,63 @@
|
|||||||
Installation
|
Installation
|
||||||
************
|
************
|
||||||
|
|
||||||
.. highlight:: bash
|
.. highlight:: console
|
||||||
|
|
||||||
Platform Specific Notes
|
Platform Specific Notes
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
|
Windows
|
||||||
|
-------
|
||||||
|
|
||||||
|
PGPy has not been formally tested on Windows. I see no reason why it wouldn't work, but your mileage may vary.
|
||||||
|
If you try it out and run into any issues, please submit bug reports on the `issue tracker <https://github.com/SecurityInnovation/PGPy/issues>`_!
|
||||||
|
|
||||||
Linux
|
Linux
|
||||||
-----
|
-----
|
||||||
|
|
||||||
Building PGPy on Linux requires a C compiler, headers for Python, headers for OpenSSL, and libffi.
|
Debian
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
PGPy is now in `Debian Sid <https://packages.debian.org/sid/python3-pgpy>`_, and can be installed simply::
|
||||||
|
|
||||||
|
$ sudo apt install python3-pgpy
|
||||||
|
|
||||||
|
Arch Linux
|
||||||
|
^^^^^^^^^^
|
||||||
|
|
||||||
|
PGPy is available on the `AUR <https://aur.archlinux.org/packages/python-pgpy/>`_
|
||||||
|
|
||||||
|
Gentoo
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
There are gentoo ebuilds available in the `gentoo branch <https://github.com/SecurityInnovation/PGPy/tree/gentoo>`_
|
||||||
|
|
||||||
|
RedHat/CentOS
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Coming Soon!
|
||||||
|
|
||||||
|
Other Linux
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
Building PGPy on Linux requires a C compiler, headers for Python, headers for OpenSSL, and libffi, to support building Cryptography.
|
||||||
|
|
||||||
For Debian/Ubuntu, these requirements can be installed like so::
|
For Debian/Ubuntu, these requirements can be installed like so::
|
||||||
|
|
||||||
$ sudo apt-get install build-essential libssl-dev libffi-dev python-dev
|
$ sudo apt install build-essential libssl-dev libffi-dev python-dev
|
||||||
|
|
||||||
You may need to install ``python3-dev`` if you are using PGPy on Python 3.
|
You may need to install ``python3-dev`` if you are using PGPy on Python 3.
|
||||||
|
|
||||||
|
For Fedora/RHEL derivatives, the build requirements can be installed like so::
|
||||||
|
|
||||||
|
$ sudo yum install gcc libffi-devel python-devel openssl-devel
|
||||||
|
|
||||||
Mac OS X
|
Mac OS X
|
||||||
--------
|
--------
|
||||||
|
|
||||||
If you are on Mac OS, you may experience more limited functionality without installing a more capable version of OpenSSL.
|
If you are on Mac OS, you may experience more limited functionality without installing a more capable version of OpenSSL.
|
||||||
|
|
||||||
You may refer to Cryptography's documentation on `Using your own OpenSSL on OS X <https://cryptography.io/en/latest/installation/#using-your-own-openssl-on-os-x>`_ for information on how to do so.
|
You may refer to Cryptography's documentation on `Building cryptography on OS X <https://cryptography.io/en/latest/installation/#building-cryptography-on-os-x>`_ for information on how to do so.
|
||||||
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
@@ -33,4 +67,3 @@ Installation
|
|||||||
Once you have the prerequisites specified above, PGPy can be installed from PyPI using pip, like so::
|
Once you have the prerequisites specified above, PGPy can be installed from PyPI using pip, like so::
|
||||||
|
|
||||||
$ pip install PGPy
|
$ pip install PGPy
|
||||||
|
|
||||||
|
|||||||
@@ -277,14 +277,14 @@ This section covers things that are considered extensions to PGP, but are not co
|
|||||||
:text: Some additional curves that can be used with ECDSA/ECDH that are not explicitly called out in :rfc:`6637`, but have standardized OIDs and are implemented in other software.
|
:text: Some additional curves that can be used with ECDSA/ECDH that are not explicitly called out in :rfc:`6637`, but have standardized OIDs and are implemented in other software.
|
||||||
|
|
||||||
:Curves:
|
:Curves:
|
||||||
- Curve, False, Brainpool P-256
|
- Curve, True, Brainpool P-256
|
||||||
- Curve, False, Brainpool P-384
|
- Curve, True, Brainpool P-384
|
||||||
- Curve, False, Brainpool P-512
|
- Curve, True, Brainpool P-512
|
||||||
- Curve, False, Curve25519
|
- Curve, False, Curve25519
|
||||||
- Curve, True, SECP256K1
|
- Curve, True, SECP256K1
|
||||||
|
|
||||||
.. note:: Use of Brainpool curves with ECDSA/ECDH
|
.. note:: Use of Brainpool curves with ECDSA/ECDH
|
||||||
:text: Although these curves are not explicitly mentioned in an RFC for OpenPGP at this point, GnuPG 2.1.x+ does support using them, so I have included them here as well.
|
Although these curves are not explicitly mentioned in an RFC for OpenPGP at this point, GnuPG 2.1.x+ does support using them, so I have included them here as well.
|
||||||
|
|
||||||
\* Cipher availability depends on the currently installed OpenSSL being compiled with support for it
|
\* Cipher availability depends on the currently installed OpenSSL being compiled with support for it
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
# Copyright 2014 Michael Greene
|
|
||||||
# Distributed under the terms of the BSD 3-Clause License
|
|
||||||
# $HEADER: $
|
|
||||||
|
|
||||||
EAPI=5
|
|
||||||
PYTHON_COMPAT=( python{2_7,3_2,3_3,3_4} )
|
|
||||||
|
|
||||||
inherit distutils-r1
|
|
||||||
|
|
||||||
DESCRIPTION="Pretty Good Privacy for Python - a pure Python OpenPGP implementation."
|
|
||||||
HOMEPAGE="https://github.com/SecurityInnovation/PGPy"
|
|
||||||
SRC_URI="mirror://pypi/P/PGPy/PGPy-${PV}.tar.gz"
|
|
||||||
|
|
||||||
LICENSE="BSD"
|
|
||||||
SLOT="0"
|
|
||||||
KEYWORDS="~amd64"
|
|
||||||
IUSE=""
|
|
||||||
|
|
||||||
DEPEND="dev-python/setuptools[${PYTHON_USEDEP}]"
|
|
||||||
RDEPEND="dev-python/singledispatch[${PYTHON_USEDEP}]
|
|
||||||
>=dev-python/six-1.7.2[${PYTHON_USEDEP}]
|
|
||||||
>=dev-python/cryptography-0.5.4[${PYTHON_USEDEP}]
|
|
||||||
$(python_gen_cond_dep 'dev-python/enum34[${PYTHON_USEDEP}]' python2_7 python3_2 python3_3)"
|
|
||||||
DOCS=( README.rst )
|
|
||||||
|
|
||||||
src_unpack() {
|
|
||||||
if [ "${A}" != "" ]; then
|
|
||||||
unpack ${A}
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "${WORKDIR}"
|
|
||||||
mv PGPy-${PV} pgpy-${PV}
|
|
||||||
}
|
|
||||||
4
install_dependencies.linux.sh
Executable file
4
install_dependencies.linux.sh
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# make sure libffi-dev, gnupg2, and pgpdump are installed
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libffi-dev gnupg2 pgpdump
|
||||||
22
install_dependencies.osx.sh
Executable file
22
install_dependencies.osx.sh
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# mapping to get from the TRAVIS_PYTHON_VERSION environment variable to something pyenv understands
|
||||||
|
# this will need to be manually kept up to date until travis support for python on osx improves
|
||||||
|
declare -A pyver
|
||||||
|
pyver["2.7"]="2.7.13"
|
||||||
|
pyver["3.3"]="3.3.6"
|
||||||
|
pyver["3.4"]="3.4.6"
|
||||||
|
pyver["3.5"]="3.5.3"
|
||||||
|
pyver["3.6"]="3.6.0"
|
||||||
|
pyver["pypy"]="pypy2-5.6.0"
|
||||||
|
pyver["pypy3"]="pypy3.3-5.5-alpha"
|
||||||
|
|
||||||
|
sudo brew update
|
||||||
|
# travis doesn't natively support python on osx yet, so start by installing pyenv
|
||||||
|
# also install newer openssl here
|
||||||
|
sudo brew install -y pyenv openssl
|
||||||
|
# now install the requested version of python, and set it to local
|
||||||
|
pyenv install ${pyver[${TRAVIS_PYTHON_VERSION}]}
|
||||||
|
pyenv local ${pyver[${TRAVIS_PYTHON_VERSION}]}
|
||||||
|
|
||||||
|
# make sure libffi-dev, gnupg2, pgpdump, and newer openssl are installed as well
|
||||||
|
sudo brew install -y libffi-dev gnupg2 pgpdump
|
||||||
@@ -15,4 +15,4 @@ __all__ = ['__author__',
|
|||||||
__author__ = "Michael Greene"
|
__author__ = "Michael Greene"
|
||||||
__copyright__ = "Copyright (c) 2014 Michael Greene"
|
__copyright__ = "Copyright (c) 2014 Michael Greene"
|
||||||
__license__ = "BSD"
|
__license__ = "BSD"
|
||||||
__version__ = str(LooseVersion("0.4.0"))
|
__version__ = str(LooseVersion("0.4.1"))
|
||||||
|
|||||||
54
pgpy/_curves.py
Normal file
54
pgpy/_curves.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
""" _curves.py
|
||||||
|
specify some additional curves that OpenSSL provides but cryptography doesn't explicitly expose
|
||||||
|
"""
|
||||||
|
|
||||||
|
from cryptography import utils
|
||||||
|
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import ec
|
||||||
|
|
||||||
|
from cryptography.hazmat.bindings.openssl.binding import Binding
|
||||||
|
|
||||||
|
__all__ = tuple()
|
||||||
|
|
||||||
|
# TODO: investigate defining additional curves using EC_GROUP_new_curve
|
||||||
|
# https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography#Defining_Curves
|
||||||
|
|
||||||
|
|
||||||
|
def _openssl_get_supported_curves():
|
||||||
|
if hasattr(_openssl_get_supported_curves, '_curves'):
|
||||||
|
return _openssl_get_supported_curves._curves
|
||||||
|
|
||||||
|
# use cryptography's cffi bindings to get an array of curve names
|
||||||
|
b = Binding()
|
||||||
|
cn = b.lib.EC_get_builtin_curves(b.ffi.NULL, 0)
|
||||||
|
cs = b.ffi.new('EC_builtin_curve[]', cn)
|
||||||
|
b.lib.EC_get_builtin_curves(cs, cn)
|
||||||
|
|
||||||
|
# store the result so we don't have to do all of this every time
|
||||||
|
curves = { b.ffi.string(b.lib.OBJ_nid2sn(c.nid)).decode('utf-8') for c in cs }
|
||||||
|
_openssl_get_supported_curves._curves = curves
|
||||||
|
return curves
|
||||||
|
|
||||||
|
|
||||||
|
@utils.register_interface(ec.EllipticCurve)
|
||||||
|
class BrainpoolP256R1(object):
|
||||||
|
name = 'brainpoolP256r1'
|
||||||
|
key_size = 256
|
||||||
|
|
||||||
|
|
||||||
|
@utils.register_interface(ec.EllipticCurve)
|
||||||
|
class BrainpoolP384R1(object):
|
||||||
|
name = 'brainpoolP384r1'
|
||||||
|
key_size = 384
|
||||||
|
|
||||||
|
|
||||||
|
@utils.register_interface(ec.EllipticCurve)
|
||||||
|
class BrainpoolP512R1(object):
|
||||||
|
name = 'brainpoolP512r1'
|
||||||
|
key_size = 512
|
||||||
|
|
||||||
|
|
||||||
|
# add these curves to the _CURVE_TYPES list
|
||||||
|
for curve in [BrainpoolP256R1, BrainpoolP384R1, BrainpoolP512R1]:
|
||||||
|
if curve.name not in ec._CURVE_TYPES and curve.name in _openssl_get_supported_curves():
|
||||||
|
ec._CURVE_TYPES[curve.name] = curve
|
||||||
@@ -20,6 +20,7 @@ from cryptography.hazmat.primitives.ciphers import algorithms
|
|||||||
|
|
||||||
from .decorators import classproperty
|
from .decorators import classproperty
|
||||||
from .types import FlagEnum
|
from .types import FlagEnum
|
||||||
|
from ._curves import BrainpoolP256R1, BrainpoolP384R1, BrainpoolP512R1
|
||||||
|
|
||||||
__all__ = ['Backend',
|
__all__ = ['Backend',
|
||||||
'EllipticCurveOID',
|
'EllipticCurveOID',
|
||||||
@@ -41,8 +42,8 @@ __all__ = ['Backend',
|
|||||||
'TrustFlags']
|
'TrustFlags']
|
||||||
|
|
||||||
|
|
||||||
# this is 100 KiB
|
# this is 50 KiB
|
||||||
_hashtunedata = bytearray([10, 11, 12, 13, 14, 15, 16, 17] * 128 * 100)
|
_hashtunedata = bytearray([10, 11, 12, 13, 14, 15, 16, 17] * 128 * 50)
|
||||||
|
|
||||||
|
|
||||||
class Backend(Enum):
|
class Backend(Enum):
|
||||||
@@ -71,29 +72,34 @@ class EllipticCurveOID(Enum):
|
|||||||
NIST_P521 = ('1.3.132.0.35', ec.SECP521R1)
|
NIST_P521 = ('1.3.132.0.35', ec.SECP521R1)
|
||||||
#: Brainpool Standard Curve, 256-bit
|
#: Brainpool Standard Curve, 256-bit
|
||||||
#:
|
#:
|
||||||
#: .. warning::
|
#: .. note::
|
||||||
#: This curve is not currently usable by PGPy
|
#: Requires OpenSSL >= 1.0.2
|
||||||
Brainpool_P256 = ('1.3.36.3.3.2.8.1.1.7', )
|
Brainpool_P256 = ('1.3.36.3.3.2.8.1.1.7', BrainpoolP256R1)
|
||||||
#: Brainpool Standard Curve, 384-bit
|
#: Brainpool Standard Curve, 384-bit
|
||||||
#:
|
#:
|
||||||
#: .. warning::
|
#: .. note::
|
||||||
#: This curve is not currently usable by PGPy
|
#: Requires OpenSSL >= 1.0.2
|
||||||
Brainpool_P384 = ('1.3.36.3.3.2.8.1.1.11', )
|
Brainpool_P384 = ('1.3.36.3.3.2.8.1.1.11', BrainpoolP384R1)
|
||||||
#: Brainpool Standard Curve, 512-bit
|
#: Brainpool Standard Curve, 512-bit
|
||||||
#:
|
#:
|
||||||
#: .. warning::
|
#: .. note::
|
||||||
#: This curve is not currently usable by PGPy
|
#: Requires OpenSSL >= 1.0.2
|
||||||
Brainpool_P512 = ('1.3.36.3.3.2.8.1.1.13', )
|
Brainpool_P512 = ('1.3.36.3.3.2.8.1.1.13', BrainpoolP512R1)
|
||||||
#: SECG curve secp256k1
|
#: SECG curve secp256k1
|
||||||
SECP256K1 = ('1.3.132.0.10', ec.SECP256K1)
|
SECP256K1 = ('1.3.132.0.10', ec.SECP256K1)
|
||||||
|
|
||||||
def __new__(cls, oid, curve=None):
|
def __new__(cls, oid, curve=None):
|
||||||
# preprocessing stage for enum members:
|
# preprocessing stage for enum members:
|
||||||
# - set enum_member.value to ObjectIdentifier(oid)
|
# - set enum_member.value to ObjectIdentifier(oid)
|
||||||
# - set enum_member.curve to curve
|
# - if curve is not None and curve.name is in ec._CURVE_TYPES, set enum_member.curve to curve
|
||||||
|
# - otherwise, set enum_member.curve to None
|
||||||
obj = object.__new__(cls)
|
obj = object.__new__(cls)
|
||||||
obj._value_ = ObjectIdentifier(oid)
|
obj._value_ = ObjectIdentifier(oid)
|
||||||
obj.curve = curve
|
obj.curve = None
|
||||||
|
|
||||||
|
if curve is not None and curve.name in ec._CURVE_TYPES:
|
||||||
|
obj.curve = curve
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -110,6 +116,7 @@ class EllipticCurveOID(Enum):
|
|||||||
# return the hash algorithm to specify in the KDF fields when generating a key
|
# return the hash algorithm to specify in the KDF fields when generating a key
|
||||||
algs = {256: HashAlgorithm.SHA256,
|
algs = {256: HashAlgorithm.SHA256,
|
||||||
384: HashAlgorithm.SHA384,
|
384: HashAlgorithm.SHA384,
|
||||||
|
512: HashAlgorithm.SHA512,
|
||||||
521: HashAlgorithm.SHA512}
|
521: HashAlgorithm.SHA512}
|
||||||
|
|
||||||
return algs.get(self.key_size, None)
|
return algs.get(self.key_size, None)
|
||||||
@@ -119,6 +126,7 @@ class EllipticCurveOID(Enum):
|
|||||||
# return the AES algorithm to specify in the KDF fields when generating a key
|
# return the AES algorithm to specify in the KDF fields when generating a key
|
||||||
algs = {256: SymmetricKeyAlgorithm.AES128,
|
algs = {256: SymmetricKeyAlgorithm.AES128,
|
||||||
384: SymmetricKeyAlgorithm.AES192,
|
384: SymmetricKeyAlgorithm.AES192,
|
||||||
|
512: SymmetricKeyAlgorithm.AES256,
|
||||||
521: SymmetricKeyAlgorithm.AES256}
|
521: SymmetricKeyAlgorithm.AES256}
|
||||||
|
|
||||||
return algs.get(self.key_size, None)
|
return algs.get(self.key_size, None)
|
||||||
@@ -343,15 +351,24 @@ class HashAlgorithm(IntEnum):
|
|||||||
return self._tuned_count
|
return self._tuned_count
|
||||||
|
|
||||||
def tune_count(self):
|
def tune_count(self):
|
||||||
start = time.time()
|
start = end = 0
|
||||||
h = self.hasher
|
htd = _hashtunedata[:]
|
||||||
h.update(_hashtunedata)
|
|
||||||
end = time.time()
|
while start == end:
|
||||||
|
# potentially do this multiple times in case the resolution of time.time is low enough that
|
||||||
|
# hashing 100 KiB isn't enough time to produce a measurable difference
|
||||||
|
# (e.g. if the timer for time.time doesn't have enough precision)
|
||||||
|
htd = htd + htd
|
||||||
|
h = self.hasher
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
h.update(htd)
|
||||||
|
end = time.time()
|
||||||
|
|
||||||
# now calculate how many bytes need to be hashed to reach our expected time period
|
# now calculate how many bytes need to be hashed to reach our expected time period
|
||||||
# GnuPG tunes for about 100ms, so we'll do that as well
|
# GnuPG tunes for about 100ms, so we'll do that as well
|
||||||
_TIME = 0.100
|
_TIME = 0.100
|
||||||
ct = int(len(_hashtunedata) * (_TIME / (end - start)))
|
ct = int(len(htd) * (_TIME / (end - start)))
|
||||||
c1 = ((ct >> (ct.bit_length() - 5)) - 16)
|
c1 = ((ct >> (ct.bit_length() - 5)) - 16)
|
||||||
c2 = (ct.bit_length() - 11)
|
c2 = (ct.bit_length() - 11)
|
||||||
c = ((c2 << 4) + c1)
|
c = ((c2 << 4) + c1)
|
||||||
@@ -432,7 +449,7 @@ class KeyFlags(FlagEnum):
|
|||||||
Sign = 0x02
|
Sign = 0x02
|
||||||
#: Signifies that a key may be used to encrypt messages.
|
#: Signifies that a key may be used to encrypt messages.
|
||||||
EncryptCommunications = 0x04
|
EncryptCommunications = 0x04
|
||||||
#: Signifies that a key may be used to encrypt storage. Currently equivalent to :py:obj:~pgpy.constants.EncryptCommunications`.
|
#: Signifies that a key may be used to encrypt storage. Currently equivalent to :py:obj:`~pgpy.constants.EncryptCommunications`.
|
||||||
EncryptStorage = 0x08
|
EncryptStorage = 0x08
|
||||||
#: Signifies that the private component of a given key may have been split by a secret-sharing mechanism. Split
|
#: Signifies that the private component of a given key may have been split by a secret-sharing mechanism. Split
|
||||||
#: keys are not currently supported by PGPy.
|
#: keys are not currently supported by PGPy.
|
||||||
|
|||||||
@@ -788,6 +788,17 @@ class String2Key(Field):
|
|||||||
def __nonzero__(self):
|
def __nonzero__(self):
|
||||||
return self.__bool__()
|
return self.__bool__()
|
||||||
|
|
||||||
|
def __copy__(self):
|
||||||
|
s2k = String2Key()
|
||||||
|
s2k.usage = self.usage
|
||||||
|
s2k.encalg = self.encalg
|
||||||
|
s2k.specifier = self.specifier
|
||||||
|
s2k.iv = self.iv
|
||||||
|
s2k.halg = self.halg
|
||||||
|
s2k.salt = copy.copy(self.salt)
|
||||||
|
s2k.count = self._count
|
||||||
|
return s2k
|
||||||
|
|
||||||
def parse(self, packet, iv=True):
|
def parse(self, packet, iv=True):
|
||||||
self.usage = packet[0]
|
self.usage = packet[0]
|
||||||
del packet[0]
|
del packet[0]
|
||||||
@@ -985,6 +996,13 @@ class PrivKey(PubKey):
|
|||||||
|
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
def __copy__(self):
|
||||||
|
pk = super(PrivKey, self).__copy__()
|
||||||
|
pk.s2k = copy.copy(self.s2k)
|
||||||
|
pk.encbytes = copy.copy(self.encbytes)
|
||||||
|
pk.chksum = copy.copy(self.chksum)
|
||||||
|
return pk
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def __privkey__(self):
|
def __privkey__(self):
|
||||||
"""return the requisite *PrivateKey class from the cryptography library"""
|
"""return the requisite *PrivateKey class from the cryptography library"""
|
||||||
|
|||||||
@@ -556,7 +556,7 @@ class SKESessionKeyV4(SKESessionKey):
|
|||||||
|
|
||||||
# if there is no ciphertext, then the first session key is the session key being used
|
# if there is no ciphertext, then the first session key is the session key being used
|
||||||
if len(self.ct) == 0:
|
if len(self.ct) == 0:
|
||||||
return sk
|
return self.symalg, sk
|
||||||
|
|
||||||
# otherwise, we now need to decrypt the encrypted session key
|
# otherwise, we now need to decrypt the encrypted session key
|
||||||
m = bytearray(_decrypt(bytes(self.ct), sk, self.symalg))
|
m = bytearray(_decrypt(bytes(self.ct), sk, self.symalg))
|
||||||
@@ -565,7 +565,7 @@ class SKESessionKeyV4(SKESessionKey):
|
|||||||
symalg = SymmetricKeyAlgorithm(m[0])
|
symalg = SymmetricKeyAlgorithm(m[0])
|
||||||
del m[0]
|
del m[0]
|
||||||
|
|
||||||
return (symalg, bytes(m))
|
return symalg, bytes(m)
|
||||||
|
|
||||||
def encrypt_sk(self, passphrase, sk):
|
def encrypt_sk(self, passphrase, sk):
|
||||||
# generate the salt and derive the key to encrypt sk with from it
|
# generate the salt and derive the key to encrypt sk with from it
|
||||||
@@ -867,7 +867,7 @@ class PrivKeyV4(PrivKey, PubKeyV4):
|
|||||||
|
|
||||||
def pubkey(self):
|
def pubkey(self):
|
||||||
# return a copy of ourselves, but just the public half
|
# return a copy of ourselves, but just the public half
|
||||||
pk = PubKeyV4()
|
pk = PubKeyV4() if not isinstance(self, PrivSubKeyV4) else PubSubKeyV4()
|
||||||
pk.created = self.created
|
pk.created = self.created
|
||||||
pk.pkalg = self.pkalg
|
pk.pkalg = self.pkalg
|
||||||
|
|
||||||
|
|||||||
@@ -162,8 +162,8 @@ class ByteFlag(Signature):
|
|||||||
_bytes = super(ByteFlag, self).__bytearray__()
|
_bytes = super(ByteFlag, self).__bytearray__()
|
||||||
_bytes += self.int_to_bytes(sum(self.flags))
|
_bytes += self.int_to_bytes(sum(self.flags))
|
||||||
# null-pad _bytes if they are not up to the end now
|
# null-pad _bytes if they are not up to the end now
|
||||||
if self.header.length + len(self.header) != len(_bytes):
|
if len(_bytes) < len(self):
|
||||||
_bytes += b'\x00' * (len(_bytes) - len(self.header) - 1)
|
_bytes += b'\x00' * (len(self) - len(_bytes))
|
||||||
return _bytes
|
return _bytes
|
||||||
|
|
||||||
def parse(self, packet):
|
def parse(self, packet):
|
||||||
|
|||||||
136
pgpy/pgp.py
136
pgpy/pgp.py
@@ -3,6 +3,7 @@
|
|||||||
this is where the armorable PGP block objects live
|
this is where the armorable PGP block objects live
|
||||||
"""
|
"""
|
||||||
import binascii
|
import binascii
|
||||||
|
import calendar
|
||||||
import collections
|
import collections
|
||||||
import contextlib
|
import contextlib
|
||||||
import copy
|
import copy
|
||||||
@@ -360,10 +361,9 @@ class PGPSignature(Armorable, ParentRef, PGPObject):
|
|||||||
"""
|
"""
|
||||||
_data += re.subn(br'\r?\n', b'\r\n', subject)[0]
|
_data += re.subn(br'\r?\n', b'\r\n', subject)[0]
|
||||||
|
|
||||||
if self.type in [SignatureType.Generic_Cert, SignatureType.Persona_Cert, SignatureType.Casual_Cert,
|
if self.type in {SignatureType.Generic_Cert, SignatureType.Persona_Cert, SignatureType.Casual_Cert,
|
||||||
SignatureType.Positive_Cert, SignatureType.CertRevocation, SignatureType.Subkey_Binding,
|
SignatureType.Positive_Cert, SignatureType.CertRevocation, SignatureType.Subkey_Binding,
|
||||||
SignatureType.PrimaryKey_Binding, SignatureType.DirectlyOnKey, SignatureType.KeyRevocation,
|
SignatureType.PrimaryKey_Binding}:
|
||||||
SignatureType.SubkeyRevocation]:
|
|
||||||
"""
|
"""
|
||||||
When a signature is made over a key, the hash data starts with the
|
When a signature is made over a key, the hash data starts with the
|
||||||
octet 0x99, followed by a two-octet length of the key, and then body
|
octet 0x99, followed by a two-octet length of the key, and then body
|
||||||
@@ -385,7 +385,7 @@ class PGPSignature(Armorable, ParentRef, PGPObject):
|
|||||||
if len(_s) > 0:
|
if len(_s) > 0:
|
||||||
_data += b'\x99' + self.int_to_bytes(len(_s), 2) + _s
|
_data += b'\x99' + self.int_to_bytes(len(_s), 2) + _s
|
||||||
|
|
||||||
if self.type in [SignatureType.Subkey_Binding, SignatureType.PrimaryKey_Binding, SignatureType.SubkeyRevocation]:
|
if self.type in {SignatureType.Subkey_Binding, SignatureType.PrimaryKey_Binding}:
|
||||||
"""
|
"""
|
||||||
A subkey binding signature
|
A subkey binding signature
|
||||||
(type 0x18) or primary key binding signature (type 0x19) then hashes
|
(type 0x18) or primary key binding signature (type 0x19) then hashes
|
||||||
@@ -400,8 +400,34 @@ class PGPSignature(Armorable, ParentRef, PGPObject):
|
|||||||
|
|
||||||
_data += b'\x99' + self.int_to_bytes(len(_s), 2) + _s
|
_data += b'\x99' + self.int_to_bytes(len(_s), 2) + _s
|
||||||
|
|
||||||
if self.type in [SignatureType.Generic_Cert, SignatureType.Persona_Cert, SignatureType.Casual_Cert,
|
if self.type in {SignatureType.KeyRevocation, SignatureType.SubkeyRevocation, SignatureType.DirectlyOnKey}:
|
||||||
SignatureType.Positive_Cert, SignatureType.CertRevocation]:
|
"""
|
||||||
|
The signature is calculated directly on the key being revoked. A
|
||||||
|
revoked key is not to be used. Only revocation signatures by the
|
||||||
|
key being revoked, or by an authorized revocation key, should be
|
||||||
|
considered valid revocation signatures.
|
||||||
|
|
||||||
|
Subkey revocation signature
|
||||||
|
The signature is calculated directly on the subkey being revoked.
|
||||||
|
A revoked subkey is not to be used. Only revocation signatures
|
||||||
|
by the top-level signature key that is bound to this subkey, or
|
||||||
|
by an authorized revocation key, should be considered valid
|
||||||
|
revocation signatures.
|
||||||
|
|
||||||
|
Signature directly on a key
|
||||||
|
This signature is calculated directly on a key. It binds the
|
||||||
|
information in the Signature subpackets to the key, and is
|
||||||
|
appropriate to be used for subpackets that provide information
|
||||||
|
about the key, such as the Revocation Key subpacket. It is also
|
||||||
|
appropriate for statements that non-self certifiers want to make
|
||||||
|
about the key itself, rather than the binding between a key and a
|
||||||
|
name.
|
||||||
|
"""
|
||||||
|
_s = subject.hashdata
|
||||||
|
_data += b'\x99' + self.int_to_bytes(len(_s), 2) + _s
|
||||||
|
|
||||||
|
if self.type in {SignatureType.Generic_Cert, SignatureType.Persona_Cert, SignatureType.Casual_Cert,
|
||||||
|
SignatureType.Positive_Cert, SignatureType.CertRevocation}:
|
||||||
"""
|
"""
|
||||||
A certification signature (type 0x10 through 0x13) hashes the User
|
A certification signature (type 0x10 through 0x13) hashes the User
|
||||||
ID being bound to the key into the hash context after the above
|
ID being bound to the key into the hash context after the above
|
||||||
@@ -421,10 +447,12 @@ class PGPSignature(Armorable, ParentRef, PGPObject):
|
|||||||
|
|
||||||
_s = subject.hashdata
|
_s = subject.hashdata
|
||||||
if subject.is_uid:
|
if subject.is_uid:
|
||||||
_data += b'\xb4' + self.int_to_bytes(len(_s), 4) + _s
|
_data += b'\xb4'
|
||||||
|
|
||||||
if subject.is_ua:
|
else:
|
||||||
_data += b'\xd1' + self.int_to_bytes(len(_s), 4) + _s
|
_data += b'\xd1'
|
||||||
|
|
||||||
|
_data += self.int_to_bytes(len(_s), 4) + _s
|
||||||
|
|
||||||
# if this is a new signature, do update_hlen
|
# if this is a new signature, do update_hlen
|
||||||
if 0 in list(self._signature.signature):
|
if 0 in list(self._signature.signature):
|
||||||
@@ -789,12 +817,18 @@ class PGPMessage(Armorable, PGPObject):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.type == 'cleartext':
|
if self.type == 'cleartext':
|
||||||
return "-----BEGIN PGP SIGNED MESSAGE-----\n" \
|
tmpl = u"-----BEGIN PGP SIGNED MESSAGE-----\n" \
|
||||||
"Hash: {hashes:s}\n\n" \
|
u"{hhdr:s}\n" \
|
||||||
"{cleartext:s}\n" \
|
u"{cleartext:s}\n" \
|
||||||
"{signature:s}".format(hashes=','.join(set(s.hash_algorithm.name for s in self.signatures)),
|
u"{signature:s}"
|
||||||
cleartext=self.dash_escape(self.bytes_to_text(self._message)),
|
|
||||||
signature=super(PGPMessage, self).__str__())
|
# only add a Hash: header if we actually have at least one signature
|
||||||
|
hashes = set(s.hash_algorithm.name for s in self.signatures)
|
||||||
|
hhdr = 'Hash: {hashes:s}\n'.format(hashes=','.join(sorted(hashes))) if hashes else ''
|
||||||
|
|
||||||
|
return tmpl.format(hhdr=hhdr,
|
||||||
|
cleartext=self.dash_escape(self.bytes_to_text(self._message)),
|
||||||
|
signature=super(PGPMessage, self).__str__())
|
||||||
|
|
||||||
return super(PGPMessage, self).__str__()
|
return super(PGPMessage, self).__str__()
|
||||||
|
|
||||||
@@ -1207,8 +1241,8 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
|||||||
if self.is_public:
|
if self.is_public:
|
||||||
return self._key.__bytearray__()[len(self._key.header):]
|
return self._key.__bytearray__()[len(self._key.header):]
|
||||||
|
|
||||||
publen = len(self._key) - len(self._key.keymaterial) + self._key.keymaterial.publen()
|
pub = self._key.pubkey()
|
||||||
return self._key.__bytearray__()[len(self._key.header):publen]
|
return pub.__bytearray__()[len(pub.header):]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_expired(self):
|
def is_expired(self):
|
||||||
@@ -1253,6 +1287,15 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
|||||||
"""The :py:obj:`constants.PubKeyAlgorithm` pertaining to this key"""
|
"""The :py:obj:`constants.PubKeyAlgorithm` pertaining to this key"""
|
||||||
return self._key.pkalg
|
return self._key.pkalg
|
||||||
|
|
||||||
|
@property
|
||||||
|
def key_size(self):
|
||||||
|
"""*new in 0.4.1*
|
||||||
|
The size pertaining to this key. ``int`` for non-EC key algorithms; :py:obj:`constants.EllipticCurveOID` for EC keys.
|
||||||
|
"""
|
||||||
|
if self.key_algorithm in {PubKeyAlgorithm.ECDSA, PubKeyAlgorithm.ECDH}:
|
||||||
|
return self._key.keymaterial.oid
|
||||||
|
return next(iter(self._key.keymaterial)).bit_length()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def magic(self):
|
def magic(self):
|
||||||
return '{:s} KEY BLOCK'.format('PUBLIC' if (isinstance(self._key, Public) and not isinstance(self._key, Private)) else
|
return '{:s} KEY BLOCK'.format('PUBLIC' if (isinstance(self._key, Public) and not isinstance(self._key, Private)) else
|
||||||
@@ -1282,7 +1325,8 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
|||||||
|
|
||||||
# copy signatures that weren't copied with uids
|
# copy signatures that weren't copied with uids
|
||||||
for sig in self._signatures:
|
for sig in self._signatures:
|
||||||
pub |= copy.copy(sig)
|
if sig.parent is None:
|
||||||
|
pub |= copy.copy(sig)
|
||||||
|
|
||||||
# keep connect the two halves using a weak reference
|
# keep connect the two halves using a weak reference
|
||||||
self._sibling = weakref.ref(pub)
|
self._sibling = weakref.ref(pub)
|
||||||
@@ -1425,7 +1469,7 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
|||||||
|
|
||||||
raise TypeError
|
raise TypeError
|
||||||
|
|
||||||
def __or__(self, other):
|
def __or__(self, other, from_sib=False):
|
||||||
if isinstance(other, Key) and self._key is None:
|
if isinstance(other, Key) and self._key is None:
|
||||||
self._key = other
|
self._key = other
|
||||||
|
|
||||||
@@ -1451,9 +1495,13 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
|||||||
raise TypeError("unsupported operand type(s) for |: '{:s}' and '{:s}'"
|
raise TypeError("unsupported operand type(s) for |: '{:s}' and '{:s}'"
|
||||||
"".format(self.__class__.__name__, other.__class__.__name__))
|
"".format(self.__class__.__name__, other.__class__.__name__))
|
||||||
|
|
||||||
if isinstance(self._sibling, weakref.ref): # pragma: no cover
|
if isinstance(self._sibling, weakref.ref) and not from_sib:
|
||||||
sib = self._sibling()
|
sib = self._sibling()
|
||||||
sib |= copy.copy(other)
|
if sib is None:
|
||||||
|
self._sibling = None
|
||||||
|
|
||||||
|
else: # pragma: no cover
|
||||||
|
sib.__or__(copy.copy(other), True)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@@ -1461,13 +1509,18 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
|||||||
key = super(PGPKey, self).__copy__()
|
key = super(PGPKey, self).__copy__()
|
||||||
key._key = copy.copy(self._key)
|
key._key = copy.copy(self._key)
|
||||||
|
|
||||||
for id, subkey in self._children.items():
|
|
||||||
key |= copy.copy(subkey)
|
|
||||||
|
|
||||||
for uid in self._uids:
|
for uid in self._uids:
|
||||||
key |= copy.copy(uid)
|
key |= copy.copy(uid)
|
||||||
|
|
||||||
|
for id, subkey in self._children.items():
|
||||||
|
key |= copy.copy(subkey)
|
||||||
|
|
||||||
for sig in self._signatures:
|
for sig in self._signatures:
|
||||||
|
if sig.embedded:
|
||||||
|
# embedded signatures don't need to be explicitly copied
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(len(key._signatures))
|
||||||
key |= copy.copy(sig)
|
key |= copy.copy(sig)
|
||||||
|
|
||||||
return key
|
return key
|
||||||
@@ -1801,10 +1854,10 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
|||||||
:keyword compression: A list of preferred compression algorithms, as :py:obj:`~constants.CompressionAlgorithm`.
|
:keyword compression: A list of preferred compression algorithms, as :py:obj:`~constants.CompressionAlgorithm`.
|
||||||
This keyword is ignored for non-self-certifications.
|
This keyword is ignored for non-self-certifications.
|
||||||
:type compression: ``list``
|
:type compression: ``list``
|
||||||
:keyword key_expires: Specify a key expiration date for when this key should expire, or a
|
:keyword key_expiration: Specify a key expiration date for when this key should expire, or a
|
||||||
:py:obj:`~datetime.timedelta` of how long after the key was created it should expire.
|
:py:obj:`~datetime.timedelta` of how long after the key was created it should expire.
|
||||||
This keyword is ignored for non-self-certifications.
|
This keyword is ignored for non-self-certifications.
|
||||||
:type key_expires: :py:obj:`datetime.datetime`, :py:obj:`datetime.timedelta`
|
:type key_expiration: :py:obj:`datetime.datetime`, :py:obj:`datetime.timedelta`
|
||||||
:keyword keyserver: Specify the URI of the preferred key server of the user.
|
:keyword keyserver: Specify the URI of the preferred key server of the user.
|
||||||
This keyword is ignored for non-self-certifications.
|
This keyword is ignored for non-self-certifications.
|
||||||
:type keyserver: ``str``, ``unicode``, ``bytes``
|
:type keyserver: ``str``, ``unicode``, ``bytes``
|
||||||
@@ -2049,24 +2102,23 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
|||||||
return [ sig for sig in sigs if sig.signer in _ids ]
|
return [ sig for sig in sigs if sig.signer in _ids ]
|
||||||
|
|
||||||
# collect signature(s)
|
# collect signature(s)
|
||||||
if isinstance(signature, PGPSignature):
|
if signature is None:
|
||||||
if signature.signer != self.fingerprint.keyid and signature.signer not in self.subkeys:
|
if isinstance(subject, PGPMessage):
|
||||||
raise PGPError("Incorrect key. Expected: {:s}".format(signature.signer))
|
sspairs += [ (sig, subject.message) for sig in _filter_sigs(subject.signatures) ]
|
||||||
sspairs.append((signature, subject))
|
|
||||||
|
|
||||||
if isinstance(subject, PGPMessage):
|
if isinstance(subject, (PGPUID, PGPKey)):
|
||||||
sspairs += [ (sig, subject.message) for sig in _filter_sigs(subject.signatures) ]
|
sspairs += [ (sig, subject) for sig in _filter_sigs(subject.__sig__) ]
|
||||||
|
|
||||||
if isinstance(subject, (PGPUID, PGPKey)):
|
if isinstance(subject, PGPKey):
|
||||||
sspairs += [ (sig, subject) for sig in _filter_sigs(subject.__sig__) ]
|
# user ids
|
||||||
|
sspairs += [ (sig, uid) for uid in subject.userids for sig in _filter_sigs(uid.__sig__) ]
|
||||||
|
# user attributes
|
||||||
|
sspairs += [ (sig, ua) for ua in subject.userattributes for sig in _filter_sigs(ua.__sig__) ]
|
||||||
|
# subkey binding signatures
|
||||||
|
sspairs += [ (sig, subkey) for subkey in subject.subkeys.values() for sig in _filter_sigs(subkey.__sig__) ]
|
||||||
|
|
||||||
if isinstance(subject, PGPKey):
|
elif signature.signer in {self.fingerprint.keyid} | set(self.subkeys):
|
||||||
# user ids
|
sspairs += [(signature, subject)]
|
||||||
sspairs += [ (sig, uid) for uid in subject.userids for sig in _filter_sigs(uid.__sig__) ]
|
|
||||||
# user attributes
|
|
||||||
sspairs += [ (sig, ua) for ua in subject.userattributes for sig in _filter_sigs(ua.__sig__) ]
|
|
||||||
# subkey binding signatures
|
|
||||||
sspairs += [ (sig, subkey) for subkey in subject.subkeys.values() for sig in _filter_sigs(subkey.__sig__) ]
|
|
||||||
|
|
||||||
if len(sspairs) == 0:
|
if len(sspairs) == 0:
|
||||||
raise PGPError("No signatures to verify")
|
raise PGPError("No signatures to verify")
|
||||||
@@ -2074,7 +2126,7 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
|||||||
# finally, start verifying signatures
|
# finally, start verifying signatures
|
||||||
sigv = SignatureVerification()
|
sigv = SignatureVerification()
|
||||||
for sig, subj in sspairs:
|
for sig, subj in sspairs:
|
||||||
if self.fingerprint.keyid != sig.signer:
|
if self.fingerprint.keyid != sig.signer and sig.signer in self.subkeys:
|
||||||
warnings.warn("Signature was signed with this key's subkey: {:s}. "
|
warnings.warn("Signature was signed with this key's subkey: {:s}. "
|
||||||
"Verifying with subkey...".format(sig.signer),
|
"Verifying with subkey...".format(sig.signer),
|
||||||
stacklevel=2)
|
stacklevel=2)
|
||||||
|
|||||||
@@ -64,10 +64,10 @@ class Armorable(six.with_metaclass(abc.ABCMeta)):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def is_ascii(text):
|
def is_ascii(text):
|
||||||
if isinstance(text, six.string_types):
|
if isinstance(text, six.string_types):
|
||||||
return bool(re.match(r'^[ -~\n]+$', text, flags=re.ASCII))
|
return bool(re.match(r'^[ -~\r\n]+$', text, flags=re.ASCII))
|
||||||
|
|
||||||
if isinstance(text, (bytes, bytearray)):
|
if isinstance(text, (bytes, bytearray)):
|
||||||
return bool(re.match(br'^[ -~\n]+$', text, flags=re.ASCII))
|
return bool(re.match(br'^[ -~\r\n]+$', text, flags=re.ASCII))
|
||||||
|
|
||||||
raise TypeError("Expected: ASCII input of type str, bytes, or bytearray") # pragma: no cover
|
raise TypeError("Expected: ASCII input of type str, bytes, or bytearray") # pragma: no cover
|
||||||
|
|
||||||
@@ -92,25 +92,24 @@ class Armorable(six.with_metaclass(abc.ABCMeta)):
|
|||||||
# the re.VERBOSE flag allows for:
|
# the re.VERBOSE flag allows for:
|
||||||
# - whitespace is ignored except when in a character class or escaped
|
# - whitespace is ignored except when in a character class or escaped
|
||||||
# - anything after a '#' that is not escaped or in a character class is ignored, allowing for comments
|
# - anything after a '#' that is not escaped or in a character class is ignored, allowing for comments
|
||||||
m = re.match(r"""# This capture group is optional because it will only be present in signed cleartext messages
|
m = re.search(r"""# This capture group is optional because it will only be present in signed cleartext messages
|
||||||
(^-{5}BEGIN\ PGP\ SIGNED\ MESSAGE-{5}(?:\r?\n)
|
(^-{5}BEGIN\ PGP\ SIGNED\ MESSAGE-{5}(?:\r?\n)
|
||||||
(Hash:\ (?P<hashes>[A-Za-z0-9\-,]+)(?:\r?\n){2})?
|
(Hash:\ (?P<hashes>[A-Za-z0-9\-,]+)(?:\r?\n){2})?
|
||||||
(?P<cleartext>(.*\n)+)(?:\r?\n)
|
(?P<cleartext>(.*\n)+)(?:\r?\n)
|
||||||
)?
|
)?
|
||||||
# armor header line; capture the variable part of the magic text
|
# armor header line; capture the variable part of the magic text
|
||||||
^-{5}BEGIN\ PGP\ (?P<magic>[A-Z0-9 ,]+)-{5}$(?:\r?\n)
|
^-{5}BEGIN\ PGP\ (?P<magic>[A-Z0-9 ,]+)-{5}(?:\r?\n)
|
||||||
# try to capture all the headers into one capture group
|
# try to capture all the headers into one capture group
|
||||||
# if this doesn't match, m['headers'] will be None
|
# if this doesn't match, m['headers'] will be None
|
||||||
((?P<headers>(^.+:\ .+$(?:\r?\n))+))?(?:\r?\n)?
|
(?P<headers>(^.+:\ .+(?:\r?\n))+)?(?:\r?\n)?
|
||||||
# capture all lines of the body, up to 76 characters long,
|
# capture all lines of the body, up to 76 characters long,
|
||||||
# including the newline, and the pad character(s)
|
# including the newline, and the pad character(s)
|
||||||
(?P<body>([A-Za-z0-9+/]{1,75}={,2}(?:\r?\n))+)
|
(?P<body>([A-Za-z0-9+/]{1,75}={,2}(?:\r?\n))+)
|
||||||
# capture the armored CRC24 value
|
# capture the armored CRC24 value
|
||||||
^=(?P<crc>[A-Za-z0-9+/]{4})$(?:\r?\n)
|
^=(?P<crc>[A-Za-z0-9+/]{4})(?:\r?\n)
|
||||||
# finally, capture the armor tail line, which must match the armor header line
|
# finally, capture the armor tail line, which must match the armor header line
|
||||||
^-{5}END\ PGP\ (?P=magic)-{5}$(?:\r?\n)?
|
^-{5}END\ PGP\ (?P=magic)-{5}(?:\r?\n)?
|
||||||
""",
|
""", text, flags=re.MULTILINE | re.VERBOSE)
|
||||||
text, flags=re.MULTILINE | re.VERBOSE)
|
|
||||||
|
|
||||||
if m is None: # pragma: no cover
|
if m is None: # pragma: no cover
|
||||||
raise ValueError("Expected: ASCII-armored PGP data")
|
raise ValueError("Expected: ASCII-armored PGP data")
|
||||||
@@ -638,15 +637,20 @@ class FlagEnumMeta(EnumMeta):
|
|||||||
return self & other
|
return self & other
|
||||||
|
|
||||||
|
|
||||||
class FlagEnum(six.with_metaclass(FlagEnumMeta, IntEnum)):
|
if six.PY2:
|
||||||
pass
|
class FlagEnum(IntEnum):
|
||||||
|
__metaclass__ = FlagEnumMeta
|
||||||
|
|
||||||
|
else:
|
||||||
|
namespace = FlagEnumMeta.__prepare__('FlagEnum', (IntEnum,))
|
||||||
|
FlagEnum = FlagEnumMeta('FlagEnum', (IntEnum,), namespace)
|
||||||
|
|
||||||
|
|
||||||
class Fingerprint(str):
|
class Fingerprint(str):
|
||||||
"""
|
"""
|
||||||
A subclass of ``str``. Can be compared using == and != to ``str``, ``unicode``, and other :py:obj:`Fingerprint` instances.
|
A subclass of ``str``. Can be compared using == and != to ``str``, ``unicode``, and other :py:obj:`Fingerprint` instances.
|
||||||
|
|
||||||
Primarily used as a key for internal dictionaries, so it ignores spaces when comparing and
|
Primarily used as a key for internal dictionaries, so it ignores spaces when comparing and hashing
|
||||||
"""
|
"""
|
||||||
@property
|
@property
|
||||||
def keyid(self):
|
def keyid(self):
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
-r requirements.txt
|
-r requirements.txt
|
||||||
pytest
|
pytest
|
||||||
pytest-cov
|
pytest-cov
|
||||||
|
pytest-ordering
|
||||||
flake8
|
flake8
|
||||||
pep8-naming
|
pep8-naming
|
||||||
1
setup.py
1
setup.py
@@ -52,6 +52,7 @@ setup(
|
|||||||
'Operating System :: Microsoft :: Windows',
|
'Operating System :: Microsoft :: Windows',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
|
'Programming Language :: Python :: 3.6',
|
||||||
'Programming Language :: Python :: 3.5',
|
'Programming Language :: Python :: 3.5',
|
||||||
'Programming Language :: Python :: 3.4',
|
'Programming Language :: Python :: 3.4',
|
||||||
'Programming Language :: Python :: 3.3',
|
'Programming Language :: Python :: 3.3',
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
|
"""PGPy conftest"""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import functools
|
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import six
|
import select
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
@@ -56,81 +56,74 @@ def _run(bin, *binargs, **pkw):
|
|||||||
|
|
||||||
return cmdo, cmde
|
return cmdo, cmde
|
||||||
|
|
||||||
# now import stuff from fixtures so it can be imported by test modules
|
|
||||||
# from fixtures import TestFiles, gpg_getfingerprint, pgpdump, gpg_verify, gpg_fingerprint
|
|
||||||
|
|
||||||
|
|
||||||
class CWD_As(object):
|
|
||||||
def __init__(self, newwd):
|
|
||||||
if not os.path.exists(newwd):
|
|
||||||
raise FileNotFoundError(newwd + " not found within " + os.getcwd())
|
|
||||||
|
|
||||||
self.oldwd = os.getcwd()
|
|
||||||
self.newwd = newwd
|
|
||||||
|
|
||||||
def __call__(self, func):
|
|
||||||
@functools.wraps(func)
|
|
||||||
def setcwd(*args, **kwargs):
|
|
||||||
# set new working directory
|
|
||||||
os.chdir(self.newwd)
|
|
||||||
|
|
||||||
# fallback value
|
|
||||||
fo = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
fo = func(*args, **kwargs)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
# always return to self.oldwd even if there was a failure
|
|
||||||
os.chdir(self.oldwd)
|
|
||||||
|
|
||||||
return fo
|
|
||||||
|
|
||||||
return setcwd
|
|
||||||
|
|
||||||
|
|
||||||
_gpg_bin = _which('gpg2')
|
_gpg_bin = _which('gpg2')
|
||||||
_gpg_args = ['--options', './pgpy.gpg.conf', '--expert', '--status-fd', '1']
|
_gpg_args = ('--options', './pgpy.gpg.conf', '--expert', '--status-fd')
|
||||||
_gpg_env = os.environ.copy()
|
_gpg_env = {}
|
||||||
_gpg_env['GNUPGHOME'] = os.path.abspath(os.path.abspath('tests/testdata'))
|
_gpg_env['GNUPGHOME'] = os.path.abspath(os.path.abspath('tests/testdata'))
|
||||||
_gpg_kwargs = dict()
|
_gpg_kwargs = dict()
|
||||||
_gpg_kwargs['cwd'] = 'tests/testdata'
|
_gpg_kwargs['cwd'] = 'tests/testdata'
|
||||||
_gpg_kwargs['env'] = _gpg_env
|
_gpg_kwargs['env'] = _gpg_env
|
||||||
_gpg_kwargs['stdout'] = subprocess.PIPE
|
_gpg_kwargs['stdout'] = subprocess.PIPE
|
||||||
_gpg_kwargs['stderr'] = subprocess.STDOUT
|
_gpg_kwargs['stderr'] = subprocess.STDOUT
|
||||||
|
_gpg_kwargs['close_fds'] = False
|
||||||
|
|
||||||
|
|
||||||
|
# GPG boilerplate function
|
||||||
|
def _gpg(*gpg_args, **popen_kwargs):
|
||||||
|
# gpgfd is our "read" end of the pipe
|
||||||
|
# _gpgfd is gpg's "write" end
|
||||||
|
gpgfd, _gpgfd = os.pipe()
|
||||||
|
|
||||||
|
# on python >= 3.4, we need to set _gpgfd as inheritable
|
||||||
|
# older versions do not have this function
|
||||||
|
if sys.version_info >= (3, 4):
|
||||||
|
os.set_inheritable(_gpgfd, True)
|
||||||
|
|
||||||
|
args = (_gpg_bin,) + _gpg_args + (str(_gpgfd),) + gpg_args
|
||||||
|
kwargs = _gpg_kwargs.copy()
|
||||||
|
kwargs.update(popen_kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# use this as the buffer for collecting status-fd output
|
||||||
|
c = bytearray()
|
||||||
|
|
||||||
|
cmd = subprocess.Popen(args, **kwargs)
|
||||||
|
while cmd.poll() is None:
|
||||||
|
while gpgfd in select.select([gpgfd,], [], [], 0)[0]:
|
||||||
|
c += os.read(gpgfd, 1)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# sleep for a bit
|
||||||
|
time.sleep(0.010)
|
||||||
|
|
||||||
|
# finish reading if needed
|
||||||
|
while gpgfd in select.select([gpgfd,], [], [], 0)[0]:
|
||||||
|
c += os.read(gpgfd, 1)
|
||||||
|
|
||||||
|
# collect stdout and stderr
|
||||||
|
o, e = cmd.communicate()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# close the pipes we used for this
|
||||||
|
os.close(gpgfd)
|
||||||
|
os.close(_gpgfd)
|
||||||
|
|
||||||
|
return c.decode('latin-1'), (o or b'').decode('latin-1'), (e or b'').decode('latin-1')
|
||||||
|
|
||||||
|
|
||||||
# fixtures
|
# fixtures
|
||||||
@pytest.fixture()
|
|
||||||
def write_clean():
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def _write_clean(fpath, mode='w', data=''):
|
|
||||||
with open(fpath, mode) as wf:
|
|
||||||
wf.write(data)
|
|
||||||
wf.flush()
|
|
||||||
|
|
||||||
try:
|
|
||||||
yield
|
|
||||||
|
|
||||||
finally:
|
|
||||||
os.remove(fpath)
|
|
||||||
|
|
||||||
return _write_clean
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def gpg_import():
|
def gpg_import():
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _gpg_import(*keypaths):
|
def _gpg_import(*keypaths):
|
||||||
gpg_args = _gpg_args + ['--import', ] + list(keypaths)
|
|
||||||
gpg_kwargs = _gpg_kwargs.copy()
|
|
||||||
gpgo, _ = _run(_gpg_bin, *gpg_args, **gpg_kwargs)
|
|
||||||
|
|
||||||
# if GPG version is 2.1 or newer, we need to add a setup/teardown step in creating the keybox folder
|
# if GPG version is 2.1 or newer, we need to add a setup/teardown step in creating the keybox folder
|
||||||
if gpg_ver >= '2.1':
|
if gpg_ver >= '2.1':
|
||||||
if not os.path.exists('tests/testdata/private-keys-v1.d'):
|
if not os.path.exists('tests/testdata/private-keys-v1.d'):
|
||||||
os.mkdir('tests/testdata/private-keys-v1.d')
|
os.mkdir('tests/testdata/private-keys-v1.d')
|
||||||
time.sleep(5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
gpgc, gpgo, gpge = _gpg('--batch', '--import', *list(keypaths))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield gpgo
|
yield gpgo
|
||||||
@@ -139,7 +132,6 @@ def gpg_import():
|
|||||||
[os.remove(f) for f in glob.glob('tests/testdata/testkeys.*')]
|
[os.remove(f) for f in glob.glob('tests/testdata/testkeys.*')]
|
||||||
if gpg_ver >= '2.1':
|
if gpg_ver >= '2.1':
|
||||||
[os.remove(f) for f in glob.glob('tests/testdata/private-keys-v1.d/*')]
|
[os.remove(f) for f in glob.glob('tests/testdata/private-keys-v1.d/*')]
|
||||||
# os.rmdir('tests/testdata/private-keys-v1.d')
|
|
||||||
|
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
@@ -149,9 +141,7 @@ def gpg_import():
|
|||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def gpg_check_sigs():
|
def gpg_check_sigs():
|
||||||
def _gpg_check_sigs(*keyids):
|
def _gpg_check_sigs(*keyids):
|
||||||
gpg_args = _gpg_args + ['--check-sigs'] + list(keyids)
|
gpgc, gpgo, gpge = _gpg('--check-sigs', *keyids)
|
||||||
gpg_kwargs = _gpg_kwargs.copy()
|
|
||||||
gpgo, _ = _run(_gpg_bin, *gpg_args, **gpg_kwargs)
|
|
||||||
return 'sig-' not in gpgo
|
return 'sig-' not in gpgo
|
||||||
|
|
||||||
return _gpg_check_sigs
|
return _gpg_check_sigs
|
||||||
@@ -163,11 +153,11 @@ def gpg_verify():
|
|||||||
r'^\[GNUPG:\] VALIDSIG (?:[0-9A-F]{,24})\1', flags=re.MULTILINE | re.DOTALL)
|
r'^\[GNUPG:\] VALIDSIG (?:[0-9A-F]{,24})\1', flags=re.MULTILINE | re.DOTALL)
|
||||||
|
|
||||||
def _gpg_verify(gpg_subjpath, gpg_sigpath=None, keyid=None):
|
def _gpg_verify(gpg_subjpath, gpg_sigpath=None, keyid=None):
|
||||||
gpg_args = _gpg_args + [ a for a in ['--verify', gpg_sigpath, gpg_subjpath] if a is not None ]
|
rargs = [gpg_sigpath, gpg_subjpath] if gpg_sigpath is not None else [gpg_subjpath,]
|
||||||
gpg_kwargs = _gpg_kwargs.copy()
|
|
||||||
gpgo, _ = _run(_gpg_bin, *gpg_args, **gpg_kwargs)
|
|
||||||
|
|
||||||
sigs = [ sv.group('keyid') for sv in sfd_verify.finditer(gpgo) ]
|
gpgc, gpgo, gpge = _gpg('--verify', *rargs)
|
||||||
|
|
||||||
|
sigs = [ sv.group('keyid') for sv in sfd_verify.finditer(gpgc) ]
|
||||||
|
|
||||||
if keyid is not None:
|
if keyid is not None:
|
||||||
return keyid in sigs
|
return keyid in sigs
|
||||||
@@ -181,53 +171,56 @@ def gpg_verify():
|
|||||||
def gpg_decrypt():
|
def gpg_decrypt():
|
||||||
sfd_decrypt = re.compile(r'^\[GNUPG:\] BEGIN_DECRYPTION\n'
|
sfd_decrypt = re.compile(r'^\[GNUPG:\] BEGIN_DECRYPTION\n'
|
||||||
r'^\[GNUPG:\] DECRYPTION_INFO \d+ \d+\n'
|
r'^\[GNUPG:\] DECRYPTION_INFO \d+ \d+\n'
|
||||||
r'^\[GNUPG:\] PLAINTEXT \d+ \S+ \n'
|
r'^\[GNUPG:\] PLAINTEXT (?:62|74|75) (?P<tstamp>\d+) (?P<fname>.*)\n'
|
||||||
r'^\[GNUPG:\] PLAINTEXT_LENGTH \d+\n'
|
r'^\[GNUPG:\] PLAINTEXT_LENGTH \d+\n'
|
||||||
r'(?P<text>(?:.|\n)*)'
|
|
||||||
r'\[GNUPG:\] DECRYPTION_OKAY\n'
|
r'\[GNUPG:\] DECRYPTION_OKAY\n'
|
||||||
r'^\[GNUPG:\] GOODMDC\n'
|
r'(?:^\[GNUPG:\] GOODMDC\n)?'
|
||||||
r'^\[GNUPG:\] END_DECRYPTION', flags=re.MULTILINE)
|
r'^\[GNUPG:\] END_DECRYPTION', flags=re.MULTILINE)
|
||||||
|
|
||||||
def _gpg_decrypt(encmsgpath, passphrase=None, keyid=None):
|
def _gpg_decrypt(encmsgpath, passphrase=None, keyid=None):
|
||||||
gpg_args = [_gpg_bin] + _gpg_args[:]
|
a = []
|
||||||
gpg_kwargs = _gpg_kwargs.copy()
|
|
||||||
gpg_kwargs['stderr'] = subprocess.PIPE
|
|
||||||
_comargs = ()
|
|
||||||
|
|
||||||
if passphrase is not None:
|
if passphrase is not None:
|
||||||
gpg_args += ['--batch', '--passphrase-fd', '0']
|
# create a pipe to send the passphrase to GnuPG through
|
||||||
gpg_kwargs['stdin'] = subprocess.PIPE
|
pfdr, pfdw = os.pipe()
|
||||||
_comargs = (passphrase.encode(),)
|
|
||||||
|
|
||||||
if keyid is not None:
|
# write the passphrase to the pipe buffer right away
|
||||||
gpg_args += ['--recipient', keyid]
|
os.write(pfdw, passphrase.encode())
|
||||||
|
os.write(pfdw, b'\n')
|
||||||
|
|
||||||
gpg_args += ['--decrypt', encmsgpath]
|
# on python >= 3.4, we need to set pfdr as inheritable
|
||||||
|
# older versions do not have this function
|
||||||
|
if sys.version_info >= (3, 4):
|
||||||
|
os.set_inheritable(pfdr, True)
|
||||||
|
|
||||||
gpgdec = subprocess.Popen(gpg_args, **gpg_kwargs)
|
a.extend(['--batch', '--passphrase-fd', str(pfdr)])
|
||||||
gpgo, gpge = gpgdec.communicate(*_comargs)
|
|
||||||
gpgdec.wait()
|
|
||||||
|
|
||||||
return sfd_decrypt.search(gpgo.decode()).group('text')
|
elif keyid is not None:
|
||||||
|
a.extend(['--recipient', keyid])
|
||||||
|
|
||||||
# return gpgo.decode() if gpgo is not None else gpge
|
a.extend(['--decrypt', encmsgpath])
|
||||||
|
|
||||||
|
gpgc, gpgo, gpge = _gpg(*a, stderr=subprocess.PIPE)
|
||||||
|
|
||||||
|
status = sfd_decrypt.match(gpgc)
|
||||||
|
return gpgo
|
||||||
|
|
||||||
return _gpg_decrypt
|
return _gpg_decrypt
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def gpg_print():
|
def gpg_print():
|
||||||
sfd_text = re.compile(r'^\[GNUPG:\] PLAINTEXT (?:62|74|75) .*\n'
|
sfd_text = re.compile(r'^\[GNUPG:\] PLAINTEXT (?:62|74|75) (?P<tstamp>\d+) (?P<fname>.*)\n'
|
||||||
r'^\[GNUPG:\] PLAINTEXT_LENGTH (?P<len>\d+)\n'
|
r'^\[GNUPG:\] PLAINTEXT_LENGTH (?P<len>\d+)\n', re.MULTILINE)
|
||||||
r'^(?P<text>(.|\n)*)', re.MULTILINE)
|
|
||||||
|
gpg_text = re.compile(r'(?:- gpg control packet\n)?(?P<text>.*)', re.MULTILINE | re.DOTALL)
|
||||||
|
|
||||||
def _gpg_print(infile):
|
def _gpg_print(infile):
|
||||||
gpg_args = _gpg_args + ['-o-', infile]
|
gpgc, gpgo, gpge = _gpg('-o-', infile, stderr=subprocess.PIPE)
|
||||||
gpg_kwargs = _gpg_kwargs.copy()
|
status = sfd_text.match(gpgc)
|
||||||
gpg_kwargs['stderr'] = subprocess.PIPE
|
tlen = len(gpgo) if status is None else int(status.group('len'))
|
||||||
|
|
||||||
gpgo, _ = _run(_gpg_bin, *gpg_args, **gpg_kwargs)
|
return gpg_text.match(gpgo).group('text')[:tlen]
|
||||||
return sfd_text.match(gpgo).group('text')
|
|
||||||
|
|
||||||
return _gpg_print
|
return _gpg_print
|
||||||
|
|
||||||
@@ -235,12 +228,8 @@ def gpg_print():
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def gpg_keyid_file():
|
def gpg_keyid_file():
|
||||||
def _gpg_keyid_file(infile):
|
def _gpg_keyid_file(infile):
|
||||||
gpg_args = _gpg_args + ['--list-packets', infile]
|
c, o, e = _gpg('--list-packets', infile)
|
||||||
gpg_kwargs = _gpg_kwargs.copy()
|
return re.findall(r'^\s+keyid: ([0-9A-F]+)', o, flags=re.MULTILINE)
|
||||||
|
|
||||||
gpgo, _ = _run(_gpg_bin, *gpg_args, **gpg_kwargs)
|
|
||||||
return re.findall(r'^\s+keyid: ([0-9A-F]+)', gpgo, flags=re.MULTILINE)
|
|
||||||
|
|
||||||
return _gpg_keyid_file
|
return _gpg_keyid_file
|
||||||
|
|
||||||
|
|
||||||
@@ -257,6 +246,8 @@ def pgpdump():
|
|||||||
# pytest_configure
|
# pytest_configure
|
||||||
# called after command line options have been parsed and all plugins and initial conftest files been loaded.
|
# called after command line options have been parsed and all plugins and initial conftest files been loaded.
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
|
print("== PGPy Test Suite ==")
|
||||||
|
|
||||||
# ensure commands we need exist
|
# ensure commands we need exist
|
||||||
for cmd in ['gpg2', 'pgpdump']:
|
for cmd in ['gpg2', 'pgpdump']:
|
||||||
if _which(cmd) is None:
|
if _which(cmd) is None:
|
||||||
@@ -270,25 +261,9 @@ def pytest_configure(config):
|
|||||||
v, _ = _run(_which('pgpdump'), '-v', stderr=subprocess.STDOUT)
|
v, _ = _run(_which('pgpdump'), '-v', stderr=subprocess.STDOUT)
|
||||||
pgpdump_ver.parse(v.split(' ')[2].strip(','))
|
pgpdump_ver.parse(v.split(' ')[2].strip(','))
|
||||||
|
|
||||||
# display the working directory and the OpenSSL version
|
# display the working directory and the OpenSSL/GPG/pgpdump versions
|
||||||
print("Working Directory: " + os.getcwd())
|
print("Working Directory: " + os.getcwd())
|
||||||
print("Using OpenSSL " + str(openssl_ver))
|
print("Using OpenSSL " + str(openssl_ver))
|
||||||
print("Using GnuPG " + str(gpg_ver))
|
print("Using GnuPG " + str(gpg_ver))
|
||||||
print("Using pgpdump " + str(pgpdump_ver))
|
print("Using pgpdump " + str(pgpdump_ver))
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
|
|
||||||
# pytest_generate_tests
|
|
||||||
# called when each test method is collected to generate parametrizations
|
|
||||||
def pytest_generate_tests(metafunc):
|
|
||||||
if metafunc.cls is not None and hasattr(metafunc.cls, 'params'):
|
|
||||||
funcargs = [ (k, v) for k, v in metafunc.cls.params.items() if k in metafunc.fixturenames ]
|
|
||||||
|
|
||||||
args = [','.join(k for k, _ in funcargs),
|
|
||||||
list(zip(*[v for _, v in funcargs])) if len(funcargs) > 1 else [vi for _, v in funcargs for vi in v]]
|
|
||||||
kwargs = {}
|
|
||||||
|
|
||||||
if hasattr(metafunc.cls, 'ids') and metafunc.function.__name__ in metafunc.cls.ids:
|
|
||||||
kwargs['ids'] = metafunc.cls.ids[metafunc.function.__name__]
|
|
||||||
|
|
||||||
metafunc.parametrize(*args, **kwargs)
|
|
||||||
|
|||||||
@@ -19,43 +19,24 @@ modules = ['pgpy.constants',
|
|||||||
'pgpy.packet.subpackets.types',
|
'pgpy.packet.subpackets.types',
|
||||||
'pgpy.packet.subpackets.userattribute']
|
'pgpy.packet.subpackets.userattribute']
|
||||||
|
|
||||||
|
|
||||||
def get_module_objs(module):
|
def get_module_objs(module):
|
||||||
# return a set of strings that represent the names of objects defined in that module
|
# return a set of strings that represent the names of objects defined in that module
|
||||||
return { n for n, o in inspect.getmembers(module) if (inspect.getmodule(o) is module) }
|
return { n for n, o in inspect.getmembers(module, lambda m: inspect.getmodule(m) is module) } | ({'FlagEnum',} if module is importlib.import_module('pgpy.types') else set()) # dirty workaround until six fixes metaclass stuff to support EnumMeta in Python >= 3.6
|
||||||
|
|
||||||
|
|
||||||
|
def get_module_all(module):
|
||||||
|
return set(getattr(module, '__all__', set()))
|
||||||
|
|
||||||
|
|
||||||
def test_pgpy_all():
|
def test_pgpy_all():
|
||||||
import pgpy
|
import pgpy
|
||||||
# just check that everything in pgpy.__all__ is actually there
|
# just check that everything in pgpy.__all__ is actually there
|
||||||
assert set(pgpy.__all__) <= { n for n, _ in inspect.getmembers(pgpy) }
|
assert set(pgpy.__all__) <= { n for n, _ in inspect.getmembers(pgpy) }
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('modname', modules)
|
@pytest.mark.parametrize('modname', modules)
|
||||||
def test_exports(modname):
|
def test_exports(modname):
|
||||||
module = importlib.import_module(modname)
|
module = importlib.import_module(modname)
|
||||||
|
|
||||||
modall = getattr(module, '__all__', None)
|
assert get_module_all(module) == get_module_objs(module)
|
||||||
if modall is None:
|
|
||||||
pytest.skip('__all__ not defined')
|
|
||||||
|
|
||||||
assert set(modall) == get_module_objs(module)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# check_modules = [pgpy.constants,
|
|
||||||
# pgpy.decorators,
|
|
||||||
# pgpy.errors,
|
|
||||||
# pgpy.pgp,
|
|
||||||
# pgpy.symenc,
|
|
||||||
# pgpy.types,
|
|
||||||
#
|
|
||||||
# pgpy.packet.fields,
|
|
||||||
# pgpy.packet.packets,
|
|
||||||
# pgpy.packet.types,
|
|
||||||
#
|
|
||||||
# pgpy.packet.subpackets.signature,
|
|
||||||
# pgpy.packet.subpackets.types,
|
|
||||||
# pgpy.packet.subpackets.userattribute,]
|
|
||||||
#
|
|
||||||
# for module in check_modules:
|
|
||||||
# if getattr(module, '__all__', None) is not None:
|
|
||||||
# assert set(module.__all__) == get_module_objs(module)
|
|
||||||
|
|||||||
@@ -1,52 +1,69 @@
|
|||||||
""" test field parsing
|
""" test field parsing
|
||||||
"""
|
"""
|
||||||
from itertools import product
|
import pytest
|
||||||
|
|
||||||
|
import itertools
|
||||||
|
|
||||||
from pgpy.constants import HashAlgorithm
|
from pgpy.constants import HashAlgorithm
|
||||||
from pgpy.constants import String2KeyType
|
from pgpy.constants import String2KeyType
|
||||||
from pgpy.constants import SymmetricKeyAlgorithm
|
from pgpy.constants import SymmetricKeyAlgorithm
|
||||||
|
|
||||||
from pgpy.packet.types import Header
|
|
||||||
from pgpy.packet.fields import String2Key
|
from pgpy.packet.fields import String2Key
|
||||||
|
from pgpy.packet.types import Header
|
||||||
from pgpy.packet.subpackets import Signature
|
from pgpy.packet.subpackets import Signature
|
||||||
from pgpy.packet.subpackets import UserAttribute
|
from pgpy.packet.subpackets import UserAttribute
|
||||||
|
|
||||||
from pgpy.packet.subpackets.types import Header as HeaderSP
|
from pgpy.packet.subpackets.types import Header as HeaderSP
|
||||||
from pgpy.packet.subpackets.types import Opaque as OpaqueSP
|
from pgpy.packet.subpackets.types import Opaque as OpaqueSP
|
||||||
|
|
||||||
|
|
||||||
|
_trailer = b'\xde\xca\xff\xba\xdd'
|
||||||
|
_tag = bytearray(b'\xc2')
|
||||||
|
pkt_headers = [
|
||||||
|
# new format
|
||||||
|
# 1 byte length - 191
|
||||||
|
_tag + b'\xbf' + (b'\x00' * 191) + _trailer,
|
||||||
|
# 2 byte length - 192
|
||||||
|
_tag + b'\xc0\x00' + (b'\x00' * 192) + _trailer,
|
||||||
|
# 2 byte length - 8383
|
||||||
|
_tag + b'\xdf\xff' + (b'\x00' * 8383) + _trailer,
|
||||||
|
# 5 byte length - 8384
|
||||||
|
_tag + b'\xff\x00\x00 \xc0' + (b'\x00' * 8384) + _trailer,
|
||||||
|
# old format
|
||||||
|
# 1 byte length - 255
|
||||||
|
bytearray(b'\x88') + b'\xff' + (b'\x00' * 255) + _trailer,
|
||||||
|
# 2 byte length - 256
|
||||||
|
bytearray(b'\x89') + b'\x01\x00' + (b'\x00' * 256) + _trailer,
|
||||||
|
# 4 byte length - 65536
|
||||||
|
bytearray(b'\x8a') + b'\x00\x01\x00\x00' + (b'\x00' * 65536) + _trailer,
|
||||||
|
]
|
||||||
|
|
||||||
|
subpkt_headers = [
|
||||||
|
# 1 byte length - 191
|
||||||
|
bytearray(b'\xbf' + b'\x00' + (b'\x00' * 190)),
|
||||||
|
# 2 byte length - 192
|
||||||
|
bytearray(b'\xc0\x00' + b'\x00' + (b'\x00' * 191)),
|
||||||
|
# 2 byte length - 8383
|
||||||
|
bytearray(b'\xdf\xff' + b'\x00' + (b'\x00' * 8382)),
|
||||||
|
# 5 byte length - 8384
|
||||||
|
bytearray(b'\xff\x00\x00 \xc0' + b'\x00' + (b'\x00' * 0x8383)),
|
||||||
|
# 5 byte length - 65535
|
||||||
|
bytearray(b'\xff\x00\x00\xff\xff' + b'\x00' + (b'\x00' * 65534)),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class TestHeaders(object):
|
class TestHeaders(object):
|
||||||
params = {
|
@pytest.mark.parametrize('pheader', pkt_headers)
|
||||||
'pheader': [# new format
|
def test_packet_header(self, pheader):
|
||||||
# 1 byte length - 191
|
b = pheader[:]
|
||||||
bytearray(b'\xc2' + b'\xbf' + (b'\x00' * 191) + b'\xca\xfe\xba\xbe'),
|
h = Header()
|
||||||
# 2 byte length - 192
|
h.parse(pheader)
|
||||||
bytearray(b'\xc2' + b'\xc0\x00' + (b'\x00' * 192) + b'\xca\xfe\xba\xbe'),
|
|
||||||
# 2 byte length - 8383
|
assert h.tag == 0x02
|
||||||
bytearray(b'\xc2' + b'\xdf\xff' + (b'\x00' * 8383) + b'\xca\xfe\xba\xbe'),
|
assert h.length == len(pheader) - len(_trailer)
|
||||||
# 5 byte length - 8384
|
assert pheader[h.length:] == _trailer
|
||||||
bytearray(b'\xc2' + b'\xff\x00\x00 \xc0' + (b'\x00' * 8384) + b'\xca\xfe\xba\xbe'),
|
assert len(h) == len(b) - len(pheader)
|
||||||
# old format
|
assert h.__bytes__() == b[:len(h)]
|
||||||
# 1 byte length - 255
|
|
||||||
bytearray(b'\x88' + b'\xff' + (b'\x00' * 255) + b'\xca\xfe\xba\xbe'),
|
@pytest.mark.parametrize('spheader', subpkt_headers)
|
||||||
# 2 byte length - 256
|
|
||||||
bytearray(b'\x89' + b'\x01\x00' + (b'\x00' * 256) + b'\xca\xfe\xba\xbe'),
|
|
||||||
# 4 byte length - 65536
|
|
||||||
bytearray(b'\x8a' + b'\x00\x01\x00\x00' + (b'\x00' * 65536) + b'\xca\xfe\xba\xbe'),
|
|
||||||
],
|
|
||||||
'spheader': [# 1 byte length - 191
|
|
||||||
bytearray(b'\xbf' + b'\x00' + (b'\x00' * 190)),
|
|
||||||
# 2 byte length - 192
|
|
||||||
bytearray(b'\xc0\x00' + b'\x00' + (b'\x00' * 191)),
|
|
||||||
# 2 byte length - 8383
|
|
||||||
bytearray(b'\xdf\xff' + b'\x00' + (b'\x00' * 8382)),
|
|
||||||
# 5 byte length - 8384
|
|
||||||
bytearray(b'\xff\x00\x00 \xc0' + b'\x00' + (b'\x00' * 0x8383)),
|
|
||||||
# 5 byte length - 65535
|
|
||||||
bytearray(b'\xff\x00\x00\xff\xff' + b'\x00' + (b'\x00' * 65534)),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
def test_subpacket_header(self, spheader):
|
def test_subpacket_header(self, spheader):
|
||||||
h = HeaderSP()
|
h = HeaderSP()
|
||||||
h.parse(spheader)
|
h.parse(spheader)
|
||||||
@@ -54,16 +71,6 @@ class TestHeaders(object):
|
|||||||
assert 65537 > h.length > 1
|
assert 65537 > h.length > 1
|
||||||
assert len(h) == len(h.__bytes__())
|
assert len(h) == len(h.__bytes__())
|
||||||
|
|
||||||
def test_packet_header(self, pheader):
|
|
||||||
b = pheader[:]
|
|
||||||
h = Header()
|
|
||||||
h.parse(pheader)
|
|
||||||
|
|
||||||
assert h.tag == 0x02
|
|
||||||
assert h.length == len(pheader) - 4
|
|
||||||
assert pheader[h.length:] == b'\xca\xfe\xba\xbe'
|
|
||||||
assert len(h) == len(b) - len(pheader)
|
|
||||||
assert h.__bytes__() == b[:len(h)]
|
|
||||||
|
|
||||||
_sspclasses = {
|
_sspclasses = {
|
||||||
# 0x00: 'Opaque',
|
# 0x00: 'Opaque',
|
||||||
@@ -112,155 +119,158 @@ _sspclasses = {
|
|||||||
0x6d: 'Opaque',
|
0x6d: 'Opaque',
|
||||||
0x6e: 'Opaque',
|
0x6e: 'Opaque',
|
||||||
}
|
}
|
||||||
|
|
||||||
_uaspclasses = {
|
_uaspclasses = {
|
||||||
0x01: 'Image'
|
0x01: 'Image'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ssps = [
|
||||||
|
# 0x02 - creation time
|
||||||
|
b'\x05\x02?z\xf7\x13',
|
||||||
|
# 0x03 - expiration time
|
||||||
|
b'\x05\x03\x00\x12u\x00',
|
||||||
|
# 0x04 - exportable certification
|
||||||
|
b'\x02\x04\x00',
|
||||||
|
# 0x05
|
||||||
|
b'\x03\x05\x01x',
|
||||||
|
# 0x06
|
||||||
|
b'\x1d\x06<[^>]+[@.]liebenzell\\.org>$\x00',
|
||||||
|
# 0x07
|
||||||
|
b'\x02\x07\x00',
|
||||||
|
# 0x08
|
||||||
|
# 0x09
|
||||||
|
b'\x05\t\x01\xe13\x80',
|
||||||
|
# 0x0a
|
||||||
|
b'\x17\n\x00\x11M,\x9e,\xee~&\rK\xbd\x9b[\x1b`\xbcu\x0c\xefW\x06',
|
||||||
|
# 0x0b
|
||||||
|
b'\x05\x0b\x07\n\x03\x04',
|
||||||
|
# 0x0c
|
||||||
|
b'\x17\x0c\x80\x119\x06\xf8\xf6\x98d\x9e\xbePG\xd0\xba\x11\xed\xa7\xd0!<\xa1\x1b',
|
||||||
|
# 0x10
|
||||||
|
b"\t\x10\n'Z\xb6\xb4\xbc\xa5\xd7",
|
||||||
|
# 0x12
|
||||||
|
b'\x05\x12R/\xe2d',
|
||||||
|
# 0x14
|
||||||
|
b'\x87\x14\x80\x00\x00\x00\x00\x10\x00nsignotes@grep.be"http://www.grep.be/gpg/CF62318D5BBE'
|
||||||
|
b'D48F33ACD5431B0006256FB29164/0138DA92EDFFB27DD270F86DB475E207BAB58229.asc"',
|
||||||
|
# 0x15
|
||||||
|
b'\x03\x15\x03\x02',
|
||||||
|
# 0x16
|
||||||
|
b'\x03\x16\x02\x01',
|
||||||
|
# 0x17
|
||||||
|
b'\x02\x17\x80',
|
||||||
|
# 0x18
|
||||||
|
b'\x19\x18hkp://fakekey.server.tld',
|
||||||
|
# 0x19
|
||||||
|
b'\x02\x19\x01',
|
||||||
|
# 0x1a
|
||||||
|
b'\x15\x1ahttp://www.blaap.org',
|
||||||
|
# 0x1b
|
||||||
|
b'\x02\x1b#',
|
||||||
|
# 0x1c
|
||||||
|
b' \x1cSander Temme <sander@temme.net>',
|
||||||
|
# 0x1d
|
||||||
|
b'\x02\x1d\x00',
|
||||||
|
# 0x1e
|
||||||
|
b'\x02\x1e\x01',
|
||||||
|
# 0x20
|
||||||
|
b"\xc0] \x04\x19\x01\n\x00\x06\x05\x02S\x9a6\x06\x00\n\t\x10\x19q\xf7\xb8\x80g\xdd\x07\xd20"
|
||||||
|
b"\x07\xfd\x19\xbb\xea;6|\xdb1\xf3\xbc\xfbZ\x1d\xb6\xcfY\xe6&\xe9\xed\xf1O\xdc\x84\xdd\xe1"
|
||||||
|
b"\x88\xff\xb9\xba\x1a\xe9\x8d\x16K\xd2\xb4\xf49\x7f(\xc9\xe8/\xf6\x87\x0f\xef\xb7*\xf9'r{E"
|
||||||
|
b"\xf3\x07?\xcb\xffm\x87\x86&H\xee\xc4\xbc\xf1L\x177\x92\xdb\xf9I\x16Q\xf6\x9ei\xf56z\x0f\xff"
|
||||||
|
b"^\x92\x88Kh\xbd; \x86\xa5\xbaL\xa2\xda\x93\xae\x10\xd1Y\xa5\xa7\xb4)*\xf6\xa1,]\xd1\xe3\\"
|
||||||
|
b"\xc3l3\xecA\xec&\x145\xe1\xc4\xd0\x15y\xb2\xf8\x0c\x0e\xd3_[\x1f\x0fM\x98\xa8J\xb3\xd9?\xa4"
|
||||||
|
b"\xb3\x16\xee8\xad/\x07\xea\x7f\xad\x1a\x0f\xbe\x06\x94\xa51\xf6@\xae\xcdy\x92B\x1c\xd5\x04z"
|
||||||
|
b"\xbf\xe9\xbc\x9c\xac\x99W6\x81\xad\xe0\x81\xb4\x89n\xd0_\x1c\x92\xbe\xf6\x1cmn\xe92_\x86\xcf"
|
||||||
|
b"\xb0v\x1f\x9dk%\xbd<\x0c\x1e\x91\x0c\xec\\\xdc\x8cCu\xd8N\xf2\x82E\x00\xc8rnSY\x1b\xa0%\x13"
|
||||||
|
b"\xc0$Q+\xd3\xd0\xd8 \x0c\xe9\xafI5&\xe5\xc1!\xaf",
|
||||||
|
# 0x65
|
||||||
|
b'\x07eGPG\x00\x01\x01',
|
||||||
|
]
|
||||||
|
|
||||||
|
sig_subpkts = [bytearray(sp) + _trailer for sp in _ssps]
|
||||||
|
|
||||||
|
|
||||||
class TestSignatureSubPackets(object):
|
class TestSignatureSubPackets(object):
|
||||||
params = {
|
@pytest.mark.parametrize('sigsubpacket', sig_subpkts)
|
||||||
'sigsubpacket': [ bytearray(sp) + b'\xca\xfe\xba\xbe' for sp in
|
|
||||||
[ # 0x02 - creation time
|
|
||||||
b'\x05\x02?z\xf7\x13',
|
|
||||||
# 0x03 - expiration time
|
|
||||||
b'\x05\x03\x00\x12u\x00',
|
|
||||||
# 0x04 - exportable certification
|
|
||||||
b'\x02\x04\x00',
|
|
||||||
# 0x05
|
|
||||||
b'\x03\x05\x01x',
|
|
||||||
# 0x06
|
|
||||||
b'\x1d\x06<[^>]+[@.]liebenzell\\.org>$\x00',
|
|
||||||
# 0x07
|
|
||||||
b'\x02\x07\x00',
|
|
||||||
# 0x08
|
|
||||||
# 0x09
|
|
||||||
b'\x05\t\x01\xe13\x80',
|
|
||||||
# 0x0a
|
|
||||||
b'\x17\n\x00\x11M,\x9e,\xee~&\rK\xbd\x9b[\x1b`\xbcu\x0c\xefW\x06',
|
|
||||||
# 0x0b
|
|
||||||
b'\x05\x0b\x07\n\x03\x04',
|
|
||||||
# 0x0c
|
|
||||||
b'\x17\x0c\x80\x119\x06\xf8\xf6\x98d\x9e\xbePG\xd0\xba\x11\xed\xa7\xd0!<\xa1\x1b',
|
|
||||||
# 0x10
|
|
||||||
b"\t\x10\n'Z\xb6\xb4\xbc\xa5\xd7",
|
|
||||||
# 0x12
|
|
||||||
b'\x05\x12R/\xe2d',
|
|
||||||
# 0x14
|
|
||||||
b'\x87\x14\x80\x00\x00\x00\x00\x10\x00nsignotes@grep.be"http://www.grep.be/gpg/CF62318D5BBE'
|
|
||||||
b'D48F33ACD5431B0006256FB29164/0138DA92EDFFB27DD270F86DB475E207BAB58229.asc"',
|
|
||||||
# 0x15
|
|
||||||
b'\x03\x15\x03\x02',
|
|
||||||
# 0x16
|
|
||||||
b'\x03\x16\x02\x01',
|
|
||||||
# 0x17
|
|
||||||
b'\x02\x17\x80',
|
|
||||||
# 0x18
|
|
||||||
b'\x19\x18hkp://fakekey.server.tld',
|
|
||||||
# 0x19
|
|
||||||
b'\x02\x19\x01',
|
|
||||||
# 0x1a
|
|
||||||
b'\x15\x1ahttp://www.blaap.org',
|
|
||||||
# 0x1b
|
|
||||||
b'\x02\x1b#',
|
|
||||||
# 0x1c
|
|
||||||
b' \x1cSander Temme <sander@temme.net>',
|
|
||||||
# 0x1d
|
|
||||||
b'\x02\x1d\x00',
|
|
||||||
# 0x1e
|
|
||||||
b'\x02\x1e\x01',
|
|
||||||
# 0x20
|
|
||||||
b"\xc0] \x04\x19\x01\n\x00\x06\x05\x02S\x9a6\x06\x00\n\t\x10\x19q\xf7\xb8\x80g\xdd\x07\xd20"
|
|
||||||
b"\x07\xfd\x19\xbb\xea;6|\xdb1\xf3\xbc\xfbZ\x1d\xb6\xcfY\xe6&\xe9\xed\xf1O\xdc\x84\xdd\xe1"
|
|
||||||
b"\x88\xff\xb9\xba\x1a\xe9\x8d\x16K\xd2\xb4\xf49\x7f(\xc9\xe8/\xf6\x87\x0f\xef\xb7*\xf9'r{E"
|
|
||||||
b"\xf3\x07?\xcb\xffm\x87\x86&H\xee\xc4\xbc\xf1L\x177\x92\xdb\xf9I\x16Q\xf6\x9ei\xf56z\x0f\xff"
|
|
||||||
b"^\x92\x88Kh\xbd; \x86\xa5\xbaL\xa2\xda\x93\xae\x10\xd1Y\xa5\xa7\xb4)*\xf6\xa1,]\xd1\xe3\\"
|
|
||||||
b"\xc3l3\xecA\xec&\x145\xe1\xc4\xd0\x15y\xb2\xf8\x0c\x0e\xd3_[\x1f\x0fM\x98\xa8J\xb3\xd9?\xa4"
|
|
||||||
b"\xb3\x16\xee8\xad/\x07\xea\x7f\xad\x1a\x0f\xbe\x06\x94\xa51\xf6@\xae\xcdy\x92B\x1c\xd5\x04z"
|
|
||||||
b"\xbf\xe9\xbc\x9c\xac\x99W6\x81\xad\xe0\x81\xb4\x89n\xd0_\x1c\x92\xbe\xf6\x1cmn\xe92_\x86\xcf"
|
|
||||||
b"\xb0v\x1f\x9dk%\xbd<\x0c\x1e\x91\x0c\xec\\\xdc\x8cCu\xd8N\xf2\x82E\x00\xc8rnSY\x1b\xa0%\x13"
|
|
||||||
b"\xc0$Q+\xd3\xd0\xd8 \x0c\xe9\xafI5&\xe5\xc1!\xaf",
|
|
||||||
# 0x65
|
|
||||||
b'\x07eGPG\x00\x01\x01',
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
def test_load(self, sigsubpacket):
|
def test_load(self, sigsubpacket):
|
||||||
spb = sigsubpacket[:]
|
spb = sigsubpacket[:]
|
||||||
sp = Signature(spb)
|
sp = Signature(spb)
|
||||||
|
|
||||||
assert spb == b'\xca\xfe\xba\xbe'
|
assert spb == _trailer
|
||||||
assert len(sp) == len(sigsubpacket) - 4
|
assert len(sp) == len(sigsubpacket) - len(_trailer)
|
||||||
assert len(sp) == len(sp.__bytes__())
|
assert len(sp) == len(sp.__bytes__())
|
||||||
assert sp.__bytes__() == bytes(sigsubpacket[:-4])
|
assert sp.__bytes__() == bytes(sigsubpacket[:-len(_trailer)])
|
||||||
|
|
||||||
if sp.header.typeid in _sspclasses:
|
if sp.header.typeid in _sspclasses:
|
||||||
assert sp.__class__.__name__ == _sspclasses[sp.header.typeid]
|
assert sp.__class__.__name__ == _sspclasses[sp.header.typeid]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
assert isinstance(sp, OpaqueSP)
|
assert isinstance(sp, OpaqueSP)
|
||||||
|
|
||||||
|
|
||||||
|
_uassps = [
|
||||||
|
# 0x01
|
||||||
|
b'\xc3\xfd\x01\x10\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xd8\xff'
|
||||||
|
b'\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x84\x00\xff\xff\xff'
|
||||||
|
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
|
||||||
|
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
|
||||||
|
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\xff\xff\xff\xff'
|
||||||
|
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
|
||||||
|
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
|
||||||
|
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x11\x08\x00x'
|
||||||
|
b'\x00x\x03\x01\x11\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x01\xa2\x00\x00\x01\x05\x01\x01\x01'
|
||||||
|
b'\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x10'
|
||||||
|
b'\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04\x00\x00\x01}\x01\x02\x03\x00\x04\x11\x05'
|
||||||
|
b'\x12!1A\x06\x13Qa\x07"q\x142\x81\x91\xa1\x08#B\xb1\xc1\x15R\xd1\xf0$3br\x82\t\n\x16\x17'
|
||||||
|
b'\x18\x19\x1a%&\'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x83\x84\x85\x86\x87\x88\x89\x8a'
|
||||||
|
b'\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5'
|
||||||
|
b'\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9'
|
||||||
|
b'\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\x01'
|
||||||
|
b'\x00\x03\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05'
|
||||||
|
b'\x06\x07\x08\t\n\x0b\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05\x04\x04\x00\x01\x02w\x00'
|
||||||
|
b'\x01\x02\x03\x11\x04\x05!1\x06\x12AQ\x07aq\x13"2\x81\x08\x14B\x91\xa1\xb1\xc1\t#3R\xf0\x15'
|
||||||
|
b'br\xd1\n\x16$4\xe1%\xf1\x17\x18\x19\x1a&\'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x82'
|
||||||
|
b'\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6'
|
||||||
|
b'\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca'
|
||||||
|
b'\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4\xf5'
|
||||||
|
b'\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00\x92\x80\n\x00('
|
||||||
|
b'\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n'
|
||||||
|
b'\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80'
|
||||||
|
b'\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02'
|
||||||
|
b'\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0'
|
||||||
|
b'\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x04\xc8\x1dH\xa0\x03'
|
||||||
|
b'#\xd4~t\x00\xb4\x00P\x02d\x0e\xa6\x80\x0c\x83\xd0\x8a\x00Z\x00:P\x02dz\x8f\xccP\x02\xd0'
|
||||||
|
b'\x01@\x05\x00\x14\x00P\x01@\x11cs\x1c\xfb\xd0\x03\xb6\x0fS@\r\x19V\xc7j\x00{\x1c\x0f~\xd4'
|
||||||
|
b'\x00\xc0\xb9\xe4\x9e\xb4\x00\xa5;\x8c\xd0\x02\xa1\xc8\xe7\xb5\x005\xb9`>\x9f\xce\x80\x1d'
|
||||||
|
b'\xb0z\x9a\x00i\x05\x0eA\xa0\t\x01\xc8\x07\xd6\x80\x16\x80\n\x00(\x00\xa0\x08\x81\x01\x89>'
|
||||||
|
b'\xff\x00\xce\x80\x1d\xbdh\x01\xbfy\xb3\xd8P\x00\xfdG\xd2\x80%\xa0\x06\xeeQ\xde\x80\x14\x10'
|
||||||
|
b'zP\x04m\xf7\x87\xe1\xfc\xe8\x01\xfb\xd6\x80\x18\xc7v\x00\xa0\x05q\xf2\x8fj\x00z\x9c\x80h'
|
||||||
|
b'\x01\x87\xe6\x7f\xa7\xf4\xa0\t(\x00\xa0\x08\x80\x05\x8e}\xff\x00\x9d\x00I\xb5}\x05\x00/N'
|
||||||
|
b'\x94\x01\x1b\x8e\x87\xf0\xa0\x07\x83\x91\x9a\x00B\xab\xd4\x8a\x00ju4\x00\x8d\xf7\x87\xe1'
|
||||||
|
b'\xfc\xe8\x02M\xab\xe8(\x00\x00\x0e\x82\x80\x14\xf21@\x11)\xdb\xb8\x1f\xf2h\x01\xc8:\x9fZ'
|
||||||
|
b'\x00}\x00\x14\x00\xc0\xa41=\xb9\xfdh\x01\xf4\x00P\x02\x11\x9e\r\x003k\x0e\x86\x80\x0c9\xeb'
|
||||||
|
b'\xd3\xfc\xfaP\x02\xa8 \x9c\xd0\x00T\x96\x07\xb7\x1f\xa5\x00>\x80\n\x00(\x02\'\x1f7\x1d\xe8'
|
||||||
|
b'\x02@01@\x0b@\x05\x00\x14\x00P\x01@\x05\x00\x14\x00P\x01@\x05\x00\x14\x00P\x01@\t\x81\x9c'
|
||||||
|
b'\xf7\xa0\x05\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02'
|
||||||
|
b'\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0'
|
||||||
|
b'\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00'
|
||||||
|
b'\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00('
|
||||||
|
b'\x00\xa0\x02\x80?\xff\xd9',
|
||||||
|
]
|
||||||
|
ua_subpkts = [bytearray(sp) + _trailer for sp in _uassps]
|
||||||
|
|
||||||
|
|
||||||
class TestUserAttributeSubPackets(object):
|
class TestUserAttributeSubPackets(object):
|
||||||
params = {
|
@pytest.mark.parametrize('uasubpacket', ua_subpkts)
|
||||||
'uasubpacket': [ bytearray(sp) + b'\xca\xfe\xba\xbe' for sp in
|
|
||||||
[ # 0x01
|
|
||||||
b'\xc3\xfd\x01\x10\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xd8\xff'
|
|
||||||
b'\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x84\x00\xff\xff\xff'
|
|
||||||
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
|
|
||||||
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
|
|
||||||
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\xff\xff\xff\xff'
|
|
||||||
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
|
|
||||||
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
|
|
||||||
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x11\x08\x00x'
|
|
||||||
b'\x00x\x03\x01\x11\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x01\xa2\x00\x00\x01\x05\x01\x01\x01'
|
|
||||||
b'\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x10'
|
|
||||||
b'\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04\x00\x00\x01}\x01\x02\x03\x00\x04\x11\x05'
|
|
||||||
b'\x12!1A\x06\x13Qa\x07"q\x142\x81\x91\xa1\x08#B\xb1\xc1\x15R\xd1\xf0$3br\x82\t\n\x16\x17'
|
|
||||||
b'\x18\x19\x1a%&\'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x83\x84\x85\x86\x87\x88\x89\x8a'
|
|
||||||
b'\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5'
|
|
||||||
b'\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9'
|
|
||||||
b'\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\x01'
|
|
||||||
b'\x00\x03\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05'
|
|
||||||
b'\x06\x07\x08\t\n\x0b\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05\x04\x04\x00\x01\x02w\x00'
|
|
||||||
b'\x01\x02\x03\x11\x04\x05!1\x06\x12AQ\x07aq\x13"2\x81\x08\x14B\x91\xa1\xb1\xc1\t#3R\xf0\x15'
|
|
||||||
b'br\xd1\n\x16$4\xe1%\xf1\x17\x18\x19\x1a&\'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x82'
|
|
||||||
b'\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6'
|
|
||||||
b'\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca'
|
|
||||||
b'\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4\xf5'
|
|
||||||
b'\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00\x92\x80\n\x00('
|
|
||||||
b'\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n'
|
|
||||||
b'\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80'
|
|
||||||
b'\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02'
|
|
||||||
b'\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0'
|
|
||||||
b'\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x04\xc8\x1dH\xa0\x03'
|
|
||||||
b'#\xd4~t\x00\xb4\x00P\x02d\x0e\xa6\x80\x0c\x83\xd0\x8a\x00Z\x00:P\x02dz\x8f\xccP\x02\xd0'
|
|
||||||
b'\x01@\x05\x00\x14\x00P\x01@\x11cs\x1c\xfb\xd0\x03\xb6\x0fS@\r\x19V\xc7j\x00{\x1c\x0f~\xd4'
|
|
||||||
b'\x00\xc0\xb9\xe4\x9e\xb4\x00\xa5;\x8c\xd0\x02\xa1\xc8\xe7\xb5\x005\xb9`>\x9f\xce\x80\x1d'
|
|
||||||
b'\xb0z\x9a\x00i\x05\x0eA\xa0\t\x01\xc8\x07\xd6\x80\x16\x80\n\x00(\x00\xa0\x08\x81\x01\x89>'
|
|
||||||
b'\xff\x00\xce\x80\x1d\xbdh\x01\xbfy\xb3\xd8P\x00\xfdG\xd2\x80%\xa0\x06\xeeQ\xde\x80\x14\x10'
|
|
||||||
b'zP\x04m\xf7\x87\xe1\xfc\xe8\x01\xfb\xd6\x80\x18\xc7v\x00\xa0\x05q\xf2\x8fj\x00z\x9c\x80h'
|
|
||||||
b'\x01\x87\xe6\x7f\xa7\xf4\xa0\t(\x00\xa0\x08\x80\x05\x8e}\xff\x00\x9d\x00I\xb5}\x05\x00/N'
|
|
||||||
b'\x94\x01\x1b\x8e\x87\xf0\xa0\x07\x83\x91\x9a\x00B\xab\xd4\x8a\x00ju4\x00\x8d\xf7\x87\xe1'
|
|
||||||
b'\xfc\xe8\x02M\xab\xe8(\x00\x00\x0e\x82\x80\x14\xf21@\x11)\xdb\xb8\x1f\xf2h\x01\xc8:\x9fZ'
|
|
||||||
b'\x00}\x00\x14\x00\xc0\xa41=\xb9\xfdh\x01\xf4\x00P\x02\x11\x9e\r\x003k\x0e\x86\x80\x0c9\xeb'
|
|
||||||
b'\xd3\xfc\xfaP\x02\xa8 \x9c\xd0\x00T\x96\x07\xb7\x1f\xa5\x00>\x80\n\x00(\x02\'\x1f7\x1d\xe8'
|
|
||||||
b'\x02@01@\x0b@\x05\x00\x14\x00P\x01@\x05\x00\x14\x00P\x01@\x05\x00\x14\x00P\x01@\t\x81\x9c'
|
|
||||||
b'\xf7\xa0\x05\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02'
|
|
||||||
b'\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0'
|
|
||||||
b'\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00'
|
|
||||||
b'\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00(\x00\xa0\x02\x80\n\x00('
|
|
||||||
b'\x00\xa0\x02\x80?\xff\xd9'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
}
|
|
||||||
def test_load(self, uasubpacket):
|
def test_load(self, uasubpacket):
|
||||||
spb = uasubpacket[:]
|
spb = uasubpacket[:]
|
||||||
sp = UserAttribute(spb)
|
sp = UserAttribute(spb)
|
||||||
|
|
||||||
assert spb == b'\xca\xfe\xba\xbe'
|
assert spb == _trailer
|
||||||
assert len(sp) == len(uasubpacket) - 4
|
assert len(sp) == len(uasubpacket) - len(_trailer)
|
||||||
assert len(sp) == len(sp.__bytes__())
|
assert len(sp) == len(sp.__bytes__())
|
||||||
assert sp.__bytes__() == uasubpacket[:-4]
|
assert sp.__bytes__() == uasubpacket[:-len(_trailer)]
|
||||||
|
|
||||||
if sp.header.typeid in _uaspclasses:
|
if sp.header.typeid in _uaspclasses:
|
||||||
assert sp.__class__.__name__ == _uaspclasses[sp.header.typeid]
|
assert sp.__class__.__name__ == _uaspclasses[sp.header.typeid]
|
||||||
@@ -269,31 +279,32 @@ class TestUserAttributeSubPackets(object):
|
|||||||
assert isinstance(sp, OpaqueSP)
|
assert isinstance(sp, OpaqueSP)
|
||||||
|
|
||||||
|
|
||||||
|
_s2k_parts = [
|
||||||
|
# usage byte is always \xff
|
||||||
|
b'\xff',
|
||||||
|
# symmetric cipher algorithm list
|
||||||
|
b'\x01\x02\x03\x04\x07\x08\x09\x0B\x0C\x0D',
|
||||||
|
# specifier
|
||||||
|
# b'\x00', (simple)
|
||||||
|
# b'\x01', (iterated)
|
||||||
|
# b'\x03', (salted)
|
||||||
|
# hash algorithm list
|
||||||
|
b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B',
|
||||||
|
]
|
||||||
|
_iv = b'\xDE\xAD\xBE\xEF\xDE\xAD\xBE\xEF'
|
||||||
|
_salt = b'\xC0\xDE\xC0\xDE\xC0\xDE\xC0\xDE'
|
||||||
|
_count = b'\x10' # expands from 0x10 to 2048
|
||||||
|
|
||||||
|
# simple S2Ks
|
||||||
|
sis2ks = [bytearray(i) + _iv for i in itertools.product(*(_s2k_parts[:2] + [b'\x00'] + _s2k_parts[2:]))]
|
||||||
|
# salted S2Ks
|
||||||
|
sas2ks = [bytearray(i) + _salt + _iv for i in itertools.product(*(_s2k_parts[:2] + [b'\x01'] + _s2k_parts[2:]))]
|
||||||
|
# iterated S2Ks
|
||||||
|
is2ks = [bytearray(i) + _salt + _count + _iv for i in itertools.product(*(_s2k_parts[:2] + [b'\x03'] + _s2k_parts[2:]))]
|
||||||
|
|
||||||
|
|
||||||
class TestString2Key(object):
|
class TestString2Key(object):
|
||||||
params = {'sis2k': [ (bytearray(i) +
|
@pytest.mark.parametrize('sis2k', sis2ks)
|
||||||
b'\xDE\xAD\xBE\xEF\xDE\xAD\xBE\xEF') # iv
|
|
||||||
for i in product(b'\xff', # usage
|
|
||||||
b'\x01\x02\x03\x04\x07\x08\x09\x0B\x0C\x0D', # symmetric cipher algorithm
|
|
||||||
b'\x00', # specifier (simple)
|
|
||||||
b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B') # hash algorithm
|
|
||||||
],
|
|
||||||
'sas2k': [ (bytearray(i) +
|
|
||||||
b'\xCA\xFE\xBA\xBE\xCA\xFE\xBA\xBE' + # salt
|
|
||||||
b'\xDE\xAD\xBE\xEF\xDE\xAD\xBE\xEF') # iv
|
|
||||||
for i in product(b'\xff', # usage
|
|
||||||
b'\x01\x02\x03\x04\x07\x08\x09\x0B\x0C\x0D', # symmetric cipher algorithm
|
|
||||||
b'\x01', # specifier (simple)
|
|
||||||
b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B') # hash algorithm
|
|
||||||
],
|
|
||||||
'is2k': [ (bytearray(i) +
|
|
||||||
b'\xCA\xFE\xBA\xBE\xCA\xFE\xBA\xBE' + # salt
|
|
||||||
b'\x10' + # count
|
|
||||||
b'\xDE\xAD\xBE\xEF\xDE\xAD\xBE\xEF') # iv
|
|
||||||
for i in product(b'\xff', # usage
|
|
||||||
b'\x01\x02\x03\x04\x07\x08\x09\x0B\x0C\x0D', # symmetric cipher algorithm
|
|
||||||
b'\x03', # specifier (simple)
|
|
||||||
b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B') # hash algorithm
|
|
||||||
]}
|
|
||||||
def test_simple_string2key(self, sis2k):
|
def test_simple_string2key(self, sis2k):
|
||||||
b = sis2k[:]
|
b = sis2k[:]
|
||||||
s = String2Key()
|
s = String2Key()
|
||||||
@@ -307,9 +318,9 @@ class TestString2Key(object):
|
|||||||
assert s.halg in HashAlgorithm
|
assert s.halg in HashAlgorithm
|
||||||
assert s.encalg in SymmetricKeyAlgorithm
|
assert s.encalg in SymmetricKeyAlgorithm
|
||||||
assert s.specifier == String2KeyType.Simple
|
assert s.specifier == String2KeyType.Simple
|
||||||
assert s.iv == b'\xDE\xAD\xBE\xEF\xDE\xAD\xBE\xEF'
|
assert s.iv == _iv
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('sas2k', sas2ks)
|
||||||
def test_salted_string2key(self, sas2k):
|
def test_salted_string2key(self, sas2k):
|
||||||
b = sas2k[:]
|
b = sas2k[:]
|
||||||
s = String2Key()
|
s = String2Key()
|
||||||
@@ -323,9 +334,10 @@ class TestString2Key(object):
|
|||||||
assert s.halg in HashAlgorithm
|
assert s.halg in HashAlgorithm
|
||||||
assert s.encalg in SymmetricKeyAlgorithm
|
assert s.encalg in SymmetricKeyAlgorithm
|
||||||
assert s.specifier == String2KeyType.Salted
|
assert s.specifier == String2KeyType.Salted
|
||||||
assert s.salt == b'\xCA\xFE\xBA\xBE\xCA\xFE\xBA\xBE'
|
assert s.salt == _salt
|
||||||
assert s.iv == b'\xDE\xAD\xBE\xEF\xDE\xAD\xBE\xEF'
|
assert s.iv == _iv
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('is2k', is2ks)
|
||||||
def test_iterated_string2key(self, is2k):
|
def test_iterated_string2key(self, is2k):
|
||||||
b = is2k[:]
|
b = is2k[:]
|
||||||
s = String2Key()
|
s = String2Key()
|
||||||
@@ -339,20 +351,6 @@ class TestString2Key(object):
|
|||||||
assert s.halg in HashAlgorithm
|
assert s.halg in HashAlgorithm
|
||||||
assert s.encalg in SymmetricKeyAlgorithm
|
assert s.encalg in SymmetricKeyAlgorithm
|
||||||
assert s.specifier == String2KeyType.Iterated
|
assert s.specifier == String2KeyType.Iterated
|
||||||
assert s.salt == b'\xCA\xFE\xBA\xBE\xCA\xFE\xBA\xBE'
|
assert s.salt == _salt
|
||||||
assert s.count == 2048
|
assert s.count == 2048
|
||||||
assert s.iv == b'\xDE\xAD\xBE\xEF\xDE\xAD\xBE\xEF'
|
assert s.iv == _iv
|
||||||
|
|
||||||
|
|
||||||
# TODO: this
|
|
||||||
# class TestKeyMaterial(object):
|
|
||||||
# params = {
|
|
||||||
# 'pkt': [],
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# ids = {
|
|
||||||
# 'test_keymaterial': [],
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# def test_keymaterial(self, pkt):
|
|
||||||
# pass
|
|
||||||
|
|||||||
@@ -3,22 +3,7 @@
|
|||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import glob
|
from pgpy.types import PGPObject
|
||||||
|
|
||||||
from pgpy.types import Armorable, PGPObject
|
|
||||||
|
|
||||||
|
|
||||||
# read txt files in tests/testdata/text/*.txt and yield ids and strings
|
|
||||||
# TODO: figure out how to set ids
|
|
||||||
# @pytest.yield_fixture
|
|
||||||
def get_text():
|
|
||||||
for tf in sorted(glob.glob('tests/testdata/text/*.txt')):
|
|
||||||
with open(tf, 'r') as f:
|
|
||||||
for line in f:
|
|
||||||
# skip comments and blank lines
|
|
||||||
if line.startswith('#') or line == "":
|
|
||||||
continue
|
|
||||||
yield line.split(': ')
|
|
||||||
|
|
||||||
text = {
|
text = {
|
||||||
# some basic utf-8 test strings - these should all pass
|
# some basic utf-8 test strings - these should all pass
|
||||||
@@ -62,22 +47,15 @@ class FakePGPObject(PGPObject):
|
|||||||
|
|
||||||
|
|
||||||
class TestPGPObject(object):
|
class TestPGPObject(object):
|
||||||
params = {
|
|
||||||
'text': [ v for _, v in sorted(text.items()) ],
|
|
||||||
'encoded_text': [ v for _, v in sorted(encoded_text.items()) ],
|
|
||||||
}
|
|
||||||
ids = {
|
|
||||||
'test_text_to_bytes': [ k for k, _ in sorted(text.items()) ],
|
|
||||||
'test_text_to_bytes_encodings': [ k for k, _ in sorted(encoded_text.items()) ],
|
|
||||||
}
|
|
||||||
|
|
||||||
@pytest.mark.regression(issue=154)
|
@pytest.mark.regression(issue=154)
|
||||||
|
@pytest.mark.parametrize('text', [v for _, v in sorted(text.items())], ids=sorted(text.keys()))
|
||||||
def test_text_to_bytes(self, text):
|
def test_text_to_bytes(self, text):
|
||||||
pgpo = FakePGPObject.new(text)
|
pgpo = FakePGPObject.new(text)
|
||||||
|
|
||||||
assert pgpo.__bytearray__() == bytearray(b'_fake_') + bytearray(text, 'utf-8')
|
assert pgpo.__bytearray__() == bytearray(b'_fake_') + bytearray(text, 'utf-8')
|
||||||
|
|
||||||
@pytest.mark.regression(issue=154)
|
@pytest.mark.regression(issue=154)
|
||||||
|
@pytest.mark.parametrize('encoded_text', [v for _, v in sorted(encoded_text.items())], ids=sorted(encoded_text.keys()))
|
||||||
def test_text_to_bytes_encodings(self, encoded_text):
|
def test_text_to_bytes_encodings(self, encoded_text):
|
||||||
pgpo = FakePGPObject.new(encoded_text)
|
pgpo = FakePGPObject.new(encoded_text)
|
||||||
# this should fail
|
# this should fail
|
||||||
|
|||||||
@@ -5,14 +5,13 @@ import pytest
|
|||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
from pgpy.packet import Packet
|
from pgpy.packet import Packet
|
||||||
from pgpy.packet import PubKeyV4, PubSubKeyV4, PrivKeyV4, PrivSubKeyV4
|
from pgpy.packet import PubKeyV4, PubSubKeyV4, PrivKeyV4, PrivSubKeyV4
|
||||||
from pgpy.packet import Opaque
|
from pgpy.packet import Opaque
|
||||||
|
|
||||||
import pgpy.packet.fields
|
# import pgpy.packet.fields
|
||||||
|
|
||||||
|
|
||||||
|
_trailer = b'\xde\xca\xff\xba\xdd'
|
||||||
_pclasses = {
|
_pclasses = {
|
||||||
(0x01, 3): 'PKESessionKeyV3',
|
(0x01, 3): 'PKESessionKeyV3',
|
||||||
(0x02, 4): 'SignatureV4',
|
(0x02, 4): 'SignatureV4',
|
||||||
@@ -36,39 +35,35 @@ _pclasses = {
|
|||||||
|
|
||||||
def binload(f):
|
def binload(f):
|
||||||
with open(f, 'rb') as ff:
|
with open(f, 'rb') as ff:
|
||||||
return bytearray(ff.read())
|
buf = bytearray(os.fstat(ff.fileno()).st_size)
|
||||||
|
ff.readinto(buf)
|
||||||
|
return buf
|
||||||
|
|
||||||
|
|
||||||
skip_files = {'tests/testdata/packets/{:s}'.format(pkt) for pkt in ['11.literal.partial']}
|
skip_files = {'tests/testdata/packets/{:s}'.format(pkt) for pkt in ['11.partial.literal']}
|
||||||
|
pktfiles = sorted(glob.glob('tests/testdata/packets/[0-9]*'))
|
||||||
|
|
||||||
|
|
||||||
class TestPacket(object):
|
class TestPacket(object):
|
||||||
params = {
|
@pytest.mark.parametrize('packet', pktfiles, ids=[os.path.basename(f) for f in pktfiles])
|
||||||
# 'packet': sorted([f for f in glob.glob('tests/testdata/packets/[0-9]*') if f not in skip_files])
|
|
||||||
'packet': sorted([f for f in glob.glob('tests/testdata/packets/[0-9]*')])
|
|
||||||
}
|
|
||||||
ids = {
|
|
||||||
'test_load': sorted([os.path.basename(f).replace('.', '_') for f in glob.glob('tests/testdata/packets/[0-9]*')])
|
|
||||||
}
|
|
||||||
|
|
||||||
def test_load(self, packet):
|
def test_load(self, packet):
|
||||||
if packet in skip_files:
|
if packet in skip_files:
|
||||||
pytest.skip("not implemented yet")
|
pytest.skip("not implemented yet")
|
||||||
|
|
||||||
b = binload(packet) + b'\xca\xfe\xba\xbe'
|
b = binload(packet) + _trailer
|
||||||
_b = b[:]
|
_b = b[:]
|
||||||
p = Packet(_b)
|
p = Packet(_b)
|
||||||
|
|
||||||
# parsed all bytes
|
# parsed all bytes
|
||||||
assert _b == b'\xca\xfe\xba\xbe'
|
assert _b == _trailer
|
||||||
|
|
||||||
# length is computed correctly
|
# length is computed correctly
|
||||||
assert p.header.length + len(p.header) == len(p)
|
assert p.header.length + len(p.header) == len(p)
|
||||||
assert len(p) == len(b) - 4
|
assert len(p) == len(b) - len(_trailer)
|
||||||
assert len(p.__bytes__()) == len(b) - 4
|
assert len(p.__bytes__()) == len(b) - len(_trailer)
|
||||||
|
|
||||||
# __bytes__ output is correct
|
# __bytes__ output is correct
|
||||||
assert p.__bytes__() == b[:-4]
|
assert p.__bytes__() == b[:-len(_trailer)]
|
||||||
|
|
||||||
# instantiated class is what we expected
|
# instantiated class is what we expected
|
||||||
if hasattr(p.header, 'version') and (p.header.tag, p.header.version) in _pclasses:
|
if hasattr(p.header, 'version') and (p.header.tag, p.header.version) in _pclasses:
|
||||||
|
|||||||
@@ -4,227 +4,301 @@ import pytest
|
|||||||
|
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from pgpy.constants import CompressionAlgorithm
|
|
||||||
from pgpy.constants import HashAlgorithm
|
from pgpy.constants import HashAlgorithm
|
||||||
from pgpy.constants import KeyFlags
|
|
||||||
from pgpy.constants import PubKeyAlgorithm
|
from pgpy.constants import PubKeyAlgorithm
|
||||||
from pgpy.constants import SignatureType
|
from pgpy.constants import SignatureType
|
||||||
from pgpy.constants import SymmetricKeyAlgorithm
|
|
||||||
|
|
||||||
from pgpy.pgp import PGPKey
|
from pgpy.pgp import PGPKey
|
||||||
from pgpy.pgp import PGPMessage
|
from pgpy.pgp import PGPMessage
|
||||||
from pgpy.pgp import PGPSignature
|
from pgpy.pgp import PGPSignature
|
||||||
|
|
||||||
|
|
||||||
|
blocks = sorted(glob.glob('tests/testdata/blocks/*.asc'))
|
||||||
|
block_attrs = {
|
||||||
|
'tests/testdata/blocks/message.ascii.asc':
|
||||||
|
[('encrypters', set()),
|
||||||
|
('filename', 'ascii'),
|
||||||
|
('is_compressed', False),
|
||||||
|
('is_encrypted', False),
|
||||||
|
('is_signed', False),
|
||||||
|
('issuers', set()),
|
||||||
|
('message', "This is stored, textually!\r\n"),
|
||||||
|
('signers', set()),
|
||||||
|
('type', 'literal'),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/message.compressed.asc':
|
||||||
|
[('encrypters', set()),
|
||||||
|
('filename', 'lit'),
|
||||||
|
('is_compressed', True),
|
||||||
|
('is_encrypted', False),
|
||||||
|
('is_signed', False),
|
||||||
|
('issuers', set()),
|
||||||
|
('message', b"This is stored, literally\\!\n\n"),
|
||||||
|
('signers', set()),
|
||||||
|
('type', 'literal'),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/message.literal.asc':
|
||||||
|
[('encrypters', set()),
|
||||||
|
('filename', 'lit'),
|
||||||
|
('is_compressed', False),
|
||||||
|
('is_encrypted', False),
|
||||||
|
('is_signed', False),
|
||||||
|
('issuers', set()),
|
||||||
|
('message', b"This is stored, literally\\!\n\n"),
|
||||||
|
('signers', set()),
|
||||||
|
('type', 'literal'),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/message.onepass.asc':
|
||||||
|
[('encrypters', set()),
|
||||||
|
('filename', 'lit'),
|
||||||
|
('is_compressed', False),
|
||||||
|
('is_encrypted', False),
|
||||||
|
('is_signed', True),
|
||||||
|
('issuers', {'2A834D8E5918E886'}),
|
||||||
|
('message', b"This is stored, literally\\!\n\n"),
|
||||||
|
('signers', {'2A834D8E5918E886'}),
|
||||||
|
('type', 'literal'),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/message.two_onepass.asc':
|
||||||
|
[('encrypters', set()),
|
||||||
|
('filename', 'lit'),
|
||||||
|
('is_compressed', False),
|
||||||
|
('is_encrypted', False),
|
||||||
|
('is_signed', True),
|
||||||
|
('issuers', {'2A834D8E5918E886', 'A5DCDC966453140E'}),
|
||||||
|
('message', b"This is stored, literally\\!\n\n"),
|
||||||
|
('signers', {'2A834D8E5918E886', 'A5DCDC966453140E'}),
|
||||||
|
('type', 'literal'),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/message.signed.asc':
|
||||||
|
[('encrypters', set()),
|
||||||
|
('filename', 'lit'),
|
||||||
|
('is_compressed', False),
|
||||||
|
('is_encrypted', False),
|
||||||
|
('is_signed', True),
|
||||||
|
('issuers', {'2A834D8E5918E886'}),
|
||||||
|
('message', b"This is stored, literally\\!\n\n"),
|
||||||
|
('signers', {'2A834D8E5918E886'}),
|
||||||
|
('type', 'literal'),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/cleartext.asc':
|
||||||
|
[('encrypters', set()),
|
||||||
|
('is_compressed', False),
|
||||||
|
('is_encrypted', False),
|
||||||
|
('is_signed', True),
|
||||||
|
('issuers', {'2A834D8E5918E886'}),
|
||||||
|
('message', "This is stored, literally\\!\n"),
|
||||||
|
('signers', {'2A834D8E5918E886'}),
|
||||||
|
('type', 'cleartext'),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/cleartext.twosigs.asc':
|
||||||
|
[('encrypters', set()),
|
||||||
|
('is_compressed', False),
|
||||||
|
('is_encrypted', False),
|
||||||
|
('is_signed', True),
|
||||||
|
('issuers', {'2A834D8E5918E886', 'A5DCDC966453140E'}),
|
||||||
|
('message', "This is stored, literally\\!\n"),
|
||||||
|
('signers', {'2A834D8E5918E886', 'A5DCDC966453140E'}),
|
||||||
|
('type', 'cleartext'),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/message.encrypted.asc':
|
||||||
|
[('encrypters', {'EEE097A017B979CA'}),
|
||||||
|
('is_compressed', False),
|
||||||
|
('is_encrypted', True),
|
||||||
|
('is_signed', False),
|
||||||
|
('issuers', {'EEE097A017B979CA'}),
|
||||||
|
('signers', set()),
|
||||||
|
('type', 'encrypted')],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/message.encrypted.signed.asc':
|
||||||
|
[('encrypters', {'EEE097A017B979CA'}),
|
||||||
|
('is_compressed', False),
|
||||||
|
('is_encrypted', True),
|
||||||
|
('is_signed', False),
|
||||||
|
('issuers', {'EEE097A017B979CA'}),
|
||||||
|
('signers', set()),
|
||||||
|
('type', 'encrypted')],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/message.ecc.encrypted.asc':
|
||||||
|
[('encrypters', {'77CEB7A34089AB73'}),
|
||||||
|
('is_compressed', False),
|
||||||
|
('is_encrypted', True),
|
||||||
|
('is_signed', False),
|
||||||
|
('issuers', {'77CEB7A34089AB73'}),
|
||||||
|
('signers', set()),
|
||||||
|
('type', 'encrypted')],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/revochiio.asc':
|
||||||
|
[('created', datetime(2014, 9, 11, 22, 55, 53)),
|
||||||
|
('fingerprint', "AE15 9FF3 4C1A 2426 B7F8 0F1A 560C F308 EF60 CFA3"),
|
||||||
|
('expires_at', datetime(2018, 9, 12, 1, 0, 59)),
|
||||||
|
('is_expired', False),
|
||||||
|
('is_primary', True),
|
||||||
|
('is_protected', False),
|
||||||
|
('is_public', True),
|
||||||
|
('is_unlocked', True),
|
||||||
|
('key_algorithm', PubKeyAlgorithm.RSAEncryptOrSign),
|
||||||
|
('magic', "PUBLIC KEY BLOCK"),
|
||||||
|
('parent', None),
|
||||||
|
('signers', {'560CF308EF60CFA3'}),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/expyro.asc':
|
||||||
|
[('created', datetime(1970, 1, 1)),
|
||||||
|
('expires_at', datetime(1970, 1, 2)),
|
||||||
|
('fingerprint', '24EB C1B0 29B1 FCF8 29A5 C150 1A48 291A FB91 A533'),
|
||||||
|
('is_expired', True),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/rsapubkey.asc':
|
||||||
|
[('created', datetime(2014, 7, 23, 21, 19, 24)),
|
||||||
|
('expires_at', None),
|
||||||
|
('fingerprint', "F429 4BC8 094A 7E05 85C8 5E86 3747 3B37 58C4 4F36"),
|
||||||
|
('is_expired', False),
|
||||||
|
('is_primary', True),
|
||||||
|
('is_protected', False),
|
||||||
|
('is_public', True),
|
||||||
|
('is_unlocked', True),
|
||||||
|
('key_algorithm', PubKeyAlgorithm.RSAEncryptOrSign),
|
||||||
|
('magic', "PUBLIC KEY BLOCK"),
|
||||||
|
('parent', None),
|
||||||
|
('signers', set()),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/rsaseckey.asc':
|
||||||
|
[('created', datetime(2014, 7, 23, 21, 19, 24)),
|
||||||
|
('expires_at', None),
|
||||||
|
('fingerprint', "F429 4BC8 094A 7E05 85C8 5E86 3747 3B37 58C4 4F36"),
|
||||||
|
('is_expired', False),
|
||||||
|
('is_primary', True),
|
||||||
|
('is_protected', False),
|
||||||
|
('is_public', False),
|
||||||
|
('is_unlocked', True),
|
||||||
|
('key_algorithm', PubKeyAlgorithm.RSAEncryptOrSign),
|
||||||
|
('magic', "PRIVATE KEY BLOCK"),
|
||||||
|
('parent', None),
|
||||||
|
('signers', set()),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/rsasignature.asc':
|
||||||
|
[('__sig__', b'\x70\x38\x79\xd0\x58\x70\x58\x7b\x50\xe6\xab\x8f\x9d\xc3\x46\x2c\x5a\x6b\x98\x96\xcf'
|
||||||
|
b'\x3b\xa3\x79\x13\x08\x6d\x90\x9d\x67\xd2\x48\x7d\xd7\x1a\xa5\x98\xa7\x8f\xca\xe3\x24'
|
||||||
|
b'\xd4\x19\xab\xe5\x45\xc5\xff\x21\x0c\x72\x88\x91\xe6\x67\xd7\xe5\x00\xb3\xf5\x55\x0b'
|
||||||
|
b'\xd0\xaf\x77\xb3\x7e\xa4\x79\x59\x06\xa2\x05\x44\x9d\xd2\xa9\xcf\xb1\xf8\x03\xc1\x90'
|
||||||
|
b'\x81\x87\x36\x1a\xa6\x5c\x79\x98\xfe\xdb\xdd\x23\x54\x69\x92\x2f\x0b\xc4\xee\x2a\x61'
|
||||||
|
b'\x77\x35\x59\x6e\xb2\xe2\x1b\x80\x61\xaf\x2d\x7a\x64\x38\xfe\xe3\x95\xcc\xe8\xa4\x05'
|
||||||
|
b'\x55\x5d'),
|
||||||
|
('cipherprefs', []),
|
||||||
|
('compprefs', []),
|
||||||
|
('created', datetime.utcfromtimestamp(1402615373)),
|
||||||
|
('embedded', False),
|
||||||
|
('exportable', True),
|
||||||
|
('features', set()),
|
||||||
|
('hash2', b'\xc4\x24'),
|
||||||
|
('hashprefs', []),
|
||||||
|
('hash_algorithm', HashAlgorithm.SHA512),
|
||||||
|
('is_expired', False),
|
||||||
|
('key_algorithm', PubKeyAlgorithm.RSAEncryptOrSign),
|
||||||
|
('key_flags', set()),
|
||||||
|
('keyserver', ''),
|
||||||
|
('keyserverprefs', []),
|
||||||
|
('magic', "SIGNATURE"),
|
||||||
|
('notation', {}),
|
||||||
|
('policy_uri', ''),
|
||||||
|
('revocable', True),
|
||||||
|
('revocation_key', None),
|
||||||
|
('signer', 'FCAE54F74BA27CF7'),
|
||||||
|
('type', SignatureType.BinaryDocument)],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/eccpubkey.asc':
|
||||||
|
[('created', datetime(2010, 9, 17, 20, 33, 49)),
|
||||||
|
('expires_at', None),
|
||||||
|
('fingerprint', "502D 1A53 65D1 C0CA A699 4539 0BA5 2DF0 BAA5 9D9C"),
|
||||||
|
('is_expired', False),
|
||||||
|
('is_primary', True),
|
||||||
|
('is_protected', False),
|
||||||
|
('is_public', True),
|
||||||
|
('is_unlocked', True),
|
||||||
|
('key_algorithm', PubKeyAlgorithm.ECDSA),
|
||||||
|
('magic', "PUBLIC KEY BLOCK"),
|
||||||
|
('parent', None),
|
||||||
|
('signers', set()),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/eccseckey.asc':
|
||||||
|
[('created', datetime(2010, 9, 17, 20, 33, 49)),
|
||||||
|
('expires_at', None),
|
||||||
|
('fingerprint', "502D 1A53 65D1 C0CA A699 4539 0BA5 2DF0 BAA5 9D9C"),
|
||||||
|
('is_expired', False),
|
||||||
|
('is_primary', True),
|
||||||
|
('is_protected', True),
|
||||||
|
('is_public', False),
|
||||||
|
('is_unlocked', False),
|
||||||
|
('key_algorithm', PubKeyAlgorithm.ECDSA),
|
||||||
|
('magic', "PRIVATE KEY BLOCK"),
|
||||||
|
('parent', None),
|
||||||
|
('signers', set()),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/dsaseckey.asc':
|
||||||
|
[('created', datetime(2017, 2, 21, 19, 21, 41)),
|
||||||
|
('expires_at', None),
|
||||||
|
('fingerprint', "2B5B BB14 3BA0 B290 DCEE 6668 B798 AE89 9087 7201"),
|
||||||
|
('is_expired', False),
|
||||||
|
('is_primary', True),
|
||||||
|
('is_protected', True),
|
||||||
|
('is_public', False),
|
||||||
|
('is_unlocked', False),
|
||||||
|
('key_algorithm', PubKeyAlgorithm.DSA),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/dsapubkey.asc':
|
||||||
|
[('created', datetime(2017, 2, 21, 19, 21, 41)),
|
||||||
|
('expires_at', None),
|
||||||
|
('fingerprint', "2B5B BB14 3BA0 B290 DCEE 6668 B798 AE89 9087 7201"),
|
||||||
|
('is_expired', False),
|
||||||
|
('is_primary', True),
|
||||||
|
('is_protected', False),
|
||||||
|
('is_public', True),
|
||||||
|
('is_unlocked', True),
|
||||||
|
('key_algorithm', PubKeyAlgorithm.DSA),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/openpgp.js.pubkey.asc':
|
||||||
|
[('created', datetime(2016, 6, 2, 21, 57, 13)),
|
||||||
|
('expires_at', None),
|
||||||
|
('fingerprint', "C7C3 8ECE E94A 4AD3 2DDB 064E 14AB 44C7 4D1B DAB8"),
|
||||||
|
('is_expired', False),
|
||||||
|
('is_primary', True),
|
||||||
|
('is_protected', False),
|
||||||
|
('is_public', True),
|
||||||
|
('is_unlocked', True),
|
||||||
|
('key_algorithm', PubKeyAlgorithm.RSAEncryptOrSign),
|
||||||
|
('magic', "PUBLIC KEY BLOCK"),
|
||||||
|
('parent', None),
|
||||||
|
('signers', set()), ],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/openpgp.js.seckey.asc':
|
||||||
|
[('created', datetime(2016, 6, 2, 21, 57, 13)),
|
||||||
|
('expires_at', None),
|
||||||
|
('fingerprint', "C7C3 8ECE E94A 4AD3 2DDB 064E 14AB 44C7 4D1B DAB8"),
|
||||||
|
('is_expired', False),
|
||||||
|
('is_primary', True),
|
||||||
|
('is_protected', False),
|
||||||
|
('is_public', False),
|
||||||
|
('is_unlocked', True),
|
||||||
|
('key_algorithm', PubKeyAlgorithm.RSAEncryptOrSign),
|
||||||
|
('magic', "PRIVATE KEY BLOCK"),
|
||||||
|
('parent', None),
|
||||||
|
('signers', set()), ],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/signature.expired.asc':
|
||||||
|
[('created', datetime(2014, 9, 28, 20, 54, 42)),
|
||||||
|
('is_expired', True),],
|
||||||
|
|
||||||
|
'tests/testdata/blocks/signature.non-exportable.asc':
|
||||||
|
[('created', datetime(2017, 2, 21, 20, 43, 34)),
|
||||||
|
('exportable', False),]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# generic block tests
|
# generic block tests
|
||||||
class TestBlocks(object):
|
class TestBlocks(object):
|
||||||
params = {
|
@pytest.mark.parametrize('block', blocks, ids=[os.path.basename(f) for f in blocks])
|
||||||
'block': sorted(glob.glob('tests/testdata/blocks/*.asc'))
|
def test_load_blob(self, block):
|
||||||
}
|
|
||||||
ids = {
|
|
||||||
'test_load': [ os.path.basename(fn).replace('.', '_') for fn in sorted(glob.glob('tests/testdata/blocks/*.asc')) ]
|
|
||||||
}
|
|
||||||
attrs = {
|
|
||||||
'tests/testdata/blocks/message.compressed.asc':
|
|
||||||
[('encrypters', set()),
|
|
||||||
('filename', 'lit'),
|
|
||||||
('is_compressed', True),
|
|
||||||
('is_encrypted', False),
|
|
||||||
('is_signed', False),
|
|
||||||
('issuers', set()),
|
|
||||||
('message', b"This is stored, literally\\!\n\n"),
|
|
||||||
('signers', set()),
|
|
||||||
('type', 'literal'),],
|
|
||||||
'tests/testdata/blocks/message.literal.asc':
|
|
||||||
[('encrypters', set()),
|
|
||||||
('filename', 'lit'),
|
|
||||||
('is_compressed', False),
|
|
||||||
('is_encrypted', False),
|
|
||||||
('is_signed', False),
|
|
||||||
('issuers', set()),
|
|
||||||
('message', b"This is stored, literally\\!\n\n"),
|
|
||||||
('signers', set()),
|
|
||||||
('type', 'literal'),],
|
|
||||||
'tests/testdata/blocks/message.onepass.asc':
|
|
||||||
[('encrypters', set()),
|
|
||||||
('filename', 'lit'),
|
|
||||||
('is_compressed', False),
|
|
||||||
('is_encrypted', False),
|
|
||||||
('is_signed', True),
|
|
||||||
('issuers', {'2A834D8E5918E886'}),
|
|
||||||
('message', b"This is stored, literally\\!\n\n"),
|
|
||||||
('signers', {'2A834D8E5918E886'}),
|
|
||||||
('type', 'literal'),],
|
|
||||||
'tests/testdata/blocks/message.two_onepass.asc':
|
|
||||||
[('encrypters', set()),
|
|
||||||
('filename', 'lit'),
|
|
||||||
('is_compressed', False),
|
|
||||||
('is_encrypted', False),
|
|
||||||
('is_signed', True),
|
|
||||||
('issuers', {'2A834D8E5918E886', 'A5DCDC966453140E'}),
|
|
||||||
('message', b"This is stored, literally\\!\n\n"),
|
|
||||||
('signers', {'2A834D8E5918E886', 'A5DCDC966453140E'}),
|
|
||||||
('type', 'literal'),],
|
|
||||||
'tests/testdata/blocks/message.signed.asc':
|
|
||||||
[('encrypters', set()),
|
|
||||||
('filename', 'lit'),
|
|
||||||
('is_compressed', False),
|
|
||||||
('is_encrypted', False),
|
|
||||||
('is_signed', True),
|
|
||||||
('issuers', {'2A834D8E5918E886'}),
|
|
||||||
('message', b"This is stored, literally\\!\n\n"),
|
|
||||||
('signers', {'2A834D8E5918E886'}),
|
|
||||||
('type', 'literal'),],
|
|
||||||
'tests/testdata/blocks/cleartext.asc':
|
|
||||||
[('encrypters', set()),
|
|
||||||
('is_compressed', False),
|
|
||||||
('is_encrypted', False),
|
|
||||||
('is_signed', True),
|
|
||||||
('issuers', {'2A834D8E5918E886'}),
|
|
||||||
('message', "This is stored, literally\\!\n"),
|
|
||||||
('signers', {'2A834D8E5918E886'}),
|
|
||||||
('type', 'cleartext'),],
|
|
||||||
'tests/testdata/blocks/cleartext.twosigs.asc':
|
|
||||||
[('encrypters', set()),
|
|
||||||
('is_compressed', False),
|
|
||||||
('is_encrypted', False),
|
|
||||||
('is_signed', True),
|
|
||||||
('issuers', {'2A834D8E5918E886', 'A5DCDC966453140E'}),
|
|
||||||
('message', "This is stored, literally\\!\n"),
|
|
||||||
('signers', {'2A834D8E5918E886', 'A5DCDC966453140E'}),
|
|
||||||
('type', 'cleartext'),],
|
|
||||||
'tests/testdata/blocks/message.encrypted.asc':
|
|
||||||
[('encrypters', {'EEE097A017B979CA'}),
|
|
||||||
('is_compressed', False),
|
|
||||||
('is_encrypted', True),
|
|
||||||
('is_signed', False),
|
|
||||||
('issuers', {'EEE097A017B979CA'}),
|
|
||||||
('signers', set()),
|
|
||||||
('type', 'encrypted')],
|
|
||||||
'tests/testdata/blocks/message.encrypted.signed.asc':
|
|
||||||
[('encrypters', {'EEE097A017B979CA'}),
|
|
||||||
('is_compressed', False),
|
|
||||||
('is_encrypted', True),
|
|
||||||
('is_signed', False),
|
|
||||||
('issuers', {'EEE097A017B979CA'}),
|
|
||||||
('signers', set()),
|
|
||||||
('type', 'encrypted')],
|
|
||||||
'tests/testdata/blocks/message.ecc.encrypted.asc':
|
|
||||||
[('encrypters', {'77CEB7A34089AB73'}),
|
|
||||||
('is_compressed', False),
|
|
||||||
('is_encrypted', True),
|
|
||||||
('is_signed', False),
|
|
||||||
('issuers', {'77CEB7A34089AB73'}),
|
|
||||||
('signers', set()),
|
|
||||||
('type', 'encrypted')],
|
|
||||||
|
|
||||||
'tests/testdata/blocks/revochiio.asc':
|
|
||||||
[('created', datetime(2014, 9, 11, 22, 55, 53)),
|
|
||||||
('fingerprint', "AE15 9FF3 4C1A 2426 B7F8 0F1A 560C F308 EF60 CFA3"),
|
|
||||||
('expires_at', datetime(2018, 9, 12, 1, 0, 59)),
|
|
||||||
('is_expired', False),
|
|
||||||
('is_primary', True),
|
|
||||||
('is_protected', False),
|
|
||||||
('is_public', True),
|
|
||||||
('is_unlocked', True),
|
|
||||||
('key_algorithm', PubKeyAlgorithm.RSAEncryptOrSign),
|
|
||||||
('magic', "PUBLIC KEY BLOCK"),
|
|
||||||
('parent', None),
|
|
||||||
('signers', {'560CF308EF60CFA3'}),],
|
|
||||||
'tests/testdata/blocks/expyro.asc':
|
|
||||||
[('created', datetime(1970, 1, 1)),
|
|
||||||
('expires_at', datetime(1970, 1, 2)),
|
|
||||||
('fingerprint', '24EB C1B0 29B1 FCF8 29A5 C150 1A48 291A FB91 A533'),
|
|
||||||
('is_expired', True),],
|
|
||||||
'tests/testdata/blocks/rsapubkey.asc':
|
|
||||||
[('created', datetime(2014, 7, 23, 21, 19, 24)),
|
|
||||||
('expires_at', None),
|
|
||||||
('fingerprint', "F429 4BC8 094A 7E05 85C8 5E86 3747 3B37 58C4 4F36"),
|
|
||||||
('is_expired', False),
|
|
||||||
('is_primary', True),
|
|
||||||
('is_protected', False),
|
|
||||||
('is_public', True),
|
|
||||||
('is_unlocked', True),
|
|
||||||
('key_algorithm', PubKeyAlgorithm.RSAEncryptOrSign),
|
|
||||||
('magic', "PUBLIC KEY BLOCK"),
|
|
||||||
('parent', None),
|
|
||||||
('signers', set()),],
|
|
||||||
'tests/testdata/blocks/rsaseckey.asc':
|
|
||||||
[('created', datetime(2014, 7, 23, 21, 19, 24)),
|
|
||||||
('expires_at', None),
|
|
||||||
('fingerprint', "F429 4BC8 094A 7E05 85C8 5E86 3747 3B37 58C4 4F36"),
|
|
||||||
('is_expired', False),
|
|
||||||
('is_primary', True),
|
|
||||||
('is_protected', False),
|
|
||||||
('is_public', False),
|
|
||||||
('is_unlocked', True),
|
|
||||||
('key_algorithm', PubKeyAlgorithm.RSAEncryptOrSign),
|
|
||||||
('magic', "PRIVATE KEY BLOCK"),
|
|
||||||
('parent', None),
|
|
||||||
('signers', set()),],
|
|
||||||
'tests/testdata/blocks/rsasignature.asc':
|
|
||||||
[('__sig__', b'\x70\x38\x79\xd0\x58\x70\x58\x7b\x50\xe6\xab\x8f\x9d\xc3\x46\x2c\x5a\x6b\x98\x96\xcf'
|
|
||||||
b'\x3b\xa3\x79\x13\x08\x6d\x90\x9d\x67\xd2\x48\x7d\xd7\x1a\xa5\x98\xa7\x8f\xca\xe3\x24'
|
|
||||||
b'\xd4\x19\xab\xe5\x45\xc5\xff\x21\x0c\x72\x88\x91\xe6\x67\xd7\xe5\x00\xb3\xf5\x55\x0b'
|
|
||||||
b'\xd0\xaf\x77\xb3\x7e\xa4\x79\x59\x06\xa2\x05\x44\x9d\xd2\xa9\xcf\xb1\xf8\x03\xc1\x90'
|
|
||||||
b'\x81\x87\x36\x1a\xa6\x5c\x79\x98\xfe\xdb\xdd\x23\x54\x69\x92\x2f\x0b\xc4\xee\x2a\x61'
|
|
||||||
b'\x77\x35\x59\x6e\xb2\xe2\x1b\x80\x61\xaf\x2d\x7a\x64\x38\xfe\xe3\x95\xcc\xe8\xa4\x05'
|
|
||||||
b'\x55\x5d'),
|
|
||||||
('cipherprefs', []),
|
|
||||||
('compprefs', []),
|
|
||||||
('created', datetime.utcfromtimestamp(1402615373)),
|
|
||||||
('embedded', False),
|
|
||||||
('exportable', True),
|
|
||||||
('features', set()),
|
|
||||||
('hash2', b'\xc4\x24'),
|
|
||||||
('hashprefs', []),
|
|
||||||
('hash_algorithm', HashAlgorithm.SHA512),
|
|
||||||
('is_expired', False),
|
|
||||||
('key_algorithm', PubKeyAlgorithm.RSAEncryptOrSign),
|
|
||||||
('key_flags', set()),
|
|
||||||
('keyserver', ''),
|
|
||||||
('keyserverprefs', []),
|
|
||||||
('magic', "SIGNATURE"),
|
|
||||||
('notation', {}),
|
|
||||||
('policy_uri', ''),
|
|
||||||
('revocable', True),
|
|
||||||
('revocation_key', None),
|
|
||||||
('signer', 'FCAE54F74BA27CF7'),
|
|
||||||
('type', SignatureType.BinaryDocument)],
|
|
||||||
'tests/testdata/blocks/eccpubkey.asc':
|
|
||||||
[('created', datetime(2010, 9, 17, 20, 33, 49)),
|
|
||||||
('expires_at', None),
|
|
||||||
('fingerprint', "502D 1A53 65D1 C0CA A699 4539 0BA5 2DF0 BAA5 9D9C"),
|
|
||||||
('is_expired', False),
|
|
||||||
('is_primary', True),
|
|
||||||
('is_protected', False),
|
|
||||||
('is_public', True),
|
|
||||||
('is_unlocked', True),
|
|
||||||
('key_algorithm', PubKeyAlgorithm.ECDSA),
|
|
||||||
('magic', "PUBLIC KEY BLOCK"),
|
|
||||||
('parent', None),
|
|
||||||
('signers', set()),],
|
|
||||||
'tests/testdata/blocks/eccseckey.asc':
|
|
||||||
[('created', datetime(2010, 9, 17, 20, 33, 49)),
|
|
||||||
('expires_at', None),
|
|
||||||
('fingerprint', "502D 1A53 65D1 C0CA A699 4539 0BA5 2DF0 BAA5 9D9C"),
|
|
||||||
('is_expired', False),
|
|
||||||
('is_primary', True),
|
|
||||||
('is_protected', True),
|
|
||||||
('is_public', False),
|
|
||||||
('is_unlocked', False),
|
|
||||||
('key_algorithm', PubKeyAlgorithm.ECDSA),
|
|
||||||
('magic', "PRIVATE KEY BLOCK"),
|
|
||||||
('parent', None),
|
|
||||||
('signers', set()),],
|
|
||||||
'tests/testdata/blocks/signature.expired.asc':
|
|
||||||
[('created', datetime(2014, 9, 28, 20, 54, 42)),
|
|
||||||
('is_expired', True),],
|
|
||||||
}
|
|
||||||
def test_load(self, block):
|
|
||||||
with open(block) as bf:
|
with open(block) as bf:
|
||||||
bc = bf.read()
|
bc = bf.read()
|
||||||
|
|
||||||
@@ -238,7 +312,7 @@ class TestBlocks(object):
|
|||||||
p = PGPMessage()
|
p = PGPMessage()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
pytest.skip("not ready for this one")
|
pytest.skip("not ready for file '{}'".format(os.path.basename(block)))
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
# load ASCII
|
# load ASCII
|
||||||
@@ -248,8 +322,8 @@ class TestBlocks(object):
|
|||||||
# assert str(p) == bc
|
# assert str(p) == bc
|
||||||
|
|
||||||
# now check attrs
|
# now check attrs
|
||||||
assert block in self.attrs
|
assert block in block_attrs
|
||||||
for attr, val in self.attrs[block]:
|
for attr, val in block_attrs[block]:
|
||||||
attrval = getattr(p, attr)
|
attrval = getattr(p, attr)
|
||||||
# this is probably more helpful than just 'assert attrval == val'
|
# this is probably more helpful than just 'assert attrval == val'
|
||||||
if attrval != val:
|
if attrval != val:
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
""" test the functionality of PGPKeyring
|
""" test the functionality of PGPKeyring
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from pgpy.packet import Packet
|
|
||||||
|
|
||||||
from pgpy import PGPKey
|
from pgpy import PGPKey
|
||||||
from pgpy import PGPKeyring
|
from pgpy import PGPKeyring
|
||||||
from pgpy import PGPMessage
|
from pgpy import PGPMessage
|
||||||
@@ -16,25 +12,8 @@ from pgpy import PGPSignature
|
|||||||
from pgpy import PGPUID
|
from pgpy import PGPUID
|
||||||
from pgpy.types import Fingerprint
|
from pgpy.types import Fingerprint
|
||||||
|
|
||||||
@pytest.fixture
|
from conftest import gpg_ver
|
||||||
def un():
|
|
||||||
return PGPUID.new(six.u('Temperair\xe9e Youx\'seur'))
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def unc():
|
|
||||||
return PGPUID.new(six.u('Temperair\xe9e Youx\'seur'), comment=six.u('\u2603'))
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def une():
|
|
||||||
return PGPUID.new(six.u('Temperair\xe9e Youx\'seur'), email='snowman@not.an.email.addre.ss')
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def unce():
|
|
||||||
return PGPUID.new(six.u('Temperair\xe9e Youx\'seur'), comment=six.u('\u2603'), email='snowman@not.an.email.addre.ss')
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def abe():
|
|
||||||
return PGPUID.new('Abraham Lincoln', comment='Honest Abe', email='abraham.lincoln@whitehouse.gov')
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def abe_image():
|
def abe_image():
|
||||||
@@ -45,14 +24,11 @@ def abe_image():
|
|||||||
return PGPUID.new(abebytes)
|
return PGPUID.new(abebytes)
|
||||||
|
|
||||||
|
|
||||||
class TestPGPMessage(object):
|
_msgfiles = sorted(glob.glob('tests/testdata/messages/*.asc'))
|
||||||
params = {
|
|
||||||
'msgfile': sorted(glob.glob('tests/testdata/messages/*.asc')),
|
|
||||||
}
|
|
||||||
ids = {
|
|
||||||
'test_load_from_file': [ os.path.basename(f) for f in params['msgfile'] ],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
class TestPGPMessage(object):
|
||||||
|
@pytest.mark.parametrize('msgfile', _msgfiles, ids=[os.path.basename(f) for f in _msgfiles])
|
||||||
def test_load_from_file(self, msgfile):
|
def test_load_from_file(self, msgfile):
|
||||||
# TODO: figure out a good way to verify that all went well here, because
|
# TODO: figure out a good way to verify that all went well here, because
|
||||||
# PGPy reorders signatures sometimes, and also unwraps compressed messages
|
# PGPy reorders signatures sometimes, and also unwraps compressed messages
|
||||||
@@ -65,6 +41,31 @@ class TestPGPMessage(object):
|
|||||||
assert len(str(msg)) == len(mt)
|
assert len(str(msg)) == len(mt)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def un():
|
||||||
|
return PGPUID.new(six.u('Temperair\xe9e Youx\'seur'))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def unc():
|
||||||
|
return PGPUID.new(six.u('Temperair\xe9e Youx\'seur'), comment=six.u('\u2603'))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def une():
|
||||||
|
return PGPUID.new(six.u('Temperair\xe9e Youx\'seur'), email='snowman@not.an.email.addre.ss')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def unce():
|
||||||
|
return PGPUID.new(six.u('Temperair\xe9e Youx\'seur'), comment=six.u('\u2603'), email='snowman@not.an.email.addre.ss')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def abe():
|
||||||
|
return PGPUID.new('Abraham Lincoln', comment='Honest Abe', email='abraham.lincoln@whitehouse.gov')
|
||||||
|
|
||||||
|
|
||||||
class TestPGPUID(object):
|
class TestPGPUID(object):
|
||||||
def test_userid(self, abe):
|
def test_userid(self, abe):
|
||||||
assert abe.name == 'Abraham Lincoln'
|
assert abe.name == 'Abraham Lincoln'
|
||||||
@@ -88,36 +89,72 @@ class TestPGPUID(object):
|
|||||||
assert six.u("{:s}").format(unce) == six.u('Temperair\xe9e Youx\'seur (\u2603) <snowman@not.an.email.addre.ss>')
|
assert six.u("{:s}").format(unce) == six.u('Temperair\xe9e Youx\'seur (\u2603) <snowman@not.an.email.addre.ss>')
|
||||||
|
|
||||||
|
|
||||||
|
_keyfiles = sorted(glob.glob('tests/testdata/blocks/*key*.asc'))
|
||||||
|
|
||||||
|
|
||||||
class TestPGPKey(object):
|
class TestPGPKey(object):
|
||||||
kf = next(iter(sorted(glob.glob('tests/testdata/keys/*.pub.asc'))))
|
@pytest.mark.parametrize('kf', _keyfiles, ids=[os.path.basename(f) for f in _keyfiles])
|
||||||
|
def test_load_from_file(self, kf, gpg_keyid_file):
|
||||||
|
key, _ = PGPKey.from_file(kf)
|
||||||
|
|
||||||
def test_load_from_file(self, gpg_keyid_file):
|
# TODO: maybe store the fingerprint instead of relying on a particular version of GnuPG...?
|
||||||
key, _ = PGPKey.from_file(self.kf)
|
if 'ecc' in kf and gpg_ver < '2.1':
|
||||||
|
assert key.fingerprint
|
||||||
|
|
||||||
assert key.fingerprint.keyid in gpg_keyid_file(self.kf.replace('tests/testdata/', ''))
|
else:
|
||||||
|
assert key.fingerprint.keyid in gpg_keyid_file(kf.replace('tests/testdata/', ''))
|
||||||
|
|
||||||
def test_load_from_str(self, gpg_keyid_file):
|
@pytest.mark.parametrize('kf', _keyfiles, ids=[os.path.basename(f) for f in _keyfiles])
|
||||||
with open(self.kf, 'r') as tkf:
|
def test_load_from_str(self, kf, gpg_keyid_file):
|
||||||
|
with open(kf, 'r') as tkf:
|
||||||
key, _ = PGPKey.from_blob(tkf.read())
|
key, _ = PGPKey.from_blob(tkf.read())
|
||||||
|
|
||||||
assert key.fingerprint.keyid in gpg_keyid_file(self.kf.replace('tests/testdata/', ''))
|
# TODO: maybe store the fingerprint instead of relying on a particular version of GnuPG...?
|
||||||
|
if 'ecc' in kf and gpg_ver < '2.1':
|
||||||
|
assert key.fingerprint
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert key.fingerprint.keyid in gpg_keyid_file(kf.replace('tests/testdata/', ''))
|
||||||
|
|
||||||
@pytest.mark.regression(issue=140)
|
@pytest.mark.regression(issue=140)
|
||||||
def test_load_from_bytes(self, gpg_keyid_file):
|
@pytest.mark.parametrize('kf', _keyfiles, ids=[os.path.basename(f) for f in _keyfiles])
|
||||||
with open(self.kf, 'rb') as tkf:
|
def test_load_from_bytes(self, kf, gpg_keyid_file):
|
||||||
|
with open(kf, 'rb') as tkf:
|
||||||
key, _ = PGPKey.from_blob(tkf.read())
|
key, _ = PGPKey.from_blob(tkf.read())
|
||||||
|
|
||||||
assert key.fingerprint.keyid in gpg_keyid_file(self.kf.replace('tests/testdata/', ''))
|
# TODO: maybe store the fingerprint instead of relying on a particular version of GnuPG...?
|
||||||
|
if 'ecc' in kf and gpg_ver < '2.1':
|
||||||
|
assert key.fingerprint
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert key.fingerprint.keyid in gpg_keyid_file(kf.replace('tests/testdata/', ''))
|
||||||
|
|
||||||
@pytest.mark.regression(issue=140)
|
@pytest.mark.regression(issue=140)
|
||||||
def test_load_from_bytearray(self, gpg_keyid_file):
|
@pytest.mark.parametrize('kf', _keyfiles, ids=[os.path.basename(f) for f in _keyfiles])
|
||||||
tkb = bytearray(os.stat(self.kf).st_size)
|
def test_load_from_bytearray(self, kf, gpg_keyid_file):
|
||||||
with open(self.kf, 'rb') as tkf:
|
tkb = bytearray(os.stat(kf).st_size)
|
||||||
|
with open(kf, 'rb') as tkf:
|
||||||
tkf.readinto(tkb)
|
tkf.readinto(tkb)
|
||||||
|
|
||||||
key, _ = PGPKey.from_blob(tkb)
|
key, _ = PGPKey.from_blob(tkb)
|
||||||
|
|
||||||
assert key.fingerprint.keyid in gpg_keyid_file(self.kf.replace('tests/testdata/', ''))
|
# TODO: maybe store the fingerprint instead of relying on a particular version of GnuPG...?
|
||||||
|
if 'ecc' in kf and gpg_ver < '2.1':
|
||||||
|
assert key.fingerprint
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert key.fingerprint.keyid in gpg_keyid_file(kf.replace('tests/testdata/', ''))
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('kf', sorted(filter(lambda f: not f.endswith('enc.asc'), glob.glob('tests/testdata/keys/*.asc'))))
|
||||||
|
def test_save(self, kf):
|
||||||
|
# load the key and export it back to binary
|
||||||
|
key, _ = PGPKey.from_file(kf)
|
||||||
|
pgpyblob = key.__bytes__()
|
||||||
|
|
||||||
|
# try loading the exported key
|
||||||
|
reloaded, _ = PGPKey.from_file(kf)
|
||||||
|
|
||||||
|
assert pgpyblob == reloaded.__bytes__()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='module')
|
@pytest.fixture(scope='module')
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import copy
|
|||||||
import glob
|
import glob
|
||||||
import inspect
|
import inspect
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
import pgpy
|
import pgpy
|
||||||
@@ -15,6 +14,10 @@ import pgpy
|
|||||||
from pgpy import PGPSignature, PGPUID, PGPMessage, PGPKey
|
from pgpy import PGPSignature, PGPUID, PGPMessage, PGPKey
|
||||||
|
|
||||||
|
|
||||||
|
_keys = glob.glob('tests/testdata/keys/*.1.pub.asc') + glob.glob('tests/testdata/keys/*.1.sec.asc')
|
||||||
|
_msgs = [ 'tests/testdata/messages/message.{}.asc'.format(f) for f in ['signed', 'rsa.cast5.no-mdc', 'rsa.dsa.pass.aes']]
|
||||||
|
|
||||||
|
|
||||||
def sig():
|
def sig():
|
||||||
return PGPSignature.from_file('tests/testdata/blocks/rsasignature.asc')
|
return PGPSignature.from_file('tests/testdata/blocks/rsasignature.asc')
|
||||||
|
|
||||||
@@ -42,65 +45,58 @@ def walk_obj(obj, prefix=""):
|
|||||||
yield n, v
|
yield n, v
|
||||||
|
|
||||||
|
|
||||||
_keys = glob.glob('tests/testdata/keys/*.1.pub.asc') + glob.glob('tests/testdata/keys/*.1.sec.asc')
|
def check_id(obj):
|
||||||
_msgs = [ 'tests/testdata/messages/message.{}.asc'.format(f) for f in ['signed', 'rsa.cast5.no-mdc', 'rsa.dsa.pass.aes']]
|
from datetime import datetime
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
# do some type checking to determine if we should check the identity of an object member
|
||||||
|
# these types are singletons
|
||||||
|
if isinstance(obj, (Enum, bool, type(None))):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# these types are immutable
|
||||||
|
if isinstance(obj, (six.string_types, datetime)):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# integers are kind of a special case.
|
||||||
|
# ints that do not exceed sys.maxsize are singletons, and in either case are immutable
|
||||||
|
# this shouldn't apply to MPIs, though, which are subclasses of int
|
||||||
|
if isinstance(obj, int) and not isinstance(obj, pgpy.packet.types.MPI):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class TestCopy(object):
|
def ksort(key):
|
||||||
params = {
|
# return a tuple of key, key.count('.') so we get a descending alphabetical, ascending depth ordering
|
||||||
'obj': [sig(), uid()] + [ PGPMessage.from_file(m) for m in _msgs ] + [ key(fn) for fn in _keys ],
|
return key, key.count('.')
|
||||||
}
|
|
||||||
ids = {
|
|
||||||
'test_copy_obj': ['sig' , 'uid'] + [ '-'.join(os.path.basename(fn).split('.')[:3]) for fn in _msgs ] + [ '-'.join(os.path.basename(fn).split('.')[:3]) for fn in _keys ],
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check_id(obj):
|
|
||||||
from datetime import datetime
|
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
# do some type checking to determine if we should check the identity of an object member
|
objs = [sig(), uid(),] + [PGPMessage.from_file(m) for m in _msgs] + [key(f) for f in _keys]
|
||||||
# these types are singletons
|
cids = ['sig', 'uid',] + [os.path.basename(m) for m in _msgs] + [os.path.basename(f) for f in _keys]
|
||||||
if isinstance(obj, (Enum, bool, type(None))):
|
|
||||||
return False
|
|
||||||
|
|
||||||
# these types are immutable
|
|
||||||
if isinstance(obj, (six.string_types, datetime)):
|
|
||||||
return False
|
|
||||||
|
|
||||||
# integers are kind of a special case.
|
@pytest.mark.parametrize('obj', objs, ids=cids)
|
||||||
# ints that do not exceed sys.maxsize are singletons, and in either case are immutable
|
def test_copy_obj(request, obj):
|
||||||
# this shouldn't apply to MPIs, though, which are subclasses of int
|
obj2 = copy.copy(obj)
|
||||||
if isinstance(obj, int) and not isinstance(obj, pgpy.packet.types.MPI):
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
objflat = {name: val for name, val in walk_obj(obj, '{}.'.format(request.node.callspec.id))}
|
||||||
|
obj2flat = {name: val for name, val in walk_obj(obj2, '{}.'.format(request.node.callspec.id))}
|
||||||
|
|
||||||
@staticmethod
|
for k in sorted(objflat, key=ksort):
|
||||||
def ksort(key):
|
print("checking attribute: {} ".format(k), end="")
|
||||||
# return a tuple of key, key.count('.') so we get a descending alphabetical, ascending depth ordering
|
if isinstance(objflat[k], pgpy.types.SorteDeque):
|
||||||
return key, key.count('.')
|
print("[SorteDeque] ", end="")
|
||||||
|
assert len(objflat[k]) == len(obj2flat[k])
|
||||||
|
|
||||||
def test_copy_obj(self, request, obj):
|
if not isinstance(objflat[k], (pgpy.types.PGPObject, pgpy.types.SorteDeque)):
|
||||||
obj2 = copy.copy(obj)
|
print("[{} ]".format(type(objflat[k])), end="")
|
||||||
|
assert objflat[k] == objflat[k], k
|
||||||
|
|
||||||
objflat = {name: val for name, val in walk_obj(obj, '{}.'.format(request.node.callspec.id))}
|
# check identity, but only types that should definitely be copied
|
||||||
obj2flat = {name: val for name, val in walk_obj(obj2, '{}.'.format(request.node.callspec.id))}
|
if check_id(objflat[k]):
|
||||||
|
print("[id] {}".format(type(objflat[k])))
|
||||||
|
assert objflat[k] is not obj2flat[k], "{}: {}".format(type(objflat[k]), k)
|
||||||
|
|
||||||
for k in sorted(objflat, key=self.ksort):
|
else:
|
||||||
print("checking attribute: {} ".format(k), end="")
|
print()
|
||||||
if isinstance(objflat[k], pgpy.types.SorteDeque):
|
|
||||||
print("[SorteDeque] ", end="")
|
|
||||||
assert len(objflat[k]) == len(obj2flat[k])
|
|
||||||
|
|
||||||
if not isinstance(objflat[k], (pgpy.types.PGPObject, pgpy.types.SorteDeque)):
|
|
||||||
print("[{} ]".format(type(objflat[k])), end="")
|
|
||||||
assert objflat[k] == objflat[k], k
|
|
||||||
|
|
||||||
# check identity, but only types that should definitely be copied
|
|
||||||
if self.check_id(objflat[k]):
|
|
||||||
print("[id] {}".format(type(objflat[k])))
|
|
||||||
assert objflat[k] is not obj2flat[k], "{}: {}".format(type(objflat[k]), k)
|
|
||||||
|
|
||||||
else:
|
|
||||||
print()
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,29 +3,24 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
|
|
||||||
from pgpy import PGPKey
|
from pgpy import PGPKey
|
||||||
from pgpy import PGPKeyring
|
from pgpy import PGPKeyring
|
||||||
from pgpy import PGPMessage
|
from pgpy import PGPMessage
|
||||||
from pgpy import PGPSignature
|
from pgpy import PGPSignature
|
||||||
from pgpy import PGPUID
|
from pgpy import PGPUID
|
||||||
|
|
||||||
from pgpy.packet import Packet
|
|
||||||
|
|
||||||
from pgpy.types import Armorable
|
|
||||||
from pgpy.types import PGPObject
|
|
||||||
from pgpy.types import Fingerprint
|
|
||||||
from pgpy.types import SignatureVerification
|
|
||||||
|
|
||||||
from pgpy.constants import EllipticCurveOID
|
from pgpy.constants import EllipticCurveOID
|
||||||
from pgpy.constants import HashAlgorithm
|
from pgpy.constants import HashAlgorithm
|
||||||
from pgpy.constants import KeyFlags
|
from pgpy.constants import KeyFlags
|
||||||
from pgpy.constants import PubKeyAlgorithm
|
from pgpy.constants import PubKeyAlgorithm
|
||||||
from pgpy.constants import SymmetricKeyAlgorithm
|
from pgpy.constants import SymmetricKeyAlgorithm
|
||||||
|
from pgpy.packet import Packet
|
||||||
|
from pgpy.types import Armorable
|
||||||
|
from pgpy.types import PGPObject
|
||||||
|
from pgpy.types import Fingerprint
|
||||||
|
from pgpy.types import SignatureVerification
|
||||||
|
from pgpy.errors import PGPError
|
||||||
from pgpy.errors import PGPDecryptionError
|
from pgpy.errors import PGPDecryptionError
|
||||||
from pgpy.errors import PGPEncryptionError
|
from pgpy.errors import PGPEncryptionError
|
||||||
from pgpy.errors import PGPError
|
|
||||||
from pgpy.errors import PGPInsecureCipher
|
from pgpy.errors import PGPInsecureCipher
|
||||||
|
|
||||||
|
|
||||||
@@ -63,6 +58,7 @@ def targette_pub():
|
|||||||
def temp_subkey():
|
def temp_subkey():
|
||||||
return PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 512)
|
return PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 512)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='module')
|
@pytest.fixture(scope='module')
|
||||||
def temp_key():
|
def temp_key():
|
||||||
u = PGPUID.new('User')
|
u = PGPUID.new('User')
|
||||||
@@ -85,6 +81,7 @@ key_algs_badsizes = {
|
|||||||
PubKeyAlgorithm.ECDSA: [curve for curve in EllipticCurveOID if not curve.can_gen],
|
PubKeyAlgorithm.ECDSA: [curve for curve in EllipticCurveOID if not curve.can_gen],
|
||||||
PubKeyAlgorithm.ECDH: [curve for curve in EllipticCurveOID if not curve.can_gen],
|
PubKeyAlgorithm.ECDH: [curve for curve in EllipticCurveOID if not curve.can_gen],
|
||||||
}
|
}
|
||||||
|
badkeyspec = [ (alg, size) for alg in key_algs_badsizes.keys() for size in key_algs_badsizes[alg] ]
|
||||||
|
|
||||||
|
|
||||||
class TestArmorable(object):
|
class TestArmorable(object):
|
||||||
@@ -133,7 +130,6 @@ class TestMetaDispatchable(object):
|
|||||||
else:
|
else:
|
||||||
Packet(d)
|
Packet(d)
|
||||||
|
|
||||||
|
|
||||||
# ensure the base packet works, first
|
# ensure the base packet works, first
|
||||||
Packet(data[:])
|
Packet(data[:])
|
||||||
|
|
||||||
@@ -150,25 +146,6 @@ class TestMetaDispatchable(object):
|
|||||||
|
|
||||||
|
|
||||||
class TestPGPKey(object):
|
class TestPGPKey(object):
|
||||||
params = {
|
|
||||||
# 'key_alg': key_algs,
|
|
||||||
'badkey': [ (alg, size) for alg in key_algs_badsizes.keys() for size in key_algs_badsizes[alg] ],
|
|
||||||
'key_alg_unim': key_algs_unim,
|
|
||||||
'key_alg_rsa_depr': key_algs_rsa_depr,
|
|
||||||
}
|
|
||||||
ids = {
|
|
||||||
# 'test_new_key_invalid_size': [ str(ka).split('.')[-1] for ka in key_algs ],
|
|
||||||
'test_new_key_invalid_size': [ '{}-{}'.format(ka.name, ks.name if not isinstance(ks, int) else ks) for ka, kss in key_algs_badsizes.items() for ks in kss],
|
|
||||||
'test_new_key_unimplemented_alg': [ str(ka).split('.')[-1] for ka in key_algs_unim ],
|
|
||||||
'test_new_key_deprecated_rsa_alg': [ str(ka).split('.')[-1] for ka in key_algs_rsa_depr ],
|
|
||||||
}
|
|
||||||
key_badsize = {
|
|
||||||
PubKeyAlgorithm.RSAEncryptOrSign: 256,
|
|
||||||
PubKeyAlgorithm.DSA: 512,
|
|
||||||
PubKeyAlgorithm.ECDSA: 1,
|
|
||||||
PubKeyAlgorithm.ECDH: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
def test_unlock_pubkey(self, rsa_pub, recwarn):
|
def test_unlock_pubkey(self, rsa_pub, recwarn):
|
||||||
with rsa_pub.unlock("QwertyUiop") as _unlocked:
|
with rsa_pub.unlock("QwertyUiop") as _unlocked:
|
||||||
assert _unlocked is rsa_pub
|
assert _unlocked is rsa_pub
|
||||||
@@ -304,15 +281,18 @@ class TestPGPKey(object):
|
|||||||
with pytest.raises(PGPError):
|
with pytest.raises(PGPError):
|
||||||
key.sign('asdf')
|
key.sign('asdf')
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('badkey', badkeyspec, ids=['{}:{}'.format(alg.name, size if isinstance(size, int) else size.name) for alg, size in badkeyspec])
|
||||||
def test_new_key_invalid_size(self, badkey):
|
def test_new_key_invalid_size(self, badkey):
|
||||||
key_alg, key_size = badkey
|
key_alg, key_size = badkey
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
PGPKey.new(key_alg, key_size)
|
PGPKey.new(key_alg, key_size)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('key_alg_unim', key_algs_unim, ids=[alg.name for alg in key_algs_unim])
|
||||||
def test_new_key_unimplemented_alg(self, key_alg_unim):
|
def test_new_key_unimplemented_alg(self, key_alg_unim):
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
PGPKey.new(key_alg_unim, 512)
|
PGPKey.new(key_alg_unim, 512)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('key_alg_rsa_depr', key_algs_rsa_depr, ids=[alg.name for alg in key_algs_rsa_depr])
|
||||||
def test_new_key_deprecated_rsa_alg(self, key_alg_rsa_depr, recwarn):
|
def test_new_key_deprecated_rsa_alg(self, key_alg_rsa_depr, recwarn):
|
||||||
k = PGPKey.new(key_alg_rsa_depr, 512)
|
k = PGPKey.new(key_alg_rsa_depr, 512)
|
||||||
|
|
||||||
@@ -405,9 +385,10 @@ class TestPGPMessage(object):
|
|||||||
|
|
||||||
|
|
||||||
class TestPGPSignature(object):
|
class TestPGPSignature(object):
|
||||||
def test_or_typeerror(self):
|
@pytest.mark.parametrize('inp', [12, None])
|
||||||
|
def test_or_typeerror(self, inp):
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
PGPSignature() | 12
|
PGPSignature() | inp
|
||||||
|
|
||||||
def test_parse_wrong_magic(self):
|
def test_parse_wrong_magic(self):
|
||||||
sigtext = _read('tests/testdata/blocks/signature.expired.asc').replace('SIGNATURE', 'SIGANTURE')
|
sigtext = _read('tests/testdata/blocks/signature.expired.asc').replace('SIGNATURE', 'SIGANTURE')
|
||||||
@@ -421,12 +402,6 @@ class TestPGPSignature(object):
|
|||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
sig.parse(notsigtext)
|
sig.parse(notsigtext)
|
||||||
|
|
||||||
def test_or_typeerror(self):
|
|
||||||
sig = PGPSignature()
|
|
||||||
|
|
||||||
with pytest.raises(TypeError):
|
|
||||||
sig |= None
|
|
||||||
|
|
||||||
|
|
||||||
class TestPGPUID(object):
|
class TestPGPUID(object):
|
||||||
def test_or_typeerror(self):
|
def test_or_typeerror(self):
|
||||||
|
|||||||
@@ -1,149 +1,195 @@
|
|||||||
""" I've got 99 problems but regression testing ain't one
|
""" I've got 99 problems but regression testing ain't one
|
||||||
"""
|
"""
|
||||||
|
import pytest
|
||||||
|
import tempfile
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
class TestRegressions(object):
|
@pytest.mark.regression(issue=56)
|
||||||
# regression tests for actions
|
def test_reg_bug_56(gpg_import, gpg_verify):
|
||||||
def test_reg_bug_56(self, write_clean, gpg_import, gpg_verify):
|
# some imports only used by this regression test
|
||||||
# some imports only used by this regression test
|
import hashlib
|
||||||
import hashlib
|
from datetime import datetime
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from pgpy.pgp import PGPKey
|
from pgpy.pgp import PGPKey
|
||||||
from pgpy.pgp import PGPSignature
|
from pgpy.pgp import PGPSignature
|
||||||
|
|
||||||
from pgpy.constants import HashAlgorithm
|
from pgpy.constants import HashAlgorithm
|
||||||
from pgpy.constants import PubKeyAlgorithm
|
from pgpy.constants import PubKeyAlgorithm
|
||||||
from pgpy.constants import SignatureType
|
from pgpy.constants import SignatureType
|
||||||
|
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
from cryptography.hazmat.primitives import hashes
|
from cryptography.hazmat.primitives import hashes
|
||||||
from cryptography.hazmat.primitives.asymmetric import padding
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
|
|
||||||
# do a regression test on issue #56
|
# do a regression test on issue #56
|
||||||
# re-create a signature that would have been encoded improperly as with issue #56
|
# re-create a signature that would have been encoded improperly as with issue #56
|
||||||
# and see if it fails to verify or not
|
# and see if it fails to verify or not
|
||||||
|
|
||||||
# this was the old seckeys/TestRSA-2048.key
|
# this was the old seckeys/TestRSA-2048.key
|
||||||
sec = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" \
|
sec = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" \
|
||||||
"\n" \
|
"\n" \
|
||||||
"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\n" \
|
||||||
"iz54ZFyNdQ==\n" \
|
"iz54ZFyNdQ==\n" \
|
||||||
"=WLpc\n" \
|
"=WLpc\n" \
|
||||||
"-----END PGP PRIVATE KEY BLOCK-----\n"
|
"-----END PGP PRIVATE KEY BLOCK-----\n"
|
||||||
|
|
||||||
pub = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" \
|
pub = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" \
|
||||||
"\n" \
|
"\n" \
|
||||||
"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\n" \
|
||||||
"dQ==\n" \
|
"dQ==\n" \
|
||||||
"=lqIH\n" \
|
"=lqIH\n" \
|
||||||
"-----END PGP PUBLIC KEY BLOCK-----\n"
|
"-----END PGP PUBLIC KEY BLOCK-----\n"
|
||||||
|
|
||||||
# load the keypair above
|
# load the keypair above
|
||||||
sk = PGPKey()
|
sk = PGPKey()
|
||||||
sk.parse(sec)
|
sk.parse(sec)
|
||||||
pk = PGPKey()
|
pk = PGPKey()
|
||||||
pk.parse(pub)
|
pk.parse(pub)
|
||||||
|
|
||||||
sigsubject = bytearray(b"Hello!I'm a test document.I'm going to get signed a bunch of times.KBYE!")
|
sigsubject = bytearray(b"Hello!I'm a test document.I'm going to get signed a bunch of times.KBYE!")
|
||||||
|
|
||||||
sig = PGPSignature.new(SignatureType.BinaryDocument, PubKeyAlgorithm.RSAEncryptOrSign, HashAlgorithm.SHA512,
|
sig = PGPSignature.new(SignatureType.BinaryDocument, PubKeyAlgorithm.RSAEncryptOrSign, HashAlgorithm.SHA512,
|
||||||
sk.fingerprint.keyid)
|
sk.fingerprint.keyid)
|
||||||
sig._signature.subpackets['h_CreationTime'][-1].created = datetime(2014, 8, 6, 23, 28, 51)
|
sig._signature.subpackets['h_CreationTime'][-1].created = datetime(2014, 8, 6, 23, 28, 51)
|
||||||
sig._signature.subpackets.update_hlen()
|
sig._signature.subpackets.update_hlen()
|
||||||
hdata = sig.hashdata(sigsubject)
|
hdata = sig.hashdata(sigsubject)
|
||||||
sig._signature.hash2 = hashlib.new('sha512', hdata).digest()[:2]
|
sig._signature.hash2 = hashlib.new('sha512', hdata).digest()[:2]
|
||||||
|
|
||||||
# create the signature
|
# create the signature
|
||||||
signer = sk.__key__.__privkey__().signer(padding.PKCS1v15(), hashes.SHA512())
|
signer = sk.__key__.__privkey__().signer(padding.PKCS1v15(), hashes.SHA512())
|
||||||
signer.update(hdata)
|
signer.update(hdata)
|
||||||
sig._signature.signature.from_signer(signer.finalize())
|
sig._signature.signature.from_signer(signer.finalize())
|
||||||
sig._signature.update_hlen()
|
sig._signature.update_hlen()
|
||||||
|
|
||||||
# check encoding
|
# check encoding
|
||||||
assert sig._signature.signature.md_mod_n.to_mpibytes()[2:3] != b'\x00'
|
assert sig._signature.signature.md_mod_n.to_mpibytes()[2:3] != b'\x00'
|
||||||
|
|
||||||
# with PGPy
|
# with PGPy
|
||||||
assert pk.verify(sigsubject, sig)
|
assert pk.verify(sigsubject, sig)
|
||||||
|
|
||||||
# with GnuPG
|
# with GnuPG
|
||||||
with write_clean('tests/testdata/subj', 'w', sigsubject.decode('latin-1')), \
|
with tempfile.NamedTemporaryFile('w+') as subjf, \
|
||||||
write_clean('tests/testdata/subj.asc', 'w', str(sig)), \
|
tempfile.NamedTemporaryFile('w+') as sigf, \
|
||||||
write_clean('tests/testdata/pub.asc', 'w', str(pk)), \
|
tempfile.NamedTemporaryFile('w+') as pubf:
|
||||||
gpg_import('pub.asc'):
|
subjf.write(sigsubject.decode('latin-1'))
|
||||||
assert gpg_verify('subj', 'subj.asc')
|
sigf.write(str(sig))
|
||||||
|
pubf.write(str(pk))
|
||||||
|
|
||||||
|
subjf.flush()
|
||||||
|
sigf.flush()
|
||||||
|
pubf.flush()
|
||||||
|
|
||||||
|
with gpg_import(pubf.name):
|
||||||
|
assert gpg_verify(subjf.name, sigf.name)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.regression(issue=157)
|
||||||
|
def test_reg_bug_157(monkeypatch):
|
||||||
|
# local imports for this
|
||||||
|
import pgpy.constants
|
||||||
|
from pgpy.packet.fields import String2Key
|
||||||
|
from time import time as rtime
|
||||||
|
|
||||||
|
# to more easily replicate this bug, hash only 8 bytes instead of 100 KiB
|
||||||
|
monkeypatch.setattr('pgpy.constants._hashtunedata', bytearray([10, 11, 12, 13, 14, 15, 16, 17]))
|
||||||
|
# also monkeypatch time.time to return fewer significant digits
|
||||||
|
monkeypatch.setattr('time.time', lambda: round(rtime(), 3))
|
||||||
|
assert len(pgpy.constants._hashtunedata) == 8
|
||||||
|
|
||||||
|
pgpy.constants.HashAlgorithm.SHA256.tune_count()
|
||||||
|
assert pgpy.constants.HashAlgorithm.SHA256.tuned_count > 0
|
||||||
|
|
||||||
|
# now let's try it out and ensure that the count actually worked
|
||||||
|
s2k = String2Key()
|
||||||
|
s2k.encalg = pgpy.constants.SymmetricKeyAlgorithm.AES256
|
||||||
|
s2k.specifier = pgpy.constants.String2KeyType.Iterated
|
||||||
|
s2k.halg = pgpy.constants.HashAlgorithm.SHA256
|
||||||
|
s2k.count = pgpy.constants.HashAlgorithm.SHA256.tuned_count
|
||||||
|
|
||||||
|
start = rtime()
|
||||||
|
sk = s2k.derive_key('sooper_sekret_passphrase')
|
||||||
|
elapsed = rtime() - start
|
||||||
|
|
||||||
|
# check that we're actually close to our target
|
||||||
|
assert len(sk) == 32
|
||||||
|
try:
|
||||||
|
assert 0.1 <= round(elapsed, 1) <= 0.2
|
||||||
|
|
||||||
|
except AssertionError:
|
||||||
|
warnings.warn("tuned_count: {}; elapsed time: {:.5f}".format(pgpy.constants.HashAlgorithm.SHA256.tuned_count, elapsed))
|
||||||
|
|||||||
24
tests/testdata/blocks/dsapubkey.asc
vendored
Normal file
24
tests/testdata/blocks/dsapubkey.asc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mQGiBFisk0URBACkc86J1iRa6gV0nQvuZve9WkbGkJWT0kopILrB5H0SckXkLwOx
|
||||||
|
kR+T+f/+S1GG+7qB39E0tCNDPEQlm06hfof0sKZ594GSNFDfWvcIYNksxyIJ3dHM
|
||||||
|
obkt7LbrWVqm/QzwhJqnvQEdNPD2tZYmJYlDEFvL3buPfyiczf7LRBVa2wCgmG43
|
||||||
|
XQ2TbbBl3oVnnpaWMRuo3RMD/27hIJ+ww3zXkH9Nd/4vC3jKsMk/DSxR/MKfcbam
|
||||||
|
LxRon409/k3c31LDskBz7secNgscvlTzsWwsib2ACw1Cr3cUWke28kypVAJlwVqt
|
||||||
|
F2OVFjSO9OKjHOwACF/N1tMjulcKHqDRW4EkkNBZpzKwn37UycDbhnxpaeHAjGOe
|
||||||
|
rPyTA/43OSP/P2WJw1hramsfHV9BAS2jd2+qz3dPdhaB2BdLa7OuZVkik2bnMyp8
|
||||||
|
IGtblORz2f1Vuc+0Wp3FL4zsrakN3L7VZwla1Gp+FkNpY3Mgfn7Ts69Pl57Y5+Cm
|
||||||
|
CTRa2lKflL3yM+nf8a/1tCHuOtl/iaQQhLoMkRSpHdrYiIQqzLQeRFNBIHZvbiBU
|
||||||
|
ZXN0S2V5IDxkc2FAdGVzdC5rZXk+iHgEExECADgWIQQrW7sUO6CykNzuZmi3mK6J
|
||||||
|
kIdyAQUCWKyTRQIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRC3mK6JkIdy
|
||||||
|
AdNGAJ9T8hKPVWoG0UJFQo2/6Cr9okRSZACeNxxJ92pOU/LEo9TgtqQqbmk73SK5
|
||||||
|
AQ0EWKyTRRAEAMXlraLYYWCNoMa0s6aeRvmlQHZiXqcB3ECI+hO4EivzisXAQhg1
|
||||||
|
BQPDn6IMCE4pX69Q/z2LOk6aeD1htMymWzGbyzG7jFRX8Q9US9ooul+nvSVf3xVW
|
||||||
|
UjeLGKi8Z3yD2lK8efB/bGxxx7xdNlrk2AyucnHjyc+HOcyKoNA8TAW3AAURA/9D
|
||||||
|
dpz9M1Rsb9vR4QFHh03fcFh46dhFMjT94ShwMVaJSWy0gHNnTp0MmDzQAdBSFJYE
|
||||||
|
rdZV7YCex6HoHfGS7btpEX//YoxL/nsoP73y34PgYmmqYLZ95cCLdn+YuYPhNAQT
|
||||||
|
klCNR4fjtZme83oZLgzd4Hkl5maVixRmA1n8TOB7QYhfBBgRAgAgFiEEK1u7FDug
|
||||||
|
spDc7mZot5iuiZCHcgEFAlisk0UCGwwACgkQt5iuiZCHcgE5wgCbB5EYVFft5QPK
|
||||||
|
KiUE3NMzKCik1pEAmOO+qvoi2g6DG2mmEU5Y56IoiSQ=
|
||||||
|
=wk7s
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
27
tests/testdata/blocks/dsaseckey.asc
vendored
Normal file
27
tests/testdata/blocks/dsaseckey.asc
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
|
||||||
|
lQHpBFisk0URBACkc86J1iRa6gV0nQvuZve9WkbGkJWT0kopILrB5H0SckXkLwOx
|
||||||
|
kR+T+f/+S1GG+7qB39E0tCNDPEQlm06hfof0sKZ594GSNFDfWvcIYNksxyIJ3dHM
|
||||||
|
obkt7LbrWVqm/QzwhJqnvQEdNPD2tZYmJYlDEFvL3buPfyiczf7LRBVa2wCgmG43
|
||||||
|
XQ2TbbBl3oVnnpaWMRuo3RMD/27hIJ+ww3zXkH9Nd/4vC3jKsMk/DSxR/MKfcbam
|
||||||
|
LxRon409/k3c31LDskBz7secNgscvlTzsWwsib2ACw1Cr3cUWke28kypVAJlwVqt
|
||||||
|
F2OVFjSO9OKjHOwACF/N1tMjulcKHqDRW4EkkNBZpzKwn37UycDbhnxpaeHAjGOe
|
||||||
|
rPyTA/43OSP/P2WJw1hramsfHV9BAS2jd2+qz3dPdhaB2BdLa7OuZVkik2bnMyp8
|
||||||
|
IGtblORz2f1Vuc+0Wp3FL4zsrakN3L7VZwla1Gp+FkNpY3Mgfn7Ts69Pl57Y5+Cm
|
||||||
|
CTRa2lKflL3yM+nf8a/1tCHuOtl/iaQQhLoMkRSpHdrYiIQqzP4HAwKXqnH0U/4k
|
||||||
|
KO7U5CKhvfx/ux6nw+SCR7BF68Slo2Emzu1u1ytu3YJul8WsfNW8DIfe43m3LYhf
|
||||||
|
Eqkaw+PS8BnQLqLFtB5EU0Egdm9uIFRlc3RLZXkgPGRzYUB0ZXN0LmtleT6IeAQT
|
||||||
|
EQIAOBYhBCtbuxQ7oLKQ3O5maLeYromQh3IBBQJYrJNFAhsDBQsJCAcCBhUICQoL
|
||||||
|
AgQWAgMBAh4BAheAAAoJELeYromQh3IB00YAn1PyEo9VagbRQkVCjb/oKv2iRFJk
|
||||||
|
AJ43HEn3ak5T8sSj1OC2pCpuaTvdIp0BYARYrJNFEAQAxeWtothhYI2gxrSzpp5G
|
||||||
|
+aVAdmJepwHcQIj6E7gSK/OKxcBCGDUFA8OfogwITilfr1D/PYs6Tpp4PWG0zKZb
|
||||||
|
MZvLMbuMVFfxD1RL2ii6X6e9JV/fFVZSN4sYqLxnfIPaUrx58H9sbHHHvF02WuTY
|
||||||
|
DK5ycePJz4c5zIqg0DxMBbcABRED/0N2nP0zVGxv29HhAUeHTd9wWHjp2EUyNP3h
|
||||||
|
KHAxVolJbLSAc2dOnQyYPNAB0FIUlgSt1lXtgJ7Hoegd8ZLtu2kRf/9ijEv+eyg/
|
||||||
|
vfLfg+Biaapgtn3lwIt2f5i5g+E0BBOSUI1Hh+O1mZ7zehkuDN3geSXmZpWLFGYD
|
||||||
|
WfxM4HtB/gcDAnepsPge3rKk7rHlUAbFtKH0vOu4HJQb5iPRzy6UHbDbQ0lN7qMX
|
||||||
|
pjHwCKQ5/Lmx8T8IIF4Cajzk31Gucb9xUJ0OxvPjCWOQ6D7jEEt9m9SIXwQYEQIA
|
||||||
|
IBYhBCtbuxQ7oLKQ3O5maLeYromQh3IBBQJYrJNFAhsMAAoJELeYromQh3IBOcIA
|
||||||
|
mweRGFRX7eUDyiolBNzTMygopNaRAJjjvqr6ItoOgxtpphFOWOeiKIkk
|
||||||
|
=MJIx
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----
|
||||||
5
tests/testdata/blocks/message.ascii.asc
vendored
Normal file
5
tests/testdata/blocks/message.ascii.asc
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
-----BEGIN PGP MESSAGE-----
|
||||||
|
|
||||||
|
yyd0BWFzY2lpWKyiRVRoaXMgaXMgc3RvcmVkLCB0ZXh0dWFsbHkhDQo=
|
||||||
|
=PmSE
|
||||||
|
-----END PGP MESSAGE-----
|
||||||
34
tests/testdata/blocks/openpgp.js.pubkey.asc
vendored
Normal file
34
tests/testdata/blocks/openpgp.js.pubkey.asc
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Version: OpenPGP.js v2.3.0
|
||||||
|
Comment: http://openpgpjs.org
|
||||||
|
|
||||||
|
xsBNBFdQq7kBB/9T77WfC8wWyfpa2x221JXLBOxan5E9FyTNzCcGxksoykYv
|
||||||
|
b/wwLPA7M2+wn6Am7OnTkbjMyKVb1D43fy1Bermc1EQqRTscIWvVLhrgzFSu
|
||||||
|
O8OUXM49DmrNbxvpEuU7/WUjuFyXcJnI0pVBaNKGH5iyLLP6BC4sOdyIPCLz
|
||||||
|
YQH+FxcWphnx63o1CqzAWIjbGcRNGkQzmLwqNmqS5/f419yTonzUkzm4ptm1
|
||||||
|
/Kpsj65n/I2vjVtvGQCR86M4aYD53s/dN5xeCirAjmbBDta/B2vlOlYFZWs2
|
||||||
|
zTBDv4ER5xC3CvMSp0Fy1jvSAJLCSiAhzYUivhSTk9L5yNEyynHbPj2BABEB
|
||||||
|
AAHNG1QuSy4gPG9wZW5wZ3AuanNAZW1haWwuY29tPsLAdQQQAQgAKQUCV1Cr
|
||||||
|
vQYLCQcIAwIJEBSrRMdNG9q4BBUIAgoDFgIBAhkBAhsDAh4BAAApRQf+Jxn/
|
||||||
|
l3Qd3qOYxjTjdf6Fql807abnRIxbDTfZekv6QiIA3TKDPVVvxSLpL/EF2nvF
|
||||||
|
ErXhxq34ZK4oypp73n9lyoKkmlAS+JLEeFUIISUzdiwgBRAfjQpE19KOImGb
|
||||||
|
7ZGvMMEfnitkn2p/uR+EnGVCjOsmHsUW5Z4eSI/KXC8+Radi55FN5WA65QHl
|
||||||
|
Jed6yXDnKQSCR6V9N6UlEQk/Qeiuh1PnBoie0kS8ct9/YA+ltsK7KGrHWKoi
|
||||||
|
GJC79Ma9ksjhLIkyazACyVK44hYXFAOeSzyCM+/cVAmbLSwfZKWSStdzfj0u
|
||||||
|
oWFkV/RTfZjwNi6BrTViNcYFb7BcACqg0x0Uzc7ATQRXUKu6AQf/YLVawSGz
|
||||||
|
HHcRiA9FzlssfosR0CrQgomha0woYnD3PtOqwy/R3FluNVD336i/kC08IsIt
|
||||||
|
MjjToKoKke/HErDBuyKiCWJmuPTbNvoCPjRJp81V6SUm1QAnjFkhYVzTs3Y6
|
||||||
|
1O/xIP2BI7yrSVr2v8wSwrQI7f4EktbrLq4w1gOKxiYZcj3zia4BUwmBsa7O
|
||||||
|
yOKnwTVrpkvMnQE4Ulkkj853V7QiBTUJaveZoSowfUQzXzTcgkWUQO04PjYR
|
||||||
|
HjUyldGWKny8UwlSxHrNo41rG7JACd8eE5esKVpX/jcwu/820rDqbo1jkhFB
|
||||||
|
cWSzRh/X2nTUClQud+SpxFE5z8h7A7bDUQARAQABwsBfBBgBCAATBQJXUKu9
|
||||||
|
CRAUq0THTRvauAIbDAAAMAUH/0j5fQZd3R3+zxR19nUJzkbbgH+PmUoiK5En
|
||||||
|
z4LzdxT9Tj1D1mDvdKbRqxLehOWlWAJSP0mRpwrs5GOYHYqC+FuCZKeMZi2+
|
||||||
|
W5RYr8YdMMwn3p5mPO2WvEOWuZLyO2hEDFsF33Jt+w08MkgH/MSDUg/UQZIY
|
||||||
|
ERiuen4Fe1Xq2FVLxEagwRMF34KXgVfUpCRkeHWjiLpK1ne74Q3+O4cy2H1v
|
||||||
|
Y1ld+OHKP78dP+Y53h5RodrOJ3bkBddZk2hmx/g7ltVFzyya9/gs+dPdaQpL
|
||||||
|
11I1n1TWjZ7S+xjcNt8q9gfmSRVYcugt/Vwi+ekg3oNZvog4/kyNW7ypvqA1
|
||||||
|
gWGghBg=
|
||||||
|
=/Uz1
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
62
tests/testdata/blocks/openpgp.js.seckey.asc
vendored
Normal file
62
tests/testdata/blocks/openpgp.js.seckey.asc
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
Version: OpenPGP.js v2.3.0
|
||||||
|
Comment: http://openpgpjs.org
|
||||||
|
|
||||||
|
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
|
||||||
|
hBg=
|
||||||
|
=w1XY
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----
|
||||||
12
tests/testdata/blocks/signature.non-exportable.asc
vendored
Normal file
12
tests/testdata/blocks/signature.non-exportable.asc
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
Version: PGPy v0.5.0
|
||||||
|
|
||||||
|
iQE2BBABCAAgFiEE9ClLyAlKfgWFyF6GN0c7N1jETzYFAlispnYCBAAACgkQN0c7
|
||||||
|
N1jETzbkhgf/cMR8dJObANItbQD6V96zoC/fSPSntE7XyVGNEl3efGY8CMIx0a9n
|
||||||
|
y+pOLgnKQcYdUi39U4U0KllnmulV4Z3HJvn6OqA77iQKdpQ3zEhWNA0/5QWIZja5
|
||||||
|
uI1zRSITfr5KMYJPni+ET09bsYkT8RB+yw5ZOUY6hFyZhuMmk2+FqF2ZS2xUf8yY
|
||||||
|
aQc6twqRo6tEck6c9zI5fO/MJQE7Zn55ijZp6Qk1yYDFfn3nq0oVp/lBkPXWiM5R
|
||||||
|
OMDrOYMqAJIliBxcab33dGU/e+afm4vNdOtidAw1CCQtwFF/R2LVei3ba2ceaj8T
|
||||||
|
ak7da9ht6w9wxU3wjgPd6iz6FIIIv4UqaQ==
|
||||||
|
=LNt/
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
31
tests/testdata/messages/message.literal.nomdc.pass.cast5.asc
vendored
Normal file
31
tests/testdata/messages/message.literal.nomdc.pass.cast5.asc
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
-----BEGIN PGP MESSAGE-----
|
||||||
|
Version: BCPG C# v1.6.1.0
|
||||||
|
|
||||||
|
jA0EAwMCjUn4xB3iQ+1gycP5SZFnGPJuc+ziJVLfwYrChXjt+tIt65OO32lmoZfg
|
||||||
|
7xvLkSnKRbEMr4fsbhBKPz02FS1T3qoDx+uFSdAXyYnwIZ/WLIfVbxmcl9AkgLvE
|
||||||
|
vfrq81rODrwtHJci71EYOKBme0luWkzGdIMHTx8MpvNVLJEBmVqj0f9CeUt/sZDn
|
||||||
|
rMdoyG8xxkKqFn7IhM7Ef9FtQiUJlE5sRFMn50kFT9gwYE6lGum1nze1aDoX1Pol
|
||||||
|
s5JRWJ2uQs8eQ4Aba9f9Jffv13qJDfkxovFyqupkCZFwm4smQkvLJUxEMxzpe5Ev
|
||||||
|
OR071ESiPDfQBfDTT+Eaw4frr0lMhoAAkEEC3Ncju7EhhQeLgQndwxgiiDF+WcVl
|
||||||
|
OuSl6b1XaTMn/X70SH0Lg3QUdBV0CF5lrLkWuN33u3GffaES4sBm25wC/Bbb13Q8
|
||||||
|
aJunYMJ8KhkyBkXXiNxqh2G1/L1dxe+nz9WIeL30YBjxxwi3/rifoRkbux7HftGV
|
||||||
|
VBThkmB2UJZszutGEXGvpNzhrfogQi4TFIOScdgb33P6uwpx+QATxBxHFv3eSJ6n
|
||||||
|
SYCS5C2b1dShlpXonISqYYXOEwkhmt3vWKEhU8pEWjB76tr7oTbCm3UqMg5QEa/e
|
||||||
|
QMstP2g9ZG8U4mExfiKFzGsCznoJJSJdG/WfkUSkhUOREZc7OGEBrhgRPHELMMzy
|
||||||
|
ebEy3DVXU3DLSMmYTh2xZY909ba+8mYzy1AXqEQfiCTQH2JgfSEdviD5E4QxRuOx
|
||||||
|
q6AhIb1iHXw1JP3ZkO/tbXlTXgBY8AXD5dv6t1mZd+hQ2AzKcAGsK+VOJcXVLhEC
|
||||||
|
dAz8N8ZYHSLc4dVxz1m3G9St81o0qpB51cfFlWYyAxhKiuKbia6cBIzHAvo2ys81
|
||||||
|
GZs2raUT6+6ehXwy6zVixi5ouO9UHl9HMA5thaOaK0hP8qZFKmRk2nyUYilKbshr
|
||||||
|
9lAhu+MxygzuBdISW/EWQjbO4GsJ+r7TG7b/b/nwOVPM02MHZOiP5wyko3hro2xa
|
||||||
|
nRTLt/8OgQXW9Dn/58bx7KqNx69oTNuvAU4MVDdzTx2pQymhMpz/KrMuGHlcyv5v
|
||||||
|
/ntTkmTBtfFNu+CNr8Uh3tMqmjlNwMBEY/BI0HagDxikpjO/qETluzG9cLwnBc1C
|
||||||
|
6ySkkOLisJIdLzrRuMvekHg3795GLhrAI/z8uSPIlvQDi4eLgPzzsckLu9PEsG/d
|
||||||
|
2p7tk6CCc7HwpBlBj2l5nZcoHUNEesDUJSkG2VpPR+TDdkSIWirEp/1y6QPCoa0d
|
||||||
|
v8M5x0gnEpWaGvBl8x1wBxIHCxO4KcoiWS+90kNNIdpzKjkSBlnmNPrZ/37E9PQd
|
||||||
|
xev7+do6CKiRO3YUzs4kT18mazd19AWGCJbxnXll294fc5xTZSJeH9HYPcvuETBG
|
||||||
|
B/4DV4TKGH10WJgG/ZHqdt+4bqyVts/xvjJI5s+AvwXHTHoFZLDnFzmJdlU8LPic
|
||||||
|
/sZUEBrFPS49x5f7ccEHt9zBgrvwioB6Db1Y3snGY0hXIl7/jM4hdWOHleKVKC16
|
||||||
|
GxSWICovLeDvL9j8J6b2NP20sPr32bYwVopdRdhWEeUUkHYIlAR3GuZZtLMQcXeK
|
||||||
|
pMf+D8ohmWrk1jLcpnYakO1L77fJZuhG5hwF
|
||||||
|
=NG/d
|
||||||
|
-----END PGP MESSAGE-----
|
||||||
5
tests/testdata/messages/message.nomdc.pass
vendored
Normal file
5
tests/testdata/messages/message.nomdc.pass
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pretium libero id orci interdum pretium. Cras at arcu in leo facilisis tincidunt ac sed lorem. Quisque quis varius quam. Integer gravida quam non cursus suscipit. Vivamus placerat convallis leo, nec lobortis ipsum. Sed fermentum ipsum sed tellus consequat elementum. Fusce id congue orci, at molestie ex.
|
||||||
|
|
||||||
|
Phasellus vel sagittis mauris. Ut in vehicula ipsum. Nullam facilisis molestie diam, in fermentum justo interdum id. Donec vestibulum tristique sapien nec rhoncus. Suspendisse venenatis consectetur mollis. Phasellus fringilla tortor non ligula malesuada, in vehicula mauris efficitur. Duis pulvinar eleifend est nec fringilla. Nunc elit nulla, sodales quis ullamcorper sit amet, elementum vitae justo. In pretium leo sit amet risus pharetra, ac tincidunt sem varius.
|
||||||
|
|
||||||
|
Nunc fermentum id risus sed lobortis. Sed id vulputate arcu. In ac quam sed nulla semper ullamcorper. Donec eleifend quam at dolor dictum, ut efficitur tortor dapibus. Nunc maximus quam non erat aliquet, quis blandit nibh sollicitudin. Fusce aliquam est enim, nec mattis orci scelerisque nec. Nullam venenatis eget elit consectetur sagittis.
|
||||||
19
tests/testdata/messages/message.nomdc.pass.asc
vendored
Normal file
19
tests/testdata/messages/message.nomdc.pass.asc
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
-----BEGIN PGP MESSAGE-----
|
||||||
|
Version: BCPG C# v1.6.1.0
|
||||||
|
|
||||||
|
jA0EAwMCCv41ziKqTRVgycGm1XyJLTiWq5AbQLN0xA8/yH/gPf/2D+L+e2yzGq7Q
|
||||||
|
1sut8FKqX2LBga8mu4gHvvotu4dvewDF5t0jT72zRqc9VsRebWihRkdNr9GU5j5K
|
||||||
|
kxlA59uOPayFHjlkkqgrwR0m1RZc63G+79w/qoAv8K6OIHnpW5AXZR0WS4ZxfDoK
|
||||||
|
p5vXPZ7U9SAEHCSNCbJDMKVWKn0haWE4LZ3k5sOmTR4fGCowPdi4qu1sDCcl9Kzk
|
||||||
|
TTHh/9mshclKvYQKfZPQD4k14PGc/OHWHHj+THhciNUSYDy4D62cXeSbSziwD+Ga
|
||||||
|
fWMhggFMRkc1kp8c6C9z7aFG7Ii6SVGBJIfY7MWvfFqte9TdDtiyKEF+3CH0jUEG
|
||||||
|
m1en0tImUFVW+qRzP4T0eUtrw5svxlx/NprgT6ruOfOBGdb9oS2T0WPm3ZgY+TYN
|
||||||
|
liKI/Co3zDAFF3oE5JjwmuNFLa47/HCsqNLWqzk9bskKBxHYv2g8Mu1lwK8wwBvh
|
||||||
|
0w85itQFf7N/7ilMSj0ABRaNNQHV+jGWY9NWBnoTfDgz4zYAg70uPL4DVEw1kdVU
|
||||||
|
HbCRmdSop3D0YHLFuCyUBMLkKg3dq0cXhCszUhkAOYb1Xu6sMGJzJw6xi2kzJliK
|
||||||
|
ZwnKJVKjE6B8NajPdtWOkganHy5jtPWgg8Et5oysW/QwviRuXh1VGwLUmX2puZ3F
|
||||||
|
2DwtEB1mJNRKS7F35sfdGfj1fOpVOMsEngiaYoZnUkBplxpb/In+Tz8QW0ocrZbh
|
||||||
|
VhA26h4uWKAHiAmd4Qlk4xUFls1Hs/p8WHr0njEuB+GjUVmlEMAw6yg160+OiWZt
|
||||||
|
VjUDkzDEAPA=
|
||||||
|
=M8wJ
|
||||||
|
-----END PGP MESSAGE-----
|
||||||
2
tests/testdata/messages/message.rsa.dsa.pass.aes
vendored
Normal file
2
tests/testdata/messages/message.rsa.dsa.pass.aes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
This is stored, literally\!
|
||||||
|
|
||||||
13
tox.ini
13
tox.ini
@@ -1,9 +1,9 @@
|
|||||||
[tox]
|
[tox]
|
||||||
envlist = pypy, pypy3, py27, py33, py34, py35, pep8, setup35, setup34, setup33, setup27
|
envlist = pypy, pypy3, py27, py33, py34, py35, py36, pep8, setup36, setup35, setup34, setup33, setup27
|
||||||
skipsdist = True
|
skipsdist = True
|
||||||
|
|
||||||
[pytest]
|
[pytest]
|
||||||
addopts = -vv -s --color=yes
|
addopts = -vv -r a -s --color=yes
|
||||||
norecursedirs = testdata
|
norecursedirs = testdata
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
@@ -21,6 +21,7 @@ deps =
|
|||||||
singledispatch
|
singledispatch
|
||||||
pytest
|
pytest
|
||||||
pytest-cov
|
pytest-cov
|
||||||
|
pytest-ordering
|
||||||
|
|
||||||
install_command = pip install {opts} --no-cache-dir {packages}
|
install_command = pip install {opts} --no-cache-dir {packages}
|
||||||
commands =
|
commands =
|
||||||
@@ -32,6 +33,12 @@ commands =
|
|||||||
pip install -e .
|
pip install -e .
|
||||||
rm -rf PGPy.egg-info
|
rm -rf PGPy.egg-info
|
||||||
|
|
||||||
|
[testenv:setup36]
|
||||||
|
recreate = True
|
||||||
|
basepython = python3.6
|
||||||
|
whitelist_externals = {[test-setup]whitelist_externals}
|
||||||
|
commands = {[test-setup]commands}
|
||||||
|
|
||||||
[testenv:setup35]
|
[testenv:setup35]
|
||||||
recreate = True
|
recreate = True
|
||||||
basepython = python3.5
|
basepython = python3.5
|
||||||
@@ -58,7 +65,7 @@ whitelist_externals = {[test-setup]whitelist_externals}
|
|||||||
commands = {[test-setup]commands}
|
commands = {[test-setup]commands}
|
||||||
|
|
||||||
[testenv:pep8]
|
[testenv:pep8]
|
||||||
basepython = python3.5
|
basepython = python3.6
|
||||||
deps =
|
deps =
|
||||||
flake8
|
flake8
|
||||||
pep8-naming
|
pep8-naming
|
||||||
|
|||||||
Reference in New Issue
Block a user