python-pskc-1.3/0000755000000000000000000000000014667356044013611 5ustar rootroot00000000000000python-pskc-1.3/.github/0000755000000000000000000000000014667356044015151 5ustar rootroot00000000000000python-pskc-1.3/.github/workflows/0000755000000000000000000000000014667356044017206 5ustar rootroot00000000000000python-pskc-1.3/.github/workflows/test.yml0000644000000000000000000000477414667346555020732 0ustar rootroot00000000000000--- name: Test on: push: pull_request: schedule: - cron: '9 0 * * 1' jobs: base: runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: python-version: [3.6, 3.7, 3.8, 3.9, '3.10', 3.11, 3.12, pypy3.8, pypy3.9] flavour: [signxml] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: python -m pip install --upgrade pip tox - name: Install development libraries if: "${{ startsWith(matrix.python-version, 'pypy') }}" run: sudo apt-get update && sudo apt-get install -y libxml2-dev libxslt-dev - name: Run tox run: tox -e "$(echo py${{ matrix.python-version }}-${{ matrix.flavour }} | sed -e 's/[.]//g;s/pypypy/pypy/')" --skip-missing-interpreters false alternatives: runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: [3.11] flavour: [legacy, legacy-defusedxml, lxml, lxml-defusedxml] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: python -m pip install --upgrade pip tox - name: Run tox run: tox -e "$(echo py${{ matrix.python-version }}-${{ matrix.flavour }} | sed -e 's/[.]//g;s/pypypy/pypy/')" --skip-missing-interpreters false tox_job: runs-on: ubuntu-latest strategy: fail-fast: false matrix: tox_job: [docs, flake8, codespell] steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: 3.x - name: Install dependencies run: python -m pip install --upgrade pip tox - name: Run tox ${{ matrix.tox_job }} run: tox -e ${{ matrix.tox_job }} --skip-missing-interpreters false CodeQL: runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write steps: - name: Checkout repository uses: actions/checkout@v3 - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: python - name: Build uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 python-pskc-1.3/.gitignore0000644000000000000000000000016113036777211015567 0ustar rootroot00000000000000# global *.pyc *.pyo __pycache__ # / /*.egg /*.egg-info /.coverage /.tox /build /coverage /dist /output.pskcxml python-pskc-1.3/COPYING0000644000000000000000000006350412323560741014640 0ustar rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! python-pskc-1.3/ChangeLog0000644000000000000000000022674614667353455015407 0ustar rootroot000000000000002024-09-08 Arthur de Jong * [d790ba4] tests/test_misc.doctest: Remove tests reliance on current time This ensures that the tests that compare the current time to expiry times should either set an explicity current time or use values relative to the current time. 2024-09-08 Arthur de Jong * [c002d92] setup.cfg, tests/test_write.doctest, tox.ini: Have tests fail on missing coverage This also only prints missed coverage on the terminal after the tests. Fixes 1d6e115 (removed requirement) and e99f7c8 (reduced coverage below 100%) 2024-09-08 Arthur de Jong * [c3f744d] tox.ini: Stop testing pypy (not 3) 2024-08-27 Arthur de Jong * [ea5db6b] pskc/scripts/csv2pskc.py, tests/certificate/README, tests/certificate/ca-certificate.pem, tests/certificate/ca-key.pem, tests/certificate/certificate.pem, tests/certificate/key.pem, tests/certificate/request.pem, tests/certificate/ss-certificate.pem, tox.ini: Replace test certificates The cryptography library now requires certificates to be in X509v3 format and also requires certain X509v3 extensions to be present. 2024-07-17 Arthur de Jong * [e99f7c8] .github/workflows/test.yml, pskc/crypto/aeskw.py, pskc/encryption.py, pskc/key.py, pskc/serialiser.py, setup.py, tox.ini: Drop Python 2 support It is getting more and more annoying to support Python 2 due to difficulty in testing. 2024-05-19 Arthur de Jong * [5cbd43f] .github/workflows/test.yml, setup.py, tox.ini: Drop support for Python 3.5 We don't have an easy way to test with Python 3.5 any more. 2023-11-12 Arthur de Jong * [4257500] .github/workflows/test.yml, setup.cfg, tox.ini: Configure testing with codespell 2023-11-12 Arthur de Jong * [348781b] pskc/key.py, tests/test_pskc2csv.doctest: Fix typos (thanks codespell) 2023-11-12 Arthur de Jong * [8f01efd] .github/workflows/test.yml, setup.py, tox.ini: Add support for Python 3.12 2023-03-19 Arthur de Jong * [cce7e02] docs/conf.py, setup.cfg, tox.ini: Ensure flake8 is run on all Python files This also fixes code style fixes in the Sphinx configuration file. 2023-06-27 Arthur de Jong * [fa32f6b] .github/workflows/test.yml: Run Python 2.7 tests in a container for GitHub Actions See https://github.com/actions/setup-python/issues/672 2023-03-19 Arthur de Jong * [b95f075] .github/workflows/test.yml, setup.py, tox.ini: Add support for Python 3.11 This also simplifies the GitHub actions file a bit and updates the PyPy versions to test. 2022-12-12 Arthur de Jong * [d4a8f09] .github/workflows/test.yml: Run most GitHub tests on older Ubuntu The ubuntu-latest image now points to ubuntu-22.04 instead of ubuntu-20.04 before and there are some issues regarding that new image that remain to be worked out. This also switches the default Python 3 implementation to Python 3.9. 2022-12-03 Arthur de Jong * [5690a2b] tox.ini: Support running tests with PyPy 2.7 This also applies the fix from bb594fb from Python 2.7 to PyPy. 2022-12-03 Arthur de Jong * [acc6d78] pskc/signature.py: Support newer versions of signxml Version 3.0.0 of signxml dropped the default_c14n_algorithm property from XMLSignatureProcessor. 2022-12-03 Arthur de Jong * [99422c0] tox.ini: Avoid newer flake8 The new 6.0.0 contains a number of backwards incompatible changes for which plugins need to be updated and configuration needs to be updated. Sadly the maintainer no longer accepts contributions or discussion See https://github.com/PyCQA/flake8/issues/1760 2022-09-12 Arthur de Jong * [f2f0fb5] ChangeLog, NEWS, README, pskc/__init__.py: Get files ready for 1.2 release 2022-09-12 Arthur de Jong * [e95767e] .github/workflows/test.yml: Configure CodeQL code scanning 2022-09-12 Arthur de Jong * [8114316] .github/workflows/test.yml, setup.py, tox.ini: Add support for Python 3.10 2022-09-12 Arthur de Jong * [7c6dfd8] .github/workflows/test.yml: Upgrade GitHub Actions Update checkout to v3 (no relevant changes) and setup-python to v4 (changes the names for pypy versions). 2022-09-12 Arthur de Jong * [1d6e115] setup.cfg, tox.ini: Switch from nose to pytest Nose hasn't seen a release since 2015 and sadly doesn't work with Python 3.10. See https://github.com/nose-devs/nose/issues/1099 2022-09-11 Arthur de Jong * [bb594fb] tox.ini: Support running tests with Python 2.7 When using recent versions of virtualenv this ensures that older versions of pip and setuptools will be used inside the virtualenvs that are created by tox. 2022-09-11 Arthur de Jong * [630012d] csv2pskc.py, pskc2csv.py, pskc2pskc.py, setup.cfg, setup.py, tox.ini: Also run flake8 on toplevel scripts 2022-09-11 Arthur de Jong * [c0733a4] tox.ini: Avoid version of signxml that doesn't work on Python <= 3.5 2022-01-30 Arthur de Jong * [b9e17d3] pskc/key.py, tests/test_write.doctest: Support bytearray for key values Related to https://github.com/arthurdejong/python-pskc/issues/5 2022-01-30 Arthur de Jong * [b543f2a] setup.py, tox.ini: Add support for Python 3.8 and 3.9 2021-08-10 Arthur de Jong * [5c02ecf] pskc/algorithms.py, pskc/key.py, tests/invalid/mac-algorithm.pskcxml, tests/misc/policy.pskcxml: Fix typos found by codespell 2021-07-09 Arthur de Jong * [dce78b0] .github/workflows/test.yml, .travis.yml: Replace Travis with GitHub actions 2021-01-13 Arthur de Jong * [39eaa71] setup.cfg: Ignore flake8 blind except Exception warning 2020-11-07 Arthur de Jong * [0c035f2] .travis.yml: Drop Travis testing for pypy2 Recent distributions no longer carry the older pypy2. This also updates to the config to use bionic instead of xenial. 2020-01-05 Arthur de Jong * [1790ed9] tests/test_signature.doctest: Remove test for PSKC file without certificate Remove this test for now because signxml cannor currently validate this certificate in a backwards compatible way. See https://github.com/XML-Security/signxml/issues/143 2020-01-05 Arthur de Jong * [54b3bb3] .travis.yml: Have Travis fail on missing Python interpreter 2020-01-05 Arthur de Jong * [288a5e6] pskc/xml.py: Support reading from stdin if input is - This is needed for some XML parsing implementations. 2020-01-05 Arthur de Jong * [96e2a8e] docs/pskc2pskc.rst, pskc/key.py: Be consistent in referencing RFC 6030 2019-08-27 Arthur de Jong * [95f65ff] docs/csv2pskc.rst, docs/pskc2pskc.rst, docs/signatures.rst: Fix typos 2019-08-16 Arthur de Jong * [543520a] tox.ini: Add pypy3 tests to tox 2019-08-16 Arthur de Jong * [09979e3] .travis.yml, setup.py, tox.ini: Drop explicit support for Python 3.4 Recent versions of lxml (since 4.4) no longer support Python 3.4 so we no longer run tests for that version of Python. 2019-08-12 Arthur de Jong * [fca4ee1] .travis.yml: Use default dist for Python 3.7 build Travis now defaults to using xenial. 2019-08-12 Arthur de Jong * [9acc216] pskc/xml.py: Force sorting of namespace definitions This ensures that namespace declarations in the generated XML files are ordered alphabetically when using lxml (mostly so our tests are reproducible). 2019-08-12 Arthur de Jong * [ddf3ab1] tox.ini: Drop pinning of pydocstyle now flake8-docstrings has been fixed Reverts e5ec0a1 2019-07-21 Arthur de Jong * [86dccc4] tox.ini: Do not require Python 2 for building Sphinx docs This results in tox using Python 3, mostly to work around https://sourceforge.net/p/docutils/bugs/365/ 2019-07-21 Arthur de Jong * [e5ec0a1] tox.ini: Avoid newer pydocstyle Do not install the latest pydocstyle because it currently breaks flake8-docstring. This pinning should be removed as soon as https://gitlab.com/pycqa/flake8-docstrings/issues/36 is resolved. 2019-02-10 Arthur de Jong * [619ad62] ChangeLog, NEWS, README, docs/conf.py, pskc/__init__.py: Get files ready for 1.1 release 2019-02-10 Arthur de Jong * [21323a0] .travis.yml, setup.py: Add Python 3.7 in Travis and reduce build matrix This runs the signxml flavour on all Python versions and only runs all other flavours on Python 2.6 and 3.6. 2019-02-10 Arthur de Jong * [c2abbec] setup.cfg: Make the multi-line operator place explicit Recent versions of flake8 changed the defaults of the errors to ignore. 2018-07-30 Arthur de Jong * [5e93d32] pskc/crypto/aeskw.py: Ignore more flake8 messages 2018-05-21 Arthur de Jong * [f4b2559] docs/index.rst, docs/scripts.rst: Add links to script documentation 2018-04-21 Arthur de Jong * [610f7cd] : Implement csv2pcks script 2018-04-19 Arthur de Jong * [7bbaac3] docs/csv2pskc.rst, pskc/scripts/csv2pskc.py, tests/test_csv2pskc.doctest: Add --skip-columns option This option can be used to skip a number of rows in the CSV file before the key data is read. If the number of rows to skip is 0, the column interpretation should be provided using the --columns option. 2018-04-05 Arthur de Jong * [88002fc] docs/csv2pskc.rst, pskc/scripts/csv2pskc.py, tests/test_csv2pskc.doctest: Add --set option This option can be used to set key properties for all keys in the PSKC file. 2018-04-05 Arthur de Jong * [e91e498] docs/csv2pskc.rst, pskc/scripts/csv2pskc.py, tests/test_csv2pskc.doctest: Add --columns option This option can be used to override the list of columns as found in the first line of the CSV file or provide a mapping for values found in the first line to PSKC properties. 2018-03-31 Arthur de Jong * [c652eee] csv2pskc.py, docs/conf.py, docs/csv2pskc.rst, pskc/scripts/csv2pskc.py, setup.py, tests/test_csv2pskc.doctest: Add a csv2pskc script for CSV to PSKC conversion This script reads a CSV file and writes out a PSKC file with the key information from the CSV file. The CSV file is expected to have one row for each key and key property values in columns. 2018-04-02 Arthur de Jong * [ce96e69] pskc/scripts/__init__.py, pskc/scripts/pskc2csv.py, pskc/scripts/pskc2pskc.py, pskc/scripts/util.py, pskc2csv.py, pskc2pskc.py, setup.cfg, setup.py, tests/test_pskc2csv.doctest, tests/test_pskc2pskc.doctest, tox.ini: Ship the script as part of the pskc package This also installs pskc2csv and pskc2pskc console script entry points as part of the package installation. 2018-03-03 Arthur de Jong * [7a56eac] pskc/__init__.py, pskc/device.py, tests/test_misc.doctest: Support setting key sub-properties via add_key() 2018-03-11 Arthur de Jong * [e6f2dd4] pskc/encryption.py, tests/test_encryption.doctest, tests/test_pskc2pskc.doctest, tests/test_write.doctest: Increase default PBKDF2 iterations to 100000 2018-02-21 Arthur de Jong * [9026e1c] setup.cfg: Support building a universal wheel 2018-02-15 Arthur de Jong * [b3e7fe7] pskc/__init__.py, pskc/crypto/aeskw.py, pskc/device.py, pskc/encryption.py, pskc/key.py, pskc/parser.py, pskc/serialiser.py, pskc/signature.py, setup.cfg: Add and cleanup docstrings This adds docstrings to public methods and cleans up a few other docstrings to pass most flake8 docstring related tests. This also adds noqa statements in a few places so we can remove most entries from the global flake8 ignore list. 2018-02-10 Arthur de Jong * [03ee35d] docs/conf.py, docs/pskc2pskc.rst, pskc2pskc.py, setup.cfg, tests/test_pskc2pskc.doctest: Add a pskc2pskc script for converting PSKC files This script reads a PSKC file in any supported format and writes out a RFC 6030 compliant version of the file, optionally with the encryption removed or (re-)encrypting the file with a new key. 2018-02-08 Arthur de Jong * [924e1f3] pskc/serialiser.py, tests/test_write.doctest: Correctly write a PSKC file without a MAC key In some cases a PSKC file can be written with a MAC algorithm but without a MAC key. This is possible when the MAC key is not supplied (allowed in older PSKC versions) and a fallback to the encryption key is done. If we have not yet decrypted the file the MAC key is not yet available and so can't be included in the written file. 2018-02-04 Arthur de Jong * [be2b49f] pskc/encryption.py, pskc/serialiser.py, tests/test_write.doctest: Correctly write a PSKC file with a global IV This ensures that the encryption IV, which should be per encrypted value is written out per encrypted value instead of globally. This is mostly useful for when reading an old format PSKC file and writing out a RFC 6030 compliant one. 2018-02-07 Arthur de Jong * [e60d7f3] pskc/mac.py, pskc/parser.py, pskc/serialiser.py: Also use EncryptedValue for MAC key This ensures that an encrypted MAC key is hanled in the same way as normal encrypted data values. This also ensures consistent fallback to the globally configured encryption algorithm if no value has been set in the EncryptedValue. 2018-01-31 Arthur de Jong * [8054c6e] pskc/serialiser.py: Always output a PSKC 1.0 format file This ignores the value of the version attribute in the PSKC object and always writes a PSKC 1.0 (RFC 6030) format file. 2018-01-31 Arthur de Jong * [97faa13] docs/encryption.rst, pskc/encryption.py, tests/test_encryption.doctest, tests/test_write.doctest: Implement removing encryption This adds a function to decrypt all values and remove the encryption of an encrypted PSKC file. 2018-02-08 Arthur de Jong * [2698657] .travis.yml: Add a Travis configuration file 2018-02-04 Arthur de Jong * [82fa3bd] pskc/encryption.py, pskc/serialiser.py, pskc2csv.py: Fix code style issues Fixes 1ff3237f, 84bfb8a6 and 20bf9c5 2017-12-29 Arthur de Jong * [2693495] tests/test_misc.doctest, tests/test_pskc2csv.doctest, tox.ini: Fixes to test suite This ensures that the tests also work without a TTY and work regardless of the PYTHONWARNINGS and TZ environment variables Fixes cd33833 2017-12-29 Arthur de Jong * [fe63c42] ChangeLog, MANIFEST.in, NEWS, pskc/__init__.py, setup.py: Get files ready for 1.0 release 2017-12-29 Arthur de Jong * [2651e80] tests/test_write.doctest: Not all XML serialisers write namespaces in same order This ignores the namespace declarations in the generated XML files because not all implementations on all environments write these in the same order. 2017-12-29 Arthur de Jong * [44b1353] docs/conf.py, setup.cfg, tox.ini: Add Sphinx documentation checks This also slightly tunes the way Sphinx documentation is built. 2017-12-15 Arthur de Jong * [42be53b] pskc2csv.py, tox.ini: Add support for PyPy 2017-12-15 Arthur de Jong * [660ed65] setup.py, tox.ini: Add support for Python 3.7 2017-12-15 Arthur de Jong * [9cd97c9] README, setup.py: Use README as package long description 2017-12-15 Arthur de Jong * [20bf9c5] docs/encryption.rst, pskc/encryption.py, pskc2csv.py, tests/test_rfc6030.doctest: Add an is_encrypted property This property can be use to see whether the PSKC file needs an additional pre-shared key or passphrase to decrypt any stored information. 2017-12-27 Arthur de Jong * [c365a70] : Implement XML signature checking 2017-12-17 Arthur de Jong * [418f3dc] docs/encryption.rst, docs/index.rst, docs/mac.rst, docs/signatures.rst, docs/usage.rst: Add documentation for signed PSKC files 2017-12-23 Arthur de Jong * [a97ac46] pskc/parser.py, pskc/serialiser.py, pskc/signature.py, pskc/xml.py, setup.py, tests/certificate/README, tests/certificate/ca-certificate.pem, tests/certificate/ca-key.pem, tests/certificate/certificate.pem, tests/certificate/key.pem, tests/certificate/request.pem, tests/certificate/ss-certificate.pem, tests/rfc6030/figure9.pskcxml, tests/test_draft_ietf_keyprov_pskc_02.doctest, tests/test_rfc6030.doctest, tests/test_signature.doctest, tox.ini: Implement signature checking This adds support for creating and verifying embedded XML signatures in PSKC files. This uses the third-party signxml library for actual signing and verification. The signxml library has a dependency on lxml and defusedxml (and a few others) but all parts of python-pskc still work correctly with our without lxml and/or defusedxml and signxml is only required when working with embedded signatures. This modifies the tox configuration to skip the signature checks if singxml is not installed and to only require 100% code coverage if the signature tests are done. 2017-12-15 Arthur de Jong * [c0bd21f] pskc/xml.py: Move namespace moving to own function 2017-09-22 Arthur de Jong * [ea503d6] pskc/__init__.py, pskc/parser.py, pskc/signature.py, tests/test_draft_ietf_keyprov_pskc_02.doctest, tests/test_rfc6030.doctest: Implement basic parsing of signature properties 2017-12-23 Arthur de Jong * [fcc6cdb] pskc2csv.py: Explicitly close output file in pskc2csv This ensures that the file descriptor is closed if we opened the file. This is not a big problem for the script (because the script exists anyway) but causes problems for the tests. 2017-12-18 Arthur de Jong * [052f5bc] docs/policy.rst, pskc/parser.py, pskc/policy.py, pskc/serialiser.py, tests/test_misc.doctest, tests/test_write.doctest: Fix typo in pin_max_failed_attempts attribute This makes the old name (pin_max_failed_attemtps) available as a deprecated property. 2017-12-15 Arthur de Jong * [6f0ca70] pskc/parser.py, tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/non-encrypted.pskcxml, tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/password-encrypted.pskcxml, tests/draft-hoyer-keyprov-portable-symmetric-key-container-01/non-encrypted.pskcxml, tests/draft-hoyer-keyprov-portable-symmetric-key-container-01/password-encrypted.pskcxml, tests/test_draft_hoyer_keyprov_portable_symmetric_key_container.doctest: Add limited support for very old draft PSKC versions This adds basic support for parsing the PSKC files as specified in draft-hoyer-keyprov-portable-symmetric-key-container-00 and draft-hoyer-keyprov-portable-symmetric-key-container-01. It should be able to extract secrets, counters, etc. but not all properties from the PSKC file are supported. It is speculated that this format resembles the "Verisign PSKC format" that some applications produce. 2016-09-19 Arthur de Jong * [9b85634] tests/multiotp/pskc-hotp-aes.txt, tests/multiotp/pskc-hotp-pbe.txt, tests/multiotp/pskc-totp-aes.txt, tests/multiotp/pskc-totp-pbe.txt, tests/multiotp/tokens_hotp_aes.pskc, tests/multiotp/tokens_hotp_pbe.pskc, tests/multiotp/tokens_ocra_aes.pskc, tests/multiotp/tokens_ocra_pbe.pskc, tests/multiotp/tokens_totp_aes.pskc, tests/multiotp/tokens_totp_pbe.pskc, tests/test_multiotp.doctest: Add test files from multiOTP This adds tests for parsing the files that are shipped as part of the multiOTP test suite. https://www.multiotp.net/ 2017-12-15 Arthur de Jong * [01507af] pskc/key.py, pskc/parser.py, pskc/serialiser.py, tests/misc/partialxml.pskcxml, tests/test_misc.doctest, tests/test_write.doctest: Refactor internal storate of encrypted values This changes the way encrypted values are stored internally before being decrypted. For example, the internal _secret property can now be a decrypted plain value or an EncryptedValue instance instead of always being a DataType, simplifying some things (e.g. all XML encoding/decoding is now done in the corresponding module). This should not change the public API but does have consequences for those who use custom serialisers or parsers. 2017-12-13 Arthur de Jong * [dcf1919] pskc/crypto/aeskw.py, pskc/encryption.py, tests/encryption/kw-camellia128.pskcxml, tests/encryption/kw-camellia192.pskcxml, tests/encryption/kw-camellia256.pskcxml, tests/test_encryption.doctest: Add support for KW-Camellia suite of algorithms 2017-12-13 Arthur de Jong * [364e93d] pskc/encryption.py, tests/encryption/camellia128-cbc.pskcxml, tests/encryption/camellia192-cbc.pskcxml, tests/encryption/camellia256-cbc.pskcxml, tests/test_encryption.doctest: Add support for Camellia-CBC suite of algorithms 2017-10-11 Arthur de Jong * [4c5e046] docs/conf.py, docs/pskc2csv.rst, setup.cfg: Add a manual page for pskc2csv 2017-10-09 Arthur de Jong * [25cb2fc] setup.cfg: Ignore missing docstring in __init__ in flake 2017-09-30 Arthur de Jong * [225e569] pskc/crypto/__init__.py, pskc/crypto/aeskw.py, pskc/crypto/tripledeskw.py, pskc/encryption.py, pskc/mac.py, setup.cfg, setup.py, tests/test_crypto.doctest, tests/test_encryption.doctest, tox.ini: Replace pycrypto with cryptography The cryptography library is better supported. This uses the functions from cryptography for AES and Triple DES encryption, replaces the (un)padding functions that were previously implemented in python-pskc with cryptography and uses PBKDF2 implementation from hashlib. 2017-09-30 Arthur de Jong * [5dff7d4] pskc/encryption.py: Use PBKDF2 from hashlib This uses pbkdf2_hmac() from hashlib for the PBKDF2 calculation. The downside of this is that this function is only available since Python 2.7.8. 2017-09-30 Arthur de Jong * [2c8a9b7] pskc/crypto/aeskw.py, pskc/crypto/tripledeskw.py, pskc/encryption.py, pskc/mac.py, tests/test_aeskw.doctest, tests/test_write.doctest: Replace use of pycrypto utility functions This uses os.urandom() as a source for random data and replaces other utility functions. This also removes one import for getting the lengths of Tripple DES keys. 2017-09-24 Arthur de Jong * [d0eddf8] pskc/serialiser.py, pskc/xml.py, tests/test_write.doctest: Implement our own XML formatting This avoids a using xml.dom.minidom to indent the XML tree and keep the attributes ordered alphabetically. This also allows for customisations to the XML formatting. 2017-09-24 Arthur de Jong * [4ed4e11] tests/test_mac.doctest: Support hashlib from Python 2.7.3 Some Python versions don't have the algorithms_available property but do have the algorithms property in hashlib. 2017-09-24 Arthur de Jong * [b90faeb] pskc/xml.py, setup.py, tox.ini: Use defusedxml if available This uses the defusedxml library if available to defend agains a number of XML-based attacks. 2017-09-23 Arthur de Jong * [7272e54] pskc/serialiser.py, tests/test_write.doctest: Fix bug in saving PBKDF2 salt on Python3 The PBKDF2 salt was saved in the wrong way (b'base64encodeddata' instead of base64encodeddata) when using Python 3. This fixes that problem and tests that saving and loading of a file that uses PBKDF2 key derivation works. 2017-09-23 Arthur de Jong * [cd33833] pskc2csv.py, setup.cfg, tests/test_pskc2csv.doctest: Add tests for the pskc2csv script This makes minor changes to the pskc2csv script to make it more easily testable. 2017-09-22 Arthur de Jong * [6028b8e] pskc2csv.py: Support adding custom CSV file headers This allows adding an optional label to the --columns option that can be used to output a label different from the key property name in the CSV file header. 2017-09-20 Arthur de Jong * [eef681b] pskc2csv.py: Add --secret-encoding option to pskc2csv This option can be used to configure the encoding of the secret in the CSV file (still hex by default). 2017-09-20 Arthur de Jong * [6f78dd6] pskc/__init__.py, pskc/crypto/aeskw.py, pskc/crypto/tripledeskw.py, pskc/exceptions.py, pskc/mac.py, pskc/parser.py, pskc/policy.py, pskc/serialiser.py, setup.cfg, tox.ini: Run flake8 from tox This also makes a few small code formatting changes to ensure that the flake8 tests pass. 2017-09-11 Arthur de Jong * [cc3acc2] tox.ini: Simplify Tox configuration 2017-06-10 Arthur de Jong * [0c00c80] pskc/__init__.py, pskc/encryption.py, pskc/parser.py, pskc/serialiser.py, pskc/xml.py, pskc2csv.py: Various minor code style improvements 2017-06-10 Arthur de Jong * [510e6a5] pskc/encryption.py, pskc/parser.py: Normalise key derivation algorithms This makes KeyDerivation.algorithm and KeyDerivation.pbkdf2_prf properties automatically normalise assigned values. 2017-06-10 Arthur de Jong * [d72e6cc] pskc/xml.py: Switch to using non-deprecated method This uses ElementTree.iter() instead of ElementTree.getiterator() for going over all the child elements in the tree because the latter is deprecated. 2017-06-10 Arthur de Jong * [7b106ff] docs/usage.rst, pskc/key.py, tests/test_yubico.doctest: Provide Key.userid convenience property This provides a read-only userid property on Key objects that uses the key_userid or device_userid value, whichever one is defined. 2017-06-09 Arthur de Jong * [f0d2991] docs/conf.py, docs/encryption.rst, docs/exceptions.rst, docs/mac.rst: Document supported encryption and MAC algorithms This also includes a few other small documentation improvements. 2017-06-09 Arthur de Jong * [8b8848d] pskc/encryption.py, pskc/mac.py, tests/test_invalid.doctest, tests/test_mac.doctest: Refactor MAC lookups This switches to using the hashlib.new() function to be able to use all hashes that are available in Python (specifically RIPEMD160). This also adds a number of tests for HMACs using test vectors from RFC 2202, RFC 4231 and RFC 2857. 2017-06-09 Arthur de Jong * [e10f9c6] pskc/algorithms.py: Handle more algortihm URIs This adds a number of algorithm URIs defined in RFC 6931 and also simplifies the definition of the list of URIs. It also adds more aliases for algorithms. 2017-01-25 Arthur de Jong * [1fc1a03] README, docs/usage.rst, setup.py: Switch URLs to HTTPS 2017-01-21 Arthur de Jong * [8de25c2] tests/actividentity/test.pskcxml, tests/test_actividentity.doctest: Correct name of ActivIdentity test file 2017-01-21 Arthur de Jong * [5889df7] ChangeLog, NEWS, README, docs/conf.py, pskc/__init__.py, pskc2csv.py, setup.py: Get files ready for 0.5 release 2017-01-15 Arthur de Jong * [29a183d] tests/test_feitian.doctest, tests/test_nagraid.doctest: Split vendor tests Have one doctest file per vendor to make tests a little more manageable. 2017-01-14 Arthur de Jong * [02eb520] tests/test_yubico.doctest, tests/yubico/example1.pskcxml, tests/yubico/example2.pskcxml, tests/yubico/example3.pskcxml: Add tests for Yubikey files This adds tests from draft-josefsson-keyprov-pskc-yubikey-00. 2017-01-13 Arthur de Jong * [12dfa64] pskc/parser.py, tests/actividentity/test.pskc, tests/test_actividentity.doctest: Support legacy ActivIdentity files This adds support for parsing ActivIdentity files that conform to a very old version of an Internet Draft. The implementation and test were based on a file provided by Jaap Ruijgrok. 2017-01-11 Arthur de Jong * [a5e2343] pskc/parser.py, tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/actividentity-3des.pskcxml, tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/ocra.pskcxml, tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/securid-aes-counter.pskcxml, tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/totp.pskcxml: Use original examples from old profiles Internet Draft This updates the tests to use the original examples from draft-hoyer-keyprov-pskc-algorithm-profiles-01 instead of modifying them to fit the RFC 6030 schema (but does include some minor changes to make them valid XML). This adds a few additions to the parser to handle legacy challenge and resposne encoding and a few key policy properties. This also includes a fix for 0b757ec in the handling of the element under a element. 2016-12-21 Arthur de Jong * [2f7cb1a] tests/rfc6030/figure8.pskcxml, tests/rfc6030/figure9.pskcxml, tests/test_rfc6030.doctest: Add all figures from RFC 6030 to test suite Note that asymmetric encryption and digital signature checking has not yet been implemented so the tests are pretty minimal. 2016-12-21 Arthur de Jong * [0b757ec] pskc/parser.py, pskc/xml.py, tests/draft-ietf-keyprov-pskc-02/figure2.pskcxml, tests/draft-ietf-keyprov-pskc-02/figure3.pskcxml, tests/draft-ietf-keyprov-pskc-02/figure4.pskcxml, tests/draft-ietf-keyprov-pskc-02/figure5.pskcxml, tests/draft-ietf-keyprov-pskc-02/figure6.pskcxml, tests/draft-ietf-keyprov-pskc-02/figure7.pskcxml, tests/draft-ietf-keyprov-pskc-02/figure8.pskcxml, tests/draft-ietf-keyprov-pskc-02/figure9.pskcxml, tests/test_draft_ietf_keyprov_pskc_02.doctest: Add support for older Internet Draft version This adds support for parsing most examples from draft-ietf-keyprov-pskc-02. That file uses a few other names for elements and attributes of the PSKC file and a few other minor differences. The XML parsing has been changed to allow specifying multiple matches and the find*() functions now return the first found match. While all examples from draft-ietf-keyprov-pskc-02 are tested support for verifying digital signatures and asymmetric keys have not yet been implemented. 2016-12-19 Arthur de Jong * [09076f8] tests/test_encryption.doctest: Fix typo in test 2016-12-20 Arthur de Jong * [46fa5f1] setup.cfg: Fail tests on missing coverage 2016-12-20 Arthur de Jong * [047a2a9] pskc/key.py, pskc/mac.py, tests/encryption/mac-over-plaintext.pskcxml, tests/invalid/mac-missing.pskcxml, tests/invalid/mac-value.pskcxml, tests/invalid/missing-encryption-method.pskcxml, tests/test_encryption.doctest, tests/test_invalid.doctest: Allow MAC over plaintext or ciphertext RFC 6030 implies that the MAC should be performed over the ciphertext but some earlier drafts implied that the MAC should be performed on the plaintext. This change accpets the MAC if either the plaintext or ciphertext match. Note that this change allows for a padding oracle attack when CBC encryption modes are used because decryption (and unpadding) needs to be done before MAC checking. However, this module is not expected to be available to users to process arbitrary PSKC files repeatedly. This removes the tests for a missing MAC key (and replaces it for tests of missing EncryptionMethod) because falling back to using the encryption key (implemented in a444f78) in combination with this change means that decryption is performed before MAC checking and is no longer possible to trigger a missing MAC key error. 2016-12-19 Arthur de Jong * [bae7084] pskc/crypto/__init__.py, pskc/encryption.py, tests/test_crypto.doctest: Add sanity checks to unpadding 2016-12-19 Arthur de Jong * [d864bc8] pskc/serialiser.py: Ensure XML file ends with a newline 2016-12-19 Arthur de Jong * [c631628] pskc/xml.py: Adapt coverage pragma annotations This fixes the pragma directives to be be correct independently of whether lxml is installed or not. 2016-12-19 Arthur de Jong * [18d82dc] .gitignore, tox.ini: Add Tox configuration This sets up Tox with various versions of Python and for each version a run with and without lxml. 2016-12-19 Arthur de Jong * [71058e2] tests/test_write.doctest: Close read files in tests This ensures that the files that are read in the test suite are properly closed to avoid leaking open file descriptors. 2016-12-18 Arthur de Jong * [f0a0a3b] pskc/parser.py: Support missing or lowercase version attribute 2016-09-26 Arthur de Jong * [3bf4737] docs/usage.rst: Fix copy-pasto in documentation This accidentally slipped in as part of beafc6b. 2016-09-19 Arthur de Jong * [02b30a9] pskc/__init__.py, pskc/parser.py, pskc/serialiser.py: Also move outer writing and parsing to modules 2016-09-17 Arthur de Jong * [b1f8f87] .gitignore, README, pskc/__init__.py: Add writing example to toplevel documentation 2016-09-17 Arthur de Jong * [e23a467] pskc/key.py: Use custom data descriptors for key properties This uses a custom data descriptor (property) for secret, counter, time_offset, time_interval and time_drift. 2016-09-17 Arthur de Jong * [beafc6b] docs/usage.rst, pskc/__init__.py, pskc/device.py, pskc/key.py, pskc/parser.py, pskc/policy.py, pskc/serialiser.py, tests/test_misc.doctest, tests/test_write.doctest: Support separate device from key This allows having multiple keys per device while also maintaining the previous API. Note that having multiple keys per device is not allowed by the RFC 6030 schema but is allowed by some older internet drafts. 2016-09-16 Arthur de Jong * [84bfb8a] pskc/__init__.py, pskc/encryption.py, pskc/key.py, pskc/mac.py, pskc/policy.py, pskc/serialiser.py: Move XML generation to own module Similar to the change for parsing, move the XML serialisation of PSKC data to a single class in a separate module. 2016-09-14 Arthur de Jong * [426e821] pskc/__init__.py, pskc/encryption.py, pskc/key.py, pskc/mac.py, pskc/parser.py, pskc/policy.py: Move document parsing to own module This moves all the parse() functions to a single class in a dedicated module that can be used for parsing PSKC files. This should make it easier to subclass the parser. 2016-09-14 Arthur de Jong * [bf34209] tests/invalid/no-mac-method.pskcxml, tests/test_invalid.doctest, tests/test_rfc6030.doctest: Some minor improvements to the tests 2016-09-12 Arthur de Jong * [600ae68] pskc/encryption.py, pskc/key.py, pskc/xml.py, setup.cfg, tests/invalid/empty-mac-key.pskcxml, tests/invalid/incomplete-derivation.pskcxml, tests/invalid/missing-encryption.pskcxml, tests/misc/SampleFullyQualifiedNS.xml, tests/misc/policy.pskcxml, tests/test_aeskw.doctest, tests/test_encryption.doctest, tests/test_invalid.doctest, tests/test_misc.doctest, tests/test_write.doctest: Improve branch coverage This enables branch coverage testing and adds tests to improve coverage. 2016-09-11 Arthur de Jong * [713d106] pskc/encryption.py, tests/test_encryption.doctest: Support specifying PRF in setup_pbkdf2() This also ensures that the PRF URL is normalised. 2016-09-11 Arthur de Jong * [ff811c9] pskc/encryption.py: Fix bug in passing explicit key to setup_preshared_key() 2016-09-11 Arthur de Jong * [fa07aa5] docs/encryption.rst, pskc/encryption.py: Clarify encryption.setup_*() documentation This tries to make it clearer that the setup_preshared_key() and setup_pbkdf2() functions are meant to be used when writing out PSKC files. 2016-04-23 Arthur de Jong * [a444f78] pskc/key.py, pskc/mac.py, tests/encryption/no-mac-key.pskcxml, tests/invalid/mac-missing.pskcxml, tests/test_encryption.doctest, tests/test_invalid.doctest: Fall back to encryption key for MAC This uses the encryption key also as MAC key if no MAC key has been specified in the PSKC file. Earlier versions of the PSKC draft specified this behaviour. 2016-04-23 Arthur de Jong * [9b76135] pskc/encryption.py, tests/encryption/aes128-cbc-noiv.pskcxml, tests/test_encryption.doctest: Allow global specification of IV In older versions of the PSKC standard it was allowed to have a global initialization vector for CBC based encryption algorithms. It is probably not a good idea to re-use an IV in general. 2016-04-23 Arthur de Jong * [d53f05b] pskc/encryption.py, pskc/mac.py: Move crypto to functions This makes it much easier to test the encryption, decryption and HMAC processing separate from the PSKC parsing. 2016-04-05 Arthur de Jong * [5dbfefd] pskc/__init__.py, pskc/encryption.py, pskc/key.py, pskc/policy.py: Remove parse call from constructors This makes the creation if internal instances a litte more consistent. 2016-04-05 Arthur de Jong * [0d7caf1] pskc/algorithms.py, pskc/encryption.py, pskc/mac.py: Move algorithm uri handling to separate module 2016-03-29 Arthur de Jong * [22ba9f1] pskc/crypto/__init__.py, pskc/encryption.py: Move padding functions to crypto package 2016-03-28 Arthur de Jong * [efbe94c] ChangeLog, NEWS, pskc/__init__.py, setup.py: Get files ready for 0.4 release 2016-03-26 Arthur de Jong * [0c57335] docs/policy.rst: Document may_use() policy function 2016-03-27 Arthur de Jong * [b4a6c72] : Implement writing encrypted files This adds support for setting up encryption keys and password-based key derivation when writing PSKC files. Also MAC keys are set up when needed. 2016-03-26 Arthur de Jong * [59aa65b] README, docs/conf.py, docs/encryption.rst, docs/mac.rst, docs/usage.rst, pskc/__init__.py: Document writing encrypted files 2016-03-21 Arthur de Jong * [5f32528] tests/test_write.doctest: Add encryption error tests 2016-03-21 Arthur de Jong * [7ede4a1] tests/test_write.doctest: Add tests for writing encrypted PSKC files 2016-03-20 Arthur de Jong * [1ff3237] pskc/encryption.py: Allow configuring a pre-shared key This method allows configuring a pre-shared encryption key and will chose reasonable defaults for needed encryption values (e.g. it will choose an algorithm, generate a new key of the appropriate length if needed, etc.). 2016-03-19 Arthur de Jong * [50414a3] pskc/encryption.py, tests/test_encryption.doctest: Allow configuring PBKDF2 key derivation This factors out the PBKDF2 key derivation to a separate function and introduces a function to configure KeyDerivation instances with PBKDF2. 2016-03-21 Arthur de Jong * [5ac9d43] pskc/mac.py, tests/test_encryption.doctest: Allow configuring a MAC key This method will set up a MAC key and algorithm as specified or use reasonable defauts. 2016-03-20 Arthur de Jong * [16da531] pskc/key.py, pskc/mac.py: Generate MAC values 2016-03-20 Arthur de Jong * [ca0fa36] pskc/__init__.py, pskc/encryption.py, pskc/mac.py: Write MACMethod This also makes the MAC.algorithm a property similarly as what is done for Encryption (normalise algorithm names) and adds a setter for the MAC.key property. 2016-03-21 Arthur de Jong * [8fd35ba] pskc/encryption.py, pskc/key.py: Write out encrypted values The Encryption class now has a fields property that lists the fields that should be encrypted when writing the PSKC file. This adds an encrypt_value() function that performs the encryption and various functions to convert the plain value to binary before writing the encrypted XML elements. 2016-03-20 Arthur de Jong * [eba541e] pskc/__init__.py, pskc/encryption.py, pskc/mac.py: Make Encryption and MAC constructors consistent This removes calling parse() from the Encryption and MAC constructors and stores a reference to the PSKC object in both objects so it can be used later on. 2016-03-20 Arthur de Jong * [fe21231] pskc/__init__.py, pskc/encryption.py, tests/test_write.doctest: Write encryption key information This writes information about a pre-shared key or PBKDF2 key derivation in the PSKC file. This also means that writing a decrypted version of a previously encrypted file requires actively removing the encryption. 2016-03-19 Arthur de Jong * [0893640] pskc/encryption.py, tests/test_misc.doctest: Add algorithm_key_lengths property This property on the Encryption object provides a list of key sizes (in bytes) that the configured encryption algorithm supports. 2016-03-22 Arthur de Jong * [8b5f6c2] pskc/policy.py, tests/test_misc.doctest, tests/test_rfc6030.doctest, tests/test_write.doctest: Also check key expiry in may_use() 2016-03-20 Arthur de Jong * [dfa57ae] pskc2csv.py: Support reading password or key in pskc2csv This supports reading the encryption password or key from the command line or from a file. 2014-06-28 Arthur de Jong * [0744222] pskc/xml.py: Copy namespaces to toplevel element Ensure that when writing an XML file all namespace definitions are on the toplevel KeyContainer element instead of scattered throughout the XML document. 2016-03-19 Arthur de Jong * [e8ef157] pskc/__init__.py, tests/test_write.doctest: Support writing to text streams in Python 3 This supports writing the XML output to binary streams as well as text streams in Python 3. 2016-03-19 Arthur de Jong * [cadc6d9] pskc/key.py, pskc/mac.py, tests/invalid/missing-encryption.pskcxml, tests/invalid/not-boolean.pskcxml, tests/invalid/not-integer.pskcxml, tests/invalid/not-integer2.pskcxml, tests/invalid/unknown-encryption.pskcxml, tests/test_aeskw.doctest, tests/test_encryption.doctest, tests/test_invalid.doctest, tests/test_misc.doctest, tests/test_rfc6030.doctest, tests/test_tripledeskw.doctest, tests/test_write.doctest: Improve tests and test coverage This adds tests to ensure that incorrect attribute and value types in the PSKC file raise a ValueError exception and extends the tests for invalid encryption options. This removes some code or adds no cover directives to a few places that have unreachable code or are Python version specific and places doctest directives inside the doctests where needed. 2016-03-19 Arthur de Jong * [b8905e0] pskc/key.py, pskc/xml.py, tests/misc/checkdigits.pskcxml, tests/test_misc.doctest: Support both CheckDigit and CheckDigits RFC 6030 is not clear about whether the attribute of ChallengeFormat and ResponseFormat should be the singular CheckDigit or the plural CheckDigits. This ensures that both forms are accepted. 2016-03-19 Arthur de Jong * [7915c55] pskc/policy.py, tests/misc/policy.pskcxml, tests/test_misc.doctest: Implement policy checking This checks for unknown policy elements in the PSKC file and will cause the key usage policy check to fail. 2016-03-18 Arthur de Jong * [1687fd6] tests/feitian/20120919-test001-4282.xml, tests/feitian/file1.pskcxml, tests/nagraid/file1.pskcxml, tests/test_vendors.doctest: Add a few tests for vendor files Some vendor-specific files were lifted from the LinOTP test suite and another Feitian file was found in the oath-toolkit repository. 2016-01-31 Arthur de Jong * [aae8a18] pskc/key.py, tests/misc/integers.pskcxml, tests/test_misc.doctest: Support various integer representations This extends support for handling various encoding methods for integer values in PSKC files. For encrypted files the decrypted value is first tried to be evaluated as an ASCII representation of the number and after that big-endian decoded. For plaintext values first ASCII decoding is tried after which base64 decoding is tried which tries the same encodings as for decrypted values. There should be no possibility for any base64 encoded value (either of an ASCII value or a big-endian value) to be interpreted as an ASCII value for any 32-bit integer. There is a possibility that a big-endian encoded integer could be incorrectly interpreted as an ASCII value but this is only the case for 110 numbers when only considering 6-digit numbers. 2016-01-24 Arthur de Jong * [c86aaea] README, pskc/__init__.py, tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/actividentity-3des.pskcxml, tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/ocra.pskcxml, tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/securid-aes-counter.pskcxml, tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/totp.pskcxml, tests/encryption/aes128-cbc.pskcxml, tests/encryption/aes192-cbc.pskcxml, tests/encryption/aes256-cbc.pskcxml, tests/encryption/kw-aes128.pskcxml, tests/encryption/kw-aes192.pskcxml, tests/encryption/kw-aes256.pskcxml, tests/encryption/kw-tripledes.pskcxml, tests/encryption/tripledes-cbc.pskcxml, tests/invalid/encryption.pskcxml, tests/invalid/mac-algorithm.pskcxml, tests/invalid/mac-value.pskcxml, tests/invalid/no-mac-method.pskcxml, tests/invalid/notxml.pskcxml, tests/invalid/wrongelement.pskcxml, tests/invalid/wrongversion.pskcxml, tests/misc/SampleFullyQualifiedNS.xml, tests/misc/odd-namespace.pskcxml, tests/rfc6030/figure10.pskcxml, tests/rfc6030/figure2.pskcxml, tests/rfc6030/figure3.pskcxml, tests/rfc6030/figure4.pskcxml, tests/rfc6030/figure5.pskcxml, tests/rfc6030/figure6.pskcxml, tests/rfc6030/figure7.pskcxml, tests/test_draft_keyprov.doctest, tests/test_encryption.doctest, tests/test_invalid.doctest, tests/test_misc.doctest, tests/test_rfc6030.doctest, tests/test_write.doctest: Re-organise test files This puts the test PSKC files in subdirectories so they can be organised more cleanly. 2016-01-23 Arthur de Jong * [1904dc2] tests/test_misc.doctest: Add test for incorrect key derivation If no key derivation algorithm has been specified in the PSKC file an exception should be raised when attempting to perform key derivation. 2016-01-24 Arthur de Jong * [91f66f4] pskc/encryption.py, pskc/key.py, pskc/mac.py: Refactor out EncryptedValue and ValueMAC This removes the EncryptedValue and ValueMAC classes and instead moves the XML parsing of these values to the DataType class. This will make it easier to support different parsing schemes. This also includes a small consistency improvement in the subclasses of DataType. 2016-01-23 Arthur de Jong * [9b13d3b] pskc/encryption.py, tests/test_misc.doctest: Normalise algorithm names This transforms the algorithm URIs that are set to known values when parsing or setting the algorithm. 2016-01-22 Arthur de Jong * [b6eab47] docs/encryption.rst, pskc/encryption.py, tests/test_encryption.doctest, tests/test_misc.doctest: Add encryption algorithm property Either determine the encryption algorithm from the PSKC file or from the explicitly set value. This also adds support for setting the encryption key name. 2016-01-22 Arthur de Jong * [b5f7de5] pskc/key.py, tests/test_write.doctest: Fix a problem when writing previously encrypted file This fixes a problem with writing a PSKC file that is based on a read file that was encrypted. 2016-01-22 Arthur de Jong * [107a836] pskc/__init__.py, pskc/encryption.py, pskc/key.py, pskc/mac.py, pskc/policy.py, pskc/xml.py: Strip XML namespaces before parsing This simplifies calls to the find() family of functions and allows parsing PSKC files that have slightly different namespace URLs. This is especially common when parsing old draft versions of the specification. This also removes passing multiple patterns to the find() functions that was introduced in 68b20e2. 2015-12-28 Arthur de Jong * [a86ff8a] README, docs/encryption.rst: Update some documentation This adds a development notes section to the README and changes the wording on the encryption page. 2015-12-01 Mathias Laurin * [0ff4154] docs/encryption.rst: Fix typo in the documentation 2015-12-01 Mathias Laurin * [3473903] pskc2csv.py: Support Python 3 2015-11-30 Mathias Laurin * [a82a60b] pskc/key.py: Make value conversion methods static private - the conversions do not call self: they are static - the conversions are not to be used out of the class: make private 2015-11-30 Mathias Laurin * [e711a30] pskc/key.py: Provide abstract methods to clarify API 2015-11-30 Mathias Laurin * [1577687] pskc/encryption.py: Fix typo in variable name 2015-11-30 Mathias Laurin * [3aa2a6f] tests/test_invalid.doctest: Fix doctest: IGNORE_EXCEPTION_DETAL 2015-10-07 Arthur de Jong * [c155d15] ChangeLog, MANIFEST.in, NEWS, pskc/__init__.py, setup.py: Get files ready for 0.3 release 2015-10-07 Arthur de Jong * [cf0c9e6] README, docs/conf.py, docs/encryption.rst, docs/exceptions.rst, docs/mac.rst, docs/policy.rst, docs/usage.rst, pskc/__init__.py: Update documentation This updates the documentation with the new features (writing PSKC files) as well as many editorial improvements, some rewording and a few typo fixes. Some things were moved around a little in order to be more easily readable and easier to find. 2015-10-06 Arthur de Jong * [671b6e2] pskc/__init__.py, pskc/crypto/aeskw.py, pskc/crypto/tripledeskw.py, pskc/encryption.py, pskc/key.py, pskc/policy.py, pskc/xml.py, setup.py, tests/test_aeskw.doctest, tests/test_draft_keyprov.doctest, tests/test_encryption.doctest, tests/test_invalid.doctest, tests/test_misc.doctest, tests/test_rfc6030.doctest, tests/test_tripledeskw.doctest, tests/test_write.doctest: Support Python 3 This enables support for Python 3 together with Python 2 support with a single codebase. On Python 3 key data is passed around as bytestrings which makes the doctests a little harder to maintain across Python versions. 2015-10-06 Arthur de Jong * [68b20e2] pskc/encryption.py, pskc/xml.py, tests/SampleFullyQualifiedNS.xml, tests/test_misc.doctest: Fix issue with namespaced PBKDF2 parameters The find() utility functions now allow specifying multiple paths to be searched where the first match is returned. This allows handling PSKC files where the PBKDF2 salt, iteration count, key length and PRF elements are prefixed with the xenc11 namespace. A test including such a PSKC file has been included. Thanks to Eric Plet for reporting this. 2014-10-12 Arthur de Jong * [ebe46f2] pskc2csv.py: Provide a sample pskc2csv script This is a simple command-line utility that reads a PSKC file and outputs information on keys as CSV. 2014-06-30 Arthur de Jong * [1363564] pskc/crypto/__init__.py, pskc/crypto/aeskw.py, pskc/crypto/tripledeskw.py, pskc/encryption.py, tests/test_aeskw.doctest, tests/test_tripledeskw.doctest: Move encryption functions in pskc.crypto package This moves the encryption functions under the pskc.crypto package to more clearly separate it from the other code. Ideally this should be replaced by third-party library code. 2014-06-30 Arthur de Jong * [e468ebe] pskc/__init__.py, pskc/encryption.py, pskc/key.py, pskc/mac.py, pskc/policy.py, pskc/xml.py: Rename pskc.parse to pskc.xml This renames the parse module to xml to better reflect the purpose of the module and it's functions. This also introduces a parse() function that wraps etree.parse(). 2014-06-28 Arthur de Jong * [480e2d0] : Support writing unencrypted PSKC files 2014-06-27 Arthur de Jong * [37dc64a] tests/test_write.doctest: Add test for writing PSKC files This makes a simple doctest that checks the writing of the XML representation of the PSKC data. 2014-06-27 Arthur de Jong * [865a755] pskc/__init__.py, pskc/parse.py: Add function for writing XML This provides a function for pretty-printing the generated XML document. 2014-06-27 Arthur de Jong * [61a192f] pskc/__init__.py, pskc/key.py, pskc/policy.py: Construct XML document with basic PKSC information This introduces make_xml() functions to build an XML document that contains the basic PSKC information and keys. This currently only supports writing unencrypted PSKC files. 2014-06-27 Arthur de Jong * [69aec9f] pskc/parse.py: Introduce mk_elem() to create elements This introduces the mk_elem() function that can be used to create ElementTree elements for building XML documents. This function transparetly handles namespaces, translation of values into XML etc. 2014-06-27 Arthur de Jong * [7591271] pskc/key.py: Simplify DataType value handling Only store the native value of the property, not the text representation. This also results in the BinaryDataType and IntegerDataType subclasses only needing from_text() and from_bin() functions. 2014-06-19 Arthur de Jong * [09eb6b3] ChangeLog, NEWS, docs/changes.rst, docs/index.rst, docs/usage.rst, pskc/__init__.py, setup.py: Get files ready for 0.2 release 2014-06-19 Arthur de Jong * [62c9af4] pskc/__init__.py: Only catch normal exceptions 2014-06-18 Arthur de Jong * [deb57d7] pskc/__init__.py: Remove unused import 2014-06-17 Arthur de Jong * [178ef1c] pskc/encryption.py: PEP8 fix 2014-06-17 Arthur de Jong * [7435552] pskc/exceptions.py: Remove __str__ from exception The message property has been deprecated as of Python 2.6 and printing the first argument is the default. 2014-06-16 Arthur de Jong * [f084735] README, docs/encryption.rst, docs/exceptions.rst, docs/index.rst, docs/mac.rst, docs/policy.rst, docs/usage.rst: Update documentation This updates the documentation with the current API, adding information on exceptions raised, HMAC algorithms supported and changes to the MAC checking. This also includes some editorial changes to some of the text and making references shorter by not including the full package path. 2014-06-15 Arthur de Jong * [d84e761] pskc/parse.py: Simplify finding ElementTree implementation These are the only ElementTree implementations that have been tested to provide the needed functionality (mostly namespaces). 2014-06-15 Arthur de Jong * [50b429d] pskc/key.py, pskc/parse.py, pskc/policy.py: Refactor out some functions to parse This introduces the getint() and getbool() functions in parse to avoid some code duplication. 2014-06-15 Arthur de Jong * [9a16ce4] pskc/key.py, tests/test_misc.doctest: Add support for setting secret This supports setters for the secret, counter, time_offset, time_interval and time_drift properties. Setting these values stores the values unencrypted internally. 2014-06-14 Arthur de Jong * [1b9ee9f] pskc/encryption.py: Support PBKDF2 PRF argument Support specifying a pseudorandom function for PBKDF2 key derivation. It currently supports any HMAC that the MAC checking also supports. 2014-06-14 Arthur de Jong * [79b9a7d] pskc/mac.py: Provide a get_hmac() function Refactor the functionality to find an HMAC function into a separate function. 2014-06-14 Arthur de Jong * [1417d4a] tests/invalid-mac-algorithm.pskcxml, tests/invalid-mac-value.pskcxml, tests/invalid-no-mac-method.pskcxml, tests/test_invalid.doctest: Add tests for missing or invalid MAC This tests for incomplete, unknown or invalid MACs in PSKC files. 2014-06-14 Arthur de Jong * [9d8aae0] pskc/key.py, pskc/mac.py: Raise exception when MAC validation fails This changes the way the check() function works to raise an exception when the MAC is not correct. The MAC is also now always checked before attempting decryption. This also renames the internal DataType.value property to a get_value() method for clarity. 2014-06-14 Arthur de Jong * [699ecf8] pskc/encryption.py: Handle missing MAC algorithm properly 2014-06-14 Arthur de Jong * [01e102b] tests/aes128-cbc.pskcxml, tests/aes192-cbc.pskcxml, tests/aes256-cbc.pskcxml, tests/test_encryption.doctest, tests/tripledes-cbc.pskcxml: Add MAC tests to all CBC encrypted keys This adds hmac-sha224, hmac-sha256, hmac-sha384 and hmac-sha512 tests for values that are encrypted using CBC block cypher modes. 2014-06-14 Arthur de Jong * [59e790e] pskc/mac.py: Automatically support all MACs in hashlib This uses the name of the hash to automatically get the correct hash object from Python's hashlib. 2014-06-14 Arthur de Jong * [566e447] pskc/__init__.py, pskc/parse.py, setup.py: Support various ElementTree implementations When using a recent enough lxml, even Python 2.6 should work now. The most important requirement is that the findall() function supports the namespaces argument. This also now catches all exceptions when parsing the PSKC file fails and wraps it in ParseError because various implementations raise different exceptions, even between versions (Python 2.6's ElementTree raises ExpatError, lxml raises XMLSyntaxError). 2014-06-13 Arthur de Jong * [5d60ee2] pskc/__init__.py, pskc/encryption.py, pskc/key.py, pskc/mac.py, pskc/parse.py, pskc/policy.py: Have parse module provide find() functions This changes the parse module functions to better match the ElementTree API and extends it with findint(), findtime() and findbin(). It also passes the namespaces to all calls that require it without duplicating this throughout the normal code. 2014-06-11 Arthur de Jong * [6a34c01] pskc/__init__.py, pskc/encryption.py, pskc/key.py, pskc/mac.py, pskc/policy.py: Use get() instead of attrib.get() (shorter) 2014-05-31 Arthur de Jong * [4d92b93] pskc/encryption.py, tests/kw-tripledes.pskcxml, tests/test_encryption.doctest: Support kw-tripledes decryption This adds support for key unwrapping using the RFC 3217 Triple DES key wrap algorithm if the PSKC file uses this. 2014-05-31 Arthur de Jong * [fd71f01] pskc/tripledeskw.py, tests/test_tripledeskw.doctest: Implement RFC 3217 Triple DES key wrapping 2014-05-31 Arthur de Jong * [f639318] tests/test_minimal.doctest, tests/test_misc.doctest: Merge test_minimal into test_misc 2014-05-31 Arthur de Jong * [1e7f861] tests/draft-keyprov-actividentity-3des.pskcxml, tests/test_draft_keyprov.doctest: Add an ActivIdentity-3DES test The test is taken from draft-hoyer-keyprov-pskc-algorithm-profiles-01 modified to fit the schema as described in RFC 6030. 2014-05-31 Arthur de Jong * [b7cb928] tests/draft-keyprov-securid-aes-counter.pskcxml, tests/test_draft_keyprov.doctest: Add an SecurID-AES-Counter test The test is taken from draft-hoyer-keyprov-pskc-algorithm-profiles-01 modified to be valid XML and to fit the schema as described in RFC 6030. 2014-05-31 Arthur de Jong * [427319f] tests/draft-keyprov-totp.pskcxml, tests/test_draft_keyprov.doctest: Add an TOTP test The test is taken from draft-hoyer-keyprov-pskc-algorithm-profiles-01 modified to fit the schema as described in RFC 6030. 2014-05-31 Arthur de Jong * [ba49d09] tests/draft-keyprov-ocra.pskcxml, tests/test_draft_keyprov.doctest: Add an OCRA test The test is taken from draft-hoyer-keyprov-pskc-algorithm-profiles-01 modified to fit the schema as described in RFC 6030. 2014-05-31 Arthur de Jong * [0a66ede] tests/odd-namespace.pskcxml, tests/test_misc.doctest: Add a test for an odd namespace 2014-05-30 Arthur de Jong * [287afa7] pskc/encryption.py, tests/kw-aes128.pskcxml, tests/kw-aes192.pskcxml, tests/kw-aes256.pskcxml, tests/test_encryption.doctest: Support kw-aes128, kw-aes192 and kw-aes256 This adds support for key unwrapping using the RFC 3394 or RFC 5649 algorithm if the PSKC file uses this. 2014-05-30 Arthur de Jong * [99ba287] pskc/aeskw.py, tests/test_aeskw.doctest: Implement padding as specified in RFC 5649 This adds a pad argument with which padding can be forced or disabled. 2014-05-29 Arthur de Jong * [ebf8945] pskc/aeskw.py, tests/test_aeskw.doctest: Allow speciying an initial value for key wrapping 2014-05-29 Arthur de Jong * [5720fe5] pskc/aeskw.py, pskc/exceptions.py, tests/test_aeskw.doctest: Provide an RFC 3394 AES key wrapping algorithm This also introduces an EncryptionError exception. 2014-05-29 Arthur de Jong * [7164d89] README, docs/usage.rst, pskc/__init__.py, tests/rfc6030-figure10.pskcxml, tests/rfc6030-figure2.pskcxml, tests/rfc6030-figure3.pskcxml, tests/rfc6030-figure4.pskcxml, tests/rfc6030-figure5.pskcxml, tests/rfc6030-figure6.pskcxml, tests/rfc6030-figure7.pskcxml, tests/test_rfc6030.doctest: Always put a space between RFC and number 2014-05-29 Arthur de Jong * [ccebb69] pskc/encryption.py, tests/test_encryption.doctest, tests/tripledes-cbc.pskcxml: Support Tripple DES decryption 2014-05-29 Arthur de Jong * [a11f31f] tests/test_invalid.doctest: Add tests for key derivation problems This tests for unknown or missing algorithms and unknown derivation parameters. 2014-05-29 Arthur de Jong * [0738c94] pskc/encryption.py, pskc/exceptions.py: Raise exception when key derivation fails This also renames the internal function that implements the derivation. 2014-05-29 Arthur de Jong * [76ef42b] pskc/encryption.py, pskc/exceptions.py, tests/invalid-encryption.pskcxml, tests/test_invalid.doctest: Add test for missing key encryption algorithm This also introduces a toplevel PSKCError exception that all exceptions have as parent. 2014-05-29 Arthur de Jong * [7f26dc6] tests/aes128-cbc.pskcxml, tests/aes192-cbc.pskcxml, tests/aes256-cbc.pskcxml, tests/test_encryption.doctest: Add test for all AES-CBC encryption schemes 2014-05-29 Arthur de Jong * [28f2c1c] pskc/encryption.py: Support more AES-CBC encryption schemes This also moves the crypto imports to the places where they are used to avoid a depenency on pycrypto if no encryption is used. 2014-05-29 Arthur de Jong * [678b127] tests/test_minimal.doctest: Add test for missing secret value 2014-05-25 Arthur de Jong * [bef2f7d] pskc/__init__.py, pskc/key.py, tests/test_minimal.doctest: Add a function for adding a new key 2014-05-25 Arthur de Jong * [46f5749] pskc/__init__.py: Consistency improvement 2014-05-25 Arthur de Jong * [83f5a4b] pskc/__init__.py, tests/test_minimal.doctest: Support creating an empty PSKC instance 2014-05-25 Arthur de Jong * [820c83c] pskc/encryption.py, pskc/mac.py: Be more lenient in accepting algorithms 2014-05-25 Arthur de Jong * [02bde47] pskc/key.py: Code simplification 2014-05-25 Arthur de Jong * [b62fec8] pskc/encryption.py, pskc/exceptions.py, tests/invalid-encryption.pskcxml, tests/test_invalid.doctest, tests/test_rfc6030.doctest: Raise an exception if decryption fails 2014-05-25 Arthur de Jong * [7bc2e6b] pskc/encryption.py: Make decryption code better readable 2014-05-23 Arthur de Jong * [714f387] setup.cfg, tests/invalid-notxml.pskcxml, tests/invalid-wrongelement.pskcxml, tests/invalid-wrongversion.pskcxml, tests/test_invalid.doctest: Add tests for invalid PSKC files 2014-05-23 Arthur de Jong * [803d24c] pskc/__init__.py, pskc/exceptions.py: Raise exceptions on some parsing problems 2014-05-23 Arthur de Jong * [8c37e26] setup.py: Fix install_requires 2014-05-23 Arthur de Jong * [8e1729e] ChangeLog, MANIFEST.in, NEWS: Get files ready for 0.1 release 2014-05-23 Arthur de Jong * [15ca643] README, pskc/__init__.py, tests/rfc6030-figure10.pskcxml, tests/rfc6030-figure2.pskcxml, tests/rfc6030-figure3.pskcxml, tests/rfc6030-figure4.pskcxml, tests/rfc6030-figure5.pskcxml, tests/rfc6030-figure6.pskcxml, tests/rfc6030-figure7.pskcxml, tests/test_rfc6030.doctest: Use pskcxml as file name extension This is the extension that is suggested in RFC6030. 2014-05-23 Arthur de Jong * [44c7d2e] docs/policy.rst, docs/usage.rst: Improve IANA links 2014-05-20 Arthur de Jong * [cda1c5f] tests/test_rfc6030.doctest: Improve test This tests that, before the PSKC ecnryption is key available, the secret from the key cannot be extracted. 2014-05-19 Arthur de Jong * [e96c746] docs/_templates/autosummary/module.rst, docs/conf.py, docs/encryption.rst, docs/index.rst, docs/mac.rst, docs/policy.rst, docs/usage.rst: Provide Sphinx documentation 2014-05-18 Arthur de Jong * [edf4d24] pskc/policy.py: Add missing policy constant 2014-05-18 Arthur de Jong * [92a994d] pskc/key.py: Fix attribute name in docstring 2014-04-20 Arthur de Jong * [cc9bbb5] README: Update README 2014-05-17 Arthur de Jong * [d0a7814] .gitignore, setup.py: Fix dateutil dependency This also ignores downloaded .egg files. 2014-04-19 Arthur de Jong * [e0159ba] pskc/parse.py: Fix module description 2014-04-19 Arthur de Jong * [ba17976] pskc/__init__.py, pskc/parse.py: Move PSKC class to toplevel module This also splits the parsing to a parse() function for consistency. 2014-04-19 Arthur de Jong * [64e207d] pskc/key.py, tests/test_rfc6030.doctest: Provide pskc.key docstrings This documents most of the information that is available per key and adds a few other minor cosmetic changes. This also re-organises the key properties to be in a slightly more logical order and renames the userid key property to key_userid to more clearly distinguish it from device_userid. 2014-04-19 Arthur de Jong * [6becc61] pskc/parse.py: Provide pskc.parse docstrings This documents most of the API of the parsing functions and the PSKC class. 2014-04-19 Arthur de Jong * [1d42fbc] pskc/policy.py: Complete pskc.policy docstrings Also contains small consistency improvement. 2014-04-19 Arthur de Jong * [b07d709] pskc/mac.py: Provide pskc.mac docstrings This also hides two properties that are not part of the public API. 2014-04-19 Arthur de Jong * [285860e] pskc/encryption.py: Provide pskc.encryption docstrings This documents classes in the pskc.encryption module. 2014-04-19 Arthur de Jong * [8c9e03d] pskc/key.py, pskc/mac.py, pskc/parse.py, pskc/policy.py: Move Key class to separate module This also allows re-organising the imports a bit. 2014-04-16 Arthur de Jong * [c883d48] MANIFEST.in, pskc/__init__.py, setup.cfg, setup.py: Add initial setup script 2014-04-14 Arthur de Jong * [3df6849] COPYING: Include a license file (LGPL) 2014-04-13 Arthur de Jong * [f08cdb5] tests/rfc6030-figure10.pskc, tests/test_rfc6030.doctest: Add bulk provisioning test from Figure 10 2014-04-13 Arthur de Jong * [41828cd] pskc/parse.py: Use slightly clearer names 2014-04-12 Arthur de Jong * [5ab731c] tests/rfc6030-figure7.pskc, tests/test_rfc6030.doctest: Add test for Figure 7 from RFC6030 This tests encrypted key derivation using PBKDF2 and a pre-shared passphrase. 2014-04-12 Arthur de Jong * [a3fd598] pskc/encryption.py: Implement PBKDF2 key derivation This supports deriving the key from a passphrase and information present in the DerivedKey and PBKDF2-params XML elements. 2014-04-12 Arthur de Jong * [2ff470f] pskc/encryption.py: Add id attribute from EncryptionKey 2014-04-12 Arthur de Jong * [460f335] tests/rfc6030-figure6.pskc, tests/test_rfc6030.doctest: Add test for Figure 6 from RFC6030 This test key encryption with a pre-shared key and MAC checks. 2014-04-12 Arthur de Jong * [a926ddb] pskc/mac.py, pskc/parse.py: Implement MAC checking This implements message message authentication code checking for the encrypted values if MACMethod and ValueMAC are present. 2014-04-12 Arthur de Jong * [e53e865] pskc/encryption.py, pskc/parse.py: Support decrypting with a pre-shared key This adds an encryption module that provides wrappers for handling decryption. 2014-04-11 Arthur de Jong * [3fe0919] pskc/parse.py: Refactor DataType value handling This ensures that DataType values are retrieved dynamically instead of at the time the PSKC file was parsed in order to make decryption work. 2014-04-11 Arthur de Jong * [591bb5d] pskc/policy.py: Document key and pin usage values 2014-04-11 Arthur de Jong * [b952b93] tests/rfc6030-figure5.pskc, tests/test_rfc6030.doctest: Add test for Figure 5 from RFC6030 This test extraction of key policy information and cross-key references. 2014-04-11 Arthur de Jong * [e939a96] pskc/parse.py, pskc/policy.py: Implement key policy parsing This parses key policy from PSKC files and provides a few utility methods to help with policy validation. 2014-04-11 Arthur de Jong * [8c9ac8c] pskc/parse.py: Support parsing date and integer values 2014-04-11 Arthur de Jong * [6446f7d] tests/rfc6030-figure4.pskc, tests/test_rfc6030.doctest: Add test for Figure 4 from RFC6030 This tests for key profile and key reference properties that can be used to reference external keys. 2014-04-07 Arthur de Jong * [e72369f] tests/rfc6030-figure3.pskc, tests/test_rfc6030.doctest: Add test for Figure 3 from RFC6030 This tests Figure 3 from RFC6030 with a very basic plain text secret key and some supplementary data. 2014-04-07 Arthur de Jong * [2c111a8] pskc/parse.py: Get more data from KeyPackage This gets most simple string values from the KeyPackage as well as some integer and boolean values. 2014-04-07 Arthur de Jong * [96b4b54] tests/rfc6030-figure2.pskc, tests/test-rfc6030.doctest: Add test for example from RFC6030 This tests Figure 2 from RFC6030 with a very basic plain text secret key. 2014-04-07 Arthur de Jong * [d662cf2] pskc/parse.py: Support getting plaintext key 2014-04-07 Arthur de Jong * [550630d] tests/test_minimal.doctest: Minimal test This adds a doctest for the absolute minimum PSKC file that does not contain any useful information. 2014-04-07 Arthur de Jong * [bf8e7f6] pskc/__init__.py, pskc/parse.py: Basic implementation of PSKC class This class is used for handling PSKC files. It will parse the file and store relevant properties for easy access. The Key class corresponds to a single key defined in the PSKC file. This is a very minimal implementation that only provides some meta-data from the file and keys (work in progress). 2014-04-04 Arthur de Jong * [9803dfc] README: Provide an initial README 2014-04-02 Arthur de Jong * [c912bb4] .gitignore, pskc/__init__.py: Initial project layout python-pskc-1.3/MANIFEST.in0000644000000000000000000000024613300356475015340 0ustar rootroot00000000000000include README NEWS ChangeLog COPYING tox.ini *.py recursive-include tests *.doctest *.py *.pskcxml *.xml *.pem *.txt *.pskc README recursive-include docs *.rst *.py python-pskc-1.3/NEWS0000644000000000000000000001223214667354015014304 0ustar rootroot00000000000000changes from 1.2 to 1.3 ----------------------- * drop support for Python 2.7 and 3.5 (support Python 3.6 - 3.12) * typo fixes in documentation * have test suite not rely on current date/time * update certificates in tests to support newer cryptography changes from 1.1 to 1.2 ----------------------- * sort namespace declarations alphabetically in generated XML * accept keys as bytearray values * spelling fixes in documentation * command-line utilities now support using - as stdin * test and build environment improvements * drop support for Python 3.4 * add support for Python 3.8 - 3.10 (was already working out-of-the-box) changes from 1.0 to 1.1 ----------------------- * portability fixes for test suite * add a remove_encryption() function * always write a 1.0 PSKC version, even when another version was read * correctly write a PSKC file with a global IV * correctly write a PSKC file without a MAC key * add a pskc2pskc script for converting a legacy PSKC file to a RFC 6030 compliant version and for adding or removing encryption * add a csv2pskc script for generating a PSKC file from a CSV file * make all the scripts (pskc2csv, pskc2pskc and csv2pskc) entry points so they are available on package installation changes from 0.5 to 1.0 ----------------------- * fix a bug in writing passphrase encrypted PSKC files on Python3 * fix a typo in the pin_max_failed_attempts attribute (the old name is available as a deprecated property) * switch from pycrypto to cryptography as provider for encryption functions because the latter is better supported * switch to using the PBKDF2 implementation from hashlib which requires Python 2.7.8 or newer * use defusedxml when available (python-pskc now supports both standard xml.etree and lxml with and without defusedxml) * support checking and generating embedded XML signatures (this requires the signxml library which is not required for any other operations) * add limited support for very old draft PSKC versions (it is speculated that this resembles the "Verisign PSKC format" that some applications produce) * support Camellia-CBC and KW-Camellia encryption algorithms * support any hashing algorithm available in Python * add a --secret-encoding option to pskc2csv to allow base64 encoded binary output * support naming the CSV column headers in pskc2csv * add a manual page for pskc2csv * a number of documentation, code style and test suite improvements changes from 0.4 to 0.5 ----------------------- * numerous compatibility improvements for reading PSKC files that do not follow the RFC 6030 schema exactly: specifically accept a number of old Internet Draft specifications that preceded RFC 6030 and support an ActivIdentity file format * split device information from key information (keep old API available) to allow multiple keys per device (this is not allowed by RFC 6030 but was allowed in older Internet Drafts) * accept MAC to be over plaintext in addition to ciphertext * fall back to using encryption key as MAC key * refactoring of some encryption, parsing and serialising functionality into separate modules for better maintainability * add configuration for running test suite via Tox * addition of a large number of test cases, bringing the branch coverage to 100% * documentation improvements * drop official support for Python 2.6 (the module still works but is just no longer tested with it) changes from 0.3 to 0.4 ----------------------- * add support for writing encrypted PSKC files (with either a pre-shared key or PBKDF2 password-based encryption) * extend may_use() policy checking function to check for unknown policy elements and key expiry * add a number of tests for existing vendor PSKC files and have full line coverage with tests * be more lenient in handling a number of XML files (e.g. automatically sanitise encryption algorithm URIs, ignore XML namespaces and support more spellings of some properties) * support reading password or key files in pskc2csv * support Python 3 in the pskc2csv script (thanks Mathias Laurin) * refactoring and clean-ups to be more easily extendible (thanks Mathias Laurin) changes from 0.2 to 0.3 ----------------------- * support writing unencrypted PSKC files * include a sample pskc2csv script in the source code * fix an issue with XML namespaces for PBKDF2 parameters * support Python 3 * update documentation changes from 0.1 to 0.2 ----------------------- * raise exceptions on parsing, decryption and other problems * support Python 2.6 and multiple ElementTree implementations (lxml is required when using Python 2.6) * support more encryption algorithms (AES128-CBC, AES192-CBC, AES256-CBC, TripleDES-CBC, KW-AES128, KW-AES192, KW-AES256 and KW-TripleDES) and be more lenient in accepting algorithm URIs * support all HMAC algorithms that Python's hashlib module has hash functions for (HMAC-MD5, HMAC-SHA1, HMAC-SHA224, HMAC-SHA256, HMAC-SHA384 and HMAC-SHA512) * support PRF attribute of PBKDF2 algorithm * support creating PSKC objects and keys * when accessing values for which a MAC is present, a MAC failure will raise an exception (DecryptionError) * many code cleanups * improve test coverage changes in 0.1 -------------- Initial release python-pskc-1.3/PKG-INFO0000644000000000000000000001100214667356044014700 0ustar rootroot00000000000000Metadata-Version: 2.1 Name: python-pskc Version: 1.3 Summary: Python module for handling PSKC files Home-page: https://arthurdejong.org/python-pskc/ Author: Arthur de Jong Author-email: arthur@arthurdejong.org License: LGPL Description: Python module for handling PSKC files ===================================== A Python module to handle Portable Symmetric Key Container (PSKC) files as defined in `RFC 6030 `_. PSKC files are used to transport and provision symmetric keys and key meta data (seed files) to different types of crypto modules, commonly one-time password systems or other authentication devices. This module can be used to extract keys from PSKC files for use in an OTP authentication system. The module can also be used for authoring PSKC files. This module should be able to handle most common PSKC files. https://arthurdejong.org/python-pskc/ API --- The module provides a straightforward API that is mostly geared towards parsing existing PSKC files. Extracting key material from encrypted PSKC files is as simple as: >>> from pskc import PSKC >>> pskc = PSKC('tests/rfc6030/figure7.pskcxml') >>> pskc.encryption.derive_key('qwerty') >>> for key in pskc.keys: ... print('%s %s' % (key.serial, str(key.secret.decode()))) 987654321 12345678901234567890 Writing am encrypted PSKC file is as simple as: >>> pskc = PSKC() >>> key = pskc.add_key( ... id='456', secret='987654321', manufacturer='Manufacturer', ... algorithm = 'urn:ietf:params:xml:ns:keyprov:pskc:hotp') >>> pskc.encryption.setup_pbkdf2('passphrase') >>> pskc.write('output.pskcxml') The key object has a number of properties. See the pskc.key.Key documentation for details. Security considerations ----------------------- This code handles private key material and is written in Python. No precautions have been taken to lock pages in memory to prevent swapping. Also no attempt is currently made to securely dispose of memory that may have held private key material. Copyright --------- Copyright (C) 2014-2024 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Keywords: PSKC,RFC 6030,key container Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+) Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Security :: Cryptography Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: System :: Systems Administration :: Authentication/Directory Classifier: Topic :: Text Processing :: Markup :: XML Provides-Extra: defuse Provides-Extra: lxml Provides-Extra: signature python-pskc-1.3/README0000644000000000000000000000475514667353256014506 0ustar rootroot00000000000000Python module for handling PSKC files ===================================== A Python module to handle Portable Symmetric Key Container (PSKC) files as defined in `RFC 6030 `_. PSKC files are used to transport and provision symmetric keys and key meta data (seed files) to different types of crypto modules, commonly one-time password systems or other authentication devices. This module can be used to extract keys from PSKC files for use in an OTP authentication system. The module can also be used for authoring PSKC files. This module should be able to handle most common PSKC files. https://arthurdejong.org/python-pskc/ API --- The module provides a straightforward API that is mostly geared towards parsing existing PSKC files. Extracting key material from encrypted PSKC files is as simple as: >>> from pskc import PSKC >>> pskc = PSKC('tests/rfc6030/figure7.pskcxml') >>> pskc.encryption.derive_key('qwerty') >>> for key in pskc.keys: ... print('%s %s' % (key.serial, str(key.secret.decode()))) 987654321 12345678901234567890 Writing am encrypted PSKC file is as simple as: >>> pskc = PSKC() >>> key = pskc.add_key( ... id='456', secret='987654321', manufacturer='Manufacturer', ... algorithm = 'urn:ietf:params:xml:ns:keyprov:pskc:hotp') >>> pskc.encryption.setup_pbkdf2('passphrase') >>> pskc.write('output.pskcxml') The key object has a number of properties. See the pskc.key.Key documentation for details. Security considerations ----------------------- This code handles private key material and is written in Python. No precautions have been taken to lock pages in memory to prevent swapping. Also no attempt is currently made to securely dispose of memory that may have held private key material. Copyright --------- Copyright (C) 2014-2024 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA python-pskc-1.3/csv2pskc.py0000755000000000000000000000176114307703306015715 0ustar rootroot00000000000000#!/usr/bin/env python # coding: utf-8 # csv2pskc.py - script to convert a CSV file to PSKC # # Copyright (C) 2018 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Script to convert a CSV file to PSKC.""" from pskc.scripts import csv2pskc if __name__ == '__main__': # pragma: no cover csv2pskc.main() python-pskc-1.3/docs/0000755000000000000000000000000014667356044014541 5ustar rootroot00000000000000python-pskc-1.3/docs/_templates/0000755000000000000000000000000014667356044016676 5ustar rootroot00000000000000python-pskc-1.3/docs/_templates/autosummary/0000755000000000000000000000000014667356044021264 5ustar rootroot00000000000000python-pskc-1.3/docs/_templates/autosummary/module.rst0000644000000000000000000000011412337607021023261 0ustar rootroot00000000000000{{ fullname }} {{ underline }} .. automodule:: {{ fullname }} :members: python-pskc-1.3/docs/changes.rst0000644000000000000000000000010412353625504016664 0ustar rootroot00000000000000Changes in python-pskc ====================== .. include:: ../NEWS python-pskc-1.3/docs/conf.py0000644000000000000000000000573014667353275016050 0ustar rootroot00000000000000# -*- coding: utf-8 -*- # # python-pskc documentation build configuration file, created by # sphinx-quickstart # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. """python-pskc documentation build configuration.""" import pskc # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage', 'sphinx.ext.intersphinx', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'python-pskc' copyright = u'2014-2024 Arthur de Jong' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = pskc.__version__ # The full version, including alpha/beta/rc tags. release = version # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_*', '.svn', '.git'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. modindex_common_prefix = ['pskc.'] # Automatically generate stub pages for autosummary entries. autosummary_generate = True # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%Y-%m-%d' # If true, links to the reST sources are added to the pages. html_show_sourcelink = False # Output file base name for HTML help builder. htmlhelp_basename = 'python-pskcdoc' # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('pskc2csv', 'pskc2csv', u'Convert a PSKC file to CSV', [u'Arthur de Jong'], 1), ('pskc2pskc', 'pskc2pskc', u'Convert a PSKC file to standard format', [u'Arthur de Jong'], 1), ('csv2pskc', 'csv2pskc', u'Convert a CSV file to PSKC', [u'Arthur de Jong'], 1), ] # If true, show URL addresses after external links. man_show_urls = True python-pskc-1.3/docs/csv2pskc.rst0000644000000000000000000000520513531212372017013 0ustar rootroot00000000000000:orphan: csv2pskc ======== Synopsis -------- **csv2pskc** [*options*] [<*FILE*>] Description ----------- :program:`csv2pskc` reads a CSV file where the first line contains column labels and following lines contain key information for one key per line. Options ------- .. program:: csv2pskc .. option:: -h, --help Display usage summary. .. option:: -V, --version Display version information. .. option:: -o FILE, --output FILE By default :program:`csv2pskc` writes a PSKC file to stdout. This option can be used to save to a file instead. .. option:: -c COL,COL,.., --columns COL,COL,.. Specify the meaning of the columns in the CSV file. By default the first row of the CSV file is expected to list the names of the columns. Any property of :class:`~pskc.key.Key` instances can be used as well as :class:`~pskc.policy.Policy` properties via ``policy``. For example: ``serial``, ``secret``, ``counter``, ``time_offset``, ``time_interval``, ``interval``, ``time_drift``, ``issuer``, ``manufacturer``, ``response_length``, ``policy.pin_min_length``. This option can either specify a list of columns or a COL:KEY mapping where COL refers to the value found in the first line of the CSV file and KEY refers to a property as described above. It is possible to map a single column in the CSV file to multiple PSKC properties (e.g. use of ``id+serial`` sets both the ID and device serial number to the value found in that column). .. option:: --skip-rows N By default the first row is treated as a header which contains labels. This option can be used to either skip more row (the first row of the CSV file will still be treated as a header) or to indicate that there is no header row. In the latter case the :option:`--columns` option is required. .. option:: -x COL=VALUE, --set COL=VALUE Specify properties that are added to all keys in the generated PSKC file. Accepted labels are the same as for the :option:`--columns` option. This can be useful for setting the ``issuer``, ``manufacturer`` or other common properties globally. .. option:: -p PASS/FILE, --password PASS/FILE, --passwd PASS/FILE Encrypt the PSKC file with the specified password. If the argument refers to a file the password is read from the file instead. .. option:: -s KEY/FILE, --secret KEY/FILE A hex encoded encryption key or a file containing the binary key (raw data, not encoded). .. option:: -e ENCODING, --secret-encoding ENCODING Specify the encoding to use for reading key material from the CSV file. By default HEX encoding is used. Valid encodings are: ``base32``, ``base64`` or ``hex``. python-pskc-1.3/docs/encryption.rst0000644000000000000000000001707413300356475017465 0ustar rootroot00000000000000PSKC encryption =============== .. module:: pskc.encryption Some of the information in PSKC files (e.g. key material) can be encrypted with either pre-shared keys, passphrase-based keys or asymmetric keys (asymmetric keys are currently unimplemented). Embedded PSKC encryption is handled inside the :class:`Encryption` class that defines encryption key or means of deriving keys. It is accessed from the :attr:`~pskc.PSKC.encryption` attribute of a :class:`~pskc.PSKC` instance:: >>> from binascii import a2b_hex >>> from pskc import PSKC >>> pskc = PSKC('somefile.pskcxml') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') or:: >>> pskc.encryption.derive_key('qwerty') Once the encryption key has been set up, any encrypted key values from the PSKC file are available transparently. If no key or an incorrect key has been configured, upon accessing encrypted information (e.g. the :attr:`~pskc.key.Key.secret` attribute of a :class:`~pskc.key.Key` instance) a :exc:`~pskc.exceptions.DecryptionError` exception will be raised. When writing out a PSKC file, encryption can be configured with the :func:`~pskc.encryption.Encryption.setup_preshared_key()` or :func:`~pskc.encryption.Encryption.setup_pbkdf2()` function:: >>> from pskc import PSKC >>> pskc = PSKC() >>> pskc.encryption.setup_preshared_key(algorithm='AES256-CBC') or:: >>> pskc.encryption.setup_pbkdf2(password='verysecure') The Encryption class -------------------- .. class:: Encryption .. attribute:: id Optional identifier of the encryption key. .. attribute:: algorithm A URI of the encryption algorithm used. See the section :ref:`encryption-algorithms` below for a list of algorithms URIs. Assigned values to this attribute will be converted to the canonical URI for the algorithm if it is known. For instance, the value ``3DES-CBC`` will automatically be converted into ``http://www.w3.org/2001/04/xmlenc#tripledes-cbc``. .. attribute:: is_encrypted An indicator of whether the PSKC file requires an additional pre-shared key or passphrase to decrypt the contents of the file. Will be ``True`` if a key or passphrase is needed, ``False`` otherwise. .. attribute:: key_names List of names provided for the encryption key. .. attribute:: key_name Since usually only one name is defined for a key but the schema allows for multiple names, this is a shortcut for accessing the first value of :attr:`key_names`. It will return ``None`` if no name is available. .. attribute:: key The binary value of the encryption key. In the case of pre-shared keys this value should be set before trying to access encrypted information in the PSKC file. When using key derivation the secret key is available in this attribute after calling :func:`derive_key`. .. function:: derive_key(password) Derive a key from the supplied password and information in the PSKC file (generally algorithm, salt, etc.). This function may raise a :exc:`~pskc.exceptions.KeyDerivationError` exception if key derivation fails for some reason. .. attribute:: fields A list of :class:`~pskc.key.Key` instance field names that will be encrypted when the PSKC file is written. List values can contain ``secret``, ``counter``, ``time_offset``, ``time_interval`` and ``time_drift``. .. function:: setup_preshared_key(...) Configure pre-shared key encryption when writing the file. :param bytes key: the encryption key to use :param str id: encryption key identifier :param str algorithm: encryption algorithm :param int key_length: encryption key length in bytes :param str key_name: a name for the key :param list key_names: a number of names for the key :param list fields: a list of fields to encrypt This is a utility function to easily set up encryption. Encryption can also be set up by manually by setting the :class:`~pskc.encryption.Encryption` properties. This method will generate a key if required and set the passed values. By default AES128-CBC encryption will be configured and unless a key is specified one of the correct length will be generated. If the algorithm does not provide integrity checks (e.g. CBC-mode algorithms) integrity checking in the PSKC file will be set up using :func:`~pskc.mac.MAC.setup()`. By default only the :attr:`~pskc.key.Key.secret` property will be encrypted when writing the file. .. function:: setup_pbkdf2(...) Configure password-based PSKC encryption when writing the file. :param str password: the password to use (required) :param str id: encryption key identifier :param str algorithm: encryption algorithm :param int key_length: encryption key length in bytes :param str key_name: a name for the key :param list key_names: a number of names for the key :param list fields: a list of fields to encrypt :param bytes salt: PBKDF2 salt :param int salt_length: used when generating random salt :param int iterations: number of PBKDF2 iterations :param function prf: PBKDF2 pseudorandom function Defaults for the above parameters are similar to those for :func:`setup_preshared_key()` but the password parameter is required. By default 12000 iterations will be used and a random salt with the length of the to-be-generated encryption key will be used. .. function:: remove_encryption() Decrypt all data stored in the PSKC file and remove the encryption configuration. This can be used to read and encrypted PSKC file, decrypt the file, remove the encryption and output an unencrypted PSKC file or to replace the encryption algorithm. .. _encryption-algorithms: Supported encryption algorithms ------------------------------- The following encryption algorithms are currently supported by python-pskc. +----------------------------------------------------+-----------------------------------------------------+ | URI | Description | +====================================================+=====================================================+ | ``http://www.w3.org/2001/04/xmlenc#aes128-cbc`` | AES encryption in CBC mode with various key lengths | | ``http://www.w3.org/2001/04/xmlenc#aes192-cbc`` | | | ``http://www.w3.org/2001/04/xmlenc#aes256-cbc`` | | +----------------------------------------------------+-----------------------------------------------------+ | ``http://www.w3.org/2001/04/xmlenc#kw-aes128`` | AES key wrap with various key lengths | | ``http://www.w3.org/2001/04/xmlenc#kw-aes192`` | | | ``http://www.w3.org/2001/04/xmlenc#kw-aes256`` | | +----------------------------------------------------+-----------------------------------------------------+ | ``http://www.w3.org/2001/04/xmlenc#tripledes-cbc`` | Triple DES (3DES) encryption in CBC mode | +----------------------------------------------------+-----------------------------------------------------+ | ``http://www.w3.org/2001/04/xmlenc#kw-tripledes`` | Triple DES (3DES) key wrap | +----------------------------------------------------+-----------------------------------------------------+ python-pskc-1.3/docs/exceptions.rst0000644000000000000000000000265613117010157017442 0ustar rootroot00000000000000Exceptions ========== The module and parser will try to interpret any provided PSKC files and will only raise exceptions on wildly invalid PSKC files. .. module:: pskc.exceptions .. exception:: PSKCError The base class for all exceptions that the module will raise. In some cases third-party code may raise additional exceptions. .. exception:: ParseError Raised when the PSKC file cannot be correctly read due to invalid XML or some required element or attribute is missing. This exception should only be raised when parsing the file (i.e. when the :class:`~pskc.PSKC` class is instantiated). .. .. exception:: EncryptionError Raised when encrypting a value is not possible due to key length issues, missing or wrong length plain text, or other issues. .. exception:: DecryptionError Raised when decrypting a value fails due to missing or incorrect key, unsupported decryption or MAC algorithm, failed message authentication check or other error. This exception is generally raised when accessing encrypted information (i.e. the :attr:`~pskc.key.Key.secret`, :attr:`~pskc.key.Key.counter`, :attr:`~pskc.key.Key.time_offset`, :attr:`~pskc.key.Key.time_interval` or :attr:`~pskc.key.Key.time_drift` attributes of the :class:`~pskc.key.Key` class). .. exception:: KeyDerivationError Raised when key derivation fails due to an unsupported algorithm or missing information in the PSKC file. python-pskc-1.3/docs/index.rst0000644000000000000000000000045313300637004016361 0ustar rootroot00000000000000.. include:: ../README :end-before: API Contents -------- .. toctree:: :maxdepth: 1 usage encryption mac signatures policy exceptions scripts changes Security considerations ----------------------- .. include:: ../README :start-after: ----------------------- python-pskc-1.3/docs/mac.rst0000644000000000000000000000674213300352662016025 0ustar rootroot00000000000000Integrity checking ================== .. module:: pskc.mac The PSKC format allows for `message authentication and integrity checking `_ for some of the values stored within the PSKC file. Integrity checking is done transparently when accessing attributes that are encrypted and contain a ValueMAC. Once the PSKC encryption key has been set up, key values can be explicitly checked using the :func:`~pskc.key.Key.check` method:: >>> pskc = PSKC('somefile.pskcxml') >>> pskc.encryption.derive_key('qwerty') >>> pskc.mac.algorithm 'http://www.w3.org/2000/09/xmldsig#hmac-sha1' >>> all(key.check() for key in pskc.keys) True The MAC class ------------- .. class:: MAC .. attribute:: algorithm A URI of the MAC algorithm used for message authentication. See the section :ref:`mac-algorithms` below for a list of algorithm URIs. Assigned values to this attribute will be converted to the canonical URI for the algorithm if it is known. For instance, the value ``HMAC-SHA-256`` will automatically be converted into ``http://www.w3.org/2001/04/xmldsig-more#hmac-sha256``. .. attribute:: key For HMAC checking, this contains the binary value of the MAC key. The MAC key is generated specifically for each PSKC file and encrypted with the PSKC encryption key, so the PSKC file should be decrypted first (see :doc:`encryption`). .. function:: setup(...) Configure an encrypted MAC key for creating a new PSKC file. :param str algorithm: encryption algorithm :param bytes key: the encryption key to use None of the arguments are required. By default HMAC-SHA1 will be used as a MAC algorithm. If no key is configured a random key will be generated with the length of the output of the configured hash. This function will automatically be called when the configured encryption algorithm requires a message authentication code. .. _mac-algorithms: Supported MAC algorithms ------------------------ The module should support all HMAC algorithms that can be constructed from hash algorithms that are available in the standard Python :mod:`hashlib` module. At the least the following algorithms should be supported: +-----------------------------------------------------------+--------------------------+ | URI | Description | +===========================================================+==========================+ | ``http://www.w3.org/2001/04/xmldsig-more#hmac-md5`` | MD5-based HMAC | +-----------------------------------------------------------+--------------------------+ | ``http://www.w3.org/2000/09/xmldsig#hmac-sha1`` | SHA-1 based HMAC | +-----------------------------------------------------------+--------------------------+ | ``http://www.w3.org/2001/04/xmldsig-more#hmac-sha224`` | SHA-2 family based HMACs | | ``http://www.w3.org/2001/04/xmldsig-more#hmac-sha256`` | | | ``http://www.w3.org/2001/04/xmldsig-more#hmac-sha384`` | | | ``http://www.w3.org/2001/04/xmldsig-more#hmac-sha512`` | | +-----------------------------------------------------------+--------------------------+ | ``http://www.w3.org/2001/04/xmldsig-more#hmac-ripemd160`` | RIPEMD-160 based HMAC | +-----------------------------------------------------------+--------------------------+ python-pskc-1.3/docs/policy.rst0000644000000000000000000001304213300352662016553 0ustar rootroot00000000000000Key usage policy ================ .. module:: pskc.policy The PSKC format allows for specifying `key and pin usage policy `__ per key. Instances of the :class:`Policy` class provide attributes that describe limits that are placed on key usage and requirements for key PIN protection:: >>> key = pskc.keys[0] >>> key.policy.may_use(key.policy.KEY_USE_OTP) True The Policy class ---------------- .. class:: Policy .. attribute:: start_date :class:`datetime.datetime` value that indicates that the key must not be used before this date. .. attribute:: expiry_date :class:`datetime.datetime` value that indicates that the key must not be used after this date. Systems should not rely upon the device to enforce key usage date restrictions, as some devices do not have an internal clock. .. attribute:: number_of_transactions The value indicates the maximum number of times a key carried within the PSKC document may be used by an application after having received it. .. attribute:: key_usage A list of `valid usage scenarios `__ for the key that the recipient should check against the intended usage of the key. Also see :func:`may_use` and :ref:`key-use-constants` below. .. attribute:: pin_key_id The unique `id` of the key within the PSKC file that contains the value of the PIN that protects this key. .. attribute:: pin_key Instance of the :class:`~pskc.key.Key` (if any) that contains the value of the PIN referenced by :attr:`pin_key_id`. .. attribute:: pin PIN value referenced by :attr:`pin_key_id` (if any). The value is transparently decrypted if possible. .. attribute:: pin_usage Describe how the PIN is used during the usage of the key. See :ref:`pin-use-constants` below. .. attribute:: pin_max_failed_attempts The maximum number of times the PIN may be entered wrongly before it MUST NOT be possible to use the key any more. .. attribute:: pin_min_length The minimum length of a PIN that can be set to protect the associated key. .. attribute:: pin_max_length The maximum length of a PIN that can be set to protect this key. .. attribute:: pin_encoding The encoding of the PIN which is one of ``DECIMAL``, ``HEXADECIMAL``, ``ALPHANUMERIC``, ``BASE64``, or ``BINARY`` (see :attr:`~pskc.key.Key.challenge_encoding`). .. attribute:: unknown_policy_elements Boolean that is set to ``True`` if the PSKC policy information contains unknown or unsupported definitions or values. A conforming implementation must assume that key usage is not permitted if this value is ``True`` to ensure that the lack of understanding of certain extensions does not lead to unintended key usage. .. function:: may_use(usage=None, now=None) Check whether the key may be used for the provided purpose. The key :attr:`start_date` and :attr:`expiry_date` are also checked. The `now` argument can be used to specify another point in time to check against. .. _key-use-constants: Key usage constants ------------------- The :class:`Policy` class provides the following key use constants (see :attr:`~Policy.key_usage` and :func:`~Policy.may_use`): .. autoattribute:: Policy.KEY_USE_OTP Key is used for OTP generation. .. autoattribute:: Policy.KEY_USE_CR The key is used for challenge-response purposes. .. autoattribute:: Policy.KEY_USE_ENCRYPT The key is used for data encryption purposes. .. autoattribute:: Policy.KEY_USE_INTEGRITY The key is used to generate a keyed message digest for data integrity or authentication purposes. .. autoattribute:: Policy.KEY_USE_VERIFY The key is used to verify a keyed message digest for data integrity or authentication purposes (this is the opposite of :attr:`KEY_USE_INTEGRITY`). .. autoattribute:: Policy.KEY_USE_UNLOCK The key is used for an inverse challenge-response in the case where a user has locked the device by entering a wrong PIN too many times (for devices with PIN-input capability). .. autoattribute:: Policy.KEY_USE_DECRYPT The key is used for data decryption purposes. .. autoattribute:: Policy.KEY_USE_KEYWRAP The key is used for key wrap purposes. .. autoattribute:: Policy.KEY_USE_UNWRAP The key is used for key unwrap purposes. .. autoattribute:: Policy.KEY_USE_DERIVE The key is used with a key derivation function to derive a new key. .. autoattribute:: Policy.KEY_USE_GENERATE The key is used to generate a new key based on a random number and the previous value of the key. .. _pin-use-constants: Pin usage constants ------------------- The following constants for PIN use are defined in the :class:`Policy` class (see :attr:`~Policy.pin_usage`): .. autoattribute:: Policy.PIN_USE_LOCAL The PIN is checked locally on the device before allowing the key to be used in executing the algorithm. .. autoattribute:: Policy.PIN_USE_PREPEND The PIN is prepended to the algorithm response. It must be checked by the party validating the response. .. autoattribute:: Policy.PIN_USE_APPEND The PIN is appended to the algorithm response. It must be checked by the party validating the response. .. autoattribute:: Policy.PIN_USE_ALGORITHMIC The PIN is used as part of the algorithm computation. python-pskc-1.3/docs/pskc2csv.rst0000644000000000000000000000336613221434047017022 0ustar rootroot00000000000000:orphan: pskc2csv ======== Synopsis -------- **pskc2csv** [*options*] <*FILE*> Description ----------- :program:`pskc2csv` reads a PSKC file, optionally decrypts any encrypted key material and outputs a CSV file with information from the PSKC file. Options ------- .. program:: pskc2csv .. option:: -h, --help Display usage summary. .. option:: -V, --version Display version information. .. option:: -o FILE, --output FILE By default :program:`pskc2csv` writes a CSV file to stdout. This option can be used to save the CSV to a file instead. .. option:: -c COL:LABEL,COL,.., --columns COL:LABEL,COL,.. Specify the columns that should be written to the CSV file. Any property of :class:`~pskc.key.Key` instances can be used as well as :class:`~pskc.policy.Policy` properties via ``policy``. For example: ``serial``, ``secret``, ``counter``, ``time_offset``, ``time_interval``, ``interval``, ``time_drift``, ``issuer``, ``manufacturer``, ``response_length``, ``policy.pin_min_length``. By default ``serial,secret,algorithm,response_length,time_interval`` is used. The column can be followed by an optional label to use in the CSV file in place of the column specification. .. option:: -p PASS/FILE, --password PASS/FILE, --passwd PASS/FILE The password to use for decryption. If the argument refers to a file the password is read from the file instead. .. option:: -s KEY/FILE, --secret KEY/FILE A hex encoded encryption key or a file containing the binary (raw data, not encoded) key. .. option:: -e ENCODING, --secret-encoding ENCODING Specify the encoding to use for writing key material to the CSV file. By default HEX encoding is used. Valid encodings are: ``base32``, ``base64`` or ``hex``. python-pskc-1.3/docs/pskc2pskc.rst0000644000000000000000000000260713604371042017164 0ustar rootroot00000000000000:orphan: pskc2pskc ========= Synopsis -------- **pskc2pskc** [*options*] <*FILE*> Description ----------- :program:`pskc2pskc` reads a PSKC file in any of the supported formats, optionally decrypts any encrypted information and outputs a PSKC file in the RFC 6030 format, optionally encrypting the file. Options ------- .. program:: pskc2pskc .. option:: -h, --help Display usage summary. .. option:: -V, --version Display version information. .. option:: -o FILE, --output FILE By default :program:`pskc2pskc` writes a PSKC file to stdout. This option can be used to save to a file instead. .. option:: -p PASS/FILE, --password PASS/FILE, --passwd PASS/FILE The password to use for decryption. If the argument refers to a file the password is read from the file instead. .. option:: -s KEY/FILE, --secret KEY/FILE A hex encoded encryption key or a file containing the binary (raw data, not encoded) key used for decryption. .. option:: --new-password PASS/FILE, --new-passwd PASS/FILE Output an encrypted PSKC file that is protected with the specified password (or read the password from the file if a file argument was specified). .. option:: --new-secret KEY/FILE Ensure that the output PSKC file is encrypted with the specified key value. The key can be provided as a hex-encoded value or point to a file that contains the binary value of the key. python-pskc-1.3/docs/scripts.rst0000644000000000000000000000062013300637004016735 0ustar rootroot00000000000000Command-line scripts ==================== This `python-pskc `__ module includes a number of scripts that can be used to handle PSKC files: Scripts ------- .. toctree:: :maxdepth: 1 pskc2csv: Convert a PSKC file to CSV pskc2pskc: Reformat or change encryption of a PSKC file csv2pskc: Convert a CSV file to PSKC python-pskc-1.3/docs/signatures.rst0000644000000000000000000000774013531212735017452 0ustar rootroot00000000000000XML Signature checking ====================== .. module:: pskc.signature PSKC files can contain embedded XML signatures that allow integrity and authenticity checks of the transmitted information. This signature typically covers the whole PSKC file while MAC checking only covers the encrypted parts. >>> pskc = PSKC('somefile.pskcxml') >>> pskc.signature.verify() >>> pskc = pskc.signature.signed_pskc When using XML signatures it is important to use the :attr:`~pskc.signature.Signature.signed_pskc` attribute after verification because that :class:`~pskc.PSKC` instance will only contain the signed information. To create a signed PSKC file build up a :class:`~pskc.PSKC` instance as usual, configure the signature and save it: >>> pskc.signature.sign(key, certificate) >>> pskc.write('output.pskcxml') The Signature class -------------------- .. class:: Signature .. attribute:: is_signed A boolan value that indicates whether an XML signature is present in the PSKC file. This property does not indicate whether the signature is validated. .. attribute:: algorithm A URI of the signing algorithm used. Assigned values to this attribute will be converted to the canonical URI for the algorithm if it is known. .. attribute:: canonicalization_method A URI that is used to identify the XML canonicalization method used. .. attribute:: digest_algorithm A URI that identifies that hashing algorithm that is used to construct the signature. .. attribute:: issuer A distinguished name of the issuer of the certificate that belongs to the key that is used for the signature. .. attribute:: serial A serial number of the certificate that belongs to the key that is used for the signature. .. attribute:: key A PEM encoded key that will be used to create the signed PSKC file. .. attribute:: certificate A PEM encoded certificate that is embedded inside the signature that can be used to validate the signature. .. attribute:: signed_pskc A :class:`~pskc.PSKC` instance that contains the signed contents of the PSKC file. It is usually required to call :func:`verify` before accessing this attribute without raising an exception. .. function:: verify(certificate=None, ca_pem_file=None) Verify the validity of the embedded XML signature. This function will raise an exception when the validation fails. :param bytes certificate: a PEM encoded certificate that is used for verification :param str ca_pem_file: the name of a file that contains a CA certificate The signature can be verified in three ways: * The signature was made with a key that has a certificate that is signed by a CA that is configured in the system CA store. In this case neither `certificate` or `ca_pem_file` need to be specified (but a certificate needs to be embedded inside the PSKC file). * The signature was made with a key and a certificate for the key was transmitted out-of-band. In this case the `certificate` argument needs to be present. * The signature was made with a key and has a certificate that is signed by a specific CA who's certificate was transmitted out-of-band. In this case the `ca_pem_file` is used to point to a CA certificate file (but a certificate needs to be embedded inside the PSKC file). After calling this function a verified version of the PSKC file will be present in the :attr:`signed_pskc` attribute. .. function:: sign(key, certificate=None) Set up a key and optionally a certificate that will be used to create an embedded XML signature when writing the file. :param bytes key: PEM encoded key used for signing :param bytes certificate: PEM encoded certificate that will be embedded This is a utility function that is used to configure the properties needed to create a signed PSKC file. python-pskc-1.3/docs/usage.rst0000644000000000000000000003434013300352662016364 0ustar rootroot00000000000000Basic usage =========== The :mod:`pskc` module implements a simple and efficient API for parsing and creating PSKC files. The :class:`~pskc.PSKC` class is used to access the file as a whole which provides access to a list of :class:`~pskc.device.Device` and :class:`~pskc.key.Key` instances which contain most of the useful information of the PSKC file. Reading a PSKC file ------------------- Importing data from a PSKC file can be done by instantiating the :class:`~pskc.PSKC` class with a file name argument:: >>> from pskc import PSKC >>> pskc = PSKC('somefile.pskcxml') >>> pskc.version '1.0' The :attr:`~pskc.PSKC.keys` attribute contains a list of keys in the PSKC file. :class:`~pskc.key.Key` instances have a number of attributes that provide information on the transmitted keys:: >>> key = pskc.keys[0] >>> key.id 'some-id' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc:hotp' >>> key.secret 'SOME_SECRET_VALUE' Attribute values will be ``None`` if it the value is not present in the PSKC file. The :attr:`~pskc.key.Key.secret`, :attr:`~pskc.key.Key.counter`, :attr:`~pskc.key.Key.time_offset`, :attr:`~pskc.key.Key.time_interval` or :attr:`~pskc.key.Key.time_drift` attributes may be stored in encrypted form in the PSKC file. Decryption of these properties is done when they are accessed. If decryption is unsuccessful a :exc:`~pskc.exceptions.DecryptionError` exception is raised. See :doc:`encryption` for more information. Writing a PSKC file ------------------- Creating a PSKC file can be done by creating a :class:`~pskc.PSKC` instance, adding keys with :func:`~pskc.PSKC.add_key()` and writing the result:: >>> from pskc import PSKC >>> pskc = PSKC() >>> key = pskc.add_key( ... id='456', secret='987654321', manufacturer='Manufacturer', ... algorithm = 'urn:ietf:params:xml:ns:keyprov:pskc:hotp') >>> pskc.write('output.pskcxml') By default an unencrypted PSKC file will be created but an encryption can be configured using the :func:`~pskc.encryption.Encryption.setup_preshared_key()` or :func:`~pskc.encryption.Encryption.setup_pbkdf2()` function. The PSKC class -------------- .. module:: pskc .. class:: PSKC([filename]) The :class:`PSKC` class is used as a wrapper to access information from a PSKC file. The `filename` argument can be either the name of a file or a file-like object. The whole file is parsed in one go. If parsing the PSKC file fails, a :exc:`~pskc.exceptions.ParseError` exception is raised. If no argument is provided, an instance without any keys is created. Instances of this class provide the following attributes and functions: .. attribute:: version The PSKC format version used. Only version ``1.0`` is currently specified in `RFC 6030 `__ and supported. .. attribute:: id A unique identifier for the container. .. attribute:: devices A list of :class:`~pskc.device.Device` instances that represent the key containers within the PSKC file. .. attribute:: keys A list of :class:`~pskc.key.Key` instances that represent the keys within the PSKC file. .. attribute:: encryption :class:`~pskc.encryption.Encryption` instance that handles PSKC file encryption. See :doc:`encryption` for more information. .. attribute:: mac :class:`~pskc.mac.MAC` instance for handling integrity checking. See :doc:`mac` for more information. .. attribute:: signature :class:`~pskc.signature.Signature` instance for handling embedded XML signatures in the file. See :doc:`signatures` for more information. .. function:: add_device([**kwargs]) Add a new key package to the PSKC instance. The keyword arguments may refer to any attributes of the :class:`~pskc.device.Device` class with which the new device is initialised. .. function:: add_key([**kwargs]) Add a new key to the PSKC instance. The keyword arguments may refer to any attributes of the :class:`~pskc.key.Key` or :class:`~pskc.device.Device` class with which the new key is initialised. .. function:: write(filename) Write the PSKC object to the provided file. The `filename` argument can be either the name of a file or a file-like object. The Key class ------------- .. module:: pskc.key .. class:: Key() Instances of this class provide the following attributes and functions: .. attribute:: id A unique identifier for the key. If there are multiple interactions with the same key in multiple instances of PSKC files the `id` is supposed to remain the same. .. attribute:: algorithm A URI that identifies the PSKC algorithm profile. The algorithm profile associates specific semantics to the key. Some `known profiles `__ are: +------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+ | URI | Purpose | +================================================+=============================================================================================================================+ | ``urn:ietf:params:xml:ns:keyprov:pskc:pin`` | `Symmetric static credential comparison `_ | +------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+ | ``urn:ietf:params:xml:ns:keyprov:pskc:hotp`` | `OATH event-based OTP `_ | +------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+ | ``urn:ietf:params:xml:ns:keyprov:pskc#totp`` | `OATH time-based OTP `_ | | ``urn:ietf:params:xml:ns:keyprov:pskc:totp`` | | +------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+ | ``urn:ietf:params:xml:ns:keyprov:pskc#OCRA-1`` | `OATH challenge-response algorithm `_ | +------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------+ .. attribute:: secret The binary value of the transported secret key. If the key information is encrypted in the PSKC file it is transparently decrypted if possible. Accessing the value may raise :exc:`~pskc.exceptions.DecryptionError` if decryption fails. .. attribute:: counter The event counter (integer) for event-based OTP algorithms. Will also be transparently decrypted and may also raise :exc:`~pskc.exceptions.DecryptionError`. .. attribute:: time_offset The time offset (integer) for time-based OTP algorithms. If time intervals are used it carries the number of time intervals passed from an algorithm-dependent start point. Will also be transparently decrypted and may also raise :exc:`~pskc.exceptions.DecryptionError`. .. attribute:: time_interval The time interval in seconds (integer) for time-based OTP algorithms (usually ``30`` or ``60``). Will also be transparently decrypted and may also raise :exc:`~pskc.exceptions.DecryptionError`. .. attribute:: time_drift For time-based OTP algorithms this contains the device clock drift in number of intervals (integer). Will also be transparently decrypted and may also raise :exc:`~pskc.exceptions.DecryptionError`. .. attribute:: issuer The name of the party that issued the key. This may be different from the :attr:`~pskc.device.Device.manufacturer` of the device. .. attribute:: key_profile A reference to a pre-shared key profile agreed upon between the sending and receiving parties. The profile information itself is not transmitted within the container. See `RFC 6030 `__. .. attribute:: key_reference A reference to an external key that is not contained within the PSKC file (e.g., a PKCS #11 key label). If this attribute is present, the :attr:`secret` attribute will generally be missing. .. attribute:: friendly_name A human-readable name for the secret key. .. attribute:: key_userid The distinguished name of the user associated with the key. Also see :attr:`~pskc.device.Device.device_userid`. .. attribute:: userid The distinguished name of the user associated with the key or the device, taken from :attr:`key_userid` or :attr:`~pskc.device.Device.device_userid` whichever one is defined. .. attribute:: algorithm_suite Additional algorithm-specific characteristics. For example, in an HMAC-based algorithm it could specify the hash algorithm used (SHA1 or SHA256). .. attribute:: challenge_encoding Encoding of the challenge accepted by the device for challenge-response authentication. One of: * ``DECIMAL``: only numerical digits * ``HEXADECIMAL``: hexadecimal * ``ALPHANUMERIC``: all letters and numbers (case sensitive) * ``BASE64``: base-64 encoded * ``BINARY``: binary data .. attribute:: challenge_min_length The minimum size of the challenge accepted by the device. .. attribute:: challenge_max_length The maximum size of the challenge accepted by the device. .. attribute:: challenge_check Boolean that indicates whether the device will check an embedded `Luhn check digit `_ contained in the challenge. .. attribute:: response_encoding Format of the response that is generated by the device. If must be one of the values as described under :attr:`challenge_encoding`. .. attribute:: response_length The length of the response generated by the device. .. attribute:: response_check Boolean that indicates whether the device will append a `Luhn check digit `_ to the response. .. attribute:: policy :class:`~pskc.policy.Policy` instance that provides key and PIN policy information. See :doc:`policy`. .. function:: check() Check if any MACs in the key data embedded in the PSKC file are valid. This will return None if there is no MAC to be checked. It will return True if all the MACs match. If any MAC fails a :exc:`~pskc.exceptions.DecryptionError` exception is raised. Apart from the above, all properties of the :class:`~pskc.device.Device` class are also transparently available in :class:`~pskc.key.Key` instances. The Device class ---------------- .. module:: pskc.device .. class:: Device() Instances of this class provide the following attributes and functions: .. attribute:: keys A list of :class:`~pskc.key.Key` instances that represent the keys that are linked to this device. Most PSKC files only allow one key per device which is why all :class:`~pskc.device.Device` attributes are available in :class:`~pskc.key.Key`. .. function:: add_key([**kwargs]) Add a new key to the device. The keyword arguments may refer to any attributes of the :class:`~pskc.key.Key` or :class:`~pskc.device.Device` class with which the new key is initialised. .. attribute:: manufacturer The name of the manufacturer of the device to which the key is provisioned. `RFC 6030 `__ prescribes that the value is of the form ``oath.prefix`` for `OATH Manufacturer Prefixes `_ or ``iana.organisation`` for `IANA Private Enterprise Numbers `_ however, it is generally just a string. The value may be different from the :attr:`~pskc.key.Key.issuer` of the key on the device. .. attribute:: serial The serial number of the device to which the key is provisioned. Together with :attr:`manufacturer` (and possibly :attr:`issue_no`) this should uniquely identify the device. .. attribute:: model A manufacturer-specific description of the model of the device. .. attribute:: issue_no The issue number in case there are devices with the same :attr:`serial` number so that they can be distinguished by different issue numbers. .. attribute:: device_binding Reference to a device identifier (e.g. IMEI) that allows a provisioning server to ensure that the key is going to be loaded into a specific device. .. attribute:: start_date :class:`datetime.datetime` value that indicates that the device should only be used after this date. .. attribute:: expiry_date :class:`datetime.datetime` value that indicates that the device should only be used before this date. Systems should not rely upon the device to enforce key usage date restrictions, as some devices do not have an internal clock. .. attribute:: device_userid The distinguished name of the user associated with the device. Also see :attr:`~pskc.key.Key.key_userid`. .. attribute:: crypto_module Implementation specific unique identifier of the cryptographic module on the device to which the keys have been (or will be) provisioned. python-pskc-1.3/pskc/0000755000000000000000000000000014667356044014551 5ustar rootroot00000000000000python-pskc-1.3/pskc/__init__.py0000644000000000000000000001027114667353227016663 0ustar rootroot00000000000000# __init__.py - main module # coding: utf-8 # # Copyright (C) 2014-2024 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Python module for handling PSKC files. This Python library handles Portable Symmetric Key Container (PSKC) files as defined in RFC 6030. PSKC files are used to transport and provision symmetric keys (seed files) to different types of crypto modules, commonly one-time password tokens or other authentication devices. This module can be used to extract keys from PSKC files for use in an OTP authentication system. The module can also be used for authoring PSKC files. The following prints all keys, decrypting using a password: >>> from pskc import PSKC >>> pskc = PSKC('tests/rfc6030/figure7.pskcxml') >>> pskc.encryption.derive_key('qwerty') >>> for key in pskc.keys: ... print('%s %s' % (key.serial, str(key.secret.decode()))) 987654321 12345678901234567890 The following generates an encrypted PSKC file: >>> pskc = PSKC() >>> key = pskc.add_key( ... id='456', secret='987654321', manufacturer='Manufacturer', ... algorithm = 'urn:ietf:params:xml:ns:keyprov:pskc:hotp') >>> pskc.encryption.setup_pbkdf2('passphrase') >>> pskc.write('output.pskcxml') The module should be able to handle most common PSKC files. """ __all__ = ['PSKC', '__version__'] # the version number of the library __version__ = '1.3' class PSKC(object): """Wrapper module for parsing a PSKC file. Instances of this class provide the following attributes: version: the PSKC format version used (1.0) id: identifier encryption: information on used encryption (Encryption instance) mac: information on used MAC method (MAC instance) devices: list of devices (Device instances) keys: list of keys (Key instances) """ def __init__(self, filename=None): from pskc.encryption import Encryption from pskc.signature import Signature from pskc.mac import MAC self.version = None self.id = None self.encryption = Encryption(self) self.signature = Signature(self) self.mac = MAC(self) self.devices = [] if filename is not None: from pskc.parser import PSKCParser PSKCParser.parse_file(self, filename) else: self.version = '1.0' @property def keys(self): """Provide a list of keys.""" return tuple(key for device in self.devices for key in device.keys) def add_device(self, **kwargs): """Create a new device instance for the PSKC file. The device is initialised with properties from the provided keyword arguments if any. """ from pskc.device import Device, update_attributes device = Device(self) self.devices.append(device) update_attributes(device, **kwargs) return device def add_key(self, **kwargs): """Create a new key instance for the PSKC file. The new key is initialised with properties from the provided keyword arguments if any. """ from pskc.device import update_attributes device = self.add_device() key = device.add_key() update_attributes(key, **kwargs) return key def write(self, filename): """Write the PSKC file to the provided file.""" from pskc.serialiser import PSKCSerialiser if hasattr(filename, 'write'): PSKCSerialiser.serialise_file(self, filename) else: with open(filename, 'wb') as output: self.write(output) python-pskc-1.3/pskc/algorithms.py0000644000000000000000000001007614104475311017261 0ustar rootroot00000000000000# algorithms.py - module for handling algorithm URIs # coding: utf-8 # # Copyright (C) 2016-2017 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Utility module that handles algorithm URIs.""" # canonical URIs of known algorithms # Note that even if a URI is listed here it does not mean that # the algorithm is supported in python-pskc. _algorithms = dict((x.rsplit('#', 1)[-1], x) for x in [ 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc', 'http://www.w3.org/2001/04/xmlenc#kw-tripledes', 'http://www.w3.org/2001/04/xmlenc#arcfour', 'http://www.w3.org/2001/04/xmlenc#aes128-cbc', 'http://www.w3.org/2001/04/xmlenc#aes192-cbc', 'http://www.w3.org/2001/04/xmlenc#aes256-cbc', 'http://www.w3.org/2001/04/xmlenc#kw-aes128', 'http://www.w3.org/2001/04/xmlenc#kw-aes192', 'http://www.w3.org/2001/04/xmlenc#kw-aes256', 'http://www.w3.org/2009/xmlenc11#aes128-gcm', 'http://www.w3.org/2009/xmlenc11#aes192-gcm', 'http://www.w3.org/2009/xmlenc11#aes256-gcm', 'http://www.w3.org/2009/xmlenc11#kw-aes-128-pad', 'http://www.w3.org/2009/xmlenc11#kw-aes-192-pad', 'http://www.w3.org/2009/xmlenc11#kw-aes-256-pad', 'http://www.w3.org/2001/04/xmldsig-more#camellia128-cbc', 'http://www.w3.org/2001/04/xmldsig-more#camellia192-cbc', 'http://www.w3.org/2001/04/xmldsig-more#camellia256-cbc', 'http://www.w3.org/2001/04/xmldsig-more#kw-camellia128', 'http://www.w3.org/2001/04/xmldsig-more#kw-camellia192', 'http://www.w3.org/2001/04/xmldsig-more#kw-camellia256', 'http://www.w3.org/2007/05/xmldsig-more#seed128-cbc' 'http://www.w3.org/2007/05/xmldsig-more#kw-seed128', 'http://www.w3.org/2001/04/xmldsig-more#hmac-md5', 'http://www.w3.org/2000/09/xmldsig#hmac-sha1', 'http://www.w3.org/2001/04/xmldsig-more#hmac-sha224', 'http://www.w3.org/2001/04/xmldsig-more#hmac-sha256', 'http://www.w3.org/2001/04/xmldsig-more#hmac-sha384', 'http://www.w3.org/2001/04/xmldsig-more#hmac-sha512', 'http://www.w3.org/2001/04/xmldsig-more#hmac-ripemd160', 'http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2', # a newer URI seems to be http://www.w3.org/2009/xmlenc11#pbkdf2 ]) # translation table to change old encryption names to new names _algorithm_aliases = { '3des-cbc': 'tripledes-cbc', '3des112-cbc': 'tripledes-cbc', '3des168-cbc': 'tripledes-cbc', 'rc4': 'arcfour', 'kw-aes128-pad': 'kw-aes-128-pad', 'kw-aes192-pad': 'kw-aes-192-pad', 'kw-aes256-pad': 'kw-aes-256-pad', 'camellia128': 'camellia128-cbc', 'camellia192': 'camellia192-cbc', 'camellia256': 'camellia256-cbc', 'hmac-sha-1': 'hmac-sha1', 'hmac-sha-224': 'hmac-sha224', 'hmac-sha-256': 'hmac-sha256', 'hmac-sha-384': 'hmac-sha384', 'hmac-sha-512': 'hmac-sha512', 'hmac-ripemd-160': 'hmac-ripemd160', 'kw-3des': 'kw-tripledes', 'pbe-3des112-cbc': 'tripledes-cbc', 'pbe-3des168-cbc': 'tripledes-cbc', 'pbe-aes128-cbc': 'aes128-cbc', 'pbe-aes192-cbc': 'aes192-cbc', 'pbe-aes256-cbc': 'aes256-cbc', 'rsa-1_5': 'rsa-1_5', 'rsa-oaep-mgf1p': 'rsa-oaep-mgf1p', } def normalise_algorithm(algorithm): """Return the canonical URI for the provided algorithm.""" if not algorithm or algorithm.lower() == 'none': return None algorithm = _algorithm_aliases.get(algorithm.lower(), algorithm) return _algorithms.get(algorithm.rsplit('#', 1)[-1].lower(), algorithm) python-pskc-1.3/pskc/crypto/0000755000000000000000000000000014667356044016071 5ustar rootroot00000000000000python-pskc-1.3/pskc/crypto/__init__.py0000644000000000000000000000155413221434047020170 0ustar rootroot00000000000000# __init__.py - general crypto utility functions # coding: utf-8 # # Copyright (C) 2017 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Implement crypto utility functions.""" python-pskc-1.3/pskc/crypto/aeskw.py0000644000000000000000000001103414667346555017562 0ustar rootroot00000000000000# aeskw.py - implementation of AES key wrapping # coding: utf-8 # # Copyright (C) 2014-2024 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Implement key wrapping as described in RFC 3394 and RFC 5649.""" import binascii import struct from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from pskc.exceptions import DecryptionError, EncryptionError def _strxor(a, b): """Return a XOR b.""" return bytes(x ^ y for (x, y) in zip(a, b)) def _split(value): return value[:8], value[8:] RFC3394_IV = binascii.a2b_hex('a6a6a6a6a6a6a6a6') RFC5649_IV = binascii.a2b_hex('a65959a6') def wrap(plaintext, key, iv=None, pad=None, algorithm=algorithms.AES): """Apply the AES key wrap algorithm to the plaintext. The iv can specify an initial value, otherwise the value from RFC 3394 or RFC 5649 will be used, depending on the plaintext length and the value of pad. If pad is True, padding as described in RFC 5649 will always be used. If pad is False, padding is disabled. Other values automatically enable RFC 5649 padding when needed. """ if iv is not None: pad = False mli = len(plaintext) if pad is False and (mli % 8 != 0 or mli < 16): raise EncryptionError('Plaintext length wrong') if mli % 8 != 0 and pad is not False: r = (mli + 7) // 8 plaintext += ((r * 8) - mli) * b'\0' if iv is None: if len(plaintext) != mli or pad is True: iv = RFC5649_IV + struct.pack('>I', mli) else: iv = RFC3394_IV cipher = Cipher(algorithm(key), modes.ECB(), default_backend()) encryptor = cipher.encryptor() n = len(plaintext) // 8 if n == 1: # RFC 5649 shortcut return encryptor.update(iv + plaintext) A = iv # noqa: N806 R = [plaintext[i * 8:i * 8 + 8] # noqa: N806 for i in range(n)] for j in range(6): for i in range(n): A, R[i] = _split(encryptor.update(A + R[i])) # noqa: N806 A = _strxor(A, struct.pack('>Q', n * j + i + 1)) # noqa: N806 return A + b''.join(R) def unwrap(ciphertext, key, iv=None, pad=None, algorithm=algorithms.AES): """Apply the AES key unwrap algorithm to the ciphertext. The iv can specify an initial value, otherwise the value from RFC 3394 or RFC 5649 will be used, depending on the value of pad. If pad is False, unpadding as described in RFC 5649 will be disabled, otherwise checking and removing the padding is automatically done. """ if iv is not None: pad = False if len(ciphertext) % 8 != 0 or (pad is False and len(ciphertext) < 24): raise DecryptionError('Ciphertext length wrong') cipher = Cipher(algorithm(key), modes.ECB(), default_backend()) decryptor = cipher.decryptor() n = len(ciphertext) // 8 - 1 if n == 1: A, plaintext = _split(decryptor.update(ciphertext)) # noqa: N806 else: A = ciphertext[:8] # noqa: N806 R = [ciphertext[(i + 1) * 8:(i + 2) * 8] # noqa: N806 for i in range(n)] for j in reversed(range(6)): for i in reversed(range(n)): A = _strxor(A, struct.pack('>Q', n * j + i + 1)) # noqa: N806 A, R[i] = _split(decryptor.update(A + R[i])) # noqa: N806 plaintext = b''.join(R) if iv is None: if A == RFC3394_IV and pad is not True: return plaintext elif A[:4] == RFC5649_IV and pad is not False: mli = struct.unpack('>I', A[4:])[0] # check padding length is valid and plaintext only contains zeros if 8 * (n - 1) < mli <= 8 * n and \ plaintext.endswith((len(plaintext) - mli) * b'\0'): return plaintext[:mli] elif A == iv: return plaintext raise DecryptionError('IV does not match') python-pskc-1.3/pskc/crypto/tripledeskw.py0000644000000000000000000000602513221434047020764 0ustar rootroot00000000000000# tripledeskw.py - implementation of Triple DES key wrapping # coding: utf-8 # # Copyright (C) 2014-2017 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Implement Triple DES key wrapping as described in RFC 3217.""" import binascii import hashlib import os from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from pskc.exceptions import DecryptionError, EncryptionError def _cms_hash(value): """Return the key hash algorithm described in RFC 3217 section 2.""" return hashlib.sha1(value).digest()[:8] RFC3217_IV = binascii.a2b_hex('4adda22c79e82105') def wrap(plaintext, key, iv=None): """Wrap one key (typically a Triple DES key) with another Triple DES key. This uses the algorithm from RFC 3217 to encrypt the plaintext (the key to wrap) using the provided key. If the iv is None, it is randomly generated. """ if 8 * len(plaintext) % algorithms.TripleDES.block_size != 0: raise EncryptionError('Plaintext length wrong') if iv is None: iv = os.urandom(8) backend = default_backend() cipher = Cipher(algorithms.TripleDES(key), modes.CBC(iv), backend) encryptor = cipher.encryptor() tmp = ( iv + encryptor.update(plaintext + _cms_hash(plaintext)) + encryptor.finalize()) cipher = Cipher(algorithms.TripleDES(key), modes.CBC(RFC3217_IV), backend) encryptor = cipher.encryptor() return encryptor.update(tmp[::-1]) + encryptor.finalize() def unwrap(ciphertext, key): """Unwrap a key (typically Triple DES key ) with another Triple DES key. This uses the algorithm from RFC 3217 to decrypt the ciphertext (the previously wrapped key) using the provided key. """ if 8 * len(ciphertext) % algorithms.TripleDES.block_size != 0: raise DecryptionError('Ciphertext length wrong') backend = default_backend() cipher = Cipher(algorithms.TripleDES(key), modes.CBC(RFC3217_IV), backend) decryptor = cipher.decryptor() tmp = (decryptor.update(ciphertext) + decryptor.finalize())[::-1] cipher = Cipher(algorithms.TripleDES(key), modes.CBC(tmp[:8]), backend) decryptor = cipher.decryptor() tmp = decryptor.update(tmp[8:]) + decryptor.finalize() if tmp[-8:] == _cms_hash(tmp[:-8]): return tmp[:-8] raise DecryptionError('CMS key checksum error') python-pskc-1.3/pskc/device.py0000644000000000000000000000517013300356475016354 0ustar rootroot00000000000000# device.py - module for handling device info from pskc files # coding: utf-8 # # Copyright (C) 2016-2018 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Module that handles device information stored in PSKC files.""" def update_attributes(obj, **kwargs): """Update object with provided properties.""" for k, v in kwargs.items(): k = k.split('.') if '.' in k else k.split('__') o = obj for name in k[:-1]: o = getattr(o, name) getattr(o, k[-1]) # raise exception for non-existing properties setattr(o, k[-1], v) class Device(object): """Representation of a single key from a PSKC file. Instances of this class provide the following properties: manufacturer: name of the organisation that made the device serial: serial number of the device model: device model description issue_no: issue number per serial number device_binding: device (class) identifier for the key to be loaded upon start_date: key should not be used before this date expiry_date: key or device may expire after this date device_userid: user distinguished name associated with the device crypto_module: id of module to which keys are provisioned within device """ def __init__(self, pskc): self.pskc = pskc self.manufacturer = None self.serial = None self.model = None self.issue_no = None self.device_binding = None self.start_date = None self.expiry_date = None self.device_userid = None self.crypto_module = None self.keys = [] def add_key(self, **kwargs): """Create a new key instance for the device. The new key is initialised with properties from the provided keyword arguments if any. """ from pskc.key import Key key = Key(self) self.keys.append(key) update_attributes(key, **kwargs) return key python-pskc-1.3/pskc/encryption.py0000644000000000000000000004144014667346555017326 0ustar rootroot00000000000000# encryption.py - module for handling encrypted values # coding: utf-8 # # Copyright (C) 2014-2024 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Module that handles encrypted PSKC values. This module defines an Encryption class that handles the encryption key, algorithms and decryption. The encryption key can be derived using the KeyDerivation class. """ import os import re def algorithm_key_lengths(algorithm): """Return the possible key lengths for the configured algorithm.""" from pskc.exceptions import DecryptionError if algorithm is None: raise DecryptionError('No algorithm specified') elif algorithm.endswith('#aes128-cbc') or \ algorithm.endswith('#aes192-cbc') or \ algorithm.endswith('#aes256-cbc'): return [int(algorithm[-7:-4]) // 8] elif algorithm.endswith('#tripledes-cbc') or \ algorithm.endswith('#kw-tripledes'): return [16, 24] elif algorithm.endswith('#kw-aes128') or \ algorithm.endswith('#kw-aes192') or \ algorithm.endswith('#kw-aes256'): return [int(algorithm[-3:]) // 8] elif (algorithm.endswith('#camellia128-cbc') or algorithm.endswith('#camellia192-cbc') or algorithm.endswith('#camellia256-cbc')): return [int(algorithm[-7:-4]) // 8] elif (algorithm.endswith('#kw-camellia128') or algorithm.endswith('#kw-camellia192') or algorithm.endswith('#kw-camellia256')): return [int(algorithm[-3:]) // 8] else: raise DecryptionError('Unsupported algorithm: %r' % algorithm) def _decrypt_cbc(algorithm, key, ciphertext, iv=None): """Decrypt the ciphertext and return the plaintext value.""" from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import padding from cryptography.hazmat.primitives.ciphers import Cipher, modes from pskc.exceptions import DecryptionError if not iv: iv = ciphertext[:algorithm.block_size // 8] ciphertext = ciphertext[algorithm.block_size // 8:] cipher = Cipher( algorithm(key), modes.CBC(iv), backend=default_backend()) decryptor = cipher.decryptor() unpadder = padding.PKCS7(algorithm.block_size).unpadder() try: return unpadder.update( decryptor.update(ciphertext) + decryptor.finalize()) + unpadder.finalize() except ValueError: raise DecryptionError('Invalid padding') def decrypt(algorithm, key, ciphertext, iv=None): """Decrypt the ciphertext and return the plaintext value.""" from cryptography.hazmat.primitives.ciphers import algorithms from pskc.exceptions import DecryptionError if key is None: raise DecryptionError('No key available') if algorithm is None: raise DecryptionError('No algorithm specified') if len(key) not in algorithm_key_lengths(algorithm): raise DecryptionError('Invalid key length') if algorithm.endswith('#aes128-cbc') or \ algorithm.endswith('#aes192-cbc') or \ algorithm.endswith('#aes256-cbc'): return _decrypt_cbc(algorithms.AES, key, ciphertext, iv) elif algorithm.endswith('#tripledes-cbc'): return _decrypt_cbc(algorithms.TripleDES, key, ciphertext, iv) elif algorithm.endswith('#kw-aes128') or \ algorithm.endswith('#kw-aes192') or \ algorithm.endswith('#kw-aes256'): from pskc.crypto.aeskw import unwrap return unwrap(ciphertext, key) elif algorithm.endswith('#kw-tripledes'): from pskc.crypto.tripledeskw import unwrap return unwrap(ciphertext, key) elif (algorithm.endswith('#camellia128-cbc') or algorithm.endswith('#camellia192-cbc') or algorithm.endswith('#camellia256-cbc')): return _decrypt_cbc(algorithms.Camellia, key, ciphertext, iv) elif (algorithm.endswith('#kw-camellia128') or # pragma: no branch algorithm.endswith('#kw-camellia192') or algorithm.endswith('#kw-camellia256')): from pskc.crypto.aeskw import unwrap return unwrap(ciphertext, key, algorithm=algorithms.Camellia) # no fallthrough because algorithm_key_lengths() fails with unknown algo def _encrypt_cbc(algorithm, key, plaintext, iv=None): """Encrypt the provided value with the key using the algorithm.""" from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import padding from cryptography.hazmat.primitives.ciphers import Cipher, modes iv = iv or os.urandom(algorithm.block_size // 8) cipher = Cipher( algorithm(key), modes.CBC(iv), backend=default_backend()) encryptor = cipher.encryptor() padder = padding.PKCS7(algorithm.block_size).padder() return ( iv + encryptor.update( padder.update(plaintext) + padder.finalize()) + encryptor.finalize()) def encrypt(algorithm, key, plaintext, iv=None): """Encrypt the provided value with the key using the algorithm.""" from cryptography.hazmat.primitives.ciphers import algorithms from pskc.exceptions import EncryptionError if key is None: raise EncryptionError('No key available') if algorithm is None: raise EncryptionError('No algorithm specified') if len(key) not in algorithm_key_lengths(algorithm): raise EncryptionError('Invalid key length') if algorithm.endswith('#aes128-cbc') or \ algorithm.endswith('#aes192-cbc') or \ algorithm.endswith('#aes256-cbc'): return _encrypt_cbc(algorithms.AES, key, plaintext, iv) elif algorithm.endswith('#tripledes-cbc'): return _encrypt_cbc(algorithms.TripleDES, key, plaintext, iv) elif algorithm.endswith('#kw-aes128') or \ algorithm.endswith('#kw-aes192') or \ algorithm.endswith('#kw-aes256'): from pskc.crypto.aeskw import wrap return wrap(plaintext, key) elif algorithm.endswith('#kw-tripledes'): from pskc.crypto.tripledeskw import wrap return wrap(plaintext, key) elif (algorithm.endswith('#camellia128-cbc') or algorithm.endswith('#camellia192-cbc') or algorithm.endswith('#camellia256-cbc')): return _encrypt_cbc(algorithms.Camellia, key, plaintext, iv) elif (algorithm.endswith('#kw-camellia128') or # pragma: no branch algorithm.endswith('#kw-camellia192') or algorithm.endswith('#kw-camellia256')): from pskc.crypto.aeskw import wrap return wrap(plaintext, key, algorithm=algorithms.Camellia) # no fallthrough because algorithm_key_lengths() fails with unknown algo class KeyDerivation(object): """Handle key derivation. The algorithm property contains the key derivation algorithm to use. For PBDKF2 the following parameters are set: pbkdf2_salt: salt value pbkdf2_iterations: number of iterations to use pbkdf2_key_length: required key length in bytes pbkdf2_prf: name of pseudorandom function used """ def __init__(self): self._algorithm = None # PBKDF2 properties self.pbkdf2_salt = None self.pbkdf2_iterations = None self.pbkdf2_key_length = None self._pbkdf2_prf = None @property def algorithm(self): """Provide the key derivation algorithm used.""" if self._algorithm: return self._algorithm @algorithm.setter def algorithm(self, value): from pskc.algorithms import normalise_algorithm self._algorithm = normalise_algorithm(value) @property def pbkdf2_prf(self): """Provide the PBKDF2 pseudorandom function used.""" if self._pbkdf2_prf: return self._pbkdf2_prf @pbkdf2_prf.setter def pbkdf2_prf(self, value): from pskc.algorithms import normalise_algorithm self._pbkdf2_prf = normalise_algorithm(value) def derive_pbkdf2(self, password): """Derive an encryption key from the provided password.""" from hashlib import pbkdf2_hmac from pskc.exceptions import KeyDerivationError prf = 'sha1' if self.pbkdf2_prf: match = re.search( r'^(.*#)?hmac-(?P[a-z0-9-]+)$', self.pbkdf2_prf) if match: prf = match.group('hash') else: raise KeyDerivationError( 'Unsupported PRF: %r' % self.pbkdf2_prf) if not all((password, self.pbkdf2_salt, self.pbkdf2_key_length, self.pbkdf2_iterations)): raise KeyDerivationError('Incomplete PBKDF2 configuration') # force conversion to bytestring if not isinstance(password, type(b'')): password = password.encode() try: return pbkdf2_hmac( prf, password, self.pbkdf2_salt, self.pbkdf2_iterations, self.pbkdf2_key_length) except ValueError: raise KeyDerivationError( 'Pseudorandom function unsupported: %r' % self.pbkdf2_prf) def derive(self, password): """Derive a key from the password.""" from pskc.exceptions import KeyDerivationError if self.algorithm is None: raise KeyDerivationError('No algorithm specified') if self.algorithm.endswith('#pbkdf2'): return self.derive_pbkdf2(password) else: raise KeyDerivationError( 'Unsupported algorithm: %r' % self.algorithm) def setup_pbkdf2(self, password, salt=None, salt_length=16, key_length=None, iterations=None, prf=None): """Configure PBKDF2 key derivation properties.""" self.algorithm = 'pbkdf2' if salt is None: salt = os.urandom(salt_length) self.pbkdf2_salt = salt if iterations: self.pbkdf2_iterations = iterations elif self.pbkdf2_iterations is None: self.pbkdf2_iterations = 100000 if key_length: # pragma: no branch (always specified) self.pbkdf2_key_length = key_length if prf: self.pbkdf2_prf = prf return self.derive_pbkdf2(password) class Encryption(object): """Class for handling encryption keys that are used in the PSKC file. Encryption generally uses a symmetric key that is used to encrypt some of the information stored in PSKC files (typically the seed). This class provides the following values: id: identifier of the key algorithm: the encryption algorithm used key_names: list of names for the key key_name: (first) name of the key (usually there is only one) key: the key value itself (binary form) iv: optional initialization vector for CBC based encryption fields: a list of Key fields that will be encrypted on writing The key can either be assigned to the key property or derived using the derive_key() method. """ def __init__(self, pskc): self.pskc = pskc self.id = None self._algorithm = None self.key_names = [] self.key = None self.iv = None self.derivation = KeyDerivation() self.fields = [] @property def key_name(self): """Provide the name of the (first) key.""" if self.key_names: return self.key_names[0] @key_name.setter def key_name(self, value): self.key_names = [value] @property def algorithm(self): """Provide the encryption algorithm used.""" if self._algorithm: return self._algorithm @algorithm.setter def algorithm(self, value): from pskc.algorithms import normalise_algorithm self._algorithm = normalise_algorithm(value) @property def is_encrypted(self): """Test whether the PSKC file requires a decryption key.""" from pskc.exceptions import DecryptionError try: for key in self.pskc.keys: key.secret, key.counter, key.time_offset key.time_interval, key.time_drift except DecryptionError: return True return False def derive_key(self, password): """Derive a key from the password.""" self.key = self.derivation.derive(password) def _setup_encryption(self, kwargs): for k in ('id', 'algorithm', 'key_name', 'key_names', 'fields'): v = kwargs.pop(k, None) if v is not None: setattr(self, k, v) # default encryption to AES128-CBC if not self.algorithm: self.algorithm = 'aes128-cbc' # default to encrypting the secret only if not self.fields: self.fields = ['secret'] # if we're using a CBC mode of encryption, add a MAC if self.algorithm.endswith('-cbc'): self.pskc.mac.setup() def setup_preshared_key(self, **kwargs): """Configure pre-shared key encryption when writing the file. The following arguments may be supplied: key: the encryption key to use id: encryption key identifier algorithm: encryption algorithm key_length: encryption key length in bytes key_name: a name for the key key_names: a number of names for the key fields: a list of fields to encrypt None of the arguments are required, reasonable defaults will be chosen for missing arguments. """ self._setup_encryption(kwargs) self.key = kwargs.pop('key', self.key) if not self.key: self.key = os.urandom( kwargs.pop('key_length', self.algorithm_key_lengths[-1])) def setup_pbkdf2(self, password, **kwargs): """Configure password-based PSKC encryption when writing the file. The following arguments may be supplied: password: the password to use (required) id: encryption key identifier algorithm: encryption algorithm key_length: encryption key length in bytes key_name: a name for the key key_names: a number of names for the key fields: a list of fields to encrypt salt: PBKDF2 salt salt_length: used when generating random salt iterations: number of PBKDF2 iterations prf: PBKDF2 pseudorandom function Only password is required, for the other arguments reasonable defaults will be chosen. """ self._setup_encryption(kwargs) # pass a key length to PBKDF2 kwargs.setdefault('key_length', self.algorithm_key_lengths[-1]) self.key = self.derivation.setup_pbkdf2(password, **kwargs) @property def algorithm_key_lengths(self): """Provide the possible key lengths for the configured algorithm.""" return algorithm_key_lengths(self.algorithm) def decrypt_value(self, cipher_value, algorithm=None): """Decrypt the cipher_value and return the plaintext value.""" return decrypt( algorithm or self.algorithm, self.key, cipher_value, self.iv) def encrypt_value(self, plaintext): """Encrypt the provided value and return the cipher_value.""" cipher_value = encrypt(self.algorithm, self.key, plaintext, self.iv) if self.iv: cipher_value = cipher_value[len(self.iv):] return cipher_value def remove_encryption(self): """Decrypt all values and remove the encryption from the PSKC file.""" # decrypt all values and store decrypted values for key in self.pskc.keys: key.secret = key.secret key.counter = key.counter key.time_offset = key.time_offset key.time_interval = key.time_interval key.time_drift = key.time_drift # remove MAC configuration self.pskc.mac.algorithm = None self.pskc.mac.key = None # remove encryption configuration self.id = None self.algorithm = None self.key_names = [] self.key = None self.iv = None self.fields = [] # remove key derivation configuration self.derivation.algorithm = None self.derivation.pbkdf2_salt = None self.derivation.pbkdf2_iterations = None self.derivation.pbkdf2_key_length = None self.derivation.pbkdf2_prf = None python-pskc-1.3/pskc/exceptions.py0000644000000000000000000000277713221434047017302 0ustar rootroot00000000000000# exceptions.py - collection of pskc exceptions # coding: utf-8 # # Copyright (C) 2014 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Collection of exceptions.""" class PSKCError(Exception): """General top-level exception.""" pass class ParseError(PSKCError): """Something went wrong with parsing the PSKC file. Either the file is invalid XML or required elements or attributes are missing. """ pass class EncryptionError(PSKCError): """There was a problem encrypting the value.""" pass class DecryptionError(PSKCError): """There was a problem decrypting the value. The encrypted value as available but something went wrong with decrypting it. """ pass class KeyDerivationError(PSKCError): """There was a problem performing the key derivation.""" pass python-pskc-1.3/pskc/key.py0000644000000000000000000001677114667346555015735 0ustar rootroot00000000000000# key.py - module for handling keys from pskc files # coding: utf-8 # # Copyright (C) 2014-2024 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Module that handles keys stored in PSKC files.""" import array import binascii from pskc.policy import Policy class EncryptedValue(object): """A container for an encrypted value.""" def __init__(self, cipher_value, mac_value, algorithm): self.cipher_value = cipher_value self.mac_value = mac_value self.algorithm = algorithm @classmethod def create(cls, pskc, value): """Construct an encrypted value from a plaintext value.""" # force conversion to bytestring if not isinstance(value, (type(b''), bytearray)): value = value.encode() cipher_value = pskc.encryption.encrypt_value(value) mac_value = None if pskc.mac.algorithm: mac_value = pskc.mac.generate_mac(cipher_value) return cls(cipher_value, mac_value, pskc.encryption.algorithm) def get_value(self, pskc): """Provide the decrypted value.""" from pskc.exceptions import DecryptionError plaintext = pskc.encryption.decrypt_value( self.cipher_value, self.algorithm) # allow MAC over plaintext or ciphertext # (RFC 6030 implies MAC over ciphertext but older draft used # MAC over plaintext) if self.mac_value and self.mac_value not in ( pskc.mac.generate_mac(self.cipher_value), pskc.mac.generate_mac(plaintext)): raise DecryptionError('MAC value does not match') return plaintext class EncryptedIntegerValue(EncryptedValue): """Class representing an encrypted integer value.""" @classmethod def create(cls, pskc, value): """Construct an encrypted value from a plaintext value.""" value = '%x' % value n = len(value) value = binascii.unhexlify(value.zfill(n + (n & 1))) return super(EncryptedIntegerValue, cls).create(pskc, value) def get_value(self, pskc): """Provide the decrypted integer value.""" value = super(EncryptedIntegerValue, self).get_value(pskc) # try to handle value as ASCII representation if value.isdigit(): return int(value) # fall back to do big-endian decoding result = 0 for x in array.array('B', value): result = (result << 8) + x return result class DataTypeProperty(object): """A data descriptor that delegates actions to DataType instances.""" def __init__(self, name, doc): self.name = name self.__doc__ = doc def __get__(self, obj, objtype): value = getattr(obj, '_' + self.name, None) if hasattr(value, 'get_value'): return value.get_value(obj.device.pskc) else: return value def __set__(self, obj, val): setattr(obj, '_' + self.name, val) class DeviceProperty(object): """A data descriptor that delegates actions to the Device instance.""" def __init__(self, name): self.name = name def __get__(self, obj, objtype): return getattr(obj.device, self.name) def __set__(self, obj, val): setattr(obj.device, self.name, val) class Key(object): """Representation of a single key from a PSKC file. Instances of this class provide the following properties: id: unique key identifier (should be constant between interchanges) algorithm: identifier of the PSKC algorithm profile (URI) secret: the secret key itself (binary form, automatically decrypted) counter: event counter for event-based OTP time_offset: time offset for time-based OTP algorithms (in intervals) time_interval: time interval for time-based OTP in seconds time_drift: device clock drift (negative means device is slow) issuer: party that issued the key key_profile: reference to pre-shared key profile information key_reference: reference to an external key friendly_name: human-readable name for the secret key key_userid: user distinguished name associated with the key algorithm_suite: additional algorithm characteristics (e.g. used hash) challenge_encoding: format of the challenge for CR devices challenge_min_length: minimum accepted challenge length by device challenge_max_length: maximum size challenge accepted by the device challenge_check: whether the device will check an embedded check digit response_encoding: format of the response the device will generate response_length: the length of the response of the device response_check: whether the device appends a Luhn check digit policy: reference to policy information (see Policy class) This class also provides access to the manufacturer, serial, model, issue_no, device_binding, start_date, expiry_date, device_userid and crypto_module properties of the Device class. """ def __init__(self, device): self.device = device self.id = None self.algorithm = None self.issuer = None self.key_profile = None self.key_reference = None self.friendly_name = None self.key_userid = None self.algorithm_suite = None self.challenge_encoding = None self.challenge_min_length = None self.challenge_max_length = None self.challenge_check = None self.response_encoding = None self.response_length = None self.response_check = None self.policy = Policy(self) secret = DataTypeProperty( 'secret', 'The secret key itself.') counter = DataTypeProperty( 'counter', 'An event counter for event-based OTP.') time_offset = DataTypeProperty( 'time_offset', 'A time offset for time-based OTP (number of intervals).') time_interval = DataTypeProperty( 'time_interval', 'A time interval in seconds.') time_drift = DataTypeProperty( 'time_drift', 'Device clock drift value (number of time intervals).') manufacturer = DeviceProperty('manufacturer') serial = DeviceProperty('serial') model = DeviceProperty('model') issue_no = DeviceProperty('issue_no') device_binding = DeviceProperty('device_binding') start_date = DeviceProperty('start_date') expiry_date = DeviceProperty('expiry_date') device_userid = DeviceProperty('device_userid') crypto_module = DeviceProperty('crypto_module') def check(self): """Check if all MACs in the message are valid.""" if all(x is not False for x in ( self.secret, self.counter, self.time_offset, self.time_interval, self.time_drift)): return True @property def userid(self): """User identifier (either the key or device userid).""" return self.key_userid or self.device_userid python-pskc-1.3/pskc/mac.py0000644000000000000000000001065213300356475015656 0ustar rootroot00000000000000# mac.py - module for checking value signatures # coding: utf-8 # # Copyright (C) 2014-2018 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Module that provides message authentication for PSKC values. This module provides a MAC class that is used to store information about how the MAC should be calculated (including the MAC key) and a ValueMAC class that provides (H)MAC checking for PSKC key data. The MAC key is generated specifically for each PSKC file and encrypted with the PSKC encryption key. """ import os import re _hmac_url_re = re.compile(r'^(.*#)?hmac-(?P[a-z0-9-]+)$') def _get_hash_obj(algorithm, *args): """Return an instantiated hash object.""" import hashlib from pskc.algorithms import normalise_algorithm from pskc.exceptions import DecryptionError match = _hmac_url_re.search(normalise_algorithm(algorithm) or '') if match: try: return hashlib.new(match.group('hash'), *args) except ValueError: pass raise DecryptionError('Unsupported MAC algorithm: %r' % algorithm) def mac(algorithm, key, value): """Generate the MAC value over the specified value.""" import hmac return hmac.new( key, value, lambda *args: _get_hash_obj(algorithm, *args)).digest() def mac_key_length(algorithm): """Recommended minimal key length in bytes for the set algorithm.""" # https://tools.ietf.org/html/rfc2104#section-3 # an HMAC key should be at least as long as the hash output length from pskc.exceptions import DecryptionError try: return int(_get_hash_obj(algorithm).digest_size) except DecryptionError: return 16 # fallback value class MAC(object): """Class describing the MAC algorithm to use and how to get the key. Instances of this class provide the following attributes: algorithm: the name of the HMAC to use (currently only HMAC_SHA1) key: the binary value of the MAC key if it can be decrypted """ def __init__(self, pskc): self.pskc = pskc self._algorithm = None @property def key(self): """Provide access to the MAC key binary value if available.""" value = getattr(self, '_key', None) if hasattr(value, 'get_value'): return value.get_value(self.pskc) elif value: return value else: # fall back to encryption key return self.pskc.encryption.key @key.setter def key(self, value): self._key = value @property def algorithm(self): """Provide the MAC algorithm used.""" if self._algorithm: return self._algorithm @algorithm.setter def algorithm(self, value): from pskc.algorithms import normalise_algorithm self._algorithm = normalise_algorithm(value) @property def algorithm_key_length(self): """Recommended minimal key length in bytes for the set algorithm.""" return mac_key_length(self.algorithm) def generate_mac(self, value): """Generate the MAC over the specified value.""" return mac(self.algorithm, self.key, value) def setup(self, key=None, algorithm=None): """Configure an encrypted MAC key. The following arguments may be supplied: key: the MAC key to use algorithm: MAC algorithm None of the arguments are required, reasonable defaults will be chosen for missing arguments. """ if key: self.key = key if algorithm: self.algorithm = algorithm # default to HMAC-SHA1 if not self.algorithm: self.algorithm = 'hmac-sha1' # generate an HMAC key if not self.key: self.key = os.urandom(self.algorithm_key_length) python-pskc-1.3/pskc/parser.py0000644000000000000000000004173213300356475016415 0ustar rootroot00000000000000# parser.py - PSKC file parsing functions # coding: utf-8 # # Copyright (C) 2016-2018 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Module for parsing PSKC files.""" import array import base64 import copy from pskc.exceptions import ParseError from pskc.key import EncryptedIntegerValue, EncryptedValue from pskc.xml import ( find, findall, findbin, findint, findtext, findtime, getbool, getint, parse, remove_namespaces) def plain2int(value): """Convert a plain text value to an int.""" # try normal integer string parsing try: return int(value) except ValueError: pass # fall back to base64 decoding value = base64.b64decode(value) # try to handle value as ASCII representation if value.isdigit(): return int(value) # fall back to do big-endian decoding result = 0 for x in array.array('B', value): result = (result << 8) + x return result class PSKCParser(object): """Class to read various PSKC XML files into a PSKC structure.""" @classmethod def parse_file(cls, pskc, filename): """Parse the provided file and store data in the PSKC instance.""" try: tree = parse(filename) except Exception: raise ParseError('Error parsing XML') # save a clean copy of the tree for signature checking pskc.signature.tree = copy.deepcopy(tree) cls.parse_document(pskc, tree.getroot()) @classmethod def parse_document(cls, pskc, container): """Read information from the provided tree.""" remove_namespaces(container) if container.tag not in ('KeyContainer', 'SecretContainer'): raise ParseError('Missing KeyContainer') # the version of the PSKC schema pskc.version = container.get('Version') or container.get('version') if (container.tag == 'KeyContainer' and pskc.version and pskc.version not in ('1', '1.0')): raise ParseError('Unsupported version %r' % pskc.version) # unique identifier for the container pskc.id = ( container.get('Id') or container.get('ID') or container.get('id')) # handle EncryptionKey entries cls.parse_encryption(pskc.encryption, find( container, 'EncryptionKey', 'EncryptionMethod')) # handle MACMethod entries cls.parse_mac_method(pskc.mac, find( container, 'MACMethod', 'DigestMethod')) # fall back to MACAlgorithm mac_algorithm = findtext(container, 'MACAlgorithm') if mac_algorithm: pskc.mac.algorithm = mac_algorithm # handle KeyPackage entries for key_package in findall(container, 'KeyPackage', 'Device'): cls.parse_key_package(pskc.add_device(), key_package) # handle Signature entries cls.parse_signature(pskc.signature, find(container, 'Signature')) @classmethod def parse_encryption(cls, encryption, key_info): """Read encryption information from the XML tree.""" if key_info is None: return encryption.id = key_info.get('Id') encryption.algorithm = ( key_info.get('Algorithm') or key_info.get('algorithm') or encryption.algorithm) for name in findall(key_info, 'KeyName', 'DerivedKey/MasterKeyName', 'DerivedKey/CarriedKeyName'): encryption.key_names.append(findtext(name, '.')) encryption.iv = findbin(key_info, 'IV') or encryption.iv cls.parse_key_derivation(encryption.derivation, find( key_info, 'DerivedKey/KeyDerivationMethod')) encryption.derivation.pbkdf2_salt = ( findbin(key_info, 'PBESalt') or encryption.derivation.pbkdf2_salt) encryption.derivation.pbkdf2_iterations = ( findint(key_info, 'PBEIterationCount') or encryption.derivation.pbkdf2_iterations) algorithm = ( key_info.get('Algorithm') or key_info.get('algorithm') or '') if (algorithm.lower().startswith('pbe') and not encryption.derivation.algorithm): encryption.derivation.algorithm = 'pbkdf2' encryption.derivation.pbkdf2_key_length = ( encryption.derivation.pbkdf2_key_length or encryption.algorithm_key_lengths[0]) @classmethod def parse_key_derivation(cls, derivation, key_derivation): """Read derivation parameters from a element.""" if key_derivation is None: return derivation.algorithm = key_derivation.get('Algorithm') # PBKDF2 properties pbkdf2 = find(key_derivation, 'PBKDF2-params') if pbkdf2 is not None: # get used salt derivation.pbkdf2_salt = findbin(pbkdf2, 'Salt/Specified') # required number of iterations derivation.pbkdf2_iterations = findint(pbkdf2, 'IterationCount') # key length derivation.pbkdf2_key_length = findint(pbkdf2, 'KeyLength') # pseudorandom function used prf = find(pbkdf2, 'PRF') if prf is not None: derivation.pbkdf2_prf = prf.get('Algorithm') @classmethod def parse_mac_method(cls, mac, mac_method): """Read MAC information from the XML tree.""" if mac_method is None: return mac.algorithm = ( mac_method.get('Algorithm') or mac_method.get('algorithm')) mac_key = find(mac_method, 'MACKey') if mac_key is not None: algorithm, cipher_value = cls.parse_encrypted_value(mac_key) mac.key = EncryptedValue(cipher_value, None, algorithm) @classmethod def parse_key_package(cls, device, key_package): """Read key information from the provided tree.""" # find basic device information info = find(key_package, 'DeviceInfo', 'DeviceId') if info is not None: device.manufacturer = findtext(info, 'Manufacturer') device.serial = findtext(info, 'SerialNo') device.model = findtext(info, 'Model') device.issue_no = findtext(info, 'IssueNo') device.device_binding = findtext(info, 'DeviceBinding') device.start_date = findtime(info, 'StartDate') device.expiry_date = findtime(info, 'ExpiryDate', 'Expiry') device.device_userid = findtext(info, 'UserId') # find crypto module info device.crypto_module = findtext(key_package, 'CryptoModuleInfo/Id') # find keys for device for key_elm in findall(key_package, 'Key', 'Secret'): cls.parse_key(device.add_key(), key_elm) @classmethod def parse_key(cls, key, key_elm): """Read key information from the provided tree.""" # get key basic information key.id = ( key_elm.get('Id') or key_elm.get('KeyId') or key_elm.get('SecretId')) key.algorithm = ( key_elm.get('Algorithm') or key_elm.get('KeyAlgorithm') or key_elm.get('SecretAlgorithm')) # parse data section with possibly encrypted data data = find(key_elm, 'Data') if data is not None: cls.parse_data(key, 'secret', find(data, 'Secret')) cls.parse_data(key, 'counter', find(data, 'Counter')) cls.parse_data(key, 'time_offset', find(data, 'Time')) cls.parse_data(key, 'time_interval', find(data, 'TimeInterval')) cls.parse_data(key, 'time_drift', find(data, 'TimeDrift')) # parse legacy data elements with name attribute for data in findall(key_elm, 'Data'): name = data.get('Name') if name: cls.parse_data(key, dict( secret='secret', counter='counter', time='time_offset', time_interval='time_interval', ).get(name.lower()), data) # parse more basic key properties key.issuer = findtext(key_elm, 'Issuer') key.key_profile = findtext(key_elm, 'KeyProfileId') key.key_reference = findtext(key_elm, 'KeyReference') key.friendly_name = findtext(key_elm, 'FriendlyName') # TODO: support multi-language values of key.key_userid = findtext(key_elm, 'UserId') key.algorithm_suite = findtext( key_elm, 'AlgorithmParameters/Suite') # parse challenge format challenge_format = find( key_elm, 'AlgorithmParameters/ChallengeFormat', 'Usage/ChallengeFormat') if challenge_format is not None: key.challenge_encoding = ( challenge_format.get('Encoding') or challenge_format.get('Format') or challenge_format.get('format')) key.challenge_min_length = ( getint(challenge_format, 'Min') or getint(challenge_format, 'min')) key.challenge_max_length = ( getint(challenge_format, 'Max') or getint(challenge_format, 'max')) key.challenge_check = getbool( challenge_format, 'CheckDigits', getbool( challenge_format, 'CheckDigit')) # parse response format response_format = find( key_elm, 'AlgorithmParameters/ResponseFormat', 'Usage/ResponseFormat') if response_format is not None: key.response_encoding = ( response_format.get('Encoding') or response_format.get('Format') or response_format.get('format')) key.response_length = ( getint(response_format, 'Length') or getint(response_format, 'length')) key.response_check = getbool( response_format, 'CheckDigits', getbool( response_format, 'CheckDigit')) # parse key policy information cls.parse_policy(key.policy, find(key_elm, 'Policy')) # parse key usage information usage = find(key_elm, 'Usage') if usage is not None: for att in ('OTP', 'CR', 'Integrity', 'Encrypt', 'Unlock'): if getbool(usage, att): key.policy.key_usage.append(att) key.policy.start_date = ( findtime(key_elm, 'StartDate') or key.policy.start_date) key.policy.expiry_date = ( findtime(key_elm, 'ExpiryDate') or key.policy.expiry_date) @classmethod def parse_encrypted_value(cls, encrypted_value): """Read encryption value from element.""" algorithm = None cipher_value = findbin(encrypted_value, 'CipherData/CipherValue') encryption_method = find(encrypted_value, 'EncryptionMethod') if encryption_method is not None: algorithm = encryption_method.attrib.get('Algorithm') encryption_scheme = find( encrypted_value, 'EncryptionMethod/EncryptionScheme') if encryption_scheme is not None: algorithm = encryption_scheme.attrib.get('Algorithm') or algorithm return (algorithm, cipher_value) @classmethod def parse_data(cls, key, field, element): """Read information from the provided element. The element is expected to contain , and/or elements that contain information on the actual value. """ if element is None: return pskc = key.device.pskc plain_value = None cipher_value = None algorithm = None # get the plain2value function and encryption storage if field == 'secret': plain2value = base64.b64decode encrypted_value_cls = EncryptedValue else: plain2value = plain2int encrypted_value_cls = EncryptedIntegerValue # read plaintext value from plain_value = findtext(element, 'PlainValue') if plain_value is not None: plain_value = plain2value(plain_value) # read encrypted data from encrypted_value = find(element, 'EncryptedValue') if encrypted_value is not None: algorithm, cipher_value = cls.parse_encrypted_value( encrypted_value) # store the found algorithm in the pskc.encryption property if not pskc.encryption.algorithm and algorithm: pskc.encryption.algorithm = algorithm # read MAC information from mac_value = findbin(element, 'ValueMAC', 'ValueDigest') # read legacy elements (can be plain or encrypted) value = findtext(element, 'Value') if value is not None: if pskc.encryption.algorithm and mac_value: cipher_value = findbin(element, 'Value') else: plain_value = plain2value(value) # store the found information if plain_value is not None: setattr(key, field, plain_value) elif cipher_value: setattr(key, field, encrypted_value_cls(cipher_value, mac_value, algorithm)) @classmethod def parse_policy(cls, policy, policy_elm): """Read key policy information from the provided tree.""" if policy_elm is None: return policy.start_date = findtime(policy_elm, 'StartDate') policy.expiry_date = findtime(policy_elm, 'ExpiryDate') policy.number_of_transactions = findint( policy_elm, 'NumberOfTransactions') for key_usage in findall(policy_elm, 'KeyUsage'): policy.key_usage.append(findtext(key_usage, '.')) pin_policy_elm = find(policy_elm, 'PINPolicy') if pin_policy_elm is not None: policy.pin_key_id = pin_policy_elm.get('PINKeyId') policy.pin_usage = pin_policy_elm.get('PINUsageMode') policy.pin_max_failed_attempts = getint( pin_policy_elm, 'MaxFailedAttempts') policy.pin_min_length = getint(pin_policy_elm, 'MinLength') policy.pin_max_length = getint(pin_policy_elm, 'MaxLength') policy.pin_encoding = pin_policy_elm.get('PINEncoding') # check for child elements if list(pin_policy_elm): policy.unknown_policy_elements = True # check for unknown attributes known_attributes = set([ 'PINKeyId', 'PINUsageMode', 'MaxFailedAttempts', 'MinLength', 'MaxLength', 'PINEncoding']) if set(pin_policy_elm.keys()) - known_attributes: policy.unknown_policy_elements = True # check for other child elements known_children = set([ 'StartDate', 'ExpiryDate', 'NumberOfTransactions', 'KeyUsage', 'PINPolicy']) for child in policy_elm: if child.tag not in known_children: policy.unknown_policy_elements = True @classmethod def parse_signature(cls, signature, signature_elm): """Read signature information from the element.""" if signature_elm is None: return cm_elm = find(signature_elm, 'SignedInfo/CanonicalizationMethod') if cm_elm is not None: signature.canonicalization_method = cm_elm.attrib.get('Algorithm') sm_elm = find(signature_elm, 'SignedInfo/SignatureMethod') if sm_elm is not None: signature.algorithm = sm_elm.attrib.get('Algorithm') dm_elm = find(signature_elm, 'SignedInfo/Reference/DigestMethod') if dm_elm is not None: signature.digest_algorithm = dm_elm.attrib.get('Algorithm') issuer = find(signature_elm, 'KeyInfo/X509Data/X509IssuerSerial') if issuer is not None: signature.issuer = findtext(issuer, 'X509IssuerName') signature.serial = findtext(issuer, 'X509SerialNumber') certificate = findbin( signature_elm, 'KeyInfo/X509Data/X509Certificate') if certificate: certificate = base64.b64encode(certificate) signature.certificate = b'\n'.join( [b'-----BEGIN CERTIFICATE-----'] + [certificate[i:i + 64] for i in range(0, len(certificate), 64)] + [b'-----END CERTIFICATE-----']) python-pskc-1.3/pskc/policy.py0000644000000000000000000001363114667345771016433 0ustar rootroot00000000000000# policy.py - module for handling PSKC policy information # coding: utf-8 # # Copyright (C) 2014-2017 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Module that provides PSKC key policy information.""" import warnings def _make_aware(d): """Make tge specified datetime timezone aware.""" import dateutil.tz if not d.tzinfo: return d.replace(tzinfo=dateutil.tz.tzlocal()) return d class Policy(object): """Representation of a policy that describes key and pin usage. Instances of this class provide attributes that describe limits that are placed on key usage and requirements for key PIN protection. The policy provides the following attributes: start_date: the key MUST not be used before this datetime expiry_date: the key MUST not be used after this datetime number_of_transactions: maximum number of times the key may be used key_usage: list of valid usage scenarios for the key (e.g. OTP) pin_key_id: id of to the key that holds the PIN pin_key: reference to the key that holds the PIN pin: value of the PIN to use pin_usage: define how the PIN is used in relation to the key pin_max_failed_attempts: max. number of times a wrong PIN may be entered pin_min_length: minimum length of a PIN that may be set pin_max_length: maximum length of a PIN that may be set pin_encoding: DECIMAL/HEXADECIMAL/ALPHANUMERIC/BASE64/BINARY unknown_policy_elements: True if the policy contains unsupported rules If unknown_policy_elements is True the recipient MUST assume that key usage is not permitted. """ # Key is used for OTP generation. KEY_USE_OTP = 'OTP' # Key is used for Challenge/Response purposes. KEY_USE_CR = 'CR' # Key is used for data encryption purposes. KEY_USE_ENCRYPT = 'Encrypt' # For generating keyed message digests. KEY_USE_INTEGRITY = 'Integrity' # For checking keyed message digests. KEY_USE_VERIFY = 'Verify' # Unlocking device when wrong PIN has been entered too many times. KEY_USE_UNLOCK = 'Unlock' # Key is used for data decryption purposes. KEY_USE_DECRYPT = 'Decrypt' # The key is used for key wrap purposes. KEY_USE_KEYWRAP = 'KeyWrap' # The key is used for key unwrap purposes. KEY_USE_UNWRAP = 'Unwrap' # Use in a key derivation function to derive a new key. KEY_USE_DERIVE = 'Derive' # Generate a new key based on a random number and the previous value. KEY_USE_GENERATE = 'Generate' # The PIN is checked on the device before the key is used. PIN_USE_LOCAL = 'Local' # The response has the PIN prepanded and needs to be checked. PIN_USE_PREPEND = 'Prepend' # The response has the PIN appended and needs to be checked. PIN_USE_APPEND = 'Append' # The PIN is used in the algorithm computation. PIN_USE_ALGORITHMIC = 'Algorithmic' def __init__(self, key=None): """Create a new policy, optionally linked to the key and parsed.""" self.key = key self.start_date = None self.expiry_date = None self.number_of_transactions = None self.key_usage = [] self.pin_key_id = None self.pin_usage = None self.pin_max_failed_attempts = None self.pin_min_length = None self.pin_max_length = None self.pin_encoding = None self.unknown_policy_elements = False @property def pin_max_failed_attemtps(self): """Provide access to deprecated name.""" warnings.warn( 'The pin_max_failed_attemtps property has been renamed to ' 'pin_max_failed_attempts.', DeprecationWarning, stacklevel=2) return self.pin_max_failed_attempts @pin_max_failed_attemtps.setter def pin_max_failed_attemtps(self, value): warnings.warn( 'The pin_max_failed_attemtps property has been renamed to ' 'pin_max_failed_attempts.', DeprecationWarning, stacklevel=2) self.pin_max_failed_attempts = value def may_use(self, usage=None, now=None): """Check whether the key may be used for the provided purpose.""" import datetime import dateutil.tz if self.unknown_policy_elements: return False if usage is not None and self.key_usage: if usage not in self.key_usage: return False # check start_date and expiry_date if now is None: now = datetime.datetime.now(dateutil.tz.tzlocal()) if self.start_date: if _make_aware(self.start_date) > _make_aware(now): return False # not-yet usable key if self.expiry_date: if _make_aware(self.expiry_date) < _make_aware(now): return False # not-yet usable key return True @property def pin_key(self): """Provide the PSKC Key that holds the PIN (if any).""" if self.pin_key_id and self.key and self.key.device.pskc: for key in self.key.device.pskc.keys: if key.id == self.pin_key_id: return key @property def pin(self): """Provide the PIN value referenced by PINKeyId if any.""" key = self.pin_key if key: return str(key.secret.decode()) python-pskc-1.3/pskc/scripts/0000755000000000000000000000000014667356044016240 5ustar rootroot00000000000000python-pskc-1.3/pskc/scripts/__init__.py0000644000000000000000000000155613300356475020347 0ustar rootroot00000000000000# __init__.py - collection of command-line scripts # coding: utf-8 # # Copyright (C) 2018 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Collection of command-line scripts.""" python-pskc-1.3/pskc/scripts/csv2pskc.py0000644000000000000000000001245514667346437020364 0ustar rootroot00000000000000# csv2pskc.py - script to convert a CSV file to PSKC # # Copyright (C) 2018 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Script to convert a CSV file to PSKC.""" import argparse import base64 import csv import sys from binascii import a2b_hex import dateutil.parser import pskc from pskc.scripts.util import ( OutputFile, VersionAction, get_key, get_password) epilog = ''' supported columns: id, serial, secret, counter, time_offset, time_interval, interval, time_drift, issuer, manufacturer, response_length, algorithm And any other properties of pskc.key.Key instances. Report bugs to . '''.strip() # set up command line parser parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description='Convert a CSV file to PSKC.', epilog=epilog) parser.add_argument( 'input', nargs='?', metavar='FILE', help='the CSV file to read') parser.add_argument( '-V', '--version', action=VersionAction) parser.add_argument( '-o', '--output', metavar='FILE', help='write PSKC to file instead of stdout') parser.add_argument( '-c', '--columns', metavar='COL|COL:LABEL,..', help='list of columns or label to column mapping to import') parser.add_argument( '--skip-rows', metavar='N', type=int, default=1, help='the number of rows before rows with key information start') parser.add_argument( '-x', '--set', metavar='COL=VALUE', action='append', type=lambda x: x.split('=', 1), dest='extra_columns', help='add an extra value that is added to all key containers') parser.add_argument( '-p', '--password', '--passwd', metavar='PASS/FILE', help='password to use for encrypting the PSKC file)') parser.add_argument( '-s', '--secret', metavar='KEY/FILE', help='hex encoded encryption key or a file containing the binary key') encodings = { 'hex': a2b_hex, 'base32': base64.b32decode, 'base64': base64.b64decode, } parser.add_argument( '-e', '--secret-encoding', choices=sorted(encodings.keys()), help='encoding used for reading key material', default='hex') def from_column(key, value, args): """Convert a key value read from a CSV file in a format for PSKC.""" # decode encoded secret if key == 'secret': return encodings[args.secret_encoding](value) # convert dates to timestamps if key.endswith('_date'): return dateutil.parser.parse(value) return value def open_csvfile(inputfile): """Open the CSV file, trying to detect the dialect.""" # Guess dialect if possible and open the CSV file dialect = 'excel' try: # seek before read to skip sniffing on non-seekable files inputfile.seek(0) try: dialect = csv.Sniffer().sniff(inputfile.read(1024)) except Exception: # pragma: no cover (very hard to test in doctest) pass inputfile.seek(0) except IOError: # pragma: no cover (very hard to test in doctest) pass return csv.reader(inputfile, dialect) def main(): """Convert a CSV file to PSKC.""" # parse command-line arguments args = parser.parse_args() # open the CSV file csvfile = open_csvfile(open(args.input, 'r') if args.input else sys.stdin) # figure out the meaning of the columns columns = [] if args.skip_rows > 0: columns = [x.lower().replace(' ', '_') for x in next(csvfile)] for _ in range(args.skip_rows - 1): next(csvfile) if args.columns: if ':' in args.columns: # --columns is a list of mappings mapping = dict( (label.lower().replace(' ', '_'), key.lower()) for label, key in ( column.split(':') for column in args.columns.split(','))) columns = [mapping.get(column, column) for column in columns] else: # --columns is a list of columns columns = [x.lower() for x in args.columns.split(',')] # store rows in PSKC structure pskcfile = pskc.PSKC() for row in csvfile: data = dict(args.extra_columns or []) for column, value in zip(columns, row): for key in column.split('+'): if value and key not in ('', '-'): data[key] = from_column(key, value, args) pskcfile.add_key(**data) # encrypt the file if needed if args.secret: pskcfile.encryption.setup_preshared_key(key=get_key(args.secret)) elif args.password: pskcfile.encryption.setup_pbkdf2(get_password(args.password)) # write output PSKC file with OutputFile(args.output) as output: pskcfile.write(output) python-pskc-1.3/pskc/scripts/pskc2csv.py0000644000000000000000000000763413300356475020351 0ustar rootroot00000000000000# pskc2csv.py - script to convert a PSKC file to CSV # # Copyright (C) 2014-2018 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Script to convert a PSKC file to CSV.""" import argparse import base64 import csv import getpass import operator import sys from binascii import b2a_hex import pskc from pskc.scripts.util import ( OutputFile, VersionAction, get_key, get_password) epilog = ''' supported columns: id, serial, secret, counter, time_offset, time_interval, interval, time_drift, issuer, manufacturer, response_length, algorithm And any other properties of pskc.key.Key instances. Report bugs to . '''.strip() # set up command line parser parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description='Convert a PSKC file to CSV.', epilog=epilog) parser.add_argument( 'input', metavar='FILE', help='the PSKC file to read') parser.add_argument( '-V', '--version', action=VersionAction) parser.add_argument( '-o', '--output', metavar='FILE', help='write CSV to file instead of stdout') parser.add_argument( '-c', '--columns', metavar='COL:LABEL,COL,..', type=lambda x: [column.split(':', 1) for column in x.split(',')], help='list of columns with optional labels to export', default='serial,secret,algorithm,response_length,time_interval') parser.add_argument( '-p', '--password', '--passwd', metavar='PASS/FILE', help='password to use for decryption (or read from a file)') parser.add_argument( '-s', '--secret', metavar='KEY/FILE', help='hex encoded encryption key or file containing the binary key') encodings = { 'hex': b2a_hex, 'base32': base64.b32encode, 'base64': base64.b64encode, } parser.add_argument( '-e', '--secret-encoding', choices=sorted(encodings.keys()), help='encoding used for outputting key material', default='hex') def get_column(key, column, encoding): """Return a string value for the given column.""" value = operator.attrgetter(column)(key) if column == 'secret': # Python 3 compatible construct for outputting a string return str(encodings[encoding](value).decode()) return value def main(): """Convert a PSKC file to CSV.""" # parse command-line arguments args = parser.parse_args() # open and parse input PSKC file pskcfile = pskc.PSKC(args.input) if args.secret: pskcfile.encryption.key = get_key(args.secret) elif args.password: pskcfile.encryption.derive_key(get_password(args.password)) elif sys.stdin.isatty() and pskcfile.encryption.is_encrypted: # prompt for a password prompt = 'Password: ' if pskcfile.encryption.key_name: prompt = '%s: ' % pskcfile.encryption.key_name passwd = getpass.getpass(prompt) pskcfile.encryption.derive_key(passwd) # open output CSV file, write header and keys with OutputFile(args.output) as output: csvfile = csv.writer(output, quoting=csv.QUOTE_MINIMAL) csvfile.writerow([column[-1] for column in args.columns]) for key in pskcfile.keys: csvfile.writerow([ get_column(key, column[0], args.secret_encoding) for column in args.columns]) python-pskc-1.3/pskc/scripts/pskc2pskc.py0000644000000000000000000000624413532444643020514 0ustar rootroot00000000000000# pskc2pskc.py - script to convert a PSKC file to another PSKC file # # Copyright (C) 2018 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Script to convert a PSKC file to another PSKC file.""" import argparse import pskc from pskc.scripts.util import ( OutputFile, VersionAction, get_key, get_password) epilog = ''' supported columns: id, serial, secret, counter, time_offset, time_interval, interval, time_drift, issuer, manufacturer, response_length, algorithm And any other properties of pskc.key.Key instances. Report bugs to . '''.strip() # set up command line parser parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description='Convert a PSKC file to another PSKC file.', epilog=epilog) parser.add_argument( 'input', metavar='FILE', help='the PSKC file to read') parser.add_argument( '-V', '--version', action=VersionAction) parser.add_argument( '-o', '--output', metavar='FILE', help='write PSKC to file instead of stdout') parser.add_argument( '-p', '--password', '--passwd', metavar='PASS/FILE', help='password to use for decryption (or read from a file)') parser.add_argument( '-s', '--secret', metavar='KEY/FILE', help='hex encoded encryption key or a file containing the binary key') parser.add_argument( '--new-password', '--new-passwd', metavar='PASS/FILE', help='password to use for encryption (or read from a file)') parser.add_argument( '--new-secret', metavar='KEY/FILE', help='hex encoded encryption key or a file containing the binary key') def main(): """Convert a PSKC file to another PSKC file.""" # parse command-line arguments args = parser.parse_args() # open and parse input PSKC file pskcfile = pskc.PSKC(args.input) if args.secret: pskcfile.encryption.key = get_key(args.secret) pskcfile.encryption.remove_encryption() elif args.password: pskcfile.encryption.derive_key(get_password(args.password)) pskcfile.encryption.remove_encryption() # encrypt the output file if needed if args.new_secret: assert not pskcfile.encryption.is_encrypted pskcfile.encryption.setup_preshared_key(key=get_key(args.new_secret)) elif args.new_password: assert not pskcfile.encryption.is_encrypted pskcfile.encryption.setup_pbkdf2(get_password(args.new_password)) # write output PSKC file with OutputFile(args.output) as output: pskcfile.write(output) python-pskc-1.3/pskc/scripts/util.py0000644000000000000000000000533613300356475017565 0ustar rootroot00000000000000# util.py - utility functions for command-line scripts # # Copyright (C) 2014-2018 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Utility functions for command-line scripts.""" import argparse import os.path import sys from binascii import a2b_hex import pskc version_string = ''' %s (python-pskc) %s Written by Arthur de Jong. Copyright (C) 2014-2018 Arthur de Jong This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. '''.lstrip() class VersionAction(argparse.Action): """Define --version argparse action.""" def __init__(self, option_strings, dest, help='output version information and exit'): super(VersionAction, self).__init__( option_strings=option_strings, dest=argparse.SUPPRESS, default=argparse.SUPPRESS, nargs=0, help=help) def __call__(self, parser, namespace, values, option_string=None): """Output version information and exit.""" sys.stdout.write(version_string % (parser.prog, pskc.__version__)) parser.exit() def get_key(argument): """Get the key from a file or a hex-encoded string.""" if os.path.isfile(argument): with open(argument, 'rb') as keyfile: return keyfile.read() else: return a2b_hex(argument) def get_password(argument): """Get the password from a file or as a string.""" if os.path.isfile(argument): with open(argument, 'r') as passfile: return passfile.read().replace('\n', '') else: return argument class OutputFile(object): """Wrapper around output file to also fall back to stdout.""" def __init__(self, output): self.output = output def __enter__(self): self.file = open(self.output, 'w') if self.output else sys.stdout return self.file def __exit__(self, *args): if self.output: self.file.close() else: # we are using stdout self.file.flush() python-pskc-1.3/pskc/serialiser.py0000644000000000000000000002760214667346555017302 0ustar rootroot00000000000000# serialiser.py - PSKC file parsing functions # coding: utf-8 # # Copyright (C) 2016-2024 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Module for serialising PSKC files to XML.""" import base64 from pskc.key import EncryptedIntegerValue, EncryptedValue from pskc.xml import find, mk_elem, move_namespaces, reformat, tostring def my_b64encode(value): """Wrap around b64encode to handle types correctly.""" if not isinstance(value, type(b'')): value = value.encode() return base64.b64encode(value).decode() class PSKCSerialiser(object): """Class for serialising a PSKC structure to PSKC 1.0 XML.""" @classmethod def serialise_file(cls, pskc, output): """Write the PSKC structure to the specified output file.""" xml = tostring(cls.serialise_document(pskc)) try: output.write(xml) except TypeError: # fall back to writing as string output.write(xml.decode('utf-8')) @classmethod def serialise_document(cls, pskc): """Convert the PSKC structure to an element tree structure.""" container = mk_elem('pskc:KeyContainer', Version='1.0', Id=pskc.id) cls.serialise_encryption(pskc.encryption, container) cls.serialise_mac(pskc.mac, container) for device in pskc.devices: cls.serialise_key_package(device, container) return cls.serialise_signature(pskc.signature, container) @classmethod def serialise_encryption(cls, encryption, container): """Provide an XML element tree for the encryption information.""" if all(x is None for x in (encryption.id, encryption.key_name, encryption.key, encryption.derivation.algorithm)): return encryption_key = mk_elem(container, 'pskc:EncryptionKey', Id=encryption.id, empty=True) if encryption.derivation.algorithm: cls.serialise_key_derivation( encryption.derivation, encryption_key, encryption.key_names) else: for name in encryption.key_names: mk_elem(encryption_key, 'ds:KeyName', name) @classmethod def serialise_key_derivation(cls, derivation, encryption_key, key_names): """Provide an XML structure for the key derivation properties.""" derived_key = mk_elem(encryption_key, 'xenc11:DerivedKey', empty=True) key_derivation = mk_elem(derived_key, 'xenc11:KeyDerivationMethod', Algorithm=derivation.algorithm) if derivation.algorithm.endswith('#pbkdf2'): pbkdf2 = mk_elem(key_derivation, 'xenc11:PBKDF2-params', empty=True) if derivation.pbkdf2_salt: salt = mk_elem(pbkdf2, 'Salt', empty=True) mk_elem(salt, 'Specified', base64.b64encode(derivation.pbkdf2_salt).decode()) mk_elem(pbkdf2, 'IterationCount', derivation.pbkdf2_iterations) mk_elem(pbkdf2, 'KeyLength', derivation.pbkdf2_key_length) mk_elem(pbkdf2, 'PRF', derivation.pbkdf2_prf) # TODO: serialise ReferenceList/DataReference for name in key_names: mk_elem(derived_key, 'xenc11:MasterKeyName', name) @classmethod def serialise_mac(cls, mac, container): """Provide an XML structure for the encrypted MAC key.""" key_value = getattr(mac, '_key', None) or mac.pskc.encryption.key if not mac.algorithm and not key_value: return mac_method = mk_elem( container, 'pskc:MACMethod', Algorithm=mac.algorithm, empty=True) if not key_value: return # encrypt the mac key if needed if not hasattr(key_value, 'get_value'): key_value = EncryptedValue.create(mac.pskc, key_value) # construct encrypted MACKey algorithm = key_value.algorithm or mac.pskc.encryption.algorithm cipher_value = key_value.cipher_value if mac.pskc.encryption.iv: cipher_value = mac.pskc.encryption.iv + cipher_value mac_key = mk_elem(mac_method, 'pskc:MACKey', empty=True) mk_elem(mac_key, 'xenc:EncryptionMethod', Algorithm=algorithm) cipher_data = mk_elem(mac_key, 'xenc:CipherData', empty=True) mk_elem(cipher_data, 'xenc:CipherValue', base64.b64encode(cipher_value).decode()) @classmethod def serialise_key_package(cls, device, container): """Provide an XML structure for key package.""" key_package = mk_elem(container, 'pskc:KeyPackage', empty=True) if any(x is not None for x in (device.manufacturer, device.serial, device.model, device.issue_no, device.device_binding, device.start_date, device.expiry_date, device.device_userid)): device_info = mk_elem(key_package, 'pskc:DeviceInfo', empty=True) mk_elem(device_info, 'pskc:Manufacturer', device.manufacturer) mk_elem(device_info, 'pskc:SerialNo', device.serial) mk_elem(device_info, 'pskc:Model', device.model) mk_elem(device_info, 'pskc:IssueNo', device.issue_no) mk_elem(device_info, 'pskc:DeviceBinding', device.device_binding) mk_elem(device_info, 'pskc:StartDate', device.start_date) mk_elem(device_info, 'pskc:ExpiryDate', device.expiry_date) mk_elem(device_info, 'pskc:UserId', device.device_userid) if device.crypto_module is not None: crypto_module = mk_elem(key_package, 'pskc:CryptoModuleInfo', empty=True) mk_elem(crypto_module, 'pskc:Id', device.crypto_module) for key in device.keys: cls.serialise_key(key, key_package) @classmethod def serialise_key(cls, key, key_package): """Provide an XML structure for the key information.""" key_elm = mk_elem(key_package, 'pskc:Key', empty=True, Id=key.id, Algorithm=key.algorithm) mk_elem(key_elm, 'pskc:Issuer', key.issuer) if any((key.algorithm_suite, key.challenge_encoding, key.response_encoding, key.response_length)): parameters = mk_elem(key_elm, 'pskc:AlgorithmParameters', empty=True) mk_elem(parameters, 'pskc:Suite', key.algorithm_suite) mk_elem(parameters, 'pskc:ChallengeFormat', Encoding=key.challenge_encoding, Min=key.challenge_min_length, Max=key.challenge_max_length, CheckDigits=key.challenge_check) mk_elem(parameters, 'pskc:ResponseFormat', Encoding=key.response_encoding, Length=key.response_length, CheckDigits=key.response_check) mk_elem(key_elm, 'pskc:KeyProfileId', key.key_profile) mk_elem(key_elm, 'pskc:KeyReference', key.key_reference) mk_elem(key_elm, 'pskc:FriendlyName', key.friendly_name) cls.serialise_data( key, 'secret', key_elm, 'pskc:Secret') cls.serialise_data( key, 'counter', key_elm, 'pskc:Counter') cls.serialise_data( key, 'time_offset', key_elm, 'pskc:Time') cls.serialise_data( key, 'time_interval', key_elm, 'pskc:TimeInterval') cls.serialise_data( key, 'time_drift', key_elm, 'pskc:TimeDrift') mk_elem(key_elm, 'pskc:UserId', key.key_userid) cls.serialise_policy(key.policy, key_elm) @classmethod def serialise_data(cls, key, field, key_elm, tag): """Provide an XML structure for the key material.""" value = getattr(key, '_%s' % field, None) pskc = key.device.pskc # skip empty values if value in (None, ''): return # get the value2text and encryption storage if field == 'secret': value2text = my_b64encode encrypted_value_cls = EncryptedValue else: value2text = str encrypted_value_cls = EncryptedIntegerValue # find the data tag and create our tag under it data = find(key_elm, 'pskc:Data') if data is None: data = mk_elem(key_elm, 'pskc:Data', empty=True) element = mk_elem(data, tag, empty=True) # see if we should encrypt the value if field in pskc.encryption.fields and not hasattr( value, 'get_value'): value = encrypted_value_cls.create(pskc, value) # write out value if not hasattr(value, 'get_value'): # unencrypted value mk_elem(element, 'pskc:PlainValue', value2text(value)) else: # encrypted value algorithm = value.algorithm or pskc.encryption.algorithm cipher_value = value.cipher_value if pskc.encryption.iv: cipher_value = pskc.encryption.iv + cipher_value encrypted_value = mk_elem( element, 'pskc:EncryptedValue', empty=True) mk_elem(encrypted_value, 'xenc:EncryptionMethod', Algorithm=algorithm) cipher_data = mk_elem( encrypted_value, 'xenc:CipherData', empty=True) mk_elem(cipher_data, 'xenc:CipherValue', base64.b64encode(cipher_value).decode()) if value.mac_value: mk_elem(element, 'pskc:ValueMAC', base64.b64encode(value.mac_value).decode()) @classmethod def serialise_policy(cls, policy, key_elm): """Provide an XML structure with the key policy information.""" # check if any policy attribute is set if not policy.key_usage and all(x is None for x in ( policy.start_date, policy.expiry_date, policy.number_of_transactions, policy.pin_key_id, policy.pin_usage, policy.pin_max_failed_attempts, policy.pin_min_length, policy.pin_max_length, policy.pin_encoding)): return policy_elm = mk_elem(key_elm, 'pskc:Policy', empty=True) mk_elem(policy_elm, 'pskc:StartDate', policy.start_date) mk_elem(policy_elm, 'pskc:ExpiryDate', policy.expiry_date) mk_elem(policy_elm, 'pskc:PINPolicy', PINKeyId=policy.pin_key_id, PINUsageMode=policy.pin_usage, MaxFailedAttempts=policy.pin_max_failed_attempts, MinLength=policy.pin_min_length, MaxLength=policy.pin_max_length, PINEncoding=policy.pin_encoding) for usage in policy.key_usage: mk_elem(policy_elm, 'pskc:KeyUsage', usage) mk_elem(policy_elm, 'pskc:NumberOfTransactions', policy.number_of_transactions) @classmethod def serialise_signature(cls, signature, container): """Provide an XML structure for embedded XML signature.""" if not signature.key: return container # move the namespace to the root element and reformat before signing mk_elem(container, 'ds:Signature', Id='placeholder') container = move_namespaces(container) reformat(container) # sign the document return signature.sign_xml(container) python-pskc-1.3/pskc/signature.py0000644000000000000000000001235214342641776017126 0ustar rootroot00000000000000# signature.py - module for handling signed XML files # coding: utf-8 # # Copyright (C) 2017-2018 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Module for handling signed PSKC files. This module defines a Signature class that handles the signature checking, keys and certificates. """ def sign_x509(xml, key, certificate, algorithm=None, digest_algorithm=None, canonicalization_method=None): """Sign PSKC data using X.509 certificate and private key. xml: an XML document key: the private key in binary format certificate: the X.509 certificate """ import signxml algorithm = algorithm or 'rsa-sha256' digest_algorithm = digest_algorithm or 'sha256' canonicalization_method = ( canonicalization_method or getattr(signxml.XMLSignatureProcessor, 'default_c14n_algorithm', None) or 'http://www.w3.org/2006/12/xml-c14n11') return signxml.XMLSigner( method=signxml.methods.enveloped, signature_algorithm=algorithm.rsplit('#', 1)[-1].lower(), digest_algorithm=digest_algorithm.rsplit('#', 1)[-1].lower(), c14n_algorithm=canonicalization_method, ).sign(xml, key=key, cert=certificate) def verify_x509(tree, certificate=None, ca_pem_file=None): """Verify signature in PSKC data against a trusted X.509 certificate. If a certificate is supplied it is used to validate the signature, otherwise any embedded certificate is used and validated against a certificate in ca_pem_file if it specified and otherwise the operating system CA certificates. """ from signxml import XMLVerifier return XMLVerifier().verify( tree, x509_cert=certificate, ca_pem_file=ca_pem_file).signed_xml class Signature(object): """Class for handling signature checking of the PSKC file. Instances of this class provide the following properties: is_signed: boolean to indicate whether a signature is present algorithm: identifier of the signing algorithm used canonicalization_method: identifier of the XML canonicalization used digest_algorithm: algorithm used for creating the hash issuer: issuer of the certificate serial: serial number of the certificate key: key that will be used when creating a signed PSKC file certificate: the certificate that is embedded in the signature signed_pskc: a PSKC instance with the signed information """ def __init__(self, pskc): self.pskc = pskc self._algorithm = None self.canonicalization_method = None self.digest_algorithm = None self.issuer = None self.serial = None self.key = None self.certificate = None @property def is_signed(self): """Test whether the PSKC file contains a signature. This method does not check whether the signature is valid but only if one was present in the PSKC file. """ return bool( self.algorithm or self.canonicalization_method or self.digest_algorithm or self.issuer or self.certificate) @property def algorithm(self): """Provide the signing algorithm used.""" if self._algorithm: return self._algorithm @algorithm.setter def algorithm(self, value): from pskc.algorithms import normalise_algorithm self._algorithm = normalise_algorithm(value) @property def signed_pskc(self): """Provide the signed PSKC information.""" if not hasattr(self, '_signed_pskc'): self.verify() return self._signed_pskc def verify(self, certificate=None, ca_pem_file=None): """Check that the signature was made with the specified certificate. If no certificate is provided the signature is expected to contain a signature that is signed by the CA certificate (or the CA standard CA certificates when ca_pem_file is absent). """ from pskc import PSKC from pskc.parser import PSKCParser signed_xml = verify_x509(self.tree, certificate, ca_pem_file) pskc = PSKC() PSKCParser.parse_document(pskc, signed_xml) self._signed_pskc = pskc return True def sign(self, key, certificate=None): """Add an XML signature to the file.""" self.key = key self.certificate = certificate def sign_xml(self, xml): """Sign an XML document with the configured key and certificate.""" return sign_x509( xml, self.key, self.certificate, self.algorithm, self.digest_algorithm, self.canonicalization_method) python-pskc-1.3/pskc/xml.py0000644000000000000000000001703413604371042015711 0ustar rootroot00000000000000# xml.py - module for parsing and writing XML for PSKC files # coding: utf-8 # # Copyright (C) 2014-2020 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Module for parsing XML in PSKC files. This module provides some utility functions for parsing XML files. """ from __future__ import absolute_import import sys from collections import OrderedDict # try to find a usable ElementTree implementation try: # pragma: no cover (different implementations) from lxml.etree import parse as xml_parse, tostring as xml_tostring from lxml.etree import register_namespace, Element, SubElement try: from defusedxml.lxml import parse as xml_parse # noqa: F811 except ImportError: pass except ImportError: # pragma: no cover (different implementations) from xml.etree.ElementTree import ( parse as xml_parse, tostring as xml_tostring) from xml.etree.ElementTree import register_namespace, Element, SubElement try: from defusedxml.ElementTree import parse as xml_parse # noqa: F811 except ImportError: pass # the relevant XML namespaces for PSKC namespaces = dict( # the XML namespace URI for version 1.0 of PSKC pskc='urn:ietf:params:xml:ns:keyprov:pskc', # the XML Signature namespace ds='http://www.w3.org/2000/09/xmldsig#', # the XML Encryption namespace xenc='http://www.w3.org/2001/04/xmlenc#', # the XML Encryption version 1.1 namespace xenc11='http://www.w3.org/2009/xmlenc11#', # the PKCS #5 namespace pkcs5='http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#', ) def register_namespaces(): """Register the namespaces so the correct short names will be used.""" for ns, namespace in namespaces.items(): register_namespace(ns, namespace) register_namespaces() def parse(source): """Parse the provided file and return an element tree.""" return xml_parse(sys.stdin if source == '-' else source) def remove_namespaces(tree): """Remove namespaces from all elements in the tree.""" import re for elem in tree.iter(): if isinstance(elem.tag, ''.__class__): # pragma: no branch elem.tag = re.sub(r'^\{[^}]*\}', '', elem.tag) def findall(tree, *matches): """Find the child elements.""" for match in matches: for element in tree.findall(match, namespaces=namespaces): yield element def find(tree, *matches): """Find a child element that matches any of the patterns (or None).""" try: return next(findall(tree, *matches)) except StopIteration: pass def findtext(tree, *matches): """Get the text value of an element (or None).""" element = find(tree, *matches) if element is not None: return element.text.strip() def findint(tree, *matches): """Return an element value as an int (or None).""" value = findtext(tree, *matches) if value: return int(value) def findtime(tree, *matches): """Return an element value as a datetime (or None).""" value = findtext(tree, *matches) if value: import dateutil.parser return dateutil.parser.parse(value) def findbin(tree, *matches): """Return the binary element value base64 decoded.""" value = findtext(tree, *matches) if value: import base64 return base64.b64decode(value) def getint(tree, attribute): """Return an attribute value as an integer (or None).""" value = tree.get(attribute) if value: return int(value) def getbool(tree, attribute, default=None): """Return an attribute value as a boolean (or None).""" value = tree.get(attribute) if value: value = value.lower() if value in ('1', 'true'): return True elif value in ('0', 'false'): return False else: raise ValueError('invalid boolean value: %r' % value) return default def _format(value): import datetime if isinstance(value, datetime.datetime): value = value.isoformat() if value.endswith('+00:00'): value = value[:-6] + 'Z' return value elif value is True: return 'true' elif value is False: return 'false' return str(value) def mk_elem(parent, tag=None, text=None, empty=False, **kwargs): """Add element as a child of parent.""" # special-case the top-level element if tag is None: tag = parent parent = None empty = True # don't create empty elements if not empty and text is None and \ all(x is None for x in kwargs.values()): return # replace namespace identifier with URL if ':' in tag: ns, name = tag.split(':', 1) tag = '{%s}%s' % (namespaces[ns], name) if parent is None: element = Element(tag, OrderedDict()) else: element = SubElement(parent, tag, OrderedDict()) # set text of element if text is not None: element.text = _format(text) # set kwargs as attributes for k, v in kwargs.items(): if v is not None: element.set(k, _format(v)) return element def move_namespaces(element): """Move the namespace declarations to the toplevel element.""" if hasattr(element, 'nsmap'): # pragma: no cover (only on lxml) # get all used namespaces nsmap = {} for e in element.iter(): nsmap.update(e.nsmap) nsmap = OrderedDict(sorted(nsmap.items())) # replace toplevel element with all namespaces e = Element(element.tag, attrib=element.attrib, nsmap=nsmap) for a in element: e.append(a) element = e return element def reformat(element, indent=''): """Reformat the XML tree to have nice wrapping and indenting.""" tag = element.tag.split('}')[-1] # re-order attributes by alphabet attrib = sorted(element.attrib.items()) element.attrib.clear() element.attrib.update(attrib) if len(element) == 0: # clean up inner text if element.text: element.text = element.text.strip() if tag in ('X509Certificate', 'SignatureValue'): element.text = ''.join(x for x in element.text if not x.isspace()) elif tag != 'SignedInfo': # indent children element.text = '\n ' + indent childred = list(element) for child in childred: reformat(child, indent + ' ') childred[-1].tail = '\n' + indent element.tail = '\n' + indent def tostring(element): """Return a serialised XML document for the element tree.""" element = move_namespaces(element) reformat(element) xml = xml_tostring(element, encoding='UTF-8') xml_decl = b"\n" if xml.startswith(xml_decl): # pragma: no cover (only a few cases) xml = xml[len(xml_decl):] return ( b'\n' + xml.replace(b' />', b'/>').strip() + b'\n') python-pskc-1.3/pskc2csv.py0000755000000000000000000000176614307703306015722 0ustar rootroot00000000000000#!/usr/bin/env python # coding: utf-8 # pskc2csv.py - script to convert a PSKC file to CSV # # Copyright (C) 2014-2018 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Script to convert a PSKC file to CSV.""" from pskc.scripts import pskc2csv if __name__ == '__main__': # pragma: no cover pskc2csv.main() python-pskc-1.3/pskc2pskc.py0000755000000000000000000000202014307703306016047 0ustar rootroot00000000000000#!/usr/bin/env python # coding: utf-8 # pskc2pskc.py - script to convert a PSKC file to another PSKC file # # Copyright (C) 2018 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """Script to convert a PSKC file to another PSKC file.""" from pskc.scripts import pskc2pskc if __name__ == '__main__': # pragma: no cover pskc2pskc.main() python-pskc-1.3/python_pskc.egg-info/0000755000000000000000000000000014667356044017644 5ustar rootroot00000000000000python-pskc-1.3/python_pskc.egg-info/PKG-INFO0000644000000000000000000001100214667356044020733 0ustar rootroot00000000000000Metadata-Version: 2.1 Name: python-pskc Version: 1.3 Summary: Python module for handling PSKC files Home-page: https://arthurdejong.org/python-pskc/ Author: Arthur de Jong Author-email: arthur@arthurdejong.org License: LGPL Description: Python module for handling PSKC files ===================================== A Python module to handle Portable Symmetric Key Container (PSKC) files as defined in `RFC 6030 `_. PSKC files are used to transport and provision symmetric keys and key meta data (seed files) to different types of crypto modules, commonly one-time password systems or other authentication devices. This module can be used to extract keys from PSKC files for use in an OTP authentication system. The module can also be used for authoring PSKC files. This module should be able to handle most common PSKC files. https://arthurdejong.org/python-pskc/ API --- The module provides a straightforward API that is mostly geared towards parsing existing PSKC files. Extracting key material from encrypted PSKC files is as simple as: >>> from pskc import PSKC >>> pskc = PSKC('tests/rfc6030/figure7.pskcxml') >>> pskc.encryption.derive_key('qwerty') >>> for key in pskc.keys: ... print('%s %s' % (key.serial, str(key.secret.decode()))) 987654321 12345678901234567890 Writing am encrypted PSKC file is as simple as: >>> pskc = PSKC() >>> key = pskc.add_key( ... id='456', secret='987654321', manufacturer='Manufacturer', ... algorithm = 'urn:ietf:params:xml:ns:keyprov:pskc:hotp') >>> pskc.encryption.setup_pbkdf2('passphrase') >>> pskc.write('output.pskcxml') The key object has a number of properties. See the pskc.key.Key documentation for details. Security considerations ----------------------- This code handles private key material and is written in Python. No precautions have been taken to lock pages in memory to prevent swapping. Also no attempt is currently made to securely dispose of memory that may have held private key material. Copyright --------- Copyright (C) 2014-2024 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Keywords: PSKC,RFC 6030,key container Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+) Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Security :: Cryptography Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: System :: Systems Administration :: Authentication/Directory Classifier: Topic :: Text Processing :: Markup :: XML Provides-Extra: defuse Provides-Extra: lxml Provides-Extra: signature python-pskc-1.3/python_pskc.egg-info/SOURCES.txt0000644000000000000000000001164214667356044021534 0ustar rootroot00000000000000.gitignore COPYING ChangeLog MANIFEST.in NEWS README csv2pskc.py pskc2csv.py pskc2pskc.py setup.cfg setup.py tox.ini .github/workflows/test.yml docs/changes.rst docs/conf.py docs/csv2pskc.rst docs/encryption.rst docs/exceptions.rst docs/index.rst docs/mac.rst docs/policy.rst docs/pskc2csv.rst docs/pskc2pskc.rst docs/scripts.rst docs/signatures.rst docs/usage.rst docs/_templates/autosummary/module.rst pskc/__init__.py pskc/algorithms.py pskc/device.py pskc/encryption.py pskc/exceptions.py pskc/key.py pskc/mac.py pskc/parser.py pskc/policy.py pskc/serialiser.py pskc/signature.py pskc/xml.py pskc/crypto/__init__.py pskc/crypto/aeskw.py pskc/crypto/tripledeskw.py pskc/scripts/__init__.py pskc/scripts/csv2pskc.py pskc/scripts/pskc2csv.py pskc/scripts/pskc2pskc.py pskc/scripts/util.py python_pskc.egg-info/PKG-INFO python_pskc.egg-info/SOURCES.txt python_pskc.egg-info/dependency_links.txt python_pskc.egg-info/entry_points.txt python_pskc.egg-info/requires.txt python_pskc.egg-info/top_level.txt tests/test_actividentity.doctest tests/test_aeskw.doctest tests/test_csv2pskc.doctest tests/test_draft_hoyer_keyprov_portable_symmetric_key_container.doctest tests/test_draft_ietf_keyprov_pskc_02.doctest tests/test_draft_keyprov.doctest tests/test_encryption.doctest tests/test_feitian.doctest tests/test_invalid.doctest tests/test_mac.doctest tests/test_misc.doctest tests/test_multiotp.doctest tests/test_nagraid.doctest tests/test_pskc2csv.doctest tests/test_pskc2pskc.doctest tests/test_rfc6030.doctest tests/test_signature.doctest tests/test_tripledeskw.doctest tests/test_write.doctest tests/test_yubico.doctest tests/actividentity/test.pskcxml tests/certificate/README tests/certificate/ca-certificate.pem tests/certificate/ca-key.pem tests/certificate/certificate.pem tests/certificate/key.pem tests/certificate/request.pem tests/certificate/ss-certificate.pem tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/non-encrypted.pskcxml tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/password-encrypted.pskcxml tests/draft-hoyer-keyprov-portable-symmetric-key-container-01/non-encrypted.pskcxml tests/draft-hoyer-keyprov-portable-symmetric-key-container-01/password-encrypted.pskcxml tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/actividentity-3des.pskcxml tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/ocra.pskcxml tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/securid-aes-counter.pskcxml tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/totp.pskcxml tests/draft-ietf-keyprov-pskc-02/figure2.pskcxml tests/draft-ietf-keyprov-pskc-02/figure3.pskcxml tests/draft-ietf-keyprov-pskc-02/figure4.pskcxml tests/draft-ietf-keyprov-pskc-02/figure5.pskcxml tests/draft-ietf-keyprov-pskc-02/figure6.pskcxml tests/draft-ietf-keyprov-pskc-02/figure7.pskcxml tests/draft-ietf-keyprov-pskc-02/figure8.pskcxml tests/draft-ietf-keyprov-pskc-02/figure9.pskcxml tests/encryption/aes128-cbc-noiv.pskcxml tests/encryption/aes128-cbc.pskcxml tests/encryption/aes192-cbc.pskcxml tests/encryption/aes256-cbc.pskcxml tests/encryption/camellia128-cbc.pskcxml tests/encryption/camellia192-cbc.pskcxml tests/encryption/camellia256-cbc.pskcxml tests/encryption/kw-aes128.pskcxml tests/encryption/kw-aes192.pskcxml tests/encryption/kw-aes256.pskcxml tests/encryption/kw-camellia128.pskcxml tests/encryption/kw-camellia192.pskcxml tests/encryption/kw-camellia256.pskcxml tests/encryption/kw-tripledes.pskcxml tests/encryption/mac-over-plaintext.pskcxml tests/encryption/no-mac-key.pskcxml tests/encryption/tripledes-cbc.pskcxml tests/feitian/20120919-test001-4282.xml tests/feitian/file1.pskcxml tests/invalid/incomplete-derivation.pskcxml tests/invalid/mac-algorithm.pskcxml tests/invalid/mac-value.pskcxml tests/invalid/missing-encryption-method.pskcxml tests/invalid/missing-encryption.pskcxml tests/invalid/no-mac-method.pskcxml tests/invalid/not-boolean.pskcxml tests/invalid/not-integer.pskcxml tests/invalid/not-integer2.pskcxml tests/invalid/notxml.pskcxml tests/invalid/unknown-encryption.pskcxml tests/invalid/wrongelement.pskcxml tests/invalid/wrongversion.pskcxml tests/misc/SampleFullyQualifiedNS.xml tests/misc/checkdigits.pskcxml tests/misc/integers.pskcxml tests/misc/odd-namespace.pskcxml tests/misc/partialxml.pskcxml tests/misc/policy.pskcxml tests/multiotp/pskc-hotp-aes.txt tests/multiotp/pskc-hotp-pbe.txt tests/multiotp/pskc-totp-aes.txt tests/multiotp/pskc-totp-pbe.txt tests/multiotp/tokens_hotp_aes.pskc tests/multiotp/tokens_hotp_pbe.pskc tests/multiotp/tokens_ocra_aes.pskc tests/multiotp/tokens_ocra_pbe.pskc tests/multiotp/tokens_totp_aes.pskc tests/multiotp/tokens_totp_pbe.pskc tests/nagraid/file1.pskcxml tests/rfc6030/figure10.pskcxml tests/rfc6030/figure2.pskcxml tests/rfc6030/figure3.pskcxml tests/rfc6030/figure4.pskcxml tests/rfc6030/figure5.pskcxml tests/rfc6030/figure6.pskcxml tests/rfc6030/figure7.pskcxml tests/rfc6030/figure8.pskcxml tests/rfc6030/figure9.pskcxml tests/yubico/example1.pskcxml tests/yubico/example2.pskcxml tests/yubico/example3.pskcxmlpython-pskc-1.3/python_pskc.egg-info/dependency_links.txt0000644000000000000000000000000114667356044023712 0ustar rootroot00000000000000 python-pskc-1.3/python_pskc.egg-info/entry_points.txt0000644000000000000000000000020714667356044023141 0ustar rootroot00000000000000[console_scripts] csv2pskc = pskc.scripts.csv2pskc:main pskc2csv = pskc.scripts.pskc2csv:main pskc2pskc = pskc.scripts.pskc2pskc:main python-pskc-1.3/python_pskc.egg-info/requires.txt0000644000000000000000000000012414667356044022241 0ustar rootroot00000000000000cryptography python-dateutil [defuse] defusedxml [lxml] lxml [signature] signxml python-pskc-1.3/python_pskc.egg-info/top_level.txt0000644000000000000000000000000514667356044022371 0ustar rootroot00000000000000pskc python-pskc-1.3/setup.cfg0000644000000000000000000000160514667356044015434 0ustar rootroot00000000000000[metadata] license_file = COPYING [sdist] owner = root group = root [bdist_wheel] universal = 1 [tool:pytest] addopts = --doctest-modules --doctest-glob="*.doctest" pskc tests --cov=pskc --cov-report=term-missing:skip-covered --cov-report=html doctest_optionflags = IGNORE_EXCEPTION_DETAIL [coverage:run] branch = true [coverage:report] show_missing = true [coverage:html] directory = coverage [build_sphinx] all_files = 1 builder = html man [flake8] ignore = B902 # catching Exception is fine D105 # Missing docstring in magic method D107 # Missing docstring in __init__ Q001 # Use of ''' multiline strings W504 # we put the binary operator on the preceding line max-complexity = 14 max-line-length = 120 extend-exclude = .github .pytest_cache build [codespell] skip = jquery*,*.egg-info,ChangeLog,./.git,./.tox,./build,./coverage,./std [egg_info] tag_build = tag_date = 0 python-pskc-1.3/setup.py0000755000000000000000000000606114667346555015337 0ustar rootroot00000000000000#!/usr/bin/env python # setup.py - python-pskc installation script # # Copyright (C) 2014-2024 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA """python-pskc installation script.""" import os import sys import pskc from setuptools import find_packages, setup # fix permissions for sdist if 'sdist' in sys.argv: os.system('chmod -R a+rX .') os.umask(int('022', 8)) base_dir = os.path.dirname(__file__) with open(os.path.join(base_dir, 'README'), 'r') as fp: long_description = fp.read() setup( name='python-pskc', version=pskc.__version__, description='Python module for handling PSKC files', long_description=long_description, author='Arthur de Jong', author_email='arthur@arthurdejong.org', keywords=['PSKC', 'RFC 6030', 'key container'], url='https://arthurdejong.org/python-pskc/', license='LGPL', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Intended Audience :: Information Technology', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Security :: Cryptography', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: System :: Systems Administration :: Authentication/Directory', 'Topic :: Text Processing :: Markup :: XML', ], packages=find_packages(), install_requires=['cryptography', 'python-dateutil'], extras_require={ 'lxml': ['lxml'], 'defuse': ['defusedxml'], 'signature': ['signxml'], }, entry_points={ 'console_scripts': [ 'csv2pskc = pskc.scripts.csv2pskc:main', 'pskc2csv = pskc.scripts.pskc2csv:main', 'pskc2pskc = pskc.scripts.pskc2pskc:main', ], }, ) python-pskc-1.3/tests/0000755000000000000000000000000014667356044014753 5ustar rootroot00000000000000python-pskc-1.3/tests/actividentity/0000755000000000000000000000000014667356044017633 5ustar rootroot00000000000000python-pskc-1.3/tests/actividentity/test.pskcxml0000644000000000000000000000251013117007605022174 0ustar rootroot00000000000000 Xus0lsc+rJLi0nc/ANE0Xg== ActivIdentity 0950380269 ActivIdentity 1HBJThmzYDCU7YCrML6OZKMsRQriSvZpoqeMfBui9dI= SlinEB9YUzcR04MUZDF5dBLtK1c= AAAAADHwRgM= python-pskc-1.3/tests/certificate/0000755000000000000000000000000014667356044017235 5ustar rootroot00000000000000python-pskc-1.3/tests/certificate/README0000644000000000000000000000200514667346437020117 0ustar rootroot00000000000000This directory contains keys and certificates that are used in the python-pskc test suite for checking XML signatures. The CA key and certificate were generated with: openssl req \ -x509 -newkey rsa:2048 -nodes \ -keyout ca-key.pem -out ca-certificate.pem \ -days 3650 -subj '/C=NL/O=python-pskc/CN=Test CA' \ -addext 'keyUsage = critical, cRLSign, digitalSignature, keyCertSign' The key used for signing the PSKC files and corresponding self-signed certificate were generated with: openssl req \ -x509 -newkey rsa:2048 -nodes \ -keyout key.pem -out ss-certificate.pem \ -days 3650 -subj '/C=NL/O=python-pskc/CN=Test signing' The certificate signed by the CA key was generated with: openssl req \ -new \ -key key.pem -out request.pem \ -subj '/C=NL/O=python-pskc/CN=Test signing' \ -addext 'subjectAltName = email:test-signing@example.com' openssl x509 \ -req \ -in request.pem -CA ca-certificate.pem -CAkey ca-key.pem -out certificate.pem \ -days 3650 -set_serial 42 \ -copy_extensions copy python-pskc-1.3/tests/certificate/ca-certificate.pem0000644000000000000000000000231014667346437022604 0ustar rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDWzCCAkOgAwIBAgIULJ5QSDFKc/3MUsG9dyVDRsqmJtQwDQYJKoZIhvcNAQEL BQAwNTELMAkGA1UEBhMCTkwxFDASBgNVBAoMC3B5dGhvbi1wc2tjMRAwDgYDVQQD DAdUZXN0IENBMB4XDTI0MDgyNzIxMjgyNloXDTM0MDgyNTIxMjgyNlowNTELMAkG A1UEBhMCTkwxFDASBgNVBAoMC3B5dGhvbi1wc2tjMRAwDgYDVQQDDAdUZXN0IENB MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn6AkAhsmtf16TZEPykMA Ytkfztph8e+MK1LGzKbY2hBv4AUQwFeQO9n/otTM0w4EL/S9pUohuugNLMQlZ38N xa5ZAZxmkJEgNF/lG9xHqW65/c/Jqze+QFnyvGSUKB2rZnxQU9mYqiD6CV/VEWbJ XW5Ko5oSz+ojEa4AOG5HaDobRsbVX9GaoUs0KdkF2NIy+fmQowoLNwsjTjWACb7y YDKwaIu/KaqXnSC44TYWfkqYuizr3dBw1ZAgNoR8KwYDe/lXwPnNNTRFrz3R3wD5 wLg+C1mLJMn/ZdWUjk+pPFDK4nF1jOH1Yp++1CUSJHjGCchYlpVqoO53EXQMp1zl IQIDAQABo2MwYTAdBgNVHQ4EFgQUE/dIsq12uv9UY9DYh57Dnt9GPAcwHwYDVR0j BBgwFoAUE/dIsq12uv9UY9DYh57Dnt9GPAcwDwYDVR0TAQH/BAUwAwEB/zAOBgNV HQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBADmC9GD0bGk4XSsjqktfzVJ1 k3QrWEznxTfY6ZxwDS9NKd9kzngVuIqeCd7qyHn6DUV9RYpa6fMhdZi00RwnL0FF wbRIM2MT2r1N/Nyx9tAk0waQ5McmqCvtfuFI5eXw2dcrj1GR9iipcUfrGN0SrQN5 QyMkbdfWszHl7Vp4IuWySKw2cNuCo8QnjWtq/WenVZRSpRvxvNDtQerpZPtHoSLh pohn/Z6zVOWyf+uCajytJBgl9l+RE1foLRWZl+GoBRoSSWrPUqn2EGQwrgqiofpx DSrPxJbvJY18qlgbAw4IzYfr15zVQyeTZCVUttWSE/yP2m53garunWR1JGQ9u4g= -----END CERTIFICATE----- python-pskc-1.3/tests/certificate/ca-key.pem0000644000000000000000000000325014667346437021116 0ustar rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCfoCQCGya1/XpN kQ/KQwBi2R/O2mHx74wrUsbMptjaEG/gBRDAV5A72f+i1MzTDgQv9L2lSiG66A0s xCVnfw3FrlkBnGaQkSA0X+Ub3Eepbrn9z8mrN75AWfK8ZJQoHatmfFBT2ZiqIPoJ X9URZsldbkqjmhLP6iMRrgA4bkdoOhtGxtVf0ZqhSzQp2QXY0jL5+ZCjCgs3CyNO NYAJvvJgMrBoi78pqpedILjhNhZ+Spi6LOvd0HDVkCA2hHwrBgN7+VfA+c01NEWv PdHfAPnAuD4LWYskyf9l1ZSOT6k8UMricXWM4fVin77UJRIkeMYJyFiWlWqg7ncR dAynXOUhAgMBAAECggEAIEyaHKb7zUVuEvXwWP/3uwiuK//6Y6cSGM4WFeEI7g73 ZEACJLlTIRk6ZetjSq2CYWXHXgPDEXvU9qL14pxN4BsoPXA1PLlw58gjpuCKy5jx H4+FAhU0dPb+a+p6JCfkv8cVxfG4t2wWzADNL0k6I6KmjbYmalW5JOGtf72oy0js 3oW9wuIxjB4Ok/nFFMN9ZnNAy3Iqxrgl7yzxg6ljA55cDTkIVlW6IaYEMaRvKi0E TOYJ9+DulnXRGF2SsrDY9dm2lUiNiDHTS9fiRcFgQtJS6u8BPhHXosnTlNcHeuth PBaBsQQH7kV6SBiDRutHMRSuCqfwJqLPXeWKXTxxBQKBgQDLosHJq4fJcer3oMb5 fLqO8gly0FSSc2kE1K3X2ebA5ZARjncP87772/sGWRIyiCMIZ7sZHLxxZBNNTkxi MN8xEeYZ8EcAXZWYXVtnOFag2EbmFrkUyMgjrldnHXjhc6vZ63MHdRXlNwmMO5NT JHFgV3QkjyhIKBlO283rBWBgowKBgQDIrDZiFLJwwH9IyxYBMPlPUrFLT5KVdAWk QA/lfK3iMcWgWK348pWO6NquqHuYd+D5+nxYzs5CmM2zL5hahu41OMb1yhMEu/zk 2L5pOr1N4ZQKCWZyv3uebm9TNQ9MaIJr42eRXzXua3VKrV1OqFYK5kfJth18sPPG OXQqa7SLawKBgQC59X+ScxolU+qHCEOsQcL8FvuZ5bXVp58X0kCPBWY8cWcHIVqJ LsYRqemq+lwr2TZkzpWFcUCFLCaUShRm9spbPUUsGsg8BTo/UcHOmRxC9j6Ij+ek m1ZpfyRB/iANglxFPUZmYP4FvCTz8tOu8XC30iU13aWjfzrdUMzS+q3wkQKBgCSN AYbR4EKbPVPj8ckbw31IQZTHFJr2PATTylFxaTaW/DkdjeqXPNh/3z0K4SA/TZIa psWCPZ1xuZ3WoHh0tTSqd8rTGK23E/QW6m++TDbATLebSLbD299anBpawsOXEZJX JEkdP3SCzCVi+GjYTnvDv/WknzBUBsLnOhpz7bmrAoGBALLOebkWdxCUG9jj+l8l LzulZE0C7v6mVeWykAanhANP4HhbmQDFX0s8T1hkeut11ncg0E5MEMeCeYUP6DrX p5t/TmqfqasaUsuKcLHZyNr/8EzLZPQ5bJIbY/QwpngGOQthGirSpYXFGhZpFVGM tMfBc7ulh5NVxuQWCEB27/pS -----END PRIVATE KEY----- python-pskc-1.3/tests/certificate/certificate.pem0000644000000000000000000000227414667346437022234 0ustar rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDUTCCAjmgAwIBAgIBKjANBgkqhkiG9w0BAQsFADA1MQswCQYDVQQGEwJOTDEU MBIGA1UECgwLcHl0aG9uLXBza2MxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjQwODI3 MjEyODM1WhcNMzQwODI1MjEyODM1WjA6MQswCQYDVQQGEwJOTDEUMBIGA1UECgwL cHl0aG9uLXBza2MxFTATBgNVBAMMDFRlc3Qgc2lnbmluZzCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAMTn3YiNxlR1mxUJyGhU65yvvzsKhl/4HpDmiVQo UGq7daxYLddLx9bW/OSUQmIZS9NiebcmZGJF0B1+ru3Au962+Cso69F4+mPN+xKo fqyQpl9gzgo5IXvFzvgjlVQYuSGjf6B5J+NoQbeuR/latqIrjT7K1QtDSYZr4WSN 0BVI1JpEmQ3ucmIgwt9ZtC1mZg8ApznDs/yf8dfN6Y5jFYjCaRt+Qzzv/Uqx3rgK /tl8Kr1fJXGWaDI0afPrja7syVCkTs1VhMaZt5nXzEmX17W4PzBz3M6ElBrO06S2 B++DDe2Hjip0wJ+qCUVmUQuDCdC9o35Oi6cf0RIy8gZReaECAwEAAaNnMGUwIwYD VR0RBBwwGoEYdGVzdC1zaWduaW5nQGV4YW1wbGUuY29tMB0GA1UdDgQWBBQaU4Kp x4G2yysRZ4du/WRn7/aBFTAfBgNVHSMEGDAWgBQT90iyrXa6/1Rj0NiHnsOe30Y8 BzANBgkqhkiG9w0BAQsFAAOCAQEAJzt19KlSPCP2RH5wMI7OiVf+Zbt9inC1pCPb vRebgz2GuDRZu+WMlXQ9WXFLwZze+j7VVdx9k84MKfasLmkarUt6xc5fhxIzopMs Of2swbIjxKssdO9GNIhjuVnC2d3ltwPqBb/Y+mpuxRwQ2zoahS1bFxTxTnhXLxQx qEzWeJ66dplYdKnYsCsnB7yDv1A7KPQTy/zHzFzD9DBk9WCluyLJQ6DASjQlKr8M Uu42tVkfhTaBJTBfEh+HlJApSJP94xE5uY5iE2cLAlEPJwZk4BCQYM8B5JR4PpHq 0L4dJYLcE5Id0f9xdO0D9L5/kmO4RipBXImdoNZwDoeqRx8aEw== -----END CERTIFICATE----- python-pskc-1.3/tests/certificate/key.pem0000644000000000000000000000325014667346437020535 0ustar rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDE592IjcZUdZsV CchoVOucr787CoZf+B6Q5olUKFBqu3WsWC3XS8fW1vzklEJiGUvTYnm3JmRiRdAd fq7twLvetvgrKOvRePpjzfsSqH6skKZfYM4KOSF7xc74I5VUGLkho3+geSfjaEG3 rkf5WraiK40+ytULQ0mGa+FkjdAVSNSaRJkN7nJiIMLfWbQtZmYPAKc5w7P8n/HX zemOYxWIwmkbfkM87/1Ksd64Cv7ZfCq9XyVxlmgyNGnz642u7MlQpE7NVYTGmbeZ 18xJl9e1uD8wc9zOhJQaztOktgfvgw3th44qdMCfqglFZlELgwnQvaN+TounH9ES MvIGUXmhAgMBAAECggEABS+/Uda8K+LaJiR3Eypzd1HYxWcaUYJRWcs8wEr8jUcp SxQahwJ/vcy1Zhbypa9ZZDTClHyuJbmFiRVaabhLKotiv45JIeAgJktZau0lwwlf XIFKBBJrq50y/a3AQ/IPxE9QcAto7dBD9ODiE/Mb/usLQiVpFa4czG3ghrvGiJzx 3eEKZNVvHhI4Q+j9+4rZGtgl9+p6GOVAiHDePQAzOd3WtyGErZehhBXc0ev+hdl7 pBttOnE0THAdrOCYI+iXb7XhDKh5xDdoGl1DLOM2t+wqESnryzn4JnWKkJdwHPBo D1MFJVjh12zNhD1GMuu+Gtc9/rLrE9YRgB44h4ffMQKBgQD2NBkvoQg3jtPDqoxj 06uwlft6e8mTMtPB9so9oJC9extyPBz7jUXRphKqucdBP7zxcr63Kw/ckRUxIOYx eIA4Hd6/vnzUjVBmf+oqiMd4o3kLoipCFNVaY07oybLclkJJmEk14s7zaqRkTfRJ oZgTSJf6HoB97uko5AexG6P5GQKBgQDMvZrtw2H98rohtBP09N/fcx/4a5r+JHeh aOhIsyRIP+ptjYWROcu+dqFeEVk3CwJLCXCEiijb/GmKKryLCZEI1I40erFTi8Bt WViDEfyV6+RexfAv0UMPY2LY7cWO4AQmb2gFZpnMa4mQfsL40IMKBUvo7ca++PYP Z8mAy8WtyQKBgQCPizyL+nVR/yIUWKFVqpnVcPyYIwgZnG6+DedIGw+IifgsmEPF nk5wt0T26S31KvY8R8xN+vtae/RBGo9blIgdL70pSiR5wJJ50GZ0uh6ihZ0TXU8K DB+AfZoKO17fIXQweGtm0YAbh7elC5Y8P8zsoUyKdbIwulSSlLCvYBAwMQKBgGHt 8xP014MzPYpvavhIxGhJ1anqNkmHQd1no2tanGkegVADWDbdkTG55ndrDuRAFMm1 aydKOj723bhjtqk+WywBFz/My9KtTs4+5RJUYR+IpEQco0UL7+qFXDtsh4lOZ03H Ddc/eyMG8Na3eySt/2+klumECgNAa9l0iDJdxWDZAoGBAJnrhhqCG+OR3PgW9yVn XWuYXQ4PjoDZtg/PnbeGr6rKFKK81TokY6VyQXUyd9f4XHwey8mZl7+jSkUY5J61 9HOzwUQEzCFR3IqyDInmFvS3ujhgWypcLpbZfxpa046FCFUpXRKgZCTYIa47tkUB m1/YiQv2wqN4juzGmRzWmRUJ -----END PRIVATE KEY----- python-pskc-1.3/tests/certificate/request.pem0000644000000000000000000000177114667346437021443 0ustar rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIICtTCCAZ0CAQAwOjELMAkGA1UEBhMCTkwxFDASBgNVBAoMC3B5dGhvbi1wc2tj MRUwEwYDVQQDDAxUZXN0IHNpZ25pbmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ggEKAoIBAQDE592IjcZUdZsVCchoVOucr787CoZf+B6Q5olUKFBqu3WsWC3XS8fW 1vzklEJiGUvTYnm3JmRiRdAdfq7twLvetvgrKOvRePpjzfsSqH6skKZfYM4KOSF7 xc74I5VUGLkho3+geSfjaEG3rkf5WraiK40+ytULQ0mGa+FkjdAVSNSaRJkN7nJi IMLfWbQtZmYPAKc5w7P8n/HXzemOYxWIwmkbfkM87/1Ksd64Cv7ZfCq9XyVxlmgy NGnz642u7MlQpE7NVYTGmbeZ18xJl9e1uD8wc9zOhJQaztOktgfvgw3th44qdMCf qglFZlELgwnQvaN+TounH9ESMvIGUXmhAgMBAAGgNjA0BgkqhkiG9w0BCQ4xJzAl MCMGA1UdEQQcMBqBGHRlc3Qtc2lnbmluZ0BleGFtcGxlLmNvbTANBgkqhkiG9w0B AQsFAAOCAQEAuVZnrqmn7SgQDAgzYI7dPLI2NeDwq4+1JOGOupEO4Iz9N4AEa5dt rj0FiFzLGRueskI0W6GgFW+FHxEiFVykGttY/oWXkjMziJWYu9QbV+JsmIHSi89y m4+zoSXEvZTDfmuj5D2FLTuDSLwIxbWLn+6c/3/hKsK1SdD7mJ/JA6gKcmuM+RAM mOOB5/ZJ5z/3heKoy68NR5kt3JdI1OJokoep0rb6hsImxf9j3gJXPFg8Rhg0spDh DOBzrlqTemokUc6n+2Xa3zY31rB7vaqfX47b4eLDZQ7Nj3Ap9Nu2wH0HXHQDiRI8 G05Tf6c1fZNuIkEm42PqE072oWZcapnMfQ== -----END CERTIFICATE REQUEST----- python-pskc-1.3/tests/certificate/ss-certificate.pem0000644000000000000000000000230014667346437022645 0ustar rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDVTCCAj2gAwIBAgIUO6vz45MS/Fhgkd//gDxaU8RuOiMwDQYJKoZIhvcNAQEL BQAwOjELMAkGA1UEBhMCTkwxFDASBgNVBAoMC3B5dGhvbi1wc2tjMRUwEwYDVQQD DAxUZXN0IHNpZ25pbmcwHhcNMjQwODI3MjEyODI5WhcNMzQwODI1MjEyODI5WjA6 MQswCQYDVQQGEwJOTDEUMBIGA1UECgwLcHl0aG9uLXBza2MxFTATBgNVBAMMDFRl c3Qgc2lnbmluZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMTn3YiN xlR1mxUJyGhU65yvvzsKhl/4HpDmiVQoUGq7daxYLddLx9bW/OSUQmIZS9Niebcm ZGJF0B1+ru3Au962+Cso69F4+mPN+xKofqyQpl9gzgo5IXvFzvgjlVQYuSGjf6B5 J+NoQbeuR/latqIrjT7K1QtDSYZr4WSN0BVI1JpEmQ3ucmIgwt9ZtC1mZg8ApznD s/yf8dfN6Y5jFYjCaRt+Qzzv/Uqx3rgK/tl8Kr1fJXGWaDI0afPrja7syVCkTs1V hMaZt5nXzEmX17W4PzBz3M6ElBrO06S2B++DDe2Hjip0wJ+qCUVmUQuDCdC9o35O i6cf0RIy8gZReaECAwEAAaNTMFEwHQYDVR0OBBYEFBpTgqnHgbbLKxFnh279ZGfv 9oEVMB8GA1UdIwQYMBaAFBpTgqnHgbbLKxFnh279ZGfv9oEVMA8GA1UdEwEB/wQF MAMBAf8wDQYJKoZIhvcNAQELBQADggEBALjBc27+A+I1hOH4L+ffI21kuj9FSKDF LUDFc9GvIna+cIM7AvyXRYxL7IweGH4dHHoINCuR4nwYdiZNABXy1rYYD998ZHjE IP8yg5wBWhBm9AJT8z0gAHQDScp7ngSUVbkrRjpxkGnMIWTsSDaHj9ub/MU3bM2n D+Xo/jn68+9mOnpREkn9b7GptV8Glrgqiwc8SGPSR2DKrMQC58obqgzLojkF39n5 B0Y//xse4r1+8bfFQ7gRNlL7+TTf7+kMy+Ilf7zCGi8hjhGFoNY1SxSKqzjkT+qp IJaXYkLesVKYY2CMcSzBEZa1Yimf5HCz3xamm1nSXrwWic6BBGHsYHo= -----END CERTIFICATE----- python-pskc-1.3/tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/0000755000000000000000000000000014667356044027477 5ustar rootroot00000000000000python-pskc-1.3/tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/non-encrypted.pskcxml0000644000000000000000000000267713300352662033664 0ustar rootroot00000000000000 Token Manufacturer 98765432187 01/01/2008 Credential Issuer MyFirstToken WldjTHZwRm9YTkhBRytseDMrUnc= WldjTHZwRm9YTkhBRytseDM= AAAAAAAAAAw= WldjTHZwRm9YTkhBRytseDM= ././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000python-pskc-1.3/tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/password-encrypted.pskcxmlpython-pskc-1.3/tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/password-encrypted.psk0000644000000000000000000000316013300352662034034 0ustar rootroot00000000000000 y6TzckeLRQw= 999 Token Manufacturer 98765432187 01/01/2008 Credential Issuer MySecondToken F/CY93NYc/SvmxT3oB6PzG7p6zpG92/t hN793ZE7GM6yCM6gz9OKNRzibhg= VVBYqRF1QSpetvIB2vBAzw== 6clqJvT9l0xIZtWSch2t6zr0IwU= python-pskc-1.3/tests/draft-hoyer-keyprov-portable-symmetric-key-container-01/0000755000000000000000000000000014667356044027500 5ustar rootroot00000000000000python-pskc-1.3/tests/draft-hoyer-keyprov-portable-symmetric-key-container-01/non-encrypted.pskcxml0000644000000000000000000000263713300352662033661 0ustar rootroot00000000000000 Token Manufacturer 98765432187 01/01/2008 Credential Issuer MyFirstToken WldjTHZwRm9YTkhBRytseDMrUnc= WldjTHZwRm9YTkhBRytseDM= AAAAAAAAACo= WldjTHZwRm9YTkhBRytseDM= ././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000python-pskc-1.3/tests/draft-hoyer-keyprov-portable-symmetric-key-container-01/password-encrypted.pskcxmlpython-pskc-1.3/tests/draft-hoyer-keyprov-portable-symmetric-key-container-01/password-encrypted.psk0000644000000000000000000000310013300352662034027 0ustar rootroot00000000000000 y6TzckeLRQw= 999 Token Manufacturer 98765432187 01/01/2008 Credential Issuer MySecondToken F/CY93NYc/SvmxT3oB6PzG7p6zpG92/t hN793ZE7GM6yCM6gz9OKNRzibhg= VVBYqRF1QSpetvIB2vBAzw== 6clqJvT9l0xIZtWSch2t6zr0IwU= python-pskc-1.3/tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/0000755000000000000000000000000014667356044025655 5ustar rootroot00000000000000python-pskc-1.3/tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/actividentity-3des.pskcxml0000644000000000000000000000244713036777211032773 0ustar rootroot00000000000000 ActivIdentity 34567890 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 32 0 python-pskc-1.3/tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/ocra.pskcxml0000644000000000000000000000201013035526505030162 0ustar rootroot00000000000000 TokenVendorAcme 987654322 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 python-pskc-1.3/tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/securid-aes-counter.pskcxml0000644000000000000000000000211613035526505033126 0ustar rootroot00000000000000 RSA, The Security Division of EMC 123456798 Issuer 2006-04-14T00:00:00Z 2010-09-30T00:00:00Z MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 python-pskc-1.3/tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/totp.pskcxml0000644000000000000000000000225213035526505030234 0ustar rootroot00000000000000 TokenVendorAcme 987654323 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 30 4 python-pskc-1.3/tests/draft-ietf-keyprov-pskc-02/0000755000000000000000000000000014667356044021652 5ustar rootroot00000000000000python-pskc-1.3/tests/draft-ietf-keyprov-pskc-02/figure2.pskcxml0000644000000000000000000000165113036777211024613 0ustar rootroot00000000000000 Manufacturer 987654321 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 python-pskc-1.3/tests/draft-ietf-keyprov-pskc-02/figure3.pskcxml0000644000000000000000000000310113036777211024604 0ustar rootroot00000000000000 Manufacturer 987654321 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 OTP Issuer MTIzNA== python-pskc-1.3/tests/draft-ietf-keyprov-pskc-02/figure4.pskcxml0000644000000000000000000000355713036777211024624 0ustar rootroot00000000000000 Pre-shared-key http://www.w3.org/2000/09/xmldsig#hmac-sha1 Manufacturer 987654321 Issuer pgznhXdDh4LJ2G3mOY2RL/e5cN9M3qjkBZJEE4w+NvVr64LbzkwRh9WHLAIkjXcW zdrZbGBj9BDZJzunbfAG3kyZyYc= 0 python-pskc-1.3/tests/draft-ietf-keyprov-pskc-02/figure5.pskcxml0000644000000000000000000000540713036777211024621 0ustar rootroot00000000000000 Passphrase1 Ej7/PEpyEpw= 1000 16 TokenVendorAcme 987654321 Example-Issuer oTvo+S22nsmS2Z/RtcoF8Hfh+jzMe0RkiafpoDpnoZTjPYZu6V+A4aEn032yCr4f cOpiQ/H7Zlj6ywiYWtwgz9cRaOA= python-pskc-1.3/tests/draft-ietf-keyprov-pskc-02/figure6.pskcxml0000644000000000000000000000452113036777211024616 0ustar rootroot00000000000000 MIIB5zCCAVCgAwIBAgIESZp/vDANBgkqhkiG9w0BAQUFADA4M Q0wCwYDVQQKEwRJRVRGMRMwEQYDVQQLEwpLZXlQcm92IFdHMRIwEAYDVQQDEwlQU0tDIF Rlc3QwHhcNMDkwMjE3MDkxMzMyWhcNMTEwMjE3MDkxMzMyWjA4MQ0wCwYDVQQKEwRJRVR GMRMwEQYDVQQLEwpLZXlQcm92IFdHMRIwEAYDVQQDEwlQU0tDIFRlc3QwgZ8wDQYJKoZI hvcNAQEBBQADgY0AMIGJAoGBALCWLDa2ItYJ6su80hd1gL4cggQYdyyKK17btt/aS6Q/e DsKjsPyFIODsxeKVV/uA3wLT4jQJM5euKJXkDajzGGOy92+ypfzTX4zDJMkh61SZwlHNJ xBKilAM5aW7C+BQ0RvCxvdYtzx2LTdB+X/KMEBA7uIYxLfXH2Mnub3WIh1AgMBAAEwDQY JKoZIhvcNAQEFBQADgYEAe875m84sYUJ8qPeZ+NG7REgTvlHTmoCdoByU0LBBLotUKuqf rnRuXJRMeZXaaEGmzY1kLonVjQGzjAkU4dJ+RPmiDlYuHLZS41Pg6VMwY+03lhk6I5A/w 4rnqdkmwZX/NgXg06alnc2pBsXWhL4O7nk0S2ZrLMsQZ6HcsXgdmHo= http://www.w3.org/2000/09/xmldsig#hmac-sha1 TokenVendorAcme 987654321 Example-Issuer hJ+fvpoMPMO9BYpK2rdyQYGIxiATYHTHC7e/sPLKYo5/r1v+4 xTYG3gJolCWuVMydJ7Ta0GaiBPHcWa8ctCVYmHKfSz5fdeV5nqbZApe6dofTqhRwZK6 Yx4ufevi91cjN2vBpSxYafvN3c3+xIgk0EnTV4iVPRCR0rBwyfFrPc4= 0 python-pskc-1.3/tests/draft-ietf-keyprov-pskc-02/figure7.pskcxml0000644000000000000000000000211013036777211024607 0ustar rootroot00000000000000 Manufacturer 987654321 Issuer keyProfile1 MasterKeyLabel 0 OTP python-pskc-1.3/tests/draft-ietf-keyprov-pskc-02/figure8.pskcxml0000644000000000000000000000430513036777211024620 0ustar rootroot00000000000000 TokenVendorAcme 0755225266 Example-Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 j6lwx3rvEPO0vKtMup4NbeVu8nk= j6lwx3rvEPO0vKtMup4NbeVu8nk= CN=Example.com,C=US 12345678 python-pskc-1.3/tests/draft-ietf-keyprov-pskc-02/figure9.pskcxml0000644000000000000000000000707413036777211024627 0ustar rootroot00000000000000 TokenVendorAcme 654321 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 2006-05-01T00:00:00Z 2006-05-31T00:00:00Z TokenVendorAcme 123456 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 2006-05-01T00:00:00Z 2006-05-31T00:00:00Z TokenVendorAcme 9999999 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 2006-03-01T00:00:00Z 2006-03-31T00:00:00Z Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 2006-04-01T00:00:00Z 2006-04-30T00:00:00Z python-pskc-1.3/tests/encryption/0000755000000000000000000000000014667356044017145 5ustar rootroot00000000000000python-pskc-1.3/tests/encryption/aes128-cbc-noiv.pskcxml0000644000000000000000000000226313167760305023245 0ustar rootroot00000000000000 Pre-shared-key Diahu/VzjP5IbRYxRgNYT+YQcIa03s5FLMnHjTM0rSQ= 5wgci2UHdatrUNSnFW87HaV5/VWY08IyXyPBmohSoa8= mNUFNm7a8VqhdmoYDX95B/V7HY36hHOKr6F9jQ== python-pskc-1.3/tests/encryption/aes128-cbc.pskcxml0000644000000000000000000000226413167760305022275 0ustar rootroot00000000000000 Pre-shared-key SVZJVklWSVZJVklWSVZJViZS3d+rzbWqD74OQPuyiwrD+XlDXK7ef602mwOebfTR AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv CjGsEXpmZYGMyejd8WJdLFRBWE9XGJLiigPObg== python-pskc-1.3/tests/encryption/aes192-cbc.pskcxml0000644000000000000000000000231013167760305022266 0ustar rootroot00000000000000 Pre-shared-key SVZJVklWSVZJVklWSVZJVmDaimFqjBwo8MSWUGmwDkqJvsb1xlkf0MHfyqeooZzM AAECAwQFBgcICQoLDA0OD/616ab2do/xcWNKuW1qE3rSzwqoZcpg5ucwpjiZ07tV ADfYOligu/3jDK9QhUGO7gGMxNxmrBUy4qtv4HyKF8o= python-pskc-1.3/tests/encryption/aes256-cbc.pskcxml0000644000000000000000000000235413167760305022277 0ustar rootroot00000000000000 Pre-shared-key SVZJVklWSVZJVklWSVZJVlAHw4GN7cbXseMBjNjUCrR8Lb4syW0I7bbNZbCBRt7T AAECAwQFBgcICQoLDA0OD7mg24krBXvsLMVBhZbLXDVFEWhqNqRTCO8AfowoBFcd JdB5+Ub/VSapUmJq+ZzEbseBPijlOp6BGy3+AAHoM7x17MbqR77xREby+9/65UOG python-pskc-1.3/tests/encryption/camellia128-cbc.pskcxml0000644000000000000000000000231613300352662023262 0ustar rootroot00000000000000 Pre-shared-key VnWO9OoCQWEuH0qsz1VnywNqjvXC/kUNcMp8cFfCqiw48doVY9XVcMV0GR5vn6g3 DvtlFS8/QZle2xG8PjfA8Kg4bsjLlU8kH/sEfXC9VLWib2Z/WU8RDHR+fI9uCqOs RDATcSJh3n8TAvMDoPzKqobgOCPZSluA7Gmvpg== python-pskc-1.3/tests/encryption/camellia192-cbc.pskcxml0000644000000000000000000000233613300352662023265 0ustar rootroot00000000000000 Pre-shared-key WAL6YRtYa5/rE/DX26NKXaHpDu2qE4q5kdtCZmvhZeHMsV76CuLlO5ybTD9RsLWK Wa5Kz1/BhuqUcpQyw8qRSDwurIsm2vjUR/PO3w1Q3//PFfHod+DgBhRW2BecWpP5 RDATcSJh3n8TAvMDoPzKqobgOCPZSluA7Gmvpg== python-pskc-1.3/tests/encryption/camellia256-cbc.pskcxml0000644000000000000000000000235613300352662023270 0ustar rootroot00000000000000 Pre-shared-key uj9VFSjENtykiBdHoxF/CZ8y2XnWA/fW89i4xonEH9iYK2AH0/hkH7bVmI2ObcSa X7We+WREABNPa0jlsUHZqF5CWUQiPYdXJ+7ure96AcNH/7TXcQs4mFuSCOHpiv/W RDATcSJh3n8TAvMDoPzKqobgOCPZSluA7Gmvpg== python-pskc-1.3/tests/encryption/kw-aes128.pskcxml0000644000000000000000000000146313167760305022167 0ustar rootroot00000000000000 Pre-shared-key H6aLCoEStEeu80vY+1p7gp0+hiNx0s/l python-pskc-1.3/tests/encryption/kw-aes192.pskcxml0000644000000000000000000000147013167760305022166 0ustar rootroot00000000000000 Pre-shared-key lneLJa5spDX5K1uXwFCu0kaKuKF62E5d python-pskc-1.3/tests/encryption/kw-aes256.pskcxml0000644000000000000000000000154413167760305022171 0ustar rootroot00000000000000 Pre-shared-key qPm8FhLGiz/25vT74w5x5Haci4CjLLiVjNXRfWslTaE= python-pskc-1.3/tests/encryption/kw-camellia128.pskcxml0000644000000000000000000000151413300352662023153 0ustar rootroot00000000000000 Pre-shared-key WB128TBZ1WGZzPNJNbwNrWRqQceU7M4FQSJPy2nw6iI= python-pskc-1.3/tests/encryption/kw-camellia192.pskcxml0000644000000000000000000000153413300352662023156 0ustar rootroot00000000000000 Pre-shared-key y/wSpn3aNjeXzY1giHLOy0P+WQ+NmkN7EovBtHBXZ14= python-pskc-1.3/tests/encryption/kw-camellia256.pskcxml0000644000000000000000000000155413300352662023161 0ustar rootroot00000000000000 Pre-shared-key tCrhhD62tBGCcbsp8GV91+79MhaXTy1MP1SkaT2OLaU= python-pskc-1.3/tests/encryption/kw-tripledes.pskcxml0000644000000000000000000000157613167760305023164 0ustar rootroot00000000000000 Pre-shared-key aQEHYY7wkrO0jKF5ayNK6foz67QVlgQDfbXWqE6zqsJ2jGMndaRn1A== python-pskc-1.3/tests/encryption/mac-over-plaintext.pskcxml0000644000000000000000000000240113036777211024254 0ustar rootroot00000000000000 Pre-shared-key SVZJVklWSVZJVklWSVZJViZS3d+rzbWqD74OQPuyiwrD+XlDXK7ef602mwOebfTR AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv RDATcSJh3n8TAvMDoPzKqobgOCPZSluA7Gmvpg== python-pskc-1.3/tests/encryption/no-mac-key.pskcxml0000644000000000000000000000175412767245644022521 0ustar rootroot00000000000000 Pre-shared-key AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv N8Agsfe5A7WQFswmdvL/MHdd3y0= python-pskc-1.3/tests/encryption/tripledes-cbc.pskcxml0000644000000000000000000000230513167760305023261 0ustar rootroot00000000000000 Pre-shared-key SVZJVklWSVbkU3i5koQy9wRwmtLzydqFV18QfbCMBR8= SVYxMjM0NTbvR25//t5tAuWfL+6ma90GGESqe3AlrJM= 4eM8sZbswb+q4q4qZ18q2Af5LEIzZy4M1Mz7XF6Gnc8KozCp87ykK10uOHZpdKLrc9j8Yz0dw9CtQUVcijQKgA== python-pskc-1.3/tests/feitian/0000755000000000000000000000000014667356044016372 5ustar rootroot00000000000000python-pskc-1.3/tests/feitian/20120919-test001-4282.xml0000644000000000000000000000330612676003033021601 0ustar rootroot00000000000000 FeiTian Technology Co.,Ltd 2600215704919 zSK3gP/9LVNpaAfs039ATa45MnA= 60 2012-09-19T00:00:00Z 2022-09-01T00:00:00Z FeiTian Technology Co.,Ltd 1000117803294 TfpfT+8Jn9s6FYNIySi+uzXkIi0= 0 2012-09-19T00:00:00Z 2022-09-01T00:00:00Z python-pskc-1.3/tests/feitian/file1.pskcxml0000644000000000000000000001125112676003033020756 0ustar rootroot00000000000000 Feitian Technology Co.,Ltd 1000133508267 PuMnCivln/14Ii3DNhR4/1zGN5A= 0 2012-08-01T00:00:00Z 2037-12-31T00:00:00Z Feitian Technology Co.,Ltd 1000133508255 wRjcslncyKj//L1oaDVQbAvCNnI= 0 2012-08-01T00:00:00Z 2037-12-31T00:00:00Z Feitian Technology Co.,Ltd 2600124809778 MRffGnGNJKmo8uSW313HCvGNIYM= 60 2012-08-01T00:00:00Z 2037-12-31T00:00:00Z Feitian Technology Co.,Ltd 2600124809787 9O9PX9g20x74kIcaLLrGiwMUReM= 60 2012-08-01T00:00:00Z 2037-12-31T00:00:00Z Feitian Technology Co.,Ltd 2600135004012 A0DxFX1zRVTsxJlMKFsDXuNQYcI= 60 2012-08-01T00:00:00Z 2037-12-31T00:00:00Z Feitian Technology Co.,Ltd 2600135004013 NSLuCF/qeQPsqY7Sod4anJMjIBg= 60 2012-08-01T00:00:00Z 2037-12-31T00:00:00Z python-pskc-1.3/tests/invalid/0000755000000000000000000000000014667356044016401 5ustar rootroot00000000000000python-pskc-1.3/tests/invalid/incomplete-derivation.pskcxml0000644000000000000000000000324712767245644024315 0ustar rootroot00000000000000 My Password 1 2GTTnLwM3I4e5IO5FkufoOEiOhNj91fhKRQBtBJYluUDsPOLTfUvoU2dStyOwYZx oTvo+S22nsmS2Z/RtcoF8Hfh+jzMe0RkiafpoDpnoZTjPYZu6V+A4aEn032yCr4f LP6xMvjtypbfT9PdkJhBZ+D6O4w= python-pskc-1.3/tests/invalid/mac-algorithm.pskcxml0000644000000000000000000000325414104475311022515 0ustar rootroot00000000000000 Pre-shared-key ESIzRFVmd4iZABEiM0RVZgKn6WjLaTC1sbeBMSvIhRejN9vJa2BOlSaMrR7I5wSX Manufacturer 987654321 CM_ID_001 Issuer AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv Su+NvtQfmvfJzF6bmQiJqoLRExc= 0 python-pskc-1.3/tests/invalid/mac-value.pskcxml0000644000000000000000000000327413036777211021654 0ustar rootroot00000000000000 Pre-shared-key ESIzRFVmd4iZABEiM0RVZgKn6WjLaTC1sbeBMSvIhRejN9vJa2BOlSaMrR7I5wSX Manufacturer 987654321 CM_ID_001 Issuer AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv Au+NvtQfmvfJzF6bmQiJqoLRExc= 0 python-pskc-1.3/tests/invalid/missing-encryption-method.pskcxml0000644000000000000000000000173113036777211025115 0ustar rootroot00000000000000 Pre-shared-key AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv python-pskc-1.3/tests/invalid/missing-encryption.pskcxml0000644000000000000000000000154513026303736023636 0ustar rootroot00000000000000 Pre-shared-key AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv python-pskc-1.3/tests/invalid/no-mac-method.pskcxml0000644000000000000000000000171712767245644022444 0ustar rootroot00000000000000 Pre-shared-key AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv LP6xMvjtypbfT9PdkJhBZ+D6O4w= python-pskc-1.3/tests/invalid/not-boolean.pskcxml0000644000000000000000000000060112676003033022177 0ustar rootroot00000000000000 python-pskc-1.3/tests/invalid/not-integer.pskcxml0000644000000000000000000000051112676003033022215 0ustar rootroot00000000000000 TWELVE python-pskc-1.3/tests/invalid/not-integer2.pskcxml0000644000000000000000000000047412676003033022307 0ustar rootroot00000000000000 python-pskc-1.3/tests/invalid/notxml.pskcxml0000644000000000000000000000006612676002045021312 0ustar rootroot00000000000000 python-pskc-1.3/tests/invalid/unknown-encryption.pskcxml0000644000000000000000000000163712676003033023663 0ustar rootroot00000000000000 Pre-shared-key AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv python-pskc-1.3/tests/invalid/wrongelement.pskcxml0000644000000000000000000000006712676002045022500 0ustar rootroot00000000000000 python-pskc-1.3/tests/invalid/wrongversion.pskcxml0000644000000000000000000000076213036777211022543 0ustar rootroot00000000000000 Issuer-A MTIzNA== python-pskc-1.3/tests/misc/0000755000000000000000000000000014667356044015706 5ustar rootroot00000000000000python-pskc-1.3/tests/misc/SampleFullyQualifiedNS.xml0000644000000000000000000001145412767245644022761 0ustar rootroot00000000000000 C8R6xBQu36C7Z1zDXc8rN//pE3ksB2rK 249 16 PassPhrase rBdEN+D5lY5511A1isLWvCHzZAhDJ779KFlvoIv48VFT/FJjLfzOpGDSeGonSLjk Company XXXX0000001F Token 1 Company HMAC-SHA256 PTTVlVTEiH/4HdphmBhxFJ7h5bGu5x476HXYd5jUtELg+MVqv+28/V1qT2bXdZXQ5rISLRNsZV0solNfH5WOSQ== snQNqaSQl8vxksYL9dDPrS0LHxM= 0 172800 OTP Company XXXX0000001F Token 2 Company OCRA-1:HOTP-SHA256-9:QN02-T2H gO8BOgs+yOpfbrvdWV7eyi9/LdNkD/YwpXSllE+koMWkx/9n0Ms3D51Q5Av4KbRyrlGHOO61oeRYI0FYSbrdRg== L1Pne7RIEU1oLM+1kok2zfsfNSw= 0 7200 CR python-pskc-1.3/tests/misc/checkdigits.pskcxml0000644000000000000000000000334412676003033021557 0ustar rootroot00000000000000 MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= python-pskc-1.3/tests/misc/integers.pskcxml0000644000000000000000000000331412676002045021115 0ustar rootroot00000000000000 Pre-shared-key 831791 NzA5Nzkx AAYxSg== I9Ro0yqZSyNhPY43+fZue7JvyPxbAqtNPuFiu3HprxY= juW3FkXwJwOoyJJ24vfY+ug/J3qAnaEMrhDeJgAMWMg= python-pskc-1.3/tests/misc/odd-namespace.pskcxml0000644000000000000000000000110112676002045021765 0ustar rootroot00000000000000 Issuer-A MTIzNA== python-pskc-1.3/tests/misc/partialxml.pskcxml0000644000000000000000000000146413300352662021454 0ustar rootroot00000000000000 python-pskc-1.3/tests/misc/policy.pskcxml0000644000000000000000000000515614104475311020600 0ustar rootroot00000000000000 MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 2006-05-01T00:00:00Z 2026-05-31T00:00:00Z 4321 OTP MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= OTP MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= OTP Bar MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= OTP bar MTIzNA== python-pskc-1.3/tests/multiotp/0000755000000000000000000000000014667356044016630 5ustar rootroot00000000000000python-pskc-1.3/tests/multiotp/pskc-hotp-aes.txt0000644000000000000000000001047213300352662022034 0ustar rootroot00000000000000 Pre-shared-key q+ZCUiDoHbBbug4XilKtI+9T99F/xZsb1T1ra35qGwLLA97mHDWnptpFIBZu8HKy Manufacturer ZZ0000000000 CM_ID_007 Issuer0 Ze63bjoRjdZRxFs8RouCE5BMP/ust0gAhCd0O8BWNn1e8JSg74hgv3/QIxv4r3lw PrKAa1V4eIP98Lh5Yv1hcSmJoxs= FErCcdjsEpAu/P4xvt3XbIsAm6FC3HpzZdRsIN/wADo= kuha13YGJLmwKRxt8fDY03IoGxk= Manufacturer ZZ0100000000 CM_ID_007 Issuer0 Ce/Mwl6WBZfuPI4OIoWhcY5G46oAznRrsYQ1zBhqMblJyf44+UIyYMHQ5gFY/e9Z qa4SZaEiwoSIDgrnvKI5c0/1nEg= n4g7hX9wq1wrxRX9W21p2FQgAjOwnvYFye8D8n4+Nc0= 5mEA0KtQRmpMltb3/AkCUzwbdaw= python-pskc-1.3/tests/multiotp/pskc-hotp-pbe.txt0000644000000000000000000000640713300352662022035 0ustar rootroot00000000000000 Ej7/PEpyEpw= 1000 16 Passphrase1 3Y5KqMW+np7kK8GpMDP+90A6VfM/inEqxoJyy93GUPE+4aaL1fwdjyiiQz/0v9vB Manufacturer ZZ0000000002 CM_ID_007 Issuer0 o1hcXgyZNxLXiopuyNyZhkbooppb/xUierAPRFKu9eSS0WubkhhOt8iUP5aD0731 X/EFKw8NsRV+alKO8VOnQDrK3sc= 6HhzB+wO/7/zJbNmLYaFd3awuEHXbck5AFb66rEsaZo= fqp0U8/7qMJEs6GyfnXnACn0Z2Q= python-pskc-1.3/tests/multiotp/pskc-totp-aes.txt0000644000000000000000000001543013300352662022047 0ustar rootroot00000000000000 Pre-shared-key aJtbg144FkWB+rAdIZLEYGeBguzImDC/s+u6hvQpbn1HKH4d9okcfRVDE1eKc/em Manufacturer ZZ1000000000 CM_ID_007 Issuer0 1/4klvTYXsM7n35dRXD8DEUmG9msDTFGj7iYPbSWd62eEdnBUDTHvuHSo0H1Oa8x FSJiyiNOXUvDl42te34LPpmQA00= 0 30 Manufacturer ZZ1100000000 CM_ID_007 Issuer0 LHgaE/1m+DeXjNhWVA5BJsX/yv6KZmAWnVG5q6dQkkWbGu0ZO9QvqhBUxU0qoe4W 1HZvOMVLYU52SnE+98BTxDZrDds= 0 30 Manufacturer ZZ1000000001 CM_ID_007 Issuer0 HMAC-SHA256 gDk4Xm6Ab4cABb8oVzDbxbHP2UqjdhwuoV2z9NQvW5uV3dqpU7uApCTF9y20IX8uEViEur80QzgftiNlnX6RRw== H1cru72pCCWvSuL7jES0Rp0nKOM= 0 30 Manufacturer ZZ1100000001 CM_ID_007 Issuer0 HMAC-SHA256 HbKFsvEFtqDd8fOrKbigA+7wv47Fdfv1m8BWclZGupgixFiqY9B5LrZq7e5Vd4QcTNatmMW2fDmNdSVbMs91rw== xQvm4mScVI+7GivxvodoTIy3rLs= 0 30 python-pskc-1.3/tests/multiotp/pskc-totp-pbe.txt0000644000000000000000000000575413300352662022055 0ustar rootroot00000000000000 Ej7/PEpyEpw= 1000 16 Passphrase1 sMSFumtOPpf+FRlkpLQNBAfLijJmm6L8iU2QgnrEpYpsukP2ewzFmGnTDtWUmtHk Manufacturer ZZ1000000002 CM_ID_007 Issuer0 +VJbRq6uiXOfcLfiWG6SlN3wtYoQ8LesDU3BolqPeoZe6fQVG2J5F3ryBZTtIZvM fjI2IRbUxUYU24boM6atz+RHRV0= 0 30 python-pskc-1.3/tests/multiotp/tokens_hotp_aes.pskc0000644000000000000000000001044713300352662022666 0ustar rootroot00000000000000 Pre-shared-key wrjW00DjkG/3Ti5w/+MmSQAiBWho8CPjT7GtcQ59PWaOd00kXObQ0N2DZXEViu4i Manufacturer ZZ7000000001 Issuer0 HMAC-SHA256 yEJynmqPig2WnnEdBaT7Yq3XkPbPiTNpaGmJFVOyzxzS3aSYEAZRCEf7wHA3Fvk/kDHEvQks3t9eVV0JV7Y/uA== OEPJcjpyjHKZSFheQU551nb0ls4= mZ+p+VX8Trg1lKkndr5p7/O9ZyS2dU5DD7cpZljhQ7o= sFa44n9rrsfWq+KcIffF1Xl3Auw= Manufacturer ZZ7000000002 Issuer0 HMAC-SHA512 fE30qGXYpqP6D4k5cCHVMMIkaJr4cJZN7oOjRWZ8DOJ8Wk3e+MoUUO3wyRa3UOz5feOvXMGlhUH9uuS0G88U9QIYcuscHuh6X/BTAaaJyrOQZmZOzfBq9Mky8+/dihHW KxF+McV9MoIwJWZkmVturQ8/GwA= u2U0Zys7h+Le/fE6Y36E3+jvU8501bQaZ9bDnQKoFTo= qIg1lePsFokepjWYGUh14kqzsYQ= python-pskc-1.3/tests/multiotp/tokens_hotp_pbe.pskc0000644000000000000000000000633313300352662022663 0ustar rootroot00000000000000 OtMW8Un/HGSlXFr/2kG1cQ== 1000 16 Passphrase1 FTqUDYQ8Y/7T5bLHBQD6n9bDkgJWBIbAnf3FFKW7HVBs6zJEyPnG7OCzeZoVurdK Manufacturer ZZ7000000000 Issuer0 HMAC-SHA1 Np1sHx016g2ZxvQtVqDRNMKYv6q+Hg0Nkapeg7RtoqDx8aaEhC1b5VJy9PmW3q8w oZw2yK9JoL9DpX3/yogFMn9PYv0= uE85joDN6QDu7CZ5+GkiVQ7JTS2gIJi/2/y9/F5Guos= TJToGTyf/oVMePIVjx5XZDZfg6c= python-pskc-1.3/tests/multiotp/tokens_ocra_aes.pskc0000644000000000000000000017477613300352662022660 0ustar rootroot00000000000000 Pre-shared-key U+4mSH23ab4JKjBqJ35favjKfKY3hf9sWKp/P7L/TAqpcxUYGSUeK/RMfL0Fv9hy Manufacturer ZZ9000000001 Issuer0 OCRA-1:HOTP-SHA1-8:QN08 lrdvTkSeq9hj9QimjKE9hcH/OkmWpmxsmJ3fP57AqHygLTrUs88MVW8KB+h5yb9r we9aOQ8+UUsWIZAd2I/eqnlJ1Cc= Manufacturer ZZ9000000002 Issuer0 OCRA-1:HOTP-SHA256-8:QA08 aYojAzVasgEkxJhbOrBCfIikgi686p4u3TILah2QT2m30SgiT/wCSzTpLXUQBuoirzKfkjbd3Bpco3tHyzc+2A== X4R8LvhswobqWv45doqicRNEtzM= Manufacturer ZZ9000000003 Issuer0 OCRA-1:HOTP-SHA1-6:QA06-PSHA1 8bxb34b2HoBWaVPbBwdLy5HI+/i036YdZz0h0MyuvIKEvGnHI9GmuGJGUXebfgCY cWN0g8q6Fb3HJNcXla0CSoOMe3A= Manufacturer ZZ9900000003 Issuer0 3piq9NJAzfkcZ7z1Iu7IpLB1CjdzY4gNQyiOVnW+kcdhBW4NTTqmHx6o8mmSuE68 4pQgbgcE1B2lBRhRmqv5HRYcbN0= Manufacturer ZZ9000000004 Issuer0 OCRA-1:HOTP-SHA1-6:C-QA06 STKpyutR8Nb3BbjNmG8kf7NoTO66yccq5C9MDCZF2AXMUSDEdUhqMwqjfhg92Qu5 3T1/FGP1wIAVOB9ViVXs6kBQs/g= xasM5vP1vlZBimdMLWoqJWuZNJMY1iEKDnw9MgB0F9I= xw+qx3/xyXDYosr3n7PU3barLSU= Manufacturer ZZ9000000005 Issuer0 OCRA-1:HOTP-SHA1-8:C-QN08 ZCAx2z7qNDvmPRPyXpvQsZkoZjiaSlNnMaz1bh5ZWAdWtYMuvSgMkPtDN0xE8KgZ 2loMyhUVMcJFUmMEfdriuPZNgLA= aVN/any99YJCpczoTns7H5JwyPUR8OQwNI2lLf4imhQ= AXc+jkG1uZOHcCXyMcqIdm7QCpA= Manufacturer ZZ9000000006 Issuer0 OCRA-1:HOTP-SHA256-8:C-QA08 BZzl+whnM71kkdW1uoY1mjM5mvNWZFxn3f5yGaZ+kuODAqgPrvqq531ipEvke6JkARSwOWddvYyOm34Zm+LtZQ== Ko/kWZbKecJ9S8W+yE86UUcy/Ko= ByzvSK/7/5kXCsu2vfwl5sOIIbiD9pON5TSXFEkuwUE= xqmZLntYdIwYdYmNzB3ZMB3+058= Manufacturer ZZ9000000007 Issuer0 OCRA-1:HOTP-SHA256-8:C-QA08-PSHA256 b8LoriGzVqbmb9MFSr6Y/Ryj+ltGY12xWoaBuyD44ZwmwKttlyuMu1+mbSVO1jnC/vKPIy5aiDK3IeFV7/BKFA== k5uW1xUA2FzmmeWPnjazgHJuJEU= tYYVbxkQ4meKaMRNRilYbHw2p3rodjakQT34MTgp9ik= Wh0MFLFLdNYrF1wdz5qQC0bXCAY= Manufacturer ZZ9900000007 Issuer0 WKU7EnTKQqtMTG28HxAbAiZdM8YSnmJPNubxIu/aPU92aN+Mun8CDfJeNMQHcQIz3Etq8VvTn+rZoi9cP13CLw== p3bXVz9GTL+davWpoHLe1wnECLk= Manufacturer ZZ9000000008 Issuer0 OCRA-1:HOTP-SHA1-6:QA06-T30S 0H0ol1Cz2cwNLyWM6tm+4qcyZElq6Pax/FgFd/8SVyio5yeyaxCjVe38uA0bqyCm H6ZOVXjnoPCGww/3ol0Sh1Y1sOQ= QLbQdbGX+w0QSRVmBSoxtEdYr2gdzVf8YdlDA1qZ4EM= NZ46EI9Ht/sgJFREVWMFcaDA6M0= Q4mCcvb1/2TAm464+MeCyoZLAZxo20D6QLWYfew71y8= kYnUTH85koj3pVJjYKP6HnCd0Ow= Manufacturer ZZ9000000009 Issuer0 OCRA-1:HOTP-SHA1-8:QN08-T30S 2G50v7BqR/c7qYl3ya4dhStvrcMzkM+CMpwT0D3DuTMkI/m43wHM0Ujmbwjxm+6X X6513x4mOmjlU4BedoX1Zha2npQ= m42G5E/Wv102ESFI5u+OGLiMsXW1/ZgH33NTOOH1WCw= 8DeEbx1p8Mdbg0eocOHeXFXWCUE= Ay4C6M0IqRKZYpRp9Au+aswdDDorRlalmwicbNF/P94= 1C8jh1+FEQsAQvv39iW95broEkU= Manufacturer ZZ9000000010 Issuer0 OCRA-1:HOTP-SHA256-6:QA06-T30S bRVzcE4zXD1bbi4ZBXEMLDxhboBPi4rRvZIvMGfe9it+99OrFumVj0B66VZkP32vLtx4w6hagxZoVm8Gynck6Q== A7mJ6qIw+1TiUm5Prsel3RcrFtI= poGDc45s8KwViudS065t+InpbSLam13d0ivLGmdELSA= lOrmfIiH3Uun4kz+/eTO4OFs3Ww= 02GUkSl7MPZKVJcBL0OA6zsA3/ztQknHgaSGNpE6kUI= 0Y0n7RthG4WeewJOL9unWxRHW20= Manufacturer ZZ9000000011 Issuer0 OCRA-1:HOTP-SHA256-8:QA08-T30S Vkrhf45HuiXuZyxXQ9O+PZI/uj674QzB8O8z6w3M7o+kGjI+jCCXXpeizcHmQMpqY9R6CyP66efXmMlNYbv1Jg== 6oKfnDGLVAHCSyikMHr/KMPDxIQ= Wxbolq2j5VjQe5vYVMgcSwMyD7qp2/7eMCesJrWUM/E= ux785A8C7k+JZOUKuUtUspOEBBo= nr9bxUFwrD3dmOCAyjC8J/NzHDkvCap7qqNkyTQBjAY= fWzAHMMua1p1MJ1GDL37SL1KeFU= Manufacturer ZZ9000000012 Issuer0 OCRA-1:HOTP-SHA1-8:QN08-PSHA1-T30S DkhESbLkTy5DwNg45IDIveBsoxvDAk3MDdneVNlDhZOaI0w1QVwSLwJpSsnISD8x 93VEBONW7G4UETgF24EPHcvBvMc= oRjGEzmzMgt7hlTc1+pzulebdNovZRRYq8vyfl82M74= 3u17msMz3fL4F5cRqHzVla2SExQ= dsyLJivZLCS3F0AznNakR49t5NyQiD1dhHGJGFw5nWY= VevPslQOu3j8iqgMe9djGxOLD1w= Manufacturer ZZ9900000012 Issuer0 A4WAKWpJ7cLE9XiPEbKw/CDmAi7H8rtV9p1shcoo2YrVOgBmiXACT8VUaFLSfMzB 41GurI1n2Z6Kk9UKBoTnxH8j+N0= Manufacturer ZZ9000000013 Issuer0 OCRA-1:HOTP-SHA256-8:QA08-PSHA256-T30S zrXIQIx5F03cfaRYwJlnwnDDFzTAwxjtF4KnN3Brs9kISfX/0QfJ80lHtQn45dtHaAcmtJMgfI19DpvlSVPLOw== UhS9tgvwHZnuK1qN8yaZIfxPTFE= 2Kmbco73faBtAE1GQGZlW2CudPy+1ttdv4uIhucZJPM= D9ug++7+F8Vgz6EdIZYHW5zgN6E= Dsa8e4kXeccvO5bV1hr3aNgOr9iSlGizp2BvvFkZ7jI= Pdr9y7KRtpkLDYeN6IUQ/Mt7Z0o= Manufacturer ZZ9900000013 Issuer0 9Jrxfs1YlWcOLlsnRsC9BCr1jsLeCxuzFMu3fb2UifkAFzpyIJmdGGLrHVgz6XuqTq6R+wvTczNyBlqL6Eec4g== DEd9uUkZS5otw3NoAljFYCiG4M8= Manufacturer ZZ9000000014 Issuer0 OCRA-1:HOTP-SHA1-6:QA06 93rMicSI4iJq4/GnE8xCHPCZg6yDxbyhHYMULxfNJTl9o0ej5e0NJai7idXpZ0dy gZaMejTDMm7HzBlr2s9JSVYMYuQ= Manufacturer ZZ9000000015 Issuer0 OCRA-1:HOTP-SHA1-8:QA08 v/a768VMIE/ILhM0ECvN2SRdQOy/aRAbDGyQ/zhKdhWwxaX3yqWdj7Jf2XfUuccb a9c35TuApOMWiTcCvTmqoAz0cTo= Manufacturer ZZ9000000016 Issuer0 OCRA-1:HOTP-SHA256-8:QA08 +IwfWgQtRPu7ltWK15F/VO71E6HTGF2qxUcGtxToXRklDD9k2fNuz/XrM1txjV2SVWCUIZc0V38na6h4MJi0Sw== bkKCVLivS0quRvTQg2x1gvX6kY0= Manufacturer ZZ9000000017 Issuer0 OCRA-1:HOTP-SHA1-6:QA06-T30S ESnLJlQD85YtFq5sDwLo4mwlMKbNgnCIk1wgVzZlCKH+KVFCr0S2HcdqA18iHklf Esd7kmOtLwGsnfvnEAUW+gcEFMQ= 9sem1svGk57xGg9vw68KnUfw1xPJOOmAEFe56FijwLw= qG9EiRdQi9CBD8N6AQBLnbRdrgU= IjjpMqJQ/8PhB8m8CHuOQY30LRtdxZU5I9/DB6yYAZo= v6fPyj34+fDabjOGOMcHs+UYh64= Manufacturer ZZ9000000018 Issuer0 OCRA-1:HOTP-SHA1-8:QA08-T30S FNJtVaoXkbTL55ZiLaJskew4ldRebOAha+SErJfRw6KYeeQDoRvAKoCvJSprpNHQ OsA/kz7r9aJFsIi+A9fnZBQMNwA= cX4bi1203JI48hWsftfzNDqd2HEjN4GOLZZZOmcwMBM= PfWAQmkkgDoVHzSuP0z3Zp1y0ME= 8f6TlXcEKVozm68TSE0bEzAvN2VmLlKOAB/XJANFFXQ= fo6Psq89kQEFJMVBMP9V7XqVbJM= Manufacturer ZZ9000000019 Issuer0 OCRA-1:HOTP-SHA256-6:QA06-T30S kgcOrgBjm4ko/wb3zauucOANcNJK9pm//dwEs1tVc0DVC9y1k6AihtLVc+T+Qj5O0/ozYwP5AbLxvCK/7l4vKg== wWE/RcUaiD91hdPtIyel0we6acA= D8E4aP+BX+yOrCqbMm6o4nrc1gB4JMVhwp6hUFaIVo8= 2XGFP2m0aAL9qjmRECqFPlqLQrk= KwVJ2SykUK4A0/40Oc4S0EsMkNiSoLA5BrVYMV69MhY= cMGUHUA2Aj/724eezVljb351d5E= Manufacturer ZZ9000000020 Issuer0 OCRA-1:HOTP-SHA256-8:QA08-T30S A1uqZaf3GyTvwkx2v6uh9pKRzil05k+Cof3CD8jVZalmWgfp6mnDxGDOEB1fHgN4nHqocpgaNDeukbMNkyEfIg== 1pU7gblaKHQMg8M4+OnU1y5z580= bUyYhX5ZXmS7aAmJO3uTuhj3HWuj0FYNMXRPkbtHkrE= GbilU0KldLYm2qzgmerdi/YdmVc= BOpE7cNil/vt29rZZv9oRnXaH6Be5+nZsVr2ld71pew= O4v8BGevBGDlgD96lvM0g+bnndA= Manufacturer ZZ9000000021 Issuer0 OCRA-1:HOTP-SHA1-6:QH40 cW7wssP4ofGpmXCMygejt6THgKKK6oE+vZp67VXR1dYZCrH6d5kmgPB4C+0isgl0 7ez8ijPPcmu5akvkEj3hNQzdzdo= Manufacturer ZZ9000000022 Issuer0 OCRA-1:HOTP-SHA1-8:QA32 Atlpw0AmlQZeqZjUbyAx/dGxPOCt+nfmzl0L5D0AvuDuRsRxeE4sOnCYt7EULSk9 IKARR2Vs2kdLv4OyaLX5ylKrbyw= Manufacturer ZZ9000000023 Issuer0 OCRA-1:HOTP-SHA1-8:QH40 i71y5cubNftGXQelHjkN9OaDfmIxl5e6EDAsPU1czgbJ9EC4VoBL3lWeBAUzKF0C lbaC6WXRCoqcoTyJURdtgHqeqiA= Manufacturer ZZ9000000024 Issuer0 OCRA-1:HOTP-SHA256-8:QA32 JTz8SLxWk3KJndIFj8cFStAL4wptD918xF43lkX69CGi3Cv+SIvxcYSFkz+3cudIEitqnWsJeUFWGoPyh8kT/w== XoweMnAqLMh2KWnCDNx0rpoPucA= Manufacturer ZZ9000000025 Issuer0 OCRA-1:HOTP-SHA256-8:QH64 EHhmfSBnElpTYcvgx2LgoQaxfQjuKXR7p78BWNpt9ToT5EufihiAhe6CMACVfV/dIeiEJpaFgNtrPQT9/CJ24g== ljgNnKpoBXVOjsQbzPBGcS5ogqw= Manufacturer ZZ9000000026 Issuer0 OCRA-1:HOTP-SHA1-6:QA32-T30S vwpradcA029rA/yj6HcUJwl5ylkrEqp8aNALDwiMMu9A+Oz6jE60mvTAGCZqSSoe N9zH7bQi+euQZ94scr12ZdR5Dek= YAK8r3BxdVd9w3aO37G0ZHQ/sQWAK+GA9RfcFP15pcA= gwItZVroPaJdiZYTfH4p+fX6mZk= DswW6MGFaOUNlJWef9kJ+rmfZqUyhZdn8Hyj+6P+RPw= 4iLnj+1VWxtPDSdCTbO8mmffzU4= Manufacturer ZZ9000000027 Issuer0 OCRA-1:HOTP-SHA1-6:QH40-T30S oVP8EpcI8WA0Qd7PPsYSG9TSP9T+Qrj9I4euTDqPkAvGAeVmjagHBibpFs10k8bB VU/O2/FQZw9ZzYLVUJhs1zK+MU4= jL4JpbKmkvtVdthap2Mmsl6DK7H0FM+SAhsSuPmrB9w= UTlNoBXskvQydI+kPJjy5BVDwvA= NGwF8XfW28qcqGlxkim7RxmD8+blsMmV3a5CjQ+b2H8= cIAvElM0Xob5tamSmbTmUts6FO8= Manufacturer ZZ9000000028 Issuer0 OCRA-1:HOTP-SHA1-8:QA32-T30S inBVnKPWyvjYRemW54qbFCkaV8QSdkM+yHnvWh+tc5QMgiv8+3+IjwVV60/7jHI/ X+hTYvcTw8bFcMCcXm8YnG5BObA= lnGeD5pFoGu8FMJwblbKKCwTFOitZpFLz+jHlFuKnxw= GxJhXxwDE3E75whjA81ww2xiDbc= HaHrtO8BBYHkXLfXdiQCoemwr/LeYdDvHC+4V+HgniU= VBsN2fam75+cNarN0OfKXYks9Tg= Manufacturer ZZ9000000029 Issuer0 OCRA-1:HOTP-SHA1-8:QH40-T30S I+ifaWEYZ4anCimA5BA1mwGMYkczCgDJGs9jhtTbviNhFUxwxj7ejCMgsf6cEk8X 1t7hwN9GMibAcCcN2sJr24tUZcA= pOklWp9UxC4t4zQqsW/xUbWBoChfc1VBNlA8SeKxusA= mwd7xtihR/iBVsHK+UGAZfEDSFk= epsMCzzn6JvhOhYK9tLIxaOR0bEHcFAoTcu5qk0Hcho= +yUkk7ZmmjrjMVc3xPz5ROlKgI8= Manufacturer ZZ9000000030 Issuer0 OCRA-1:HOTP-SHA256-6:QH64-T30S O7+CQ9pcYN0MtbqiZ3g0Tso3CvYzCrDTV9Kh+wwqZkoTV5/jg4l/eZXfRM8E8JkD1K7MlgibA0PhwtlV8DMIIA== aKwqMJLS4MICd4PMGKUsvb0lbIQ= jz2bjkZ03IjwWyl1gZqiHoJjv/AjK7/TOA9qW6fLfhQ= NHfciyj3u1IhENAoBvohcRF9Wx0= KO0Po1NyLSTkHf6niARIocLJUXLrdz7e4qiowd/NPtE= mdbPUCunsrog0KT3uo4MRQZiMqM= Manufacturer ZZ9000000031 Issuer0 OCRA-1:HOTP-SHA256-8:QA32-T30S d17Oq0cv1HFK/XEAmASgK0DlMHiO8W950Wpf5is4BMXY9D1ot5n5nnZGEftd8IoX95il7K5aBdKfBFnl2ZuFZg== p2yeNXvUOxKsU29oz9b66s2XrkM= TotQApim/RCnT8MhiFkudOaBptHFU9iESxQk9exa7mE= R6Nmk8Gjcx35T4XYIk4I7uG56V4= 8O/Hsl1q8sR302k1CJWyFtrmQUWq/ZrE/WwEZY7Bg4g= 1K2lB+KnTWAKicqqpEWf91XAIj0= Manufacturer ZZ9000000032 Issuer0 OCRA-1:HOTP-SHA256-8:QH64-T30S jAZ2fmJOcMxeOB/0uc1Oo4egMStEMpPHGDpN11wuCv32WrshyMIuZ8pLaYHE1M0jRWRYIy2bTxSiEt4OvSZoqQ== XdjbNQkRNpkHtXk3Nw9YqFcA4mE= zLRD33LH/x0e4vZTMpeBBRa+1JWmr4J5qNmLh+p1eTo= dyaS1yjyDW4PSt9vGHyn6sBmKas= 1or92lj58ODH7gFDQmD9kydoPSMb2oMdDpo5AwP79Zk= z0tP1ak/jhxAyUs3t2Y83DxBnOw= python-pskc-1.3/tests/multiotp/tokens_ocra_pbe.pskc0000644000000000000000000000533113300352662022632 0ustar rootroot00000000000000 rnTsZXC4HbpfBiJGK11/KQ== 1000 16 Passphrase1 IAC/XU8Vmm4gORzC8Ugdno/AQ6lz5jpG6y91E3dlAcEjjHzG+m4tTeUeiXPSzwoZ Manufacturer ZZ9000000000 Issuer0 OCRA-1:HOTP-SHA1-6:QA06 83rAhYlnYh6m4EwttqwBszP4oamM+NS/t01EJo1NEFuZtjVfLaE7xgin6Fo/1Bdo l383liW+s2jGwCN8OGnlto2hx9U= python-pskc-1.3/tests/multiotp/tokens_totp_aes.pskc0000644000000000000000000001251713300352662022702 0ustar rootroot00000000000000 Pre-shared-key rVUagPa+KYZM2hfbHwnpozIL7s8tPQZvsXw67jwIslQNuBEd2862YFEH6+TkY4oE Manufacturer ZZ8000000001 Issuer0 HMAC-SHA256 cqy6Iab7wSTqANYvAHpFlnD2t/bB/3w0Tr2aPXa5qBAQ119hTzrIMXwa8+NDwlbGGDEZvi3CwhkV1USzNc3ZYg== GWy0oB6y4vjUgVJ0mi1fwVyLNwg= +bvO+gPAjRaCj362X+jY/Kq+96JWEUnTrd01LMh5WXc= E8PxvkoE0+jx31dHHefFK2s+TZ8= jhCd0hvSwh9kjFi6sbtGSyTkos9OEM/DkAhcrxi2pBE= kJvlB/aQYRCExmEoSsGHbHmc8P0= Manufacturer ZZ8000000002 Issuer0 HMAC-SHA512 sqFR1kSP7U/Lh4Wmd9J68jM0xzWLYRvyTKrrJ/Zp3YSmkmO9/y/7d4O8TTA10YTdB96Fxtu7sG2Pi585ZOrLo45plWAg29bKKZnOzdfbqHWaFgz+w2NOh5nO8BPfN6Hp bgWUXTPVrf+8FqRGozQ24/SqmfU= Fmhdk1NjK+gOtF2sZOV58H8NDQdsgShJDtjtpVofhlE= oCpIL7HUiZ6Tch1SgmgNzpaFylQ= Ux0yGMphj1Y2PT+4TQf3EAMVN9ieF42vc9J7T77HuBE= y6o/A0zvLC3bGQ09EeCHkB6wvIo= python-pskc-1.3/tests/multiotp/tokens_totp_pbe.pskc0000644000000000000000000000735713300352662022706 0ustar rootroot00000000000000 etmLw9S1kDUeGS8V5e/UAw== 1000 16 Passphrase1 lF9ReQyHsJ4LTlntuVlESIKbJiYnt5MDseTFqnmNcyHqxB/ZS4BD83ZuvTaSYCu9 Manufacturer ZZ8000000000 Issuer0 HMAC-SHA1 1VNUGh7Jf+8QibELHTDFU8Lj+ZFu836aoFKTEVWtAW+XydPBHYmC9gnty3SJ1Vh0 Oy+u0zn74nlGNjMnUe2OTfrFhHI= OcePCm3TGaXtSHQlox1pfLMWQ1dP7boiOG92wv9Jk6g= STDGPo7iRGs30+pI7SZCZ+d0J7w= yUNeDTix60G1T5DxGWC1VtFlKktG9lPoowjiGe6uuyY= yZfFtq6nmW9MoxeD31C6OKVojoQ= python-pskc-1.3/tests/nagraid/0000755000000000000000000000000014667356044016360 5ustar rootroot00000000000000python-pskc-1.3/tests/nagraid/file1.pskcxml0000644000000000000000000000762312676003033020754 0ustar rootroot00000000000000 Pre-shared-key OdudVkgsZywiwE1HqPGOJtHmBl+6HzJkylgDrZU9gcflyCddzO+cxEwzYIlOiwrE NagraID Security 306EUO4-00960 306E 880479B6A2CA2080 OCRA-1:HOTP-SHA1-6:C-QN08-PSHA1 VHdEP8TXnMmE3yiAnB5Fx+SQ85UXCNAxH7IyOixJpUZHMk9GTdFYWNsxZp8jVpfp uQ1Bef+XVXHQoW4ZzyQ/cv/9zYA= 0 NagraID Security 306EUO4-00954 306E 880489CFA2CA2080 OCRA-1:HOTP-SHA1-6:C-QN08-PSHA1 YTvA1cSntb4cPJHPFkJwuSZkAsLPo+o1EJPA22DeijZRaKhJAwArQKbwDwSmNrR1 N8QGRQ7yKd8suyUgaEVme7f0HrA= 0 NagraID Security 306EUO4-00958 306E 880497B3A2CA2080 OCRA-1:HOTP-SHA1-6:C-QN08-PSHA1 BdxW7Pb46LafGV8k2zDQ48ujoyYX7M+JumfS3Wx5dP1E9y5By/97QTMiGkzJrcWj WGhmLhbGn4Dksa7lHKfKOqbsJhU= 0 python-pskc-1.3/tests/rfc6030/0000755000000000000000000000000014667356044016036 5ustar rootroot00000000000000python-pskc-1.3/tests/rfc6030/figure10.pskcxml0000644000000000000000000000741412676002045021054 0ustar rootroot00000000000000 TokenVendorAcme 654321 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 2006-05-01T00:00:00Z 2006-05-31T00:00:00Z TokenVendorAcme 123456 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 2006-05-01T00:00:00Z 2006-05-31T00:00:00Z TokenVendorAcme 9999999 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 2006-03-01T00:00:00Z 2006-03-31T00:00:00Z TokenVendorAcme 9999999 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 2006-04-01T00:00:00Z 2006-04-30T00:00:00Z python-pskc-1.3/tests/rfc6030/figure2.pskcxml0000644000000000000000000000107212767077756021015 0ustar rootroot00000000000000 Issuer-A MTIzNA== python-pskc-1.3/tests/rfc6030/figure3.pskcxml0000644000000000000000000000223612676002045020773 0ustar rootroot00000000000000 Manufacturer 987654321 DC=example-bank,DC=net CM_ID_001 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 UID=jsmith,DC=example-bank,DC=net python-pskc-1.3/tests/rfc6030/figure4.pskcxml0000644000000000000000000000220212676002045020765 0ustar rootroot00000000000000 Manufacturer 987654321 CM_ID_001 Issuer keyProfile1 MasterKeyLabel 0 OTP python-pskc-1.3/tests/rfc6030/figure5.pskcxml0000644000000000000000000000370412676002045020776 0ustar rootroot00000000000000 Manufacturer 987654321 CM_ID_001 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 OTP Manufacturer 987654321 CM_ID_001 Issuer MTIzNA== python-pskc-1.3/tests/rfc6030/figure6.pskcxml0000644000000000000000000000435212676002045020777 0ustar rootroot00000000000000 Pre-shared-key ESIzRFVmd4iZABEiM0RVZgKn6WjLaTC1sbeBMSvIhRejN9vJa2BOlSaMrR7I5wSX Manufacturer 987654321 CM_ID_001 Issuer AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv Su+NvtQfmvfJzF6bmQiJqoLRExc= 0 python-pskc-1.3/tests/rfc6030/figure7.pskcxml0000644000000000000000000000610412676002045020775 0ustar rootroot00000000000000 Ej7/PEpyEpw= 1000 16 My Password 1 2GTTnLwM3I4e5IO5FkufoOEiOhNj91fhKRQBtBJYluUDsPOLTfUvoU2dStyOwYZx TokenVendorAcme 987654321 CM_ID_001 Example-Issuer oTvo+S22nsmS2Z/RtcoF8Hfh+jzMe0RkiafpoDpnoZTjPYZu6V+A4aEn032yCr4f LP6xMvjtypbfT9PdkJhBZ+D6O4w= python-pskc-1.3/tests/rfc6030/figure8.pskcxml0000644000000000000000000000441713300356757021012 0ustar rootroot00000000000000 MIIB5zCCAVCgAwIBAgIESZp/vDANBgkqhkiG9w0BAQUFADA4M Q0wCwYDVQQKEwRJRVRGMRMwEQYDVQQLEwpLZXlQcm92IFdHMRIwEAYDVQQDEwlQU0tDIF Rlc3QwHhcNMDkwMjE3MDkxMzMyWhcNMTEwMjE3MDkxMzMyWjA4MQ0wCwYDVQQKEwRJRVR GMRMwEQYDVQQLEwpLZXlQcm92IFdHMRIwEAYDVQQDEwlQU0tDIFRlc3QwgZ8wDQYJKoZI hvcNAQEBBQADgY0AMIGJAoGBALCWLDa2ItYJ6su80hd1gL4cggQYdyyKK17btt/aS6Q/e DsKjsPyFIODsxeKVV/uA3wLT4jQJM5euKJXkDajzGGOy92+ypfzTX4zDJMkh61SZwlHNJ xBKilAM5aW7C+BQ0RvCxvdYtzx2LTdB+X/KMEBA7uIYxLfXH2Mnub3WIh1AgMBAAEwDQY JKoZIhvcNAQEFBQADgYEAe875m84sYUJ8qPeZ+NG7REgTvlHTmoCdoByU0LBBLotUKuqf rnRuXJRMeZXaaEGmzY1kLonVjQGzjAkU4dJ+RPmiDlYuHLZS41Pg6VMwY+03lhk6I5A/w 4rnqdkmwZX/NgXg06alnc2pBsXWhL4O7nk0S2ZrLMsQZ6HcsXgdmHo= TokenVendorAcme 987654321 Example-Issuer hJ+fvpoMPMO9BYpK2rdyQYGIxiATYHTHC7e/sPLKYo5/r1v+4 xTYG3gJolCWuVMydJ7Ta0GaiBPHcWa8ctCVYmHKfSz5fdeV5nqbZApe6dofTqhRwZK6 Yx4ufevi91cjN2vBpSxYafvN3c3+xIgk0EnTV4iVPRCR0rBwyfFrPc4= 0 python-pskc-1.3/tests/rfc6030/figure9.pskcxml0000644000000000000000000000446213300352662021002 0ustar rootroot00000000000000 TokenVendorAcme 0755225266 Example-Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 0 j6lwx3rvEPO0vKtMup4NbeVu8nk= j6lwx3rvEPO0vKtMup4NbeVu8nk= CN=Example.com,C=US 12345678 python-pskc-1.3/tests/test_actividentity.doctest0000644000000000000000000000351113117007614022242 0ustar rootroot00000000000000test_actividentity.doctest - tests for ActivIdentity PSKC files Copyright (C) 2017 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex, b2a_hex >>> def tostr(x): ... return str(x.decode()) >>> def decode(f): ... return lambda x: tostr(f(x)) >>> b2a_hex = decode(b2a_hex) >>> from pskc import PSKC This tests an ActivIdentity PSKC file that was found in the wild. It does not follow the RFC 6030 spec but is handled nonetheless. >>> pskc = PSKC('tests/actividentity/test.pskcxml') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' >>> b2a_hex(pskc.encryption.iv) '5eeb3496c73eac92e2d2773f00d1345e' >>> pskc.encryption.key = a2b_hex('fe0de6b806c09b762c4b49a666a27b72') >>> pskc.mac.algorithm 'http://www.w3.org/2000/09/xmldsig#hmac-sha1' >>> key = pskc.keys[0] >>> key.manufacturer 'ActivIdentity' >>> key.serial '0950380269' >>> key.algorithm 'http://www.ietf.org/keyprov/pskc#hotp' >>> key.id '0950380269' >>> key.issuer 'ActivIdentity' >>> key.policy.key_usage ['OTP'] >>> key.response_encoding 'DECIMAL' >>> key.response_length 8 >>> b2a_hex(key.secret) 'dce70c2a0c1f5806f316ca8d09456eb4765ad053' >>> key.counter 837830147 python-pskc-1.3/tests/test_aeskw.doctest0000644000000000000000000001760113300353250020473 0ustar rootroot00000000000000test_keywrap.doctest - test keywrap functions Copyright (C) 2014-2017 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> import struct >>> from binascii import a2b_hex >>> from pskc.crypto.aeskw import wrap, unwrap Wrap 128 bits of Key Data with a 128-bit KEK (test vector 4.1 from RFC 3394). >>> key = a2b_hex('000102030405060708090A0B0C0D0E0F') >>> plaintext = a2b_hex('00112233445566778899AABBCCDDEEFF') >>> ciphertext = a2b_hex('1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5') >>> wrap(plaintext, key) == ciphertext True >>> unwrap(ciphertext, key) == plaintext True Wrap 128 bits of Key Data with a 192-bit KEK (test vector 4.2 from RFC 3394). >>> key = a2b_hex('000102030405060708090A0B0C0D0E0F1011121314151617') >>> plaintext = a2b_hex('00112233445566778899AABBCCDDEEFF') >>> ciphertext = a2b_hex('96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D') >>> wrap(plaintext, key) == ciphertext True >>> unwrap(ciphertext, key) == plaintext True Wrap 128 bits of Key Data with a 256-bit KEK (test vector 4.3 from RFC 3394). >>> key = a2b_hex('000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F') >>> plaintext = a2b_hex('00112233445566778899AABBCCDDEEFF') >>> ciphertext = a2b_hex('64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7') >>> wrap(plaintext, key) == ciphertext True >>> unwrap(ciphertext, key) == plaintext True Wrap 192 bits of Key Data with a 192-bit KEK (test vector 4.4 from RFC 3394). >>> key = a2b_hex('000102030405060708090A0B0C0D0E0F1011121314151617') >>> plaintext = a2b_hex('00112233445566778899AABBCCDDEEFF0001020304050607') >>> ciphertext = a2b_hex('031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2') >>> wrap(plaintext, key) == ciphertext True >>> unwrap(ciphertext, key) == plaintext True Wrap 192 bits of Key Data with a 256-bit KEK (test vector 4.5 from RFC 3394). >>> key = a2b_hex('000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F') >>> plaintext = a2b_hex('00112233445566778899AABBCCDDEEFF0001020304050607') >>> ciphertext = a2b_hex('A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1') >>> wrap(plaintext, key) == ciphertext True >>> unwrap(ciphertext, key) == plaintext True Wrap 256 bits of Key Data with a 256-bit KEK (test vector 4.6 from RFC 3394). >>> key = a2b_hex('000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F') >>> plaintext = a2b_hex('00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F') >>> ciphertext = a2b_hex('28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21') >>> wrap(plaintext, key) == ciphertext True >>> unwrap(ciphertext, key) == plaintext True Mangling the ciphertext and unwrapping results in an exception: >>> unwrap(b'XX' + ciphertext[2:], key) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: IV does not match >>> unwrap(ciphertext[:-2] + b'XX', key) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: IV does not match >>> unwrap(ciphertext[2:], key) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: Ciphertext length wrong Wrap 20 octets with a 192-bit key (first example from section 6 of RFC 5649). >>> key = a2b_hex('5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8') >>> plaintext = a2b_hex('c37b7e6492584340bed12207808941155068f738') >>> ciphertext = a2b_hex('138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a') >>> wrap(plaintext, key) == ciphertext True >>> unwrap(ciphertext, key) == plaintext True >>> wrap(plaintext, key, pad=False) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... EncryptionError: Plaintext length wrong >>> unwrap(ciphertext, key, pad=False) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: IV does not match Wrap 7 octets with a 192-bit key (second example from section 6 of RFC 5649). >>> key = a2b_hex('5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8') >>> plaintext = a2b_hex('466f7250617369') >>> ciphertext = a2b_hex('afbeb0f07dfbf5419200f2ccb50bb24f') >>> wrap(plaintext, key) == ciphertext True >>> unwrap(ciphertext, key) == plaintext True >>> wrap(plaintext, key, pad=False) # disable padding Traceback (most recent call last): ... EncryptionError: Plaintext length wrong >>> unwrap(ciphertext, key, pad=False) Traceback (most recent call last): ... DecryptionError: Ciphertext length wrong Normally padding is only done if needed but it can be forced. >>> key = a2b_hex('000102030405060708090A0B0C0D0E0F') >>> plaintext = a2b_hex('00112233445566778899AABBCCDDEEFF') >>> ciphertext = a2b_hex('1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5') >>> wrap(plaintext, key) == ciphertext True >>> unwrap(ciphertext, key) == plaintext True >>> ciphertext = a2b_hex('2cef0c9e30de26016c230cb78bc60d51b1fe083ba0c79cd5') >>> wrap(plaintext, key, pad=True) == ciphertext True >>> unwrap(ciphertext, key) == plaintext True >>> unwrap(ciphertext, key, pad=False) # disabling padding fails IV check Traceback (most recent call last): ... DecryptionError: IV does not match Padding can also be disabled. This also disables the shortcut for small plaintexts as described in RFC 5649. >>> key = a2b_hex('000102030405060708090A0B0C0D0E0F') >>> plaintext = a2b_hex('0011223344556677') >>> ciphertext = a2b_hex('f4740052e82a225174ce86fbd7b805e7') >>> wrap(plaintext, key) == ciphertext True >>> unwrap(ciphertext, key) == plaintext True >>> wrap(plaintext, key, pad=False) # disable padding Traceback (most recent call last): ... EncryptionError: Plaintext length wrong >>> unwrap(ciphertext, key, pad=False) Traceback (most recent call last): ... DecryptionError: Ciphertext length wrong Lastly, an explicit IV can be set but this disables the padding functionality. >>> key = a2b_hex('000102030405060708090A0B0C0D0E0F') >>> plaintext = a2b_hex('0011223344556677') >>> iv = a2b_hex('1010101010101010') >>> wrap(plaintext, key, iv) Traceback (most recent call last): ... EncryptionError: Plaintext length wrong >>> plaintext = a2b_hex('00112233445566778899AABBCCDDEEFF') >>> ciphertext = a2b_hex('4cd926c570e19c35ace71d59a1062dae850e6a709066e0bf') >>> wrap(plaintext, key, iv) == ciphertext True >>> unwrap(ciphertext, key, iv) == plaintext True >>> unwrap(ciphertext, key, a2b_hex('2020202020202020')) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: IV does not match We can fake padding by specifying an RFC 5649 IV ourselves. The length of 14 works because we have padded the plaintext with two 0 bytes. >>> key = a2b_hex('5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8') >>> plaintext = a2b_hex('c37b7e6492584340bed1220765460000') >>> iv = a2b_hex('a65959a6') + struct.pack('>I', 14) >>> ciphertext = wrap(plaintext, key, iv=iv) >>> unwrap(ciphertext, key, iv=iv) == plaintext True >>> unwrap(ciphertext, key) == plaintext[:14] True If we mangle the IV to look like an RFC 5649 value but with an invalid padding length we should get an exception. >>> iv = a2b_hex('a65959a6') + struct.pack('>I', 12) >>> ciphertext = wrap(plaintext, key, iv=iv) >>> unwrap(ciphertext, key, iv=iv) == plaintext True >>> unwrap(ciphertext, key) == plaintext[:12] Traceback (most recent call last): ... DecryptionError: IV does not match python-pskc-1.3/tests/test_csv2pskc.doctest0000644000000000000000000004665313300356475021144 0ustar rootroot00000000000000test_csv2pskc.doctest - tests for the csv2pskc script Copyright (C) 2018 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex >>> import getpass >>> import shlex >>> import sys >>> import tempfile >>> from pskc import PSKC >>> from pskc.scripts.csv2pskc import main Sadly we cannot test --help and --version properly because argparse calls exit(0) which doctest does not like. >>> sys.argv = shlex.split('csv2pskc --help') >>> main() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... SystemExit: 0 >>> sys.argv = shlex.split('csv2pskc --version') >>> main() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... SystemExit: 0 We can output a PSKC file with some simple data from a CSV file. The columns in the CSV file refer to names of PSKC properties. >>> f = tempfile.NamedTemporaryFile('w+t') >>> x = f.write(''' ... serial,secret,algorithm,response_length,time_interval ... 987654321,3132333435363738393031323334353637383930,urn:ietf:params:xml:ns:keyprov:pskc:hotp,8, ... 987654321,31323334,urn:ietf:params:xml:ns:keyprov:pskc:pin,4, ... '''.lstrip()) >>> f.flush() >>> sys.argv = ['csv2pskc', f.name] >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE 987654321 MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 987654321 MTIzNA== We can also save the output to a file. >>> f = tempfile.NamedTemporaryFile('w+t') >>> x = f.write(''' ... serial,secret,algorithm,response_length,time_interval ... 987654321,3132333435363738393031323334353637383930,urn:ietf:params:xml:ns:keyprov:pskc:hotp,8, ... 987654321,31323334,urn:ietf:params:xml:ns:keyprov:pskc:pin,4, ... '''.lstrip()) >>> f2 = tempfile.NamedTemporaryFile() >>> f.flush() >>> sys.argv = ['csv2pskc', f.name, '--output', f2.name] >>> main() >>> with open(f2.name, 'r') as r: ... x = sys.stdout.write(r.read()) #doctest: +ELLIPSIS +REPORT_UDIFF +NORMALIZE_WHITESPACE ... MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= ... MTIzNA== A bigger example. >>> f = tempfile.NamedTemporaryFile('w+t') >>> x = f.write(''' ... serial,secret,algorithm,response_length,response_encoding,manufacturer,issuer,policy.start_date,policy.expiry_date,id,counter ... 654321,3132333435363738393031323334353637383930,urn:ietf:params:xml:ns:keyprov:pskc:hotp,8,DECIMAL,TokenVendorAcme,Issuer,2006-05-01 00:00:00+00:00,2006-05-31 00:00:00+00:00,1,2 ... 123456,3132333435363738393031323334353637383930,urn:ietf:params:xml:ns:keyprov:pskc:hotp,8,DECIMAL,TokenVendorAcme,Issuer,2006-05-01 00:00:00+00:00,2006-05-31 00:00:00+00:00,2,3 ... 9999999,3132333435363738393031323334353637383930,urn:ietf:params:xml:ns:keyprov:pskc:hotp,8,DECIMAL,TokenVendorAcme,Issuer,2006-03-01 00:00:00+00:00,2006-03-31 00:00:00+00:00,3,42 ... 9999999,3132333435363738393031323334353637383930,urn:ietf:params:xml:ns:keyprov:pskc:hotp,8,DECIMAL,TokenVendorAcme,Issuer,2006-04-01 00:00:00+00:00,2006-04-30 00:00:00+00:00,4,12 ... '''.lstrip()) >>> f.flush() >>> sys.argv = ['csv2pskc', f.name] >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE TokenVendorAcme 654321 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 2 2006-05-01T00:00:00Z 2006-05-31T00:00:00Z TokenVendorAcme 123456 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 3 2006-05-01T00:00:00Z 2006-05-31T00:00:00Z TokenVendorAcme 9999999 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 42 2006-03-01T00:00:00Z 2006-03-31T00:00:00Z TokenVendorAcme 9999999 Issuer MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 12 2006-04-01T00:00:00Z 2006-04-30T00:00:00Z As long as it is in a file (does not work on stdin) the script should also automatically pick up tab-separated files. >>> f = tempfile.NamedTemporaryFile('w+t') >>> x = f.write(''' ... id\tsecret\tcounter ... 654321\t3132333435363738393031323334353637383930\t2 ... 123456\t3132333435363738393031323334353637383930\t3 ... 9999999\t3132333435363738393031323334353637383930\t42 ... 9999999\t3132333435363738393031323334353637383930\t12 ... '''.lstrip()) >>> f.flush() >>> sys.argv = ['csv2pskc', f.name] >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 2 MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 3 MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 42 MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 12 We can use the --columns option to override using the first row to specify the key properties. >>> f = tempfile.NamedTemporaryFile('w+t') >>> x = f.write(''' ... nr,key,start date,info ... 121232,6848464354638468468835346896846846846846,2017-04-01,something ... 213422,9843138168168196616849849634548496832446,2017-02-12,else ... '''.lstrip()) >>> f.flush() >>> sys.argv = ['csv2pskc', f.name, '--columns', 'id+serial,secret,start_date,-'] >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE 121232 2017-04-01T00:00:00 aEhGQ1RjhGhGiDU0aJaEaEaEaEY= 213422 2017-02-12T00:00:00 mEMTgWgWgZZhaEmEljRUhJaDJEY= Alternatively, we can provide a mapping for column names found in the CSV file to key properties. >>> f = tempfile.NamedTemporaryFile('w+t') >>> x = f.write(''' ... nr,key,start date,info ... 121232,6848464354638468468835346896846846846846,2017-04-01,something ... 213422,9843138168168196616849849634548496832446,2017-02-12,else ... '''.lstrip()) >>> f.flush() >>> sys.argv = ['csv2pskc', f.name, '--columns', 'key:secret,nr:id+serial,info:-'] >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE 121232 2017-04-01T00:00:00 aEhGQ1RjhGhGiDU0aJaEaEaEaEY= 213422 2017-02-12T00:00:00 mEMTgWgWgZZhaEmEljRUhJaDJEY= We can also set global key properties with the --set option to apply values to all keys in the PSKC file: >>> f = tempfile.NamedTemporaryFile('w+t') >>> x = f.write(''' ... id+serial,secret ... 987654321,7c613e9c2194ff7da7f4770ab2ed712111fcbe95 ... 987654322,4be618e3459e936137994854bc3d2ebe46f3cce2 ... '''.lstrip()) >>> f.flush() >>> sys.argv = [ ... 'csv2pskc', f.name, ... '--set', 'manufacturer=TokenVendor', '--set', 'issuer=TokenIssuer'] >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE TokenVendor 987654321 TokenIssuer fGE+nCGU/32n9HcKsu1xIRH8vpU= TokenVendor 987654322 TokenIssuer S+YY40Wek2E3mUhUvD0uvkbzzOI= The --skip-rows option can be used to either not use the first row to denote the key properties that are set (in which case the --columns option is mandatory) or skip more rows at the beginning of the file. >>> f = tempfile.NamedTemporaryFile('w+t') >>> x = f.write(''' ... 987654321,7c613e9c2194ff7da7f4770ab2ed712111fcbe95 ... 987654322,4be618e3459e936137994854bc3d2ebe46f3cce2 ... '''.lstrip()) >>> f.flush() >>> sys.argv = ['csv2pskc', f.name, '--skip-rows=0', '--columns=id+serial,secret'] >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF ... ... fGE+nCGU/32n9HcKsu1xIRH8vpU= ... ... S+YY40Wek2E3mUhUvD0uvkbzzOI= ... >>> f = tempfile.NamedTemporaryFile('w+t') >>> x = f.write(''' ... id+serial,secret ... IGNORED LINE ... 987654321,7c613e9c2194ff7da7f4770ab2ed712111fcbe95 ... 987654322,4be618e3459e936137994854bc3d2ebe46f3cce2 ... '''.lstrip()) >>> f.flush() >>> sys.argv = ['csv2pskc', f.name, '--skip-rows=2'] >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF ... ... fGE+nCGU/32n9HcKsu1xIRH8vpU= ... ... S+YY40Wek2E3mUhUvD0uvkbzzOI= ... We can encrypt the resulting PSKC file with a passphrase. >>> f = tempfile.NamedTemporaryFile('w+t') >>> x = f.write(''' ... id,secret ... 987654321,7c613e9c2194ff7da7f4770ab2ed712111fcbe95 ... 987654322,4be618e3459e936137994854bc3d2ebe46f3cce2 ... '''.lstrip()) >>> f.flush() >>> sys.argv = ['csv2pskc', f.name, '--passwd', 'supersecure'] >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF ... ... 16 ... ... ... ... ... ... ... ... ... Alternatively we can switch from passphrase-based encryption to key-based encryption. >>> f = tempfile.NamedTemporaryFile('w+t') >>> x = f.write(''' ... id,secret ... 987654321,7c613e9c2194ff7da7f4770ab2ed712111fcbe95 ... 987654322,4be618e3459e936137994854bc3d2ebe46f3cce2 ... '''.lstrip()) >>> f.flush() >>> sys.argv = ['csv2pskc', f.name, '--secret', '12345678901234567890123456789012'] >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF ... ... ... ... ... ... ... ... ... python-pskc-1.3/tests/test_draft_hoyer_keyprov_portable_symmetric_key_container.doctest0000644000000000000000000001151713300352662032352 0ustar rootroot00000000000000test_draft_hoyer_keyprov_portable_symmetric_key_container.doctest - tests for examples from various versions of draft-hoyer-keyprov-portable-symmetric-key-container Copyright (C) 2017 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex, b2a_hex >>> def tostr(x): ... return str(x.decode()) >>> def decode(f): ... return lambda x: tostr(f(x)) >>> b2a_hex = decode(b2a_hex) >>> from pskc import PSKC This reads a simple PSKC file with a single non-encrypted HOTP secret key example from section 10.1 of draft-hoyer-keyprov-portable-symmetric-key-container-00. >>> pskc = PSKC('tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/non-encrypted.pskcxml') >>> pskc.encryption.algorithm is None True >>> key = pskc.keys[0] >>> key.manufacturer 'Token Manufacturer' >>> key.serial '98765432187' >>> key.id '98765432187' >>> key.algorithm 'HOTP' >>> key.expiry_date datetime.datetime(2008, 1, 1, 0, 0) >>> key.issuer 'Credential Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 6 >>> key.friendly_name 'MyFirstToken' >>> b2a_hex(key.secret) '5a57634c7670466f584e4841472b6c78332b5277' >>> key.counter 12 This reads a simple PSKC file with a single password-based encrypted HOTP secret key example from section 10.2 of draft-hoyer-keyprov-portable-symmetric-key-container-00. >>> pskc = PSKC('tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/password-encrypted.pskcxml') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' >>> pskc.encryption.derivation.algorithm 'http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2' >>> pskc.encryption.derivation.pbkdf2_iterations 999 >>> b2a_hex(pskc.encryption.derivation.pbkdf2_salt) 'cba4f372478b450c' >>> pskc.encryption.derive_key('qwerty') >>> b2a_hex(pskc.encryption.key) '651f8b1cafafa433d8c46ec996b3a274' >>> pskc.mac.algorithm 'http://www.w3.org/2000/09/xmldsig#hmac-sha1' >>> key = pskc.keys[0] >>> key.manufacturer 'Token Manufacturer' >>> key.serial '98765432187' >>> key.id '77654321870' >>> key.algorithm 'HOTP' >>> key.expiry_date datetime.datetime(2008, 1, 1, 0, 0) >>> key.issuer 'Credential Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 6 >>> key.friendly_name 'MySecondToken' >>> b2a_hex(key.secret) '65670bbe91685cd1c01be971dfe470' >>> key.counter 100 This reads a simple PSKC file with a single non-encrypted HOTP secret key example from section 10.1 of draft-hoyer-keyprov-portable-symmetric-key-container-01. >>> pskc = PSKC('tests/draft-hoyer-keyprov-portable-symmetric-key-container-01/non-encrypted.pskcxml') >>> pskc.encryption.algorithm is None True >>> key = pskc.keys[0] >>> key.manufacturer 'Token Manufacturer' >>> key.serial '98765432187' >>> key.id '98765432187' >>> key.algorithm 'HOTP' >>> key.expiry_date datetime.datetime(2008, 1, 1, 0, 0) >>> key.issuer 'Credential Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 6 >>> key.friendly_name 'MyFirstToken' >>> b2a_hex(key.secret) '5a57634c7670466f584e4841472b6c78332b5277' >>> key.counter 42 This reads a simple PSKC file with a single password-based encrypted HOTP secret key example from section 10.2 of draft-hoyer-keyprov-portable-symmetric-key-container-01. >>> pskc = PSKC('tests/draft-hoyer-keyprov-portable-symmetric-key-container-01/password-encrypted.pskcxml') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' >>> pskc.encryption.derivation.algorithm 'http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2' >>> pskc.encryption.derivation.pbkdf2_iterations 999 >>> b2a_hex(pskc.encryption.derivation.pbkdf2_salt) 'cba4f372478b450c' >>> pskc.encryption.derive_key('qwerty') >>> b2a_hex(pskc.encryption.key) '651f8b1cafafa433d8c46ec996b3a274' >>> pskc.mac.algorithm 'http://www.w3.org/2000/09/xmldsig#hmac-sha1' >>> key = pskc.keys[0] >>> key.manufacturer 'Token Manufacturer' >>> key.serial '98765432187' >>> key.id '77654321870' >>> key.algorithm 'HOTP' >>> key.expiry_date datetime.datetime(2008, 1, 1, 0, 0) >>> key.issuer 'Credential Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 6 >>> key.friendly_name 'MySecondToken' >>> b2a_hex(key.secret) '65670bbe91685cd1c01be971dfe470' >>> key.counter 100 python-pskc-1.3/tests/test_draft_ietf_keyprov_pskc_02.doctest0000644000000000000000000002014613300352662024574 0ustar rootroot00000000000000test_draft_ietf_keyprov_pskc_02.doctest - test for examples from draft-ietf-keyprov-pskc-02 Copyright (C) 2016 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex, b2a_hex >>> def tostr(x): ... return str(x.decode()) >>> def decode(f): ... return lambda x: tostr(f(x)) >>> b2a_hex = decode(b2a_hex) >>> from pskc import PSKC This tests figure 2 from draft-ietf-keyprov-pskc-02. It is a basic key container example with a simple plain text secret key. >>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure2.pskcxml') >>> pskc.id 'exampleID1' >>> key = pskc.keys[0] >>> key.id '12345678' >>> key.manufacturer 'Manufacturer' >>> key.serial '987654321' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc#hotp' >>> key.issuer 'Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 8 >>> tostr(key.secret) '12345678901234567890' >>> key.counter 0 This tests figure 3 from draft-ietf-keyprov-pskc-02. It is a basic key container example with a non-encrypted HOTP secret key protected by a PIN with some extra policy elements. >>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure3.pskcxml') >>> pskc.id 'exampleID1' >>> device = pskc.devices[0] >>> device.manufacturer 'Manufacturer' >>> device.serial '987654321' >>> key = pskc.keys[0] >>> key.id '12345678' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc#hotp' >>> key.issuer 'Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 8 >>> tostr(key.secret) '12345678901234567890' >>> key.counter 0 >>> key.policy.pin_min_length 4 >>> key.policy.pin_max_length 4 >>> key.policy.pin_encoding 'DECIMAL' >>> key.policy.pin_usage 'Local' >>> key.policy.key_usage ['OTP'] >>> pinkey = key.policy.pin_key >>> pinkey == pskc.keys[1] True >>> pinkey.id '123456781' >>> pinkey.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc#pin' >>> pinkey.issuer 'Issuer' >>> pinkey.response_encoding 'DECIMAL' >>> pinkey.response_length 4 >>> tostr(pinkey.secret) '1234' This tests figure 4 from draft-ietf-keyprov-pskc-02. It is a basic key container example using a AES-128-CBC encrypted pre-shared secret key. >>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure4.pskcxml') >>> pskc.encryption.key_name 'Pre-shared-key' >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' >>> pskc.mac.algorithm 'http://www.w3.org/2000/09/xmldsig#hmac-sha1' >>> key = pskc.keys[0] >>> key.manufacturer 'Manufacturer' >>> key.serial '987654321' >>> key.id '12345678' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc#hotp' >>> key.issuer 'Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 8 >>> key.counter 0 >>> b2a_hex(key.secret) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: No key available >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> b2a_hex(key.secret) '3132333435363738393031323334353637383930' This tests figure 5 from draft-ietf-keyprov-pskc-02 which uses an encryption key that is derived from a passphrase. While the PSKC file contains a element, there is no MAC algorithm specified anywhere. >>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure5.pskcxml') >>> pskc.encryption.key_name 'Passphrase1' >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' >>> key = pskc.keys[0] >>> key.manufacturer 'TokenVendorAcme' >>> key.serial '987654321' >>> key.id '123456' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc#hotp' >>> key.issuer 'Example-Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 8 >>> tostr(key.secret) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: No key available >>> pskc.encryption.derive_key('qwerty') >>> b2a_hex(pskc.encryption.key) '651e63cd57008476af1ff6422cd02e41' >>> tostr(key.secret) '12345678901234567890' This tests figure 6 from draft-ietf-keyprov-pskc-02 which uses an encryption based on asymmetric keys. Note that python-pskc does not yet support asymmetric encryption so this test is really limited. >>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure6.pskcxml') >>> pskc.id 'KC0001' >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#rsa_1_5' >>> pskc.mac.algorithm 'http://www.w3.org/2000/09/xmldsig#hmac-sha1' >>> key = pskc.keys[0] >>> key.manufacturer 'TokenVendorAcme' >>> key.serial '987654321' >>> key.id 'MBK000000001' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc#hotp' >>> key.issuer 'Example-Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 6 >>> tostr(key.secret) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: No key available >>> key.counter 0 This tests figure 7 from draft-ietf-keyprov-pskc-02 which uses a reference to an external mechanism to construct a HOTP key. >>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure7.pskcxml') >>> pskc.id 'exampleID1' >>> key = pskc.keys[0] >>> key.manufacturer 'Manufacturer' >>> key.serial '987654321' >>> key.id '12345678' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc#hotp' >>> key.issuer 'Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 8 >>> key.key_profile 'keyProfile1' >>> key.key_reference 'MasterKeyLabel' >>> key.counter 0 >>> key.policy.key_usage ['OTP'] This tests figure 8 from draft-ietf-keyprov-pskc-02 which uses a a digital signature to sign the PSKC file. Note that this file does not appear to contain a valid signature and if it would it wouldn't have a valid signature anyway. >>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure8.pskcxml') >>> pskc.signature.is_signed True >>> pskc.signature.canonicalization_method 'http://www.w3.org/2001/10/xml-exc-c14n#' >>> pskc.signature.algorithm 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' >>> pskc.signature.digest_algorithm 'http://www.w3.org/2000/09/xmldsig#sha1' >>> pskc.signature.issuer 'CN=Example.com,C=US' >>> pskc.signature.serial '12345678' >>> key = pskc.keys[0] >>> key.manufacturer 'TokenVendorAcme' >>> key.serial '0755225266' >>> key.id '123' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc#hotp' >>> key.issuer 'Example-Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 6 >>> tostr(key.secret) '12345678901234567890' >>> key.counter 0 This tests figure 9 from draft-ietf-keyprov-pskc-02 which contains multiple devices and keys. >>> pskc = PSKC('tests/draft-ietf-keyprov-pskc-02/figure9.pskcxml') >>> [device.manufacturer for device in pskc.devices] ['TokenVendorAcme', 'TokenVendorAcme', 'TokenVendorAcme'] >>> [device.serial for device in pskc.devices] ['654321', '123456', '9999999'] >>> [len(device.keys) for device in pskc.devices] [1, 1, 2] >>> [key.id for key in pskc.keys] ['1', '2', '3', '4'] >>> all(key.algorithm.endswith('#hotp') for key in pskc.keys) True >>> [key.issuer for key in pskc.keys] ['Issuer', 'Issuer', 'Issuer', 'Issuer'] >>> [bool(key.secret) for key in pskc.keys] [True, True, True, True] >>> [key.counter for key in pskc.keys] [0, 0, 0, 0] >>> pskc.keys[0].policy.start_date datetime.datetime(2006, 5, 1, 0, 0, tzinfo=tzutc()) >>> pskc.keys[0].policy.expiry_date datetime.datetime(2006, 5, 31, 0, 0, tzinfo=tzutc()) >>> pskc.keys[2].policy.start_date datetime.datetime(2006, 3, 1, 0, 0, tzinfo=tzutc()) >>> pskc.keys[2].policy.expiry_date datetime.datetime(2006, 3, 31, 0, 0, tzinfo=tzutc()) >>> pskc.keys[3].policy.start_date datetime.datetime(2006, 4, 1, 0, 0, tzinfo=tzutc()) >>> pskc.keys[3].policy.expiry_date datetime.datetime(2006, 4, 30, 0, 0, tzinfo=tzutc()) python-pskc-1.3/tests/test_draft_keyprov.doctest0000644000000000000000000000752613300356757022264 0ustar rootroot00000000000000test_draft_keyprov.doctest - test for examples from draft-hoyer-keyprov-pskc-algorithm-profiles-01 Copyright (C) 2014-2015 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> def tostr(x): ... return str(x.decode()) >>> from pskc import PSKC This tests an OCRA (OATH Challenge Response Algorithm) key contained within a PSKC file as described in section 3 of draft-hoyer-keyprov-pskc-algorithm-profiles-01. >>> pskc = PSKC('tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/ocra.pskcxml') >>> pskc.version '1.0' >>> key = pskc.keys[0] >>> key.manufacturer 'TokenVendorAcme' >>> key.serial '987654322' >>> key.id '12345678' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc#OCRA-1:HOTP-SHA512-8:C-QN08' >>> key.issuer 'Issuer' >>> key.challenge_encoding 'DECIMAL' >>> key.challenge_min_length 8 >>> key.challenge_max_length 8 >>> key.response_encoding 'DECIMAL' >>> key.response_length 8 >>> tostr(key.secret) '12345678901234567890' >>> key.counter 0 >>> key.policy.key_usage ['CR'] This tests an TOTP (OATH Time based OTP) key contained within a PSKC file as described in section 4 of draft-hoyer-keyprov-pskc-algorithm-profiles-01. >>> pskc = PSKC('tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/totp.pskcxml') >>> pskc.version '1.0' >>> key = pskc.keys[0] >>> key.manufacturer 'TokenVendorAcme' >>> key.serial '987654323' >>> key.id '987654323' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc#totp' >>> key.issuer 'Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 6 >>> tostr(key.secret) '12345678901234567890' >>> key.time_offset 0 >>> key.time_interval 30 >>> key.time_drift 4 >>> key.policy.key_usage ['OTP'] This tests an SecurID-AES-Counter key contained within a PSKC file as described in section 6 of draft-hoyer-keyprov-pskc-algorithm-profiles-01. >>> pskc = PSKC('tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/securid-aes-counter.pskcxml') >>> pskc.version '1.0' >>> key = pskc.keys[0] >>> key.manufacturer 'RSA, The Security Division of EMC' >>> key.serial '123456798' >>> key.id '23456789' >>> key.algorithm 'http://www.rsa.com/names/2008/04/algorithms/SecurID/SecurID-AES128-Counter' >>> key.issuer 'Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 6 >>> tostr(key.secret) '12345678901234567890' >>> key.counter 0 >>> key.policy.key_usage ['OTP'] >>> key.policy.start_date datetime.datetime(2006, 4, 14, 0, 0, tzinfo=tzutc()) >>> key.policy.expiry_date datetime.datetime(2010, 9, 30, 0, 0, tzinfo=tzutc()) This tests an ActivIdentity-3DES key contained within a PSKC file as described in section 8 of draft-hoyer-keyprov-pskc-algorithm-profiles-01. >>> pskc = PSKC('tests/draft-hoyer-keyprov-pskc-algorithm-profiles-01/actividentity-3des.pskcxml') >>> pskc.version '1.0' >>> key = pskc.keys[0] >>> key.manufacturer 'ActivIdentity' >>> key.serial '34567890' >>> key.id '12345677' >>> key.algorithm 'http://www.actividentity.com/2008/04/algorithms/algorithms#ActivIdentity-3DES' >>> key.issuer 'Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 8 >>> tostr(key.secret) '12345678901234567890' >>> key.counter 0 >>> key.time_offset 0 >>> key.time_interval 32 >>> key.time_drift 0 >>> key.policy.key_usage ['OTP'] python-pskc-1.3/tests/test_encryption.doctest0000644000000000000000000003004213300356475021561 0ustar rootroot00000000000000test_encryption.doctest - test various encryption schemes Copyright (C) 2014-2018 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex, b2a_hex >>> def tostr(x): ... return str(x.decode()) >>> def decode(f): ... return lambda x: tostr(f(x)) >>> b2a_hex = decode(b2a_hex) >>> import base64 >>> from pskc import PSKC >>> from pskc.encryption import encrypt, decrypt >>> pskc = PSKC('tests/encryption/aes128-cbc.pskcxml') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' >>> tostr(pskc.keys[0].secret) '12345678901234567890' >>> pskc.mac.algorithm 'http://www.w3.org/2001/04/xmldsig-more#hmac-sha224' >>> tostr(pskc.mac.key) 'MacMacMacMacMacMacMa' >>> pskc = PSKC('tests/encryption/aes192-cbc.pskcxml') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes192-cbc' >>> pskc.keys[0].secret # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: Invalid key length >>> pskc.encryption.key = a2b_hex('123456789012345678901234567890123456789012345678') >>> tostr(pskc.keys[0].secret) '12345678901234567890' >>> pskc.mac.algorithm 'http://www.w3.org/2001/04/xmldsig-more#hmac-sha256' >>> tostr(pskc.mac.key) 'MacMacMacMacMacMacMa' >>> pskc = PSKC('tests/encryption/aes256-cbc.pskcxml') >>> pskc.encryption.key = a2b_hex('1234567890123456789012345678901234567890123456789012345678901234') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' >>> tostr(pskc.keys[0].secret) '12345678901234567890' >>> pskc.mac.algorithm 'http://www.w3.org/2001/04/xmldsig-more#hmac-sha384' >>> tostr(pskc.mac.key) 'MacMacMacMacMacMacMa' >>> pskc = PSKC('tests/encryption/tripledes-cbc.pskcxml') >>> pskc.encryption.key = a2b_hex('1234') >>> pskc.keys[0].secret # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: Invalid key length >>> pskc.encryption.key = a2b_hex('11111111111111111111111111111111') >>> pskc.keys[0].secret # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: Invalid padding >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> tostr(pskc.keys[0].secret) '12345678901234567890' >>> pskc.mac.algorithm 'http://www.w3.org/2001/04/xmldsig-more#hmac-sha512' >>> tostr(pskc.mac.key) 'MacMacMacMacMacMacMa' >>> pskc = PSKC('tests/encryption/kw-aes128.pskcxml') >>> pskc.encryption.key = a2b_hex('1234') >>> pskc.keys[0].secret # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: Invalid key length >>> pskc.encryption.key = a2b_hex('000102030405060708090a0b0c0d0e0f') >>> b2a_hex(pskc.keys[0].secret) '00112233445566778899aabbccddeeff' >>> pskc = PSKC('tests/encryption/kw-aes192.pskcxml') >>> pskc.encryption.key = a2b_hex('000102030405060708090a0b0c0d0e0f') >>> pskc.keys[0].secret # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: Invalid key length >>> pskc.encryption.key = a2b_hex('000102030405060708090a0b0c0d0e0f1011121314151617') >>> b2a_hex(pskc.keys[0].secret) '00112233445566778899aabbccddeeff' >>> pskc = PSKC('tests/encryption/kw-aes256.pskcxml') >>> pskc.encryption.key = a2b_hex('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f') >>> b2a_hex(pskc.keys[0].secret) '00112233445566778899aabbccddeeff0001020304050607' >>> pskc = PSKC('tests/encryption/kw-tripledes.pskcxml') >>> pskc.encryption.key = a2b_hex('255e0d1c07b646dfb3134cc843ba8aa71f') >>> pskc.keys[0].secret # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: Invalid key length >>> pskc.encryption.key = a2b_hex('255e0d1c07b646dfb3134cc843ba8aa71f025b7c0838251f') >>> b2a_hex(pskc.keys[0].secret) '2923bf85e06dd6ae529149f1f1bae9eab3a7da3d860d3e98' >>> pskc = PSKC('tests/encryption/camellia128-cbc.pskcxml') >>> pskc.encryption.key = a2b_hex('200497e673a6fae2256e9749468a67ac') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmldsig-more#camellia128-cbc' >>> tostr(pskc.keys[0].secret) '12345678901234567890' >>> tostr(base64.b64encode(encrypt(pskc.encryption.algorithm, pskc.encryption.key, ... pskc.keys[0].secret, a2b_hex('0efb65152f3f41995edb11bc3e37c0f0')))) 'DvtlFS8/QZle2xG8PjfA8Kg4bsjLlU8kH/sEfXC9VLWib2Z/WU8RDHR+fI9uCqOs' >>> pskc = PSKC('tests/encryption/camellia192-cbc.pskcxml') >>> pskc.encryption.key = a2b_hex('e263279877384c84c987661a9d06766affdb9b3211eae801') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmldsig-more#camellia192-cbc' >>> tostr(pskc.keys[0].secret) '12345678901234567890' >>> tostr(base64.b64encode(encrypt(pskc.encryption.algorithm, pskc.encryption.key, ... pskc.keys[0].secret, a2b_hex('59AE4ACF5FC186EA94729432C3CA9148')))) 'Wa5Kz1/BhuqUcpQyw8qRSDwurIsm2vjUR/PO3w1Q3//PFfHod+DgBhRW2BecWpP5' >>> pskc = PSKC('tests/encryption/camellia256-cbc.pskcxml') >>> pskc.encryption.key = a2b_hex('33b37e31c5a0a16f004e7fe727d4ff808fc1f879d85ccd8f06dbb5799565d2f5') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmldsig-more#camellia256-cbc' >>> tostr(pskc.keys[0].secret) '12345678901234567890' >>> tostr(base64.b64encode(encrypt(pskc.encryption.algorithm, pskc.encryption.key, ... pskc.keys[0].secret, a2b_hex('5FB59EF9644400134F6B48E5B141D9A8')))) 'X7We+WREABNPa0jlsUHZqF5CWUQiPYdXJ+7ure96AcNH/7TXcQs4mFuSCOHpiv/W' >>> pskc = PSKC('tests/encryption/kw-camellia128.pskcxml') >>> pskc.encryption.key = a2b_hex('e35b135a09bfff8b314a5a0c32193c37') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmldsig-more#kw-camellia128' >>> tostr(pskc.keys[0].secret) '12345678901234567890' >>> tostr(base64.b64encode(encrypt(pskc.encryption.algorithm, pskc.encryption.key, pskc.keys[0].secret))) 'WB128TBZ1WGZzPNJNbwNrWRqQceU7M4FQSJPy2nw6iI=' >>> pskc = PSKC('tests/encryption/kw-camellia192.pskcxml') >>> pskc.encryption.key = a2b_hex('5eb0bccad29abe52d143d5aebc1c1ba174b8d379ce763c28') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmldsig-more#kw-camellia192' >>> tostr(pskc.keys[0].secret) '12345678901234567890' >>> tostr(base64.b64encode(encrypt(pskc.encryption.algorithm, pskc.encryption.key, pskc.keys[0].secret))) 'y/wSpn3aNjeXzY1giHLOy0P+WQ+NmkN7EovBtHBXZ14=' >>> pskc = PSKC('tests/encryption/kw-camellia256.pskcxml') >>> pskc.encryption.key = a2b_hex('0e187c656c36975b0d6bded79d7089142209457114ce8e6f4ae78339d71114e8') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmldsig-more#kw-camellia256' >>> tostr(pskc.keys[0].secret) '12345678901234567890' >>> tostr(base64.b64encode(encrypt(pskc.encryption.algorithm, pskc.encryption.key, pskc.keys[0].secret))) 'tCrhhD62tBGCcbsp8GV91+79MhaXTy1MP1SkaT2OLaU=' The IV can also be specified globally. >>> pskc = PSKC('tests/encryption/aes128-cbc-noiv.pskcxml') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> pskc.encryption.iv = a2b_hex('000102030405060708090a0b0c0d0e0f') >>> tostr(pskc.keys[0].secret) '12345678901234567890' >>> tostr(pskc.mac.key) 'MacMacMacMacMacMacMa' If the PSKC file does not have a MAC key configured, older versions of the PSKC format allowed using the encryption key for the HMAC function. >>> pskc = PSKC('tests/encryption/no-mac-key.pskcxml') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> b2a_hex(pskc.mac.key) '12345678901234567890123456789012' >>> tostr(pskc.keys[0].secret) '12345678901234567890' Older versions of the PSKC format allowed having the MAC go over the plaintext instead of the ciphertext. >>> pskc = PSKC('tests/encryption/mac-over-plaintext.pskcxml') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> tostr(pskc.keys[0].secret) '12345678901234567890' Test decryption with tripledes-cbc and a specified IV. >>> iv = a2b_hex('1010101010101010') >>> key = a2b_hex('12345678901234567890123456789012') >>> ciphertext = encrypt('#tripledes-cbc', key, b'FOOBAR', iv) >>> ciphertext = ciphertext[8:] # strip IV >>> tostr(decrypt('#tripledes-cbc', key, ciphertext, iv)) 'FOOBAR' >>> tostr(decrypt('#tripledes-cbc', key, iv + ciphertext)) 'FOOBAR' MAC key and algorithm will use useful defaults but can also be manually specified. >>> pskc = PSKC() >>> pskc.mac.setup() >>> pskc.mac.algorithm 'http://www.w3.org/2000/09/xmldsig#hmac-sha1' >>> len(pskc.mac.key) 20 >>> pskc.mac.setup(key=a2b_hex('548512684595'), algorithm='unknown') >>> pskc.mac.algorithm 'unknown' >>> len(pskc.mac.key) 6 >>> pskc.mac.algorithm_key_length # this is the default 16 >>> pskc.mac.algorithm = None >>> pskc.mac.key = None >>> pskc.mac.setup(algorithm='hmac-sha224') >>> pskc.mac.algorithm 'http://www.w3.org/2001/04/xmldsig-more#hmac-sha224' >>> pskc.mac.algorithm_key_length 28 >>> len(pskc.mac.key) 28 Test PBKDF2 key derivation set-up. Only specifying a passphrase picks reasonable defaults. >>> pskc = PSKC() >>> pskc.encryption.setup_pbkdf2('test') >>> pskc.encryption.derivation.algorithm 'http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2' >>> pskc.encryption.derivation.pbkdf2_iterations 100000 >>> len(pskc.encryption.derivation.pbkdf2_salt) 16 >>> pskc.encryption.derivation.pbkdf2_key_length 16 >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' >>> len(pskc.encryption.key) 16 The function will pick up an pre-specified values. If an encryption algorithm is defined (can also be passed) the key with the correct size will be generated. >>> pskc = PSKC() >>> pskc.encryption.algorithm = 'aes256-cbc' >>> pskc.encryption.derivation.pbkdf2_iterations = 15000 >>> pskc.encryption.setup_pbkdf2('test') >>> pskc.encryption.derivation.algorithm 'http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2' >>> pskc.encryption.derivation.pbkdf2_iterations 15000 >>> len(pskc.encryption.derivation.pbkdf2_salt) 16 >>> pskc.encryption.derivation.pbkdf2_key_length 32 >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' >>> len(pskc.encryption.key) 32 All properties can also be manually specified. >>> pskc = PSKC() >>> pskc.encryption.setup_pbkdf2( ... 'qwerty', iterations=1000, algorithm='aes256-cbc', key_length=24, ... salt=base64.b64decode('Ej7/PEpyEpw='), ... key_name='PBKDF2 passphrase', ... prf='hmac-md5') >>> pskc.encryption.derivation.algorithm 'http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2' >>> pskc.encryption.derivation.pbkdf2_iterations 1000 >>> b2a_hex(pskc.encryption.derivation.pbkdf2_salt) '123eff3c4a72129c' >>> pskc.encryption.derivation.pbkdf2_key_length 24 >>> pskc.encryption.derivation.pbkdf2_prf 'http://www.w3.org/2001/04/xmldsig-more#hmac-md5' >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes256-cbc' >>> pskc.encryption.key_name 'PBKDF2 passphrase' >>> b2a_hex(pskc.encryption.key) 'e8c5fecfb2a5cbb80ff791782ff5e125cc375bb6ba113071' We can also remove the encryption totally and end up with a PSKC structure that only contains decrypted values (note that the original encryption properties are lost in this case). >>> pskc = PSKC('tests/encryption/aes192-cbc.pskcxml') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes192-cbc' >>> pskc.encryption.key = a2b_hex('123456789012345678901234567890123456789012345678') >>> tostr(pskc.keys[0].secret) '12345678901234567890' >>> pskc.encryption.remove_encryption() >>> pskc.encryption.algorithm is None True >>> pskc.encryption.key is None True >>> pskc.mac.key is None True >>> tostr(pskc.keys[0].secret) '12345678901234567890' python-pskc-1.3/tests/test_feitian.doctest0000644000000000000000000000520713117007614021005 0ustar rootroot00000000000000test_feitian.doctest - tests for Feitian PSKC files Copyright (C) 2016 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex, b2a_hex >>> def tostr(x): ... return str(x.decode()) >>> def decode(f): ... return lambda x: tostr(f(x)) >>> b2a_hex = decode(b2a_hex) >>> from pskc import PSKC This tests a simple non-encrypted PSKC file from Feitian. The PSKC file contains two HOTP keys and four TOTP keys. >>> pskc = PSKC('tests/feitian/file1.pskcxml') >>> pskc.keys[0].manufacturer 'Feitian Technology Co.,Ltd' >>> print('\n'.join(key.serial for key in pskc.keys)) #doctest: +REPORT_UDIFF 1000133508267 1000133508255 2600124809778 2600124809787 2600135004012 2600135004013 >>> print('\n'.join(key.algorithm for key in pskc.keys)) #doctest: +REPORT_UDIFF urn:ietf:params:xml:ns:keyprov:pskc:hotp urn:ietf:params:xml:ns:keyprov:pskc:hotp urn:ietf:params:xml:ns:keyprov:pskc:totp urn:ietf:params:xml:ns:keyprov:pskc:totp urn:ietf:params:xml:ns:keyprov:pskc:totp urn:ietf:params:xml:ns:keyprov:pskc:totp >>> pskc.keys[5].time_interval 60 This tests a sample seed file originally provided by GOOZE for Feitian c100 / c200 hardware tokens. There is one TOTP key and one HTOP key in the file. >>> pskc = PSKC('tests/feitian/20120919-test001-4282.xml') >>> pskc.keys[0].manufacturer 'FeiTian Technology Co.,Ltd' >>> print('\n'.join(key.serial for key in pskc.keys)) 2600215704919 1000117803294 >>> key = pskc.keys[0] >>> key.algorithm, key.response_length, key.time_offset, key.time_interval ('urn:ietf:params:xml:ns:keyprov:pskc:totp', 6, 0, 60) >>> key.policy.start_date datetime.datetime(2012, 9, 19, 0, 0, tzinfo=tzutc()) >>> key.policy.expiry_date datetime.datetime(2022, 9, 1, 0, 0, tzinfo=tzutc()) >>> key = pskc.keys[1] >>> key.algorithm, key.response_length, key.counter ('urn:ietf:params:xml:ns:keyprov:pskc:hotp', 6, 0) >>> key.policy.start_date datetime.datetime(2012, 9, 19, 0, 0, tzinfo=tzutc()) >>> key.policy.expiry_date datetime.datetime(2022, 9, 1, 0, 0, tzinfo=tzutc()) python-pskc-1.3/tests/test_invalid.doctest0000644000000000000000000001547313221434301021013 0ustar rootroot00000000000000test_invalid.doctest - test for invalid PSKC file Copyright (C) 2014-2017 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex, b2a_hex >>> def tostr(x): ... return str(x.decode()) >>> def decode(f): ... return lambda x: tostr(f(x)) >>> b2a_hex = decode(b2a_hex) >>> from pskc import PSKC Load a number of invalid files. This file is plain invalid XML. >>> pskc = PSKC('tests/invalid/notxml.pskcxml') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... ParseError: Error parsing XML This XML file has a wrong top-level element. >>> pskc = PSKC('tests/invalid/wrongelement.pskcxml') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... ParseError: Missing KeyContainer This file has an unknown PSKC version. >>> pskc = PSKC('tests/invalid/wrongversion.pskcxml') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... ParseError: Unsupported version This PSKC file has a key with an unknown algorithm specified. >>> pskc = PSKC('tests/invalid/unknown-encryption.pskcxml') >>> key = pskc.keys[0] >>> key.id '12345678' >>> key.secret # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: No key available >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> key.secret # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: Unsupported algorithm: ... This PSKC file has a key without an algorithm specified. >>> pskc = PSKC('tests/invalid/missing-encryption.pskcxml') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> key = pskc.keys[0] >>> key.id '45678901' >>> b2a_hex(key.secret) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: No algorithm specified >>> pskc.encryption.algorithm = 'aes128-cbc' >>> b2a_hex(key.secret) '3132333435363738393031323334353637383930' This PSKC file has a key without an encryption method specified. >>> pskc = PSKC('tests/invalid/missing-encryption-method.pskcxml') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> key = pskc.keys[0] >>> key.id '45678901' >>> b2a_hex(key.secret) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: No algorithm specified >>> pskc.encryption.algorithm = 'aes128-cbc' >>> b2a_hex(key.secret) '3132333435363738393031323334353637383930' This PSKC file has an incomplete key derivation configuration. >>> pskc = PSKC('tests/invalid/incomplete-derivation.pskcxml') >>> pskc.encryption.derive_key('qwerty') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... KeyDerivationError: Incomplete PBKDF2 configuration Specify an unknown key derivation algorithm. >>> pskc = PSKC('tests/rfc6030/figure7.pskcxml') >>> pskc.encryption.derivation.algorithm = 'unknown' >>> pskc.encryption.derive_key('qwerty') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... KeyDerivationError: Unsupported algorithm: 'unknown' Figure 6 does use encryption but with a pre-shared key. Attempting key derivation with such a PSKC file should result in an exception. >>> pskc = PSKC('tests/rfc6030/figure6.pskcxml') >>> pskc.encryption.derive_key('qwerty') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... KeyDerivationError: No algorithm specified Specify an unknown PBKDF2 PRF (pseudorandom function). >>> pskc = PSKC('tests/rfc6030/figure7.pskcxml') >>> pskc.encryption.derivation.pbkdf2_prf = 'unknown' >>> pskc.encryption.derive_key('qwerty') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... KeyDerivationError: Pseudorandom function unsupported: 'unknown' Specify an unknown hash for the HMAC for the PBKDF2 PRF. >>> pskc = PSKC('tests/rfc6030/figure7.pskcxml') >>> pskc.encryption.derivation.pbkdf2_prf = 'hmac-unknown' >>> pskc.encryption.derive_key('qwerty') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... KeyDerivationError: Pseudorandom function unsupported: 'hmac-unknown' There is a ValueMAC element but no MACMethod element. >>> pskc = PSKC('tests/invalid/no-mac-method.pskcxml') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' >>> pskc.mac.algorithm is None True >>> key = pskc.keys[0] >>> key.id '12345678' >>> key.secret # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: No MAC algorithm set There is an unknown algorithm specified in MACMethod. >>> pskc = PSKC('tests/invalid/mac-algorithm.pskcxml') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> key = pskc.keys[0] >>> key.id '12345678' >>> key.secret # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: Unsupported MAC algorithm: ... The MAC value does not match the calculated MAC, something was modified in transit. >>> pskc = PSKC('tests/invalid/mac-value.pskcxml') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' >>> pskc.mac.algorithm 'http://www.w3.org/2000/09/xmldsig#hmac-sha1' >>> key = pskc.keys[0] >>> key.id '12345678' >>> key.secret # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: MAC value does not match >>> key.check() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: MAC value does not match Checks to see that invalid values are detected. >>> pskc = PSKC('tests/invalid/not-integer.pskcxml') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... ValueError: invalid literal for int() with base 10: 'TWELVE' >>> pskc = PSKC('tests/invalid/not-integer2.pskcxml') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... ValueError: invalid literal for int() with base 10: 'FOUR' >>> pskc = PSKC('tests/invalid/not-boolean.pskcxml') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... ValueError: invalid boolean value: 'not really' python-pskc-1.3/tests/test_mac.doctest0000644000000000000000000001065213221434050020120 0ustar rootroot00000000000000test_mac.doctest - test various mac schemes Copyright (C) 2017 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex, b2a_hex >>> def tostr(x): ... return str(x.decode()) >>> def decode(f): ... return lambda x: tostr(f(x)) >>> b2a_hex = decode(b2a_hex) >>> import hashlib >>> from pskc.mac import mac, mac_key_length The module supports a wide variety of HMAC algorithms. Some test cases from RFC 2202 for HMAC-MD5 and HMAC-SHA-1. >>> key = a2b_hex('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b') >>> b2a_hex(mac('HMAC-MD5', key, b'Hi There')) '9294727a3638bb1c13f48ef8158bfc9d' >>> b2a_hex(mac('HMAC-MD5', b'Jefe', b'what do ya want for nothing?')) '750c783e6ab0b503eaa86e310a5db738' >>> key = a2b_hex('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b') >>> b2a_hex(mac('HMAC-SHA-1', key, b'Hi There')) 'b617318655057264e28bc0b6fb378c8ef146be00' >>> b2a_hex(mac('HMAC-SHA-1', b'Jefe', b'what do ya want for nothing?')) 'effcdf6ae5eb2fa2d27416d5f184df9c259a7c79' Some test cases from RFC 4231 for HMAC-SHA-224, HMAC-SHA-256, HMAC-SHA-384 and HMAC-SHA-512. >>> key = a2b_hex('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b') >>> data = b'Hi There' >>> b2a_hex(mac('HMAC-SHA-224', key, data)) '896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22' >>> b2a_hex(mac('HMAC-SHA-256', key, data)) 'b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7' >>> b2a_hex(mac('HMAC-SHA-384', key, data)) 'afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6' >>> b2a_hex(mac('HMAC-SHA-512', key, data)) '87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854' >>> key = b'Jefe' >>> data = b'what do ya want for nothing?' >>> b2a_hex(mac('HMAC-SHA-224', key, data)) 'a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44' >>> b2a_hex(mac('HMAC-SHA-256', key, data)) '5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843' >>> b2a_hex(mac('HMAC-SHA-384', key, data)) 'af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649' >>> b2a_hex(mac('HMAC-SHA-512', key, data)) '164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737' Some test cases from RFC 2857 for HMAC-RIPEMD160 but not all versions of hashlib have RIPEMD-160. >>> hashlib_algorithms = ( ... getattr(hashlib, 'algorithms_available', None) or ... getattr(hashlib, 'algorithms', None)) >>> if 'ripemd160' in hashlib_algorithms: ... key = a2b_hex('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b') ... b2a_hex(mac('HMAC-RIPEMD160', key, b'Hi There')) == \ ... '24cb4bd67d20fc1a5d2ed7732dcc39377f0a5668' ... b2a_hex(mac('HMAC-RIPEMD160', b'Jefe', b'what do ya want for nothing?')) == \ ... 'dda6c0213a485a9e24f4742064a7f033b43c4069' ... else: ... True # some hashlib implementations ... True # do not have RIPEMD-160 True True Some recommended minimal key lengths for some algorithms. >>> mac_key_length('HMAC-MD5') 16 >>> mac_key_length('HMAC-SHA-1') 20 >>> mac_key_length('HMAC-SHA-224') 28 >>> mac_key_length('HMAC-SHA-256') 32 >>> mac_key_length('HMAC-SHA-384') 48 >>> mac_key_length('HMAC-SHA-512') 64 Unknown algorithms should raise an exception for the mac() function. >>> mac('unknown', b'FOO', b'BAR') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: Unsupported MAC algorithm: 'unknown' >>> mac('hmac-unknown', b'FOO', b'BAR') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: Unsupported MAC algorithm: 'hmac-unknown' The mac_key_length() function should return a default value for unknown MACs. >>> mac_key_length('unknown') 16 >>> mac_key_length('hmac-unknown') 16 python-pskc-1.3/tests/test_misc.doctest0000644000000000000000000001775314667352753020354 0ustar rootroot00000000000000test_misc.doctest - miscellaneous tests Copyright (C) 2014-2024 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> import warnings >>> from binascii import a2b_hex, b2a_hex >>> def tostr(x): ... return str(x.decode()) >>> def decode(f): ... return lambda x: tostr(f(x)) >>> b2a_hex = decode(b2a_hex) >>> import datetime >>> now = datetime.datetime(2016, 3, 23, 0, 0, 0) >>> import dateutil.tz >>> from pskc import PSKC This tests the most minimal valid PSKC file with one empty key. >>> try: ... from StringIO import StringIO ... except ImportError: ... from io import StringIO >>> minimal_pskc = StringIO(''' ... ... ... ... ... '''.strip()) >>> pskc = PSKC(minimal_pskc) >>> len(pskc.keys) 0 >>> len(pskc.devices) 1 Check creation of empty PSKC structure and adding an empty key to the list. >>> pskc = PSKC() >>> key = pskc.add_key(id='123') >>> key.id '123' >>> key.secret is None True We can also put device-specific properties in a device: >>> pskc = PSKC() >>> device = pskc.add_device(manufacturer='Tokens INC.') >>> len(pskc.keys) 0 >>> key = device.add_key(id='123', serial='456') >>> len(pskc.keys) 1 >>> key.id '123' >>> key.manufacturer 'Tokens INC.' >>> device.serial '456' Adding a key or device with unknown attributes raises an error. >>> key = pskc.add_key(foo='bar') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... AttributeError >>> device.add_key(foo='bar') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... AttributeError >>> device = pskc.add_device(foo='bar') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... AttributeError Setting secret, counter, etc. also works >>> key = pskc.add_key(secret='VERYSECRET') >>> key.counter = 10 >>> key.secret 'VERYSECRET' >>> key.counter 10 We can set policy properties in two ways: policy.property or policy__property: >>> data = { ... 'policy__start_date': datetime.datetime(2018, 3, 2, 10, 12, 16), ... 'policy.expiry_date': datetime.datetime(2018, 3, 3, 16, 37, 21), ... } >>> key = pskc.add_key(**data) >>> key.policy.start_date datetime.datetime(2018, 3, 2, 10, 12, 16) >>> key.policy.expiry_date datetime.datetime(2018, 3, 3, 16, 37, 21) Setting encryption key name and algorithm also works. >>> pskc.encryption.key_name = 'Test encryption key' >>> pskc.encryption.key_names ['Test encryption key'] >>> pskc.encryption.algorithm is None True >>> pskc.encryption.algorithm = 'aes128-cbc' >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' >>> pskc.encryption.algorithm_key_lengths [16] >>> pskc.encryption.algorithm = '3des-cbc' >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' >>> pskc.encryption.algorithm_key_lengths [16, 24] >>> pskc.encryption.algorithm = 'none' >>> pskc.encryption.algorithm is None True >>> pskc.encryption.algorithm_key_lengths # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: No algorithm specified Load an PSKC file with an odd namespace. >>> pskc = PSKC('tests/misc/odd-namespace.pskcxml') >>> pskc.version '1.0' >>> pskc.id 'exampleID1' >>> key = pskc.keys[0] >>> key.id '12345678' >>> key.issuer 'Issuer-A' >>> tostr(key.secret) '1234' Load a PSKC file that uses the xenc11 namespace for the PBKDF2 parameters. >>> pskc = PSKC('tests/misc/SampleFullyQualifiedNS.xml') >>> pskc.encryption.key_name 'PassPhrase' >>> pskc.encryption.derive_key('3FCA3158035072D6') >>> key = pskc.keys[0] >>> b2a_hex(key.secret) '09fbecfd0bf47910839e2eb05ffa10b95cd0390950ce32ab790583ed134171e0' >>> key.check() True Empty PSKC files should raise a useful exception when trying to derive an encryption key from a password. >>> pskc = PSKC() >>> pskc.encryption.derive_key('123456') # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... KeyDerivationError: No algorithm specified Integers can be represented in different ways in PSKC files. >>> pskc = PSKC('tests/misc/integers.pskcxml') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> [key.counter for key in pskc.keys] [831791, 709791, 405834, 298507, 961392] This tests key policy and unknown policy elements. The first key should have all known policy elements set while other keys should have extra unknown information added which should result in rejected usage. >>> pskc = PSKC('tests/misc/policy.pskcxml') >>> key = pskc.keys[0] >>> key.policy.start_date datetime.datetime(2006, 5, 1, 0, 0, tzinfo=tzutc()) >>> key.policy.expiry_date datetime.datetime(2026, 5, 31, 0, 0, tzinfo=tzutc()) >>> key.policy.number_of_transactions 4321 >>> key.policy.key_usage ['OTP'] >>> key.policy.unknown_policy_elements False >>> key.policy.may_use('OTP', datetime.datetime(2005, 4, 3, 0, 0, 0)) False >>> key.policy.may_use('OTP', now) True >>> key.policy.may_use('OTP', datetime.datetime(2028, 12, 31, 0, 0, 0)) False >>> key.policy.start_date = datetime.datetime.now() + datetime.timedelta(seconds=10) >>> key.policy.expiry_date = datetime.datetime.now() + datetime.timedelta(days=1) >>> key.policy.may_use() False >>> key.policy.start_date = datetime.datetime.now() - datetime.timedelta(seconds=10) >>> key.policy.may_use() True >>> key = pskc.keys[1] >>> key.policy.key_usage ['OTP'] >>> key.policy.unknown_policy_elements True >>> key.policy.may_use('OTP', now) False >>> key.policy.pin '1234' >>> key = pskc.keys[2] >>> key.policy.key_usage ['OTP'] >>> key.policy.unknown_policy_elements True >>> key.policy.may_use('OTP', now) False >>> key.policy.pin is None True >>> key = pskc.keys[3] >>> key.policy.key_usage ['OTP'] >>> key.policy.unknown_policy_elements True >>> key.policy.may_use('OTP', now) False >>> key.policy.pin is None True This checks the ChallengeFormat and ResponseFormat handling of keys and specifically the attribute indicating presence of check digits. >>> pskc = PSKC('tests/misc/checkdigits.pskcxml') >>> for key in pskc.keys: ... print('challenge %r %r %r %r' % ( ... key.challenge_encoding, key.challenge_min_length, ... key.challenge_max_length, key.challenge_check)) ... print('response %r %r %r' % ( ... key.response_encoding, key.response_length, ... key.response_check)) #doctest: +REPORT_UDIFF challenge 'DECIMAL' 12 34 True response 'DECIMAL' 8 False challenge 'DECIMAL' 56 78 False response 'DECIMAL' 9 True challenge 'DECIMAL' 16 87 False response 'DECIMAL' 3 True challenge 'HEXADECIMAL' 4 6 None response 'ALPHANUMERIC' 6 None This checks an PSKC file with a number of different empty sections that normally contain data. >>> pskc = PSKC('tests/misc/partialxml.pskcxml') >>> all(key.counter is None for key in pskc.keys) True A typo was fixed and the old name was provided via a deprecation wrapper property. >>> pskc = PSKC() >>> key = pskc.add_key() >>> with warnings.catch_warnings(record=True) as w: ... warnings.simplefilter('always') ... key.policy.pin_max_failed_attemtps = 10 ... assert len(w) == 1 ... assert issubclass(w[0].category, DeprecationWarning) >>> key.policy.pin_max_failed_attempts 10 >>> with warnings.catch_warnings(record=True) as w: ... warnings.simplefilter('always') ... key.policy.pin_max_failed_attemtps ... assert len(w) == 1 ... assert issubclass(w[0].category, DeprecationWarning) 10 python-pskc-1.3/tests/test_multiotp.doctest0000644000000000000000000000576413300352662021253 0ustar rootroot00000000000000test_multiotp.doctest - test for files from multiOTP Copyright (C) 2017 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex, b2a_hex >>> def tostr(x): ... return str(x.decode()) >>> def decode(f): ... return lambda x: tostr(f(x)) >>> b2a_hex = decode(b2a_hex) >>> from pskc import PSKC This tests some files that are shipped with the multiOTP PHP authentication solution, https://www.multiotp.net/ >>> pskc = PSKC('tests/multiotp/pskc-hotp-aes.txt') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> b2a_hex(pskc.keys[0].secret) '3132333435363738393031323334353637383930' >>> pskc = PSKC('tests/multiotp/pskc-hotp-pbe.txt') >>> pskc.encryption.derive_key('qwerty') >>> b2a_hex(pskc.keys[0].secret) '3031323334353637383930313233343536373839' >>> pskc = PSKC('tests/multiotp/pskc-totp-aes.txt') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> b2a_hex(pskc.keys[0].secret) '3132333435363738393031323334353637383930' >>> pskc = PSKC('tests/multiotp/pskc-totp-pbe.txt') >>> pskc.encryption.derive_key('qwerty') >>> b2a_hex(pskc.keys[0].secret) '3031323334353637383930313233343536373839' >>> pskc = PSKC('tests/multiotp/tokens_hotp_aes.pskc') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> b2a_hex(pskc.keys[0].secret) '91f0dc4e239977e6bcc273e4f5414a8a6cf6d62c6990f58b4914a2d588b3475f' >>> pskc = PSKC('tests/multiotp/tokens_hotp_pbe.pskc') >>> pskc.encryption.derive_key('qwerty') >>> b2a_hex(pskc.keys[0].secret) '5d3a38bf5476d6f0b897f1e62887cb3ce833a5b9' >>> pskc = PSKC('tests/multiotp/tokens_ocra_aes.pskc') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> b2a_hex(pskc.keys[0].secret) 'e65f2c66247355fda360acdf3390972c16a1a164' >>> pskc = PSKC('tests/multiotp/tokens_ocra_pbe.pskc') >>> pskc.encryption.derive_key('qwerty') >>> b2a_hex(pskc.keys[0].secret) '4f40e1c6a7436e84620b170ceddfe110083cbd6d' >>> pskc = PSKC('tests/multiotp/tokens_totp_aes.pskc') >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> b2a_hex(pskc.keys[0].secret) '38c2506a8e0708a5e929c2686b827e0ba7ae28c9de3c83e6d27308345981a3de' >>> pskc.keys[0].algorithm_suite 'HMAC-SHA256' >>> pskc = PSKC('tests/multiotp/tokens_totp_pbe.pskc') >>> pskc.encryption.derive_key('qwerty') >>> b2a_hex(pskc.keys[0].secret) '2c8792d34a3a8711b7cfc4304bcc84e3e67815a6' python-pskc-1.3/tests/test_nagraid.doctest0000644000000000000000000000337613117007614021000 0ustar rootroot00000000000000test_vendors.doctest - tests for NagraID PSKC files Copyright (C) 2016 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex, b2a_hex >>> def tostr(x): ... return str(x.decode()) >>> def decode(f): ... return lambda x: tostr(f(x)) >>> b2a_hex = decode(b2a_hex) >>> from pskc import PSKC This tests a simple PSKC file from NagraID which is protected by a pre-shared key. The file contains three OCRA keys. >>> pskc = PSKC('tests/nagraid/file1.pskcxml') >>> print('\n'.join(key.serial for key in pskc.keys)) #doctest: +REPORT_UDIFF 306EUO4-00960 306EUO4-00954 306EUO4-00958 >>> key = pskc.keys[0] >>> bool(key.secret) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: No key available >>> pskc.encryption.key_name 'Pre-shared-key' >>> pskc.encryption.key = a2b_hex('4A057F6AB6FCB57AB5408E46A9835E68') >>> bool(key.secret) True >>> key.check() True >>> print('\n'.join(key.algorithm_suite for key in pskc.keys)) #doctest: +REPORT_UDIFF OCRA-1:HOTP-SHA1-6:C-QN08-PSHA1 OCRA-1:HOTP-SHA1-6:C-QN08-PSHA1 OCRA-1:HOTP-SHA1-6:C-QN08-PSHA1 python-pskc-1.3/tests/test_pskc2csv.doctest0000644000000000000000000001651214524137447021140 0ustar rootroot00000000000000test_pskc2csv.doctest - tests for the pskc2csv script Copyright (C) 2017 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex >>> import getpass >>> import shlex >>> import sys >>> import tempfile >>> from pskc import PSKC >>> from pskc.scripts.pskc2csv import main Sadly we cannot test --help and --version properly because argparse calls exit(0) which doctest does not like. >>> sys.argv = shlex.split('pskc2csv --help') >>> main() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... SystemExit: 0 >>> sys.argv = shlex.split('pskc2csv --version') >>> main() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... SystemExit: 0 We hack stdin to lie about being a TTY to ensure the password prompt is always presented. >>> class TTYFile(object): ... def __init__(self, f): ... self.f = f ... ... def isatty(self): ... return True ... ... def __getattr__(self, attr): ... return getattr(self.f, attr) >>> sys.stdin = TTYFile(sys.stdin) >>> sys.stdin.isatty() True We can output a CSV file with some columns with the just default arguments. >>> sys.argv = shlex.split('pskc2csv tests/rfc6030/figure5.pskcxml') >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE serial,secret,algorithm,response_length,time_interval 987654321,3132333435363738393031323334353637383930,urn:ietf:params:xml:ns:keyprov:pskc:hotp,8, 987654321,31323334,urn:ietf:params:xml:ns:keyprov:pskc:pin,4, We can also save the output to a file. >>> f = tempfile.NamedTemporaryFile() >>> sys.argv = shlex.split( ... 'pskc2csv tests/rfc6030/figure5.pskcxml --output') + [f.name] >>> main() >>> with open(f.name, 'r') as r: ... x = sys.stdout.write(r.read()) #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE serial,secret,algorithm,response_length,time_interval 987654321,3132333435363738393031323334353637383930,urn:ietf:params:xml:ns:keyprov:pskc:hotp,8, 987654321,31323334,urn:ietf:params:xml:ns:keyprov:pskc:pin,4, We can specify the columns to output with the --columns option and this also allows specifying custom CSV file column headers. >>> sys.argv = shlex.split( ... 'pskc2csv tests/rfc6030/figure5.pskcxml' + ... ' --columns id:NUMBER,secret,counter,policy.pin_min_length') >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE NUMBER,secret,counter,policy.pin_min_length 12345678,3132333435363738393031323334353637383930,0,4 123456781,31323334,, For password-encrypted files we should be prompted on the command line for a password if the --password option was not specified. The prompt should include the key name if one is present in the PSKC file. The --password option can specify a literal password on the command line of point to a file containing the password. >>> getpass.getpass = lambda x: 'qwerty' if 'My Password 1' in x else 'WRONG' >>> sys.argv = shlex.split('pskc2csv tests/rfc6030/figure7.pskcxml') >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE serial,secret,algorithm,response_length,time_interval 987654321,3132333435363738393031323334353637383930,urn:ietf:params:xml:ns:keyprov:pskc:hotp,8, >>> getpass.getpass = lambda x: 'WRONGPASSWORD' >>> sys.argv = shlex.split('pskc2csv tests/rfc6030/figure7.pskcxml') >>> main() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: ... >>> f = tempfile.NamedTemporaryFile() >>> pskc = PSKC() >>> key = pskc.add_key(secret='1234') >>> pskc.encryption.setup_pbkdf2('qwerty') >>> pskc.write(f.name) >>> getpass.getpass = lambda x: 'qwerty' >>> sys.argv = shlex.split('pskc2csv') + [f.name] >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE serial,secret,algorithm,response_length,time_interval ,31323334,,, >>> sys.argv = shlex.split( ... 'pskc2csv tests/rfc6030/figure7.pskcxml --password qwerty') >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE serial,secret,algorithm,response_length,time_interval 987654321,3132333435363738393031323334353637383930,urn:ietf:params:xml:ns:keyprov:pskc:hotp,8, >>> f = tempfile.NamedTemporaryFile() >>> with open(f.name, 'w') as f2: # open second file to keep tempfile ... x = f2.write('qwerty\n') >>> sys.argv = shlex.split( ... 'pskc2csv tests/rfc6030/figure7.pskcxml --password') + [f.name] >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE serial,secret,algorithm,response_length,time_interval 987654321,3132333435363738393031323334353637383930,urn:ietf:params:xml:ns:keyprov:pskc:hotp,8, For PSKC files that are encrypted with a pre-shared key we can use --secret option to either supply a hex-encoded secret or point to a file name that holds the secret. >>> sys.argv = shlex.split('pskc2csv tests/rfc6030/figure6.pskcxml') >>> main() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... KeyDerivationError: ... >>> sys.argv = shlex.split( ... 'pskc2csv tests/rfc6030/figure6.pskcxml' + ... ' --secret 12345678901234567890123456789012') >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE serial,secret,algorithm,response_length,time_interval 987654321,3132333435363738393031323334353637383930,urn:ietf:params:xml:ns:keyprov:pskc:hotp,8, >>> f = tempfile.NamedTemporaryFile() >>> with open(f.name, 'wb') as f2: # open second file to keep tempfile ... x = f2.write(a2b_hex('12345678901234567890123456789012')) >>> sys.argv = shlex.split( ... 'pskc2csv tests/rfc6030/figure6.pskcxml --secret') + [f.name] >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE serial,secret,algorithm,response_length,time_interval 987654321,3132333435363738393031323334353637383930,urn:ietf:params:xml:ns:keyprov:pskc:hotp,8, The --secret-encoding option can be used to specify the output encoding of the secret (HEX, BASE64 or BASE32). >>> sys.argv = shlex.split( ... 'pskc2csv tests/rfc6030/figure5.pskcxml' + ... ' -c serial,secret -e base64') >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE serial,secret 987654321,MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= 987654321,MTIzNA== >>> sys.argv = shlex.split( ... 'pskc2csv tests/rfc6030/figure5.pskcxml' + ... ' -c serial,secret -e base32') >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE serial,secret 987654321,GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ 987654321,GEZDGNA= Corner-case test: we an also handle empty PSKC files. >>> f = tempfile.NamedTemporaryFile() >>> with open(f.name, 'w') as f2: # open second file to keep tempfile ... x = f2.write(''' ... ... ... ... ... '''.strip()) >>> sys.argv = shlex.split('pskc2csv') + [f.name] >>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE serial,secret,algorithm,response_length,time_interval python-pskc-1.3/tests/test_pskc2pskc.doctest0000644000000000000000000003132513300356475021277 0ustar rootroot00000000000000test_pskc2pskc.doctest - tests for the pskc2pskc script Copyright (C) 2018 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex >>> import shlex >>> import sys >>> import tempfile >>> from pskc import PSKC >>> from pskc.scripts.pskc2pskc import main Sadly we cannot test --help and --version properly because argparse calls exit(0) which doctest does not like. >>> sys.argv = shlex.split('pskc2pskc --help') >>> main() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... SystemExit: 0 >>> sys.argv = shlex.split('pskc2pskc --version') >>> main() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... SystemExit: 0 We can convert the PSKC file and just dump it to stdout. >>> sys.argv = shlex.split('pskc2pskc tests/rfc6030/figure2.pskcxml') >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF Issuer-A MTIzNA== We can also save the output to a file. >>> f = tempfile.NamedTemporaryFile() >>> sys.argv = shlex.split( ... 'pskc2pskc tests/rfc6030/figure2.pskcxml --output') + [f.name] >>> main() >>> with open(f.name, 'r') as r: ... x = sys.stdout.write(r.read()) #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE Issuer-A MTIzNA== We can also re-write the file without decrypting the data at all. >>> sys.argv = shlex.split( ... 'pskc2pskc tests/encryption/aes128-cbc.pskcxml') >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF Pre-shared-key SVZJVklWSVZJVklWSVZJViZS3d+rzbWqD74OQPuyiwrD+XlDXK7ef602mwOebfTR AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv CjGsEXpmZYGMyejd8WJdLFRBWE9XGJLiigPObg== This should also work with legacy PSKC files that have a global encryption IV. The output file should be a clean PSKC 1.0 format with the IV embedded in the CipherValue. Note however that the MAC key is missing because it is equal to the encryption key so we cannot make en encrypted version. >>> sys.argv = shlex.split( ... 'pskc2pskc tests/actividentity/test.pskcxml') >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF ActivIdentity 0950380269 ActivIdentity Xus0lsc+rJLi0nc/ANE0XtRwSU4Zs2AwlO2AqzC+jmSjLEUK4kr2aaKnjHwbovXS SlinEB9YUzcR04MUZDF5dBLtK1c= 837830147 OTP We can also output a decrypted version of an encrypted PSKC file. >>> sys.argv = shlex.split( ... 'pskc2pskc tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/password-encrypted.pskcxml' + ... ' --passwd qwerty') >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF Token Manufacturer 98765432187 2008-01-01T00:00:00 Credential Issuer MySecondToken ZWcLvpFoXNHAG+lx3+Rw 100 The password can also be read from a file. >>> f = tempfile.NamedTemporaryFile() >>> with open(f.name, 'wt') as f2: # open second file to keep tempfile ... x = f2.write('qwerty\n') >>> sys.argv = shlex.split( ... 'pskc2pskc tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/password-encrypted.pskcxml' + ... ' --passwd') + [f.name] >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF ... ZWcLvpFoXNHAG+lx3+Rw ... But we get an error if the password is wrong. >>> sys.argv = shlex.split( ... 'pskc2pskc tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/password-encrypted.pskcxml' + ... ' --passwd wrongpassword') >>> main() #doctest: +ELLIPSIS Traceback (most recent call last): ... DecryptionError: ... We can also supply a secret (both on the command line and via a file). >>> sys.argv = shlex.split( ... 'pskc2csv tests/rfc6030/figure6.pskcxml' + ... ' --secret 12345678901234567890123456789012') >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF ... MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= ... >>> sys.argv = shlex.split( ... 'pskc2csv tests/rfc6030/figure6.pskcxml' + ... ' --secret 12345678901234567890123456789012') >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF ... MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= ... >>> f = tempfile.NamedTemporaryFile() >>> with open(f.name, 'wb') as f2: # open second file to keep tempfile ... x = f2.write(a2b_hex('12345678901234567890123456789012')) >>> sys.argv = shlex.split( ... 'pskc2csv tests/rfc6030/figure6.pskcxml' + ... ' --secret') + [f.name] >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF ... MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= ... We can also decrypt a file and configure a new passphrase. >>> sys.argv = shlex.split( ... 'pskc2pskc tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/password-encrypted.pskcxml' + ... ' --passwd qwerty --new-passwd moresecure') >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF ... ... 16 ... ... ... ... ... Alternatively we can switch from passphrase-based encryption to key-based encryption. >>> sys.argv = shlex.split( ... 'pskc2pskc tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/password-encrypted.pskcxml' + ... ' --passwd qwerty --new-secret 12345678901234567890123456789012') >>> main() #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE +REPORT_UDIFF ... ... ... ... ... If we leave out the original password we get an error. >>> sys.argv = shlex.split( ... 'pskc2pskc tests/draft-hoyer-keyprov-portable-symmetric-key-container-00/password-encrypted.pskcxml' + ... ' --new-passwd moresecure') >>> main() #doctest: +ELLIPSIS Traceback (most recent call last): ... AssertionError python-pskc-1.3/tests/test_rfc6030.doctest0000644000000000000000000002041113300353253020440 0ustar rootroot00000000000000test_rfc6030.doctest - test for examples from RFC 6030 Copyright (C) 2014-2017 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex, b2a_hex >>> def tostr(x): ... return str(x.decode()) >>> def decode(f): ... return lambda x: tostr(f(x)) >>> b2a_hex = decode(b2a_hex) >>> import datetime >>> now = datetime.datetime(2016, 3, 23, 0, 0, 0) >>> from pskc import PSKC This tests figure 2 from RFC 6030. It is a basic key container example with a simple plain text secret key. >>> pskc = PSKC('tests/rfc6030/figure2.pskcxml') >>> [tostr(key.secret) for key in pskc.keys] ['1234'] >>> key = pskc.keys[0] >>> key.id '12345678' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc:hotp' >>> key.issuer 'Issuer-A' >>> tostr(key.secret) '1234' This tests figure 3 from RFC 6030. Relative to figure 2 this includes device, cryptographic module and user identification as well as some more parameters. >>> pskc = PSKC('tests/rfc6030/figure3.pskcxml') >>> pskc.id 'exampleID1' >>> key = pskc.keys[0] >>> key.manufacturer 'Manufacturer' >>> key.serial '987654321' >>> key.device_userid 'DC=example-bank,DC=net' >>> key.crypto_module 'CM_ID_001' >>> key.id '12345678' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc:hotp' >>> key.issuer 'Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 8 >>> tostr(key.secret) '12345678901234567890' >>> key.counter 0 >>> key.key_userid 'UID=jsmith,DC=example-bank,DC=net' This tests figure 4 from RFC 6030. In this case the key value itself is not contained but can be derived using the serial and out-of-band agreements on the meanings of key_profile and key_reference. >>> pskc = PSKC('tests/rfc6030/figure4.pskcxml') >>> key = pskc.keys[0] >>> key.serial '987654321' >>> key.key_profile 'keyProfile1' >>> key.key_reference 'MasterKeyLabel' >>> key.counter 0 This tests the key policy properties as illustrated in figure 5 of RFC 6030. >>> pskc = PSKC('tests/rfc6030/figure5.pskcxml') >>> len(pskc.keys) 2 >>> key1, key2 = pskc.keys >>> key1.serial '987654321' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc:hotp' >>> key.response_length 8 >>> key.response_encoding 'DECIMAL' >>> tostr(key1.secret) '12345678901234567890' >>> key1.counter 0 >>> key1.policy.pin_min_length 4 >>> key1.policy.pin_max_length 4 >>> key1.policy.pin_key_id '123456781' >>> key1.policy.pin_encoding 'DECIMAL' >>> key1.policy.pin_usage 'Local' >>> key1.policy.key_usage ['OTP'] >>> key1.policy.may_use('OTP', now) True >>> key1.policy.may_use('Encrypt', now) False >>> key1.policy.unknown_policy_elements False >>> key2.id '123456781' >>> key2.serial '987654321' >>> key2.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc:pin' >>> key2.response_length 4 >>> key2.response_encoding 'DECIMAL' >>> tostr(key2.secret) '1234' >>> key1.policy.pin '1234' This tests key encryption based on pre-shared keys as illustrated in figure 6 of RFC 6030. The first attempt at extracting the key will fail due to the encryption. >>> pskc = PSKC('tests/rfc6030/figure6.pskcxml') >>> key = pskc.keys[0] >>> key.id '12345678' >>> key.secret # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: No key available >>> pskc.encryption.is_encrypted True >>> pskc.encryption.key_name 'Pre-shared-key' >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012') >>> pskc.encryption.is_encrypted False >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' >>> b2a_hex(pskc.mac.key) '1122334455667788990011223344556677889900' >>> pskc.mac.algorithm 'http://www.w3.org/2000/09/xmldsig#hmac-sha1' >>> b2a_hex(key.secret) '3132333435363738393031323334353637383930' >>> key.check() True >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc:hotp' >>> key.response_length 8 >>> key.manufacturer 'Manufacturer' This tests a derived master key using PBKDF2 as seen in figure 7 of RFC 6030. >>> pskc = PSKC('tests/rfc6030/figure7.pskcxml') >>> pskc.encryption.key_name 'My Password 1' >>> pskc.encryption.is_encrypted True >>> pskc.encryption.derive_key('qwerty') >>> pskc.encryption.is_encrypted False >>> b2a_hex(pskc.encryption.key) '651e63cd57008476af1ff6422cd02e41' >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#aes128-cbc' >>> b2a_hex(pskc.mac.key) 'bdaab8d648e850d25a3289364f7d7eaaf53ce581' >>> pskc.mac.algorithm 'http://www.w3.org/2000/09/xmldsig#hmac-sha1' >>> key = pskc.keys[0] >>> tostr(key.secret) '12345678901234567890' >>> key.check() True >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc:hotp' >>> key.response_length 8 >>> key.manufacturer 'TokenVendorAcme' This tests a PSKC file that uses asymmetric encryption as seen in figure 8 of RFC 6030. Note thet python-pskc does not yet support asymmetric encryption so this test is really limited. >>> pskc = PSKC('tests/rfc6030/figure8.pskcxml') >>> pskc.id 'KC0001' >>> pskc.encryption.algorithm 'http://www.w3.org/2001/04/xmlenc#rsa_1_5' >>> key = pskc.keys[0] >>> key.manufacturer 'TokenVendorAcme' >>> key.serial '987654321' >>> key.id 'MBK000000001' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc:hotp' >>> key.issuer 'Example-Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 6 >>> tostr(key.secret) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: No key available >>> key.counter 0 This tests a PSKC file that uses digital signature to sign the PSKC file as seen in figure 8 of RFC 6030. Since the RFC does not supply the certificate that was used in the signature (which is likely invalid) we cannot verify it in this example and can only get some signature properties. >>> pskc = PSKC('tests/rfc6030/figure9.pskcxml') >>> pskc.signature.is_signed True >>> pskc.signature.algorithm 'http://www.w3.org/2000/09/xmldsig#rsa-sha1' >>> pskc.signature.canonicalization_method 'http://www.w3.org/2001/10/xml-exc-c14n#' >>> pskc.signature.digest_algorithm 'http://www.w3.org/2000/09/xmldsig#sha1' >>> pskc.signature.issuer 'CN=Example.com,C=US' >>> pskc.signature.serial '12345678' >>> key = pskc.keys[0] >>> key.manufacturer 'TokenVendorAcme' >>> key.serial '0755225266' >>> key.id '123' >>> key.algorithm 'urn:ietf:params:xml:ns:keyprov:pskc:hotp' >>> key.issuer 'Example-Issuer' >>> key.response_encoding 'DECIMAL' >>> key.response_length 6 >>> tostr(key.secret) '12345678901234567890' >>> key.counter 0 This tests bulk provisioning as shown in figure 10 of RFC 6030. >>> pskc = PSKC('tests/rfc6030/figure10.pskcxml') >>> all(key.manufacturer == 'TokenVendorAcme' for key in pskc.keys) True >>> [key.serial for key in pskc.keys] ['654321', '123456', '9999999', '9999999'] >>> all(key.algorithm == 'urn:ietf:params:xml:ns:keyprov:pskc:hotp' for key in pskc.keys) True >>> all(key.issuer == 'Issuer' for key in pskc.keys) True >>> all(key.response_length == 8 for key in pskc.keys) True >>> all(key.response_encoding == 'DECIMAL' for key in pskc.keys) True >>> all(key.secret == b'12345678901234567890' for key in pskc.keys) True >>> all(key.counter == 0 for key in pskc.keys) True >>> pskc.keys[0].policy.start_date datetime.datetime(2006, 5, 1, 0, 0, tzinfo=tzutc()) >>> pskc.keys[0].policy.expiry_date datetime.datetime(2006, 5, 31, 0, 0, tzinfo=tzutc()) >>> pskc.keys[1].policy.start_date datetime.datetime(2006, 5, 1, 0, 0, tzinfo=tzutc()) >>> pskc.keys[1].policy.expiry_date datetime.datetime(2006, 5, 31, 0, 0, tzinfo=tzutc()) >>> pskc.keys[2].policy.start_date datetime.datetime(2006, 3, 1, 0, 0, tzinfo=tzutc()) >>> pskc.keys[2].policy.expiry_date datetime.datetime(2006, 3, 31, 0, 0, tzinfo=tzutc()) >>> pskc.keys[3].policy.start_date datetime.datetime(2006, 4, 1, 0, 0, tzinfo=tzutc()) >>> pskc.keys[3].policy.expiry_date datetime.datetime(2006, 4, 30, 0, 0, tzinfo=tzutc()) python-pskc-1.3/tests/test_signature.doctest0000644000000000000000000001403613604371042021367 0ustar rootroot00000000000000test_signature.doctest - test XML signature checking functions Copyright (C) 2017 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> import sys >>> import tempfile >>> try: ... from StringIO import StringIO ... except ImportError: ... from io import StringIO >>> from binascii import a2b_hex, b2a_hex >>> def tostr(x): ... return str(x.decode()) >>> def decode(f): ... return lambda x: tostr(f(x)) >>> b2a_hex = decode(b2a_hex) >>> from pskc import PSKC >>> from pskc.encryption import encrypt, decrypt >>> with open('tests/certificate/key.pem', 'rb') as f: ... signing_key = f.read() >>> with open('tests/certificate/ss-certificate.pem', 'rb') as f: ... self_signed_certificate = f.read() >>> with open('tests/certificate/certificate.pem', 'rb') as f: ... signed_certificate = f.read() Build a simple PSKC structure and sign the file including a self-signed certificate. >>> pskc = PSKC() >>> key = pskc.add_key(id='456', manufacturer='Manufacturer') >>> key.secret = a2b_hex('4e1790ba272406ba309c5a31') >>> pskc.signature.sign(signing_key, self_signed_certificate) Write the PSKC file (use temporary file to test passing file name as argument). >>> f = tempfile.NamedTemporaryFile() >>> pskc.write(f.name) >>> with open(f.name, 'r') as r: ... x = sys.stdout.write(r.read()) #doctest: +ELLIPSIS +REPORT_UDIFF +NORMALIZE_WHITESPACE ... ... ... ... Read back the PSKC file and verify the signature. >>> newpskc = PSKC(f.name) >>> print(tostr(newpskc.signature.certificate)) #doctest: +ELLIPSIS -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- >>> newpskc.signature.signed_pskc # we need a certificate for verification Traceback (most recent call last): ... InvalidCertificate: [18, 0, 'self signed certificate'] >>> newpskc.signature.verify(self_signed_certificate) True >>> newpskc = newpskc.signature.signed_pskc >>> newpskc.keys[0].secret == pskc.keys[0].secret True We can also use the certificate that is embedded in the PSKC file but that does not add any security. >>> newpskc = PSKC(f.name) >>> newpskc.signature.verify(newpskc.signature.certificate) True >>> newpskc = newpskc.signature.signed_pskc >>> newpskc.keys[0].secret == pskc.keys[0].secret True We can also sign a PSKC file and include a certificate that can be validated using a CA certificate. >>> pskc = PSKC() >>> key = pskc.add_key(id='456', manufacturer='Manufacturer') >>> key.secret = a2b_hex('4e1790ba272406ba309c5a31') >>> pskc.signature.sign(signing_key, signed_certificate) >>> f = tempfile.NamedTemporaryFile() >>> pskc.write(f.name) >>> with open(f.name, 'r') as r: ... x = sys.stdout.write(r.read()) #doctest: +ELLIPSIS +REPORT_UDIFF +NORMALIZE_WHITESPACE ... ... ... ... Read back the PSKC file and verify the signature. This file can be verified using the self-signed certificate, the signing certificate or by providing the CA certificate. >>> newpskc = PSKC(f.name) >>> newpskc.signature.signed_pskc # we need a certificate for verification Traceback (most recent call last): ... InvalidCertificate: [20, 0, 'unable to get local issuer certificate'] >>> newpskc.signature.verify(self_signed_certificate) True >>> newpskc.signature.verify(signed_certificate) True >>> newpskc.signature.verify(ca_pem_file='tests/certificate/ca-certificate.pem') True We could also sign a PSKC file and include a certificate that is validated by the default operating system recorded CAs but that is sadly not appropriate for a test suite (the key needs to be included in the test suite so would not be private, depending on the CA used there would be costs involved and the certificates would expire too quickly). A simple test for parsing an incomplete signature element. >>> pskc = PSKC(StringIO(''' ... ... ... ... TheQuickBrownFox ... ... ... ... ... ... ... ... '''.strip())) >>> len(pskc.keys) 1 >>> pskc.signature.canonicalization_method is None True >>> pskc.signature.algorithm is None True >>> pskc.signature.digest_algorithm is None True python-pskc-1.3/tests/test_tripledeskw.doctest0000644000000000000000000000526012676003033021722 0ustar rootroot00000000000000test_tripledeskw.doctest - test keywrap functions Copyright (C) 2014-2016 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> import re >>> from binascii import a2b_hex >>> from pskc.crypto.tripledeskw import wrap, unwrap >>> def fromhex(value): ... return a2b_hex(re.sub(r'\s', '', value)) Example from RFC 3217 section 3.2 wrapping a 192 bit Triple DES key with another 192 bit Triple DES key. >>> plaintext = fromhex(''' ... 2923 bf85 e06d d6ae 5291 49f1 f1ba e9ea b3a7 da3d 860d 3e98 ... ''') >>> key = fromhex(''' ... 255e 0d1c 07b6 46df b313 4cc8 43ba 8aa7 1f02 5b7c 0838 251f ... ''') >>> iv = fromhex('5dd4 cbfc 96f5 453b') >>> ciphertext = fromhex(''' ... 6901 0761 8ef0 92b3 b48c a179 6b23 4ae9 fa33 ebb4 1596 0403 ... 7db5 d6a8 4eb3 aac2 768c 6327 75a4 67d4 ... ''') >>> wrap(plaintext, key, iv=iv) == ciphertext True >>> unwrap(ciphertext, key) == plaintext True Leaving out the iv ensures that a random iv is used. >>> c = wrap(plaintext, key) >>> c == ciphertext False >>> unwrap(c, key) == plaintext True Wrapping is only specified for Triple DES keys but the algorithms works for any plaintext that is a multiple of the Triple DES block size but fails otherwise. >>> short_plaintext = fromhex(''' ... 2923 bf85 e06d d6ae 5291 49f1 f1ba e9ea ... ''') >>> unwrap(wrap(short_plaintext, key), key) == short_plaintext True >>> short_plaintext = fromhex(''' ... 2923 bf85 e06d d6ae 5291 49f1 f1ba e9 ... ''') >>> wrap(short_plaintext, key) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... EncryptionError: Plaintext length wrong The ciphertext must have the correct length (multiple of Triple DES block size) and unwrapping is also authenticated. >>> unwrap(ciphertext, key) == plaintext True >>> unwrap(ciphertext[:-1], key) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: Ciphertext length wrong >>> unwrap(ciphertext[:-1] + b'A', key) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... DecryptionError: CMS key checksum error python-pskc-1.3/tests/test_write.doctest0000644000000000000000000006077514667347427020557 0ustar rootroot00000000000000test_write.doctest - tests for writing PSKC files Copyright (C) 2014-2024 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from pskc import PSKC >>> import datetime >>> import os >>> import sys >>> import tempfile >>> from binascii import a2b_hex >>> from dateutil.tz import tzutc Build a PSKC structure. >>> pskc = PSKC() Add a key with all attributes set. >>> key = pskc.add_key(id='456', manufacturer='Manufacturer') >>> key.id = '123' >>> key.serial = '987654321' >>> key.model = 'Model' >>> key.issue_no = 2 >>> key.start_date = datetime.datetime(2006, 5, 1, 0, 0, tzinfo=tzutc()) >>> key.expiry_date = datetime.datetime(2014, 5, 31, 0, 0, tzinfo=tzutc()) >>> key.device_userid = 'uid=arthur, dc=arthurdejong, dc=org' >>> key.crypto_module = 'CyrptoId' >>> key.algorithm = 'urn:ietf:params:xml:ns:keyprov:pskc:hotp' >>> key.issuer = 'Issuer' >>> key.key_profile = 'key profile id' >>> key.key_reference = 'reference to some key' >>> key.friendly_name = 'a friendly key' >>> key.key_userid = 'cn=Arthur de Jong, dc=arthurdejong, dc=org' >>> key.algorithm_suite = 'Clubs' >>> key.challenge_encoding = 'DECIMAL' >>> key.challenge_min_length = 6 >>> key.challenge_max_length = 8 >>> key.challenge_check = True >>> key.response_encoding = 'DECIMAL' >>> key.response_length = 8 >>> key.response_check = False >>> key.counter = 0 >>> key.secret = a2b_hex('4e1790ba272406ba309c5a31') Add policy information and a PIN. >>> key.policy.key_usage.append('OTP') >>> key.policy.key_usage.append(key.policy.KEY_USE_VERIFY) >>> key.policy.start_date = datetime.datetime(2008, 5, 1, 0, 0, tzinfo=tzutc()) >>> key.policy.expiry_date = datetime.datetime(2012, 6, 13, 0, 0, tzinfo=tzutc()) >>> key.policy.number_of_transactions = 42 >>> key.policy.pin_key_id = 'pinID' >>> key.policy.pin_usage = 'Local' >>> key.policy.pin_max_failed_attempts = 3 >>> key.policy.pin_min_length = 4 >>> key.policy.pin_max_length = 4 >>> key.policy.pin_encoding = 'DECIMAL' >>> pin_key = pskc.add_key(id='pinID', secret='1234', ... algorithm='urn:ietf:params:xml:ns:keyprov:pskc:pin', ... response_encoding='DECIMAL', response_length=4) Add a second (TOTP) key: >>> key = pskc.add_key() >>> key.serial = key.id = '267469811' >>> key.start_date = datetime.datetime(2006, 5, 1, 0, 0) >>> key.expiry_date = datetime.datetime(2014, 5, 31, 0, 0) >>> key.algorithm = 'urn:ietf:params:xml:ns:keyprov:pskc:totp' >>> key.response_encoding = 'DECIMAL' >>> key.response_length = 6 >>> key.time_offset = 0 >>> key.time_interval = 30 >>> key.time_drift = 6 >>> key.secret = a2b_hex('4e1790ba272406ba309c5a31') Write the PSKC file (use temporary file to test passing file name as argument). >>> f = tempfile.NamedTemporaryFile() >>> pskc.write(f.name) >>> with open(f.name, 'r') as r: ... x = sys.stdout.write(r.read()) #doctest: +REPORT_UDIFF Manufacturer 987654321 Model 2 2006-05-01T00:00:00Z 2014-05-31T00:00:00Z uid=arthur, dc=arthurdejong, dc=org CyrptoId Issuer Clubs key profile id reference to some key a friendly key TheQuickBrownFox 0 cn=Arthur de Jong, dc=arthurdejong, dc=org 2008-05-01T00:00:00Z 2012-06-13T00:00:00Z OTP Verify 42 MTIzNA== 267469811 2006-05-01T00:00:00 2014-05-31T00:00:00 TheQuickBrownFox 0 30 6 Read an encrypted PSKC file and write it out as an unencrypted file. >>> pskc = PSKC('tests/encryption/kw-aes128.pskcxml') >>> pskc.encryption.key = a2b_hex('000102030405060708090a0b0c0d0e0f') >>> pskc.encryption.remove_encryption() >>> pskc.write(sys.stdout) #doctest: +REPORT_UDIFF ABEiM0RVZneImaq7zN3u/w== Read an encrypted PSKC file and write it out as-is. This does not require providing the encryption key. >>> pskc = PSKC('tests/rfc6030/figure6.pskcxml') >>> pskc.write(sys.stdout) #doctest: +ELLIPSIS +REPORT_UDIFF Pre-shared-key ESIzRFVmd4iZABEiM0RVZgKn6WjLaTC1sbeBMSvIhRejN9vJa2BOlSaMrR7I5wSX Manufacturer 987654321 CM_ID_001 Issuer AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv Su+NvtQfmvfJzF6bmQiJqoLRExc= 0 Read a legacy encrypted PSKC file and write it out as-is. This should convert the format to RFC 6030 format as best it can. Note that this does not include a MAC key (but does include a MAC algorithm because the MAC key is not specified and we assume to use the encryption key as MAC key). >>> pskc = PSKC('tests/draft-hoyer-keyprov-portable-symmetric-key-container-01/password-encrypted.pskcxml') >>> pskc.write(sys.stdout) #doctest: +ELLIPSIS +REPORT_UDIFF y6TzckeLRQw= 999 16 Token Manufacturer 98765432187 2008-01-01T00:00:00 Credential Issuer MySecondToken F/CY93NYc/SvmxT3oB6PzG7p6zpG92/t hN793ZE7GM6yCM6gz9OKNRzibhg= VVBYqRF1QSpetvIB2vBAzw== 6clqJvT9l0xIZtWSch2t6zr0IwU= If we decrypt the file the MAC key will be included in encrypted form. >>> pskc.encryption.derive_key(b'qwerty') >>> pskc.write(sys.stdout) #doctest: +ELLIPSIS +REPORT_UDIFF y6TzckeLRQw= 999 16 ... ... Set up an encrypted PSKC file and generate a pre-shared key for it. >>> pskc = PSKC() >>> key = pskc.add_key( ... id='1', serial='123456', secret=b'1234', counter=42) >>> pskc.encryption.setup_preshared_key( ... algorithm='aes128-cbc', ... key=a2b_hex('12345678901234567890123456789012'), ... key_name='Pre-shared KEY', fields = ['secret', 'counter']) >>> f = tempfile.NamedTemporaryFile() >>> pskc.write(f.name) >>> with open(f.name, 'r') as r: ... x = sys.stdout.write(r.read()) #doctest: +ELLIPSIS +REPORT_UDIFF Pre-shared KEY ... 123456 ... ... ... ... Read the generated file back in and verify that it matches the original data. >>> newpskc = PSKC(f.name) >>> newpskc.encryption.algorithm == pskc.encryption.algorithm True >>> newpskc.encryption.key = pskc.encryption.key >>> all(newkey.check() for newkey in newpskc.keys) True >>> key = pskc.keys[0] >>> newkey = newpskc.keys[0] >>> newkey.secret == key.secret True >>> newkey.counter == key.counter True Use PBKDF2 to derive a key instead of using a pre-shared key. >>> pskc = PSKC() >>> key = pskc.add_key( ... id='1', serial='123456', secret=b'1234', counter=42) >>> pskc.encryption.setup_pbkdf2( ... 'passphrase', key_name='Passphrase') >>> pskc.write(f.name) >>> with open(f.name, 'r') as r: ... x = sys.stdout.write(r.read()) #doctest: +ELLIPSIS +REPORT_UDIFF ... ... 16 Passphrase ... 123456 ... ... 42 Read the generated file back in and verify that it matches the original data. >>> newpskc = PSKC(f.name) >>> newpskc.encryption.algorithm == pskc.encryption.algorithm True >>> newpskc.encryption.derive_key('passphrase') >>> all(newkey.check() for newkey in newpskc.keys) True >>> key = pskc.keys[0] >>> newkey = newpskc.keys[0] >>> newkey.secret == key.secret True >>> newkey.counter == key.counter True Test encryption and decryption of the generated file to test encryption/ decryption combinations. >>> def test_algorithm(algorithm): ... f = tempfile.NamedTemporaryFile() ... pskc1 = PSKC() ... pskc1.add_key(secret=os.urandom(16)) ... pskc1.encryption.setup_preshared_key(algorithm=algorithm) ... pskc1.write(f.name) ... pskc2 = PSKC(f.name) ... pskc2.encryption.key = pskc1.encryption.key ... assert pskc1.keys[0].secret == pskc2.keys[0].secret ... return (pskc1, pskc2) >>> pskc1, pskc2 = test_algorithm('aes192-cbc') >>> len(pskc1.encryption.key) 24 >>> pskc1, pskc2 = test_algorithm('aes256-cbc') >>> len(pskc1.encryption.key) 32 >>> pskc1, pskc2 = test_algorithm('tripledes-cbc') >>> len(pskc1.encryption.key) 24 >>> pskc1, pskc2 = test_algorithm('kw-aes128') >>> len(pskc1.encryption.key) 16 >>> pskc1, pskc2 = test_algorithm('kw-aes192') >>> len(pskc1.encryption.key) 24 >>> pskc1, pskc2 = test_algorithm('kw-aes256') >>> len(pskc1.encryption.key) 32 >>> pskc1, pskc2 = test_algorithm('kw-tripledes') >>> len(pskc1.encryption.key) 24 Not having a key and trying encryption will fail. >>> f = tempfile.NamedTemporaryFile() >>> pskc = PSKC() >>> key = pskc.add_key(secret='1234') >>> pskc.encryption.setup_preshared_key() >>> pskc.encryption.key = None >>> pskc.write(f.name) Traceback (most recent call last): ... EncryptionError: No key available >>> pskc = PSKC() >>> key = pskc.add_key(secret='1234') >>> pskc.encryption.setup_preshared_key() >>> pskc.encryption.algorithm = None >>> pskc.write(f.name) Traceback (most recent call last): ... EncryptionError: No algorithm specified >>> pskc = PSKC() >>> key = pskc.add_key(secret='1234') >>> pskc.encryption.setup_preshared_key() >>> pskc.encryption.algorithm = 'FOOBAR' >>> pskc.write(f.name) Traceback (most recent call last): ... DecryptionError: Unsupported algorithm: 'FOOBAR' >>> pskc = PSKC() >>> key = pskc.add_key(secret='1234') >>> pskc.encryption.setup_preshared_key() >>> pskc.encryption.algorithm = 'aes256-cbc' >>> pskc.write(f.name) Traceback (most recent call last): ... EncryptionError: Invalid key length Setting up something else than PBKDF2 as derivation algorithm will just result in an empty KeyDerivation element. >>> pskc = PSKC() >>> key = pskc.add_key(secret='1234') >>> pskc.encryption.setup_pbkdf2('qwerty') >>> pskc.encryption.derivation.algorithm = 'unknown' >>> pskc.write(sys.stdout) #doctest: +ELLIPSIS +REPORT_UDIFF ... ... We can make the PKKDF2 salt have to be transmitted out-of-bounds: >>> pskc = PSKC() >>> key = pskc.add_key(secret='1234') >>> pskc.encryption.setup_pbkdf2('qwerty', salt=a2b_hex('1234567890')) >>> pskc.encryption.derivation.pbkdf2_salt = None >>> pskc.write(sys.stdout) #doctest: +ELLIPSIS +REPORT_UDIFF ... 16 ... ... Write a PSKC file with two keys in onde KeyPackage section. Note that this is not allowed in the RFC 6030 schema. Note that device properties that are set on one key end up being applied to both keys. >>> pskc = PSKC() >>> device = pskc.add_device(manufacturer='TokenVendorAcme') >>> key = device.add_key(id='1', serial='123456', secret='1234', counter=42) >>> key = device.add_key(id='pin0', secret='5678') >>> pskc.write(sys.stdout) #doctest: +ELLIPSIS +REPORT_UDIFF TokenVendorAcme 123456 MTIzNA== 42 NTY3OA== If we specify a global IV it will be used for all encrypted values but will be not be written as a global IV in the PSKC file because RFC 6030 does not specify this (and re-using an IV is a bad idea). >>> pskc = PSKC() >>> key = pskc.add_key(secret='1234') >>> pskc.encryption.setup_preshared_key(key=a2b_hex('12345678901234567890123456789012')) >>> pskc.encryption.iv = a2b_hex('000102030405060708090a0b0c0d0e0f') >>> pskc.write(sys.stdout) #doctest: +ELLIPSIS +REPORT_UDIFF AAECAwQFBgcICQoLDA0OD... AAECAwQFBgcICQoLDA0OD... ... Check that we can add secrets as bytearray values >>> pskc = PSKC() >>> key = pskc.add_key( ... id='1', serial='123456', secret=bytearray(b'1234'), counter=42) >>> pskc.encryption.setup_preshared_key( ... algorithm='aes128-cbc', ... key=bytearray(a2b_hex('12345678901234567890123456789012')), ... key_name='Pre-shared KEY', fields = ['secret', 'counter']) >>> f = tempfile.NamedTemporaryFile() >>> pskc.write(f.name) >>> with open(f.name, 'r') as r: ... x = sys.stdout.write(r.read()) #doctest: +ELLIPSIS +REPORT_UDIFF Pre-shared KEY ... 123456 ... ... ... ... python-pskc-1.3/tests/test_yubico.doctest0000644000000000000000000000655013117010157020656 0ustar rootroot00000000000000test_yubico.doctest - tests for Yubico PSKC files Copyright (C) 2017 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >>> from binascii import a2b_hex, b2a_hex >>> def tostr(x): ... return str(x.decode()) >>> def decode(f): ... return lambda x: tostr(f(x)) >>> b2a_hex = decode(b2a_hex) >>> from pskc import PSKC This tests the first example from draft-josefsson-keyprov-pskc-yubikey-00. >>> pskc = PSKC('tests/yubico/example1.pskcxml') >>> pskc.id 'yk-pskc-283599' >>> len(pskc.keys) 1 >>> key = pskc.keys[0] >>> key.manufacturer 'oath.UB' >>> key.serial '283599' >>> key.start_date datetime.datetime(2009, 1, 22, 0, 25, 11, tzinfo=tzutc()) >>> key.crypto_module '1' >>> key.id '283599:1' >>> key.algorithm 'http://www.yubico.com/#yubikey-aes' >>> key.issuer 'ACME Inc.' >>> key.response_encoding 'ALPHANUMERIC' >>> key.response_length 44 >>> b2a_hex(key.secret) '2b7e151628aed2a6abf7158809cf4f3c' >>> key.userid 'CN=ekhgjhbctrgn, UID=ca62baca62ba' This tests the second example from draft-josefsson-keyprov-pskc-yubikey-00. >>> pskc = PSKC('tests/yubico/example2.pskcxml') >>> pskc.id 'yk-pskc-283598' >>> len(pskc.keys) 2 >>> key = pskc.keys[0] # first key >>> key.manufacturer 'oath.UB' >>> key.serial '283598' >>> key.start_date datetime.datetime(2009, 1, 22, 0, 25, 10, tzinfo=tzutc()) >>> key.crypto_module '1' >>> key.id '283598:1' >>> key.algorithm 'http://www.yubico.com/#yubikey-aes' >>> key.issuer 'ACME Inc.' >>> key.response_encoding 'ALPHANUMERIC' >>> key.response_length 44 >>> b2a_hex(key.secret) '5698356d30868c4201e26f66c582bb45' >>> key.userid 'CN=ekhgjhbctrgn, UID=ca62baca62ba' >>> key = pskc.keys[1] # second key >>> key.manufacturer 'oath.UB' >>> key.serial '283598' >>> key.start_date datetime.datetime(2009, 1, 22, 0, 25, 10, tzinfo=tzutc()) >>> key.crypto_module '2' >>> key.id '283598:2' >>> key.algorithm 'http://www.yubico.com/#yubikey-aes' >>> key.issuer 'ACME Inc.' >>> key.response_encoding 'ALPHANUMERIC' >>> key.response_length 44 >>> b2a_hex(key.secret) '38892b82abf1807788458fc5a5165c80' >>> key.userid 'CN=ekhgjhbctrgn, UID=ca62baca62ba' This tests the third example from draft-josefsson-keyprov-pskc-yubikey-00. >>> pskc = PSKC('tests/yubico/example3.pskcxml') >>> pskc.id 'yk-pskc-283597' >>> len(pskc.keys) 1 >>> key = pskc.keys[0] >>> key.manufacturer 'oath.UB' >>> key.serial '283597' >>> key.start_date datetime.datetime(2009, 1, 22, 0, 25, 9, tzinfo=tzutc()) >>> key.crypto_module '1' >>> key.id '283597:1' >>> key.algorithm 'http://www.yubico.com/#yubikey-aes' >>> key.issuer 'ACME Inc.' >>> key.response_encoding 'ALPHANUMERIC' >>> key.response_length 40 >>> b2a_hex(key.secret) '2b7e151628aed2a6abf7158809cf4f3c' >>> key.userid 'CN=ekhgjhbctrgn, UID=ca62baca62ba' # TODO: support extensions python-pskc-1.3/tests/yubico/0000755000000000000000000000000014667356044016245 5ustar rootroot00000000000000python-pskc-1.3/tests/yubico/example1.pskcxml0000644000000000000000000000216713037001220021337 0ustar rootroot00000000000000 oath.UB 283599 2009-01-22T00:25:11Z 1 ACME Inc. K34VFiiu0qar9xWICc9PPA== CN=ekhgjhbctrgn, UID=ca62baca62ba python-pskc-1.3/tests/yubico/example2.pskcxml0000644000000000000000000000327113037001220021335 0ustar rootroot00000000000000 oath.UB 283598 2009-01-22T00:25:10Z 1 ACME Inc. Vpg1bTCGjEIB4m9mxYK7RQ== CN=ekhgjhbctrgn, UID=ca62baca62ba oath.UB 283598 2009-01-22T00:25:10Z 2 ACME Inc. OIkrgqvxgHeIRY/FpRZcgA== CN=ekhgjhbctrgn, UID=ca62baca62ba python-pskc-1.3/tests/yubico/example3.pskcxml0000644000000000000000000000275513037001220021344 0ustar rootroot00000000000000 oath.UB 283597 2009-01-22T00:25:09Z 1 ACME Inc. K34VFiiu0qar9xWICc9PPA== CN=ekhgjhbctrgn, UID=ca62baca62ba python-pskc-1.3/tox.ini0000644000000000000000000000221614667347427015132 0ustar rootroot00000000000000[tox] envlist = py{36,37,38,39,310,312,py,py3}-signxml,py{36,37,38,39,310,311,312,py3}{-legacy,-lxml}{,-defusedxml},flake8,docs,codespell skip_missing_interpreters = true [testenv] deps = pytest pytest-cov signxml: signxml signxml: defusedxml lxml: lxml defusedxml: defusedxml commands = signxml: pytest --cov-fail-under 100 legacy: pytest --ignore=tests/test_signature.doctest --cov-fail-under 95 lxml: pytest --ignore=tests/test_signature.doctest --cov-fail-under 95 setenv= PYTHONWARNINGS=all TZ=Europe/Amsterdam [testenv:flake8] skip_install = true deps = flake8<6.0 flake8-author flake8-blind-except flake8-bugbear flake8-class-newline flake8-commas flake8-deprecated flake8-docstrings flake8-exact-pin flake8-import-order flake8-print flake8-quotes flake8-tidy-imports flake8-tuple pep8-naming commands = flake8 . [testenv:codespell] skip_install = true deps = codespell commands = codespell {posargs} [testenv:docs] deps = Sphinx commands = sphinx-build -N -b html docs {envtmpdir}/sphinx -W