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
|
||||
python:
|
||||
- "3.6"
|
||||
- "3.5"
|
||||
- "3.4"
|
||||
- "3.3"
|
||||
@@ -9,38 +13,50 @@ python:
|
||||
matrix:
|
||||
include:
|
||||
# add a pep8 test
|
||||
- python: 3.5
|
||||
- python: 3.6
|
||||
env: TOXENV=pep8
|
||||
|
||||
# test setup.py using each tested version
|
||||
- python: 3.6
|
||||
env: TOXENV=setup36
|
||||
|
||||
- python: 3.5
|
||||
env: TOXENV=setup35
|
||||
|
||||
- python: 3.4
|
||||
env: TOXENV=setup34
|
||||
|
||||
- python: 3.3
|
||||
env: TOXENV=setup33
|
||||
|
||||
- python: 2.7
|
||||
env: TOXENV=setup27
|
||||
|
||||
allow_failures:
|
||||
# pep8 failures shouldn't be considered fatal
|
||||
- env: TOXENV=pep8
|
||||
# pypy and pypy3 tests are just for fun
|
||||
- python: "pypy"
|
||||
- python: "pypy3"
|
||||
# osx, until it's working
|
||||
- os: osx
|
||||
|
||||
# install requirements
|
||||
install:
|
||||
# make sure libffi-dev, gnupg2, and pgpdump are installed
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install -y libffi-dev gnupg2 pgpdump
|
||||
- ./install_dependencies.${TRAVIS_OS_NAME}.sh
|
||||
# ensure tox and coveralls are installed
|
||||
- pip install tox python-coveralls
|
||||
|
||||
# set TOXENV if it isn't yet
|
||||
before_script:
|
||||
- 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" == 'pypy3' ]]; then export TOXENV=pypy3; fi
|
||||
- if [[ "${TRAVIS_PYTHON_VERSION}" == 'pypy' ]]; then export TOXENV=pypy; fi
|
||||
- if [[ "${TRAVIS_PYTHON_VERSION}" == 'pypy3' ]]; then export TOXENV=pypy3; fi
|
||||
|
||||
# run tox
|
||||
script:
|
||||
- ./tox.sh
|
||||
|
||||
# and report coverage to coveralls, but only if this was a pytest run
|
||||
after_success:
|
||||
- if [[ "${TOXENV}" == "py"* ]]; then coveralls; fi
|
||||
|
||||
10
README.rst
10
README.rst
@@ -38,12 +38,19 @@ Documentation
|
||||
|
||||
`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
|
||||
------------
|
||||
|
||||
- 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>`_
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
:members:
|
||||
:noindex:
|
||||
|
||||
.. py:attribute:: ascii_header
|
||||
:noindex:
|
||||
: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.
|
||||
|
||||
.. 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.
|
||||
|
||||
@@ -32,7 +29,6 @@ Classes
|
||||
# others: { (Fingerprint, bool(key.is_public): PGPKey }
|
||||
|
||||
.. 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.
|
||||
|
||||
@@ -55,7 +51,6 @@ Classes
|
||||
:members:
|
||||
|
||||
.. py:attribute:: ascii_header
|
||||
:noindex:
|
||||
|
||||
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:
|
||||
|
||||
.. py:attribute:: ascii_header
|
||||
:noindex:
|
||||
|
||||
An :py:obj:`~collections.OrderedDict` of headers that appear, in order, in the ASCII-armored form of this object.
|
||||
|
||||
.. 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.
|
||||
|
||||
@@ -83,7 +76,6 @@ Classes
|
||||
:returns: :py:obj:`PGPMessage`
|
||||
|
||||
.. 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.
|
||||
|
||||
@@ -102,12 +94,10 @@ Classes
|
||||
:members:
|
||||
|
||||
.. py:attribute:: ascii_header
|
||||
:noindex:
|
||||
|
||||
An :py:obj:`~collections.OrderedDict` of headers that appear, in order, in the ASCII-armored form of this object.
|
||||
|
||||
.. 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.
|
||||
|
||||
@@ -118,7 +108,6 @@ Classes
|
||||
:returns: :py:obj:`PGPSignature`
|
||||
|
||||
.. 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.
|
||||
|
||||
@@ -8,68 +8,53 @@ Constants
|
||||
|
||||
.. autoclass:: PubKeyAlgorithm
|
||||
:no-members:
|
||||
:noindex:
|
||||
|
||||
.. autoattribute:: RSAEncryptOrSign
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: DSA
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: ElGamal
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: ECDH
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: ECDSA
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
:py:class:`EllipticCurveOID`
|
||||
----------------------------
|
||||
|
||||
.. autoclass:: EllipticCurveOID
|
||||
:noindex:
|
||||
:no-members:
|
||||
|
||||
.. autoattribute:: Curve25519
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Ed25519
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: NIST_P256
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: NIST_P384
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: NIST_P521
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Brainpool_P256
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Brainpool_P384
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Brainpool_P512
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: SECP256K1
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
|
||||
@@ -78,38 +63,35 @@ Constants
|
||||
|
||||
.. autoclass:: SymmetricKeyAlgorithm
|
||||
:no-members:
|
||||
:noindex:
|
||||
|
||||
.. autoattribute:: IDEA
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: TripleDES
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: CAST5
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Blowfish
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: AES128
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: AES192
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: AES256
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Camellia128
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Camellia192
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Camellia256
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
|
||||
@@ -118,22 +100,17 @@ Constants
|
||||
|
||||
.. autoclass:: CompressionAlgorithm
|
||||
:no-members:
|
||||
:noindex:
|
||||
|
||||
.. autoattribute:: Uncompressed
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: ZIP
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: ZLIB
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: BZ2
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
:py:class:`HashAlgorithm`
|
||||
@@ -141,34 +118,26 @@ Constants
|
||||
|
||||
.. autoclass:: HashAlgorithm
|
||||
:no-members:
|
||||
:noindex:
|
||||
|
||||
.. autoattribute:: MD5
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: SHA1
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: RIPEMD160
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: SHA256
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: SHA384
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: SHA512
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: SHA224
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
|
||||
@@ -177,62 +146,47 @@ Constants
|
||||
|
||||
.. autoclass:: SignatureType
|
||||
:no-members:
|
||||
:noindex:
|
||||
|
||||
.. autoattribute:: BinaryDocument
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: CanonicalDocument
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Standalone
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Generic_Cert
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Persona_Cert
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Positive_Cert
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Subkey_Binding
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: PrimaryKey_Binding
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: DirectlyOnKey
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: KeyRevocation
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: SubkeyRevocation
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: CertRevocation
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Timestamp
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: ThirdParty_Confirmation
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
|
||||
@@ -241,34 +195,26 @@ Constants
|
||||
|
||||
.. autoclass:: KeyFlags
|
||||
:no-members:
|
||||
:noindex:
|
||||
|
||||
.. autoattribute:: Certify
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Sign
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: EncryptCommunications
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: EncryptStorage
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Split
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Authentication
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: MultiPerson
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
|
||||
@@ -277,25 +223,19 @@ Constants
|
||||
|
||||
.. autoclass:: RevocationReason
|
||||
:no-members:
|
||||
:noindex:
|
||||
|
||||
.. autoattribute:: NotSpecified
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Superseded
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Compromised
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: Retired
|
||||
:noindex:
|
||||
:annotation:
|
||||
|
||||
.. autoattribute:: UserID
|
||||
:noindex:
|
||||
: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
|
||||
********
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
.. include:: exceptions.rst.inc
|
||||
|
||||
exceptions
|
||||
constants
|
||||
classes
|
||||
.. include:: constants.rst.inc
|
||||
|
||||
.. include:: exceptions.rst
|
||||
|
||||
.. include:: constants.rst
|
||||
|
||||
.. include:: classes.rst
|
||||
.. include:: classes.rst.inc
|
||||
|
||||
@@ -4,10 +4,26 @@
|
||||
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
|
||||
======
|
||||
|
||||
Released: |today|
|
||||
Released: April 21, 2016
|
||||
|
||||
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],
|
||||
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
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -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:
|
||||
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})
|
||||
|
||||
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_protected is False
|
||||
key.protect("C0rrectPassphr@se")
|
||||
key.protect("C0rrectPassphr@se", SymmetricKeyAlgorithm.AES256, HashAlgorithm.SHA256)
|
||||
# key.is_protected is now True
|
||||
|
||||
Unlocking Protected Secret Keys
|
||||
@@ -106,6 +123,11 @@ Key unlocking is quite simple::
|
||||
# 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
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
@@ -126,4 +148,3 @@ in Python 2::
|
||||
|
||||
# ASCII armored
|
||||
keystr = str(key)
|
||||
|
||||
|
||||
@@ -57,4 +57,3 @@ in Python 2::
|
||||
# if message is cleartext, this will also properly canonicalize and dash-escape
|
||||
# the message text
|
||||
msgstr = str(message)
|
||||
|
||||
|
||||
@@ -2,29 +2,63 @@
|
||||
Installation
|
||||
************
|
||||
|
||||
.. highlight:: bash
|
||||
.. highlight:: console
|
||||
|
||||
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
|
||||
-----
|
||||
|
||||
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::
|
||||
|
||||
$ 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.
|
||||
|
||||
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
|
||||
--------
|
||||
|
||||
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
|
||||
@@ -33,4 +67,3 @@ Installation
|
||||
Once you have the prerequisites specified above, PGPy can be installed from PyPI using pip, like so::
|
||||
|
||||
$ 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.
|
||||
|
||||
:Curves:
|
||||
- Curve, False, Brainpool P-256
|
||||
- Curve, False, Brainpool P-384
|
||||
- Curve, False, Brainpool P-512
|
||||
- Curve, True, Brainpool P-256
|
||||
- Curve, True, Brainpool P-384
|
||||
- Curve, True, Brainpool P-512
|
||||
- Curve, False, Curve25519
|
||||
- Curve, True, SECP256K1
|
||||
|
||||
.. 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
|
||||
|
||||
|
||||
@@ -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"
|
||||
__copyright__ = "Copyright (c) 2014 Michael Greene"
|
||||
__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 .types import FlagEnum
|
||||
from ._curves import BrainpoolP256R1, BrainpoolP384R1, BrainpoolP512R1
|
||||
|
||||
__all__ = ['Backend',
|
||||
'EllipticCurveOID',
|
||||
@@ -41,8 +42,8 @@ __all__ = ['Backend',
|
||||
'TrustFlags']
|
||||
|
||||
|
||||
# this is 100 KiB
|
||||
_hashtunedata = bytearray([10, 11, 12, 13, 14, 15, 16, 17] * 128 * 100)
|
||||
# this is 50 KiB
|
||||
_hashtunedata = bytearray([10, 11, 12, 13, 14, 15, 16, 17] * 128 * 50)
|
||||
|
||||
|
||||
class Backend(Enum):
|
||||
@@ -71,29 +72,34 @@ class EllipticCurveOID(Enum):
|
||||
NIST_P521 = ('1.3.132.0.35', ec.SECP521R1)
|
||||
#: Brainpool Standard Curve, 256-bit
|
||||
#:
|
||||
#: .. warning::
|
||||
#: This curve is not currently usable by PGPy
|
||||
Brainpool_P256 = ('1.3.36.3.3.2.8.1.1.7', )
|
||||
#: .. note::
|
||||
#: Requires OpenSSL >= 1.0.2
|
||||
Brainpool_P256 = ('1.3.36.3.3.2.8.1.1.7', BrainpoolP256R1)
|
||||
#: Brainpool Standard Curve, 384-bit
|
||||
#:
|
||||
#: .. warning::
|
||||
#: This curve is not currently usable by PGPy
|
||||
Brainpool_P384 = ('1.3.36.3.3.2.8.1.1.11', )
|
||||
#: .. note::
|
||||
#: Requires OpenSSL >= 1.0.2
|
||||
Brainpool_P384 = ('1.3.36.3.3.2.8.1.1.11', BrainpoolP384R1)
|
||||
#: Brainpool Standard Curve, 512-bit
|
||||
#:
|
||||
#: .. warning::
|
||||
#: This curve is not currently usable by PGPy
|
||||
Brainpool_P512 = ('1.3.36.3.3.2.8.1.1.13', )
|
||||
#: .. note::
|
||||
#: Requires OpenSSL >= 1.0.2
|
||||
Brainpool_P512 = ('1.3.36.3.3.2.8.1.1.13', BrainpoolP512R1)
|
||||
#: SECG curve secp256k1
|
||||
SECP256K1 = ('1.3.132.0.10', ec.SECP256K1)
|
||||
|
||||
def __new__(cls, oid, curve=None):
|
||||
# preprocessing stage for enum members:
|
||||
# - 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._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
|
||||
|
||||
@property
|
||||
@@ -110,6 +116,7 @@ class EllipticCurveOID(Enum):
|
||||
# return the hash algorithm to specify in the KDF fields when generating a key
|
||||
algs = {256: HashAlgorithm.SHA256,
|
||||
384: HashAlgorithm.SHA384,
|
||||
512: HashAlgorithm.SHA512,
|
||||
521: HashAlgorithm.SHA512}
|
||||
|
||||
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
|
||||
algs = {256: SymmetricKeyAlgorithm.AES128,
|
||||
384: SymmetricKeyAlgorithm.AES192,
|
||||
512: SymmetricKeyAlgorithm.AES256,
|
||||
521: SymmetricKeyAlgorithm.AES256}
|
||||
|
||||
return algs.get(self.key_size, None)
|
||||
@@ -343,15 +351,24 @@ class HashAlgorithm(IntEnum):
|
||||
return self._tuned_count
|
||||
|
||||
def tune_count(self):
|
||||
start = time.time()
|
||||
h = self.hasher
|
||||
h.update(_hashtunedata)
|
||||
end = time.time()
|
||||
start = end = 0
|
||||
htd = _hashtunedata[:]
|
||||
|
||||
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
|
||||
# GnuPG tunes for about 100ms, so we'll do that as well
|
||||
_TIME = 0.100
|
||||
ct = int(len(_hashtunedata) * (_TIME / (end - start)))
|
||||
ct = int(len(htd) * (_TIME / (end - start)))
|
||||
c1 = ((ct >> (ct.bit_length() - 5)) - 16)
|
||||
c2 = (ct.bit_length() - 11)
|
||||
c = ((c2 << 4) + c1)
|
||||
@@ -432,7 +449,7 @@ class KeyFlags(FlagEnum):
|
||||
Sign = 0x02
|
||||
#: Signifies that a key may be used to encrypt messages.
|
||||
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
|
||||
#: 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.
|
||||
|
||||
@@ -788,6 +788,17 @@ class String2Key(Field):
|
||||
def __nonzero__(self):
|
||||
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):
|
||||
self.usage = packet[0]
|
||||
del packet[0]
|
||||
@@ -985,6 +996,13 @@ class PrivKey(PubKey):
|
||||
|
||||
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
|
||||
def __privkey__(self):
|
||||
"""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 len(self.ct) == 0:
|
||||
return sk
|
||||
return self.symalg, sk
|
||||
|
||||
# otherwise, we now need to decrypt the encrypted session key
|
||||
m = bytearray(_decrypt(bytes(self.ct), sk, self.symalg))
|
||||
@@ -565,7 +565,7 @@ class SKESessionKeyV4(SKESessionKey):
|
||||
symalg = SymmetricKeyAlgorithm(m[0])
|
||||
del m[0]
|
||||
|
||||
return (symalg, bytes(m))
|
||||
return symalg, bytes(m)
|
||||
|
||||
def encrypt_sk(self, passphrase, sk):
|
||||
# generate the salt and derive the key to encrypt sk with from it
|
||||
@@ -867,7 +867,7 @@ class PrivKeyV4(PrivKey, PubKeyV4):
|
||||
|
||||
def pubkey(self):
|
||||
# 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.pkalg = self.pkalg
|
||||
|
||||
|
||||
@@ -162,8 +162,8 @@ class ByteFlag(Signature):
|
||||
_bytes = super(ByteFlag, self).__bytearray__()
|
||||
_bytes += self.int_to_bytes(sum(self.flags))
|
||||
# null-pad _bytes if they are not up to the end now
|
||||
if self.header.length + len(self.header) != len(_bytes):
|
||||
_bytes += b'\x00' * (len(_bytes) - len(self.header) - 1)
|
||||
if len(_bytes) < len(self):
|
||||
_bytes += b'\x00' * (len(self) - len(_bytes))
|
||||
return _bytes
|
||||
|
||||
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
|
||||
"""
|
||||
import binascii
|
||||
import calendar
|
||||
import collections
|
||||
import contextlib
|
||||
import copy
|
||||
@@ -360,10 +361,9 @@ class PGPSignature(Armorable, ParentRef, PGPObject):
|
||||
"""
|
||||
_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.PrimaryKey_Binding, SignatureType.DirectlyOnKey, SignatureType.KeyRevocation,
|
||||
SignatureType.SubkeyRevocation]:
|
||||
SignatureType.PrimaryKey_Binding}:
|
||||
"""
|
||||
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
|
||||
@@ -385,7 +385,7 @@ class PGPSignature(Armorable, ParentRef, PGPObject):
|
||||
if len(_s) > 0:
|
||||
_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
|
||||
(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
|
||||
|
||||
if self.type in [SignatureType.Generic_Cert, SignatureType.Persona_Cert, SignatureType.Casual_Cert,
|
||||
SignatureType.Positive_Cert, SignatureType.CertRevocation]:
|
||||
if self.type in {SignatureType.KeyRevocation, SignatureType.SubkeyRevocation, SignatureType.DirectlyOnKey}:
|
||||
"""
|
||||
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
|
||||
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
|
||||
if subject.is_uid:
|
||||
_data += b'\xb4' + self.int_to_bytes(len(_s), 4) + _s
|
||||
_data += b'\xb4'
|
||||
|
||||
if subject.is_ua:
|
||||
_data += b'\xd1' + self.int_to_bytes(len(_s), 4) + _s
|
||||
else:
|
||||
_data += b'\xd1'
|
||||
|
||||
_data += self.int_to_bytes(len(_s), 4) + _s
|
||||
|
||||
# if this is a new signature, do update_hlen
|
||||
if 0 in list(self._signature.signature):
|
||||
@@ -789,12 +817,18 @@ class PGPMessage(Armorable, PGPObject):
|
||||
|
||||
def __str__(self):
|
||||
if self.type == 'cleartext':
|
||||
return "-----BEGIN PGP SIGNED MESSAGE-----\n" \
|
||||
"Hash: {hashes:s}\n\n" \
|
||||
"{cleartext:s}\n" \
|
||||
"{signature:s}".format(hashes=','.join(set(s.hash_algorithm.name for s in self.signatures)),
|
||||
cleartext=self.dash_escape(self.bytes_to_text(self._message)),
|
||||
signature=super(PGPMessage, self).__str__())
|
||||
tmpl = u"-----BEGIN PGP SIGNED MESSAGE-----\n" \
|
||||
u"{hhdr:s}\n" \
|
||||
u"{cleartext:s}\n" \
|
||||
u"{signature:s}"
|
||||
|
||||
# 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__()
|
||||
|
||||
@@ -1207,8 +1241,8 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
||||
if self.is_public:
|
||||
return self._key.__bytearray__()[len(self._key.header):]
|
||||
|
||||
publen = len(self._key) - len(self._key.keymaterial) + self._key.keymaterial.publen()
|
||||
return self._key.__bytearray__()[len(self._key.header):publen]
|
||||
pub = self._key.pubkey()
|
||||
return pub.__bytearray__()[len(pub.header):]
|
||||
|
||||
@property
|
||||
def is_expired(self):
|
||||
@@ -1253,6 +1287,15 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
||||
"""The :py:obj:`constants.PubKeyAlgorithm` pertaining to this key"""
|
||||
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
|
||||
def magic(self):
|
||||
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
|
||||
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
|
||||
self._sibling = weakref.ref(pub)
|
||||
@@ -1425,7 +1469,7 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
||||
|
||||
raise TypeError
|
||||
|
||||
def __or__(self, other):
|
||||
def __or__(self, other, from_sib=False):
|
||||
if isinstance(other, Key) and self._key is None:
|
||||
self._key = other
|
||||
|
||||
@@ -1451,9 +1495,13 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
||||
raise TypeError("unsupported operand type(s) for |: '{:s}' and '{:s}'"
|
||||
"".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 |= copy.copy(other)
|
||||
if sib is None:
|
||||
self._sibling = None
|
||||
|
||||
else: # pragma: no cover
|
||||
sib.__or__(copy.copy(other), True)
|
||||
|
||||
return self
|
||||
|
||||
@@ -1461,13 +1509,18 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
||||
key = super(PGPKey, self).__copy__()
|
||||
key._key = copy.copy(self._key)
|
||||
|
||||
for id, subkey in self._children.items():
|
||||
key |= copy.copy(subkey)
|
||||
|
||||
for uid in self._uids:
|
||||
key |= copy.copy(uid)
|
||||
|
||||
for id, subkey in self._children.items():
|
||||
key |= copy.copy(subkey)
|
||||
|
||||
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)
|
||||
|
||||
return key
|
||||
@@ -1801,10 +1854,10 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
||||
:keyword compression: A list of preferred compression algorithms, as :py:obj:`~constants.CompressionAlgorithm`.
|
||||
This keyword is ignored for non-self-certifications.
|
||||
: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.
|
||||
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.
|
||||
This keyword is ignored for non-self-certifications.
|
||||
: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 ]
|
||||
|
||||
# collect signature(s)
|
||||
if isinstance(signature, PGPSignature):
|
||||
if signature.signer != self.fingerprint.keyid and signature.signer not in self.subkeys:
|
||||
raise PGPError("Incorrect key. Expected: {:s}".format(signature.signer))
|
||||
sspairs.append((signature, subject))
|
||||
if signature is None:
|
||||
if isinstance(subject, PGPMessage):
|
||||
sspairs += [ (sig, subject.message) for sig in _filter_sigs(subject.signatures) ]
|
||||
|
||||
if isinstance(subject, PGPMessage):
|
||||
sspairs += [ (sig, subject.message) for sig in _filter_sigs(subject.signatures) ]
|
||||
if isinstance(subject, (PGPUID, PGPKey)):
|
||||
sspairs += [ (sig, subject) for sig in _filter_sigs(subject.__sig__) ]
|
||||
|
||||
if isinstance(subject, (PGPUID, PGPKey)):
|
||||
sspairs += [ (sig, subject) for sig in _filter_sigs(subject.__sig__) ]
|
||||
if isinstance(subject, PGPKey):
|
||||
# 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):
|
||||
# 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__) ]
|
||||
elif signature.signer in {self.fingerprint.keyid} | set(self.subkeys):
|
||||
sspairs += [(signature, subject)]
|
||||
|
||||
if len(sspairs) == 0:
|
||||
raise PGPError("No signatures to verify")
|
||||
@@ -2074,7 +2126,7 @@ class PGPKey(Armorable, ParentRef, PGPObject):
|
||||
# finally, start verifying signatures
|
||||
sigv = SignatureVerification()
|
||||
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}. "
|
||||
"Verifying with subkey...".format(sig.signer),
|
||||
stacklevel=2)
|
||||
|
||||
@@ -64,10 +64,10 @@ class Armorable(six.with_metaclass(abc.ABCMeta)):
|
||||
@staticmethod
|
||||
def is_ascii(text):
|
||||
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)):
|
||||
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
|
||||
|
||||
@@ -92,25 +92,24 @@ class Armorable(six.with_metaclass(abc.ABCMeta)):
|
||||
# the re.VERBOSE flag allows for:
|
||||
# - 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
|
||||
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)
|
||||
(Hash:\ (?P<hashes>[A-Za-z0-9\-,]+)(?:\r?\n){2})?
|
||||
(?P<cleartext>(.*\n)+)(?:\r?\n)
|
||||
)?
|
||||
# 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
|
||||
# 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,
|
||||
# including the newline, and the pad character(s)
|
||||
(?P<body>([A-Za-z0-9+/]{1,75}={,2}(?:\r?\n))+)
|
||||
# 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
|
||||
^-{5}END\ PGP\ (?P=magic)-{5}$(?:\r?\n)?
|
||||
""",
|
||||
text, flags=re.MULTILINE | re.VERBOSE)
|
||||
^-{5}END\ PGP\ (?P=magic)-{5}(?:\r?\n)?
|
||||
""", text, flags=re.MULTILINE | re.VERBOSE)
|
||||
|
||||
if m is None: # pragma: no cover
|
||||
raise ValueError("Expected: ASCII-armored PGP data")
|
||||
@@ -638,15 +637,20 @@ class FlagEnumMeta(EnumMeta):
|
||||
return self & other
|
||||
|
||||
|
||||
class FlagEnum(six.with_metaclass(FlagEnumMeta, IntEnum)):
|
||||
pass
|
||||
if six.PY2:
|
||||
class FlagEnum(IntEnum):
|
||||
__metaclass__ = FlagEnumMeta
|
||||
|
||||
else:
|
||||
namespace = FlagEnumMeta.__prepare__('FlagEnum', (IntEnum,))
|
||||
FlagEnum = FlagEnumMeta('FlagEnum', (IntEnum,), namespace)
|
||||
|
||||
|
||||
class Fingerprint(str):
|
||||
"""
|
||||
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
|
||||
def keyid(self):
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
-r requirements.txt
|
||||
pytest
|
||||
pytest-cov
|
||||
pytest-ordering
|
||||
flake8
|
||||
pep8-naming
|
||||
1
setup.py
1
setup.py
@@ -52,6 +52,7 @@ setup(
|
||||
'Operating System :: Microsoft :: Windows',
|
||||
'Intended Audience :: Developers',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"""PGPy conftest"""
|
||||
import pytest
|
||||
|
||||
import contextlib
|
||||
import functools
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import six
|
||||
import select
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
@@ -56,81 +56,74 @@ def _run(bin, *binargs, **pkw):
|
||||
|
||||
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_args = ['--options', './pgpy.gpg.conf', '--expert', '--status-fd', '1']
|
||||
_gpg_env = os.environ.copy()
|
||||
_gpg_args = ('--options', './pgpy.gpg.conf', '--expert', '--status-fd')
|
||||
_gpg_env = {}
|
||||
_gpg_env['GNUPGHOME'] = os.path.abspath(os.path.abspath('tests/testdata'))
|
||||
_gpg_kwargs = dict()
|
||||
_gpg_kwargs['cwd'] = 'tests/testdata'
|
||||
_gpg_kwargs['env'] = _gpg_env
|
||||
_gpg_kwargs['stdout'] = subprocess.PIPE
|
||||
_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
|
||||
@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()
|
||||
def gpg_import():
|
||||
@contextlib.contextmanager
|
||||
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_ver >= '2.1':
|
||||
if not os.path.exists('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:
|
||||
yield gpgo
|
||||
@@ -139,7 +132,6 @@ def gpg_import():
|
||||
[os.remove(f) for f in glob.glob('tests/testdata/testkeys.*')]
|
||||
if gpg_ver >= '2.1':
|
||||
[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)
|
||||
|
||||
@@ -149,9 +141,7 @@ def gpg_import():
|
||||
@pytest.fixture()
|
||||
def gpg_check_sigs():
|
||||
def _gpg_check_sigs(*keyids):
|
||||
gpg_args = _gpg_args + ['--check-sigs'] + list(keyids)
|
||||
gpg_kwargs = _gpg_kwargs.copy()
|
||||
gpgo, _ = _run(_gpg_bin, *gpg_args, **gpg_kwargs)
|
||||
gpgc, gpgo, gpge = _gpg('--check-sigs', *keyids)
|
||||
return 'sig-' not in gpgo
|
||||
|
||||
return _gpg_check_sigs
|
||||
@@ -163,11 +153,11 @@ def gpg_verify():
|
||||
r'^\[GNUPG:\] VALIDSIG (?:[0-9A-F]{,24})\1', flags=re.MULTILINE | re.DOTALL)
|
||||
|
||||
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 ]
|
||||
gpg_kwargs = _gpg_kwargs.copy()
|
||||
gpgo, _ = _run(_gpg_bin, *gpg_args, **gpg_kwargs)
|
||||
rargs = [gpg_sigpath, gpg_subjpath] if gpg_sigpath is not None else [gpg_subjpath,]
|
||||
|
||||
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:
|
||||
return keyid in sigs
|
||||
@@ -181,53 +171,56 @@ def gpg_verify():
|
||||
def gpg_decrypt():
|
||||
sfd_decrypt = re.compile(r'^\[GNUPG:\] BEGIN_DECRYPTION\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'(?P<text>(?:.|\n)*)'
|
||||
r'\[GNUPG:\] DECRYPTION_OKAY\n'
|
||||
r'^\[GNUPG:\] GOODMDC\n'
|
||||
r'(?:^\[GNUPG:\] GOODMDC\n)?'
|
||||
r'^\[GNUPG:\] END_DECRYPTION', flags=re.MULTILINE)
|
||||
|
||||
def _gpg_decrypt(encmsgpath, passphrase=None, keyid=None):
|
||||
gpg_args = [_gpg_bin] + _gpg_args[:]
|
||||
gpg_kwargs = _gpg_kwargs.copy()
|
||||
gpg_kwargs['stderr'] = subprocess.PIPE
|
||||
_comargs = ()
|
||||
a = []
|
||||
|
||||
if passphrase is not None:
|
||||
gpg_args += ['--batch', '--passphrase-fd', '0']
|
||||
gpg_kwargs['stdin'] = subprocess.PIPE
|
||||
_comargs = (passphrase.encode(),)
|
||||
# create a pipe to send the passphrase to GnuPG through
|
||||
pfdr, pfdw = os.pipe()
|
||||
|
||||
if keyid is not None:
|
||||
gpg_args += ['--recipient', keyid]
|
||||
# write the passphrase to the pipe buffer right away
|
||||
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)
|
||||
gpgo, gpge = gpgdec.communicate(*_comargs)
|
||||
gpgdec.wait()
|
||||
a.extend(['--batch', '--passphrase-fd', str(pfdr)])
|
||||
|
||||
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
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def gpg_print():
|
||||
sfd_text = re.compile(r'^\[GNUPG:\] PLAINTEXT (?:62|74|75) .*\n'
|
||||
r'^\[GNUPG:\] PLAINTEXT_LENGTH (?P<len>\d+)\n'
|
||||
r'^(?P<text>(.|\n)*)', re.MULTILINE)
|
||||
sfd_text = re.compile(r'^\[GNUPG:\] PLAINTEXT (?:62|74|75) (?P<tstamp>\d+) (?P<fname>.*)\n'
|
||||
r'^\[GNUPG:\] PLAINTEXT_LENGTH (?P<len>\d+)\n', re.MULTILINE)
|
||||
|
||||
gpg_text = re.compile(r'(?:- gpg control packet\n)?(?P<text>.*)', re.MULTILINE | re.DOTALL)
|
||||
|
||||
def _gpg_print(infile):
|
||||
gpg_args = _gpg_args + ['-o-', infile]
|
||||
gpg_kwargs = _gpg_kwargs.copy()
|
||||
gpg_kwargs['stderr'] = subprocess.PIPE
|
||||
gpgc, gpgo, gpge = _gpg('-o-', infile, stderr=subprocess.PIPE)
|
||||
status = sfd_text.match(gpgc)
|
||||
tlen = len(gpgo) if status is None else int(status.group('len'))
|
||||
|
||||
gpgo, _ = _run(_gpg_bin, *gpg_args, **gpg_kwargs)
|
||||
return sfd_text.match(gpgo).group('text')
|
||||
return gpg_text.match(gpgo).group('text')[:tlen]
|
||||
|
||||
return _gpg_print
|
||||
|
||||
@@ -235,12 +228,8 @@ def gpg_print():
|
||||
@pytest.fixture
|
||||
def gpg_keyid_file():
|
||||
def _gpg_keyid_file(infile):
|
||||
gpg_args = _gpg_args + ['--list-packets', infile]
|
||||
gpg_kwargs = _gpg_kwargs.copy()
|
||||
|
||||
gpgo, _ = _run(_gpg_bin, *gpg_args, **gpg_kwargs)
|
||||
return re.findall(r'^\s+keyid: ([0-9A-F]+)', gpgo, flags=re.MULTILINE)
|
||||
|
||||
c, o, e = _gpg('--list-packets', infile)
|
||||
return re.findall(r'^\s+keyid: ([0-9A-F]+)', o, flags=re.MULTILINE)
|
||||
return _gpg_keyid_file
|
||||
|
||||
|
||||
@@ -257,6 +246,8 @@ def pgpdump():
|
||||
# pytest_configure
|
||||
# called after command line options have been parsed and all plugins and initial conftest files been loaded.
|
||||
def pytest_configure(config):
|
||||
print("== PGPy Test Suite ==")
|
||||
|
||||
# ensure commands we need exist
|
||||
for cmd in ['gpg2', 'pgpdump']:
|
||||
if _which(cmd) is None:
|
||||
@@ -270,25 +261,9 @@ def pytest_configure(config):
|
||||
v, _ = _run(_which('pgpdump'), '-v', stderr=subprocess.STDOUT)
|
||||
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("Using OpenSSL " + str(openssl_ver))
|
||||
print("Using GnuPG " + str(gpg_ver))
|
||||
print("Using pgpdump " + str(pgpdump_ver))
|
||||
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.userattribute']
|
||||
|
||||
|
||||
def get_module_objs(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():
|
||||
import pgpy
|
||||
# just check that everything in pgpy.__all__ is actually there
|
||||
assert set(pgpy.__all__) <= { n for n, _ in inspect.getmembers(pgpy) }
|
||||
|
||||
|
||||
@pytest.mark.parametrize('modname', modules)
|
||||
def test_exports(modname):
|
||||
module = importlib.import_module(modname)
|
||||
|
||||
modall = getattr(module, '__all__', None)
|
||||
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)
|
||||
assert get_module_all(module) == get_module_objs(module)
|
||||
|
||||
@@ -1,52 +1,69 @@
|
||||
""" test field parsing
|
||||
"""
|
||||
from itertools import product
|
||||
import pytest
|
||||
|
||||
import itertools
|
||||
|
||||
from pgpy.constants import HashAlgorithm
|
||||
from pgpy.constants import String2KeyType
|
||||
from pgpy.constants import SymmetricKeyAlgorithm
|
||||
|
||||
from pgpy.packet.types import Header
|
||||
from pgpy.packet.fields import String2Key
|
||||
|
||||
from pgpy.packet.types import Header
|
||||
from pgpy.packet.subpackets import Signature
|
||||
from pgpy.packet.subpackets import UserAttribute
|
||||
|
||||
from pgpy.packet.subpackets.types import Header as HeaderSP
|
||||
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):
|
||||
params = {
|
||||
'pheader': [# new format
|
||||
# 1 byte length - 191
|
||||
bytearray(b'\xc2' + b'\xbf' + (b'\x00' * 191) + b'\xca\xfe\xba\xbe'),
|
||||
# 2 byte length - 192
|
||||
bytearray(b'\xc2' + b'\xc0\x00' + (b'\x00' * 192) + b'\xca\xfe\xba\xbe'),
|
||||
# 2 byte length - 8383
|
||||
bytearray(b'\xc2' + b'\xdf\xff' + (b'\x00' * 8383) + b'\xca\xfe\xba\xbe'),
|
||||
# 5 byte length - 8384
|
||||
bytearray(b'\xc2' + b'\xff\x00\x00 \xc0' + (b'\x00' * 8384) + b'\xca\xfe\xba\xbe'),
|
||||
# old format
|
||||
# 1 byte length - 255
|
||||
bytearray(b'\x88' + b'\xff' + (b'\x00' * 255) + b'\xca\xfe\xba\xbe'),
|
||||
# 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)),
|
||||
]
|
||||
}
|
||||
@pytest.mark.parametrize('pheader', pkt_headers)
|
||||
def test_packet_header(self, pheader):
|
||||
b = pheader[:]
|
||||
h = Header()
|
||||
h.parse(pheader)
|
||||
|
||||
assert h.tag == 0x02
|
||||
assert h.length == len(pheader) - len(_trailer)
|
||||
assert pheader[h.length:] == _trailer
|
||||
assert len(h) == len(b) - len(pheader)
|
||||
assert h.__bytes__() == b[:len(h)]
|
||||
|
||||
@pytest.mark.parametrize('spheader', subpkt_headers)
|
||||
def test_subpacket_header(self, spheader):
|
||||
h = HeaderSP()
|
||||
h.parse(spheader)
|
||||
@@ -54,16 +71,6 @@ class TestHeaders(object):
|
||||
assert 65537 > h.length > 1
|
||||
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 = {
|
||||
# 0x00: 'Opaque',
|
||||
@@ -112,155 +119,158 @@ _sspclasses = {
|
||||
0x6d: 'Opaque',
|
||||
0x6e: 'Opaque',
|
||||
}
|
||||
|
||||
_uaspclasses = {
|
||||
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):
|
||||
params = {
|
||||
'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',
|
||||
]
|
||||
]
|
||||
}
|
||||
@pytest.mark.parametrize('sigsubpacket', sig_subpkts)
|
||||
def test_load(self, sigsubpacket):
|
||||
spb = sigsubpacket[:]
|
||||
sp = Signature(spb)
|
||||
spb = sigsubpacket[:]
|
||||
sp = Signature(spb)
|
||||
|
||||
assert spb == b'\xca\xfe\xba\xbe'
|
||||
assert len(sp) == len(sigsubpacket) - 4
|
||||
assert len(sp) == len(sp.__bytes__())
|
||||
assert sp.__bytes__() == bytes(sigsubpacket[:-4])
|
||||
assert spb == _trailer
|
||||
assert len(sp) == len(sigsubpacket) - len(_trailer)
|
||||
assert len(sp) == len(sp.__bytes__())
|
||||
assert sp.__bytes__() == bytes(sigsubpacket[:-len(_trailer)])
|
||||
|
||||
if sp.header.typeid in _sspclasses:
|
||||
assert sp.__class__.__name__ == _sspclasses[sp.header.typeid]
|
||||
if sp.header.typeid in _sspclasses:
|
||||
assert sp.__class__.__name__ == _sspclasses[sp.header.typeid]
|
||||
|
||||
else:
|
||||
assert isinstance(sp, OpaqueSP)
|
||||
else:
|
||||
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):
|
||||
params = {
|
||||
'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'
|
||||
]
|
||||
],
|
||||
}
|
||||
@pytest.mark.parametrize('uasubpacket', ua_subpkts)
|
||||
def test_load(self, uasubpacket):
|
||||
spb = uasubpacket[:]
|
||||
sp = UserAttribute(spb)
|
||||
|
||||
assert spb == b'\xca\xfe\xba\xbe'
|
||||
assert len(sp) == len(uasubpacket) - 4
|
||||
assert spb == _trailer
|
||||
assert len(sp) == len(uasubpacket) - len(_trailer)
|
||||
assert len(sp) == len(sp.__bytes__())
|
||||
assert sp.__bytes__() == uasubpacket[:-4]
|
||||
assert sp.__bytes__() == uasubpacket[:-len(_trailer)]
|
||||
|
||||
if sp.header.typeid in _uaspclasses:
|
||||
assert sp.__class__.__name__ == _uaspclasses[sp.header.typeid]
|
||||
@@ -269,31 +279,32 @@ class TestUserAttributeSubPackets(object):
|
||||
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):
|
||||
params = {'sis2k': [ (bytearray(i) +
|
||||
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
|
||||
]}
|
||||
@pytest.mark.parametrize('sis2k', sis2ks)
|
||||
def test_simple_string2key(self, sis2k):
|
||||
b = sis2k[:]
|
||||
s = String2Key()
|
||||
@@ -307,9 +318,9 @@ class TestString2Key(object):
|
||||
assert s.halg in HashAlgorithm
|
||||
assert s.encalg in SymmetricKeyAlgorithm
|
||||
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):
|
||||
b = sas2k[:]
|
||||
s = String2Key()
|
||||
@@ -323,9 +334,10 @@ class TestString2Key(object):
|
||||
assert s.halg in HashAlgorithm
|
||||
assert s.encalg in SymmetricKeyAlgorithm
|
||||
assert s.specifier == String2KeyType.Salted
|
||||
assert s.salt == b'\xCA\xFE\xBA\xBE\xCA\xFE\xBA\xBE'
|
||||
assert s.iv == b'\xDE\xAD\xBE\xEF\xDE\xAD\xBE\xEF'
|
||||
assert s.salt == _salt
|
||||
assert s.iv == _iv
|
||||
|
||||
@pytest.mark.parametrize('is2k', is2ks)
|
||||
def test_iterated_string2key(self, is2k):
|
||||
b = is2k[:]
|
||||
s = String2Key()
|
||||
@@ -339,20 +351,6 @@ class TestString2Key(object):
|
||||
assert s.halg in HashAlgorithm
|
||||
assert s.encalg in SymmetricKeyAlgorithm
|
||||
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.iv == b'\xDE\xAD\xBE\xEF\xDE\xAD\xBE\xEF'
|
||||
|
||||
|
||||
# TODO: this
|
||||
# class TestKeyMaterial(object):
|
||||
# params = {
|
||||
# 'pkt': [],
|
||||
# }
|
||||
#
|
||||
# ids = {
|
||||
# 'test_keymaterial': [],
|
||||
# }
|
||||
#
|
||||
# def test_keymaterial(self, pkt):
|
||||
# pass
|
||||
assert s.iv == _iv
|
||||
|
||||
@@ -3,22 +3,7 @@
|
||||
"""
|
||||
import pytest
|
||||
|
||||
import glob
|
||||
|
||||
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(': ')
|
||||
from pgpy.types import PGPObject
|
||||
|
||||
text = {
|
||||
# some basic utf-8 test strings - these should all pass
|
||||
@@ -62,22 +47,15 @@ class FakePGPObject(PGPObject):
|
||||
|
||||
|
||||
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.parametrize('text', [v for _, v in sorted(text.items())], ids=sorted(text.keys()))
|
||||
def test_text_to_bytes(self, text):
|
||||
pgpo = FakePGPObject.new(text)
|
||||
|
||||
assert pgpo.__bytearray__() == bytearray(b'_fake_') + bytearray(text, 'utf-8')
|
||||
|
||||
@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):
|
||||
pgpo = FakePGPObject.new(encoded_text)
|
||||
# this should fail
|
||||
|
||||
@@ -5,14 +5,13 @@ import pytest
|
||||
import glob
|
||||
import os
|
||||
|
||||
|
||||
from pgpy.packet import Packet
|
||||
from pgpy.packet import PubKeyV4, PubSubKeyV4, PrivKeyV4, PrivSubKeyV4
|
||||
from pgpy.packet import Opaque
|
||||
|
||||
import pgpy.packet.fields
|
||||
|
||||
# import pgpy.packet.fields
|
||||
|
||||
_trailer = b'\xde\xca\xff\xba\xdd'
|
||||
_pclasses = {
|
||||
(0x01, 3): 'PKESessionKeyV3',
|
||||
(0x02, 4): 'SignatureV4',
|
||||
@@ -36,39 +35,35 @@ _pclasses = {
|
||||
|
||||
def binload(f):
|
||||
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):
|
||||
params = {
|
||||
# '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]*')])
|
||||
}
|
||||
|
||||
@pytest.mark.parametrize('packet', pktfiles, ids=[os.path.basename(f) for f in pktfiles])
|
||||
def test_load(self, packet):
|
||||
if packet in skip_files:
|
||||
pytest.skip("not implemented yet")
|
||||
|
||||
b = binload(packet) + b'\xca\xfe\xba\xbe'
|
||||
b = binload(packet) + _trailer
|
||||
_b = b[:]
|
||||
p = Packet(_b)
|
||||
|
||||
# parsed all bytes
|
||||
assert _b == b'\xca\xfe\xba\xbe'
|
||||
assert _b == _trailer
|
||||
|
||||
# length is computed correctly
|
||||
assert p.header.length + len(p.header) == len(p)
|
||||
assert len(p) == len(b) - 4
|
||||
assert len(p.__bytes__()) == len(b) - 4
|
||||
assert len(p) == len(b) - len(_trailer)
|
||||
assert len(p.__bytes__()) == len(b) - len(_trailer)
|
||||
|
||||
# __bytes__ output is correct
|
||||
assert p.__bytes__() == b[:-4]
|
||||
assert p.__bytes__() == b[:-len(_trailer)]
|
||||
|
||||
# instantiated class is what we expected
|
||||
if hasattr(p.header, 'version') and (p.header.tag, p.header.version) in _pclasses:
|
||||
|
||||
@@ -4,227 +4,301 @@ import pytest
|
||||
|
||||
import glob
|
||||
import os
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pgpy.constants import CompressionAlgorithm
|
||||
from pgpy.constants import HashAlgorithm
|
||||
from pgpy.constants import KeyFlags
|
||||
from pgpy.constants import PubKeyAlgorithm
|
||||
from pgpy.constants import SignatureType
|
||||
from pgpy.constants import SymmetricKeyAlgorithm
|
||||
|
||||
from pgpy.pgp import PGPKey
|
||||
from pgpy.pgp import PGPMessage
|
||||
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
|
||||
class TestBlocks(object):
|
||||
params = {
|
||||
'block': sorted(glob.glob('tests/testdata/blocks/*.asc'))
|
||||
}
|
||||
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):
|
||||
@pytest.mark.parametrize('block', blocks, ids=[os.path.basename(f) for f in blocks])
|
||||
def test_load_blob(self, block):
|
||||
with open(block) as bf:
|
||||
bc = bf.read()
|
||||
|
||||
@@ -238,7 +312,7 @@ class TestBlocks(object):
|
||||
p = PGPMessage()
|
||||
|
||||
else:
|
||||
pytest.skip("not ready for this one")
|
||||
pytest.skip("not ready for file '{}'".format(os.path.basename(block)))
|
||||
assert False
|
||||
|
||||
# load ASCII
|
||||
@@ -248,8 +322,8 @@ class TestBlocks(object):
|
||||
# assert str(p) == bc
|
||||
|
||||
# now check attrs
|
||||
assert block in self.attrs
|
||||
for attr, val in self.attrs[block]:
|
||||
assert block in block_attrs
|
||||
for attr, val in block_attrs[block]:
|
||||
attrval = getattr(p, attr)
|
||||
# this is probably more helpful than just 'assert attrval == val'
|
||||
if attrval != val:
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
""" test the functionality of PGPKeyring
|
||||
"""
|
||||
import pytest
|
||||
|
||||
import glob
|
||||
import os
|
||||
|
||||
import six
|
||||
|
||||
from pgpy.packet import Packet
|
||||
|
||||
from pgpy import PGPKey
|
||||
from pgpy import PGPKeyring
|
||||
from pgpy import PGPMessage
|
||||
@@ -16,25 +12,8 @@ from pgpy import PGPSignature
|
||||
from pgpy import PGPUID
|
||||
from pgpy.types import Fingerprint
|
||||
|
||||
@pytest.fixture
|
||||
def un():
|
||||
return PGPUID.new(six.u('Temperair\xe9e Youx\'seur'))
|
||||
from conftest import gpg_ver
|
||||
|
||||
@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
|
||||
def abe_image():
|
||||
@@ -45,14 +24,11 @@ def abe_image():
|
||||
return PGPUID.new(abebytes)
|
||||
|
||||
|
||||
class TestPGPMessage(object):
|
||||
params = {
|
||||
'msgfile': sorted(glob.glob('tests/testdata/messages/*.asc')),
|
||||
}
|
||||
ids = {
|
||||
'test_load_from_file': [ os.path.basename(f) for f in params['msgfile'] ],
|
||||
}
|
||||
_msgfiles = sorted(glob.glob('tests/testdata/messages/*.asc'))
|
||||
|
||||
|
||||
class TestPGPMessage(object):
|
||||
@pytest.mark.parametrize('msgfile', _msgfiles, ids=[os.path.basename(f) for f in _msgfiles])
|
||||
def test_load_from_file(self, msgfile):
|
||||
# TODO: figure out a good way to verify that all went well here, because
|
||||
# PGPy reorders signatures sometimes, and also unwraps compressed messages
|
||||
@@ -65,6 +41,31 @@ class TestPGPMessage(object):
|
||||
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):
|
||||
def test_userid(self, abe):
|
||||
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>')
|
||||
|
||||
|
||||
_keyfiles = sorted(glob.glob('tests/testdata/blocks/*key*.asc'))
|
||||
|
||||
|
||||
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):
|
||||
key, _ = PGPKey.from_file(self.kf)
|
||||
# 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
|
||||
|
||||
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):
|
||||
with open(self.kf, 'r') as tkf:
|
||||
@pytest.mark.parametrize('kf', _keyfiles, ids=[os.path.basename(f) for f in _keyfiles])
|
||||
def test_load_from_str(self, kf, gpg_keyid_file):
|
||||
with open(kf, 'r') as tkf:
|
||||
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)
|
||||
def test_load_from_bytes(self, gpg_keyid_file):
|
||||
with open(self.kf, 'rb') as tkf:
|
||||
@pytest.mark.parametrize('kf', _keyfiles, ids=[os.path.basename(f) for f in _keyfiles])
|
||||
def test_load_from_bytes(self, kf, gpg_keyid_file):
|
||||
with open(kf, 'rb') as tkf:
|
||||
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)
|
||||
def test_load_from_bytearray(self, gpg_keyid_file):
|
||||
tkb = bytearray(os.stat(self.kf).st_size)
|
||||
with open(self.kf, 'rb') as tkf:
|
||||
@pytest.mark.parametrize('kf', _keyfiles, ids=[os.path.basename(f) for f in _keyfiles])
|
||||
def test_load_from_bytearray(self, kf, gpg_keyid_file):
|
||||
tkb = bytearray(os.stat(kf).st_size)
|
||||
with open(kf, 'rb') as tkf:
|
||||
tkf.readinto(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')
|
||||
|
||||
@@ -7,7 +7,6 @@ import copy
|
||||
import glob
|
||||
import inspect
|
||||
import os.path
|
||||
|
||||
import six
|
||||
|
||||
import pgpy
|
||||
@@ -15,6 +14,10 @@ import pgpy
|
||||
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():
|
||||
return PGPSignature.from_file('tests/testdata/blocks/rsasignature.asc')
|
||||
|
||||
@@ -42,65 +45,58 @@ def walk_obj(obj, prefix=""):
|
||||
yield n, v
|
||||
|
||||
|
||||
_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 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
|
||||
# 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):
|
||||
params = {
|
||||
'obj': [sig(), uid()] + [ PGPMessage.from_file(m) for m in _msgs ] + [ key(fn) for fn in _keys ],
|
||||
}
|
||||
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 ],
|
||||
}
|
||||
def ksort(key):
|
||||
# return a tuple of key, key.count('.') so we get a descending alphabetical, ascending depth ordering
|
||||
return key, key.count('.')
|
||||
|
||||
@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
|
||||
# these types are singletons
|
||||
if isinstance(obj, (Enum, bool, type(None))):
|
||||
return False
|
||||
objs = [sig(), uid(),] + [PGPMessage.from_file(m) for m in _msgs] + [key(f) for f in _keys]
|
||||
cids = ['sig', 'uid',] + [os.path.basename(m) for m in _msgs] + [os.path.basename(f) for f in _keys]
|
||||
|
||||
# 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
|
||||
@pytest.mark.parametrize('obj', objs, ids=cids)
|
||||
def test_copy_obj(request, obj):
|
||||
obj2 = copy.copy(obj)
|
||||
|
||||
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
|
||||
def ksort(key):
|
||||
# return a tuple of key, key.count('.') so we get a descending alphabetical, ascending depth ordering
|
||||
return key, key.count('.')
|
||||
for k in sorted(objflat, key=ksort):
|
||||
print("checking attribute: {} ".format(k), end="")
|
||||
if isinstance(objflat[k], pgpy.types.SorteDeque):
|
||||
print("[SorteDeque] ", end="")
|
||||
assert len(objflat[k]) == len(obj2flat[k])
|
||||
|
||||
def test_copy_obj(self, request, obj):
|
||||
obj2 = copy.copy(obj)
|
||||
if not isinstance(objflat[k], (pgpy.types.PGPObject, pgpy.types.SorteDeque)):
|
||||
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))}
|
||||
obj2flat = {name: val for name, val in walk_obj(obj2, '{}.'.format(request.node.callspec.id))}
|
||||
# check identity, but only types that should definitely be copied
|
||||
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):
|
||||
print("checking attribute: {} ".format(k), end="")
|
||||
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()
|
||||
else:
|
||||
print()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,29 +3,24 @@
|
||||
import pytest
|
||||
|
||||
import glob
|
||||
|
||||
from pgpy import PGPKey
|
||||
from pgpy import PGPKeyring
|
||||
from pgpy import PGPMessage
|
||||
from pgpy import PGPSignature
|
||||
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 HashAlgorithm
|
||||
from pgpy.constants import KeyFlags
|
||||
from pgpy.constants import PubKeyAlgorithm
|
||||
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 PGPEncryptionError
|
||||
from pgpy.errors import PGPError
|
||||
from pgpy.errors import PGPInsecureCipher
|
||||
|
||||
|
||||
@@ -63,6 +58,7 @@ def targette_pub():
|
||||
def temp_subkey():
|
||||
return PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 512)
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def temp_key():
|
||||
u = PGPUID.new('User')
|
||||
@@ -85,6 +81,7 @@ key_algs_badsizes = {
|
||||
PubKeyAlgorithm.ECDSA: [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):
|
||||
@@ -133,7 +130,6 @@ class TestMetaDispatchable(object):
|
||||
else:
|
||||
Packet(d)
|
||||
|
||||
|
||||
# ensure the base packet works, first
|
||||
Packet(data[:])
|
||||
|
||||
@@ -150,25 +146,6 @@ class TestMetaDispatchable(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):
|
||||
with rsa_pub.unlock("QwertyUiop") as _unlocked:
|
||||
assert _unlocked is rsa_pub
|
||||
@@ -304,15 +281,18 @@ class TestPGPKey(object):
|
||||
with pytest.raises(PGPError):
|
||||
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):
|
||||
key_alg, key_size = badkey
|
||||
with pytest.raises(ValueError):
|
||||
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):
|
||||
with pytest.raises(NotImplementedError):
|
||||
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):
|
||||
k = PGPKey.new(key_alg_rsa_depr, 512)
|
||||
|
||||
@@ -405,9 +385,10 @@ class TestPGPMessage(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):
|
||||
PGPSignature() | 12
|
||||
PGPSignature() | inp
|
||||
|
||||
def test_parse_wrong_magic(self):
|
||||
sigtext = _read('tests/testdata/blocks/signature.expired.asc').replace('SIGNATURE', 'SIGANTURE')
|
||||
@@ -421,12 +402,6 @@ class TestPGPSignature(object):
|
||||
with pytest.raises(ValueError):
|
||||
sig.parse(notsigtext)
|
||||
|
||||
def test_or_typeerror(self):
|
||||
sig = PGPSignature()
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
sig |= None
|
||||
|
||||
|
||||
class TestPGPUID(object):
|
||||
def test_or_typeerror(self):
|
||||
|
||||
@@ -1,149 +1,195 @@
|
||||
""" I've got 99 problems but regression testing ain't one
|
||||
"""
|
||||
import pytest
|
||||
import tempfile
|
||||
import warnings
|
||||
|
||||
|
||||
class TestRegressions(object):
|
||||
# regression tests for actions
|
||||
def test_reg_bug_56(self, write_clean, gpg_import, gpg_verify):
|
||||
# some imports only used by this regression test
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
@pytest.mark.regression(issue=56)
|
||||
def test_reg_bug_56(gpg_import, gpg_verify):
|
||||
# some imports only used by this regression test
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
|
||||
from pgpy.pgp import PGPKey
|
||||
from pgpy.pgp import PGPSignature
|
||||
from pgpy.pgp import PGPKey
|
||||
from pgpy.pgp import PGPSignature
|
||||
|
||||
from pgpy.constants import HashAlgorithm
|
||||
from pgpy.constants import PubKeyAlgorithm
|
||||
from pgpy.constants import SignatureType
|
||||
from pgpy.constants import HashAlgorithm
|
||||
from pgpy.constants import PubKeyAlgorithm
|
||||
from pgpy.constants import SignatureType
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
|
||||
# do a regression test on 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
|
||||
# do a regression test on 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
|
||||
|
||||
# this was the old seckeys/TestRSA-2048.key
|
||||
sec = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" \
|
||||
"\n" \
|
||||
"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\n" \
|
||||
"iz54ZFyNdQ==\n" \
|
||||
"=WLpc\n" \
|
||||
"-----END PGP PRIVATE KEY BLOCK-----\n"
|
||||
# this was the old seckeys/TestRSA-2048.key
|
||||
sec = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" \
|
||||
"\n" \
|
||||
"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\n" \
|
||||
"iz54ZFyNdQ==\n" \
|
||||
"=WLpc\n" \
|
||||
"-----END PGP PRIVATE KEY BLOCK-----\n"
|
||||
|
||||
pub = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" \
|
||||
"\n" \
|
||||
"mQENBFOaNYoBCAC9FDOrASOxJoo3JhwCCln4wPy+A/UY1H0OYlc+MMolSwev2uDj\n" \
|
||||
"nnwmt8ziNTjLuLEh3AnKjDWA9xvY5NW2ZkB6Edo6HQkquKLH7AkpLd82kiTWHdOw\n" \
|
||||
"OH7OWQpz7z2z6e40wwHEduhyGcZ/Ja/A0+6GIb/2YFKlkwfnT92jtB94W//mL6wu\n" \
|
||||
"LOkZMoU/CS/QatervzAf9VCemvAR9NI0UJc7Y0RC1B/1cBTQAUg70EhjnmJkqyYx\n" \
|
||||
"yWqaXyfX10dsEX3+MyiP1kvUDfFwhdeL7E2H9sbFE5+MC9Eo99/Qezv3QoXzH2Tj\n" \
|
||||
"bTun+QMVkbM92dj70KiExAJya9lSLZGCoOrDABEBAAG0M1Rlc3RSU0EtMjA0OCAo\n" \
|
||||
"VEVTVElORy1VU0UtT05MWSkgPGVtYWlsQGFkZHJlc3MudGxkPokBNwQTAQoAIQUC\n" \
|
||||
"U5o1igIbIwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRDA8iEODxk9zYQPB/4+\n" \
|
||||
"kZlIwLE27J7IiZSkk+4T5CPrASxoSsRMadUvoHc0eiZIlQD2Gu05oQcm4kZojJAz\n" \
|
||||
"Mv12rLtk+ZPwVOZU/TUxPYwuEyJP4keFJEW9P0GiURAvYQRQCbQ5IOlIkZ0tPotb\n" \
|
||||
"010Ej3u5rHAiVCvh/cxF16UhkXknf/wgDDWErfGIMaaruAIr0G05p4Q2G/NLgBcc\n" \
|
||||
"owSgFFfWprg3zfNPEQhH/qNs8O5mByniMZk4n2TsKGlX6eT9RrfJVQhSLoQXxYik\n" \
|
||||
"MtiZTki4yPUhTQev62KWHQcY6zNV2p9VQ24NUhVCIBnZ0CLkm38QFsS5flWVGat5\n" \
|
||||
"kraHTXxvffz7yGHJiFkiuQENBFOaNYoBCADBPjB83l1O2m/Nr5KDm6/BwKfrRsoJ\n" \
|
||||
"DmMZ8nNHNUc/zK4RI4EFKkr35PSmgbA8yOlaSDWVz9zuKyOtb8Nohct2/lrac8zI\n" \
|
||||
"+b4enZ/Z6qehoAdY1t4QYmA2PebKuerBXjIF1RWsPQDpu3GIZw4oBbdu5oUGB4I9\n" \
|
||||
"yIepindM2b2I9dlY3ct4uhRbBmXPFcslmJ1K4pCurXvr4Po4DCcWqUmsGUQQbI1G\n" \
|
||||
"UyAzSad7u9y3CRqhHFwzyFRRfl+/mgB2a6XvbGlG5Dkp1g7T/HIVJu+zv58AQkFw\n" \
|
||||
"+ABuWNKCXa3TB51bkiBQlkRTSAu2tVZ8hVGZE+wUw0o9rLiy6mldFvbLABEBAAGJ\n" \
|
||||
"AR8EGAEKAAkFAlOaNYoCGwwACgkQwPIhDg8ZPc1uDwf/SGoiZHjUsTWm4gZgZCzA\n" \
|
||||
"jOpZs7dKjLL8Wm5G3HTFIGX0O8HCzQJARWq05N6EYmI4nPXxu08ba30SubybSeFU\n" \
|
||||
"+iAPymqm2YNXrE2RwLWko78M0r9enUep6SvbGKnukPG7lz/33PsxIVyATfMmcmzV\n" \
|
||||
"4chyC7pICTwgHv/zC3S/k7GoS82Z39LO4R4aDa4aubNq6mx4eHUd0MSnYud1IzRx\n" \
|
||||
"D8cPxh9fCdoW0OpddqKNczAvO4bl5wwDafrEa7HpIX/sMVMZXo2h6TkitdLCdEfk\n" \
|
||||
"tgEjS0hTsFtfwsXt9TKi1x3HJIbcm8t78ubpWXepB/iNKVzv4punFHhKiz54ZFyN\n" \
|
||||
"dQ==\n" \
|
||||
"=lqIH\n" \
|
||||
"-----END PGP PUBLIC KEY BLOCK-----\n"
|
||||
pub = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" \
|
||||
"\n" \
|
||||
"mQENBFOaNYoBCAC9FDOrASOxJoo3JhwCCln4wPy+A/UY1H0OYlc+MMolSwev2uDj\n" \
|
||||
"nnwmt8ziNTjLuLEh3AnKjDWA9xvY5NW2ZkB6Edo6HQkquKLH7AkpLd82kiTWHdOw\n" \
|
||||
"OH7OWQpz7z2z6e40wwHEduhyGcZ/Ja/A0+6GIb/2YFKlkwfnT92jtB94W//mL6wu\n" \
|
||||
"LOkZMoU/CS/QatervzAf9VCemvAR9NI0UJc7Y0RC1B/1cBTQAUg70EhjnmJkqyYx\n" \
|
||||
"yWqaXyfX10dsEX3+MyiP1kvUDfFwhdeL7E2H9sbFE5+MC9Eo99/Qezv3QoXzH2Tj\n" \
|
||||
"bTun+QMVkbM92dj70KiExAJya9lSLZGCoOrDABEBAAG0M1Rlc3RSU0EtMjA0OCAo\n" \
|
||||
"VEVTVElORy1VU0UtT05MWSkgPGVtYWlsQGFkZHJlc3MudGxkPokBNwQTAQoAIQUC\n" \
|
||||
"U5o1igIbIwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRDA8iEODxk9zYQPB/4+\n" \
|
||||
"kZlIwLE27J7IiZSkk+4T5CPrASxoSsRMadUvoHc0eiZIlQD2Gu05oQcm4kZojJAz\n" \
|
||||
"Mv12rLtk+ZPwVOZU/TUxPYwuEyJP4keFJEW9P0GiURAvYQRQCbQ5IOlIkZ0tPotb\n" \
|
||||
"010Ej3u5rHAiVCvh/cxF16UhkXknf/wgDDWErfGIMaaruAIr0G05p4Q2G/NLgBcc\n" \
|
||||
"owSgFFfWprg3zfNPEQhH/qNs8O5mByniMZk4n2TsKGlX6eT9RrfJVQhSLoQXxYik\n" \
|
||||
"MtiZTki4yPUhTQev62KWHQcY6zNV2p9VQ24NUhVCIBnZ0CLkm38QFsS5flWVGat5\n" \
|
||||
"kraHTXxvffz7yGHJiFkiuQENBFOaNYoBCADBPjB83l1O2m/Nr5KDm6/BwKfrRsoJ\n" \
|
||||
"DmMZ8nNHNUc/zK4RI4EFKkr35PSmgbA8yOlaSDWVz9zuKyOtb8Nohct2/lrac8zI\n" \
|
||||
"+b4enZ/Z6qehoAdY1t4QYmA2PebKuerBXjIF1RWsPQDpu3GIZw4oBbdu5oUGB4I9\n" \
|
||||
"yIepindM2b2I9dlY3ct4uhRbBmXPFcslmJ1K4pCurXvr4Po4DCcWqUmsGUQQbI1G\n" \
|
||||
"UyAzSad7u9y3CRqhHFwzyFRRfl+/mgB2a6XvbGlG5Dkp1g7T/HIVJu+zv58AQkFw\n" \
|
||||
"+ABuWNKCXa3TB51bkiBQlkRTSAu2tVZ8hVGZE+wUw0o9rLiy6mldFvbLABEBAAGJ\n" \
|
||||
"AR8EGAEKAAkFAlOaNYoCGwwACgkQwPIhDg8ZPc1uDwf/SGoiZHjUsTWm4gZgZCzA\n" \
|
||||
"jOpZs7dKjLL8Wm5G3HTFIGX0O8HCzQJARWq05N6EYmI4nPXxu08ba30SubybSeFU\n" \
|
||||
"+iAPymqm2YNXrE2RwLWko78M0r9enUep6SvbGKnukPG7lz/33PsxIVyATfMmcmzV\n" \
|
||||
"4chyC7pICTwgHv/zC3S/k7GoS82Z39LO4R4aDa4aubNq6mx4eHUd0MSnYud1IzRx\n" \
|
||||
"D8cPxh9fCdoW0OpddqKNczAvO4bl5wwDafrEa7HpIX/sMVMZXo2h6TkitdLCdEfk\n" \
|
||||
"tgEjS0hTsFtfwsXt9TKi1x3HJIbcm8t78ubpWXepB/iNKVzv4punFHhKiz54ZFyN\n" \
|
||||
"dQ==\n" \
|
||||
"=lqIH\n" \
|
||||
"-----END PGP PUBLIC KEY BLOCK-----\n"
|
||||
|
||||
# load the keypair above
|
||||
sk = PGPKey()
|
||||
sk.parse(sec)
|
||||
pk = PGPKey()
|
||||
pk.parse(pub)
|
||||
# load the keypair above
|
||||
sk = PGPKey()
|
||||
sk.parse(sec)
|
||||
pk = PGPKey()
|
||||
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,
|
||||
sk.fingerprint.keyid)
|
||||
sig._signature.subpackets['h_CreationTime'][-1].created = datetime(2014, 8, 6, 23, 28, 51)
|
||||
sig._signature.subpackets.update_hlen()
|
||||
hdata = sig.hashdata(sigsubject)
|
||||
sig._signature.hash2 = hashlib.new('sha512', hdata).digest()[:2]
|
||||
sig = PGPSignature.new(SignatureType.BinaryDocument, PubKeyAlgorithm.RSAEncryptOrSign, HashAlgorithm.SHA512,
|
||||
sk.fingerprint.keyid)
|
||||
sig._signature.subpackets['h_CreationTime'][-1].created = datetime(2014, 8, 6, 23, 28, 51)
|
||||
sig._signature.subpackets.update_hlen()
|
||||
hdata = sig.hashdata(sigsubject)
|
||||
sig._signature.hash2 = hashlib.new('sha512', hdata).digest()[:2]
|
||||
|
||||
# create the signature
|
||||
signer = sk.__key__.__privkey__().signer(padding.PKCS1v15(), hashes.SHA512())
|
||||
signer.update(hdata)
|
||||
sig._signature.signature.from_signer(signer.finalize())
|
||||
sig._signature.update_hlen()
|
||||
# create the signature
|
||||
signer = sk.__key__.__privkey__().signer(padding.PKCS1v15(), hashes.SHA512())
|
||||
signer.update(hdata)
|
||||
sig._signature.signature.from_signer(signer.finalize())
|
||||
sig._signature.update_hlen()
|
||||
|
||||
# check encoding
|
||||
assert sig._signature.signature.md_mod_n.to_mpibytes()[2:3] != b'\x00'
|
||||
# check encoding
|
||||
assert sig._signature.signature.md_mod_n.to_mpibytes()[2:3] != b'\x00'
|
||||
|
||||
# with PGPy
|
||||
assert pk.verify(sigsubject, sig)
|
||||
# with PGPy
|
||||
assert pk.verify(sigsubject, sig)
|
||||
|
||||
# with GnuPG
|
||||
with write_clean('tests/testdata/subj', 'w', sigsubject.decode('latin-1')), \
|
||||
write_clean('tests/testdata/subj.asc', 'w', str(sig)), \
|
||||
write_clean('tests/testdata/pub.asc', 'w', str(pk)), \
|
||||
gpg_import('pub.asc'):
|
||||
assert gpg_verify('subj', 'subj.asc')
|
||||
# with GnuPG
|
||||
with tempfile.NamedTemporaryFile('w+') as subjf, \
|
||||
tempfile.NamedTemporaryFile('w+') as sigf, \
|
||||
tempfile.NamedTemporaryFile('w+') as pubf:
|
||||
subjf.write(sigsubject.decode('latin-1'))
|
||||
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]
|
||||
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
|
||||
|
||||
[pytest]
|
||||
addopts = -vv -s --color=yes
|
||||
addopts = -vv -r a -s --color=yes
|
||||
norecursedirs = testdata
|
||||
|
||||
[flake8]
|
||||
@@ -21,6 +21,7 @@ deps =
|
||||
singledispatch
|
||||
pytest
|
||||
pytest-cov
|
||||
pytest-ordering
|
||||
|
||||
install_command = pip install {opts} --no-cache-dir {packages}
|
||||
commands =
|
||||
@@ -32,6 +33,12 @@ commands =
|
||||
pip install -e .
|
||||
rm -rf PGPy.egg-info
|
||||
|
||||
[testenv:setup36]
|
||||
recreate = True
|
||||
basepython = python3.6
|
||||
whitelist_externals = {[test-setup]whitelist_externals}
|
||||
commands = {[test-setup]commands}
|
||||
|
||||
[testenv:setup35]
|
||||
recreate = True
|
||||
basepython = python3.5
|
||||
@@ -58,7 +65,7 @@ whitelist_externals = {[test-setup]whitelist_externals}
|
||||
commands = {[test-setup]commands}
|
||||
|
||||
[testenv:pep8]
|
||||
basepython = python3.5
|
||||
basepython = python3.6
|
||||
deps =
|
||||
flake8
|
||||
pep8-naming
|
||||
|
||||
Reference in New Issue
Block a user