├── .gitignore ├── LICENSE-TLSLITE.txt ├── LICENSE.txt ├── README.md ├── alpnprotocolinfo.py ├── ciphersuiteinfo.py ├── issue_templates.py ├── reporting.py ├── sslxray.py ├── sslxray_ciphers.py ├── sslxray_consts.py └── tlslite ├── __init__.py ├── api.py ├── basedb.py ├── bufferedsocket.py ├── checker.py ├── constants.py ├── defragmenter.py ├── dh.py ├── errors.py ├── extensions.py ├── handshakehashes.py ├── handshakehelpers.py ├── handshakesettings.py ├── integration ├── __init__.py ├── asyncstatemachine.py ├── clienthelper.py ├── httptlsconnection.py ├── imap4_tls.py ├── pop3_tls.py ├── smtp_tls.py ├── tlsasyncdispatchermixin.py ├── tlssocketservermixin.py ├── xmlrpcserver.py └── xmlrpctransport.py ├── keyexchange.py ├── mathtls.py ├── messages.py ├── messagesocket.py ├── recordlayer.py ├── session.py ├── sessioncache.py ├── tlsconnection.py ├── tlsrecordlayer.py ├── utils ├── __init__.py ├── aes.py ├── aesgcm.py ├── asn1parser.py ├── chacha.py ├── chacha20_poly1305.py ├── cipherfactory.py ├── codec.py ├── compat.py ├── constanttime.py ├── cryptomath.py ├── datefuncs.py ├── dns_utils.py ├── ecc.py ├── keyfactory.py ├── lists.py ├── openssl_aes.py ├── openssl_rc4.py ├── openssl_rsakey.py ├── openssl_tripledes.py ├── pem.py ├── poly1305.py ├── pycrypto_aes.py ├── pycrypto_aesgcm.py ├── pycrypto_rc4.py ├── pycrypto_rsakey.py ├── pycrypto_tripledes.py ├── python_aes.py ├── python_aesgcm.py ├── python_chacha20_poly1305.py ├── python_rc4.py ├── python_rsakey.py ├── rc4.py ├── rijndael.py ├── rsakey.py ├── tackwrapper.py ├── tlshashlib.py ├── tripledes.py └── x25519.py ├── verifierdb.py ├── x509.py └── x509certchain.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | tlslite-ng/ 3 | tlslite-old/ 4 | tlslite-ng/* 5 | tlslite-old/* 6 | .idea/ 7 | .idea/* -------------------------------------------------------------------------------- /LICENSE-TLSLITE.txt: -------------------------------------------------------------------------------- 1 | TLS Lite includes code from different sources. All code is either dedicated to 2 | the public domain by its authors, or available under a BSD-style license. In 3 | particular: 4 | 5 | - 6 | 7 | Code written by Trevor Perrin, Kees Bos, Sam Rushing, Dimitris Moraitis, 8 | Marcelo Fernandez, Martin von Loewis, Dave Baggett, Yngve Pettersen, and 9 | Mirko Dziadzka is available under the following terms: 10 | 11 | This is free and unencumbered software released into the public domain. 12 | 13 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute 14 | this software, either in source code form or as a compiled binary, for any 15 | purpose, commercial or non-commercial, and by any means. 16 | 17 | In jurisdictions that recognize copyright laws, the author or authors of this 18 | software dedicate any and all copyright interest in the software to the public 19 | domain. We make this dedication for the benefit of the public at large and to 20 | the detriment of our heirs and successors. We intend this dedication to be an 21 | overt act of relinquishment in perpetuity of all present and future rights to 22 | this software under copyright law. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | - 32 | 33 | Code written by Bram Cohen (rijndael.py) was dedicated to the public domain by 34 | its author. See rijndael.py for details. 35 | 36 | - 37 | 38 | Code written by Google is available under the following terms: 39 | 40 | Copyright (c) 2008, The Chromium Authors 41 | All rights reserved. 42 | 43 | Redistribution and use in source and binary forms, with or without 44 | modification, are permitted provided that the following conditions are met: 45 | 46 | * Redistributions of source code must retain the above copyright notice, this 47 | list of conditions and the following disclaimer. 48 | 49 | * Redistributions in binary form must reproduce the above copyright notice, 50 | this list of conditions and the following disclaimer in the documentation 51 | and/or other materials provided with the distribution. 52 | 53 | * Neither the name of the Google Inc. nor the names of its contributors may 54 | be used to endorse or promote products derived from this software without 55 | specific prior written permission. 56 | 57 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 58 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 59 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 60 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 61 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 62 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 63 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 64 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 65 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 66 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 67 | 68 | - 69 | 70 | Code written by Hubert Kario is available under the following terms: 71 | 72 | Copyright (c) 2014, Hubert Kario, Red Hat Inc. 73 | All rights reserved. 74 | 75 | Redistribution and use in source and binary forms, with or without modification, 76 | are permitted provided that the following conditions are met: 77 | 78 | 1. Redistributions of source code must retain the above copyright notice, this 79 | list of conditions and the following disclaimer. 80 | 81 | 2. Redistributions in binary form must reproduce the above copyright notice, 82 | this list of conditions and the following disclaimer in the documentation 83 | and/or other materials provided with the distribution. 84 | 85 | 3. Neither the name of the copyright holder nor the names of its contributors 86 | may be used to endorse or promote products derived from this software without 87 | specific prior written permission. 88 | 89 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 90 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 91 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 92 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 93 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 94 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 95 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 96 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 97 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 98 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | sslxray is an SSL/TLS analysis tool designed to identify errors in the implementation and configuration of SSL/TLS. It is currently being developed by [Graham Sutherland](https://github.com/gsuberland). 4 | 5 | Written in Python, heavily utilising a modified version of tlslite - https://pypi.python.org/pypi/tlslite 6 | 7 | The long-term development plan is to build a local script which does everything that the Qualys SSL Labs scanner does, and a bit more. 8 | 9 | ## INSTALLATION 10 | 11 | Prerequisites: 12 | 13 | * The included version of tlslite has been heavily modified. Do not try to use the original library! 14 | * Python 2.7.15 or later, or an up-to-date version of Python 3.x (last tested with Python 3.6.x) 15 | * M2Crypto python library (`pip install m2crypto`) 16 | * pycrypto python library (`pip install pycrypto`) 17 | * ecdsa python library (`pip install ecdsa`) 18 | * hexdump python library (`pip install hexdump`) when running in debug mode 19 | * GMP and GMPY are optional but recommended for speed (`apt-get install python-gmpy`). 20 | 21 | Note that some of these require an up-to-date version of pip and setuptools which can be installed via: 22 | * `python -m pip install --upgrade pip` 23 | * `python -m pip install --upgrade setuptools` 24 | 25 | **Linux support:** 26 | 27 | You may need the following prerequisites to install M2Crypto properly on Linux: 28 | 29 | * libssl-dev 30 | * python-dev 31 | 32 | **Windows support:** 33 | 34 | * M2Crypto has a Windows package (m2cryptowin32 / m2cryptowin64) which must match the bitness of your Python install (usually 32-bit). 35 | * If you get an error about --single-version-externally-managed when installing M2Crypto, run `pip install -U wheel` first. The library documentation recommends `pip install --egg m2cryptowin32` but this is no longer supported. 36 | * pycrypto's installer requires Microsoft Visual C++ Compiler for Python, available here: http://aka.ms/vcpython27 37 | 38 | ## CURRENT FEATURES 39 | 40 | Version 0.1.6 supports the following features: 41 | 42 | * Manual SNI selection 43 | * SSL/TLS cipher suite enumeration, including TLSv1.3 draft support 44 | * Per-bit MAC validation checks 45 | * ServerRandom randomness checks 46 | * Elliptic curve support detection 47 | * ALPN and NPN feature detection 48 | 49 | ## KNOWN BUGS/ISSUES 50 | 51 | * SNI is set to empty by default. On some servers (e.g. CloudFlare stuff) this results in no ciphers being supported. Use `-S ` to fix this. 52 | * SSLv2.0 support is currently tentative, and only works on servers which accept a SSLv2.0 version field on SSLv3/TLS packet format. 53 | * Randomness tests on ServerRandom are rudimentary and a bit slow (although better as of 0.1.4) 54 | * Certain flags haven't been fully tested with each other yet and might have weird consequences. 55 | * Almost no handling of server timeouts at the moment. 56 | 57 | ## IMMEDIATE DEVELOPMENT 58 | 59 | The following features are immediately planned: 60 | 61 | * Migration to tlslite-ng 62 | * Reporting and logging/output engine 63 | * Proper SSLv2 packet support 64 | * Reporting when elliptic curve support varies between requested suites 65 | * SCSV extension support detection 66 | * Padding bit validation checks 67 | * Full suite enumeration (0x0000 to 0xFFFF) 68 | * X.509 parsing and validation checks (e.g. expiry, RSA key size, CA chain) 69 | * Broken/bad/known certificate checks (e.g. Debian OpenSSL stack contents bug) 70 | * Common DH prime checks 71 | * Weak / sketchy DH group detection 72 | * Improved ServerRandom tests: epoch prefix detection, bit position probabilities, additional compression algorithms 73 | 74 | ## FUTURE DEVELOPMENT 75 | 76 | The following features will be implemented in future, pending feasibility: 77 | 78 | * Superfluous chain checking on certs 79 | * Support for DSA certificates (should come with tlslite-ng migration) 80 | * Ability to save all traffic sent/recv to a pcap(ng) 81 | * PCI mode (checking compliance with PCI guidelines) 82 | * Client certificate support 83 | * Various features from Qualys SSL Scan 84 | * TLS extension fuzzer 85 | 86 | ## CONTRIBUTION GUIDELINES 87 | 88 | Pull requests and bug reports welcome. 89 | 90 | If you want to push code to sslxray, please adhere to the following guidelines: 91 | 92 | General: 93 | 94 | * Please make sure your check actually works! (and not just against Google and Amazon) 95 | * I use PyCharm, I suggest you do too. 96 | * If you're adding new files, please do so via PyCharm IDE, or let me know so I can properly update the project file. 97 | * Per change, update the version number by 0.0.1, and wrap such that 0.1.9 + 0.0.1 becomes 0.2.0 98 | * Update the changelog below 99 | * If you push a commit, write a sensible and informative commit message. 100 | 101 | Style: 102 | 103 | * Comment your code. Every function and class needs a documenting comment, including rtype and return comments where applicable. See existing code for style. 104 | * An indent is four ASCII spaces (0x20 * 4), not a tab character or any other delimiter. 105 | * Functions are separated by two newlines. 106 | * Use single line breaks in functions to break up logical sections and operations. 107 | * Double-quotes for strings, unless they're for inline dictionary keys, in which case use single quotes, i.e. foo['bar'] 108 | * If you think you need a global constant, you probably need a new entry for argparse instead, so that the user can customise it. 109 | * All constants, global or local, should use CAPITAL_LETTERS for styling. 110 | * Use Python 3.x style printing. The print function has been imported from __future__ so make sure to use print("foo") with parentheses, not spaces. 111 | 112 | If you're not sure, check existing style for precedent. 113 | 114 | ## CHANGELOG 115 | 116 | Version 0.1.6: 117 | * Updated the tlslite library to a newer version 118 | * Improved elliptic curve security checks 119 | * Fixed a bug that caused a crash during testing of MAC bits 120 | * Support for Python 2 and Python 3 121 | 122 | Version 0.1.5: 123 | * Project renamed from original codename to 'sslxray' 124 | * Added NPN support check. 125 | * Added ALPN support check. 126 | * Improved ServerRandom randomness checks. 127 | 128 | Version 0.1.4: 129 | * Added -S flag for specifying SNI hostname (contrib from [Mark Lowe](https://github.com/pentestmonkey/)) 130 | * Fixed bug where SSLv2.0 header would be sent for non-SSLv2 headers. 131 | * Improved ServerRandom randomness checks. 132 | * Added enumeration of elliptic curve support on all cipher suites which use ECDH/ECDHE. 133 | 134 | Version 0.1.3: 135 | * First internally published version. Rough cut but probably useful. 136 | * Added ServerRandom randomness checks. 137 | * Added ability to disable checks on certain protocols. 138 | 139 | Version 0.1.2: 140 | * Improved error handling. 141 | * Added --list-suites and --list-protocols switches. 142 | 143 | Version 0.1.1: 144 | * Added support for MAC fuzzing. 145 | * Fixed oversight where weird suites (e.g. PCT stuff) with ID > 0xFFFF being scanned for. 146 | 147 | Version 0.1.0: 148 | 149 | * Initial functioning alpha. 150 | -------------------------------------------------------------------------------- /alpnprotocolinfo.py: -------------------------------------------------------------------------------- 1 | 2 | class ALPNProtocolInfo: 3 | """ 4 | ALPN Protocol Info class. Stores information about each ALPN protocol. 5 | """ 6 | Name = "" 7 | Outdated = False 8 | 9 | Issues = [] 10 | 11 | def __init__(self, name="", outdated=False): 12 | self.Name = name 13 | self.Outdated = outdated 14 | -------------------------------------------------------------------------------- /ciphersuiteinfo.py: -------------------------------------------------------------------------------- 1 | 2 | class CipherSuiteInfo: 3 | """ 4 | Cipher suite info class. Stores information about each cipher suite, such as its identifier, name, and minimum protocol version. 5 | """ 6 | ID = 0 7 | Name = "" 8 | Protocol = None 9 | KeyExchange = None 10 | Authenticity = None 11 | Encryption = None 12 | Bits = 0 13 | MAC = None 14 | 15 | Issues = [] 16 | 17 | def __init__(self, name="", id=0, protocol=None, keyExchange=None, authenticity=None, encryption=None, bits=0, mac=None): 18 | self.Name = name 19 | self.ID = id 20 | self.Protocol = protocol 21 | self.KeyExchange = keyExchange 22 | self.Authenticity = authenticity 23 | self.Encryption = encryption 24 | self.Bits = bits 25 | self.MAC = mac 26 | -------------------------------------------------------------------------------- /issue_templates.py: -------------------------------------------------------------------------------- 1 | from reporting import * 2 | 3 | IssueTemplates = [ 4 | Issue("CIPHERS_SUPPORTED", STCA_REPORT_TYPE_INFO, "The following cipher suites were found to be enabled:"), 5 | Issue("WEAK_CIPHER_KEY_SIZE", STCA_REPORT_TYPE_ISSUE, "Cipher suites with weak key sizes (i.e. less than 128-bit) were found to be enabled."), 6 | Issue("SSL2_SUPPORTED", STCA_REPORT_TYPE_ISSUE, "The SSL version 2 protocol was found to be accepted by the server."), 7 | Issue("SSL3_SUPPORTED", STCA_REPORT_TYPE_ISSUE, "The SSL version 3 protocol was found to be accepted by the server."), 8 | Issue("SERVER_RANDOM_DUPLICATES", STCA_REPORT_TYPE_ISSUE, "The SSL/TLS implementation on the server was found to send duplicate ServerRandom values between connections."), 9 | Issue("MAC_VALIDATION_ERROR", STCA_REPORT_TYPE_ISSUE, "The server did not properly validate the following bits:"), 10 | ] 11 | 12 | # check that there aren't any issues with duplicate template IDs 13 | duperefs = set([t.ref for t in IssueTemplates if IssueTemplates.count(t.ref) > 1]) 14 | if len(duperefs) > 0: 15 | raise RuntimeError("Duplicate index in IssueTemplates (" + duperefs[0] + ")") 16 | 17 | def getIssueTemplate(ref): 18 | issues = [t for t in IssueTemplates if t.ref == ref] 19 | assert len(issues) <= 1 20 | if len(issues) < 1: 21 | return None 22 | return issues[0] 23 | -------------------------------------------------------------------------------- /reporting.py: -------------------------------------------------------------------------------- 1 | 2 | class Report: 3 | def __init__(self): 4 | self.ref = "" 5 | self.date = None 6 | self.targethost = "" 7 | self.targetport = 0 8 | self.issues = [] 9 | 10 | def __len__(self): 11 | return len(self.issues) 12 | 13 | def addIssue(self, issue): 14 | self.issues.append(issue) 15 | 16 | def findByRef(self, ref): 17 | return [i for i in self.issues if i.ref == ref] 18 | 19 | 20 | STCA_REPORT_TYPE_ISSUE = 1 21 | STCA_REPORT_TYPE_INFO = 2 22 | 23 | class Issue: 24 | def __init__(self, ref, type, message): 25 | self.ref = ref 26 | self.type = type 27 | self.message = message 28 | self.findings = "" 29 | self.findingTemplate = None 30 | 31 | def __str__(self): 32 | return self.message + "\r\n" + self.findings 33 | -------------------------------------------------------------------------------- /sslxray_consts.py: -------------------------------------------------------------------------------- 1 | 2 | class ProtocolType: 3 | """ 4 | Protocol type (e.g. SSLv2.0, TLS), used to identify which packet format should be used for a suite. 5 | """ 6 | SSL = 1, 7 | SSL2 = 2, 8 | TLS = 3, 9 | PCT1_CERT_X509 = 100, 10 | PCT1_CERT_X509_CHAIN = 101, 11 | PCT1_HASH_MD5 = 102, 12 | PCT1_HASH_SHA = 103, 13 | PCT1_EXCH_RSA_PKCS1 = 104, 14 | PCT1_CIPHER_RC4 = 105, 15 | PCT1_ENC_BITS_40 = 106, 16 | PCT1_ENC_BITS_128 = 107, 17 | PCT_VERSION_1 = 108 18 | 19 | class KeyExchangeType: 20 | """ 21 | Key exchange type (e.g. RSA, DH), used to provide grouping in cipher suite definitions, to make issue mapping easier. 22 | """ 23 | NULL = 0 24 | PSK = 1 25 | RSA = 2 26 | RSA_EXPORT = 3 27 | RSA_EXPORT_1024 = 4 28 | RSA_FIPS = 5 29 | DH = 6 30 | DHE = 7 31 | ECDH = 8 32 | ECDHE = 9 33 | FORTEZZA = 10 34 | KRB5 = 11 35 | KRB5_EXPORT = 12 36 | SRP = 13 37 | VKO_GOST_R_34_10_94 = 14 38 | VKO_GOST_R_34_10_2001 = 15 39 | PCT = 100 40 | PCT1_MAC_BITS_128 = 101 41 | 42 | class AuthenticityType: 43 | """ 44 | Authenticity type (e.g. RSA, POLY1305), used to provide grouping in cipher suite definitions, to make issue mapping easier. 45 | """ 46 | NULL = 0, 47 | RSA = 1, 48 | RSA_EXPORT = 2, 49 | RSA_EXPORT_1024 = 3, 50 | RSA_FIPS = 4, 51 | DSS = 5, 52 | ECDSA = 6, 53 | Anon = 7, 54 | KRB5 = 8, 55 | KRB5_EXPORT = 9, 56 | PSK = 10, 57 | SHA = 11, 58 | VKO_GOST_R_34_10_94 = 12 59 | VKO_GOST_R_34_10_2001 = 13 60 | KEA = 14 61 | POLY1305 = 15 62 | PCT = 100 63 | 64 | class EncryptionType: 65 | """ 66 | Encryption type (e.g. AES128-CBC, RC4), used to provide grouping in cipher suite definitions, to make issue mapping easier. 67 | """ 68 | NULL = 0 69 | AES_128_CBC = 10 70 | AES_128_GCM = 11 71 | AES_256_CBC = 12 72 | AES_256_GCM = 13 73 | RC4_128 = 20 74 | RC4_128_EXPORT40 = 21 75 | RC4_40 = 22 76 | RC4_56 = 23 77 | RC4_64 = 24 78 | CAMELLIA_128_CBC = 30 79 | CAMELLIA_256_CBC = 31 80 | FORTEZZA_CBC = 40 81 | GOST28147 = 50 82 | IDEA_128_CBC = 60 83 | IDEA_CBC = 61 84 | RC2_CBC_128_CBC = 70 85 | RC2_CBC_40 = 71 86 | RC2_CBC_56 = 72 87 | SEED_CBC = 80 88 | TDES_EDE_CBC = 91 89 | DES_192_EDE3_CBC = 92 90 | DES_64_CBC = 93 91 | DES_CBC = 94 92 | DES_CBC_40 = 95 93 | DES40_CBC = 96 94 | CHACHA20 = 100 95 | 96 | class MACType: 97 | """ 98 | Record MAC type (e.g. MD5, SHA256), used to provide grouping in cipher suite definitions, to make issue mapping easier. 99 | """ 100 | NULL = 0, 101 | MD5 = 1, 102 | SHA = 2, 103 | SHA256 = 3, 104 | SHA384 = 4, 105 | SHA512 = 5, 106 | GOST28147 = 6, 107 | GOSTR3411 = 7, 108 | RIPEMD160 = 8 109 | -------------------------------------------------------------------------------- /tlslite/__init__.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """TLS Lite is a free python library that implements SSL and TLS. TLS Lite 5 | supports RSA and SRP ciphersuites. TLS Lite is pure python, however it can use 6 | other libraries for faster crypto operations. TLS Lite integrates with several 7 | stdlib neworking libraries. 8 | 9 | API documentation is available in the 'docs' directory. 10 | 11 | If you have questions or feedback, feel free to contact me. 12 | 13 | To use, do:: 14 | 15 | from tlslite import TLSConnection, ... 16 | 17 | If you want to import the most useful objects, the cleanest way is:: 18 | 19 | from tlslite.api import * 20 | 21 | Then use the L{tlslite.TLSConnection.TLSConnection} class with a socket. 22 | (Or, use one of the integration classes in L{tlslite.integration}). 23 | 24 | @version: 0.8.0-alpha1 25 | """ 26 | 27 | from tlslite.api import * 28 | from tlslite.api import __version__ # Unsure why this is needed, but it is 29 | 30 | print("Custom TLSLite") -------------------------------------------------------------------------------- /tlslite/api.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | __version__ = "0.8.0-alpha1" 5 | from .constants import AlertLevel, AlertDescription, Fault 6 | from .errors import * 7 | from .checker import Checker 8 | from .handshakesettings import HandshakeSettings 9 | from .session import Session 10 | from .sessioncache import SessionCache 11 | from .tlsconnection import TLSConnection 12 | from .verifierdb import VerifierDB 13 | from .x509 import X509 14 | from .x509certchain import X509CertChain 15 | 16 | from .integration.httptlsconnection import HTTPTLSConnection 17 | from .integration.tlssocketservermixin import TLSSocketServerMixIn 18 | from .integration.tlsasyncdispatchermixin import TLSAsyncDispatcherMixIn 19 | from .integration.pop3_tls import POP3_TLS 20 | from .integration.imap4_tls import IMAP4_TLS 21 | from .integration.smtp_tls import SMTP_TLS 22 | from .integration.xmlrpctransport import XMLRPCTransport 23 | from .integration.xmlrpcserver import TLSXMLRPCRequestHandler, \ 24 | TLSXMLRPCServer, \ 25 | MultiPathTLSXMLRPCServer 26 | 27 | from .utils.cryptomath import m2cryptoLoaded, gmpyLoaded, \ 28 | pycryptoLoaded, prngName 29 | from .utils.keyfactory import generateRSAKey, parsePEMKey, \ 30 | parseAsPublicKey, parsePrivateKey 31 | from .utils.tackwrapper import tackpyLoaded 32 | from .dh import parse as parseDH 33 | -------------------------------------------------------------------------------- /tlslite/basedb.py: -------------------------------------------------------------------------------- 1 | # Authors: 2 | # Trevor Perrin 3 | # Martin von Loewis - python 3 port 4 | # 5 | # See the LICENSE file for legal information regarding use of this file. 6 | 7 | """Base class for SharedKeyDB and VerifierDB.""" 8 | 9 | try: 10 | import anydbm 11 | except ImportError: 12 | # Python 3 13 | import dbm as anydbm 14 | import threading 15 | 16 | class BaseDB(object): 17 | def __init__(self, filename, type): 18 | self.type = type 19 | self.filename = filename 20 | if self.filename: 21 | self.db = None 22 | else: 23 | self.db = {} 24 | self.lock = threading.Lock() 25 | 26 | def create(self): 27 | """ 28 | Create a new on-disk database. 29 | 30 | :raises anydbm.error: If there's a problem creating the database. 31 | """ 32 | if self.filename: 33 | self.db = anydbm.open(self.filename, "n") #raises anydbm.error 34 | self.db["--Reserved--type"] = self.type 35 | self.db.sync() 36 | else: 37 | self.db = {} 38 | 39 | def open(self): 40 | """ 41 | Open a pre-existing on-disk database. 42 | 43 | :raises anydbm.error: If there's a problem opening the database. 44 | :raises ValueError: If the database is not of the right type. 45 | """ 46 | if not self.filename: 47 | raise ValueError("Can only open on-disk databases") 48 | self.db = anydbm.open(self.filename, "w") #raises anydbm.error 49 | try: 50 | if self.db["--Reserved--type"] != self.type: 51 | raise ValueError("Not a %s database" % self.type) 52 | except KeyError: 53 | raise ValueError("Not a recognized database") 54 | 55 | def __getitem__(self, username): 56 | if self.db == None: 57 | raise AssertionError("DB not open") 58 | 59 | self.lock.acquire() 60 | try: 61 | valueStr = self.db[username] 62 | finally: 63 | self.lock.release() 64 | 65 | return self._getItem(username, valueStr) 66 | 67 | def __setitem__(self, username, value): 68 | if self.db == None: 69 | raise AssertionError("DB not open") 70 | 71 | valueStr = self._setItem(username, value) 72 | 73 | self.lock.acquire() 74 | try: 75 | self.db[username] = valueStr 76 | if self.filename: 77 | self.db.sync() 78 | finally: 79 | self.lock.release() 80 | 81 | def __delitem__(self, username): 82 | if self.db == None: 83 | raise AssertionError("DB not open") 84 | 85 | self.lock.acquire() 86 | try: 87 | del(self.db[username]) 88 | if self.filename: 89 | self.db.sync() 90 | finally: 91 | self.lock.release() 92 | 93 | def __contains__(self, username): 94 | """ 95 | Check if the database contains the specified username. 96 | 97 | :param str username: The username to check for. 98 | 99 | :rtype: bool 100 | :returns: True if the database contains the username, False 101 | otherwise. 102 | """ 103 | if self.db == None: 104 | raise AssertionError("DB not open") 105 | 106 | self.lock.acquire() 107 | try: 108 | return username in self.db 109 | finally: 110 | self.lock.release() 111 | 112 | def check(self, username, param): 113 | value = self.__getitem__(username) 114 | return self._checkItem(value, username, param) 115 | 116 | def keys(self): 117 | """ 118 | Return a list of usernames in the database. 119 | 120 | :rtype: list 121 | :returns: The usernames in the database. 122 | """ 123 | if self.db == None: 124 | raise AssertionError("DB not open") 125 | 126 | self.lock.acquire() 127 | try: 128 | usernames = self.db.keys() 129 | finally: 130 | self.lock.release() 131 | usernames = [u for u in usernames if not u.startswith("--Reserved--")] 132 | return usernames 133 | -------------------------------------------------------------------------------- /tlslite/bufferedsocket.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016, Hubert Kario 2 | # 3 | # See the LICENSE file for legal information regarding use of this file. 4 | 5 | """Wrapper around the socket.socket interface that provides buffering""" 6 | 7 | from collections import deque 8 | 9 | 10 | class BufferedSocket(object): 11 | """ 12 | Socket that will buffer reads and writes to a real socket object 13 | 14 | When buffer_writes is enabled, writes won't be passed to the real socket 15 | until flush() is called. 16 | 17 | Not multithread safe. 18 | 19 | :vartype buffer_writes: boolean 20 | :ivar buffer_writes: whether to buffer data writes, False by default 21 | """ 22 | 23 | def __init__(self, socket): 24 | """Associate socket with the object""" 25 | self.socket = socket 26 | self._write_queue = deque() 27 | self.buffer_writes = False 28 | 29 | def send(self, data): 30 | """Send data to the socket""" 31 | if self.buffer_writes: 32 | self._write_queue.append(data) 33 | return len(data) 34 | return self.socket.send(data) 35 | 36 | def sendall(self, data): 37 | """Send data to the socket""" 38 | if self.buffer_writes: 39 | self._write_queue.append(data) 40 | return None 41 | return self.socket.sendall(data) 42 | 43 | def flush(self): 44 | """Send all buffered data""" 45 | buf = bytearray() 46 | for i in self._write_queue: 47 | buf += i 48 | self._write_queue.clear() 49 | if buf: 50 | self.socket.sendall(buf) 51 | 52 | def recv(self, bufsize): 53 | """Receive data from socket (socket emulation)""" 54 | return self.socket.recv(bufsize) 55 | 56 | def getsockname(self): 57 | """Return the socket's own address (socket emulation).""" 58 | return self.socket.getsockname() 59 | 60 | def getpeername(self): 61 | """ 62 | Return the remote address to which the socket is connected 63 | 64 | (socket emulation) 65 | """ 66 | return self.socket.getpeername() 67 | 68 | def settimeout(self, value): 69 | """Set a timeout on blocking socket operations (socket emulation).""" 70 | return self.socket.settimeout(value) 71 | 72 | def gettimeout(self): 73 | """ 74 | Return the timeout associated with socket operations 75 | 76 | (socket emulation) 77 | """ 78 | return self.socket.gettimeout() 79 | 80 | def setsockopt(self, level, optname, value): 81 | """Set the value of the given socket option (socket emulation).""" 82 | return self.socket.setsockopt(level, optname, value) 83 | 84 | def shutdown(self, how): 85 | """Shutdown the underlying socket.""" 86 | self.flush() 87 | return self.socket.shutdown(how) 88 | 89 | def close(self): 90 | """Close the underlying socket.""" 91 | self.flush() 92 | return self.socket.close() 93 | -------------------------------------------------------------------------------- /tlslite/checker.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """Class for post-handshake certificate checking.""" 5 | 6 | from .x509 import X509 7 | from .x509certchain import X509CertChain 8 | from .errors import * 9 | 10 | 11 | class Checker(object): 12 | """ 13 | This class is passed to a handshake function to check the other 14 | party's certificate chain. 15 | 16 | If a handshake function completes successfully, but the Checker 17 | judges the other party's certificate chain to be missing or 18 | inadequate, a subclass of 19 | :py:class:`tlslite.errors.TLSAuthenticationError` 20 | will be raised. 21 | 22 | Currently, the Checker can check an X.509 chain. 23 | """ 24 | 25 | def __init__(self, 26 | x509Fingerprint=None, 27 | checkResumedSession=False): 28 | """ 29 | Create a new Checker instance. 30 | 31 | You must pass in one of these argument combinations: 32 | - x509Fingerprint 33 | 34 | :param str x509Fingerprint: A hex-encoded X.509 end-entity 35 | fingerprint which the other party's end-entity certificate must 36 | match. 37 | 38 | :param bool checkResumedSession: If resumed sessions should be 39 | checked. This defaults to False, on the theory that if the 40 | session was checked once, we don't need to bother 41 | re-checking it. 42 | """ 43 | 44 | self.x509Fingerprint = x509Fingerprint 45 | self.checkResumedSession = checkResumedSession 46 | 47 | def __call__(self, connection): 48 | """Check a TLSConnection. 49 | 50 | When a Checker is passed to a handshake function, this will 51 | be called at the end of the function. 52 | 53 | :param tlslite.tlsconnection.TLSConnection connection: The 54 | TLSConnection to examine. 55 | 56 | :raises tlslite.errors.TLSAuthenticationError: If the other 57 | party's certificate chain is missing or bad. 58 | """ 59 | if not self.checkResumedSession and connection.resumed: 60 | return 61 | 62 | if self.x509Fingerprint: 63 | if connection._client: 64 | chain = connection.session.serverCertChain 65 | else: 66 | chain = connection.session.clientCertChain 67 | 68 | if self.x509Fingerprint: 69 | if isinstance(chain, X509CertChain): 70 | if self.x509Fingerprint: 71 | if chain.getFingerprint() != self.x509Fingerprint: 72 | raise TLSFingerprintError(\ 73 | "X.509 fingerprint mismatch: %s, %s" % \ 74 | (chain.getFingerprint(), self.x509Fingerprint)) 75 | elif chain: 76 | raise TLSAuthenticationTypeError() 77 | else: 78 | raise TLSNoAuthenticationError() 79 | -------------------------------------------------------------------------------- /tlslite/defragmenter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Hubert Kario 2 | # 3 | # See the LICENSE file for legal information regarding use of this file. 4 | 5 | """ Helper package for handling fragmentation of messages """ 6 | 7 | from __future__ import generators 8 | 9 | from .utils.codec import Parser 10 | 11 | class Defragmenter(object): 12 | 13 | """ 14 | Class for demultiplexing TLS messages. 15 | 16 | Since the messages can be interleaved and fragmented between each other 17 | we need to cache not complete ones and return in order of urgency. 18 | 19 | Supports messages with given size (like Alerts) or with a length header 20 | in specific place (like Handshake messages). 21 | 22 | :ivar priorities: order in which messages from given types should be 23 | returned. 24 | :ivar buffers: data buffers for message types 25 | :ivar decoders: functions which check buffers if a message of given type 26 | is complete 27 | """ 28 | 29 | def __init__(self): 30 | """Set up empty defregmenter""" 31 | self.priorities = [] 32 | self.buffers = {} 33 | self.decoders = {} 34 | 35 | def addStaticSize(self, msgType, size): 36 | """Add a message type which all messages are of same length""" 37 | if msgType in self.priorities: 38 | raise ValueError("Message type already defined") 39 | if size < 1: 40 | raise ValueError("Message size must be positive integer") 41 | 42 | self.priorities += [msgType] 43 | 44 | self.buffers[msgType] = bytearray(0) 45 | def sizeHandler(data): 46 | """ 47 | Size of message in parameter 48 | 49 | If complete message is present in parameter returns its size, 50 | None otherwise. 51 | """ 52 | if len(data) < size: 53 | return None 54 | else: 55 | return size 56 | self.decoders[msgType] = sizeHandler 57 | 58 | def addDynamicSize(self, msgType, sizeOffset, sizeOfSize): 59 | """Add a message type which has a dynamic size set in a header""" 60 | if msgType in self.priorities: 61 | raise ValueError("Message type already defined") 62 | if sizeOfSize < 1: 63 | raise ValueError("Size of size must be positive integer") 64 | if sizeOffset < 0: 65 | raise ValueError("Offset can't be negative") 66 | 67 | self.priorities += [msgType] 68 | self.buffers[msgType] = bytearray(0) 69 | 70 | def sizeHandler(data): 71 | """ 72 | Size of message in parameter 73 | 74 | If complete message is present in parameter returns its size, 75 | None otherwise. 76 | """ 77 | if len(data) < sizeOffset+sizeOfSize: 78 | return None 79 | else: 80 | parser = Parser(data) 81 | # skip the header 82 | parser.getFixBytes(sizeOffset) 83 | 84 | payloadLength = parser.get(sizeOfSize) 85 | if parser.getRemainingLength() < payloadLength: 86 | # not enough bytes in buffer 87 | return None 88 | return sizeOffset + sizeOfSize + payloadLength 89 | 90 | self.decoders[msgType] = sizeHandler 91 | 92 | def addData(self, msgType, data): 93 | """Adds data to buffers""" 94 | if msgType not in self.priorities: 95 | raise ValueError("Message type not defined") 96 | 97 | self.buffers[msgType] += data 98 | 99 | def getMessage(self): 100 | """Extract the highest priority complete message from buffer""" 101 | for msgType in self.priorities: 102 | length = self.decoders[msgType](self.buffers[msgType]) 103 | if length is None: 104 | continue 105 | 106 | # extract message 107 | data = self.buffers[msgType][:length] 108 | # remove it from buffer 109 | self.buffers[msgType] = self.buffers[msgType][length:] 110 | return (msgType, data) 111 | return None 112 | 113 | def clearBuffers(self): 114 | """Remove all data from buffers""" 115 | for key in self.buffers.keys(): 116 | self.buffers[key] = bytearray(0) 117 | -------------------------------------------------------------------------------- /tlslite/dh.py: -------------------------------------------------------------------------------- 1 | # Author: 2 | # Hubert Kario 3 | 4 | """Handling of Diffie-Hellman parameter files.""" 5 | 6 | from .utils.asn1parser import ASN1Parser 7 | from .utils.pem import dePem 8 | from .utils.cryptomath import bytesToNumber 9 | 10 | 11 | def parseBinary(data): 12 | """ 13 | Parse DH parameters from ASN.1 DER encoded binary string. 14 | 15 | :param bytes data: DH parameters 16 | :rtype: tuple of int 17 | """ 18 | parser = ASN1Parser(data) 19 | 20 | prime = parser.getChild(0) 21 | gen = parser.getChild(1) 22 | 23 | return (bytesToNumber(gen.value), bytesToNumber(prime.value)) 24 | 25 | 26 | def parse(data): 27 | """ 28 | Parses DH parameters from a binary string. 29 | 30 | The string can either by PEM or DER encoded 31 | 32 | :param bytes data: DH parameters 33 | :rtype: tuple of int 34 | :returns: generator and prime 35 | """ 36 | try: 37 | return parseBinary(data) 38 | except (SyntaxError, TypeError): 39 | pass 40 | 41 | binData = dePem(data, "DH PARAMETERS") 42 | return parseBinary(binData) 43 | -------------------------------------------------------------------------------- /tlslite/errors.py: -------------------------------------------------------------------------------- 1 | # Authors: 2 | # Trevor Perrin 3 | # Dave Baggett (Arcode Corporation) - Added TLSUnsupportedError. 4 | # 5 | # See the LICENSE file for legal information regarding use of this file. 6 | 7 | """Exception classes.""" 8 | import socket 9 | 10 | from .constants import AlertDescription, AlertLevel 11 | 12 | class BaseTLSException(Exception): 13 | """ 14 | Metaclass for TLS Lite exceptions. 15 | 16 | Look to :py:class:`tlslite.errors.TLSError` for exceptions that should be 17 | caught by tlslite 18 | consumers 19 | """ 20 | 21 | pass 22 | 23 | 24 | class EncryptionError(BaseTLSException): 25 | """Base class for exceptions thrown while encrypting.""" 26 | 27 | pass 28 | 29 | 30 | class TLSError(BaseTLSException): 31 | """Base class for all TLS Lite exceptions.""" 32 | 33 | def __str__(self): 34 | """At least print out the Exception time for str(...).""" 35 | return repr(self) 36 | 37 | 38 | class TLSClosedConnectionError(TLSError, socket.error): 39 | """An attempt was made to use the connection after it was closed.""" 40 | 41 | pass 42 | 43 | 44 | class TLSAbruptCloseError(TLSError): 45 | """The socket was closed without a proper TLS shutdown. 46 | 47 | The TLS specification mandates that an alert of some sort 48 | must be sent before the underlying socket is closed. If the socket 49 | is closed without this, it could signify that an attacker is trying 50 | to truncate the connection. It could also signify a misbehaving 51 | TLS implementation, or a random network failure. 52 | """ 53 | 54 | pass 55 | 56 | 57 | class TLSAlert(TLSError): 58 | """A TLS alert has been signalled.""" 59 | 60 | pass 61 | 62 | _descriptionStr = {\ 63 | AlertDescription.close_notify: "close_notify",\ 64 | AlertDescription.unexpected_message: "unexpected_message",\ 65 | AlertDescription.bad_record_mac: "bad_record_mac",\ 66 | AlertDescription.decryption_failed: "decryption_failed",\ 67 | AlertDescription.record_overflow: "record_overflow",\ 68 | AlertDescription.decompression_failure: "decompression_failure",\ 69 | AlertDescription.handshake_failure: "handshake_failure",\ 70 | AlertDescription.no_certificate: "no certificate",\ 71 | AlertDescription.bad_certificate: "bad_certificate",\ 72 | AlertDescription.unsupported_certificate: "unsupported_certificate",\ 73 | AlertDescription.certificate_revoked: "certificate_revoked",\ 74 | AlertDescription.certificate_expired: "certificate_expired",\ 75 | AlertDescription.certificate_unknown: "certificate_unknown",\ 76 | AlertDescription.illegal_parameter: "illegal_parameter",\ 77 | AlertDescription.unknown_ca: "unknown_ca",\ 78 | AlertDescription.access_denied: "access_denied",\ 79 | AlertDescription.decode_error: "decode_error",\ 80 | AlertDescription.decrypt_error: "decrypt_error",\ 81 | AlertDescription.export_restriction: "export_restriction",\ 82 | AlertDescription.protocol_version: "protocol_version",\ 83 | AlertDescription.insufficient_security: "insufficient_security",\ 84 | AlertDescription.internal_error: "internal_error",\ 85 | AlertDescription.inappropriate_fallback: "inappropriate_fallback",\ 86 | AlertDescription.user_canceled: "user_canceled",\ 87 | AlertDescription.no_renegotiation: "no_renegotiation",\ 88 | AlertDescription.unknown_psk_identity: "unknown_psk_identity"} 89 | 90 | 91 | class TLSLocalAlert(TLSAlert): 92 | """A TLS alert has been signalled by the local implementation. 93 | 94 | :vartype description: int 95 | :ivar description: Set to one of the constants in 96 | :py:class:`tlslite.constants.AlertDescription` 97 | 98 | :vartype level: int 99 | :ivar level: Set to one of the constants in 100 | :py:class:`tlslite.constants.AlertLevel` 101 | 102 | :vartype message: str 103 | :ivar message: Description of what went wrong. 104 | """ 105 | 106 | def __init__(self, alert, message=None): 107 | self.description = alert.description 108 | self.level = alert.level 109 | self.message = message 110 | 111 | def __str__(self): 112 | alertStr = TLSAlert._descriptionStr.get(self.description) 113 | if alertStr == None: 114 | alertStr = str(self.description) 115 | if self.message: 116 | return alertStr + ": " + self.message 117 | else: 118 | return alertStr 119 | 120 | 121 | class TLSRemoteAlert(TLSAlert): 122 | """ 123 | A TLS alert has been signalled by the remote implementation. 124 | 125 | :vartype description: int 126 | :ivar description: Set to one of the constants in 127 | :py:class:`tlslite.constants.AlertDescription` 128 | 129 | :vartype level: int 130 | :ivar level: Set to one of the constants in 131 | :py:class:`tlslite.constants.AlertLevel` 132 | """ 133 | 134 | def __init__(self, alert): 135 | self.description = alert.description 136 | self.level = alert.level 137 | 138 | def __str__(self): 139 | alertStr = TLSAlert._descriptionStr.get(self.description) 140 | if alertStr == None: 141 | alertStr = str(self.description) 142 | return alertStr 143 | 144 | 145 | class TLSAuthenticationError(TLSError): 146 | """ 147 | The handshake succeeded, but the other party's authentication 148 | was inadequate. 149 | 150 | This exception will only be raised when a 151 | :py:class:`tlslite.Checker.Checker` has been passed to a handshake 152 | function. 153 | The Checker will be invoked once the handshake completes, and if 154 | the Checker objects to how the other party authenticated, a 155 | subclass of this exception will be raised. 156 | """ 157 | 158 | pass 159 | 160 | 161 | class TLSNoAuthenticationError(TLSAuthenticationError): 162 | """The Checker was expecting the other party to authenticate with a 163 | certificate chain, but this did not occur.""" 164 | 165 | pass 166 | 167 | 168 | class TLSAuthenticationTypeError(TLSAuthenticationError): 169 | """The Checker was expecting the other party to authenticate with a 170 | different type of certificate chain.""" 171 | 172 | pass 173 | 174 | 175 | class TLSFingerprintError(TLSAuthenticationError): 176 | """The Checker was expecting the other party to authenticate with a 177 | certificate chain that matches a different fingerprint.""" 178 | 179 | pass 180 | 181 | 182 | class TLSAuthorizationError(TLSAuthenticationError): 183 | """The Checker was expecting the other party to authenticate with a 184 | certificate chain that has a different authorization.""" 185 | 186 | pass 187 | 188 | 189 | class TLSValidationError(TLSAuthenticationError): 190 | """The Checker has determined that the other party's certificate 191 | chain is invalid.""" 192 | 193 | def __init__(self, msg, info=None): 194 | # Include a dict containing info about this validation failure 195 | TLSAuthenticationError.__init__(self, msg) 196 | self.info = info 197 | 198 | 199 | class TLSFaultError(TLSError): 200 | """The other party responded incorrectly to an induced fault. 201 | 202 | This exception will only occur during fault testing, when a 203 | :py:class:`tlslite.tlsconnection.TLSConnection`'s fault variable is 204 | set to induce some sort of 205 | faulty behavior, and the other party doesn't respond appropriately. 206 | """ 207 | 208 | pass 209 | 210 | 211 | class TLSUnsupportedError(TLSError): 212 | """The implementation doesn't support the requested (or required) 213 | capabilities.""" 214 | 215 | pass 216 | 217 | 218 | class TLSInternalError(TLSError): 219 | """The internal state of object is unexpected or invalid. 220 | 221 | Caused by incorrect use of API. 222 | """ 223 | 224 | pass 225 | 226 | 227 | class TLSProtocolException(BaseTLSException): 228 | """Exceptions used internally for handling errors in received messages""" 229 | 230 | pass 231 | 232 | 233 | class TLSIllegalParameterException(TLSProtocolException): 234 | """Parameters specified in message were incorrect or invalid""" 235 | 236 | pass 237 | 238 | 239 | class TLSDecodeError(TLSProtocolException): 240 | """The received message encoding does not match specification.""" 241 | 242 | pass 243 | 244 | 245 | class TLSUnexpectedMessage(TLSProtocolException): 246 | """ 247 | The received message was unexpected or parsing of Inner Plaintext 248 | failed 249 | """ 250 | 251 | pass 252 | 253 | 254 | class TLSRecordOverflow(TLSProtocolException): 255 | """The received record size was too big""" 256 | 257 | pass 258 | 259 | 260 | class TLSDecryptionFailed(TLSProtocolException): 261 | """Decryption of data was unsuccessful""" 262 | 263 | pass 264 | 265 | 266 | class TLSBadRecordMAC(TLSProtocolException): 267 | """Bad MAC (or padding in case of mac-then-encrypt)""" 268 | 269 | pass 270 | 271 | 272 | class TLSInsufficientSecurity(TLSProtocolException): 273 | """Parameters selected by user are too weak""" 274 | 275 | pass 276 | 277 | 278 | class TLSUnknownPSKIdentity(TLSProtocolException): 279 | """The PSK or SRP identity is unknown""" 280 | 281 | pass 282 | 283 | 284 | class TLSHandshakeFailure(TLSProtocolException): 285 | """Could not find acceptable set of handshake parameters""" 286 | 287 | pass 288 | 289 | 290 | class MaskTooLongError(EncryptionError): 291 | """The maskLen passed into function is too high""" 292 | 293 | pass 294 | 295 | 296 | class MessageTooLongError(EncryptionError): 297 | """The message passed into function is too long""" 298 | 299 | pass 300 | 301 | 302 | class EncodingError(EncryptionError): 303 | """An error appeared while encoding""" 304 | 305 | pass 306 | 307 | 308 | class InvalidSignature(EncryptionError): 309 | """Verification function found invalid signature""" 310 | 311 | pass 312 | 313 | 314 | class UnknownRSAType(EncryptionError): 315 | """Unknown RSA algorithm type passed""" 316 | 317 | pass 318 | -------------------------------------------------------------------------------- /tlslite/handshakehashes.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Hubert Kario 2 | # 3 | # See the LICENSE file for legal information regarding use of this file. 4 | """Handling cryptographic hashes for handshake protocol""" 5 | 6 | from .utils.compat import compat26Str, compatHMAC 7 | from .utils.cryptomath import MD5, SHA1 8 | from .utils import tlshashlib as hashlib 9 | 10 | class HandshakeHashes(object): 11 | 12 | """ 13 | Store and calculate necessary hashes for handshake protocol 14 | 15 | Calculates message digests of messages exchanged in handshake protocol 16 | of SSLv3 and TLS. 17 | """ 18 | 19 | def __init__(self): 20 | """Create instance""" 21 | self._handshakeMD5 = hashlib.md5() 22 | self._handshakeSHA = hashlib.sha1() 23 | self._handshakeSHA224 = hashlib.sha224() 24 | self._handshakeSHA256 = hashlib.sha256() 25 | self._handshakeSHA384 = hashlib.sha384() 26 | self._handshakeSHA512 = hashlib.sha512() 27 | 28 | def update(self, data): 29 | """ 30 | Add `data` to hash input. 31 | 32 | :param bytearray data: serialized TLS handshake message 33 | """ 34 | text = compat26Str(data) 35 | self._handshakeMD5.update(text) 36 | self._handshakeSHA.update(text) 37 | self._handshakeSHA224.update(text) 38 | self._handshakeSHA256.update(text) 39 | self._handshakeSHA384.update(text) 40 | self._handshakeSHA512.update(text) 41 | 42 | def digest(self, digest=None): 43 | """ 44 | Calculate and return digest for the already consumed data. 45 | 46 | Used for Finished and CertificateVerify messages. 47 | 48 | :param str digest: name of digest to return 49 | """ 50 | if digest is None: 51 | return self._handshakeMD5.digest() + self._handshakeSHA.digest() 52 | elif digest == 'md5': 53 | return self._handshakeMD5.digest() 54 | elif digest == 'sha1': 55 | return self._handshakeSHA.digest() 56 | elif digest == 'sha224': 57 | return self._handshakeSHA224.digest() 58 | elif digest == 'sha256': 59 | return self._handshakeSHA256.digest() 60 | elif digest == 'sha384': 61 | return self._handshakeSHA384.digest() 62 | elif digest == 'sha512': 63 | return self._handshakeSHA512.digest() 64 | else: 65 | raise ValueError("Unknown digest name") 66 | 67 | def digestSSL(self, masterSecret, label): 68 | """ 69 | Calculate and return digest for already consumed data (SSLv3 version) 70 | 71 | Used for Finished and CertificateVerify messages. 72 | 73 | :param bytearray masterSecret: value of the master secret 74 | :param bytearray label: label to include in the calculation 75 | """ 76 | #pylint: disable=maybe-no-member 77 | imacMD5 = self._handshakeMD5.copy() 78 | imacSHA = self._handshakeSHA.copy() 79 | #pylint: enable=maybe-no-member 80 | 81 | # the below difference in input for MD5 and SHA-1 is why we can't reuse 82 | # digest() method 83 | imacMD5.update(compatHMAC(label + masterSecret + bytearray([0x36]*48))) 84 | imacSHA.update(compatHMAC(label + masterSecret + bytearray([0x36]*40))) 85 | 86 | md5Bytes = MD5(masterSecret + bytearray([0x5c]*48) + \ 87 | bytearray(imacMD5.digest())) 88 | shaBytes = SHA1(masterSecret + bytearray([0x5c]*40) + \ 89 | bytearray(imacSHA.digest())) 90 | 91 | return md5Bytes + shaBytes 92 | 93 | #pylint: disable=protected-access, maybe-no-member 94 | def copy(self): 95 | """ 96 | Copy object 97 | 98 | Return a copy of the object with all the hashes in the same state 99 | as the source object. 100 | 101 | :rtype: HandshakeHashes 102 | """ 103 | other = HandshakeHashes() 104 | other._handshakeMD5 = self._handshakeMD5.copy() 105 | other._handshakeSHA = self._handshakeSHA.copy() 106 | other._handshakeSHA224 = self._handshakeSHA224.copy() 107 | other._handshakeSHA256 = self._handshakeSHA256.copy() 108 | other._handshakeSHA384 = self._handshakeSHA384.copy() 109 | other._handshakeSHA512 = self._handshakeSHA512.copy() 110 | return other 111 | -------------------------------------------------------------------------------- /tlslite/handshakehelpers.py: -------------------------------------------------------------------------------- 1 | # Authors: 2 | # Karel Srot 3 | # 4 | # See the LICENSE file for legal information regarding use of this file. 5 | 6 | """Class with various handshake helpers.""" 7 | 8 | from .extensions import PaddingExtension 9 | 10 | 11 | class HandshakeHelpers(object): 12 | """ 13 | This class encapsulates helper functions to be used with a TLS handshake. 14 | """ 15 | 16 | @staticmethod 17 | def alignClientHelloPadding(clientHello): 18 | """ 19 | Align ClientHello using the Padding extension to 512 bytes at least. 20 | 21 | :param ClientHello clientHello: ClientHello to be aligned 22 | """ 23 | # Check clientHello size if padding extension should be added 24 | # we want to add the extension even when using just SSLv3 25 | # cut-off 4 bytes with the Hello header (ClientHello type + Length) 26 | clientHelloLength = len(clientHello.write()) - 4 27 | if 256 <= clientHelloLength <= 511: 28 | if clientHello.extensions is None: 29 | clientHello.extensions = [] 30 | # we need to recalculate the size after extension list addition 31 | # results in extra 2 bytes, equals to 32 | # clientHelloLength = len(clientHello.write()) - 4 33 | clientHelloLength += 2 34 | # we want to get 512 bytes in total, including the padding 35 | # extension header (4B) 36 | paddingExtensionInstance = PaddingExtension().create( 37 | max(512 - clientHelloLength - 4, 0)) 38 | clientHello.extensions.append(paddingExtensionInstance) 39 | -------------------------------------------------------------------------------- /tlslite/integration/__init__.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """Classes for integrating TLS Lite with other packages.""" 5 | 6 | __all__ = ["asyncstatemachine", 7 | "httptlsconnection", 8 | "pop3_tls", 9 | "imap4_tls", 10 | "smtp_tls", 11 | "xmlrpctransport", 12 | "tlssocketservermixin", 13 | "tlsasyncdispatchermixin"] 14 | -------------------------------------------------------------------------------- /tlslite/integration/asyncstatemachine.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """ 5 | A state machine for using TLS Lite with asynchronous I/O. 6 | """ 7 | 8 | class AsyncStateMachine: 9 | """ 10 | This is an abstract class that's used to integrate TLS Lite with 11 | asyncore and Twisted. 12 | 13 | This class signals wantsReadsEvent() and wantsWriteEvent(). When 14 | the underlying socket has become readable or writeable, the event 15 | should be passed to this class by calling inReadEvent() or 16 | inWriteEvent(). This class will then try to read or write through 17 | the socket, and will update its state appropriately. 18 | 19 | This class will forward higher-level events to its subclass. For 20 | example, when a complete TLS record has been received, 21 | outReadEvent() will be called with the decrypted data. 22 | """ 23 | 24 | def __init__(self): 25 | self.result = None 26 | self.handshaker = None 27 | self.closer = None 28 | self.reader = None 29 | self.writer = None 30 | self._clear() 31 | 32 | def _clear(self): 33 | #These store the various asynchronous operations (i.e. 34 | #generators). Only one of them, at most, is ever active at a 35 | #time. 36 | self.handshaker = None 37 | self.closer = None 38 | self.reader = None 39 | self.writer = None 40 | 41 | #This stores the result from the last call to the 42 | #currently active operation. If 0 it indicates that the 43 | #operation wants to read, if 1 it indicates that the 44 | #operation wants to write. If None, there is no active 45 | #operation. 46 | self.result = None 47 | 48 | def _checkAssert(self, maxActive=1): 49 | #This checks that only one operation, at most, is 50 | #active, and that self.result is set appropriately. 51 | activeOps = 0 52 | if self.handshaker: 53 | activeOps += 1 54 | if self.closer: 55 | activeOps += 1 56 | if self.reader: 57 | activeOps += 1 58 | if self.writer: 59 | activeOps += 1 60 | 61 | if self.result == None: 62 | if activeOps != 0: 63 | raise AssertionError() 64 | elif self.result in (0,1): 65 | if activeOps != 1: 66 | raise AssertionError() 67 | else: 68 | raise AssertionError() 69 | if activeOps > maxActive: 70 | raise AssertionError() 71 | 72 | def wantsReadEvent(self): 73 | """If the state machine wants to read. 74 | 75 | If an operation is active, this returns whether or not the 76 | operation wants to read from the socket. If an operation is 77 | not active, this returns None. 78 | 79 | :rtype: bool or None 80 | :returns: If the state machine wants to read. 81 | """ 82 | if self.result != None: 83 | return self.result == 0 84 | return None 85 | 86 | def wantsWriteEvent(self): 87 | """If the state machine wants to write. 88 | 89 | If an operation is active, this returns whether or not the 90 | operation wants to write to the socket. If an operation is 91 | not active, this returns None. 92 | 93 | :rtype: bool or None 94 | :returns: If the state machine wants to write. 95 | """ 96 | if self.result != None: 97 | return self.result == 1 98 | return None 99 | 100 | def outConnectEvent(self): 101 | """Called when a handshake operation completes. 102 | 103 | May be overridden in subclass. 104 | """ 105 | pass 106 | 107 | def outCloseEvent(self): 108 | """Called when a close operation completes. 109 | 110 | May be overridden in subclass. 111 | """ 112 | pass 113 | 114 | def outReadEvent(self, readBuffer): 115 | """Called when a read operation completes. 116 | 117 | May be overridden in subclass.""" 118 | pass 119 | 120 | def outWriteEvent(self): 121 | """Called when a write operation completes. 122 | 123 | May be overridden in subclass.""" 124 | pass 125 | 126 | def inReadEvent(self): 127 | """Tell the state machine it can read from the socket.""" 128 | try: 129 | self._checkAssert() 130 | if self.handshaker: 131 | self._doHandshakeOp() 132 | elif self.closer: 133 | self._doCloseOp() 134 | elif self.reader: 135 | self._doReadOp() 136 | elif self.writer: 137 | self._doWriteOp() 138 | else: 139 | self.reader = self.tlsConnection.readAsync(16384) 140 | self._doReadOp() 141 | except: 142 | self._clear() 143 | raise 144 | 145 | def inWriteEvent(self): 146 | """Tell the state machine it can write to the socket.""" 147 | try: 148 | self._checkAssert() 149 | if self.handshaker: 150 | self._doHandshakeOp() 151 | elif self.closer: 152 | self._doCloseOp() 153 | elif self.reader: 154 | self._doReadOp() 155 | elif self.writer: 156 | self._doWriteOp() 157 | else: 158 | self.outWriteEvent() 159 | except: 160 | self._clear() 161 | raise 162 | 163 | def _doHandshakeOp(self): 164 | try: 165 | self.result = next(self.handshaker) 166 | except StopIteration: 167 | self.handshaker = None 168 | self.result = None 169 | self.outConnectEvent() 170 | 171 | def _doCloseOp(self): 172 | try: 173 | self.result = next(self.closer) 174 | except StopIteration: 175 | self.closer = None 176 | self.result = None 177 | self.outCloseEvent() 178 | 179 | def _doReadOp(self): 180 | self.result = next(self.reader) 181 | if not self.result in (0,1): 182 | readBuffer = self.result 183 | self.reader = None 184 | self.result = None 185 | self.outReadEvent(readBuffer) 186 | 187 | def _doWriteOp(self): 188 | try: 189 | self.result = next(self.writer) 190 | except StopIteration: 191 | self.writer = None 192 | self.result = None 193 | 194 | def setHandshakeOp(self, handshaker): 195 | """Start a handshake operation. 196 | 197 | :param generator handshaker: A generator created by using one of the 198 | asynchronous handshake functions (i.e. 199 | :py:meth:`~.TLSConnection.handshakeServerAsync` , or 200 | handshakeClientxxx(..., async=True). 201 | """ 202 | try: 203 | self._checkAssert(0) 204 | self.handshaker = handshaker 205 | self._doHandshakeOp() 206 | except: 207 | self._clear() 208 | raise 209 | 210 | def setServerHandshakeOp(self, **args): 211 | """Start a handshake operation. 212 | 213 | The arguments passed to this function will be forwarded to 214 | :py:obj:`~tlslite.tlsconnection.TLSConnection.handshakeServerAsync`. 215 | """ 216 | handshaker = self.tlsConnection.handshakeServerAsync(**args) 217 | self.setHandshakeOp(handshaker) 218 | 219 | def setCloseOp(self): 220 | """Start a close operation. 221 | """ 222 | try: 223 | self._checkAssert(0) 224 | self.closer = self.tlsConnection.closeAsync() 225 | self._doCloseOp() 226 | except: 227 | self._clear() 228 | raise 229 | 230 | def setWriteOp(self, writeBuffer): 231 | """Start a write operation. 232 | 233 | :param str writeBuffer: The string to transmit. 234 | """ 235 | try: 236 | self._checkAssert(0) 237 | self.writer = self.tlsConnection.writeAsync(writeBuffer) 238 | self._doWriteOp() 239 | except: 240 | self._clear() 241 | raise 242 | 243 | -------------------------------------------------------------------------------- /tlslite/integration/clienthelper.py: -------------------------------------------------------------------------------- 1 | # Authors: 2 | # Trevor Perrin 3 | # Dimitris Moraitis - Anon ciphersuites 4 | # 5 | # See the LICENSE file for legal information regarding use of this file. 6 | 7 | """ 8 | A helper class for using TLS Lite with stdlib clients 9 | (httplib, xmlrpclib, imaplib, poplib). 10 | """ 11 | 12 | from tlslite.checker import Checker 13 | from tlslite.utils.dns_utils import is_valid_hostname 14 | 15 | class ClientHelper(object): 16 | """This is a helper class used to integrate TLS Lite with various 17 | TLS clients (e.g. poplib, smtplib, httplib, etc.)""" 18 | 19 | def __init__(self, 20 | username=None, password=None, 21 | certChain=None, privateKey=None, 22 | checker=None, 23 | settings=None, 24 | anon=False, 25 | host=None): 26 | """ 27 | For client authentication, use one of these argument 28 | combinations: 29 | 30 | - username, password (SRP) 31 | - certChain, privateKey (certificate) 32 | 33 | For server authentication, you can either rely on the 34 | implicit mutual authentication performed by SRP, 35 | or you can do certificate-based server 36 | authentication with one of these argument combinations: 37 | 38 | - x509Fingerprint 39 | 40 | Certificate-based server authentication is compatible with 41 | SRP or certificate-based client authentication. 42 | 43 | The constructor does not perform the TLS handshake itself, but 44 | simply stores these arguments for later. The handshake is 45 | performed only when this class needs to connect with the 46 | server. Then you should be prepared to handle TLS-specific 47 | exceptions. See the client handshake functions in 48 | :py:class:`~tlslite.tlsconnection.TLSConnection` for details on which 49 | exceptions might be raised. 50 | 51 | :param str username: SRP username. Requires the 52 | 'password' argument. 53 | 54 | :param str password: SRP password for mutual authentication. 55 | Requires the 'username' argument. 56 | 57 | :param X509CertChain certChain: Certificate chain for client 58 | authentication. 59 | Requires the 'privateKey' argument. Excludes the SRP arguments. 60 | 61 | :param RSAKey privateKey: Private key for client authentication. 62 | Requires the 'certChain' argument. Excludes the SRP arguments. 63 | 64 | :param Checker checker: Callable object called after handshaking to 65 | evaluate the connection and raise an Exception if necessary. 66 | 67 | :type settings: HandshakeSettings 68 | :param settings: Various settings which can be used to control 69 | the ciphersuites, certificate types, and SSL/TLS versions 70 | offered by the client. 71 | 72 | :param bool anon: set to True if the negotiation should advertise only 73 | anonymous TLS ciphersuites. Mutually exclusive with client 74 | certificate 75 | authentication or SRP authentication 76 | 77 | :type host: str or None 78 | :param host: the hostname that the connection is made to. Can be an 79 | IP address (in which case the SNI extension won't be sent). Can 80 | include the port (in which case the port will be stripped and 81 | ignored). 82 | """ 83 | 84 | self.username = None 85 | self.password = None 86 | self.certChain = None 87 | self.privateKey = None 88 | self.checker = None 89 | self.anon = anon 90 | 91 | #SRP Authentication 92 | if username and password and not \ 93 | (certChain or privateKey): 94 | self.username = username 95 | self.password = password 96 | 97 | #Certificate Chain Authentication 98 | elif certChain and privateKey and not \ 99 | (username or password): 100 | self.certChain = certChain 101 | self.privateKey = privateKey 102 | 103 | #No Authentication 104 | elif not password and not username and not \ 105 | certChain and not privateKey: 106 | pass 107 | 108 | else: 109 | raise ValueError("Bad parameters") 110 | 111 | self.checker = checker 112 | self.settings = settings 113 | 114 | self.tlsSession = None 115 | 116 | if host is not None and not self._isIP(host): 117 | # name for SNI so port can't be sent 118 | colon = host.find(':') 119 | if colon > 0: 120 | host = host[:colon] 121 | self.serverName = host 122 | if host and not is_valid_hostname(host): 123 | raise ValueError("Invalid hostname: {0}".format(host)) 124 | else: 125 | self.serverName = None 126 | 127 | @staticmethod 128 | def _isIP(address): 129 | """Return True if the address is an IPv4 address""" 130 | if not address: 131 | return False 132 | vals = address.split('.') 133 | if len(vals) != 4: 134 | return False 135 | for i in vals: 136 | if not i.isdigit(): 137 | return False 138 | j = int(i) 139 | if not 0 <= j <= 255: 140 | return False 141 | return True 142 | 143 | def _handshake(self, tlsConnection): 144 | if self.username and self.password: 145 | tlsConnection.handshakeClientSRP(username=self.username, 146 | password=self.password, 147 | checker=self.checker, 148 | settings=self.settings, 149 | session=self.tlsSession, 150 | serverName=self.serverName) 151 | elif self.anon: 152 | tlsConnection.handshakeClientAnonymous(session=self.tlsSession, 153 | settings=self.settings, 154 | checker=self.checker, 155 | serverName=self.serverName) 156 | else: 157 | tlsConnection.handshakeClientCert(certChain=self.certChain, 158 | privateKey=self.privateKey, 159 | checker=self.checker, 160 | settings=self.settings, 161 | session=self.tlsSession, 162 | serverName=self.serverName) 163 | self.tlsSession = tlsConnection.session 164 | -------------------------------------------------------------------------------- /tlslite/integration/httptlsconnection.py: -------------------------------------------------------------------------------- 1 | # Authors: 2 | # Trevor Perrin 3 | # Kees Bos - Added ignoreAbruptClose parameter 4 | # Dimitris Moraitis - Anon ciphersuites 5 | # Martin von Loewis - python 3 port 6 | # 7 | # See the LICENSE file for legal information regarding use of this file. 8 | 9 | """TLS Lite + httplib.""" 10 | 11 | import socket 12 | try: 13 | import httplib 14 | except ImportError: 15 | # Python 3 16 | from http import client as httplib 17 | from tlslite.tlsconnection import TLSConnection 18 | from tlslite.integration.clienthelper import ClientHelper 19 | 20 | 21 | class HTTPTLSConnection(httplib.HTTPConnection, ClientHelper): 22 | """This class extends L{httplib.HTTPConnection} to support TLS.""" 23 | 24 | def __init__(self, host, port=None, strict=None, 25 | timeout=socket._GLOBAL_DEFAULT_TIMEOUT, 26 | source_address=None, 27 | username=None, password=None, 28 | certChain=None, privateKey=None, 29 | checker=None, 30 | settings=None, 31 | ignoreAbruptClose=False, 32 | anon=False): 33 | """Create a new HTTPTLSConnection. 34 | 35 | For client authentication, use one of these argument 36 | combinations: 37 | 38 | - username, password (SRP) 39 | - certChain, privateKey (certificate) 40 | 41 | For server authentication, you can either rely on the 42 | implicit mutual authentication performed by SRP 43 | or you can do certificate-based server 44 | authentication with one of these argument combinations: 45 | 46 | - x509Fingerprint 47 | 48 | Certificate-based server authentication is compatible with 49 | SRP or certificate-based client authentication. 50 | 51 | The constructor does not perform the TLS handshake itself, but 52 | simply stores these arguments for later. The handshake is 53 | performed only when this class needs to connect with the 54 | server. Thus you should be prepared to handle TLS-specific 55 | exceptions when calling methods inherited from 56 | :py:class:`httplib.HTTPConnection` such as request(), connect(), and 57 | send(). See the client handshake functions in 58 | :py:class:`~tlslite.tlsconnection.TLSConnection` for details on which 59 | exceptions might be raised. 60 | 61 | :type host: str 62 | :param host: Server to connect to. 63 | 64 | :type port: int 65 | :param port: Port to connect to. 66 | 67 | :type username: str 68 | :param username: SRP username. Requires the 69 | 'password' argument. 70 | 71 | :type password: str 72 | :param password: SRP password for mutual authentication. 73 | Requires the 'username' argument. 74 | 75 | :type certChain: ~tlslite.x509certchain.X509CertChain 76 | :param certChain: Certificate chain for client authentication. 77 | Requires the 'privateKey' argument. Excludes the SRP arguments. 78 | 79 | :type privateKey: ~tlslite.utils.rsakey.RSAKey 80 | :param privateKey: Private key for client authentication. 81 | Requires the 'certChain' argument. Excludes the SRP arguments. 82 | 83 | :type checker: ~tlslite.checker.Checker 84 | :param checker: Callable object called after handshaking to 85 | evaluate the connection and raise an Exception if necessary. 86 | 87 | :type settings: ~tlslite.handshakesettings.HandshakeSettings 88 | :param settings: Various settings which can be used to control 89 | the ciphersuites, certificate types, and SSL/TLS versions 90 | offered by the client. 91 | 92 | :type ignoreAbruptClose: bool 93 | :param ignoreAbruptClose: ignore the TLSAbruptCloseError on 94 | unexpected hangup. 95 | """ 96 | if source_address: 97 | httplib.HTTPConnection.__init__(self, 98 | host=host, 99 | port=port, 100 | timeout=timeout, 101 | source_address=source_address) 102 | if not source_address: 103 | httplib.HTTPConnection.__init__(self, 104 | host=host, 105 | port=port, 106 | timeout=timeout) 107 | self.ignoreAbruptClose = ignoreAbruptClose 108 | ClientHelper.__init__(self, 109 | username, password, 110 | certChain, privateKey, 111 | checker, 112 | settings, 113 | anon, 114 | host) 115 | 116 | def connect(self): 117 | httplib.HTTPConnection.connect(self) 118 | self.sock = TLSConnection(self.sock) 119 | self.sock.ignoreAbruptClose = self.ignoreAbruptClose 120 | ClientHelper._handshake(self, self.sock) 121 | -------------------------------------------------------------------------------- /tlslite/integration/imap4_tls.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """TLS Lite + imaplib.""" 5 | 6 | import socket 7 | from imaplib import IMAP4 8 | from tlslite.tlsconnection import TLSConnection 9 | from tlslite.integration.clienthelper import ClientHelper 10 | 11 | # IMAP TLS PORT 12 | IMAP4_TLS_PORT = 993 13 | 14 | class IMAP4_TLS(IMAP4, ClientHelper): 15 | """This class extends :py:class:`imaplib.IMAP4` with TLS support.""" 16 | 17 | def __init__(self, host = '', port = IMAP4_TLS_PORT, 18 | username=None, password=None, 19 | certChain=None, privateKey=None, 20 | checker=None, 21 | settings=None): 22 | """Create a new IMAP4_TLS. 23 | 24 | For client authentication, use one of these argument 25 | combinations: 26 | 27 | - username, password (SRP) 28 | - certChain, privateKey (certificate) 29 | 30 | For server authentication, you can either rely on the 31 | implicit mutual authentication performed by SRP 32 | or you can do certificate-based server 33 | authentication with one of these argument combinations: 34 | 35 | - x509Fingerprint 36 | 37 | Certificate-based server authentication is compatible with 38 | SRP or certificate-based client authentication. 39 | 40 | The caller should be prepared to handle TLS-specific 41 | exceptions. See the client handshake functions in 42 | :py:class:`~tlslite.tlsconnection.TLSConnection` for details on which 43 | exceptions might be raised. 44 | 45 | :type host: str 46 | :param host: Server to connect to. 47 | 48 | :type port: int 49 | :param port: Port to connect to. 50 | 51 | :type username: str 52 | :param username: SRP username. Requires the 53 | 'password' argument. 54 | 55 | :type password: str 56 | :param password: SRP password for mutual authentication. 57 | Requires the 'username' argument. 58 | 59 | :type certChain: ~tlslite.x509certchain.X509CertChain 60 | :param certChain: Certificate chain for client authentication. 61 | Requires the 'privateKey' argument. Excludes the SRP arguments. 62 | 63 | :type privateKey: ~tlslite.utils.rsakey.RSAKey 64 | :param privateKey: Private key for client authentication. 65 | Requires the 'certChain' argument. Excludes the SRP arguments. 66 | 67 | :type checker: ~tlslite.checker.Checker 68 | :param checker: Callable object called after handshaking to 69 | evaluate the connection and raise an Exception if necessary. 70 | 71 | :type settings: ~tlslite.handshakesettings.HandshakeSettings 72 | :param settings: Various settings which can be used to control 73 | the ciphersuites, certificate types, and SSL/TLS versions 74 | offered by the client. 75 | """ 76 | 77 | ClientHelper.__init__(self, 78 | username, password, 79 | certChain, privateKey, 80 | checker, 81 | settings) 82 | 83 | IMAP4.__init__(self, host, port) 84 | 85 | 86 | def open(self, host = '', port = IMAP4_TLS_PORT): 87 | """Setup connection to remote server on "host:port". 88 | 89 | This connection will be used by the routines: 90 | read, readline, send, shutdown. 91 | """ 92 | self.host = host 93 | self.port = port 94 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 95 | self.sock.connect((host, port)) 96 | self.sock = TLSConnection(self.sock) 97 | ClientHelper._handshake(self, self.sock) 98 | self.file = self.sock.makefile('rb') 99 | -------------------------------------------------------------------------------- /tlslite/integration/pop3_tls.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """TLS Lite + poplib.""" 5 | 6 | import socket 7 | from poplib import POP3, POP3_SSL_PORT 8 | from tlslite.tlsconnection import TLSConnection 9 | from tlslite.integration.clienthelper import ClientHelper 10 | 11 | class POP3_TLS(POP3, ClientHelper): 12 | """This class extends :py:class:`poplib.POP3` with TLS support.""" 13 | 14 | def __init__(self, host, port = POP3_SSL_PORT, 15 | timeout=socket._GLOBAL_DEFAULT_TIMEOUT, 16 | username=None, password=None, 17 | certChain=None, privateKey=None, 18 | checker=None, 19 | settings=None): 20 | """Create a new POP3_TLS. 21 | 22 | For client authentication, use one of these argument 23 | combinations: 24 | 25 | - username, password (SRP) 26 | - certChain, privateKey (certificate) 27 | 28 | For server authentication, you can either rely on the 29 | implicit mutual authentication performed by SRP or 30 | you can do certificate-based server 31 | authentication with one of these argument combinations: 32 | 33 | - x509Fingerprint 34 | 35 | Certificate-based server authentication is compatible with 36 | SRP or certificate-based client authentication. 37 | 38 | The caller should be prepared to handle TLS-specific 39 | exceptions. See the client handshake functions in 40 | :py:class:`~tlslite.tlsconnection.TLSConnection` 41 | for details on which 42 | exceptions might be raised. 43 | 44 | :type host: str 45 | :param host: Server to connect to. 46 | 47 | :type port: int 48 | :param port: Port to connect to. 49 | 50 | :type username: str 51 | :param username: SRP username. 52 | 53 | :type password: str 54 | :param password: SRP password for mutual authentication. 55 | Requires the 'username' argument. 56 | 57 | :type certChain: ~tlslite.x509certchain.X509CertChain 58 | :param certChain: Certificate chain for client authentication. 59 | Requires the 'privateKey' argument. Excludes the SRP argument. 60 | 61 | :type privateKey: ~tlslite.utils.rsakey.RSAKey 62 | :param privateKey: Private key for client authentication. 63 | Requires the 'certChain' argument. Excludes the SRP argument. 64 | 65 | :type checker: ~tlslite.checker.Checker 66 | :param checker: Callable object called after handshaking to 67 | evaluate the connection and raise an Exception if necessary. 68 | 69 | :type settings: ~tlslite.handshakesettings.HandshakeSettings 70 | :param settings: Various settings which can be used to control 71 | the ciphersuites, certificate types, and SSL/TLS versions 72 | offered by the client. 73 | """ 74 | self.host = host 75 | self.port = port 76 | sock = socket.create_connection((host, port), timeout) 77 | ClientHelper.__init__(self, 78 | username, password, 79 | certChain, privateKey, 80 | checker, 81 | settings) 82 | connection = TLSConnection(sock) 83 | ClientHelper._handshake(self, connection) 84 | self.sock = connection 85 | self.file = self.sock.makefile('rb') 86 | self._debugging = 0 87 | self.welcome = self._getresp() 88 | -------------------------------------------------------------------------------- /tlslite/integration/smtp_tls.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """TLS Lite + smtplib.""" 5 | 6 | from smtplib import SMTP 7 | from tlslite.tlsconnection import TLSConnection 8 | from tlslite.integration.clienthelper import ClientHelper 9 | 10 | class SMTP_TLS(SMTP): 11 | """This class extends :py:class:`smtplib.SMTP` with TLS support.""" 12 | 13 | def starttls(self, 14 | username=None, password=None, 15 | certChain=None, privateKey=None, 16 | checker=None, 17 | settings=None): 18 | """Puts the connection to the SMTP server into TLS mode. 19 | 20 | If the server supports TLS, this will encrypt the rest of the SMTP 21 | session. 22 | 23 | For client authentication, use one of these argument 24 | combinations: 25 | 26 | - username, password (SRP) 27 | - certChain, privateKey (certificate) 28 | 29 | For server authentication, you can either rely on the 30 | implicit mutual authentication performed by SRP or 31 | you can do certificate-based server 32 | authentication with one of these argument combinations: 33 | 34 | - x509Fingerprint 35 | 36 | Certificate-based server authentication is compatible with 37 | SRP or certificate-based client authentication. 38 | 39 | The caller should be prepared to handle TLS-specific 40 | exceptions. See the client handshake functions in 41 | :py:class:`~tlslite.tlsconnection.TLSConnection` for details on which 42 | exceptions might be raised. 43 | 44 | :type username: str 45 | :param username: SRP username. Requires the 46 | 'password' argument. 47 | 48 | :type password: str 49 | :param password: SRP password for mutual authentication. 50 | Requires the 'username' argument. 51 | 52 | :type certChain: ~tlslite.x509certchain.X509CertChain 53 | :param certChain: Certificate chain for client authentication. 54 | Requires the 'privateKey' argument. Excludes the SRP arguments. 55 | 56 | :type privateKey: ~tlslite.utils.rsakey.RSAKey 57 | :param privateKey: Private key for client authentication. 58 | Requires the 'certChain' argument. Excludes the SRP arguments. 59 | 60 | :type checker: ~tlslite.checker.Checker 61 | :param checker: Callable object called after handshaking to 62 | evaluate the connection and raise an Exception if necessary. 63 | 64 | :type settings: ~tlslite.handshakesettings.HandshakeSettings 65 | :param settings: Various settings which can be used to control 66 | the ciphersuites, certificate types, and SSL/TLS versions 67 | offered by the client. 68 | """ 69 | (resp, reply) = self.docmd("STARTTLS") 70 | if resp == 220: 71 | helper = ClientHelper( 72 | username, password, 73 | certChain, privateKey, 74 | checker, 75 | settings) 76 | conn = TLSConnection(self.sock) 77 | helper._handshake(conn) 78 | self.sock = conn 79 | self.file = conn.makefile('rb') 80 | return (resp, reply) 81 | -------------------------------------------------------------------------------- /tlslite/integration/tlsasyncdispatchermixin.py: -------------------------------------------------------------------------------- 1 | # Authors: 2 | # Trevor Perrin 3 | # Martin von Loewis - python 3 port 4 | # 5 | # See the LICENSE file for legal information regarding use of this file. 6 | 7 | """TLS Lite + asyncore.""" 8 | 9 | 10 | import asyncore 11 | from tlslite.tlsconnection import TLSConnection 12 | from .asyncstatemachine import AsyncStateMachine 13 | 14 | 15 | class TLSAsyncDispatcherMixIn(AsyncStateMachine): 16 | """ 17 | This class can be "mixed in" with an 18 | :py:class:`asyncore.dispatcher` to add TLS support. 19 | 20 | This class essentially sits between the dispatcher and the select 21 | loop, intercepting events and only calling the dispatcher when 22 | applicable. 23 | 24 | In the case of :py:meth:`handle_read`, a read operation will be activated, 25 | and when it completes, the bytes will be placed in a buffer where 26 | the dispatcher can retrieve them by calling :py:meth:`recv`, and the 27 | dispatcher's :py:meth:`handle_read` will be called. 28 | 29 | In the case of :py:meth:`handle_write`, the dispatcher's 30 | :py:meth:`handle_write` will 31 | be called, and when it calls :py:meth:`send`, a write operation will be 32 | activated. 33 | 34 | To use this class, you must combine it with an asyncore.dispatcher, 35 | and pass in a handshake operation with setServerHandshakeOp(). 36 | 37 | Below is an example of using this class with medusa. This class is 38 | mixed in with http_channel to create http_tls_channel. Note: 39 | 40 | 1. the mix-in is listed first in the inheritance list 41 | 42 | 2. the input buffer size must be at least 16K, otherwise the 43 | dispatcher might not read all the bytes from the TLS layer, 44 | leaving some bytes in limbo. 45 | 46 | 3. IE seems to have a problem receiving a whole HTTP response in a 47 | single TLS record, so HTML pages containing '\\r\\n\\r\\n' won't 48 | be displayed on IE. 49 | 50 | Add the following text into 'start_medusa.py', in the 'HTTP Server' 51 | section:: 52 | 53 | from tlslite import * 54 | s = open("./serverX509Cert.pem").read() 55 | x509 = X509() 56 | x509.parse(s) 57 | certChain = X509CertChain([x509]) 58 | 59 | s = open("./serverX509Key.pem").read() 60 | privateKey = parsePEMKey(s, private=True) 61 | 62 | class http_tls_channel(TLSAsyncDispatcherMixIn, 63 | http_server.http_channel): 64 | ac_in_buffer_size = 16384 65 | 66 | def __init__ (self, server, conn, addr): 67 | http_server.http_channel.__init__(self, server, conn, addr) 68 | TLSAsyncDispatcherMixIn.__init__(self, conn) 69 | self.tlsConnection.ignoreAbruptClose = True 70 | self.setServerHandshakeOp(certChain=certChain, 71 | privateKey=privateKey) 72 | 73 | hs.channel_class = http_tls_channel 74 | 75 | If the TLS layer raises an exception, the exception will be caught 76 | in asyncore.dispatcher, which will call :py:meth:`close` on this class. The 77 | TLS layer always closes the TLS connection before raising an 78 | exception, so the close operation will complete right away, causing 79 | asyncore.dispatcher.close() to be called, which closes the socket 80 | and removes this instance from the asyncore loop. 81 | """ 82 | 83 | 84 | def __init__(self, sock=None): 85 | AsyncStateMachine.__init__(self) 86 | 87 | if sock: 88 | self.tlsConnection = TLSConnection(sock) 89 | 90 | #Calculate the sibling I'm being mixed in with. 91 | #This is necessary since we override functions 92 | #like readable(), handle_read(), etc., but we 93 | #also want to call the sibling's versions. 94 | for cl in self.__class__.__bases__: 95 | if cl != TLSAsyncDispatcherMixIn and cl != AsyncStateMachine: 96 | self.siblingClass = cl 97 | break 98 | else: 99 | raise AssertionError() 100 | 101 | def readable(self): 102 | result = self.wantsReadEvent() 103 | if result != None: 104 | return result 105 | return self.siblingClass.readable(self) 106 | 107 | def writable(self): 108 | result = self.wantsWriteEvent() 109 | if result != None: 110 | return result 111 | return self.siblingClass.writable(self) 112 | 113 | def handle_read(self): 114 | self.inReadEvent() 115 | 116 | def handle_write(self): 117 | self.inWriteEvent() 118 | 119 | def outConnectEvent(self): 120 | self.siblingClass.handle_connect(self) 121 | 122 | def outCloseEvent(self): 123 | asyncore.dispatcher.close(self) 124 | 125 | def outReadEvent(self, readBuffer): 126 | self.readBuffer = readBuffer 127 | self.siblingClass.handle_read(self) 128 | 129 | def outWriteEvent(self): 130 | self.siblingClass.handle_write(self) 131 | 132 | def recv(self, bufferSize=16384): 133 | if bufferSize < 16384 or self.readBuffer == None: 134 | raise AssertionError() 135 | returnValue = self.readBuffer 136 | self.readBuffer = None 137 | return returnValue 138 | 139 | def send(self, writeBuffer): 140 | self.setWriteOp(writeBuffer) 141 | return len(writeBuffer) 142 | 143 | def close(self): 144 | if hasattr(self, "tlsConnection"): 145 | self.setCloseOp() 146 | else: 147 | asyncore.dispatcher.close(self) 148 | -------------------------------------------------------------------------------- /tlslite/integration/tlssocketservermixin.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """TLS Lite + SocketServer.""" 5 | 6 | from tlslite.tlsconnection import TLSConnection 7 | 8 | class TLSSocketServerMixIn: 9 | """ 10 | This class can be mixed in with any :py:class:`SocketServer.TCPServer` to 11 | add TLS support. 12 | 13 | To use this class, define a new class that inherits from it and 14 | some :py:class:`SocketServer.TCPServer` (with the mix-in first). Then 15 | implement the :py:meth:`handshake` method, doing some sort of server 16 | handshake on the connection argument. If the handshake method 17 | returns True, the RequestHandler will be triggered. Below is a 18 | complete example of a threaded HTTPS server:: 19 | 20 | from SocketServer import * 21 | from BaseHTTPServer import * 22 | from SimpleHTTPServer import * 23 | from tlslite import * 24 | 25 | s = open("./serverX509Cert.pem").read() 26 | x509 = X509() 27 | x509.parse(s) 28 | certChain = X509CertChain([x509]) 29 | 30 | s = open("./serverX509Key.pem").read() 31 | privateKey = parsePEMKey(s, private=True) 32 | 33 | sessionCache = SessionCache() 34 | 35 | class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn, 36 | HTTPServer): 37 | def handshake(self, tlsConnection): 38 | try: 39 | tlsConnection.handshakeServer(certChain=certChain, 40 | privateKey=privateKey, 41 | sessionCache=sessionCache) 42 | tlsConnection.ignoreAbruptClose = True 43 | return True 44 | except TLSError, error: 45 | print "Handshake failure:", str(error) 46 | return False 47 | 48 | httpd = MyHTTPServer(('localhost', 443), SimpleHTTPRequestHandler) 49 | httpd.serve_forever() 50 | """ 51 | 52 | 53 | def finish_request(self, sock, client_address): 54 | tlsConnection = TLSConnection(sock) 55 | if self.handshake(tlsConnection) == True: 56 | self.RequestHandlerClass(tlsConnection, client_address, self) 57 | tlsConnection.close() 58 | 59 | #Implement this method to do some form of handshaking. Return True 60 | #if the handshake finishes properly and the request is authorized. 61 | def handshake(self, tlsConnection): 62 | raise NotImplementedError() 63 | -------------------------------------------------------------------------------- /tlslite/integration/xmlrpcserver.py: -------------------------------------------------------------------------------- 1 | # Authors: 2 | # Kees Bos 3 | # Martin von Loewis - python 3 port 4 | # 5 | # See the LICENSE file for legal information regarding use of this file. 6 | 7 | """xmlrpcserver.py - simple XML RPC server supporting TLS.""" 8 | 9 | try: 10 | from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler 11 | except ImportError: 12 | # Python 3 13 | from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler 14 | from .tlssocketservermixin import TLSSocketServerMixIn 15 | 16 | 17 | class TLSXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 18 | """XMLRPCRequestHandler using TLS.""" 19 | 20 | # Redefine the setup method (see SocketServer.StreamRequestHandler) 21 | def setup(self): 22 | """Setup the connection for TLS.""" 23 | self.connection = self.request 24 | if getattr(self, 'timeout', None) is not None: 25 | # Python 2.7 26 | self.connection.settimeout(self.timeout) 27 | self.rfile = self.connection.makefile('rb', self.rbufsize) 28 | self.wfile = self.connection.makefile('wb', self.wbufsize) 29 | 30 | def do_POST(self): 31 | """Handle the HTTPS POST request.""" 32 | SimpleXMLRPCRequestHandler.do_POST(self) 33 | try: 34 | # shut down the connection 35 | self.connection.shutdown() 36 | except: 37 | pass 38 | 39 | 40 | class TLSXMLRPCServer(TLSSocketServerMixIn, 41 | SimpleXMLRPCServer): 42 | """Simple XML-RPC server using TLS.""" 43 | 44 | def __init__(self, addr, *args, **kwargs): 45 | if not args and not 'requestHandler' in kwargs: 46 | kwargs['requestHandler'] = TLSXMLRPCRequestHandler 47 | SimpleXMLRPCServer.__init__(self, addr, *args, **kwargs) 48 | 49 | 50 | class MultiPathTLSXMLRPCServer(TLSXMLRPCServer): 51 | """Multipath XML-RPC Server using TLS.""" 52 | 53 | def __init__(self, addr, *args, **kwargs): 54 | TLSXMLRPCServer.__init__(addr, *args, **kwargs) 55 | self.dispatchers = {} 56 | self.allow_none = allow_none 57 | self.encoding = encoding 58 | -------------------------------------------------------------------------------- /tlslite/integration/xmlrpctransport.py: -------------------------------------------------------------------------------- 1 | # Authors: 2 | # Trevor Perrin 3 | # Kees Bos - Fixes for compatibility with different Python versions 4 | # Martin von Loewis - python 3 port 5 | # 6 | # See the LICENSE file for legal information regarding use of this file. 7 | 8 | 9 | """TLS Lite + xmlrpclib.""" 10 | 11 | try: 12 | import xmlrpclib 13 | import httplib 14 | except ImportError: 15 | # Python 3 16 | from xmlrpc import client as xmlrpclib 17 | from http import client as httplib 18 | from tlslite.integration.httptlsconnection import HTTPTLSConnection 19 | from tlslite.integration.clienthelper import ClientHelper 20 | import tlslite.errors 21 | 22 | 23 | class XMLRPCTransport(xmlrpclib.Transport, ClientHelper): 24 | """Handles an HTTPS transaction to an XML-RPC server.""" 25 | 26 | # Pre python 2.7, the make_connection returns a HTTP class 27 | transport = xmlrpclib.Transport() 28 | conn_class_is_http = not hasattr(transport, '_connection') 29 | del(transport) 30 | 31 | def __init__(self, use_datetime=0, 32 | username=None, password=None, 33 | certChain=None, privateKey=None, 34 | checker=None, 35 | settings=None, 36 | ignoreAbruptClose=False): 37 | """ 38 | Create a new XMLRPCTransport. 39 | 40 | An instance of this class can be passed to 41 | :py:class:`xmlrpclib.ServerProxy` 42 | to use TLS with XML-RPC calls:: 43 | 44 | from tlslite import XMLRPCTransport 45 | from xmlrpclib import ServerProxy 46 | 47 | transport = XMLRPCTransport(user="alice", password="abra123") 48 | server = ServerProxy("https://localhost", transport) 49 | 50 | For client authentication, use one of these argument 51 | combinations: 52 | 53 | - username, password (SRP) 54 | - certChain, privateKey (certificate) 55 | 56 | For server authentication, you can either rely on the 57 | implicit mutual authentication performed by SRP or 58 | you can do certificate-based server 59 | authentication with one of these argument combinations: 60 | 61 | - x509Fingerprint 62 | 63 | Certificate-based server authentication is compatible with 64 | SRP or certificate-based client authentication. 65 | 66 | The constructor does not perform the TLS handshake itself, but 67 | simply stores these arguments for later. The handshake is 68 | performed only when this class needs to connect with the 69 | server. Thus you should be prepared to handle TLS-specific 70 | exceptions when calling methods of :py:class:`xmlrpclib.ServerProxy`. 71 | See the 72 | client handshake functions in 73 | :py:class:`~tlslite.tlsconnection.TLSConnection` for details on which 74 | exceptions might be raised. 75 | 76 | :type username: str 77 | :param username: SRP username. Requires the 78 | 'password' argument. 79 | 80 | :type password: str 81 | :param password: SRP password for mutual authentication. 82 | Requires the 'username' argument. 83 | 84 | :type certChain: ~tlslite.x509certchain.X509CertChain 85 | :param certChain: Certificate chain for client authentication. 86 | Requires the 'privateKey' argument. Excludes the SRP arguments. 87 | 88 | :type privateKey: ~tlslite.utils.rsakey.RSAKey 89 | :param privateKey: Private key for client authentication. 90 | Requires the 'certChain' argument. Excludes the SRP arguments. 91 | 92 | :type checker: ~tlslite.checker.Checker 93 | :param checker: Callable object called after handshaking to 94 | evaluate the connection and raise an Exception if necessary. 95 | 96 | :type settings: ~tlslite.handshakesettings.HandshakeSettings 97 | :param settings: Various settings which can be used to control 98 | the ciphersuites, certificate types, and SSL/TLS versions 99 | offered by the client. 100 | 101 | :type ignoreAbruptClose: bool 102 | :param ignoreAbruptClose: ignore the TLSAbruptCloseError on 103 | unexpected hangup. 104 | """ 105 | # self._connection is new in python 2.7, since we're using it here, 106 | # we'll add this ourselves too, just in case we're pre-2.7 107 | self._connection = (None, None) 108 | xmlrpclib.Transport.__init__(self, use_datetime) 109 | self.ignoreAbruptClose = ignoreAbruptClose 110 | ClientHelper.__init__(self, 111 | username, password, 112 | certChain, privateKey, 113 | checker, 114 | settings) 115 | 116 | def make_connection(self, host): 117 | """Make a connection to `host`. Reuse keepalive connections.""" 118 | # return an existing connection if possible. This allows 119 | # HTTP/1.1 keep-alive. 120 | if self._connection and host == self._connection[0]: 121 | http = self._connection[1] 122 | else: 123 | # create a HTTPS connection object from a host descriptor 124 | chost, extra_headers, x509 = self.get_host_info(host) 125 | 126 | http = HTTPTLSConnection(chost, None, 127 | username=self.username, password=self.password, 128 | certChain=self.certChain, privateKey=self.privateKey, 129 | checker=self.checker, 130 | settings=self.settings, 131 | ignoreAbruptClose=self.ignoreAbruptClose) 132 | # store the host argument along with the connection object 133 | self._connection = host, http 134 | if not self.conn_class_is_http: 135 | return http 136 | http2 = httplib.HTTP() 137 | http2._setup(http) 138 | return http2 139 | -------------------------------------------------------------------------------- /tlslite/messagesocket.py: -------------------------------------------------------------------------------- 1 | # vim: set fileencoding=utf8 2 | # 3 | # Copyright © 2015, Hubert Kario 4 | # 5 | # See the LICENSE file for legal information regarding use of this file. 6 | 7 | """Wrapper of TLS RecordLayer providing message-level abstraction""" 8 | 9 | from .recordlayer import RecordLayer 10 | from .constants import ContentType 11 | from .messages import RecordHeader3, Message 12 | from .utils.codec import Parser 13 | 14 | class MessageSocket(RecordLayer): 15 | 16 | """TLS Record Layer socket that provides Message level abstraction 17 | 18 | Because the record layer has a hard size limit on sent messages, they need 19 | to be fragmented before sending. Similarly, a single record layer record 20 | can include multiple handshake protocol messages (very common with 21 | ServerHello, Certificate and ServerHelloDone), as such, the user of 22 | RecordLayer needs to fragment those records into multiple messages. 23 | Unfortunately, fragmentation of messages requires some degree of 24 | knowledge about the messages passed and as such is outside scope of pure 25 | record layer implementation. 26 | 27 | This class tries to provide a useful abstraction for handling Handshake 28 | protocol messages. 29 | 30 | :vartype recordSize: int 31 | :ivar recordSize: maximum size of records sent through socket. Messages 32 | bigger than this size will be fragmented to smaller chunks. Setting it 33 | to higher value than the default 2^14 will make the implementation 34 | non RFC compliant and likely not interoperable with other peers. 35 | 36 | :vartype defragmenter: Defragmenter 37 | :ivar defragmenter: defragmenter used for read records 38 | 39 | :vartype unfragmentedDataTypes: tuple 40 | :ivar unfragmentedDataTypes: data types which will be passed as-read, 41 | TLS application_data by default 42 | """ 43 | 44 | def __init__(self, sock, defragmenter): 45 | """Apply TLS Record Layer abstraction to raw network socket. 46 | 47 | :type sock: socket.socket 48 | :param sock: network socket to wrap 49 | :type defragmenter: Defragmenter 50 | :param defragmenter: defragmenter to apply on the records read 51 | """ 52 | super(MessageSocket, self).__init__(sock) 53 | 54 | self.defragmenter = defragmenter 55 | self.unfragmentedDataTypes = tuple((ContentType.application_data, )) 56 | self._lastRecordVersion = (0, 0) 57 | 58 | self._sendBuffer = bytearray(0) 59 | self._sendBufferType = None 60 | 61 | self.recordSize = 2**14 62 | 63 | def recvMessage(self): 64 | """ 65 | Read next message in queue 66 | 67 | will return a 0 or 1 if the read is blocking, a tuple of 68 | :py:class:`RecordHeader3` and :py:class:`Parser` in case a message was 69 | received. 70 | 71 | :rtype: generator 72 | """ 73 | while True: 74 | while True: 75 | ret = self.defragmenter.getMessage() 76 | if ret is None: 77 | break 78 | header = RecordHeader3().create(self._lastRecordVersion, 79 | ret[0], 80 | 0) 81 | yield header, Parser(ret[1]) 82 | 83 | for ret in self.recvRecord(): 84 | if ret in (0, 1): 85 | yield ret 86 | else: 87 | break 88 | 89 | header, parser = ret 90 | if header.type in self.unfragmentedDataTypes: 91 | yield ret 92 | # TODO probably needs a bit better handling... 93 | if header.ssl2: 94 | yield ret 95 | 96 | self.defragmenter.addData(header.type, parser.bytes) 97 | self._lastRecordVersion = header.version 98 | 99 | def recvMessageBlocking(self): 100 | """Blocking variant of :py:meth:`recvMessage`.""" 101 | for res in self.recvMessage(): 102 | if res in (0, 1): 103 | pass 104 | else: 105 | return res 106 | 107 | def flush(self): 108 | """ 109 | Empty the queue of messages to write 110 | 111 | Will fragment the messages and write them in as little records as 112 | possible. 113 | 114 | :rtype: generator 115 | """ 116 | while len(self._sendBuffer) > 0: 117 | recordPayload = self._sendBuffer[:self.recordSize] 118 | self._sendBuffer = self._sendBuffer[self.recordSize:] 119 | msg = Message(self._sendBufferType, recordPayload) 120 | for res in self.sendRecord(msg): 121 | yield res 122 | 123 | assert len(self._sendBuffer) == 0 124 | self._sendBufferType = None 125 | 126 | def flushBlocking(self): 127 | """Blocking variant of :py:meth:`flush`.""" 128 | for _ in self.flush(): 129 | pass 130 | 131 | def queueMessage(self, msg): 132 | """ 133 | Queue message for sending 134 | 135 | If the message is of same type as messages in queue, the message is 136 | just added to queue. 137 | 138 | If the message is of different type as messages in queue, the queue is 139 | flushed and then the message is queued. 140 | 141 | :rtype: generator 142 | """ 143 | if self._sendBufferType is None: 144 | self._sendBufferType = msg.contentType 145 | 146 | if msg.contentType == self._sendBufferType: 147 | self._sendBuffer += msg.write() 148 | return 149 | 150 | for res in self.flush(): 151 | yield res 152 | 153 | assert self._sendBufferType is None 154 | self._sendBufferType = msg.contentType 155 | self._sendBuffer += msg.write() 156 | 157 | def queueMessageBlocking(self, msg): 158 | """Blocking variant of :py:meth:`queueMessage`.""" 159 | for _ in self.queueMessage(msg): 160 | pass 161 | 162 | def sendMessage(self, msg): 163 | """ 164 | Fragment and send a message. 165 | 166 | If a messages already of same type reside in queue, the message if 167 | first added to it and then the queue is flushed. 168 | 169 | If the message is of different type than the queue, the queue is 170 | flushed, the message is added to queue and the queue is flushed again. 171 | 172 | Use the sendRecord() message if you want to send a message outside 173 | the queue, or a message of zero size. 174 | 175 | :rtype: generator 176 | """ 177 | for res in self.queueMessage(msg): 178 | yield res 179 | 180 | for res in self.flush(): 181 | yield res 182 | 183 | def sendMessageBlocking(self, msg): 184 | """Blocking variant of :py:meth:`sendMessage`.""" 185 | for _ in self.sendMessage(msg): 186 | pass 187 | -------------------------------------------------------------------------------- /tlslite/session.py: -------------------------------------------------------------------------------- 1 | # Authors: 2 | # Trevor Perrin 3 | # Dave Baggett (Arcode Corporation) - canonicalCipherName 4 | # 5 | # See the LICENSE file for legal information regarding use of this file. 6 | 7 | """Class representing a TLS session.""" 8 | 9 | from .utils.compat import * 10 | from .mathtls import * 11 | from .constants import * 12 | 13 | class Session(object): 14 | """ 15 | This class represents a TLS session. 16 | 17 | TLS distinguishes between connections and sessions. A new 18 | handshake creates both a connection and a session. Data is 19 | transmitted over the connection. 20 | 21 | The session contains a more permanent record of the handshake. The 22 | session can be inspected to determine handshake results. The 23 | session can also be used to create a new connection through 24 | "session resumption". If the client and server both support this, 25 | they can create a new connection based on an old session without 26 | the overhead of a full handshake. 27 | 28 | The session for a :py:class:`~tlslite.tlsconnection.TLSConnection` can be 29 | retrieved from the connection's 'session' attribute. 30 | 31 | :vartype srpUsername: str 32 | :ivar srpUsername: The client's SRP username (or None). 33 | 34 | :vartype clientCertChain: ~tlslite.x509certchain.X509CertChain 35 | :ivar clientCertChain: The client's certificate chain (or None). 36 | 37 | :vartype serverCertChain: ~tlslite.x509certchain.X509CertChain 38 | :ivar serverCertChain: The server's certificate chain (or None). 39 | 40 | :vartype tackExt: tack.structures.TackExtension.TackExtension 41 | :ivar tackExt: The server's TackExtension (or None). 42 | 43 | :vartype tackInHelloExt: bool 44 | :ivar tackInHelloExt: True if a TACK was presented via TLS Extension. 45 | 46 | :vartype encryptThenMAC: bool 47 | :ivar encryptThenMAC: True if connection uses CBC cipher in 48 | encrypt-then-MAC mode 49 | 50 | :vartype appProto: bytearray 51 | :ivar appProto: name of the negotiated application level protocol, None 52 | if not negotiated 53 | """ 54 | 55 | def __init__(self): 56 | self.masterSecret = bytearray(0) 57 | self.sessionID = bytearray(0) 58 | self.cipherSuite = 0 59 | self.srpUsername = "" 60 | self.clientCertChain = None 61 | self.serverCertChain = None 62 | self.tackExt = None 63 | self.tackInHelloExt = False 64 | self.serverName = "" 65 | self.resumable = False 66 | self.encryptThenMAC = False 67 | self.extendedMasterSecret = False 68 | self.appProto = bytearray(0) 69 | 70 | def create(self, masterSecret, sessionID, cipherSuite, 71 | srpUsername, clientCertChain, serverCertChain, 72 | tackExt, tackInHelloExt, serverName, resumable=True, 73 | encryptThenMAC=False, extendedMasterSecret=False, 74 | appProto=bytearray(0)): 75 | self.masterSecret = masterSecret 76 | self.sessionID = sessionID 77 | self.cipherSuite = cipherSuite 78 | self.srpUsername = srpUsername 79 | self.clientCertChain = clientCertChain 80 | self.serverCertChain = serverCertChain 81 | self.tackExt = tackExt 82 | self.tackInHelloExt = tackInHelloExt 83 | self.serverName = serverName 84 | self.resumable = resumable 85 | self.encryptThenMAC = encryptThenMAC 86 | self.extendedMasterSecret = extendedMasterSecret 87 | self.appProto = appProto 88 | 89 | def _clone(self): 90 | other = Session() 91 | other.masterSecret = self.masterSecret 92 | other.sessionID = self.sessionID 93 | other.cipherSuite = self.cipherSuite 94 | other.srpUsername = self.srpUsername 95 | other.clientCertChain = self.clientCertChain 96 | other.serverCertChain = self.serverCertChain 97 | other.tackExt = self.tackExt 98 | other.tackInHelloExt = self.tackInHelloExt 99 | other.serverName = self.serverName 100 | other.resumable = self.resumable 101 | other.encryptThenMAC = self.encryptThenMAC 102 | other.extendedMasterSecret = self.extendedMasterSecret 103 | other.appProto = self.appProto 104 | return other 105 | 106 | def valid(self): 107 | """If this session can be used for session resumption. 108 | 109 | :rtype: bool 110 | :returns: If this session can be used for session resumption. 111 | """ 112 | return self.resumable and self.sessionID 113 | 114 | def _setResumable(self, boolean): 115 | #Only let it be set to True if the sessionID is non-null 116 | if (not boolean) or (boolean and self.sessionID): 117 | self.resumable = boolean 118 | 119 | def getTackId(self): 120 | if self.tackExt and self.tackExt.tack: 121 | return self.tackExt.tack.getTackId() 122 | else: 123 | return None 124 | 125 | def getBreakSigs(self): 126 | if self.tackExt and self.tackExt.break_sigs: 127 | return self.tackExt.break_sigs 128 | else: 129 | return None 130 | 131 | def getCipherName(self): 132 | """Get the name of the cipher used with this connection. 133 | 134 | :rtype: str 135 | :returns: The name of the cipher used with this connection. 136 | """ 137 | return CipherSuite.canonicalCipherName(self.cipherSuite) 138 | 139 | def getMacName(self): 140 | """Get the name of the HMAC hash algo used with this connection. 141 | 142 | :rtype: str 143 | :returns: The name of the HMAC hash algo used with this connection. 144 | """ 145 | return CipherSuite.canonicalMacName(self.cipherSuite) 146 | -------------------------------------------------------------------------------- /tlslite/sessioncache.py: -------------------------------------------------------------------------------- 1 | # Authors: 2 | # Trevor Perrin 3 | # Martin von Loewis - python 3 port 4 | # Mirko Dziadzka - bugfix 5 | # 6 | # See the LICENSE file for legal information regarding use of this file. 7 | 8 | """Class for caching TLS sessions.""" 9 | 10 | import threading 11 | import time 12 | 13 | class SessionCache(object): 14 | """This class is used by the server to cache TLS sessions. 15 | 16 | Caching sessions allows the client to use TLS session resumption 17 | and avoid the expense of a full handshake. To use this class, 18 | simply pass a SessionCache instance into the server handshake 19 | function. 20 | 21 | This class is thread-safe. 22 | """ 23 | 24 | #References to these instances 25 | #are also held by the caller, who may change the 'resumable' 26 | #flag, so the SessionCache must return the same instances 27 | #it was passed in. 28 | 29 | def __init__(self, maxEntries=10000, maxAge=14400): 30 | """Create a new SessionCache. 31 | 32 | :type maxEntries: int 33 | :param maxEntries: The maximum size of the cache. When this 34 | limit is reached, the oldest sessions will be deleted as 35 | necessary to make room for new ones. The default is 10000. 36 | 37 | :type maxAge: int 38 | :param maxAge: The number of seconds before a session expires 39 | from the cache. The default is 14400 (i.e. 4 hours).""" 40 | 41 | self.lock = threading.Lock() 42 | 43 | # Maps sessionIDs to sessions 44 | self.entriesDict = {} 45 | 46 | #Circular list of (sessionID, timestamp) pairs 47 | self.entriesList = [(None,None)] * maxEntries 48 | 49 | self.firstIndex = 0 50 | self.lastIndex = 0 51 | self.maxAge = maxAge 52 | 53 | def __getitem__(self, sessionID): 54 | self.lock.acquire() 55 | try: 56 | self._purge() #Delete old items, so we're assured of a new one 57 | session = self.entriesDict[bytes(sessionID)] 58 | 59 | #When we add sessions they're resumable, but it's possible 60 | #for the session to be invalidated later on (if a fatal alert 61 | #is returned), so we have to check for resumability before 62 | #returning the session. 63 | 64 | if session.valid(): 65 | return session 66 | else: 67 | raise KeyError() 68 | finally: 69 | self.lock.release() 70 | 71 | 72 | def __setitem__(self, sessionID, session): 73 | self.lock.acquire() 74 | try: 75 | #Add the new element 76 | self.entriesDict[bytes(sessionID)] = session 77 | self.entriesList[self.lastIndex] = (bytes(sessionID), time.time()) 78 | self.lastIndex = (self.lastIndex+1) % len(self.entriesList) 79 | 80 | #If the cache is full, we delete the oldest element to make an 81 | #empty space 82 | if self.lastIndex == self.firstIndex: 83 | del(self.entriesDict[self.entriesList[self.firstIndex][0]]) 84 | self.firstIndex = (self.firstIndex+1) % len(self.entriesList) 85 | finally: 86 | self.lock.release() 87 | 88 | #Delete expired items 89 | def _purge(self): 90 | currentTime = time.time() 91 | 92 | #Search through the circular list, deleting expired elements until 93 | #we reach a non-expired element. Since elements in list are 94 | #ordered in time, we can break once we reach the first non-expired 95 | #element 96 | index = self.firstIndex 97 | while index != self.lastIndex: 98 | if currentTime - self.entriesList[index][1] > self.maxAge: 99 | del(self.entriesDict[self.entriesList[index][0]]) 100 | index = (index+1) % len(self.entriesList) 101 | else: 102 | break 103 | self.firstIndex = index 104 | -------------------------------------------------------------------------------- /tlslite/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """Toolkit for crypto and other stuff.""" 5 | 6 | __all__ = ["aes", 7 | "asn1parser", 8 | "cipherfactory", 9 | "codec", 10 | "cryptomath", 11 | "datefuncs", 12 | "compat", 13 | "keyfactory", 14 | "openssl_aes", 15 | "openssl_rc4", 16 | "openssl_rsakey", 17 | "openssl_tripledes", 18 | "pycrypto_aes", 19 | "pycrypto_rc4", 20 | "pycrypto_rsakey", 21 | "pycrypto_tripledes", 22 | "python_aes", 23 | "python_rc4", 24 | "python_rsakey", 25 | "rc4", 26 | "rijndael", 27 | "rsakey", 28 | "tackpywrapper", 29 | "tripledes"] 30 | -------------------------------------------------------------------------------- /tlslite/utils/aes.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """Abstract class for AES.""" 5 | 6 | class AES(object): 7 | def __init__(self, key, mode, IV, implementation): 8 | if len(key) not in (16, 24, 32): 9 | raise AssertionError() 10 | if mode != 2: 11 | raise AssertionError() 12 | if len(IV) != 16: 13 | raise AssertionError() 14 | self.isBlockCipher = True 15 | self.isAEAD = False 16 | self.block_size = 16 17 | self.implementation = implementation 18 | if len(key)==16: 19 | self.name = "aes128" 20 | elif len(key)==24: 21 | self.name = "aes192" 22 | elif len(key)==32: 23 | self.name = "aes256" 24 | else: 25 | raise AssertionError() 26 | 27 | #CBC-Mode encryption, returns ciphertext 28 | #WARNING: *MAY* modify the input as well 29 | def encrypt(self, plaintext): 30 | assert(len(plaintext) % 16 == 0) 31 | 32 | #CBC-Mode decryption, returns plaintext 33 | #WARNING: *MAY* modify the input as well 34 | def decrypt(self, ciphertext): 35 | assert(len(ciphertext) % 16 == 0) 36 | -------------------------------------------------------------------------------- /tlslite/utils/aesgcm.py: -------------------------------------------------------------------------------- 1 | # Author: Google 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | # GCM derived from Go's implementation in crypto/cipher. 5 | # 6 | # https://golang.org/src/crypto/cipher/gcm.go 7 | 8 | # GCM works over elements of the field GF(2^128), each of which is a 128-bit 9 | # polynomial. Throughout this implementation, polynomials are represented as 10 | # Python integers with the low-order terms at the most significant bits. So a 11 | # 128-bit polynomial is an integer from 0 to 2^128-1 with the most significant 12 | # bit representing the x^0 term and the least significant bit representing the 13 | # x^127 term. This bit reversal also applies to polynomials used as indices in a 14 | # look-up table. 15 | 16 | from __future__ import division 17 | from .cryptomath import bytesToNumber, numberToByteArray 18 | 19 | class AESGCM(object): 20 | """ 21 | AES-GCM implementation. Note: this implementation does not attempt 22 | to be side-channel resistant. It's also rather slow. 23 | """ 24 | 25 | def __init__(self, key, implementation, rawAesEncrypt): 26 | self.isBlockCipher = False 27 | self.isAEAD = True 28 | self.nonceLength = 12 29 | self.tagLength = 16 30 | self.implementation = implementation 31 | if len(key) == 16: 32 | self.name = "aes128gcm" 33 | elif len(key) == 32: 34 | self.name = "aes256gcm" 35 | else: 36 | raise AssertionError() 37 | 38 | self._rawAesEncrypt = rawAesEncrypt 39 | 40 | # The GCM key is AES(0). 41 | h = bytesToNumber(self._rawAesEncrypt(bytearray(16))) 42 | 43 | # Pre-compute all 4-bit multiples of h. Note that bits are reversed 44 | # because our polynomial representation places low-order terms at the 45 | # most significant bit. Thus x^0 * h = h is at index 0b1000 = 8 and 46 | # x^1 * h is at index 0b0100 = 4. 47 | self._productTable = [0] * 16 48 | self._productTable[self._reverseBits(1)] = h 49 | for i in range(2, 16, 2): 50 | self._productTable[self._reverseBits(i)] = \ 51 | self._gcmShift(self._productTable[self._reverseBits(i//2)]) 52 | self._productTable[self._reverseBits(i+1)] = \ 53 | self._gcmAdd(self._productTable[self._reverseBits(i)], h) 54 | 55 | def _rawAesCtrEncrypt(self, counter, inp): 56 | """ 57 | Encrypts (or decrypts) plaintext with AES-CTR. counter is modified. 58 | """ 59 | out = bytearray(len(inp)) 60 | rawAesEncrypt = self._rawAesEncrypt 61 | for i in range(0, len(out), 16): 62 | mask = rawAesEncrypt(counter) 63 | for j in range(i, min(len(out), i + 16)): 64 | out[j] = inp[j] ^ mask[j-i] 65 | self._inc32(counter) 66 | return out 67 | 68 | def _auth(self, ciphertext, ad, tagMask): 69 | y = 0 70 | y = self._update(y, ad) 71 | y = self._update(y, ciphertext) 72 | y ^= (len(ad) << (3 + 64)) | (len(ciphertext) << 3) 73 | y = self._mul(y) 74 | y ^= bytesToNumber(tagMask) 75 | return numberToByteArray(y, 16) 76 | 77 | def _update(self, y, data): 78 | for i in range(0, len(data) // 16): 79 | y ^= bytesToNumber(data[16*i:16*i+16]) 80 | y = self._mul(y) 81 | extra = len(data) % 16 82 | if extra != 0: 83 | block = bytearray(16) 84 | block[:extra] = data[-extra:] 85 | y ^= bytesToNumber(block) 86 | y = self._mul(y) 87 | return y 88 | 89 | def _mul(self, y): 90 | """ Returns y*H, where H is the GCM key. """ 91 | ret = 0 92 | # Multiply H by y 4 bits at a time, starting with the highest power 93 | # terms. 94 | for i in range(0, 128, 4): 95 | # Multiply by x^4. The reduction for the top four terms is 96 | # precomputed. 97 | retHigh = ret & 0xf 98 | ret >>= 4 99 | ret ^= (AESGCM._gcmReductionTable[retHigh] << (128-16)) 100 | 101 | # Add in y' * H where y' are the next four terms of y, shifted down 102 | # to the x^0..x^4. This is one of the pre-computed multiples of 103 | # H. The multiplication by x^4 shifts them back into place. 104 | ret ^= self._productTable[y & 0xf] 105 | y >>= 4 106 | assert y == 0 107 | return ret 108 | 109 | def seal(self, nonce, plaintext, data): 110 | """ 111 | Encrypts and authenticates plaintext using nonce and data. Returns the 112 | ciphertext, consisting of the encrypted plaintext and tag concatenated. 113 | """ 114 | 115 | if len(nonce) != 12: 116 | raise ValueError("Bad nonce length") 117 | 118 | # The initial counter value is the nonce, followed by a 32-bit counter 119 | # that starts at 1. It's used to compute the tag mask. 120 | counter = bytearray(16) 121 | counter[:12] = nonce 122 | counter[-1] = 1 123 | tagMask = self._rawAesEncrypt(counter) 124 | 125 | # The counter starts at 2 for the actual encryption. 126 | counter[-1] = 2 127 | ciphertext = self._rawAesCtrEncrypt(counter, plaintext) 128 | 129 | tag = self._auth(ciphertext, data, tagMask) 130 | 131 | return ciphertext + tag 132 | 133 | def open(self, nonce, ciphertext, data): 134 | """ 135 | Decrypts and authenticates ciphertext using nonce and data. If the 136 | tag is valid, the plaintext is returned. If the tag is invalid, 137 | returns None. 138 | """ 139 | 140 | if len(nonce) != 12: 141 | raise ValueError("Bad nonce length") 142 | if len(ciphertext) < 16: 143 | return None 144 | 145 | tag = ciphertext[-16:] 146 | ciphertext = ciphertext[:-16] 147 | 148 | # The initial counter value is the nonce, followed by a 32-bit counter 149 | # that starts at 1. It's used to compute the tag mask. 150 | counter = bytearray(16) 151 | counter[:12] = nonce 152 | counter[-1] = 1 153 | tagMask = self._rawAesEncrypt(counter) 154 | 155 | if tag != self._auth(ciphertext, data, tagMask): 156 | return None 157 | 158 | # The counter starts at 2 for the actual decryption. 159 | counter[-1] = 2 160 | return self._rawAesCtrEncrypt(counter, ciphertext) 161 | 162 | @staticmethod 163 | def _reverseBits(i): 164 | assert i < 16 165 | i = ((i << 2) & 0xc) | ((i >> 2) & 0x3) 166 | i = ((i << 1) & 0xa) | ((i >> 1) & 0x5) 167 | return i 168 | 169 | @staticmethod 170 | def _gcmAdd(x, y): 171 | return x ^ y 172 | 173 | @staticmethod 174 | def _gcmShift(x): 175 | # Multiplying by x is a right shift, due to bit order. 176 | highTermSet = x & 1 177 | x >>= 1 178 | if highTermSet: 179 | # The x^127 term was shifted up to x^128, so subtract a 1+x+x^2+x^7 180 | # term. This is 0b11100001 or 0xe1 when represented as an 8-bit 181 | # polynomial. 182 | x ^= 0xe1 << (128-8) 183 | return x 184 | 185 | @staticmethod 186 | def _inc32(counter): 187 | for i in range(len(counter)-1, len(counter)-5, -1): 188 | counter[i] = (counter[i] + 1) % 256 189 | if counter[i] != 0: 190 | break 191 | return counter 192 | 193 | # _gcmReductionTable[i] is i * (1+x+x^2+x^7) for all 4-bit polynomials i. The 194 | # result is stored as a 16-bit polynomial. This is used in the reduction step to 195 | # multiply elements of GF(2^128) by x^4. 196 | _gcmReductionTable = [ 197 | 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0, 198 | 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0, 199 | ] 200 | -------------------------------------------------------------------------------- /tlslite/utils/asn1parser.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # Patch from Google adding getChildBytes() 3 | # 4 | # See the LICENSE file for legal information regarding use of this file. 5 | 6 | """Abstract Syntax Notation One (ASN.1) parsing""" 7 | 8 | from .codec import Parser 9 | 10 | 11 | class ASN1Parser(object): 12 | """ 13 | Parser and storage of ASN.1 DER encoded objects. 14 | 15 | :vartype length: int 16 | :ivar length: length of the value of the tag 17 | :vartype value: bytearray 18 | :ivar value: literal value of the tag 19 | """ 20 | 21 | def __init__(self, bytes): 22 | """Create an object from bytes. 23 | 24 | :type bytes: bytearray 25 | :param bytes: DER encoded ANS.1 object 26 | """ 27 | p = Parser(bytes) 28 | p.get(1) #skip Type 29 | 30 | #Get Length 31 | self.length = self._getASN1Length(p) 32 | 33 | #Get Value 34 | self.value = p.getFixBytes(self.length) 35 | 36 | def getChild(self, which): 37 | """ 38 | Return n-th child assuming that the object is a SEQUENCE. 39 | 40 | :type which: int 41 | :param which: ordinal of the child to return 42 | 43 | :rtype: ASN1Parser 44 | :returns: decoded child object 45 | """ 46 | return ASN1Parser(self.getChildBytes(which)) 47 | 48 | def getChildCount(self): 49 | """ 50 | Return number of children, assuming that the object is a SEQUENCE. 51 | 52 | :rtype: int 53 | :returns: number of children in the object 54 | """ 55 | p = Parser(self.value) 56 | count = 0 57 | while True: 58 | if p.getRemainingLength() == 0: 59 | break 60 | p.get(1) # skip Type 61 | length = self._getASN1Length(p) 62 | p.getFixBytes(length) # skip value 63 | count += 1 64 | return count 65 | 66 | def getChildBytes(self, which): 67 | """ 68 | Return raw encoding of n-th child, assume self is a SEQUENCE 69 | 70 | :type which: int 71 | :param which: ordinal of the child to return 72 | 73 | :rtype: bytearray 74 | :returns: raw child object 75 | """ 76 | p = Parser(self.value) 77 | for _ in range(which+1): 78 | markIndex = p.index 79 | p.get(1) #skip Type 80 | length = self._getASN1Length(p) 81 | p.getFixBytes(length) 82 | return p.bytes[markIndex : p.index] 83 | 84 | @staticmethod 85 | def _getASN1Length(p): 86 | """Decode the ASN.1 DER length field""" 87 | firstLength = p.get(1) 88 | if firstLength <= 127: 89 | return firstLength 90 | else: 91 | lengthLength = firstLength & 0x7F 92 | return p.get(lengthLength) 93 | -------------------------------------------------------------------------------- /tlslite/utils/chacha.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Hubert Kario 2 | # 3 | # See the LICENSE file for legal information regarding use of this file. 4 | """Pure Python implementation of ChaCha cipher 5 | 6 | Implementation that follows RFC 7539 closely. 7 | """ 8 | 9 | from __future__ import division 10 | from .compat import compat26Str 11 | import copy 12 | import struct 13 | try: 14 | # in Python 3 the native zip returns iterator 15 | from itertools import izip 16 | except ImportError: 17 | izip = zip 18 | 19 | class ChaCha(object): 20 | 21 | """Pure python implementation of ChaCha cipher""" 22 | 23 | constants = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574] 24 | 25 | @staticmethod 26 | def rotl32(v, c): 27 | """Rotate left a 32 bit integer v by c bits""" 28 | return ((v << c) & 0xffffffff) | (v >> (32 - c)) 29 | 30 | @staticmethod 31 | def quarter_round(x, a, b, c, d): 32 | """Perform a ChaCha quarter round""" 33 | xa = x[a] 34 | xb = x[b] 35 | xc = x[c] 36 | xd = x[d] 37 | 38 | xa = (xa + xb) & 0xffffffff 39 | xd = xd ^ xa 40 | xd = ((xd << 16) & 0xffffffff | (xd >> 16)) 41 | 42 | xc = (xc + xd) & 0xffffffff 43 | xb = xb ^ xc 44 | xb = ((xb << 12) & 0xffffffff | (xb >> 20)) 45 | 46 | xa = (xa + xb) & 0xffffffff 47 | xd = xd ^ xa 48 | xd = ((xd << 8) & 0xffffffff | (xd >> 24)) 49 | 50 | xc = (xc + xd) & 0xffffffff 51 | xb = xb ^ xc 52 | xb = ((xb << 7) & 0xffffffff | (xb >> 25)) 53 | 54 | x[a] = xa 55 | x[b] = xb 56 | x[c] = xc 57 | x[d] = xd 58 | 59 | _round_mixup_box = [(0, 4, 8, 12), 60 | (1, 5, 9, 13), 61 | (2, 6, 10, 14), 62 | (3, 7, 11, 15), 63 | (0, 5, 10, 15), 64 | (1, 6, 11, 12), 65 | (2, 7, 8, 13), 66 | (3, 4, 9, 14)] 67 | 68 | @classmethod 69 | def double_round(cls, x): 70 | """Perform two rounds of ChaCha cipher""" 71 | for a, b, c, d in cls._round_mixup_box: 72 | xa = x[a] 73 | xb = x[b] 74 | xc = x[c] 75 | xd = x[d] 76 | 77 | xa = (xa + xb) & 0xffffffff 78 | xd = xd ^ xa 79 | xd = ((xd << 16) & 0xffffffff | (xd >> 16)) 80 | 81 | xc = (xc + xd) & 0xffffffff 82 | xb = xb ^ xc 83 | xb = ((xb << 12) & 0xffffffff | (xb >> 20)) 84 | 85 | xa = (xa + xb) & 0xffffffff 86 | xd = xd ^ xa 87 | xd = ((xd << 8) & 0xffffffff | (xd >> 24)) 88 | 89 | xc = (xc + xd) & 0xffffffff 90 | xb = xb ^ xc 91 | xb = ((xb << 7) & 0xffffffff | (xb >> 25)) 92 | 93 | x[a] = xa 94 | x[b] = xb 95 | x[c] = xc 96 | x[d] = xd 97 | 98 | @staticmethod 99 | def chacha_block(key, counter, nonce, rounds): 100 | """Generate a state of a single block""" 101 | state = ChaCha.constants + key + [counter] + nonce 102 | 103 | working_state = state[:] 104 | dbl_round = ChaCha.double_round 105 | for _ in range(0, rounds // 2): 106 | dbl_round(working_state) 107 | 108 | return [(st + wrkSt) & 0xffffffff for st, wrkSt 109 | in izip(state, working_state)] 110 | 111 | @staticmethod 112 | def word_to_bytearray(state): 113 | """Convert state to little endian bytestream""" 114 | return bytearray(struct.pack('= (3,0): 15 | 16 | def compat26Str(x): return x 17 | 18 | # Python 3 requires bytes instead of bytearrays for HMAC 19 | 20 | # So, python 2.6 requires strings, python 3 requires 'bytes', 21 | # and python 2.7 can handle bytearrays... 22 | def compatHMAC(x): return bytes(x) 23 | 24 | def raw_input(s): 25 | return input(s) 26 | 27 | # So, the python3 binascii module deals with bytearrays, and python2 28 | # deals with strings... I would rather deal with the "a" part as 29 | # strings, and the "b" part as bytearrays, regardless of python version, 30 | # so... 31 | def a2b_hex(s): 32 | try: 33 | b = bytearray(binascii.a2b_hex(bytearray(s, "ascii"))) 34 | except Exception as e: 35 | raise SyntaxError("base16 error: %s" % e) 36 | return b 37 | 38 | def a2b_base64(s): 39 | try: 40 | if isinstance(s, str): 41 | s = bytearray(s, "ascii") 42 | b = bytearray(binascii.a2b_base64(s)) 43 | except Exception as e: 44 | raise SyntaxError("base64 error: %s" % e) 45 | return b 46 | 47 | def b2a_hex(b): 48 | return binascii.b2a_hex(b).decode("ascii") 49 | 50 | def b2a_base64(b): 51 | return binascii.b2a_base64(b).decode("ascii") 52 | 53 | def readStdinBinary(): 54 | return sys.stdin.buffer.read() 55 | 56 | def compatLong(num): 57 | return int(num) 58 | 59 | int_types = tuple([int]) 60 | 61 | def formatExceptionTrace(e): 62 | """Return exception information formatted as string""" 63 | return str(e) 64 | 65 | else: 66 | # Python 2.6 requires strings instead of bytearrays in a couple places, 67 | # so we define this function so it does the conversion if needed. 68 | # same thing with very old 2.7 versions 69 | # or on Jython 70 | if sys.version_info < (2, 7) or sys.version_info < (2, 7, 4) \ 71 | or platform.system() == 'Java': 72 | def compat26Str(x): return str(x) 73 | else: 74 | def compat26Str(x): return x 75 | 76 | # So, python 2.6 requires strings, python 3 requires 'bytes', 77 | # and python 2.7 can handle bytearrays... 78 | def compatHMAC(x): return compat26Str(x) 79 | 80 | def a2b_hex(s): 81 | try: 82 | b = bytearray(binascii.a2b_hex(s)) 83 | except Exception as e: 84 | raise SyntaxError("base16 error: %s" % e) 85 | return b 86 | 87 | def a2b_base64(s): 88 | try: 89 | b = bytearray(binascii.a2b_base64(s)) 90 | except Exception as e: 91 | raise SyntaxError("base64 error: %s" % e) 92 | return b 93 | 94 | def b2a_hex(b): 95 | return binascii.b2a_hex(compat26Str(b)) 96 | 97 | def b2a_base64(b): 98 | return binascii.b2a_base64(compat26Str(b)) 99 | 100 | def compatLong(num): 101 | return long(num) 102 | 103 | int_types = (int, long) 104 | 105 | # pylint on Python3 goes nuts for the sys dereferences... 106 | 107 | #pylint: disable=no-member 108 | def formatExceptionTrace(e): 109 | """Return exception information formatted as string""" 110 | newStr = "".join(traceback.format_exception(sys.exc_type, 111 | sys.exc_value, 112 | sys.exc_traceback)) 113 | return newStr 114 | #pylint: enable=no-member 115 | 116 | try: 117 | # Fedora and Red Hat Enterprise Linux versions have small curves removed 118 | getattr(ecdsa, 'NIST192p') 119 | except AttributeError: 120 | ecdsaAllCurves = False 121 | else: 122 | ecdsaAllCurves = True 123 | -------------------------------------------------------------------------------- /tlslite/utils/constanttime.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Hubert Kario 2 | # 3 | # See the LICENSE file for legal information regarding use of this file. 4 | """Various constant time functions for processing sensitive data""" 5 | 6 | from __future__ import division 7 | 8 | from .compat import compatHMAC 9 | import hmac 10 | 11 | def ct_lt_u32(val_a, val_b): 12 | """ 13 | Returns 1 if val_a < val_b, 0 otherwise. Constant time. 14 | 15 | :type val_a: int 16 | :type val_b: int 17 | :param val_a: an unsigned integer representable as a 32 bit value 18 | :param val_b: an unsigned integer representable as a 32 bit value 19 | :rtype: int 20 | """ 21 | val_a &= 0xffffffff 22 | val_b &= 0xffffffff 23 | 24 | return (val_a^((val_a^val_b)|(((val_a-val_b)&0xffffffff)^val_b)))>>31 25 | 26 | def ct_gt_u32(val_a, val_b): 27 | """ 28 | Return 1 if val_a > val_b, 0 otherwise. Constant time. 29 | 30 | :type val_a: int 31 | :type val_b: int 32 | :param val_a: an unsigned integer representable as a 32 bit value 33 | :param val_b: an unsigned integer representable as a 32 bit value 34 | :rtype: int 35 | """ 36 | return ct_lt_u32(val_b, val_a) 37 | 38 | def ct_le_u32(val_a, val_b): 39 | """ 40 | Return 1 if val_a <= val_b, 0 otherwise. Constant time. 41 | 42 | :type val_a: int 43 | :type val_b: int 44 | :param val_a: an unsigned integer representable as a 32 bit value 45 | :param val_b: an unsigned integer representable as a 32 bit value 46 | :rtype: int 47 | """ 48 | return 1 ^ ct_gt_u32(val_a, val_b) 49 | 50 | def ct_lsb_prop_u8(val): 51 | """Propagate LSB to all 8 bits of the returned byte. Constant time.""" 52 | val &= 0x01 53 | val |= val << 1 54 | val |= val << 2 55 | val |= val << 4 56 | return val 57 | 58 | def ct_isnonzero_u32(val): 59 | """ 60 | Returns 1 if val is != 0, 0 otherwise. Constant time. 61 | 62 | :type val: int 63 | :param val: an unsigned integer representable as a 32 bit value 64 | :rtype: int 65 | """ 66 | val &= 0xffffffff 67 | return (val|(-val&0xffffffff)) >> 31 68 | 69 | def ct_neq_u32(val_a, val_b): 70 | """ 71 | Return 1 if val_a != val_b, 0 otherwise. Constant time. 72 | 73 | :type val_a: int 74 | :type val_b: int 75 | :param val_a: an unsigned integer representable as a 32 bit value 76 | :param val_b: an unsigned integer representable as a 32 bit value 77 | :rtype: int 78 | """ 79 | val_a &= 0xffffffff 80 | val_b &= 0xffffffff 81 | 82 | return (((val_a-val_b)&0xffffffff) | ((val_b-val_a)&0xffffffff)) >> 31 83 | 84 | def ct_eq_u32(val_a, val_b): 85 | """ 86 | Return 1 if val_a == val_b, 0 otherwise. Constant time. 87 | 88 | :type val_a: int 89 | :type val_b: int 90 | :param val_a: an unsigned integer representable as a 32 bit value 91 | :param val_b: an unsigned integer representable as a 32 bit value 92 | :rtype: int 93 | """ 94 | return 1 ^ ct_neq_u32(val_a, val_b) 95 | 96 | def ct_check_cbc_mac_and_pad(data, mac, seqnumBytes, contentType, version): 97 | """ 98 | Check CBC cipher HMAC and padding. Close to constant time. 99 | 100 | :type data: bytearray 101 | :param data: data with HMAC value to test and padding 102 | 103 | :type mac: hashlib mac 104 | :param mac: empty HMAC, initialised with a key 105 | 106 | :type seqnumBytes: bytearray 107 | :param seqnumBytes: TLS sequence number, used as input to HMAC 108 | 109 | :type contentType: int 110 | :param contentType: a single byte, used as input to HMAC 111 | 112 | :type version: tuple of int 113 | :param version: a tuple of two ints, used as input to HMAC and to guide 114 | checking of padding 115 | 116 | :rtype: boolean 117 | :returns: True if MAC and pad is ok, False otherwise 118 | """ 119 | assert version in ((3, 0), (3, 1), (3, 2), (3, 3)) 120 | 121 | data_len = len(data) 122 | if mac.digest_size + 1 > data_len: # data_len is public 123 | return False 124 | 125 | # 0 - OK 126 | result = 0x00 127 | 128 | # 129 | # check padding 130 | # 131 | pad_length = data[data_len-1] 132 | pad_start = data_len - pad_length - 1 133 | pad_start = max(0, pad_start) 134 | 135 | if version == (3, 0): # version is public 136 | # in SSLv3 we can only check if pad is not longer than overall length 137 | 138 | # subtract 1 for the pad length byte 139 | mask = ct_lsb_prop_u8(ct_lt_u32(data_len-1, pad_length)) 140 | result |= mask 141 | else: 142 | start_pos = max(0, data_len - 256) 143 | for i in range(start_pos, data_len): 144 | # if pad_start < i: mask = 0xff; else: mask = 0x00 145 | mask = ct_lsb_prop_u8(ct_le_u32(pad_start, i)) 146 | # if data[i] != pad_length and "inside_pad": result = False 147 | result |= (data[i] ^ pad_length) & mask 148 | 149 | # 150 | # check MAC 151 | # 152 | 153 | # real place where mac starts and data ends 154 | mac_start = pad_start - mac.digest_size 155 | mac_start = max(0, mac_start) 156 | 157 | # place to start processing 158 | start_pos = max(0, data_len - (256 + mac.digest_size)) // mac.block_size 159 | start_pos *= mac.block_size 160 | 161 | # add start data 162 | data_mac = mac.copy() 163 | data_mac.update(compatHMAC(seqnumBytes)) 164 | data_mac.update(compatHMAC(bytearray([contentType]))) 165 | if version != (3, 0): # version is public 166 | data_mac.update(compatHMAC(bytearray([version[0]]))) 167 | data_mac.update(compatHMAC(bytearray([version[1]]))) 168 | data_mac.update(compatHMAC(bytearray([mac_start >> 8]))) 169 | data_mac.update(compatHMAC(bytearray([mac_start & 0xff]))) 170 | data_mac.update(compatHMAC(data[:start_pos])) 171 | 172 | # don't check past the array end (already checked to be >= zero) 173 | end_pos = data_len - 1 - mac.digest_size 174 | 175 | # calculate all possible 176 | for i in range(start_pos, end_pos): # constant for given overall length 177 | cur_mac = data_mac.copy() 178 | cur_mac.update(compatHMAC(data[start_pos:i])) 179 | mac_compare = bytearray(cur_mac.digest()) 180 | # compare the hash for real only if it's the place where mac is 181 | # supposed to be 182 | mask = ct_lsb_prop_u8(ct_eq_u32(i, mac_start)) 183 | for j in range(0, mac.digest_size): # digest_size is public 184 | result |= (data[i+j] ^ mac_compare[j]) & mask 185 | 186 | # return python boolean 187 | return result == 0 188 | 189 | if hasattr(hmac, 'compare_digest'): 190 | ct_compare_digest = hmac.compare_digest 191 | else: 192 | def ct_compare_digest(val_a, val_b): 193 | """Compares if string like objects are equal. Constant time.""" 194 | if len(val_a) != len(val_b): 195 | return False 196 | 197 | result = 0 198 | for x, y in zip(val_a, val_b): 199 | result |= x ^ y 200 | 201 | return result == 0 202 | -------------------------------------------------------------------------------- /tlslite/utils/datefuncs.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | import os 5 | 6 | #Functions for manipulating datetime objects 7 | #CCYY-MM-DDThh:mm:ssZ 8 | def parseDateClass(s): 9 | year, month, day = s.split("-") 10 | day, tail = day[:2], day[2:] 11 | hour, minute, second = tail[1:].split(":") 12 | second = second[:2] 13 | year, month, day = int(year), int(month), int(day) 14 | hour, minute, second = int(hour), int(minute), int(second) 15 | return createDateClass(year, month, day, hour, minute, second) 16 | 17 | 18 | if os.name != "java": 19 | from datetime import datetime, timedelta 20 | 21 | #Helper functions for working with a date/time class 22 | def createDateClass(year, month, day, hour, minute, second): 23 | return datetime(year, month, day, hour, minute, second) 24 | 25 | def printDateClass(d): 26 | #Split off fractional seconds, append 'Z' 27 | return d.isoformat().split(".")[0]+"Z" 28 | 29 | def getNow(): 30 | return datetime.utcnow() 31 | 32 | def getHoursFromNow(hours): 33 | return datetime.utcnow() + timedelta(hours=hours) 34 | 35 | def getMinutesFromNow(minutes): 36 | return datetime.utcnow() + timedelta(minutes=minutes) 37 | 38 | def isDateClassExpired(d): 39 | return d < datetime.utcnow() 40 | 41 | def isDateClassBefore(d1, d2): 42 | return d1 < d2 43 | 44 | else: 45 | #Jython 2.1 is missing lots of python 2.3 stuff, 46 | #which we have to emulate here: 47 | import java 48 | import jarray 49 | 50 | def createDateClass(year, month, day, hour, minute, second): 51 | c = java.util.Calendar.getInstance() 52 | c.setTimeZone(java.util.TimeZone.getTimeZone("UTC")) 53 | c.set(year, month-1, day, hour, minute, second) 54 | return c 55 | 56 | def printDateClass(d): 57 | return "%04d-%02d-%02dT%02d:%02d:%02dZ" % \ 58 | (d.get(d.YEAR), d.get(d.MONTH)+1, d.get(d.DATE), \ 59 | d.get(d.HOUR_OF_DAY), d.get(d.MINUTE), d.get(d.SECOND)) 60 | 61 | def getNow(): 62 | c = java.util.Calendar.getInstance() 63 | c.setTimeZone(java.util.TimeZone.getTimeZone("UTC")) 64 | c.get(c.HOUR) #force refresh? 65 | return c 66 | 67 | def getHoursFromNow(hours): 68 | d = getNow() 69 | d.add(d.HOUR, hours) 70 | return d 71 | 72 | def isDateClassExpired(d): 73 | n = getNow() 74 | return d.before(n) 75 | 76 | def isDateClassBefore(d1, d2): 77 | return d1.before(d2) 78 | -------------------------------------------------------------------------------- /tlslite/utils/dns_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017 Hubert Kario 2 | # 3 | # See the LICENSE file for legal information regarding use of this file. 4 | 5 | """Utilities for handling DNS hostnames""" 6 | 7 | import re 8 | 9 | 10 | def is_valid_hostname(hostname): 11 | """ 12 | Check if the parameter is a valid hostname. 13 | 14 | :type hostname: str or bytearray 15 | :param hostname: string to check 16 | :rtype: boolean 17 | """ 18 | try: 19 | if not isinstance(hostname, str): 20 | hostname = hostname.decode('ascii', 'strict') 21 | except UnicodeDecodeError: 22 | return False 23 | if hostname[-1] == ".": 24 | # strip exactly one dot from the right, if present 25 | hostname = hostname[:-1] 26 | # the maximum length of the domain name is 255 bytes, but because they 27 | # are encoded as labels (which is a length byte and an up to 63 character 28 | # ascii string), you change the dots to the length bytes, but the 29 | # host element of the FQDN doesn't start with a dot and the name doesn't 30 | # end with a dot (specification of a root label), we need to subtract 2 31 | # bytes from the 255 byte maximum when looking at dot-deliminated FQDN 32 | # with the trailing dot removed 33 | # see RFC 1035 34 | if len(hostname) > 253: 35 | return False 36 | 37 | # must not be all-numeric, so that it can't be confused with an ip-address 38 | if re.match(r"[\d.]+$", hostname): 39 | return False 40 | 41 | allowed = re.compile(r"(?!-)[A-Z\d-]{1,63}(? key.pem 66 | 67 | This format also supports password-encrypted private keys. TLS 68 | Lite can only handle password-encrypted private keys when OpenSSL 69 | and M2Crypto are installed. In this case, passwordCallback will be 70 | invoked to query the user for the password. 71 | 72 | :type s: str 73 | :param s: A string containing a PEM-encoded public or private key. 74 | 75 | :type private: bool 76 | :param private: If True, a :py:class:`SyntaxError` will be raised if the 77 | private key component is not present. 78 | 79 | :type public: bool 80 | :param public: If True, the private key component (if present) will 81 | be discarded, so this function will always return a public key. 82 | 83 | :type passwordCallback: callable 84 | :param passwordCallback: This function will be called, with no 85 | arguments, if the PEM-encoded private key is password-encrypted. 86 | The callback should return the password string. If the password is 87 | incorrect, SyntaxError will be raised. If no callback is passed 88 | and the key is password-encrypted, a prompt will be displayed at 89 | the console. 90 | 91 | :rtype: ~tlslite.utils.rsakey.RSAKey 92 | :returns: An RSA key. 93 | 94 | :raises SyntaxError: If the key is not properly formatted. 95 | """ 96 | for implementation in implementations: 97 | if implementation == "openssl" and cryptomath.m2cryptoLoaded: 98 | key = OpenSSL_RSAKey.parse(s, passwordCallback) 99 | break 100 | elif implementation == "python": 101 | key = Python_RSAKey.parsePEM(s) 102 | break 103 | else: 104 | raise ValueError("No acceptable implementations") 105 | 106 | return _parseKeyHelper(key, private, public) 107 | 108 | 109 | def _parseKeyHelper(key, private, public): 110 | if private: 111 | if not key.hasPrivateKey(): 112 | raise SyntaxError("Not a private key!") 113 | 114 | if public: 115 | return _createPublicKey(key) 116 | 117 | if private: 118 | if hasattr(key, "d"): 119 | return _createPrivateKey(key) 120 | else: 121 | return key 122 | 123 | return key 124 | 125 | def parseAsPublicKey(s): 126 | """Parse a PEM-formatted public key. 127 | 128 | :type s: str 129 | :param s: A string containing a PEM-encoded public or private key. 130 | 131 | :rtype: ~tlslite.utils.rsakey.RSAKey 132 | :returns: An RSA public key. 133 | 134 | :raises SyntaxError: If the key is not properly formatted. 135 | """ 136 | return parsePEMKey(s, public=True) 137 | 138 | def parsePrivateKey(s): 139 | """Parse a PEM-formatted private key. 140 | 141 | :type s: str 142 | :param s: A string containing a PEM-encoded private key. 143 | 144 | :rtype: ~tlslite.utils.rsakey.RSAKey 145 | :returns: An RSA private key. 146 | 147 | :raises SyntaxError: If the key is not properly formatted. 148 | """ 149 | return parsePEMKey(s, private=True) 150 | 151 | def _createPublicKey(key): 152 | """ 153 | Create a new public key. Discard any private component, 154 | and return the most efficient key possible. 155 | """ 156 | if not isinstance(key, RSAKey): 157 | raise AssertionError() 158 | return _createPublicRSAKey(key.n, key.e) 159 | 160 | def _createPrivateKey(key): 161 | """ 162 | Create a new private key. Return the most efficient key possible. 163 | """ 164 | if not isinstance(key, RSAKey): 165 | raise AssertionError() 166 | if not key.hasPrivateKey(): 167 | raise AssertionError() 168 | return _createPrivateRSAKey(key.n, key.e, key.d, key.p, key.q, key.dP, 169 | key.dQ, key.qInv) 170 | 171 | def _createPublicRSAKey(n, e, implementations = ["openssl", "pycrypto", 172 | "python"]): 173 | for implementation in implementations: 174 | if implementation == "openssl" and cryptomath.m2cryptoLoaded: 175 | return OpenSSL_RSAKey(n, e) 176 | elif implementation == "pycrypto" and cryptomath.pycryptoLoaded: 177 | return PyCrypto_RSAKey(n, e) 178 | elif implementation == "python": 179 | return Python_RSAKey(n, e) 180 | raise ValueError("No acceptable implementations") 181 | 182 | def _createPrivateRSAKey(n, e, d, p, q, dP, dQ, qInv, 183 | implementations = ["pycrypto", "python"]): 184 | for implementation in implementations: 185 | if implementation == "pycrypto" and cryptomath.pycryptoLoaded: 186 | return PyCrypto_RSAKey(n, e, d, p, q, dP, dQ, qInv) 187 | elif implementation == "python": 188 | return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv) 189 | raise ValueError("No acceptable implementations") 190 | -------------------------------------------------------------------------------- /tlslite/utils/lists.py: -------------------------------------------------------------------------------- 1 | # Authors: 2 | # Hubert Kario (2016) 3 | # 4 | # See the LICENSE file for legal information regarding use of this file. 5 | 6 | """Helper functions for handling lists""" 7 | 8 | from itertools import chain 9 | 10 | 11 | def getFirstMatching(values, matches): 12 | """ 13 | Return the first element in :py:obj:`values` that is also in 14 | :py:obj:`matches`. 15 | 16 | Return None if values is None, empty or no element in values is also in 17 | matches. 18 | 19 | :type values: collections.abc.Iterable 20 | :param values: list of items to look through, can be None 21 | :type matches: collections.abc.Container 22 | :param matches: list of items to check against 23 | """ 24 | assert matches is not None 25 | if not values: 26 | return None 27 | return next((i for i in values if i in matches), None) 28 | 29 | 30 | def to_str_delimiter(values, delim=", ", last_delim=" or "): 31 | """ 32 | Format the list as a human readable string. 33 | 34 | Will format the list as a human readable enumeration, separated by commas 35 | (changable with `delim`) with last value separated with "or" (changable 36 | with `last_delim`). 37 | 38 | :type values: collections.abc.Iterable 39 | :param values: list of items to concatenate 40 | :type delim: str 41 | :param delim: primary delimiter for objects, comma by default 42 | :type last_delim: str 43 | :param last_delim: delimiter for last object in list 44 | :rtype: str 45 | """ 46 | # we need to slice the iterator, so we need a copy 47 | values = list(values) 48 | return delim.join(chain((str(i) for i in values[:-2]), 49 | [last_delim.join(str(i) for i in values[-2:])])) 50 | -------------------------------------------------------------------------------- /tlslite/utils/openssl_aes.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """OpenSSL/M2Crypto AES implementation.""" 5 | 6 | from .cryptomath import * 7 | from .aes import * 8 | 9 | if m2cryptoLoaded: 10 | 11 | def new(key, mode, IV): 12 | return OpenSSL_AES(key, mode, IV) 13 | 14 | class OpenSSL_AES(AES): 15 | 16 | def __init__(self, key, mode, IV): 17 | AES.__init__(self, key, mode, IV, "openssl") 18 | self.key = key 19 | self.IV = IV 20 | 21 | def _createContext(self, encrypt): 22 | context = m2.cipher_ctx_new() 23 | if len(self.key)==16: 24 | cipherType = m2.aes_128_cbc() 25 | if len(self.key)==24: 26 | cipherType = m2.aes_192_cbc() 27 | if len(self.key)==32: 28 | cipherType = m2.aes_256_cbc() 29 | m2.cipher_init(context, cipherType, self.key, self.IV, encrypt) 30 | return context 31 | 32 | def encrypt(self, plaintext): 33 | AES.encrypt(self, plaintext) 34 | context = self._createContext(1) 35 | ciphertext = m2.cipher_update(context, plaintext) 36 | m2.cipher_ctx_free(context) 37 | self.IV = ciphertext[-self.block_size:] 38 | return bytearray(ciphertext) 39 | 40 | def decrypt(self, ciphertext): 41 | AES.decrypt(self, ciphertext) 42 | context = self._createContext(0) 43 | #I think M2Crypto has a bug - it fails to decrypt and return the last block passed in. 44 | #To work around this, we append sixteen zeros to the string, below: 45 | plaintext = m2.cipher_update(context, ciphertext+('\0'*16)) 46 | 47 | #If this bug is ever fixed, then plaintext will end up having a garbage 48 | #plaintext block on the end. That's okay - the below code will discard it. 49 | plaintext = plaintext[:len(ciphertext)] 50 | m2.cipher_ctx_free(context) 51 | self.IV = ciphertext[-self.block_size:] 52 | return bytearray(plaintext) 53 | -------------------------------------------------------------------------------- /tlslite/utils/openssl_rc4.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """OpenSSL/M2Crypto RC4 implementation.""" 5 | 6 | from .cryptomath import * 7 | from .rc4 import RC4 8 | 9 | if m2cryptoLoaded: 10 | 11 | def new(key): 12 | return OpenSSL_RC4(key) 13 | 14 | class OpenSSL_RC4(RC4): 15 | 16 | def __init__(self, key): 17 | RC4.__init__(self, key, "openssl") 18 | self.rc4 = m2.rc4_new() 19 | m2.rc4_set_key(self.rc4, key) 20 | 21 | def __del__(self): 22 | m2.rc4_free(self.rc4) 23 | 24 | def encrypt(self, plaintext): 25 | return bytearray(m2.rc4_update(self.rc4, plaintext)) 26 | 27 | def decrypt(self, ciphertext): 28 | return bytearray(self.encrypt(ciphertext)) 29 | -------------------------------------------------------------------------------- /tlslite/utils/openssl_rsakey.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """OpenSSL/M2Crypto RSA implementation.""" 5 | 6 | from .cryptomath import * 7 | 8 | from .rsakey import * 9 | from .python_rsakey import Python_RSAKey 10 | 11 | #copied from M2Crypto.util.py, so when we load the local copy of m2 12 | #we can still use it 13 | def password_callback(v, prompt1='Enter private key passphrase:', 14 | prompt2='Verify passphrase:'): 15 | from getpass import getpass 16 | while 1: 17 | try: 18 | p1=getpass(prompt1) 19 | if v: 20 | p2=getpass(prompt2) 21 | if p1==p2: 22 | break 23 | else: 24 | break 25 | except KeyboardInterrupt: 26 | return None 27 | return p1 28 | 29 | 30 | if m2cryptoLoaded: 31 | class OpenSSL_RSAKey(RSAKey): 32 | def __init__(self, n=0, e=0): 33 | self.rsa = None 34 | self._hasPrivateKey = False 35 | if (n and not e) or (e and not n): 36 | raise AssertionError() 37 | if n and e: 38 | self.rsa = m2.rsa_new() 39 | m2.rsa_set_n(self.rsa, numberToMPI(n)) 40 | m2.rsa_set_e(self.rsa, numberToMPI(e)) 41 | 42 | def __del__(self): 43 | if self.rsa: 44 | m2.rsa_free(self.rsa) 45 | 46 | def __getattr__(self, name): 47 | if name == 'e': 48 | if not self.rsa: 49 | return 0 50 | return mpiToNumber(m2.rsa_get_e(self.rsa)) 51 | elif name == 'n': 52 | if not self.rsa: 53 | return 0 54 | return mpiToNumber(m2.rsa_get_n(self.rsa)) 55 | else: 56 | raise AttributeError 57 | 58 | def hasPrivateKey(self): 59 | return self._hasPrivateKey 60 | 61 | def _rawPrivateKeyOp(self, m): 62 | b = numberToByteArray(m, numBytes(self.n)) 63 | s = m2.rsa_private_encrypt(self.rsa, bytes(b), m2.no_padding) 64 | c = bytesToNumber(bytearray(s)) 65 | return c 66 | 67 | def _rawPublicKeyOp(self, c): 68 | b = numberToByteArray(c, numBytes(self.n)) 69 | s = m2.rsa_public_decrypt(self.rsa, bytes(b), m2.no_padding) 70 | m = bytesToNumber(bytearray(s)) 71 | return m 72 | 73 | def acceptsPassword(self): return True 74 | 75 | def write(self, password=None): 76 | bio = m2.bio_new(m2.bio_s_mem()) 77 | if self._hasPrivateKey: 78 | if password: 79 | def f(v): return password 80 | m2.rsa_write_key(self.rsa, bio, m2.des_ede_cbc(), f) 81 | else: 82 | def f(): pass 83 | m2.rsa_write_key_no_cipher(self.rsa, bio, f) 84 | else: 85 | if password: 86 | raise AssertionError() 87 | m2.rsa_write_pub_key(self.rsa, bio) 88 | s = m2.bio_read(bio, m2.bio_ctrl_pending(bio)) 89 | m2.bio_free(bio) 90 | return s 91 | 92 | def generate(bits): 93 | key = OpenSSL_RSAKey() 94 | def f():pass 95 | key.rsa = m2.rsa_generate_key(bits, 3, f) 96 | key._hasPrivateKey = True 97 | return key 98 | generate = staticmethod(generate) 99 | 100 | def parse(s, passwordCallback=None): 101 | # Skip forward to the first PEM header 102 | start = s.find("-----BEGIN ") 103 | if start == -1: 104 | raise SyntaxError() 105 | s = s[start:] 106 | if s.startswith("-----BEGIN "): 107 | if passwordCallback==None: 108 | callback = password_callback 109 | else: 110 | def f(v, prompt1=None, prompt2=None): 111 | return passwordCallback() 112 | callback = f 113 | bio = m2.bio_new(m2.bio_s_mem()) 114 | try: 115 | m2.bio_write(bio, s) 116 | key = OpenSSL_RSAKey() 117 | # parse SSLay format PEM file 118 | if s.startswith("-----BEGIN RSA PRIVATE KEY-----"): 119 | def f():pass 120 | key.rsa = m2.rsa_read_key(bio, callback) 121 | if key.rsa == None: 122 | raise SyntaxError() 123 | key._hasPrivateKey = True 124 | # parse a standard PKCS#8 PEM file 125 | elif s.startswith("-----BEGIN PRIVATE KEY-----"): 126 | def f():pass 127 | key.rsa = m2.pkey_read_pem(bio, callback) 128 | # the below code assumes RSA key while PKCS#8 files 129 | # (and by extension the EVP_PKEY structure) can be 130 | # also DSA or EC, thus the double check against None 131 | # (first if the file was properly loaded and second 132 | # if the file actually has a RSA key in it) 133 | # tlslite doesn't support DSA or EC so it's useless 134 | # to handle them in a different way 135 | if key.rsa == None: 136 | raise SyntaxError() 137 | key.rsa = m2.pkey_get1_rsa(key.rsa) 138 | if key.rsa == None: 139 | raise SyntaxError() 140 | key._hasPrivateKey = True 141 | elif s.startswith("-----BEGIN PUBLIC KEY-----"): 142 | key.rsa = m2.rsa_read_pub_key(bio) 143 | if key.rsa == None: 144 | raise SyntaxError() 145 | key._hasPrivateKey = False 146 | else: 147 | raise SyntaxError() 148 | return key 149 | finally: 150 | m2.bio_free(bio) 151 | else: 152 | raise SyntaxError() 153 | 154 | parse = staticmethod(parse) 155 | -------------------------------------------------------------------------------- /tlslite/utils/openssl_tripledes.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """OpenSSL/M2Crypto 3DES implementation.""" 5 | 6 | from .cryptomath import * 7 | from .tripledes import * 8 | 9 | if m2cryptoLoaded: 10 | 11 | def new(key, mode, IV): 12 | return OpenSSL_TripleDES(key, mode, IV) 13 | 14 | class OpenSSL_TripleDES(TripleDES): 15 | 16 | def __init__(self, key, mode, IV): 17 | TripleDES.__init__(self, key, mode, IV, "openssl") 18 | self.key = key 19 | self.IV = IV 20 | 21 | def _createContext(self, encrypt): 22 | context = m2.cipher_ctx_new() 23 | cipherType = m2.des_ede3_cbc() 24 | m2.cipher_init(context, cipherType, self.key, self.IV, encrypt) 25 | return context 26 | 27 | def encrypt(self, plaintext): 28 | TripleDES.encrypt(self, plaintext) 29 | context = self._createContext(1) 30 | ciphertext = m2.cipher_update(context, plaintext) 31 | m2.cipher_ctx_free(context) 32 | self.IV = ciphertext[-self.block_size:] 33 | return bytearray(ciphertext) 34 | 35 | def decrypt(self, ciphertext): 36 | TripleDES.decrypt(self, ciphertext) 37 | context = self._createContext(0) 38 | #I think M2Crypto has a bug - it fails to decrypt and return the last block passed in. 39 | #To work around this, we append sixteen zeros to the string, below: 40 | plaintext = m2.cipher_update(context, ciphertext+('\0'*16)) 41 | 42 | #If this bug is ever fixed, then plaintext will end up having a garbage 43 | #plaintext block on the end. That's okay - the below code will ignore it. 44 | plaintext = plaintext[:len(ciphertext)] 45 | m2.cipher_ctx_free(context) 46 | self.IV = ciphertext[-self.block_size:] 47 | return bytearray(plaintext) -------------------------------------------------------------------------------- /tlslite/utils/pem.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | from .compat import * 5 | import binascii 6 | 7 | #This code is shared with tackpy (somewhat), so I'd rather make minimal 8 | #changes, and preserve the use of a2b_base64 throughout. 9 | 10 | def dePem(s, name): 11 | """Decode a PEM string into a bytearray of its payload. 12 | 13 | The input must contain an appropriate PEM prefix and postfix 14 | based on the input name string, e.g. for name="CERTIFICATE":: 15 | 16 | -----BEGIN CERTIFICATE----- 17 | MIIBXDCCAUSgAwIBAgIBADANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDEwRUQUNL 18 | ... 19 | KoZIhvcNAQEFBQADAwA5kw== 20 | -----END CERTIFICATE----- 21 | 22 | The first such PEM block in the input will be found, and its 23 | payload will be base64 decoded and returned. 24 | """ 25 | prefix = "-----BEGIN %s-----" % name 26 | postfix = "-----END %s-----" % name 27 | start = s.find(prefix) 28 | if start == -1: 29 | raise SyntaxError("Missing PEM prefix") 30 | end = s.find(postfix, start+len(prefix)) 31 | if end == -1: 32 | raise SyntaxError("Missing PEM postfix") 33 | s = s[start+len("-----BEGIN %s-----" % name) : end] 34 | retBytes = a2b_base64(s) # May raise SyntaxError 35 | return retBytes 36 | 37 | def dePemList(s, name): 38 | """Decode a sequence of PEM blocks into a list of bytearrays. 39 | 40 | The input must contain any number of PEM blocks, each with the appropriate 41 | PEM prefix and postfix based on the input name string, e.g. for 42 | name="TACK BREAK SIG". Arbitrary text can appear between and before and 43 | after the PEM blocks. For example:: 44 | 45 | Created by TACK.py 0.9.3 Created at 2012-02-01T00:30:10Z 46 | -----BEGIN TACK BREAK SIG----- 47 | ATKhrz5C6JHJW8BF5fLVrnQss6JnWVyEaC0p89LNhKPswvcC9/s6+vWLd9snYTUv 48 | YMEBdw69PUP8JB4AdqA3K6Ap0Fgd9SSTOECeAKOUAym8zcYaXUwpk0+WuPYa7Zmm 49 | SkbOlK4ywqt+amhWbg9txSGUwFO5tWUHT3QrnRlE/e3PeNFXLx5Bckg= 50 | -----END TACK BREAK SIG----- 51 | Created by TACK.py 0.9.3 Created at 2012-02-01T00:30:11Z 52 | -----BEGIN TACK BREAK SIG----- 53 | ATKhrz5C6JHJW8BF5fLVrnQss6JnWVyEaC0p89LNhKPswvcC9/s6+vWLd9snYTUv 54 | YMEBdw69PUP8JB4AdqA3K6BVCWfcjN36lx6JwxmZQncS6sww7DecFO/qjSePCxwM 55 | +kdDqX/9/183nmjx6bf0ewhPXkA0nVXsDYZaydN8rJU1GaMlnjcIYxY= 56 | -----END TACK BREAK SIG----- 57 | 58 | All such PEM blocks will be found, decoded, and return in an ordered list 59 | of bytearrays, which may have zero elements if not PEM blocks are found. 60 | """ 61 | bList = [] 62 | prefix = "-----BEGIN %s-----" % name 63 | postfix = "-----END %s-----" % name 64 | while 1: 65 | start = s.find(prefix) 66 | if start == -1: 67 | return bList 68 | end = s.find(postfix, start+len(prefix)) 69 | if end == -1: 70 | raise SyntaxError("Missing PEM postfix") 71 | s2 = s[start+len(prefix) : end] 72 | retBytes = a2b_base64(s2) # May raise SyntaxError 73 | bList.append(retBytes) 74 | s = s[end+len(postfix) : ] 75 | 76 | def pem(b, name): 77 | """Encode a payload bytearray into a PEM string. 78 | 79 | The input will be base64 encoded, then wrapped in a PEM prefix/postfix 80 | based on the name string, e.g. for name="CERTIFICATE":: 81 | 82 | -----BEGIN CERTIFICATE----- 83 | MIIBXDCCAUSgAwIBAgIBADANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDEwRUQUNL 84 | ... 85 | KoZIhvcNAQEFBQADAwA5kw== 86 | -----END CERTIFICATE----- 87 | """ 88 | s1 = b2a_base64(b)[:-1] # remove terminating \n 89 | s2 = "" 90 | while s1: 91 | s2 += s1[:64] + "\n" 92 | s1 = s1[64:] 93 | s = ("-----BEGIN %s-----\n" % name) + s2 + \ 94 | ("-----END %s-----\n" % name) 95 | return s 96 | 97 | def pemSniff(inStr, name): 98 | searchStr = "-----BEGIN %s-----" % name 99 | return searchStr in inStr 100 | -------------------------------------------------------------------------------- /tlslite/utils/poly1305.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Hubert Kario 2 | # 3 | # See the LICENSE file for legal information regarding use of this file. 4 | """Implementation of Poly1305 authenticator for RFC 7539""" 5 | 6 | from .cryptomath import divceil 7 | 8 | class Poly1305(object): 9 | 10 | """Poly1305 authenticator""" 11 | 12 | P = 0x3fffffffffffffffffffffffffffffffb # 2^130-5 13 | 14 | @staticmethod 15 | def le_bytes_to_num(data): 16 | """Convert a number from little endian byte format""" 17 | ret = 0 18 | for i in range(len(data) - 1, -1, -1): 19 | ret <<= 8 20 | ret += data[i] 21 | return ret 22 | 23 | @staticmethod 24 | def num_to_16_le_bytes(num): 25 | """Convert number to 16 bytes in little endian format""" 26 | ret = [0]*16 27 | for i, _ in enumerate(ret): 28 | ret[i] = num & 0xff 29 | num >>= 8 30 | return bytearray(ret) 31 | 32 | def __init__(self, key): 33 | """Set the authenticator key""" 34 | if len(key) != 32: 35 | raise ValueError("Key must be 256 bit long") 36 | self.acc = 0 37 | self.r = self.le_bytes_to_num(key[0:16]) 38 | self.r &= 0x0ffffffc0ffffffc0ffffffc0fffffff 39 | self.s = self.le_bytes_to_num(key[16:32]) 40 | 41 | def create_tag(self, data): 42 | """Calculate authentication tag for data""" 43 | for i in range(0, divceil(len(data), 16)): 44 | n = self.le_bytes_to_num(data[i*16:(i+1)*16] + b'\x01') 45 | self.acc += n 46 | self.acc = (self.r * self.acc) % self.P 47 | self.acc += self.s 48 | return self.num_to_16_le_bytes(self.acc) 49 | 50 | -------------------------------------------------------------------------------- /tlslite/utils/pycrypto_aes.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """PyCrypto AES implementation.""" 5 | 6 | from .cryptomath import * 7 | from .aes import * 8 | 9 | if pycryptoLoaded: 10 | import Crypto.Cipher.AES 11 | 12 | def new(key, mode, IV): 13 | return PyCrypto_AES(key, mode, IV) 14 | 15 | class PyCrypto_AES(AES): 16 | 17 | def __init__(self, key, mode, IV): 18 | AES.__init__(self, key, mode, IV, "pycrypto") 19 | key = bytes(key) 20 | IV = bytes(IV) 21 | self.context = Crypto.Cipher.AES.new(key, mode, IV) 22 | 23 | def encrypt(self, plaintext): 24 | plaintext = bytes(plaintext) 25 | return bytearray(self.context.encrypt(plaintext)) 26 | 27 | def decrypt(self, ciphertext): 28 | ciphertext = bytes(ciphertext) 29 | return bytearray(self.context.decrypt(ciphertext)) 30 | -------------------------------------------------------------------------------- /tlslite/utils/pycrypto_aesgcm.py: -------------------------------------------------------------------------------- 1 | # Author: Google 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """PyCrypto AES-GCM implementation.""" 5 | 6 | from .cryptomath import * 7 | from .aesgcm import AESGCM 8 | 9 | if pycryptoLoaded: 10 | import Crypto.Cipher.AES 11 | 12 | def new(key): 13 | cipher = Crypto.Cipher.AES.new(bytes(key)) 14 | def encrypt(plaintext): 15 | return bytearray(cipher.encrypt(bytes(plaintext))) 16 | return AESGCM(key, "pycrypto", encrypt) 17 | -------------------------------------------------------------------------------- /tlslite/utils/pycrypto_rc4.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """PyCrypto RC4 implementation.""" 5 | 6 | from .cryptomath import * 7 | from .rc4 import * 8 | 9 | if pycryptoLoaded: 10 | import Crypto.Cipher.ARC4 11 | 12 | def new(key): 13 | return PyCrypto_RC4(key) 14 | 15 | class PyCrypto_RC4(RC4): 16 | 17 | def __init__(self, key): 18 | RC4.__init__(self, key, "pycrypto") 19 | key = bytes(key) 20 | self.context = Crypto.Cipher.ARC4.new(key) 21 | 22 | def encrypt(self, plaintext): 23 | plaintext = bytes(plaintext) 24 | return bytearray(self.context.encrypt(plaintext)) 25 | 26 | def decrypt(self, ciphertext): 27 | ciphertext = bytes(ciphertext) 28 | return bytearray(self.context.decrypt(ciphertext)) -------------------------------------------------------------------------------- /tlslite/utils/pycrypto_rsakey.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """PyCrypto RSA implementation.""" 5 | 6 | from __future__ import print_function 7 | import sys 8 | 9 | from .cryptomath import * 10 | 11 | from .rsakey import * 12 | from .python_rsakey import Python_RSAKey 13 | from .compat import compatLong 14 | 15 | if pycryptoLoaded: 16 | 17 | from Crypto.PublicKey import RSA 18 | 19 | class PyCrypto_RSAKey(RSAKey): 20 | def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0): 21 | if not d: 22 | self.rsa = RSA.construct((compatLong(n), compatLong(e))) 23 | else: 24 | self.rsa = RSA.construct((compatLong(n), compatLong(e), 25 | compatLong(d), compatLong(p), 26 | compatLong(q))) 27 | 28 | def __getattr__(self, name): 29 | return getattr(self.rsa, name) 30 | 31 | def hasPrivateKey(self): 32 | return self.rsa.has_private() 33 | 34 | def _rawPrivateKeyOp(self, m): 35 | try: 36 | return self.rsa.decrypt((compatLong(m),)) 37 | except ValueError as e: 38 | print("rsa: {0!r}".format(self.rsa), file=sys.stderr) 39 | values = [] 40 | for name in ["n", "e", "d", "p", "q", "dP", "dQ", "qInv"]: 41 | values.append("{0}: {1}".format(name, 42 | getattr(self, name, None))) 43 | print(", ".join(values), file=sys.stderr) 44 | print("m: {0}".format(m), file=sys.stderr) 45 | raise 46 | 47 | 48 | def _rawPublicKeyOp(self, c): 49 | try: 50 | return self.rsa.encrypt(compatLong(c), None)[0] 51 | except ValueError as e: 52 | print("rsa: {0!r}".format(self.rsa), file=sys.stderr) 53 | values = [] 54 | for name in ["n", "e", "d", "p", "q", "dP", "dQ", "qInv"]: 55 | values.append("{0}: {1}".format(name, 56 | getattr(self, name, None))) 57 | print(", ".join(values), file=sys.stderr) 58 | print("c: {0}".format(c), file=sys.stderr) 59 | raise 60 | 61 | def generate(bits): 62 | key = PyCrypto_RSAKey() 63 | def f(numBytes): 64 | return bytes(getRandomBytes(numBytes)) 65 | key.rsa = RSA.generate(bits, f) 66 | return key 67 | generate = staticmethod(generate) 68 | -------------------------------------------------------------------------------- /tlslite/utils/pycrypto_tripledes.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """PyCrypto 3DES implementation.""" 5 | 6 | from .cryptomath import * 7 | from .tripledes import * 8 | 9 | if pycryptoLoaded: 10 | import Crypto.Cipher.DES3 11 | 12 | def new(key, mode, IV): 13 | return PyCrypto_TripleDES(key, mode, IV) 14 | 15 | class PyCrypto_TripleDES(TripleDES): 16 | 17 | def __init__(self, key, mode, IV): 18 | TripleDES.__init__(self, key, mode, IV, "pycrypto") 19 | key = bytes(key) 20 | IV = bytes(IV) 21 | self.context = Crypto.Cipher.DES3.new(key, mode, IV) 22 | 23 | def encrypt(self, plaintext): 24 | plaintext = bytes(plaintext) 25 | return bytearray(self.context.encrypt(plaintext)) 26 | 27 | def decrypt(self, ciphertext): 28 | ciphertext = bytes(ciphertext) 29 | return bytearray(self.context.decrypt(ciphertext)) -------------------------------------------------------------------------------- /tlslite/utils/python_aes.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """Pure-Python AES implementation.""" 5 | 6 | from .cryptomath import * 7 | 8 | from .aes import * 9 | from .rijndael import rijndael 10 | 11 | def new(key, mode, IV): 12 | return Python_AES(key, mode, IV) 13 | 14 | class Python_AES(AES): 15 | def __init__(self, key, mode, IV): 16 | AES.__init__(self, key, mode, IV, "python") 17 | self.rijndael = rijndael(key, 16) 18 | self.IV = IV 19 | 20 | def encrypt(self, plaintext): 21 | AES.encrypt(self, plaintext) 22 | 23 | plaintextBytes = plaintext[:] 24 | chainBytes = self.IV[:] 25 | 26 | #CBC Mode: For each block... 27 | for x in range(len(plaintextBytes)//16): 28 | 29 | #XOR with the chaining block 30 | blockBytes = plaintextBytes[x*16 : (x*16)+16] 31 | for y in range(16): 32 | blockBytes[y] ^= chainBytes[y] 33 | 34 | #Encrypt it 35 | encryptedBytes = self.rijndael.encrypt(blockBytes) 36 | 37 | #Overwrite the input with the output 38 | for y in range(16): 39 | plaintextBytes[(x*16)+y] = encryptedBytes[y] 40 | 41 | #Set the next chaining block 42 | chainBytes = encryptedBytes 43 | 44 | self.IV = chainBytes[:] 45 | return plaintextBytes 46 | 47 | def decrypt(self, ciphertext): 48 | AES.decrypt(self, ciphertext) 49 | 50 | ciphertextBytes = ciphertext[:] 51 | chainBytes = self.IV[:] 52 | 53 | #CBC Mode: For each block... 54 | for x in range(len(ciphertextBytes)//16): 55 | 56 | #Decrypt it 57 | blockBytes = ciphertextBytes[x*16 : (x*16)+16] 58 | decryptedBytes = self.rijndael.decrypt(blockBytes) 59 | 60 | #XOR with the chaining block and overwrite the input with output 61 | for y in range(16): 62 | decryptedBytes[y] ^= chainBytes[y] 63 | ciphertextBytes[(x*16)+y] = decryptedBytes[y] 64 | 65 | #Set the next chaining block 66 | chainBytes = blockBytes 67 | 68 | self.IV = chainBytes[:] 69 | return ciphertextBytes 70 | -------------------------------------------------------------------------------- /tlslite/utils/python_aesgcm.py: -------------------------------------------------------------------------------- 1 | # Author: Google 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """Pure-Python AES-GCM implementation.""" 5 | 6 | from .aesgcm import AESGCM 7 | from .rijndael import rijndael 8 | 9 | def new(key): 10 | return AESGCM(key, "python", rijndael(key, 16).encrypt) 11 | -------------------------------------------------------------------------------- /tlslite/utils/python_chacha20_poly1305.py: -------------------------------------------------------------------------------- 1 | # Author: Hubert Kario (c) 2015 2 | # 3 | # See the LICENSE file for legal information regarding use of this file. 4 | 5 | """Pure-Python ChaCha20/Poly1305 implementation.""" 6 | 7 | from .chacha20_poly1305 import CHACHA20_POLY1305 8 | 9 | def new(key): 10 | """Return an AEAD cipher implementation""" 11 | return CHACHA20_POLY1305(key, "python") 12 | -------------------------------------------------------------------------------- /tlslite/utils/python_rc4.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """Pure-Python RC4 implementation.""" 5 | 6 | from .rc4 import RC4 7 | from .cryptomath import * 8 | 9 | def new(key): 10 | return Python_RC4(key) 11 | 12 | class Python_RC4(RC4): 13 | def __init__(self, keyBytes): 14 | RC4.__init__(self, keyBytes, "python") 15 | S = [i for i in range(256)] 16 | j = 0 17 | for i in range(256): 18 | j = (j + S[i] + keyBytes[i % len(keyBytes)]) % 256 19 | S[i], S[j] = S[j], S[i] 20 | 21 | self.S = S 22 | self.i = 0 23 | self.j = 0 24 | 25 | def encrypt(self, plaintextBytes): 26 | ciphertextBytes = plaintextBytes[:] 27 | S = self.S 28 | i = self.i 29 | j = self.j 30 | for x in range(len(ciphertextBytes)): 31 | i = (i + 1) % 256 32 | j = (j + S[i]) % 256 33 | S[i], S[j] = S[j], S[i] 34 | t = (S[i] + S[j]) % 256 35 | ciphertextBytes[x] ^= S[t] 36 | self.i = i 37 | self.j = j 38 | return ciphertextBytes 39 | 40 | def decrypt(self, ciphertext): 41 | return self.encrypt(ciphertext) 42 | -------------------------------------------------------------------------------- /tlslite/utils/python_rsakey.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """Pure-Python RSA implementation.""" 5 | 6 | from .cryptomath import * 7 | from .asn1parser import ASN1Parser 8 | from .rsakey import * 9 | from .pem import * 10 | 11 | class Python_RSAKey(RSAKey): 12 | def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0): 13 | if (n and not e) or (e and not n): 14 | raise AssertionError() 15 | self.n = n 16 | self.e = e 17 | if p and not q or not p and q: 18 | raise ValueError("p and q must be set or left unset together") 19 | if not d and p and q: 20 | t = lcm(p - 1, q - 1) 21 | d = invMod(e, t) 22 | self.d = d 23 | self.p = p 24 | self.q = q 25 | if not dP and p: 26 | dP = d % (p - 1) 27 | self.dP = dP 28 | if not dQ and q: 29 | dQ = d % (q - 1) 30 | self.dQ = dQ 31 | if not qInv: 32 | qInv = invMod(q, p) 33 | self.qInv = qInv 34 | self.blinder = 0 35 | self.unblinder = 0 36 | 37 | def hasPrivateKey(self): 38 | return self.d != 0 39 | 40 | def _rawPrivateKeyOp(self, m): 41 | #Create blinding values, on the first pass: 42 | if not self.blinder: 43 | self.unblinder = getRandomNumber(2, self.n) 44 | self.blinder = powMod(invMod(self.unblinder, self.n), self.e, 45 | self.n) 46 | 47 | #Blind the input 48 | m = (m * self.blinder) % self.n 49 | 50 | #Perform the RSA operation 51 | c = self._rawPrivateKeyOpHelper(m) 52 | 53 | #Unblind the output 54 | c = (c * self.unblinder) % self.n 55 | 56 | #Update blinding values 57 | self.blinder = (self.blinder * self.blinder) % self.n 58 | self.unblinder = (self.unblinder * self.unblinder) % self.n 59 | 60 | #Return the output 61 | return c 62 | 63 | 64 | def _rawPrivateKeyOpHelper(self, m): 65 | #Non-CRT version 66 | #c = powMod(m, self.d, self.n) 67 | 68 | #CRT version (~3x faster) 69 | s1 = powMod(m, self.dP, self.p) 70 | s2 = powMod(m, self.dQ, self.q) 71 | h = ((s1 - s2) * self.qInv) % self.p 72 | c = s2 + self.q * h 73 | return c 74 | 75 | def _rawPublicKeyOp(self, c): 76 | m = powMod(c, self.e, self.n) 77 | return m 78 | 79 | def acceptsPassword(self): return False 80 | 81 | def generate(bits): 82 | key = Python_RSAKey() 83 | p = getRandomPrime(bits//2, False) 84 | q = getRandomPrime(bits//2, False) 85 | t = lcm(p-1, q-1) 86 | key.n = p * q 87 | key.e = 65537 88 | key.d = invMod(key.e, t) 89 | key.p = p 90 | key.q = q 91 | key.dP = key.d % (p-1) 92 | key.dQ = key.d % (q-1) 93 | key.qInv = invMod(q, p) 94 | return key 95 | generate = staticmethod(generate) 96 | 97 | def parsePEM(s, passwordCallback=None): 98 | """Parse a string containing a PEM-encoded .""" 99 | 100 | if pemSniff(s, "PRIVATE KEY"): 101 | bytes = dePem(s, "PRIVATE KEY") 102 | return Python_RSAKey._parsePKCS8(bytes) 103 | elif pemSniff(s, "RSA PRIVATE KEY"): 104 | bytes = dePem(s, "RSA PRIVATE KEY") 105 | return Python_RSAKey._parseSSLeay(bytes) 106 | else: 107 | raise SyntaxError("Not a PEM private key file") 108 | parsePEM = staticmethod(parsePEM) 109 | 110 | def _parsePKCS8(bytes): 111 | p = ASN1Parser(bytes) 112 | 113 | # first element in PrivateKeyInfo is an INTEGER 114 | version = p.getChild(0).value 115 | if bytesToNumber(version) != 0: 116 | raise SyntaxError("Unrecognized PKCS8 version") 117 | 118 | # second element in PrivateKeyInfo is a SEQUENCE of type 119 | # AlgorithmIdentifier 120 | algIdent = p.getChild(1) 121 | seqLen = algIdent.getChildCount() 122 | # first item of AlgorithmIdentifier is an OBJECT (OID) 123 | oid = algIdent.getChild(0) 124 | if list(oid.value) == [42, 134, 72, 134, 247, 13, 1, 1, 1]: 125 | keyType = "rsa" 126 | elif list(oid.value) == [42, 134, 72, 134, 247, 13, 1, 1, 10]: 127 | keyType = "rsa-pss" 128 | else: 129 | raise SyntaxError("Unrecognized AlgorithmIdentifier: {0}" 130 | .format(list(oid.value))) 131 | # second item of AlgorithmIdentifier are parameters (defined by 132 | # above algorithm) 133 | if keyType == "rsa": 134 | if seqLen != 2: 135 | raise SyntaxError("Missing parameters for RSA algorithm ID") 136 | parameters = algIdent.getChild(1) 137 | if parameters.value != bytearray(0): 138 | raise SyntaxError("RSA parameters are not NULL") 139 | else: # rsa-pss 140 | pass # ignore parameters - don't apply restrictions 141 | 142 | if seqLen > 2: 143 | raise SyntaxError("Invalid encoding of AlgorithmIdentifier") 144 | 145 | #Get the privateKey 146 | privateKeyP = p.getChild(2) 147 | 148 | #Adjust for OCTET STRING encapsulation 149 | privateKeyP = ASN1Parser(privateKeyP.value) 150 | 151 | return Python_RSAKey._parseASN1PrivateKey(privateKeyP) 152 | _parsePKCS8 = staticmethod(_parsePKCS8) 153 | 154 | def _parseSSLeay(bytes): 155 | privateKeyP = ASN1Parser(bytes) 156 | return Python_RSAKey._parseASN1PrivateKey(privateKeyP) 157 | _parseSSLeay = staticmethod(_parseSSLeay) 158 | 159 | def _parseASN1PrivateKey(privateKeyP): 160 | version = privateKeyP.getChild(0).value[0] 161 | if version != 0: 162 | raise SyntaxError("Unrecognized RSAPrivateKey version") 163 | n = bytesToNumber(privateKeyP.getChild(1).value) 164 | e = bytesToNumber(privateKeyP.getChild(2).value) 165 | d = bytesToNumber(privateKeyP.getChild(3).value) 166 | p = bytesToNumber(privateKeyP.getChild(4).value) 167 | q = bytesToNumber(privateKeyP.getChild(5).value) 168 | dP = bytesToNumber(privateKeyP.getChild(6).value) 169 | dQ = bytesToNumber(privateKeyP.getChild(7).value) 170 | qInv = bytesToNumber(privateKeyP.getChild(8).value) 171 | return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv) 172 | _parseASN1PrivateKey = staticmethod(_parseASN1PrivateKey) 173 | -------------------------------------------------------------------------------- /tlslite/utils/rc4.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """Abstract class for RC4.""" 5 | 6 | 7 | class RC4(object): 8 | def __init__(self, keyBytes, implementation): 9 | if len(keyBytes) < 16 or len(keyBytes) > 256: 10 | raise ValueError() 11 | self.isBlockCipher = False 12 | self.isAEAD = False 13 | self.name = "rc4" 14 | self.implementation = implementation 15 | 16 | def encrypt(self, plaintext): 17 | raise NotImplementedError() 18 | 19 | def decrypt(self, ciphertext): 20 | raise NotImplementedError() 21 | -------------------------------------------------------------------------------- /tlslite/utils/tackwrapper.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | try: 5 | from tack.structures.Tack import Tack 6 | from tack.structures.TackExtension import TackExtension 7 | from tack.tls.TlsCertificate import TlsCertificate 8 | 9 | tackpyLoaded = True 10 | except ImportError: 11 | tackpyLoaded = False 12 | -------------------------------------------------------------------------------- /tlslite/utils/tlshashlib.py: -------------------------------------------------------------------------------- 1 | # Author: Hubert Kario (c) 2015 2 | # see LICENCE file for legal information regarding use of this file 3 | 4 | """hashlib that handles FIPS mode.""" 5 | 6 | # Because we are extending the hashlib module, we need to import all its 7 | # fields to suppport the same uses 8 | # pylint: disable=unused-wildcard-import, wildcard-import 9 | from hashlib import * 10 | # pylint: enable=unused-wildcard-import, wildcard-import 11 | import hashlib 12 | 13 | 14 | def _fipsFunction(func, *args, **kwargs): 15 | """Make hash function support FIPS mode.""" 16 | try: 17 | return func(*args, **kwargs) 18 | except ValueError: 19 | return func(*args, usedforsecurity=False, **kwargs) 20 | 21 | 22 | # redefining the function is exactly what we intend to do 23 | # pylint: disable=function-redefined 24 | def md5(*args, **kwargs): 25 | """MD5 constructor that works in FIPS mode.""" 26 | return _fipsFunction(hashlib.md5, *args, **kwargs) 27 | 28 | 29 | def new(*args, **kwargs): 30 | """General constructor that works in FIPS mode.""" 31 | return _fipsFunction(hashlib.new, *args, **kwargs) 32 | # pylint: enable=function-redefined 33 | -------------------------------------------------------------------------------- /tlslite/utils/tripledes.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """Abstract class for 3DES.""" 5 | 6 | class TripleDES(object): 7 | def __init__(self, key, mode, IV, implementation): 8 | if len(key) != 24: 9 | raise ValueError() 10 | if mode != 2: 11 | raise ValueError() 12 | if len(IV) != 8: 13 | raise ValueError() 14 | self.isBlockCipher = True 15 | self.isAEAD = False 16 | self.block_size = 8 17 | self.implementation = implementation 18 | self.name = "3des" 19 | 20 | #CBC-Mode encryption, returns ciphertext 21 | #WARNING: *MAY* modify the input as well 22 | def encrypt(self, plaintext): 23 | assert(len(plaintext) % 8 == 0) 24 | 25 | #CBC-Mode decryption, returns plaintext 26 | #WARNING: *MAY* modify the input as well 27 | def decrypt(self, ciphertext): 28 | assert(len(ciphertext) % 8 == 0) 29 | -------------------------------------------------------------------------------- /tlslite/utils/x25519.py: -------------------------------------------------------------------------------- 1 | # Authors: 2 | # Hubert Kario (2017) 3 | # 4 | # See the LICENSE file for legal information regarding use of this file. 5 | 6 | """Handling X25519 and X448 curve based key agreement protocol.""" 7 | 8 | from .cryptomath import bytesToNumber, numberToByteArray, divceil 9 | # the names of the variables come directly from RFC 7748 so changing them 10 | # would make the code harder to audit/compare 11 | # pylint: disable=invalid-name 12 | 13 | 14 | def decodeUCoordinate(u, bits): 15 | """Function to decode the public U coordinate of X25519-family curves.""" 16 | if bits not in (255, 448): 17 | raise ValueError("Invalid number of expected bits") 18 | if bits % 8: 19 | u[-1] &= (1 << (bits % 8)) - 1 20 | return bytesToNumber(u, endian="little") 21 | 22 | 23 | def decodeScalar22519(k): 24 | """Function to decode the private K parameter of the x25519 function.""" 25 | k[0] &= 248 26 | k[31] &= 127 27 | k[31] |= 64 28 | return bytesToNumber(k, endian="little") 29 | 30 | 31 | def decodeScalar448(k): 32 | """Function to decode the private K parameter of the X448 function.""" 33 | k[0] &= 252 34 | k[55] |= 128 35 | return bytesToNumber(k, endian="little") 36 | 37 | 38 | def cswap(swap, x_2, x_3): 39 | """Conditional swap function.""" 40 | if swap: 41 | return x_3, x_2 42 | else: 43 | return x_2, x_3 44 | 45 | 46 | X25519_G = numberToByteArray(9, 32, endian="little") 47 | 48 | 49 | X25519_ORDER_SIZE = 32 50 | 51 | 52 | def x25519(k, u): 53 | """ 54 | Perform point multiplication on X25519 curve. 55 | 56 | :type k: bytearray 57 | :param k: random secret value (multiplier), should be 32 byte long 58 | 59 | :type u: bytearray 60 | :param u: curve generator or the other party key share 61 | 62 | :rtype: bytearray 63 | """ 64 | bits = 255 65 | k = decodeScalar22519(k) 66 | u = decodeUCoordinate(u, bits) 67 | 68 | a24 = 121665 69 | p = 2**255 - 19 70 | 71 | return _x25519_generic(k, u, bits, a24, p) 72 | 73 | 74 | X448_G = numberToByteArray(5, 56, endian="little") 75 | 76 | 77 | X448_ORDER_SIZE = 56 78 | 79 | 80 | def x448(k, u): 81 | """ 82 | Perform point multiplication on X448 curve. 83 | 84 | :type k: bytearray 85 | :param k: random secret value (multiplier), should be 56 bytes long 86 | 87 | :type u: bytearray 88 | :param u: curve generator or the other party key share 89 | 90 | :rtype: bytearray 91 | """ 92 | bits = 448 93 | k = decodeScalar448(k) 94 | u = decodeUCoordinate(u, bits) 95 | 96 | a24 = 39081 97 | p = 2**448 - 2**224 - 1 98 | 99 | return _x25519_generic(k, u, bits, a24, p) 100 | 101 | 102 | def _x25519_generic(k, u, bits, a24, p): 103 | """Generic Montgomery ladder implementation of the x25519 algorithm.""" 104 | x_1 = u 105 | x_2 = 1 106 | z_2 = 0 107 | x_3 = u 108 | z_3 = 1 109 | swap = 0 110 | 111 | for t in range(bits-1, -1, -1): 112 | k_t = (k >> t) & 1 113 | swap ^= k_t 114 | x_2, x_3 = cswap(swap, x_2, x_3) 115 | z_2, z_3 = cswap(swap, z_2, z_3) 116 | swap = k_t 117 | 118 | A = (x_2 + z_2) % p 119 | AA = pow(A, 2, p) 120 | B = (x_2 - z_2) % p 121 | BB = pow(B, 2, p) 122 | E = (AA - BB) % p 123 | C = (x_3 + z_3) % p 124 | D = (x_3 - z_3) % p 125 | DA = (D * A) % p 126 | CB = (C * B) % p 127 | x_3 = pow(DA + CB, 2, p) 128 | z_3 = (x_1 * pow(DA - CB, 2, p)) % p 129 | x_2 = (AA * BB) % p 130 | z_2 = (E * (AA + a24 * E)) % p 131 | 132 | x_2, x_3 = cswap(swap, x_2, x_3) 133 | z_2, z_3 = cswap(swap, z_2, z_3) 134 | ret = (x_2 * pow(z_2, p - 2, p)) % p 135 | return numberToByteArray(ret, divceil(bits, 8), endian="little") 136 | # pylint: enable=invalid-name 137 | -------------------------------------------------------------------------------- /tlslite/verifierdb.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """Class for storing SRP password verifiers.""" 5 | 6 | from .utils.cryptomath import * 7 | from .utils.compat import * 8 | from tlslite import mathtls 9 | from .basedb import BaseDB 10 | 11 | class VerifierDB(BaseDB): 12 | """This class represent an in-memory or on-disk database of SRP 13 | password verifiers. 14 | 15 | A VerifierDB can be passed to a server handshake to authenticate 16 | a client based on one of the verifiers. 17 | 18 | This class is thread-safe. 19 | """ 20 | def __init__(self, filename=None): 21 | """Create a new VerifierDB instance. 22 | 23 | :type filename: str 24 | :param filename: Filename for an on-disk database, or None for 25 | an in-memory database. If the filename already exists, follow 26 | this with a call to open(). To create a new on-disk database, 27 | follow this with a call to create(). 28 | """ 29 | BaseDB.__init__(self, filename, b"verifier") 30 | 31 | def _getItem(self, username, valueStr): 32 | (N, g, salt, verifier) = valueStr.split(b" ") 33 | N = bytesToNumber(a2b_base64(N)) 34 | g = bytesToNumber(a2b_base64(g)) 35 | salt = a2b_base64(salt) 36 | verifier = bytesToNumber(a2b_base64(verifier)) 37 | return (N, g, salt, verifier) 38 | 39 | def __setitem__(self, username, verifierEntry): 40 | """Add a verifier entry to the database. 41 | 42 | :type username: str 43 | :param username: The username to associate the verifier with. 44 | Must be less than 256 characters in length. Must not already 45 | be in the database. 46 | 47 | :type verifierEntry: tuple 48 | :param verifierEntry: The verifier entry to add. Use 49 | :py:meth:`~tlslite.verifierdb.VerifierDB.makeVerifier` to create a 50 | verifier entry. 51 | """ 52 | BaseDB.__setitem__(self, username, verifierEntry) 53 | 54 | 55 | def _setItem(self, username, value): 56 | if len(username)>=256: 57 | raise ValueError("username too long") 58 | N, g, salt, verifier = value 59 | N = b2a_base64(numberToByteArray(N)).encode("ascii") 60 | g = b2a_base64(numberToByteArray(g)).encode("ascii") 61 | salt = b2a_base64(salt).encode("ascii") 62 | verifier = b2a_base64(numberToByteArray(verifier)).encode("ascii") 63 | valueStr = b" ".join((N, g, salt, verifier)) 64 | return valueStr 65 | 66 | def _checkItem(self, value, username, param): 67 | (N, g, salt, verifier) = value 68 | x = mathtls.makeX(salt, username, param) 69 | v = powMod(g, x, N) 70 | return (verifier == v) 71 | 72 | @staticmethod 73 | def makeVerifier(username, password, bits): 74 | """Create a verifier entry which can be stored in a VerifierDB. 75 | 76 | :type username: str 77 | :param username: The username for this verifier. Must be less 78 | than 256 characters in length. 79 | 80 | :type password: str 81 | :param password: The password for this verifier. 82 | 83 | :type bits: int 84 | :param bits: This values specifies which SRP group parameters 85 | to use. It must be one of (1024, 1536, 2048, 3072, 4096, 6144, 86 | 8192). Larger values are more secure but slower. 2048 is a 87 | good compromise between safety and speed. 88 | 89 | :rtype: tuple 90 | :returns: A tuple which may be stored in a VerifierDB. 91 | """ 92 | if isinstance(username, str): 93 | usernameBytes = bytearray(username, "utf-8") 94 | else: 95 | usernameBytes = bytearray(username) 96 | if isinstance(password, str): 97 | passwordBytes = bytearray(password, "utf-8") 98 | else: 99 | passwordBytes = bytearray(password) 100 | return mathtls.makeVerifier(usernameBytes, passwordBytes, bits) 101 | -------------------------------------------------------------------------------- /tlslite/x509.py: -------------------------------------------------------------------------------- 1 | # Authors: 2 | # Trevor Perrin 3 | # Google - parsing subject field 4 | # 5 | # See the LICENSE file for legal information regarding use of this file. 6 | 7 | """Class representing an X.509 certificate.""" 8 | 9 | from .utils.asn1parser import ASN1Parser 10 | from .utils.cryptomath import * 11 | from .utils.keyfactory import _createPublicRSAKey 12 | from .utils.pem import * 13 | 14 | 15 | class X509(object): 16 | """ 17 | This class represents an X.509 certificate. 18 | 19 | :vartype bytes: bytearray 20 | :ivar bytes: The DER-encoded ASN.1 certificate 21 | 22 | :vartype publicKey: ~tlslite.utils.rsakey.RSAKey 23 | :ivar publicKey: The subject public key from the certificate. 24 | 25 | :vartype subject: bytearray 26 | :ivar subject: The DER-encoded ASN.1 subject distinguished name. 27 | 28 | :vartype certAlg: str 29 | :ivar certAlg: algorithm of the public key, "rsa" for RSASSA-PKCS#1 v1.5 30 | and "rsa-pss" for RSASSA-PSS 31 | """ 32 | 33 | def __init__(self): 34 | """Create empty certificate object.""" 35 | self.bytes = bytearray(0) 36 | self.publicKey = None 37 | self.subject = None 38 | self.certAlg = None 39 | 40 | def parse(self, s): 41 | """ 42 | Parse a PEM-encoded X.509 certificate. 43 | 44 | :type s: str 45 | :param s: A PEM-encoded X.509 certificate (i.e. a base64-encoded 46 | certificate wrapped with "-----BEGIN CERTIFICATE-----" and 47 | "-----END CERTIFICATE-----" tags). 48 | """ 49 | bytes = dePem(s, "CERTIFICATE") 50 | self.parseBinary(bytes) 51 | return self 52 | 53 | def parseBinary(self, bytes): 54 | """ 55 | Parse a DER-encoded X.509 certificate. 56 | 57 | :type bytes: str or L{bytearray} of unsigned bytes 58 | :param bytes: A DER-encoded X.509 certificate. 59 | """ 60 | self.bytes = bytearray(bytes) 61 | p = ASN1Parser(bytes) 62 | 63 | #Get the tbsCertificate 64 | tbsCertificateP = p.getChild(0) 65 | 66 | #Is the optional version field present? 67 | #This determines which index the key is at. 68 | if tbsCertificateP.value[0]==0xA0: 69 | subjectPublicKeyInfoIndex = 6 70 | else: 71 | subjectPublicKeyInfoIndex = 5 72 | 73 | #Get the subject 74 | self.subject = tbsCertificateP.getChildBytes(\ 75 | subjectPublicKeyInfoIndex - 1) 76 | 77 | #Get the subjectPublicKeyInfo 78 | subjectPublicKeyInfoP = tbsCertificateP.getChild(\ 79 | subjectPublicKeyInfoIndex) 80 | 81 | # Get the AlgorithmIdentifier 82 | algIdentifier = subjectPublicKeyInfoP.getChild(0) 83 | algIdentifierLen = algIdentifier.getChildCount() 84 | # first item of AlgorithmIdentifier is the algorithm 85 | alg = algIdentifier.getChild(0) 86 | rsaOID = alg.value 87 | if list(rsaOID) == [42, 134, 72, 134, 247, 13, 1, 1, 1]: 88 | self.certAlg = "rsa" 89 | elif list(rsaOID) == [42, 134, 72, 134, 247, 13, 1, 1, 10]: 90 | self.certAlg = "rsa-pss" 91 | else: 92 | raise SyntaxError("Unrecognized AlgorithmIdentifier") 93 | 94 | # for RSA the parameters of AlgorithmIdentifier should be a NULL 95 | if self.certAlg == "rsa": 96 | if algIdentifierLen != 2: 97 | raise SyntaxError("Missing parameters in AlgorithmIdentifier") 98 | params = algIdentifier.getChild(1) 99 | if params.value != bytearray(0): 100 | raise SyntaxError("Unexpected non-NULL parameters in " 101 | "AlgorithmIdentifier") 102 | else: # rsa-pss 103 | pass # ignore parameters, if any - don't apply key restrictions 104 | 105 | #Get the subjectPublicKey 106 | subjectPublicKeyP = subjectPublicKeyInfoP.getChild(1) 107 | 108 | #Adjust for BIT STRING encapsulation 109 | if (subjectPublicKeyP.value[0] !=0): 110 | raise SyntaxError() 111 | subjectPublicKeyP = ASN1Parser(subjectPublicKeyP.value[1:]) 112 | 113 | #Get the modulus and exponent 114 | modulusP = subjectPublicKeyP.getChild(0) 115 | publicExponentP = subjectPublicKeyP.getChild(1) 116 | 117 | #Decode them into numbers 118 | n = bytesToNumber(modulusP.value) 119 | e = bytesToNumber(publicExponentP.value) 120 | 121 | #Create a public key instance 122 | self.publicKey = _createPublicRSAKey(n, e) 123 | 124 | def getFingerprint(self): 125 | """ 126 | Get the hex-encoded fingerprint of this certificate. 127 | 128 | :rtype: str 129 | :returns: A hex-encoded fingerprint. 130 | """ 131 | return b2a_hex(SHA1(self.bytes)) 132 | 133 | def writeBytes(self): 134 | """Serialise object to a DER encoded string.""" 135 | return self.bytes 136 | 137 | 138 | -------------------------------------------------------------------------------- /tlslite/x509certchain.py: -------------------------------------------------------------------------------- 1 | # Author: Trevor Perrin 2 | # See the LICENSE file for legal information regarding use of this file. 3 | 4 | """Class representing an X.509 certificate chain.""" 5 | 6 | from .utils import cryptomath 7 | from .utils.tackwrapper import * 8 | from .utils.pem import * 9 | from .x509 import X509 10 | 11 | class X509CertChain(object): 12 | """This class represents a chain of X.509 certificates. 13 | 14 | :vartype x509List: list 15 | :ivar x509List: A list of :py:class:`tlslite.x509.X509` instances, 16 | starting with the end-entity certificate and with every 17 | subsequent certificate certifying the previous. 18 | """ 19 | 20 | def __init__(self, x509List=None): 21 | """Create a new X509CertChain. 22 | 23 | :type x509List: list 24 | :param x509List: A list of :py:class:`tlslite.x509.X509` instances, 25 | starting with the end-entity certificate and with every 26 | subsequent certificate certifying the previous. 27 | """ 28 | if x509List: 29 | self.x509List = x509List 30 | else: 31 | self.x509List = [] 32 | 33 | def parsePemList(self, s): 34 | """Parse a string containing a sequence of PEM certs. 35 | 36 | Raise a SyntaxError if input is malformed. 37 | """ 38 | x509List = [] 39 | bList = dePemList(s, "CERTIFICATE") 40 | for b in bList: 41 | x509 = X509() 42 | x509.parseBinary(b) 43 | x509List.append(x509) 44 | self.x509List = x509List 45 | 46 | def getNumCerts(self): 47 | """Get the number of certificates in this chain. 48 | 49 | :rtype: int 50 | """ 51 | return len(self.x509List) 52 | 53 | def getEndEntityPublicKey(self): 54 | """Get the public key from the end-entity certificate. 55 | 56 | :rtype: ~tlslite.utils.rsakey.RSAKey` 57 | """ 58 | if self.getNumCerts() == 0: 59 | raise AssertionError() 60 | return self.x509List[0].publicKey 61 | 62 | def getFingerprint(self): 63 | """Get the hex-encoded fingerprint of the end-entity certificate. 64 | 65 | :rtype: str 66 | :returns: A hex-encoded fingerprint. 67 | """ 68 | if self.getNumCerts() == 0: 69 | raise AssertionError() 70 | return self.x509List[0].getFingerprint() 71 | 72 | def checkTack(self, tack): 73 | if self.x509List: 74 | tlsCert = TlsCertificate(self.x509List[0].bytes) 75 | if tlsCert.matches(tack): 76 | return True 77 | return False 78 | 79 | def getTackExt(self): 80 | """Get the TACK and/or Break Sigs from a TACK Cert in the chain.""" 81 | tackExt = None 82 | # Search list in backwards order 83 | for x509 in self.x509List[::-1]: 84 | tlsCert = TlsCertificate(x509.bytes) 85 | if tlsCert.tackExt: 86 | if tackExt: 87 | raise SyntaxError("Multiple TACK Extensions") 88 | else: 89 | tackExt = tlsCert.tackExt 90 | return tackExt 91 | 92 | --------------------------------------------------------------------------------