tlslite-ng-0.5.1/000077500000000000000000000000001261665411200136035ustar00rootroot00000000000000tlslite-ng-0.5.1/.landscape.yaml000066400000000000000000000002141261665411200164740ustar00rootroot00000000000000doc-warnings: true test-warnings: false strictness: medium max-line-length: 80 ignore-paths: - tests - unit_tests - scripts tlslite-ng-0.5.1/LICENSE000066400000000000000000000663521261665411200146240ustar00rootroot00000000000000 TLS Lite includes code from different sources. All code is either dedicated to the public domain by its authors, available under a BSD-style license or available under GNU LGPL v2 license. In particular: - Code written by Trevor Perrin, Kees Bos, Sam Rushing, Dimitris Moraitis, Marcelo Fernandez, Martin von Loewis, Dave Baggett, Yngve Pettersen, and Mirko Dziadzka is available under the following terms: This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - Code written by Bram Cohen (rijndael.py) was dedicated to the public domain by its author. See rijndael.py for details. - Code written by Google is available under the following terms: Copyright (c) 2008, The Chromium Authors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - Code written by Hubert Kario is available under the following terms: Copyright (c) 2014, Hubert Kario, Red Hat Inc. All rights reserved. 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 tlslite-ng-0.5.1/MANIFEST.in000066400000000000000000000001661261665411200153440ustar00rootroot00000000000000recursive-include tests * recursive-include docs * include LICENSE include README include Makefile include MANIFEST.intlslite-ng-0.5.1/Makefile000066400000000000000000000061341261665411200152470ustar00rootroot00000000000000# Authors: # Trevor Perrin # Hubert Kario - test and test-dev # PYTHON2 := $(shell which python2 2>/dev/null) PYTHON3 := $(shell which python3 2>/dev/null) COVERAGE := $(shell which coverage 2>/dev/null) COVERAGE2 := $(shell which coverage2 2>/dev/null) COVERAGE3 := $(shell which coverage3 2>/dev/null) .PHONY : default default: @echo To install tlslite run \"./setup.py install\" or \"make install\" .PHONY: install install: ./setup.py install .PHONY : clean clean: rm -rf tlslite/__pycache__ rm -rf tlslite/integration/__pycache__ rm -rf tlslite/utils/__pycache__ rm -rf tlslite/*.pyc rm -rf tlslite/utils/*.pyc rm -rf tlslite/integration/*.pyc rm -rf unit_tests/*.pyc rm -rf unit_tests/__pycache__ rm -rf dist rm -rf docs rm -rf build rm -f MANIFEST docs: epydoc --html -v --introspect-only -o docs --graph all tlslite dist: docs ./setup.py sdist test: cd tests/ && python ./tlstest.py server localhost:4433 . & sleep 1 cd tests/ && python ./tlstest.py client localhost:4433 . test-local: cd tests/ && PYTHONPATH=.. python ./tlstest.py server localhost:4433 . & sleep 1 cd tests/ && PYTHONPATH=.. python ./tlstest.py client localhost:4433 . test-dev: ifdef PYTHON2 @echo "Running test suite with Python 2" python2 -m unittest discover -v cd tests/ && PYTHONPATH=.. python2 ./tlstest.py server localhost:4433 . & sleep 1 cd tests/ && PYTHONPATH=.. python2 ./tlstest.py client localhost:4433 . endif ifdef PYTHON3 @echo "Running test suite with Python 3" python3 -m unittest discover -v cd tests/ && PYTHONPATH=.. python3 ./tlstest.py server localhost:4433 . & sleep 1 cd tests/ && PYTHONPATH=.. python3 ./tlstest.py client localhost:4433 . endif ifndef PYTHON2 ifndef PYTHON3 @echo "Running test suite with default Python" python -m unittest discover -v cd tests/ && PYTHONPATH=.. python ./tlstest.py server localhost:4433 . & sleep 1 cd tests/ && PYTHONPATH=.. python ./tlstest.py client localhost:4433 . endif endif epydoc --check --fail-on-error -v tlslite pylint --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" tlslite > pylint_report.txt || : diff-quality --violations=pylint --fail-under=90 pylint_report.txt ifdef COVERAGE2 coverage2 run --branch --source tlslite -m unittest discover coverage2 report -m coverage2 xml diff-cover --fail-under=90 coverage.xml endif ifdef COVERAGE3 coverage3 run --branch --source tlslite -m unittest discover coverage3 report -m coverage3 xml diff-cover --fail-under=90 coverage.xml endif ifndef COVERAGE2 ifndef COVERAGE3 ifdef COVERAGE coverage run --branch --source tlslite -m unittest discover coverage report -m coverage xml diff-cover --fail-under=90 coverage.xml endif endif endif tests/TACK_Key1.pem: tack genkey -x -p test -o tests/TACK_Key1.pem tests/TACK_Key2.pem: tack genkey -x -p test -o tests/TACK_Key2.pem # the following needs to be used only when the server certificate gets recreated gen-tacks: tests/TACK_Key1.pem tests/TACK_Key2.pem tack sign -x -k tests/TACK_Key1.pem -p test -c tests/serverX509Cert.pem -o tests/TACK1.pem tack sign -x -k tests/TACK_Key2.pem -p test -c tests/serverX509Cert.pem -o tests/TACK2.pem tlslite-ng-0.5.1/README000066400000000000000000000013541261665411200144660ustar00rootroot00000000000000tlslite-ng is a pure python implementation of SSLv3.0, TLS 1.0, TLS 1.1 and TLS 1.2 protocols. It can use pycrypto, m2crypto and gmp for acceleration of cryptographic operations but is not dependant upon them. Functionality implemented include: - RC4, 3DES-CBC, AES-CBC and AES-GCM ciphers - MD5, SHA1, SHA256 and SHA384 HMACs as well as AEAD mode of operation - RSA and DHE_RSA key exchange - anonymous DHE key exchange - NULL encryption ciphersuites - FALLBACK_SCSV - encrypt-then-MAC mode of operation for CBC ciphersuites - client certificates - TACK certificate pinning - SRP_SHA_RSA ciphersuites tlslite-ng aims to be a drop-in replacement for tlslite while providing more comprehensive set of features and more secure defautls. tlslite-ng-0.5.1/README.md000066400000000000000000000702251261665411200150700ustar00rootroot00000000000000``` tlslite-ng version 0.5.1 2015-11-05 Hubert Kario https://github.com/tomato42/tlslite-ng/ ``` [![Build Status](https://travis-ci.org/tomato42/tlslite-ng.svg?branch=master)](https://travis-ci.org/tomato42/tlslite-ng) [![Coverage Status](https://coveralls.io/repos/tomato42/tlslite-ng/badge.svg?branch=master)](https://coveralls.io/r/tomato42/tlslite-ng?branch=master) [![Code Health](https://landscape.io/github/tomato42/tlslite-ng/master/landscape.svg?style=flat)](https://landscape.io/github/tomato42/tlslite-ng/master) [![Code Climate](https://codeclimate.com/github/tomato42/tlslite-ng/badges/gpa.svg)](https://codeclimate.com/github/tomato42/tlslite-ng) Table of Contents ================== 1. Introduction 2. License/Acknowledgements 3. Installation 4. Getting Started with the Command-Line Tools 5. Getting Started with the Library 6. Using tlslite-ng with httplib 7. Using tlslite-ng with poplib or imaplib 8. Using tlslite-ng with smtplib 9. Using tlslite-ng with SocketServer 10. Using tlslite-ng with asyncore 11. SECURITY CONSIDERATIONS 12. History 1 Introduction =============== tlslite-ng is an open source python library that implements SSL and TLS. tlslite-ng supports RSA and SRP ciphersuites. tlslite-ng is pure python, however it can use other libraries for faster crypto operations. tlslite-ng integrates with several stdlib neworking libraries. API documentation is available in the 'docs' directory. If you have questions or feedback, feel free to contact me. Issues and pull requests can also be submitted through github issue tracking system. tlslite-ng aims to be a drop in replacement for the original TLS Lite. 2 Licenses/Acknowledgements ============================ tlslite-ng is a fork of TLS Lite. TLS Lite was written (mostly) by Trevor Perrin. It includes code from Bram Cohen, Google, Kees Bos, Sam Rushing, Dimitris Moraitis, Marcelo Fernandez, Martin von Loewis, Dave Baggett, Yngve N. Pettersen (ported by Paul Sokolovsky), Mirko Dziadzka, David Benjamin, and Hubert Kario. Original code in TLS Lite has either been dedicated to the public domain by its authors, or placed under a BSD-style license. See the LICENSE file for details. Currently it is distributed under Gnu LGPLv2 license. Thanks to Edward Loper for Epydoc, which generated the API docs. 3 Installation =============== Requirements: * Python 2.6 or higher is required. * Python 3.2 or higher is supported. Options: * If you have the M2Crypto interface to OpenSSL, this will be used for fast RSA operations and fast ciphers. * If you have pycrypto this will be used for fast RSA operations and fast ciphers. * If you have the GMPY interface to GMP, this will be used for fast RSA and SRP operations. * These modules don't need to be present at installation - you can install them any time. Run 'python setup.py install' Test the Installation * From the distribution's directory, run: ``` make test ``` * If it says "Test succeeded" at the end, you're ready to go. 4 Getting Started with the Command-Line Tools ============================================== tlslite-ng installs two command-line scripts: `tlsdb.py` and `tls.py`. `tls.py` lets you run test clients and servers. It can be used for testing other TLS implementations, or as example code. Note that `tls.py server` runs an HTTPS server which will serve files rooted at the current directory by default, so be careful. `tlsdb.py` lets you manage SRP verifier databases. These databases are used by a TLS server when authenticating clients with SRP. X.509 ------ To run an X.509 server, go to the ./tests directory and do: ``` tls.py server -k serverX509Key.pem -c serverX509Cert.pem localhost:4443 ``` Try connecting to the server with a web browser, or with: ``` tls.py client localhost:4443 ``` X.509 with TACK ---------------- To run an X.509 server using a TACK, install TACKpy, then run the same server command as above with added arguments: ``` ... -t TACK1.pem localhost:4443 ``` SRP ---- To run an SRP server, try something like: ``` tlsdb.py createsrp verifierDB tlsdb.py add verifierDB alice abra123cadabra 1024 tlsdb.py add verifierDB bob swordfish 2048 tls.py server -v verifierDB localhost:4443 ``` Then try connecting to the server with: ``` tls.py client localhost:4443 alice abra123cadabra ``` HTTPS ------ To run an HTTPS server with less typing, run `./tests/httpsserver.sh`. To run an HTTPS client, run `./tests/httpsclient.py`. 5 Getting Started with the Library =================================== Whether you're writing a client or server, there are six steps: 1. Create a socket and connect it to the other party. 2. Construct a TLSConnection instance with the socket. 3. Call a handshake function on TLSConnection to perform the TLS handshake. 4. Check the results to make sure you're talking to the right party. 5. Use the TLSConnection to exchange data. 6. Call close() on the TLSConnection when you're done. tlslite-ng also integrates with several stdlib python libraries. See the sections following this one for details. 5 Step 1 - create a socket --------------------------- Below demonstrates a socket connection to Amazon's secure site. ``` from socket import * sock = socket(AF_INET, SOCK_STREAM) sock.connect( ("www.amazon.com", 443) ) ``` 5 Step 2 - construct a TLSConnection ------------------------------------- You can import tlslite objects individually, such as: ``` from tlslite import TLSConnection ``` Or import the most useful objects through: ``` from tlslite.api import * ``` Then do: ``` connection = TLSConnection(sock) ``` 5 Step 3 - call a handshake function (client) ---------------------------------------------- If you're a client, there's two different handshake functions you can call, depending on how you want to authenticate: ``` connection.handshakeClientCert() connection.handshakeClientCert(certChain, privateKey) connection.handshakeClientSRP("alice", "abra123cadabra") ``` The ClientCert function without arguments is used when connecting to a site like Amazon, which doesn't require client authentication, but which will authenticate itself using an X.509 certificate chain. The ClientCert function can also be used to do client authentication with an X.509 certificate chain and corresponding private key. To use X.509 chains, you'll need some way of creating these, such as OpenSSL (see http://www.openssl.org/docs/HOWTO/ for details). Below is an example of loading an X.509 chain and private key: ``` from tlslite import X509, X509CertChain, parsePEMKey s = open("./test/clientX509Cert.pem").read() x509 = X509() x509.parse(s) certChain = X509CertChain([x509]) s = open("./test/clientX509Key.pem").read() privateKey = parsePEMKey(s, private=True) ``` The SRP function does mutual authentication with a username and password - see RFC 5054 for details. If you want more control over the handshake, you can pass in a HandshakeSettings instance. For example, if you're performing SRP, but you only want to use SRP parameters of at least 2048 bits, and you only want to use the AES-256 cipher, and you only want to allow TLS (version 3.1), not SSL (version 3.0), you can do: ``` settings = HandshakeSettings() settings.minKeySize = 2048 settings.cipherNames = ["aes256"] settings.minVersion = (3,1) settings.useExperimentalTACKExtension = True # Needed for TACK support connection.handshakeClientSRP("alice", "abra123cadabra", settings=settings) ``` If you want to check the server's certificate using TACK, you should set the "useExperiementalTACKExtension" value in HandshakeSettings. (Eventually, TACK support will be enabled by default, but for now it is an experimental feature which relies on a temporary TLS Extension number, and should not be used for production software.) This will cause the client to request the server to send you a TACK (and/or any TACK Break Signatures): Finally, every TLSConnection has a session object. You can try to resume a previous session by passing in the session object from the old session. If the server remembers this old session and supports resumption, the handshake will finish more quickly. Otherwise, the full handshake will be done. For example: ``` connection.handshakeClientSRP("alice", "abra123cadabra") . . oldSession = connection.session connection2.handshakeClientSRP("alice", "abra123cadabra", session= oldSession) ``` 5 Step 3 - call a handshake function (server) ---------------------------------------------- If you're a server, there's only one handshake function, but you can pass it several different parameters, depending on which types of authentication you're willing to perform. To perform SRP authentication, you have to pass in a database of password verifiers. The VerifierDB class manages an in-memory or on-disk verifier database. ``` verifierDB = VerifierDB("./test/verifierDB") verifierDB.open() connection.handshakeServer(verifierDB=verifierDB) ``` To perform authentication with a certificate and private key, the server must load these as described in the previous section, then pass them in. If the server sets the reqCert boolean to True, a certificate chain will be requested from the client. ``` connection.handshakeServer(certChain=certChain, privateKey=privateKey, reqCert=True) ``` You can pass in a verifier database and/or a certificate chain+private key. The client will use one or both to authenticate the server. You can also pass in a HandshakeSettings object, as described in the last section, for finer control over handshaking details. If you are passing in a certificate chain+private key, you may additionally provide a TACK to assist the client in authenticating your certificate chain. This requires the TACKpy library. Load a TACKpy.TACK object, then do: ``` settings = HandshakeSettings() settings.useExperimentalTACKExtension = True # Needed for TACK support connection.handshakeServer(certChain=certChain, privateKey=privateKey, tack=tack, settings=settings) ``` Finally, the server can maintain a SessionCache, which will allow clients to use session resumption: ``` sessionCache = SessionCache() connection.handshakeServer(verifierDB=verifierDB, sessionCache=sessionCache) ``` It should be noted that the session cache, and the verifier databases, are all thread-safe. 5 Step 4 - check the results ----------------------------- If the handshake completes without raising an exception, authentication results will be stored in the connection's session object. The following variables will be populated if applicable, or else set to None: ``` connection.session.srpUsername # string connection.session.clientCertChain # X509CertChain connection.session.serverCertChain # X509CertChain connection.session.tackExt # TACKpy.TACK_Extension ``` X.509 chain objects return the end-entity fingerprint via getFingerprint(), and ignore the other certificates. TACK objects return the (validated) TACK ID via getTACKID(). To save yourself the trouble of inspecting certificates after the handshake, you can pass a Checker object into the handshake function. The checker will be called if the handshake completes successfully. If the other party isn't approved by the checker, a subclass of TLSAuthenticationError will be raised. If the handshake fails for any reason, including a Checker error, an exception will be raised and the socket will be closed. If the socket timed out or was unexpectedly closed, a socket.error or TLSAbruptCloseError will be raised. Otherwise, either a TLSLocalAlert or TLSRemoteAlert will be raised, depending on whether the local or remote implementation signalled the error. The exception object has a 'description' member which identifies the error based on the codes in RFC 2246. A TLSLocalAlert also has a 'message' string that may have more details. Example of handling a remote alert: ``` try: [...] except TLSRemoteAlert as alert: if alert.description == AlertDescription.unknown_psk_identity: print "Unknown user." [...] ``` Below are some common alerts and their probable causes, and whether they are signalled by the client or server. Client `handshake_failure`: * SRP parameters are not recognized by client * Server's TACK was unrelated to its certificate chain Client `insufficient_security`: * SRP parameters are too small Client `protocol_version`: * Client doesn't support the server's protocol version Server `protocol_version`: * Server doesn't support the client's protocol version Server `bad_record_mac`: * bad SRP username or password Server `unknown_psk_identity`: * bad SRP username (`bad_record_mac` could be used for the same thing) Server `handshake_failure`: * no matching cipher suites 5 Step 5 - exchange data ------------------------- Now that you have a connection, you can call read() and write() as if it were a socket.SSL object. You can also call send(), sendall(), recv(), and makefile() as if it were a socket. These calls may raise TLSLocalAlert, TLSRemoteAlert, socket.error, or TLSAbruptCloseError, just like the handshake functions. Once the TLS connection is closed by the other side, calls to read() or recv() will return an empty string. If the socket is closed by the other side without first closing the TLS connection, calls to read() or recv() will return a TLSAbruptCloseError, and calls to write() or send() will return a socket.error. 5 Step 6 - close the connection -------------------------------- When you're finished sending data, you should call close() to close the connection and socket. When the connection is closed properly, the session object can be used for session resumption. If an exception is raised the connection will be automatically closed; you don't need to call close(). Furthermore, you will probably not be able to re-use the socket, the connection object, or the session object, and you shouldn't even try. By default, calling close() will close the underlying socket. If you set the connection's closeSocket flag to False, the socket will remain open after close. (NOTE: some TLS implementations will not respond properly to the `close_notify` alert that close() generates, so the connection will hang if closeSocket is set to True.) 6 Using tlslite-ng with httplib =============================== tlslite-ng comes with an HTTPTLSConnection class that extends httplib to work over SSL/TLS connections. Depending on how you construct it, it will do different types of authentication. ``` #No authentication whatsoever h = HTTPTLSConnection("www.amazon.com", 443) h.request("GET", "") r = h.getresponse() [...] #Authenticate server based on its TACK ID h = HTTPTLSConnection("localhost", 4443, tackID="B3ARS.EQ61B.F34EL.9KKLN.3WEW5", hardTack=False) [...] #Mutually authenticate with SRP h = HTTPTLSConnection("localhost", 443, username="alice", password="abra123cadabra") [...] ``` 7 Using tlslite-ng with poplib or imaplib ========================================= tlslite-ng comes with `POP3_TLS` and `IMAP4_TLS` classes that extend poplib and imaplib to work over SSL/TLS connections. These classes can be constructed with the same parameters as HTTPTLSConnection (see previous section), and behave similarly. ``` #To connect to a POP3 server over SSL and display its fingerprint: from tlslite.api import * p = POP3_TLS("---------.net", port=995) print p.sock.session.serverCertChain.getFingerprint() [...] #To connect to an IMAP server once you know its fingerprint: from tlslite.api import * i = IMAP4_TLS("cyrus.andrew.cmu.edu", x509Fingerprint="00c14371227b3b677ddb9c4901e6f2aee18d3e45") [...] ``` 8 Using tlslite-ng with smtplib =============================== tlslite-ng comes with an `SMTP_TLS` class that extends smtplib to work over SSL/TLS connections. This class accepts the same parameters as HTTPTLSConnection (see previous section), and behaves similarly. Depending on how you call starttls(), it will do different types of authentication. ``` #To connect to an SMTP server once you know its fingerprint: from tlslite.api import * s = SMTP_TLS("----------.net", port=587) s.ehlo() s.starttls(x509Fingerprint="7e39be84a2e3a7ad071752e3001d931bf82c32dc") [...] ``` 9 Using tlslite-ng with SocketServer ==================================== You can use tlslite-ng to implement servers using Python's SocketServer framework. tlslite-ng comes with a TLSSocketServerMixIn class. You can combine this with a TCPServer such as HTTPServer. To combine them, define a new class that inherits from both of them (with the mix-in first). Then implement the handshake() method, doing some sort of server handshake on the connection argument. If the handshake method returns True, the RequestHandler will be triggered. See the tests/httpsserver.py example. 10 Using tlslite-ng with asyncore ================================= tlslite-ng can be used with subclasses of asyncore.dispatcher. See the comments in TLSAsyncDispatcherMixIn.py for details. This is still experimental, and may not work with all asyncore.dispatcher subclasses. 11 Security Considerations =========================== tlslite-ng is beta-quality code. It hasn't received much security analysis. Use at your own risk. tlslite-ng **CANNOT** verify certificates - you must use external means to check if the certificate is the expected one. Because python execution environmnet uses hash tables to store variables (that includes functions, objects and classes) it's very hard to create implementations that are timing attack resistant. This includes both the pure-python implementation of ciphers (i.e. AES or 3DES) and the HMAC and padding check of ciphers working in CBC MAC-then-encrypt mode. In other words, pure-python (tlslite-ng internal) implementations of all ciphers, as well as all CBC mode ciphers working in MAC-then-encrypt mode are **NOT** secure. Don't use them. Prefer AEAD ciphersuites (AES-GCM) or encrypt-then-MAC mode for CBC ciphers. 12 History =========== 0.5.1 - 2015-11-05 - fix SRP_SHA_RSA ciphersuites in TLSv1.2 (for real this time) - minor enchancements in test scripts - NOTE: KeyExchange class is not part of stable API yet (it will be moved to different module later)! 0.5.0 - 10/10/2015 - fix generators in AsyncStateMachine to work on Python3 (Theron Lewis) - fix CVE-2015-3220 - remote DoS caused by incorrect malformed packet handling - removed RC4 from ciphers supported by default - add supported_groups, supported_point_formats, signature_algorithms and renegotiation_info extensions - remove most CBC MAC-ing and padding timing side-channel leaks (should fix CVE-2013-0169, a.k.a. Lucky13) - add support for NULL encryption - TLS_RSA_WITH_NULL_MD5, TLS_RSA_WITH_NULL_SHA and TLS_RSA_WITH_NULL_SHA256 ciphersuites - add more ADH ciphers (TLS_DH_ANON_WITH_RC4_128_MD5, TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA, TLS_DH_ANON_WITH_AES_128_CBC_SHA256, TLS_DH_ANON_WITH_AES_256_CBC_SHA256, TLS_DH_ANON_WITH_AES_128_GCM_SHA256, TLS_DH_ANON_WITH_AES_256_GCM_SHA384) - implement a TLS record layer abstraction that makes it very easy to handle TLS handshake and alert protocol messages (MessageSocket) - fix reqCert option in tls.py server - implement AES-256-GCM ciphersuites and SHA384 PRF - implement AES-GCM cipher and AES-128-GCM ciphersuites (David Benjamin - Chromium) - implement client side DHE_RSA key exchange and DHE with certificate based client authentication - implement server side DHE_RSA key exchange (David Benjamin - Chromium) - don't use TLSv1.2 ciphers in earlier protocols (David Benjamin - Chromium) - fix certificate-based client authentication in TLSv1.2 (David Benjamin - Chromium) - fix SRP_SHA_RSA ciphersuites - properly implement record layer fragmentation (previously worked just for Application Data) - RFC 5246 Section 6.2.1 - Implement RFC 7366 - Encrypt-then-MAC - generate minimal padding for CBC ciphers (David Benjamin - Chromium) - implementation of `FALLBACK_SCSV` (David Benjamin - Chromium) - fix issue with handling keys in session cache (Mirko Dziadzka) - coverage measurement for unit tests - introduced Continous Integration, targetting 2.6, 2.7, 3.2, 3.3 and 3.4 - support PKCS#8 files with m2crypto installed for loading private keys - fix Writer not to silently overflow integers - fix Parser getFixBytes boundary checking - big code refactors, mainly TLSRecordLayer and TLSConnection, lot of code put under unit test coverage 0.4.8 - 11/12/2014 - Added more acknowledgements and security considerations 0.4.7 - 11/12/2014 - Added TLS 1.2 support (Yngve Pettersen and Paul Sokolovsky) - Don't offer SSLv3 by default (e.g. POODLE) - Fixed bug with `PyCrypto_RSA` integration - Fixed harmless bug that added non-prime into sieves list - Added "make test" and "make test-dev" targets (Hubert Kario) 0.4.5 - 3/20/2013 - **API CHANGE**: TLSClosedConnectionError instead of ValueError when writing to a closed connection. This inherits from socket.error, so should interact better with SocketServer (see http://bugs.python.org/issue14574) and other things expecting a socket.error in this situation. - Added support for RC4-MD5 ciphersuite (if enabled in settings) - This is allegedly necessary to connect to some Internet servers. - Added TLSConnection.unread() function - Switched to New-style classes (inherit from 'object') - Minor cleanups 0.4.4 - 2/25/2013 - Added Python 3 support (Martin von Loewis) - Added NPN client support (Marcelo Fernandez) - Switched to RC4 as preferred cipher - faster in Python, avoids "Lucky 13" timing attacks - Fixed bug when specifying ciphers for anon ciphersuites - Made RSA hashAndVerify() tolerant of sigs w/o encoded NULL AlgorithmParam - (this function is not used for TLS currently, and this tolerance may not even be necessary) 0.4.3 - 9/27/2012 - Minor bugfix (0.4.2 doesn't load tackpy) 0.4.2 - 9/25/2012 - Updated TACK (compatible with tackpy 0.9.9) 0.4.1 - 5/22/2012 - Fixed RSA padding bugs (w/help from John Randolph) - Updated TACK (compatible with tackpy 0.9.7) - Added SNI - Added NPN server support (Sam Rushing/Google) - Added AnonDH (Dimitris Moraitis) - Added X509CertChain.parsePemList - Improved XML-RPC (Kees Bos) 0.4.0 - 2/11/2012 - Fixed pycrypto support - Fixed python 2.6 problems 0.3.9.x - 2/7/2012 Much code cleanup, in particular decomposing the handshake functions so they are readable. The main new feature is support for TACK, an experimental authentication method that provides a new way to pin server certificates (See https://github.com/moxie0/Convergence/wiki/TACK ). Also: - Security Fixes - Sends SCSV ciphersuite as per RFC 5746, to signal non-renegotiated Client Hello. Does not support renegotiation (never has). - Change from e=3 to e=65537 for generated RSA keys, not strictly necessary but mitigates risk of sloppy verifier. - 1/(n-1) countermeasure for BEAST. - Behavior changes: - Split cmdline into tls.py and tlstest.py, improved options. - Formalized LICENSE. - Defaults to closing socket after sending `close_notify`, fixes hanging. problem that would occur sometime when waiting for other party's close_notify. - Update SRP to RFC 5054 compliance. - Removed client handshake "callbacks", no longer support the SRP re-handshake idiom within a single handshake function. - Bugfixes - Added hashlib support, removes Deprecation Warning due to sha and md5. - Handled GeneratorExit exceptions that are a new Python feature, and interfere with the async code if not handled. - Removed: - Shared keys (it was based on an ancient I-D, not TLS-PSK). - cryptlib support, it wasn't used much, we have enough other options. - cryptoIDs (TACK is better). - win32prng extension module, as os.urandom is now available. - Twisted integration (unused?, slowed down loading). - Jython code (ancient, didn't work). - Compat support for python versions < 2.7. - Additions - Support for TACK via TACKpy. - Support for `CertificateRequest.certificate_authorities` ("reqCAs") - Added TLSConnection.shutdown() to better mimic socket. - Enabled Session resumption for XMLRPCTransport. 0.3.8 - 2/21/2005 - Added support for poplib, imaplib, and smtplib - Added python 2.4 windows installer - Fixed occassional timing problems with test suite 0.3.7 - 10/05/2004 - Added support for Python 2.2 - Cleaned up compatibility code, and docs, a bit 0.3.6 - 9/28/2004 - Fixed script installation on UNIX - Give better error message on old Python versions 0.3.5 - 9/16/2004 - TLS 1.1 support - os.urandom() support - Fixed win32prng on some systems 0.3.4 - 9/12/2004 - Updated for TLS/SRP draft 8 - Bugfix: was setting `_versioncheck` on SRP 1st hello, causing problems with GnuTLS (which was offering TLS 1.1) - Removed `_versioncheck` checking, since it could cause interop problems - Minor bugfix: when `cryptlib_py` and and cryptoIDlib present, cryptlib was complaining about being initialized twice 0.3.3 - 6/10/2004 - Updated for TLS/SRP draft 7 - Updated test cryptoID cert chains for cryptoIDlib 0.3.1 0.3.2 - 5/21/2004 - fixed bug when handling multiple handshake messages per record (e.g. IIS) 0.3.1 - 4/21/2004 - added xmlrpclib integration - fixed hanging bug in Twisted integration - fixed win32prng to work on a wider range of win32 sytems - fixed import problem with cryptoIDlib - fixed port allocation problem when test scripts are run on some UNIXes - made tolerant of buggy IE sending wrong version in premaster secret 0.3.0 - 3/20/2004 - added API docs thanks to epydoc - added X.509 path validation via cryptlib - much cleaning/tweaking/re-factoring/minor fixes 0.2.7 - 3/12/2004 - changed Twisted error handling to use connectionLost() - added ignoreAbruptClose 0.2.6 - 3/11/2004 - added Twisted errorHandler - added TLSAbruptCloseError - added 'integration' subdirectory 0.2.5 - 3/10/2004 - improved asynchronous support a bit - added first-draft of Twisted support 0.2.4 - 3/5/2004 - cleaned up asyncore support - added proof-of-concept for Twisted 0.2.3 - 3/4/2004 - added pycrypto RSA support - added asyncore support 0.2.2 - 3/1/2004 - added GMPY support - added pycrypto support - added support for PEM-encoded private keys, in pure python 0.2.1 - 2/23/2004 - improved PRNG use (cryptlib, or /dev/random, or CryptoAPI) - added RSA blinding, to avoid timing attacks - don't install local copy of M2Crypto, too problematic 0.2.0 - 2/19/2004 - changed VerifierDB to take per-user parameters - renamed `tls_lite` -> tlslite 0.1.9 - 2/16/2004 - added post-handshake 'Checker' - made compatible with Python 2.2 - made more forgiving of abrupt closure, since everyone does it: if the socket is closed while sending/recv'ing `close_notify`, just ignore it. 0.1.8 - 2/12/2004 - TLSConnections now emulate sockets, including makefile() - HTTPTLSConnection and TLSMixIn simplified as a result 0.1.7 - 2/11/2004 - fixed httplib.HTTPTLSConnection with multiple requests - fixed SocketServer to handle `close_notify` - changed handshakeClientNoAuth() to ignore CertificateRequests - changed handshakeClient() to ignore non-resumable session arguments 0.1.6 - 2/10/2004 - fixed httplib support 0.1.5 - 2/09/2004 - added support for httplib and SocketServer - added support for SSLv3 - added support for 3DES - cleaned up read()/write() behavior - improved HMAC speed 0.1.4 - 2/06/2004 - fixed dumb bug in tls.py 0.1.3 - 2/05/2004 - change read() to only return requested number of bytes - added support for shared-key and in-memory databases - added support for PEM-encoded X.509 certificates - added support for SSLv2 ClientHello - fixed shutdown/re-handshaking behavior - cleaned up handling of `missing_srp_username` - renamed readString()/writeString() -> read()/write() - added documentation 0.1.2 - 2/04/2004 - added clienttest/servertest functions - improved OpenSSL cipher wrappers speed - fixed server when it has a key, but client selects plain SRP - fixed server to postpone errors until it has read client's messages - fixed ServerHello to only include extension data if necessary 0.1.1 - 2/02/2004 - fixed `close_notify` behavior - fixed handling of empty application data packets - fixed socket reads to not consume extra bytes - added testing functions to tls.py 0.1.0 - 2/01/2004 - first release tlslite-ng-0.5.1/build-requirements.txt000066400000000000000000000000341261665411200201610ustar00rootroot00000000000000pylint diff_cover coveralls tlslite-ng-0.5.1/scripts/000077500000000000000000000000001261665411200152725ustar00rootroot00000000000000tlslite-ng-0.5.1/scripts/tls.py000077500000000000000000000265101261665411200164550ustar00rootroot00000000000000#!/usr/bin/env python # Authors: # Trevor Perrin # Marcelo Fernandez - bugfix and NPN support # Martin von Loewis - python 3 port # # See the LICENSE file for legal information regarding use of this file. from __future__ import print_function import sys import os import os.path import socket import time import getopt try: import httplib from SocketServer import * from BaseHTTPServer import * from SimpleHTTPServer import * except ImportError: # Python 3.x from http import client as httplib from socketserver import * from http.server import * from http.server import SimpleHTTPRequestHandler if __name__ != "__main__": raise "This must be run as a command, not used as a module!" from tlslite.api import * from tlslite.constants import CipherSuite from tlslite import __version__ try: from tack.structures.Tack import Tack except ImportError: pass def printUsage(s=None): if s: print("ERROR: %s" % s) print("") print("Version: %s" % __version__) print("") print("RNG: %s" % prngName) print("") print("Modules:") if tackpyLoaded: print(" tackpy : Loaded") else: print(" tackpy : Not Loaded") if m2cryptoLoaded: print(" M2Crypto : Loaded") else: print(" M2Crypto : Not Loaded") if pycryptoLoaded: print(" pycrypto : Loaded") else: print(" pycrypto : Not Loaded") if gmpyLoaded: print(" GMPY : Loaded") else: print(" GMPY : Not Loaded") print("") print("""Commands: server [-k KEY] [-c CERT] [-t TACK] [-v VERIFIERDB] [-d DIR] [--reqcert] HOST:PORT client [-k KEY] [-c CERT] [-u USER] [-p PASS] HOST:PORT """) sys.exit(-1) def printError(s): """Print error message and exit""" sys.stderr.write("ERROR: %s\n" % s) sys.exit(-1) def handleArgs(argv, argString, flagsList=[]): # Convert to getopt argstring format: # Add ":" after each arg, ie "abc" -> "a:b:c:" getOptArgString = ":".join(argString) + ":" try: opts, argv = getopt.getopt(argv, getOptArgString, flagsList) except getopt.GetoptError as e: printError(e) # Default values if arg not present privateKey = None certChain = None username = None password = None tacks = None verifierDB = None reqCert = False directory = None for opt, arg in opts: if opt == "-k": s = open(arg, "rb").read() if sys.version_info[0] >= 3: s = str(s, 'utf-8') privateKey = parsePEMKey(s, private=True) elif opt == "-c": s = open(arg, "rb").read() if sys.version_info[0] >= 3: s = str(s, 'utf-8') x509 = X509() x509.parse(s) certChain = X509CertChain([x509]) elif opt == "-u": username = arg elif opt == "-p": password = arg elif opt == "-t": if tackpyLoaded: s = open(arg, "rU").read() tacks = Tack.createFromPemList(s) elif opt == "-v": verifierDB = VerifierDB(arg) verifierDB.open() elif opt == "-d": directory = arg elif opt == "--reqcert": reqCert = True else: assert(False) if not argv: printError("Missing address") if len(argv)>1: printError("Too many arguments") #Split address into hostname/port tuple address = argv[0] address = address.split(":") if len(address) != 2: raise SyntaxError("Must specify :") address = ( address[0], int(address[1]) ) # Populate the return list retList = [address] if "k" in argString: retList.append(privateKey) if "c" in argString: retList.append(certChain) if "u" in argString: retList.append(username) if "p" in argString: retList.append(password) if "t" in argString: retList.append(tacks) if "v" in argString: retList.append(verifierDB) if "d" in argString: retList.append(directory) if "reqcert" in flagsList: retList.append(reqCert) return retList def printGoodConnection(connection, seconds): print(" Handshake time: %.3f seconds" % seconds) print(" Version: %s" % connection.getVersionName()) print(" Cipher: %s %s" % (connection.getCipherName(), connection.getCipherImplementation())) print(" Ciphersuite: {0}".\ format(CipherSuite.ietfNames[connection.session.cipherSuite])) if connection.session.srpUsername: print(" Client SRP username: %s" % connection.session.srpUsername) if connection.session.clientCertChain: print(" Client X.509 SHA1 fingerprint: %s" % connection.session.clientCertChain.getFingerprint()) else: print(" No client certificate provided by peer") if connection.session.serverCertChain: print(" Server X.509 SHA1 fingerprint: %s" % connection.session.serverCertChain.getFingerprint()) if connection.session.serverName: print(" SNI: %s" % connection.session.serverName) if connection.session.tackExt: if connection.session.tackInHelloExt: emptyStr = "\n (via TLS Extension)" else: emptyStr = "\n (via TACK Certificate)" print(" TACK: %s" % emptyStr) print(str(connection.session.tackExt)) print(" Next-Protocol Negotiated: %s" % connection.next_proto) print(" Encrypt-then-MAC: {0}".format(connection.encryptThenMAC)) def clientCmd(argv): (address, privateKey, certChain, username, password) = \ handleArgs(argv, "kcup") if (certChain and not privateKey) or (not certChain and privateKey): raise SyntaxError("Must specify CERT and KEY together") if (username and not password) or (not username and password): raise SyntaxError("Must specify USER with PASS") if certChain and username: raise SyntaxError("Can use SRP or client cert for auth, not both") #Connect to server sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(address) connection = TLSConnection(sock) settings = HandshakeSettings() settings.useExperimentalTackExtension = True try: start = time.clock() if username and password: connection.handshakeClientSRP(username, password, settings=settings, serverName=address[0]) else: connection.handshakeClientCert(certChain, privateKey, settings=settings, serverName=address[0]) stop = time.clock() print("Handshake success") except TLSLocalAlert as a: if a.description == AlertDescription.user_canceled: print(str(a)) else: raise sys.exit(-1) except TLSRemoteAlert as a: if a.description == AlertDescription.unknown_psk_identity: if username: print("Unknown username") else: raise elif a.description == AlertDescription.bad_record_mac: if username: print("Bad username or password") else: raise elif a.description == AlertDescription.handshake_failure: print("Unable to negotiate mutually acceptable parameters") else: raise sys.exit(-1) printGoodConnection(connection, stop-start) connection.close() def serverCmd(argv): (address, privateKey, certChain, tacks, verifierDB, directory, reqCert) = handleArgs(argv, "kctbvd", ["reqcert"]) if (certChain and not privateKey) or (not certChain and privateKey): raise SyntaxError("Must specify CERT and KEY together") if tacks and not certChain: raise SyntaxError("Must specify CERT with Tacks") print("I am an HTTPS test server, I will listen on %s:%d" % (address[0], address[1])) if directory: os.chdir(directory) print("Serving files from %s" % os.getcwd()) if certChain and privateKey: print("Using certificate and private key...") if verifierDB: print("Using verifier DB...") if tacks: print("Using Tacks...") if reqCert: print("Asking for client certificates...") ############# sessionCache = SessionCache() username = None class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn, HTTPServer): def handshake(self, connection): print("About to handshake...") activationFlags = 0 if tacks: if len(tacks) == 1: activationFlags = 1 elif len(tacks) == 2: activationFlags = 3 try: start = time.clock() settings = HandshakeSettings() settings.useExperimentalTackExtension=True connection.handshakeServer(certChain=certChain, privateKey=privateKey, verifierDB=verifierDB, tacks=tacks, activationFlags=activationFlags, sessionCache=sessionCache, settings=settings, nextProtos=[b"http/1.1"], reqCert=reqCert) # As an example (does not work here): #nextProtos=[b"spdy/3", b"spdy/2", b"http/1.1"]) stop = time.clock() except TLSRemoteAlert as a: if a.description == AlertDescription.user_canceled: print(str(a)) return False else: raise except TLSLocalAlert as a: if a.description == AlertDescription.unknown_psk_identity: if username: print("Unknown username") return False else: raise elif a.description == AlertDescription.bad_record_mac: if username: print("Bad username or password") return False else: raise elif a.description == AlertDescription.handshake_failure: print("Unable to negotiate mutually acceptable parameters") return False else: raise connection.ignoreAbruptClose = True printGoodConnection(connection, stop-start) return True httpd = MyHTTPServer(address, SimpleHTTPRequestHandler) httpd.serve_forever() if __name__ == '__main__': if len(sys.argv) < 2: printUsage("Missing command") elif sys.argv[1] == "client"[:len(sys.argv[1])]: clientCmd(sys.argv[2:]) elif sys.argv[1] == "server"[:len(sys.argv[1])]: serverCmd(sys.argv[2:]) else: printUsage("Unknown command: %s" % sys.argv[1]) tlslite-ng-0.5.1/scripts/tlsdb.py000077500000000000000000000072631261665411200167670ustar00rootroot00000000000000#!/usr/bin/env python # Authors: # Trevor Perrin # Martin von Loewis - python 3 port # # See the LICENSE file for legal information regarding use of this file. from __future__ import print_function import sys import os import socket import math if __name__ != "__main__": raise "This must be run as a command, not used as a module!" from tlslite import * from tlslite import __version__ if len(sys.argv) == 1 or (len(sys.argv)==2 and sys.argv[1].lower().endswith("help")): print("") print("Version: %s" % __version__) print("") print("RNG: %s" % prngName) print("") print("Modules:") if m2cryptoLoaded: print(" M2Crypto : Loaded") else: print(" M2Crypto : Not Loaded") if pycryptoLoaded: print(" pycrypto : Loaded") else: print(" pycrypto : Not Loaded") if gmpyLoaded: print(" GMPY : Loaded") else: print(" GMPY : Not Loaded") print("") print("Commands:") print("") print(" createsrp ") print("") print(" add []") print(" del ") print(" check []") print(" list ") sys.exit() cmd = sys.argv[1].lower() class Args: def __init__(self, argv): self.argv = argv def get(self, index): if len(self.argv)<=index: raise SyntaxError("Not enough arguments") return self.argv[index] def getLast(self, index): if len(self.argv)>index+1: raise SyntaxError("Too many arguments") return self.get(index) args = Args(sys.argv) def reformatDocString(s): lines = s.splitlines() newLines = [] for line in lines: newLines.append(" " + line.strip()) return "\n".join(newLines) try: if cmd == "help": command = args.getLast(2).lower() if command == "valid": print("") else: print("Bad command: '%s'" % command) elif cmd == "createsrp": dbName = args.get(2) db = VerifierDB(dbName) db.create() elif cmd == "add": dbName = args.get(2) username = args.get(3) password = args.get(4) db = VerifierDB(dbName) db.open() if username in db: print("User already in database!") sys.exit() bits = int(args.getLast(5)) N, g, salt, verifier = VerifierDB.makeVerifier(username, password, bits) db[username] = N, g, salt, verifier elif cmd == "del": dbName = args.get(2) username = args.getLast(3) db = VerifierDB(dbName) db.open() del(db[username]) elif cmd == "check": dbName = args.get(2) username = args.get(3) if len(sys.argv)>=5: password = args.getLast(4) else: password = None db = VerifierDB(dbName) db.open() try: db[username] print("Username exists") if password: if db.check(username, password): print("Password is correct") else: print("Password is wrong") except KeyError: print("Username does not exist") sys.exit() elif cmd == "list": dbName = args.get(2) db = VerifierDB(dbName) db.open() print("Verifier Database") def numBits(n): if n==0: return 0 return int(math.floor(math.log(n, 2))+1) for username in db.keys(): N, g, s, v = db[username] print(numBits(N), username) else: print("Bad command: '%s'" % cmd) except: raise tlslite-ng-0.5.1/setup.py000077500000000000000000000026341261665411200153250ustar00rootroot00000000000000#!/usr/bin/env python # Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. from distutils.core import setup setup(name="tlslite-ng", version="0.5.1", author="Hubert Kario", author_email="hkario@redhat.com", url="https://github.com/tomato42/tlslite-ng", description="Pure python implementation of SSL and TLS.", license="LGPLv2", scripts=["scripts/tls.py", "scripts/tlsdb.py"], packages=["tlslite", "tlslite.utils", "tlslite.integration"], package_data={ 'package1': ['LICENSE', 'README.md']}, obsoletes=["tlslite"], classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Topic :: Security :: Cryptography', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: System :: Networking' ], keywords="ssl, tls, pure-python" ) tlslite-ng-0.5.1/tests/000077500000000000000000000000001261665411200147455ustar00rootroot00000000000000tlslite-ng-0.5.1/tests/TACK1.pem000066400000000000000000000005051261665411200162530ustar00rootroot00000000000000Created by tack.py 0.9.9 Created at 2015-03-03T21:10:40Z -----BEGIN TACK----- QjeXykNtxc22jUSQHO32Uq0HVfg9jKEUHNlqfxtpPAaiJ3mA1O+lI2ZDs8uS+Zdb /uxswqubymN2Fg5ujrQr2QAAAbtbWsbjtgd+sy84p8w/aiD1a08WGz1A6IsN36Hs Z5wb70F54Y8N6jZxJp9PZcT4MnFiv58MxrmJAQnIlqx6y8TEzHMS7zj2uu6d+m1N HhZK/UtLpncs2wwqw8PI5Ea9kcYQXw== -----END TACK----- tlslite-ng-0.5.1/tests/TACK2.pem000066400000000000000000000005051261665411200162540ustar00rootroot00000000000000Created by tack.py 0.9.9 Created at 2015-03-03T21:10:41Z -----BEGIN TACK----- 9C+V4IbqF3l/GsK4y1X2wWhis+f2Jw4pX2bCpB22NFDdUV+QiI5Ztpvd1jQFtIH+ ayr7ycK52KsU7/rRYjX7JwAAAbtbWsbjtgd+sy84p8w/aiD1a08WGz1A6IsN36Hs Z5wb70F5bmWSnoZ8rqEHkLbqmqjiM+XCDZW+57MUqzT5sid4mkS00g7hMZCFw1q+ pmv6B7SqyKd5IumU/bdmqt3g/g6xzw== -----END TACK----- tlslite-ng-0.5.1/tests/TACK_Key1.pem000066400000000000000000000005051261665411200170630ustar00rootroot00000000000000Created by tack.py 0.9.9 Created at 2015-03-03T21:09:21Z -----BEGIN TACK PRIVATE KEY----- AQAAIABwzwuq6r+poACwbpVJ/DI9GWMdPNh2r5C+khOxccgvTtnb36Rz+YRvdWM5 uFwZKh5CN5fKQ23FzbaNRJAc7fZSrQdV+D2MoRQc2Wp/G2k8BqIneYDU76UjZkOz y5L5l1v+7GzCq5vKY3YWDm6OtCvZCEvqRIl7bOvhNKa608Xj+49YlJeVzFxlX8Sb HN1BlEU= -----END TACK PRIVATE KEY----- tlslite-ng-0.5.1/tests/TACK_Key2.pem000066400000000000000000000005051261665411200170640ustar00rootroot00000000000000Created by tack.py 0.9.9 Created at 2015-03-03T21:09:21Z -----BEGIN TACK PRIVATE KEY----- AQAAIABrNu+M/CwjNVTJe4gD7vow75Bws7ggXZNKl33QAAh81VS+tKgOiCPuLwYS ft6djk70L5XghuoXeX8awrjLVfbBaGKz5/YnDilfZsKkHbY0UN1RX5CIjlm2m93W NAW0gf5rKvvJwrnYqxTv+tFiNfsnllxaTH/Pi0hoCjdGNxqFnTGu53H6TYzpU7aK 3U4t/nA= -----END TACK PRIVATE KEY----- tlslite-ng-0.5.1/tests/TACKs.pem000066400000000000000000000012121261665411200163510ustar00rootroot00000000000000Created by TACK.py 0.9.6 Created at 2012-05-08T15:53:56Z -----BEGIN TACK----- lJ7JcxIC9y6i/jTkTh+MXf0aO23J58PjUQCAI4vCMINlcMGSC8Vyq9On51hk5zAz DlIdXzC7zcUC7AN7/alXYwAAAkJ0Bb8+RaM9YEywaJEGViKJJmpYG/gJHgfGaefI 9kKbXSDmXHI2tbZPnCxzR4ZXz21HxFm1SPYijTKm4zm5dAzXzvneOTRf/SFbY0dZ s7UpHKK4yOhREoGH8z8kxxD5/BXb5A== -----END TACK----- Created by tack.py 0.9.7 Created at 2012-08-31T19:15:38Z -----BEGIN TACK----- x7MspJSqsflA4qZ6qG8r8Hd5AB0+BB09n96vuF5Z1ayBeGzrny90WeCu3E3G2d8Y 620TVYfnALIaMg//MZ8ovQAAAkMcxb8+RaM9YEywaJEGViKJJmpYG/gJHgfGaefI 9kKbXSDmq3tmkncGftPeMAUh3T2vcXNiRnRqyFArnCKr2gCSj2vsCiS3F+qVhaUv OyxAcIhminamGruajdPSXMtlCCwWag== -----END TACK----- tlslite-ng-0.5.1/tests/TACKunrelated.pem000066400000000000000000000005051261665411200200760ustar00rootroot00000000000000Created by TACK.py 0.9.6 Created at 2012-05-08T17:12:57Z -----BEGIN TACK----- lJ7JcxIC9y6i/jTkTh+MXf0aO23J58PjUQCAI4vCMINlcMGSC8Vyq9On51hk5zAz DlIdXzC7zcUC7AN7/alXYwMFAchwkzK2S2ZyeiBj5AZvO5WMsKruV2pezv2VM5m7 iHRzHZWHnUVusrs/d04QnVS2Btmt5hECAKdcWK0qZHnMxhZhom9DExiqLQW0A05E xHvWKhN8y6J9UATLvGjjm3U7oyNxzQ== -----END TACK----- tlslite-ng-0.5.1/tests/clientX509Cert.pem000066400000000000000000000016341261665411200201360ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIICfjCCAecCCQDgdeCLz7d92jANBgkqhkiG9w0BAQUFADCBgDELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQHEwdPYWtsYW5kMQ8wDQYDVQQKEwZUcmV2 Q28xCzAJBgNVBAsTAkNBMRIwEAYDVQQDEwlUcmV2Q28gQ0ExIDAeBgkqhkiG9w0B CQEWEXRsc2xpdGVAdHJldnAubmV0MB4XDTEyMDIwNjAxMDMxMVoXDTQyMDEyOTAx MDMxMVowgYUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEBxMHT2Fr bGFuZDEPMA0GA1UEChMGVHJldkNvMQwwCgYDVQQLFANSJkQxFjAUBgNVBAMTDVRy ZXZvciBQZXJyaW4xIDAeBgkqhkiG9w0BCQEWEXRsc2xpdGVAdHJldnAubmV0MIGf MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrncYzz0HWlT3ELBTZ1Jt/ZDFWlHW9 SrhlwR5Pd8bEonXCxhomAssz4SkJMaByGFyAUAT4bqf41PV50y5lkYVfgUIy8qQS Gd/qJrNuFa6odWt3MExQdVCXKTOdjYQmhwvXo6zlf7u/Sj5NICAWmXRHZFBljqG8 QasxPrLSbWUtWQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAJRqUTQ6pZ71rm46+qXo Sqc5tnj42uIn1eIfnm5pbPxXFvuP3RbuHVO140+LQL844c2JCOKeu9dQPKxoRNU7 sQLdo8+o+KboiYif6m9Ial4ss0I5MhPPVi9heguRbcFHx+87q8xdN7vd6Wn2gw/l IV+b9EUQMWYFBCokR8o7o+IV -----END CERTIFICATE----- tlslite-ng-0.5.1/tests/clientX509Key.pem000066400000000000000000000015731261665411200177730ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXwIBAAKBgQCrncYzz0HWlT3ELBTZ1Jt/ZDFWlHW9SrhlwR5Pd8bEonXCxhom Assz4SkJMaByGFyAUAT4bqf41PV50y5lkYVfgUIy8qQSGd/qJrNuFa6odWt3MExQ dVCXKTOdjYQmhwvXo6zlf7u/Sj5NICAWmXRHZFBljqG8QasxPrLSbWUtWQIDAQAB AoGBAIo99cMWWTq5zZ+QwtsV4Iyl8PiFbrKk1hVhk0EAgyUymRnk6ntkFLwdzCiT yWMfLKRP0TEuMjDHm1YfP2ih6ITMh83SLF4og0dDzU+tn3PX/XWaoYZr8HsFTDMi FhrWXXBX8ST3F2aEyOxXZj8SGFu5YIJ8iemztANzZVSvYkWBAkEA2EbI6rnpXc5t fKhdkXa0c8l+Uyn/8JadHJ8sobzNhZ/CeC0a9PwhZLQvsRgNXXjYUFJ7N2igoIrk y0e8f+iD/QJBAMsjFrsrhmXcksf4RX6ZAg1rcJS115JjH2EnbbWd2nBLHUBwywGh VJhNyQRWBNlVGMrYWCQwHcZNOdUsvmLX140CQQC5v2GDpxQ0irwh2gAylH67CwzB pEq6eMCK+nI8nojtAJ7m0+ZZDcooUVC8imnAI6+0nIJSvjtmZqPFquDMAgiJAkEA ox/mXR+yqZHbfSHuDJ+qekRQ/9qW5kMbK9WR0EqW454uO1VYcFKxsCymxAiflDc1 1Y6uGUFaZ6gUTQ/FQ3K48QJBAIlPKkD8VfNg/3B6wq7gmqRIy9MblLLZo2Qh4nZA 4icCv/vpHH+o6Ccxad03LQKsTNJl3z52G4sKgASudWiBbLs= -----END RSA PRIVATE KEY----- tlslite-ng-0.5.1/tests/httpsclient.py000077500000000000000000000005071261665411200176650ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function from tlslite import HTTPTLSConnection, HandshakeSettings settings = HandshakeSettings() settings.useExperimentalTackExtension = True h = HTTPTLSConnection("localhost", 4443, settings=settings) h.request("GET", "/index.html") r = h.getresponse() print(r.read()) tlslite-ng-0.5.1/tests/httpsserver.sh000077500000000000000000000001611261665411200176730ustar00rootroot00000000000000#!/bin/sh python ../scripts/tls.py server -k serverX509Key.pem -c serverX509Cert.pem -t TACK1.pem localhost:4443 tlslite-ng-0.5.1/tests/index.html000066400000000000000000000002301261665411200167350ustar00rootroot00000000000000 TLS Lite test server

TLS Lite test server

I am a TLS Lite HTTPS test server
tlslite-ng-0.5.1/tests/serverX509Cert.pem000066400000000000000000000021061261665411200201610ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIC+zCCAeOgAwIBAgIJALEP97+mvF6wMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV BAMMCWxvY2FsaG9zdDAeFw0xNTAzMDMxNTUzNDhaFw0yNTAyMjgxNTUzNDhaMBQx EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBAOYz+2ar/b83RjKoFvYG7Zff7N0BT7qWdSzP6k03r2czbRo0+L1Afh6TLGz/ H66PftmXDaEPADmmJ8DjiQW05eTDV7HiVLXHG1YK4a7VhdMODFLUjV1w1rip1i/C WAEbLHZPQZ0gN4bexY1Y7HY5cF5brCbRFHGpSHIj1Jdo3zJBawJpqM9nHDz04nVd pJKFat8PbGrrGZBhXpg/opwg5GVAbbVroG8Nt7ATeIzY4YCaUS/9rs7J0qV6tatz egUEGg6aCdsu4FrGQFA2/RRCtEF8H5A6gPdwTex4COR+RVXbaWUnlwyfTRjAVF2x FiWA0WShLeTblixMxv42qUZidHUCAwEAAaNQME4wHQYDVR0OBBYEFFpCTwDYmMcu zo+fxFRZJTIChcHqMB8GA1UdIwQYMBaAFFpCTwDYmMcuzo+fxFRZJTIChcHqMAwG A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBABF66SE6d9Px5nTjuwvq3112 Vw3l1ejJFwauYY36ctPnuNgG0V0A7Hs5ksAvsSe+SGedzASr9vRDy/aZ007Voo4a tmP3vVtyjT2EHSXWFxLRNVr+vPnFCEBjPVpbmRNeB5odGjFh9oG6UmcdvlS/4rAI pVOeGw0kx/yXpGRkVB6kc+XcsEi45ERIEYBBdE1RU04y/Q0YfPi6jXF6hTV7bIoH LEKV/td6Edje2ihjlz33amR8qOv6wH1vUiRj3VRp+aegnigTq3S+FO9mmh7tP/Wg DOOM6qxItHCcnphOYzY2gaQM5YnsLVmr3HUn/IEUT4uf2VjDNz/a7JWebscQsZI= -----END CERTIFICATE----- tlslite-ng-0.5.1/tests/serverX509Key.pem000066400000000000000000000032501261665411200200150ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDmM/tmq/2/N0Yy qBb2Bu2X3+zdAU+6lnUsz+pNN69nM20aNPi9QH4ekyxs/x+uj37Zlw2hDwA5pifA 44kFtOXkw1ex4lS1xxtWCuGu1YXTDgxS1I1dcNa4qdYvwlgBGyx2T0GdIDeG3sWN WOx2OXBeW6wm0RRxqUhyI9SXaN8yQWsCaajPZxw89OJ1XaSShWrfD2xq6xmQYV6Y P6KcIORlQG21a6BvDbewE3iM2OGAmlEv/a7OydKlerWrc3oFBBoOmgnbLuBaxkBQ Nv0UQrRBfB+QOoD3cE3seAjkfkVV22llJ5cMn00YwFRdsRYlgNFkoS3k25YsTMb+ NqlGYnR1AgMBAAECggEAUCu6Wj9716RAZlPz6yrug/4QV8elJK5RkJG4X7wM8jwO uxnHpuFXCv7mce9H8Vs4Kj9ZF8ZJpcof/iVACyS9C7acS+8u4T++XXDcuC7UtHQo BpDPysMJhLZhSbC9RWVZTrq7dyVJMUdUNa3KbEIEyFfU1I/sNsll2Zpw52o2kSFe Ip1TGcnVmFu0uKxPrlNLSSNOVQqz2fOYWBJLk98gk54HAkHpFk92FVorn17seAfS ksF70B9X6MBUa6PDSgQfKCwGd27KBpTivx6d8QVtMNqrFq/cqZ7TwWDIq1atZ0aF 3mYXfXR0toRyYZEXaa14Ao7iCUt5D8d2IG9u3q88AQKBgQD5x6kiqO+ApY7V0S0g SyaIdTBjYc9Rbb0qvgy0Mhq68Ekc2fBIdTLc+G9ajkVFIe5blZc0nvwgSLdRfWrJ bFpX8SS9Aelgp0mcfXgfIpJmLrPijtgEipTCh/73GTJM3ZnHI1z6xrRP0hi1ww2Q Z8oqF34H6glXfYHfMqy9VaGQ4QKBgQDr74T4BxiXK4dIQ0T4oRE/881dScrVz9Ok 3wPINa5bIvfqHPl3eAJgRYBkxjKRyxt29wvGtWBQvCTHvFgF9F+3v3mfXJPRHaZZ e1VJn9Eqjz1KuArIOwSrmnCFrd9jim10Qo36AFU0myridllN/NQn4l7yYgnw2a1/ WbLYq2nSFQKBgAkJWyog2IFb+/3qUmqfrWY0byq5SCnXAYgBVi5SvbrTpKGBlPra Gpv59PVevkzQ/HGdyNmjgtWcK92r3ugonmAeHkkkP5A6nSQnOehOdONzfxiMOG55 oQYkq2m/JJ25Sq30rpF4DN/yZuh0hRIbXyoErY+VvP7IUKGFkNBMv8qhAoGBANDV pLPJzClanRcIfA86ukMKMPfm7kQM/gAMapOXeGow7JHr7aCiuC+wtTH+ARrtVbUa fPD48HTl5ARroNo8cVD6idPWJPzPKsQ/l8FgVcs/GHh/qQOMwdiHDhw1R+sax0FF +9eS3dh/lBj5uph+NufKxlHzF2t5sclsgxKnvzX1AoGAZlNZt2xn3q/kusUXLovS WN8C3ty06qLbD99kiWqEC2gSXc94rk7K7R/1XgfxXV8uOA9eUPDBpchd9PUnhwBE tnkuQZ0fZ1P6EpNTumeL/UvIaA2UFtqrzxxJPJQExPRqX5foT6FhXVtGrNGKw78C Ft7IqSkjX742rx0ephmvZgE= -----END PRIVATE KEY----- tlslite-ng-0.5.1/tests/tlstest.py000077500000000000000000001207351261665411200170340ustar00rootroot00000000000000#!/usr/bin/env python # Authors: # Trevor Perrin # Kees Bos - Added tests for XML-RPC # Dimitris Moraitis - Anon ciphersuites # Marcelo Fernandez - Added test for NPN # Martin von Loewis - python 3 port # Hubert Kario - several improvements # Google - FALLBACK_SCSV test # # See the LICENSE file for legal information regarding use of this file. from __future__ import print_function import sys import os import os.path import socket import time import getopt try: from BaseHTTPServer import HTTPServer from SimpleHTTPServer import SimpleHTTPRequestHandler except ImportError: from http.server import HTTPServer, SimpleHTTPRequestHandler from tlslite import TLSConnection, Fault, HandshakeSettings, \ X509, X509CertChain, IMAP4_TLS, VerifierDB, Session, SessionCache, \ parsePEMKey, constants, \ AlertDescription, HTTPTLSConnection, TLSSocketServerMixIn, \ POP3_TLS, m2cryptoLoaded, pycryptoLoaded, gmpyLoaded, tackpyLoaded, \ Checker, __version__ from tlslite.errors import * from tlslite.utils.cryptomath import prngName try: import xmlrpclib except ImportError: # Python 3 from xmlrpc import client as xmlrpclib import ssl from tlslite import * try: from tack.structures.Tack import Tack except ImportError: pass def printUsage(s=None): if m2cryptoLoaded: crypto = "M2Crypto/OpenSSL" else: crypto = "Python crypto" if s: print("ERROR: %s" % s) print("""\ntls.py version %s (using %s) Commands: server HOST:PORT DIRECTORY client HOST:PORT DIRECTORY """ % (__version__, crypto)) sys.exit(-1) def testConnClient(conn): b1 = os.urandom(1) b10 = os.urandom(10) b100 = os.urandom(100) b1000 = os.urandom(1000) conn.write(b1) conn.write(b10) conn.write(b100) conn.write(b1000) assert(conn.read(min=1, max=1) == b1) assert(conn.read(min=10, max=10) == b10) assert(conn.read(min=100, max=100) == b100) assert(conn.read(min=1000, max=1000) == b1000) def clientTestCmd(argv): address = argv[0] dir = argv[1] #Split address into hostname/port tuple address = address.split(":") address = ( address[0], int(address[1]) ) #open synchronisation FIFO synchro = socket.socket(socket.AF_INET, socket.SOCK_STREAM) synchro.settimeout(5) synchro.connect((address[0], address[1]-1)) def connect(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if hasattr(sock, 'settimeout'): #It's a python 2.3 feature sock.settimeout(5) sock.connect(address) c = TLSConnection(sock) return c test_no = 0 badFault = False print("Test {0} - anonymous handshake".format(test_no)) synchro.recv(1) connection = connect() connection.handshakeClientAnonymous() testConnClient(connection) connection.close() test_no += 1 print("Test {0} - good X509 (plus SNI)".format(test_no)) synchro.recv(1) connection = connect() connection.handshakeClientCert(serverName=address[0]) testConnClient(connection) assert(isinstance(connection.session.serverCertChain, X509CertChain)) assert(connection.session.serverName == address[0]) assert(connection.session.cipherSuite in constants.CipherSuite.aeadSuites) assert(connection.encryptThenMAC == False) connection.close() test_no += 1 print("Test {0} - good X509, SSLv3".format(test_no)) synchro.recv(1) connection = connect() settings = HandshakeSettings() settings.minVersion = (3,0) settings.maxVersion = (3,0) connection.handshakeClientCert(settings=settings) testConnClient(connection) assert(isinstance(connection.session.serverCertChain, X509CertChain)) connection.close() test_no += 1 print("Test {0} - good X509, RC4-MD5".format(test_no)) synchro.recv(1) connection = connect() settings = HandshakeSettings() settings.macNames = ["md5"] settings.cipherNames = ["rc4"] connection.handshakeClientCert(settings=settings) testConnClient(connection) assert(isinstance(connection.session.serverCertChain, X509CertChain)) assert(connection.session.cipherSuite == constants.CipherSuite.TLS_RSA_WITH_RC4_128_MD5) assert(connection.encryptThenMAC == False) connection.close() if tackpyLoaded: settings = HandshakeSettings() settings.useExperimentalTackExtension = True test_no += 1 print("Test {0} - good X.509, TACK".format(test_no)) synchro.recv(1) connection = connect() connection.handshakeClientCert(settings=settings) assert(connection.session.tackExt.tacks[0].getTackId() == "5lcbe.eyweo.yxuan.rw6xd.jtoz7") assert(connection.session.tackExt.activation_flags == 1) testConnClient(connection) connection.close() test_no += 1 print("Test {0} - good X.509, TACK unrelated to cert chain".\ format(test_no)) synchro.recv(1) connection = connect() try: connection.handshakeClientCert(settings=settings) assert(False) except TLSLocalAlert as alert: if alert.description != AlertDescription.illegal_parameter: raise connection.close() else: test_no += 1 print("Test {0} - good X.509, TACK...skipped (no tackpy)".\ format(test_no)) test_no += 1 print("Test {0} - good X.509, TACK unrelated to cert chain...skipped" " (no tackpy)".\ format(test_no)) test_no += 1 print("Test {0} - good SRP".format(test_no)) synchro.recv(1) connection = connect() connection.handshakeClientSRP("test", "password") testConnClient(connection) connection.close() test_no += 1 print("Test {0} - SRP faults".format(test_no)) for fault in Fault.clientSrpFaults + Fault.genericFaults: synchro.recv(1) connection = connect() connection.fault = fault try: connection.handshakeClientSRP("test", "password") print(" Good Fault %s" % (Fault.faultNames[fault])) except TLSFaultError as e: print(" BAD FAULT %s: %s" % (Fault.faultNames[fault], str(e))) badFault = True test_no += 1 print("Test {0} - good SRP: with X.509 certificate, TLSv1.0".format(test_no)) settings = HandshakeSettings() settings.minVersion = (3,1) settings.maxVersion = (3,1) synchro.recv(1) connection = connect() connection.handshakeClientSRP("test", "password", settings=settings) assert(isinstance(connection.session.serverCertChain, X509CertChain)) testConnClient(connection) connection.close() test_no += 1 print("Test {0} - X.509 with SRP faults".format(test_no)) for fault in Fault.clientSrpFaults + Fault.genericFaults: synchro.recv(1) connection = connect() connection.fault = fault try: connection.handshakeClientSRP("test", "password") print(" Good Fault %s" % (Fault.faultNames[fault])) except TLSFaultError as e: print(" BAD FAULT %s: %s" % (Fault.faultNames[fault], str(e))) badFault = True test_no += 1 print("Test {0} - X.509 faults".format(test_no)) for fault in Fault.clientNoAuthFaults + Fault.genericFaults: synchro.recv(1) connection = connect() connection.fault = fault try: connection.handshakeClientCert() print(" Good Fault %s" % (Fault.faultNames[fault])) except TLSFaultError as e: print(" BAD FAULT %s: %s" % (Fault.faultNames[fault], str(e))) badFault = True test_no += 1 print("Test {0} - good mutual X509".format(test_no)) x509Cert = X509().parse(open(os.path.join(dir, "clientX509Cert.pem")).read()) x509Chain = X509CertChain([x509Cert]) s = open(os.path.join(dir, "clientX509Key.pem")).read() x509Key = parsePEMKey(s, private=True) synchro.recv(1) connection = connect() connection.handshakeClientCert(x509Chain, x509Key) testConnClient(connection) assert(isinstance(connection.session.serverCertChain, X509CertChain)) connection.close() test_no += 1 print("Test {0} - good mutual X509, TLSv1.1".format(test_no)) synchro.recv(1) connection = connect() settings = HandshakeSettings() settings.minVersion = (3,2) settings.maxVersion = (3,2) connection.handshakeClientCert(x509Chain, x509Key, settings=settings) testConnClient(connection) assert(isinstance(connection.session.serverCertChain, X509CertChain)) connection.close() test_no += 1 print("Test {0} - good mutual X509, SSLv3".format(test_no)) synchro.recv(1) connection = connect() settings = HandshakeSettings() settings.minVersion = (3,0) settings.maxVersion = (3,0) connection.handshakeClientCert(x509Chain, x509Key, settings=settings) testConnClient(connection) assert(isinstance(connection.session.serverCertChain, X509CertChain)) connection.close() test_no += 1 print("Test {0} - mutual X.509 faults".format(test_no)) for fault in Fault.clientCertFaults + Fault.genericFaults: synchro.recv(1) connection = connect() connection.fault = fault try: connection.handshakeClientCert(x509Chain, x509Key) print(" Good Fault %s" % (Fault.faultNames[fault])) except TLSFaultError as e: print(" BAD FAULT %s: %s" % (Fault.faultNames[fault], str(e))) badFault = True test_no += 1 print("Test {0} - good SRP, prepare to resume... (plus SNI)".\ format(test_no)) synchro.recv(1) connection = connect() connection.handshakeClientSRP("test", "password", serverName=address[0]) testConnClient(connection) connection.close() session = connection.session test_no += 1 print("Test {0} - resumption (plus SNI)".format(test_no)) synchro.recv(1) connection = connect() connection.handshakeClientSRP("test", "garbage", serverName=address[0], session=session) testConnClient(connection) #Don't close! -- see below test_no += 1 print("Test {0} - invalidated resumption (plus SNI)".format(test_no)) synchro.recv(1) connection.sock.close() #Close the socket without a close_notify! synchro.recv(1) connection = connect() try: connection.handshakeClientSRP("test", "garbage", serverName=address[0], session=session) assert(False) except TLSRemoteAlert as alert: if alert.description != AlertDescription.bad_record_mac: raise connection.close() test_no += 1 print("Test {0} - HTTPS test X.509".format(test_no)) address = address[0], address[1]+1 if hasattr(socket, "timeout"): timeoutEx = socket.timeout else: timeoutEx = socket.error while 1: try: htmlBody = bytearray(open(os.path.join(dir, "index.html")).read(), "utf-8") fingerprint = None for y in range(2): checker =Checker(x509Fingerprint=fingerprint) h = HTTPTLSConnection(\ address[0], address[1], checker=checker) for x in range(3): synchro.recv(1) h.request("GET", "/index.html") r = h.getresponse() assert(r.status == 200) b = bytearray(r.read()) assert(b == htmlBody) fingerprint = h.tlsSession.serverCertChain.getFingerprint() assert(fingerprint) break except timeoutEx: print("timeout, retrying...") pass address = address[0], address[1]+1 implementations = [] if m2cryptoLoaded: implementations.append("openssl") if pycryptoLoaded: implementations.append("pycrypto") implementations.append("python") test_no += 1 print("Test {0} - different ciphers, TLSv1.0".format(test_no)) for implementation in implementations: for cipher in ["aes128", "aes256", "rc4"]: test_no += 1 print("Test {0}:".format(test_no), end=' ') synchro.recv(1) connection = connect() settings = HandshakeSettings() settings.cipherNames = [cipher] settings.cipherImplementations = [implementation, "python"] settings.minVersion = (3,1) settings.maxVersion = (3,1) connection.handshakeClientCert(settings=settings) testConnClient(connection) print("%s %s" % (connection.getCipherName(), connection.getCipherImplementation())) connection.close() test_no += 1 print("Test {0} - throughput test".format(test_no)) for implementation in implementations: for cipher in ["aes128gcm", "aes256gcm", "aes128", "aes256", "3des", "rc4"]: if cipher == "3des" and implementation not in ("openssl", "pycrypto"): continue if cipher in ("aes128gcm", "aes256gcm") and \ implementation not in ("pycrypto", "python"): continue test_no += 1 print("Test {0}:".format(test_no), end=' ') synchro.recv(1) connection = connect() settings = HandshakeSettings() settings.cipherNames = [cipher] settings.cipherImplementations = [implementation, "python"] connection.handshakeClientCert(settings=settings) print("%s %s:" % (connection.getCipherName(), connection.getCipherImplementation()), end=' ') startTime = time.clock() connection.write(b"hello"*10000) h = connection.read(min=50000, max=50000) stopTime = time.clock() if stopTime-startTime: print("100K exchanged at rate of %d bytes/sec" % int(100000/(stopTime-startTime))) else: print("100K exchanged very fast") assert(h == b"hello"*10000) connection.close() test_no += 1 print("Test {0} - Next-Protocol Client Negotiation".format(test_no)) synchro.recv(1) connection = connect() connection.handshakeClientCert(nextProtos=[b"http/1.1"]) #print(" Next-Protocol Negotiated: %s" % connection.next_proto) assert(connection.next_proto == b'http/1.1') connection.close() test_no += 1 print("Test {0} - Next-Protocol Client Negotiation".format(test_no)) synchro.recv(1) connection = connect() connection.handshakeClientCert(nextProtos=[b"spdy/2", b"http/1.1"]) #print(" Next-Protocol Negotiated: %s" % connection.next_proto) assert(connection.next_proto == b'spdy/2') connection.close() test_no += 1 print("Test {0} - Next-Protocol Client Negotiation".format(test_no)) synchro.recv(1) connection = connect() connection.handshakeClientCert(nextProtos=[b"spdy/2", b"http/1.1"]) #print(" Next-Protocol Negotiated: %s" % connection.next_proto) assert(connection.next_proto == b'spdy/2') connection.close() test_no += 1 print("Test {0} - Next-Protocol Client Negotiation".format(test_no)) synchro.recv(1) connection = connect() connection.handshakeClientCert(nextProtos=[b"spdy/3", b"spdy/2", b"http/1.1"]) #print(" Next-Protocol Negotiated: %s" % connection.next_proto) assert(connection.next_proto == b'spdy/2') connection.close() test_no += 1 print("Test {0} - Next-Protocol Client Negotiation".format(test_no)) synchro.recv(1) connection = connect() connection.handshakeClientCert(nextProtos=[b"spdy/3", b"spdy/2", b"http/1.1"]) #print(" Next-Protocol Negotiated: %s" % connection.next_proto) assert(connection.next_proto == b'spdy/3') connection.close() test_no += 1 print("Test {0} - Next-Protocol Client Negotiation".format(test_no)) synchro.recv(1) connection = connect() connection.handshakeClientCert(nextProtos=[b"http/1.1"]) #print(" Next-Protocol Negotiated: %s" % connection.next_proto) assert(connection.next_proto == b'http/1.1') connection.close() test_no += 1 print("Test {0} - Next-Protocol Client Negotiation".format(test_no)) synchro.recv(1) connection = connect() connection.handshakeClientCert(nextProtos=[b"spdy/2", b"http/1.1"]) #print(" Next-Protocol Negotiated: %s" % connection.next_proto) assert(connection.next_proto == b'spdy/2') connection.close() test_no += 1 print("Test {0} - FALLBACK_SCSV".format(test_no)) synchro.recv(1) connection = connect() settings = HandshakeSettings() settings.sendFallbackSCSV = True connection.handshakeClientCert(settings=settings) testConnClient(connection) connection.close() test_no += 1 print("Test {0} - FALLBACK_SCSV".format(test_no)) synchro.recv(1) connection = connect() settings = HandshakeSettings() settings.sendFallbackSCSV = True settings.maxVersion = (3, 2) try: connection.handshakeClientCert(settings=settings) assert() except TLSRemoteAlert as alert: if alert.description != AlertDescription.inappropriate_fallback: raise connection.close() test_no += 1 print("Test {0} - no EtM server side".format(test_no)) synchro.recv(1) connection = connect() settings = HandshakeSettings() settings.macNames.remove("aead") assert(settings.useEncryptThenMAC) connection.handshakeClientCert(serverName=address[0], settings=settings) testConnClient(connection) assert(isinstance(connection.session.serverCertChain, X509CertChain)) assert(connection.session.serverName == address[0]) assert(not connection.encryptThenMAC) connection.close() test_no += 1 print("Test {0} - no EtM client side".format(test_no)) synchro.recv(1) connection = connect() settings = HandshakeSettings() settings.macNames.remove("aead") settings.useEncryptThenMAC = False connection.handshakeClientCert(serverName=address[0], settings=settings) testConnClient(connection) assert(isinstance(connection.session.serverCertChain, X509CertChain)) assert(connection.session.serverName == address[0]) assert(not connection.encryptThenMAC) connection.close() test_no += 1 print("Test {0} - resumption with EtM".format(test_no)) synchro.recv(1) connection = connect() settings = HandshakeSettings() settings.macNames.remove("aead") connection.handshakeClientCert(serverName=address[0], settings=settings) testConnClient(connection) assert(isinstance(connection.session.serverCertChain, X509CertChain)) assert(connection.session.serverName == address[0]) assert(not connection.resumed) assert(connection.encryptThenMAC) connection.close() session = connection.session # resume synchro.recv(1) connection = connect() connection.handshakeClientCert(serverName=address[0], session=session) testConnClient(connection) assert(isinstance(connection.session.serverCertChain, X509CertChain)) assert(connection.session.serverName == address[0]) assert(connection.resumed) assert(connection.encryptThenMAC) connection.close() test_no += 1 print("Test {0} - resumption with no EtM in 2nd handshake".format(test_no)) synchro.recv(1) connection = connect() settings = HandshakeSettings() settings.macNames.remove("aead") connection.handshakeClientCert(serverName=address[0], settings=settings) testConnClient(connection) assert(isinstance(connection.session.serverCertChain, X509CertChain)) assert(connection.session.serverName == address[0]) assert(not connection.resumed) assert(connection.encryptThenMAC) connection.close() session = connection.session # resume synchro.recv(1) settings = HandshakeSettings() settings.useEncryptThenMAC = False settings.macNames.remove("aead") connection = connect() try: connection.handshakeClientCert(serverName=address[0], session=session, settings=settings) except TLSRemoteAlert as e: assert(str(e) == "handshake_failure") else: raise AssertionError("No exception raised") connection.close() test_no += 1 print('Test {0} - good standard XMLRPC https client'.format(test_no)) address = address[0], address[1]+1 synchro.recv(1) try: # python 2.7.9 introduced certificate verification (context option) # python 3.4.2 doesn't have it though context = ssl.create_default_context(\ cafile=os.path.join(dir, "serverX509Cert.pem")) server = xmlrpclib.Server('https://%s:%s' % address, context=context) except (TypeError, AttributeError): server = xmlrpclib.Server('https://%s:%s' % address) synchro.recv(1) assert server.add(1,2) == 3 synchro.recv(1) assert server.pow(2,4) == 16 test_no += 1 print('Test {0} - good tlslite XMLRPC client'.format(test_no)) transport = XMLRPCTransport(ignoreAbruptClose=True) server = xmlrpclib.Server('https://%s:%s' % address, transport) synchro.recv(1) assert server.add(1,2) == 3 synchro.recv(1) assert server.pow(2,4) == 16 test_no += 1 print('Test {0} - good XMLRPC ignored protocol'.format(test_no)) server = xmlrpclib.Server('http://%s:%s' % address, transport) synchro.recv(1) assert server.add(1,2) == 3 synchro.recv(1) assert server.pow(2,4) == 16 test_no += 1 print("Test {0} - Internet servers test".format(test_no)) try: i = IMAP4_TLS("cyrus.andrew.cmu.edu") i.login("anonymous", "anonymous@anonymous.net") i.logout() test_no += 1 print("Test {0}: IMAP4 good".format(test_no)) p = POP3_TLS("pop.gmail.com") p.quit() test_no += 1 print("Test {0}: POP3 good".format(test_no)) except socket.error as e: print("Non-critical error: socket error trying to reach internet server: ", e) synchro.close() if not badFault: print("Test succeeded, {0} good".format(test_no)) else: print("Test failed") def testConnServer(connection): count = 0 while 1: s = connection.read() count += len(s) if len(s) == 0: break connection.write(s) if count == 1111: break def serverTestCmd(argv): address = argv[0] dir = argv[1] #Split address into hostname/port tuple address = address.split(":") address = ( address[0], int(address[1]) ) #Create synchronisation FIFO synchroSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) synchroSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) synchroSocket.bind((address[0], address[1]-1)) synchroSocket.listen(2) #Connect to server lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) lsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) lsock.bind(address) lsock.listen(5) # following is blocking until the other side doesn't open synchro = synchroSocket.accept()[0] def connect(): return TLSConnection(lsock.accept()[0]) x509Cert = X509().parse(open(os.path.join(dir, "serverX509Cert.pem")).read()) x509Chain = X509CertChain([x509Cert]) s = open(os.path.join(dir, "serverX509Key.pem")).read() x509Key = parsePEMKey(s, private=True) test_no = 0 print("Test {0} - Anonymous server handshake".format(test_no)) synchro.send(b'R') connection = connect() connection.handshakeServer(anon=True) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - good X.509".format(test_no)) synchro.send(b'R') connection = connect() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key) assert(connection.session.serverName == address[0]) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - good X.509, SSL v3".format(test_no)) synchro.send(b'R') connection = connect() settings = HandshakeSettings() settings.minVersion = (3,0) settings.maxVersion = (3,0) connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, settings=settings) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - good X.509, RC4-MD5".format(test_no)) synchro.send(b'R') connection = connect() settings = HandshakeSettings() settings.macNames = ["sha", "md5"] settings.cipherNames = ["rc4"] connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, settings=settings) testConnServer(connection) connection.close() if tackpyLoaded: tack = Tack.createFromPem(open("./TACK1.pem", "rU").read()) tackUnrelated = Tack.createFromPem(open("./TACKunrelated.pem", "rU").read()) settings = HandshakeSettings() settings.useExperimentalTackExtension = True test_no += 1 print("Test {0} - good X.509, TACK".format(test_no)) synchro.send(b'R') connection = connect() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, tacks=[tack], activationFlags=1, settings=settings) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - good X.509, TACK unrelated to cert chain".\ format(test_no)) synchro.send(b'R') connection = connect() try: connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, tacks=[tackUnrelated], settings=settings) assert(False) except TLSRemoteAlert as alert: if alert.description != AlertDescription.illegal_parameter: raise else: test_no += 1 print("Test {0} - good X.509, TACK...skipped (no tackpy)".\ format(test_no)) test_no += 1 print("Test {0} - good X.509, TACK unrelated to cert chain" "...skipped (no tackpy)".format(test_no)) test_no += 1 print("Test {0} - good SRP".format(test_no)) verifierDB = VerifierDB() verifierDB.create() entry = VerifierDB.makeVerifier("test", "password", 1536) verifierDB["test"] = entry synchro.send(b'R') connection = connect() connection.handshakeServer(verifierDB=verifierDB) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - SRP faults".format(test_no)) for fault in Fault.clientSrpFaults + Fault.genericFaults: synchro.send(b'R') connection = connect() connection.fault = fault connection.handshakeServer(verifierDB=verifierDB) connection.close() test_no += 1 print("Test {0} - good SRP: with X.509 cert".format(test_no)) synchro.send(b'R') connection = connect() connection.handshakeServer(verifierDB=verifierDB, \ certChain=x509Chain, privateKey=x509Key) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - X.509 with SRP faults".format(test_no)) for fault in Fault.clientSrpFaults + Fault.genericFaults: synchro.send(b'R') connection = connect() connection.fault = fault connection.handshakeServer(verifierDB=verifierDB, \ certChain=x509Chain, privateKey=x509Key) connection.close() test_no += 1 print("Test {0} - X.509 faults".format(test_no)) for fault in Fault.clientNoAuthFaults + Fault.genericFaults: synchro.send(b'R') connection = connect() connection.fault = fault connection.handshakeServer(certChain=x509Chain, privateKey=x509Key) connection.close() test_no += 1 print("Test {0} - good mutual X.509".format(test_no)) synchro.send(b'R') connection = connect() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, reqCert=True) testConnServer(connection) assert(isinstance(connection.session.clientCertChain, X509CertChain)) connection.close() test_no += 1 print("Test {0} - good mutual X.509, TLSv1.1".format(test_no)) synchro.send(b'R') connection = connect() settings = HandshakeSettings() settings.minVersion = (3,2) settings.maxVersion = (3,2) connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, reqCert=True, settings=settings) testConnServer(connection) assert(isinstance(connection.session.clientCertChain, X509CertChain)) connection.close() test_no += 1 print("Test {0} - good mutual X.509, SSLv3".format(test_no)) synchro.send(b'R') connection = connect() settings = HandshakeSettings() settings.minVersion = (3,0) settings.maxVersion = (3,0) connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, reqCert=True, settings=settings) testConnServer(connection) assert(isinstance(connection.session.clientCertChain, X509CertChain)) connection.close() test_no += 1 print("Test {0} - mutual X.509 faults".format(test_no)) for fault in Fault.clientCertFaults + Fault.genericFaults: synchro.send(b'R') connection = connect() connection.fault = fault connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, reqCert=True) connection.close() test_no += 1 print("Test {0} - good SRP, prepare to resume".format(test_no)) synchro.send(b'R') sessionCache = SessionCache() connection = connect() connection.handshakeServer(verifierDB=verifierDB, sessionCache=sessionCache) assert(connection.session.serverName == address[0]) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - resumption".format(test_no)) synchro.send(b'R') connection = connect() connection.handshakeServer(verifierDB=verifierDB, sessionCache=sessionCache) assert(connection.session.serverName == address[0]) testConnServer(connection) #Don't close! -- see next test test_no += 1 print("Test {0} - invalidated resumption".format(test_no)) synchro.send(b'R') try: connection.read(min=1, max=1) assert() #Client is going to close the socket without a close_notify except TLSAbruptCloseError as e: pass synchro.send(b'R') connection = connect() try: connection.handshakeServer(verifierDB=verifierDB, sessionCache=sessionCache) except TLSLocalAlert as alert: if alert.description != AlertDescription.bad_record_mac: raise connection.close() test_no += 1 print("Test {0} - HTTPS test X.509".format(test_no)) #Close the current listening socket lsock.close() #Create and run an HTTP Server using TLSSocketServerMixIn class MyHTTPServer(TLSSocketServerMixIn, HTTPServer): def handshake(self, tlsConnection): tlsConnection.handshakeServer(certChain=x509Chain, privateKey=x509Key) return True def server_bind(self): self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) HTTPServer.server_bind(self) cd = os.getcwd() os.chdir(dir) address = address[0], address[1]+1 httpd = MyHTTPServer(address, SimpleHTTPRequestHandler) for x in range(6): synchro.send(b'R') httpd.handle_request() httpd.server_close() cd = os.chdir(cd) #Re-connect the listening socket lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) lsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) address = address[0], address[1]+1 lsock.bind(address) lsock.listen(5) implementations = [] if m2cryptoLoaded: implementations.append("openssl") if pycryptoLoaded: implementations.append("pycrypto") implementations.append("python") test_no += 1 print("Test {0} - different ciphers".format(test_no)) for implementation in ["python"] * len(implementations): for cipher in ["aes128", "aes256", "rc4"]: test_no += 1 print("Test {0}:".format(test_no), end=' ') synchro.send(b'R') connection = connect() settings = HandshakeSettings() settings.cipherNames = [cipher] settings.cipherImplementations = [implementation, "python"] connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, settings=settings) print(connection.getCipherName(), connection.getCipherImplementation()) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - throughput test".format(test_no)) for implementation in implementations: for cipher in ["aes128gcm", "aes256gcm", "aes128", "aes256", "3des", "rc4"]: if cipher == "3des" and implementation not in ("openssl", "pycrypto"): continue if cipher in ("aes128gcm", "aes256gcm") and \ implementation not in ("pycrypto", "python"): continue test_no += 1 print("Test {0}:".format(test_no), end=' ') synchro.send(b'R') connection = connect() settings = HandshakeSettings() settings.cipherNames = [cipher] settings.cipherImplementations = [implementation, "python"] connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, settings=settings) print(connection.getCipherName(), connection.getCipherImplementation()) h = connection.read(min=50000, max=50000) assert(h == b"hello"*10000) connection.write(h) connection.close() test_no += 1 print("Test {0} - Next-Protocol Server Negotiation".format(test_no)) synchro.send(b'R') connection = connect() settings = HandshakeSettings() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, settings=settings, nextProtos=[b"http/1.1"]) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - Next-Protocol Server Negotiation".format(test_no)) synchro.send(b'R') connection = connect() settings = HandshakeSettings() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, settings=settings, nextProtos=[b"spdy/2", b"http/1.1"]) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - Next-Protocol Server Negotiation".format(test_no)) synchro.send(b'R') connection = connect() settings = HandshakeSettings() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, settings=settings, nextProtos=[b"http/1.1", b"spdy/2"]) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - Next-Protocol Server Negotiation".format(test_no)) synchro.send(b'R') connection = connect() settings = HandshakeSettings() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, settings=settings, nextProtos=[b"spdy/2", b"http/1.1"]) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - Next-Protocol Server Negotiation".format(test_no)) synchro.send(b'R') connection = connect() settings = HandshakeSettings() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, settings=settings, nextProtos=[b"http/1.1", b"spdy/2", b"spdy/3"]) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - Next-Protocol Server Negotiation".format(test_no)) synchro.send(b'R') connection = connect() settings = HandshakeSettings() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, settings=settings, nextProtos=[b"spdy/3", b"spdy/2"]) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - Next-Protocol Server Negotiation".format(test_no)) synchro.send(b'R') connection = connect() settings = HandshakeSettings() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, settings=settings, nextProtos=[]) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - FALLBACK_SCSV".format(test_no)) synchro.send(b'R') connection = connect() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - FALLBACK_SCSV".format(test_no)) synchro.send(b'R') connection = connect() try: connection.handshakeServer(certChain=x509Chain, privateKey=x509Key) assert() except TLSLocalAlert as alert: if alert.description != AlertDescription.inappropriate_fallback: raise connection.close() test_no += 1 print("Test {0} - no EtM server side".format(test_no)) synchro.send(b'R') connection = connect() settings = HandshakeSettings() settings.useEncryptThenMAC = False connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, settings=settings) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - no EtM client side".format(test_no)) synchro.send(b'R') connection = connect() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - resumption with EtM".format(test_no)) synchro.send(b'R') sessionCache = SessionCache() connection = connect() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, sessionCache=sessionCache) testConnServer(connection) connection.close() # resume synchro.send(b'R') connection = connect() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, sessionCache=sessionCache) testConnServer(connection) connection.close() test_no += 1 print("Test {0} - resumption with no EtM in 2nd handshake".format(test_no)) synchro.send(b'R') sessionCache = SessionCache() connection = connect() connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, sessionCache=sessionCache) testConnServer(connection) connection.close() # resume synchro.send(b'R') connection = connect() try: connection.handshakeServer(certChain=x509Chain, privateKey=x509Key, sessionCache=sessionCache) except TLSLocalAlert as e: assert(str(e) == "handshake_failure") else: raise AssertionError("no exception raised") connection.close() test_no += 1 print("Tests {0}-{1} - XMLRPXC server".format(test_no, test_no + 2)) test_no += 2 address = address[0], address[1]+1 class Server(TLSXMLRPCServer): def handshake(self, tlsConnection): try: tlsConnection.handshakeServer(certChain=x509Chain, privateKey=x509Key, sessionCache=sessionCache) tlsConnection.ignoreAbruptClose = True return True except TLSError as error: print("Handshake failure:", str(error)) return False class MyFuncs: def pow(self, x, y): return pow(x, y) def add(self, x, y): return x + y server = Server(address) server.register_instance(MyFuncs()) synchro.send(b'R') #sa = server.socket.getsockname() #print "Serving HTTPS on", sa[0], "port", sa[1] for i in range(6): synchro.send(b'R') server.handle_request() synchro.close() synchroSocket.close() print("Test succeeded") if __name__ == '__main__': if len(sys.argv) < 2: printUsage("Missing command") elif sys.argv[1] == "client"[:len(sys.argv[1])]: clientTestCmd(sys.argv[2:]) elif sys.argv[1] == "server"[:len(sys.argv[1])]: serverTestCmd(sys.argv[2:]) else: printUsage("Unknown command: %s" % sys.argv[1]) tlslite-ng-0.5.1/tests/verifierDB000066400000000000000000000600001261665411200167050ustar00rootroot00000000000000a ýìâÛÿg¼ Ñh^“ïæáic“rGvbQTJKmpvxZt5eE4lYL69ytmUZh+4H/DGSlD21YFCjcynLtKCZ7YGT4HV3Z6E91SMSq0sDMQ3Nf0ip2gT9UOgIOWntt2ewz2CVF5oWOrNmGgX71fqq6CkYqZYvC5O4Vfl5k+yXXuqoDXQK2/T/dHNZ0EHVwz6nHSgeRGsUdzvKl7Q6I/uAFna9IHpDbGSB8dK5B4cXRhpbnTLmiPh3SFRFI7UksNV9Xqd6J3XS7PoDLPvb9S+zeGFgJ5AE5Xrmr4dOcwPOUymczAQce8MI2CpWmPOo0MOCca41+Onb+7aUtcgD2J965DXeI21SX1R1m2XjcvzWjvIPpxEfnkr/cw== Ag== e/YxmpqCa8zeEqSR9kUUJA== hyytZmE9AyoleRAOH9zC9n5s7LBIOQmsCI4Agnv9SYTlqT7hPkefNr8d9jwe3rSjjN1asfZgBvIdBsoEUiLfV7uG70U4IiVbEuU4q7C43T880YBgZE4FDAVmJGaQjvG2ZT8BqFQ5XhaTxnHwC8M8dpQcB2l+i2HjLTzK/8IOE/NlTZUR3126ycYMDCu/NtGtwTAy3BqwrqdBlLaUfbCjEpvpX5YmozjYfDtYdKDZMGi8jbsjDoqCMSjaux7/dMv/V4TWbNAIvoud/WMH21dBwnhPGvudATW0CfaaYHwqmsIM3KNn+8lLLAKINsesrWabmxCQvozBhHgOFRy6bNswig==alice7q8Kua2zjdacM/gK+o/F6GByYYd1/zwLnqIxTJwlZXbWdN90luqB0zg7SBPWksbg4NXY4lC5i+SOSVwdYIna0V3H17RhVNa2zo70rWmxXUmCVZspe88YhcUp9WZmDlfsaO28PAVybMAv1Mv0l26qmv1ROP6DdkNbn8YdL8DrBuM= Ag== TM0a3EJSmuWsB0QdCW45Mg== IUCCkPsN81En/ZgBf1fJ5Hp0VI3AlAwyXvLPmcM6ic2tYBPUfMMiwdROLQBYhxf0FtVM2QAjEfl6lrswZb+v3MdXuU+1kie9MJKezJCMTPeBr2sx6SUq7uYFJH2oGz4Nu8tu0h7Qcn1/TIQE0AzCgf6XEuxcQ+Rrd77vPFbDBD4=testverifier--Reserved--type ýìâÛÿg¼ Ñh^tlslite-ng-0.5.1/tlslite/000077500000000000000000000000001261665411200152635ustar00rootroot00000000000000tlslite-ng-0.5.1/tlslite/__init__.py000066400000000000000000000016111261665411200173730ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """TLS Lite is a free python library that implements SSL and TLS. TLS Lite supports RSA and SRP ciphersuites. TLS Lite is pure python, however it can use other libraries for faster crypto operations. TLS Lite integrates with several stdlib neworking libraries. API documentation is available in the 'docs' directory. If you have questions or feedback, feel free to contact me. To use, do:: from tlslite import TLSConnection, ... If you want to import the most useful objects, the cleanest way is:: from tlslite.api import * Then use the L{tlslite.TLSConnection.TLSConnection} class with a socket. (Or, use one of the integration classes in L{tlslite.integration}). @version: 0.5.1 """ from tlslite.api import * from tlslite.api import __version__ # Unsure why this is needed, but it is tlslite-ng-0.5.1/tlslite/api.py000066400000000000000000000025011261665411200164040ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. __version__ = "0.5.1" from .constants import AlertLevel, AlertDescription, Fault from .errors import * from .checker import Checker from .handshakesettings import HandshakeSettings from .session import Session from .sessioncache import SessionCache from .tlsconnection import TLSConnection from .verifierdb import VerifierDB from .x509 import X509 from .x509certchain import X509CertChain from .integration.httptlsconnection import HTTPTLSConnection from .integration.tlssocketservermixin import TLSSocketServerMixIn from .integration.tlsasyncdispatchermixin import TLSAsyncDispatcherMixIn from .integration.pop3_tls import POP3_TLS from .integration.imap4_tls import IMAP4_TLS from .integration.smtp_tls import SMTP_TLS from .integration.xmlrpctransport import XMLRPCTransport from .integration.xmlrpcserver import TLSXMLRPCRequestHandler, \ TLSXMLRPCServer, \ MultiPathTLSXMLRPCServer from .utils.cryptomath import m2cryptoLoaded, gmpyLoaded, \ pycryptoLoaded, prngName from .utils.keyfactory import generateRSAKey, parsePEMKey, \ parseAsPublicKey, parsePrivateKey from .utils.tackwrapper import tackpyLoaded tlslite-ng-0.5.1/tlslite/basedb.py000066400000000000000000000070301261665411200170550ustar00rootroot00000000000000# Authors: # Trevor Perrin # Martin von Loewis - python 3 port # # See the LICENSE file for legal information regarding use of this file. """Base class for SharedKeyDB and VerifierDB.""" try: import anydbm except ImportError: # Python 3 import dbm as anydbm import threading class BaseDB(object): def __init__(self, filename, type): self.type = type self.filename = filename if self.filename: self.db = None else: self.db = {} self.lock = threading.Lock() def create(self): """Create a new on-disk database. @raise anydbm.error: If there's a problem creating the database. """ if self.filename: self.db = anydbm.open(self.filename, "n") #raises anydbm.error self.db["--Reserved--type"] = self.type self.db.sync() else: self.db = {} def open(self): """Open a pre-existing on-disk database. @raise anydbm.error: If there's a problem opening the database. @raise ValueError: If the database is not of the right type. """ if not self.filename: raise ValueError("Can only open on-disk databases") self.db = anydbm.open(self.filename, "w") #raises anydbm.error try: if self.db["--Reserved--type"] != self.type: raise ValueError("Not a %s database" % self.type) except KeyError: raise ValueError("Not a recognized database") def __getitem__(self, username): if self.db == None: raise AssertionError("DB not open") self.lock.acquire() try: valueStr = self.db[username] finally: self.lock.release() return self._getItem(username, valueStr) def __setitem__(self, username, value): if self.db == None: raise AssertionError("DB not open") valueStr = self._setItem(username, value) self.lock.acquire() try: self.db[username] = valueStr if self.filename: self.db.sync() finally: self.lock.release() def __delitem__(self, username): if self.db == None: raise AssertionError("DB not open") self.lock.acquire() try: del(self.db[username]) if self.filename: self.db.sync() finally: self.lock.release() def __contains__(self, username): """Check if the database contains the specified username. @type username: str @param username: The username to check for. @rtype: bool @return: True if the database contains the username, False otherwise. """ if self.db == None: raise AssertionError("DB not open") self.lock.acquire() try: return self.db.has_key(username) finally: self.lock.release() def check(self, username, param): value = self.__getitem__(username) return self._checkItem(value, username, param) def keys(self): """Return a list of usernames in the database. @rtype: list @return: The usernames in the database. """ if self.db == None: raise AssertionError("DB not open") self.lock.acquire() try: usernames = self.db.keys() finally: self.lock.release() usernames = [u for u in usernames if not u.startswith("--Reserved--")] return usernames tlslite-ng-0.5.1/tlslite/checker.py000066400000000000000000000053041261665411200172430ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Class for post-handshake certificate checking.""" from .x509 import X509 from .x509certchain import X509CertChain from .errors import * class Checker(object): """This class is passed to a handshake function to check the other party's certificate chain. If a handshake function completes successfully, but the Checker judges the other party's certificate chain to be missing or inadequate, a subclass of L{tlslite.errors.TLSAuthenticationError} will be raised. Currently, the Checker can check an X.509 chain. """ def __init__(self, x509Fingerprint=None, checkResumedSession=False): """Create a new Checker instance. You must pass in one of these argument combinations: - x509Fingerprint @type x509Fingerprint: str @param x509Fingerprint: A hex-encoded X.509 end-entity fingerprint which the other party's end-entity certificate must match. @type checkResumedSession: bool @param checkResumedSession: If resumed sessions should be checked. This defaults to False, on the theory that if the session was checked once, we don't need to bother re-checking it. """ self.x509Fingerprint = x509Fingerprint self.checkResumedSession = checkResumedSession def __call__(self, connection): """Check a TLSConnection. When a Checker is passed to a handshake function, this will be called at the end of the function. @type connection: L{tlslite.tlsconnection.TLSConnection} @param connection: The TLSConnection to examine. @raise tlslite.errors.TLSAuthenticationError: If the other party's certificate chain is missing or bad. """ if not self.checkResumedSession and connection.resumed: return if self.x509Fingerprint: if connection._client: chain = connection.session.serverCertChain else: chain = connection.session.clientCertChain if self.x509Fingerprint: if isinstance(chain, X509CertChain): if self.x509Fingerprint: if chain.getFingerprint() != self.x509Fingerprint: raise TLSFingerprintError(\ "X.509 fingerprint mismatch: %s, %s" % \ (chain.getFingerprint(), self.x509Fingerprint)) elif chain: raise TLSAuthenticationTypeError() else: raise TLSNoAuthenticationError()tlslite-ng-0.5.1/tlslite/constants.py000066400000000000000000000566321261665411200176650ustar00rootroot00000000000000# Authors: # Trevor Perrin # Google - defining ClientCertificateType # Google (adapted by Sam Rushing) - NPN support # Dimitris Moraitis - Anon ciphersuites # Dave Baggett (Arcode Corporation) - canonicalCipherName # Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2 # # See the LICENSE file for legal information regarding use of this file. """Constants used in various places.""" class CertificateType: x509 = 0 openpgp = 1 class ClientCertificateType: rsa_sign = 1 dss_sign = 2 rsa_fixed_dh = 3 dss_fixed_dh = 4 class HandshakeType: hello_request = 0 client_hello = 1 server_hello = 2 certificate = 11 server_key_exchange = 12 certificate_request = 13 server_hello_done = 14 certificate_verify = 15 client_key_exchange = 16 finished = 20 next_protocol = 67 class ContentType: change_cipher_spec = 20 alert = 21 handshake = 22 application_data = 23 all = (20,21,22,23) class ExtensionType: # RFC 6066 / 4366 server_name = 0 # RFC 6066 / 4366 cert_type = 9 # RFC 6091 supported_groups = 10 # RFC 4492, RFC-ietf-tls-negotiated-ff-dhe-10 ec_point_formats = 11 # RFC 4492 srp = 12 # RFC 5054 signature_algorithms = 13 # RFC 5246 encrypt_then_mac = 22 # RFC 7366 tack = 0xF300 supports_npn = 13172 renegotiation_info = 0xff01 class HashAlgorithm: """Hash algorithm IDs used in TLSv1.2""" none = 0 md5 = 1 sha1 = 2 sha224 = 3 sha256 = 4 sha384 = 5 sha512 = 6 class SignatureAlgorithm: """Signing algorithms used in TLSv1.2""" anonymous = 0 rsa = 1 dsa = 2 ecdsa = 3 class GroupName(object): """Name of groups supported for (EC)DH key exchange""" # RFC4492 sect163k1 = 1 sect163r1 = 2 sect163r2 = 3 sect193r1 = 4 sect193r2 = 5 sect233k1 = 6 sect233r1 = 7 sect239k1 = 8 sect283k1 = 9 sect283r1 = 10 sect409k1 = 11 sect409r1 = 12 sect571k1 = 13 sect571r1 = 14 secp160k1 = 15 secp160r1 = 16 secp160r2 = 17 secp192k1 = 18 secp192r1 = 19 secp224k1 = 20 secp224r1 = 21 secp256k1 = 22 secp256r1 = 23 secp384r1 = 24 secp521r1 = 25 allEC = list(range(1, 26)) # RFC7027 brainpoolP256r1 = 26 brainpoolP384r1 = 27 brainpoolP512r1 = 28 allEC.append(list(range(26, 29))) # RFC-ietf-tls-negotiated-ff-dhe-10 ffdhe2048 = 256 ffdhe3072 = 257 ffdhe4096 = 258 ffdhe6144 = 259 ffdhe8192 = 260 allFF = list(range(256, 261)) all = allEC + allFF class ECPointFormat(object): """Names and ID's of supported EC point formats""" uncompressed = 0 ansiX962_compressed_prime = 1 ansiX962_compressed_char2 = 2 all = [uncompressed, ansiX962_compressed_prime, ansiX962_compressed_char2] class NameType: host_name = 0 class AlertLevel: warning = 1 fatal = 2 class AlertDescription: """ @cvar bad_record_mac: A TLS record failed to decrypt properly. If this occurs during a SRP handshake it most likely indicates a bad password. It may also indicate an implementation error, or some tampering with the data in transit. This alert will be signalled by the server if the SRP password is bad. It may also be signalled by the server if the SRP username is unknown to the server, but it doesn't wish to reveal that fact. @cvar handshake_failure: A problem occurred while handshaking. This typically indicates a lack of common ciphersuites between client and server, or some other disagreement (about SRP parameters or key sizes, for example). @cvar protocol_version: The other party's SSL/TLS version was unacceptable. This indicates that the client and server couldn't agree on which version of SSL or TLS to use. @cvar user_canceled: The handshake is being cancelled for some reason. """ close_notify = 0 unexpected_message = 10 bad_record_mac = 20 decryption_failed = 21 record_overflow = 22 decompression_failure = 30 handshake_failure = 40 no_certificate = 41 #SSLv3 bad_certificate = 42 unsupported_certificate = 43 certificate_revoked = 44 certificate_expired = 45 certificate_unknown = 46 illegal_parameter = 47 unknown_ca = 48 access_denied = 49 decode_error = 50 decrypt_error = 51 export_restriction = 60 protocol_version = 70 insufficient_security = 71 internal_error = 80 inappropriate_fallback = 86 user_canceled = 90 no_renegotiation = 100 unknown_psk_identity = 115 class CipherSuite: """ Numeric values of ciphersuites and ciphersuite types @cvar tripleDESSuites: ciphersuties which use 3DES symmetric cipher in CBC mode @cvar aes128Suites: ciphersuites which use AES symmetric cipher in CBC mode with 128 bit key @cvar aes256Suites: ciphersuites which use AES symmetric cipher in CBC mode with 128 bit key @cvar rc4Suites: ciphersuites which use RC4 symmetric cipher with 128 bit key @cvar shaSuites: ciphersuites which use SHA-1 HMAC integrity mechanism and protocol default Pseudo Random Function @cvar sha256Suites: ciphersuites which use SHA-256 HMAC integrity mechanism and SHA-256 Pseudo Random Function @cvar md5Suites: ciphersuites which use MD-5 HMAC integrity mechanism and protocol default Pseudo Random Function @cvar srpSuites: ciphersuites which use Secure Remote Password (SRP) key exchange protocol @cvar srpCertSuites: ciphersuites which use Secure Remote Password (SRP) key exchange protocol with RSA server authentication @cvar srpAllSuites: all SRP ciphersuites, pure SRP and with RSA based server authentication @cvar certSuites: ciphersuites which use RSA key exchange with RSA server authentication @cvar certAllSuites: ciphersuites which use RSA server authentication @cvar anonSuites: ciphersuites which use anonymous Finite Field Diffie-Hellman key exchange @cvar ietfNames: dictionary with string names of the ciphersuites """ ietfNames = {} # Weird pseudo-ciphersuite from RFC 5746 # Signals that "secure renegotiation" is supported # We actually don't do any renegotiation, but this # prevents renegotiation attacks TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF ietfNames[0x00FF] = 'TLS_EMPTY_RENEGOTIATION_INFO_SCSV' # RFC 7507 - Fallback Signaling Cipher Suite Value for Preventing Protocol # Downgrade Attacks TLS_FALLBACK_SCSV = 0x5600 ietfNames[0x5600] = 'TLS_FALLBACK_SCSV' # RFC 5054 - Secure Remote Password (SRP) Protocol for TLS Authentication TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A ietfNames[0xC01A] = 'TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA' TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D ietfNames[0xC01D] = 'TLS_SRP_SHA_WITH_AES_128_CBC_SHA' TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020 ietfNames[0xC020] = 'TLS_SRP_SHA_WITH_AES_256_CBC_SHA' # RFC 5054 - Secure Remote Password (SRP) Protocol for TLS Authentication TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B ietfNames[0xC01B] = 'TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA' TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E ietfNames[0xC01E] = 'TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA' TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021 ietfNames[0xC021] = 'TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA' # RFC 5246 - TLS v1.2 Protocol TLS_RSA_WITH_NULL_MD5 = 0x0001 ietfNames[0x0001] = 'TLS_RSA_WITH_NULL_MD5' TLS_RSA_WITH_NULL_SHA = 0x0002 ietfNames[0x0002] = 'TLS_RSA_WITH_NULL_SHA' TLS_RSA_WITH_NULL_SHA256 = 0x003B ietfNames[0x003B] = 'TLS_RSA_WITH_NULL_SHA256' # RFC 5246 - TLS v1.2 Protocol TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A ietfNames[0x000A] = 'TLS_RSA_WITH_3DES_EDE_CBC_SHA' TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F ietfNames[0x002F] = 'TLS_RSA_WITH_AES_128_CBC_SHA' TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035 ietfNames[0x0035] = 'TLS_RSA_WITH_AES_256_CBC_SHA' TLS_RSA_WITH_RC4_128_SHA = 0x0005 ietfNames[0x0005] = 'TLS_RSA_WITH_RC4_128_SHA' # RFC 5246 - TLS v1.2 Protocol TLS_RSA_WITH_RC4_128_MD5 = 0x0004 ietfNames[0x0004] = 'TLS_RSA_WITH_RC4_128_MD5' # RFC 5246 - TLS v1.2 Protocol TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016 ietfNames[0x0016] = 'TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA' TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033 ietfNames[0x0016] = 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA' TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039 ietfNames[0x0039] = 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA' # RFC 5246 - TLS v1.2 Protocol TLS_DH_ANON_WITH_RC4_128_MD5 = 0x0018 ietfNames[0x0018] = 'TLS_DH_ANON_WITH_RC4_128_MD5' TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA = 0x001B ietfNames[0x001B] = 'TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA' TLS_DH_ANON_WITH_AES_128_CBC_SHA = 0x0034 ietfNames[0x0034] = 'TLS_DH_ANON_WITH_AES_128_CBC_SHA' TLS_DH_ANON_WITH_AES_256_CBC_SHA = 0x003A ietfNames[0x003A] = 'TLS_DH_ANON_WITH_AES_256_CBC_SHA' TLS_DH_ANON_WITH_AES_128_CBC_SHA256 = 0x006C ietfNames[0x006C] = 'TLS_DH_ANON_WITH_AES_128_CBC_SHA256' TLS_DH_ANON_WITH_AES_256_CBC_SHA256 = 0x006D ietfNames[0x006D] = 'TLS_DH_ANON_WITH_AES_256_CBC_SHA256' TLS_DH_ANON_WITH_AES_128_GCM_SHA256 = 0x00A6 ietfNames[0x00A6] = 'TLS_DH_ANON_WITH_AES_128_GCM_SHA256' TLS_DH_ANON_WITH_AES_256_GCM_SHA384 = 0x00A7 ietfNames[0x00A7] = 'TLS_DH_ANON_WITH_AES_256_GCM_SHA384' # RFC 5246 - TLS v1.2 Protocol TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C ietfNames[0x003C] = 'TLS_RSA_WITH_AES_128_CBC_SHA256' TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D ietfNames[0x003D] = 'TLS_RSA_WITH_AES_256_CBC_SHA256' # RFC 5246 - TLS v1.2 TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067 ietfNames[0x0067] = 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA256' TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B ietfNames[0x006B] = 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA256' # RFC 5288 - AES-GCM ciphers for TLSv1.2 TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C ietfNames[0x009C] = 'TLS_RSA_WITH_AES_128_GCM_SHA256' TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E ietfNames[0x009E] = 'TLS_DHE_RSA_WITH_AES_128_GCM_SHA256' TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D ietfNames[0x009D] = 'TLS_RSA_WITH_AES_256_GCM_SHA384' TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F ietfNames[0x009F] = 'TLS_DHE_RSA_WITH_AES_256_GCM_SHA384' # # Define cipher suite families below # # 3DES CBC ciphers tripleDESSuites = [] tripleDESSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) tripleDESSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) tripleDESSuites.append(TLS_RSA_WITH_3DES_EDE_CBC_SHA) tripleDESSuites.append(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) tripleDESSuites.append(TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA) # AES-128 CBC ciphers aes128Suites = [] aes128Suites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA) aes128Suites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) aes128Suites.append(TLS_RSA_WITH_AES_128_CBC_SHA) aes128Suites.append(TLS_DHE_RSA_WITH_AES_128_CBC_SHA) aes128Suites.append(TLS_DH_ANON_WITH_AES_128_CBC_SHA) aes128Suites.append(TLS_RSA_WITH_AES_128_CBC_SHA256) aes128Suites.append(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) aes128Suites.append(TLS_DH_ANON_WITH_AES_128_CBC_SHA256) # AES-256 CBC ciphers aes256Suites = [] aes256Suites.append(TLS_SRP_SHA_WITH_AES_256_CBC_SHA) aes256Suites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) aes256Suites.append(TLS_RSA_WITH_AES_256_CBC_SHA) aes256Suites.append(TLS_DH_ANON_WITH_AES_256_CBC_SHA) aes256Suites.append(TLS_DHE_RSA_WITH_AES_256_CBC_SHA) aes256Suites.append(TLS_RSA_WITH_AES_256_CBC_SHA256) aes256Suites.append(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) aes256Suites.append(TLS_DH_ANON_WITH_AES_256_CBC_SHA256) # AES-128 GCM ciphers aes128GcmSuites = [] aes128GcmSuites.append(TLS_RSA_WITH_AES_128_GCM_SHA256) aes128GcmSuites.append(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) aes128GcmSuites.append(TLS_DH_ANON_WITH_AES_128_GCM_SHA256) # AES-256-GCM ciphers (implicit SHA384, see sha384PrfSuites) aes256GcmSuites = [] aes256GcmSuites.append(TLS_RSA_WITH_AES_256_GCM_SHA384) aes256GcmSuites.append(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384) aes256GcmSuites.append(TLS_DH_ANON_WITH_AES_256_GCM_SHA384) # RC4 128 stream cipher rc4Suites = [] rc4Suites.append(TLS_DH_ANON_WITH_RC4_128_MD5) rc4Suites.append(TLS_RSA_WITH_RC4_128_SHA) rc4Suites.append(TLS_RSA_WITH_RC4_128_MD5) # no encryption nullSuites = [] nullSuites.append(TLS_RSA_WITH_NULL_MD5) nullSuites.append(TLS_RSA_WITH_NULL_SHA) nullSuites.append(TLS_RSA_WITH_NULL_SHA256) # SHA-1 HMAC, protocol default PRF shaSuites = [] shaSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) shaSuites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA) shaSuites.append(TLS_SRP_SHA_WITH_AES_256_CBC_SHA) shaSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) shaSuites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) shaSuites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) shaSuites.append(TLS_RSA_WITH_3DES_EDE_CBC_SHA) shaSuites.append(TLS_RSA_WITH_AES_128_CBC_SHA) shaSuites.append(TLS_RSA_WITH_AES_256_CBC_SHA) shaSuites.append(TLS_RSA_WITH_RC4_128_SHA) shaSuites.append(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) shaSuites.append(TLS_DHE_RSA_WITH_AES_128_CBC_SHA) shaSuites.append(TLS_DHE_RSA_WITH_AES_256_CBC_SHA) shaSuites.append(TLS_DH_ANON_WITH_AES_128_CBC_SHA) shaSuites.append(TLS_DH_ANON_WITH_AES_256_CBC_SHA) shaSuites.append(TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA) shaSuites.append(TLS_RSA_WITH_NULL_SHA) # SHA-256 HMAC, SHA-256 PRF sha256Suites = [] sha256Suites.append(TLS_RSA_WITH_AES_128_CBC_SHA256) sha256Suites.append(TLS_RSA_WITH_AES_256_CBC_SHA256) sha256Suites.append(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) sha256Suites.append(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) sha256Suites.append(TLS_RSA_WITH_NULL_SHA256) sha256Suites.append(TLS_DH_ANON_WITH_AES_128_CBC_SHA256) sha256Suites.append(TLS_DH_ANON_WITH_AES_256_CBC_SHA256) # SHA-384 HMAC, SHA-384 PRF sha384Suites = [] # stream cipher construction streamSuites = [] streamSuites.extend(rc4Suites) streamSuites.extend(nullSuites) # AEAD integrity, any PRF aeadSuites = [] aeadSuites.extend(aes128GcmSuites) aeadSuites.extend(aes256GcmSuites) # TLS1.2 with SHA384 PRF sha384PrfSuites = [] sha384PrfSuites.extend(sha384Suites) sha384PrfSuites.extend(aes256GcmSuites) # MD-5 HMAC, protocol default PRF md5Suites = [] md5Suites.append(TLS_DH_ANON_WITH_RC4_128_MD5) md5Suites.append(TLS_RSA_WITH_RC4_128_MD5) md5Suites.append(TLS_RSA_WITH_NULL_MD5) # SSL3, TLS1.0, TLS1.1 and TLS1.2 compatible ciphers ssl3Suites = [] ssl3Suites.extend(shaSuites) ssl3Suites.extend(md5Suites) # TLS1.2 specific ciphersuites tls12Suites = [] tls12Suites.extend(sha256Suites) tls12Suites.extend(sha384Suites) tls12Suites.extend(aeadSuites) @staticmethod def filterForVersion(suites, minVersion, maxVersion): """Return a copy of suites without ciphers incompatible with version""" includeSuites = set([]) if (3, 0) <= minVersion <= (3, 3): includeSuites.update(CipherSuite.ssl3Suites) if maxVersion == (3, 3): includeSuites.update(CipherSuite.tls12Suites) return [s for s in suites if s in includeSuites] @staticmethod def _filterSuites(suites, settings, version=None): if version is None: version = settings.maxVersion macNames = settings.macNames cipherNames = settings.cipherNames keyExchangeNames = settings.keyExchangeNames macSuites = [] if "sha" in macNames: macSuites += CipherSuite.shaSuites if "sha256" in macNames and version >= (3, 3): macSuites += CipherSuite.sha256Suites if "sha384" in macNames and version >= (3, 3): macSuites += CipherSuite.sha384Suites if "md5" in macNames: macSuites += CipherSuite.md5Suites if "aead" in macNames and version >= (3, 3): macSuites += CipherSuite.aeadSuites cipherSuites = [] if "aes128gcm" in cipherNames and version >= (3, 3): cipherSuites += CipherSuite.aes128GcmSuites if "aes256gcm" in cipherNames and version >= (3, 3): cipherSuites += CipherSuite.aes256GcmSuites if "aes128" in cipherNames: cipherSuites += CipherSuite.aes128Suites if "aes256" in cipherNames: cipherSuites += CipherSuite.aes256Suites if "3des" in cipherNames: cipherSuites += CipherSuite.tripleDESSuites if "rc4" in cipherNames: cipherSuites += CipherSuite.rc4Suites if "null" in cipherNames: cipherSuites += CipherSuite.nullSuites keyExchangeSuites = [] if "rsa" in keyExchangeNames: keyExchangeSuites += CipherSuite.certSuites if "dhe_rsa" in keyExchangeNames: keyExchangeSuites += CipherSuite.dheCertSuites if "srp_sha" in keyExchangeNames: keyExchangeSuites += CipherSuite.srpSuites if "srp_sha_rsa" in keyExchangeNames: keyExchangeSuites += CipherSuite.srpCertSuites if "dh_anon" in keyExchangeNames: keyExchangeSuites += CipherSuite.anonSuites return [s for s in suites if s in macSuites and s in cipherSuites and s in keyExchangeSuites] # SRP key exchange srpSuites = [] srpSuites.append(TLS_SRP_SHA_WITH_AES_256_CBC_SHA) srpSuites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA) srpSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) @staticmethod def getSrpSuites(settings, version=None): return CipherSuite._filterSuites(CipherSuite.srpSuites, settings, version) # SRP key exchange, RSA authentication srpCertSuites = [] srpCertSuites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) srpCertSuites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) srpCertSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) @staticmethod def getSrpCertSuites(settings, version=None): return CipherSuite._filterSuites(CipherSuite.srpCertSuites, settings, version) srpAllSuites = srpSuites + srpCertSuites @staticmethod def getSrpAllSuites(settings, version=None): return CipherSuite._filterSuites(CipherSuite.srpAllSuites, settings, version) # RSA key exchange, RSA authentication certSuites = [] certSuites.append(TLS_RSA_WITH_AES_256_GCM_SHA384) certSuites.append(TLS_RSA_WITH_AES_128_GCM_SHA256) certSuites.append(TLS_RSA_WITH_AES_256_CBC_SHA256) certSuites.append(TLS_RSA_WITH_AES_128_CBC_SHA256) certSuites.append(TLS_RSA_WITH_AES_256_CBC_SHA) certSuites.append(TLS_RSA_WITH_AES_128_CBC_SHA) certSuites.append(TLS_RSA_WITH_3DES_EDE_CBC_SHA) certSuites.append(TLS_RSA_WITH_RC4_128_SHA) certSuites.append(TLS_RSA_WITH_RC4_128_MD5) certSuites.append(TLS_RSA_WITH_NULL_MD5) certSuites.append(TLS_RSA_WITH_NULL_SHA) certSuites.append(TLS_RSA_WITH_NULL_SHA256) @staticmethod def getCertSuites(settings, version=None): return CipherSuite._filterSuites(CipherSuite.certSuites, settings, version) # FFDHE key exchange, RSA authentication dheCertSuites = [] dheCertSuites.append(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384) dheCertSuites.append(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) dheCertSuites.append(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) dheCertSuites.append(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) dheCertSuites.append(TLS_DHE_RSA_WITH_AES_256_CBC_SHA) dheCertSuites.append(TLS_DHE_RSA_WITH_AES_128_CBC_SHA) dheCertSuites.append(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) @staticmethod def getDheCertSuites(settings, version=None): return CipherSuite._filterSuites(CipherSuite.dheCertSuites, settings, version) # RSA authentication certAllSuites = srpCertSuites + certSuites + dheCertSuites # anon FFDHE key exchange anonSuites = [] anonSuites.append(TLS_DH_ANON_WITH_AES_256_CBC_SHA) anonSuites.append(TLS_DH_ANON_WITH_AES_128_CBC_SHA) anonSuites.append(TLS_DH_ANON_WITH_RC4_128_MD5) anonSuites.append(TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA) anonSuites.append(TLS_DH_ANON_WITH_AES_128_CBC_SHA256) anonSuites.append(TLS_DH_ANON_WITH_AES_256_CBC_SHA256) anonSuites.append(TLS_DH_ANON_WITH_AES_128_GCM_SHA256) anonSuites.append(TLS_DH_ANON_WITH_AES_256_GCM_SHA384) @staticmethod def getAnonSuites(settings, version=None): return CipherSuite._filterSuites(CipherSuite.anonSuites, settings, version) dhAllSuites = dheCertSuites + anonSuites @staticmethod def canonicalCipherName(ciphersuite): """Return the canonical name of the cipher whose number is provided.""" if ciphersuite in CipherSuite.aes128GcmSuites: return "aes128gcm" elif ciphersuite in CipherSuite.aes256GcmSuites: return "aes256gcm" elif ciphersuite in CipherSuite.aes128Suites: return "aes128" elif ciphersuite in CipherSuite.aes256Suites: return "aes256" elif ciphersuite in CipherSuite.rc4Suites: return "rc4" elif ciphersuite in CipherSuite.tripleDESSuites: return "3des" elif ciphersuite in CipherSuite.nullSuites: return "null" else: return None @staticmethod def canonicalMacName(ciphersuite): """Return the canonical name of the MAC whose number is provided.""" if ciphersuite in CipherSuite.sha384Suites: return "sha384" elif ciphersuite in CipherSuite.sha256Suites: return "sha256" elif ciphersuite in CipherSuite.shaSuites: return "sha" elif ciphersuite in CipherSuite.md5Suites: return "md5" else: return None # The following faults are induced as part of testing. The faultAlerts # dictionary describes the allowed alerts that may be triggered by these # faults. class Fault: badUsername = 101 badPassword = 102 badA = 103 clientSrpFaults = list(range(101,104)) badVerifyMessage = 601 clientCertFaults = list(range(601,602)) badPremasterPadding = 501 shortPremasterSecret = 502 clientNoAuthFaults = list(range(501,503)) badB = 201 serverFaults = list(range(201,202)) badFinished = 300 badMAC = 301 badPadding = 302 genericFaults = list(range(300,303)) faultAlerts = {\ badUsername: (AlertDescription.unknown_psk_identity, \ AlertDescription.bad_record_mac),\ badPassword: (AlertDescription.bad_record_mac,),\ badA: (AlertDescription.illegal_parameter,),\ badPremasterPadding: (AlertDescription.bad_record_mac,),\ shortPremasterSecret: (AlertDescription.bad_record_mac,),\ badVerifyMessage: (AlertDescription.decrypt_error,),\ badFinished: (AlertDescription.decrypt_error,),\ badMAC: (AlertDescription.bad_record_mac,),\ badPadding: (AlertDescription.bad_record_mac,) } faultNames = {\ badUsername: "bad username",\ badPassword: "bad password",\ badA: "bad A",\ badPremasterPadding: "bad premaster padding",\ shortPremasterSecret: "short premaster secret",\ badVerifyMessage: "bad verify message",\ badFinished: "bad finished message",\ badMAC: "bad MAC",\ badPadding: "bad padding" } tlslite-ng-0.5.1/tlslite/defragmenter.py000066400000000000000000000073761261665411200203150ustar00rootroot00000000000000# Copyright (c) 2015, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. """ Helper package for handling fragmentation of messages """ from __future__ import generators from .utils.codec import Parser class Defragmenter(object): """ Class for demultiplexing TLS messages. Since the messages can be interleaved and fragmented between each other we need to cache not complete ones and return in order of urgency. Supports messages with given size (like Alerts) or with a length header in specific place (like Handshake messages). @ivar priorities: order in which messages from given types should be returned. @ivar buffers: data buffers for message types @ivar decoders: functions which check buffers if a message of given type is complete """ def __init__(self): """Set up empty defregmenter""" self.priorities = [] self.buffers = {} self.decoders = {} def addStaticSize(self, msgType, size): """Add a message type which all messages are of same length""" if msgType in self.priorities: raise ValueError("Message type already defined") if size < 1: raise ValueError("Message size must be positive integer") self.priorities += [msgType] self.buffers[msgType] = bytearray(0) def sizeHandler(data): """ Size of message in parameter If complete message is present in parameter returns its size, None otherwise. """ if len(data) < size: return None else: return size self.decoders[msgType] = sizeHandler def addDynamicSize(self, msgType, sizeOffset, sizeOfSize): """Add a message type which has a dynamic size set in a header""" if msgType in self.priorities: raise ValueError("Message type already defined") if sizeOfSize < 1: raise ValueError("Size of size must be positive integer") if sizeOffset < 0: raise ValueError("Offset can't be negative") self.priorities += [msgType] self.buffers[msgType] = bytearray(0) def sizeHandler(data): """ Size of message in parameter If complete message is present in parameter returns its size, None otherwise. """ if len(data) < sizeOffset+sizeOfSize: return None else: parser = Parser(data) # skip the header parser.getFixBytes(sizeOffset) payloadLength = parser.get(sizeOfSize) if parser.getRemainingLength() < payloadLength: # not enough bytes in buffer return None return sizeOffset + sizeOfSize + payloadLength self.decoders[msgType] = sizeHandler def addData(self, msgType, data): """Adds data to buffers""" if msgType not in self.priorities: raise ValueError("Message type not defined") self.buffers[msgType] += data def getMessage(self): """Extract the highest priority complete message from buffer""" for msgType in self.priorities: length = self.decoders[msgType](self.buffers[msgType]) if length is None: continue # extract message data = self.buffers[msgType][:length] # remove it from buffer self.buffers[msgType] = self.buffers[msgType][length:] return (msgType, data) return None def clearBuffers(self): """Remove all data from buffers""" for key in self.buffers.keys(): self.buffers[key] = bytearray(0) tlslite-ng-0.5.1/tlslite/errors.py000066400000000000000000000165461261665411200171650ustar00rootroot00000000000000# Authors: # Trevor Perrin # Dave Baggett (Arcode Corporation) - Added TLSUnsupportedError. # # See the LICENSE file for legal information regarding use of this file. """Exception classes. @sort: TLSError, TLSAbruptCloseError, TLSAlert, TLSLocalAlert, TLSRemoteAlert, TLSAuthenticationError, TLSNoAuthenticationError, TLSAuthenticationTypeError, TLSFingerprintError, TLSAuthorizationError, TLSValidationError, TLSFaultError, TLSUnsupportedError """ import socket from .constants import AlertDescription, AlertLevel class BaseTLSException(Exception): """Metaclass for TLS Lite exceptions. Look to L{TLSError} for exceptions that should be caught by tlslite consumers """ pass class TLSError(BaseTLSException): """Base class for all TLS Lite exceptions.""" def __str__(self): """"At least print out the Exception time for str(...).""" return repr(self) class TLSClosedConnectionError(TLSError, socket.error): """An attempt was made to use the connection after it was closed.""" pass class TLSAbruptCloseError(TLSError): """The socket was closed without a proper TLS shutdown. The TLS specification mandates that an alert of some sort must be sent before the underlying socket is closed. If the socket is closed without this, it could signify that an attacker is trying to truncate the connection. It could also signify a misbehaving TLS implementation, or a random network failure. """ pass class TLSAlert(TLSError): """A TLS alert has been signalled.""" pass _descriptionStr = {\ AlertDescription.close_notify: "close_notify",\ AlertDescription.unexpected_message: "unexpected_message",\ AlertDescription.bad_record_mac: "bad_record_mac",\ AlertDescription.decryption_failed: "decryption_failed",\ AlertDescription.record_overflow: "record_overflow",\ AlertDescription.decompression_failure: "decompression_failure",\ AlertDescription.handshake_failure: "handshake_failure",\ AlertDescription.no_certificate: "no certificate",\ AlertDescription.bad_certificate: "bad_certificate",\ AlertDescription.unsupported_certificate: "unsupported_certificate",\ AlertDescription.certificate_revoked: "certificate_revoked",\ AlertDescription.certificate_expired: "certificate_expired",\ AlertDescription.certificate_unknown: "certificate_unknown",\ AlertDescription.illegal_parameter: "illegal_parameter",\ AlertDescription.unknown_ca: "unknown_ca",\ AlertDescription.access_denied: "access_denied",\ AlertDescription.decode_error: "decode_error",\ AlertDescription.decrypt_error: "decrypt_error",\ AlertDescription.export_restriction: "export_restriction",\ AlertDescription.protocol_version: "protocol_version",\ AlertDescription.insufficient_security: "insufficient_security",\ AlertDescription.internal_error: "internal_error",\ AlertDescription.inappropriate_fallback: "inappropriate_fallback",\ AlertDescription.user_canceled: "user_canceled",\ AlertDescription.no_renegotiation: "no_renegotiation",\ AlertDescription.unknown_psk_identity: "unknown_psk_identity"} class TLSLocalAlert(TLSAlert): """A TLS alert has been signalled by the local implementation. @type description: int @ivar description: Set to one of the constants in L{tlslite.constants.AlertDescription} @type level: int @ivar level: Set to one of the constants in L{tlslite.constants.AlertLevel} @type message: str @ivar message: Description of what went wrong. """ def __init__(self, alert, message=None): self.description = alert.description self.level = alert.level self.message = message def __str__(self): alertStr = TLSAlert._descriptionStr.get(self.description) if alertStr == None: alertStr = str(self.description) if self.message: return alertStr + ": " + self.message else: return alertStr class TLSRemoteAlert(TLSAlert): """A TLS alert has been signalled by the remote implementation. @type description: int @ivar description: Set to one of the constants in L{tlslite.constants.AlertDescription} @type level: int @ivar level: Set to one of the constants in L{tlslite.constants.AlertLevel} """ def __init__(self, alert): self.description = alert.description self.level = alert.level def __str__(self): alertStr = TLSAlert._descriptionStr.get(self.description) if alertStr == None: alertStr = str(self.description) return alertStr class TLSAuthenticationError(TLSError): """The handshake succeeded, but the other party's authentication was inadequate. This exception will only be raised when a L{tlslite.Checker.Checker} has been passed to a handshake function. The Checker will be invoked once the handshake completes, and if the Checker objects to how the other party authenticated, a subclass of this exception will be raised. """ pass class TLSNoAuthenticationError(TLSAuthenticationError): """The Checker was expecting the other party to authenticate with a certificate chain, but this did not occur.""" pass class TLSAuthenticationTypeError(TLSAuthenticationError): """The Checker was expecting the other party to authenticate with a different type of certificate chain.""" pass class TLSFingerprintError(TLSAuthenticationError): """The Checker was expecting the other party to authenticate with a certificate chain that matches a different fingerprint.""" pass class TLSAuthorizationError(TLSAuthenticationError): """The Checker was expecting the other party to authenticate with a certificate chain that has a different authorization.""" pass class TLSValidationError(TLSAuthenticationError): """The Checker has determined that the other party's certificate chain is invalid.""" def __init__(self, msg, info=None): # Include a dict containing info about this validation failure TLSAuthenticationError.__init__(self, msg) self.info = info class TLSFaultError(TLSError): """The other party responded incorrectly to an induced fault. This exception will only occur during fault testing, when a TLSConnection's fault variable is set to induce some sort of faulty behavior, and the other party doesn't respond appropriately. """ pass class TLSUnsupportedError(TLSError): """The implementation doesn't support the requested (or required) capabilities.""" pass class TLSInternalError(TLSError): """The internal state of object is unexpected or invalid. Caused by incorrect use of API. """ pass class TLSProtocolException(BaseTLSException): """Exceptions used internally for handling errors in received messages""" pass class TLSIllegalParameterException(TLSProtocolException): """Parameters specified in message were incorrect or invalid""" pass class TLSRecordOverflow(TLSProtocolException): """The received record size was too big""" pass class TLSDecryptionFailed(TLSProtocolException): """Decryption of data was unsuccessful""" pass class TLSBadRecordMAC(TLSProtocolException): """Bad MAC (or padding in case of mac-then-encrypt)""" pass tlslite-ng-0.5.1/tlslite/extensions.py000066400000000000000000000773241261665411200200510ustar00rootroot00000000000000# Copyright (c) 2014, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. """ Helper package for handling TLS extensions encountered in ClientHello and ServerHello messages. """ from __future__ import generators from .utils.codec import Writer, Parser from collections import namedtuple from .constants import NameType, ExtensionType from .errors import TLSInternalError class TLSExtension(object): """ This class handles the generic information about TLS extensions used by both sides of connection in Client Hello and Server Hello messages. See U{RFC 4366} for more info. It is used as a base class for specific users and as a way to store extensions that are not implemented in library. @type extType: int @ivar extType: a 2^16-1 limited integer specifying the type of the extension that it contains, e.g. 0 indicates server name extension @type extData: bytearray @ivar extData: a byte array containing the value of the extension as to be written on the wire @type serverType: boolean @ivar serverType: indicates that the extension was parsed with ServerHello specific parser, otherwise it used universal or ClientHello specific parser @type _universalExtensions: dict @cvar _universalExtensions: dictionary with concrete implementations of specific TLS extensions where key is the numeric value of the extension ID. Contains ClientHello version of extensions or universal implementations @type _serverExtensions: dict @cvar _serverExtensions: dictionary with concrete implementations of specific TLS extensions where key is the numeric value of the extension ID. Includes only those extensions that require special handlers for ServerHello versions. """ # actual definition at the end of file, after definitions of all classes _universalExtensions = {} _serverExtensions = {} def __init__(self, server=False): """ Creates a generic TLS extension that can be used either for client hello or server hello message parsing or creation. You'll need to use L{create} or L{parse} methods to create an extension that is actually usable. @type server: boolean @param server: whatever to select ClientHello or ServerHello version for parsing """ self.extType = None self.extData = bytearray(0) self.serverType = server def create(self, extType, data): """ Initializes a generic TLS extension that can later be used in client hello or server hello messages @type extType: int @param extType: type of the extension encoded as an integer between M{0} and M{2^16-1} @type data: bytearray @param data: raw data representing extension on the wire @rtype: L{TLSExtension} """ self.extType = extType self.extData = data return self def write(self): """ Returns encoded extension, as encoded on the wire @rtype: bytearray @return: An array of bytes formatted as is supposed to be written on the wire, including the extension_type, length and the extension data @raise AssertionError: when the object was not initialized """ assert self.extType is not None w = Writer() w.add(self.extType, 2) w.add(len(self.extData), 2) w.addFixSeq(self.extData, 1) return w.bytes def parse(self, p): """ Parses extension from the wire format @type p: L{tlslite.util.codec.Parser} @param p: data to be parsed @raise SyntaxError: when the size of the passed element doesn't match the internal representation @rtype: L{TLSExtension} """ extType = p.get(2) ext_length = p.get(2) # first check if we shouldn't use server side parser if self.serverType and extType in self._serverExtensions: ext = self._serverExtensions[extType]() ext_parser = Parser(p.getFixBytes(ext_length)) ext = ext.parse(ext_parser) return ext # then fallback to universal/ClientHello-specific parsers if extType in self._universalExtensions: ext = self._universalExtensions[extType]() ext_parser = Parser(p.getFixBytes(ext_length)) ext = ext.parse(ext_parser) return ext # finally, just save the extension data as there are extensions which # don't require specific handlers and indicate option by mere presence self.extType = extType self.extData = p.getFixBytes(ext_length) assert len(self.extData) == ext_length return self def __eq__(self, that): """ Test if two TLS extensions will result in the same on the wire representation. Will return False for every object that's not an extension. """ if hasattr(that, 'extType') and hasattr(that, 'extData'): return self.extType == that.extType and \ self.extData == that.extData else: return False def __repr__(self): """ Output human readable representation of object @rtype: str """ return "TLSExtension(extType={0!r}, extData={1!r},"\ " serverType={2!r})".format(self.extType, self.extData, self.serverType) class SNIExtension(TLSExtension): """ Class for handling Server Name Indication (server_name) extension from RFC 4366. Note that while usually the client does advertise just one name, it is possible to provide a list of names, each of different type. The type is a single byte value (represented by ints), the names are opaque byte strings, in case of DNS host names (records of type 0) they are UTF-8 encoded domain names (without the ending dot). @type hostNames: tuple of bytearrays @ivar hostNames: tuple of hostnames (server name records of type 0) advertised in the extension. Note that it may not include all names from client hello as the client can advertise other types. Also note that while it's not possible to change the returned array in place, it is possible to assign a new set of names. IOW, this won't work:: sni_extension.hostNames[0] = bytearray(b'example.com') while this will work:: names = list(sni_extension.hostNames) names[0] = bytearray(b'example.com') sni_extension.hostNames = names @type serverNames: list of L{ServerName} @ivar serverNames: list of all names advertised in extension. L{ServerName} is a namedtuple with two elements, the first element (type) defines the type of the name (encoded as int) while the other (name) is a bytearray that carries the value. Known types are defined in L{tlslite.constants.NameType}. The list will be empty if the on the wire extension had and empty list while it will be None if the extension was empty. @type extType: int @ivar extType: numeric type of SNIExtension, i.e. 0 @type extData: bytearray @ivar extData: raw representation of the extension """ ServerName = namedtuple('ServerName', 'name_type name') def __init__(self): """ Create an instance of SNIExtension. See also: L{create} and L{parse}. """ self.serverNames = None def __repr__(self): """ Return programmer-readable representation of extension @rtype: str """ return "SNIExtension(serverNames={0!r})".format(self.serverNames) def create(self, hostname=None, hostNames=None, serverNames=None): """ Initializes an instance with provided hostname, host names or raw server names. Any of the parameters may be None, in that case the list inside the extension won't be defined, if either hostNames or serverNames is an empty list, then the extension will define a list of lenght 0. If multiple parameters are specified at the same time, then the resulting list of names will be concatenated in order of hostname, hostNames and serverNames last. @type hostname: bytearray @param hostname: raw UTF-8 encoding of the host name @type hostNames: list of bytearrays @param hostNames: list of raw UTF-8 encoded host names @type serverNames: list of L{ServerName} @param serverNames: pairs of name_type and name encoded as a namedtuple @rtype: L{SNIExtension} """ if hostname is None and hostNames is None and serverNames is None: self.serverNames = None return self else: self.serverNames = [] if hostname: self.serverNames += [SNIExtension.ServerName(NameType.host_name,\ hostname)] if hostNames: self.serverNames +=\ [SNIExtension.ServerName(NameType.host_name, x) for x in\ hostNames] if serverNames: self.serverNames += serverNames return self @property def extType(self): """ Return the type of TLS extension, in this case - 0 @rtype: int """ return ExtensionType.server_name @property def hostNames(self): """ Returns a simulated list of hostNames from the extension. @rtype: tuple of bytearrays """ # because we can't simulate assignments to array elements we return # an immutable type if self.serverNames is None: return tuple() else: return tuple([x.name for x in self.serverNames if \ x.name_type == NameType.host_name]) @hostNames.setter def hostNames(self, hostNames): """ Removes all host names from the extension and replaces them by names in X{hostNames} parameter. Newly added parameters will be added at the I{beginning} of the list of extensions. @type hostNames: iterable of bytearrays @param hostNames: host names to replace the old server names of type 0 """ self.serverNames = \ [SNIExtension.ServerName(NameType.host_name, x) for x in \ hostNames] + \ [x for x in self.serverNames if \ x.name_type != NameType.host_name] @hostNames.deleter def hostNames(self): """ Remove all host names from extension, leaves other name types unmodified """ self.serverNames = [x for x in self.serverNames if \ x.name_type != NameType.host_name] @property def extData(self): """ raw encoding of extension data, without type and length header @rtype: bytearray """ if self.serverNames is None: return bytearray(0) w2 = Writer() for server_name in self.serverNames: w2.add(server_name.name_type, 1) w2.add(len(server_name.name), 2) w2.bytes += server_name.name # note that when the array is empty we write it as array of length 0 w = Writer() w.add(len(w2.bytes), 2) w.bytes += w2.bytes return w.bytes def write(self): """ Returns encoded extension, as encoded on the wire @rtype: bytearray @return: an array of bytes formatted as they are supposed to be written on the wire, including the type, length and extension data """ raw_data = self.extData w = Writer() w.add(self.extType, 2) w.add(len(raw_data), 2) w.bytes += raw_data return w.bytes def parse(self, p): """ Deserialise the extension from on-the-wire data The parser should not include the type or length of extension! @type p: L{tlslite.util.codec.Parser} @param p: data to be parsed @rtype: L{SNIExtension} @raise SyntaxError: when the internal sizes don't match the attached data """ if p.getRemainingLength() == 0: return self self.serverNames = [] p.startLengthCheck(2) while not p.atLengthCheck(): sn_type = p.get(1) sn_name = p.getVarBytes(2) self.serverNames += [SNIExtension.ServerName(sn_type, sn_name)] p.stopLengthCheck() return self class ClientCertTypeExtension(TLSExtension): """ This class handles the Certificate Type extension (variant sent by client) defined in RFC 6091. @type extType: int @ivar extType: numeric type of Certificate Type extension, i.e. 9 @type extData: bytearray @ivar extData: raw representation of the extension data @type certTypes: list of int @ivar certTypes: list of certificate type identifiers (each one byte long) """ def __init__(self): """ Create an instance of ClientCertTypeExtension See also: L{create} and L{parse} """ self.certTypes = None def __repr__(self): """ Return programmer-centric representation of extension @rtype: str """ return "ClientCertTypeExtension(certTypes={0!r})"\ .format(self.certTypes) @property def extType(self): """ Return the type of TLS extension, in this case - 9 @rtype: int """ return ExtensionType.cert_type @property def extData(self): """ Return the raw encoding of this extension @rtype: bytearray """ if self.certTypes is None: return bytearray(0) w = Writer() w.add(len(self.certTypes), 1) for c_type in self.certTypes: w.add(c_type, 1) return w.bytes def create(self, certTypes=None): """ Return instance of this extension with specified certificate types @type certTypes: iterable list of int @param certTypes: list of certificate types to advertise, all values should be between 0 and 2^8-1 inclusive @raises ValueError: when the list includes too big or negative integers """ self.certTypes = certTypes return self def parse(self, p): """ Parse the extension from binary data @type p: L{tlslite.util.codec.Parser} @param p: data to be parsed @raise SyntaxError: when the size of the passed element doesn't match the internal representation @rtype: L{ClientCertTypeExtension} """ self.certTypes = p.getVarList(1, 1) return self class ServerCertTypeExtension(TLSExtension): """ This class handles the Certificate Type extension (variant sent by server) defined in RFC 6091. @type extType: int @ivar extType: byneruc ttoe if Certificate Type extension, i.e. 9 @type extData: bytearray @ivar extData: raw representation of the extension data @type cert_type: int @ivar cert_type: the certificate type selected by server """ def __init__(self): """ Create an instance of ServerCertTypeExtension See also: L{create} and L{parse} """ self.cert_type = None def __repr__(self): """ Return programmer-centric description of object @rtype: str """ return "ServerCertTypeExtension(cert_type={0!r})".format(self.cert_type) @property def extType(self): """ Return the type of TLS extension, in this case - 9 @rtype: int """ return ExtensionType.cert_type @property def extData(self): """ Return the raw encoding of the extension data @rtype: bytearray """ if self.cert_type is None: return bytearray(0) w = Writer() w.add(self.cert_type, 1) return w.bytes def create(self, val): """Create an instance for sending the extension to client. @type val: int @param val: selected type of certificate """ self.cert_type = val return self def parse(self, p): """Parse the extension from on the wire format @type p: L{Parser} @param p: parser with data """ self.cert_type = p.get(1) if p.getRemainingLength() > 0: raise SyntaxError() return self class SRPExtension(TLSExtension): """ This class handles the Secure Remote Password protocol TLS extension defined in RFC 5054. @type extType: int @ivar extType: numeric type of SRPExtension, i.e. 12 @type extData: bytearray @ivar extData: raw representation of extension data @type identity: bytearray @ivar identity: UTF-8 encoding of user name """ def __init__(self): """ Create an instance of SRPExtension See also: L{create} and L{parse} """ self.identity = None def __repr__(self): """ Return programmer-centric description of extension @rtype: str """ return "SRPExtension(identity={0!r})".format(self.identity) @property def extType(self): """ Return the type of TLS extension, in this case - 12 @rtype: int """ return ExtensionType.srp @property def extData(self): """ Return raw data encoding of the extension @rtype: bytearray """ if self.identity is None: return bytearray(0) w = Writer() w.add(len(self.identity), 1) w.addFixSeq(self.identity, 1) return w.bytes def create(self, identity=None): """ Create and instance of SRPExtension with specified protocols @type identity: bytearray @param identity: UTF-8 encoded identity (user name) to be provided to user. MUST be shorter than 2^8-1. @raise ValueError: when the identity lenght is longer than 2^8-1 """ if identity is None: return self if len(identity) >= 2**8: raise ValueError() self.identity = identity return self def parse(self, p): """ Parse the extension from on the wire format @type p: L{tlslite.util.codec.Parser} @param p: data to be parsed @raise SyntaxError: when the data is internally inconsistent @rtype: L{SRPExtension} """ self.identity = p.getVarBytes(1) return self class NPNExtension(TLSExtension): """ This class handles the unofficial Next Protocol Negotiation TLS extension. @type protocols: list of bytearrays @ivar protocols: list of protocol names supported by the server @type extType: int @ivar extType: numeric type of NPNExtension, i.e. 13172 @type extData: bytearray @ivar extData: raw representation of extension data """ def __init__(self): """ Create an instance of NPNExtension See also: L{create} and L{parse} """ self.protocols = None def __repr__(self): """ Create programmer-readable version of representation @rtype: str """ return "NPNExtension(protocols={0!r})".format(self.protocols) @property def extType(self): """ Return the type of TLS extension, in this case - 13172 @rtype: int """ return ExtensionType.supports_npn @property def extData(self): """ Return the raw data encoding of the extension @rtype: bytearray """ if self.protocols is None: return bytearray(0) w = Writer() for prot in self.protocols: w.add(len(prot), 1) w.addFixSeq(prot, 1) return w.bytes def create(self, protocols=None): """ Create an instance of NPNExtension with specified protocols @type protocols: list of bytearray @param protocols: list of protocol names that are supported """ self.protocols = protocols return self def parse(self, p): """ Parse the extension from on the wire format @type p: L{tlslite.util.codec.Parser} @param p: data to be parsed @raise SyntaxError: when the size of the passed element doesn't match the internal representation @rtype: L{NPNExtension} """ self.protocols = [] while p.getRemainingLength() > 0: self.protocols += [p.getVarBytes(1)] return self class TACKExtension(TLSExtension): """ This class handles the server side TACK extension (see draft-perrin-tls-tack-02). @type tacks: list @ivar tacks: list of L{TACK}'s supported by server @type activation_flags: int @ivar activation_flags: activation flags for the tacks """ class TACK(object): """ Implementation of the single TACK """ def __init__(self): """ Create a single TACK object """ self.public_key = bytearray(64) self.min_generation = 0 self.generation = 0 self.expiration = 0 self.target_hash = bytearray(32) self.signature = bytearray(64) def __repr__(self): """ Return programmmer readable representation of TACK object @rtype: str """ return "TACK(public_key={0!r}, min_generation={1!r}, "\ "generation={2!r}, expiration={3!r}, target_hash={4!r}, "\ "signature={5!r})".format( self.public_key, self.min_generation, self.generation, self.expiration, self.target_hash, self.signature) def create(self, public_key, min_generation, generation, expiration, target_hash, signature): """ Initialise the TACK with data """ self.public_key = public_key self.min_generation = min_generation self.generation = generation self.expiration = expiration self.target_hash = target_hash self.signature = signature return self def write(self): """ Convert the TACK into on the wire format @rtype: bytearray """ w = Writer() if len(self.public_key) != 64: raise TLSInternalError("Public_key must be 64 bytes long") w.bytes += self.public_key w.add(self.min_generation, 1) w.add(self.generation, 1) w.add(self.expiration, 4) if len(self.target_hash) != 32: raise TLSInternalError("Target_hash must be 32 bytes long") w.bytes += self.target_hash if len(self.signature) != 64: raise TLSInternalError("Signature must be 64 bytes long") w.bytes += self.signature return w.bytes def parse(self, p): """ Parse the TACK from on the wire format @type p: L{tlslite.util.codec.Parser} @param p: data to be parsed @rtype: L{TACK} @raise SyntaxError: when the internal sizes don't match the provided data """ self.public_key = p.getFixBytes(64) self.min_generation = p.get(1) self.generation = p.get(1) self.expiration = p.get(4) self.target_hash = p.getFixBytes(32) self.signature = p.getFixBytes(64) return self def __eq__(self, other): """ Tests if the other object is equivalent to this TACK Returns False for every object that's not a TACK """ if hasattr(other, 'public_key') and\ hasattr(other, 'min_generation') and\ hasattr(other, 'generation') and\ hasattr(other, 'expiration') and\ hasattr(other, 'target_hash') and\ hasattr(other, 'signature'): if self.public_key == other.public_key and\ self.min_generation == other.min_generation and\ self.generation == other.generation and\ self.expiration == other.expiration and\ self.target_hash == other.target_hash and\ self.signature == other.signature: return True else: return False else: return False def __init__(self): """ Create an instance of TACKExtension See also: L{create} and L{parse} """ self.tacks = [] self.activation_flags = 0 def __repr__(self): """ Create a programmer readable representation of TACK extension @rtype: str """ return "TACKExtension(activation_flags={0!r}, tacks={1!r})".format( self.activation_flags, self.tacks) @property def extType(self): """ Returns the type of TLS extension, in this case - 62208 @rtype: int """ return ExtensionType.tack @property def extData(self): """ Return the raw data encoding of the extension @rtype: bytearray """ w2 = Writer() for t in self.tacks: w2.bytes += t.write() w = Writer() w.add(len(w2.bytes), 2) w.bytes += w2.bytes w.add(self.activation_flags, 1) return w.bytes def create(self, tacks, activation_flags): """ Initialize the insance of TACKExtension @rtype: TACKExtension """ self.tacks = tacks self.activation_flags = activation_flags return self def parse(self, p): """ Parse the extension from on the wire format @type p: L{tlslite.util.codec.Parser} @param p: data to be parsed @rtype: L{TACKExtension} """ self.tacks = [] p.startLengthCheck(2) while not p.atLengthCheck(): tack = TACKExtension.TACK().parse(p) self.tacks += [tack] p.stopLengthCheck() self.activation_flags = p.get(1) return self class SupportedGroupsExtension(TLSExtension): """ Client side list of supported groups of (EC)DHE key exchage. See RFC4492, RFC7027 and RFC-ietf-tls-negotiated-ff-dhe-10 @type groups: int @ivar groups: list of groups that the client supports """ def __init__(self): """Create instance of class""" self.groups = None @property def extType(self): """ Type of extension, in this case - 10 @rtype: int """ return ExtensionType.supported_groups @property def extData(self): """ Return raw data encoding of the extension @rtype: bytearray """ if self.groups is None: return bytearray(0) writer = Writer() # encode length of two bytes per group in two bytes writer.add(len(self.groups) * 2, 2) for group in self.groups: writer.add(group, 2) return writer.bytes def create(self, groups): """ Set the supported groups in the extension @type groups: list of int @param groups: list of supported groups """ self.groups = groups return self def parse(self, parser): """ Deserialise extension from on-the-wire data @type parser: L{Parser} @rtype: SupportedGroupsExtension """ if parser.getRemainingLength() == 0: self.groups = None return self self.groups = [] parser.startLengthCheck(2) while not parser.atLengthCheck(): self.groups.append(parser.get(2)) parser.stopLengthCheck() return self class ECPointFormatsExtension(TLSExtension): """ Client side list of supported ECC point formats. See RFC4492. """ def __init__(self): """Create instance of class""" self.formats = None @property def extType(self): """ Type of extension, in this case - 11 @rtype: int """ return ExtensionType.ec_point_formats @property def extData(self): """ Return raw encoding of the extension @rtype: bytearray """ if self.formats is None: return bytearray(0) writer = Writer() # the length is number of formats encoded in one byte writer.add(len(self.formats), 1) for fmt in self.formats: writer.add(fmt, 1) return writer.bytes def create(self, formats): """ Set the list of supported EC point formats @type formats: list of int @param formats: list of supported EC point formats """ self.formats = formats return self def parse(self, parser): """ Deserialise extension from on the wire data @type parser: L{Parser} @rtype: ECPointFormatsExtension """ if parser.getRemainingLength() == 0: self.formats = None return self self.formats = [] parser.startLengthCheck(1) while not parser.atLengthCheck(): self.formats.append(parser.get(1)) parser.stopLengthCheck() return self class SignatureAlgorithmsExtension(TLSExtension): """ Client side list of supported signature algorithms. Should be used by server to select certificate and signing method for Server Key Exchange messages. In practice used only for the latter. See RFC5246. """ def __init__(self): """Create instance of class""" self.sigalgs = None @property def extType(self): """ Type of extension, in this case - 13 @rtype: int """ return ExtensionType.signature_algorithms @property def extData(self): """ Return raw encoding of the exteion @rtype: bytearray """ if self.sigalgs is None: return bytearray(0) writer = Writer() # elements 1 byte each, overall length encoded in 2 bytes writer.addVarTupleSeq(self.sigalgs, 1, 2) return writer.bytes def create(self, sigalgs): """ Set the list of supported algorithm types @type sigalgs: list of tuples @param sigalgs: list of pairs of a hash algorithm and signature algorithm """ self.sigalgs = sigalgs return self def parse(self, parser): """ Deserialise extension from on the wire data @type parser: L{Parser} @rtype: SignatureAlgorithmsExtension """ if parser.getRemainingLength() == 0: self.sigalgs = None return self self.sigalgs = parser.getVarTupleList(1, 2, 2) if parser.getRemainingLength() != 0: raise SyntaxError() return self TLSExtension._universalExtensions = \ { ExtensionType.server_name : SNIExtension, ExtensionType.cert_type : ClientCertTypeExtension, ExtensionType.supported_groups : SupportedGroupsExtension, ExtensionType.ec_point_formats : ECPointFormatsExtension, ExtensionType.srp : SRPExtension, ExtensionType.signature_algorithms : SignatureAlgorithmsExtension, ExtensionType.supports_npn : NPNExtension} TLSExtension._serverExtensions = \ { ExtensionType.cert_type : ServerCertTypeExtension, ExtensionType.tack : TACKExtension} tlslite-ng-0.5.1/tlslite/handshakehashes.py000066400000000000000000000066531261665411200207710ustar00rootroot00000000000000# Copyright (c) 2015, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. """Handling cryptographic hashes for handshake protocol""" from .utils.compat import compat26Str, compatHMAC from .utils.cryptomath import MD5, SHA1 import hashlib class HandshakeHashes(object): """ Store and calculate necessary hashes for handshake protocol Calculates message digests of messages exchanged in handshake protocol of SSLv3 and TLS. """ def __init__(self): """Create instance""" self._handshakeMD5 = hashlib.md5() self._handshakeSHA = hashlib.sha1() self._handshakeSHA256 = hashlib.sha256() self._handshakeSHA384 = hashlib.sha384() def update(self, data): """ Add L{data} to hash input. @type data: bytearray @param data: serialized TLS handshake message """ text = compat26Str(data) self._handshakeMD5.update(text) self._handshakeSHA.update(text) self._handshakeSHA256.update(text) self._handshakeSHA384.update(text) def digest(self, digest=None): """ Calculate and return digest for the already consumed data. Used for Finished and CertificateVerify messages. @type digest: str @param digest: name of digest to return """ if digest is None: return self._handshakeMD5.digest() + self._handshakeSHA.digest() elif digest == 'md5': return self._handshakeMD5.digest() elif digest == 'sha1': return self._handshakeSHA.digest() elif digest == 'sha256': return self._handshakeSHA256.digest() elif digest == 'sha384': return self._handshakeSHA384.digest() else: raise ValueError("Unknown digest name") def digestSSL(self, masterSecret, label): """ Calculate and return digest for already consumed data (SSLv3 version) Used for Finished and CertificateVerify messages. @type masterSecret: bytearray @param masterSecret: value of the master secret @type label: bytearray @param label: label to include in the calculation """ #pylint: disable=maybe-no-member imacMD5 = self._handshakeMD5.copy() imacSHA = self._handshakeSHA.copy() #pylint: enable=maybe-no-member # the below difference in input for MD5 and SHA-1 is why we can't reuse # digest() method imacMD5.update(compatHMAC(label + masterSecret + bytearray([0x36]*48))) imacSHA.update(compatHMAC(label + masterSecret + bytearray([0x36]*40))) md5Bytes = MD5(masterSecret + bytearray([0x5c]*48) + \ bytearray(imacMD5.digest())) shaBytes = SHA1(masterSecret + bytearray([0x5c]*40) + \ bytearray(imacSHA.digest())) return md5Bytes + shaBytes #pylint: disable=protected-access, maybe-no-member def copy(self): """ Copy object Return a copy of the object with all the hashes in the same state as the source object. @rtype: HandshakeHashes """ other = HandshakeHashes() other._handshakeMD5 = self._handshakeMD5.copy() other._handshakeSHA = self._handshakeSHA.copy() other._handshakeSHA256 = self._handshakeSHA256.copy() other._handshakeSHA384 = self._handshakeSHA384.copy() return other tlslite-ng-0.5.1/tlslite/handshakesettings.py000066400000000000000000000202551261665411200213500ustar00rootroot00000000000000# Authors: # Trevor Perrin # Dave Baggett (Arcode Corporation) - cleanup handling of constants # Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2 # # See the LICENSE file for legal information regarding use of this file. """Class for setting handshake parameters.""" from .constants import CertificateType from .utils import cryptomath from .utils import cipherfactory CIPHER_NAMES = ["aes256gcm", "aes128gcm", "aes256", "aes128", "3des"] ALL_CIPHER_NAMES = CIPHER_NAMES + ["rc4", "null"] MAC_NAMES = ["sha", "sha256", "aead"] # Don't allow "md5" by default. ALL_MAC_NAMES = MAC_NAMES + ["md5"] KEY_EXCHANGE_NAMES = ["rsa", "dhe_rsa", "srp_sha", "srp_sha_rsa", "dh_anon"] CIPHER_IMPLEMENTATIONS = ["openssl", "pycrypto", "python"] CERTIFICATE_TYPES = ["x509"] class HandshakeSettings(object): """This class encapsulates various parameters that can be used with a TLS handshake. @sort: minKeySize, maxKeySize, cipherNames, macNames, certificateTypes, minVersion, maxVersion @type minKeySize: int @ivar minKeySize: The minimum bit length for asymmetric keys. If the other party tries to use SRP, RSA, or Diffie-Hellman parameters smaller than this length, an alert will be signalled. The default is 1023. @type maxKeySize: int @ivar maxKeySize: The maximum bit length for asymmetric keys. If the other party tries to use SRP, RSA, or Diffie-Hellman parameters larger than this length, an alert will be signalled. The default is 8193. @type cipherNames: list @ivar cipherNames: The allowed ciphers. The allowed values in this list are 'aes256', 'aes128', '3des', and 'rc4'. If these settings are used with a client handshake, they determine the order of the ciphersuites offered in the ClientHello message. If these settings are used with a server handshake, the server will choose whichever ciphersuite matches the earliest entry in this list. NOTE: If '3des' is used in this list, but TLS Lite can't find an add-on library that supports 3DES, then '3des' will be silently removed. The default value is ['rc4', 'aes256', 'aes128', '3des']. @type macNames: list @ivar macNames: The allowed MAC algorithms. The allowed values in this list are 'sha' and 'md5'. The default value is ['sha']. @type certificateTypes: list @ivar certificateTypes: The allowed certificate types. The only allowed certificate type is 'x509'. This list is only used with a client handshake. The client will advertise to the server which certificate types are supported, and will check that the server uses one of the appropriate types. @type minVersion: tuple @ivar minVersion: The minimum allowed SSL/TLS version. This variable can be set to (3,0) for SSL 3.0, (3,1) for TLS 1.0, (3,2) for TLS 1.1, or (3,3) for TLS 1.2. If the other party wishes to use a lower version, a protocol_version alert will be signalled. The default is (3,1). @type maxVersion: tuple @ivar maxVersion: The maximum allowed SSL/TLS version. This variable can be set to (3,0) for SSL 3.0, (3,1) for TLS 1.0, (3,2) for TLS 1.1, or (3,3) for TLS 1.2. If the other party wishes to use a higher version, a protocol_version alert will be signalled. The default is (3,3). (WARNING: Some servers may (improperly) reject clients which offer support for TLS 1.1. In this case, try lowering maxVersion to (3,1)). @type useExperimentalTackExtension: bool @ivar useExperimentalTackExtension: Whether to enabled TACK support. Note that TACK support is not standardized by IETF and uses a temporary TLS Extension number, so should NOT be used in production software. @type sendFallbackSCSV: bool @ivar sendFallbackSCSV: Whether to, as a client, send FALLBACK_SCSV. """ def __init__(self): self.minKeySize = 1023 self.maxKeySize = 8193 self.cipherNames = list(CIPHER_NAMES) self.macNames = list(MAC_NAMES) self.keyExchangeNames = list(KEY_EXCHANGE_NAMES) self.cipherImplementations = list(CIPHER_IMPLEMENTATIONS) self.certificateTypes = list(CERTIFICATE_TYPES) self.minVersion = (3, 1) self.maxVersion = (3, 3) self.useExperimentalTackExtension = False self.sendFallbackSCSV = False self.useEncryptThenMAC = True def validate(self): """ Validate the settings, filter out unsupported ciphersuites and return a copy of object. Does not modify the original object. @rtype: HandshakeSettings @return: a self-consistent copy of settings @raise ValueError: when settings are invalid, insecure or unsupported. """ other = HandshakeSettings() other.minKeySize = self.minKeySize other.maxKeySize = self.maxKeySize other.cipherNames = self.cipherNames other.macNames = self.macNames other.keyExchangeNames = self.keyExchangeNames other.cipherImplementations = self.cipherImplementations other.certificateTypes = self.certificateTypes other.minVersion = self.minVersion other.maxVersion = self.maxVersion other.sendFallbackSCSV = self.sendFallbackSCSV other.useEncryptThenMAC = self.useEncryptThenMAC if not cipherfactory.tripleDESPresent: other.cipherNames = [e for e in self.cipherNames if e != "3des"] if len(other.cipherNames)==0: raise ValueError("No supported ciphers") if len(other.certificateTypes)==0: raise ValueError("No supported certificate types") if not cryptomath.m2cryptoLoaded: other.cipherImplementations = \ [e for e in other.cipherImplementations if e != "openssl"] if not cryptomath.pycryptoLoaded: other.cipherImplementations = \ [e for e in other.cipherImplementations if e != "pycrypto"] if len(other.cipherImplementations)==0: raise ValueError("No supported cipher implementations") if other.minKeySize<512: raise ValueError("minKeySize too small") if other.minKeySize>16384: raise ValueError("minKeySize too large") if other.maxKeySize<512: raise ValueError("maxKeySize too small") if other.maxKeySize>16384: raise ValueError("maxKeySize too large") if other.maxKeySize < other.minKeySize: raise ValueError("maxKeySize smaller than minKeySize") for s in other.cipherNames: if s not in ALL_CIPHER_NAMES: raise ValueError("Unknown cipher name: '%s'" % s) for s in other.macNames: if s not in ALL_MAC_NAMES: raise ValueError("Unknown MAC name: '%s'" % s) for s in other.keyExchangeNames: if s not in KEY_EXCHANGE_NAMES: raise ValueError("Unknown key exchange name: '%s'" % s) for s in other.cipherImplementations: if s not in CIPHER_IMPLEMENTATIONS: raise ValueError("Unknown cipher implementation: '%s'" % s) for s in other.certificateTypes: if s not in CERTIFICATE_TYPES: raise ValueError("Unknown certificate type: '%s'" % s) if other.minVersion > other.maxVersion: raise ValueError("Versions set incorrectly") if not other.minVersion in ((3,0), (3,1), (3,2), (3,3)): raise ValueError("minVersion set incorrectly") if not other.maxVersion in ((3,0), (3,1), (3,2), (3,3)): raise ValueError("maxVersion set incorrectly") if other.maxVersion < (3,3): # No sha-2 and AEAD pre TLS 1.2 other.macNames = [e for e in self.macNames if \ e == "sha" or e == "md5"] if other.useEncryptThenMAC not in (True, False): raise ValueError("useEncryptThenMAC can only be True or False") return other def getCertificateTypes(self): """Get list of certificate types as IDs""" ret = [] for ct in self.certificateTypes: if ct == "x509": ret.append(CertificateType.x509) else: raise AssertionError() return ret tlslite-ng-0.5.1/tlslite/integration/000077500000000000000000000000001261665411200176065ustar00rootroot00000000000000tlslite-ng-0.5.1/tlslite/integration/__init__.py000066400000000000000000000006141261665411200217200ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Classes for integrating TLS Lite with other packages.""" __all__ = ["asyncstatemachine", "httptlsconnection", "pop3_tls", "imap4_tls", "smtp_tls", "xmlrpctransport", "tlssocketservermixin", "tlsasyncdispatchermixin"] tlslite-ng-0.5.1/tlslite/integration/asyncstatemachine.py000066400000000000000000000160341261665411200236670ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """ A state machine for using TLS Lite with asynchronous I/O. """ class AsyncStateMachine: """ This is an abstract class that's used to integrate TLS Lite with asyncore and Twisted. This class signals wantsReadsEvent() and wantsWriteEvent(). When the underlying socket has become readable or writeable, the event should be passed to this class by calling inReadEvent() or inWriteEvent(). This class will then try to read or write through the socket, and will update its state appropriately. This class will forward higher-level events to its subclass. For example, when a complete TLS record has been received, outReadEvent() will be called with the decrypted data. """ def __init__(self): self.result = None self.handshaker = None self.closer = None self.reader = None self.writer = None self._clear() def _clear(self): #These store the various asynchronous operations (i.e. #generators). Only one of them, at most, is ever active at a #time. self.handshaker = None self.closer = None self.reader = None self.writer = None #This stores the result from the last call to the #currently active operation. If 0 it indicates that the #operation wants to read, if 1 it indicates that the #operation wants to write. If None, there is no active #operation. self.result = None def _checkAssert(self, maxActive=1): #This checks that only one operation, at most, is #active, and that self.result is set appropriately. activeOps = 0 if self.handshaker: activeOps += 1 if self.closer: activeOps += 1 if self.reader: activeOps += 1 if self.writer: activeOps += 1 if self.result == None: if activeOps != 0: raise AssertionError() elif self.result in (0,1): if activeOps != 1: raise AssertionError() else: raise AssertionError() if activeOps > maxActive: raise AssertionError() def wantsReadEvent(self): """If the state machine wants to read. If an operation is active, this returns whether or not the operation wants to read from the socket. If an operation is not active, this returns None. @rtype: bool or None @return: If the state machine wants to read. """ if self.result != None: return self.result == 0 return None def wantsWriteEvent(self): """If the state machine wants to write. If an operation is active, this returns whether or not the operation wants to write to the socket. If an operation is not active, this returns None. @rtype: bool or None @return: If the state machine wants to write. """ if self.result != None: return self.result == 1 return None def outConnectEvent(self): """Called when a handshake operation completes. May be overridden in subclass. """ pass def outCloseEvent(self): """Called when a close operation completes. May be overridden in subclass. """ pass def outReadEvent(self, readBuffer): """Called when a read operation completes. May be overridden in subclass.""" pass def outWriteEvent(self): """Called when a write operation completes. May be overridden in subclass.""" pass def inReadEvent(self): """Tell the state machine it can read from the socket.""" try: self._checkAssert() if self.handshaker: self._doHandshakeOp() elif self.closer: self._doCloseOp() elif self.reader: self._doReadOp() elif self.writer: self._doWriteOp() else: self.reader = self.tlsConnection.readAsync(16384) self._doReadOp() except: self._clear() raise def inWriteEvent(self): """Tell the state machine it can write to the socket.""" try: self._checkAssert() if self.handshaker: self._doHandshakeOp() elif self.closer: self._doCloseOp() elif self.reader: self._doReadOp() elif self.writer: self._doWriteOp() else: self.outWriteEvent() except: self._clear() raise def _doHandshakeOp(self): try: self.result = next(self.handshaker) except StopIteration: self.handshaker = None self.result = None self.outConnectEvent() def _doCloseOp(self): try: self.result = next(self.closer) except StopIteration: self.closer = None self.result = None self.outCloseEvent() def _doReadOp(self): self.result = next(self.reader) if not self.result in (0,1): readBuffer = self.result self.reader = None self.result = None self.outReadEvent(readBuffer) def _doWriteOp(self): try: self.result = next(self.writer) except StopIteration: self.writer = None self.result = None def setHandshakeOp(self, handshaker): """Start a handshake operation. @type handshaker: generator @param handshaker: A generator created by using one of the asynchronous handshake functions (i.e. handshakeServerAsync, or handshakeClientxxx(..., async=True). """ try: self._checkAssert(0) self.handshaker = handshaker self._doHandshakeOp() except: self._clear() raise def setServerHandshakeOp(self, **args): """Start a handshake operation. The arguments passed to this function will be forwarded to L{tlslite.tlsconnection.TLSConnection.handshakeServerAsync}. """ handshaker = self.tlsConnection.handshakeServerAsync(**args) self.setHandshakeOp(handshaker) def setCloseOp(self): """Start a close operation. """ try: self._checkAssert(0) self.closer = self.tlsConnection.closeAsync() self._doCloseOp() except: self._clear() raise def setWriteOp(self, writeBuffer): """Start a write operation. @type writeBuffer: str @param writeBuffer: The string to transmit. """ try: self._checkAssert(0) self.writer = self.tlsConnection.writeAsync(writeBuffer) self._doWriteOp() except: self._clear() raise tlslite-ng-0.5.1/tlslite/integration/clienthelper.py000066400000000000000000000111321261665411200226340ustar00rootroot00000000000000# Authors: # Trevor Perrin # Dimitris Moraitis - Anon ciphersuites # # See the LICENSE file for legal information regarding use of this file. """ A helper class for using TLS Lite with stdlib clients (httplib, xmlrpclib, imaplib, poplib). """ from tlslite.checker import Checker class ClientHelper(object): """This is a helper class used to integrate TLS Lite with various TLS clients (e.g. poplib, smtplib, httplib, etc.)""" def __init__(self, username=None, password=None, certChain=None, privateKey=None, checker=None, settings = None, anon = False): """ For client authentication, use one of these argument combinations: - username, password (SRP) - certChain, privateKey (certificate) For server authentication, you can either rely on the implicit mutual authentication performed by SRP, or you can do certificate-based server authentication with one of these argument combinations: - x509Fingerprint Certificate-based server authentication is compatible with SRP or certificate-based client authentication. The constructor does not perform the TLS handshake itself, but simply stores these arguments for later. The handshake is performed only when this class needs to connect with the server. Then you should be prepared to handle TLS-specific exceptions. See the client handshake functions in L{tlslite.TLSConnection.TLSConnection} for details on which exceptions might be raised. @type username: str @param username: SRP username. Requires the 'password' argument. @type password: str @param password: SRP password for mutual authentication. Requires the 'username' argument. @type certChain: L{tlslite.x509certchain.X509CertChain} @param certChain: Certificate chain for client authentication. Requires the 'privateKey' argument. Excludes the SRP arguments. @type privateKey: L{tlslite.utils.rsakey.RSAKey} @param privateKey: Private key for client authentication. Requires the 'certChain' argument. Excludes the SRP arguments. @type checker: L{tlslite.checker.Checker} @param checker: Callable object called after handshaking to evaluate the connection and raise an Exception if necessary. @type settings: L{tlslite.handshakesettings.HandshakeSettings} @param settings: Various settings which can be used to control the ciphersuites, certificate types, and SSL/TLS versions offered by the client. """ self.username = None self.password = None self.certChain = None self.privateKey = None self.checker = None self.anon = anon #SRP Authentication if username and password and not \ (certChain or privateKey): self.username = username self.password = password #Certificate Chain Authentication elif certChain and privateKey and not \ (username or password): self.certChain = certChain self.privateKey = privateKey #No Authentication elif not password and not username and not \ certChain and not privateKey: pass else: raise ValueError("Bad parameters") self.checker = checker self.settings = settings self.tlsSession = None def _handshake(self, tlsConnection): if self.username and self.password: tlsConnection.handshakeClientSRP(username=self.username, password=self.password, checker=self.checker, settings=self.settings, session=self.tlsSession) elif self.anon: tlsConnection.handshakeClientAnonymous(session=self.tlsSession, settings=self.settings, checker=self.checker) else: tlsConnection.handshakeClientCert(certChain=self.certChain, privateKey=self.privateKey, checker=self.checker, settings=self.settings, session=self.tlsSession) self.tlsSession = tlsConnection.sessiontlslite-ng-0.5.1/tlslite/integration/httptlsconnection.py000066400000000000000000000104611261665411200237440ustar00rootroot00000000000000# Authors: # Trevor Perrin # Kees Bos - Added ignoreAbruptClose parameter # Dimitris Moraitis - Anon ciphersuites # Martin von Loewis - python 3 port # # See the LICENSE file for legal information regarding use of this file. """TLS Lite + httplib.""" import socket try: import httplib except ImportError: # Python 3 from http import client as httplib from tlslite.tlsconnection import TLSConnection from tlslite.integration.clienthelper import ClientHelper class HTTPTLSConnection(httplib.HTTPConnection, ClientHelper): """This class extends L{httplib.HTTPConnection} to support TLS.""" def __init__(self, host, port=None, strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None, username=None, password=None, certChain=None, privateKey=None, checker=None, settings=None, ignoreAbruptClose=False, anon=False): """Create a new HTTPTLSConnection. For client authentication, use one of these argument combinations: - username, password (SRP) - certChain, privateKey (certificate) For server authentication, you can either rely on the implicit mutual authentication performed by SRP or you can do certificate-based server authentication with one of these argument combinations: - x509Fingerprint Certificate-based server authentication is compatible with SRP or certificate-based client authentication. The constructor does not perform the TLS handshake itself, but simply stores these arguments for later. The handshake is performed only when this class needs to connect with the server. Thus you should be prepared to handle TLS-specific exceptions when calling methods inherited from L{httplib.HTTPConnection} such as request(), connect(), and send(). See the client handshake functions in L{tlslite.TLSConnection.TLSConnection} for details on which exceptions might be raised. @type host: str @param host: Server to connect to. @type port: int @param port: Port to connect to. @type username: str @param username: SRP username. Requires the 'password' argument. @type password: str @param password: SRP password for mutual authentication. Requires the 'username' argument. @type certChain: L{tlslite.x509certchain.X509CertChain} or @param certChain: Certificate chain for client authentication. Requires the 'privateKey' argument. Excludes the SRP arguments. @type privateKey: L{tlslite.utils.rsakey.RSAKey} @param privateKey: Private key for client authentication. Requires the 'certChain' argument. Excludes the SRP arguments. @type checker: L{tlslite.checker.Checker} @param checker: Callable object called after handshaking to evaluate the connection and raise an Exception if necessary. @type settings: L{tlslite.handshakesettings.HandshakeSettings} @param settings: Various settings which can be used to control the ciphersuites, certificate types, and SSL/TLS versions offered by the client. @type ignoreAbruptClose: bool @param ignoreAbruptClose: ignore the TLSAbruptCloseError on unexpected hangup. """ if source_address: httplib.HTTPConnection.__init__(self, host=host, port=port, timeout=timeout, source_address=source_address) if not source_address: httplib.HTTPConnection.__init__(self, host=host, port=port, timeout=timeout) self.ignoreAbruptClose = ignoreAbruptClose ClientHelper.__init__(self, username, password, certChain, privateKey, checker, settings, anon) def connect(self): httplib.HTTPConnection.connect(self) self.sock = TLSConnection(self.sock) self.sock.ignoreAbruptClose = self.ignoreAbruptClose ClientHelper._handshake(self, self.sock) tlslite-ng-0.5.1/tlslite/integration/imap4_tls.py000066400000000000000000000065151261665411200220630ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """TLS Lite + imaplib.""" import socket from imaplib import IMAP4 from tlslite.tlsconnection import TLSConnection from tlslite.integration.clienthelper import ClientHelper # IMAP TLS PORT IMAP4_TLS_PORT = 993 class IMAP4_TLS(IMAP4, ClientHelper): """This class extends L{imaplib.IMAP4} with TLS support.""" def __init__(self, host = '', port = IMAP4_TLS_PORT, username=None, password=None, certChain=None, privateKey=None, checker=None, settings=None): """Create a new IMAP4_TLS. For client authentication, use one of these argument combinations: - username, password (SRP) - certChain, privateKey (certificate) For server authentication, you can either rely on the implicit mutual authentication performed by SRP or you can do certificate-based server authentication with one of these argument combinations: - x509Fingerprint Certificate-based server authentication is compatible with SRP or certificate-based client authentication. The caller should be prepared to handle TLS-specific exceptions. See the client handshake functions in L{tlslite.TLSConnection.TLSConnection} for details on which exceptions might be raised. @type host: str @param host: Server to connect to. @type port: int @param port: Port to connect to. @type username: str @param username: SRP username. Requires the 'password' argument. @type password: str @param password: SRP password for mutual authentication. Requires the 'username' argument. @type certChain: L{tlslite.x509certchain.X509CertChain} @param certChain: Certificate chain for client authentication. Requires the 'privateKey' argument. Excludes the SRP arguments. @type privateKey: L{tlslite.utils.rsakey.RSAKey} @param privateKey: Private key for client authentication. Requires the 'certChain' argument. Excludes the SRP arguments. @type checker: L{tlslite.checker.Checker} @param checker: Callable object called after handshaking to evaluate the connection and raise an Exception if necessary. @type settings: L{tlslite.handshakesettings.HandshakeSettings} @param settings: Various settings which can be used to control the ciphersuites, certificate types, and SSL/TLS versions offered by the client. """ ClientHelper.__init__(self, username, password, certChain, privateKey, checker, settings) IMAP4.__init__(self, host, port) def open(self, host = '', port = IMAP4_TLS_PORT): """Setup connection to remote server on "host:port". This connection will be used by the routines: read, readline, send, shutdown. """ self.host = host self.port = port self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((host, port)) self.sock = TLSConnection(self.sock) ClientHelper._handshake(self, self.sock) self.file = self.sock.makefile('rb')tlslite-ng-0.5.1/tlslite/integration/pop3_tls.py000066400000000000000000000061321261665411200217250ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """TLS Lite + poplib.""" import socket from poplib import POP3, POP3_SSL_PORT from tlslite.tlsconnection import TLSConnection from tlslite.integration.clienthelper import ClientHelper class POP3_TLS(POP3, ClientHelper): """This class extends L{poplib.POP3} with TLS support.""" def __init__(self, host, port = POP3_SSL_PORT, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, username=None, password=None, certChain=None, privateKey=None, checker=None, settings=None): """Create a new POP3_TLS. For client authentication, use one of these argument combinations: - username, password (SRP) - certChain, privateKey (certificate) For server authentication, you can either rely on the implicit mutual authentication performed by SRP or you can do certificate-based server authentication with one of these argument combinations: - x509Fingerprint Certificate-based server authentication is compatible with SRP or certificate-based client authentication. The caller should be prepared to handle TLS-specific exceptions. See the client handshake functions in L{tlslite.TLSConnection.TLSConnection} for details on which exceptions might be raised. @type host: str @param host: Server to connect to. @type port: int @param port: Port to connect to. @type username: str @param username: SRP username. @type password: str @param password: SRP password for mutual authentication. Requires the 'username' argument. @type certChain: L{tlslite.x509certchain.X509CertChain} @param certChain: Certificate chain for client authentication. Requires the 'privateKey' argument. Excludes the SRP argument. @type privateKey: L{tlslite.utils.rsakey.RSAKey} @param privateKey: Private key for client authentication. Requires the 'certChain' argument. Excludes the SRP argument. @type checker: L{tlslite.checker.Checker} @param checker: Callable object called after handshaking to evaluate the connection and raise an Exception if necessary. @type settings: L{tlslite.handshakesettings.HandshakeSettings} @param settings: Various settings which can be used to control the ciphersuites, certificate types, and SSL/TLS versions offered by the client. """ self.host = host self.port = port sock = socket.create_connection((host, port), timeout) ClientHelper.__init__(self, username, password, certChain, privateKey, checker, settings) connection = TLSConnection(sock) ClientHelper._handshake(self, connection) self.sock = connection self.file = self.sock.makefile('rb') self._debugging = 0 self.welcome = self._getresp()tlslite-ng-0.5.1/tlslite/integration/smtp_tls.py000066400000000000000000000056511261665411200220340ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """TLS Lite + smtplib.""" from smtplib import SMTP from tlslite.tlsconnection import TLSConnection from tlslite.integration.clienthelper import ClientHelper class SMTP_TLS(SMTP): """This class extends L{smtplib.SMTP} with TLS support.""" def starttls(self, username=None, password=None, certChain=None, privateKey=None, checker=None, settings=None): """Puts the connection to the SMTP server into TLS mode. If the server supports TLS, this will encrypt the rest of the SMTP session. For client authentication, use one of these argument combinations: - username, password (SRP) - certChain, privateKey (certificate) For server authentication, you can either rely on the implicit mutual authentication performed by SRP or you can do certificate-based server authentication with one of these argument combinations: - x509Fingerprint Certificate-based server authentication is compatible with SRP or certificate-based client authentication. The caller should be prepared to handle TLS-specific exceptions. See the client handshake functions in L{tlslite.TLSConnection.TLSConnection} for details on which exceptions might be raised. @type username: str @param username: SRP username. Requires the 'password' argument. @type password: str @param password: SRP password for mutual authentication. Requires the 'username' argument. @type certChain: L{tlslite.x509certchain.X509CertChain} @param certChain: Certificate chain for client authentication. Requires the 'privateKey' argument. Excludes the SRP arguments. @type privateKey: L{tlslite.utils.rsakey.RSAKey} @param privateKey: Private key for client authentication. Requires the 'certChain' argument. Excludes the SRP arguments. @type checker: L{tlslite.checker.Checker} @param checker: Callable object called after handshaking to evaluate the connection and raise an Exception if necessary. @type settings: L{tlslite.handshakesettings.HandshakeSettings} @param settings: Various settings which can be used to control the ciphersuites, certificate types, and SSL/TLS versions offered by the client. """ (resp, reply) = self.docmd("STARTTLS") if resp == 220: helper = ClientHelper( username, password, certChain, privateKey, checker, settings) conn = TLSConnection(self.sock) helper._handshake(conn) self.sock = conn self.file = conn.makefile('rb') return (resp, reply)tlslite-ng-0.5.1/tlslite/integration/tlsasyncdispatchermixin.py000066400000000000000000000114121261665411200251330ustar00rootroot00000000000000# Authors: # Trevor Perrin # Martin von Loewis - python 3 port # # See the LICENSE file for legal information regarding use of this file. """TLS Lite + asyncore.""" import asyncore from tlslite.tlsconnection import TLSConnection from .asyncstatemachine import AsyncStateMachine class TLSAsyncDispatcherMixIn(AsyncStateMachine): """This class can be "mixed in" with an L{asyncore.dispatcher} to add TLS support. This class essentially sits between the dispatcher and the select loop, intercepting events and only calling the dispatcher when applicable. In the case of handle_read(), a read operation will be activated, and when it completes, the bytes will be placed in a buffer where the dispatcher can retrieve them by calling recv(), and the dispatcher's handle_read() will be called. In the case of handle_write(), the dispatcher's handle_write() will be called, and when it calls send(), a write operation will be activated. To use this class, you must combine it with an asyncore.dispatcher, and pass in a handshake operation with setServerHandshakeOp(). Below is an example of using this class with medusa. This class is mixed in with http_channel to create http_tls_channel. Note: 1. the mix-in is listed first in the inheritance list 2. the input buffer size must be at least 16K, otherwise the dispatcher might not read all the bytes from the TLS layer, leaving some bytes in limbo. 3. IE seems to have a problem receiving a whole HTTP response in a single TLS record, so HTML pages containing '\\r\\n\\r\\n' won't be displayed on IE. Add the following text into 'start_medusa.py', in the 'HTTP Server' section:: from tlslite import * s = open("./serverX509Cert.pem").read() x509 = X509() x509.parse(s) certChain = X509CertChain([x509]) s = open("./serverX509Key.pem").read() privateKey = parsePEMKey(s, private=True) class http_tls_channel(TLSAsyncDispatcherMixIn, http_server.http_channel): ac_in_buffer_size = 16384 def __init__ (self, server, conn, addr): http_server.http_channel.__init__(self, server, conn, addr) TLSAsyncDispatcherMixIn.__init__(self, conn) self.tlsConnection.ignoreAbruptClose = True self.setServerHandshakeOp(certChain=certChain, privateKey=privateKey) hs.channel_class = http_tls_channel If the TLS layer raises an exception, the exception will be caught in asyncore.dispatcher, which will call close() on this class. The TLS layer always closes the TLS connection before raising an exception, so the close operation will complete right away, causing asyncore.dispatcher.close() to be called, which closes the socket and removes this instance from the asyncore loop. """ def __init__(self, sock=None): AsyncStateMachine.__init__(self) if sock: self.tlsConnection = TLSConnection(sock) #Calculate the sibling I'm being mixed in with. #This is necessary since we override functions #like readable(), handle_read(), etc., but we #also want to call the sibling's versions. for cl in self.__class__.__bases__: if cl != TLSAsyncDispatcherMixIn and cl != AsyncStateMachine: self.siblingClass = cl break else: raise AssertionError() def readable(self): result = self.wantsReadEvent() if result != None: return result return self.siblingClass.readable(self) def writable(self): result = self.wantsWriteEvent() if result != None: return result return self.siblingClass.writable(self) def handle_read(self): self.inReadEvent() def handle_write(self): self.inWriteEvent() def outConnectEvent(self): self.siblingClass.handle_connect(self) def outCloseEvent(self): asyncore.dispatcher.close(self) def outReadEvent(self, readBuffer): self.readBuffer = readBuffer self.siblingClass.handle_read(self) def outWriteEvent(self): self.siblingClass.handle_write(self) def recv(self, bufferSize=16384): if bufferSize < 16384 or self.readBuffer == None: raise AssertionError() returnValue = self.readBuffer self.readBuffer = None return returnValue def send(self, writeBuffer): self.setWriteOp(writeBuffer) return len(writeBuffer) def close(self): if hasattr(self, "tlsConnection"): self.setCloseOp() else: asyncore.dispatcher.close(self) tlslite-ng-0.5.1/tlslite/integration/tlssocketservermixin.py000066400000000000000000000043621261665411200244740ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """TLS Lite + SocketServer.""" from tlslite.tlsconnection import TLSConnection class TLSSocketServerMixIn: """ This class can be mixed in with any L{SocketServer.TCPServer} to add TLS support. To use this class, define a new class that inherits from it and some L{SocketServer.TCPServer} (with the mix-in first). Then implement the handshake() method, doing some sort of server handshake on the connection argument. If the handshake method returns True, the RequestHandler will be triggered. Below is a complete example of a threaded HTTPS server:: from SocketServer import * from BaseHTTPServer import * from SimpleHTTPServer import * from tlslite import * s = open("./serverX509Cert.pem").read() x509 = X509() x509.parse(s) certChain = X509CertChain([x509]) s = open("./serverX509Key.pem").read() privateKey = parsePEMKey(s, private=True) sessionCache = SessionCache() class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn, HTTPServer): def handshake(self, tlsConnection): try: tlsConnection.handshakeServer(certChain=certChain, privateKey=privateKey, sessionCache=sessionCache) tlsConnection.ignoreAbruptClose = True return True except TLSError, error: print "Handshake failure:", str(error) return False httpd = MyHTTPServer(('localhost', 443), SimpleHTTPRequestHandler) httpd.serve_forever() """ def finish_request(self, sock, client_address): tlsConnection = TLSConnection(sock) if self.handshake(tlsConnection) == True: self.RequestHandlerClass(tlsConnection, client_address, self) tlsConnection.close() #Implement this method to do some form of handshaking. Return True #if the handshake finishes properly and the request is authorized. def handshake(self, tlsConnection): raise NotImplementedError()tlslite-ng-0.5.1/tlslite/integration/xmlrpcserver.py000066400000000000000000000035221261665411200227160ustar00rootroot00000000000000# Authors: # Kees Bos # Martin von Loewis - python 3 port # # See the LICENSE file for legal information regarding use of this file. """xmlrpcserver.py - simple XML RPC server supporting TLS""" try: from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler except ImportError: # Python 3 from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler from .tlssocketservermixin import TLSSocketServerMixIn class TLSXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): """XMLRPCRequestHandler using TLS""" # Redefine the setup method (see SocketServer.StreamRequestHandler) def setup(self): self.connection = self.request if getattr(self, 'timeout', None) is not None: # Python 2.7 self.connection.settimeout(self.timeout) self.rfile = self.connection.makefile('rb', self.rbufsize) self.wfile = self.connection.makefile('wb', self.wbufsize) def do_POST(self): """Handles the HTTPS POST request.""" SimpleXMLRPCRequestHandler.do_POST(self) try: # shut down the connection self.connection.shutdown() except: pass class TLSXMLRPCServer(TLSSocketServerMixIn, SimpleXMLRPCServer): """Simple XML-RPC server using TLS""" def __init__(self, addr, *args, **kwargs): if not args and not 'requestHandler' in kwargs: kwargs['requestHandler'] = TLSXMLRPCRequestHandler SimpleXMLRPCServer.__init__(self, addr, *args, **kwargs) class MultiPathTLSXMLRPCServer(TLSXMLRPCServer): """Multipath XML-RPC Server using TLS""" def __init__(self, addr, *args, **kwargs): TLSXMLRPCServer.__init__(addr, *args, **kwargs) self.dispatchers = {} self.allow_none = allow_none self.encoding = encoding tlslite-ng-0.5.1/tlslite/integration/xmlrpctransport.py000066400000000000000000000122511261665411200234430ustar00rootroot00000000000000# Authors: # Trevor Perrin # Kees Bos - Fixes for compatibility with different Python versions # Martin von Loewis - python 3 port # # See the LICENSE file for legal information regarding use of this file. """TLS Lite + xmlrpclib.""" try: import xmlrpclib import httplib except ImportError: # Python 3 from xmlrpc import client as xmlrpclib from http import client as httplib from tlslite.integration.httptlsconnection import HTTPTLSConnection from tlslite.integration.clienthelper import ClientHelper import tlslite.errors class XMLRPCTransport(xmlrpclib.Transport, ClientHelper): """Handles an HTTPS transaction to an XML-RPC server.""" # Pre python 2.7, the make_connection returns a HTTP class transport = xmlrpclib.Transport() conn_class_is_http = not hasattr(transport, '_connection') del(transport) def __init__(self, use_datetime=0, username=None, password=None, certChain=None, privateKey=None, checker=None, settings=None, ignoreAbruptClose=False): """Create a new XMLRPCTransport. An instance of this class can be passed to L{xmlrpclib.ServerProxy} to use TLS with XML-RPC calls:: from tlslite import XMLRPCTransport from xmlrpclib import ServerProxy transport = XMLRPCTransport(user="alice", password="abra123") server = ServerProxy("https://localhost", transport) For client authentication, use one of these argument combinations: - username, password (SRP) - certChain, privateKey (certificate) For server authentication, you can either rely on the implicit mutual authentication performed by SRP or you can do certificate-based server authentication with one of these argument combinations: - x509Fingerprint Certificate-based server authentication is compatible with SRP or certificate-based client authentication. The constructor does not perform the TLS handshake itself, but simply stores these arguments for later. The handshake is performed only when this class needs to connect with the server. Thus you should be prepared to handle TLS-specific exceptions when calling methods of L{xmlrpclib.ServerProxy}. See the client handshake functions in L{tlslite.TLSConnection.TLSConnection} for details on which exceptions might be raised. @type username: str @param username: SRP username. Requires the 'password' argument. @type password: str @param password: SRP password for mutual authentication. Requires the 'username' argument. @type certChain: L{tlslite.x509certchain.X509CertChain} @param certChain: Certificate chain for client authentication. Requires the 'privateKey' argument. Excludes the SRP arguments. @type privateKey: L{tlslite.utils.rsakey.RSAKey} @param privateKey: Private key for client authentication. Requires the 'certChain' argument. Excludes the SRP arguments. @type checker: L{tlslite.checker.Checker} @param checker: Callable object called after handshaking to evaluate the connection and raise an Exception if necessary. @type settings: L{tlslite.handshakesettings.HandshakeSettings} @param settings: Various settings which can be used to control the ciphersuites, certificate types, and SSL/TLS versions offered by the client. @type ignoreAbruptClose: bool @param ignoreAbruptClose: ignore the TLSAbruptCloseError on unexpected hangup. """ # self._connection is new in python 2.7, since we're using it here, # we'll add this ourselves too, just in case we're pre-2.7 self._connection = (None, None) xmlrpclib.Transport.__init__(self, use_datetime) self.ignoreAbruptClose = ignoreAbruptClose ClientHelper.__init__(self, username, password, certChain, privateKey, checker, settings) def make_connection(self, host): # return an existing connection if possible. This allows # HTTP/1.1 keep-alive. if self._connection and host == self._connection[0]: http = self._connection[1] else: # create a HTTPS connection object from a host descriptor chost, extra_headers, x509 = self.get_host_info(host) http = HTTPTLSConnection(chost, None, username=self.username, password=self.password, certChain=self.certChain, privateKey=self.privateKey, checker=self.checker, settings=self.settings, ignoreAbruptClose=self.ignoreAbruptClose) # store the host argument along with the connection object self._connection = host, http if not self.conn_class_is_http: return http http2 = httplib.HTTP() http2._setup(http) return http2 tlslite-ng-0.5.1/tlslite/mathtls.py000066400000000000000000000327221261665411200173170ustar00rootroot00000000000000# Authors: # Trevor Perrin # Dave Baggett (Arcode Corporation) - MD5 support for MAC_SSL # Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2 # Hubert Kario - SHA384 PRF # # See the LICENSE file for legal information regarding use of this file. """Miscellaneous helper functions.""" from .utils.compat import * from .utils.cryptomath import * from .constants import CipherSuite import hmac #1024, 1536, 2048, 3072, 4096, 6144, and 8192 bit groups] goodGroupParameters = [(2,0xEEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9AFD5138FE8376435B9FC61D2FC0EB06E3),\ (2,0x9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA9614B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F84380B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0BE3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF56EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734AF7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB),\ (2,0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73),\ (2,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF),\ (5,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF),\ (5,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF),\ (5,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF)] def P_hash(macFunc, secret, seed, length): bytes = bytearray(length) A = seed index = 0 while 1: A = macFunc(secret, A) output = macFunc(secret, A + seed) for c in output: if index >= length: return bytes bytes[index] = c index += 1 return bytes def PRF(secret, label, seed, length): #Split the secret into left and right halves # which may share a byte if len is odd S1 = secret[ : int(math.ceil(len(secret)/2.0))] S2 = secret[ int(math.floor(len(secret)/2.0)) : ] #Run the left half through P_MD5 and the right half through P_SHA1 p_md5 = P_hash(HMAC_MD5, S1, label + seed, length) p_sha1 = P_hash(HMAC_SHA1, S2, label + seed, length) #XOR the output values and return the result for x in range(length): p_md5[x] ^= p_sha1[x] return p_md5 def PRF_1_2(secret, label, seed, length): """Pseudo Random Function for TLS1.2 ciphers that use SHA256""" return P_hash(HMAC_SHA256, secret, label + seed, length) def PRF_1_2_SHA384(secret, label, seed, length): """Pseudo Random Function for TLS1.2 ciphers that use SHA384""" return P_hash(HMAC_SHA384, secret, label + seed, length) def PRF_SSL(secret, seed, length): bytes = bytearray(length) index = 0 for x in range(26): A = bytearray([ord('A')+x] * (x+1)) # 'A', 'BB', 'CCC', etc.. input = secret + SHA1(A + secret + seed) output = MD5(input) for c in output: if index >= length: return bytes bytes[index] = c index += 1 return bytes def calcMasterSecret(version, cipherSuite, premasterSecret, clientRandom, serverRandom): """Derive Master Secret from premaster secret and random values""" if version == (3,0): masterSecret = PRF_SSL(premasterSecret, clientRandom + serverRandom, 48) elif version in ((3,1), (3,2)): masterSecret = PRF(premasterSecret, b"master secret", clientRandom + serverRandom, 48) elif version == (3,3): if cipherSuite in CipherSuite.sha384PrfSuites: masterSecret = PRF_1_2_SHA384(premasterSecret, b"master secret", clientRandom + serverRandom, 48) else: masterSecret = PRF_1_2(premasterSecret, b"master secret", clientRandom + serverRandom, 48) else: raise AssertionError() return masterSecret def calcFinished(version, masterSecret, cipherSuite, handshakeHashes, isClient): """Calculate the Handshake protocol Finished value @param version: TLS protocol version tuple @param masterSecret: negotiated master secret of the connection @param cipherSuite: negotiated cipher suite of the connection, @param handshakeHashes: running hash of the handshake messages @param isClient: whether the calculation should be performed for message sent by client (True) or by server (False) side of connection """ assert version in ((3, 0), (3, 1), (3, 2), (3, 3)) if version == (3,0): if isClient: senderStr = b"\x43\x4C\x4E\x54" else: senderStr = b"\x53\x52\x56\x52" verifyData = handshakeHashes.digestSSL(masterSecret, senderStr) else: if isClient: label = b"client finished" else: label = b"server finished" if version in ((3,1), (3,2)): handshakeHash = handshakeHashes.digest() verifyData = PRF(masterSecret, label, handshakeHash, 12) else: # version == (3,3): if cipherSuite in CipherSuite.sha384PrfSuites: handshakeHash = handshakeHashes.digest('sha384') verifyData = PRF_1_2_SHA384(masterSecret, label, handshakeHash, 12) else: handshakeHash = handshakeHashes.digest('sha256') verifyData = PRF_1_2(masterSecret, label, handshakeHash, 12) return verifyData def makeX(salt, username, password): if len(username)>=256: raise ValueError("username too long") if len(salt)>=256: raise ValueError("salt too long") innerHashResult = SHA1(username + bytearray(b":") + password) outerHashResult = SHA1(salt + innerHashResult) return bytesToNumber(outerHashResult) #This function is used by VerifierDB.makeVerifier def makeVerifier(username, password, bits): bitsIndex = {1024:0, 1536:1, 2048:2, 3072:3, 4096:4, 6144:5, 8192:6}[bits] g,N = goodGroupParameters[bitsIndex] salt = getRandomBytes(16) x = makeX(salt, username, password) verifier = powMod(g, x, N) return N, g, salt, verifier def PAD(n, x): nLength = len(numberToByteArray(n)) b = numberToByteArray(x) if len(b) < nLength: b = (b"\0" * (nLength-len(b))) + b return b def makeU(N, A, B): return bytesToNumber(SHA1(PAD(N, A) + PAD(N, B))) def makeK(N, g): return bytesToNumber(SHA1(numberToByteArray(N) + PAD(N, g))) def createHMAC(k, digestmod=hashlib.sha1): h = hmac.HMAC(k, digestmod=digestmod) h.block_size = digestmod().block_size return h def createMAC_SSL(k, digestmod=None): mac = MAC_SSL() mac.create(k, digestmod=digestmod) return mac class MAC_SSL(object): def create(self, k, digestmod=None): self.digestmod = digestmod or hashlib.sha1 self.block_size = self.digestmod().block_size # Repeat pad bytes 48 times for MD5; 40 times for other hash functions. self.digest_size = 16 if (self.digestmod is hashlib.md5) else 20 repeat = 40 if self.digest_size == 20 else 48 opad = b"\x5C" * repeat ipad = b"\x36" * repeat self.ohash = self.digestmod(k + opad) self.ihash = self.digestmod(k + ipad) def update(self, m): self.ihash.update(m) def copy(self): new = MAC_SSL() new.ihash = self.ihash.copy() new.ohash = self.ohash.copy() new.digestmod = self.digestmod new.digest_size = self.digest_size new.block_size = self.block_size return new def digest(self): ohash2 = self.ohash.copy() ohash2.update(self.ihash.digest()) return bytearray(ohash2.digest()) tlslite-ng-0.5.1/tlslite/messages.py000066400000000000000000001255221261665411200174530ustar00rootroot00000000000000# Authors: # Trevor Perrin # Google - handling CertificateRequest.certificate_types # Google (adapted by Sam Rushing and Marcelo Fernandez) - NPN support # Dimitris Moraitis - Anon ciphersuites # Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2 # Hubert Kario - 'extensions' cleanup # # See the LICENSE file for legal information regarding use of this file. """Classes representing TLS messages.""" from .utils.compat import * from .utils.cryptomath import * from .errors import * from .utils.codec import * from .constants import * from .x509 import X509 from .x509certchain import X509CertChain from .utils.tackwrapper import * from .extensions import * class RecordHeader(object): """Generic interface to SSLv2 and SSLv3 (and later) record headers""" def __init__(self, ssl2): """define instance variables""" self.type = 0 self.version = (0, 0) self.length = 0 self.ssl2 = ssl2 class RecordHeader3(RecordHeader): """SSLv3 (and later) TLS record header""" def __init__(self): """Define a SSLv3 style class""" super(RecordHeader3, self).__init__(ssl2=False) def create(self, version, type, length): """Set object values for writing (serialisation)""" self.type = type self.version = version self.length = length return self def write(self): """Serialise object to bytearray""" writer = Writer() writer.add(self.type, 1) writer.add(self.version[0], 1) writer.add(self.version[1], 1) writer.add(self.length, 2) return writer.bytes def parse(self, parser): """Deserialise object from Parser""" self.type = parser.get(1) self.version = (parser.get(1), parser.get(1)) self.length = parser.get(2) self.ssl2 = False return self @property def typeName(self): matching = [x[0] for x in ContentType.__dict__.items() if x[1] == self.type] if len(matching) == 0: return "unknown(" + str(self.type) + ")" else: return str(matching[0]) def __str__(self): return "SSLv3 record,version({0[0]}.{0[1]}),"\ "content type({1}),length({2})".format(self.version, self.typeName, self.length) def __repr__(self): return "RecordHeader3(type={0}, version=({1[0]}.{1[1]}), length={2})".\ format(self.type, self.version, self.length) class RecordHeader2(RecordHeader): """SSLv2 record header (just reading)""" def __init__(self): """Define a SSLv2 style class""" super(RecordHeader2, self).__init__(ssl2=True) def parse(self, parser): """Deserialise object from Parser""" if parser.get(1) != 128: raise SyntaxError() self.type = ContentType.handshake self.version = (2, 0) #XXX We don't support 2-byte-length-headers; could be a problem self.length = parser.get(1) return self class Message(object): """Generic TLS message""" def __init__(self, contentType, data): """ Initialize object with specified contentType and data @type contentType: int @param contentType: TLS record layer content type of associated data @type data: bytearray @param data: data """ self.contentType = contentType self.data = data def write(self): """Return serialised object data""" return self.data class Alert(object): def __init__(self): self.contentType = ContentType.alert self.level = 0 self.description = 0 def create(self, description, level=AlertLevel.fatal): self.level = level self.description = description return self def parse(self, p): p.setLengthCheck(2) self.level = p.get(1) self.description = p.get(1) p.stopLengthCheck() return self def write(self): w = Writer() w.add(self.level, 1) w.add(self.description, 1) return w.bytes @property def levelName(self): matching = [x[0] for x in AlertLevel.__dict__.items() if x[1] == self.level] if len(matching) == 0: return "unknown({0})".format(self.level) else: return str(matching[0]) @property def descriptionName(self): matching = [x[0] for x in AlertDescription.__dict__.items() if x[1] == self.description] if len(matching) == 0: return "unknown({0})".format(self.description) else: return str(matching[0]) def __str__(self): return "Alert, level:{0}, description:{1}".format(self.levelName, self.descriptionName) def __repr__(self): return "Alert(level={0}, description={1})".format(self.level, self.description) class HandshakeMsg(object): def __init__(self, handshakeType): self.contentType = ContentType.handshake self.handshakeType = handshakeType def postWrite(self, w): headerWriter = Writer() headerWriter.add(self.handshakeType, 1) headerWriter.add(len(w.bytes), 3) return headerWriter.bytes + w.bytes class ClientHello(HandshakeMsg): """ Class for handling the ClientHello TLS message, supports both the SSLv2 and SSLv3 style messages. @type certificate_types: list @ivar certificate_types: list of supported certificate types (deprecated) @type srp_username: bytearray @ivar srp_username: name of the user in SRP extension (deprecated) @type supports_npn: boolean @ivar supports_npn: NPN extension presence (deprecated) @type tack: boolean @ivar tack: TACK extension presence (deprecated) @type server_name: bytearray @ivar server_name: first host_name (type 0) present in SNI extension (deprecated) @type extensions: list of L{TLSExtension} @ivar extensions: list of TLS extensions parsed from wire or to send, see L{TLSExtension} and child classes for exact examples """ def __init__(self, ssl2=False): HandshakeMsg.__init__(self, HandshakeType.client_hello) self.ssl2 = ssl2 self.client_version = (0,0) self.random = bytearray(32) self.session_id = bytearray(0) self.cipher_suites = [] # a list of 16-bit values self.compression_methods = [] # a list of 8-bit values self.extensions = None def __str__(self): """ Return human readable representation of Client Hello @rtype: str """ if self.session_id.count(bytearray(b'\x00')) == len(self.session_id)\ and len(self.session_id) != 0: session = "bytearray(b'\\x00'*{0})".format(len(self.session_id)) else: session = repr(self.session_id) ret = "client_hello,version({0[0]}.{0[1]}),random(...),"\ "session ID({1!s}),cipher suites({2!r}),"\ "compression methods({3!r})".format( self.client_version, session, self.cipher_suites, self.compression_methods) if self.extensions is not None: ret += ",extensions({0!r})".format(self.extensions) return ret def __repr__(self): """ Return machine readable representation of Client Hello @rtype: str """ return "ClientHello(ssl2={0}, client_version=({1[0]}.{1[1]}), "\ "random={2!r}, session_id={3!r}, cipher_suites={4!r}, "\ "compression_methods={5}, extensions={6})".format(\ self.ssl2, self.client_version, self.random, self.session_id, self.cipher_suites, self.compression_methods, self.extensions) def getExtension(self, extType): """ Returns extension of given type if present, None otherwise @rtype: L{tlslite.extensions.TLSExtension} @raise TLSInternalError: when there are multiple extensions of the same type """ if self.extensions is None: return None exts = [ext for ext in self.extensions if ext.extType == extType] if len(exts) > 1: raise TLSInternalError( "Multiple extensions of the same type present") elif len(exts) == 1: return exts[0] else: return None def addExtension(self, ext): """ Adds extension to internal list of extensions @type ext: TLSExtension @param ext: extension object to add to list """ if self.extensions is None: self.extensions = [] self.extensions.append(ext) @property def certificate_types(self): """ Returns the list of certificate types supported. @deprecated: use extensions field to get the extension for inspection """ cert_type = self.getExtension(ExtensionType.cert_type) if cert_type is None: # XXX backwards compatibility: TLSConnection # depends on a default value of this property return [CertificateType.x509] else: return cert_type.certTypes @certificate_types.setter def certificate_types(self, val): """ Sets the list of supported types to list given in L{val} if the cert_type extension is present. Creates the extension and places it last in the list otherwise. @type val: list @param val: list of supported certificate types by client encoded as single byte integers """ cert_type = self.getExtension(ExtensionType.cert_type) if cert_type is None: ext = ClientCertTypeExtension().create(val) self.addExtension(ext) else: cert_type.certTypes = val @property def srp_username(self): """ Returns username for the SRP. @deprecated: use extensions field to get the extension for inspection """ srp_ext = self.getExtension(ExtensionType.srp) if srp_ext is None: return None else: return srp_ext.identity @srp_username.setter def srp_username(self, name): """ Sets the username for SRP. @type name: bytearray @param name: UTF-8 encoded username """ srp_ext = self.getExtension(ExtensionType.srp) if srp_ext is None: ext = SRPExtension().create(name) self.addExtension(ext) else: srp_ext.identity = name @property def tack(self): """ Returns whatever the client supports TACK @rtype: boolean @deprecated: use extensions field to get the extension for inspection """ tack_ext = self.getExtension(ExtensionType.tack) if tack_ext is None: return False else: return True @tack.setter def tack(self, present): """ Creates or deletes the TACK extension. @type present: boolean @param present: True will create extension while False will remove extension from client hello """ if present: tack_ext = self.getExtension(ExtensionType.tack) if tack_ext is None: ext = TLSExtension().create(ExtensionType.tack, bytearray(0)) self.addExtension(ext) else: return else: if self.extensions is None: return # remove all extensions of this type without changing reference self.extensions[:] = [ext for ext in self.extensions if ext.extType != ExtensionType.tack] @property def supports_npn(self): """ Returns whatever client supports NPN extension @rtype: boolean @deprecated: use extensions field to get the extension for inspection """ npn_ext = self.getExtension(ExtensionType.supports_npn) if npn_ext is None: return False else: return True @supports_npn.setter def supports_npn(self, present): """ Creates or deletes the NPN extension @type present: boolean @param present: selects whatever to create or remove the extension from list of supported ones """ if present: npn_ext = self.getExtension(ExtensionType.supports_npn) if npn_ext is None: ext = TLSExtension().create( ExtensionType.supports_npn, bytearray(0)) self.addExtension(ext) else: return else: if self.extensions is None: return #remove all extension of this type without changing reference self.extensions[:] = [ext for ext in self.extensions if ext.extType != ExtensionType.supports_npn] @property def server_name(self): """ Returns first host_name present in SNI extension @rtype: bytearray @deprecated: use extensions field to get the extension for inspection """ sni_ext = self.getExtension(ExtensionType.server_name) if sni_ext is None: return bytearray(0) else: if len(sni_ext.hostNames) > 0: return sni_ext.hostNames[0] else: return bytearray(0) @server_name.setter def server_name(self, hostname): """ Sets the first host_name present in SNI extension @type hostname: bytearray @param hostname: name of the host_name to set """ sni_ext = self.getExtension(ExtensionType.server_name) if sni_ext is None: sni_ext = SNIExtension().create(hostname) self.addExtension(sni_ext) else: names = list(sni_ext.hostNames) names[0] = hostname sni_ext.hostNames = names def create(self, version, random, session_id, cipher_suites, certificate_types=None, srpUsername=None, tack=False, supports_npn=False, serverName=None, extensions=None): """ Create a ClientHello message for sending. @type version: tuple @param version: the highest supported TLS version encoded as two int tuple @type random: bytearray @param random: client provided random value, in old versions of TLS (before 1.2) the first 32 bits should include system time @type session_id: bytearray @param session_id: ID of session, set when doing session resumption @type cipher_suites: list @param cipher_suites: list of ciphersuites advertised as supported @type certificate_types: list @param certificate_types: list of supported certificate types, uses TLS extension for signalling, as such requires TLS1.0 to work @type srpUsername: bytearray @param srpUsername: utf-8 encoded username for SRP, TLS extension @type tack: boolean @param tack: whatever to advertise support for TACK, TLS extension @type supports_npn: boolean @param supports_npn: whatever to advertise support for NPN, TLS extension @type serverName: bytearray @param serverName: the hostname to request in server name indication extension, TLS extension. Note that SNI allows to set multiple hostnames and values that are not hostnames, use L{SNIExtension} together with L{extensions} to use it. @type extensions: list of L{TLSExtension} @param extensions: list of extensions to advertise """ self.client_version = version self.random = random self.session_id = session_id self.cipher_suites = cipher_suites self.compression_methods = [0] if not extensions is None: self.extensions = extensions if not certificate_types is None: self.certificate_types = certificate_types if not srpUsername is None: self.srp_username = bytearray(srpUsername, "utf-8") self.tack = tack self.supports_npn = supports_npn if not serverName is None: self.server_name = bytearray(serverName, "utf-8") return self def parse(self, p): if self.ssl2: self.client_version = (p.get(1), p.get(1)) cipherSpecsLength = p.get(2) sessionIDLength = p.get(2) randomLength = p.get(2) self.cipher_suites = p.getFixList(3, cipherSpecsLength//3) self.session_id = p.getFixBytes(sessionIDLength) self.random = p.getFixBytes(randomLength) if len(self.random) < 32: zeroBytes = 32-len(self.random) self.random = bytearray(zeroBytes) + self.random self.compression_methods = [0]#Fake this value #We're not doing a stopLengthCheck() for SSLv2, oh well.. else: p.startLengthCheck(3) self.client_version = (p.get(1), p.get(1)) self.random = p.getFixBytes(32) self.session_id = p.getVarBytes(1) self.cipher_suites = p.getVarList(2, 2) self.compression_methods = p.getVarList(1, 1) if not p.atLengthCheck(): self.extensions = [] totalExtLength = p.get(2) while not p.atLengthCheck(): ext = TLSExtension().parse(p) self.extensions += [ext] p.stopLengthCheck() return self def write(self): w = Writer() w.add(self.client_version[0], 1) w.add(self.client_version[1], 1) w.addFixSeq(self.random, 1) w.addVarSeq(self.session_id, 1, 1) w.addVarSeq(self.cipher_suites, 2, 2) w.addVarSeq(self.compression_methods, 1, 1) if not self.extensions is None: w2 = Writer() for ext in self.extensions: w2.bytes += ext.write() w.add(len(w2.bytes), 2) w.bytes += w2.bytes return self.postWrite(w) class ServerHello(HandshakeMsg): """server_hello message @type server_version: tuple @ivar server_version: protocol version encoded as two int tuple @type random: bytearray @ivar random: server random value @type session_id: bytearray @ivar session_id: session identifier for resumption @type cipher_suite: int @ivar cipher_suite: server selected cipher_suite @type compression_method: int @ivar compression_method: server selected compression method @type next_protos: list of bytearray @ivar next_protos: list of advertised protocols in NPN extension @type next_protos_advertised: list of bytearray @ivar next_protos_advertised: list of protocols advertised in NPN extension @type certificate_type: int @ivar certificate_type: certificate type selected by server @type extensions: list @ivar extensions: list of TLS extensions present in server_hello message, see L{TLSExtension} and child classes for exact examples """ def __init__(self): """Initialise ServerHello object""" HandshakeMsg.__init__(self, HandshakeType.server_hello) self.server_version = (0,0) self.random = bytearray(32) self.session_id = bytearray(0) self.cipher_suite = 0 self.compression_method = 0 self._tack_ext = None self.extensions = None def __str__(self): base = "server_hello,length({0}),version({1[0]}.{1[1]}),random(...),"\ "session ID({2!r}),cipher({3:#x}),compression method({4})"\ .format(len(self.write())-4, self.server_version, self.session_id, self.cipher_suite, self.compression_method) if self.extensions is None: return base ret = ",extensions[" ret += ",".join(repr(x) for x in self.extensions) ret += "]" return base + ret def __repr__(self): return "ServerHello(server_version=({0[0]}.{0[1]}), random={1!r}, "\ "session_id={2!r}, cipher_suite={3}, compression_method={4}, "\ "_tack_ext={5}, extensions={6!r})".format(\ self.server_version, self.random, self.session_id, self.cipher_suite, self.compression_method, self._tack_ext, self.extensions) def getExtension(self, extType): """Return extension of a given type, None if extension of given type is not present @rtype: L{TLSExtension} @raise TLSInternalError: multiple extensions of the same type present """ if self.extensions is None: return None exts = [ext for ext in self.extensions if ext.extType == extType] if len(exts) > 1: raise TLSInternalError( "Multiple extensions of the same type present") elif len(exts) == 1: return exts[0] else: return None def addExtension(self, ext): """ Add extension to internal list of extensions @type ext: TLSExtension @param ext: extension to add to list """ if self.extensions is None: self.extensions = [] self.extensions.append(ext) @property def tackExt(self): """ Returns the TACK extension """ if self._tack_ext is None: ext = self.getExtension(ExtensionType.tack) if ext is None or not tackpyLoaded: return None else: self._tack_ext = TackExtension(ext.extData) return self._tack_ext @tackExt.setter def tackExt(self, val): """ Set the TACK extension """ self._tack_ext = val # makes sure that extensions are included in the on the wire encoding if not val is None: if self.extensions is None: self.extensions = [] @property def certificate_type(self): """Returns the certificate type selected by server @rtype: int """ cert_type = self.getExtension(ExtensionType.cert_type) if cert_type is None: # XXX backwards compatibility, TLSConnection expects the default # value to be that return CertificateType.x509 return cert_type.cert_type @certificate_type.setter def certificate_type(self, val): """Sets the certificate type supported @type val: int @param val: type of certificate """ # XXX backwards compatibility, 0 means x.509 and should not be sent if val == 0 or val is None: return cert_type = self.getExtension(ExtensionType.cert_type) if cert_type is None: ext = ServerCertTypeExtension().create(val) self.addExtension(ext) else: cert_type.cert_type = val @property def next_protos(self): """Returns the advertised protocols in NPN extension @rtype: list of bytearrays """ npn_ext = self.getExtension(ExtensionType.supports_npn) if npn_ext is None: return None else: return npn_ext.protocols @next_protos.setter def next_protos(self, val): """Sets the advertised protocols in NPN extension @type val: list @param val: list of protocols to advertise as UTF-8 encoded names """ if val is None: return else: # convinience function, make sure the values are properly encoded val = [ bytearray(x) for x in val ] npn_ext = self.getExtension(ExtensionType.supports_npn) if npn_ext is None: ext = NPNExtension().create(val) self.addExtension(ext) else: npn_ext.protocols = val @property def next_protos_advertised(self): """Returns the advertised protocols in NPN extension @rtype: list of bytearrays """ return self.next_protos @next_protos_advertised.setter def next_protos_advertised(self, val): """Sets the advertised protocols in NPN extension @type val: list @param val: list of protocols to advertise as UTF-8 encoded names """ self.next_protos = val def create(self, version, random, session_id, cipher_suite, certificate_type=None, tackExt=None, next_protos_advertised=None, extensions=None): """Initialize the object for deserialisation""" self.extensions = extensions self.server_version = version self.random = random self.session_id = session_id self.cipher_suite = cipher_suite self.certificate_type = certificate_type self.compression_method = 0 if tackExt is not None: self.tackExt = tackExt self.next_protos_advertised = next_protos_advertised return self def parse(self, p): p.startLengthCheck(3) self.server_version = (p.get(1), p.get(1)) self.random = p.getFixBytes(32) self.session_id = p.getVarBytes(1) self.cipher_suite = p.get(2) self.compression_method = p.get(1) if not p.atLengthCheck(): self.extensions = [] totalExtLength = p.get(2) p2 = Parser(p.getFixBytes(totalExtLength)) while p2.getRemainingLength() > 0: ext = TLSExtension(server=True).parse(p2) self.extensions += [ext] p.stopLengthCheck() return self def write(self): w = Writer() w.add(self.server_version[0], 1) w.add(self.server_version[1], 1) w.addFixSeq(self.random, 1) w.addVarSeq(self.session_id, 1, 1) w.add(self.cipher_suite, 2) w.add(self.compression_method, 1) if not self.extensions is None: w2 = Writer() for ext in self.extensions: w2.bytes += ext.write() if self.tackExt: b = self.tackExt.serialize() w2.add(ExtensionType.tack, 2) w2.add(len(b), 2) w2.bytes += b w.add(len(w2.bytes), 2) w.bytes += w2.bytes return self.postWrite(w) class Certificate(HandshakeMsg): def __init__(self, certificateType): HandshakeMsg.__init__(self, HandshakeType.certificate) self.certificateType = certificateType self.certChain = None def create(self, certChain): self.certChain = certChain return self def parse(self, p): p.startLengthCheck(3) if self.certificateType == CertificateType.x509: chainLength = p.get(3) index = 0 certificate_list = [] while index != chainLength: certBytes = p.getVarBytes(3) x509 = X509() x509.parseBinary(certBytes) certificate_list.append(x509) index += len(certBytes)+3 if certificate_list: self.certChain = X509CertChain(certificate_list) else: raise AssertionError() p.stopLengthCheck() return self def write(self): w = Writer() if self.certificateType == CertificateType.x509: chainLength = 0 if self.certChain: certificate_list = self.certChain.x509List else: certificate_list = [] #determine length for cert in certificate_list: bytes = cert.writeBytes() chainLength += len(bytes)+3 #add bytes w.add(chainLength, 3) for cert in certificate_list: bytes = cert.writeBytes() w.addVarSeq(bytes, 1, 3) else: raise AssertionError() return self.postWrite(w) class CertificateRequest(HandshakeMsg): def __init__(self, version): HandshakeMsg.__init__(self, HandshakeType.certificate_request) self.certificate_types = [] self.certificate_authorities = [] self.version = version self.supported_signature_algs = [] def create(self, certificate_types, certificate_authorities, sig_algs=()): self.certificate_types = certificate_types self.certificate_authorities = certificate_authorities self.supported_signature_algs = sig_algs return self def parse(self, p): p.startLengthCheck(3) self.certificate_types = p.getVarList(1, 1) if self.version >= (3,3): self.supported_signature_algs = p.getVarTupleList(1, 2, 2) ca_list_length = p.get(2) index = 0 self.certificate_authorities = [] while index != ca_list_length: ca_bytes = p.getVarBytes(2) self.certificate_authorities.append(ca_bytes) index += len(ca_bytes)+2 p.stopLengthCheck() return self def write(self): w = Writer() w.addVarSeq(self.certificate_types, 1, 1) if self.version >= (3,3): w.addVarTupleSeq(self.supported_signature_algs, 1, 2) caLength = 0 #determine length for ca_dn in self.certificate_authorities: caLength += len(ca_dn)+2 w.add(caLength, 2) #add bytes for ca_dn in self.certificate_authorities: w.addVarSeq(ca_dn, 1, 2) return self.postWrite(w) class ServerKeyExchange(HandshakeMsg): """ Handling TLS Handshake protocol Server Key Exchange messages @type cipherSuite: int @cvar cipherSuite: id of ciphersuite selected in Server Hello message @type srp_N: int @cvar srp_N: SRP protocol prime @type srp_g: int @cvar srp_g: SRP protocol generator @type srp_s: bytearray @cvar srp_s: SRP protocol salt value @type srp_B: int @cvar srp_B: SRP protocol server public value @type dh_p: int @cvar dh_p: FFDHE protocol prime @type dh_g: int @cvar dh_g: FFDHE protocol generator @type dh_Ys: int @cvar dh_Ys: FFDH protocol server key share @type signature: bytearray @cvar signature: signature performed over the parameters by server @type hashAlg: int @cvar hashAlg: id of hash algorithm used for signature @type signAlg: int @cvar signAlg: id of signature algorithm used for signature """ def __init__(self, cipherSuite, version): """ Initialise Server Key Exchange for reading or writing @type cipherSuite: int @param cipherSuite: id of ciphersuite selected by server """ HandshakeMsg.__init__(self, HandshakeType.server_key_exchange) self.cipherSuite = cipherSuite self.version = version self.srp_N = 0 self.srp_g = 0 self.srp_s = bytearray(0) self.srp_B = 0 # Anon DH params: self.dh_p = 0 self.dh_g = 0 self.dh_Ys = 0 # signature for certificate authenticated ciphersuites self.signature = bytearray(0) # signature hash algorithm and signing algorithm for TLSv1.2 self.hashAlg = 0 self.signAlg = 0 def __repr__(self): ret = "ServerKeyExchange(cipherSuite=CipherSuite.{0}, version={1}"\ "".format(CipherSuite.ietfNames[self.cipherSuite], self.version) if self.srp_N != 0: ret += ", srp_N={0}, srp_g={1}, srp_s={2!r}, srp_B={3}".format(\ self.srp_N, self.srp_g, self.srp_s, self.srp_B) if self.dh_p != 0: ret += ", dh_p={0}, dh_g={1}, dh_Ys={2}".format(\ self.dh_p, self.dh_g, self.dh_Ys) if self.signAlg != 0: ret += ", hashAlg={0}, signAlg={1}".format(\ self.hashAlg, self.signAlg) if self.signature != bytearray(0): ret += ", signature={0!r}".format(self.signature) ret += ")" return ret def createSRP(self, srp_N, srp_g, srp_s, srp_B): """Set SRP protocol parameters""" self.srp_N = srp_N self.srp_g = srp_g self.srp_s = srp_s self.srp_B = srp_B return self def createDH(self, dh_p, dh_g, dh_Ys): """Set FFDH protocol parameters""" self.dh_p = dh_p self.dh_g = dh_g self.dh_Ys = dh_Ys return self def parse(self, parser): """Deserialise message from L{Parser} @type parser: L{Parser} @param parser: parser to read data from """ parser.startLengthCheck(3) if self.cipherSuite in CipherSuite.srpAllSuites: self.srp_N = bytesToNumber(parser.getVarBytes(2)) self.srp_g = bytesToNumber(parser.getVarBytes(2)) self.srp_s = parser.getVarBytes(1) self.srp_B = bytesToNumber(parser.getVarBytes(2)) elif self.cipherSuite in CipherSuite.dhAllSuites: self.dh_p = bytesToNumber(parser.getVarBytes(2)) self.dh_g = bytesToNumber(parser.getVarBytes(2)) self.dh_Ys = bytesToNumber(parser.getVarBytes(2)) else: raise AssertionError() if self.cipherSuite in CipherSuite.certAllSuites: if self.version == (3, 3): self.hashAlg = parser.get(1) self.signAlg = parser.get(1) self.signature = parser.getVarBytes(2) parser.stopLengthCheck() return self def writeParams(self): """Serialise the key exchange parameters @rtype: bytearray """ writer = Writer() if self.cipherSuite in CipherSuite.srpAllSuites: writer.addVarSeq(numberToByteArray(self.srp_N), 1, 2) writer.addVarSeq(numberToByteArray(self.srp_g), 1, 2) writer.addVarSeq(self.srp_s, 1, 1) writer.addVarSeq(numberToByteArray(self.srp_B), 1, 2) elif self.cipherSuite in CipherSuite.dhAllSuites: writer.addVarSeq(numberToByteArray(self.dh_p), 1, 2) writer.addVarSeq(numberToByteArray(self.dh_g), 1, 2) writer.addVarSeq(numberToByteArray(self.dh_Ys), 1, 2) else: assert(False) return writer.bytes def write(self): """ Serialise complete message @rtype: bytearray """ writer = Writer() writer.bytes += self.writeParams() if self.cipherSuite in CipherSuite.certAllSuites: if self.version >= (3, 3): assert self.hashAlg != 0 and self.signAlg != 0 writer.add(self.hashAlg, 1) writer.add(self.signAlg, 1) writer.addVarSeq(self.signature, 1, 2) return self.postWrite(writer) def hash(self, clientRandom, serverRandom): """ Calculate hash of paramters to sign @rtype: bytearray """ bytesToHash = clientRandom + serverRandom + self.writeParams() if self.version >= (3, 3): # TODO: Signature algorithm negotiation not supported. return SHA1(bytesToHash) return MD5(bytesToHash) + SHA1(bytesToHash) class ServerHelloDone(HandshakeMsg): def __init__(self): HandshakeMsg.__init__(self, HandshakeType.server_hello_done) def create(self): return self def parse(self, p): p.startLengthCheck(3) p.stopLengthCheck() return self def write(self): w = Writer() return self.postWrite(w) class ClientKeyExchange(HandshakeMsg): """ Handling of TLS Handshake protocol ClientKeyExchange message @type cipherSuite: int @ivar cipherSuite: the cipher suite id used for the connection @type version: tuple(int, int) @ivar version: TLS protocol version used for the connection @type srp_A: int @ivar srp_A: SRP protocol client answer value @type dh_Yc: int @ivar dh_Yc: client Finite Field Diffie-Hellman protocol key share @type encryptedPreMasterSecret: bytearray @ivar encryptedPreMasterSecret: client selected PremMaster secret encrypted with server public key (from certificate) """ def __init__(self, cipherSuite, version=None): """ Initialise ClientKeyExchange for reading or writing @type cipherSuite: int @param cipherSuite: id of the ciphersuite selected by server @type version: tuple(int, int) @param version: protocol version selected by server """ HandshakeMsg.__init__(self, HandshakeType.client_key_exchange) self.cipherSuite = cipherSuite self.version = version self.srp_A = 0 self.dh_Yc = 0 self.encryptedPreMasterSecret = bytearray(0) def createSRP(self, srp_A): """ Set the SRP client answer returns self @type srp_A: int @param srp_A: client SRP answer @rtype: L{ClientKeyExchange} """ self.srp_A = srp_A return self def createRSA(self, encryptedPreMasterSecret): """ Set the encrypted PreMaster Secret returns self @type encryptedPreMasterSecret: bytearray @rtype: L{ClientKeyExchange} """ self.encryptedPreMasterSecret = encryptedPreMasterSecret return self def createDH(self, dh_Yc): """ Set the client FFDH key share returns self @type dh_Yc: int @rtype: L{ClientKeyExchange} """ self.dh_Yc = dh_Yc return self def parse(self, parser): """ Deserialise the message from L{Parser} returns self @type parser: L{Parser} @rtype: L{ClientKeyExchange} """ parser.startLengthCheck(3) if self.cipherSuite in CipherSuite.srpAllSuites: self.srp_A = bytesToNumber(parser.getVarBytes(2)) elif self.cipherSuite in CipherSuite.certSuites: if self.version in ((3,1), (3,2), (3,3)): self.encryptedPreMasterSecret = parser.getVarBytes(2) elif self.version == (3,0): self.encryptedPreMasterSecret = \ parser.getFixBytes(parser.getRemainingLength()) else: raise AssertionError() elif self.cipherSuite in CipherSuite.dhAllSuites: self.dh_Yc = bytesToNumber(parser.getVarBytes(2)) else: raise AssertionError() parser.stopLengthCheck() return self def write(self): """ Serialise the object @rtype: bytearray """ w = Writer() if self.cipherSuite in CipherSuite.srpAllSuites: w.addVarSeq(numberToByteArray(self.srp_A), 1, 2) elif self.cipherSuite in CipherSuite.certSuites: if self.version in ((3,1), (3,2), (3,3)): w.addVarSeq(self.encryptedPreMasterSecret, 1, 2) elif self.version == (3,0): w.addFixSeq(self.encryptedPreMasterSecret, 1) else: raise AssertionError() elif self.cipherSuite in CipherSuite.dhAllSuites: w.addVarSeq(numberToByteArray(self.dh_Yc), 1, 2) else: raise AssertionError() return self.postWrite(w) class CertificateVerify(HandshakeMsg): """Serializer for TLS handshake protocol Certificate Verify message""" def __init__(self, version): """Create message @param version: TLS protocol version in use """ HandshakeMsg.__init__(self, HandshakeType.certificate_verify) self.version = version self.signatureAlgorithm = None self.signature = bytearray(0) def create(self, signature, signatureAlgorithm=None): """ Provide data for serialisation of message @param signature: signature carried in the message @param signatureAlgorithm: signature algorithm used to make the signature (TLSv1.2 only) """ self.signatureAlgorithm = signatureAlgorithm self.signature = signature return self def parse(self, parser): """ Deserialize message from parser @param parser: parser with data to read """ parser.startLengthCheck(3) if self.version >= (3, 3): self.signatureAlgorithm = (parser.get(1), parser.get(1)) self.signature = parser.getVarBytes(2) parser.stopLengthCheck() return self def write(self): """ Serialize the data to bytearray @rtype: bytearray """ writer = Writer() if self.version >= (3, 3): writer.add(self.signatureAlgorithm[0], 1) writer.add(self.signatureAlgorithm[1], 1) writer.addVarSeq(self.signature, 1, 2) return self.postWrite(writer) class ChangeCipherSpec(object): def __init__(self): self.contentType = ContentType.change_cipher_spec self.type = 1 def create(self): self.type = 1 return self def parse(self, p): p.setLengthCheck(1) self.type = p.get(1) p.stopLengthCheck() return self def write(self): w = Writer() w.add(self.type,1) return w.bytes class NextProtocol(HandshakeMsg): def __init__(self): HandshakeMsg.__init__(self, HandshakeType.next_protocol) self.next_proto = None def create(self, next_proto): self.next_proto = next_proto return self def parse(self, p): p.startLengthCheck(3) self.next_proto = p.getVarBytes(1) _ = p.getVarBytes(1) p.stopLengthCheck() return self def write(self, trial=False): w = Writer() w.addVarSeq(self.next_proto, 1, 1) paddingLen = 32 - ((len(self.next_proto) + 2) % 32) w.addVarSeq(bytearray(paddingLen), 1, 1) return self.postWrite(w) class Finished(HandshakeMsg): def __init__(self, version): HandshakeMsg.__init__(self, HandshakeType.finished) self.version = version self.verify_data = bytearray(0) def create(self, verify_data): self.verify_data = verify_data return self def parse(self, p): p.startLengthCheck(3) if self.version == (3,0): self.verify_data = p.getFixBytes(36) elif self.version in ((3,1), (3,2), (3,3)): self.verify_data = p.getFixBytes(12) else: raise AssertionError() p.stopLengthCheck() return self def write(self): w = Writer() w.addFixSeq(self.verify_data, 1) return self.postWrite(w) class ApplicationData(object): def __init__(self): self.contentType = ContentType.application_data self.bytes = bytearray(0) def create(self, bytes): self.bytes = bytes return self def splitFirstByte(self): newMsg = ApplicationData().create(self.bytes[:1]) self.bytes = self.bytes[1:] return newMsg def parse(self, p): self.bytes = p.bytes return self def write(self): return self.bytes tlslite-ng-0.5.1/tlslite/messagesocket.py000066400000000000000000000137301261665411200204760ustar00rootroot00000000000000# vim: set fileencoding=utf8 # # Copyright © 2015, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. """Wrapper of TLS RecordLayer providing message-level abstraction""" from .recordlayer import RecordLayer from .constants import ContentType from .messages import RecordHeader3, Message from .utils.codec import Parser class MessageSocket(RecordLayer): """TLS Record Layer socket that provides Message level abstraction Because the record layer has a hard size limit on sent messages, they need to be fragmented before sending. Similarly, a single record layer record can include multiple handshake protocol messages (very common with ServerHello, Certificate and ServerHelloDone), as such, the user of RecordLayer needs to fragment those records into multiple messages. Unfortunately, fragmentation of messages requires some degree of knowledge about the messages passed and as such is outside scope of pure record layer implementation. This class tries to provide a useful abstraction for handling Handshake protocol messages. @type recordSize: int @ivar recordSize: maximum size of records sent through socket. Messages bigger than this size will be fragmented to smaller chunks. Setting it to higher value than the default 2^14 will make the implementation non RFC compliant and likely not interoperable with other peers. @type defragmenter: L{Defragmenter} @ivar defragmenter: defragmenter used for read records @type unfragmentedDataTypes: tuple @ivar unfragmentedDataTypes: data types which will be passed as-read, TLS application_data by default """ def __init__(self, sock, defragmenter): """Apply TLS Record Layer abstraction to raw network socket. @type sock: L{socket.socket} @param sock: network socket to wrap @type defragmenter: L{Defragmenter} @param defragmenter: defragmenter to apply on the records read """ super(MessageSocket, self).__init__(sock) self.defragmenter = defragmenter self.unfragmentedDataTypes = tuple((ContentType.application_data, )) self._lastRecordVersion = (0, 0) self._sendBuffer = bytearray(0) self._sendBufferType = None self.recordSize = 2**14 def recvMessage(self): """ Read next message in queue will return a 0 or 1 if the read is blocking, a tuple of L{RecordHeader3} and L{Parser} in case a message was received. @rtype: generator """ while True: while True: ret = self.defragmenter.getMessage() if ret is None: break header = RecordHeader3().create(self._lastRecordVersion, ret[0], 0) yield header, Parser(ret[1]) for ret in self.recvRecord(): if ret in (0, 1): yield ret else: break header, parser = ret if header.type in self.unfragmentedDataTypes: yield ret # TODO probably needs a bit better handling... if header.ssl2: yield ret self.defragmenter.addData(header.type, parser.bytes) self._lastRecordVersion = header.version def recvMessageBlocking(self): """Blocking variant of L{recvMessage}""" for res in self.recvMessage(): if res in (0, 1): pass else: return res def flush(self): """ Empty the queue of messages to write Will fragment the messages and write them in as little records as possible. @rtype: generator """ while len(self._sendBuffer) > 0: recordPayload = self._sendBuffer[:self.recordSize] self._sendBuffer = self._sendBuffer[self.recordSize:] msg = Message(self._sendBufferType, recordPayload) for res in self.sendRecord(msg): yield res assert len(self._sendBuffer) == 0 self._sendBufferType = None def flushBlocking(self): """Blocking variant of L{flush}""" for _ in self.flush(): pass def queueMessage(self, msg): """ Queue message for sending If the message is of same type as messages in queue, the message is just added to queue. If the message is of different type as messages in queue, the queue is flushed and then the message is queued. @rtype: generator """ if self._sendBufferType is None: self._sendBufferType = msg.contentType if msg.contentType == self._sendBufferType: self._sendBuffer += msg.write() return for res in self.flush(): yield res assert self._sendBufferType is None self._sendBufferType = msg.contentType self._sendBuffer += msg.write() def queueMessageBlocking(self, msg): """Blocking variant of L{queueMessage}""" for _ in self.queueMessage(msg): pass def sendMessage(self, msg): """ Fragment and send a message. If a messages already of same type reside in queue, the message if first added to it and then the queue is flushed. If the message is of different type than the queue, the queue is flushed, the message is added to queue and the queue is flushed again. Use the sendRecord() message if you want to send a message outside the queue, or a message of zero size. @rtype: generator """ for res in self.queueMessage(msg): yield res for res in self.flush(): yield res def sendMessageBlocking(self, msg): """Blocking variant of L{sendMessage}""" for _ in self.sendMessage(msg): pass tlslite-ng-0.5.1/tlslite/recordlayer.py000066400000000000000000000674101261665411200201600ustar00rootroot00000000000000# Copyright (c) 2014, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. """Implementation of the TLS Record Layer protocol""" import socket import errno import hashlib from .constants import ContentType, CipherSuite from .messages import RecordHeader3, RecordHeader2, Message from .utils.cipherfactory import createAESGCM, createAES, createRC4, \ createTripleDES from .utils.codec import Parser, Writer from .utils.compat import compatHMAC from .utils.cryptomath import getRandomBytes from .utils.constanttime import ct_compare_digest, ct_check_cbc_mac_and_pad from .errors import TLSRecordOverflow, TLSIllegalParameterException,\ TLSAbruptCloseError, TLSDecryptionFailed, TLSBadRecordMAC from .mathtls import createMAC_SSL, createHMAC, PRF_SSL, PRF, PRF_1_2, \ PRF_1_2_SHA384 class RecordSocket(object): """Socket wrapper for reading and writing TLS Records""" def __init__(self, sock): """ Assign socket to wrapper @type sock: socket.socket """ self.sock = sock self.version = (0, 0) def _sockSendAll(self, data): """ Send all data through socket @type data: bytearray @param data: data to send @raise socket.error: when write to socket failed """ while 1: try: bytesSent = self.sock.send(data) except socket.error as why: if why.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN): yield 1 continue raise if bytesSent == len(data): return data = data[bytesSent:] yield 1 def send(self, msg): """ Send the message through socket. @type msg: bytearray @param msg: TLS message to send @raise socket.error: when write to socket failed """ data = msg.write() header = RecordHeader3().create(self.version, msg.contentType, len(data)) data = header.write() + data for result in self._sockSendAll(data): yield result def _sockRecvAll(self, length): """ Read exactly the amount of bytes specified in L{length} from raw socket. @rtype: generator @return: generator that will return 0 or 1 in case the socket is non blocking and would block and bytearray in case the read finished @raise TLSAbruptCloseError: when the socket closed """ buf = bytearray(0) if length == 0: yield buf while True: try: socketBytes = self.sock.recv(length - len(buf)) except socket.error as why: if why.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN): yield 0 continue else: raise #if the connection closed, raise socket error if len(socketBytes) == 0: raise TLSAbruptCloseError() buf += bytearray(socketBytes) if len(buf) == length: yield buf def _recvHeader(self): """Read a single record header from socket""" #Read the next record header buf = bytearray(0) ssl2 = False result = None for result in self._sockRecvAll(1): if result in (0, 1): yield result else: break assert result is not None buf += result if buf[0] in ContentType.all: ssl2 = False # SSLv3 record layer header is 5 bytes long, we already read 1 result = None for result in self._sockRecvAll(4): if result in (0, 1): yield result else: break assert result is not None buf += result # XXX this should be 'buf[0] & 128', otherwise hello messages longer # than 127 bytes won't be properly parsed elif buf[0] == 128: ssl2 = True # in SSLv2 we need to read 2 bytes in total to know the size of # header, we already read 1 result = None for result in self._sockRecvAll(1): if result in (0, 1): yield result else: break assert result is not None buf += result else: raise TLSIllegalParameterException( "Record header type doesn't specify known type") #Parse the record header if ssl2: record = RecordHeader2().parse(Parser(buf)) else: record = RecordHeader3().parse(Parser(buf)) yield record def recv(self): """ Read a single record from socket, handle SSLv2 and SSLv3 record layer @rtype: generator @return: generator that returns 0 or 1 in case the read would be blocking or a tuple containing record header (object) and record data (bytearray) read from socket @raise socket.error: In case of network error @raise TLSAbruptCloseError: When the socket was closed on the other side in middle of record receiving @raise TLSRecordOverflow: When the received record was longer than allowed by TLS @raise TLSIllegalParameterException: When the record header was malformed """ record = None for record in self._recvHeader(): if record in (0, 1): yield record else: break assert record is not None #Check the record header fields # 18432 = 2**14 (basic record size limit) + 1024 (maximum compression # overhead) + 1024 (maximum encryption overhead) if record.length > 18432: raise TLSRecordOverflow() #Read the record contents buf = bytearray(0) result = None for result in self._sockRecvAll(record.length): if result in (0, 1): yield result else: break assert result is not None buf += result yield (record, buf) class ConnectionState(object): """Preserve the connection state for reading and writing data to records""" def __init__(self): """Create an instance with empty encryption and MACing contexts""" self.macContext = None self.encContext = None self.fixedNonce = None self.seqnum = 0 def getSeqNumBytes(self): """Return encoded sequence number and increment it.""" writer = Writer() writer.add(self.seqnum, 8) self.seqnum += 1 return writer.bytes class RecordLayer(object): """ Implementation of TLS record layer protocol @ivar version: the TLS version to use (tuple encoded as on the wire) @ivar sock: underlying socket @ivar client: whether the connection should use encryption @ivar encryptThenMAC: use the encrypt-then-MAC mechanism for record integrity """ def __init__(self, sock): self.sock = sock self._recordSocket = RecordSocket(sock) self._version = (0, 0) self.client = True self._writeState = ConnectionState() self._readState = ConnectionState() self._pendingWriteState = ConnectionState() self._pendingReadState = ConnectionState() self.fixedIVBlock = None self.encryptThenMAC = False @property def version(self): """Return the TLS version used by record layer""" return self._version @version.setter def version(self, val): """Set the TLS version used by record layer""" self._version = val self._recordSocket.version = val def getCipherName(self): """ Return the name of the bulk cipher used by this connection @rtype: str @return: The name of the cipher, like 'aes128', 'rc4', etc. """ if self._writeState.encContext is None: return None return self._writeState.encContext.name def getCipherImplementation(self): """ Return the name of the implementation used for the connection 'python' for tlslite internal implementation, 'openssl' for M2crypto and 'pycrypto' for pycrypto @rtype: str @return: Name of cipher implementation used, None if not initialised """ if self._writeState.encContext is None: return None return self._writeState.encContext.implementation def shutdown(self): """Clear read and write states""" self._writeState = ConnectionState() self._readState = ConnectionState() self._pendingWriteState = ConnectionState() self._pendingReadState = ConnectionState() def isCBCMode(self): """Returns true if cipher uses CBC mode""" if self._writeState and self._writeState.encContext and \ self._writeState.encContext.isBlockCipher: return True else: return False # # sending messages # def _addPadding(self, data): """Add padding to data so that it is multiple of block size""" currentLength = len(data) blockLength = self._writeState.encContext.block_size paddingLength = blockLength - 1 - (currentLength % blockLength) paddingBytes = bytearray([paddingLength] * (paddingLength+1)) data += paddingBytes return data def _calculateMAC(self, mac, seqnumBytes, contentType, data): """Calculate the SSL/TLS version of a MAC""" mac.update(compatHMAC(seqnumBytes)) mac.update(compatHMAC(bytearray([contentType]))) assert self.version in ((3, 0), (3, 1), (3, 2), (3, 3)) if self.version != (3, 0): mac.update(compatHMAC(bytearray([self.version[0]]))) mac.update(compatHMAC(bytearray([self.version[1]]))) mac.update(compatHMAC(bytearray([len(data)//256]))) mac.update(compatHMAC(bytearray([len(data)%256]))) mac.update(compatHMAC(data)) return bytearray(mac.digest()) def _macThenEncrypt(self, data, contentType): """MAC, pad then encrypt data""" if self._writeState.macContext: seqnumBytes = self._writeState.getSeqNumBytes() mac = self._writeState.macContext.copy() macBytes = self._calculateMAC(mac, seqnumBytes, contentType, data) data += macBytes #Encrypt for Block or Stream Cipher if self._writeState.encContext: #Add padding (for Block Cipher): if self._writeState.encContext.isBlockCipher: #Add TLS 1.1 fixed block if self.version >= (3, 2): data = self.fixedIVBlock + data data = self._addPadding(data) #Encrypt data = self._writeState.encContext.encrypt(data) return data def _encryptThenMAC(self, buf, contentType): """Pad, encrypt and then MAC the data""" if self._writeState.encContext: # add IV for TLS1.1+ if self.version >= (3, 2): buf = self.fixedIVBlock + buf buf = self._addPadding(buf) buf = self._writeState.encContext.encrypt(buf) # add MAC if self._writeState.macContext: seqnumBytes = self._writeState.getSeqNumBytes() mac = self._writeState.macContext.copy() # append MAC macBytes = self._calculateMAC(mac, seqnumBytes, contentType, buf) buf += macBytes return buf def _encryptThenSeal(self, buf, contentType): """Encrypt with AEAD cipher""" #Assemble the authenticated data. seqNumBytes = self._writeState.getSeqNumBytes() authData = seqNumBytes + bytearray([contentType, self.version[0], self.version[1], len(buf)//256, len(buf)%256]) #The nonce is always the fixed nonce and the sequence number. nonce = self._writeState.fixedNonce + seqNumBytes assert len(nonce) == self._writeState.encContext.nonceLength buf = self._writeState.encContext.seal(nonce, buf, authData) #The only AEAD supported, AES-GCM, has an explicit variable #nonce. buf = seqNumBytes + buf return buf def sendRecord(self, msg): """ Encrypt, MAC and send arbitrary message as-is through socket. Note that if the message was not fragmented to below 2**14 bytes it will be rejected by the other connection side. @param msg: TLS message to send @type msg: ApplicationData, HandshakeMessage, etc. """ data = msg.write() contentType = msg.contentType if self._writeState and \ self._writeState.encContext and \ self._writeState.encContext.isAEAD: data = self._encryptThenSeal(data, contentType) elif self.encryptThenMAC: data = self._encryptThenMAC(data, contentType) else: data = self._macThenEncrypt(data, contentType) encryptedMessage = Message(contentType, data) for result in self._recordSocket.send(encryptedMessage): yield result # # receiving messages # def _decryptStreamThenMAC(self, recordType, data): """Decrypt a stream cipher and check MAC""" if self._readState.encContext: assert self.version in ((3, 0), (3, 1), (3, 2), (3, 3)) data = self._readState.encContext.decrypt(data) if self._readState.macContext: #Check MAC macGood = True macLength = self._readState.macContext.digest_size endLength = macLength if endLength > len(data): macGood = False else: #Read MAC startIndex = len(data) - endLength endIndex = startIndex + macLength checkBytes = data[startIndex : endIndex] #Calculate MAC seqnumBytes = self._readState.getSeqNumBytes() data = data[:-endLength] mac = self._readState.macContext.copy() macBytes = self._calculateMAC(mac, seqnumBytes, recordType, data) #Compare MACs if not ct_compare_digest(macBytes, checkBytes): macGood = False if not macGood: raise TLSBadRecordMAC() return data def _decryptThenMAC(self, recordType, data): """Decrypt data, check padding and MAC""" if self._readState.encContext: assert self.version in ((3, 0), (3, 1), (3, 2), (3, 3)) assert self._readState.encContext.isBlockCipher assert self._readState.macContext # # decrypt the record # blockLength = self._readState.encContext.block_size if len(data) % blockLength != 0: raise TLSDecryptionFailed() data = self._readState.encContext.decrypt(data) if self.version >= (3, 2): #For TLS 1.1, remove explicit IV data = data[self._readState.encContext.block_size : ] # # check padding and MAC # seqnumBytes = self._readState.getSeqNumBytes() if not ct_check_cbc_mac_and_pad(data, self._readState.macContext, seqnumBytes, recordType, self.version): raise TLSBadRecordMAC() # # strip padding and MAC # endLength = data[-1] + 1 + self._readState.macContext.digest_size data = data[:-endLength] return data def _macThenDecrypt(self, recordType, buf): """ Check MAC of data, then decrypt and remove padding @raise TLSBadRecordMAC: when the mac value is invalid @raise TLSDecryptionFailed: when the data to decrypt has invalid size """ if self._readState.macContext: macLength = self._readState.macContext.digest_size if len(buf) < macLength: raise TLSBadRecordMAC("Truncated data") checkBytes = buf[-macLength:] buf = buf[:-macLength] seqnumBytes = self._readState.getSeqNumBytes() mac = self._readState.macContext.copy() macBytes = self._calculateMAC(mac, seqnumBytes, recordType, buf) if not ct_compare_digest(macBytes, checkBytes): raise TLSBadRecordMAC("MAC mismatch") if self._readState.encContext: blockLength = self._readState.encContext.block_size if len(buf) % blockLength != 0: raise TLSDecryptionFailed("data length not multiple of "\ "block size") buf = self._readState.encContext.decrypt(buf) # remove explicit IV if self.version >= (3, 2): buf = buf[blockLength:] if len(buf) == 0: raise TLSBadRecordMAC("No data left after IV removal") # check padding paddingLength = buf[-1] if paddingLength + 1 > len(buf): raise TLSBadRecordMAC("Invalid padding length") paddingGood = True totalPaddingLength = paddingLength+1 if self.version != (3, 0): paddingBytes = buf[-totalPaddingLength:-1] for byte in paddingBytes: if byte != paddingLength: paddingGood = False if not paddingGood: raise TLSBadRecordMAC("Invalid padding byte values") # remove padding buf = buf[:-totalPaddingLength] return buf def _decryptAndUnseal(self, recordType, buf): """Decrypt AEAD encrypted data""" #The only AEAD supported, AES-GCM, has an explicit variable #nonce. explicitNonceLength = 8 if explicitNonceLength > len(buf): #Publicly invalid. raise TLSBadRecordMAC("Truncated nonce") nonce = self._readState.fixedNonce + buf[:explicitNonceLength] buf = buf[8:] if self._readState.encContext.tagLength > len(buf): #Publicly invalid. raise TLSBadRecordMAC("Truncated tag") #Assemble the authenticated data. seqnumBytes = self._readState.getSeqNumBytes() plaintextLen = len(buf) - self._readState.encContext.tagLength authData = seqnumBytes + bytearray([recordType, self.version[0], self.version[1], plaintextLen//256, plaintextLen%256]) buf = self._readState.encContext.open(nonce, buf, authData) if buf is None: raise TLSBadRecordMAC("Invalid tag, decryption failure") return buf def recvRecord(self): """ Read, decrypt and check integrity of a single record @rtype: tuple @return: message header and decrypted message payload @raise TLSDecryptionFailed: when decryption of data failed @raise TLSBadRecordMAC: when record has bad MAC or padding @raise socket.error: when reading from socket was unsuccessful """ result = None for result in self._recordSocket.recv(): if result in (0, 1): yield result else: break assert result is not None (header, data) = result if self._readState and \ self._readState.encContext and \ self._readState.encContext.isAEAD: data = self._decryptAndUnseal(header.type, data) elif self.encryptThenMAC: data = self._macThenDecrypt(header.type, data) elif self._readState and \ self._readState.encContext and \ self._readState.encContext.isBlockCipher: data = self._decryptThenMAC(header.type, data) else: data = self._decryptStreamThenMAC(header.type, data) yield (header, Parser(data)) # # cryptography state methods # def changeWriteState(self): """ Change the cipher state to the pending one for write operations. This should be done only once after a call to L{calcPendingStates} was performed and directly after sending a L{ChangeCipherSpec} message. """ self._writeState = self._pendingWriteState self._pendingWriteState = ConnectionState() def changeReadState(self): """ Change the cipher state to the pending one for read operations. This should be done only once after a call to L{calcPendingStates} was performed and directly after receiving a L{ChangeCipherSpec} message. """ self._readState = self._pendingReadState self._pendingReadState = ConnectionState() @staticmethod def _getCipherSettings(cipherSuite): """Get the settings for cipher suite used""" if cipherSuite in CipherSuite.aes256GcmSuites: keyLength = 32 ivLength = 4 createCipherFunc = createAESGCM elif cipherSuite in CipherSuite.aes128GcmSuites: keyLength = 16 ivLength = 4 createCipherFunc = createAESGCM elif cipherSuite in CipherSuite.aes128Suites: keyLength = 16 ivLength = 16 createCipherFunc = createAES elif cipherSuite in CipherSuite.aes256Suites: keyLength = 32 ivLength = 16 createCipherFunc = createAES elif cipherSuite in CipherSuite.rc4Suites: keyLength = 16 ivLength = 0 createCipherFunc = createRC4 elif cipherSuite in CipherSuite.tripleDESSuites: keyLength = 24 ivLength = 8 createCipherFunc = createTripleDES elif cipherSuite in CipherSuite.nullSuites: keyLength = 0 ivLength = 0 createCipherFunc = None else: raise AssertionError() return (keyLength, ivLength, createCipherFunc) @staticmethod def _getMacSettings(cipherSuite): """Get settings for HMAC used""" if cipherSuite in CipherSuite.aeadSuites: macLength = 0 digestmod = None elif cipherSuite in CipherSuite.shaSuites: macLength = 20 digestmod = hashlib.sha1 elif cipherSuite in CipherSuite.sha256Suites: macLength = 32 digestmod = hashlib.sha256 elif cipherSuite in CipherSuite.md5Suites: macLength = 16 digestmod = hashlib.md5 else: raise AssertionError() return macLength, digestmod @staticmethod def _getHMACMethod(version): """Get the HMAC method""" assert version in ((3, 0), (3, 1), (3, 2), (3, 3)) if version == (3, 0): createMACFunc = createMAC_SSL elif version in ((3, 1), (3, 2), (3, 3)): createMACFunc = createHMAC return createMACFunc def _calcKeyBlock(self, cipherSuite, masterSecret, clientRandom, serverRandom, outputLength): """Calculate the overall key to slice up""" if self.version == (3, 0): keyBlock = PRF_SSL(masterSecret, serverRandom + clientRandom, outputLength) elif self.version in ((3, 1), (3, 2)): keyBlock = PRF(masterSecret, b"key expansion", serverRandom + clientRandom, outputLength) elif self.version == (3, 3): if cipherSuite in CipherSuite.sha384PrfSuites: keyBlock = PRF_1_2_SHA384(masterSecret, b"key expansion", serverRandom + clientRandom, outputLength) else: keyBlock = PRF_1_2(masterSecret, b"key expansion", serverRandom + clientRandom, outputLength) else: raise AssertionError() return keyBlock def calcPendingStates(self, cipherSuite, masterSecret, clientRandom, serverRandom, implementations): """Create pending states for encryption and decryption.""" keyLength, ivLength, createCipherFunc = \ self._getCipherSettings(cipherSuite) macLength, digestmod = self._getMacSettings(cipherSuite) if not digestmod: createMACFunc = None else: createMACFunc = self._getHMACMethod(self.version) outputLength = (macLength*2) + (keyLength*2) + (ivLength*2) #Calculate Keying Material from Master Secret keyBlock = self._calcKeyBlock(cipherSuite, masterSecret, clientRandom, serverRandom, outputLength) #Slice up Keying Material clientPendingState = ConnectionState() serverPendingState = ConnectionState() parser = Parser(keyBlock) clientMACBlock = parser.getFixBytes(macLength) serverMACBlock = parser.getFixBytes(macLength) clientKeyBlock = parser.getFixBytes(keyLength) serverKeyBlock = parser.getFixBytes(keyLength) clientIVBlock = parser.getFixBytes(ivLength) serverIVBlock = parser.getFixBytes(ivLength) if digestmod: # Legacy cipher clientPendingState.macContext = createMACFunc( compatHMAC(clientMACBlock), digestmod=digestmod) serverPendingState.macContext = createMACFunc( compatHMAC(serverMACBlock), digestmod=digestmod) if createCipherFunc is not None: clientPendingState.encContext = \ createCipherFunc(clientKeyBlock, clientIVBlock, implementations) serverPendingState.encContext = \ createCipherFunc(serverKeyBlock, serverIVBlock, implementations) else: # AEAD clientPendingState.macContext = None serverPendingState.macContext = None clientPendingState.encContext = createCipherFunc(clientKeyBlock, implementations) serverPendingState.encContext = createCipherFunc(serverKeyBlock, implementations) clientPendingState.fixedNonce = clientIVBlock serverPendingState.fixedNonce = serverIVBlock #Assign new connection states to pending states if self.client: self._pendingWriteState = clientPendingState self._pendingReadState = serverPendingState else: self._pendingWriteState = serverPendingState self._pendingReadState = clientPendingState if self.version >= (3, 2) and ivLength: #Choose fixedIVBlock for TLS 1.1 (this is encrypted with the CBC #residue to create the IV for each sent block) self.fixedIVBlock = getRandomBytes(ivLength) tlslite-ng-0.5.1/tlslite/session.py000066400000000000000000000111311261665411200173150ustar00rootroot00000000000000# Authors: # Trevor Perrin # Dave Baggett (Arcode Corporation) - canonicalCipherName # # See the LICENSE file for legal information regarding use of this file. """Class representing a TLS session.""" from .utils.compat import * from .mathtls import * from .constants import * class Session(object): """ This class represents a TLS session. TLS distinguishes between connections and sessions. A new handshake creates both a connection and a session. Data is transmitted over the connection. The session contains a more permanent record of the handshake. The session can be inspected to determine handshake results. The session can also be used to create a new connection through "session resumption". If the client and server both support this, they can create a new connection based on an old session without the overhead of a full handshake. The session for a L{tlslite.TLSConnection.TLSConnection} can be retrieved from the connection's 'session' attribute. @type srpUsername: str @ivar srpUsername: The client's SRP username (or None). @type clientCertChain: L{tlslite.x509certchain.X509CertChain} @ivar clientCertChain: The client's certificate chain (or None). @type serverCertChain: L{tlslite.x509certchain.X509CertChain} @ivar serverCertChain: The server's certificate chain (or None). @type tackExt: L{tack.structures.TackExtension.TackExtension} @ivar tackExt: The server's TackExtension (or None). @type tackInHelloExt: L{bool} @ivar tackInHelloExt:True if a TACK was presented via TLS Extension. @type encryptThenMAC: bool @ivar encryptThenMAC: True if connection uses CBC cipher in encrypt-then-MAC mode """ def __init__(self): self.masterSecret = bytearray(0) self.sessionID = bytearray(0) self.cipherSuite = 0 self.srpUsername = "" self.clientCertChain = None self.serverCertChain = None self.tackExt = None self.tackInHelloExt = False self.serverName = "" self.resumable = False self.encryptThenMAC = False def create(self, masterSecret, sessionID, cipherSuite, srpUsername, clientCertChain, serverCertChain, tackExt, tackInHelloExt, serverName, resumable=True, encryptThenMAC=False): self.masterSecret = masterSecret self.sessionID = sessionID self.cipherSuite = cipherSuite self.srpUsername = srpUsername self.clientCertChain = clientCertChain self.serverCertChain = serverCertChain self.tackExt = tackExt self.tackInHelloExt = tackInHelloExt self.serverName = serverName self.resumable = resumable self.encryptThenMAC = encryptThenMAC def _clone(self): other = Session() other.masterSecret = self.masterSecret other.sessionID = self.sessionID other.cipherSuite = self.cipherSuite other.srpUsername = self.srpUsername other.clientCertChain = self.clientCertChain other.serverCertChain = self.serverCertChain other.tackExt = self.tackExt other.tackInHelloExt = self.tackInHelloExt other.serverName = self.serverName other.resumable = self.resumable other.encryptThenMAC = self.encryptThenMAC return other def valid(self): """If this session can be used for session resumption. @rtype: bool @return: If this session can be used for session resumption. """ return self.resumable and self.sessionID def _setResumable(self, boolean): #Only let it be set to True if the sessionID is non-null if (not boolean) or (boolean and self.sessionID): self.resumable = boolean def getTackId(self): if self.tackExt and self.tackExt.tack: return self.tackExt.tack.getTackId() else: return None def getBreakSigs(self): if self.tackExt and self.tackExt.break_sigs: return self.tackExt.break_sigs else: return None def getCipherName(self): """Get the name of the cipher used with this connection. @rtype: str @return: The name of the cipher used with this connection. """ return CipherSuite.canonicalCipherName(self.cipherSuite) def getMacName(self): """Get the name of the HMAC hash algo used with this connection. @rtype: str @return: The name of the HMAC hash algo used with this connection. """ return CipherSuite.canonicalMacName(self.cipherSuite) tlslite-ng-0.5.1/tlslite/sessioncache.py000066400000000000000000000067241261665411200203150ustar00rootroot00000000000000# Authors: # Trevor Perrin # Martin von Loewis - python 3 port # Mirko Dziadzka - bugfix # # See the LICENSE file for legal information regarding use of this file. """Class for caching TLS sessions.""" import threading import time class SessionCache(object): """This class is used by the server to cache TLS sessions. Caching sessions allows the client to use TLS session resumption and avoid the expense of a full handshake. To use this class, simply pass a SessionCache instance into the server handshake function. This class is thread-safe. """ #References to these instances #are also held by the caller, who may change the 'resumable' #flag, so the SessionCache must return the same instances #it was passed in. def __init__(self, maxEntries=10000, maxAge=14400): """Create a new SessionCache. @type maxEntries: int @param maxEntries: The maximum size of the cache. When this limit is reached, the oldest sessions will be deleted as necessary to make room for new ones. The default is 10000. @type maxAge: int @param maxAge: The number of seconds before a session expires from the cache. The default is 14400 (i.e. 4 hours).""" self.lock = threading.Lock() # Maps sessionIDs to sessions self.entriesDict = {} #Circular list of (sessionID, timestamp) pairs self.entriesList = [(None,None)] * maxEntries self.firstIndex = 0 self.lastIndex = 0 self.maxAge = maxAge def __getitem__(self, sessionID): self.lock.acquire() try: self._purge() #Delete old items, so we're assured of a new one session = self.entriesDict[bytes(sessionID)] #When we add sessions they're resumable, but it's possible #for the session to be invalidated later on (if a fatal alert #is returned), so we have to check for resumability before #returning the session. if session.valid(): return session else: raise KeyError() finally: self.lock.release() def __setitem__(self, sessionID, session): self.lock.acquire() try: #Add the new element self.entriesDict[bytes(sessionID)] = session self.entriesList[self.lastIndex] = (bytes(sessionID), time.time()) self.lastIndex = (self.lastIndex+1) % len(self.entriesList) #If the cache is full, we delete the oldest element to make an #empty space if self.lastIndex == self.firstIndex: del(self.entriesDict[self.entriesList[self.firstIndex][0]]) self.firstIndex = (self.firstIndex+1) % len(self.entriesList) finally: self.lock.release() #Delete expired items def _purge(self): currentTime = time.time() #Search through the circular list, deleting expired elements until #we reach a non-expired element. Since elements in list are #ordered in time, we can break once we reach the first non-expired #element index = self.firstIndex while index != self.lastIndex: if currentTime - self.entriesList[index][1] > self.maxAge: del(self.entriesDict[self.entriesList[index][0]]) index = (index+1) % len(self.entriesList) else: break self.firstIndex = index tlslite-ng-0.5.1/tlslite/tlsconnection.py000066400000000000000000002631541261665411200205320ustar00rootroot00000000000000# Authors: # Trevor Perrin # Google - added reqCAs parameter # Google (adapted by Sam Rushing and Marcelo Fernandez) - NPN support # Google - FALLBACK_SCSV # Dimitris Moraitis - Anon ciphersuites # Martin von Loewis - python 3 port # Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2 # # See the LICENSE file for legal information regarding use of this file. """ MAIN CLASS FOR TLS LITE (START HERE!). """ from __future__ import division import socket from .utils.compat import formatExceptionTrace from .tlsrecordlayer import TLSRecordLayer from .session import Session from .constants import * from .utils.cryptomath import getRandomBytes from .errors import * from .messages import * from .mathtls import * from .handshakesettings import HandshakeSettings from .utils.tackwrapper import * from .utils.rsakey import RSAKey class KeyExchange(object): """ Common API for calculating Premaster secret NOT stable, will get moved from this file """ def __init__(self, cipherSuite, clientHello, serverHello, privateKey): """ Initializes the KeyExchange. privateKey is the signing private key. """ self.cipherSuite = cipherSuite self.clientHello = clientHello self.serverHello = serverHello self.privateKey = privateKey def makeServerKeyExchange(): """ Returns a ServerKeyExchange object for the server's initial leg in the handshake. If the key exchange method does not send ServerKeyExchange (e.g. RSA), it returns None. """ raise NotImplementedError() def processClientKeyExchange(clientKeyExchange): """ Processes the client's ClientKeyExchange message and returns the premaster secret. Raises TLSLocalAlert on error. """ raise NotImplementedError() class RSAKeyExchange(KeyExchange): """ Handling of RSA key exchange NOT stable API, do NOT use """ def makeServerKeyExchange(self): return None def processClientKeyExchange(self, clientKeyExchange): premasterSecret = self.privateKey.decrypt(\ clientKeyExchange.encryptedPreMasterSecret) # On decryption failure randomize premaster secret to avoid # Bleichenbacher's "million message" attack randomPreMasterSecret = getRandomBytes(48) if not premasterSecret: premasterSecret = randomPreMasterSecret elif len(premasterSecret) != 48: premasterSecret = randomPreMasterSecret else: versionCheck = (premasterSecret[0], premasterSecret[1]) if versionCheck != self.clientHello.client_version: #Tolerate buggy IE clients if versionCheck != self.serverHello.server_version: premasterSecret = randomPreMasterSecret return premasterSecret class DHE_RSAKeyExchange(KeyExchange): """ Handling of ephemeral Diffe-Hellman Key exchange NOT stable API, do NOT use """ def __init__(self, cipherSuite, clientHello, serverHello, privateKey): super(DHE_RSAKeyExchange, self).__init__(cipherSuite, clientHello, serverHello, privateKey) self.dh_Xs = None # 2048-bit MODP Group (RFC 3526, Section 3) dh_g, dh_p = goodGroupParameters[2] # RFC 3526, Section 8. strength = 160 def makeServerKeyExchange(self): # Per RFC 3526, Section 1, the exponent should have double the entropy # of the strength of the curve. self.dh_Xs = bytesToNumber(getRandomBytes(self.strength * 2 // 8)) dh_Ys = powMod(self.dh_g, self.dh_Xs, self.dh_p) version = self.serverHello.server_version serverKeyExchange = ServerKeyExchange(self.cipherSuite, version) serverKeyExchange.createDH(self.dh_p, self.dh_g, dh_Ys) hashBytes = serverKeyExchange.hash(self.clientHello.random, self.serverHello.random) if version >= (3, 3): # TODO: Signature algorithm negotiation not supported. hashBytes = RSAKey.addPKCS1SHA1Prefix(hashBytes) serverKeyExchange.signAlg = SignatureAlgorithm.rsa serverKeyExchange.hashAlg = HashAlgorithm.sha1 serverKeyExchange.signature = self.privateKey.sign(hashBytes) return serverKeyExchange def processClientKeyExchange(self, clientKeyExchange): dh_Yc = clientKeyExchange.dh_Yc # First half of RFC 2631, Section 2.1.5. Validate the client's public # key. if not 2 <= dh_Yc <= self.dh_p - 1: raise TLSLocalAlert(AlertDescription.illegal_parameter, "Invalid dh_Yc value") S = powMod(dh_Yc, self.dh_Xs, self.dh_p) return numberToByteArray(S) class TLSConnection(TLSRecordLayer): """ This class wraps a socket and provides TLS handshaking and data transfer. To use this class, create a new instance, passing a connected socket into the constructor. Then call some handshake function. If the handshake completes without raising an exception, then a TLS connection has been negotiated. You can transfer data over this connection as if it were a socket. This class provides both synchronous and asynchronous versions of its key functions. The synchronous versions should be used when writing single-or multi-threaded code using blocking sockets. The asynchronous versions should be used when performing asynchronous, event-based I/O with non-blocking sockets. Asynchronous I/O is a complicated subject; typically, you should not use the asynchronous functions directly, but should use some framework like asyncore or Twisted which TLS Lite integrates with (see L{tlslite.integration.tlsasyncdispatchermixin.TLSAsyncDispatcherMixIn}). """ def __init__(self, sock): """Create a new TLSConnection instance. @param sock: The socket data will be transmitted on. The socket should already be connected. It may be in blocking or non-blocking mode. @type sock: L{socket.socket} """ TLSRecordLayer.__init__(self, sock) #********************************************************* # Client Handshake Functions #********************************************************* def handshakeClientAnonymous(self, session=None, settings=None, checker=None, serverName=None, async=False): """Perform an anonymous handshake in the role of client. This function performs an SSL or TLS handshake using an anonymous Diffie Hellman ciphersuite. Like any handshake function, this can be called on a closed TLS connection, or on a TLS connection that is already open. If called on an open connection it performs a re-handshake. If the function completes without raising an exception, the TLS connection will be open and available for data transfer. If an exception is raised, the connection will have been automatically closed (if it was ever open). @type session: L{tlslite.Session.Session} @param session: A TLS session to attempt to resume. If the resumption does not succeed, a full handshake will be performed. @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} @param settings: Various settings which can be used to control the ciphersuites, certificate types, and SSL/TLS versions offered by the client. @type checker: L{tlslite.Checker.Checker} @param checker: A Checker instance. This instance will be invoked to examine the other party's authentication credentials, if the handshake completes succesfully. @type serverName: string @param serverName: The ServerNameIndication TLS Extension. @type async: bool @param async: If False, this function will block until the handshake is completed. If True, this function will return a generator. Successive invocations of the generator will return 0 if it is waiting to read from the socket, 1 if it is waiting to write to the socket, or will raise StopIteration if the handshake operation is completed. @rtype: None or an iterable @return: If 'async' is True, a generator object will be returned. @raise socket.error: If a socket error occurs. @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed without a preceding alert. @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. @raise tlslite.errors.TLSAuthenticationError: If the checker doesn't like the other party's authentication credentials. """ handshaker = self._handshakeClientAsync(anonParams=(True), session=session, settings=settings, checker=checker, serverName=serverName) if async: return handshaker for result in handshaker: pass def handshakeClientSRP(self, username, password, session=None, settings=None, checker=None, reqTack=True, serverName=None, async=False): """Perform an SRP handshake in the role of client. This function performs a TLS/SRP handshake. SRP mutually authenticates both parties to each other using only a username and password. This function may also perform a combined SRP and server-certificate handshake, if the server chooses to authenticate itself with a certificate chain in addition to doing SRP. If the function completes without raising an exception, the TLS connection will be open and available for data transfer. If an exception is raised, the connection will have been automatically closed (if it was ever open). @type username: str @param username: The SRP username. @type password: str @param password: The SRP password. @type session: L{tlslite.session.Session} @param session: A TLS session to attempt to resume. This session must be an SRP session performed with the same username and password as were passed in. If the resumption does not succeed, a full SRP handshake will be performed. @type settings: L{tlslite.handshakesettings.HandshakeSettings} @param settings: Various settings which can be used to control the ciphersuites, certificate types, and SSL/TLS versions offered by the client. @type checker: L{tlslite.checker.Checker} @param checker: A Checker instance. This instance will be invoked to examine the other party's authentication credentials, if the handshake completes succesfully. @type reqTack: bool @param reqTack: Whether or not to send a "tack" TLS Extension, requesting the server return a TackExtension if it has one. @type serverName: string @param serverName: The ServerNameIndication TLS Extension. @type async: bool @param async: If False, this function will block until the handshake is completed. If True, this function will return a generator. Successive invocations of the generator will return 0 if it is waiting to read from the socket, 1 if it is waiting to write to the socket, or will raise StopIteration if the handshake operation is completed. @rtype: None or an iterable @return: If 'async' is True, a generator object will be returned. @raise socket.error: If a socket error occurs. @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed without a preceding alert. @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. @raise tlslite.errors.TLSAuthenticationError: If the checker doesn't like the other party's authentication credentials. """ handshaker = self._handshakeClientAsync(srpParams=(username, password), session=session, settings=settings, checker=checker, reqTack=reqTack, serverName=serverName) # The handshaker is a Python Generator which executes the handshake. # It allows the handshake to be run in a "piecewise", asynchronous # fashion, returning 1 when it is waiting to able to write, 0 when # it is waiting to read. # # If 'async' is True, the generator is returned to the caller, # otherwise it is executed to completion here. if async: return handshaker for result in handshaker: pass def handshakeClientCert(self, certChain=None, privateKey=None, session=None, settings=None, checker=None, nextProtos=None, reqTack=True, serverName=None, async=False): """Perform a certificate-based handshake in the role of client. This function performs an SSL or TLS handshake. The server will authenticate itself using an X.509 certificate chain. If the handshake succeeds, the server's certificate chain will be stored in the session's serverCertChain attribute. Unless a checker object is passed in, this function does no validation or checking of the server's certificate chain. If the server requests client authentication, the client will send the passed-in certificate chain, and use the passed-in private key to authenticate itself. If no certificate chain and private key were passed in, the client will attempt to proceed without client authentication. The server may or may not allow this. If the function completes without raising an exception, the TLS connection will be open and available for data transfer. If an exception is raised, the connection will have been automatically closed (if it was ever open). @type certChain: L{tlslite.x509certchain.X509CertChain} @param certChain: The certificate chain to be used if the server requests client authentication. @type privateKey: L{tlslite.utils.rsakey.RSAKey} @param privateKey: The private key to be used if the server requests client authentication. @type session: L{tlslite.session.Session} @param session: A TLS session to attempt to resume. If the resumption does not succeed, a full handshake will be performed. @type settings: L{tlslite.handshakesettings.HandshakeSettings} @param settings: Various settings which can be used to control the ciphersuites, certificate types, and SSL/TLS versions offered by the client. @type checker: L{tlslite.checker.Checker} @param checker: A Checker instance. This instance will be invoked to examine the other party's authentication credentials, if the handshake completes succesfully. @type nextProtos: list of strings. @param nextProtos: A list of upper layer protocols ordered by preference, to use in the Next-Protocol Negotiation Extension. @type reqTack: bool @param reqTack: Whether or not to send a "tack" TLS Extension, requesting the server return a TackExtension if it has one. @type serverName: string @param serverName: The ServerNameIndication TLS Extension. @type async: bool @param async: If False, this function will block until the handshake is completed. If True, this function will return a generator. Successive invocations of the generator will return 0 if it is waiting to read from the socket, 1 if it is waiting to write to the socket, or will raise StopIteration if the handshake operation is completed. @rtype: None or an iterable @return: If 'async' is True, a generator object will be returned. @raise socket.error: If a socket error occurs. @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed without a preceding alert. @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. @raise tlslite.errors.TLSAuthenticationError: If the checker doesn't like the other party's authentication credentials. """ handshaker = \ self._handshakeClientAsync(certParams=(certChain, privateKey), session=session, settings=settings, checker=checker, serverName=serverName, nextProtos=nextProtos, reqTack=reqTack) # The handshaker is a Python Generator which executes the handshake. # It allows the handshake to be run in a "piecewise", asynchronous # fashion, returning 1 when it is waiting to able to write, 0 when # it is waiting to read. # # If 'async' is True, the generator is returned to the caller, # otherwise it is executed to completion here. if async: return handshaker for result in handshaker: pass def _handshakeClientAsync(self, srpParams=(), certParams=(), anonParams=(), session=None, settings=None, checker=None, nextProtos=None, serverName=None, reqTack=True): handshaker = self._handshakeClientAsyncHelper(srpParams=srpParams, certParams=certParams, anonParams=anonParams, session=session, settings=settings, serverName=serverName, nextProtos=nextProtos, reqTack=reqTack) for result in self._handshakeWrapperAsync(handshaker, checker): yield result def _handshakeClientAsyncHelper(self, srpParams, certParams, anonParams, session, settings, serverName, nextProtos, reqTack): self._handshakeStart(client=True) #Unpack parameters srpUsername = None # srpParams[0] password = None # srpParams[1] clientCertChain = None # certParams[0] privateKey = None # certParams[1] # Allow only one of (srpParams, certParams, anonParams) if srpParams: assert(not certParams) assert(not anonParams) srpUsername, password = srpParams if certParams: assert(not srpParams) assert(not anonParams) clientCertChain, privateKey = certParams if anonParams: assert(not srpParams) assert(not certParams) #Validate parameters if srpUsername and not password: raise ValueError("Caller passed a username but no password") if password and not srpUsername: raise ValueError("Caller passed a password but no username") if clientCertChain and not privateKey: raise ValueError("Caller passed a certChain but no privateKey") if privateKey and not clientCertChain: raise ValueError("Caller passed a privateKey but no certChain") if reqTack: if not tackpyLoaded: reqTack = False if not settings or not settings.useExperimentalTackExtension: reqTack = False if nextProtos is not None: if len(nextProtos) == 0: raise ValueError("Caller passed no nextProtos") # Validates the settings and filters out any unsupported ciphers # or crypto libraries that were requested if not settings: settings = HandshakeSettings() settings = settings.validate() if clientCertChain: if not isinstance(clientCertChain, X509CertChain): raise ValueError("Unrecognized certificate type") if "x509" not in settings.certificateTypes: raise ValueError("Client certificate doesn't match "\ "Handshake Settings") if session: # session.valid() ensures session is resumable and has # non-empty sessionID if not session.valid(): session = None #ignore non-resumable sessions... elif session.resumable: if session.srpUsername != srpUsername: raise ValueError("Session username doesn't match") if session.serverName != serverName: raise ValueError("Session servername doesn't match") #Add Faults to parameters if srpUsername and self.fault == Fault.badUsername: srpUsername += "GARBAGE" if password and self.fault == Fault.badPassword: password += "GARBAGE" #Tentatively set the version to the client's minimum version. #We'll use this for the ClientHello, and if an error occurs #parsing the Server Hello, we'll use this version for the response self.version = settings.maxVersion # OK Start sending messages! # ***************************** # Send the ClientHello. for result in self._clientSendClientHello(settings, session, srpUsername, srpParams, certParams, anonParams, serverName, nextProtos, reqTack): if result in (0,1): yield result else: break clientHello = result #Get the ServerHello. for result in self._clientGetServerHello(settings, clientHello): if result in (0,1): yield result else: break serverHello = result cipherSuite = serverHello.cipher_suite # Choose a matching Next Protocol from server list against ours # (string or None) nextProto = self._clientSelectNextProto(nextProtos, serverHello) # Check if server selected encrypt-then-MAC if serverHello.getExtension(ExtensionType.encrypt_then_mac): self._recordLayer.encryptThenMAC = True #If the server elected to resume the session, it is handled here. for result in self._clientResume(session, serverHello, clientHello.random, settings.cipherImplementations, nextProto): if result in (0,1): yield result else: break if result == "resumed_and_finished": self._handshakeDone(resumed=True) return #If the server selected an SRP ciphersuite, the client finishes #reading the post-ServerHello messages, then derives a #premasterSecret and sends a corresponding ClientKeyExchange. if cipherSuite in CipherSuite.srpAllSuites: for result in self._clientSRPKeyExchange(\ settings, cipherSuite, serverHello.certificate_type, srpUsername, password, clientHello.random, serverHello.random, serverHello.tackExt): if result in (0,1): yield result else: break (premasterSecret, serverCertChain, tackExt) = result #If the server selected an anonymous ciphersuite, the client #finishes reading the post-ServerHello messages. elif cipherSuite in CipherSuite.dhAllSuites: for result in self._clientDHEKeyExchange(settings, cipherSuite, clientCertChain, privateKey, serverHello.certificate_type, serverHello.tackExt, clientHello.random, serverHello.random): if result in (0,1): yield result else: break (premasterSecret, serverCertChain, clientCertChain, tackExt) = result #If the server selected a certificate-based RSA ciphersuite, #the client finishes reading the post-ServerHello messages. If #a CertificateRequest message was sent, the client responds with #a Certificate message containing its certificate chain (if any), #and also produces a CertificateVerify message that signs the #ClientKeyExchange. else: for result in self._clientRSAKeyExchange(settings, cipherSuite, clientCertChain, privateKey, serverHello.certificate_type, clientHello.random, serverHello.random, serverHello.tackExt): if result in (0,1): yield result else: break (premasterSecret, serverCertChain, clientCertChain, tackExt) = result #After having previously sent a ClientKeyExchange, the client now #initiates an exchange of Finished messages. for result in self._clientFinished(premasterSecret, clientHello.random, serverHello.random, cipherSuite, settings.cipherImplementations, nextProto): if result in (0,1): yield result else: break masterSecret = result # Create the session object which is used for resumptions self.session = Session() self.session.create(masterSecret, serverHello.session_id, cipherSuite, srpUsername, clientCertChain, serverCertChain, tackExt, (serverHello.tackExt is not None), serverName, encryptThenMAC=self._recordLayer.encryptThenMAC) self._handshakeDone(resumed=False) def _clientSendClientHello(self, settings, session, srpUsername, srpParams, certParams, anonParams, serverName, nextProtos, reqTack): #Initialize acceptable ciphersuites cipherSuites = [CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] if srpParams: cipherSuites += CipherSuite.getSrpAllSuites(settings) elif certParams: cipherSuites += CipherSuite.getDheCertSuites(settings) cipherSuites += CipherSuite.getCertSuites(settings) elif anonParams: cipherSuites += CipherSuite.getAnonSuites(settings) else: assert(False) #Add any SCSVs. These are not real cipher suites, but signaling #values which reuse the cipher suite field in the ClientHello. wireCipherSuites = list(cipherSuites) if settings.sendFallbackSCSV: wireCipherSuites.append(CipherSuite.TLS_FALLBACK_SCSV) #Initialize acceptable certificate types certificateTypes = settings.getCertificateTypes() #Initialize TLS extensions if settings.useEncryptThenMAC: extensions = [TLSExtension().create(ExtensionType.encrypt_then_mac, bytearray(0))] else: extensions = None #Either send ClientHello (with a resumable session)... if session and session.sessionID: #If it's resumable, then its #ciphersuite must be one of the acceptable ciphersuites if session.cipherSuite not in cipherSuites: raise ValueError("Session's cipher suite not consistent "\ "with parameters") else: clientHello = ClientHello() clientHello.create(settings.maxVersion, getRandomBytes(32), session.sessionID, wireCipherSuites, certificateTypes, session.srpUsername, reqTack, nextProtos is not None, session.serverName, extensions=extensions) #Or send ClientHello (without) else: clientHello = ClientHello() clientHello.create(settings.maxVersion, getRandomBytes(32), bytearray(0), wireCipherSuites, certificateTypes, srpUsername, reqTack, nextProtos is not None, serverName, extensions=extensions) for result in self._sendMsg(clientHello): yield result yield clientHello def _clientGetServerHello(self, settings, clientHello): for result in self._getMsg(ContentType.handshake, HandshakeType.server_hello): if result in (0,1): yield result else: break serverHello = result #Get the server version. Do this before anything else, so any #error alerts will use the server's version self.version = serverHello.server_version #Check ServerHello if serverHello.server_version < settings.minVersion: for result in self._sendError(\ AlertDescription.protocol_version, "Too old version: %s" % str(serverHello.server_version)): yield result if serverHello.server_version > settings.maxVersion: for result in self._sendError(\ AlertDescription.protocol_version, "Too new version: %s" % str(serverHello.server_version)): yield result serverVer = serverHello.server_version cipherSuites = CipherSuite.filterForVersion(clientHello.cipher_suites, minVersion=serverVer, maxVersion=serverVer) if serverHello.cipher_suite not in cipherSuites: for result in self._sendError(\ AlertDescription.illegal_parameter, "Server responded with incorrect ciphersuite"): yield result if serverHello.certificate_type not in clientHello.certificate_types: for result in self._sendError(\ AlertDescription.illegal_parameter, "Server responded with incorrect certificate type"): yield result if serverHello.compression_method != 0: for result in self._sendError(\ AlertDescription.illegal_parameter, "Server responded with incorrect compression method"): yield result if serverHello.tackExt: if not clientHello.tack: for result in self._sendError(\ AlertDescription.illegal_parameter, "Server responded with unrequested Tack Extension"): yield result if not serverHello.tackExt.verifySignatures(): for result in self._sendError(\ AlertDescription.decrypt_error, "TackExtension contains an invalid signature"): yield result if serverHello.next_protos and not clientHello.supports_npn: for result in self._sendError(\ AlertDescription.illegal_parameter, "Server responded with unrequested NPN Extension"): yield result yield serverHello def _clientSelectNextProto(self, nextProtos, serverHello): # nextProtos is None or non-empty list of strings # serverHello.next_protos is None or possibly-empty list of strings # # !!! We assume the client may have specified nextProtos as a list of # strings so we convert them to bytearrays (it's awkward to require # the user to specify a list of bytearrays or "bytes", and in # Python 2.6 bytes() is just an alias for str() anyways... if nextProtos is not None and serverHello.next_protos is not None: for p in nextProtos: if bytearray(p) in serverHello.next_protos: return bytearray(p) else: # If the client doesn't support any of server's protocols, # or the server doesn't advertise any (next_protos == []) # the client SHOULD select the first protocol it supports. return bytearray(nextProtos[0]) return None def _clientResume(self, session, serverHello, clientRandom, cipherImplementations, nextProto): #If the server agrees to resume if session and session.sessionID and \ serverHello.session_id == session.sessionID: if serverHello.cipher_suite != session.cipherSuite: for result in self._sendError(\ AlertDescription.illegal_parameter,\ "Server's ciphersuite doesn't match session"): yield result #Calculate pending connection states self._calcPendingStates(session.cipherSuite, session.masterSecret, clientRandom, serverHello.random, cipherImplementations) #Exchange ChangeCipherSpec and Finished messages for result in self._getFinished(session.masterSecret, session.cipherSuite): yield result for result in self._sendFinished(session.masterSecret, session.cipherSuite, nextProto): yield result #Set the session for this connection self.session = session yield "resumed_and_finished" def _clientSRPKeyExchange(self, settings, cipherSuite, certificateType, srpUsername, password, clientRandom, serverRandom, tackExt): #If the server chose an SRP+RSA suite... if cipherSuite in CipherSuite.srpCertSuites: #Get Certificate, ServerKeyExchange, ServerHelloDone for result in self._getMsg(ContentType.handshake, HandshakeType.certificate, certificateType): if result in (0,1): yield result else: break serverCertificate = result else: serverCertificate = None for result in self._getMsg(ContentType.handshake, HandshakeType.server_key_exchange, cipherSuite): if result in (0,1): yield result else: break serverKeyExchange = result for result in self._getMsg(ContentType.handshake, HandshakeType.server_hello_done): if result in (0,1): yield result else: break serverHelloDone = result #Calculate SRP premaster secret #Get and check the server's group parameters and B value N = serverKeyExchange.srp_N g = serverKeyExchange.srp_g s = serverKeyExchange.srp_s B = serverKeyExchange.srp_B if (g,N) not in goodGroupParameters: for result in self._sendError(\ AlertDescription.insufficient_security, "Unknown group parameters"): yield result if numBits(N) < settings.minKeySize: for result in self._sendError(\ AlertDescription.insufficient_security, "N value is too small: %d" % numBits(N)): yield result if numBits(N) > settings.maxKeySize: for result in self._sendError(\ AlertDescription.insufficient_security, "N value is too large: %d" % numBits(N)): yield result if B % N == 0: for result in self._sendError(\ AlertDescription.illegal_parameter, "Suspicious B value"): yield result #Check the server's signature, if server chose an #SRP+RSA suite serverCertChain = None if cipherSuite in CipherSuite.srpCertSuites: #Hash ServerKeyExchange/ServerSRPParams hashBytes = serverKeyExchange.hash(clientRandom, serverRandom) if self.version == (3, 3): hashBytes = RSAKey.addPKCS1SHA1Prefix(hashBytes) #Extract signature bytes from ServerKeyExchange sigBytes = serverKeyExchange.signature if len(sigBytes) == 0: for result in self._sendError(\ AlertDescription.illegal_parameter, "Server sent an SRP ServerKeyExchange "\ "message without a signature"): yield result # Get server's public key from the Certificate message # Also validate the chain against the ServerHello's TACKext (if any) # If none, and a TACK cert is present, return its TACKext for result in self._clientGetKeyFromChain(serverCertificate, settings, tackExt): if result in (0,1): yield result else: break publicKey, serverCertChain, tackExt = result #Verify signature if not publicKey.verify(sigBytes, hashBytes): for result in self._sendError(\ AlertDescription.decrypt_error, "Signature failed to verify"): yield result #Calculate client's ephemeral DH values (a, A) a = bytesToNumber(getRandomBytes(32)) A = powMod(g, a, N) #Calculate client's static DH values (x, v) x = makeX(s, bytearray(srpUsername, "utf-8"), bytearray(password, "utf-8")) v = powMod(g, x, N) #Calculate u u = makeU(N, A, B) #Calculate premaster secret k = makeK(N, g) S = powMod((B - (k*v)) % N, a+(u*x), N) if self.fault == Fault.badA: A = N S = 0 premasterSecret = numberToByteArray(S) #Send ClientKeyExchange for result in self._sendMsg(\ ClientKeyExchange(cipherSuite, self.version).createSRP(A)): yield result yield (premasterSecret, serverCertChain, tackExt) def _clientRSAKeyExchange(self, settings, cipherSuite, clientCertChain, privateKey, certificateType, clientRandom, serverRandom, tackExt): #Get Certificate[, CertificateRequest], ServerHelloDone for result in self._getMsg(ContentType.handshake, HandshakeType.certificate, certificateType): if result in (0,1): yield result else: break serverCertificate = result # Get CertificateRequest or ServerHelloDone for result in self._getMsg(ContentType.handshake, (HandshakeType.server_hello_done, HandshakeType.certificate_request)): if result in (0,1): yield result else: break msg = result certificateRequest = None if isinstance(msg, CertificateRequest): certificateRequest = msg # We got CertificateRequest, so this must be ServerHelloDone for result in self._getMsg(ContentType.handshake, HandshakeType.server_hello_done): if result in (0,1): yield result else: break serverHelloDone = result elif isinstance(msg, ServerHelloDone): serverHelloDone = msg # Get server's public key from the Certificate message # Also validate the chain against the ServerHello's TACKext (if any) # If none, and a TACK cert is present, return its TACKext for result in self._clientGetKeyFromChain(serverCertificate, settings, tackExt): if result in (0,1): yield result else: break publicKey, serverCertChain, tackExt = result #Calculate premaster secret premasterSecret = getRandomBytes(48) premasterSecret[0] = settings.maxVersion[0] premasterSecret[1] = settings.maxVersion[1] if self.fault == Fault.badPremasterPadding: premasterSecret[0] = 5 if self.fault == Fault.shortPremasterSecret: premasterSecret = premasterSecret[:-1] #Encrypt premaster secret to server's public key encryptedPreMasterSecret = publicKey.encrypt(premasterSecret) #If client authentication was requested, send Certificate #message, either with certificates or empty if certificateRequest: clientCertificate = Certificate(certificateType) if clientCertChain: #Check to make sure we have the same type of #certificates the server requested wrongType = False if certificateType == CertificateType.x509: if not isinstance(clientCertChain, X509CertChain): wrongType = True if wrongType: for result in self._sendError(\ AlertDescription.handshake_failure, "Client certificate is of wrong type"): yield result clientCertificate.create(clientCertChain) for result in self._sendMsg(clientCertificate): yield result else: #The server didn't request client auth, so we #zeroize these so the clientCertChain won't be #stored in the session. privateKey = None clientCertChain = None #Send ClientKeyExchange clientKeyExchange = ClientKeyExchange(cipherSuite, self.version) clientKeyExchange.createRSA(encryptedPreMasterSecret) for result in self._sendMsg(clientKeyExchange): yield result #If client authentication was requested and we have a #private key, send CertificateVerify if certificateRequest and privateKey: signatureAlgorithm = None if self.version == (3,0): masterSecret = calcMasterSecret(self.version, cipherSuite, premasterSecret, clientRandom, serverRandom) verifyBytes = self._handshake_hash.digestSSL(masterSecret, b"") elif self.version in ((3,1), (3,2)): verifyBytes = self._handshake_hash.digest() elif self.version == (3,3): # TODO: Signature algorithm negotiation not supported. signatureAlgorithm = (HashAlgorithm.sha1, SignatureAlgorithm.rsa) verifyBytes = self._handshake_hash.digest('sha1') verifyBytes = RSAKey.addPKCS1SHA1Prefix(verifyBytes) if self.fault == Fault.badVerifyMessage: verifyBytes[0] = ((verifyBytes[0]+1) % 256) signedBytes = privateKey.sign(verifyBytes) certificateVerify = CertificateVerify(self.version) certificateVerify.create(signedBytes, signatureAlgorithm) for result in self._sendMsg(certificateVerify): yield result yield (premasterSecret, serverCertChain, clientCertChain, tackExt) def _clientDHEKeyExchange(self, settings, cipherSuite, clientCertChain, privateKey, certificateType, tackExt, clientRandom, serverRandom): #TODO: check if received messages match cipher suite # (abort if CertfificateRequest and ADH) # if server chose DHE_RSA cipher get the certificate if cipherSuite in CipherSuite.dheCertSuites: for result in self._getMsg(ContentType.handshake, HandshakeType.certificate, certificateType): if result in (0, 1): yield result else: break serverCertificate = result # get rest of handshake messages for result in self._getMsg(ContentType.handshake, HandshakeType.server_key_exchange, cipherSuite): if result in (0,1): yield result else: break serverKeyExchange = result for result in self._getMsg(ContentType.handshake, (HandshakeType.certificate_request, HandshakeType.server_hello_done)): if result in (0,1): yield result else: break certificateRequest = None if isinstance(result, CertificateRequest): certificateRequest = result # we got CertificateRequest so now we'll get ServerHelloDone for result in self._getMsg(ContentType.handshake, HandshakeType.server_hello_done): if result in (0, 1): yield result else: break serverHelloDone = result #Check the server's signature, if the server chose an DHE_RSA suite serverCertChain = None if cipherSuite in CipherSuite.dheCertSuites: #Check if the signature algorithm is sane in TLSv1.2 if self.version == (3, 3): if serverKeyExchange.hashAlg != HashAlgorithm.sha1 or \ serverKeyExchange.signAlg != SignatureAlgorithm.rsa: for result in self._sendError(\ AlertDescription.illegal_parameter, "Server selected not advertised " "signature algorithm"): yield result hashBytes = serverKeyExchange.hash(clientRandom, serverRandom) sigBytes = serverKeyExchange.signature if len(sigBytes) == 0: for result in self._sendError(\ AlertDescription.illegal_parameter, "Server sent DHE ServerKeyExchange message " "without a signature"): yield result for result in self._clientGetKeyFromChain(serverCertificate, settings, tackExt): if result in (0, 1): yield result else: break publicKey, serverCertChain, tackExt = result # actually a check for hashAlg == sha1 and signAlg == rsa if self.version == (3, 3): hashBytes = publicKey.addPKCS1SHA1Prefix(hashBytes) if not publicKey.verify(sigBytes, hashBytes): for result in self._sendError( AlertDescription.decrypt_error, "signature failed to verify"): yield result # TODO: make it changeable if 2**1023 > serverKeyExchange.dh_p: for result in self._sendError( AlertDescription.insufficient_security, "Server sent a DHE key exchange with very small prime"): yield result #Send Certificate if we were asked for it if certificateRequest: # if a peer doesn't advertise support for any algorithm in TLSv1.2, # support for SHA1+RSA can be assumed if self.version == (3, 3) and \ (HashAlgorithm.sha1, SignatureAlgorithm.rsa) \ not in certificateRequest.supported_signature_algs and\ len(certificateRequest.supported_signature_algs) > 0: for result in self._sendError(\ AlertDescription.handshake_failure, "Server doesn't accept any sigalgs we support: " + str(certificateRequest.supported_signature_algs)): yield result clientCertificate = Certificate(certificateType) if clientCertChain: #Check to make sure we have the same type of #certificates the server requested wrongType = False if certificateType == CertificateType.x509: if not isinstance(clientCertChain, X509CertChain): wrongType = True if wrongType: for result in self._sendError(\ AlertDescription.handshake_failure, "Client certificate is of wrong type"): yield result clientCertificate.create(clientCertChain) # we need to send the message even if we don't have a certificate for result in self._sendMsg(clientCertificate): yield result else: #Server didn't ask for cer, zeroise so session doesn't store them privateKey = None clientCertChain = None #calculate Yc dh_p = serverKeyExchange.dh_p dh_g = serverKeyExchange.dh_g dh_Xc = bytesToNumber(getRandomBytes(32)) dh_Ys = serverKeyExchange.dh_Ys dh_Yc = powMod(dh_g, dh_Xc, dh_p) #Send ClientKeyExchange for result in self._sendMsg(\ ClientKeyExchange(cipherSuite, self.version).createDH(dh_Yc)): yield result #Calculate premaster secret S = powMod(dh_Ys, dh_Xc, dh_p) premasterSecret = numberToByteArray(S) #if client auth was requested and we have a private key, send a #CertificateVerify if certificateRequest and privateKey: signatureAlgorithm = None if self.version == (3,0): masterSecret = calcMasterSecret(self.version, cipherSuite, premasterSecret, clientRandom, serverRandom) verifyBytes = self._handshake_hash.digestSSL(masterSecret, b"") elif self.version in ((3,1), (3,2)): verifyBytes = self._handshake_hash.digest() else: # self.version == (3,3): # TODO: Signature algorithm negotiation not supported. signatureAlgorithm = (HashAlgorithm.sha1, SignatureAlgorithm.rsa) verifyBytes = self._handshake_hash.digest('sha1') verifyBytes = RSAKey.addPKCS1SHA1Prefix(verifyBytes) if self.fault == Fault.badVerifyMessage: verifyBytes[0] = ((verifyBytes[0]+1) % 256) signedBytes = privateKey.sign(verifyBytes) certificateVerify = CertificateVerify(self.version) certificateVerify.create(signedBytes, signatureAlgorithm) for result in self._sendMsg(certificateVerify): yield result yield (premasterSecret, serverCertChain, clientCertChain, tackExt) def _clientFinished(self, premasterSecret, clientRandom, serverRandom, cipherSuite, cipherImplementations, nextProto): masterSecret = calcMasterSecret(self.version, cipherSuite, premasterSecret, clientRandom, serverRandom) self._calcPendingStates(cipherSuite, masterSecret, clientRandom, serverRandom, cipherImplementations) #Exchange ChangeCipherSpec and Finished messages for result in self._sendFinished(masterSecret, cipherSuite, nextProto): yield result for result in self._getFinished(masterSecret, cipherSuite, nextProto=nextProto): yield result yield masterSecret def _clientGetKeyFromChain(self, certificate, settings, tackExt=None): #Get and check cert chain from the Certificate message certChain = certificate.certChain if not certChain or certChain.getNumCerts() == 0: for result in self._sendError(AlertDescription.illegal_parameter, "Other party sent a Certificate message without "\ "certificates"): yield result #Get and check public key from the cert chain publicKey = certChain.getEndEntityPublicKey() if len(publicKey) < settings.minKeySize: for result in self._sendError(AlertDescription.handshake_failure, "Other party's public key too small: %d" % len(publicKey)): yield result if len(publicKey) > settings.maxKeySize: for result in self._sendError(AlertDescription.handshake_failure, "Other party's public key too large: %d" % len(publicKey)): yield result # If there's no TLS Extension, look for a TACK cert if tackpyLoaded: if not tackExt: tackExt = certChain.getTackExt() # If there's a TACK (whether via TLS or TACK Cert), check that it # matches the cert chain if tackExt and tackExt.tacks: for tack in tackExt.tacks: if not certChain.checkTack(tack): for result in self._sendError( AlertDescription.illegal_parameter, "Other party's TACK doesn't match their public key"): yield result yield publicKey, certChain, tackExt #********************************************************* # Server Handshake Functions #********************************************************* def handshakeServer(self, verifierDB=None, certChain=None, privateKey=None, reqCert=False, sessionCache=None, settings=None, checker=None, reqCAs = None, tacks=None, activationFlags=0, nextProtos=None, anon=False): """Perform a handshake in the role of server. This function performs an SSL or TLS handshake. Depending on the arguments and the behavior of the client, this function can perform an SRP, or certificate-based handshake. It can also perform a combined SRP and server-certificate handshake. Like any handshake function, this can be called on a closed TLS connection, or on a TLS connection that is already open. If called on an open connection it performs a re-handshake. This function does not send a Hello Request message before performing the handshake, so if re-handshaking is required, the server must signal the client to begin the re-handshake through some other means. If the function completes without raising an exception, the TLS connection will be open and available for data transfer. If an exception is raised, the connection will have been automatically closed (if it was ever open). @type verifierDB: L{tlslite.verifierdb.VerifierDB} @param verifierDB: A database of SRP password verifiers associated with usernames. If the client performs an SRP handshake, the session's srpUsername attribute will be set. @type certChain: L{tlslite.x509certchain.X509CertChain} @param certChain: The certificate chain to be used if the client requests server certificate authentication. @type privateKey: L{tlslite.utils.rsakey.RSAKey} @param privateKey: The private key to be used if the client requests server certificate authentication. @type reqCert: bool @param reqCert: Whether to request client certificate authentication. This only applies if the client chooses server certificate authentication; if the client chooses SRP authentication, this will be ignored. If the client performs a client certificate authentication, the sessions's clientCertChain attribute will be set. @type sessionCache: L{tlslite.sessioncache.SessionCache} @param sessionCache: An in-memory cache of resumable sessions. The client can resume sessions from this cache. Alternatively, if the client performs a full handshake, a new session will be added to the cache. @type settings: L{tlslite.handshakesettings.HandshakeSettings} @param settings: Various settings which can be used to control the ciphersuites and SSL/TLS version chosen by the server. @type checker: L{tlslite.checker.Checker} @param checker: A Checker instance. This instance will be invoked to examine the other party's authentication credentials, if the handshake completes succesfully. @type reqCAs: list of L{bytearray} of unsigned bytes @param reqCAs: A collection of DER-encoded DistinguishedNames that will be sent along with a certificate request. This does not affect verification. @type nextProtos: list of strings. @param nextProtos: A list of upper layer protocols to expose to the clients through the Next-Protocol Negotiation Extension, if they support it. @raise socket.error: If a socket error occurs. @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed without a preceding alert. @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. @raise tlslite.errors.TLSAuthenticationError: If the checker doesn't like the other party's authentication credentials. """ for result in self.handshakeServerAsync(verifierDB, certChain, privateKey, reqCert, sessionCache, settings, checker, reqCAs, tacks=tacks, activationFlags=activationFlags, nextProtos=nextProtos, anon=anon): pass def handshakeServerAsync(self, verifierDB=None, certChain=None, privateKey=None, reqCert=False, sessionCache=None, settings=None, checker=None, reqCAs=None, tacks=None, activationFlags=0, nextProtos=None, anon=False ): """Start a server handshake operation on the TLS connection. This function returns a generator which behaves similarly to handshakeServer(). Successive invocations of the generator will return 0 if it is waiting to read from the socket, 1 if it is waiting to write to the socket, or it will raise StopIteration if the handshake operation is complete. @rtype: iterable @return: A generator; see above for details. """ handshaker = self._handshakeServerAsyncHelper(\ verifierDB=verifierDB, certChain=certChain, privateKey=privateKey, reqCert=reqCert, sessionCache=sessionCache, settings=settings, reqCAs=reqCAs, tacks=tacks, activationFlags=activationFlags, nextProtos=nextProtos, anon=anon) for result in self._handshakeWrapperAsync(handshaker, checker): yield result def _handshakeServerAsyncHelper(self, verifierDB, certChain, privateKey, reqCert, sessionCache, settings, reqCAs, tacks, activationFlags, nextProtos, anon): self._handshakeStart(client=False) if (not verifierDB) and (not certChain) and not anon: raise ValueError("Caller passed no authentication credentials") if certChain and not privateKey: raise ValueError("Caller passed a certChain but no privateKey") if privateKey and not certChain: raise ValueError("Caller passed a privateKey but no certChain") if reqCAs and not reqCert: raise ValueError("Caller passed reqCAs but not reqCert") if certChain and not isinstance(certChain, X509CertChain): raise ValueError("Unrecognized certificate type") if activationFlags and not tacks: raise ValueError("Nonzero activationFlags requires tacks") if tacks: if not tackpyLoaded: raise ValueError("tackpy is not loaded") if not settings or not settings.useExperimentalTackExtension: raise ValueError("useExperimentalTackExtension not enabled") if not settings: settings = HandshakeSettings() settings = settings.validate() # OK Start exchanging messages # ****************************** # Handle ClientHello and resumption for result in self._serverGetClientHello(settings, certChain,\ verifierDB, sessionCache, anon): if result in (0,1): yield result elif result == None: self._handshakeDone(resumed=True) return # Handshake was resumed, we're done else: break (clientHello, cipherSuite) = result #If not a resumption... # Create the ServerHello message if sessionCache: sessionID = getRandomBytes(32) else: sessionID = bytearray(0) if not clientHello.supports_npn: nextProtos = None # If not doing a certificate-based suite, discard the TACK if not cipherSuite in CipherSuite.certAllSuites: tacks = None # Prepare a TACK Extension if requested if clientHello.tack: tackExt = TackExtension.create(tacks, activationFlags) else: tackExt = None # Prepare other extensions if requested if settings.useEncryptThenMAC and \ clientHello.getExtension(ExtensionType.encrypt_then_mac) and \ cipherSuite not in CipherSuite.streamSuites and \ cipherSuite not in CipherSuite.aeadSuites: extensions = [TLSExtension().create(ExtensionType.encrypt_then_mac, bytearray(0))] self._recordLayer.encryptThenMAC = True else: extensions = None serverHello = ServerHello() serverHello.create(self.version, getRandomBytes(32), sessionID, \ cipherSuite, CertificateType.x509, tackExt, nextProtos, extensions=extensions) # Perform the SRP key exchange clientCertChain = None if cipherSuite in CipherSuite.srpAllSuites: for result in self._serverSRPKeyExchange(clientHello, serverHello, verifierDB, cipherSuite, privateKey, certChain): if result in (0,1): yield result else: break premasterSecret = result # Perform a certificate-based key exchange elif (cipherSuite in CipherSuite.certSuites or cipherSuite in CipherSuite.dheCertSuites): if cipherSuite in CipherSuite.certSuites: keyExchange = RSAKeyExchange(cipherSuite, clientHello, serverHello, privateKey) elif cipherSuite in CipherSuite.dheCertSuites: keyExchange = DHE_RSAKeyExchange(cipherSuite, clientHello, serverHello, privateKey) else: assert(False) for result in self._serverCertKeyExchange(clientHello, serverHello, certChain, keyExchange, reqCert, reqCAs, cipherSuite, settings): if result in (0,1): yield result else: break (premasterSecret, clientCertChain) = result # Perform anonymous Diffie Hellman key exchange elif cipherSuite in CipherSuite.anonSuites: for result in self._serverAnonKeyExchange(clientHello, serverHello, cipherSuite, settings): if result in (0,1): yield result else: break premasterSecret = result else: assert(False) # Exchange Finished messages for result in self._serverFinished(premasterSecret, clientHello.random, serverHello.random, cipherSuite, settings.cipherImplementations, nextProtos): if result in (0,1): yield result else: break masterSecret = result #Create the session object self.session = Session() if cipherSuite in CipherSuite.certAllSuites: serverCertChain = certChain else: serverCertChain = None srpUsername = None serverName = None if clientHello.srp_username: srpUsername = clientHello.srp_username.decode("utf-8") if clientHello.server_name: serverName = clientHello.server_name.decode("utf-8") self.session.create(masterSecret, serverHello.session_id, cipherSuite, srpUsername, clientCertChain, serverCertChain, tackExt, (serverHello.tackExt is not None), serverName, encryptThenMAC=self._recordLayer.encryptThenMAC) #Add the session object to the session cache if sessionCache and sessionID: sessionCache[sessionID] = self.session self._handshakeDone(resumed=False) def _serverGetClientHello(self, settings, certChain, verifierDB, sessionCache, anon): #Tentatively set version to most-desirable version, so if an error #occurs parsing the ClientHello, this is what we'll use for the #error alert self.version = settings.maxVersion #Get ClientHello for result in self._getMsg(ContentType.handshake, HandshakeType.client_hello): if result in (0,1): yield result else: break clientHello = result #If client's version is too low, reject it if clientHello.client_version < settings.minVersion: self.version = settings.minVersion for result in self._sendError(\ AlertDescription.protocol_version, "Too old version: %s" % str(clientHello.client_version)): yield result #If client's version is too high, propose my highest version elif clientHello.client_version > settings.maxVersion: self.version = settings.maxVersion else: #Set the version to the client's version self.version = clientHello.client_version #Detect if the client performed an inappropriate fallback. if clientHello.client_version < settings.maxVersion and \ CipherSuite.TLS_FALLBACK_SCSV in clientHello.cipher_suites: for result in self._sendError(\ AlertDescription.inappropriate_fallback): yield result #Now that the version is known, limit to only the ciphers available to #that version. cipherSuites = [] if verifierDB: if certChain: cipherSuites += \ CipherSuite.getSrpCertSuites(settings, self.version) cipherSuites += CipherSuite.getSrpSuites(settings, self.version) elif certChain: cipherSuites += CipherSuite.getDheCertSuites(settings, self.version) cipherSuites += CipherSuite.getCertSuites(settings, self.version) elif anon: cipherSuites += CipherSuite.getAnonSuites(settings, self.version) else: assert(False) cipherSuites = CipherSuite.filterForVersion(cipherSuites, minVersion=self.version, maxVersion=self.version) #If resumption was requested and we have a session cache... if clientHello.session_id and sessionCache: session = None #Check in the session cache if sessionCache and not session: try: session = sessionCache[clientHello.session_id] if not session.resumable: raise AssertionError() #Check for consistency with ClientHello if session.cipherSuite not in cipherSuites: for result in self._sendError(\ AlertDescription.handshake_failure): yield result if session.cipherSuite not in clientHello.cipher_suites: for result in self._sendError(\ AlertDescription.handshake_failure): yield result if clientHello.srp_username: if not session.srpUsername or \ clientHello.srp_username != bytearray(session.srpUsername, "utf-8"): for result in self._sendError(\ AlertDescription.handshake_failure): yield result if clientHello.server_name: if not session.serverName or \ clientHello.server_name != bytearray(session.serverName, "utf-8"): for result in self._sendError(\ AlertDescription.handshake_failure): yield result if session.encryptThenMAC and \ not clientHello.getExtension( ExtensionType.encrypt_then_mac): for result in self._sendError(\ AlertDescription.handshake_failure): yield result except KeyError: pass #If a session is found.. if session: #Send ServerHello if session.encryptThenMAC: self._recordLayer.encryptThenMAC = True mte = TLSExtension().create(ExtensionType.encrypt_then_mac, bytearray(0)) extensions = [mte] else: extensions = None serverHello = ServerHello() serverHello.create(self.version, getRandomBytes(32), session.sessionID, session.cipherSuite, CertificateType.x509, None, None, extensions=extensions) for result in self._sendMsg(serverHello): yield result #Calculate pending connection states self._calcPendingStates(session.cipherSuite, session.masterSecret, clientHello.random, serverHello.random, settings.cipherImplementations) #Exchange ChangeCipherSpec and Finished messages for result in self._sendFinished(session.masterSecret, session.cipherSuite): yield result for result in self._getFinished(session.masterSecret, session.cipherSuite): yield result #Set the session self.session = session yield None # Handshake done! #Calculate the first cipher suite intersection. #This is the 'privileged' ciphersuite. We'll use it if we're #doing a new negotiation. In fact, #the only time we won't use it is if we're resuming a #session, in which case we use the ciphersuite from the session. # #Given the current ciphersuite ordering, this means we prefer SRP #over non-SRP. for cipherSuite in cipherSuites: if cipherSuite in clientHello.cipher_suites: break else: for result in self._sendError(\ AlertDescription.handshake_failure, "No mutual ciphersuite"): yield result if cipherSuite in CipherSuite.srpAllSuites and \ not clientHello.srp_username: for result in self._sendError(\ AlertDescription.unknown_psk_identity, "Client sent a hello, but without the SRP username"): yield result #If an RSA suite is chosen, check for certificate type intersection if cipherSuite in CipherSuite.certAllSuites and CertificateType.x509 \ not in clientHello.certificate_types: for result in self._sendError(\ AlertDescription.handshake_failure, "the client doesn't support my certificate type"): yield result # If resumption was not requested, or # we have no session cache, or # the client's session_id was not found in cache: yield (clientHello, cipherSuite) def _serverSRPKeyExchange(self, clientHello, serverHello, verifierDB, cipherSuite, privateKey, serverCertChain): srpUsername = clientHello.srp_username.decode("utf-8") self.allegedSrpUsername = srpUsername #Get parameters from username try: entry = verifierDB[srpUsername] except KeyError: for result in self._sendError(\ AlertDescription.unknown_psk_identity): yield result (N, g, s, v) = entry #Calculate server's ephemeral DH values (b, B) b = bytesToNumber(getRandomBytes(32)) k = makeK(N, g) B = (powMod(g, b, N) + (k*v)) % N #Create ServerKeyExchange, signing it if necessary serverKeyExchange = ServerKeyExchange(cipherSuite, self.version) serverKeyExchange.createSRP(N, g, s, B) if cipherSuite in CipherSuite.srpCertSuites: hashBytes = serverKeyExchange.hash(clientHello.random, serverHello.random) if self.version == (3, 3): hashBytes = RSAKey.addPKCS1SHA1Prefix(hashBytes) serverKeyExchange.signature = privateKey.sign(hashBytes) if self.version == (3, 3): # TODO signing algorithm not negotiatied serverKeyExchange.signAlg = SignatureAlgorithm.rsa serverKeyExchange.hashAlg = HashAlgorithm.sha1 #Send ServerHello[, Certificate], ServerKeyExchange, #ServerHelloDone msgs = [] msgs.append(serverHello) if cipherSuite in CipherSuite.srpCertSuites: certificateMsg = Certificate(CertificateType.x509) certificateMsg.create(serverCertChain) msgs.append(certificateMsg) msgs.append(serverKeyExchange) msgs.append(ServerHelloDone()) for result in self._sendMsgs(msgs): yield result #Get and check ClientKeyExchange for result in self._getMsg(ContentType.handshake, HandshakeType.client_key_exchange, cipherSuite): if result in (0,1): yield result else: break clientKeyExchange = result A = clientKeyExchange.srp_A if A % N == 0: for result in self._sendError(AlertDescription.illegal_parameter, "Suspicious A value"): yield result assert(False) # Just to ensure we don't fall through somehow #Calculate u u = makeU(N, A, B) #Calculate premaster secret S = powMod((A * powMod(v,u,N)) % N, b, N) premasterSecret = numberToByteArray(S) yield premasterSecret def _serverCertKeyExchange(self, clientHello, serverHello, serverCertChain, keyExchange, reqCert, reqCAs, cipherSuite, settings): #Send ServerHello, Certificate[, ServerKeyExchange] #[, CertificateRequest], ServerHelloDone msgs = [] # If we verify a client cert chain, return it clientCertChain = None msgs.append(serverHello) msgs.append(Certificate(CertificateType.x509).create(serverCertChain)) serverKeyExchange = keyExchange.makeServerKeyExchange() if serverKeyExchange is not None: msgs.append(serverKeyExchange) if reqCert: certificateRequest = CertificateRequest(self.version) if not reqCAs: reqCAs = [] # TODO add support for more HashAlgorithms certificateRequest.create([ClientCertificateType.rsa_sign], reqCAs, [(HashAlgorithm.sha1, SignatureAlgorithm.rsa)]) msgs.append(certificateRequest) msgs.append(ServerHelloDone()) for result in self._sendMsgs(msgs): yield result #Get [Certificate,] (if was requested) if reqCert: if self.version == (3,0): for result in self._getMsg((ContentType.handshake, ContentType.alert), HandshakeType.certificate, CertificateType.x509): if result in (0,1): yield result else: break msg = result if isinstance(msg, Alert): #If it's not a no_certificate alert, re-raise alert = msg if alert.description != \ AlertDescription.no_certificate: self._shutdown(False) raise TLSRemoteAlert(alert) elif isinstance(msg, Certificate): clientCertificate = msg if clientCertificate.certChain and \ clientCertificate.certChain.getNumCerts()!=0: clientCertChain = clientCertificate.certChain else: raise AssertionError() elif self.version in ((3,1), (3,2), (3,3)): for result in self._getMsg(ContentType.handshake, HandshakeType.certificate, CertificateType.x509): if result in (0,1): yield result else: break clientCertificate = result if clientCertificate.certChain and \ clientCertificate.certChain.getNumCerts()!=0: clientCertChain = clientCertificate.certChain else: raise AssertionError() #Get ClientKeyExchange for result in self._getMsg(ContentType.handshake, HandshakeType.client_key_exchange, cipherSuite): if result in (0,1): yield result else: break clientKeyExchange = result #Process ClientKeyExchange try: premasterSecret = \ keyExchange.processClientKeyExchange(clientKeyExchange) except TLSLocalAlert as alert: for result in self._sendError(alert.description, alert.message): yield result #Get and check CertificateVerify, if relevant if clientCertChain: if self.version == (3,0): masterSecret = calcMasterSecret(self.version, cipherSuite, premasterSecret, clientHello.random, serverHello.random) verifyBytes = self._handshake_hash.digestSSL(masterSecret, b"") elif self.version in ((3,1), (3,2)): verifyBytes = self._handshake_hash.digest() elif self.version == (3,3): verifyBytes = self._handshake_hash.digest('sha1') verifyBytes = RSAKey.addPKCS1SHA1Prefix(verifyBytes) for result in self._getMsg(ContentType.handshake, HandshakeType.certificate_verify): if result in (0,1): yield result else: break certificateVerify = result publicKey = clientCertChain.getEndEntityPublicKey() if len(publicKey) < settings.minKeySize: for result in self._sendError(\ AlertDescription.handshake_failure, "Client's public key too small: %d" % len(publicKey)): yield result if len(publicKey) > settings.maxKeySize: for result in self._sendError(\ AlertDescription.handshake_failure, "Client's public key too large: %d" % len(publicKey)): yield result if not publicKey.verify(certificateVerify.signature, verifyBytes): for result in self._sendError(\ AlertDescription.decrypt_error, "Signature failed to verify"): yield result yield (premasterSecret, clientCertChain) def _serverAnonKeyExchange(self, clientHello, serverHello, cipherSuite, settings): # Calculate DH p, g, Xs, Ys # TODO make configurable dh_g, dh_p = goodGroupParameters[2] dh_Xs = bytesToNumber(getRandomBytes(32)) dh_Ys = powMod(dh_g, dh_Xs, dh_p) #Create ServerKeyExchange serverKeyExchange = ServerKeyExchange(cipherSuite, self.version) serverKeyExchange.createDH(dh_p, dh_g, dh_Ys) #Send ServerHello[, Certificate], ServerKeyExchange, #ServerHelloDone msgs = [] msgs.append(serverHello) msgs.append(serverKeyExchange) msgs.append(ServerHelloDone()) for result in self._sendMsgs(msgs): yield result #Get and check ClientKeyExchange for result in self._getMsg(ContentType.handshake, HandshakeType.client_key_exchange, cipherSuite): if result in (0,1): yield result else: break clientKeyExchange = result dh_Yc = clientKeyExchange.dh_Yc if dh_Yc % dh_p == 0: for result in self._sendError(AlertDescription.illegal_parameter, "Suspicious dh_Yc value"): yield result assert(False) # Just to ensure we don't fall through somehow #Calculate premaster secre S = powMod(dh_Yc,dh_Xs,dh_p) premasterSecret = numberToByteArray(S) yield premasterSecret def _serverFinished(self, premasterSecret, clientRandom, serverRandom, cipherSuite, cipherImplementations, nextProtos): masterSecret = calcMasterSecret(self.version, cipherSuite, premasterSecret, clientRandom, serverRandom) #Calculate pending connection states self._calcPendingStates(cipherSuite, masterSecret, clientRandom, serverRandom, cipherImplementations) #Exchange ChangeCipherSpec and Finished messages for result in self._getFinished(masterSecret, cipherSuite, expect_next_protocol=nextProtos is not None): yield result for result in self._sendFinished(masterSecret, cipherSuite): yield result yield masterSecret #********************************************************* # Shared Handshake Functions #********************************************************* def _sendFinished(self, masterSecret, cipherSuite=None, nextProto=None): #Send ChangeCipherSpec for result in self._sendMsg(ChangeCipherSpec()): yield result #Switch to pending write state self._changeWriteState() if nextProto is not None: nextProtoMsg = NextProtocol().create(nextProto) for result in self._sendMsg(nextProtoMsg): yield result #Calculate verification data verifyData = calcFinished(self.version, masterSecret, cipherSuite, self._handshake_hash, self._client) if self.fault == Fault.badFinished: verifyData[0] = (verifyData[0]+1)%256 #Send Finished message under new state finished = Finished(self.version).create(verifyData) for result in self._sendMsg(finished): yield result def _getFinished(self, masterSecret, cipherSuite=None, expect_next_protocol=False, nextProto=None): #Get and check ChangeCipherSpec for result in self._getMsg(ContentType.change_cipher_spec): if result in (0,1): yield result changeCipherSpec = result if changeCipherSpec.type != 1: for result in self._sendError(AlertDescription.illegal_parameter, "ChangeCipherSpec type incorrect"): yield result #Switch to pending read state self._changeReadState() #Server Finish - Are we waiting for a next protocol echo? if expect_next_protocol: for result in self._getMsg(ContentType.handshake, HandshakeType.next_protocol): if result in (0,1): yield result if result is None: for result in self._sendError(AlertDescription.unexpected_message, "Didn't get NextProtocol message"): yield result self.next_proto = result.next_proto else: self.next_proto = None #Client Finish - Only set the next_protocol selected in the connection if nextProto: self.next_proto = nextProto #Calculate verification data verifyData = calcFinished(self.version, masterSecret, cipherSuite, self._handshake_hash, not self._client) #Get and check Finished message under new state for result in self._getMsg(ContentType.handshake, HandshakeType.finished): if result in (0,1): yield result finished = result if finished.verify_data != verifyData: for result in self._sendError(AlertDescription.decrypt_error, "Finished message is incorrect"): yield result def _handshakeWrapperAsync(self, handshaker, checker): try: for result in handshaker: yield result if checker: try: checker(self) except TLSAuthenticationError: alert = Alert().create(AlertDescription.close_notify, AlertLevel.fatal) for result in self._sendMsg(alert): yield result raise except GeneratorExit: raise except TLSAlert as alert: if not self.fault: raise if alert.description not in Fault.faultAlerts[self.fault]: raise TLSFaultError(str(alert)) else: pass except: self._shutdown(False) raise tlslite-ng-0.5.1/tlslite/tlsrecordlayer.py000066400000000000000000001041301261665411200206720ustar00rootroot00000000000000# Authors: # Trevor Perrin # Google (adapted by Sam Rushing) - NPN support # Google - minimal padding # Martin von Loewis - python 3 port # Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2 # Hubert Kario # # See the LICENSE file for legal information regarding use of this file. """Helper class for TLSConnection.""" from __future__ import generators from .utils.compat import * from .utils.cryptomath import * from .utils.codec import Parser from .errors import * from .messages import * from .mathtls import * from .constants import * from .recordlayer import RecordLayer from .defragmenter import Defragmenter from .handshakehashes import HandshakeHashes import socket import traceback class TLSRecordLayer(object): """ This class handles data transmission for a TLS connection. Its only subclass is L{tlslite.TLSConnection.TLSConnection}. We've separated the code in this class from TLSConnection to make things more readable. @type sock: socket.socket @ivar sock: The underlying socket object. @type session: L{tlslite.Session.Session} @ivar session: The session corresponding to this connection. Due to TLS session resumption, multiple connections can correspond to the same underlying session. @type version: tuple @ivar version: The TLS version being used for this connection. (3,0) means SSL 3.0, and (3,1) means TLS 1.0. @type closed: bool @ivar closed: If this connection is closed. @type resumed: bool @ivar resumed: If this connection is based on a resumed session. @type allegedSrpUsername: str or None @ivar allegedSrpUsername: This is set to the SRP username asserted by the client, whether the handshake succeeded or not. If the handshake fails, this can be inspected to determine if a guessing attack is in progress against a particular user account. @type closeSocket: bool @ivar closeSocket: If the socket should be closed when the connection is closed, defaults to True (writable). If you set this to True, TLS Lite will assume the responsibility of closing the socket when the TLS Connection is shutdown (either through an error or through the user calling close()). The default is False. @type ignoreAbruptClose: bool @ivar ignoreAbruptClose: If an abrupt close of the socket should raise an error (writable). If you set this to True, TLS Lite will not raise a L{tlslite.errors.TLSAbruptCloseError} exception if the underlying socket is unexpectedly closed. Such an unexpected closure could be caused by an attacker. However, it also occurs with some incorrect TLS implementations. You should set this to True only if you're not worried about an attacker truncating the connection, and only if necessary to avoid spurious errors. The default is False. @type encryptThenMAC: bool @ivar encryptThenMAC: Whether the connection uses the encrypt-then-MAC construct for CBC cipher suites, will be False also if connection uses RC4 or AEAD. @type recordSize: int @ivar recordSize: maimum size of data to be sent in a single record layer message. Note that after encryption is established (generally after handshake protocol has finished) the actual amount of data written to network socket will be larger because of the record layer header, padding or encryption overhead. It can be set to low value (so that there is no fragmentation on Ethernet, IP and TCP level) at the beginning of connection to reduce latency and set to protocol max (2**14) to maximise throughput after sending few kiB of data. Setting to values greater than 2**14 will cause the connection to be dropped by RFC compliant peers. @sort: __init__, read, readAsync, write, writeAsync, close, closeAsync, getCipherImplementation, getCipherName """ def __init__(self, sock): self.sock = sock self._recordLayer = RecordLayer(sock) #My session object (Session instance; read-only) self.session = None #Buffers for processing messages self._defragmenter = Defragmenter() self._defragmenter.addStaticSize(ContentType.change_cipher_spec, 1) self._defragmenter.addStaticSize(ContentType.alert, 2) self._defragmenter.addDynamicSize(ContentType.handshake, 1, 3) self.clearReadBuffer() self.clearWriteBuffer() #Handshake digests self._handshake_hash = HandshakeHashes() #Is the connection open? self.closed = True #read-only self._refCount = 0 #Used to trigger closure #Is this a resumed session? self.resumed = False #read-only #What username did the client claim in his handshake? self.allegedSrpUsername = None #On a call to close(), do we close the socket? (writeable) self.closeSocket = True #If the socket is abruptly closed, do we ignore it #and pretend the connection was shut down properly? (writeable) self.ignoreAbruptClose = False #Fault we will induce, for testing purposes self.fault = None #Limit the size of outgoing records to following size self.recordSize = 16384 # 2**14 @property def _client(self): """Boolean stating if the endpoint acts as a client""" return self._recordLayer.client @_client.setter def _client(self, value): """Set the endpoint to act as a client or not""" self._recordLayer.client = value @property def version(self): """Get the SSL protocol version of connection""" return self._recordLayer.version @version.setter def version(self, value): """ Set the SSL protocol version of connection The setter is a public method only for backwards compatibility. Don't use it! See at HandshakeSettings for options to set desired protocol version. """ self._recordLayer.version = value @property def encryptThenMAC(self): """Whether the connection uses Encrypt Then MAC (RFC 7366)""" return self._recordLayer.encryptThenMAC def clearReadBuffer(self): self._readBuffer = b'' def clearWriteBuffer(self): self._send_writer = None #********************************************************* # Public Functions START #********************************************************* def read(self, max=None, min=1): """Read some data from the TLS connection. This function will block until at least 'min' bytes are available (or the connection is closed). If an exception is raised, the connection will have been automatically closed. @type max: int @param max: The maximum number of bytes to return. @type min: int @param min: The minimum number of bytes to return @rtype: str @return: A string of no more than 'max' bytes, and no fewer than 'min' (unless the connection has been closed, in which case fewer than 'min' bytes may be returned). @raise socket.error: If a socket error occurs. @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed without a preceding alert. @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. """ for result in self.readAsync(max, min): pass return result def readAsync(self, max=None, min=1): """Start a read operation on the TLS connection. This function returns a generator which behaves similarly to read(). Successive invocations of the generator will return 0 if it is waiting to read from the socket, 1 if it is waiting to write to the socket, or a string if the read operation has completed. @rtype: iterable @return: A generator; see above for details. """ try: while len(self._readBuffer) self.recordSize: newB = buf[:self.recordSize] buf = buf[self.recordSize:] msgFragment = Message(contentType, newB) for result in self._sendMsgThroughSocket(msgFragment): yield result msgFragment = Message(contentType, buf) for result in self._sendMsgThroughSocket(msgFragment): yield result def _sendMsgThroughSocket(self, msg): """Send message, handle errors""" try: for result in self._recordLayer.sendRecord(msg): if result in (0, 1): yield result except socket.error: # The socket was unexpectedly closed. The tricky part # is that there may be an alert sent by the other party # sitting in the read buffer. So, if we get here after # handshaking, we will just raise the error and let the # caller read more data if it would like, thus stumbling # upon the error. # # However, if we get here DURING handshaking, we take # it upon ourselves to see if the next message is an # Alert. if msg.contentType == ContentType.handshake: # See if there's an alert record # Could raise socket.error or TLSAbruptCloseError for result in self._getNextRecord(): if result in (0, 1): yield result else: break # Closes the socket self._shutdown(False) # If we got an alert, raise it recordHeader, p = result if recordHeader.type == ContentType.alert: alert = Alert().parse(p) raise TLSRemoteAlert(alert) else: # If we got some other message who know what # the remote side is doing, just go ahead and # raise the socket.error raise def _getMsg(self, expectedType, secondaryType=None, constructorType=None): try: if not isinstance(expectedType, tuple): expectedType = (expectedType,) #Spin in a loop, until we've got a non-empty record of a type we #expect. The loop will be repeated if: # - we receive a renegotiation attempt; we send no_renegotiation, # then try again # - we receive an empty application-data fragment; we try again while 1: for result in self._getNextRecord(): if result in (0,1): yield result else: break recordHeader, p = result #If this is an empty application-data fragment, try again if recordHeader.type == ContentType.application_data: if p.index == len(p.bytes): continue #If we received an unexpected record type... if recordHeader.type not in expectedType: #If we received an alert... if recordHeader.type == ContentType.alert: alert = Alert().parse(p) #We either received a fatal error, a warning, or a #close_notify. In any case, we're going to close the #connection. In the latter two cases we respond with #a close_notify, but ignore any socket errors, since #the other side might have already closed the socket. if alert.level == AlertLevel.warning or \ alert.description == AlertDescription.close_notify: #If the sendMsg() call fails because the socket has #already been closed, we will be forgiving and not #report the error nor invalidate the "resumability" #of the session. try: alertMsg = Alert() alertMsg.create(AlertDescription.close_notify, AlertLevel.warning) for result in self._sendMsg(alertMsg): yield result except socket.error: pass if alert.description == \ AlertDescription.close_notify: self._shutdown(True) elif alert.level == AlertLevel.warning: self._shutdown(False) else: #Fatal alert: self._shutdown(False) #Raise the alert as an exception raise TLSRemoteAlert(alert) #If we received a renegotiation attempt... if recordHeader.type == ContentType.handshake: subType = p.get(1) reneg = False if self._client: if subType == HandshakeType.hello_request: reneg = True else: if subType == HandshakeType.client_hello: reneg = True #Send no_renegotiation, then try again if reneg: alertMsg = Alert() alertMsg.create(AlertDescription.no_renegotiation, AlertLevel.warning) for result in self._sendMsg(alertMsg): yield result continue #Otherwise: this is an unexpected record, but neither an #alert nor renegotiation for result in self._sendError(\ AlertDescription.unexpected_message, "received type=%d" % recordHeader.type): yield result break #Parse based on content_type if recordHeader.type == ContentType.change_cipher_spec: yield ChangeCipherSpec().parse(p) elif recordHeader.type == ContentType.alert: yield Alert().parse(p) elif recordHeader.type == ContentType.application_data: yield ApplicationData().parse(p) elif recordHeader.type == ContentType.handshake: #Convert secondaryType to tuple, if it isn't already if not isinstance(secondaryType, tuple): secondaryType = (secondaryType,) #If it's a handshake message, check handshake header if recordHeader.ssl2: subType = p.get(1) if subType != HandshakeType.client_hello: for result in self._sendError(\ AlertDescription.unexpected_message, "Can only handle SSLv2 ClientHello messages"): yield result if HandshakeType.client_hello not in secondaryType: for result in self._sendError(\ AlertDescription.unexpected_message): yield result subType = HandshakeType.client_hello else: subType = p.get(1) if subType not in secondaryType: for result in self._sendError(\ AlertDescription.unexpected_message, "Expecting %s, got %s" % (str(secondaryType), subType)): yield result #Update handshake hashes self._handshake_hash.update(p.bytes) #Parse based on handshake type if subType == HandshakeType.client_hello: yield ClientHello(recordHeader.ssl2).parse(p) elif subType == HandshakeType.server_hello: yield ServerHello().parse(p) elif subType == HandshakeType.certificate: yield Certificate(constructorType).parse(p) elif subType == HandshakeType.certificate_request: yield CertificateRequest(self.version).parse(p) elif subType == HandshakeType.certificate_verify: yield CertificateVerify(self.version).parse(p) elif subType == HandshakeType.server_key_exchange: yield ServerKeyExchange(constructorType, self.version).parse(p) elif subType == HandshakeType.server_hello_done: yield ServerHelloDone().parse(p) elif subType == HandshakeType.client_key_exchange: yield ClientKeyExchange(constructorType, \ self.version).parse(p) elif subType == HandshakeType.finished: yield Finished(self.version).parse(p) elif subType == HandshakeType.next_protocol: yield NextProtocol().parse(p) else: raise AssertionError() #If an exception was raised by a Parser or Message instance: except SyntaxError as e: for result in self._sendError(AlertDescription.decode_error, formatExceptionTrace(e)): yield result #Returns next record or next handshake message def _getNextRecord(self): """read next message from socket, defragment message""" while True: # support for fragmentation # (RFC 5246 Section 6.2.1) # Because the Record Layer is completely separate from the messages # that traverse it, it should handle both application data and # hadshake data in the same way. For that we buffer the handshake # messages until they are completely read. # This makes it possible to handle both handshake data not aligned # to record boundary as well as handshakes longer than single # record. while True: # empty message buffer ret = self._defragmenter.getMessage() if ret is None: break header = RecordHeader3().create(self.version, ret[0], 0) yield header, Parser(ret[1]) # when the message buffer is empty, read next record from socket for result in self._getNextRecordFromSocket(): if result in (0, 1): yield result else: break header, parser = result # application data isn't made out of messages, pass it through if header.type == ContentType.application_data: yield (header, parser) # If it's an SSLv2 ClientHello, we can return it as well, since # it's the only ssl2 type we support elif header.ssl2: yield (header, parser) else: # other types need to be put into buffers self._defragmenter.addData(header.type, parser.bytes) def _getNextRecordFromSocket(self): """Read a record, handle errors""" try: # otherwise... read the next record for result in self._recordLayer.recvRecord(): if result in (0, 1): yield result else: break except TLSRecordOverflow: for result in self._sendError(AlertDescription.record_overflow): yield result except TLSIllegalParameterException: for result in self._sendError(AlertDescription.illegal_parameter): yield result except TLSDecryptionFailed: for result in self._sendError( AlertDescription.decryption_failed, "Encrypted data not a multiple of blocksize"): yield result except TLSBadRecordMAC: for result in self._sendError( AlertDescription.bad_record_mac, "MAC failure (or padding failure)"): yield result header, parser = result # RFC5246 section 5.2.1: Implementations MUST NOT send # zero-length fragments of content types other than Application # Data. if header.type != ContentType.application_data \ and parser.getRemainingLength() == 0: for result in self._sendError(\ AlertDescription.decode_error, \ "Received empty non-application data record"): yield result if header.type not in ContentType.all: for result in self._sendError(\ AlertDescription.unexpected_message, \ "Received record with unknown ContentType"): yield result yield (header, parser) def _handshakeStart(self, client): if not self.closed: raise ValueError("Renegotiation disallowed for security reasons") self._client = client self._handshake_hash = HandshakeHashes() self._defragmenter.clearBuffers() self.allegedSrpUsername = None self._refCount = 1 def _handshakeDone(self, resumed): self.resumed = resumed self.closed = False def _calcPendingStates(self, cipherSuite, masterSecret, clientRandom, serverRandom, implementations): self._recordLayer.calcPendingStates(cipherSuite, masterSecret, clientRandom, serverRandom, implementations) def _changeWriteState(self): self._recordLayer.changeWriteState() def _changeReadState(self): self._recordLayer.changeReadState() tlslite-ng-0.5.1/tlslite/utils/000077500000000000000000000000001261665411200164235ustar00rootroot00000000000000tlslite-ng-0.5.1/tlslite/utils/__init__.py000066400000000000000000000013551261665411200205400ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Toolkit for crypto and other stuff.""" __all__ = ["aes", "asn1parser", "cipherfactory", "codec", "cryptomath", "datefuncs", "compat", "keyfactory", "openssl_aes", "openssl_rc4", "openssl_rsakey", "openssl_tripledes", "pycrypto_aes", "pycrypto_rc4", "pycrypto_rsakey", "pycrypto_tripledes", "python_aes", "python_rc4", "python_rsakey", "rc4", "rijndael", "rsakey", "tackpywrapper", "tripledes"] tlslite-ng-0.5.1/tlslite/utils/aes.py000066400000000000000000000021051261665411200175430ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Abstract class for AES.""" class AES(object): def __init__(self, key, mode, IV, implementation): if len(key) not in (16, 24, 32): raise AssertionError() if mode != 2: raise AssertionError() if len(IV) != 16: raise AssertionError() self.isBlockCipher = True self.isAEAD = False self.block_size = 16 self.implementation = implementation if len(key)==16: self.name = "aes128" elif len(key)==24: self.name = "aes192" elif len(key)==32: self.name = "aes256" else: raise AssertionError() #CBC-Mode encryption, returns ciphertext #WARNING: *MAY* modify the input as well def encrypt(self, plaintext): assert(len(plaintext) % 16 == 0) #CBC-Mode decryption, returns plaintext #WARNING: *MAY* modify the input as well def decrypt(self, ciphertext): assert(len(ciphertext) % 16 == 0) tlslite-ng-0.5.1/tlslite/utils/aesgcm.py000066400000000000000000000156041261665411200202420ustar00rootroot00000000000000# Author: Google # See the LICENSE file for legal information regarding use of this file. # GCM derived from Go's implementation in crypto/cipher. # # https://golang.org/src/crypto/cipher/gcm.go # GCM works over elements of the field GF(2^128), each of which is a 128-bit # polynomial. Throughout this implementation, polynomials are represented as # Python integers with the low-order terms at the most significant bits. So a # 128-bit polynomial is an integer from 0 to 2^128-1 with the most significant # bit representing the x^0 term and the least significant bit representing the # x^127 term. This bit reversal also applies to polynomials used as indices in a # look-up table. from __future__ import division from .cryptomath import bytesToNumber, numberToByteArray class AESGCM(object): """ AES-GCM implementation. Note: this implementation does not attempt to be side-channel resistant. It's also rather slow. """ def __init__(self, key, implementation, rawAesEncrypt): self.isBlockCipher = False self.isAEAD = True self.nonceLength = 12 self.tagLength = 16 self.implementation = implementation if len(key) == 16: self.name = "aes128gcm" elif len(key) == 32: self.name = "aes256gcm" else: raise AssertionError() self._rawAesEncrypt = rawAesEncrypt # The GCM key is AES(0). h = bytesToNumber(self._rawAesEncrypt(bytearray(16))) # Pre-compute all 4-bit multiples of h. Note that bits are reversed # because our polynomial representation places low-order terms at the # most significant bit. Thus x^0 * h = h is at index 0b1000 = 8 and # x^1 * h is at index 0b0100 = 4. self._productTable = [0] * 16 self._productTable[self._reverseBits(1)] = h for i in range(2, 16, 2): self._productTable[self._reverseBits(i)] = \ self._gcmShift(self._productTable[self._reverseBits(i//2)]) self._productTable[self._reverseBits(i+1)] = \ self._gcmAdd(self._productTable[self._reverseBits(i)], h) def _rawAesCtrEncrypt(self, counter, inp): """ Encrypts (or decrypts) plaintext with AES-CTR. counter is modified. """ out = bytearray(len(inp)) for i in range(0, len(out), 16): mask = self._rawAesEncrypt(counter) for j in range(i, min(len(out), i + 16)): out[j] = inp[j] ^ mask[j-i] self._inc32(counter) return out def _auth(self, ciphertext, ad, tagMask): y = 0 y = self._update(y, ad) y = self._update(y, ciphertext) y ^= (len(ad) << (3 + 64)) | (len(ciphertext) << 3) y = self._mul(y) y ^= bytesToNumber(tagMask) return numberToByteArray(y, 16) def _update(self, y, data): for i in range(0, len(data) // 16): y ^= bytesToNumber(data[16*i:16*i+16]) y = self._mul(y) extra = len(data) % 16 if extra != 0: block = bytearray(16) block[:extra] = data[-extra:] y ^= bytesToNumber(block) y = self._mul(y) return y def _mul(self, y): """ Returns y*H, where H is the GCM key. """ ret = 0 # Multiply H by y 4 bits at a time, starting with the highest power # terms. for i in range(0, 128, 4): # Multiply by x^4. The reduction for the top four terms is # precomputed. retHigh = ret & 0xf ret >>= 4 ret ^= (AESGCM._gcmReductionTable[retHigh] << (128-16)) # Add in y' * H where y' are the next four terms of y, shifted down # to the x^0..x^4. This is one of the pre-computed multiples of # H. The multiplication by x^4 shifts them back into place. ret ^= self._productTable[y & 0xf] y >>= 4 assert y == 0 return ret def seal(self, nonce, plaintext, data): """ Encrypts and authenticates plaintext using nonce and data. Returns the ciphertext, consisting of the encrypted plaintext and tag concatenated. """ if len(nonce) != 12: raise ValueError("Bad nonce length") # The initial counter value is the nonce, followed by a 32-bit counter # that starts at 1. It's used to compute the tag mask. counter = bytearray(16) counter[:12] = nonce counter[-1] = 1 tagMask = self._rawAesEncrypt(counter) # The counter starts at 2 for the actual encryption. counter[-1] = 2 ciphertext = self._rawAesCtrEncrypt(counter, plaintext) tag = self._auth(ciphertext, data, tagMask) return ciphertext + tag def open(self, nonce, ciphertext, data): """ Decrypts and authenticates ciphertext using nonce and data. If the tag is valid, the plaintext is returned. If the tag is invalid, returns None. """ if len(nonce) != 12: raise ValueError("Bad nonce length") if len(ciphertext) < 16: return None tag = ciphertext[-16:] ciphertext = ciphertext[:-16] # The initial counter value is the nonce, followed by a 32-bit counter # that starts at 1. It's used to compute the tag mask. counter = bytearray(16) counter[:12] = nonce counter[-1] = 1 tagMask = self._rawAesEncrypt(counter) if tag != self._auth(ciphertext, data, tagMask): return None # The counter starts at 2 for the actual decryption. counter[-1] = 2 return self._rawAesCtrEncrypt(counter, ciphertext) @staticmethod def _reverseBits(i): assert i < 16 i = ((i << 2) & 0xc) | ((i >> 2) & 0x3) i = ((i << 1) & 0xa) | ((i >> 1) & 0x5) return i @staticmethod def _gcmAdd(x, y): return x ^ y @staticmethod def _gcmShift(x): # Multiplying by x is a right shift, due to bit order. highTermSet = x & 1 x >>= 1 if highTermSet: # The x^127 term was shifted up to x^128, so subtract a 1+x+x^2+x^7 # term. This is 0b11100001 or 0xe1 when represented as an 8-bit # polynomial. x ^= 0xe1 << (128-8) return x @staticmethod def _inc32(counter): for i in range(len(counter)-1, len(counter)-5, -1): counter[i] = (counter[i] + 1) % 256 if counter[i] != 0: break return counter # _gcmReductionTable[i] is i * (1+x+x^2+x^7) for all 4-bit polynomials i. The # result is stored as a 16-bit polynomial. This is used in the reduction step to # multiply elements of GF(2^128) by x^4. _gcmReductionTable = [ 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0, 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0, ] tlslite-ng-0.5.1/tlslite/utils/asn1parser.py000066400000000000000000000022471261665411200210610ustar00rootroot00000000000000# Author: Trevor Perrin # Patch from Google adding getChildBytes() # # See the LICENSE file for legal information regarding use of this file. """Class for parsing ASN.1""" from .compat import * from .codec import * #Takes a byte array which has a DER TLV field at its head class ASN1Parser(object): def __init__(self, bytes): p = Parser(bytes) p.get(1) #skip Type #Get Length self.length = self._getASN1Length(p) #Get Value self.value = p.getFixBytes(self.length) #Assuming this is a sequence... def getChild(self, which): return ASN1Parser(self.getChildBytes(which)) def getChildBytes(self, which): p = Parser(self.value) for x in range(which+1): markIndex = p.index p.get(1) #skip Type length = self._getASN1Length(p) p.getFixBytes(length) return p.bytes[markIndex : p.index] #Decode the ASN.1 DER length field def _getASN1Length(self, p): firstLength = p.get(1) if firstLength<=127: return firstLength else: lengthLength = firstLength & 0x7F return p.get(lengthLength) tlslite-ng-0.5.1/tlslite/utils/cipherfactory.py000066400000000000000000000067271261665411200216530ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Factory functions for symmetric cryptography.""" import os from tlslite.utils import python_aes from tlslite.utils import python_aesgcm from tlslite.utils import python_rc4 from tlslite.utils import cryptomath tripleDESPresent = False if cryptomath.m2cryptoLoaded: from tlslite.utils import openssl_aes from tlslite.utils import openssl_rc4 from tlslite.utils import openssl_tripledes tripleDESPresent = True if cryptomath.pycryptoLoaded: from tlslite.utils import pycrypto_aes from tlslite.utils import pycrypto_aesgcm from tlslite.utils import pycrypto_rc4 from tlslite.utils import pycrypto_tripledes tripleDESPresent = True # ************************************************************************** # Factory Functions for AES # ************************************************************************** def createAES(key, IV, implList=None): """Create a new AES object. @type key: str @param key: A 16, 24, or 32 byte string. @type IV: str @param IV: A 16 byte string @rtype: L{tlslite.utils.AES} @return: An AES object. """ if implList is None: implList = ["openssl", "pycrypto", "python"] for impl in implList: if impl == "openssl" and cryptomath.m2cryptoLoaded: return openssl_aes.new(key, 2, IV) elif impl == "pycrypto" and cryptomath.pycryptoLoaded: return pycrypto_aes.new(key, 2, IV) elif impl == "python": return python_aes.new(key, 2, IV) raise NotImplementedError() def createAESGCM(key, implList=None): """Create a new AESGCM object. @type key: bytearray @param key: A 16 or 32 byte byte array. @rtype: L{tlslite.utils.AESGCM} @return: An AESGCM object. """ if implList is None: implList = ["pycrypto", "python"] for impl in implList: if impl == "pycrypto" and cryptomath.pycryptoLoaded: return pycrypto_aesgcm.new(key) if impl == "python": return python_aesgcm.new(key) raise NotImplementedError() def createRC4(key, IV, implList=None): """Create a new RC4 object. @type key: str @param key: A 16 to 32 byte string. @type IV: object @param IV: Ignored, whatever it is. @rtype: L{tlslite.utils.RC4} @return: An RC4 object. """ if implList is None: implList = ["openssl", "pycrypto", "python"] if len(IV) != 0: raise AssertionError() for impl in implList: if impl == "openssl" and cryptomath.m2cryptoLoaded: return openssl_rc4.new(key) elif impl == "pycrypto" and cryptomath.pycryptoLoaded: return pycrypto_rc4.new(key) elif impl == "python": return python_rc4.new(key) raise NotImplementedError() #Create a new TripleDES instance def createTripleDES(key, IV, implList=None): """Create a new 3DES object. @type key: str @param key: A 24 byte string. @type IV: str @param IV: An 8 byte string @rtype: L{tlslite.utils.TripleDES} @return: A 3DES object. """ if implList is None: implList = ["openssl", "pycrypto"] for impl in implList: if impl == "openssl" and cryptomath.m2cryptoLoaded: return openssl_tripledes.new(key, 2, IV) elif impl == "pycrypto" and cryptomath.pycryptoLoaded: return pycrypto_tripledes.new(key, 2, IV) raise NotImplementedError() tlslite-ng-0.5.1/tlslite/utils/codec.py000066400000000000000000000106321261665411200200540ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Classes for reading/writing binary data (such as TLS records).""" from .compat import * class Writer(object): def __init__(self): self.bytes = bytearray(0) def add(self, x, length): self.bytes += bytearray(length) newIndex = len(self.bytes) - 1 for count in range(length): self.bytes[newIndex] = x & 0xFF x >>= 8 newIndex -= 1 if x != 0: raise ValueError("Can't represent value in specified length") def addFixSeq(self, seq, length): for e in seq: self.add(e, length) def addVarSeq(self, seq, length, lengthLength): self.add(len(seq)*length, lengthLength) for e in seq: self.add(e, length) def addVarTupleSeq(self, seq, length, lengthLength): """ Add a variable length list of same-sized element tuples. Note that all tuples must have the same size. Inverse of Parser.getVarTupleList() @type seq: enumerable @param seq: list of tuples @type length: int @param length: length of single element in tuple @type lengthLength: int @param lengthLength: length in bytes of overall length field """ if len(seq) == 0: self.add(0, lengthLength) else: tupleSize = len(seq[0]) tupleLength = tupleSize*length self.add(len(seq)*tupleLength, lengthLength) for elemTuple in seq: if len(elemTuple) != tupleSize: raise ValueError("Tuples of different sizes") for elem in elemTuple: self.add(elem, length) class Parser(object): def __init__(self, bytes): self.bytes = bytes self.index = 0 def get(self, length): if self.index + length > len(self.bytes): raise SyntaxError() x = 0 for count in range(length): x <<= 8 x |= self.bytes[self.index] self.index += 1 return x def getFixBytes(self, lengthBytes): if self.index + lengthBytes > len(self.bytes): raise SyntaxError() bytes = self.bytes[self.index : self.index+lengthBytes] self.index += lengthBytes return bytes def getVarBytes(self, lengthLength): lengthBytes = self.get(lengthLength) return self.getFixBytes(lengthBytes) def getFixList(self, length, lengthList): l = [0] * lengthList for x in range(lengthList): l[x] = self.get(length) return l def getVarList(self, length, lengthLength): lengthList = self.get(lengthLength) if lengthList % length != 0: raise SyntaxError() lengthList = lengthList // length l = [0] * lengthList for x in range(lengthList): l[x] = self.get(length) return l def getVarTupleList(self, elemLength, elemNum, lengthLength): """Read a variable length list of same sized tuples @param elemLength: length in bytes of single tuple element @param elemNum: number of elements in tuple @param lengthLength: length in bytes of the list length variable """ lengthList = self.get(lengthLength) if lengthList % (elemLength * elemNum) != 0: raise SyntaxError() tupleCount = lengthList // (elemLength * elemNum) tupleList = [] for _ in range(tupleCount): currentTuple = [] for _ in range(elemNum): currentTuple.append(self.get(elemLength)) tupleList.append(tuple(currentTuple)) return tupleList def startLengthCheck(self, lengthLength): self.lengthCheck = self.get(lengthLength) self.indexCheck = self.index def setLengthCheck(self, length): self.lengthCheck = length self.indexCheck = self.index def stopLengthCheck(self): if (self.index - self.indexCheck) != self.lengthCheck: raise SyntaxError() def atLengthCheck(self): if (self.index - self.indexCheck) < self.lengthCheck: return False elif (self.index - self.indexCheck) == self.lengthCheck: return True else: raise SyntaxError() def getRemainingLength(self): return len(self.bytes) - self.index tlslite-ng-0.5.1/tlslite/utils/compat.py000066400000000000000000000052071261665411200202640ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Miscellaneous functions to mask Python version differences.""" import sys import os import math import binascii if sys.version_info >= (3,0): def compat26Str(x): return x # Python 3 requires bytes instead of bytearrays for HMAC # So, python 2.6 requires strings, python 3 requires 'bytes', # and python 2.7 can handle bytearrays... def compatHMAC(x): return bytes(x) def raw_input(s): return input(s) # So, the python3 binascii module deals with bytearrays, and python2 # deals with strings... I would rather deal with the "a" part as # strings, and the "b" part as bytearrays, regardless of python version, # so... def a2b_hex(s): try: b = bytearray(binascii.a2b_hex(bytearray(s, "ascii"))) except Exception as e: raise SyntaxError("base16 error: %s" % e) return b def a2b_base64(s): try: b = bytearray(binascii.a2b_base64(bytearray(s, "ascii"))) except Exception as e: raise SyntaxError("base64 error: %s" % e) return b def b2a_hex(b): return binascii.b2a_hex(b).decode("ascii") def b2a_base64(b): return binascii.b2a_base64(b).decode("ascii") def readStdinBinary(): return sys.stdin.buffer.read() def compatLong(num): return int(num) else: # Python 2.6 requires strings instead of bytearrays in a couple places, # so we define this function so it does the conversion if needed. if sys.version_info < (2,7): def compat26Str(x): return str(x) else: def compat26Str(x): return x # So, python 2.6 requires strings, python 3 requires 'bytes', # and python 2.7 can handle bytearrays... def compatHMAC(x): return compat26Str(x) def a2b_hex(s): try: b = bytearray(binascii.a2b_hex(s)) except Exception as e: raise SyntaxError("base16 error: %s" % e) return b def a2b_base64(s): try: b = bytearray(binascii.a2b_base64(s)) except Exception as e: raise SyntaxError("base64 error: %s" % e) return b def b2a_hex(b): return binascii.b2a_hex(compat26Str(b)) def b2a_base64(b): return binascii.b2a_base64(compat26Str(b)) def compatLong(num): return long(num) import traceback def formatExceptionTrace(e): newStr = "".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) return newStr tlslite-ng-0.5.1/tlslite/utils/constanttime.py000066400000000000000000000137471261665411200215210ustar00rootroot00000000000000# Copyright (c) 2015, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. """Various constant time functions for processing sensitive data""" from __future__ import division from .compat import compatHMAC import hmac def ct_lt_u32(val_a, val_b): """ Returns 1 if val_a < val_b, 0 otherwise. Constant time. @type val_a: int @type val_b: int @param val_a: an unsigned integer representable as a 32 bit value @param val_b: an unsigned integer representable as a 32 bit value @rtype: int """ val_a &= 0xffffffff val_b &= 0xffffffff return (val_a^((val_a^val_b)|(((val_a-val_b)&0xffffffff)^val_b)))>>31 def ct_gt_u32(val_a, val_b): """ Return 1 if val_a > val_b, 0 otherwise. Constant time. @type val_a: int @type val_b: int @param val_a: an unsigned integer representable as a 32 bit value @param val_b: an unsigned integer representable as a 32 bit value @rtype: int """ return ct_lt_u32(val_b, val_a) def ct_le_u32(val_a, val_b): """ Return 1 if val_a <= val_b, 0 otherwise. Constant time. @type val_a: int @type val_b: int @param val_a: an unsigned integer representable as a 32 bit value @param val_b: an unsigned integer representable as a 32 bit value @rtype: int """ return 1 ^ ct_gt_u32(val_a, val_b) def ct_lsb_prop_u8(val): """Propagate LSB to all 8 bits of the returned byte. Constant time.""" val &= 0x01 val |= val << 1 val |= val << 2 val |= val << 4 return val def ct_isnonzero_u32(val): """ Returns 1 if val is != 0, 0 otherwise. Constant time. @type val: int @param val: an unsigned integer representable as a 32 bit value @rtype: int """ val &= 0xffffffff return (val|(-val&0xffffffff)) >> 31 def ct_neq_u32(val_a, val_b): """ Return 1 if val_a != val_b, 0 otherwise. Constant time. @type val_a: int @type val_b: int @param val_a: an unsigned integer representable as a 32 bit value @param val_b: an unsigned integer representable as a 32 bit value @rtype: int """ val_a &= 0xffffffff val_b &= 0xffffffff return (((val_a-val_b)&0xffffffff) | ((val_b-val_a)&0xffffffff)) >> 31 def ct_eq_u32(val_a, val_b): """ Return 1 if val_a == val_b, 0 otherwise. Constant time. @type val_a: int @type val_b: int @param val_a: an unsigned integer representable as a 32 bit value @param val_b: an unsigned integer representable as a 32 bit value @rtype: int """ return 1 ^ ct_neq_u32(val_a, val_b) def ct_check_cbc_mac_and_pad(data, mac, seqnumBytes, contentType, version): """ Check CBC cipher HMAC and padding. Close to constant time. @type data: bytearray @param data: data with HMAC value to test and padding @type mac: hashlib mac @param mac: empty HMAC, initialised with a key @type seqnumBytes: bytearray @param seqnumBytes: TLS sequence number, used as input to HMAC @type contentType: int @param contentType: a single byte, used as input to HMAC @type version: tuple of int @param version: a tuple of two ints, used as input to HMAC and to guide checking of padding @rtype: boolean @return: True if MAC and pad is ok, False otherwise """ assert version in ((3, 0), (3, 1), (3, 2), (3, 3)) data_len = len(data) if mac.digest_size + 1 > data_len: # data_len is public return False # 0 - OK result = 0x00 # # check padding # pad_length = data[data_len-1] pad_start = data_len - pad_length - 1 pad_start = max(0, pad_start) if version == (3, 0): # version is public # in SSLv3 we can only check if pad is not longer than overall length # subtract 1 for the pad length byte mask = ct_lsb_prop_u8(ct_lt_u32(data_len-1, pad_length)) result |= mask else: start_pos = max(0, data_len - 256) for i in range(start_pos, data_len): # if pad_start < i: mask = 0xff; else: mask = 0x00 mask = ct_lsb_prop_u8(ct_le_u32(pad_start, i)) # if data[i] != pad_length and "inside_pad": result = False result |= (data[i] ^ pad_length) & mask # # check MAC # # real place where mac starts and data ends mac_start = pad_start - mac.digest_size mac_start = max(0, mac_start) # place to start processing start_pos = max(0, data_len - (256 + mac.digest_size)) // mac.block_size start_pos *= mac.block_size # add start data data_mac = mac.copy() data_mac.update(compatHMAC(seqnumBytes)) data_mac.update(compatHMAC(bytearray([contentType]))) if version != (3, 0): # version is public data_mac.update(compatHMAC(bytearray([version[0]]))) data_mac.update(compatHMAC(bytearray([version[1]]))) data_mac.update(compatHMAC(bytearray([mac_start >> 8]))) data_mac.update(compatHMAC(bytearray([mac_start & 0xff]))) data_mac.update(compatHMAC(data[:start_pos])) # don't check past the array end (already checked to be >= zero) end_pos = data_len - 1 - mac.digest_size # calculate all possible for i in range(start_pos, end_pos): # constant for given overall length cur_mac = data_mac.copy() cur_mac.update(compatHMAC(data[start_pos:i])) mac_compare = bytearray(cur_mac.digest()) # compare the hash for real only if it's the place where mac is # supposed to be mask = ct_lsb_prop_u8(ct_eq_u32(i, mac_start)) for j in range(0, mac.digest_size): # digest_size is public result |= (data[i+j] ^ mac_compare[j]) & mask # return python boolean return result == 0 if hasattr(hmac, 'compare_digest'): ct_compare_digest = hmac.compare_digest else: def ct_compare_digest(val_a, val_b): """Compares if string like objects are equal. Constant time.""" if len(val_a) != len(val_b): return False result = 0 for x, y in zip(val_a, val_b): result |= x ^ y return result == 0 tlslite-ng-0.5.1/tlslite/utils/cryptomath.py000066400000000000000000000204651261665411200211760ustar00rootroot00000000000000# Authors: # Trevor Perrin # Martin von Loewis - python 3 port # Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2 # # See the LICENSE file for legal information regarding use of this file. """cryptomath module This module has basic math/crypto code.""" from __future__ import print_function import os import math import base64 import binascii from .compat import * # ************************************************************************** # Load Optional Modules # ************************************************************************** # Try to load M2Crypto/OpenSSL try: from M2Crypto import m2 m2cryptoLoaded = True except ImportError: m2cryptoLoaded = False #Try to load GMPY try: import gmpy gmpyLoaded = True except ImportError: gmpyLoaded = False #Try to load pycrypto try: import Crypto.Cipher.AES pycryptoLoaded = True except ImportError: pycryptoLoaded = False # ************************************************************************** # PRNG Functions # ************************************************************************** # Check that os.urandom works import zlib length = len(zlib.compress(os.urandom(1000))) assert(length > 900) del length def getRandomBytes(howMany): b = bytearray(os.urandom(howMany)) assert(len(b) == howMany) return b prngName = "os.urandom" # ************************************************************************** # Simple hash functions # ************************************************************************** import hmac import hashlib def MD5(b): return bytearray(hashlib.md5(compat26Str(b)).digest()) def SHA1(b): return bytearray(hashlib.sha1(compat26Str(b)).digest()) def HMAC_MD5(k, b): k = compatHMAC(k) b = compatHMAC(b) return bytearray(hmac.new(k, b, hashlib.md5).digest()) def HMAC_SHA1(k, b): k = compatHMAC(k) b = compatHMAC(b) return bytearray(hmac.new(k, b, hashlib.sha1).digest()) def HMAC_SHA256(k, b): k = compatHMAC(k) b = compatHMAC(b) return bytearray(hmac.new(k, b, hashlib.sha256).digest()) def HMAC_SHA384(k, b): k = compatHMAC(k) b = compatHMAC(b) return bytearray(hmac.new(k, b, hashlib.sha384).digest()) # ************************************************************************** # Converter Functions # ************************************************************************** def bytesToNumber(b): total = 0 multiplier = 1 for count in range(len(b)-1, -1, -1): byte = b[count] total += multiplier * byte multiplier *= 256 return total def numberToByteArray(n, howManyBytes=None): """Convert an integer into a bytearray, zero-pad to howManyBytes. The returned bytearray may be smaller than howManyBytes, but will not be larger. The returned bytearray will contain a big-endian encoding of the input integer (n). """ if howManyBytes == None: howManyBytes = numBytes(n) b = bytearray(howManyBytes) for count in range(howManyBytes-1, -1, -1): b[count] = int(n % 256) n >>= 8 return b def mpiToNumber(mpi): #mpi is an openssl-format bignum string if (ord(mpi[4]) & 0x80) !=0: #Make sure this is a positive number raise AssertionError() b = bytearray(mpi[4:]) return bytesToNumber(b) def numberToMPI(n): b = numberToByteArray(n) ext = 0 #If the high-order bit is going to be set, #add an extra byte of zeros if (numBits(n) & 0x7)==0: ext = 1 length = numBytes(n) + ext b = bytearray(4+ext) + b b[0] = (length >> 24) & 0xFF b[1] = (length >> 16) & 0xFF b[2] = (length >> 8) & 0xFF b[3] = length & 0xFF return bytes(b) # ************************************************************************** # Misc. Utility Functions # ************************************************************************** def numBits(n): if n==0: return 0 s = "%x" % n return ((len(s)-1)*4) + \ {'0':0, '1':1, '2':2, '3':2, '4':3, '5':3, '6':3, '7':3, '8':4, '9':4, 'a':4, 'b':4, 'c':4, 'd':4, 'e':4, 'f':4, }[s[0]] return int(math.floor(math.log(n, 2))+1) def numBytes(n): if n==0: return 0 bits = numBits(n) return int(math.ceil(bits / 8.0)) # ************************************************************************** # Big Number Math # ************************************************************************** def getRandomNumber(low, high): if low >= high: raise AssertionError() howManyBits = numBits(high) howManyBytes = numBytes(high) lastBits = howManyBits % 8 while 1: bytes = getRandomBytes(howManyBytes) if lastBits: bytes[0] = bytes[0] % (1 << lastBits) n = bytesToNumber(bytes) if n >= low and n < high: return n def gcd(a,b): a, b = max(a,b), min(a,b) while b: a, b = b, a % b return a def lcm(a, b): return (a * b) // gcd(a, b) #Returns inverse of a mod b, zero if none #Uses Extended Euclidean Algorithm def invMod(a, b): c, d = a, b uc, ud = 1, 0 while c != 0: q = d // c c, d = d-(q*c), c uc, ud = ud - (q * uc), uc if d == 1: return ud % b return 0 if gmpyLoaded: def powMod(base, power, modulus): base = gmpy.mpz(base) power = gmpy.mpz(power) modulus = gmpy.mpz(modulus) result = pow(base, power, modulus) return compatLong(result) else: def powMod(base, power, modulus): if power < 0: result = pow(base, power*-1, modulus) result = invMod(result, modulus) return result else: return pow(base, power, modulus) #Pre-calculate a sieve of the ~100 primes < 1000: def makeSieve(n): sieve = list(range(n)) for count in range(2, int(math.sqrt(n))+1): if sieve[count] == 0: continue x = sieve[count] * 2 while x < len(sieve): sieve[x] = 0 x += sieve[count] sieve = [x for x in sieve[2:] if x] return sieve def isPrime(n, iterations=5, display=False, sieve=makeSieve(1000)): #Trial division with sieve for x in sieve: if x >= n: return True if n % x == 0: return False #Passed trial division, proceed to Rabin-Miller #Rabin-Miller implemented per Ferguson & Schneier #Compute s, t for Rabin-Miller if display: print("*", end=' ') s, t = n-1, 0 while s % 2 == 0: s, t = s//2, t+1 #Repeat Rabin-Miller x times a = 2 #Use 2 as a base for first iteration speedup, per HAC for count in range(iterations): v = powMod(a, s, n) if v==1: continue i = 0 while v != n-1: if i == t-1: return False else: v, i = powMod(v, 2, n), i+1 a = getRandomNumber(2, n) return True def getRandomPrime(bits, display=False): if bits < 10: raise AssertionError() #The 1.5 ensures the 2 MSBs are set #Thus, when used for p,q in RSA, n will have its MSB set # #Since 30 is lcm(2,3,5), we'll set our test numbers to #29 % 30 and keep them there low = ((2 ** (bits-1)) * 3) // 2 high = 2 ** bits - 30 p = getRandomNumber(low, high) p += 29 - (p % 30) while 1: if display: print(".", end=' ') p += 30 if p >= high: p = getRandomNumber(low, high) p += 29 - (p % 30) if isPrime(p, display=display): return p #Unused at the moment... def getRandomSafePrime(bits, display=False): if bits < 10: raise AssertionError() #The 1.5 ensures the 2 MSBs are set #Thus, when used for p,q in RSA, n will have its MSB set # #Since 30 is lcm(2,3,5), we'll set our test numbers to #29 % 30 and keep them there low = (2 ** (bits-2)) * 3//2 high = (2 ** (bits-1)) - 30 q = getRandomNumber(low, high) q += 29 - (q % 30) while 1: if display: print(".", end=' ') q += 30 if (q >= high): q = getRandomNumber(low, high) q += 29 - (q % 30) #Ideas from Tom Wu's SRP code #Do trial division on p and q before Rabin-Miller if isPrime(q, 0, display=display): p = (2 * q) + 1 if isPrime(p, display=display): if isPrime(q, display=display): return p tlslite-ng-0.5.1/tlslite/utils/datefuncs.py000066400000000000000000000043461261665411200207600ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. import os #Functions for manipulating datetime objects #CCYY-MM-DDThh:mm:ssZ def parseDateClass(s): year, month, day = s.split("-") day, tail = day[:2], day[2:] hour, minute, second = tail[1:].split(":") second = second[:2] year, month, day = int(year), int(month), int(day) hour, minute, second = int(hour), int(minute), int(second) return createDateClass(year, month, day, hour, minute, second) if os.name != "java": from datetime import datetime, timedelta #Helper functions for working with a date/time class def createDateClass(year, month, day, hour, minute, second): return datetime(year, month, day, hour, minute, second) def printDateClass(d): #Split off fractional seconds, append 'Z' return d.isoformat().split(".")[0]+"Z" def getNow(): return datetime.utcnow() def getHoursFromNow(hours): return datetime.utcnow() + timedelta(hours=hours) def getMinutesFromNow(minutes): return datetime.utcnow() + timedelta(minutes=minutes) def isDateClassExpired(d): return d < datetime.utcnow() def isDateClassBefore(d1, d2): return d1 < d2 else: #Jython 2.1 is missing lots of python 2.3 stuff, #which we have to emulate here: import java import jarray def createDateClass(year, month, day, hour, minute, second): c = java.util.Calendar.getInstance() c.setTimeZone(java.util.TimeZone.getTimeZone("UTC")) c.set(year, month-1, day, hour, minute, second) return c def printDateClass(d): return "%04d-%02d-%02dT%02d:%02d:%02dZ" % \ (d.get(d.YEAR), d.get(d.MONTH)+1, d.get(d.DATE), \ d.get(d.HOUR_OF_DAY), d.get(d.MINUTE), d.get(d.SECOND)) def getNow(): c = java.util.Calendar.getInstance() c.setTimeZone(java.util.TimeZone.getTimeZone("UTC")) c.get(c.HOUR) #force refresh? return c def getHoursFromNow(hours): d = getNow() d.add(d.HOUR, hours) return d def isDateClassExpired(d): n = getNow() return d.before(n) def isDateClassBefore(d1, d2): return d1.before(d2) tlslite-ng-0.5.1/tlslite/utils/keyfactory.py000066400000000000000000000155131261665411200211620ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Factory functions for asymmetric cryptography. @sort: generateRSAKey, parsePEMKey, parseAsPublicKey """ from .compat import * from .rsakey import RSAKey from .python_rsakey import Python_RSAKey from tlslite.utils import cryptomath if cryptomath.m2cryptoLoaded: from .openssl_rsakey import OpenSSL_RSAKey if cryptomath.pycryptoLoaded: from .pycrypto_rsakey import PyCrypto_RSAKey # ************************************************************************** # Factory Functions for RSA Keys # ************************************************************************** def generateRSAKey(bits, implementations=["openssl", "python"]): """Generate an RSA key with the specified bit length. @type bits: int @param bits: Desired bit length of the new key's modulus. @rtype: L{tlslite.utils.rsakey.RSAKey} @return: A new RSA private key. """ for implementation in implementations: if implementation == "openssl" and cryptomath.m2cryptoLoaded: return OpenSSL_RSAKey.generate(bits) elif implementation == "python": return Python_RSAKey.generate(bits) raise ValueError("No acceptable implementations") #Parse as an OpenSSL or Python key def parsePEMKey(s, private=False, public=False, passwordCallback=None, implementations=["openssl", "python"]): """Parse a PEM-format key. The PEM format is used by OpenSSL and other tools. The format is typically used to store both the public and private components of a key. For example:: -----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDYscuoMzsGmW0pAYsmyHltxB2TdwHS0dImfjCMfaSDkfLdZY5+ dOWORVns9etWnr194mSGA1F0Pls/VJW8+cX9+3vtJV8zSdANPYUoQf0TP7VlJxkH dSRkUbEoz5bAAs/+970uos7n7iXQIni+3erUTdYEk2iWnMBjTljfgbK/dQIDAQAB AoGAJHoJZk75aKr7DSQNYIHuruOMdv5ZeDuJvKERWxTrVJqE32/xBKh42/IgqRrc esBN9ZregRCd7YtxoL+EVUNWaJNVx2mNmezEznrc9zhcYUrgeaVdFO2yBF1889zO gCOVwrO8uDgeyj6IKa25H6c1N13ih/o7ZzEgWbGG+ylU1yECQQDv4ZSJ4EjSh/Fl aHdz3wbBa/HKGTjC8iRy476Cyg2Fm8MZUe9Yy3udOrb5ZnS2MTpIXt5AF3h2TfYV VoFXIorjAkEA50FcJmzT8sNMrPaV8vn+9W2Lu4U7C+K/O2g1iXMaZms5PC5zV5aV CKXZWUX1fq2RaOzlbQrpgiolhXpeh8FjxwJBAOFHzSQfSsTNfttp3KUpU0LbiVvv i+spVSnA0O4rq79KpVNmK44Mq67hsW1P11QzrzTAQ6GVaUBRv0YS061td1kCQHnP wtN2tboFR6lABkJDjxoGRvlSt4SOPr7zKGgrWjeiuTZLHXSAnCY+/hr5L9Q3ZwXG 6x6iBdgLjVIe4BZQNtcCQQDXGv/gWinCNTN3MPWfTW/RGzuMYVmyBFais0/VrgdH h1dLpztmpQqfyH/zrBXQ9qL/zR4ojS6XYneO/U18WpEe -----END RSA PRIVATE KEY----- To generate a key like this with OpenSSL, run:: openssl genrsa 2048 > key.pem This format also supports password-encrypted private keys. TLS Lite can only handle password-encrypted private keys when OpenSSL and M2Crypto are installed. In this case, passwordCallback will be invoked to query the user for the password. @type s: str @param s: A string containing a PEM-encoded public or private key. @type private: bool @param private: If True, a L{SyntaxError} will be raised if the private key component is not present. @type public: bool @param public: If True, the private key component (if present) will be discarded, so this function will always return a public key. @type passwordCallback: callable @param passwordCallback: This function will be called, with no arguments, if the PEM-encoded private key is password-encrypted. The callback should return the password string. If the password is incorrect, SyntaxError will be raised. If no callback is passed and the key is password-encrypted, a prompt will be displayed at the console. @rtype: L{tlslite.utils.RSAKey.RSAKey} @return: An RSA key. @raise SyntaxError: If the key is not properly formatted. """ for implementation in implementations: if implementation == "openssl" and cryptomath.m2cryptoLoaded: key = OpenSSL_RSAKey.parse(s, passwordCallback) break elif implementation == "python": key = Python_RSAKey.parsePEM(s) break else: raise ValueError("No acceptable implementations") return _parseKeyHelper(key, private, public) def _parseKeyHelper(key, private, public): if private: if not key.hasPrivateKey(): raise SyntaxError("Not a private key!") if public: return _createPublicKey(key) if private: if hasattr(key, "d"): return _createPrivateKey(key) else: return key return key def parseAsPublicKey(s): """Parse a PEM-formatted public key. @type s: str @param s: A string containing a PEM-encoded public or private key. @rtype: L{tlslite.utils.rsakey.RSAKey} @return: An RSA public key. @raise SyntaxError: If the key is not properly formatted. """ return parsePEMKey(s, public=True) def parsePrivateKey(s): """Parse a PEM-formatted private key. @type s: str @param s: A string containing a PEM-encoded private key. @rtype: L{tlslite.utils.rsakey.RSAKey} @return: An RSA private key. @raise SyntaxError: If the key is not properly formatted. """ return parsePEMKey(s, private=True) def _createPublicKey(key): """ Create a new public key. Discard any private component, and return the most efficient key possible. """ if not isinstance(key, RSAKey): raise AssertionError() return _createPublicRSAKey(key.n, key.e) def _createPrivateKey(key): """ Create a new private key. Return the most efficient key possible. """ if not isinstance(key, RSAKey): raise AssertionError() if not key.hasPrivateKey(): raise AssertionError() return _createPrivateRSAKey(key.n, key.e, key.d, key.p, key.q, key.dP, key.dQ, key.qInv) def _createPublicRSAKey(n, e, implementations = ["openssl", "pycrypto", "python"]): for implementation in implementations: if implementation == "openssl" and cryptomath.m2cryptoLoaded: return OpenSSL_RSAKey(n, e) elif implementation == "pycrypto" and cryptomath.pycryptoLoaded: return PyCrypto_RSAKey(n, e) elif implementation == "python": return Python_RSAKey(n, e) raise ValueError("No acceptable implementations") def _createPrivateRSAKey(n, e, d, p, q, dP, dQ, qInv, implementations = ["pycrypto", "python"]): for implementation in implementations: if implementation == "pycrypto" and cryptomath.pycryptoLoaded: return PyCrypto_RSAKey(n, e, d, p, q, dP, dQ, qInv) elif implementation == "python": return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv) raise ValueError("No acceptable implementations") tlslite-ng-0.5.1/tlslite/utils/openssl_aes.py000066400000000000000000000036301261665411200213120ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """OpenSSL/M2Crypto AES implementation.""" from .cryptomath import * from .aes import * if m2cryptoLoaded: def new(key, mode, IV): return OpenSSL_AES(key, mode, IV) class OpenSSL_AES(AES): def __init__(self, key, mode, IV): AES.__init__(self, key, mode, IV, "openssl") self.key = key self.IV = IV def _createContext(self, encrypt): context = m2.cipher_ctx_new() if len(self.key)==16: cipherType = m2.aes_128_cbc() if len(self.key)==24: cipherType = m2.aes_192_cbc() if len(self.key)==32: cipherType = m2.aes_256_cbc() m2.cipher_init(context, cipherType, self.key, self.IV, encrypt) return context def encrypt(self, plaintext): AES.encrypt(self, plaintext) context = self._createContext(1) ciphertext = m2.cipher_update(context, plaintext) m2.cipher_ctx_free(context) self.IV = ciphertext[-self.block_size:] return bytearray(ciphertext) def decrypt(self, ciphertext): AES.decrypt(self, ciphertext) context = self._createContext(0) #I think M2Crypto has a bug - it fails to decrypt and return the last block passed in. #To work around this, we append sixteen zeros to the string, below: plaintext = m2.cipher_update(context, ciphertext+('\0'*16)) #If this bug is ever fixed, then plaintext will end up having a garbage #plaintext block on the end. That's okay - the below code will discard it. plaintext = plaintext[:len(ciphertext)] m2.cipher_ctx_free(context) self.IV = ciphertext[-self.block_size:] return bytearray(plaintext) tlslite-ng-0.5.1/tlslite/utils/openssl_rc4.py000066400000000000000000000013051261665411200212270ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """OpenSSL/M2Crypto RC4 implementation.""" from .cryptomath import * from .rc4 import RC4 if m2cryptoLoaded: def new(key): return OpenSSL_RC4(key) class OpenSSL_RC4(RC4): def __init__(self, key): RC4.__init__(self, key, "openssl") self.rc4 = m2.rc4_new() m2.rc4_set_key(self.rc4, key) def __del__(self): m2.rc4_free(self.rc4) def encrypt(self, plaintext): return bytearray(m2.rc4_update(self.rc4, plaintext)) def decrypt(self, ciphertext): return bytearray(self.encrypt(ciphertext)) tlslite-ng-0.5.1/tlslite/utils/openssl_rsakey.py000066400000000000000000000131661261665411200220450ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """OpenSSL/M2Crypto RSA implementation.""" from .cryptomath import * from .rsakey import * from .python_rsakey import Python_RSAKey #copied from M2Crypto.util.py, so when we load the local copy of m2 #we can still use it def password_callback(v, prompt1='Enter private key passphrase:', prompt2='Verify passphrase:'): from getpass import getpass while 1: try: p1=getpass(prompt1) if v: p2=getpass(prompt2) if p1==p2: break else: break except KeyboardInterrupt: return None return p1 if m2cryptoLoaded: class OpenSSL_RSAKey(RSAKey): def __init__(self, n=0, e=0): self.rsa = None self._hasPrivateKey = False if (n and not e) or (e and not n): raise AssertionError() if n and e: self.rsa = m2.rsa_new() m2.rsa_set_n(self.rsa, numberToMPI(n)) m2.rsa_set_e(self.rsa, numberToMPI(e)) def __del__(self): if self.rsa: m2.rsa_free(self.rsa) def __getattr__(self, name): if name == 'e': if not self.rsa: return 0 return mpiToNumber(m2.rsa_get_e(self.rsa)) elif name == 'n': if not self.rsa: return 0 return mpiToNumber(m2.rsa_get_n(self.rsa)) else: raise AttributeError def hasPrivateKey(self): return self._hasPrivateKey def _rawPrivateKeyOp(self, m): b = numberToByteArray(m, numBytes(self.n)) s = m2.rsa_private_encrypt(self.rsa, bytes(b), m2.no_padding) c = bytesToNumber(bytearray(s)) return c def _rawPublicKeyOp(self, c): b = numberToByteArray(c, numBytes(self.n)) s = m2.rsa_public_decrypt(self.rsa, bytes(b), m2.no_padding) m = bytesToNumber(bytearray(s)) return m def acceptsPassword(self): return True def write(self, password=None): bio = m2.bio_new(m2.bio_s_mem()) if self._hasPrivateKey: if password: def f(v): return password m2.rsa_write_key(self.rsa, bio, m2.des_ede_cbc(), f) else: def f(): pass m2.rsa_write_key_no_cipher(self.rsa, bio, f) else: if password: raise AssertionError() m2.rsa_write_pub_key(self.rsa, bio) s = m2.bio_read(bio, m2.bio_ctrl_pending(bio)) m2.bio_free(bio) return s def generate(bits): key = OpenSSL_RSAKey() def f():pass key.rsa = m2.rsa_generate_key(bits, 3, f) key._hasPrivateKey = True return key generate = staticmethod(generate) def parse(s, passwordCallback=None): # Skip forward to the first PEM header start = s.find("-----BEGIN ") if start == -1: raise SyntaxError() s = s[start:] if s.startswith("-----BEGIN "): if passwordCallback==None: callback = password_callback else: def f(v, prompt1=None, prompt2=None): return passwordCallback() callback = f bio = m2.bio_new(m2.bio_s_mem()) try: m2.bio_write(bio, s) key = OpenSSL_RSAKey() # parse SSLay format PEM file if s.startswith("-----BEGIN RSA PRIVATE KEY-----"): def f():pass key.rsa = m2.rsa_read_key(bio, callback) if key.rsa == None: raise SyntaxError() key._hasPrivateKey = True # parse a standard PKCS#8 PEM file elif s.startswith("-----BEGIN PRIVATE KEY-----"): def f():pass key.rsa = m2.pkey_read_pem(bio, callback) # the below code assumes RSA key while PKCS#8 files # (and by extension the EVP_PKEY structure) can be # also DSA or EC, thus the double check against None # (first if the file was properly loaded and second # if the file actually has a RSA key in it) # tlslite doesn't support DSA or EC so it's useless # to handle them in a different way if key.rsa == None: raise SyntaxError() key.rsa = m2.pkey_get1_rsa(key.rsa) if key.rsa == None: raise SyntaxError() key._hasPrivateKey = True elif s.startswith("-----BEGIN PUBLIC KEY-----"): key.rsa = m2.rsa_read_pub_key(bio) if key.rsa == None: raise SyntaxError() key._hasPrivateKey = False else: raise SyntaxError() return key finally: m2.bio_free(bio) else: raise SyntaxError() parse = staticmethod(parse) tlslite-ng-0.5.1/tlslite/utils/openssl_tripledes.py000066400000000000000000000033741261665411200225420ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """OpenSSL/M2Crypto 3DES implementation.""" from .cryptomath import * from .tripledes import * if m2cryptoLoaded: def new(key, mode, IV): return OpenSSL_TripleDES(key, mode, IV) class OpenSSL_TripleDES(TripleDES): def __init__(self, key, mode, IV): TripleDES.__init__(self, key, mode, IV, "openssl") self.key = key self.IV = IV def _createContext(self, encrypt): context = m2.cipher_ctx_new() cipherType = m2.des_ede3_cbc() m2.cipher_init(context, cipherType, self.key, self.IV, encrypt) return context def encrypt(self, plaintext): TripleDES.encrypt(self, plaintext) context = self._createContext(1) ciphertext = m2.cipher_update(context, plaintext) m2.cipher_ctx_free(context) self.IV = ciphertext[-self.block_size:] return bytearray(ciphertext) def decrypt(self, ciphertext): TripleDES.decrypt(self, ciphertext) context = self._createContext(0) #I think M2Crypto has a bug - it fails to decrypt and return the last block passed in. #To work around this, we append sixteen zeros to the string, below: plaintext = m2.cipher_update(context, ciphertext+('\0'*16)) #If this bug is ever fixed, then plaintext will end up having a garbage #plaintext block on the end. That's okay - the below code will ignore it. plaintext = plaintext[:len(ciphertext)] m2.cipher_ctx_free(context) self.IV = ciphertext[-self.block_size:] return bytearray(plaintext)tlslite-ng-0.5.1/tlslite/utils/pem.py000066400000000000000000000071231261665411200175610ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. from .compat import * import binascii #This code is shared with tackpy (somewhat), so I'd rather make minimal #changes, and preserve the use of a2b_base64 throughout. def dePem(s, name): """Decode a PEM string into a bytearray of its payload. The input must contain an appropriate PEM prefix and postfix based on the input name string, e.g. for name="CERTIFICATE":: -----BEGIN CERTIFICATE----- MIIBXDCCAUSgAwIBAgIBADANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDEwRUQUNL ... KoZIhvcNAQEFBQADAwA5kw== -----END CERTIFICATE----- The first such PEM block in the input will be found, and its payload will be base64 decoded and returned. """ prefix = "-----BEGIN %s-----" % name postfix = "-----END %s-----" % name start = s.find(prefix) if start == -1: raise SyntaxError("Missing PEM prefix") end = s.find(postfix, start+len(prefix)) if end == -1: raise SyntaxError("Missing PEM postfix") s = s[start+len("-----BEGIN %s-----" % name) : end] retBytes = a2b_base64(s) # May raise SyntaxError return retBytes def dePemList(s, name): """Decode a sequence of PEM blocks into a list of bytearrays. The input must contain any number of PEM blocks, each with the appropriate PEM prefix and postfix based on the input name string, e.g. for name="TACK BREAK SIG". Arbitrary text can appear between and before and after the PEM blocks. For example:: Created by TACK.py 0.9.3 Created at 2012-02-01T00:30:10Z -----BEGIN TACK BREAK SIG----- ATKhrz5C6JHJW8BF5fLVrnQss6JnWVyEaC0p89LNhKPswvcC9/s6+vWLd9snYTUv YMEBdw69PUP8JB4AdqA3K6Ap0Fgd9SSTOECeAKOUAym8zcYaXUwpk0+WuPYa7Zmm SkbOlK4ywqt+amhWbg9txSGUwFO5tWUHT3QrnRlE/e3PeNFXLx5Bckg= -----END TACK BREAK SIG----- Created by TACK.py 0.9.3 Created at 2012-02-01T00:30:11Z -----BEGIN TACK BREAK SIG----- ATKhrz5C6JHJW8BF5fLVrnQss6JnWVyEaC0p89LNhKPswvcC9/s6+vWLd9snYTUv YMEBdw69PUP8JB4AdqA3K6BVCWfcjN36lx6JwxmZQncS6sww7DecFO/qjSePCxwM +kdDqX/9/183nmjx6bf0ewhPXkA0nVXsDYZaydN8rJU1GaMlnjcIYxY= -----END TACK BREAK SIG----- All such PEM blocks will be found, decoded, and return in an ordered list of bytearrays, which may have zero elements if not PEM blocks are found. """ bList = [] prefix = "-----BEGIN %s-----" % name postfix = "-----END %s-----" % name while 1: start = s.find(prefix) if start == -1: return bList end = s.find(postfix, start+len(prefix)) if end == -1: raise SyntaxError("Missing PEM postfix") s2 = s[start+len(prefix) : end] retBytes = a2b_base64(s2) # May raise SyntaxError bList.append(retBytes) s = s[end+len(postfix) : ] def pem(b, name): """Encode a payload bytearray into a PEM string. The input will be base64 encoded, then wrapped in a PEM prefix/postfix based on the name string, e.g. for name="CERTIFICATE":: -----BEGIN CERTIFICATE----- MIIBXDCCAUSgAwIBAgIBADANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDEwRUQUNL ... KoZIhvcNAQEFBQADAwA5kw== -----END CERTIFICATE----- """ s1 = b2a_base64(b)[:-1] # remove terminating \n s2 = "" while s1: s2 += s1[:64] + "\n" s1 = s1[64:] s = ("-----BEGIN %s-----\n" % name) + s2 + \ ("-----END %s-----\n" % name) return s def pemSniff(inStr, name): searchStr = "-----BEGIN %s-----" % name return searchStr in inStr tlslite-ng-0.5.1/tlslite/utils/pycrypto_aes.py000066400000000000000000000015101261665411200215130ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """PyCrypto AES implementation.""" from .cryptomath import * from .aes import * if pycryptoLoaded: import Crypto.Cipher.AES def new(key, mode, IV): return PyCrypto_AES(key, mode, IV) class PyCrypto_AES(AES): def __init__(self, key, mode, IV): AES.__init__(self, key, mode, IV, "pycrypto") key = bytes(key) IV = bytes(IV) self.context = Crypto.Cipher.AES.new(key, mode, IV) def encrypt(self, plaintext): plaintext = bytes(plaintext) return bytearray(self.context.encrypt(plaintext)) def decrypt(self, ciphertext): ciphertext = bytes(ciphertext) return bytearray(self.context.decrypt(ciphertext)) tlslite-ng-0.5.1/tlslite/utils/pycrypto_aesgcm.py000066400000000000000000000006761261665411200222160ustar00rootroot00000000000000# Author: Google # See the LICENSE file for legal information regarding use of this file. """PyCrypto AES-GCM implementation.""" from .cryptomath import * from .aesgcm import AESGCM if pycryptoLoaded: import Crypto.Cipher.AES def new(key): cipher = Crypto.Cipher.AES.new(bytes(key)) def encrypt(plaintext): return bytearray(cipher.encrypt(bytes(plaintext))) return AESGCM(key, "pycrypto", encrypt) tlslite-ng-0.5.1/tlslite/utils/pycrypto_rc4.py000066400000000000000000000013741261665411200214430ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """PyCrypto RC4 implementation.""" from .cryptomath import * from .rc4 import * if pycryptoLoaded: import Crypto.Cipher.ARC4 def new(key): return PyCrypto_RC4(key) class PyCrypto_RC4(RC4): def __init__(self, key): RC4.__init__(self, key, "pycrypto") key = bytes(key) self.context = Crypto.Cipher.ARC4.new(key) def encrypt(self, plaintext): plaintext = bytes(plaintext) return bytearray(self.context.encrypt(plaintext)) def decrypt(self, ciphertext): ciphertext = bytes(ciphertext) return bytearray(self.context.decrypt(ciphertext))tlslite-ng-0.5.1/tlslite/utils/pycrypto_rsakey.py000066400000000000000000000025151261665411200222470ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """PyCrypto RSA implementation.""" from .cryptomath import * from .rsakey import * from .python_rsakey import Python_RSAKey from .compat import compatLong if pycryptoLoaded: from Crypto.PublicKey import RSA class PyCrypto_RSAKey(RSAKey): def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0): if not d: self.rsa = RSA.construct((compatLong(n), compatLong(e))) else: self.rsa = RSA.construct((compatLong(n), compatLong(e), compatLong(d), compatLong(p), compatLong(q))) def __getattr__(self, name): return getattr(self.rsa, name) def hasPrivateKey(self): return self.rsa.has_private() def _rawPrivateKeyOp(self, m): c = self.rsa.decrypt((m,)) return c def _rawPublicKeyOp(self, c): m = self.rsa.encrypt(c, None)[0] return m def generate(bits): key = PyCrypto_RSAKey() def f(numBytes): return bytes(getRandomBytes(numBytes)) key.rsa = RSA.generate(bits, f) return key generate = staticmethod(generate) tlslite-ng-0.5.1/tlslite/utils/pycrypto_tripledes.py000066400000000000000000000015501261665411200227420ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """PyCrypto 3DES implementation.""" from .cryptomath import * from .tripledes import * if pycryptoLoaded: import Crypto.Cipher.DES3 def new(key, mode, IV): return PyCrypto_TripleDES(key, mode, IV) class PyCrypto_TripleDES(TripleDES): def __init__(self, key, mode, IV): TripleDES.__init__(self, key, mode, IV, "pycrypto") key = bytes(key) IV = bytes(IV) self.context = Crypto.Cipher.DES3.new(key, mode, IV) def encrypt(self, plaintext): plaintext = bytes(plaintext) return bytearray(self.context.encrypt(plaintext)) def decrypt(self, ciphertext): ciphertext = bytes(ciphertext) return bytearray(self.context.decrypt(ciphertext))tlslite-ng-0.5.1/tlslite/utils/python_aes.py000066400000000000000000000037021261665411200211500ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Pure-Python AES implementation.""" from .cryptomath import * from .aes import * from .rijndael import rijndael def new(key, mode, IV): return Python_AES(key, mode, IV) class Python_AES(AES): def __init__(self, key, mode, IV): AES.__init__(self, key, mode, IV, "python") self.rijndael = rijndael(key, 16) self.IV = IV def encrypt(self, plaintext): AES.encrypt(self, plaintext) plaintextBytes = plaintext[:] chainBytes = self.IV[:] #CBC Mode: For each block... for x in range(len(plaintextBytes)//16): #XOR with the chaining block blockBytes = plaintextBytes[x*16 : (x*16)+16] for y in range(16): blockBytes[y] ^= chainBytes[y] #Encrypt it encryptedBytes = self.rijndael.encrypt(blockBytes) #Overwrite the input with the output for y in range(16): plaintextBytes[(x*16)+y] = encryptedBytes[y] #Set the next chaining block chainBytes = encryptedBytes self.IV = chainBytes[:] return plaintextBytes def decrypt(self, ciphertext): AES.decrypt(self, ciphertext) ciphertextBytes = ciphertext[:] chainBytes = self.IV[:] #CBC Mode: For each block... for x in range(len(ciphertextBytes)//16): #Decrypt it blockBytes = ciphertextBytes[x*16 : (x*16)+16] decryptedBytes = self.rijndael.decrypt(blockBytes) #XOR with the chaining block and overwrite the input with output for y in range(16): decryptedBytes[y] ^= chainBytes[y] ciphertextBytes[(x*16)+y] = decryptedBytes[y] #Set the next chaining block chainBytes = blockBytes self.IV = chainBytes[:] return ciphertextBytes tlslite-ng-0.5.1/tlslite/utils/python_aesgcm.py000066400000000000000000000004131261665411200216330ustar00rootroot00000000000000# Author: Google # See the LICENSE file for legal information regarding use of this file. """Pure-Python AES-GCM implementation.""" from .aesgcm import AESGCM from .rijndael import rijndael def new(key): return AESGCM(key, "python", rijndael(key, 16).encrypt) tlslite-ng-0.5.1/tlslite/utils/python_rc4.py000066400000000000000000000020611261665411200210650ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Pure-Python RC4 implementation.""" from .rc4 import RC4 from .cryptomath import * def new(key): return Python_RC4(key) class Python_RC4(RC4): def __init__(self, keyBytes): RC4.__init__(self, keyBytes, "python") S = [i for i in range(256)] j = 0 for i in range(256): j = (j + S[i] + keyBytes[i % len(keyBytes)]) % 256 S[i], S[j] = S[j], S[i] self.S = S self.i = 0 self.j = 0 def encrypt(self, plaintextBytes): ciphertextBytes = plaintextBytes[:] S = self.S i = self.i j = self.j for x in range(len(ciphertextBytes)): i = (i + 1) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] t = (S[i] + S[j]) % 256 ciphertextBytes[x] ^= S[t] self.i = i self.j = j return ciphertextBytes def decrypt(self, ciphertext): return self.encrypt(ciphertext) tlslite-ng-0.5.1/tlslite/utils/python_rsakey.py000066400000000000000000000104261261665411200216770ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Pure-Python RSA implementation.""" from .cryptomath import * from .asn1parser import ASN1Parser from .rsakey import * from .pem import * class Python_RSAKey(RSAKey): def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0): if (n and not e) or (e and not n): raise AssertionError() self.n = n self.e = e self.d = d self.p = p self.q = q self.dP = dP self.dQ = dQ self.qInv = qInv self.blinder = 0 self.unblinder = 0 def hasPrivateKey(self): return self.d != 0 def _rawPrivateKeyOp(self, m): #Create blinding values, on the first pass: if not self.blinder: self.unblinder = getRandomNumber(2, self.n) self.blinder = powMod(invMod(self.unblinder, self.n), self.e, self.n) #Blind the input m = (m * self.blinder) % self.n #Perform the RSA operation c = self._rawPrivateKeyOpHelper(m) #Unblind the output c = (c * self.unblinder) % self.n #Update blinding values self.blinder = (self.blinder * self.blinder) % self.n self.unblinder = (self.unblinder * self.unblinder) % self.n #Return the output return c def _rawPrivateKeyOpHelper(self, m): #Non-CRT version #c = powMod(m, self.d, self.n) #CRT version (~3x faster) s1 = powMod(m, self.dP, self.p) s2 = powMod(m, self.dQ, self.q) h = ((s1 - s2) * self.qInv) % self.p c = s2 + self.q * h return c def _rawPublicKeyOp(self, c): m = powMod(c, self.e, self.n) return m def acceptsPassword(self): return False def generate(bits): key = Python_RSAKey() p = getRandomPrime(bits//2, False) q = getRandomPrime(bits//2, False) t = lcm(p-1, q-1) key.n = p * q key.e = 65537 key.d = invMod(key.e, t) key.p = p key.q = q key.dP = key.d % (p-1) key.dQ = key.d % (q-1) key.qInv = invMod(q, p) return key generate = staticmethod(generate) def parsePEM(s, passwordCallback=None): """Parse a string containing a PEM-encoded .""" if pemSniff(s, "PRIVATE KEY"): bytes = dePem(s, "PRIVATE KEY") return Python_RSAKey._parsePKCS8(bytes) elif pemSniff(s, "RSA PRIVATE KEY"): bytes = dePem(s, "RSA PRIVATE KEY") return Python_RSAKey._parseSSLeay(bytes) else: raise SyntaxError("Not a PEM private key file") parsePEM = staticmethod(parsePEM) def _parsePKCS8(bytes): p = ASN1Parser(bytes) version = p.getChild(0).value[0] if version != 0: raise SyntaxError("Unrecognized PKCS8 version") rsaOID = p.getChild(1).value if list(rsaOID) != [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0]: raise SyntaxError("Unrecognized AlgorithmIdentifier") #Get the privateKey privateKeyP = p.getChild(2) #Adjust for OCTET STRING encapsulation privateKeyP = ASN1Parser(privateKeyP.value) return Python_RSAKey._parseASN1PrivateKey(privateKeyP) _parsePKCS8 = staticmethod(_parsePKCS8) def _parseSSLeay(bytes): privateKeyP = ASN1Parser(bytes) return Python_RSAKey._parseASN1PrivateKey(privateKeyP) _parseSSLeay = staticmethod(_parseSSLeay) def _parseASN1PrivateKey(privateKeyP): version = privateKeyP.getChild(0).value[0] if version != 0: raise SyntaxError("Unrecognized RSAPrivateKey version") n = bytesToNumber(privateKeyP.getChild(1).value) e = bytesToNumber(privateKeyP.getChild(2).value) d = bytesToNumber(privateKeyP.getChild(3).value) p = bytesToNumber(privateKeyP.getChild(4).value) q = bytesToNumber(privateKeyP.getChild(5).value) dP = bytesToNumber(privateKeyP.getChild(6).value) dQ = bytesToNumber(privateKeyP.getChild(7).value) qInv = bytesToNumber(privateKeyP.getChild(8).value) return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv) _parseASN1PrivateKey = staticmethod(_parseASN1PrivateKey) tlslite-ng-0.5.1/tlslite/utils/rc4.py000066400000000000000000000010611261665411200174630ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Abstract class for RC4.""" class RC4(object): def __init__(self, keyBytes, implementation): if len(keyBytes) < 16 or len(keyBytes) > 256: raise ValueError() self.isBlockCipher = False self.isAEAD = False self.name = "rc4" self.implementation = implementation def encrypt(self, plaintext): raise NotImplementedError() def decrypt(self, ciphertext): raise NotImplementedError() tlslite-ng-0.5.1/tlslite/utils/rijndael.py000066400000000000000000000254601261665411200205740ustar00rootroot00000000000000# Authors: # Bram Cohen # Trevor Perrin - various changes # # See the LICENSE file for legal information regarding use of this file. # Also see Bram Cohen's statement below """ A pure python (slow) implementation of rijndael with a decent interface To include - from rijndael import rijndael To do a key setup - r = rijndael(key, block_size = 16) key must be a string of length 16, 24, or 32 blocksize must be 16, 24, or 32. Default is 16 To use - ciphertext = r.encrypt(plaintext) plaintext = r.decrypt(ciphertext) If any strings are of the wrong length a ValueError is thrown """ # ported from the Java reference code by Bram Cohen, bram@gawth.com, April 2001 # this code is public domain, unless someone makes # an intellectual property claim against the reference # code, in which case it can be made public domain by # deleting all the comments and renaming all the variables import copy import string shifts = [[[0, 0], [1, 3], [2, 2], [3, 1]], [[0, 0], [1, 5], [2, 4], [3, 3]], [[0, 0], [1, 7], [3, 5], [4, 4]]] # [keysize][block_size] num_rounds = {16: {16: 10, 24: 12, 32: 14}, 24: {16: 12, 24: 12, 32: 14}, 32: {16: 14, 24: 14, 32: 14}} A = [[1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1], [1, 0, 0, 0, 1, 1, 1, 1], [1, 1, 0, 0, 0, 1, 1, 1], [1, 1, 1, 0, 0, 0, 1, 1], [1, 1, 1, 1, 0, 0, 0, 1]] # produce log and alog tables, needed for multiplying in the # field GF(2^m) (generator = 3) alog = [1] for i in range(255): j = (alog[-1] << 1) ^ alog[-1] if j & 0x100 != 0: j ^= 0x11B alog.append(j) log = [0] * 256 for i in range(1, 255): log[alog[i]] = i # multiply two elements of GF(2^m) def mul(a, b): if a == 0 or b == 0: return 0 return alog[(log[a & 0xFF] + log[b & 0xFF]) % 255] # substitution box based on F^{-1}(x) box = [[0] * 8 for i in range(256)] box[1][7] = 1 for i in range(2, 256): j = alog[255 - log[i]] for t in range(8): box[i][t] = (j >> (7 - t)) & 0x01 B = [0, 1, 1, 0, 0, 0, 1, 1] # affine transform: box[i] <- B + A*box[i] cox = [[0] * 8 for i in range(256)] for i in range(256): for t in range(8): cox[i][t] = B[t] for j in range(8): cox[i][t] ^= A[t][j] * box[i][j] # S-boxes and inverse S-boxes S = [0] * 256 Si = [0] * 256 for i in range(256): S[i] = cox[i][0] << 7 for t in range(1, 8): S[i] ^= cox[i][t] << (7-t) Si[S[i] & 0xFF] = i # T-boxes G = [[2, 1, 1, 3], [3, 2, 1, 1], [1, 3, 2, 1], [1, 1, 3, 2]] AA = [[0] * 8 for i in range(4)] for i in range(4): for j in range(4): AA[i][j] = G[i][j] AA[i][i+4] = 1 for i in range(4): pivot = AA[i][i] if pivot == 0: t = i + 1 while AA[t][i] == 0 and t < 4: t += 1 assert t != 4, 'G matrix must be invertible' for j in range(8): AA[i][j], AA[t][j] = AA[t][j], AA[i][j] pivot = AA[i][i] for j in range(8): if AA[i][j] != 0: AA[i][j] = alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255] for t in range(4): if i != t: for j in range(i+1, 8): AA[t][j] ^= mul(AA[i][j], AA[t][i]) AA[t][i] = 0 iG = [[0] * 4 for i in range(4)] for i in range(4): for j in range(4): iG[i][j] = AA[i][j + 4] def mul4(a, bs): if a == 0: return 0 r = 0 for b in bs: r <<= 8 if b != 0: r = r | mul(a, b) return r T1 = [] T2 = [] T3 = [] T4 = [] T5 = [] T6 = [] T7 = [] T8 = [] U1 = [] U2 = [] U3 = [] U4 = [] for t in range(256): s = S[t] T1.append(mul4(s, G[0])) T2.append(mul4(s, G[1])) T3.append(mul4(s, G[2])) T4.append(mul4(s, G[3])) s = Si[t] T5.append(mul4(s, iG[0])) T6.append(mul4(s, iG[1])) T7.append(mul4(s, iG[2])) T8.append(mul4(s, iG[3])) U1.append(mul4(t, iG[0])) U2.append(mul4(t, iG[1])) U3.append(mul4(t, iG[2])) U4.append(mul4(t, iG[3])) # round constants rcon = [1] r = 1 for t in range(1, 30): r = mul(2, r) rcon.append(r) del A del AA del pivot del B del G del box del log del alog del i del j del r del s del t del mul del mul4 del cox del iG class rijndael: def __init__(self, key, block_size = 16): if block_size != 16 and block_size != 24 and block_size != 32: raise ValueError('Invalid block size: ' + str(block_size)) if len(key) != 16 and len(key) != 24 and len(key) != 32: raise ValueError('Invalid key size: ' + str(len(key))) self.block_size = block_size ROUNDS = num_rounds[len(key)][block_size] BC = block_size // 4 # encryption round keys Ke = [[0] * BC for i in range(ROUNDS + 1)] # decryption round keys Kd = [[0] * BC for i in range(ROUNDS + 1)] ROUND_KEY_COUNT = (ROUNDS + 1) * BC KC = len(key) // 4 # copy user material bytes into temporary ints tk = [] for i in range(0, KC): tk.append((key[i * 4] << 24) | (key[i * 4 + 1] << 16) | (key[i * 4 + 2] << 8) | key[i * 4 + 3]) # copy values into round key arrays t = 0 j = 0 while j < KC and t < ROUND_KEY_COUNT: Ke[t // BC][t % BC] = tk[j] Kd[ROUNDS - (t // BC)][t % BC] = tk[j] j += 1 t += 1 tt = 0 rconpointer = 0 while t < ROUND_KEY_COUNT: # extrapolate using phi (the round key evolution function) tt = tk[KC - 1] tk[0] ^= (S[(tt >> 16) & 0xFF] & 0xFF) << 24 ^ \ (S[(tt >> 8) & 0xFF] & 0xFF) << 16 ^ \ (S[ tt & 0xFF] & 0xFF) << 8 ^ \ (S[(tt >> 24) & 0xFF] & 0xFF) ^ \ (rcon[rconpointer] & 0xFF) << 24 rconpointer += 1 if KC != 8: for i in range(1, KC): tk[i] ^= tk[i-1] else: for i in range(1, KC // 2): tk[i] ^= tk[i-1] tt = tk[KC // 2 - 1] tk[KC // 2] ^= (S[ tt & 0xFF] & 0xFF) ^ \ (S[(tt >> 8) & 0xFF] & 0xFF) << 8 ^ \ (S[(tt >> 16) & 0xFF] & 0xFF) << 16 ^ \ (S[(tt >> 24) & 0xFF] & 0xFF) << 24 for i in range(KC // 2 + 1, KC): tk[i] ^= tk[i-1] # copy values into round key arrays j = 0 while j < KC and t < ROUND_KEY_COUNT: Ke[t // BC][t % BC] = tk[j] Kd[ROUNDS - (t // BC)][t % BC] = tk[j] j += 1 t += 1 # inverse MixColumn where needed for r in range(1, ROUNDS): for j in range(BC): tt = Kd[r][j] Kd[r][j] = U1[(tt >> 24) & 0xFF] ^ \ U2[(tt >> 16) & 0xFF] ^ \ U3[(tt >> 8) & 0xFF] ^ \ U4[ tt & 0xFF] self.Ke = Ke self.Kd = Kd def encrypt(self, plaintext): if len(plaintext) != self.block_size: raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext))) Ke = self.Ke BC = self.block_size // 4 ROUNDS = len(Ke) - 1 if BC == 4: SC = 0 elif BC == 6: SC = 1 else: SC = 2 s1 = shifts[SC][1][0] s2 = shifts[SC][2][0] s3 = shifts[SC][3][0] a = [0] * BC # temporary work array t = [] # plaintext to ints + key for i in range(BC): t.append((plaintext[i * 4 ] << 24 | plaintext[i * 4 + 1] << 16 | plaintext[i * 4 + 2] << 8 | plaintext[i * 4 + 3] ) ^ Ke[0][i]) # apply round transforms for r in range(1, ROUNDS): for i in range(BC): a[i] = (T1[(t[ i ] >> 24) & 0xFF] ^ T2[(t[(i + s1) % BC] >> 16) & 0xFF] ^ T3[(t[(i + s2) % BC] >> 8) & 0xFF] ^ T4[ t[(i + s3) % BC] & 0xFF] ) ^ Ke[r][i] t = copy.copy(a) # last round is special result = [] for i in range(BC): tt = Ke[ROUNDS][i] result.append((S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) result.append((S[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) result.append((S[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) result.append((S[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF) return bytearray(result) def decrypt(self, ciphertext): if len(ciphertext) != self.block_size: raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext))) Kd = self.Kd BC = self.block_size // 4 ROUNDS = len(Kd) - 1 if BC == 4: SC = 0 elif BC == 6: SC = 1 else: SC = 2 s1 = shifts[SC][1][1] s2 = shifts[SC][2][1] s3 = shifts[SC][3][1] a = [0] * BC # temporary work array t = [0] * BC # ciphertext to ints + key for i in range(BC): t[i] = (ciphertext[i * 4 ] << 24 | ciphertext[i * 4 + 1] << 16 | ciphertext[i * 4 + 2] << 8 | ciphertext[i * 4 + 3] ) ^ Kd[0][i] # apply round transforms for r in range(1, ROUNDS): for i in range(BC): a[i] = (T5[(t[ i ] >> 24) & 0xFF] ^ T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^ T7[(t[(i + s2) % BC] >> 8) & 0xFF] ^ T8[ t[(i + s3) % BC] & 0xFF] ) ^ Kd[r][i] t = copy.copy(a) # last round is special result = [] for i in range(BC): tt = Kd[ROUNDS][i] result.append((Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) result.append((Si[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) result.append((Si[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) result.append((Si[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF) return bytearray(result) def encrypt(key, block): return rijndael(key, len(block)).encrypt(block) def decrypt(key, block): return rijndael(key, len(block)).decrypt(block) def test(): def t(kl, bl): b = 'b' * bl r = rijndael('a' * kl, bl) assert r.decrypt(r.encrypt(b)) == b t(16, 16) t(16, 24) t(16, 32) t(24, 16) t(24, 24) t(24, 32) t(32, 16) t(32, 24) t(32, 32) tlslite-ng-0.5.1/tlslite/utils/rsakey.py000066400000000000000000000210741261665411200202770ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Abstract class for RSA.""" from .cryptomath import * class RSAKey(object): """This is an abstract base class for RSA keys. Particular implementations of RSA keys, such as L{openssl_rsakey.OpenSSL_RSAKey}, L{python_rsakey.Python_RSAKey}, and L{pycrypto_rsakey.PyCrypto_RSAKey}, inherit from this. To create or parse an RSA key, don't use one of these classes directly. Instead, use the factory functions in L{tlslite.utils.keyfactory}. """ def __init__(self, n=0, e=0): """Create a new RSA key. If n and e are passed in, the new key will be initialized. @type n: int @param n: RSA modulus. @type e: int @param e: RSA public exponent. """ raise NotImplementedError() def __len__(self): """Return the length of this key in bits. @rtype: int """ return numBits(self.n) def hasPrivateKey(self): """Return whether or not this key has a private component. @rtype: bool """ raise NotImplementedError() def hashAndSign(self, bytes): """Hash and sign the passed-in bytes. This requires the key to have a private component. It performs a PKCS1-SHA1 signature on the passed-in data. @type bytes: str or L{bytearray} of unsigned bytes @param bytes: The value which will be hashed and signed. @rtype: L{bytearray} of unsigned bytes. @return: A PKCS1-SHA1 signature on the passed-in data. """ hashBytes = SHA1(bytearray(bytes)) prefixedHashBytes = self.addPKCS1SHA1Prefix(hashBytes) sigBytes = self.sign(prefixedHashBytes) return sigBytes def hashAndVerify(self, sigBytes, bytes): """Hash and verify the passed-in bytes with the signature. This verifies a PKCS1-SHA1 signature on the passed-in data. @type sigBytes: L{bytearray} of unsigned bytes @param sigBytes: A PKCS1-SHA1 signature. @type bytes: str or L{bytearray} of unsigned bytes @param bytes: The value which will be hashed and verified. @rtype: bool @return: Whether the signature matches the passed-in data. """ hashBytes = SHA1(bytearray(bytes)) # Try it with/without the embedded NULL prefixedHashBytes1 = self.addPKCS1SHA1Prefix(hashBytes, False) prefixedHashBytes2 = self.addPKCS1SHA1Prefix(hashBytes, True) result1 = self.verify(sigBytes, prefixedHashBytes1) result2 = self.verify(sigBytes, prefixedHashBytes2) return (result1 or result2) def sign(self, bytes): """Sign the passed-in bytes. This requires the key to have a private component. It performs a PKCS1 signature on the passed-in data. @type bytes: L{bytearray} of unsigned bytes @param bytes: The value which will be signed. @rtype: L{bytearray} of unsigned bytes. @return: A PKCS1 signature on the passed-in data. """ if not self.hasPrivateKey(): raise AssertionError() paddedBytes = self._addPKCS1Padding(bytes, 1) m = bytesToNumber(paddedBytes) if m >= self.n: raise ValueError() c = self._rawPrivateKeyOp(m) sigBytes = numberToByteArray(c, numBytes(self.n)) return sigBytes def verify(self, sigBytes, bytes): """Verify the passed-in bytes with the signature. This verifies a PKCS1 signature on the passed-in data. @type sigBytes: L{bytearray} of unsigned bytes @param sigBytes: A PKCS1 signature. @type bytes: L{bytearray} of unsigned bytes @param bytes: The value which will be verified. @rtype: bool @return: Whether the signature matches the passed-in data. """ if len(sigBytes) != numBytes(self.n): return False paddedBytes = self._addPKCS1Padding(bytes, 1) c = bytesToNumber(sigBytes) if c >= self.n: return False m = self._rawPublicKeyOp(c) checkBytes = numberToByteArray(m, numBytes(self.n)) return checkBytes == paddedBytes def encrypt(self, bytes): """Encrypt the passed-in bytes. This performs PKCS1 encryption of the passed-in data. @type bytes: L{bytearray} of unsigned bytes @param bytes: The value which will be encrypted. @rtype: L{bytearray} of unsigned bytes. @return: A PKCS1 encryption of the passed-in data. """ paddedBytes = self._addPKCS1Padding(bytes, 2) m = bytesToNumber(paddedBytes) if m >= self.n: raise ValueError() c = self._rawPublicKeyOp(m) encBytes = numberToByteArray(c, numBytes(self.n)) return encBytes def decrypt(self, encBytes): """Decrypt the passed-in bytes. This requires the key to have a private component. It performs PKCS1 decryption of the passed-in data. @type encBytes: L{bytearray} of unsigned bytes @param encBytes: The value which will be decrypted. @rtype: L{bytearray} of unsigned bytes or None. @return: A PKCS1 decryption of the passed-in data or None if the data is not properly formatted. """ if not self.hasPrivateKey(): raise AssertionError() if len(encBytes) != numBytes(self.n): return None c = bytesToNumber(encBytes) if c >= self.n: return None m = self._rawPrivateKeyOp(c) decBytes = numberToByteArray(m, numBytes(self.n)) #Check first two bytes if decBytes[0] != 0 or decBytes[1] != 2: return None #Scan through for zero separator for x in range(1, len(decBytes)-1): if decBytes[x]== 0: break else: return None return decBytes[x+1:] #Return everything after the separator def _rawPrivateKeyOp(self, m): raise NotImplementedError() def _rawPublicKeyOp(self, c): raise NotImplementedError() def acceptsPassword(self): """Return True if the write() method accepts a password for use in encrypting the private key. @rtype: bool """ raise NotImplementedError() def write(self, password=None): """Return a string containing the key. @rtype: str @return: A string describing the key, in whichever format (PEM) is native to the implementation. """ raise NotImplementedError() def generate(bits): """Generate a new key with the specified bit length. @rtype: L{tlslite.utils.RSAKey.RSAKey} """ raise NotImplementedError() generate = staticmethod(generate) # ************************************************************************** # Helper Functions for RSA Keys # ************************************************************************** @staticmethod def addPKCS1SHA1Prefix(bytes, withNULL=True): # There is a long history of confusion over whether the SHA1 # algorithmIdentifier should be encoded with a NULL parameter or # with the parameter omitted. While the original intention was # apparently to omit it, many toolkits went the other way. TLS 1.2 # specifies the NULL should be included, and this behavior is also # mandated in recent versions of PKCS #1, and is what tlslite has # always implemented. Anyways, verification code should probably # accept both. if not withNULL: prefixBytes = bytearray(\ [0x30,0x1f,0x30,0x07,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x04,0x14]) else: prefixBytes = bytearray(\ [0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14]) prefixedBytes = prefixBytes + bytes return prefixedBytes def _addPKCS1Padding(self, bytes, blockType): padLength = (numBytes(self.n) - (len(bytes)+3)) if blockType == 1: #Signature padding pad = [0xFF] * padLength elif blockType == 2: #Encryption padding pad = bytearray(0) while len(pad) < padLength: padBytes = getRandomBytes(padLength * 2) pad = [b for b in padBytes if b != 0] pad = pad[:padLength] else: raise AssertionError() padding = bytearray([0,blockType] + pad + [0]) paddedBytes = padding + bytes return paddedBytes tlslite-ng-0.5.1/tlslite/utils/tackwrapper.py000066400000000000000000000005161261665411200213220ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. try: from tack.structures.Tack import Tack from tack.structures.TackExtension import TackExtension from tack.tls.TlsCertificate import TlsCertificate tackpyLoaded = True except ImportError: tackpyLoaded = False tlslite-ng-0.5.1/tlslite/utils/tripledes.py000066400000000000000000000015461261665411200207760ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Abstract class for 3DES.""" class TripleDES(object): def __init__(self, key, mode, IV, implementation): if len(key) != 24: raise ValueError() if mode != 2: raise ValueError() if len(IV) != 8: raise ValueError() self.isBlockCipher = True self.isAEAD = False self.block_size = 8 self.implementation = implementation self.name = "3des" #CBC-Mode encryption, returns ciphertext #WARNING: *MAY* modify the input as well def encrypt(self, plaintext): assert(len(plaintext) % 8 == 0) #CBC-Mode decryption, returns plaintext #WARNING: *MAY* modify the input as well def decrypt(self, ciphertext): assert(len(ciphertext) % 8 == 0) tlslite-ng-0.5.1/tlslite/verifierdb.py000066400000000000000000000065151261665411200177650ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Class for storing SRP password verifiers.""" from .utils.cryptomath import * from .utils.compat import * from tlslite import mathtls from .basedb import BaseDB class VerifierDB(BaseDB): """This class represent an in-memory or on-disk database of SRP password verifiers. A VerifierDB can be passed to a server handshake to authenticate a client based on one of the verifiers. This class is thread-safe. """ def __init__(self, filename=None): """Create a new VerifierDB instance. @type filename: str @param filename: Filename for an on-disk database, or None for an in-memory database. If the filename already exists, follow this with a call to open(). To create a new on-disk database, follow this with a call to create(). """ BaseDB.__init__(self, filename, "verifier") def _getItem(self, username, valueStr): (N, g, salt, verifier) = valueStr.split(" ") N = bytesToNumber(a2b_base64(N)) g = bytesToNumber(a2b_base64(g)) salt = a2b_base64(salt) verifier = bytesToNumber(a2b_base64(verifier)) return (N, g, salt, verifier) def __setitem__(self, username, verifierEntry): """Add a verifier entry to the database. @type username: str @param username: The username to associate the verifier with. Must be less than 256 characters in length. Must not already be in the database. @type verifierEntry: tuple @param verifierEntry: The verifier entry to add. Use L{tlslite.verifierdb.VerifierDB.makeVerifier} to create a verifier entry. """ BaseDB.__setitem__(self, username, verifierEntry) def _setItem(self, username, value): if len(username)>=256: raise ValueError("username too long") N, g, salt, verifier = value N = b2a_base64(numberToByteArray(N)) g = b2a_base64(numberToByteArray(g)) salt = b2a_base64(salt) verifier = b2a_base64(numberToByteArray(verifier)) valueStr = " ".join( (N, g, salt, verifier) ) return valueStr def _checkItem(self, value, username, param): (N, g, salt, verifier) = value x = mathtls.makeX(salt, username, param) v = powMod(g, x, N) return (verifier == v) def makeVerifier(username, password, bits): """Create a verifier entry which can be stored in a VerifierDB. @type username: str @param username: The username for this verifier. Must be less than 256 characters in length. @type password: str @param password: The password for this verifier. @type bits: int @param bits: This values specifies which SRP group parameters to use. It must be one of (1024, 1536, 2048, 3072, 4096, 6144, 8192). Larger values are more secure but slower. 2048 is a good compromise between safety and speed. @rtype: tuple @return: A tuple which may be stored in a VerifierDB. """ usernameBytes = bytearray(username, "utf-8") passwordBytes = bytearray(password, "utf-8") return mathtls.makeVerifier(usernameBytes, passwordBytes, bits) makeVerifier = staticmethod(makeVerifier) tlslite-ng-0.5.1/tlslite/x509.py000066400000000000000000000064031261665411200163450ustar00rootroot00000000000000# Authors: # Trevor Perrin # Google - parsing subject field # # See the LICENSE file for legal information regarding use of this file. """Class representing an X.509 certificate.""" from .utils.asn1parser import ASN1Parser from .utils.cryptomath import * from .utils.keyfactory import _createPublicRSAKey from .utils.pem import * class X509(object): """This class represents an X.509 certificate. @type bytes: L{bytearray} of unsigned bytes @ivar bytes: The DER-encoded ASN.1 certificate @type publicKey: L{tlslite.utils.rsakey.RSAKey} @ivar publicKey: The subject public key from the certificate. @type subject: L{bytearray} of unsigned bytes @ivar subject: The DER-encoded ASN.1 subject distinguished name. """ def __init__(self): self.bytes = bytearray(0) self.publicKey = None self.subject = None def parse(self, s): """Parse a PEM-encoded X.509 certificate. @type s: str @param s: A PEM-encoded X.509 certificate (i.e. a base64-encoded certificate wrapped with "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----" tags). """ bytes = dePem(s, "CERTIFICATE") self.parseBinary(bytes) return self def parseBinary(self, bytes): """Parse a DER-encoded X.509 certificate. @type bytes: str or L{bytearray} of unsigned bytes @param bytes: A DER-encoded X.509 certificate. """ self.bytes = bytearray(bytes) p = ASN1Parser(bytes) #Get the tbsCertificate tbsCertificateP = p.getChild(0) #Is the optional version field present? #This determines which index the key is at. if tbsCertificateP.value[0]==0xA0: subjectPublicKeyInfoIndex = 6 else: subjectPublicKeyInfoIndex = 5 #Get the subject self.subject = tbsCertificateP.getChildBytes(\ subjectPublicKeyInfoIndex - 1) #Get the subjectPublicKeyInfo subjectPublicKeyInfoP = tbsCertificateP.getChild(\ subjectPublicKeyInfoIndex) #Get the algorithm algorithmP = subjectPublicKeyInfoP.getChild(0) rsaOID = algorithmP.value if list(rsaOID) != [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0]: raise SyntaxError("Unrecognized AlgorithmIdentifier") #Get the subjectPublicKey subjectPublicKeyP = subjectPublicKeyInfoP.getChild(1) #Adjust for BIT STRING encapsulation if (subjectPublicKeyP.value[0] !=0): raise SyntaxError() subjectPublicKeyP = ASN1Parser(subjectPublicKeyP.value[1:]) #Get the modulus and exponent modulusP = subjectPublicKeyP.getChild(0) publicExponentP = subjectPublicKeyP.getChild(1) #Decode them into numbers n = bytesToNumber(modulusP.value) e = bytesToNumber(publicExponentP.value) #Create a public key instance self.publicKey = _createPublicRSAKey(n, e) def getFingerprint(self): """Get the hex-encoded fingerprint of this certificate. @rtype: str @return: A hex-encoded fingerprint. """ return b2a_hex(SHA1(self.bytes)) def writeBytes(self): return self.bytes tlslite-ng-0.5.1/tlslite/x509certchain.py000066400000000000000000000053421261665411200202270ustar00rootroot00000000000000# Author: Trevor Perrin # See the LICENSE file for legal information regarding use of this file. """Class representing an X.509 certificate chain.""" from .utils import cryptomath from .utils.tackwrapper import * from .utils.pem import * from .x509 import X509 class X509CertChain(object): """This class represents a chain of X.509 certificates. @type x509List: list @ivar x509List: A list of L{tlslite.x509.X509} instances, starting with the end-entity certificate and with every subsequent certificate certifying the previous. """ def __init__(self, x509List=None): """Create a new X509CertChain. @type x509List: list @param x509List: A list of L{tlslite.x509.X509} instances, starting with the end-entity certificate and with every subsequent certificate certifying the previous. """ if x509List: self.x509List = x509List else: self.x509List = [] def parsePemList(self, s): """Parse a string containing a sequence of PEM certs. Raise a SyntaxError if input is malformed. """ x509List = [] bList = dePemList(s, "CERTIFICATE") for b in bList: x509 = X509() x509.parseBinary(b) x509List.append(x509) self.x509List = x509List def getNumCerts(self): """Get the number of certificates in this chain. @rtype: int """ return len(self.x509List) def getEndEntityPublicKey(self): """Get the public key from the end-entity certificate. @rtype: L{tlslite.utils.rsakey.RSAKey} """ if self.getNumCerts() == 0: raise AssertionError() return self.x509List[0].publicKey def getFingerprint(self): """Get the hex-encoded fingerprint of the end-entity certificate. @rtype: str @return: A hex-encoded fingerprint. """ if self.getNumCerts() == 0: raise AssertionError() return self.x509List[0].getFingerprint() def checkTack(self, tack): if self.x509List: tlsCert = TlsCertificate(self.x509List[0].bytes) if tlsCert.matches(tack): return True return False def getTackExt(self): """Get the TACK and/or Break Sigs from a TACK Cert in the chain.""" tackExt = None # Search list in backwards order for x509 in self.x509List[::-1]: tlsCert = TlsCertificate(x509.bytes) if tlsCert.tackExt: if tackExt: raise SyntaxError("Multiple TACK Extensions") else: tackExt = tlsCert.tackExt return tackExt tlslite-ng-0.5.1/unit_tests/000077500000000000000000000000001261665411200160045ustar00rootroot00000000000000tlslite-ng-0.5.1/unit_tests/__init__.py000066400000000000000000000000001261665411200201030ustar00rootroot00000000000000tlslite-ng-0.5.1/unit_tests/mocksock.py000066400000000000000000000064101261665411200201700ustar00rootroot00000000000000# Copyright (c) 2015, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. import socket import errno class MockSocket(socket.socket): def __init__(self, buf, maxRet=None, maxWrite=None, blockEveryOther=False): # current position in read buffer (buf) self.index = 0 # read buffer self.buf = buf # write buffer (data sent from application, to be asserted by test) self.sent = [] self.closed = False # maximum number of bytes that socket will read/return at a time self.maxRet = maxRet # maximum number of bytes that socket will write at a time self.maxWrite = maxWrite # make socket rise errno.EWOULDBLOCK every other read or write self.blockEveryOther = blockEveryOther # if next read will be blocked self.blockRead = False # if next write will be blocked self.blockWrite = False def __repr__(self): return "MockSocket(index={0}, buf={1!r}, sent={2!r})".format( self.index, self.buf, self.sent) def recv(self, size): if self.closed: raise ValueError("Read from closed socket") # simulate a socket with full buffers, make it rise "Would block" # every other call if self.blockEveryOther: if self.blockRead: self.blockRead = False raise socket.error(errno.EWOULDBLOCK) else: self.blockRead = True # return empty array if the caller asked for no data if size == 0: return bytearray(0) # limit returned data (if set) # this will cause the socket to return just maxRet bytes, even if it # has more in buf or was asked to return more in this call if self.maxRet is not None and self.maxRet < size: size = self.maxRet # don't allow reading past array end if len(self.buf[self.index:]) == 0: raise socket.error(errno.EWOULDBLOCK) # if asked for more than we have prepared, return just as much as we # have elif len(self.buf[self.index:]) < size: ret = self.buf[self.index:] self.index = len(self.buf) return ret # regular call, return as much as was asked for else: ret = self.buf[self.index:self.index+size] self.index+=size return ret def send(self, data): if self.closed: raise ValueError("Write to closed socket") # simulate a socket with full buffer, raise "Would Block" every other # call if self.blockEveryOther: if self.blockWrite: self.blockWrite = False raise socket.error(errno.EWOULDBLOCK) else: self.blockWrite = True # regular write, just append to list of performed writes if self.maxWrite is None or len(data) < self.maxWrite: self.sent.append(data) return len(data) # simulate a socket that won't write more data that it can # (e.g. because the simulated buffers are mostly full) self.sent.append(data[:self.maxWrite]) return self.maxWrite def close(self): self.closed = True tlslite-ng-0.5.1/unit_tests/test_tlslite_constants.py000066400000000000000000000042571261665411200232010ustar00rootroot00000000000000# Copyright (c) 2015, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. # compatibility with Python 2.6, for that we need unittest2 package, # which is not available on 3.3 or 3.4 try: import unittest2 as unittest except ImportError: import unittest from tlslite.constants import CipherSuite class TestCipherSuite(unittest.TestCase): def test___init__(self): cipherSuites = CipherSuite() self.assertIsNotNone(cipherSuites) def test_filterForVersion_with_SSL3_ciphers(self): suites = [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_RC4_128_MD5] filtered = CipherSuite.filterForVersion(suites, (3, 0), (3, 0)) self.assertEqual(filtered, [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_RC4_128_MD5]) filtered = CipherSuite.filterForVersion(suites, (3, 3), (3, 3)) self.assertEqual(filtered, [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_RC4_128_MD5]) def test_filterForVersion_with_unknown_ciphers(self): suites = [0, 0xfffe] filtered = CipherSuite.filterForVersion(suites, (3, 0), (3, 3)) self.assertEqual(filtered, []) def test_filterForVersion_with_TLS_1_2_ciphers(self): suites = [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_RC4_128_MD5, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256] filtered = CipherSuite.filterForVersion(suites, (3, 2), (3, 2)) self.assertEqual(filtered, [CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_RC4_128_MD5]) tlslite-ng-0.5.1/unit_tests/test_tlslite_defragmenter.py000066400000000000000000000152661261665411200236320ustar00rootroot00000000000000# Copyright (c) 2015, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. # compatibility with Python 2.6, for that we need unittest2 package, # which is not available on 3.3 or 3.4 try: import unittest2 as unittest except ImportError: import unittest from tlslite.defragmenter import Defragmenter class TestDefragmenter(unittest.TestCase): def test___init__(self): a = Defragmenter() self.assertIsNotNone(a) def test_getMessage(self): a = Defragmenter() self.assertIsNone(a.getMessage()) self.assertIsNone(a.getMessage()) def test_addStaticSize(self): d = Defragmenter() d.addStaticSize(10, 2) d.addData(10, bytearray(b'\x03'*2)) ret = d.getMessage() self.assertIsNotNone(ret) msgType, data = ret self.assertEqual(10, msgType) self.assertEqual(bytearray(b'\x03'*2), data) def test_addStaticSize_with_already_defined_type(self): d = Defragmenter() d.addStaticSize(10, 255) with self.assertRaises(ValueError): d.addStaticSize(10, 2) def test_addStaticSize_with_uncomplete_message(self): d = Defragmenter() d.addStaticSize(10, 2) d.addData(10, bytearray(b'\x10')) ret = d.getMessage() self.assertIsNone(ret) d.addData(10, bytearray(b'\x11')) ret = d.getMessage() self.assertIsNotNone(ret) msgType, data = ret self.assertEqual(10, msgType) self.assertEqual(bytearray(b'\x10\x11'), data) ret = d.getMessage() self.assertIsNone(ret) def test_addStaticSize_with_multiple_types(self): d = Defragmenter() # types are added in order of priority... d.addStaticSize(10, 2) # so type 8 should be returned later than type 10 if both are in buffer d.addStaticSize(8, 4) d.addData(8, bytearray(b'\x08'*4)) d.addData(10, bytearray(b'\x10'*2)) ret = d.getMessage() self.assertIsNotNone(ret) msgType, data = ret self.assertEqual(10, msgType) self.assertEqual(bytearray(b'\x10'*2), data) ret = d.getMessage() self.assertIsNotNone(ret) msgType, data = ret self.assertEqual(8, msgType) self.assertEqual(bytearray(b'\x08'*4), data) ret = d.getMessage() self.assertIsNone(ret) def test_addStaticSize_with_multiple_uncompleted_messages(self): d = Defragmenter() d.addStaticSize(10, 2) d.addStaticSize(8, 4) d.addData(8, bytearray(b'\x08'*3)) d.addData(10, bytearray(b'\x10')) ret = d.getMessage() self.assertIsNone(ret) d.addData(8, bytearray(b'\x09')) ret = d.getMessage() self.assertIsNotNone(ret) msgType, data = ret self.assertEqual(8, msgType) self.assertEqual(bytearray(b'\x08'*3 + b'\x09'), data) ret = d.getMessage() self.assertIsNone(ret) def test_addDynamicSize(self): d = Defragmenter() d.addDynamicSize(10, 2, 2) ret = d.getMessage() self.assertIsNone(ret) d.addData(10, bytearray( b'\xee\xee' + # header bytes b'\x00\x00' + # remaining length # next message b'\xff\xff' + # header bytes b'\x00\x01' + # remaining length b'\xf0')) ret = d.getMessage() self.assertIsNotNone(ret) msgType, data = ret self.assertEqual(10, msgType) self.assertEqual(bytearray(b'\xee\xee\x00\x00'), data) ret = d.getMessage() self.assertIsNotNone(ret) msgType, data = ret self.assertEqual(10, msgType) self.assertEqual(bytearray(b'\xff\xff\x00\x01\xf0'), data) ret = d.getMessage() self.assertIsNone(ret) def test_addDynamicSize_with_incomplete_header(self): d = Defragmenter() d.addDynamicSize(10, 2, 2) d.addData(10, bytearray(b'\xee')) self.assertIsNone(d.getMessage()) d.addData(10, bytearray(b'\xee')) self.assertIsNone(d.getMessage()) d.addData(10, bytearray(b'\x00')) self.assertIsNone(d.getMessage()) d.addData(10, bytearray(b'\x00')) ret = d.getMessage() self.assertIsNotNone(ret) msgType, data = ret self.assertEqual(10, msgType) self.assertEqual(bytearray(b'\xee\xee\x00\x00'), data) def test_addDynamicSize_with_incomplete_payload(self): d = Defragmenter() d.addDynamicSize(10, 2, 2) d.addData(10, bytearray(b'\xee\xee\x00\x01')) self.assertIsNone(d.getMessage()) d.addData(10, bytearray(b'\x99')) msgType, data = d.getMessage() self.assertEqual(10, msgType) self.assertEqual(bytearray(b'\xee\xee\x00\x01\x99'), data) def test_addDynamicSize_with_two_streams(self): d = Defragmenter() d.addDynamicSize(9, 0, 3) d.addDynamicSize(10, 2, 2) d.addData(10, bytearray(b'\x44\x44\x00\x04')) d.addData(9, bytearray(b'\x00\x00\x02')) self.assertIsNone(d.getMessage()) d.addData(9, bytearray(b'\x09'*2)) d.addData(10, bytearray(b'\x10'*4)) msgType, data = d.getMessage() self.assertEqual(msgType, 9) self.assertEqual(data, bytearray(b'\x00\x00\x02\x09\x09')) msgType, data = d.getMessage() self.assertEqual(msgType, 10) self.assertEqual(data, bytearray(b'\x44'*2 + b'\x00\x04' + b'\x10'*4)) def test_addStaticSize_with_zero_size(self): d = Defragmenter() with self.assertRaises(ValueError): d.addStaticSize(10, 0) def test_addStaticSize_with_invalid_size(self): d = Defragmenter() with self.assertRaises(ValueError): d.addStaticSize(10, -10) def test_addDynamicSize_with_double_type(self): d = Defragmenter() d.addDynamicSize(1, 0, 1) with self.assertRaises(ValueError): d.addDynamicSize(1, 2, 2) def test_addDynamicSize_with_invalid_size(self): d = Defragmenter() with self.assertRaises(ValueError): d.addDynamicSize(1, 2, 0) def test_addDynamicSize_with_invalid_offset(self): d = Defragmenter() with self.assertRaises(ValueError): d.addDynamicSize(1, -1, 2) def test_addData_with_undefined_type(self): d = Defragmenter() with self.assertRaises(ValueError): d.addData(1, bytearray(10)) def test_clearBuffers(self): d = Defragmenter() d.addStaticSize(10, 2) d.addData(10, bytearray(10)) d.clearBuffers() self.assertIsNone(d.getMessage()) tlslite-ng-0.5.1/unit_tests/test_tlslite_extensions.py000066400000000000000000001315031261665411200233570ustar00rootroot00000000000000# Copyright (c) 2014, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. # compatibility with Python 2.6, for that we need unittest2 package, # which is not available on 3.3 or 3.4 try: import unittest2 as unittest except ImportError: import unittest from tlslite.extensions import TLSExtension, SNIExtension, NPNExtension,\ SRPExtension, ClientCertTypeExtension, ServerCertTypeExtension,\ TACKExtension, SupportedGroupsExtension, ECPointFormatsExtension,\ SignatureAlgorithmsExtension from tlslite.utils.codec import Parser from tlslite.constants import NameType, ExtensionType, GroupName,\ ECPointFormat, HashAlgorithm, SignatureAlgorithm from tlslite.errors import TLSInternalError class TestTLSExtension(unittest.TestCase): def test___init__(self): tls_extension = TLSExtension() assert(tls_extension) self.assertIsNone(tls_extension.extType) self.assertEqual(bytearray(0), tls_extension.extData) def test_create(self): tls_extension = TLSExtension().create(1, bytearray(b'\x01\x00')) assert tls_extension self.assertEqual(1, tls_extension.extType) self.assertEqual(bytearray(b'\x01\x00'), tls_extension.extData) def test_write(self): tls_extension = TLSExtension() with self.assertRaises(AssertionError) as environment: tls_extension.write() def test_write_with_data(self): tls_extension = TLSExtension().create(44, bytearray(b'garbage')) self.assertEqual(bytearray( b'\x00\x2c' + # type of extension - 44 b'\x00\x07' + # length of extension - 7 bytes # utf-8 encoding of "garbage" b'\x67\x61\x72\x62\x61\x67\x65' ), tls_extension.write()) def test_parse(self): p = Parser(bytearray( b'\x00\x42' + # type of extension b'\x00\x01' + # length of rest of data b'\xff' # value of extension )) tls_extension = TLSExtension().parse(p) self.assertEqual(66, tls_extension.extType) self.assertEqual(bytearray(b'\xff'), tls_extension.extData) def test_parse_with_length_long_by_one(self): p = Parser(bytearray( b'\x00\x42' + # type of extension b'\x00\x03' + # length of rest of data b'\xff\xfa' # value of extension )) with self.assertRaises(SyntaxError) as context: TLSExtension().parse(p) def test_parse_with_sni_ext(self): p = Parser(bytearray( b'\x00\x00' + # type of extension - SNI (0) b'\x00\x10' + # length of extension - 16 bytes b'\x00\x0e' + # length of array b'\x00' + # type of entry - host_name (0) b'\x00\x0b' + # length of name - 11 bytes # UTF-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d')) tls_extension = TLSExtension().parse(p) self.assertIsInstance(tls_extension, SNIExtension) self.assertEqual(bytearray(b'example.com'), tls_extension.hostNames[0]) def test_parse_with_SNI_server_side(self): p = Parser(bytearray( b'\x00\x00' + # type of extension - SNI b'\x00\x00' # overall length - 0 bytes )) ext = TLSExtension(server=True).parse(p) self.assertIsInstance(ext, SNIExtension) self.assertIsNone(ext.serverNames) def test_parse_with_SRP_ext(self): p = Parser(bytearray( b'\x00\x0c' + # ext type - 12 b'\x00\x09' + # overall length b'\x08' + # name length b'username' # name )) ext = TLSExtension().parse(p) self.assertIsInstance(ext, SRPExtension) self.assertEqual(ext.identity, b'username') def test_parse_with_NPN_ext(self): p = Parser(bytearray( b'\x33\x74' + # type of extension - NPN b'\x00\x09' + # overall length b'\x08' + # first name length b'http/1.1' )) ext = TLSExtension().parse(p) self.assertIsInstance(ext, NPNExtension) self.assertEqual(ext.protocols, [b'http/1.1']) def test_parse_with_SNI_server_side(self): p = Parser(bytearray( b'\x00\x00' + # type of extension - SNI b'\x00\x00' # overall length - 0 bytes )) ext = TLSExtension(server=True).parse(p) self.assertIsInstance(ext, SNIExtension) self.assertIsNone(ext.serverNames) def test_parse_with_renego_info_server_side(self): p = Parser(bytearray( b'\xff\x01' + # type of extension - renegotiation_info b'\x00\x01' + # overall length b'\x00' # extension length )) ext = TLSExtension(server=True).parse(p) # XXX not supported self.assertIsInstance(ext, TLSExtension) self.assertEqual(ext.extData, bytearray(b'\x00')) self.assertEqual(ext.extType, 0xff01) def test_parse_with_elliptic_curves(self): p = Parser(bytearray( b'\x00\x0a' + # type of extension b'\x00\x08' + # overall length b'\x00\x06' + # length of array b'\x00\x17' + # secp256r1 b'\x00\x18' + # secp384r1 b'\x00\x19' # secp521r1 )) ext = TLSExtension().parse(p) self.assertIsInstance(ext, SupportedGroupsExtension) self.assertEqual(ext.groups, [GroupName.secp256r1, GroupName.secp384r1, GroupName.secp521r1]) def test_parse_with_ec_point_formats(self): p = Parser(bytearray( b'\x00\x0b' + # type of extension b'\x00\x02' + # overall length b'\x01' + # length of array b'\x00' # type - uncompressed )) ext = TLSExtension().parse(p) self.assertIsInstance(ext, ECPointFormatsExtension) self.assertEqual(ext.formats, [ECPointFormat.uncompressed]) def test_parse_with_signature_algorithms(self): p = Parser(bytearray( b'\x00\x0d' + # type of extension b'\x00\x1c' + # overall length b'\x00\x1a' + # length of array b'\x04\x01' + # SHA256+RSA b'\x04\x02' + # SHA256+DSA b'\x04\x03' + # SHA256+ECDSA b'\x05\x01' + # SHA384+RSA b'\x05\x03' + # SHA384+ECDSA b'\x06\x01' + # SHA512+RSA b'\x06\x03' + # SHA512+ECDSA b'\x03\x01' + # SHA224+RSA b'\x03\x02' + # SHA224+DSA b'\x03\x03' + # SHA224+ECDSA b'\x02\x01' + # SHA1+RSA b'\x02\x02' + # SHA1+DSA b'\x02\x03' # SHA1+ECDSA )) ext = TLSExtension().parse(p) self.assertIsInstance(ext, SignatureAlgorithmsExtension) self.assertEqual(ext.sigalgs, [(HashAlgorithm.sha256, SignatureAlgorithm.rsa), (HashAlgorithm.sha256, SignatureAlgorithm.dsa), (HashAlgorithm.sha256, SignatureAlgorithm.ecdsa), (HashAlgorithm.sha384, SignatureAlgorithm.rsa), (HashAlgorithm.sha384, SignatureAlgorithm.ecdsa), (HashAlgorithm.sha512, SignatureAlgorithm.rsa), (HashAlgorithm.sha512, SignatureAlgorithm.ecdsa), (HashAlgorithm.sha224, SignatureAlgorithm.rsa), (HashAlgorithm.sha224, SignatureAlgorithm.dsa), (HashAlgorithm.sha224, SignatureAlgorithm.ecdsa), (HashAlgorithm.sha1, SignatureAlgorithm.rsa), (HashAlgorithm.sha1, SignatureAlgorithm.dsa), (HashAlgorithm.sha1, SignatureAlgorithm.ecdsa)]) def test_equality(self): a = TLSExtension().create(0, bytearray(0)) b = SNIExtension().create() self.assertTrue(a == b) def test_equality_with_empty_array_in_sni_extension(self): a = TLSExtension().create(0, bytearray(b'\x00\x00')) b = SNIExtension().create(serverNames=[]) self.assertTrue(a == b) def test_equality_with_nearly_good_object(self): class TestClass(object): def __init__(self): self.extType = 0 a = TLSExtension().create(0, bytearray(b'\x00\x00')) b = TestClass() self.assertFalse(a == b) def test_parse_of_server_hello_extension(self): ext = TLSExtension(server=True) p = Parser(bytearray( b'\x00\x09' + # extension type - cert_type (9) b'\x00\x01' + # extension length - 1 byte b'\x01' # certificate type - OpenGPG (1) )) ext = ext.parse(p) self.assertIsInstance(ext, ServerCertTypeExtension) self.assertEqual(1, ext.cert_type) def test_parse_with_client_cert_type_extension(self): ext = TLSExtension() p = Parser(bytearray( b'\x00\x09' + # ext type b'\x00\x02' + # ext length b'\x01' + # length of array b'\x01')) # type - opengpg (1) ext = ext.parse(p) self.assertIsInstance(ext, ClientCertTypeExtension) self.assertEqual([1], ext.certTypes) def test___repr__(self): ext = TLSExtension() ext = ext.create(0, bytearray(b'\x00\x00')) self.assertEqual("TLSExtension(extType=0, "\ "extData=bytearray(b'\\x00\\x00'), serverType=False)", repr(ext)) class TestSNIExtension(unittest.TestCase): def test___init__(self): server_name = SNIExtension() self.assertIsNone(server_name.serverNames) self.assertEqual(tuple(), server_name.hostNames) # properties inherited from TLSExtension: self.assertEqual(0, server_name.extType) self.assertEqual(bytearray(0), server_name.extData) def test_create(self): server_name = SNIExtension() server_name = server_name.create() self.assertIsNone(server_name.serverNames) self.assertEqual(tuple(), server_name.hostNames) def test_create_with_hostname(self): server_name = SNIExtension() server_name = server_name.create(bytearray(b'example.com')) self.assertEqual((bytearray(b'example.com'),), server_name.hostNames) self.assertEqual([SNIExtension.ServerName( NameType.host_name, bytearray(b'example.com') )], server_name.serverNames) def test_create_with_hostNames(self): server_name = SNIExtension() server_name = server_name.create(hostNames=[bytearray(b'example.com'), bytearray(b'www.example.com')]) self.assertEqual(( bytearray(b'example.com'), bytearray(b'www.example.com') ), server_name.hostNames) self.assertEqual([ SNIExtension.ServerName( NameType.host_name, bytearray(b'example.com')), SNIExtension.ServerName( NameType.host_name, bytearray(b'www.example.com'))], server_name.serverNames) def test_create_with_serverNames(self): server_name = SNIExtension() server_name = server_name.create(serverNames=[ SNIExtension.ServerName(1, bytearray(b'example.com')), SNIExtension.ServerName(4, bytearray(b'www.example.com')), SNIExtension.ServerName(0, bytearray(b'example.net'))]) self.assertEqual((bytearray(b'example.net'),), server_name.hostNames) self.assertEqual([ SNIExtension.ServerName( 1, bytearray(b'example.com')), SNIExtension.ServerName( 4, bytearray(b'www.example.com')), SNIExtension.ServerName( 0, bytearray(b'example.net'))], server_name.serverNames) def test_hostNames(self): server_name = SNIExtension() server_name = server_name.create(serverNames=[ SNIExtension.ServerName(0, bytearray(b'example.net')), SNIExtension.ServerName(1, bytearray(b'example.com')), SNIExtension.ServerName(4, bytearray(b'www.example.com')) ]) server_name.hostNames = \ [bytearray(b'example.com')] self.assertEqual((bytearray(b'example.com'),), server_name.hostNames) self.assertEqual([ SNIExtension.ServerName(0, bytearray(b'example.com')), SNIExtension.ServerName(1, bytearray(b'example.com')), SNIExtension.ServerName(4, bytearray(b'www.example.com'))], server_name.serverNames) def test_hostNames_delete(self): server_name = SNIExtension() server_name = server_name.create(serverNames=[ SNIExtension.ServerName(0, bytearray(b'example.net')), SNIExtension.ServerName(1, bytearray(b'example.com')), SNIExtension.ServerName(4, bytearray(b'www.example.com')) ]) del server_name.hostNames self.assertEqual(tuple(), server_name.hostNames) self.assertEqual([ SNIExtension.ServerName(1, bytearray(b'example.com')), SNIExtension.ServerName(4, bytearray(b'www.example.com'))], server_name.serverNames) def test_write(self): server_name = SNIExtension() server_name = server_name.create(bytearray(b'example.com')) self.assertEqual(bytearray( b'\x00\x0e' + # length of array - 14 bytes b'\x00' + # type of element - host_name (0) b'\x00\x0b' + # length of element - 11 bytes # UTF-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d' ), server_name.extData) self.assertEqual(bytearray( b'\x00\x00' + # type of extension - SNI (0) b'\x00\x10' + # length of extension - 16 bytes b'\x00\x0e' + # length of array - 14 bytes b'\x00' + # type of element - host_name (0) b'\x00\x0b' + # length of element - 11 bytes # UTF-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d' ), server_name.write()) def test_write_with_multiple_hostnames(self): server_name = SNIExtension() server_name = server_name.create(hostNames=[ bytearray(b'example.com'), bytearray(b'example.org')]) self.assertEqual(bytearray( b'\x00\x1c' + # lenght of array - 28 bytes b'\x00' + # type of element - host_name (0) b'\x00\x0b' + # length of element - 11 bytes # utf-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d' + b'\x00' + # type of elemnt - host_name (0) b'\x00\x0b' + # length of elemnet - 11 bytes # utf-8 encoding of example.org b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6f\x72\x67' ), server_name.extData) self.assertEqual(bytearray( b'\x00\x00' + # type of extension - SNI (0) b'\x00\x1e' + # length of extension - 26 bytes b'\x00\x1c' + # lenght of array - 24 bytes b'\x00' + # type of element - host_name (0) b'\x00\x0b' + # length of element - 11 bytes # utf-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d' + b'\x00' + # type of elemnt - host_name (0) b'\x00\x0b' + # length of elemnet - 11 bytes # utf-8 encoding of example.org b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6f\x72\x67' ), server_name.write()) def test_write_of_empty_extension(self): server_name = SNIExtension() self.assertEqual(bytearray( b'\x00\x00' + # type of extension - SNI (0) b'\x00\x00' # length of extension - 0 bytes ), server_name.write()) def test_write_of_empty_list_of_names(self): server_name = SNIExtension() server_name = server_name.create(serverNames=[]) self.assertEqual(bytearray( b'\x00\x00' # length of array - 0 bytes ), server_name.extData) self.assertEqual(bytearray( b'\x00\x00' + # type of extension - SNI 0 b'\x00\x02' + # length of extension - 2 bytes b'\x00\x00' # length of array of names - 0 bytes ), server_name.write()) def tes_parse_with_invalid_data(self): server_name = SNIExtension() p = Parser(bytearray(b'\x00\x01')) with self.assertRaises(SyntaxError): server_name.parse(p) def test_parse_of_server_side_version(self): server_name = SNIExtension() p = Parser(bytearray(0)) server_name = server_name.parse(p) self.assertIsNone(server_name.serverNames) def test_parse_null_length_array(self): server_name = SNIExtension() p = Parser(bytearray(b'\x00\x00')) server_name = server_name.parse(p) self.assertEqual([], server_name.serverNames) def test_parse_with_host_name(self): server_name = SNIExtension() p = Parser(bytearray( b'\x00\x0e' + # length of array b'\x00' + # type of entry - host_name (0) b'\x00\x0b' + # length of name - 11 bytes # UTF-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d')) server_name = server_name.parse(p) self.assertEqual(bytearray(b'example.com'), server_name.hostNames[0]) self.assertEqual(tuple([bytearray(b'example.com')]), server_name.hostNames) def test_parse_with_multiple_hostNames(self): server_name = SNIExtension() p = Parser(bytearray( b'\x00\x1c' + # length of array - 28 bytes b'\x0a' + # type of entry - unassigned (10) b'\x00\x0b' + # length of name - 11 bytes # UTF-8 encoding of example.org b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6f\x72\x67' + b'\x00' + # type of entry - host_name (0) b'\x00\x0b' + # length of name - 11 bytes # UTF-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d')) server_name = server_name.parse(p) self.assertEqual(bytearray(b'example.com'), server_name.hostNames[0]) self.assertEqual(tuple([bytearray(b'example.com')]), server_name.hostNames) SN = SNIExtension.ServerName self.assertEqual([ SN(10, bytearray(b'example.org')), SN(0, bytearray(b'example.com')) ], server_name.serverNames) def test_parse_with_array_length_long_by_one(self): server_name = SNIExtension() p = Parser(bytearray( b'\x00\x0f' + # length of array (one too long) b'\x00' + # type of entry - host_name (0) b'\x00\x0b' + # length of name - 11 bytes # UTF-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d')) with self.assertRaises(SyntaxError): server_name = server_name.parse(p) def test_parse_with_array_length_short_by_one(self): server_name = SNIExtension() p = Parser(bytearray( b'\x00\x0d' + # length of array (one too short) b'\x00' + # type of entry - host_name (0) b'\x00\x0b' + # length of name - 11 bytes # UTF-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d')) with self.assertRaises(SyntaxError): server_name = server_name.parse(p) def test_parse_with_name_length_long_by_one(self): server_name = SNIExtension() p = Parser(bytearray( b'\x00\x1c' + # length of array - 28 bytes b'\x0a' + # type of entry - unassigned (10) b'\x00\x0c' + # length of name - 12 bytes (long by one) # UTF-8 encoding of example.org b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6f\x72\x67' + b'\x00' + # type of entry - host_name (0) b'\x00\x0b' + # length of name - 11 bytes # UTF-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d')) with self.assertRaises(SyntaxError): server_name = server_name.parse(p) server_name = SNIExtension() p = Parser(bytearray( b'\x00\x1c' + # length of array - 28 bytes b'\x0a' + # type of entry - unassigned (10) b'\x00\x0b' + # length of name - 11 bytes # UTF-8 encoding of example.org b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6f\x72\x67' + b'\x00' + # type of entry - host_name (0) b'\x00\x0c' + # length of name - 12 bytes (long by one) # UTF-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d')) with self.assertRaises(SyntaxError): server_name = server_name.parse(p) def test_parse_with_name_length_short_by_one(self): server_name = SNIExtension() p = Parser(bytearray( b'\x00\x1c' + # length of array - 28 bytes b'\x0a' + # type of entry - unassigned (10) b'\x00\x0a' + # length of name - 10 bytes (short by one) # UTF-8 encoding of example.org b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6f\x72\x67' + b'\x00' + # type of entry - host_name (0) b'\x00\x0b' + # length of name - 11 bytes # UTF-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d')) with self.assertRaises(SyntaxError): server_name = server_name.parse(p) server_name = SNIExtension() p = Parser(bytearray( b'\x00\x1c' + # length of array - 28 bytes b'\x0a' + # type of entry - unassigned (10) b'\x00\x0b' + # length of name - 11 bytes # UTF-8 encoding of example.org b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6f\x72\x67' + b'\x00' + # type of entry - host_name (0) b'\x00\x0a' + # length of name - 10 bytes (short by one) # UTF-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d')) with self.assertRaises(SyntaxError): server_name = server_name.parse(p) def test___repr__(self): server_name = SNIExtension() server_name = server_name.create( serverNames=[ SNIExtension.ServerName(0, bytearray(b'example.com')), SNIExtension.ServerName(1, bytearray(b'\x04\x01'))]) self.assertEqual("SNIExtension(serverNames=["\ "ServerName(name_type=0, name=bytearray(b'example.com')), "\ "ServerName(name_type=1, name=bytearray(b'\\x04\\x01'))])", repr(server_name)) class TestClientCertTypeExtension(unittest.TestCase): def test___init___(self): cert_type = ClientCertTypeExtension() self.assertEqual(9, cert_type.extType) self.assertEqual(bytearray(0), cert_type.extData) self.assertIsNone(cert_type.certTypes) def test_create(self): cert_type = ClientCertTypeExtension() cert_type = cert_type.create() self.assertEqual(9, cert_type.extType) self.assertEqual(bytearray(0), cert_type.extData) self.assertIsNone(cert_type.certTypes) def test_create_with_empty_list(self): cert_type = ClientCertTypeExtension() cert_type = cert_type.create([]) self.assertEqual(bytearray(b'\x00'), cert_type.extData) self.assertEqual([], cert_type.certTypes) def test_create_with_list(self): cert_type = ClientCertTypeExtension() cert_type = cert_type.create([0]) self.assertEqual(bytearray(b'\x01\x00'), cert_type.extData) self.assertEqual([0], cert_type.certTypes) def test_write(self): cert_type = ClientCertTypeExtension() cert_type = cert_type.create([0, 1]) self.assertEqual(bytearray( b'\x00\x09' + b'\x00\x03' + b'\x02' + b'\x00\x01'), cert_type.write()) def test_parse(self): cert_type = ClientCertTypeExtension() p = Parser(bytearray(b'\x00')) cert_type = cert_type.parse(p) self.assertEqual(9, cert_type.extType) self.assertEqual([], cert_type.certTypes) def test_parse_with_list(self): cert_type = ClientCertTypeExtension() p = Parser(bytearray(b'\x02\x01\x00')) cert_type = cert_type.parse(p) self.assertEqual([1, 0], cert_type.certTypes) def test_parse_with_length_long_by_one(self): cert_type = ClientCertTypeExtension() p = Parser(bytearray(b'\x03\x01\x00')) with self.assertRaises(SyntaxError): cert_type.parse(p) def test___repr__(self): cert_type = ClientCertTypeExtension() cert_type = cert_type.create([0, 1]) self.assertEqual("ClientCertTypeExtension(certTypes=[0, 1])", repr(cert_type)) class TestServerCertTypeExtension(unittest.TestCase): def test___init__(self): cert_type = ServerCertTypeExtension() self.assertEqual(9, cert_type.extType) self.assertEqual(bytearray(0), cert_type.extData) self.assertIsNone(cert_type.cert_type) def test_create(self): cert_type = ServerCertTypeExtension().create(0) self.assertEqual(9, cert_type.extType) self.assertEqual(bytearray(b'\x00'), cert_type.extData) self.assertEqual(0, cert_type.cert_type) def test_parse(self): p = Parser(bytearray( b'\x00' # certificate type - X.509 (0) )) cert_type = ServerCertTypeExtension().parse(p) self.assertEqual(0, cert_type.cert_type) def test_parse_with_no_data(self): p = Parser(bytearray(0)) cert_type = ServerCertTypeExtension() with self.assertRaises(SyntaxError): cert_type.parse(p) def test_parse_with_too_much_data(self): p = Parser(bytearray(b'\x00\x00')) cert_type = ServerCertTypeExtension() with self.assertRaises(SyntaxError): cert_type.parse(p) def test_write(self): cert_type = ServerCertTypeExtension().create(1) self.assertEqual(bytearray( b'\x00\x09' + # extension type - cert_type (9) b'\x00\x01' + # extension length - 1 byte b'\x01' # selected certificate type - OpenPGP (1) ), cert_type.write()) def test___repr__(self): cert_type = ServerCertTypeExtension().create(1) self.assertEqual("ServerCertTypeExtension(cert_type=1)", repr(cert_type)) class TestSRPExtension(unittest.TestCase): def test___init___(self): srp_extension = SRPExtension() self.assertIsNone(srp_extension.identity) self.assertEqual(12, srp_extension.extType) self.assertEqual(bytearray(0), srp_extension.extData) def test_create(self): srp_extension = SRPExtension() srp_extension = srp_extension.create() self.assertIsNone(srp_extension.identity) self.assertEqual(12, srp_extension.extType) self.assertEqual(bytearray(0), srp_extension.extData) def test_create_with_name(self): srp_extension = SRPExtension() srp_extension = srp_extension.create(bytearray(b'username')) self.assertEqual(bytearray(b'username'), srp_extension.identity) self.assertEqual(bytearray( b'\x08' + # length of string - 8 bytes b'username'), srp_extension.extData) def test_create_with_too_long_name(self): srp_extension = SRPExtension() with self.assertRaises(ValueError): srp_extension = srp_extension.create(bytearray(b'a'*256)) def test_write(self): srp_extension = SRPExtension() srp_extension = srp_extension.create(bytearray(b'username')) self.assertEqual(bytearray( b'\x00\x0c' + # type of extension - SRP (12) b'\x00\x09' + # length of extension - 9 bytes b'\x08' + # length of encoded name b'username'), srp_extension.write()) def test_parse(self): srp_extension = SRPExtension() p = Parser(bytearray(b'\x00')) srp_extension = srp_extension.parse(p) self.assertEqual(bytearray(0), srp_extension.identity) def test_parse(self): srp_extension = SRPExtension() p = Parser(bytearray( b'\x08' + b'username')) srp_extension = srp_extension.parse(p) self.assertEqual(bytearray(b'username'), srp_extension.identity) def test_parse_with_length_long_by_one(self): srp_extension = SRPExtension() p = Parser(bytearray( b'\x09' + b'username')) with self.assertRaises(SyntaxError): srp_extension = srp_extension.parse(p) def test___repr__(self): srp_extension = SRPExtension() srp_extension = srp_extension.create(bytearray(b'user')) self.assertEqual("SRPExtension(identity=bytearray(b'user'))", repr(srp_extension)) class TestNPNExtension(unittest.TestCase): def test___init___(self): npn_extension = NPNExtension() self.assertIsNone(npn_extension.protocols) self.assertEqual(13172, npn_extension.extType) self.assertEqual(bytearray(0), npn_extension.extData) def test_create(self): npn_extension = NPNExtension() npn_extension = npn_extension.create() self.assertIsNone(npn_extension.protocols) self.assertEqual(13172, npn_extension.extType) self.assertEqual(bytearray(0), npn_extension.extData) def test_create_with_list_of_protocols(self): npn_extension = NPNExtension() npn_extension = npn_extension.create([ bytearray(b'http/1.1'), bytearray(b'spdy/3')]) self.assertEqual([ bytearray(b'http/1.1'), bytearray(b'spdy/3')], npn_extension.protocols) self.assertEqual(bytearray( b'\x08' + # length of name of protocol # utf-8 encoding of "http/1.1" b'\x68\x74\x74\x70\x2f\x31\x2e\x31' + b'\x06' + # length of name of protocol # utf-8 encoding of "http/1.1" b'\x73\x70\x64\x79\x2f\x33' ), npn_extension.extData) def test_write(self): npn_extension = NPNExtension().create() self.assertEqual(bytearray( b'\x33\x74' + # type of extension - NPN b'\x00\x00' # length of extension ), npn_extension.write()) def test_write_with_list(self): npn_extension = NPNExtension() npn_extensnio = npn_extension.create([ bytearray(b'http/1.1'), bytearray(b'spdy/3')]) self.assertEqual(bytearray( b'\x33\x74' + # type of extension - NPN b'\x00\x10' + # length of extension b'\x08' + # length of name of protocol # utf-8 encoding of "http/1.1" b'\x68\x74\x74\x70\x2f\x31\x2e\x31' + b'\x06' + # length of name of protocol # utf-8 encoding of "spdy/3" b'\x73\x70\x64\x79\x2f\x33' ), npn_extension.write()) def test_parse(self): npn_extension = NPNExtension() p = Parser(bytearray(0)) npn_extension = npn_extension.parse(p) self.assertEqual(bytearray(0), npn_extension.extData) self.assertEqual([], npn_extension.protocols) def test_parse_with_procotol(self): npn_extension = NPNExtension() p = Parser(bytearray( b'\x08' + # length of name b'\x68\x74\x74\x70\x2f\x31\x2e\x31')) npn_extension = npn_extension.parse(p) self.assertEqual([bytearray(b'http/1.1')], npn_extension.protocols) def test_parse_with_protocol_length_short_by_one(self): npn_extension = NPNExtension() p = Parser(bytearray( b'\x07' + # length of name - 7 (short by one) b'\x68\x74\x74\x70\x2f\x31\x2e\x31')) with self.assertRaises(SyntaxError): npn_extension.parse(p) def test_parse_with_protocol_length_long_by_one(self): npn_extension = NPNExtension() p = Parser(bytearray( b'\x09' + # length of name - 9 (short by one) b'\x68\x74\x74\x70\x2f\x31\x2e\x31')) with self.assertRaises(SyntaxError): npn_extension.parse(p) def test___repr__(self): npn_extension = NPNExtension().create([bytearray(b'http/1.1')]) self.assertEqual("NPNExtension(protocols=[bytearray(b'http/1.1')])", repr(npn_extension)) class TestTACKExtension(unittest.TestCase): def test___init__(self): tack_ext = TACKExtension() self.assertEqual([], tack_ext.tacks) self.assertEqual(0, tack_ext.activation_flags) self.assertEqual(62208, tack_ext.extType) self.assertEqual(bytearray(b'\x00\x00\x00'), tack_ext.extData) def test_create(self): tack_ext = TACKExtension().create([], 1) self.assertEqual([], tack_ext.tacks) self.assertEqual(1, tack_ext.activation_flags) def test_tack___init__(self): tack = TACKExtension.TACK() self.assertEqual(bytearray(64), tack.public_key) self.assertEqual(0, tack.min_generation) self.assertEqual(0, tack.generation) self.assertEqual(0, tack.expiration) self.assertEqual(bytearray(32), tack.target_hash) self.assertEqual(bytearray(64), tack.signature) def test_tack_create(self): tack = TACKExtension.TACK().create( bytearray(b'\x01'*64), 2, 3, 4, bytearray(b'\x05'*32), bytearray(b'\x06'*64)) self.assertEqual(bytearray(b'\x01'*64), tack.public_key) self.assertEqual(2, tack.min_generation) self.assertEqual(3, tack.generation) self.assertEqual(4, tack.expiration) self.assertEqual(bytearray(b'\x05'*32), tack.target_hash) self.assertEqual(bytearray(b'\x06'*64), tack.signature) def test_tack_write(self): tack = TACKExtension.TACK().create( bytearray(b'\x01'*64), 2, 3, 4, bytearray(b'\x05'*32), bytearray(b'\x06'*64)) self.assertEqual(bytearray( b'\x01'*64 + # public_key b'\x02' + # min_generation b'\x03' + # generation b'\x00\x00\x00\x04' + # expiration b'\x05'*32 + # target_hash b'\x06'*64) # signature , tack.write()) def test_tack_write_with_bad_length_public_key(self): tack = TACKExtension.TACK().create( bytearray(b'\x01'*65), 2, 3, 4, bytearray(b'\x05'*32), bytearray(b'\x06'*64)) with self.assertRaises(TLSInternalError): tack.write() def test_tack_write_with_bad_length_target_hash(self): tack = TACKExtension.TACK().create( bytearray(b'\x01'*64), 2, 3, 4, bytearray(b'\x05'*33), bytearray(b'\x06'*64)) with self.assertRaises(TLSInternalError): tack.write() def test_tack_write_with_bad_length_signature(self): tack = TACKExtension.TACK().create( bytearray(b'\x01'*64), 2, 3, 4, bytearray(b'\x05'*32), bytearray(b'\x06'*65)) with self.assertRaises(TLSInternalError): tack.write() def test_tack_parse(self): p = Parser(bytearray( b'\x01'*64 + # public_key b'\x02' + # min_generation b'\x03' + # generation b'\x00\x00\x00\x04' + # expiration b'\x05'*32 + # target_hash b'\x06'*64)) # signature tack = TACKExtension.TACK() tack = tack.parse(p) self.assertEqual(bytearray(b'\x01'*64), tack.public_key) self.assertEqual(2, tack.min_generation) self.assertEqual(3, tack.generation) self.assertEqual(4, tack.expiration) self.assertEqual(bytearray(b'\x05'*32), tack.target_hash) self.assertEqual(bytearray(b'\x06'*64), tack.signature) def test_tack___eq__(self): a = TACKExtension.TACK() b = TACKExtension.TACK() self.assertTrue(a == b) self.assertFalse(a == None) self.assertFalse(a == "test") def test_tack___eq___with_different_tacks(self): a = TACKExtension.TACK() b = TACKExtension.TACK().create( bytearray(b'\x01'*64), 2, 3, 4, bytearray(b'\x05'*32), bytearray(b'\x06'*64)) self.assertFalse(a == b) def test_extData(self): tack = TACKExtension.TACK().create( bytearray(b'\x01'*64), 2, 3, 4, bytearray(b'\x05'*32), bytearray(b'\x06'*64)) tack_ext = TACKExtension().create([tack], 1) self.assertEqual(bytearray( b'\x00\xa6' + # length b'\x01'*64 + # public_key b'\x02' + # min_generation b'\x03' + # generation b'\x00\x00\x00\x04' + # expiration b'\x05'*32 + # target_hash b'\x06'*64 + # signature b'\x01' # activation flag ), tack_ext.extData) def test_parse(self): p = Parser(bytearray(3)) tack_ext = TACKExtension().parse(p) self.assertEqual([], tack_ext.tacks) self.assertEqual(0, tack_ext.activation_flags) def test_parse_with_a_tack(self): p = Parser(bytearray( b'\x00\xa6' + # length of array (166 bytes) b'\x01'*64 + # public_key b'\x02' + # min_generation b'\x03' + # generation b'\x00\x00\x00\x04' + # expiration b'\x05'*32 + # target_hash b'\x06'*64 + # signature b'\x01')) # activation_flags tack_ext = TACKExtension().parse(p) tack = TACKExtension.TACK().create( bytearray(b'\x01'*64), 2, 3, 4, bytearray(b'\x05'*32), bytearray(b'\x06'*64)) self.assertEqual([tack], tack_ext.tacks) self.assertEqual(1, tack_ext.activation_flags) def test___repr__(self): tack = TACKExtension.TACK().create( bytearray(b'\x00'), 1, 2, 3, bytearray(b'\x04'), bytearray(b'\x05')) tack_ext = TACKExtension().create([tack], 1) self.maxDiff = None self.assertEqual("TACKExtension(activation_flags=1, tacks=["\ "TACK(public_key=bytearray(b'\\x00'), min_generation=1, "\ "generation=2, expiration=3, target_hash=bytearray(b'\\x04'), "\ "signature=bytearray(b'\\x05'))"\ "])", repr(tack_ext)) class TestSupportedGroups(unittest.TestCase): def test___init__(self): ext = SupportedGroupsExtension() self.assertIsNotNone(ext) self.assertIsNone(ext.groups) def test_write(self): ext = SupportedGroupsExtension() ext.create([19, 21]) self.assertEqual(bytearray( b'\x00\x0A' + # type of extension - 10 b'\x00\x06' + # overall length of extension b'\x00\x04' + # length of extension list array b'\x00\x13' + # secp192r1 b'\x00\x15' # secp224r1 ), ext.write()) def test_write_empty(self): ext = SupportedGroupsExtension() self.assertEqual(bytearray(b'\x00\x0A\x00\x00'), ext.write()) def test_parse(self): parser = Parser(bytearray( b'\x00\x04' + # length of extension list array b'\x00\x13' + # secp192r1 b'\x00\x15' # secp224r1 )) ext = SupportedGroupsExtension().parse(parser) self.assertEqual(ext.extType, ExtensionType.supported_groups) self.assertEqual(ext.groups, [GroupName.secp192r1, GroupName.secp224r1]) for group in ext.groups: self.assertTrue(group in GroupName.allEC) self.assertFalse(group in GroupName.allFF) def test_parse_with_empty_data(self): parser = Parser(bytearray()) ext = SupportedGroupsExtension().parse(parser) self.assertEqual(ext.extType, ExtensionType.supported_groups) self.assertIsNone(ext.groups) def test_parse_with_empty_array(self): parser = Parser(bytearray(2)) ext = SupportedGroupsExtension().parse(parser) self.assertEqual([], ext.groups) def test_parse_with_invalid_data(self): parser = Parser(bytearray(b'\x00\x01\x00')) ext = SupportedGroupsExtension() with self.assertRaises(SyntaxError): ext.parse(parser) class TestECPointFormatsExtension(unittest.TestCase): def test___init__(self): ext = ECPointFormatsExtension() self.assertIsNotNone(ext) self.assertEqual(ext.extData, bytearray(0)) self.assertEqual(ext.extType, 11) def test_write(self): ext = ECPointFormatsExtension() ext.create([ECPointFormat.ansiX962_compressed_prime]) self.assertEqual(bytearray( b'\x00\x0b' + # type of extension b'\x00\x02' + # overall length b'\x01' + # length of list b'\x01'), ext.write()) def test_parse(self): parser = Parser(bytearray(b'\x01\x00')) ext = ECPointFormatsExtension() self.assertIsNone(ext.formats) ext.parse(parser) self.assertEqual(ext.formats, [ECPointFormat.uncompressed]) def test_parse_with_empty_data(self): parser = Parser(bytearray(0)) ext = ECPointFormatsExtension() ext.parse(parser) self.assertIsNone(ext.formats) class TestSignatureAlgorithmsExtension(unittest.TestCase): def test__init__(self): ext = SignatureAlgorithmsExtension() self.assertIsNotNone(ext) self.assertIsNone(ext.sigalgs) self.assertEqual(ext.extType, 13) self.assertEqual(ext.extData, bytearray(0)) def test_write(self): ext = SignatureAlgorithmsExtension() ext.create([(HashAlgorithm.sha1, SignatureAlgorithm.rsa), (HashAlgorithm.sha256, SignatureAlgorithm.rsa)]) self.assertEqual(bytearray( b'\x00\x0d' + # type of extension b'\x00\x06' + # overall length of extension b'\x00\x04' + # array length b'\x02\x01' + # SHA1+RSA b'\x04\x01' # SHA256+RSA ), ext.write()) def test_parse_with_empty_data(self): parser = Parser(bytearray(0)) ext = SignatureAlgorithmsExtension() ext.parse(parser) self.assertIsNone(ext.sigalgs) def test_parse_with_extra_data_at_end(self): parser = Parser(bytearray( b'\x00\x02' + # array length b'\x04\x01' + # SHA256+RSA b'\xff\xff')) # padding ext = SignatureAlgorithmsExtension() with self.assertRaises(SyntaxError): ext.parse(parser) if __name__ == '__main__': unittest.main() tlslite-ng-0.5.1/unit_tests/test_tlslite_handshakehashes.py000066400000000000000000000052551261665411200243060ustar00rootroot00000000000000# Copyright (c) 2014, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. # compatibility with Python 2.6, for that we need unittest2 package, # which is not available on 3.3 or 3.4 try: import unittest2 as unittest except ImportError: import unittest from tlslite.handshakehashes import HandshakeHashes class TestHandshakeHashes(unittest.TestCase): def test___init__(self): hh = HandshakeHashes() self.assertIsNotNone(hh) def test_update(self): hh = HandshakeHashes() hh.update(bytearray(10)) def test_update_with_str(self): hh = HandshakeHashes() hh.update(b'text') def test_digest_SSL3(self): hh = HandshakeHashes() self.assertEqual(bytearray( b'\xb5Q\x15\xa4\xcd\xff\xfdF\xa6\x9c\xe2\x0f\x83~\x948\xc3\xb5'\ b'\xc1\x8d\xb6|\x10n@a\x97\xccG\xfeI\xa8s T\\'), hh.digestSSL(bytearray(48), b'')) def test_digest_TLS1_0(self): hh = HandshakeHashes() self.assertEqual( b'\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\t\x98\xec\xf8B~\xda'\ b'9\xa3\xee^kK\r2U\xbf\xef\x95`\x18\x90\xaf\xd8\x07\t', hh.digest()) def test_copy(self): hh = HandshakeHashes() hh.update(b'text') hh2 = hh.copy() self.assertEqual(hh2.digest(), hh.digest()) def test_digest_md5(self): hh = HandshakeHashes() self.assertEqual( b"\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\t\x98\xec\xf8B~", hh.digest('md5')) def test_digest_sha1(self): hh = HandshakeHashes() self.assertEqual( b"\xda9\xa3\xee^kK\r2U\xbf\xef\x95`\x18\x90\xaf\xd8\x07\t", hh.digest('sha1')) def test_digest_sha256(self): hh = HandshakeHashes() self.assertEqual( b"\xe3\xb0\xc4B\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99o\xb9$'\xae"\ b"A\xe4d\x9b\x93L\xa4\x95\x99\x1bxR\xb8U", hh.digest('sha256')) def test_digest_with_partial_writes(self): hh = HandshakeHashes() hh.update(b'text') hh2 = HandshakeHashes() hh2.update(b'te') hh2.update(b'xt') self.assertEqual(hh.digest(), hh2.digest()) def test_digest_with_invalid_hash(self): hh = HandshakeHashes() with self.assertRaises(ValueError): hh.digest('md2') def test_digest_with_repeated_calls(self): hh = HandshakeHashes() hh.update(b'text') self.assertEqual(hh.digest(), hh.digest()) hh.update(b'ext') self.assertEqual(hh.digest('sha256'), hh.digest('sha256')) tlslite-ng-0.5.1/unit_tests/test_tlslite_handshakesettings.py000066400000000000000000000115621261665411200246710ustar00rootroot00000000000000# Author: Hubert Kario (c) 2014 # see LICENCE file for legal information regarding use of this file # compatibility with Python 2.6, for that we need unittest2 package, # which is not available on 3.3 or 3.4 try: import unittest2 as unittest except ImportError: import unittest from tlslite.handshakesettings import HandshakeSettings class TestHandshakeSettings(unittest.TestCase): def test___init__(self): hs = HandshakeSettings() self.assertIsNotNone(hs) def test_validate(self): hs = HandshakeSettings() newHS = hs.validate() self.assertIsNotNone(newHS) self.assertIsNot(hs, newHS) def test_minKeySize_too_small(self): hs = HandshakeSettings() hs.minKeySize = 511 with self.assertRaises(ValueError): hs.validate() def test_minKeySize_too_large(self): hs = HandshakeSettings() hs.minKeySize = 16385 with self.assertRaises(ValueError): hs.validate() def test_maxKeySize_too_small(self): hs = HandshakeSettings() hs.maxKeySize = 511 with self.assertRaises(ValueError): hs.validate() def test_maxKeySize_too_large(self): hs = HandshakeSettings() hs.maxKeySize = 16385 with self.assertRaises(ValueError): hs.validate() def test_maxKeySize_smaller_than_minKeySize(self): hs = HandshakeSettings() hs.maxKeySize = 1024 hs.minKeySize = 2048 with self.assertRaises(ValueError): hs.validate() def test_cipherNames_with_unknown_name(self): hs = HandshakeSettings() hs.cipherNames = ["aes256"] newHs = hs.validate() self.assertEqual(["aes256"], newHs.cipherNames) def test_cipherNames_with_unknown_name(self): hs = HandshakeSettings() hs.cipherNames = ["camellia256gcm", "aes256"] with self.assertRaises(ValueError): hs.validate() def test_cipherNames_empty(self): hs = HandshakeSettings() hs.cipherNames = [] with self.assertRaises(ValueError): hs.validate() def test_certificateTypes_empty(self): hs = HandshakeSettings() hs.certificateTypes = [] with self.assertRaises(ValueError): hs.validate() def test_certificateTypes_with_unknown_type(self): hs = HandshakeSettings() hs.certificateTypes = [0, 42] with self.assertRaises(ValueError): hs.validate() def test_cipherImplementations_empty(self): hs = HandshakeSettings() hs.cipherImplementations = [] with self.assertRaises(ValueError): hs.validate() def test_cipherImplementations_with_unknown_implementations(self): hs = HandshakeSettings() hs.cipherImplementations = ["openssl", "NSS"] with self.assertRaises(ValueError): hs.validate() def test_minVersion_higher_than_maxVersion(self): hs = HandshakeSettings() hs.minVersion = (3, 3) hs.maxVersion = (3, 0) with self.assertRaises(ValueError): hs.validate() def test_minVersion_with_unknown_version(self): hs = HandshakeSettings() hs.minVersion = (2, 0) with self.assertRaises(ValueError): hs.validate() def test_maxVersion_with_unknown_version(self): hs = HandshakeSettings() hs.maxVersion = (3, 4) with self.assertRaises(ValueError): hs.validate() def test_maxVersion_without_TLSv1_2(self): hs = HandshakeSettings() hs.maxVersion = (3, 2) self.assertTrue('sha256' in hs.macNames) new_hs = hs.validate() self.assertFalse("sha256" in new_hs.macNames) def test_getCertificateTypes(self): hs = HandshakeSettings() self.assertEqual([0], hs.getCertificateTypes()) def test_getCertificateTypes_with_unsupported_type(self): hs = HandshakeSettings() hs.certificateTypes = ["x509", "openpgp"] with self.assertRaises(AssertionError): hs.getCertificateTypes() def test_useEncryptThenMAC(self): hs = HandshakeSettings() self.assertTrue(hs.useEncryptThenMAC) hs.useEncryptThenMAC = False n_hs = hs.validate() self.assertFalse(n_hs.useEncryptThenMAC) def test_useEncryptThenMAC_with_wrong_value(self): hs = HandshakeSettings() hs.useEncryptThenMAC = None with self.assertRaises(ValueError): hs.validate() def test_invalid_MAC(self): hs = HandshakeSettings() hs.macNames = ['sha1', 'whirpool'] with self.assertRaises(ValueError): hs.validate() def test_invalid_KEX(self): hs = HandshakeSettings() hs.keyExchangeNames = ['rsa', 'ecdhe_rsa', 'gost'] with self.assertRaises(ValueError): hs.validate() tlslite-ng-0.5.1/unit_tests/test_tlslite_mathtls.py000066400000000000000000000141061261665411200226330ustar00rootroot00000000000000# Copyright (c) 2014, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. # compatibility with Python 2.6, for that we need unittest2 package, # which is not available on 3.3 or 3.4 try: import unittest2 as unittest except ImportError: import unittest from tlslite.mathtls import PRF_1_2, calcMasterSecret, calcFinished from tlslite.handshakehashes import HandshakeHashes from tlslite.constants import CipherSuite class TestCalcMasterSecret(unittest.TestCase): def test_with_empty_values(self): ret = calcMasterSecret((3, 3), 0, bytearray(48), bytearray(32), bytearray(32)) self.assertEqual(bytearray( b'I\xcf\xae\xe5[\x86\x92\xd3\xbbm\xd6\xeekSo/' + b'\x17\xaf\xbc\x84\x18\tGc\xbc\xb5\xbe\xd6\xb0\x05\xad\xf8' + b'\x88\xd0`\xe4\x8c^\xb2&ls\xcb\x1a=-Kh' ), ret) self.assertEqual(48, len(ret)) class TestPRF1_2(unittest.TestCase): def test_with_bogus_values(self): ret = PRF_1_2(bytearray(1), b"key expansion", bytearray(1), 10) self.assertEqual(bytearray(b'\xaa2\xca\r\x8b\x85N\xad?\xab'), ret) def test_with_realistic_values(self): ret = PRF_1_2(bytearray(48), b"key expansion", bytearray(64), 16) self.assertEqual(bytearray(b'S\xb5\xdb\xc8T }u)BxuB\xe4\xeb\xeb'), ret) class TestCalcFinished(unittest.TestCase): def setUp(self): self.hhashes = HandshakeHashes() self.hhashes.update(bytearray(10)) class TestCalcFinishedInSSL3(TestCalcFinished): def setUp(self): super(TestCalcFinishedInSSL3, self).setUp() self.finished = calcFinished((3, 0), bytearray(48), 0, self.hhashes, True) def test_client_value(self): self.assertEqual(bytearray( b'\x15\xa9\xd7\xf1\x8bV\xecY\xab\xee\xbaS\x9c}\xffW\xa0'+ b'\xa8\\q\xe5x8"\xf4\xedp\xabl\x8aV\xd9G\xab\x0fz'), self.finished) def test_server_value(self): ret = calcFinished((3, 0), bytearray(48), 0, self.hhashes, False) self.assertEqual(bytearray( b'\xe3^aCb\x8a\xfc\x98\xbf\xd7\x08\xddX\xdc[\xeac\x02\xdb'+ b'\x9b\x8aN\xed\xed\xaaZ\xcb\xda"\x87K\xff\x89m\xa9/'), ret) def test_if_multiple_runs_are_the_same(self): ret2 = calcFinished((3, 0), bytearray(48), 0, self.hhashes, True) self.assertEqual(self.finished, ret2) def test_if_client_and_server_values_differ(self): ret_srv = calcFinished((3, 0), bytearray(48), 0, self.hhashes, False) self.assertNotEqual(self.finished, ret_srv) class TestCalcFinishedInTLS1_0(TestCalcFinished): def setUp(self): super(TestCalcFinishedInTLS1_0, self).setUp() self.finished = calcFinished((3, 1), bytearray(48), 0, self.hhashes, True) def test_client_value(self): self.assertEqual(12, len(self.finished)) self.assertEqual(bytearray( b'\xf8N\x8a\x8dx\xb8\xfe\x9e1\x0b\x8a#'), self.finished) def test_server_value(self): ret_srv = calcFinished((3, 1), bytearray(48), 0, self.hhashes, False) self.assertEqual(12, len(ret_srv)) self.assertEqual(bytearray( b'kYB\xce \x7f\xbb\xee\xe5\xe7<\x9d'), ret_srv) def test_if_client_and_server_values_differ(self): ret_srv = calcFinished((3, 1), bytearray(48), 0, self.hhashes, False) self.assertNotEqual(self.finished, ret_srv) def test_if_values_for_TLS1_0_and_TLS1_0_are_same(self): ret = calcFinished((3, 2), bytearray(48), 0, self.hhashes, True) self.assertEqual(self.finished, ret) class TestCalcFinishedInTLS1_2WithSHA256(TestCalcFinished): def setUp(self): super(TestCalcFinishedInTLS1_2WithSHA256, self).setUp() self.finished = calcFinished((3, 3), bytearray(48), 0, self.hhashes, True) def test_client_value(self): self.assertEqual(12, len(self.finished)) self.assertEqual(bytearray( b'\x8e\x8c~\x03lU$S\x9fz\\\xcc'), self.finished) def test_server_value(self): ret_srv = calcFinished((3, 3), bytearray(48), 0, self.hhashes, False) self.assertEqual(12, len(self.finished)) self.assertEqual(bytearray( b'\xa8\xf1\xdf8s|\xedU\\Z=U'), ret_srv) def test_if_client_and_server_values_differ(self): ret_srv = calcFinished((3, 3), bytearray(48), 0, self.hhashes, False) self.assertNotEqual(ret_srv, self.finished) class TestCalcFinishedInTLS1_2WithSHA384(TestCalcFinished): def setUp(self): super(TestCalcFinishedInTLS1_2WithSHA384, self).setUp() self.finished = calcFinished((3, 3), bytearray(48), CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, self.hhashes, True) def test_client_value(self): self.assertEqual(12, len(self.finished)) self.assertEqual(bytearray( b'UB\xeeq\x86\xa5\x88L \x04\x893'), self.finished) def test_server_value(self): ret_srv = calcFinished((3, 3), bytearray(48), CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, self.hhashes, False) self.assertEqual(bytearray( b'\x02St\x13\xa8\xe6\xb6\xa2\x1c4\xff\xc5'), ret_srv) def test_if_client_and_server_values_differ(self): ret_srv = calcFinished((3, 3), bytearray(48), CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, self.hhashes, False) self.assertNotEqual(self.finished, ret_srv) tlslite-ng-0.5.1/unit_tests/test_tlslite_messages.py000066400000000000000000002167261261665411200230020ustar00rootroot00000000000000# Author: Hubert Kario (c) 2014 # see LICENCE file for legal information regarding use of this file # compatibility with Python 2.6, for that we need unittest2 package, # which is not available on 3.3 or 3.4 try: import unittest2 as unittest except ImportError: import unittest from tlslite.messages import ClientHello, ServerHello, RecordHeader3, Alert, \ RecordHeader2, Message, ClientKeyExchange, ServerKeyExchange, \ CertificateRequest, CertificateVerify from tlslite.utils.codec import Parser from tlslite.constants import CipherSuite, CertificateType, ContentType, \ AlertLevel, AlertDescription, ExtensionType, ClientCertificateType, \ HashAlgorithm, SignatureAlgorithm from tlslite.extensions import SNIExtension, ClientCertTypeExtension, \ SRPExtension, TLSExtension from tlslite.errors import TLSInternalError class TestMessage(unittest.TestCase): def test___init__(self): msg = Message(ContentType.application_data, bytearray(0)) self.assertEqual(ContentType.application_data, msg.contentType) self.assertEqual(bytearray(0), msg.data) def test_write(self): msg = Message(0, bytearray(10)) self.assertEqual(bytearray(10), msg.write()) class TestClientHello(unittest.TestCase): def test___init__(self): client_hello = ClientHello() assert client_hello self.assertEqual(False, client_hello.ssl2) self.assertEqual((0,0), client_hello.client_version) self.assertEqual(bytearray(32), client_hello.random) self.assertEqual(bytearray(0), client_hello.session_id) self.assertEqual([], client_hello.cipher_suites) self.assertEqual([], client_hello.compression_methods) def test_create(self): client_hello = ClientHello() client_hello.create((3,0), bytearray(32), bytearray(0), \ []) self.assertEqual((3,0), client_hello.client_version) self.assertEqual(bytearray(32), client_hello.random) self.assertEqual(bytearray(0), client_hello.session_id) self.assertEqual([], client_hello.cipher_suites) self.assertEqual([0], client_hello.compression_methods) def test_create_with_one_ciphersuite(self): client_hello = ClientHello() client_hello.create((3,0), bytearray(32), bytearray(0), \ [CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]) self.assertEqual((3,0), client_hello.client_version) self.assertEqual(bytearray(32), client_hello.random) self.assertEqual(bytearray(0), client_hello.session_id) self.assertEqual([CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV], \ client_hello.cipher_suites) self.assertEqual([0], client_hello.compression_methods) def test_create_with_random(self): client_hello = ClientHello() client_hello.create((3,0), bytearray(b'\x01' + \ b'\x00'*30 + b'\x02'), bytearray(0), \ []) self.assertEqual((3,0), client_hello.client_version) self.assertEqual(bytearray(b'\x01' + b'\x00'*30 + b'\x02'), \ client_hello.random) self.assertEqual(bytearray(0), client_hello.session_id) self.assertEqual([], client_hello.cipher_suites) self.assertEqual([0], client_hello.compression_methods) def test_parse(self): p = Parser(bytearray( # we don't include the type of message as it is handled by the # hello protocol parser #b'x01' + # type of message - client_hello b'\x00'*2 + b'\x26' + # length - 38 bytes b'\x01\x01' + # protocol version - arbitrary (invalid) b'\x00'*32 + # client random b'\x00' + # session ID length b'\x00'*2 + # cipher suites length b'\x00' # compression methods length )) client_hello = ClientHello() client_hello = client_hello.parse(p) self.assertEqual((1,1), client_hello.client_version) self.assertEqual(bytearray(32), client_hello.random) self.assertEqual(bytearray(0), client_hello.session_id) self.assertEqual([], client_hello.cipher_suites) self.assertEqual([], client_hello.compression_methods) self.assertEqual(bytearray(0), client_hello.server_name) # XXX not sent self.assertEqual([0], client_hello.certificate_types) self.assertEqual(False, client_hello.supports_npn) self.assertEqual(False, client_hello.tack) self.assertEqual(None, client_hello.srp_username) self.assertEqual(None, client_hello.extensions) def test_parse_with_empty_extensions(self): p = Parser(bytearray( # we don't include the type of message as it is handled by the # hello protocol parser #b'x01' + # type of message - client_hello b'\x00'*2 + b'\x28' + # length - 38 bytes b'\x01\x01' + # protocol version - arbitrary (invalid) b'\x00'*32 + # client random b'\x00' + # session ID length b'\x00'*2 + # cipher suites length b'\x00' + # compression methods length b'\x00\x00' # extensions length )) client_hello = ClientHello() client_hello = client_hello.parse(p) self.assertEqual((1,1), client_hello.client_version) self.assertEqual(bytearray(32), client_hello.random) self.assertEqual(bytearray(0), client_hello.session_id) self.assertEqual([], client_hello.cipher_suites) self.assertEqual([], client_hello.compression_methods) self.assertEqual([], client_hello.extensions) def test_parse_with_SNI_extension(self): p = Parser(bytearray( # we don't include the type of message as it is handled by the # hello protocol parser #b'x01' + # type of message - client_hello b'\x00'*2 + b'\x3c' + # length - 60 bytes b'\x01\x01' + # protocol version - arbitrary (invalid) b'\x00'*32 + # client random b'\x00' + # session ID length b'\x00'*2 + # cipher suites length b'\x00' + # compression methods length b'\x00\x14' + # extensions length - 20 bytes b'\x00\x00' + # extension type - SNI (0) b'\x00\x10' + # extension length - 16 bytes b'\x00\x0e' + # length of array - 14 bytes b'\x00' + # type of entry - host_name (0) b'\x00\x0b' + # length of name - 11 bytes # UTF-8 encoding of example.com b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d' )) client_hello = ClientHello() client_hello = client_hello.parse(p) self.assertEqual((1,1), client_hello.client_version) self.assertEqual(bytearray(32), client_hello.random) self.assertEqual(bytearray(0), client_hello.session_id) self.assertEqual([], client_hello.cipher_suites) self.assertEqual([], client_hello.compression_methods) self.assertEqual(bytearray(b'example.com'), client_hello.server_name) sni = SNIExtension().create(bytearray(b'example.com')) self.assertEqual([sni], client_hello.extensions) def test_parse_with_cert_type_extension(self): p = Parser(bytearray( # we don't include the type of message as it is handled by the # hello protocol parser #b'x01' + # type of message - client_hello b'\x00'*2 + b'\x2f' + # length - 47 bytes b'\x01\x01' + # protocol version - arbitrary (invalid) b'\x00'*32 + # client random b'\x00' + # session ID length b'\x00'*2 + # cipher suites length b'\x00' + # compression methods length b'\x00\x07' + # extensions length - 7 bytes b'\x00\x09' + # extension type - certTypes (9) b'\x00\x03' + # extension length - 3 bytes b'\x02' + # length of array - 2 bytes b'\x00' + # type - x509 (0) b'\x01' # type - opengpg (1) )) client_hello = ClientHello() client_hello = client_hello.parse(p) self.assertEqual((1,1), client_hello.client_version) self.assertEqual(bytearray(32), client_hello.random) self.assertEqual(bytearray(0), client_hello.session_id) self.assertEqual([], client_hello.cipher_suites) self.assertEqual([], client_hello.compression_methods) self.assertEqual([0,1], client_hello.certificate_types) certTypes = ClientCertTypeExtension().create([0,1]) self.assertEqual([certTypes], client_hello.extensions) def test_parse_with_SRP_extension(self): p = Parser(bytearray( # we don't include the type of message as it is handled by the # hello protocol parser #b'x01' + # type of message - client_hello b'\x00'*2 + b'\x35' + # length - 53 bytes b'\x01\x01' + # protocol version - arbitrary (invalid) b'\x00'*32 + # client random b'\x00' + # session ID length b'\x00'*2 + # cipher suites length b'\x00' + # compression methods length b'\x00\x0d' + # extensions length - 13 bytes b'\x00\x0c' + # extension type - SRP (12) b'\x00\x09' + # extension length - 9 bytes b'\x08' + # length of name - 8 bytes b'username' # UTF-8 encoding of "username" :) )) client_hello = ClientHello() client_hello = client_hello.parse(p) self.assertEqual((1,1), client_hello.client_version) self.assertEqual(bytearray(32), client_hello.random) self.assertEqual(bytearray(0), client_hello.session_id) self.assertEqual([], client_hello.cipher_suites) self.assertEqual([], client_hello.compression_methods) self.assertEqual(bytearray(b'username'), client_hello.srp_username) srp = SRPExtension().create(bytearray(b'username')) self.assertEqual([srp], client_hello.extensions) def test_parse_with_NPN_extension(self): p = Parser(bytearray( # we don't include the type of message as it is handled by the # hello protocol parser #b'x01' + # type of message - client_hello b'\x00'*2 + b'\x2c' + # length - 44 bytes b'\x01\x01' + # protocol version - arbitrary (invalid) b'\x00'*32 + # client random b'\x00' + # session ID length b'\x00'*2 + # cipher suites length b'\x00' + # compression methods length b'\x00\x04' + # extensions length - 4 bytes b'\x33\x74' + # extension type - NPN (13172) b'\x00\x00' # extension length - 0 bytes )) client_hello = ClientHello() client_hello = client_hello.parse(p) self.assertEqual((1,1), client_hello.client_version) self.assertEqual(bytearray(32), client_hello.random) self.assertEqual(bytearray(0), client_hello.session_id) self.assertEqual([], client_hello.cipher_suites) self.assertEqual([], client_hello.compression_methods) self.assertEqual(True, client_hello.supports_npn) npn = TLSExtension().create(13172, bytearray(0)) self.assertEqual([npn], client_hello.extensions) def test_parse_with_TACK_extension(self): p = Parser(bytearray( # we don't include the type of message as it is handled by the # hello protocol parser #b'x01' + # type of message - client_hello b'\x00'*2 + b'\x2c' + # length - 44 bytes b'\x01\x01' + # protocol version - arbitrary (invalid) b'\x00'*32 + # client random b'\x00' + # session ID length b'\x00'*2 + # cipher suites length b'\x00' + # compression methods length b'\x00\x04' + # extensions length - 4 bytes b'\xf3\x00' + # extension type - TACK (62208) b'\x00\x00' # extension length - 0 bytes )) client_hello = ClientHello() client_hello = client_hello.parse(p) self.assertEqual((1,1), client_hello.client_version) self.assertEqual(bytearray(32), client_hello.random) self.assertEqual(bytearray(0), client_hello.session_id) self.assertEqual([], client_hello.cipher_suites) self.assertEqual([], client_hello.compression_methods) self.assertEqual(True, client_hello.tack) tack = TLSExtension().create(62208, bytearray(0)) self.assertEqual([tack], client_hello.extensions) def test_write(self): # client_hello = ClientHello(ssl2) client_hello = ClientHello() self.assertEqual(list(bytearray( b'\x01' + # type of message - client_hello b'\x00'*2 + b'\x26' + # length - 38 bytes b'\x00'*2 + # protocol version b'\x00'*32 + # client random b'\x00' + # session ID length b'\x00'*2 + # cipher suites length b'\x00' # compression methods length )), list(client_hello.write())) def test_write_with_certificate_types(self): # note that ClienHello is "clever" and doesn't send the extension # if only x509 certificate type is present, so we pass two values client_hello = ClientHello().create((3,1), bytearray(b'\x00'*31 + b'\xff'), bytearray(0), [], certificate_types=[ CertificateType.x509, CertificateType.openpgp]) self.assertEqual(list(bytearray( b'\x01' + # type of message - client_hello b'\x00'*2 + b'\x30' + # length - 48 bytes b'\x03\x01' + # protocol version (TLS 1.0) b'\x00'*31 + b'\xff' + # client random b'\x00' + # session ID length b'\x00\x00' + # cipher suites length b'\x01' + # compression methods length b'\x00' + # supported method - NULL b'\x00\x07' + # extensions length b'\x00\x09' + # cert_type extension value (9) b'\x00\x03' + # size of the extension b'\x02' + # length of supported types b'\x00' + # type - X.509 b'\x01' # type - OpenPGP )), list(client_hello.write())) def test_write_with_srp_username(self): client_hello = ClientHello().create((3,1), bytearray(b'\x00'*31 + b'\xff'), bytearray(0), [], srpUsername="example-test") self.assertEqual(list(bytearray( b'\x01' + # type of message - client_hello b'\x00'*2 + b'\x3a' + # length - 58 bytes b'\x03\x01' + # protocol version (TLS 1.0) b'\x00'*31 + b'\xff' + # client random b'\x00' + # session ID length b'\x00\x00' + # cipher suites length b'\x01' + # compression methods length b'\x00' + # supported method - NULL b'\x00\x11' + # extensions length b'\x00\x0c' + # srp extension value (12) b'\x00\x0d' + # size of the extension b'\x0c' + # length of name # ascii encoding of "example-test": b'\x65\x78\x61\x6d\x70\x6c\x65\x2d\x74\x65\x73\x74' )), list(client_hello.write())) def test_write_with_tack(self): client_hello = ClientHello().create((3,1), bytearray(b'\x00'*31 + b'\xff'), bytearray(0), [], tack=True) self.assertEqual(list(bytearray( b'\x01' + # type of message - client_hello b'\x00'*2 + b'\x2d' + # length - 45 bytes b'\x03\x01' + # protocol version b'\x00'*31 + b'\xff' + # client random b'\x00' + # session ID length b'\x00\x00' + # cipher suites length b'\x01' + # compression methods length b'\x00' + # supported method - NULL b'\x00\x04' + # extensions length b'\xf3\x00' + # TACK extension value (62208) b'\x00\x00' # size of the extension )), list(client_hello.write())) def test_write_with_npn(self): client_hello = ClientHello().create((3,1), bytearray(b'\x00'*31 + b'\xff'), bytearray(0), [], supports_npn=True) self.assertEqual(list(bytearray( b'\x01' + # type of message - client_hello b'\x00'*2 + b'\x2d' + # length - 45 bytes b'\x03\x01' + # protocol version b'\x00'*31 + b'\xff' + # client random b'\x00' + # session ID length b'\x00\x00' + # cipher suites length b'\x01' + # compression methods length b'\x00' + # supported method - NULL b'\x00\x04' + # extensions length b'\x33\x74' + # NPN extension value (13172) b'\x00\x00' # size of the extension )), list(client_hello.write())) def test_write_with_server_name(self): client_hello = ClientHello().create((3,1), bytearray(b'\x00'*31 + b'\xff'), bytearray(0), [], serverName="example.com") self.assertEqual(list(bytearray( b'\x01' + # type of message - client_hello b'\x00'*2 + b'\x3d' + # length - 61 bytes b'\x03\x01' + # protocol version b'\x00'*31 + b'\xff' + # client random b'\x00' + # session ID length b'\x00\x00' + # cipher suites length b'\x01' + # compression methods length b'\x00' + # supported method - NULL b'\x00\x14' + # extensions length b'\x00\x00' + # servername extension value (0) b'\x00\x10' + # byte size of the extension b'\x00\x0e' + # length of the list b'\x00' + # name type: host_name (0) b'\x00\x0b' + # length of host name # utf-8 encoding of "example.com" b'\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d' )), list(client_hello.write())) def test___str__(self): client_hello = ClientHello().create((3,0), bytearray(4), bytearray(0),\ []) self.assertEqual("client_hello,version(3.0),random(...),"\ "session ID(bytearray(b'')),cipher suites([]),"\ "compression methods([0])", str(client_hello)) def test___str___with_all_null_session_id(self): client_hello = ClientHello().create((3,0), bytearray(4), bytearray(10),\ []) self.assertEqual("client_hello,version(3.0),random(...),"\ "session ID(bytearray(b'\\x00'*10)),cipher suites([]),"\ "compression methods([0])", str(client_hello)) def test___str___with_extensions(self): client_hello = ClientHello().create((3,0), bytearray(4), bytearray(0),\ [], extensions=[TLSExtension().create(0, bytearray(b'\x00'))]) self.assertEqual("client_hello,version(3.0),random(...),"\ "session ID(bytearray(b'')),cipher suites([]),"\ "compression methods([0]),extensions(["\ "TLSExtension(extType=0, extData=bytearray(b'\\x00'), "\ "serverType=False)])", str(client_hello)) def test___repr__(self): client_hello = ClientHello().create((3,3), bytearray(1), bytearray(0),\ [], extensions=[TLSExtension().create(0, bytearray(0))]) self.assertEqual("ClientHello(ssl2=False, client_version=(3.3), "\ "random=bytearray(b'\\x00'), session_id=bytearray(b''), "\ "cipher_suites=[], compression_methods=[0], "\ "extensions=[TLSExtension(extType=0, "\ "extData=bytearray(b''), serverType=False)])", repr(client_hello)) def test_getExtension(self): client_hello = ClientHello().create((3, 3), bytearray(1), bytearray(0), [], extensions=[TLSExtension().create(0, bytearray(0))]) ext = client_hello.getExtension(1) self.assertIsNone(ext) def test_getExtension_with_present_id(self): client_hello = ClientHello().create((3, 3), bytearray(1), bytearray(0), [], extensions=[TLSExtension().create(0, bytearray(0))]) ext = client_hello.getExtension(0) self.assertEqual(ext, TLSExtension().create(0, bytearray(0))) def test_getExtension_with_duplicated_extensions(self): client_hello = ClientHello().create((3, 3), bytearray(1), bytearray(0), [], extensions=[TLSExtension().create(0, bytearray(0)), SNIExtension().create(b'localhost')]) with self.assertRaises(TLSInternalError): client_hello.getExtension(0) def test_certificate_types(self): client_hello = ClientHello().create((3, 3), bytearray(1), bytearray(0), []) self.assertEqual(client_hello.certificate_types, [0]) client_hello.certificate_types = [0, 1] self.assertEqual(client_hello.certificate_types, [0, 1]) client_hello.certificate_types = [0, 1, 2] self.assertEqual(client_hello.certificate_types, [0, 1, 2]) ext = client_hello.getExtension(ExtensionType.cert_type) self.assertEqual(ext.certTypes, [0, 1, 2]) def test_srp_username(self): client_hello = ClientHello().create((3, 3), bytearray(1), bytearray(0), []) self.assertIsNone(client_hello.srp_username) client_hello.srp_username = b'my-name' self.assertEqual(client_hello.srp_username, b'my-name') client_hello.srp_username = b'her-name' self.assertEqual(client_hello.srp_username, b'her-name') ext = client_hello.getExtension(ExtensionType.srp) self.assertEqual(ext.identity, b'her-name') def test_tack(self): client_hello = ClientHello().create((3, 3), bytearray(1), bytearray(0), []) self.assertFalse(client_hello.tack) client_hello.tack = True self.assertTrue(client_hello.tack) client_hello.tack = True self.assertTrue(client_hello.tack) ext = client_hello.getExtension(ExtensionType.tack) self.assertIsNotNone(ext) client_hello.tack = False self.assertFalse(client_hello.tack) ext = client_hello.getExtension(ExtensionType.tack) self.assertIsNone(ext) def test_supports_npn(self): client_hello = ClientHello().create((3, 3), bytearray(1), bytearray(0), []) self.assertFalse(client_hello.supports_npn) client_hello.supports_npn = True self.assertTrue(client_hello.supports_npn) client_hello.supports_npn = True self.assertTrue(client_hello.supports_npn) ext = client_hello.getExtension(ExtensionType.supports_npn) self.assertIsNotNone(ext) client_hello.supports_npn = False self.assertFalse(client_hello.supports_npn) ext = client_hello.getExtension(ExtensionType.supports_npn) self.assertIsNone(ext) def test_server_name(self): client_hello = ClientHello().create((3, 3), bytearray(1), bytearray(0), []) client_hello.server_name = b'example.com' self.assertEqual(client_hello.server_name, b'example.com') client_hello.server_name = b'example.org' self.assertEqual(client_hello.server_name, b'example.org') ext = client_hello.getExtension(ExtensionType.server_name) self.assertIsNotNone(ext) def test_server_name_other_than_dns_name(self): client_hello = ClientHello().create((3, 3), bytearray(1), bytearray(0), []) sni_ext = SNIExtension().create(serverNames=[\ SNIExtension.ServerName(1, b'test')]) client_hello.extensions = [sni_ext] self.assertEqual(client_hello.server_name, bytearray(0)) def test_parse_with_SSLv2_client_hello(self): parser = Parser(bytearray( # length and type is handled by hello protocol parser #b'\x80\x2e' + # length - 46 bytes #b'\x01' + # message type - client hello b'\x00\x02' + # version - SSLv2 b'\x00\x15' + # cipher spec length - 21 bytes b'\x00\x00' + # session ID length - 0 bytes b'\x00\x10' + # challange length - 16 bytes b'\x07\x00\xc0' + # cipher - SSL2_DES_192_EDE3_CBC_WITH_MD5 b'\x05\x00\x80' + # cipher - SSL2_IDEA_128_CBC_WITH_MD5 b'\x03\x00\x80' + # cipher - SSL2_RC2_CBC_128_CBC_WITH_MD5 b'\x01\x00\x80' + # cipher - SSL2_RC4_128_WITH_MD5 b'\x06\x00\x40' + # cipher - SSL2_DES_64_CBC_WITH_MD5 b'\x04\x00\x80' + # cipher - SSL2_RC2_CBC_128_CBC_WITH_MD5 b'\x02\x00\x80' + # cipher - SSL2_RC4_128_EXPORT40_WITH_MD5 b'\x01' * 16 # challenge )) client_hello = ClientHello(ssl2=True) client_hello = client_hello.parse(parser) # XXX the value on the wire is LSB, but should be interpreted MSB for # SSL2 self.assertEqual((0, 2), client_hello.client_version) self.assertEqual(bytearray(0), client_hello.session_id) self.assertEqual([458944, 327808, 196736, 65664, 393280, 262272, 131200], client_hello.cipher_suites) self.assertEqual(bytearray(b'\x00'*16 + b'\x01'*16), client_hello.random) self.assertEqual([0], client_hello.compression_methods) class TestServerHello(unittest.TestCase): def test___init__(self): server_hello = ServerHello() self.assertEqual((0,0), server_hello.server_version) self.assertEqual(bytearray(32), server_hello.random) self.assertEqual(bytearray(0), server_hello.session_id) self.assertEqual(0, server_hello.cipher_suite) self.assertEqual(CertificateType.x509, server_hello.certificate_type) self.assertEqual(0, server_hello.compression_method) self.assertEqual(None, server_hello.tackExt) self.assertEqual(None, server_hello.next_protos_advertised) self.assertEqual(None, server_hello.next_protos) def test_create(self): server_hello = ServerHello().create( (1,1), # server version bytearray(b'\x00'*31+b'\x01'), # random bytearray(0), # session id 4, # cipher suite 1, # certificate type None, # TACK ext None) # next protos advertised self.assertEqual((1,1), server_hello.server_version) self.assertEqual(bytearray(b'\x00'*31 + b'\x01'), server_hello.random) self.assertEqual(bytearray(0), server_hello.session_id) self.assertEqual(4, server_hello.cipher_suite) self.assertEqual(CertificateType.openpgp, server_hello.certificate_type) self.assertEqual(0, server_hello.compression_method) self.assertEqual(None, server_hello.tackExt) self.assertEqual(None, server_hello.next_protos_advertised) def test_create_with_minimal_options(self): server_hello = ServerHello().create( (3, 3), # server version bytearray(b'\x02'*31+b'\x01'), # random bytearray(4), # session ID CipherSuite.TLS_RSA_WITH_RC4_128_MD5) # ciphersuite self.assertEqual(bytearray( b'\x02' + # type of message b'\x00\x00\x2a' + # length b'\x03\x03' + # server version b'\x02'*31+b'\x01' + # random b'\x04' + # Session ID length b'\x00'*4 + # session id b'\x00\x04' + # selected ciphersuite b'\x00' # selected compression method ), server_hello.write()) def test_parse(self): p = Parser(bytearray( # don't include type of message as it is handled by the hello # protocol layer # b'\x02' + # type of message - server_hello b'\x00\x00\x36' + # length - 54 bytes b'\x03\x03' + # version - TLS 1.2 b'\x01'*31 + b'\x02' + # random b'\x00' + # session id length b'\x00\x9d' + # cipher suite b'\x01' + # compression method (zlib) b'\x00\x0e' + # extensions length - 14 bytes b'\xff\x01' + # ext type - renegotiation_info b'\x00\x01' + # ext length - 1 byte b'\x00' + # value - supported (0) b'\x00\x23' + # ext type - session ticket (35) b'\x00\x00' + # ext length - 0 bytes b'\x00\x0f' + # ext type - heartbeat (15) b'\x00\x01' + # ext length - 1 byte b'\x01')) # peer allowed to send requests (1) server_hello = ServerHello() server_hello = server_hello.parse(p) self.assertEqual((3,3), server_hello.server_version) self.assertEqual(bytearray(b'\x01'*31 + b'\x02'), server_hello.random) self.assertEqual(bytearray(0), server_hello.session_id) self.assertEqual(157, server_hello.cipher_suite) # XXX not sent by server! self.assertEqual(CertificateType.x509, server_hello.certificate_type) self.assertEqual(1, server_hello.compression_method) self.assertEqual(None, server_hello.tackExt) self.assertEqual(None, server_hello.next_protos_advertised) def test_parse_with_length_short_by_one(self): p = Parser(bytearray( # don't include type of message as it is handled by the hello # protocol layer # b'\x02' + # type of message - server_hello b'\x00\x00\x25' + # length - 37 bytes (one short) b'\x03\x03' + # version - TLS 1.2 b'\x01'*31 + b'\x02' + # random b'\x00' + # session id length b'\x00\x9d' + # cipher suite b'\x01' # compression method (zlib) )) server_hello = ServerHello() with self.assertRaises(SyntaxError) as context: server_hello.parse(p) # TODO the message probably could be more descriptive... self.assertIsNone(context.exception.msg) def test_parse_with_length_long_by_one(self): p = Parser(bytearray( # don't include type of message as it is handled by the hello # protocol layer # b'\x02' + # type of message - server_hello b'\x00\x00\x27' + # length - 39 bytes (one long) b'\x03\x03' + # version - TLS 1.2 b'\x01'*31 + b'\x02' + # random b'\x00' + # session id length b'\x00\x9d' + # cipher suite b'\x01' # compression method (zlib) )) server_hello = ServerHello() with self.assertRaises(SyntaxError) as context: server_hello.parse(p) # TODO the message probably could be more descriptive... self.assertIsNone(context.exception.msg) def test_parse_with_extensions_length_short_by_one(self): p = Parser(bytearray( # don't include type of message as it is handled by the hello # protocol layer # b'\x02' + # type of message - server_hello b'\x00\x00\x36' + # length - 54 bytes b'\x03\x03' + # version - TLS 1.2 b'\x01'*31 + b'\x02' + # random b'\x00' + # session id length b'\x00\x9d' + # cipher suite b'\x01' + # compression method (zlib) b'\x00\x0d' + # extensions length - 13 bytes (!) b'\xff\x01' + # ext type - renegotiation_info b'\x00\x01' + # ext length - 1 byte b'\x00' + # value - supported (0) b'\x00\x23' + # ext type - session ticket (35) b'\x00\x00' + # ext length - 0 bytes b'\x00\x0f' + # ext type - heartbeat (15) b'\x00\x01' + # ext length - 1 byte b'\x01')) # peer allowed to send requests (1) server_hello = ServerHello() with self.assertRaises(SyntaxError) as context: server_hello.parse(p) # TODO the message could be more descriptive... self.assertIsNone(context.exception.msg) def test_parse_with_extensions_length_long_by_one(self): p = Parser(bytearray( # don't include type of message as it is handled by the hello # protocol layer # b'\x02' + # type of message - server_hello b'\x00\x00\x36' + # length - 54 bytes b'\x03\x03' + # version - TLS 1.2 b'\x01'*31 + b'\x02' + # random b'\x00' + # session id length b'\x00\x9d' + # cipher suite b'\x01' + # compression method (zlib) b'\x00\x0f' + # extensions length - 15 bytes (!) b'\xff\x01' + # ext type - renegotiation_info b'\x00\x01' + # ext length - 1 byte b'\x00' + # value - supported (0) b'\x00\x23' + # ext type - session ticket (35) b'\x00\x00' + # ext length - 0 bytes b'\x00\x0f' + # ext type - heartbeat (15) b'\x00\x01' + # ext length - 1 byte b'\x01')) # peer allowed to send requests (1) server_hello = ServerHello() with self.assertRaises(SyntaxError) as context: server_hello.parse(p) # TODO the message could be more descriptive... self.assertIsNone(context.exception.msg) def test_parse_with_cert_type_extension(self): p = Parser(bytearray( b'\x00\x00\x2d' + # length - 45 bytes b'\x03\x03' + # version - TLS 1.2 b'\x01'*31 + b'\x02' + # random b'\x00' + # session id length b'\x00\x9d' + # cipher suite b'\x00' + # compression method (none) b'\x00\x05' + # extensions length - 5 bytes b'\x00\x09' + # ext type - cert_type (9) b'\x00\x01' + # ext length - 1 byte b'\x01' # value - OpenPGP (1) )) server_hello = ServerHello().parse(p) self.assertEqual(1, server_hello.certificate_type) def test_parse_with_bad_cert_type_extension(self): p = Parser(bytearray( b'\x00\x00\x2e' + # length - 46 bytes b'\x03\x03' + # version - TLS 1.2 b'\x01'*31 + b'\x02' + # random b'\x00' + # session id length b'\x00\x9d' + # cipher suite b'\x00' + # compression method (none) b'\x00\x06' + # extensions length - 5 bytes b'\x00\x09' + # ext type - cert_type (9) b'\x00\x02' + # ext length - 2 bytes b'\x00\x01' # value - X.509 (0), OpenPGP (1) )) server_hello = ServerHello() with self.assertRaises(SyntaxError) as context: server_hello.parse(p) def test_parse_with_NPN_extension(self): p = Parser(bytearray( b'\x00\x00\x3c' + # length - 60 bytes b'\x03\x03' + # version - TLS 1.2 b'\x01'*31 + b'\x02' + # random b'\x00' + # session id length b'\x00\x9d' + # cipher suite b'\x00' + # compression method (none) b'\x00\x14' + # extensions length - 20 bytes b'\x33\x74' + # ext type - npn b'\x00\x10' + # ext length - 16 bytes b'\x08' + # length of first name - 8 bytes b'http/1.1' + b'\x06' + # length of second name - 6 bytes b'spdy/3' )) server_hello = ServerHello().parse(p) self.assertEqual([bytearray(b'http/1.1'), bytearray(b'spdy/3')], server_hello.next_protos) def test_write(self): server_hello = ServerHello().create( (1,1), # server version bytearray(b'\x00'*31+b'\x02'), # random bytearray(0), # session id 4, # cipher suite None, # certificate type None, # TACK ext None) # next protos advertised self.assertEqual(list(bytearray( b'\x02' + # type of message - server_hello b'\x00\x00\x26' + # length b'\x01\x01' + # proto version b'\x00'*31 + b'\x02' + # random b'\x00' + # session id length b'\x00\x04' + # cipher suite b'\x00' # compression method )), list(server_hello.write())) def test_write_with_next_protos(self): server_hello = ServerHello().create( (1,1), # server version bytearray(b'\x00'*31+b'\x02'), # random bytearray(0), # session id 4, # cipher suite 0, # certificate type None, # TACK ext [b'spdy/3', b'http/1.1']) # next protos advertised self.assertEqual(list(bytearray( b'\x02' + # type of message - server_hello b'\x00\x00\x3c' + # length b'\x01\x01' + # proto version b'\x00'*31 + b'\x02' + # random b'\x00' + # session id length b'\x00\x04' + # cipher suite b'\x00' + # compression method b'\x00\x14' + # extensions length b'\x33\x74' + # ext type - NPN (13172) b'\x00\x10' + # ext length - 16 bytes b'\x06' + # first entry length - 6 bytes # utf-8 encoding of 'spdy/3' b'\x73\x70\x64\x79\x2f\x33' b'\x08' + # second entry length - 8 bytes # utf-8 endoding of 'http/1.1' b'\x68\x74\x74\x70\x2f\x31\x2e\x31' )), list(server_hello.write())) def test___str__(self): server_hello = ServerHello() server_hello = server_hello.create( (3,0), bytearray(b'\x00'*32), bytearray(b'\x01\x20'), 34500, 0, None, None) self.assertEqual("server_hello,length(40),version(3.0),random(...),"\ "session ID(bytearray(b'\\x01 ')),cipher(0x86c4),"\ "compression method(0)", str(server_hello)) def test___repr__(self): server_hello = ServerHello() server_hello = server_hello.create( (3,0), bytearray(b'\x00'*32), bytearray(0), 34500, 0, None, None, extensions=[]) self.maxDiff = None self.assertEqual("ServerHello(server_version=(3.0), "\ "random=bytearray(b'\\x00\\x00"\ "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00"\ "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00"\ "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'), "\ "session_id=bytearray(b''), "\ "cipher_suite=34500, compression_method=0, _tack_ext=None, "\ "extensions=[])", repr(server_hello)) class TestRecordHeader2(unittest.TestCase): def test___init__(self): rh = RecordHeader2() self.assertTrue(rh.ssl2) self.assertEqual(0, rh.type) self.assertEqual((0, 0), rh.version) def test_parse(self): parser = Parser(bytearray( b'\x80' + # head b'\x12' # length )) rh = RecordHeader2() rh = rh.parse(parser) self.assertTrue(rh.ssl2) self.assertEqual(ContentType.handshake, rh.type) self.assertEqual((2, 0), rh.version) self.assertEqual(18, rh.length) def test_parse_with_invalid_header(self): parser = Parser(bytearray( b'\x00' + # header (bad) b'\x12' # length )) rh = RecordHeader2() with self.assertRaises(SyntaxError): rh.parse(parser) def test_parse_with_very_long_message(self): parser = Parser(bytearray( b'\x82' + # header and a nibble of length b'\x00' )) rh = RecordHeader2() #XXX can't handle two-byte length with self.assertRaises(SyntaxError): rh = rh.parse(parser) #self.assertEqual(512, rh.length) class TestRecordHeader3(unittest.TestCase): def test___init__(self): rh = RecordHeader3() self.assertEqual(0, rh.type) self.assertEqual((0, 0), rh.version) self.assertEqual(0, rh.length) self.assertFalse(rh.ssl2) def test_create(self): rh = RecordHeader3() rh = rh.create((3, 3), ContentType.application_data, 10) self.assertEqual((3, 3), rh.version) self.assertEqual(ContentType.application_data, rh.type) self.assertEqual(10, rh.length) self.assertFalse(rh.ssl2) def test_write(self): rh = RecordHeader3() rh = rh.create((3, 3), ContentType.application_data, 10) self.assertEqual(bytearray( b'\x17' + # protocol type b'\x03\x03' + # protocol version b'\x00\x0a' # length ), rh.write()) def test_write_with_too_big_length(self): rh = RecordHeader3() rh = rh.create((3, 3), ContentType.application_data, 2**17) with self.assertRaises(ValueError): rh.write() def test_parse(self): parser = Parser(bytearray( b'\x17' + # protocol type - app data b'\x03\x03' + # protocol version b'\x00\x0f' # length )) rh = RecordHeader3() rh = rh.parse(parser) self.assertFalse(rh.ssl2) self.assertEqual(ContentType.application_data, rh.type) self.assertEqual((3, 3), rh.version) self.assertEqual(15, rh.length) def test_typeName(self): rh = RecordHeader3() rh = rh.create((3,0), ContentType.application_data, 0) self.assertEqual("application_data", rh.typeName) def test___str__(self): rh = RecordHeader3() rh = rh.create((3,0), ContentType.handshake, 12) self.assertEqual("SSLv3 record,version(3.0),content type(handshake)," +\ "length(12)", str(rh)) def test___str___with_invalid_content_type(self): rh = RecordHeader3() rh = rh.create((3,3), 12, 0) self.assertEqual("SSLv3 record,version(3.3)," +\ "content type(unknown(12)),length(0)", str(rh)) def test___repr__(self): rh = RecordHeader3() rh = rh.create((3,0), ContentType.application_data, 256) self.assertEqual("RecordHeader3(type=23, version=(3.0), length=256)", repr(rh)) class TestAlert(unittest.TestCase): def test___init__(self): alert = Alert() self.assertEqual(alert.contentType, ContentType.alert) self.assertEqual(alert.level, 0) self.assertEqual(alert.description, 0) def test_levelName(self): alert = Alert().create(AlertDescription.record_overflow, AlertLevel.fatal) self.assertEqual("fatal", alert.levelName) def test_levelName_with_wrong_level(self): alert = Alert().create(AlertDescription.close_notify, 11) self.assertEqual("unknown(11)", alert.levelName) def test_descriptionName(self): alert = Alert().create(AlertDescription.record_overflow, AlertLevel.fatal) self.assertEqual("record_overflow", alert.descriptionName) def test_descriptionName_with_wrong_id(self): alert = Alert().create(1) self.assertEqual("unknown(1)", alert.descriptionName) def test___str__(self): alert = Alert().create(AlertDescription.record_overflow, AlertLevel.fatal) self.assertEqual("Alert, level:fatal, description:record_overflow", str(alert)) def test___repr__(self): alert = Alert().create(AlertDescription.record_overflow, AlertLevel.fatal) self.assertEqual("Alert(level=2, description=22)", repr(alert)) def test_parse(self): alert = Alert() parser = Parser(bytearray( b'\x01' + # level b'\x02' # description )) alert = alert.parse(parser) self.assertEqual(alert.level, 1) self.assertEqual(alert.description, 2) def test_parse_with_missing_data(self): alert = Alert() parser = Parser(bytearray( b'\x01')) # level with self.assertRaises(SyntaxError): alert.parse(parser) def test_write(self): alert = Alert().create(AlertDescription.record_overflow) self.assertEqual(bytearray( b'\x02\x16'), alert.write()) class TestClientKeyExchange(unittest.TestCase): def test___init__(self): cke = ClientKeyExchange(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA) self.assertIsNotNone(cke) self.assertIsNone(cke.version) self.assertEqual(0, cke.srp_A) self.assertEqual(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, cke.cipherSuite) self.assertEqual(bytearray(0), cke.encryptedPreMasterSecret) def test_createSRP(self): cke = ClientKeyExchange(CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA) cke.createSRP(2**128+3) bts = cke.write() self.assertEqual(bts, bytearray( b'\x10' + # CKE b'\x00\x00\x13' + # Handshake message length b'\x00\x11' + # length of value b'\x01' + # 2... b'\x00'*15 + # ...**128... b'\x03')) # ...+3 def test_createRSA(self): cke = ClientKeyExchange(CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, (3, 3)) cke.createRSA(bytearray(12)) bts = cke.write() self.assertEqual(bts, bytearray( b'\x10' + # CKE b'\x00\x00\x0e' + # Handshake message length b'\x00\x0c' + # length of encrypted value b'\x00'*12)) def test_createRSA_with_SSL3(self): cke = ClientKeyExchange(CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, (3, 0)) cke.createRSA(bytearray(12)) bts = cke.write() self.assertEqual(bts, bytearray( b'\x10' + # CKE b'\x00\x00\x0c' + # Handshake message length b'\x00'*12)) def test_createDH(self): cke = ClientKeyExchange(CipherSuite.TLS_DH_ANON_WITH_AES_128_CBC_SHA) cke.createDH(2**64+3) bts = cke.write() self.assertEqual(bts, bytearray( b'\x10' + b'\x00\x00\x0b' + b'\x00\x09' + b'\x01' + b'\x00'*7 + b'\x03')) def test_createRSA_with_unset_protocol(self): cke = ClientKeyExchange(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA) cke.createRSA(bytearray(12)) with self.assertRaises(AssertionError): cke.write() def test_write_with_unknown_cipher_suite(self): cke = ClientKeyExchange(0) with self.assertRaises(AssertionError): cke.write() def test_write_with_DHE_RSA(self): cke = ClientKeyExchange(CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, (3, 1)) cke.createDH(2**64+3) self.assertEqual(cke.write(), bytearray( b'\x10' + b'\x00\x00\x0b' + b'\x00\x09' + b'\x01' + b'\x00'*7 + b'\x03')) def test_parse_with_RSA(self): cke = ClientKeyExchange(CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, (3, 1)) parser = Parser(bytearray( b'\x00\x00\x0e' + b'\x00\x0c' + b'\x00'*12)) cke.parse(parser) self.assertEqual(bytearray(12), cke.encryptedPreMasterSecret) def test_parse_with_RSA_in_SSL3(self): cke = ClientKeyExchange(CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA, (3, 0)) parser = Parser(bytearray( b'\x00\x00\x0c' + b'\x00'*12)) cke.parse(parser) self.assertEqual(bytearray(12), cke.encryptedPreMasterSecret) def test_parse_with_RSA_and_unset_protocol(self): cke = ClientKeyExchange(CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA) parser = Parser(bytearray( b'\x00\x00\x0c' + b'x\00'*12)) with self.assertRaises(AssertionError): cke.parse(parser) def test_parse_with_SRP(self): cke = ClientKeyExchange(CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA) parser = Parser(bytearray( b'\x00\x00\x0a' + b'\x00\x08' + b'\x00'*7 + b'\xff')) cke.parse(parser) self.assertEqual(255, cke.srp_A) def test_parse_with_DH(self): cke = ClientKeyExchange(CipherSuite.TLS_DH_ANON_WITH_AES_128_CBC_SHA) parser = Parser(bytearray( b'\x00\x00\x0a' + b'\x00\x08' + b'\x01' + b'\x00'*7)) cke.parse(parser) self.assertEqual(2**56, cke.dh_Yc) def test_parse_with_unknown_cipher(self): cke = ClientKeyExchange(0) parser = Parser(bytearray( b'\x00\x00\x00')) with self.assertRaises(AssertionError): cke.parse(parser) class TestServerKeyExchange(unittest.TestCase): def test___init__(self): ske = ServerKeyExchange(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, (3, 1)) self.assertIsNotNone(ske) self.assertEqual(ske.cipherSuite, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA) self.assertEqual(ske.srp_N, 0) self.assertEqual(ske.srp_g, 0) self.assertEqual(ske.srp_s, bytearray(0)) self.assertEqual(ske.srp_B, 0) self.assertEqual(ske.dh_p, 0) self.assertEqual(ske.dh_g, 0) self.assertEqual(ske.dh_Ys, 0) self.assertEqual(ske.signature, bytearray(0)) self.assertEqual(ske.version, (3, 1)) def test___repr__(self): ske = ServerKeyExchange(CipherSuite.TLS_DH_ANON_WITH_AES_128_CBC_SHA, (3, 1)) self.assertEqual("ServerKeyExchange(" "cipherSuite=CipherSuite.TLS_DH_ANON_WITH_AES_128_CBC_SHA, " "version=(3, 1))", repr(ske)) def test__repr___with_ADH(self): ske = ServerKeyExchange(CipherSuite.TLS_DH_ANON_WITH_AES_128_CBC_SHA, (3, 1)) ske.createDH(dh_p=31, dh_g=2, dh_Ys=16) self.assertEqual("ServerKeyExchange(" "cipherSuite=CipherSuite.TLS_DH_ANON_WITH_AES_128_CBC_SHA, " "version=(3, 1), dh_p=31, dh_g=2, dh_Ys=16)", repr(ske)) def test___repr___with_DHE(self): ske = ServerKeyExchange(CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, (3, 3)) ske.createDH(dh_p=31, dh_g=2, dh_Ys=16) ske.signature = bytearray(b'\xff'*4) ske.signAlg = SignatureAlgorithm.rsa ske.hashAlg = HashAlgorithm.sha384 self.assertEqual("ServerKeyExchange(" "cipherSuite=CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, " "version=(3, 3), dh_p=31, dh_g=2, dh_Ys=16, " "hashAlg=5, signAlg=1, " "signature=bytearray(b'\\xff\\xff\\xff\\xff'))", repr(ske)) def test___repr___with_SRP(self): ske = ServerKeyExchange(CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA, (3, 3)) ske.createSRP(srp_N=1, srp_g=2, srp_s=bytearray(3), srp_B=4) self.assertEqual("ServerKeyExchange(" "cipherSuite=CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA, " "version=(3, 3), " "srp_N=1, srp_g=2, " "srp_s=bytearray(b'\\x00\\x00\\x00'), srp_B=4)", repr(ske)) def test_createSRP(self): ske = ServerKeyExchange(CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA, (3, 3)) ske.createSRP(srp_N=1, srp_g=2, srp_s=bytearray(3), srp_B=4) self.assertEqual(ske.write(), bytearray( b'\x0c' + # message type b'\x00\x00\x0d' + # overall length b'\x00\x01' + # N parameter length b'\x01' + # N value b'\x00\x01' + # g parameter length b'\x02' + # g value b'\x03' + # s parameter length b'\x00'*3 + # s value b'\x00\x01' + # B parameter length b'\x04' # B value )) def test_createSRP_with_signature(self): ske = ServerKeyExchange( CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, (3, 1)) ske.createSRP(srp_N=1, srp_g=2, srp_s=bytearray(3), srp_B=4) ske.signature = bytearray(b'\xc0\xff\xee') self.assertEqual(ske.write(), bytearray( b'\x0c' + # message type b'\x00\x00\x12' + # overall length b'\x00\x01' + # N parameter length b'\x01' + # N value b'\x00\x01' + # g parameter length b'\x02' + # g value b'\x03' + # s parameter length b'\x00'*3 + # s value b'\x00\x01' + # B parameter length b'\x04' # B value b'\x00\x03' + # signature length b'\xc0\xff\xee' # signature value )) def test_createSRP_with_signature_in_TLS_v1_2(self): ske = ServerKeyExchange( CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, (3, 3)) ske.createSRP(srp_N=1, srp_g=2, srp_s=bytearray(3), srp_B=4) ske.hashAlg = HashAlgorithm.sha512 ske.signAlg = SignatureAlgorithm.rsa ske.signature = bytearray(b'\xc0\xff\xee') self.assertEqual(ske.write(), bytearray( b'\x0c' + # message type b'\x00\x00\x14' + # overall length b'\x00\x01' + # N parameter length b'\x01' + # N value b'\x00\x01' + # g parameter length b'\x02' + # g value b'\x03' + # s parameter length b'\x00'*3 + # s value b'\x00\x01' + # B parameter length b'\x04' + # B value b'\x06\x01' + # SHA512+RSA b'\x00\x03' + # signature length b'\xc0\xff\xee' # signature value )) def test_createDH(self): ske = ServerKeyExchange(CipherSuite.TLS_DH_ANON_WITH_AES_128_CBC_SHA, (3, 3)) ske.createDH(dh_p=31, dh_g=2, dh_Ys=16) self.assertEqual(ske.write(), bytearray( b'\x0c' + # message type b'\x00\x00\x09' + # overall length b'\x00\x01' + # p parameter length b'\x1f' + # p value b'\x00\x01' + # g parameter length b'\x02' + # g value b'\x00\x01' + # Ys parameter length b'\x10' # Ys value )) def test_parse_with_unknown_cipher(self): ske = ServerKeyExchange(0, (3, 1)) parser = Parser(bytearray( b'\x00\x00\x03' + b'\x00\x01' + b'\xff' )) with self.assertRaises(AssertionError): ske.parse(parser) def test_parse_with_read_past_message_end(self): ske = ServerKeyExchange(\ CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, (3, 1)) parser = Parser(bytearray( b'\x00\x00\x13' + # overall length b'\x00\x01' + # N parameter length b'\x01' + # N value b'\x00\x01' + # g parameter length b'\x02' + # g value b'\x03' + # s parameter length b'\x00'*3 + # s value b'\x00\x01' + # B parameter length b'\x04' + # B value b'\x00\x06' + # signature length b'\xff'*4)) with self.assertRaises(SyntaxError): ske.parse(parser) def test_parse_with_SRP(self): ske = ServerKeyExchange(CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA, (3, 3)) parser = Parser(bytearray( b'\x00\x00\x0d' + # overall length b'\x00\x01' + # N parameter length b'\x01' + # N value b'\x00\x01' + # g parameter length b'\x02' + # g value b'\x03' + # s parameter length b'\x00'*3 + # s value b'\x00\x01' + # B parameter length b'\x04' # B value )) ske.parse(parser) self.assertEqual(ske.srp_N, 1) self.assertEqual(ske.srp_g, 2) self.assertEqual(ske.srp_s, bytearray(3)) self.assertEqual(ske.srp_B, 4) def test_parser_with_SRP_RSA(self): ske = ServerKeyExchange( CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, (3, 1)) parser = Parser(bytearray( b'\x00\x00\x12' + # overall length b'\x00\x01' + # N parameter length b'\x01' + # N value b'\x00\x01' + # g parameter length b'\x02' + # g value b'\x03' + # s parameter length b'\x00'*3 + # s value b'\x00\x01' + # B parameter length b'\x04' # B value b'\x00\x03' + # signature length b'\xc0\xff\xee' # signature value )) ske.parse(parser) self.assertEqual(ske.srp_N, 1) self.assertEqual(ske.srp_g, 2) self.assertEqual(ske.srp_s, bytearray(3)) self.assertEqual(ske.srp_B, 4) self.assertEqual(ske.signature, bytearray(b'\xc0\xff\xee')) def test_parser_with_SRP_RSA_in_TLS_v1_2(self): ske = ServerKeyExchange( CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, (3, 3)) parser = Parser(bytearray( b'\x00\x00\x14' + # overall length b'\x00\x01' + # N parameter length b'\x01' + # N value b'\x00\x01' + # g parameter length b'\x02' + # g value b'\x03' + # s parameter length b'\x00'*3 + # s value b'\x00\x01' + # B parameter length b'\x04' # B value b'\x06\x01' + # SHA512+RSA b'\x00\x03' + # signature length b'\xc0\xff\xee' # signature value )) ske.parse(parser) self.assertEqual(ske.srp_N, 1) self.assertEqual(ske.srp_g, 2) self.assertEqual(ske.srp_s, bytearray(3)) self.assertEqual(ske.srp_B, 4) self.assertEqual(ske.signature, bytearray(b'\xc0\xff\xee')) self.assertEqual(ske.hashAlg, HashAlgorithm.sha512) self.assertEqual(ske.signAlg, SignatureAlgorithm.rsa) def test_parser_with_DH(self): ske = ServerKeyExchange(CipherSuite.TLS_DH_ANON_WITH_AES_128_CBC_SHA, (3, 3)) parser = Parser(bytearray( b'\x00\x00\x09' + # overall length b'\x00\x01' + # p parameter length b'\x1f' + # p value b'\x00\x01' + # g parameter length b'\x02' + # g value b'\x00\x01' + # Ys parameter length b'\x10' # Ys value )) ske.parse(parser) self.assertEqual(ske.dh_p, 31) self.assertEqual(ske.dh_g, 2) self.assertEqual(ske.dh_Ys, 16) def test_hash(self): ske = ServerKeyExchange( CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, (3, 1)) ske.createSRP(srp_N=1, srp_g=2, srp_s=bytearray(3), srp_B=4) hash1 = ske.hash(bytearray(32), bytearray(32)) self.assertEqual(hash1, bytearray( b'\xcb\xe6\xd3=\x8b$\xff\x97e&\xb2\x89\x1dA\xab>' + b'\x8e?YW\xcd\xad\xc6\x83\x91\x1d.fe,\x17y' + b'=\xc4T\x89')) def test_hash_with_invalid_ciphersuite(self): ske = ServerKeyExchange(0, (3, 1)) with self.assertRaises(AssertionError): ske.hash(bytearray(32), bytearray(32)) def test_hash_with_default_hash_algorithm_for_TLS_v1_2(self): ske = ServerKeyExchange( CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, (3, 3)) ske.createSRP(srp_N=1, srp_g=2, srp_s=bytearray(3), srp_B=4) hash1 = ske.hash(bytearray(32), bytearray(32)) self.assertEqual(hash1, bytearray( b'\x8e?YW\xcd\xad\xc6\x83\x91\x1d.fe,\x17y=\xc4T\x89' )) class TestCertificateRequest(unittest.TestCase): def test___init__(self): cr = CertificateRequest((3, 0)) self.assertIsNotNone(cr) self.assertEqual(cr.version, (3, 0)) self.assertEqual(cr.certificate_types, []) self.assertEqual(cr.certificate_authorities, []) self.assertEqual(cr.supported_signature_algs, []) def test_create(self): cr = CertificateRequest((3, 0)) cr.create([ClientCertificateType.rsa_sign], []) self.assertEqual(cr.certificate_authorities, []) self.assertEqual(cr.certificate_types, [ClientCertificateType.rsa_sign]) # XXX type change from array! self.assertEqual(cr.supported_signature_algs, tuple()) def test_parse(self): cr = CertificateRequest((3, 1)) parser = Parser(bytearray( b'\x00\x00\x04' + # overall length b'\x01' + # length of certificate types b'\x01' + # type rsa_sign b'\x00\x00' # length of CA list )) cr.parse(parser) self.assertEqual(cr.certificate_authorities, []) self.assertEqual(cr.certificate_types, [ClientCertificateType.rsa_sign]) def test_parse_with_TLSv1_2(self): cr = CertificateRequest((3, 3)) parser = Parser(bytearray( b'\x00\x00\x1a' + # overall length b'\x01' + # length of certificate types b'\x01' + # type rsa_sign b'\x00\x0a' + # length of signature types b'\x06\x01' + # SHA512+RSA b'\x05\x01' + # SHA384+RSA b'\x04\x01' + # SHA256+RSA b'\x03\x01' + # SHA224+RSA b'\x02\x01' + # SHA1+RSA b'\x00\x0a' + # length of CA list b'\x00'*10 # opaque data type )) cr.parse(parser) self.assertEqual(cr.certificate_types, [ClientCertificateType.rsa_sign]) self.assertEqual(cr.supported_signature_algs, [(HashAlgorithm.sha512, SignatureAlgorithm.rsa), (HashAlgorithm.sha384, SignatureAlgorithm.rsa), (HashAlgorithm.sha256, SignatureAlgorithm.rsa), (HashAlgorithm.sha224, SignatureAlgorithm.rsa), (HashAlgorithm.sha1, SignatureAlgorithm.rsa)]) self.assertEqual(len(cr.certificate_authorities), 5) for cert_auth in cr.certificate_authorities: self.assertEqual(cert_auth, bytearray(0)) def test_write(self): cr = CertificateRequest((3, 1)) cr.create([ClientCertificateType.rsa_sign], [bytearray(b'\xff\xff')]) self.assertEqual(cr.write(), bytearray( b'\x0d' + # type b'\x00\x00\x08' + # overall length b'\x01' + # length of certificate types b'\x01' + # type rsa sign b'\x00\x04' + # length of CA list b'\x00\x02' + # length of entry b'\xff\xff' # opaque )) def test_write_in_TLS_v1_2(self): cr = CertificateRequest((3, 3)) self.assertEqual(cr.version, (3, 3)) cr.create([ClientCertificateType.rsa_sign], [], [(6, 1), (4, 1), (2, 1)]) self.assertEqual(cr.write(), bytearray( b'\x0d' + # type b'\x00\x00\x0c' + # overall length b'\x01' + # length of certificate types b'\x01' + # type rsa sign b'\x00\x06' + # signature types b'\x06\x01' + # SHA512+RSA b'\x04\x01' + # SHA256+RSA b'\x02\x01' + # SHA1+RSA b'\x00\x00' # length of CA list )) class TestCertificateVerify(unittest.TestCase): def test___init__(self): cv = CertificateVerify((3, 1)) self.assertIsNotNone(cv) self.assertEqual(cv.signature, bytearray(0)) def test_create(self): cv = CertificateVerify((3, 1)) cv.create(bytearray(b'\xf0\x0f')) self.assertEqual(cv.signature, bytearray(b'\xf0\x0f')) def test_write(self): cv = CertificateVerify((3, 1)) cv.create(bytearray(b'\xf0\x0f')) self.assertEqual(cv.write(), bytearray( b'\x0f' + # type b'\x00\x00\x04' + # overall length b'\x00\x02' + # length of signature b'\xf0\x0f' # signature )) def test_parse(self): cv = CertificateVerify((3, 1)) parser = Parser(bytearray( b'\x00\x00\x04' + # length b'\x00\x02' + # length of signature b'\xf0\x0f' # signature )) cv.parse(parser) self.assertEqual(cv.signature, bytearray(b'\xf0\x0f')) def test_parse_with_TLSv1_2(self): cv = CertificateVerify((3, 3)) parser = Parser(bytearray( b'\x00\x00\x06' + # length b'\x02\x01' + # SHA1 + RSA b'\x00\x02' + # length of signature b'\xab\xcd' # signature )) cv.parse(parser) self.assertEqual(cv.signature, bytearray(b'\xab\xcd')) self.assertEqual(cv.signatureAlgorithm, (HashAlgorithm.sha1, SignatureAlgorithm.rsa)) def test_write_with_TLSv1_2(self): cv = CertificateVerify((3, 3)) cv.create(bytearray(b'\xff\xba'), (HashAlgorithm.sha512, SignatureAlgorithm.rsa)) self.assertEqual(cv.write(), bytearray( b'\x0f' + # type b'\x00\x00\x06' + # overall length b'\x06\x01' + # SHA512+RSA b'\x00\x02' + # signature length b'\xff\xba' # signature )) if __name__ == '__main__': unittest.main() tlslite-ng-0.5.1/unit_tests/test_tlslite_messagesocket.py000066400000000000000000000304421261665411200240150ustar00rootroot00000000000000# Copyright (c) 2015, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. # compatibility with Python 2.6, for that we need unittest2 package, # which is not available on 3.3 or 3.4 try: import unittest2 as unittest except ImportError: import unittest from unit_tests.mocksock import MockSocket from tlslite.messagesocket import MessageSocket from tlslite.defragmenter import Defragmenter from tlslite.messages import Message from tlslite.constants import ContentType class TestMessageSocket(unittest.TestCase): def test___init__(self): msgSock = MessageSocket(None, None) self.assertIsNotNone(msgSock) def test_recvMessage(self): defragmenter = Defragmenter() defragmenter.addStaticSize(21, 2) sock = MockSocket(bytearray( b'\x15' + # message type b'\x03\x03' + # TLS version b'\x00\x04' + # payload length b'\xff\xff' + # first message b'\xbb\xbb' # second message )) msgSock = MessageSocket(sock, defragmenter) for res in msgSock.recvMessage(): if res in (0, 1): self.assertTrue(False, "Blocking read") else: break self.assertIsNotNone(res) header, parser = res self.assertEqual(header.type, 21) self.assertEqual(header.version, (3, 3)) self.assertEqual(header.length, 0) self.assertEqual(parser.bytes, bytearray(b'\xff\xff')) res = None for res in msgSock.recvMessage(): if res in (0, 1): self.assertTrue(False, "Blocking read") else: break self.assertIsNotNone(res) header, parser = res self.assertEqual(header.type, 21) self.assertEqual(header.version, (3, 3)) self.assertEqual(header.length, 0) self.assertEqual(parser.bytes, bytearray(b'\xbb\xbb')) def test_recvMessage_with_unfragmentable_type(self): defragmenter = Defragmenter() defragmenter.addStaticSize(21, 2) sock = MockSocket(bytearray( b'\x17' + # message type b'\x03\x03' + # TLS version b'\x00\x06' + # payload length b'\x00\x04' + b'\xff'*4 )) msgSock = MessageSocket(sock, defragmenter) for res in msgSock.recvMessage(): if res in (0, 1): self.assertTrue(False, "Blocking read") else: break self.assertIsNotNone(res) header, parser = res self.assertEqual(header.type, 23) self.assertEqual(header.version, (3, 3)) self.assertEqual(header.length, 6) self.assertEqual(parser.bytes, bytearray(b'\x00\x04' + b'\xff'*4)) def test_recvMessage_with_blocking_socket(self): defragmenter = Defragmenter() defragmenter.addStaticSize(21, 2) sock = MockSocket(bytearray( b'\x15' + # message type b'\x03\x03' + # TLS version b'\x00\x02' + # payload length b'\xff\xff' # message ), blockEveryOther=True, maxRet=1) msgSock = MessageSocket(sock, defragmenter) gotBlocked = False for res in msgSock.recvMessage(): if res in (0, 1): gotBlocked = True else: break self.assertTrue(gotBlocked) self.assertIsNotNone(res) header, parser = res self.assertEqual(header.type, 21) self.assertEqual(header.version, (3, 3)) self.assertEqual(parser.bytes, bytearray(b'\xff\xff')) def test_recvMessageBlocking(self): defragmenter = Defragmenter() defragmenter.addStaticSize(21, 2) sock = MockSocket(bytearray( b'\x15' + # message type b'\x03\x03' + # TLS version b'\x00\x02' + # payload length b'\xff\xff' # message ), blockEveryOther=True, maxRet=1) msgSock = MessageSocket(sock, defragmenter) res = msgSock.recvMessageBlocking() self.assertIsNotNone(res) header, parser = res self.assertEqual(header.type, 21) self.assertEqual(parser.bytes, bytearray(b'\xff\xff')) def test_flush(self): sock = MockSocket(bytearray()) msgSock = MessageSocket(sock, None) for res in msgSock.flush(): if res in (0, 1): self.assertTrue(False, "Blocking flush") else: break self.assertEqual(len(sock.sent), 0) for res in msgSock.flush(): if res in (0, 1): self.assertTrue(False, "Blocking flush") else: break self.assertEqual(len(sock.sent), 0) def test_queueMessage(self): sock = MockSocket(bytearray()) msgSocket = MessageSocket(sock, None) msg = Message(ContentType.alert, bytearray(b'\xff\xbb')) for res in msgSocket.queueMessage(msg): if res in (0, 1): self.assertTrue(False, "Blocking queue") else: break self.assertEqual(len(sock.sent), 0) msg = Message(ContentType.alert, bytearray(b'\xff\xaa')) for res in msgSocket.queueMessage(msg): if res in (0, 1): self.assertTrue(False, "Blocking queue") else: break self.assertEqual(len(sock.sent), 0) for res in msgSocket.flush(): if res in (0, 1): self.assertTrue(False, "Blocking flush") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(sock.sent[0], bytearray( b'\x15' + b'\x00\x00' + b'\x00\x04' + b'\xff\xbb' + b'\xff\xaa')) def test_queueMessage_with_conflicting_types(self): sock = MockSocket(bytearray()) msgSock = MessageSocket(sock, None) msgSock.version = (3, 3) msg = Message(ContentType.handshake, bytearray(b'\xaa\xaa\xaa')) for res in msgSock.queueMessage(msg): if res in (0, 1): self.assertTrue(False, "Blocking queue") else: break self.assertEqual(len(sock.sent), 0) msg = Message(ContentType.alert, bytearray(b'\x02\x01')) for res in msgSock.queueMessage(msg): if res in (0, 1): self.assertTrue(False, "Blocking queue") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(bytearray( b'\x16' + b'\x03\x03' + b'\x00\x03' + b'\xaa'*3), sock.sent[0]) for res in msgSock.flush(): if res in (0, 1): self.assertTrue(False, "Blocking flush") else: break self.assertEqual(len(sock.sent), 2) self.assertEqual(bytearray( b'\x15' + b'\x03\x03' + b'\x00\x02' + b'\x02\x01'), sock.sent[1]) def test_queueMessage_with_conflicting_types_and_blocking_socket(self): sock = MockSocket(bytearray(), blockEveryOther=True) sock.blockWrite = True msgSock = MessageSocket(sock, None) msgSock.version = (3, 3) msg = Message(ContentType.handshake, bytearray(b'\xaa\xaa\xaa')) blocked = False for res in msgSock.queueMessage(msg): if res in (0, 1): blocked = True else: break # no write so no blocking self.assertFalse(blocked) self.assertEqual(len(sock.sent), 0) msg = Message(ContentType.alert, bytearray(b'\x02\x01')) blocked = False for res in msgSock.queueMessage(msg): if res in (0, 1): blocked = True else: break # blocked once, so one write self.assertTrue(blocked) self.assertEqual(len(sock.sent), 1) self.assertEqual(bytearray( b'\x16' + b'\x03\x03' + b'\x00\x03' + b'\xaa'*3), sock.sent[0]) sock.blockWrite = True blocked = False for res in msgSock.flush(): if res in (0, 1): blocked = True else: break # blocked once, so one write self.assertTrue(blocked) self.assertEqual(len(sock.sent), 2) self.assertEqual(bytearray( b'\x15' + b'\x03\x03' + b'\x00\x02' + b'\x02\x01'), sock.sent[1]) def test_sendMessage(self): sock = MockSocket(bytearray(), blockEveryOther=True) sock.blockWrite = True msgSock = MessageSocket(sock, None) msgSock.version = (3, 3) msg = Message(ContentType.handshake, bytearray(b'\xaa\xaa\xaa')) blocked = False for res in msgSock.queueMessage(msg): if res in (0, 1): blocked = True else: break # no write so no blocking self.assertFalse(blocked) self.assertEqual(len(sock.sent), 0) msg = Message(ContentType.alert, bytearray(b'\x02\x01')) blocked = False for res in msgSock.sendMessage(msg): if res in (0, 1): blocked = True else: break self.assertTrue(blocked) self.assertEqual(len(sock.sent), 2) self.assertEqual(bytearray( b'\x16' + b'\x03\x03' + b'\x00\x03' + b'\xaa'*3), sock.sent[0]) self.assertEqual(bytearray( b'\x15' + b'\x03\x03' + b'\x00\x02' + b'\x02\x01'), sock.sent[1]) def test_sendMessageBlocking(self): sock = MockSocket(bytearray(), blockEveryOther=True) sock.blockWrite = True msgSock = MessageSocket(sock, None) msgSock.version = (3, 3) msg = Message(ContentType.handshake, bytearray(b'\xaa\xaa\xaa')) blocked = False for res in msgSock.queueMessage(msg): if res in (0, 1): blocked = True else: break # no write so no blocking self.assertFalse(blocked) self.assertEqual(len(sock.sent), 0) msg = Message(ContentType.alert, bytearray(b'\x02\x01')) msgSock.sendMessageBlocking(msg) self.assertEqual(len(sock.sent), 2) self.assertEqual(bytearray( b'\x16' + b'\x03\x03' + b'\x00\x03' + b'\xaa'*3), sock.sent[0]) self.assertEqual(bytearray( b'\x15' + b'\x03\x03' + b'\x00\x02' + b'\x02\x01'), sock.sent[1]) def test_queueMessageBlocking(self): sock = MockSocket(bytearray(), blockEveryOther=True) sock.blockWrite = True msgSock = MessageSocket(sock, None) msgSock.version = (3, 3) msg = Message(ContentType.handshake, bytearray(b'\xaa\xaa\xaa')) msgSock.queueMessageBlocking(msg) self.assertEqual(len(sock.sent), 0) msg = Message(ContentType.alert, bytearray(b'\x02\x01')) msgSock.queueMessageBlocking(msg) self.assertEqual(len(sock.sent), 1) self.assertEqual(bytearray( b'\x16' + b'\x03\x03' + b'\x00\x03' + b'\xaa'*3), sock.sent[0]) def test_flushBlocking(self): sock = MockSocket(bytearray()) msgSock = MessageSocket(sock, None) msgSock.flushBlocking() self.assertEqual(len(sock.sent), 0) def test_flushBlocking_with_data(self): sock = MockSocket(bytearray(), blockEveryOther=True) sock.blockWrite = True msgSock = MessageSocket(sock, None) msgSock.version = (3, 3) msg = Message(ContentType.handshake, bytearray(b'\xaa\xaa\xaa')) msgSock.queueMessageBlocking(msg) self.assertEqual(len(sock.sent), 0) msgSock.flushBlocking() self.assertEqual(len(sock.sent), 1) self.assertEqual(bytearray( b'\x16' + b'\x03\x03' + b'\x00\x03' + b'\xaa'*3), sock.sent[0]) tlslite-ng-0.5.1/unit_tests/test_tlslite_recordlayer.py000066400000000000000000002305271261665411200235010ustar00rootroot00000000000000# Copyright (c) 2014, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. # compatibility with Python 2.6, for that we need unittest2 package, # which is not available on 3.3 or 3.4 try: import unittest2 as unittest except ImportError: import unittest try: import mock from mock import call except ImportError: import unittest.mock as mock from unittest.mock import call import os import socket import errno import tlslite.utils.cryptomath as cryptomath from tlslite.messages import Message, ApplicationData, RecordHeader3 from tlslite.recordlayer import RecordSocket, ConnectionState, RecordLayer from tlslite.constants import ContentType, CipherSuite from unit_tests.mocksock import MockSocket from tlslite.errors import TLSRecordOverflow, TLSIllegalParameterException,\ TLSAbruptCloseError, TLSDecryptionFailed, TLSBadRecordMAC class TestRecordSocket(unittest.TestCase): def test___init__(self): sock = RecordSocket(-42) self.assertIsNotNone(sock) self.assertEqual(sock.sock, -42) self.assertEqual(sock.version, (0, 0)) def test_send(self): mockSock = MockSocket(bytearray(0)) sock = RecordSocket(mockSock) sock.version = (3, 3) msg = Message(ContentType.handshake, bytearray(10)) for result in sock.send(msg): if result in (0, 1): self.assertTrue(False, "Blocking socket") else: break self.assertEqual(len(mockSock.sent), 1) self.assertEqual(bytearray( b'\x16' + # handshake message b'\x03\x03' + # version b'\x00\x0a' + # payload length b'\x00'*10 # payload ), mockSock.sent[0]) def test_send_with_very_slow_socket(self): mockSock = MockSocket(bytearray(0), maxWrite=1, blockEveryOther=True) sock = RecordSocket(mockSock) msg = Message(ContentType.handshake, bytearray(b'\x32'*2)) gotRetry = False for result in sock.send(msg): if result in (0, 1): gotRetry = True else: break self.assertTrue(gotRetry) self.assertEqual([ bytearray(b'\x16'), # handshake message bytearray(b'\x00'), bytearray(b'\x00'), # version (unset) bytearray(b'\x00'), bytearray(b'\x02'), # payload length bytearray(b'\x32'), bytearray(b'\x32')], mockSock.sent) def test_send_with_errored_out_socket(self): mockSock = mock.MagicMock() mockSock.send.side_effect = socket.error(errno.ETIMEDOUT) sock = RecordSocket(mockSock) msg = Message(ContentType.handshake, bytearray(10)) gen = sock.send(msg) with self.assertRaises(socket.error): next(gen) def test_recv(self): mockSock = MockSocket(bytearray( b'\x16' + # type - handshake b'\x03\x03' + # TLSv1.2 b'\x00\x04' + # length b'\x00'*4 )) sock = RecordSocket(mockSock) for result in sock.recv(): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break header, data = result self.assertEqual(data, bytearray(4)) self.assertEqual(header.type, ContentType.handshake) self.assertEqual(header.version, (3, 3)) self.assertEqual(header.length, 4) def test_recv_stops_itelf(self): mockSock = MockSocket(bytearray( b'\x16' + # type - handshake b'\x03\x03' + # TLSv1.2 b'\x00\x04' + # length b'\x00'*4 )) sock = RecordSocket(mockSock) for result in sock.recv(): if result in (0, 1): self.assertTrue(False, "blocking socket") header, data = result self.assertEqual(data, bytearray(4)) self.assertEqual(header.type, ContentType.handshake) self.assertEqual(header.version, (3, 3)) self.assertEqual(header.length, 4) def test_recv_with_trickling_socket(self): mockSock = MockSocket(bytearray( b'\x16' + # type - handshake b'\x03\x03' + # TLSv1.2 b'\x00\x04' + # length b'\x00'*4 ), maxRet=1) sock = RecordSocket(mockSock) for result in sock.recv(): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break header, data = result self.assertEqual(bytearray(4), data) def test_recv_with_blocking_socket(self): mockSock = mock.MagicMock() mockSock.recv.side_effect = socket.error(errno.EWOULDBLOCK) sock = RecordSocket(mockSock) gen = sock.recv() self.assertEqual(0, next(gen)) def test_recv_with_errored_out_socket(self): mockSock = mock.MagicMock() mockSock.recv.side_effect = socket.error(errno.ETIMEDOUT) sock = RecordSocket(mockSock) gen = sock.recv() with self.assertRaises(socket.error): next(gen) def test_recv_with_empty_socket(self): mockSock = mock.MagicMock() mockSock.recv.side_effect = [bytearray(0)] sock = RecordSocket(mockSock) gen = sock.recv() with self.assertRaises(TLSAbruptCloseError): next(gen) def test_recv_with_slow_socket(self): mockSock = MockSocket(bytearray( b'\x16' + # type - handshake b'\x03\x03' + # TLSv1.2 b'\x00\x04' + # length b'\x00'*4 ), maxRet=1, blockEveryOther=True) sock = RecordSocket(mockSock) gotRetry = False for result in sock.recv(): if result in (0, 1): gotRetry = True else: break header, data = result self.assertTrue(gotRetry) self.assertEqual(bytearray(4), data) def test_recv_with_malformed_record(self): mockSock = MockSocket(bytearray( b'\x01' + # wrong type b'\x03\x03' + # TLSv1.2 b'\x00\x01' + # length b'\x00')) sock = RecordSocket(mockSock) gen = sock.recv() with self.assertRaises(TLSIllegalParameterException): next(gen) def test_recv_with_too_big_record(self): mockSock = MockSocket(bytearray( b'\x16' + # type - handshake b'\x03\x03' + # TLSv1.2 b'\xff\xff' + # length b'\x00'*65536)) sock = RecordSocket(mockSock) gen = sock.recv() with self.assertRaises(TLSRecordOverflow): next(gen) def test_recv_with_empty_data(self): mockSock = MockSocket(bytearray( b'\x16' + # type - handshake b'\x03\x03' + # TLSv1.2 b'\x00\x00')) # length sock = RecordSocket(mockSock) gen = sock.recv() for result in sock.recv(): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break header, data = result self.assertEqual(ContentType.handshake, header.type) self.assertEqual((3, 3), header.version) self.assertEqual(0, header.length) self.assertEqual(bytearray(0), data) def test_recv_with_SSL2_record(self): mockSock = MockSocket(bytearray( b'\x80' + # tag b'\x04' + # length b'\x00'*4)) sock = RecordSocket(mockSock) for result in sock.recv(): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break header, data = result self.assertTrue(header.ssl2) self.assertEqual(ContentType.handshake, header.type) self.assertEqual(4, header.length) self.assertEqual((2, 0), header.version) self.assertEqual(bytearray(4), data) def test_recv_with_not_complete_SSL2_record(self): mockSock = MockSocket(bytearray( b'\x80' + # tag b'\x04' + # length b'\x00'*3)) sock = RecordSocket(mockSock) for result in sock.recv(): break self.assertEqual(0, result) def test_recv_with_SSL2_record_with_incomplete_header(self): mockSock = MockSocket(bytearray( b'\x80' # tag )) sock = RecordSocket(mockSock) for result in sock.recv(): break self.assertEqual(0, result) class TestConnectionState(unittest.TestCase): def test___init__(self): connState = ConnectionState() self.assertIsNotNone(connState) self.assertIsNone(connState.macContext) self.assertIsNone(connState.encContext) self.assertEqual(0, connState.seqnum) def test_getSeqNumBytes(self): connState = ConnectionState() self.assertEqual(bytearray(b'\x00'*8), connState.getSeqNumBytes()) self.assertEqual(bytearray(b'\x00'*7 + b'\x01'), connState.getSeqNumBytes()) self.assertEqual(bytearray(b'\x00'*7 + b'\x02'), connState.getSeqNumBytes()) self.assertEqual(bytearray(b'\x00'*7 + b'\x03'), connState.getSeqNumBytes()) self.assertEqual(4, connState.seqnum) class TestRecordLayer(unittest.TestCase): def test___init__(self): recordLayer = RecordLayer(None) self.assertIsNotNone(recordLayer) self.assertIsNone(recordLayer.getCipherName()) self.assertIsNone(recordLayer.getCipherImplementation()) self.assertFalse(recordLayer.isCBCMode()) def test_sendRecord(self): sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) hello = Message(ContentType.handshake, bytearray(10)) for result in recordLayer.sendRecord(hello): if result in (0, 1): self.assertTrue(False, "Blocking write") else: break self.assertEqual(len(sock.sent), 1) def test_shutdown(self): sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) # make sure it doesn't throw exceptions recordLayer.shutdown() def test_getCipherName(self): sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) recordLayer.version = (3, 3) recordLayer.calcPendingStates(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) recordLayer.changeWriteState() self.assertEqual('aes128', recordLayer.getCipherName()) self.assertTrue(recordLayer.isCBCMode()) def test_getCipherImplementation(self): sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) recordLayer.version = (3, 3) recordLayer.calcPendingStates(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) recordLayer.changeWriteState() if cryptomath.m2cryptoLoaded: self.assertEqual('openssl', recordLayer.getCipherImplementation()) elif cryptomath.pycryptoLoaded: self.assertEqual('pycrypto', recordLayer.getCipherImplementation()) else: self.assertEqual('python', recordLayer.getCipherImplementation()) def test_sendRecord_with_encrypting_set_up_tls1_2(self): patcher = mock.patch.object(os, 'urandom', lambda x: bytearray(x)) mock_random = patcher.start() self.addCleanup(patcher.stop) sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) recordLayer.version = (3, 3) recordLayer.calcPendingStates(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) recordLayer.changeWriteState() app_data = ApplicationData().create(bytearray(b'test')) self.assertIsNotNone(app_data) self.assertTrue(len(app_data.write()) > 3) for result in recordLayer.sendRecord(app_data): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(sock.sent[0][:5], bytearray( b'\x17' + # application data b'\x03\x03' + # TLS1.2 b'\x00\x30' # length - 48 bytes (3 blocks) )) # (4 bytes of data + 20 bytes of MAC + IV) self.assertEqual(bytearray( b'\x48\x26\x1f\xc1\x9c\xde\x22\x92\xdd\xe4\x7c\xfc\x6f\x29\x52\xd6'+ b'\xc5\xec\x44\x21\xca\xe3\xd1\x34\x64\xad\xff\xb1\xea\xfa\xd5\xe3'+ b'\x9f\x73\xec\xa9\xa6\x82\x55\x8e\x3a\x8c\x94\x96\xda\x06\x09\x8d' ), sock.sent[0][5:]) def test_sendRecord_with_SHA256_tls1_2(self): patcher = mock.patch.object(os, 'urandom', lambda x: bytearray(x)) mock_random = patcher.start() self.addCleanup(patcher.stop) sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) recordLayer.version = (3, 3) recordLayer.calcPendingStates( CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) recordLayer.changeWriteState() app_data = ApplicationData().create(bytearray(b'test')) self.assertIsNotNone(app_data) self.assertTrue(len(app_data.write()) > 3) for result in recordLayer.sendRecord(app_data): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(sock.sent[0][:5], bytearray( b'\x17' + # application data b'\x03\x03' + # TLS1.2 b'\x00\x40' # length - 64 bytes (4 blocks) )) # (4 bytes of data + 32 bytes of MAC + IV) self.assertEqual(bytearray( b'pd\x87\xde\xab\x9aU^\x7f\x7f\xa9\x00\xd14\'\x0c' + b'\xde\xa73r\x9f\xb0O\x0eo_\x93\xec-\xb1c^' + b'\x9a{\xde7g=\xef\x94\xd9K\xcc\x92\xe8\xa6\x10R' + b'\xe0"c:7\xa9\xd7}X\x00[\x88\xce\xfe|\t' ), sock.sent[0][5:]) def test_sendRecord_with_encrypting_set_up_tls1_1(self): patcher = mock.patch.object(os, 'urandom', lambda x: bytearray(x)) mock_random = patcher.start() self.addCleanup(patcher.stop) sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) recordLayer.version = (3, 2) recordLayer.calcPendingStates(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) recordLayer.changeWriteState() app_data = ApplicationData().create(bytearray(b'test')) self.assertIsNotNone(app_data) self.assertTrue(len(app_data.write()) > 3) for result in recordLayer.sendRecord(app_data): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(sock.sent[0][:5], bytearray( b'\x17' + # application data b'\x03\x02' + # TLS1.2 b'\x00\x30' # length - 48 bytes (3 blocks) )) # (4 bytes of data + 20 bytes of MAC + IV) self.assertEqual(bytearray( b'b\x8e\xee\xddV\\W=\x810\xd5\x0c\xae \x84\xa8' + b'^\x91\xa4d[\xe4\xde\x90\xee{f\xbb\xcd_\x1ao' + b'\xa8\x8c!k\xab\x03\x03\x19.\x1dFMt\x08h^' ), sock.sent[0][5:]) def test_sendRecord_with_encrypting_set_up_tls1_0(self): sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) recordLayer.version = (3, 1) recordLayer.calcPendingStates(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) recordLayer.changeWriteState() app_data = ApplicationData().create(bytearray(b'test')) self.assertIsNotNone(app_data) self.assertTrue(len(app_data.write()) > 3) for result in recordLayer.sendRecord(app_data): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(sock.sent[0][:5], bytearray( b'\x17' + # application data b'\x03\x01' + # TLS1.0 b'\x00\x20' # length - 48 bytes (3 blocks) )) # (4 bytes of data + 20 bytes of MAC) self.assertEqual(sock.sent[0][5:], bytearray( b'\xebK\x0ff\x9cI\n\x011\xd0w\x9d\x11Z\xb4\xe5' + b'D\xe9\xec\x8d\xdfd\xed\x94\x9f\xe6K\x08(\x08\xf6\xb7' )) def test_sendRecord_with_stream_cipher_and_tls1_0(self): sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) recordLayer.version = (3, 1) recordLayer.calcPendingStates(CipherSuite.TLS_RSA_WITH_RC4_128_SHA, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) recordLayer.changeWriteState() app_data = ApplicationData().create(bytearray(b'test')) self.assertIsNotNone(app_data) self.assertTrue(len(app_data.write()) > 3) for result in recordLayer.sendRecord(app_data): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(sock.sent[0][:5], bytearray( b'\x17' + # application data b'\x03\x01' + # SSL3 b'\x00\x18' # length - 24 bytes )) # (4 bytes of data + 20 bytes of MAC) self.assertEqual(sock.sent[0][5:], bytearray( b'B\xb8H\xc6\xd7\\\x01\xe27\xa9\x86\xf2\xfdm!\x1d' + b'\xa1\xaf]Q%y5\x1e' )) def test_sendRecord_with_MD5_MAC_and_tls1_0(self): sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) recordLayer.version = (3, 1) recordLayer.calcPendingStates(CipherSuite.TLS_RSA_WITH_RC4_128_MD5, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) recordLayer.changeWriteState() app_data = ApplicationData().create(bytearray(b'test')) self.assertIsNotNone(app_data) self.assertTrue(len(app_data.write()) > 3) for result in recordLayer.sendRecord(app_data): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(sock.sent[0][:5], bytearray( b'\x17' + # application data b'\x03\x01' + # SSL3 b'\x00\x14' # length - 20 bytes )) # (4 bytes of data + 16 bytes of MAC) self.assertEqual(sock.sent[0][5:], bytearray( b'0}R\xe3T\xce`\xf9\x8f\x9d\xe6r\xc4\xdf\xd9\xd5' + b'\xbf/sL' )) def test_sendRecord_with_AES256_cipher_and_tls1_0(self): sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) recordLayer.version = (3, 1) recordLayer.calcPendingStates(CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) recordLayer.changeWriteState() app_data = ApplicationData().create(bytearray(b'test')) self.assertIsNotNone(app_data) self.assertTrue(len(app_data.write()) > 3) for result in recordLayer.sendRecord(app_data): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(sock.sent[0][:5], bytearray( b'\x17' + # application data b'\x03\x01' + # SSL3 b'\x00\x20' # length - 32 bytes (2 blocks) )) # (4 bytes of data + 20 bytes of MAC) self.assertEqual(sock.sent[0][5:], bytearray( b'\xb8\xe5\xc5\x9c\xe6\xad\xf0uY\x19L\x17\xf8\xe7F3' + b'}\xcct\x84 3) for result in recordLayer.sendRecord(app_data): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(sock.sent[0][:5], bytearray( b'\x17' + # application data b'\x03\x01' + # SSL3 b'\x00\x20' # length - 32 bytes (2 blocks) )) # (4 bytes of data + 20 bytes of MAC) self.assertEqual(sock.sent[0][5:], bytearray( b'\xac\x12\xa55\x1a\x1f\xe2\xe5<\xb3[;\xc4\xa6\x9bF' + b'\x8d\x16\x8b\xa3N\xe6\xfa\x14\xa9\xb9\xc7\x08w\xf2V\xe2' )) def test_sendRecord_with_encrypting_set_up_ssl3(self): sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) recordLayer.version = (3, 0) recordLayer.calcPendingStates(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) recordLayer.changeWriteState() app_data = ApplicationData().create(bytearray(b'test')) self.assertIsNotNone(app_data) self.assertTrue(len(app_data.write()) > 3) for result in recordLayer.sendRecord(app_data): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(sock.sent[0][:5], bytearray( b'\x17' + # application data b'\x03\x00' + # SSL3 b'\x00\x20' # length - 48 bytes (3 blocks) )) # (4 bytes of data + 20 bytes of MAC) self.assertEqual(sock.sent[0][5:], bytearray( b'\xc5\x16y\xf9\ra\xd9=\xec\x8b\x93\'\xb7\x05\xe6\xad' + b'\xff\x842\xc7\xa2\x0byd\xab\x1a\xfd\xaf\x05\xd6\xba\x89' )) def test_sendRecord_with_wrong_SSL_version(self): sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) with self.assertRaises(AssertionError): recordLayer.calcPendingStates( CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) def test_sendRecord_with_invalid_ciphersuite(self): sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) with self.assertRaises(AssertionError): recordLayer.calcPendingStates( CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) def test_sendRecord_with_slow_socket(self): mockSock = MockSocket(bytearray(0), maxWrite=1, blockEveryOther=True) sock = RecordLayer(mockSock) msg = Message(ContentType.handshake, bytearray(b'\x32'*2)) gotRetry = False for result in sock.sendRecord(msg): if result in (0, 1): gotRetry = True else: break self.assertTrue(gotRetry) self.assertEqual([ bytearray(b'\x16'), # handshake message bytearray(b'\x00'), bytearray(b'\x00'), # version (unset) bytearray(b'\x00'), bytearray(b'\x02'), # payload length bytearray(b'\x32'), bytearray(b'\x32')], mockSock.sent) def test_sendRecord_with_encryptThenMAC_and_unset_crypto_state(self): sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) recordLayer.version = (3, 1) recordLayer.encryptThenMAC = True app_data = ApplicationData().create(bytearray(b'test')) for result in recordLayer.sendRecord(app_data): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(bytearray( b'\x17' + # application data b'\x03\x01' + # TLS version b'\x00\x04' + # length b'test'), sock.sent[0]) def test_sendRecord_with_encryptThenMAC_in_TLSv1_0(self): sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) recordLayer.version = (3, 1) recordLayer.encryptThenMAC = True recordLayer.calcPendingStates(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) recordLayer.changeWriteState() app_data = ApplicationData().create(bytearray(b'test')) for result in recordLayer.sendRecord(app_data): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(bytearray( b'\x17' + # application data b'\x03\x01' + # TLS version b'\x00\x24' + # length - 1 block + 20 bytes of MAC b'\xc7\xd6\xaf:.MY\x80W\x81\xd2|5A#\xd5' + b'X\xcd\xdc\'o\xb3I\xdd-\xfc\tneq~\x0f' + b'd\xdb\xbdw'), sock.sent[0]) def test_sendRecord_with_encryptThenMAC_in_TLSv1_2(self): patcher = mock.patch.object(os, 'urandom', lambda x: bytearray(x)) mock_random = patcher.start() self.addCleanup(patcher.stop) sock = MockSocket(bytearray(0)) recordLayer = RecordLayer(sock) recordLayer.version = (3, 3) recordLayer.encryptThenMAC = True recordLayer.calcPendingStates(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, bytearray(48), # master secret bytearray(32), # client random bytearray(32), # server random None) recordLayer.changeWriteState() app_data = ApplicationData().create(bytearray(b'test')) for result in recordLayer.sendRecord(app_data): if result in (0, 1): self.assertTrue(False, "blocking socket") else: break self.assertEqual(len(sock.sent), 1) self.assertEqual(bytearray( b'\x17' + # application data b'\x03\x03' + # TLS version b'\x00\x34' + # length - IV + 1 block + 20 bytes of MAC b'H&\x1f\xc1\x9c\xde"\x92\xdd\xe4|\xfco)R\xd6' + b'\x11~\xf2\xed\xa0l\x11\xb4\xb7\xbd\x1a- j), (ct_gt_u32(i, j) == 1)) for i in range(2**32-256, 2**32): for j in range(2**32-256, 2**32): self.assertEqual((i > j), (ct_gt_u32(i, j) == 1)) for i in range(0, 256): for j in range(2**32-256, 2**32): self.assertEqual((i > j), (ct_gt_u32(i, j) == 1)) for i in range(2**32-256, 2**32): for j in range(0, 256): self.assertEqual((i > j), (ct_gt_u32(i, j) == 1)) def test_ct_le_u32(self): for i in range(0, 256): for j in range(0, 256): self.assertEqual((i <= j), (ct_le_u32(i, j) == 1)) for i in range(2**32-256, 2**32): for j in range(2**32-256, 2**32): self.assertEqual((i <= j), (ct_le_u32(i, j) == 1)) for i in range(0, 256): for j in range(2**32-256, 2**32): self.assertEqual((i <= j), (ct_le_u32(i, j) == 1)) for i in range(2**32-256, 2**32): for j in range(0, 256): self.assertEqual((i <= j), (ct_le_u32(i, j) == 1)) def test_ct_lsb_prop_u8(self): for i in range(0, 256): self.assertEqual(((i & 0x1) == 1), (ct_lsb_prop_u8(i) == 0xff)) self.assertEqual(((i & 0x1) == 0), (ct_lsb_prop_u8(i) == 0x00)) def test_ct_isnonzero_u32(self): for i in range(0, 256): self.assertEqual((i != 0), (ct_isnonzero_u32(i) == 1)) def test_ct_neq_u32(self): for i in range(0, 256): for j in range(0, 256): self.assertEqual((i != j), (ct_neq_u32(i, j) == 1)) for i in range(2**32-128, 2**32): for j in range(2**32-128, 2**32): self.assertEqual((i != j), (ct_neq_u32(i, j) == 1)) def test_ct_eq_u32(self): for i in range(0, 256): for j in range(0, 256): self.assertEqual((i == j), (ct_eq_u32(i, j) == 1)) for i in range(2**32-128, 2**32): for j in range(2**32-128, 2**32): self.assertEqual((i == j), (ct_eq_u32(i, j) == 1)) class TestContanttimeCBCCheck(unittest.TestCase): @staticmethod def data_prepare(application_data, seqnum_bytes, content_type, version, mac, key): r_layer = RecordLayer(None) r_layer.version = version h = hmac.new(key, digestmod=mac) digest = r_layer._calculateMAC(h, seqnum_bytes, content_type, application_data) return application_data + digest def test_with_empty_data_and_minimum_pad(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 1) application_data = bytearray(0) mac = hashlib.sha1 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray(b'\x00') data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertTrue(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_empty_data_and_maximum_pad(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 1) application_data = bytearray(0) mac = hashlib.sha1 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray(b'\xff'*256) data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertTrue(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_little_data_and_minimum_pad(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 1) application_data = bytearray(b'\x01'*32) mac = hashlib.sha1 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray(b'\x00') data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertTrue(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_little_data_and_maximum_pad(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 1) application_data = bytearray(b'\x01'*32) mac = hashlib.sha1 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray(b'\xff'*256) data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertTrue(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_lots_of_data_and_minimum_pad(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 1) application_data = bytearray(b'\x01'*1024) mac = hashlib.sha1 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray(b'\x00') data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertTrue(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_lots_of_data_and_maximum_pad(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 1) application_data = bytearray(b'\x01'*1024) mac = hashlib.sha1 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray(b'\xff'*256) data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertTrue(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_lots_of_data_and_small_pad(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 1) application_data = bytearray(b'\x01'*1024) mac = hashlib.sha1 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray(b'\x0a'*11) data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertTrue(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_too_little_data(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 1) mac = hashlib.sha1 data = bytearray(mac().digest_size) h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertFalse(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_invalid_hash(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 1) application_data = bytearray(b'\x01'*1024) mac = hashlib.sha1 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) data[-1] ^= 0xff padding = bytearray(b'\xff'*256) data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertFalse(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_invalid_pad(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 1) application_data = bytearray(b'\x01'*1024) mac = hashlib.sha1 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray(b'\x00' + b'\xff'*255) data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertFalse(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_pad_longer_than_data(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 1) application_data = bytearray(b'\x01') mac = hashlib.sha1 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray(b'\xff') data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertFalse(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_pad_longer_than_data_in_SSLv3(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 0) application_data = bytearray(b'\x01') mac = hashlib.sha1 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray([len(application_data) + mac().digest_size + 1]) data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertFalse(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_null_pad_in_SSLv3(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 0) application_data = bytearray(b'\x01'*10) mac = hashlib.md5 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray(b'\x00'*10 + b'\x0a') data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertTrue(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_MD5(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 1) application_data = bytearray(b'\x01'*10) mac = hashlib.md5 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray(b'\x0a'*11) data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertTrue(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_SHA256(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 3) application_data = bytearray(b'\x01'*10) mac = hashlib.sha256 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray(b'\x0a'*11) data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertTrue(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) def test_with_SHA384(self): key = compatHMAC(bytearray(20)) seqnum_bytes = bytearray(16) content_type = 0x14 version = (3, 3) application_data = bytearray(b'\x01'*10) mac = hashlib.sha384 data = self.data_prepare(application_data, seqnum_bytes, content_type, version, mac, key) padding = bytearray(b'\x0a'*11) data += padding h = hmac.new(key, digestmod=mac) h.block_size = mac().block_size # python2 workaround self.assertTrue(ct_check_cbc_mac_and_pad(data, h, seqnum_bytes, content_type, version)) class TestCompareDigest(unittest.TestCase): def test_with_equal_length(self): self.assertTrue(ct_compare_digest(bytearray(10), bytearray(10))) self.assertTrue(ct_compare_digest(bytearray(b'\x02'*8), bytearray(b'\x02'*8))) def test_different_lengths(self): self.assertFalse(ct_compare_digest(bytearray(10), bytearray(12))) self.assertFalse(ct_compare_digest(bytearray(20), bytearray(12))) def test_different(self): self.assertFalse(ct_compare_digest(bytearray(b'\x01'), bytearray(b'\x03'))) self.assertFalse(ct_compare_digest(bytearray(b'\x01'*10 + b'\x02'), bytearray(b'\x01'*10 + b'\x03'))) self.assertFalse(ct_compare_digest(bytearray(b'\x02' + b'\x01'*10), bytearray(b'\x03' + b'\x01'*10))) tlslite-ng-0.5.1/unit_tests/test_tlslite_utils_cryptomath.py000066400000000000000000000072341261665411200245750ustar00rootroot00000000000000# Copyright (c) 2014, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. # compatibility with Python 2.6, for that we need unittest2 package, # which is not available on 3.3 or 3.4 try: import unittest2 as unittest except ImportError: import unittest from tlslite.utils.cryptomath import isPrime class TestIsPrime(unittest.TestCase): def test_with_small_primes(self): self.assertTrue(isPrime(3)) self.assertTrue(isPrime(5)) self.assertTrue(isPrime(7)) self.assertTrue(isPrime(11)) def test_with_small_composites(self): self.assertFalse(isPrime(4)) self.assertFalse(isPrime(6)) self.assertFalse(isPrime(9)) self.assertFalse(isPrime(10)) def test_with_hard_primes_to_test(self): # XXX Rabin-Miller fails to properly detect following composites with self.assertRaises(AssertionError): for i in range(100): # OEIS A014233 self.assertFalse(isPrime(2047)) self.assertFalse(isPrime(1373653)) self.assertFalse(isPrime(25326001)) self.assertFalse(isPrime(3215031751)) self.assertFalse(isPrime(2152302898747)) self.assertFalse(isPrime(3474749660383)) self.assertFalse(isPrime(341550071728321)) self.assertFalse(isPrime(341550071728321)) self.assertFalse(isPrime(3825123056546413051)) self.assertFalse(isPrime(3825123056546413051)) self.assertFalse(isPrime(3825123056546413051)) def test_with_big_primes(self): # NextPrime[2^256] self.assertTrue(isPrime(115792089237316195423570985008687907853269984665640564039457584007913129640233)) # NextPrime[2^1024] self.assertTrue(isPrime(179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137859)) def test_with_big_composites(self): # NextPrime[2^256]-2 (factors: 71, 1559, 4801, 7703, 28286...8993) self.assertFalse(isPrime(115792089237316195423570985008687907853269984665640564039457584007913129640233-2)) # NextPrime[2^256]+2 (factors: 3^2, 5, 7, 11, 1753, 19063..7643) self.assertFalse(isPrime(115792089237316195423570985008687907853269984665640564039457584007913129640233+2)) # NextPrime[2^1024]-2 self.assertFalse(isPrime(179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137859-2)) # NextPrime[2^1024]+2 self.assertFalse(isPrime(179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137859+2)) # NextPrime[NextPrime[2^512]]*NextPrime[2^512] self.assertFalse(isPrime(179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639477074095512480796227391561801824887394139579933613278628104952355769470429079061808809522886423955917442317693387325171135071792698344550223571732405562649211)) tlslite-ng-0.5.1/unit_tests/test_tlslite_utils_keyfactory.py000066400000000000000000000241131261665411200245560ustar00rootroot00000000000000# Copyright (c) 2015, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. # compatibility with Python 2.6, for that we need unittest2 package, # which is not available on 3.3 or 3.4 try: import unittest2 as unittest except ImportError: import unittest from tlslite.utils.keyfactory import parsePEMKey from tlslite.utils.rsakey import RSAKey from tlslite.utils import cryptomath class TestParsePEMKey(unittest.TestCase): # generated with: # openssl req -x509 -newkey rsa:1024 -keyout localhost.key \ # -out localhost.crt -subj /CN=localhost -nodes -batch -sha256 privKey_str = str( "-----BEGIN PRIVATE KEY-----"\ "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANEJBHmpEslfyzLU"\ "3gEXUbV+aXW81blLqjiHc95YO2DskSf6Mi0z81l6Ssa//7eBT0L2LEiYlTpT5PPe"\ "RTburDRf7iUMkBnxVmCpBOn8xYn0OrPZLLLJBZS9Q1SP3Q/2Z+7IM7mtj9UsiyR0"\ "E07NTLTG9e9P319hAT5A8/tpGCjdAgMBAAECgYBVItsTwezI358fANu6jgjVZrsF"\ "HPffFBYsF971O/JTM4abRaeSCYqfctNpx2EbGCt0FldK6fo9W1XwjSKbkPHJVo12"\ "Lfeyn48iRlTfzp/VVSpydieaCyexRAQElC59RmaA0z5t9H5F+WLgx7DyVDSyitn5"\ "3b/l+wzSDzRCGLkzcQJBAO9d4LKtzLS78dkU2MiWjJdoAi9q9notzqB/OcJJ8dzl"\ "jCmU5jt0hanwVFElzJeQDfvSXl0nQRePkbG51X1BDjcCQQDfj5HGNGTgNPtmj61s"\ "z8WSiLuOHX/SEWRTk0MfB4l4f+Ymx6Ie2wco5w8a0QYEGpPYo09ZXPgWPX0uJSaa"\ "NZeLAkEAgGzj07n/7LAx0ACpVuW/RLSfB4Xh/Cd7hwz7lkxKIfRewSiMZjXcSRMS"\ "if83x9GYTxXNXzliaRu0VaCY9Hzk/QJBAKx6VZs3XQRlm/f6rXAftGxjNWBlffIS"\ "HPclzEkqRXNEKcqNhpSLozB5Y3vq+9s6rgobpOJrCbQO6H8rhma/JhUCQGmkTlFF"\ "CpeK/UoX1sCtwAke8ubS+cc+l/XIhCvltbqeMG4vipzGVoolUZFdPvIW2PZ+PSC/"\ "f3XiNjay5aqnxck="\ "-----END PRIVATE KEY-----") cert_str = str( "-----BEGIN CERTIFICATE-----"\ "MIIB9jCCAV+gAwIBAgIJAMyn9DpsTG55MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV"\ "BAMMCWxvY2FsaG9zdDAeFw0xNTAxMjExNDQzMDFaFw0xNTAyMjAxNDQzMDFaMBQx"\ "EjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA"\ "0QkEeakSyV/LMtTeARdRtX5pdbzVuUuqOIdz3lg7YOyRJ/oyLTPzWXpKxr//t4FP"\ "QvYsSJiVOlPk895FNu6sNF/uJQyQGfFWYKkE6fzFifQ6s9kssskFlL1DVI/dD/Zn"\ "7sgzua2P1SyLJHQTTs1MtMb170/fX2EBPkDz+2kYKN0CAwEAAaNQME4wHQYDVR0O"\ "BBYEFJtvXbRmxRFXYVMOPH/29pXCpGmLMB8GA1UdIwQYMBaAFJtvXbRmxRFXYVMO"\ "PH/29pXCpGmLMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADgYEAkOgC7LP/"\ "Rd6uJXY28HlD2K+/hMh1C3SRT855ggiCMiwstTHACGgNM+AZNqt6k8nSfXc6k1gw"\ "5a7SGjzkWzMaZC3ChBeCzt/vIAGlMyXeqTRhjTCdc/ygRv3NPrhUKKsxUYyXRk5v"\ "g/g6MwxzXfQP3IyFu3a9Jia/P89Z1rQCNRY="\ "-----END CERTIFICATE-----"\ ) privKey_str_newLines = str( "-----BEGIN PRIVATE KEY-----\n"\ "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANEJBHmpEslfyzLU\n"\ "3gEXUbV+aXW81blLqjiHc95YO2DskSf6Mi0z81l6Ssa//7eBT0L2LEiYlTpT5PPe\n"\ "RTburDRf7iUMkBnxVmCpBOn8xYn0OrPZLLLJBZS9Q1SP3Q/2Z+7IM7mtj9UsiyR0\n"\ "E07NTLTG9e9P319hAT5A8/tpGCjdAgMBAAECgYBVItsTwezI358fANu6jgjVZrsF\n"\ "HPffFBYsF971O/JTM4abRaeSCYqfctNpx2EbGCt0FldK6fo9W1XwjSKbkPHJVo12\n"\ "Lfeyn48iRlTfzp/VVSpydieaCyexRAQElC59RmaA0z5t9H5F+WLgx7DyVDSyitn5\n"\ "3b/l+wzSDzRCGLkzcQJBAO9d4LKtzLS78dkU2MiWjJdoAi9q9notzqB/OcJJ8dzl\n"\ "jCmU5jt0hanwVFElzJeQDfvSXl0nQRePkbG51X1BDjcCQQDfj5HGNGTgNPtmj61s\n"\ "z8WSiLuOHX/SEWRTk0MfB4l4f+Ymx6Ie2wco5w8a0QYEGpPYo09ZXPgWPX0uJSaa\n"\ "NZeLAkEAgGzj07n/7LAx0ACpVuW/RLSfB4Xh/Cd7hwz7lkxKIfRewSiMZjXcSRMS\n"\ "if83x9GYTxXNXzliaRu0VaCY9Hzk/QJBAKx6VZs3XQRlm/f6rXAftGxjNWBlffIS\n"\ "HPclzEkqRXNEKcqNhpSLozB5Y3vq+9s6rgobpOJrCbQO6H8rhma/JhUCQGmkTlFF\n"\ "CpeK/UoX1sCtwAke8ubS+cc+l/XIhCvltbqeMG4vipzGVoolUZFdPvIW2PZ+PSC/\n"\ "f3XiNjay5aqnxck=\n"\ "-----END PRIVATE KEY-----\n") cert_str_newLines = str( "-----BEGIN CERTIFICATE-----\n"\ "MIIB9jCCAV+gAwIBAgIJAMyn9DpsTG55MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV\n"\ "BAMMCWxvY2FsaG9zdDAeFw0xNTAxMjExNDQzMDFaFw0xNTAyMjAxNDQzMDFaMBQx\n"\ "EjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n"\ "0QkEeakSyV/LMtTeARdRtX5pdbzVuUuqOIdz3lg7YOyRJ/oyLTPzWXpKxr//t4FP\n"\ "QvYsSJiVOlPk895FNu6sNF/uJQyQGfFWYKkE6fzFifQ6s9kssskFlL1DVI/dD/Zn\n"\ "7sgzua2P1SyLJHQTTs1MtMb170/fX2EBPkDz+2kYKN0CAwEAAaNQME4wHQYDVR0O\n"\ "BBYEFJtvXbRmxRFXYVMOPH/29pXCpGmLMB8GA1UdIwQYMBaAFJtvXbRmxRFXYVMO\n"\ "PH/29pXCpGmLMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADgYEAkOgC7LP/\n"\ "Rd6uJXY28HlD2K+/hMh1C3SRT855ggiCMiwstTHACGgNM+AZNqt6k8nSfXc6k1gw\n"\ "5a7SGjzkWzMaZC3ChBeCzt/vIAGlMyXeqTRhjTCdc/ygRv3NPrhUKKsxUYyXRk5v\n"\ "g/g6MwxzXfQP3IyFu3a9Jia/P89Z1rQCNRY=\n"\ "-----END CERTIFICATE-----\n"\ ) # generated with: # openssl genrsa -out privkey.pem 1024 privRSAKey_str = str( "-----BEGIN RSA PRIVATE KEY-----"\ "MIICXAIBAAKBgQCnBW08FYymHDwA+Vug5QWH2g0nX2EnTnzdyvaZ/mE1pCTxV+Fp"\ "j0glrRIoPJPP+rZTcl/cqm7FSD+n2QDWHrg4h8xFPC7uPyfrbd/u6hTO3edu0los"\ "tKkq93ZiM/kmfHIS57/nOiG9ETySx4TP4ca6dhNoIAU5uMQDHjhgSXSU4wIDAQAB"\ "AoGAOB2PpOdMmSbVVjJxga5Q3GL7lmXqW214cIBXuEeKW55ptxiiqHe2csoiVph7"\ "xR3kEkdUQ+yTSP9MO9Wh/U7W78RTKM21tRn2uwzVD4p0whVK/WCa0zsSu41VQ23l"\ "wxN3Byrxw6jTTKD3gSLJc/4kGaduXgc/1IHCtmVaD9L2XJkCQQDVjqaDuQhPqzGI"\ "kHZ77PARFLf3q+nVIFSIf1m/wxLQEj1HZ9PuyHNm0USQYswwDnh9g7F25YylWex+"\ "yiefS0/fAkEAyDcekKtYudtgOhyN7tgSlUiHEyLCRo5IeazKQ0wNCDWfok9HYpEo"\ "mOuE+NIQEcCJu+sRXK6rykJQGkHgYsALfQJAN5aJK3Jngm1aWGTaIonbN2cAN/zM"\ "wghHWLxlfS/m3rhQsRyKovYUa+f/A+JjqgKqRGmaMQuxX30XvS0bwTAWWwJAQl3j"\ "B9mEg7cwYpLsiWueXVW5UKKI+5JWe97G/R/MghgkXk0hQI8VgfswDLq1EO1duqjl"\ "DG/qChWJL+r+Uj2OkQJBAK22WDZnIa52dm6G2dC+pM7TC10p7pwOS+G4YsA92Jd2"\ "rBjtgPGNR6tCjWMh0+2AUF5lTbXAPqECeV6MIvJXGpg="\ "-----END RSA PRIVATE KEY-----"\ ) privRSAKey_str_newLines = str( "-----BEGIN RSA PRIVATE KEY-----\n"\ "MIICXAIBAAKBgQCnBW08FYymHDwA+Vug5QWH2g0nX2EnTnzdyvaZ/mE1pCTxV+Fp\n"\ "j0glrRIoPJPP+rZTcl/cqm7FSD+n2QDWHrg4h8xFPC7uPyfrbd/u6hTO3edu0los\n"\ "tKkq93ZiM/kmfHIS57/nOiG9ETySx4TP4ca6dhNoIAU5uMQDHjhgSXSU4wIDAQAB\n"\ "AoGAOB2PpOdMmSbVVjJxga5Q3GL7lmXqW214cIBXuEeKW55ptxiiqHe2csoiVph7\n"\ "xR3kEkdUQ+yTSP9MO9Wh/U7W78RTKM21tRn2uwzVD4p0whVK/WCa0zsSu41VQ23l\n"\ "wxN3Byrxw6jTTKD3gSLJc/4kGaduXgc/1IHCtmVaD9L2XJkCQQDVjqaDuQhPqzGI\n"\ "kHZ77PARFLf3q+nVIFSIf1m/wxLQEj1HZ9PuyHNm0USQYswwDnh9g7F25YylWex+\n"\ "yiefS0/fAkEAyDcekKtYudtgOhyN7tgSlUiHEyLCRo5IeazKQ0wNCDWfok9HYpEo\n"\ "mOuE+NIQEcCJu+sRXK6rykJQGkHgYsALfQJAN5aJK3Jngm1aWGTaIonbN2cAN/zM\n"\ "wghHWLxlfS/m3rhQsRyKovYUa+f/A+JjqgKqRGmaMQuxX30XvS0bwTAWWwJAQl3j\n"\ "B9mEg7cwYpLsiWueXVW5UKKI+5JWe97G/R/MghgkXk0hQI8VgfswDLq1EO1duqjl\n"\ "DG/qChWJL+r+Uj2OkQJBAK22WDZnIa52dm6G2dC+pM7TC10p7pwOS+G4YsA92Jd2\n"\ "rBjtgPGNR6tCjWMh0+2AUF5lTbXAPqECeV6MIvJXGpg=\n"\ "-----END RSA PRIVATE KEY-----\n"\ ) @unittest.skipIf(cryptomath.m2cryptoLoaded, "requires no M2Crypto") def test_with_missing_m2crypto(self): with self.assertRaises(ValueError): key = parsePEMKey(self.privKey_str, private=True, implementations=["openssl"]) @unittest.skipUnless(cryptomath.m2cryptoLoaded, "requires M2Crypto") def test_key_parse_using_openssl(self): # XXX doesn't handle files without newlines with self.assertRaises(SyntaxError): key = parsePEMKey(self.privKey_str, private=True, implementations=["openssl"]) #self.assertIsInstance(key, RSAKey) #self.assertEqual(1024, len(key)) #self.assertTrue(key.hasPrivateKey()) @unittest.skipUnless(cryptomath.m2cryptoLoaded, "requires M2Crypto") def test_key_parse_with_new_lines_using_openssl(self): key = parsePEMKey(self.privKey_str_newLines, private=True, implementations=["openssl"]) self.assertIsInstance(key, RSAKey) self.assertEqual(1024, len(key)) self.assertTrue(key.hasPrivateKey()) @unittest.skipUnless(cryptomath.m2cryptoLoaded, "requires M2Crypto") def test_rsa_key_parse_using_openssl(self): # XXX doesn't handle files without newlines with self.assertRaises(SyntaxError): key = parsePEMKey(self.privRSAKey_str, private=True, implementations=["openssl"]) #self.assertIsInstance(key, RSAKey) #self.assertEqual(1024, len(key)) #self.assertTrue(key.hasPrivateKey()) @unittest.skipUnless(cryptomath.m2cryptoLoaded, "requires M2Crypto") def test_rsa_key_parse_with_new_lines_using_openssl(self): key = parsePEMKey(self.privRSAKey_str_newLines, private=True, implementations=["openssl"]) self.assertIsInstance(key, RSAKey) self.assertEqual(1024, len(key)) self.assertTrue(key.hasPrivateKey()) def test_key_parse_using_python(self): key = parsePEMKey(self.privKey_str, private=True, implementations=["python"]) self.assertIsInstance(key, RSAKey) self.assertEqual(1024, len(key)) self.assertTrue(key.hasPrivateKey()) def test_key_parse_with_new_lines_using_python(self): key = parsePEMKey(self.privKey_str_newLines, private=True, implementations=["python"]) self.assertIsInstance(key, RSAKey) self.assertEqual(1024, len(key)) self.assertTrue(key.hasPrivateKey()) def test_rsa_key_parse_using_python(self): key = parsePEMKey(self.privRSAKey_str, private=True, implementations=["python"]) self.assertIsInstance(key, RSAKey) self.assertEqual(1024, len(key)) self.assertTrue(key.hasPrivateKey()) def test_rsa_key_parse_with_new_lines_using_python(self): key = parsePEMKey(self.privRSAKey_str_newLines, private=True, implementations=["python"]) self.assertIsInstance(key, RSAKey) self.assertEqual(1024, len(key)) self.assertTrue(key.hasPrivateKey()) tlslite-ng-0.5.1/unit_tests/test_tlslite_utils_rsakey.py000066400000000000000000000110501261665411200236700ustar00rootroot00000000000000# Copyright (c) 2015, Hubert Kario # # See the LICENSE file for legal information regarding use of this file. # compatibility with Python 2.6, for that we need unittest2 package, # which is not available on 3.3 or 3.4 try: import unittest2 as unittest except ImportError: import unittest from tlslite.utils.rsakey import RSAKey from tlslite.utils.python_rsakey import Python_RSAKey # because RSAKey is an abstract class... class TestRSAKey(unittest.TestCase): # random RSA parameters N = int("101394340507163232476731540998223559348384567842249950630680016" "729829651735259973644737329194901739140557378171784099933376993" "53519793819698299093375577631") e = 65537 d = int("141745721972918790698280063566067268498148845185400775263435953" "111621933337897734637889622802200979017278309730638712431978569" "771023240787627463565420833") p = int("903614668974112441151570413608036278756730123846327797584414732" "71561046135679") q = int("112209710608480690748363491355148749700390327497055102381924341" "581861552321889") dP = int("37883511062045429960298073888481933556799848761465588242411735" "654811958185817") dQ = int("62620473256245674709410658602365234471246407950887183034263101" "286525236349249") qInv = int("479278327226690415958629934820002183615697717603796111150941" "44623120451328875") def test___init__(self): rsa = Python_RSAKey() self.assertIsNotNone(rsa) def test___init___with_values(self): rsa = Python_RSAKey(self.N, self.e, self.d, self.p, self.q, self.dP, self.dQ, self.qInv) self.assertIsNotNone(rsa) def test_hashAndSign(self): rsa = Python_RSAKey(self.N, self.e, self.d, self.p, self.q, self.dP, self.dQ, self.qInv) sigBytes = rsa.hashAndSign(bytearray(b'text to sign')) self.assertEqual(bytearray( b'K\x7f\xf2\xca\x81\xf0A1\x95\xb1\x19\xe3\xd7QTL*Q|\xb6\x04' + b'\xbdG\x88H\x12\xc3\xe2\xb3\x97\xd2\xcd\xd8\xe8^Zn^\x8f\x1a' + b'\xae\x9a\x0b)\xb5K\xe8\x98|R\xac\xdc\xdc\n\x7f\x8b\xe7\xe6' + b'HQ\xc3hS\x19'), sigBytes) def test_hashAndVerify(self): rsa = Python_RSAKey(self.N, self.e) sigBytes = bytearray( b'K\x7f\xf2\xca\x81\xf0A1\x95\xb1\x19\xe3\xd7QTL*Q|\xb6\x04' + b'\xbdG\x88H\x12\xc3\xe2\xb3\x97\xd2\xcd\xd8\xe8^Zn^\x8f\x1a' + b'\xae\x9a\x0b)\xb5K\xe8\x98|R\xac\xdc\xdc\n\x7f\x8b\xe7\xe6' + b'HQ\xc3hS\x19') self.assertTrue(rsa.hashAndVerify(sigBytes, bytearray(b'text to sign'))) def test_hashAndVerify_without_NULL_encoding_of_SHA1(self): rsa = Python_RSAKey(self.N, self.e) sigBytes = bytearray( b'F\xe7\x8a>\x8a<;Cj\xdd\xea\x7f\x9d\x0c\xfd\xa7r\xd8\xa1O' + b'\xe1\xf5\x174\x0bR\xad:+\xc9C\x06\xf4\x88n\tp\x14FJ=\xfa' + b'\x8b\xefc\xe2\xdf\x00e\xc1\x1e\xe8\xd2\x97@\x8a\x96\xe2' + b'\x039Y_\x9c\xc9') self.assertTrue(rsa.hashAndVerify(sigBytes, bytearray(b'text to sign'))) def test_hashAndVerify_with_invalid_signature(self): rsa = Python_RSAKey(self.N, self.e) sigBytes = bytearray(64) self.assertFalse(rsa.hashAndVerify(sigBytes, bytearray(b'text to sign'))) def test_hashAndVerify_with_slightly_wrong_signature(self): rsa = Python_RSAKey(self.N, self.e) sigBytes = bytearray( b'K\x7f\xf2\xca\x81\xf0A1\x95\xb1\x19\xe3\xd7QTL*Q|\xb6\x04' + b'\xbdG\x88H\x12\xc3\xe2\xb3\x97\xd2\xcd\xd8\xe8^Zn^\x8f\x1a' + b'\xae\x9a\x0b)\xb5K\xe8\x98|R\xac\xdc\xdc\n\x7f\x8b\xe7\xe6' + b'HQ\xc3hS\x19') sigBytes[0] = 255 self.assertFalse(rsa.hashAndVerify(sigBytes, bytearray(b'text to sign'))) def test_addPKCS1SHA1Prefix(self): data = bytearray(b' sha-1 hash of data ') self.assertEqual(RSAKey.addPKCS1SHA1Prefix(data), bytearray( b'0!0\t\x06\x05+\x0e\x03\x02\x1a\x05\x00\x04\x14' + b' sha-1 hash of data ')) def test_addPKCS1SHA1Prefix_without_NULL(self): data = bytearray(b' sha-1 hash of data ') self.assertEqual(RSAKey.addPKCS1SHA1Prefix(data, False), bytearray( b'0\x1f0\x07\x06\x05+\x0e\x03\x02\x1a\x04\x14' + b' sha-1 hash of data '))